/* Общие стили страниц: дашборд и страницы действий выглядят одинаково, потому что
   берут разметку отсюда, а не копируют её друг у друга. */

/* ── Как файл поделён ──────────────────────────────────────────────────────
   Файл длинный, и правка в нём опасна не длиной, а каскадом: селектор наверху
   меняет вид внизу, и в диффе это не видно. Поэтому он размечен зонами —
   якорями вида `[zone:...]`, по одной зоне на панель карточки:

       [zone:common]     общие токены, теги и всё, что не про одну панель
       [zone:registry]   дашборд: фильтры, чипы, таблица объектов
       [zone:card-core]  карточка и оболочка панели (.board, .panel*)
       [zone:contracts]  панель «Контракты»
       [zone:notes]      панели «Комментарии» и «Свои заметки»
       [zone:files]      панель «Файлы»
       [zone:documents]  панель «Документы» из Акцента
       [zone:expertises] панель «Экспертизы» из Акцента
       [zone:control-points] панель «Контрольные точки» из Акцента
       [zone:facts]      панель «Данные»
       [zone:people]     панель «Люди»
       [zone:shared-ui]  окна, тосты, кнопки, поля ввода, статусы
       [zone:login]      форма входа (единственная страница без шапки)

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

   Две вещи менять только по согласованию, потому что они бьют по всем зонам
   сразу: переменные `:root` и селекторы по тегу в `[zone:shared-ui]`
   (`textarea`, `button`, `input`). Правка `textarea` в одной панели меняет
   каждое поле ввода на всех страницах. */

/* ===== [zone:common] ===== */

:root {
    --line: #d6d6d6;
    --muted: #666;
    --accent: #1f5f9e;
    --accent-soft: #dce8f4;

    /* Высота шапки сайта. Значение ставит `js/nav.js`, померив её на месте;
       здесь — запасное, на случай, если скрипт ещё не отработал. Нужна всему,
       что тоже липнет к верху: шапка таблицы обязана вставать под шапкой сайта,
       а не под неё. */
    --head-h: 3rem;
}

/* Цвет действия. Комментарий, фото и (позже) файл должны отличаться цветом, а не
   только заголовком: вечером усталый глаз видит, что окно зелёное, раньше, чем
   читает, что оно про фотографии.

   Тон объявляется один раз здесь и дальше берётся переменной `--tone`: и кнопка на
   карточке, и шапка окна, и кнопка отправки внутри формы красятся одним и тем же,
   поэтому разъехаться им негде. Новое действие = ещё один класс `.tone-*`. */

.tone-comment {
    --tone: #1f5f9e;
    --tone-soft: #dce8f4;
}

.tone-photos {
    --tone: #1d7a4b;
    --tone-soft: #dcefe3;
}

.tone-files {
    --tone: #9a6410;
    --tone-soft: #f6e8d0;
}

/* Запись в нашу базу — не отправка наружу, и цвет об этом говорит раньше, чем
   заголовок окна: фиолетовый не спутать ни с синим комментарием, ни с зелёными
   фотографиями, которые уезжают в Акцент. */
.tone-person {
    --tone: #6b4a9e;
    --tone-soft: #e8e0f4;
}

/* Заведение организации стоит в ряду рядом с заведением человека и делает то же
   самое — пишет в нашу базу. Поэтому тон соседний фиолетовому, а не из другой
   части круга: перепутать две записи между собой не страшно, а спутать любую из
   них с отправкой наружу — страшно. */
.tone-organization {
    --tone: #4a5a9e;
    --tone-soft: #dfe3f4;
}

/* «Записать на объект» — не заведение записи, а связывание двух уже заведённых, и
   цвет об этом говорит: зелёно-синий стоит в ряду между фиолетовыми заведениями и
   серым забором, и ни с одним из них не путается. */
.tone-project {
    --tone: #1d6b7a;
    --tone-soft: #dbeaee;
}

/* Слияние дублей — единственное необратимое действие в ряду, и цвет предупреждает
   об этом раньше заголовка: приглушённый красный, ближе к опасному, чем к записи. */
.tone-merge {
    --tone: #94402f;
    --tone-soft: #f4e0dc;
}

/* Справка — не запись и не отправка: она только печатает то, что уже есть. Цвет
   тёплый и единственный такой в ряду кнопок: перепутать «скачать справку» с
   «отправить комментарий» нельзя. */
.tone-spravka {
    --tone: #a1541f;
    --tone-soft: #f6e4d6;
}

/* Повестка совещания — тоже печать документа, но не по объекту, а по совещанию:
   свой тон, чтобы страница не выглядела вкладкой справки. */
.tone-agenda {
    --tone: #35617a;
    --tone-soft: #dde8ee;
}

/* Забор данных из Акцента — не отправка, поэтому и цвет другой: серо-синий вместо
   «действующих» синего и зелёного. Нажать «обновить контракты», думая, что
   отправляешь комментарий, не получится. */
.tone-pull {
    --tone: #4a5a6a;
    --tone-soft: #e2e7ec;
}

body {
    font-family: system-ui, sans-serif;
    margin: 0;
    padding: 1rem 1.25rem 3rem;
    line-height: 1.45;
}

/* Шапка не уезжает при прокрутке: на длинной таблице и на карточке с десятком
   разделов возврат к меню и переключателю слоёв стоил бы прокрутки в начало.

   Отрицательный отступ сверху съедает `padding` тела: без него между верхом
   экрана и прилипшей шапкой оставалась бы щель, сквозь которую ехало содержимое.
   `z-index` выше выпадашки бургера (10) и шапки таблицы (1), но ниже окна
   действия (20) и тостов (30): шапка перекрывает страницу, а не диалог.

   Селектор — корень приложения и его прямой `<header>`, а не тег: `<header>` это
   шапка **раздела**, и такие есть у этапа повестки, у блока участников и у окна
   действия. Пока правило было теговым, все они тоже липли к верху с тем же
   `z-index`, и подписи «Этап 2», «Список участников» вставали поверх открытого
   бургера — а стоять они должны в потоке, на своём месте. */
#app > header,
#nav-app > header {
    position: sticky;
    top: 0;
    z-index: 15;
    background: #fff;
    display: flex;
    flex-wrap: wrap;
    gap: .75rem;
    align-items: baseline;
    border-bottom: 1px solid var(--line);
    margin: -1rem 0 1rem;
    padding: 1rem 0 .6rem;
}

/* Безграничный режим (`Me.unlimited`): работа с полными правами должна быть видна
   глазами, как в dbeaver при подключении к прод-базе. Класс на `<html>` ставит
   `js/api.js`.

   Красится фон, а не рамка: рамку на прокрученной странице не видно, а бледный фон
   виден всегда и не мешает читать. Шапка красится вторым правилом — она липкая и
   белая, и без него поверх красного фона ехала бы белая полоса. */
html.unlimited body {
    background: #fdecea;
}

html.unlimited #app > header,
html.unlimited #nav-app > header {
    background: #fdecea;
    border-bottom: 3px solid #a12a1a;
}

h1 {
    font-size: 1.25rem;
    margin: 0;
}

h2 {
    font-size: 1rem;
    margin: 0 0 .6rem;
}

h3 {
    font-size: .8rem;
    text-transform: uppercase;
    letter-spacing: .03em;
    color: var(--muted);
    margin: 0 0 .35rem;
}

a {
    color: var(--accent);
}

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

/* --- шапка: burger и переключатель слоёв ---
   Разметку задаёт компонент `site-nav` из js/nav.js, здесь только вид. Меню
   всплывает поверх страницы (position: absolute), а не раздвигает её: иначе при
   каждом открытии таблица под шапкой прыгала бы вниз. */

.burger {
    position: relative;
    align-self: center;
}

.burger > summary {
    cursor: pointer;
    list-style: none;
    font-size: 1.15rem;
    line-height: 1;
    padding: .25rem .45rem;
    border: 1px solid var(--line);
    border-radius: 6px;
}

.burger > summary::-webkit-details-marker {
    display: none;
}

.burger[open] > summary {
    border-color: var(--accent);
    color: var(--accent);
}

.burger .pages {
    position: absolute;
    top: calc(100% + .3rem);
    left: 0;
    z-index: 10;
    display: flex;
    flex-direction: column;
    min-width: 14rem;
    padding: .3rem;
    background: #fff;
    border: 1px solid var(--line);
    border-radius: 6px;
    box-shadow: 0 4px 14px rgba(0, 0, 0, .12);
}

.burger .pages a {
    text-decoration: none;
    white-space: nowrap;
    padding: .35rem .6rem;
    border-radius: 4px;
}

.burger .pages a:hover {
    background: #f2f6fa;
}

.burger .pages a.current {
    background: var(--accent-soft);
}

nav.layers {
    display: flex;
    flex-wrap: wrap;
    gap: .4rem;
    align-self: center;
}

nav.layers a {
    text-decoration: none;
    font-size: .85rem;
    border: 1px solid var(--accent);
    border-radius: 6px;
    padding: .25rem .7rem;
}

nav.layers a.current {
    background: var(--accent);
    color: #fff;
}

/* Кнопка своего цвета в том же ряду: тон берётся у класса `tone-*` рядом с ней
   (`js/nav.js`), поэтому новый цветной переход — это строка в навигации, а не
   правило здесь. */
nav.layers a.toned {
    border-color: var(--tone);
    color: var(--tone);
}

nav.layers a.toned.current {
    background: var(--tone);
    color: #fff;
}

/* --- двухколоночный макет: слева неподвижное, справа прокручиваемое --- */

.layout {
    display: grid;
    grid-template-columns: minmax(260px, 360px) 1fr;
    gap: 1.25rem;
    align-items: start;
}

/* Колонка справа не расширяется под своё содержимое. Без этого `1fr` считается
   от `min-width: auto`, то есть от ширины таблицы объектов, — и на телефоне
   таблица растягивала не свою обёртку, а всю страницу: шапка, фильтры и строка
   поиска уезжали за край и наезжали друг на друга. Прокручиваться широкой
   таблице положено внутри `.table-wrap`, а не вместе со страницей. */
.layout > main {
    min-width: 0;
}

.layout.wide-left {
    grid-template-columns: minmax(320px, 440px) 1fr;
}

/* Левая колонка, ширину которой двигают рукой (`asidePane` в js/prefs.js). Здесь
   она задана числом, а не `minmax`: границу тянут в пикселях, и вилка означала бы,
   что колонка сама решает, слушаться ли.

   Система отсчёта для ручки — вся сетка: ручка стоит не внутри колонки, а на
   границе между колонками, и колонка вдобавок прокручивается сама по себе
   (`.layout > .sticky`), то есть ручка уезжала бы вместе с её содержимым. */
.layout.split {
    position: relative;
    grid-template-columns: var(--aside) 1fr;
}

/* Полоса на границе колонок: та же узкая ручка, что у панелей карточки
   (`.panel-grip`), и узкая по той же причине — шире она накрыла бы полосу
   прокрутки левой колонки. Стоит в середине зазора: полширины зазора минус
   полширины самой ручки. */
.aside-grip {
    position: absolute;
    top: 0;
    bottom: 0;
    left: calc(var(--aside) + .375rem);
    width: .5rem;
    border-radius: 4px;
    cursor: col-resize;
    /* иначе браузер примет движение за прокрутку страницы и жест оборвётся */
    touch-action: none;
}

.aside-grip:hover {
    background: var(--accent-soft);
}

.sticky {
    position: sticky;
    top: calc(var(--head-h) + .5rem);
}

/* Левая колонка прокручивается сама по себе. Без этого до нижнего фильтра можно
   было добраться, только прокрутив весь список объектов справа: раскрытых
   выпадашек больше, чем помещается в экран, а страницу двигает соседняя колонка.
   `overscroll-behavior: contain` не даёт прокрутке перескочить на страницу, когда
   фильтры кончились, — иначе список объектов уезжал бы «в довесок». */
.layout > .sticky {
    max-height: calc(100vh - var(--head-h) - 1.5rem);
    overflow-y: auto;
    overscroll-behavior: contain;
}

/* Media query идёт после базового правила намеренно: селектор тот же
   (`.layout > .sticky`), specificity тоже, а media query её не повышает — при
   обратном порядке побеждало бы базовое правило и мобильная отмена не работала. */
@media (max-width: 800px) {
    .layout, .layout.wide-left, .layout.split {
        grid-template-columns: 1fr;
    }

    /* Колонка одна — границы между колонками нет, и двигать нечего. */
    .aside-grip {
        display: none;
    }

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

       `position: static` здесь обязателен, а не «на всякий случай»: у липкого
       grid-элемента контейнер прилипания — вся сетка, а не своя строка, поэтому
       колонка фильтров ехала за пальцем и наезжала на список объектов, стоящий
       под ней. На двух колонках этого не видно — там ей некуда наехать. */
    .layout > .sticky {
        position: static;
        max-height: none;
        overflow-y: visible;
    }

    /* По вертикали таблица растёт, страница едет целиком — вложенный скролл на
       тач-экране это «палец застрял в таблице». По горизонтали прокрутка внутри
       обёртки остаётся: колонок больше, чем ширины телефона, и второй разметки
       под узкий экран для этого не нужно. Липкая шапка колонок при этом
       перестаёт липнуть — прокручивать внутри обёртки больше нечего. */
    .table-wrap {
        max-height: none;
    }
}

/* ===== [zone:registry] — дашборд: фильтры, чипы, таблица объектов ===== */

/* --- фильтры-выпадашки --- */

.dropdown {
    border: 1px solid var(--line);
    border-radius: 6px;
    margin-bottom: .5rem;
    background: #fff;
}

.dropdown > summary {
    cursor: pointer;
    padding: .4rem .6rem;
    list-style: none;
    display: flex;
    align-items: center;
    gap: .4rem;
}

.dropdown > summary::-webkit-details-marker {
    display: none;
}

.dropdown > summary::before {
    content: "▸";
    color: var(--muted);
    font-size: .8em;
}

.dropdown[open] > summary::before {
    content: "▾";
}

.dropdown .chosen {
    margin-left: auto;
    font-size: .8rem;
    color: var(--accent);
}

/* Перестановка фильтров — та же ручка и та же причина, что у колонок таблицы
   (см. `table.objects-table th .grip`): жест забирает себе только она, вся
   остальная строка по-прежнему раскрывает фильтр обычным кликом. */

.dropdown > summary .grip {
    cursor: grab;
    touch-action: none;
    user-select: none;
    color: #b3b3b3;
    margin-left: -.15rem;
}

.dropdown > summary .grip:hover {
    color: var(--accent);
}

.dropdown.dragged {
    border-color: var(--accent);
    background: var(--accent-soft);
}

.dropdown.dragged > summary .grip {
    cursor: grabbing;
    color: var(--accent);
}

/* Пока фильтр едет: протяжка не выделяет текст соседних фильтров, а палец не
   начинает прокручивать колонку. `touch-action` на одной ручке для этого мало —
   перестановка двигает `<details>` по DOM, и палец теряет исходную цель. */
aside.dragging {
    user-select: none;
    touch-action: none;
}

.dropdown .body {
    max-height: 16rem;
    overflow-y: auto;
    padding: .5rem .8rem .8rem;
    border-top: 1px solid var(--line);
}

/* Поиск по значениям внутри выпадашки. Липнет к верху её же прокрутки, а не
   стоит первой строкой: список фильтра прокручивается (`max-height` выше), и
   уехавшее вверх поле пришлось бы возвращать прокруткой обратно — ровно в тот
   момент, когда набранное хочется поправить. Отрицательные отступы съедают
   padding `.body`, чтобы поле не оставляло щели по краям при прокрутке. */
.dropdown .body .sift {
    position: sticky;
    top: 0;
    z-index: 1;
    box-sizing: border-box;
    width: calc(100% + 1.6rem);
    margin: -.5rem -.8rem .3rem;
    padding: .25rem .5rem;
    font: inherit;
    font-size: .8rem;
    background: #fff;
    border: 0;
    border-bottom: 1px solid var(--line);
}

/* Выпадашка, поставленная в строку с другими элементами, а не в колонку фильтров:
   шириной в пару слов, а раскрытый список висит поверх страницы. Поверх — не
   украшение: раздвигая строку, выбор года уводил бы вниз то, ради чего строку и
   открыли, а признак объекта — саму карточку. Ширину задаёт место (`--drop-w`):
   в строке чипов помещается меньше, чем под заголовком панели. */
.dropdown.compact {
    position: relative;
    width: var(--drop-w, 15rem);
    margin-bottom: 0;
}

.dropdown.compact > summary {
    padding: .25rem .5rem;
    font-size: .85rem;
    white-space: nowrap;
}

.dropdown.compact > .body {
    position: absolute;
    left: -1px;
    right: -1px;
    z-index: 6;
    background: #fff;
    border: 1px solid var(--line);
    border-radius: 0 0 6px 6px;
    box-shadow: 0 4px 12px rgb(0 0 0 / .12);
}

