/* Репрайсер — надстройка над дизайн-системой панели.
   Своих цветов не вводит: всё через токены panel-theme.css. */

/* .wrap задан в app.css как двухколоночный; берём его wrap-wide и только
   расширяем поле. Своей раскладки не вводим. */
.wrap.wrap-wide { padding: 20px 24px 56px; max-width: 1560px; }

.h1 { font-family: Montserrat, Inter, sans-serif; font-size: 26px; font-weight: 700;
      margin: 0 0 4px; letter-spacing: -.01em; }
.sub { color: var(--text-3); margin: 0 0 22px; font-size: 14px; }

/* РАЗДЕЛИТЕЛЬ СЕКЦИИ. Экран площадки отвечает на четыре разных вопроса
   подряд — состояние, готовность к отправке, данные, настройки, — и все
   блоки шли одинаковым весом. Заголовок объявляет, где что, не требуя
   перетасовывать сами блоки. Своих цветов не вводит. */
.sect { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
        margin: 26px 0 12px; padding-bottom: 6px;
        border-bottom: 1px solid var(--border); }
.sect > span { font-family: Montserrat, Inter, sans-serif; font-size: 12px;
               font-weight: 700; text-transform: uppercase; letter-spacing: .07em;
               color: var(--text-2); }
.sect > i { font-style: normal; color: var(--text-3); font-size: 12px; }

.grid { display: grid; gap: 14px; }
.grid-2 { grid-template-columns: repeat(auto-fit, minmax(420px, 1fr)); }
.grid-4 { grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }

.tile { background: var(--surface); border: 1px solid var(--border); border-radius: 12px;
        padding: 14px 16px; }
.tile-k { color: var(--text-3); font-size: 12px; text-transform: uppercase;
          letter-spacing: .05em; margin-bottom: 6px; }
.tile-v { font-family: Montserrat, Inter, sans-serif; font-size: 26px; font-weight: 700;
          line-height: 1.1; }
.tile-n { color: var(--text-3); font-size: 12px; margin-top: 4px; }

.panel { background: var(--surface); border: 1px solid var(--border); border-radius: 12px;
         overflow: hidden; margin-bottom: 18px; }
.panel > h2 { font-family: Montserrat, Inter, sans-serif; font-size: 15px; font-weight: 600;
              margin: 0; padding: 13px 16px; border-bottom: 1px solid var(--border); }
.panel-b { padding: 14px 16px; }

table.t { width: 100%; border-collapse: collapse; font-size: 13px; }
table.t th { text-align: left; font-weight: 600; color: var(--text-3); font-size: 11px;
             text-transform: uppercase; letter-spacing: .04em;
             padding: 9px 12px; border-bottom: 1px solid var(--border); white-space: nowrap; }
table.t td { padding: 8px 12px; border-bottom: 1px solid var(--border); vertical-align: top; }
table.t tr:last-child td { border-bottom: 0; }
table.t td.num, table.t th.num { text-align: right; font-variant-numeric: tabular-nums;
                                 font-family: 'IBM Plex Mono', monospace; }
table.t td.id { font-family: 'IBM Plex Mono', monospace; font-size: 12px; }
.scroll-x { overflow-x: auto; }

.tag { display: inline-block; padding: 2px 8px; border-radius: 999px; font-size: 11px;
       font-weight: 500; white-space: nowrap; border: 1px solid var(--border); }
.tag-ok   { color: var(--ok);   border-color: color-mix(in srgb, var(--ok) 40%, transparent); }
.tag-warn { color: var(--warn); border-color: color-mix(in srgb, var(--warn) 40%, transparent); }
.tag-err  { color: var(--err);  border-color: color-mix(in srgb, var(--err) 40%, transparent); }
.tag-mut  { color: var(--text-3); }

.log { font-family: 'IBM Plex Mono', monospace; font-size: 12px; line-height: 1.55;
       white-space: pre-wrap; word-break: break-word; margin: 0;
       max-height: 62vh; overflow: auto; padding: 14px 16px; }
.log .sec { color: var(--acc); font-weight: 500; }

/* Полоска прогресса. `:not(svg *)` — не украшение: в SVG свойства width/height
   являются геометрией и ПЕРЕБИВАЮТ одноимённые атрибуты. Без этой оговорки
   правило доставало до <rect class="bar"> диаграмм раздела «Аналитика», и все
   столбики становились одинаковыми чёрточками по 8 px, хотя в атрибуте height
   честно стояли разные значения. Обычную разметку оговорка не трогает. */
.bar:not(svg *) { height: 8px; border-radius: 999px; background: var(--border); overflow: hidden; }
.bar > i { display: block; height: 100%; background: var(--acc); }

.filters { display: flex; gap: 8px; flex-wrap: wrap; align-items: center;
           padding: 12px 16px; border-bottom: 1px solid var(--border); }
.filters a { text-decoration: none; }

.kv { display: grid; grid-template-columns: minmax(160px, 220px) 1fr; gap: 6px 16px;
      font-size: 13px; }
.kv dt { color: var(--text-3); }
.kv dd { margin: 0; }

.inline-form { display: inline; }

/* Мелкая подпись рядом с основным текстом.
   ЗАЧЕМ ОТДЕЛЬНЫЙ КЛАСС. Раньше здесь стоял `muted mini`, но `.mini` в общем
   каноне (app.css) — это МИНИКАРТА: position:absolute, right:12, top:12,
   150x60, overflow:hidden. Совпадение имён, а не стиль. Все 27 подписей в
   панели вынимались из потока и рисовались одной коробкой в правом верхнем
   углу; в шапке она ложилась поверх кнопки «Обновить снимок» и перехватывала
   клик — замерено 09.09.2026: кнопка x 1213-1353, коробка с 1263.
   Размера шрифта `.mini` при этом не задаёт вовсе, так что «мелким» текст
   не был никогда.

   ПОЧЕМУ ПРЕФИКС. Первая попытка назвать класс `.note` наступила на те же
   грабли: `.note` в каноне — выноска с рамкой и синей полосой слева, и все
   подписи получили рамку. Панель подключает общий app.css, поэтому любое
   короткое имя рискует совпасть. Свои классы — только с префиксом `rp-`. */
.rp-note { font-size: 12px; color: var(--text-3); }

/* Меню деревом: площадка -> ИП -> магазин. Уровень ИП не ссылка, а метка:
   отдельного экрана кабинета пока нет, и кликабельный заголовок, ведущий
   в никуда, хуже некликабельного. */
.side-acc { padding: 7px 16px 3px 26px; font-size: 12px; font-weight: 600;
            color: var(--side-text-2); }
.side-link.side-sub { padding-left: 38px; }

/* ── раскрывающиеся разделы меню ────────────────────────────────────────
   Анимация по канону: только на раскрытие, 0.24 с ease-out, один акцент —
   сама высота. Раскрытие живёт на <details>, поэтому без JS и с рабочим
   поиском по странице. Высоту анимируем через grid-template-rows: max-height
   с «примерным потолком» рвёт длинные списки. */
.side-mp > summary,
.side-acc-d > summary {
  list-style: none; cursor: pointer; display: flex; align-items: center;
  gap: 8px; user-select: none;
}
.side-mp > summary::-webkit-details-marker,
.side-acc-d > summary::-webkit-details-marker { display: none; }

.side-mp > summary {
  padding: 8px 14px 8px 16px; font-size: 12.5px; font-weight: 600;
  color: var(--side-text); border-radius: 8px; margin: 2px 6px;
}
.side-acc-d > summary {
  padding: 6px 14px 6px 26px; font-size: 12px; font-weight: 600;
  color: var(--side-text-2); border-radius: 8px; margin: 1px 6px;
}
.side-mp > summary:hover,
.side-acc-d > summary:hover { background: var(--side-active); }
.side-mp > summary:focus-visible,
.side-acc-d > summary:focus-visible { outline: 2px solid var(--blue); outline-offset: -2px; }

.mp-caret {
  width: 0; height: 0; flex: 0 0 auto;
  border-left: 5px solid currentColor; border-top: 4px solid transparent;
  border-bottom: 4px solid transparent; opacity: .55;
  transition: transform .24s ease-out;
}
details[open] > summary > .mp-caret { transform: rotate(90deg); }
.mp-cnt { margin-left: auto; }

/* Заголовок раздела — ссылка на его сводную (решение владельца 15.09.2026:
   «Сводная должна открываться сразу при нажатии на кнопки Акции, Скидки,
   Цены и остатки»). Отдельный пункт «Сводная» после этого убран: два входа
   в одну страницу заставляют выбирать там, где выбора нет.
   Наследуем вид заголовка раздела из канона и только добавляем поведение
   ссылки — раздел не должен превратиться в обычный пункт меню. */
/* Корневой раздел — свёрнутый список (решение владельца 15.09.2026).
   Заголовок выглядит как заголовок раздела из канона, но ведёт себя как
   строка: стрелка раскрывает состав, имя открывает сводную. */
.side-root > summary {
  list-style: none; cursor: pointer; display: flex; align-items: center;
  gap: 8px; user-select: none;
  margin: 12px 6px 4px; padding: 5px 10px; border-radius: 7px;
  font-size: 11px; font-weight: 600; letter-spacing: .11em;
  text-transform: uppercase; color: var(--side-muted);
}
.side-root > summary::-webkit-details-marker { display: none; }
.side-root > summary:hover { background: var(--side-active); color: var(--side-text); }
.side-root > summary:focus-visible { outline: 2px solid var(--blue); outline-offset: -2px; }
/* Ссылка занимает ВСЮ строку, а не только слово. Было `flex: 0 1 auto`, и у
   площадки «Ozon» кликабельными оказались 30 px из 250 (замер 19.09.2026):
   всё остальное — сама `summary`, то есть клик по строке сворачивал список
   вместо того, чтобы открыть сводную. Со стороны это выглядит как «кнопка
   не сделана», хотя ссылка в разметке есть.
   `align-self: stretch` добавляет высоту: попасть надо в строку, а не в
   текст. Стрелка слева остаётся переключателем — она вне ссылки. */
