/*
 * Система оформления «Сталь» — одна на три кабинета: витрину с кабинетом покупателя, кабинет продавца и
 * кабинет менеджера (change 0064, platform/cabinet-look, требование «Оформление кабинетов задаётся одной
 * системой в общей библиотеке»). Здесь переменные, шрифты, правило каждого голого тега, который встречается
 * в разметке кабинетов, пять видов кнопок, поля, таблицы, общие составные части и шапка.
 *
 * Своя система, а не готовый набор с правками поверх (design.md → D1): геометрия «Стали» — высоты
 * 54/44/40/36 px, а набор выводил высоту кнопки и поля из отступов и корневого кегля, и 130 строк «слоя
 * плотности» из change 0047 воевали с ним. Словарь разметки, к которому шаблоны привыкли, остался — ссылка-
 * кнопка через role="button", классы secondary и outline, — но смысл ему даёт этот файл (D4).
 *
 * Кабинеты подключают этот файл первым и дописывают в своём только свои составные части — мега-меню,
 * карточку позиции, фасеты, очередь решений — на переменных системы: голые теги, числа цвета и свои высоты
 * в стилях кабинета запрещены (сценарий «Кабинет не повторяет систему»).
 *
 * Отдаётся файл с того же хоста, что и страница, вместе с папкой ../fonts/: внешних адресов у платформы нет —
 * стенд и прод работают в закрытых сетях (ADR-0011, сценарий «Оформление приходит своей статикой»).
 */

/*
 * ── переменные системы «Сталь» ──────────────────────────────────────────────────────────────────────
 *
 * Палитра, метки, кегли, высоты, скругления и поля страницы — здесь, одним блоком (change 0064, design.md →
 * D3; требование «Размеры, палитра и шрифт заданы переменными»). Шестнадцатеричное число цвета в системе
 * встречается только в этом блоке: в правилах ниже и в стилях кабинетов — только var(--…), поэтому сменить
 * цвет или высоту — это одна правка здесь, а не обход файлов.
 *
 * Размеры — в пикселях, а не в rem: кегль страницы не зависит от корневого кегля браузера, и одна страница
 * на 1280 и на 1920 набрана одним кеглем (сценарий «Кегль не зависит от ширины окна»).
 *
 * Контраст надписи к её фону посчитан по WCAG 2.1 и записан в design.md → D3. Из него два правила:
 * --c-text-3 и --c-link прямо на полотно не ставятся (там 4,1 и 4,3:1 — ниже AA) — на полотне вторичный текст
 * берёт --c-text-2, а ссылка — --c-link-hover.
 */
:root {
  /* полотно, блоки, линии */
  --c-canvas: #E8EBED;          /* полотно страницы */
  --c-surface: #FFFFFF;         /* блок, карточка, шапка, выпадающие панели */
  --c-border: #D2D7DB;          /* рамка блока, кнопки с рамкой и поля */
  --c-line: #E4E8EB;            /* внутренние линии блока, строки таблицы */
  --c-subtle: #F3F5F6;          /* фон поля, шапка таблицы, пояснение, текущий раздел */
  --c-neutral: #E4E8EB;         /* кнопка «Найти»: значение то же, что у --c-line, смысл другой */
  --c-zebra: #F6F7F8;           /* полосы через одну в характеристиках */
  --c-media: #E1E5E8;           /* подложка фотографии */
  --c-media-ink: #7D868E;       /* контур рисунка на подложке — не текст */

  /* текст */
  --c-text: #15181B;            /* текст и графитовые кнопки, 17,8:1 на белом */
  --c-text-2: #596068;          /* вторичный: крошки, подписи колонок, пояснения; 5,3:1 на полотне */
  --c-text-3: #6A727A;          /* третичный: подписи фактов, счётчики; на полотно не ставится */
  --c-text-soft: #3E454C;       /* текст пояснения на --c-subtle */
  --c-placeholder: #7A828A;     /* заполнитель поля */
  --c-mark-off: #9AA2A9;        /* рамка невыбранного квадрата фасета, число пустой очереди — не текст */
  --c-seller-dot: #B8BFC5;      /* отметка продавца не площадки */
  --c-on-dark: #FFFFFF;         /* надпись на графите */

  /* акцент — отметка по закрытому перечню (D5), надпись на нём графитовая: белый дал бы 3,05:1 */
  --c-accent: #F26B1D;
  --c-on-accent: #15181B;

  /* ссылки и значки */
  --c-link: #C2410C;            /* ссылка, метка «PDF»; 5,2:1 на белом */
  --c-link-hover: #9A3412;      /* ссылка при наведении и ссылка на полотне */
  --c-ok: #1F8A4C;              /* галочка «юрлицо привязано» — значок */

  /* метки состояний: фон и надпись, контраст пары не ниже 4,5:1 (требование «Состояния и ошибки…») */
  --c-ok-bg: #E3F1E8;     --c-ok-ink: #1F6B3F;       /* готово */
  --c-info-bg: #E6EEF7;   --c-info-ink: #1F4E80;     /* на проверке */
  --c-draft-bg: #EEF0F2;  --c-draft-ink: #3E454C;    /* черновик, нейтральное */
  --c-danger-bg: #FBE6E3; --c-danger-ink: #A1281C;   /* отказ */
  --c-warn-bg: #FDF0DC;   --c-warn-ink: #8A4B00;     /* внимание */

  /* ошибки форм */
  --c-error: #A1281C;           /* подпись и сообщение поля с ошибкой */
  --c-error-line: #C0392B;      /* рамка поля с ошибкой */
  --c-alert-bg: #FBE6E3;        /* сводка отказа над формой (role="alert") */
  --c-alert-line: #E8B4AE;
  --c-alert-ink: #7F1D14;

  /* шрифт: Plex своей статикой (D6), за ним — системный, которым текст показан, пока Plex не загрузился */
  --font-sans: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;

  /*
    Кегли. Шкала заголовков — решение владельца 2026-09-27 по снимкам стенда (задача 1.12): 40 px макетов на
    рабочих экранах велики — длинное название позиции вставало двумя строками по 40, название фасета в
    узкой колонке — тремя по 20.
  */
  --fs-body: 15px;
  --lh-body: 1.45;
  --fs-h1: 28px;                /* заголовок страницы */
  --fs-h2: 20px;                /* заголовок раздела страницы */
  --fs-h2-block: 18px;          /* заголовок внутри блока: карточка, боковая колонка */
  --fs-h3: 16px;                /* подзаголовок внутри блока */
  --fs-h4: 15px;
  --fs-h-narrow: 14px;          /* заголовок секции узкой колонки — фасеты */
  --fs-sm: 14px;                /* крошки, подписи полей, пояснения, фасеты, сегменты */
  --fs-xs: 13px;                /* шапка таблицы, метки, условия под ценой, счётчики */
  --fs-2xs: 12px;               /* подписи фактов, код на подложке, строка компании в шапке */
  --fs-unit: 11px;              /* метка формата документа («PDF») */
  --fs-brand: 19px;             /* название в шапке */
  --fs-price: 22px;             /* цена в карточке сетки */
  --fs-price-buy: 42px;         /* цена в блоке покупки */
  --fs-fact: 17px;              /* значение факта в блоке покупки, сумма строки корзины */
  --fs-total: 32px;             /* итог корзины (D9) */
  --fs-count: 56px;             /* число очереди менеджера */

  /*
    Высоты элементов управления — ступенями, и задаёт их место, а не класс: поле и кнопка одной строки
    одной высоты — высоты поля (сценарий «Поле и кнопка одной строки»). Поле ниже кнопки формы и с меньшим
    отступом текста — решение владельца 2026-09-27 (задача 2.20): в поле 44 px с отступом 12 вокруг числа
    «3» стояло больше воздуха, чем текста.
  */
  --h-xl: 54px;                 /* покупка: «Оформить заявку» в корзине */
  --h-l: 44px;                  /* шапка, кнопки форм и действия страницы, строка выбора — умолчание кнопки */
  --h-field: 40px;              /* поле ввода: текст, число, поиск, телефон, дата, выбор из списка */
  --h-m: 40px;                  /* сетка позиций, фасеты, страницы перечня */
  --h-s: 36px;                  /* действие и поле в строке таблицы */
  --h-seg: 34px;                /* сегмент переключателя: рамка 1 + отступ 3 → коробка 42 */
  --field-pad: 10px;            /* отступ текста поля от рамки по горизонтали */

  /*
    Вертикальный ритм голой разметки: отступ под блоком потока, над заголовком после содержимого, и предел
    ширины поля одиночной формы (задача 1.12). Pico давал ритм абзацам, спискам, таблицам, формам и полям
    сам; без него непеределанные экраны слипались.
  */
  --flow: 16px;                 /* под абзацем, перечнем, таблицей, формой, блоком, рядом действий */
  --flow-section: 24px;         /* над заголовком, который идёт после содержимого */
  --field-max: 560px;           /* поле одиночной формы — вход, привязка юрлица — не шире этого */

  --radius: 4px;                /* кнопки, поля, блоки */
  --radius-mark: 3px;           /* метки состояний, сегменты, квадрат фасета */
  --radius-tag: 2px;            /* код модели на подложке, «1 из 5» */

  /* поле страницы и шапка (D7) */
  --stripe: 4px;                /* полоса акцента над шапкой */
  --header-h: 72px;
  --page-max: 1680px;           /* контейнер страницы, как в change 0045 */
  --gutter: 40px;               /* поле у края: 40 от 1440, ниже — точками перелома после блока */

  /* раскладки кабинетов */
  --gap-grid: 16px;             /* промежуток сетки позиций */
  --facets-w: 272px;
  --facets-gap: 28px;
  --aside-buy: 440px;           /* блок цены на карточке позиции и модели */
  --aside-cart: 420px;          /* итог корзины */

  /* единственная тень системы — у выпадающих панелей; блоки держит рамка */
  --shadow-panel: 0 8px 24px rgba(21, 24, 27, .12);
}
@media (max-width: 1439px) { :root { --gutter: 24px; } }
@media (max-width: 720px) { :root { --gutter: 16px; } }

