/* Запасной шрифт из статики: файлы в fonts, сведения и метрики в fonts/SOURCES.md. Метрики подогнаны под Segoe UI,
   поэтому строки и отступы совпадают с Windows. Начертания 500 у набора нет: запрос 500 берёт 400. */
@font-face {
  font-family: "MDM PT Sans";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  size-adjust: 105.6%;
  ascent-override: 102.2%;
  descent-override: 23.8%;
  line-gap-override: 0%;
  unicode-range: U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116;
  src: url("/static/fonts/pt-sans-cyrillic-400.woff2") format("woff2");
}
@font-face {
  font-family: "MDM PT Sans";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  size-adjust: 105.6%;
  ascent-override: 102.2%;
  descent-override: 23.8%;
  line-gap-override: 0%;
  unicode-range: U+0460-052F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F;
  src: url("/static/fonts/pt-sans-cyrillic-ext-400.woff2") format("woff2");
}
@font-face {
  font-family: "MDM PT Sans";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  size-adjust: 105.6%;
  ascent-override: 102.2%;
  descent-override: 23.8%;
  line-gap-override: 0%;
  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;
  src: url("/static/fonts/pt-sans-latin-ext-400.woff2") format("woff2");
}
@font-face {
  font-family: "MDM PT Sans";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  size-adjust: 105.6%;
  ascent-override: 102.2%;
  descent-override: 23.8%;
  line-gap-override: 0%;
  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;
  src: url("/static/fonts/pt-sans-latin-400.woff2") format("woff2");
}
/* Оформление плоское: светлые поверхности, тонкие рамки, два начертания (400 и
   500), скругление 12 у карточек и 8 у элементов управления. Цвета и скругления
   экраны берут только из этих переменных. */
:root {
  --control-h: 36px;
  --ink: #263044;
  --muted: #5e687d;
  --line: #e1e5ec;
  --line-strong: #cbd2dc;
  --bg: #f6f7fa;
  --card: #fff;
  --tile: #eef0f5;
  /* Акцент темнее, чем просит белая карточка: ссылка стоит и на плитке, и на
     мягкой подложке, и там её контраст с фоном обязан остаться не ниже 4,5:1. */
  --primary: #3457c5;
  --primary-hover: #2b49a8;
  --primary-soft: #eaeefb;
  --warn: #7a4e0c;
  --warn-bg: #faf1dc;
  --bad: #8b2942;
  --bad-bg: #f8e8ed;
  --ok: #1d6b4c;
  --ok-bg: #e4f3ec;
  --radius: 8px;
  --radius-card: 12px;
  --gap-inline: 6px;
  --gap-name: 8px;
  --gap-tag: 4px;
  /* Промежуток между шапкой страницы и первым блоком содержимого (руководство, раздел 0, п. 33). В низком окне свой. */
  --head-gap: 16px;
  --head-gap-low: 8px;
  /* Пределы ширины объектов (руководство, раздел 0, п. 34). Карточка и плитка не растягиваются на весь экран:
     сетка раздела берёт колонки этих ширин, пустое место справа - фон страницы. Значения только здесь. */
  --card-min: 280px;
  --card-max: 380px;
  --tile-min: 170px;
  --tile-max: 240px;
  --block-max: 960px;
  --settings-max: 1280px;
  /* Подчёркивание ссылок (руководство, раздел 0, п. 29): отступ от строки и толщина, чтобы ножки букв не резались. */
  --underline-offset: 0.2em;
  --underline-thickness: 1px;
  --font-mono: ui-monospace, "Cascadia Mono", Consolas, monospace;
  /* Боковое меню разделов: поверхность, правая линия, текст пункта, наведение. */
  --side-bg: #f0f1f4;
  --side-line: #d8dbe2;
  --nav-ink: #3a4254;
  --nav-ink-strong: #141a26;
  --nav-hover: #e6e9ef;
  --side-width: 256px;
  --side-rail: 64px;
  /* Текст на цветной кнопке, наведение на строку, мягкие линии и подложки. */
  --on-fill: #fff;
  --row-hover: #f8f9fc;
  --line-soft: #eceff4;
  --muted-bg: #eceef3;
  --retired-bg: #fafbfd;
  /* Рамки цветных плашек и средний тон предупреждения для точек и значков. */
  --primary-line: #c3cef0;
  --ok-line: #b7e0c8;
  --warn-line: #e3cf9a;
  --warn-mid: #c08a1e;
  /* Тени только у всплывающих слоёв: меню, подсказка, окно. */
  --shadow-menu: 0 2px 8px rgb(0 0 0 / 18%);
  --shadow-pop: 0 6px 20px rgb(0 0 0 / 8%);
  --shadow-dialog: 0 8px 20px rgb(20 28 46 / 12%);
  --backdrop: rgb(20 28 46 / 40%);
}
* { box-sizing: border-box; }
body {
  margin: 0;
  font: 15px/1.55 "Segoe UI Variable Text", "Segoe UI", "MDM PT Sans", system-ui, -apple-system, "Helvetica Neue", Arial, sans-serif;
  color: var(--ink);
  background: var(--bg);
}
strong, b { font-weight: 500; }
a {
  color: var(--primary);
  text-underline-offset: var(--underline-offset);
  text-decoration-thickness: var(--underline-thickness);
  text-decoration-skip-ink: auto;
}
/* Каркас страницы: меню разделов слева на всю высоту окна, рабочая область
   занимает остальную ширину. Широкой таблице и сравнению записей нужна вся
   ширина экрана, поэтому предела ширины у рабочей области нет. */
.has-side { display: grid; grid-template-columns: auto minmax(0, 1fr); min-height: 100vh; }
/* Рабочая область - контейнер: раскладка экрана считается от её ширины. От
   ширины окна считать нельзя: меню занимает разную ширину. */
.page { min-width: 0; container: page / inline-size; }
.content-width { margin-inline: 32px; }
/* Рамка фокуса нужна тому, кто идёт с клавиатуры, и не нужна после щелчка мышью. */
a:focus-visible, button:focus-visible, [tabindex]:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}
/* У поля ввода рамка фокуса прилегает к границе и граница берёт цвет акцента: зазор с серой линией внутри давал двойную рамку. */
input:focus-visible, select:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 0;
  border-color: var(--primary);
}
textarea:focus-visible {
  outline-offset: 0;
  border-color: var(--primary);
}
/* Страница без меню (вход): полоса с названием платформы. */
.top { background: var(--side-bg); border-bottom: 1px solid var(--side-line); }
.header-inner { display: flex; align-items: center; min-height: 48px; margin-inline: 32px; }
.brand { color: var(--ink); text-decoration: none; font-size: 16px; font-weight: 500; white-space: nowrap; }
.side {
  position: sticky; top: 0; align-self: start; z-index: 10;
  display: flex; flex-direction: column; width: var(--side-width); height: 100vh;
  padding: 12px 10px; overflow-y: auto; scrollbar-width: thin; font-size: 14px;
  background: var(--side-bg); border-right: 1px solid var(--side-line);
}
.side-head { display: flex; align-items: center; min-height: 36px; padding: 0 10px 8px; }
.side-body { display: flex; flex: 1; flex-direction: column; min-height: 0; }
.side nav { display: flex; flex-direction: column; gap: 1px; }
.side-foot { display: flex; flex-direction: column; gap: 1px; margin-top: auto; padding-top: 12px; }
/* Пункт меню - цель указателя, и она не меньше 24 пикселей по обеим сторонам. */
.side-link {
  display: flex; align-items: center; gap: 10px; width: 100%; min-height: 32px; padding: 4px 10px;
  border: 0; border-radius: var(--radius); background: none; color: var(--nav-ink);
  font: inherit; text-align: left; text-decoration: none; white-space: nowrap; cursor: pointer;
}
.side-link:hover, .nav-menu.is-open > .side-link { background: var(--nav-hover); color: var(--nav-ink-strong); }
/* Открытый раздел выделен цветом акцента и подложкой: место на платформе видно без чтения заголовка. */
.side-link[aria-current="page"] { background: var(--primary-soft); color: var(--primary); font-weight: 500; }
.btn-icon.nav { flex: none; width: 18px; height: 18px; }
.side-text { overflow: hidden; text-overflow: ellipsis; }
.side-count {
  margin-left: auto; padding: 0 7px; border-radius: 9px; background: var(--card); color: var(--muted);
  font-size: 12px; font-weight: 400; font-variant-numeric: tabular-nums;
}
/* Отметка отказа у раздела и состояние выдачи внизу меню: точка цвета смысла.
   Смысл повторён словами в надписи или подсказке, цвет не единственный знак. */
.side-dot { flex: none; width: 8px; height: 8px; margin-left: auto; border-radius: 50%; background: var(--bad); }
.side-mark { flex: none; width: 8px; height: 8px; margin: 0 5px; border-radius: 50%; background: var(--line-strong); }
.side-release { color: var(--muted); font-size: 13px; }
.side-release.is-current .side-mark { background: var(--ok); }
.side-release.is-behind .side-mark { background: var(--warn-mid); }
.side-group { margin: 10px 0 2px; padding: 0 10px; color: var(--muted); font-size: 12px; }
/* Черта над группой «Инструменты»: группа стоит внизу меню, отдельно от рабочих разделов. */
.side-group-rule { margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--side-line); }
.nav-menu { position: relative; }
/* Панель профиля закреплена относительно окна: меню прокручивается само, и
   всплывающий слой внутри него был бы обрезан по ширине полосы значков. */
.nav-popup {
  display: none; position: fixed; z-index: 20; left: 10px; bottom: 56px; min-width: 200px;
  padding: 4px 0; background: var(--card); border-radius: var(--radius);
  box-shadow: var(--shadow-menu);
}
.nav-menu.is-open > .nav-popup { display: block; }
.nav-popup a, .nav-popup button {
  display: block; width: 100%; padding: 8px 16px; white-space: nowrap;
  color: var(--nav-ink); text-decoration: none; text-align: left;
  background: none; border: 0; font: inherit; cursor: pointer;
}
.nav-popup a:hover, .nav-popup a[aria-current="page"],
.nav-popup button:hover { color: var(--nav-ink-strong); background: var(--nav-hover); }
.nav-toggle { display: none; }
/* Строка пользователя: правый верхний угол рабочей области, имя и роль текущей
   учётной записи, меню раскрывается вниз под кнопкой и не перекрывает меню разделов. */
.user-bar { background: var(--side-bg); border-bottom: 1px solid var(--side-line); }
.user-bar .content-width { display: flex; justify-content: flex-end; align-items: center; min-height: 52px; }
.user-menu { position: relative; }
.user-trigger {
  display: inline-flex; align-items: center; gap: 8px; height: var(--control-h); box-sizing: border-box;
  padding: 0 10px; border: 0; border-radius: var(--radius); background: none; color: var(--ink);
  font: inherit; font-size: 13px; cursor: pointer;
}
.user-trigger:hover, .user-menu.is-open > .user-trigger, .user-trigger[aria-current="page"] { background: var(--nav-hover); }
.user-name { font-weight: 500; }
.user-role { color: var(--muted); }
.user-chevron { color: var(--muted); transition: transform .12s ease; }
.user-menu.is-open .user-chevron { transform: rotate(180deg); }
.nav-popup.user-popup { position: absolute; left: auto; right: 0; top: calc(100% + 4px); bottom: auto; box-shadow: var(--shadow-menu); background: var(--card); padding: 4px 0; }
/* Невысокое окно ноутбука: пункты меню плотнее, иначе меню администратора не
   помещается, получает свою полосу прокрутки и обрезает длинные названия. */
@media (max-height: 860px) and (min-width: 761px) {
  .side { padding-block: 8px; }
  .side-link { min-height: 28px; padding-block: 2px; }
  .side-group { margin: 6px 0 0; }
  .side-foot { padding-top: 6px; }
}
/* Свёрнутое меню - полоса значков. Надписи остаются в разметке для чтения с
   экрана и видны подсказкой при наведении. */
@media (min-width: 761px) {
  :is([data-side="collapsed"], .side-narrow) .side { width: var(--side-rail); }
  :is([data-side="collapsed"], .side-narrow) .side-head { justify-content: center; padding-inline: 0; }
  :is([data-side="collapsed"], .side-narrow) .side-link { justify-content: center; padding-inline: 0; }
  :is([data-side="collapsed"], .side-narrow) :is(.side-text, .side-count) {
    position: absolute; width: 1px; height: 1px; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap;
  }
  /* В полосе значков отметка отказа стоит в углу значка раздела. */
  :is([data-side="collapsed"], .side-narrow) .side-link { position: relative; }
  :is([data-side="collapsed"], .side-narrow) .side-dot { position: absolute; top: 5px; right: 14px; margin: 0; }
  :is([data-side="collapsed"], .side-narrow) .side-group {
    height: 1px; margin: 8px 6px; padding: 0; overflow: hidden; background: var(--side-line); font-size: 0;
  }
  :is([data-side="collapsed"], .side-narrow) .side-group-rule { border-top: 0; padding-top: 0; }
}
.banner {
  background: var(--warn-bg);
  color: var(--warn);
  padding: 8px 0;
  font-size: 13px;
  border-bottom: 1px solid var(--warn-line);
}
/* Полоса под шапкой говорит цветом, что это: предупреждение, сведение или
   принятое действие. Одинаковый цвет приучает не читать ни одно из них. */
.banner-info { background: var(--muted-bg); color: var(--muted); border-bottom-color: var(--line); }
.banner-ok { background: var(--ok-bg); color: var(--ok); border-bottom-color: var(--ok-line); }
/* Всплывающее сообщение: поверх угла экрана, вёрстку не сдвигает. */
.banner-toast { position: fixed; right: 20px; bottom: 20px; z-index: 40; max-width: min(420px, calc(100vw - 40px)); padding: 10px 14px; border: 1px solid var(--ok-line); border-radius: var(--radius-card); box-shadow: var(--shadow-pop); cursor: pointer; }
.banner-toast[hidden] { display: none; }
.banner-toast .content-width { padding: 0; max-width: none; }
main { margin-inline: 32px; padding: 28px 0 56px; }
/* Порядок очереди, причина решения и переход к следующей записи. */
.decide-comment { display: flex; flex-direction: column; gap: 4px; margin: 12px 0 0; font-size: 13px; color: var(--muted); }
.decide-comment textarea { width: 100%; resize: vertical; }
.decide-advance { font-size: 13px; color: var(--muted); }
/* Отметка «в работе» на карточке: кто взял запись и действие с отметкой. */
.claim-line { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 10px; margin: 0; }
/* Сводка перед публикацией: что накопилось после действующего выпуска. */
.pending-list { margin: 4px 0 0; padding-left: 18px; font-size: 13px; }
/* Протокол загрузки файла: подтверждение стоит между сводкой и строками. */
.protocol-confirm { margin: 12px 0 16px; }
.protocol-confirm p { margin: 0; }
.protocol-confirm .card-actions { display: flex; flex-wrap: wrap; gap: 8px; }
.protocol-confirm form { margin: 0; }
/* Дашборд идёт по ходу данных: что пришло, что связано, что отдаём. */
.dash-section { margin: 0 0 24px; }
/* Абзац раздела не тянется строкой на всю ширину рабочей области: длинную строку трудно читать.
   Сообщение «пусто» (`.empty-state`) - плашка с фоном: ограничение длины строки к ней не относится, она во всю ширину блока. */