.side-root-nm {
  color: inherit; text-decoration: none; font: inherit; letter-spacing: inherit;
  flex: 1 1 auto; min-width: 0;
  align-self: stretch; display: flex; align-items: center;
}
.side-root-nm:hover { color: var(--side-text); text-decoration: none; }
.side-root-nm.on { color: var(--blue-text); }
summary:has(> .side-root-nm.on) { background: var(--side-active); }
/* Подпись «сводная» — подсказка, что имя кликабельно. Прижата вправо, чтобы
   не расталкивать название раздела. */
/* Кегль 11, а не 9,5: 9,5 был единственным размером ниже одиннадцати во всей
   панели (замер аудита 22.09.2026 — 5 таких подписей на экране) и читался
   как сор. Одиннадцать — это шаг, которым уже набраны соседние пункты меню и
   теги состояния, поэтому подпись встаёт в общий ряд, а не выпирает.
   Двенадцать, как просил аудит, сделало бы подсказку КРУПНЕЕ названия
   раздела (11 px) — это уже другая ошибка. */
.side-group-go {
  margin-left: auto; font-size: 11px; letter-spacing: .06em;
  opacity: .55; font-weight: 500; text-transform: none; flex-shrink: 0;
}
/* Площадки внутри корневого раздела сдвигаем на ступень: без отступа три
   уровня вложенности читаются как один список. */
.side-root > .side-mp-b .side-mp > summary { padding-left: 22px; }

/* Имя площадки и ИП — ссылка внутри <summary>. Клик по имени ведёт на его
   сводку, клик по строке рядом раскрывает список. Наследуем цвет и размер
   у summary: ссылка тут не «ещё один элемент», а само название раздела,
   подчёркивание и синева сделали бы меню пёстрым. */
.side-head {
  color: inherit; text-decoration: none; font: inherit;
  flex: 0 1 auto; min-width: 0;
}
.side-head:hover { color: var(--side-text); text-decoration: none; }
.side-head.on { color: var(--blue-text); }
/* Текущий раздел подсвечиваем строкой целиком — так же, как обычный пункт. */
summary:has(> .side-head.on) { background: var(--side-active); }

/* Анимация раскрытия — на ВХОД содержимого, а не на высоту контейнера.
   Приём «grid-template-rows: 0fr -> 1fr» здесь не работает: в гриде с
   авто-высотой минимальный размер трека берётся от содержимого, а
   `overflow:hidden` на обёртке обнуляет его, и открытый раздел оставался
   высотой 0 (замерено 11.09.2026). Вход даёт тот же эффект и не зависит
   от того, сколько внутри кабинетов. Один акцент, 0.24 с, только на вход —
   как требует канон. */
details[open] > .side-mp-b > .side-mp-i { animation: side-in .28s ease-out; }
@keyframes side-in {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: none; }
}
/* Пункты внутри раскрытого раздела проявляются лесенкой. Разделы теперь
   свёрнуты по умолчанию, раскрытие стало частым действием, и движение
   помогает связать щелчок с тем, что появилось. Ступеней четыре: у кабинета
   два-три склада, дальше задержку не наращиваем — ждать никто не должен. */
details[open] > .side-mp-b > .side-mp-i > .side-link,
details[open] > .side-mp-b > .side-mp-i > .side-acc-d {
  animation: side-in .28s ease-out both;
}
details[open] > .side-mp-b > .side-mp-i > :nth-child(2) { animation-delay: .04s; }
details[open] > .side-mp-b > .side-mp-i > :nth-child(3) { animation-delay: .08s; }
details[open] > .side-mp-b > .side-mp-i > :nth-child(n+4) { animation-delay: .12s; }

/* Корневые разделы проявляются при открытии страницы — тем же движением,
   что и содержимое раскрытого раздела, чтобы меню читалось как одно целое.
   Только на вход, по одному разу, с крошечным сдвигом между разделами.
   Дольше .3 с не делаем: меню — не место, где ждут. */
.side-root > summary { animation: side-in .3s ease-out both; }
.side-root:nth-of-type(2) > summary { animation-delay: .05s; }
.side-root:nth-of-type(3) > summary { animation-delay: .1s; }

/* Склад выключенной площадки — не ссылка: экрана товаров по нему нет. */
.side-link.side-off { opacity: .5; cursor: default; }

@media (prefers-reduced-motion: reduce) {
  .mp-caret { transition: none; }
  .side-root > summary { animation: none; }
  details[open] > .side-mp-b > .side-mp-i,
  details[open] > .side-mp-b > .side-mp-i > .side-link,
  details[open] > .side-mp-b > .side-mp-i > .side-acc-d { animation: none; }
}

/* ── таблица магазинов на обзоре ─────────────────────────────────────── */
.tbl-shops td { vertical-align: top; padding-top: 9px; padding-bottom: 9px; }
.tbl-shops .tbl-sub { display: block; margin-top: 2px; }
.tbl-shops .tbl-act { text-align: right; white-space: nowrap; }
.tbl-shops .tag { margin: 0 4px 3px 0; }
/* Выключенный магазин приглушён, но читается: он в хозяйстве есть. */
.tbl-shops tr.row-off td { opacity: .55; }
/* ...а ячейка с действием НЕ приглушается. `opacity` гасит всё поддерево, и
   поднять кнопку обратно изнутри нельзя — дочерний элемент ярче родителя не
   станет. Поэтому исключаем саму ячейку: строка остаётся второстепенной, а
   действие в ней читаемым. Замер 19.09.2026: при .55 надпись на синей кнопке
   давала контраст 2,94 при норме 4,5 — та же нечитаемая кнопка, только по
   другой причине. */
.tbl-shops tr.row-off td.tbl-act { opacity: 1; }

/* Подпись в заголовке панели должна отступать от самого заголовка: без этого
   «Запуск вручную» и «кабинет Петроченко Н.В.» слипались в одно слово. */
.panel > h2 > .rp-note { margin-left: 8px; font-weight: 400; }
/* Та же пометка в ЗАГОЛОВКЕ СТРАНИЦЫ. Отступ стоял только у заголовков
   внутри панели, и в h1 пометка липла к тексту вплотную: «…WBКомаров А.С.».
   На коротком заголовке это читалось как опечатка, на длинном — как наезд. */
.h1 > .rp-note { margin-left: 8px; font-weight: 400; }

/* Имя ИП в разделе «Цены и акции» и процент акции при нём.
   Без этого «Комаров А.С.» ломался на две строки: процент — отдельный
   flex-элемент, и на него уходила ширина. Имя держим одной строкой, процент
   прижимаем к правому краю, как счётчик складов у площадки.
   Замер 11.09.2026: ширина меню 232 px, ссылка 192, «Петроченко Н.В.»
   требует 110 — со штатным gap 10 и процентом «−5%» не хватало пары
   пикселей. Поджали отступы, знак процента из бейджа убрали (он остался в
   сводке и в кабинете). */
