/*
 * Оболочка приложения — направление «Тёмный кабинет»
 * (docs/design/2026-07-27-shell-visual-direction.md).
 *
 * Суть направления — контраст материалов: тёмная панель навигации остаётся
 * тёмной и в светлой теме, справа от неё — светлый рабочий лист. Поэтому
 * цвета сайдбара живут в собственных токенах `--app-side-*` и не читаются
 * из `--bs-*`: общие токены переворачиваются вместе с темой, а сайдбар нет.
 *
 * Всё остальное по-прежнему идёт через токены Bootstrap 5.3 — мы их
 * переопределяем, но не обходим, иначе тёмная тема будет «дырявой» (11Г.2).
 * Готовый `bootstrap.min.css` не пересобирается: node и SCSS в проекте нет.
 *
 * Акцент — ровно одна переменная `--app-accent`. Когда заказчик назовёт
 * фирменный цвет (§11Д), меняться будет она одна, а не полфайла.
 */

/* ── Токены ─────────────────────────────────────────────────────── */

:root,
[data-bs-theme='light'] {
    /* Приглушённая латунь. Работает в двух местах: рельс активного пункта
       меню и главная кнопка — больше акцента интерфейсу не нужно */
    --app-accent: #b99b62;
    /* Латунь на белом даёт всего 2:1 — для текста ссылок она затемнена
       до 5.2:1 (WCAG AA для основного текста) */
    --app-accent-text: #8a6d2f;
    --app-accent-text-hover: #6f571f;

    --app-font-sans: 'Source Sans 3', system-ui, -apple-system, 'Segoe UI', sans-serif;
    --app-font-display: 'Montserrat', system-ui, -apple-system, 'Segoe UI', sans-serif;

    --app-sidebar-width: 262px;
    --app-topbar-height: 58px;
    --app-content-px: 30px;
    --app-content-py: 26px;

    /* Стрелка «откроется соседней вкладкой». Токен общий на обе темы: цвет
       берётся от текста ссылки, в самой картинке он не задан */
    --app-icon-external: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6'/%3E%3Cpath d='M15 3h6v6'/%3E%3Cpath d='M10 14 21 3'/%3E%3C/svg%3E");

    /* Один радиус на контейнеры, другой на контролы — третьего нет */
    --app-radius: 10px;
    --app-radius-sm: 7px;
    /* Тень многослойная: ближний слой отделяет карточку от фона, дальний
       даёт ей вес. Одним слоем получается либо грязь, либо ничего */
    --app-shadow: 0 1px 2px rgba(24, 28, 34, 0.05), 0 8px 24px -12px rgba(24, 28, 34, 0.18);

    /* Сайдбар: тёмный при любой теме */
    --app-side-bg: #15181d;
    --app-side-fg: #a8aeb8;
    --app-side-muted: #7b828d;
    --app-side-icon: #767d88;
    --app-side-brand: #f2f1ee;
    --app-side-hover: rgba(255, 255, 255, 0.045);
    --app-side-active-bg: rgba(185, 155, 98, 0.13);
    --app-side-active-fg: #f0eadd;

    /* Главная кнопка: на светлом — чернильная, на тёмном — латунная */
    --app-btn-bg: #15181d;
    --app-btn-fg: #f2f1ee;
    --app-btn-hover-bg: #23272e;
    --app-btn-shadow: 0 1px 2px rgba(21, 24, 29, 0.16), 0 4px 12px rgba(21, 24, 29, 0.1);

    /* Три серых ниже темнее мокапных: там они давали 3.2–4.5:1 и не проходили
       WCAG AA для основного текста. Тон и насыщенность сохранены, изменена
       только светлота — на глаз разницы почти нет */
    --app-th-fg: #6f757e; /* было #8B9199 — 3.18:1 */
    --app-filter-bg: #fbfbfc;
    --app-ok-bg: #eaf2ed;
    --app-ok-fg: #35664d;
    --app-off-bg: #eff0f2;
    --app-off-fg: #666c75; /* было #6C737C — 4.20:1 */

    /* Переопределения Bootstrap. `body-bg` — это поверхность карточки, а не
       фон окна: так белыми остаются и карточки, и дропдауны, и поля ввода */
    --bs-body-bg: #ffffff;
    --bs-body-bg-rgb: 255, 255, 255;
    --bs-secondary-bg: #f1f2f4;
    --bs-secondary-bg-rgb: 241, 242, 244;
    --bs-tertiary-bg: #fbfbfc;
    --bs-tertiary-bg-rgb: 251, 251, 252;

    --bs-body-color: #1f2328;
    --bs-body-color-rgb: 31, 35, 40;
    --bs-emphasis-color: #0f1216;
    --bs-secondary-color: #686d76; /* было #6A7079 — на фоне окна 4.46:1 */
    --bs-secondary-color-rgb: 104, 109, 118;
    --bs-tertiary-color: #949aa2;
    --bs-heading-color: inherit;

    --bs-border-color: #e4e6e9;
    --bs-border-color-translucent: rgba(24, 28, 34, 0.1);

    --bs-link-color: var(--app-accent-text);
    --bs-link-color-rgb: 138, 109, 47;
    --bs-link-hover-color: var(--app-accent-text-hover);
    --bs-link-hover-color-rgb: 111, 87, 31;

    --bs-primary: var(--app-accent);
    --bs-primary-rgb: 185, 155, 98;
    --bs-focus-ring-color: rgba(185, 155, 98, 0.25);

    --bs-body-font-family: var(--app-font-sans);
    --bs-body-font-size: 0.9375rem;
    --bs-body-line-height: 1.5;

    --bs-border-radius: var(--app-radius-sm);
    --bs-border-radius-sm: 5px;
    --bs-border-radius-lg: var(--app-radius);
    --bs-border-radius-xl: var(--app-radius);
}

[data-bs-theme='dark'] {
    --app-accent-text: #cdaf77;
    --app-accent-text-hover: #ddc79b;

    --app-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);

    --app-side-bg: #0c0e12;

    --app-btn-bg: var(--app-accent);
    --app-btn-fg: #14171b;
    --app-btn-hover-bg: #c8ac76;
    --app-btn-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);

    --app-th-fg: #838b95;
    --app-filter-bg: #15191e;
    --app-ok-bg: #17301f;
    --app-ok-fg: #8cc49e;
    --app-off-bg: #22272d;
    --app-off-fg: #8d959f;

    --bs-body-bg: #181c21;
    --bs-body-bg-rgb: 24, 28, 33;
    --bs-secondary-bg: #101317;
    --bs-secondary-bg-rgb: 16, 19, 23;
    --bs-tertiary-bg: #15191e;
    --bs-tertiary-bg-rgb: 21, 25, 30;

    --bs-body-color: #e6e8eb;
    --bs-body-color-rgb: 230, 232, 235;
    --bs-emphasis-color: #f4f5f7;
    --bs-secondary-color: #929aa4;
    --bs-secondary-color-rgb: 146, 154, 164;
    --bs-tertiary-color: #6e767f;

    --bs-border-color: #252a30;
    --bs-border-color-translucent: rgba(255, 255, 255, 0.1);

    --bs-link-color-rgb: 205, 175, 119;
    --bs-link-hover-color-rgb: 221, 199, 155;
}

/* ── Контрастный режим ──────────────────────────────────────────────
 *
 * Действует поверх обеих тем, а не вместо них: тумблер светлая/тёмная
 * остаётся, меняется только глубина контраста. Обычные цвета проходят
 * WCAG AA (4.5:1) — это порог сломанности, а не комфорт; здесь всё
 * текстовое поднято до AAA (7:1), границы — до 3:1.
 *
 * Значения посчитаны, не подобраны: цифра в комментарии — фактический
 * коэффициент против фона, на котором цвет реально лежит.
 */

[data-contrast='high'] {
    /* Ссылочная латунь темнее: 5.2:1 → 7.57:1 на белом */
    --app-accent-text: #6a5017;
    --app-accent-text-hover: #4d3a0f;

    /* Тень заменяется рамкой карточки: при слабом зрении тень не читается */
    --app-shadow: none;

    /* Сайдбар тёмный при любой теме и из --bs-* не читается — иначе
       контрастный режим обошёл бы навигацию стороной */
    --app-side-fg: #eef0f3; /* 15.58:1 */
    --app-side-muted: #c3c9d1; /* 10.67:1 — было 4.9:1 */
    --app-side-icon: #ced4db; /* 11.92:1 */
    --app-side-brand: #ffffff;
    --app-side-hover: rgba(255, 255, 255, 0.12);
    --app-side-active-bg: rgba(185, 155, 98, 0.3);
    --app-side-active-fg: #ffffff;

    --app-th-fg: #2b3036; /* 13.30:1 — было 4.6:1 */
    /* Панель фильтров на белом была неотличима от карточки */
    --app-filter-bg: #f1f2f4;
    --app-ok-bg: #dcecdf;
    --app-ok-fg: #17452c; /* 8.89:1 на своей плашке */
    --app-off-bg: #e4e6e9;
    --app-off-fg: #3f444b; /* 7.85:1 на своей плашке */

    --bs-body-color: #000000; /* 21:1 */
    --bs-body-color-rgb: 0, 0, 0;
    --bs-emphasis-color: #000000;
    --bs-secondary-color: #3f444b; /* 9.81:1 — было 4.46:1 */
    --bs-secondary-color-rgb: 63, 68, 75;
    --bs-tertiary-color: #4d525a; /* 7.86:1 */

    --bs-border-color: #767c85; /* 4.21:1 — было 1.2:1, границы не было видно */
    --bs-border-color-translucent: rgba(0, 0, 0, 0.45);

    --bs-link-color-rgb: 106, 80, 23;
    --bs-link-hover-color-rgb: 77, 58, 15;

    /* Клавиатурная навигация не должна угадываться */
    --bs-focus-ring-color: rgba(185, 155, 98, 0.65);
}

[data-contrast='high'][data-bs-theme='dark'] {
    --app-accent-text: #e0c58d; /* 11.39:1 на карточке */
    --app-accent-text-hover: #efdcb6; /* 14.15:1 */

    --app-side-bg: #000000;

    /* Латунь под чёрным текстом даёт 7.93:1, под прежним #14171b было 6.79 */
    --app-btn-fg: #000000;

    --app-th-fg: #c2c8d0; /* 11.32:1 */
    --app-filter-bg: #1a1f25;
    --app-ok-bg: #0f2a19;
    --app-ok-fg: #a8d9b8; /* 9.73:1 на своей плашке */
    --app-off-bg: #2a2f36;
    --app-off-fg: #c2c8d0; /* 8.00:1 на своей плашке */

    /* Поверхность карточки и фон окна расходятся сильнее: окно уходит
       в чистый чёрный, карточка остаётся почти чёрной */
    --bs-body-bg: #0d1014;
    --bs-body-bg-rgb: 13, 16, 20;
    --bs-secondary-bg: #000000;
    --bs-secondary-bg-rgb: 0, 0, 0;
    --bs-tertiary-bg: #1a1f25;
    --bs-tertiary-bg-rgb: 26, 31, 37;

    --bs-body-color: #ffffff; /* 19.07:1 на карточке */
    --bs-body-color-rgb: 255, 255, 255;
    --bs-emphasis-color: #ffffff;
    --bs-secondary-color: #c2c8d0; /* 11.32:1 — было 5.9:1 */
    --bs-secondary-color-rgb: 194, 200, 208;
    --bs-tertiary-color: #a3aab3; /* 8.13:1 */

    --bs-border-color: #6d757e; /* 4.08:1 */
    --bs-border-color-translucent: rgba(255, 255, 255, 0.5);

    --bs-link-color-rgb: 224, 197, 141;
    --bs-link-hover-color-rgb: 239, 220, 182;
}

/* Кольцо фокуса шире: 0.2rem по краю контрола заметно не всем. Ширина
   задаётся здесь же, где и в обычном режиме, — правила фокуса написаны
   руками через box-shadow и переменной `--bs-focus-ring-width` не читают */
[data-contrast='high'] .form-control:focus,
[data-contrast='high'] .form-select:focus,
[data-contrast='high'] .form-check-input:focus {
    box-shadow: 0 0 0 0.3rem var(--bs-focus-ring-color);
}

/* Содержательные ссылки подчёркиваются всегда: цвет не должен быть
   единственным признаком ссылки. Блочные плитки (.app-metric,
   .app-task-link) — исключение: это карточки, а не текст в строке */
[data-contrast='high'] .app-content a:not(.btn):not(.nav-link):not(.app-metric):not(.app-task-link) {
    text-decoration: underline;
}

/* ── Типографика ────────────────────────────────────────────────── */

body {
    min-height: 100vh;
    font-family: var(--app-font-sans);
    background-color: var(--bs-secondary-bg);
    -webkit-font-smoothing: antialiased;
}

/* Montserrat берёт на себя только заголовки страниц и название системы:
   в тексте и таблицах она читается хуже гуманистической гарнитуры */
.app-page-header h1,
.app-brand,
.app-display {
    font-family: var(--app-font-display);
}

