/* ============================================================================
   mp-fulfillment · screens.css
   Прикладной слой поверх дизайн-системы. Требует tokens.css и components.css.

   ЗАЧЕМ ОТДЕЛЬНЫЙ ФАЙЛ, А НЕ ДОПИСЫВАНИЕ В components.css:
   components.css и mobile.css — библиотека компонентов 1–25, она приходит из
   макетов и обновляется целиком. Здесь живёт то, чего в библиотеке нет по её
   собственному замыслу: каркас страницы приложения, приведение виджетов Django
   к виду контролов дизайн-системы и три компонента (26–28), появившиеся у
   реальных экранов. Разделение позволяет заново залить библиотеку из макетов,
   не потеряв прикладной слой и не разбираясь, где чьи строки.

   ПРАВИЛА ТЕ ЖЕ, ЧТО У components.css, и проверяются тем же скриптом
   (файл добавлен в COMPONENT_CSS в tools/check_design_system.py):
   ни одного литерального цвета, кегля, отступа, радиуса и толщины рамки —
   только var(--mp-*). Ширины и высоты раскладки — исключение.
   ========================================================================= */

/* ============================================================================
   26 · PageLayout — каркас страницы приложения
   AppShell (01) даёт шапку и навигацию, но не то, как устроена страница внутри
   main. У 71 экрана повторяется одно и то же: заголовок с действиями, пара
   блоков, таблица, пояснение под ней. Это и есть компонент.
   ========================================================================= */

.mp-page { display: flex; flex-direction: column; gap: var(--mp-sp-5); }
.mp-page__head {
  display: flex; align-items: flex-start; justify-content: space-between;
  flex-wrap: wrap; gap: var(--mp-sp-3);
}
.mp-page__title { margin: 0; font-size: var(--mp-fs-6); line-height: var(--mp-lh-6); font-weight: var(--mp-weight-bold); }
.mp-page__actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--mp-sp-2); }
/* Форма внутри полосы действий не должна ломать строку: кнопка «Сбросить
   пароль» — это POST-форма, а не ссылка, и таких кнопок в ряду несколько.
   То же в подвале панели и в ряду кнопок под формой. */
.mp-page__actions form,
.mp-panel__foot form,
.mp-form__actions > form,
.mp-btn-row form { display: contents; }

.mp-page__section { margin: 0; font-size: var(--mp-fs-5); line-height: var(--mp-lh-5); font-weight: var(--mp-weight-bold); }

/* Панель — блок содержимого с необязательной шапкой. От EntityCard (10)
   отличается тем, что не описывает сущность: внутри может быть что угодно. */
.mp-panel {
  background: var(--mp-surface);
  border: var(--mp-border-width) solid var(--mp-border);
  border-radius: var(--mp-radius-sm);
  overflow: hidden;
}
.mp-panel__head {
  display: flex; align-items: center; justify-content: space-between; gap: var(--mp-sp-3);
  padding: var(--mp-sp-2) var(--mp-sp-3);
  border-bottom: var(--mp-border-width) solid var(--mp-border);
  font-size: var(--mp-fs-3); font-weight: var(--mp-weight-medium);
}
.mp-panel__head h2, .mp-panel__head h3 { margin: 0; font-size: var(--mp-fs-3); font-weight: var(--mp-weight-medium); }
.mp-panel__body {
  display: flex; flex-direction: column; gap: var(--mp-sp-3);
  padding: var(--mp-sp-4);
  font-size: var(--mp-fs-3); line-height: var(--mp-lh-3);
}
.mp-panel__body > p { margin: 0; }
.mp-panel__foot {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--mp-sp-2);
  padding: var(--mp-sp-3) var(--mp-sp-4);
  border-top: var(--mp-border-width) solid var(--mp-border);
  background: var(--mp-surface-sunken);
}
/* таблица внутри панели уже обрамлена самой панелью */
.mp-panel > .mp-table { border-top: 0; }

/* Колонки: два-три блока в ряд, на узком экране — друг под другом.
   Нижняя граница дорожки — min(380px, 100%), а не голые 380px: дорожка
   шире контейнера не помещается в одну колонку и распирает страницу вбок
   (на 360 — до 604 px). С min() она сжимается до ширины контейнера, и
   обещание «друг под другом» исполняется без медиа-запроса (РЕШ-059, п. 3). */
.mp-cols {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(min(380px, 100%), 1fr));
  gap: var(--mp-sp-4); align-items: start;
}
.mp-cols > * { min-width: 0; }

/* Сводка — строка ключевых фактов над таблицей. Не метрики (те крупные и
   считаются), а «период такой-то, позиций столько-то»: читается один раз. */
.mp-summary {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--mp-sp-2) var(--mp-sp-5);
  padding: var(--mp-sp-3) var(--mp-sp-4);
  background: var(--mp-surface); border: var(--mp-border-width) solid var(--mp-border);
  border-radius: var(--mp-radius-sm);
  font-size: var(--mp-fs-3);
}
.mp-summary b { font-weight: var(--mp-weight-medium); }

