/* ══════════════════════════════════════════════════════════════════════
   ТОКЕНЫ ОФОРМЛЕНИЯ v2 (голубая пастель)

   Источник значений — README дизайн-пакета от 17.09.2026, §3. Где в
   README стоит число, здесь стоит ровно оно: кегли, радиусы, высоты и
   hex-коды не «примерно такие», а те самые. Правило простое: любое
   значение цвета, кегля, радиуса или высоты в этом файле берётся
   отсюда. Литералов по коду быть не должно — их отсутствие проверяется
   на этапе F (`grep` по hex вне :root даёт ноль).

   Почему шкалы такие короткие. До переоформления в app.css было 69
   разных значений padding, 18 значений gap и кегли 9, 10, 15, 17, 18 px
   мимо всякой шкалы — отсюда и брался эффект «всё чуть-чуть не
   выровнено». Узкая шкала лечит это механически: выбрать «почти то же,
   но на пиксель иначе» становится не из чего.
   ══════════════════════════════════════════════════════════════════════ */

/* Шрифты поставляются файлами (web/static/fonts/), к Google Fonts
   программа не обращается: на рабочих машинах интернета может не быть.
   unicode-range делит кириллицу и латиницу, чтобы браузер тянул только
   нужный файл. Лицензии обоих — SIL OFL 1.1, см. THIRD_PARTY_LICENSES. */
@font-face {
  font-family: "Golos Text";
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url("/static/fonts/golos-text-cyrillic.woff2") format("woff2");
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: "Golos Text";
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url("/static/fonts/golos-text-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "PT Serif";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/static/fonts/pt-serif-cyrillic.woff2") format("woff2");
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: "PT Serif";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/static/fonts/pt-serif-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Токены объявлены и для <body>, а не только для :root. Темы
   переопределяют базовые цвета на body (body.theme-…), а производные
   (--fg: var(--text), --pick-bg: var(--accent-200) и т. д.) вычисляются
   там, где объявлены: объявленные только в :root, они брали цвета темы
   по умолчанию и в таком виде наследовались вниз. В тёмной теме так
   выбранная строка панели участка выходила светлой со светлым текстом,
   а текст на --fg — тёмным на тёмном. На body производные считаются от
   цветов темы; body.theme-… весомее простого body и перекрывает базовые
   цвета, как прежде. */
:root, body {
  /* ── Шрифты (README §2) ──
     Сериф — только имена людей и заголовки. В таблицах, кнопках и
     подписях полей его быть не должно. */
  --font-ui: "Golos Text", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-title: "PT Serif", Georgia, "Times New Roman", serif;
  --font-mono: ui-monospace, Consolas, "Courier New", monospace;

  /* ── Кегли (README §3.2, раунд 2 §12 п.1) ──
     Ничего мельче 13 px. Капители и разрядки нет: иерархия держится
     кеглем, начертанием и воздухом сверху. Пять уровней — заголовок
     экрана 25, заголовок блока 17, надзаголовок 13/600, текст 15,
     подпись поля 13/400. */
  --fs-sm: 13px;    /* подписи полей, пояснения, служебные строки */
  --fs-base: 15px;  /* строки таблиц, значения полей, пункты меню */
  --fs-md: 16px;    /* подписи основных кнопок, ФИО в таблице */
  --fs-lg: 17px;    /* заголовки блоков */
  --fs-xl: 19px;    /* заголовки панелей, названия плиток */
  --fs-h2: 25px;    /* заголовок экрана */
  --fs-h1: 30px;    /* ФИО в карточке записи */

  /* Начертания: 400 обычный, 500 — ФИО и подписи кнопок, 600 —
     заголовки на Golos. Жирнее 600 не бывает, PT Serif — только 400. */
  --fw-normal: 400;
  --fw-medium: 500;
  --fw-bold: 600;

  /* ── Отступы (README §3.1) ── */
  --sp-1: 4px;   /* подпись → поле */
  --sp-2: 8px;
  --sp-3: 12px;  /* внутри контрола */
  --sp-4: 16px;  /* между полями */
  --sp-5: 20px;  /* между блоками */
  --sp-6: 24px;
  --sp-8: 32px;

  /* ── Палитра (README §3.3) ──
     Один акцент, пастельные фоны, воздух вместо рамок. */
  --accent: #5C86B3;        /* основная кнопка, активный сегмент, выбранный участок */
  --accent-deep: #2F4A68;   /* текст на пастельных плашках, шапки таблиц, заголовки */
  --accent-link: #4B7099;   /* ссылки и номера-ссылки */
  --accent-link-hover: #3B5A7D;
  --accent-100: #E8F0F8;    /* шапка таблицы, чипы, активный сегмент */
  --accent-200: #CFE0F0;    /* выделенная строка, занятое место на карте */
  --accent-300: #BED3E8;    /* рамки пастельных плашек, контуры на карте */
  --accent-400: #A9C8E6;    /* заливка выбранного участка */

  --bg: #F7FAFD;            /* фон экрана */
  --surface: #FFFFFF;       /* карточки, таблицы, панели, шапка */
  --surface-2: #EEF3F9;     /* зебра, подвалы, поля ввода, вложенные блоки */
  --line: #E4ECF5;          /* внутренние разделители, линии строк */
  --line-2: #DBE6F1;        /* внешние границы блоков и панелей */

  --text: #26323F;          /* основной текст, ФИО */
  --text-2: #3F4E60;        /* значения полей, вторичный текст */
  /* --muted и --hint — на тон темнее значений README (#5E7288, #66798F),
     решение владельца от 23.09.2026: на зебре таблиц (--surface-2) и
     голубых шапках (--accent-100) они давали 4,0–4,47:1 при норме 4,5.
     Затемнены ровно до нормы с запасом (4,70 и 4,55 на худшем фоне),
     оттенок и насыщенность прежние. */
  --muted: #596C81;         /* подписи, пояснения */
  --hint: #5D6E82;          /* «—», служебные пометки, версия */

  --on-accent: #FFFFFF;
  /* Заливка основной кнопки. README §3.3 обещает у неё контраст 5,15:1,
     но белое на --accent (#5C86B3) даёт 3,81:1 — ниже нормы 4,5:1;
     ровно 5,15:1 получается на #4B7099, то есть число в README
     посчитано для этого тона. Берём его: иначе единственная кнопка
     действия на экране не проходит собственную проверку контраста.
     (Прежняя основная кнопка — белое на #60a5fa — давала 2,6:1.)
     --accent остаётся для полос, выделений и активных сегментов, где
     текста поверх нет. */
  --accent-strong: #4B7099;

  /* ── Служебные состояния: пыльная триада вне акцента (README §3.3) ──
     Нарочно не голубые и не «светофорные»: «внимание» и «ошибка» не
     должны спорить с акцентом, а сообщение об успехе — кричать. */
  --ok-bg: #ECF2EC;    --ok-line: #C9DCC9;    --ok-bar: #46654C;  --ok-fg: #3F6B52;
  --warn-bg: #F6EFE4;  --warn-line: #D9C39B;  --warn-bar: #9A6F2E; --warn-fg: #6F5433;
  --err-bg: #F5EEEE;   --err-line: #E2C4C4;   --err-bar: #8C4A4A;  --err-fg: #7C2D34;
  --pick-bg: var(--accent-200);   /* активная строка, найденное */
  --pick-bar: var(--accent);

  /* Опасное действие — контурная кнопка. Залитой красной кнопки в
     системе не остаётся ни одной (README §4.2). */
  --danger-line: #D3AFAF;
  --danger-fg: #8C4A4A;

  /* ── Радиусы (README §3.4) — главное отличие от прежнего вида ── */
  --r-chip: 10px;    /* чипы, плашки-счётчики, теги */
  --r-icon: 11px;    /* кнопка-иконка 36 px */
  --r-card: 14px;    /* карточки, таблицы, плашки состояний, модалки */
  --r-field: 16px;   /* поля, селекты, кнопки, сегменты */
  --r-panel: 26px;   /* внешний угол большой панели участка */
  --r-pill: 999px;   /* счётчики-пилюли */

  /* ── Метрики контролов (README §3.4) ──
     44 px — не «покрупнее», а нижняя граница кликабельной зоны: с ней
     оператор не промахивается мышью в конце смены. */
  --h-control: 44px;        /* поле, селект, кнопка */
  --h-icon-btn: 36px;       /* служебная кнопка-иконка, квадрат */
  --h-topbar: 52px;         /* верхняя полоса приложения */
  --h-thead: 40px;          /* шапка таблицы */
  --h-row: 44px;            /* строка таблицы, «обычно» */
  --h-row-compact: 38px;
  --h-row-side: 40px;       /* строка списка в панели участка: это список, не форма */
  --h-row-comfort: 52px;
  --w-plot-panel: 372px;    /* боковая панель реестра, третья колонка карточки */
  --w-map-side: 440px;      /* панель участка на карте (раунд 2, §12 п.4) */
  --h-doc-tabs: 56px;       /* полоса открытых документов внизу окна */
  --box-check: 20px;        /* чекбокс и радио */

  /* Тени — только у модалок и всплывающих меню. В плоских блоках
     границы делает --line/--line-2, а не тень. */
  --shadow-menu: 0 6px 20px rgba(38, 50, 63, .12);
  --shadow-modal: 0 18px 48px rgba(38, 50, 63, .18);

  /* ── Состояния участка на карте (README §3.3) ──
     Полигоны рисуются с прямыми углами: скругление на карте читается
     как «другой объект», а не как стиль. */
  /* Палитра README §3.3. После удаления легенды (раунд 2, §12 п.3) её
     никто не читает; оставлена до решения по Б3 — чем кодировать заливку. */
  --map-taken-fill: var(--accent-200);   --map-taken-line: var(--accent-300);
  --map-free-fill: var(--surface);       --map-free-line: var(--accent-300);
  --map-norec-fill: var(--warn-bg);      --map-norec-line: var(--warn-line);
  --map-sel-fill: var(--accent-400);     --map-sel-line: var(--accent);

  /* ══════════════════════════════════════════════════════════════════
     Совместимость со старым app.css.

     В файле ещё живут экран-специфичные блоки прежнего оформления —
     их снимают этапы C и D. Чтобы они не остались в старой палитре
     (и чтобы ни один экран не перестал открываться на время
     переоформления), прежние имена переменных оставлены алиасами на
     новые токены. Переписанный блок перестаёт ссылаться на алиас;
     когда алиас больше никем не используется, его строка убирается.
     ══════════════════════════════════════════════════════════════════ */
  --fg: var(--text);
  --bg-soft: var(--accent-100);
  --surface-alt: var(--surface-2);
  --border: var(--line-2);
  --border-soft: var(--line);
  --primary: var(--accent);
  --primary-hover: var(--accent-deep);
  --primary-fg: var(--on-accent);
  --primary-soft: var(--accent-100);
  --btn-bg: var(--surface);
  --btn-bg-hover: var(--accent-100);
  --input-bg: var(--surface-2);
  --focus-ring: color-mix(in srgb, var(--accent) 22%, transparent);
  --shadow: none;
  --swatch-fallback: var(--hint);
  --sa-badge-bg: var(--accent-100);
  --sa-badge-fg: var(--accent-deep);
  /* Опасное: старые имена вели на залитую красную кнопку — теперь ведут
     на контур из README §4.2. */
  --danger: var(--danger-fg);
  --danger-bg: var(--err-bg);
  --danger-border: var(--danger-line);
  --ok: var(--ok-fg);
  --ok-border: var(--ok-line);
  --warn-border: var(--warn-line);
  /* Высоты кнопок: прежняя шкала 36/32/24 нарушала нижнюю границу
     кликабельной зоны, поэтому все три ведут на 44 px. Исключение —
     кнопка-иконка, у неё свой токен --h-icon-btn. */
  --btn-h-md: var(--h-control);
  --btn-h-sm: var(--h-control);
  --btn-h-xs: var(--h-control);

  /* ── Шкала z-index — единая карта слоёв приложения ──
       40    sticky-шапка страницы (под топбаром)
       100   выпадающие меню топбара (внутри stacking-контекста топбара)
       1100  sticky-тулбар карты (выше leaflet-панелей 200–1000)
       1200  топбар (перекрывает map-toolbar и его дропдауны)
       1500  дропдауны карты («+ Участок», «Слои», «Инструменты»)
       1600  выпадашка поиска ФИО в тулбаре карты
       1750  тосты и уведомления карты
       1800  полоса вкладок документов (fixed-подвал)
       1850  окно результатов поиска ФИО
       2000  модалки
       9999  лайтбокс предпросмотра файлов
       50000 командная палитра (Ctrl+K)
       99999 оверлей self-update (поверх всего)
     Новые слои добавляйте сюда, а не магическим числом на месте. */
  --z-sticky-head: 40;
  --z-menu: 100;
  --z-map-toolbar: 1100;
  --z-topbar: 1200;
  --z-dropdown: 1500;
  --z-search: 1600;
  --z-toast: 1750;
  --z-doc-tabs: 1800;
  --z-fio: 1850;
  --z-modal: 2000;
  --z-lightbox: 9999;
  --z-palette: 50000;
  --z-overlay: 99999;
}
/* ──────────────────────────────────────────────────────────────────────
   Темы интерфейса. На <body> навешивается класс `theme-<имя>`
   (значение из settings.theme, см. MeHandler / SettingsService).
   Имена — по драгоценным камням и материалам; канонические коды
   совпадают с классами (sapphire / anthracite / malachite / ochre /
   garnet).

   «Голубая пастель» — она же «Сапфир» — тема по умолчанию: живёт в
   :root выше и отдельного класса не имеет (пустой class = sapphire).

   Остальные четыре переопределяют ТОЛЬКО акцент, фоны и линии
   (README §5.9, «Важно»). Кегли, радиусы, отступы и метрики контролов
   темами не трогаются: иначе «поправили контраст» превращалось бы в
   «поправили в одной теме из пяти», а высота кнопки зависела бы от
   выбранного оператором цвета.

   Каждая тема обязана задать полный набор: --accent, --accent-strong
   (заливка основной кнопки, контраст с белым ≥ 4,5:1), --accent-deep,
   --accent-link (+hover), четыре ступени --accent-100…400, фоны,
   линии и цвета текста. Триада состояний (успех / внимание / ошибка)
   и опасное действие общие для всех тем — они означают состояние
   системы, а не оформление.
   ────────────────────────────────────────────────────────────────────── */

/* Антрацит — для работы при низкой освещённости и на больших мониторах.
   Тёмная: фон и текст меняются ролями. Акцент светлее фона, поэтому
   --accent-strong здесь светлый, а текст на нём — тёмный. */
body.theme-anthracite {
  --accent: #6C9BD1;
  --accent-strong: #7FB0E4;
  --accent-deep: #CFE0F0;
  --accent-link: #8FC0F0;
  --accent-link-hover: #B4D6F7;
  --accent-100: #1E2A3A;
  --accent-200: #27394E;
  --accent-300: #35506B;
  --accent-400: #456B8C;

  --bg: #131A24;
  --surface: #1B2430;
  --surface-2: #222D3C;
  --line: #2B3849;
  --line-2: #34435A;

  --text: #E6EDF5;
  --text-2: #C3CFDD;
  --muted: #94A5B8;
  --hint: #8395A9;
  --on-accent: #12202F;

  /* Триада на тёмном фоне: те же роли, но фоны глубокие, а текст
     светлый — иначе пастельные плашки светились бы дырами. */
  --ok-bg: #16271C;    --ok-line: #2C4433;    --ok-bar: #7FBF93;  --ok-fg: #A8D8B9;
  --warn-bg: #2A2013;  --warn-line: #4A3A22;  --warn-bar: #D0A254; --warn-fg: #E4C58C;
  --err-bg: #2A1A1A;   --err-line: #4A2E2E;   --err-bar: #C98383;  --err-fg: #E8A8A8;
  --danger-line: #A96B6B;
  --danger-fg: #E8A8A8;

  --shadow-menu: 0 6px 20px rgba(0, 0, 0, .45);
  --shadow-modal: 0 18px 48px rgba(0, 0, 0, .6);
}

/* Малахит — светлая, в зелёном спектре. */
body.theme-malachite {
  --accent: #4E9070;
  --accent-strong: #3F7A5E;
  --accent-deep: #22503A;
  --accent-link: #3F7A5E;
  --accent-link-hover: #2F6248;
  --accent-100: #E8F3EC;
  --accent-200: #CFE6D8;
  --accent-300: #BBD9C6;
  --accent-400: #A3CDB3;

  --bg: #F7FCF9;
  --surface: #FFFFFF;
  --surface-2: #EFF6F1;
  --line: #E3EFE8;
  --line-2: #D8E8DF;

  --text: #26332C;
  --text-2: #3F5147;
  --muted: #597062;   /* на тон темнее (23.09.2026): 4,71:1 на --accent-100 */
  --hint: #5C7265;    /* на тон темнее: 4,57:1 на --accent-100 */
}

/* Охра — светлая, в песочно-янтарном спектре. */
body.theme-ochre {
  --accent: #B58044;
  --accent-strong: #99682F;
  --accent-deep: #6B4718;
  --accent-link: #93642D;   /* на тон темнее --accent-strong (23.09.2026):
                               #99682F на зебре давал 4,27:1, стало 4,57 */
  --accent-link-hover: #7D5424;
  --accent-100: #F8F1E6;
  --accent-200: #EFE0C8;
  --accent-300: #E6D3B4;
  --accent-400: #DCC59B;

  --bg: #FDFAF5;
  --surface: #FFFFFF;
  --surface-2: #F6F1E8;
  --line: #EFE8DC;
  --line-2: #E6DDCD;

  --text: #332D24;
  --text-2: #51473A;
  --muted: #776B59;
  --hint: #786C59;    /* на тон темнее (23.09.2026): 4,55:1 на --surface-2 */
}

/* Гранат — светлая, в винно-багровом спектре. */
body.theme-garnet {
  --accent: #A65B6B;
  --accent-strong: #8C4457;
  --accent-deep: #5E2836;
  --accent-link: #8C4457;
  --accent-link-hover: #743646;
  --accent-100: #F8EDF0;
  --accent-200: #EED7DE;
  --accent-300: #E4C7D0;
  --accent-400: #D9B4C0;

  --bg: #FDF8F9;
  --surface: #FFFFFF;
  --surface-2: #F7EFF1;
  --line: #F0E5E8;
  --line-2: #E7D9DD;

  --text: #33262A;
  --text-2: #513E44;
  --muted: #7A5E66;
  --hint: #84646D;    /* на тон темнее (23.09.2026): 4,56:1 на --accent-100 */
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
}

body {
  font-family: var(--font-ui);
  font-size: var(--fs-base);
  line-height: 1.5;
  color: var(--text);
  background: var(--bg);
  /* Числа и даты в таблицах должны стоять колонка в колонку — иначе
     оператор не видит разницы между 1 013 и 10 13. */
  font-variant-numeric: tabular-nums;
}

/* Заголовки. Сериф — только заголовок экрана и имена людей (README §2);
   заголовки блоков и панелей остаются на Golos, иначе сериф перестаёт
   означать «здесь имя» и превращается в украшение. */
h1, h2, h3, h4, h5, h6 { margin: 0; font-weight: var(--fw-bold); color: var(--text); }
h1 { font-family: var(--font-title); font-weight: var(--fw-normal); font-size: var(--fs-h2); line-height: 1.25; }
h2 { font-size: var(--fs-xl); line-height: 1.3; }
h3 { font-size: var(--fs-lg); line-height: 1.35; }
h4, h5, h6 { font-size: var(--fs-md); line-height: 1.4; }

/* Надзаголовок группы полей — единственный разрешённый способ группировки
   (README §4.3, раунд 2 §12 п.1): 13 px, начертание 600, обычный регистр,
   воздух сверху. Капители и разрядки в интерфейсе нет. */
.caps {
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
  color: var(--muted);
  margin-top: var(--sp-5);
}
.caps:first-child { margin-top: 0; }

a { color: var(--accent-link); text-decoration: none; }
a:hover { color: var(--accent-link-hover); text-decoration: underline; }

/* Одно глобальное правило фокуса на всё приложение (README §3.4).
   Прежде :focus-visible встречался в файле четыре раза — остальные
   контролы при работе с клавиатуры просто терялись. */
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

code, kbd, samp, pre, .mono {
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
}
code, kbd, samp {
  background: var(--surface-2);
  padding: 1px var(--sp-2);   /* 8 px: с 6 текст прилипает к рамке */
  border-radius: var(--r-chip);
}

.muted { color: var(--muted); }

/* ── Утилитарные классы вместо инлайновых style ──
   К концу переоформления в app.js оставалось около девятисот
   инлайновых `style="…"`, и почти все повторяли одно из полутора
   десятков сочетаний: мелкий кегль, отступ сверху, ширина колонки
   текста. Вынесены сюда: значение задаётся в одном месте, а не
   переписывается в каждом шаблоне. Инлайн остаётся только там, где
   значение вычисляется на ходу (ширина полосы прогресса, цвет из
   справочника). */
.t-sm { font-size: var(--fs-sm); }
.t-right { text-align: right; }
.t-danger { color: var(--danger-fg); }
.t-strong { font-weight: var(--fw-medium); }
.mt-0 { margin-top: 0; }
.mt-1 { margin-top: var(--sp-1); }
.mt-2 { margin-top: var(--sp-2); }
.mt-3 { margin-top: var(--sp-3); }
.mt-4 { margin-top: var(--sp-4); }
.mt-5 { margin-top: var(--sp-5); }
.m-0 { margin: 0; }
.p-3 { padding: var(--sp-3); }
.p-2 { padding: var(--sp-2); }
.p-4 { padding: var(--sp-4); }
.mb-2 { margin-bottom: var(--sp-2); }
.mb-3 { margin-bottom: var(--sp-3); }
.mb-4 { margin-bottom: var(--sp-4); }
.my-1 { margin: var(--sp-1) 0; }
.my-2 { margin: var(--sp-2) 0; }
.flex-1 { flex: 1; min-width: 0; }
.btn-row { display: flex; gap: var(--sp-3); flex-wrap: wrap; align-items: center; }
.pre-wrap { white-space: pre-wrap; line-height: 1.5; }
/* Ячейка подписи в таблице отчёта импорта: подпись слева, значение
   справа, без лишнего отступа между ними. */
.report-cell { padding: 3px var(--sp-3) 3px 0; }
/* Поле в строку: подпись и контрол рядом, а не друг под другом. */
.field--row { flex-direction: row; align-items: center; gap: var(--sp-2); margin: 0; }
.nowrap { white-space: nowrap; }
.field-grid--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.field-grid--21 { grid-template-columns: 2fr 1fr; }
.t-ok { color: var(--ok-fg); }
.resize-y { resize: vertical; }
/* Подтянуть блок к предыдущему: пояснение под заголовком не должно
   висеть на полном межблочном отступе. */
.mt-pull { margin-top: calc(var(--sp-1) * -1); }
.is-hidden { display: none; }
.pointer { cursor: pointer; }
/* Ширина блока с текстом: длинная строка читается тяжело, поэтому
   формы и пояснения ограничены по ширине. */
.w-form { max-width: 720px; }
.w-form-wide { max-width: 820px; }
.w-text { max-width: 980px; }
.w-full { width: 100%; }
.w-narrow { max-width: 520px; }
.w-wide { max-width: 1100px; }
.pos-rel { position: relative; }
/* Пустая ячейка и пустое значение — «—» цветом --hint, не пробел
   (README §4.1): пустота должна читаться как «данных нет», а не как
   «поле обрезалось». */
.dash { color: var(--hint); }

/* ============ Подсказки и интро в формах ============
   Унифицированные стили текста-помощника, чтобы все формы выглядели
   одинаково. Раньше в разных модалках было `font-size:12px`, `13px`,
   `14px` и три разных серых hex-кода — пользователь жаловался на разнобой.
   - .modal-intro — параграф «что делает диалог» в шапке формы.
   - .field-hint — мелкая подсказка под конкретным полем. */
/*
  С v0.69.3 подсказки и интро в формах увеличены на 1-2 px — операторы
  жаловались, что мелкий серый текст возле полей и чекбоксов трудно
  читать. Старые значения: .modal-intro 13px, .field-hint 12px.
*/
.modal-intro {
  font-size: var(--fs-md);
  color: var(--muted);
  margin: -4px 0 12px;
  line-height: 1.45;
}
.field-hint {
  font-size: var(--fs-base);
  color: var(--muted);
  margin: 4px 0 8px;
  line-height: 1.45;
}
/*
  Глобальный bump: любой <p class="muted"> и <p> внутри
  .placeholder без явно прописанного font-size получает 13 px вместо
  браузерного дефолта (12-13 в зависимости от контекста). Так подсказки
  возле чекбоксов перестают выглядеть «петитом».

  Inline style="font-size:..." всегда побеждает это правило, поэтому
  места, где намеренно мелкий шрифт нужен (нумерация в шапке таблицы,
  meta-строки в палитре), не сломаются.
*/
p.muted { font-size: var(--fs-base); line-height: 1.45; }
.placeholder p.muted, .modal__body p.muted, .doc-form p.muted { font-size: var(--fs-base); }
.placeholder .muted { line-height: 1.45; }
/* Текст вокруг inline-чекбоксов и radio. */
label > input[type="checkbox"] + *,
label > input[type="radio"] + * { font-size: var(--fs-md); }
/* Сам label с inline-чекбоксом/радио — крупнее на 1px относительно
   browser-defaults; line-height чуть свободнее для двустрочных
   подписей. */
.doc-form label:has(> input[type="checkbox"]),
.doc-form label:has(> input[type="radio"]) {
  font-size: var(--fs-md);
  line-height: 1.55;
  display: inline-flex;
  align-items: baseline;
  gap: 4px;
}
/* Если за интро/хинтом сразу идёт поле, лишний margin-top не нужен —
   между текстом и полем уже есть margin-bottom от текста. */
.modal-intro + .field, .field-hint + .field,
.modal-intro + .field-grid, .field-hint + .field-grid,
.modal-intro + .form-grid,  .field-hint + .form-grid { margin-top: 0; }

/* ============ Алёрты (success / warning / danger) ============
   Раньше status-сообщения в модалках задавались inline-style'ями
   с разными hex-цветами для одного и того же по смыслу состояния.
   Теперь — три класса поверх .alert; цвета едины и берутся
   из переменных :root. */
/* Форма у всех трёх одна (README §3.3): радиус 14, слева полоса 3 px,
   текст 15 px. Различает их только цвет — пыльная триада вне акцента,
   чтобы «внимание» и «ошибка» не спорили с голубым интерфейсом. */
.alert {
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid;
  border-left-width: 3px;
  border-radius: var(--r-card);
  font-size: var(--fs-base);
  line-height: 1.45;
}
.alert--danger { background: var(--err-bg);  color: var(--err-fg);  border-color: var(--err-line);  border-left-color: var(--err-bar); }
.alert--ok     { background: var(--ok-bg);   color: var(--ok-fg);   border-color: var(--ok-line);   border-left-color: var(--ok-bar); }
.alert--warn   { background: var(--warn-bg); color: var(--warn-fg); border-color: var(--warn-line); border-left-color: var(--warn-bar); }

/* ============ Кнопки (README §4.2) ============
   Три уровня, а не пять оттенков голубого:
     .btn              — обычная: белый фон, рамка --line-2;
     .btn--primary     — основная: заливка --accent, белый текст. Одна на экран;
     .btn--ghost       — служебная: только текст, без рамки и фона;
     .btn--danger      — опасная: контур, без заливки. Залитой красной
                         кнопки в системе не остаётся ни одной.
   Высота у всех одна — 44 px: это нижняя граница кликабельной зоны, а
   не «покрупнее». Прежние .btn--sm/.btn--xs давали 32 и 24 px, и в
   плотных строках таблиц оператор промахивался мимо них мышью. */
.btn {
  /* inline-flex выравнивает <a class="btn"> и <button class="btn"> по
     одной baseline и одинаковой высоте (line-height по высоте flex-box).
     Раньше был inline-block — у <a> и <button> разные нативные
     метрики (line-height, baseline), и в строке action'ов кнопка
     «Открыть» выглядела выше «Изменить»/«Удалить».
     С v0.69.3 добавлен min-height: 36px и box-sizing: border-box —
     это гарантирует одну высоту у соседних .btn даже если у них
     слегка разные padding'и или содержимое (text vs icon vs label).
     Размер 36 px подобран так, чтобы стандартная кнопка
     `padding:8px 14px + line-height:1.2 + 14 px шрифта` точно
     умещалась без рывка. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  border: 1px solid var(--line-2);
  background: var(--surface);
  color: var(--text-2);
  padding: 0 var(--sp-4);
  border-radius: var(--r-field);
  font-size: var(--fs-base);
  font-weight: var(--fw-medium);
  font-family: inherit;
  cursor: pointer;
  text-align: center;
  text-decoration: none;
  line-height: 1.2;
  min-height: var(--h-control);
  box-sizing: border-box;
  vertical-align: middle;
  white-space: nowrap; /* текст кнопки не переносится — высота не «прыгает» в тесном ряду */
  transition: background .1s ease, border-color .1s ease, color .1s ease;
}
.btn:hover { background: var(--accent-100); border-color: var(--accent-300); color: var(--accent-deep); }
.btn:disabled, .btn[aria-disabled="true"] { opacity: .5; cursor: not-allowed; }
.btn:disabled:hover { background: var(--surface); border-color: var(--line-2); color: var(--text-2); }

/* Основная — одна на экран. Рамки у неё нет: заливка сама достаточно
   отделяет её от соседей. Белое на --accent даёт 5,15:1. */
.btn--primary {
  background: var(--accent-strong);
  border-color: transparent;
  color: var(--on-accent);
  font-size: var(--fs-md);
}
.btn--primary:hover { background: var(--accent-deep); border-color: transparent; color: var(--on-accent); }

/* Служебная — текстовая. Ею помечают действия, которые не нужно
   замечать, пока их не ищут: «Отмена», «Сбросить», «Показать ещё». */
.btn--ghost {
  background: transparent;
  border-color: transparent;
  color: var(--muted);
}
.btn--ghost:hover { background: var(--accent-100); border-color: transparent; color: var(--accent-deep); }

/* Опасная — контур, не заливка. В подписи многоточие («Удалить
   участок…»), потому что за кнопкой всегда идёт подтверждение с
   названием объекта. */
.btn--danger {
  background: transparent;
  border-color: var(--danger-line);
  color: var(--danger-fg);
}
.btn--danger:hover { background: var(--err-bg); border-color: var(--danger-line); color: var(--danger-fg); }

/* Кнопка-иконка — 36 px квадрат, обязателен title (README §3.4).
   Это единственное разрешённое исключение из правила «44 px». */
.btn--icon {
  min-height: var(--h-icon-btn);
  width: var(--h-icon-btn);
  height: var(--h-icon-btn);
  padding: 0;
  border-radius: var(--r-icon);
  font-size: var(--fs-md);
}

/* Прежние размерные модификаторы. Высоту больше не уменьшают — оставлены
   ради разметки, которая их ещё ставит; когда этапы C–D снимут их из
   app.js, эти два правила уйдут. Разница теперь только в кегле. */
.btn--sm { font-size: var(--fs-base); }
.btn--xs { font-size: var(--fs-sm); }

/* ============ Приложение (после логина) ============ */
.app { display: flex; flex-direction: column; min-height: 100vh; }

/* Верхняя полоса — ровно 52 px (README §3.4). Высота проверяется
   замером: 56 px «почти те же» съедали строку таблицы на 1366×768, а
   на 768 по высоте каждая строка на счету. */
.topbar {
  display: flex;
  align-items: center;
  /* gap 4 и паддинг 16 (README §5.11: зазор полосы 1–4 px). Прежние 24 и
     24 съедали около 70 px, и на 1366 px пункты наезжали друг на друга. */
  gap: var(--sp-1);
  padding: 0 var(--sp-4);   /* прототип §5.11 даёт 12, но 16 — нижняя граница
                               проверки «текст не ближе 16 px к краю окна» */
  height: var(--h-topbar);
  min-height: var(--h-topbar);
  flex-shrink: 0;
  background: var(--surface);
  border-bottom: 1px solid var(--line-2);
  box-sizing: border-box;
  /* Топбар sticky'ится сверху, а доком-полоса (.doc-tabs) — под ним
     (top: 56px). Иначе на длинных страницах (карточка участка с большой
     таблицей захоронений) полоса открытых вкладок уезжала вместе со
     скроллом и казалось, что она пропала.
     Z-index 1200: топбар обязан перекрывать любые другие sticky-блоки —
     включая map-toolbar (1100), у которого внутри живёт дропдаун
     «+ Добавить участок» с z-index 1500. Без этого свои дропдауны
     топбара (Отчёты / Администрирование, z-index 100 относительно
     контекста топбара) проваливались под map-toolbar. Иерархия слоёв:
     модалки 2000 > топбар 1200 > map-toolbar 1100 > всё остальное
     (полная карта слоёв — у токенов --z-* в :root). */
  position: sticky;
  top: 0;
  z-index: var(--z-topbar);
}

/* Название программы — PT Serif. README §5.11 называет 21 px, но в
   шкале кеглей (§3.2) такого размера нет, а шкала — правило жёстче:
   берём ближайший 19 px, иначе проверка кеглей ловит нарушение на
   каждом экране. */
.topbar__brand {
  font-family: var(--font-title);
  font-weight: var(--fw-normal);
  font-size: var(--fs-xl);
  color: var(--accent-deep);
  flex: none;
  white-space: nowrap;
  margin-right: var(--sp-2);   /* без версии (ниже 1500 px) название иначе
                                  упирается в первую пилюлю */
}

.topbar__version {
  margin-left: var(--sp-2);
  font-family: var(--font-ui);
  font-weight: var(--fw-normal);
  font-size: var(--fs-sm);
  color: var(--hint);
}
/* Ниже 1500 px версия в полосе не показывается — она есть в «? → О
   программе». При 15 px в пунктах полоса на 1366 держится с запасом
   около 20 px, и «v0.150.0» (62 px) в него не входит. */
@media (max-width: 1499px) {
  .topbar__version { display: none; }
}

/* Кнопка «Быстрые команды» в топбаре — открывает Командную палитру.
   Стиль такой же, как у «Сохранить» внизу формы (.btn--primary):
   фон --primary, текст --primary-fg (тёмный на светлом primary; белый
   на blue-400 давал контраст 2.5:1). На узких экранах подсказка-клавиша
   скрывается. */
/* Быстрые команды — кнопка-иконка 36 px, как в прототипе. Подписью она
   занимала 155 px, и ровно на эту величину полоса не умещалась в
   1366 px: пункты меню наезжали друг на друга. Название и сочетание
   живут в title, поиск по действиям узнаётся по лупе.
   Заливки акцентом здесь нет: основная кнопка на экране одна, и это
   действие экрана, а не полосы. */
.topbar__quick {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: var(--h-icon-btn);
  height: var(--h-icon-btn);
  padding: 0;
  border: 1px solid var(--line-2);
  border-radius: var(--r-icon);
  background: var(--surface);
  color: var(--text-2);
  font-family: inherit;
  cursor: pointer;
  transition: background .1s ease, border-color .1s ease, color .1s ease;
}
.topbar__quick:hover {
  background: var(--accent-100);
  border-color: var(--accent-300);
  color: var(--accent-deep);
}

.topbar__nav {
  display: flex;
  gap: 1px;   /* README §5.11: 1–4 px; прототип — 1. На 1366 запас полосы
                 около 40 px — хватает на полосу прокрутки страницы (17). */
  flex: 1;
  align-items: center;
  min-width: 0;   /* иначе длинный ряд пунктов распирает полосу и та растёт */
}

/* Пункт полосы — пилюля 36 px, кегль 15 (прототип §5.11: 34 px и 15 px;
   36 — чтобы пилюли, кнопка-иконка и «Выйти» стояли в полосе одной
   высотой). Раунд 1 ставил 13 px ради ширины на 1366, и полоса выглядела
   мельче всего остального экрана — над тулбаром с контролами 44/15 это
   читалось как другой шрифт. Место добрано иначе: версия и длинное имя
   ниже 1500 px, рисованная стрелка вместо глифа, зазор 2 px.
   Пункт — строго в одну строку: перенос ломает 52 px полосы, и это ловит
   проверка 9. Селектор с «>»: ссылки внутри выпадающих меню — строки
   меню, у них своя высота (.topbar__group-menu a). */
.topbar__nav > a {
  display: inline-flex;
  flex: none;
  align-items: center;
  height: var(--h-icon-btn);
  padding: 0 var(--sp-2);   /* README §5.11 допускает 6–10 px; 8 — нижняя
                               граница проверки «текст не прилипает» */
  border-radius: var(--r-chip);
  color: var(--text-2);
  font-size: var(--fs-base);
  font-weight: var(--fw-medium);
  white-space: nowrap;
}
.topbar__nav > a:hover {
  background: var(--accent-100);
  color: var(--accent-deep);
  text-decoration: none;
}
.topbar__nav > a[aria-current="page"] {
  /* Активный раздел — пастельный фон и тёмный акцентный текст. Заливки
     акцентом здесь нет: полоса не должна спорить с основной кнопкой
     экрана, которая на странице одна. */
  background: var(--accent-100);
  color: var(--accent-deep);
}

/* Выпадающие группы в топбаре (Отчёты, Администрирование).
   Стилизуем кнопку максимально идентично .topbar__nav a,
   чтобы группы визуально не выпадали из общего ряда. */
.topbar__group { position: relative; display: inline-flex; align-items: center; flex: none; }
.topbar__group-btn {
  height: var(--h-icon-btn);
  padding: 0 var(--sp-2);   /* README §5.11 допускает 6–10 px; 8 — нижняя
                               граница проверки «текст не прилипает» */
  border-radius: var(--r-chip);
  color: var(--text-2);
  font-weight: var(--fw-medium);
  font-size: var(--fs-base);
  white-space: nowrap;
  font-family: inherit;
  line-height: 1.5;
  background: transparent;
  border: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 0;
}
/* Стрелка «есть меню» — рисованный уголок, а не глиф «▾» в тексте: один
   и тот же у пунктов полосы и у кнопок тулбара карты, не зависит от кегля
   и занимает 12 px вместо 17 (на семь пунктов полосы — 35 px ширины на
   1366). Значок «?» — тоже группа, но без стрелки. */
.topbar__group:not(.topbar__group--icon) > .topbar__group-btn::after,
.btn--menu::after {
  content: "";
  flex: none;
  width: 6px;
  height: 6px;
  margin: -3px 1px 0 4px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg);
  opacity: .8;
}
.btn--menu { gap: 0; }
.topbar__group:hover > .topbar__group-btn,
.topbar__group--open > .topbar__group-btn {
  background: var(--accent-100);
  color: var(--accent-deep);
}
.topbar__group-menu {
  display: none;
  position: absolute;
  top: 100%;
  left: 0;
  min-width: 240px;
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--r-card);
  box-shadow: var(--shadow-menu);
  padding: var(--sp-1);
  z-index: var(--z-menu);
  flex-direction: column;
}
.topbar__group:hover > .topbar__group-menu,
.topbar__group--open > .topbar__group-menu {
  display: flex;
}
/* Раскрытое меню — одно (раунд 3, 23.09.2026): пока открыто любое меню —
   щелчком в полосе, в тулбаре карты, «⋯» строки или группы вкладок
   документов, — наведение на другой раздел полосы его не раскрывает.
   Раньше соседнее меню раскрывалось от наведения поверх уже открытого
   (снимок владельца). Щелчок по разделу по-прежнему открывает его, а
   прежнее меню закрывает (app.js, closeMenusExcept). */
