/**
 * @file static/portal.css
 * @version 1.33.0
 * @date 2026-09-26
 * @description Компонентный слой рабочих экранов портала: раскладка, шапка, меню, таблицы,
 *              формы, кнопки, бейджи. Значения берутся только из static/brand/tokens.css —
 *              своих цветов и размеров здесь нет ни одного. Классы .btn/.input/.checkbox
 *              повторяют контракт дизайн-системы ADPULS v4 (эталон — globals.css сайта
 *              adpuls.ru v3.16.0), остальное — слой плотности, которого у витрины нет (ТЗ §15.4).
 *              Схема одна, светлая: переключателя тем нет и не будет.
 *              Раскладка считается от ширины окна, а не от монитора: пять порогов перечислены
 *              в разделе 10 tokens.css, числа здесь и там менять только парой.
 *              Раздел 12 — карточка товара: вкладки без JS, конфигуратор, подсказки.
 *              Раздел 14 — вход и приглашение: экраны без меню, выход в шапке.
 * @author oxipavel
 */

@import url("brand/tokens.css");

/* ==========================================================================
   1. ОСНОВА
   ========================================================================== */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text-body);
  font-family: var(--font-text);
  font-size: var(--ui-font-size);
  line-height: var(--ui-line-height);
  -webkit-font-smoothing: antialiased;
}

a {
  color: inherit;
  text-decoration: none;
}

h1,
h2,
h3 {
  font-family: var(--font-display);
  font-weight: 300;
  color: var(--text-heading);
  letter-spacing: var(--track-hero);
  text-transform: uppercase;
  margin: 0;
}

h1 {
  font-size: var(--ui-h1);
}

h2 {
  font-size: var(--ui-h2);
  letter-spacing: var(--track-label);
}

/* Без своего размера h3 брал браузерный (1.17em от 15px ≈ 17.5px) и выходил крупнее h2:
   раздел выпуска в справке кричал громче самого выпуска. Ступень шкалы ниже h2. */
h3 {
  font-size: var(--ui-h3);
  letter-spacing: var(--track-label);
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* Ссылка к рабочей области: первая по табу, на экране не видна, пока не получит фокус.
   Без неё путь с клавиатуры до первой ячейки счёта — шестнадцать нажатий по шапке и меню,
   и так на каждой странице. */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 40;
}

.skip-link:focus {
  left: var(--space-3);
  top: var(--space-3);
  padding: var(--space-2) var(--space-4);
  background: var(--bg-inverse);
  color: var(--text-inverse);
  font-family: var(--font-mono);
  font-size: var(--text-label);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
}

/* Рабочая область получает фокус по ссылке выше, но своей рамки не рисует: фокус здесь —
   точка входа для клавиатуры, а не элемент управления. */
.content:focus {
  outline: none;
}

/* Моно-метка: надчерк раздела, подпись поля, название колонки. */
.label {
  font-family: var(--font-mono);
  font-size: var(--text-label);
  font-weight: 500;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--text-muted);
}

/* Как менялась цена: столбик на период. Столбики, а не линия, — цена держится, пока
   период открыт, и меняется скачком в день замены; линия обещала бы плавный переход,
   которого не было (запрос владельца 18.09.2026). Рисуется разметкой: библиотеки графиков
   в портале нет и не заводится ради одного поля на карточку. */
.chart {
  display: flex;
  align-items: flex-end;
  gap: 1px;
  height: 160px;
  margin-top: var(--space-4);
  padding: var(--space-4) var(--space-4) 0;
  border: var(--border);
  background: var(--surface);
  overflow-x: auto;
}

.chart-bar {
  position: relative;
  flex: 1 0 44px;
  min-width: 44px;
  background: var(--surface-raised);
  border-top: 2px solid var(--line);
}

/* Действующая цена — единственный столбик акцентом: остальное задаёт форма, а не цвет,
   и правило «акцент меньше десятой части экрана» остаётся в силе. */
.chart-bar-now {
  border-top-color: var(--accent);
}

.chart-value,
.chart-since {
  position: absolute;
  left: 0;
  right: 0;
  text-align: center;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}

.chart-value {
  top: -18px;
  font-size: var(--text-micro);
  color: var(--text-body);
}

.chart-since {
  bottom: var(--space-1);
  font-size: var(--text-micro);
  color: var(--text-muted);
}

/* Несколько пояснений под блоком — строками, а не лентой через точку. Три определения
   в одну строку читались сплошным текстом: глаз не находил, где кончается одно
   и начинается следующее (правка по показу 18.09.2026). */
.notes {
  margin: var(--space-2) 0 0;
  padding: 0;
  list-style: none;
}

.notes li {
  font-family: var(--font-mono);
  font-size: var(--text-label);
  font-weight: 500;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  line-height: 1.7;
  color: var(--text-muted);
}

.mono {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}

/* Артикул, код товара, номер направления — техслой, всегда моноширинным. */
.code {
  font-family: var(--font-mono);
  font-size: var(--ui-code);
  letter-spacing: 0.04em;
  color: var(--text-heading);
  font-variant-numeric: tabular-nums;
}

.muted {
  color: var(--text-muted);
}

.accent {
  color: var(--accent-subtle);
}

/* Метка, требующая внимания: «цена по запросу», «закупка может измениться». Ставится рядом
   с .label и только перекрашивает её — плашкой не заливается, доля акцента на экране ≤10%. */
.accent-note {
  color: var(--accent-subtle);
}

/* Описания, подсказки и пустые состояния — не шире меры чтения (03-brand.md §2). На мониторе
   3440px абзац иначе растягивается через весь экран. Таблиц ограничение не касается. */
p {
  max-width: var(--ui-measure);
}

.empty p {
  margin-inline: auto;
}

/* ==========================================================================
   2. РАСКЛАДКА: шапка сверху, меню слева, работа справа
   ========================================================================== */

.shell {
  /* dvh, а не vh: на телефоне адресная строка Safari съедает нижнюю полосу экрана. */
  min-height: 100dvh;
  display: grid;
  /* Колонка именно minmax(0, 1fr), а не 1fr: неявная колонка грида равна максимальной
     ширине содержимого, и одна широкая таблица растягивала бы вместе с собой и шапку. */
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto 1fr;
}

.topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  height: var(--ui-topbar-h);
  padding: 0 var(--ui-pad-x);
  background: var(--bg-inverse);
  color: var(--text-inverse);
  border-bottom: 1px solid var(--line-inverse);
}

/* Полоса тянется во всю ширину, содержимое — по общей сетке портала. */
.topbar-inner {
  display: flex;
  align-items: center;
  gap: var(--space-5);
  height: 100%;
  max-width: var(--container-max);
  margin-inline: auto;
}

.topbar-logo {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex: none;
}

/* Знак, а не лок-ап: 03-brand.md §1 требует в шапке знак, а горизонтальному лок-апу канон
   задаёт минимум 200px по ширине — в полосу высотой 56px он не помещается вовсе, и стоял
   здесь вчетверо мельче минимума. Роль подписи играет .topbar-name рядом. */
.topbar-logo img {
  height: var(--mark-min-screen);
  width: auto;
  display: block;
}

.topbar-name {
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  letter-spacing: var(--track-section);
  text-transform: uppercase;
  color: var(--body-inverse);
  border-left: 1px solid var(--line-inverse);
  padding-left: var(--space-3);
}

.topbar-spacer {
  flex: 1;
}

.topbar-meta {
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--body-inverse);
}

.topbar-meta a:hover {
  color: var(--accent);
}

.layout {
  /* Меню и работа центрируются вместе, одним блоком (решение владельца 16.09.2026):
     раньше меню стояло у кромки окна, а работа — по центру своей колонки, и на
     мониторе 2560px они расходились в разные стороны. */
  max-width: var(--container-max);
  margin-inline: auto;
  width: 100%;
  display: grid;
  /* Рабочая колонка — minmax(0, 1fr), а не 1fr: сокращение 1fr означает minmax(auto, 1fr),
     то есть «не уже содержимого», и широкая таблица растягивала бы колонку, а вместе с ней
     и всю страницу. Прокрутка таблицы должна жить внутри .table-wrap, а не в окне браузера. */
  grid-template-columns: 232px minmax(0, 1fr);
  align-items: start;
  min-width: 0;
}

.sidebar {
  position: sticky;
  top: var(--ui-topbar-h);
  align-self: start;
  height: calc(100dvh - var(--ui-topbar-h));
  overflow-y: auto;
  border-right: 1px solid var(--line);
  padding: var(--space-4) 0;
  background: var(--bg);
}

/* Отбивка между группами — 24px, а не 32px: с шестой группой («Помощь») меню перестало
   помещаться в ноутбучные 900px, и последний пункт уходил за прокрутку — то есть исчезал
   с глаз ровно там, где его ищут. */
.sidebar-group + .sidebar-group {
  margin-top: var(--space-5);
}

/* Отступы меню фиксированы: --ui-pad-x после 1600px растёт до 48px, а колонка меню
   остаётся 232px — от строки оставалось 136px, и «Импорт прайсов» переносился на две
   строки. Поля страницы и поля колонки меню — разные величины. */