.side-nm { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.side-pct { margin-left: auto; padding-left: 4px; flex-shrink: 0; font-size: 11px; }
.side-link:has(> .side-pct) { gap: 6px; padding-right: 8px; }

/* Числовые и временные столбцы канонической таблицы `.tbl`.
   ЗАЧЕМ ЗДЕСЬ. В repricer.css правила для `.num` были написаны только под
   `table.t` — старую таблицу из журнала. Сводные экраны собраны на `.tbl`
   (канон app.css), и колонки с числами в них шли по левому краю: столбец
   «Обработано» сравнивать построчно было невозможно. Канон не трогаем,
   дополняем — правило §11 CLAUDE.md. */
.tbl td.num, .tbl th.num { text-align: right; font-variant-numeric: tabular-nums;
                           font-family: 'IBM Plex Mono', monospace; white-space: nowrap; }
.tbl td.num .rp-note { font-family: Inter, sans-serif; }
.tbl td.when, .tbl th.when { white-space: nowrap;
                             font-variant-numeric: tabular-nums; }
/* Открытый прогон в списке — подсветкой, а не только тегом. */
.tbl tbody tr.row-on { background: color-mix(in srgb, var(--acc) 9%, transparent); }
.tbl-runs td { padding-top: 7px; padding-bottom: 7px; }

/* СКЛАДЫ КАБИНЕТА в плане WB. Были прозой в одну строку на склад, и числа
   соседних складов не выстраивались друг под другом — строка читалась, а не
   охватывалась взглядом. Три колонки ставят их в столбик; nowrap не даёт
   «с остатком» оторваться от своего числа. */
.wb-wh { display: grid; gap: 2px 14px; align-items: baseline;
         grid-template-columns: minmax(92px, auto) auto auto;
         white-space: nowrap; }
.wb-wh + .wb-wh { margin-top: 4px; }
.wb-wh-n { color: var(--text-3); }
.wb-wh b { font-variant-numeric: tabular-nums; }

/* Заголовок кабинета над его магазинами на обзоре. */
.acc-head { font-size: 15px; margin: 22px 0 10px; display: flex;
            align-items: baseline; gap: 10px; flex-wrap: wrap; }
.acc-head:first-of-type { margin-top: 4px; }
.warnbox { border-left: 3px solid var(--warn); background: var(--surface);
           border-radius: 0 10px 10px 0; padding: 12px 16px; margin-bottom: 16px;
           font-size: 13px; }

@media (max-width: 720px) {
  .wrap { padding: 16px 14px 48px; }
  .grid-2 { grid-template-columns: 1fr; }
  /* Списки «ключ — значение» кладём в одну колонку. В две по 160 px + 1fr
     длинное пояснение не влезало и молча ОБРЕЗАЛОСЬ: у .panel стоит
     overflow:hidden, поэтому текст не уезжал в скролл, а пропадал. Замер
     15.09.2026 на 390 px: правый край dd уходил на 459 при ширине 374. */
  .kv { grid-template-columns: 1fr; gap: 2px 0; }
  .kv dt { margin-top: 10px; }
  .kv dt:first-child { margin-top: 0; }
}

/* Заблокированная кнопка должна выглядеть заблокированной: без этого «Боевая
   отправка» неотличима от рабочей и провоцирует клик. */
.btn:disabled { opacity: .42; cursor: not-allowed; filter: grayscale(.5); }

/* Необратимое действие красим предупреждением, а не акцентом. */
.btn-danger { border-color: color-mix(in srgb, var(--err) 45%, transparent);
              color: var(--err); background: transparent; }
.btn-danger:not(:disabled):hover { background: color-mix(in srgb, var(--err) 12%, transparent); }

/* Длинная подпись на телефоне переносится, а не вылезает за карточку: у .btn
   фиксированная высота и nowrap. Зона нажатия остаётся не ниже 44 px. */
@media (max-width: 640px) {
  .btn-wrap { white-space: normal; height: auto; min-height: 44px;
              padding: 10px 16px; line-height: 1.3; }
}

/* КНОПКА — КОМПОНЕНТ, А НЕ ССЫЛКА.
   `.btn` объявляет себе белый текст, но кнопки у нас часто ссылки
   (`<a class="btn">`), и по ним били два правила «на все ссылки сразу»:
   `body a` в теме и `.tbl a` в app.css. Оба весом (0,1,1) против (0,1,0) у
   `.btn`, поэтому выигрывали они, и на синей кнопке оказывался цвет ССЫЛКИ.
   Замер 19.09.2026: текст rgb(77,170,252) на фоне rgb(0,120,212) — контраст
   1,83:1 при норме 4,5. Надпись читалась с трудом, что владелец и увидел.
   Лечим не отменой общих правил (они нужны обычным ссылкам), а тем, что
   возвращаем кнопке её собственный цвет тем же весом, но ПОЗЖЕ: этот файл
   грузится последним. Подчёркивание при наведении снимаем здесь же — оно
   прилетало из `a:hover` по той же причине. */
a.btn, a.btn:hover, a.btn:focus { color: #FFFFFF; text-decoration: none; }
a.btn.ghost, a.btn.ghost:hover { color: var(--text); }
a.btn.danger, a.btn.danger:hover,
a.btn-danger, a.btn-danger:hover { color: var(--err); }

/* ─── конвейер прогона ─────────────────────────────────────────────────────
   Узлы в ряд со связями между ними: видно, где сейчас идёт работа.
   Состояний четыре: ожидает, идёт, готово, ошибка. Цвет берём из токенов. */
.flow { display: flex; align-items: stretch; gap: 0; flex-wrap: wrap; }

.node-card { flex: 1 1 170px; min-width: 158px;
  border: 1px solid var(--border); border-radius: 10px; padding: 11px 13px;
  background: var(--surface); transition: border-color .2s ease, box-shadow .2s ease; }
.node-top { display: flex; align-items: center; gap: 8px; }
.node-ic { width: 9px; height: 9px; border-radius: 50%; background: var(--border-2, var(--border));
           flex: none; }
.node-name { font-weight: 600; font-size: 13px; line-height: 1.25; }
.node-note { color: var(--text-3); font-size: 11px; margin: 5px 0 9px; line-height: 1.35; }
.node-foot { display: flex; align-items: baseline; justify-content: space-between; gap: 8px;
             font-size: 12px; }
.node-state { color: var(--text-3); font-size: 11px; text-transform: uppercase;
              letter-spacing: .04em; }
.node-foot b { font-family: 'IBM Plex Mono', monospace; font-size: 14px; }

.node-link { flex: 0 0 18px; align-self: center; height: 2px; background: var(--border); }

.node-card.is-wait { opacity: .55; }
.node-card.is-done { border-color: color-mix(in srgb, var(--ok) 40%, var(--border)); }
.node-card.is-done .node-ic { background: var(--ok); }
.node-card.is-done .node-state { color: var(--ok); }
.node-card.is-run  { border-color: var(--acc);
                     box-shadow: 0 0 0 3px color-mix(in srgb, var(--acc) 16%, transparent); }
.node-card.is-run .node-ic { background: var(--acc); animation: pulse 1.1s ease-in-out infinite; }
.node-card.is-run .node-state { color: var(--acc); }
.node-card.is-err { border-color: var(--err); }
.node-card.is-err .node-ic { background: var(--err); }
.node-card.is-err .node-state { color: var(--err); }

@keyframes pulse { 0%,100% { opacity: 1 } 50% { opacity: .35 } }

/* Пульс — единственная анимация на экране, и она несёт смысл «идёт работа».
   При запросе на уменьшение движения гасим её: состояние остаётся читаемым
   по цвету и подписи. */
@media (prefers-reduced-motion: reduce) {
  .node-card.is-run .node-ic { animation: none; }
}

@media (max-width: 900px) {
  .node-link { display: none; }
  .flow { gap: 8px; }
}

/* ── Ссылки на прайсы ────────────────────────────────────────────────
   БЫЛА ТАБЛИЦА В ПЯТЬ КОЛОНОК с полем ввода прямо в строке. Поле ссылки —
   самый широкий элемент экрана, и таблица не влезала: уезжала в
   горизонтальный скролл, а на планшете за край уходила кнопка
   «Сохранить». Поэтому склад описывается сверху, а ввод идёт отдельной
   строкой во всю ширину карточки.

   Карточки, а не строки: у каждого склада свой прайс, и перепутать их
   — значит показать чужие остатки. Отдельная коробка на склад делает
   границу видимой. */
/* Прыжок по якорю не должен загонять заголовок блока под липкую шапку
   (header.top, высота 60). Запас 12 сверху — чтобы блок не лип к ней
   вплотную. */
.panel[id] { scroll-margin-top: 72px; }

/* Сообщение о результате липнет под шапкой. Действия теперь возвращают к
   своему блоку, а не на верх экрана, и сообщение наверху осталось бы за
   кадром — вместе с объяснением, почему действие НЕ вышло. */
.flash-top { position: sticky; top: 64px; z-index: 4; }

/* min(340px, 100%): на телефоне карточка уже 340 px, и трек в 340 выталкивал
   карточку склада с кнопкой «Сохранить» за край (обход экранов 24.09.2026). */
.feeds { display: grid; gap: 12px;
         grid-template-columns: repeat(auto-fill, minmax(min(340px, 100%), 1fr)); }

/* Колонка с прижатой к низу формой. Без этого поле ввода у соседних
   карточек стоит на разной высоте — у одного склада есть строка «Общий»,
   у другого нет, — и ряд кнопок «Сохранить» идёт лесенкой. */
.feed { display: flex; flex-direction: column;
        border: 1px solid var(--border); border-radius: 10px;
        background: var(--node); padding: 12px 14px 14px;
        box-shadow: var(--edge-top); }
.feed-form { margin-top: auto; }
.feed-off { border-color: color-mix(in srgb, var(--err) 34%, var(--border)); }

.feed-h { display: flex; align-items: center; gap: 10px;
          justify-content: space-between; margin-bottom: 8px; }
.feed-nm { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
.feed-nm b { font-size: 15px; letter-spacing: -.01em;
             overflow-wrap: anywhere; }
.feed-lvl { font-size: 11px; color: var(--text-3); text-transform: uppercase;
            letter-spacing: .06em; white-space: nowrap; }

/* Пары «что → значение». Своя сетка, а не .kv: там колонка под широкие
   подписи дашборда, здесь подписи короткие и место нужно значению. */
.feed-kv { display: grid; grid-template-columns: 84px minmax(0, 1fr);
           gap: 3px 10px; margin: 0 0 12px; font-size: 12.5px; }
.feed-kv dt { color: var(--text-3); }
.feed-kv dd { margin: 0; color: var(--text-2); overflow-wrap: anywhere; }
.feed-kv code { font-size: 11.5px; color: var(--text-2);
                background: var(--head); border: 1px solid var(--border-soft);
                border-radius: 4px; padding: 1px 5px; }
/* Селектор с dd НАМЕРЕННО: `.feed-kv dd` весит больше одного класса и
   перебивал цвет предупреждения — «склад не в расписании» и «прогон
   работать не будет» рисовались обычным серым. Поймано просмотром в
   светлой теме, из кода это не видно. */
.feed-kv dd.feed-bad,  .feed-kv dd .feed-bad  { color: var(--err); }
.feed-kv dd.feed-warn, .feed-kv dd .feed-warn { color: var(--warn); }

.feed-lb { display: block; font-size: 11px; color: var(--text-3);
           text-transform: uppercase; letter-spacing: .06em;
           margin-bottom: 5px; }
/* Поле тянется, кнопка нет; высоту поле берёт от кнопки (36px по канону),
   иначе .inp со своими 30px стоит ниже и строка выглядит кривой. */
.feed-row { display: flex; gap: 8px; align-items: stretch; }
.feed-in { flex: 1 1 auto; min-width: 0; font-size: 13.5px; }
.feed-row .btn { flex: 0 0 auto; }

/* Узкий экран: подписи над значениями, иначе колонка в 84px съедает
   половину строки и адрес складывается в лесенку. */
@media (max-width: 420px) {
  .feed-kv { grid-template-columns: 1fr; gap: 0 0; }
  .feed-kv dt { margin-top: 6px; }
  .feed-row { flex-wrap: wrap; }
  .feed-row .btn { width: 100%; }
}

/* ── Обмен ценами WB: чек-лист ворот ─────────────────────────────────
   Экран отвечает на один вопрос: можно ли нажать кнопку и что мешает.
   Поэтому не список «да/нет», а карточка на позицию: состояние, причина
   и лекарство рядом. «Отправка недоступна» без причины заставляет искать
   её в коде, а причин там пять разных. */
/* «Требуют внимания» на Главной: список поводов вмешаться, каждый — ссылка
   туда, где он виден подробно. Сетка, а не столбец: при пяти-шести поводах
   столбец отодвигает цифры за первый экран, а именно они дают контекст. */
.attn { display: grid; gap: 8px; margin-top: 10px;
        grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); }
.attn-i { display: block; padding: 9px 11px; border-radius: 8px;
          border: 1px solid color-mix(in srgb, var(--err) 30%, var(--border));
          background: var(--node); text-decoration: none; }
.attn-i:hover { border-color: var(--err); }
.attn-w { display: block; font-size: 13.5px; font-weight: 600; color: var(--text); }

.gates { display: grid; gap: 10px; margin-bottom: 14px;
         grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); }
.gate { border: 1px solid var(--border); border-radius: 9px;
        background: var(--node); padding: 10px 12px 11px; }
.gate-ok { border-color: color-mix(in srgb, var(--ok) 34%, var(--border)); }
.gate-no { border-color: color-mix(in srgb, var(--err) 40%, var(--border)); }
.gate-h { display: flex; align-items: center; gap: 8px;
          justify-content: space-between; margin-bottom: 5px; }
.gate-h b { font-size: 13.5px; }
.gate-what { font-size: 12.5px; color: var(--text-2); overflow-wrap: anywhere; }
/* Лекарство отделено чертой: это не описание состояния, а инструкция. */
.gate-fix { margin-top: 7px; padding-top: 7px; font-size: 12px;
            color: var(--text-3); border-top: 1px dashed var(--border); }

/* Форма запуска: объём, подтверждение и кнопка в одну строку на широком
   экране, в столбик на узком. Поле «ДА» намеренно узкое — его набирают
   руками, а не выбирают. */
.wave-form { display: flex; gap: 14px; align-items: flex-end;
             flex-wrap: wrap; margin-bottom: 10px; }
.wave-form .fld { margin-bottom: 0; }
.wave-form select { min-width: 180px; }
.wave-form input[name="confirm"] { width: 120px; text-transform: uppercase; }
.wave-form .btn[disabled] { opacity: .45; cursor: not-allowed; }
.wave-form .btn[disabled]:hover { background: var(--blue);
                                  border-color: var(--blue); }

@media (max-width: 560px) {
  .wave-form { flex-direction: column; align-items: stretch; }
  .wave-form .btn { width: 100%; }
}

/* ── ОБЗОР: одна главная с четырьмя вкладками (23.09.2026) ────────────────
   Своих цветов не вводит — всё через токены panel-theme.css, поэтому тёмная,
   приглушённая и светлая темы получаются сами. Состояние показывается
   существующим `.tag`, а не новой палитрой: один словарь цветов на панель.

   БЮДЖЕТ ВЫСОТЫ — ограничение, а не пожелание. Вся вкладка обязана
   укладываться в два экрана ноутбука (1280×800 и 1440×900), иначе «сводка»
   превращается в ленту, по которой всё равно надо скроллить. Отсюда плотные
   отступы в блоках ниже: это не экономия ради красоты, а условие задачи. */

.ov-top { display: flex; align-items: flex-start; justify-content: space-between;
          gap: 16px; flex-wrap: wrap; margin-bottom: 12px; }
.ov-ttl .h1 { margin-bottom: 2px; }
.ov-ctl { display: flex; align-items: center; gap: 8px; }

/* ФИЛЬТР ИП на <details>: раскрытие умеет браузер, без JS список остаётся
   доступен с клавиатуры. Всплывает поверх содержимого, поэтому отдельного
   места в раскладке не занимает и высоту страницы не двигает. */
.ov-ip { position: relative; }
.ov-ip > summary { list-style: none; cursor: pointer; user-select: none;
                   display: inline-flex; align-items: center; gap: 6px;
                   height: 32px; padding: 0 12px; border-radius: 6px;
                   border: 1px solid var(--border); background: var(--surface);
                   font-size: 13px; color: var(--text-2); white-space: nowrap; }
.ov-ip > summary::-webkit-details-marker { display: none; }
.ov-ip > summary::after { content: '▾'; font-size: 10px; color: var(--text-3); }
.ov-ip > summary:hover { border-color: var(--border-2); color: var(--text); }
.ov-ip > summary:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }
.ov-ip[open] > summary { border-color: var(--acc); color: var(--text); }
.ov-ip-b { position: absolute; z-index: 30; right: 0; top: calc(100% + 6px);
           min-width: 240px; padding: 6px; border-radius: 10px;
           border: 1px solid var(--border); background: var(--elev);
           box-shadow: var(--shadow-2); display: grid; gap: 2px; }