.option {
    display: flex;
    gap: .4rem;
    align-items: baseline;
    width: 100%;
    text-align: left;
    font: inherit;
    font-size: .875rem;
    background: none;
    border: 0;
    border-radius: 4px;
    padding: .2rem .3rem;
    cursor: pointer;
}

.option:hover {
    background: #f2f6fa;
}

.option.on {
    background: var(--accent-soft);
}

.option .box {
    width: .9em;
    color: var(--accent);
}

.option .n {
    margin-left: auto;
    color: var(--muted);
}

/* Перенос названий — не колонка, а вид таблицы, поэтому отделён от списка колонок
   чертой: иначе он читается как ещё одна галочка «показать поле». */
.option.wrap-lines {
    margin-top: .35rem;
    padding-top: .45rem;
    border-top: 1px solid var(--line);
}

/* --- кнопки-чипы (округа и быстрые фильтры) --- */

.chips {
    display: flex;
    flex-wrap: wrap;
    gap: .3rem;
    margin-bottom: .6rem;
}

.chip {
    font: inherit;
    font-size: .85rem;
    background: #fff;
    border: 1px solid var(--line);
    border-radius: 999px;
    padding: .2rem .7rem;
    cursor: pointer;
}

.chip:hover {
    border-color: var(--accent);
}

.chip.on {
    background: var(--accent);
    border-color: var(--accent);
    color: #fff;
}

.chip .n {
    color: var(--muted);
    margin-left: .3rem;
}

.chip.on .n {
    color: var(--accent-soft);
}

/* --- список объектов в подборщике --- */

.objects {
    max-height: 66vh;
    overflow-y: auto;
    border: 1px solid var(--line);
    border-radius: 8px;
}

.object {
    display: block;
    width: 100%;
    text-align: left;
    font: inherit;
    background: none;
    border: 0;
    border-bottom: 1px solid var(--line);
    padding: .5rem .7rem;
    cursor: pointer;
}

.object:last-child {
    border-bottom: 0;
}

.object:hover {
    background: #f2f6fa;
}

.object.on {
    background: var(--accent-soft);
}

.object .title {
    font-size: .9rem;
}

.object .meta {
    font-size: .78rem;
    color: var(--muted);
}

/* --- таблица объектов ---
   Колонок бывает много, поэтому горизонтально прокручивается сама таблица, а не
   страница. Строка всегда в одну линию: длинное имя обрезается многоточием, целиком
   показывается в подсказке (title) — так высота строк одинаковая и глазами по
   таблице идти легче.

   Ширина колонки — по самой длинной её строке, но не больше 70 символов. Это
   `width: max-content` на таблице (а не `100%`, которое растягивало узкие колонки
   на весь экран) плюс потолок в `ch` — единице, равной ширине символа, то есть
   ровно в тех единицах, в которых ограничение и задумано. Считает всё браузер
   при раскладке: ни замеров, ни JS, ни лишней перерисовки на каждую загрузку. */

/* Таблица прокручивается сама, а не вместе со страницей. Так шапка колонок
   остаётся на виду: `overflow-x: auto` и без того делал обёртку контейнером
   прокрутки, но по вертикали прокручивать было нечего — и `top` у прилипшего
   `thead` не удерживал его наверху, а **сдвигал вниз**, на первые строки.
   Высота даёт вертикали смысл, и `top: 0` внутри обёртки означает ровно
   «под своей рамкой», а не «под шапкой сайта». */
.table-wrap {
    overflow: auto;
    max-height: calc(100vh - var(--head-h) - 1.5rem);
    border: 1px solid var(--line);
    border-radius: 8px;
    /* Полоса горизонтальной прокрутки рисуется поверх содержимого (overlay), а не
       рядом с ним, и без отступа она ложится на последнюю строку — её и читать, и
       нажать нельзя. Отступ у контейнера прокрутки входит в прокручиваемую
       область, поэтому строка уезжает из-под полосы, а не таблица становится выше. */
    padding-bottom: .45rem;
}

table.objects-table {
    border-collapse: collapse;
    width: max-content;
    font-size: .85rem;
}

table.objects-table th,
table.objects-table td {
    padding: .3rem .6rem;
    text-align: left;
    white-space: nowrap;
    border-bottom: 1px solid var(--line);
}

/* Ширину колонки держит коробка внутри ячейки, а не сама ячейка. Так пришлось
   сделать не для красоты: `max-width` у `td` в таблице с автоматической
   раскладкой браузер не соблюдает — колонка всё равно растягивалась под самое
   длинное название, и ни многоточие, ни перенос не включались никогда.

   Сколько знаков — говорит `--cell-w` с самой таблицы: это ползунок над ней. */
