/* Отступы держим на одной шкале 8 / 16 / 24 / 32 / 48 — числа «на глаз»
   (17, 18, 26, 27) разъезжались между шапкой и контентом.
   `--site-shell` — ширина колонки страницы, `--site-gutter` — её поля;
   и то и другое применяется к шапке, контенту и футеру, поэтому логотип,
   крошки, заголовок и таблицы стоят на одной вертикальной линии при любой
   ширине окна. Меняя гуттер, менять его только здесь и в медиа-запросах.

   Вертикальный ритм верха страницы (линия шапки → крошки → H1 → контент)
   держит отдельный токен `--site-vgap`, а не гуттер: приравняв его к боковому
   полю, мы получили на десктопе три пустые полосы по 32 px подряд. Это рабочие
   экраны, а не витрина, — сверху нужна тонкая рамка, а не поле для дыхания.
   На десктопе гуттер и vgap намеренно расходятся (32 против 8). */
:root {
    --ink: #171717;
    --muted: #6f7275;
    --line: #e9ece8;
    --paper: #ffffff;
    --wash: #f7f8f6;
    --site-shell: 1120px;
    --site-gutter: 16px;
    --site-vgap: 16px;
}

* {
    letter-spacing: 0;
}

body {
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    color: var(--ink);
    background: var(--paper);
    font-family: Inter, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
    font-size: 15px;
}

main {
    flex: 1 0 auto;
}

.site-header {
    position: sticky;
    top: 0;
    z-index: 30;
    background: rgba(255, 255, 255, .96);
    border-bottom: 1px solid var(--line);
    backdrop-filter: blur(14px);
}

/* Содержимое шапки живёт в той же колонке, что и контент страницы
   (`.page-shell`): фон и нижняя линия остаются во всю ширину на
   `.site-header`, а логотип встаёт ровно над крошками и заголовком. */
.header-bar {
    width: min(var(--site-shell), 100%);
    min-height: 51px;
    margin: 0 auto;
    display: grid;
    grid-template-columns: 38px minmax(96px, 141px) 1fr;
    align-items: center;
    gap: 16px;
    padding: 0 var(--site-gutter);
}

/* Вошедший сотрудник: между логотипом и меню встаёт его короткое имя
   («Фамилия И. О.»). Колонка логотипа у этого варианта сжата по содержимому,
   чтобы имя шло сразу за ним, а колонка имени — minmax(0, 1fr): длинная
   фамилия обрезается многоточием, а не выталкивает меню за край экрана. */
.header-bar--user {
    grid-template-columns: 38px auto minmax(0, 1fr) auto;
}