.sidebar-group-title {
  display: block;
  padding: 0 var(--space-5) var(--space-2);
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  letter-spacing: var(--track-section);
  text-transform: uppercase;
  color: var(--text-muted);
}

.nav-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  /* Высоту держит min-height рабочей плотности, а не вертикальный паддинг: с ним пункт
     вырастал до 38px, и шестая группа меню («Помощь») не помещалась в ноутбучные 900px.
     На тач-экране min-height поднимается до 44px правилом pointer: coarse. */
  padding: 0 var(--space-5);
  min-height: 34px;
  font-size: var(--ui-font-size);
  color: var(--text-body);
  border-left: 2px solid transparent;
  transition: background var(--transition-fast), color var(--transition-fast);
}

@media (hover: hover) and (pointer: fine) {
  .nav-item:hover {
    background: var(--surface-raised);
    color: var(--text-heading);
  }
}

.nav-item[aria-current="page"] {
  border-left-color: var(--accent);
  color: var(--text-heading);
  font-weight: 500;
  background: var(--surface-raised);
}

.nav-count {
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

.content {
  padding: var(--space-5) var(--ui-pad-x) var(--space-9);
  /* Потолок ширины — 1440px, как у витрины adpuls.ru (решение владельца 16.09.2026).
     Без него на мониторе 2560px и шире блоки расчёта расползались на четыре колонки
     по краям экрана, и глаз бежал через всю ширину от выбора к цене. Таблицы шире
     потолка не страдают: прокрутка живёт внутри .table-wrap, а не в окне браузера. */
  /* Своего потолка больше нет: ширину держит .layout, внутри которого работа и меню
     стоят одним блоком. Второй потолок сузил бы работу вдвое. */
  width: 100%;
  min-width: 0;
}

/* ==========================================================================
   3. ЗАГОЛОВОК СТРАНИЦЫ
   ========================================================================== */

.page-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-4);
  flex-wrap: wrap;
  padding-bottom: var(--space-4);
  border-bottom: var(--accent-rule);
  margin-bottom: var(--space-5);
}

.page-head-actions {
  display: flex;
  gap: var(--space-2);
  flex-wrap: wrap;
}

.eyebrow {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  letter-spacing: var(--track-section);
  text-transform: uppercase;
  color: var(--accent-subtle);
  margin-bottom: var(--space-2);
}

.crumbs {
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: var(--space-3);
}

.crumbs a:hover {
  color: var(--accent-subtle);
}

.section-title {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding-bottom: var(--space-2);
  border-bottom: 1px solid var(--line);
  margin: var(--space-6) 0 var(--space-3);
}

/* ==========================================================================
   4. КНОПКИ — контракт Button дизайн-системы, размер подогнан под строку таблицы
   ========================================================================== */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: var(--text-label);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  border: 1px solid transparent;
  border-radius: var(--radius);
  padding: 0 var(--space-4);
  min-height: var(--ui-control-height);
  cursor: pointer;
  user-select: none;
  transition: background var(--transition-fast), color var(--transition-fast),
    border-color var(--transition-fast);
}

.btn:disabled,
.btn[aria-disabled="true"] {
  cursor: not-allowed;
  opacity: 0.4;
}

.btn:active:not(:disabled) {
  transform: var(--press-shift);
}

.btn-sm {
  min-height: var(--ui-control-height-sm);
  font-size: var(--text-micro);
  padding: 0 var(--space-3);
}

.btn-primary {
  background: var(--text-heading);
  color: var(--bg);
  border-color: var(--text-heading);
}

.btn-accent {
  background: var(--accent);
  color: var(--accent-ink);
  border-color: var(--accent);
}

.btn-outline {
  background: transparent;
  color: var(--text-heading);
  border-color: var(--text-heading);
}

.btn-ghost {
  background: transparent;
  color: var(--text-muted);
  border-color: var(--line-control);
}

@media (hover: hover) and (pointer: fine) {
  .btn-primary:hover:not(:disabled) {
    background: var(--accent);
    color: var(--accent-ink);
    border-color: var(--accent);
  }

  .btn-accent:hover:not(:disabled) {
    background: var(--text-heading);
    color: var(--bg);
    border-color: var(--text-heading);
  }

  .btn-outline:hover:not(:disabled) {
    background: var(--text-heading);
    color: var(--bg);
  }

  .btn-ghost:hover:not(:disabled) {
    color: var(--text-heading);
    border-color: var(--text-heading);
  }
}

/* ==========================================================================
   5. ПОЛЯ — контракт Input дизайн-системы
   ========================================================================== */

.input {
  width: 100%;
  font-family: var(--font-text);
  font-size: var(--ui-font-size);
  color: var(--text-heading);
  background: transparent;
  /* Не хайрлайн: заливки у поля нет, и рамка — единственный признак контрола.
     --line давал 1,28:1 при норме 3:1 WCAG 1.4.11. */
  border: 1px solid var(--line-control);
  border-radius: var(--radius);
  padding: 0 var(--space-3);
  min-height: var(--ui-control-height);
  transition: border-color var(--transition-fast);
}

/* outline у поля не снимается: глобальное правило доступности в tokens.css §11 держит
   фокус видимым, а «.input { outline: none }» его перебивало — специфичность :where()
   нулевая. Человек, ведущий форму табом, терял место на экране. */

textarea.input {
  padding: var(--space-2) var(--space-3);
  min-height: 90px;
  line-height: var(--ui-line-height);
}

/* Длинный реквизит: высоту задаёт rows, а не общий минимум в 90px — адресу хватает двух
   строк, а комментарию нужен блок. Тянуть за угол незачем: строк ровно столько, сколько
   отведено, и лишняя высота съедала бы форму. */
textarea.input-wide {
  min-height: 0;
  resize: vertical;
}

.input:focus {
  border-color: var(--accent);
}

.input::placeholder {
  color: var(--text-muted);
}

/* Системная стрелка списка чужая канону: скруглённый двойной шеврон с градиентом.
   Рисуем свою — прямой тонкий угол цвета Бетона. */
select.input {
  appearance: none;
  -webkit-appearance: none;
  /* Треугольник, а не шеврон: тот же глиф «▾», что рисует каретка списка в стиле ДС
     (.ds-select-caret). На экране выбора клиента нативный список стоит рядом со
     стилизованным, и две разные стрелки читались как два разных элемента. */
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='5' viewBox='0 0 8 5'%3E%3Cpath d='M0 0h8L4 5z' fill='%238a8781'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--space-3) center;
  padding-right: calc(var(--space-3) * 2 + 10px);
}

.input[type="number"] {
  -moz-appearance: textfield;
  appearance: textfield;
}

.input[type="number"]::-webkit-outer-spin-button,
.input[type="number"]::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

.input-file {
  padding: var(--space-1) var(--space-2);
  font-family: var(--font-mono);
  font-size: var(--text-micro);
}

/* Кнопку выбора файла рисует ОС: скруглённая, серая, с системным шрифтом — на плоском
   экране она единственная выбивается из ряда. */
.input-file::file-selector-button {
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  font-weight: 500;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--text-heading);
  background: transparent;
  border: 1px solid var(--text-heading);
  border-radius: var(--radius);
  padding: 4px 10px;
  margin-right: var(--space-3);
  cursor: pointer;
  transition: background var(--transition-fast), color var(--transition-fast);
}

@media (hover: hover) and (pointer: fine) {
  .input-file::file-selector-button:hover {
    background: var(--text-heading);
    color: var(--bg);
  }
}

/* iOS Safari зумит страницу при фокусе на поле мельче 16px. Кнопка списка ДС держит
   тот же кегль, иначе поле и список в одной форме набраны по-разному. */
@media (max-width: 767px) {
  .input,
  .ds-select-button {
    font-size: 16px;
  }
}

.field {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  min-width: 0;
}

.field-help,
.field-error {
  font-size: var(--ui-help);
}

.field-help {
  color: var(--text-muted);
}

.field-error {
  color: var(--accent-subtle);
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
}

.form-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--ui-gap) var(--space-5);
  /* Три колонки — потолок. Без него на широком мониторе форма расходится в пять-шесть полей
     в строку, и глаз перестаёт держать пару «подпись — значение». */
  max-width: var(--ui-form-max);
}

/* Сетка, которая держит пустые колонки. Обычная схлопывает их (auto-fit), и единственное
   поле блока растягивалось во всю ширину формы: «Отделка в цене» выглядела полем другого
   рода, чем соседние сверху. Здесь колонки те же, а поле занимает одну из них. */
.form-grid-fill {
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
}

/* Поле, значение которого длиннее колонки, встаёт во всю ширину сетки: полное наименование
   юрлица и юридический адрес в колонке 260px показывали первую треть, и человек не видел,
   что он туда вписал и что сохранится в реквизитах счёта. */
.form-grid .field:has(> .input-wide) {
  grid-column: 1 / -1;
}

.form-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-6);
  padding-top: var(--space-4);
  border-top: 1px solid var(--line);
}

