/*
 * Мобильный слой CRM (2026-07-30).
 *
 * CRM смотрят с телефонов, а вёрстка строилась под десктоп: сайдбар, широкие
 * таблицы, двухколоночные модалки.
 * Файл подключается ПОСЛЕДНИМ слоем каскада в `base.html` (после crm-theme.css
 * и nav-shell.css) и содержит ТОЛЬКО адаптив: ничего нового не рисует, лишь
 * ужимает уже существующее под узкий экран.
 *
 * Брейкпоинты совпадают с сеткой Bootstrap — третью систему не заводим:
 *   ≤991.98px — сайдбара нет (его прячет nav-shell.css), контенту вся ширина;
 *   ≤767.98px — телефон: модалки во весь экран, компактные таблицы и карточки.
 *
 * Постраничный адаптив живёт в самой странице (журнал — блок
 * `@media (max-width: 767.98px)` в конце `<style>` timetable.html). Здесь —
 * только сквозное, что касается каждого экрана.
 */

/* ─────────────── Планшет и уже: сайдбара нет, ширина — контенту ─────────── */
@media (max-width: 991.98px) {
    .app-main {
        padding: 14px 12px 72px !important;
    }
    /* Вложенный container-fluid дублировал бы боковой отступ .app-main */
    .app-main > .container-fluid,
    .app-main > .container {
        padding-left: 0;
        padding-right: 0;
        max-width: none;
    }
}