.header-user {
    min-width: 0;
    color: var(--ink);
    font-size: 15px;
    font-weight: 400;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.brand,
.drawer-brand {
    color: var(--ink);
    font-size: 15px;
    font-weight: 600;
    text-decoration: none;
    text-transform: uppercase;
}

.brand img,
.drawer-brand img {
    width: auto;
    max-width: 96px;
    max-height: 27px;
    object-fit: contain;
    display: block;
}

.menu-button {
    width: 38px;
    height: 38px;
    display: inline-grid;
    align-content: center;
    justify-content: center;
    gap: 6px;
    border: 0;
    color: var(--ink);
    background: transparent;
    padding: 0;
}

.menu-button span {
    display: block;
    width: 25px;
    height: 2px;
    background: currentColor;
}

.top-nav {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 28px;
}

.top-nav .nav-link {
    color: var(--ink);
    padding: 4px 0;
    font-size: 13px;
    font-weight: 520;
    text-transform: uppercase;
    text-decoration: none;
}

.top-nav-icon {
    display: none;
    width: 24px;
    height: 24px;
    object-fit: contain;
}

/* Иконка пункта из Bootstrap Icons (страницы-модули: «Заявки», «Личный
   кабинет»). Как и картинка-иконка, показывается только на телефоне —
   на десктопе у пункта остаётся подпись. */
.top-nav-glyph {
    display: none;
    font-size: 22px;
    line-height: 1;
}

/* Счётчики заявок в шапке: сколько сейчас «Заявок принятых» и (правее,
   краснее) «Новых заявок». Пилюли тех же цветов, что плашки статусов в
   списке; индикаторы, а не ссылки. Селекторы через .top-nav: одиночный
   класс проиграл бы по порядку файла базовому .request-status. */
.top-nav .header-counters {
    display: flex;
    align-items: center;
    gap: 8px;
}

/* display у пилюль и контейнера задан своим правилом, поэтому атрибут
   hidden без этих строк молча перестал бы прятать (UA-стиль слабее). */
.top-nav .header-counters[hidden],
.top-nav .header-counter[hidden] {
    display: none;
}

.top-nav .header-counter {
    min-width: 22px;
    padding: 2px 7px;
    text-align: center;
    font-variant-numeric: tabular-nums;
}

.site-drawer {
    --bs-offcanvas-width: min(88vw, 430px);
    border: 0;
    color: var(--ink);
    background: #fff;
}

.drawer-head {
    min-height: 112px;
    display: flex;
    align-items: center;
    gap: 34px;
    padding: 28px 30px 18px;
}

.drawer-close {
    position: relative;
    width: 30px;
    height: 30px;
    flex: 0 0 auto;
    border: 0;
    background: transparent;
    padding: 0;
}

.drawer-close::before,
.drawer-close::after {
    content: "";
    position: absolute;
    top: 1px;
    left: 14px;
    width: 2px;
    height: 28px;
    background: var(--ink);
}

.drawer-close::before {
    transform: rotate(45deg);
}

.drawer-close::after {
    transform: rotate(-45deg);
}

.drawer-body {
    padding: 36px 30px 44px;
}

.drawer-nav {
    display: grid;
    gap: 22px;
}

.drawer-group {
    display: grid;
    gap: 8px;
}

.drawer-link {
    color: var(--ink);
    text-decoration: none;
    text-transform: uppercase;
}

.drawer-parent {
    font-size: 18.48px;
    line-height: 1.18;
    font-weight: 520;
}

.drawer-toggle {
    appearance: none;
    width: auto;
    max-width: 100%;
    display: inline-grid;
    grid-template-columns: auto 14px;
    align-items: center;
    justify-content: start;
    gap: 8px;
    border: 0;
    background: transparent;
    padding: 0;
    text-align: left;
    cursor: pointer;
    font-family: inherit;
}

.drawer-toggle::after {
    content: "";
    width: 9px;
    height: 9px;
    justify-self: end;
    border-right: 1.8px solid #9b9f9c;
    border-bottom: 1.8px solid #9b9f9c;
    transform: translateY(-2px) rotate(45deg);
    transition: transform .18s ease;
}

.drawer-toggle.is-open::after {
    transform: translateY(3px) rotate(225deg);
}

.drawer-link-label {
    min-width: 0;
}

.drawer-submenu {
    display: none;
    gap: 9.6px;
    padding: 0;
}

.drawer-group.is-open > .drawer-submenu {
    display: grid;
}

.drawer-child {
    color: #2c2c2c;
    font-size: 14.9688px;
    line-height: 1.22;
    font-weight: 430;
}

.drawer-category {
    margin-top: 22px;
}

.drawer-subgroup {
    display: grid;
    gap: 8px;
    margin: 0;
}

.drawer-grandchild {
    color: #3b3b3b;
    font-weight: 430;
}

.offcanvas-backdrop.show {
    opacity: 1;
    background: rgba(0, 0, 0, .42);
}

.page-shell {
    width: min(var(--site-shell), 100%);
    margin: 0 auto;
}

/* Заголовок страницы и действия модуля (например «Новая заявка») стоят в
   одной строке: заголовок слева, кнопки справа по нижней линии текста. */
.page-hero {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 16px;
    padding: var(--site-vgap) var(--site-gutter) 0;
}

.hero-copy {
    flex: 1 1 320px;
    max-width: 760px;
}

.page-hero-actions {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
}

.site-breadcrumbs {
    max-width: 100%;
    margin: 0 0 var(--site-vgap);
    overflow-x: auto;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
}

.site-breadcrumbs::-webkit-scrollbar {
    display: none;
}

.site-breadcrumbs ol {
    display: inline-flex;
    align-items: center;
    min-width: max-content;
    gap: 7px;
    margin: 0;
    padding: 0;
    color: var(--muted);
    font-size: 13px;
    line-height: 1.35;
    list-style: none;
    white-space: nowrap;
}

.site-breadcrumbs li {
    display: inline-flex;
    align-items: center;
    gap: 7px;
}

.site-breadcrumbs li + li::before {
    content: "›";
    color: #9a9a9a;
}

.site-breadcrumbs a {
    color: var(--ink);
    text-decoration: none;
}

.site-breadcrumbs a:hover {
    text-decoration: underline;
    text-underline-offset: 3px;
}

.site-breadcrumbs span {
    color: var(--muted);
}

/* Заголовок страницы скрыт (внутренний экран модуля): отступ под крошками
   отдает `padding-top` контент-блока, где стоит h1 экрана. Иначе два отступа
   складываются и заголовок карточки заявки висит на 8 px ниже, чем на списке. */
.page-hero--flush .site-breadcrumbs {
    margin-bottom: 0;
}

/* Размер H1 один на всю публичную часть и на всех ширинах: заголовок страницы
   («Заявки») и заголовок экрана внутри модуля («Заявка №11», «Новая заявка») —
   это один и тот же h1, и разный кегль читался как прыжок при переходе из
   списка в карточку. Отдельного правила под десктоп быть не должно. */
h1 {
    margin: 0;
    font-size: 24px;
    line-height: 1.2;
    font-weight: 500;
}

.content-block {
    padding: var(--site-vgap) var(--site-gutter) 24px;
    max-width: 700px;
    line-height: 1.75;
}

.content-block p {
    margin-bottom: 15px;
}

.child-grid {
    display: grid;
    gap: 1px;
    padding: 0 var(--site-gutter) 40px;
    background: var(--line);
}

.child-card {
    display: grid;
    gap: 10px;
    padding: 14px;
    color: var(--ink);
    background: var(--paper);
    text-decoration: none;
}

.child-card span {
    font-size: 18px;
    font-weight: 500;
}

.site-footer {
    border-top: 1px solid var(--line);
    color: var(--muted);
}

.footer-inner {
    width: min(var(--site-shell), 100%);
    margin: 0 auto;
    padding: 24px var(--site-gutter);
    font-size: 13px;
}

.footer-nav {
    display: flex;
    flex-wrap: wrap;
    gap: 10px 20px;
}

.footer-link {
    color: var(--muted);
    text-decoration: none;
}

.footer-link:hover {
    color: var(--ink);
}

@media (min-width: 992px) {
    :root {
        --site-gutter: 32px;
        /* Вертикаль намеренно не следует за гуттером: боковые поля на десктопе
           широкие (колонка не должна упираться в края экрана), а верх страницы
           должен быть плотным — крошки и заголовок сразу под линией шапки. */
        --site-vgap: 8px;
    }

    .header-bar {
        grid-template-columns: 38px 141px 1fr;
        gap: 24px;
        min-height: 58px;
    }

    .header-bar--user {
        grid-template-columns: 38px auto minmax(0, 1fr) auto;
    }

    .site-drawer {
        --bs-offcanvas-width: min(45vw, 520px);
    }

    .drawer-head {
        padding: 34px 38px 22px;
    }

    .drawer-body {
        padding: 48px 38px 54px;
    }

    .content-block {
        padding-bottom: 48px;
    }

    .child-grid {
        grid-template-columns: repeat(3, 1fr);
        padding-bottom: 48px;
    }
}

@media (max-width: 767.98px) {
    :root {
        --site-gutter: 8px;
        --site-vgap: 8px;
    }

    .header-bar {
        grid-template-columns: 38px minmax(68px, 82px) minmax(0, 1fr);
        gap: 8px;
    }

    /* У вошедшего сотрудника логотип уступает место имени: логотип, фамилия
       и иконки меню в одну строку телефона не помещаются. */
    .header-bar--user {
        grid-template-columns: 38px minmax(0, 1fr) auto;
    }

    .header-bar--user .brand {
        display: none;
    }

    .brand,
    .top-nav {
        min-width: 0;
    }

    .brand img {
        max-width: 78px;
    }

    .top-nav {
        gap: 7px;
    }

    .top-nav .nav-link.has-top-nav-icon {
        position: relative;
        width: 30px;
        height: 38px;
        display: grid;
        place-items: center;
        padding: 0;
    }

    .top-nav .nav-link.has-top-nav-icon .top-nav-icon {
        display: block;
    }

    .top-nav .nav-link.has-top-nav-icon .top-nav-glyph {
        display: block;
    }

    .top-nav .nav-link.has-top-nav-icon .top-nav-text {
        position: absolute;
        width: 1px;
        height: 1px;
        overflow: hidden;
        clip: rect(0 0 0 0);
        clip-path: inset(50%);
        white-space: nowrap;
    }
}


body.drawer-open {
    overflow: hidden;
}

.site-drawer {
    position: fixed;
    inset: 0 auto 0 0;
    z-index: 1055;
    width: min(88vw, 430px);
    color: var(--ink);
    background: #fff;
    transform: translateX(-100%);
    visibility: hidden;
    transition: transform .28s ease, visibility .28s ease;
    overflow-y: auto;
}

.site-drawer.is-open {
    transform: translateX(0);
    visibility: visible;
}

.site-drawer-backdrop {
    position: fixed;
    inset: 0;
    z-index: 1040;
    border: 0;
    background: rgba(0, 0, 0, .42);
    opacity: 0;
    pointer-events: none;
    transition: opacity .28s ease;
}

.site-drawer-backdrop.is-open {
    opacity: 1;
    pointer-events: auto;
}

.site-header {
    z-index: 1200;
}

.menu-button {
    position: relative;
    z-index: 1201;
    cursor: pointer;
    text-decoration: none;
    pointer-events: auto;
}

.site-drawer:target {
    transform: translateX(0);
    visibility: visible;
}

.site-drawer:target + .site-drawer-target-backdrop {
    opacity: 1;
    pointer-events: auto;
}

.site-drawer-target-backdrop {
    position: fixed;
    inset: 0;
    z-index: 1040;
    background: rgba(0, 0, 0, .42);
    opacity: 0;
    pointer-events: none;
    transition: opacity .28s ease;
}

.drawer-close {
    display: block;
    color: var(--ink);
    text-decoration: none;
    cursor: pointer;
    z-index: 1060;
}

.drawer-close::before,
.drawer-close::after {
    display: block;
}

.site-drawer {
    z-index: 1300;
}

.site-drawer-backdrop,
.site-drawer-target-backdrop {
    z-index: 1290;
}

.drawer-close {
    z-index: 1310;
}

.drawer-head {
    position: relative;
    z-index: 1310;
}

.drawer-close {
    display: flex !important;
    align-items: center;
    justify-content: center;
    width: 54px;
    height: 54px;
    color: var(--ink) !important;
    font-size: 56px;
    font-weight: 300;
    line-height: 1;
    text-decoration: none !important;
}

.drawer-close::before,
.drawer-close::after {
    content: none !important;
}

/* Final drawer behavior: the header stays fixed, and the hamburger turns into the close control. */
.site-header {
    z-index: 1400;
}

.menu-button {
    position: relative;
    display: block;
    width: 38px;
    height: 38px;
    z-index: 1401;
}

.menu-button span {
    position: absolute;
    left: 6px;
    top: 50%;
    width: 25px;
    height: 2px;
    background: currentColor;
    transform-origin: center;
    transition: transform .22s ease, opacity .16s ease;
}

.menu-button span:nth-child(1) {
    transform: translateY(-8px);
}

.menu-button span:nth-child(2) {
    transform: translateY(0);
}

.menu-button span:nth-child(3) {
    transform: translateY(8px);
}

body.drawer-open .menu-button span:nth-child(1) {
    transform: translateY(0) rotate(45deg);
}

body.drawer-open .menu-button span:nth-child(2) {
    opacity: 0;
}

body.drawer-open .menu-button span:nth-child(3) {
    transform: translateY(0) rotate(-45deg);
}

.site-drawer {
    z-index: 1300;
}

.site-drawer-backdrop,
.site-drawer-target-backdrop {
    z-index: 1250;
}

.drawer-head {
    display: none !important;
}

.drawer-body {
    padding: 30px 30px 44px;
}

.drawer-child {
    font-size: 14.9688px;
    line-height: 1.28;
}

.menu-button {
    transform: translateY(-2px);
}

body.drawer-open .menu-button span:nth-child(1) {
    transform: translateY(0) rotate(45deg);
}

body.drawer-open .menu-button span:nth-child(3) {
    transform: translateY(0) rotate(-45deg);
}

.menu-button {
    transform: none;
}

/* Логотип не сдвигаем: раньше он оптически подтягивался к кнопке-бургеру
   (translate -2px / -6px), а после скрытия drawer-меню этот сдвиг уводил
   его левее крошек и заголовка. Выравнивание держит общий `--site-gutter`. */

/* Модуль «Сотрудники»: блок входа по коду и личный кабинет на странице-модуле.
   Блок прижат к левому краю колонки, как крошки, заголовок и весь остальной
   контент сайта: центрированная коробка висела в случайном месте экрана. */
.employee-auth {
    display: flex;
    justify-content: flex-start;
}

.employee-login-box {
    width: 100%;
    max-width: 420px;
}

.employee-login-note {
    color: #6c757d;
    margin-bottom: 16px;
}

.employee-auth .login-actions {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 6px;
}

/* Точка смены: один и тот же блок на странице входа сотрудника и вместо
   содержимого модуля заявок, пока смена не выбрана. Ширина — как у формы
   входа: это продолжение того же диалога, а не отдельная панель. */
.employee-shift-box {
    width: 100%;
    max-width: 420px;
}

/* Разделитель перед «Выйти»: обе кнопки чёрные и во всю ширину, без линии
   выход читался бы как продолжение выбора точки. */
.employee-shift-sep {
    margin: 24px 0;
    border-top-color: #e3e3e3;
}

/* Модуль «Заявки»: список заявок, форма формирования, экраны взвешивания и приемки.
   Ширину не режем: блок должен начинаться и заканчиваться там же, где крошки и
   заголовок страницы (`.page-hero` внутри `.page-shell`), — горизонтальные padding
   у `.content-block` и `.page-hero` совпадают в обоих брейкпоинтах. */
.supply-requests {
    max-width: none;
}

/* Действия карточки заявки живут в строке заголовка (отдельная панель под
   метаданными занимала целую строку ради одной-двух кнопок), поэтому кнопки
   здесь компактные — по высоте плашки статуса, а не btn-lg. */
.request-head-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

.request-head-actions .btn {
    padding: 3px 11px;
    font-size: 13.5px;
    border-radius: 8px;
}

.request-back {
    display: inline-block;
    margin-bottom: var(--site-vgap);
    color: #6c757d;
    text-decoration: none;
}

.request-back:hover {
    color: var(--ink, #17191f);
}

/* Заголовок экрана внутри модуля — это h1 страницы (заголовок самой страницы на
   внутренних экранах скрыт), размер и межстрочный интервал берутся у общего h1.
   Здесь остается только просвет под заголовком: он равен `--site-vgap`, тому же
   отступу, что отделяет h1 списка заявок от таблицы. */
.request-subtitle {
    margin-bottom: var(--site-vgap);
}

.request-head {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 14px;
    margin-bottom: var(--site-vgap);
}

.request-head .request-subtitle {
    margin-bottom: 0;
}

/* Статус и фото бланка переносятся вместе: камера относится ко всей заявке. */
.request-head-status {
    display: flex;
    align-items: center;
    gap: 8px;
}

.request-scan-photo-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    min-height: 32px;
    padding: 3px 8px;
    border: 1px solid #c2c8d2;
    border-radius: 8px;
    background: #fff;
    color: #3f4652;
    font-size: 13px;
    font-variant-numeric: tabular-nums;
}

.request-scan-photo-button[hidden] {
    display: none;
}

.request-scan-photo-button:hover {
    border-color: var(--ink);
    color: var(--ink);
}

.request-note {
    color: #6c757d;
}

.request-route-arrow {
    color: #6c757d;
    padding: 0 2px;
}

.request-link {
    font-weight: 600;
}

/* Плашка статуса — подпись к строке, а не кнопка: держим её мельче текста
   таблицы, иначе в списке она перетягивает внимание на себя. Ниже 12px не
   опускать — статусы читают с планшета в цеху.

   Цвета ведут заявку от тревожного к спокойному: красная «Новая заявка» зовёт
   склад, жёлтая и зелёная — работа идёт, фиолетовая и серые — разбирать больше
   нечего. Тона приглушённые: светлый фон + насыщенный, но не чистый текст того
   же семейства; чистых цветов в плашках не заводить. */
.request-status {
    display: inline-block;
    padding: 2px 9px;
    border-radius: 999px;
    font-size: 12px;
    font-weight: 550;
    white-space: nowrap;
}

/* Новая заявка: создана и ждёт печати бланка — самый заметный статус в списке. */
.request-status.status-request {
    background: #f8d7da;
    color: #b02a37;
}

/* Заявка на складе: бланк напечатан, склад собирает продукцию. */
.request-status.status-warehouse {
    background: #fff3cd;
    color: #8a6d00;
}

/* Заявка в пути: отправка создана, товар едет на точку, идет приемка. */
.request-status.status-shipment {
    background: #d1e7dd;
    color: #146c43;
}

/* Заявка принята: приход создан, ждет печати итогового документа и закрытия. */
.request-status.status-arrival {
    background: #e8e6f8;
    color: #5a4fa8;
}

/* Заявка выполнена: финал — заявка отработана и закрыта, действий больше нет. */
.request-status.status-closed {
    background: #e2e3e5;
    color: #41464b;
}

/* Строка своего create-черновика в списке заявок. */
.request-status.status-draft {
    background: #e9ecef;
    color: #495057;
}

.request-draft-delete {
    margin-left: 10px;
    padding: 1px 7px;
    font-size: 12px;
    border-radius: 8px;
}

/* Фильтр списка заявок по статусам: кнопка в строке заголовка и окошко с
   чекбоксами. Кнопка остаётся контурной и при включённом фильтре — рядом
   стоит чёрная «Новая заявка», две сплошные тёмные кнопки в шапке спорят
   друг с другом; что фильтр активен, показывает счётчик выбранных. */
.request-filter-btn {
    display: inline-flex;
    align-items: center;
    gap: 7px;
}

.request-filter-count {
    min-width: 18px;
    padding: 0 5px;
    border-radius: 999px;
    background: #212529;
    color: #fff;
    font-size: 11px;
    font-weight: 600;
    line-height: 18px;
    text-align: center;
}

.request-filter-menu {
    min-width: 220px;
    padding: 10px;
    border: 1px solid #e6e6e6;
    border-radius: 14px;
    box-shadow: 0 12px 28px rgba(0, 0, 0, .12);
}

.request-filter-tools {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 1px 6px 6px;
}

.request-filter-tools button {
    padding: 0;
    border: 0;
    background: transparent;
    color: #6c757d;
    font-size: 12px;
    font-weight: 600;
    cursor: pointer;
}

.request-filter-tools button:hover {
    color: #212529;
    text-decoration: underline;
}

.request-filter-option {
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 5px 6px;
    border-radius: 8px;
    cursor: pointer;
}

.request-filter-option:hover {
    background: #f4f5f7;
}

.request-filter-option .form-check-input {
    flex: 0 0 auto;
    margin: 0;
    cursor: pointer;
}

.request-filter-actions {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 8px;
    margin-top: 8px;
    padding-top: 8px;
    border-top: 1px solid #ececec;
}

.request-table th {
    white-space: nowrap;
}

/* Серверная пагинация списка заявок: итог относится только к настоящим
   заявкам. Личные черновики закреплены сверху первой страницы сверх лимита. */
.request-pagination {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 10px 16px;
    margin-top: 16px;
}

.request-pagination-summary {
    color: #6c757d;
    font-size: 13px;
    font-weight: 600;
}

.request-pager {
    display: inline-flex;
    align-items: center;
    overflow: hidden;
    border: 1px solid #dee2e6;
    border-radius: 10px;
    background: #fff;
}

.request-page {
    min-width: 38px;
    height: 38px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0 11px;
    border-right: 1px solid #dee2e6;
    color: #212529;
    background: #fff;
    font-size: 13px;
    font-weight: 600;
    text-decoration: none;
}

.request-page:last-child {
    border-right: 0;
}

.request-page:hover {
    color: #212529;
    background: #f1f3f5;
    text-decoration: none;
}

.request-page.is-active {
    color: #fff;
    background: #212529;
}

.request-page.is-disabled,
.request-page-ellipsis {
    color: #adb5bd;
    background: #f8f9fa;
    pointer-events: none;
}

.request-page-icon {
    padding: 0;
}

.request-page-position {
    display: none;
}

/* Номер из печатного бланка: узкая колонка, на телефоне — перед названием. */
.request-table .request-position-no {
    width: 1%;
    text-align: right;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

.request-table td.request-position-no,
.request-position-prefix {
    color: #6c757d;
}

.request-position-prefix {
    display: none;
}

.request-meta {
    background: #f8f9fa;
    border-radius: 14px;
    padding: 8px;
    margin-bottom: 16px;
}

.request-meta-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px 12px;
    min-height: 26px;
}

/* Отступы между строками держим на margin, а не на grid gap: скрытый блок
   этапов — тоже строка блока, и при раскрытии он мгновенно добавлял два
   зазора (16px), а уже потом плавно тянулся — то самое подёргивание.
   Теперь весь прирост высоты, включая отступ первой строки этапов, живёт
   внутри collapse и анимируется вместе с ним. */
.request-meta > .request-meta-row:not(:first-child) {
    margin-top: 8px;
}

/* Ширина колонки подписей рассчитана на самый длинный статус: подписи строк —
   это статусы, и при прежних 110px, посчитанных под слово «Печать», значения
   этапов вставали по рваной вертикали. Больше 132px не ставить: «Заявка
   выполнена» занимает 131px, а на телефоне 393px строке остаётся 361px, из
   которых 211px забирает значение «Фамилия И. О., 11 авг 15ч 06м» — при 142px
   значение переносилось под подпись, и блок этапов вырастал вдвое. */
.request-meta-row > span:first-child {
    min-width: 132px;
    color: #6c757d;
}

/* Значения этапов держим обычным начертанием: жирный в блоке из шести строк
   превращал справку в заголовки. */
.request-meta-value {
    color: var(--ink);
}

/* Свёрнутые этапы: в блоке видны маршрут и последний этап, остальные
   раскрывает стрелка справа от последнего этапа. Отступ сверху — padding
   контейнера внутри collapse, чтобы он входил в анимируемую высоту. */
.request-meta-stages {
    padding-top: 8px;
}

.request-meta-stages > .request-meta-row + .request-meta-row {
    margin-top: 8px;
}

.request-stages-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    padding: 0;
    color: #6c757d;
    background: transparent;
    border: 0;
    border-radius: 999px;
    line-height: 1;
}