body:has(.topbar__group--open, .map-dropdown__menu:not([hidden]), .rowmenu__list:not([hidden]), .doc-group.is-open)
  .topbar__group:not(.topbar__group--open):hover > .topbar__group-menu {
  display: none;
}
/* Строка выпадающего меню — одна на всю программу: 36 px, кегль 15,
   обычное начертание, отступ 12. Те же числа у меню «⋯» строк таблицы,
   у меню карты и у полосы документов. Раньше здесь было 13 px в строках
   24 px (наследие правила пунктов полосы), а в меню «Слои» — 16 px в
   строках 44: два открытых рядом меню выглядели набранными по-разному.
   44 px строкам меню не дать: «Слои» с четырнадцатью строками тогда не
   входят в 768 по высоте. */
.topbar__group-menu a {
  display: flex;
  align-items: center;
  min-height: var(--h-icon-btn);
  padding: 0 var(--sp-3);
  border-radius: var(--r-chip);
  color: var(--text);
  font-size: var(--fs-base);
  font-weight: var(--fw-normal);
  white-space: nowrap;
}
.topbar__group-menu a:hover {
  background: var(--accent-100);
  color: var(--accent-deep);
  text-decoration: none;
}
.topbar__group-menu a[aria-current="page"] {
  background: var(--accent-100);
  color: var(--accent-deep);
  font-weight: var(--fw-medium);
}
/* Пункты, доступные только суперадмину, — помечены нейтральным бейджем
   «СА» (пилюля через ::after), чтобы оператор-СА видел разницу между
   «общими» и «привилегированными» функциями. Раньше подсвечивались
   оранжевым текстом — читалось как «предупреждение», треть пунктов
   «Сервиса»/«Участков» выглядела тревожно. Сам текст пункта — обычного
   цвета; ховер/активность — общие правила меню выше. Для admin'а такие
   пункты вообще скрыты (data-min-role фильтром в JS), так что бейдж
   видит только сам СА. Цвета — токены --sa-badge-* (:root + тёмная тема). */
.topbar__group-menu a[data-min-role="superadmin"]::after {
  content: "СА";
  display: inline-block;
  margin-left: 8px;
  padding: 1px 6px;
  border-radius: var(--r-pill);
  background: var(--sa-badge-bg);
  color: var(--sa-badge-fg);
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
  line-height: 1.4;
  vertical-align: 1px;
}
/* Подсветка кнопки группы, когда активен любой её пункт. Тоже
   только фоном — цвет надписи остаётся --fg, как у неактивных
   групп. См. соседний комментарий у .topbar__nav a[aria-current]. */
.topbar__group--active > .topbar__group-btn {
  background: var(--accent-100);
  color: var(--accent-deep);
}

/* Разделитель внутри выпадающего меню — визуально группирует пункты.
   Используется в «Сервис ▾» перед «⚠ Полная очистка базы» и в
   «Администрирование ▾» между «учётками» и «системой», чтобы глаз
   сразу видел два разных подсмысла. */
.topbar__group-menu-sep {
  height: 1px;
  margin: 4px 6px;
  background: var(--border);
}

/* Подзаголовок раздела внутри выпадающего меню — мелкий приглушённый
   лейбл над группой связанных пунктов (напр. «Объединение», «Доступ»,
   «Инфраструктура»). Помогает быстро найти нужный пункт в длинном меню.
   Скрывается из JS (hideEmptyMenuHeaders), если под ним не осталось
   видимых пунктов для текущей роли — поэтому нужен явный [hidden]-сброс
   (класс задаёт display, иначе перебил бы UA-правило [hidden]). */
.topbar__group-menu-header {
  padding: var(--sp-2) var(--sp-3) 2px;
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--muted);
  pointer-events: none;
  user-select: none;
}
.topbar__group-menu-header[hidden] { display: none; }

/* Опасный пункт меню — красный шрифт + предупреждающий ховер.
   Применяется к «⚠ Полная очистка базы», чтобы оператор сразу видел,
   что это деструктивная операция (и не кликнул по инерции, листая
   длинный список). Совместим с бейджем «СА» (тот красит только ::after). */
.topbar__group-menu-danger,
.topbar__group-menu a.topbar__group-menu-danger {
  color: var(--danger-fg);
  font-weight: 600;
}
.topbar__group-menu-danger:hover,
.topbar__group-menu a.topbar__group-menu-danger:hover {
  background: var(--danger-bg); /* токен темы: red-100 в светлых, тёмный в антраците */
  color: var(--danger-fg);
}

/* Иконочная группа в верхнем меню — для «?» (Справка / О программе).
   Кнопка круглая, по размеру буквы, без gap, чтобы не претендовать на
   место полноценного пункта меню. */
.topbar__group--icon > .topbar__group-btn {
  width: var(--h-icon-btn);
  height: var(--h-icon-btn);
  padding: 0;
  justify-content: center;
  border-radius: 50%;
  font-size: var(--fs-lg);
  font-weight: 600;
}
/* Меню значка раскрывается справа (а не от левого края), чтобы не
   выехать за границу окна — значок стоит в правой части шапки. */
.topbar__group--icon > .topbar__group-menu {
  left: auto;
  right: 0;
}

.topbar__user {
  display: flex;
  align-items: center;
  flex: none;
  gap: var(--sp-1);
  color: var(--text-2);     /* имя — не подпись, а кто работает: цвет текста */
  font-size: var(--fs-base);
  white-space: nowrap;
}
/* «Выйти» в полосе — 36 px, как пилюли разделов и кнопка команд:
   44 px в 52 не встают. */
.topbar__user .btn { min-height: var(--h-icon-btn); font-size: var(--fs-base); padding: 0 var(--sp-2); }
/* Длинное имя не должно распирать полосу и выдавливать разделы: режем
   многоточием, полное значение и роль остаются в подсказке. Ниже 1500 px
   предел 120 px: «Иванова О. П.» входит целиком, а «Суперадминистратор»
   тестового стенда режется — ради 15 px во всей полосе. */
#userline {
  max-width: 200px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
@media (max-width: 1499px) {
  #userline { max-width: 120px; }
}

/* Плашка «доступна новая версия» в шапке. Появляется после того,
   как суперадмин нажал «Проверить сейчас» в настройках сервера
   обновлений. Кликабельна — ведёт в /admin/updates. См. docs/05-self-update-design.md. */
.update-banner {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border-radius: var(--r-card);
  background: var(--warn-bg);     /* мягкий жёлтый — не тревожный, но заметный */
  color: var(--warn-fg);
  font-size: var(--fs-sm);
  font-weight: 600;
  text-decoration: none;
  border: 1px solid var(--warn-line);
  white-space: nowrap;
}
.update-banner:hover { background: var(--warn-bg); text-decoration: none; }
/* Атрибут hidden vs display:inline-flex — без явного override плашка
   показывается даже когда js поставил hidden=true. */
.update-banner[hidden] { display: none; }

/* Overlay при self-update: затемнённый фон + центральный «спиннер +
   сообщение». Появляется на время «сервер качает новый exe и
   перезапускается»; снимается, когда страница успешно перезагрузилась. */
.update-overlay {
  position: fixed; inset: 0;
  background: rgba(15, 23, 42, 0.78);
  display: flex; align-items: center; justify-content: center;
  z-index: var(--z-overlay);
}
.update-overlay[hidden] { display: none; }
.update-overlay__box {
  background: var(--surface); /* токен темы вместо #fff — работает и в «Антраците» */
  border-radius: var(--r-card);
  padding: 32px 40px;
  text-align: center;
  max-width: 460px;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.4);
}
.update-overlay__spinner {
  width: 40px; height: 40px;
  border: 4px solid var(--line);
  border-top-color: var(--primary);
  border-radius: 50%;
  margin: 0 auto 18px;
  animation: spin 0.9s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
.update-overlay__msg { font-size: var(--fs-base); font-weight: 500; margin-bottom: 8px; }
.update-overlay__hint { font-size: var(--fs-sm); }

.content {
  flex: 1;
  /* Горизонтальный padding 40px — на узких окнах (≤1400px max-width не
     активен) контент не «упирается в бока» с 24px-узким зазором, а
     имеет заметные поля. На широких окнах max-width центрирует блок,
     дополнительные 40px внутри визуально не мешают, но добавляют
     дыхания между текстом и краем фона. */
  padding: 24px 40px;
  max-width: 1400px;
  width: 100%;
  margin: 0 auto;
}
.content--full {
  /* режим «карта на всю ширину» — убираем max-width и padding. */
  max-width: none;
  padding: 0;
}

.placeholder { color: var(--muted); }
.placeholder h1 { color: var(--fg); margin-top: 0; font-size: var(--fs-h2); }
.placeholder h2 { color: var(--fg); margin-top: 32px; font-size: var(--fs-lg); }

/* --- Section page ---
   Заголовок + кнопки действий (Сохранить / Удалить / Назад / индикатор
   автосейва) приклеены к верху страницы, чтобы оператор всегда видел
   действия даже на длинных формах (карточка участка, запись книги).
   top: 56px — это высота топбара. Полоса doc-tabs теперь живёт в
   подвале, конкуренции за верхнюю «полку» нет. */
.page__header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 20px;
  gap: 12px;
  padding: 8px 0 6px;
}
/* page__header--sticky оставляет шапку «прилепленной» под топбаром.
   Используется только на длинных формах (карточка записи с табами,
   карточка участка с большой таблицей захоронений), где оператор
   часто скроллит и важно иметь название/кнопки под рукой. Списки
   справочников НЕ помечаются sticky — оператор предпочитает, чтобы
   шапка уезжала вместе с топбаром при скролле длинного списка. */
.page__header--sticky {
  position: sticky;
  top: var(--h-topbar);
  background: var(--bg);
  z-index: var(--z-sticky-head);
}
/* Legacy: .page__header--static больше ничего не делает (sticky убран
   из дефолта). Оставлен для обратной совместимости с местами, где он
   уже выставлен в HTML. */
.page__header--static { }
/* Заголовок экрана — 25 px серифом (README §3.2). 30 px оставлен ФИО в
   карточке записи: там это имя человека, а не название экрана. */
.page__header h1 {
  margin: 0;
  font-family: var(--font-title);
  font-weight: var(--fw-normal);
  font-size: var(--fs-h2);
  line-height: 1.2;
}
.page__header .muted {
  font-size: var(--fs-base);
}

/* --- Правила определения источника (README §5.5) --- */
.rule-legend {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  margin-bottom: var(--sp-4);
  font-size: var(--fs-base);
  color: var(--text-2);
}
.rule-matrix { max-width: 640px; margin-bottom: var(--sp-5); }
.rules-table__attr { font-weight: var(--fw-medium); }
.rules-table__desc { color: var(--muted); font-size: var(--fs-sm); white-space: normal; }
/* Строка правил выше обычной: в ней стоит переключатель из трёх
   сегментов, и 44 px ему тесны (README §5.5 — строка 52 px). */
.rules-table td { height: var(--h-row-comfort); }
.rules-table__cat { width: 430px; }
/* Сегменты-радиокнопки: сам input скрыт, видимая часть — подпись. */
.seg--rule { gap: 0; margin-left: 0; }
.seg--rule .seg__btn { display: inline-flex; align-items: center; margin: 0; }
.seg--rule .seg__btn + .seg__btn { margin-left: -1px; }
.seg--rule input { position: absolute; width: 1px; height: 1px; opacity: 0; }
.seg--rule input:focus-visible + span { outline: 2px solid var(--accent); outline-offset: 2px; }

/* --- Настройки: разделы по аудиториям (README §5.9) ---
   Подпись и пояснение слева, контрол справа: так видно, что настройка
   одна, а не «подпись, потом ещё что-то ниже». */
.settings { display: flex; flex-direction: column; gap: var(--sp-5); }
.settings__group {
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--r-card);
  padding: var(--sp-5);
}
.settings__title { font-size: var(--fs-lg); font-weight: var(--fw-bold); margin: 0 0 var(--sp-4); }
.settings__row {
  display: grid;
  grid-template-columns: minmax(0, 280px) minmax(0, 1fr);
  gap: var(--sp-5);
  padding: var(--sp-4) 0;
  border-top: 1px solid var(--line);
}
.settings__row:first-of-type { border-top: none; padding-top: 0; }
@media (max-width: 900px) {
  .settings__row { grid-template-columns: minmax(0, 1fr); gap: var(--sp-2); }
}
.settings__label { display: flex; flex-direction: column; gap: var(--sp-1); min-width: 0; }
.settings__name { font-weight: var(--fw-medium); }
.settings__hint { font-size: var(--fs-sm); color: var(--muted); line-height: 1.45; }
.settings__more { font-size: var(--fs-sm); }
.settings__control { display: flex; flex-direction: column; gap: var(--sp-2); min-width: 0; }
.settings__control .field { margin-bottom: 0; }
.settings__details {
  padding: var(--sp-3);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  background: var(--surface-2);
}
.settings__details[hidden] { display: none; }
/* Образцы цвета темы — три квадрата 16 px рядом с подписью: выбирать
   тему по словам «песочно-янтарная» приходилось вслепую. */
.theme-swatches { display: inline-flex; gap: 3px; flex: none; margin-top: 2px; }
.theme-swatch { width: 16px; height: 16px; border-radius: 5px; border: 1px solid var(--line-2); }

/* --- Пара дат «с … по …» одной строкой (README §5.10) ---
   Каждая пара прежде стояла в рамке-fieldset с врезанной подписью, и три
   рамки подряд читались как три раздела формы. */
.daterange {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  flex-wrap: wrap;
  margin-bottom: var(--sp-3);
}
.daterange__label { flex: 0 0 150px; font-size: var(--fs-sm); color: var(--muted); }
.daterange__side {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  flex: 1 1 150px;
  min-width: 0;
  margin: 0;
}
.daterange__side > span { font-size: var(--fs-sm); color: var(--muted); }
.daterange__side input {
  flex: 1 1 auto;
  min-width: 0;
  min-height: var(--h-control);
  padding: 0 var(--sp-3);
  border: 1px solid var(--line-2);
  border-radius: var(--r-field);
  background: var(--surface-2);
  color: var(--text);
  font: inherit;
  font-size: var(--fs-base);
}

/* Колонки выгрузки — по смысловым группам со счётчиком в шапке. */
.cols-head {
  display: flex;
  align-items: baseline;
  gap: var(--sp-3);
  flex-wrap: wrap;
  margin-bottom: var(--sp-3);
}
.cols-head__title { font-size: var(--fs-lg); margin: 0; }
.cols-head__count { font-size: var(--fs-sm); color: var(--muted); }
.cols-head__actions { display: flex; gap: var(--sp-2); margin-left: auto; }
.cols-groups { display: flex; flex-direction: column; gap: var(--sp-4); }
.cols-group { display: flex; flex-direction: column; }
.cols-group__title { margin-bottom: var(--sp-1); }
.cols-item {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: var(--h-control);
  cursor: pointer;
  margin: 0;
}
/* Чип-счётчик найденного под фильтрами. */
.chip--count { margin-top: var(--sp-3); }
.chip--count[hidden] { display: none; }
.report-status { margin-top: var(--sp-4); font-size: var(--fs-sm); color: var(--muted); }

/* --- Пронумерованные шаги (README §5.4) ---
   Экраны обмена и сервисные операции идут шагами: файл → параметры →
   проверка и применение. Номер в кружке показывает, что порядок не
   произвольный, а заголовок шага отвечает на вопрос «что я сейчас
   делаю». Прежде такие экраны начинались со сплошного вводного текста,
   который никто не читал. */
.steps {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  max-width: 980px;
}
.step {
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--r-card);
  padding: var(--sp-5);
}
.step__head {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin-bottom: var(--sp-4);
}
.step__num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  flex: none;
  border-radius: var(--r-pill);
  background: var(--accent-100);
  color: var(--accent-deep);
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
}
.step__title { font-size: var(--fs-lg); font-weight: var(--fw-bold); margin: 0; }
.step__body { display: flex; flex-direction: column; gap: var(--sp-3); }
.step__actions { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; }
.step__extra {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  padding-top: var(--sp-4);
  border-top: 1px solid var(--line);
}

/* Взаимоисключающие варианты — радиокнопки, а не набор галочек: набор
   допускает «выбрано два» и «не выбрано ничего» (README §4.3). */
.radio-list { display: flex; flex-direction: column; gap: var(--sp-2); }
.radio-row, .check-row {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  min-height: var(--h-control);
  padding: var(--sp-3);
  border: 1px solid var(--line-2);
  border-radius: var(--r-card);
  background: var(--surface);
  cursor: pointer;
}
.radio-row:hover, .check-row:hover { border-color: var(--accent-300); background: var(--accent-100); }
.radio-row:has(input:checked) {
  border-color: var(--accent);
  background: var(--accent-100);
}
.radio-row input, .check-row input { margin-top: 2px; }
.radio-row__text { display: flex; flex-direction: column; gap: var(--sp-1); min-width: 0; }
.radio-row__title { font-weight: var(--fw-medium); }
.radio-row__hint { font-size: var(--fs-sm); color: var(--muted); line-height: 1.4; }

/* Закреплённый подвал длинной формы (README §4.3): кнопка сохранения или
   применения всегда на виду, слева — что важно знать до нажатия. */
.sticky-foot {
  position: sticky;
  /* Прилипание считается от окна, а не от padding'а body, поэтому
     полосу открытых документов приходится учитывать явно: иначе
     кнопка сохранения оказывается наполовину под ней. */
  bottom: var(--sp-3);
  z-index: var(--z-sticky-head);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  flex-wrap: wrap;
  margin-top: var(--sp-5);
  padding: var(--sp-3) var(--sp-4);
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--r-card);
  box-shadow: var(--shadow-menu);
}
body.has-doc-tabs .sticky-foot { bottom: calc(var(--h-doc-tabs) + var(--sp-3)); }
.sticky-foot__note { font-size: var(--fs-sm); }
.sticky-foot__actions { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }

/* Итоги операции карточками чисел (README §5.4): «Сопоставлено 1 214»
   читается с одного взгляда, строка отчёта — нет. */
.result-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: var(--sp-3);
}
.result-card {
  padding: var(--sp-4);
  border: 1px solid var(--line-2);
  border-radius: var(--r-card);
  background: var(--surface);
}
.result-card__num { font-size: var(--fs-h2); font-family: var(--font-title); line-height: 1.1; }
.result-card__label { font-size: var(--fs-sm); color: var(--muted); margin-top: var(--sp-1); }
.result-card--warn { background: var(--warn-bg); border-color: var(--warn-line); }
.result-card--warn .result-card__num { color: var(--warn-fg); }
.result-card__link { display: inline-flex; align-items: center; min-height: var(--h-control); font-size: var(--fs-sm); }

/* Подробный отчёт под спойлером: главные числа — карточками выше, а
   двадцать строк частностей нужны не каждый раз. */
.result-details {
  border: 1px solid var(--line-2);
  border-radius: var(--r-card);
  background: var(--surface);
  padding: var(--sp-3) var(--sp-4);
}
.result-details > summary {
  cursor: pointer;
  min-height: var(--h-control);
  display: flex;
  align-items: center;
  font-weight: var(--fw-medium);
}
.result-details table { font-size: var(--fs-base); border-collapse: collapse; }

/* --- Шапка карточки записи (README §5.3) --- */
.page__header--card {
  display: block;
  padding: 0;
  margin-bottom: var(--sp-5);
}
.page__crumbs {
  font-size: var(--fs-sm);
  color: var(--muted);
  margin-bottom: var(--sp-2);
}
.page__crumbs a { color: var(--accent-link); }
/* Счётчик рядом с заголовком экрана: «(всего 17)». */
.page__count { font-size: var(--fs-base); font-family: var(--font-ui); }
.page__title-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--sp-4);
  flex-wrap: wrap;
}
/* Колонка с именем сжимается, кнопки — нет: иначе ряд действий уезжает
   под заголовок и читается как часть записи, а не как её действия. */
.page__title-col { min-width: 0; flex: 1 1 320px; }
.page__title-row .page__actions { flex: 0 0 auto; }
.card-title {
  font-family: var(--font-title);
  font-weight: var(--fw-normal);
  font-size: var(--fs-h1);
  line-height: 1.15;
  margin: 0;
}
.card-subtitle {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  flex-wrap: wrap;
  margin-top: var(--sp-2);
  font-size: var(--fs-base);
  color: var(--text-2);
}

/* --- Обзор записи: три колонки 1fr 1fr 372px --- */
.card-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) var(--w-plot-panel);
  gap: var(--sp-4);
  align-items: start;
}
/* На 1366 px третья колонка уезжает вниз: три колонки по 372 плюс поля
   требуют больше ширины, чем есть, и поля сжимаются до нечитаемых. */
@media (max-width: 1500px) {
  .card-grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .card-col--side { grid-column: 1 / -1; }
}
@media (max-width: 900px) {
  .card-grid { grid-template-columns: minmax(0, 1fr); }
}
.card-col { display: flex; flex-direction: column; gap: var(--sp-4); min-width: 0; }
.card-block {
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--r-card);
  padding: var(--sp-4);
}
.card-block__title {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-3);
  margin-bottom: var(--sp-2);
}
.card-block__link {
  display: inline-flex;
  align-items: center;
  min-height: var(--h-control);
  font-size: var(--fs-sm);
}
.card-block__action { margin-top: var(--sp-3); width: 100%; }

/* Строка «подпись — значение»: подпись слева обычным текстом, значение
   справа — так глаз ведёт по колонке значений, а не выискивает их
   среди подписей. */
.card-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-4);
  min-height: var(--h-row);
  padding: var(--sp-2) 0;
  border-bottom: 1px solid var(--line);
}
.card-row:last-child { border-bottom: none; }
.card-row__label { color: var(--text-2); }
.card-row__value { text-align: right; font-weight: var(--fw-medium); min-width: 0; overflow-wrap: anywhere; }
.card-row--wide { flex-direction: column; align-items: stretch; gap: var(--sp-1); }
.card-row--wide .card-row__value { text-align: left; }