/*
 * Шрифты IBM Plex (D6): файлы лежат рядом, в ../fonts/ (пакеты, версии и суммы — fonts/README.md). Путь
 * относительный — от /css/base.css он разрешается одинаково в трёх кабинетах. Каждое подмножество объявлено
 * своим unicode-range, дословно из css пакета: браузер качает файл, только если такой знак есть на странице.
 * font-display: swap — пока Plex едет, текст показан запасным шрифтом из --font-sans, а не пропадает.
 */
@font-face { font-family: "IBM Plex Sans"; font-style: normal; font-weight: 400; font-display: swap;
  src: url("../fonts/plex-sans/IBMPlexSans-Regular-Latin1.woff2") format("woff2");
  unicode-range: U+0000, U+000D, U+0020-007E, U+00A0-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2013-2014, U+2018-201A, U+201C-201E, U+2020-2022, U+2026, U+2030, U+2039-203A, U+2044, U+20AC, U+2122, U+2212, U+FB01-FB02; }
@font-face { font-family: "IBM Plex Sans"; font-style: normal; font-weight: 400; font-display: swap;
  src: url("../fonts/plex-sans/IBMPlexSans-Regular-Latin2.woff2") format("woff2");
  unicode-range: U+0100-0101, U+0104-0130, U+0132-0151, U+0154-017F, U+018F, U+0192, U+01A0-01A1, U+01AF-01B0, U+01FA-01FF, U+0218-021B, U+0237, U+0259, U+1E80-1E85, U+1E9E, U+20A1, U+20A4, U+20A6, U+20A8-20AA, U+20AD-20AE, U+20B1-20B2, U+20B4-20B5, U+20B8-20BA, U+20BD, U+20BF; }
@font-face { font-family: "IBM Plex Sans"; font-style: normal; font-weight: 400; font-display: swap;
  src: url("../fonts/plex-sans/IBMPlexSans-Regular-Cyrillic.woff2") format("woff2");
  unicode-range: U+0400-045F, U+0462-0463, U+046A-046B, U+0472-0475, U+0490-04C2, U+04CF-04D9, U+04DC-04E9, U+04EE-04F9, U+0524-0525; }
@font-face { font-family: "IBM Plex Sans"; font-style: normal; font-weight: 400; font-display: swap;
  src: url("../fonts/plex-sans/IBMPlexSans-Regular-Pi.woff2") format("woff2");
  unicode-range: U+0E3F, U+2000-200D, U+2015, U+2028-2029, U+202F, U+2032-2033, U+2070, U+2074-2079, U+2080-2089, U+2113, U+2116, U+2126, U+212E, U+2150-2151, U+2153-215E, U+2190-2199, U+21A9-21AA, U+21B0-21B3, U+21B6-21B7, U+21BA-21BB, U+21C4, U+21C6, U+2202, U+2206, U+220F, U+2211, U+2215, U+221A, U+221E, U+222B, U+2248, U+2260, U+2264-2265, U+25CA, U+2713, U+274C, U+2B0E-2B11, U+ECE0, U+EFCC, U+FEFF, U+FFFD; }
@font-face { font-family: "IBM Plex Sans"; font-style: normal; font-weight: 500; font-display: swap;
  src: url("../fonts/plex-sans/IBMPlexSans-Medium-Latin1.woff2") format("woff2");
  unicode-range: U+0000, U+000D, U+0020-007E, U+00A0-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2013-2014, U+2018-201A, U+201C-201E, U+2020-2022, U+2026, U+2030, U+2039-203A, U+2044, U+20AC, U+2122, U+2212, U+FB01-FB02; }
@font-face { font-family: "IBM Plex Sans"; font-style: normal; font-weight: 500; font-display: swap;
  src: url("../fonts/plex-sans/IBMPlexSans-Medium-Latin2.woff2") format("woff2");
  unicode-range: U+0100-0101, U+0104-0130, U+0132-0151, U+0154-017F, U+018F, U+0192, U+01A0-01A1, U+01AF-01B0, U+01FA-01FF, U+0218-021B, U+0237, U+0259, U+1E80-1E85, U+1E9E, U+20A1, U+20A4, U+20A6, U+20A8-20AA, U+20AD-20AE, U+20B1-20B2, U+20B4-20B5, U+20B8-20BA, U+20BD, U+20BF; }
@font-face { font-family: "IBM Plex Sans"; font-style: normal; font-weight: 500; font-display: swap;
  src: url("../fonts/plex-sans/IBMPlexSans-Medium-Cyrillic.woff2") format("woff2");
  unicode-range: U+0400-045F, U+0462-0463, U+046A-046B, U+0472-0475, U+0490-04C2, U+04CF-04D9, U+04DC-04E9, U+04EE-04F9, U+0524-0525; }
@font-face { font-family: "IBM Plex Sans"; font-style: normal; font-weight: 500; font-display: swap;
  src: url("../fonts/plex-sans/IBMPlexSans-Medium-Pi.woff2") format("woff2");
  unicode-range: U+0E3F, U+2000-200D, U+2015, U+2028-2029, U+202F, U+2032-2033, U+2070, U+2074-2079, U+2080-2089, U+2113, U+2116, U+2126, U+212E, U+2150-2151, U+2153-215E, U+2190-2199, U+21A9-21AA, U+21B0-21B3, U+21B6-21B7, U+21BA-21BB, U+21C4, U+21C6, U+2202, U+2206, U+220F, U+2211, U+2215, U+221A, U+221E, U+222B, U+2248, U+2260, U+2264-2265, U+25CA, U+2713, U+274C, U+2B0E-2B11, U+ECE0, U+EFCC, U+FEFF, U+FFFD; }
@font-face { font-family: "IBM Plex Sans"; font-style: normal; font-weight: 600; font-display: swap;
  src: url("../fonts/plex-sans/IBMPlexSans-SemiBold-Latin1.woff2") format("woff2");
  unicode-range: U+0000, U+000D, U+0020-007E, U+00A0-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2013-2014, U+2018-201A, U+201C-201E, U+2020-2022, U+2026, U+2030, U+2039-203A, U+2044, U+20AC, U+2122, U+2212, U+FB01-FB02; }
@font-face { font-family: "IBM Plex Sans"; font-style: normal; font-weight: 600; font-display: swap;
  src: url("../fonts/plex-sans/IBMPlexSans-SemiBold-Latin2.woff2") format("woff2");
  unicode-range: U+0100-0101, U+0104-0130, U+0132-0151, U+0154-017F, U+018F, U+0192, U+01A0-01A1, U+01AF-01B0, U+01FA-01FF, U+0218-021B, U+0237, U+0259, U+1E80-1E85, U+1E9E, U+20A1, U+20A4, U+20A6, U+20A8-20AA, U+20AD-20AE, U+20B1-20B2, U+20B4-20B5, U+20B8-20BA, U+20BD, U+20BF; }
@font-face { font-family: "IBM Plex Sans"; font-style: normal; font-weight: 600; font-display: swap;
  src: url("../fonts/plex-sans/IBMPlexSans-SemiBold-Cyrillic.woff2") format("woff2");
  unicode-range: U+0400-045F, U+0462-0463, U+046A-046B, U+0472-0475, U+0490-04C2, U+04CF-04D9, U+04DC-04E9, U+04EE-04F9, U+0524-0525; }