/* Отбивка с чертой отделяет кнопки от того, что стоит выше. Когда выше ничего нет —
   форма-строка в ячейке таблицы и форма первым блоком панели — черта рисуется из ниоткуда,
   а 32+16px добавляют к каждой строке таблицы почти полторы её высоты. Правило одно на
   пять мест разметки: справочник свойств, значения, операции отделки и два экрана цен. */
td .form-actions,
.panel > .form-actions:first-child {
  margin-top: 0;
  padding-top: 0;
  border-top: 0;
}

/* Отметка держится на :checked, без JavaScript. */
.check {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  cursor: pointer;
  font-size: var(--ui-font-size);
  min-height: 26px;
}

.check input {
  appearance: none;
  -webkit-appearance: none;
  flex: none;
  width: 16px;
  height: 16px;
  margin: 0;
  border: 1px solid var(--text-muted);
  background: transparent;
  cursor: pointer;
  transition: background var(--transition-fast), border-color var(--transition-fast);
}

.check input:checked {
  border-color: var(--text-heading);
  background: var(--text-heading);
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='8' viewBox='0 0 10 8'%3E%3Cpath d='M1 4l3 3 5-6' fill='none' stroke='%23f4f3f0' stroke-width='1.6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: center;
}

/* Галочки стоят в той же сетке, что и поля формы: колонки одной ширины и на своих местах.
   Перенос по ширине содержимого расставлял их всякий раз по-новому — между «Заведён в 1С»
   и «Выгружен в Битрикс24» зиял провал, а подпись под галочкой съезжала к соседке. */
.check-list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--ui-gap) var(--space-5);
  max-width: var(--ui-form-max);
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Подпись — под галочкой, а не в строку с ней: в строке она удлиняла пункт, и пункты
   разъезжались по ширине текста. */
.check-list .field-help {
  display: block;
  margin-top: var(--space-1);
}

.check-line {
  display: flex;
  align-items: center;
}

/* Состояние, которое отмечает сам портал: читается как галочка, но не нажимается. */
.check-state {
  display: inline-flex;
  align-items: center;
  min-height: 26px;
  font-size: var(--ui-font-size);
  color: var(--text-muted);
}

/* ==========================================================================
   6. ТАБЛИЦЫ — главный экран работы
   ========================================================================== */

.table-wrap {
  border: 1px solid var(--line);
  /* Прокрутка по обеим осям живёт здесь, а не в окне. Горизонтальная была и раньше; вертикаль
     добавлена ради липкой шапки колонок: пока у обёртки не было своей высоты, точкой отсчёта
     для sticky оставалась она сама, и заголовки уезжали вверх вместе с телом таблицы — то
     есть липкости не было вообще, хотя правило стояло. */
  overflow: auto;
  /* Во всю ширину рабочей области (решение владельца 17.09.2026). Прежняя ширина по
     содержимому спасала монитор 3440px, но с потолком .layout в 1440px причина ушла, а
     узкая таблица под длинной чертой заголовка выглядела недоделанной. Содержимое шире
     экрана по-прежнему прокручивается внутри рамки. */
  width: 100%;
  max-height: var(--ui-table-max-h);
}

table.grid {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--ui-font-size);
}

/* Ячейка, которую нельзя ломать переносом: стадия, разница, кнопка выбора.
   «В ПРОРАБОТКЕ» в две строки сбивает высоту всей строки таблицы. */
.nowrap {
  white-space: nowrap;
}

/* Заголовок-ссылка: сортировка по колонке. Активная помечается чертой, а не цветом —
   акцент на экране держится под 10 %. */
.th-sort {
  color: inherit;
  border-bottom: 1px solid transparent;
}

@media (hover: hover) and (pointer: fine) {
  .th-sort:hover {
    color: var(--text-heading);
    border-bottom-color: var(--line);
  }
}

.th-sort-on {
  color: var(--text-heading);
  border-bottom-color: var(--text-heading);
}

/* Строка, по которой считаем: подложка Пыль, без цветной заливки. */
table.grid tbody tr.row-chosen td {
  background: var(--surface-raised);
}

/* Помеченная на удаление строка: приглушена, но читается — её ещё можно вернуть. */
table.grid tbody tr.row-muted td {
  color: var(--text-muted);
}

/* Липнет к верху своего контейнера, а не к шапке портала: .table-wrap прокручивается
   по горизонтали и тем самым сам становится точкой отсчёта для sticky. При top: 56px
   заголовок съезжал вниз внутри рамки и накрывал первую строку таблицы. */
table.grid th {
  position: sticky;
  top: 0;
  z-index: 5;
  text-align: left;
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  font-weight: 500;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--text-muted);
  /* Пыль, как требует 03-brand.md §2: под липкой шапкой проезжают строки, и на Грунте
     заголовки колонок сливались бы с телом таблицы. */
  background: var(--surface-raised);
  padding: var(--space-2) var(--ui-row-pad-x);
  border-bottom: 1px solid var(--line);
  /* Заголовок переносится на две строки, а колонка — нет. «Сумма со скидкой, ₽» моношрифтом
     с трекингом занимает 170px, и одной этой подписи хватало, чтобы таблица счёта вылезла
     за рамку на ноутбуке 1280: действия строки уезжали за горизонтальную прокрутку.
     Две строки в шапке дешевле, чем невидимая кнопка «Удалить». Колонки, которым перенос
     противопоказан, помечаются .nowrap поштучно. */
  white-space: normal;
}

/* Заголовок числовой колонки — тоже вправо. Одиночный .num проигрывает в специфичности
   правилу table.grid th выше, поэтому подпись стояла слева, а цифры — справа. */
table.grid th.num {
  text-align: right;
}

/* То же с переносом: одиночный .nowrap проигрывал `white-space: normal` правила выше, и
   «Что не так» над пустой колонкой вставало в три строки (найдено 24.09.2026). */
table.grid th.nowrap {
  white-space: nowrap;
}

table.grid td {
  padding: var(--ui-row-pad-y) var(--ui-row-pad-x);
  border-bottom: 1px solid var(--line);
  height: var(--ui-row-height);
  vertical-align: middle;
  color: var(--text-body);
}

table.grid tbody tr:last-child td {
  border-bottom: 0;
}

@media (hover: hover) and (pointer: fine) {
  table.grid tbody tr:hover td {
    background: var(--surface-raised);
  }
}

table.grid tbody tr:hover .row-link {
  color: var(--accent-subtle);
}

.row-link {
  color: var(--text-heading);
  font-weight: 500;
}

.num {
  text-align: right;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}

/* Главные числа сводки заработка — сумма заказа, прибыль и налог к уплате. Остальные
   колонки объясняют, как они получились, и стоят обычным начертанием. */
table.grid td.key-number {
  font-weight: 500;
  color: var(--text-heading);
}

/* Пояснение под числом в колонке цифр: строкой она слипалась с самим числом —
   «−166 382,00прибыли −136 378,67» (найдено 18.09.2026). */
.num .label {
  display: block;
}

.thumb {
  width: var(--ui-thumb-w);
  height: var(--ui-thumb-h);
  object-fit: cover;
  display: block;
  border: 1px solid var(--line);
  background: var(--surface-raised);
}

.thumb-empty {
  /* span без display размеры игнорирует: заглушка рисовалась вертикальной чертой в пиксель
     шириной, и строка без картинки выглядела как строка с дефектом. */
  display: block;
  width: var(--ui-thumb-w);
  height: var(--ui-thumb-h);
  border: 1px solid var(--line);
  background: repeating-linear-gradient(
    45deg,
    var(--surface-raised),
    var(--surface-raised) 4px,
    var(--bg) 4px,
    var(--bg) 8px
  );
}

/* ==========================================================================
   7. БЕЙДЖИ СТАТУСОВ
   ========================================================================== */

.badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  font-weight: 500;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  padding: 3px var(--space-2);
  border: 1px solid var(--line);
  color: var(--text-muted);
  white-space: nowrap;
}

.badge-active {
  border-color: var(--text-heading);
  color: var(--text-heading);
}

.badge-draft {
  border-color: var(--line);
  color: var(--text-muted);
}

.badge-quarantine {
  border-color: var(--accent);
  color: var(--accent-subtle);
}

.badge-archived {
  border-color: var(--line);
  color: var(--text-muted);
  text-decoration: line-through;
}

/* Класс собирается из статуса товара (templates/catalog/_status.html), а статусов пять.
   Без своего правила демо-товар выглядел ровно как черновик — и выдуманные данные
   нельзя было отличить от недоделанных. Штриховка отделяет его от рабочих статусов. */
.badge-demo {
  border-style: dashed;
  border-color: var(--text-muted);
  color: var(--text-muted);
}

.badge-on {
  background: var(--text-heading);
  border-color: var(--text-heading);
  color: var(--bg);
}

/* ==========================================================================
   8. ПАНЕЛИ, КАРТОЧКИ, ПОКАЗАТЕЛИ
   ========================================================================== */

.panel {
  border: 1px solid var(--line);
  padding: var(--space-4);
  background: var(--surface);
}

.panel + .panel {
  margin-top: var(--space-4);
}