/* ───────────────────────────── Телефон ─────────────────────────────────── */
@media (max-width: 767.98px) {

    /* ── Полоса выручки. Ужимаем, но НЕ прячем: процент выполнения плана
          нигде больше с телефона не виден (пилюля у названия клуба в шапке
          показывает утилизацию кортов, а не план, а отдельного экрана с
          прогрессом плана в CRM нет — план только заводят в настройках
          клуба). Скрыв полосу, мы отобрали бы у директора единственный доступ
          к цифре. 32 → 22px: цифры и процент на 390px помещаются в одну
          строку и без запаса по высоте. ── */
    .has-revenue-plan .revenue-bar-slot {
        height: 22px;
    }
    .revenue-bar {
        font-size: 0.68rem;
        gap: 6px;
    }
    .revenue-bar .badge {
        padding: 0.1rem 0.35rem;
        font-size: 0.66rem;
    }
    /* Дорожка прогресса задана инлайном (height: 14px) — перебиваем её */
    .revenue-bar > .position-relative {
        height: 9px !important;
    }

    /* ── Типографика: display-заголовки в 900 весе на узком экране
          переносились по 4 строки ── */
    h1, .h1 { font-size: 1.45rem; }
    h2, .h2 { font-size: 1.28rem; }
    h3, .h3 { font-size: 1.12rem; }
    h4, .h4 { font-size: 1rem; }
    .display-1, .display-2, .display-3, .display-4 { font-size: 1.6rem; }

    /* ── Карточки и таблицы: плотнее, но без обрезки текста ── */
    .card-body { padding: 0.8rem 0.7rem; }
    .card-header { padding: 0.6rem 0.7rem; }

    .table {
        font-size: 0.82rem;
    }
    .table > :not(caption) > * > * {
        padding: 0.45rem 0.4rem;
    }
    /* Таблица шире экрана скроллится ВНУТРИ обёртки, а не тянет всю страницу
       (обёртку доклеивает mobile.js там, где её нет в шаблоне) */
    .table-responsive {
        -webkit-overflow-scrolling: touch;
        overscroll-behavior-x: contain;
    }

    /* ── Формы. 16px — не косметика: при меньшем размере iOS Safari зумит
          страницу на фокусе поля и обратно её уже не отдаёт. ── */
    .form-control,
    .form-select,
    input[type="text"],
    input[type="search"],
    input[type="tel"],
    input[type="number"],
    input[type="date"],
    input[type="time"],
    input[type="password"],
    textarea,
    select {
        font-size: 16px;
    }
    /* Двухстрочная подсказка в textarea обрезалась по нижнему краю поля; на
       телефоне в поле пишут длиннее, чем на десктопе (это единственный ввод) */
    textarea.form-control { min-height: 80px; }
    /* Мелкие поля в тулбарах остаются мелкими по высоте, но не по шрифту */
    .form-control-sm,
    .form-select-sm {
        font-size: 16px;
        padding: 0.2rem 0.4rem;
    }
    /* У селекта справа стоит нарисованная фоном каретка, и общий правый
       паддинг 0.4rem заводил значение прямо под неё: «50» читалось как «5Ө»,
       «— все» — как «— вс⌄». Селекту нужен свой правый отступ. */
    .form-select-sm {
        padding: 0.2rem 1.9rem 0.2rem 0.4rem;
    }

    /* ── Тач-таргеты: палец промахивается по 24px-кнопке ── */
    /* Исключения завёрнуты в :where(), чтобы не давать им специфичности:
       иначе правило «мелких» ниже (0,2,0) проигрывало бы этому (0,4,0), и
       сегменты .btn-group-sm всё равно росли до 40px. */
    .btn:not(:where(.btn-sm, .btn-link, .btn-close, .btn-group-sm > .btn)) {
        min-height: 40px;
    }
    /* Bootstrap делает кнопки мелкими и через ГРУППУ, а не только классом на
       каждой кнопке: без второй строки сегменты внутри .btn-group-sm вырастали
       до 40px и стояли враскоряку рядом с 36px-соседями (тулбар журнала). */
    /* 36, а не 32: 32 — это порог, ниже которого обход помечает цель мелкой,
       то есть ровно граница, а не запас. Палец уверенно берёт от 40 — столько
       получают кнопки действий в карточках (К3). */
    .btn-sm,
    .btn-group-sm > .btn {
        min-height: 36px;
    }
    .btn-close {
        width: 1.1rem;
        height: 1.1rem;
        padding: 0.6rem;
    }
    /* Группы кнопок-фильтров переносятся, а не выдавливают страницу вбок */
    .btn-group,
    .btn-group-sm {
        flex-wrap: wrap;
    }

    /* ── Модалки во весь экран (как .modal-fullscreen-sm-down, но глобально:
          на телефоне окошко с полями по центру всегда проигрывает). ── */
    .modal-dialog {
        max-width: none;
        margin: 0;
    }
    .modal-content {
        min-height: 100dvh;
        border: 0;
        border-radius: 0;
    }
    /* Липкие шапка и подвал: в длинной карточке записи кнопки «Сохранить» и
       крестик иначе уезжают за экран. Фон — только у нейтральных: у цветных
       (bg-primary, bg-danger) свой, и заливка серым сломала бы их. */
    .modal-header,
    .modal-footer {
        position: sticky;
        z-index: 2;
    }
    .modal-header { top: 0; }
    .modal-footer { bottom: 0; }
    .modal-header:not([class*="bg-"]) { background: var(--bp-sheet); }
    .modal-footer:not([class*="bg-"]) { background: var(--bp-sheet); }
    .modal-body { padding: 0.85rem 0.75rem; }
    /* Кнопки подвала — в ряд по ширине экрана, крупным пальцем попадаемо */
    .modal-footer > .btn {
        flex: 1 1 auto;
    }

    /* ── Плавающие кнопки. Две по 56px в нижних углах закрывали данные;
          на телефоне ужимаем и прижимаем к самому краю. ── */
    .chat-dock-launcher {
        width: 44px;
        height: 44px;
        right: 10px;
        /* Низ съедают двое: домашняя полоса iPhone и полоса просьб из зала */
        bottom: calc(10px + env(safe-area-inset-bottom) + var(--bp-floor-alerts, 0px));
    }
    .chat-dock-launcher .bi {
        font-size: 1.05rem;
    }
    /* Окно чата — во весь экран, 360px-панель на 390px смотрелась обрезком.
       max-height обязателен: у панели он задан inline (`calc(100vh - 200px)`)
       и без перебивки выигрывал у height, оставляя 138px пустоты сверху. */
    .app-chat-panel {
        left: 8px !important;
        right: 8px !important;
        bottom: 62px !important;
        width: auto !important;
        max-width: none !important;
        height: calc(100dvh - 130px) !important;
        max-height: none !important;
    }

    /* ── Подвал: на телефоне это просто ссылка, а не полоса в пол-экрана ── */
    .footer-links {
        margin-top: 1.25rem !important;
        padding: 1rem 0 !important;
    }

    /* ── Тултипы Bootstrap на тач раскрываются по тапу и легко вылезают ── */
    .tooltip-inner {
        max-width: calc(100vw - 24px);
    }
    /* Открытый тултип перехватывал тап, предназначенный кнопке под ним:
       палец попадал в подсказку, а не в действие. */
    .tooltip {
        pointer-events: none;
    }
}

