/*
 * lists.css
 * Блоки: списки записей и списки свойств
 *
 * Два виджета, одно оформление:
 *
 * - `common\widgets\RecordList`   — много записей, у каждой одни колонки
 * - `common\widgets\PropertyList` — одна запись, строки её свойств
 *
 * Файл общий намеренно. Раньше такие блоки собирались каждый раз
 * заново: где таблица, где `dl` с колонками, где `list-group`.
 * Выглядели по-разному, хотя показывали одно и то же, и разнобой
 * читался как сырая система (правило 0.8). Пока оформление лежит
 * в одном файле, разъехаться ему негде.
 *
 * Что внутри:
 * 1. Углы: список внутри карточки не вылезает из её скруглений
 * 2. Свойства записи: подпись слева, значение справа
 * 3. Список записей на телефоне: строка становится карточкой
 */

/* --- 1. Углы --- */

/*
 * Карточка скруглена, а таблица и список внутри — нет. Их прямые
 * углы вылезают за скругление, и получается то, что видно глазом:
 * тень карточки закруглена, а угол под ней квадратный.
 *
 * Bootstrap скругляет так только то, что знает сам. Наши списки
 * он не знает, поэтому говорим ему про них здесь.
 *
 * `overflow` стоит на внешнем блоке виджета, а не на `table-responsive`:
 * тому нужна своя прокрутка вбок, и `hidden` её бы убил.
 */
.card > .record-list:last-child,
.card > .property-list:last-child,
.card > .collapse:last-child > .record-list:last-child,
.card > .collapse:last-child > .property-list:last-child {
    border-bottom-left-radius: var(--bs-card-inner-border-radius);
    border-bottom-right-radius: var(--bs-card-inner-border-radius);

    /*
     * `clip`, а не `hidden`, и это принципиально.
     *
     * Обрезают они одинаково, но `hidden` заводит свою прокрутку —
     * пусть и невидимую. А липкое цепляется за ближайшего предка
     * с прокруткой: шапка таблицы начинала держаться за эту обёртку
     * вместо страницы, а обёртка уезжает вверх целиком, унося липкое
     * с собой.
     *
     * Ловится плохо: свойства верные, в разборе стилей всё на месте,
     * а на экране ничего не липнет.
     */
    overflow: clip;
}

.card > .record-list:first-child,
.card > .property-list:first-child,
.card > .collapse:first-child > .record-list:first-child,
.card > .collapse:first-child > .property-list:first-child {
    border-top-left-radius: var(--bs-card-inner-border-radius);
    border-top-right-radius: var(--bs-card-inner-border-radius);

    /* `clip`, а не `hidden` — причина выше. */
    overflow: clip;
}

/*
 * Блок сворачивается — и между карточкой и списком встаёт обёртка.
 *
 * Из-за неё список перестаёт быть прямым ребёнком карточки, и правила
 * выше до него не достают: угол таблицы остаётся прямым, а тень под
 * карточкой закруглена. С первого взгляда незаметно, но стоит
 * присмотреться — бросается в глаза.
 *
 * Радиус берём переменной Bootstrap, а не `inherit`: у обёртки своего
 * скругления нет, и наследовать было бы нечего — вышел бы ноль.
 */
.card > .collapse:last-child {
    border-bottom-left-radius: var(--bs-card-inner-border-radius);
    border-bottom-right-radius: var(--bs-card-inner-border-radius);

    /*
     * `clip`, а не `hidden`, и это принципиально.
     *
     * Обрезают они одинаково, но `hidden` заводит свою прокрутку —
     * пусть и невидимую. А липкое цепляется за ближайшего предка
     * с прокруткой: шапка таблицы начинала держаться за эту обёртку
     * вместо страницы, а обёртка уезжает вверх целиком, унося липкое
     * с собой.
     *
     * Ловится плохо: свойства верные, в разборе стилей всё на месте,
     * а на экране ничего не липнет.
     */
    overflow: clip;
}

/* --- Отступ между соседними блоками --- */

/*
 * Два списка подряд не слипаются.
 *
 * На странице планировщика их два — «Расписание» и «История
 * запусков», — и они стояли вплотную: снизу у одного рамка, сверху
 * у другого, и читалось это как один блок с чертой посередине
 * (заметил Александр 06.09.2026).
 *
 * Величина взята не на глаз: столько же остаётся между последним
 * блоком и подвалом страницы — `1rem` внизу у `.app-content`. Тогда
 * промежутки на странице одинаковые, и глазу не за что зацепиться.
 *
 * Правило общее, а не на странице планировщика: списков по два
 * и по четыре на странице будет всё больше, и дописывать отступ
 * каждому — верный способ однажды забыть (правило 0.8).
 */
.record-block + .record-block {
    margin-top: 1rem;
}

/* --- Шапка блока: сворачивание --- */

/*
 * Заголовок блока — зона сворачивания.
 *
 * Занимает всё место слева от кнопок: нажать можно по названию,
 * значку, счётчику и пустому месту рядом. Кнопки справа лежат вне
 * этой зоны, поэтому шестерёнка, переключатель вида и обновление
 * товаров блок не складывают — и гасить события для этого не нужно.
 */
.record-block-title {
    display: flex;
    align-items: center;
    gap: .5rem;
    flex: 1 1 auto;
    min-width: 0;
}

/*
 * Курсор-рука — только там, где нажатие действительно сворачивает
 * блок. У несворачиваемого (`collapsible => false`) заголовок остаётся
 * простой надписью, без роли кнопки, — и рука на нём была бы обманом:
 * человек нажимает, ничего не происходит (07.09.2026, разбор
 * координатора).
 */
.record-block-title[role="button"] {
    cursor: pointer;
    user-select: none;
}

/*
 * Значок в шапке блока — один вид на все такие кнопки разом:
 * шестерёнка столбцов, вид плотности, стрелка сворачивания, кнопки
 * действий списка («Обновить товары» и подобные). Раньше у каждой был
 * свой класс с почти тем же самым CSS — отступы, рамка, скругление
 * едва заметно расходились (у вида плотности скругление было чуть
 * заметнее остальных), и на снимке рядом это было видно (слово
 * Александра 14.09.2026: «кнопки в шапке списка должны быть
 * одинаковые — один вид, размер, отступы, общим кодом виджета»).
 * Теперь стиль один, конкретная кнопка добавляет только то, что у неё
 * своё: стрелка — вращение при сворачивании (ниже), переключатель —
 * подсветку выбранного (`.active`, у `DensitySwitch` и у будущих
 * переключателей).
 */