/* Панель ввода закупки, полученной запросом: тот же .panel, но с акцентной кромкой — это
   работа, которую ждут, а не справка. */
.request-row {
  border-left: var(--accent-rule);
  background: var(--surface-raised);
}

.stat-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 1px;
  /* Фоном хайрлайн не красим: auto-fit схлопывает пустые дорожки только в единственном
     ряду, и хвост второго ряда (семь плашек на экране расчёта) выходил сплошной серой
     полосой. Хайрлайн даёт тень на самой плашке — она не занимает места, тени соседей
     ложатся в общий просвет 1px, а outline остаётся свободным под кольцо фокуса. */
  background: transparent;
  border: 1px solid var(--line);
  margin-bottom: var(--space-6);
  /* Потолок ширины держит .layout (1440px, решение владельца 16.09.2026): четыре плашки
     выходят по ~290px. Прежний потолок в 1080px остался от времён, когда рабочая область
     была без ограничения, и обрывал ряд плашек раньше таблиц под ним — края не сходились. */
}

.stat {
  background: var(--bg);
  box-shadow: 0 0 0 1px var(--line);
  padding: var(--space-4);
  display: block;
  transition: background var(--transition-fast);
}

@media (hover: hover) and (pointer: fine) {
  a.stat:hover {
    background: var(--surface-raised);
  }
}

/* Подпись и число — каждое своей строкой. Это span'ы: без display: block подпись вставала
   в строку перед числом, и сумма на экране расчёта рвалась на две строки. */
.stat-value,
.stat-label {
  display: block;
}

.stat-value {
  font-family: var(--font-display);
  font-weight: 200;
  font-size: var(--ui-stat);
  line-height: 1.1;
  color: var(--text-heading);
  font-variant-numeric: tabular-nums;
}

/* Сумма в плашке — неразрывная строка: разряды в русской локали разделены неразрывным
   пробелом, переносить её нечем. На телефоне «18 890,00 ₽» при кегле 34px выходила за
   плашку и наезжала на соседнюю, поэтому там цифра мельче. */
@media (max-width: 600px) {
  .stat-value {
    font-size: var(--text-h3);
  }
}

.stat-label {
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--text-muted);
  margin-top: var(--space-2);
}

.cols {
  display: grid;
  /* Потолок колонки держит .layout (1440px): рабочая область — около 1160px, и две колонки
     выходят по 570px, что уже меньше прежнего жёсткого потолка 720px. С ним же две колонки
     переставали помещаться рядом, и сводка вставала в одну колонку с пустым полем справа. */
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 340px), 1fr));
  gap: var(--space-5);
}

/* Пара «подпись — значение»: карточка товара, сводка направления. */
.pairs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--space-3) var(--space-5);
  margin: 0;
  /* Пара «подпись — значение» читается как текст, а не как таблица: тот же потолок. */
  max-width: var(--ui-form-max);
}

.pairs div {
  min-width: 0;
  border-top: 1px solid var(--line);
  padding-top: var(--space-2);
}

.pairs dt {
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--text-muted);
}

.pairs dd {
  margin: 4px 0 0;
  color: var(--text-heading);
}

.photo {
  width: 100%;
  max-width: 380px;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border: 1px solid var(--line);
  background: var(--surface-raised);
  display: block;
}

.chip {
  display: inline-block;
  font-size: var(--ui-chip);
  padding: 2px var(--space-2);
  border: 1px solid var(--line);
  background: var(--bg);
  margin: 0 4px 4px 0;
}

/* ==========================================================================
   9. ФИЛЬТРЫ, СООБЩЕНИЯ, ПУСТО, СТРАНИЦЫ
   ========================================================================== */

/* Единый блок отбора портала (решение владельца 19.09.2026). Один вид на всех экранах со
   списком — тот, что был на «Счетах»: отдельный блок НАД таблицей, без рамки и подложки,
   отбитый от неё отступом. К таблице он не прицепляется: отбор — это не её шапка.

   Сетка из трёх колонок (решение владельца 19.09.2026). Ширину задаёт не длина значения, а
   род поля: **выбор** (список) занимает колонку — три списка в ряд; **ввод** (строка поиска,
   сумма, дата, тираж) идёт во всю ширину блока, потому что в него пишут и видеть надо всё
   написанное. Колонок ровно три и на любом окне: сетка из «сколько влезет» меняла раскладку
   вместе с шириной, и одно и то же поле оказывалось то узким, то широким. */
.filters {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-2) var(--space-3);
  /* Подписи бывают в две строки, поля от этого не должны разъезжаться по высоте. */
  align-items: end;
  margin-bottom: var(--space-4);
  /* Тот же потолок, что у формы: строка отбора шириной в монитор выглядит как ошибка. */
  max-width: var(--ui-form-max);
}

/* Список распирает колонку длинным `option`, если минимум не снят. */
.filters .field {
  min-width: 0;
}

/* Поле ввода — во всю ширину; пара «с — по» — тоже, двумя половинами внутри. Прямые потомки:
   поле внутри пары живёт по её сетке, а не по сетке блока.

   Дата и число из этого правила исключены: у них длина значения известна заранее, и поле в
   1080px под тираж «1000» — это §2.1, числовое поле шириной по самому длинному значению. */
.filters > .field:has(> input.input:not([type="date"]):not([type="number"])),
.filters > .field-search,
.filters > .field-pair {
  grid-column: 1 / -1;
}

/* Пара «с — по»: две половины одной строки. Переносом не разрывается — начало периода на
   одном ряду, конец на другом читается как два разных отбора. */
.filters .field-pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-3);
  align-items: end;
}

/* «Показать» стоит на одном месте на любом экране — своей строкой под полями, у левого края
   блока (запрос владельца 19.09.2026). Место не зависит ни от числа полей, ни от ширины окна:
   поля кончились — под ними кнопка. В углу сетки она висела оторванной от полей, а число
   колонок меняется от ширины окна, и вместе с ним менялось её место. */
.filters .filter-actions {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-1);
}

/* Планшет: три списка в ряд сжимаются до неразличимых, остаётся два. */
@media (max-width: 900px) {
  .filters {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* Телефон и узкое окно: поля по одному в ряд. */
@media (max-width: 600px) {
  .filters {
    grid-template-columns: 1fr;
  }
}

.messages {
  list-style: none;
  margin: 0 0 var(--space-4);
  padding: 0;
}

.messages li {
  border: 1px solid var(--line);
  border-left: 3px solid var(--text-heading);
  padding: var(--space-2) var(--space-3);
  margin-bottom: var(--space-2);
  background: var(--surface-raised);
  font-size: var(--ui-font-size);
}

.messages li.error {
  border-left-color: var(--accent);
  color: var(--accent-subtle);
}

/* Предупреждение — не ошибка: расчёт прошёл, но требует внимания (объём вне подтверждённой
   сетки, часть закупки получена запросом). Зелёного и жёлтого в палитре нет, поэтому отличие
   от обычного сообщения — акцентная кромка, а не заливка плашки. */
.messages li.warning {
  border-left-color: var(--accent-subtle);
}

/* Кромка в 3px — слишком тихое отличие: на экране плашка всегда одна, и сравнить не с чем.
   Слово читается сразу, а доля акцента на экране не растёт. */
.messages li.warning::before {
  content: "Внимание";
  display: block;
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--accent-subtle);
}

.empty {
  border: 1px solid var(--line);
  padding: var(--space-7) var(--space-5);
  text-align: center;
  color: var(--text-muted);
}

.empty-title {
  font-family: var(--font-mono);
  font-size: var(--text-label);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--text-heading);
  margin-bottom: var(--space-2);
}

.pager {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-top: var(--space-4);
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--text-muted);
}

/* ==========================================================================
   10. ПРИОРИТЕТ КОЛОНОК И ТАЧ
   Номенклатура — десять колонок. На мониторе видны все; дальше колонки снимаются по
   значимости, а не прячутся за горизонтальной прокруткой, в которой на телефоне ищут вслепую.
   На телефоне остаются код, наименование и состояние — то, по чему строку узнают.
   Пометка ставится и на th, и на td одной колонки: иначе строка разъезжается.
   ========================================================================== */

@media (max-width: 1200px) {
  .col-wide {
    display: none;
  }

  /* Ячейки поджимаются раньше, чем таблица уходит в горизонтальную прокрутку: строка
     счёта с полями скидки и срока не влезала в ноутбук 1280 даже без колонки цены
     (18.09.2026). Прокрутка внутри рамки остаётся запасным вариантом, а не нормой. */
  table.grid th,
  table.grid td {
    padding-left: var(--space-2);
    padding-right: var(--space-2);
  }
}

@media (max-width: 600px) {
  .col-mid {
    display: none;
  }
}

/* Палец против мыши. 34px — удобная цель курсора и промах пальцем; поднимаем высоту только
   там, где указатель грубый, чтобы не разрыхлять таблицу на рабочем месте с мышью. */