.dash-section > p:not(.empty-state) { max-width: 100ch; }
.dash-section-head { display: flex; align-items: baseline; flex-wrap: wrap; gap: 6px 12px; margin: 0 0 10px; }
.dash-section-head h2 { margin: 0; }
.dash-sources td { vertical-align: middle; }
.dash-sources .data-date { font-weight: 500; white-space: nowrap; }
.link-bar { display: flex; height: 8px; overflow: hidden; border-radius: 4px; background: var(--line); }
.link-bar > span { display: block; min-width: 2px; }
.link-bar .seg-linked { background: var(--primary); }
.link-bar .seg-choice { background: var(--warn); }
.link-bar .seg-nopair { background: var(--muted); }
.link-bar .seg-wait { background: var(--line-strong); }
.link-bar .seg-rest { background: transparent; }
.link-legend { display: flex; flex-wrap: wrap; gap: 2px 12px; margin: 0; padding: 0; list-style: none; font-size: 12px; color: var(--muted); }
.link-legend li { display: inline-flex; align-items: center; gap: 4px; }
.link-legend i { width: 8px; height: 8px; border-radius: 2px; flex: none; }
.link-legend .seg-linked { background: var(--primary); }
.link-legend .seg-choice { background: var(--warn); }
.link-legend .seg-nopair { background: var(--muted); }
.link-legend .seg-wait { background: var(--line-strong); }
.source-summary th { padding-left: 8px; padding-right: 8px; }
/* Дата - одна неделимая строка, пояснение после неё переносится по словам. Иначе ячейка даёт таблице ширину
   в одну строку и таблица шире блока (Д103, СО-50). */
.source-summary .data-date { white-space: nowrap; }
.source-summary .data-date .note { white-space: normal; }
/* Состояние - одно слово в одну строку: колонка не сжимается ниже слова. */
.source-summary .chain-state-inline { white-space: nowrap; }
.source-summary td:first-child { min-width: 200px; }
.source-summary .row-end { text-align: right; white-space: nowrap; }
.chain-state-inline { display: inline-flex; align-items: center; gap: 6px; }
.chain-state-inline .chain-dot { margin-top: 0; }
/* Плашка показателя - серая поверхность без рамки: число, подпись над ним. */
.metric {
  display: flex; flex-direction: column; gap: 4px; min-width: 0;
  padding: 16px; background: var(--tile); border-radius: var(--radius);
  font-size: 13px;
}
/* Подпись плитки занимает две строки всегда: длинная подпись не сдвигает значение соседней плитки ряда. */
.metric-label {
  color: var(--muted);
  min-height: 2.7em; line-height: 1.35;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden;
}
/* Ряд плиток с метриками: заголовок (подпись плитки и причина очереди) занимает не больше двух строк,
   полный текст - в подсказке. Число и подпись стоят на одних уровнях у всех плиток ряда при любой длине заголовка. */
.reason-title {
  min-height: 2.7em; line-height: 1.35;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden;
  font-weight: 500;
}
.metric-value { font-size: 24px; font-weight: 500; line-height: 1.2; color: var(--ink); font-variant-numeric: tabular-nums; }
.metric-value small, .dash-value small { font-size: 13px; color: var(--muted); }
.metric-value a { color: inherit; text-decoration: none; }
.metric-value.is-waiting { color: var(--warn); }
.metric-links { display: flex; flex-wrap: wrap; gap: 4px 12px; }
/* Действия в соседних карточках стоят на одной линии: карточки ряда одной
   высоты, и последняя ссылка прижата к низу. */
.metric > a:last-child, .metric > .metric-links, .dash-card > .card-actions { margin-top: auto; padding-top: 8px; }
/* Кнопка раскрытия в строке таблицы: подпись и шеврон справа, который поворачивается, фона и рамки нет. */
.row-toggle { display: inline-flex; align-items: center; gap: 6px; height: var(--control-h); padding: 0 6px; border: 0; background: none; color: var(--primary); font: inherit; cursor: pointer; white-space: nowrap; }
.row-toggle:hover { background: var(--tile); border-radius: var(--radius); }
.row-toggle .filters-chevron { color: var(--muted); transition: transform .12s ease; }
.row-toggle[aria-expanded="true"] .filters-chevron { transform: rotate(180deg); }
.source-set td { background: var(--tile); white-space: normal; }
.source-set-title { padding-left: 12px; }
/* Таблица списка не шире своего блока (1000, 1360, 1920 px): раскрытые строки переносят длинный текст. */
.source-summary { width: 100%; max-width: 100%; }
/* Слова переносятся по словам, разрыв внутри слова только у строки без пробелов (адрес, код). */
.source-summary td { padding-left: 8px; padding-right: 8px; overflow-wrap: break-word; }
.source-set td:nth-child(3), .source-set .data-date { white-space: normal; }
/* Витрины идут колонками постоянной ширины (до `--card-max`): одна карточка не растягивается на весь экран. */
.mart-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(var(--card-min), var(--card-max))); gap: 12px; }
.dash-card {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  padding: 16px 20px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
  /* Карточка любого раздела имеет предел ширины (руководство, раздел 0, п. 34): шире настроек не бывает. */
  max-width: var(--settings-max);
}
.dash-card h2, .dash-card h3 { margin: 0; font-size: 15px; font-weight: 500; }
/* Название витрины занимает две строки всегда: короткое и длинное названия дают строки кода и значения на одной линии. */
.mart-title-row { display: grid; gap: 6px; }
.mart-title-row .mart-name { min-height: 2.6em; line-height: 1.3; }
.mart-state-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; min-height: 24px; }
.mart-title-row .help-pop { margin-left: 0; }
.dash-card .note { margin: 0; overflow-wrap: anywhere; }
.dash-card .data-date { color: var(--ink, inherit); }
.dash-value {
  margin: 6px 0 0;
  font-size: 24px;
  font-weight: 500;
  line-height: 1.2;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}
/* Справка раздела: пояснения открываются по «?» и не занимают экран. */
.help-pop { position: relative; margin-left: auto; }
.help-pop > summary { list-style: none; display: inline-flex; align-items: center; justify-content: center; }
.help-pop > summary::-webkit-details-marker { display: none; }
.help-pop-body {
  position: absolute; right: 0; top: 30px; z-index: 5; width: min(420px, 80vw);
  padding: 12px 38px 12px 14px; background: var(--card); border: 1px solid var(--line); border-radius: 8px;
  box-shadow: var(--shadow-pop); font-size: 13px;
}
.help-pop-body p { margin: 0 0 8px; }
.help-pop-body p:last-child { margin: 0; }
/* Источники данных: карточка на систему, внутри подключение и наборы. */
.page-head-side { display: flex; align-items: center; gap: 12px; }
.source-card-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.source-card-head h2 { margin: 0; font-size: 16px; }
.tile-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(var(--tile-min), var(--tile-max))); gap: 8px; }
.tile-row .metric { padding: 10px 12px; gap: 2px; }
.tile-row .metric-wide { grid-column: span 2; }
.tile-row .metric > span:last-child { overflow-wrap: anywhere; }
.metric.is-warn span:last-child { color: var(--warn); }
/* Широкая таблица наборов прокручивается вбок внутри карточки: страница горизонтально не прокручивается.
   Подсказки «?» в строках идут вместе с таблицей. */
.table-wrap.datasets { margin: 4px 0 0; overflow-x: auto; }
/* Номер версии остаётся при слове «версия», иначе в узкой колонке остаётся одинокая цифра. */
.dataset-version { white-space: nowrap; }
.dataset-actions { width: 1%; white-space: nowrap; text-align: right; }
h1 { font-size: 22px; font-weight: 500; margin: 0 0 6px; }
h2 { font-size: 16px; font-weight: 500; margin: 24px 0 8px; }
h3 { font-weight: 500; }
.page-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 16px; flex-wrap: wrap;
  /* Промежуток до содержимого задаёт шапка на всех экранах (Д139): первый блок после шапки своего отступа не добавляет. */
  margin-bottom: var(--head-gap);
}
/* Действия шапки стоят на одном месте на всех экранах одного вида: длинная
   строка под заголовком переносится сама и не сталкивает кнопки вниз. Иначе
   при переходе «Далее» кнопка уезжает из-под курсора. */
/* Место под полосу прокрутки держится всегда: короткая и длинная страница
   одинаковой ширины, и кнопки шапки не сдвигаются при переходе между ними. */
html { scrollbar-gutter: stable; }
.page-head > :first-child { flex: 1 1 0; min-width: 0; }
.page-head > .page-head-side, .page-head > .card-nav { flex: none; }
.page-head h1 { margin: 0; }
table { width: 100%; border-collapse: collapse; background: var(--card); }
td { text-align: left; padding: 8px 10px; border-bottom: 1px solid var(--line); vertical-align: top; font-variant-numeric: tabular-nums; }
th { text-align: left; padding: 8px 10px; border-bottom: 1px solid var(--line); vertical-align: top; font-variant-numeric: tabular-nums; font-size: 12px; font-weight: 400; color: var(--muted); }
tbody tr:hover { background: var(--row-hover); }
/* Заголовок колонки, по которой список можно упорядочить: ссылка со стрелкой
   направления. Колонка, задающая порядок сейчас, выделена цветом акцента. */
.th-sort { color: inherit; text-decoration: none; }
.th-sort:hover { color: var(--ink); }
.th-sort::after { content: "↕"; margin-left: 4px; color: var(--line-strong); }
th[aria-sort="ascending"] .th-sort, th[aria-sort="descending"] .th-sort { color: var(--primary); }
th[aria-sort="ascending"] .th-sort::after { content: "↑"; color: var(--primary); }
th[aria-sort="descending"] .th-sort::after { content: "↓"; color: var(--primary); }
/* Плашка статуса: светлый фон цвета смысла и тёмный текст того же цвета. */
.tag {
  display: inline-block;
  font-size: 12px;
  padding: 2px 8px;
  border-radius: 6px;
  background: var(--primary-soft);
  white-space: nowrap;
}
.tag.REVIEW, .tag.DEFERRED { background: var(--warn-bg); color: var(--warn); }
.tag.CONFLICT, .tag.NO_MATCH { background: var(--bad-bg); color: var(--bad); }
.tag.AUTO_ACCEPT, .tag.CONFIRMED, .tag.success, .tag.ACTIVE { background: var(--ok-bg); color: var(--ok); }
.tag.failed { background: var(--bad-bg); color: var(--bad); }
.tag.stopped { background: var(--warn-bg); color: var(--warn); }
.tag.running, .tag.queued, .tag.waiting, .tag.DRAFT, .tag.VALIDATED { background: var(--warn-bg); color: var(--warn); }
.tag.paused, .tag.unknown, .tag.RETIRED { background: var(--primary-soft); color: var(--muted); }
/* Название и метки одной строки: промежутки задаёт раскладка из токенов, пробел в разметке не нужен
   (руководство, раздел 0, п. 20). Название - метка 8, метка - метка 4. Контейнер без своего gap. */
.label-row {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  column-gap: var(--gap-name);
  row-gap: var(--gap-tag);
  max-width: 100%;
}
/* Группа меток подряд (без названия между ними) - один элемент строки с промежутком --gap-tag. Отрицательных отступов нет:
   при переносе группа целиком встаёт в начало новой строки (руководство, раздел 0, п. 20). */
.tag-group { display: inline-flex; flex-wrap: wrap; align-items: center; column-gap: var(--gap-tag); row-gap: var(--gap-tag); max-width: 100%; }
/* Надпись кнопки - одна строка текста: innerText и доступное имя читаются фразой. */
.btn-label { display: inline; }
.btn {
  /* Высота кнопки --control-h, как у поля и списка рядом: одна строка управления ровная. Длинная надпись растит кнопку, не обрезается. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* Части надписи (текст и пометка, две подписи) разделяет gap: пробел между элементами во flex не отрисовывается. */
  column-gap: var(--gap-inline);
  box-sizing: border-box;
  min-height: var(--control-h);
  background: var(--primary);
  color: var(--on-fill);
  border: 0;
  padding: 0 14px;
  border-radius: var(--radius);
  cursor: pointer;
  text-decoration: none;
  font: inherit;
}
[hidden] {
  display: none !important;
}
/* Состояния кнопки: наведение, нажатие и отправка. Без них нажатие ничем не
   подтверждается, и по кнопке бьют второй раз. */
.btn:hover { background: var(--primary-hover); }
.btn:active { transform: translateY(1px); }
.btn.ghost { background: var(--card); color: var(--ink); border: 1px solid var(--line-strong); }
/* Опасная кнопка контуром (Д171): фон карточки, красный текст и рамка. Заливка опасной кнопки - `.btn.is-danger`. */
.btn.ghost.is-danger { background: var(--card); color: var(--bad); border-color: var(--bad); }
.btn.ghost:hover { background: var(--bg); }
/* Отправка: надпись остаётся на месте и скрыта, поверх неё кольцо ожидания.
   Ширина кнопки не меняется, и соседние кнопки не сдвигаются. */
.btn.is-busy { position: relative; color: transparent; pointer-events: none; }
.btn.is-busy > * { visibility: hidden; }
.btn.is-busy::after {
  content: ""; position: absolute; left: 50%; top: 50%; width: 14px; height: 14px; margin: -7px 0 0 -7px;
  border: 2px solid var(--on-fill); border-right-color: transparent; border-radius: 50%;
  animation: btn-busy .7s linear infinite;
}
.btn.ghost.is-busy::after { border-color: var(--muted); border-right-color: transparent; }
@keyframes btn-busy { to { transform: rotate(360deg); } }
.actions { display: flex; gap: 8px; flex-wrap: wrap; margin: 16px 0; }
.filter-panel {
  display: flex; align-items: end; gap: 12px; flex-wrap: wrap;
  margin: 0 0 20px; padding: 16px; background: var(--card);
  border: 1px solid var(--line); border-radius: var(--radius-card);
}
.filter-panel label { display: grid; gap: 4px; color: var(--muted); font-size: 12px; position: relative; }
.clear-input {
  position: absolute; right: 6px; bottom: 6px;
  width: 24px; height: 24px; padding: 0; border: 0; border-radius: 50%;
  background: var(--primary-soft); color: var(--muted);
  font: inherit; line-height: 1; cursor: pointer;
}
.clear-input:hover { color: var(--ink); }

/* Строка о главном на дашборде: одно предложение и, если есть куда идти, ссылка. */
.headline {
  display: flex; flex-wrap: wrap; gap: 8px 16px; align-items: baseline;
  margin: 0 0 20px; padding: 10px 14px; border-radius: var(--radius);
  border: 0; background: var(--card);
}
.headline-warn { background: var(--warn-bg); color: var(--warn); }
.headline a { color: inherit; font-weight: 500; }

/* Доля распределённого объёма: полоса читается быстрее, чем два числа. */
.meter {
  display: block; height: 6px; margin: 4px 0 6px; border-radius: 3px;
  background: var(--line); overflow: hidden;
}
.meter span { display: block; height: 100%; background: var(--primary); }

