/* =========================================================================
   Дизайн-система «Стыка». Направление — «акт сверки».
   Обоснование и правила: packages/design-system/DESIGN.md
   Значения: packages/design-system/src/tokens.ts (руками здесь не писать)

   Несущая идея вёрстки — ВЕРТИКАЛЬНАЯ РАЗГРАФКА. Колонки разделены линиями,
   строки не разделены ничем, кроме ритма и чередования плоскости. Сверка —
   это сравнение колонок, и вертикаль здесь несёт смысл.
   ========================================================================= */

/* ---- Шрифты -------------------------------------------------------------
   Начертания лежат рядом, в `fonts/`, и раздаются со своего домена: в
   закрытом контуре банка сторонний шрифтовой хост недоступен ВСЕГДА, и
   кабинет, у которого типографика приезжает из интернета, там открывается
   запасными системными. Политика безопасности страницы сужена до
   `font-src 'self'` — посторонний домен не подгрузится и незаметно.

   ВЗЯТО РОВНО ТО, ЧТО СИСТЕМА УПОТРЕБЛЯЕТ, а не семейства целиком:

     • Geologica только 600. Заголовочное начертание живёт ровно в четырёх
       местах (знак, заголовок экрана, заголовок панели, заголовок состояния),
       и во всех четырёх — полужирным. 400 и 500 у неё не набирает ничто.
     • IBM Plex Sans переменным файлом на диапазон 400–600 вместо трёх
       статических. Все три толщины употребляются на каждой странице разом
       (текст 400, кнопка и текущий пункт 500, шапка таблицы и подпись поля
       600), поэтому переменный файл — не «на будущее», а меньше байт прямо
       сейчас: 104 КБ против 161 КБ у трёх статических на тот же набор
       подмножеств. Замерено, а не предположено.
     • IBM Plex Mono статикой 400 и 500: переменного у неё нет.
     • Курсивов нет ни у одного семейства — система не набирает курсивом
       ничего.

   ПОДМНОЖЕСТВА — три из шести: латиница, расширенная латиница, кириллица.
   Греческого и вьетнамского в кабинете не бывает, расширенной кириллицы
   (U+0460-052F — старославянское и нерусские алфавиты) тоже. Расширенная
   латиница взята из-за ОДНОГО знака: ₽ это U+20BD, и он лежит именно там.
   У Geologica её нет намеренно: ₽ не набирается заголовочным начертанием
   никогда — по правилу «деньги всегда моноширинным» сумма физически не может
   оказаться в заголовке. Полнота набора сверяется тестом `fonts.test.ts`:
   знак, которого нет ни в одном подмножестве, роняет сборку, а не всплывает
   прямоугольником в чужой сумме.

   Лицензии обоих семейств — SIL OFL 1.1, самостоятельный хостинг разрешён.
   Тексты лицензий лежат рядом с файлами (`fonts/OFL-*.txt`) и уезжают вместе
   с ними в `dist/`: это условие лицензии, а не формальность.               */