/* ── Иконки ─────────────────────────────────────────────────────── */

.app-icon {
    width: 1em;
    height: 1em;
    flex-shrink: 0;
    /* Штриховая иконка рядом с текстом всегда чуть «всплывает» — сдвиг
       возвращает её на оптическую середину строки */
    vertical-align: -0.135em;
}

/* Ссылка, открывающая соседнюю вкладку, помечается стрелкой — иначе она
   ничем не отличается от кнопки, которая достраивает текущую форму.
   Правило висит на самом `target="_blank"`, а не на классе: пометку нельзя
   забыть проставить, она появляется вместе с открытием новой вкладки.
   Исключения — ссылки-картинки и ссылки, свёрстанные кнопками: там стрелка
   встала бы поверх содержимого.
   Стрелка нарисована маской, а не спрайтом: ::after не умеет <use>, а цвет
   маски наследуется от текста ссылки в обеих темах */
a[target='_blank']:not(.btn):not(.app-gallery-item)::after {
    content: '';
    display: inline-block;
    width: 0.8em;
    height: 0.8em;
    margin-left: 0.25em;
    vertical-align: -0.02em;
    background-color: currentColor;
    -webkit-mask: var(--app-icon-external) center / contain no-repeat;
    mask: var(--app-icon-external) center / contain no-repeat;
}

/* ── Оболочка ───────────────────────────────────────────────────── */

.app-main {
    min-width: 0;
}

/* ── Сайдбар ────────────────────────────────────────────────────── */

.app-sidebar {
    width: var(--app-sidebar-width);
    padding: 22px 16px;
    /* `!important` тут вынужденный: выше breakpoint'а Bootstrap гасит фон
       `.offcanvas-lg` своим `background-color: transparent !important`, и без
       этого панель на десктопе оставалась бы светлой */
    background-color: var(--app-side-bg) !important;
    border-right: 0;
    /* На узком экране это off-canvas Bootstrap — ему нужен свой фон,
       иначе панель выедет светлой */
    --bs-offcanvas-bg: var(--app-side-bg);
    --bs-offcanvas-color: var(--app-side-fg);
    --bs-offcanvas-width: var(--app-sidebar-width);
    --bs-offcanvas-border-width: 0;
}

/* На десктопе сайдбар — колонка во всю высоту окна, off-canvas
   отключается (11Д.3) */
@media (min-width: 992px) {
    .app-sidebar.offcanvas-lg {
        position: sticky;
        top: 0;
        height: 100vh;
        overflow-y: auto;
        visibility: visible !important;
        transform: none !important;
    }
}

.app-sidebar-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 24px;
}

.app-brand {
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 4px 8px 0;
    font-size: 15px;
    font-weight: 600;
    letter-spacing: 0.055em;
    color: var(--app-side-brand);
    text-decoration: none;
}

.app-brand:hover {
    color: var(--app-side-brand);
}

.app-sidebar .nav {
    gap: 2px;
}

.app-sidebar .nav-link {
    position: relative;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 9px 12px;
    font-size: 14.5px;
    font-weight: 400;
    color: var(--app-side-fg);
    border-radius: var(--app-radius-sm);
}

.app-sidebar .nav-link .app-icon {
    width: 17px;
    height: 17px;
    color: var(--app-side-icon);
    vertical-align: 0;
}

.app-sidebar .nav-link:hover,
.app-sidebar .nav-link:focus-visible {
    color: var(--app-side-brand);
    background-color: var(--app-side-hover);
}

.app-sidebar .nav-link.active {
    background-color: var(--app-side-active-bg);
    color: var(--app-side-active-fg);
    font-weight: 600;
}

.app-sidebar .nav-link.active .app-icon {
    color: var(--app-side-active-fg);
}

/* Латунный рельс слева — единственное место, где акцент обозначает
   состояние. Подложки одной было бы мало: она почти не видна */
.app-sidebar .nav-link.active::before {
    content: '';
    position: absolute;
    left: 0;
    top: 6px;
    bottom: 6px;
    width: 2px;
    border-radius: 2px;
    background-color: var(--app-accent);
}

/* Подпункты аккордеона (второй уровень навигации, 11Д.1) */
.app-sidebar .nav-sub .nav-link {
    padding-left: 42px;
    font-size: 14px;
    color: var(--app-side-muted);
}

.app-sidebar .nav-sub .nav-link.active {
    color: var(--app-side-active-fg);
}

.app-sidebar hr {
    border-color: rgba(255, 255, 255, 0.08);
    opacity: 1;
}

.app-sidebar .accordion-caret {
    width: 14px;
    height: 14px;
    margin-left: auto;
    color: var(--app-side-muted);
    transition: transform 0.15s ease-in-out;
}

.app-sidebar [aria-expanded='true'] .accordion-caret {
    transform: rotate(90deg);
}

.app-sidebar .btn-close {
    filter: var(--bs-btn-close-white-filter, invert(1) grayscale(100%) brightness(200%));
}

/* ── Top-плашка ─────────────────────────────────────────────────── */

.app-topbar {
    height: var(--app-topbar-height);
    padding: 0 var(--app-content-px);
    background-color: var(--bs-body-bg);
    border-bottom: 1px solid var(--bs-border-color);
    position: sticky;
    top: 0;
    z-index: 1020;
}

/* Кнопки плашки — одна высота и одна геометрия, включая те, что открывают
   дропдаун: разнобой в 2px в ряду из четырёх контролов очень заметен */
.app-topbar .btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    height: 32px;
    padding: 0 11px;
    font-size: 14.5px;
}

.app-topbar .btn-icon {
    width: 32px;
    padding: 0;
    justify-content: center;
}

.app-topbar .btn .app-icon {
    width: 16px;
    height: 16px;
    vertical-align: 0;
}

/* Иконка в обёртке (переключатель темы прячет неактивную через `d-none`)
   иначе остаётся инлайновой и садится на базовую линию — а она выше середины
   строки, и иконка уезжает вверх на треть высоты. Обёртка тоже должна быть
   flex-контейнером, тогда центрируется она сама, а не её базовая линия */
.app-topbar .btn > span {
    display: inline-flex;
    align-items: center;
}

.app-topbar .btn .app-icon.caret {
    width: 12px;
    height: 12px;
    opacity: 0.65;
}

/* Стрелка у дропдауна нарисована иконкой — треугольник Bootstrap лишний */
.app-topbar .dropdown-toggle::after {
    display: none;
}

/* ── Контент ────────────────────────────────────────────────────── */

.app-content {
    min-width: 0; /* иначе широкие таблицы растянут grid-колонку */
    max-width: 1600px;
    padding: var(--app-content-py) var(--app-content-px);
}

/* Индикатор загрузки Turbo Frame */
turbo-frame[busy] {
    opacity: 0.6;
    transition: opacity 0.15s ease-in-out;
}

/* Ссылки в таблицах и карточках без подчёркивания: строк много, и рябь
   подчёркиваний утомляет */
.app-content a:not(.btn) {
    text-decoration: none;
}

.app-content a:not(.btn):hover {
    text-decoration: underline;
}

/* ── Заголовок страницы ─────────────────────────────────────────── */

.app-page-header {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
    margin-bottom: 22px;
}

.app-page-header h1 {
    margin: 0;
    font-size: 21px;
    font-weight: 600;
    letter-spacing: 0.005em;
    line-height: 1.25;
}

.app-page-subtitle {
    margin: 4px 0 0;
    font-size: 14.5px;
    color: var(--bs-secondary-color);
}

/* ── Карточка — основной контейнер контента ─────────────────────── */

.app-card {
    background-color: var(--bs-body-bg);
    border: 1px solid var(--bs-border-color);
    border-radius: var(--app-radius);
    box-shadow: var(--app-shadow);
    /* Радиус карточки обрезает таблицу и панель фильтров: у них своих
       скруглений нет, и без этого углы вылезали бы поверх рамки */
    overflow: hidden;
}

.app-card + .app-card {
    margin-top: 1rem;
}

.app-card-header {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    padding: 14px 20px;
    border-bottom: 1px solid var(--bs-border-color);
}

.app-card-header h2 {
    margin: 0;
    font-size: 15px;
    font-weight: 600;
}

.app-card-body {
    padding: 20px;
}

.app-card-body > :last-child {
    margin-bottom: 0;
}

/* ── Таблицы ────────────────────────────────────────────────────── */

/* Таблица занимает карточку целиком — рамка карточки и есть её рамка */
.app-card > .table-responsive > .table,
.app-card > .table {
    margin-bottom: 0;
}

.app-card .table {
    --bs-table-bg: transparent;
    --bs-table-color: var(--bs-body-color);
    --bs-table-border-color: var(--bs-border-color);
    --bs-table-hover-bg: var(--bs-tertiary-bg);
    --bs-table-hover-color: var(--bs-body-color);
}

.app-card .table > thead > tr > th {
    padding: 11px 20px;
    background-color: transparent;
    border-bottom: 1px solid var(--bs-border-color);
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.085em;
    color: var(--app-th-fg);
}

.app-card .table > tbody > tr > td {
    padding: 13px 20px;
    font-size: 14.5px;
    vertical-align: middle;
}

/* Последняя строка не рисует линию: её роль играет край карточки */
.app-card .table > tbody > tr:last-child > td {
    border-bottom-width: 0;
}

/* ── Аватар и индикатор доступа ─────────────────────────────────── */

.app-avatar {
    width: var(--avatar-size, 32px);
    height: var(--avatar-size, 32px);
    border-radius: 50%;
    /* Круг режет CSS, а не GD: картинка на диске остаётся целой */
    object-fit: cover;
    flex-shrink: 0;
}

.app-avatar-initials {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background-color: var(--bs-secondary-bg);
    color: var(--bs-secondary-color);
    font-size: 0.75rem;
    text-transform: uppercase;
}

.app-dot {
    display: inline-block;
    width: 0.5rem;
    height: 0.5rem;
    border-radius: 50%;
}

.app-dot.is-on { background-color: var(--bs-success); }
.app-dot.is-off { background-color: var(--bs-secondary-color); }

/* ── Плитка сотрудников ─────────────────────────────────────────── */

/* auto-fill сам уменьшает число колонок на узком экране вплоть до одной —
   отдельная точка перелома на телефон не нужна, плитка просто не переносится
   за край */
.employee-grid {
    display: grid;
    /* min(15rem, 100%): жёсткий минимум трека не даёт колонке сжаться на
       очень узком экране или при увеличенном шрифте браузера — .app-card у
       тайла режет всё, что не помещается (overflow: hidden), и без min()
       обрезка была бы молчаливой */
    grid-template-columns: repeat(auto-fill, minmax(min(15rem, 100%), 1fr));
    gap: 0.75rem;
    padding: 20px;
}

/* Карточка внутри карточки: `.app-card + .app-card { margin-top: 1rem }`
   задумана для панелей друг под другом на странице и здесь распирает верхний
   ряд плитки вниз у всех колонок, кроме первой, при переносе на несколько
   строк (тот же класс пороков специфичности, что и в волне А, только со
   стороны общего правила, а не нового) */
.employee-grid > .app-card + .app-card {
    margin-top: 0;
}

/* ── Панель фильтров ────────────────────────────────────────────── */

/* Визуально спокойнее таблицы, но заметнее карточки */
.app-filters {
    padding: 12px 20px;
    background-color: var(--app-filter-bg);
    border-bottom: 1px solid var(--bs-border-color);
}

.app-filters .form-control,
.app-filters .form-select,
.app-filters .btn {
    height: 32px;
    font-size: 14.5px;
}

/* Поле поиска с иконкой внутри: отдельного input-group не заводим — он
   ломает высоту ряда и добавляет вторую рамку */
.app-search {
    position: relative;
}

.app-search .app-icon {
    position: absolute;
    left: 10px;
    top: 50%;
    width: 14px;
    height: 14px;
    margin-top: -7px;
    color: var(--bs-tertiary-color);
    pointer-events: none;
}

.app-search .form-control {
    padding-left: 31px;
}

/* ── Формы ──────────────────────────────────────────────────────── */

.app-form-panel {
    border-top: 1px solid var(--bs-border-color);
    padding: 20px;
    background-color: var(--app-filter-bg);
}

/* Звёздочка у обязательных полей. Класс `required` на подписи ставит сам
   form_label — метка появляется у каждого нового обязательного поля без
   правки шаблонов и не разъезжается с реальными констрейнтами.
   Символ рисуется в ::after, а не в подписи: в каталог переводов он бы попал
   частью текста и терялся при переводе */
.form-label.required::after,
.form-check-label.required::after,
.app-form-legend.required::after {
    content: ' *';
    color: var(--bs-danger);
}

/* Фокус в Bootstrap скомпилирован синим — цвет фокуса переопределяем
   вручную, к переменным он не сведён */
.form-control:focus,
.form-select:focus {
    border-color: var(--app-accent);
    box-shadow: 0 0 0 0.2rem var(--bs-focus-ring-color);
}

.form-check-input:checked {
    background-color: var(--app-accent);
    border-color: var(--app-accent);
}