/* Цепочка обновлений: четыре шага подряд одной полосой. Отдельных карточек нет. */
.chain { display: flex; flex-wrap: wrap; gap: 8px; margin: 12px 0 20px; padding: 0; list-style: none; }
.chain-step {
  position: relative; display: flex; flex-direction: column; gap: 2px; flex: 1 1 200px;
  padding: 10px 14px 10px 28px; background: var(--card);
  border: 1px solid var(--line); border-radius: var(--radius-card);
}
.chain-dot { width: 10px; height: 10px; margin-top: 5px; border-radius: 50%; background: var(--muted); flex: none; }
.chain-step > .chain-dot { position: absolute; left: 12px; top: 14px; margin: 0; }
.chain-success .chain-dot { background: var(--ok); }
.chain-failed .chain-dot { background: var(--bad); }
.chain-running .chain-dot, .chain-waiting .chain-dot, .chain-stopped .chain-dot { background: var(--warn); }
.chain-paused .chain-dot { background: var(--line); }
.chain-title { font-weight: 500; }
.chain-state-row { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.chain-extra { display: flex; flex-wrap: wrap; gap: 0 10px; }

/* Редкие операции скрыты до раскрытия: рядом с ежедневными они путают. */
.rare-ops { margin: 0 0 20px; }
/* Раскрывающийся блок: строка-кнопка, шеврон справа показывает состояние и
   поворачивается. Стрелки перед текстом не используются: они читались как пункт
   навигации, не как управляющий элемент. Высота не меньше 36, нажимается вся строка. */
.rare-ops > summary {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 36px;
  padding: 6px 12px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background: var(--card);
  cursor: pointer;
  color: var(--ink);
  list-style: none;
}
.rare-ops > summary::-webkit-details-marker { display: none; }
.rare-ops > summary::after {
  content: "";
  width: 7px;
  height: 7px;
  margin: -3px 0 0 4px;
  border: solid var(--muted);
  border-width: 0 1.8px 1.8px 0;
  transform: rotate(45deg);
  transition: transform .12s ease;
}
.rare-ops[open] > summary::after { margin-top: 3px; transform: rotate(-135deg); }
.rare-ops > summary:hover { background: var(--tile); }

/* Узкий экран: шапка и заголовки страниц выстраиваются в столбец, чтобы пункты
   меню не наезжали на название и действия не уезжали к правому краю. */
/* Подключение к источнику: проверка и секрет стоят столбцом рядом с параметрами. */
.connection-side { display: flex; flex-direction: column; gap: 12px; }
.connection-side .dash-card .card-actions { padding-top: 4px; }
.dash-card .mart-name { margin: 0; font-size: 15px; font-weight: 500; }
@media (max-width: 760px) {
  /* Узкий экран: меню становится полосой сверху и раскрывается кнопкой, иначе
     оно занимало бы треть ширины. */
  .has-side { display: block; }
  .side { position: static; width: auto; height: auto; padding: 6px 16px; border-right: 0; border-bottom: 1px solid var(--side-line); }
  .side-head { padding: 0; }
  .nav-toggle {
    display: inline-flex; align-items: center; margin-left: auto; min-height: 36px; padding: 0 14px;
    border: 1px solid var(--line-strong); border-radius: var(--radius); background: var(--card);
    color: var(--ink); font: inherit; cursor: pointer;
  }
  .side-body { display: none; padding: 6px 0; }
  .side-body.is-open { display: flex; }
  .side-link { min-height: 44px; }
  .side-toggle { display: none; }
  /* Панель профиля стоит в потоке под своей кнопкой: всплывающий слой на
     узком экране перекрывал бы соседние пункты. */
  .nav-popup { position: static; box-shadow: none; background: none; padding: 0 0 4px 12px; }
  .page-head { flex-direction: column; align-items: stretch; gap: 8px; }
  .page-head > :first-child { flex: none; }
  .page-head .card-nav, .page-head .btn { align-self: flex-start; margin-left: 0; }
  .card-nav { flex-wrap: wrap; }
  .filter-panel { padding: 12px; }
  .filter-panel .search-field { flex-basis: 100%; }
}
.filter-panel .search-field { min-width: min(100%, 360px); flex: 1 1 300px; }
.filter-panel .search-field input { width: 100%; }
.search-empty { flex-basis: 100%; margin: 0; }
.row-action { display: inline-block; padding: 5px 9px; border: 1px solid var(--line); border-radius: 5px; text-decoration: none; }
.row-action:hover { background: var(--primary-soft); }
.note { color: var(--muted); font-size: 13px; }
.counts { margin: 8px 0 12px; display: flex; flex-wrap: wrap; gap: 4px 10px; align-items: baseline; }
.counts b { color: var(--primary); }
@media (max-width: 800px) {
  .header-inner, .content-width, main { margin-inline: 16px; }
  main { padding: 20px 0 40px; overflow-x: auto; }
  .filter-panel { align-items: stretch; }
}
.card {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 14px;
}
.choice { display: block; padding: 7px 0; border-bottom: 1px solid var(--line); }
code { font-size: 12px; }
/* Служебное значение администратору: вторая строка текста поля, моноширинная, переносится без вылома строки. */
.tech-value { display: block; margin-top: 2px; font-size: 12px; color: var(--muted); overflow-wrap: anywhere; }
.tech-value code { font-family: var(--font-mono); font-size: 12px; }

/* Формы входа */
/* Вход и смена пароля: одна карточка по центру, поля и кнопка на всю ширину. */
.auth {
  max-width: 400px; margin: 56px auto; padding: 24px 28px;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius-card);
}
.auth h1 { margin: 0 0 4px; }
.auth-sub { margin: 0 0 16px; }
.auth form { margin: 0; }
.auth label { display: block; margin: 12px 0 4px; font-size: 13px; color: var(--muted); }
.auth input { width: 100%; }
.auth input:focus { border-color: var(--primary); outline: none; box-shadow: 0 0 0 3px var(--primary-soft); }
.auth button { margin-top: 20px; width: 100%; }
.auth .note { margin: 16px 0 0; }
.auth-rules { margin: 0 0 4px; padding: 10px 12px; background: var(--tile); border-radius: var(--radius); font-size: 13px; color: var(--muted); }
.error { color: var(--bad); background: var(--bad-bg); border: 1px solid var(--bad-bg); padding: 10px 12px; border-radius: var(--radius); }
.notice {
  background: var(--ok-bg);
  color: var(--ok);
  border: 1px solid var(--ok-line);
  padding: 10px 14px;
  border-radius: 8px;
  margin: 0 0 16px;
}
.setup-blocker { display: flex; align-items: center; justify-content: space-between; gap: 20px; margin: 16px 0; padding: 16px 18px; color: var(--warn); background: var(--warn-bg); border: 1px solid var(--warn-line); border-radius: var(--radius-card); }
.setup-blocker h2 { margin: 2px 0 6px; font-size: 16px; }
.setup-blocker p { margin: 0; }
.setup-blocker > .btn { flex: 0 0 auto; min-width: max-content; white-space: nowrap; }
.eyebrow { font-size: 12px; font-weight: 500; text-transform: uppercase; letter-spacing: .04em; }
.attention-list { display: grid; gap: 10px; padding: 0; list-style: none; }
.attention-item { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 12px 14px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--tile); }
.attention-item.is-failure { border-color: var(--bad); background: var(--bad-bg); }
.attention-main { min-width: 0; }
.attention-title { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }
.attention-title strong { font-size: 14px; }
.attention-item p { margin: 5px 0 0; color: var(--muted); font-size: 13px; }
.attention-action { flex: none; }
@container page (max-width: 700px) { .attention-item { align-items: flex-start; flex-direction: column; } }
.failure-overview { max-width: var(--block-max); margin: 16px 0 20px; padding: 18px 20px; background: var(--card); border: 1px solid var(--line); border-radius: var(--radius-card); }
.failure-overview-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; }
.failure-overview-head h2 { margin: 2px 0 5px; font-size: 18px; }
.failure-overview-head form { margin: 0; flex: none; }
.failure-detail-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(var(--card-min), var(--card-max))); gap: 12px; margin-top: 16px; }
.failure-detail-card { padding: 14px 16px; background: var(--tile); border-radius: var(--radius); }
.failure-detail-card h3 { margin: 0 0 8px; font-size: 14px; }
.failure-detail-card p { margin: 0 0 8px; }
.failure-detail-card p:last-child { margin-bottom: 0; }
.failure-detail-card ol { margin: 0; padding-left: 20px; }
.failure-detail-card li + li { margin-top: 5px; }
.failure-technical { max-width: var(--block-max); margin: 0 0 20px; padding: 16px 20px; background: var(--tile); border: 1px solid var(--line); border-radius: var(--radius-card); }
.failure-technical-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.failure-technical h2 { margin: 0; font-size: 16px; }
.failure-facts { display: grid; grid-template-columns: 180px 1fr; gap: 8px 16px; margin: 14px 0 0; }
.failure-facts dd { margin: 0; overflow-wrap: anywhere; }
.failure-facts dt { margin: 0; overflow-wrap: anywhere; color: var(--muted); }
.failure-facts code { white-space: pre-wrap; }
.failure-log { margin: 0 0 20px; padding: 12px 16px; background: var(--card); border: 1px solid var(--line); border-radius: var(--radius-card); }
.failure-log summary { list-style: none; cursor: pointer; color: var(--primary); font-weight: 500; }
.failure-log summary::-webkit-details-marker { display: none; }
.failure-log .filters-chevron { color: var(--muted); transition: transform .12s ease; }
.failure-log[open] .filters-chevron { transform: rotate(180deg); }
.failure-log .table-wrap { margin: 12px 0 0; }
.doc-text { margin: 12px 0 0; padding: 12px 16px; background: var(--card); border: 1px solid var(--line); border-radius: var(--radius-card); }
.doc-text summary { list-style: none; cursor: pointer; color: var(--primary); font-weight: 500; }
.doc-text summary::-webkit-details-marker { display: none; }
.doc-text .filters-chevron { color: var(--muted); transition: transform .12s ease; }
.doc-text[open] .filters-chevron { transform: rotate(180deg); }
.doc-text .table-wrap { margin: 12px 0 0; }
@container page (max-width: 700px) { .failure-overview-head, .failure-technical-head { flex-direction: column; } .failure-detail-grid { grid-template-columns: 1fr; } .failure-facts { grid-template-columns: 1fr; gap: 3px; } .failure-facts dd { margin-bottom: 8px; } }
@container page (max-width: 700px) { .setup-blocker { align-items: flex-start; flex-direction: column; } .setup-blocker > .btn { width: 100%; min-width: 0; white-space: normal; } }
/* Таблица лежит в карточке: рамка и скругление у обёртки, у последней строки
   нижней линии нет. */
.table-wrap {
  overflow-x: auto; margin: 0 0 8px;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius-card);
}
.table-wrap table { background: transparent; }
/* Длинный список: строка заголовков остаётся у верхнего края окна при
   прокрутке. Обёртка с прокруткой вбок закрепить строку не даёт, поэтому у
   таких таблиц её нет, пока рабочая область достаточно широка. */
.table-wrap.sticky-head { overflow: visible; }
.sticky-head thead th {
  position: sticky; top: 0; z-index: 2; background: var(--card);
  border-bottom: 0; box-shadow: inset 0 -1px 0 var(--line);
}
.sticky-head thead th:first-child { border-top-left-radius: var(--radius-card); }
.sticky-head thead th:last-child { border-top-right-radius: var(--radius-card); }
@container page (max-width: 899px) {
  .table-wrap.sticky-head { overflow-x: auto; }
  .sticky-head thead th { position: static; }
}
.table-wrap tbody tr:last-child > td { border-bottom: 0; }
.copy-cell {
  position: relative;
  padding-right: 38px;
}
.copy-cell-button {
  position: absolute;
  top: 50%; right: 7px;
  width: 24px; height: 24px;
  padding: 0;
  border: 0;
  border-radius: 5px;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  opacity: 0;
  transform: translateY(-50%);
  transition: opacity .12s ease, background-color .12s ease, color .12s ease;
}
.copy-cell:hover .copy-cell-button,
.copy-cell:focus-within .copy-cell-button {
  opacity: 1;
}
.copy-cell-button:hover {
  color: var(--primary);
  background: var(--primary-soft);
  outline: none;
}
.copy-cell-button:focus-visible {
  opacity: 1;
  color: var(--primary);
  background: var(--primary-soft);
  outline: none;
}
.copy-cell-button::before {
  content: "";
  position: absolute;
  width: 8px; height: 8px;
  border: 1.5px solid currentColor;
  border-radius: 2px;
  top: 5px; left: 7px; background: var(--card);
}
.copy-cell-button::after {
  content: "";
  position: absolute;
  width: 8px; height: 8px;
  border: 1.5px solid currentColor;
  border-radius: 2px;
  top: 8px; left: 4px;
}
.copy-cell-button.is-copied { color: var(--ok); }
.users-table { min-width: 720px; }
.users-table td { vertical-align: middle; }
.when {
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
  font-size: 13px;
}
.users-table .when {
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
  font-size: 13px;
}
.users-table tr.is-blocked td { background: var(--retired-bg); color: var(--muted); }
.inline-role {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: nowrap;
}
.nowrap { white-space: nowrap; }
/* В ячейке nowrap метки и группы меток стоят в одну строку (руководство, раздел 0, п. 20). */
.nowrap .label-row, .nowrap .tag-group { flex-wrap: nowrap; }
/* Секрет, который виден один раз - ключ потребителя, одноразовый пароль: блок
   крупный и выделенный, чтобы его не пролистали и значение можно было выделить
   мышью целиком. */
.secret-once {
  margin: 0 0 16px; padding: 14px 16px;
  background: var(--warn-bg); border: 1px solid var(--warn-line); border-radius: 8px;
}
.secret-once .panel-title { margin: 0 0 8px; font-size: 14px; }
.secret-value { margin: 0 0 8px; overflow-wrap: anywhere; }
.secret-value code { font-size: 15px; user-select: all; }

.card-nav {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  align-items: center;
  margin-left: auto;
}
.card-nav-queue {
  display: flex;
  gap: 8px;
}
.btn.is-disabled,
.btn[aria-disabled="true"] {
  opacity: .45;
  pointer-events: none;
  cursor: default;
}
/* Отключённая кнопка видна как отключённая: иначе она выглядит рабочей, и
   нажатие молча ничего не делает. */