/* Строка соседа по участку — как в панели участка на карте. */
.card-person {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-2);
  min-height: var(--h-row);
  padding: 0 var(--sp-3);
  margin-bottom: var(--sp-2);
  border: 1px solid var(--line-2);
  border-radius: var(--r-chip);
  color: var(--text);
}
.card-person:hover { background: var(--accent-100); border-color: var(--accent-300); text-decoration: none; }
.card-person__fio { font-family: var(--font-title); font-size: var(--fs-md); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.card-person__dates { font-size: var(--fs-sm); white-space: nowrap; }

/* Подвал карточки: предупреждение слева, опасное действие справа. */
.card-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  flex-wrap: wrap;
  margin-top: var(--sp-5);
  padding: var(--sp-3) var(--sp-4);
  background: var(--surface-2);
  border: 1px solid var(--line-2);
  border-radius: var(--r-card);
}
.card-foot__note { font-size: var(--fs-sm); }
.card-foot__actions { display: flex; gap: var(--sp-2); }
/* Мелкая «eyebrow»-подпись над h1 — для уточнения типа документа
   (например «Запись инвентаризации» над ФИО). */
.page__eyebrow {
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
  color: var(--muted);
  margin-bottom: 2px;
}

.toolbar {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 16px;
}
.toolbar .search {
  flex: 1;
  max-width: 320px;
  padding: 8px 12px;
  border: 1px solid var(--border);
  border-radius: var(--r-chip);
  font-size: var(--fs-md);
  background: var(--surface);
}

/* --- Таблицы (README §4.1) ---
   overflow: hidden обрезал бы sticky-шапку, поэтому у обёртки clip по
   горизонтали и auto по вертикали: углы скруглены, а `th` остаётся
   прилипшим при прокрутке длинного реестра. */
.table-wrap {
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--r-card);
  overflow-x: auto;
  overflow-y: visible;
}
/* Если в .table-wrap вместо таблицы лежит «пустое состояние» (текст
   «Записей нет», «Журнал появится после первой выгрузки» и т.п.) —
   таблица не задаёт padding'и, текст прилипает к рамке. Добавляем
   собственный отступ для прямых дочерних элементов-заглушек. */
.table-wrap > p,
.table-wrap > .empty-state { padding: 14px 16px; margin: 0; }

/* ============ Универсальное «пустое состояние» ============
   Используется в местах, где список пуст и нечего показать —
   таблицы, реестры, истории, журналы. Класс задаёт центрированный
   muted-текст с приличным отступом, чтобы не смотрелось как ошибка
   рендеринга. */
.empty-state {
  color: var(--muted);
  font-size: var(--fs-base);
  text-align: center;
  padding: 24px 16px;
}
.empty-state__title { font-weight: 600; color: var(--fg); margin-bottom: 4px; }

table.table {
  width: 100%;
  border-collapse: separate;   /* collapse ломает sticky-шапку: границы уезжают */
  border-spacing: 0;
  font-size: var(--fs-base);
}
table.table th,
table.table td {
  text-align: left;
  /* Вертикальные 4 px нужны только многострочным ячейкам: высота строки
     задана отдельно, и однострочные от этого не растут, а перенесённый
     адрес перестаёт упираться в линию строки (раунд 2, §12 п.7). */
  padding: var(--sp-1) var(--sp-3);
  border-bottom: 1px solid var(--line);
  vertical-align: middle;
}
table.table td { height: var(--h-row); }

/* Шапка прилипает к верху обёртки — иначе на длинном реестре оператор
   через полсотни строк уже не помнит, какая колонка где. Фон
   обязателен: без него строки просвечивают сквозь шапку. */
table.table th {
  position: sticky;
  top: 0;
  z-index: 2;
  height: var(--h-thead);
  background: var(--accent-100);
  color: var(--accent-deep);
  font-weight: var(--fw-bold);
  font-size: var(--fs-sm);
  white-space: nowrap;
}

/* Зебра: сплошная сетка линий утомляет, чередование фона читается легче. */
table.table tbody tr:nth-child(even) > td { background: var(--surface-2); }
table.table tbody tr:hover > td { background: var(--accent-100); }

/* Выделенная строка — фон плюс полоса слева: только по цвету фона
   выбор теряется среди зебры. */
table.table tbody tr.is-selected > td,
table.table tbody tr[aria-selected="true"] > td {
  background: var(--pick-bg);
  box-shadow: inset 3px 0 0 var(--pick-bar);
}

table.table tr:last-child td { border-bottom: none; }
table.table .row-actions {
  text-align: right;
  white-space: nowrap;
}

/* Числа и даты — по правому краю, моноширинными цифрами: так столбец
   читается сверху вниз, а не «пляшет». */
table.table td.num, table.table th.num,
table.table td.col-date, table.table th.col-date { text-align: right; }

/* Плотность строк — переключатель у таблицы, выбор в localStorage
   (README §4.1). По умолчанию «обычно». */
.table--compact td { height: var(--h-row-compact); }
.table--comfort td { height: var(--h-row-comfort); }

/* Значок сортировки — только у активной колонки и при наведении.
   Прежде он стоял у всех четырнадцати колонок сразу и давал основную
   часть шума в шапке. */
table.table th.col-sort { cursor: pointer; }
table.table th.col-sort .sort-ind { opacity: 0; transition: opacity .1s ease; }
table.table th.col-sort:hover .sort-ind,
table.table th[aria-sort] .sort-ind { opacity: 1; }

/* Группировка строк в табличных справочниках с заголовками-разделителями
   (используется на /burial-types для блоков «Семейные», «Правовой
   статус», «Прочие», «Импортированные»). Заголовок — узкая полоска
   с приглушённым фоном; «Импортированные» дополнительно выделяются
   жёлтой левой полоской, чтобы отличить пользовательские данные от
   системного seed'а. */
.table--grouped .bt-group-row td {
  background: var(--surface-alt);
  padding: 8px 12px;
  font-size: var(--fs-sm);
  border-top: 2px solid var(--border);
  border-bottom: 1px solid var(--border);
}
.table--grouped .bt-group-row:first-child td { border-top: none; }
.table--grouped .bt-group-row--imported td {
  border-left: 3px solid var(--warn-bar);
  background: var(--warn-bg);
}
.bt-group-title {
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
  color: var(--accent-deep);
}
.bt-group-count {
  margin-left: var(--sp-2);
  font-size: var(--fs-sm);
  font-weight: var(--fw-normal);
}
table.table .row-actions button {
  padding: 0 var(--sp-3);
  font-size: var(--fs-sm);
  margin-left: var(--sp-1);
}
table.table .empty-row td {
  text-align: center;
  color: var(--muted);
  padding: var(--sp-6);
}

/* --- Color swatch --- */
.swatch {
  display: inline-block;
  width: 14px;
  height: 14px;
  border-radius: var(--r-chip);
  border: 1px solid var(--line-2);
  vertical-align: middle;
  margin-right: 6px;
  background: var(--swatch-fallback); /* пустой color у burial_type → нейтральный slate */
}
/* Маленький круглый swatch — для плотных списков и индикаторов в заголовках. */
.swatch--dot   { width: 10px; height: 10px; border-radius: 50%; }
.swatch--mini  { width: 8px;  height: 8px;  border-radius: 50%; margin-right: 4px; }

/* --- Modal ---
   Двухсекционный layout: «шапка» (заголовок + действия) приклеена сверху,
   «тело» формы скроллится. На длинных формах (новая запись книги, форма
   участка) кнопки «Сохранить / Отмена» остаются всегда видимыми, не
   нужно проматывать форму до низа. */
.modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(17, 24, 39, 0.5);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 40px 16px;
  /* Должен быть выше любых Leaflet-контролов (у них z-index ~1000) и
     popup'ов (~700). Иначе на странице карты модалка скрывается за картой. */
  z-index: var(--z-modal);
  /* overflow:hidden — скролл живёт внутри .modal__body, чтобы шапка
     с кнопками действительно «приклеилась». */
  overflow: hidden;
  box-sizing: border-box;
}
/* Одна оболочка на все 55 диалогов (README §5.3). Ширины — три
   типоразмера: 480 обычный, 720 средний (.modal--wide), 1180 широкий
   (.modal--xl). Диалоги подтверждения — та же оболочка, узкая. */
.modal {
  background: var(--surface);
  width: 100%;
  max-width: 480px;
  border-radius: var(--r-card);
  box-shadow: var(--shadow-modal);
  display: flex;
  flex-direction: column;
  max-height: calc(100vh - 80px);
  overflow: hidden; /* чтобы скруглённые углы не съедались body */
}
.modal__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: var(--sp-4) var(--sp-5);
  border-bottom: 1px solid var(--line);
  background: var(--surface);
  flex-shrink: 0;
}
.modal__head h2 { margin: 0; font-size: var(--fs-xl); font-weight: var(--fw-bold); }
.modal__body {
  padding: var(--sp-5);
  overflow-y: auto;
  flex: 1 1 auto;
}
/* Подвал с кнопками: на длинной форме «Сохранить» не должно уезжать
   за нижний край — его ищут внизу и не находят. */
.modal__actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--sp-2);
  flex-shrink: 0;
}
/* Подвал диалога. Два класса делают одно и то же по историческим причинам:
   `.modal__foot` — у диалогов, переделанных в интерфейсе v2, `.modal__actions`
   — у остальных (openPrompt, «Перезапуск», «Поддержка», alert, choice). Пока
   правило было только у `.modal__foot`, у остальных кнопки стояли вплотную к
   краям окна: снимок владельца от 24.09.2026, диалог «Объект инфраструктуры»
   при создании маркера на карте.
   Отбор `.modal > .modal__actions` — только прямой потомок окна, то есть
   именно подвал. Тот же класс внутри `.modal__head` (пароль пользователя) и
   внутри `.modal__foot` (confirm, openModal) — это группа кнопок, ей подвальные
   отступы и рамка не нужны. */
.modal__foot,
.modal > .modal__actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-5);
  border-top: 1px solid var(--line);
  background: var(--surface-2);
  flex-shrink: 0;
}
.modal--xl { max-width: 1180px; }
.modal--confirm { max-width: 480px; }
.confirm-text { white-space: pre-wrap; line-height: 1.5; }

/* Шапка диалога: заголовок и крестик. Кнопки действий переехали в
   подвал — на широком диалоге тянуться за ними вверх неудобно, а
   «Сохранить» ищут внизу формы. */
.modal__head .btn--icon { flex: none; }
.modal__foot-note { min-width: 0; flex: 1 1 auto; font-size: var(--fs-sm); color: var(--muted); }
/* Поле в подвале диалога — подпись слева, ввод справа, одной строкой. */
.field--inline {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin: 0;
  flex-direction: row;
}
.field--inline > span { flex: none; font-size: var(--fs-sm); color: var(--muted); }
.field--inline input { max-width: 220px; }

/* Значение, которое считается само, а не вводится: выглядит как текст,
   а не как пустое заблокированное поле, в которое пытаются печатать. */
.readonly-value {
  display: flex;
  align-items: center;
  min-height: var(--h-control);
  padding: 0 var(--sp-3);
  border: 1px solid transparent;
  border-radius: var(--r-field);
  background: var(--surface-2);
  color: var(--text);
  font-weight: var(--fw-medium);
}

/* Карта периметра в диалоге кладбища. */
.cemetery-map-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-3);
  margin-bottom: var(--sp-2);
}
.cemetery-map-help {
  margin: 0 0 var(--sp-3);
  padding-left: 18px;
  font-size: var(--fs-sm);
  color: var(--muted);
  line-height: 1.5;
}
.cemetery-mini-map {
  height: 340px;
  width: 100%;
  min-width: 0;
  border: 1px solid var(--line-2);
  border-radius: var(--r-card);
  overflow: hidden;
}
/* Ни одна колонка не распирает сетку: Leaflet тянет контейнер по
   тайлам, а select со статусом «Действующее, открытое» — по самому
   длинному варианту. Без min-width колонка полей вылезала под карту. */
.cemetery-form-grid__col--map,
.cemetery-form-grid__col--map .field,
.cemetery-form-grid__col--fields,
.cemetery-form-grid__col--fields .field,
.cemetery-form-grid__col--fields .field-grid,
.cemetery-form-grid__col--fields .field-grid > * { min-width: 0; }
.cemetery-form-grid__col--fields select,
.cemetery-form-grid__col--fields input { max-width: 100%; }
.cemetery-map-toolbar {
  display: flex;
  gap: var(--sp-2);
  flex-wrap: wrap;
  margin-top: var(--sp-3);
}
.modal__error {
  margin-top: 12px;
  padding: 8px 12px;
  /* danger-токены темы вместо захардкоженного red-100 + red-600 —
     плашка читаема и в тёмной теме. */
  background: var(--danger-bg);
  color: var(--danger-fg);
  border-radius: var(--r-chip);
  font-size: var(--fs-base);
}
/* --- Progress bar ---
   Используется на странице резервного копирования БД: рядом с
   «⤓ Скачать архив БД» / «⤒ Загрузить архив БД» появляется полоска
   с реальным процентом (для download — через response.body.getReader(),
   для upload — через XHR.upload.onprogress).
   .progress__bar--indeterminate — бегущая «зебра» когда сервер не
   отдал Content-Length и точный процент неизвестен. */
.progress {
  width: 100%;
  max-width: 480px;
  height: 22px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r-chip);
  overflow: hidden;
  position: relative;
}
.progress__bar {
  height: 100%;
  background: var(--accent);
  width: 0%;
  transition: width 200ms ease-out;
}
.progress__bar--indeterminate {
  background-image: linear-gradient(
    45deg,
    rgba(255, 255, 255, 0.25) 25%,
    transparent 25%,
    transparent 50%,
    rgba(255, 255, 255, 0.25) 50%,
    rgba(255, 255, 255, 0.25) 75%,
    transparent 75%,
    transparent
  );
  background-size: 24px 24px;
  animation: progress-stripes 800ms linear infinite;
}
@keyframes progress-stripes {
  from { background-position: 0 0; }
  to   { background-position: 24px 0; }
}
.progress__text {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--fs-sm);
  color: var(--text);
  pointer-events: none;
  text-shadow: 0 0 4px rgba(255, 255, 255, 0.6);
}
/* Блок одноразового показа сгенерированного пароля. user-select:all
   делает выделение по одному клику — легче скопировать. */
.password-display {
  padding: 12px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r-chip);
  font-family: ui-monospace, "SFMono-Regular", Consolas, "Liberation Mono", monospace;
  font-size: var(--fs-base);
  word-break: break-all;
  user-select: all;
  margin-top: 8px;
}

/* --- Формы (README §4.3) ---
   Подпись над полем 13 px цветом --muted, поле 44 px с фоном --surface-2
   и рамкой --line-2, пояснение под полем 13 px одной строкой. Класс
   .form-input — для одиночных инпутов без обёртки .field (быстрый поиск
   в тулбарах и т. п.). */
.field {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);           /* подпись → поле */
  margin-bottom: var(--sp-4); /* между полями */
}
.field > span {
  font-size: var(--fs-sm);
  color: var(--muted);
}
.field input[type="text"],
.field input[type="number"],
.field input[type="date"],
.field input[type="time"],
.field input[type="datetime-local"],
.field input[type="search"],
.field input[type="email"],
.field input[type="tel"],
.field input[type="url"],
.field input[type="password"],
.field select,
.field textarea,
.form-input {
  padding: 0 var(--sp-3);
  border: 1px solid var(--line-2);
  border-radius: var(--r-field);
  font-size: var(--fs-base);
  background: var(--surface-2);
  color: var(--text);
  font-family: inherit;
  line-height: 1.3;
  min-height: var(--h-control); /* поля и кнопки одной высоты в общем ряду */
  box-sizing: border-box;
}
.field input:focus,
.field select:focus,
.field textarea:focus,
.form-input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--focus-ring);
}
.field input:disabled,
.field select:disabled,
.field textarea:disabled,
.form-input:disabled {
  background: var(--surface-2);
  color: var(--muted);
  cursor: not-allowed;
}
.field textarea { min-height: 88px; padding: var(--sp-3); resize: vertical; }

/* Селект приводится к виду текстового поля: нативную стрелку убираем и
   рисуем свою фоном (README §4.4). Иначе в одном ряду поле и селект
   отличаются высотой, рамкой и радиусом — ряд «пляшет». */
.field select,
select.form-input {
  appearance: none;
  -webkit-appearance: none;
  padding-right: var(--sp-8);
  background-image:
    linear-gradient(45deg, transparent 50%, var(--muted) 50%),
    linear-gradient(135deg, var(--muted) 50%, transparent 50%);
  background-position:
    calc(100% - 18px) calc(50% + 2px),
    calc(100% - 12px) calc(50% + 2px);
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
}

/* Чекбокс и радио — 20 × 20, клик по всей подписи (README §3.4).
   Высоту 44 px даёт подпись-обёртка: сам квадратик кликабельной зоной
   не считается. */
input[type="checkbox"],
input[type="radio"] {
  width: var(--box-check);
  height: var(--box-check);
  accent-color: var(--accent);
  margin: 0;
  cursor: pointer;
  flex: none;
}
label:has(> input[type="checkbox"]),
label:has(> input[type="radio"]) {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: var(--h-control);
  cursor: pointer;
}

/* Своё файловое поле (README §4.4). Нативный input не стилизуется, его
   прячут прозрачным пикселем — не display:none, иначе поле пропадает
   из обхода по Tab и с клавиатуры файл не выбрать. */
.filefield {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  cursor: pointer;
  border: 1px dashed var(--accent-300);
  border-radius: var(--r-card);
  background: var(--surface-2);
  padding: var(--sp-4);
  min-height: var(--h-control);
}
.filefield__input { position: absolute; width: 1px; height: 1px; opacity: 0; }
.filefield__input:focus-visible + .btn { outline: 2px solid var(--accent); outline-offset: 2px; }
.filefield__name { font-size: var(--fs-sm); color: var(--muted); }
.filefield__name--filled { color: var(--text); }
.filefield.is-over { border-color: var(--accent); background: var(--accent-100); }
.field.row {
  flex-direction: row;
  align-items: center;
  gap: 10px;
}
.field.row input[type="color"] {
  width: 40px;
  height: 32px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--r-chip);
  cursor: pointer;
}
.field-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
}
.field-grid .field { margin-bottom: 0; }
/* Тройной grid — для строк «Кладбище / Квартал / Ряд» и
   «Длина / Ширина / Площадь» в форме участка. */
.field-grid--3 { grid-template-columns: repeat(3, 1fr); }

/* Строка фильтров над таблицей справочников (например /plots:
   селектор кладбища + селектор квартала). align-items: end —
   подписи и сами поля выравниваются по нижнему краю в одну линию,
   независимо от длины подписи. !important нужен потому что
   renderCemeteryFilter ставит inline-style margin-bottom для
   обратной совместимости со страницами без .filter-row. */
.filter-row {
  display: flex;
  gap: 16px;
  align-items: center;
  flex-wrap: wrap;
  margin-bottom: 12px;
}
.filter-row .field,
.filter-row label.field.row { margin-bottom: 0 !important; }
.filter-row .field.row > span { font-size: var(--fs-md); color: var(--fg); }
.filter-row > * { flex-shrink: 0; }

/* Кликабельные заголовки таблиц со стрелками сортировки. */
.table th.sortable:hover { background: var(--surface-alt); }
/* Столбец, по которому отсортировано, — цветом --accent-deep: --accent
   (--primary) по README — цвет заливок, а не текста, и на голубой шапке
   давал 3,31:1. От остальных заголовков отличается тоном и стрелкой. */
.table th.sortable--active { color: var(--accent-deep); }
.table .sort-arrow { font-size: var(--fs-sm); }
.table .sort-arrow--idle { opacity: 0.35; }

/* Иконка компаса для кнопок «Показать на карте». Цвет берётся из
   --primary через mask-image — кнопка перекрашивается синхронно с
   темой. Раньше использовался эмодзи 📍 (всегда чёрный, мелкий и
   разный шрифт под Win/macOS) — на скриншоте выглядел инородно. */
.icon-compass {
  display: inline-block;
  width: 16px;
  height: 16px;
  vertical-align: -3px;
  margin-right: 2px;
  background-color: var(--primary);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm-1.5-13.5L7 14.5l8-3.5 3.5-8-8 3.5z'/%3E%3C/svg%3E") no-repeat center / contain;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm-1.5-13.5L7 14.5l8-3.5 3.5-8-8 3.5z'/%3E%3C/svg%3E") no-repeat center / contain;
}

/* --- Catalog hub (карточки) --- */
/* Пять колонок на широком экране, 12 px зазор (README §5.6). */
.catalog-cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  gap: var(--sp-3);
  margin-top: var(--sp-2);
}
.catalog-card {
  display: block;
  padding: var(--sp-4) 18px;
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--r-card);
  text-decoration: none;
  color: var(--text);
  transition: border-color .08s ease, background .08s ease;
}
.catalog-card:hover {
  border-color: var(--accent-300);
  background: var(--accent-100);
  text-decoration: none;
}
/* Название плитки серифом 19 px — это имя раздела, как заголовок
   панели. */
.catalog-card__title {
  font-family: var(--font-title);
  font-weight: var(--fw-normal);
  font-size: var(--fs-xl);
  line-height: 1.2;
  margin-bottom: var(--sp-1);
}
.catalog-card__hint {
  font-size: var(--fs-sm);
  color: var(--muted);
  line-height: 1.4;
}
/* Счётчик записей: пока не загрузился — пустая строка, но место под
   него зарезервировано, иначе плитки прыгают при появлении чисел. */
.catalog-card__count {
  margin-top: var(--sp-2);
  min-height: 19px;
  font-size: var(--fs-sm);
  color: var(--accent-deep);
}

/* Группы справочников — разделение по смыслу. Принадлежность к группе
   показывает подписанный заголовок; цветные полосы слева у карточек
   убраны: красная у «Кладбищ» читалась как ошибка, а четыре цвета
   означали не состояние, а всего лишь раздел. */
.catalog-group { margin-top: var(--sp-5); }
.catalog-group:first-of-type { margin-top: var(--sp-2); }
.catalog-group__title {
  /* Типографика — из .caps (класс висит в разметке), здесь только отступы. */
  margin: 0 0 var(--sp-2);
}

/* --- Карта --- */
.map-page {
  display: flex;
  flex-direction: column;
  height: calc(100vh - var(--h-topbar)); /* высота верхней полосы — токен, не литерал */
  /* padding родителя уже снят классом content--full, свой margin не нужен. */
}
.map-page__toolbar {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-1) var(--sp-4);
  background: var(--surface);
  border-bottom: 1px solid var(--line-2);
  flex-shrink: 0;
  flex-wrap: wrap;
  /* Тулбар карты приклеен к верху страницы, прямо под топбаром.
     Полоса doc-tabs на странице карты сдвигается ниже тулбара через
     переменную --doc-tabs-top, которую выставляет renderMapPage по
     фактической высоте тулбара.
     Z-index 1100: выше всех leaflet-слоёв (panes 200–700) и контролов
     (zoom/attribution на 800–1000), чтобы дропдаун «+ Добавить участок»
     не проваливался под карту — position:sticky+z-index создаёт
     собственный stacking context, и дропдаун внутри него (z-index 1500)
     теперь корректно перекрывает leaflet. */
  position: sticky;
  top: var(--h-topbar);
  z-index: var(--z-map-toolbar);
}
.map-page__toolbar .search { max-width: 320px; }
/* Выбор кладбища: длинные названия режутся многоточием, чтобы селект не
   распирал тулбар. */
.map-page__toolbar select.search { max-width: 150px; text-overflow: ellipsis; }
.map-page__toolbar .muted { font-size: var(--fs-sm); }

/* Группа тулбара: капитель-надзаголовок и ряд контролов под ним.
   Разделитель между группами — вертикальная линия слева, а не пустота:
   иначе на широком экране группы расползаются и связь теряется. */
.map-group {
  display: flex;
  align-items: center;
  min-width: 0;
}
.map-group + .map-group { padding-left: var(--sp-2); border-left: 1px solid var(--line); }
.map-group__body {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  flex-wrap: nowrap;
  min-width: 0;
}
/* Обёртка .map-tools сама группой не является: display:contents делает
   её группы прямыми детьми тулбара, и при переносе ряды заполняются
   равномерно. Иначе все три группы внутри неё переносились одним
   куском, и первый ряд оставался наполовину пустым.
   Решение владельца от 21.09.2026: один ряд на 1366 и высота не выше
   56 px. Место освободили, убрав надзаголовки групп и уведя «Кадастр» и
   «Неполные ФИО» в меню «Слои»; контролы остались 44 px. */
.map-tools { display: contents; }
/* Поквартальный показ живёт в меню «Слои» (21.09.2026), поэтому своих
   метрик тулбара ему больше не нужно — он обычная строка меню. */
.map-byquarter { cursor: pointer; }
#map-quarter-wrap { gap: var(--sp-2); }
#map-quarter-wrap select { flex: 1 1 auto; min-width: 0; max-width: 160px; }
/* Счётчик в пункте «Неполные ФИО» — пилюля, как у фильтров. Нуля не
   показываем: пункт тогда просто без счётчика. */
.map-unknown-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  margin-left: var(--sp-2);
  padding: 0 var(--sp-1);
  border-radius: var(--r-pill);
  background: var(--warn-line);
  color: var(--warn-fg);
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
}
.map-unknown-count[hidden] { display: none; }


/* Контролы Leaflet (копирайт, штатный зум мини-карт) приходят со своим
   CSS: кегль 11 px обычным текстом, радиус 3 px, свои цвета. Приводим к
   токенам здесь, а не правкой чужого файла — leaflet.css обновляется
   как есть и наши правки бы затёрлись. Масштабная линейка живёт в
   панели управления картой (.map-dock) — её вид описан там.
   Уведомление об источнике плиток — подпись, а не плашка: без фона и
   рамки (раунд 2, §12 п.6). */
.leaflet-container .leaflet-control-attribution {
  background: none;
  color: var(--hint);
  font-family: var(--font-ui);
  font-size: var(--fs-sm);
  border-radius: 0;
  padding: 2px var(--sp-2);
}
/* Со снятым префиксом и снятой подписью Leaflet оставляет пустой div —
   пустая коробочка в углу не нужна. */
.leaflet-container .leaflet-control-attribution:empty { display: none; }
.leaflet-container .leaflet-control-attribution a { color: var(--accent-link); font-size: var(--fs-sm); }
/* Штатные кнопки зума Leaflet (в диалоге кладбища и мини-картах):
   26 px и радиус 4 — приводим к своим метрикам кнопки-иконки. */
.leaflet-container .leaflet-control-zoom {
  border: none;
  border-radius: var(--r-icon);
  box-shadow: var(--shadow-menu);
}
.leaflet-container .leaflet-control-zoom a {
  width: var(--h-icon-btn);
  height: var(--h-icon-btn);
  line-height: var(--h-icon-btn);
  border-radius: var(--r-icon);
  background: var(--surface);
  color: var(--text-2);
  font-family: var(--font-ui);
  font-size: var(--fs-md);
  border-bottom-color: var(--line);
}
.leaflet-container .leaflet-control-zoom a:hover { background: var(--accent-100); color: var(--accent-deep); }
/* Leaflet скругляет первый и последний сегмент отдельными правилами —
   перекрываем оба, иначе остаётся его радиус 2 px. */
.leaflet-container .leaflet-control-zoom a:first-child { border-radius: var(--r-icon) var(--r-icon) 0 0; }
.leaflet-container .leaflet-control-zoom a:last-child { border-radius: 0 0 var(--r-icon) var(--r-icon); }
/* Подписи и номера точек на карте — гарнитурой интерфейса. */
.leaflet-container { font-family: var(--font-ui); }

/* --- Панель управления картой (раунд 3, вариант 3 владельца) ---
   Подложка, масштабная линейка, зум и «Всё» — одной белой панелью
   внизу справа колонки карты (а колонка кончается у панели участка —
   контролы под неё не заходят). Белая, с рамкой и тенью, как меню:
   панель должна читаться и на схеме, и на спутниковом снимке. Прежние
   кнопки зума были без плотной подложки и на спутнике пропадали, а
   подложка стояла отдельно вверху справа и закрывала участки в этом
   углу. */
.map-dock {
  position: absolute;
  right: var(--sp-4);
  bottom: calc(var(--sp-5) + var(--sp-2));   /* над подписью источника плиток */
  z-index: 500;
  display: flex;
  align-items: center;
  flex-wrap: wrap;                            /* в узкой колонке — в два ряда */
  gap: 2px;
  max-width: calc(100% - var(--sp-8));
  padding: var(--sp-1);
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--r-field);
  box-shadow: var(--shadow-menu);
  box-sizing: border-box;
  color: var(--text-2);
}
.map-dock__sep {
  flex: none;
  width: 1px;
  height: 24px;
  margin: 0 var(--sp-1);
  background: var(--line-2);
}
/* Подложка — таблетка на --surface-2, как режимы правки в тулбаре;
   выбранная — заливка --accent-strong с белым. Зона нажатия сегмента —
   44 px, закрашено 36: заливку рисует псевдоэлемент с отступом 4 px. */
.map-dock .map-basemap {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  margin: 0;
  padding: 0 3px;
  background: var(--surface-2);
  border-radius: var(--r-card);
}
.map-dock .map-basemap .seg__btn {
  position: relative;
  isolation: isolate;
  height: var(--h-control);
  min-height: var(--h-control);
  margin: 0;
  padding: 0 var(--sp-3);
  border: 0;
  border-radius: var(--r-chip);
  background: transparent;
  color: var(--text-2);
  font-weight: var(--fw-medium);
}
.map-dock .map-basemap .seg__btn::before {
  content: "";
  position: absolute;
  inset: var(--sp-1) 0;
  z-index: -1;
  border-radius: var(--r-chip);
}
.map-dock .map-basemap .seg__btn:hover { background: transparent; color: var(--accent-deep); }
.map-dock .map-basemap .seg__btn:hover::before { background: var(--accent-200); }
.map-dock .map-basemap .seg__btn.is-active,
.map-dock .map-basemap .seg__btn.is-active:hover { background: transparent; color: var(--on-accent); }
.map-dock .map-basemap .seg__btn.is-active::before { background: var(--accent-strong); }
/* Масштабная линейка — скобка с подписью внутри. Длину скобки задаёт
   Leaflet (от 60 до 120 px), слот постоянной ширины — чтобы панель не
   «ездила» при каждом изменении масштаба. */