.form-check-input:focus {
    border-color: var(--app-accent);
    box-shadow: 0 0 0 0.2rem var(--bs-focus-ring-color);
}

/* Ветка полей внутри формы: партнёрская трата, компенсация, реквизиты.
   Рамка нужна, чтобы длинная форма читалась блоками, а не сплошным полем */
.app-form-branch {
    border: 1px solid var(--bs-border-color);
    border-radius: var(--bs-border-radius);
    padding: 12px 16px 16px;
}

.app-form-legend {
    float: none;
    width: auto;
    padding: 0 6px;
    margin-bottom: 0;
    font-size: 0.8125rem;
    font-weight: 600;
    color: var(--bs-secondary-color);
    text-transform: uppercase;
    letter-spacing: 0.02em;
}

/* Форма создания — одна сетка полей без рамок-блоков (п. 34.1 замечаний
   финансиста 25.09.2026; вид — вариант A холста, выбран владельцем
   29.09.2026, спека 2026-09-29 §1). Поле — ячейка с подписью над ним,
   ширина — классом ячейки. Рамки `.app-form-branch` остаются там, где
   форма ещё блочная (`profile/recurring`) */
.app-form-grid {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 16px;
    align-items: start;
}

/* Строки из пяти полей (шапка траты, обмен конвертации) — вложенная сетка
   во всю ширину основной */
.app-form-grid-5 {
    grid-template-columns: repeat(5, minmax(0, 1fr));
}

.app-form-grid > .app-form-grid,
.app-span-4,
.app-span-full {
    grid-column: 1 / -1;
}

.app-span-2 {
    grid-column: span 2;
}

/* Поле, которое обязано начать строку: после ячейки шириной в две колонки и
   одной обычной четвёртая колонка пуста, и автоматическая раскладка
   втянула бы туда следующее поле (статья партнёрской заявки, §2) */
.app-row-start {
    grid-column-start: 1;
}

/* Обёртка ячейки со своим контроллером (реквизит, Affiliate ID, подстатья):
   контроллеру нужен общий предок выбора и панели, а сетке — отдельные
   элементы. Сама обёртка коробки не рисует */
.app-cell-contents {
    display: contents;
}

/* Промежутки задаёт сетка: нижний отступ `form_row` из темы bootstrap
   удваивал бы их */
.app-form-grid .mb-3 {
    margin-bottom: 0;
}

/* Галка в ряду полей (командировка) — по низу полей, а не по подписям */
.app-cell-bottom {
    align-self: end;
    padding-bottom: 7px;
}

/* Список и кнопка «добавить» вплотную (реквизит, Affiliate ID) и список с
   галкой «Нужного нет» в строку (юр. лицо) */
.app-select-with-check {
    display: flex;
    align-items: center;
    gap: 12px;
}

.app-select-with-check > .form-select {
    flex: 1 1 auto;
    min-width: 0;
}

.app-select-with-check .form-check {
    margin-bottom: 0;
    white-space: nowrap;
}

/* Озаглавленный участок формы — только чеки (п. 35): заголовок тем же
   приглушённым капсом, что прежние legend, но без рамки вокруг */
.app-form-section-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 16px;
    margin-bottom: 4px;
}

.app-form-section-title {
    margin: 0;
    font-size: 0.8125rem;
    font-weight: 600;
    color: var(--bs-secondary-color);
    text-transform: uppercase;
    letter-spacing: 0.02em;
}

.app-form-section-title.required::after {
    content: ' *';
    color: var(--bs-danger);
}

/* Страж красит пустую таблицу чеков целиком (`form_guard#checkRows`):
   рамки у секции нет, поэтому красное — контуром */
.app-form-section.border-danger {
    outline: 1px solid var(--bs-danger);
    outline-offset: 6px;
    border-radius: var(--bs-border-radius);
}

/* Под сеткой — линия и кнопки (§1) */
.app-form-actions {
    display: flex;
    gap: 8px;
    margin-top: 16px;
    padding-top: 16px;
    border-top: 1px solid var(--bs-border-color);
}

/* Под таблицей чеков: «+ Добавить чек» слева, итог справа (§3.3) */
.app-lines-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-top: 8px;
}

.app-lines-total {
    font-size: 0.875rem;
    font-weight: 600;
    text-align: right;
}

.app-lines-total-sum + .app-lines-total-sum::before {
    content: ' · ';
}

/* Итог над карточками — только на телефоне, под таблицей — на остальных
   (§3.3). Оба в разметке: переставить один элемент между шапкой секции и
   подвалом нельзя без JS, а у спрятанного `aria-live` диктор молчит */
.app-lines-total-top {
    display: none;
}

@media (max-width: 767.98px) {
    .app-lines-total-top {
        display: block;
        text-align: left;
    }

    .app-lines-total-foot {
        display: none;
    }
}