.btn:disabled { opacity: .45; cursor: not-allowed; }
.btn-icon { width: 14px; height: 14px; vertical-align: -2px; }
.btn-icon.lead { margin-right: 5px; }
.btn-icon.trail { margin-left: 5px; }
.btn.is-pressed { transform: translateY(1px); filter: brightness(0.93); }
.btn.ghost.is-pressed { background: var(--primary-soft); border-color: var(--primary); filter: none; }
.card > h2:first-child { margin-top: 0; }
.decide-card {
  background: var(--primary-soft);
  border-color: var(--primary-line);
}
.decide-card .choice {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 12px 0;
}
.decide-card .choice > span {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.decide-card .choice input { margin-top: 4px; }
.choice-head { display: flex; flex-wrap: wrap; gap: 8px; align-items: baseline; }
/* Сравнение с записью МДЛП: что совпало, что разошлось. Цвет повторяет смысл
   статусных плашек, поэтому состояние читается до чтения подписи. */
.compare { display: flex; flex-wrap: wrap; gap: 6px; margin: 6px 0 2px; }
.cmp-label { font-weight: 500; }
.cmp-value { color: var(--ink); overflow-wrap: anywhere; }
.cmp-same { background: var(--ok-bg); color: var(--ok); }
.cmp-close { background: var(--warn-bg); color: var(--warn); }
.cmp-differ { background: var(--bad-bg); color: var(--bad); }
.cmp-missing { background: var(--muted-bg); color: var(--muted); }
/* Реквизиты сторон: строка против строки, чтобы сравнивать по горизонтали. */
.facts { margin: 0; }
.fact {
  display: grid;
  grid-template-columns: 8.5rem minmax(0, 1fr);
  gap: 2px 12px;
  padding: 5px 0;
  border-bottom: 1px solid var(--line-soft);
}
.fact:last-child { border-bottom: 0; }
.fact dt { color: var(--muted); }
.fact dd { margin: 0; overflow-wrap: anywhere; }
@media (max-width: 760px) {
  .fact { grid-template-columns: 1fr; }
  .fact dt { font-size: 13px; }
}
/* Подсказки справочника CRM под полем поиска. */
.search-field { position: relative; }
.suggest {
  position: absolute; z-index: 20; left: 0; right: 0; top: 100%;
  margin: 4px 0 0; padding: 4px; list-style: none;
  background: var(--card); border: 1px solid var(--line); border-radius: 8px;
  box-shadow: var(--shadow-dialog);
  max-height: 320px; overflow-y: auto;
}
.suggest li {
  display: flex; flex-direction: column; gap: 2px;
  padding: 8px 10px; border-radius: 6px; cursor: pointer;
}
.suggest li.is-active, .suggest li:hover { background: var(--primary-soft); }
.suggest-title { font-weight: 500; }
.suggest-added { align-self: flex-start; }
.suggest li.suggest-all { color: var(--primary); font-weight: 500; }
.suggest li.suggest-status { color: var(--muted); cursor: default; }
.suggest li.suggest-status:hover { background: none; }
.suggest-empty { padding: 8px 10px; color: var(--muted); }
/* Сообщение «пусто» (нет данных, ещё не настроено, недоступно) - один вид на всех экранах: фон карточки,
   тонкая рамка, радиус элементов управления, отступы. Ширина - во всю ширину блока, который оно заменяет. */
.empty-state {
  margin: 16px 0; padding: 16px; background: var(--card);
  border: 1px solid var(--line); border-radius: var(--radius); color: var(--muted);
}
.empty-state a { margin-left: 4px; }
.queue-refresh-state {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px;
  margin: 0 0 12px; padding: 10px 14px;
  background: var(--warn-bg); color: var(--warn); border-radius: var(--radius);
}
.queue-refresh-state strong { font-weight: 500; }
.queue-refresh-state span { color: var(--muted); }
/* История: одна хронология, строка события раскрывается. Шеврон справа стоит у
   всех строк, цвет метки слева говорит о роде события. Строка не ниже 44. */
.history-tabs { margin: 12px 0; }
.history-events { list-style: none; margin: 0 0 16px; padding: 0; }
.history-events > li { border-bottom: 1px solid var(--line); }
.history-events > li:last-child { border-bottom: 0; }
.history-event > summary { display: flex; align-items: center; gap: 12px; min-height: 44px; padding: 8px 4px; cursor: pointer; list-style: none; }
.history-event > summary::-webkit-details-marker { display: none; }
.history-event > summary:hover { background: var(--tile); }
.event-mark { flex: none; display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px; }
.event-mark:not(:has(svg))::before { content: ""; width: 10px; height: 10px; border-radius: 50%; background: var(--line-strong); }
.event-mark.tone-ok::before { background: var(--ok); }
.event-mark.tone-warn::before { background: var(--warn-mid); }
.event-mark.tone-link { color: var(--primary); }
.event-mark .event-icon { width: 16px; height: 16px; margin: 0; }
.event-main { display: grid; gap: 4px; flex: 1; min-width: 0; }
.event-main strong { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; font-weight: 500; }
.event-main strong::first-letter { text-transform: uppercase; }
.event-chevron { flex: none; width: 16px; height: 16px; margin: 0; color: var(--muted); transition: transform .12s ease; }
.history-event[open] > summary .event-chevron { transform: rotate(180deg); }
.event-facts { display: grid; grid-template-columns: minmax(96px, 30%) 1fr; gap: 8px 12px; margin: 0 0 12px; padding: 12px; background: var(--tile); border-radius: var(--radius); font-size: 13px; }
.event-facts dt { color: var(--muted); }
.event-facts dd { margin: 0; overflow-wrap: anywhere; }
.search-panel { margin-top: 16px; }
.confirm-dialog {
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 20px 22px;
  max-width: 28rem;
  width: min(100% - 32px, 28rem);
  color: var(--ink);
  background: var(--card);
}
.confirm-dialog::backdrop {
  background: var(--backdrop);
}
.confirm-dialog h2 { margin-top: 0; }
.confirm-dialog .actions { margin-bottom: 0; }
kbd {
  font: inherit;
  font-size: 0.85em;
  padding: 0 5px;
  border: 1px solid var(--line);
  border-bottom-width: 2px;
  border-radius: 4px;
  background: var(--card);
  white-space: nowrap;
}
.choice-key { min-width: 1.5em; text-align: center; color: var(--muted); font-size: 12px; }
.help-btn {
  flex: none;
  width: 24px;
  height: 24px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: var(--card);
  color: var(--muted);
  font: 500 13px/1 inherit;
  cursor: pointer;
}
.help-btn:hover, .help-btn:focus-visible { border-color: var(--primary); color: var(--primary); }
.keys-list { display: grid; grid-template-columns: max-content 1fr; gap: 6px 14px; margin: 0 0 14px; }
.keys-list dt { white-space: nowrap; }
.keys-list dd { margin: 0; }
.linklike {
  font: inherit;
  padding: 0;
  border: 0;
  background: none;
  color: var(--primary);
  text-decoration: underline;
  cursor: pointer;
}

/* Невысокое окно ноутбука: те же блоки, но плотнее - иначе список решений
   уезжает за нижний край, и выбор приходится делать вслепую. */
@media (max-height: 800px) {
  main { padding: 16px 0 32px; }
  .card { padding: 10px 12px; }
  .fact { padding: 3px 0; }
  .choice { padding: 5px 0; }
  .actions { margin: 10px 0; }
  .row { margin: 8px 0 12px; }
  .page-head { margin-bottom: var(--head-gap-low); }
}
/* Вкладки раздела: подчёркнута открытая, число рядом с подписью. */
.tabs { display: flex; gap: 20px; margin: 12px 0 14px; border-bottom: 1px solid var(--line); }
/* Ряд разделов журналов и настроек платформы стоит одной строкой: подписи не переносятся. */
.tabs-line { flex-wrap: nowrap; }
.tabs-line .tab { white-space: nowrap; }
/* Полные подписи «Настроек платформы» (778 px с отступами) при открытом меню на 1000 px не помещаются.
   Тогда страница уже 850 px и меньше: видны короткие подписи, полная - в подсказке (title). */
.tab-short { display: none; }
@container page (max-width: 849px) {
  .tab-long { display: none; }
  .tab-short { display: inline; }
}
.tab { padding: 6px 2px; margin-bottom: -1px; border-bottom: 2px solid transparent; color: var(--muted); text-decoration: none; }
.tab:hover { border-bottom-color: var(--line-strong); color: var(--ink); }
/* Открытая вкладка выделена тем же цветом акцента, что и открытый раздел меню. */
.tab[aria-current="page"], .tab[aria-current="true"] { border-bottom-color: var(--primary); color: var(--primary); font-weight: 500; }
/* Вкладка-кнопка: переключает блок на той же странице, без перехода. */
button.tab { background: none; border-width: 0 0 2px; border-style: solid; font: inherit; cursor: pointer; }
/* Список найденного поиском занимает всю ширину панели и прокручивается сам. */
.search-results { flex-basis: 100%; max-height: 320px; overflow-y: auto; margin: 0; }
.search-results caption { padding: 8px 10px; text-align: left; }
button.row-action { background: var(--card); color: var(--primary); font: inherit; cursor: pointer; }
/* Плитки причин: одна компактная строка, иначе список уходит за первый экран. */
.filter-tiles {
  display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 8px;
  grid-template-columns: none;
}
/* Рамка есть у каждой плитки строки, у ссылки она видна при выборе: строки внутри плиток лежат на одной высоте. */
.filter-tiles .metric { flex: 1 1 140px; max-width: var(--tile-max); padding: 8px 10px; gap: 0; border: 1px solid transparent; }
.filter-tile { color: var(--ink); text-decoration: none; border: 1px solid transparent; }
.filter-tile:hover { border-color: var(--line-strong); }
/* Выбранная плитка обведена акцентом в две линии и остаётся на поверхности плитки. */
.filter-tile[aria-current="true"] { border-color: var(--primary); box-shadow: inset 0 0 0 1px var(--primary); }
/* Все плитки строки фильтров (ссылки и обычные) с одним размером значения и примечания: иначе строки под значением съезжают. */
.filter-tiles .metric-value { font-size: 18px; }
.filter-tiles .note { font-size: 12px; }
.metric-value.is-bad { color: var(--bad); }
.waiting-line { margin: 0 0 12px; }
/* Причины проверки: компактные переключатели с числом, не статистика. Активная обведена акцентом. */
.reason-row { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 12px; }
.reason-card { flex: 1 1 170px; max-width: var(--tile-max); display: grid; gap: 0; padding: 8px 14px; border: 1px solid var(--line); border-radius: var(--radius-card); background: var(--card); color: var(--ink); text-decoration: none; }
.reason-card:hover { border-color: var(--line-strong); }
.reason-card[aria-current="true"] { border-color: var(--primary); background: var(--primary-soft); box-shadow: inset 0 0 0 1px var(--primary); }
.reason-count { font-size: 22px; font-weight: 500; line-height: 1.25; }
.reason-count.is-bad { color: var(--bad); }
/* Панель управления очередью: поиск, фильтры, сортировка и принадлежность в одной строке. */
/* Элементы одной строки управления одной высоты --control-h: поле, кнопки с окнами и кнопки стоят на одной линии. */
.queue-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; margin: 0 0 12px; }
.queue-bar .search-box { align-self: flex-end; }
/* Значок лупы стоит во всех строках поиска: одна подпись поля на всех экранах. */
.search-box .btn-icon, .cand-search .search-field .btn-icon { position: absolute; left: 10px; top: 50%; transform: translateY(-50%); margin: 0; color: var(--muted); pointer-events: none; }
.search-box input { box-sizing: border-box; height: var(--control-h); flex: 1; min-width: 0; padding: 0 36px 0 34px; }
.queue-field { display: grid; gap: 2px; color: var(--muted); font-size: 12px; }
.queue-bar .filters-pop > summary { box-sizing: border-box; height: var(--control-h); padding-top: 0; padding-bottom: 0; }
.queue-state { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 6px 16px; margin: 0 0 12px; }
.queue-state .filter-chips { margin: 0; }
.queue-state .found-line { margin: 0; margin-left: auto; }
.changed-col { white-space: nowrap; }
.page-sizes { display: flex; gap: 8px; align-items: baseline; margin: 0; }
.page-sizes strong { color: var(--ink); }
.filters-fields { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.filters-fields .queue-field select { width: 100%; }
.filters-actions { display: flex; gap: 8px; }
.filters-actions .btn { flex: 1 1 0; justify-content: center; }
/* Кнопки у правого края панели: окно прижато к ним справа и не уходит за край экрана. */
.queue-bar .filters-body { left: auto; right: 0; }
.filters-body.sort-body { width: min(300px, 90vw); }
.sort-options { display: grid; gap: 8px; margin: 0; padding: 0; border: 0; }
.sort-options legend { margin-bottom: 8px; padding: 0; font-weight: 500; }
.sort-option { display: flex; align-items: center; gap: 8px; cursor: pointer; }
.row-title { color: var(--ink); text-decoration: none; }
.row-title:hover strong { color: var(--primary); }
.cand-pct { color: var(--primary); font-weight: 500; }
/* Причина строки: подпись с цветом смысла и текстом, цвет не единственный признак. */
.reason-chip { display: inline-block; padding: 2px 8px; border-radius: var(--radius); background: var(--muted-bg); color: var(--ink); font-size: 12px; font-weight: 500; }
.reason-chip.reason-conflict, .reason-chip.reason-CONFLICT { background: var(--bad-bg); color: var(--bad); }
.reason-chip.reason-weak { background: var(--warn-bg); color: var(--warn); }
.reason-chip.reason-close { background: var(--primary-soft); color: var(--primary); }
.filter-chips { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: 0 0 10px; }
.chip {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 4px 10px; border: 1px solid var(--line-strong); border-radius: var(--radius);
  background: var(--card); color: var(--ink); text-decoration: none; font-size: 13px;
}
.chip:hover { border-color: var(--ink); }
.chip.is-current, .chip[aria-current="true"] {
  border-color: var(--primary); background: var(--primary-soft);
}
.chip-reset { font-size: 13px; }
/* Строка поиска: одно поле на всю ширину, редкие условия раскрываются ниже. */
.search-line { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin: 0 0 12px; }
/* Строка отбора с подписями над полями: поля, флажок и кнопка стоят по нижнему краю, подпись над полем с промежутком 4. */
.search-line.search-line-captioned { align-items: flex-end; }
.search-line-captioned .queue-field { gap: 4px; }
.search-line-captioned select { font-size: 13px; }
.search-line-captioned .check-line { display: inline-flex; align-items: center; gap: 6px; min-height: var(--control-h); color: var(--ink); cursor: pointer; }
.search-box { position: relative; display: flex; flex: 1 1 320px; min-width: 0; }
/* Крестик внутри поля: место под него оставлено всегда, поэтому строка не прыгает. */
.search-clear {
  position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
  width: 24px; height: 24px; padding: 0; border: 0; border-radius: 50%;
  background: var(--tile); color: var(--muted); font: inherit; line-height: 1; cursor: pointer;
}
.search-clear:hover { color: var(--ink); }
/* Крестик закрытия всплывающей панели, ставит его скрипт в base.html. */
.pop-close {
  position: absolute; right: 8px; top: 8px; width: 26px; height: 26px; padding: 0;
  border: 0; border-radius: 50%; background: transparent; color: var(--muted);
  font: inherit; font-size: 18px; line-height: 1; cursor: pointer;
}
.pop-close:hover { background: var(--tile); color: var(--ink); }
/* Всплывающая панель в шапке раздела, как справка, но с действиями. */
.head-pop { position: relative; }
.head-pop > summary { list-style: none; }
.head-pop > summary::-webkit-details-marker { display: none; }
.head-pop-body {
  position: absolute; right: 0; top: 42px; z-index: 5; width: min(440px, 86vw);
  display: flex; flex-direction: column; gap: 10px;
  padding: 14px 38px 14px 16px; background: var(--card); border: 1px solid var(--line);
  border-radius: var(--radius-card); box-shadow: var(--shadow-pop);
}
.head-pop-body p, .head-pop-body form { margin: 0; }
.import-form { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.queue-table td { vertical-align: middle; }
.check-col { width: 1%; }
.row-end { width: 1%; text-align: right; white-space: nowrap; }
/* Действие над отмеченными видно и в середине длинного списка. */
/* Панель появляется только при отмеченных строках и тогда прилипает к низу.
   Листание стоит рядом отдельно: без отметок экран не занимает лишнее место. */
.queue-foot { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; margin: 0 0 16px; }
.bulk-bar {
  position: sticky; bottom: 16px; z-index: 3; display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px;
  margin: 12px 0; padding: 10px 16px; background: var(--card); border: 1px solid var(--line-strong); border-radius: var(--radius-card);
  box-shadow: var(--shadow-pop);
}
.bulk-bar[hidden] { display: none; }
.pager { display: flex; align-items: center; gap: 4px 14px; margin-left: auto; }
/* Ссылка «К содержимому» для клавиатуры: видна только в фокусе, иначе не занимает место. */
.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  padding: 8px 14px; background: var(--card); color: var(--primary); border: 1px solid var(--line-strong);
  border-radius: var(--radius); text-decoration: none; font-size: 14px;
}
.skip-link:focus { left: 12px; top: 12px; outline: 2px solid var(--primary); outline-offset: 2px; }
.pager-link { display: inline-flex; align-items: center; gap: 4px; font-size: 13px; text-decoration: none; }
.pager-link:hover { text-decoration: underline; }
/* Справочник: запись МДЛП, стрелка связи, карточка CRM. */
.catalog-table td { vertical-align: middle; }
.catalog-search { margin: 12px 0 16px; }
.catalog-state { display: flex; flex-direction: column; align-items: flex-start; gap: 8px; }
.catalog-state .row-action { padding: 0; border: 0; }
.list-foot { display: flex; margin: 0 0 16px; }
/* Переключатель из двух-трёх вариантов: выбранный на серой подложке. */
.seg { display: inline-flex; border: 1px solid var(--line-strong); border-radius: var(--radius); overflow: hidden; font-size: 13px; background: var(--card); }
.seg a { padding: 6px 12px; color: var(--ink); text-decoration: none; }
.seg a + a { border-left: 1px solid var(--line); }
.seg a:hover { background: var(--tile); }
.seg a[aria-current="page"] { background: var(--primary-soft); color: var(--primary); font-weight: 500; }
/* Строка переключателя вида под вкладками журнала: отступы как у ряда вкладок. */
.view-switch { margin: 12px 0 14px; }
/* Строка версии витрины: дата отчёта, выпуск, строки и сверка с черновиком. */
.version-line {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; margin: 12px 0;
  padding: 10px 14px; background: var(--card); border: 1px solid var(--line); border-radius: var(--radius-card); font-size: 14px;
}
.version-state { margin-left: auto; }
.mart-view td { vertical-align: middle; }
.num { text-align: right; font-variant-numeric: tabular-nums; }
th.num { text-align: right; }
/* Широкая справка: описание полей витрины с таблицей. */
.help-pop-body.help-wide { width: min(760px, 92vw); max-height: 70vh; overflow-y: auto; }
.help-wide .table-wrap { margin: 8px 0 0; }
/* Карточка куратора: запись МДЛП и кандидаты CRM столбцами одной таблицы.
   Поле и значение МДЛП закреплены слева, кандидаты прокручиваются вбок. */
/* Путь к экрану: раздел, из которого он открыт. Стоит над заголовком на всех
   вложенных экранах, чтобы место на платформе читалось без меню. */
.crumbs { margin: 0 0 4px; }
.crumbs a { color: var(--muted); text-decoration: none; }
.crumbs a:hover { color: var(--primary); }
/* Заголовок карточки куратора остаётся у верхнего края окна: при прокрутке
   длинного сравнения видно, какая запись открыта, и «Далее» стоит под рукой.
   Полоса идёт на всю ширину рабочей области и закрывает строки под собой. */
.page-head.card-head {
  position: sticky; top: 0; z-index: 4;
  margin: 0 -32px var(--head-gap); padding: 8px 32px;
  background: var(--bg); border-bottom: 1px solid var(--line);
}
/* Строка под названием записи держится в один ряд: длинное пояснение
   обрезается и читается целиком в подсказке. Иначе закреплённый заголовок
   рос бы на ряд и отнимал место у сравнения. */
.card-head .card-sub { flex-wrap: nowrap; min-width: 0; }
.card-head .card-sub > * { flex: none; white-space: nowrap; }
.card-head .card-sub > .card-why { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
/* Сначала уступает место хвост строки (код и дата справочника), пояснение
   обрезается последним: оно нужнее для решения. */
.card-head .card-sub > .note:last-child { flex: 0 1000 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
/* На узком экране заголовок с кнопками занимает несколько строк и закрыл бы
   половину окна, поэтому он уходит вместе со страницей. */
@media (max-width: 800px) {
  .page-head.card-head { position: static; margin-inline: 0; padding-inline: 0; border-bottom: 0; }
  .card-head .card-sub { flex-wrap: wrap; }
  .card-head .card-sub > .card-why { white-space: normal; }
}
/* Название записи и её состояние стоят в одной строке. */
.card-title { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 12px; }
.card-title h1 { margin: 0; }
.card-title .tag { padding: 3px 10px; font-size: 13px; border-radius: var(--radius); }
.card-title .tag::first-letter { text-transform: uppercase; }
.card-title .claim-line { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; font-size: 13px; }
/* Назначение: нейтральный индикатор без цвета смысла, цвет остаётся за состоянием записи. */
.assign { display: inline-flex; align-items: center; gap: 4px; padding: 3px 8px; border-radius: var(--radius); background: var(--muted-bg); color: var(--muted); font-size: 12px; }
.assign .btn-icon { width: 14px; height: 14px; margin: 0; }
.assign.is-mine { color: var(--ink); }
.card-sub { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 8px; margin: 4px 0 0; }
/* Карточка куратора: страница ограничена по ширине и стоит по центру, под
   заголовком одна рабочая область с одной рамкой. Внутри только разделители,
   вложенных рамок нет. */
.card-page { max-width: 1560px; margin-inline: auto; }
/* Три отдельных блока с зазором 16: полоса связи, сравнение и панель решений. Каждый
   в своей рамке на фоне страницы, поэтому границы читаются без напряжения. */
.workspace { margin: 12px 0 0; display: flex; flex-direction: column; gap: 16px; }
.compare-panel { background: var(--card); border: 1px solid var(--line); border-radius: var(--radius-card); margin-bottom: 12px; }
.compare-card { background: var(--card); border: 1px solid var(--line); border-radius: var(--radius-card); margin-bottom: 16px; }
.compare-card .compare-wrap { border-radius: 0 0 var(--radius-card) var(--radius-card); }
.decide-card.decide-card { background: none; border: 0; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 0; }
.decide-card p { margin: 10px 16px 0; }
.decide-card details.rare-ops { margin: 0; }
.decide-card details.rare-ops > summary { padding: 10px 16px; }
/* Список остальных кандидатов: строка на всю ширину блока, шеврон справа у края. */
.rare-ops.more-candidates > summary { display: flex; width: 100%; border: 0; border-radius: 0 0 var(--radius-card) var(--radius-card); }
.rare-ops.more-candidates > summary::after { margin-left: auto; }
.decide-lead { margin: 0; }
/* Решённая запись: два режима одной строкой кнопок, сравнение под ними закрыто. */
/* Ожидание данных: причина с единственным действием и таблица МДЛП в две колонки 35 и 65. */
.wait-block { display: grid; gap: 8px; padding: 16px 20px; margin-bottom: 16px; background: var(--card); border: 1px solid var(--line); border-radius: var(--radius-card); }
.decide-card .wait-block p { margin: 0; }
.wait-block .decide-modes { margin: 4px 0 0; }
.wait-data { margin-bottom: 16px; background: var(--card); border: 1px solid var(--line); border-radius: var(--radius-card); }
.wait-table { width: 100%; table-layout: fixed; border-collapse: collapse; }
.wait-table th { width: 35%; padding: 10px 12px; color: var(--muted); font-weight: 400; text-align: left; vertical-align: top; border-bottom: 1px solid var(--line); }
.wait-table td { padding: 10px 12px; vertical-align: top; border-bottom: 1px solid var(--line); overflow-wrap: anywhere; }
.wait-table tr:last-child th, .wait-table tr:last-child td { border-bottom: 0; }
.decide-card.is-manual .wait-data { display: none; }
.decide-modes { display: flex; flex-wrap: wrap; gap: 8px; margin: 12px 16px; }
.decide-card #decide-more > summary { display: none; }
.btn.ghost.is-on { background: var(--primary-soft); border-color: var(--primary); color: var(--primary); }
/* В режиме чтения выбор и кнопки решения скрыты: сравнение только для просмотра. */
.decide-card.is-reading .decide-bar, .decide-card.is-reading .cand-search, .decide-card.is-reading .search-panel,
.decide-card.is-reading #decide-more > p, .decide-card.is-reading .claim-warn, .decide-card.is-reading .replace-note { display: none; }
/* «Нет пары» в режиме чтения: без пустой строки сравнения и без подсказки «выберите карточку» (она для режима решения). */
.decide-card.is-reading .cand-toolbar.cand-empty, .decide-card.is-reading .state-note { display: none; }
/* Верх рабочей области: текущая связь слева, отметка «в работе» и история справа. */
.link-strip { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 6px 16px; padding: 10px 16px; background: var(--primary-soft); border: 1px solid var(--primary-line); border-radius: var(--radius-card); }
/* Подпись полосы: значок и слова цветом акцента, дальше обычный текст. */
.link-label { display: inline-flex; align-items: center; gap: 6px; margin-right: 12px; color: var(--primary); font-weight: 500; }
.link-label .btn-icon { width: 16px; height: 16px; margin: 0; }
.link-strip p { margin: 0; }
.link-main { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 0; }
.link-main .skeleton-block { display: grid; width: 120px; margin: 0 0 0 12px; }
.link-side { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 14px; }
.link-strip .btn { padding: 0 10px; }
.replace-note { margin: 8px 0 0; }
/* Панель истории уже боковой панели просмотра: две короткие ленты событий. */
.side-panel.history-drawer { width: min(460px, 94vw); }
.history-drawer h3 { margin: 16px 0 6px; font-size: 14px; }
/* Чужая отметка «в работе» у кнопок решения: внимание, не отказ. */
.decide-card .claim-warn { margin: 0 0 16px; padding: 8px 12px; border-radius: var(--radius); background: var(--warn-bg); color: var(--warn); font-size: 13px; }
/* Название карточки на кнопке подтверждения не растягивает панель решения. */
#confirm-label { display: inline-block; max-width: 44ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom; }
/* Вкладки кандидатов и легенда состояний стоят одной строкой над сравнением. */
.cand-toolbar { min-height: 56px; display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 4px 16px; padding: 8px 16px 0; border-bottom: 1px solid var(--line); background: var(--card); border-radius: var(--radius-card) var(--radius-card) 0 0; }
/* Название рабочей области и число кандидатов, подчёркивания нет, оно принадлежит настоящим вкладкам. */
.compare-heading { display: flex; align-items: center; gap: 8px; padding: 8px 0; }
.compare-heading h2 { margin: 0; font-size: 15px; font-weight: 500; }
.count-badge { padding: 2px 8px; border-radius: var(--radius); background: var(--muted-bg); color: var(--muted); font-size: 12px; }
/* Строка поиска всегда в полосе над сравнением, подсказки раскрываются под ней. */
.cand-search { flex: 1 1 300px; max-width: 440px; margin: 0 0 0 auto; }
.cand-search .search-field { display: block; position: relative; }
.cand-search input { width: 100%; box-sizing: border-box; height: var(--control-h); padding: 0 10px 0 34px; }
/* Вкладки и легенда не прижаты к линиям полосы: текст не ближе 8 px к линии над и под ним. */
.help-pop-body .compare-legend { margin: 0 0 8px; font-size: 12px; }
/* Отсутствующие данные приглушены токеном цвета, без прозрачности: прозрачность текста снижает контраст ниже нормы. */
.compare-table td > .note { color: var(--muted); }
/* Сообщение о состоянии записи и режим просмотра. */
.state-note { display: grid; gap: 2px; margin: 0 0 12px; padding: 12px 16px; border: 1px solid var(--warn-line); border-radius: var(--radius-card); background: var(--warn-bg); color: var(--warn); font-size: 13px; }
.decide-card .read-note { display: none; margin: 0; padding: 10px 16px; border-top: 1px solid var(--line); }
.decide-card.is-reading .read-note { display: block; }
.decide-card .search-panel { margin: 0; padding: 12px 16px; border: 0; border-bottom: 1px solid var(--line); border-radius: 0; background: var(--tile); }
/* Сравнение: столбцы данных равной ширины, МДЛП и кандидаты одинаково. */
.compare-wrap { overflow-x: auto; margin: 0; border: 0; background: transparent; }
/* Сетка: поле 150, МДЛП 29%, кандидаты поровну и не уже 210: ширина таблицы не меньше (150 + 210 на кандидата) / 0.71. Уже этого: прокрутка только таблицы. */
.compare-table { table-layout: fixed; width: 100%; min-width: calc((150px + (var(--cols, 3) - 1) * 210px) / 0.71); background: transparent; }
.compare-table th, .compare-table td { vertical-align: top; font-size: 13px; }
.compare-table tbody tr { height: 44px; }
.compare-table thead th { font-size: 13px; color: var(--ink); background: var(--tile); }
.compare-table .field-col { position: sticky; left: 0; z-index: 1; width: 150px; color: var(--muted); font-weight: 400; background: var(--card); }
.compare-table .source-col { position: sticky; left: 150px; z-index: 1; width: 29%; background: var(--card); border-right: 1px solid var(--line-strong); }
.compare-table thead .field-col, .compare-table thead .source-col { background: var(--tile); }
.compare-table .cand { font-weight: 400; }
/* Выбранный кандидат отмечен только шапкой столбца: тело столбца остаётся белым. */
/* Полоса и фон выбранного столбца строго в границах ячейки шапки. Полоса - псевдоэлемент у верхнего края
   ячейки, тени нет. Фон не выходит за ячейку, тело столбца остаётся белым. */
.compare-table .cand.is-picked { position: relative; background: var(--primary-soft); box-shadow: none; }
.compare-table .cand.is-picked::before { content: ""; position: absolute; top: 0; left: 0; right: 0; height: 3px; background: var(--primary); }
/* Метка источника в заголовке колонки сравнения: откуда значения под ней.
   Источник не бывает «верным» или «неверным», поэтому цвет метки о качестве не говорит. */
.src-tag { display: inline-block; padding: 1px 8px; border-radius: var(--radius); font-size: 12px; font-weight: 500; line-height: 1.5; white-space: nowrap; }
.src-mdlp { background: var(--line-strong); color: var(--ink); }
.card-head .tag, .workspace .tag { border-radius: var(--radius); }
.rare-ops.more-candidates { margin: 0; border-top: 1px solid var(--line); }
.search-filter { flex: 0 1 160px; }
/* Фильтры поиска одним компонентом: кнопка с иконкой, числом и шевроном справа, чипы применённых условий. */
.filter-bar { flex-basis: 100%; display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.filters-pop { position: relative; }
.filters-pop > summary { display: inline-flex; align-items: center; gap: 8px; min-height: 36px; padding: 6px 12px; border: 1px solid var(--line-strong); border-radius: var(--radius); background: var(--card); color: var(--ink); cursor: pointer; list-style: none; }
.filters-pop > summary::-webkit-details-marker { display: none; }
.filters-pop > summary:hover { background: var(--tile); }
.filters-pop > summary .btn-icon { margin: 0; }
.filters-pop > summary .filters-count { min-width: 20px; padding: 0 6px; border-radius: var(--radius); background: var(--primary-soft); color: var(--primary); font-size: 12px; text-align: center; }
.filters-pop > summary .filters-chevron { color: var(--muted); transition: transform .12s ease; }
.filters-pop[open] > summary .filters-chevron { transform: rotate(180deg); }
.filters-body { position: absolute; z-index: 6; top: calc(100% + 4px); left: 0; display: grid; gap: 12px; width: min(360px, 90vw); padding: 12px; background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow-pop); }
.filters-body .search-filter { flex: none; }
.catalog-filter-pop { flex: 0 0 auto; margin: 0; }
.catalog-filters-body {
  left: auto; right: 0; width: 360px; padding: 16px;
}
.catalog-filters-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.catalog-filters-head strong { font-size: 15px; font-weight: 500; }
.catalog-filters-body label { display: grid; gap: 4px; color: var(--ink); }
.catalog-filters-body input { width: 100%; min-width: 0; }
.catalog-filters-actions { display: flex; justify-content: flex-end; gap: 8px; }
.catalog-filter-chips { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 8px; }
.catalog-filter-chips .filter-chip { max-width: 100%; overflow-wrap: anywhere; }
.empty-state:has(.empty-icon) { display: flex; flex-direction: column; align-items: center; gap: 8px; min-height: 170px; justify-content: center; text-align: center; }
.empty-state:has(.empty-icon) > strong { color: var(--ink); font-weight: 500; }
.empty-state:has(.empty-icon) > .btn { margin-left: 0; }
.empty-icon { display: grid; place-items: center; width: 44px; height: 44px; border-radius: 50%; background: var(--primary-soft); color: var(--primary); }
.empty-icon .btn-icon { width: 24px; height: 24px; }
@media (max-width: 700px) {
  .catalog-filters-body { width: min(360px, calc(100vw - 32px)); }
}
.filter-chip { display: inline-flex; align-items: center; gap: 6px; min-height: 28px; padding: 2px 8px 2px 10px; border: 1px solid var(--primary-line); border-radius: var(--radius); background: var(--primary-soft); color: var(--primary); font-size: 13px; text-decoration: none; }
.filter-chip:hover { background: var(--tile); }
.filter-chip .btn-icon { width: 12px; height: 12px; margin: 0; }
.search-panel.is-suppressed { display: none; }
/* Связи нет: полоса нейтральная, голубой цвет остаётся за действующей связью и не спорит с выбором CRM. */
.link-strip.is-empty { background: var(--tile); border-color: var(--line); }
.link-strip.is-empty .link-label { color: var(--muted); }
/* Итог по столбцу: числа в строку, перенос целыми словами. */
/* Заголовок: выбор, номер и оценка в первой строке, дальше название и адрес. */
.compare-table .choice-head { display: block; cursor: pointer; }
.choice-top { display: flex; align-items: center; gap: 8px; margin-bottom: 4px; }
.cand-no { color: var(--muted); font-size: 12px; }
.cand-score { margin-left: auto; font-size: 15px; font-weight: 500; color: var(--primary); }
/* Строка выбора фиксированной высоты: номер, балл и название стоят на одной линии у всех столбцов. */
.compare-table .choice-top { min-height: 28px; }
/* Название одной строкой с многоточием, полное - в подсказке шапки: шапка не растёт и не выравнивается по адресу. */
.compare-table .choice-head strong { display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; line-height: 20px; }
.cand-toolbar .help-pop { margin: 0 0 8px; }
.cand-found { margin-left: auto; }
/* Ячейка сравнения: значение, справа на той же строке точка смысла и слово.
   Строки равны по высоте, пустое значение приглушено и без точки. */
.compare-table td.cmp { border-radius: 0; padding: 10px 12px; background: none; color: var(--ink); line-height: 1.45; }
/* Раскладка содержимого - во внутреннем блоке ячейки, у самой ячейки display не меняется (руководство, раздел 0, п. 27). */
.cmp-body { overflow-wrap: anywhere; }
/* Значение первой строкой, объяснение сравнения второй. */
.compare-table .cmp-value { display: block; overflow-wrap: anywhere; }
.compare-table .cmp-state { display: block; margin: 4px 0 0; font-size: 12px; color: var(--muted); }
.compare-table .cmp-state::before { content: ""; display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 6px; background: var(--line-strong); }
.compare-table td.cmp-same .cmp-state::before, .cmp-dot.cmp-same { background: var(--ok); }
.compare-table td.cmp-close .cmp-state::before, .cmp-dot.cmp-close { background: var(--warn-mid); }
.compare-table td.cmp-differ .cmp-state::before, .cmp-dot.cmp-differ { background: var(--bad); }
.compare-table .id-row td, .compare-table .id-row th { border-bottom: 0; }
.cmp-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; padding: 0; margin: 0 4px 0 8px; }
.compare-legend { margin: 0; }
/* «Только различия» и строка-сводка: переключатель стоит справа от заголовка, сводка - в конце тела таблицы. */
.compare-heading .only-diff { margin-left: auto; font-size: 13px; color: var(--ink); }
.compare-table tr.hidden-same td.hidden-same-cell, .compare-table tr.other-hidden-same td.hidden-same-cell { padding: 8px 12px; color: var(--muted); background: var(--tile); }
.hidden-same-cell .hidden-same-toggle { margin-left: 8px; }
/* Закреплённая шапка кандидатов: копия шапки таблицы. Её место - под заголовком карточки, видна, когда основная шапка ушла вверх. */
.compare-stick { position: sticky; top: 0; z-index: 3; overflow: hidden; visibility: hidden; background: var(--card); }
.compare-stick.is-on { visibility: visible; }
.compare-head-copy { table-layout: fixed; background: transparent; }
.compare-head-copy thead th { cursor: pointer; border-bottom: 1px solid var(--line-strong); }
/* Сообщение о замене карточки: всплывает в углу поверх страницы, вёрстку таблицы не сдвигает, уходит само. */
.replace-toast { background: var(--card); color: var(--ink); border-color: var(--line-strong); }
.replace-toast .linklike { font-weight: 500; }
/* Заменивший или вернувшийся столбец ненадолго подсвечивается фоном шапки, класс снимает скрипт. */
.compare-table th.cand.is-fresh { background: var(--primary-soft); transition: background-color 0.3s; }
/* Раздел «Остальные атрибуты»: свёрнутая строка - полоса с числом различий, раскрытые строки - как строки ролей. */
.compare-table tr.other-head td { padding: 10px 12px; background: var(--primary-soft); border-bottom: 1px solid var(--line); }
.compare-table tr.other-head th { padding: 10px 12px; background: var(--primary-soft); border-bottom: 1px solid var(--line); font-weight: 500; color: var(--primary); }
.other-toggle { font-weight: 500; color: var(--primary); text-align: left; }
.other-chevron { display: inline-block; width: 14px; height: 14px; margin-left: 6px; vertical-align: -2px; transition: transform 0.15s; }
.other-toggle[aria-expanded="true"] .other-chevron { transform: rotate(180deg); }
.other-state { margin-left: 8px; font-size: 12.5px; }
.compare-table tr.other-row th, .compare-table tr.other-row td { padding: 10px 12px; min-height: 44px; line-height: 1.45; border-bottom: 1px solid var(--line-soft); }
.compare-table tr.other-row th.field-col { font-weight: 400; color: var(--muted); }
/* Таблица кандидатов строками: одна разметка для «Ещё кандидатов системы» и выдачи поиска. Шириной
   1000 px умещается без прокрутки вбок: столбцы ролей узкие, название переносится по словам. */
.cand-rows { width: 100%; table-layout: fixed; border-collapse: collapse; }
.cand-rows caption { text-align: left; padding: 8px 10px; }
.cand-rows th, .cand-rows td { padding: 6px 8px; font-size: 13px; line-height: 1.4; vertical-align: top; text-align: left; border-bottom: 1px solid var(--line); }
.cand-rows th.role-col { font-weight: 400; color: var(--muted); overflow-wrap: anywhere; }
.cand-rows .score-col { width: 56px; }
.cand-rows .role-col { width: 78px; }
.cand-rows .action-col { width: 118px; text-align: right; }
.cand-rows .crm-col strong { display: block; overflow-wrap: anywhere; }
.cand-rows .crm-sub { display: block; margin-top: 2px; overflow-wrap: anywhere; }
.cand-rows .role-cell { text-align: center; }
.cand-rows .cmp-dot { margin: 0; }
.cand-rows tr[hidden] { display: none; }
.cmp-dot.cmp-missing { background: var(--line-strong); }
.cand-legend { margin: 6px 0 0; font-size: 12px; }
.more-candidates[hidden] { display: none; }
/* Кнопки решения: слева группой, справка справа. Рамки у панели нет: её верх - разделитель. */
.decide-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; padding: 12px 16px; border: 1px solid var(--line); border-radius: var(--radius-card); position: sticky; bottom: 8px; z-index: 3; background: var(--card); }
.decide-bar .help-btn { margin-left: auto; font-size: 13px; }
/* Выбранная карточка слева в панели решения: название, код и сходство, чтобы подтверждать не вслепую. */
.selected-score { display: grid; gap: 2px; min-width: 0; }
.selected-card { display: grid; gap: 2px; min-width: 0; max-width: 380px; padding-right: 16px; border-right: 1px solid var(--line); }
.selected-card .sel-title { overflow-wrap: anywhere; }
.sel-label { color: var(--muted); font-size: 12px; }
.selected-score .sel-score { font-size: 15px; color: var(--primary); }
.decide-card.is-reading .selected-card, .decide-card.is-reading .selected-score { display: none; }
.decide-revert { margin-left: 12px; }
/* Журнал решений: строки по дням, заголовок дня - строкой таблицы. */
.log-table td { vertical-align: middle; }
.log-table .day-row th { background: var(--tile); color: var(--ink); font-size: 13px; font-weight: 500; padding: 6px 10px; }
.log-table .when { width: 1%; }
/* Задания: карточка действия, которое пора сделать, обведена. */
.operations-head { align-items: flex-start; }
.operations-head .card-sub { margin: 4px 0 0; }
/* Задания: блоки заданий цепочки, шаги и полоса прошлых запусков */
.ops-status { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 12px; margin: 0 0 16px; }
.ops-chain, .ops-service { margin-top: 8px; }
.ops-schedules { display: inline-flex; align-items: center; gap: 8px; margin-left: auto; }
.ops-schedules form { display: inline-flex; margin: 0; }
.job-list { display: grid; gap: 16px; margin: 12px 0 0; }
/* Цепочка и сервисные операции - одна таблица одного вида: колонки на одной позиции (В21а). */
/* Колонки состояния, последнего запуска, запуска, действий и раскрытия - ширина в пикселях по самому широкому
   содержимому (Д135, замер шрифтом Segoe UI 15 px и 13 px): «ежедневно в 00:00 UTC» 160 px + поля 16 = 180 (колонка
   «Запуск»); «публиковалась» 103 px + точка и промежуток 12 + поля 16 = 131, берём 136; «не запускалось» 13 px 90 px
   + поля 16 = 106, берём 110; главная кнопка 144 (подпись «Опубликовать…» 109 px + поля 28), отступ 6, меню 36,
   поля ячейки 16, итого 202 с запасом до 204; «Шаги 12» с шевроном 104. Название занимает остаток блока: при 855 px
   это около 121 px, при 1360 px - больше. Без прокрутки в блоке 855 px (Д103, СО-49, Д115, Д135).
   Колонки состояния и последнего запуска растут долей блока, когда место есть (Д138): «не публиковалась» и «14:36 ·
   по расписанию» стоят в одну строку на широком экране, минимум - прежние 136 и 110 px, поэтому блок 855 px не меняется. */
.chain-summary.job-table { table-layout: fixed; width: 100%; }
.job-table col.job-col-state { width: max(136px, 13%); }
.job-table col.job-col-last { width: max(110px, 12%); }
.job-table col.job-col-launch { width: 180px; }
.job-table col.job-col-actions { width: 204px; }
.job-table col.job-col-toggle { width: 104px; }
/* Вторая строка ячейки строки задания (пояснение, состояние, время запуска, полнота сопоставления) переносится
   не больше чем на две строки (Д111/К28, СО-60, СО-61); полный текст - в подсказке ячейки. */
.job-table tr.chain-group .job-cell > .note { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; overflow-wrap: break-word; }
/* Содержимое ячейки строки задания - в блоке `job-cell` (Д140): высота строки - резерв 4.75rem при полях ячейки 8 px
   сверху и снизу и линии 1 px, то есть содержимое до 59 px. Всё, что длиннее, обрезается в блоке, строка не растёт:
   строки одного вида одной высоты при любой длине текста (Д135). Полный текст - в подсказке ячейки. */
.job-table tbody tr.chain-group .job-cell { max-height: calc(4.75rem - 17px); overflow: hidden; overflow-wrap: anywhere; }
/* Строки одного вида одной высоты (Д135): звенья между собой и сервисные операции между собой. Ячейки стоят сверху,
   первые строки названия, состояния и времени на одном уровне, высота строки - не меньше резерва 4.75rem, длинное
   содержимое растит строку, поэтому второе поле каждой ячейки ограничено двумя строками. Проверка по замеру - в
   браузерном наборе (test_browser_alignment.py, стадия browser). */
.job-table tbody tr.chain-group { height: 4.75rem; }
.job-table tbody tr.chain-group > td:not(.chain-actions) { vertical-align: top; }
/* Состояние переносится по словам внутри своей колонки (Д135): «не публиковалась» из колонки 136 px не выходит на
   колонку «Последний запуск». Колонка шире самого длинного слова с точкой. */
.job-table .chain-state-inline { white-space: normal; }
/* Состояние задания - точка с подписью, цвета как у блоков экрана: идёт синий, на паузе жёлтый, ожидает серый. */
.job-table .chain-running .chain-dot { background: var(--primary); }
.job-table .chain-waiting .chain-dot { background: var(--muted); }
.job-table .is-paused .chain-dot { background: var(--warn); }
/* Звено цепочки - строка общей таблицы списка с раскрытием (та же разметка, что «Источники данных» на «Сводке»). */
/* Колонка действий (В21в): одна главная кнопка постоянной ширины, меню «⋮» справа. Пустое место той же ширины.
   Ячейка - flex (`action-cell`): слоты стоят в одном порядке и на одних координатах, пробелы разметки на них не влияют.
   Ширина 9rem - по самой длинной подписи «Опубликовать…» (Д115). */
.job-table td.chain-actions { white-space: nowrap; }
.job-table .action-cell { display: flex; align-items: center; }
.job-table .action-cell > * { flex: none; }
.job-table .action-main { width: 9rem; max-width: 100%; justify-content: center; margin-right: 6px; white-space: nowrap; }
.job-table .action-empty { display: inline-block; height: var(--control-h); }
.job-table .row-menu-empty { display: inline-block; flex: none; width: var(--control-h); height: var(--control-h); }
.job-table .action-form { display: inline-flex; margin: 0; }
/* Меню строки «⋮» - один компонент на всех экранах (Д128), без привязки к экрану: кнопка размером `--control-h`,
   всплывающий список, пункты, опасный и неактивный пункт. Системный знак раскрытия скрыт. Правка под один экран -
   модификатор, не перенос правил внутрь экрана. Таблица, где стоит меню, получает `has-menus` (без прокрутки вбок). */
.row-menu { position: relative; display: inline-block; vertical-align: middle; }
.row-menu > summary { list-style: none; display: inline-flex; align-items: center; justify-content: center; width: var(--control-h); height: var(--control-h); border: 1px solid var(--line-strong); border-radius: var(--radius); background: var(--card); color: var(--ink); font-size: 16px; line-height: 1; cursor: pointer; }
.row-menu > summary::-webkit-details-marker { display: none; }
.row-menu > summary:hover { background: var(--bg); }
.row-menu > summary:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
.row-menu-body { position: absolute; right: 0; top: calc(100% + 4px); z-index: 20; display: grid; gap: 2px; min-width: 220px; padding: 6px; background: var(--card); border: 1px solid var(--line); border-radius: var(--radius-card); box-shadow: var(--shadow-pop); text-align: left; }
.row-menu-item { display: block; width: 100%; padding: 7px 10px; border: 0; border-radius: var(--radius); background: none; color: var(--ink); font: inherit; font-size: 13px; line-height: 1.3; text-align: left; text-decoration: none; white-space: nowrap; cursor: pointer; }
.row-menu-item:hover { background: var(--tile); }
.row-menu-item:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }
.row-menu-item:disabled { opacity: .45; cursor: not-allowed; }
.row-menu-item.is-danger { color: var(--bad); }
.chain-summary td.chain-actions .btn { height: var(--control-h); margin-right: 6px; }
.chain-summary td.chain-actions form { display: inline-flex; margin: 0; }
.chain-summary .chain-group.is-live td { background: var(--primary-soft); }
.chain-stopping .chain-dot { background: var(--primary); }
.job-block { display: grid; gap: 8px; padding: 16px 18px; background: var(--card); border: 1px solid var(--line); border-radius: var(--radius-card); }
.job-block.is-running, .job-block.is-stopping { border-color: var(--primary); }
.job-block.is-failed { border-color: var(--bad); }
.job-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.job-head h3 { margin: 0; font-size: 15px; font-weight: 500; }
.job-meta, .job-wait { margin: 0; line-height: 1.35; }
.job-actions { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }
.job-actions form { margin: 0; }
.job-steps { margin: 0; }
.job-step-list { list-style: none; margin: 0; padding: 4px 0 0; }
.job-step { display: grid; grid-template-columns: 14px minmax(0, 1fr) auto 72px; align-items: baseline; gap: 4px 10px; padding: 6px 0; font-size: 13px; }
.job-step-dot { width: 10px; height: 10px; margin-top: 4px; border-radius: 50%; background: var(--line-strong); }
.job-step.is-success .job-step-dot { background: var(--ok); }
.job-step.is-failed .job-step-dot { background: var(--bad); }
.job-step.is-running .job-step-dot, .job-step.is-stopping .job-step-dot { background: var(--primary); }
.job-step.is-stopped .job-step-dot { background: var(--warn-mid); }
.job-step-word { color: var(--muted); }
.job-step-time { color: var(--muted); text-align: right; font-variant-numeric: tabular-nums; }
.job-history { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.run-strip { display: inline-flex; gap: 4px; }
.run-cell { width: 10px; height: 10px; border-radius: 2px; background: var(--line-strong); }
.run-cell.is-success { background: var(--ok); }
.run-cell.is-failed { background: var(--bad); }
.run-cell.is-running, .run-cell.is-stopping { background: var(--primary); }
.run-cell.is-stopped { background: var(--warn-mid); }
.job-releases { display: grid; gap: 8px; margin-top: 4px; padding-top: 12px; border-top: 1px solid var(--line-soft); }
.job-releases h4 { margin: 0; font-size: 13px; font-weight: 500; }
.job-releases .table-wrap { margin: 0; }
.stop-dialog { width: min(100% - 32px, 34rem); }
.stop-reason { display: grid; gap: 4px; margin: 12px 0; }
.stop-consequences { margin: 8px 0 12px; padding-left: 18px; font-size: 13px; }
.btn.is-danger { background: var(--bad); border-color: var(--bad); color: var(--card); }
.btn.is-danger:hover { filter: brightness(0.9); }
.tag.stopping { background: var(--warn-bg); color: var(--warn); }
/* Карточки операций ряда делят пять строк сетки: название, состояние, время, текст, действия.
   Строка текста у всех карточек одна и та же, даже когда в ней пусто, поэтому кнопки стоят на одной линии. */
/* Звенья ряда выравниваются по строкам: каждое звено занимает четыре строки сетки и берёт их подсеткой.
   Название, состояние и время стоят на одной высоте во всех звеньях ряда, пустая строка пояснения не сдвигает их. */
.chain.chain-wide { display: grid; grid-template-columns: repeat(5, minmax(172px, var(--tile-max))); grid-template-rows: repeat(4, auto); gap: 0 24px; margin: 12px 0 0; overflow-x: auto; padding-bottom: 2px; }
.chain.chain-wide .chain-step { grid-row: span 4; display: grid; grid-template-rows: subgrid; row-gap: 4px; min-width: 172px; padding: 12px 12px 12px 28px; overflow-wrap: anywhere; }
.chain.chain-wide .chain-step > .chain-dot { left: 12px; top: 15px; }
/* Между звеньями стрелок нет: порядок виден по расположению. Раскрытие «Подробнее» - строка-кнопка без отступа сверху. */
.chain-wide .rare-ops { margin: 0; }
.chain-meta { color: var(--muted); font-size: 12px; line-height: 1.35; }
/* Меню действий строки: редкие и опасные действия не стоят кнопками в каждой строке. */
.row-menu-body .inline-role { display: flex; gap: 6px; flex-wrap: wrap; }
.linklike.is-danger { color: var(--bad); }
.create-form { display: flex; flex-direction: column; gap: 8px; }
.create-form label { display: flex; flex-direction: column; gap: 2px; font-size: 13px; color: var(--muted); }
.create-form .btn { align-self: flex-start; }
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
/* Таблица с меню строк не обрезает всплывающее меню. */
.table-wrap.has-menus { overflow: visible; }
/* Потребитель витрин: карточка с метками витрин и строкой на каждый ключ. */
.client-card { margin: 0 0 10px; gap: 6px; }
.client-card h2 { margin: 0; font-size: 16px; }
.client-card.is-idle { background: var(--bg); }
.client-marts { display: flex; flex-wrap: wrap; gap: 4px; }
.key-row { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; padding: 6px 0; border-top: 1px solid var(--line); font-size: 14px; }
.key-row form { margin: 0 0 0 auto; }
.client-foot { margin: 4px 0 0; }
/* Окно с формой по центру экрана: длинная форма во всплывающей панели у
   кнопки уходила за нижний край. */
.form-dialog {
  width: min(100% - 32px, 34rem); max-height: calc(100vh - 48px); overflow-y: auto;
  padding: 18px 22px 20px; border: 1px solid var(--line); border-radius: var(--radius-card);
  background: var(--card); color: var(--ink); box-shadow: var(--shadow-dialog);
}
.form-dialog::backdrop { background: var(--backdrop); }
.form-dialog .create-form { gap: 10px; }
.dialog-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.dialog-head h2 { margin: 0; font-size: 17px; }
.dialog-close {
  width: 28px; height: 28px; padding: 0; border: 0; border-radius: 50%;
  background: transparent; color: var(--muted); font: inherit; font-size: 18px; line-height: 1; cursor: pointer;
}
.dialog-close:hover { background: var(--tile); color: var(--ink); }
.field-row { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 12px; }
.field-hint { margin: -6px 0 0; font-size: 12px; }
.create-form .field-short { width: 11rem; }
.dialog-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 6px; }
.create-form .dialog-actions .btn { align-self: auto; }
/* Перечень витрин растёт до десятка: по строке на витрину, код справа,
   длинный перечень прокручивается внутри рамки. */