@font-face {
  font-family: 'Geologica';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('fonts/geologica-600-latin.woff2') format('woff2');
  unicode-range:
    U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
    U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Geologica';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('fonts/geologica-600-cyrillic.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

@font-face {
  font-family: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 400 600;
  font-stretch: 100%;
  font-display: swap;
  src: url('fonts/ibm-plex-sans-400-600-latin.woff2') format('woff2');
  unicode-range:
    U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
    U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 400 600;
  font-stretch: 100%;
  font-display: swap;
  src: url('fonts/ibm-plex-sans-400-600-latin-ext.woff2') format('woff2');
  unicode-range:
    U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329,
    U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F,
    U+A720-A7FF;
}

@font-face {
  font-family: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 400 600;
  font-stretch: 100%;
  font-display: swap;
  src: url('fonts/ibm-plex-sans-400-600-cyrillic.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('fonts/ibm-plex-mono-400-latin.woff2') format('woff2');
  unicode-range:
    U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
    U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('fonts/ibm-plex-mono-400-latin-ext.woff2') format('woff2');
  unicode-range:
    U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329,
    U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F,
    U+A720-A7FF;
}

@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('fonts/ibm-plex-mono-400-cyrillic.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('fonts/ibm-plex-mono-500-latin.woff2') format('woff2');
  unicode-range:
    U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
    U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('fonts/ibm-plex-mono-500-latin-ext.woff2') format('woff2');
  unicode-range:
    U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329,
    U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F,
    U+A720-A7FF;
}

@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('fonts/ibm-plex-mono-500-cyrillic.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

/* ---- Основание --------------------------------------------------------- */

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

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

body.st-body {
  margin: 0;
  background: var(--st-ground);
  color: var(--st-ink);
  font-family: var(--st-font-ui);
  font-size: var(--st-text-body);
  line-height: var(--st-leading-normal);
  font-variant-numeric: tabular-nums;
  -webkit-font-smoothing: antialiased;
}

.st-body :focus-visible {
  outline: var(--st-border-seam) solid var(--st-seam);
  outline-offset: 2px;
  border-radius: var(--st-radius-sm);
}

/* Ничто не уезжает вбок: широкое содержимое прокручивается внутри своей
   области, а не растягивает страницу. Проверяется замером, а не на глаз. */
.st-scroll-x {
  overflow-x: auto;
  overscroll-behavior-x: contain;
}

/* ---- Ссылки -------------------------------------------------------------
   ОДНО ПРАВИЛО НА ВСЕ ССЫЛКИ КАБИНЕТА, а не по месту. До этого правила
   покрашены были только ссылки таблицы, а те же самые идентификаторы в
   карточках телефона доставались браузеру: он рисовал их своим #0000ee, а
   после первого перехода — посещённым #551a8b. На тёмной поверхности это
   1,77:1 и 1,51:1 при пороге 4,5:1 — то самое «фиолетовое подчёркнутое,
   которое не читается».

   ССЫЛКА ОТЛИЧАЕТСЯ ОТ ТЕКСТА НЕ ЦВЕТОМ. Цвет у неё тот же, что у соседнего
   текста, — тушь. Отличают её ЛИНИЯ подчёркивания (`link-line`, ≥ 3:1 к
   поверхности в обеих темах) и моноширинное начертание идентификатора.
   Цветовая разница в тёмной теме садится первой, поэтому нести признак
   ссылки ей нельзя. Акцент появляется на наведении и фокусе — и появляется
   линией, как ему и положено в этой системе.

   `:visited` перекрыт намеренно: история переходов не имеет отношения к
   реестру заявок, а браузерная умолчальная фиолетовая ссылка — единственный
   цвет на экране, который система не выбирала.

   ПРАВИЛО ЖИВЁТ ВНУТРИ `.st-main`, А НЕ НА ВСЁМ `.st-body`. Рельс разделов,
   знак и ссылка «перейти к содержимому» — это навигация, а не текстовая
   ссылка: у них своя подача (вертикаль слева, заголовочное начертание) и своё
   `text-decoration: none`. Общее правило по одному классу перебивало их по
   силе селектора и подчёркивало всю шапку разом. Граница здесь структурная —
   область содержимого против навигационной обвязки, — а не список исключений,
   который забудут дополнить. */

.st-main a {
  color: var(--st-ink);
  text-decoration-line: underline;
  text-decoration-color: var(--st-link-line);
  text-decoration-thickness: var(--st-border-hairline);
  text-underline-offset: 2px;
  transition:
    color var(--st-duration-instant) var(--st-ease-out),
    text-decoration-color var(--st-duration-instant) var(--st-ease-out);
}

.st-main a:visited {
  color: var(--st-ink);
}

/* Моноширинный внутри ссылки наследует её цвет: иначе `.st-mono` перекрывает
   его собственным, и отклик на наведение не доходит до текста — а текст в
   ссылке реестра целиком и есть этот `.st-mono`. */
.st-main a .st-mono {
  color: inherit;
}

.st-main a:hover {
  color: var(--st-seam-ink);
  text-decoration-color: var(--st-seam);
}

.st-visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

@media (prefers-reduced-motion: reduce) {
  .st-body *,
  .st-body *::before,
  .st-body *::after {
    transition-duration: 1ms !important;
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
  }
}

/* ---- Каркас ------------------------------------------------------------ */

.st-shell {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

.st-header {
  position: sticky;
  top: 0;
  z-index: var(--st-z-sticky);
  background: var(--st-surface);
  border-bottom: var(--st-border-hairline) solid var(--st-line-strong);
}

.st-header__top {
  max-width: var(--st-content-max);
  margin: 0 auto;
  padding: var(--st-space-2) var(--st-gutter);
  display: flex;
  align-items: center;
  gap: var(--st-space-4);
}

/* Знак. Два столбца разной высоты и шов между ними — это и есть «стык».
   Рисуется разметкой, а не картинкой: знак обязан жить в тёмной теме. */
.st-mark {
  display: inline-flex;
  align-items: flex-end;
  gap: 3px;
  height: 22px;
}

.st-mark__bar {
  width: 4px;
  background: var(--st-ink);
}

.st-mark__bar:nth-child(1) {
  height: 14px;
}

.st-mark__bar:nth-child(2) {
  height: 22px;
}

.st-mark__seam {
  width: var(--st-border-seam);
  height: 22px;
  background: var(--st-seam);
}

.st-mark__bar:nth-child(4) {
  height: 18px;
}

.st-mark__bar:nth-child(5) {
  height: 10px;
}

.st-wordmark {
  font-family: var(--st-font-display);
  font-size: var(--st-text-section);
  font-weight: var(--st-weight-semibold);
  letter-spacing: var(--st-tracking-tight);
  color: var(--st-ink);
  text-decoration: none;
}

.st-header__spacer {
  flex: 1 1 auto;
}

.st-org {
  display: flex;
  flex-direction: column;
  line-height: var(--st-leading-tight);
  text-align: right;
  min-width: 0;
}

.st-org__name {
  font-size: var(--st-text-caption);
  font-weight: var(--st-weight-medium);
  color: var(--st-ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.st-org__role {
  font-size: var(--st-text-micro);
  color: var(--st-ink-faint);
}

/* Кто вошёл. Стоит рядом с тем, ЧЕЙ это кабинет, и отвечает на другой вопрос:
   имя партнёра — «чей экран», имя вошедшего — «под кем я сижу». В кабинете,
   где один человек готовит расчёт, а второй его утверждает, второй вопрос
   важнее первого: вход под чужой учётной записью там равен подписи чужим
   именем. Разделяющая линия появляется только когда слева действительно
   стоит имя партнёра — иначе она висела бы у края экрана ни при чём. */
.st-org--partner + .st-org--viewer {
  border-left: var(--st-border-hairline) solid var(--st-line);
  padding-left: var(--st-space-4);
}

/* Выход — форма, а не ссылка: выход по GET срабатывает от чужой картинки на
   постороннем сайте и от предзагрузчика браузера. Форма в шапке не должна
   ничего занимать собой. */
.st-signout {
  display: flex;
  margin: 0;
}

/* Метка контура. Не штриховка по верху окна (так сделано в ЦК) — рамка
   вокруг слова, как оттиск штампа на документе. */
.st-contour {
  font-family: var(--st-font-mono);
  font-size: var(--st-text-micro);
  letter-spacing: var(--st-tracking-label);
  text-transform: uppercase;
  color: var(--st-seam-ink);
  border: var(--st-border-hairline) solid var(--st-seam);
  border-radius: var(--st-radius-sm);
  padding: 2px var(--st-space-2);
  white-space: nowrap;
}

/* ---- Рельс разделов ---------------------------------------------------- */

.st-rail {
  border-top: var(--st-border-hairline) solid var(--st-line-faint);
  background: var(--st-surface);
}

.st-rail__inner {
  max-width: var(--st-content-max);
  margin: 0 auto;
  padding: 0 var(--st-gutter);
  display: flex;
  gap: 0;
  /* РЕЛЬС ПРОКРУЧИВАЕТСЯ ВНУТРИ СВОЕЙ ОБЛАСТИ НА ЛЮБОЙ ШИРИНЕ, а не только на
     телефоне. Замерено 25.08.2026: на 768 рельс с пометками «нет прав» у пяти
     разделов вырастал до 959 px и растягивал страницу вбок — 959 против 753.
     Раньше прокрутка стояла только в блоке ≤640, потому что семь коротких
     слов в рельс помещались всегда; они перестали помещаться в тот день,
     когда рядом с ними встало объяснение. Ничего не уезжает вбок ни на 390,
     ни на 1440 — это правило доктрины, и держать его обязана сама вёрстка, а
     не длина слов, которые в неё положат. */
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: thin;
}

/* Текущий раздел отмечен ВЕРТИКАЛЬНОЙ линией слева, а не полосой сверху и не
   заливкой: та же разграфка, что в таблицах. */
.st-rail__link {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: var(--st-space-2);
  min-height: var(--st-rail-height);
  padding: 0 var(--st-space-4);
  border-left: var(--st-border-seam) solid transparent;
  font-size: var(--st-text-lead);
  color: var(--st-ink-muted);
  text-decoration: none;
  white-space: nowrap;
  transition: color var(--st-duration-instant) var(--st-ease-out);
}

.st-rail__link:hover {
  color: var(--st-ink);
  background: var(--st-surface-sunken);
}

.st-rail__link[aria-current='page'] {
  border-left-color: var(--st-seam);
  color: var(--st-ink);
  font-weight: var(--st-weight-medium);
  background: var(--st-seam-wash);
}

.st-rail__count {
  font-family: var(--st-font-mono);
  font-size: var(--st-text-micro);
  color: var(--st-tone-alarm-ink);
  border: var(--st-border-hairline) solid var(--st-tone-alarm-edge);
  border-radius: var(--st-radius-sm);
  padding: 0 4px;
}

/* Раздел, на который нет права, НАЗВАН, а не спрятан: спрятанная кнопка
   порождает вопрос «а где?», названная — ответ «не ваша роль». Признак не
   цветовой — рядом стоит слово: цветовая разница в тёмной теме садится первой,
   а значков в этой системе нет вовсе. Ссылкой пункт остаётся намеренно, чтобы
   переход по нему привёл на честный экран «этот раздел вам не открыт», а не в
   никуда. Пометка при этом ничего не закрывает: доступ закрывает оркестратор. */
.st-rail__link--locked {
  color: var(--st-ink-faint);
}

.st-rail__locked {
  font-size: var(--st-text-micro);
  letter-spacing: var(--st-tracking-label);
  color: var(--st-ink-faint);
  white-space: nowrap;
}

/* ---- Полотно ----------------------------------------------------------- */

.st-main {
  flex: 1 1 auto;
  width: 100%;
  max-width: var(--st-content-max);
  margin: 0 auto;
  padding: var(--st-space-5) var(--st-gutter) var(--st-space-8);
}

/* Полотно ворот: заголовок экрана и панель формы стоят в одной колонке.
   Без этого заголовок «Вход» уезжал бы к левому краю полотна в 1440 px, а
   форма стояла бы посреди экрана — два разных выравнивания на странице, где
   всего два элемента. Ширина — колонка формы плюс поля полотна. */
.st-main--gate {
  max-width: calc(420px + 2 * var(--st-gutter));
}

.st-page-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--st-space-3);
  margin-bottom: var(--st-space-5);
}

.st-title {
  font-family: var(--st-font-display);
  font-size: var(--st-text-title);
  font-weight: var(--st-weight-semibold);
  letter-spacing: var(--st-tracking-tight);
  line-height: var(--st-leading-tight);
  margin: 0;
}

/* ИМЯ ОБЪЕКТА И ЕСТЬ ЗАГОЛОВОК. Человек пришёл смотреть конкретную заявку, а
   не узнать, что это заявка, — поэтому в заголовке стоит идентификатор, и по
   правилу системы он набран моноширинным: ни одно имя, по которому человек
   принимает решение, не набрано пропорциональным. Разрядка сбрасывается —
   у моноширинного она своя. */
.st-title--mono {
  font-family: var(--st-font-mono);
  font-weight: var(--st-weight-medium);
  letter-spacing: 0;
}

.st-subtitle {
  color: var(--st-ink-muted);
  font-size: var(--st-text-lead);
  margin: 0;
  max-width: 68ch;
}

.st-stack {
  display: flex;
  flex-direction: column;
  gap: var(--st-space-5);
  margin-top: var(--st-space-5);
}

.st-cols {
  display: grid;
  gap: var(--st-space-5);
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  align-items: start;
}

/* КОЛОНКИ ДЛЯ ТЕКСТА, А НЕ ДЛЯ ПАР «ПОДПИСЬ — ЗНАЧЕНИЕ». Обычные `.st-cols`
   рассчитаны на панели со ставками и лимитами: там в строке короткое значение,
   и 320 px хватает. Объяснения «этого раздела ещё не бывает» — сплошной текст
   по несколько строк, и на 1440 их вставало ЧЕТЫРЕ в ряд по 332 px: мера
   строки падала примерно до тридцати знаков, текст рвался посреди слов, а
   низ ряда становился рваным, потому что объяснения разной длины.

   Нижняя граница здесь 30rem вместо 320 px: на широком экране это ровно две
   колонки, на планшете — одна, и мера строки не выходит за то, что читается
   без усилия. `min(100%, …)` обязателен — без него колонка не даёт себя сжать
   на узком экране и распирает страницу вбок. */
.st-cols--prose {
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 30rem), 1fr));
}

/* ---- Панель ------------------------------------------------------------ */

.st-panel {
  background: var(--st-surface);
  border: var(--st-border-hairline) solid var(--st-line);
  border-radius: var(--st-radius-md);
}

.st-panel__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--st-space-3);
  padding: var(--st-space-3) var(--st-space-4);
  border-bottom: var(--st-border-hairline) solid var(--st-line);
  background: var(--st-surface-sunken);
  border-radius: var(--st-radius-md) var(--st-radius-md) 0 0;
}