/* Пояснение под таблицей: почему цифра считается так, а не иначе. Их в проекте
   много и они несут смысл — это не «мелкий серый текст», а документация
   экрана на самом экране. */
.mp-hint {
  margin: 0; max-width: 96ch;
  font-size: var(--mp-fs-2); line-height: var(--mp-lh-3);
  color: var(--mp-text-secondary);
}
.mp-hint code { font-family: var(--mp-font-mono); }
/* Вторая строка в ячейке таблицы: название под артикулом, ФИО под логином. */
.mp-subline { display: block; font-size: var(--mp-fs-2); line-height: var(--mp-lh-2); color: var(--mp-text-muted); }
.mp-nowrap { white-space: nowrap; }
.mp-truncate { display: block; max-width: 40ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Пустая выборка внутри таблицы. Полноэкранный EmptyState (08) применяется,
   когда пусто вообще; строка в таблице — когда пусто по фильтру и шапка
   таблицы должна остаться видимой. */
.mp-table__empty {
  padding: var(--mp-sp-5) var(--mp-sp-3);
  text-align: center; color: var(--mp-text-secondary);
}
.mp-table caption { padding: var(--mp-sp-2) var(--mp-sp-3); text-align: left; font-weight: var(--mp-weight-medium); }

/* Список ссылок «куда смотреть дальше» */
.mp-links { display: flex; flex-direction: column; gap: var(--mp-sp-2); margin: 0; padding: 0; }
.mp-links li { list-style: none; }
/* Перечень претензий площадки к карточке */
.mp-problems { margin: 0; padding-left: var(--mp-sp-4); font-size: var(--mp-fs-3); line-height: var(--mp-lh-3); }
.mp-problems li { margin-bottom: var(--mp-sp-1); }

/* Плитка-ссылка на отчёт */
.mp-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr)); gap: var(--mp-sp-3); }
.mp-tile {
  display: flex; flex-direction: column; gap: var(--mp-sp-1);
  padding: var(--mp-sp-4);
  background: var(--mp-surface); border: var(--mp-border-width) solid var(--mp-border);
  border-radius: var(--mp-radius-sm); color: var(--mp-text);
}
.mp-tile:hover { border-color: var(--mp-border-control); background: var(--mp-surface-sunken); color: var(--mp-text); }
.mp-tile b { font-size: var(--mp-fs-4); line-height: var(--mp-lh-4); }
.mp-tile span { font-size: var(--mp-fs-2); color: var(--mp-text-secondary); line-height: var(--mp-lh-3); }

/* ============================================================================
   Формы: приведение виджетов Django к контролам дизайн-системы
   Django рисует поля сам, класса .mp-input у них нет и добавлять его в
   forms.py на каждое поле — тридцать мест правки вместо одного. Поэтому
   правило по тегу внутри .mp-field и .mp-filters.
   ========================================================================= */

.mp-field input:not([type="checkbox"]):not([type="radio"]),
.mp-field select,
.mp-field textarea,
.mp-filters input:not([type="checkbox"]):not([type="radio"]),
.mp-filters select {
  height: var(--mp-control-h); padding: 0 var(--mp-sp-3);
  border: var(--mp-border-width) solid var(--mp-border-control);
  border-radius: var(--mp-radius-sm); background: var(--mp-surface);
  font: inherit; font-size: var(--mp-fs-3); color: var(--mp-text);
  max-width: 100%;
}
.mp-field textarea { height: auto; padding: var(--mp-sp-2) var(--mp-sp-3); line-height: var(--mp-lh-3); resize: vertical; }
.mp-field input[type="file"] { height: auto; padding: var(--mp-sp-2); border-style: dashed; }
.mp-field input:focus, .mp-field select:focus, .mp-field textarea:focus,
.mp-filters input:focus, .mp-filters select:focus {
  border-color: var(--mp-info-solid); outline: none; box-shadow: var(--mp-focus-ring);
}
.mp-field--invalid input, .mp-field--invalid select, .mp-field--invalid textarea {
  border-color: var(--mp-danger-solid); background: var(--mp-danger-surface);
}
.mp-field input[type="checkbox"], .mp-field input[type="radio"] { accent-color: var(--mp-info-solid); }

/* Габариты одной строкой (компонент 17 «DimensionsField», catalog/_dimensions.html).
   Ширину полей компонент задаёт классом .mp-dims__input, но у виджетов Django
   классов нет — их вид задаётся правилом по тегу внутри .mp-field, и здесь
   ровно тот же случай. Без этих двух строк четыре поля со своей природной
   шириной (у <input> это ~20 знаков) не помещаются в колонку формы и вес
   уезжает за правый край: min-width: auto у флекс-элемента не даёт им сжаться.
   Подписи полей остаются видимыми, поэтому каждое поле — это .mp-field
   со своей подписью, а не голый input, как в партиале библиотеки. */
.mp-dims__row .mp-field { min-width: 0; flex: 1; }
.mp-dims__row .mp-field input { width: 100%; }