.mart-pick { display: flex; flex-direction: column; gap: 6px; margin: 0; padding: 0; border: 0; }
.mart-pick legend { padding: 0 0 4px; color: var(--muted); font-size: 13px; }
.mart-pick .check-field { display: flex; flex-direction: row; align-items: center; gap: 8px; color: var(--ink); font-size: 14px; }
.pick-list { margin: 2px 0 0 24px; max-height: 15rem; overflow-y: auto; border: 1px solid var(--line); border-radius: var(--radius); }
.create-form .pick-row {
  display: flex; flex-direction: row; align-items: center; justify-content: space-between; gap: 12px;
  padding: 7px 10px; border-bottom: 1px solid var(--line); color: var(--ink); font-size: 14px;
}
.pick-row:last-child { border-bottom: 0; }
.pick-row span { display: flex; align-items: center; gap: 8px; }
.pick-row code { color: var(--muted); font-size: 12px; }
@media (max-width: 560px) { .field-row { grid-template-columns: 1fr; } }
/* Журнал выдачи: по строке на витрину в ячейке версии потребителя. */
.read-line { padding: 2px 0; }
.read-line + .read-line { border-top: 1px solid var(--line-soft); margin-top: 4px; padding-top: 6px; }
.access-consumers td { vertical-align: top; }
/* Паспорт и настройки витрины. */
.metric-value.metric-small { font-size: 16px; }
.source-chain { display: flex; flex-wrap: wrap; gap: 6px 24px; margin: 10px 0 0; padding: 0; list-style: none; font-size: 13px; }
.source-chain li { position: relative; padding: 4px 10px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--card); }
.source-chain li + li::before { content: "→"; position: absolute; left: -18px; color: var(--muted); }
.source-chain li.is-target { border-color: var(--line-strong); font-weight: 500; }
.check-list { margin: 8px 0 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 6px; font-size: 14px; }
.check-list .is-ok span { color: var(--ok); }
.check-list .is-bad { color: var(--bad); }
.note.is-bad { color: var(--bad); }
.passport-columns td { vertical-align: top; }
.settings-grid { display: grid; grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr); gap: 12px; align-items: start; max-width: var(--settings-max); }
.settings-grid .dash-card { gap: 10px; }
.setting-history { margin: 0; padding: 0; list-style: none; }
.setting-history li { display: flex; flex-direction: column; gap: 2px; padding: 8px 0; border-top: 1px solid var(--line); }
.setting-history .revert-open { float: right; }
.create-form .inline-date { display: inline-block; width: auto; margin-left: 6px; }
/* Подтверждение GTIN на вкладке «Область данных»: поля серии и срока по содержимому в строке,
   основание на всю ширину формы, форма не шире 640 px, кнопка под полями слева. */
