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

   Цвета заданы через light-dark(): пара значений в одной строке вместо двух
   палитр в разных местах файла. Так видно оба цвета токена разом, и новый
   не получится завести в одной теме, забыв про вторую.

   Первое объявление каждой пары — запасное, на случай браузера без
   light-dark(): он не поймёт вторую строку и оставит первую. Панель
   упаковывается в десктоп, и остаться совсем без цветов там дороже,
   чем повторить шестнадцать значений.

   Какая пара сработает, решает color-scheme ниже. */

:root {
  color-scheme: light dark;

  --bg: #0b101b;
  --bg: light-dark(#f5f7fb, #0b101b);
  --surface: #141b2b;
  --surface: light-dark(#ffffff, #141b2b);
  --surface-2: #1a2334;
  --surface-2: light-dark(#f2f5fa, #1a2334);
  --surface-3: #212c40;
  --surface-3: light-dark(#e9eef7, #212c40);
  --border: #26314a;
  --border: light-dark(#dde3ee, #26314a);
  --border-soft: #1e2739;
  --border-soft: light-dark(#e8ecf4, #1e2739);

  --text: #e9eef8;
  --text: light-dark(#131a29, #e9eef8);
  --text-2: #aab4c8;
  --text-2: light-dark(#4a5468, #aab4c8);
  /* Приглушённый текст доведён до WCAG AA. Было 3,47 в светлой теме
     и 4,34 в тёмной на панели — при норме 4,5 для обычного текста.
     А приглушённым у нас набраны подписи под числами, то есть ровно то,
     что объясняет, чем это число является. Стало 4,61 и 4,77. */
  --muted: #7b87a1;
  --muted: light-dark(#67717f, #7b87a1);

  /* Фирменные цвета AITract. Взяты из айдентики и названы её словами:
     мятный — знак и всё, что показывает «здесь система действует»;
     чернила и мел — фон и текст. Индиго и янтарь остаются интерфейсными
     и в знак не заходят.

     Мятный на светлом фоне берётся глубокий: #34E1A1 на белом не читается,
     и это ровно тот случай, ради которого в айдентике заведён mint-deep. */
  --mint: #34e1a1;
  --mint: light-dark(#0b9c60, #34e1a1);
  --ink: #0b1220;
  --chalk: #e8edf7;

  --accent: #6c7bff;
  --accent: light-dark(#4b57e8, #6c7bff);
  --green: #3ddc97;
  --green: light-dark(#0f9d58, #3ddc97);
  --teal: #2dd4bf;
  --teal: light-dark(#0d9488, #2dd4bf);
  --violet: #a78bfa;
  --violet: light-dark(#7c3aed, #a78bfa);
  --amber: #fbbf24;
  --amber: light-dark(#b45309, #fbbf24);
  --red: #f87171;
  --red: light-dark(#dc2626, #f87171);

  /* Тени на светлом фоне должны быть заметно легче: та же прозрачность,
     что держит форму карточки на тёмном, на белом читается как грязь. */
  --shadow: light-dark(rgba(18, 26, 41, 0.1), rgba(0, 0, 0, 0.35));
  --shadow-soft: light-dark(rgba(18, 26, 41, 0.07), rgba(0, 0, 0, 0.3));

  --radius: 12px;
  --radius-sm: 8px;
  --mono: ui-monospace, "Cascadia Mono", Consolas, monospace;
  --sidebar: 236px;
}

/* Выбор пользователя перебивает систему. Атрибут ставит сервер по cookie,
   ещё до первой отрисовки: если бы тему включал скрипт, страница успевала
   бы мигнуть чужим фоном на каждом переходе. */
:root[data-theme="light"] { color-scheme: light; }
:root[data-theme="dark"] { color-scheme: dark; }

* { box-sizing: border-box; }

/* Полосы прокрутки — тонкие и в цвет темы везде: системная серая полоса
   на тёмном фоне выглядит инородной и толще самого меню. */
* {
  scrollbar-width: thin;
  scrollbar-color: var(--border) transparent;
}
::-webkit-scrollbar { width: 7px; height: 7px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--border); border-radius: 4px; }
::-webkit-scrollbar-thumb:hover { background: var(--muted); }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 14px/1.55 -apple-system, "Segoe UI", Roboto, sans-serif;
  -webkit-font-smoothing: antialiased;
}

a { color: inherit; }

/* ---------- раскладка ---------- */
.shell { display: flex; min-height: 100vh; }

.sidebar {
  width: var(--sidebar);
  flex: none;
  background: var(--surface);
  border-right: 1px solid var(--border-soft);
  padding: 18px 12px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  position: sticky;
  /* Липнет и по горизонтали: широкая таблица прокручивает страницу вбок,
     и меню уезжало бы за левый край вместе с ней. */
  top: 0;
  left: 0;
  height: 100vh;
  overflow-y: auto;
  z-index: 10;
}

.brand {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 4px 8px 18px;
}
/* Подложка у знака своя, внутри SVG: панель поддерживает светлую тему,
   а неон на белом фоне пропадает. Поэтому здесь только размер. */
.brand-mark {
  width: 34px; height: 34px;
  border-radius: 10px;
  flex: none;
  /* Знак приходит картинкой со своей подложкой — фон под ним не нужен. */
  background: none;
  display: block;
}
.brand-title { font-weight: 600; line-height: 1.2; }
.brand-sub { color: var(--muted); font-size: 11px; }

.nav-strip { display: flex; flex-direction: column; gap: 4px; }
.account-select { width: 100%; margin-bottom: 10px; }
.account-text { min-width: 0; }

.nav-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 10px;
  border-radius: var(--radius-sm);
  text-decoration: none;
  color: var(--text-2);
}
.nav-item:hover { background: var(--surface-2); color: var(--text); }
.nav-item.active { background: var(--surface-3); color: var(--text); }
/* Активный пункт помечен мятным — тем же рельсом, что держит знак.
   Это единственное место в меню, где цвет означает «вы здесь». */
.nav-item.active .nav-icon { background: var(--mint); color: var(--ink); }
.nav-icon {
  width: 28px; height: 28px; flex: none;
  border-radius: 7px;
  background: var(--surface-3);
  display: grid; place-items: center;
  font-size: 13px;
}
/* Маячок раздела: красная точка на углу иконки. Точкой, а не заливкой —
   мятная заливка уже означает «вы здесь», и два цветных состояния на одном
   элементе перестали бы читаться. Работает и в узком меню, где подписи нет. */
.nav-icon { position: relative; }
.nav-alarm::after {
  content: "";
  position: absolute; top: -2px; right: -2px;
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--red);
  border: 2px solid var(--surface);
}
.nav-label { line-height: 1.25; }
.nav-label small { display: block; color: var(--muted); font-size: 11px; }

.sidebar-foot { margin-top: auto; padding-top: 16px; }

/* Переключатель темы. Правый верхний угол страницы: там его ищут глазами.
   Показывает, куда переключит, а не что сейчас: значок луны на светлом фоне
   понятен без пояснений, а солнце на светлом читалось бы как «уже включено».

   Позиционируется внутри содержимого, а не окна: панель агента, открываясь
   справа, сдвигает содержимое — вместе с ним уезжает и кнопка, вместо того
   чтобы оказаться под панелью. */
.theme-switch {
  position: absolute; top: 18px; right: 26px; z-index: 12;
  width: 34px; height: 34px;
  display: grid; place-items: center;
  background: var(--surface); border: 1px solid var(--border-soft);
  border-radius: 50%;
  color: var(--text-2); cursor: pointer;
}
.theme-switch:hover { background: var(--surface-2); color: var(--text); border-color: var(--border); }

/* Какую половину кнопки показать, решает та же настройка, что и палитру.
   Считать это скриптом нельзя: он выполняется после первой отрисовки,
   и значок успевал бы моргнуть. */
.theme-switch .to-dark { display: contents; }
.theme-switch .to-light { display: none; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .theme-switch .to-dark { display: none; }
  :root:not([data-theme="light"]) .theme-switch .to-light { display: contents; }
}
:root[data-theme="dark"] .theme-switch .to-dark { display: none; }
:root[data-theme="dark"] .theme-switch .to-light { display: contents; }
/* Карточка кабинета внизу меню. Она же кнопка меню: разделов в кабинете
   семь, и одна страница со всем сразу — это список, притворившийся разделом. */
.account-card {
  display: flex; align-items: center; gap: 10px;
  width: 100%; padding: 10px;
  background: var(--surface-2);
  border: none; border-radius: var(--radius-sm);
  text-decoration: none; color: inherit;
  font: inherit; text-align: left; cursor: pointer;
}
button.account-card:hover { background: var(--surface-3); }
button.account-card.active { background: var(--surface-3); box-shadow: inset 2px 0 0 var(--accent); }
.account-name {
  display: block; font-size: 13px; font-weight: 500;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.account-go { margin-left: auto; color: var(--muted); display: flex; flex: none; }
.account-go svg { transition: transform 150ms; }
button.account-card.open .account-go svg { transform: rotate(180deg); }

/* Меню кабинета. Разворачивается вверх — карточка стоит у нижнего края,
   и вниз ему некуда. Ширина своя, шире меню: в пунктах по две строки,
   и «Реферальная программа» в 210 пикселей не помещается. */
.cabinet-menu {
  position: fixed; z-index: 60;
  width: 276px; max-width: calc(100vw - 24px);
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 6px;
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.28);
  max-height: 74vh; overflow-y: auto;
}
.cabinet-menu[hidden] { display: none; }

.cabinet-item {
  display: block; padding: 8px 10px;
  border-radius: var(--radius-sm); text-decoration: none; color: var(--text);
}
.cabinet-item:hover { background: var(--surface-2); }
.cabinet-item.picked { background: var(--surface-3); }
.cabinet-item-title { display: block; font-size: 13.5px; }
.cabinet-item-note { display: block; color: var(--muted); font-size: 11.5px; margin-top: 1px; }

.cabinet-foot {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  margin-top: 6px; padding: 8px 10px 4px;
  border-top: 1px solid var(--border-soft);
  color: var(--muted); font-size: 11.5px;
}
.cabinet-foot button {
  background: none; border: 1px solid var(--border); border-radius: var(--radius-sm);
  color: var(--text-2); font: inherit; font-size: 12px; padding: 5px 10px; cursor: pointer;
}
.cabinet-foot button:hover { color: var(--red); border-color: var(--red); }

.avatar {
  width: 30px; height: 30px; border-radius: 50%;
  background: var(--surface-3); color: var(--text);
  display: grid; place-items: center; font-size: 12px; font-weight: 600;
}

/* position: relative — опора для кнопки темы в правом верхнем углу. */
.content { flex: 1; min-width: 0; padding: 22px 26px 60px; position: relative; }

/* ---------- шапка страницы ---------- */
/* Место справа оставлено под кнопку темы: у половины страниц в шапке стоит
   своя ссылка или переключатель, и без запаса они наезжали бы друг на друга. */
.page-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 20px; flex-wrap: wrap; margin-bottom: 18px; padding-right: 46px;
}
.page-title { font-size: 21px; font-weight: 600; margin: 0; }
.page-sub { color: var(--muted); font-size: 13px; margin-top: 2px; }

/* Переключатель, спрятанный до появления скриптов, обязан прятаться:
   `display` из этого правила сильнее браузерного `hidden`, и без своей
   строки ниже он остался бы на экране кнопкой, которая ничего не делает. */
.segmented[hidden] { display: none; }

.segmented {
  display: inline-flex;
  background: var(--surface-2);
  border: 1px solid var(--border-soft);
  border-radius: 10px;
  padding: 3px;
  gap: 2px;
}
.segmented a, .segmented button {
  padding: 5px 12px;
  border-radius: 7px;
  font-size: 13px;
  text-decoration: none;
  color: var(--text-2);
  border: none; background: none; cursor: pointer; font: inherit;
}
.segmented a:hover { color: var(--text); }
.segmented a.active { background: var(--surface); color: var(--text); font-weight: 500; }

.pill {
  padding: 4px 11px; border-radius: 999px; font-size: 12px;
  border: 1px solid var(--border);
  color: var(--text-2);
}
.pill-ok { color: var(--green); border-color: color-mix(in srgb, var(--green) 40%, transparent); }
.pill-danger { color: var(--red); border-color: color-mix(in srgb, var(--red) 45%, transparent); }
/* Тона состояний. Тем же языком, что и остальная панель: словами «отстаёт»
   и «не уехало» на одинаково серых пилюлях разницу видно только вчитавшись,
   а на карточке гипотезы её нужно видеть с расстояния вытянутой руки. */
.pill-warn { color: var(--amber); border-color: color-mix(in srgb, var(--amber) 40%, transparent); }
.pill-bad { color: var(--red); border-color: color-mix(in srgb, var(--red) 45%, transparent); }
.pill-muted { color: var(--muted); }

/* ---------- метрики ---------- */
.metrics {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 12px;
  margin-bottom: 22px;
}
@media (max-width: 1400px) { .metrics { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 860px) { .metrics { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

.metric {
  background: var(--surface);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius);
  padding: 14px 16px 15px;
}
.metric-label {
  color: var(--muted); font-size: 12px;
  display: flex; align-items: center; gap: 5px;
}
.metric-value {
  font-size: 25px; font-weight: 600; margin-top: 7px;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
  display: flex; align-items: baseline; gap: 9px; flex-wrap: wrap;
}
.metric-hint { color: var(--muted); font-size: 11.5px; margin-top: 5px; }
.metric-delta { font-size: 12.5px; font-weight: 500; }
.delta-good { color: var(--green); }
.delta-bad { color: var(--red); }
.delta-flat { color: var(--muted); }

.v-white { color: var(--text); }
/* Цена покупателя выделена: в ряду одинаковых чисел она отвечает
   на другой вопрос — не «сколько мы получим», а «что видит человек». */
.v-buyer { color: var(--teal); font-weight: 500; }
.v-green { color: var(--green); }
.v-teal { color: var(--teal); }
.v-violet { color: var(--violet); }
.v-amber { color: var(--amber); }
.v-red { color: var(--red); }

/* ---------- панели ---------- */
.panel {
  background: var(--surface);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius);
  padding: 18px 20px;
  margin-bottom: 18px;
}
.panel-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 16px; margin-bottom: 16px; flex-wrap: wrap;
}
.panel-title { font-size: 15px; font-weight: 600; margin: 0; }
.panel-sub { color: var(--muted); font-size: 12.5px; margin-top: 2px; }

.cols { display: grid; grid-template-columns: 1.25fr 1fr; gap: 18px; }
@media (max-width: 1150px) { .cols { grid-template-columns: 1fr; } }

/* ---------- движение денег ---------- */
.stack {
  display: flex; height: 8px; border-radius: 4px; overflow: hidden;
  background: var(--surface-3); margin-bottom: 18px;
}
.stack-part { display: block; height: 100%; }
.tone-ok { background: var(--green); }
.tone-pending { background: var(--accent); }
.tone-danger { background: var(--red); }
.tone-muted { background: var(--muted); }
.tone-violet { background: var(--violet); }
.tone-teal { background: var(--teal); }
.tone-amber { background: var(--amber); }

.flow-row {
  display: flex; align-items: center; gap: 12px;
  padding: 11px 0; border-bottom: 1px solid var(--border-soft);
}
.flow-row:last-child { border-bottom: none; }
.dot { width: 9px; height: 9px; border-radius: 50%; flex: none; }
.flow-main { flex: 1; min-width: 0; }
.flow-label { font-weight: 500; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.flow-hint { color: var(--muted); font-size: 12px; }
.flow-amount { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.flow-units { color: var(--muted); font-size: 12px; }

.badge {
  font-size: 11px; padding: 2px 8px; border-radius: 999px;
  background: var(--surface-3); color: var(--text-2);
}
/* Отмена не красная: это не претензия к товару, а несостоявшаяся доставка.
   Красным в этой таблице помечен возврат, и путать их цветом нельзя. */
.badge-warn { color: var(--amber); border-color: color-mix(in srgb, var(--amber) 40%, transparent); }

/* ---------- графики ---------- */
.chart { width: 100%; height: auto; display: block; }
/* Сетка — опора для глаза, а не линейка: почти невидима и не спорит
   с линией. Читать значения по ней не нужно, для этого есть подписи. */
.chart-grid { stroke: var(--border-soft); stroke-width: 1; opacity: 0.55; }
.chart-label { fill: var(--muted); font-size: 11px; }
.chart-axis { fill: var(--muted); font-size: 10px; opacity: 0.75; }


/* Ориентир на комбинированном графике: план, прошлый период, среднее.
   Пунктиром и приглушённо — это фон для сравнения, а не вторая правда. */
.chart-target {
  stroke: var(--muted); stroke-width: 1.5; stroke-dasharray: 5 4; opacity: 0.75;
}
/* Полоса наведения во всю высоту столбца: попасть мышью в узкий столбец
   нельзя, а в полосу вокруг него — можно. */
.chart-hit rect { cursor: default; }
.chart-hit:hover rect { fill: color-mix(in srgb, var(--accent) 8%, transparent); }
/* Легенда над графиком: цветная точка и название, как в референсе. */
.chart-legend .legend-item i { box-shadow: 0 0 8px currentColor; }

/* ---------- виды графика: кольцо и шкала ---------- */

/* Панель переключателей над графиком: период, шаг, вид. */
/* Кнопки поджаты по бокам: в одной строке их семь, и с обычными отступами
   выбор вида графика срывался на вторую строку — панель из-за одной кнопки
   становилась на полсотни пикселей выше. */
.chart-tools { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px; }
.chart-tools .segmented button { white-space: nowrap; padding-left: 9px; padding-right: 9px; }
.chart-tools .chart-period > summary { padding: 5px 9px; font-size: 13px; }
.chart-tools .chart-period.on > summary { color: var(--accent); }

/* Схема продаж — списком рядом с шагом. Своя рамка, как у выбора вида:
   это не третий сегмент периода, а отбор того, что вообще попало на график. */
.chart-scheme {
  background: var(--surface-2); border: 1px solid var(--border-soft); border-radius: 10px;
  padding: 3px;
}
.chart-scheme > summary { padding: 5px 10px; margin: 0; font-size: 13px; color: var(--text-2); }
.chart-scheme > summary:hover { color: var(--text); background: var(--surface); }
.chart-scheme[open] > summary { background: var(--surface); color: var(--text); }
.chart-scheme.on > summary { color: var(--accent); }
.chart-scheme .drop-list { min-width: 150px; }

/* Выбор вида графика: иконка с подписью, список раскрывается вправо
   и прижат к правому краю — кнопка стоит последней в строке. */
.chart-kind {
  margin-left: auto;
  background: var(--surface-2); border: 1px solid var(--border-soft); border-radius: 10px;
  padding: 3px;
}
.chart-kind > summary { padding: 5px 10px; margin: 0; font-size: 13px; color: var(--text-2); }
.chart-kind > summary:hover { color: var(--text); background: var(--surface); }
.chart-kind[open] > summary { background: var(--surface); color: var(--text); }
.chart-kind .drop-list { left: auto; right: 0; min-width: 168px; }

/* Пункт списка — кнопка, а не переключатель: выбор применяется сразу. */
.drop-option {
  display: flex; align-items: center; gap: 9px; width: 100%;
  background: none; border: none; border-radius: 6px; cursor: pointer;
  padding: 6px 8px; font: inherit; font-size: 13px; color: var(--text-2); text-align: left;
}
.drop-option:hover { background: var(--surface-2); color: var(--text); }
.drop-option.picked { color: var(--accent); font-weight: 500; }
#revenue-panel.loading { opacity: 0.55; transition: opacity 0.12s; }

/* Кольцо: диаграмма слева, легенда справа — на узком экране друг под другом. */
.donut-wrap { display: flex; align-items: center; gap: 24px; flex-wrap: wrap; }
.chart-donut { width: 220px; height: 220px; flex: none; }
.chart-donut-total { fill: var(--text); font-size: 20px; font-weight: 600; }
.chart-legend-wrap { flex-wrap: wrap; max-width: 460px; gap: 8px 16px; }

/* Шкала: подпись, полоса, значение — по строке на отрезок. */
.scale-chart { display: flex; flex-direction: column; gap: 7px; }
.scale-row { display: grid; grid-template-columns: 64px 1fr auto; align-items: center; gap: 12px; }
.scale-label { color: var(--muted); font-size: 12.5px; font-variant-numeric: tabular-nums; }
.scale-track {
  height: 12px; border-radius: 999px; background: var(--surface-2); overflow: hidden;
}
.scale-track i {
  display: block; height: 100%; border-radius: 999px;
  background: linear-gradient(90deg,
    color-mix(in srgb, var(--accent) 65%, transparent), var(--accent));
}
.scale-value {
  font-size: 13px; font-variant-numeric: tabular-nums; color: var(--text-2); min-width: 90px;
  text-align: right;
}
.chart-empty {
  color: var(--muted); font-size: 13px; text-align: center; padding: 40px 0;
}

/* ---------- таблицы ---------- */
.table-wrap {
  border: 1px solid var(--border-soft);
  border-radius: var(--radius);
  overflow: auto;
  background: var(--surface);
}
table { width: 100%; border-collapse: collapse; }
th, td { padding: 10px 13px; text-align: left; border-bottom: 1px solid var(--border-soft); }
th {
  background: var(--surface-2); font-size: 12.5px; font-weight: 600;
  color: var(--text-2); text-transform: uppercase; letter-spacing: 0.02em;
  position: sticky; top: 0; white-space: nowrap; z-index: 1;
}
tbody tr:last-child td { border-bottom: none; }
tbody tr:hover { background: var(--surface-2); }
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; font-family: var(--mono); }
td.code { font-family: var(--mono); font-size: 12px; word-break: break-all; }

.thumb {
  width: 40px; height: 40px; border-radius: 7px; object-fit: cover;
  background: var(--surface-3); flex: none;
}
.thumb-empty { display: grid; place-items: center; color: var(--muted); font-size: 15px; }
/* В заказах и возвратах строк на экране втрое больше, чем в каталоге:
   картинка в полный рост растянула бы таблицу так, что за строкой заказа
   пришлось бы листать. Здесь она опознаёт товар, а рассматривают его
   в карточке. */
.thumb-sm { width: 28px; height: 28px; border-radius: 5px; font-size: 12px; }
.product-cell { display: flex; align-items: center; gap: 11px; min-width: 0; }
.product-name {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  max-width: 330px;
}
.product-offer { color: var(--text-2); font-size: 12.5px; font-family: var(--mono); }

.ok { color: var(--green); }
.warn { color: var(--amber); }
.danger { color: var(--red); }
.muted { color: var(--muted); font-size: 13px; }

/* ---------- уведомления и действия ---------- */
.card-item {
  background: var(--surface);
  border: 1px solid var(--border-soft);
  border-left: 3px solid var(--muted);
  border-radius: 0 var(--radius) var(--radius) 0;
  padding: 15px 18px;
  margin-bottom: 11px;
}
.card-item.critical { border-left-color: var(--red); }
.card-item.warning { border-left-color: var(--amber); }
.card-item.info { border-left-color: var(--accent); }
.card-title { font-weight: 600; margin-bottom: 6px; }
.card-body { white-space: pre-wrap; color: var(--text-2); font-size: 13px; }

.empty {
  background: var(--surface);
  border: 1px dashed var(--border);
  border-radius: var(--radius);
  padding: 44px; text-align: center; color: var(--muted);
}
.note {
  background: color-mix(in srgb, var(--amber) 12%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--amber) 32%, var(--border));
  border-radius: var(--radius);
  padding: 12px 15px; font-size: 13px; margin: 12px 0;
}

button, select {
  background: var(--surface-2); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 7px 14px; font: inherit; cursor: pointer;
}
button:hover, select:hover { background: var(--surface-3); }
button.primary { background: var(--accent); border-color: var(--accent); color: #fff; }
button.primary:hover { filter: brightness(1.08); }

/* Выключенная кнопка должна выглядеть выключенной.
   Правила на это не было вовсе, и `disabled` меняла только поведение:
   в облаке «Снять цены» светилась как рабочая и молчала в ответ на нажатие —
   а это худший вид поломки, потому что человек винит себя, а не панель.
   Причину отказа держим в `title` рядом с самой кнопкой. */
button:disabled, button:disabled:hover, select:disabled {
  opacity: 0.55; cursor: default; filter: none;
}

form { display: inline; }
code { font-family: var(--mono); font-size: 12.5px; background: var(--surface-2); padding: 1px 5px; border-radius: 4px; }

/* ---------- вход ---------- */
.login-shell { min-height: 100vh; display: grid; place-items: center; padding: 24px; }
.login-card {
  width: 100%; max-width: 360px;
  background: var(--surface);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius);
  padding: 26px 24px 22px;
}
.login-card form { display: block; }
.login-lead { font-size: 15px; font-weight: 500; margin: 0 0 14px; }
.login-input {
  width: 100%; box-sizing: border-box;
  background: var(--surface-2); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 11px 13px; font: inherit;
}
.login-input:focus { outline: none; border-color: var(--accent); }
.login-submit { width: 100%; margin-top: 10px; padding: 11px 14px; }
.login-problem {
  background: color-mix(in srgb, var(--red) 14%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--red) 40%, var(--border));
  border-radius: var(--radius-sm);
  padding: 10px 12px; font-size: 13px; margin-bottom: 12px;
}
.login-cmd {
  background: var(--surface-2); border: 1px solid var(--border-soft);
  border-radius: var(--radius-sm); padding: 10px 12px;
  font-family: var(--mono); font-size: 12.5px; overflow-x: auto; margin: 12px 0;
}
.login-foot { margin: 16px 0 0; font-size: 12px; }
.login-open { display: inline-block; color: var(--accent); font-size: 13px; }

/* Подпись над полем. Не placeholder: тот исчезает, как только начали
   печатать, и человек, отвлёкшись, уже не знает, что это за поле. */
.login-label {
  display: block; margin: 12px 0 5px;
  font-size: 12px; color: var(--text-2);
}

/* Поле с кнопкой внутри: пароль и глазок. Кнопка не наезжает на текст —
   у поля справа отступ ровно под неё. */
.login-secret { position: relative; }
.login-secret .login-input,
.login-secret input[type="password"],
.login-secret input[type="text"] { padding-right: 42px; }
.login-eye {
  position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
  display: flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; padding: 0;
  background: none; border: none; border-radius: var(--radius-sm);
  color: var(--muted); cursor: pointer;
}
.login-eye:hover { color: var(--text-2); background: var(--surface-3); }
.login-eye[aria-pressed="true"] { color: var(--accent); }

/* Caps Lock — вторая причина «неверного пароля» после раскладки.
   Появляется только когда он включён. */
.login-caps { margin-top: 6px; font-size: 12px; color: var(--amber); }

/* Вход почтой Яндекса или Mail: кнопки во всю ширину, над разделителем. */
.login-providers { display: flex; flex-direction: column; gap: 8px; margin-bottom: 14px; }
.login-provider {
  display: flex; align-items: center; justify-content: center; gap: 9px;
  padding: 11px 14px;
  background: var(--surface-2); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  font-size: 13.5px; text-decoration: none;
}
.login-provider:hover { border-color: var(--border-strong, var(--accent)); }
.provider-mark {
  display: flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; border-radius: 50%;
  font-size: 12px; font-weight: 700; color: #fff;
}
.provider-yandex { background: #fc3f1d; }
.provider-mail { background: #005ff9; }

/* Разделитель способов входа: черта, разорванная словом «или». */
.login-divider {
  display: flex; align-items: center; gap: 10px;
  margin: 0 0 14px; color: var(--muted); font-size: 12px;
}
.login-divider::before,
.login-divider::after {
  content: ""; flex: 1; height: 1px; background: var(--border-soft);
}
.logout-form { display: block; margin-top: 10px; }
.logout-form button { width: 100%; }

/* ---------- телефон ---------- */
/* Панель свёрстана для монитора: меню сбоку фиксированной ширины, широкие
   таблицы. На узком экране меню становится полосой над содержимым, подписи
   пунктов прячутся, а таблицы прокручиваются внутри своей карточки —
   горизонтально ездить должна таблица, а не вся страница. */
@media (max-width: 760px) {
  .shell { flex-direction: column; }

  .sidebar {
    width: 100%; height: auto;
    position: static; overflow: visible;
    flex-flow: row wrap; align-items: center;
    gap: 8px; padding: 10px 12px;
    border-right: none; border-bottom: 1px solid var(--border-soft);
  }
  .brand { order: 1; padding: 0; flex: 1 1 auto; min-width: 0; }
  .sidebar-foot {
    order: 2; margin-top: 0; padding-top: 0; flex: none;
    display: flex; align-items: center; gap: 8px;
  }
  .account-select { width: auto; max-width: 140px; margin-bottom: 0; }
  .account-card { padding: 0; background: none; }
  .account-text { display: none; }
  .logout-form { margin-top: 0; }
  .logout-form button { padding: 7px 11px; font-size: 13px; }

  .nav-strip {
    order: 3; width: 100%;
    flex-direction: row; gap: 6px;
    overflow-x: auto; padding-bottom: 2px;
    scrollbar-width: none;
  }
  .nav-strip::-webkit-scrollbar { display: none; }
  .nav-item { flex: none; padding: 7px 10px; }
  .nav-label { white-space: nowrap; font-size: 13px; }
  .nav-label small { display: none; }

  .content { padding: 16px 13px 44px; }
  /* Меню на телефоне — полоса над содержимым, и угол страницы начинается
     сразу под ней. Кнопку темы поджимаем к краю вслед за отступами. */
  .theme-switch { top: 12px; right: 13px; width: 32px; height: 32px; }
  .page-head { padding-right: 40px; }
  .page-title { font-size: 19px; }

  /* Четыре вкладки шире телефона. Ездит полоса вкладок, а не вся страница:
     иначе от горизонтальной прокрутки уезжает и таблица, и шапка. */
  .segmented.tabs {
    display: flex; max-width: 100%;
    overflow-x: auto; scrollbar-width: none;
  }
  .segmented.tabs::-webkit-scrollbar { display: none; }
  .segmented.tabs a { white-space: nowrap; }
  .metric-value { font-size: 21px; }
  .metric { padding: 12px 13px 13px; }
  .panel { padding: 15px 14px; }
  .product-name { max-width: 150px; }
  th, td { padding: 9px 10px; }
  .login-card { padding: 22px 18px 20px; }
}

/* ---------- чат ---------- */
.chat { display: flex; flex-direction: column; gap: 12px; margin-bottom: 18px; }

.bubble {
  max-width: 78%;
  padding: 11px 14px;
  border-radius: var(--radius);
  overflow-wrap: anywhere;
}
/* Переносы сохраняем только там, где лежит сам текст ответа: на всём пузыре
   pre-wrap показывал бы ещё и отступы из шаблона — пустыми строками сверху
   и снизу. */
.bubble-user, .bubble-text { white-space: pre-wrap; }
.bubble-user {
  align-self: flex-end;
  background: var(--accent); color: #fff;
  border-bottom-right-radius: 4px;
}
.bubble-agent {
  align-self: flex-start;
  background: var(--surface);
  border: 1px solid var(--border-soft);
  border-bottom-left-radius: 4px;
}
.bubble-error {
  background: color-mix(in srgb, var(--red) 12%, var(--surface));
  border-color: color-mix(in srgb, var(--red) 35%, var(--border));
}
.bubble-trace {
  margin-top: 8px; padding-top: 7px;
  border-top: 1px solid var(--border-soft);
  color: var(--muted); font-size: 11.5px; white-space: normal;
}

/* Две колонки: разговор и работа агента рядом. Правая узкая и фиксированной
   ширины — это список, из которого берут, а не то, что читают. */
.chat-layout {
  display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 18px;
  align-items: start;
}
.chat-main { min-width: 0; border-radius: var(--radius); }
/* Цель перетаскивания — вся левая колонка: попасть мышью с карточкой в руке
   в строку ввода высотой в сорок пикселей неудобно, и промах читается
   как «не сработало». */
.chat-main.drop-on {
  outline: 2px dashed color-mix(in srgb, var(--accent) 60%, transparent);
  outline-offset: 6px;
}

/* Правая колонка липнет к верху: разговор длинный, и уезжающий вместе с ним
   список карточек пришлось бы догонять прокруткой вверх. */
.chat-side {
  position: sticky; top: 16px;
  max-height: calc(100vh - 32px); overflow-y: auto;
  display: flex; flex-direction: column; gap: 14px;
}
.side-title { font-size: 13px; font-weight: 600; }
.side-note, .side-foot { color: var(--muted); font-size: 11.5px; line-height: 1.45; }
.side-note { margin-top: -8px; }
.side-group { display: flex; flex-direction: column; gap: 12px; }
.side-head {
  display: flex; gap: 6px; align-items: baseline;
  font-size: 11px; text-transform: uppercase; letter-spacing: .04em; color: var(--text-2);
}
.side-empty { color: var(--muted); font-size: 12px; }

/* Карточки разделены тенью и полем, а не только рамкой: подряд идущие
   рамки сливались в один список, из которого не видно, где кончается одна
   задача и начинается следующая. */
.topic-card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-sm); padding: 11px 12px;
  box-shadow: 0 1px 3px var(--shadow-soft);
  display: flex; flex-direction: column; gap: 6px;
  cursor: grab; user-select: none;
}
.topic-card:hover { border-color: var(--accent); }
.topic-card.dragging { opacity: 0.5; cursor: grabbing; }
.topic-row { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }
.topic-kind {
  flex: none;
  font-size: 10px; text-transform: uppercase; letter-spacing: .04em;
  padding: 2px 6px; border-radius: 5px;
  background: var(--surface-3); color: var(--text-2);
}
/* Прототип отмечен цветом, а не только словом: карточка с выдуманными
   числами не должна выглядеть так же, как предложение на живые деньги. */
.topic-proto { color: var(--amber); }
.topic-name { font-size: 12.5px; font-weight: 500; overflow-wrap: anywhere; }
.topic-line { color: var(--text-2); font-size: 12px; line-height: 1.4; }
.topic-take { font-size: 12px; align-self: flex-start; }

/* Прикреплённая тема — внутри формы: пока она здесь, каждый вопрос уходит
   вместе с ней, и это должно быть видно. */
/* Атрибут hidden слабее правила ниже: без своей строчки блок темы виден
   всегда, а разметка прячет его именно атрибутом. */
.chat-topic[hidden] { display: none; }
.chat-topic {
  background: var(--surface-2); border: 1px solid var(--border-soft);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-sm); padding: 8px 10px; margin-bottom: 8px;
  display: flex; flex-direction: column; gap: 6px;
}
.topic-off {
  margin-left: auto; font-size: 11.5px; color: var(--muted);
  background: none; border: none; padding: 0; font-family: inherit; cursor: pointer;
}
.topic-off:hover { color: var(--text-2); }
.topic-what > summary { cursor: pointer; color: var(--text-2); font-size: 11.5px; }
.topic-what pre {
  margin: 6px 0 0; padding: 8px 10px;
  background: var(--surface-3); border-radius: var(--radius-sm);
  color: var(--text-2); font-size: 11.5px; line-height: 1.5;
  white-space: pre-wrap; overflow-wrap: anywhere;
}

/* Разобранный ответ живёт абзацами и таблицами, и pre-wrap добавлял бы
   к ним пустые строки из исходника. Пометку ставит скрипт — до него текст
   показывается как есть, переносами. */
.bubble-text.md-done { white-space: normal; }
.bubble-text p { margin: 0 0 7px; }
.bubble-text p:last-child, .bubble-text ul:last-child, .bubble-text ol:last-child {
  margin-bottom: 0;
}
.bubble-text h4 { margin: 0 0 6px; font-size: 14px; font-weight: 600; }
.bubble-text ul, .bubble-text ol { margin: 0 0 7px; padding-left: 20px; }
.bubble-text li { margin-bottom: 3px; }
.bubble-text code {
  font-family: var(--mono); font-size: 12.5px;
  background: color-mix(in srgb, var(--muted) 18%, transparent);
  padding: 1px 5px; border-radius: 4px;
}
.bubble-text a { color: var(--accent); text-decoration: underline; text-underline-offset: 2px; }

/* Действия под сообщением — те же, что в панели, и внутри пузыря: снаружи
   они выстраивались бы в столбец под каждым ответом и удлиняли переписку
   вдвое. На синем пузыре значки белые: серые на нём не видно. */
.bubble .a-actions { margin-top: 6px; padding: 0; }
.bubble-user .a-actions { text-align: right; }
.bubble-user .a-actions button { color: #fff; }
.bubble-user .a-actions button:hover {
  color: #fff; background: rgba(255, 255, 255, 0.18);
}

/* Шапка чата: беседа, новый чат, очистка. Кнопка «Открыть» рядом со списком
   нужна только там, где нет скриптов, — со скриптом список отправляет форму
   сам, и кнопка убирается. */
.chat-tools { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.chat-pick { display: flex; gap: 6px; align-items: center; }
.chat-conv { max-width: 200px; }

/* Выгрузка — списком из двух пунктов: markdown читают, csv разбирают.
   Пункты здесь ссылки, а не кнопки, поэтому им нужна отмена подчёркивания
   и та же ширина, что у кнопок в других списках. */
.chat-save .drop-list { left: auto; right: 0; min-width: 232px; }
.chat-save a.drop-option { text-decoration: none; }
.chat-pick.js-auto .chat-pick-go { display: none; }

/* Цитата: сообщение, про которое спрашивают. Полосой слева, как
   прикреплённая задача, — это одного рода вещи, только цитата на один
   вопрос. */
.chat-quote {
  display: flex; gap: 8px; align-items: center;
  background: var(--surface-2); border: 1px solid var(--border-soft);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-sm); padding: 7px 10px; margin-bottom: 8px;
}
.chat-quote .quote-mark { color: var(--accent); flex: none; }
.chat-quote .quote-text {
  color: var(--text-2); font-size: 12.5px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* display: flex сильнее скрытия браузером по атрибуту, и пустая полоса
   цитаты висела над полем ввода всегда. */
.chat-quote[hidden] { display: none; }

.chat-form { display: flex; flex-direction: column; }

/* Поле во всю ширину и на несколько строк — как в постановке задачи.
   Однострочное заставляло писать короче, чем нужно: вопрос про деньги
   редко умещается в одну фразу, а не видя написанного, его сокращают. */
.chat-box {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 10px 12px 9px;
}
.chat-box:focus-within { border-color: var(--accent); }
.chat-input {
  width: 100%; min-width: 0; display: block;
  background: none; color: var(--text); border: none;
  padding: 2px 0 8px; font: inherit; resize: none;
  min-height: 62px; max-height: 220px;
}
.chat-input:focus { outline: none; }
.chat-bar { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.chat-send { flex: none; padding: 9px 18px; margin-left: auto; }
.chat-send[disabled] { opacity: 0.6; cursor: default; }
.chat-hint { margin-top: 8px; font-size: 12px; }

/* Приложенные файлы над полем. Скрываются, когда их нет: пустая полоса
   отодвигала бы поле ввода вниз без причины. */
.chat-files { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; }
.chat-files[hidden] { display: none; }
.chat-thumbs { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 7px; }
.chat-thumbs img {
  width: 72px; height: 72px; object-fit: cover;
  border-radius: var(--radius-sm); display: block;
}

/* Усилители под полем: то же, что в постановке задачи, и теми же кнопками. */
.chat-boosters {
  display: flex; flex-wrap: wrap; gap: 7px; align-items: center; margin-top: 9px;
}

/* Ход работы на странице — те же классы, что в панели, только крупнее:
   там колонка 380 пикселей, здесь ширина страницы. */
.chat .a-run { margin: 4px 0 8px; }
.chat .a-run-title { font-size: 13.5px; }
.chat .a-step { font-size: 13px; }
.chat .a-notice { font-size: 13px; }

/* На узком экране колонка уезжает под разговор: перетаскивания там нет вовсе,
   и работает ссылка «Обсудить». Липкость снимаем — прилипший к верху список
   на телефоне съел бы пол-экрана. */
@media (max-width: 1080px) {
  .chat-layout { grid-template-columns: minmax(0, 1fr); }
  .chat-side { position: static; max-height: none; margin-top: 8px; }
}
@media (max-width: 760px) {
  .bubble { max-width: 92%; }
}

/* ---------- юнит-экономика ---------- */
.filters {
  display: flex; flex-wrap: wrap; gap: 9px; align-items: center;
  margin-bottom: 12px;
}
.filters form { display: inline-flex; gap: 9px; }
.filter-input {
  background: var(--surface); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 7px 11px; font: inherit; min-width: 200px;
}
.filter-reset { color: var(--accent); font-size: 13px; text-decoration: none; }
.check { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; color: var(--text-2); }

/* Полоса отбора под переключателем разделов.
   Раньше поля стояли строкой вплотную к вкладкам и читались как их
   продолжение: не было видно ни где кончается навигация, ни отобрано ли
   сейчас что-нибудь. Отдельная карточка с отступом отвечает и на то,
   и на другое — рядом с кнопкой написано, что именно отобрано. */
.filter-bar {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
  background: var(--surface); border: 1px solid var(--border-soft);
  border-radius: var(--radius); padding: 11px 14px;
  margin: 18px 0 16px;
}
.filter-bar .filter-input { min-width: 190px; }
/* Выпадающий список отбора выглядел системным полем чужого приложения:
   белая рамка браузера посреди тёмной панели. Здесь он такой же, как
   поле поиска рядом. */
select.filter-input {
  appearance: none;
  padding-right: 30px; min-width: 210px; cursor: pointer;
  background-image:
    linear-gradient(45deg, transparent 50%, currentColor 50%),
    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 16px) 52%, calc(100% - 11px) 52%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}
select.filter-input option { background: var(--surface); color: var(--text); }
.filter-note { color: var(--text-2); font-size: 12.5px; }

/* Выбор периода в шапке: кнопки и календарь одной полосой.
   Кнопок стало шесть вместо трёх, и в одну строку они больше не влезают —
   без переноса последняя уезжала под край экрана. */
.period-picker { flex-wrap: wrap; max-width: 100%; }
.period-bar { margin-top: 14px; }

table.economics th, table.economics td { white-space: nowrap; }
table.economics td.num { font-variant-numeric: tabular-nums; }

/* Полоса действий над таблицей: очередь, откат, подбор цены.
   Отдельной карточкой, а не в ряду с фильтрами: фильтр меняет вид,
   а эти кнопки — данные и то, что уедет в Ozon. */
.sheet-actions {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
  background: var(--surface); border: 1px solid var(--border-soft);
  border-radius: var(--radius); padding: 10px 14px; margin-bottom: 12px;
}
.sheet-actions form { display: inline-flex; align-items: center; gap: 8px; margin: 0; }
.sheet-actions label {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 13px; color: var(--text-2);
}
.sheet-actions .cell-input { width: 58px; text-align: center; }
/* Пояснение занимает остаток строки и прижимает ссылку настроек к краю. */
.sheet-actions-note { flex: 1; color: var(--muted); font-size: 12.5px; min-width: 200px; }

/* Подсказка под полем числового фильтра: синтаксис условия. */
.drop-hint { color: var(--muted); font-size: 11.5px; margin-top: 5px; }

/* Плитки заказа отчётов: карточка с описанием и кнопкой. Шире обычных
   метрик — в них текст, а не число.

   Своя типографика, а не метричная. Плитка метрики устроена так, что
   читается число, а подпись к нему приглушена: двенадцать пикселей серым.
   Здесь же читать нужно именно текст — название отчёта и то, что в нём
   лежит, — и в размере подписи он превращался в нечитаемую сноску. */
.reports-grid { grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); }
.reports-grid .metric { display: flex; flex-direction: column; }
.reports-grid .metric-label {
  color: var(--text); font-size: 16px; font-weight: 600; letter-spacing: -0.01em;
}
.reports-grid .metric-hint {
  color: var(--text-2); font-size: 13.5px; line-height: 1.45;
}
.reports-grid form { margin-top: auto; }
.reports-grid select { margin-left: 6px; }
.reports-grid .check { font-size: 13px; color: var(--text-2); }

/* Переключатель разделов и то, что идёт следом. Восемнадцати пикселей
   не хватало: фильтр под вкладками читался как их продолжение. */
#section-tabs { margin-bottom: 22px; }

/* Заголовок раздела внутри окна: в истории остатков их теперь два —
   движение по дням и разбивка по складам. */
.modal-section {
  font-size: 14px; font-weight: 600; margin: 18px 0 8px; color: var(--text);
}

/* Лист таблицы: своя область прокрутки.
   Без ограничения высоты липкой шапке не за что зацепиться — прокручивается
   страница целиком, а не таблица, и `position: sticky` ничего не держит.
   Заодно это даёт горизонтальную прокрутку между закреплёнными краями.

   Запас под окном — 140 пикселей, а не 290. Прежний считался от высокого
   экрана и на обычном оставлял от таблицы полосу в три строки: на мониторе
   1080 с масштабом 150 % окно браузера — 640 точек, и 100vh − 290 давало
   350. Убирать нечего: над таблицей ничего не закреплено, а под ней только
   строка страниц. Со ста сорока лист занимает почти всё, что есть.

   Больше не выжать: строка воронки — 84 точки, два десятка строк это
   1700, и столько экрана просто нет. Дальше листают. */
.table-wrap.sheet {
  max-height: calc(100vh - 140px);
  min-height: 420px;
  overflow: auto;
}
table.economics td { padding: 9px 12px; }
table.economics th { padding: 9px 12px; }

/* Закреплённые колонки: слева товар, справа итоги.
   Отступы справа проставляет panel.js по измеренной ширине. Ширину столбца
   назначает алгоритм раскладки таблицы, а не эти правила: заявленные здесь
   значения он свободно ужимает по содержимому, и офсеты, посчитанные по ним,
   разъезжаются. Значения ниже — только запас на случай, если скрипт не дошёл. */
.economics { --w-roi: 92px; --w-margin: 98px; }
.economics .pin { position: sticky; z-index: 2; background: var(--surface); }
/* Шапка теперь липнет к окну, а не к краю таблицы, и закреплённые колонки
   в ней должны быть выше и её самой, и раскрытого фильтра соседней. */
.economics thead .pin { z-index: 9; background: var(--surface-2); }
tbody tr:hover .pin { background: var(--surface-2); }

.economics .pin-l { left: 0; box-shadow: 1px 0 0 var(--border); }
.economics .pin-net {
  right: calc(var(--w-roi) + var(--w-margin));
  box-shadow: -1px 0 0 var(--border);
}
.economics .pin-margin { right: var(--w-roi); }
.economics .pin-roi { right: 0; }
.economics .pin-net, .economics .pin-margin, .economics .pin-roi { min-width: 88px; }

/* Кнопка разбора товара. Держится в закреплённой колонке, поэтому она
   доступна на любой горизонтальной прокрутке. */
.row-detail {
  flex: none; margin-left: auto;
  background: var(--surface-2); color: var(--muted);
  border: 1px solid var(--border); border-radius: 6px;
  padding: 3px 7px; font-size: 13px; line-height: 1.2; cursor: pointer;
}
.row-detail:hover { color: var(--text); border-color: var(--accent); }

/* Остаток — ссылка в историю, а не просто число. */
.stock-link {
  background: none; border: none; padding: 0; cursor: pointer;
  color: var(--text); font: inherit; font-variant-numeric: tabular-nums;
  border-bottom: 1px dashed var(--muted);
}
.stock-link:hover { color: var(--accent); border-bottom-color: var(--accent); }

/* ---------- окно разбора ---------- */
.modal-back {
  position: fixed; inset: 0; z-index: 50;
  background: color-mix(in srgb, #000 55%, transparent);
  display: grid; place-items: start center;
  padding: 40px 20px; overflow: auto;
}
/* Атрибут hidden слабее правила выше — без этого окно видно всегда. */
.modal-back[hidden] { display: none; }
.modal {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 20px 22px;
  width: min(1080px, 100%);
}
.modal-head {
  display: flex; justify-content: space-between;
  gap: 16px; align-items: flex-start; margin-bottom: 14px;
}
.modal-title { font-size: 17px; font-weight: 600; margin: 0; }
.modal-sub { color: var(--muted); font-size: 13px; margin-top: 3px; }
.modal-close {
  background: none; border: none; color: var(--muted);
  font-size: 26px; line-height: 1; cursor: pointer; padding: 0 4px;
}
.modal-close:hover { color: var(--text); }

.chart-legend {
  display: flex; gap: 16px; margin-bottom: 8px;
  font-size: 13px; color: var(--text-2);
}
.legend-item { display: inline-flex; align-items: center; gap: 6px; }
.legend-item i { width: 10px; height: 10px; border-radius: 3px; display: inline-block; }

/* Блок настроек гармошкой. summary — тот же заголовок, что был у h2,
   поэтому вид не меняется, добавляется только стрелка и нажимаемость. */
.card.fold > summary {
  cursor: pointer; list-style: none; user-select: none;
  display: flex; align-items: center; gap: 9px; margin: 0;
}
.card.fold > summary::-webkit-details-marker { display: none; }
.card.fold > summary::before {
  content: '▸'; color: var(--muted); font-size: 13px;
  transition: transform 120ms; display: inline-block;
}
.card.fold[open] > summary::before { transform: rotate(90deg); }
.card.fold[open] > summary { margin-bottom: 14px; }
.card.fold > summary:hover::before { color: var(--accent); }

/* ---------- справочник страницы ---------- */
.guide {
  margin-top: 26px;
  border: 1px solid var(--border-soft); border-radius: var(--radius);
  background: var(--surface);
}
.guide > summary {
  padding: 13px 18px; cursor: pointer; font-size: 14px; font-weight: 500;
  color: var(--text-2); list-style: none; user-select: none;
}
.guide > summary::-webkit-details-marker { display: none; }
.guide > summary::before { content: '?'; margin-right: 9px; color: var(--accent); font-weight: 700; }
.guide > summary:hover { color: var(--text); }
.guide[open] > summary { border-bottom: 1px solid var(--border-soft); }
.guide-body {
  padding: 6px 18px 18px;
  display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 4px 26px;
}
.guide-block { padding: 12px 0; }
.guide-title { font-weight: 600; font-size: 13.5px; margin-bottom: 4px; }
.guide-block p { margin: 0; color: var(--text-2); font-size: 13.5px; line-height: 1.6; }

/* Заголовок с объяснением: подчёркиванием показываем, что есть подсказка. */
th.has-hint { cursor: help; text-decoration: underline dotted 1px; text-underline-offset: 3px; }

/* ---------- инструменты таблиц: колонки и фильтры ---------- */
.sheet-tools {
  display: flex; flex-wrap: wrap; gap: 9px; align-items: center;
  margin-bottom: 10px;
}
.sheet-toggle, .col-picker > summary {
  background: var(--surface); color: var(--text-2);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 7px 12px; font: inherit; font-size: 13px; cursor: pointer;
  list-style: none; user-select: none;
}
.col-picker > summary::-webkit-details-marker { display: none; }
/* Тем же классом размечены и ссылки отбора — «Только с остатком»,
   «Сбросить фильтры». У строчного элемента вертикальные отступы не
   раздвигают строку, и ссылка вышла бы ниже соседних кнопок на пару
   пикселей: ряд из четырёх кнопок, одна из которых чуть съехала. */
a.sheet-toggle { display: inline-block; text-decoration: none; }
.sheet-toggle:hover, .col-picker > summary:hover { color: var(--text); border-color: var(--accent); }
.sheet-toggle.on { color: var(--text); border-color: var(--accent); }
.sheet-count { color: var(--muted); font-size: 12.5px; margin-left: auto; }

.col-picker { position: relative; }
.col-list {
  position: absolute; z-index: 20; top: calc(100% + 5px); left: 0;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 10px 12px;
  display: grid; grid-template-columns: repeat(2, minmax(150px, 1fr)); gap: 3px 14px;
  max-height: 340px; overflow: auto; box-shadow: 0 10px 30px var(--shadow);
}
.col-list label {
  display: flex; align-items: center; gap: 7px;
  font-size: 13px; color: var(--text-2); white-space: nowrap; cursor: pointer;
}
.col-list label:hover { color: var(--text); }

/* Строка фильтров прилипает под шапкой — её высоту проставляет sheet.js:
   в стилях она неизвестна, потому что зависит от шрифта и переносов. */
.filter-row td { position: sticky; z-index: 3; background: var(--surface-2); padding: 5px 8px; }
.filter-row td.pin { z-index: 5; }
.filter-cell {
  width: 100%; min-width: 62px;
  background: var(--surface); color: var(--text);
  border: 1px solid var(--border-soft); border-radius: 5px;
  padding: 4px 6px; font: inherit; font-size: 12.5px;
}
.filter-cell:focus { outline: none; border-color: var(--accent); }
.filter-cell.on { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 12%, var(--surface)); }

/* Каталог: та же механика закреплённой колонки, что и в юнит-экономике. */
table.catalog th, table.catalog td { white-space: nowrap; padding: 9px 12px; }
table.catalog .pin { position: sticky; z-index: 2; background: var(--surface); }
table.catalog thead .pin { z-index: 4; background: var(--surface-2); }
table.catalog .pin-l { left: 0; box-shadow: 1px 0 0 var(--border); }
tbody tr:hover .pin { background: var(--surface-2); }
/* Незаполненная карточка: без габаритов или закупки расчёт неполный. */
tr.row-incomplete .pin-l { box-shadow: 1px 0 0 var(--border), inset 3px 0 0 var(--amber); }

/* Заголовок группы внутри таблицы: «Доходы», «Расходы». */
td.group-row {
  background: var(--surface-2); font-weight: 600;
  font-size: 12px; text-transform: uppercase; letter-spacing: 0.02em;
  color: var(--text-2);
}

/* Разбор товара: слева статьи, справа графики. На узком экране — в столбик. */
.detail-grid { display: grid; grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr); gap: 16px; }
.detail-side { display: flex; flex-direction: column; gap: 14px; }
.detail-side .card { padding: 14px 16px; }
.detail-changes { margin: 8px 0 0; padding-left: 18px; font-size: 13px; color: var(--text-2); }
.detail-changes li { margin-bottom: 4px; }
@media (max-width: 900px) {
  .detail-grid { grid-template-columns: minmax(0, 1fr); }
}
/* Поле правки прямо в таблице.

   У этого правила когда-то потерялся селектор, и весь блок ниже висел
   без него — ширина не применялась вовсе, поля растягивались раскладкой
   таблицы до 177px и уводили правые колонки за край экрана.

   Ширину теперь ставит скрипт по длине значения, здесь — только запас
   на случай, если он не дошёл, и границы, за которые поле не выходит. */
.cell-input {
  width: 82px; min-width: 52px; max-width: 150px;
  text-align: right;
  background: var(--surface-2); color: var(--text);
  border: 1px solid var(--border); border-radius: 6px;
  padding: 4px 6px; font: inherit; font-size: 13px;
  /* Цифры одной ширины: без этого поле, посчитанное в ch, прыгало бы
     от того, какие именно цифры набраны. */
  font-variant-numeric: tabular-nums;
  transition: width 90ms ease-out;
}
.cell-input:focus { outline: none; border-color: var(--accent); background: var(--surface); }
.cell-input.saving { border-color: var(--amber); }
.fact { color: var(--text-2); font-size: 12px; margin-top: 3px; }
/* Правленая строка. Тень у закреплённой колонки двойная: разделитель края
   листа и метка правки — иначе одна перебивает другую. */
tr.row-edited .pin-l { box-shadow: 1px 0 0 var(--border), inset 3px 0 0 var(--accent); }
tr.row-edited td { background: color-mix(in srgb, var(--accent) 6%, transparent); }
tr.row-edited .pin { background: color-mix(in srgb, var(--accent) 6%, var(--surface)); }
td.flash { transition: background 120ms; background: color-mix(in srgb, var(--accent) 18%, transparent); }

/* Изменённое значение видно и после пересчёта: рамка держится, пока цена
   отличается от той, что пришла с сервера, а рядом показано исходное. */
.cell-input.changed {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 14%, var(--surface-2));
  font-weight: 600;
}
.was { color: var(--muted); font-size: 12px; margin-top: 3px; text-decoration: line-through; }

/* ---------- настройки ---------- */
.settings { display: flex; flex-direction: column; gap: 16px; max-width: 900px; }
.settings .card { padding: 18px 20px; }
.card-title { font-size: 15px; font-weight: 600; margin: 0 0 14px; }
.settings .grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 12px 16px;
}
.settings label { display: flex; flex-direction: column; gap: 5px; font-size: 13px; color: var(--text-2); }
.settings label.check { flex-direction: row; align-items: center; }
/* Поля ввода одного вида на всю панель. Раньше здесь стояли только text
   и select, и любое поле с типом number или date оставалось в оформлении
   браузера — светлый прямоугольник посреди тёмной страницы. Тип поля
   не должен решать, как оно выглядит. */
.settings input[type="text"],
.settings input[type="number"],
.settings input[type="date"],
.settings input[type="email"],
.settings select {
  background: var(--surface-2); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 8px 10px; font: inherit;
}
.settings input:focus, .settings select:focus {
  outline: none; border-color: var(--accent);
}
/* Крутилки у числовых полей: место занимают, попасть по ним мышью трудно,
   а число здесь набирают с клавиатуры. */
.settings input[type="number"] { appearance: textfield; }
.settings input[type="number"]::-webkit-outer-spin-button,
.settings input[type="number"]::-webkit-inner-spin-button { appearance: none; margin: 0; }

/* Строка условий расчёта: та же сетка, что в настройках, плюс кнопка,
   прижатая к нижнему краю — она стоит в одну линию с полями, а не под ними. */
.terms { margin-bottom: 14px; }
.terms .grid { align-items: end; }
.terms button {
  background: var(--accent); color: #fff; border: none;
  border-radius: var(--radius-sm); padding: 9px 18px;
  font: inherit; font-weight: 600; cursor: pointer; height: 38px;
}
.terms button:hover { filter: brightness(1.08); }
.terms .hint { font-size: 12px; color: var(--muted); margin-top: 6px; }
.settings p.muted { margin: 12px 0 0; }

/* ---------- база знаний ---------- */
.news-add { display: flex; gap: 9px; align-items: flex-end; margin-bottom: 4px; }
.news-item { margin-top: 14px; }
.news-head { display: flex; justify-content: space-between; gap: 14px; flex-wrap: wrap; }
.news-actions { display: flex; gap: 8px; flex: none; }
.news-confirmed { border-left: 3px solid var(--green); }
.news-steps {
  margin-top: 10px; padding-top: 10px;
  border-top: 1px solid var(--border-soft); font-size: 13px; color: var(--text-2);
}
.news-source { margin-top: 10px; font-size: 12px; color: var(--muted); }
.news-source summary { cursor: pointer; }

/* ---------- складное меню ---------- */
.brand-text { min-width: 0; }
.nav-collapse {
  margin-left: auto; flex: none;
  background: none; border: 1px solid transparent; border-radius: 7px;
  color: var(--muted); width: 26px; height: 26px; cursor: pointer;
  display: grid; place-items: center; padding: 0;
}
.nav-collapse:hover { color: var(--text); background: var(--surface-2); }
.nav-collapse svg { transition: transform 150ms; }
body.nav-slim .nav-collapse svg { transform: rotate(180deg); }

body.nav-slim .sidebar { width: 64px; padding: 18px 8px; }
body.nav-slim .brand-text,
body.nav-slim .nav-label,
body.nav-slim .account-select,
body.nav-slim .account-text,
body.nav-slim .account-go,
body.nav-slim .logout-form { display: none; }
/* Карточка кабинета в свёрнутом меню остаётся — это единственный вход
   в настройки, тарифы и документы. От неё виден только кружок с буквой,
   а список раскрывается поверх страницы, как и в развёрнутом. */
body.nav-slim .account-card { justify-content: center; padding: 8px 0; }
/* Колонка «знак, под ним стрелка» одной ширины: кнопка не гуляет по меню,
   а стрелка разворачивается на месте. */
body.nav-slim .brand {
  flex-direction: column; align-items: center; gap: 6px; padding: 4px 0 14px;
}
body.nav-slim .nav-collapse { margin: 0; width: 34px; }
body.nav-slim .nav-item { justify-content: center; padding: 11px 0; position: relative; }

/* Название раздела всплывает справа от иконки. Подсказку ставит скрипт
   отдельным слоем поверх страницы: рисовать её внутри меню нельзя —
   у меню overflow: auto, и всё, что выступает за край, обрезается.
   Родного title мало: он появляется с секундной задержкой. */
.nav-tip {
  position: fixed; z-index: 80;
  background: var(--surface-3); color: var(--text);
  padding: 5px 10px; border-radius: 7px; font-size: 12.5px; white-space: nowrap;
  border: 1px solid var(--border);
  pointer-events: none;
  box-shadow: 0 6px 20px var(--shadow-soft);
}

/* ---------- заказы и возвраты ---------- */

/* Таблица без своей области прокрутки: листается страница целиком.
   Шапка при этом липнет к окну, а не к краю таблицы — метрики и заметки
   уезжают вверх и не занимают треть экрана всё время, пока смотришь заказы.
   Прокрутка внутри таблицы держала бы их на виду постоянно. */
.table-wrap.flow { overflow: visible; }
.table-wrap.flow table { border-radius: inherit; }
.table-wrap.flow thead th { z-index: 5; }
/* Ячейки по углам скругляем сами: без своей прокрутки обрезать их рамкой
   контейнера больше нечем. */
.table-wrap.flow thead th:first-child { border-top-left-radius: var(--radius); }
.table-wrap.flow thead th:last-child { border-top-right-radius: var(--radius); }

/* Крупный переключатель разделов — для вкладок сводки: баллы, начисления,
   отчёты. Обычный сегмент теряется на странице с таблицами и метриками,
   а это выбор того, что вообще смотрим, а не мелкая настройка вида. */
.segmented.big {
  padding: 4px;
  margin-bottom: 18px;
  gap: 4px;
  border-color: var(--border);
}
.segmented.big a {
  padding: 9px 20px;
  font-size: 14px;
  font-weight: 500;
  border-radius: 8px;
}
.segmented.big a:hover { background: var(--surface); }
.segmented.big a.active {
  background: var(--accent);
  color: #fff;
  font-weight: 600;
  box-shadow: 0 2px 10px color-mix(in srgb, var(--accent) 45%, transparent);
}

/* Переключатель разделов стоит отдельной строкой над фильтрами: это выбор
   того, что смотрим, а не фильтр внутри выбранного. */
.sheet-head {
  display: flex; align-items: center; flex-wrap: wrap; gap: 14px; margin-bottom: 14px;
}
.segmented.tabs { margin-bottom: 0; }

/* Два ряда вкладок кабинета: пользовательские настройки и системные.
   Подпись слева, а не над рядом: рядов всего два, и заголовок над каждым
   растянул бы шапку на четыре строки ради двух слов. */
/* Ссылки-строки на обзоре кабинета: название и что там лежит. Не карточки
   сеткой — их девять, и сетка из девяти плиток читается как список, только
   занимает вдвое больше места. */
/* Справка. Оглавление ссылками в строку, статьи — списками: их читают
   не подряд, а по одной, придя за конкретным разделом. */
.help-index { display: flex; flex-wrap: wrap; gap: 6px 10px; }
.help-index a {
  font-size: 13px; text-decoration: none; color: var(--text-2);
  padding: 4px 10px; border-radius: 999px; background: var(--surface-2);
}
.help-index a:hover { color: var(--text); background: var(--surface-3); }
.help-block { margin-top: 14px; }
.help-title { font-size: 12px; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; }
.help-list { margin: 6px 0 0; padding-left: 18px; color: var(--text-2); font-size: 13.5px; }
.help-list li { margin-bottom: 5px; }
/* «На что обращать внимание» — то, что чаще всего читают неверно.
   Отмечено полосой: в сплошном списке оно тонет среди «что смотреть». */
.help-care { border-left: 2px solid var(--amber); padding-left: 16px; margin-left: 0; list-style: none; }
.help-care li { position: relative; }

/* ---------- панель работы агента ---------- */

/* Прототип помечен полосой: он выглядит как рабочий экран, и человек,
   принявший выдуманные числа за настоящие, примет по ним решение. */
.prototype-note { border-left: 3px solid var(--amber); }

/* Неделя плашками. Не календарь месяцем: план агента живёт неделей —
   дальше недели вперёд его расписание не имеет смысла показывать,
   а назад смотрят «что было вчера», а не «что было в марте». */
.week-line { display: flex; align-items: stretch; gap: 8px; margin-bottom: 10px; }
.week-move {
  display: grid; place-items: center; width: 32px; flex: none;
  border: 1px solid var(--border-soft); border-radius: var(--radius);
  background: var(--surface); color: var(--muted); text-decoration: none; font-size: 15px;
}
.week-move:hover { color: var(--text); border-color: var(--border); }
.week-back { margin-bottom: 14px; font-size: 12.5px; }
.week-back a { color: var(--accent); text-decoration: none; }
.week-strip { display: grid; grid-template-columns: repeat(7, 1fr); gap: 8px; flex: 1; min-width: 0; }
.week-day {
  display: flex; flex-direction: column; gap: 2px;
  padding: 10px 12px; border-radius: var(--radius);
  background: var(--surface); border: 1px solid var(--border-soft);
  text-decoration: none; color: var(--text-2);
}
.week-day:hover { border-color: var(--border); color: var(--text); }
.week-day.chosen { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, var(--surface)); color: var(--text); }
.week-day.is-today .week-name { color: var(--accent); font-weight: 600; }
/* Будущее приглушено: там нечего смотреть, там только план. */
.week-day.ahead { opacity: 0.62; }
.week-name { font-size: 12px; text-transform: uppercase; letter-spacing: .04em; }
.week-date { font-size: 15px; font-weight: 600; color: var(--text); }
.week-count { font-size: 11.5px; color: var(--muted); }
.week-flag { font-size: 11.5px; color: var(--amber); }
@media (max-width: 760px) {
  .week-strip { grid-template-columns: repeat(4, 1fr); }
}

/* Три колонки: задача, статус, отчёт. Ширины неравные намеренно — читают
   слева направо и дольше всего смотрят в правую. */
.plan-head, .plan-row {
  display: grid; grid-template-columns: minmax(0, 1.15fr) 168px minmax(0, 1.35fr);
  gap: 18px;
}
.plan-head {
  padding: 0 0 8px; border-bottom: 1px solid var(--border-soft);
  color: var(--muted); font-size: 11.5px; text-transform: uppercase; letter-spacing: .04em;
}
.plan-row { padding: 14px 0; border-bottom: 1px solid var(--border-soft); align-items: start; }
.plan-row:last-child { border-bottom: none; }
@media (max-width: 900px) {
  .plan-head { display: none; }
  .plan-row { grid-template-columns: 1fr; gap: 10px; }
}

.plan-task { display: flex; gap: 12px; }
/* Время моноширинным и в колонку: по нему глаз идёт вниз по расписанию. */
.plan-time { font-family: var(--mono); font-size: 13px; color: var(--muted); padding-top: 1px; }
.plan-title { font-size: 14px; font-weight: 500; }
.plan-purpose { color: var(--muted); font-size: 12.5px; margin-top: 2px; }
.plan-meta { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 6px; font-size: 11.5px; }

/* Режим задачи цветом: «делает сам» должно быть видно издалека. */
.mode-chip {
  font-size: 11.5px; padding: 2px 9px; border-radius: 999px;
  border: 1px solid var(--border); color: var(--text-2); white-space: nowrap;
}
.mode-auto { color: var(--green); border-color: color-mix(in srgb, var(--green) 40%, transparent); }
.mode-ask { color: var(--amber); border-color: color-mix(in srgb, var(--amber) 45%, transparent); }
.mode-advice { color: var(--text-2); }

.plan-status { display: flex; flex-direction: column; gap: 5px; font-size: 12px; }
.plan-outcome { color: var(--text-2); font-size: 12px; }
.plan-changes { color: var(--accent); font-size: 12.5px; text-decoration: none; }
.plan-changes:hover { text-decoration: underline; }
.tone-ok { color: var(--green); border-color: color-mix(in srgb, var(--green) 40%, transparent); }
.tone-amber { color: var(--amber); border-color: color-mix(in srgb, var(--amber) 45%, transparent); }
.tone-pending { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 45%, transparent); }
.tone-danger { color: var(--red); border-color: color-mix(in srgb, var(--red) 45%, transparent); }
.tone-muted { color: var(--muted); }

/* Отчёт: суть прямо в карточке, файл — рядом. Отчёт, ради сути которого
   надо скачать файл, читают один раз, а потом перестают. */
.plan-report { display: flex; flex-direction: column; gap: 10px; }
.report-card {
  background: var(--surface-2); border: 1px solid var(--border-soft);
  border-radius: var(--radius-sm); padding: 10px 12px;
}
.report-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 12.5px; }
.report-kind {
  font-size: 10.5px; text-transform: uppercase; letter-spacing: .04em;
  padding: 2px 7px; border-radius: 5px; background: var(--surface-3); color: var(--text-2);
}
.report-xlsx { color: var(--green); }
.report-pdf { color: var(--red); }
.report-title { font-weight: 500; color: var(--text); }
.report-lines { margin: 8px 0 0; padding-left: 16px; color: var(--text-2); font-size: 12.5px; }
.report-lines li { margin-bottom: 3px; }
.report-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 10px; }
.report-actions button {
  font: inherit; font-size: 12px; padding: 5px 10px;
  background: var(--surface); color: var(--text-2);
  border: 1px solid var(--border); border-radius: var(--radius-sm); cursor: pointer;
}
.report-actions button:disabled { opacity: 0.6; cursor: default; }
.report-empty { color: var(--muted); font-size: 12.5px; padding-top: 2px; }

/* Настройки агента: режимы легендой, чтобы не объяснять их в каждой строке. */
.modes-legend { display: flex; flex-direction: column; gap: 8px; margin-bottom: 16px; }
.mode-legend { display: flex; align-items: center; gap: 10px; font-size: 12.5px; flex-wrap: wrap; }
.jobs .job-when input, .jobs .job-mode select {
  background: var(--surface-2); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 6px 9px; font: inherit; font-size: 12.5px; width: 100%;
}
.jobs td { vertical-align: top; }

/* Окно постановки задачи внизу раздела. Во всю ширину и с большим полем:
   сюда пишут работу на несколько шагов, и в однострочном поле, где не видно
   начала фразы, человек пишет короче, чем нужно. Прижато к содержимому,
   а не к окну: это часть раздела, а не вторая плавающая панель. */
.composer {
  margin-top: 18px; padding: 16px 18px 14px;
  background: var(--surface); border: 1px solid var(--border-soft); border-radius: var(--radius);
}
.composer-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 16px; flex-wrap: wrap; margin-bottom: 12px;
}
/* Навыки: меню у кнопки рядом с диктовкой. Компактное — одни названия,
   описание всплывает рядом при наведении. */
.skills-drop { position: relative; }
.composer-tool.on { color: var(--text); border-color: var(--accent); }
.skills-menu {
  position: absolute; bottom: calc(100% + 8px); left: 0; z-index: 40;
  min-width: 236px; padding: 6px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: 0 16px 36px rgba(0, 0, 0, 0.28);
}
.skills-menu[hidden] { display: none; }
.skill-item {
  display: block; width: 100%; text-align: left; cursor: pointer;
  font: inherit; font-size: 13px; padding: 8px 10px;
  background: none; border: none; border-radius: var(--radius-sm); color: var(--text);
}
.skill-item:hover { background: var(--surface-2); }
.skill-none { color: var(--muted); border-top: 1px solid var(--border-soft); border-radius: 0; margin-top: 4px; }

/* Описание навыка — слоем поверх страницы: внутри меню его обрезало бы
   по краю, а прочитать его нужно до выбора, а не после. */
.skill-note-pop {
  position: fixed; z-index: 90; width: 320px;
  padding: 10px 12px; border-radius: var(--radius-sm);
  background: var(--surface-3); color: var(--text-2);
  border: 1px solid var(--border); box-shadow: 0 12px 30px rgba(0, 0, 0, 0.3);
  font-size: 12px; line-height: 1.5; pointer-events: none;
}
.skill-note-pop[hidden] { display: none; }
.skill-note-pop > div + div { margin-top: 7px; }
.note-models { color: var(--text); }
/* Расход выделен: о нём узнают до выбора, а не по счёту в конце месяца. */
.note-spend { color: var(--amber); }
.note-gap { color: var(--muted); }

/* Модели в полосе выбранного навыка: название, цена, зачем она здесь. */
.picked-models { margin-top: 12px; }
.picked-label { color: var(--muted); font-size: 11px; text-transform: uppercase; letter-spacing: .04em; margin-bottom: 6px; }
.model-row {
  display: grid; grid-template-columns: 152px 128px minmax(0, 1fr); gap: 10px;
  padding: 5px 0; font-size: 12.5px; align-items: baseline;
}
@media (max-width: 760px) { .model-row { grid-template-columns: 1fr; gap: 2px; } }
.model-name { font-weight: 500; }
.model-price { color: var(--amber); font-size: 11.5px; }
.model-why { color: var(--text-2); line-height: 1.45; }
.picked-spend {
  margin: 10px 0 0; padding: 9px 12px;
  background: color-mix(in srgb, var(--amber) 12%, transparent);
  border-left: 2px solid var(--amber); border-radius: var(--radius-sm);
  color: var(--text-2); font-size: 12.5px; line-height: 1.5;
}
.picked-spend b { color: var(--text); }

/* Выбранный навык — полосой над полем: от него зависит, чем агент будет
   работать, и это должно быть видно, пока пишут задачу. */
.picked-skill {
  margin-bottom: 12px; padding: 12px 14px;
  background: color-mix(in srgb, var(--accent) 8%, var(--surface-2));
  border: 1px solid color-mix(in srgb, var(--accent) 35%, var(--border-soft));
  border-radius: var(--radius);
}
.picked-skill[hidden] { display: none; }
.picked-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.picked-name { font-size: 13.5px; font-weight: 600; }
.picked-off {
  font: inherit; font-size: 12px; cursor: pointer;
  background: none; border: 1px solid var(--border); border-radius: var(--radius-sm);
  color: var(--text-2); padding: 4px 10px;
}
.picked-off:hover { color: var(--text); border-color: var(--accent); }
.picked-who { margin: 6px 0 8px; color: var(--text-2); font-size: 12.5px; line-height: 1.45; }
.picked-tools { display: flex; flex-wrap: wrap; gap: 6px; }
.picked-asks { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.picked-gap { margin: 10px 0 0; color: var(--amber); font-size: 12px; line-height: 1.45; }
.tool-chip {
  font-size: 11.5px; padding: 3px 9px; border-radius: 999px;
  background: var(--surface-3); color: var(--text-2);
}
.ask-chip {
  font: inherit; font-size: 12.5px; text-align: left; cursor: pointer;
  padding: 6px 11px; border-radius: var(--radius-sm);
  background: var(--surface); color: var(--text-2); border: 1px solid var(--border-soft);
}
.ask-chip:hover { color: var(--text); border-color: var(--accent); }

.composer-box {
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface-2); padding: 12px 12px 10px;
}
.composer-box:focus-within { border-color: var(--accent); }
#composer-text {
  width: 100%; min-height: 92px; resize: vertical;
  background: none; border: none; color: var(--text);
  font: inherit; font-size: 14px; line-height: 1.5;
}
#composer-text:focus { outline: none; }
.composer-files { color: var(--text-2); font-size: 12px; padding: 6px 0 0; }

.composer-bar { display: flex; align-items: center; gap: 8px; margin-top: 8px; flex-wrap: wrap; }
/* То же самое: кнопку инструмента прячут атрибутом, а inline-flex ниже
   это перебивает. */
.composer-tool[hidden] { display: none; }
.composer-tool {
  display: inline-flex; align-items: center; gap: 6px;
  font: inherit; font-size: 12.5px; padding: 6px 11px;
  background: var(--surface); color: var(--text-2);
  border: 1px solid var(--border-soft); border-radius: var(--radius-sm); cursor: pointer;
}
.composer-tool:hover { color: var(--text); border-color: var(--border); }
/* Идущая запись видна и без звука: иначе непонятно, слушает ли она вообще. */
#composer-mic.on { color: var(--red); border-color: var(--red); animation: a-pulse 1.2s infinite; }
.composer-hint { flex: 1; text-align: right; color: var(--muted); font-size: 11.5px; }
.composer-send {
  background: var(--accent); color: #fff; border: none; border-radius: var(--radius-sm);
  padding: 9px 18px; font: inherit; font-size: 13.5px; cursor: pointer;
}

/* Усилители — переключатели, а не кнопки отправки: нажал, увидел в тексте
   запроса, что добавилось, и оставил включённым до следующего вопроса. */
.composer-boosters { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 12px; }
.composer-label { color: var(--muted); font-size: 11.5px; text-transform: uppercase; letter-spacing: .04em; }
.boost-chip {
  font: inherit; font-size: 12px; padding: 5px 11px; border-radius: 999px;
  background: var(--surface-2); color: var(--text-2);
  border: 1px solid var(--border-soft); cursor: pointer;
}
.boost-chip:hover { color: var(--text); }
.boost-chip.on {
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  border-color: var(--accent); color: var(--text);
}

/* Итоговый запрос — до отправки, а не после: из него человек и понимает,
   что делают навыки и усилители, вместо того чтобы верить на слово. */
.composer-preview { margin-top: 12px; }
.composer-preview > summary { cursor: pointer; color: var(--text-2); font-size: 12.5px; }
.composer-preview pre {
  margin: 8px 0 0; padding: 12px 14px; white-space: pre-wrap;
  background: var(--surface-2); border: 1px solid var(--border-soft);
  border-radius: var(--radius-sm); color: var(--text-2);
  font-family: var(--mono); font-size: 12.5px; line-height: 1.6;
}
.composer-note { font-size: 11.5px; margin-top: 10px; }

/* Частые вопросы: свёрнутый список. Развёрнутые двенадцать ответов — это
   страница, которую листают, вместо того чтобы найти свой вопрос. */
.faq { border-bottom: 1px solid var(--border-soft); }
.faq:last-child { border-bottom: none; }
.faq > summary {
  cursor: pointer; padding: 12px 0 12px 22px; position: relative;
  font-size: 14px; font-weight: 500; list-style: none;
}
.faq > summary::-webkit-details-marker { display: none; }
.faq > summary::before {
  content: '+'; position: absolute; left: 4px; top: 11px;
  color: var(--muted); font-weight: 400;
}
.faq[open] > summary::before { content: '−'; color: var(--accent); }
.faq > summary:hover { color: var(--accent); }
.faq p {
  margin: 0 0 14px; padding-left: 22px; max-width: 760px;
  color: var(--text-2); font-size: 13.5px;
}

/* Реферальный код: крупно и отдельно — его чаще диктуют голосом,
   чем нажимают на ссылку. */
.referral-code {
  font-family: var(--mono); font-size: 30px; font-weight: 600;
  letter-spacing: .12em; padding: 14px 16px; margin-bottom: 14px;
  background: var(--surface-2); border: 1px dashed var(--border);
  border-radius: var(--radius); text-align: center;
}
.referral-link { display: flex; flex-direction: column; gap: 5px; font-size: 13px; color: var(--text-2); }
.referral-link input {
  background: var(--surface-2); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 9px 10px; font: inherit; font-size: 13px;
}

/* Правовые тексты: колонка уже страницы — длинную строку глаз теряет. */
.legal { max-width: 760px; color: var(--text-2); font-size: 13.5px; }
.legal p { margin: 0 0 12px; }
.legal b { color: var(--text); }

.hub-links { display: flex; flex-direction: column; }
.hub-link {
  display: block; padding: 12px 12px 12px 14px;
  border-radius: var(--radius-sm); text-decoration: none;
  border-left: 2px solid transparent;
}
.hub-link:hover { background: var(--surface-2); border-left-color: var(--accent); }
.hub-name { display: block; font-size: 14px; font-weight: 500; }
.hub-note { display: block; color: var(--muted); font-size: 12.5px; margin-top: 2px; }

.tab-groups { display: flex; flex-direction: column; gap: 8px; margin-bottom: 18px; }
.tab-group { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.tab-group-name {
  color: var(--muted); font-size: 11.5px; text-transform: uppercase;
  letter-spacing: .04em; min-width: 118px;
}
@media (max-width: 760px) {
  .tab-group { align-items: flex-start; flex-direction: column; gap: 4px; }
  .tab-group-name { min-width: 0; }
}

/* Форма фильтров сама ничего не показывает: её поля стоят в шапке таблицы,
   а здесь остаётся только кнопка на случай выключенных скриптов. */
.head-form { margin: 0; }
.head-form [data-fallback] { margin-bottom: 12px; }
/* Пока идёт перезапрос, таблица глохнет — видно, что цифры сейчас сменятся. */
.head-form.loading ~ #sheet-view { opacity: 0.55; transition: opacity 0.12s; }

.order-row { cursor: pointer; }
.order-row:hover td { background: var(--surface-2); }

/* Номера, схемы и даты не переносим: «40847216-0320-5», разорванный на две
   строки, перестаёт читаться как номер и удваивает высоту строки. Длинные
   тексты, наоборот, обрезаем — целиком они доступны наведением. */
.sheet.orders td:nth-child(-n+3),
.sheet.orders td:nth-child(5),
.sheet.orders td:nth-child(6),
.sheet.returns td:nth-child(-n+5) { white-space: nowrap; }

/* Склад: «Митино Real FBS EXPRESS (Ангелов пер 6к2)» шире всей колонки. */
.sheet.orders td:nth-child(4) {
  max-width: 150px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.sheet.orders td:nth-child(5) small { display: block; color: var(--muted); font-size: 11px; }

/* ---------- товары ---------- */

/* Строка над таблицей: сколько строк слева, сброс отбора справа. */
.sheet-line { display: flex; align-items: center; gap: 14px; margin-bottom: 10px; }
.sheet-line span { flex: 1; }

/* Шестерёнка выбора колонок. Стоит над таблицей — как в кабинете Ozon,
   где её и ищут.

   Прилипает к левому краю: широкая таблица прокручивает страницу вбок,
   и панель уезжала вместе с ней — ровно тогда, когда до неё и нужно
   дотянуться, чтобы спрятать лишние колонки. */
.sheet-tools {
  justify-content: flex-start; margin-bottom: 8px;
  position: sticky; left: 0; z-index: 8; width: fit-content;
}
.col-picker > summary {
  display: inline-flex; align-items: center; gap: 7px; color: var(--text-2);
}
.col-picker > summary svg { display: block; }
.col-picker.on > summary { color: var(--accent); border-color: var(--accent); }
.col-picker .drop-list { left: 0; right: auto; }
.col-picker .drop-list .sheet-toggle { margin-top: 6px; }

/* Группа ABC: доля товара в обороте. Цветом, а не только буквой — глаз
   ищет в таблице на сотню строк пятно, а не символ. */
.abc {
  display: inline-block; min-width: 20px; padding: 1px 6px; border-radius: 5px;
  font-size: 11.5px; font-weight: 600; text-align: center;
}
.abc-a { color: var(--green); background: color-mix(in srgb, var(--green) 16%, transparent); }
.abc-b { color: var(--amber); background: color-mix(in srgb, var(--amber) 16%, transparent); }
.abc-c { color: var(--muted); background: var(--surface-2); }

/* Контент-рейтинг в каталоге: тем же светофором, что и остальные оценки. */
.content-score { font-weight: 600; }
.content-score.ok { color: var(--green); }
.content-score.warn { color: var(--amber); }
.content-score.danger { color: var(--red); }

/* Ячейка-кнопка: остаток и коды открывают своё окно. Выглядит как значение,
   а не как кнопка, — подчёркиванием показываем, что по ней можно щёлкнуть. */
.cell-link {
  background: none; border: none; padding: 0; font: inherit; color: inherit;
  cursor: pointer; text-decoration: underline dotted 1px; text-underline-offset: 3px;
}
.cell-link:hover { color: var(--accent); }

/* Первая колонка не уезжает при прокрутке вбок: без неё в правой половине
   таблицы непонятно, чья это строка — цифры без товара ничего не значат. */
.sheet.products th:first-child,
.sheet.products td:first-child {
  position: sticky; left: 0; z-index: 3; background: var(--surface);
  box-shadow: 1px 0 0 var(--border-soft);
}
.sheet.products thead th:first-child { z-index: 7; background: var(--surface-2); }
.sheet.products tbody tr:hover td:first-child { background: var(--surface-2); }

/* Товар в таблице: картинка и название. Артикул теперь своей колонкой,
   поэтому под названием его больше нет. */
.sheet.products td:nth-child(2),
.sheet.products td:nth-child(3) { white-space: nowrap; }
.sheet.products td:nth-child(4),
.sheet.products td:nth-child(5),
.sheet.products td:nth-child(6),
.sheet.products td:nth-child(7) {
  max-width: 170px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ---------- окно истории остатков ---------- */
.stock-filters {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-bottom: 14px;
}
.stock-date {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 13px; color: var(--muted);
}
.stock-date input {
  background: var(--surface-2); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 5px 8px; font: inherit; font-size: 12.5px;
}
.stock-date input:focus { outline: none; border-color: var(--accent); }
/* Внутри окна метрик четыре, и растягивать их на пять колонок незачем. */
.stock-metrics { grid-template-columns: repeat(4, minmax(0, 1fr)); margin-bottom: 16px; }
.stock-metrics .metric { padding: 11px 13px; }
.stock-metrics .metric-value { font-size: 19px; }

/* Сколько ещё артикулов в отправлении, кроме показанного. */
.more {
  margin-left: 5px; padding: 1px 5px; border-radius: 999px;
  background: var(--surface-2); color: var(--muted); font-size: 11px;
}

/* Товар, причина, склад и адрес — длинные тексты: обрезаем, целиком они
   доступны наведением. Номера колонок сдвинулись, когда между суммой
   и причиной встала логистика. */
.sheet.returns td:nth-child(6),
.sheet.returns td:nth-child(10),
.sheet.returns td:nth-child(12),
.sheet.returns td:nth-child(13) {
  max-width: 210px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.sheet.returns td:nth-child(11) { white-space: nowrap; }

/* Логистика: сумма крупно, из чего сложилась — под ней. */
.sheet.returns td:nth-child(9) small {
  display: block; color: var(--muted); font-size: 11px; font-weight: 400;
}

/* Хранение тремя состояниями. Жёлтый — не украшение: это последний день,
   когда возврат можно забрать бесплатно, и завтра он начнёт стоить денег. */
td.storage { white-space: nowrap; }
td.storage small { display: block; font-size: 11px; color: var(--muted); }
td.storage-free { color: var(--text-2); }
td.storage-soon { color: var(--amber); }
td.storage-soon small { color: var(--amber); opacity: 0.85; }
td.storage-paid { color: var(--red); font-weight: 600; }
td.storage-paid small { color: var(--red); opacity: 0.8; font-weight: 400; }

.filters { align-items: flex-start; }

/* Фильтр колонки прячется под её названием.
   Раскрытая строка полей отнимала у таблицы две строки высоты и читалась
   как часть данных: поле стояло вплотную к первому заказу. Здесь название
   колонки само и есть кнопка, а отобранная колонка помечена — иначе
   спрятанный фильтр не найти и не понять, почему строк мало. */
/* Название колонки остаётся обычным заголовком — крупнее его делать нечего,
   он и так читается. Заметной должна быть галочка: она единственная говорит,
   что за названием прячется фильтр, и в прежнем размере её не находили. */
/* Список раскрывается от самой кнопки. Без этого он отсчитывался
   от ближайшего предка с position, а если такого нет — от окна: в шапке
   таблицы предком была липкая ячейка и всё сходилось, а на панели графика
   календарь уезжал в левый край экрана. */
.head-drop { position: relative; }
.head-drop > summary {
  list-style: none; cursor: pointer; user-select: none;
  display: inline-flex; align-items: center; gap: 7px;
  padding: 4px 7px; margin: -4px -7px; border-radius: 7px;
}
.head-drop > summary::-webkit-details-marker { display: none; }
.head-drop > summary::after {
  content: ''; width: 0; height: 0; margin-top: 4px;
  border: 5.5px solid transparent; border-top-color: var(--accent);
  opacity: 0.8;
}
.head-drop > summary:hover {
  color: var(--text); background: color-mix(in srgb, var(--accent) 15%, transparent);
}
.head-drop > summary:hover::after { opacity: 1; }
.head-drop[open] > summary { color: var(--text); background: var(--surface); }
.head-drop[open] > summary::after { transform: rotate(180deg); margin-top: -4px; opacity: 1; }

/* Колонка в отборе: видно по шапке, не открывая фильтр. */
th.filtered .head-drop > summary {
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 14%, transparent);
}

/* Граница колонки: потянуть — изменить ширину, двойной щелчок — вернуть.
   Полоса шире того, что видно: попасть мышью в один пиксель невозможно. */
.col-grip {
  position: absolute; top: 0; right: -4px; width: 9px; height: 100%;
  cursor: col-resize; z-index: 7;
}
/* У последней колонки ручка убирается внутрь. Выступая наружу, она делала
   таблицу на четыре пикселя шире её же места — и любая широкая таблица
   получала горизонтальную прокрутку из ниоткуда: полоса есть, а тянуть
   нечего. Схватить ручку это не мешает, в ней всё те же девять пикселей. */
.sheet th:last-child .col-grip { right: 0; }
/* Линию видно всегда, пусть и бледной: невидимую ручку не ищут — о том,
   что колонку можно потянуть, просто не догадываются. */
.col-grip::after {
  content: ''; position: absolute; top: 22%; bottom: 22%; left: 4px; width: 2px;
  background: var(--border); border-radius: 2px; opacity: 0.9;
}
.col-grip:hover::after, .col-grip.dragging::after {
  background: var(--accent); opacity: 1; top: 10%; bottom: 10%;
}
/* Пока тянут, выделение текста только мешает. */
.sheet.dragging, .sheet.dragging * { user-select: none; }

/* Заданная ширина держится, только когда таблица раскладывается по ней,
   а не по содержимому: иначе браузер тут же растянет колонку обратно
   под самое длинное значение. Включаем этот режим лишь после того,
   как ширину действительно задали. */
.sheet.resizable { table-layout: fixed; }
/* Обрезаем только данные. Шапке нельзя: список фильтра лежит внутри ячейки,
   и обрезка по её краю оставила бы от него полоску в одну строку. Название
   вместо этого переносится — колонка от этого не разъезжается. */
.sheet.resizable td { overflow: hidden; text-overflow: ellipsis; }
.sheet.resizable th { white-space: normal; }

/* Раскрытая колонка должна лежать поверх соседних: они тоже липкие
   и с тем же z-index, а в потоке идут позже — иначе список уходит под них. */
thead th:has(.head-drop[open]) { z-index: 6; }

.drop-list {
  position: absolute; z-index: 20; margin-top: 6px; left: 8px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 9px 11px;
  box-shadow: 0 10px 26px var(--shadow-soft);
  display: flex; flex-direction: column; gap: 2px;
  max-height: 340px; overflow: auto;
}
/* У правых колонок список открывается влево: у правого края окна он иначе
   уезжает за экран, и половина значений оказывается недоступной. */
th:nth-last-child(-n+4) .drop-list { left: auto; right: 8px; }

/* То же и вне таблиц. Календарь периода стоит последним в строке переключателей,
   то есть у самого правого края страницы, и `left` уводил его за экран: от
   календаря оставались три колонки дней и обрезанная подпись с выбранным
   отрезком. Прижимаем к правому краю кнопки — дальше он растёт влево, в поле,
   которое всегда свободно. */
.chart-period .drop-list { left: auto; right: 0; }

.drop-list label {
  display: flex; align-items: center; gap: 7px;
  font-size: 13px; color: var(--text-2); white-space: nowrap; cursor: pointer;
  text-transform: none; letter-spacing: normal; font-weight: 400;
  padding: 3px 5px; border-radius: 5px;
}
.drop-list label:hover { color: var(--text); background: var(--surface-2); }
.drop-list label.picked { color: var(--text); font-weight: 500; }

/* Поиск по номеру: то самое поле ввода, которое открывается по щелчку. */
.drop-search {
  min-width: 230px;
  background: var(--surface-2); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 6px 9px; font: inherit; font-size: 13px;
  text-transform: none; letter-spacing: normal; font-weight: 400;
}
.drop-search:focus { outline: none; border-color: var(--accent); }

/* ---------- календарь периода ---------- */

/* Две даты на одном календаре, а не два поля «дд.мм.гггг»: период — это
   отрезок, и выбирать его удобнее там же, где видно, что между границами. */
.calendar {
  /* Ширина с запасом на дорожку: при 238px на клетку приходилось 33px,
     и любой шрифт пошире упирался в этот предел. */
  width: 266px; max-width: calc(100vw - 32px);
  text-transform: none; letter-spacing: normal;
}
/* Список значений можно листать, календарь — нет: у него шесть недель,
   шапка с месяцем и подпись отрезка, и обрезать их полосой прокрутки
   значит спрятать либо выбранные даты, либо кнопку сброса. */
.drop-list:has(.calendar) { max-height: none; overflow: visible; }
.calendar-head {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 8px; font-size: 13px; font-weight: 500; color: var(--text);
}
.calendar-head button {
  background: none; border: none; color: var(--text-2); cursor: pointer;
  padding: 2px 7px; border-radius: 5px; font-size: 15px; line-height: 1;
}
.calendar-head button:hover { background: var(--surface-2); color: var(--text); }
/* `minmax(0, 1fr)`, а не `1fr`. Это не украшательство: `1fr` — это
   `minmax(auto, 1fr)`, то есть дорожка не ужимается ниже содержимого клетки.
   Стоит клетке оказаться шире дорожки — на другом шрифте, при масштабе
   браузера, на длинном числе, — и семь колонок перестают помещаться в ширину
   календаря. Сетка при этом остаётся прежней ширины, а последняя колонка
   просто рисуется за рамкой: замер показал вынос на 71 пиксель. Ноль в нижней
   границе разрешает дорожкам сжаться, и календарь остаётся календарём при
   любом содержимом. */
.calendar-grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 1px; }
.calendar-day, .calendar-week { min-width: 0; }
.calendar-week {
  font-size: 11px; color: var(--muted); text-align: center; padding-bottom: 4px;
}
.calendar-day {
  border: none; background: none; color: var(--text-2); cursor: pointer;
  font: inherit; font-size: 12.5px; padding: 5px 0; border-radius: 6px;
}
.calendar-day:hover { background: var(--surface-2); color: var(--text); }
.calendar-day.other { color: var(--muted); opacity: 0.45; }
.calendar-day.today { box-shadow: inset 0 0 0 1px var(--border); }
/* Между границами — заливка послабее: отрезок виден целиком, а концы
   всё равно читаются как выбранные. */
.calendar-day.between {
  background: color-mix(in srgb, var(--accent) 16%, transparent);
  color: var(--text); border-radius: 0;
}
.calendar-day.edge {
  background: var(--accent); color: #fff; font-weight: 600;
}
.calendar-day.edge.from { border-radius: 6px 0 0 6px; }
.calendar-day.edge.to { border-radius: 0 6px 6px 0; }
.calendar-day.edge.from.to { border-radius: 6px; }
.calendar-foot {
  display: flex; align-items: center; justify-content: space-between;
  margin-top: 9px; font-size: 12px; color: var(--muted);
}
.calendar-foot button {
  background: none; border: none; color: var(--accent); cursor: pointer;
  font: inherit; font-size: 12px; padding: 0;
}

/* Факты о заказе — плитками, а не строками: пар «поле — значение» дюжина,
   и в столбик они занимают весь экран до состава заказа. */
.order-facts {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 10px 18px;
  margin: 0 0 20px;
}
.order-facts dt { color: var(--muted); font-size: 11.5px; }
.order-facts dd { margin: 2px 0 0; font-size: 13.5px; }

.order-codes { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 4px; }
.order-codes span {
  font-family: var(--mono); font-size: 11px;
  background: var(--surface-3); color: var(--text-2);
  padding: 1px 6px; border-radius: 5px;
  /* Код маркировки длиной под сотню знаков растянул бы таблицу, поэтому
     показываем начало, а целиком он доступен выделением. */
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* Лента статусов. Точка на вертикальной линии — состояние, под ней время. */
.timeline { list-style: none; margin: 0; padding: 0 0 0 18px; position: relative; }
.timeline::before {
  content: ""; position: absolute; left: 4px; top: 6px; bottom: 6px;
  border-left: 1px solid var(--border);
}
.timeline li { position: relative; padding: 0 0 14px; }
.timeline li::before {
  content: ""; position: absolute; left: -18px; top: 5px;
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--surface); border: 2px solid var(--border);
}
.timeline li.current::before { background: var(--green); border-color: var(--green); }
.timeline-name { font-size: 13.5px; }
.timeline-time { color: var(--muted); font-size: 12px; margin-top: 1px; }

.pager {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; margin-top: 12px; font-size: 13px; color: var(--muted);
}
.pager-links { display: inline-flex; gap: 8px; }
.pager-links a {
  padding: 4px 12px; border-radius: 7px;
  border: 1px solid var(--border); color: var(--text-2); text-decoration: none;
}
.pager-links a:hover { color: var(--text); border-color: var(--accent); }

/* Низ длинной таблицы: размер страницы слева, номера по центру, счёт справа.
   Раньше у таблицы товаров низа не было вовсе — сколько строк нашлось,
   столько и показывалось, — и «где я в списке» отвечала только прокрутка. */
.pages {
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
  margin-top: 14px; padding-top: 12px;
  border-top: 1px solid var(--border-soft);
  font-size: 13px; color: var(--muted);
}
.pages-size { display: inline-flex; align-items: center; gap: 6px; }
.pages-size a {
  padding: 3px 9px; border-radius: 7px; text-decoration: none;
  border: 1px solid transparent; color: var(--text-2);
}
.pages-size a:hover { border-color: var(--border); color: var(--text); }
.pages-size a.on {
  border-color: var(--accent); color: var(--text);
  background: color-mix(in srgb, var(--accent) 14%, transparent);
}

.pages-list { display: inline-flex; align-items: center; gap: 4px; margin: 0 auto; }
.pages-list a, .pages-here, .pages-gap {
  min-width: 30px; padding: 4px 8px; border-radius: 7px;
  text-align: center; text-decoration: none;
}
.pages-list a { color: var(--text-2); border: 1px solid var(--border); }
.pages-list a:hover { color: var(--text); border-color: var(--accent); }
/* Текущая страница — не ссылка: нажимать на неё незачем, и подсвечена она
   затем, чтобы в ряду номеров было видно, где ты. */
.pages-here {
  color: #fff; background: var(--accent); border: 1px solid var(--accent);
}
.pages-gap { color: var(--muted); min-width: 16px; padding: 4px 2px; }
.pages-step { font-size: 15px; line-height: 1.1; }
.pages-count { margin-left: auto; white-space: nowrap; }

@media (max-width: 760px) {
  /* На телефоне номера уезжают в свою строку: вместе со счётом и размером
     они не помещаются и рвутся посередине. */
  .pages { gap: 10px; }
  .pages-list { margin: 0; order: 3; width: 100%; justify-content: center; }
}

/* График продаж: три шкалы вместо одного столбца.

   Отгрузка синим, оплаченная её часть закрашивает тот же столбец снизу
   зелёным, возвраты стоят своей узкой шкалой рядом красным. Цвета не
   декоративные: это три разных состояния денег, и между отгрузкой
   и деньгами проходят недели. */
.flow-shipped { fill: var(--accent); fill-opacity: 0.55; }
.flow-paid { fill: var(--green); fill-opacity: 0.95; }
.flow-returned { fill: var(--red); fill-opacity: 0.85; }

/* Что означает цвет — рядом с графиком, а не в справке внизу страницы. */
.chart-legend {
  display: flex; gap: 16px; flex-wrap: wrap;
  margin-top: 8px; font-size: 12px; color: var(--text-2);
}
.chart-legend span { display: inline-flex; align-items: center; gap: 6px; }
.chart-legend i { width: 10px; height: 10px; border-radius: 3px; display: inline-block; }
.chart-legend .dot-shipped { background: color-mix(in srgb, var(--accent) 55%, transparent); }
.chart-legend .dot-paid { background: var(--green); }
.chart-legend .dot-returned { background: var(--red); }
.chart-note { margin-top: 6px; font-size: 11.5px; line-height: 1.45; }

/* Страница «Данные»: месяц в первой колонке не переносится — «2026-0 8»
   на двух строках читается как две разные вещи. */
.sheet td.code { white-space: nowrap; }

/* ---------- панель агента ---------- */
/* Кнопка агента. Висит поверх страницы, а таблицы теперь во всю ширину —
   в углу она накрывала последнюю колонку последних строк. Поэтому её можно
   перетащить куда удобно: место запоминается, двойной щелчок возвращает
   в угол. Отступы задаёт скрипт, здесь только значения по умолчанию. */
/* Кнопка должна быть видна сразу: сжатая до бледного кружка, она терялась
   на странице, и «где агент» приходилось вспоминать. Поэтому она крупная
   и в акцентном цвете — единственный такой элемент поверх страницы. */
.assistant-fab {
  position: fixed; right: 22px; bottom: 22px; z-index: 45;
  /* Зазор появляется вместе с подписью: в покое он растягивал круг
     в яйцо на девять пикселей. */
  display: flex; align-items: center; gap: 0;
  background: var(--accent); color: #fff;
  border: none; border-radius: 999px;
  padding: 12px; font: inherit; font-size: 14px; font-weight: 500; cursor: grab;
  box-shadow: 0 10px 28px color-mix(in srgb, var(--accent) 45%, transparent);
  transition: padding 140ms ease, box-shadow 140ms ease, transform 140ms ease;
}
.assistant-fab img {
  width: 28px; height: 28px; border-radius: 8px; flex: none;
  /* Логотип цветной, а фон теперь акцентный: подсветка отделяет знак
     от подложки, не превращая его в пятно. */
  background: rgba(255, 255, 255, 0.16); padding: 3px;
}
/* Содержимое поверх обеих анимаций. */
.assistant-fab img, .assistant-fab span { position: relative; z-index: 2; }

/* Энергия по кругу.
   Кольцо — не рамка, а вырезанный маской ободок: рамка не умеет быть
   градиентом, а conic-gradient в маске повторяет любую форму кнопки —
   и круг в покое, и вытянутую пилюлю с подписью при наведении. */
.assistant-fab::before {
  content: ''; position: absolute; inset: -3px; border-radius: inherit;
  padding: 3px; z-index: 0; pointer-events: none;
  background: conic-gradient(
    from 0deg,
    transparent 0 42%,
    color-mix(in srgb, #fff 55%, var(--accent)) 60%,
    #fff 70%,
    color-mix(in srgb, #fff 55%, var(--accent)) 80%,
    transparent 96%
  );
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
  animation: fab-spin 2.6s linear infinite;
}

/* Вторая волна — по самой заливке, медленнее и в другую сторону: одно
   вращение читается как деталь оформления, два — как живое движение. */
.assistant-fab::after {
  content: ''; position: absolute; inset: 0; border-radius: inherit;
  z-index: 1; pointer-events: none;
  background: conic-gradient(
    from 0deg,
    transparent 0 55%,
    rgba(255, 255, 255, 0.32) 72%,
    transparent 88%
  );
  animation: fab-spin 4.4s linear infinite reverse;
}

@keyframes fab-spin { to { transform: rotate(1turn); } }

/* Дыхание тени: медленное, чтобы кнопку было видно боковым зрением,
   но она не мигала на краю глаза при работе с таблицей. */
@keyframes fab-breathe {
  0%, 100% { box-shadow: 0 10px 28px color-mix(in srgb, var(--accent) 42%, transparent); }
  50% { box-shadow: 0 10px 34px color-mix(in srgb, var(--accent) 68%, transparent); }
}
.assistant-fab { animation: fab-breathe 3.6s ease-in-out infinite; }
.assistant-fab:hover, .assistant-fab.dragging { animation: none; }

/* Кому анимация мешает — системная настройка это уже сказала. */
@media (prefers-reduced-motion: reduce) {
  .assistant-fab, .assistant-fab::before, .assistant-fab::after { animation: none; }
}

/* Подпись разворачивается при наведении: в покое кнопка занимает угол
   кружком, а не полосой в треть таблицы. */
.assistant-fab span {
  max-width: 0; overflow: hidden; white-space: nowrap;
  transition: max-width 160ms ease;
}
.assistant-fab:hover, .assistant-fab.dragging, .assistant-fab:focus-visible {
  padding-right: 18px; gap: 9px; transform: translateY(-1px);
  box-shadow: 0 14px 34px color-mix(in srgb, var(--accent) 55%, transparent);
}
.assistant-fab:hover span,
.assistant-fab.dragging span,
.assistant-fab:focus-visible span { max-width: 120px; }
.assistant-fab.dragging { cursor: grabbing; }

/* Панель не накрывает содержимое, а двигает его: у страницы появляется
   отступ справа на ширину панели, и середина остаётся адаптивной —
   меню слева, агент справа, между ними живёт страница. Ширину панель
   сообщает через переменную, отступ следует за перетаскиванием. */
:root { --assistant-w: 420px; }
body.assistant-open .content { margin-right: var(--assistant-w); }
.content { transition: margin-right 180ms ease; }
/* Пока панель тянут за край, содержимое должно липнуть к руке. */
body.assistant-dragging .content { transition: none; }
body.assistant-dragging { user-select: none; }

.assistant {
  position: fixed; top: 0; right: 0; bottom: 0; z-index: 50;
  width: var(--assistant-w); max-width: 100vw;
  background: var(--surface);
  border-left: 1px solid var(--border);
  display: flex; flex-direction: column;
}
/* Явное display: flex сильнее атрибута hidden — без этого правила кнопка
   «Свернуть» ставила атрибут, а панель оставалась на экране. */
.assistant[hidden] { display: none; }
/* Ручка ширины — волосок, который проявляется только под курсором. */
.assistant-resize {
  position: absolute; left: -2px; top: 0; bottom: 0; width: 5px;
  cursor: ew-resize; z-index: 51;
}
.assistant-resize::after {
  content: ''; position: absolute; left: 2px; top: 0; bottom: 0; width: 1px;
  background: transparent; transition: background 120ms;
}
.assistant-resize:hover::after { background: var(--accent); }

.assistant-head {
  display: flex; gap: 7px; align-items: center;
  padding: 11px 12px; border-bottom: 1px solid var(--border-soft);
}
.assistant-head select {
  flex: 1; min-width: 0;
  background: var(--surface-2); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 7px 9px; font: inherit; font-size: 13px;
}
.assistant-head button {
  flex: none; background: none; border: 1px solid var(--border); border-radius: 7px;
  color: var(--muted); width: 30px; height: 30px; cursor: pointer;
  display: grid; place-items: center; padding: 0;
}
.assistant-head button:hover { color: var(--text); border-color: var(--accent); }

.assistant-log { flex: 1; overflow-y: auto; padding: 14px; }

.a-msg { margin-bottom: 12px; max-width: 94%; }
/* pre-wrap больше не нужен: абзацы и списки собирает разбор разметки,
   а с ним переносы строк давали бы двойные пустоты. */
.a-msg .a-body {
  padding: 9px 12px; border-radius: 12px; font-size: 13.5px; line-height: 1.55;
  word-break: break-word;
}
.a-body p { margin: 0 0 7px; }
.a-body p:last-child, .a-body ul:last-child, .a-body ol:last-child { margin-bottom: 0; }
.a-body h4 { margin: 0 0 6px; font-size: 14px; font-weight: 600; }
.a-body ul, .a-body ol { margin: 0 0 7px; padding-left: 20px; }
.a-body li { margin-bottom: 3px; }
.a-body code {
  font-family: var(--mono); font-size: 12.5px;
  background: color-mix(in srgb, var(--muted) 18%, transparent);
  padding: 1px 5px; border-radius: 4px;
}
.a-body strong { font-weight: 600; }
.a-user { margin-left: auto; }
.a-user .a-body { background: color-mix(in srgb, var(--accent) 18%, var(--surface-2)); }
.a-assistant .a-body { background: var(--surface-2); }
.a-meta { color: var(--muted); font-size: 11.5px; margin-top: 3px; padding: 0 4px; }

.a-body a { color: var(--accent); text-decoration: underline; text-underline-offset: 2px; }

/* Разметка ответа — общая для панели и страницы «Чат»: разбирает её один
   md.js, и выглядеть она обязана одинаково, иначе это два разных чата.

   Таблица прокручивается внутри сообщения, а не растягивает его: в панели
   ширина 380 пикселей, и шесть колонок себестоимости в неё не встанут
   никогда. Раньше таблицы не было вовсе — модель рисовала её палками,
   и человек читал «| SKU | 1 240 |» глазами. */
.md-scroll { overflow-x: auto; margin: 2px 0 8px; }
.md-scroll:last-child { margin-bottom: 0; }
.md-table { width: auto; min-width: 100%; border-collapse: collapse; font-size: 12.5px; }
.md-table th, .md-table td {
  padding: 5px 9px; border-bottom: 1px solid var(--border-soft); white-space: nowrap;
}
.md-table th {
  text-align: left; font-weight: 600; color: var(--text-2);
  border-bottom-color: var(--border);
}
.md-table tr:last-child td { border-bottom: none; }
.md-code {
  margin: 0 0 8px; padding: 8px 10px; overflow-x: auto;
  background: var(--surface-3); border-radius: var(--radius-sm);
  font-family: var(--mono); font-size: 12px; line-height: 1.5;
}
.md-code:last-child { margin-bottom: 0; }
/* Мини-график в ответе. Рисует его md.js по блоку ```chart, а размеры
   и цвета живут здесь: график в узкой панели и на широкой странице должен
   быть одним и тем же графиком, только разной ширины. */
.md-chart { margin: 4px 0 10px; }
.md-chart:last-child { margin-bottom: 0; }
.md-chart figcaption {
  color: var(--text-2); font-size: 12px; margin-bottom: 4px;
}
.md-chart svg { display: block; overflow: visible; }
.md-chart-bar { fill: var(--accent); }
/* Убыток красным: столбик вниз того же цвета читается как «меньше»,
   а не как «минус». */
.md-chart-bar.neg { fill: var(--red); }
.md-chart-line { fill: none; stroke: var(--accent); stroke-width: 2; }
.md-chart-dot { fill: var(--accent); }
.md-chart-zero { stroke: var(--border); stroke-width: 1; }
.md-chart-name { fill: var(--muted); font-size: 11px; }
.md-chart-value { fill: var(--text-2); font-size: 11px; }
.md-chart-value.end { text-anchor: end; }
.md-chart-note { color: var(--muted); font-size: 11.5px; margin-top: 3px; }

.a-body hr, .bubble-text hr {
  border: none; border-top: 1px solid var(--border-soft); margin: 9px 0;
}

/* Цитата в поле ввода — как вложение: пока она здесь, вопрос уйдёт вместе
   с ней. Отличается цветом, чтобы не путать с приложенным файлом. */
.a-chip-quote {
  border-left: 3px solid var(--accent);
  max-width: 100%;
}
.a-chip-quote span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Кнопки под сообщением. Едва заметные, ярче под курсором:
   они нужны раз от раза, а не в каждом взгляде на переписку. */
.a-actions { margin-top: 3px; padding: 0 2px; }
.a-user .a-actions { text-align: right; }
.a-actions button {
  background: none; border: none; padding: 3px; cursor: pointer;
  color: var(--muted); opacity: 0.55; border-radius: 5px;
  display: inline-grid; place-items: center;
}
.a-actions button:hover { opacity: 1; color: var(--text); background: var(--surface-2); }
.a-actions button.a-copied { color: var(--green); opacity: 1; }

.a-notice {
  color: var(--amber); font-size: 12.5px; margin: 8px 0;
  padding: 8px 11px; border: 1px dashed color-mix(in srgb, var(--amber) 50%, transparent);
  border-radius: var(--radius-sm);
}

.a-run { margin: 8px 0 12px; }
.a-run-title { color: var(--text-2); font-size: 13px; animation: a-pulse 1.4s infinite; }
.a-run-steps { margin-top: 6px; }
.a-step { color: var(--muted); font-size: 12.5px; line-height: 1.7; }
@keyframes a-pulse { 50% { opacity: 0.45; } }

.a-trace { margin: -6px 0 12px; }
.a-trace summary { color: var(--muted); font-size: 12px; cursor: pointer; }

.assistant-form { border-top: 1px solid var(--border-soft); padding: 10px 12px 12px; }
.assistant-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.assistant-chips:not(:empty) { margin-bottom: 8px; }
.a-chip {
  display: inline-flex; align-items: center; gap: 6px;
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: 999px; padding: 3px 10px; font-size: 12px;
}
.a-chip button { background: none; border: none; color: var(--muted); cursor: pointer; font-size: 13px; }
.a-chip button:hover { color: var(--red); }

/* Скриншот в поле ввода — миниатюрой, крестик поверх угла. */
.a-chip-img { padding: 0; border-radius: 9px; position: relative; overflow: hidden; }
.a-chip-img img {
  width: 56px; height: 56px; object-fit: cover; display: block; cursor: zoom-in;
}
.a-chip-img button {
  position: absolute; top: 1px; right: 1px;
  background: rgba(0, 0, 0, 0.6); color: #fff;
  border-radius: 50%; width: 17px; height: 17px; line-height: 1; padding: 0;
}

/* Картинки в отправленном сообщении. */
.a-thumbs { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 6px; justify-content: flex-end; }
.a-msg.a-assistant .a-thumbs { justify-content: flex-start; }
.a-thumbs img {
  width: 96px; height: 96px; object-fit: cover;
  border-radius: 9px; border: 1px solid var(--border); cursor: zoom-in;
}

/* Просмотр во весь экран. Клик в любом месте закрывает; сама картинка
   при этом никуда не девается — это окно, а не действие над вложением. */
.a-lightbox {
  position: fixed; inset: 0; z-index: 70;
  background: rgba(0, 0, 0, 0.82);
  display: grid; place-items: center; cursor: zoom-out;
}
.a-lightbox[hidden] { display: none; }
.a-lightbox img {
  max-width: 94vw; max-height: 94vh;
  border-radius: 10px; box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6);
}

.assistant-form textarea {
  width: 100%; resize: none;
  background: var(--surface-2); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 9px 11px; font: inherit; font-size: 13.5px;
}
.assistant-form textarea:focus { outline: none; border-color: var(--accent); }

.assistant-bar { display: flex; align-items: center; gap: 7px; margin-top: 8px; }
.assistant-bar button[type="button"] {
  background: none; border: 1px solid var(--border); border-radius: 7px;
  width: 32px; height: 32px; cursor: pointer; color: var(--muted);
  display: grid; place-items: center; padding: 0;
}
.assistant-bar button[type="button"]:hover { border-color: var(--accent); color: var(--text); }
#assistant-mic.on { border-color: var(--red); color: var(--red); animation: a-pulse 1.2s infinite; }
.assistant-hint { color: var(--muted); font-size: 11.5px; flex: 1; text-align: right; }
.assistant-send {
  background: var(--accent); color: #fff; border: none; border-radius: var(--radius-sm);
  padding: 8px 16px; font: inherit; font-size: 13.5px; cursor: pointer;
}
.assistant-send:disabled { opacity: 0.55; cursor: default; }
.assistant.a-drop { outline: 2px dashed var(--accent); outline-offset: -6px; }

@media (max-width: 800px) {
  /* На телефоне двигать нечего — панель занимает весь экран поверх. */
  .assistant { width: 100vw !important; }
  .assistant-resize, .nav-collapse { display: none; }
  body.assistant-open .content { margin-right: 0; }
}

/* ---------- карта складов ---------- */

/* Материк приподнят: тень под ним и заливка с уклоном сверху вниз. Это
   не украшение — так граница страны читается как край, а не как линия
   на плоскости, и точки складов явно лежат сверху, а не внутри заливки.
   Соседи по СНГ ниже и тише: склад в Алматы должен читаться как склад
   в другой стране, а не где-то под Омском. */
/* Обычно карта не обрезается: тень под материком и подписи у края холста
   выходят за viewBox, и обрезка съела бы их. Но при увеличении обрезать
   обязательно — иначе увеличенный контур вылезает за панель и заливает
   собой страницу. Проверено: при масштабе девять карта закрывала всё окно. */
.map { width: 100%; height: auto; display: block; overflow: visible; }
.map-land { fill: var(--surface-3); stroke: var(--border); stroke-width: 0.8; }
.map-land-near { fill: var(--surface-2); stroke: var(--border-soft); stroke-width: 0.6; }
.map-relief-top { stop-color: color-mix(in srgb, var(--surface-3) 82%, white); }
.map-relief-bottom { stop-color: color-mix(in srgb, var(--surface-3) 88%, black); }

/* Точка склада. Заливка полупрозрачная: склады в Москве и Подмосковье
   стоят вплотную, и без просвета крупная точка съедала бы соседние. */
.map-dot { stroke-width: 1.2; fill-opacity: 0.6; cursor: default; }
.map-dot-ok { fill: var(--accent); stroke: var(--accent); }
.map-dot-short { fill: var(--amber); stroke: var(--amber); }
.map-dot-empty { fill: none; stroke: var(--muted); stroke-dasharray: 2 2; }
/* Чужой склад — тот, где нашего товара нет: виден, но не спорит со своими. */
.map-dot-idle { fill: var(--muted); stroke: var(--muted); fill-opacity: 0.22; }
.map-point:hover .map-dot { fill-opacity: 0.9; }

/* Название склада — только под указателем. На всю страну их под сотню,
   и вместе они читаются как чернильное пятно поверх карты. Наводят на склад
   как раз затем, чтобы понять, какой из соседних кружков — он. */
.map-name {
  fill: var(--text); font-size: 11px; font-weight: 600; opacity: 0;
  paint-order: stroke; stroke: var(--bg); stroke-width: 3.5px; stroke-linejoin: round;
  pointer-events: none; transition: opacity 160ms ease;
}

/* Подсветка склада, на который навели, — на карте и в таблице разом.
   Точек под сотню, подписаны из них не все, и найти склад из таблицы
   на карте иначе нельзя. Кольцо, а не смена цвета: цвет здесь занят
   запасом, и перекрасив точку, мы соврали бы про остаток. */
.map-point.is-hot .map-dot {
  fill-opacity: 0.95;
  stroke: var(--text);
  stroke-width: 2.4;
  paint-order: stroke;
}
/* У подсвеченного склада имя видно и без приближения: наводят на него как раз
   затем, чтобы понять, какой из соседних кружков — он. */
.map-point.is-hot .map-name { opacity: 1; }

.map-holder { position: relative; }

/* Карточка кластера: список его складов поверх карты.
   Появилась не для красоты. Подписать склады прямо на карте нельзя: подпись
   занимает полтораста пикселей, а склады области стоят в получасе езды —
   на любом масштабе, при котором область видна целиком, из шести подписей
   помещаются две. Место склада показывает точка, а «сколько там» читают
   списком; наведение на строку зажигает её точку, так что оба вопроса
   закрываются одним движением. */
.map-card {
  position: absolute; top: 12px; left: 12px; z-index: 3;
  max-width: min(420px, 52%); padding: 10px 12px;
  border-radius: var(--radius); border: 1px solid var(--border);
  background: color-mix(in srgb, var(--surface-2) 94%, transparent);
  backdrop-filter: blur(6px); box-shadow: 0 8px 24px rgb(0 0 0 / 0.18);
}
.map-card-head {
  font-size: 11px; text-transform: uppercase; letter-spacing: 0.03em;
  color: var(--muted); margin-bottom: 7px;
}
.map-card ul { list-style: none; margin: 0; padding: 0; }
.map-card li {
  font-size: 12px; line-height: 1.55; padding: 2px 6px; border-radius: 6px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.map-card li.is-hot { background: color-mix(in srgb, var(--accent) 22%, transparent); }
.map-card li b { font-weight: 600; }

/* Своя подсказка вместо родной. Родная в Chrome — плашка размером
   с четверть карты, и она переживает щелчок: карта уехала, а подсказка
   висит поверх соседних складов. Эта гаснет от щелчка, прокрутки
   и ухода указателя. */
.map-tip {
  position: fixed; z-index: 60; pointer-events: none;
  max-width: 280px; padding: 7px 10px; border-radius: 8px;
  background: var(--surface-2); color: var(--text);
  border: 1px solid var(--border); box-shadow: 0 6px 20px rgb(0 0 0 / 0.22);
  font-size: 12px; line-height: 1.45; white-space: pre-line;
}
.place.is-hot {
  background: color-mix(in srgb, var(--accent) 30%, var(--surface-3));
  color: var(--text);
  outline: 1px solid var(--accent);
}
/* Метку можно обойти табом — значит у неё должен быть указатель и видимый
   отклик, иначе клавиатурный обход выглядит сломанным. */
.place[data-warehouse] { cursor: pointer; }

/* Подпись города. Обводка цветом фона — чтобы название читалось и там,
   где оно легло на границу или на соседнюю точку. */
.map-label {
  fill: var(--text-2); font-size: 9.5px; font-weight: 500;
  paint-order: stroke; stroke: var(--bg); stroke-width: 2.5px; stroke-linejoin: round;
}

.map-legend { display: flex; gap: 14px; flex-wrap: wrap; color: var(--muted); font-size: 12px; }
.map-legend span { display: inline-flex; align-items: center; gap: 6px; }
.map-key { width: 9px; height: 9px; border-radius: 50%; display: inline-block; }
.map-key-ok { background: var(--accent); }
.map-key-short { background: var(--amber); }
.map-key-empty { border: 1px dashed var(--muted); }
.map-key-idle { background: var(--muted); opacity: 0.4; }

/* ---------- остатки по кластерам ---------- */

/* Пустой кластер и кластер на исходе помечаем полосой слева, а не цветом
   строки целиком: строк два десятка, и залитые красным они сливаются
   в сплошное полотно, из которого уже ничего не выделяется. */
tr.row-danger td:first-child { box-shadow: inset 3px 0 0 var(--red); }
tr.row-warn td:first-child { box-shadow: inset 3px 0 0 var(--amber); }

/* Склады кластера — россыпью в одной ячейке. Списком строк они растянули бы
   таблицу на три экрана ради того, что читают одним взглядом. */
.cluster-places { display: flex; flex-wrap: wrap; gap: 5px; }
.place {
  font-size: 11.5px; padding: 2px 7px; border-radius: 999px;
  background: var(--surface-3); color: var(--text-2); white-space: nowrap;
}

/* ---------- поставки ---------- */

.supply-row { cursor: pointer; }
.supply-items > td { padding: 0 0 0 34px; background: var(--surface-2); }
table.inner { margin: 6px 0 10px; width: auto; min-width: 420px; }
table.inner th { background: none; font-size: 10.5px; }
table.inner td { border-bottom: 1px solid var(--border-soft); }

/* ---------- покрытие кластеров ---------- */

.coverage-row { cursor: pointer; }
.coverage-detail > td { background: var(--surface-2); padding: 12px 16px 16px 34px; }
/* Две колонки: куда везти здесь и куда, если здесь нельзя. Рядом, потому что
   выбирают из них вместе, а не по очереди. */
.coverage-body { display: grid; grid-template-columns: 1.6fr 1fr; gap: 24px; }
@media (max-width: 1000px) { .coverage-body { grid-template-columns: 1fr; } }
.coverage-title {
  font-size: 11px; text-transform: uppercase; letter-spacing: 0.03em;
  color: var(--muted); margin-bottom: 8px;
}
.zone { margin-bottom: 10px; }
.zone-name { font-size: 12.5px; font-weight: 500; margin-bottom: 4px; }
.zone-points { display: flex; flex-wrap: wrap; gap: 5px; }
/* Склад, где наш товар уже лежит, помечен: везти туда и на пустой склад —
   разные решения, и различать их надо до клика, а не после. */
.place-here { background: color-mix(in srgb, var(--accent) 18%, var(--surface-3)); color: var(--text); }

/* ---------- личный кабинет ---------- */

/* Тарифы карточками: выбор из двух сроков делают глазами, а не читая
   таблицу. Годовая колонка выделена — она и есть предлагаемая. */
.plans { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 18px; }
.plan { border: 1px solid var(--border); border-radius: var(--radius); padding: 18px 20px 20px; }
.plan-current { border-color: var(--accent); }
.plan-head { display: flex; align-items: center; gap: 10px; margin-bottom: 6px; }
.plan-name { font-size: 16px; font-weight: 600; margin: 0; }
.plan-note { color: var(--muted); font-size: 12.5px; margin: 0 0 16px; }

/* Чем тариф отличается от соседнего — списком, а не абзацем: сравнивают
   их построчно, взглядом влево-вправо, и слитный текст этому мешает. */
.plan-limits {
  list-style: none; margin: 0 0 16px; padding: 0;
  color: var(--text-2); font-size: 12.5px;
}
.plan-limits li { padding: 4px 0 4px 18px; position: relative; }
.plan-limits li::before {
  content: ''; position: absolute; left: 3px; top: 11px;
  width: 5px; height: 5px; border-radius: 50%; background: var(--accent);
}

.plan-prices { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.plan-option {
  border: 1px solid var(--border-soft); border-radius: var(--radius-sm);
  padding: 14px 14px 12px; display: flex; flex-direction: column; gap: 3px;
}
/* Выгодный вариант подсвечен фоном, а не рамкой: рамка спорит с рамкой
   самой карточки тарифа и делает вложенность на три уровня. */
.plan-best { background: color-mix(in srgb, var(--accent) 8%, transparent); border-color: transparent; }
.plan-price { font-size: 20px; font-weight: 600; font-variant-numeric: tabular-nums; }
.plan-period { color: var(--muted); font-size: 11.5px; line-height: 1.35; }
.plan-save { color: var(--green); font-size: 11.5px; font-weight: 500; }
.plan-option button {
  margin-top: 8px; padding: 7px 12px; font: inherit; font-size: 13px;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface-2); color: var(--text); cursor: pointer;
}
.plan-best button { background: var(--accent); border-color: var(--accent); color: #fff; }
.plan-option button:hover { border-color: var(--accent); }
.plan-vat { color: var(--muted); font-size: 11.5px; margin-top: 12px; }

/* Расход за текущий месяц — крупным числом с полосой заполнения:
   «187 из 300» читается быстрее строки в таблице. */
.usage-now { display: flex; align-items: baseline; gap: 10px; margin-bottom: 8px; }
.usage-value { font-size: 30px; font-weight: 600; font-variant-numeric: tabular-nums; }
.usage-of { color: var(--muted); font-size: 13px; }
.usage-bar { height: 6px; border-radius: 3px; background: var(--surface-3); overflow: hidden; margin-bottom: 18px; }
.usage-fill { display: block; height: 100%; background: var(--accent); border-radius: 3px; }
.usage-over { background: var(--red); }

.settings label.wide { grid-column: 1 / -1; }

/* Разделы галочками в несколько колонок: их четырнадцать, и в один столбец
   список уезжает за экран — а выбирают их взглядом целиком, сравнивая
   с тем, чем человек занимается. */
.sections {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 8px 18px;
}
.sections .check { align-items: flex-start; gap: 8px; }
.sections .check small { display: block; color: var(--muted); font-size: 11px; }
.sections .check input { margin-top: 3px; }

/* Пакеты токенов. Карточки уже тарифных: это не третий тариф, а доплата
   поверх любого из двух, и по размеру она должна читаться меньше. */
.packs { display: flex; flex-wrap: wrap; gap: 14px; margin-bottom: 14px; }
.pack {
  border: 1px solid var(--border-soft); border-radius: var(--radius);
  padding: 14px 16px 14px; min-width: 210px;
  display: flex; flex-direction: column; gap: 3px;
}
.pack-name { font-size: 13px; color: var(--text-2); }
.pack-price { font-size: 22px; font-weight: 600; font-variant-numeric: tabular-nums; }
.pack-note { color: var(--muted); font-size: 11.5px; line-height: 1.35; }
.pack button {
  margin-top: 10px; padding: 7px 12px; font: inherit; font-size: 13px;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface-2); color: var(--text); cursor: pointer;
}
.pack button:hover { border-color: var(--accent); }

/* ---------- интеграции ---------- */

/* Карточка на сервис, а не строка в таблице: у каждого свои поля доступа,
   и в общей таблице половина клеток была бы пустой. Форма тоже своя —
   опечатка в адресе базы 1С не должна мешать сохранить ключ Диадока. */
.services { display: grid; grid-template-columns: repeat(auto-fit, minmax(330px, 1fr)); gap: 16px; }
.service {
  border: 1px solid var(--border-soft); border-radius: var(--radius);
  padding: 16px 18px 14px;
  display: flex; flex-direction: column; gap: 10px;
}
/* Подключённый сервис отмечен рамкой: в сетке из восьми карточек взгляд
   ищет именно те, что уже работают. */
.service-on { border-color: color-mix(in srgb, var(--green) 45%, var(--border)); }
.service-partial { border-color: color-mix(in srgb, var(--red) 35%, var(--border)); }
.service-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.service-name { font-size: 15px; font-weight: 600; }
.service-note { color: var(--muted); font-size: 12.5px; margin: 0; }
.service .grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
.service label { display: flex; flex-direction: column; gap: 5px; font-size: 13px; color: var(--text-2); }
.service label.wide { grid-column: 1 / -1; }
.service label.check { flex-direction: row; align-items: center; }
.service label small { font-weight: 400; }
.service input[type="text"], .service input[type="password"] {
  background: var(--surface-2); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 8px 10px; font: inherit; font-size: 13px; min-width: 0;
}
.service input:focus { outline: none; border-color: var(--accent); }
.service-foot {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 12px; flex-wrap: wrap; margin-top: auto;
}
/* Где взять доступы — самый частый вопрос при подключении, поэтому написано
   прямо в карточке, а не спрятано в справочнике внизу страницы. */
.service-where { color: var(--muted); font-size: 11.5px; flex: 1 1 180px; }
/* row-reverse — не украшение: в разметке первой идёт «Сохранить», чтобы
   именно она срабатывала по Enter, а на вид кнопки остаются в прежнем
   порядке. Первой submit-кнопкой была «Отключить», и Enter в поле
   стирал подключение вместе с секретом. */
.service-actions { display: flex; gap: 8px; flex: none; flex-direction: row-reverse; }
.service-actions button {
  padding: 7px 14px; font: inherit; font-size: 13px;
  border: 1px solid var(--accent); border-radius: var(--radius-sm);
  background: var(--accent); color: #fff; cursor: pointer;
}
.service-actions button.ghost {
  background: none; color: var(--text-2); border-color: var(--border);
}
.service-actions button.ghost:hover { color: var(--red); border-color: var(--red); }

/* Дорожная карта настройки кабинета.

   Полоска-приглашение видна на каждой странице, пока карта не пройдена:
   маржа, посчитанная по умолчанию, выглядит настоящей, и сказать об этом
   больше негде. Ссылкой целиком, а не кнопкой в углу — по ней и нажимают. */
.setup-strip {
  display: flex; flex-direction: column; gap: 2px;
  padding: 12px 16px; margin-bottom: 16px;
  border: 1px solid var(--border-soft); border-left: 3px solid var(--accent);
  border-radius: var(--radius); background: var(--surface);
  text-decoration: none; color: inherit;
}
.setup-strip:hover { border-color: var(--accent); }
.setup-strip-title { font-size: 13.5px; font-weight: 600; }
.setup-strip .muted { font-size: 12px; }

.setup-count {
  font-size: 12.5px; color: var(--muted);
  border: 1px solid var(--border-soft); border-radius: 999px; padding: 4px 12px;
}

/* Карта слева, раскрытый шаг справа. На узком экране карта уезжает наверх:
   список из шести строк читается и там, а форма в две колонки — нет. */
.setup-layout { display: grid; grid-template-columns: 260px 1fr; gap: 18px; align-items: start; }
@media (max-width: 900px) { .setup-layout { grid-template-columns: 1fr; } }

.setup-map { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
.setup-point a {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 12px; border-radius: 10px;
  text-decoration: none; color: var(--text); font-size: 13px;
}
.setup-point a:hover { background: var(--surface); }
.setup-mark {
  flex: 0 0 22px; height: 22px; border-radius: 50%;
  display: grid; place-items: center;
  font-size: 11.5px; border: 1px solid var(--border-soft); color: var(--muted);
}
.setup-done .setup-mark { background: var(--accent); border-color: var(--accent); color: #fff; }
/* Пропущенный шаг — не ошибка и не достижение: он просто закрыт. Поэтому
   тем же серым, что и непройденный, но с прочерком вместо номера. */
.setup-skipped .setup-name { color: var(--muted); }
.setup-open a { background: var(--surface); font-weight: 600; }

.setup-step { margin-bottom: 0; }
.setup-actions { display: flex; gap: 10px; align-items: center; margin-top: 16px; flex-wrap: wrap; }
.setup-actions a.primary { text-decoration: none; display: inline-flex; align-items: center; }

/* Период и графики на странице заказов.

   Свой период стоит рядом с быстрым, а не прячется в фильтре колонки:
   период выбирают до того, как смотрят таблицу. */
.period-pick { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.period-own {
  display: flex; align-items: center; gap: 6px;
  padding: 4px 8px; border: 1px solid var(--border-soft);
  border-radius: 10px; background: var(--surface);
}
.period-own.on { border-color: var(--accent); }
.period-own input[type="date"] {
  border: none; background: transparent; color: var(--text);
  font: inherit; font-size: 12.5px; padding: 3px 4px;
}
.period-dash { color: var(--muted); }
.period-own button {
  font-size: 12.5px; padding: 4px 10px; border-radius: 8px;
  border: 1px solid var(--border-soft); background: transparent; color: var(--text);
  cursor: pointer;
}
.period-own button:hover { border-color: var(--accent); }
.period-clear { color: var(--muted); text-decoration: none; padding: 0 4px; font-size: 15px; }

/* Три графика в ряд: динамика шире колец — по ней читают период,
   а кольца отвечают на вопрос «чья доля» и в ширине не нуждаются. */
.orders-charts {
  display: grid; grid-template-columns: 2fr 1fr 1fr; gap: 14px; margin-bottom: 18px;
}
.orders-charts .chart-card { margin-bottom: 0; }
@media (max-width: 1100px) { .orders-charts { grid-template-columns: 1fr; } }

/* ---------- заказы поставщикам ---------- */
/* Дорожная карта заказа: пять шагов в строку. Пройденные подсвечены,
   текущий обведён — взгляд находит «где мы сейчас» без чтения дат. */
.roadmap { display: flex; gap: 10px; flex-wrap: wrap; margin: 4px 0 16px; }
.roadmap-step {
  flex: 1 1 120px; padding: 10px 12px;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface-2);
}
.roadmap-step.done { border-color: var(--green); }
.roadmap-step.now { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.roadmap-label { font-size: 13px; color: var(--text-2); }
.roadmap-date { font-size: 14px; font-weight: 600; margin-top: 3px; }

/* Этап в списке заказов — тем же цветом, что и в карточке. */
.stage { font-size: 12px; padding: 2px 8px; border-radius: 999px; border: 1px solid var(--border); }
.stage-sent, .stage-invoiced { border-color: var(--accent); }
.stage-waybill { border-color: var(--amber); }
.stage-received { border-color: var(--green); }
.stage-cancelled { color: var(--muted); }

/* Кнопка внутри строки инструментов таблицы не должна переносить строку. */
.inline-form { display: inline; }
.sheet-tools button, .sheet-head button {
  background: var(--accent); color: #fff; border: none;
  border-radius: var(--radius-sm); padding: 7px 14px;
  font: inherit; font-weight: 600; cursor: pointer;
}
.sheet-tools button:hover, .sheet-head button:hover { filter: brightness(1.08); }

/* ---------- приёмка со сканером ---------- */
/* Поле ввода кода — главный элемент страницы: в него смотрит проводной
   сканер, и оно должно быть заметно шире соседей. Код длинный, и обрезанный
   на середине он не читается глазами при разборе ошибки. */
.scan-row { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; margin: 6px 0 4px; }
.scan-input { flex: 1 1 380px; font-family: var(--mono); font-size: 13px; }

.scan-video { margin-top: 12px; display: flex; flex-direction: column; gap: 8px; align-items: flex-start; }
.scan-video video {
  width: 100%; max-width: 420px; border-radius: var(--radius);
  border: 1px solid var(--border); background: #000;
}

/* Лента сканирования. Читается сверху вниз — последний код первым:
   на складе смотрят на неё краем глаза и только чтобы убедиться,
   что сработало. */
.scan-log { margin-top: 12px; display: flex; flex-direction: column; gap: 4px; max-height: 220px; overflow-y: auto; }
.scan-line {
  font-size: 13px; padding: 6px 10px; border-radius: var(--radius-sm);
  border-left: 3px solid var(--border); background: var(--surface-2);
}
.scan-line.ok { border-left-color: var(--green); }
.scan-line.bad { border-left-color: var(--red); color: var(--text); }

.receipt-lines tr.flash td { background: color-mix(in srgb, var(--green) 16%, transparent); }
.receipt-lines tr { transition: background 200ms; }

/* Строка-ссылка: если по ней можно нажать, это должно быть видно до нажатия. */
tr.clickable { cursor: pointer; }
tr.clickable:hover td { background: var(--surface-2); }
tr.clickable a { color: inherit; text-decoration: none; }
tr.clickable a:hover { text-decoration: underline; }

/* Текст письма в очереди: подтверждают не «действие №7», а то, что уйдёт. */
.letter-body {
  margin: 8px 0 0; padding: 10px 12px; white-space: pre-wrap; word-break: break-word;
  background: var(--surface-2); border-radius: var(--radius-sm);
  font: inherit; font-size: 13px; max-height: 260px; overflow-y: auto;
}

/* ---------- переписка с поставщиком ---------- */
/* Письмо свёрнуто до темы: их десятки, а читают одно. Развёрнутое показывает
   текст как есть — разбор ошибается, и последнее слово за глазами. */
.letter {
  border: 1px solid var(--border-soft); border-radius: var(--radius-sm);
  padding: 8px 12px; margin-top: 8px; background: var(--surface);
}
.letter[open] { border-color: var(--border); }
.letter summary { cursor: pointer; display: flex; gap: 8px; align-items: baseline; flex-wrap: wrap; }
.letter summary::marker { color: var(--muted); }
.letter-kind {
  font-size: 11.5px; padding: 1px 8px; border-radius: 999px;
  border: 1px solid var(--border); color: var(--text-2); white-space: nowrap;
}
/* Наше письмо — другим цветом полосы: в списке из двадцати важно с одного
   взгляда видеть, кто говорил последним. */
.letter-out { border-left: 3px solid var(--accent); }
.letter-out .letter-kind { border-color: var(--accent); color: var(--accent); }

/* Кнопка камеры на приёмке крупнее прочих: её ищут на телефоне одной рукой,
   держа коробку другой. */
button.primary.big { font-size: 15px; padding: 10px 18px; }

/* Колонка с галочками узкая: она служебная и не должна спорить за место
   с артикулом, ради которого в таблицу и смотрят. */
th.pick, td.pick { width: 32px; text-align: center; padding-right: 0; }
.stock-actions { flex-wrap: wrap; gap: 8px; }
.stock-actions .filter-input { flex: 0 1 220px; }

/* Картинка товара в списке: не украшение, а способ узнать товар глазом
   раньше, чем прочитаешь название из восьмидесяти символов. */
.thing { display: flex; align-items: center; gap: 10px; }
.thing-photo {
  width: 34px; height: 34px; flex: none; object-fit: contain;
  border-radius: 6px; background: var(--surface-2);
}

/* Ход загрузки данных. Полоса, а не «идёт…»: загрузка истории занимает часы,
   и человеку нужен ответ не «работает ли», а «сколько осталось». Считается
   она в кусках (месяц × источник), потому что доля времени врёт — месяц
   отправлений закрывается за секунды, месяц воронки за четверть часа. */
.job-state { margin-top: 14px; }
.job-state.is-hidden { display: none; }
.job-line { display: flex; gap: 10px; align-items: baseline; flex-wrap: wrap; }
.job-title { font-weight: 600; font-size: 13px; }
.job-count { margin-left: auto; font-variant-numeric: tabular-nums; }
.progress {
  height: 6px; margin-top: 8px; border-radius: 999px;
  background: var(--surface-2); overflow: hidden;
}
.progress > i { display: block; height: 100%; background: var(--accent); transition: width .4s ease; }
.job-note { margin-top: 6px; }

/* Ссылка, которая ведёт себя как кнопка, и кнопка, которая выглядит ссылкой.
   Пара нужна из-за форм: «Новая задача» — это переход, а «запустить сейчас» —
   действие, и оно обязано быть кнопкой внутри формы, иначе POST не уйдёт.
   Выглядеть при этом они должны так, как читаются. */
a.button-link {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 7px 14px; border-radius: 8px; font-size: 13px; text-decoration: none;
  border: 1px solid var(--border); color: var(--text-2); background: var(--surface-2);
}
a.button-link.primary { background: var(--accent); border-color: var(--accent); color: #fff; }
a.button-link:hover { filter: brightness(1.06); }
button.linkish {
  background: none; border: none; padding: 0; font-size: inherit;
  color: var(--accent); cursor: pointer;
}

/* Ответ агента в карточке запуска: моноширинным он читается хуже, а вот
   переносы строк важны — модель отвечает абзацами и списками. */
pre.answer-text {
  white-space: pre-wrap; word-break: break-word; margin: 0;
  font: inherit; color: var(--text-2);
}

/* Ближайшие запуски в пустом дне: человек завёл задачу на утро и открыл
   план днём — без этого списка экран читается как «ничего не записалось». */
.plan-upcoming { margin-top: 10px; display: grid; gap: 4px; font-size: 13px; }
.plan-upcoming b { font-size: 12px; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; }

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

   Липнет только полоса: заголовок страницы уезжает наверх, потому что место
   на экране нужнее самой переписке, а название раздела и так видно в меню. */
.chat-topbar {
  position: sticky;
  top: 0;
  z-index: 30;
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  /* Фон растянут на отступы содержимого: иначе текст просвечивает
     по краям полосы, когда проезжает под ней. */
  margin: 0 -26px 16px;
  padding: 10px 26px 12px;
  background: var(--bg);
  border-bottom: 1px solid var(--border-soft);
}
.chat-topbar .segmented { margin: 0; }
/* Кнопки прижаты к правому краю, как были в шапке страницы. */
.chat-topbar .chat-tools { margin-left: auto; }

@media (max-width: 900px) {
  /* На телефоне полоса не переносится в две строки, а едет вбок: перенос
     съедал бы седьмую часть экрана у переписки, ради которой сюда и пришли. */
  .chat-topbar {
    margin: 0 -13px 12px;
    padding: 8px 13px 10px;
    flex-wrap: nowrap;
    overflow-x: auto;
    scrollbar-width: none;
  }
  .chat-topbar::-webkit-scrollbar { display: none; }
  .chat-topbar .segmented,
  .chat-topbar .chat-tools { flex: none; }
  .chat-topbar .chat-conv { max-width: 140px; }
}

/* Фото товара в разборе. Вписывается целиком, а не обрезается: у игрушек
   важна форма, и кадрирование по центру превращает набор из трёх фигурок
   в одну. Подложка своя — у части карточек фон прозрачный, и на тёмной теме
   товар пропал бы вместе с ней. */
.detail-photo img {
  display: block;
  width: 100%;
  max-height: 260px;
  object-fit: contain;
  margin-top: 10px;
  border-radius: var(--radius-sm);
  background: #fff;
}

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

   Фон растянут на отступы содержимого: сообщения проезжают под полем,
   и просвечивающие края читались бы как поломка вёрстки. */
.chat-composer {
  position: sticky;
  bottom: 0;
  z-index: 20;
  margin: 0 -26px -60px;
  padding: 12px 26px 14px;
  background: var(--bg);
  border-top: 1px solid var(--border-soft);
}

/* Выбранный навык — полосой над полем, а не галочкой в меню: от него зависит,
   чем агент будет работать, и это видно, пока пишут вопрос. */
.chat-skill {
  display: flex; align-items: center; gap: 8px;
  margin-bottom: 8px; padding: 6px 10px;
  border: 1px solid color-mix(in srgb, var(--mint) 45%, transparent);
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--mint) 10%, transparent);
  font-size: 13px;
}
.chat-skill[hidden] { display: none; }
.chat-skill-name { font-weight: 500; }

@media (max-width: 900px) {
  .chat-composer { margin: 0 -13px -44px; padding: 10px 13px 12px; }
}

/* Поле «сколько перенести» узкое: оно стоит в каждой строке рядом с остатком,
   и широкое поле увело бы вправо колонки, ради которых в таблицу и смотрят. */
.qty-input { width: 72px; text-align: right; }

/* Заметка кладовщика тянется по колонке: в ней пишут фразу, а не число. */
.note-input { width: 100%; min-width: 140px; }
/* Ответ на сохранение: без него глазу непонятно, записалось ли, и заметку
   пишут второй раз поверх первой. Зелёная рамка гаснет сама. */
.cell-saved { border-color: var(--mint); }
.cell-bad { border-color: var(--red); }

/* Цена конкурента в таблице товаров: кликается и ведёт к его предложению.
   Под ценой — имя продавца: «дешевле на триста рублей» и «дешевле на триста
   у магазина с двумя отзывами» — разные новости, и решение по ним разное. */
.rival-price { color: var(--accent); text-decoration: none; font-variant-numeric: tabular-nums; }
.rival-price:hover { text-decoration: underline; }
.rival-seller {
  color: var(--muted); font-size: 11.5px; margin-top: 2px;
  max-width: 130px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}


/* Выбор модели в композере: тем же ростом и тоном, что кнопки рядом.
   Это часть строки инструментов, а не настройка страницы. */
.composer-model { display: inline-flex; align-items: center; }
.model-pick {
  max-width: 260px;
  font: inherit; font-size: 12.5px; padding: 6px 8px;
  background: var(--surface); color: var(--text-2);
  border: 1px solid var(--border-soft); border-radius: var(--radius-sm); cursor: pointer;
}
.model-pick:hover { color: var(--text); border-color: var(--border); }

/* --- Гипотезы ------------------------------------------------------------ */

/* Доска из двух колонок. На узком экране колонки становятся одна под другой:
   перетаскивать там всё равно нечем, а читать столбцы в половину ширины
   телефона невозможно. */
.hyp-board {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 12px;
  margin-top: 12px;
}
@media (max-width: 900px) {
  .hyp-board { grid-template-columns: minmax(0, 1fr); }
}

.hyp-col {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 14px;
  min-height: 160px;
  transition: border-color .12s, background .12s;
}
.hyp-col-head { display: flex; align-items: center; gap: 8px; }
.hyp-count {
  background: var(--surface-3); color: var(--text-2);
  border-radius: 999px; padding: 1px 9px; font-size: 12px;
}
.hyp-col-hint { color: var(--muted); font-size: 12.5px; margin: 2px 0 10px; }
.hyp-empty { padding: 18px 8px; }

/* Колонка под карточкой в руке. Мятный — тот же цвет, которым панель
   обозначает «принято»: активный пункт меню, применённое действие. */
.hyp-col.drop-on {
  border-color: var(--mint);
  background: color-mix(in srgb, var(--mint) 7%, transparent);
}

.hyp-card {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-left: 3px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  margin-bottom: 8px;
  cursor: grab;
}
.hyp-card.dragging { opacity: .45; }
.hyp-card summary { cursor: pointer; list-style: none; }
.hyp-card summary::-webkit-details-marker { display: none; }

/* Полоска слева — состояние. Цветом только она: красить карточку целиком
   значит сделать доску нечитаемой, когда тревожных больше одной. */
.hyp-card.hyp-ok { border-left-color: var(--mint); }
.hyp-card.hyp-warn { border-left-color: var(--amber); }
.hyp-card.hyp-bad { border-left-color: var(--red); }

.hyp-line { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.hyp-name { font-weight: 600; }
.hyp-scope-said { color: var(--muted); font-size: 12.5px; margin-top: 2px; }

.hyp-bar {
  height: 4px; border-radius: 3px; background: var(--surface-3);
  margin-top: 8px; overflow: hidden;
}
.hyp-bar i { display: block; height: 100%; background: var(--accent); }
.hyp-days { font-size: 12px; margin-top: 3px; }

.hyp-body { margin-top: 10px; border-top: 1px solid var(--border); padding-top: 10px; }
.hyp-statement { margin: 0 0 8px; color: var(--text-2); }
.hyp-note {
  border-radius: var(--radius-sm); padding: 7px 10px; font-size: 12.5px;
  background: var(--surface-3); margin-bottom: 8px;
}
.hyp-note.hyp-bad, .hyp-note.warn { color: var(--text); }
.hyp-goals { margin: 8px 0; }
.hyp-goals small.good { color: var(--mint); }
.hyp-goals small.bad { color: var(--red); }
.hyp-chart { margin-top: 10px; }
.hyp-chart-note, .hyp-small-note { font-size: 12px; margin-top: 6px; }
.hyp-acts { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 10px; }
.hyp-acts form { margin: 0; }

/* Шаги замысла: что уезжает в Ozon. Рамка слева и отступ — блок читают
   не как список свойств, а как перечень поручений, и он должен отделяться
   от целей выше, иначе «цена 3 548» и «заказы до 30» сливаются в одно. */
.hyp-steps {
  margin: 10px 0; padding: 8px 10px;
  border-left: 2px solid var(--accent); background: var(--surface-3);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
}
.hyp-steps-head { font-size: 12px; color: var(--muted); margin-bottom: 6px; }
.hyp-steps-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
.hyp-steps-list li { display: flex; gap: 8px; align-items: baseline; flex-wrap: wrap; }
.hyp-step-said { font-size: 13px; }

/* Черновик ответа покупателю. Отбит рамкой слева: это не часть вопроса,
   а то, что продавец скопирует и отправит от своего имени. */
.draft {
  margin-top: 8px; padding: 8px 10px;
  border-left: 2px solid var(--mint); background: var(--surface-3);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
}
.draft-head { display: flex; align-items: center; gap: 10px; margin-bottom: 4px; }
.draft-head .muted { font-size: 12px; }
.draft-copy { padding: 2px 10px; font-size: 12px; }
.draft-text { white-space: pre-wrap; font-size: 13.5px; }
.draft-ask { margin-top: 6px; }
.draft-ask button { padding: 3px 12px; font-size: 12.5px; }

/* Строка шага в форме: артикул, что меняем, цена, акция. Той же сеткой,
   что и строка цели рядом, — заполняют их подряд, и разъезжающиеся колонки
   на соседних строках читаются как разные формы. */
.hyp-step-row {
  display: grid; gap: 6px; margin-bottom: 6px;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr) minmax(0, 1fr) minmax(0, 1fr);
}
@media (max-width: 720px) { .hyp-step-row { grid-template-columns: 1fr 1fr; } }
.hyp-step-note { font-size: 12px; flex-basis: 100%; }

/* Форма новой гипотезы. Раскрывается на всю ширину: охват, цели и границы
   в узкой колонке не помещаются, а заполняют их вдумчиво и один раз. */
.hyp-new summary { cursor: pointer; list-style: none; }
.hyp-new summary::-webkit-details-marker { display: none; }
.hyp-new-head { display: flex; align-items: center; gap: 10px; }
.hyp-new-head small { display: block; color: var(--muted); font-size: 12.5px; }
.hyp-plus {
  width: 30px; height: 30px; flex: none; border-radius: 8px;
  background: var(--mint); color: var(--ink);
  display: grid; place-items: center; font-size: 19px; line-height: 1;
}
.hyp-form { margin-top: 12px; display: grid; gap: 12px; }
.hyp-two { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 16px; }
@media (max-width: 900px) { .hyp-two { grid-template-columns: minmax(0, 1fr); } }
.hyp-legend { font-weight: 600; margin-bottom: 6px; }
.hyp-field { display: grid; gap: 4px; }
.hyp-field > span { font-size: 12.5px; color: var(--text-2); }
.hyp-field input, .hyp-field textarea, .hyp-field select, .hyp-goal select, .hyp-goal input {
  background: var(--surface-2); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 7px 9px; font: inherit; width: 100%;
}
.hyp-field small { color: var(--muted); font-size: 12px; }
.hyp-scope { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
@media (max-width: 700px) { .hyp-scope { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.hyp-wide { grid-column: 1 / -1; }
.hyp-goal {
  display: grid;
  grid-template-columns: minmax(0, 1.6fr) minmax(0, .9fr) minmax(0, .8fr) minmax(0, .9fr);
  gap: 6px; margin-bottom: 6px;
}
.hyp-tally { display: flex; gap: 16px; flex-wrap: wrap; margin-top: 12px; }
.hyp-tally-one { display: flex; align-items: center; gap: 6px; }
.hyp-hist-said { font-size: 12px; margin-top: 2px; }
.hyp-history { margin-top: 14px; }
.hyp-history summary { cursor: pointer; }
.hyp-history summary small { color: var(--muted); margin-left: 8px; }
.hyp-from {
  display: inline-block; margin-bottom: 8px; font-size: 12.5px;
  color: var(--accent); text-decoration: none;
}
.hyp-from:hover { text-decoration: underline; }
.hyp-log { margin-top: 10px; }
.hyp-log summary { cursor: pointer; font-size: 12.5px; color: var(--text-2); }
.hyp-log summary small { color: var(--muted); }
.hyp-log-list { margin: 8px 0 0; padding-left: 18px; font-size: 12.5px; }
.hyp-log-list li { margin-bottom: 5px; }
.hyp-log-said { color: var(--text-2); }
.hyp-edit { margin-top: 10px; }
.hyp-edit summary { cursor: pointer; font-size: 12.5px; color: var(--text-2); }
.hyp-edit .hyp-form { margin-top: 10px; gap: 8px; }

/* --- Справочник по Ozon --------------------------------------------------- */
.know-item { display: block; text-decoration: none; color: inherit; cursor: pointer; }
.know-item:hover { border-left-color: var(--accent); }
.know-item.know-open { border-left-color: var(--mint); background: var(--surface-3); }
.know-text { margin-top: 10px; line-height: 1.55; }
.know-text h1, .know-text h2, .know-text h3 { font-size: 15px; margin: 14px 0 6px; }
.know-text p { margin: 0 0 10px; }
.know-text ul, .know-text ol { margin: 0 0 10px; padding-left: 20px; }
.know-text table { border-collapse: collapse; margin: 10px 0; font-size: 12.5px; }
.know-text td, .know-text th { border: 1px solid var(--border); padding: 5px 8px; }
.know-text code { background: var(--surface-3); padding: 1px 5px; border-radius: 4px; }
.know-see { margin-top: 14px; padding-top: 10px; border-top: 1px solid var(--border); }
.know-see a { margin-right: 12px; color: var(--accent); text-decoration: none; }
.know-see a:hover { text-decoration: underline; }

/* --- Акции ---------------------------------------------------------------
   Плитки акций стоят выше таблицы и работают как выбор: у продавца их
   больше десятка, и переключаться между ними приходится постоянно.
   Цвет плитки — про то, что с акцией не так, а не про её тип: тип
   продавцу ничего не решает, а «в минус» решает всё. */
.promos {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  gap: 10px;
  margin-bottom: 20px;
}
.promo {
  display: block;
  text-decoration: none;
  color: inherit;
  background: var(--surface);
  border: 1px solid var(--border-soft);
  border-left: 3px solid var(--border);
  border-radius: var(--radius);
  padding: 11px 13px 12px;
}
.promo:hover { background: var(--surface-2); }
.promo.on { background: var(--surface-2); border-color: var(--accent); }
.promo-in { border-left-color: var(--green); }
.promo-bad { border-left-color: var(--red); }
.promo-name {
  font-weight: 550; font-size: 13px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.promo-when { color: var(--muted); font-size: 11.5px; margin-top: 3px; }
.promo-count { display: flex; gap: 5px; flex-wrap: wrap; margin-top: 8px; }

.promo-hero {
  background: var(--surface);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius);
  padding: 16px 18px 15px;
  margin-bottom: 14px;
}
.promo-hero-title { font-size: 17px; margin: 0; }
.promo-hero-when { color: var(--muted); font-size: 12.5px; margin-top: 3px; }
.promo-note { margin-top: 12px; line-height: 1.55; font-size: 13.5px; }
.promo-figures {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
  gap: 12px;
  margin: 14px 0 6px;
}
.promo-figure-label { display: block; color: var(--muted); font-size: 12px; }
.promo-figure {
  font-size: 19px; font-weight: 600; letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}
/* Кнопка в строке таблицы: обычная кнопка втрое выше строки и ломает ритм. */
.promo-sheet button { padding: 3px 9px; font-size: 12px; }

/* Раскрытие длинного уведомления. Список из шестидесяти товаров нельзя
   показывать целиком — экран станет лентой, — но и прятать навсегда
   нельзя: раньше «…и ещё 48» было просто текстом. */
.alert-more { margin-top: 6px; }
.alert-more summary {
  cursor: pointer; font-size: 12.5px; color: var(--accent);
  list-style: none; display: inline-block; padding: 2px 0;
}
.alert-more summary::-webkit-details-marker { display: none; }
.alert-more summary::before { content: "▾ "; }
.alert-more[open] summary::before { content: "▴ "; }
.alert-more summary:hover { text-decoration: underline; }

/* «Объясни просто»: перевод отчёта с языка показателей на человеческий.
   Кнопка неяркая — она не главное действие на экране, а помощь тому,
   кому показатели ничего не говорят. */
.explain-simply { margin-top: 14px; border-top: 1px solid var(--border-soft); padding-top: 12px; }
.explain-ask { font-size: 12.5px; padding: 5px 12px; }
.explain-answer {
  margin-top: 10px; line-height: 1.6; font-size: 13.5px;
  background: var(--surface-2); border-radius: var(--radius); padding: 12px 14px;
}
.explain-answer:empty { display: none; }

/* ---------- заявки на скидку ---------- */

/* Числовое поле без крутилок — везде, а не только в настройках.
   Крутилки занимают треть узкого поля в ячейке таблицы, попасть по ним
   мышью трудно, а число здесь набирают с клавиатуры. В строке заявки
   они к тому же съедали место у самой цены: поле выглядело сломанным. */
.plain-number { appearance: textfield; -moz-appearance: textfield; }
.plain-number::-webkit-outer-spin-button,
.plain-number::-webkit-inner-spin-button { appearance: none; margin: 0; }

/* Колонка решения закреплена у правого края. Таблица шире экрана, и раньше
   кнопки уезжали за него: чтобы одобрить заявку, приходилось прокручивать
   вбок, теряя из виду товар и маржу — то самое, ради чего решение
   и принимают. */
.discounts .pin-right {
  position: sticky; right: 0; z-index: 2;
  background: var(--surface);
  border-left: 1px solid var(--border);
}
.discounts thead .pin-right { z-index: 9; background: var(--surface-2); }
.discounts tbody tr:hover .pin-right { background: var(--surface-2); }

/* Решение одной строкой: цена, «Одобрить», «Отклонить». Раньше кнопки
   переносились под поле и строка занимала три высоты. */
/* Поле и обе кнопки — в одну строку, причина отказа — во вторую.
   Без переноса причина вставала в тот же ряд и растягивала закреплённую
   колонку до трети экрана: чем длиннее объяснение, тем меньше оставалось
   видно товара, ради которого объяснение и читают. */
.discounts .decide {
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  width: 292px;
}
.discounts .decide > input, .discounts .decide > button { flex: 0 0 auto; }
/* Ширину колонке задаём явно. Без неё браузер ужимает ячейку по самому
   широкому одиночному элементу, и перенос, задуманный для одной причины
   отказа, разносит по строкам поле и обе кнопки. */
.discounts .pin-right { min-width: 316px; }
.discounts .decide-price {
  width: 82px; text-align: right;
  background: var(--surface-2); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 6px 9px; font: inherit; font-family: var(--mono); font-size: 13px;
}
.discounts .decide-price:focus { outline: none; border-color: var(--accent); }
.discounts .decide button { white-space: nowrap; }
/* Причина отказа лежит под кнопками и появляется, только когда есть.
   Пустая строка не должна раздвигать колонку. */
.discounts .decide-note:empty { display: none; }
.discounts .decide-note {
  flex-basis: 100%; color: var(--amber); font-size: 11.5px; line-height: 1.35;
}

.discounts .cell-percent { display: inline-flex; align-items: center; gap: 4px; }
.discounts .cell-percent .cell-input { width: 62px; text-align: right; }
.field-warn { border-color: var(--amber) !important; }
.field-hint { color: var(--muted); font-size: 11.5px; line-height: 1.4; }

/* Строка, которую правила не пропускают, помечена целиком: продавец
   смотрит на маржу, а не на подпись у кнопки. */
.discounts tr.row-blocked td { background: color-mix(in srgb, var(--red) 7%, transparent); }
.discounts tr.row-counting { opacity: 0.75; }

/* Разбор удержаний по наведению.
   Не окно и не отдельная страница: вопрос «из чего эти две тысячи»
   возникает на секунду и на секунду же должен закрываться. Открывается
   и с клавиатуры — по таблице ходят табом. */
.held-cell { position: relative; cursor: help; border-bottom: 1px dotted var(--border); }
.held-drop {
  display: none;
  position: absolute; right: 0; top: calc(100% + 6px); z-index: 20;
  min-width: 268px; text-align: left;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 11px 13px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45);
  font-family: var(--font); white-space: normal;
}
.held-cell:hover .held-drop, .held-cell:focus .held-drop,
.held-cell:focus-within .held-drop { display: block; }
.held-title {
  display: block; font-size: 11.5px; color: var(--muted);
  text-transform: uppercase; letter-spacing: 0.02em; margin-bottom: 5px;
}
.held-title + .held-table { margin-bottom: 10px; }
.held-table { width: 100%; font-size: 12.5px; }
.held-table td { padding: 3px 0; border: none; color: var(--text-2); }
.held-table td.num { color: var(--text); font-family: var(--mono); }
.held-table tr.held-total td { border-top: 1px solid var(--border-soft); font-weight: 600; }
.held-empty { color: var(--muted); font-size: 12px; }

/* Правила: таблица полей, у каждого своя маленькая форма сохранения. */
.rules-table .cell-input { width: 78px; text-align: right; }
.rules-table .filter-input { min-width: 160px; padding: 5px 9px; font-size: 12.5px; }
.rules-table .rule-actions { display: flex; gap: 8px; align-items: center; white-space: nowrap; }
.link-button {
  background: none; border: none; padding: 0; cursor: pointer;
  color: var(--muted); font: inherit; font-size: 12.5px; text-decoration: underline;
}
.link-button:hover { color: var(--red); }

/* ---------- реклама ---------- */

/* Название строки и подпись под ним. Подпись — артикул или номер кампании:
   то, по чему строку находят в кабинете Ozon, когда решают её судьбу. */
.ads-name { max-width: 320px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ads-sub { color: var(--muted); font-size: 11.5px; margin-top: 2px; }

/* Совет строкой под числами, а не подсказкой при наведении.
   Показы без кликов чинятся карточкой, клики без корзины — ценой, корзина
   без заказа — остатком: это разные действия, и прятать их за курсором
   значит не сказать вовсе. */
.ads-advice td {
  padding-top: 0; padding-bottom: 10px;
  color: var(--amber); font-size: 12px; line-height: 1.4;
  border-bottom: 1px solid var(--border-soft);
}
tbody tr.ads-advice:hover { background: none; }

/* Сдвиг к прошлому периоду стоит рядом с числом, а не вместо него:
   «20 685 ₽ −23 %» отвечает и на «сколько», и на «куда идёт». В ячейке
   таблицы он мельче, чем в карточке метрики, — иначе перебивает само число. */
td .metric-delta { font-size: 11px; margin-left: 5px; }

/* ---------- воронка ---------- */

/* Ступени: название, полоса, число. Полоса в логарифме — почему так,
   написано в analytics/funnel.py и подписано на самой странице. */
.funnel-steps { display: flex; flex-direction: column; gap: 2px; margin-top: 4px; }
.fstep, .fgap { display: grid; grid-template-columns: 132px 1fr 160px; gap: 14px; }
.fstep { align-items: center; }
.fname { font-size: 14px; font-weight: 500; }
.fbar { height: 32px; background: var(--surface-2); border-radius: 7px; overflow: hidden; }
.fbar i {
  display: block; height: 100%;
  background: linear-gradient(90deg,
    color-mix(in srgb, var(--accent) 85%, transparent), var(--accent));
}
.fnum {
  text-align: right;
  font: 600 15px/1 var(--mono); font-variant-numeric: tabular-nums;
}
.fnum em { display: block; font-style: normal; font-size: 11.5px; color: var(--muted); margin-top: 5px; }

/* Переход между ступенями. Он и есть предмет разговора: ломается не воронка
   целиком, а один переход, и у каждого своя причина. */
.fgap { font-size: 12.5px; color: var(--text-2); padding: 4px 0; align-items: center; }
.fgap b { color: var(--text); font-family: var(--mono); font-weight: 600; }
.fgap .muted { margin-left: 8px; font-size: 11.5px; }

/* Строка с просевшим переходом помечена целиком: в таблице на двадцать
   колонок один подсвеченный процент теряется. */
.funnel tr.row-weak td { background: color-mix(in srgb, var(--amber) 7%, transparent); }
.funnel td.warn { font-weight: 600; }

@media (max-width: 760px) {
  .fstep, .fgap { grid-template-columns: 96px 1fr 118px; gap: 10px; }
  .fname { font-size: 12.5px; }
}

/* ---------- предохранители записи ---------- */

/* Три строки с галочкой или крестом. Состояние читается формой и словом,
   а не только цветом: «зелёная пилюля» ничего не говорит человеку, который
   не знает, чего он ждёт. */
.guards { display: flex; flex-direction: column; gap: 10px; margin: 4px 0 16px; }
.guard {
  display: grid; grid-template-columns: 26px 1fr; gap: 12px; align-items: start;
  background: var(--surface-2); border: 1px solid var(--border-soft);
  border-radius: var(--radius); padding: 12px 14px;
}
.guard-mark {
  font: 600 15px/1.4 var(--font); text-align: center;
}
.guard-on .guard-mark { color: var(--green); }
.guard-off .guard-mark { color: var(--amber); }
.guard-off { border-color: color-mix(in srgb, var(--amber) 35%, var(--border-soft)); }
.guard-name { font-size: 14.5px; font-weight: 600; }
.guard-state { font-size: 13px; color: var(--text-2); margin-top: 2px; }
.guard-off .guard-state { color: var(--amber); }
/* Что делать — только у невзведённого: у снятого советовать нечего. */
.guard-what { font-size: 12.5px; color: var(--muted); line-height: 1.45; margin-top: 6px; }

/* Остаток вопросов у строки ввода. Не полоска и не счётчик со всеми числами:
   человеку в этот момент нужно одно — хватит ли на сегодня. Подробности
   в подсказке, вся картина — в кабинете. */
.composer-left {
  font-size: 12px; color: var(--muted); white-space: nowrap;
  padding: 3px 8px; border-radius: 999px; background: var(--surface-2);
}
.composer-left.low { color: var(--text); background: color-mix(in srgb, var(--accent) 18%, transparent); }
.composer-left.empty { color: #fff; background: var(--red); }

/* Три графика заказов: высота ряда задаётся кольцами, а столбцы её не
   заполняли. Кольцо рисуется квадратом 220×220, под ним переносится легенда —
   карточка выходила под 450 пикселей. Столбцы при этом рисуются в пропорции
   900×200, то есть в широкой колонке дают около 180: под ними оставалось
   двести пикселей пустоты, и график висел в верхней части карточки.

   Лечим с обеих сторон: кольцо ужимаем, столбцам разрешаем растянуться на всю
   оставшуюся высоту. Растяжение безопасно — у графика `preserveAspectRatio`
   выключен, он и рассчитан тянуться по контейнеру. Ряд становится примерно
   на треть ниже, а таблица заказов поднимается ближе к началу страницы. */
.orders-charts .chart-card { display: flex; flex-direction: column; }
/* Растянуть график флексом нельзя: у svg с viewBox своя внутренняя пропорция,
   и `flex-grow` её не пересиливает — проверено, высота остаётся прежней.
   Поэтому задаём её прямо. Число подобрано под высоту карточки с кольцом,
   чтобы ряд получался ровным без подпорок. */
.orders-charts .chart-card > .chart { height: 190px; }
.orders-charts .donut-wrap { justify-content: center; }
.orders-charts .chart-donut { width: auto; height: 150px; }

/* ---------- сборка заказов ---------- */
/* Карточка на отправление, а не строка таблицы: у позиции есть фото,
   признаки и счётчик кодов, и в ячейку это не складывается. Собирают
   по одному отправлению за раз — карточка ровно об этом. */
.assembly { display: flex; flex-direction: column; gap: 12px; }
.asm {
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface); padding: 14px 16px;
}
/* Просроченное — красной кромкой слева. Не заливкой: заливка кричит,
   а тут нужно, чтобы взгляд нашёл строку и пошёл дальше по делу. */
.asm-late { border-left: 3px solid var(--red); }
.asm-ready { border-left: 3px solid var(--mint); }

.asm-head { display: flex; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.asm-number { font-weight: 600; font-size: 15px; font-variant-numeric: tabular-nums; }
.asm-sub { color: var(--muted); font-size: 12.5px; margin-top: 2px; }
.asm-when { text-align: right; font-size: 12.5px; }
.asm-deadline { color: var(--text-2); }
.asm-deadline.danger { color: var(--red); font-weight: 600; }

.asm-money {
  display: flex; flex-wrap: wrap; align-items: center; gap: 14px;
  margin: 10px 0; padding: 8px 10px; border-radius: var(--radius-sm);
  background: var(--surface-2); font-size: 13px;
}
.asm-money .good { color: var(--mint); }
.asm-money .bad { color: var(--red); }
.asm-warn { color: var(--muted); font-size: 12px; }
.asm-flag {
  font-size: 11.5px; padding: 2px 8px; border-radius: 999px;
  background: color-mix(in srgb, var(--accent) 18%, transparent); color: var(--text);
}

.asm-lines { display: flex; flex-direction: column; gap: 10px; }
.asm-line { display: flex; gap: 12px; align-items: flex-start; }
.asm-pic {
  width: 56px; height: 56px; flex: 0 0 56px; border-radius: var(--radius-sm);
  overflow: hidden; background: var(--surface-2);
  display: flex; align-items: center; justify-content: center;
}
.asm-pic img { width: 100%; height: 100%; object-fit: cover; }
.asm-nopic { font-size: 10px; color: var(--muted); text-align: center; }
.asm-what { min-width: 0; flex: 1; }
.asm-name { font-size: 13.5px; line-height: 1.35; }
.asm-meta { color: var(--muted); font-size: 12px; margin-top: 3px; }

.asm-codes { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 7px; }
/* Позиция, по которой Ozon кода не требует. Поле стоит всё равно —
   его требование отстаёт от закона, — но кричать о нём незачем. */
.asm-codes-quiet { opacity: .68; }
.asm-codes-quiet:hover, .asm-codes-quiet:focus-within { opacity: 1; }
.asm-count { font-size: 12px; color: var(--text-2); }
.asm-count.ok { color: var(--mint); }
.asm-code {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 11.5px; padding: 2px 4px 2px 8px; border-radius: 999px;
  background: var(--surface-2); color: var(--text-2);
}
.asm-drop {
  border: none; background: none; color: var(--muted); cursor: pointer;
  font-size: 14px; line-height: 1; padding: 0 4px;
}
.asm-drop:hover { color: var(--red); }

.asm-actions {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
  margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--border-soft);
}
.asm-actions form { margin: 0; }
.asm-log { font-size: 12px; color: var(--muted); }

/* Окно сканера. Поверх страницы, но не на весь экран: у стола смотрят
   и на список кодов, и на карточку заказа под ним. */
.scan-sheet {
  position: fixed; inset: 0; z-index: 60; display: flex;
  align-items: center; justify-content: center; padding: 16px;
  background: color-mix(in srgb, #000 55%, transparent);
}
/* Атрибут hidden слабее правила выше — без этого окно видно всегда, а кнопка
   закрытия выглядит сломанной: она снимает атрибут, а на экране ничего не
   меняется. Та же строчка стоит у `.modal-back`, и наступить на это второй
   раз оказалось легко. */
.scan-sheet[hidden] { display: none; }
.scan-box {
  width: min(420px, 100%); max-height: 90vh; overflow: auto;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 16px;
}
.scan-top { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; }
.scan-title { font-weight: 600; }
.scan-where { font-size: 12.5px; margin-top: 2px; }
.scan-close {
  border: none; background: none; color: var(--muted);
  font-size: 20px; line-height: 1; cursor: pointer;
}
.scan-field {
  width: 100%; margin: 12px 0; padding: 9px 11px; font: inherit;
  background: var(--surface-2); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
}
.scan-camera-wrap { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.scan-video { width: 100%; margin-top: 10px; border-radius: var(--radius-sm); overflow: hidden; }
.scan-video[hidden] { display: none; }
.scan-video video { width: 100%; display: block; background: #000; }
.scan-lines { margin-top: 12px; display: flex; flex-direction: column; gap: 5px; }
.scan-line { font-size: 12.5px; padding: 5px 8px; border-radius: var(--radius-sm); }
.scan-line.ok { background: color-mix(in srgb, var(--mint) 16%, transparent); color: var(--text); }
.scan-line.bad { background: color-mix(in srgb, var(--red) 16%, transparent); color: var(--text); }

/* ---------- карточка товара ---------- */
/* Два столбца: слева то, что читают подряд — медиа, описание, разбор;
   справа то, к чему возвращаются глазами — характеристики, вопросы,
   запись о бренде. На узком экране столбцы складываются в один. */
.card-layout { display: grid; grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr); gap: 16px; }
@media (max-width: 1000px) { .card-layout { grid-template-columns: minmax(0, 1fr); } }

.gallery { display: grid; grid-template-columns: repeat(auto-fill, minmax(110px, 1fr)); gap: 8px; }
.gallery-item {
  display: block; aspect-ratio: 1; border-radius: var(--radius-sm);
  overflow: hidden; background: var(--surface-2);
}
.gallery-item img { width: 100%; height: 100%; object-fit: cover; display: block; }
.card-video { width: 100%; margin-top: 10px; border-radius: var(--radius-sm); }

/* Описание приходит из Ozon с разметкой продавца: заголовки, списки, переносы.
   Показываем как есть, но ограничиваем — иначе чужой тег ломает страницу. */
.card-description { font-size: 13.5px; line-height: 1.55; color: var(--text-2); }
.card-description img { max-width: 100%; height: auto; }
.card-description * { max-width: 100%; }
.card-note { white-space: pre-wrap; font-size: 13.5px; line-height: 1.5; color: var(--text-2); }

.props { width: 100%; border-collapse: collapse; font-size: 13px; }
.props th {
  text-align: left; font-weight: 400; color: var(--muted);
  padding: 5px 10px 5px 0; vertical-align: top; white-space: nowrap;
}
.props td { padding: 5px 0; color: var(--text-2); }

.qlist { display: flex; flex-direction: column; gap: 10px; margin-bottom: 10px; }
.qitem { padding: 8px 10px; border-radius: var(--radius-sm); background: var(--surface-2); }
.qtext { font-size: 13px; line-height: 1.45; }

.brands { display: flex; flex-direction: column; gap: 12px; }
.brand-note {
  width: 100%; padding: 10px 12px; font: inherit; font-size: 13.5px; line-height: 1.5;
  background: var(--surface-2); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius-sm); resize: vertical;
}
.brand-note:focus { outline: none; border-color: var(--accent); }

/* ---------- база знаний продавца ---------- */
/* Карточками, а не строками таблицы: у базы есть название, область и
   состояние сборки — три разных по смыслу вещи, и в ячейку они ложатся
   одинаково серо. */
.kb-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 12px; }
.kb-card {
  display: block; padding: 14px 16px; text-decoration: none; color: inherit;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
}
.kb-card:hover { border-color: var(--accent); }
.kb-title { font-weight: 600; font-size: 14.5px; }
.kb-scope { color: var(--muted); font-size: 12.5px; margin-top: 2px; }
.kb-meta { font-size: 12px; color: var(--text-2); margin-top: 8px; }
.kb-ready { color: var(--mint); }
.kb-stale { color: var(--muted); }

.kb-layout { display: grid; grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr); gap: 16px; }
@media (max-width: 1000px) { .kb-layout { grid-template-columns: minmax(0, 1fr); } }

.kb-field {
  width: 100%; margin-bottom: 8px; padding: 9px 11px; font: inherit; font-size: 13.5px;
  background: var(--surface-2); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius-sm); resize: vertical;
}
.kb-field:focus { outline: none; border-color: var(--accent); }

.kb-sources { display: flex; flex-direction: column; gap: 10px; }
.kb-source { padding: 10px 12px; border-radius: var(--radius-sm); background: var(--surface-2); }
.kb-source-head { display: flex; align-items: center; gap: 8px; font-size: 12.5px; flex-wrap: wrap; }
.kb-kind {
  font-size: 11px; padding: 1px 7px; border-radius: 999px;
  background: color-mix(in srgb, var(--accent) 18%, transparent); color: var(--text);
}
.kb-drop { border: none; background: none; color: var(--muted); cursor: pointer; font-size: 15px; }
.kb-drop:hover { color: var(--red); }
.kb-preview { margin-top: 6px; font-size: 12.5px; color: var(--text-2); line-height: 1.45; }
.kb-error { margin-top: 6px; font-size: 12.5px; color: var(--red); }

/* Печать пачкой: отметки в карточках, кнопка сверху. */
.asm-batch {
  display: flex; flex-wrap: wrap; align-items: center; gap: 12px;
  margin-bottom: 12px; padding: 10px 14px;
  background: var(--surface-2); border-radius: var(--radius-sm); font-size: 13px;
}
.asm-all { display: inline-flex; align-items: center; gap: 7px; cursor: pointer; }
.asm-head-left { display: flex; gap: 10px; align-items: flex-start; }
.asm-pick { padding-top: 2px; }
/* Номер отправления — кнопка, а не текст: по нему открывают подробности.
   Выглядит ссылкой, потому что ведёт себя как ссылка. */
.asm-number.asm-open {
  border: none; background: none; padding: 0; cursor: pointer;
  color: var(--text); font: inherit; font-weight: 600; font-size: 15px;
  font-variant-numeric: tabular-nums; text-align: left;
}
.asm-number.asm-open:hover { color: var(--accent); text-decoration: underline; }

/* ---------- карта прав ключа ---------- */
/* Четыре исхода и четыре смысла: «нет роли» чинится новым ключом, «нужна
   подписка» ключом не чинится вовсе, «не ответил» — про сеть, а «нечем
   проверить» значит, что вопрос не задавали. Свести их в одно «недоступно»
   значило бы отправить продавца чинить не то. */
.rights { margin-top: 12px; }
.rights-head {
  padding: 10px 12px; border-radius: var(--radius-sm); margin-bottom: 10px;
  background: var(--surface-2); font-size: 13px; line-height: 1.45;
}
.rights-table { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.rights-table td { padding: 5px 8px 5px 0; vertical-align: top; }
.rights-area { white-space: nowrap; font-weight: 500; }
.rights-write {
  font-size: 10.5px; padding: 1px 6px; margin-left: 6px; border-radius: 999px;
  background: color-mix(in srgb, var(--accent) 20%, transparent);
}
.rights-what { color: var(--text-2); }
.rights-verdict { white-space: nowrap; }
.rights-detail { font-size: 11.5px; }
/* Каким ключом проверена строка. Ключей два, и без подписи отказ по роли
   не говорит, какой из них менять. */
.rights-key {
  display: inline-block; margin-right: 6px; padding: 0 5px; border-radius: 4px;
  background: var(--surface-2); color: var(--text-2); white-space: nowrap;
}
.rights-ok .rights-verdict { color: var(--mint); }
.rights-bad .rights-verdict { color: var(--red); }
.rights-plan .rights-verdict { color: var(--accent); }
.rights-odd .rights-verdict { color: var(--muted); }

/* ---------- поле секрета ---------- */
/* Точки рисует CSS, а не тип поля. Причина не в красоте: по типу `password`
   менеджеры паролей опознают поле как пароль от сайта и подставляют туда своё,
   заново после каждой очистки. На живом кабинете это стоило часа — в поле
   ключа Ozon оказывался чужой пароль. */
.secret-field { display: flex; align-items: center; gap: 6px; }
.secret-input {
  flex: 1; min-width: 0;
  -webkit-text-security: disc;  /* Chromium, Safari, Яндекс */
  text-security: disc;
  font-family: ui-monospace, Consolas, monospace;
}
.secret-input.secret-shown { -webkit-text-security: none; text-security: none; }
.secret-input.secret-odd { border-color: var(--red); }
.secret-eye {
  border: 1px solid var(--border); background: var(--surface-2); color: var(--muted);
  border-radius: var(--radius-sm); cursor: pointer; padding: 6px 9px; line-height: 1;
}
.secret-eye:hover { color: var(--text); }
.secret-note { display: block; margin-top: 5px; font-size: 12px; line-height: 1.4; color: var(--red); }
/* Свой `display` перебивает атрибут `hidden`, и подсказка про подставленный
   пароль висела бы под пустым полем всегда. Правило нужно каждому классу,
   у которого `display` задан: скрытость атрибутом — не сильнее автора. */
.secret-note[hidden] { display: none; }

/* Загрузка документов одной кнопкой.

   Раньше здесь стоял ряд полей: номер накладной (обязательный), поставщик,
   дата, НДС и узкий `input type=file`. Продавец приходит сюда с пачкой
   бумаг, а не с одним документом, и номера в этот момент не знает — форма
   требовала ввести то, что он собирался переписать позже.

   Поэтому осталась одна большая область: сюда кидают файлы или нажимают
   и выбирают. Область крупная нарочно — это единственное действие панели,
   которое делают мышью с файлами в руке. */
.drop-form { display: flex; align-items: stretch; gap: 12px; margin-top: 12px; }
.drop {
  flex: 1; min-width: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 6px; padding: 22px 18px; text-align: center; cursor: pointer;
  border: 1px dashed var(--border); border-radius: var(--radius);
  background: var(--surface-2); transition: border-color .15s, background .15s;
}
.drop:hover, .drop.over { border-color: var(--accent); background: var(--surface-3, var(--surface-2)); }
/* Само поле не прячем через `display:none`: тогда его не видит проверка
   `required` у формы и не находят программы чтения с экрана. */
.drop input[type="file"] { position: absolute; width: 1px; height: 1px; opacity: 0; }
.drop-main { font-size: 14px; }
.drop-note { font-size: 12px; line-height: 1.45; max-width: 46em; }
.drop-form > button { align-self: center; padding: 10px 20px; }
.drop-help { margin-top: 10px; font-size: 12.5px; }
.drop-help summary { cursor: pointer; }

/* Коды маркировки списком под товаром. Моноширинный, чтобы одинаковые
   начала кодов различались глазами, и с обрезкой: код длиной под сотню
   знаков, целиком он нужен только в подсказке. */
.code-list { display: flex; flex-direction: column; gap: 3px; margin-top: 8px; }
.code-line {
  display: flex; justify-content: space-between; gap: 12px;
  font-size: 12px; padding: 3px 0; border-bottom: 1px solid var(--border);
}
.code-line .code { font-family: ui-monospace, Consolas, monospace; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Статус отправления у стола сборки — плашкой, а не серой строкой.

   У стола он главный: по нему видно, чем заказ отличается от соседнего,
   и в кабинете Ozon он тоже выделен цветом. Серым он читался как подпись
   к номеру, то есть как ничто.

   Цвет по смыслу, а не по красоте: «ожидает сборки» — то, что ждёт рук,
   и это янтарный, как в кабинете; «ожидает отгрузки» — уже сделанное,
   и оно спокойно-зелёное. Незнакомый статус остаётся серым: придумывать
   ему цвет значило бы придумывать ему смысл. */
.asm-status {
  display: inline-block; padding: 1px 8px; margin-right: 6px;
  border-radius: 999px; font-size: 12px; line-height: 1.6;
  border: 1px solid var(--border); color: var(--muted);
}
.asm-status-awaiting_packaging {
  color: var(--amber);
  border-color: color-mix(in srgb, var(--amber) 45%, transparent);
  background: color-mix(in srgb, var(--amber) 12%, transparent);
}
.asm-status-awaiting_deliver {
  color: var(--green);
  border-color: color-mix(in srgb, var(--green) 40%, transparent);
  background: color-mix(in srgb, var(--green) 10%, transparent);
}

/* ---------- сборка: два этапа, разбор денег, отгрузка без КИЗ ---------- */
/* Этапы стола отдельной полосой, а не вперемешку с разделами: «Заказы —
   Возвраты — Сборка» отвечают на вопрос «куда я попал», а «Ожидает сборки —
   Готов к отгрузке» — на «что я сейчас делаю». Это разные вопросы. */
/* Этапы слева, поиск справа — но в одной строке и вплотную к списку.
   В шапке страницы поле стояло через полэкрана от того, что фильтрует. */
.asm-stages { margin: 0 0 12px; }
.asm-stages > .filters { margin-left: auto; }

/* Разбор денег по статьям. Свёрнут по умолчанию: у стола сумма важнее
   её устройства, но проверить устройство должно быть можно не отходя. */
.asm-parts { display: inline-block; }
.asm-parts > summary { cursor: pointer; font-size: 12px; }
.asm-parts table { margin-top: 6px; border-collapse: collapse; font-size: 12.5px; }
.asm-parts td { padding: 2px 10px 2px 0; white-space: nowrap; }
.asm-parts td:last-child { text-align: right; font-variant-numeric: tabular-nums; }
.asm-parts-total td { border-top: 1px solid var(--border); font-weight: 600; padding-top: 4px; }

/* Кнопка, которой подписывают отгрузку без кода маркировки. Красная
   нарочно: у неё цена выше, чем у соседних, — расхождение в Честном Знаке
   всплывёт через недели и закрывать его придётся руками. */
.modal-actions { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 16px; }
button.danger {
  border-color: color-mix(in srgb, var(--red) 55%, var(--border));
  color: var(--red);
}
button.danger:hover { background: color-mix(in srgb, var(--red) 14%, transparent); }

/* ---------- панель агента: навык, модель и усилители ---------- */
/* Панель была младшей копией чата: спросить можно, а выбрать, кем и чем
   отвечать, — нет. Списки те же самые, поэтому и вид общий: `chat-skill`,
   `model-pick`, `boost-chip` берутся как есть. Своё здесь только то, что
   диктует ширина: панель узкая, и её тянут мышью. */
.assistant-bar-second { margin-top: 7px; }
.assistant-bar-second .composer-model { flex: 1; min-width: 0; }
/* Во всю ширину панели: список моделей длинный, и обрезанный по 260 px
   он показывал бы одно название без цены — то есть ровно половину того,
   ради чего человек в него смотрит. */
.assistant-bar-second .model-pick { max-width: none; width: 100%; }
.assistant-bar-second .composer-left { margin-right: 7px; white-space: nowrap; }

/* Усилители свёрнуты: в панели шириной в треть экрана десяток кнопок занял бы
   половину высоты, а нужны они не каждый раз. */
.assistant-boosters { margin-top: 7px; font-size: 12px; }
.assistant-boosters > summary { cursor: pointer; color: var(--muted); }
.assistant-boost-row { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 7px; }

/* Кнопка навыков стоит в одном ряду с прочими и должна быть такой же:
   иначе она читается как что-то другое по важности. */
.assistant-bar .skills-drop { display: inline-flex; position: relative; }

/* ---------- путь назад со страницы ---------- */
/* Над заголовком и слева. Справа его искали глазами через весь экран
   и не находили: там стоят действия страницы, а не выход с неё. Читают
   слева сверху — там и место обратной дороге. */
.page-back {
  display: inline-flex; align-items: center; gap: 6px;
  margin-bottom: 10px; padding: 5px 10px 5px 8px;
  border: 1px solid var(--border-soft); border-radius: 999px;
  color: var(--text-2); text-decoration: none; font-size: 13px;
  transition: border-color .15s, color .15s;
}
.page-back:hover { border-color: var(--accent); color: var(--text); }
.page-back svg { flex: none; }

/* ---------- срочность в таблице находок ---------- */
/* Плашка «разбираться сейчас» переносилась по словам и разъезжала колонку:
   два слова вставали друг под другом с рамкой вокруг каждого, и столбец
   переставал читаться. Плашка — цельная, колонка — по её ширине. */
.sheet td .pill { display: inline-block; white-space: nowrap; }
.watch-urgency { width: 1%; white-space: nowrap; }
.watch-advice { max-width: 260px; }

/* ---------- разбор находки ---------- */
/* Отдельная страница вместо ссылки в раздел: раздел — это общая таблица
   на семьсот позиций, где нужные четырнадцать надо искать заново. */
.find-lead { margin: 0; font-size: 14px; line-height: 1.6; color: var(--text); }

/* Итог до таблицы: сначала сумма, потом из чего она сложилась. Обратный
   порядок заставляет складывать глазами. */
.find-totals {
  display: flex; flex-wrap: wrap; gap: 10px; margin-top: 14px;
}
.find-total {
  flex: 1 1 150px; padding: 11px 14px;
  background: var(--surface); border: 1px solid var(--border-soft);
  border-radius: var(--radius-sm);
}
.find-total-name { color: var(--muted); font-size: 12px; margin-bottom: 4px; }
.find-total-value { font-size: 19px; font-variant-numeric: tabular-nums; }

/* Картинка в таблице разбора: товар узнают по ней быстрее, чем по артикулу,
   а решение «брать или нет» принимают глядя на то, что берут. */
.find-pic {
  width: 44px; height: 44px; object-fit: cover; border-radius: 6px; display: block;
}
.find-nopic { color: var(--muted); font-size: 10.5px; }
.find-table td { vertical-align: middle; }

.find-steps { margin: 0; padding-left: 20px; line-height: 1.7; font-size: 13.5px; }
.find-steps li { margin-bottom: 7px; }
.find-step-link { margin-left: 6px; color: var(--accent); text-decoration: none; white-space: nowrap; }
.find-step-link:hover { text-decoration: underline; }
.find-action {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--border-soft);
}

/* Подробности находки свёрнуты: на пятьдесят строк они занимали весь экран,
   и следующая находка уезжала под сгиб. */
.watch-more > summary { cursor: pointer; }
.watch-more[open] > summary { margin-bottom: 5px; }

/* Печать разбора: убираем всё, что не отчёт. Ради PDF своего генератора
   не заводим — браузер печатает страницу лучше и без зависимостей. */
@media print {
  .sidebar, .page-back, .assistant, .assistant-fab, .filters, .find-action { display: none; }
  .panel { border: none; page-break-inside: avoid; }
}

/* ---------- ключи для уведомлений ---------- */
/* Первая версия кнопки писала «положите значения в переменные проекта»
   и нигде их не показывала — совет, который нечем выполнить. */
.push-keys { margin-bottom: 12px; }
.push-key {
  display: flex; align-items: center; gap: 8px; margin-bottom: 8px; flex-wrap: wrap;
}
.push-key > span {
  min-width: 190px; font-size: 12px; color: var(--muted);
  font-family: var(--mono, ui-monospace, monospace);
}
.push-key > input {
  flex: 1 1 260px; min-width: 0;
  background: var(--surface); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 7px 10px; font: inherit; font-size: 12.5px;
  font-family: var(--mono, ui-monospace, monospace);
}
.push-key-warn { color: var(--amber, var(--muted)); }

/* ---------- настройка каналов уведомлений ---------- */
/* Всё это жило в переменных окружения и в подписи «выполните команду».
   У продавца нет ни терминала, ни доступа к серверу — и три канала из пяти
   молча не работали: задача отрабатывала, а сообщение не приходило никуда. */
.stack-form { display: grid; gap: 12px; max-width: 560px; }
.stack-form .field { display: grid; gap: 5px; }
.stack-form .field > span { font-size: 12.5px; color: var(--text-2); }
.stack-form .field > input {
  background: var(--surface); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 8px 11px; font: inherit; font-size: 13px;
}
.stack-form .form-actions { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }

/* Порядок шагов важнее их полноты: настройку делают один раз и в первый раз,
   а «создайте бота» без «как» — это тупик. */
.steps-list { margin: 0 0 14px; padding-left: 20px; line-height: 1.7; font-size: 13.5px; }
.steps-list li { margin-bottom: 6px; }
.check-said:empty { display: none; }
.panel-title .pill { margin-left: 8px; font-size: 11px; vertical-align: middle; }

/* ---------- закупка: отметки, картинки, количество ---------- */
/* Заказ собирают сверху вниз и глазами: колонка выбора встречает взгляд
   первой, картинка отвечает «что это», поле количества — «сколько беру». */
.purchase .pick-cell { width: 1%; padding-right: 4px; }
.purchase .pic-cell { width: 46px; padding-right: 6px; }
.pic-thumb {
  width: 38px; height: 38px; object-fit: cover; border-radius: 5px; display: block;
}
.qty-input {
  width: 70px; text-align: right;
  background: var(--surface); color: var(--text);
  border: 1px solid var(--border-soft); border-radius: var(--radius-sm);
  padding: 4px 7px; font: inherit; font-size: 12.5px;
  font-variant-numeric: tabular-nums;
}
.qty-input:focus { border-color: var(--accent); outline: none; }
/* Отмеченная строка подсвечена: в списке на полсотни позиций «что я уже
   выбрал» иначе выясняется только прокруткой до конца и обратно. */
.purchase tr:has(.pick-one:checked) { background: color-mix(in srgb, var(--accent) 9%, transparent); }
#pick-count:empty { display: none; }

/* Стрелочки у поля количества убраны: в таблице они появляются при наведении
   и дёргают ширину колонки, а число здесь набирают с клавиатуры или оставляют
   расчётное — щёлкать по стрелке на четвёртый десяток штук никто не станет. */
.qty-input::-webkit-outer-spin-button,
.qty-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.qty-input { -moz-appearance: textfield; appearance: textfield; }

/* --- Раздел «Коды»: лента состояний ------------------------------------

   Четыре карточки с подписью в 11 пикселей раздел не описывали: числа
   стояли рядом, но не складывались в картину, а объяснение под каждым
   читать было нечем. Здесь другая мысль: заказы за период — это одна
   партия товара, разложенная по состояниям, и главное в ней — доли.
   Поэтому лента, а не карточки: видно сразу, чего много, а не четыре
   числа, которые надо складывать в уме.

   Цвет несёт смысл, а не украшает. Тёплое — требует рук: красное «кода
   нет», синее «пора выводить», янтарное «вернулось». Холодное и серое
   идёт само: едет, собирается, выводит Ozon. */

.codes-state { margin: 4px 0 20px; }

.codes-bar {
  display: flex;
  height: 10px;
  border-radius: 999px;
  overflow: hidden;
  background: var(--surface-2);
  border: 1px solid var(--border-soft);
}
.codes-bar > * { display: block; height: 100%; background: var(--state); }
/* Крохотная доля не должна исчезать: один заказ из тысячи — это тоже
   работа, и увидеть его надо. */
.codes-bar > * { min-width: 3px; }

.codes-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 14px;
}
.codes-chip {
  display: inline-flex;
  align-items: baseline;
  gap: 9px;
  padding: 9px 15px 10px;
  border-radius: 999px;
  border: 1px solid var(--border-soft);
  background: var(--surface);
  color: var(--text);
  text-decoration: none;
  transition: border-color .12s ease, background .12s ease;
}
.codes-chip:hover { border-color: color-mix(in srgb, var(--state) 55%, var(--border)); }
.codes-chip.active {
  border-color: var(--state);
  background: color-mix(in srgb, var(--state) 12%, var(--surface));
}
.codes-chip .dot {
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--state); align-self: center; flex: none;
}
.codes-chip b {
  font-size: 19px; font-weight: 600;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}
.codes-chip .what { font-size: 13.5px; color: var(--text-2); }
.codes-chip.active .what { color: var(--text); }

/* Объяснение одно на выбранное состояние, а не под каждым числом.
   Четыре мелкие строки рядом читаются как шум; одна крупная — как ответ. */
.codes-note { color: var(--muted); font-size: 13px; margin-top: 12px; }

/* Состояния. Значения берутся из общих токенов панели: свой набор цветов
   на одной странице означал бы второй язык внутри одного продукта. */
.state-withdraw  { --state: var(--accent); }
.state-missing   { --state: var(--red); }
.state-returned  { --state: var(--amber); }
.state-cancelled { --state: color-mix(in srgb, var(--amber) 45%, var(--muted)); }
.state-waiting   { --state: color-mix(in srgb, var(--accent) 42%, var(--surface-3)); }
.state-packing   { --state: color-mix(in srgb, var(--green) 42%, var(--surface-3)); }
.state-ozon      { --state: var(--muted); }
.state-done      { --state: var(--green); }
.state-all       { --state: var(--text-2); }

/* Прайсы поставщиков. Цвет здесь про деньги, а не про настроение:
   подорожание — красное, потому что это расход, который уже случился,
   а подешевело — зелёное, потому что это окно на закупку. Новинка
   нейтральна: она ещё ничего не значит, пока по ней не решили. */
.price-up   { --state: var(--red); }
.price-down { --state: var(--green); }
.price-new  { --state: var(--accent); }
.price-same { --state: var(--muted); }
.price-all  { --state: var(--text-2); }

.codes-bar > .price-up,
.codes-bar > .price-down,
.codes-bar > .price-new,
.codes-bar > .price-same { background: var(--state); }

/* Название товара и артикул одной ячейкой: у поставщика они всегда
   рядом, а разносить их по колонкам значит удвоить ширину таблицы
   ради того, что читается вместе. */
.price-name { font-weight: 500; line-height: 1.35; }
.price-offer {
  display: flex; align-items: center; gap: 8px;
  margin-top: 3px; font-size: 12.5px;
}
.price-mark {
  padding: 1px 8px; border-radius: 999px;
  font-size: 11.5px; letter-spacing: 0.01em;
  color: var(--state);
  background: color-mix(in srgb, var(--state) 14%, transparent);
}
/* «Без изменений» не красим вовсе: таких строк большинство, и метка
   на каждой превратила бы таблицу в рябь. */
.price-mark.price-same { display: none; }

.price-delta {
  margin-top: 2px; font-size: 12.5px; font-weight: 500;
  color: var(--state); font-variant-numeric: tabular-nums;
}

/* Выбор поставщика — своей строкой. Подпись слева, потому что без неё
   ряд названий рядом с вкладками раздела читается как их продолжение. */
.prices-who {
  display: flex; align-items: center; gap: 12px;
  margin: 16px 0 4px;
}
.prices-who-label { font-size: 13px; color: var(--text-2); }

/* Ширины по содержимому, а не поровну. Служебные колонки берут ровно
   столько, сколько нужно их числу, а название забирает остаток: делить
   ширину поровну значит сжать «Игровой набор Сара и Мистер» в три строки
   при пустом месте под штрихкодом. */
.sheet.prices { table-layout: auto; }
.sheet.prices td { vertical-align: top; }
.sheet.prices .strong { font-weight: 600; }
.sheet.prices th:not(:first-child),
.sheet.prices td:not(:first-child) { width: 1%; white-space: nowrap; }
.sheet.prices th:first-child,
.sheet.prices td:first-child { width: 100%; min-width: 210px; }
.sheet.prices .price-name { white-space: normal; }

/* Описание — единственная колонка, которой перенос нужен: там предложение,
   а не число. Но и ей нужен предел, иначе одна многословная строка
   растянет таблицу за край экрана. */
.sheet.prices .price-note {
  white-space: normal;
  max-width: 260px;
  font-size: 12.5px;
  color: var(--text-2);
}


/* Ввод кода в строке заказа: поле и кнопка одной деталью, а не двумя
   прямоугольниками рядом. Сканер печатает в поле, палец жмёт кнопку. */
.code-add { display: inline-flex; margin: 0; }
.code-add input {
  border-radius: var(--radius-sm) 0 0 var(--radius-sm);
  min-width: 150px;
}
.code-add button {
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  margin-left: -1px;
}
.code-add button:hover { border-color: var(--accent); }

/* Действия у вручённого заказа. Документ — тихой ссылкой, отметка —
   кнопкой: одно смотрят, другое нажимают, и путать их дорого. */
.code-done { display: inline-flex; align-items: center; gap: 10px; }
.code-done a { font-size: 13px; }

/* Период уходит вправо от вкладок: одно отвечает «что смотрим»,
   другое «за какой срок», и в одной строке они не спорят. */
.sheet-head .period-pick { margin-left: auto; }

/* Таблица заказов с кодами. Ширины заданы явно: без них номер отправления
   переносится посреди цифр — «33333 333-0 003-1», — а кнопку действия
   обрезает край. И то и другое делает строку нечитаемой. */
.codes-table td:first-child .code,
.codes-table td:first-child a { white-space: nowrap; }
/* Ширины не назначены в процентах, а выведены из содержимого.

   Процент — обещание, которое стол не держит: треть ширины уходила кодам
   даже там, где в ячейке стояло «нет кода», название товара сжималось
   до двух слов, а между кодом и кнопкой зияла пустота на пол-экрана.

   Здесь все служебные колонки просят «сколько нужно» (`width: 1%` плюс
   запрет переноса — так браузер даёт им ровно по содержимому), а весь
   остаток забирает товар. Меняется ширина окна — меняется и он один,
   остальное стоит на месте. */
.codes-table th, .codes-table td { white-space: nowrap; }
.codes-table th:nth-child(1), .codes-table td:nth-child(1),
.codes-table th:nth-child(3), .codes-table td:nth-child(3),
.codes-table th:nth-child(4), .codes-table td:nth-child(4),
.codes-table th:nth-child(5), .codes-table td:nth-child(5),
.codes-table th:last-child, .codes-table td:last-child { width: 1%; }

/* Товар забирает весь остаток и переносится по словам. */
.codes-table th:nth-child(2), .codes-table td:nth-child(2) {
  width: 100%; min-width: 220px; white-space: normal;
}
/* Коды длинные по природе: своя полоса с переносом внутри неё. */
.codes-table th:nth-child(6), .codes-table td:nth-child(6) {
  min-width: 190px; max-width: 340px; white-space: normal;
}
.codes-code {
  font-family: var(--mono); font-size: 11.5px; line-height: 1.45;
  color: var(--text-2); word-break: break-all; margin-top: 3px;
}

/* Оговорки к расчёту: плашками в один ряд, текст по нажатию.
   Развёрнутыми они занимали половину экрана, и таблица начиналась ниже
   сгиба — а читать их каждый раз не нужно. Знать, что они есть, нужно
   всегда, поэтому ряд стоит на виду, а не прячется под «подробнее». */
.notes-row {
  display: flex; flex-wrap: wrap; gap: 6px;
  margin: 12px 0;
}
.note-chip {
  background: color-mix(in srgb, var(--amber) 12%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--amber) 32%, var(--border));
  border-radius: var(--radius);
  font-size: 12px;
  max-width: 100%;
}
.note-chip > summary {
  cursor: pointer; padding: 5px 10px; list-style: none;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.note-chip > summary::-webkit-details-marker { display: none; }
.note-chip > summary::before {
  content: '!'; display: inline-grid; place-items: center;
  width: 14px; height: 14px; margin-right: 6px; vertical-align: -2px;
  border-radius: 50%; font-size: 10px; font-weight: 700;
  background: color-mix(in srgb, var(--amber) 55%, transparent);
  color: var(--bg);
}
/* Раскрытая плашка занимает свою строку целиком: текст в три предложения
   внутри узкой колонки читается хуже, чем не читается вовсе. */
.note-chip[open] { flex-basis: 100%; }
.note-chip[open] > summary { font-weight: 600; white-space: normal; }
.note-chip-body {
  padding: 0 10px 9px; font-size: 13px; line-height: 1.45;
}

/* Свёрнутый ряд действий: то, что нужно раз в месяц, не должно занимать
   полосу всегда. Загрузка себестоимости — два шаблона, файл, два поля
   и три строки подсказки — держала верх страницы, и таблица начиналась
   ниже сгиба. */
.sheet-fold { margin: 8px 0; }
.sheet-fold > summary {
  cursor: pointer; list-style: none; display: inline-block;
  padding: 6px 11px; font-size: 13px; color: var(--text-2);
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-sm); user-select: none;
}
.sheet-fold > summary::-webkit-details-marker { display: none; }
.sheet-fold > summary::after { content: ' ▾'; color: var(--muted); }
.sheet-fold[open] > summary::after { content: ' ▴'; }
.sheet-fold > summary:hover { color: var(--text); border-color: var(--accent); }
.sheet-fold[open] > summary { border-bottom-left-radius: 0; border-bottom-right-radius: 0; }

/* Переключатель вида таблицы. Не пилюля в шапке: рядом с выбором периода
   он читался как ещё один фильтр — та же форма, тот же размер, — и понять,
   что он меняет саму таблицу, было нельзя.

   Прячется атрибутом, поэтому своё правило [hidden] обязательно: `display`
   отсюда сильнее браузерного, и без него переключатель остался бы
   на экране кнопкой, которая без скриптов ничего не делает. */
.view-switch[hidden] { display: none; }

.view-switch {
  display: flex; align-items: stretch; gap: 8px;
  margin: 14px 0 4px;
}

/* Те же кнопки, что «В очередь» и «Выгрузить»: акцентный фон, белые буквы.
   Своя палитра для переключателя означала бы третий вид кнопки на одной
   странице — глаз начинает искать смысл там, где его нет. */
.view-switch button {
  display: grid; gap: 1px; text-align: left;
  padding: 8px 16px; font: inherit; cursor: pointer;
  background: var(--accent); color: #fff; border: 1px solid var(--accent);
  border-radius: var(--radius-sm);
}
.view-switch button b { font-size: 14px; font-weight: 600; }
.view-switch button small { font-size: 11.5px; color: color-mix(in srgb, #fff 72%, transparent); }
.view-switch button:hover { filter: brightness(1.08); }

/* Невыбранный — тот же цвет, но приглушённый. Две одинаково яркие кнопки
   не отвечают на вопрос «а что сейчас показано», а разные цвета читались бы
   как разные по смыслу действия. */
.view-switch button:not(.active) {
  background: color-mix(in srgb, var(--accent) 26%, transparent);
  border-color: color-mix(in srgb, var(--accent) 40%, transparent);
  color: color-mix(in srgb, #fff 78%, transparent);
}
.view-switch button:not(.active) small { color: color-mix(in srgb, #fff 55%, transparent); }

/* Оценка в таблице фактов. Налог по товарам разносится приблизительно —
   он платится с общей базы, — и он же решал знак итога: у 14917 до налогов
   оставался 201 ₽, а «чистая» уходила в минус. Объявлять товар убыточным
   на основании оценки нельзя, поэтому она отличается на вид от факта. */
.sheet td.guess { color: var(--text-2); font-style: italic; }
.sheet th.guess, .sheet td.guess { opacity: .88; }

/* Галочка в полосе действий: подпись и квадратик на одной линии.
   Без этого квадратик садится на базовую линию текста и уезжает вниз
   относительно соседних полей — в ряду из четырёх элементов это видно. */
.sheet-actions label.check {
  display: inline-flex; align-items: center; gap: 5px;
  white-space: nowrap; cursor: pointer;
}
.sheet-actions label.check input { margin: 0; cursor: pointer; }

/* Плитки-итоги наверху раздела. Человек заходит за ответом, а не за таблицей:
   таблица нужна, чтобы этот ответ поправить. Поэтому число крупно и первым,
   а подпись под ним говорит, от чего оно считано, — «11 %» без базы
   не значит ничего. */
.hero-row {
  display: flex; flex-wrap: wrap; gap: 10px;
  margin: 14px 0;
}
.hero-tile {
  flex: 1 1 190px; min-width: 170px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 12px 15px;
}
.hero-tile.strong { border-color: var(--accent); }
.hero-label {
  font-size: 11.5px; text-transform: uppercase; letter-spacing: .04em;
  color: var(--muted);
}
.hero-value { font-size: 25px; font-weight: 600; margin: 3px 0 2px; }
.hero-note { font-size: 12px; color: var(--text-2); }
.hero-note a { color: var(--accent); }

/* Итоговая строка таблицы: по ней сверяют сумму глазами, и она обязана
   отличаться от обычных — иначе её прочитают как ещё одно слагаемое. */
.sheet tr.row-total > td {
  border-top: 2px solid var(--border);
  background: color-mix(in srgb, var(--surface-2) 60%, transparent);
}
.pill.muted { color: var(--muted); border-color: var(--border-soft); }

/* Выбор вида расхода карточками, а не выпадающим списком. В списке названия
   видны по одному, и человек не понимает, из чего выбирает; здесь все четыре
   на виду с подписью, что к чему относится. */
.kind-pick { display: flex; flex-wrap: wrap; gap: 8px; margin: 4px 0 14px; }
.kind-card {
  flex: 1 1 180px; display: grid; gap: 2px; cursor: pointer;
  padding: 10px 13px; border-radius: var(--radius);
  background: var(--surface); border: 1px solid var(--border);
}
.kind-card input { position: absolute; opacity: 0; pointer-events: none; }
.kind-card .kind-title { font-size: 13.5px; font-weight: 500; }
.kind-card .kind-hint { font-size: 11.5px; color: var(--muted); }
.kind-card:hover { border-color: var(--accent); }
.kind-card.on {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 14%, var(--surface));
}
/* Скрытое поле формы обязано прятаться: `display` из `.settings label`
   сильнее браузерного правила для `hidden`, и без своей строки поле
   осталось бы на экране. Панель на этом уже спотыкалась дважды. */
.settings label[hidden] { display: none; }

/* --- Пульс ---------------------------------------------------------------

   Плитки здесь не витрина, а навигация: под каждой лежит свой разбор,
   и выбранная плитка обязана читаться как выбранная — иначе человек
   не поймёт, к какому числу относится таблица внизу. Отсюда сплошная
   рамка акцентом и подсветка фона: полутон на тёмной теме не виден
   на глаз, а именно он тут несёт смысл. */
.pulse-goal {
  display: flex; flex-wrap: wrap; align-items: flex-end; gap: 12px;
  padding: 12px 15px; margin: 14px 0;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius);
}
.pulse-goal-head { flex: 1 1 100%; display: flex; gap: 10px; align-items: baseline; }
.pulse-goal-head .muted { font-size: 12px; }
.pulse-goal label { display: grid; gap: 5px; font-size: 12px; color: var(--text-2); }
/* Кнопка ровно по нижнему краю полей: `align-items: flex-end` у формы
   выравнивает по низу, но у кнопки своя высота, и без этого она садилась
   на пару пикселей ниже строки ввода. */
.pulse-goal button { padding: 9px 16px; border-radius: var(--radius-sm); }
/* Знак процента внутри поля, а не подписью рядом: подпись читается как
   часть следующего поля, и «11» уходило в потолок рекламы. */
.pulse-field { position: relative; display: inline-block; }
/* Поля цели выглядели голыми: прямоугольник от браузера посреди панели
   со скруглёнными углами. Настройки свои поля оформляют, но форма цели
   живёт не в `.settings` и до этих правил не доставала. Повторяем их
   здесь, а не расширяем те: правило `.settings input` описывает раздел
   настроек, и цеплять к нему чужую форму значит связать два места,
   которые меняются порознь. */
.pulse-field input {
  width: 108px; padding: 8px 26px 8px 10px; text-align: right;
  font-size: 15px; font-variant-numeric: tabular-nums;
  background: var(--surface-2); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  font-family: inherit;
}
.pulse-field input:hover { border-color: var(--border-strong, var(--muted)); }
.pulse-field input:focus {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent);
}
.pulse-field input::placeholder { color: var(--muted); }
.pulse-field i {
  position: absolute; right: 10px; top: 50%; transform: translateY(-50%);
  font-style: normal; color: var(--muted); font-size: 13px; pointer-events: none;
}

.pulse-tiles {
  display: grid; gap: 10px; margin: 14px 0 18px;
  grid-template-columns: repeat(auto-fit, minmax(168px, 1fr));
}
.pulse-tile {
  display: flex; flex-direction: column; gap: 3px;
  padding: 12px 15px 13px; text-decoration: none; color: inherit;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); transition: border-color .12s, background .12s;
}
.pulse-tile:hover { border-color: color-mix(in srgb, var(--accent) 55%, var(--border)); }
.pulse-tile.on {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, var(--surface));
}
.pulse-tile .hero-value { margin: 1px 0 0; font-size: 23px; }
.pulse-tile .hero-value.minus { color: var(--red); }
.pulse-tile .hero-note { font-size: 11.5px; }
.pulse-plan { font-size: 12.5px; font-weight: 500; }
.pulse-plan.ok { color: var(--green); }
.pulse-plan.bad { color: var(--amber); }
.pulse-plan.muted { color: var(--muted); font-weight: 400; }

/* Полоска «сколько факта от плана». Одна формула на цель и на потолок:
   у цели полная полоса — это хорошо, у потолка — уже перебор, и цвет
   берётся из вердикта, а не из ширины. */
.pulse-bar {
  display: block; height: 4px; margin: 5px 0 2px;
  background: var(--surface-2); border-radius: 3px; overflow: hidden;
}
.pulse-bar i { display: block; height: 100%; background: var(--muted); }
.pulse-bar i.ok { background: var(--green); }
.pulse-bar i.bad { background: var(--amber); }

.pulse-panel { display: grid; gap: 14px; margin-bottom: 18px; }
/* Скрытая панель обязана прятаться: `display: grid` выше сильнее
   браузерного правила для `hidden`. Панель на этом спотыкалась трижды —
   у `.segmented`, у поля формы и у переключателя вида. */
.pulse-panel[hidden] { display: none; }
.pulse-lead { margin: 0; font-size: 14px; line-height: 1.55; color: var(--text-2); }
.pulse-lead b { color: var(--text); }
.pulse-go { display: flex; flex-wrap: wrap; gap: 8px; }
.stack-legend {
  display: flex; flex-wrap: wrap; gap: 12px; margin-top: 8px;
  font-size: 11.5px; color: var(--muted);
}

/* Переключатель разделов «Пульса»: цель и контроль. Стоит отдельной строкой
   под шапкой, а не рядом с периодом справа: период — уточнение внутри
   раздела, а это выбор самого раздела, и путать их нельзя. */
.pulse-views { margin: 4px 0 2px; }
.pulse-views-hint { margin: 0 0 14px; }
/* Строка таблицы, отвечающая заданной цели. Подсветка слабая: она метит
   строку, а не кричит — рядом стоит пилюля «ваша цель», и двух сигналов
   одной силы на одну строку много. */
.sheet tr.on > td { background: color-mix(in srgb, var(--green) 9%, transparent); }

/* ---------------------------------------------------- «Пульс»: разрыв до цели
   Первый экран раздела. Раньше подзаголовок обещал показать, что стоит между
   прибылью и целью, а сам разрыв лежал внутри прозы под таблицей — читатель
   складывал его в уме из трёх чисел в разных абзацах. */
.gap {
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 16px 18px;
  margin: 0 0 16px;
  background: var(--surface);
}
.gap-head { display: flex; align-items: baseline; gap: 18px; flex-wrap: wrap; }
.gap-head b { font-size: 22px; font-variant-numeric: tabular-nums; }
.gap-label { display: block; font-size: 12px; color: var(--muted); }
.gap-arrow { color: var(--muted); font-size: 20px; }
.gap-diff { margin-left: auto; text-align: right; }
.gap-note { font-size: 12px; margin-top: 2px; }
/* Полоса всегда подписана числом рядом: значение, закодированное одной
   шириной, нечитаемо для того, кто смотрит бегло, и недоступно тому,
   кто плохо различает цвет. */
.gap-bar {
  display: block; height: 8px; margin: 12px 0 4px;
  background: var(--border-soft); border-radius: 4px; overflow: hidden;
}
.gap-bar > i { display: block; height: 100%; background: var(--green); border-radius: 4px; }
.gap-bar.small { height: 6px; margin: 0; width: 120px; }
.gap-bar.small > i { background: var(--muted); }
.gap-progress { font-size: 12px; color: var(--muted); }
.gap-levers { margin-top: 14px; border-top: 1px solid var(--border-soft); padding-top: 12px; }
.gap-lever-title { font-size: 12px; color: var(--muted); margin-bottom: 6px; }
.gap-lever {
  display: flex; align-items: center; gap: 12px;
  padding: 4px 0; font-size: 13px; flex-wrap: wrap;
}
.gap-lever-name { min-width: 130px; }
.gap-lever-from { font-variant-numeric: tabular-nums; }
.gap-lever-mult { color: var(--muted); font-variant-numeric: tabular-nums; }
.gap-verdict { margin-top: 8px; font-size: 13px; }
/* ------------------------------------------------ «Пульс»: шрифт раздела
   Inter вместо системного. Числа в нём одной ширины и без моноширинной
   «пишущей машинки», которой раньше набирались все суммы в таблицах: цифры
   в Consolas читались как код, а не как деньги. Файлы лежат в статике —
   панель не должна ходить за шрифтом в чужую сеть на каждой странице. */
@font-face {
  font-family: "Inter"; font-style: normal; font-weight: 100 900; font-display: swap;
  src: url(/static/fonts/inter-cyrillic-ext.woff2) format("woff2");
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
@font-face {
  font-family: "Inter"; font-style: normal; font-weight: 100 900; font-display: swap;
  src: url(/static/fonts/inter-cyrillic.woff2) format("woff2");
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: "Inter"; font-style: normal; font-weight: 100 900; font-display: swap;
  src: url(/static/fonts/inter-latin-ext.woff2) format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Inter"; font-style: normal; font-weight: 100 900; font-display: swap;
  src: url(/static/fonts/inter-latin.woff2) format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* Только на странице раздела: другие разделы свёрстаны под системный шрифт,
   и менять им ширину букв разом — значит проверять шестьдесят экранов. */
.pulse-page { font-family: "Inter", -apple-system, "Segoe UI", Roboto, sans-serif; }
.pulse-page td.num, .pulse-page th.num, .pulse-page .num {
  font-family: inherit; font-variant-numeric: tabular-nums;
}
.pulse-page td.num { font-weight: 500; }
.pulse-page .hero-value, .pulse-page .goal-stat > b, .pulse-page .gap-line b {
  letter-spacing: -0.01em;
}

/* ------------------------------------------------ «Пульс»: путь к заказу
   Пять шагов одной полосой: где стою, что дальше. Первые три — на этой
   странице, два последних — в «Закупке». Сделанный шаг закрашен зелёным,
   текущий обведён акцентом; остальное — ссылки, а не украшение. */
.path {
  list-style: none; margin: 0 0 14px; padding: 0;
  display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 8px;
}
.path a {
  display: grid; grid-template-columns: auto 1fr; column-gap: 10px; align-items: center;
  padding: 9px 12px; min-width: 0;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-sm);
  color: var(--text-2); text-decoration: none;
}
.path a:hover { border-color: var(--accent); color: var(--text); }
.path i {
  grid-row: 1 / span 2; width: 26px; height: 26px; border-radius: 50%;
  display: grid; place-items: center; font-style: normal; font-size: 12px; font-weight: 600;
  border: 1px solid var(--border); color: var(--muted);
}
.path span { font-size: 13px; font-weight: 600; color: var(--text); white-space: nowrap; }
.path small { font-size: 11.5px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.path li.done i { background: var(--green); border-color: var(--green); color: var(--ink); }
.path li.now a { border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 16%, transparent); }
.path li.now i { background: var(--accent); border-color: var(--accent); color: #fff; }
@media (max-width: 980px) { .path { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* Шаги пути — карточками с рамкой. Без неё цель, сценарии и разборы
   сливались в одно полотно, и граница между «вписал цель» и «выбрал
   путь» проходила по пустой строке, которую не видно. */
.pulse-page .goal-card, .pulse-page .pulse-key, .pulse-page details.card.fold {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 16px 18px; margin: 0 0 12px;
}
.pulse-page details.card.fold > summary .muted { font-size: 12.5px; }
.pulse-page .goal-title { margin-bottom: 10px; }
/* Третий рычаг — тихим абзацем с полосой слева: объяснение, а не тревога. */
.lever-3 { margin-top: 14px; padding: 10px 14px; border-left: 3px solid var(--border); }

/* Мост: таблица слагаемых на всю ширину. Название с пояснением занимает
   сколько нужно, числа и кнопки — сколько есть; на узком экране пояснения
   уходят, остаются имя, число и кнопка. */
.bridge { width: 100%; }
.bridge td:first-child { min-width: 240px; }
.bridge td:first-child .fact { max-width: 64ch; }
.bridge td.num, .bridge td:last-child { white-space: nowrap; }
.bridge td.num.ok { color: var(--green); }
.bridge td.num.danger { color: var(--red); }
@media (max-width: 760px) {
  .bridge td:first-child { min-width: 150px; }
  .bridge .fact { display: none; }
}
.pulse-page .hero-value.danger { color: var(--red); }
.pulse-page .hero-value.ok { color: var(--green); }

/* Карточка цели: форма и разрыв под одной крышей. Форма раньше сама была
   карточкой — здесь она теряет рамку, иначе рамка в рамке. */
.goal-card .pulse-goal { background: none; border: 0; padding: 0; margin: 0 0 10px; }
.goal-card .pulse-goal-head { display: none; }
.goal-card .pulse-goal-hint { margin: 0 0 12px; }
.gap-line {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 14px;
  padding-top: 12px; border-top: 1px solid var(--border-soft);
}
.gap-line b { font-size: 21px; font-variant-numeric: tabular-nums; }
.gap-line .gap-label { display: inline; margin-right: 4px; }
.gap-line .gap-diff { margin-left: auto; }
.goal-card .gap-levers { margin-top: 12px; }

/* Подбор — продолжение таблицы сценариев, отделён линией, а не карточкой. */
/* Строка под переключателем путей: когда подобран, пересобрать, убрать. */
.plan-line { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin: 6px 0 4px; }
.plan-line .muted { font-size: 12.5px; }
.plan-line form { margin: 0; }
.pick-block { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--border-soft); }
.pick-title { font-weight: 600; margin-bottom: 2px; }

/* Подсказка под полем цели: обычным текстом, а не по наведению — разницу
   между «заработать» и «останется» должны прочитать все. */
.pulse-goal-hint { font-size: 13px; margin: -6px 0 16px; max-width: 70ch; }

/* Полоса настройки, раскрывающаяся в список шагов. Кнопка-призыв внутри
   summary, а не отдельной ссылкой: полоса и так занимает верх каждой
   страницы, и второй элемент рядом с ней спорил бы с содержимым. */
/* Раскладка переехала с самой полосы на её summary: полоса была ссылкой
   и раскладывала заголовок с подписью столбцом, а теперь она — details,
   и столбцом обязано ложиться содержимое summary, иначе призыв уезжает
   под подпись, а список шагов встаёт рядом с ней. */
.setup-strip > summary {
  cursor: pointer; list-style: none;
  display: grid; grid-template-columns: 1fr auto; gap: 2px 12px;
  align-items: center;
}
.setup-strip > summary > .setup-strip-title { grid-column: 1; }
.setup-strip > summary > .muted { grid-column: 1; }
.setup-strip > summary > .setup-strip-cta { grid-column: 2; grid-row: 1 / span 2; }
.setup-strip > summary::-webkit-details-marker { display: none; }
.setup-strip-cta {
  margin-left: auto; white-space: nowrap;
  color: var(--accent); font-size: 13px;
}
.setup-strip[open] .setup-strip-cta::after { content: " ▴"; }
.setup-strip:not([open]) .setup-strip-cta::after { content: " ▾"; }
.setup-steps { margin: 10px 0 2px; padding: 0 0 0 4px; list-style: none; }
.setup-step a {
  display: flex; align-items: center; gap: 10px;
  padding: 6px 2px; font-size: 13px; text-decoration: none; color: var(--text);
  border-bottom: 1px solid var(--border-soft);
}
.setup-step:last-child a { border-bottom: 0; }
.setup-step-mark { width: 16px; text-align: center; color: var(--muted); }
.setup-step.done .setup-step-mark { color: var(--green); }
.setup-step-state { margin-left: auto; font-size: 12px; }

/* Таблица сценариев «Пульса»: якоря против гипотез.
   «Сейчас» и «ваша цель» — реальные точки, остальные строки предположения.
   Без различия строка «25 коп.» подавалась наравне с фактом и читалась
   как совет, хотя наценка под неё товаром недостижима. */
.sheet tr.anchor > td { background: color-mix(in srgb, var(--muted) 8%, transparent); }
.sheet tr.after-anchors > td { border-bottom: 2px solid var(--border); }
/* Недостижимые сценарии остаются в таблице — убрать их значило бы соврать,
   что верхних вариантов нет вовсе, — но подаются тише. */
.sheet tr.faded > td { opacity: .55; }
.th-base { display: block; font-weight: 400; font-size: 11px; margin-top: 2px; }

/* Карточка при наведении на точку графика. Системная подсказка браузера
   выводит всё одной строкой, ждёт секунду и не выделяет главное —
   на графике плана против факта на точку приходится семь чисел. */
.chart-tip {
  position: fixed; z-index: 60; pointer-events: none;
  min-width: 210px; padding: 10px 12px;
  background: var(--surface); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  box-shadow: 0 10px 30px rgb(0 0 0 / .28);
  font-size: 12.5px;
}
.chart-tip-title {
  font-weight: 600; margin-bottom: 6px; padding-bottom: 6px;
  border-bottom: 1px solid var(--border-soft);
}
.chart-tip-row {
  display: flex; justify-content: space-between; gap: 16px; padding: 2px 0;
}
.chart-tip-row span { color: var(--muted); }
.chart-tip-row b { font-variant-numeric: tabular-nums; }

/* Форма подбора ассортимента под цель — одной строкой: сценарий,
   бренды, кнопка, ответ. Столбиком она занимала пол-экрана и заставляла
   скроллить между «что выбрал» и «что нажал», хотя это один жест из трёх
   движений. На узком экране строка переносится сама. */
.pick-form {
  display: flex; align-items: flex-end; flex-wrap: wrap;
  gap: 12px; margin-top: 10px;
}
.pick-form .field { display: grid; gap: 4px; }
.pick-form .field-title { font-size: 12px; line-height: 1.4; min-height: 17px; }
.pick-form select[name="rung"] { max-width: 340px; }
.pick-form > .muted { flex-basis: 100%; margin: 0; }
/* Счёт товаров у бренда — к правому краю строки: так столбик чисел
   читается сам, без выравнивания глазами по разной длине названий. */
.pick-form .drop-list label > .muted { margin-left: auto; padding-left: 18px; }
/* Числа в таблицах не переносятся: «1 966 469» в две строки читается
   как два разных числа, а колонка от переноса не становится уже —
   она просто становится выше. */
.sheet td.num, .sheet th.num { white-space: nowrap; }

/* Блок «Что нужно, чтобы дойти до цели». Он прошёл три вида: три абзаца
   прозы с пятнадцатью числами внутри (не читают), восемь строк столбиком
   (читают, но четыреста пикселей на восемь чисел), и вот этот — ряды
   по четыре. Ряд читается одним движением глаз, а весь разбор занимает
   столько же места, сколько занимал один прежний абзац. */
.goal-title { color: var(--green); font-weight: 600; }
.goal-aim { margin-top: 8px; font-size: 17px; line-height: 1.4; }
.goal-aim b { color: var(--green); font-size: 21px; font-weight: 600; }
.goal-aim-note { display: block; color: var(--muted); font-size: 12.5px; }

.goal-row { margin-top: 14px; }
.goal-row-name {
  font-size: 12px; text-transform: uppercase; letter-spacing: .04em;
  color: var(--text-2); margin-bottom: 6px;
}
.goal-row-name .muted { text-transform: none; letter-spacing: normal; }

/* Значение сверху, подпись под ним: так столбик чисел выравнивается сам,
   и «×5.71» рядом с «817 280 ₽» сравниваются взглядом, а не счётом.
   Снизу воздух: без него ряд плиток прилипал к шапке таблицы сценариев. */
.goal-stats { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 16px; }
.goal-stat {
  flex: 1 1 170px; min-width: 150px; padding: 9px 12px;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface-2);
}
.goal-stat > b {
  display: block; font-size: 16px; font-weight: 600;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.goal-stat > span { display: block; font-size: 11.5px; color: var(--muted); line-height: 1.35; }

/* Переключатель путей — своей строкой. Вплотную к вкладкам раздела он
   читался их продолжением: «Накладные · Zvezda · Schleich» выглядело одним
   рядом, хотя это разные вещи — где я в панели и какой путь смотрю. */
.plan-switch {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 12px;
}
.plan-switch .chip {
  padding: 4px 10px; font-size: 12.5px; border-radius: 999px;
  border: 1px solid var(--border); color: var(--text-2); text-decoration: none;
}
.plan-switch .chip.on { border-color: var(--accent); color: var(--text); }

/* «Убрать путь» — тихой строкой под переключателем. Кнопка того же веса,
   что «Сформировать заказ», предлагала бы удаление наравне с работой. */
.plan-line .plan-drop { margin: 0; }

/* Дорожная карта пути: сколько уже куплено и сколько идти. Полосой,
   а не числом: доля читается взглядом, а число надо сравнивать с другим. */
.road {
  margin-top: 14px; padding: 12px 14px;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
}
.road-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 12px; flex-wrap: wrap; margin-bottom: 8px;
}
.road-bar {
  height: 8px; border-radius: 999px; overflow: hidden; margin-bottom: 8px;
  background: color-mix(in srgb, var(--muted) 22%, transparent);
}
.road-bar > i { display: block; height: 100%; background: var(--green); }

/* Имя товара в одну строку. В две-три строки таблица растягивалась так,
   что на экран помещалось четыре позиции из пятидесяти, а решение принимают,
   пробегая список глазами. Полное имя — по наведению. */
/* Колонке товара — не меньше трети таблицы. На широком экране таблица
   растягивалась на всю карточку, лишнее место забирали узкие колонки
   с выпадающим списком, а имя товара ужималось до «Эврикус Насто…». */
#plan-form .sheet { width: 100%; }
/* Флажки — узкая колонка фиксированной ширины: у автоматической раскладки
   она растягивалась вместе с остальными и отодвигала товар от края. */
#plan-form .sheet .pick-col { width: 34px; min-width: 34px; max-width: 34px; padding-left: 8px; padding-right: 0; text-align: center; }
/* Наличие — три состояния, каждое своим цветом; углы скруглены как у всех
   полей панели, слово по центру. `text-align-last` — единственное, что
   центрирует выбранное значение в закрытом списке: обычный `text-align`
   у `select` браузер применяет к пунктам, а не к самому полю. */
select.cell-input.supply {
  width: auto; min-width: 96px; border-radius: var(--radius-sm);
  padding: 5px 24px 5px 12px; font-weight: 600;
  text-align: center; text-align-last: center;
}
select.cell-input.supply[data-supply="yes"] { color: var(--green); border-color: color-mix(in srgb, var(--green) 45%, var(--border)); }
select.cell-input.supply[data-supply="out"] { color: var(--amber); border-color: color-mix(in srgb, var(--amber) 45%, var(--border)); }
select.cell-input.supply[data-supply="gone"] { color: var(--red); border-color: color-mix(in srgb, var(--red) 45%, var(--border)); }
/* `max-width: 0` — приём для таблицы с автоматической раскладкой: без него
   колонка тянется под самое длинное имя без переносов, и остальные
   столбцы уезжают за край. Ширина тогда берётся из процента, а имя
   обрезается многоточием внутри. */
.sheet td:has(> .plan-name) { width: 36%; max-width: 0; min-width: 240px; }
.sheet td:has(> .plan-name) .muted { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.plan-name {
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Разбор места в очереди — мелкой строкой под товаром. Своими колонками
   он выгонял таблицу за край экрана, а читают его про конкретный товар,
   а не столбиком по всему списку. */
.plan-why { font-size: 11.5px; }

/* Панель работы над планом: чем отобрать слева, что сделать — справа. */
.plan-tools {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap; margin-top: 14px;
}
.plan-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }

/* Кнопка и то, что происходит после неё, — в одной строке: человек смотрит
   туда, куда только что нажал, а не ищет ответ этажом ниже. */
.pick-go { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.pick-state { display: inline-flex; align-items: center; gap: 10px; font-size: 13px; }
/* Своё правило для спрятанного: `display` выше перебивает атрибут `hidden`,
   и пустое место индикатора висело бы на экране до первого запуска. */
.pick-state[hidden] { display: none; }
.pick-state.waiting { color: var(--muted); }
.pick-state.waiting::before {
  content: ''; width: 13px; height: 13px; border-radius: 50%;
  border: 2px solid var(--border); border-top-color: var(--accent);
  animation: pick-spin .8s linear infinite;
}
.pick-state.warn { color: var(--amber); }
@keyframes pick-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .pick-state.waiting::before { animation: none; }
}

/* Таблица окон и справка по дню — в один ряд. Справка узкая нарочно:
   в ней предложение, а не отчёт, и растянутая на пол-экрана строка
   читается хуже колонки. На узком экране обе уходят одна под другую —
   таблица первой, потому что она есть всегда, а справка ждёт наведения. */
/* Колонки считаются от ширины самого места, а не окна: панель агента
   забирает половину экрана, окно при этом остаётся широким, и медиазапрос
   оставлял бы две колонки там, где на них уже нет ширины. Контейнер и сетка —
   разные узлы нарочно: элемент не отвечает на запрос о собственной ширине. */
.under-chart { container-type: inline-size; }
.under-chart-row {
  display: grid; gap: 16px; align-items: start;
  grid-template-columns: minmax(0, 1fr);
}
@container (min-width: 860px) {
  .under-chart-row { grid-template-columns: minmax(0, 1fr) minmax(240px, 320px); }
}
/* Таблица прокручивается внутри своей колонки — обёрткой, а не `display:
   block` на самой таблице. Блочная таблица прокрутку получает, но ширину
   держит по содержимому: колонка растянута, таблица кончается на середине,
   и между ней и карточкой зияет пустое поле. Обёртке же таблица подчиняется
   как обычная — на всю ширину, а прокручивается, когда места мало. */
.under-chart-row > .sheet-wrap { min-width: 0; }
.under-chart-row .sheet { margin-bottom: 0; }

.day-card {
  padding: 14px 16px;
  background: var(--surface-soft, var(--surface));
  border: 1px solid var(--border); border-radius: var(--radius-sm);
}
.day-card-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px; margin-bottom: 8px; padding-bottom: 8px;
  border-bottom: 1px solid var(--border-soft);
}
.day-card-row {
  display: flex; justify-content: space-between; gap: 12px; padding: 3px 0;
  font-size: 12.5px;
}
.day-card-row span { color: var(--muted); }
.day-card-row b { font-variant-numeric: tabular-nums; white-space: nowrap; }
.day-card-note {
  margin: 10px 0 0; font-size: 12.5px; line-height: 1.5; color: var(--muted);
}
.day-card-idle .day-card-note { margin-top: 0; }

/* Заголовок карточки с переключателем справа: ряд один, вопросы разные,
   и выбор ряда обязан стоять там же, где его название. */
.card-head-row {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap; margin-bottom: 6px;
}
.segmented.small a { padding: 4px 10px; font-size: 12px; }

/* «Взять сценарий» в строке таблицы. Отдельно от `.link-button`: у того
   красный ховер — он метит разрушительное действие вроде удаления правила,
   а здесь всего лишь выбор цели, и красным его красить незачем. */
.rung-take { display: inline; }
.rung-take button {
  background: none; border: 1px solid var(--border); border-radius: 999px;
  padding: 1px 9px; margin-left: 6px; cursor: pointer;
  color: var(--muted); font: inherit; font-size: 11.5px;
}
.rung-take button:hover { color: var(--accent); border-color: var(--accent); }

/* «Что чинить»: три списка товаров на «Контроле». Стоят выше разборов —
   разбор объясняет, куда делись деньги, а список говорит, на ком именно
   они теряются, и с этим можно пойти и что-то сделать. */
.troubles {
  display: grid; gap: 12px; margin: 4px 0 18px;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
}
.trouble-card {
  padding: 12px 14px; background: var(--surface);
  border: 1px solid var(--border); border-radius: var(--radius);
}
.trouble-head { display: flex; flex-direction: column; gap: 2px; margin-bottom: 8px; }
.trouble-head .muted { font-size: 11.5px; }
.trouble-list { margin: 0; padding: 0; list-style: none; }
.trouble-list li {
  display: grid; grid-template-columns: 1fr auto; gap: 2px 10px;
  padding: 6px 0; border-bottom: 1px solid var(--border-soft);
}
.trouble-list li:last-child { border-bottom: 0; }
.trouble-list a { font-size: 13px; text-decoration: none; color: var(--text); }
.trouble-list a:hover { color: var(--accent); }
.trouble-note { grid-column: 1; font-size: 11.5px; }
.trouble-list b { grid-column: 2; grid-row: 1 / span 2; align-self: center; font-variant-numeric: tabular-nums; }

/* Недельная таблица: план и факт разведены группами, полоски в ячейках.
   Колонки плана и факта шли вперемешку, и глаз спотыкался на каждой
   строке; отставание считали в уме — работа, которую обязана делать
   система. */
.sheet.weeks th.group, .sheet.weeks td.group { border-right: 2px solid var(--border); }
.sheet.weeks thead th { vertical-align: bottom; }
/* Полоска шириной по модулю значения: масштаб читается без чтения цифр.
   Фоном, а не отдельным элементом, — иначе она спорит с числом за место. */
.sheet td.bar { position: relative; }
.sheet td.bar::before {
  content: ""; position: absolute; right: 0; top: 3px; bottom: 3px;
  width: var(--fill, 0); border-radius: 3px;
  background: color-mix(in srgb, var(--muted) 20%, transparent);
}
.sheet td.bar.danger::before { background: color-mix(in srgb, var(--red) 20%, transparent); }
.sheet td.bar > * { position: relative; }
/* Будущие недели приглушены: взгляд должен идти к строкам с фактом. */
.sheet tr.ahead > td { opacity: .45; }
/* Широкие таблицы прокручиваются внутри себя, а не тянут страницу. */
.sheet-wrap { overflow-x: auto; }

/* Подсказка у заголовка колонки. Определение обязано стоять там, где
   на колонку смотрят, а не абзацем под таблицей. Открывается и с клавиатуры:
   подсказка только по наведению недоступна тем, кто ходит табом. */
/* Обёртка носит своё имя не для красоты: под именем `hint` в панели уже
   жили абзацы-пояснения — под кнопкой «Собрать заказ» в «Закупке», например.
   `display: inline-block` сжимал такой абзац в узкий столбик, и правка
   подсказки молча ломала соседний раздел. */
.term-hint { position: relative; display: inline-block; }
/* Знак заметный: в пятнадцать пикселей серым по серому его не находили
   даже те, кто знал, что он есть. */
.hint-mark {
  width: 18px; height: 18px; padding: 0; line-height: 16px; margin-left: 2px;
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 55%, var(--border)); border-radius: 50%;
  color: var(--accent); font-size: 12px; font-weight: 600; cursor: help;
  vertical-align: -4px; display: inline-grid; place-items: center; line-height: 1;
}
.hint-mark:hover, .hint-mark:focus-visible { background: var(--accent); color: #fff; border-color: var(--accent); }
/* Место окошку выбирает panel.js: в заголовке таблицы оно открывалось вниз
   и накрывало первые строки — ровно те числа, ради которых в подсказку
   и заглядывают. Здесь остаётся запасной вариант на случай без скриптов. */
.hint-body {
  display: none; position: absolute; z-index: 40; left: 0; top: 22px;
  width: 250px; max-width: calc(100vw - 16px);
  padding: 9px 11px; text-align: left; white-space: normal;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-sm); box-shadow: 0 10px 30px rgb(0 0 0 / .28);
  font-weight: 400; font-size: 12px; line-height: 1.45;
}
.hint-body b { display: block; margin-bottom: 3px; }
.hint-body i { display: block; margin-top: 5px; color: var(--muted); font-style: normal; }
.term-hint:hover .hint-body,
.term-hint:focus-within .hint-body { display: block; }

/* «Как это считается» и словарь: закрыты по умолчанию. Нужны один раз,
   а место занимали всегда. */
.how { margin: 10px 0 4px; font-size: 12.5px; color: var(--text-2); }
.how > summary { cursor: pointer; color: var(--muted); }
.how ul { margin: 8px 0 0; padding-left: 18px; }
.how li { margin: 4px 0; }
.how dl { margin: 8px 0 0; }
.how dt { font-weight: 600; margin-top: 8px; }
.how dd { margin: 2px 0 0; }

/* Период с датами: «30 дней» без них заставляет держать в голове,
   от какого числа их отсчитывают. */
.period-box { display: flex; flex-direction: column; align-items: flex-end; gap: 4px; }
.period-dates { font-size: 11.5px; }

/* P5-1: иерархия заголовков. Названия секций были почти равны основному
   тексту, и страница читалась сплошным полотном. */
.pulse-panel > .panel-title, .card > .card-title, .card-head-row .card-title {
  font-size: 17px; font-weight: 600; line-height: 1.3;
}
.pulse-panel > .panel-title { margin-top: 26px; }
.troubles .trouble-head b { font-size: 14.5px; }
.trouble-more { display: inline-block; margin-top: 10px; }

/* Первая колонка широких таблиц держится при горизонтальной прокрутке:
   без неё на телефоне непонятно, к какой неделе относится число. */
.sheet-wrap .sheet tbody td:first-child,
.sheet-wrap .sheet thead th:first-child {
  position: sticky; left: 0; background: var(--surface); z-index: 1;
}

/* Подпись поля и знак «?» одной строкой: label — сетка, и без обёртки
   подсказка вставала отдельным рядом, растягивая форму вдвое. */
.field-title { display: flex; align-items: center; gap: 5px; }

/* P5-8: на узком экране страница уезжала вбок целиком. Причина не в таблице,
   а в сетке: колонка `.pulse-panel` росла под самую широкую из них, и вместе
   с ней растягивалось всё остальное — заголовки, врезки, график. Колонку
   держим в границах, а таблицам разрешаем прокручиваться внутри себя. */
.pulse-panel { grid-template-columns: minmax(0, 1fr); }
.pulse-panel > .sheet, .card > .sheet { display: block; overflow-x: auto; }
/* Строка отчёта, которая раскрывается в свои статьи. Итог накладных расходов
   без разбивки ничего не объясняет: продавцу важно видеть подписку, штраф
   и страховку по отдельности, а не одно число «прочее». */
.fold-line > summary {
  cursor: pointer; list-style: none;
  display: inline-flex; align-items: center; gap: 4px;
}
.fold-line > summary::-webkit-details-marker { display: none; }
.fold-line > summary::before {
  content: "▸"; color: var(--muted); font-size: 11px; transition: transform .12s;
}
.fold-line[open] > summary::before { transform: rotate(90deg); }
.fold-list { margin: 6px 0 2px; padding: 0 0 0 14px; list-style: none; }
.fold-list li {
  display: flex; justify-content: space-between; gap: 16px;
  padding: 3px 0; font-size: 13px; border-bottom: 1px solid var(--border-soft);
}
.fold-list li:last-child { border-bottom: 0; }

/* Бейдж «по умолчанию» у числа, посчитанного подставленными значениями.
   Не в шапке страницы: полоса наверху одна на всё, а не верить надо
   конкретному числу — и сказать об этом надо рядом с ним. Приглушённо
   и мелко: это оговорка, а не тревога. */
.guess-mark {
  display: inline-block; margin-left: 5px; padding: 0 6px;
  border: 1px dashed var(--border); border-radius: 999px;
  font-size: 10.5px; font-weight: 400; white-space: nowrap;
}
.guess-mark a { color: var(--muted); text-decoration: none; }
.guess-mark a:hover { color: var(--accent); }
/* Само число рядом с бейджем — приглушённое: оно ещё не проверенный факт. */
td:has(> .guess-mark), .gap-lever-from:has(.guess-mark) { opacity: .78; }

/* --------------------------------------------- графики: линия, которая рисуется
   Ряд, появляющийся сразу целиком, читается как картинка. Отрисовка слева
   направо возвращает ему время: видно, что это ход, а не узор.

   **Скрывать линию стилями нельзя.** Первая версия прятала её через
   `stroke-dashoffset` и открывала классом от наблюдателя — а если наблюдатель
   не сработает (старый браузер, отключённый скрипт, вкладка, которую браузер
   притормозил), график остаётся пустым навсегда. Пустой график хуже
   неанимированного, поэтому здесь только свечение: прячет и оживляет линию
   скрипт, и делает это ровно в тот момент, когда решил рисовать. */
.chart-line {
  filter: drop-shadow(0 0 4px color-mix(in srgb, currentcolor 35%, transparent));
}

/* ---------- Репрайсер ----------
   Раздел про одно число — цену покупателя. Оформление подчинено тому,
   что рядом с каждой ценой обязана стоять её дата: цена без времени
   снятия ведёт к неверному решению вернее, чем её отсутствие.
   Поэтому подпись под числом здесь не украшение, а часть числа. */

.note-ok {
  background: color-mix(in srgb, var(--green) 10%, var(--surface));
  border-color: color-mix(in srgb, var(--green) 30%, var(--border));
}
.note-warn {
  background: color-mix(in srgb, var(--red) 10%, var(--surface));
  border-color: color-mix(in srgb, var(--red) 32%, var(--border));
}

/* Просьба пройти проверку. Кнопка справа от текста: человек читает,
   идёт в окно браузера и возвращается нажать — путь должен быть виден
   целиком, без прокрутки. */
.repricer-check {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; flex-wrap: wrap;
}

.repricer-how p { margin: 0 0 10px; color: var(--text-2); font-size: 13px; line-height: 1.55; }
.repricer-how ul { margin: 0 0 10px; padding-left: 18px; color: var(--text-2); font-size: 13px; }
.repricer-how li { margin-bottom: 5px; line-height: 1.5; }

.repricer-table td { vertical-align: top; }
.repricer-table .num { font-variant-numeric: tabular-nums; white-space: nowrap; }

/* Название второй строкой и приглушённое: артикул — то, чем товар зовут
   в работе, а название нужно, только чтобы узнать его в лицо. */
.rp-name { display: block; font-size: 12px; margin-top: 2px; max-width: 320px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.rp-sub { display: block; font-style: normal; font-size: 11.5px;
  color: var(--muted); margin-top: 3px; white-space: normal; }
.rp-stale { color: var(--amber); }

/* Цена покупателя — главное число страницы, и выглядеть должна главной. */
.rp-card { font-size: 15px; }

.rp-gap { font-weight: 600; }
.rp-gap-ok { color: var(--green); }
.rp-gap-bad { color: var(--amber); }

.rp-anchor { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.rp-anchor select { padding: 5px 9px; font-size: 12.5px; }
.rp-price { width: 78px; }

.rp-what { max-width: 320px; }

/* Строка, требующая действия, помечена полосой слева, а не заливкой:
   заливка на десятке строк превращает таблицу в светофор, по которому
   уже ничего не читается. */
.rp-acting td:first-child { box-shadow: inset 3px 0 0 var(--amber); }
.rp-watched td:first-child { box-shadow: inset 3px 0 0 color-mix(in srgb, var(--accent) 50%, transparent); }

@media (max-width: 900px) {
  .rp-name { max-width: 180px; }
  .rp-what { max-width: 200px; }
}

/* Первый шаг: раздел, который ещё ничего не ведёт, обязан сказать, что делать.
   Кнопка стоит рядом с объяснением, а не внизу страницы: читают одно,
   нажимают тут же. */
.rp-start {
  display: flex; align-items: center; justify-content: space-between;
  gap: 20px; flex-wrap: wrap;
  border-left: 3px solid var(--accent);
}
.rp-start .panel-title { margin: 0 0 6px; }
.rp-start .panel-sub { max-width: 720px; }

/* Стратегия рядом с якорем: два коротких списка в одной ячейке.
   Второй строкой, а не рядом, — иначе колонка растягивается и вытесняет
   то, ради чего таблицу читают: цену покупателя. */
.rp-priority { padding: 4px 8px; font-size: 12px; width: auto; max-width: 150px; }
/* Второй строкой, но не во всю ширину: растянутый список раздувает колонку
   и оттесняет то, ради чего таблицу читают, — цену покупателя. */
.rp-anchor .rp-priority { margin-top: 4px; }
.rp-anchor { max-width: 260px; }
.rp-bulk { display: inline-flex; }

/* Цена подъёма в показах. Янтарным, но подписью, а не значком: это
   не запрет, а вторая сторона решения, и читать её должен тот,
   кто уже решил поднимать. */
.rp-boost { color: var(--amber); }

/* Динамика цен: ссылка у строки и отсечённые предложения на странице товара. */
.rp-actions { display: flex; flex-direction: column; gap: 6px; align-items: flex-start; }
.rp-dyn { font-size: 12px; color: var(--muted); }
.rp-dyn:hover { color: var(--accent); }
.rp-verdict { margin: 0; font-size: 14px; line-height: 1.6; color: var(--text); }
/* Отсечённые как другой товар — приглушены, но видны: по ним понятно,
   что карточку держат копии, и это само по себе новость. */
.rp-dropped td { opacity: 0.55; }

/* Ступени цены в строке. Восемь колонок читаются только тогда, когда
   у каждой один смысл: число сверху, откуда оно — подписью снизу. */
.repricer-table .product-cell { min-width: 260px; }
.rp-head { display: flex; align-items: center; gap: 6px; }
/* Значок динамики рядом с артикулом — как в кабинете Ozon: он открывает
   историю, а не карточку, и стоять должен там, где его ищут. */
.rp-chart { display: inline-flex; color: var(--muted); line-height: 0; }
.rp-chart:hover { color: var(--accent); }
.rp-inline { display: block; }
.repricer-table .rp-price { width: 76px; text-align: right; }
/* Целевая цена — не подпись, а число: по ней принимают решение. */
.rp-target { display: block; font-style: normal; font-size: 13px;
  font-family: var(--mono); color: var(--text); margin-top: 4px; }


/* Числовые колонки не должны переносить подписи: «маржа 9.9 %» в две строки
   вытягивает строку вдвое, а таблица на восемь колонок и без того высокая. */
.repricer-table td.num { white-space: nowrap; }
.repricer-table td.num .rp-sub { white-space: nowrap; }
.repricer-table th { white-space: nowrap; }
/* Ширины: имя товара получает место, числовые колонки — ровно столько,
   сколько нужно самой длинной цене с подписью. */
.repricer-table td.num, .repricer-table th.num { min-width: 104px; }
.repricer-table .rp-what { min-width: 210px; }

/* Выбранный продавец в списке: подсветка, а не галочка — глаз ищет,
   чей ход сейчас на графике. */
.rp-chosen { color: var(--violet); font-weight: 600; }


/* Чья это цена — подписью под заголовком. В таблице две лестницы вперемешку,
   и они различаются вдвое: путаница здесь стоит неверного решения о цене. */
.rp-scope {
  display: block; font-style: normal; font-weight: 400; font-size: 10.5px;
  letter-spacing: 0; text-transform: none; color: var(--muted); margin-top: 2px;
}

/* Документ поставки в приёмке: план из накладной рядом с фактом. */
.receipt-upload { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.receipt-upload input[type="file"] { font-size: 12.5px; max-width: 260px; }
.doc-name { display: block; font-size: 12px; margin-top: 2px; }
.doc-note { display: block; font-style: normal; font-size: 11.5px; color: var(--muted); margin-top: 3px; }
.doc-gap { color: var(--amber); }
/* Строка, которой нет в каталоге: она не попадёт ни в остатки, ни
   в себестоимость, и увидеть это надо при приёмке, а не через месяц. */
.doc-unknown td:first-child { box-shadow: inset 3px 0 0 var(--amber); }

/* Прицел камеры: рамка поверх видео, куда надо навести код.
   Нужна не для красоты. На коробке кодов бывает несколько рядом, и без
   рамки сканер брал тот, что первым попался камере, — человек не понимал,
   какой именно записался. Теперь берётся только тот, что в рамке. */
.scan-frame {
  position: relative; display: block; overflow: hidden;
  border-radius: var(--radius-sm); background: #000;
}
.scan-frame video { display: block; width: 100%; }
.scan-aim {
  position: absolute; inset: 0; pointer-events: none;
  display: flex; align-items: center; justify-content: center;
}
/* Сторону рамки задаёт высота кадра, а не ширина: на телефоне кадр выше,
   чем шире, и квадрат по ширине вылезал бы за картинку. */
.scan-aim-box {
  position: relative;
  height: 62%; aspect-ratio: 1; max-width: 62%;
  border-radius: 12px;
  /* Затемнение вокруг рамки — тенью наружу: видно, куда целиться,
     и не мешает самой картинке внутри. */
  box-shadow: 0 0 0 100vmax rgba(0, 0, 0, 0.4);
  transition: box-shadow 0.15s ease;
}
.scan-aim-corner {
  position: absolute; width: 24px; height: 24px;
  border: 3px solid #fff; border-radius: 4px;
}
.scan-aim-corner.tl { top: -2px; left: -2px; border-right: 0; border-bottom: 0; }
.scan-aim-corner.tr { top: -2px; right: -2px; border-left: 0; border-bottom: 0; }
.scan-aim-corner.bl { bottom: -2px; left: -2px; border-right: 0; border-top: 0; }
.scan-aim-corner.br { bottom: -2px; right: -2px; border-left: 0; border-top: 0; }

/* Состояния рамки: код рядом, но мимо — янтарный; взят — зелёный.
   Цвет на углах, а не на подписи: смотрят в кадр, а не под него. */
.scan-aim-box[data-state="near"] .scan-aim-corner { border-color: var(--amber); }
.scan-aim-box[data-state="hit"] .scan-aim-corner { border-color: var(--green); }
.scan-aim-box[data-state="hit"] { box-shadow: 0 0 0 100vmax rgba(0, 0, 0, 0.4), 0 0 22px var(--green); }

.scan-aim-hint {
  position: absolute; left: 50%; bottom: 12px; transform: translateX(-50%);
  padding: 4px 12px; border-radius: 999px;
  background: rgba(0, 0, 0, 0.55); color: #fff;
  font-size: 12.5px; white-space: nowrap;
}

/* Сборщик цен: путь для тех, у кого панель в облаке, а браузер дома.
   Ключ и адрес показываем готовыми к переносу — их не набирают по памяти,
   их копируют, и строка с переносом посередине стоила бы часа поисков. */
.collector { display: grid; gap: 12px; }
.collector-why { font-size: 13.5px; }
.collector-steps { margin: 0; padding-left: 20px; display: grid; gap: 8px; font-size: 13px; }
.collector-keys {
  margin: 6px 0 0; padding: 10px 12px;
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-family: var(--mono); font-size: 12.5px;
  white-space: pre-wrap; word-break: break-all;
  user-select: all;
}
.collector-note { font-size: 12.5px; color: var(--muted); margin: 0; }
.collector-more { font-size: 12.5px; color: var(--muted); }
.collector-more summary { cursor: pointer; }
.collector-more p { margin: 8px 0 0; }

/* Стоит расширение или нет — знает только оно само: метку на странице
   ставит его же скрипт, уже после отрисовки. Поэтому переключаем видимость
   стилями, а не скриптом: скрипту пришлось бы ждать метку и мигать. */
.collector-ready { display: none; font-size: 13.5px; }
html[data-aitract-collector] .collector-install { display: none; }
html[data-aitract-collector] .collector-ready { display: block; }

/* Ход обхода — рядом с кнопкой, которой его запустили. */
.collector-line { font-size: 12.5px; color: var(--muted); }
.collector-line[data-state="busy"] { color: var(--amber); }
.collector-line[data-state="ok"] { color: var(--green); }
.collector-line[data-state="bad"] { color: var(--red); }