@media (pointer: coarse) {
  .nav-item,
  .btn,
  .input,
  .check {
    min-height: 44px;
  }

  .btn-sm {
    min-height: 40px;
  }

  /* Знак вопроса у поля: 16px — промах пальцем. 24px — минимум WCAG 2.5.8, и он
     влезает в строку подписи, не растягивая её. Полные 44px потребовали бы
     отрицательных полей, а они уже однажды отдали нажатие соседнему label. */
  .hint {
    width: 24px;
    height: 24px;
    font-size: 12px;
  }
}

/* ==========================================================================
   11. УЗКИЙ ЭКРАН
   Меню уезжает наверх горизонтальной полосой: на телефоне колонка в 232px
   съедает половину ширины, а работать всё равно приходится в таблице.
   ========================================================================== */

@media (max-width: 900px) {
  .layout {
    grid-template-columns: minmax(0, 1fr);
  }

  .sidebar {
    position: static;
    height: auto;
    /* Без min-width: 0 полоса меню не ужимается — элемент грида по умолчанию шире
       колонки, и вслед за ним горизонтально разъезжается вся страница. */
    min-width: 0;
    border-right: 0;
    border-bottom: 1px solid var(--line);
    padding: var(--space-2) 0;
    display: flex;
    /* Перенос, а не горизонтальная прокрутка: прокручиваемая полоса прячет пункты за краем,
       и активный раздел можно не найти, не зная, что полосу надо тянуть. Семь пунктов
       укладываются в две строки и видны целиком. */
    flex-wrap: wrap;
    gap: var(--space-2);
  }

  .sidebar-group + .sidebar-group {
    margin-top: 0;
  }

  .sidebar-group {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-1);
  }

  .sidebar-group-title {
    display: none;
  }

  .nav-item {
    border-left: 0;
    border-bottom: 2px solid transparent;
    padding: var(--space-2) var(--space-3);
    white-space: nowrap;
  }

  .nav-item[aria-current="page"] {
    border-left: 0;
    border-bottom-color: var(--accent);
  }

  .content {
    padding-top: var(--space-4);
  }
}

/* Кнопка «Меню ▾» — только на телефоне и только со скриптом: без скрипта меню открыто
   всегда, как на планшете. */
.nav-toggle {
  display: none;
}

@media (max-width: 600px) {
  /* Меню на телефоне сворачивается в одну строку (решение владельца 22.09.2026): полоса
     из шести групп занимала полэкрана, и работа начиналась под сгибом. Раскрытое меню —
     та же полоса, текущий раздел в ней отмечен. */
  .js .nav-toggle {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-basis: 100%;
    /* Телефон — палец, а не курсор: цель 44px, как у пунктов меню при pointer: coarse. */
    min-height: 44px;
    padding: 0 var(--space-3);
    font-family: var(--font-mono);
    font-size: var(--text-label);
    font-weight: 500;
    letter-spacing: var(--track-label);
    text-transform: uppercase;
    color: var(--text-heading);
    background: transparent;
    border: 0;
    cursor: pointer;
  }

  .js .nav-toggle[aria-expanded="false"] ~ .sidebar-group {
    display: none;
  }

  .nav-toggle[aria-expanded="true"] .nav-toggle-caret {
    transform: rotate(180deg);
  }

  .topbar {
    padding: 0 var(--space-4);
  }

  .topbar-name {
    display: none;
  }

  .content {
    padding-left: var(--space-4);
    padding-right: var(--space-4);
  }
}

/* ==========================================================================
   12. КАРТОЧКА ТОВАРА: ИТОГ, ВКЛАДКИ, РАСЧЁТ, СПИСКИ В СТИЛЕ ДС
   Всё управление товаром живёт в одном окне. Порядок сверху вниз: цена клиенту,
   затем вкладки — справка о товаре, расчёт, поставщики, цены. Ничего свёрнутого:
   портал внутренний, прятать от своих нечего, а раскрывашка — лишний щелчок
   на каждой из сорока карточек в день.
   ========================================================================== */

/* 11.1. Ритм разделов. До этого заголовки блоков стояли классом без стилей, и
   разделы слипались в сплошную простыню — отсюда «скомкано и нет отступов». */
.block-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-4);
  padding-bottom: var(--space-2);
  border-bottom: 1px solid var(--line);
  margin: var(--space-7) 0 var(--space-4);
}

/* Первый блок панели не отбивается сверху: вкладка уже дала отступ. Селектор смотрит
   только на прямых детей: иначе под правило попадал «Откуда цена» — первый заголовок
   внутри своей колонки, — и в один столбец он слипался с отделкой. */
.tab-panel > .block-head:first-child,
.tab-panel > *:first-child > .block-head:first-child,
.tab-panel > .calc-cols > .calc-col:first-child > form > .block-head:first-child,
.card-block .block-head {
  margin-top: 0;
}

.table-wrap + .block-head,
.pairs + .block-head {
  margin-top: var(--space-7);
}

/* Наименование позиции: товар строкой, параметры под ним через «·». Строка для 1С с «|»
   в узкой колонке ломалась на десять строк. Параметры — тем же цветом текста, мельче:
   Бетоном наименование не набирается (03-brand.md §1). */
.col-name {
  min-width: 160px;
  /* Остаток ширины таблицы уходит наименованию: служебные колонки шире своего содержимого
     не нужны, а сжатое наименование рвётся на десять строк и растит всю строку. */
  width: 100%;
}

.item-title {
  display: block;
  color: var(--text-heading);
}

.item-article {
  display: block;
  margin-top: 2px;
  font-size: var(--text-micro);
}

.item-params {
  display: block;
  font-size: var(--ui-chip);
  line-height: 1.45;
  color: var(--text-body);
}

/* Кнопки строки стоят в ряд, пока колонке хватает ширины, и переносятся, когда тесно.
   Прежняя редакция ставила их друг под друга всегда — строка таблицы вырастала вдвое
   на экране, где места хватало на обе. */
/* Колонка действий липнет к правому краю: таблица счёта на планшете шире рамки, и кнопки
   «Изменить» и «Удалить» уезжали за горизонтальную прокрутку — человек видел строку, но не
   мог её тронуть. Фон непрозрачный, иначе под кнопками просвечивают колонки, которые
   проезжают под ними; слева хайрлайн, чтобы граница читалась при прокрутке. */
th:last-child:empty,
td.row-actions {
  position: sticky;
  right: 0;
  z-index: 4;
  background: var(--bg);
  /* Просвет слева: под липкой колонкой проезжает соседняя, и сумма подходила к кнопке
     вплотную — «18 890,00УДАЛИТЬ» одной строкой. */
  padding-left: var(--space-3);
  /* Колонка — по ширине кнопок, а не доля свободного места. «1%» при table-layout: auto
     сжимает колонку до содержимого: иначе таблица отдавала ей часть ширины, и слева от
     кнопок оставалось пустое поле (найдено 19.09.2026). Остаток забирают колонки данных. */
  width: 1%;
  white-space: nowrap;
}

/* Хайрлайн рисуется псевдоэлементом, а не border: у таблицы border-collapse, и граница
   липкой ячейки схлопывается с соседней — на экране её просто нет. Тень здесь запрещена
   канону (03-brand.md §3), поэтому линия своя. */
th:last-child:empty::before,
td.row-actions::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 1px;
  background: var(--line);
}

th:last-child:empty {
  background: var(--surface-raised);
}

table.grid tbody tr:hover td.row-actions,
table.grid tbody tr.row-chosen td.row-actions {
  background: var(--surface-raised);
}

/* Телефон: колонка действий не липнет (решение владельца 22.09.2026). На узком экране
   прилипшие кнопки занимали полширины рамки и закрывали соседнюю колонку — «100 шт.»
   читалось как «10». Таблица прокручивается целиком, кнопки стоят в конце строки. Шапка
   колонки остаётся липкой сверху, как соседние; хайрлайн слева нужен только липкой. */
@media (max-width: 600px) {
  th:last-child:empty,
  td.row-actions {
    right: auto;
  }

  th:last-child:empty::before,
  td.row-actions::before {
    content: none;
  }
}

/* Кнопки строки живут в одном контейнере `.actions` внутри ячейки. Раскладку держит он,
   а не сама ячейка: ячейка-флексбокс выпадает из выравнивания строки таблицы (18.09.2026).
   Отступы задаёт `gap` контейнера, а не поля у кнопок: «Изменить» ссылкой и «Удалить» кнопкой
   внутри формы получали разные поля и вставали ступенькой (найдено 19.09.2026). */
.row-actions {
  text-align: right;
  white-space: nowrap;
  line-height: 1;
}

/* Кнопки идут друг над другом, а не рядом (запрос владельца 19.09.2026): столбик у правого
   края читается сверху вниз и не растит ширину колонки на узкой таблице счёта. */
.row-actions .actions {
  display: inline-flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--space-1);
  width: max-content;
  min-width: 0;
}

/* Форма нужна только ради POST и не должна влиять на раскладку: её кнопка встаёт в общий
   столбик сама. */
.row-actions .actions .inline-form {
  display: contents;
}

/* Кнопки столбика одной ширины — по «Изменить» (запрос владельца 19.09.2026). Ширину задаёт
   контейнер по содержимому, а кнопки тянутся на неё: жёсткие 11ch делали «Удалить» вдвое шире
   надписи. Надпись по словам не переносится. */