.scope-confirm { max-width: 640px; }
.confirm-fields { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-end; }
.confirm-fields input[name="gtin_code"] { width: 16ch; }
.confirm-fields input[name="batch"] { width: 16ch; }
.confirm-fields input[name="valid_to"] { width: auto; }
.scope-confirm .basis-field, .scope-confirm .basis-field input { width: 100%; }
@media (max-width: 900px) { .settings-grid { grid-template-columns: 1fr; } }
/* Адрес обращения в журнале выдачи: длинный путь переносится внутри ячейки. */
.path-code { word-break: break-all; }
/* Витрина с обеими сторонами связи: заголовки групп CRM и МДЛП, колонки МДЛП на
   мягкой подложке, первая колонка закреплена при прокрутке вбок. */
.mart-sides .side-row th { padding-bottom: 2px; color: var(--ink); font-weight: 500; }
.mart-sides .side-row th.side-crm { border-bottom: 2px solid var(--line-strong); }
.mart-sides .side-row th.side-mdlp { border-bottom: 2px solid var(--primary-line); }
.mart-sides .side-mdlp { background: var(--primary-soft); }
.mart-sides { min-width: 1700px; }
.mart-sides th { white-space: nowrap; }
.mart-sides .mdlp-name { min-width: 160px; max-width: 220px; }
.mart-sides td.sticky-col { min-width: 170px; max-width: 220px; }
.mart-sides .sticky-col { position: sticky; left: 0; z-index: 1; background: var(--card); box-shadow: 1px 0 0 var(--line); }
.mart-sides tr:hover > .sticky-col { background: var(--row-hover); }