.ov-ip-b a { display: flex; align-items: baseline; justify-content: space-between;
             gap: 10px; padding: 7px 10px; border-radius: 6px; font-size: 13px;
             color: var(--text-2); text-decoration: none; }
.ov-ip-b a:hover { background: var(--panel); color: var(--text); }
.ov-ip-b a.on { background: var(--blue-soft); color: var(--text); font-weight: 600; }

/* ВКЛАДКИ — четыре представления одной страницы, поэтому и выглядят как
   переключатель вида, а не как меню разделов: общая рамка, подчёркнутая
   активная. Счётчик проблем стоит на самой вкладке — иначе человек уходит
   на площадку, чтобы узнать, что там ничего не случилось. */
.ov-tabs { display: flex; gap: 2px; margin: 0 0 12px;
           border-bottom: 1px solid var(--border); overflow-x: auto; }
.ov-tab { display: inline-flex; align-items: center; gap: 7px; white-space: nowrap;
          padding: 9px 14px; margin-bottom: -1px; font-size: 13.5px; font-weight: 500;
          color: var(--text-3); text-decoration: none;
          border-bottom: 2px solid transparent; }
.ov-tab:hover { color: var(--text); text-decoration: none; }
.ov-tab.on { color: var(--text); font-weight: 600; border-bottom-color: var(--acc); }
.ov-tab:focus-visible { outline: 2px solid var(--blue); outline-offset: -2px; }
.ov-cnt { display: inline-flex; align-items: center; justify-content: center;
          min-width: 18px; height: 18px; padding: 0 5px; border-radius: 999px;
          font-size: 11px; font-variant-numeric: tabular-nums;
          color: var(--warn);
          border: 1px solid color-mix(in srgb, var(--warn) 45%, transparent); }

/* Строка свежести. Живёт ОТДЕЛЬНО от плашки внимания: возраст данных — это
   не инцидент, но без него зелёный экран ничего не значит. */
.ov-fresh { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
            margin: 0 0 12px; }

/* ПРОЦЕССЫ СЕРВЕРА — диспетчер фоновых работ. Точка есть у КАЖДОГО
   состояния (гайдбук §15), слово рядом всегда; пульс — только у «идёт» и
   только в строке главной, чтобы на странице он был один. Период 2,2 с —
   тот же, что у стрелок карты процесса. */
.ov-jobs { display: flex; align-items: baseline; gap: 6px 14px; flex-wrap: wrap;
           margin: -4px 0 12px; font-size: 12.5px; color: var(--text-2); }
.ov-jobs > b { font-size: 13px; color: var(--text); }
.ov-jobs-a { margin-left: auto; white-space: nowrap; }
.ov-jobs-a + .ov-jobs-a { margin-left: 0; }
/* Точка — у ПЕРВОЙ строки, а не посередине блока: на телефоне список идущих
   работ переносится на три строки, и точка по центру висела ни при чём. */
.jb { display: inline-flex; align-items: flex-start; gap: 6px; }
.jb-d { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto;
        margin-top: .43em; background: var(--text-3); }
.jb-ok .jb-d   { background: var(--ok); }
.jb-warn .jb-d { background: var(--warn); }
.jb-err .jb-d  { background: var(--err); }
/* «Идёт» — статусный синий, а не акцент: акцент красит ДЕЙСТВИЯ и меняется
   по выбору пользователя, статусные цвета не меняются (panel-theme.css). */
.jb-run .jb-d  { background: var(--blue); }
.jb-warn { color: var(--warn); }
.jb-err  { color: var(--err); }
.ov-jobs .jb-run .jb-d { animation: pulse 2.2s ease-out infinite; }
@media (prefers-reduced-motion: reduce) {
  .ov-jobs .jb-run .jb-d { animation: none; }
}
/* Экран «Наблюдатели»: журнал как есть. Строки лимитов Ozon бывают в тысячи
   знаков — переносим, иначе они раздвигают страницу на телефоне. */
.w-pre { white-space: pre-wrap; word-break: break-word; margin: 0; font-size: 12px;
         line-height: 1.55; max-height: 28em; overflow: auto; }
.w-files { display: flex; flex-wrap: wrap; gap: 6px; }
.ov-jobs-g { margin: 16px 0 4px; font-size: 11.5px; font-weight: 600;
             letter-spacing: .04em; text-transform: uppercase; color: var(--text-3); }
/* Диспетчер шире обычной детали: три колонки со временем и кодом в 460 px
   рвали «ошибка, код 1» на две строки. */
.ov-dlg.is-wide { width: min(680px, 100%); }
.ov-jobs-t .tbl-sub { display: block; margin-top: 2px; }
/* Одинаковые колонки во всех группах: без fixed каждая таблица мерила себя
   сама, и «Состояние» у Ozon и WB стояло в разных местах. */