.request-stages-toggle:hover {
    color: var(--ink);
    background: #eceef0;
}

/* Пока блок раскрыт, стрелку «развернуть» гасим — работает верхняя. Прячем
   через visibility: display: none укоротил бы строку последнего этапа.
   Иконки у кнопок разные (вниз/вверх), поэтому крутить их не нужно: поворот
   на 180° во время роста блока читался как второе, лишнее движение. */
.request-stages-toggle--show {
    /* Обратно стрелка «развернуть» проявляется только после того, как блок
       схлопнулся (длительность collapse у Bootstrap — .35s): иначе во время
       сворачивания на экране на треть секунды видны сразу две стрелки. */
    transition: visibility 0s .35s;
}

.request-stages-toggle--show[aria-expanded="true"] {
    visibility: hidden;
    transition: visibility 0s;
}

@media (prefers-reduced-motion: reduce) {
    .request-stages-toggle--show {
        transition: none;
    }
}

.request-points {
    display: flex;
    flex-wrap: wrap;
    gap: 16px;
    margin-bottom: 18px;
}

.request-point-field {
    flex: 1 1 240px;
}

.request-search {
    display: flex;
    align-items: center;
    gap: 16px;
    margin-bottom: 6px;
}

.request-search .form-control {
    flex: 1 1 auto;
    min-width: 0;
    min-height: 48px;
}

/* Ссылка «Скрыть пустые» / «Показать все» стоит в строке поиска справа: форма
   охватывает весь ассортимент, и перепроверить заполненное перед отправкой
   иначе можно только прокруткой всего списка. Оформлена как «← К заявке»:
   приглушенная ссылка, а не кнопка, — это вспомогательный режим просмотра. */