/* Форма-колонка: поля друг под другом. Ширина ограничена не ради красоты —
   строка ввода длиной в экран читается хуже, чем поле в 480px. */
.mp-form { display: flex; flex-direction: column; gap: var(--mp-sp-4); max-width: 560px; }
.mp-form--wide { max-width: none; }
.mp-form__actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--mp-sp-2); }
.mp-form__actions .mp-input, .mp-form__actions input, .mp-form__actions select { flex: 0 1 auto; }
/* Количество в строке состава приёмки: контрол стоит в ячейке таблицы и на всю
   колонку не тянется — короткое число должно читаться как число, а не как
   строка ввода. Это модификатор существующего .mp-input, а не новый компонент. */
.mp-table .mp-input--qty { width: 88px; text-align: right; font-variant-numeric: tabular-nums; }

/* Окно выбора товара из номенклатуры — нативный <dialog> в оформлении Modal (06).
   Браузер даёт запирание фокуса, Esc, подложку, возврат фокуса и верхний слой
   (иначе окно обрезал бы overflow: hidden у панели). Свои у dialog только рамка
   с отступом — их и снимаем; подложка красится токеном общей плашки. */
dialog.mp-modal { border: 0; padding: 0; max-width: 94vw; max-height: 92vh; }
dialog.mp-modal::backdrop { background: var(--mp-overlay); }
/* Список номенклатуры шире разговора «да/нет»: в нём таблица из пяти колонок,
   и прокручиваться должна таблица, а не окно целиком. */
.mp-modal--wide { width: min(920px, 94vw); }
.mp-modal--wide .mp-dialog__body { max-height: 62vh; }
/* Листание внутри окна идёт htmx-ом и потому кнопкой, а не ссылкой: ссылка без
   адреса — мёртвый элемент, когда сценарий не сработал. Вид берётся у обычной
   страницы пейджера, гасятся только собственные умолчания кнопки. */
button.mp-pager__page { background: none; font: inherit; cursor: pointer; }
.mp-fieldset { border: 0; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--mp-sp-3); }
.mp-fieldset > legend { padding: 0; font-size: var(--mp-fs-4); font-weight: var(--mp-weight-medium); }

/* Подпись с флажком: цель — вся подпись, не квадрат 13px. */
.mp-checkline {
  display: inline-flex; align-items: center; gap: var(--mp-sp-2);
  min-height: var(--mp-control-h); padding-right: var(--mp-sp-2);
  font-size: var(--mp-fs-3); cursor: pointer;
}
.mp-checkline input { accent-color: var(--mp-info-solid); }
/* Подпись слева от контрола в полосе фильтров: «Продавец [список]» */
.mp-filters label {
  display: inline-flex; align-items: center; gap: var(--mp-sp-2);
  font-size: var(--mp-fs-2); color: var(--mp-text-secondary);
}
.mp-filters label.mp-checkline { font-size: var(--mp-fs-3); color: var(--mp-text); }

.mp-btn--block { width: 100%; justify-content: center; }

/* Раскрывающийся блок: изменения в журнале, подробности строки, «Ещё
   фильтры» списка заказов. inline-flex гасит штатный маркер <summary>,
   поэтому указатель свой: треугольник слева, при открытии смотрит вниз.
   Состояние несёт форма значка, а не цвет. Для диктора значок пуст —
   раскрыто ли, <summary> объявляет сам (CC-464). */
.mp-details > summary {
  display: inline-flex; align-items: center; gap: var(--mp-sp-1);
  min-height: var(--mp-row-h); cursor: pointer; list-style: none;
  font-size: var(--mp-fs-2); color: var(--mp-text-link);
}
.mp-details > summary::-webkit-details-marker { display: none; }
.mp-details > summary::before {
  content: "\25BA";
  content: "\25BA" / ""; font-size: var(--mp-fs-2); line-height: 1;
}
.mp-details[open] > summary::before { transform: rotate(90deg); }
.mp-details[open] > summary { margin-bottom: var(--mp-sp-2); }

/* ============================================================================
   27 · PhotoGallery — фотографии карточки площадки и их просмотр
   В библиотеке компонента нет: макеты не доходили до карточки товара с
   площадки. Экран есть, и без просмотрщика он бесполезен — фотографию 100×100
   не разглядеть, а открывать каждую в новой вкладке значит потерять карточку.
   ========================================================================= */

.mp-gallery { display: flex; flex-wrap: wrap; gap: var(--mp-sp-2); }
.mp-gallery__thumb {
  display: block; width: 100px; height: 100px; flex: 0 0 auto;
  overflow: hidden; background: var(--mp-surface);
  border: var(--mp-border-width) solid var(--mp-border); border-radius: var(--mp-radius-sm);
}
.mp-gallery__thumb:hover { border-color: var(--mp-border-control); }
.mp-gallery__thumb img { display: block; width: 100%; height: 100%; object-fit: contain; }

