/* ============================================================================
   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; }

/* Колонки: два-три блока в ряд, на узком экране — друг под другом. */
.mp-cols {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(380px, 1fr));
  gap: var(--mp-sp-4); align-items: start;
}
.mp-cols--wide { grid-template-columns: repeat(auto-fit, minmax(520px, 1fr)); }
.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(280px, 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); }

/* Форма-колонка: поля друг под другом. Ширина ограничена не ради красоты —
   строка ввода длиной в экран читается хуже, чем поле в 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-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; }

/* Раскрывающийся блок: изменения в журнале, подробности строки. */
.mp-details > summary {
  display: inline-flex; align-items: center; gap: var(--mp-sp-1);
  min-height: var(--mp-row-h); cursor: pointer;
  font-size: var(--mp-fs-2); color: var(--mp-text-link);
}
.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); }

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

.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. */
.mp-m-page {
  display: flex; flex-direction: column;
  min-height: 100vh; min-height: 100dvh;
  background: var(--mp-m-surface);
}
/* Превью: просмотрщик показывается в потоке страницы, а не поверх неё — иначе
   галерея компонентов открывается сразу чёрным экраном. Библиотека поступает
   так же: в 06-modal-drawer.html печатается .mp-modal без обёртки .mp-overlay. */
.mp-lightbox--static { position: static; height: 320px; }