.row-actions .btn {
  margin: 0;
  width: 100%;
  white-space: nowrap;
}

/* Правка строки на месте (контакт в карточке клиента, решение владельца 22.09.2026):
   «Изменить» — summary у <details> в колонке действий, а форма стоит строкой ниже во всю
   ширину таблицы. В ячейке действий форма не помещается, поэтому строку формы прячет CSS,
   пока <details> закрыт; скрипт не нужен. Браузер без :has() правило отбросит и покажет
   формы открытыми — работать можно, просто теснее. */
.row-edit > summary {
  list-style: none;
}

.row-edit > summary::-webkit-details-marker {
  display: none;
}

table.grid tbody tr:has(.row-edit:not([open])) + tr.row-edit-form {
  display: none;
}

/* Числовое поле: цифры моноширинные и прижаты вправо, как в колонке сумм, — иначе
   «250 000,99» и «25 000,99» в соседних строках глазом не сравнить. Боковой отступ
   меньше обычного: в коротком поле 12px с каждой стороны съедали треть ширины. */
.input-short,
.input-money {
  text-align: right;
  font-variant-numeric: tabular-nums;
  padding: 0 var(--space-2);
}

/* Срок в днях — три знака. */
.input-short {
  width: 4em;
}

/* Текстовое поле в строке справочника: наименование свойства, значения, операции. Ширина
   стояла в разметке — style="max-width:360px", и «Фальцовка с биговкой — Дважды пополам»
   обрезалось на середине. Поле занимает свободную ширину строки и не уже 240px. */
.input-name {
  flex: 1 1 240px;
  min-width: 240px;
  max-width: 520px;
}

/* Отбивка блока, который идёт следом за формой или таблицей. Стояла в разметке девятью
   style="margin-top:12px" — правило живёт в стилях, как и все прочие размеры. */
.gap-top {
  margin-top: var(--space-3);
}

/* Колонка с картинкой товара и колонка кода: ширина по содержимому, а не по остатку. */
.col-thumb {
  width: 52px;
}

.col-code {
  width: 120px;
}

/* Скидка суммой доходит до суммы позиции: «250 000,99» — девять знаков, и поле в 4.5em
   показывало от них хвост «00,99». Ширина берётся по самому длинному значению, а не по
   ожидаемому: невидимое число человек не проверит. */
/* Поле суммы: «250 000,99» помещается целиком. Шире не делаем — в строке счёта рядом
   стоят выбор «% или ₽», сумма и срок, и лишние полсантиметра уводили таблицу
   за горизонтальную прокрутку (18.09.2026). */
.input-money {
  width: 7.2em;
}

/* Скидка: значение и выбор «% или ₽» стоят в одну строку. Порознь они не влезали в колонку,
   переносились на вторую строку и растили высоту таблицы — а у неё своя прокрутка, и строка
   с открытым списком уезжала под неё. Список знака короткий: в нём один символ. */
.discount-cell {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

/* В таблице пара «число + знак» прижимается вправо, под заголовок числовой колонки:
   слева от него стояли суммы, и разнобой читался как сбой выравнивания. В форме
   наоборот — поле стоит под своей подписью, у левого края. */
td .discount-cell {
  justify-content: flex-end;
}

.discount-cell select.input {
  width: 3.8em;
  padding-right: calc(var(--space-2) + 12px);
  background-position: right var(--space-2) center;
}

/* Итог под таблицей — справа, как в счёте 1С: без НДС, НДС, с НДС. */
.totals {
  display: grid;
  grid-template-columns: auto auto;
  gap: var(--space-2) 0;
  width: fit-content;
  margin: var(--space-4) 0 0 auto;
  font-variant-numeric: tabular-nums;
}

.totals dt {
  padding-right: var(--space-6);
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--text-muted);
  text-align: right;
  align-self: center;
}

.totals dd {
  margin: 0;
  text-align: right;
  font-family: var(--font-money);
  color: var(--text-heading);
}

.totals .totals-main {
  align-self: stretch;
  padding-top: var(--space-2);
  border-top: 1px solid var(--text-heading);
  font-weight: 500;
  color: var(--text-heading);
}

/* Окно выбора (клиент, ответственный). Скрипт подгружает в <dialog> ту же страницу, что
   открывается без него; закрывается Esc (03-brand.md §2, четвёртый случай скрипта). */
dialog.picker {
  width: min(960px, calc(100vw - 32px));
  max-height: calc(100dvh - 64px);
  padding: 0;
  border: var(--border);
  background: var(--bg);
  color: var(--text-body);
}

dialog.picker::backdrop {
  background: color-mix(in srgb, var(--bg-inverse) 45%, transparent);
}

.picker-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-5);
  border-bottom: var(--border);
}

.picker-body {
  padding: var(--space-4) var(--space-5) var(--space-5);
  overflow: auto;
}

/* Ошибка синхронизации с 1С. Со скриптом — модальное окно узкое, по центру; без скрипта
   тот же <dialog open> стоит плашкой вверху экрана и ничего не перекрывает. */
dialog.onec-alert {
  width: min(560px, calc(100vw - 32px));
  border-left: 3px solid var(--accent);
}

dialog.onec-alert:not(:modal) {
  position: static;
  width: auto;
  margin: 0 0 var(--space-4);
}

/* Образец факсимиле в настройках организации. */
.facsimile-preview {
  height: 80px;
  margin-right: var(--space-5);
  border: var(--border);
  background: var(--surface-raised);
}

/* Кнопка-форма в строке таблицы стоит рядом со ссылкой, а не под ней. */
.inline-form {
  display: inline;
}

/* Итог под таблицей: та же сетка колонок, отбит чертой. */
table.grid tfoot td {
  border-top: 1px solid var(--text-heading);
  font-weight: 500;
  color: var(--text-heading);
}

/* Плашка под парами «подпись — значение» не прилипает к последней паре. */
.pairs + .messages {
  margin-top: var(--space-5);
}

/* Форма под таблицей (добавить контакт, строку) не прилипает к её рамке. */
.table-wrap + .filters,
.table-wrap + form {
  margin-top: var(--space-4);
}

.tab-panel .table-wrap,
.tab-panel .pairs,
.tab-panel .question-list {
  margin-bottom: var(--space-5);
}

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

   Липнет к шапке портала: вкладка «Цены» на три сотни строк уводит итог за верхний край,
   и человек прокручивает обратно только чтобы посмотреть цену. Фон обязателен — под
   полосой проезжает содержимое. На узком экране липкости нет: там она съела бы пятую
   часть экрана. */
.result-strip {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--space-4) var(--space-7);
  padding: var(--space-4) 0 var(--space-5);
  border-bottom: var(--accent-rule);
  margin-bottom: var(--space-5);
}

@media (min-width: 901px) {
  .result-strip {
    position: sticky;
    top: var(--ui-topbar-h);
    z-index: 15;
    background: var(--bg);
  }
}

/* Группа полосы итога: подпись сверху, числа в строку. Вторая группа отделена
   вертикальной чертой — это «нам», и смешивать её с «клиенту» нельзя даже взглядом. */
.result-group {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  min-width: 0;
}

.result-group-title {
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  letter-spacing: var(--track-section);
  text-transform: uppercase;
  color: var(--text-muted);
}

.result-row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--space-4) var(--space-6);
}

@media (min-width: 901px) {
  .result-group-inner {
    border-left: 1px solid var(--line);
    padding-left: var(--space-6);
  }
}

/* Без цены — та же высота, что с ценой: меняется только черта снизу. */
.result-strip-empty {
  border-bottom: 1px solid var(--line);
}

.result {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  min-width: 0;
}

.result-value {
  font-family: var(--font-money);
  font-variant-numeric: tabular-nums;
  font-size: var(--text-lead);
  color: var(--text-heading);
  white-space: nowrap;
}

/* Цена клиенту — единственное число карточки, набранное крупно. */
.result-main .result-value {
  font-size: var(--ui-stat);
  line-height: 1.1;
}

.result-loss {
  color: var(--accent-subtle);
}

/* 11.3. Вкладки. Radio + ~ вместо скрипта: переключение не перезагружает страницу
   и работает при отказавшем соединении. Какая вкладка открыта после расчёта,
   решает сервер — форма расчёта возвращает человека на свою вкладку. */
.tab-radio {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.tab-bar {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
  border-bottom: 1px solid var(--line);
  margin-bottom: var(--space-5);
}

.tab {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  /* 44px — минимальная цель для пальца; на мыши лишняя высота не мешает. */
  min-height: 44px;
  padding: 0 var(--space-4);
  font-family: var(--font-mono);
  font-size: var(--text-label);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--text-muted);
  border-bottom: 2px solid transparent;
  cursor: pointer;
  transition: color var(--transition-fast), border-color var(--transition-fast);
}

@media (hover: hover) and (pointer: fine) {
  .tab:hover {
    color: var(--text-heading);
    background: var(--surface-raised);
  }
}