.request-search-toggle {
    flex: 0 0 auto;
    color: #6c757d;
    text-decoration: none;
    white-space: nowrap;
}

.request-search-toggle:hover {
    color: var(--ink, #17191f);
}

.request-category-title {
    font-size: 19px;
    margin: 26px 0 10px;
}

.request-product-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    padding: 9px 0;
    border-bottom: 1px solid #eceff1;
}

.request-product-name {
    flex: 1 1 auto;
}

/* Крупные контролы количества: планшет — основной сценарий использования */
.request-qty-control {
    display: flex;
    align-items: center;
    gap: 8px;
    flex: 0 0 auto;
}

.request-qty-btn {
    width: 44px;
    height: 44px;
    border-radius: 12px;
    border: 1px solid #ced4da;
    background: #fff;
    font-size: 22px;
    line-height: 1;
    color: #333;
}

.request-qty-btn:active {
    background: #e9ecef;
}

.request-qty-input {
    width: 92px;
    min-height: 44px;
    text-align: center;
    font-size: 17px;
}

.request-qty-input.is-invalid {
    border-color: #dc3545;
}

/* Расхождение факта прихода с весом отправки; ставится из requests.js после blur. */
.request-qty-input.request-qty-mismatch {
    border-color: #dc3545;
}

.request-qty-unit {
    color: #6c757d;
    font-size: 14px;
}

/* В форме заявки подпись единицы стоит последней в строке позиции; резерв под
   самую длинную («порц») держит правый край строк ровным. В таблицах отправки,
   прихода и итога единицы живут отдельной колонкой (.request-unit-col). */
.request-qty-control .request-qty-unit {
    min-width: 40px;
}

/* Единица измерения вынесена из значений в свою колонку — иначе подписи разной
   ширины («кг», «шт», «порц») не давали числовым колонкам общий правый край:
   при постоянном зазоре до поля разъезжались правые края, при общем правом крае
   плавал зазор. Отдельной колонкой снимается и повтор единицы в каждой из
   четырёх колонок итоговой таблицы. Порядок колонок как в печатном бланке:
   позиция → единица → числа. */
.request-unit-col {
    width: 1%;
    white-space: nowrap;
    color: #6c757d;
    /* По правому краю, как числовые колонки: единицы разной длины («кг», «шт»,
       «порц») встают на общую вертикаль, и зазор до числа не пляшет по строкам. */
    text-align: right;
}

/* Числовые колонки сжаты по содержимому, весь остаток ширины забирает колонка
   с названием позиции. Без этого свободное место делили между собой «Позиция»
   и «Заказано», и колонка «Ед. изм.» повисала где-то посередине пустой строки,
   оторванная и от названия, и от числа. Ширину задаём только на `th`: ширину
   колонки браузер считает по всей колонке, а thead на телефоне скрыт — значит
   мобильная grid-раскладка строк остаётся нетронутой. */
th.request-num-col {
    width: 1%;
}

.request-fact-col {
    /* `width: 1%` — приём сжатия колонки по содержимому: свободное место
       таблица отдаёт колонке с названием позиции, а эта остаётся по клетке
       ввода. Без него колонка растягивалась на весь остаток ширины и уезжала
       от заголовка на две сотни пикселей. */
    width: 1%;
    white-space: nowrap;
}

th.request-fact-col {
    /* Заголовок и клетка ввода прижаты к правому краю колонки, поэтому их
       правые края совпадают в любой строке — как «Заказано» со своими числами. */
    text-align: right;
}

.request-fact-control {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 8px;
}

.request-submit-bar {
    margin-top: 26px;
}

.request-submit-bar .btn {
    min-width: 240px;
    min-height: 52px;
}

/* Расхождение «отправлено vs принято» — красным. Селектор с td обязателен:
   bootstrap красит каждую ячейку правилом `.table > :not(caption) > * > *`,
   у которого специфичность выше одиночного класса, поэтому одинокий
   .request-diff давал только жирное начертание, а цвет оставался чёрным. */
.request-table td.request-diff {
    color: #e60000;
    font-weight: 600;
}

/* Комментарий к приходу: блок под таблицей позиций. Ширину берёт у колонки
   страницы, но не растягивается во всю: строка текста длиннее ~70 знаков
   читается тяжело. */
.request-comment {
    max-width: 720px;
    margin-top: 24px;
}

.request-comment .form-label {
    margin-bottom: 6px;
    font-weight: 650;
}

.request-comment textarea {
    resize: vertical;
}

.request-comment-status {
    display: block;
    min-height: 18px;
    margin-top: 4px;
    color: #6c757d;
    font-size: .8rem;
}

/* Не сохранилось: тот же индикатор, но текст читается как отказ, а не как
   служебная подпись. */
.request-comment-status.is-error {
    color: #e60000;
}

/* Чужие комментарии: под своим полем, только на чтение. Приёмку ведут вдвоём,
   у каждого своя запись — поэтому чужой текст показывается карточкой с
   подписью, а не подставляется в поле. Тот же список — на завершённой заявке,
   где своё поле уже закрыто и карточки идут все подряд. */
.request-comment-feed {
    display: grid;
    gap: 8px;
}

.request-comment-feed:not(:empty) {
    margin-top: 12px;
}

.request-comment-card {
    padding: 10px 12px;
    border: 1px solid #e6e8ec;
    border-radius: 10px;
    background: #f8f9fb;
}

.request-comment-head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 4px 10px;
    margin-bottom: 4px;
}

.request-comment-author {
    font-weight: 650;
    font-size: .875rem;
}

.request-comment-at {
    color: #6c757d;
    font-size: .8rem;
}

.request-comment-body {
    margin: 0;
    color: #3f4652;
    white-space: pre-line;
}

/* Автосохранение черновиков заявок: плашка восстановления и тихий индикатор. */
.request-draft-banner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 10px 14px;
}

.request-draft-banner .btn {
    white-space: nowrap;
}

.request-submit-bar {
    display: flex;
    align-items: center;
    gap: 14px;
    flex-wrap: wrap;
}

.request-draft-status {
    color: #6c757d;
    font-size: .85rem;
}

.request-draft-status:empty {
    display: none;
}

/* Модальные окна уведомлений и подтверждений — стиль стандартной админки
   (.admin-notice-modal / .admin-confirm-modal в admin.css). */
.site-notice-modal .modal-dialog {
    width: min(100% - 28px, 430px);
}

.site-notice-modal .modal-content {
    border: 1px solid rgba(255, 255, 255, .74);
    border-radius: 24px;
    background: rgba(255, 255, 255, .9);
    box-shadow: 0 22px 65px rgba(30, 36, 48, .12);
    backdrop-filter: blur(28px) saturate(1.35);
}

.site-notice-modal .modal-body {
    display: grid;
    gap: 14px;
    padding: 22px;
}

.site-notice-modal h2 {
    margin: 0;
    font-size: 18px;
    font-weight: 760;
}

.site-notice-modal p {
    margin: 0;
    color: #3f4652;
    font-size: 13px;
    line-height: 1.45;
}

.site-notice-modal .btn {
    justify-self: end;
}

.confirm-modal-actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: 8px;
}

.site-confirm-modal .btn {
    justify-self: auto;
}

.site-confirm-modal p {
    white-space: pre-line;
    overflow-wrap: anywhere;
}

/* Подтверждение может открываться поверх другого открытого окна (удаление
   фото из окна фото позиции, перезапись значений из окна распознавания).
   Все модалки Bootstrap лежат на одном z-index (1055), и выше оказалась бы
   та, что позже в DOM, — а confirm подключён в разметке раньше остальных.
   Поэтому confirm поднят отдельно: он всегда верхний. */
.site-confirm-modal {
    --bs-modal-zindex: 1075;
    z-index: 1075;
}

/* Распознавание фото бланка заявки: окно загрузки снимков и экран сверки. */
.site-recognize-modal .modal-dialog {
    width: min(100% - 28px, 720px);
}

.site-recognize-modal .modal-body {
    gap: 18px;
}

.site-recognize-modal p {
    margin: 0;
}

/* Шаг модалки — собственная сетка: иначе всё внутри шага слипается,
   потому что gap задан только у modal-body. */
.site-recognize-modal [data-recognize-step] {
    display: grid;
    gap: 18px;
}

.site-recognize-modal [data-recognize-step][hidden] {
    display: none;
}

/* Кнопки внутри окна не тянуть вправо: justify-self: end приходит
   от .site-notice-modal и ломает центровку в дропзоне. */
.site-recognize-modal .btn {
    justify-self: auto;
}

.recognize-dropzone {
    display: grid;
    justify-items: center;
    gap: 10px;
    padding: 32px 20px;
    border: 1.5px dashed #c2c8d2;
    border-radius: 18px;
    background: rgba(255, 255, 255, .6);
    text-align: center;
    transition: border-color .15s ease, background-color .15s ease;
}

.recognize-dropzone.is-dragover {
    border-color: var(--ink, #17191f);
    background: rgba(23, 25, 31, .04);
}

.recognize-dropzone .bi {
    font-size: 26px;
    color: #6c757d;
}

.recognize-dropzone-text {
    color: #6c757d;
    font-size: 14px;
}

/* Кнопки дропзоны в ряд: «Снять фото» (её показывает requests.js только на
   устройствах с камерой) и «Выбрать файлы». */
.recognize-dropzone-actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 8px;
    margin-top: 6px;
}

/* Нижняя зона окна отделена линией, как нижняя зона форм админки. */
.site-recognize-modal .confirm-modal-actions {
    gap: 10px;
    padding-top: 16px;
    border-top: 1px solid rgba(23, 25, 31, .1);
}