.st-panel__title {
  font-family: var(--st-font-display);
  font-size: var(--st-text-section);
  font-weight: var(--st-weight-semibold);
  letter-spacing: var(--st-tracking-tight);
  margin: 0;
}

/* `min-width: 0` обязателен: подпись — гибкий элемент шапки, и внутрь неё
   кладут содержимое, которое прокручивается внутри себя (легенда тонов на
   телефоне). Без сброса минимальной ширины такое содержимое распирает шапку,
   панель и страницу — а выезд вбок в этой системе запрещён. */
.st-panel__note {
  min-width: 0;
  font-size: var(--st-text-caption);
  color: var(--st-ink-faint);
}

/* Действия панели прижаты вправо. Классом, а не атрибутом style: правило
   одно на все панели, и правится оно тоже в одном месте. */
.st-panel__actions {
  margin-left: auto;
}

.st-panel__body {
  padding: var(--st-space-4);
}

.st-panel__body--flush {
  padding: 0;
}

.st-panel__foot {
  padding: var(--st-space-3) var(--st-space-4);
  border-top: var(--st-border-hairline) solid var(--st-line-faint);
  font-size: var(--st-text-caption);
  color: var(--st-ink-faint);
}

/* ---- Деньги ------------------------------------------------------------
   Каждая сумма носит на себе шов: волосяная вертикаль перед дробной частью.
   Копейки НЕ приглушены — в этом продукте копейка несущая: она уникальна и
   служит вторым признаком сопоставления платежа с заявкой.               */

.st-money {
  font-family: var(--st-font-mono);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  letter-spacing: -0.01em;
  /* Разряды отбиты неразрывным пробелом, а в моноширинном он шириной в целый
     знак. Без сжатия просвет между разрядами получается шире шва, и «1 946 204»
     читается как три отдельных числа, а шов перестаёт означать дробную часть.
     Проверено замером: до сжатия просвет разряда 9,4 px против 8 px у шва. */
  word-spacing: -0.28em;
}

.st-money__kop {
  margin-left: 4px;
  padding-left: 5px;
  border-left: var(--st-border-hairline) solid var(--st-line-strong);
}

.st-money__unit {
  margin-left: 5px;
  color: var(--st-ink-faint);
}

.st-money--figure {
  font-size: var(--st-text-figure);
  font-weight: var(--st-weight-medium);
  line-height: var(--st-leading-tight);
}

.st-money--muted {
  color: var(--st-ink-muted);
}

.st-money--strike {
  text-decoration: line-through;
  color: var(--st-ink-faint);
}

.st-money--alarm {
  color: var(--st-tone-alarm-ink);
}

.st-money--alarm .st-money__kop {
  border-left-color: var(--st-tone-alarm-edge);
}

.st-money--done {
  color: var(--st-tone-done-ink);
}

.st-money--done .st-money__kop {
  border-left-color: var(--st-tone-done-edge);
}

/* Машинные строки: идентификаторы, ключи, хэши, время. */
.st-mono {
  font-family: var(--st-font-mono);
  font-size: var(--st-text-caption);
  font-variant-numeric: tabular-nums;
  color: var(--st-ink-muted);
  white-space: nowrap;
}

.st-mono--ink {
  color: var(--st-ink);
}

.st-mono--wrap {
  white-space: normal;
  overflow-wrap: anywhere;
}

/* ---- Состояние заявки --------------------------------------------------
   Вертикальный штрих тона и слово. Без точки и без овальной рамки: точка в
   рамке — язык «Цифрового Контура», и повторять его здесь нельзя.        */

.st-state {
  display: inline-flex;
  align-items: center;
  gap: var(--st-space-2);
  white-space: nowrap;
  color: var(--st-ink);
}

.st-state::before {
  content: '';
  width: var(--st-border-edge);
  height: 1.05em;
  flex: none;
  background: var(--st-line-strong);
}

.st-state--wait::before {
  background: var(--st-tone-wait-edge);
}

.st-state--wait {
  color: var(--st-tone-wait-ink);
}

.st-state--closed::before {
  background: var(--st-tone-closed-edge);
}

.st-state--closed {
  color: var(--st-tone-closed-ink);
}

.st-state--move::before {
  background: var(--st-tone-move-edge);
}

.st-state--move {
  color: var(--st-tone-move-ink);
}

.st-state--act::before {
  background: var(--st-tone-act-edge);
}

.st-state--act {
  color: var(--st-tone-act-ink);
}

.st-state--alarm::before {
  background: var(--st-tone-alarm-edge);
}

.st-state--alarm {
  color: var(--st-tone-alarm-ink);
  font-weight: var(--st-weight-medium);
}

.st-state--done::before {
  background: var(--st-tone-done-edge);
}

.st-state--done {
  color: var(--st-tone-done-ink);
}

/* Легенда тонов. Шесть цветов без расшифровки читает каждый по-своему. */
.st-legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--st-space-2) var(--st-space-4);
  font-size: var(--st-text-caption);
  color: var(--st-ink-muted);
}

/* ---- Реестр ------------------------------------------------------------ */

.st-table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  font-size: var(--st-text-body);
  font-variant-numeric: tabular-nums;
}

.st-table th,
.st-table td {
  text-align: left;
  padding: 0 var(--st-space-3);
  height: var(--st-row-height);
  border-right: var(--st-border-hairline) solid var(--st-line-faint);
  white-space: nowrap;
}

.st-table th:last-child,
.st-table td:last-child {
  border-right: 0;
}

.st-table thead th {
  position: sticky;
  top: 0;
  z-index: var(--st-z-sticky);
  background: var(--st-surface-sunken);
  border-bottom: var(--st-border-hairline) solid var(--st-line-strong);
  border-right-color: var(--st-line);
  font-size: var(--st-text-micro);
  font-weight: var(--st-weight-semibold);
  color: var(--st-ink-muted);
  height: auto;
  padding-top: var(--st-space-2);
  padding-bottom: var(--st-space-2);
}