.tab-count {
  font-size: var(--text-micro);
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

.tab-panel {
  display: none;
}

/* Вкладка-ссылка: в справке переключение идёт адресом, а не radio — страницу присылают
   ссылкой на нужную вкладку. Вид у активной тот же. */
.tab.is-active {
  color: var(--text-heading);
  border-bottom-color: var(--accent);
}

/* Вкладки всех карточек по стандарту 03-brand.md §2: товар — товар, расчёт, тарифы,
   поставщики; поставщик — поставщик, что закрывает; направление — свойства, отделка,
   товары. Растущие списки — окнами, не вкладками. Новое имя вкладки дописывается во все
   три списка ниже. */
#tab-tovar:checked ~ .tab-bar .tab[for="tab-tovar"],
#tab-raschet:checked ~ .tab-bar .tab[for="tab-raschet"],
#tab-tarify:checked ~ .tab-bar .tab[for="tab-tarify"],
#tab-postavshchiki:checked ~ .tab-bar .tab[for="tab-postavshchiki"],
#tab-postavshchik:checked ~ .tab-bar .tab[for="tab-postavshchik"],
#tab-zakryvaet:checked ~ .tab-bar .tab[for="tab-zakryvaet"],
#tab-svoystva:checked ~ .tab-bar .tab[for="tab-svoystva"],
#tab-otdelka:checked ~ .tab-bar .tab[for="tab-otdelka"],
#tab-tovary:checked ~ .tab-bar .tab[for="tab-tovary"] {
  color: var(--text-heading);
  border-bottom-color: var(--accent);
}

#tab-tovar:checked ~ #panel-tovar,
#tab-raschet:checked ~ #panel-raschet,
#tab-tarify:checked ~ #panel-tarify,
#tab-postavshchiki:checked ~ #panel-postavshchiki,
#tab-postavshchik:checked ~ #panel-postavshchik,
#tab-zakryvaet:checked ~ #panel-zakryvaet,
#tab-svoystva:checked ~ #panel-svoystva,
#tab-otdelka:checked ~ #panel-otdelka,
#tab-tovary:checked ~ #panel-tovary {
  display: block;
}

/* Привязка к for обязательна: комбинатор ~ от любого сфокусированного radio достаёт
   все подписи полосы, и кольцо фокуса рисовалось сразу на всей полосе. */
#tab-tovar:focus-visible ~ .tab-bar .tab[for="tab-tovar"],
#tab-raschet:focus-visible ~ .tab-bar .tab[for="tab-raschet"],
#tab-tarify:focus-visible ~ .tab-bar .tab[for="tab-tarify"],
#tab-postavshchiki:focus-visible ~ .tab-bar .tab[for="tab-postavshchiki"],
#tab-postavshchik:focus-visible ~ .tab-bar .tab[for="tab-postavshchik"],
#tab-zakryvaet:focus-visible ~ .tab-bar .tab[for="tab-zakryvaet"],
#tab-svoystva:focus-visible ~ .tab-bar .tab[for="tab-svoystva"],
#tab-otdelka:focus-visible ~ .tab-bar .tab[for="tab-otdelka"],
#tab-tovary:focus-visible ~ .tab-bar .tab[for="tab-tovary"] {
  outline: var(--focus-ring);
  outline-offset: 2px;
}

/* 11.4. Справка о товаре: две колонки — данные и изображение. */
.card-cols {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
  gap: var(--space-7);
  align-items: start;
  margin-bottom: var(--space-5);
}

@media (max-width: 900px) {
  .card-cols {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-5);
  }
}

.card-block {
  min-width: 0;
}

/* Пары в две колонки шире, чем в форме: значения справки длиннее подписи. */
.pairs-wide {
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr));
  max-width: none;
}

.photo-empty {
  width: 100%;
  max-width: 380px;
  height: 180px;
}

.spec-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--space-5);
  margin-bottom: var(--space-5);
}

.spec {
  min-width: 0;
  border-top: 1px solid var(--line);
  padding-top: var(--space-2);
}

.chip-list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
  list-style: none;
  margin: var(--space-2) 0 0;
  padding: 0;
}

.question-list {
  margin: 0;
  padding-left: var(--space-5);
  max-width: var(--ui-measure);
}

.question-list li {
  padding: var(--space-1) 0;
}

/* 11.5. Расчёт: четыре смысловых блока вместо ряда цифр — что закупаем,
   во что это встаёт, что платит клиент, что остаётся нам. */
/* 11.6. Расчёт в две колонки: слева что печатаем, справа во что это встаёт.
   На мониторе выбор и цена видны одновременно — правка свойства сразу отзывается
   числом справа, и ради цены больше не прокручивают. Порог 1200px: ниже две колонки
   по 360px и 480px не помещаются, и раскладка возвращается к обычной, сверху вниз. */
@media (min-width: 1200px) {
  .calc-cols {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(360px, 0.8fr);
    gap: var(--space-6);
    align-items: start;
  }

  /* В узкой колонке четыре денежных блока встают столбиком, а не сеткой по 320px. */
  .calc-col-price .money-cols {
    grid-template-columns: minmax(0, 1fr);
  }

  /* Заголовки соседних колонок — на одной высоте. Только здесь: в один столбец
     «Откуда цена» идёт следом за отделкой, и отступ между ними нужен. */
  .calc-col > *:first-child .block-head:first-child,
  .calc-col > .block-head:first-child {
    margin-top: 0;
  }

}

.money-cols {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 320px), 1fr));
  gap: 1px;
  /* Тот же случай, что у .stat-row: фон-хайрлайн показывался в незанятых дорожках
     второго ряда сплошной серой полосой — четыре блока на три колонки. */
  background: transparent;
  border: 1px solid var(--line);
  margin-bottom: var(--space-5);
}

.money-block {
  background: var(--bg);
  box-shadow: 0 0 0 1px var(--line);
  padding: var(--space-4);
  min-width: 0;
}

/* Блок цены клиенту выделяется плоскостью, а не цветом: акцент держится под 10 %. */
.money-block-main {
  background: var(--surface-raised);
}

.money-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  flex-wrap: wrap;
  padding-bottom: var(--space-2);
  border-bottom: 1px solid var(--line);
  margin-bottom: var(--space-2);
}

.money-supplier {
  font-size: var(--ui-code);
  color: var(--text-heading);
}

@media (hover: hover) and (pointer: fine) {
  .money-supplier:hover {
    color: var(--accent-subtle);
  }
}

.money-rows {
  margin: 0;
}

.money-rows > div {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--line);
}

.money-rows > div:last-child {
  border-bottom: 0;
}

.money-rows dt {
  min-width: 0;
  color: var(--text-body);
}

.money-rows dd {
  margin: 0;
  font-family: var(--font-money);
  font-variant-numeric: tabular-nums;
  color: var(--text-heading);
  white-space: nowrap;
}

/* Итоговая строка блока: жирнее и на полтона темнее, без заливки акцентом. */
.money-sum dt,
.money-sum dd {
  font-weight: 700;
  color: var(--text-heading);
}

.money-note {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--text-muted);
  margin-top: 2px;
}

/* Конфигуратор: по списку на параметр плюс тираж. Колонки шириной 220px —
   формат «90×55 мм (евро)» помещается целиком, не переносясь. */
.config-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--space-4);
  margin-bottom: var(--space-5);
  max-width: var(--ui-form-max);
}

/* Наименование и кнопка копирования. Кнопка стоит отдельной строкой под текстом и
   прижата влево: втиснутая в конец строки, она разрывала наименование по словам и
   прыгала по высоте вместе с переносами. */
.copy-row {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-2);
}

.copy-text {
  display: block;
}

.copy-actions {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  flex: none;
}

/* Копирует только скрипт: без него кнопка видна и ничего не делает. Скрипт прячет её
   и сам, атрибутом hidden, если буфер обмена недоступен, — .btn перебивал бы его. */
.copy-actions [data-copy] {
  display: none;
}

.js .copy-actions [data-copy]:not([hidden]) {
  display: inline-flex;
}

/* Главное поле набора — тираж. Стоит первым и набрано крупнее: от него зависят обе
   ступени сразу, и меняют его чаще всего остального вместе взятого. */
.field-primary .input {
  font-size: var(--text-lead);
  height: auto;
  padding-block: var(--space-2);
}

.field-primary .label {
  color: var(--text-heading);
}

/* Переключатель изготовителя внутри денежного блока: подпись и список в строку,
   потому что вокруг — таблица чисел, а не форма. */
.field-inline {
  gap: var(--space-2);
  margin-bottom: var(--space-3);
}

.field-inline .input {
  max-width: 100%;
}

/* Галочка работы без вариантов стоит в той же сетке, что и списки. Высота равна
   высоте поля: иначе пункт «Скругление углов» съезжает вверх относительно соседей. */
.check-field {
  display: flex;
  align-items: center;
  min-height: var(--ui-control-height);
}

.calc-actions {
  align-items: center;
  gap: var(--space-4);
}

/* Кнопка нужна там, где скрипт не выполнился. Есть скрипт — считается само. */
.js .calc-actions .btn {
  display: none;
}

.calc-auto-note {
  display: none;
}

.js .calc-auto-note {
  display: inline;
}