.record-block-icon-button {
    display: inline-flex;
    align-items: center;
    padding: 0 .35rem;
    line-height: 1.4;
    border: 1px solid var(--bs-border-color, #dee2e6);
    border-radius: var(--bs-border-radius-sm, .25rem);
    background: var(--bs-tertiary-bg, #f8f9fa);
    color: var(--bs-secondary-color, #6c757d);
}

/*
 * Значок — ровно посередине кнопки. У шрифта значков строка выше самого
 * рисунка и свой сдвиг вниз на 0,125em — рисунок вставал выше середины
 * (слово Александра 14.10.2026: «содержимое отцентровать»). Высота строки
 * значка — ровно рисунок, сдвига нет, середину держит flex кнопки.
 */
.record-block-icon-button {
    justify-content: center;

    /* Прежняя высота кнопки — строка 1.4 и рамка: без неё кнопка стала
       бы ниже вместе со строкой значка. */
    min-height: calc(1.4em + 2px);
}

.record-block-icon-button .bi {
    display: block;
    line-height: 1;
}

.record-block-icon-button .bi::before {
    display: block;
    vertical-align: 0;
}

.record-block-icon-button:hover {
    border-color: var(--bs-secondary-color, #6c757d);
    color: var(--bs-body-color, #212529);
}

.record-block-icon-button.active {
    background: var(--bs-secondary-bg, #e9ecef);
    border-color: var(--bs-primary, #0d6efd);
    color: var(--bs-primary, #0d6efd);
}

/* Отбор про то, что горит («Только с минусами»): значок красный всегда,
   нажатый — залит красным (`RecordList::headerToggleButton()`, `danger`). */
.record-block-icon-button-danger {
    border-color: var(--bs-danger-border-subtle, #f1aeb5);
    color: var(--bs-danger, #dc3545);
}

/* Наведение — красное, не серое: `.btn-outline-secondary` Bootstrap
   иначе заливает кнопку серым (слово Александра 14.10.2026). */
.btn.record-block-icon-button-danger:hover,
.btn.record-block-icon-button-danger:focus-visible {
    background: var(--bs-danger-bg-subtle, #f8d7da);
    border-color: var(--bs-danger, #dc3545);
    color: var(--bs-danger, #dc3545);
}

.btn.record-block-icon-button-danger.active {
    background: var(--bs-danger, #dc3545);
    border-color: var(--bs-danger, #dc3545);
    color: #fff;
}

/* Нажатая под мышью — темнее, как наведение у красных кнопок Bootstrap
   (`.btn-danger:hover`): видно, что её можно нажать ещё раз. */
.btn.record-block-icon-button-danger.active:hover,
.btn.record-block-icon-button-danger.active:focus-visible {
    background: #bb2d3b;
    border-color: #b02a37;
    color: #fff;
}

/*
 * Стрелка. Сворачивает и она, и заголовок рядом.
 *
 * Раньше нажималась вся шапка целиком, вместе с кнопками: обновление
 * товаров, шестерёнка, переключатель вида — каждое нажатие заодно
 * складывало список, а галочку в списке столбцов снять было нельзя
 * вовсе. Чинить это гашением событий не вышло, и день я потратил
 * на попытки.
 *
 * Решение Александра 06.09.2026: развести зоны. Заголовок сворачивает,
 * кнопки — нет, и гасить ничего не надо.
 */
.record-block-toggle {
    transition: transform .15s;
}

/*
 * Стрелка поворачивается, когда блок свёрнут.
 *
 * Класс `collapsed` Bootstrap снимает и ставит сам — на том элементе,
 * который нажимают. Теперь нажимают кнопку, поэтому класс на ней.
 */
.record-block-toggle.collapsed {
    transform: rotate(180deg);
}

/*
 * Как сворачивается блок: содержимое уезжает вверх, под заголовок.
 *
 * Не сжимается по высоте — именно уезжает, как будто прячется
 * за шапку блока. Строки уходят одна за другой, а заголовок стоит
 * на месте.
 *
 * **Почему не обычным схлопыванием.** Пока Bootstrap уменьшает
 * высоту, липким строкам — заголовкам столбцов и отбору — перестаёт
 * хватать места, за которое они держались. Замер по кадрам показывал,
 * как строка стоит на 98 точках, потом проваливается на 307
 * и исчезает. Глазами это две белые полосы, ползущие поверх
 * схлопывающегося содержимого, и пропадающая тень карточки.
 *
 * Сдвиг эту беду снимает разом: он делает содержимое собственной
 * системой отсчёта, и липкие строки перестают цепляться за окно —
 * едут вместе со всем остальным, как обычные.
 *
 * Направление подсказывает скрипт (`sticky-stack.js`): у Bootstrap
 * класс `collapsing` один на оба случая, и без подсказки содержимое
 * уезжало бы вверх и при разворачивании.
 */
/*
 * Переход задан всегда, а не только на время сворачивания.
 *
 * Это не мелочь: Bootstrap ставит признак сворачивания и сдвиг
 * в один и тот же миг, и если переход появляется вместе с ними,
 * браузеру не с чего начинать движение — он применяет сдвиг сразу.
 * Сворачивание из-за этого происходило рывком, а разворачивание
 * плавно: там движение задано отдельной анимацией, ей начальное
 * состояние не нужно.
 */
.record-block .record-list {
    transition: transform .35s ease;
}

/* Сворачиваем: содержимое уходит наверх, за шапку. */
.record-block-hiding .collapsing > .record-list {
    transform: translateY(-100%);
}

/* Разворачиваем: выезжает оттуда же обратно. */
.record-block-showing .collapsing > .record-list {
    animation: record-block-slide .35s ease;
}

@keyframes record-block-slide {
    from {
        transform: translateY(-100%);
    }

    to {
        transform: translateY(0);
    }
}

/* --- Сортировка по заголовку столбца --- */

/*
 * Подпись столбца — ссылка, но выглядит как подпись: подчёркивания
 * и синевы здесь быть не должно, иначе шапка таблицы читается как
 * список ссылок, а не как названия колонок.
 *
 * **Перенос запрещён** (слово Александра 14.09.2026: заголовок —
 * `nowrap`, стрелка сортировки на той же строке, во всех столбцах).
 * До этого перенос был разрешён нарочно (разбор координатора
 * 08.09.2026: алгоритм ширин резервировал под заголовок целую строку
 * целиком, а не самое длинное слово, — на нескольких столбцах сразу
 * набегали лишние точки сверх обёртки). Теперь важнее не терять
 * стрелку сортировки с подписью на одной строке — с шириной, если
 * понадобится, разбираемся ячейкой (`cell.width`), не переносом.
 */
.record-list-sort {
    color: inherit;
    text-decoration: none;
    white-space: nowrap;
}

/*
 * Число в ячейке занимает несколько знаков, а длинная сортируемая
 * подпись не должна растягивать весь столбец. Разрешается адресно через
 * `header.wrap`, остальные заголовки сортировки по-прежнему в одну строку.
 */
.record-list-header-wrap .record-list-sort {
    white-space: normal;
}

.record-list-sort:hover {
    color: var(--bs-primary, #0d6efd);
}

/*
 * Значок сортировки — три полоски убывающей длины, без стрелки
 * (`bi-filter`). Длинная сверху означает «сверху большее»: 9 перед 0,
 * «Я» перед «А». Отражённый по вертикали — наоборот.
 *
 * Стрелку не берём намеренно: вверх-вниз читается как «свернуть»
 * и «развернуть», а полоски показывают саму суть — что крупное,
 * а что мелкое.
 */
.record-list-sort-icon {
    margin-left: .35rem;
    font-size: .85em;
    vertical-align: middle;
}

.record-list-sort-icon.flipped {
    display: inline-block;
    transform: scaleY(-1);
}

/*
 * По этой колонке сейчас не сортируют. Значок приглушён: видно,
 * что щёлкнуть можно, но не кажется, что список отсортирован
 * по всем колонкам разом.
 */
.record-list-sort-icon.idle {
    opacity: .25;
}

.record-list-sort:hover .record-list-sort-icon.idle {
    opacity: .6;
}

/* --- Отбор --- */

/*
 * Форма отбора пустая и невидимая.
 *
 * Поля стоят внутри таблицы, строкой шапки, — чтобы каждое было ровно
 * над своим столбцом. Обернуть строку таблицы формой разметка
 * не позволяет, поэтому форма стоит рядом, а поля связаны с ней
 * признаком `form`.
 */
.record-list-filter-form {
    display: none;
}

/*
 * Ячейка отбора: обычная ячейка шапки, только без жирного текста.
 *
 * Выравнивание по верху обязательно: у даты полей два, в столбик,
 * и без него соседние поля прижались бы к низу — строка отбора
 * выглядела бы ступенькой.
 */
.record-list-filter-row > th {
    padding: .4rem .5rem;
    font-weight: normal;
    vertical-align: top;
}

/*
 * Высота полей отбора здесь не задаётся, и это осознанно.
 *
 * Была заплатка: полям натягивалась высота под чужой список,
 * потому что наши текстовые были мельче на семь точек. Оказалось,
 * что причина в нас — полям задавался уменьшенный размер
 * (`form-control-sm`). Убрали уменьшение, поля сравнялись сами.
 */

/*
 * Поле отбора вместе с крестиком сброса.
 *
 * Крестик стоит внутри поля, у правого края. Отдельной кнопкой рядом
 * он занимал бы место в узком столбце, а на телефоне съедал бы ширину,
 * которой и так мало.
 */
.record-list-filter-field {
    position: relative;
}

.record-list-clear {
    position: absolute;
    top: 0;
    right: 0;
    display: none;
    width: 1.9rem;
    height: 100%;
    padding: 0;
    border: 0;
    background: none;
    font-size: 1.15rem;
    line-height: 1;
    color: var(--bs-secondary-color, #6c757d);
}

/*
 * Крестик появляется, только когда в поле что-то есть. Пустое поле
 * с крестиком выглядит так, будто отбор уже задан.
 */
.record-list-filter-field.filled .record-list-clear {
    display: block;
}

/*
 * Название поля в пустом списке — серое, как подсказка в текстовом поле.
 *
 * У выпадающего списка первая строка — это название столбца, а не
 * значение: «Состояние ключа». Чёрным она читается как уже выбранный
 * вариант, и человек думает, что отбор задан, хотя он пустой.
 *
 * Цвет берём той же переменной, которой Bootstrap красит подсказки
 * в текстовых полях, — тогда серый одинаковый во всей строке отбора.
 *
 * Сами варианты в раскрытом списке остаются обычными: серыми они
 * выглядели бы недоступными.
 */
.record-list-filter-field:not(.filled) .form-select {
    color: var(--bs-secondary-color, rgba(33, 37, 41, .75));
}

.record-list-filter-field .form-select option {
    color: var(--bs-body-color, #212529);
}

.record-list-clear:hover {
    color: var(--bs-danger, #dc3545);
}

/*
 * Место под крестик внутри поля, чтобы он не налезал на текст.
 * У выпадающего списка своя стрелка справа — сдвигаем крестик левее её.
 */
.record-list-filter-field.filled .form-control {
    padding-right: 1.9rem;
}

/*
 * Правая зона поля со стрелкой — под два значка разом, стрелку
 * и крестик, а не «с запасом»: стрелка (родная, Bootstrap) держит
 * правые 1.6rem, дальше зазор в 0.3rem (около 5 точек — не меньше
 * четырёх, чтобы не целиться в одно вместо другого), и крестик своей
 * шириной. Кнопка нашего списка (`.wms-select-toggle`) несёт тот же
 * класс `form-select`, второе правило под неё отдельно не пишем
 * (правило 0.6) — только здесь, одно место на оба вида поля со
 * стрелкой (08.09.2026, разбор координатора).
 */
.record-list-filter-field.filled .form-select {
    padding-right: 3.2rem;
}

/*
 * Крестик отодвинут левее стрелки, с зазором, и сужен под сам знак —
 * прежняя ширина (1.9rem) была с запасом на глаз.
 *
 * У поля со списком справа своя стрелка, и крестик ложился прямо
 * на неё: человек целится в стрелку, чтобы раскрыть список,
 * а попадает в крестик и снимает отбор.
 *
 * Селектор ловит и наш виджет: раньше здесь стоял `select`,
 * и после перехода на свой список правило перестало срабатывать —
 * молча, потому что тега `select` в поле больше нет.
 */
.record-list-filter-field.filled select.form-select ~ .record-list-clear,
.record-list-filter-field.filled .wms-select ~ .record-list-clear {
    right: 1.9rem;
    width: 1.3rem;
}

/*
 * Ячейка отбора по товару: поиск и размеры рядом, каждое — своя
 * `.record-list-filter-field` со своим крестиком (`RecordList::
 * filterField()`, `withClear($extraClass)`) — общий крестик на обе
 * части стоял бы у правого края всей ячейки, а не рядом со своим
 * полем, и ложился на стрелку размера (08.09.2026, разбор
 * координатора).
 *
 * Слева поле, ищущее сразу по артикулу и названию, справа список
 * размеров. Размер короткий — «XS», «42-43», — и половина ячейки ему
 * не нужна: отдаём по содержимому, остальное забирает поиск.
 *
 * Появилось 06.09.2026 вместе с решением убрать отдельный столбец
 * «Размер»: размер уехал в ячейку товара, а отбирать по нему надо
 * по-прежнему.
 */
.record-list-product-filter {
    display: flex;
    gap: 4px;
    align-items: center;
}

/* Поиск занимает всё, что осталось, и не меньше своего минимума. */
.record-list-product-search {
    flex: 1 1 auto;
    min-width: 0;
}

/*
 * Ширина по содержимому, не жёсткая: раньше держала шесть с небольшим
 * знаков (5.5rem) — «XS» помещалось, «34-35» резалось до «34…»
 * (08.09.2026, слово Александра). Настоящий предел — самый широкий
 * пункт списка размеров, а он всегда короткий: буквы «XS…XXXL» или
 * диапазон «34-35», шесть знаков с запасом хватает на оба вида.
 * `ch` — ширина цифры текущим шрифтом, точнее, чем гадать в пикселях.
 * Плюс — та же правая зона под стрелку и крестик, что и у любого поля
 * со списком (`.record-list-filter-field.filled .form-select` выше),
 * и небольшой отступ слева (`.wms-select-toggle`, `select.css`).
 *
 * Не растягивается и не сжимается — иначе на узком столбце список
 * то раздувается, забирая место у поиска, то схлопывается до стрелки,
 * и по нему не понять, что это отбор по размеру.
 */
.record-list-product-size {
    flex: 0 0 auto;
    width: calc(6ch + 3.45rem);
    min-width: 0;
}

/*
 * На телефоне строка превращается в карточку, а отбор — в столбик
 * полей. Там ставить два поля рядом незачем: места нет, и оба
 * получаются нажимаемыми с трудом.
 */
@media (max-width: 767.98px) {
    .record-list-product-filter {
        display: block;
    }

    .record-list-product-size {
        margin-top: 4px;
    }
}


/* --- Список обновляется --- */

/*
 * Пока список подгружается, он приглушён и не принимает нажатий.
 *
 * Крутилку поверх не показываем: при отборе список меняется быстро,
 * и мигающая крутилка раздражает сильнее, чем помогает. Приглушение
 * же говорит ровно то, что нужно: «это уже не окончательный ответ».
 *
 * Задержка перед приглушением — чтобы на быстром ответе оно не успело
 * появиться и не мигнуло зря.
 */
.record-list tbody {
    transition: opacity .15s .12s;
}

/*
 * Приглушаем только записи. Шапку с полями отбора трогать нельзя:
 * человек в этот момент как раз в ней и набирает, а `pointer-events`
 * отнял бы у него поле посреди слова.
 */
.record-list-busy tbody {
    opacity: .45;
    pointer-events: none;
}

/* --- Кнопки в колонке действий --- */

/*
 * Просвет между кнопками.
 *
 * Виджет собирает их подряд, без пробелов в разметке, — иначе браузер
 * добавил бы между ними случайный пробел, разный при разных переносах
 * строк в шаблоне. Но вплотную обводки двух кнопок сливаются в одну
 * рамку, и две кнопки читаются как одна широкая.
 *
 * Просвет небольшой: колонка действий узкая, и разгонять её незачем.
 * На телефоне он больше — там кнопкам отведена своя строка, и туда
 * же метит палец (см. ниже).
 */
.record-list-actions .record-list-value {
    display: inline-flex;
    align-items: center;
    gap: .25rem;
}

/* --- Галочка выбора --- */

/*
 * Галочка стоит посередине высоты ячейки.
 *
 * Без этого она съезжает к краю: Bootstrap выравнивает шапку таблицы
 * по нижней линии текста — так подписи столбцов разной длины стоят
 * на одной линии, — и галочка, у которой никакого текста нет,
 * прижимается туда же. В строке то же самое: там высоту задаёт
 * фотография товара, и галочка оказывается у её верхнего края,
 * далеко от названия, к которому относится.
 *
 * На телефоне это правило не работает и не нужно: там галочка
 * обтекает название слева, ячейка перестаёт быть ячейкой,
 * и выравнивать нечего.
 */
.record-list .record-list-choice {
    vertical-align: middle;
}

/*
 * Сама галочка — отдельной строкой, без запаса на буквы.
 *
 * Одного выравнивания ячейки мало: галочка остаётся строчным
 * элементом, и вокруг неё держится высота строки текста — заметно
 * больше её самой. Ячейка центрирует эту строку, а галочка внутри
 * строки сидит на линии письма, и получается сдвиг вверх на четыре
 * точки. Замерено, а не на глаз.
 *
 * Блочная галочка строки не образует: ячейка центрирует её саму.
 */
.record-list .record-list-choice .form-check-input {
    display: block;
    margin-top: 0;
    margin-bottom: 0;
}

/* --- 2. Свойства записи --- */

/*
 * Строка свойства: подпись слева, значение справа.
 *
 * Ровно та же раскладка, что у списка записей на телефоне, — и это
 * не совпадение. Показывают они одно и то же, значит и выглядеть
 * должны одинаково.
 */
.property-list-row {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 1rem;
    padding: .4rem 1rem;
    border-bottom: 1px solid var(--bs-border-color-translucent, rgba(0, 0, 0, .1));
}

/* У последней строки черты нет: там уже край карточки. */
.property-list-row:last-child {
    border-bottom: 0;
}

.property-list-label {
    flex: 0 1 auto;
    font-size: .85em;
    color: var(--bs-secondary-color, #6c757d);
}

/*
 * `min-width: 0` — чтобы длинное значение переносилось внутри своей
 * половины, а не растягивало строку и не выдавливало подпись.
 *
 * `overflow-wrap: anywhere` — значение без пробелов (адрес возврата
 * `https://test.wmsdb.ru/subscription`, ключ, подпись платёжной
 * системы) на телефоне иначе не переносится вовсе и вылезает за край
 * окна (дефект седьмого, задание #144). Общее место для всех карточек
 * кабинета и панели, не правка одного окна.
 */
.property-list-value {
    flex: 0 1 auto;
    min-width: 0;
    text-align: right;
    overflow-wrap: anywhere;
}

/* --- 3. Список записей на телефоне --- */

/*
 * На широком экране это обычная таблица AdminLTE, и здесь мы её
 * не трогаем. На телефоне строка становится карточкой.
 *
 * Алгоритм превращения:
 * 3.1. Шапка прячется — подписи колонок переезжают к значениям
 * 3.2. Таблица, тело, строки и ячейки перестают быть табличными
 * 3.3. Строка получает отступы и черту снизу — это и есть карточка
 * 3.4. Ячейка становится строчкой «подпись слева, значение справа»,
 *      подпись берётся из `data-label`
 * 3.5. Главная колонка идёт заголовком карточки, без подписи
 * 3.6. Колонка с кнопками уходит вниз своим рядом
 *
 * Порог — 767.98px, нижняя граница `md` в Bootstrap. Планшет держит
 * таблицу, телефон получает карточки.
 *
 * Правила Bootstrap для ячеек заданы селектором `.table > :not(caption)
 * > * > *`, а он весит больше простого `.record-list td`. Поэтому
 * перебиваем длинным селектором — иначе отступы и черты Bootstrap
 * остаются и ломают карточку.
 */

@media (max-width: 767.98px) {

    /*
     * 3.1. Подписи столбцов прячутся — они переезжают к значениям
     * внутрь карточек.
     *
     * А строка отбора остаётся и становится столбиком полей: отбирать
     * с телефона нужно не меньше, чем с монитора.
     */
    .record-list .record-list-labels {
        display: none;
    }

    .record-list .table > thead,
    .record-list .record-list-filter-row {
        display: block;
        width: 100%;
    }

    .record-list .record-list-filter-row {
        padding: .6rem 1rem;
        border-bottom: 1px solid var(--bs-border-color, #dee2e6);
    }

    /*
     * Ячейка отбора — строка столбика. Пустые убираем совсем, иначе
     * между полями зияют дыры от столбцов с кнопками.
     */
    .record-list .record-list-filter-row > th {
        display: block;
        width: 100%;
        padding: 0 0 .4rem;
        border: 0;
    }

    .record-list .record-list-filter-row > th:empty {
        display: none;
    }

    .record-list .record-list-filter-row > th:last-of-type:not(:empty) {
        padding-bottom: 0;
    }

    /* 3.2. Разбираем табличную раскладку. */
    .record-list .table,
    .record-list .table > tbody,
    .record-list .table > tbody > tr {
        display: block;
        width: 100%;
    }

    /*
     * 3.3. Строка — карточка. Черта снизу отделяет её от следующей;
     * у последней её нет, иначе получится вторая линия рядом
     * с краем карточки страницы.
     */
    .record-list .table > tbody > tr {
        padding: .7rem 1rem;
        border-bottom: 1px solid var(--bs-border-color, #dee2e6);
    }

    .record-list .table > tbody > tr:last-child {
        border-bottom: 0;
    }

    /*
     * 3.4. Ячейка — строчка «подпись слева, значение справа».
     *
     * `align-items: baseline` ставит подпись и значение на одну
     * линию письма: подпись мельче, и по верхнему краю они
     * выглядели бы съехавшими.
     */
    .record-list .table > tbody > tr > td {
        display: flex;
        justify-content: space-between;
        align-items: baseline;
        gap: 1rem;
        padding: .15rem 0;
        border-bottom: 0;
        box-shadow: none;
        text-align: right;
    }

    .record-list .table > tbody > tr > td::before {
        content: attr(data-label);
        flex: 0 1 auto;
        text-align: left;
        font-size: .85em;
        color: var(--bs-secondary-color, #6c757d);
    }

    /*
     * Значение — один элемент строки, а не россыпь кусков.
     *
     * Виджет заворачивает содержимое ячейки в эту обёртку: без неё
     * дата и «(181 дн.)» становятся двумя отдельными элементами,
     * и flex растаскивает их по разным краям.
     */
    .record-list .record-list-value {
        flex: 0 1 auto;
        min-width: 0;

        /* Длинное значение без пробелов переносится, а не вылезает
         * за край карточки — то же, что у `.property-list-value`. */
        overflow-wrap: anywhere;
    }

    /*
     * 3.5. Главная колонка — название записи. Это заголовок карточки:
     * во всю ширину, слева, без подписи. Подпись здесь была бы
     * шумом — и так видно, что это название.
     */
    .record-list .table > tbody > tr > td.record-list-main {
        display: block;
        text-align: left;
        padding-bottom: .4rem;
    }

    .record-list .table > tbody > tr > td.record-list-main::before {
        content: none;
    }

    /*
     * 3.6. Кнопки — своим рядом внизу карточки, прижаты вправо.
     *
     * Так же, как на широком экране: там колонка с кнопками тоже
     * последняя и справа. Одинаковое место — правило 0.8.
     */
    .record-list .table > tbody > tr > td.record-list-actions {
        display: flex;
        justify-content: flex-end;
        padding-top: .6rem;
    }

    .record-list .record-list-actions .record-list-value {
        display: flex;
        align-items: center;
        gap: .5rem;
    }

    .record-list .table > tbody > tr > td.record-list-actions::before {
        content: none;
    }

    /*
     * Пустой список: там одна ячейка на всю ширину, и подпись
     * ей не нужна.
     */
    .record-list .table > tbody > tr > td[colspan] {
        display: block;
        text-align: center;
    }

    .record-list .table > tbody > tr > td[colspan]::before {
        content: none;
    }

    /*
     * 3.7. Картинка — слева от всей карточки, как обложка.
     *
     * Без этого она встала бы отдельной строкой «пусто: картинка»
     * между названием и остальным. На широком экране столбец
     * с картинкой идёт первым и слева — на телефоне должно быть
     * так же (правило 0.8).
     *
     * Обтекание, а не колонка: строк в карточке разное число,
     * и жёсткая раскладка ломалась бы то на одной, то на другой.
     */
    .record-list .table > tbody > tr > td.record-list-image {
        display: block;
        float: left;
        margin: 0 .75rem .25rem 0;
        padding: 0;
    }

    .record-list .table > tbody > tr > td.record-list-image::before {
        content: none;
    }

    /* Карточка обхватывает обтекаемую картинку: без этого высокая
       картинка вылезала бы на следующую карточку. */
    .record-list .table > tbody > tr::after {
        content: '';
        display: block;
        clear: both;
    }

    /*
     * Галочка выбора — слева от названия, как обложка у товара.
     * Отдельной строкой «пусто: галочка» она выглядела бы нелепо.
     */
    .record-list .table > tbody > tr > td.record-list-choice {
        display: block;
        float: left;
        margin: 0 .6rem 0 0;
        padding: 0;
    }

    .record-list .table > tbody > tr > td.record-list-choice::before {
        content: none;
    }

    /*
     * Прокрутка вбок больше не нужна — карточки помещаются всегда.
     * Оставленный `overflow-x` подрезал бы тени кнопок при наведении.
     */
    .record-list .table-responsive {
        overflow-x: visible;
    }

    /*
     * 3.8. Итог под списком — той же раскладкой «подпись слева,
     * значение справа», что и строки: без этого на телефоне под
     * карточками стояло голое число («46 287 831,30»), а на широком
     * экране подписи не нужны — там видно, под каким столбцом стоит
     * итог (правило от 07.09.2026, `RecordList::footer()`). На узком
     * экране столбцов нет вовсе, и без подписи не понять, что это
     * за число (задача №18 трекера, снимок Александра).
     *
     * Та же обёртка `data-label`, что кладёт виджет на итог каждого
     * столбца, — рисуем той же самой парой правил, что тело таблицы
     * чуть выше, только для `tfoot`, а не для `tbody`.
     */
    .record-list .table > tfoot,
    .record-list .table > tfoot > tr {
        display: block;
        width: 100%;
    }

    /*
     * Прилипает к низу, пока прокручиваются карточки строк — тем же
     * словом Александра, что и у шапки выше (задача №18 трекера):
     * итог должен быть виден всегда, не только когда долистал
     * до самого конца. У `record-block-page-scroll` это низ окна
     * браузера, у списка со своей прокруткой — низ его же обёртки:
     * `position: sticky` сам считает от ближайшего скроллящегося
     * предка, второй расчёт под два вида прокрутки не заводим.
     */
    .record-list .table > tfoot {
        position: sticky;
        /* Над строкой состояния (`--wms-footer`, `app.css`), не под ней. */
        bottom: var(--wms-footer, 0px);
        z-index: 20;
        background: var(--bs-body-bg, #fff);
        border-top: 1px solid var(--bs-border-color, #dee2e6);
    }

    .record-list .table > tfoot > tr > td:empty {
        display: none;
    }

    .record-list .table > tfoot > tr > td:not(:empty) {
        display: flex;
        justify-content: space-between;
        align-items: baseline;
        gap: 1rem;
        padding: .3rem 1rem;
        text-align: right;
    }

    .record-list .table > tfoot > tr > td:not(:empty)::before {
        content: attr(data-label);
        flex: 0 1 auto;
        text-align: left;
        font-size: .85em;
        color: var(--bs-secondary-color, #6c757d);
    }
}

/* --- 4. Перечисления внутри ячейки --- */

/*
 * Размеры товара, штрихкоды, ярлыки — список значений в одной ячейке.
 *
 * Сколько строк показывать, сказано в разметке (`data-clamp`),
 * а что именно поместилось, считает `clamp-list.js`: это зависит
 * от ширины столбца и длины самих значений, и на сервере не угадать.
 *
 * Запятые рисуются стилями, а не текстом: спрятанное значение
 * не должно оставлять за собой висящую запятую.
 */
.record-clamp {
    display: block;
}

.record-clamp-item::after {
    content: ', ';
}

/*
 * Последнему запятая не нужна. Кто последний, помечает скрипт:
 * в разметке за ним стоят спрятанные значения, и CSS считает
 * последним не того.
 */
.record-clamp-item.is-last::after {
    content: '';
}

.record-clamp-more {
    margin-left: .25rem;
    white-space: nowrap;
}

/* --- 5. Липкая шапка таблицы --- */

/*
 * Заголовки столбцов и поля отбора остаются на виду при прокрутке
 * длинного списка.
 *
 * Без этого на сороковой строке уже непонятно, что означает столбец:
 * человек прокручивает наверх, смотрит, возвращается — и так каждый
 * раз.
 *
 * **Липнет вся шапка целиком, а не каждая строка отдельно.** Так было
 * сперва: заголовкам задавался верх ноль, полям отбора — высота
 * заголовков, посчитанная скриптом. Между двумя липкими строками
 * оставалась волосяная щель — высота дробная, а положение считается
 * с округлением, — и сквозь неё при прокрутке просвечивали уезжающие
 * строки. Закрыть её удавалось только нахлёстом, от которого строки
 * шапки съезжались.
 *
 * Целая шапка снимает вопрос: внутри неё ничего не разъезжается,
 * высоту считать не надо, скрипт не нужен.
 *
 * **Прокручивается сама таблица, а не страница.** Иначе не работает:
 * `sticky` цепляется за ближайшего предка со своей прокруткой,
 * а у нас это контейнер таблицы — он уезжает вверх вместе со всем
 * содержимым, и шапка уезжает с ним. Поэтому контейнеру задана
 * высота: он прокручивается внутри себя, и шапке есть за что
 * держаться.
 *
 * На телефоне ничего этого нет: там строки превращаются в карточки,
 * подписи стоят рядом со значениями, и шапки не существует вовсе.
 */
/*
 * Столбец с заданной шириной.
 *
 * Ширину держит эта обёртка, а не ячейка: ячейке ширина — только
 * пожелание, браузер всё равно считает столбец по самому длинному
 * значению. Обёртка не тянется, и столбцу расти не от чего.
 *
 * Текст переносится, а не обрезается в одну строку: длинное название
 * юрлица должно читаться целиком, а строка списка и так высокая —
 * её держит фотография.
 *
 * Но не больше трёх строк: иначе одна запись с длинным названием
 * растянет строку вдвое против соседних, и список пойдёт волнами.
 * Что не влезло — многоточие.
 */
/*
 * Содержимое переносится, но не больше трёх строк: иначе одна запись
 * с длинным названием растянет строку вдвое против соседних, и список
 * пойдёт волнами. Что не влезло — многоточие.
 *
 * Длинное слово без пробелов тоже переносим, а не выталкиваем за край:
 * артикулы и коды бывают в полстроки.
 */
.record-list-value-fixed > *,
.record-list-value-fixed .ref-name {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
    overflow: hidden;
    min-width: 0;
}

/*
 * Перенос где угодно — только у названия записи, не у любого прямого
 * потомка. У «Последний запуск» рядом с датой в этой же обёртке
 * стоит короткая пометка («Готово») — обычное слово, не артикул,
 * и оно распадалось по буквам без всякой пользы: переноситься ему
 * некуда было и незачем, слово короткое (07.09.2026, разбор
 * координатора).
 */
.record-list-value-fixed .ref-name {
    overflow-wrap: anywhere;
}

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


/*
 * У значка «запись удалена» своя колонка внутри ячейки.
 *
 * **Действует во всех списках, ширину столбца задавать для этого
 * не нужно.** Пометка ведёт себя одинаково везде — иначе на одной
 * странице точки стоят столбиком, а на соседней вразнобой, и система
 * читается как собранная из кусков (правило 0.8).
 *
 * Иначе значок стоит сразу за последним словом и гуляет по
 * горизонтали: у одной записи название кончается посередине, у другой
 * у самого края — и красные точки идут вразнобой, как рассыпанные.
 *
 * Место под значок держится **у всех записей столбца**, а не только
 * у помеченных. Тогда текст у всех одной ширины, а точки выстраиваются
 * в ровный столбик — их видно одним взглядом, не читая строки.
 *
 * Отдельного столбца таблицы для этого заводить не нужно: он потянул
 * бы за собой пустой заголовок, объединение ячеек там, где пометки
 * нет, и своё поведение на телефоне.
 */
.record-list td .record-list-value:has(.ref-deleted),
.record-list-value-fixed {
    display: grid;
    align-items: center;
    gap: .35rem;
}

/*
 * Вторая колонка сетки — под саму пометку, а не всегда под маленький
 * значок «запись удалена»: он один и тот же размер везде, ему
 * заранее известного `1.1rem` хватает. Второй кусок бывает и другим —
 * короткая пометка вроде «Готово» рядом с датой, — и у него своя
 * ширина, откуда её ни возьми заранее не зная. `1.1rem` держим только
 * там, где второй кусок и есть тот самый значок; иначе колонка сама
 * подстраивается под то, что в неё положили (07.09.2026, разбор
 * координатора: «Последний запуск», пометка жалась в те же 1.1rem,
 * что и значок, хотя значком не была).
 */
.record-list td .record-list-value:has(.ref-deleted),
.record-list-value-fixed:has(.ref-deleted) {
    grid-template-columns: minmax(0, 1fr) 1.1rem;
}

.record-list-value-fixed:not(:has(.ref-deleted)) {
    grid-template-columns: minmax(0, 1fr) auto;
}

/* Ссылка занимает первую колонку целиком. */
.record-list .ref-deleted {
    display: contents;
}

.record-list-value-fixed .ref-name {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
    overflow: hidden;
    overflow-wrap: anywhere;
    min-width: 0;
}

/* Значок стоит во второй колонке, посередине её высоты. */
.record-list .ref-mark {
    margin-left: 0;
    justify-self: center;
}

/*
 * На телефоне ширина не нужна: там строка становится карточкой,
 * подпись слева, значение справа, столбцов нет вовсе.
 */
@media (max-width: 767.98px) {
    .record-list-value-fixed {
        width: auto !important;
        display: block;
    }

    .record-list .ref-deleted {
        display: inline;
    }
}

/*
 * Пружина у края таблицы.
 *
 * Докрутили список до конца на ходу — строки чуть подаются в сторону
 * прокрутки и возвращаются. Это знак «дальше некуда», после которого
 * страница уже едет сама. Класс ставит скрипт колеса и снимает через
 * треть секунды.
 */
@keyframes record-list-bounce-down {
    0% { transform: translateY(0); }
    40% { transform: translateY(-.5rem); }
    100% { transform: translateY(0); }
}

@keyframes record-list-bounce-up {
    0% { transform: translateY(0); }
    40% { transform: translateY(.5rem); }
    100% { transform: translateY(0); }
}

.record-list-bounce-down > .table {
    animation: record-list-bounce-down .3s ease-out;
}

.record-list-bounce-up > .table {
    animation: record-list-bounce-up .3s ease-out;
}

@media (min-width: 768px) {
    /*
     * Прокрутка вбок — родная, у самой обёртки: настоящая полоса,
     * настоящий свайп пальцем. По устройству floatThead
     * (github.com/mkoryak/floatThead, MIT — устройство прочитано
     * в исходнике, код свой, `record-list-float.js`): шапка и подвал
     * таблицы вынесены наружу, в свои узкие полосы над обёрткой
     * и под ней (раздел 5.1), и в её собственной прокрутке не
     * участвуют — крутится только тело.
     */
    .record-list .table-responsive {
        overflow-x: auto;

        /*
         * Вычитаем то, что занято сверху: шапка кабинета, заголовок
         * страницы, крошки, отступы карточки. Число подобрано так,
         * чтобы под таблицей оставалось немного воздуха, а не впритык.
         *
         * Список во весь экран (раздел 5.1) это число снимает —
         * там страница растёт вместе с таблицей, а не таблица
         * прокручивается внутри своего окошка.
         */
        max-height: calc(100vh - 15rem);
        overflow-y: auto;

        /*
         * Докрутив таблицу до конца, страница едет дальше — нарочно.
         *
         * Запрет перетекания прокрутки здесь стоял и был снят 02.09.2026:
         * в «Удалённом» три списка, и после конца первого страница
         * замирала — до второго и третьего не добраться колесом,
         * приходилось тянуть мышь за пределы таблицы.
         *
         * Запрет остаётся там, где он по делу: у выпадающего списка
         * и календаря, — они висят поверх страницы, и уезжать ей из-под
         * них незачем.
         */
    }

    /*
     * Границы разделены, фон непрозрачный: то же самое нужно и шапке,
     * и подвалу — обе полосы держат свой кусок таблицы (раздел 5.1),
     * и без сплошного фона сквозь щели между ячейками было бы видно,
     * что происходит под ними при прокрутке.
     */
    /*
     * Границы таблицы разделены, а не схлопнуты.
     *
     * При схлопнутых границах соседние ячейки делят общую линию,
     * и на стыках остаются волосяные зазоры: фон ячейки до них не достаёт,
     * а сквозь них при прокрутке просвечивают строки. Заметно
     * только в движении.
     *
     * Промежуток обнулён — иначе между ячейками появятся видимые
     * щели, и таблица разъедется.
     */
    .record-list .table {
        border-collapse: separate;
        border-spacing: 0;
    }

    /*
     * Черта под шапкой — граница нижних ячеек.
     *
     * Тенью её не заменить: тень мягкая, а нужна ещё и внятная линия,
     * иначе шапка сливается с первой строкой.
     */
    .record-list .record-list-filter-row > th {
        border-bottom: 1px solid var(--bs-border-color, #dee2e6);
        position: relative;
    }

    /*
     * Тень под шапкой — полоса, нарисованная своим слоем.
     *
     * Настоящей тенью (`box-shadow`) не выходит, и обе попытки
     * упёрлись в одно и то же:
     *
     * - на всю шапку не ложится: `thead` — это группа строк, тень
     *   на ней браузер не рисует вовсе. Свойство применяется,
     *   в разборе стилей видно, на экране пусто;
     * - на каждой ячейке разрывается: чтобы тень не лезла вбок,
     *   ей задают отрицательное растяжение, а оно поджимает её
     *   с боков — между соседними ячейками остаются просветы,
     *   и вдоль шапки идёт пунктир по границам столбцов. Без
     *   растяжения просветы пропадают, но тень вылезает вбок
     *   и обводит каждое поле отбора по краям.
     *
     * Полоса свободна и от того, и от другого: у каждой ячейки
     * свой кусок во всю её ширину, куски смыкаются встык, вбок
     * не выходит ничего.
     */
    .record-list .record-list-filter-row > th::after {
        content: '';
        position: absolute;
        left: 0;
        right: 0;
        top: 100%;
        height: .5rem;

        /* Плотная у самой шапки, к низу сходит на нет. */
        background: linear-gradient(to bottom,
            rgba(0, 0, 0, .22),
            rgba(0, 0, 0, 0));

        /* Полоса лежит поверх строк, но нажатия сквозь неё проходят:
           иначе она перехватывала бы щелчки по первой строке. */
        pointer-events: none;
    }

    /*
     * Полосы шапки и подвала (`record-list-float.js`) — своей
     * прокрутки не показывают: у них ей взяться неоткуда, `scrollLeft`
     * сдвигает их содержимое программно, вслед за обёрткой.
     *
     * Фон непрозрачный — полоса стоит поверх обычного содержимого
     * страницы (список во весь экран, раздел 5.1) или поверх строк
     * своего блока (список среди нескольких), и без фона они были бы
     * видны сквозь неё.
     *
     * У таблицы внутри убран нижний отступ Bootstrap: между полосой
     * и обёрткой не должно быть щели.
     */
    .record-list-float-head,
    .record-list-float-foot {
        overflow: hidden;
        background: var(--bs-body-bg);
    }

    .record-list-float-head table,
    .record-list-float-foot table {
        margin-bottom: 0;
    }

    /*
     * Родная полоса обёртки прячется — видна только эта, третья
     * полоса, ниже. У широкой таблицы в сотни строк родная полоса
     * стоит у самого низа таблицы, за всеми строками, и человеку,
     * не умеющему крутить колесом вбок, её просто не найти — не то
     * что не видно, а физически не докрутить до неё (07.09.2026,
     * слово Александра: список товаров, подвал с итогами закрывает
     * собой то место, где могла бы быть полоса).
     */
    /* Не там, где полосы «поверх» (планшет, телефон, macOS): подменная
       полоса там выходит нулевой высоты (scrollbar.js), и родная
       остаётся единственной — её не прячем (15.09.2026). */
    html:not(.wms-overlay-scrollbars) .record-list .table-responsive {
        scrollbar-width: none;
    }

    html:not(.wms-overlay-scrollbars) .record-list .table-responsive::-webkit-scrollbar {
        display: none;
    }

    /*
     * Третья полоса — настоящая, родная прокрутка, только вынесенная
     * к низу окна вместо низа таблицы. Устройство то же, что
     * у floatThead: спереди свободно двигать глазами и пальцем,
     * а `scrollLeft` идёт в обе стороны с обёрткой
     * (`record-list-float.js`).
     *
     * Высота — по-умолчанию нулевая: показывать нечего, пока таблица
     * помещается в обёртку целиком. Класс `record-list-scrollable`
     * ставит скрипт, только когда есть что прокручивать — тогда
     * полоса забирает высоту родной полосы прокрутки, которую сама
     * же и заменяет (`--wms-scrollbar`, `scrollbar.js`, правило 6.2).
     */
    .record-list-float-scroll {
        overflow-x: auto;
        overflow-y: hidden;
        height: 0;
        background: var(--bs-body-bg);
    }

    .record-list.record-list-scrollable .record-list-float-scroll {
        height: var(--wms-scrollbar, 15px);
    }

    /* Распорка внутри полосы: её ширина равна полной ширине таблицы —
       ровно поэтому у полосы получается настоящий, пропорциональный
       бегунок, а не просто прямоугольник во всю ширину. */
    .record-list-float-scroll > div {
        height: 1px;
    }
}

/* --- 5.1. Второй вид прокрутки: список во весь экран --- */

/*
 * Список один на странице — значит он занимает её целиком: таблица
 * растёт вместе со страницей, а не прокручивается в своём окошке.
 *
 * Включается параметром виджета — по умолчанию работает раздел 5,
 * где высота таблицы задана числом:
 *
 * ```php
 * 'scroll' => ['mode' => 'page'],
 * ```
 *
 * **Зачем понадобился второй вид.** На маленьком экране до списка
 * добираешься через полэкрана служебного: заголовок страницы, крошки,
 * шапка блока. Таблица при этом заперта в окне на три-четыре строки,
 * а под ней пустует подвал. Здесь она забирает всю оставшуюся высоту.
 *
 * Устройство горизонтальной прокрутки общее с разделом 5, отдельного
 * тут ничего нет (`record-list-float.js`: шапка и подвал таблицы —
 * в своих полосах вне обёртки, полосы едут вбок вместе с ней).
 * Разница только в высоте обёртки и в том, к чему прилипают сами
 * полосы: там — к краю обёртки со своей внутренней прокруткой,
 * здесь — к окну браузера, потому что прокручивается вся страница.
 */
@media (min-width: 768px) {
    .record-block-page-scroll .table-responsive {
        max-height: none;
    }

    /* Шапка блока — первая ступень стопки, под шапкой кабинета.
       Слой из общей шкалы (раздел 5): выше и полосы с названиями
       столбцов, и липкой колонки действий. */
    .record-block-page-scroll > .card-header {
        position: sticky;
        top: var(--stack-header, 0px);
        z-index: 50;
        background: var(--bs-body-bg);
    }

    /* Полоса с названиями столбцов и строкой отбора — вторая ступень,
       под шапкой блока. Число то же самое, что раньше держало одну
       строку названий: сдвинулась не ступень, а то, что на ней стоит —
       раньше отдельная строка, теперь вся полоса разом. */
    .record-block-page-scroll .record-list-float-head {
        position: sticky;
        top: var(--stack-labels, 120px);
        z-index: 40;
    }

    /* Подвал — предпоследняя ступень: если под ним есть ещё видимая
       полоса прокрутки, подвал уступает ей нижний край, сама полоса
       занимает его вместо себя (класс ставит скрипт, только когда
       таблица действительно шире обёртки). */
    .record-block-page-scroll .record-list-float-foot {
        position: sticky;
        /* Низ окна здесь — верх строки состояния (`--wms-footer`, `app.css`). */
        bottom: var(--wms-footer, 0px);
        z-index: 20;
    }

    .record-block-page-scroll .record-list.record-list-scrollable .record-list-float-foot {
        bottom: calc(var(--wms-scrollbar, 15px) + var(--wms-footer, 0px));
    }

    /* Полоса прокрутки — последняя ступень, у самого низа (над строкой состояния). */
    .record-block-page-scroll .record-list-float-scroll {
        position: sticky;
        bottom: var(--wms-footer, 0px);
        z-index: 20;
    }
}

/*
 * То же самое — на телефоне: шапка блока прилипает к верху,
 * пока прокручиваются карточки строк (задача №18 трекера, слово
 * Александра: «чтоб он также фиксированный был»).
 *
 * Названий столбцов и строки отбора на телефоне нет вовсе (раздел 3.1),
 * прилипать в стопке больше нечему — только шапка блока. Число для
 * `top` то же самое, что и на широком экране: `sticky-stack.js` уже
 * считает его для любой ширины (`measure()` внутри проверяет `narrow`
 * только для строки отбора, шапка блока — до этой проверки).
 */
@media (max-width: 767.98px) {
    .record-block-page-scroll > .card-header {
        position: sticky;
        top: var(--stack-header, 0px);
        z-index: 50;
        background: var(--bs-body-bg);
    }
}

/* --- 6. Липкая колонка действий --- */

/*
 * Кнопки строки остаются у правого края, даже когда таблица шире
 * экрана.
 *
 * Без этого на среднем мониторе колонка действий уезжает за край:
 * человек видит строку, но не видит, чем с ней что-то сделать, —
 * надо сперва прокрутить вбок. А кнопки нужны чаще всего.
 *
 * Работает на том же основании, что и липкая шапка: контейнер
 * таблицы прокручивается сам, и колонке есть за что держаться.
 */
@media (min-width: 768px) {
    .record-list .table > tbody > tr > td.record-list-actions,
    .record-list .table > thead > tr > th.record-list-actions {
        position: sticky;
        right: 0;
        background: var(--bs-body-bg);
    }

    /* Корзинка строки — часть тела таблицы, её слой из общей шкалы
       (см. раздел 5): ниже стопки шапки, но выше обычных ячеек. */
    .record-list .table > tbody > tr > td.record-list-actions {
        z-index: 10;
    }

    /* Угол шапки своего слоя не получает — того же слоя, что весь
       её ряд (ряд названий или строка отбора), той же причиной,
       что и в разделе 5 (07.09.2026, разбор координатора). */

    /*
     * Черта слева — чтобы колонка отделялась, когда под неё уезжает
     * содержимое. Тенью, а не рамкой: рамка ячейки сдвинула бы
     * разметку на пиксель и разъехалась бы с шапкой.
     */
    .record-list .table > tbody > tr > td.record-list-actions::before {
        content: '';
        position: absolute;
        left: 0;
        top: 0;
        bottom: 0;
        width: 1px;
        background: var(--bs-border-color, #dee2e6);
        opacity: 0;
        transition: opacity .15s;
    }

    /* Черта появляется, только когда таблица действительно уехала
       вбок: на просторном экране она была бы лишней линией. */
    .record-list .table-responsive.is-scrolled
        .table > tbody > tr > td.record-list-actions::before {
        opacity: 1;
    }
}

/* ------------------------------------------------------------------
   Живое обновление: строка появилась или изменилась.

   Список перечитался сам по событию с сервера, и без подсветки
   человек не заметит, что в нём стало иначе: строки выглядят
   так же, как минуту назад. Новые и изменившиеся строки мигают
   жёлтым и гаснут; класс снимает скрипт по концу анимации.
   ------------------------------------------------------------------ */

@keyframes record-list-changed {
    0%   { background-color: rgba(255, 193, 7, .55); }
    35%  { background-color: rgba(255, 193, 7, .15); }
    55%  { background-color: rgba(255, 193, 7, .55); }
    100% { background-color: transparent; }
}

.record-list tbody > tr.record-list-changed > td {
    animation: record-list-changed 2.4s ease-out 1;
}

/* --- 8. Сжатый вид списка --- */

/*
 * Сжатая таблица — как в Google Таблицах: строка 21 точка, шрифт 13,
 * тонкая сетка со всех сторон. На экран влезает вдвое больше строк.
 *
 * Так работают все списки по умолчанию: Татьяна посмотрела оба вида
 * 03.09.2026 и сказала прямо — «второй вариант неудобный, достаточно
 * первого». Просторный остаётся кнопкой в шапке блока.
 *
 * Только на широком экране. На телефоне строка превращается
 * в карточку (раздел 3), и сжимать там нечего: высоту держит не
 * таблица, а содержимое.
 */
@media (min-width: 768px) {
    /*
     * Границы раздельные, а не схлопнутые — как у липкой шапки
     * (раздел 5). Здесь до 05.09.2026 стояло `collapse`, и оно
     * перебивало то правило: при схлопнутых границах браузер не
     * дотягивает фон липкой ячейки до её края, и у нижнего края
     * шапки блока и строки заголовков оставались прорези в несколько
     * точек, сквозь которые при прокрутке проезжали строки. На
     * неподвижной странице не видно, Татьяна заметила в движении.
     */
    .record-block-dense .record-list .table,
    .record-block-dense .document-lines {
        font-size: 13px;
        line-height: 15px;
        border-collapse: separate;
        border-spacing: 0;
    }

    /*
     * У раздельных границ каждая ячейка рисует свою, и линия «со всех
     * сторон» удвоилась бы на стыках. Поэтому у ячейки только правая
     * и нижняя: левую и верхнюю линии сетки даёт соседняя ячейка,
     * а по краю таблицы — рамка блока и черта под его шапкой.
     */
    .record-block-dense .record-list .table > thead > tr > th,
    .record-block-dense .record-list .table > tbody > tr > td,
    .record-block-dense .document-lines > thead > tr > th,
    .record-block-dense .document-lines > tbody > tr > td {
        padding: 3px 4px;
        border: 0 solid var(--bs-border-color, #dee2e6);
        border-width: 0 1px 1px 0;
        vertical-align: middle;
    }

    /*
     * Табличная часть документа — числа и выбор, переноситься там
     * нечему: строка держит ровно одну строку текста, 21 точку.
     *
     * Строка-участник группы (размер артикула, задача №126 трекера) —
     * тот же случай: там тоже одни числа и короткое поле, второй раз
     * перенос не нужен. Слово Александра при четвёртой сдаче: «высота
     * под одну строку, отступы как у сжатой таблицы документов» —
     * то же самое правило, второй раз не пишем (правило 0.6).
     */
    .record-block-dense .document-lines > tbody > tr > td,
    .record-block-dense .record-list .table > tbody > tr.record-group-member > td {
        height: 21px;
        white-space: nowrap;
    }

    .record-block-dense .document-lines > thead > tr > th {
        height: 21px;
        white-space: nowrap;
    }

    /*
     * Список записей — не всё в нём число или дата. Раньше запрет
     * на перенос стоял у каждой ячейки без разбора (умолчание
     * правила 6.5), и длинный статус или название вылезали за край
     * своего столбца, хотя соседний столбец с фотографией мог бы
     * ужаться и отдать место (07.09.2026, слово Александра: карточка
     * поставки, статусы Wildberries и продавца).
     *
     * Теперь переносится всё, что не сказало обратного явно —
     * `nowrap: true` у столбца (число, дата, кнопка). Высота строки
     * не фиксирована — растёт на вторую строку там, где текст её
     * потребовал, а `min-height` держит прежние 21 точку там, где
     * перенос не понадобился.
     */
    .record-block-dense .record-list .table > thead > tr > th,
    .record-block-dense .record-list .table > tbody > tr > td {
        min-height: 21px;
        white-space: normal;
    }

    /*
     * Перенос — только по пробелам, никогда внутри слова, ни в сжатом
     * виде, ни в просторном. Без этого правила короткое слово в узком
     * столбце ломалось по буквам («Гот / ов / о») — столбцу это
     * не помогало вместить слово целиком, только портило вид
     * (07.09.2026, разбор координатора: `/schedule`, распределение
     * ширин по природным размерам столбцов зависит от того, где
     * на самом деле кончается слово, а не где браузер решит его
     * оборвать).
     *
     * `.record-list-value-fixed` этим правилом не задет — у него свой,
     * осознанный перенос где угодно (`overflow-wrap: anywhere`,
     * выше в этом файле): длинный артикул без единого пробела иначе
     * просто вылез бы за край в столбце заданной ширины.
     */
    .record-list .table > thead > tr > th,
    .record-list .table > tbody > tr > td {
        overflow-wrap: normal;
        word-break: normal;
    }

    .record-block-dense .record-list .table > thead > tr > th,
    .record-block-dense .document-lines > thead > tr > th {
        font-weight: 500;
    }

    /*
     * Подсветка строки под курсором убрана намеренно: при 21 точке
     * на строку полоса под мышью перекрывает соседние и мешает вести
     * взгляд по столбцу. В просторном виде она остаётся.
     */
    .record-block-dense .record-list .table.table-hover > tbody > tr:hover > * {
        --bs-table-bg-state: transparent;
    }

    /*
     * Строка отбора — под ту же высоту, что строка данных. Иначе поля
     * втрое выше строк, и сжатая таблица выглядит так, будто отбор
     * приклеили от другой страницы.
     */
    .record-block-dense .record-list .table > thead > tr > th.record-list-filter {
        padding: 1px 2px;
    }

    /*
     * `.wms-window-filter` — то же самое поле отбора, только внутри
     * окна, а не строки таблицы (задача №124 трекера, замечание
     * координатора: «в окне их будет много» — поля и подписи должны
     * быть той же высоты и шрифта, что отборы в полосе списка, а не
     * обычная крупная форма). Не второе правило — та же строка
     * объявления, второй селектор через запятую (правило 0.6): окно
     * не привязано к плотности списка (`record-block-dense`), поэтому
     * свой признак, а не чужой родитель.
     */
    .record-block-dense .record-list-filter .form-control,
    .record-block-dense .record-list-filter .form-select,
    .record-block-dense .record-list-filter .btn,
    .record-block-dense .record-list-filter .input-group-text,
    .wms-window-filter .form-control {
        font-size: 13px;
        line-height: 18px;
        min-height: 0;
        height: 22px;
        padding: 1px 6px;
        border-radius: 2px;
    }

    .record-block-dense .record-list-filter .form-select {
        padding-right: 1.6rem;
        background-size: 10px 8px;
    }

    /*
     * Поля в строках документа — той же высоты, что строки.
     *
     * Строки документа не список записей: там в каждой ячейке поле
     * ввода или выпадающий список, и без этого сжатая таблица
     * оставалась бы просторной — высоту держали бы поля.
     */
    .record-block-dense .document-lines .form-control,
    .record-block-dense .document-lines .form-select,
    .record-block-dense .document-lines .btn,
    .record-block-dense .record-list .table tr.record-group-member .form-control,
    .record-block-dense .record-list .table tr.record-group-member .btn {
        font-size: 13px;
        line-height: 18px;
        min-height: 0;
        height: 22px;
        padding: 1px 6px;
        border-radius: 2px;
    }

    .record-block-dense .document-lines .form-select {
        padding-right: 1.6rem;
        background-size: 10px 8px;
    }

    /*
     * Картинка товара в строке документа — 50 точек, и строка под неё
     * подстраивается.
     *
     * Раньше здесь стояло 19: сжатая строка держала высоту 21 точку,
     * и картинку ужимали под неё. Но в документе товар узнают именно
     * по снимку — на девятнадцати точках не разглядеть ни цвет,
     * ни фасон (слово Александра 06.09.2026). Строк в документе
     * не тысячи, а десятки, и высота здесь дешевле узнавания.
     *
     * В списках всё остаётся как было: там своя картинка и свои
     * правила (правило 6.5).
     */
    .record-block-dense .document-lines .document-line-photo {
        /* Потолком, не жёстко: поле ровно по картинке (15.09.2026). */
        height: auto;
        max-height: 50px;
        max-width: 38px;
    }

    /* Ячейка снимка не сжимает его до высоты сжатой строки. */
    .record-block-dense .document-lines > tbody > tr > td[data-line-photo] {
        height: auto;
    }

    .record-block-dense .record-list-filter .record-list-clear,
    .wms-window-filter .record-list-clear {
        font-size: 15px;
        line-height: 20px;
    }

    /*
     * Подпись поля — тем же шрифтом, что и само поле (13px): рядом
     * стоящий обычный текст рядом с полем в 22px высотой выглядел бы
     * непропорционально крупным.
     */
    .wms-window-filter label {
        font-size: 13px;
    }

    /* Ряды полей плотнее друг к другу, когда их несколько. */
    .wms-window-filter + .wms-window-filter {
        margin-top: .35rem;
    }
}

/* --- Переключатель вида в шапке блока --- */

/*
 * Два значка рядом: сжатый вид и просторный. Выбранный подсвечен.
 *
 * На телефоне не показывается: там строка и так карточка.
 */
.record-block-density {
    display: none;
}

@media (min-width: 768px) {
    .record-block-density {
        display: inline-flex;
        gap: 2px;
    }
}

/*
 * Шапка сжатого блока — низкая, как и всё в нём.
 *
 * У Bootstrap на неё уходит 8 точек сверху и 16 по бокам: заголовок
 * блока получается выше двух строк таблицы. В сжатом списке это
 * несоразмерно, а место на экране — главный ресурс (правило 6.6).
 *
 * Правится в одном месте и потому одинаково везде: журнал, товары,
 * склады, «Удалённое», строки документа.
 */
.record-block-dense > .card-header {
    padding: 3px 8px;
    min-height: 0;
}

.record-block-dense > .card-header .card-title {
    font-size: .9rem;
    line-height: 1.3;
}

/*
 * Строка, которая открывается нажатием.
 *
 * Курсор — единственный внятный признак: подсветки под мышью
 * в сжатом виде нет намеренно (правило 6.5), а подчёркивать всю
 * строку значило бы превратить таблицу в список ссылок.
 */
.record-list-row-open {
    cursor: pointer;
}

/*
 * Переключатель вида — та же кнопка-значок, что и остальные в шапке
 * (`.record-block-icon-button`, см. её докблок выше) — стиль и подсветка
 * выбранного вида у неё общие, здесь заводить больше нечего.
 */

/* --- 8.0. Ячейка товара: снимок и две строки текста --- */

/*
 * Один вид во всех местах, где показывается товар (виджет `ProductCell`).
 *
 * Снимок задаёт высоту строки, и в неё встают две строки текста:
 * артикул продавца и название под ним. Так столбец выходит заметно уже,
 * чем когда название тянется в одну строку с артикулом, — слово
 * Александра 06.09.2026.
 */
.product-cell {
    display: flex;
    align-items: center;
    gap: .4rem;
    min-width: 0;
}

/*
 * Текст сжимается, а снимок нет: без этого длинное название растягивает
 * столбец на пол-экрана и выталкивает соседние за край.
 */
.product-cell-text {
    display: flex;
    flex-direction: column;
    justify-content: center;
    min-width: 0;
    line-height: 1.25;
}

.product-cell-line {
    display: flex;
    align-items: baseline;
    gap: .35rem;
    min-width: 0;
}

.product-cell-code {
    font-weight: 500;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Размер приписан к артикулу — мелкий и приглушённый, чтобы не спорить
   с ним за внимание. */
.product-cell-size {
    font-size: 11px;
    color: var(--bs-secondary-color, #6c757d);
    white-space: nowrap;
}

/*
 * Название — вторая строка, приглушённая и обрезанная многоточием.
 * Оно длинное и читается редко: товар узнают по артикулу и снимку.
 */
.product-cell-title {
    font-size: 11px;
    color: var(--bs-secondary-color, #6c757d);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* --- 8.0.0.1. Строка-группа (общая возможность списка, задача №126) --- */

/*
 * Строка-участник спрятана, пока её группа свёрнута
 * (`record-group.js`, `data-group-toggle`).
 */
.record-group-hidden {
    display: none;
}

/*
 * +/− свёрнутой/развёрнутой группы — размером с галочку рядом (замечание
 * координатора при повторной сдаче задачи №126), без своей рамки и фона:
 * это не отдельная кнопка шапки, а служебный значок в её же узком
 * столбце — заметнее галочки он быть не должен, а столбец не должен
 * раздаваться шире её из-за него.
 */
.record-group-toggle,
.record-group-toggle-all {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1em;
    height: 1em;
    padding: 0;
    margin: 0;
    border: 0;
    background: none;
    font-size: inherit;
    line-height: 1;
    color: var(--bs-secondary-color, #6c757d);
}

.record-group-toggle:hover,
.record-group-toggle-all:hover {
    color: var(--bs-body-color, #212529);
}

/*
 * Сводная строка группы — заметно весомее строк-участников (замечание
 * координатора при третьей сдаче задачи №126: «жирнее и сероватая
 * подсветка»): вес текста и лёгкая заливка фона разом, чтобы взгляд
 * сразу находил её среди размеров под ней, даже когда группа свёрнута.
 * Строка сама кликабельна — курсор-рука, тот же признак, что открывает
 * окно у обычной строки в остальных списках.
 */
.record-group-header > td {
    font-weight: 700;
    background: var(--bs-tertiary-bg, #f8f9fa);
}

.record-group-header {
    cursor: pointer;
}

/*
 * Размер — по правому краю столбца (замечание координатора при сдаче
 * задачи №126): столбец «Товар» у обычной строки выровнен по левому
 * краю, а у строки-участника в нём остаётся только размер — правый
 * край собирает его в ровный столбик под числами остальных столбцов,
 * а не оставляет прижатым к левому краю пустой ширины.
 *
 * `.product-cell` — флекс-строка (`display: flex`), и внутри неё
 * выравнивает не `text-align`, а `justify-content`: одного признака
 * на `td` было бы мало.
 */
.record-group-member td.record-list-main {
    text-align: right;
}

.record-group-member .product-cell {
    justify-content: flex-end;

    /*
     * Отступ от края — как у чисел в соседних столбцах (замечание
     * координатора при повторной сдаче задачи №126: было «впритык»,
     * затем «ещё дальше от края» — увеличено вторично). Числа получают
     * его от обычного `padding` ячейки, а этот блок — флекс на всю
     * ширину ячейки, и без своего отступа прижимался к самому краю
     * содержимого.
     */
    padding-right: .9rem;
}

/*
 * Снимок и текст артикула у строки-участника не повторяем — сводная
 * строка над ней уже их показала. Видно только размер: он один и меняет
 * смысл строки (Александр: «размер напротив столбца «Размер»» — того
 * самого места в ячейке, где размер и так стоит у обычной строки).
 *
 * Прячем стилями, не вторым видом `ProductCell`: разметка ячейки
 * ровно та же, что у обычной строки размера (`html` столбца «Товар»
 * не различает группу и обычный показ), второго способа рисовать
 * товар не заводим (правило 0.6).
 */
.record-group-member .product-cell > .wms-gallery,
.record-group-member .product-cell > .wms-gallery-empty {
    display: none;
}

.record-group-member .product-cell-code,
.record-group-member .product-cell-title {
    display: none;
}

/*
 * Дата и время — одной строкой у строки-размера (замечание координатора
 * при повторной сдаче задачи №126): двухстрочный вид (`RecordList::
 * stackedDateTime()`) держится ради миниатюры, которая задаёт высоту
 * строки, а у строки-размера миниатюры нет — второй строке нечего
 * занимать. Формат остаётся `d.m.Y H:i:s`, тем же текстом, что
 * и в двухстрочном виде — только без переноса и без приглушения
 * времени отдельным цветом, чтобы строка читалась одним числом.
 */
.record-group-member .record-list-date-line,
.record-group-member .record-list-time-line {
    /* `!important` — сами строки идут с `d-block`, бутстраповской
       утилитой, тоже с `!important`: без него не пересилить. */
    display: inline !important;
    color: inherit;
}

.record-group-member .record-list-time-line::before {
    content: ' ';
}

/* --- 8.0.1. Строка, где товара не хватает --- */

/*
 * Свободное на складе площадки ушло в минус: заказов больше, чем
 * лежит на складе. Красим всю строку, а не одно число: человек ищет
 * глазами не столбец, а товар, и в таблице на две сотни строк
 * одна красная цифра теряется (слово Александра 06.09.2026).
 *
 * Фон мягкий: строк таких бывает много, и заливка в полную силу
 * превратила бы таблицу в сплошное красное поле, где снова ничего
 * не видно.
 *
 * Тот же класс красит и строку табличной части документа, где после
 * ввода остаток на главном складе документа стал отрицательным: тот
 * же смысл — «не хватает», тот же стиль, из этого же места, вторая
 * заводиться не должна (правило вида, координатор 07.09.2026). Ставит
 * его `common\widgets\DocumentTable` (таблица `.document-lines`),
 * а не только `RecordList`.
 */
.record-list .table > tbody > tr.record-list-row-short > td,
.document-lines > tbody > tr.record-list-row-short > td {
    background-color: rgba(var(--bs-danger-rgb, 220, 53, 69), .07);
}

/* Само число — жирным: оно и есть то, ради чего строка покрашена. */
.record-list .table > tbody > tr.record-list-row-short .text-danger,
.document-lines > tbody > tr.record-list-row-short .text-danger {
    font-weight: 600;
}

/* --- 8.0.2. Шестерёнка: какие столбцы показывать --- */

/*
 * Столбцов в таблице учёта много, и нужны они не все сразу. Человек
 * прячет лишние и переставляет остальные сам, а выбор запоминается
 * за ним (просьба Александра 06.09.2026).
 */
.record-col-hidden {
    display: none;
}

/* Шестерёнка — та же кнопка-значок, что у остальных в шапке (`.record-block-icon-button`). */

/* Список столбцов не должен вырастать выше экрана: их бывает и двадцать. */
/*
 * Обёртка шестерёнки — точка отсчёта для её списка.
 */
.record-columns {
    position: relative;
}

/*
 * Список столбцов раскрывается влево, правым краем к кнопке.
 *
 * Кнопка стоит у правого края шапки, и список, раскрытый вправо,
 * наполовину уходил за край экрана. У Bootstrap для этого есть готовый
 * класс, но работает он только когда меню открывает он сам: правило
 * висит на признаке, который ставит его же скрипт. А открываем мы —
 * иначе нажатие складывало бы блок (см. историю выше). Поэтому
 * положение задаём здесь.
 *
 * Ширину ограничиваем шириной окна: у списка с полутора десятками
 * столбцов меню длинное, и на узком экране оно всё равно упёрлось бы
 * в край.
 */
.record-columns-menu {
    position: absolute;
    top: 100%;
    right: 0;
    left: auto;
    z-index: 1030;
    min-width: 24rem;
    max-width: calc(100vw - 1rem);
    max-height: 60vh;
    overflow-y: auto;
    padding: .35rem;

    /*
     * Список крутится пальцем как обычно — жест по ручке в это
     * не входит, у неё свой запрет ниже. Без этой строки браузер
     * иногда решает сам, что делать с вертикальным движением пальца,
     * и перетаскивание за ручку срывается на прокрутку страницы.
     */
    touch-action: pan-y;
}

/*
 * На телефоне список столбцов — лист снизу экрана, как в приложениях.
 *
 * Раскрывать его «под кнопкой» негде: шапка блока стоит ниже середины
 * страницы, и список уходил вниз за край — на товарах он занимал
 * 420 точек при видимых 700, начинаясь от самого низа. Считать
 * оставшееся место стилями нечем.
 *
 * Прижатый к низу лист снимает вопрос целиком: он всегда на виду,
 * всегда одинаковой высоты и прокручивается внутри себя. Так же
 * ведут себя настройки в приложениях площадок, и человеку это
 * привычно.
 */
@media (max-width: 767.98px) {
    .record-columns-menu {
        position: fixed;
        top: auto;
        bottom: .5rem;
        left: .5rem;
        right: .5rem;
        width: auto;
        min-width: 0;
        max-width: none;
        max-height: 70vh;
    }
}

.record-columns-list {
    margin: 0;
    padding: 0;
    list-style: none;
}

.record-columns-item {
    display: flex;
    align-items: center;
    gap: .4rem;
    padding: .15rem .25rem;
    border-radius: var(--bs-border-radius-sm, .25rem);
    cursor: default;
}

.record-columns-item:hover {
    background: var(--bs-tertiary-bg, #f8f9fa);
}

/*
 * Промежуток на месте будущей вставки — соседи раздвинуты заранее,
 * а не просто подсвечено место (просьба Александра 07.09.2026).
 * Пунктир держит его заметным, но лёгким: строк в списке бывает
 * полтора десятка, и промежуток не должен читаться как ошибка.
 */
.record-columns-gap {
    border: 1px dashed var(--bs-border-color, #dee2e6);
    border-radius: var(--bs-border-radius-sm, .25rem);
    background: var(--bs-tertiary-bg, #f8f9fa);
    list-style: none;
}

/*
 * Строка, которую тащат, — копия поверх всего списка, летит за
 * пальцем. Настоящая строка в это время скрыта (`display: none`
 * в скрипте), а на её месте — промежуток выше.
 */
.record-columns-ghost {
    position: fixed;
    margin: 0;
    pointer-events: none;
    z-index: 1040;
    opacity: .92;
    background: var(--bs-body-bg);
    box-shadow: 0 8px 20px rgba(0, 0, 0, .25);
}

/*
 * Ручка: за неё тащат — мышью и пальцем одинаково, событиями
 * указателя (`record-list.js`).
 *
 * `touch-action: none` забирает жест по ручке у браузера целиком:
 * без этого палец на ручке пытается прокрутить страницу вместе
 * с перетаскиванием, и движение дёргается.
 */
.record-columns-grip {
    color: var(--bs-secondary-color, #6c757d);
    cursor: grab;
    touch-action: none;
}

.record-columns-name {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Название столбца редактируется прямо в его строке настройки. */
.record-columns-label {
    flex: 1 1 auto;
    min-width: 7rem;
}

/*
 * Стрелки рядом с ручкой — не украшение: на телефоне тащить пальцем
 * ненадёжно, а порядок менять надо и там.
 */
.record-columns-arrows {
    display: flex;
    gap: .15rem;
}

.record-columns-move {
    padding: 0 .3rem;
    line-height: 1.2;
    border: 1px solid var(--bs-border-color, #dee2e6);
    color: var(--bs-secondary-color, #6c757d);
}

.record-columns-footer {
    display: flex;
    flex-wrap: wrap;
    gap: .3rem;
    justify-content: flex-end;
    padding-top: .35rem;
    margin-top: .35rem;
    border-top: 1px solid var(--bs-border-color, #dee2e6);
}

/* --- 8.1. Кнопки действия в шапке блока --- */

/*
 * Кнопка «Обновить товары» и ей подобные — теперь значок без подписи,
 * тот же вид, что у прочих кнопок шапки (`.record-block-icon-button`,
 * слово Александра 14.09.2026: одна кнопка — один размер везде, второй
 * стиль здесь не заводим). Раньше у кнопки был текст, и для неё стоял
 * свой мелкий размер (`font-size: 13px`) — с уходом текста он остался
 * не у дел и расходился с соседними значками; правило снято совсем.
 *
 * Пока идёт обмен, кнопка выключена и показывает, чего ждём.
 * Приглушаем её, но оставляем читаемой: человек должен видеть текст
 * «Спрашиваю Wildberries…», а не гадать, нажалось ли.
 */
.record-block-actions .btn:disabled {
    opacity: .75;
}

/* --- 9. Правка в строке и итог под таблицей --- */

/*
 * Поле прямо в ячейке списка: себестоимость на странице «Товары».
 * Вписал число, Enter — сохранилось, курсор строкой ниже. Разметку
 * даёт `RecordList` (столбец `input`), поведение — `record-input.js`.
 *
 * **Поле видно всегда, в каждой строке.** Сперва я сделал его
 * прозрачным — без рамки и фона, чтобы таблица не превращалась
 * в решётку из двух сотен рамок. Вышло хуже: Александр посмотрел
 * и увидел не поле, а текст с прочерком, в который непонятно, можно
 * ли писать. А просили именно поле — «как в Excel»: подошёл, вписал,
 * нажал Enter (решение 03.09.2026).
 *
 * Поэтому рамка есть всегда, фон белый, число прижато вправо. Узкое:
 * цена — это четыре-пять знаков, шире делать незачем (правило 6.6).
 */
.record-list-input {
    width: 100%;
    min-width: 4.5rem;
    max-width: 6.5rem;
    text-align: right;
    background: var(--bs-body-bg, #fff);
    border-color: var(--bs-border-color, #dee2e6);
    box-shadow: none;
}

.record-list-input:hover {
    border-color: var(--bs-secondary-color, #6c757d);
}

.record-list-input:focus {
    border-color: var(--bs-primary, #0d6efd);
}

/*
 * Пока ответ не пришёл — поле приглушено. Сохранение занимает
 * доли секунды, но на медленной связи человек должен видеть, что
 * число ушло, а не думать, что ввод пропал.
 */
.record-list-input-saving {
    opacity: .6;
}

/*
 * Ответ пришёл — поле коротко мигает и гаснет.
 *
 * Кнопки «Сохранить» у него нет, и без этого человек не понимает,
 * применилось ли число: нажал Enter, курсор ушёл ниже — и всё.
 * Зелёное с галочкой слева значит «записалось», красное — «нет»,
 * и тогда причина остаётся в подсказке поля.
 *
 * Гашение плавное и делается снятием класса: переход объявлен
 * у самого поля, поэтому цвет уходит сам, без второго класса
 * и без счёта времени в стилях.
 */
.record-list-input {
    transition: background-color .5s ease, background-image .5s ease;
}

.record-list-input-saved {
    background-color: rgba(var(--bs-success-rgb, 25, 135, 84), .18);

    /* Галочка слева: справа стоит само число, и его не закрываем. */
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23198754'%3E%3Cpath d='M12.7 3.7 6 10.4 3.3 7.7l-1 1L6 12.4l7.7-7.7z'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: left .3rem center;
    background-size: 11px 11px;
}

.record-list-input-failed {
    background-color: rgba(var(--bs-danger-rgb, 220, 53, 69), .18);
}

/*
 * Итог под таблицей. Липнет к низу окна списка: в длинном списке
 * сумма нужна на виду, а не в конце прокрутки.
 */
.record-list-footer > tr > td {
    position: sticky;
    /* Над строкой состояния кабинета (`--wms-footer`, `app.css`). */
    bottom: var(--wms-footer, 0px);
    /* Слой из общей шкалы (раздел 5): выше липкой колонки действий,
       но подвал не спорит с шапкой блока — они на разных краях. */
    z-index: 20;
    background: var(--bs-tertiary-bg, #f8f9fa);
    border-top: 1px solid var(--bs-border-color, #dee2e6);
}

@media (min-width: 768px) {
    /*
     * Поле в сжатой строке — той же высоты, что строка. Способ тот же,
     * что у полей в строках документа выше: без этого сжатая таблица
     * осталась бы просторной, высоту держали бы поля.
     */
    .record-block-dense .record-list .table .record-list-input {
        font-size: 13px;
        line-height: 18px;
        min-height: 0;
        height: 20px;
        padding: 0 4px;
        border-radius: 2px;
    }

    /* Подвал живёт по тем же правилам сетки, что и строки. */
    .record-block-dense .record-list .table > tfoot > tr > td {
        padding: 3px 4px;
        height: 21px;
        border: 0 solid var(--bs-border-color, #dee2e6);
        border-width: 1px 1px 0 0;
        white-space: nowrap;
        vertical-align: middle;
    }
}

/*
 * Строка «Загружаю ещё…» с крутилкой: стоит в конце таблицы, пока едет
 * следующая порция (record-list.js, loadMore). Одна ячейка на всю ширину,
 * по центру, приглушённо — это не запись, а состояние списка.
 * На телефоне строки идут карточками; эта — тоже, но без рамки карточки.
 */
.record-list .table > tbody > tr[data-more-loading] > td,
.document-lines > tbody > tr[data-more-loading] > td {
    text-align: center;
    color: var(--bs-secondary-color, #6c757d);
    padding: .75rem;
    background: transparent;
    box-shadow: none;
}

/*
 * Телефон: плавающая шапка и подвал (record-list-float.js) не нужны —
 * шапки таблицы там нет вовсе, строки идут карточками. А строки-призраки
 * (data-float-ghost, копии шапки с visibility: collapse, держат ширины
 * столбцов на широком экране) в раскладке карточек перестают быть
 * строками таблицы: collapse для них — то же, что hidden, и они занимают
 * место пустым белым куском между кнопками и первой карточкой (планшет
 * Александра, 15.09.2026: сузил окно — появился, перезагрузил — пропал).
 * Прячем всё это на узком экране; обратно на широкий — скрипт собирает
 * шапку заново.
 */
@media (max-width: 767.98px) {
    /* Вес выше правил карточек («тело таблицы — блок», .record-list
       .table > tbody), иначе призраки всё равно рисуются блоком. */
    .record-list .record-list-float-head,
    .record-list .record-list-float-foot,
    .record-list .table > tbody[data-float-ghost],
    .record-list tbody[data-float-ghost] {
        display: none;
    }

    /*
     * Строка с нехваткой на телефоне: фон одним куском на всю карточку,
     * а не розовыми плашками на каждой ячейке — в раскладке карточек
     * ячейки стоят россыпью, и покрашенные по одной они читались как
     * ошибка вёрстки (планшет Александра, 15.09.2026).
     */
    .record-list .table > tbody > tr.record-list-row-short,
    .document-lines > tbody > tr.record-list-row-short {
        background-color: rgba(var(--bs-danger-rgb, 220, 53, 69), .07);
    }

    .record-list .table > tbody > tr.record-list-row-short > td,
    .document-lines > tbody > tr.record-list-row-short > td {
        background-color: transparent;
    }
}