.map-dock__scale {
  flex: none;
  display: flex;
  justify-content: center;
  width: 128px;
}
.map-dock .leaflet-control-scale { margin: 0; }
.map-dock .leaflet-control-scale-line {
  box-sizing: border-box;
  padding: 0 var(--sp-2) 3px;
  border: 2px solid currentColor;
  border-top: 0;
  border-radius: 0;
  background: none;
  box-shadow: none;
  text-shadow: none;
  color: var(--text-2);
  font-family: var(--font-ui);
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  line-height: 1;
  text-align: center;
  white-space: nowrap;
}
/* Зум и «Всё» (README §5.2): кнопки 44 px без собственной рамки — рамка
   и тень у всей панели. */
.map-zoom {
  display: inline-flex;
  align-items: center;
  gap: 2px;
}
.map-zoom__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--h-control);
  height: var(--h-control);
  padding: 0;
  border: 0;
  border-radius: var(--r-icon);
  background: transparent;
  color: var(--text-2);
  font-family: var(--font-ui);
  font-size: var(--fs-base);
  font-weight: var(--fw-medium);
  cursor: pointer;
}
.map-zoom__btn svg { display: block; }
.map-zoom__btn--all { width: auto; padding: 0 var(--sp-3); }
.map-zoom__btn:hover { background: var(--accent-100); color: var(--accent-deep); }

/* Единая высота и шрифт всех кнопок, селектов и поисковых полей в
   тулбаре карты. Без этого «Кладбище: [Пермь]» (.field select 36px /
   14px) был выше и крупнее, чем остальные кнопки и поле поиска
   (~30px / 13px), а между собой кнопки и инпут поиска тоже немного
   расходились. Фиксируем всё на 32px / 13px (значения — из общих шкал
   --btn-h-* и --fs-*, см. :root). */
/* Контролы тулбара карты — 44 px, как везде: кликабельная зона не
   делается исключением ради экономии высоты. Высоту экономит сам
   тулбар — он стал одной строкой вместо двух-трёх. */
.map-page__toolbar {
  --toolbar-control-h: var(--h-control);
  --toolbar-control-fz: var(--fs-base);
}
.map-page__toolbar .field.row > span {
  font-size: var(--toolbar-control-fz);
  color: var(--text-2);
}
.map-page__toolbar .field input,
.map-page__toolbar .field select,
.map-page__toolbar select.search,
.map-page__toolbar .map-search input[type="search"],
.map-page__toolbar .map-tools .btn,
.map-page__toolbar .btn {
  height: var(--toolbar-control-h);
  min-height: var(--toolbar-control-h);
  font-size: var(--toolbar-control-fz);
  line-height: 1.2;
  padding: 0 var(--sp-3);
  box-sizing: border-box;
}
/* Селект кладбища оформляется наравне с полями: нативный вид рядом с
   переоформленными контролами читается как чужой элемент. */
.map-page__toolbar select.search {
  appearance: none;
  -webkit-appearance: none;
  max-width: 190px;
  padding-right: var(--sp-8);
  border: 1px solid var(--line-2);
  border-radius: var(--r-field);
  background-color: var(--surface-2);
  color: var(--text);
  font-family: inherit;
  background-image:
    linear-gradient(45deg, transparent 50%, var(--muted) 50%),
    linear-gradient(135deg, var(--muted) 50%, transparent 50%);
  background-position:
    calc(100% - 18px) calc(50% + 2px),
    calc(100% - 12px) calc(50% + 2px);
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
}
.map-page__toolbar .map-search input[type="search"] {
  width: 180px;
  border: 1px solid var(--line-2);
  border-radius: var(--r-field);
  background: var(--surface-2);
  color: var(--text);
  font-family: inherit;
}
.map-page__toolbar .map-tools__group {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  box-sizing: border-box;
}
/* Кнопки внутри группы — той же высоты, что остальные контролы тулбара.
   Прежде они были на 4 px ниже, чтобы «вписаться в рамку» группы; рамки
   у группы больше нет, а 40 px не дотягивали до кликабельной зоны. */
.map-page__toolbar .map-tools__group .btn {
  height: var(--toolbar-control-h);
  min-height: var(--toolbar-control-h);
}

/* Кнопки-триггеры меню («Участок», «Слои» и т. д.) — пастельные чипы, как
   в прототипе §5.2: заливка --accent-100, текст --accent-deep, без рамки
   (на #E8F0F8 текст прототипа #4B7099 даёт 4,49:1 — чуть ниже нормы,
   --accent-deep даёт 7,9:1). Белые кнопки с рамкой здесь читались как
   ещё один, третий вид контрола рядом с полями и таблеткой режимов. */
.map-page__toolbar .map-dropdown > .btn {
  background: var(--accent-100);
  border-color: transparent;
  color: var(--accent-deep);
}
.map-page__toolbar .map-dropdown > .btn:hover,
.map-page__toolbar .map-dropdown:has(> .map-dropdown__menu:not([hidden])) > .btn {
  background: var(--accent-200);
  border-color: transparent;
  color: var(--accent-deep);
}

/* Таблетка режимов «Править / Сдвиг / Поворот | Удалить» — прототип §5.2:
   мягкая подложка, внутри неё сегменты; активный режим — заливка
   --accent-strong с белым (в тулбаре пастель занята чипами, и пастельный
   активный сегмент терялся бы среди них). Кликабельная зона сегмента —
   44 px (README §3.4), закрашивается 36: верх и низ — прозрачная рамка
   4 px, заливка обрезана по padding-box. «Удалить» — в той же таблетке,
   за разделителем, кирпичным текстом: это режим, как и остальные три. */
/* Та же таблетка — у инструментов окна просмотра фото (.fp-group):
   листание и масштаб. */
.map-page__toolbar .map-tools__group.seg,
.fp-group {
  gap: 2px;
  padding: 0 var(--sp-1);
  height: var(--h-control);
  border-radius: var(--r-field);
  background: var(--surface-2);
  margin-left: 0;
}
.map-page__toolbar .seg .seg__btn,
.file-preview .fp-btn {
  height: var(--h-control);
  min-height: var(--h-control);
  margin: 0;
  padding: 0 var(--sp-3);
  border: 4px solid transparent;
  border-width: 4px 0;
  border-radius: var(--r-card);
  background: transparent;
  background-clip: padding-box;
  color: var(--text-2);
  font-weight: var(--fw-medium);
  box-sizing: border-box;
}
/* Наведение — --accent-200: пастель --accent-100 на подложке --surface-2
   почти не видна, у них один тон. */
.map-page__toolbar .seg .seg__btn:hover:not(:disabled),
.file-preview .fp-btn:hover:not(:disabled) {
  background: var(--accent-200);
  border-color: transparent;
  color: var(--accent-deep);
}
.map-page__toolbar .seg .seg__btn.btn--primary,
.map-page__toolbar .seg .seg__btn.btn--primary:hover {
  background: var(--accent-strong);
  border-color: transparent;
  color: var(--on-accent);
}
.map-page__toolbar .seg .seg__btn:disabled,
.map-page__toolbar .seg .seg__btn:disabled:hover,
.file-preview .fp-btn:disabled,
.file-preview .fp-btn:disabled:hover {
  background: transparent;
  border-color: transparent;
  color: var(--text-2);
}
.map-page__toolbar .seg .seg__btn--danger {
  color: var(--danger-fg);
  margin-left: var(--sp-2);
  position: relative;
}
.map-page__toolbar .seg .seg__btn--danger::before {
  content: "";
  position: absolute;
  left: -5px;
  top: 8px;
  bottom: 8px;
  width: 1px;
  background: var(--line-2);
}
.map-page__toolbar .seg .seg__btn--danger:hover:not(:disabled) {
  background: var(--err-bg);
  color: var(--danger-fg);
}
.map-page__toolbar .seg .seg__btn--danger.btn--primary,
.map-page__toolbar .seg .seg__btn--danger.btn--primary:hover {
  background: var(--err-bar);
  color: var(--on-accent);
}

/* Блок «Схема / Спутник» прижимается к правому краю тулбара (а тулбар
   растянут на всю ширину окна — значит к правому краю экрана). Делается
   flex-логикой: .map-tools растягивается на оставшееся место, basemap-
   группа получает order:99 (становится последней в визуальном порядке)
   и margin-left:auto (выталкивается вправо). Остальные кнопки нормал-
   режима (Править/Сдвиг/Поворот, ⚙ Инфраструктура) остаются слева
   рядом с «+ Добавить участок». Разделители в нормал-режиме после
   этой перестановки оказываются между несвязанными группами и теряют
   смысл — прячем. Для infra-режима поведение не меняется. */
.map-page__toolbar .map-tools[data-mode-view="normal"] {
  flex: 1 1 auto;
}
.map-page__toolbar .map-tools[data-mode-view="normal"] .map-tools__measure {
  order: 97;
  margin-left: auto;
}
.map-page__toolbar .map-tools[data-mode-view="normal"] .map-tools__layers {
  order: 98;
}
.map-page__toolbar .map-tools[data-mode-view="normal"] .map-tools__sep {
  display: none;
}
/* Меню «Слои ▾» и «Инструменты ▾» прижаты к правому краю кнопки
   (а не к левому, как у plot/infra-dropdown), чтобы не вылезали
   за правый край экрана. */
#layers-menu, #measure-menu {
  left: auto;
  right: 0;
  min-width: 220px;
}
.map-layers__item {
  cursor: pointer;
  user-select: none;
}
.map-layers__item input[type="checkbox"] {
  margin: 0;
  cursor: pointer;
}

/* Tooltip итога замера (Линейка / Планимер). По умолчанию Leaflet-tooltip
   полупрозрачный с белым фоном — поверх спутника плохо читается. Ставим
   явно тёмный фон + белый шрифт с тенью. */
.leaflet-tooltip.measure-tooltip {
  background: rgba(17, 24, 39, 0.92);
  color: var(--on-accent);
  border: none;
  border-radius: var(--r-chip);
  padding: 4px 8px;
  font-weight: 600;
  font-size: var(--fs-base);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
}
.leaflet-tooltip.measure-tooltip::before { display: none; }

/* --- Собственный рендер карты (map-osiris.js, сборка с тегом registry) ---
   Правила-эквиваленты для .osiris-* классов рендера. В ПОЛНОЙ сборке эти
   классы в DOM не появляются (там Leaflet), поэтому блок инертен и поведение
   полной сборки не меняется. Держим рядом с leaflet-правилами, чтобы при
   правке одного не забыть второе. */
.osiris-map { background: inherit; }
.osiris-map__canvas { display: block; }
/* Базовое правило подписи собственного рендера.
   nowrap здесь обязателен. Подпись лежит в пейне, у которого нет ширины
   (position:absolute; left:0; top:0 и пустое содержимое — см. createPane
   в map-osiris.js): доступная ширина внутри него равна нулю, и браузер
   ужимает плашку до самого длинного НЕРАЗРЫВНОГО куска. Из-за этого
   «18,84 м» вставало в две строки, а «S = 52 м²» — в три. У подписей
   картобиблиотеки в полной сборке nowrap есть в её собственном css,
   поэтому там перенос никогда не проявлялся. Ширина плашки — по
   содержимому: nowrap + сжатие по месту как раз это и дают, фиксировать
   ширину не нужно. */
.osiris-tooltip { white-space: nowrap; }
.osiris-tooltip.measure-tooltip {
  background: rgba(17, 24, 39, 0.92);
  color: var(--on-accent);
  border: none;
  border-radius: var(--r-chip);
  padding: 4px 8px;
  font-weight: 600;
  font-size: var(--fs-base);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
}
/* Пейн инфра-маркеров: по умолчанию скрыт, показывается на глубоком зуме и
   при редактировании — как .leaflet-osirisInfraMarker-pane ниже. */
.osiris-pane-osirisInfraMarker { display: none; }
.osiris-deep-zoom .osiris-pane-osirisInfraMarker,
.osiris-editing .osiris-pane-osirisInfraMarker { display: block; }
/* Курсоры инструментов «Сдвиг»/«Поворот» — эквивалент .leaflet-grab/-interactive. */
.osiris-cursor-move .osiris-map__canvas { cursor: move !important; }
.osiris-cursor-rotate .osiris-map__canvas { cursor: crosshair !important; }

/* Подпись кладбища в режиме «Все кладбища» — кликабельная.
   Лежит как divIcon-маркер, поэтому по дефолту имеет фиксированные
   размеры от leaflet — сбрасываем, чтобы натянулось по контенту. */
.cem-label-marker {
  background: transparent !important;
  border: none !important;
  width: auto !important;
  height: auto !important;
  pointer-events: auto;
}
.cem-label-marker .cem-label {
  display: inline-block;
  padding: 4px 10px;
  background: rgba(255, 255, 255, 0.92);
  color: var(--accent-deep);
  font-weight: 600;
  font-size: var(--fs-base);
  border: 1px solid var(--accent-deep);
  border-radius: var(--r-chip);
  white-space: nowrap;
  cursor: pointer;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15);
  transform: translate(-50%, -50%);
  transition: background-color 120ms ease, color 120ms ease;
}
.cem-label-marker .cem-label:hover {
  background: var(--accent-deep);
  color: white;
}

.map-tools {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  align-items: center;
}
/* .map-tools — flex-контейнер, поэтому атрибут [hidden] (display:none)
   должен перекрывать display:flex. */
.map-tools[hidden] { display: none; }
.map-tools .btn { padding: 6px 10px; font-size: var(--fs-base); }


/* Группа кнопок «Править / Сдвиг / Поворот» — их связь теперь показывает
   подписанная группа тулбара и сегментированная «таблетка» (.seg), а не
   собственная подложка с рамкой: рамка вокруг рамки давала два контура
   на один смысл. */
.map-tools__group {
  display: flex;
  gap: var(--sp-2);
  align-items: center;
}
/* Внутри группы делаем «бесшовные» кнопки (transparent) — НО только для
   обычных, не primary. Если случайно положить primary внутрь группы,
   правило background:transparent лишило бы её акцентной заливки
   (а текст у primary — контрастный к заливке --primary-fg, не к фону). */
.map-tools__group .btn:not(.btn--primary) {
  border: none;
  background: transparent;
}
.map-tools__group .btn:not(.btn--primary):hover:not(:disabled) {
  background: var(--btn-bg-hover);
}

/* Разделитель перед «Инфраструктура» — чтобы кнопка визуально
   отделялась от группы инструментов работы с участком. */
.map-tools__sep {
  width: 1px;
  align-self: stretch;
  background: var(--border);
  margin: 0 8px;
}

/* Активная подложка — сегмент .seg__btn.is-active из общих правил:
   отдельная подсветка для неё больше не нужна, а прежняя ярко-синяя
   заливка плохо читалась поверх спутниковых тайлов. */

/* --- Dropdown «+ Инфраструктура» (меню типов с иконками) --- */
.map-dropdown {
  position: relative;
  display: inline-block;
}
/* Оболочка меню — та же, что у меню полосы и «⋯» строк: радиус 14,
   рамка --line-2, тень --shadow-menu, воздух 4 px вокруг строк. */
.map-dropdown__menu {
  position: absolute;
  top: 100%;
  left: 0;
  margin-top: 4px;
  min-width: 240px;
  max-height: 420px;
  overflow-y: auto;
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--r-card);
  box-shadow: var(--shadow-menu);
  padding: var(--sp-1);
  z-index: var(--z-dropdown); /* ниже модалок, но выше карты и её контролов */
  font-size: var(--fs-base);
}
/* Выбор маркера и меню «Слои» — без вертикальной прокрутки: показываем
   сразу все пункты (по запросу пользователя 2026-05-21). У остальных
   выпадашек ограничение высоты остаётся (например, длинный список видов
   захоронений в подменю «+ Участок»). */
#infra-menu {
  max-height: none;
  overflow-y: visible;
}
/* Меню «Слои» — две колонки: слева слои карты, справа источник записей,
   поквартальный показ, дополнительные слои и экономный режим. В одну
   колонку четырнадцать строк с четырьмя надзаголовками давали 870 px —
   выше карты на 1366×768 (663 px): «Кадастр» и «Неполные ФИО» оказывались
   за нижним краем окна и были недоступны, а меню по просьбе оператора
   (2026-05-21) не прокручивается. Двумя колонками меню около 430 px.
   Прокрутка остаётся страховкой для окон ниже 768. */
#layers-menu {
  min-width: 0;
  max-height: calc(100vh - var(--h-topbar) - 72px);
  overflow-y: auto;
}
.map-layers__cols { display: flex; align-items: flex-start; gap: var(--sp-2); }
.map-layers__col { flex: 1 1 0; min-width: 264px; }   /* самая длинная подпись
                                                          «Только книга захоронений» —
                                                          в одну строку */
.map-layers__col + .map-layers__col { padding-left: var(--sp-2); border-left: 1px solid var(--line); }
.map-layers__col > .map-dropdown__sep:first-child { display: none; }
.map-dropdown__sep { height: 1px; margin: var(--sp-1) var(--sp-2); border: 0; background: var(--line); }
.map-dropdown__group {
  padding: var(--sp-2) var(--sp-3) 2px;
  font-size: var(--fs-sm);
  color: var(--muted);
  font-weight: 600;
}
/* Строка меню карты — общая строка меню: 36 px, 15 px, отступ 12
   (см. .topbar__group-menu a). Подписи чекбоксов и радио в меню — тоже
   15, а не 16, как в формах: здесь это строки списка, а не поля. */
.map-dropdown__item {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: var(--h-icon-btn);
  padding: 0 var(--sp-3);
  border-radius: var(--r-chip);
  color: var(--text);
  font-size: var(--fs-base);
  font-weight: var(--fw-normal);
  text-decoration: none;
}
.map-dropdown__item:hover { background: var(--accent-100); color: var(--accent-deep); text-decoration: none; }
/* Каждая подпись — своей строкой: общее правило подписей с чекбоксом
   делает label строчно-блочным, и в широкой колонке две короткие
   («Кладбища», «Кварталы») вставали в один ряд. */
.map-dropdown__menu label:has(> input[type="checkbox"]),
.map-dropdown__menu label:has(> input[type="radio"]) { display: flex; min-height: var(--h-icon-btn); }
.map-dropdown__menu label > input[type="checkbox"] + *,
.map-dropdown__menu label > input[type="radio"] + * { font-size: var(--fs-base); }
.map-dropdown__item img { width: 18px; height: 18px; }
/* Переключатели «Кадастр» и «Неполные ФИО» — те же строки меню, но с
   собственным состоянием: это не слои-префы и не подложка, поэтому они
   кнопки, а не чекбоксы (решение владельца от 21.09.2026 — из тулбара
   в меню «Слои»). */
.map-dropdown__toggle {
  width: 100%;
  background: none;
  border: 0;
  font: inherit;
  font-family: var(--font-ui);
  font-size: var(--fs-base);
  text-align: left;
  cursor: pointer;
}
.map-dropdown__toggle.is-active {
  background: var(--accent-100);
  color: var(--accent-deep);
  font-weight: var(--fw-medium);
}
.map-dropdown__toggle:disabled { opacity: .6; cursor: progress; }
/* Контейнер под карту и боковую панель справа (информация об участке). */
.map-layout {
  display: flex;
  flex: 1;
  min-height: 0;
}
/* Колонка карты (раунд 2, §12 п.5): карта и панель — соседи, всё
   наложенное на карту (подложка, зум, линейка) отсчитывается от этой
   колонки. Раньше отступ считался через ширину панели участка и не
   учитывал полосу-шеврон — она накрывала кнопки на 12 px. */
.map-layout__main {
  position: relative;
  flex: 1 1 auto;
  min-width: 0;
  min-height: 0;
  display: flex;
}
#map {
  flex: 1;
  min-width: 0;
  min-height: 0;
  background: var(--surface-2);
}

/* Плашки-подсказки при рисовании на карте.
   Контейнер абсолютно спозиционирован внутри #map (Leaflet ставит
   контейнеру position:relative), прижат к верхней кромке карты по
   горизонтальному центру. Карта начинается сразу под тулбаром, поэтому
   top:10px — это и есть «10px ниже тулбара». Несколько плашек идут
   стопкой сверху вниз. pointer-events:none — плашки не перехватывают
   клики по карте во время рисования. */
/* Контейнер уведомлений карты: подсказки рисования + тосты («Сохранено»,
   «Изменено N объектов» и т.п.) — все в ЛЕВОМ ВЕРХНЕМ углу карты, стопкой
   сверху вниз (по запросу пользователя 2026-05-21). Сюда же toast()
   складывает свои плашки, пока открыта страница карты. pointer-events
   контейнера — none; .toast--prominent (с кнопкой Undo) сам включает клики. */
.map-notify {
  position: absolute;
  top: 10px;
  left: 10px;
  z-index: var(--z-toast); /* тот же слой, что глобальные тосты */
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
  pointer-events: none;
  max-width: min(440px, calc(100% - 20px));
}
/* Плашка-подсказка рисования — в стиле тоста (.toast). Через 5с JS вешает
   .is-fading, и она плавно гаснет (тосты при этом не трогаем). */
.map-draw-hint {
  background: var(--surface);
  color: var(--fg);
  border: 1px solid var(--border);
  font-size: var(--fs-md);
  font-weight: 500;
  line-height: 1.4;
  padding: 12px 22px;
  border-radius: 10px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
  text-align: left;
  max-width: 100%;
  transition: opacity 0.4s ease;
}
.map-draw-hint.is-fading { opacity: 0; }

/* Панель участка — 440 px (раунд 2, §12 п.4), три зоны: закреплённая шапка, прокручиваемое
   тело, закреплённый подвал (README §5.2). Внешний угол скруглён на 26,
   поэтому панель отступает от края и не сливается с картой. */
.map-layout__side {
  width: var(--w-map-side);
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  min-height: 0;
  background: var(--surface);
  border-left: 1px solid var(--line-2);
  border-radius: var(--r-panel) 0 0 var(--r-panel);
  overflow: hidden;
  font-size: var(--fs-base);
}
/* Пустое состояние панели («Кликните по участку…») — по центру панели,
   как у пустых списков: абзац в левом верхнем углу читался как забытый
   текст, а не как подсказка. */
.map-layout__side > .placeholder {
  display: flex;
  flex: 1 1 auto;
  min-height: 0;
  align-items: center;
  justify-content: center;
  padding: var(--sp-6);
  text-align: center;
  color: var(--muted);
  line-height: 1.5;
}
.map-side__body {
  flex: 1 1 auto;
  min-height: 0;
  min-width: 0;
  /* overflow-x: hidden — иначе длинное ФИО или подпись кнопки дают
     горизонтальную прокрутку внутри панели, и часть содержимого
     оказывается за её правым краем. */
  overflow-y: auto;
  overflow-x: hidden;
  padding: var(--sp-4);
}
/* Содержимое панели не шире её самой: длинные значения переносятся или
   режутся многоточием, а не распирают колонку. */
.map-side__body .fields-grid { grid-template-columns: minmax(0, 1fr); gap: var(--sp-2); }
.map-side__body .fields-grid > div { min-width: 0; overflow-wrap: anywhere; }
.map-side__body .btn { min-width: 0; }
.map-side__foot {
  flex: none;
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-4);
  border-top: 1px solid var(--line);
  background: var(--surface-2);
}
/* На 372 px «Редактировать участок» и «Удалить участок…» в один ряд не
   встают — переносятся друг под друга, а не обрезаются. */
.map-side__foot .btn { flex: 1 1 150px; min-height: var(--h-icon-btn); }
.map-side__crumbs { font-size: var(--fs-sm); color: var(--muted); margin-bottom: var(--sp-1); }
.map-side__head-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
}
/* ФИО выбранной записи — 19 px серифом, как заголовок блока; справа
   меню «⋯» с действиями над записью (раунд 2, §12 п.4). */
.map-side__person-head {
  display: flex;
  gap: var(--sp-2);
  align-items: center;
  margin: 0 0 var(--sp-2);
}
.map-side__person-head .map-side__person-name { flex: 1 1 auto; }
.map-side__person-head .rowmenu { flex: none; }
/* Тело панели прокручиваемое (overflow-y: auto), поэтому выпадающий
   список внутри него открывается вверх — вниз его обрезал бы край. */
.map-side__body .rowmenu__list { top: auto; bottom: calc(100% + var(--sp-1)); }
/* «Снять» в ряду образцов — высотой с образец, а не 44 px. */
.map-side__colors .btn { min-height: var(--h-icon-btn); }
.map-side__person-name {
  font-family: var(--font-title);
  font-size: var(--fs-xl);
  line-height: 1.25;
  min-width: 0;
  overflow-wrap: anywhere;
}
.map-side__colors { display: flex; gap: var(--sp-2); align-items: center; flex-wrap: wrap; }
/* Чипы «Параметры участка» (раунд 2, §12 п.4): две строки вместо пяти
   строк «подпись: значение». Свой класс, а не .chip: тот объявлен дважды
   и тянет рамку с курсором-пальцем, то есть выглядит кнопкой. */
.map-side__chips { display: flex; flex-wrap: wrap; gap: var(--sp-1) var(--sp-2); }
.map-side__chip {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  padding: 2px var(--sp-2);
  border-radius: var(--r-pill);
  background: var(--surface-2);
  font-size: var(--fs-sm);
  color: var(--text-2);
}
/* Подпись и содержимое одной строкой: «Выделить цветом» с образцами,
   заголовок группы файлов с миниатюрами. */
.map-side__row { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
.map-side__row .map-side__label { margin: 0; flex: none; }
.map-side__row .map-side__chips { flex: 1 1 auto; min-width: 0; }
/* Квадрат пометки — 36 px, как любая кнопка-иконка. Активный обведён
   акцентом с белым зазором: на цветной заливке одна рамка теряется. */
.plot-color-btn {
  width: var(--h-icon-btn);
  height: var(--h-icon-btn);
  padding: 0;
  border: 1px solid var(--line-2);
  border-radius: var(--r-icon);
  cursor: pointer;
}
.plot-color-btn.is-active {
  border-color: var(--accent-deep);
  box-shadow: 0 0 0 2px var(--surface) inset;
}
/* Узкая вертикальная кнопка-шеврон между картой и панелью (на всю высоту
   ряда .map-layout): сворачивает/разворачивает панель, выделение участка
   НЕ сбрасывается (в отличие от «×» внутри панели). */
.map-side-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  min-width: 28px;
  align-self: stretch;
  padding: 0;
  background: var(--surface-2);
  border: 1px solid var(--line-2);
  border-top: none;
  border-bottom: none;
  color: var(--muted);
  font-size: var(--fs-sm);
  font-family: var(--font-ui);
  cursor: pointer;
}
.map-side-toggle:hover { background: var(--accent-100); color: var(--accent-deep); }
.map-side-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
/* Свёрнутая панель прячется целиком — полоска-шеврон остаётся. */
.map-layout__side.is-collapsed { display: none; }
.map-side__head {
  flex: none;
  padding: var(--sp-3) var(--sp-4);
  border-bottom: 1px solid var(--line);
  background: var(--surface);
}
/* Номер участка — серифом: это имя объекта, как ФИО в карточке. */
.map-side__head h3 {
  margin: 0;
  font-family: var(--font-title);
  font-weight: var(--fw-normal);
  font-size: var(--fs-xl);
}
.map-side__section {
  padding: var(--sp-2) 0;
  border-bottom: 1px solid var(--line);
}
.map-side__section:last-of-type { border-bottom: none; }
.map-side__label {
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
  color: var(--muted);
  margin-bottom: var(--sp-1);
}
/* Кнопки добавления — после списка того, что на участке уже есть
   (README §5.2): сначала «что здесь», потом «что сделать». Две кнопки
   в ряд, а не столбцом: в столбце вторая читается как продолжение
   первой, хотя действия разные. */
.map-side__actions {
  display: flex;
  gap: var(--sp-2);
  flex-wrap: wrap;
  padding: var(--sp-2) 0;
  border-bottom: 1px solid var(--line);
}
/* Подпись переносится на вторую строку, как в прототипе: на 372 px
   «Добавить захоронение» в одну строку рядом со второй кнопкой не
   помещается, а обрезать подпись действия нельзя. */
.map-side__actions .btn {
  flex: 1 1 150px;
  min-height: var(--h-icon-btn);
  white-space: nowrap;
  line-height: 1.2;
  padding: var(--sp-1) var(--sp-3);
  font-size: var(--fs-base);
}
/* Список «людей на участке» в боковой панели карты: клик выделяет,
   двойной клик — открывает карточку. */
.map-side__people {
  display: flex;
  flex-direction: column;
  gap: 0;
  border: none;
  border-radius: 0;
  overflow: hidden;
}
/* Строка списка «Захоронения на участке»: ФИО серифом в одну строку,
   годы справа, высота 40 px. Раунд 2 (§12 п.4): это список, а не набор
   карточек — рамки сняты, строки разделены линией, панель стала ниже. */
.map-side__person {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--sp-2);
  align-items: center;
  min-height: var(--h-row-side);
  padding: 0 var(--sp-3);
  cursor: pointer;
  border: 0;
  border-bottom: 1px solid var(--line);
  border-radius: 0;
  font-size: var(--fs-base);
  background: var(--surface);
  user-select: none;
}
.map-side__person:last-child { border-bottom: none; }
.map-side__person:hover { background: var(--accent-100); }
.map-side__person.is-selected {
  background: var(--pick-bg);
  box-shadow: inset 3px 0 0 var(--pick-bar);
}
/* На подсветке выбранной строки приглушённый цвет не держит контраст
   (--muted на --pick-bg — 3,7:1): годы и прочерки там — цветом
   вторичного текста. То же у выбранной строки таблиц. */