@font-face { font-family: "IBM Plex Sans"; font-style: normal; font-weight: 600; font-display: swap;
  src: url("../fonts/plex-sans/IBMPlexSans-SemiBold-Pi.woff2") format("woff2");
  unicode-range: U+0E3F, U+2000-200D, U+2015, U+2028-2029, U+202F, U+2032-2033, U+2070, U+2074-2079, U+2080-2089, U+2113, U+2116, U+2126, U+212E, U+2150-2151, U+2153-215E, U+2190-2199, U+21A9-21AA, U+21B0-21B3, U+21B6-21B7, U+21BA-21BB, U+21C4, U+21C6, U+2202, U+2206, U+220F, U+2211, U+2215, U+221A, U+221E, U+222B, U+2248, U+2260, U+2264-2265, U+25CA, U+2713, U+274C, U+2B0E-2B11, U+ECE0, U+EFCC, U+FEFF, U+FFFD; }
@font-face { font-family: "IBM Plex Sans"; font-style: normal; font-weight: 700; font-display: swap;
  src: url("../fonts/plex-sans/IBMPlexSans-Bold-Latin1.woff2") format("woff2");
  unicode-range: U+0000, U+000D, U+0020-007E, U+00A0-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2013-2014, U+2018-201A, U+201C-201E, U+2020-2022, U+2026, U+2030, U+2039-203A, U+2044, U+20AC, U+2122, U+2212, U+FB01-FB02; }
@font-face { font-family: "IBM Plex Sans"; font-style: normal; font-weight: 700; font-display: swap;
  src: url("../fonts/plex-sans/IBMPlexSans-Bold-Latin2.woff2") format("woff2");
  unicode-range: U+0100-0101, U+0104-0130, U+0132-0151, U+0154-017F, U+018F, U+0192, U+01A0-01A1, U+01AF-01B0, U+01FA-01FF, U+0218-021B, U+0237, U+0259, U+1E80-1E85, U+1E9E, U+20A1, U+20A4, U+20A6, U+20A8-20AA, U+20AD-20AE, U+20B1-20B2, U+20B4-20B5, U+20B8-20BA, U+20BD, U+20BF; }
@font-face { font-family: "IBM Plex Sans"; font-style: normal; font-weight: 700; font-display: swap;
  src: url("../fonts/plex-sans/IBMPlexSans-Bold-Cyrillic.woff2") format("woff2");
  unicode-range: U+0400-045F, U+0462-0463, U+046A-046B, U+0472-0475, U+0490-04C2, U+04CF-04D9, U+04DC-04E9, U+04EE-04F9, U+0524-0525; }
@font-face { font-family: "IBM Plex Sans"; font-style: normal; font-weight: 700; font-display: swap;
  src: url("../fonts/plex-sans/IBMPlexSans-Bold-Pi.woff2") format("woff2");
  unicode-range: U+0E3F, U+2000-200D, U+2015, U+2028-2029, U+202F, U+2032-2033, U+2070, U+2074-2079, U+2080-2089, U+2113, U+2116, U+2126, U+212E, U+2150-2151, U+2153-215E, U+2190-2199, U+21A9-21AA, U+21B0-21B3, U+21B6-21B7, U+21BA-21BB, U+21C4, U+21C6, U+2202, U+2206, U+220F, U+2211, U+2215, U+221A, U+221E, U+222B, U+2248, U+2260, U+2264-2265, U+25CA, U+2713, U+274C, U+2B0E-2B11, U+ECE0, U+EFCC, U+FEFF, U+FFFD; }
@font-face { font-family: "IBM Plex Mono"; font-style: normal; font-weight: 400; font-display: swap;
  src: url("../fonts/plex-mono/IBMPlexMono-Regular-Latin1.woff2") format("woff2");
  unicode-range: U+0020-007E, U+00A0-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2013-2014, U+2018-201A, U+201C-201E, U+2020-2022, U+2026, U+2030, U+2039-203A, U+2044, U+20AC, U+2122, U+2212, U+FB01-FB02; }
@font-face { font-family: "IBM Plex Mono"; font-style: normal; font-weight: 400; font-display: swap;
  src: url("../fonts/plex-mono/IBMPlexMono-Regular-Cyrillic.woff2") format("woff2");
  unicode-range: U+0400-045F, U+0462-0463, U+046A-046B, U+0472-0475, U+0490-04C2, U+04CF-04D9, U+04DC-04E9, U+04EE-04F9, U+0524-0525; }
@font-face { font-family: "IBM Plex Mono"; font-style: normal; font-weight: 500; font-display: swap;
  src: url("../fonts/plex-mono/IBMPlexMono-Medium-Latin1.woff2") format("woff2");
  unicode-range: U+0020-007E, U+00A0-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2013-2014, U+2018-201A, U+201C-201E, U+2020-2022, U+2026, U+2030, U+2039-203A, U+2044, U+20AC, U+2122, U+2212, U+FB01-FB02; }
@font-face { font-family: "IBM Plex Mono"; font-style: normal; font-weight: 500; font-display: swap;
  src: url("../fonts/plex-mono/IBMPlexMono-Medium-Cyrillic.woff2") format("woff2");
  unicode-range: U+0400-045F, U+0462-0463, U+046A-046B, U+0472-0475, U+0490-04C2, U+04CF-04D9, U+04DC-04E9, U+04EE-04F9, U+0524-0525; }

/*
 * ── основа ──────────────────────────────────────────────────────────────────────────────────────────
 *
 * Тема одна — светлая, и браузеру это сказано прямо: встроенные элементы управления — полосы прокрутки,
 * выпадающие списки, поля дат — тоже остаются светлыми, какую бы тему ни выбрал посетитель в системе.
 * Тёмной темы у платформы нет — её никто не рисовал и не проверял (требование «Тема страницы светлая и не
 * переключается сама»).
 *
 * Корневой кегль браузера не трогается: кегль страницы задан на body переменной, остальные размеры — в
 * пикселях от переменных, поэтому ни от корневого кегля, ни от ширины окна они не зависят.
 */
:root { color-scheme: light; }
*,
*::before,
*::after { box-sizing: border-box; }

/* цифры табличные: в колонках сумм и характеристик разряды стоят друг под другом (требование «Шрифты…») */
body {
  margin: 0;
  background: var(--c-canvas);
  color: var(--c-text);
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  font-variant-numeric: tabular-nums;
}

/*
 * ── текст ───────────────────────────────────────────────────────────────────────────────────────────
 *
 * Заголовки ступенями (решение владельца 2026-09-27, задача 1.12): страница — 28, раздел страницы — 20,
 * заголовок внутри блока — карточки или боковой колонки — 18, подзаголовок — 16, четвёртый уровень — 15.
 * Заголовок секции узкой колонки (фасеты) — 14, переменной --fs-h-narrow: её берёт кабинет, у которого такая
 * колонка есть. Под заголовком — 12 px; над ним отступ даётся, только когда перед ним уже есть содержимое:
 * первым в карточке заголовок встаёт вплотную к её полю.
 */
h1,
h2,
h3,
h4 { margin: 0 0 12px; font-weight: 600; color: var(--c-text); }
h1 { font-size: var(--fs-h1); line-height: 1.2; letter-spacing: -0.01em; }
h2 { font-size: var(--fs-h2); line-height: 1.3; }
h3 { font-size: var(--fs-h3); line-height: 1.35; }
h4 { font-size: var(--fs-h4); line-height: 1.4; }
:where(.card, aside) h2 { font-size: var(--fs-h2-block); }

/*
 * Вертикальный ритм голой разметки (задача 1.12). Набор Pico давал отступы абзацам, перечням, таблицам,
 * формам и полям сам; своя система обязана давать их тоже — иначе экраны, которые группы 2–4 ещё не
 * переделали, стоят вплотную: подпись, поле и кнопка слипаются, кнопка прилипает к блоку над ней.
 *
 * Правило одно: блок потока — абзац, перечень, таблица, форма, блок, ряд действий, пояснение — оставляет
 * под собой --flow; последний в своём контейнере — ничего, поле контейнера уже есть. Заголовок, идущий после
 * содержимого, отделён сверху --flow-section: он начинает новую часть, а не продолжает предыдущую.
 */
p,
ul,
ol,
dl,
table,
form,
fieldset,
pre,
blockquote,
figure { margin: 0 0 var(--flow); }
:where(p, ul, ol, dl, table, form, fieldset, pre, blockquote, figure, .card, .cards, .actions, .note, .empty, .props)
  ~ :is(h1, h2, h3, h4) { margin-top: var(--flow-section); }
/* заголовок сразу под заголовком — подзаголовок той же части, а не новая часть */
:is(h1, h2, h3, h4) + :is(h1, h2, h3, h4) { margin-top: 0; }