/* Чередование плоскости вместо горизонтальных линеек: строку ведёт глаз, а
   не линия. Разграфка в этой системе вертикальная. */
.st-table tbody tr:nth-child(even) {
  background: var(--st-surface-sunken);
}

.st-table tbody tr:hover {
  background: var(--st-seam-wash);
}

.st-table td.st-num,
.st-table th.st-num {
  text-align: right;
}

/* Строка, требующая человека: штрих тона у левого края. */
.st-table tbody tr.st-row--alarm td:first-child {
  box-shadow: inset var(--st-border-edge) 0 0 0 var(--st-tone-alarm-edge);
}

.st-table tbody tr.st-row--act td:first-child {
  box-shadow: inset var(--st-border-edge) 0 0 0 var(--st-tone-act-edge);
}

.st-table tbody tr.st-row--muted {
  color: var(--st-ink-faint);
}

.st-table--dense th,
.st-table--dense td {
  height: var(--st-row-height-dense);
  padding-left: var(--st-space-2);
  padding-right: var(--st-space-2);
}

/* Своего правила у ссылок таблицы нет: оно было вторым описанием той же
   ссылки и отставало от общего — подчёркивание держало `line-strong`
   (1,83:1 на тёмной), пока карточки телефона вообще не были покрашены.
   Ссылка описана один раз, в разделе «Ссылки». */

/* ---- Шов сверки — подпись системы --------------------------------------
   Две колонки сумм и шов между ними. Сошлось — шов сомкнут сплошной линией.
   Не сошлось — шов расходится, и в разрыв встаёт расхождение. Это
   единственное сильное движение системы: оно означает ровно то, что
   показывает. Движений в системе три (шов, пульс скелета, отклик на
   наведение), и каждое сообщает состояние — правило в DESIGN.md, раздел 10. */

.st-seam {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: stretch;
  gap: var(--st-space-4);
  padding: var(--st-space-4);
}

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

.st-seam__side--right {
  align-items: flex-end;
  text-align: right;
}

.st-seam__label {
  font-size: var(--st-text-micro);
  font-weight: var(--st-weight-semibold);
  color: var(--st-ink-faint);
}

.st-seam__source {
  font-size: var(--st-text-caption);
  color: var(--st-ink-muted);
}

.st-seam__rule {
  display: flex;
  flex-direction: column;
  align-items: center;
  min-width: 96px;
}

.st-seam__stub {
  flex: 1 1 auto;
  width: var(--st-border-seam);
  min-height: var(--st-space-3);
  background: var(--st-seam);
}

.st-seam__gap {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  overflow: hidden;
  height: 0;
  transition:
    height var(--st-duration-seam) var(--st-ease-out),
    padding var(--st-duration-seam) var(--st-ease-out);
}

.st-seam--match .st-seam__gap {
  height: 0;
  padding: 0;
}

.st-seam--break .st-seam__gap,
.st-seam--unknown .st-seam__gap {
  height: auto;
  padding: var(--st-space-2) 0;
}

/* Разошедшийся шов красится цветом расхождения, а не гаснет до серого.
   Серые стойки на тёмной теме практически не видны (замерено: #4c4840 на
   #201e1a — контраст 1,6:1), и разрыв читается как пустое место, а не как
   разошедшийся шов. Смысл при этом не теряется: сомкнутый шов — цвета
   акцента, разошедшийся — цвета расхождения. */
.st-seam--break .st-seam__stub {
  background: var(--st-tone-alarm-edge);
}

.st-seam--unknown .st-seam__stub {
  background: var(--st-line-strong);
}

.st-seam__verdict {
  font-size: var(--st-text-micro);
  font-weight: var(--st-weight-semibold);
  letter-spacing: var(--st-tracking-label);
  text-transform: uppercase;
  white-space: nowrap;
  padding: 2px var(--st-space-2);
  border-radius: var(--st-radius-sm);
}

.st-seam--match .st-seam__verdict {
  color: var(--st-tone-done-ink);
  background: var(--st-tone-done-wash);
}

.st-seam--break .st-seam__verdict {
  color: var(--st-tone-alarm-ink);
  background: var(--st-tone-alarm-wash);
}

.st-seam--unknown .st-seam__verdict {
  color: var(--st-ink-faint);
  background: var(--st-surface-sunken);
}

/* Метка «сошлось» сидит верхом на сомкнутом шве. */
.st-seam--match .st-seam__mark {
  position: relative;
  margin: var(--st-space-2) 0;
  background: var(--st-surface);
  z-index: var(--st-z-base);
}

@media (max-width: 640px) {
  .st-seam {
    grid-template-columns: 1fr;
    gap: var(--st-space-2);
  }

  .st-seam__side--right {
    align-items: flex-start;
    text-align: left;
  }

  .st-seam__rule {
    flex-direction: row;
    min-width: 0;
    width: 100%;
  }

  .st-seam__stub {
    width: auto;
    height: var(--st-border-seam);
    min-width: var(--st-space-3);
    min-height: 0;
  }

  .st-seam__gap {
    flex-direction: row;
    height: auto;
    width: 0;
    transition: width var(--st-duration-seam) var(--st-ease-out);
  }

  .st-seam--break .st-seam__gap,
  .st-seam--unknown .st-seam__gap {
    width: auto;
    padding: 0 var(--st-space-2);
  }

  .st-seam--match .st-seam__mark {
    margin: 0 var(--st-space-2);
  }
}

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

.st-figures {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  border: var(--st-border-hairline) solid var(--st-line);
  border-radius: var(--st-radius-md);
  background: var(--st-surface);
  overflow: hidden;
}

.st-figure {
  padding: var(--st-space-4);
  border-right: var(--st-border-hairline) solid var(--st-line-faint);
  display: flex;
  flex-direction: column;
  gap: var(--st-space-1);
  min-width: 0;
}

.st-figure:last-child {
  border-right: 0;
}

.st-figure__label {
  font-size: var(--st-text-micro);
  font-weight: var(--st-weight-semibold);
  color: var(--st-ink-faint);
}

.st-figure__note {
  font-size: var(--st-text-caption);
  color: var(--st-ink-muted);
}

/* Плитки, положенные в панель вплотную к рамке, своей рамки не заводят:
   иначе на экране появляется рамка в рамке, а панель для того и взята, чтобы
   период стоял в её шапке ОДИН раз, а не подписью под каждой плиткой. */
.st-panel__body--flush > .st-figures {
  border: 0;
  border-radius: 0;
}

/* ---- Пары «подпись — значение» ----------------------------------------- */

.st-facts {
  display: grid;
  grid-template-columns: minmax(140px, max-content) 1fr;
}

.st-fact__key {
  padding: var(--st-space-2) var(--st-space-4) var(--st-space-2) 0;
  font-size: var(--st-text-caption);
  color: var(--st-ink-faint);
  border-right: var(--st-border-hairline) solid var(--st-line-faint);
}

.st-fact__value {
  padding: var(--st-space-2) 0 var(--st-space-2) var(--st-space-4);
  min-width: 0;
  overflow-wrap: anywhere;
}

/* ---- Журнал состояний --------------------------------------------------
   Неизменяемый след: время, состояние, источник события. Держится вертикалью
   слева — это то же самое ребро, что у строки реестра.                    */

.st-journal {
  list-style: none;
  margin: 0;
  padding: 0 0 0 var(--st-space-4);
  border-left: var(--st-border-seam) solid var(--st-line);
  display: flex;
  flex-direction: column;
  gap: var(--st-space-3);
}

.st-journal__item {
  position: relative;
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: var(--st-space-1) var(--st-space-3);
  align-items: baseline;
}

.st-journal__item::before {
  content: '';
  position: absolute;
  left: calc(-1 * var(--st-space-4) - var(--st-border-seam));
  top: 0.45em;
  width: var(--st-space-3);
  height: var(--st-border-hairline);
  background: var(--st-line-strong);
}