/* Пока сервер считает новый набор, прежние числа бледнеют: видно, что на экране ещё
   старая цена. Задержка — чтобы быстрый ответ не мигал; возврат без задержки. */
[data-fragment] {
  transition: opacity var(--transition-fast);
}

.is-busy [data-fragment] {
  opacity: 0.45;
  transition-delay: 150ms;
}

/* Блок подмены без своей коробки: содержимое встаёт в ряд фильтра наравне с полями,
   а пустой блок не добавляет ряду лишний промежуток. */
.fragment-flow {
  display: contents;
}

.field-note {
  display: block;
  margin-top: var(--space-1);
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

.link {
  color: var(--text-heading);
  border-bottom: 1px solid var(--line);
}

/* Знак вопроса у поля: что это, какие значения принимает и — у чисел счёта — по какой
   формуле получено. Подсказка раскрывается по нажатию, а не по наведению: наведения нет
   на телефоне, с которого работает выездной менеджер (решение владельца 18.09.2026).
   Текст продублирован в title — в браузере без popover это единственный способ его прочесть. */
.hint {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  padding: 0;
  margin-left: var(--space-1);
  border: 1px solid var(--line);
  background: transparent;
  font-family: var(--font-mono);
  font-size: 10px;
  line-height: 1;
  color: var(--text-muted);
  cursor: help;
  vertical-align: middle;
}

@media (hover: hover) and (pointer: fine) {
  .hint:hover {
    color: var(--accent-subtle);
    border-color: var(--accent-subtle);
  }
}

/* Окошко подсказки. Рисуется поверх страницы силами браузера, поэтому не обрезается
   прокруткой таблицы — внутри .table-wrap обычный absolute обрезало бы по рамке.
   display задан обоими правилами намеренно: браузер без popover отбросит второе,
   подсказка останется закрытой и не будет висеть посреди экрана. */
/* Шрифт задан целиком: подсказка стоит внутри подписи поля и внутри шапки таблицы, а те
   набраны моноширинным в разрядку прописными. Унаследовав это, текст подсказки читался
   как заголовок и занимал пол-экрана (найдено 18.09.2026). */
.hint-body {
  display: none;
  max-width: min(420px, calc(100vw - 2 * var(--space-5)));
  padding: var(--space-4);
  border: var(--border);
  background: var(--surface-raised);
  color: var(--text-body);
  font-family: var(--font-text);
  font-size: var(--text-small);
  font-weight: 400;
  letter-spacing: normal;
  text-transform: none;
  text-align: left;
  line-height: 1.5;
}

.hint-body:popover-open {
  display: block;
}

.hint-body::backdrop {
  background: rgb(11 11 11 / 12%);
}

/* Чего никто из подрядчиков не делает — видно, но выбрать нельзя. Гасим приглушённым
   цветом и зачёркиванием: цвета для статусов в палитре нет, а причина пишется словом
   рядом. Прозрачность здесь не годится — 0.45 роняли контраст текста до 2,38:1. */
.chip-off {
  color: var(--text-muted);
  text-decoration: line-through;
}

/* Строка подрядчика, который это сочетание не делает. Остаётся видимой: исчезнув,
   он оставил бы вопрос «его сравнили или забыли». Приглушается, но не зачёркивается —
   зачёркнутые числа читаются хуже прочерка. */
table.grid tbody tr.row-off td {
  color: var(--text-muted);
}

.input option:disabled {
  color: var(--text-muted);
}

/* Формат в натуральных пропорциях: длинная сторона 120px, короткая — по отношению.
   Бумагу выбирают глазами, а «А6» держал в руках не каждый. */
/* Формат рисуется в натуральных пропорциях: «А6» держал в руках не каждый. Миниатюра
   живёт внутри своего поля, в строку с размерами, и ячейку сетки не занимает. */
.size-figure {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-2);
}

.size-box {
  display: block;
  border: 1px solid var(--text-heading);
  background: var(--surface-raised);
}

/* ==========================================================================
   13. СПИСОК В СТИЛЕ ДС
   Раскрытый список нативного select рисует операционная система: скруглённый,
   с подсветкой системным синим и системным шрифтом — то есть мимо канона целиком.
   Стилизовать его нельзя ничем, поэтому список строится разметкой, а нативный
   select остаётся в форме и продолжает отправлять значение. Скрипта нет —
   работает нативный, и карточка остаётся рабочей.
   ========================================================================== */

.ds-select {
  position: relative;
  min-width: 0;
}

.ds-select-button {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  width: 100%;
  min-height: var(--ui-control-height);
  padding: 0 var(--space-3);
  font-family: var(--font-text);
  font-size: var(--ui-font-size);
  text-align: left;
  color: var(--text-heading);
  background: transparent;
  border: 1px solid var(--line-control);
  border-radius: var(--radius);
  cursor: pointer;
  transition: border-color var(--transition-fast);
}

.ds-select-button:focus-visible {
  outline: var(--focus-ring);
  outline-offset: 2px;
}

.ds-select-button[aria-expanded="true"] {
  border-color: var(--accent);
}

/* Обязательный список без выбора при отправке: рамка акцентом, под полем — сообщение
   (portal.js). Без этого отправка отменялась молча (найдено 21.09.2026). */
.ds-select-button[aria-invalid="true"] {
  border-color: var(--accent);
}

/* Закрытое поле (направление товара с исполнениями) не раскрывается — и видно почему. */
.ds-select-button:disabled {
  color: var(--text-muted);
  cursor: not-allowed;
}

.ds-select-value {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.ds-select-caret {
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  color: var(--text-muted);
}

.ds-select-list {
  position: absolute;
  z-index: 20;
  top: calc(100% - 1px);
  left: 0;
  right: 0;
  max-height: 280px;
  overflow-y: auto;
  margin: 0;
  padding: 0;
  list-style: none;
  background: var(--bg);
  border: 1px solid var(--text-heading);
}

.ds-select-option {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-height: var(--ui-control-height);
  padding: var(--space-1) var(--space-3);
  cursor: pointer;
  border-bottom: 1px solid var(--line);
}

.ds-select-option:last-child {
  border-bottom: 0;
}

/* Наведение и выбор — Пылью, как строка таблицы: системного синего в палитре нет. */
.ds-select-option[data-active="true"] {
  background: var(--surface-raised);
}

.ds-select-option[aria-selected="true"] {
  color: var(--text-heading);
  font-weight: 500;
}

.ds-select-option[aria-selected="true"]::before {
  content: "✓";
  /* Галочка — рисунок: выбор диктору называет aria-selected, а без пустой замены он
     читал «галочка, — любое —» (найдено 21.09.2026). Браузер без этой записи берёт
     строку выше. */
  content: "✓" / "";
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  color: var(--accent-subtle);
}

.ds-select-option[aria-selected="false"]::before {
  content: "";
  width: 7px;
}

.ds-select-option[aria-disabled="true"] {
  color: var(--text-muted);
  cursor: not-allowed;
  text-decoration: line-through;
}

/* Заголовок группы (<optgroup>) — моно-меткой, как подпись колонки: он делит список,
   а не выбирается (найдено 21.09.2026). */
.ds-select-group > ul {
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Последний вариант группы — последний в своём ul и черты снизу не имеет. */
.ds-select-group + .ds-select-group {
  border-top: 1px solid var(--line);
}

.ds-select-group-label {
  display: block;
  padding: var(--space-2) var(--space-3) var(--space-1);
  font-family: var(--font-mono);
  font-size: var(--text-label);
  font-weight: 500;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--text-muted);
  border-bottom: 1px solid var(--line);
}

/* Блок обязан стоять здесь, а не в разделе 10: .ds-select-button объявлена выше по файлу,
   специфичность у селекторов равная, и правило из раннего блока было бы инертным. */
@media (pointer: coarse) {
  .ds-select-button,
  .ds-select-option {
    min-height: 44px;
  }
}

/* ==========================================================================
   14. ВХОД И ПРИГЛАШЕНИЕ — экраны без меню и выход в шапке (ТЗ §15.1)
   ========================================================================== */

.topbar-meta {
  display: flex;
  align-items: center;
  gap: var(--space-4);
}

/* Форма выхода не ломает строку шапки: кнопка стоит в ряд с именем. */
.topbar-meta form {
  display: contents;
}

/* Кнопка выхода выглядит ссылкой шапки — тот же моно-капс, та же реакция на наведение. */
.topbar-meta button {
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  color: inherit;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
}

.topbar-meta button:hover {
  color: var(--accent);
}

/* Колонка входа: одна форма посередине, ширина — под почту и QR-код. */
.auth {
  width: 100%;
  max-width: 440px;
  margin: var(--space-7) auto;
  padding: 0 var(--space-4);
}

.auth .panel {
  display: grid;
  gap: var(--space-4);
}

.auth .panel .form-actions {
  margin-top: 0;
}

.auth-qr svg {
  display: block;
  width: 200px;
  height: auto;
}

/* Ключ для ручного ввода — моно: в пропорциональном шрифте не отличить «I» от «1». */
.auth-key {
  font-family: var(--font-mono);
  word-break: break-all;
  color: var(--text-heading);
}