strong,
b { font-weight: 600; }
img { max-width: 100%; height: auto; vertical-align: middle; border: 0; }

ul,
ol { padding-left: 20px; }
li { margin: 0 0 4px; }
li > :is(ul, ol) { margin: 4px 0 0; }

dt { color: var(--c-text-2); }
dd { margin: 0 0 8px; }

/*
 * Длинное значение не вылезает из своей колонки (задача 1.12): код модели в строке факта карточки позиции
 * уходил в соседний блок. Значение факта и ячейка таблицы переносятся в пределах колонки, код в них — тоже:
 * короткий код стоит одной строкой, как и прежде, а длиннее колонки — переносится, а не режет соседа.
 *
 * В ячейке — break-word, а не anywhere (задача 1.16, заявка группы 4): с anywhere таблица считала, что слово
 * можно разорвать где угодно, и сжимала колонку уже слова — «CONTRACT_SIG|NED» в ходе сделки. С break-word
 * колонка не уже самого длинного слова, а слово длиннее колонки, которой расти некуда, всё равно переносится.
 * Факт — строка сетки с колонкой заданной ширины, там сжимать некого, и anywhere остаётся.
 */
dd { overflow-wrap: anywhere; }
td { overflow-wrap: break-word; }

/*
 * Ссылка — своим цветом, а не акцентом: акцент — отметка по закрытому перечню (D5). Ссылка-кнопка
 * (role="button") правилами ссылки не красится — её рисуют правила кнопок ниже; :where() держит вес
 * селектора таким же, как у голого тега, чтобы кнопка побеждала порядком, а не !important.
 *
 * Подчёркнута ссылка только в тексте абзаца (задача 1.12): там она — слово среди слов, и без черты её не
 * отличить. На полотне ссылка темнее: цвет ссылки на полотне даёт 4,3:1 — ниже AA для кегля 15 px (D3).
 * Правило берёт текст, стоящий прямо в содержимом страницы; в блоках, таблицах и панелях фон белый.
 */
a:where(:not([role="button"])) { color: var(--c-link); text-decoration: none; text-underline-offset: 2px; }
a:where(:not([role="button"])):hover { color: var(--c-link-hover); text-decoration: underline; }
p a:where(:not([role="button"])) { text-decoration: underline; }
:where(.content > :is(p, ul, ol, dl)) a:where(:not([role="button"])) { color: var(--c-link-hover); }

/*
 * Навигация и перечни — ряды ссылок, а не текст: подчёркнутые оранжевые подкатегории, пункты меню и строки
 * таблиц шумели. В них ссылка — цветом текста без черты; при наведении — цветом ссылки с чертой. Цвет
 * наведения тот, что читается и на полотне (6,1:1), и на белом (7,3:1).
 */
:where(nav, ul, ol, table) a:where(:not([role="button"])) { color: var(--c-text); text-decoration: none; }
:where(nav, ul, ol, table) a:where(:not([role="button"])):hover { color: var(--c-link-hover); text-decoration: underline; }

/*
 * ── кнопки ──────────────────────────────────────────────────────────────────────────────────────────
 *
 * Пять видов (D4), у всех высота --h-l, скругление --radius, надпись 15/600. Вид задаёт класс, высоту —
 * место: в сетке позиций, фасетах и страницах перечня кнопка ниже, в строке таблицы — ещё ниже, в блоке
 * покупки — выше; эти правила стоят у своих мест, а не у кнопки.
 *
 *   без класса  — графитовая: «Каталог», «В корзину» в сетке, действие в блоке;
 *   .outline    — графитовый контур: заметный переход («Карточка модели →»);
 *   .secondary  — с рамкой: разделы шапки, «Открыть», «Отмена», «Показать ещё»;
 *   .accent     — главное действие экрана, одно на экран (D5);
 *   в строке поиска — нейтральная «Найти».
 *
 * Кнопка с рамкой объявлена после контура: у страниц перечня обе они стоят на одной ссылке, и такая ссылка —
 * кнопка с рамкой.
 */
button,
[role="button"],
input:is([type="submit"], [type="button"], [type="reset"]) {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  height: var(--h-l);
  margin: 0;
  padding: 0 20px;
  border: 1px solid var(--c-text);
  border-radius: var(--radius);
  background: var(--c-text);
  color: var(--c-on-dark);
  font-family: inherit;
  font-size: var(--fs-body);
  font-weight: 600;
  line-height: 1.2;
  text-decoration: none;
  white-space: nowrap;
  vertical-align: middle;
  cursor: pointer;
}
button:hover,
[role="button"]:hover { background: var(--c-text-soft); border-color: var(--c-text-soft); color: var(--c-on-dark); }

.outline { background: var(--c-surface); border-color: var(--c-text); color: var(--c-text); }
.outline:hover { background: var(--c-subtle); border-color: var(--c-text); color: var(--c-text); }

.secondary { background: var(--c-surface); border-color: var(--c-border); color: var(--c-text); }
.secondary:hover { background: var(--c-surface); border-color: var(--c-text); color: var(--c-text); }

/* главное действие: надпись графитовая — белая на оранжевом дала бы 3,05:1 (сценарий «Текст на акценте») */
.accent { background: var(--c-accent); border-color: var(--c-accent); color: var(--c-on-accent); }
.accent:hover { background: var(--c-accent); border-color: var(--c-text); color: var(--c-on-accent); }

/* кнопка строки поиска — нейтральная: она продолжает поле, а не спорит с кнопками шапки */
[role="search"] button { background: var(--c-neutral); border-color: var(--c-border); color: var(--c-text); }
[role="search"] button:hover { background: var(--c-neutral); border-color: var(--c-text); color: var(--c-text); }

button:disabled,
[role="button"][aria-disabled="true"] { opacity: .5; cursor: not-allowed; }

/* действие в строке таблицы ниже: строка не должна расти ради кнопки */
td :is(button, [role="button"]) { height: var(--h-s); padding: 0 14px; }

/*
 * Фокус с клавиатуры виден у каждого элемента одинаково — графитовой рамкой с зазором. Акцентом фокус не
 * выделяется: фокус есть у всего, и акцент перестал бы быть отметкой (D5).
 */
:focus-visible { outline: 2px solid var(--c-text); outline-offset: 2px; }

/*
 * ── поля ────────────────────────────────────────────────────────────────────────────────────────────
 *
 * Поле — --h-field, 40 px, текст в 10 px от рамки (--field-pad): компактнее кнопки формы, и воздуха вокруг
 * значения не больше, чем нужно глазу (решение владельца 2026-09-27, задача 2.20; прежде поле было высотой
 * кнопки, 44, с отступом 12). Кнопка, стоящая с полем в одном ряду, берёт высоту поля — правило ряда ниже
 * (сценарий «Поле и кнопка одной строки»). Фон поля светлее блока, рамка — как у кнопки с рамкой. Поле —
 * блоком на ширину своего места, но никогда не шире родителя и не шире --field-max: одиночная форма — вход,
 * привязка юрлица, точка доставки — не растягивается на всю страницу (задача 1.12). В ряду действий и в
 * отборе ширину задают правила ряда; в ячейке таблицы предела нет — поле держит колонку.
 */
input:not([type="checkbox"], [type="radio"], [type="file"], [type="hidden"], [type="submit"], [type="button"], [type="reset"]),
select,
textarea {
  display: block;
  width: 100%;
  max-width: min(100%, var(--field-max));
  height: var(--h-field);
  margin: 0;
  padding: 0 var(--field-pad);
  border: 1px solid var(--c-border);
  border-radius: var(--radius);
  background: var(--c-subtle);
  color: var(--c-text);
  font-family: inherit;
  font-size: var(--fs-body);
  line-height: 1.2;
}
textarea { height: auto; min-height: calc(var(--h-field) * 2); padding: 8px var(--field-pad); line-height: var(--lh-body); resize: vertical; }
/*
 * Поле поиска браузеры на WebKit рисуют своим видом: с ним поле не берёт отступ текста, и значок, стоящий в
 * поле, наезжал бы на подсказку. Вид обычного поля — как у остальных полей системы.
 */
input[type="search"] { -webkit-appearance: textfield; appearance: textfield; }
input[type="search"]::-webkit-search-decoration { -webkit-appearance: none; }
::placeholder { color: var(--c-placeholder); opacity: 1; }
td :is(input, select, textarea):not([type="checkbox"], [type="radio"]) { max-width: 100%; }

/* подпись над полем — 14/500, промежуток до поля 6 px */
label { display: block; margin: 0 0 6px; font-size: var(--fs-sm); font-weight: 500; }