.mp-lightbox {
  position: fixed; inset: 0; z-index: 100;
  display: flex; align-items: center; justify-content: center;
  padding: var(--mp-sp-7);
  background: var(--mp-overlay-strong);
}
.mp-lightbox img {
  max-width: 100%; max-height: 100%; object-fit: contain;
  background: var(--mp-surface); border-radius: var(--mp-radius-sm);
}
.mp-lightbox__close, .mp-lightbox__nav {
  position: absolute;
  border: var(--mp-border-width) solid var(--mp-border-dark);
  border-radius: var(--mp-radius-sm);
  background: var(--mp-surface-dark-raised); color: var(--mp-n-0);
  font: inherit; cursor: pointer; line-height: 1;
}
.mp-lightbox__close:hover, .mp-lightbox__nav:hover { background: var(--mp-n-700); }
.mp-lightbox__close { top: var(--mp-sp-4); right: var(--mp-sp-4); width: 48px; height: 48px; font-size: var(--mp-m-fs-4); }
.mp-lightbox__nav { top: 50%; transform: translateY(-50%); width: 48px; height: 72px; font-size: var(--mp-m-fs-5); }
.mp-lightbox__nav--prev { left: var(--mp-sp-4); }
.mp-lightbox__nav--next { right: var(--mp-sp-4); }
.mp-lightbox__counter {
  position: absolute; bottom: var(--mp-sp-4);
  padding: var(--mp-sp-1) var(--mp-sp-3); border-radius: var(--mp-radius-sm);
  background: var(--mp-surface-dark-raised); color: var(--mp-text-inverse);
  font-size: var(--mp-fs-2); font-variant-numeric: tabular-nums;
}

/* Описание товара приходит с площадки готовым HTML: ограничиваем высоту,
   иначе полотно на три экрана отодвигает всё остальное. */
.mp-desc { max-height: 420px; overflow-y: auto; font-size: var(--mp-fs-3); line-height: var(--mp-lh-3); }
.mp-desc ul, .mp-desc ol { padding-left: var(--mp-sp-4); }

/* ============================================================================
   28 · AuthBox — вход, блокировка входа, страница ошибки сервера
   Единственные экраны без AppShell: пользователь ещё не вошёл, навигации нет.
   ========================================================================= */

.mp-auth {
  display: flex; align-items: center; justify-content: center;
  min-height: 100vh; padding: var(--mp-sp-5);
  background: var(--mp-surface-app);
}
.mp-auth__box {
  display: flex; flex-direction: column; gap: var(--mp-sp-4);
  width: 380px; max-width: 100%;
  padding: var(--mp-sp-6);
  background: var(--mp-surface);
  border: var(--mp-border-width) solid var(--mp-border);
  border-radius: var(--mp-radius-md);
  box-shadow: var(--mp-shadow-card);
}
.mp-auth__brand { display: flex; align-items: center; gap: var(--mp-sp-2); }
.mp-auth__title { margin: 0; font-size: var(--mp-fs-5); line-height: var(--mp-lh-5); font-weight: var(--mp-weight-bold); }
.mp-auth__box p { margin: 0; font-size: var(--mp-fs-3); line-height: var(--mp-lh-3); }

/* ============================================================================
   29 · Thumbnail — миниатюра товара в списке
   В библиотеке компонента нет: макеты рисовали список товаров текстом.
   Заказчик попросил картинку, и она приезжает первой колонкой.

   Размер равен высоте строки (--mp-row-h) — не совпадение, а условие:
   миниатюра выше строки завела бы третью плотность таблицы, а их в системе
   ровно две. Рамка внутри (box-sizing: border-box), поэтому строка остаётся
   32px ровно.

   object-fit: contain, а не cover: площадки отдают и квадратные, и вытянутые
   кадры, и обрезка на 32px съедала бы то, ради чего в список смотрят.

   Заглушка — спокойный прямоугольник, а не битая иконка: адрес фотографии
   ведёт на чужой сервер, и «сломанная картинка» в каждой второй строке
   выглядит как сломанный экран. Текст для читалки экрана даёт разметка:
   у колонки нет заголовка, и ячейка обязана назваться сама.

   Заглушка — фон **контейнера**, а не отдельное состояние. Картинка лежит
   поверх и закрывает её собой, только если действительно загрузилась.
   Фотография площадки живёт на её сервере: карточку удалили, схему адресов
   поменяли, доступ закрыли — и вместо картинки браузер рисует пустоту, а не
   нашу заглушку, потому что элемент `img` на месте. Раньше в списке от этого
   получалась дыра.

   Отсюда же прозрачный фон у самой картинки: закрась её белым — и она
   закроет заглушку, не загрузившись. По этой же причине поля от
   `object-fit: contain` у невписавшегося кадра серые, а не белые: это фон
   контейнера, он один на все состояния.

   Одного фона контейнера мало: поверх заглушки браузер печатает свой значок
   «битая картинка» и текст `alt`. Убирает его псевдоэлемент, и это не хитрость
   ради хитрости — ровно то условие, которое нужно. Псевдоэлементы у `img`
   отрисовываются только тогда, когда картинки нет: с загруженной картинкой
   `img` — замещаемый элемент, и своего содержимого у него не бывает. То есть
   `::after` появляется в точности в тот момент, когда фотография не пришла,
   и закрывает собой и значок, и `alt`. Скрипт для этого не нужен.

   Миниатюра не интерактивна, поэтому под правило о размере цели не подпадает.
   ========================================================================= */