.ov-jobs-t { table-layout: fixed; }
.ov-jobs-t th:nth-child(1) { width: 40%; }
.ov-jobs-t th:nth-child(2) { width: 27%; }

/* ТРЕБУЕТ ВНИМАНИЯ. Рамка карточки красится по худшему из поводов, а не по
   их количеству: один подтверждённый сбой важнее пяти просроченных снимков. */
.ov-attn { padding: 13px 16px 14px; margin-bottom: 16px; }
.ov-attn.is-crit { border-color: color-mix(in srgb, var(--err) 45%, var(--border)); }
.ov-attn.is-warn { border-color: color-mix(in srgb, var(--warn) 45%, var(--border)); }
.ov-attn.is-ok   { border-color: color-mix(in srgb, var(--ok) 32%, var(--border)); }
.ov-attn-h { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.ov-attn-h > b { font-size: 14px; }
/* Три повода — ТРИ колонки, а не две и перенос. Общая сетка `.attn` рассчитана
   на 320 px и на 1280×800 ломалась в два ряда, добавляя около 90 px к высоте
   страницы. Само правило не трогаем — уточняем только внутри обзора. */
.ov-attn .attn { grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); }
/* Пояснение к поводу — не больше двух строк. Полный текст открывается по
   ссылке; три-четыре строки прозы на каждом из трёх поводов забирали
   четверть экрана, ради которого сводка и делалась. */
.ov-attn .attn-i > .rp-note { display: -webkit-box; -webkit-line-clamp: 2;
                              -webkit-box-orient: vertical; overflow: hidden; }
.ov-attn-h > .btn { margin-left: auto; align-self: center; }
.ov-rest { margin: 9px 0 0; }

/* Уровень повода — цветом рамки. Базовый `.attn-i` покрашен под ошибку;
   здесь три уровня разводятся явно, иначе ожидание расписания выглядит так
   же тревожно, как сорванная отправка. */
.attn-i.lvl-crit { border-color: color-mix(in srgb, var(--err) 45%, var(--border)); }
.attn-i.lvl-crit:hover { border-color: var(--err); }
.attn-i.lvl-warn { border-color: color-mix(in srgb, var(--warn) 42%, var(--border)); }
.attn-i.lvl-warn:hover { border-color: var(--warn); }
.attn-i.lvl-info { border-color: var(--border); }
.attn-i.lvl-info:hover { border-color: var(--border-2); }

.ov-p { margin-bottom: 16px; }
.ov-p > h2 { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }

/* СРАВНЕНИЕ ПЛОЩАДОК и МАТРИЦА МОДУЛЕЙ. Обе — `.tbl` (канон app.css),
   дополняем только плотностью: строк немного, а высота дорога. */
.ov-mps td, .ov-matrix td, .ov-accs td { padding-top: 9px; padding-bottom: 9px; }
.ov-mps .tbl-sub, .ov-matrix .tbl-sub, .ov-accs .tbl-sub { display: block; margin-top: 3px; }
.ov-mps .tbl-act { text-align: right; white-space: nowrap; }
.ov-matrix th[scope="row"] { color: var(--text); font-weight: 600;
                             text-transform: none; letter-spacing: 0;
                             font-size: 13px; white-space: nowrap; }

/* Ячейка матрицы кликабельна ЦЕЛИКОМ: цель размером с ячейку попадается
   мышью с первого раза, ссылка в тексте — нет. */
.ov-cell { padding: 0 !important; }
.ov-cell-a { display: block; padding: 8px 12px; text-decoration: none;
             color: inherit; border-radius: 6px; }
a.ov-cell-a:hover { background: var(--panel); text-decoration: none; }
a.ov-cell-a:focus-visible { outline: 2px solid var(--blue); outline-offset: -2px; }
.ov-svc { padding: 10px 16px 13px; margin: 0; line-height: 1.5; }

.ov-empty { padding: 12px 16px; margin: 0; line-height: 1.5; }

/* КАРТОЧКИ МОДУЛЕЙ на вкладке площадки: шесть штук в три колонки — два
   ряда, и вкладка остаётся в бюджете высоты. Карточка держит состояние,
   до двух фактов, дату и охват; всё остальное открывается сбоку. */
.ov-cards { display: grid; gap: 10px; padding: 14px 16px;
            grid-template-columns: repeat(auto-fit, minmax(270px, 1fr)); }
.ov-card { display: flex; flex-direction: column; gap: 6px;
           padding: 11px 13px 10px; border-radius: 9px;
           border: 1px solid var(--border); background: var(--node); }
.ov-card.st-err  { border-color: color-mix(in srgb, var(--err) 38%, var(--border)); }
.ov-card.st-warn { border-color: color-mix(in srgb, var(--warn) 38%, var(--border)); }
.ov-card-h { display: flex; align-items: baseline; justify-content: space-between;
             gap: 8px; }
.ov-card-h > b { font-size: 13.5px; }
.ov-card-f { list-style: none; margin: 0; padding: 0; display: grid; gap: 3px;
             font-size: 12.5px; color: var(--text-2); }
.ov-card-m, .ov-card-n { line-height: 1.45; }
.ov-card-n { padding-top: 6px; border-top: 1px dashed var(--border); }
.ov-card-a { display: flex; gap: 12px; flex-wrap: wrap; margin-top: auto;
             padding-top: 7px; font-size: 12.5px; }

/* ── БОКОВАЯ ДЕТАЛИЗАЦИЯ ───────────────────────────────────────────────
   Нативный <dialog>. БЕЗ JS это панель в потоке страницы (браузер рисует
   `dialog[open]` сам), СО скриптом тот же элемент поднимается showModal() и
   получает подложку, Esc и ловушку фокуса от браузера. Поэтому позиция
   задана одинаково для обоих случаев: панель у правого края, страница под
   ней не растягивается и не перестраивается. */
.ov-dlg { position: fixed; inset: 0 0 0 auto; z-index: 60;
          width: min(460px, 100%); max-width: 100%; max-height: 100%; height: 100%;
          margin: 0; padding: 0; border: 0; border-left: 1px solid var(--border);
          border-radius: 0; background: var(--surface); color: var(--text);
          box-shadow: var(--shadow-3);
          display: flex; flex-direction: column;
          animation: ovIn .22s cubic-bezier(.16,1,.3,1) both; }