.recognize-files {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
}

.recognize-files:empty {
    display: none;
}

.recognize-file {
    position: relative;
    width: 92px;
    height: 92px;
    border-radius: 14px;
    overflow: hidden;
    box-shadow: 0 2px 10px rgba(30, 36, 48, .14);
}

.recognize-file img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.recognize-file-remove {
    position: absolute;
    top: 4px;
    right: 4px;
    width: 22px;
    height: 22px;
    padding: 0;
    border: none;
    border-radius: 50%;
    background: rgba(23, 25, 31, .68);
    color: #fff;
    font-size: 15px;
    line-height: 1;
}

.recognize-loading {
    display: grid;
    justify-items: center;
    gap: 14px;
    padding: 26px 0;
    text-align: center;
}

.recognize-table th,
.recognize-table td {
    vertical-align: middle;
}

.recognize-table .request-qty-input {
    max-width: 110px;
}

/* Неуверенное распознавание и пустые клетки — то, что нужно просмотреть глазами. */
.recognize-table .request-qty-input.is-unsure {
    border-color: #e0a800;
    background: #fff8e5;
}

.recognize-table .request-qty-input.is-empty {
    background: #f4f5f7;
}

/* Фото позиций на экране «Создать приход»: кнопка-камера в строке таблицы
   со счётчиком приложенных снимков; само окно переиспользует стили
   распознавания (дропзона, миниатюры). */
.request-photo-col {
    width: 56px;
    text-align: center;
}

.request-photo-btn {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 42px;
    height: 42px;
    padding: 0;
    border: 1px solid #c2c8d2;
    border-radius: 12px;
    background: #fff;
    color: #3f4652;
    font-size: 17px;
    transition: border-color .15s ease, color .15s ease;
}

.request-photo-btn:hover {
    border-color: var(--ink, #17191f);
    color: var(--ink, #17191f);
}

.request-photo-count {
    position: absolute;
    top: -7px;
    right: -7px;
    min-width: 18px;
    height: 18px;
    padding: 0 5px;
    border-radius: 9px;
    background: var(--ink, #17191f);
    color: #fff;
    font-size: 11px;
    font-weight: 650;
    line-height: 18px;
}

.site-item-photo-modal .item-photo-name {
    font-weight: 650;
}

/* Счётчик «Фото: N из 5» слева, кнопка «Готово» справа. */
.site-item-photo-modal .confirm-modal-actions {
    justify-content: space-between;
    align-items: center;
}

.item-photo-counter {
    color: #6c757d;
    font-size: .85rem;
}

/* Снимок, который ещё грузится: полупрозрачный, со спиннером по центру. */
.recognize-file.is-uploading img {
    opacity: .45;
}

.recognize-file .item-photo-spinner {
    position: absolute;
    top: 50%;
    left: 50%;
    margin: -8px 0 0 -8px;
    color: #3f4652;
}

/* Лимит в 5 фото достигнут: дропзона гаснет, кнопка выбора выключена. */
.recognize-dropzone.is-full {
    opacity: .55;
}

/* Описание позиции — под миниатюрами, отделено от них воздухом. */
.item-photo-note {
    margin-top: 14px;
}

.item-photo-note .form-label {
    margin-bottom: 6px;
    font-weight: 650;
}

.item-photo-note textarea {
    resize: vertical;
}

.item-photo-note-status {
    display: block;
    min-height: 18px;
    margin-top: 4px;
    color: #6c757d;
    font-size: .8rem;
}

/* Точка на кнопке-камере: у позиции есть описание. Слева, чтобы не столкнуться
   со счётчиком фото справа. */
.request-photo-btn.has-note::before {
    content: "";
    position: absolute;
    top: -4px;
    left: -4px;
    width: 10px;
    height: 10px;
    border: 2px solid #fff;
    border-radius: 50%;
    background: #b4881c;
}

/* Итоговая таблица завершённой заявки: фото и пометка приёмщика открываются
   тем же окном, что на приёмке, — только для чтения. Кнопок в разметке две,
   видимость разводит медиазапрос: на десктопе колонка-камера справа (как на
   экране «Создать приход» — тот же жест на том же месте), на телефоне значок
   за названием позиции. Кнопка 42px в карточке позиции вставала отдельной
   строкой и растила её высоту, а значок помещается в строку названия. */
.request-table--final .request-photo-col {
    width: 56px;
    text-align: center;
}

/* На приёмке камера — рабочая кнопка под палец (42px), здесь она справочная:
   в полный размер она поднимала строку с фото на 16px над соседними, и в листе
   на сотню позиций строки заметно прыгали. */
.request-table--final .request-photo-btn {
    width: 30px;
    height: 30px;
    border-radius: 9px;
    font-size: 14px;
}

.request-table--final .request-photo-count {
    top: -6px;
    right: -6px;
    min-width: 16px;
    height: 16px;
    padding: 0 4px;
    border-radius: 8px;
    font-size: 10px;
    line-height: 16px;
}

.request-table--final .request-photo-btn.has-note::before {
    top: -3px;
    left: -3px;
    width: 9px;
    height: 9px;
}

.request-name-mark {
    position: relative;
    display: none;
    align-items: center;
    gap: 4px;
    margin-left: 8px;
    padding: 1px 7px 1px 6px;
    border: 1px solid #c2c8d2;
    border-radius: 999px;
    background: #fff;
    color: #3f4652;
    font-size: 12.5px;
    line-height: 18px;
    vertical-align: 2px;
}

.request-name-mark .bi {
    font-size: 13px;
}

/* Точка «есть описание» — та же, что на кнопке-камере. */
.request-name-mark.has-note::before {
    content: "";
    position: absolute;
    top: -3px;
    left: -3px;
    width: 9px;
    height: 9px;
    border: 2px solid #fff;
    border-radius: 50%;
    background: #b4881c;
}

/* История ввода в колонке «Принято» (завершённая заявка): приход заполняют
   двое, и по журналу видно, чей ввод стоял в поле. Триггером сделано само
   значение, а не отдельная иконка, — история относится ровно к нему; поэтому
   кнопка выглядит текстом с пунктирным подчёркиванием, а не кнопкой. Мышью
   поповер открывается по наведению, пальцем — по тапу (см. requests.js). */
.request-qty-log {
    padding: 0;
    border: 0;
    border-bottom: 1px dashed #9aa0a6;
    background: none;
    color: inherit;
    font: inherit;
    line-height: inherit;
    cursor: pointer;
}

.request-qty-log:hover,
.request-qty-log:focus-visible {
    border-bottom-color: var(--ink);
}

/* Ширину считаем от экрана: на телефоне поповер иначе упрётся в края. */
.popover.request-log-popover {
    max-width: min(320px, calc(100vw - 32px));
    border-color: var(--line);
    border-radius: 12px;
    box-shadow: 0 12px 28px rgba(23, 23, 23, .14);
}

.popover.request-log-popover .popover-header {
    border-bottom-color: var(--line);
    background: var(--wash);
    border-radius: 12px 12px 0 0;
    font-size: 13px;
    font-weight: 600;
}

.popover.request-log-popover .popover-body {
    padding: 8px 12px;
}

.request-log-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

/* Записи идут сверху вниз — от первого ввода к последней правке, как в
   блоке этапов заявки и в таблице «История ввода» в админке. */
.request-log-row + .request-log-row {
    padding-top: 8px;
    border-top: 1px solid var(--line);
}

.request-log-head {
    color: var(--muted);
    font-size: 12px;
    line-height: 1.3;
}

.request-log-change {
    font-size: 14px;
    line-height: 1.4;
}

/* Прежнее значение приглушено — как в админской истории ввода. */
.request-log-from {
    color: var(--muted);
}

/* Описание в окне просмотра: тот же текст, что приёмщик вводил в поле. */
.item-photo-note-text {
    margin: 0;
    color: #3f4652;
    white-space: pre-line;
}

/* Фото бланков: попытки разделены, лист целиком вписан в миниатюру. */
.site-scan-photo-modal {
    z-index: 1450;
}

/* Длинное окно доходит до верха экрана: шапка (1400) не должна его закрывать. */
body:has(.site-scan-photo-modal.show) .modal-backdrop {
    z-index: 1440;
}

.scan-photo-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-shrink: 0;
    gap: 16px;
    padding: 24px 24px 0;
}

.site-scan-photo-modal .scan-photo-header h2 {
    margin: 0;
}

.scan-photo-attempts,
.scan-photo-attempt {
    display: grid;
    gap: 8px;
}

.scan-photo-attempt + .scan-photo-attempt {
    margin-top: 8px;
    padding-top: 16px;
    border-top: 1px solid rgba(23, 25, 31, .1);
}

.site-scan-photo-modal .scan-photo-attempt h3 {
    margin: 0;
    font-size: 14px;
    font-weight: 600;
    line-height: 1.5;
}

.site-scan-photo-modal .scan-photo-author {
    font-size: 13px;
    overflow-wrap: anywhere;
}

.site-scan-photo-modal .scan-photo-warning {
    padding: 8px;
    border-radius: 8px;
    background: #fff3cd;
    color: #8a6d00;
    font-size: 13px;
    overflow-wrap: anywhere;
}

.scan-photo-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(104px, 1fr));
    gap: 8px;
}

.scan-photo-thumbnail {
    width: 100%;
    max-width: 160px;
    height: 144px;
    padding: 4px;
    border: 1px solid #d8dde4;
    border-radius: 12px;
    background: #fff;
    color: #6c757d;
    font-size: 13px;
    cursor: zoom-in;
}

.scan-photo-thumbnail img {
    width: 100%;
    height: 100%;
    object-fit: contain;
}

.scan-photo-thumbnail:disabled {
    cursor: default;
}