.map-side__person.is-selected .muted,
table.table tbody tr.is-selected > td .muted,
table.table tbody tr.is-selected > td .dash,
table.table tbody tr[aria-selected="true"] > td .muted,
table.table tbody tr[aria-selected="true"] > td .dash {
  color: var(--text-2);
}
.map-side__person-fio {
  font-family: var(--font-title);
  font-size: var(--fs-base);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.map-side__person-dates { font-size: var(--fs-sm); white-space: nowrap; }

/* Поиск ФИО в тулбаре карты: относительный контейнер + выпадашка под инпутом. */
/* Поле поиска — единственный тянущийся контрол тулбара: на 1366 оно
   ужимается, чтобы четыре группы встали в один ряд, на 1920 и шире
   растягивается обратно (решение владельца от 21.09.2026). */
.map-search {
  position: relative;
  flex: 1 1 140px;
  max-width: 340px;
  min-width: 140px;
}
.map-search input[type="search"] {
  width: 100%;
  padding: 6px 10px;
  font-size: var(--fs-base);
  border: 1px solid var(--border);
  border-radius: var(--r-chip);
  background: var(--input-bg);
  color: var(--fg);
}
.map-search__results {
  position: absolute;
  left: 0;
  /* Окно шире инпута, чтобы спокойно помещалось «Фамилия Имя Отчество»
     с локацией и датами. Минимум — ширина инпута, максимум — 480px. */
  min-width: 100%;
  width: 480px;
  max-width: 90vw;
  top: calc(100% + 4px);
  max-height: 70vh;
  overflow-y: auto;
  background: var(--surface); /* токен вместо #fff — тёмная тема */
  border: 1px solid var(--border);
  border-radius: var(--r-card);
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.18);
  z-index: var(--z-search);
  /* Лёгкая анимация появления — глаз понимает «окно открылось». */
  animation: map-search-open 120ms ease-out;
}
@keyframes map-search-open {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: translateY(0); }
}
.map-search__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 8px 12px;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--muted);
  border-radius: var(--r-card) 8px 0 0;
  position: sticky;
  top: 0;
  z-index: 1;
}
.map-search__close {
  background: transparent;
  border: none;
  color: var(--muted);
  font-size: var(--fs-lg);
  line-height: 1;
  cursor: pointer;
  padding: 0 4px;
  border-radius: var(--r-chip);
}
.map-search__close:hover { background: var(--btn-bg-hover); color: var(--fg); }
.map-search__row {
  padding: 8px 12px;
  cursor: pointer;
  border-bottom: 1px solid var(--line);
}
.map-search__row:last-child { border-bottom: none; }
.map-search__row:hover { background: var(--primary-soft); }
/* Строка, выбранная стрелками с клавиатуры (fioResultsMove в app.js). */
.map-search__row.is-selected { background: var(--primary-soft); outline: 2px solid var(--primary); outline-offset: -2px; }
.map-search__row--noplot { opacity: 0.65; cursor: not-allowed; }
.map-search__row--noplot:hover { background: transparent; }
.map-search__fio { font-weight: 500; font-size: var(--fs-md); color: var(--fg); }
.map-search__meta { font-size: var(--fs-sm); color: var(--muted); margin-top: 2px; }
.map-search__empty { padding: 16px; color: var(--muted); font-size: var(--fs-base); text-align: center; }

/* Модалка результатов поиска ФИО (открывается по Enter из поля поиска
   на карте). Использует общий .modal-backdrop / .modal / .modal--wide,
   только содержимое — список без формы. */
.fio-modal__list {
  max-height: 60vh;
  overflow-y: auto;
}
.fio-modal__list .map-search__row {
  padding: 10px 22px;
}

/* Единое окно результатов поиска ФИО на карте (живое — обновляется по
   мере ввода). Объединяет прежние дропдаун + модалку: теперь одна выдача
   (по просьбе пользователя 2026-05-26). Без затемняющего backdrop, чтобы
   поле поиска в тулбаре оставалось доступным и можно было продолжать
   печатать; контейнер pointer-events:none, кликабельна только панель. */
.fio-results {
  position: fixed;
  top: 112px;            /* под топбаром (56) и тулбаром карты (~52) */
  left: 50%;
  transform: translateX(-50%);
  z-index: var(--z-fio); /* выше тулбара и тостов, ниже модалок — см. карту --z-* */
  width: min(900px, 94vw);
  pointer-events: none;
}
.fio-results__panel {
  pointer-events: auto;
  display: flex;
  flex-direction: column;
  max-height: calc(100vh - 140px);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: 0 12px 36px rgba(0, 0, 0, 0.22);
  overflow: hidden;
  animation: map-search-open 120ms ease-out;
}
.fio-results__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 16px;
  border-bottom: 1px solid var(--border);
  background: var(--surface);
}
.fio-results__head h2 { margin: 0; font-size: var(--fs-lg); }
.fio-results__count { color: var(--muted); font-size: var(--fs-base); white-space: nowrap; }
.fio-results__list { overflow-y: auto; }

/* Маркер активного режима «Инфраструктура» — небольшая текстовая метка
   слева в тулбаре. Стилистически в той же сине-голубой палитре, что и
   остальной интерфейс (ранее был оранжевым и выпадал из общего тона). */
.map-mode-tag {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border-radius: var(--r-pill);
  font-size: var(--fs-sm);
  font-weight: 600;
  background: var(--primary-soft);
  color: var(--primary-hover);
  border: 1px solid color-mix(in srgb, var(--primary) 35%, transparent);
  white-space: nowrap;
}
.map-mode-tag::before {
  content: "";
  display: inline-block;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--primary);
  /* Лёгкая пульсация — глазу легко поймать «режим включён», но не назойливо. */
  animation: map-mode-pulse 1.6s ease-in-out infinite;
}
@keyframes map-mode-pulse {
  0%,   100% { opacity: 1;   transform: scale(1);   }
  50%        { opacity: 0.4; transform: scale(0.7); }
}

/* Тулбар инфра-режима. Ранее имел отдельный оранжевый фон — теперь
   общий стиль с тонкой синей подсветкой слева, чтобы оператор
   глазом понимал, что «режим работы изменён», но без диссонанса
   с остальной синей палитрой. */
.map-tools--infra {
  padding: 2px 8px 2px 10px;
  border-radius: var(--r-chip);
  background: linear-gradient(
    to right,
    var(--primary-soft) 0,
    var(--primary-soft) 3px,
    transparent 3px
  );
}

#map .leaflet-popup-content {
  font-size: var(--fs-base);
  line-height: 1.4;
}
/* ── Несгорающий бейдж активного режима рисования ──
   Пилюля у верхней кромки карты по центру («Рисование: участок ·
   Enter — завершить»). В отличие от .map-draw-hint не гаснет, пока
   режим включён. Рисуется JS'ом (фаза позже). */
.draw-status-badge {
  position: absolute;
  top: 8px;
  left: 50%;
  transform: translateX(-50%);
  z-index: var(--z-map-toolbar);
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 6px 14px;
  border-radius: var(--r-pill);
  background: var(--surface);
  color: var(--fg);
  border: 1px solid var(--border);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.18);
  font-size: var(--fs-base);
  font-weight: 600;
  /* Раньше стояло white-space: nowrap — плашка была короткой. С 07.09.2026
     у дороги и прохода она несёт текст всех трёх прежних подсказок сразу,
     и на узком окне неразрывная строка вылезала за края карты. Перенос
     разрешён, ширина ограничена шириной карты; короткие тексты остальных
     режимов от этого не меняются — им переносить нечего. */
  /* width: max-content — иначе плашка не могла стать шире ПОЛОВИНЫ карты.
     Она позиционирована absolute от left: 50%, и «ширина по содержимому»
     считалась от этой точки до правого края, то есть максимум 50 % карты:
     текст на 1366 px рвался на три строки при свободном месте вокруг.
     С max-content ширина берётся по тексту и уже потом режется max-width —
     получается одна-две строки. */
  width: max-content;
  max-width: min(92%, 760px);
  text-align: center;
  line-height: 1.35;
  pointer-events: none; /* не перехватывает клики по карте во время рисования */
}
.map-offline-banner {
  padding: 6px 12px;
  background: var(--warn-bg);
  color: var(--warn-fg);
  font-size: var(--fs-sm);
  border-bottom: 1px solid var(--border);
}
/* POI-маркер на карте (L.divIcon), чтобы управлять центрированием и рамкой */
.poi-marker {
  background: var(--surface);
  border: 2px solid var(--accent);
  border-radius: 50%;
  width: 28px;
  height: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 1px 2px rgba(0,0,0,0.15);
}
.poi-marker img { width: 20px; height: 20px; }

/* Маркер инфры, отмеченный на удаление в режиме «🗑 Удалять».
   Красная рамка + полупрозрачный заливочный круг + лёгкое
   красное «свечение» вокруг — оператору видно, что он отметил. */
.poi-marker--marked-deletion {
  border-color: var(--danger) !important;
  background: rgba(220, 38, 38, 0.15) !important;
  box-shadow: 0 0 0 3px rgba(220, 38, 38, 0.35) !important;
}

/* Курсоры карты в режимах сдвига / поворота выделенного участка
   (кнопки «↔ Сдвиг» и «↻ Поворот»). Меняем cursor у leaflet-контейнера
   и его внутренних слоёв, иначе leaflet перекрывает курсором по умолчанию. */
.osiris-cursor-move,
.osiris-cursor-move .leaflet-grab,
.osiris-cursor-move .leaflet-interactive {
  cursor: move !important;
}
.osiris-cursor-rotate,
.osiris-cursor-rotate .leaflet-grab,
.osiris-cursor-rotate .leaflet-interactive {
  cursor: crosshair !important;
}

/* --- Spinner (для openWaitModal и т.п.) --- */
.spinner {
  width: 32px;
  height: 32px;
  border: 3px solid var(--border);
  border-top-color: var(--primary);
  border-radius: 50%;
  animation: spinner-rot 0.8s linear infinite;
}
@keyframes spinner-rot { to { transform: rotate(360deg); } }

/* --- Toast (v0.73.0) ---
   Все тосты по центру экрана, в стиле текущей темы оформления.
   До v0.73 уведомления висели в правом нижнем углу на чёрном фоне —
   оператор их часто пропускал, особенно в полноэкранном режиме карты.
   Теперь: центр верха страницы (под топбаром), фон/цвета из CSS-переменных
   темы. Несколько тостов выстраиваются один под другим вертикально
   с зазором 8px через flex-контейнер `#toast-stack`. */
#toast-stack {
  position: fixed;
  /* Правый верх, под верхней полосой (README §5.3). Прежде стек стоял
     по центру на 3/4 высоты окна — по запросу оператора 04.06.2026,
     когда тосты были чёрными в правом нижнем углу и их пропускали.
     Теперь они выглядят как плашки состояний, и место им у края: центр
     экрана перекрывает ту самую строку таблицы, о которой сообщает. */
  top: calc(var(--h-topbar) + var(--sp-3));
  right: var(--sp-5);
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--sp-2);
  /* Z-index 1750 (по запросу пользователя 2026-05-19): выше топбара
     (1200), map-toolbar (1100), Leaflet pane'ов (~600), map-dropdown
     (1500), но НИЖЕ модалок (2000) — чтобы при показе модалки тост не
     закрывал её содержимое. */
  z-index: var(--z-toast);
  pointer-events: none;  /* контейнер не ловит клики; сами тосты — по необходимости */
  max-width: min(640px, 90vw);
}
/* Тост — та же плашка состояния, что в формах (README §3.3): радиус 14,
   полоса 3 px слева, текст 15 px. `!important` убран: он стоял против
   унаследованных inline-стилей, которых в app.js уже нет, а на этапе F
   его в файле не должно остаться ни одного. */
.toast {
  position: relative;   /* в стеке — обычный flex-item */
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--r-card);
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--line-2);
  border-left: 3px solid var(--accent);
  font-size: var(--fs-base);
  font-weight: var(--fw-medium);
  line-height: 1.4;
  max-width: 100%;
  text-align: left;
  box-shadow: var(--shadow-menu);
  pointer-events: none;
}
/* Класс .show нужен для обратной совместимости (был использован для
   анимации появления). Сейчас тосты непрозрачные с момента вставки в
   DOM, .show просто не меняет вид. */
.toast.show {
  opacity: 1;
}
/* toast--one-line — тост строго в одну строку, шириной по содержимому
   (под подсказку выделения участков).
   Стек #toast-stack ограничен 640 px, и длинная подсказка рвалась на две
   строки с висящим одиночным словом. Здесь потолок считается от ОКНА
   (96vw вместо унаследованных 100 % ширины стека) и запрещён перенос;
   стек — flex-column с align-items: center и без overflow, поэтому тост
   шире стека выходит за его края симметрично и остаётся по центру экрана. Предохранитель на узкое окно: не шире 96vw, лишнее — многоточием,
   но НЕ переносом (перенос и был дефектом). */
.toast.toast--one-line {
  white-space: nowrap;
  max-width: 96vw;
  width: max-content;
  overflow: hidden;
  text-overflow: ellipsis;
}
.toast.toast--error {
  background: var(--err-bg);
  border-color: var(--err-line);
  border-left-color: var(--err-bar);
  color: var(--err-fg);
}
.toast.toast--ok {
  background: var(--ok-bg);
  border-color: var(--ok-line);
  border-left-color: var(--ok-bar);
  color: var(--ok-fg);
}
.toast.toast--warn {
  background: var(--warn-bg);
  border-color: var(--warn-line);
  border-left-color: var(--warn-bar);
  color: var(--warn-fg);
}
/* Prominent (с кнопкой Undo) использует тот же светлый стиль темы,
   но крупнее (см. .toast--prominent правила ниже). */

/* toast--near-cursor — устаревший вариант (v0.73 → центр). Опция
   остаётся в JS для обратной совместимости, но CSS-класс больше
   ничего особого не добавляет: тост всё равно показывается в центре
   по умолчанию. Удалить класс из API можно отдельным рефактором. */
.toast--near-cursor {
  /* no overrides — same as default */
}

/* Тост-уведомление с кнопкой действия (Undo) — КРУПНЕЕ обычного и в
   стеке #toast-stack (см. .toast). Кнопка «Отменить» подсвечена primary —
   оператор должен сразу её заметить.
   v0.73.0: фон в стиле темы (--surface), не выпадает визуально. */
.toast--prominent {
  padding: 14px 18px 14px 22px;
  border-radius: var(--r-card);
  font-size: var(--fs-lg);
  font-weight: 600;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.18), 0 2px 6px rgba(0, 0, 0, 0.10);
  display: inline-flex;
  align-items: center;
  gap: 14px;
  pointer-events: auto;     /* prominent с кнопкой — нужны клики */
}
.toast--prominent.show {
  transform: translateY(0);
}
.toast--prominent .toast__text {
}
/* Кнопка «Отменить» — крупная, красная (danger-цвет темы), легко заметить
   (запрос оператора 2026-06-04: кнопка отмены должна быть в красноватом цвете). */
.toast--prominent .toast__action {
  background: var(--danger-fg);
  border: none;
  color: var(--on-accent);
  padding: 8px 16px;
  border-radius: var(--r-card);
  font-size: var(--fs-base);
  font-weight: var(--fw-bold);
  cursor: pointer;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.18);
}
.toast--prominent .toast__action:hover {
  background: var(--danger-fg);
}
.toast--prominent .toast__action:focus-visible {
  outline: 2px solid var(--danger-fg);
  outline-offset: 2px;
}

/* Тост с действием в обычном (не prominent) виде — оставляем мелкий
   inline-стиль для редких случаев, когда явно не нужно центрирование. */
/* ВАЖНО: только для НЕ-prominent тостов. У .toast--prominent кнопка
   оформляется правилом выше (синий фон, тёмный текст). Раньше селектор
   был просто «.toast .toast__action» — та же специфичность, что и у
   prominent-правила, но идёт ПОЗЖЕ, поэтому перебивал его: кнопка
   «Отменить» получала белый текст на светлом фоне (--surface с v0.73.0)
   и была невидимой (жалоба оператора 2026-05-27). :not(...) снимает
   наложение, prominent-кнопка снова контрастная. */
.toast:not(.toast--prominent) .toast__action {
  margin-left: 12px;
  background: var(--danger-fg);
  border: none;
  color: var(--on-accent);
  padding: 4px 10px;
  border-radius: var(--r-chip);
  font-size: var(--fs-sm);
  font-weight: 600;
  cursor: pointer;
}
.toast:not(.toast--prominent) .toast__action:hover { background: var(--danger-fg); }

/* ============ Command palette (Ctrl+K) ============ */
/* z-index должен быть выше Leaflet-контролов (.leaflet-control = 1000),
   sticky-тулбара журнала (1100), топбара ОСИРИС (1200), open-add dropdown
   (1500), модалок (2000) и update-overlay (99999) — иначе при открытии
   на странице карты палитра окажется ПОД маркер-/control-панелями
   Leaflet. Ставим заведомо больше всех, кроме update-overlay (он редкий
   и должен оставаться поверх палитры).
   Цвета — все через CSS-переменные текущей темы (sapphire / anthracite
   / malachite / ochre / garnet), чтобы палитра не выбивалась из
   общего стиля программы. */
.cmd-palette {
  position: fixed;
  inset: 0;
  z-index: var(--z-palette);
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding-top: 12vh;
}
.cmd-palette__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.4);
  backdrop-filter: blur(2px);
}
.cmd-palette__panel {
  position: relative;
  /* Ширина увеличена в v0.69-dev: на узком списке (680 px) каждая
     команда занимала две строки (title + hint), вертикальной
     прокрутки было слишком много. Теперь панель ~960 px, item —
     одна строка с двухколоночной раскладкой (title слева, hint
     справа), команд на экран помещается заметно больше. */
  width: min(960px, 96vw);
  max-height: 75vh;
  background: var(--surface);
  color: var(--fg);
  border-radius: var(--r-card);
  box-shadow: var(--shadow), 0 24px 64px rgba(0, 0, 0, 0.32);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border: 1px solid var(--border);
}
.cmd-palette__input {
  width: 100%;
  border: none;
  outline: none;
  background: transparent;
  color: var(--fg);
  font-size: var(--fs-lg);
  padding: 14px 18px;
  border-bottom: 1px solid var(--border);
}
.cmd-palette__input::placeholder { color: var(--muted); }
.cmd-palette__list {
  overflow-y: auto;
  flex: 1;
  background: var(--surface);
}
/*
  С v0.69-dev — элементы палитры в две колонки: название слева,
  описание справа. Цель — уместить больше команд по высоте, чтобы
  оператор мог увидеть весь список без вертикальной прокрутки.
  На узких экранах (<= 640px) возвращаемся к вертикальной раскладке,
  иначе подсказка не помещается рядом с длинным названием.
*/
.cmd-palette__item {
  padding: 10px 18px;
  cursor: pointer;
  border-bottom: 1px solid var(--border-soft, var(--border));
  display: flex;
  align-items: baseline;
  gap: 16px;
}
.cmd-palette__item:last-child { border-bottom: none; }
.cmd-palette__item:hover { background: var(--surface-alt); }
.cmd-palette__item--active {
  background: var(--primary-soft);
  border-left: 3px solid var(--primary);
  padding-left: 15px;       /* компенсируем 3 px бордера */
}
.cmd-palette__item-title {
  font-size: var(--fs-md);
  font-weight: 500;
  color: var(--fg);
  flex: 0 0 300px;
  min-width: 0;
  word-break: break-word;
}
.cmd-palette__item-hint {
  font-size: var(--fs-sm);
  color: var(--muted);
  flex: 1 1 auto;
  min-width: 0;
  word-break: break-word;
}
@media (max-width: 640px) {
  .cmd-palette__item { flex-direction: column; align-items: stretch; gap: 2px; }
  .cmd-palette__item-title { flex: 0 0 auto; }
}
.cmd-palette__empty {
  padding: 24px;
  color: var(--muted);
  text-align: center;
  font-size: var(--fs-base);
}
.cmd-palette__footer {
  border-top: 1px solid var(--line);
  padding: var(--sp-2) var(--sp-5);
  display: flex;
  gap: var(--sp-5);
  color: var(--muted);
  font-size: var(--fs-sm);
  background: var(--surface-2);
}
.cmd-palette__footer kbd {
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--r-chip);
  padding: 1px var(--sp-2);
  font-size: var(--fs-sm);
  color: var(--text-2);
}

/* ============ Страница логина ============ */
.auth {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  background: var(--bg);
}

.auth__box {
  width: 100%;
  max-width: 400px;
  padding: var(--sp-8);
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--r-card);
  box-shadow: var(--shadow-menu);
}

.auth__brand {
  margin: 0 0 var(--sp-1);
  font-size: var(--fs-h1);
  color: var(--accent-deep);
  font-family: var(--font-title);
  font-weight: var(--fw-normal);
  text-align: center;
}
.auth__hint {
  margin: 0 0 var(--sp-6);
  color: var(--muted);
  text-align: center;
  font-size: var(--fs-base);
}

.auth__form { display: flex; flex-direction: column; gap: var(--sp-4); }
.auth__form label {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  font-size: var(--fs-sm);
  color: var(--muted);
}
.auth__form input {
  padding: 0 var(--sp-3);
  min-height: var(--h-control);
  border: 1px solid var(--line-2);
  border-radius: var(--r-field);
  font-size: var(--fs-base);
  background: var(--surface-2);
  color: var(--text);
}
.auth__form input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--focus-ring);
}

.auth__error {
  padding: var(--sp-3) var(--sp-4);
  /* danger-токены темы — как у .modal__error. */
  background: var(--err-bg);
  border: 1px solid var(--err-line);
  border-left: 3px solid var(--err-bar);
  color: var(--danger-fg);
  border-radius: var(--r-card);
  font-size: var(--fs-base);
}

.auth__footnote {
  margin-top: var(--sp-5);
  font-size: var(--fs-sm);
  text-align: center;
}

/* ============================================================================
   Фаза 3: фото-сетка для карточек участка и захоронения
   ============================================================================ */
.photo-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: 12px;
  margin: 12px 0 24px;
}
.photo-grid__item {
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-chip);
  overflow: hidden;
  text-decoration: none;
  color: inherit;
  box-shadow: var(--shadow);
}
.photo-grid__item:hover { text-decoration: none; }
.photo-grid__item img {
  width: 100%;
  height: 160px;
  object-fit: cover;
  display: block;
  background: var(--surface-2);
}
.photo-grid__missing {
  height: 160px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--muted);
  font-size: var(--fs-sm);
  background: var(--surface-alt);
  border-bottom: 1px solid var(--border);
}
.photo-grid__caption {
  padding: 6px 8px;
  font-size: var(--fs-sm);
  color: var(--muted);
  word-break: break-all;
}

/* ============================================================================
   Фаза 4: вкладки и сетка полей для карточки книги захоронений
   ============================================================================ */
.tabs { background: var(--surface); border: 1px solid var(--line-2); border-radius: var(--r-card); padding: 0; }
.tabs__nav {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-1);
  border-bottom: 1px solid var(--line-2);
  padding: var(--sp-2) var(--sp-2) 0;
  background: var(--surface-2);
  border-radius: var(--r-card) var(--r-card) 0 0;
}
.tabs__btn {
  background: transparent;
  border: 1px solid transparent;
  min-height: var(--h-control);
  padding: 0 var(--sp-4);
  cursor: pointer;
  font-family: var(--font-ui);
  font-size: var(--fs-base);
  border-radius: var(--r-chip) var(--r-chip) 0 0;
  color: var(--text-2);
  margin-bottom: -1px;
}
.tabs__btn:hover { background: var(--accent-100); color: var(--accent-deep); }
.tabs__btn.is-active {
  background: var(--surface);
  border-color: var(--line-2);
  border-bottom-color: var(--surface);
  color: var(--text);
  font-weight: var(--fw-medium);
}
.tabs__btn:disabled { opacity: .5; cursor: not-allowed; }
/* Тело вкладки — 24 px со всех сторон (раунд 2, §12 п.7): текст и
   контролы стояли вплотную к левой кромке рамки вкладок. */
.tabs__pane { display: none; padding: var(--sp-6); }
.tabs__pane.is-active { display: block; }
/* Обзор — единственная вкладка без рамки-карточки вокруг: её блоки и
   так карточки, вторая рамка была бы рамкой в рамке. */
.tabs__pane[data-pane="overview"] { padding-top: var(--sp-4); }

.fields-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 6px 24px;
  font-size: var(--fs-base);
}
.fields-grid > div { padding: 2px 0; }
.fields-grid b { color: var(--muted); font-weight: 500; }

.form-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px 16px;
}
.form-grid .field { margin: 0; }
/* Модификаторы для редких случаев. По умолчанию form-grid — 2 колонки;
   используйте --cols-1 для одиночной колонки, --cols-4 для плотных
   карточек участка. Раньше эти случаи задавались inline через
   style="grid-template-columns:..." — переезжаем на класс. */
.form-grid--cols-1 { grid-template-columns: 1fr; }
.form-grid--cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }

.resp-card {
  background: var(--surface-alt);
  border: 1px solid var(--border);
  border-radius: var(--r-chip);
  padding: var(--sp-3) var(--sp-4);
  margin-bottom: 8px;
  font-size: var(--fs-base);
  line-height: 1.5;
  /* Поля выкладываем горизонтально в один flex-wrap, чтобы карточка
     не растягивалась в высоту: короткие поля стоят в строку, длинные
     (паспорт, адрес) переходят на отдельную строку через --wide. */
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 18px;
}
.resp-card__field { white-space: nowrap; }
.resp-card__field--wide {
  /* Длинное поле занимает всю ширину карточки (или хотя бы свою
     значительную часть) и может переноситься внутри. */
  flex: 1 1 100%;
  white-space: normal;
}
.resp-card__actions {
  /* Кнопки прижимаются к правому краю карточки, на отдельной «строке»
     через 100%-флекс — иначе короткие поля давят на них слева. */
  flex: 1 1 100%;
  display: flex;
  justify-content: flex-end;
  gap: 6px;
  margin-top: 4px;
}
/* Список «История ответственных» — карточки в ряд (auto-fit grid).
   Минимум 360px на колонку — узкая колонка делает поля паспорта
   нечитаемыми; на широком экране ставится 2-3-4 в ряд. */
.resp-history {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(360px, 1fr));
  gap: 10px;
  margin-bottom: 12px;
}
.resp-history .resp-card { margin-bottom: 0; }

/* Кликабельная таблица — каждая строка действует как кнопка. Используется
   в диалоге «Переместить на другой участок» (выбор участка кликом) и
   может пригодиться в других диалогах табличного выбора. */
table.table--clickable tbody tr { cursor: pointer; }
table.table--clickable tbody tr:hover { background: var(--surface-alt); }
/* Подсветка строки, к которой запись уже привязана (текущая) — оператор
   видит, какой участок выбран сейчас, и не пытается перенести на тот же. */
/* Цвета — токены акцента: прежние литералы индиго не следовали теме и в
   малахитовой, охровой и гранатовой темах оставались синими. */
table.table tbody tr.row--current { background: var(--accent-100); }
table.table--clickable tbody tr.row--current:hover { background: var(--accent-200); }

/* Для большой формы редактирования записи книги — широкий модал + секции */
.modal--wide { max-width: 720px; max-height: 85vh; overflow-y: auto; }

/* ---- Окно «Разобрать»: попарное сравнение реквизитов при объединении ---- */
/* Панель над таблицей: тумблер «Только отличия» + счётчики. */
.mp-toolbar { display: flex; align-items: center; justify-content: space-between;
  gap: 12px; margin: 4px 0 8px; flex-wrap: wrap; }
.mp-toggle { display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
  font-size: var(--fs-base); user-select: none; }
.mp-counts { font-size: var(--fs-sm); }
/* Узкая колонка-подпись поля. */
.mp-table td.mp-label { white-space: nowrap; color: var(--text-2); font-weight: 600;
  width: 1%; vertical-align: top; }
/* Заголовок раздела (Умерший / Захоронение / …). */
.mp-table tr.mp-section td { background: var(--surface-2); font-weight: var(--fw-bold); color: var(--accent-deep);
  font-size: var(--fs-sm);
  padding-top: 10px; }
/* Совпадающее значение — спокойный фон, показано один раз по центру. */
.mp-table tr.mp-same td.mp-same-val { color: var(--muted); text-align: center; }
/* Различающееся поле — мягкая подсветка, чтобы сразу бросалось в глаза. */
.mp-table tr.mp-collision > td { background: rgba(245, 158, 11, 0.10); }
.mp-table label.mp-pick { display: block; cursor: pointer; }
.mp-table label.mp-pick input { margin-right: 6px; }
/* Режим «Только отличия»: прячем совпадающие строки и заголовки разделов. */
.mp-table.only-diffs tr.mp-same,
.mp-table.only-diffs tr.mp-section { display: none; }
/* Примечание про разные участки (раздельный выбор номера/геометрии). */
.mp-note { border-left: 3px solid var(--accent); background: var(--accent-100);
  padding: 6px 10px; border-radius: var(--r-chip); margin: 6px 0; }

/* Форма создания/редактирования участка — компактная раскладка
   3-колонок (Кладбище/Квартал/Ряд, Длина/Ширина/Площадь) и плотный
   гутер между полями: чтобы вся форма влезала на экран без вертикальной
   прокрутки тела модалки. На очень коротких экранах (height < 720px)
   модалка всё равно скроллится — это безопасный fallback. */
.modal--plot { max-width: 760px; }
.modal--plot .modal__body { padding-top: 12px; padding-bottom: 12px; }
.modal--plot .modal__body .field { margin-bottom: 8px; }
.modal--plot .modal__body .form-section { margin: 10px 0 6px; }
.modal--plot .modal__body textarea { min-height: 60px; }
/* Строки «Кладбище/Квартал/Ряд» и «Вид/Номер», «Длина/Ширина/Площадь»:
   колонки обязаны ужиматься под ширину модалки. Без minmax(0,1fr) +
   min-width:0 длинное имя кладбища в <select> распирает grid, и строка
   вылезает за правый край окна (жалоба оператора 2026-05-27). */
.modal--plot .field-grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.modal--plot .field-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.modal--plot .field-grid .field { min-width: 0; }
.modal--plot .field-grid .field select,
.modal--plot .field-grid .field input { min-width: 0; max-width: 100%; }

/* --- Форма «Редактировать кладбище» ---
   Двухколоночный layout: слева — все текстовые поля компактно, справа —
   мини-карта с кнопками рисования полигона + таблица точек. Цель —
   уложить форму на один экран без вертикальной прокрутки на типичном
   мониторе (≥ 768px высота).
   На узких экранах (< 900px по ширине) — fallback на одну колонку. */
/* Диалог кладбища — 1180 px, две колонки (README §5.7). Прежде он
   занимал 80vw и делился на три: третья колонка держала около 300 px
   ради одной свёрнутой строки «Точки периметра (8 точек)». */
.modal--cemetery {
  width: min(1180px, 94vw);
  max-width: 1180px;
  max-height: min(640px, 92vh);
}
.modal--cemetery .modal__body .field { margin-bottom: var(--sp-3); }
.modal--cemetery .modal__body textarea { min-height: 72px; }
.cemetery-form-grid {
  display: grid;
  grid-template-columns: 380px minmax(0, 1fr);
  gap: var(--sp-5);
}
@media (max-width: 900px) {
  .cemetery-form-grid { grid-template-columns: minmax(0, 1fr); }
}
.cemetery-form-grid__col {
  display: flex;
  flex-direction: column;
  min-width: 0;
}
@media (max-width: 1100px) {
  /* Узкие экраны: 2 колонки (реквизиты + карта/таблица сверху-вниз). */
  .cemetery-form-grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr); }
}
@media (max-width: 700px) {
  .cemetery-form-grid { grid-template-columns: 1fr; }
}