.st-journal__item--now::before {
  height: var(--st-border-seam);
  background: var(--st-seam);
}

.st-journal__source {
  grid-column: 2;
  font-size: var(--st-text-caption);
  color: var(--st-ink-faint);
}

/* ---- Управление -------------------------------------------------------- */

.st-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--st-space-2);
  align-items: center;
}

.st-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--st-space-2);
  min-height: var(--st-control-height);
  padding: 0 var(--st-space-4);
  border: var(--st-border-hairline) solid var(--st-line-strong);
  border-radius: var(--st-radius-md);
  background: var(--st-surface);
  color: var(--st-ink);
  font: inherit;
  font-size: var(--st-text-lead);
  line-height: 1;
  cursor: pointer;
  white-space: nowrap;
  transition:
    background var(--st-duration-instant) var(--st-ease-out),
    border-color var(--st-duration-instant) var(--st-ease-out);
}

.st-btn:hover {
  background: var(--st-surface-sunken);
  border-color: var(--st-ink-faint);
}

/*
 * Действие, которое на самом деле переход, набирается ссылкой в оболочке
 * кнопки: «Обновить», «Показать все», «К реестру». Правило `.st-main a` дало
 * бы ей подчёркивание и цвет ссылки — то есть кнопку с подчёркнутой надписью.
 * Подчёркивание снимается тем же способом, что с рельса разделов и со знака,
 * а цвет берётся кнопочный. Признак «это переход» никуда не девается: рамка,
 * курсор и адрес в строке состояния остаются.
 */
a.st-btn,
a.st-btn:visited,
a.st-btn:hover {
  text-decoration: none;
  color: var(--st-ink);
}

/*
 * Модификаторы перечислены поимённо и с тем же весом селектора, что и базовое
 * правило выше. Иначе `a.st-btn` (класс + элемент) перебил бы по силе
 * `.st-btn--primary` (один класс), и главное действие набралось бы чернилами
 * по чернильной заливке — то есть стало бы нечитаемым. Ловится это только
 * глазами и только на той единственной кнопке, которая оказалась ссылкой.
 */
a.st-btn--primary,
a.st-btn--primary:visited,
a.st-btn--primary:hover {
  color: var(--st-action-ink);
}

a.st-btn--quiet,
a.st-btn--quiet:visited {
  color: var(--st-ink-muted);
}

a.st-btn--quiet:hover {
  color: var(--st-ink);
}

a.st-btn--danger,
a.st-btn--danger:visited,
a.st-btn--danger:hover {
  color: var(--st-danger);
}

/* Главное действие — чернильная заливка, а не цветная. Цвет остаётся швом. */
.st-btn--primary {
  background: var(--st-action);
  border-color: var(--st-action);
  color: var(--st-action-ink);
  font-weight: var(--st-weight-medium);
}

.st-btn--primary:hover {
  background: var(--st-action-hover);
  border-color: var(--st-action-hover);
}

.st-btn--quiet {
  border-color: transparent;
  background: transparent;
  color: var(--st-ink-muted);
}

.st-btn--quiet:hover {
  background: var(--st-surface-sunken);
  color: var(--st-ink);
}

.st-btn--danger {
  border-color: var(--st-danger);
  color: var(--st-danger);
}

.st-btn--danger:hover {
  background: var(--st-danger-wash);
}

.st-btn[disabled],
.st-btn[aria-disabled='true'] {
  opacity: 0.5;
  cursor: not-allowed;
}

@media (max-width: 640px) {
  .st-btn {
    min-height: var(--st-tap-target);
  }
}

/* Фильтры. Тот же приём, что в рельсе: вертикаль слева, не заливка. */
/* Фильтры прокручиваются, а не обрезаются: на 390 четыре фильтра не влезают,
   и `overflow: hidden` ради скругления углов прятал последний за краем —
   молча, без полосы прокрутки и без единого признака, что он там есть. */
.st-filters {
  display: flex;
  gap: 0;
  border: var(--st-border-hairline) solid var(--st-line);
  border-radius: var(--st-radius-md);
  background: var(--st-surface);
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  scrollbar-width: thin;
}

.st-filter {
  display: inline-flex;
  align-items: center;
  gap: var(--st-space-2);
  min-height: var(--st-control-height);
  padding: 0 var(--st-space-3);
  border: 0;
  border-left: var(--st-border-seam) solid transparent;
  background: transparent;
  color: var(--st-ink-muted);
  font: inherit;
  font-size: var(--st-text-caption);
  cursor: pointer;
  white-space: nowrap;
}

.st-filter:hover {
  background: var(--st-surface-sunken);
  color: var(--st-ink);
}

.st-filter[aria-pressed='true'] {
  border-left-color: var(--st-seam);
  background: var(--st-seam-wash);
  color: var(--st-ink);
  font-weight: var(--st-weight-medium);
}

.st-filter__count {
  font-family: var(--st-font-mono);
  color: var(--st-ink-faint);
}

/* Панель управления реестром: фильтры и поиск над строками. Отдельным
   классом, а не атрибутом style в разметке: на телефоне у неё свои поля, и
   задать их из разметки нечем — медиазапроса там нет. */
.st-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--st-space-3);
  padding: var(--st-space-3) var(--st-space-4);
}

.st-toolbar__search {
  flex: 1 1 240px;
}

/* ФОРМА ФИЛЬТРОВ ОБЯЗАНА УМЕТЬ СЖИМАТЬСЯ, ИНАЧЕ ПРОКРУТКА ВНУТРИ НЕЁ НЕ
   ВКЛЮЧАЕТСЯ. Поймано замером 25.08.2026: на 390 px реестр уезжал вбок на
   74 px, на 375 — на 89. У `.st-filters` прокрутка по горизонтали стояла и
   раньше, но работать ей было негде: фильтры обёрнуты формой (они стали
   отправляться на сервер), форма — элемент гибкой раскладки, а у такого
   элемента `min-width` по умолчанию равен `auto` и не даёт ему стать уже
   своего содержимого. Форма распирала панель, панель — страницу, и вбок
   уезжал ВЕСЬ документ: рельс разделов и легенда тонов при этом тоже
   вылезали за край, хотя со своей прокруткой у них всё было в порядке —
   они мерили ширину уже разъехавшегося документа.

   Правило поэтому про сжатие, а не про прокрутку: прокрутка уже описана там,
   где ей место. Глазами это не находится — на снимке страница выглядит
   обычной, пока не потянешь её вбок. */
.st-toolbar__filters {
  min-width: 0;
  flex: 0 1 auto;
}

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

.st-field__label {
  font-size: var(--st-text-micro);
  font-weight: var(--st-weight-semibold);
  color: var(--st-ink-faint);
}

.st-input {
  min-height: var(--st-control-height);
  padding: 0 var(--st-space-3);
  border: var(--st-border-hairline) solid var(--st-line-strong);
  border-radius: var(--st-radius-md);
  background: var(--st-surface);
  color: var(--st-ink);
  font: inherit;
  font-size: var(--st-text-lead);
}

.st-input::placeholder {
  color: var(--st-ink-faint);
}

.st-input--mono {
  font-family: var(--st-font-mono);
  font-size: var(--st-text-body);
}

/* ---- Форма ------------------------------------------------------------- */

.st-form {
  display: flex;
  flex-direction: column;
  gap: var(--st-space-4);
}

/* Кнопка формы во всю ширину колонки. Не мода: на телефоне это единственная
   цель нажатия на экране, и промахнуться по ней нельзя. */
.st-form__act {
  display: flex;
}

.st-form__act .st-btn {
  width: 100%;
  justify-content: center;
}

.st-form__rules {
  display: flex;
  flex-direction: column;
  gap: var(--st-space-1);
}