table.objects-table td .cell {
    display: block;
    max-width: var(--cell-w, 44ch);
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Прилипает к верху **обёртки**, а не окна: ближайший контейнер прокрутки —
   `.table-wrap`, и отсчёт идёт от него. Шапку сайта учитывать не надо, её
   перекрытие уже решает высота обёртки. */
table.objects-table thead th {
    position: sticky;
    top: 0;
    z-index: 1;
    background: #fff;
    font-size: .78rem;
    text-transform: uppercase;
    letter-spacing: .02em;
    color: var(--muted);
    border-bottom: 1px solid var(--line);
}

table.objects-table tbody tr:hover {
    background: #f2f6fa;
}

table.objects-table tbody tr:last-child td {
    border-bottom: 0;
}

/* Перестановка колонок — только за ручку. Иначе на планшете пришлось бы выбирать:
   либо колонка тащится, либо широкая таблица прокручивается пальцем. Ручка забирает
   жест себе (touch-action: none), вся остальная шапка прокручивается как обычно.
   Отрицательные margin'ы растягивают площадь под палец, не делая строку выше. */

table.objects-table th .grip {
    display: inline-block;
    vertical-align: middle;
    cursor: grab;
    touch-action: none;
    user-select: none;
    color: #b3b3b3;
    padding: .35rem .4rem;
    margin: -.35rem .1rem -.35rem -.25rem;
}

table.objects-table th .grip:hover {
    color: var(--accent);
}

/* Стрелки перестановки: то же, что ручка, но нажатием. Общие с выбором
   показателей денег (`.pick-line`), где переставляют по вертикали. В шапке
   реестра стоят справа от подписи, после стрелки сортировки, — слева уже живёт
   ручка, и две «хваталки» подряд читались бы как одна.

   Нужны они прежде всего на телефоне: там таблица прокручивается тем же пальцем,
   которым пришлось бы тащить колонку, и перетаскивание в ней недоступно вовсе.
   На ПК не мешают — колонка от них шире не становится.

   Оба места перечислены поимённо, а не одним `.move`: класс с таким именем уже
   занят движениями лимитов (`.limits .move`), и общее правило перевернуло бы им
   вёрстку. */
table.objects-table th .move,
.pick-line .move {
    display: inline-flex;
    gap: .1rem;
}

table.objects-table th .move {
    margin-left: .3rem;
    vertical-align: middle;
}

table.objects-table th .move button,
.pick-line .move button {
    font: inherit;
    line-height: 1;
    color: #b3b3b3;
    background: none;
    border: 1px solid transparent;
    border-radius: 4px;
    padding: .2rem .3rem;
    cursor: pointer;
}

table.objects-table th .move button:hover:enabled,
.pick-line .move button:hover:enabled {
    color: var(--accent);
    border-color: var(--line);
}

/* Крайний в ряду: кнопка остаётся на месте, но не обещает движения — иначе
   шапка дёргалась бы по ширине от каждой перестановки. */
table.objects-table th .move button:disabled,
.pick-line .move button:disabled {
    color: #e0e0e0;
    cursor: default;
}

table.objects-table th.dragged {
    background: var(--accent-soft);
}

table.objects-table th.dragged .grip {
    cursor: grabbing;
    color: var(--accent);
}

/* Сортировка — клик по самой подписи, а не по всей ячейке шапки: рядом живёт
   ручка перестановки, и промах по ней не должен молча перестроить таблицу.
   Стрелка занимает место всегда (`.arrow` шириной с символ), поэтому колонка не
   дёргается по ширине, когда сортировку с неё сняли. */

table.objects-table th .sort {
    cursor: pointer;
    user-select: none;
}

table.objects-table th .sort:hover {
    color: var(--accent);
}

table.objects-table th .arrow {
    display: inline-block;
    width: 1em;
    text-align: center;
    color: var(--accent);
}

table.objects-table thead th.sorted {
    color: var(--accent);
}

/* Пока колонка едет: протяжка мимо не выделяет текст соседних ячеек, а палец не
   начинает прокручивать таблицу. `touch-action` на одной ручке для этого мало —
   перестановка двигает `<th>` по DOM, палец теряет исходную цель и оказывается
   над обычной ячейкой, с которой прокрутка разрешена. */
table.objects-table.dragging {
    user-select: none;
    touch-action: none;
}

/* Переключатель «переносить длинные названия». Строка перестаёт быть одной линией:
   название рисуется целиком в две-три строки, и одинаковое начало у соседей больше
   не значит «не прочитать, не наведя курсор». Многоточие при этом само перестаёт
   работать (`text-overflow` действует только при `nowrap`) — обрезать больше нечего.

   `vertical-align: top` — иначе короткие соседние ячейки повисают посередине
   высокой строки. */
table.objects-table.wrap td {
    vertical-align: top;
}

table.objects-table.wrap td .cell {
    white-space: normal;
    overflow-wrap: anywhere;
}

/* Строка над таблицей: перенос и ширина колонки. Обе настройки про вид, а не про
   отбор, поэтому стоят у таблицы, а не в колонке фильтров. */
.table-tools {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .5rem 1rem;
    margin: 0 0 .5rem;
    font-size: .85rem;
}

/* Кнопка переноса здесь стоит сама по себе, а не в списке колонок, — черта
   сверху, отделявшая её от галочек, тут лишняя. */
.table-tools .option.wrap-lines {
    width: auto;
    margin-top: 0;
    padding-top: .2rem;
    border-top: 0;
}

.cell-width {
    display: flex;
    align-items: center;
    gap: .4rem;
    color: var(--muted);
}

/* Участники выборки — выпадашка над таблицей, а не в колонке фильтров: это не
   отбор строк, а вопрос «кто за ними стоит», и раскрывают её, глядя на таблицу.
   Обычная, а не всплывающая (`.compact`): столбик бывает в шестьсот строк, и
   висеть поверх страницы ему нечем. */
.dropdown.participants {
    margin: 0 0 .5rem;
}

.dropdown.participants > .body {
    max-height: 24rem;
}

/* Кнопки ролей стоят в строку и по содержимому: их полдесятка, и колонка из
   пяти строк на всю ширину съедала бы экран ради выбора одного слова. */
.dropdown.participants .option {
    display: inline-flex;
    width: auto;
    margin-right: .3rem;
}

/* Сам столбик: строка на участника, читается сверху вниз как то, что из него
   скопируется. Прокрутка своя — список длинный, а кнопки копирования должны
   остаться на виду. */
.participants-list {
    display: flex;
    flex-direction: column;
    gap: .15rem;
    max-height: 14rem;
    overflow-y: auto;
    margin: .5rem 0 0;
    font-size: .85rem;
}

.participants-list .one {
    display: flex;
    align-items: baseline;
    gap: .5rem;
}

.participants-list .one .n {
    margin-left: auto;
    color: var(--muted);
}

.cell-width input {
    width: 10rem;
}

table.objects-table td.num {
    text-align: right;
    font-variant-numeric: tabular-nums;
}

/* Признак — колонка на один знак: галочка по центру ячейки, а не прижата к
   левому краю рядом с прочерком соседа. Сама галочка крупнее и зелёная: в
   таблице из двадцати колонок она отвечает на вопрос «стоит или нет», и искать
   её глазами не должно приходиться. Прочерк остаётся бледным — «нет» не должно
   быть заметнее, чем «да». */
table.objects-table td.flag {
    text-align: center;
}

table.objects-table td.flag.yes {
    color: #1d7a4b;
    font-size: 1.05rem;
    font-weight: 700;
    line-height: 1;
}

table.objects-table td.flag:not(.yes) {
    color: var(--muted);
}

table.objects-table td.links a {
    margin-right: .6rem;
}

/* ===== [zone:card-core] — карточка и оболочка панели =====
   Всё, что общее для любой панели: доска, место панели, шапка, свёртка,
   раскрытие поверх страницы, полоса прокрутки тела. Правит тот, кто занят
   механикой панелей, а не отдельным разделом. */

/* --- карточка объекта ---
   Доска панелей: каждый раздел — своя карточка со своей полосой прокрутки. Число
   колонок считает браузер (`auto-fill`), поэтому media query на каждый размер
   экрана не нужна: ПК берёт три, ноутбук две, планшет одну-две, телефон одну. */

.card-head {
    position: sticky;
    top: var(--head-h);
    z-index: 2;
    background: #fff;
    padding: .3rem 0 .5rem;
}

.card-title {
    font-size: 1.05rem;
    margin: 0 0 .5rem;
}

/* Строка несохранённых правок. Стоит в липкой шапке и заметна цветом: правка,
   про которую забыли нажать «сохранить», пропадёт при уходе со страницы, и
   узнавать об этом человек должен до того, как уйдёт, а не после. */
.card-drafts {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .5rem;
    margin: 0 0 .8rem;
    padding: .35rem .6rem;
    border-radius: 6px;
    background: var(--accent-soft);
    font-size: .85rem;
}

/* «Отменить» — тихой кнопкой: отказ от правки не должен выглядеть заметнее самой
   правки, ради которой строка и появилась. */
.card-drafts .act.plain {
    color: var(--muted);
    background: none;
    border-color: var(--line);
}

.card-title .title-input.dirty {
    border-bottom-color: var(--accent);
    background: var(--accent-soft);
}

.marks {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .3rem;
    margin: 0 0 1rem;
}

.mark {
    background: var(--accent-soft);
    color: var(--accent);
    border-radius: 999px;
    padding: .1rem .6rem;
    font-size: .78rem;
    white-space: nowrap;
}

.board {
    display: grid;
    /* Колонка доски — не панель, а её пятая часть: панель занимает `--span`
       таких единиц, и шаг растяжки получается в пять раз мельче ширины панели.
       4.4rem с зазором 1rem дают те же 26rem на пять единиц, что были раньше. */
    grid-template-columns: repeat(auto-fill, minmax(min(4.4rem, 100%), 1fr));
    gap: 1rem;
    align-items: start;
}

/* Панель — карточка фиксированной высоты со своей полосой прокрутки. Высота
   `clamp`: на маленьком экране панель не занимает его целиком, на большом не
   вырождается в узкую щель.

   `content-visibility: auto` — браузер не тратит рендер на панель за пределами
   экрана; `contain-intrinsic-size` подсказывает ему её примерный размер, иначе
   полоса прокрутки страницы дёргалась бы по мере показа панелей. */
.panel {
    border: 1px solid var(--line);
    border-radius: 8px;
    background: #fff;
    padding: .9rem 1.1rem;
    /* система отсчёта для ручки растяжки — она стоит по правому краю панели */
    position: relative;
    content-visibility: auto;
    contain-intrinsic-size: auto 20rem;
    grid-column: span var(--span, 5);
}

/* Ручка растяжки: полоса по правому краю панели, за которую её тянут на
   соседние колонки (`resizeSpan` в js/prefs.js).

   Узкая намеренно: шире 8px она накрыла бы полосу прокрутки тела панели, и
   прокрутить список мышью стало бы нельзя. Видна по наведению — постоянная
   вертикальная черта у каждой панели читалась бы как рамка колонки.

   На телефоне её нет: колонка там одна, растягивать нечего (`--span` ниже тоже
   отменяется), а 8px под пальцем — это промах по содержимому. */
.panel-grip {
    position: absolute;
    top: 0;
    right: 0;
    width: .5rem;
    height: 100%;
    border-radius: 0 8px 8px 0;
    cursor: col-resize;
    /* иначе браузер примет движение за прокрутку страницы и жест оборвётся */
    touch-action: none;
}

.panel-grip:hover {
    background: var(--accent-soft);
}

/* У раскрытой поверх страницы панели доски под ней нет — тянуть не по чему. */
.panel-full .panel-grip {
    display: none;
}

/* Ширина панели — в единицах доски, из `--span`: сколько занять, решает человек
   ручкой или кнопкой на самой панели, а не размер экрана. На широком мониторе в
   ряд встают несколько панелей, и какая из них важнее — знает только он.

   На узком экране единица одна, и растянутая панель означала бы уехавшую за край
   страницу, поэтому ниже, в media query, растяжка отменяется. Свойство, а не
   inline-стиль: правило в media query inline так не перебить. */
@media (max-width: 800px) {
    /* Доска на телефоне — одна колонка, а не `auto-fill` из единиц растяжки.
       Без этого правила `grid-column: auto` ниже означает «панель шириной в одну
       единицу», то есть 4.4rem: панели вставали в три-четыре щели по краю
       экрана, и в каждой стояло по одному слову. */
    .board {
        /* `minmax(0, …)`, а не голый `1fr`: у `1fr` минимум — содержимое, и
           реестровая таблица внутри раздела растягивала не свою обёртку, а всю
           страницу — карточка организации уезжала за край экрана вместе с
           шапкой и фильтрами. Прокручиваться широкой таблице положено внутри
           `.table-wrap` (та же причина, что у `.layout > main` выше). */
        grid-template-columns: minmax(0, 1fr);
    }

    .panel {
        grid-column: auto;
    }

    .panel-grip {
        display: none;
    }
}

/* Перестановка прямо в списке выпадашки: так переставляются разделы карточки и
   поля «Данных». Ручка стоит внутри строки, а не в шапке выпадашки, поэтому
   правила свои — селекторы для шапки (`.dropdown > summary .grip`) к ней не
   подходят. */
.dropdown .option .grip {
    cursor: grab;
    touch-action: none;
    user-select: none;
    color: #b3b3b3;
}

.dropdown .option.dragged {
    background: var(--accent-soft);
    outline: 1px solid var(--accent);
}

.dropdown.dragging {
    user-select: none;
    touch-action: none;
}

/* Выбор разделов карточки. Выпадашка та же, что у фильтров дашборда, но стоит не
   в колонке слева, а над доской, и во всю ширину ей растягиваться незачем. */
.board-settings {
    max-width: 22rem;
}

/* Место панели на доске. Панель лежит в нём, а раскрытая — уезжает из него в
   `body` (`<teleport>`), и место остаётся пустым до её возвращения. Своих
   размеров у него нет: пустое место в сетке не должно держать колонку. */
.panel-slot {
    display: contents;
}

.panel-head {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
    align-items: baseline;
    margin-bottom: .4rem;
}

/* Шапка сворачиваемой панели — она же `summary`. Всё, что про свёртку, висит
   именно на ней: «Данные» тоже панель, но не сворачиваются (их поля приезжают со
   скелетом, экономить нечего), и ни стрелка, ни рука над ними ничего не обещают.

   Родной треугольник убран: его место занимает своя стрелка справа, которая не
   спорит с левой частью заголовка — та раскрывает панель поверх страницы. */
summary.panel-head {
    cursor: pointer;
    list-style: none;
}

summary.panel-head::-webkit-details-marker {
    display: none;
}

summary.panel-head::after {
    content: '⌄';
    margin-left: auto;
    color: var(--muted);
    line-height: 1;
}

.panel-fold[open] > summary.panel-head::after {
    content: '⌃';
}

/* Левая часть заголовка — раскрытие поверх страницы. Подчёркивание пунктиром, а
   не кнопкой: это заголовок раздела, а не действие над данными. */
.panel-title {
    text-decoration: underline dotted;
    text-underline-offset: .2rem;
}

.panel-title:hover {
    color: var(--accent);
}

/* Раскрытая панель: то же, что на доске, но во весь экран и без обрезки. Высота
   тела считается от экрана, а не от доски, — ради этого её и раскрывают. */
.panel-full {
    width: min(68rem, 100%);
    max-height: 100%;
    overflow-y: auto;
    box-shadow: 0 10px 30px rgba(0, 0, 0, .25);
    /* панель за пределами экрана не рендерится (`content-visibility` выше), а
       раскрытая — это ровно то, на что человек сейчас смотрит */
    content-visibility: visible;
}

.panel-full .panel-body {
    max-height: none;
}

.panel-full .modal-close {
    /* стрелка свёртки в окне не нужна: закрывают его крестиком или мимо */
    order: 1;
}

/* Стрелка свёртки в раскрытой панели не нужна: закрывают её крестиком или мимо.
   Селектор длиннее нужного намеренно — он обязан перебить правило раскрытой
   свёртки выше, а то короче. */
.panel-full .panel-fold[open] > summary.panel-head::after {
    content: none;
}

.panel-head h3 {
    margin: 0;
    font-size: .95rem;
}

.panel-head .count {
    font-variant-numeric: tabular-nums;
}

.panel-filters {
    display: flex;
    flex-wrap: wrap;
    gap: .3rem;
    margin-bottom: .5rem;
}

.panel-filters input {
    font: inherit;
    font-size: .8rem;
    padding: .15rem .3rem;
    border: 1px solid var(--line);
    border-radius: 4px;
    min-width: 0;
    flex: 1 1 7rem;
}

/* `overscroll-behavior: contain` не даёт прокрутке перескочить на страницу, когда
   строки в панели кончились, — тот же приём, что у колонки фильтров дашборда. */
.panel-body {
    max-height: clamp(17rem, 40vh, 30rem);
    overflow-y: auto;
    overscroll-behavior: contain;
    /* запас под последней строкой: без него нижняя строка упирается в край
       прокрутки вплотную и читается как обрезанная.

       Справа — запас под саму полосу прокрутки: в Chrome она overlay, места в
       потоке не занимает и ложится поверх правого края строк (кнопки, длинные
       ФИО). `scrollbar-gutter: stable` здесь не спасает — у overlay-полосы
       gutter нулевой, поэтому отступ живой, а не зарезервированный. */
    padding-bottom: .7rem;
    padding-right: .7rem;
}

.panel-more {
    margin: .5rem 0 0;
}

/* Media query после базового правила намеренно — та же причина, что у `.layout`
   выше: селектор и specificity те же, а media query её не повышает.

   На телефоне внутренняя прокрутка выключается: вложенный скролл на тач-экране —
   это когда палец «застревает» в панели вместо прокрутки страницы. Панель растёт,
   страница едет целиком. */
@media (max-width: 800px) {
    .panel-body {
        max-height: none;
        overflow-y: visible;
        /* прокрутки внутри панели нет — и запаса под её полосу тоже не нужно */
        padding-right: 0;
    }

    /* Липкая шапка тут вредна: название объекта занимает четыре строки, и вместе с
       кнопками она заморозила бы половину экрана телефона, оставив содержимому
       щель. На ПК та же шапка стоит одной строкой и не мешает. */
    .card-head {
        position: static;
    }
}

/* ===== [zone:contracts] ===== */

/* Контракты: у объекта их несколько, и каждый — своя карточка с рамкой сверху,
   как заметка. Суммы стоят колонкой, по строке на пару «подпись — сумма». */

.contract {
    border-top: 1px solid var(--line);
    padding: .6rem 0;
    max-width: 80ch;
}

.contract-head {
    display: flex;
    flex-wrap: wrap;
    gap: .6rem;
    align-items: baseline;
    margin: 0 0 .25rem;
    font-size: .8rem;
}

.contract-head .number {
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

.contract-head .status {
    color: var(--accent);
}

/* Срок уходит вправо: он есть не у всех контрактов, и в общем ряду его отсутствие
   дёргало бы соседей влево на каждой второй строке. */
.contract-head .term {
    margin-left: auto;
    font-variant-numeric: tabular-nums;
}

/* Правка сроков: два поля даты и две кнопки — в строку помещаются на любом
   экране, поэтому своей раскладки, в отличие от формы человека, не нужно. */
.contract-term {
    display: flex;
    flex-wrap: wrap;
    gap: .3rem;
    align-items: baseline;
    margin: 0 0 .35rem;
    font-size: .8rem;
}

/* Поле даты не растягивается на всю строку: их два, и рядом с ними стоят кнопки —
   общее правило для полей ввода вытолкнуло бы каждое на свою полосу (та же
   причина, что у даты события в заметке). */
.contract-term .date-field {
    width: auto;
    font: inherit;
    padding: .15rem .3rem;
    border: 1px solid var(--line);
    border-radius: 3px;
    font-variant-numeric: tabular-nums;
}

/* Лента изменений срока. Свёрнута, потому что у контракта, который переподписывали
   трижды, она длиннее самого контракта; строка — «начало 31.12.2025 -> 30.06.2026»
   плюс поле обоснования, и в узкой панели она честно переносится. */
.contract-terms {
    margin: 0 0 .35rem;
    font-size: .8rem;
}

.contract-terms > summary {
    cursor: pointer;
    color: var(--muted);
}

.term-list {
    margin: .25rem 0 0;
    padding: 0;
    list-style: none;
}

.term-list li {
    display: flex;
    flex-wrap: wrap;
    gap: .3rem;
    align-items: baseline;
    margin-bottom: .25rem;
}

/* Обе даты перехода вместе и моноширинными цифрами: их сравнивают глазами, и
   разъехавшиеся разряды это ровно то, что мешает. */
.term-step {
    font-variant-numeric: tabular-nums;
}

.term-list .when {
    font-variant-numeric: tabular-nums;
}

/* Обоснование забирает остаток строки: «подписали ДС № 3 от 14.08.2026» в поле
   шириной в дату не читается. */
.term-reason {
    flex: 1 1 10rem;
    min-width: 8rem;
    font: inherit;
    padding: .15rem .3rem;
    color: inherit;
    background: transparent;
    border: 1px solid var(--line);
    border-radius: 3px;
}

/* Помеченное опечаткой показано бледнее: это не история объекта, а исправленная
   описка, и в общем ряду она читалась бы как настоящий перенос. */
.term-list.typos li {
    color: var(--muted);
}

.contract-subject {
    margin: 0 0 .35rem;
    font-size: .88rem;
    line-height: 1.45;
}

/* Сетка из двух колонок, а не flex-ряд: подпись и её сумма стоят в одной строке
   сетки по построению, и разорвать пару переносом нечем. В ряду они держались
   отрицательным отступом у `dd` — пока три суммы помещались в строку. Длинная
   «оплачено» переносилась на следующую, и этот же отступ выносил её за левый край
   карточки. Второй столбец `max-content`: суммы выравниваются по левому краю друг
   с другом, а сама колонка шире самой длинной из них не становится. */
/* Три колонки: подпись, сумма и её доля от цены контракта. Доля своей колонкой,
   а не хвостом к сумме, — иначе разряды перестают стоять друг под другом. */
.money {
    display: grid;
    grid-template-columns: max-content max-content max-content;
    gap: .1rem .8rem;
    margin: 0 0 .35rem;
    font-size: .85rem;
}

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

/* Числа по правому краю: разряды выстраиваются друг под другом, и «больше» видно
   по длине столбика, а не вычитанием глазами. */
.money dd {
    margin: 0;
    text-align: right;
    font-variant-numeric: tabular-nums;
}

/* «43,2 %» рядом с суммой: сколько это от цены контракта. У самой цены доли нет,
   и ячейка остаётся пустой — место под неё держится, чтобы столбец не прыгал. */
.money .share {
    text-align: right;
    white-space: nowrap;
}

/* Панель «Доведённые лимиты»: две колонки — подпись и сумма. Той же природы, что
   `.money` у контракта, но своя: там третья колонка под долю, а здесь под неё
   пришлось бы держать пустое место во всех четырёх строках. */
.limits {
    display: grid;
    grid-template-columns: 1fr max-content;
    gap: .15rem .8rem;
    margin: 0;
    font-size: .9rem;
}

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

/* Даты доведения — приписка к подписи, а не колонка: колонка под них стояла бы
   пустой у обоих остатков, а сумма — главное в строке, и разряды она уступать
   датам не должна. */
.limits .dates {
    margin-left: .4rem;
    font-size: .78rem;
    color: var(--muted);
    white-space: nowrap;
}

/* Разворачивается только та строка, у которой есть разбивка по годам: у остатков
   её нет, и курсор-указатель обещал бы то, чего не случится. */
.limits dt.foldable {
    cursor: pointer;
    color: inherit;
}

.limits .fold {
    display: inline-block;
    width: .9em;
    color: var(--muted);
}

.limits dd {
    margin: 0;
    text-align: right;
    font-variant-numeric: tabular-nums;
}

/* Движение — вложенная строка: сдвиг слева отделяет их от итога, разряды при
   этом остаются под разрядами (правый край общий). */
.limits .move {
    font-size: .82rem;
    color: var(--muted);
}

.limits dt.move {
    padding-left: 1.4rem;
}

/* Остаток ушёл в минус — расхождение источников, а не поломка панели. Цветом, а
   не пометкой: минус в самой сумме уже написан, задача — чтобы его заметили. */
.limits dd.negative {
    color: #b3261e;
}

.parties {
    display: flex;
    flex-wrap: wrap;
    gap: .1rem 1.2rem;
    margin: 0;
    font-size: .85rem;
}

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

/* ===== [zone:notes] — комментарии и свои заметки =====
   Две панели в одной зоне: у них общие `.note*` и общая форма. */

/* Комментарии: сплошной текст с ограничением строки по длине — читать строку в
   200 символов глаз не умеет. Переносы из источника сохраняются (`pre-line`):
   комментарий с выезда почти всегда список работ. */

.note {
    border-top: 1px solid var(--line);
    padding: .6rem 0;
    max-width: 80ch;
}

.note-head {
    display: flex;
    flex-wrap: wrap;
    gap: .6rem;
    align-items: baseline;
    margin: 0 0 .25rem;
    font-size: .8rem;
}

.note-head .when {
    font-variant-numeric: tabular-nums;
    color: var(--accent);
}

.note-head .who {
    font-weight: 600;
}

.note-head .from {
    margin-left: auto;
}

/* Состояние отправки комментария в Акцент. Рамкой, а не цветом текста: слов там
   два-три («отправляется», «не отправлено»), и в строке с датой и автором они
   иначе читаются как продолжение фамилии. Цвет один на все состояния — какое
   именно, сказано словом; светофор из трёх оттенков пришлось бы объяснять. */
.note-head .delivery {
    margin-left: auto;
    padding: 0 .35rem;
    border: 1px solid var(--line);
    border-radius: .6rem;
    color: var(--muted);
    font-size: .75rem;
    white-space: nowrap;
}

/* Источник и состояние доставки стоят рядом справа: `auto` у обоих раздвинул бы
   их по краям, и подпись «accent» уехала бы к дате. */
.note-head .from + .delivery {
    margin-left: 0;
}

.note-text {
    margin: 0;
    font-size: .88rem;
    line-height: 1.45;
    white-space: pre-line;
}

/* Комментарий, удалённый в чужой системе. Строка остаётся на месте — хронология
   объекта не должна зиять дырами, — но читается как бывшая: текст зачёркнут и
   приглушён, подпись говорит, где и когда его не стало. Прячем не строку, а её
   вес: зачёркнутое видно боковым зрением и не притворяется актуальным. */
.note.gone .note-text {
    text-decoration: line-through;
    color: var(--muted);
}

.note-head .gone-mark {
    margin-left: auto;
    padding: 0 .35rem;
    border: 1px dashed var(--line);
    border-radius: .6rem;
    color: var(--muted);
    font-size: .75rem;
    white-space: nowrap;
}

/* Та же причина, что и у `.from + .delivery`: подписи справа стоят рядом, а не
   расходятся по краям строки. */
.note-head .from + .gone-mark,
.note-head .delivery + .gone-mark {
    margin-left: 0;
}

/* Задача при заметке — отдельной строкой под текстом, а не в шапке. В шапке уже
   стоят дата, автор и кнопки правки, и срок с исполнителем там читались бы как
   продолжение подписи: «12.09 Иванов до 30.09 Петров». Своя строка отвечает на
   свой вопрос — «кому и к какому дню». */
.note-task {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
    align-items: baseline;
    margin: .3rem 0 0;
    font-size: .78rem;
    color: var(--muted);
}

/* Срок — тем же приёмом, что дата в шапке: моноширинные цифры и цвет акцента,
   чтобы взгляд находил его, не читая строку целиком. */
.note-task .due,
.note-head .when {
    font-variant-numeric: tabular-nums;
}

.note-task .due {
    color: var(--accent);
}

.note-task .who {
    font-weight: 600;
    color: initial;
}

/* Важность и «сделано» — рамкой, как состояние доставки: слов там одно-два, и без
   рамки они сливаются с соседними подписями. Светофора нет намеренно: какая именно
   важность, сказано словом из справочника, а три оттенка пришлось бы объяснять. */
.note-task .level,
.note-task .done,
.note-head .level,
.note-head .done {
    padding: 0 .35rem;
    border: 1px solid var(--line);
    border-radius: .6rem;
    white-space: nowrap;
}

.note-task .done,
.note-head .done {
    border-style: dashed;
}

/* Чем чужая система объяснила отказ. Строкой под задачей, а не в самой строке:
   фраза длинная, чужая и нужна редко — но когда нужна, она единственное, по чему
   видно, что чинить. Цветом ошибки, чтобы не читалась как часть заметки. */
.task-reason {
    margin: .2rem 0 0;
    font-size: .75rem;
    color: #94402f;
    overflow-wrap: anywhere;
}

/* Нажатая кнопка-переключатель («Открытые» / «Сделанные», открытый блок задачи):
   видно, в каком состоянии список, без второго элемента управления. */
.act.on {
    border-color: var(--accent);
    color: var(--accent);
}

/* Блок задачи в форме: три поля в одну строку, как и остальные строки формы. */
.task-line select {
    max-width: 10rem;
}

/* Час срока — узкое поле рядом с датой: в нём пять знаков, и растянутое по общему
   правилу для полей ввода оно вытолкнуло бы важность на отдельную полосу. */
.time-field {
    width: 4.5rem;
}

/* Готовые метки задачи: ряд цветных кнопок под формой. Цвета приходят с сервера
   атрибутом `style` — они живут строкой справочника и здесь названы быть не могут;
   стилю остаётся форма, отступы и то, чем выбранная метка отличается от прочих. */
.task-marks {
    display: flex;
    flex-wrap: wrap;
    gap: .3rem;
    margin: 0;
}

.task-mark {
    font: inherit;
    font-size: .75rem;
    padding: .15rem .5rem;
    border: 1px solid transparent;
    border-radius: 999px;
    cursor: pointer;
    /* невыбранная метка приглушена, а не перекрашена: цвет — это и есть её смысл,
       и подменив его, ряд перестал бы читаться как набор, из которого выбирают */
    opacity: .45;
}

.task-mark.on {
    opacity: 1;
    border-color: currentColor;
}

/* Форма своей заметки — внутри самой панели, а не во всплывающем окне: заметку
   пишут, глядя на список уже написанного, и окно поверх него мешало бы. */

.note-form {
    display: grid;
    gap: .4rem;
    margin-bottom: .4rem;
    max-width: 80ch;
}

/* Поле ниже, чем у формы комментария (`textarea { min-height: 7rem }`): заметка
   живёт внутри панели, и вычесть у списка треть высоты ради пустого поля ввода
   дороже, чем растянуть его, когда действительно пишут длинно. */
.note-form textarea {
    min-height: 4rem;
    font-size: .88rem;
    padding: .4rem .5rem;
    border: 1px solid var(--line);
    border-radius: 4px;
}

/* Тема: узкое поле в одну строку, а не во всю ширину формы, как остальные поля
   ввода. Тема — это два-три слова («водоканал», «звонок в МинЖКХ»), и поле во всю
   форму обещало бы предложение, а рядом с текстом заметки читалось бы как её
   заголовок обязательный к заполнению. */
.note-form .note-topic-field {
    width: min(100%, 22rem);
    margin-bottom: .35rem;
    padding: .25rem .4rem;
    font-size: .8rem;
    border: 1px solid var(--line);
    border-radius: 4px;
}

.note-form-line {
    display: flex;
    flex-wrap: wrap;
    gap: .4rem;
    align-items: center;
    margin: 0;
}

/* Дата события стоит в одну строку с кнопкой, а не отдельной полосой: общее
   правило растягивает поля ввода на всю ширину формы, а здесь форма — часть
   панели, и лишняя строка отъедает место у списка. */
.note-form-line .date-field,
.note-form-line .time-field {
    width: auto;
    font-size: .8rem;
    padding: .2rem .3rem;
    border: 1px solid var(--line);
    border-radius: 4px;
}

/* Выпадашки предмета не растягиваются на всю строку: их две, и рядом с ними в той
   же строке стоят дата и кнопки — общее правило для полей ввода вытолкнуло бы их
   на отдельную полосу. */
.note-form-line select {
    font: inherit;
    font-size: .8rem;
    padding: .2rem .3rem;
    border: 1px solid var(--line);
    border-radius: 4px;
    max-width: 14rem;
}

.note-form-line button[type="submit"],
.note-form-line .act {
    padding: .25rem .9rem;
    font-size: .85rem;
}

/* Кнопки правки уходят вправо и бледнеют: список читают чаще, чем правят, и
   ряд одинаково ярких кнопок у каждой заметки перетягивал бы на себя внимание. */
.note-acts {
    margin-left: auto;
    display: flex;
    gap: .3rem;
}

/* Тихий вариант `.act`: без заливки и бледнее текстом. Список читают чаще, чем
   правят, и ряд ярких кнопок у каждой заметки перетягивал бы внимание с текста,
   ради которого панель и открыли. «Отмена» рядом с «Сохранить» — тем же тихим
   видом: заливка делала бы отказ от правки заметнее самой правки. */
.note-form-line .act,
.note-acts .act {
    padding: .1rem .4rem;
    font-size: .75rem;
    color: var(--muted);
    background: none;
    border-color: var(--line);
}

/* Второе нажатие на «Удалить» — уже подтверждение, и выглядеть оно должно иначе,
   иначе разница видна только по тексту. */
.note-acts .act.danger {
    color: #a3271d;
    border-color: #e0a19b;
    background: #fdecea;
}

/* Файлы, выбранные в форме, но ещё не отправленные. Чипами, а не списком: их
   единицы, а рядом с каждым стоит «убрать», и столбик из двух строк с крестиками
   занимал бы больше места, чем сама форма. */
.note-form-files {
    display: flex;
    flex-wrap: wrap;
    gap: .3rem;
    margin: 0;
}

.chosen-file {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    padding: .1rem .4rem;
    font-size: .78rem;
    background: var(--accent-soft);
    border: 1px solid var(--line);
    border-radius: 4px;
}

.chosen-file .act {
    padding: 0 .2rem;
    font-size: .7rem;
    color: var(--muted);
    background: none;
    border: none;
}

/* Файлы уже записанной заметки — той же строкой, что и её текст, а не отдельной
   панелью: это часть записи («вот акт, о котором речь»), и уводить их в общий
   список файлов объекта значило бы разорвать её надвое. */
.note-files {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
    margin: .2rem 0 0;
}

.note-file {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    max-width: 100%;
    font-size: .8rem;
}

/* Длинное имя обрезается многоточием — по той же причине, что и в списке файлов
   объекта: ровные строки читаются лучше, а имя целиком есть в подсказке. */
.note-file a {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.note-file .act {
    padding: 0 .25rem;
    font-size: .7rem;
    color: var(--muted);
    background: none;
    border-color: var(--line);
}

.note-file .act.danger {
    color: #a3271d;
    border-color: #e0a19b;
    background: #fdecea;
}

/* Строка «восстановлен черновик»: набранное вернулось само, и человек должен
   узнать об этом раньше, чем удивится тексту в пустой, как он думал, форме.
   Бледная и одной строкой — это сообщение, а не действие. */
.draft-mark {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .4rem;
    margin: 0;
    font-size: .78rem;
}

.draft-mark .act {
    padding: .1rem .4rem;
    font-size: .72rem;
    color: var(--muted);
    background: none;
    border-color: var(--line);
}

/* ===== [zone:files] =====
   `.hidden-file` живёт здесь, но нужен и форме заметки — правка его
   касается обеих панелей. */

/* Файлы объекта: строка на файл, имя ссылкой, остальное бледным. */

.file-form {
    display: flex;
    flex-wrap: wrap;
    gap: .4rem;
    align-items: center;
    margin-bottom: .5rem;
}

.file-form button {
    padding: .25rem .9rem;
    font-size: .85rem;
}

/* Системное поле выбора файла спрятано, а не убрано: `display: none` вырезал бы
   его из формы вместе с выбранным файлом, а нам нужно только не показывать
   надпись «Файл не выбран», которая в колонку панели всё равно не помещается. */
.hidden-file {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
    pointer-events: none;
}

.file {
    padding: .3rem 0;
    border-top: 1px solid var(--line);
}

/* Длинное имя обрезается многоточием, а не переносится: в списке из двадцати
   документов важнее ровные строки, чем целиком видное имя — оно есть в подсказке. */
.file-name {
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Размер, автор и кнопка — второй строкой. Высота строки файла от этого не
   зависит от длины надписи на кнопке, и список не дёргается на подтверждении. */
.file-meta {
    display: flex;
    gap: .4rem;
    align-items: center;
    margin: .1rem 0 0;
    min-height: 1.4rem;
}

/* Размер моноширинным: столбец цифр читается глазом как столбец, а не как текст. */
.file-meta .size {
    font-variant-numeric: tabular-nums;
    font-size: .8rem;
}

/* Кнопка «убрать» — тем же тихим видом, что у заметок, и уходит вправо. */
.file-meta .act {
    margin-left: auto;
    padding: .1rem .4rem;
    font-size: .75rem;
    color: var(--muted);
    background: none;
    border-color: var(--line);
}

.file-meta .act.danger {
    color: #a3271d;
    border-color: #e0a19b;
    background: #fdecea;
}

/* ===== [zone:documents] — панель «Документы» из Акцента =====
   Строка тяжелее файловой: у документа есть название, дата, автор, подпись и
   несколько файлов сразу. Поэтому не одна строка с многоточием, а блок. */

/* Форма заведения: поля в одну ленту, переносится сама. Отдельной страницы у
   документа нет — заводят его отсюда же, где смотрят. */
.doc-form {
    display: flex;
    flex-wrap: wrap;
    gap: .4rem;
    align-items: center;
    margin-bottom: .5rem;
}

.doc-form input[type="text"] {
    flex: 1 1 10rem;
    min-width: 0;
}

.doc-form input[type="number"] {
    width: 8rem;
}

.doc-form button {
    padding: .25rem .9rem;
    font-size: .85rem;
}

/* Строка журнала: что делали и чем кончилось. Стоит над списком, потому что это
   про то, чего в списке ещё нет либо уже не будет. */
.doc-op {
    display: flex;
    flex-wrap: wrap;
    gap: .4rem;
    align-items: center;
    padding: .25rem 0;
    font-size: .85rem;
    border-top: 1px solid var(--line);
}

.doc-op .what {
    font-weight: 600;
}

.doc {
    padding: .4rem 0;
    border-top: 1px solid var(--line);
}

/* Убранный в Акценте документ остаётся видимым и зачёркнутым — как комментарий:
   исчезнувшая из середины строка означала бы подмену хронологии. */
.doc.gone .doc-name {
    text-decoration: line-through;
    color: var(--muted);
}

.doc-head {
    display: flex;
    flex-wrap: wrap;
    gap: .4rem;
    align-items: baseline;
    margin: 0;
}

.doc-name {
    font-weight: 600;
}

.doc-rename {
    flex: 1 1 12rem;
    min-width: 0;
}

.doc-desc {
    margin: .1rem 0 0;
    color: var(--muted);
    font-size: .85rem;
}

/* Файлы в строку через запятую по месту: их у документа единицы, и вертикальный
   список ради двух имён растянул бы панель втрое. */
.doc-files {
    display: flex;
    flex-wrap: wrap;
    gap: .1rem .6rem;
    margin: .15rem 0 0;
    font-size: .85rem;
}

/* Имя файла здесь не обрезается многоточием, в отличие от панели «Файлы»:
   там строка = файл, а тут файл — часть строки документа, и обрезанное имя
   рядом с названием документа читалось бы как продолжение названия. */
.doc-files .file-name {
    display: inline;
    white-space: normal;
}

.doc-acts {
    display: flex;
    gap: .4rem;
    margin: .2rem 0 0;
    min-height: 1.4rem;
}

/* Тем же тихим видом, что у файлов и заметок. */
.doc-acts .act {
    padding: .1rem .4rem;
    font-size: .75rem;
    color: var(--muted);
    background: none;
    border-color: var(--line);
}

.doc-acts .act.danger {
    color: #a3271d;
    border-color: #e0a19b;
    background: #fdecea;
}

/* ===== [zone:expertises] — панель «Экспертизы» из Акцента =====
   Устроена как «Документы» (`.doc*`) — та же копия чужого реестра, без своей формы:
   заводит и ведёт экспертизу Accent, здесь только показ. */

.expertise {
    border-top: 1px solid var(--line);
    padding: .6rem 0;
    max-width: 80ch;
}

.expertise-head {
    display: flex;
    flex-wrap: wrap;
    gap: .6rem;
    align-items: baseline;
    margin: 0 0 .25rem;
    font-size: .8rem;
}

.expertise-head .status {
    color: var(--accent);
}

.expertise-dates {
    display: flex;
    flex-wrap: wrap;
    gap: .1rem 1rem;
    margin: 0 0 .35rem;
    font-size: .8rem;
    font-variant-numeric: tabular-nums;
}

/* «Положительное» / «Отрицательное» — тихая подпись, кроме отрицательного
   результата: его стоит заметить не читая карточку целиком. */
.conclusion {
    color: var(--accent);
}

.conclusion.negative {
    color: #a3271d;
}

.expertise-money {
    display: grid;
    grid-template-columns: max-content max-content;
    gap: .1rem .8rem;
    margin: 0 0 .35rem;
    font-size: .85rem;
}

.expertise-money dt {
    color: var(--muted);
}

.expertise-money dd {
    margin: 0;
    text-align: right;
    font-variant-numeric: tabular-nums;
}

.expertise-designer,
.expertise-contract {
    margin: 0 0 .2rem;
    font-size: .85rem;
}

.expertise-sections {
    display: flex;
    flex-wrap: wrap;
    gap: .15rem 1rem;
    margin: .2rem 0 0;
    padding: 0;
    list-style: none;
    font-size: .8rem;
}

.expertise-sections li {
    display: flex;
    gap: .3rem;
    align-items: baseline;
}

.expertise-files {
    display: flex;
    flex-wrap: wrap;
    gap: .1rem .6rem;
    margin: .3rem 0 0;
    font-size: .85rem;
}

/* ===== [zone:nts] — панель «НТС» из Акцента =====
   Устроена как «Экспертизы» (`.expertise*`) — та же копия чужого реестра, без
   своей формы: заседание заводит и ведёт Accent, здесь только показ. */

.nts-meeting {
    border-top: 1px solid var(--line);
    padding: .6rem 0;
    max-width: 80ch;
}

.nts-head {
    display: flex;
    flex-wrap: wrap;
    gap: .6rem;
    align-items: baseline;
    margin: 0 0 .25rem;
    font-size: .8rem;
}

.nts-head .status {
    color: var(--accent);
}

.nts-dates,
.nts-protocol {
    margin: 0 0 .2rem;
    font-size: .85rem;
}

.nts-decision {
    margin: .2rem 0 0;
    font-size: .85rem;
}

/* ===== [zone:control-points] — панель «Контрольные точки» из Акцента =====
   Не как «Экспертизы» и «НТС»: там у каждой строки свой набор полей, и они идут
   абзацами, а здесь у всех строк одни и те же четыре даты — их сверяют глазами по
   вертикали, и колонки для этого обязаны стоять друг под другом. */

.control-points {
    font-size: .85rem;
    max-width: 80ch;
}

.cp-row {
    display: grid;
    grid-template-columns: minmax(8rem, 1fr) max-content max-content max-content;
    gap: .2rem .8rem;
    align-items: baseline;
    margin: 0;
    padding: .3rem 0;
    border-top: 1px solid var(--line);
}

.cp-head {
    border-top: 0;
    font-size: .78rem;
}

.cp-term,
.cp-gap {
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.cp-gap {
    text-align: right;
    color: var(--muted);
}

/* Светофор точки — код источника (`green_circle`). Знакомые красятся, незнакомый
   остаётся серым: коды заводят в Акценте, и новый не должен ломать строку. */
.cp-dot {
    display: inline-block;
    width: .55rem;
    height: .55rem;
    margin-right: .35rem;
    border-radius: 50%;
    background: #c4c4c4;
}

.cp-green_circle {
    background: #2e7d32;
}

.cp-yellow_circle {
    background: #e0a100;
}

.cp-red_circle {
    background: #b3261e;
}

/* ===== [zone:facts] — панель «Данные» ===== */

/* Настройки панели «Данные» — в её же теле: они про эту панель, и искать их в
   общей выпадашке разделов было бы негде. Показ полей слева, ширина справа —
   выпадашка раскрывается вниз и подпись ширины ей не мешает. */
.facts-tools {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: center;
    gap: .5rem;
    margin: 0 0 .5rem;
}

.facts-width {
    display: flex;
    align-items: center;
    gap: .3rem;
    margin-left: auto;
    font-size: .78rem;
}

.facts-empty {
    margin: .2rem 0 0;
    color: var(--muted);
    font-size: .85rem;
}

/* Список полей раскрывается **внутри** прокручиваемого тела панели и обрезается
   им. Пока поля показаны, тело высокое и обрезки не видно; после «Сбросить все» в
   панели остаётся одна строка настроек — и выпадашка схлопывается в щель на один
   пункт, то есть перестаёт работать ровно тогда, когда она единственное, чем
   пользоваться. Открытый список держит телу высоту сам; `min-height` сильнее
   `max-height` панели, и это здесь то, что нужно. */
.panel-body:has(.fields-picker[open]) {
    min-height: 19.5rem;
}

/* Подпись и значение парами: grid, а не таблица — значения выравниваются в
   колонку, а длинное имя подрядчика переносится, не растягивая соседей. */
.facts {
    display: grid;
    grid-template-columns: minmax(8rem, max-content) 1fr;
    gap: .15rem .8rem;
    margin: 0;
    font-size: .85rem;
}

/* Поле карточки, показанное внутри раздела («ожидается заход» в экспертизах).
   Раскладка та же, что у «Данных», — это то же самое поле, просто в другом месте;
   отличается только отбивкой снизу, потому что под ним идут строки раздела. */
.panel-facts {
    display: grid;
    grid-template-columns: minmax(8rem, max-content) 1fr;
    gap: .15rem .8rem;
    margin: 0 0 .5rem;
    font-size: .85rem;
}

.panel-facts dt {
    color: var(--muted);
}

.panel-facts dd {
    margin: 0;
}

.panel-facts .fact-input {
    font: inherit;
    width: 100%;
    max-width: 12rem;
    padding: 0 .2rem;
    color: inherit;
    background: transparent;
    border: 1px solid transparent;
    border-bottom-color: var(--line);
}

/* Непринятая правка подсвечена тем же, чем в «Данных»: поле одно и то же, и
   различать его по месту показа человеку не за чем. */
.panel-facts .fact-input.dirty {
    border-color: var(--accent);
    background: var(--accent-soft);
}

/* Кнопка записи встаёт под полем, а не рядом: колонка значения в разделе узкая,
   и в строку они не помещаются. */
.panel-facts dd .act {
    margin-top: .2rem;
}

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

.facts dd {
    margin: 0;
    overflow-wrap: anywhere;
}

/* Числа в «Данных» стоят по левому краю, как остальные значения панели: столбца
   тут нет, выравнивать не с чем, а прижатая вправо сумма отрывается от своей
   подписи. Правило перебивает общий `.num` (он про таблицы, где колонка есть). */
.facts dd.num {
    text-align: left;
    font-variant-numeric: tabular-nums;
}

/* Правимое поле стоит на месте своего значения и не шире его: «охват» и
   координаты — это число, а не строка текста. Рамка бледная, пока в поле не
   встали: два десятка обычных полей ввода посреди справки читались бы как форма,
   а не как карточка. */
.facts .fact-input {
    font: inherit;
    width: 100%;
    max-width: 100%;
    padding: 0 .2rem;
    color: inherit;
    background: transparent;
    border: 1px solid transparent;
    border-bottom-color: var(--line);
    border-radius: 3px;
}

/* Число — не строка текста: «охват», координаты и суммы занимают ровно свою
   ширину, и растягивать их на всю колонку значило бы гонять глаз по пустому
   месту. Названию и дате, наоборот, нужна вся ширина — из-за них панель и
   расширяют. Отличает их тип поля, а не имя: имён полей страница не знает. */
.facts .fact-input[type="number"] {
    width: 9rem;
}

/* Поле с непринятой правкой видно на просмотр: пока правки не сохранены, важно
   не гадать, что именно изменено. */
.facts .fact-input.dirty {
    border-color: var(--accent);
    background: var(--accent-soft);
}

.facts .fact-input:hover:not([disabled]) {
    border-color: var(--line);
}

.facts .fact-input:focus {
    border-color: var(--accent);
    outline: none;
}

/* Признаки и поля панели — те же маленькие выпадашки, что и в повестке
   (`.dropdown.compact`), только уже: слева от них чипы и подпись ширины, и на
   пол-строки такая выпадашка занимала бы больше места, чем то, что ей открывать. */
.marks-picker,
.fields-picker {
    --drop-w: 13rem;
}

/* Кнопки под списком полей — это настройка показа, а не действие над объектом:
   вдвое мельче обычной кнопки и стоят парой в одной строке, чтобы не занимать
   собой пол-выпадашки. */
.fields-picker .more {
    display: flex;
    gap: .3rem;
    margin-top: .4rem;
}

.fields-picker .more button {
    font-size: .65rem;
    padding: .12rem .35rem;
    /* без этого подпись переносится на три строки и кнопка становится выше
       обычной, а не вдвое ниже */
    white-space: nowrap;
}

/* Маркер «поле менялось» — той же природы, что пометки доставки у комментариев:
   значок бледный, пока на него не навели, и не мешает читать само значение.
   Один и тот же вид у кнопки в списке полей и у значка внутри строки признака —
   поэтому оба правила селектором общие, а не разъезжаются по зонам. */
.facts .track,
.ext-links .track,
.marks-picker .track {
    font: inherit;
    line-height: 1;
    cursor: pointer;
    color: var(--muted);
    background: transparent;
    border: 0;
    padding: 0 .25rem;
}

.facts .track:hover,
.ext-links .track:hover,
.marks-picker .track:hover {
    color: var(--accent);
}

/* Значок внутри строки признака уходит вправо: слева галочка и название, и
   маркер посередине рвал бы колонку названий. */
.marks-picker .track {
    margin-left: auto;
}

/* Название объекта в правке: поле ввода, а не текст с карандашом — как и
   остальные правимые поля. Вид у него заголовочный, чтобы карточка не
   перестраивалась от того, что название стало правимым. */
.card-title .title-input {
    font: inherit;
    width: 100%;
    color: inherit;
    background: transparent;
    border: 0;
    border-bottom: 1px dashed var(--muted);
    padding: 0;
}

/* Заметка про строку внутри объекта: подпись «про: …» и полоса слева. Полосой, а
   не фоном: заметки читают подряд, и цветной прямоугольник в середине списка
   мешает читать соседей больше, чем помогает найти нужную. */
.note.about-subject {
    border-left: 3px solid var(--accent);
    padding-left: .5rem;
}

.note-head .subject {
    color: var(--accent);
}

/* Тема в шапке: плотнее соседей, но без цвета — цветом уже помечен предмет
   («про: Иванов И.И.»), и вторая цветная подпись рядом с ним спорила бы с ней. */
.note-head .topic {
    font-weight: 600;
}

/* Кнопка «заметка про эту строку» и её форма: значок бледный, пока на него не
   навели, — как маркер истории в «Данных». Форма разворачивается под строкой,
   поэтому занимает всю ширину. */
.subject-note .track {
    font: inherit;
    line-height: 1;
    cursor: pointer;
    color: var(--muted);
    background: transparent;
    border: 0;
    padding: 0 .25rem;
}

.subject-note .track:hover {
    color: var(--accent);
}

/* Раскрытый значок: сначала написанное про эту строку, потом форма. Блоком на
   всю ширину, потому что стоит он внутри строки — контракта, поля «Данных», —
   и без этого текст заметки поехал бы в её конец. */
.subject-note-body {
    display: block;
    width: 100%;
}

/* Прежде написанное: та же полоса слева, что у заметки с предметом в общем
   списке, — одно и то же означает одно и то же, где бы ни стояло. */
.subject-note-one {
    display: block;
    border-left: 3px solid var(--accent);
    padding-left: .5rem;
    margin: .3rem 0;
    font-size: .9em;
}

.subject-note-one .when,
.subject-note-one .who {
    color: var(--muted);
    margin-right: .4rem;
}

.subject-note-form {
    display: flex;
    flex-wrap: wrap;
    gap: .3rem;
    width: 100%;
    margin: .3rem 0;
}

.subject-note-form textarea {
    flex: 1 1 100%;
}

/* Полное наименование организации под её сокращением: своей строкой и бледнее
   значения — это подпись к нему, а не второе значение поля. */
.facts .full-name,
.parties .full-name,
.workplace .full-name {
    display: block;
    color: var(--muted);
    font-size: .9em;
}

/* Кнопка «полное название» в панелях выглядит так же, как маркер истории в
   «Данных»: одно действие — один вид, где бы оно ни стояло. */
.parties .track,
.workplace .track {
    font: inherit;
    line-height: 1;
    cursor: pointer;
    color: var(--muted);
    background: transparent;
    border: 0;
    padding: 0 .25rem;
}

.parties .track:hover,
.workplace .track:hover {
    color: var(--accent);
}

/* Переход в карточку конторы стоит рядом с её названием тем же значком, что и
   остальные маркеры строки, — значит и выглядеть должен так же: подчёркивание
   ссылки выбивалось бы из ряда, в котором оно единственное. */
.facts a.track,
.parties a.track,
.workplace a.track {
    text-decoration: none;
}

/* Хронология поля: строка на переход, «когда — было — стало». Сетка, а не ряд:
   даты стоят колонкой, и глазами по ним идти легче, чем по обтекающему тексту. */
.track-list {
    display: grid;
    grid-template-columns: max-content 1fr max-content;
    gap: .3rem .8rem;
    align-items: baseline;
    margin: 0;
    padding: 0;
    list-style: none;
    font-size: .9rem;
}

.track-list > li {
    display: contents;
}

.track-list .when {
    color: var(--muted);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.track-list .was {
    color: var(--muted);
    text-decoration: line-through;
}

.track-list .became {
    font-weight: 600;
}

/* ===== [zone:people] ===== */

.people {
    margin-bottom: .9rem;
}

.people h4 {
    font-size: .8rem;
    margin: 0 0 .25rem;
    color: var(--accent);
}

.person {
    display: flex;
    flex-wrap: wrap;
    gap: .1rem .6rem;
    font-size: .85rem;
    padding: .2rem 0;
    border-bottom: 1px solid var(--line);
}

.person:last-child {
    border-bottom: 0;
}

.person .who {
    min-width: 13rem;
}

/* Скрытая роль стоит в строке рядом с должностью, и без пометки они неразличимы:
   обе — серый текст после имени. Приставкой, а не цветом: цветом в этой панели уже
   помечено расхождение источников, и второй цвет рядом читался бы как ошибка. */
.person .actual::before {
    content: "факт.: ";
}

.person .contacts {
    display: flex;
    flex-wrap: wrap;
    gap: .1rem .6rem;
}

/* Контакт, из которого не сделать ссылку (несколько номеров в одном поле),
   остаётся текстом — но выглядеть должен так же, чтобы строка не рвалась. */
.person .raw {
    color: var(--muted);
}

.person .contact {
    display: inline-flex;
    align-items: baseline;
    gap: .2rem;
}

/* Номер, признанный неверным, зачёркивается, но остаётся на виду: так же, как
   комментарий, удалённый в источнике. Видно и что звонить по нему не надо, и что
   возвращать его обратно не надо тоже. */
.person .contact.gone a,
.person .contact.gone .raw,
.person .contacts input.gone {
    text-decoration: line-through;
    color: var(--muted);
}

.person .wrong-mark {
    font-size: .72rem;
}

/* В правке контакт — поле: неверный номер чинится там же, где виден. Ширина по
   содержимому не выйдет (input её не умеет), поэтому фиксированная и небольшая —
   телефон и почта в неё помещаются. */
.person .contacts.editable input {
    font: inherit;
    font-size: .82rem;
    padding: .1rem .25rem;
    border: 1px solid var(--line);
    border-radius: 3px;
    width: 11rem;
}

/* «в accent: Иванов И.И. (18.08.2026)» — что об этом человеке говорит другой
   источник. Своей строкой во всю ширину: приписка сбоку потерялась бы среди
   контактов, а расхождение показывают именно затем, чтобы его заметили. */
.person .elsewhere {
    flex-basis: 100%;
    margin: .1rem 0 0;
    font-size: .78rem;
}

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

   Отдельным правилом, потому что глобальное `input[type="text"]` не покрывает поля
   с подсказками: у них тип тот же, но растут они из своего компонента. */
.stacked input {
    display: block;
    width: 100%;
    box-sizing: border-box;
    padding: .4rem;
    font: inherit;
}

/* Место работы: строка «должность: контора». Во всю ширину, потому что мест бывает
   несколько — директор трёх ООО это три строки, и вставать они должны друг под
   другом, а не в один ряд с контактами. */
.person .workplace {
    flex-basis: 100%;
    margin: .1rem 0 0;
    display: flex;
    flex-wrap: wrap;
    gap: .1rem .4rem;
    font-size: .8rem;
}

.person .workplace .role {
    color: var(--muted);
}

/* Форма правки человека. Тоже во всю ширину строки и обычным переносом: полей
   пять, а колонка панели узкая — в одну строку они не помещаются ни на каком
   экране, и раскладку решает сама вёрстка, а не подбор ширин. */
.person-form {
    flex-basis: 100%;
    display: flex;
    flex-wrap: wrap;
    gap: .3rem;
    margin-top: .3rem;
}

.person-form input,
.person-form select {
    font: inherit;
    padding: .15rem .3rem;
    border: 1px solid var(--line);
    border-radius: 3px;
    min-width: 7rem;
    flex: 1 1 7rem;
}

/* Способ связи и его значение — одна мысль, и разрывать их переносом нельзя:
   «Телефон» без поля рядом читается как отдельный элемент формы. */
.person-form .person-contact {
    display: flex;
    gap: .3rem;
    flex: 2 1 14rem;
}

/* Машина в окне заведения человека. Своей рамкой, потому что это не седьмая
   подробность о человеке, а отдельная запись: заполняют её редко, и в общем
   столбце она читалась бы как обязательное продолжение формы. */
.person-vehicle {
    border: 1px solid var(--line);
    border-radius: 6px;
    padding: .5rem .7rem .1rem;
    margin: 0 0 .75rem;
}

.person-vehicle legend {
    padding: 0 .3rem;
    font-size: .85rem;
    color: var(--muted);
}

/* Поля парами: порознь «Год» и «Цвет» растянулись бы во всю ширину окна, а
   набирают в них по три знака. */
.person-vehicle .pair {
    display: flex;
    gap: .5rem;
}

.person-vehicle .pair > label {
    flex: 1 1 0;
    min-width: 0;
}

.person-vehicle p {
    margin: 0 0 .6rem;
    font-size: .8rem;
}

/* «Править» и крестик в строке человека — мельче, чем кнопка действия в шапке.
   Общий `.act` рассчитан на «Добавить человека» в ряду действий над объектом, и в
   строке панели он выходит крупнее самой строки: два слова служебных кнопок
   занимали места больше, чем ФИО с телефоном.

   Правило только на кнопки самой строки (прямые дети `.person`) и на крестики у
   контакта и места работы — кнопки формы правки и подтверждения удаления остаются
   обычного размера: их читают, а не проскакивают глазом. */
.person > .act,
.person .contact .act,
.person .workplace .act {
    font-size: .72rem;
    line-height: 1.2;
    padding: .1rem .35rem;
    border-radius: 4px;
}

/* Спросили «точно убрать?» — кнопка меняет вид, а не только подпись: у крестика
   подписи нет вовсе. Цвета те же, что у подтверждения в заметках, чтобы «сейчас
   что-то исчезнет» выглядело на карточке одинаково. */
.person .act.danger {
    color: #a3271d;
    border-color: #e0a19b;
    background: #fdecea;
}

/* Подтверждение удаления человека. Во всю ширину строки и с вопросом отдельным
   абзацем: вопрос должен читаться до того, как рука дойдёт до кнопки, а не
   стоять четвёртой подписью в ряду. */
.person-removal {
    flex-basis: 100%;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .3rem;
    margin-top: .3rem;
}

.person-removal p {
    flex-basis: 100%;
    margin: 0;
}

.person-removal input {
    font: inherit;
    padding: .15rem .3rem;
    border: 1px solid var(--line);
    border-radius: 3px;
    flex: 1 1 10rem;
}

/* ===== [zone:shared-ui] — окна, тосты, кнопки, поля, статусы =====
   Ниже начинаются селекторы по тегу (`textarea`, `button`, `input`):
   они действуют на каждой странице, и правка здесь — по согласованию. */

/* --- ссылки в чужие системы (ПИК, УТНКР, ЕАСУЗ, реестр МБС) ---
   Один вид на все места: у поля в «Данных», в шапке раздела и у строки контракта
   это одна и та же мысль — «отсюда можно уйти наружу», и выглядеть она должна
   одинаково. Класс, а не селектор по тегу: обычные ссылки страницы не трогаются. */

.ext-links {
    display: flex;
    flex-wrap: wrap;
    gap: .3rem .5rem;
    margin: 0 0 .4rem;
}

/* Чипом, как признаки объекта: подпись короткая («Стройконтроль УТНКР»), стоит
   вплотную к значению, и подчёркнутая ссылка сливалась бы с самим значением. */
.ext-link {
    background: var(--accent-soft);
    color: var(--accent);
    border-radius: 999px;
    padding: .05rem .5rem;
    font-size: .78rem;
    white-space: nowrap;
    text-decoration: none;
}

.ext-link:hover {
    text-decoration: underline;
}

/* Ссылка и кнопка «скопировать» — одно целое: перенос строки между ними оставил
   бы кнопку без хозяина, а их в строке несколько. */
.ext-pair {
    display: inline-flex;
    align-items: center;
    gap: .1rem;
}

/* --- правка ссылок объекта (object.html, «Данные») --- */

/* Свёрнута по умолчанию и тише соседей: это разбор, а не то, ради чего карточку
   открывают. Ширину не задаём — панель «Данных» бывает и в одну колонку, и в три. */
.links-edit {
    margin-top: .5rem;
}

.links-edit > summary {
    font-size: .8rem;
}

.links-why {
    margin: 0 0 .5rem;
    font-size: .78rem;
}

/* Подпись, адрес и кнопки в строку, а на узкой панели — переносом: адрес длинный,
   и держать его в одну строку с двумя кнопками негде. `min-width: 0` обязателен,
   иначе поле ввода растягивает строку по длине содержимого. */
.link-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .3rem;
    margin-bottom: .35rem;
}

.link-row input {
    flex: 1 1 12rem;
    min-width: 0;
    font: inherit;
    font-size: .8rem;
    padding: .15rem .35rem;
}

.link-row button {
    font-size: .78rem;
    padding: .15rem .5rem;
}

/* Подпись ссылки. Вписанная нами выделяется, потому что от этого зависит, что
   делает «Снять»: у чужой строки этой кнопки нет вовсе. */
.link-name {
    flex: 0 0 auto;
    font-size: .8rem;
    color: var(--muted);
}

.link-name.human {
    color: var(--accent);
    font-weight: 600;
}

/* --- строки перехода к другому объекту (js/object-jump.js) --- */

/* Строка перехода — вспомогательная, поэтому мельче и тише всего остального в
   шапке: она про уход с карточки, а не про саму карточку. */
.card-jump {
    display: flex;
    flex-wrap: wrap;
    gap: .4rem;
    margin: 0 0 .6rem;
}

/* Список находок ложится поверх карточки, а не раздвигает её: шапка липкая, и
   выпадающий список, меняющий её высоту, сдвигал бы всё содержимое под ней. */
.jump {
    position: relative;
    display: inline-block;
}

/* Ширину поле берёт из `size` — оно растёт по набранному (см. `JUMP_WIDTH`).
   `max-width` нужен на телефоне, где вставленное название шире экрана. */
.jump-field {
    font: inherit;
    font-size: .82rem;
    max-width: 100%;
    color: inherit;
    background: transparent;
    border: 1px solid var(--line);
    border-radius: 6px;
    padding: .2rem .5rem;
}

.jump-field:focus {
    border-color: var(--accent);
    outline: none;
}

.jump-field.busy {
    border-style: dashed;
}

/* Галка «только мои» — того же роста, что и строки рядом: она им подпись, а не
   отдельная кнопка. `align-items: center` держит её на одной линии с полями,
   когда строка переносится. */
.jump-mine {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    font-size: .82rem;
    color: var(--muted);
    cursor: pointer;
    user-select: none;
}

.jump-mine input {
    margin: 0;
}

/* Подписи шапки рядом с галкой — того же роста, но жирнее: это про саму
   карточку, а не про уход с неё. */
.head-fact {
    display: inline-flex;
    align-items: center;
    font-size: .82rem;
    padding: .2rem .5rem;
    border: 1px solid var(--line);
    border-radius: 6px;
}

.jump-hits {
    position: absolute;
    z-index: 3;
    top: calc(100% + .2rem);
    left: 0;
    min-width: 100%;
    max-width: min(28rem, 90vw);
    max-height: 16rem;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    background: #fff;
    border: 1px solid var(--line);
    border-radius: 6px;
    box-shadow: 0 4px 12px rgba(0, 0, 0, .12);
}

.jump-hit {
    display: block;
    padding: .3rem .5rem;
    font-size: .82rem;
    color: inherit;
    text-decoration: none;
    border-bottom: 1px solid var(--line);
}

/* Находка-кнопка (режим выбора объекта, `pick`): выглядит так же, как находка-
   ссылка, — у кнопки свои рамка, фон и шрифт, и без этого строка выпала бы из
   списка. Разница между ними одна и не визуальная: ссылка уводит, кнопка выбирает. */
button.jump-hit {
    width: 100%;
    text-align: left;
    font: inherit;
    font-size: .82rem;
    background: transparent;
    border: 0;
    border-bottom: 1px solid var(--line);
    border-radius: 0;
    cursor: pointer;
}

.jump-hit:last-child {
    border-bottom: 0;
}

.jump-hit:hover {
    background: var(--accent-soft);
}

.jump-hit .meta {
    display: block;
    color: var(--muted);
    font-size: .75rem;
}

.jump-hit.empty {
    color: var(--muted);
}

.jump-hit.empty:hover {
    background: none;
}

/* --- кнопки действий на карточке объекта --- */

.card-actions {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
    margin: 0 0 1rem;
}

.act {
    font: inherit;
    font-size: .9rem;
    color: #fff;
    background: var(--tone, var(--accent));
    border: 1px solid var(--tone, var(--accent));
    border-radius: 6px;
    padding: .35rem .9rem;
}

/* Действие бывает и ссылкой (переход на другую страницу должен открываться
   средним кликом), а подчёркнутый текст в ряду кнопок читается как чужеродный. */
a.act {
    display: inline-block;
    text-decoration: none;
}

.act:hover {
    filter: brightness(1.1);
}

/* Результат забора данных стоит рядом со своей кнопкой, а не общей строкой внизу:
   заборов на карточке будет несколько (контракты, дальше закупки и экспертизы), и
   «успешно» под ними всеми не отвечает на вопрос, что именно обновилось. */
.pull {
    display: inline-flex;
    align-items: center;
    gap: .5rem;
}

/* «Обновить всё» — та же кнопка забора, только пожирнее: она стоит первой в ряду
   из семи одинаковых, и без выделения её приходится искать глазами каждый раз. */
.pull-all {
    font-weight: 600;
}

.pull .status {
    margin: 0;
    font-size: .85rem;
}

/* Забор внутри своего раздела: кнопка мелкая и стоит первой строкой тела панели.
   Мельче ряда действий над объектом намеренно — это не то, ради чего карточку
   открывают, а починка устаревшего; крупная кнопка спорила бы за внимание с
   самими строками раздела. */
.panel-pulls {
    display: flex;
    flex-wrap: wrap;
    gap: .4rem;
    margin: 0 0 .5rem;
}

.act.mini {
    font-size: .8rem;
    padding: .15rem .5rem;
    border-radius: 5px;
}

/* Ответ забора уезжает под кнопку, а не встаёт с ней в строку: колонка панели
   узкая, и «Документов в Акценте: 36» рядом с кнопкой растянул бы её на всю доску. */
.pull-mini {
    flex-wrap: wrap;
    gap: .3rem;
}

.pull-mini .status {
    font-size: .8rem;
}

/* --- всплывающее окно действия ---
   Затенение — обычный слой поверх страницы: ни анимаций, ни библиотек, ни второго
   корня приложения. Окно прокручивается само (max-height + overflow), поэтому
   длинная форма на невысоком экране остаётся доступной целиком. */

.backdrop {
    position: fixed;
    inset: 0;
    z-index: 20;
    display: flex;
    align-items: flex-start;
    justify-content: center;
    padding: 2rem 1rem;
    background: rgba(20, 24, 28, .55);
    overflow-y: auto;
}

.modal {
    width: min(34rem, 100%);
    background: #fff;
    border-radius: 10px;
    box-shadow: 0 10px 30px rgba(0, 0, 0, .25);
    overflow: hidden;
}

/* Шапка целиком в цвете действия — то самое отличие, которое видно, не читая. */
.modal-head {
    display: flex;
    align-items: center;
    gap: .6rem;
    padding: .6rem .9rem;
    background: var(--tone-soft, var(--accent-soft));
    border-bottom: 3px solid var(--tone, var(--accent));
}

.modal-head h2 {
    margin: 0;
    font-size: 1rem;
    color: var(--tone, var(--accent));
}

.modal-close {
    margin-left: auto;
    font: inherit;
    line-height: 1;
    color: var(--tone, var(--accent));
    background: none;
    border: 0;
    padding: .2rem .4rem;
}

.modal-body {
    padding: .9rem 1rem;
}

/* Окно под форму, которой нужна ширина (справка: четыре десятка полей). Ширину
   просит действие, а не решает окно, — см. `wide` в `ActionModal`. На узком
   экране `min()` всё равно отдаёт всю ширину, и правило ничего не меняет. */
.modal.wide {
    width: min(58rem, 100%);
}

/* Длинная форма прокручивается внутри окна, а не вместе с ним: иначе кнопки
   выгрузки, прижатые к низу формы, уезжают за край экрана — `.modal` скрывает
   переполнение ради скруглённых углов, и `position: sticky` внутри него
   прилипать не к чему. */
.modal.wide .modal-body {
    max-height: calc(100vh - 8rem);
    overflow-y: auto;
}

/* --- форма справки ---
   Поля идут в две-три колонки, сколько влезет: их четыре десятка, и в один
   столбик они превращаются в экран прокрутки, по которому не видно ни одного
   раздела целиком. Раскладку считает браузер (`auto-fit`), media query на каждый
   размер экрана не нужна. */

.spravka-group {
    margin-bottom: 1.1rem;
}

.spravka-group h3 {
    margin: 0 0 .5rem;
    font-size: .85rem;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--tone, var(--accent));
    border-bottom: 1px solid var(--line);
    padding-bottom: .2rem;
}

.spravka-fields {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
    gap: .1rem .8rem;
}

.spravka-fields > * {
    margin-bottom: .5rem;
}

/* Абзац и поле с галочками — во всю ширину: три строки текста в колонке шириной
   в треть окна читаются столбиком по три слова. */
.spravka-fields > .as-long,
.spravka-fields > .as-multi {
    grid-column: 1 / -1;
}

/* Блок, который решили не печатать. Он остаётся на месте и заполненным: галочку
   возвращают так же легко, как снимают, и набранный текст переживать это обязан. */
.spravka-fields > .off textarea {
    opacity: .45;
}

/* Галочка «печатать» стоит вместо подписи, поэтому подпись перестаёт быть
   бледной: это уже не название поля, а то, на что нажимают. */
.spravka-fields label.cap {
    display: flex;
    align-items: center;
    gap: .35rem;
    color: inherit;
}

.spravka-fields .cap {
    display: block;
    font-size: .8rem;
    color: var(--muted);
}

.spravka-fields textarea {
    resize: vertical;
}

/* Варианты в строку: их два-три, и столбик из «да»/«нет» занял бы вдвое больше
   места, чем сами слова. */
.spravka-fields .picks {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .2rem .9rem;
}

.spravka-fields .picks .inline {
    margin: 0;
    white-space: nowrap;
}

.spravka-fields .picks .own {
    flex: 1 1 12rem;
    width: auto;
}

/* Вид справки — первое, что видно в окне: от него зависит и набор показателей, и
   сам шаблон, поэтому переключатель стоит над формой, а не внутри разделов. */
.spravka-kinds {
    display: flex;
    flex-wrap: wrap;
    gap: .2rem 1.2rem;
    margin: 0 0 .8rem;
}

.spravka-kinds .inline {
    margin: 0;
}

/* Кнопки выгрузки липнут к низу окна: форма длинная, и уезжать за ней вниз ради
   «Скачать .docx» пришлось бы после каждой правки. */
.spravka-print {
    position: sticky;
    bottom: 0;
    display: flex;
    flex-wrap: wrap;
    gap: .6rem;
    margin: 0;
    padding: .6rem 0;
    background: #fff;
    border-top: 1px solid var(--line);
}

.spravka-print button {
    font: inherit;
    padding: .45rem .9rem;
    color: #fff;
    background: var(--tone, var(--accent));
    border: 1px solid var(--tone, var(--accent));
    border-radius: 6px;
}

.spravka-print button:hover:not([disabled]) {
    filter: brightness(1.1);
}

/* --- оповещения ---
   Поверх окна действия (`z-index` больше, чем у `.backdrop`): окно в момент показа
   как раз закрывается, и оповещение не должно моргнуть под уходящим затенением.

   Снизу, а не сверху: сверху липкая шапка карточки, и оповещение перекрывало бы
   название объекта — то самое, по чему человек проверяет, куда он написал. */

.toasts {
    position: fixed;
    left: 50%;
    bottom: 1.2rem;
    transform: translateX(-50%);
    z-index: 30;
    display: flex;
    flex-direction: column;
    gap: .4rem;
    align-items: center;
    /* по клику мимо самого оповещения страница должна работать как обычно:
       контейнер тянется во всю ширину и без этого ловил бы чужие клики */
    pointer-events: none;
    width: min(30rem, calc(100% - 2rem));
}

.toast {
    pointer-events: auto;
    margin: 0;
    padding: .55rem .9rem;
    border-radius: 6px;
    font-size: .9rem;
    color: #fff;
    background: rgba(20, 24, 28, .92);
    box-shadow: 0 6px 18px rgba(0, 0, 0, .25);
    cursor: pointer;
}

/* --- формы действий --- */

.action {
    border: 1px solid var(--line);
    border-radius: 8px;
    padding: .9rem 1rem;
}

/* Форма одна и та же на странице и в окне, поэтому рамка не её: снаружи её ставит
   страница (`.action`), в окне рамкой служит само окно. Своё у формы — полоса
   цветом действия и такая же кнопка отправки: на странице, где шапки окна нет,
   это единственное, чем комментарий отличается от фотографий. */
.action-form {
    border-left: 3px solid var(--tone, var(--accent));
    padding-left: .8rem;
}

.action-form button[type="submit"] {
    color: #fff;
    background: var(--tone, var(--accent));
    border: 1px solid var(--tone, var(--accent));
    border-radius: 6px;
}

.action-form button[type="submit"]:hover:not([disabled]) {
    filter: brightness(1.1);
}

label {
    display: block;
    margin-bottom: .75rem;
}

input[type="number"], input[type="text"], input[type="date"], textarea {
    display: block;
    width: 100%;
    box-sizing: border-box;
    padding: .4rem;
    font: inherit;
}

label.inline {
    display: flex;
    align-items: center;
    gap: .4rem;
}

label.inline input {
    width: auto;
}

/* Поле растёт под текст само — и делает это браузер, а не мы. `field-sizing:
   content` считает высоту по содержимому; обработчика на каждое нажатие клавиши,
   который читал бы `scrollHeight` и тем самым заставлял страницу пересчитывать
   раскладку, для этого не нужно ни одного.

   `min-height` — с чего поле начинается: пустое оно занимает три строки, а не
   семь, потому что растянется само, как только текст того потребует. Прежние
   7rem были попыткой угадать длину комментария заранее — теперь угадывать нечего.

   `max-height` — про телефон. Дальше поле не растёт, а прокручивается внутри
   себя: без потолка длинная заметка выдавила бы с экрана и дату, и кнопку
   «Добавить», и человек писал бы вслепую. `dvh`, а не `vh`: на мобильном адресная
   строка то есть, то нет, и `vh` считает по той высоте, которой сейчас нет.

   Тянуть можно только вниз. По ширине поле уже занимает всю форму (`width: 100%`
   выше), и растягивание вбок ничего не добавляло — только уводило поле за край
   всплывающего окна, потому что окно о размере своего содержимого не знает.
   Потянули руками — браузер запоминает высоту и перестаёт считать сам: это
   осознанно, ручка сильнее автоматики.

   Браузер без `field-sizing` (Chrome/Edge до 123) ведёт себя как раньше: поле
   в `min-height` с полосой прокрутки и ручкой. Хуже, чем было, не становится. */
textarea {
    field-sizing: content;
    min-height: 4.5rem;
    max-height: 60dvh;
    resize: vertical;
}

button {
    font: inherit;
    font-size: .85rem;
    padding: .3rem .75rem;
    cursor: pointer;
}

button[disabled] {
    cursor: not-allowed;
    opacity: .6;
}

.chosen-object {
    border: 1px dashed var(--line);
    border-radius: 6px;
    padding: .4rem .6rem;
    margin-bottom: .75rem;
    font-size: .9rem;
}

.dropzone {
    border: 2px dashed var(--line);
    border-radius: 8px;
    padding: 1.1rem;
    text-align: center;
    color: var(--muted);
    margin-bottom: .75rem;
}

.dropzone.over {
    border-color: var(--accent);
    background: #f2f6fa;
    color: var(--accent);
}

.files {
    margin: .4rem 0 .75rem;
    padding-left: 1.2rem;
    font-size: .85rem;
}

.status {
    margin-top: .9rem;
    padding: .6rem .8rem;
    border-radius: 6px;
    white-space: pre-wrap;
}

.ok {
    background: #e6f4ea;
    border: 1px solid #98cfa8;
}

.fail {
    background: #fdecea;
    border: 1px solid #e0a19b;
}

/* --- журнал отправок фотографий ---
   Строки те же по устройству, что у комментариев (`.note`), и оформлены так же:
   у фотографий нет своей панели в карточке, а вопрос человек задаёт один и тот же
   — «ушло или ещё нет». Отдельные классы, а не `.note`, потому что общего у них
   ровно оформление: заметка — это текст, а строка журнала — состояние операции. */

.deliveries {
    margin-top: 1.2rem;
    border-top: 1px solid var(--line);
    padding-top: .8rem;
}

.deliveries h2 {
    display: flex;
    align-items: baseline;
    gap: .6rem;
    margin: 0 0 .4rem;
    font-size: .95rem;
}

.deliveries h2 .act {
    margin-left: auto;
    font-size: .8rem;
    padding: .2rem .6rem;
}

.delivery {
    border-top: 1px solid var(--line);
    padding: .5rem 0;
}

.delivery-head {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
    align-items: baseline;
    margin: 0 0 .2rem;
    font-size: .8rem;
}

.delivery-head .when {
    font-variant-numeric: tabular-nums;
    color: var(--accent);
}

.delivery-head .what {
    font-weight: 600;
}

/* Состояние отправки — рамкой, а не цветом текста, ровно как у комментария:
   светофор из трёх оттенков пришлось бы объяснять, а слово объяснять не надо. */
.delivery-head .state {
    padding: 0 .35rem;
    border: 1px solid var(--line);
    border-radius: .6rem;
    color: var(--muted);
    font-size: .75rem;
    white-space: nowrap;
}

.delivery-head .act {
    margin-left: auto;
    font-size: .8rem;
    padding: .2rem .6rem;
}

.delivery .names {
    margin: 0;
    font-size: .8rem;
    word-break: break-word;
}

.delivery .status {
    margin-top: .4rem;
    font-size: .85rem;
}

.more {
    margin: .6rem 0 0;
}

.empty {
    color: var(--muted);
    padding: .8rem;
}

/* Поиск. Своих цветов и рамок здесь почти нет: группа находок — та же `.panel`,
   что и раздел карточки, строка находки — тот же `.object`, что и в выборе
   объекта. Новый вид блока завёл бы вторую манеру показывать список. */
.search {
    padding: 0 1rem 1rem;
}

.search-line {
    display: flex;
    gap: .5rem;
    margin: .6rem 0 .4rem;
}

/* Над таблицей объектов та же строка начинает колонку, а не разделяет два блока,
   поэтому верхний отступ ей не нужен. */
main > .search-line {
    margin-top: 0;
}

.search-line input {
    flex: 1;
    min-width: 0;
    font: inherit;
    padding: .45rem .6rem;
    border: 1px solid var(--line);
    border-radius: 6px;
}

/* Ссылкой, а не кнопкой: находка открывается в новой вкладке средним кликом, как
   любая другая ссылка. `.object` из выбора объекта рисует её как строку списка. */
a.object {
    color: inherit;
    text-decoration: none;
}

.object .snippet {
    display: block;
    margin-top: .2rem;
    word-break: break-word;
}

/* Реестры организаций и людей. Своей манеры показывать список здесь нет: строка —
   тот же `.object`, что в поиске и подборщике. Отличается реестр только строкой
   переключателей над списком — и она же единственное, что пришлось описать. */
.registry-tools {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .3rem 1rem;
    margin: 0 0 .4rem;
}

/* Галочка в списке колонок занимает всю ширину выпадашки, здесь их две в строку —
   поэтому ширина по содержимому (та же правка, что у `.table-tools`). */
.registry-tools .option {
    width: auto;
}

/* Реестр листается весь, а не показывает первые находки, как поиск: высота под
   порцию в полсотни строк, дальше прокрутка внутри списка, чтобы «Показать ещё»
   не уезжало за нижний край экрана. */
.registry-rows {
    max-height: max(20rem, calc(100vh - var(--head-h) - 12rem));
}

/* Строка реестра — галочка и ссылка в ряд. Ссылка занимает остаток ширины, чтобы
   клик мимо букв всё равно открывал карточку, а не проваливался в пустоту. */
.registry-row {
    display: flex;
    align-items: center;
    gap: .4rem;
}

.registry-row > a {
    flex: 1;
    min-width: 0;
    color: inherit;
    text-decoration: none;
}

/* Галочка не растягивается на строку, в отличие от галочки в списке колонок, и
   ловит клик площадью, а не одним символом: промах по ней ставит пометку соседу. */
.registry-row > .pick {
    width: auto;
    align-self: stretch;
    align-items: center;
    padding: 0 .3rem;
}

/* Квадрат галочки рисуется рамкой, а не одним символом «✓»: в списке колонок
   пустую галочку опознают по подписи рядом, а в строке реестра рядом с ней ничего
   нет — непомеченная строка выглядела бы как строка, которую нечем пометить. */
.registry-row > .pick .box {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1rem;
    height: 1rem;
    border: 1px solid var(--line);
    border-radius: 3px;
    font-size: .8rem;
    line-height: 1;
}

.registry-row > .pick:hover .box,
.registry-row > .pick.on .box {
    border-color: var(--accent);
}

/* Список помеченных к стиранию: подпись слева, «убрать» справа — та же раскладка,
   что у цены стирания на карточке. Кнопка тихая (как в `.note-acts`): список читают
   ради названий, и ряд заливок сбоку перетягивал бы взгляд на себя. */
.purge-cost .act {
    font-size: .75rem;
    padding: 0 .3rem;
    color: var(--muted);
    background: none;
    border-color: var(--line);
}


/* --- Повестка совещания --------------------------------------------------- */

/* Поля повестки на треть ниже обычных: их тут два десятка, и лишние десять
   пикселей на каждом — это ровно тот экран, который приходится прокручивать
   вместо того, чтобы видеть шапку и первый этап разом. Правило своё, а не правка
   общего `input`: остальные формы короткие, и ужимать их незачем. */
.action-form.agenda input[type="text"],
.action-form.agenda input[type="number"],
.action-form.agenda select {
    padding: .1rem .35rem;
    font-size: .85rem;
    line-height: 1.3;
}

/* Шаблоны повесток. Выпадашка обычная, а не `compact`: тема совещания — строка в
   полстроки протокола, и в колонку «на два слова» не помещается ни одна. В потоке,
   а не поверх страницы: открывают её один раз в начале, и подвинутая шапка тут
   никому не мешает. */
.agenda-templates {
    margin-bottom: 1rem;
}

.agenda-templates .option {
    align-items: first baseline;
    gap: .8rem;
    padding: .35rem .3rem;
}

.agenda-templates .option .n {
    white-space: nowrap;
}

/* Сохранённые заготовки — вторая группа в той же выпадашке: человек ищет в ней
   «чем заполнить форму», а не «откуда оно взялось». Отделены чертой, а не своей
   выпадашкой: два одинаковых списка рядом пришлось бы открывать по очереди. */
.agenda-templates .cap {
    display: block;
    font-size: .8rem;
    color: var(--muted);
    border-top: 1px solid var(--line);
    margin-top: .5rem;
    padding-top: .5rem;
}

/* Шапка повестки — та же сетка, что у справки: поля короткие, и колонка на всю
   ширину окна оставила бы поле «Время» длиной в экран. */
.agenda-head {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
    gap: .1rem .8rem;
    margin-bottom: 1rem;
}

.agenda-head > * {
    margin-bottom: .5rem;
}

.agenda-head > .picks,
.agenda-head > .wide {
    grid-column: 1 / -1;
}

/* ФИО и должность руководителя — своей строкой во всю ширину шапки: в колонку
   13rem длинное ФИО не помещается, а `span 2` на широком экране всё равно давал
   колонку по ширине соседних. Внутри обычный flex, поэтому на телефоне поля
   встают друг под друга без второго набора правил. */
.agenda-head > .pair {
    display: flex;
    flex-wrap: wrap;
    gap: 0 .8rem;
}

.agenda-head > .pair > label {
    flex: 1 1 18rem;
}

/* ФИО шире должности и не ужимается ниже 26rem: должность повторяется из
   совещания в совещание и узнаётся по первым словам, а ФИО читают целиком.
   Базис здесь работает и порогом переноса — как только двум полям перестаёт
   хватать строки, ФИО уезжает на свою и получает её всю. */
.agenda-head > .pair > label.grow {
    flex: 2 1 26rem;
}

.agenda-head .cap {
    display: block;
    font-size: .8rem;
    color: var(--muted);
}

.agenda-head input,
.agenda-head select {
    width: 100%;
    box-sizing: border-box;
}

/* Карточка этапа: РП, его объекты и вопросы по ним. Рамка слева тоном действия —
   в списке из пяти этапов границы карточек должны читаться с одного взгляда.

   `position: relative` — под раскрытый список объектов: он меряет ширину по
   карточке (см. `.dropdown.objects-pick` ниже). */
.stage {
    position: relative;
    border: 1px solid var(--line);
    border-left: 3px solid var(--tone, var(--accent));
    border-radius: 4px;
    padding: .6rem .7rem;
    margin-bottom: .8rem;
}

.stage-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .5rem;
    margin-bottom: .4rem;
}

.stage-acts {
    display: flex;
    gap: .25rem;
}

.stage-people {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .35rem;
    margin-bottom: .5rem;
}

.stage .picked {
    margin: 0 0 .5rem;
    padding: 0;
    list-style: none;
}

.stage .picked > li {
    border-top: 1px solid var(--line);
    padding: .4rem 0;
}

.picked-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: .5rem;
}

/* Вопросы стоят колонкой, по строке на вопрос: у каждого свои кнопки «выше»,
   «ниже» и «удалить», и в общем ряду они перепутались бы с соседними. */
.stage .questions {
    display: flex;
    flex-direction: column;
    gap: .3rem;
    margin-top: .3rem;
    align-items: flex-start;
}

.stage .question {
    display: flex;
    align-items: center;
    gap: .3rem;
    width: 100%;
}

.stage .questions input {
    flex: 1 1 20rem;
    min-width: 0;
}

.stage .search {
    width: 100%;
    box-sizing: border-box;
    margin-bottom: .3rem;
}

/* Подсказка объектов раскрывается по клику в поле поиска и лежит поверх карточки,
   а не в потоке: развёрнутая всегда, она отодвигала вниз всё, ради чего этап и
   открыт, — вопросы и следующий этап. */
.stage .finder {
    position: relative;
}

.stage .objects {
    position: absolute;
    left: 0;
    right: 0;
    z-index: 5;
    max-height: 14rem;
    overflow-y: auto;
    padding: .2rem;
    background: #fff;
    border: 1px solid var(--line);
    border-radius: 4px;
    box-shadow: 0 4px 12px rgb(0 0 0 / .12);
}

.stage .object {
    display: block;
    width: 100%;
    text-align: left;
}

/* Объект, которого ещё нет в базе: название длинное, округ и кнопка короткие. */
.stage .sketch {
    display: flex;
    flex-wrap: wrap;
    gap: .35rem;
    margin-top: .4rem;
}

.stage .sketch input {
    flex: 1 1 18rem;
}

/* Округ и год рядом с фамилией РП: выпадашка на два слова, а не на всю строку. */
.stage .narrow {
    max-width: 12rem;
}

/* Годы — маленькая выпадашка (`.dropdown.compact`), поставленная в строку с РП.
   Своей ширины: «Год окончания» с числом выбранных в 11rem не помещается. */
.stage-people > .dropdown.years {
    --drop-w: 15rem;
}

/* Объекты — третья выпадашка той же строки. Кнопка узкая (слева от неё уже стоят
   округ и год), а раскрытый список — во всю карточку этапа: название ОКС это
   строка протокола, а не пара слов, и в ширину кнопки от него видно первую треть.

   Список меряется по карточке, а не по кнопке: у самой выпадашки снят
   `position: relative`, и её `.body` считает края от `.stage`. Иначе ширину
   пришлось бы задавать числом, и на узком экране список уезжал бы за правый край
   страницы — ровно тем, из-за чего его и расширяли. По вертикали он остаётся под
   своей кнопкой: `top` не задан, а значит берётся место, где список стоял бы в
   потоке. */
.stage-people > .dropdown.objects-pick {
    --drop-w: 11rem;
    position: static;
}

.stage .dropdown.objects-pick > .body {
    left: 0;
    right: 0;
    max-height: 20rem;
}

/* Название переносится и занимает две-три строки. Галочка при этом держится
   первой строки, а не середины: по базовой линии она уезжала бы к последней. */
.stage .dropdown.objects-pick .option {
    align-items: flex-start;
    white-space: normal;
    line-height: 1.3;
    padding: .25rem .3rem;
}

.stage .dropdown.objects-pick .option .box {
    flex: 0 0 auto;
}

/* Поиск человека для этапа: своей строкой, чтобы найденные не растаскивали
   выпадашки округа и года по ширине. */
.stage-find {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .35rem;
    flex-basis: 100%;
}

/* Округ уже добавленного объекта — подпись и выпадашка в строку, с отступом от
   номера объекта, чтобы читалось как свойство объекта, а не как ещё один вопрос. */
.stage .sketch-okrug {
    display: flex;
    align-items: baseline;
    gap: .4rem;
    margin-top: .3rem;
}

.stage .sketch-okrug .cap {
    font-size: .8rem;
    color: var(--muted);
}

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

   Правила scoped'ы на `.agenda` и `.guests`: те же классы (`.stage`, `.picked`,
   `.act`) стоят на карточках объекта и организации, где строк единицы и ужимать
   нечего. */
.action-form.agenda .act,
.guests .act {
    font-size: .78rem;
    padding: .1rem .45rem;
    border-radius: 5px;
}

/* Крупные кнопки формы (печать, выгрузка, «Сформировать список») не тронуты
   намеренно: они без класса `.act`, и разница в размере как раз и отделяет
   действие над строкой от действия над всей бумагой. */

.action-form.agenda .stage,
.guests .stage {
    padding: .4rem .5rem;
    margin-bottom: .5rem;
}

.action-form.agenda .stage-head,
.guests .stage-head {
    margin-bottom: .25rem;
}

.action-form.agenda .stage-acts,
.guests .stage-acts {
    gap: .15rem;
}

/* Кнопки блока и участника — сразу за названием организации и за ФИО, а не у
   правого края. Список узкий по тексту и широкий по экрану: на полутора тысячах
   пикселей взгляд ездил от фамилии к крестику через пустую строку. Должность
   встаёт после кнопок — она подпись, а не то, ради чего строку читают. */
.guests .stage-head,
.guests .picked-head {
    justify-content: flex-start;
    flex-wrap: wrap;
}

/* Название объекта и ФИО участника — мельче обычного текста, но не бледнее: это
   по-прежнему главное в строке, просто строк много. Полужирности хватает, чтобы
   отличить название от вопроса под ним. */
.action-form.agenda .picked-head b,
.guests .picked-head b {
    font-size: .85rem;
    font-weight: 600;
    line-height: 1.35;
}

.action-form.agenda .picked > li,
.guests .picked > li {
    padding: .25rem 0;
}

.action-form.agenda .questions,
.guests .questions {
    gap: .2rem;
    margin-top: .2rem;
}

.action-form.agenda .sketch-okrug {
    margin-top: .2rem;
}

.guests .guest-post {
    font-size: .8rem;
}

.agenda-acts,
.agenda-print {
    display: flex;
    align-items: center;
    gap: .5rem;
    margin: .6rem 0 0;
}

/* Имя заготовки — поле в ряд с кнопкой, а не во всю строку: длиннее двух-трёх
   слов его не набирают, а по списку потом ищут глазами. */
.agenda-acts input[type="text"] {
    flex: 0 1 18rem;
}

/* Список участников — приложение к повестке на той же странице. Отделён от неё
   чертой сверху, а не рамкой: это продолжение той же бумаги, а не соседняя форма.
   Блоки организаций переиспользуют `.stage`: карточка с рамкой тоном — ровно то
   же, что и этап, и второй набор правил под тот же вид был бы копией. */
.guests {
    border-top: 1px solid var(--line);
    margin-top: 1rem;
    padding-top: .6rem;
}

/* Шапка списка участников — заголовок раздела, а не шапка страницы: черта под
   ней своя (раньше она приезжала правилом по тегу `header`, заодно приклеивая
   заголовок к верху экрана поверх открытого бургера). */
.guests-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: .5rem;
    padding-bottom: .4rem;
    margin-bottom: .6rem;
    border-bottom: 1px solid var(--line);
}