.ov-dlg::backdrop { background: rgba(0,0,0,.42); }
@keyframes ovIn { from { transform: translateX(18px); opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .ov-dlg { animation: none; } }

.ov-dlg-h { display: flex; align-items: center; gap: 10px; flex: 0 0 auto;
            padding: 13px 16px; border-bottom: 1px solid var(--border); }
.ov-dlg-h h3 { margin: 0; font-family: Montserrat, Inter, sans-serif;
               font-size: 15px; font-weight: 600; }
.ov-dlg-x { margin-left: auto; width: 30px; height: 30px; flex: 0 0 auto;
            display: inline-flex; align-items: center; justify-content: center;
            border-radius: 6px; border: 1px solid var(--border);
            color: var(--text-2); text-decoration: none; font-size: 13px; }
.ov-dlg-x:hover { background: var(--panel); color: var(--text); text-decoration: none; }
.ov-dlg-b { flex: 1 1 auto; overflow-y: auto; padding: 14px 16px 18px; }
.ov-dlg-b > p:first-child { margin-top: 0; }
.ov-dlg-f { list-style: none; margin: 0 0 12px; padding: 0; display: grid; gap: 4px;
            font-size: 13px; color: var(--text-2); }
/* Таблица внутри панели УЖЕ 780 px канона — здесь ей столько не нужно и
   негде: панель 460 px, и min-width дал бы горизонтальный скролл на ровном
   месте. */
.ov-dlg-t { min-width: 0; font-size: 12.5px; }
.ov-dlg-t td, .ov-dlg-t th { padding: 7px 8px; }
.ov-dlg-b .kv { grid-template-columns: 96px 1fr; gap: 4px 10px;
                font-size: 12.5px; margin: 0 0 12px; }

/* ── УЗКИЕ ЭКРАНЫ ──────────────────────────────────────────────────────
   Планшет и телефон: вкладки в две строки по две, карточки модулей в
   столбик, боковая панель во весь экран. Порядок блоков не меняется —
   человек, привыкший к десктопу, ищет то же самое на тех же местах. */
@media (max-width: 900px) {
  .ov-top { gap: 10px; }
  .ov-ctl { width: 100%; }
  .ov-ip { flex: 1 1 auto; }
  .ov-ip > summary { width: 100%; justify-content: space-between; }
  .ov-ip-b { left: 0; right: auto; width: 100%; }
  .ov-tabs { display: grid; grid-template-columns: 1fr 1fr; gap: 0;
             border-bottom: 0; overflow: visible; }
  .ov-tab { justify-content: center; border: 1px solid var(--border);
            border-radius: 8px; margin: 0 0 6px; }
  .ov-tab + .ov-tab { margin-left: 6px; }
  .ov-tab:nth-child(odd) { margin-left: 0; }
  .ov-tab.on { border-color: var(--acc); background: var(--blue-soft); }
  .ov-cards { grid-template-columns: 1fr; }
  .ov-dlg { width: 100%; border-left: 0; }
  .ov-attn-h > .btn { margin-left: 0; }
}
@media (max-width: 560px) {
  .ov-card-a { gap: 8px; }
  /* Зона нажатия под палец: сама строка 19 px, и две ссылки
     стоят рядом — промах ведёт не «мимо», а НЕ ТУДА. На desktop
     высота не меняется: там указатель точный, а каждый лишний
     пиксель идёт в бюджет двух экранов. */
  .ov-card-a a { display: inline-flex; align-items: center;
                 min-height: 40px; padding: 0 2px; }
  .ov-dlg-b .kv { grid-template-columns: 1fr; gap: 1px 0; }
  .ov-dlg-b .kv dt { margin-top: 8px; }
  .ov-dlg-b .kv dt:first-child { margin-top: 0; }
  /* Диспетчер на телефоне — карточками в столбик: в трёх колонках по
     110 px «прерван перезапуском» вылезал в соседнюю. */
  .ov-jobs-t thead { display: none; }
  .ov-jobs-t, .ov-jobs-t tbody, .ov-jobs-t tr, .ov-jobs-t td { display: block; width: auto; }
  .ov-jobs-t tr { padding: 7px 0; border-bottom: 1px solid var(--border); }
  .ov-jobs-t td { padding: 2px 8px; border: 0; }
}

/* ---- Сводные площадок (/mp/ym, /mp/wb, /mp/ozon) ------------------------
   Три класса на весь экран: полоса ответа, строка-приписка под таблицей и
   раскрытие. Остальное собрано из панелей, таблиц и тегов, которые уже
   есть, — свой набор карточек тут завёл бы второй визуальный язык. */
.ys-head { display: flex; gap: 18px; align-items: center; justify-content: space-between;
  flex-wrap: wrap; background: var(--surface); border: 1px solid var(--border);
  border-left-width: 3px; border-radius: 12px; padding: 14px 18px; margin-bottom: 18px; }
.ys-head-l { font-size: 14.5px; line-height: 1.5; }
.ys-head-r { text-align: right; min-width: 200px; }
.ys-head-r .tile-v { font-size: 30px; line-height: 1.1; }
.ys-ok  { border-left-color: var(--ok); }
.ys-warn{ border-left-color: var(--warn); }
.ys-err { border-left-color: var(--err); }
.ys-mut { border-left-color: var(--border-2); }
/* Цепочка ступеней на сводной Ozon — те же четыре цвета, что у полосы
   ответа. Своего набора плиток не заводим: цвет уже назван словом в
   заголовке плитки, кант лишь позволяет найти незелёную взглядом. */
.tile.ys-ok, .tile.ys-warn, .tile.ys-err, .tile.ys-mut { border-left-width: 3px; }

/* Приписка под таблицей внутри той же панели: объясняет цифры, не заводя
   ради одной фразы ещё одну панель. */
.ys-line { padding: 10px 16px; border-top: 1px solid var(--border-soft);
  display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.ys-line.ys-bad { background: color-mix(in srgb, var(--err) 7%, transparent); }

.ys-more { background: var(--surface); border: 1px solid var(--border);
  border-radius: 12px; margin-bottom: 12px; }
.ys-more > summary { cursor: pointer; padding: 12px 16px; font-size: 13.5px;
  color: var(--text-2); list-style: none; }
.ys-more > summary::-webkit-details-marker { display: none; }
.ys-more > summary::before { content: '▸'; display: inline-block; width: 16px;
  color: var(--text-3); transition: transform .15s ease; }
.ys-more[open] > summary::before { transform: rotate(90deg); }
.ys-more[open] > summary { border-bottom: 1px solid var(--border-soft); }
/* Панель внутри раскрытия уже в рамке — вторая рамка давала бы двойной кант. */
.ys-more .panel { border: 0; border-radius: 0; margin: 0; }
/* Ссылка на фид — одно слово без пробелов, шире телефона: переносим по любому
   знаку, как артикул в каноне (.fs-sku), иначе она растягивает страницу. */
.ys-more a.mono { overflow-wrap: anywhere; }

/* Плотная таблица сводной: сводка живёт в бюджете двух экранов, и строка в
   164 px (подписи столбиком) растягивала её втрое. Подписи в строку. */
.ys-tbl td { padding-top: 7px; padding-bottom: 7px; }
.ys-tbl td .rp-note { margin-left: 4px; }
.ys-tbl .tbl-sub { margin-top: 1px; }
.ys-flow { display: flex; flex-wrap: wrap; gap: 3px 5px; align-items: baseline; }
.ys-flow .tag { margin: 0; }
/* Лента фактов вместо списка «ключ — значение»: пять коротких фактов
   столбиком занимали полэкрана. */
.ys-facts { display: flex; flex-wrap: wrap; gap: 8px 22px; font-size: 13px;
  color: var(--text-2); }
.ys-facts > span { max-width: 460px; }
/* Склады после прогона — блок на ИП (29.09.2026). Полоса на 100 % каталога:
   в продаже · готовы · прочие. Красный только у заблокированных с остатком. */
.ysk-ip + .ysk-ip { border-top: 1px solid var(--border-2); }
.ysk-ip { padding: 12px 16px 6px; }
.ysk-head { display: flex; flex-wrap: wrap; gap: 4px 12px; align-items: baseline;
  font-size: 14.5px; margin-bottom: 4px; }
.ysk-head .muted { font-size: 12.5px; }
.ysk-row { display: grid; grid-template-columns: 150px minmax(0, 1fr) 190px;
  gap: 6px 18px; align-items: center; padding: 8px 0;
  border-top: 1px solid var(--border-soft); }
.ysk-head + .ysk-row { border-top: 0; }
.ysk-city .rp-note { display: block; margin: 1px 0 0; }
.ysk-bar { display: flex; height: 10px; border-radius: 5px; overflow: hidden;
  background: var(--border-soft); }
.ysk-bar > i { display: block; height: 100%; }
.b-pub { background: var(--ok); }
.b-rdy { background: #f6b30e; }   /* «Готов к продаже»; цвет задал владелец 03.10.2026 */
.b-oth { background: var(--border-2); }
.b-err { background: var(--err); }
/* Число ошибок — красным тегом, как счётчик «Ошибки» на Озоне. */
.ysk-badge { display: inline-block; padding: 0 6px; border-radius: 4px; line-height: 17px;
  background: var(--err); color: #FFFFFF; font-weight: 600; }
a.ysk-badge, a.ysk-badge:hover { color: #FFFFFF; text-decoration: none; }
.ysk-nums { display: flex; flex-wrap: wrap; gap: 2px 18px; margin-top: 5px;
  font-size: 12.5px; color: var(--text-2); font-variant-numeric: tabular-nums; }
.ysk-nums b { color: var(--text); }
.ysk-d { display: inline-block; width: 8px; height: 8px; border-radius: 2px;
  margin-right: 5px; }
.ysk-blk { display: flex; flex-direction: column; gap: 3px; align-items: flex-end;
  font-size: 12.5px; }
.ysk-blk .tag { margin: 0; }
@media (max-width: 760px) {
  .ysk-row { grid-template-columns: minmax(0, 1fr); gap: 5px; }
  .ysk-blk { flex-direction: row; gap: 8px; align-items: baseline; }
}

/* ── Колокольчик в шапке (notify_view, 24.09.2026) ─────────────────────────
   Всплывашка на <details>, как фильтр ИП: без JS раскрывается сама. Цвет
   счётчика — по худшему непрочитанному; жёлтый фон несёт тёмный текст, иначе
   белая цифра на нём не читается. */
/* margin-left: auto — колокольчик в правом углу шапки и тогда, когда она на
   телефоне переносится на несколько строк (решение владельца 24.09.2026). */
.bell { position: relative; margin-left: auto; }
.bell > summary { list-style: none; cursor: pointer; position: relative;
  display: inline-grid; place-items: center; width: 36px; height: 36px;
  border: 1px solid var(--border); border-radius: 6px; color: var(--text-2);
  transition: background-color .18s ease, border-color .18s ease; }
.bell > summary::-webkit-details-marker { display: none; }
.bell > summary:hover { background: var(--panel); border-color: var(--border-2); color: var(--text); }
.bell > summary:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }
.bell[open] > summary { border-color: var(--acc); color: var(--text); }
.bell-n { position: absolute; top: -7px; right: -7px; min-width: 18px; height: 18px;
  padding: 0 5px; border-radius: 9px; font-size: 11px; font-weight: 600; line-height: 18px;
  text-align: center; color: #FFFFFF; background: var(--acc); }
.bell-n.lv-err  { background: var(--err); }
.bell-n.lv-warn { background: var(--warn); color: var(--deep); }
.bell-pop { position: absolute; z-index: 45; right: 0; top: calc(100% + 8px);
  width: min(430px, calc(100vw - 32px)); max-height: min(72vh, 600px); overflow: auto;
  padding: 6px; border-radius: 10px; border: 1px solid var(--border);
  background: var(--elev); box-shadow: var(--shadow-2); display: grid; gap: 2px; }
.bell-head { display: flex; justify-content: space-between; align-items: baseline; gap: 12px;
  padding: 6px 10px 9px; margin-bottom: 2px; border-bottom: 1px solid var(--border);
  font-size: 13px; color: var(--text); }
.bell-head a, .bell-more { font-size: 12.5px; }
.bell-i { display: grid; gap: 3px; padding: 8px 10px 8px 11px; border-radius: 6px;
  border-left: 3px solid transparent; font-size: 12.5px; line-height: 1.45;
  color: var(--text-2); text-decoration: none; }
.bell-i:hover { background: var(--panel); color: var(--text-2); }
.bell-i.lv-err  { border-left-color: var(--err); }
.bell-i.lv-warn { border-left-color: var(--warn); }
.bell-i.lv-ok   { border-left-color: var(--ok); }
.bell-i.lv-info { border-left-color: var(--acc); }
.bell-i.is-new  { background: var(--blue-soft); }
.bell-t { color: var(--text); font-weight: 600; font-size: 13px; }
.bell-x { overflow-wrap: anywhere; }
.bell-w { color: var(--text-3); font-size: 11.5px; }
.bell-empty, .bell-more { margin: 0; padding: 8px 10px; font-size: 12.5px; color: var(--text-2); }
/* Телефон: колокольчик после переноса шапки стоит где угодно, и окно от
   его правого края уезжало бы за левую кромку. Прибиваем к краям экрана. */
@media (max-width: 560px) {
  .bell-pop { position: fixed; left: 16px; right: 16px; top: 64px; width: auto; }
}
@media (prefers-reduced-motion: reduce) {
  .bell > summary { transition: none; }
}
/* Страница уведомлений — те же пункты, во всю ширину. */
.nt-list { display: grid; gap: 4px; }
/* Левый кант остаётся цветом уровня: рамку задаём без левой стороны. */
.nt-list .bell-i { background: var(--surface); border-style: solid; border-width: 1px 1px 1px 3px;
  border-top-color: var(--border); border-right-color: var(--border);
  border-bottom-color: var(--border); }
.nt-list .bell-i.is-new { background: var(--blue-soft); }

/* АВТОПРОХОД WB (/mp/wb, 25.09.2026): карточка на кабинет. Кант — цветом
   статуса, как у .ys-head; «идёт» — статусный синий, как у .jb-run. Пульс —
   только у счётчика в заголовке секции, чтобы на странице он был один. */
.av-grid { grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
  margin-bottom: 8px; }
.av-card { border-left-width: 3px; padding: 14px 16px; margin-bottom: 0;
  display: flex; flex-direction: column; gap: 10px; min-width: 0; }
.av-card.av-ok   { border-left-color: var(--ok); }
.av-card.av-warn { border-left-color: var(--warn); }
.av-card.av-err  { border-left-color: var(--err); }
.av-card.av-run  { border-left-color: var(--blue); }
.av-card.av-mut  { border-left-color: var(--border-2); }
.av-top { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.av-name { font-size: 14.5px; }
.av-link { font-size: 12.5px; white-space: nowrap; }
.av-state { font-size: 13.5px; line-height: 1.45; color: var(--text); }
.av-state .tbl-sub { display: block; margin-top: 2px; overflow-wrap: anywhere; }
.av-tags { display: flex; flex-wrap: wrap; gap: 6px; }
/* Чипы — переходы к редакторам карточки (27.09.2026): крупнее тега, чтобы попасть. */
.av-tags .av-go { height: auto; padding: 3px 10px; font-size: 12px; }
.av-tags .av-go:hover { text-decoration: none; border-color: currentColor; }
/* body a:hover (panel-theme.css) красил чип в цвет ссылки — держим цвет состояния. */
.av-tags .av-go.tag-ok:hover { color: var(--ok); } .av-tags .av-go.tag-warn:hover { color: var(--warn); }
.av-tags .av-go.tag-mut:hover { color: var(--text-3); }
@media (max-width: 640px) { .av-tags .av-go { padding: 8px 12px; } }
.av-more[id] { scroll-margin-top: 72px; }   /* без скрипта — как у .panel[id] */
.av-facts { display: grid; grid-template-columns: max-content minmax(0, 1fr);
  gap: 4px 12px; margin: 0; font-size: 12.5px; }
.av-facts dt { color: var(--text-3); }
.av-facts dd { margin: 0; overflow-wrap: anywhere; }
.av-main { margin-bottom: 0; }
.av-card .ys-more { margin: 0; border-radius: 8px; }
.av-card .ys-more > summary { padding: 9px 12px; font-size: 13px; }
.av-sw { list-style: none; margin: 0; padding: 4px 12px; }
.av-sw > li { display: flex; align-items: center; flex-wrap: wrap; gap: 6px 10px;
  padding: 8px 0; border-top: 1px solid var(--border-soft); }
.av-sw > li:first-child { border-top: 0; }
.av-sw-n { flex: 1 1 150px; min-width: 0; font-size: 13px; }
.av-sw-n .tbl-sub { display: block; margin-top: 2px; }
.av-sw form { margin: 0; }
.av-why { white-space: normal; margin-top: 4px; }
.av-note { margin: 0; padding: 0 12px 10px; }
.av-sched { padding: 10px 12px 0; }
.av-sched .fld { flex: 1 1 96px; }
.av-chk { display: inline-flex; align-items: center; gap: 6px; font-size: 13px;
  min-height: 40px; flex: 1 1 100%; }
.av-need { flex: 1 1 100%; font-size: 12.5px; color: var(--warn); overflow-wrap: anywhere; }
/* На узком .wave-form встаёт в столбик, и основа ширины становится высотой. */
@media (max-width: 560px) { .av-sched .fld, .av-chk, .av-need { flex: 0 0 auto; } }
/* Круглосуточно — время не спрашиваем: поля гаснут без скрипта. */
.av-sched:has(input[name="allday"]:checked) .av-hm { opacity: .45; pointer-events: none; }
.av-foot { margin: 0 0 18px; }
.av-live { font-size: 12px; font-weight: 600; }
.av-live .jb-d { animation: pulse 2.2s ease-out infinite; }
/* Идущий проход пульсирует и в своей карточке (владелец 28.09.2026: «не вижу
   индикации»). */
.av-card.av-run .av-state .jb-d { animation: pulse 1.4s ease-out infinite; }
@media (prefers-reduced-motion: reduce) {
  .av-live .jb-d, .av-card.av-run .av-state .jb-d { animation: none; }
}
/* Журнал событий кабинета — этапы как в журнале 1С (владелец 28.09.2026). */
.av-log-rec { border-top: 1px solid var(--border); padding: 8px 0; }
.av-log-rec > summary { cursor: pointer; font-size: 13px; overflow-wrap: anywhere; }
.av-log-st { font-family: 'IBM Plex Mono', monospace; font-size: 12px; color: var(--text-2);
             margin: 8px 0 2px; }
.av-log-ln { list-style: none; margin: 0; padding: 0;
             font-family: 'IBM Plex Mono', monospace; font-size: 12px; line-height: 1.55; }
.av-log-ln li { overflow-wrap: anywhere; }
.av-log-ln li.av-log-bad { color: var(--err); }

/* РЕШЕНИЯ «АКЦИЙ» НА ТЕЛЕФОНЕ — КАРТОЧКАМИ. Шесть колонок на 390 px
   уезжали вбок, и «что делать» с кнопкой оказывались за краем экрана.
   Гайдбук, «Мобильная 375»: строки таблицы становятся карточками, кнопки
   не мельче 44 px, горизонтальной прокрутки нет. Порог 900, а не 640: на
   планшете 768 таблица с min-width 780 обрезала кнопки справа. */
.dec-due { display: block; margin-top: 4px; white-space: nowrap; }
.dec-rubn { display: block; white-space: normal; min-width: 11em; }
.dec-idle { display: block; margin-top: 4px; color: var(--text-2); }
/* Статус выхода: пилюля длинная, поэтому переносится и не держит ширину. */
.dec-st { min-width: 12em; }
.dec-st .tag { white-space: normal; border-radius: 8px; margin: 0 0 4px; line-height: 1.35; }
.dec-mark { margin-top: 4px; }
@media (max-width: 900px) {
  .tbl-cards { min-width: 0; }
  .tbl-cards thead { display: none; }
  .tbl-cards, .tbl-cards tbody, .tbl-cards tr, .tbl-cards td { display: block; width: 100%; }
  .tbl-cards tbody tr { padding: 10px 0; border-bottom: 1px solid var(--border-soft); }
  .tbl-cards tbody tr:last-child { border-bottom: 0; }
  .tbl-cards td { border-bottom: 0; padding: 3px 14px; }
  .tbl-cards td.num { text-align: left; white-space: normal; }
  .tbl-cards .dec-rubn { min-width: 0; }
  .tbl-cards td[data-label]:not(:first-child)::before {
    content: attr(data-label); display: block; margin-top: 6px;
    font-size: 11px; font-weight: 600; text-transform: uppercase;
    letter-spacing: .06em; color: var(--text-3); font-family: Inter, sans-serif; }
  .tbl-cards td.tbl-act { padding-top: 10px; text-align: left; }
  .tbl-cards td.tbl-act .btn { min-height: 44px; display: inline-flex; align-items: center; }
  .tbl-cards .dec-mark .btn { min-height: 44px; }
}

/* СВОДНАЯ АКЦИЙ — светофор (actions_board). Три цвета владельца: зелёный —
   под контролем, оранжевый — нужны руки, красный — срочно. Цвет берём из
   существующих --ok/--warn/--err: один словарь цветов на панель. Шрифт
   смысловых строк не мельче 14 px (жалоба «мелкого много, толку мало»). */
.ab-rule { font-size: 14px; color: var(--text-2); margin: 0 0 14px; }
.ab-fresh { font-size: 13px; color: var(--text-3); margin: 0 0 12px; }
.ab-fresh > summary { cursor: pointer; width: max-content; max-width: 100%; }
.ab-fresh > p { margin: 6px 0 0; }
.ab-gd > summary.ab-gh { cursor: pointer; }
.ab-kpi { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px;
          margin: 0 0 16px; }
.ab-tile { display: flex; flex-direction: column; gap: 2px; padding: 14px 16px;
           background: var(--surface); border: 1px solid var(--border);
           border-left: 4px solid var(--border); border-radius: 12px; color: var(--text); }
.ab-tile:hover { text-decoration: none; border-color: var(--text-3); }
.ab-tile:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }
.ab-t-red { border-left-color: var(--err); }
.ab-t-orange { border-left-color: var(--warn); }
.ab-t-green { border-left-color: var(--ok); }
.ab-tile-k { font-size: 14px; font-weight: 600; }
.ab-tile-v { font-family: Montserrat, Inter, sans-serif; font-size: 28px; font-weight: 700;
             line-height: 1.15; font-variant-numeric: tabular-nums; }
.ab-t-red:not(.ab-zero) .ab-tile-v { color: var(--err); }
.ab-t-orange:not(.ab-zero) .ab-tile-v { color: var(--warn); }
.ab-t-green .ab-tile-v { color: var(--ok); }
.ab-zero .ab-tile-v { color: var(--text-3); }
.ab-tile-n { font-size: 13px; color: var(--text-3); }
.ab-short { display: none; }

.ab-group + .ab-group { border-top: 1px solid var(--border); }
.panel > .ab-group > .ab-gh, .ab-gd > summary.ab-gh { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 12px;
          margin: 0; padding: 14px 16px 10px; border: 0; font-size: 16px; }
.ab-gh > .tag { font-size: 14px; font-weight: 600; padding: 3px 12px; }
.ab-hint { font: 400 14px/1.4 Inter, sans-serif; color: var(--text-2); }
.ab-list { list-style: none; margin: 0; padding: 0 16px 8px; }
.ab-row { display: grid; align-items: start; gap: 6px 16px; padding: 12px 0;
          grid-template-columns: minmax(0, 2.4fr) minmax(0, 1fr) minmax(0, 1.3fr);
          border-top: 1px solid var(--border); font-size: 14px; line-height: 1.45; }
.ab-list > .ab-row:first-child { border-top: 0; }
.ab-name b { font-weight: 600; }
.ab-mp { display: inline-block; min-width: 52px; margin-right: 6px; padding: 1px 7px;
         border-radius: 6px; background: var(--border); color: var(--text-2);
         font-size: 12.5px; font-weight: 600; text-align: center; }
.ab-due { color: var(--warn); font-size: 13px; white-space: nowrap; }
.ab-reach b { font-variant-numeric: tabular-nums; }
.ab-act { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px; }
.ab-act .tag, .ab-pay .tag { font-size: 13px; font-weight: 600; padding: 2px 10px; }
.ab-act .dec-mark { margin: 0; }
.ab-act .ab-st { font-weight: 400; white-space: normal; }
.ab-more { grid-column: 1 / -1; }
.ab-more > summary { cursor: pointer; width: max-content; color: var(--text-3); font-size: 13px; }
.ab-more > summary:hover { color: var(--text); }
.ab-more-b { margin-top: 8px; padding: 10px 12px; border-radius: 8px;
             background: color-mix(in srgb, var(--border) 35%, transparent);
             color: var(--text-2); font-size: 14px; }
.ab-more-b p { margin: 0 0 6px; }
.ab-more-b p:last-child { margin: 0; }
.ab-idle { color: var(--text-3); }
.ab-many { margin: 0 0 6px; padding-left: 18px; }
.ab-info { margin: 0; border-width: 1px 0 0; border-radius: 0; }

@media (max-width: 900px) {
  .ab-row { grid-template-columns: minmax(0, 1fr) auto; }
  .ab-name { grid-column: 1 / -1; }
  .ab-act { justify-self: end; }
}
@media (max-width: 560px) {
  .ab-kpi { gap: 8px; }
  .ab-tile { padding: 10px 10px 10px 12px; }
  .ab-tile-k { font-size: 14px; line-height: 1.25; }
  .ab-long { display: none; }
  .ab-short { display: inline; }
  .ab-tile-v { font-size: 24px; }
  .ab-list { padding: 0 12px 6px; }
  .panel > .ab-group > .ab-gh, .ab-gd > summary.ab-gh { padding: 12px 12px 8px; }
  .ab-hint { font-size: 13.5px; }
}

/* Шапка на телефоне: плашки акции, скидки площадки и снимка уезжают в меню
   (base.html, head_pills), в шапке остаются меню, тема, вход и колокольчик —
   один ряд вместо четырёх. На широком экране копия в меню скрыта. */
.head-pills { display: contents; }
.side-pills { display: none; }
@media (max-width: 560px) {
  header.top .head-pills { display: none; }
  .side-pills { display: flex; flex-direction: column; align-items: flex-start;
                gap: 6px; padding: 10px 8px 4px; }
  .side-pills > .tag { white-space: normal; font-size: 12.5px; }
}

/* РЕПРАЙСЕР РРЦ (rrc.html): длинный артикул переносится, подписи фильтров над полем,
   на телефоне зоны нажатия не меньше 44 px. */
.rrc-wrap { overflow-wrap: anywhere; }
.rrc-filters label { display: flex; flex-direction: column; gap: 4px; font-size: 12px; color: var(--text-3); }
/* теги на --panel не держат 4,5:1 — подкладываем фон страницы (аудит G003, F1) */
.rrc-page .tag-ok, .rrc-page .tag-warn, .rrc-page .tag-err { background: var(--bg); }
[data-theme="light"] .rrc-page .tag-ok, [data-theme="light"] .rrc-page .tag-warn { background: var(--surface); }
.rrc-spp .tag { height: auto; min-height: 26px; padding-top: 3px; padding-bottom: 3px;
  white-space: normal; overflow-wrap: anywhere; }
/* колонка контента 710 px при окне 1024: таблица сжимается, а не уезжает за край */
.rrc-tbl { min-width: 0; }
.rrc-tbl th { white-space: normal; }
@media (max-width: 900px) {
  .rrc-sum.tbl-cards tbody tr { display: grid; grid-template-columns: 1fr 1fr; column-gap: 12px; }
  .rrc-sum.tbl-cards tbody td:first-child { grid-column: 1 / -1; }
  .rrc-filters label, .rrc-filters .inp { width: 100%; }
  .rrc-filters .inp, .rrc-filters .btn, .rrc-pages .tag { min-height: 44px; }
  .rrc-pages .tag { display: inline-flex; align-items: center; min-width: 44px; justify-content: center; }
}

/* СПП (spp.html): серверный SVG, цвета рядов — палитра данных --data-N (не акцент),
   подписи осей не мельче 11 px на телефоне: viewBox 720 сжимается вдвое. */
.spp-page .tag-ok, .spp-page .tag-warn, .spp-page .tag-err { background: var(--bg); }
[data-theme="light"] .spp-page .tag-ok, [data-theme="light"] .spp-page .tag-warn { background: var(--surface); }
.spp-page .panel h2 .tag { white-space: normal; height: auto; min-height: 22px; }
.spp-filters { margin-bottom: 12px; }
.spp-cards .tile-n { overflow-wrap: anywhere; }
.spp-cards .tile { border: 0; background: none; padding: 4px 0; }   /* не карточка в карточке и не ссылка */
.spp-plot { position: relative; }
/* max-width: на широком экране SVG не растягивается, подписи осей ≈ 14 px. */
.spp-chart { width: 100%; height: auto; display: block; overflow: visible; touch-action: pan-y; }
.spp-chart:focus-visible, .spp-chart a:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }
.spp-chart .hit, .spp-chart .col { fill: transparent; }
.spp-chart .hit { cursor: crosshair; }
.spp-chart a.hit { cursor: pointer; }
.spp-chart .bar { fill-opacity: .85; transition: fill-opacity .15s ease; }
.spp-chart a.hit:focus .bar { fill-opacity: 1; }
@media (hover: hover) { .spp-chart a.hit:hover .bar { fill-opacity: 1; } }
.spp-chart .bar.sel { stroke: var(--text); stroke-width: 2.5; fill-opacity: 1; }
.spp-chart .xh { stroke: var(--text-3); stroke-width: 1; stroke-dasharray: 3 3; display: none; pointer-events: none; }
.spp-chart .xh.on { display: inline; }
.spp-tip { position: absolute; top: 0; z-index: 2; pointer-events: none; white-space: pre-line;
           max-width: 240px; padding: 6px 10px; border-radius: 8px; font-size: 13px; line-height: 1.4;
           color: var(--text); background: var(--surface); border: 1px solid var(--border);
           box-shadow: 0 4px 14px rgba(0, 0, 0, .18); }
.spp-tip[hidden] { display: none; }
.spp-chart .g { stroke: var(--border-soft); stroke-width: 1; }
.spp-chart .ref { stroke: var(--text-3); stroke-width: 1.5; stroke-dasharray: 6 5; }
.spp-chart .wh { stroke: var(--text-2); stroke-width: 1.5; }
.spp-chart .t { fill: var(--text-2); font-size: 12px; }
.spp-chart .t.v { fill: var(--text); font-weight: 600; paint-order: stroke; stroke: var(--surface); stroke-width: 4px; stroke-linejoin: round; }   /* подложка прерывает ус под подписью */
.spp-legend { display: flex; flex-wrap: wrap; gap: 6px 16px; margin: 10px 0 0; font-size: 13px; }
.spp-legend > span { display: inline-flex; align-items: center; gap: 6px; }
.spp-key { display: inline-block; width: 10px; height: 10px; background: var(--c); border: 2px solid var(--c); }
.spp-key-circle { border-radius: 50%; }
.spp-key-diamond { transform: rotate(45deg) scale(.85); }
.spp-key-hollow { background: transparent; }
.spp-tbl { min-width: 0; }
.spp-tbl th { white-space: normal; }
@media (max-width: 600px) {
  .spp-chart .t { font-size: 24px; }
  .spp-chart .x2, .spp-chart .y0 { display: none; }   /* «0 %» налезает на первую дату */
  .spp-chart .pc { display: none; }   /* подписи столбиков без « %» — единица на оси */
}
@media (max-width: 400px) {   /* 320–400 px: иначе мельче 11 px; n — в подсказке и таблице */
  .spp-chart .t { font-size: 30px; }
  .spp-chart .nn { display: none; }
}
@media (max-width: 900px) {
  .spp-filters label, .spp-filters .inp { width: 100%; }
  .spp-filters .inp, .spp-filters .btn { min-height: 44px; }
  .spp-tbl.tbl-cards tbody tr { display: grid; grid-template-columns: 1fr 1fr; column-gap: 12px; }
  .spp-tbl.tbl-cards tbody td:first-child { grid-column: 1 / -1; }
}