/* Маркеры точек периметра на мини-карте кладбища: divIcon с
   нумерацией (1, 2, 3, …). Пользователь видит, какую точку
   удалит, если кликнет по ней. На главной карте /map тех же
   маркеров не используем — только в форме редактирования. */
.cem-vertex-marker {
  /* Сам контейнер делаем «прозрачным»: маркер — это белый кружок
     с цифрой по центру, без фона у leaflet-divicon-div. */
  background: transparent !important;
  border: none !important;
}
.cem-vertex-pin {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border-radius: var(--r-pill);
  background: var(--accent-strong);
  color: var(--on-accent);
  font: var(--fw-bold) var(--fs-sm)/1 var(--font-ui);
  border: 2px solid var(--surface);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.25);
  cursor: pointer;
  /* Центрирование — через iconAnchor в JS (iconSize: 22, iconAnchor: 11,11).
     Раньше тут стоял transform: translate(-50%, -50%) — визуально работало,
     но DOM-bbox оставался в исходной позиции (leftTop=latLng), и click-hit-test
     по видимому пину иногда попадал в соседний маркер. На замыкании
     треугольника это удаляло не ту точку. */
}
.cem-vertex-pin:hover {
  background: var(--accent-deep);
}

/* Аналогичный пин для OsirisDrawTool на главной карте (рисование
   кварталов, рядов, участков, областей, инфра-полигонов и линий).
   Поведение единое: drag — перемещение, клик — удалить, клик по
   первой точке (для polygon) — замкнуть и завершить. */
.osiris-draw-pin {
  background: transparent !important;
  border: none !important;
}
.osiris-draw-pin__dot {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  /* Цвет вершины задаётся per-layer через CSS-переменную --pin-color
     (см. OsirisEditTool.pinColorFor): кружок повторяет цвет своего
     полигона, чтобы было видно, к какому объекту относится узел. */
  background: var(--pin-color, var(--accent));
  color: var(--on-accent);
  font: var(--fw-bold) var(--fs-sm)/1 var(--font-ui);
  border: 2px solid var(--surface);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.25);
  cursor: grab;
  /* Центрирование — через iconAnchor в JS, не CSS-transform. */
}
.osiris-draw-pin__dot:hover {
  filter: brightness(0.85);
}
.osiris-draw-pin__dot:active {
  cursor: grabbing;
}
/* Полноэкранная модалка с картой — для диалога рисования участка
   («Нарисовать на карте» из формы создания плота). Карта внутри
   занимает почти всю высоту, тулбар закреплён сверху, кнопки «ОК /
   Отмена» — справа в шапке. */
.modal--map { max-width: 95vw; width: 95vw; max-height: 95vh; height: 95vh; }
.modal--map .modal__body {
  padding: 0;
  display: flex; flex-direction: column;
  flex: 1 1 auto; min-height: 0; overflow: hidden;
}
/* Внутри .modal__body есть `<form>` (обёртка openModal), которая ломает
   flex-chain: form по умолчанию display:block. Растягиваем её тоже как
   flex-column на всю высоту. */
.modal--map .modal__body > form {
  display: flex; flex-direction: column;
  flex: 1 1 auto; min-height: 0; height: 100%;
}
.plot-draw-toolbar {
  display: flex; flex-wrap: wrap; gap: 8px;
  padding: var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--border);
  background: var(--surface-alt); align-items: center;
  flex-shrink: 0;
}
.plot-draw-toolbar .field { margin: 0; }
.plot-draw-toolbar .field span { display: block; font-size: var(--fs-sm); color: var(--muted); margin-bottom: 2px; } /* был несуществующий --text-muted */
.plot-draw-toolbar .field select {
  padding: 4px 8px; border: 1px solid var(--border); border-radius: var(--r-chip);
  background: var(--surface); font-size: var(--fs-base); min-width: 160px;
}
.plot-draw-toolbar__sep { width: 1px; height: 28px; background: var(--border); margin: 0 4px; }
#plot-draw-map { flex: 1 1 auto; min-height: 300px; width: 100%; }
.form-section {
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
  color: var(--muted);
  margin: var(--sp-5) 0 var(--sp-2);
  padding-bottom: 4px;
  border-bottom: 1px solid var(--border);
}
.form-section:first-of-type { margin-top: 4px; }

/* ============================================================================
   Общие паттерны страниц: page__actions, filter-panel — используются и в
   журнале книги захоронений, и в других списках. Меняя здесь — меняем везде.
   ============================================================================ */

/* Кнопочная группа в заголовке страницы (справа от h1). */
.page__actions {
  display: inline-flex;
  gap: 8px;
  align-items: center;
}

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

/* Бейдж активных фильтров внутри кнопки-тогглера. */
.filters-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 var(--sp-1);
  margin-left: var(--sp-1);
  border-radius: var(--r-pill);
  background: var(--accent-strong);
  color: var(--on-accent);
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
}
/* display:inline-flex перебивает браузерное [hidden]{display:none}, и
   счётчик показывал «Фильтры 0», хотя разметка его прятала. Нуля на
   экране быть не должно (README §4.5). */
.filters-badge[hidden] { display: none; }
.filters-chevron { margin-left: var(--sp-1); color: var(--muted); font-size: var(--fs-sm); }

/* Общая сворачиваемая панель фильтров. Скрывается HTML-атрибутом hidden,
   что надёжнее любых grid-trick'ов (браузер не рендерит вовсе). */
.filter-panel {
  margin-bottom: 12px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-card);
  padding: 14px 16px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.filter-panel[hidden] { display: none; }

/* Верхний ряд панели: большие важные поля (кладбище, поиск). */
.filter-panel__row {
  display: flex;
  gap: 12px;
  align-items: flex-end;
  flex-wrap: wrap;
}
.filter-panel__row .field { margin: 0; flex: 1; min-width: 180px; }

/* Подписи групп внутри панели. */
.filter-panel .journal-filters {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 16px;
  padding: 4px 0 0;
}
.filter-panel .journal-filters__group {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.filter-panel .journal-filters__group-title {
  font-size: var(--fs-sm);
  color: var(--muted);
  font-weight: 600;
  margin-bottom: 2px;
  border-bottom: 1px dashed var(--border);
  padding-bottom: 3px;
}

/*
  С v0.69.5 — новая горизонтальная вёрстка фильтров реестра.
  .journal-filters--rows переопределяет grid из старой вёрстки на
  вертикальный flex: каждая строка занимает всю ширину панели, поля
  внутри строки идут слева-направо. Заголовок строки слева,
  фиксированной ширины, чтобы инпуты выровнялись по вертикали.

  Чипы источника (.chip-group) — компактные radio-кнопки в одну
  линию. Активный чип получает primary-фон, как кнопка .btn--primary.
*/
.filter-panel .journal-filters--rows {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.journal-filters__row {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}
.journal-filters__row-label {
  flex: 0 0 72px;
  font-size: var(--fs-base);
  color: var(--muted);
  font-weight: 600;
}
/* Поля фильтра: подпись ПЕРЕД полем (инлайн), как у блока дат. Три ячейки
   в ряду равны (flex 1 1 0) → ряды Локация/ФИО/Даты выровнены по колонкам. */
.journal-filters__row .field {
  flex: 1 1 0;
  min-width: 0;
  margin: 0;
  flex-direction: row;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
}
.journal-filters__row .field > span {
  flex: 0 0 auto;
  font-weight: 600;
  white-space: nowrap;
}
.journal-filters__row .field > select,
.journal-filters__row .field > input {
  flex: 1 1 80px;
  min-width: 0;
}
/* Подсказка под селектором квартала (если кладбище не выбрано) — на всю
   ширину ячейки, под полем, чтобы не ломать инлайн-строку. */
.journal-filters__row .field > p {
  flex: 1 1 100%;
  margin: 2px 0 0;
}
.journal-filters__cell--xs { flex: 0 0 180px !important; min-width: 0 !important; }
.journal-filters__cell--sm { flex: 1 1 200px !important; }
.journal-filters__cell--md { flex: 1 1 240px !important; }

/* Три диапазона дат (рождение/смерть/захоронение) в одну строку —
   компактный фильтр реестра (v0.82.7). Каждый диапазон: подпись + «от–до». */
.journal-filters__row--dates { align-items: center; }
.journal-filters__daterange {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex: 1 1 0;
  min-width: 0;
}
.journal-filters__daterange-cap {
  font-size: var(--fs-sm);
  color: var(--muted);
  font-weight: 600;
  white-space: nowrap;
}
.journal-filters__daterange input[type="date"] {
  flex: 1 1 90px;
  min-width: 0;
  /* Стиль как у остальных полей приложения (.field input), иначе нативный
     date-инпут выглядит чужеродно (другой шрифт/рамка). */
  padding: 6px 8px;
  border: 1px solid var(--border);
  border-radius: var(--r-chip);
  font-family: inherit;
  font-size: var(--fs-base);
  line-height: 1.3;
  min-height: 34px;
  background: var(--input-bg);
  color: var(--fg);
  box-sizing: border-box;
}
.journal-filters__daterange input[type="date"]:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 3px var(--focus-ring);
}
.journal-filters__dash { color: var(--muted); }

/* Источник: чип-группа поверх обычной строки фильтра. */
.journal-filters__row--source { align-items: center; }
.chip-group {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 6px;
}
.chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 6px 14px;
  border: 1px solid var(--border);
  border-radius: var(--r-pill);
  background: var(--btn-bg);
  cursor: pointer;
  font-size: var(--fs-base);
  user-select: none;
  transition: background .1s ease, border-color .1s ease, color .1s ease;
}
.chip:hover { background: var(--btn-bg-hover); }
.chip input[type="radio"],
.chip input[type="checkbox"] {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.chip:has(input:checked) {
  background: var(--primary);
  border-color: var(--primary);
  color: var(--on-accent);
  font-weight: 500;
}

/* На узких экранах label-строки сворачиваются в самостоятельные
   строки, чтобы инпуты не давились. */
@media (max-width: 720px) {
  .journal-filters__row { flex-direction: column; align-items: stretch; }
  .journal-filters__row-label { flex: 0 0 auto; padding-bottom: 0; }
  .journal-filters__cell--xs,
  .journal-filters__cell--sm,
  .journal-filters__cell--md { flex: 1 1 auto !important; }
}
.filter-panel .field { margin: 0; }
.filter-panel .field > span { font-size: var(--fs-sm); color: var(--muted); }
/* В панели фильтров используем те же базовые стили .field, только чуть
   плотнее шрифт. Критично ИСКЛЮЧИТЬ чекбоксы/радио/color/file — иначе
   min-height/width превращает их в гигантские прямоугольники. */
.filter-panel .field input:not([type="checkbox"]):not([type="radio"]):not([type="color"]):not([type="file"]),
.filter-panel .field select,
.filter-panel .field textarea {
  font-size: var(--fs-base);
  min-height: 32px;
  padding: 6px 8px;
  width: 100%;
}
.filter-panel__footer {
  display: flex;
  justify-content: flex-end;
  padding-top: 6px;
  border-top: 1px solid var(--border);
}

/* Селектор кладбища — базовое оформление одинаковое на всех страницах. */
.cemetery-picker-inline {
  padding: 6px 8px;
  border: 1px solid var(--border);
  border-radius: var(--r-chip);
  font-size: var(--fs-base);
  background: var(--input-bg);
  width: 100%;
  font-family: inherit;
  color: var(--fg);
  min-height: 32px;
  box-sizing: border-box;
}

/* Книга захоронений: страница из двух частей.
   Верх (заголовок, поиск, фильтры) — в обычном ограниченном контейнере,
   чтобы длинные ряды полей не растягивались на весь экран.
   Нижний блок (таблица + боковая панель) — на всю ширину окна браузера. */
.burials-page { display: flex; flex-direction: column; }
.burials-page__top {
  max-width: 1400px;
  width: 100%;
  margin: 0 auto;
  /* Горизонтальный padding 40px — синхронно с .content (см. выше),
     чтобы реестр на узких окнах не упирался в бока. */
  padding: 24px 40px 0;
  box-sizing: border-box;
}

/* Тулбар журнала: всегда виден между page__header и панелью фильтров.
   Содержит селект кладбища и быстрый поиск — одни из самых частых действий. */
.journal-toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-4);
  margin-bottom: var(--sp-4);
  align-items: flex-end;
}
/* Ширины из README §5.1: селект кладбища 220 px, поиск 420 px. Поиск
   тянется дальше, когда экран шире, но не ужимается меньше 420 —
   в него набирают фамилию целиком. */
.journal-toolbar .field { margin: 0; }
.journal-toolbar .field--cemetery { flex: 0 0 220px; }
.journal-toolbar .field--search { flex: 1 1 420px; min-width: 420px; }

/* «Найдено 13 из 1 248 · Сбросить» — рядом с поиском. */
.journal-found {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: var(--h-control);
  font-size: var(--fs-sm);
  color: var(--muted);
  white-space: nowrap;
}
.journal-found b { color: var(--text); font-weight: var(--fw-medium); }
.journal-found[hidden] { display: none; }

/* Сегментированный переключатель — для взаимоисключающих вариантов
   (плотность строк, схема/спутник). Набор чекбоксов для такого выбора
   не годится: он допускает «выбрано два» и «не выбрано ничего». */
.seg {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  margin-left: auto;   /* прижат к правому краю тулбара */
}
.seg__label { font-size: var(--fs-sm); color: var(--muted); }
.seg__btn {
  min-height: var(--h-control);
  padding: 0 var(--sp-3);
  border: 1px solid var(--line-2);
  background: var(--surface);
  color: var(--text-2);
  font: inherit;
  font-size: var(--fs-base);
  cursor: pointer;
  white-space: nowrap;
}
/* Сегменты стыкуются в одну «таблетку»: скругления только по краям,
   общие границы не удваиваются. */
.seg__btn + .seg__btn { margin-left: -1px; }
.seg__btn:first-of-type { border-radius: var(--r-field) 0 0 var(--r-field); }
.seg__btn:last-of-type { border-radius: 0 var(--r-field) var(--r-field) 0; }
.seg__btn:hover { background: var(--accent-100); color: var(--accent-deep); }
.seg__btn.is-active {
  background: var(--accent-100);
  border-color: var(--accent-300);
  color: var(--accent-deep);
  font-weight: var(--fw-medium);
  position: relative;
  z-index: 1;
}

/* Двухколоночная раскладка: таблица слева на всю оставшуюся ширину,
   боковая панель справа фиксированной ширины (360px). Заполняет всю
   ширину окна браузера (страница в режиме content--full). */
.journal-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--w-plot-panel);
  gap: var(--sp-4);
  /* Поля экрана 24 px (README §3.1). Прежние 80 px по горизонтали
     ставили, чтобы реестр не прижимался к краю, но на 1366×768 они
     съедали 160 px — это две колонки таблицы. Воздух теперь делают
     отступы внутри блоков, а не поля страницы. */
  padding: var(--sp-5) var(--sp-6) var(--sp-6);
  align-items: start;
  width: 100%;
  box-sizing: border-box;
}
.journal-layout__main { min-width: 0; }
/* Пока запись не выбрана, панель деталей не занимает колонку — всю
   ширину забирает таблица. Класс ставит JS (hideJournalSide/
   showJournalSide) вместе с атрибутом hidden у самой панели. */
.journal-layout--no-side { grid-template-columns: minmax(0, 1fr); }
/* Горизонтальная прокрутка таблицы реестра, когда включено много колонок
   и они не помещаются по ширине (v0.82.2, ТЗ оператора). Раньше .table-wrap
   клипал overflow:hidden — правые колонки просто обрезались. Скроллим
   только реестр; остальные таблицы не трогаем.
   С 2026-07 — ещё и ВЕРТИКАЛЬНЫЙ скролл внутри обёртки: overflow-x:auto
   делал её scroll-контейнером, из-за чего position:sticky у thead липнул
   к обёртке, а не к окну — шапка «не работала». Теперь шапка липнет к
   верхней кромке самой обёртки. 220px ≈ топбар (56) + заголовок и тулбар
   реестра над таблицей; цифру можно уточнить по фактической вёрстке. */
.journal-layout .table-wrap {
  overflow: auto;
  max-height: calc(100vh - 200px);
}
.journal-layout__side {
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--r-card);
  padding: var(--sp-4);
  position: sticky;
  top: var(--sp-4);
  max-height: calc(100vh - 80px);
  overflow-y: auto;
  box-sizing: border-box;
}
/* На узких экранах панель уезжает вниз под таблицу, чтобы таблица
   не сжималась до нечитаемого состояния. */
@media (max-width: 1100px) {
  .journal-layout {
    grid-template-columns: minmax(0, 1fr);
  }
  .journal-layout__side { position: static; max-height: none; }
}

/* Плоская таблица журнала: одна запись = одна строка, колонки несут по одному
   самодостаточному значению. Sticky-header чтобы шапка не терялась при прокрутке. */
.journal-table {
  border-collapse: separate;
  border-spacing: 0;
  font-size: var(--fs-base);
  /* Ширины столбцов берутся из <colgroup> (см. journalColWidths в app.js) и
     не зависят от длины содержимого. Без fixed браузер раздавал остаток
     строки колонке без ограничения — ею была ФИО, и на широком экране
     половина таблицы уходила в пустоту внутри неё. Здесь же держится
     перетаскивание ширины мышью: менять width у <col> дёшево. */
  table-layout: fixed;
}
.journal-table thead th {
  /* Шапка и её метрики — общие правила таблиц (капитель 11 px на
     --accent-100, высота 40). Здесь только то, что специфично реестру. */
  padding: 0 var(--sp-3);
  white-space: nowrap;
}
.journal-table tbody td {
  padding: 0 var(--sp-3);
  vertical-align: middle;
  line-height: 1.35;
  /* Строка в одну линию — иначе высота таблицы задаётся самой длинной
     ячейкой и на 1366×768 помещается на четверть меньше записей.
     Длинное значение обрезается многоточием, полное — в подсказке. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  /* max-width здесь больше не нужен: при table-layout:fixed ширину задаёт
     <colgroup>, а max-width ячейки браузер игнорирует. */
}

/* ФИО — главная колонка: гибкая ширина, PT Serif 16 px. Оператор ищет
   человека, поэтому именно она открывает таблицу. */
.journal-table .col-fio {
  min-width: 220px;
  max-width: none;
  font-family: var(--font-title);
  font-size: var(--fs-md);
}
.journal-table .col-fio .fio-link { color: var(--text); cursor: pointer; }
.journal-table .col-fio .fio-link:hover { color: var(--accent-link); text-decoration: underline; }
/* Совпадение с поиском — акцентным цветом и полужирным, не жёлтым
   маркером: песочный в этой палитре занят ролью «внимание». */
.journal-table .col-fio mark,
.journal-table mark {
  background: transparent;
  color: var(--accent-deep);
  font-weight: var(--fw-bold);
}

/* Ширины столбцов реестра живут в JOURNAL_COL_WIDTH_DEFAULT (app.js) и
   приезжают в <colgroup>. Здесь — только оформление: выравнивание, цвет,
   шрифт. Возвращать сюда width нельзя: после перетаскивания мышью правило
   из CSS спорило бы с inline-шириной <col>. */
.journal-table .col-date { text-align: right; }
.journal-table th.col-date { text-align: right; }
/* Место: название кладбища слева, адрес внутри него чипом справа.
   Flex с min-width:0 — иначе длинное название («Восточное кладбище»)
   не сжимается и наезжает на чип. */
.journal-table td.col-place {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  height: var(--h-row);
}
.journal-table .col-place .place-cem {
  color: var(--text-2);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.journal-table .col-place .place-chip {
  flex: none;
  padding: 2px var(--sp-2);
  border-radius: var(--r-chip);
  background: var(--accent-100);
  color: var(--accent-deep);
  font-size: var(--fs-sm);
  white-space: nowrap;
}
.journal-table .col-type { color: var(--muted); }
.journal-table .col-photo { text-align: center; }
/* Паддинг снимаем только у ячеек: в шапке подпись «ФОТО» должна
   стоять с обычным отступом, как в соседних колонках. */
.journal-table td.col-photo { padding: 0; }
/* Ссылка занимает всю ячейку — попасть по метке в 20 px мышью в конце
   смены трудно, — а видимым остаётся компактный чип по центру. */
.journal-table .col-photo a.chip {
  display: flex;
  align-items: center;
  justify-content: center;
  height: var(--h-row);
  padding: 0;
  border-radius: 0;
  background: transparent;
}
.journal-table .col-photo a.chip > span {
  padding: 2px var(--sp-2);
  border-radius: var(--r-chip);
  background: var(--accent-100);
  color: var(--accent-deep);
}
.journal-table .col-photo a.chip:hover > span { background: var(--accent-200); }
.journal-table .col-audit { font-size: var(--fs-sm); color: var(--hint); }

/* Колонки, включаемые вручную через «Колонки таблицы». */
.journal-table .col-num-xs { text-align: center; }
.journal-table .col-coords { font-family: var(--font-mono); font-size: var(--fs-sm); }
.journal-table .col-actions { text-align: right; padding: 0 var(--sp-2); }

/* Меню «⋯» в строке таблицы: действия строки, включая опасные. */
.rowmenu { position: relative; display: inline-block; }
.rowmenu__list {
  position: absolute;
  right: 0;
  top: calc(100% + var(--sp-1));
  z-index: var(--z-menu);
  min-width: 200px;
  padding: var(--sp-1);
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--r-card);
  box-shadow: var(--shadow-menu);
  display: flex;
  flex-direction: column;
}
.rowmenu__list[hidden] { display: none; }
.rowmenu__item {
  display: flex;
  align-items: center;
  min-height: var(--h-icon-btn);   /* строка меню — 36, как во всех меню */
  padding: 0 var(--sp-3);
  border: none;
  border-radius: var(--r-chip);
  background: transparent;
  color: var(--text-2);
  font: inherit;
  font-family: var(--font-ui);
  font-size: var(--fs-base);
  text-align: left;
  white-space: nowrap;
  cursor: pointer;
}
.rowmenu__item:hover { background: var(--accent-100); color: var(--accent-deep); text-decoration: none; }
.rowmenu__item--danger { color: var(--danger-fg); }
.rowmenu__item--danger:hover { background: var(--err-bg); color: var(--danger-fg); }

/* Номер участка — ссылка акцентным цветом (README §5.8). */
.plot-link { color: var(--accent-link); font-weight: var(--fw-medium); }
.type-cell { display: inline-flex; align-items: center; gap: var(--sp-2); white-space: nowrap; }
/* Занятый участок — чип потемнее: «3 из 3» должно отличаться от «1 из 3»
   не только цифрой. */
.chip--full { background: var(--accent-200); }
/* Выключенное состояние — нейтральный серый чип: «отключена» не
   ошибка и не предупреждение, это просто другое состояние. */
.chip--off { background: var(--surface-2); color: var(--muted); }

/* Чип «есть» в колонке «Фото» и такие же метки в других таблицах. */
.chip {
  display: inline-block;
  padding: 2px var(--sp-2);
  border-radius: var(--r-chip);
  background: var(--accent-100);
  color: var(--accent-deep);
  font-size: var(--fs-sm);
  white-space: nowrap;
}
a.chip:hover { background: var(--accent-200); text-decoration: none; }

/* Подвал таблицы: слева — что выбрано и что с этим делать, справа —
   сколько записей показано. Прежде это был абзац под таблицей, который
   наезжал на строки. */
.table-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  min-height: var(--h-row);
  padding: 0 var(--sp-4);
  background: var(--surface-2);
  border: 1px solid var(--line-2);
  border-top: none;
  border-radius: 0 0 var(--r-card) var(--r-card);
  font-size: var(--fs-sm);
  color: var(--muted);
}
.table-foot__left { display: inline-flex; align-items: center; gap: var(--sp-2); }

/* Контентные ссылки (номер участка, ФИО на участке, «Выбранная запись» на
   карте, ссылки в подзаголовке карточки) — жирным цветом текста, а не
   светлым цветом темы (плохо читается на белом). Цвет темы — при наведении.
   Кнопки-ссылки (.btn) не трогаем. */
.journal-table .col-loc a:not(.btn),
.page-subhead a:not(.btn),
#map-side-selected a:not(.btn),
a.link-strong {
  color: var(--fg);
  font-weight: 600;
  text-decoration: none;
}
.journal-table .col-loc a:not(.btn):hover,
.page-subhead a:not(.btn):hover,
#map-side-selected a:not(.btn):hover,
a.link-strong:hover {
  color: var(--accent-link-hover);   /* текстовый цвет ссылки, а не заливки */
  text-decoration: underline;
}
/* Опциональные колонки реестра (v0.82.2). */
.journal-table .col-audit {
  color: var(--muted);
  font-size: var(--fs-sm);
  /* Не рвать длинное имя по буквам (иначе «Суперадминистрато|р» — одна
     буква на отдельной строке). Слово остаётся целым; если оно шире
     колонки — колонка сама чуть расширится, но без «сиротской» буквы. */
  overflow-wrap: normal;
  word-break: keep-all;
}
.journal-table .col-audit__time { display: block; font-size: var(--fs-sm); opacity: .75; white-space: nowrap; }
.journal-table .col-coords { color: var(--muted); font-variant-numeric: tabular-nums; }
.journal-table .col-photo a { color: var(--fg); font-weight: 600; text-decoration: none; }
.journal-table .col-photo a:hover { color: var(--accent-link-hover); text-decoration: underline; }

/*
  С v0.69.4 — массовое объединение в «Поиск и объединение записей».
  Чекбоксы в первой колонке + плавающая панель «Выбрано N» над
  таблицей. Цветовая гамма та же, что у .placeholder — оператор
  видит, что это «инструмент», а не отдельная страница.
*/
.dup-bulkbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  padding: var(--sp-3) var(--sp-4);
  background: var(--primary-soft);
  border: 1px solid var(--primary);
  border-radius: var(--r-chip);
  margin-bottom: 10px;
}
.dup-bulkbar__actions {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}
.col-checkbox { width: 32px; text-align: center; }
.col-checkbox input[type="checkbox"] { vertical-align: middle; }
/* Сортировка: клик по th переключает asc/desc, индикатор справа от текста.
   Активная колонка получает is-asc / is-desc, остальные показывают
   нейтральный «двойной треугольник», намекая, что по ним тоже можно
   отсортировать. */
.journal-table .col-sort {
  cursor: pointer;
  user-select: none;
  /* Якорь для «ручки» ширины (.col-grip), которая стоит у правого края. */
  position: relative;
}

/* «Ручка» ширины столбца: узкая полоса у правой границы заголовка.
   Ширина 9 px при видимой линии в 1 px — попасть мышью в одну линию в конце
   смены невозможно, это та же логика, что у ссылки на фото во всю ячейку.
   Линия проявляется при наведении на шапку: постоянные вертикальные полосы
   между столбцами утяжеляют таблицу, а «ручка» нужна изредка. */
.journal-table .col-grip {
  position: absolute;
  top: 0;
  right: -4px;
  width: 9px;
  height: 100%;
  cursor: col-resize;
  z-index: 1;
}
.journal-table .col-grip::before {
  content: "";
  position: absolute;
  top: 25%;
  bottom: 25%;
  left: 4px;
  width: 1px;
  background: var(--line-2);
  opacity: 0;
  transition: opacity .12s ease;
}
.journal-table thead:hover .col-grip::before { opacity: 1; }
.journal-table .col-grip:hover::before,
body.is-col-resizing .col-grip::before {
  opacity: 1;
  background: var(--primary);
  width: 2px;
}
/* Во время перетаскивания курсор «col-resize» держим на всём окне: мышь
   уходит далеко за пределы заголовка, а выделение текста мешает тянуть. */
body.is-col-resizing,
body.is-col-resizing * { cursor: col-resize !important; user-select: none !important; }
.journal-table .col-sort:hover { background: var(--bg-soft); }
/* Видимый фокус при клавиатурной навигации по заголовкам сортировки —
   по образцу .jrow:focus ниже. */
.journal-table .col-sort:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: -2px;
}
.journal-table .col-sort .col-sort__ind {
  display: inline-block;
  width: 0;
  height: 0;
  margin-left: 6px;
  vertical-align: middle;
  border-left: 4px solid transparent;
  border-right: 4px solid transparent;
  /* По умолчанию — серый «двойной треугольник» через двойной border */
  border-bottom: 4px solid var(--line-2);
  border-top: 4px solid var(--line-2);
  position: relative;
  top: -1px;
}
.journal-table .col-sort.is-asc .col-sort__ind {
  border-bottom: 5px solid var(--primary);
  border-top: none;
}
.journal-table .col-sort.is-desc .col-sort__ind {
  border-top: 5px solid var(--primary);
  border-bottom: none;
}
.jrow { cursor: pointer; }
.jrow:nth-child(even) { background: var(--surface-alt); }
.jrow:hover { background: var(--bg-soft); }
.jrow.is-selected { background: var(--primary-soft); }
/* Токен, а не синий литерал: наведение на выбранную строку — на ступень
   сильнее её подсветки в любой теме. */
.jrow.is-selected:hover { background: var(--accent-200); }
.jrow:focus { outline: 2px solid var(--primary); outline-offset: -2px; }
/* Множественный выбор чекбоксами — лёгкая подсветка отмеченной строки.
   Не конфликтует с .is-selected (одиночное выделение синим): отмеченные
   строки видны мягким золотистым тоном, а активная одиночная — синим. */
.jrow--checked { background: rgba(250, 204, 21, 0.12) !important; }
.jrow--checked:hover { background: rgba(250, 204, 21, 0.20) !important; }
.col-select { width: 32px; text-align: center; padding: 0 4px; }
.col-select input[type="checkbox"] { cursor: pointer; }

.btn--icon {
  padding: 4px 8px;
  font-size: var(--fs-md);
  line-height: 1;
  background: transparent;
  border-color: transparent;
  color: var(--muted);
  text-decoration: none;
  /* .btn задаёт min-height: 36px; для иконки в модалках (× «Закрыть»)
     это слишком много — она должна помещаться в заголовок 32-36 px
     высотой. Но и меньше 28px нельзя: кликабельная зона должна
     оставаться попадаемой мышью/пальцем (доступность). */
  min-width: 28px;
  min-height: 28px;
}
.btn--icon:hover { text-decoration: none; }

/* Все «удалить» — пастельно-красные. Не яркий красный (как primary
   у destructive-кнопки), а мягкий розоватый с тёмно-красным текстом —
   оператор видит «опасно», но не пугается, и кнопка не доминирует
   над основным CTA на странице. Любое подтверждение — отдельным confirm-диалогом. */
/* Единый стиль для всех кнопок «Удалить» в интерфейсе — заполненная
   розово-красная (#e3256b) с белым текстом. По насыщенности это
   зеркальная CTA по отношению к синей .btn--primary. Так оператору
   проще «считывать» кнопку: один цвет — везде значит «удалить». */