.guests-head h2 {
    margin: 0;
    font-size: 1rem;
}

/* Должность — рядом с ФИО и бледнее: в бланке это отдельная колонка, а на
   странице отдельная строка на неё — четыре сотни лишних строк в списке. */
.guest-post {
    margin-left: .5rem;
    color: var(--muted);
    font-size: .9rem;
}

.guests .sketch input:first-of-type {
    flex: 1 1 16rem;
}

.guests .sketch input + input {
    flex: 1 1 14rem;
}

/* Строка «добавить организацию» стоит ниже всех блоков и потому раскладывается
   своими правилами: `.sketch` разложен у блока (`.stage .sketch`), а эта строка
   не внутри блока, а про весь список. */
.guests > .new-stage {
    display: flex;
    flex-wrap: wrap;
    gap: .35rem;
    margin-top: .6rem;
}

.guests > .new-stage input {
    flex: 1 1 20rem;
}

.guests .new-stage-hint {
    margin: .25rem 0 0;
    font-size: .85rem;
}

/* ===== [zone:login] — форма входа =====

   Единственная страница без шапки, без меню и без сессии, поэтому и стилей у неё
   свои несколько правил, а не переопределение общих. Ничего лишнего на ней нет
   намеренно: это первое, что видит человек, и грузиться оно должно мгновенно. */