.mp-thumb {
  display: block; overflow: hidden;
  width: var(--mp-row-h); height: var(--mp-row-h);
  background: var(--mp-surface-sunken);
  border: var(--mp-border-width) solid var(--mp-border);
  border-radius: var(--mp-radius-sm);
}
.mp-thumb__img {
  display: block; position: relative; width: 100%; height: 100%;
  object-fit: contain;
  /* Фона нет намеренно — см. шапку компонента. */
}
.mp-thumb__img::after {
  content: ""; position: absolute; inset: 0;
  background: var(--mp-surface-sunken);
}

/* Колонка не тянется: её содержимое одного размера во всех строках. */
.mp-thumb-cell { width: var(--mp-row-h); }

/* ============================================================================
   Ключи и пароли, показанные один раз (экран выдачи учётной записи)
   ========================================================================= */

.mp-creds { display: grid; grid-template-columns: 150px 1fr; gap: var(--mp-sp-2) var(--mp-sp-3); margin: 0; }
.mp-creds dt { color: var(--mp-text-muted); font-size: var(--mp-fs-2); }
.mp-creds dd { margin: 0; }
.mp-creds code {
  display: inline-block;
  padding: var(--mp-sp-quarter) var(--mp-sp-2); border-radius: var(--mp-radius-sm);
  background: var(--mp-surface-sunken); border: var(--mp-border-width) solid var(--mp-border);
  font-family: var(--mp-font-mono); font-size: var(--mp-fs-4);
}

/* Alpine.js: x-cloak прячет разметку до инициализации, иначе на долю секунды
   виден и просмотрщик фотографий, и обе надписи кнопки копирования. */
[x-cloak] { display: none !important; }

/* ============================================================================
   Мобильный склад: то, чего нет в mobile.css
   Компоненты 18–25 покрывают сборку и приёмку. Экраны «найти товар»,
   «перенести», «списать» из тех же кирпичей, но им нужен список результатов
   и подпись шага — их в библиотеке нет.
   ========================================================================= */

/* Карточка результата: что нашли, где лежит, чей товар. */
.mp-m-card {
  display: flex; flex-direction: column; gap: var(--mp-sp-1);
  padding: var(--mp-sp-3);
  background: var(--mp-m-surface);
  border: var(--mp-border-width) solid var(--mp-border); border-radius: var(--mp-radius-sm);
  font-size: var(--mp-m-fs-2); line-height: var(--mp-m-lh-2); color: var(--mp-m-text);
}
.mp-m-card b, .mp-m-card strong { font-weight: var(--mp-weight-bold); }
.mp-m-card__meta { font-size: var(--mp-m-fs-1); line-height: var(--mp-m-lh-1); color: var(--mp-m-text-secondary); }
.mp-m-card__qty { font-size: var(--mp-m-fs-4); line-height: var(--mp-m-lh-4); font-weight: var(--mp-weight-bold); font-variant-numeric: tabular-nums; }

.mp-m-list { display: flex; flex-direction: column; gap: var(--mp-sp-2); margin: 0; padding: 0; }
.mp-m-list > li { list-style: none; }
.mp-m-list > li > a { display: block; color: var(--mp-m-text); }

/* Заголовок группы на складском экране: «Где лежит», «Уже пересчитано». */
.mp-m-label {
  margin: 0; font-size: var(--mp-m-fs-1); line-height: var(--mp-m-lh-1);
  font-weight: var(--mp-weight-bold); text-transform: uppercase; letter-spacing: .04em;
  color: var(--mp-m-text-secondary);
}
.mp-m-text { margin: 0; font-size: var(--mp-m-fs-2); line-height: var(--mp-m-lh-2); color: var(--mp-m-text); }
.mp-m-text--secondary { color: var(--mp-m-text-secondary); }

/* Поле ввода на складском экране: не меньше 16px, иначе iOS зумит страницу
   и следующий скан уходит мимо поля. */
.mp-m-input {
  width: 100%; min-height: var(--mp-touch);
  padding: var(--mp-sp-2) var(--mp-sp-3);
  border: var(--mp-border-width-focus) solid var(--mp-border-control); border-radius: var(--mp-radius-sm);
  background: var(--mp-m-surface); color: var(--mp-m-text);
  font: inherit; font-size: var(--mp-m-fs-2); line-height: var(--mp-m-lh-2);
}
.mp-m-input:focus { border-color: var(--mp-info-solid); outline: none; box-shadow: var(--mp-focus-ring); }
.mp-m-input--qty { font-size: var(--mp-m-fs-4); line-height: var(--mp-m-lh-4); font-weight: var(--mp-weight-bold); text-align: center; font-variant-numeric: tabular-nums; }
textarea.mp-m-input { min-height: 96px; resize: vertical; }