/* ═════════════════════════ Мобильный кит ═══════════════════════════════════
 *
 * Один набор приёмов на всю CRM: реализация живёт здесь, страница подключает
 * приём классом или атрибутом. Контракт каждого приёма (что делает, чего НЕ
 * делает, какие `data-*` понимает) — web/README.md → «Мобильная адаптация» →
 * «Кит»; разметочные зацепки — templates/components/mobile_kit.html; JS —
 * static/js/mobile.js.
 *
 * ⚠️ ВСЁ, что начинается на `.bp-`, обязано лежать ВНУТРИ медиа-запроса
 * телефона. За его пределами классы кита не имеют стилей вовсе — иначе они
 * утекут на десктоп, где вёрстка рассчитана на широкий экран. Гейт —
 * tests/unit/web/test_mobile_kit.py.
 */
@media (max-width: 767.98px) {

    /* ─────────── К1. Шторка снизу (`.bp-sheet` + триггер) ──────────────
       Блок остаётся ТЕМ ЖЕ узлом разметки, что на десктопе (Alpine-привязки
       живут как жили) — меняется только способ показа. */
    /* `!important`: mobile.css подключён РАНЬШЕ страничных стилей, и без
       перевеса любое правило страницы той же специфичности на том же узле
       (`.d-flex`, инлайновый `position: sticky` со своим display, локальный
       класс вроде `journal-trainers-bar`) выигрывает порядком подключения —
       шторка остаётся раскрытой на телефоне. Ловилось трижды. Перебить
       сознательно можно только встречным `!important` в странице — тихой
       перебивки без него больше не бывает. */
    .bp-sheet {
        display: none !important;
    }
    .bp-sheet.is-open {
        display: block !important;
        position: fixed;
        left: 0;
        right: 0;
        bottom: 0;
        /* Выше лаунчера чата (1050), ниже модалок Bootstrap (1055): шторка не
           спорит с модалкой, но и не прячется под плавающей кнопкой. */
        z-index: 1052;
        max-height: 85dvh;
        overflow-y: auto;
        overscroll-behavior: contain;
        margin: 0;
        border: 0;
        border-radius: var(--bp-radius-lg) var(--bp-radius-lg) 0 0;
        background: var(--bp-sheet);
        box-shadow: var(--bp-shadow-modal);
        padding-bottom: env(safe-area-inset-bottom);
    }
    .bp-sheet-backdrop {
        position: fixed;
        inset: 0;
        z-index: 1051;
        background: color-mix(in srgb, var(--bp-ink) 45%, transparent);
    }
    .bp-sheet__head {
        position: sticky;
        top: 0;
        z-index: 1;
        display: flex;
        align-items: center;
        gap: 8px;
        padding: 6px 12px;
        background: var(--bp-sheet);
        border-bottom: 1px solid var(--bp-line);
    }
    .bp-sheet__title {
        font-family: var(--ff-body);
        font-size: 0.95rem;
        font-weight: 600;
        color: var(--bp-ink);
        overflow-wrap: break-word;
    }
    .bp-sheet__done {
        margin-left: auto;
        min-height: 44px;
        min-width: 44px;
    }
    /* Внутренний паддинг открытой шторки. `.card-body` (кит), `.row` и
       Bootstrap-колонки (`col-*`) уже приносят свой гаттер — трогаем только
       голый контент без обёртки (ссылки, свитчи), как раньше по одной
       странице дописывали `/staff`, `/admin/users`. */
    .bp-sheet.is-open > :not(.bp-sheet__head):not(.card-body):not(.row):not([class*="col-"]) {
        margin: 0 12px;
    }
    /* Пока шторка открыта, страница под ней не листается */
    body.bp-sheet-open {
        overflow: hidden;
    }
    .bp-sheet-trigger {
        display: inline-flex;
        align-items: center;
        gap: 6px;
        /* Сжимается, но не растёт: длинная сводка переносится внутри кнопки,
           а не выдавливает поле поиска из полки. */
        flex: 0 1 auto;
        min-width: 0;
        min-height: 40px;
        white-space: normal;
        text-align: left;
    }
    .bp-sheet-trigger__summary {
        color: var(--bp-muted);
        font-size: 0.75rem;
        overflow-wrap: break-word;
    }

    /* ─────────── К14. Меню действий «⋯» — модификатор К1 ────────────────
       Три пакета первого захода независимо попросили «меню ⋯ у заголовка»
       для редких кнопок (синк YClients, ведомость, пересчёт снимка): каждая
       заявила «шторки не хватает своего JS», хотя у К1 уже есть всё нужное —
       подложка, Escape, закрытие, запрет вложенности. Заводить вторую шторку
       ради трёх кнопок — дублировать эту механику дважды; вместо этого
       `.bp-menu` — тот же `.bp-sheet` (тот же `data-bp-sheet-open`, тот же
       `window.bpSheet`, ни одной новой строки в mobile.js), только раскладка
       открытого состояния — колонка с зазором, а не блок на 85dvh: три
       кнопки под заголовком не нужно листать, как фильтры. */
    .bp-menu-trigger {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        flex: 0 0 auto;
        min-height: 44px;
        min-width: 44px;
        padding: 0;
    }
    .bp-menu.is-open {
        display: flex !important;
        flex-direction: column;
        align-items: stretch;
        gap: 10px;
        padding-top: 10px;
        /* `.bp-sheet.is-open` уже даёт safe-area отступ снизу (домашняя
           полоса iPhone) — дописываем к нему, а не заменяем тем же свойством. */
        padding-bottom: calc(16px + env(safe-area-inset-bottom));
    }
    /* Боковой отступ детям уже даёт общее правило шторки (12px margin у
       голого контента, см. выше) — здесь только тач-цель по высоте. */
    .bp-menu.is-open > * {
        min-height: 44px;
    }

    /* ─────────── К2. Полка страницы (`.bp-toolbar`) ─────────────────────
       То, что остаётся снаружи шторки: поиск + вход в шторку + 1–2 кнопки. */
    .bp-toolbar {
        display: flex;
        align-items: center;
        gap: 8px;
        margin-bottom: 0.6rem;
    }
    /* basis 8rem, а не auto: поле поиска обязано остаться пригодным для ввода,
       даже когда рядом стоит кнопка с длинной сводкой фильтров. */
    .bp-toolbar > .form-control,
    .bp-toolbar > .input-group,
    .bp-toolbar > input[type="search"],
    .bp-toolbar > .bp-toolbar__grow {
        flex: 1 1 8rem;
        min-width: 0;
    }
    .bp-toolbar--sticky {
        position: sticky;
        top: var(--app-header-h);
        z-index: 2;
        background: var(--bp-sheet);
        padding: 6px 0;
    }

    /* ─────────── К3. Строка таблицы → карточка (`.bp-m-cards`) ──────────
       Подписи ячеек берёт из `<th>` mobile.js и кладёт в `data-m-label`;
       роль ячейки страница задаёт атрибутом `data-m`. */
    table.bp-m-cards,
    table.bp-m-cards > tbody {
        display: block;
        width: 100%;
    }
    table.bp-m-cards > thead {
        display: none;
    }
    table.bp-m-cards > tbody > tr {
        display: flex;
        flex-wrap: wrap;
        align-items: baseline;
        padding: 0.55rem 0.6rem;
        margin-bottom: 0.5rem;
        border: 1px solid var(--bp-line);
        border-radius: var(--bp-radius-sm);
        background: var(--bp-sheet);
    }
    table.bp-m-cards > tbody > tr > td {
        display: flex;
        /* `stretch` (по умолчанию) растягивал `.badge.rounded-pill` до
           высоты многострочного соседа — круглый бейдж превращался в овал
           поверх текста. Прижимаем к верхнему краю строки данных. */
        align-items: flex-start;
        /* Без wrap второй и следующий блочный ребёнок (`td > * + *` ниже,
           `flex: 1 1 100%`) не переносится на свою строку, а жмётся В ОДИН
           РЯД с первым — несколько таких детей (например 3 строки разбивки
           суммы возврата на `/refunds`) суммируют свои естественные ширины
           и вылезают за карточку (измерено: ячейка 368px при контейнере
           364px). `title`/`actions` уже задавали wrap сами — переносим
           правило в общий `td`, чтобы держали контракт ВСЕ роли, включая
           `lead` (сумма + несколько подстрок). */
        flex-wrap: wrap;
        gap: 0.5rem;
        flex: 1 1 100%;
        min-width: 0;
        border: 0;
        padding: 0.15rem 0;
        /* Инлайновые `width`/`min-width` и `overflow-wrap: anywhere` из
           шаблонов рвали слово посреди («Дарья / Карава / ева») — в карточке
           ширина не нужна вовсе, а перенос обязан идти по слову. */
        width: auto !important;
        min-width: 0 !important;
        /* `max-width: none` пускало ячейку шире карточки: `flex: 0 0 auto`
           у роли `lead` не сжимается, а её базовый размер (flex-basis:auto)
           для МНОГОСТРОЧНОГО flex-контейнера — это СУММА max-content всех
           перенесённых строк (`td > * + *` кладёт каждую на свою строку),
           а не ширина самой широкой из них — сумма трёх коротких строк
           разбивки суммы возврата уже больше карточки. 100% относительно
           `tr` (её flex-родителя) — тот же потолок, что уже держит `title`
           и `wide`, никогда не бывает теснее его, поэтому слова по-прежнему
           не режет. */
        max-width: 100% !important;
        white-space: normal !important;
        overflow-wrap: break-word !important;
    }
    /* Десктопные утилиты выравнивания (`.text-end`, `.text-center`) красят
       КОЛОНКУ таблицы, но в карточке колонок нет: подпись `::before` — это
       текст внутри ячейки, и он наследует их выравнивание. Получалась
       лесенка: «Телефон» (без класса) слева, «Брони» (`.text-center`) по
       центру своих 38%, «Баллы» (`.text-end`) прижаты вправо — у каждой
       строки свой отступ, и левый край подписей рвался.
       ⚠️ `!important` обязателен: у bootstrap-утилит он свой, и прежнее
       правило без него (стояло только для `.text-end`) молча проигрывало —
       поэтому лесенка и держалась. `.text-start` перечислять незачем, но и
       не мешает: приводим ячейку к одному виду независимо от того, чем её
       разметили для широкого экрана. */
    table.bp-m-cards > tbody > tr > td,
    table.bp-m-cards > tbody > tr > td.text-start,
    table.bp-m-cards > tbody > tr > td.text-center,
    table.bp-m-cards > tbody > tr > td.text-end {
        text-align: left !important;
    }
    table.bp-m-cards > tbody > tr > td::before {
        content: attr(data-m-label);
        flex: 0 0 38%;
        color: var(--bp-muted);
        font-size: 0.78rem;
        line-height: 1.35;
    }
    /* До 2026-09-04 у самой ячейки не было `flex-wrap` (см. правило `td`
       выше) — второй и следующий блочный ребёнок (например бейдж рядом со
       строкой текста) вставал в ряд с первым БЕЗ возможности перенестись и
       жался до дырки посреди слова; `flex: 1 1 100%` было костылём —
       принудительно уводило КАЖДОГО ребёнка на свою строку целиком, даже
       если сосед короткий и спокойно уместился бы рядом.
       С тех пор у ячейки есть `flex-wrap: wrap` (чинили горизонтальный
       перебор в 4–19px), и он сам открывает переносу дорогу — костыль стал
       двойной ценой: несколько КОРОТКИХ значений (три строки разбивки суммы
       возврата на `/refunds`) занимали три отдельные строки вместо одной,
       раздувая карточку на четверть-треть высоты (замер по 27 экранам —
       `web/README.md` → «Мобильный кит»). `flex: 0 1 auto` — это initial-
       значение флекс-айтема (то же самое, что было бы без этого правила
       вовсе), явное для документации: короткие дети пакуются в ряд, а
       длинный, что не помещается ни с кем, переносится сам — на новую
       строку целиком (wrap) или по слову внутри своей строки
       (`overflow-wrap: break-word` от родителя, наследуется). Ужаться ниже
       переноса по слову ему всё ещё некуда — до символа рвутся только
       технические идентификаторы без пробелов, это разрешено правилом
       переноса CRM (`web/CLAUDE.md`), не костылём этой строки. */
    table.bp-m-cards > tbody > tr > td > * + * {
        flex: 0 1 auto;
    }
    /* Ячейка без значения строкой «подпись: —» не нужна */
    table.bp-m-cards > tbody > tr > td:empty,
    table.bp-m-cards > tbody > tr > td.bp-m-blank,
    table.bp-m-cards > tbody > tr > td[data-m="hide"] {
        display: none !important;
    }
    table.bp-m-cards > tbody > tr > td[data-m="title"] {
        order: -2;
        flex: 1 1 auto;
        align-items: baseline;
        flex-wrap: wrap;
        font-size: 0.95rem;
        font-weight: 600;
    }
    table.bp-m-cards > tbody > tr > td[data-m="lead"] {
        order: -1;
        flex: 0 0 auto;
        margin-left: auto;
        font-weight: 600;
    }
    /* Два лида подряд (например два бейджа-итога) шли вплотную — `~`, а не
       `+`: между ними в разметке могут стоять другие ячейки. */
    table.bp-m-cards > tbody > tr > td[data-m="lead"] ~ td[data-m="lead"] {
        margin-left: 0.4rem;
    }
    table.bp-m-cards > tbody > tr > td[data-m="wide"] {
        display: block;
    }
    table.bp-m-cards > tbody > tr > td[data-m="title"]::before,
    table.bp-m-cards > tbody > tr > td[data-m="lead"]::before,
    table.bp-m-cards > tbody > tr > td[data-m="wide"]::before,
    table.bp-m-cards > tbody > tr > td[data-m="actions"]::before {
        content: none;
    }
    table.bp-m-cards > tbody > tr > td[data-m="title"] a {
        display: inline-flex;
        align-items: center;
        min-height: 44px;
    }
    /* Ссылка в любой другой ячейке карточки (позвонить, перейти) — та же
       тач-цель, что раньше по одной странице копировали три пакета 6
       (`/staff`, `/staff-schedule/journal`, «Исключения»). Специфичнее
       правило выше (44px) для роли `title` не проигрывает — атрибутный
       селектор весит больше. `min-width`: короткая ссылка-число («услуг: 6»)
       без него давала цель шириной в одну цифру — обход `tiny_taps` поймал
       ровно это на `/staff`. */
    table.bp-m-cards > tbody > tr > td a {
        display: inline-flex;
        align-items: center;
        min-height: 40px;
        min-width: 40px;
    }
    table.bp-m-cards > tbody > tr > td[data-m="actions"] {
        order: 9;
        flex-wrap: wrap;
        gap: 8px;
        margin-top: 0.4rem;
    }
    table.bp-m-cards > tbody > tr > td[data-m="actions"] .btn {
        flex: 1 1 auto;
        min-height: 40px;
    }
    /* Группа кнопок в карточке распадается на отдельные кнопки: сегменты в
       ряд по 44px читаются пальцем, слипшаяся группа — нет. */
    table.bp-m-cards > tbody > tr > td[data-m="actions"] .btn-group {
        display: contents;
    }
    /* Строка «ничего не найдено» (одна ячейка с colspan) — не карточка:
       рамка вокруг одинокой фразы читается как пустая запись. */
    table.bp-m-cards > tbody > tr.bp-m-cards__empty {
        display: block;
        border: 0;
        padding: 0.6rem 0;
        text-align: center;
        color: var(--bp-muted);
    }

    /* ─────────── К4. Вложенная прокрутка отдаёт странице ───────────────── */
    .bp-m-flow {
        max-height: none !important;
        height: auto !important;
        overflow: visible !important;
    }

    /* ─────────── К5. Описание под заголовком → «Подробнее» ──────────────
       Текст НЕ обрезается: он скрыт целиком и раскрывается целиком. */
    .bp-intro {
        display: none;
    }
    .bp-intro.is-open {
        display: block;
    }
    /* `data-bp-intro-first`: ведущий кусок, выделенный страницей, виден
       всегда — остальное поднимает тот же чип. */
    .bp-intro[data-bp-intro-first] {
        display: block;
        font-size: 0;
        line-height: 0;
    }
    .bp-intro[data-bp-intro-first] .bp-intro__lead {
        font-size: 0.85rem;
        line-height: 1.5;
    }
    .bp-intro[data-bp-intro-first].is-open {
        font-size: 0.85rem;
        line-height: 1.5;
    }
    .bp-intro-chip {
        position: relative;
        display: inline-flex;
        align-items: center;
        gap: 6px;
        min-height: 44px;
        margin: 0 0 0.6rem;
        padding: 0 0.85rem;
        border: 1px solid var(--bp-line-strong);
        border-radius: 999px;
        background: transparent;
        color: var(--bp-ink-soft);
        font-family: var(--ff-body);
        font-weight: 400;
        font-size: 0.82rem;
        letter-spacing: 0;
        text-align: left;
        vertical-align: middle;
    }
    /* Чип внутри заголовка делит с ним строку: 44px там раздули бы строку
       заголовка. Сам чип 32px, а палец получает свои 44 невидимым слоем. */
    h1 > .bp-intro-chip,
    h2 > .bp-intro-chip,
    h3 > .bp-intro-chip,
    h4 > .bp-intro-chip {
        min-height: 32px;
        margin: 0 0 0 0.5rem;
        padding: 0 0.7rem;
        font-size: 0.76rem;
    }
    h1 > .bp-intro-chip::after,
    h2 > .bp-intro-chip::after,
    h3 > .bp-intro-chip::after,
    h4 > .bp-intro-chip::after {
        content: "";
        position: absolute;
        inset: -6px;
    }

    /* ─────────── К6. Сетки 2/3 в ряд и компактные KPI ───────────────────
       Bootstrap-классы колонок не трогаем — они остаются для десктопа. */
    .bp-m-2col > [class*="col-"],
    .bp-kpi > [class*="col-"] {
        flex: 0 0 50%;
        max-width: 50%;
    }
    .bp-m-3col > [class*="col-"] {
        flex: 0 0 33.3333%;
        max-width: 33.3333%;
    }
    .bp-m-2col > .bp-kpi--full,
    .bp-m-3col > .bp-kpi--full,
    .bp-kpi > .bp-kpi--full {
        flex: 0 0 100%;
        max-width: 100%;
    }
    .bp-kpi .card-body {
        padding: 0.55rem 0.6rem;
    }
    .bp-kpi .card-body h1,
    .bp-kpi .card-body h2,
    .bp-kpi .card-body h3,
    .bp-kpi .card-body h4,
    .bp-kpi .card-body .h1,
    .bp-kpi .card-body .h2,
    .bp-kpi .card-body .h3,
    .bp-kpi .card-body .h4,
    .bp-kpi .card-body .bp-kpi__value {
        font-size: 1.25rem;
        line-height: 1.15;
    }
    .bp-kpi .card-body .card-title,
    .bp-kpi .card-body .bp-kpi__label {
        /* Не nowrap: подпись обязана переноситься, а не обрезаться */
        font-size: 0.72rem;
        white-space: normal;
        overflow-wrap: break-word;
    }
    /* Пара «Период с / по»: ярлыки остаются, но не съедают строку */
    .bp-m-2col .form-label,
    .bp-m-3col .form-label {
        font-size: 0.72rem;
        margin-bottom: 0.2rem;
    }

    /* ─────────── К7. Легенда «Что означают поля» ────────────────────────
       Собирает mobile.js из тултипов `<th>`: в карточном виде шапки нет,
       и объяснения колонок иначе пропадают совсем. */
    .bp-legend {
        margin: 0.2rem 0 0.9rem;
        font-family: var(--ff-body);
        font-size: 0.82rem;
        color: var(--bp-ink-soft);
    }
    .bp-legend > summary {
        display: flex;
        align-items: center;
        min-height: 44px;
        color: var(--bp-blue);
        cursor: pointer;
    }
    .bp-legend dl {
        margin: 0.2rem 0 0;
    }
    .bp-legend dt {
        font-weight: 600;
        color: var(--bp-ink);
    }
    .bp-legend dd {
        margin: 0 0 0.45rem;
        color: var(--bp-muted);
    }

    /* ─────────── К8. Признак горизонтальной прокрутки ───────────────────
       Тень у того края, куда ещё есть что крутить: `local`-слои уезжают
       вместе с содержимым и открывают `scroll`-тень. Цвет «крышек» — фон
       того, на чём стоит скроллер; страница может переопределить его
       собственным `--bp-scroll-bg`. */
    .table-responsive,
    [data-bp-scroll] {
        --bp-scroll-bg: var(--bp-sheet);
    }
    .nav-tabs {
        --bp-scroll-bg: var(--bp-paper);
    }
    .table-responsive,
    [data-bp-scroll],
    .nav-tabs {
        background-image:
            linear-gradient(to right, var(--bp-scroll-bg), var(--bp-scroll-bg)),
            linear-gradient(to left, var(--bp-scroll-bg), var(--bp-scroll-bg)),
            linear-gradient(to right,
                color-mix(in srgb, var(--bp-ink) 18%, transparent), transparent),
            linear-gradient(to left,
                color-mix(in srgb, var(--bp-ink) 18%, transparent), transparent);
        background-position: left center, right center, left center, right center;
        background-repeat: no-repeat;
        background-size: 18px 100%, 18px 100%, 10px 100%, 10px 100%;
        background-attachment: local, local, scroll, scroll;
    }
    .bp-m-sticky-first th:first-child,
    .bp-m-sticky-first td:first-child {
        position: sticky;
        left: 0;
        z-index: 1;
        background: var(--bp-sheet);
        box-shadow: 1px 0 0 var(--bp-line);
    }

    /* К10. Прямые дети меняют порядок отрисовки, узлы не переезжают. */
    .bp-m-stack {
        display: flex;
        flex-direction: column;
    }
    .bp-m-stack > * {
        order: 0;
    }
    .bp-m-stack > .bp-m-head {
        order: -3;
    }
    .bp-m-stack > .bp-m-controls,
    .bp-m-stack > .bp-m-first {
        order: -2;
    }
    .bp-m-stack > .bp-m-second {
        order: -1;
    }

    /* ─────────── К11. Плавающие кнопки ──────────────────────────────────
       «Новая задача для IT…» на телефоне живёт пунктом в подвале drawer'а
       (base.html), лаунчер чата остаётся один и уезжает вниз при прокрутке
       к данным. */
    .app-fab-improve {
        display: none !important;
    }
    .chat-dock-launcher {
        transition: transform 0.18s ease;
    }
    body.bp-fab-hidden .chat-dock-launcher {
        transform: translateY(90px);
    }
    body:has(.bp-no-fab) .chat-dock-launcher {
        display: none;
    }

    /* ─────────── К12. Хром: шапка, вкладки, кнопки, формы ───────────────
       Тач-цели шапки — сквозная находка обхода: они мелкие на КАЖДОМ из 175
       экранов, поэтому чинятся здесь один раз. */
    .app-header .header-right {
        gap: 8px;
    }
    .app-header .dropdown-toggle,
    .app-header .app-icon-btn,
    .app-header .app-profile-link,
    .app-header .app-profile-caret,
    .app-header .staff-feed-bell {
        display: inline-flex;
        align-items: center;
        min-height: 44px;
        min-width: 44px;
    }
    .app-header .app-profile-caret {
        justify-content: center;
    }
    /* Бейдж непрочитанного наезжал на соседнюю иконку */
    .app-header .staff-feed-badge {
        top: -2px;
        right: -6px;
    }
    .app-header .staff-feed-bell {
        justify-content: center;
        margin-right: 0 !important;
    }

    /* Кружок справки не должен падать на свою строку — page-help.js
       оборачивает последнее слово заголовка и иконку в `.page-help-tail`. */
    .page-help-tail {
        white-space: nowrap;
    }
    /* Тач-цель 44px даём НЕВИДИМЫМ слоем поверх: сам кружок стоит в строке
       заголовка, и рост его коробки до 44px раздул бы строку заголовка на
       каждой странице CRM — цена больше пользы. */
    .page-help-icon {
        position: relative;
        display: inline-block;
    }
    .page-help-icon::after {
        content: "";
        position: absolute;
        top: 50%;
        left: 50%;
        width: 44px;
        height: 44px;
        transform: translate(-50%, -50%);
    }

    /* Вкладки — лента вместо 2–4 рядов «обрубков» */
    .nav-tabs {
        flex-wrap: nowrap;
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
        overscroll-behavior-x: contain;
    }
    .nav-tabs .nav-link {
        white-space: nowrap;
        min-height: 44px;
        display: inline-flex;
        align-items: center;
    }

    /* Группа кнопок — ряд чипов: Bootstrap склеивает сегменты
       (`margin-left: -1px`, нулевые радиусы), и на переносе строки они
       выглядят обрубками. */
    .btn-group > .btn,
    .btn-group-sm > .btn {
        margin-left: 0;
        border-radius: var(--bp-radius-sm) !important;
    }
    .btn-group,
    .btn-group-sm {
        gap: 6px;
    }

    /* Тумблер в 24px пальцем не переключается */
    .form-check-input[role="switch"] {
        width: 3rem;
        height: 1.6rem;
    }
    .form-check:has(.form-check-input[role="switch"]) {
        min-height: 44px;
        display: flex;
        align-items: center;
        gap: 0.5rem;
    }
    .form-check:has(.form-check-input[role="switch"]) .form-check-label {
        min-height: 44px;
        display: flex;
        align-items: center;
    }

    /* Подвал: одна строка ссылок по 44px вместо полосы в пол-экрана */
    .footer-links a {
        display: inline-flex;
        align-items: center;
        min-height: 44px;
    }
    .footer-links .container-fluid > .d-flex {
        gap: 0.25rem !important;
    }
    /* Диагностическая плашка гейта (только суперадминам) занимала пять строк
       — больше, чем таблица под ней; на телефоне сворачивается в строку. */
    #bp-gate-badge > summary {
        display: flex;
        align-items: center;
        min-height: 44px;
        cursor: pointer;
    }

    /* ─────────── К13. Утилиты ───────────────────────────────────────────
       `.bp-m-hide` — «на телефоне не показывать»: дубли фильтров, колонки
       из одного значения, экспорт. Никогда не форсирует ПОКАЗ, поэтому
       совместим с `x-show` (в отличие от Bootstrap `d-*`). */
    .bp-m-hide {
        display: none !important;
    }
    /* Кнопки формы липнут к низу экрана — как `.modal-footer` в модалке */
    .bp-m-sticky-actions {
        position: sticky;
        bottom: 0;
        z-index: 2;
        padding: 8px 0;
        background: var(--bp-sheet);
        box-shadow: 0 -1px 0 var(--bp-line);
    }
}