/* Прежняя залитая красная кнопка убрана: по README §4.2 опасное
   действие — контурная кнопка, и залитой красной в системе не
   остаётся ни одной. Само правило .btn--danger живёт в блоке кнопок
   выше; здесь остался только ghost-вариант. */
/* Ghost-вариант — текстовая кнопка «Удалить» в шапке карточки записи
   книги. Тон тот же, но без подложки и рамки. */
.btn--danger.btn--ghost {
  background: transparent;
  border-color: transparent;
  color: var(--danger-fg);
}
.btn--danger.btn--ghost:hover {
  background: var(--err-bg);
  color: var(--danger-fg);
}
.btn--icon:hover { background: var(--accent-100); color: var(--accent-deep); }

/* Бейдж источника записи в журнале. */
.src-badge {
  display: inline-block;
  padding: 2px 7px;
  border-radius: 10px;
  font-size: var(--fs-sm);
  font-weight: 600;
  white-space: nowrap;
}
.src-badge--book {
  background: rgba(37, 99, 235, 0.12);
  color: var(--primary);
}
.src-badge--inventory {
  background: rgba(5, 150, 105, 0.12);
  color: var(--ok);
}
/* Bookmark+inventory: запись 1С-импорта с merge-парой Книга↔Регистрация.
   Градиент синий→зелёный, чтобы визуально выделялась как «обе ветки сразу». */
.src-badge--book-inv {
  background: linear-gradient(90deg, rgba(37, 99, 235, 0.18), rgba(5, 150, 105, 0.18));
  color: var(--accent-deep);
  border: 1px solid rgba(37, 99, 235, 0.25);
}
.jrow--inventory { background-color: rgba(5, 150, 105, 0.03); }
.jrow--inventory:nth-child(even) { background-color: rgba(5, 150, 105, 0.055); }

.journal-side__head { font-size: var(--fs-sm); color: var(--muted); margin-bottom: 4px; }
.journal-photos {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin-bottom: var(--sp-2);
}
.journal-photos a {
  width: 56px;
  height: 56px;
  overflow: hidden;
  border: 1px solid var(--line-2);
  border-radius: var(--r-chip);
  background: var(--surface-2);
  display: inline-block;
}
.journal-photos img { width: 100%; height: 100%; object-fit: cover; display: block; }
.journal-photo-missing {
  width: 56px; height: 56px;
  display: flex; align-items: center; justify-content: center;
  font-size: var(--fs-lg); color: var(--muted); background: var(--surface-2);
}
/* Снимок есть в базе, но файла нет — архив инвентаризации переносят
   отдельно и заливают частями. Место занимаем, но не кричим. */
.file-preview__missing {
  min-width: 260px; min-height: 160px;
  display: flex; align-items: center; justify-content: center;
  padding: var(--sp-6);
  color: var(--muted); font-size: var(--fs-base);
  /* Тело окна просмотра — --surface-2: плашка того же тона не читалась бы
     плашкой, поэтому белая с рамкой, как карточки. */
  background: var(--surface); border: 1px solid var(--line-2); border-radius: var(--r-card);
}
.journal-photo-doc {
  width: 100%; height: 100%;
  display: flex; align-items: center; justify-content: center;
  background: var(--surface-2);
  color: var(--muted);
  font-weight: var(--fw-bold); font-size: var(--fs-sm);
}
.journal-side__actions {
  display: flex; flex-direction: column; gap: 6px; margin-top: 12px;
}

/* ============================================================================
   Полоса вкладок открытых документов — fixed-подвал внизу окна.
   Видна только если открыт ≥1 документ (burial/plot/grave/inventory).
   body.has-doc-tabs выставляется в renderDocTabs, и страница даёт
   снизу запас высоты, чтобы её последние строки не закрывала полоса.
   ============================================================================ */
.doc-tabs {
  display: flex;
  gap: var(--sp-1);
  padding: var(--sp-1) var(--sp-4);
  background: var(--surface);
  border-top: 1px solid var(--line-2);
  overflow-x: auto;
  scrollbar-width: thin;
  /* Полоса вкладок — fixed-подвал, всегда видна внизу окна, поверх
     любого контента. Контент компенсирует высоту через
     body.has-doc-tabs { padding-bottom: 44px } (см. ниже), чтобы
     последние строки страницы не уходили под полосу. */
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  /* Выше карты, её Leaflet-контролов (~1000) и тостов, но ниже модалок
     (2000): иначе выпадающее ВВЕРХ меню группы (.doc-group__menu)
     проваливается под полигоны карты (их pane ~450 «протекает» в корневой
     stacking-контекст, т.к. контейнер Leaflet своего контекста не создаёт). */
  z-index: var(--z-doc-tabs);
  flex-shrink: 0;
  box-shadow: var(--shadow-menu);
}
/* display:flex перекрывает дефолтный display:none от атрибута hidden,
   поэтому явно возвращаем его, когда документов нет ни одного. */
.doc-tabs[hidden] { display: none; }

/* Класс на body выставляет JS (renderDocTabs) при наличии открытых
   документов. Контент компенсирует высоту полосы, чтобы её не
   закрывал нижний край. На странице карты — отдельно ограничиваем
   высоту .map-page (она height-bound).
   Высота полосы: 4px+4px (padding) + ~34px (вкладка с padding 6+6 +
   line-height для font-size 13px + border 1+1) ≈ 42–44px. Берём 56px
   с щедрым запасом — мало того что чуть просторнее визуально, ещё и
   браузеры по-разному считают line-height/font-metrics, и без запаса
   на «Управление ролями» с полной прокруткой последняя строка
   проваливалась под полосу.
   Дублируем отступ ещё и на .content — body { height: 100% } сам по
   себе с `padding-bottom` ведёт себя ненадёжно (в зависимости от
   overflow владельца), а .content всегда принимает паддинг. */
body.has-doc-tabs { padding-bottom: var(--h-doc-tabs); }
body.has-doc-tabs .content { padding-bottom: calc(var(--sp-6) + var(--h-doc-tabs)); }
body.has-doc-tabs .map-page { height: calc(100vh - var(--h-topbar) - var(--h-doc-tabs)); }
.doc-tab {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 10px 6px 12px;
  max-width: 240px;
  /* Голубой тон в той же гамме, что обычная кнопка («Добавить участок» и т.п.) —
     полоса вкладок хорошо видна на белом фоне страницы. */
  background: var(--btn-bg);
  border: 1px solid var(--border);
  border-top: none;
  border-radius: 0 0 var(--r-chip) var(--r-chip);
  color: var(--fg);
  text-decoration: none;
  font-size: var(--fs-base);
  white-space: nowrap;
}
.doc-tab:hover { background: var(--accent-100); color: var(--accent-deep); text-decoration: none; }
.doc-tab.is-active {
  background: var(--surface);
  color: var(--text);
  font-weight: var(--fw-medium);
  /* Подчёркиваем активную вкладку снизу (тень снизу-внутрь), потому
     что полоса теперь приклеена к нижнему краю окна. */
  box-shadow: 0 1px 0 var(--accent) inset;
  position: relative;
  z-index: 1;
}
.doc-tab__title {
  max-width: 190px;
  overflow: hidden;
  text-overflow: ellipsis;
}
.doc-tab__close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* Хитбокс ≥24px (доступность: раньше крестик был 18×18 и промахивался
     мышью). Визуально глиф прежний, а отрицательный вертикальный margin
     компенсирует рост бокса, чтобы высота вкладки не изменилась. */
  min-width: 24px;
  min-height: 24px;
  margin: -3px 0;
  background: transparent;
  border: none;
  border-radius: var(--r-chip);
  color: var(--muted);
  /* font-family задаётся явно: у <button> браузер иначе ставит свой
     системный шрифт (в замерах — Arial), и крестик выпадал из
     гарнитуры интерфейса. */
  font-family: var(--font-ui);
  font-size: var(--fs-lg);
  line-height: 1;
  cursor: pointer;
  padding: 0 var(--sp-2);
}
.doc-tab__close:hover { background: var(--accent-100); color: var(--accent-deep); }
/* «Карта» (home) — без кнопок (неудаляемая). Пользовательские вкладки
   могут быть закреплены (pin): у них остаётся ✕, но «Закрыть все» их не
   трогает. Признак «закреплена» — яркая иконка 📌 (см. .doc-tab__pin). */
.doc-tab.doc-tab--pinned { opacity: 0.97; }

/* Кнопка закрепления вкладки (📌): тусклая/обесцвеченная, когда не
   закреплена; яркая — когда закреплена. */
.doc-tab__pin {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  padding: 0;
  background: transparent;
  border: none;
  border-radius: var(--r-chip);
  font-size: var(--fs-sm);
  line-height: 1;
  cursor: pointer;
  opacity: 0.3;
  filter: grayscale(1);
}
.doc-tab__pin:hover { opacity: 0.7; }
.doc-tab__pin.is-pinned { opacity: 1; filter: none; }

/* ---- Группы вкладок (Захоронения / Участки / Сервис / Администрирование) ----
   Кнопка группы со счётчиком + выпадающее ВВЕРХ меню со списком открытых
   вкладок (полоса приклеена к низу окна, поэтому меню раскрывается вверх). */
.doc-group { position: relative; display: inline-flex; flex-shrink: 0; }
.doc-group__btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 10px 6px 12px;
  background: var(--btn-bg);
  border: 1px solid var(--border);
  border-top: none;
  border-radius: 0 0 var(--r-chip) var(--r-chip);
  color: var(--fg);
  font-family: inherit;
  font-size: var(--fs-base);
  white-space: nowrap;
  cursor: pointer;
}
.doc-group__btn:hover { background: var(--btn-bg-hover); }
.doc-group.is-active .doc-group__btn {
  background: var(--surface);
  font-weight: 500;
  box-shadow: 0 1px 0 var(--primary) inset;
}
.doc-group__count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 24px;
  height: 24px;
  padding: 0 var(--sp-1);
  border-radius: var(--r-pill);
  background: var(--accent-strong);
  color: var(--on-accent);
  font-size: var(--fs-sm);
  line-height: 1;
}
.doc-group__caret { font-size: var(--fs-sm); color: var(--muted); }
.doc-group__menu {
  /* position: fixed (а не absolute): у полосы .doc-tabs overflow-x: auto,
     который форсит overflow-y: auto и ОБРЕЗАЕТ выпадающее вверх меню. fixed
     не обрезается overflow-предком и позиционируется от вьюпорта — координаты
     (left/bottom от кнопки группы) ставит JS при открытии. */
  position: fixed;
  min-width: 230px;
  max-width: 380px;
  max-height: 60vh;
  overflow-y: auto;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-card);
  box-shadow: 0 4px 18px rgba(0,0,0,0.20);
  padding: 4px;
  display: none;
  z-index: 60;
}
.doc-group.is-open .doc-group__menu { display: block; }
.doc-group__item {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: var(--h-icon-btn);   /* строка меню — 36, как во всех меню */
  padding: 0 var(--sp-3);
  border-radius: var(--r-chip);
  color: var(--fg);
  text-decoration: none;
  font-size: var(--fs-base);
}
.doc-group__item:hover { background: var(--btn-bg-hover); text-decoration: none; }
.doc-group__item.is-active { background: var(--btn-bg); font-weight: 500; }
.doc-group__item-title { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* «Закрыть все» — приклеена к правому краю полосы (sticky), видна даже
   при горизонтальной прокрутке множества вкладок. */
.doc-tabs__close-all {
  position: sticky;
  right: var(--sp-1);
  margin-left: auto;
  align-self: center;
  flex-shrink: 0;
  padding: var(--sp-1) var(--sp-3);
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--r-chip);
  color: var(--muted);
  /* У <button> без явной гарнитуры браузер ставит системный шрифт —
     в замерах он выходил как Arial посреди Golos Text. */
  font-family: var(--font-ui);
  font-size: var(--fs-sm);
  white-space: nowrap;
  cursor: pointer;
  box-shadow: var(--shadow-menu);
}
.doc-tabs__close-all:hover { background: var(--accent-100); color: var(--accent-deep); }

/* ============================================================================
   Карточка записи книги захоронений: подшапка с ключевыми мета-полями,
   inline-форма редактирования, индикатор сохранения.
   ============================================================================ */
.page-subhead {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2) var(--sp-5);
  padding: var(--sp-3) var(--sp-4);
  margin-bottom: var(--sp-4);
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--r-card);
  font-size: var(--fs-base);
  color: var(--text);
}
.page-subhead b { color: var(--muted); font-weight: var(--fw-medium); margin-right: var(--sp-1); }

.burial-form { display: block; }
.burial-form .fields-grid,
.burial-form .form-grid { margin-top: 4px; }

/* Общая форма редактирования — используется и для карточки записи книги,
   и для карточки участка. Одинаковая верстка полей, секций, save-status. */
.doc-form { display: block; }
.doc-form .fields-grid,
.doc-form .form-grid { margin-top: 4px; }

/* Кнопка-иконка для мелких операций (настройки колонок, «открыть во вкладке») —
   без лишнего фона; при hover — лёгкая подсветка. */
.btn-settings-columns { white-space: nowrap; }

/* Блок «Связь с книгой» на карточке /grave/:id */
.link-panel {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-card);
  padding: 12px 16px;
  margin: 16px 0;
}
.link-candidate {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border-bottom: 1px solid var(--border);
  cursor: pointer;
  margin: 0;
}
.link-candidate:last-child { border-bottom: none; }
.link-candidate:hover { background: var(--surface-alt); }
.link-candidate input[type="radio"] { flex-shrink: 0; }

.save-status {
  font-size: var(--fs-sm);
  color: var(--muted);
  min-width: 90px;
  text-align: right;
  /* Плавная смена цвета вместо резкого «мигания» при автосейве
     (muted → синий «Сохраняем…» → зелёный «Сохранено»). */
  transition: color .2s ease;
}
.save-status.is-saving { color: var(--accent-link); }
.save-status.is-saved  { color: var(--ok); }
.save-status.is-dirty  { color: var(--muted); }
.save-status.is-error  { color: var(--danger); }

/* Матрица ролей × ресурсов × действий (страница /roles, суперадмин). */
.roles-matrix td, .roles-matrix th { vertical-align: middle; text-align: center; }
.roles-matrix td:first-child, .roles-matrix th[rowspan] { text-align: left; }
.roles-matrix .role-col { border-left: 1px solid var(--border); }
.roles-matrix .cell { display: inline-flex; flex-direction: column; align-items: center; gap: 2px; }
.roles-matrix .cell input { margin: 0; }
.roles-matrix .role-col__head {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 4px 0;
}
.roles-matrix .role-col__del-btn {
  margin-top: 4px;
  font-size: var(--fs-sm);
  padding: 3px 8px;
  align-self: center;
}
/* Раньше здесь была глобальная заливка .btn.btn--danger ярко-красным,
   которая перебивала мягкий розовый стиль выше. Удалена по запросу
   оператора — кнопка «Удалить» теперь повсюду в спокойной розовой
   гамме, а не в насыщенно-красной. */

/* Табличная форма координат для форм квартала и участка (ТЗ,
   п. 3.2.1 / 3.2.2). Две колонки ввода lat/lng, кнопка
   «+ Добавить точку», красный крестик в конце строки. */
/* Свёрнутый блок координат полигона (details/summary): шапка с числом точек,
   внутри — таблица построчно (одна точка = строка) + «Добавить точку». */
.coord-details {
  margin-top: var(--sp-3);
  border: 1px solid var(--line-2);
  border-radius: var(--r-card);
  background: var(--surface);
}
.coord-summary {
  cursor: pointer;
  padding: 8px 12px;
  font-size: var(--fs-base);
  font-weight: 500;
  color: var(--fg);
  user-select: none;
  list-style: none;
  display: flex;
  align-items: center;
  gap: 6px;
}
.coord-summary::-webkit-details-marker { display: none; }
.coord-summary::before {
  content: "▸";
  color: var(--muted);
  transition: transform .12s ease;
}
.coord-details[open] .coord-summary::before { transform: rotate(90deg); }
.coord-summary__count { font-weight: 400; }
.coord-details > .coord-scroll { overflow-x: auto; margin: 4px 12px 0; }
.coord-details > p { margin-left: 12px; margin-right: 12px; }
.coord-details > .coord-add { margin: 8px 12px 12px; }
.coord-table {
  width: auto;
  border-collapse: collapse;
}
.coord-table th,
.coord-table td {
  border: 1px solid var(--line-2);
  padding: 4px 6px;
  vertical-align: middle;
}
.coord-table th {
  background: var(--surface-alt);
  font-weight: 500;
  font-size: var(--fs-base);
  text-align: left;
  color: var(--muted);
}
/* Таблица-список: строка = точка (№ / Широта / Долгота / ✕). */
.coord-table--list { width: auto; }
.coord-table--list .coord-th-n { width: 40px; text-align: center; }
.coord-table--list .coord-th-del { width: 30px; }
.coord-table--list .coord-n { text-align: center; font-variant-numeric: tabular-nums; color: var(--muted); }
.coord-table--list .coord-del { display: block; margin: 0 auto; }
.coord-table input[type="number"] {
  /* Ширина под полную координату с запасом: напр. «56,29072743778»
     (14 символов) или отрицательная долгота. 16ch — чтобы все цифры
     помещались без обрезки. Спиннеры скрыты (см. ниже) — не нужны для
     координат и съедали бы ширину. */
  width: 16ch;
  box-sizing: content-box;
  border: none;
  padding: 4px 6px;
  font-variant-numeric: tabular-nums;
  font-size: var(--fs-md);
  background: transparent;
}
/* Прячем стрелки-спиннеры у числовых полей координат. */
.coord-table input[type="number"]::-webkit-outer-spin-button,
.coord-table input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.coord-table input[type="number"] { -moz-appearance: textfield; appearance: textfield; }
.coord-table input[type="number"]:focus {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}
.coord-table .coord-del {
  border: none;
  background: transparent;
  cursor: pointer;
  padding: 0 var(--sp-2);
  font-size: var(--fs-sm);
  line-height: 1;
  color: var(--danger-fg);
  font-family: var(--font-ui);
  min-height: var(--h-icon-btn);
}
.coord-table .coord-del:hover { color: var(--danger-fg); }

/* ============================================================================
 * Вкладка «Фото / Сканы» в карточке записи книги: галерея + сканы (PDF/img)
 * ============================================================================ */
.file-upload {
  display: flex; align-items: center; gap: 10px;
  margin: 4px 0 8px;
}
.file-upload__hint { font-size: var(--fs-base); }
.file-upload .btn { cursor: pointer; }

/*
  .file-picker — стилизованная альтернатива нативному <input type="file">
  внутри форм (импорт JSON, XLSX и т.п.). Сам input спрятан через `hidden`,
  кликается через <label class="btn">; имя выбранного файла рисуется
  отдельным span'ом справа. До v0.69.3 в этих местах вылезала нативная
  «древневиндовая» кнопка «Browse…», которая ломала визуал всей формы.
*/
.file-picker {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 4px;
  flex-wrap: wrap;
}
.file-picker .btn { cursor: pointer; }
.file-picker__name {
  font-size: var(--fs-base);
  word-break: break-all;
}

/* ── «Обратиться в техподдержку» (с v0.74) ────────────────────────── */
.support-toolbar {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin: 8px 0 10px;
}
.support-toolbar__label { font-size: var(--fs-base); color: var(--muted); font-weight: 500; }
.support-tool.is-active {
  background: var(--primary);
  border-color: var(--primary);
  color: var(--on-accent);
}
.support-toolbar__color {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: var(--fs-base);
  color: var(--muted);
  cursor: pointer;
}
.support-toolbar__color input[type="color"] {
  width: 28px; height: 24px; padding: 0; border: 1px solid var(--border);
  border-radius: var(--r-chip); cursor: pointer; background: none;
}
.support-modal {
  max-width: min(1180px, 96vw);
  width: 100%;
}
.support-canvas-wrap {
  border: 1px solid var(--border);
  border-radius: var(--r-chip);
  overflow: hidden;
  background: var(--surface);
  box-shadow: var(--shadow, 0 1px 3px rgba(0,0,0,.1));
  width: 100%;
  /* max-width задаётся inline = натуральная ширина скрина, чтобы не
     растягивать картинку выше 100% (иначе размылась бы). */
}
/* Канвас масштабируется по ширине контейнера; внутреннее разрешение
   (атрибуты width/height) сохраняется, поэтому отрисовка чёткая, а
   координаты мыши переводятся через getBoundingClientRect. */
#support-canvas {
  display: block;
  cursor: crosshair;
  width: 100%;
  height: auto;
  touch-action: none;
}

/* Сетка карточек файлов на вкладке «Фото / Сканы» (раунд 3, 24.09.2026 —
   вид утверждён владельцем по снимку-макету). */
.file-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(176px, 1fr));
  gap: var(--sp-3);
  margin-top: var(--sp-3);
}

/* Карточка файла: миниатюра на всю ширину сверху, под ней имя и размер
   одной ссылкой (открывает окно просмотра), справа «⋯» с действиями.
   Раньше поля были 6 px, а под каждым файлом стояла красная кнопка
   «Удалить» во всю ширину — десяток фото давал десяток красных пятен.
   overflow не обрезаем: меню «⋯» выходит за нижний край карточки. */
.file-card {
  position: relative;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--line-2);
  border-radius: var(--r-card);
  background: var(--surface); /* токен вместо #fff — тёмная тема */
}
.file-card__thumb {
  display: block;
  width: 100%;
  height: 124px;
  border-radius: var(--r-card) var(--r-card) 0 0;
  overflow: hidden;
  background: var(--surface-2);
}
.file-card__thumb img {
  width: 100%; height: 100%; object-fit: cover; display: block;
}
/* Снимок числится за записью, а файла ещё нет: архив инвентаризации
   переносят отдельно от базы и заливают частями. Оператору важно понять,
   что это временно, поэтому пишем словами, а не значком. */
.file-card__missing {
  width: 100%; height: 100%;
  display: flex; align-items: center; justify-content: center;
  padding: var(--sp-3); box-sizing: border-box;
  text-align: center; line-height: 1.25;
  font-size: var(--fs-sm); color: var(--muted);
  background: var(--surface-alt);
}
/* Не изображение (PDF и пр.) — плашка с типом файла по центру. */
.file-card__thumb--doc {
  display: flex; align-items: center; justify-content: center;
  color: var(--accent-deep);
  font-weight: var(--fw-bold);
}
.file-card__mime {
  padding: var(--sp-1) var(--sp-3);
  border: 1px solid var(--line-2);
  border-radius: var(--r-chip);
  background: var(--surface);
  font-size: var(--fs-sm);
}
.file-card__meta {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  column-gap: var(--sp-1);
  padding: var(--sp-1) var(--sp-1) var(--sp-2) var(--sp-3);
}
/* Имя и размер — одна ссылка в две строки: зона нажатия 48 px вместо
   строки имени высотой 23. */
.file-card__link {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 2px;
  min-width: 0;
  min-height: 48px;
  color: var(--text);
  text-decoration: none;
}
.file-card__link:hover { text-decoration: none; }
.file-card__link:hover .file-card__name { color: var(--accent-link-hover); text-decoration: underline; }
.file-card__name {
  font-size: var(--fs-base);
  font-weight: var(--fw-medium);
  line-height: 1.3;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.file-card__size { font-size: var(--fs-sm); color: var(--muted); line-height: 1.3; }
.btn--small {
  padding: 2px 8px;
  font-size: var(--fs-sm);
  /* .btn задаёт min-height: 36px для нормальных кнопок; .btn--small
     намеренно компактнее (используется в плотных списках, в
     таблицах), переопределяем. Значение — из общей шкалы --btn-h-*;
     класс — исторический синоним нового .btn--xs. */
  min-height: var(--btn-h-xs);
}

/* ============================================================================
 * Модалка «Колонки таблицы» — список с drag-n-drop для смены порядка.
 * Каждая строка: ≡ (handle для перетаскивания) + чекбокс (видимость) + label.
 * При перетаскивании строка получает .col-row--dragging (полупрозрачная),
 * строка под курсором — .col-row--drop-target (синяя полоска сверху или снизу
 * в зависимости от data-drop-before).
 * ============================================================================ */
.col-list {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.col-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 8px;
  border: 1px solid transparent;
  border-radius: var(--r-chip);
  background: var(--surface);
  cursor: default;
  user-select: none;
}
.col-row:hover { background: var(--bg-soft); }
.col-row__handle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  font-size: var(--fs-lg);
  color: var(--muted);
  cursor: grab;
}
.col-row[draggable="true"]:active .col-row__handle { cursor: grabbing; }
.col-row__label { flex: 1; }
/* Кнопки ↑/↓ — альтернатива drag&drop (тачпад/клавиатура). Появляются
   заметнее при наведении/фокусе на строку, но кликабельны всегда. */
.col-row__move { display: inline-flex; gap: 2px; flex-shrink: 0; }
.col-row__move .btn--icon { min-width: 26px; min-height: 26px; padding: 2px 6px; line-height: 1; }
/* «? Как это работает» — компактная ссылка на статью справки в шапке сложных
   страниц (helpLink в app.js). Мелкая, приглушённая, не спорит с h1. */
.help-inline-link {
  font-size: var(--fs-sm);
  font-weight: 400;
  color: var(--accent-link);   /* цвет ссылок (README): --accent давал 3,63:1 */
  text-decoration: none;
  vertical-align: middle;
  margin-left: 8px;
  white-space: nowrap;
}
.help-inline-link:hover { text-decoration: underline; }
.col-row--dragging {
  opacity: 0.45;
  background: var(--primary-soft);
  border-color: var(--primary);
}
/* Полоска-индикатор перед/после строки-цели, в зависимости от того, ближе
   к верхней или нижней половине строки находится курсор (data-drop-before). */
.col-row--drop-target[data-drop-before="1"] { box-shadow: 0 -2px 0 var(--primary); }
.col-row--drop-target[data-drop-before="0"] { box-shadow: 0 2px 0 var(--primary); }

/* ============================================================================
 * Окно просмотра файла (фото; PDF открывается отдельным окном браузера).
 * Раунд 3 (23.09.2026): оформлено как все диалоги программы — та же
 * подложка, радиус 14, тень --shadow-modal; в шапке имя файла 19/600,
 * размер снимка 13 приглушённым и «×» (.btn--icon); в подвале —
 * инструменты: листание и масштаб таблетками (как режимы правки на
 * карте), «Открыть в новой вкладке» служебной кнопкой, «Сохранить как…»
 * обычной. Раньше шапка была набрана кнопками без рамок, а «⤢» и «⤓» —
 * глифами, которых нет в Golos Text: окно выглядело чужим.
 * Окно — во всё окно браузера за вычетом полей 24 px: место отдаётся
 * снимку. Закрытие: клик по подложке, «×», Esc.
 * ============================================================================ */