/* 768–1199: две колонки; ячейка в две колонки — во всю ширину */
@media (max-width: 1199.98px) {
    .app-form-grid,
    .app-form-grid-5 {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

/* Телефон: одна колонка, кроме оговорённых холстом пар (сумма и валюта,
   шапка траты — §0 спеки). Сетка остаётся в две колонки, а каждая ячейка
   занимает обе, если она не `app-phone-half`: так пара встаёт в ряд без
   второй сетки. Поля и кнопки — 44px и шрифт 16px: мельче iOS увеличивает
   страницу при фокусе */
@media (max-width: 767.98px) {
    .app-form-grid > *,
    .app-form-grid > .app-cell-contents > * {
        grid-column: 1 / -1;
    }

    .app-form-grid > .app-phone-half,
    .app-form-grid > .app-cell-contents > .app-phone-half {
        grid-column: auto;
    }

    .app-form-grid .form-control,
    .app-form-grid .form-select,
    .app-form-actions .btn {
        min-height: 44px;
        font-size: 16px;
    }

    .app-form-actions {
        flex-direction: column;
    }

    .app-form-actions .btn-primary {
        width: 100%;
    }

    /* «Отмена» — ссылкой под кнопкой отправки (§0) */
    .app-form-actions .app-form-cancel {
        border: 0;
        background: none;
        color: var(--app-accent-text);
        text-decoration: underline;
    }

    /* «+ Добавить реквизит» — ссылкой под списком, как на холсте (§0) */
    .app-form-grid .app-inline-add-group {
        flex-wrap: wrap;
    }

    .app-form-grid .app-inline-add-group > .form-select {
        width: 100%;
        border-radius: var(--bs-border-radius) !important;
    }

    .app-form-grid .app-inline-add-group > .app-inline-add {
        width: 100%;
        margin-left: 0 !important;
        padding-left: 0;
        border: 0;
        background: none;
        color: var(--app-accent-text);
        text-align: left;
    }
}

/* Приветственное окно заявки: выбор типа плитками (макет image2) */
.app-choice {
    color: inherit;
    transition: border-color 0.15s ease, transform 0.15s ease;
}

.app-choice:hover {
    border-color: var(--app-accent);
    transform: translateY(-1px);
}

.app-choice-title {
    font-weight: 600;
}

/* Шаги согласования в карточке заявки */
.app-steps {
    padding-left: 18px;
    margin-bottom: 0;
}

.app-steps li + li {
    margin-top: 6px;
}

/* Зона перетаскивания файлов в форме заявки */
.app-dropzone {
    border: 1px dashed var(--bs-border-color);
    border-radius: var(--bs-border-radius);
    padding: 18px;
    text-align: center;
    cursor: pointer;
    transition: border-color 0.15s ease, background-color 0.15s ease;
}

.app-dropzone.is-dragover {
    border-color: var(--app-accent);
    background-color: var(--app-filter-bg);
}

/* «Документы» одной строкой (§5): иконка, текст, мелкая вторая строка */
.app-dropzone-compact {
    padding: 10px 14px;
    text-align: left;
}

.app-dropzone-desktop {
    display: flex;
    align-items: center;
    gap: 12px;
}

.app-dropzone-icon {
    flex: none;
    width: 22px;
    height: 22px;
    color: var(--bs-secondary-color);
}

.app-dropzone-mobile {
    display: none;
}

/* Список под зоной — вне её рамки (п. 40 замечаний финансиста 30.09.2026) */
.app-dropzone-list:not(:empty),
.app-dropzone-saved {
    margin-top: 6px;
}

/* Выбранные, но не отправленные файлы — по одному в строке: имя, размер,
   «Убрать». Длинное имя обрезается, кнопка остаётся видна */
.app-picked-files li {
    display: flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
}

.app-picked-name {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* В меню кнопки «Файлы: N» строки — с отступом пунктов меню */
.app-files-button-menu .app-picked-files li {
    padding: 4px 16px;
}

/* Телефон: зона — кнопка во всю ширину (§5) */
@media (max-width: 767.98px) {
    .app-dropzone-compact {
        padding: 0;
        border: 0;
    }

    .app-dropzone-compact .app-dropzone-desktop {
        display: none;
    }

    .app-dropzone-compact .app-dropzone-mobile {
        display: flex;
        align-items: center;
        justify-content: center;
        gap: 6px;
        width: 100%;
        min-height: 44px;
        font-size: 16px;
    }
}

.app-files,
.app-thread {
    list-style: none;
    padding-left: 0;
    margin-bottom: 0;
}

/* Галерея вложений: чек глазами узнают быстрее, чем по имени файла.
   Миниатюра — та же картинка, ужатая рамкой: сервер их не режет, потому что
   обработчика изображений в образе нет, а чеки весят килобайты. Появятся
   мегабайтные фото — здесь добавится настоящий превью-файл, разметка не
   изменится */
.app-gallery {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
}

.app-gallery-item {
    display: block;
    width: 120px;
    height: 120px;
    border: 1px solid var(--bs-border-color);
    border-radius: var(--bs-border-radius);
    overflow: hidden;
    background-color: var(--bs-tertiary-bg);
}

.app-gallery-item:hover {
    border-color: var(--app-accent);
}

.app-gallery-item img {
    width: 100%;
    height: 100%;
    /* Обрезка, а не растяжение: пропорции чека важнее заполненной плитки */
    object-fit: cover;
    display: block;
}

.app-files li + li,
.app-thread-item + .app-thread-item {
    margin-top: 10px;
}

/* Плотные сведения и документы на карточке заявки (замечания финансиста
   09.10.2026, А.1–А.2): на обработке экран занят заказами */
.app-summary-compact dl {
    font-size: 0.875rem;
    --bs-gutter-y: 0.125rem;
}

.app-summary-compact dt,
.app-summary-compact dd {
    margin-bottom: 0.125rem;
}

.app-gallery-compact {
    gap: 6px;
}

.app-gallery-compact .app-gallery-item {
    width: 72px;
    height: 72px;
}

.app-files-inline {
    display: flex;
    flex-wrap: wrap;
    gap: 2px 16px;
}

/* Стоит после общего `.app-files li + li`: вес селекторов равный, и отступ
   между пунктами обнуляет только правило, идущее ниже */
.app-files-inline li + li {
    margin-top: 0;
}

/* Внутренняя заметка видна только финконтролю — и должна отличаться взглядом,
   а не только подписью: перепутать её с сообщением автору дорого */
.app-thread-item {
    border-left: 2px solid var(--bs-border-color);
    padding-left: 10px;
}

.app-thread-internal {
    border-left-color: var(--bs-warning);
}

/* Непрочитанное уведомление — свой знак, а не «внутренняя заметка»: жёлтая
   полоса у ленты уведомлений означала бы то же, что у комментариев, то есть
   «видно только финконтролю», а это неправда */
.app-thread-unread {
    border-left-color: var(--bs-primary);
    font-weight: 500;
}

.app-thread-system {
    border-left-style: dashed;
}

.app-thread-meta {
    margin-bottom: 2px;
}

/* Панель быстрого ввода внутри формы */
.app-inline-panel {
    border: 1px solid var(--bs-border-color);
    border-radius: var(--bs-border-radius);
    background-color: var(--app-filter-bg);
    padding: 12px;
}

/* Строка табличного ввода: «группа чеков» вводится подряд, поэтому строки
   должны читаться как строки, а не как отдельные формы */
.app-collection-row {
    border-bottom: 1px solid var(--bs-border-color);
    padding-bottom: 8px;
    margin-bottom: 8px;
}

.app-collection-row:last-child {
    border-bottom: 0;
}

/* Карточка расхода: поля идут в ряд — так строка читается как строка
   таблицы, а не как отдельная форма (`.app-lines` в `expense/show.html.twig` —
   чеки расхода и списания без заказа на этой же карточке).

   Разнесение заявки по статьям этим приёмом раньше тоже пользовалось, но
   теперь заказы идут стопкой карточек (`.app-order-stack`, `.app-order-card`,
   задача 22 волны «Документы обязательны; финконтроль уходит в заказы и
   списания», §6.1) со своей раскладкой — класс `.app-lines` там больше не
   стоит.

   ⚠️ Сетка вешается на **вложенный** div, а не на саму `.app-collection-row`:
   `form_widget()` составной формы заворачивает поля строки в один контейнер, и
   на строке грид получил бы ровно один элемент — отчего поля и вставали
   столбиком. Сама строка при этом flex: рядом с полями живёт кнопка удаления.

   Чеки компенсации этим приёмом больше не пользуются: там настоящая `<table>`
   с `<thead>`, и подпись столбца стоит один раз, а не в каждой строке. */
.app-lines .app-collection-row {
    display: flex;
    align-items: flex-end;
    gap: 12px;
}

.app-lines .app-collection-row > div:first-child {
    flex: 1 1 auto;
    min-width: 0;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: 0 12px;
    align-items: end;
}

/* Период строки чека у финконтроля — одно поле на месте двух: две колонки */
.app-lines .app-collection-row > div:first-child > .app-period-range {
    grid-column: span 2;
}

/* Файлы строки чека: во всю ширину строки, а не одной колонкой сетки.
   `minmax(150px, 1fr)` дал бы дропзоне колонку в полтора сантиметра, в которую
   не попасть ни перетаскиванием, ни кликом. В формах без сетки (заведение и
   правка расхода автором) правило ничего не меняет: `grid-column` действует
   только внутри грида. */
.app-lines .app-line-files,
.app-line-files {
    grid-column: 1 / -1;
}

/* Дропзона внутри строки — тише и ниже общей: она повторяется на каждом чеке,
   и восемнадцать пикселей отступа превращали таблицу из десяти строк в
   бесконечную ленту */
.app-dropzone-inline {
    padding: 8px;
}

.app-collection-remove {
    padding-left: 0;
    padding-right: 0;
    white-space: nowrap;
}

/* Таблица чеков компенсации. Поля внутри ячеек не носят собственных отступов:
   расстояние между строками задаёт сама таблица, а `mb-3` из темы формы
   раздувал бы строку вдвое */
.app-receipts td .mb-3,
.app-receipts td .form-check {
    margin-bottom: 0;
}

/* Таблицы чеков автора — трата и компенсация (п. 37 замечаний финансиста
   25.09.2026, §3.1 спеки 2026-09-29). Ширины — у заголовков, раскладка
   `fixed`: при `auto` браузер делил ширину по содержимому, и наименованию
   доставалось столько же, сколько коду валюты. Наименование забирает
   остаток, но не меньше 220px — отсюда `min-width` всей таблицы: уже
   этого `.table-responsive` прокручивает её, а не сжимает поля */
.app-card .table.app-line-table {
    table-layout: fixed;
}

.app-card .table.app-line-table > thead > tr > th,
.app-card .table.app-line-table > tbody > tr > td {
    padding: 8px 6px;
}

.app-card .table.app-line-table > thead > tr > th:first-child,
.app-card .table.app-line-table > tbody > tr > td:first-child {
    padding-left: 0;
}

.app-line-table .app-col-date { width: 138px; }
.app-line-table .app-col-amount { width: 120px; }
.app-line-table .app-col-currency { width: 92px; }
.app-line-table .app-col-period { width: 236px; }
.app-line-table .app-col-files { width: 112px; }
.app-line-table .app-col-trip { width: 120px; }
.app-line-table .app-col-remove { width: 32px; }

/* Код валюты обязан читаться целиком («USDT», а не «US…» — эту поломку
   уже фиксировали): у `.form-select` отступы 0.75rem слева и 2.25rem справа
   под стрелку, на текст в столбце 86px оставалось ~26px. Отступы сужены до
   0.5rem и 1.5rem, стрелка сдвинута к краю. Счёт для 92px: 92 - 12 (отступы
   ячейки) - 2 (рамка) - 32 (отступы поля) = 46px под текст; «USDT» в 15px
   ≈ 41px. Столбец шире на 6px против §3.1 (86px) — при 86px запас был бы
   лишь 40px, впритык */
.app-line-table .app-col-currency .form-select {
    padding-left: 0.5rem;
    padding-right: 1.5rem;
    background-position: right 0.4rem center;
    background-size: 16px 12px;
}

/* Ячейка периода при `fixed` не должна распирать столбец: её собственные
   236px совпадают с шириной столбца, а с отступами ячейки выступали на 12px */
.app-line-table .app-period-range-cell {
    min-width: 0;
}

/* 138 + 120 + 92 + 220 + 236 + 112 + 32 */
.app-card .table.app-expense-lines {
    min-width: 950px;
}

/* 138 + 92 + 120 + 112 + 220 + 120 + 32 */
.app-card .table.app-receipts {
    min-width: 834px;
}

/* Кнопка снятия строки в столбце 32px — крестиком. Текст кнопки («Удалить
   чек») остаётся её именем для диктора: `font-size: 0` прячет его от глаз,
   но не из дерева доступности. На карточке (< 992) — снова текстом */
.app-line-table .app-collection-remove {
    font-size: 0;
    line-height: 1;
}

.app-line-table .app-collection-remove::before {
    content: '\00d7';
    font-size: 1.25rem;
}

/* Кнопка файлов строки (§3.2): «Файлы: N» и стрелка списка в столбце 112px.
   Не `.app-line-files` — это класс блока файлов карточки расхода */
/* Счёт для «Файлы: 12» в 12px, худший случай (двузначное N): текст ≈ 55px,
   скрепка 12 + зазор 3, отступы 2 x 4 + рамки 2 = 80px у кнопки; стрелка —
   отступы 2 x 5 + значок ≈ 8 + рамки 2 = 20px; кнопки в группе перекрываются
   рамкой на 1px, итого ≈ 99px. В 104px столбца (92px под содержимое после
   отступов ячейки 12) это не влезало — столбец расширен до 112px (100px под
   содержимое), это на 8px шире §3.1 спеки (104px); min-width таблиц выросли
   на те же 8px (950 у траты, 834 у компенсации) */
.app-files-button .btn {
    padding: 2px 4px;
    font-size: 0.75rem;
}

.app-files-button .dropdown-toggle-split {
    padding-left: 5px;
    padding-right: 5px;
}

.app-files-button-pick {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    white-space: nowrap;
}

.app-files-button-pick .app-icon {
    width: 12px;
    height: 12px;
}

.app-files-button-menu {
    min-width: 18rem;
    max-width: 22rem;
}

/* Окно просмотра файла и миниатюры в списках (п. 46 замечаний финансиста
   01.10.2026) */
.app-file-viewer-body {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 0.5rem;
}

/* Документ в окне (спека 2026-10-28): столбец страниц PDF или фрейм — во всю
   ширину тела; прокручивается тело окна (`modal-dialog-scrollable`) */
.app-file-viewer-document {
    width: 100%;
}

/* Страница PDF — заглушка нужной высоты (`aspect-ratio` из размеров
   страницы), canvas внутри появляется, когда страница рядом с видимой */
.app-pdf-page {
    width: 100%;
    max-width: 100%;
    margin: 0 auto 0.75rem;
    background: #fff;
    box-shadow: var(--bs-box-shadow-sm);
}

.app-pdf-page:last-child {
    margin-bottom: 0;
}

.app-pdf-page canvas {
    display: block;
    width: 100%;
    height: 100%;
}

/* Фрейм docx и таблиц (спека 2026-10-28, §«Фрейм»): во всю ширину, не ниже
   70vh; высоту по содержимому ставит `assets/viewer/frame.js`, прокручивается
   тело окна. Белый — бумага, и в тёмной теме */
.app-file-viewer-frame {
    display: block;
    width: 100%;
    min-height: 70vh;
    border: 0;
    background: #fff;
}

/* Вкладки листов над таблицей: имён может быть больше, чем влезает, —
   прокрутка вбок, а не перенос в несколько рядов */
.app-file-viewer-sheets {
    flex-wrap: nowrap;
    overflow-x: auto;
    overflow-y: hidden;
    margin-bottom: 0.5rem;
}

.app-file-viewer-sheets .nav-link {
    white-space: nowrap;
}

.app-file-viewer-note {
    margin: 0.5rem 0 0;
}

.app-file-viewer-body img {
    max-width: 100%;
    max-height: 80vh;
    object-fit: contain;
}

.app-file-thumb {
    flex: 0 0 40px;
    width: 40px;
    height: 40px;
    border: 1px solid var(--bs-border-color);
    border-radius: var(--bs-border-radius-sm);
    object-fit: cover;
    padding: 0;
    background: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
}

.app-saved-image {
    padding: 4px 16px;
}

.app-saved-image .dropdown-item {
    width: auto;
    padding: 0;
    background: none;
}

.app-file-thumb > img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* Перетаскивание на строку подсвечивает кнопку — зону строки */
.app-files-button.is-dragover .btn {
    border-color: var(--app-accent);
    background-color: var(--app-filter-bg);
}

/* Файл над страницей — видны все места, куда его можно бросить (п. 46
   замечаний финансиста 01.10.2026) */
.app-files-dragging .app-files-button,
.app-files-dragging .app-dropzone {
    outline: 2px dashed var(--app-accent);
    outline-offset: 2px;
    border-radius: var(--bs-border-radius);
    background-color: var(--app-side-active-bg);
}

/* Строка под курсором — целиком: видно, в какой чек ляжет файл. Фон на самой
   строке, а не на ячейках: ячейки таблицы в карточке прозрачны, а в карточном
   виде (< 992) строка — сетка, и ячейки красили бы лоскутами. Подложка —
   `--app-side-active-bg`: `--app-filter-bg` почти неотличим от фона карточки */
tr.is-dragover {
    background-color: var(--app-side-active-bg);
}

/* Скрин из буфера лёг в эту строку */
.app-files-button.is-landed {
    outline: 2px solid var(--app-accent);
    outline-offset: 2px;
}

/* Страж (правило 7): у кнопки своей рамки-обёртки нет, красится она сама */
.app-files-button.border-danger .btn {
    border-color: var(--bs-danger);
}

/* Экран < 992 — строки карточками (§3.1): с сайдбаром наименованию в строке
   таблицы не хватает места. Строка остаётся `<tr>` той же темы — меняется
   только отображение: `collection`, заготовка и имена полей те же.
   Подпись поля — из `data-label` ячейки, заголовок «Чек N» — из
   `data-card-title` строки и CSS-счётчика: строки добавляются и снимаются
   в браузере, номер в разметку не записать */
@media (max-width: 991.98px) {
    .app-card .table.app-line-table {
        display: block;
        min-width: 0;
        table-layout: auto;
    }

    .app-line-table > thead {
        display: none;
    }

    .app-line-table > tbody {
        display: block;
        counter-reset: app-line;
    }

    .app-line-table > tbody > tr.app-collection-row {
        display: grid;
        grid-template-columns: repeat(3, minmax(0, 1fr));
        gap: 8px 12px;
        margin-bottom: 12px;
        padding: 12px;
        border: 1px solid var(--bs-border-color);
        border-radius: var(--app-radius-sm);
        counter-increment: app-line;
    }

    .app-expense-lines > tbody > tr.app-collection-row {
        grid-template-areas:
            'title title remove'
            'purpose purpose purpose'
            'date amount currency'
            'period period period'
            'files files files';
    }

    .app-receipts > tbody > tr.app-collection-row {
        grid-template-areas:
            'title title remove'
            'purpose purpose purpose'
            'date amount currency'
            'files files files'
            'trip trip trip';
    }

    .app-line-table > tbody > tr.app-collection-row::before {
        grid-area: title;
        align-self: center;
        content: attr(data-card-title) ' ' counter(app-line);
        font-weight: 600;
    }

    .app-card .table.app-line-table > tbody > tr > td {
        display: block;
        width: auto;
        padding: 0;
        border: 0;
        text-align: left;
    }

    .app-line-table td[data-label]::before {
        content: attr(data-label);
        display: block;
        margin-bottom: 2px;
        font-size: 0.75rem;
        color: var(--bs-secondary-color);
    }

    .app-line-table td[data-label][data-required]::before {
        content: attr(data-label) ' *';
    }

    .app-line-table .app-col-date { grid-area: date; }
    .app-line-table .app-col-amount { grid-area: amount; }
    .app-line-table .app-col-currency { grid-area: currency; }
    .app-line-table .app-col-purpose { grid-area: purpose; }
    .app-line-table .app-col-period { grid-area: period; }
    .app-line-table .app-col-files { grid-area: files; }
    .app-line-table .app-col-trip { grid-area: trip; }

    .app-files-button .btn-group {
        width: 100%;
    }

    .app-files-button-pick {
        flex: 1 1 auto;
        justify-content: center;
        min-height: 36px;
    }

    .app-line-table .app-col-remove {
        grid-area: remove;
        justify-self: end;
    }

    /* Флажок командировки на карточке — слева под своей подписью */
    .app-receipts td.app-col-trip .form-check {
        justify-content: flex-start;
    }

    .app-line-table .app-collection-remove {
        font-size: inherit;
    }

    .app-line-table .app-collection-remove::before {
        content: none;
    }
}

/* Подпись флажка прячется от глаз, но остаётся для экранного диктора: её роль
   в таблице играет заголовок столбца, а голый чекбокс без подписи диктор
   называет никак. Приём тот же, что у `.visually-hidden` в bootstrap */
.app-receipts td .form-check-label {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

.app-receipts td .form-check {
    display: flex;
    justify-content: center;
    padding-left: 0;
}

.app-receipts td .form-check-input {
    margin-left: 0;
    float: none;
}

/* `.app-line-pair` — столбиковая пара в одной ячейке (задача 2 волны «Один
   блок „Заказы“»): статья/подстатья строки разнесения. Период, который делил
   ячейку тем же приёмом, стал одним полем (`period-range`) */
.app-line-pair {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

/* Подпись подписанного поля (`_shell/_labeled_field.html.twig`, замечание
   финансиста 15.09.2026): мелкая и приглушённая — она объясняет поле, а не
   спорит с подписью столбца над ним. Над полем, а не слева: слева она
   забирала бы ширину самого узкого столбца таблицы. Заводился приём под
   период (дата «с» / дата «по»), обобщён на кассу и хеш заказа (круг правок 1
   задачи 11 волны «Финконтроль на заказе») — та же ошибка чтения нашлась в
   соседней ячейке */
.app-field-label {
    display: block;
    margin-bottom: 0.125rem;
    font-size: 0.75rem;
    line-height: 1.2;
    color: var(--bs-secondary-color);
}

/* Блок «Заказы» — стопка карточек, по одной на заказ (замечание финансиста
   16.09.2026, п. 6, вариант А; задача 22 волны «Документы обязательны;
   финконтроль уходит в заказы и списания»). Карточка — вложенная, без тени:
   тень держит внешняя `.app-card`, вторая читалась бы кашей */
.app-order-stack {
    display: grid;
    gap: 0.75rem;
}

.app-order-card {
    border: 1px solid var(--bs-border-color);
    border-radius: var(--app-radius);
    background-color: var(--bs-body-bg);
    overflow: hidden;
}

.app-order-card-head {
    display: grid;
    /* Номер с наименованием — растягивающаяся колонка: в `auto` наименованию
       расти было некуда (п. 53 замечаний финансиста 07.10.2026) */
    grid-template-columns: minmax(0, 1fr) auto auto;
    align-items: center;
    gap: 0.5rem 1rem;
    padding: 10px 16px;
    background-color: var(--app-filter-bg);
    border-bottom: 1px solid var(--bs-border-color);
}

.app-order-card-id {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.app-order-card-id .form-control {
    width: 10rem;
}

.app-order-card-label {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.085em;
    color: var(--app-th-fg);
}

.app-order-card-settle {
    justify-self: end;
    /* Не процент: в `auto`-колонке он считается как `auto`, и полоса
       списанного сжалась бы до ширины текста (п. 53, ревью С2) */
    width: 22rem;
    color: var(--bs-secondary-color);
}

.app-order-card-bar {
    height: 5px;
    margin-top: 4px;
    border-radius: 3px;
    background-color: var(--app-off-bg);
    overflow: hidden;
}

.app-order-card-bar > i {
    display: block;
    height: 100%;
    background-color: var(--app-accent);
}

/* Поля заказа — одним рядом «сумма | касса | сумма в валюте заявки | дата |
   хеш» (замечание финансиста 27.10.2026, «покомпактнее»): суммы — по ширине
   числа, дата — своей, касса и хеш делят остаток. На узком экране ряд
   переносится сам. Строки, которым нужна вся ширина (статья и период под
   «показать»), — `flex-basis: 100%` */
.app-order-fields {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: 0.625rem 0.75rem;
    padding: 12px 16px;
}

.app-order-fields > * {
    flex: 1 1 100%;
    min-width: 0;
}

.app-order-fields > .app-order-amount {
    flex: 0 1 11rem;
}

.app-order-fields > .app-order-currency {
    flex: 0 1 7rem;
}

.app-order-fields > .app-order-cashbox,
.app-order-fields > [data-partner-cashbox],
.app-order-fields > .app-order-hash {
    flex: 1 1 12rem;
}

.app-order-fields > .app-order-date {
    flex: 0 0 11rem;
}

/* Подпись со значком подсказки «?» (`_labeled_field.html.twig`, `field_tip`) */
.app-field-label-tip {
    display: flex;
    align-items: center;
    gap: 0.25rem;
}

.app-field-label-tip > label {
    margin: 0;
}

.app-help-tip {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1rem;
    height: 1rem;
    padding: 0;
    border: 1px solid currentColor;
    border-radius: 50%;
    background: none;
    color: inherit;
    font-size: 0.625rem;
    line-height: 1;
    cursor: help;
}

.app-help-tip::before {
    content: "?";
}

/* Статья, подстатья и период заказа — под «показать» (волна 1 ответов
   финансиста 15.10.2026): во всю ширину сетки заказа, своя сетка внутри */
.app-order-extra {
    grid-column: 1 / -1;
}

.app-order-extra > summary {
    color: var(--bs-secondary-color);
    font-size: .875rem;
    cursor: pointer;
}

.app-order-extra-fields {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 0.625rem 0.75rem;
    padding-top: .5rem;
}

/* Период — две колонки: две даты в одну не влезут */
.app-order-extra-fields > .app-period-range {
    grid-column: span 2;
}

.app-order-fields input[type="date"][name$="[conductedAt]"] {
    max-width: 11rem;
}

/* Наименование заказа занимает остаток строки шапки; `width` номера
   (`.app-order-card-id .form-control`) сильнее одного класса — сбиваем тем же
   весом */
.app-order-card-id .app-order-card-title {
    flex: 1 1 14rem;
    width: auto;
    min-width: 0;
}

.app-order-writeoffs {
    border-top: 1px dashed var(--bs-border-color);
    padding: 8px 16px 10px;
}

.app-card .table.app-writeoffs > thead > tr > th,
.app-card .table.app-writeoffs > tbody > tr > td {
    padding: 4px 6px 4px 0;
}

@media (max-width: 767.98px) {
    .app-order-card-head {
        grid-template-columns: 1fr;
    }

    .app-order-card-settle {
        justify-self: stretch;
        width: 100%;
    }

    .app-order-extra-fields {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

/* Номер расхода и подписи кнопок — одной строкой: «ID-000203» переносился
   по дефису на две, а «Удалить списание» — по пробелу, и строка таблицы
   вырастала вдвое */
.app-writeoffs td a {
    white-space: nowrap;
}

/* Метки расхождения и «по кассе не проведено» — переносятся: у bootstrap
   `.badge` в одну строку, и «Статья списания отличается от статьи заказа»
   раздвигала столбец состояния так, что столбец расхода уезжал за край */
.app-writeoffs .badge {
    white-space: normal;
    text-align: start;
}

/* Ссылка в журнал прогонов сверки — иконкой рядом с полем номера: текст
   «Прогоны сверки» в каждой строке съел бы ячейку. Подпись — `aria-label` и
   `title` ключом. Стрелка та же, что у ссылок в соседнюю вкладку, и
   нарисована тем же приёмом — маской, цвет от текста ссылки */
.app-writeoff-history {
    flex: none;
    display: inline-block;
    width: 1rem;
    height: 1rem;
    background-color: currentColor;
    -webkit-mask: var(--app-icon-external) center / contain no-repeat;
    mask: var(--app-icon-external) center / contain no-repeat;
}

/* Счётчик непрочитанных на колокольчике. Сидит на углу кнопки-иконки:
   отдельной строкой рядом он растянул бы плашку, а плашка одна на все экраны.

   ⚠️ Селектор с `.btn` не для красоты: у bootstrap есть `.btn .badge
   { position: relative }` — специфичность (0,2,0) против (0,1,0) у одного
   класса, и значок вставал бы в строку рядом с иконкой, растягивая кнопку. */
.btn .app-bell-badge {
    position: absolute;
    top: -6px;
    right: -6px;
    /* Кнопка делает содержимое flex-контейнером, и `text-align` значка не
       касается: раскладку flex-элементов решает `justify-content`, а он по
       умолчанию прижимает их к началу — однозначная цифра вставала левее
       центра. Центрируем обеими осями, а не текстом */
    display: flex;
    align-items: center;
    justify-content: center;
    min-width: 1.15rem;
    padding: .15rem .3rem;
    font-size: .6875rem;
    line-height: 1;
    color: #fff;
    /* Красный, а не акцент: акцент в оболочке работает ровно в двух местах
       (рельс меню и главная кнопка), и третье размывало бы его смысл */
    background: var(--bs-danger);
}

/* ── Кнопки ─────────────────────────────────────────────────────── */

/* Главная кнопка — второе и последнее место, где работает акцент */
.btn-primary {
    --bs-btn-bg: var(--app-btn-bg);
    --bs-btn-border-color: var(--app-btn-bg);
    --bs-btn-color: var(--app-btn-fg);
    --bs-btn-hover-bg: var(--app-btn-hover-bg);
    --bs-btn-hover-border-color: var(--app-btn-hover-bg);
    --bs-btn-hover-color: var(--app-btn-fg);
    --bs-btn-active-bg: var(--app-btn-hover-bg);
    --bs-btn-active-border-color: var(--app-btn-hover-bg);
    --bs-btn-active-color: var(--app-btn-fg);
    --bs-btn-disabled-bg: var(--app-btn-bg);
    --bs-btn-disabled-border-color: var(--app-btn-bg);
    --bs-btn-disabled-color: var(--app-btn-fg);
    box-shadow: var(--app-btn-shadow);
    font-weight: 600;
}

.btn-outline-secondary {
    --bs-btn-color: var(--bs-body-color);
    --bs-btn-border-color: var(--bs-border-color);
    --bs-btn-bg: var(--bs-body-bg);
    --bs-btn-hover-color: var(--bs-body-color);
    --bs-btn-hover-bg: var(--bs-tertiary-bg);
    --bs-btn-hover-border-color: var(--bs-border-color);
    --bs-btn-active-color: var(--bs-body-color);
    --bs-btn-active-bg: var(--bs-secondary-bg);
    --bs-btn-active-border-color: var(--bs-border-color);
    --bs-btn-disabled-color: var(--bs-secondary-color);
    --bs-btn-disabled-border-color: var(--bs-border-color);
    font-weight: 500;
}

.btn {
    --bs-btn-focus-box-shadow: 0 0 0 0.2rem var(--bs-focus-ring-color);
}

/* Кнопка «с иконкой и подписью» — иконка не должна разъезжаться с текстом */
.btn .app-icon {
    width: 15px;
    height: 15px;
    margin-right: 7px;
    vertical-align: -0.15em;
}

.app-topbar .btn .app-icon,
.btn .app-icon:only-child {
    margin-right: 0;
}

/* ── Дропдауны ──────────────────────────────────────────────────── */

.dropdown-menu {
    --bs-dropdown-border-color: var(--bs-border-color);
    --bs-dropdown-border-radius: var(--app-radius-sm);
    --bs-dropdown-link-hover-bg: var(--bs-tertiary-bg);
    --bs-dropdown-link-active-bg: var(--bs-secondary-bg);
    --bs-dropdown-link-active-color: var(--bs-emphasis-color);
    --bs-dropdown-font-size: 14.5px;
    box-shadow: var(--app-shadow);
}

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

   Заливка берётся у Bootstrap (`--bs-danger-bg-subtle`), а не заводится своим
   токеном: у него уже есть значения под светлую и тёмную тему, а тут нужен
   ровно фон предупреждения, а не собственный оттенок.

   ⚠️ Красится переменной, а не `background-color`: ячейку таблицы Bootstrap
   заливает внутренней тенью поверх фона, и обычный фон под ней не виден.
   Приём тот же, что у собственного `.table-active`, и наведение поверх
   расхождения продолжает работать — оно ставит ту же переменную на ячейке. */
.app-cash-mismatch {
    --bs-table-bg-state: var(--bs-danger-bg-subtle);
}

/* ── Бейджи состояния ───────────────────────────────────────────── */

/* Мягкая заливка вместо плотной: в таблице десяток плотных бейджей
   перетягивает внимание на себя. `!important` — потому что классы
   `text-bg-*` в готовом bootstrap.min.css объявлены с ним же */
.badge {
    padding: 3px 9px;
    border-radius: 4px;
    font-size: 12px;
    font-weight: 600;
    letter-spacing: 0.03em;
}

.badge.text-bg-success {
    background-color: var(--app-ok-bg) !important;
    color: var(--app-ok-fg) !important;
}

.badge.text-bg-secondary {
    background-color: var(--app-off-bg) !important;
    color: var(--app-off-fg) !important;
}

/* Точка перед подписью: состояние читается раньше, чем прочитан текст */
.badge-status::before {
    content: '';
    display: inline-block;
    width: 6px;
    height: 6px;
    margin-right: 5px;
    vertical-align: 0.08em;
    border-radius: 50%;
    background-color: currentColor;
}

/* ── Светофор состояния (11Г.7) ─────────────────────────────────── */

.status-dot {
    display: inline-block;
    width: 0.625rem;
    height: 0.625rem;
    border-radius: 50%;
}

.status-dot-ok {
    background-color: var(--bs-success);
}

.status-dot-warn {
    background-color: var(--bs-warning);
}

.status-dot-fail {
    background-color: var(--bs-danger);
}

/* ── Дерево департаментов ───────────────────────────────────────── */

.app-card .table.org-grid > tbody > tr > td:first-child {
    /* Отступ уровня — из атрибута, а не из пробелов в тексте: скринридер
       читает уровень из aria-level, а глаз — отсюда. Селектор длинный
       намеренно: общее `.app-card .table > tbody > tr > td { padding }`
       иначе перебивает отступ, и все уровни встают в одну колонку */
    padding-left: calc(20px + (var(--org-level, 1) - 1) * 1.75rem);
    white-space: nowrap;
}

.org-grid-name {
    font-weight: 500;
}

.org-grid-toggle,
.org-grid-toggle-placeholder {
    display: inline-flex;
    width: 1.5rem;
    padding: 0;
    vertical-align: middle;
}

.org-grid-toggle {
    color: var(--bs-secondary-color);
}

.org-grid tr[aria-expanded='false'] .org-grid-toggle .app-icon {
    transform: rotate(-90deg);
}

/* Ячейка действий строки: «+» и меню в одну линию, без переноса */
.org-grid-actions {
    white-space: nowrap;
}

.org-grid-actions .btn-link {
    padding: 0.125rem 0.375rem;
    color: var(--bs-secondary-color);
}

.org-grid-actions .btn-link:hover {
    color: var(--bs-body-color);
}

/* ── Структура проекта: панель департамента поверх карты и списка ── */

.org-stage {
    position: relative;
}

/* Поверх содержимого, справа. Пустой фрейм высоты не имеет и ничего не
   заслоняет; высота панели не ограничена — длинная форма просто выходит ниже
   карты, страница прокручивается */
.org-stage-panel {
    position: absolute;
    z-index: 3;
    top: 0.75rem;
    right: 0.75rem;
    width: min(24rem, calc(100% - 1.5rem));
}

.org-stage-panel .org-panel {
    box-shadow: var(--bs-box-shadow);
}

/* Архив под деревом: отступ от списка или карты */
.org-archive {
    margin-top: 1.5rem;
}

/* ── Карта оргструктуры ─────────────────────────────────────────── */

.org-map {
    position: relative;
}

/* Высота фиксированная: карту двигают внутри окна, а не растягивают
   страницу вниз на всё дерево */
.org-map-viewport {
    position: relative;
    overflow: hidden;
    height: min(70vh, 720px);
    background-color: var(--bs-body-bg);
    background-image: radial-gradient(var(--bs-border-color) 1px, transparent 1px);
    background-size: 24px 24px;
    cursor: grab;
    touch-action: none;
}

.org-map-viewport.is-dragging {
    cursor: grabbing;
}

.org-map-layer {
    position: absolute;
    top: 0;
    left: 0;
    transform-origin: 0 0;
}

.org-map-edges {
    position: absolute;
    top: 0;
    left: 0;
    overflow: visible;
}

.org-map-edge {
    fill: none;
    stroke: var(--bs-border-color);
    stroke-width: 1.5;
}

.org-map-node {
    position: absolute;
    display: flex;
    flex-direction: column;
    gap: 0.125rem;
    width: 220px;
    min-height: 78px;
    padding: 0.6rem 0.75rem;
    border: 1px solid var(--bs-border-color);
    border-radius: var(--app-radius-sm);
    background-color: var(--bs-body-bg);
}

.org-map-node.is-collapsible {
    cursor: pointer;
}

.org-map-node-name {
    font-weight: 500;
    line-height: 1.2;
}

.org-map-node-meta {
    font-size: 0.8125rem;
    line-height: 1.2;
    color: var(--bs-secondary-color);
}

.org-map-node-count {
    margin-left: auto;
    text-decoration: none;
}

.org-map-node-badge {
    position: absolute;
    bottom: -0.7rem;
    left: 50%;
    transform: translateX(-50%);
    padding: 0 0.4rem;
    border-radius: var(--app-radius-sm);
    font-size: 0.75rem;
    background-color: var(--bs-primary);
    color: var(--bs-white);
}

.org-map-node-badge.is-collapsed {
    background-color: var(--bs-secondary-bg);
    color: var(--bs-secondary-color);
}

.org-map-toolbar {
    position: absolute;
    z-index: 2;
    top: 0.75rem;
    right: 0.75rem;
    display: flex;
    gap: 0.25rem;
}

.org-map-hint {
    margin: 0;
    padding: 0.5rem 0.75rem;
    border-top: 1px solid var(--bs-border-color);
    font-size: 0.8125rem;
}

/* ── Оргструктура сотрудников ───────────────────────────────────── */

/* Полотно — то же, что у карты департаментов (.org-map-viewport, -layer,
   -toolbar). Здесь только раскладка дерева: его рисует сервер вложенными
   списками, а не браузер по координатам */

/* Ширина по содержимому: слой абсолютный, и без неё он был бы шириной с
   окно — «Уместить» мерил бы окно, а не дерево */
.employee-tree-layer {
    width: max-content;
    padding: 1.5rem;
    /* Полотно таскают мышью: выделение текста при этом только мешает */
    user-select: none;
}

.employee-tree-roots,
.employee-tree-branch {
    margin: 0;
    padding: 0;
    list-style: none;
}

/* Корни — в один ряд без переноса: перенос рядов ломает чтение «кто под кем» */
.employee-tree-roots {
    display: flex;
    flex-wrap: nowrap;
    align-items: flex-start;
    gap: 2rem;
}

.employee-tree-roots > .employee-tree-item {
    flex: 0 0 auto;
}

/* Подчинённые — столбиком под руководителем */
.employee-tree-branch {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 1.25rem;
    padding-top: 2.75rem;
}

/* display: flex перебивает атрибут hidden — возвращаем его явно */
.employee-tree-branch[hidden] {
    display: none;
}

/* Линия со стрелкой от значка руководителя к первому подчинённому */
.employee-tree-roots > .employee-tree-item > .employee-tree-branch::before {
    content: '';
    position: absolute;
    top: 0.75rem;
    left: 150px;
    height: 1.5rem;
    border-left: 1.5px solid var(--bs-border-color);
}

.employee-tree-roots > .employee-tree-item > .employee-tree-branch::after {
    content: '';
    position: absolute;
    top: 2.25rem;
    left: calc(150px - 4px);
    border: 4.5px solid transparent;
    border-top-color: var(--bs-border-color);
    border-bottom-width: 0;
}

/* Глубже — «лесенкой»: сдвиг вправо и вертикальная линия слева */
.employee-tree-branch .employee-tree-branch {
    margin: 1.25rem 0 0 1.5rem;
    padding: 0 0 0 1rem;
    border-left: 1.5px solid var(--bs-border-color);
}

.employee-tree-card {
    position: relative;
    width: 300px;
    padding: 0.75rem 0.875rem 1rem;
    border: 1px solid var(--bs-border-color);
    border-radius: var(--app-radius-sm);
    background-color: var(--bs-body-bg);
}

/* Испытательный срок: цвет warning темы, как у плашки в плитке */
.employee-tree-card.is-probation {
    border-color: var(--bs-warning);
}

.employee-tree-dot {
    position: absolute;
    top: 0.6rem;
    right: 0.6rem;
    width: 0.5rem;
    height: 0.5rem;
    border-radius: 50%;
    background-color: var(--bs-warning);
}

.employee-tree-card-head {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    min-width: 0;
    /* Место под точку испытательного срока */
    padding-right: 0.75rem;
}

.employee-tree-card-text {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.employee-tree-card-name,
.employee-tree-card-position {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.employee-tree-card-name {
    font-weight: 500;
    line-height: 1.25;
    text-decoration: none;
}

.employee-tree-card-position {
    font-size: 0.8125rem;
    line-height: 1.25;
    color: var(--bs-secondary-color);
}

/* Колонка текста — flex: без этого плашка растянулась бы на всю ширину */
.employee-tree-card-text .badge {
    align-self: flex-start;
    margin-top: 0.125rem;
}

.employee-tree-card-tags {
    display: flex;
    gap: 0.25rem;
    min-width: 0;
    margin-top: 0.5rem;
}

.employee-tree-card-tags .badge {
    flex: 0 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    font-weight: 400;
}

/* Значок с числом подчинённых — кнопка на нижней кромке карточки */
.employee-tree-toggle {
    position: absolute;
    z-index: 1;
    bottom: -0.7rem;
    left: 50%;
    transform: translateX(-50%);
    padding: 0 0.4rem;
    border: 0;
    border-radius: var(--app-radius-sm);
    font-size: 0.75rem;
    background-color: var(--bs-secondary-bg);
    color: var(--bs-secondary-color);
}

.employee-tree-toggle[aria-expanded="true"] {
    background-color: var(--bs-primary);
    color: var(--bs-white);
}

/* Рабочее место финконтроля: Tabulator приходит со своим светлым оформлением,
   поэтому цвета переопределяются переменными Bootstrap — иначе в тёмной теме
   таблица светится белым прямоугольником */
.app-workbench .tabulator {
    background-color: transparent;
    border: 1px solid var(--bs-border-color);
    border-radius: var(--app-radius-sm);
    font-size: 0.875rem;
}

.app-workbench .tabulator .tabulator-header,
.app-workbench .tabulator .tabulator-header .tabulator-col,
.app-workbench .tabulator .tabulator-footer {
    border-color: var(--bs-border-color);
    background-color: var(--bs-tertiary-bg);
    color: var(--bs-body-color);
}

.app-workbench .tabulator-row,
.app-workbench .tabulator-row.tabulator-row-even {
    border-color: var(--bs-border-color);
    background-color: var(--bs-body-bg);
    color: var(--bs-body-color);
}

.app-workbench .tabulator-row:hover,
.app-workbench .tabulator-row.tabulator-selected {
    background-color: var(--bs-secondary-bg);
}

.app-workbench .tabulator-row .tabulator-cell {
    border-color: var(--bs-border-color);
}

/* Редактируемая ячейка обязана отличаться от нередактируемой до нажатия:
   иначе финансист выясняет это перебором */
.app-workbench .tabulator-row .tabulator-cell.tabulator-editable:not(.tabulator-editing):hover {
    outline: 1px solid var(--bs-primary-border-subtle);
}

.app-workbench .tabulator-row .tabulator-cell.tabulator-editing input,
.app-workbench .tabulator-row .tabulator-cell.tabulator-editing select {
    color: var(--bs-body-color);
    background-color: var(--bs-body-bg);
}

.app-workbench .tabulator .tabulator-header .tabulator-col .tabulator-header-filter input {
    color: var(--bs-body-color);
    background-color: var(--bs-body-bg);
    border: 1px solid var(--bs-border-color);
    border-radius: var(--app-radius-sm);
}

/* ── Панель фильтров сотрудников ────────────────────────────────── */

.employee-filter-panel {
    --bs-offcanvas-width: min(26rem, 100vw);
}

/* Два состояния панели — фильтры и управление — занимают её целиком, каждое со
   своей шапкой, телом и подвалом */
.employee-filter-state {
    display: flex;
    flex-direction: column;
    min-height: 0;
    height: 100%;
}

/* display: flex перебивает атрибут hidden — возвращаем его явно */
.employee-filter-state[hidden] {
    display: none;
}

.employee-filter-footer {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 0.5rem;
    padding: 0.75rem var(--bs-offcanvas-padding-x);
    border-top: 1px solid var(--bs-border-color);
}

.employee-filter-foreign {
    padding-top: 0.5rem;
    border-top: 1px dashed var(--bs-border-color);
}

.employee-filter-layout {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

.employee-filter-layout-row {
    display: flex;
    align-items: center;
    gap: 0.25rem;
    padding: 0.35rem 0.5rem;
    border: 1px solid var(--bs-border-color);
    border-radius: var(--app-radius-sm);
    background-color: var(--bs-body-bg);
}

/* display: flex перебивает атрибут hidden — возвращаем его явно */
.employee-filter-layout-row[hidden] {
    display: none;
}

.employee-filter-layout-row.is-dragged {
    opacity: 0.5;
}

.employee-filter-layout-handle {
    color: var(--bs-secondary-color);
    cursor: grab;
}

/* Списки галок в панели: без предела по высоте нижние варианты уходят за экран,
   а страница под панелью заперта от прокрутки. Только внутри панели — выпадающие
   списки заявок и расходов не меняются */
.employee-filter-panel .dropdown-menu {
    max-height: min(22rem, 50vh);
    max-width: calc(var(--bs-offcanvas-width) - 2rem);
    overflow-y: auto;
}

.employee-filter-panel .dropdown-menu .form-check-label {
    overflow-wrap: anywhere;
}

.employee-filter-layout-label {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* ── Главная: дела и блоки ──────────────────────────────────────── */

/* Зона дел стоит над сеткой блоков и всегда во всю ширину: то, что ждёт
   лично человека, не соревнуется за место с витринами */
.app-tasks {
    margin-bottom: 1rem;
}

/* Ряд действий — верхняя полоса карточки дел: место кнопок постоянное,
   и это первое, что видит человек, открывший главную */
.app-quick-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    padding: 14px 20px;
    border-bottom: 1px solid var(--bs-border-color);
}

/* На узком экране кнопка занимает строку целиком: в ряд они там всё равно
   не встают, а половинчатая ширина — промах пальцем */
@media (max-width: 575.98px) {
    .app-quick-actions .btn {
        flex: 1 1 100%;
    }
}

.app-task-list {
    margin: 0;
    padding: 0;
    list-style: none;
}

.app-task + .app-task {
    border-top: 1px solid var(--bs-border-color);
}

.app-task > a {
    display: flex;
    align-items: center;
    gap: 0.65rem;
    padding: 12px 20px;
    font-size: 14.5px;
    text-decoration: none;
    color: var(--bs-body-color);
}

.app-task > a:hover {
    background-color: var(--bs-tertiary-bg);
}

.app-task .app-task-label {
    flex-grow: 1;
}

/* Счётчик — плашка, а не текст: взгляд ищет число, а не строку */
.app-task .app-task-count {
    min-width: 1.85rem;
    padding: 1px 8px;
    border-radius: 999px;
    background-color: var(--bs-secondary-bg);
    font-size: 12.5px;
    font-weight: 600;
    text-align: center;
}

/* Тон задаёт полосу слева и цвет иконки — не заливку строки: список из
   пяти цветных прямоугольников перестаёт читаться */
.app-task-urgent > a {
    box-shadow: inset 3px 0 0 var(--bs-danger);
}

.app-task-urgent .app-icon {
    color: var(--bs-danger);
}

.app-task-urgent .app-task-count {
    background-color: var(--bs-danger-bg-subtle);
    color: var(--bs-danger-text-emphasis);
}

.app-task-warn > a {
    box-shadow: inset 3px 0 0 var(--bs-warning);
}

.app-task-warn .app-icon {
    color: var(--bs-warning-text-emphasis);
}

/* Сетка блоков: широкие занимают строку целиком, остальные встают парами
   и сами схлопываются в одну колонку на узком экране */
.app-dashboard {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(330px, 1fr));
    gap: 1rem;
    align-items: start;
}

.app-widget-wide {
    grid-column: 1 / -1;
}

/* Отступ между карточками задаёт сетка — иначе он удваивается */
.app-dashboard .app-card + .app-card {
    margin-top: 0;
}

.app-widget-subtitle {
    margin: 0 0 0.5rem;
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.085em;
    color: var(--app-th-fg);
}

/* Ряд счётчиков внутри блока */
.app-metrics {
    display: flex;
    flex-wrap: wrap;
}

.app-metric {
    flex: 1 1 120px;
    padding: 16px 20px;
    text-decoration: none;
    color: var(--bs-body-color);
}

.app-metric + .app-metric {
    border-left: 1px solid var(--bs-border-color);
}

a.app-metric:hover {
    background-color: var(--bs-tertiary-bg);
}

.app-metric-value {
    display: block;
    font-family: var(--app-font-display);
    font-size: 26px;
    line-height: 1.1;
    font-weight: 600;
}

.app-metric-label {
    display: block;
    margin-top: 2px;
    font-size: 12.5px;
    color: var(--bs-secondary-color);
}

/* Ненулевая очередь — единственное, что выделяется цветом: остальные
   счётчики остаются нейтральными, иначе выделение перестаёт работать */
.app-metric-alert .app-metric-value {
    color: var(--bs-danger-text-emphasis);
}

/* Список «строка — значение» для блоков без таблицы */
.app-list {
    margin: 0;
    padding: 0;
    list-style: none;
}

.app-card > .app-list > li,
.app-card-body > .app-list > li {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.75rem;
    font-size: 14.5px;
}

.app-card > .app-list > li {
    padding: 11px 20px;
}

.app-card > .app-list > li + li,
.app-card-body > .app-list > li + li {
    border-top: 1px solid var(--bs-border-color);
}

.app-card-body > .app-list > li {
    padding: 7px 0;
}

/* Светофор состояния: цветом красится только рамка карточки */
.app-health-warn {
    border-color: var(--bs-warning-border-subtle);
}

.app-health-fail {
    border-color: var(--bs-danger-border-subtle);
}

/* ── Переводы между кассами ─────────────────────────────────────── */

/* Строка перевода — это пара «касса и сумма» дважды. Касса отвечает на
   вопрос колонки, сумма читается ниже: цифры выровнены по разряду, иначе
   столбец сумм рассыпается и его приходится читать, а не просматривать */
.app-transfers .app-transfer-cashbox {
    font-weight: 600;
    line-height: 1.3;
}

.app-transfers .app-transfer-amount {
    font-size: 13px;
    color: var(--bs-secondary-color);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.app-transfers .app-transfer-rate {
    color: var(--bs-secondary-color);
    font-variant-numeric: tabular-nums;
}

/* Направление показывает стрелка, а не порядок колонок: пара «откуда —
   куда» иначе различима только по заголовку таблицы. Селектор длинный,
   потому что отступы ячеек в карточке заданы через `.app-card .table` */
.app-card .table > thead > tr > .app-transfer-arrow-cell,
.app-card .table > tbody > tr > .app-transfer-arrow-cell {
    width: 1%;
    padding-left: 0;
    padding-right: 0;
    text-align: center;
}

.app-transfers .app-transfer-arrow::before {
    content: '→';
    color: var(--bs-secondary-color);
}

.app-transfers .app-transfer-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-end;
    gap: 8px;
}

.app-transfers .app-transfer-conduct {
    display: flex;
    align-items: center;
    gap: 8px;
}

.app-transfers .app-transfer-conduct .form-control {
    width: auto;
}

.btn .app-transfer-caret {
    width: 13px;
    height: 13px;
    margin-right: 0;
    margin-left: 6px;
    transition: transform 0.15s ease-in-out;
}

.btn[aria-expanded='true'] .app-transfer-caret {
    transform: rotate(180deg);
}

/* Подробности свёрнуты: развёрнутыми они удваивали каждую строку списка.
   Ячейка-обёртка своих отступов и линии не рисует — в свёрнутом виде от
   неё не должно остаться и полосы */
.app-card .table > tbody > tr.app-transfer-details > td {
    padding: 0;
    border-bottom-width: 0;
}

.app-transfer-panel {
    padding: 16px 20px;
    background-color: var(--app-filter-bg);
    border-bottom: 1px solid var(--bs-border-color);
}

/* У последнего перевода линию под панелью рисует край карточки */
.app-card .table > tbody > tr.app-transfer-details:last-child .app-transfer-panel {
    border-bottom-width: 0;
}

.app-transfer-panel .app-list > li {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    padding: 5px 0;
    font-size: 14px;
}

.app-transfer-panel .app-list > li + li {
    border-top: 1px solid var(--bs-border-color);
}

/* Шкала этапов документа — макет `info/status-tracker.html` (п. 13 правок
   03.08.2026). Из макета взяты приёмы: соединительные линии, градиент на
   переходе «пройдено → текущее», кольцо у текущей точки и приписка снизу
   цветом состояния.

   Цвета — переменными темы, а не числами из макета: оболочка живёт в светлой
   теме, тёмной и контрастном режиме (11Г.2). Зелёный макета `#17a06f` — это
   `--bs-success`, охра `#c9a15e` — акцент темы `--bs-primary`. Шрифт и
   фиксированная ширина карточки из макета не переносятся: это оформление
   демонстрационной страницы, а не трекера.

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

   Состояния узла и тон приписки называются одними и теми же словами
   («done», «halted», «broken»), но живут на разных элементах — поэтому правила
   узлов уточнены классом `.app-progress-node`. Без него тон на обёртке
   раскрашивал бы все точки разом и вдобавок дорисовывал бы ей линию. */
.app-progress {
    /* Диаметр точки нужен ещё и линии — она держится за его половину */
    --app-progress-dot-size: .8125rem;
    /* Промежуток между узлами. Без него длинные подписи соседних узлов
       («Согласование» и «На проверке») смыкаются в одно слово: колонки делят
       ширину поровну, и текст занимает её всю. Тот же промежуток вычитается
       из линии — иначе она обрывалась бы, не дойдя до следующей точки */
    --app-progress-gap: 1.25rem;

    margin: .75rem 0 0;
}

.app-progress-track {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: 1fr;
    gap: 0 var(--app-progress-gap);
    margin: 0 0 .625rem;
    padding: 0;
    list-style: none;
}

.app-progress-node {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .5rem;
    min-width: 0;
    color: var(--bs-secondary-color);
    font-size: .8125rem;
    line-height: 1.25;
    text-align: center;
}

/* Линия к следующему узлу висит на самом узле, а не отдельным элементом:
   последнему она тогда не достаётся сама собой, без правила «а сколько их» */
.app-progress-node:not(:last-child)::before {
    content: '';
    position: absolute;
    top: calc(var(--app-progress-dot-size) / 2 - 1px);
    left: 50%;
    /* До центра следующей точки: половина своей колонки плюс промежуток */
    right: calc(-50% - var(--app-progress-gap));
    height: 2px;
    background: var(--bs-border-color);
}

.app-progress-dot {
    position: relative;
    z-index: 1;
    width: var(--app-progress-dot-size);
    height: var(--app-progress-dot-size);
    border-radius: 50%;
    background: var(--bs-tertiary-color);
}

.app-progress-node.app-progress-done { color: var(--bs-success); }
.app-progress-node.app-progress-done .app-progress-dot { background: var(--bs-success); }
.app-progress-node.app-progress-done:not(:last-child)::before { background: var(--bs-success); }

/* Переход «пройдено → текущее». Какой узел последний пройденный, знает
   `:has()`, а не шаблон: расчёт этапа не должен знать про вёрстку */
.app-progress-node.app-progress-done:has(+ .app-progress-current)::before {
    background: linear-gradient(to right, var(--bs-success), var(--bs-primary));
}

.app-progress-node.app-progress-done:has(+ .app-progress-halted)::before {
    background: linear-gradient(to right, var(--bs-success), var(--bs-warning));
}

.app-progress-node.app-progress-done:has(+ .app-progress-broken)::before {
    background: linear-gradient(to right, var(--bs-success), var(--bs-danger));
}

.app-progress-node.app-progress-done:has(+ .app-progress-upcoming)::before {
    background: linear-gradient(to right, var(--bs-success), var(--bs-border-color));
}

/* Кольцо-подсветка: текущий узел должен находиться взглядом сразу, до чтения
   подписей. Подпись при этом берёт затемнённый акцент — латунь на белом даёт
   2:1 и на текст не годится */
.app-progress-node.app-progress-current { color: var(--app-accent-text); font-weight: 600; }

.app-progress-node.app-progress-current .app-progress-dot {
    background: var(--bs-primary);
    box-shadow: 0 0 0 3px rgba(var(--bs-primary-rgb), .22);
}

.app-progress-node.app-progress-halted { color: var(--bs-warning-text-emphasis); font-weight: 600; }

.app-progress-node.app-progress-halted .app-progress-dot {
    background: var(--bs-warning);
    box-shadow: 0 0 0 3px rgba(var(--bs-warning-rgb), .22);
}

.app-progress-node.app-progress-broken { color: var(--bs-danger-text-emphasis); font-weight: 600; }

.app-progress-node.app-progress-broken .app-progress-dot {
    background: var(--bs-danger);
    box-shadow: 0 0 0 3px rgba(var(--bs-danger-rgb), .22);
}

/* Пометка «из шаблона регулярных платежей» — иконка рядом с типом заявки.
   Приглушена: это свойство заявки, а не предупреждение, и спорить за внимание
   со статусом она не должна */
.app-recurring-mark {
    display: inline-flex;
    align-items: center;
    margin-left: .3rem;
    color: var(--bs-secondary-color);
    vertical-align: -.15em;
}

.app-recurring-mark .app-icon {
    width: .9375rem;
    height: .9375rem;
}

/* Тот же словарь состояний, но одним словом без шкалы: в списке заявок
   показывается только узел, на котором заявка стоит сейчас. Класс свой —
   правила узла держатся за соседей и за колонку, а здесь ни того, ни другого */
.app-progress-mark.app-progress-done { color: var(--bs-success); }
.app-progress-mark.app-progress-current { color: var(--app-accent-text); font-weight: 600; }
.app-progress-mark.app-progress-halted { color: var(--bs-warning-text-emphasis); font-weight: 600; }
.app-progress-mark.app-progress-broken { color: var(--bs-danger-text-emphasis); font-weight: 600; }

/* Приписка снизу — цветом состояния: она и есть ответ «чего ждём и от кого» */
.app-progress-note { font-size: .9375rem; }
.app-progress-waiting .app-progress-note { color: var(--bs-body-color); }
.app-progress-done .app-progress-note { color: var(--bs-success); }
.app-progress-halted .app-progress-note { color: var(--bs-warning-text-emphasis); }
.app-progress-broken .app-progress-note { color: var(--bs-danger-text-emphasis); }

/* На узкой ширине шкала разворачивается вертикально. Пять колонок на телефоне
   дают по 60 px на подпись, а «Согласование» требует вдвое больше — подписи
   наезжают друг на друга. Ужать шрифт не помогает: слово одно и не переносится.

   Приёмы при этом те же самые — линия, градиент, кольцо, — просто линия идёт
   сверху вниз. Отдельного трекера для телефона нет. */
@media (max-width: 575.98px) {
    .app-progress-track {
        grid-auto-flow: row;
        grid-auto-columns: auto;
        gap: .75rem;
    }

    /* В вертикальном режиме промежуток между узлами задаёт `gap` строк, и
       поправка на колонки линии только помешала бы */
    .app-progress {
        --app-progress-gap: 0rem;
    }

    .app-progress-node {
        flex-direction: row;
        align-items: center;
        gap: .625rem;
        text-align: left;
    }

    /* Линия тянется от центра своей точки до центра следующей: при равной
       высоте строк это ровно «высота строки плюс промежуток». Верхний её
       кусок закрывает сама точка — она непрозрачна и лежит слоем выше */
    .app-progress-node:not(:last-child)::before {
        top: 50%;
        right: auto;
        bottom: calc(-.75rem - 50%);
        left: calc(var(--app-progress-dot-size) / 2 - 1px);
        width: 2px;
        height: auto;
    }

    .app-progress-node.app-progress-done:has(+ .app-progress-current)::before {
        background: linear-gradient(to bottom, var(--bs-success), var(--bs-primary));
    }

    .app-progress-node.app-progress-done:has(+ .app-progress-halted)::before {
        background: linear-gradient(to bottom, var(--bs-success), var(--bs-warning));
    }

    .app-progress-node.app-progress-done:has(+ .app-progress-broken)::before {
        background: linear-gradient(to bottom, var(--bs-success), var(--bs-danger));
    }

    .app-progress-node.app-progress-done:has(+ .app-progress-upcoming)::before {
        background: linear-gradient(to bottom, var(--bs-success), var(--bs-border-color));
    }
}

/* Числовое поле без стрелок-спиннера.

   В поле, где значение вводят один раз и целиком (ID на входе), спиннер только
   мешает попадать курсором: кнопки съедают правый край поля, а нажатие на них
   меняет значение на единицу — ровно то, чего от этого поля никогда не хотят. */
.app-numeric {
    -moz-appearance: textfield;
    appearance: textfield;
}

.app-numeric::-webkit-outer-spin-button,
.app-numeric::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

/* ── История блока «Должность» на карточке сотрудника ───────────── */

/* Восемь колонок: общий отступ ячеек карточки (`.app-card .table > … > td`,
   13px 20px) разносит их за край. Селектор на класс длиннее общего — иначе
   общее правило перебило бы этот отступ молча */
.app-card .table.app-assignment-history > thead > tr > th,
.app-card .table.app-assignment-history > tbody > tr > td {
    padding: 8px 12px;
}

/* ── Команды ────────────────────────────────────────────────────── */

.team-grid {
    display: grid;
    /* min(17rem, 100%): жёсткий минимум трека не даёт колонке сжаться на
       очень узком экране — .app-card блока режет всё, что не помещается
       (overflow: hidden), и без min() перенос строк не спасал бы от обрезки */
    grid-template-columns: repeat(auto-fill, minmax(min(17rem, 100%), 1fr));
    gap: 0.75rem;
}

/* Блок команды — это `.app-card` внутри `.team-grid`, а не единственная
   карточка страницы, поэтому общее `.app-card + .app-card { margin-top: 1rem }`
   (задумано для панелей друг под другом) распирает верхний ряд сетки вниз у
   всех блоков, кроме первого, ровно как в волне Б с `.employee-grid` —
   тот же класс поломки, только сам её не унаследовал: у `.team-grid` нет
   `.employee-grid`, правило нужно завести заново. */
.team-grid > .app-card + .app-card {
    margin-top: 0;
}

.team-letter {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    height: 2.25rem;
    border-radius: var(--app-radius-sm);
    /* Цвет из палитры оболочки, а не фиолетовый со скрина: правки идут
       переменными поверх готового bootstrap.min.css */
    background-color: var(--bs-primary);
    color: var(--bs-white);
    font-weight: 600;
}

.team-stack {
    display: inline-flex;
    align-items: center;
}

.team-stack .app-avatar:not(:first-child) {
    margin-left: -0.5rem;
}

.team-stack-more {
    margin-left: 0.25rem;
    font-size: 0.75rem;
    color: var(--bs-secondary-color);
}

/* Подбор участников — список флажков, а не выпадающий список: сотрудников
   может быть много, и без прокрутки своей области список растянул бы всю
   панель формы. */
.team-members-list {
    max-height: 18rem;
    overflow-y: auto;
}

/* ── Период одним полем (`period-range`, п. 34.3 замечаний финансиста
   25.09.2026, §4 спеки 2026-09-29) ─────────────────────────────────────── */

.app-period-range {
    min-width: 0;
}

/* Обёртка — якорь иконки. Замечание стража формы встаёт ПОД обёрткой
   (`form_guard#mark`), поэтому иконка может держаться за всю её высоту */
.app-period-range-control {
    position: relative;
}

/* Место под иконку справа. Серверная тема красит ошибку фоном-иконкой
   справа — она легла бы под кнопку календаря; красной рамки достаточно */
.app-period-range-control > .form-control,
.app-period-range-control > .form-control.is-invalid {
    padding-right: 2.25rem;
    background-image: none;
}

.app-period-range-toggle {
    position: absolute;
    top: 0;
    right: 0;
    height: 100%;
    width: 2.25rem;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: 0;
    background: transparent;
    color: var(--bs-secondary-color);
}

.app-period-range-toggle:hover {
    color: var(--bs-body-color);
}

.app-period-range-toggle:focus-visible {
    outline: 2px solid var(--app-accent);
    outline-offset: -2px;
    border-radius: var(--app-radius-sm);
}

.app-period-range-toggle .app-icon {
    width: 1rem;
    height: 1rem;
}

/* Ячейка таблицы (строка чека, списание): «01.09.2026 — 30.09.2026» и
   иконка — 236px (§3.1 спеки 2026-09-29) */
.app-period-range-cell {
    min-width: 236px;
}

/* Попап календаря — прямо в <body>, `position: fixed` через Popper:
   `.table-responsive` строк обрезал бы его (ревью А5). Цвета — токены
   оболочки: выбранные дни — как основная кнопка */
.app-period-popup {
    position: fixed;
    z-index: 1060;
    padding: 12px;
    background: var(--bs-body-bg);
    border: 1px solid var(--bs-border-color);
    border-radius: var(--app-radius);
    box-shadow: var(--app-shadow);
}

.app-period-months {
    display: flex;
    gap: 20px;
}

.app-period-month-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 6px;
}

.app-period-month-title {
    display: inline-block;
    font-weight: 600;
}

.app-period-month-title::first-letter {
    text-transform: uppercase;
}

.app-period-nav,
.app-period-nav-gap {
    width: 2rem;
    height: 2rem;
}

.app-period-nav {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: 0;
    border-radius: var(--app-radius-sm);
    background: transparent;
    color: var(--bs-secondary-color);
}

.app-period-nav:hover:not(:disabled) {
    background: var(--app-off-bg);
    color: var(--bs-body-color);
}

.app-period-nav:disabled {
    color: var(--bs-tertiary-color);
    cursor: not-allowed;
}

.app-period-nav-prev .app-icon {
    transform: rotate(180deg);
}

.app-period-grid {
    display: grid;
    grid-template-columns: repeat(7, 2.25rem);
    gap: 2px 0;
}

.app-period-weekday {
    font-size: 0.75rem;
    line-height: 1.75rem;
    text-align: center;
    color: var(--bs-secondary-color);
}

.app-period-day {
    height: 2.25rem;
    padding: 0;
    border: 0;
    border-radius: var(--app-radius-sm);
    background: transparent;
    color: inherit;
    font-variant-numeric: tabular-nums;
}

.app-period-day:hover:not(:disabled) {
    background: var(--app-off-bg);
}

.app-period-day.is-today {
    box-shadow: inset 0 0 0 1px var(--bs-border-color);
}

.app-period-day.is-in-range {
    border-radius: 0;
    background: var(--app-side-active-bg);
}

.app-period-day.is-start,
.app-period-day.is-end {
    background: var(--app-btn-bg);
    color: var(--app-btn-fg);
}

.app-period-day:disabled {
    color: var(--bs-tertiary-color);
    cursor: not-allowed;
}

.app-period-day:focus-visible,
.app-period-nav:focus-visible {
    outline: 2px solid var(--app-accent);
    outline-offset: -2px;
}

.app-period-foot {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 10px;
    padding-top: 10px;
    border-top: 1px solid var(--bs-border-color);
}

.app-period-hint {
    margin-right: auto;
    font-size: 0.8125rem;
    color: var(--bs-secondary-color);
}

/* Телефон: шторка снизу на всю ширину, один месяц, «Готово» во всю
   ширину, цели касания 44px (§4.2 спеки 2026-09-29) */
.app-period-popup.is-sheet {
    top: auto;
    right: 0;
    bottom: 0;
    left: 0;
    padding: 16px 16px calc(16px + env(safe-area-inset-bottom));
    border-radius: var(--app-radius) var(--app-radius) 0 0;
}

.app-period-popup.is-sheet .app-period-grid {
    grid-template-columns: repeat(7, 1fr);
}

.app-period-popup.is-sheet .app-period-day,
.app-period-popup.is-sheet .app-period-nav {
    height: 44px;
}

.app-period-popup.is-sheet .app-period-nav,
.app-period-popup.is-sheet .app-period-nav-gap {
    width: 44px;
}

.app-period-popup.is-sheet .app-period-foot {
    flex-wrap: wrap;
}

.app-period-popup.is-sheet .app-period-hint {
    flex-basis: 100%;
}

.app-period-popup.is-sheet .app-period-done {
    width: 100%;
    min-height: 44px;
}

/* Телефон: иконка календаря — единственный путь к шторке, поэтому цель
   касания не меньше 44×44px (§1 спеки 2026-09-29). Поле ниже 44px —
   кнопка выступает за него поровну сверху и снизу: она прозрачна, выступ
   не виден, а касание ловит */
@media (max-width: 767.98px) {
    .app-period-range-control > .form-control,
    .app-period-range-control > .form-control.is-invalid {
        padding-right: 44px;
    }

    .app-period-range-toggle {
        top: 50%;
        width: 44px;
        height: 44px;
        min-height: 100%;
        transform: translateY(-50%);
    }
}

/* ── Поле поиска в длинном списке (`choice-search`, дополнение к п. 45,
   01.10.2026) ─────────────────────────────────────────────────────────────

   Видимое поле носит классы самого списка (`form-select`, `form-select-sm`):
   в покое оно и выглядит списком, со стрелкой, и подпадает под те же правила
   размера — узкий столбец валюты в строках чеков, панель фильтров, 44px на
   телефоне. Своих цветов нет: всё — переменные bootstrap и темы */

/* Обёртка видимого поля и меню — блок на месте списка; под полем же встаёт
   замечание стража формы */
.app-choice-search {
    position: relative;
}

/* В покое поле — список, а не строка ввода: курсор-стрелка, как у списка.
   Открыто под запрос (bootstrap ставит полю `show`) — обычный текстовый */
.app-choice-search > .form-select:not(.show):not(:disabled) {
    cursor: default;
}

/* Ошибка в узком столбце валюты: знак ошибки bootstrap рисует вторым фоном и
   раздвигает правый отступ до 4rem — от кода в 92px оставалась одна буква.
   Красной рамки достаточно (как у периода); отступы — те же, что у поля без
   ошибки (`.app-line-table .app-col-currency .form-select`) */
.app-line-table .app-col-currency .app-choice-search > .form-select.is-invalid {
    --bs-form-select-bg-icon: none;
    padding-right: 1.5rem;
    background-position: right 0.4rem center;
    background-size: 16px 12px;
}

/* Меню открывается «от окна» (`strategy: fixed`) и шире поля: в столбце
   валюты 92px, а строка варианта — «USD — Доллар США» и страны под ней.
   Совпадений бывает два десятка и больше («доллар») — прокрутка внутри */
.app-choice-search-menu {
    min-width: 20rem;
    max-width: min(28rem, 100vw);
    max-height: 60vh;
    overflow-y: auto;
}

/* Две строки: подпись и, мелко, страны. Длинное название переносится, а не
   распирает меню — у `.dropdown-item` перенос выключен */
.app-choice-search-item {
    white-space: normal;
    overflow-wrap: anywhere;
}

.app-choice-search-item > span {
    display: block;
}

/* Телефон: список на всю ширину экрана под полем. По горизонтали его ставит
   Popper — меню шириной в экран он сам прижимает к левому краю */
@media (max-width: 575.98px) {
    .app-choice-search-menu {
        width: 100vw;
        min-width: 0;
        max-width: 100vw;
    }
}

/* Реквизит целиком под списком (п. 47 замечаний финансиста 01.10.2026): длинный адрес переносится, колонку не раздвигает */
.app-requisite-details {
    font-family: var(--bs-font-monospace);
    word-break: break-all;
}