/* Требования к паролю стоят ДО отправки, а не после отказа: форма, которая
   принимает пароль и отвечает «слишком короткий», заставляет придумывать
   вслепую второй раз. Список без маркеров — вертикальным штрихом у левого
   края: тем же приёмом, каким в этой системе подаётся всё перечислимое.
   Точек и значков в ней нет. */
.st-rules {
  margin: 0;
  padding: 0 0 0 var(--st-space-3);
  list-style: none;
  border-left: var(--st-border-hairline) solid var(--st-line);
  display: flex;
  flex-direction: column;
  gap: var(--st-space-1);
  font-size: var(--st-text-caption);
  color: var(--st-ink-muted);
}

/* ---- Полоса контекста: ЧЬИ это данные ----------------------------------

   ПОЯВИЛАСЬ ВМЕСТЕ С АДМИНКОЙ (25.08.2026) И ПОЯВИЛАСЬ ПО СЛУЧАЮ. Владелец
   открыл стенд, попал в кабинет партнёра — и не понял, чьи суммы перед ним.
   В кабинете этой полосы нет и не нужно: там партнёр всегда один и он же
   смотрящий. В админке партнёр меняется переходом по ссылке, и вопрос «чьи
   это числа» становится тем единственным, на который экран обязан отвечать,
   не дожидаясь, пока его зададут.

   ЖИВЁТ ВНУТРИ ШАПКИ, А НЕ НАД СОДЕРЖИМЫМ, И ЭТО РЕШЕНИЕ, А НЕ УДОБСТВО
   ВЁРСТКИ. Требование — «полоса не прокручивается вместе с таблицей»;
   выполнить его отдельным липким блоком в содержимом можно только задав ему
   смещение, равное высоте шапки, — а шапка липкая, многострочная и на
   телефоне переносится, то есть её высота не выражается ни одним значением.
   Расхождение на несколько пикселей даёт либо щель, сквозь которую при
   прокрутке видны строки реестра, либо полосу, заехавшую под шапку. Внутри
   шапки вопрос снимается: полоса стоит там же, где рельс, и держится тем же
   `position: sticky`, что и он.

   ШОВ ЛИНИЕЙ У ЛЕВОГО КРАЯ И НИКОГДА ЗАЛИВКОЙ — правило системы, тот же
   приём, которым помечены текущий раздел рельса, тон состояния и край
   карточки. Плоскость взята утопленная: полоса обязана оставаться
   непрозрачной — сквозь прозрачную при прокрутке просвечивают строки
   реестра, и подпись становится нечитаемой ровно в тот момент, когда она
   нужна. */
.st-context {
  max-width: var(--st-content-max);
  margin: 0 auto;
  padding: var(--st-space-2) var(--st-gutter);
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--st-space-2) var(--st-space-4);
  background: var(--st-surface-sunken);
  border-top: var(--st-border-hairline) solid var(--st-line);
  font-size: var(--st-text-caption);
  color: var(--st-ink-muted);
}

/* Шов — отдельной полосой у левого края полотна, а не рамкой вокруг: полоса
   тянется во всю ширину шапки, и рамка вокруг неё была бы рамкой вокруг
   экрана. */
.st-context::before {
  content: '';
  align-self: stretch;
  width: var(--st-border-seam);
  background: var(--st-seam);
}

.st-context__whose {
  color: var(--st-ink);
  font-weight: var(--st-weight-medium);
}

/* Код партнёра — моноширинным, как всякий идентификатор в системе: по нему
   сверяют адрес в строке браузера с тем, что написано на экране. */
.st-context__code {
  font-family: var(--st-font-mono);
  color: var(--st-seam-ink);
}

/* Выход из партнёра — ЯВНАЯ ССЫЛКА, а не кнопка «назад» браузера. «Назад»
   после трёх переходов внутри одного партнёра возвращает не туда, откуда
   пришли, и человек выясняет это уже глядя в чужие числа. */
.st-context__out {
  margin-left: auto;
}

/* ---- Ворота: вход и установка пароля -----------------------------------
   Узкая колонка, ПРИЖАТАЯ К ВЕРХУ, а не отцентрованная по высоте: у
   отцентрованной формы появление сообщения об отказе сдвигает поля вниз —
   под уже занесённым пальцем. Экран входа человек видит первым, и он же
   единственный, где на странице нет ни одной строки данных. */
.st-gate {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: var(--st-space-4);
}

/* Шов у левого края панели. Тот же приём, которым в системе помечено всё
   значимое: текущий раздел, тон состояния, край карточки на телефоне. Акцент
   линией и никогда заливкой. */
.st-gate > .st-panel {
  border-left: var(--st-border-seam) solid var(--st-seam);
}

/* ---- Сообщение --------------------------------------------------------- */

.st-notice {
  display: flex;
  flex-direction: column;
  gap: var(--st-space-1);
  padding: var(--st-space-3) var(--st-space-4);
  border: var(--st-border-hairline) solid var(--st-line);
  border-left: var(--st-border-seam) solid var(--st-line-strong);
  border-radius: var(--st-radius-md);
  background: var(--st-surface);
}

.st-notice__title {
  font-weight: var(--st-weight-semibold);
  margin: 0;
  font-size: var(--st-text-lead);
}

.st-notice__text {
  margin: 0;
  color: var(--st-ink-muted);
  max-width: 76ch;
}

.st-notice--alarm {
  border-left-color: var(--st-tone-alarm-edge);
  background: var(--st-tone-alarm-wash);
}

.st-notice--act {
  border-left-color: var(--st-tone-act-edge);
  background: var(--st-tone-act-wash);
}

.st-notice--seam {
  border-left-color: var(--st-seam);
  background: var(--st-seam-wash);
}

/* ---- Четыре состояния экрана ------------------------------------------
   Загрузка, пусто, отказ, ограничение. Экран без объяснения — дефект.    */

/* ТОН СОСТОЯНИЯ — ВЕРТИКАЛЬНЫЙ ШТРИХ, А НЕ ЗНАЧОК. Над заголовком стояла
   марка из трёх полосок, перекрашенная по тону. Она не добавляла ничего к
   тому, что уже сказано заголовком и текстом, то есть работала значком —
   а значков в системе нет ни рисованных, ни юникодных. Штрих у левого края
   говорит то же самое тем же приёмом, каким тон подаётся везде: в состоянии
   заявки, в строке реестра, в карточке на телефоне. */

.st-screen-state {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--st-space-3);
  padding: var(--st-space-7) var(--st-space-4);
  border-left: var(--st-border-edge) solid var(--st-line-strong);
  max-width: 62ch;
}

.st-screen-state__title {
  font-family: var(--st-font-display);
  font-size: var(--st-text-section);
  font-weight: var(--st-weight-semibold);
  margin: 0;
}

.st-screen-state__text {
  margin: 0;
  color: var(--st-ink-muted);
}

.st-screen-state--error {
  border-left-color: var(--st-tone-alarm-edge);
}

.st-screen-state--limited {
  border-left-color: var(--st-tone-act-edge);
}

/* Скелет загрузки: полосы той же ширины, что реальные колонки. Пульсация
   одна на всю систему и гасится при prefers-reduced-motion. */
.st-skeleton {
  display: block;
  height: 0.85em;
  border-radius: var(--st-radius-sm);
  background: var(--st-line-faint);
  animation: st-pulse 1.4s ease-in-out infinite;
}

@keyframes st-pulse {
  0%,
  100% {
    opacity: 1;
  }

  50% {
    opacity: 0.45;
  }
}

/* ---- Полоса лимита -----------------------------------------------------
   ДВА ПРАВИЛА СИСТЕМЫ, КОТОРЫМ ОНА ОБЯЗАНА ПОДЧИНЯТЬСЯ, И РАНЬШЕ НАРУШАЛА.
   Радиусы здесь 2 и 4: 999 px — язык потребительских приложений, и одна
   пилюля посреди ведомости выдаёт систему целиком. Акцент появляется линиями
   и никогда заливкой: штемпельный фиолетовый залитой полосой перестаёт быть
   оттиском и становится индикатором заряда.

   Отсюда устройство. Заполнение набрано НЕЙТРАЛЬНОЙ линией, а фиолетовый
   оставлен ровно одному штриху — отметке порога, за которым полоса меняет
   тон. Тона `act` и `alarm` заливают полосу законно: `tone-*-edge` в этой
   системе и заведены для штрихов и полос, текстом ими не набирают.        */