/* Шаги операции: видно, что уже отсканировано, а что осталось. */
.mp-m-steps { display: flex; flex-wrap: wrap; gap: var(--mp-sp-1); }
.mp-m-step {
  display: inline-flex; align-items: center; gap: var(--mp-sp-1);
  padding: var(--mp-sp-1) var(--mp-sp-2); border-radius: var(--mp-radius-sm);
  border: var(--mp-border-width) solid var(--mp-border);
  background: var(--mp-m-surface); color: var(--mp-m-text-secondary);
  font-size: var(--mp-m-fs-1); line-height: var(--mp-m-lh-1);
}
.mp-m-step.is-done { background: var(--mp-success-bg); border-color: var(--mp-success-border); color: var(--mp-success-text); font-weight: var(--mp-weight-medium); }
.mp-m-step.is-now { background: var(--mp-info-bg); border-color: var(--mp-info-border); color: var(--mp-info-text); font-weight: var(--mp-weight-medium); }

/* Предупреждение на складском экране: списание необратимо, ячейка не в
   продаже, штрихкод у двух товаров. Норма контраста мобильная (§5.4). */
.mp-m-warn {
  padding: var(--mp-sp-2) var(--mp-sp-3); border-radius: var(--mp-radius-sm);
  background: var(--mp-warning-bg); border: var(--mp-border-width) solid var(--mp-warning-border);
  color: var(--mp-warning-text);
  font-size: var(--mp-m-fs-1); line-height: var(--mp-m-lh-1); font-weight: var(--mp-weight-medium);
}
.mp-m-note {
  padding: var(--mp-sp-2) var(--mp-sp-3); border-radius: var(--mp-radius-sm);
  background: var(--mp-surface-sunken); border: var(--mp-border-width) solid var(--mp-border);
  color: var(--mp-m-text-secondary);
  font-size: var(--mp-m-fs-1); line-height: var(--mp-m-lh-1);
}
.mp-m-form { display: flex; flex-direction: column; gap: var(--mp-sp-3); }

/* ============================================================================
   Дополнения к AppShell (01) для реального приложения
   ========================================================================= */

/* Логотип — ссылка на сводку, а не надпись: щелчок по логотипу «домой» —
   привычка, за которую платить нечем. */
.mp-shell__logo { color: var(--mp-text-inverse); }
.mp-shell__logo:hover { color: var(--mp-n-0); }
/* Роль и продавец под именем: в системе с изоляцией данных видеть, от чьего
   имени смотришь, важнее, чем экономить строку. */
.mp-shell__role { display: block; color: var(--mp-text-inverse-muted); font-size: var(--mp-fs-1); }
/* Кнопка на тёмной шапке: обычная .mp-btn там белая и выглядит вырезом. */
.mp-btn--onhead { background: var(--mp-surface-dark-raised); border-color: var(--mp-border-dark); color: var(--mp-n-0); }
.mp-btn--onhead:hover { background: var(--mp-n-700); }
/* Двадцать три пункта навигации без группировки читаются как список,
   а не как разделы: отбивка ставится между смысловыми группами. */
.mp-nav { overflow-y: auto; }

/* Строка отключённой сущности: пользователь уволен, склад закрыт, оффер в
   архиве. Данные показываются полностью — приглушается только текст.
   Не путать с tr.is-locked дизайн-системы: там строка чужого продавца,
   и там запрещено действие, а не отключена сущность. */
.mp-table tbody tr.is-off td { color: var(--mp-text-secondary); }
.mp-table tbody tr.is-off .mp-code { color: var(--mp-text-secondary); }
/* Панель с формой в широкой раскладке: поля остаются узкими, даже когда
   рядом стоит матрица прав во всю ширину. */
.mp-panel--narrow { max-width: 560px; }
/* Проблемная строка таблицы: обязательное поле не заполнено, остаток не
   сходится с площадкой, проверка провалилась. Имя класса то же, что у
   MarkingCodePanel (.mp-codes tr.is-problem) — правило одно на проект.
   Полосы срочности (is-soon / is-overdue) для этого не берутся: они про
   срок отгрузки, и смешивать два измерения нельзя. */
.mp-table tbody tr.is-problem td { background: var(--mp-warning-surface); }
.mp-table tbody tr.is-problem:hover td { background: var(--mp-warning-bg); }
/* Кандидат склейки — кнопка отправки формы: связывание идёт POST-ом с
   csrf-токеном, а не ссылкой. Форма не должна становиться строкой списка. */
.mp-cands form { display: contents; }
/* Форма в нижней панели складского экрана: кнопка должна быть во всю ширину
   панели, а не шириной формы. */
.mp-m-foot form, .mp-m-btn-row form { display: contents; }
/* Источник изменения «API»: в библиотеке DiffRow (11) его нет — макеты
   писались до внешнего API. Источник машинный, поэтому оформляется так же,
   как синхронизация и система: приглушённо и пунктиром. */
.mp-diff__source--api {
  background: var(--mp-surface-sunken); color: var(--mp-muted-text);
  border: var(--mp-border-width) dashed var(--mp-border-strong);
}