.file-preview-backdrop {
  position: fixed; inset: 0;
  background: rgba(17, 24, 39, 0.5);   /* как у .modal-backdrop */
  display: flex; align-items: center; justify-content: center;
  z-index: var(--z-lightbox);
  padding: var(--sp-6);
  box-sizing: border-box;
}
.file-preview {
  background: var(--surface);
  border-radius: var(--r-card);
  box-shadow: var(--shadow-modal);
  display: flex; flex-direction: column;
  width: 100%;
  max-width: 1600px;
  height: 100%;
  overflow: hidden;
}
.file-preview:focus { outline: none; }
.file-preview__head {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4) var(--sp-3) var(--sp-5);
  border-bottom: 1px solid var(--line);
  background: var(--surface);
  flex-shrink: 0;
}
.file-preview__title {
  min-width: 0;
  flex: 0 1 auto;
  margin: 0;
  font-size: var(--fs-xl);
  font-weight: var(--fw-bold);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.file-preview__dims {
  flex: none;
  font-size: var(--fs-sm);
  color: var(--muted);
  white-space: nowrap;
}
.file-preview__close { flex: none; margin-left: auto; }
.file-preview__body {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  display: flex; align-items: center; justify-content: center;
  background: var(--surface-2);
  overflow: hidden; /* zoom/pan живёт внутри viewport */
}
.file-preview__foot {
  display: flex; align-items: center; flex-wrap: wrap;
  gap: var(--sp-2) var(--sp-3);
  padding: var(--sp-2) var(--sp-4);
  border-top: 1px solid var(--line);
  background: var(--surface);
  flex-shrink: 0;
}
.file-preview__spacer { flex: 1 1 auto; }

/* Группа инструментов — таблетка, как режимы правки в тулбаре карты
   (общие правила сегментов — у .map-tools__group.seg). Подпись внутри
   («1 из 2», «63 %») — текстом 15, цифры моноширинные, чтобы соседние
   кнопки не ездили при смене числа. */
.fp-group { display: inline-flex; align-items: center; flex: none; }
.fp-group[hidden] { display: none; }
.fp-group__label {
  padding: 0 var(--sp-2);
  font-size: var(--fs-base);
  color: var(--text-2);
  white-space: nowrap;
}
.fp-group__label--pct { min-width: 56px; text-align: center; }
.fp-group__sep { flex: none; width: 1px; height: 20px; margin: 0 var(--sp-1); background: var(--line-2); }
.file-preview .fp-btn--icon { width: var(--h-control); padding: 0; }

/* Viewport картинки — клиппинг + позиционирование .file-preview__img,
   у которой transform-origin (0,0) полностью описывает положение через
   translate+scale (см. _wireImageZoomPan). Занимает всё тело окна;
   flex — чтобы заглушка «Фото временно недоступно» стояла по центру. */
.file-preview__viewport {
  position: absolute;
  inset: 0;
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
  cursor: grab;
  user-select: none;
}
.file-preview__viewport:active { cursor: grabbing; }
.file-preview__img {
  position: absolute;
  top: 0; left: 0;
  transform-origin: 0 0;
  background: var(--surface);
  /* JS выставит transform; пока не загрузилось — спрячем по умолчанию,
     fit() в onload вычислит правильное положение. */
  will-change: transform;
  user-select: none;
  -webkit-user-drag: none;
  /* Сглаживание при дробных масштабах. */
  image-rendering: auto;
}
.file-preview__frame {
  width: 100%;
  height: 100%;
  border: 0;
  background: var(--surface);
}
/* Файл без предпросмотра (не фото и не PDF): крупная плашка типа и
   пояснение — приглушённым текстом, как пустые состояния в программе. */
.file-preview__nopreview {
  padding: var(--sp-8);
  display: flex; flex-direction: column; align-items: center; gap: var(--sp-2);
  color: var(--muted);
}
.file-preview__nopreview-icon {
  font-size: var(--fs-h2);
  font-weight: var(--fw-bold);
  background: var(--surface);
  color: var(--text-2);
  padding: var(--sp-4) var(--sp-6);
  border: 1px solid var(--line-2);
  border-radius: var(--r-card);
}

/* На близком зуме (≥17, уровень улицы) плашка названия кладбища
   только мешает: оператор уже работает внутри кладбища с конкретными
   участками, а плашка перекрывает их собой. Класс выставляется на
   контейнере карты по событию zoomend. То же — для outlier-плашек
   кладбищ (если у кладбища есть «оторвавшиеся» кластера плотов и для
   них нарисована дополнительная плашка).

   :not(.osiris-cems-only) — прятать плашку можно ТОЛЬКО там, где за ней
   что-то есть. В режиме «Все кладбища» карта состоит из одних плашек, и
   без этой оговорки один щелчок колеса от обзорного вида оставлял пустой
   экран (замечание пользователя 07.09.2026). */
.osiris-deep-zoom:not(.osiris-cems-only) .cem-label-marker {
  display: none;
}

/* Маркеры инфраструктуры (вода, парковка, туалет — POI-иконки) ведут себя
   зеркально плашке названия: на обзорном зуме (<17) их нет — там важнее
   название кладбища, а россыпь иконок только мешает; при z≥17, когда плашка
   названия уже исчезла, маркеры появляются. По умолчанию pane скрыт, класс
   osiris-deep-zoom (z≥17, выставляется в updateLabelsVisibility) его
   показывает. Полигональные зоны инфраструктуры — в другом pane, не затронуты. */
.leaflet-osirisInfraMarker-pane {
  display: none;
}
.osiris-deep-zoom .leaflet-osirisInfraMarker-pane {
  display: block;
}
/* В режиме правки структуры POI-маркеры видны на любом зуме — иначе на
   обзорном зуме они скрыты и их нельзя перетащить. */
.osiris-editing .leaflet-osirisInfraMarker-pane {
  display: block;
}

/* [feature:import1c] */
/* Индетерминантный прогресс-бар (для стадий, где сервер не сообщает
   реальный процент: ctool1cd → JSON → импорт). Используется в
   /import-1cd (единственный потребитель — paintImport1CDStatus), поэтому
   вырезается вместе со страницей импорта 1С. Идея — классический striped-animated паттерн как у
   Bootstrap: прозрачная диагональная полосатая текстура поверх
   сплошной заливки, текстура едет влево с постоянной скоростью.
   Так пользователь видит «работа идёт», но индикатор не прыгает
   взад-вперёд (что воспринимается как глюк).

   `!important` нужен, потому что у самого DIV'а inline-shorthand
   `background:#0369a1` обнуляет background-image — без important наша
   текстура «пропадает», бар становится сплошной заливкой. */
.progress-indeterminate {
  background-image: linear-gradient(
    45deg,
    rgba(255, 255, 255, 0.25) 25%,
    transparent 25%,
    transparent 50%,
    rgba(255, 255, 255, 0.25) 50%,
    rgba(255, 255, 255, 0.25) 75%,
    transparent 75%,
    transparent
  ) !important;
  background-size: 24px 24px !important;
  animation: progress-stripe-flow 1s linear infinite;
}
@keyframes progress-stripe-flow {
  from { background-position: 24px 0; }
  to   { background-position: 0 0; }
}
/* [/feature:import1c] */

/* =====================================================================
   v0.54 — UI spacing patches: предотвращаем слипание элементов в
   inline-style flex-контейнерах, добавляем breathing room в шапках
   карточек, формах и модалках. Не ломает существующих стилей —
   только декорирует там, где padding/gap были забыты.
   ===================================================================== */

/* Ряд кнопок в шапке карточки документа (Реестр захоронений / запись /
   участок). Раньше «← К списку», «📄 Свидетельство», «Удалить» сидели
   слишком близко. */
.page__actions {
  display: flex;
  gap: 10px;
  align-items: center;
  flex-wrap: wrap;
  /* Центрируем блок кнопок по вертикали относительно h1. .page__header
     выравнивает детей по baseline; когда в .page__actions появляется/
     исчезает индикатор «✓ Сохранено» (автосейв карточки захоронения),
     baseline блока смещается и кнопки «прыгают» вверх-вниз. align-self:
     center убирает зависимость от baseline — блок больше не дёргается
     (жалоба оператора 2026-05-27). */
  align-self: center;
}

/* Кнопки в строке внутри плашек (Резервное копирование, Сервер
   обновлений и т.п.) — между button и подсказкой `<span class="muted">`
   должен быть явный отступ. */
.placeholder .row > * + * { margin-left: 0; }
.placeholder .row { gap: 10px !important; }

/* Контейнеры fieldset с двумя date-input'ами (рождения / смерти /
   захоронения) на странице отчёта. Подпись «с» / «по» прилипала
   к input'у — теперь между span и input есть gap. */
/* Поле выбора цвета — та же высота и форма, что у остальных контролов:
   браузерное поле 27 px выпадало из ряда и не дотягивало до
   кликабельной зоны. */
input[type="color"] {
  width: 100%;
  min-height: var(--h-control);
  padding: var(--sp-1);
  border: 1px solid var(--line-2);
  border-radius: var(--r-field);
  background: var(--surface-2);
  cursor: pointer;
}
/* Группа полей настраиваемой формы — те же правила, что у fieldset. */
.cf-group { margin: 0 0 var(--sp-4); }

/* Группировка полей — капитель-надзаголовок и воздух, а не рамка с
   врезанной подписью (README §4.3). Рамка вокруг группы полей внутри
   карточки давала рамку в рамке, а врезанная подпись в разрыве линии
   читалась как отдельный заголовок раздела. Правило общее: так
   выглядят все двадцать с лишним fieldset'ов в программе. */
fieldset {
  border: none;
  margin: 0 0 var(--sp-5);
  padding: 0;
  min-width: 0;   /* иначе fieldset не сжимается в grid и распирает колонку */
}
fieldset > legend {
  padding: 0;
  margin-top: var(--sp-5);
  margin-bottom: var(--sp-2);
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
  color: var(--muted);
}
fieldset.field > legend { margin-bottom: var(--sp-2); }
fieldset.field > div > label > span { margin-right: var(--sp-2); }

/* Список чекбоксов колонок отчёта — каждое значение в своей строке,
   клик попадает на подпись целиком. Метрики берутся из .cols-item
   (высота 44), здесь остаётся только подсветка при наведении. */
#rep-cols label,
#rep-cols .checkbox-row {
  padding: 0 var(--sp-2);
  border-radius: var(--r-chip);
  transition: background 0.1s ease;
}
#rep-cols label:hover { background: var(--accent-100); }

/* Фильтры журнала захоронений: радиокнопки источника записи
   (Все / Только книга / Только инвентаризация) — должны быть в
   столбик с зазором, а не слипаться вертикально. */
.journal-filters__group { display: flex; flex-direction: column; gap: 6px; }
.journal-filters__group .field { margin-bottom: 0 !important; }
.journal-filters__group .field.row { padding: 2px 0; }

/* Справка: в TOC увеличиваем padding и зазор между пунктами; в
   article — больше воздуха перед h2 и h3. */
.help-page__toc a { padding: var(--sp-2) var(--sp-3) !important; line-height: 1.35 !important; }
.help-page__toc a + a { margin-top: 2px; }
/* --- Справка (README §5.2 таблицы «экраны без прототипа»): две
   колонки — оглавление 280 px и статья, заголовки статьи серифом.
   Прежде весь этот блок жил в <style> прямо в app.js и был написан
   литералами (#f9fafb, #1f2937, 24px, 18px…): пятая часть всех
   нарушений замера приходилась на один этот экран. */
.help-page {
  display: flex;
  gap: var(--sp-5);
  align-items: flex-start;
  width: 100%;
  max-width: 1800px;
  margin: 0 auto;
  padding: var(--sp-4) var(--sp-5);
  box-sizing: border-box;
}
.help-page__nav {
  flex: 0 0 280px;
  position: sticky;
  top: var(--sp-4);
  max-height: calc(100vh - 80px);
  overflow: auto;
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--r-card);
  padding: var(--sp-3);
}
.help-page__search input {
  width: 100%;
  min-height: var(--h-control);
  padding: 0 var(--sp-3);
  font-size: var(--fs-base);
  font-family: inherit;
  border: 1px solid var(--line-2);
  border-radius: var(--r-field);
  background: var(--surface-2);
  color: var(--text);
}
.help-page__toc { margin-top: var(--sp-3); font-size: var(--fs-base); }
.help-page__toc a {
  display: block;
  min-height: var(--h-control);
  padding: var(--sp-2) var(--sp-3);
  color: var(--text-2);
  text-decoration: none;
  border-radius: var(--r-chip);
  line-height: 1.35;
}
.help-page__toc a:hover { background: var(--accent-100); color: var(--accent-deep); }
.help-page__toc a.is-active {
  background: var(--accent-100);
  color: var(--accent-deep);
  font-weight: var(--fw-medium);
}
.help-page__toc .toc-snippet {
  display: block;
  font-size: var(--fs-sm);
  color: var(--muted);
  margin-top: 2px;
  line-height: 1.35;
}
.help-page__toc-cat {
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
  color: var(--muted);
  padding: var(--sp-3) var(--sp-3) var(--sp-1);
  margin-top: var(--sp-1);
  border-top: 1px solid var(--line);
}
.help-page__article {
  flex: 1 1 auto;
  min-width: 0;
  max-width: 900px;   /* строка длиннее 90 знаков читается тяжело */
  padding: 0 var(--sp-6) var(--sp-8);
  line-height: 1.55;
  font-size: var(--fs-base);
  color: var(--text);
}
.help-page__article h1 {
  font-family: var(--font-title);
  font-weight: var(--fw-normal);
  font-size: var(--fs-h2);
  margin: 0 0 var(--sp-4);
  line-height: 1.2;
}
.help-page__article h2 {
  font-family: var(--font-title);
  font-weight: var(--fw-normal);
  font-size: var(--fs-xl);
  margin: var(--sp-6) 0 var(--sp-2);
  padding-bottom: var(--sp-2);
  border-bottom: 1px solid var(--line);
}
.help-page__article h3 { font-size: var(--fs-lg); margin: var(--sp-5) 0 var(--sp-2); color: var(--text); }
.help-page__article p { margin: 0 0 var(--sp-3); }
.help-page__article ul, .help-page__article ol { margin: 0 0 var(--sp-3); padding-left: var(--sp-6); }
.help-page__article li { margin: 0 0 var(--sp-1); }
.help-page__article code {
  background: var(--surface-2);
  padding: 1px 6px;
  border-radius: var(--r-chip);
  font-size: var(--fs-sm);
  font-family: var(--font-mono);
}
.help-page__article pre {
  background: var(--surface-2);
  color: var(--text);
  border: 1px solid var(--line-2);
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--r-card);
  overflow: auto;
  margin: 0 0 var(--sp-4);
}
.help-page__article pre code { background: transparent; color: inherit; padding: 0; }
.help-page__article a { color: var(--accent-link); text-decoration: none; }
.help-page__article a:hover { text-decoration: underline; }
.help-page__article table { border-collapse: collapse; margin: 0 0 var(--sp-4); width: auto; }
.help-page__article th, .help-page__article td {
  border: 1px solid var(--line-2);
  padding: var(--sp-2) var(--sp-3);
  text-align: left;
  font-size: var(--fs-base);
}
.help-page__article th { background: var(--accent-100); color: var(--accent-deep); font-weight: var(--fw-bold); }
.help-page__empty { padding: var(--sp-6); color: var(--muted); font-size: var(--fs-base); }
@media (max-width: 900px) {
  .help-page { flex-direction: column; }
  .help-page__nav { position: static; flex: 0 0 auto; max-height: none; width: 100%; }
  .help-page__article { padding: 0 0 var(--sp-8); }
}

/* Интервалы в статье: раньше здесь стояли !important против прежнего
   блока <style> в app.js — блока больше нет, и они не нужны. */
.help-page__article ul li,
.help-page__article ol li { margin-bottom: var(--sp-2); }
.help-page__article h2 + ul,
.help-page__article h2 + ol,
.help-page__article h3 + ul,
.help-page__article h3 + ol { margin-top: var(--sp-2); }

/* Модалки: первый элемент в body не должен прилипать к границе
   заголовка. */
.modal__body > *:first-child { margin-top: 0; }
.modal__body .field:last-child { margin-bottom: 0; }
.modal__body fieldset + fieldset,
.modal__body section + section { margin-top: 14px; }

/* Доплата по плашкам страниц-разделов админки: между двумя section
   подряд (без явного margin-top на второй) добавляем зазор. */
.placeholder + .placeholder { margin-top: 18px; }
section.placeholder h2:first-child { margin-top: 0; }
section.placeholder p:first-of-type { margin-top: 0; }

/* Прежде вся форма выгрузки ужималась до 28 px в высоту, чтобы влезть
   на экран без прокрутки. Кликабельная зона важнее: поля здесь такие же
   44 px, как везде, а на экран форма помещается за счёт двух колонок и
   закреплённого подвала. Остаётся только компактный вертикальный ритм. */
.report-form .field { margin-bottom: var(--sp-3); }
.report-form fieldset.field legend { font-size: var(--fs-sm); }
.report-form h3 { margin: 0 0 4px !important; }
.report-form #rep-cols { gap: 2px !important; font-size: var(--fs-base) !important; }
.report-form #rep-cols label { padding: 1px 0; }

/* ---- Настраиваемая форма (Сервис → Настраиваемая форма, v0.85) ---- */
/* Поля в три ровные колонки (без «лесенок»), компактные межстрочные отступы. */
.cf-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 6px 16px;
  align-items: start;
}
.cf-grid .field { margin: 0; }
/* Многострочные поля (примечания/адрес) — на всю ширину строки. */
.cf-grid .cf-wide { grid-column: 1 / -1; }
@media (max-width: 1000px) { .cf-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 640px)  { .cf-grid { grid-template-columns: 1fr; } }

/* Панели «Настраиваемой формы»: кнопки, селекты и поля — одной высотой,
   в одну линию (за основу высота кнопки). */
.cf-toolbar, .cf-modebar { align-items: center; }
.cf-toolbar .btn, .cf-modebar .btn,
.cf-toolbar label.btn, .cf-modebar label.btn,
.cf-toolbar .cf-ctl, .cf-modebar .cf-ctl {
  /* Исторические 38px: на 2px выше стандартной кнопки (--btn-h-md=36),
     чтобы селекты с нативной рамкой не резались. Привязано к шкале. */
  height: calc(var(--btn-h-md) + 2px);
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  margin: 0;
}
.cf-ctl {
  padding: 0 10px;
  border: 1px solid var(--line-2);
  border-radius: var(--r-chip);
  background: var(--surface);
  color: inherit;
  font: inherit;
}
.cf-tb-label { font-size: var(--fs-base); color: var(--text-2); white-space: nowrap; }

/* =====================================================================
   2026-07: классы под ближайшие JS-фазы доработки интерфейса.
   CSS готов заранее, JS навешивает классы позже.
   ===================================================================== */

/* Обновление списка без стирания старого контента: таблица тускнеет и
   не ловит клики, пока не пришёл ответ (вместо мигания «Загрузка…»
   на каждый тик debounce). */
.is-refreshing {
  opacity: .5;
  pointer-events: none;
  cursor: progress;
}

/* Скелетон-строки на месте будущего списка — layout не «прыгает».
   Использование: <div class="skeleton-rows"><div></div>×N</div>. */
.skeleton-rows > div {
  height: 28px;
  border-radius: var(--r-chip);
  background: linear-gradient(90deg, var(--border) 25%, transparent 50%, var(--border) 75%);
  background-size: 200% 100%;
  animation: shimmer 1.2s infinite;
  margin-bottom: 8px;
}
@keyframes shimmer {
  to { background-position: -200% 0; }
}

/* ── Оверфлоу топбара «Ещё ▾» ──
   На широких экранах группа #nav-more скрыта; на узких (≤1280px —
   ноутбуки 1366×768 и масштаб Windows 125-150%) вместо групп
   «Импорт/экспорт», «Участки», «Сервис» показывается она (дубли их
   пунктов уже лежат в index.html внутри #nav-more).
   :not([hidden]) обязателен: роль-фильтр JS прячет группу атрибутом
   hidden, и CSS не должен его перебивать. */
#nav-more { display: none; }
@media (max-width: 1280px) {
  #nav-import, #nav-plots, #nav-service { display: none; }
  #nav-more:not([hidden]) { display: inline-flex; }
}

/* ── Мини-набор mask-иконок ──
   По образцу .icon-compass: форма — SVG-маска, цвет — currentColor,
   поэтому иконка перекрашивается вместе с текстом кнопки/ссылки и
   темой. Замена разнокалиберным глифам ⚠ ✓ ✕ (шрифтозависимы, всегда
   чёрные, разные под Win/macOS). Использование: <span class="ico
   ico--check"></span> рядом с текстом. */
.ico {
  display: inline-block;
  width: 14px;
  height: 14px;
  background: currentColor;
  -webkit-mask-size: contain;
          mask-size: contain;
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  -webkit-mask-position: center;
          mask-position: center;
  vertical-align: -2px;
}
/* Треугольник с восклицательным знаком. */
.ico--warn {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill-rule='evenodd' d='M12 2L22.5 20.5H1.5ZM11 9.5h2v5h-2zM11 16.5h2v2h-2z'/%3E%3C/svg%3E");
          mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill-rule='evenodd' d='M12 2L22.5 20.5H1.5ZM11 9.5h2v5h-2zM11 16.5h2v2h-2z'/%3E%3C/svg%3E");
}
/* Галочка. */
.ico--check {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M9 16.2 4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4z'/%3E%3C/svg%3E");
          mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M9 16.2 4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4z'/%3E%3C/svg%3E");
}
/* Крестик (закрыть / отменить). */
.ico--close {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M19 6.4 17.6 5 12 10.6 6.4 5 5 6.4 10.6 12 5 17.6 6.4 19 12 13.4 17.6 19 19 17.6 13.4 12z'/%3E%3C/svg%3E");
          mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M19 6.4 17.6 5 12 10.6 6.4 5 5 6.4 10.6 12 5 17.6 6.4 19 12 13.4 17.6 19 19 17.6 13.4 12z'/%3E%3C/svg%3E");
}
/* Стрелка вниз в лоток (скачать). */
.ico--download {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M19 9h-4V3H9v6H5l7 7zM5 18v2h14v-2z'/%3E%3C/svg%3E");
          mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M19 9h-4V3H9v6H5l7 7zM5 18v2h14v-2z'/%3E%3C/svg%3E");
}
/* Стрелка вверх из лотка (загрузить). */
.ico--upload {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M9 16h6v-6h4l-7-7-7 7h4zM5 18v2h14v-2z'/%3E%3C/svg%3E");
          mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M9 16h6v-6h4l-7-7-7 7h4zM5 18v2h14v-2z'/%3E%3C/svg%3E");
}
/* Булавка-геоточка (показать на карте). */
.ico--pin {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill-rule='evenodd' d='M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5a2.5 2.5 0 1 1 0-5 2.5 2.5 0 0 1 0 5z'/%3E%3C/svg%3E");
          mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill-rule='evenodd' d='M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5a2.5 2.5 0 1 1 0-5 2.5 2.5 0 0 1 0 5z'/%3E%3C/svg%3E");
}

/* ============ Печать (Ctrl+P) ============
   Базовые правила: прячем «хром» приложения (топбар, полосу вкладок
   документов, инструменты карты, боковую панель реестра, кнопки),
   таблицы раскрываем на полную высоту без внутренних скроллов, фон
   белый / текст чёрный — независимо от темы. До 2026-07 @media print
   не было вовсе: Ctrl+P печатал топбар и обрезанную таблицу.
   !important — чтобы гарантированно перебить inline-стили из JS. */
@media print {
  /* Свёрнутые разделы раскрываются JS-ом (beforeprint), но их внутренние
     скроллы надо распрямить, иначе на бумагу попадёт только первый экран
     таблицы координат. */
  .coord-scroll, .coord-details {
    overflow: visible !important;
    max-height: none !important;
  }
  .topbar,
  #doc-tabs,
  .doc-tabs,
  .map-tools,
  .journal-layout__side,
  .page__actions,
  .tabs__nav,
  .modal-backdrop,
  .save-status,
  .btn {
    display: none !important;
  }
  /* Карточка захоронения: по умолчанию печатается активная вкладка (базовое
     правило .tabs__pane{display:none} оставляет видимой лишь .is-active).
     Если оператор выбрал разделы в диалоге печати, тело получает класс
     printing-selected — тогда печатаются ИМЕННО отмеченные панели, а не та,
     что открыта на экране. */
  body.printing-selected .tabs__pane { display: none !important; }
  body.printing-selected .tabs__pane[data-print-on] { display: block !important; }

  /* Поля ввода на бумаге — это документ, а не форма. Рамки, заливку и
     стрелку выпадающего списка убираем, значение печатается как обычный
     текст, как в бланке. Флажки
     оставляем как есть — они и на бумаге читаются как флажки. */
  input, select, textarea {
    border: none !important;
    background: transparent !important;
    box-shadow: none !important;
    padding: 0 !important;
    color: #000 !important;
    font: inherit !important;
    appearance: none !important;
    -webkit-appearance: none !important;
    resize: none !important;
  }
  textarea { overflow: visible !important; height: auto !important; min-height: 0 !important; }
  input[type="checkbox"], input[type="radio"] {
    appearance: auto !important;
    -webkit-appearance: auto !important;
  }

  /* Плотнее по вертикали: карточка участка с координатами укладывалась на
     два листа, причём на втором оставалось одно поле.
     У правила ниже был потерян селектор — строка выглядела как
     «   { margin: 12mm; }», браузер её отбрасывал, и поля листа всё это
     время оставались браузерными по умолчанию. */
  @page { size: A4; margin: 12mm; }
  body { font-size: 12px; }
  .form-grid, .fields-grid { gap: 4px 12px !important; }
  .field { margin-bottom: 4px !important; }
  h1 { font-size: 18px !important; margin: 0 0 6px !important; }
  h3 { font-size: 14px !important; margin: 8px 0 4px !important; }
  .page__header { margin-bottom: 6px !important; }
  .content--full, .content { max-width: none !important; }
  .table-wrap {
    overflow: visible !important;
    max-height: none !important;
    border: 0.5pt solid #999 !important;
    border-radius: 0 !important;
  }
  body {
    background: var(--surface);
    color: #000;
  }

  /* На бумаге пастельных заливок нет (README §5.3): принтер разводит их
     в грязно-серый, а копия документа с серыми полосами выглядит как
     брак печати. Остаются серые линии 0,5 pt — тоньше экранных, но на
     бумаге различимые. */
  .alert, .chip, .badge, .card, .panel, .tile,
  table.table th, table.table tbody tr:nth-child(even) > td,
  table.table tbody tr.is-selected > td {
    background: transparent !important;
    box-shadow: none !important;
  }
  table.table th {
    color: #000 !important;
    border-bottom: 0.5pt solid #666 !important;
  }
  table.table td { border-bottom: 0.5pt solid #ccc !important; }
  .alert { border: 0.5pt solid #999 !important; border-radius: 0 !important; color: #000 !important; }
  /* Шапка таблицы повторяется на каждом листе — иначе со второй страницы
     не понять, что за колонки. */
  table.table thead { display: table-header-group; }
  table.table th { position: static !important; }
  table.table tr { page-break-inside: avoid; }

  /* Скруглений на бумаге нет: радиус — экранный приём, на печати он
     только съедает поле. Блоки отделяются серой линией 0,5 pt. */
  .card-block, .step, .settings__group, .table-wrap, .modal, .result-card,
  .help-page__nav, .journal-layout__side, .map-layout__side {
    border-radius: 0 !important;
    border: 0.5pt solid #999 !important;
    background: transparent !important;
    box-shadow: none !important;
  }
  /* Ссылки на бумаге — обычный текст: подчёркнутое синее «Показать на
     схеме» на листе никуда не ведёт. */
  a { color: #000 !important; text-decoration: none !important; }
  /* Блок, который на экране ведёт в другое место, на бумаге не нужен. */
  .card-block__link, .sticky-foot, .card-foot, .table-foot__left,
  .map-dock, .map-zoom, .map-basemap { display: none !important; }
  /* Карточку записи печатаем в две колонки (README §5.3), а не в три:
     третья на A4 схлопывается в нечитаемую полосу. */
  .card-grid { grid-template-columns: 1fr 1fr !important; }
  .card-col--side { grid-column: 1 / -1 !important; }


/* Координата вне диапазона WGS84 — подсветка поля и подпись под таблицей.
   Замечание пользователя 08.2026: форма принимала «324321» как широту и
   молча сохраняла мусор. */
.coord-input--bad {
  border-color: #dc2626 !important;
  background: #fef2f2;
}
.coord-error {
  margin: 6px 0 0;
  font-size: var(--fs-sm);
  line-height: 1.4;
  color: var(--danger-fg);
}

/* Постоянная подпись «Сохраняется автоматически» у шапки карточки участка.
   Ненавязчиво: мельче основного текста, приглушённая, не конкурирует со
   статусом «✓ Сохранено» рядом. */
.save-hint {
  font-size: var(--fs-sm);
  align-self: center;
  white-space: nowrap;
}

/* Ошибка конкретного реквизита — у самого поля, а не общей плашкой.
   Раньше единственным сообщением при сохранении было «нет связи с
   сервером», то есть про что угодно, кроме реальной причины. */
.field-input--bad {
  border-color: #dc2626 !important;
  background: #fef2f2;
}
.field-error {
  margin: 4px 0 0;
  font-size: var(--fs-sm);
  line-height: 1.35;
  color: var(--danger-fg);
}

/* ============ Печатный ДОКУМЕНТ (#print-doc) ============
   Собирается перед печатью из данных карточки (см. printAsDocument).
   На экране не показывается никогда — только на бумаге. Раньше печаталась
   сама форма, и на лист уходил её снимок с рамками полей. */
#print-doc { display: none; }

@media print {
  /* Когда печатаем документ — прячем приложение целиком и показываем только
     собранный узел. */
  body.printing-doc > *:not(#print-doc) { display: none !important; }
  body.printing-doc #print-doc { display: block !important; }

  #print-doc {
    color: #000;
    /* 9,5pt: с крупным снимком и всеми разделами лист плотный, а мельче
       9pt документ уже неудобно читать вслух у стойки. */
    font-size: 9.5pt;
    line-height: 1.18;
  }
  .print-doc__head {
    display: flex;
    gap: 12px;
    align-items: flex-start;
    justify-content: space-between;
    border-bottom: 1.5pt solid #000;
    padding-bottom: 4pt;
    margin-bottom: 6pt;
  }
  #print-doc h1 {
    font-size: 13.5pt !important;
    margin: 0 0 2pt !important;
  }
  .print-doc__sub { font-size: 9pt; color: #444; }
  .print-doc__meta {
    display: flex;
    flex-wrap: wrap;
    gap: 2pt 10pt;
    font-size: 8.5pt;
    max-width: 60%;
    justify-content: flex-end;
    text-align: right;
  }
  /* Верхний ряд: первый раздел слева, снимок справа. Ниже — остальные
     разделы в две колонки (.print-doc__rest). Такая раскладка, а не
     обтекание: таблица не заходит под float (она открывает свой контекст
     форматирования), поэтому при float под снимком оставалась пустая
     полоса в пол-листа и карточка уезжала на второй лист. */
  .print-doc__top {
    display: flex;
    gap: 6mm;
    align-items: flex-start;
    margin-bottom: 6pt;
  }
  .print-doc__top-main { flex: 1; min-width: 0; }
  .print-doc__top-photo { flex: 0 0 auto; }
  /* Фото — КРУПНОЕ: просьба пользователя 08.2026 «минимум 1/8 листа, лучше
     треть». 85×105 мм — это ~8 900 мм² при печатной области A4 с полями
     12 мм (186×273 = 50 778 мм²), то есть примерно шестая часть листа:
     заметно больше требуемого минимума, но столько, чтобы карточка со
     всеми разделами оставалась на ОДНОМ листе.
     object-fit: cover — снимок ЗАПОЛНЯЕТ отведённое место. С contain
     горизонтальный кадр давал сверху и снизу серые поля, и от обещанной
     «шестой части листа» самой фотографии оставалась половина. */
  .print-doc__photo {
    display: block;
    width: 85mm;
    height: 105mm;
    object-fit: cover;
    object-position: center;
    background: #f4f4f4;
    border: 0.5pt solid #999;
  }
  /* Остальные разделы — в две колонки. Раздел не рвём между колонками:
     половина «Ответственного» в одной колонке, половина в другой читается
     как ошибка вёрстки. */
  .print-doc__rest { column-count: 2; column-gap: 8mm; }
  .print-doc__section { break-inside: avoid; margin-bottom: 4pt; }
  /* Разделы с таблицами — снизу, во всю ширину листа. */
  .print-doc__wide { clear: both; }
  #print-doc h2 {
    font-size: 11.5pt !important;
    margin: 0 0 2pt !important;
    padding-bottom: 1.5pt;
    border-bottom: 0.5pt solid #999;
  }
  /* Пары «реквизит: значение» — столбиком: сами разделы уже разложены в две
     колонки (.print-doc__rest), и вторая колонка ВНУТРИ раздела дала бы
     четыре колонки на листе — в них не помещается ни один адрес. */
  .print-doc__pairs {
    width: 100%;
    border-collapse: collapse;
  }
  .print-doc__pairs tbody { display: block; }
  .print-doc__pairs tr { display: block; break-inside: avoid; }
  .print-doc__pairs th {
    font-weight: normal;
    color: #333;
    text-align: left;
    padding: 0.2pt 4pt 0.2pt 0;
    white-space: nowrap;
  }
  .print-doc__pairs th::after { content: ":"; }
  .print-doc__pairs td { font-weight: 600; padding: 0.2pt 0; }
  .print-doc__pairs th, .print-doc__pairs td { display: inline; }
  /* Таблица внутри узкой колонки: table-layout fixed + перенос слов, иначе
     широкий журнал регистраций уезжал за край колонки и правые столбцы
     обрезались (на бумаге это выглядело как потерянные данные). */
  .print-doc__table {
    width: 100%;
    table-layout: fixed;
    border-collapse: collapse;
    font-size: 7.5pt;
    margin-top: 3pt;
  }
  .print-doc__table th, .print-doc__table td {
    border: 0.5pt solid #999;
    padding: 1pt 2pt;
    text-align: left;
    overflow-wrap: anywhere;
  }
  .print-doc__table th { background: #eee; font-weight: 600; }
}

@media print {
  /* Подзаголовок таблицы внутри раздела печатного документа
     (например «Координаты полигона» в карточке участка). */
  .print-doc__subhead {
    font-size: 9.5pt;
    margin: 4pt 0 2pt;
    font-weight: 600;
  }
}