.st-meter {
  /* Порог локальной переменной, а не числом в двух местах: полоса меняет тон
     на тех же 80 %, что и отметка (см. `meter()` в кабинете). */
  --st-meter-threshold: 80%;
  position: relative;
  display: block;
  height: var(--st-space-2);
  background: var(--st-surface-sunken);
  border: var(--st-border-hairline) solid var(--st-line);
  border-radius: var(--st-radius-sm);
  overflow: hidden;
}

/* Заполнение — приглушённая тушь, а не линия разграфки: `line-strong` на
   утопленной дорожке даёт 2,1:1 в светлой теме и 1,9:1 в тёмной, то есть
   полоса читается только на просвет. Замерено. `ink-faint` держит 4,5:1
   как текст на своей поверхности и здесь тем более. */
.st-meter__fill {
  display: block;
  height: 100%;
  background: var(--st-ink-faint);
}

.st-meter--act .st-meter__fill {
  background: var(--st-tone-act-edge);
}

.st-meter--alarm .st-meter__fill {
  background: var(--st-tone-alarm-edge);
}

/* Порог. Единственный акцент на полосе — и он линия. Стоит на 80 %: там
   полоса меняет тон, и там же партнёру пора планировать расширение лимита,
   а не узнавать о нём из кода 429. */
.st-meter::after {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  left: var(--st-meter-threshold);
  width: var(--st-border-seam);
  background: var(--st-seam);
}

/* ---- Мелочи ------------------------------------------------------------ */

.st-hint {
  font-size: var(--st-text-caption);
  color: var(--st-ink-faint);
  margin: 0;
}

.st-sep {
  border: 0;
  border-top: var(--st-border-hairline) solid var(--st-line-faint);
  margin: var(--st-space-4) 0;
}

.st-secret {
  display: flex;
  align-items: center;
  gap: var(--st-space-2);
  padding: var(--st-space-2) var(--st-space-3);
  border: var(--st-border-hairline) dashed var(--st-line-strong);
  border-radius: var(--st-radius-md);
  background: var(--st-surface-sunken);
  font-family: var(--st-font-mono);
  font-size: var(--st-text-body);
  overflow-wrap: anywhere;
}

/* ---- Период ------------------------------------------------------------
   Пресеты и две даты. Пресеты — те же `.st-filters`, что у реестра: приём
   один, и заводить рядом второй вид переключателя незачем. Отдельного здесь
   ровно две вещи — поля дат и кнопка, и им нужна своя раскладка.

   ПОРЯДОК В РАЗМЕТКЕ: СНАЧАЛА ПРЕСЕТЫ, ПОТОМ ДАТЫ. На телефоне это порядок
   сверху вниз, и он же порядок надобности: «7 дней» нажимают в девяти
   случаях из десяти, руками даты набирают в десятом.                     */

.st-period {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--st-space-3);
  padding: var(--st-space-3) var(--st-space-4);
}

/* Та же беда, что у `.st-toolbar__filters`, и то же лечение: форма — элемент
   гибкой раскладки, её `min-width` по умолчанию `auto`, и без явного нуля она
   распирает панель, а панель — документ. Проверяется замером ширины на 390,
   а не глазами: на снимке разъехавшаяся страница выглядит обычной. */
.st-period__presets {
  min-width: 0;
  flex: 0 1 auto;
}

.st-period__range {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--st-space-2);
  min-width: 0;
}

/* Поле даты своей ширины не имеет: в Chrome оно раздувается под формат, в
   Firefox сжимается в ничто. Задаём явно — иначе два поля рядом на 390 либо
   уезжают вбок, либо становятся нечитаемыми. */
.st-input--date {
  min-width: 9.5rem;
  font-family: var(--st-font-mono);
  font-size: var(--st-text-body);
}

/* ---- Ряд по суткам ------------------------------------------------------
   Столбики без осей, без сетки и без подписей у каждого — это не график для
   разглядывания, а форма ряда: где густо, где пусто, где всплеск. Числа
   читают в таблице под ним, и она настоящая, а не подпись к картинке.

   СТОЛБИК ПУСТЫХ СУТОК ОСТАЁТСЯ ВИДЕН волосяной чертой у основания. Совсем
   нулевой высоты он был бы неотличим от промежутка между столбиками, и месяц
   с тремя рабочими днями выглядел бы как три дня подряд.                  */

.st-series {
  display: flex;
  flex-direction: column;
  gap: var(--st-space-2);
  padding: var(--st-space-4);
}

.st-series__bars {
  display: flex;
  align-items: flex-end;
  gap: 2px;
  height: 96px;
  border-bottom: var(--st-border-hairline) solid var(--st-line);
}

.st-series__bar {
  flex: 1 1 0;
  /* Ниже волоска столбик не опускается: пустые сутки обязаны быть видны. */
  min-height: var(--st-border-hairline);
  min-width: 2px;
  background: var(--st-ink-faint);
  border-radius: var(--st-radius-sm) var(--st-radius-sm) 0 0;
}

/* Наибольшие сутки периода набраны цветом чернил, а не акцентом: акцент в
   системе — линии, и заливать им столбик значило бы завести исключение. */
.st-series__bar--peak {
  background: var(--st-ink-muted);
}

.st-series__scale {
  display: flex;
  justify-content: space-between;
  font-family: var(--st-font-mono);
  font-size: var(--st-text-micro);
  color: var(--st-ink-faint);
}

/* Таблица ряда под графиком: свёрнута, потому что тридцать строк чисел над
   реестром заявок отжали бы его за край экрана. Развёрнутая — это и есть
   доступный вид того же ряда, поэтому она настоящая таблица, а не картинка. */
.st-series-table > summary {
  cursor: pointer;
  padding: var(--st-space-2) var(--st-space-4);
  font-size: var(--st-text-caption);
  color: var(--st-ink-muted);
}

/* ---- Телефон -----------------------------------------------------------
   Реестр разворачивается в карточки: горизонтальная прокрутка таблицы на
   телефоне — это способ спрятать половину данных.                        */