.photo-viewer-error {
    color: #fff;
    text-align: center;
}

/* Полноэкранный просмотр фото позиции или бланка. Слой поверх всего: у модалок
   bootstrap 1055, у .site-confirm-modal 1075, но выше них стоит шапка сайта
   (1400) и drawer-меню (1300) — без запаса над ними крестик и счётчик кадра
   оказывались под шапкой. */
.site-photo-viewer {
    position: fixed;
    inset: 0;
    z-index: 1500;
    display: flex;
    flex-direction: column;
    /* Фон сплошной, а не полупрозрачный: просвечивающая страница под кадром
       мешает разглядывать снимок — а его открывают именно ради этого. */
    background: #0f1115;
}

/* Верхняя строка: слева счётчик кадров, справа крестик. Отступ сверху считает
   safe-area — в установленном на iPhone приложении (standalone) угол экрана
   уезжает под вырез, и крестик оказался бы под ним. */
.photo-viewer-bar {
    position: relative;
    z-index: 2;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: calc(10px + env(safe-area-inset-top)) 16px 10px;
}

.photo-viewer-counter {
    color: rgba(255, 255, 255, .72);
    font-size: 14px;
    font-variant-numeric: tabular-nums;
}

.photo-viewer-close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: rgba(255, 255, 255, .14);
    color: #fff;
    font-size: 17px;
}

.photo-viewer-close:hover {
    background: rgba(255, 255, 255, .24);
}

/* Лента кадров: каждый кадр — во всю ширину, прокрутка прилипает к границам.
   overscroll-behavior не даёт свайпу утащить за собой страницу под слоем. */
.photo-viewer-track {
    flex: 1;
    display: flex;
    min-height: 0;
    overflow-x: auto;
    overflow-y: hidden;
    scroll-snap-type: x mandatory;
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
}

.photo-viewer-track::-webkit-scrollbar {
    display: none;
}

.photo-viewer-frame {
    flex: 0 0 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0 12px calc(16px + env(safe-area-inset-bottom));
    scroll-snap-align: center;
    scroll-snap-stop: always;
}

.photo-viewer-frame img {
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
    border-radius: 6px;
}

/* Стрелки — только для мыши: пальцем листают свайпом, а на узком экране
   кнопки закрывали бы собой сам кадр. */
.photo-viewer-nav {
    position: absolute;
    top: 50%;
    display: none;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    margin-top: -22px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: rgba(255, 255, 255, .14);
    color: #fff;
    font-size: 18px;
}

.photo-viewer-nav:hover {
    background: rgba(255, 255, 255, .24);
}

.photo-viewer-nav--prev {
    left: 16px;
}

.photo-viewer-nav--next {
    right: 16px;
}

@media (min-width: 768px) {
    .site-photo-viewer.has-many .photo-viewer-nav {
        display: inline-flex;
    }
}

/* Миниатюра открывает кадр: курсор должен об этом говорить. Только в окне
   фото позиции — в окне распознавания миниатюры не открываются. */
.site-item-photo-modal .recognize-file img {
    cursor: zoom-in;
}

/* ===== Плашка уведомления о новой заявке =====
   Всплывает поверх страницы, когда складу приходит заявка, и держится, пока
   идёт звуковая серия. Клик по ней — это и есть «увидел»: она вся кликабельна,
   не только ссылка. Тень и скругление — те же, что у модалок модуля. */
.site-toast {
    position: fixed;
    right: 16px;
    bottom: 16px;
    z-index: 1080;
    display: flex;
    gap: 12px;
    align-items: flex-start;
    width: min(360px, calc(100vw - 32px));
    padding: 12px 16px;
    background: var(--paper);
    border: 1px solid var(--line);
    border-left: 4px solid var(--ink);
    border-radius: 12px;
    box-shadow: 0 8px 30px rgba(23, 23, 23, .16);
    cursor: pointer;
    /* Скрытая плашка не должна перехватывать клики по странице. */
    opacity: 0;
    visibility: hidden;
    transform: translateY(16px);
    transition: opacity .2s, transform .2s, visibility 0s .2s;
}

.site-toast.is-visible {
    opacity: 1;
    visibility: visible;
    transform: none;
    transition: opacity .2s, transform .2s;
}

.site-toast-icon {
    margin-top: 2px;
    font-size: 18px;
    color: var(--ink);
}