/*
 * Поле прямо в простой форме — подпись, поле, кнопка подряд, как на входе, — отделено от следующего --flow:
 * кнопка под полем не встаёт вплотную (задача 1.12). Форма с классом — ряд действий, строка поиска, диапазон
 * фасета, ставки в ячейке — раскладывает поля сама, промежутком ряда.
 */
:where(form:not([class]), fieldset) > :is(input:not([type="hidden"], [type="checkbox"], [type="radio"], [type="submit"], [type="button"], [type="reset"]), select, textarea) {
  margin-bottom: var(--flow);
}

/*
 * Ошибка поля — у самого поля (требование «Состояния и ошибки названы словом и цветом системы»): рамка
 * толще и цветом ошибки, фон белый, подпись и сообщение — цветом ошибки. Отступ меньше на пиксель, чтобы
 * текст в поле не сдвигался от толщины рамки. Помечает поле атрибут aria-invalid, а не класс: его же читают
 * с экрана.
 */
:is(input, select, textarea)[aria-invalid="true"]:not([type="file"]) {
  border: 2px solid var(--c-error-line);
  background: var(--c-surface);
  padding-inline: calc(var(--field-pad) - 1px);
}
textarea[aria-invalid="true"] { padding-block: 7px; }
.field:has([aria-invalid="true"]) > label { color: var(--c-error); }
.field-error { margin: 0; color: var(--c-error); font-size: var(--fs-xs); }

/*
 * Выбор файла — кнопкой с рамкой, как «Отмена» рядом: у браузера своя серая кнопка, которую ни с чем в
 * кабинетах не спутать.
 */
input[type="file"] { display: block; max-width: 100%; color: var(--c-text-2); font-family: inherit; font-size: var(--fs-sm); }
input[type="file"]::file-selector-button {
  height: var(--h-l);
  margin-right: 12px;
  padding: 0 18px;
  border: 1px solid var(--c-border);
  border-radius: var(--radius);
  background: var(--c-surface);
  color: var(--c-text);
  font-family: inherit;
  font-size: var(--fs-body);
  font-weight: 600;
  cursor: pointer;
}
input[type="file"]::file-selector-button:hover { border-color: var(--c-text); }
input[type="file"][aria-invalid="true"]::file-selector-button { border: 2px solid var(--c-error-line); }

/*
 * Флажок и радиокнопка — родные, 18 px, отмечены графитом, как на макете задачи. Строка выбора в форме —
 * рамка высотой поля; выбранная строка — графитовой рамкой и светлым фоном: выбор видно и без галочки.
 */
input:is([type="checkbox"], [type="radio"]) {
  flex: none;
  width: 18px;
  height: 18px;
  margin: 0;
  accent-color: var(--c-text);
  vertical-align: middle;
}
/*
 * Вес правила — как у голого label (:where): строку выбора в ряду действий ряд ставит без отступа, и правило
 * ряда должно её перебивать.
 */
label:where(:has(> input:is([type="checkbox"], [type="radio"]))) {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: var(--h-l);
  margin: 0 0 8px;
  padding: 0 14px;
  border: 1px solid var(--c-border);
  border-radius: var(--radius);
  background: var(--c-surface);
  font-size: var(--fs-body);
  font-weight: 400;
  cursor: pointer;
}
label:has(> input:checked) { border-color: var(--c-text); background: var(--c-subtle); }

fieldset { min-width: 0; padding: 0; border: 0; }
legend { margin: 0 0 8px; padding: 0; font-size: var(--fs-sm); font-weight: 500; }

/*
 * ── таблицы ─────────────────────────────────────────────────────────────────────────────────────────
 *
 * Перечни остаются таблицами: прогон продавца считает строки, спека продавца требует перечень таблицей (D4).
 * Таблица на полотне — сама себе блок: белая, с рамкой; в карточке рамку держит карточка. Шапка — светлой
 * полосой и мелкой подписью, строки — через линию; зебры у таблиц нет, она у перечня характеристик.
 */
table {
  width: 100%;
  border: 1px solid var(--c-border);
  border-radius: var(--radius);
  border-collapse: separate;
  border-spacing: 0;
  background: var(--c-surface);
}
th,
td { padding: 12px 20px; border-bottom: 1px solid var(--c-line); text-align: left; vertical-align: middle; }
th { font-weight: 600; }
/*
 * Подпись колонки — одной строкой: подписи короткие, а «Кол-во», разорванное по дефису, читается двумя словами
 * (задача 1.16). Поле по бокам у подписи то же, что у ячеек, — задаётся только высота: до 720 поле ячеек уже, и
 * подпись числа должна стоять над числом.
 */
thead th { padding-block: 10px; background: var(--c-subtle); color: var(--c-text-2); font-size: var(--fs-xs); font-weight: 500; white-space: nowrap; }
thead th:first-child { border-top-left-radius: var(--radius); }
thead th:last-child { border-top-right-radius: var(--radius); }
:is(tbody, tfoot) tr:last-child > :is(th, td) { border-bottom: 0; }
.card table { border: 0; border-radius: 0; }
/*
 * Узкий экран: таблица, которой не хватило ширины и после переноса слов, прокручивается внутри своего блока,
 * а страница не уезжает (D4). Блок шириной по содержимому, но не шире страницы: узкая таблица не растянута
 * рамкой шире своих строк, широкая — упирается в край и прокручивается.
 */
@media (max-width: 720px) {
  table { display: block; width: fit-content; max-width: 100%; overflow-x: auto; }
  th,
  td { padding-inline: 12px; }
}

/* числа в таблице — вправо: суммы читаются справа, разряды стоят друг под другом */
th.num,
td.num { text-align: right; }
/*
 * «Не переносить» — просьба ячейки: номер заявки, ИНН, сумма. Код в ячейке система переносит (ниже, .mono), но
 * ячейку с .nowrap это правило обходит — просьба побеждает (задача 1.16).
 */
.nowrap { white-space: nowrap; }

/*
 * ── раскрытие: details / summary ─────────────────────────────────────────────────────────────────────
 *
 * Меню каталога, «Ещё», меню посетителя и разделы мега-меню раскрываются без скриптов (ADR-0011). Значок
 * раскрытия рисуется рамками и поворотом, а не картинкой: картинка потребовала бы адреса в файле стилей.
 */
details { margin: 0; }
summary { cursor: pointer; list-style: none; }
summary::-webkit-details-marker { display: none; }
details > summary::after {
  content: "";
  flex: none;
  display: inline-block;
  width: 7px;
  height: 7px;
  margin-left: 10px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: translateY(-2px) rotate(45deg);
}
details[open] > summary::after { transform: translateY(2px) rotate(-135deg); }

/*
 * ── полотно страницы и шапка ────────────────────────────────────────────────────────────────────────
 *
 * Шапка одна на три кабинета (требование «Шапка трёх кабинетов устроена одинаково», design.md → D7): полоса
 * акцента 4 px, под ней белая строка 72 px с нижней рамкой; слева знак и название, кнопка каталога кабинета,
 * разделы кнопками с рамкой; справа — меню посетителя под его именем. Что стоит в шапке, решает раскладка
 * кабинета и сервер; как оно выглядит — эти правила.
 */

/*
 * Одно поле по бокам на шапку и на содержимое: контейнер один, поэтому левый край названия площадки
 * совпадает с левым краем содержимого под ним (change 0045, сценарий «Шапка выровнена с содержимым»).
 *
 * Поле считается от ширины окна, а не через max-width на каждом блоке: подложка шапки при этом остаётся во
 * всю ширину — это полоса, а не карточка, — а её содержимое встаёт в тот же контейнер, что и страница. Пока
 * окно уже контейнера (--page-max), поле равно --gutter и содержимое тянется; когда шире — поле растёт
 * поровну с двух сторон (сценарий «Широкий экран не оставляет поле с одной стороны»). Само поле меняется
 * точками перелома переменной --gutter в блоке переменных: 40, 24 и 16 px.
 */
.topbar,
.content { padding-inline: max(var(--gutter), calc((100% - var(--page-max)) / 2)); }

/* полотно: сверху воздух до крошек и заголовка, снизу — больше, чтобы последний блок не лип к краю окна */
.content { padding-block: 28px 48px; }

/*
 * Шапка. Полоса акцента — верхней рамкой самой шапки, а не отдельным элементом: раскладки кабинетов её не
 * повторяют, и прилипает к верху окна она вместе с шапкой. Высота — наименьшая: ниже 1280 части шапки
 * переносятся строками, и строка растёт.
 *
 * Промежуток между частями — 12 px от 1440 и 8 px ниже: на 1280 строка витрины тесная (бюджет — D7), и каждый
 * лишний пиксель промежутка отнимается у строки поиска.
 */
.topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: calc(var(--header-h) + var(--stripe));
  border-top: var(--stripe) solid var(--c-accent);
  border-bottom: 1px solid var(--c-border);
  background: var(--c-surface);
}
@media (min-width: 1440px) {
  .topbar { gap: 12px; }
}
.topbar a:where(:not([role="button"])) { color: var(--c-text); text-decoration: none; }

/* знак — квадрат акцента, у всех кабинетов один; кабинет различает название рядом (D4) */
.brand {
  display: flex;
  flex: 0 0 auto;
  align-items: center;
  gap: 10px;
  margin-right: 8px;
  font-size: var(--fs-brand);
  font-weight: 700;
  letter-spacing: -0.01em;
  white-space: nowrap;
}
.brand-dot { flex: none; width: 14px; height: 14px; background: var(--c-accent); }
/*
 * Подпись у названия площадки — пояснение, а не название: у кабинетов продавца и менеджера это их имя
 * («кабинет продавца»), и видна она всегда — строка их шапки без поиска, место есть (задача 1.13). Витрина,
 * у которой строка тесна, прячет свою подпись сама, ниже 1920 (change 0045).
 */
.brand-sub { color: var(--c-text-3); font-size: var(--fs-2xs); font-weight: 400; letter-spacing: 0; }

/*
 * Кнопка каталога кабинета («Каталог» у покупателя, «Мой каталог» у продавца) — главная кнопка шапки, залитая
 * графитом, со значком «≡» вместо стрелки раскрытия. Каталог не ответил — кнопка остаётся на месте и
 * раскрывается, но выглядит кнопкой с рамкой и надписью цветом «внимания» (D9).
 */
.topbar .catalog-button { padding: 0 18px 0 14px; border-color: var(--c-text); background: var(--c-text); color: var(--c-on-dark); }
.topbar .catalog-button:hover { border-color: var(--c-text-soft); background: var(--c-text-soft); color: var(--c-on-dark); }
.topbar .catalog-button::after { content: none; }
.topbar .catalog-button.stale,
.topbar .catalog-button.stale:hover { border-color: var(--c-border); background: var(--c-surface); color: var(--c-warn-ink); }

/*
 * Разделы — кнопками с рамкой одной высоты с кнопкой каталога, подряд слева. Переноса нет и сжатия нет
 * (change 0045): перенос заворачивал разделы по одному, сжатие обрезало бы названия. Лишние разделы сервер
 * убирает в «Ещё».
 *
 * Раздел бывает ссылкой-кнопкой (витрина) и простой ссылкой (продавец, менеджер) — вид у них один. Текущий
 * раздел — графитовой рамкой, светлым фоном и насыщенной надписью, одинаково во всех кабинетах (сценарий
 * «Текущий раздел выделен одинаково»); акцентом он не выделяется — D5.
 */
.topbar .sections { display: flex; justify-content: flex-start; gap: 8px; flex-wrap: nowrap; flex: 0 0 auto; }
.topbar .sections > a,
.topbar .sections-more > summary {
  display: inline-flex;
  align-items: center;
  height: var(--h-l);
  padding: 0 16px;
  border: 1px solid var(--c-border);
  border-radius: var(--radius);
  background: var(--c-surface);
  color: var(--c-text);
  font-size: var(--fs-body);
  font-weight: 500;
  text-decoration: none;
  white-space: nowrap;
}
.topbar .sections > a:hover,
.topbar .sections-more > summary:hover { border-color: var(--c-text); background: var(--c-surface); color: var(--c-text); }
.topbar .sections > a:is(.active, [aria-current="page"]),
.topbar .sections-more:has(a:is(.active, [aria-current="page"])) > summary {
  border-color: var(--c-text);
  background: var(--c-subtle);
  color: var(--c-text);
  font-weight: 600;
}

/*
 * «Ещё» и меню посетителя раскрываются без скриптов (ADR-0011). Панель выпадает поверх страницы, а не
 * раздвигает шапку: белая, с рамкой и единственной тенью системы; пункты по 40 px, текущий — как текущий
 * раздел, «Выйти» отделён линией: он не раздел, а конец работы.
 */
.topbar .sections-more,
.topbar .visitor-menu { position: relative; }
.topbar .sections-more .more-panel,
.topbar .visitor-menu .more-panel {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  z-index: 30;
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 220px;
  padding: 6px;
  border: 1px solid var(--c-border);
  border-radius: var(--radius);
  background: var(--c-surface);
  box-shadow: var(--shadow-panel);
}
.topbar .more-panel > a {
  display: flex;
  align-items: center;
  min-height: var(--h-m);
  padding: 0 12px;
  border: 1px solid transparent;
  border-radius: var(--radius-mark);
  color: var(--c-text);
  font-weight: 500;
  text-decoration: none;
  white-space: nowrap;
}
.topbar .more-panel > a:hover { background: var(--c-subtle); }
.topbar .more-panel > a:is(.active, [aria-current="page"]) { border-color: var(--c-text); background: var(--c-subtle); font-weight: 600; }
.topbar .more-panel > .sign-out { margin-top: 6px; border-top-color: var(--c-line); border-radius: 0; }
/*
 * Обычное состояние вошедшего — «продавец», «менеджер» — шапку не занимает и названо первой строкой меню под
 * именем (D7). Строка — не пункт: не ссылка и не подсвечивается, метка стоит на высоте пункта. Правило одно на
 * кабинеты (задача 1.16): прежде у продавца и менеджера строка была своя и стояла по-разному.
 */
.topbar .more-panel > .visitor-role { display: flex; align-items: center; min-height: var(--h-m); margin: 0; padding: 0 12px; }

/*
 * Посетитель — справа. Меню под его именем — прозрачная кнопка: имя 15/500 и стрелка третичным цветом;
 * рядом с именем — только состояние, требующее внимания, или значок привязки к юрлицу (D7).
 */
.visitor { display: flex; flex: 0 0 auto; align-items: center; gap: 10px; margin-left: auto; white-space: nowrap; }
.topbar .visitor-menu > summary {
  display: flex;
  align-items: center;
  gap: 8px;
  height: var(--h-l);
  padding: 0 2px 0 8px;
  border-radius: var(--radius);
  color: var(--c-text);
  font-weight: 500;
  white-space: nowrap;
}
.topbar .visitor-menu > summary::after { margin-left: 2px; color: var(--c-text-3); }
.topbar .visitor-menu:has(a:is(.active, [aria-current="page"])) > summary { font-weight: 600; }
/*
 * Имя приходит из профиля и бывает длинным; в строке шапки оно занимает не больше 12 знаков и не растягивает
 * её (D7: при кегле 15 px строка витрины на 1280 сходится с этим пределом). Обрезается конец — начало
 * человек узнаёт, полностью имя — в профиле.
 */
.visitor-name { max-width: 12ch; overflow: hidden; text-overflow: ellipsis; font-weight: 500; }

/*
 * Уже 1280 одна строка не обещана (D7: одна строка — на объявленных широких ширинах), но и лишних строк шапка
 * не заводит: на следующую строку уходит только то, что не поместилось в эту, а не сразу все разделы (задача
 * 1.15, замечание владельца 2026-09-27: на 1000 шапка продавца, которой хватало места на одну строку, вставала
 * в три по 177 px). Сжатия до многоточия нет по-прежнему: разделы не сжимаются, пока помещаются в строку, а не
 * поместились и в пустую — переносятся внутри своего ряда.
 *
 * Подпись у знака кабинета («кабинет продавца», «кабинет менеджера») между 721 и 1279 скрыта: без неё
 * шапка менеджера на 1000 встаёт в одну строку (знак 259 px с подписью и 136 без неё), а имя кабинета на
 * этих ширинах видно в заголовке вкладки. До 720 шапка всё равно в несколько строк, и подпись там есть (1.13).
 */
@media (max-width: 1279px) {
  .topbar { flex-wrap: wrap; padding-block: 12px; }
  .topbar .sections { flex: 0 1 auto; flex-wrap: wrap; min-width: 0; }
}
@media (min-width: 721px) and (max-width: 1279px) {
  .topbar .brand-sub { display: none; }
}
/*
 * До 720 панели «Ещё» и меню посетителя встают в поток: на узкой ширине им некуда выпадать, а выпавшая
 * вправо панель «Ещё», стоящая у левого края второй строки, ушла бы за край окна.
 */
@media (max-width: 720px) {
  .topbar .sections-more,
  .topbar .visitor-menu { position: static; }
  .topbar .sections-more .more-panel,
  .topbar .visitor-menu .more-panel {
    position: static;
    flex-direction: row;
    flex-wrap: wrap;
    min-width: 0;
    padding: 0;
    border: 0;
    box-shadow: none;
  }
}

/*
 * ── составные части ─────────────────────────────────────────────────────────────────────────────────
 *
 * Общие для кабинетов части (design.md → D4): имена прежние — на них стоят шаблоны и тесты, — вид «Стали».
 * Своё у кабинета — мега-меню, карточка позиции, фасеты, очередь — живёт в его файле.
 */