.signin {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
    padding: 1rem;
}

.signin form {
    display: flex;
    flex-direction: column;
    gap: .7rem;
    width: 100%;
    max-width: 20rem;
    padding: 1.5rem;
    border: 1px solid var(--line);
    border-radius: .5rem;
}

.signin h1 {
    margin: 0 0 .3rem;
    font-size: 1.2rem;
}

.signin input {
    padding: .5rem;
}

.signin button {
    background: var(--accent);
    color: #fff;
    padding: .55rem;
}

/* Место под причину отказа держится всегда: без этого форма прыгает на пол-строки
   ровно в тот момент, когда человек и так промахнулся. */
.signin .why {
    min-height: 1.2rem;
    color: #a11;
    font-size: .9rem;
}

.signin .consent {
    display: flex;
    flex-direction: column;
    gap: .4rem;
    font-size: .85rem;
}

/* display: flex выше перебил бы атрибут hidden */
.signin .consent[hidden] {
    display: none;
}

/* Полоса снизу, а не окно поверх формы: вход она не загораживает, и закрывать её
   перед тем, как ввести пароль, не обязательно. */
.cookie-notice {
    position: fixed;
    inset: auto 0 0;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: .5rem 1rem;
    padding: .7rem 1rem;
    background: #fff;
    border-top: 1px solid var(--line);
    font-size: .85rem;
}