/* Пороги сопоставления: поля по сущностям, полоса зон балла, оценка исходов. */
.policy-entity { border: 1px solid var(--line); border-radius: var(--radius-card); padding: 14px 16px; display: flex; flex-direction: column; gap: 10px; }
.policy-entity h2, .policy-entity h3 { margin: 0; font-size: 15px; font-weight: 500; }
.policy-entity-head { display: flex; align-items: center; gap: 8px; }
.policy-entity .help-pop-body { width: min(520px, 86vw); }
.policy-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 10px 14px; }
.policy-grid label { display: flex; flex-direction: column; gap: 2px; font-size: 13px; color: var(--muted); }
.policy-grid input { width: 6.5rem; font-variant-numeric: tabular-nums; }
.policy-grid input.is-changed { border-color: var(--primary); background: var(--primary-soft); }
.policy-grid .field-hint { margin: 0; }
.zone-band { display: flex; height: 22px; border-radius: var(--radius); overflow: hidden; font-size: 12px; }
.zone-band span { display: flex; align-items: center; justify-content: center; white-space: nowrap; overflow: hidden; }
.zone-none { background: var(--muted-bg); color: var(--muted); }
.zone-review { background: var(--warn-bg); color: var(--warn); }
.zone-auto { background: var(--ok-bg); color: var(--ok); }
.zone-scale { display: flex; justify-content: space-between; font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; margin-top: -6px; }
.estimate { width: 100%; border-collapse: collapse; font-size: 13px; font-variant-numeric: tabular-nums; }
.estimate th { text-align: left; font-weight: 500; color: var(--muted); padding: 4px 8px 4px 0; border-bottom: 1px solid var(--line); }
.estimate td { padding: 5px 8px 5px 0; border-bottom: 1px solid var(--line-soft); }
.estimate .num { text-align: right; }
.estimate .is-moved { color: var(--warn); }
.notice > form { display: inline-block; margin-left: 8px; }

/* Каналы, пороги и журнал уведомлений сохраняют одну ширину и порядок действий. */
.alert-section { gap: 16px; margin: 0 0 12px; max-width: var(--block-max); }
.alert-section-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.alert-section-head h2 { margin: 0; font-size: 16px; font-weight: 500; }
.alert-section-head .note { margin: 2px 0 0; }
.alert-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.alert-channel-list { border-top: 1px solid var(--line); }
.alert-channel-row { display: flex; align-items: center; gap: 12px; padding: 10px 0; border-bottom: 1px solid var(--line-soft); }
.alert-channel-name { display: flex; flex: 1; flex-direction: column; min-width: 0; overflow-wrap: anywhere; }
.alert-channel-row > .btn { margin-left: auto; }
.alert-check-form { margin: 0; }
.alert-threshold-form { gap: 14px; }
.alert-threshold-group { display: flex; flex-direction: column; gap: 10px; }
.alert-threshold-group + .alert-threshold-group { padding-top: 14px; border-top: 1px solid var(--line); }
.alert-threshold-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; }
.alert-threshold-grid label { gap: 4px; }
.alert-value { display: flex; align-items: center; gap: 8px; color: var(--ink); }
.alert-value input { width: 7rem; max-width: 100%; font-variant-numeric: tabular-nums; }
.alert-threshold-form .card-actions { margin: 0; padding-top: 2px; }
.alert-history { min-width: 620px; }
.alert-history td { vertical-align: middle; }
.alert-number { font-variant-numeric: tabular-nums; }
.alert-dialog { width: min(100% - 32px, 42rem); }
.alert-dialog .dialog-head { margin-bottom: 14px; }
.alert-options { display: flex; align-items: center; gap: 4px 16px; flex-wrap: wrap; margin: 0; padding: 0; border: 0; }
.alert-options legend { padding: 0; color: var(--muted); font-size: 13px; }
.alert-options .note { flex-basis: 100%; }
.create-form label.alert-check { display: inline-flex; flex-direction: row; align-items: center; gap: 6px; color: var(--ink); }
.alert-check input { margin: 0; }
@media (max-width: 900px) { .alert-threshold-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px) {
  .alert-threshold-grid { grid-template-columns: 1fr; }
  .alert-channel-row { flex-wrap: wrap; }
  .alert-channel-row > .btn { margin-left: 0; }
}

/* Узкий экран, сравнение на карточке: закреплён только столбец полей. Два
   закреплённых столбца занимают всю ширину телефона, и кандидаты не видны. */
@media (max-width: 760px) {
  .compare-table .source-col { position: static; border-right: 1px solid var(--line); }
  .compare-table .field-col { width: 92px; }
  .compare-table { min-width: calc(92px + var(--cols, 3) * 200px); }
  /* Плитки-счётчики идут по две в ряд: столбцом они уводили список за первый экран. */
  .tile-row { grid-template-columns: repeat(2, minmax(0, var(--tile-max))); }
}
/* Подпись поля из реестра источников начинается со строчной: на экране - с прописной. */
.field-title { display: block; }
.field-title::first-letter { text-transform: uppercase; }

/* Скелет ожидания при опросе. */
.skeleton-block { display: grid; gap: 8px; margin: 0 0 12px; }
.skeleton-line {
  display: block; height: 12px; border-radius: 4px;
  background: linear-gradient(90deg, var(--tile), var(--line-soft), var(--tile));
  background-size: 200% 100%;
  animation: skeleton-pulse 1.2s ease-in-out infinite;
}
.skeleton-line.short { width: 40%; }
@keyframes skeleton-pulse {
  0% { background-position: 100% 0; }
  100% { background-position: -100% 0; }
}

/* Модель данных и описание набора: правка полей прямо в таблице. */
.fields-form td input:not([type="checkbox"]), .fields-form td select { width: 100%; min-width: 120px; }
.entity-settings { display: flex; flex-wrap: wrap; gap: 12px 16px; align-items: flex-end; margin-top: 12px; }
.entity-settings label { display: flex; flex-direction: column; gap: 4px; min-width: 16rem; font-size: 12px; color: var(--muted); }
.entity-settings .check-field { flex-direction: row; align-items: center; min-width: 0; color: var(--ink); font-size: 13px; }
.fields-actions { align-items: center; margin-top: 14px; }

/* Ошибка ввода под своим полем: цвет отказа и слова, рамка поля того же цвета. */
.field-error { display: block; margin-top: 4px; color: var(--bad); font-size: 12px; white-space: normal; }
[aria-invalid="true"] { border-color: var(--bad) !important; }