/* Складской экран занимает высоту телефона целиком. В макетах .mp-m-shell
   лежит внутри рамки .mp-phone__screen и растягивается её флексом; на живой
   странице таким контейнером должен стать сам body, иначе под коротким
   экраном остаётся серое поле приложения. dvh — ради адресной строки
   мобильного браузера, которая съедает часть vh.

   Высота именно `height`, а не `min-height`: min-height — только пол, он не
   мешает body вырасти под длинный список. Пока он рос, `overflow:auto`
   у .mp-m-body не срабатывал ни разу (нечего было ограничивать), прокручивалась
   страница целиком, и .mp-m-foot уезжала под нижний край экрана вместе с ней.
   Кнопка основного действия должна стоять на месте, а прокручиваться — тело:
   на Urovo DT50 (≈360 CSS-px) кнопка, до которой надо домотать, равна кнопке,
   которой нет. overflow-y: hidden — страховка от прокрутки самого body: тело
   уже прокручивается само, а вторая полоса поверх первой только сбивает палец. */
.mp-m-page {
  display: flex; flex-direction: column;
  height: 100vh; height: 100dvh;
  overflow-y: hidden;
  background: var(--mp-m-surface);
}
/* Превью: просмотрщик показывается в потоке страницы, а не поверх неё — иначе
   галерея компонентов открывается сразу чёрным экраном. Библиотека поступает
   так же: в 06-modal-drawer.html печатается .mp-modal без обёртки .mp-overlay. */
.mp-lightbox--static { position: static; height: 320px; }

/* ============================================================================
   30 · NavDrawer — меню каркаса в узком режиме (РЕШ-059, п. 2)
   Точка перелома одна — 700 px, та же, на которой tokens.css выключает
   анимации. Выше неё AppShell (01) остаётся как был: меню колонкой слева.
   Ниже меню уходит за кнопку в шапке и выезжает поверх содержимого, а
   содержимое получает всю ширину: на 360 px — 312 вместо 112.

   Почему не .mp-nav--collapsed: разметки свёрнутого пункта-ссылки нет, а
   подписи у продавца начинаются одинаково — «С» трижды, «Т», «К» и «П» по
   два раза. Значками он их не различит. Поэтому пункты остаются словами.

   Поведение — static/js/nav_drawer.js: закрытие по выбору пункта, по клику
   мимо и по Esc, фокус после закрытия — на кнопку. Пока меню открыто, шапка
   и содержимое под подложкой inert: Tab ходит только по меню.
   ========================================================================= */

/* Кнопка в шапке и голова меню существуют только в узком режиме. */
.mp-navdrawer__toggle, .mp-navdrawer__head { display: none; }

@media (max-width: 700px) {
  /* Шапка: кнопка меню, логотип, чей контекст, выход — в 360 px.
     Имя человека и слово «Продавец» скрыты глазу, но не чтению с экрана:
     в узкой шапке остаётся название продавца, от чьего имени смотришь. */
  .mp-shell__header { gap: var(--mp-sp-2); padding: 0 var(--mp-sp-3) 0 var(--mp-sp-1); }
  .mp-shell__logo { flex: 0 0 auto; }
  .mp-shell__avatar { display: none; }
  .mp-shell__user { min-width: 0; }
  .mp-shell__who { min-width: 0; }
  .mp-shell__role { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--mp-fs-2); }
  .mp-shell__name, .mp-shell__role-title {
    position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
  }

  .mp-navdrawer__toggle {
    display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto;
    width: 44px; height: 44px; padding: 0;
    border: var(--mp-border-width) solid var(--mp-border-dark); border-radius: var(--mp-radius-sm);
    background: var(--mp-surface-dark-raised); color: var(--mp-n-0); cursor: pointer;
  }
  .mp-navdrawer__toggle:hover { background: var(--mp-n-700); }
  /* Три черты: одна полоса и две её тени. Без шрифтового значка — ☰ есть
     не в каждом шрифте Android. */
  .mp-navdrawer__bars {
    display: block; width: 18px; height: 2px; background: currentColor;
    box-shadow: 0 calc(-1 * var(--mp-sp-1h)) 0 currentColor, 0 var(--mp-sp-1h) 0 currentColor;
  }

  /* Меню выходит из потока и прячется, пока его не открыли. */
  .mp-nav {
    display: none; position: fixed; top: 0; bottom: 0; left: 0; z-index: 50;
    width: 300px; max-width: calc(100% - var(--mp-sp-7));
    padding: 0 var(--mp-sp-2) var(--mp-sp-3);
    background: var(--mp-surface); border-right: 0;
    box-shadow: var(--mp-shadow-overlay);
  }
  .mp-shell.is-nav-open .mp-nav { display: flex; }
  /* Меню высотой в экран, пунктов больше, чем влезает: колонка сперва
     ужимает детей и лишь потом прокручивается, и отбивка .mp-nav__sep
     (пустой элемент в 1 px) схлопывалась в ноль. Та же беда, что у
     .mp-m-body (design-system.md, правка 6). */
  .mp-nav > * { flex-shrink: 0; }
  /* Подложка: клик по ней — «клик мимо». Целью клика станет .mp-shell, и
     ни одна ссылка под подложкой нажатие не получит. */
  .mp-shell.is-nav-open::after {
    content: ""; position: fixed; inset: 0; z-index: 40;
    background: var(--mp-overlay);
  }

  .mp-navdrawer__head {
    position: sticky; top: 0; z-index: 1;
    display: flex; align-items: center; justify-content: space-between; gap: var(--mp-sp-2);
    flex: 0 0 auto; height: var(--mp-header-h);
    margin: 0 calc(-1 * var(--mp-sp-2)) var(--mp-sp-2); padding: 0 var(--mp-sp-1) 0 var(--mp-sp-4);
    border-bottom: var(--mp-border-width) solid var(--mp-border);
    background: var(--mp-surface);
  }
  .mp-navdrawer__title { font-size: var(--mp-fs-4); line-height: var(--mp-lh-4); font-weight: var(--mp-weight-bold); }
  .mp-navdrawer__close {
    display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto;
    width: 44px; height: 44px; padding: 0;
    border: var(--mp-border-width) solid var(--mp-border-strong); border-radius: var(--mp-radius-sm);
    background: var(--mp-surface); color: var(--mp-text-secondary);
    font: inherit; font-size: var(--mp-fs-4); cursor: pointer;
  }
  .mp-navdrawer__close:hover { background: var(--mp-surface-app); color: var(--mp-text); }

  /* Пункт под палец: 12 + 20 + 12 = 44 px, а не 30 под мышь. */
  .mp-nav__item { padding: var(--mp-sp-3); font-size: var(--mp-fs-4); line-height: var(--mp-lh-4); }
}