/* подписи и приглушённый текст; вводный абзац страницы не шире строки, которую читают целиком */
.muted { color: var(--c-text-2); }
.lead { max-width: 760px; color: var(--c-text-2); }
/*
 * Моноширинный — только код: модели, артикула, номера, ключа процесса (требование «Шрифты приходят своей
 * статикой»). Код не рвётся переносом посередине: «MP-MT0101-1», разорванный по дефису, читается как два
 * разных (change 0045).
 */
.mono { font-family: var(--font-mono); white-space: nowrap; }
/*
 * Код в значении факта и в ячейке таблицы — исключение: колонка там уже его, и длинный код («high-precision-
 * nc-machine-reamers-…» из legacy-каталога) переносится в её пределах, а не уходит в соседний блок (задача
 * 1.12). Короткий код в колонке помещается и стоит одной строкой, как прежде.
 *
 * Исключение не касается ячейки, которая просит не переносить (.nowrap), и кода под названием (.row-code):
 * прежде правило весило больше, чем .nowrap, и номер «ЗА-2026-0007» рвался по дефису вопреки просьбе, а
 * кабинеты держали для этого свои td.mono.nowrap (задача 1.16, заявки групп 3 и 4).
 */
:is(dd, td):not(.nowrap).mono,
:is(dd, td):not(.nowrap) .mono:not(.row-code) { white-space: normal; }
/*
 * Код под названием в строке таблицы — код модели у продукта, путь категории у зоны продаж: 12 px третичным
 * цветом, своей строкой и одной строкой — разорванный по дефису код читается как два разных; длинный код
 * расширяет колонку названия (D4; задача 1.16 — у продавца была своя копия).
 */
.row-code { display: block; color: var(--c-text-3); font-family: var(--font-mono); font-size: var(--fs-2xs); white-space: nowrap; }
/* текст только для чтения с экрана: подпись, которую глазу заменяет значок */
.vh {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/*
 * Хлебные крошки: ряд ссылок через «/» с промежутком 8 px — без него «Каталог/Станки/Системы» читается
 * одним словом. Цвет вторичный и на полотне читается (5,3:1), поэтому ссылки крошек не красятся ссылкой;
 * при наведении — цветом ссылки с чертой, как у всех перечней.
 */
.crumbs {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 8px;
  margin: 0 0 14px;
  color: var(--c-text-2);
  font-size: var(--fs-sm);
}
.crumbs a:where(:not([role="button"])) { color: var(--c-text-2); text-decoration: none; }
.crumbs a:where(:not([role="button"])):hover { color: var(--c-link-hover); text-decoration: underline; }

/*
 * ── блок ──
 *
 * Карточка — единица содержимого страницы и единица деградации блока: белая, рамкой, без тени. Без полосы
 * заголовка — поля 20/24; с полосой (.card.flush) — заголовок в полосе с нижней линией, а таблица и перечень
 * фактов во всю ширину блока (D3).
 */
.card {
  margin: 0 0 var(--flow);
  padding: 20px 24px 24px;
  border: 1px solid var(--c-border);
  border-radius: var(--radius);
  background: var(--c-surface);
}
.card > :last-child { margin-bottom: 0; }
.card.flush { padding: 0; overflow: hidden; }
.card.flush > :is(h2, h3) { margin: 0; padding: 16px 20px; border-bottom: 1px solid var(--c-line); }
.card.flush > :not(h2, h3, table, .props) { margin: 0; padding: 14px 20px; }
.card.flush > table { margin: 0; }
.card.flush > .props { margin: 0; padding: 0 20px; }
.card.flush > .props.striped { padding: 0; }

/* блоки в ряд: ряд переносится, когда блоку становится уже 280 px */
.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: var(--flow); align-items: start; margin: 0 0 var(--flow); }
.cards > .card { margin: 0; }
/*
 * Блоки фактов — не уже 340 px: у фактов подпись 160 px, и в блоке 280 px значению оставалось 72–88 px — цена
 * «1 345 678,90 RUB» и название вылезали за край (задача 1.16; у продавца был свой .facts). На экране уже 340 px
 * блок во всю ширину.
 */
.cards:has(> .card > .props) { grid-template-columns: repeat(auto-fit, minmax(min(100%, 340px), 1fr)); }

/*
 * Факты «метка — значение» строками через линию, метка — колонкой 160 px. Полосами через одну (.striped) —
 * перечень характеристик: значение справа, строка 42 px, зебра вместо линий.
 */
.props { display: grid; grid-template-columns: 160px minmax(0, 1fr); margin: 0 0 var(--flow); }
.props > dt,
.props > dd { margin: 0; padding: 9px 0; border-bottom: 1px solid var(--c-line); }
.props > dt { padding-right: 16px; color: var(--c-text-2); }
.props > dt:last-of-type,
.props > dd:last-of-type { border-bottom: 0; }
.props.striped { grid-template-columns: minmax(0, 1fr) auto; }
.props.striped > dt,
.props.striped > dd { display: flex; align-items: center; min-height: 42px; padding-block: 8px; border-bottom: 0; }
.props.striped > dt { padding-left: 20px; }
.props.striped > dd { justify-content: flex-end; padding-right: 20px; font-weight: 500; text-align: right; }
.props.striped > :is(dt, dd):nth-of-type(even) { background: var(--c-zebra); }

/*
 * ── пустое состояние и пометка ──
 *
 * Пусто — это ответ, а не поломка: белый блок, первая строка — что произошло, ниже — что это значит, и
 * графитовое действие (фрагмент не знает, главное ли оно на своём экране, — D5).
 */
.empty {
  margin: 0 0 var(--flow);
  padding: 32px 24px;
  border: 1px solid var(--c-border);
  border-radius: var(--radius);
  background: var(--c-surface);
  text-align: center;
}
.empty > p { margin: 0 0 6px; color: var(--c-text-2); }
.empty > p:first-child { color: var(--c-text); font-size: var(--fs-fact); font-weight: 600; }
.empty > [role="button"] { margin-top: 14px; }

/* пояснение в блоке: светлая плашка, текст мягче основного */
.note {
  margin: 0 0 var(--flow);
  padding: 12px 14px;
  border-radius: var(--radius);
  background: var(--c-subtle);
  color: var(--c-text-soft);
  font-size: var(--fs-sm);
}
/*
 * Блок, чьи данные не пришли, стоит на своём месте, а пометка в нём — цветами «внимания» (требование
 * «Состояния и ошибки названы словом и цветом системы», сценарий «Блок не загрузился»).
 */
.stale .note { background: var(--c-warn-bg); color: var(--c-warn-ink); }

/*
 * ── метки ──
 *
 * Состояние предмета — словом, цвет только подсказывает; контраст надписи к фону каждой пары не ниже 4,5:1
 * (сценарий «Метка читается без цвета», числа — design.md → D3). Без модификатора — черновик и нейтральное.
 */
.pill {
  display: inline-block;
  padding: 3px 9px;
  border-radius: var(--radius-mark);
  background: var(--c-draft-bg);
  color: var(--c-draft-ink);
  font-size: var(--fs-xs);
  font-weight: 600;
  line-height: 1.35;
  white-space: nowrap;
  vertical-align: middle;
}
.pill-green { background: var(--c-ok-bg); color: var(--c-ok-ink); }
.pill-blue { background: var(--c-info-bg); color: var(--c-info-ink); }
.pill-amber { background: var(--c-warn-bg); color: var(--c-warn-ink); }
.pill-red { background: var(--c-danger-bg); color: var(--c-danger-ink); }

/* нейтральная метка с рамкой: счётчик у заголовка, «1 из 5»; с .mono — код модели */
.tag {
  display: inline-flex;
  align-items: center;
  padding: 3px 8px;
  border: 1px solid var(--c-border);
  border-radius: var(--radius-tag);
  background: var(--c-surface);
  color: var(--c-text-2);
  font-size: var(--fs-xs);
  line-height: 1.35;
  white-space: nowrap;
}
.tag.mono { color: var(--c-text); font-weight: 500; }

/*
 * Значок состояния посетителя: привязку к юрлицу видно, а строки на неё в шапке не тратится. Цвет —
 * «готово» у привязанного, «внимание» у непривязанного.
 */
.verified,
.unverified { display: inline-flex; align-items: center; }
.verified { color: var(--c-ok); }
.unverified { color: var(--c-warn-ink); }

/*
 * Отметка продавца квадратом перед его строкой: у площадки — акцент (ADR-0001: площадка — обычный продавец,
 * но покупатель её узнаёт), у остальных — серый. Отметка встречается у витрины и у менеджера — поэтому она
 * в системе.
 */