/* display: flex выше перебил бы атрибут hidden */
.cookie-notice[hidden] {
    display: none;
}

.cookie-notice p {
    margin: 0;
}

/* --- Юридические страницы (static/legal/) ------------------------------------ */

.legal {
    max-width: 46rem;
    margin: 0 auto;
    padding: 1rem 1rem 3rem;
    line-height: 1.5;
}

.legal h2 {
    margin-top: 1.6rem;
    font-size: 1.05rem;
}

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

.legal th {
    padding-right: 1rem;
    text-align: left;
    vertical-align: top;
}

/* Поле, которое оператор обязан заполнить: пропустить глазами его нельзя. */
.legal mark.fill {
    background: #f6e8d0;
}

/* Кнопка выхода в шапке: с краю и незаметная. Нажимают её раз в неделю, а
   соседствует она с кнопками, которые нажимают весь день. */
.signout {
    margin-left: auto;
    background: none;
    color: var(--muted);
    border: 1px solid var(--line);
    font-size: .85rem;
    padding: .25rem .6rem;
}

/* --- Карточки организации и человека --------------------------------------- */

/* Классы здесь общие для обеих карточек: устроены они одинаково (шапка с
   приметами и доска разделов), и вторая копия тех же правил под именем
   `.person-*` разъехалась бы с первой на первой же правке отступа. Имена
   остались от организации — переименовывать их дороже, чем помнить это. */