/* Боковая панель просмотра записи: справа на всю высоту окна, поверх списка. */
.side-panel {
  position: fixed; inset: 0 0 0 auto; width: min(760px, 94vw); height: 100vh; max-height: none; max-width: none;
  margin: 0; padding: 16px 20px 24px; overflow-y: auto;
  border: 0; border-left: 1px solid var(--line); background: var(--bg); color: var(--ink);
  box-shadow: var(--shadow-dialog);
}
.side-panel::backdrop { background: var(--backdrop); }
.side-panel .dialog-head { position: sticky; top: -16px; z-index: 5; margin: -16px -20px 12px; padding: 12px 20px; background: var(--bg); border-bottom: 1px solid var(--line); }
.side-panel-actions { display: flex; align-items: center; gap: 8px; }
/* Кнопка закрытия панели стоит в строке заголовка: в углу всплывающего слоя ей здесь не место. */
.side-panel-actions .pop-close { position: static; }
.side-panel-body > :first-child { margin-top: 0; }
/* Заголовок раскрывающегося блока, нужный только для действия, в панели не показан: блок раскрыт. */
.side-panel summary[data-panel-skip] { display: none; }
/* Закреплённые части страницы записи в панели идут в потоке: закреплён заголовок самой панели. */
.side-panel .page-head.card-head { position: static; margin: 0 0 var(--head-gap); padding: 0; border: 0; }
.side-panel .sticky-head thead th { position: static; }

/* Формы настроек не растягиваются на всю ширину рабочей области: длинная
   строка поля и подписи читается хуже, и кнопка уходит далеко от полей. */
.form-width { max-width: var(--block-max); }

/* Пользователь просил систему убрать движение: ожидание и отклик остаются,
   вращение, мерцание и сдвиг - нет. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; }
  .btn:active, .btn.is-pressed { transform: none; }
  .btn.is-busy::after { border-right-color: var(--on-fill); opacity: .6; }
}

/* Общий вид полей, списков, многострочных полей и флажков на всех экранах: рамка, радиус, фон, шрифт, цвет и отступы
   одни. Правила экранов и контейнеров задают только раскладку и ширину, вид поля не переопределяют. Селекторы в :where
   имеют нулевую специфичность: правило раскладки перекрывает их без опоры на порядок в файле. */
:where(select, input:not([type]), input[type="text"], input[type="search"], input[type="number"], input[type="password"],
  input[type="email"], input[type="url"], input[type="tel"], input[type="date"], input[type="time"], textarea) {
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background: var(--card);
  color: var(--ink);
  font: inherit;
}
:where(select, input:not([type]), input[type="text"], input[type="search"], input[type="number"], input[type="password"],
  input[type="email"], input[type="url"], input[type="tel"], input[type="date"], input[type="time"]) {
  padding: 0 10px;
}
:where(textarea) { padding: 8px 10px; }
:where(select, input:not([type]), input[type="text"], input[type="search"], input[type="number"], input[type="password"],
  input[type="email"], input[type="url"], input[type="tel"], input[type="date"], input[type="time"], textarea):disabled {
  background: var(--tile);
  color: var(--muted);
  cursor: not-allowed;
}
:where(input[type="checkbox"]) { accent-color: var(--primary); vertical-align: middle; }

/* Одна высота у всех однострочных полей и списков на всех экранах: поле и кнопка в строке стоят на одной линии.
   Правило стоит последним, чтобы отступы отдельных экранов не возвращали прежнюю высоту. Многострочное поле не затрагивается. */
select,
input:not([type]),
input[type="text"], input[type="search"], input[type="number"], input[type="password"],
input[type="email"], input[type="url"], input[type="tel"], input[type="date"], input[type="time"] {
  box-sizing: border-box;
  height: var(--control-h);
  padding-top: 0;
  padding-bottom: 0;
}

/* Карточки экрана «Задания»: заголовок и плашка состояния в одной строке. */
.publish-dialog { width: min(100% - 32px, 36rem); }
.publish-facts { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 8px 16px; margin: 12px 0; }
.publish-facts dt { color: var(--muted); }
.publish-facts dd { margin: 0; }

/* Панель «Все атрибуты» на карточке. */
.side-panel.attrs-drawer { width: min(500px, 100vw); }
/* Ссылка «Все атрибуты» в шапке столбца, третья строка шапки: высота одна у всех столбцов, «атрибутов нет» приглушено. */
.compare-table .attr-head { height: 20px; margin-top: 6px; }
.compare-table .attr-open { display: inline-flex; align-items: center; gap: 4px; margin: 0; padding: 0; height: 20px; line-height: 20px; font-size: 13px; max-width: 100%; }
.compare-table .attr-open .btn-icon { width: 14px; height: 14px; margin: 0; }
.compare-table .attr-none { display: block; height: 20px; line-height: 20px; font-size: 12px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.in-compare-mark { font-size: 12px; color: var(--muted); white-space: nowrap; }
.attr-group { margin: 0 0 20px; }
.attr-group h3 { margin: 0 0 8px; font-size: 14px; font-weight: 500; }
.attr-list { display: grid; grid-template-columns: minmax(120px, 38%) 1fr; margin: 0; }
.attr-list dd { margin: 0; padding: 8px 0; min-height: 36px; border-bottom: 1px solid var(--line); font-size: 13px; overflow-wrap: anywhere; }
.attr-list dt { margin: 0; padding: 8px 12px 8px 0; min-height: 36px; border-bottom: 1px solid var(--line); font-size: 13px; overflow-wrap: anywhere; color: var(--muted); }
.attr-note { margin: 0 0 8px; padding: 10px 12px; border-radius: var(--radius); background: var(--tile); }
.attr-note p { margin: 4px 0 0; font-size: 14px; white-space: pre-wrap; overflow-wrap: anywhere; }
.attrs-foot { display: flex; justify-content: flex-end; gap: 8px; margin-top: 16px; }

/* Источники данных: реестр строками, страница источника и набор данных с вкладками. */
.field-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; margin: 0 0 12px; }
.field-toolbar .search-box { flex: 1 1 320px; max-width: 560px; }
.source-facts { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 8px 16px; margin: 0; padding-top: 8px; border-top: 1px solid var(--line); }
.source-facts > div { min-width: 0; }
.source-facts dt { margin: 0 0 2px; color: var(--muted); font-size: 12px; }
.source-facts dd { margin: 0; font-size: 13px; overflow-wrap: anywhere; }
.source-facts dd.is-warn, .fact-list dd.is-warn { color: var(--warn); }
.source-tabs { margin-top: 16px; }
.source-panel[hidden], #panel-fields[hidden], #panel-recon[hidden] { display: none; }
.fact-list { display: grid; grid-template-columns: minmax(130px, 38%) 1fr; margin: 0; }
.fact-list dd { margin: 0; padding: 8px 0; min-height: 36px; border-bottom: 1px solid var(--line); font-size: 13px; overflow-wrap: anywhere; }
.fact-list dt { margin: 0; padding: 8px 12px 8px 0; min-height: 36px; border-bottom: 1px solid var(--line); font-size: 13px; overflow-wrap: anywhere; color: var(--muted); }
.stat-row { margin: 12px 0; }
.stat-value { font-size: 20px; font-weight: 500; }
.dataset-tabs { margin-top: 16px; }
.dataset-actions-row { display: flex; align-items: center; justify-content: flex-end; gap: 8px; }
/* Таблица наборов: все ячейки по центру строки, кнопки и метки на одной линии с текстом. */
.table-wrap.datasets td, .table-wrap.datasets th { vertical-align: middle; }
.table-wrap.datasets td:nth-child(5) { white-space: nowrap; }
.dataset-actions-row .btn { white-space: nowrap; }
/* Пункты выпадающих меню строки: ссылка и кнопка выглядят одинаково. */

/* Просмотр баз разработки: дерево слева, таблица справа. */
.db-layout { display: grid; grid-template-columns: minmax(220px, 300px) minmax(0, 1fr); gap: 16px; align-items: start; }
.db-tree .db-schema { margin: 0 0 8px; }
.db-tree .db-schema > summary { min-height: 32px; padding: 4px 10px; }
.db-tree ul { list-style: none; margin: 4px 0 4px 12px; padding: 0; }
.db-tree li { margin: 2px 0; }
.db-filter, .db-search { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin: 8px 0; }
.db-fields { margin: 8px 0 12px; }
.db-main { min-width: 0; }
/* Таблица данных прокручивается внутри карточки: ширина страницы не растёт. Заголовки остаются
   у верхнего края обёртки. Обычный sticky-head здесь не годится: он снимает прокрутку вбок. */
.db-table { max-height: 70vh; overflow: auto; }
.db-table thead th { position: sticky; top: 0; z-index: 1; background: var(--card); }
.db-cells td { max-width: 280px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@media (max-width: 900px) { .db-layout { grid-template-columns: 1fr; } }

/* Источники: новый источник по типу, строки перечня поставки. */
.source-kinds { display: grid; grid-template-columns: repeat(3, minmax(0, var(--card-max))); gap: 16px; align-items: start; }
@media (max-width: 900px) { .source-kinds { grid-template-columns: 1fr; } }
.datasets .group-row th { text-align: left; color: var(--muted); font-weight: 500; background: var(--line-soft); }
.is-retired td, .is-retired th { color: var(--muted); }
.notice.is-error { color: var(--bad); }

.dataset-retire { margin-top: 24px; }

/* Сводка: одна сетка из 12 колонок с промежутком 16 px. Полосы занимают 12 колонок, плитки 3 или 4, таблицы 6.
   Ниже 1200 px плитки идут по две в ряд, таблицы одна под другой. Ниже 760 px всё в одну колонку. */
.sum-grid { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); gap: 16px; align-items: stretch; }
.sum-grid > * { min-width: 0; }
.sum-col-12 { grid-column: span 12; }
.sum-col-6 { grid-column: span 6; }
.sum-col-4 { grid-column: span 4; }
.sum-col-3 { grid-column: span 3; }
@media (max-width: 1199px) {
  .sum-grid > .sum-col-3, .sum-grid > .sum-col-4 { grid-column: span 6; }
  .sum-grid > .sum-col-6 { grid-column: span 12; }
}
@media (max-width: 759px) { .sum-grid > * { grid-column: span 12; } }
.sum-card { display: flex; flex-direction: column; gap: 10px; min-width: 0; padding: 16px; background: var(--card); border: 1px solid var(--line); border-radius: var(--radius-card); }
.sum-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 4px 12px; }
.sum-head h2 { margin: 0; font-size: 15px; font-weight: 500; }
.sum-head a { font-size: 13px; }
.sum-count { margin-left: 4px; color: var(--muted); font-size: 13px; font-weight: 400; }
.sum-release { margin: 0; font-size: 15px; text-align: right; }
.sum-release strong { color: var(--primary); }
.sum-release .note { display: block; }
.sum-events { display: flex; flex-direction: column; }
.sum-event { display: grid; grid-template-columns: 20px minmax(0, 1fr) 170px 140px 96px; gap: 0 12px; align-items: baseline; padding: 10px 0; border-top: 1px solid var(--line-soft); }
.sum-events > .sum-event:first-child { border-top: 0; }
.sum-event-text { min-width: 0; }
.sum-event-area { overflow-wrap: anywhere; }
.sum-event-time { white-space: nowrap; }
.sum-event-action { text-align: right; white-space: nowrap; }
@media (max-width: 900px) {
  .sum-event { grid-template-columns: 20px minmax(0, 1fr); }
  .sum-event-area, .sum-event-time, .sum-event-action { grid-column: 2; text-align: left; }
}
.sum-sev { display: inline-flex; align-items: center; justify-content: center; width: 18px; height: 18px; margin-top: 1px; border: 2px solid var(--primary); border-radius: 50%; color: var(--primary); font-size: 12px; font-weight: 500; line-height: 1; }
.sum-sev.sev-bad { background: var(--bad); border-color: var(--bad); color: var(--on-fill); }
.sum-sev.sev-warn { border-color: var(--warn-mid); color: var(--warn); }
.sum-sev.sev-info { border-color: var(--primary); color: var(--primary); }
.sum-empty { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 0; }
.sum-dot { display: inline-block; flex: none; width: 8px; height: 8px; margin-right: 6px; border-radius: 50%; background: var(--line-strong); vertical-align: middle; }
.sum-dot.ok { background: var(--ok); }
.sum-dot.bad { background: var(--bad); }
.sum-dot.warn { background: var(--warn-mid); }
.sum-dot.info { background: var(--primary); }
.sum-dot.idle { background: var(--line-strong); }
.sum-flow { display: grid; grid-template-columns: minmax(0, 1fr) 240px; gap: 16px; align-items: start; }
.sum-steps { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 0; margin: 0; padding: 0; list-style: none; }
.sum-step { display: flex; flex-direction: column; gap: 4px; min-width: 0; padding: 0 16px; border-left: 1px solid var(--line-soft); }
.sum-step:first-child { padding-left: 0; border-left: 0; }
.sum-step-title { display: flex; align-items: center; font-weight: 500; }
.sum-step-state { font-size: 13px; color: var(--ink); }
.sum-schedules { display: flex; flex-direction: column; gap: 4px; padding-left: 16px; border-left: 1px solid var(--line-soft); }
@media (max-width: 900px) {
  .sum-flow { grid-template-columns: minmax(0, 1fr); }
  .sum-steps { grid-template-columns: repeat(2, minmax(0, 1fr)); row-gap: 12px; }
  .sum-step:nth-child(odd) { padding-left: 0; border-left: 0; }
  .sum-schedules { padding-left: 0; border-left: 0; }
}
.sum-tile { gap: 6px; }
.sum-tile-title { margin: 0; font-size: 13px; font-weight: 500; color: var(--muted); }
.sum-tile-value { margin: 0; font-size: 22px; font-weight: 500; line-height: 1.2; color: var(--ink); font-variant-numeric: tabular-nums; }
.sum-tile-value.is-waiting { color: var(--warn); }
.sum-tile-unit { font-size: 13px; font-weight: 400; color: var(--muted); }
.sum-tile .note { margin: 0; overflow-wrap: anywhere; }
.sum-tile-links { display: flex; flex-wrap: wrap; gap: 4px 12px; margin: auto 0 0; padding-top: 8px; font-size: 13px; }
.sum-breakdown { display: flex; flex-direction: column; gap: 2px; margin: 0; padding: 0; list-style: none; font-size: 13px; }
.sum-breakdown li { display: flex; justify-content: space-between; gap: 12px; }
.sum-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.sum-table th { padding: 6px 8px; border-bottom: 1px solid var(--line); color: var(--muted); font-size: 12px; font-weight: 400; text-align: left; white-space: nowrap; }
.sum-table td { padding: 10px 8px; border-bottom: 1px solid var(--line-soft); vertical-align: top; overflow-wrap: anywhere; }
.sum-table tbody tr:last-child td { border-bottom: 0; }
.sum-table td strong { font-weight: 500; }
.sum-table td .note { display: block; }
.sum-table .data-date { white-space: nowrap; }
.sum-state { display: inline-flex; align-items: center; white-space: nowrap; }
.crm-coverage { margin: 4px 0 0; }

/* План обновления модели на странице источника (М4б): блок отделён от форм, списки и таблица - от текста. */
.model-update, .model-revert { margin: 12px 0 0; }
.model-plan { margin: 16px 0 0; padding-top: 12px; border-top: 1px solid var(--line-soft); }
.plan-reasons, .plan-decisions { margin: 8px 0 0; padding-left: 20px; }
.plan-table { margin: 8px 0 0; }
.plan-table td { vertical-align: top; overflow-wrap: anywhere; }

/* Первый блок после шапки страницы своего верхнего отступа не добавляет (Д139): промежуток задаёт `.page-head`.
   Правило стоит в конце файла, чтобы перебить отступы классов той же силы (вкладки, плашки, заголовок h2). */
.page-head + * { margin-top: 0; }