@media (max-width: 640px) {
  /* ПЕРВАЯ СТРОКА ДАННЫХ ОБЯЗАНА ПОПАДАТЬ В ПЕРВЫЙ ЭКРАН.
     Замерено до правки на 375: первая заявка начиналась на 589-м пикселе —
     бухгалтер открывал раздел заявок и видел что угодно, кроме заявок. Уходило
     это на служебное: подзаголовок в четыре строки, легенда тонов в три,
     переключатель плотности, который на телефоне вообще ничего не
     переключает, и поля рабочего стола.

     Ничего нужного за кнопку «показать фильтры» не спрятано: фильтры — то,
     чем реестр разбирают каждый раз, и они остались на месте. Ужато ровно
     служебное: заголовок ближе к данным, подзаголовок набран строкой факта,
     легенда идёт одним прокручиваемым рядом вместо трёх, а мёртвый на
     телефоне переключатель убран совсем.                                   */
  .st-main {
    padding: var(--st-space-3) var(--st-gutter-compact) var(--st-space-7);
  }

  .st-page-head {
    margin-bottom: var(--st-space-3);
  }

  /* Подзаголовок на телефоне — служебная строка факта, а не абзац. */
  .st-subtitle {
    font-size: var(--st-text-caption);
  }

  .st-stack {
    gap: var(--st-space-4);
    margin-top: var(--st-space-4);
  }

  .st-panel__head {
    gap: var(--st-space-2);
    padding: var(--st-space-2) var(--st-space-3);
  }

  .st-toolbar {
    gap: var(--st-space-2);
    padding: var(--st-space-2) var(--st-space-3);
  }

  .st-period,
  .st-series {
    gap: var(--st-space-2);
    padding: var(--st-space-2) var(--st-space-3);
  }

  /* СВОДНЫЕ ВЕЛИЧИНЫ — В ОДНУ КОЛОНКУ, И ЭТО НЕ ПРО ПРОСТОР.

     Поймано замером на 375 после появления периодов: плитка отдавала числу
     150 px, а «126 810|00 ₽» занимает 172 — рубль уезжал за край плитки
     МОЛЧА. Документ при этом вбок не выезжал ни на пиксель, и на снимке всё
     выглядело обычным: обрезанное внутри своего ящика глазами не находится.
     А за сутки числа были короче, поэтому раньше беды и не было — период её
     не создал, он её показал.

     Уменьшить начертание нельзя: сумма за месяц бывает любой длины, и всякий
     подобранный размер обрежется на следующем разряде. Одна колонка снимает
     вопрос целиком: на 375 числу остаётся 317 px, и это не «пока хватает», а
     «хватает на порядок больше, чем бывает». Цена — вертикаль, и на телефоне
     она правильная: четыре величины читают одну под другой, а не парами.

     Обрезанная сумма в кабинете, где смотрят чужие деньги, — это неверная
     сумма, а не мелкое оформление. */
  .st-figures {
    grid-template-columns: 1fr;
  }

  .st-figure {
    border-right: 0;
    border-bottom: var(--st-border-hairline) solid var(--st-line-faint);
    padding: var(--st-space-3) var(--st-space-4);
  }

  .st-figure:last-child {
    border-bottom: 0;
  }

  /* ДВЕ ДАТЫ В ДВЕ РАВНЫЕ КОЛОНКИ, КНОПКА ПОД НИМИ ВО ВСЮ ШИРИНУ.

     Гибкой раскладкой это не выходит, и найдено это снимком на 390: три
     элемента в ряд делили ширину так, что у полей оставалось по 120 px, и
     родное поле даты обрезало год — `08/25/202`. Обрезанный год в поле, по
     которому задают отчётный период, — это не косметика: человек не видит, за
     что смотрит, и правит вслепую.

     Сетка делит ровно пополам и не зависит от того, сколько места запросила
     кнопка; кнопка переезжает на свою строку, где ей и место — она единственная
     цель нажатия в этой полосе. */
  .st-period__range {
    width: 100%;
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--st-space-2);
  }

  .st-period__range .st-btn {
    grid-column: 1 / -1;
  }

  .st-input--date {
    min-width: 0;
    font-size: var(--st-text-caption);
  }

  /* График ниже: на телефоне за ним сразу идут числа, и сотня пикселей
     картинки отжимала бы их под сгиб. */
  .st-series__bars {
    height: 64px;
  }

  /* Цель нажатия у поля ввода — 44 px, как у кнопок. Правило доктрины
     (раздел 7) держалось только для `.st-btn`, а поле оставалось 30 px:
     это единственный способ найти заявку на узком экране, и палец по нему
     промахивается. Правило стоит ЗДЕСЬ, а не рядом с кнопочным: `.st-input`
     объявлен ниже по файлу, и медиазапрос с равной силой обязан идти после
     него, иначе побеждает базовые 30 px. */
  .st-input {
    min-height: var(--st-tap-target);
  }

  /* Легенда в один ряд с прокруткой внутри своей области — тот же приём, что
     у фильтров. Три строки цветных слов над реестром отжимали первую заявку
     на 68 px и читались как содержание, хотя это справочник. */
  .st-legend {
    flex-wrap: nowrap;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    /* Полоса прокрутки видимая, как у фильтров: спрятанная молча уносит за
       край последние тона, и человек не знает, что они там есть. */
    scrollbar-width: thin;
    width: 100%;
    gap: var(--st-space-3);
  }

  /* Переключатель плотности на телефоне не переключает НИЧЕГО: он меняет
     высоту строки таблицы, а таблица здесь скрыта — реестр развёрнут в
     карточки. Кнопка, которая ничего не делает, хуже отсутствующей. */
  [data-density-toggle] {
    display: none;
  }

  .st-header__top,
  .st-rail__inner {
    padding-left: var(--st-gutter-compact);
    padding-right: var(--st-gutter-compact);
  }

  .st-rail__inner {
    overflow-x: auto;
    scrollbar-width: none;
  }

  /* Шапка на телефоне уходит НА ДВЕ СТРОКИ, а не сжимается. Знак, метка
     контура, имя вошедшего, смена темы и выход не помещаются в одну строку
     даже на 390, а горизонтальная прокрутка страницы в этой системе
     запрещена. Распорка растянута на всю ширину — перенос получается
     предсказуемый, а не «где придётся». */
  .st-header__top {
    flex-wrap: wrap;
    row-gap: var(--st-space-2);
    gap: var(--st-space-2);
  }

  /* Перенос нужен только шапке кабинета: там на второй строке стоят имя
     вошедшего, смена темы и выход. На воротах второй строке достаться нечему
     — пустая строка под знаком выглядела бы недогруженной страницей. */
  .st-header__top:not(.st-header__top--gate) .st-header__spacer {
    flex-basis: 100%;
    height: 0;
  }

  /* Имя партнёра уходит целиком: человек, который открыл СВОЙ кабинет, знает,
     чей он и по какому договору. Имя ВОШЕДШЕГО остаётся вместе с ролью — оно
     отвечает на другой вопрос, и ответ на него нужен перед каждым действием,
     а не один раз при входе. */
  .st-org--partner {
    display: none;
  }

  .st-org {
    max-width: 40vw;
  }

  /* Разделяющая линия снимается вместе с именем партнёра: сам элемент
     остаётся в разметке, поэтому соседский селектор продолжает попадать, и
     на телефоне у левого края висела вертикальная черта ни при чём. */
  .st-org--viewer {
    margin-right: auto;
  }

  /* Разделяющая линия снимается вместе с именем партнёра. Селектор повторён
     соседским НЕ ради красоты: базовое правило стоит на `.st-org--partner +
     .st-org--viewer` (сила 0,2,0), и `.st-org--viewer` (0,1,0) его не
     перебивает — первая редакция этой правки не сработала ровно поэтому, и
     на телефоне у левого края осталась вертикальная черта ни при чём.
     Сам партнёр из разметки не исчезает, только скрыт, поэтому соседский
     селектор продолжает попадать. */
  .st-org--partner + .st-org--viewer {
    border-left: 0;
    padding-left: 0;
  }

  .st-facts {
    grid-template-columns: 1fr;
  }

  .st-fact__key {
    border-right: 0;
    padding-bottom: 0;
  }

  .st-fact__value {
    padding-left: 0;
    padding-top: 0;
    border-bottom: var(--st-border-hairline) solid var(--st-line-faint);
    padding-bottom: var(--st-space-2);
  }

  .st-figure {
    border-right: 0;
    border-bottom: var(--st-border-hairline) solid var(--st-line-faint);
  }

  .st-cards {
    display: flex;
    flex-direction: column;
  }

  .st-card {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: var(--st-space-1) var(--st-space-3);
    padding: var(--st-space-3) var(--st-space-3) var(--st-space-3) var(--st-space-4);
    border-bottom: var(--st-border-hairline) solid var(--st-line-faint);
    border-left: var(--st-border-edge) solid transparent;
  }

  .st-card--alarm {
    border-left-color: var(--st-tone-alarm-edge);
  }

  .st-card--act {
    border-left-color: var(--st-tone-act-edge);
  }

  .st-card__key {
    font-size: var(--st-text-caption);
    color: var(--st-ink-faint);
  }

  .st-card__head {
    grid-column: 1 / -1;
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: var(--st-space-3);
    margin-bottom: var(--st-space-1);
  }
}

@media (min-width: 641px) {
  .st-cards {
    display: none;
  }
}

@media (max-width: 640px) {
  .st-table-wrap {
    display: none;
  }
}