/* Приметы строкой под названием: у организации это ИНН и форма, у человека —
   телефоны и машины. Каждая — пара «что: значение», и таблица из них заняла бы
   полэкрана под шапкой. */
.org-props {
    display: flex;
    flex-wrap: wrap;
    gap: .2rem 1.2rem;
    margin: .3rem 0 0;
    font-size: .9rem;
}

/* Руководство и доли: тот же ряд, что реквизиты, но внутри строки два-три слова
   подряд («Иванов 51 %»), и общий межколоночный зазор их бы растащил. */
.org-props.control span > * + * {
    margin-left: .3rem;
}

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

.card-tools {
    display: flex;
    align-items: center;
    gap: .5rem;
    margin: .5rem 0 0;
}

/* Раскрытая правка выключает липкость шапки: блок высотой в экран, приклеенный
   к верху, закрыл бы собой всё, ради чего его открыли. */
.card-head.editing {
    position: static;
}

/* Блок ручной правки под шапкой карточки — общий человеку и организации: правят
   в обеих одно и то же (кто это, чем подписан, как связаться), и две копии стилей
   разъехались бы на первой же правке. */
.card-edit {
    border-top: 1px solid var(--line);
    margin-top: .5rem;
    padding-top: .5rem;
    font-size: .9rem;
}