.seller-mark { display: inline-flex; align-items: center; gap: 8px; }
.seller-mark::before { content: ""; flex: none; width: 6px; height: 6px; background: var(--c-seller-dot); }
.seller-mark.own::before { background: var(--c-accent); }

/* отметка непустой очереди у числа: пустая очередь акцента не получает (сценарий «Пустая очередь без отметки») */
.count-mark { display: inline-block; flex: none; width: 10px; height: 10px; background: var(--c-accent); }
.count-mark.none { background: var(--c-border); }

/*
 * ── переключатель, отбор, страницы ──
 *
 * Переключатель (.segments): белая коробка с рамкой, сегменты 34 px, выбранный — графитом, не акцентом:
 * выбор не отметка, а состояние (D5). Выбранный сегмент помечают .cur или aria-current — как у прежних
 * строк порядка и области поиска.
 */
.segments {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 2px;
  padding: 3px;
  border: 1px solid var(--c-border);
  border-radius: var(--radius);
  background: var(--c-surface);
}
.segments > a {
  display: inline-flex;
  align-items: center;
  height: var(--h-seg);
  padding: 0 14px;
  border-radius: var(--radius-mark);
  color: var(--c-text);
  font-size: var(--fs-sm);
  font-weight: 500;
  text-decoration: none;
  white-space: nowrap;
}
.segments > a:hover { background: var(--c-subtle); color: var(--c-text); }
.segments > :is(.cur, [aria-current="page"], [aria-current="true"]),
.segments > :is(.cur, [aria-current="page"], [aria-current="true"]):hover { background: var(--c-text); color: var(--c-on-dark); }

/* метка отбора: что сузило выдачу, и «×», которым это снимается */
.chip {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  height: var(--h-seg);
  padding: 0 12px;
  border: 1px solid var(--c-border);
  border-radius: var(--radius);
  background: var(--c-surface);
  color: var(--c-text);
  font-size: var(--fs-sm);
  text-decoration: none;
  white-space: nowrap;
}
.chip:hover { border-color: var(--c-text); color: var(--c-text); }

/*
 * Страницы перечня — квадраты 40 × 40: текущая графитовая, остальные с рамкой. Страница бывает ссылкой-
 * кнопкой с классами прежнего набора и простой ссылкой с .cur — вид у них один. Ряд переносится: на узком
 * экране полсотни страниц уехали бы за край (change 0045).
 */
.pager { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: 20px 0; }
.pager > a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: var(--h-m);
  height: var(--h-m);
  padding: 0 12px;
  border: 1px solid var(--c-border);
  border-radius: var(--radius);
  background: var(--c-surface);
  color: var(--c-text);
  font-weight: 500;
  text-decoration: none;
}
.pager > a:hover { border-color: var(--c-text); background: var(--c-surface); color: var(--c-text); }
.pager > :is([aria-current="page"], .cur),
.pager > :is([aria-current="page"], .cur):hover { border-color: var(--c-text); background: var(--c-text); color: var(--c-on-dark); }
.pager > .muted { margin-left: 10px; font-size: var(--fs-sm); }

/*
 * ── строка заголовка страницы ──
 *
 * Заголовок слева, действия раздела справа одним рядом (макет seller-products): главное действие раздела
 * стоит у заголовка, а не под вводным абзацем. На узкой ширине ряд уходит под заголовок. Строка — вместо
 * заголовка, поэтому отступ под ней тот же, что под заголовком (задача 1.16; у продавца была своя копия).
 */
.page-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 12px 24px;
  margin: 0 0 12px;
}
.page-head > :is(h1, .actions) { margin: 0; }

/*
 * ── формы: ряд действий, отбор, поле, сводка отказа ──
 */
/*
 * Ряд действий: кнопки и ссылки-кнопки подряд через 8 px, с переносом на узкой ширине (задача 1.12). Всё,
 * что стоит в ряду, — форма с одной кнопкой, подпись с флажком, абзац — отступа потока не несёт: промежуток
 * держит ряд, а отступ снизу сдвинул бы элемент с линии ряда.
 */
.actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 0 0 var(--flow); }
.actions > * { margin: 0; }
.actions input,
.actions select { width: auto; }
input.qty { width: 88px; text-align: right; }
.filters { display: flex; flex-wrap: wrap; gap: 10px; margin: 16px 0; }
.filters input,
.filters select { width: auto; min-width: 200px; }
/*
 * Поле и кнопка одного ряда — одной высоты, высоты поля (сценарий «Поле и кнопка одной строки»): ряд действий,
 * отбор или строка поиска, в котором стоит поле, ставит кнопку рядом с ним высотой поля, а не кнопки формы
 * (задача 2.20: поле стало ниже кнопки). Ряд без поля — «Сохранить» и «Отмена» — держит высоту кнопки.
 */
:is(.actions, .filters, [role="search"]):has(> :is(input:not([type="hidden"], [type="checkbox"], [type="radio"], [type="file"]), select)) > :is(button, [role="button"]) {
  height: var(--h-field);
}
/*
 * Поле-группа — подпись, поле, подсказка, ошибка — одной колонкой через 6 px и отступ потока под собой. Не
 * шире предела поля: подсказка под полем читается той же строкой, что и поле.
 */
.field { display: flex; flex-direction: column; gap: 6px; max-width: var(--field-max); margin-bottom: var(--flow); }
.field > * { margin: 0; }
.field > .muted { font-size: var(--fs-sm); }

/*
 * Раскладка формы по образцу seller-terms (D4, D9, D10; задача 1.16 — у продавца и витрины были свои копии):
 *   .form-page     — форма и сводка отказа над ней не шире 1080 px;
 *   .form-sections — секции-блоки рядом, уже 900 — одна под другой; пояснение под заголовком секции —
 *                    .section-hint;
 *   .field-row     — ряд полей: колонки поровну по числу полей, поле .wide — вдвое шире соседа (цена рядом с
 *                    валютой, адрес рядом с названием точки); до 720 — столбцом.
 * Поле в секции и в ряду — во всю ширину своей колонки: предел одиночной формы (--field-max) здесь держит
 * колонка, а поле, упёршись в предел, оставляло бы рядом пустоту.
 */
.form-page { max-width: 1080px; }
.form-sections {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 20px;
  align-items: start;
  margin: 0 0 20px;
}
.form-sections > .card { margin: 0; }
.form-sections h2:has(+ .section-hint) { margin-bottom: 4px; }
.section-hint { color: var(--c-text-2); font-size: var(--fs-sm); }
.field-row {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  gap: 12px;
  align-items: start;
  margin: 0 0 var(--flow);
}
.field-row > .field { margin: 0; }
.field-row > .wide { grid-column: span 2; }
:is(.form-sections, .field-row) .field { max-width: none; }
:is(.form-sections, .field-row) :is(input, select, textarea) { max-width: 100%; }
@media (max-width: 899px) {
  .form-sections { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 720px) {
  .field-row { grid-auto-flow: row; }
  .field-row > .wide { grid-column: auto; }
}

/*
 * Отказ без поля — сводкой над формой, цветом ошибки (сценарий «Отказ без поля»): роль alert ставит
 * фрагмент, значок «!» — встроенным рисунком в нём же. В разметке значок идёт после текста — прогоны читают
 * сообщение сразу за якорем, — слева его ставит порядок в ряду.
 */
.form-error {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  margin: 0 0 20px;
  padding: 12px 16px;
  border: 1px solid var(--c-alert-line);
  border-radius: var(--radius);
  background: var(--c-alert-bg);
  color: var(--c-alert-ink);
}
.form-error > svg { flex: none; order: -1; margin-top: 2px; }

/* цена: выделена в строке перечня и в карточке; её отсутствие — обычный приглушённый текст */
.price { font-weight: 600; white-space: nowrap; }

/* итог: сумма отделена от строк */
.total { display: flex; justify-content: flex-end; align-items: baseline; gap: 12px; margin: 16px 0; }
.total .sum { font-size: var(--fs-h2); font-weight: 700; }

/*
 * ── ритм: последний в контейнере ──
 *
 * Последний блок потока в своём контейнере отступа под собой не оставляет: воздух внизу карточки, ячейки,
 * пояснения даёт поле самого контейнера, и отступ сверх него сделал бы низ толще верха (задача 1.12).
 * Правило стоит в конце файла: вес у него как у правил составных частей, и побеждает оно порядком.
 */
:where(p, ul, ol, dl, table, form, fieldset, pre, blockquote, figure, .card, .cards, .actions, .note, .empty, .props, .field):last-child {
  margin-bottom: 0;
}

/* htmx: пока фрагмент едет, блок приглушён */
.htmx-request { opacity: .6; transition: opacity .2s; }