.site-toast-body {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.site-toast-body b {
    font-size: 15px;
    font-weight: 600;
}

.site-toast-body span {
    color: var(--muted);
    font-size: 14px;
}

.site-toast-link {
    margin-top: 4px;
    font-size: 14px;
    color: var(--ink);
}

/* Управление уведомлениями в кабинете сотрудника: строка «подпись + кнопка»,
   как соседний блок точки смены. */
.employee-notify {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.employee-notify-row {
    display: flex;
    gap: 8px;
    align-items: center;
    justify-content: space-between;
}

.employee-notify-row .btn {
    flex: 0 0 auto;
}

/* Тумблер звука и кнопка «прослушать» — одна группа у правого края строки:
   первая переключает звук, вторая только даёт его услышать. */
.employee-notify-actions {
    display: inline-flex;
    flex: 0 0 auto;
    gap: 8px;
    align-items: center;
}

/* Квадратная кнопка под один значок: вертикальные отступы bootstrap не
   трогаем, иначе она станет ниже соседней. */
.employee-notify-play {
    padding-right: 9px;
    padding-left: 9px;
}

.employee-notify-play .bi {
    font-size: 15px;
    line-height: 1;
}

.employee-notify-status {
    margin: 0;
    color: var(--muted);
    font-size: 13px;
}

@media (max-width: 767.98px) {
    /* На телефоне плашка занимает низ экрана: там её достаёт палец, и она не
       спорит с sticky-шапкой таблиц модуля. */
    .site-toast {
        right: 8px;
        left: 8px;
        bottom: 8px;
        width: auto;
    }
}

/* ===== Мобильная версия модуля «Заявки» (телефоны, до 768px) =====
   Планшет — основной сценарий и не меняется. На телефоне таблицы модуля
   перекладываются в карточки: разметка таблиц помечена модификаторами
   request-table--list / --ship / --receive / --readonly / --final, подписи, видимые
   только на телефоне, — span.request-m-label. Выравнивание числовых
   колонок задано своими классами вместо bootstrap-утилит text-end /
   text-center: их !important не дал бы переопределить на телефоне. */

.request-m-label {
    display: none;
}

.request-cell-ordered,
.request-cell-val {
    text-align: right;
}

.request-cell-count {
    text-align: center;
}

/* В отправке обычные числа и поля складских дополнений стоят по одной оси. */
@media (min-width: 768px) {
    .request-table--ship .request-unit-col,
    .request-table--ship .request-cell-ordered {
        text-align: center;
    }
}

@media (max-width: 767.98px) {
    /* Поля контента на телефоне вдвое уже (спискам заявок нужна вся ширина) —
       теперь это общий `--site-gutter: 8px` из верхнего медиа-запроса.

       Заголовок и действия модуля на телефоне не делят строку: кнопка уходит
       под заголовок на всю ширину, иначе длинный H1 её сплющивает. */
    .page-hero {
        display: block;
    }

    .page-hero-actions {
        margin-top: 16px;
        flex-wrap: nowrap;
    }

    .page-hero-actions .btn {
        width: 100%;
    }

    /* Фильтр статусов и «Новая заявка» делят одну строку: воронка сжимается
       до иконки со счётчиком (подпись «Статус» скрыта), кнопка создания
       забирает остаток ширины. В два этажа шапка на телефоне не помещается —
       под ней сразу начинается список. */
    .page-hero-actions .request-filter-btn {
        width: auto;
        flex: 0 0 auto;
    }

    .request-filter-text {
        display: none;
    }

    .request-new-btn {
        flex: 1 1 auto;
    }

    .request-pagination {
        align-items: flex-start;
        flex-direction: column;
    }

    .request-page {
        min-width: 42px;
        height: 42px;
    }

    .request-page-number,
    .request-page-ellipsis {
        display: none;
    }

    .request-page-position {
        min-width: 74px;
        display: inline-flex;
        color: #495057;
        background: #fff;
        font-variant-numeric: tabular-nums;
    }

    .request-m-label {
        display: inline;
    }

    /* Строка поиска на телефоне в одну линию не помещается: поле ввода ужалось
       бы до огрызка. Ссылка режима уходит под поле и прижимается вправо. */
    .request-search {
        flex-wrap: wrap;
        gap: 8px;
    }

    .request-search .form-control {
        flex: 1 1 100%;
    }

    .request-search-toggle {
        margin-left: auto;
    }

    /* Карточная таблица на телефоне — уже не таблица, и скроллить её вбок
       незачем: гасим горизонтальный скролл обёртки, чтобы декоративные вылеты
       за габарит (счётчик фото у кнопки-камеры, тени, длинное слово) не катали
       список из стороны в сторону. clip, а не hidden: он не делает из обёртки
       scroll container, поэтому фокус на поле у края не оставляет блок в
       прокрученном состоянии; hidden оставлен фолбэком для старых браузеров.
       Место под сам вылет всё равно нужно давать (см. .request-photo-col ниже),
       иначе элемент не поедет, а обрежется. */
    .request-cards {
        overflow-x: hidden;
        overflow-x: clip;
    }

    /* Список заявок: карточка вместо шести колонок — № и статусы в первой
       строке, маршрут во второй на всю ширину, дата · позиции · автор в третьей. */
    .request-table--list,
    .request-table--list tbody,
    .request-table--ship,
    .request-table--ship tbody,
    .request-table--receive,
    .request-table--receive tbody,
    .request-table--readonly,
    .request-table--readonly tbody,
    .request-table--final,
    .request-table--final tbody {
        display: block;
    }

    .request-table--list thead,
    .request-table--ship thead,
    .request-table--receive thead,
    .request-table--readonly thead,
    .request-table--final thead {
        display: none;
    }

    .request-table--list td,
    .request-table--ship td,
    .request-table--receive td,
    .request-table--readonly td,
    .request-table--final td {
        padding: 0;
        border: 0;
    }

    .request-table--numbered .request-position-no {
        display: none;
    }

    .request-table--numbered .request-position-prefix {
        display: inline;
    }

    .request-table--list tr {
        display: grid;
        grid-template-columns: auto auto 1fr;
        grid-template-areas:
            "num    num    status"
            "route  route  route"
            "date   count  author";
        column-gap: 8px;
        row-gap: 4px;
        padding: 11px 0 12px;
        border-bottom: 1px solid #dee2e6;
    }

    /* У настоящей заявки вся карточка ведёт к просмотру: на телефоне точнее
       попасть в один номер в тесной строке неудобно. Оверлей живёт только в
       мобильной раскладке, поэтому на десктопе ссылкой остаётся лишь номер. */
    .request-table--list .request-list-row {
        position: relative;
        cursor: pointer;
    }

    .request-table--list .request-list-row .request-link::after {
        content: "";
        position: absolute;
        inset: 0;
        z-index: 1;
    }

    .request-cell-num {
        grid-area: num;
    }

    .request-cell-route {
        grid-area: route;
    }

    .request-cell-status {
        grid-area: status;
        justify-self: end;
        display: flex;
        gap: 6px;
    }

    .request-cell-date,
    .request-cell-count,
    .request-cell-author {
        color: #6c757d;
        font-size: 13.5px;
    }

    .request-cell-date {
        grid-area: date;
    }

    .request-cell-count {
        grid-area: count;
        text-align: left;
    }

    .request-cell-author {
        grid-area: author;
    }

    /* Точки-разделители третьей строки: «дата · позиции · автор». */
    .request-cell-count::before,
    .request-cell-author::before {
        content: "· ";
    }

    /* Формы создания/редактирования: контролы чуть компактнее; у длинного
       названия блок «−/поле/+» сам переносится на вторую строку и
       прижимается вправо (flex-wrap + margin-left: auto). */
    .request-product-row {
        flex-wrap: wrap;
        gap: 8px 10px;
    }

    .request-qty-control {
        margin-left: auto;
    }

    .request-qty-btn {
        width: 42px;
        height: 42px;
    }

    .request-qty-input {
        width: 78px;
        min-height: 42px;
    }

    /* Клетки факта — 92px, как на десктопе: 78px обрезали значение из семи
       знаков («100.250» — сто кило с граммами, «999.125»), и на телефоне было
       видно «999.12». Расширяем только факт: в строках отправки и прихода
       свободного места ~139px на 393px, а вот в форме заявки те же 14px стоят
       дорого — на 360px число позиций, у которых контролы переносятся на вторую
       строку, выросло бы со 175 до 192 из 248. Заказ вводят округлённо, три
       знака после запятой появляются на весах. */
    .request-table--ship .request-qty-input,
    .request-table--receive .request-qty-input,
    .recognize-table .request-qty-input {
        width: 92px;
    }

    .request-qty-unit {
        min-width: 34px;
    }

    /* Отправка и приход: карточка позиции — название своей строкой, ниже
       слева «Заказано: …», справа поле факта (и кнопка-камера на приходе). */
    .request-table--ship tr,
    .request-table--receive tr {
        display: grid;
        column-gap: 10px;
        row-gap: 8px;
        align-items: center;
        padding: 10px 0 12px;
        border-bottom: 1px solid #dee2e6;
    }

    /* Колонки «Ед. изм.» на телефоне нет — единица встаёт сразу за «Заказано: N»
       той же строкой, иначе значение осталось бы без единицы вовсе. */
    .request-table--ship tr {
        grid-template-columns: auto 1fr auto;
        grid-template-areas:
            "name    name name"
            "ordered unit fact";
    }

    .request-table--receive tr {
        grid-template-columns: auto 1fr auto auto;
        grid-template-areas:
            "name    name name name"
            "ordered unit fact photo";
    }

    .request-table--ship .request-unit-col,
    .request-table--receive .request-unit-col {
        grid-area: unit;
        width: auto;
        justify-self: start;
        font-size: 13.5px;
    }

    .request-cell-name {
        grid-area: name;
    }

    .request-table--ship .request-cell-ordered,
    .request-table--receive .request-cell-ordered {
        grid-area: ordered;
        text-align: left;
        color: #6c757d;
        font-size: 13.5px;
    }

    .request-table--ship .request-fact-col,
    .request-table--receive .request-fact-col {
        grid-area: fact;
        width: auto;
    }

    /* Запас справа под счётчик снимков: он висит абсолютом за габаритом кнопки
       (right: -7px), и на десктопе его принимала на себя ширина колонки (56px
       под кнопку в 42px). На телефоне колонка сжата по кнопке и упирается в
       правый край, поэтому счётчик вылезал наружу и включал горизонтальный
       скролл обёртки: список ездил на 6px, как только у позиции появлялось
       первое фото. Отступ возвращает тот же запас. */
    .request-table--receive .request-photo-col {
        grid-area: photo;
        width: auto;
        padding-right: 7px;
    }

    /* Таблицы просмотра заявки: название своей строкой, ниже —
       подписанные значения в строку с переносом; расхождение остаётся красным. */
    .request-table--readonly tr,
    .request-table--final tr {
        display: flex;
        flex-wrap: wrap;
        gap: 3px 14px;
        padding: 10px 0 12px;
        border-bottom: 1px solid #dee2e6;
    }

    .request-table--readonly td,
    .request-table--final td {
        font-size: 14px;
        text-align: left;
    }

    .request-table--readonly .request-cell-name,
    .request-table--final .request-cell-name {
        flex: 0 0 100%;
        font-size: 15px;
    }

    .request-table--readonly .request-m-label,
    .request-table--final .request-m-label {
        color: #6c757d;
        font-weight: 400;
    }

    /* В итоговой карточке единица идёт таким же подписанным значением, как
       «Заказано», «Отправлено» и остальные, — колонок на телефоне нет. */
    .request-table--readonly .request-unit-col,
    .request-table--final .request-unit-col {
        width: auto;
        color: inherit;
    }

    /* Позиция без расхождения: на десктопе клетка просто пустая, а в карточке
       пустой td остался бы flex-элементом и добавил к строке лишний зазор
       (gap считается и по нулевой ширине) — гасим ячейку целиком. */
    .request-table--final .request-cell-empty {
        display: none;
    }

    /* Фото и пометка: на телефоне колонка-камера уступает место значку за
       названием — иначе кнопка 42px становится отдельной строкой карточки. */
    .request-table--final .request-photo-col {
        display: none;
    }

    .request-name-mark {
        display: inline-flex;
    }

    /* Экран сверки распознавания — такая же карточка, как отправка и приход:
       номер с названием первой строкой, ниже «Заказано» слева и клетка факта
       справа. Четырьмя колонками он в телефон не влезал: внутри модалки таблице
       нужно было 375px при 319 доступных — колонка «Распознано» уезжала за край
       вместе с единицей, а названия рвались на четыре строки. Разметку строит
       JS (renderReview в requests.js), классы ячеек проставляются там же. */
    .recognize-table,
    .recognize-table tbody {
        display: block;
    }

    .recognize-table thead {
        display: none;
    }

    /* Прозрачный фон ячеек: белый --bs-table-bg на странице не виден (там фон
       и так белый), а в модалке он серый — и ячейки-грид-элементы разной ширины
       рассыпались по карточке белыми плашками. */
    .recognize-table td {
        padding: 0;
        border: 0;
        background-color: transparent;
    }

    .recognize-table tr {
        display: grid;
        grid-template-columns: auto 1fr auto;
        grid-template-areas:
            "no      name    name"
            "ordered ordered fact";
        column-gap: 10px;
        row-gap: 8px;
        align-items: center;
        padding: 10px 0 12px;
        border-bottom: 1px solid #dee2e6;
    }

    .recognize-cell-no {
        grid-area: no;
        color: #6c757d;
    }

    .recognize-cell-name {
        grid-area: name;
    }

    /* Левое выравнивание перебивает общий .request-cell-val, которым ячейка
       прижата к правому краю на десктопе. */
    .recognize-table .recognize-cell-ordered {
        grid-area: ordered;
        text-align: left;
        color: #6c757d;
        font-size: 13.5px;
    }

    .recognize-cell-fact {
        grid-area: fact;
    }
}

/* Прямое сохранение факта отправки/прихода: состояние поля и плашка связи.
   На параллельном приходе подписей «кто и когда» у поля нет (решение
   заказчика 2026-08-21): чужое значение только вспыхивает фоном и гаснет. */
.request-qty-input.request-qty-saving {
    border-style: dashed;
}

/* Значение не дошло до сервера (нет связи) — красная рамка до успешного повтора. */
.request-qty-input.request-qty-unsaved {
    border-color: #dc3545;
    box-shadow: inset 0 0 0 1px #dc3545;
}

/* Коллега сейчас стоит в этом поле (client-событие presence-канала). */
.request-qty-input.request-qty-busy {
    outline: 2px dotted #6c757d;
    outline-offset: 2px;
}

.request-qty-input.request-qty-remote,
.request-order-qty-remote,
.request-table--readonly .request-order-row-remote > td {
    animation: request-qty-remote 2.4s ease-out;
}

@keyframes request-qty-remote {
    0% {
        background-color: #fff3cd;
    }

    60% {
        background-color: #fff3cd;
    }

    100% {
        background-color: #fff;
    }
}

/* Плашка над таблицей: липнет под шапкой (она sticky, 51/58px), чтобы число
   несохранённых позиций было видно и в середине длинного списка. */
.request-live-banner {
    position: sticky;
    top: 59px;
    z-index: 5;
    margin: 0 0 12px;
    padding: 10px 14px;
    border: 1px solid transparent;
    border-radius: 10px;
    font-size: 14px;
    line-height: 1.35;
}

.request-live-banner[hidden] {
    display: none;
}

.request-live-banner--offline {
    background: #fff3cd;
    border-color: #ffe69c;
    color: #664d03;
}

.request-live-banner--error {
    background: #f8d7da;
    border-color: #f1aeb5;
    color: #58151c;
}

.request-live-banner--ok {
    background: #d1e7dd;
    border-color: #a3cfbb;
    color: #0a3622;
}

@media (max-width: 991.98px) {
    .request-live-banner {
        top: 67px;
    }
}

@media (max-width: 767.98px) {
    .request-live-banner {
        top: 59px;
        font-size: 13.5px;
    }
}

/* Подписи строк блока этапов — теперь статусы («Заявка на складе», «Заявка
   выполнена»), а они длиннее слов, под которые блок верстался. На телефоне
   393px строке остаётся 345px (у блока свой padding), из которых значение
   «Фамилия И. О., 11 авг 15ч 06м» забирает 211px: при общем кегле 15px
   подпись не вставала рядом со значением ни при какой ширине колонки — каждая
   строка этапа становилась двухэтажной, и блок вырастал вдвое. 13px — тот же
   кегль, что у мобильных подписей ячеек `request-m-label`. */
@media (max-width: 767.98px) {
    .request-meta-row > span:first-child {
        min-width: 116px;
        font-size: 13px;
    }
}

/* Заголовки внутри контентной страницы мельче h1 страницы (24px): без этого
   правила bootstrap рисует h2 в ~32px, и подзаголовок текста оказывается
   крупнее названия самой страницы. Отступы — по общей шкале 8/16/24. */
.content-block h2 {
    margin: 24px 0 8px;
    font-size: 18px;
    font-weight: 600;
    line-height: 1.3;
}

.content-block h2:first-child {
    margin-top: 0;
}

.content-block h3 {
    margin: 16px 0 8px;
    font-size: 16px;
    font-weight: 600;
    line-height: 1.3;
}

.content-block ul,
.content-block ol {
    margin-bottom: 15px;
}

/* Цепочка статусов заявки на странице «Документация»: плашки в порядке
   жизненного цикла, на узком экране переносятся строками. Стрелка приглушена —
   она разделитель, а не содержание. */
.doc-flow {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    margin-bottom: 24px;
}

.doc-flow-arrow {
    color: #6c757d;
}

/* Плашка статуса в роли заголовка раздела документации: цвет общий с плашками
   в списке заявок (те же классы `status-*`, правятся в одном месте), крупнее
   только кегль — в 12px заголовок потерялся бы среди текста. */
.request-status.request-status--doc {
    font-size: 15px;
    padding: 3px 12px;
}

.request-order-actions {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 16px;
}
.request-order-actions [data-order-status] { margin-left: auto; }

.request-order-add {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 100px 140px;
    gap: 16px;
    margin-top: 16px;
    padding: 16px;
    background: #f8f9fa;
    border-radius: 12px;
}

.request-order-add[hidden] { display: none; }
.request-order-add label { font-size: 14px; min-width: 0; }
.request-order-add label > :is(input, select) { margin-top: 8px; }
.request-order-name { position: relative; min-width: 0; }
.request-order-name > input { margin-top: 8px; }
.request-order-options {
    position: absolute;
    z-index: 20;
    top: 100%;
    left: 0;
    right: 0;
    max-height: min(280px, 35dvh);
    overflow-y: auto;
    overscroll-behavior: contain;
    background: #fff;
    border: 1px solid var(--bs-border-color);
    border-radius: 8px;
    box-shadow: 0 8px 24px #0002;
}
.request-order-options[hidden] { display: none; }
.request-order-options button {
    display: block;
    width: 100%;
    min-height: 48px;
    padding: 8px 12px;
    border: 0;
    border-bottom: 1px solid #eee;
    background: transparent;
    color: var(--ink);
    text-align: left;
    overflow-wrap: anywhere;
    touch-action: pan-y;
}
.request-order-options button small { display: block; color: var(--muted); }
.request-order-options button[aria-selected="true"],
.request-order-options button:not(:disabled):hover { background: #f1f3f5; }
.request-order-options button:disabled { opacity: .5; }
.request-order-options-note { padding: 8px 12px; font-size: 13px; color: var(--muted); }
.request-order-add-actions { display: flex; gap: 8px; grid-column: 1 / -1; }
.request-order-add [data-order-add-error] { grid-column: 1 / -1; }
.request-order-catalog-modal { z-index: 1450; }
body.request-order-catalog-open .modal-backdrop { z-index: 1440; }
.request-order-catalog-modal .modal-dialog { max-width: 640px; }
.request-order-catalog-modal .modal-content { overflow: hidden; }
.request-order-catalog-host {
    display: flex;
    flex: 1 1 auto;
    min-height: 0;
    height: min(620px, calc(100dvh - 160px));
    overflow: hidden;
}
.request-order-add.request-order-add--catalog {
    flex: 1 1 auto;
    min-height: 0;
    margin: 0;
    border-radius: 0;
    background: #fff;
    grid-template-columns: minmax(0, 1fr) 180px;
    grid-template-rows: auto minmax(180px, 1fr) auto auto auto auto;
    grid-template-areas:
        "name category"
        "options options"
        "unit quantity"
        "actions actions"
        "error error"
        "status status";
    overflow: hidden;
}
.request-order-add--catalog .request-order-name { grid-area: name; }
.request-order-add--catalog [data-order-catalog-category] { grid-area: category; }
.request-order-add--catalog .request-order-options {
    grid-area: options;
    position: static;
    max-height: none;
    min-height: 0;
    box-shadow: none;
}
.request-order-add--catalog .request-order-add-unit { grid-area: unit; }
.request-order-add--catalog .request-order-add-quantity { grid-area: quantity; }
.request-order-add--catalog .request-order-add-actions { grid-area: actions; }
.request-order-add--catalog [data-order-add-error] { grid-area: error; }
.request-order-add--catalog .request-order-catalog-save-status { grid-area: status; }
.request-table--order .request-fact-unit { display: none; }
.request-warehouse-order .request-qty-input { display: inline-block; width: 92px; }
.request-table [data-warehouse-additions-heading] > td {
    padding-top: 24px;
    font-weight: 600;
    color: var(--muted);
}
.request-table [data-warehouse-additions-heading][hidden] { display: none; }

@media (max-width: 767.98px) {
    .request-table--ship [data-warehouse-additions-heading] { display: block; border: 0; }
    .request-table--ship [data-warehouse-additions-heading] > td { display: block; padding-top: 8px; }
    .request-table--order tr {
        grid-template-columns: minmax(0, 1fr);
        grid-template-areas: "name" "fact";
    }
    .request-table--order .request-unit-col { display: none; }
    .request-table--order .request-fact-control { justify-content: flex-end; gap: 8px; }
    .request-table--order .request-fact-unit { display: inline; min-width: 32px; color: #6c757d; }
    .request-table--order .request-m-label { margin-right: auto; }
    .request-order-add { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
    .request-order-add .request-order-name { grid-column: 1 / -1; }
    .request-order-add :is(input, select) { font-size: 16px; min-height: 44px; }
    .request-order-actions > button { min-height: 44px; white-space: normal; }
    .request-order-catalog-host { height: auto; }
    .request-order-add.request-order-add--catalog {
        padding: 16px 8px max(16px, env(safe-area-inset-bottom));
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
        grid-template-rows: auto auto minmax(0, 1fr) auto auto auto auto;
        grid-template-areas:
            "name name"
            "category category"
            "options options"
            "unit quantity"
            "actions actions"
            "error error"
            "status status";
    }
    .request-order-catalog-modal .modal-header {
        padding-top: max(16px, env(safe-area-inset-top));
    }
}

/* ВРЕМЕННО: drawer-меню скрыто до доработки функционала (2026-08-12).
   Чтобы вернуть меню — удалить этот блок целиком, до конца файла.
   Разметка, JS и настройки страниц не тронуты: прячем только визуально.
   Панель и подложки гасим вместе с кнопкой, иначе меню откроется
   :target-фолбэком по прямой ссылке /#siteMenu. */
.menu-button,
.site-drawer,
.site-drawer-backdrop,
.site-drawer-target-backdrop {
    display: none !important;
}

/* Первая колонка шапки держала кнопку (38px); убираем её, чтобы логотип
   встал к левому краю, а не оставил после себя пустое место.
   Горизонтальные поля не трогаем: их задаёт общий `--site-gutter`, и логотип
   встаёт ровно над крошками и заголовком страницы. */
.header-bar {
    grid-template-columns: minmax(96px, 141px) 1fr;
}

.header-bar--user {
    grid-template-columns: auto minmax(0, 1fr) auto;
}

@media (min-width: 992px) {
    .header-bar {
        grid-template-columns: 141px 1fr;
    }

    .header-bar--user {
        grid-template-columns: auto minmax(0, 1fr) auto;
    }
}

@media (max-width: 767.98px) {
    .header-bar {
        grid-template-columns: minmax(68px, 82px) minmax(0, 1fr);
    }

    .header-bar--user {
        grid-template-columns: minmax(0, 1fr) auto;
    }
}