/* ============================================================================
   Поля ввода в узком режиме — 16 px (РЕШ-059, п. 7)
   iOS увеличивает страницу на фокусе любого поля мельче 16 px, и после ввода
   экран остаётся увеличенным. maximum-scale во viewport не трогаем: он
   запрещает увеличение пальцами вообще, а это хуже болезни.
   Кегль — ступень мобильного набора, там 16 px заведены ровно по этой причине.
   Только внутри AppShell: складские экраны /m/ живут на своём наборе и своих
   полях, и их размеры (у количества — 22 px) отсюда трогать нельзя.
   ========================================================================= */

@media (max-width: 700px) {
  .mp-shell input:not([type="checkbox"], [type="radio"], [type="submit"], [type="button"], [type="reset"], [type="file"], [type="image"], [type="range"], [type="color"]),
  .mp-shell select,
  .mp-shell textarea { font-size: var(--mp-m-fs-2); }
}

/* ============================================================================
   FilterBar (03) в узком режиме (РЕШ-059, п. 6–7)
   На мониторе полоса фильтров — строка с переносом: контролы своей природной
   ширины стоят в ряд, что не влезло — уходит ниже. На 312 px в ряд не встаёт
   почти ничего, и строки выходят разной ширины — у приёмок 134, 254 и 87 px.
   Список с длинным значением при этом распирал страницу вбок: «Кабинет»
   у товаров площадок — до 551 px.
   В узком режиме полоса — колонка: каждый элемент во всю ширину, контролы
   одной высоты — 44 px, цель под палец. Подпись поля встаёт над полем; у поля
   без подписи её нет и здесь — подпись не выдумывается. Плотный модификатор
   .mp-filters--dense в узком режиме не действует вовсе (components.css).
   ========================================================================= */

@media (max-width: 700px) {
  .mp-filters__row { flex-direction: column; flex-wrap: nowrap; align-items: stretch; }
  .mp-filters__more { grid-template-columns: minmax(0, 1fr); }
  /* Распорка отодвигает пресеты вправо; в колонке от неё остался бы
     только лишний зазор. */
  .mp-filters__row > .mp-shell__spacer { display: none; }

  .mp-filters label:not(.mp-checkline) { flex-direction: column; align-items: stretch; gap: var(--mp-sp-1); }

  /* Селектор поля повторяет правило виджетов Django выше: оно сильнее
     .mp-shell input из блока про 16 px, и поле поиска и даты в фильтрах
     оставались 13 px — кегль поэтому повторён здесь. */
  .mp-filters .mp-select, .mp-filters .mp-input, .mp-filters .mp-btn,
  .mp-filters select,
  .mp-filters input:not([type="checkbox"]):not([type="radio"]) {
    width: 100%; min-width: 0; height: 44px;
  }
  .mp-filters input:not([type="checkbox"]):not([type="radio"]) { font-size: var(--mp-m-fs-2); }
  .mp-filters .mp-btn { justify-content: center; }
  /* Флажок с подписью, пресет и раскрытие «Ещё фильтры» — тоже цели. */
  .mp-filters label.mp-checkline,
  .mp-filters .mp-preset,
  .mp-filters .mp-details > summary { min-height: 44px; }
  .mp-filters .mp-preset { display: flex; align-items: center; }
}