.card-edit h4 {
    margin: .8rem 0 .3rem;
    font-size: .85rem;
    color: var(--muted);
}

.card-edit p.muted {
    margin: .2rem 0;
}

/* Опасный раздел карточки: стирание записи и обнуление связей. Отбит рамкой и
   цветом, потому что стоит в том же столбце, что и «Добавить контакт», а делает
   несравнимое — рука, идущая к кнопке по памяти, должна на нём споткнуться. */
.purge {
    border: 1px solid #e0a19b;
    border-radius: 4px;
    padding: .5rem;
    margin: .5rem 0;
    background: #fdf7f6;
}

.purge .act.danger {
    color: #a3271d;
    border-color: #e0a19b;
    background: #fdecea;
}

.purge .status {
    margin-top: 0;
    padding: .1rem .4rem;
}

/* Цена стирания: подпись слева, число справа. Числа в столбик, чтобы «12» и «1»
   сравнивались глазом, а не чтением. */
.purge-cost {
    list-style: none;
    margin: .3rem 0;
    padding: 0;
    font-size: .85rem;
}

.purge-cost li {
    display: flex;
    justify-content: space-between;
    gap: .5rem;
    border-bottom: 1px dotted var(--line);
    padding: .1rem 0;
}

/* Форма заведения — общая организации и человеку: и та и другая стоит между
   строкой поиска и списком (либо под шапкой карточки), поэтому отбита рамкой: без
   неё семь-восемь полей читаются как часть реестра. */
/* Расхождения после слияния дублей: два разных ответа на один вопрос. Красным и
   на виду, потому что молчаливая карточка показала бы один из двух, а второй
   лежал бы рядом никому не видный. Рамкой, а не только цветом текста: строка
   читается целиком, а красный текст означал бы «ошибка», хотя это «выбери». */
.clashes {
    border: 1px solid #e0a19b;
    border-radius: 6px;
    background: #fdecea;
    padding: .4rem .6rem;
    margin: .5rem 0;
}

.clash {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: .2rem .6rem;
    margin: .25rem 0;
    font-size: .9rem;
    color: #a3271d;
}

.clash .what {
    font-weight: 600;
}

.clash a {
    color: inherit;
}

.entity-form {
    border: 1px solid var(--line);
    border-radius: 4px;
    padding: .5rem;
    margin: .5rem 0;
}

/* Второе нажатие «Завести» — уже подтверждение, и выглядеть оно обязано иначе:
   сюда попадают по Enter из поля поиска, и одного изменившегося текста мало. */
.entity-form button.danger {
    color: #a3271d;
    border-color: #e0a19b;
    background: #fdecea;
}

/* Строка правки: подпись, поля и кнопки в ряд, с переносом — полей у машины
   семь, и в одну строку они не помещаются ни на каком экране. */
.edit-line {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .3rem;
    margin: .25rem 0;
}

.edit-line input,
.edit-line select {
    font: inherit;
    padding: .15rem .3rem;
    border: 1px solid var(--line);
    border-radius: 3px;
    min-width: 7rem;
    flex: 1 1 9rem;
}

/* Увольнение: дата и час короткие, причина забирает остаток строки. Без этого
   общий `flex: 1 1 9rem` делит строку на три равные части, и поле под «сокращение
   штата» оказывается такой же ширины, как поле под «17.09.2026». */
.edit-line.dismissal .date-field {
    flex: 0 0 7rem;
    min-width: 0;
}

.edit-line.dismissal .time-field {
    flex: 0 0 5rem;
    min-width: 0;
}

.edit-line.dismissal input:not(.date-field):not(.time-field) {
    flex: 3 1 12rem;
}

.edit-line .kind,
.edit-line .who {
    min-width: 7rem;
}

/* Поля машины — с подписями над ними: «а123бв750» и «17 знаков» без подписи
   различает только тот, кто уже знает, где что. */
.edit-line.wrap label {
    display: flex;
    flex-direction: column;
    flex: 1 1 7rem;
    gap: .1rem;
    font-size: .8rem;
}

/* Поле внутри подписи не тянется: `flex-basis` из `.edit-line select` в колонке
   меряет высоту, а не ширину, и выпадашка вырастала в квадрат на девять строк. */
.edit-line.wrap label input,
.edit-line.wrap label select {
    flex: 0 0 auto;
}

.edit-line.wrap label input {
    min-width: 0;
}

/* Таблица денег стоит над доской и своей полосой прокрутки не заводит: строк в
   ней столько же, сколько у организации ролей, — три-четыре. Полосы нет — значит,
   не нужен и отступ под неё. */
.org-money .table-wrap {
    max-height: none;
    padding-bottom: 0;
}

.org-money table {
    border-collapse: collapse;
    width: 100%;
    font-size: .85rem;
}

.org-money th,
.org-money td {
    padding: .3rem .6rem;
    text-align: left;
    white-space: nowrap;
    border-bottom: 1px solid var(--line);
}

.org-money th.num,
.org-money td.num,
.num {
    text-align: right;
    font-variant-numeric: tabular-nums;
}

/* На телефоне шесть колонок сумм не помещаются никак: таблица уезжала за край
   вместе с заголовками, и видно было «Роль — Объектов — Доведено до ГР…».
   Поэтому строка роли разворачивается сверху вниз: по строке на пару «подпись —
   сумма», подпись берётся из `data-label` (его ставит `MONEY_COLUMNS` в
   js/pages/organization.js).

   Горизонтальной прокрутки при этом не остаётся вовсе — прокручивать нечего, и
   суммы читаются, не убегая из-под пальца. */
@media (max-width: 800px) {
    .org-money thead {
        display: none;
    }

    .org-money table,
    .org-money tbody,
    .org-money tr,
    .org-money td {
        display: block;
    }

    /* Роль от роли отбивается рамкой: строк здесь две-три, и без неё «Подрядчик»
       и «Заказчик» читаются как один длинный список сумм. */
    .org-money tr + tr {
        border-top: 1px solid var(--line);
        margin-top: .4rem;
        padding-top: .4rem;
    }

    /* Рамка сумм — почти во всю ширину экрана. Отступы панели на телефоне
       съедали по трети ширины под пустоту, а суммы в оставшейся щели ломались
       переносом. */
    .panel.org-money {
        padding: .7rem .4rem;
    }

    .org-money td {
        display: flex;
        justify-content: space-between;
        gap: .8rem;
        padding: .1rem .45rem;
        border-bottom: none;
        /* подпись переносится, сумма нет: в ней разряды разделены неразрывным
           пробелом (`Intl.NumberFormat`), и пополам она не рвётся */
        white-space: normal;
    }

    .org-money td::before {
        content: attr(data-label);
        color: var(--muted);
    }
}

/* Выбор показателей таблицы денег. Выпадашка обычная, а не всплывающая: панель
   денег низкая, а всплывающий список обрезала бы она сама — `.panel` живёт с
   `content-visibility: auto`, то есть с paint containment. Раскрытый список
   вместо этого держит панели высоту, как список колонок на дашборде.

   Шириной с колонку фильтров, а не во всю панель: в ней подписи в два слова, и
   растянутая на весь монитор строка «Роль ˄ ˅» читается хуже узкой. */
.money-picker {
    max-width: 22rem;
    font-size: .85rem;
}

/* Строка выбора: галочка показателя и стрелки «выше-ниже» рядом. Стрелки не
   внутри кнопки-галочки, а рядом с ней: нажатие на показатель его включает, и
   вложенная кнопка отменяла бы включение вместе с перестановкой. */
.pick-line {
    display: flex;
    align-items: center;
    gap: .2rem;
    margin: 0;
}

/* Галочка занимает строку, стрелки — сколько нужно: иначе подписи разной длины
   разъезжали бы по вертикали вместе со стрелками. */
.pick-line .option {
    flex: 1 1 auto;
    min-width: 0;
}

/* Отрицательная разница — расхождение источников, а не поломка: заказчику довели
   меньше, чем он уже оплатил подрядчикам. Тем же цветом, что и остаток в панели
   лимитов: это то же самое расхождение, только сложенное по всем объектам. */
.negative {
    color: #b3261e;
}

/* Строки разделов карточки: контракт, человек, связанная фирма, совещание.
   Список, а не таблица: у строки две-три подстроки разной природы (стороны,
   объекты, люди), и в колонки они не встают. */
.org-rows {
    list-style: none;
    margin: 0;
    padding: 0;
}

.org-rows > li {
    padding: .4rem 0;
    border-bottom: 1px solid var(--line);
}

.org-rows > li:last-child {
    border-bottom: none;
}

.org-rows p {
    margin: .1rem 0;
}

.org-row-head {
    display: flex;
    flex-wrap: wrap;
    gap: .2rem .6rem;
    align-items: baseline;
}

.org-row-money {
    display: flex;
    flex-wrap: wrap;
    gap: .2rem 1.2rem;
    font-size: .85rem;
}

/* Сама организация среди сторон контракта — не ссылка и потемнее: уходить с её
   карточки на её же карточку некуда. */
.parties .mine {
    font-weight: 600;
}

/* Объекты организации — тот же список, что на дашборде, но внутри раздела
   карточки: колонки фильтров рядом нет, поэтому фильтры, выбор колонок и
   настройки вида стоят одной строкой над таблицей. */
.objects-list {
    display: flex;
    flex-direction: column;
    gap: .5rem;
}

.list-tools {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .4rem .8rem;
    font-size: .85rem;
}

/* Кнопка переноса тут стоит сама по себе, а не в списке колонок, — черта
   сверху, отделявшая её от галочек, здесь лишняя (как и в `.table-tools`). */
.list-tools .option.wrap-lines {
    width: auto;
    margin-top: 0;
    padding-top: .2rem;
    border-top: 0;
}

.list-tools .cell-width input {
    width: 7rem;
}

/* Прокрутка одна на весь раздел — её ведёт таблица. Иначе их было бы две
   вложенных: тело раздела и обёртка таблицы, и палец (или колесо) застревал бы
   во внутренней. Заодно строка настроек и «Показать ещё» остаются на виду, а не
   уезжают вместе со строками. */
.panel-body:has(.objects-list) {
    max-height: none;
    overflow-y: visible;
}

.objects-list .table-wrap {
    max-height: clamp(15rem, 45vh, 32rem);
}

.objects-list .search-line {
    margin: 0;
}

/* Сравнение: две (изредка три-четыре) записи одного вида рядом. Половины —
   grid'ом с одинаковыми долями, а не таблицей: у блока разное число строк с
   каждой стороны, и таблица потребовала бы выравнивать пустотой то, чего нет. */
.compare-halves {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
    gap: .8rem;
}

.compare-half {
    min-width: 0;
    padding: .4rem .6rem;
    border: 1px solid var(--line);
    border-radius: 6px;
}

/* Строка блока и её кнопка копирования: текст занимает всё, кнопка прижата к
   краю. Кнопка тихая — строк в блоке десятки, и ряд ярких стрелок вдоль столбика
   перетягивал бы внимание с того, что в нём написано. */
.compare-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: .5rem;
}

.compare-row .act {
    flex: none;
    padding: .1rem .4rem;
    font-size: .75rem;
    color: var(--muted);
    background: none;
    border-color: var(--line);
}

/* Второе нажатие — уже подтверждение, и выглядеть оно обязано иначе: отличать
   «скопировать» от «точно?» одним текстом в мелкой кнопке слишком тонко. */
.compare-row .act.danger {
    color: #a3271d;
    border-color: #e0a19b;
    background: #fdecea;
}

/* Расхождение отмечено фоном, а не цветом текста: строку читают целиком, а
   красный текст на строке связи означал бы «ошибка», хотя это всего лишь «есть
   не у всех». */
.differs > td,
li.differs {
    background: rgba(255, 214, 0, .16);
}

/* Вход ключом — вторым, тише основной кнопки: пароль остаётся главным способом. */
.signin .by-key {
    background: none;
    color: var(--accent);
    border: 1px solid var(--accent);
}

/* Страница «Ключи входа»: одна колонка, ей больше не нужно. */
.keys {
    max-width: 40rem;
    padding: 1rem;
}

.keys-add {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
}

.keys-add input {
    flex: 1 1 12rem;
    padding: .4rem;
}

.keys .why {
    min-height: 1.2rem;
    color: #a11;
}

.keys-list {
    list-style: none;
    padding: 0;
}

.keys-list li {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: .5rem;
    padding: .5rem 0;
    border-bottom: 1px solid #ddd;
}

.keys-list button {
    margin-left: auto;
}
