/* ==========================================================================
   Sellpi — Дашборд-система (spd-)
   Самодостаточный файл. Две темы через [data-theme]; по умолчанию — тёмная.
   Переключатель: spd-theme.js (ставит data-theme на <html>).
   ========================================================================== */

/* ----- 1. Токены тем ------------------------------------------------------ */
:root,
:root[data-theme="dark"] {
  --ink:#eef3f0; --muted:#8b958f; --faint:#59635d;
  --accent:#34d399; --bright:#6ee7b7; --deep:#0c5f48; --glow:rgba(52,211,153,0.45);
  --line:rgba(255,255,255,0.08);

  --sp-bg:#070908;
  --sidebar:#090c0b;
  --topbar:rgba(7,9,8,0.72);
  --card:linear-gradient(180deg,rgba(255,255,255,0.035),rgba(255,255,255,0.01));
  --card-2:linear-gradient(180deg,rgba(255,255,255,0.04),rgba(255,255,255,0.012));
  --inset:rgba(255,255,255,0.02);
  --track:rgba(255,255,255,0.05);
  --hover:rgba(255,255,255,0.04);
  --shadow:0 1px 2px rgba(0,0,0,0.4),0 18px 40px rgba(0,0,0,0.22);
  --active:rgba(52,211,153,0.10);
  --chip:rgba(255,255,255,0.03);
  --on-accent:#04130d;
  --warn:#fb923c; --warn-bg:rgba(240,138,60,0.12); --warn-line:rgba(240,138,60,0.30);
  --bad:#fb6f92; --bad-bg:rgba(251,111,146,0.12);
  --info:#5b8cff;

  /* Палитра рядов: цвет означает «КТО», а не «как дела». Раньше ряды красились
     смысловыми токенами, и четвёртый по выручке канал получал --bad — читался
     как проблемный, хотя он просто четвёртый. Смысловые цвета остаются за
     состоянием (мёртвый сток, ниже пола), эти — за принадлежностью. */
  --cat-1:#34d399; --cat-2:#5b8cff; --cat-3:#c084fc; --cat-4:#f0b23c;
  --cat-5:#2dd4bf; --cat-6:#f472b6; --cat-7:#94a3b8; --cat-8:#a3e635;

  --mono:'JetBrains Mono',ui-monospace,monospace;
  --sans:'Onest',system-ui,-apple-system,sans-serif;
  color-scheme: dark;
}

:root[data-theme="light"] {
  --ink:#0c1310; --muted:#5a655f; --faint:#94a09a;
  --accent:#0e9c69; --bright:#7fd0ab; --deep:#063d2a; --glow:rgba(14,156,105,0.40);
  --line:rgba(12,19,15,0.10);

  --sp-bg:#f6f7f3;
  --sidebar:#eef1ea;
  --topbar:rgba(246,247,243,0.82);
  --card:#ffffff;
  --card-2:#ffffff;
  --inset:#f5f7f3;
  --track:rgba(12,19,15,0.07);
  --hover:rgba(12,19,15,0.035);
  --shadow:0 1px 2px rgba(12,19,15,0.04),0 18px 40px rgba(12,19,15,0.05);
  --active:rgba(14,156,105,0.12);
  --chip:#ffffff;
  --on-accent:#04130d;
  --warn:#b4540a; --warn-bg:rgba(240,138,60,0.12); --warn-line:rgba(240,138,60,0.30);
  --bad:#e0436a; --bad-bg:rgba(224,67,106,0.10);
  --info:#3b6fe0;

  /* Те же ряды, затемнённые: на белом фоне светлые варианты не читаются. */
  --cat-1:#0e9c69; --cat-2:#3b6fe0; --cat-3:#9333ea; --cat-4:#b4540a;
  --cat-5:#0d9488; --cat-6:#db2777; --cat-7:#64748b; --cat-8:#65a30d;
  color-scheme: light;
}

/* ----- 2. База ------------------------------------------------------------ */
* { box-sizing:border-box; }
html,body { margin:0; padding:0; background:var(--sp-bg); }
body {
  color:var(--ink);
  font-family:var(--sans);
  -webkit-font-smoothing:antialiased;
  transition:background .25s ease, color .25s ease;
}
::selection { background:color-mix(in oklab, var(--accent), transparent 72%); }
::-webkit-scrollbar { width:10px; height:10px; }
::-webkit-scrollbar-thumb { background:var(--line); border-radius:6px; }
@keyframes sp-twinkle { 0%,100%{opacity:.85} 50%{opacity:.3} }
@keyframes spd-spin { to { transform:rotate(360deg); } }
.spd-mono { font-family:var(--mono); }

/* ----- 3. Каркас ---------------------------------------------------------- */
.spd-app { display:grid; grid-template-columns:236px 1fr; min-height:100vh; }

/* ----- 4. Сайдбар --------------------------------------------------------- */
.spd-sidebar {
  position:sticky; top:0; height:100vh;
  display:flex; flex-direction:column;
  border-right:1px solid var(--line);
  background:var(--sidebar); padding:22px 16px;
}
.spd-brand { display:flex; align-items:center; gap:11px; padding:6px 8px 22px; }
.spd-brand__mark { position:relative; width:28px; height:28px; display:grid; place-items:center; }
.spd-brand__mark::before { content:""; position:absolute; inset:0; border-radius:8px; background:radial-gradient(circle at 30% 25%,var(--bright),var(--deep)); box-shadow:0 0 18px var(--glow); }
.spd-brand__mark::after { content:""; position:relative; width:10px; height:10px; border-radius:3px; background:#06120e; }
.spd-brand__name { font-weight:800; letter-spacing:-0.02em; font-size:17px; }
.spd-brand__badge { margin-left:auto; font-family:var(--mono); font-size:9.5px; letter-spacing:0.14em; color:var(--faint); border:1px solid var(--line); border-radius:6px; padding:3px 7px; }
.spd-sidebar__cap { font-family:var(--mono); font-size:10.5px; letter-spacing:0.14em; color:var(--faint); padding:6px 10px; }
.spd-nav { display:flex; flex-direction:column; gap:3px; margin-top:4px; }
.spd-nav__item { display:flex; align-items:center; gap:11px; padding:10px 12px; border-radius:11px; text-decoration:none; font-size:14.5px; color:var(--muted); border:1px solid transparent; transition:background .15s, color .15s; }
.spd-nav__item:hover { background:var(--hover); color:var(--ink); }
.spd-nav__ico { font-size:15px; width:18px; text-align:center; color:var(--faint); }
.spd-nav__item--active { color:var(--ink); background:var(--active); border-color:var(--glow); }
.spd-nav__item--active .spd-nav__ico { color:var(--accent); }
.spd-sidebar__spacer { flex:1; }
.spd-user { display:flex; align-items:center; gap:11px; padding:11px; border-radius:13px; border:1px solid var(--line); background:var(--inset); }
.spd-user__avatar { width:32px; height:32px; border-radius:9px; display:grid; place-items:center; font-family:var(--mono); font-size:12px; font-weight:700; color:var(--on-accent); background:linear-gradient(160deg,var(--bright),var(--accent)); flex:0 0 auto; }
.spd-user__info { flex:1; min-width:0; }
.spd-user__mail { font-size:13px; color:var(--ink); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.spd-user__role { font-size:11px; color:var(--faint); }
.spd-user__caret { color:var(--faint); font-size:13px; }

/* Переключатель темы */
.spd-theme-toggle {
  display:flex; align-items:center; gap:10px; margin-bottom:12px;
  padding:10px 12px; border-radius:13px; border:1px solid var(--line);
  background:var(--inset); cursor:pointer; color:var(--muted); font-size:13px;
  font-family:inherit; width:100%; text-align:left;
}
.spd-theme-toggle__ico { width:18px; text-align:center; }
.spd-theme-toggle__label { flex:1; }
.spd-switch { width:38px; height:21px; border-radius:999px; background:var(--track); position:relative; flex:0 0 auto; transition:background .2s; }
.spd-switch::after { content:""; position:absolute; top:2px; left:2px; width:17px; height:17px; border-radius:50%; background:var(--accent); transition:transform .2s; }
:root[data-theme="dark"] .spd-switch { background:color-mix(in oklab,var(--accent),transparent 60%); }
:root[data-theme="dark"] .spd-switch::after { transform:translateX(17px); }
.spd-topbar .spd-theme-toggle { width:auto; margin:0; padding:8px 12px; }

/* ----- 5. Основная колонка ------------------------------------------------ */
.spd-main { min-width:0; display:flex; flex-direction:column; }
.spd-topbar {
  position:sticky; top:0; z-index:30;
  display:flex; align-items:center; gap:14px; padding:16px 32px;
  border-bottom:1px solid var(--line);
  backdrop-filter:blur(14px); -webkit-backdrop-filter:blur(14px);
  background:var(--topbar);
}
.spd-topbar__menu { display:grid; place-items:center; width:32px; height:32px; border-radius:9px; border:1px solid var(--line); color:var(--muted); cursor:pointer; font-size:14px; }
.spd-topbar__crumb { font-size:14px; color:var(--faint); }
.spd-topbar__sep { color:var(--faint); }
.spd-topbar__cur { font-size:14px; color:var(--ink); font-weight:600; }
.spd-topbar__spacer { flex:1; }
.spd-content { padding:30px 32px 56px; }

/* Заголовок страницы */
.spd-head { display:flex; align-items:flex-start; justify-content:space-between; gap:20px; flex-wrap:wrap; }
.spd-head h1 { margin:0; font-size:30px; font-weight:800; letter-spacing:-0.03em; }
.spd-head p { margin:8px 0 0; font-size:14.5px; color:var(--muted); max-width:70ch; }

/* Кнопки */
.spd-btn { display:inline-flex; align-items:center; justify-content:center; gap:8px; font-family:inherit; font-size:14px; font-weight:600; cursor:pointer; text-decoration:none; border:1px solid transparent; border-radius:12px; padding:11px 18px; transition:transform .15s, border-color .15s, background .15s; }
.spd-btn--primary { color:var(--on-accent); background:linear-gradient(180deg,var(--bright),var(--accent)); box-shadow:0 8px 22px var(--glow); }
.spd-btn--primary:hover { transform:translateY(-1px); }
.spd-btn--ghost { color:var(--ink); border-color:var(--line); background:var(--inset); }
.spd-btn--ghost:hover { background:var(--hover); }
.spd-btn--sm { padding:9px 14px; font-size:13px; border-radius:10px; }
.spd-edit { /* legacy alias */ display:inline-flex; align-items:center; gap:8px; font-size:14px; font-weight:600; color:var(--on-accent); text-decoration:none; padding:11px 18px; border-radius:12px; background:linear-gradient(180deg,var(--bright),var(--accent)); box-shadow:0 8px 22px var(--glow); }

/* Селекторы */
.spd-selectors { display:flex; align-items:flex-end; gap:14px; margin-top:24px; flex-wrap:wrap; }
.spd-select__cap { font-family:var(--mono); font-size:10.5px; letter-spacing:0.12em; color:var(--faint); margin-bottom:7px; }
.spd-select__box { display:flex; align-items:center; justify-content:space-between; gap:24px; padding:11px 14px; border-radius:11px; border:1px solid var(--line); background:var(--chip); font-size:14px; cursor:pointer; }
.spd-caret { color:var(--faint); }
.spd-star { color:var(--accent); }
.spd-more { display:grid; place-items:center; width:42px; height:42px; border-radius:11px; border:1px solid var(--line); background:var(--chip); color:var(--muted); cursor:pointer; }

/* Пресет-заголовок */
.spd-preset-head { margin-top:30px; display:flex; align-items:center; gap:11px; }
.spd-preset-head h2 { margin:0; font-size:20px; font-weight:700; letter-spacing:-0.02em; }
.spd-badge { font-size:12px; color:var(--muted); padding:4px 10px; border-radius:7px; background:var(--chip); border:1px solid var(--line); }
.spd-preset-sub { margin:8px 0 0; font-size:14px; color:var(--muted); }

/* Вкладки (как ссылки между страницами) */
.spd-tabs { margin-top:20px; display:inline-flex; gap:4px; padding:4px; border-radius:13px; border:1px solid var(--line); background:var(--inset); flex-wrap:wrap; }
.spd-tab { display:inline-flex; align-items:center; gap:8px; font-size:13.5px; font-weight:500; padding:9px 16px; border-radius:10px; cursor:pointer; color:var(--muted); text-decoration:none; }
.spd-tab::before { content:""; width:6px; height:6px; border-radius:50%; background:var(--faint); }
.spd-tab:hover { color:var(--ink); }
.spd-tab--active { color:var(--ink); background:var(--active); }
.spd-tab--active::before { background:var(--accent); }

/* Фильтр-чипы */
.spd-filters { margin-top:18px; display:flex; align-items:center; gap:10px; flex-wrap:wrap; }
.spd-filters__cap { font-family:var(--mono); font-size:10.5px; letter-spacing:0.12em; color:var(--faint); }
.spd-fchip { display:inline-flex; align-items:center; gap:8px; font-size:13px; color:var(--muted); padding:8px 13px; border-radius:10px; border:1px solid var(--line); background:var(--chip); cursor:pointer; }

/* Баннер-предупреждение */
.spd-banner { margin-top:24px; display:flex; align-items:center; gap:16px; padding:18px 22px; border-radius:16px; border:1px solid var(--warn-line); background:linear-gradient(135deg,var(--warn-bg),transparent); }
.spd-banner__ico { display:grid; place-items:center; width:34px; height:34px; border-radius:10px; background:var(--warn-bg); color:var(--warn); font-size:17px; flex:0 0 auto; }
.spd-banner__title { font-size:15px; font-weight:700; }
.spd-banner__sub { margin-top:2px; font-size:13px; color:var(--muted); }
.spd-banner__action { margin-left:auto; }

/* ----- 6. Карточки/панели ------------------------------------------------- */
.spd-grid-2 { display:grid; grid-template-columns:1fr 1fr; gap:16px; }
.spd-grid-3 { display:grid; grid-template-columns:repeat(3,1fr); gap:16px; }
.spd-grid-4 { display:grid; grid-template-columns:repeat(4,1fr); gap:14px; }
.spd-charts { margin-top:16px; display:grid; grid-template-columns:1.7fr 1fr; gap:16px; }
.spd-breakdown { margin-top:16px; display:grid; grid-template-columns:1fr 1fr; gap:16px; }
.spd-panel { padding:24px; border-radius:20px; border:1px solid var(--line); background:var(--card); box-shadow:var(--shadow); }
.spd-panel__head { display:flex; align-items:flex-start; justify-content:space-between; gap:12px; flex-wrap:wrap;
                   padding-bottom:15px; }
.spd-panel h3 { margin:0; font-size:16px; font-weight:700; }
.spd-panel__ico { display:grid; place-items:center; width:30px; height:30px; border-radius:9px; background:var(--active); color:var(--accent); font-size:14px; flex:0 0 auto; }
.spd-panel__title { display:flex; align-items:center; gap:11px; }
.spd-panel__big { font-family:var(--mono); font-size:15px; font-weight:700; }
.spd-panel__meta { margin-top:4px; font-family:var(--mono); font-size:12px; color:var(--faint); }
/* Два разных пояснения, и путать их нельзя. __sub говорит, ЧТО за число и как
   читать панель, — строкой под заголовком, до того как человек посмотрел на
   цифры. .spd-note говорит, КАК посчитано и чему нельзя верить, — абзацем под
   содержимым. Раньше было только второе, и объяснение приходило слишком поздно. */
/* Подсказка под иконкой. Пузырь лежит в разметке всегда и скрыт визуально —
   программе чтения с экрана и поиску по странице текст нужен без наведения.
   Открывается и по focus-within: с клавиатуры и с касания hover не наступает,
   и подсказка иначе была бы недостижима. */
.spd-hint { position:relative; display:inline-grid; place-items:center; flex:0 0 auto;
            width:17px; height:17px; margin-left:8px; vertical-align:middle; cursor:help;
            border-radius:50%; border:1px solid var(--line); background:var(--inset);
            color:var(--faint); transition:color .15s, border-color .15s; }
.spd-hint:hover, .spd-hint:focus-visible { color:var(--accent); border-color:var(--accent); outline:none; }
.spd-hint__ico { font-family:var(--mono); font-size:10px; font-weight:600; line-height:1; }
/* Заголовок жирный и с трекингом — пузырь внутри него это наследует, если не сбросить. */
.spd-hint__bubble { position:absolute; top:calc(100% + 9px); left:-9px; z-index:60;
                    width:max-content; max-width:min(380px,78vw); padding:11px 14px;
                    border-radius:12px; border:1px solid var(--line); background:var(--sp-bg);
                    box-shadow:var(--shadow); color:var(--muted);
                    font-size:12.5px; font-weight:400; line-height:1.45;
                    letter-spacing:normal; text-transform:none; text-align:left; white-space:normal;
                    opacity:0; visibility:hidden; transform:translateY(-3px); pointer-events:none;
                    transition:opacity .14s, transform .14s, visibility .14s; }
.spd-hint:hover .spd-hint__bubble,
.spd-hint:focus-within .spd-hint__bubble { opacity:1; visibility:visible; transform:translateY(0); }
@media (prefers-reduced-motion:reduce) { .spd-hint__bubble { transition:none; } }
.spd-section-cap { margin:34px 0 14px; font-family:var(--mono); font-size:11px; letter-spacing:0.16em; color:var(--faint); }


/* ----- 6б. Карточки разбора и каналов ------------------------------------- */
/* Полоса слева задаёт тон наблюдения. Ставится псевдоэлементом, а не border-left:
   у карточки-ссылки border участвует в hover-состоянии, и два правила спорили бы. */
.spd-tabsrow { margin-top:20px; display:flex; align-items:center; justify-content:space-between; gap:12px; flex-wrap:wrap; }
.spd-tabsrow .spd-tabs { margin-top:0; }
.spd-colswitch { font-size:12px; color:var(--muted); text-decoration:none; padding:7px 12px; border-radius:10px;
                 border:1px solid var(--line); white-space:nowrap; transition:color .15s, border-color .15s; }
.spd-colswitch:hover { color:var(--ink); border-color:var(--glow); }
.spd-briefs { margin-top:20px; display:grid; grid-template-columns:repeat(auto-fit,minmax(232px,1fr)); gap:14px; }
/* Карточки лежат ВНУТРИ панели, поэтому поверхность вложенная (--inset), а не
   карточная: в светлой теме --card-2 и --card оба белые, и на фоне панели
   карточка отличалась бы только тенью — то есть выглядела бы висящей отдельно
   ровно там, где смысл в обратном. Тень по той же причине убрана. */
.spd-brief { position:relative; overflow:hidden; padding:16px 18px 16px 22px; border-radius:15px;
             border:1px solid var(--line); background:var(--inset); }
.spd-brief::before { content:''; position:absolute; left:0; top:0; bottom:0; width:3px; background:var(--accent); }
.spd-brief--bad::before  { background:var(--bad); }
.spd-brief--warn::before { background:var(--warn); }
.spd-brief--info::before { background:var(--info); }
.spd-brief--idle::before { background:var(--faint); }
.spd-brief__title { font-size:14.5px; font-weight:700; line-height:1.35; letter-spacing:-0.01em; }
.spd-brief__sub { margin-top:6px; font-size:12.5px; line-height:1.45; color:var(--muted); }
.spd-brief--link { display:block; color:inherit; text-decoration:none; transition:border-color .15s, transform .15s; }
.spd-brief--link:hover { border-color:var(--glow); transform:translateY(-1px); }

/* Карточка канала: один шаблон на все каналы. Список со свёрнутыми долями
   отвечал только «кто больше», а вопрос к каналу всегда второй — «и что там
   с ценой и объёмом». */
.spd-chcards { margin-top:16px; display:grid; grid-template-columns:repeat(auto-fit,minmax(210px,1fr)); gap:12px; }
.spd-chcard { padding:14px 16px; border-radius:15px; border:1px solid var(--line); background:var(--inset); }
.spd-chcard__top { display:flex; align-items:center; gap:7px; }
.spd-chcard__dot { width:8px; height:8px; border-radius:50%; flex:0 0 auto; }
.spd-chcard__name { font-size:13px; font-weight:600; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.spd-chcard__share { margin-left:auto; font-family:var(--mono); font-size:11px; color:var(--faint); }
.spd-chcard__val { margin-top:11px; font-family:var(--mono); font-size:19px; font-weight:800; letter-spacing:-0.02em; white-space:nowrap; }
.spd-chcard__delta { margin-top:5px; font-size:11.5px; font-weight:600; }
.spd-chcard__rows { margin-top:11px; padding-top:10px; border-top:1px solid var(--line); display:grid; gap:4px; }
.spd-chcard__row { display:flex; justify-content:space-between; gap:8px; font-size:11.5px; color:var(--muted); }
.spd-chcard__row b { font-family:var(--mono); font-weight:600; color:var(--ink); }

@media (max-width:900px) {
  .spd-briefs { grid-template-columns:1fr; }
  .spd-chcards { grid-template-columns:1fr 1fr; }
}
/* Две карточки канала по 210px плюс отступы и поля панели перестают помещаться
   раньше, чем кончается экран телефона, — и страница получает горизонтальную
   прокрутку. Ниже этой ширины карточки идут в одну колонку. */
@media (max-width:560px) {
  .spd-chcards { grid-template-columns:1fr; }
}

/* ----- 7. KPI ------------------------------------------------------------- */
/* Число колонок — переменной, а не инлайновой сеткой на элементе: инлайн
   перебивает брейкпоинты внизу файла, и на телефоне остаётся шесть колонок
   по сорок пикселей. Экраны с четырьмя плитками задают --kpi-cols. */
.spd-kpis { margin-top:22px; display:grid; grid-template-columns:repeat(var(--kpi-cols,6),1fr); gap:14px; }
.spd-kpi { position:relative; overflow:hidden; padding:18px; border-radius:18px; border:1px solid var(--line); background:var(--card-2); box-shadow:var(--shadow); }
.spd-kpi__top { display:flex; align-items:center; gap:8px; }
/* Плитка иконки. Тон задаётся по СМЫСЛУ метрики, а не по её месту в ряду:
   зелёная плитка у «упущено из-за дефицита» читается как «всё хорошо».
   По умолчанию акцент — так выглядит нейтральный показатель. */
.spd-kpi__ico { display:grid; place-items:center; width:26px; height:26px; border-radius:8px; font-size:13px;
                background:var(--active); color:var(--accent); }
.spd-kpi__ico--info, .spd-panel__ico--info { background:color-mix(in oklab,var(--info),transparent 88%); color:var(--info); }
.spd-kpi__ico--warn, .spd-panel__ico--warn { background:var(--warn-bg); color:var(--warn); }
.spd-kpi__ico--bad,  .spd-panel__ico--bad  { background:var(--bad-bg);  color:var(--bad); }
.spd-kpi__ico--idle, .spd-panel__ico--idle { background:var(--chip);    color:var(--muted); }
.spd-kpi__label { font-family:var(--mono); font-size:10.5px; letter-spacing:0.08em; color:var(--faint); }
.spd-kpi__caret { margin-left:auto; color:var(--faint); font-size:11px; }
.spd-kpi__val { margin-top:16px; font-size:26px; font-weight:800; letter-spacing:-0.03em; font-family:var(--mono); white-space:nowrap; }
/* Цвет дельты — от СМЫСЛА, не от направления стрелки: у «упущено из-за
   дефицита» рост это ↗ красным. Модификатор ставит App\Support\Delta::$tone,
   поэтому --up объявлен явно, а не остался умолчанием. */
.spd-kpi__delta { margin-top:7px; font-size:11.5px; font-weight:600; color:var(--accent); }
.spd-kpi__delta--up   { color:var(--accent); }
.spd-kpi__delta--down { color:var(--bad); }
.spd-kpi__delta--flat { color:var(--muted); }
.spd-kpi__delta span { color:var(--muted); font-weight:400; }
.spd-kpi__prev { margin-top:7px; font-size:11.5px; color:var(--faint); }
.spd-plan { margin-top:12px; }
.spd-plan__row { display:flex; justify-content:space-between; font-family:var(--mono); font-size:10px; color:var(--faint); margin-bottom:5px; }
.spd-plan__track { height:5px; border-radius:4px; background:var(--track); overflow:hidden; }
.spd-plan__track span { display:block; height:100%; border-radius:4px; background:linear-gradient(90deg,var(--deep),var(--accent)); }
.spd-spark { margin-top:14px; display:flex; align-items:flex-end; gap:3px; height:30px; }
.spd-spark span { flex:1; border-radius:2px; background:color-mix(in oklab,var(--accent),transparent 60%); }
.spd-spark span:last-child { background:var(--accent); }
/* День без данных штрихуется во всю высоту, а не рисуется нулевым столбиком:
   иначе «синк не работал» становится неотличим от «продаж не было». */
.spd-spark span[data-none] { height:100%; background:repeating-linear-gradient(45deg,
                             transparent,transparent 3px,var(--line) 3px,var(--line) 4px); }

/* Линейный спарклайн. Своя разметка, не ApexCharts: их на экране до восьми, и
   каждый инстанс тянет дерево и ResizeObserver ради ломаной без осей.
   Ни <defs>, ни градиентов — id в SVG глобальны на страницу, и восемь копий
   одного градиента дадут восемь спарклайнов цвета первого. */
.spd-sparkline { margin-top:14px; color:var(--accent); }
.spd-sparkline svg { display:block; width:100%; height:30px; overflow:visible; }
.spd-sparkline polyline { fill:none; stroke:currentColor; stroke-width:1.5;
                          stroke-linejoin:round; stroke-linecap:round; }
.spd-sparkline__dot  { fill:currentColor; opacity:.55; }
.spd-sparkline__last { fill:currentColor; }
.spd-sparkline__gaps { margin-top:5px; font-size:10.5px; color:var(--faint); }
/* Пустой ряд — пунктир и подпись, НИКОГДА не плоская линия по нулю: та
   утверждает «всё время было ровно», а правда — «мы не знаем». */
.spd-sparkline--empty { display:flex; align-items:center; gap:9px; height:30px; }
.spd-sparkline--empty i { flex:1; height:0; border-top:1px dashed var(--line); }
.spd-sparkline--empty span { font-size:10.5px; color:var(--faint); white-space:nowrap; }

/* ----- 8. Линейный график / двойная ось / стэк ---------------------------- */
/* Место под график ApexCharts. min-height держит высоту до того, как поднимется
   библиотека: без него подпись под графиком прыгает вверх и обратно на каждой
   загрузке. Размеры самому SVG здесь не задаём — см. .spd-line ниже. */
.spd-chart { margin-top:16px; min-height:320px; }
.spd-chart--sm { min-height:200px; }

.spd-line { margin-top:8px; }
/* Только для рисованных вручную SVG дизайн-системы. ApexCharts сам ставит
   своему SVG ширину и высоту и сам их пересчитывает при ресайзе; height:auto
   поверх этого тянет картинку по вертикали вместе с кеглем подписей и
   толщиной линий — график выглядел «разъехавшимся» без единой ошибки в JS. */
.spd-line svg { width:100%; height:auto; overflow:visible; display:block; }
/* Своя подсказка графика. ApexCharts кладёт её в свою рамку с собственным
   фоном и тенью — их приходится гасить, иначе получается коробка в коробке. */
.apexcharts-tooltip.apexcharts-theme-light,
.apexcharts-tooltip.apexcharts-theme-dark {
  background:transparent !important; border:0 !important; box-shadow:none !important;
}
.spd-tt { min-width:186px; padding:11px 13px; border-radius:12px; border:1px solid var(--line);
          background:var(--sidebar); box-shadow:var(--shadow);
          font-family:var(--sans); font-size:12.5px; color:var(--ink); }
.spd-tt__head { font-family:var(--mono); font-size:11px; color:var(--faint);
                text-transform:uppercase; letter-spacing:.04em; margin-bottom:8px; }
.spd-tt__row { display:flex; align-items:center; gap:8px; padding:3px 0; }
.spd-tt__row i { width:8px; height:8px; border-radius:2px; flex:0 0 auto; }
.spd-tt__row span { flex:1; color:var(--muted); overflow:hidden;
                    text-overflow:ellipsis; white-space:nowrap; }
.spd-tt__row b { font-family:var(--mono); font-weight:600; }
.spd-tt__none { color:var(--faint); font-style:italic; }
.spd-tt__sum { display:flex; justify-content:space-between; gap:12px; margin-top:8px;
               padding-top:8px; border-top:1px solid var(--line); }
.spd-tt__sum span { color:var(--muted); }
.spd-tt__sum b { font-family:var(--mono); font-weight:700; }

.spd-legend-row { display:flex; gap:16px; flex-wrap:wrap; margin-top:10px; font-size:12px; }
.spd-legend-row span { display:inline-flex; align-items:center; gap:7px; color:var(--muted); }
.spd-legend-row i { width:14px; height:3px; border-radius:2px; display:inline-block; }
.spd-legend-row i.dot { width:10px; height:10px; border-radius:3px; }

/* ----- 10. Рейтинги ------------------------------------------------------- */
.spd-ranks { margin-top:20px; display:flex; flex-direction:column; gap:16px; }
.spd-rank__top { display:flex; align-items:center; gap:10px; font-size:14px; margin-bottom:8px; }
.spd-rank__n { font-family:var(--mono); font-size:11px; color:var(--faint); width:14px; }
.spd-rank__name { flex:1; color:var(--ink); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.spd-rank__name--warn { color:var(--warn); }
.spd-rank__val { font-family:var(--mono); font-weight:700; }
.spd-rank__pct { font-family:var(--mono); font-size:11.5px; color:var(--faint); width:34px; text-align:right; }
.spd-rank__track { height:7px; border-radius:4px; background:var(--track); overflow:hidden; margin-left:24px; }
.spd-rank__track span { display:block; height:100%; border-radius:4px; background:linear-gradient(90deg,var(--deep),var(--accent)); }

/* ----- 9. Донат ----------------------------------------------------------- */
/* Доли каналов: бублик слева, список справа. Цвет сегмента и точки в списке —
   один и тот же токен, он и связывает две половины между собой. */
.spd-split { display:flex; align-items:center; gap:22px; margin-top:18px; }
.spd-split__list { flex:1; min-width:0; margin-top:0; gap:18px; }
/* Размер SVG не трогаем: его ставит себе ApexCharts, а viewBox у него нет —
   любое width/height отсюда не масштабирует картинку, а режет её. Ровно на
   этом уже обожглись с .spd-line (см. комментарий в разделе 8): здесь то же
   правило пережило свой макет и полгода резало боевой бублик.
   Ширина — свойство раскладки «бублик слева, список справа», не самого бублика:
   в другой колонке он встанет другим. */
.spd-donut { position:relative; flex:0 0 auto; }
.spd-split .spd-donut { flex:0 0 236px; }
.spd-donut__mid { position:absolute; inset:0; display:grid; place-content:center;
                  text-align:center; pointer-events:none; max-width:150px; margin:0 auto; }
.spd-donut__sum { font-family:var(--mono); font-size:17px; font-weight:700; color:var(--ink);
                  transition:color .16s ease; }
.spd-donut__cap { font-size:11px; color:var(--faint); margin-top:3px; line-height:1.35;
                  overflow-wrap:anywhere; }
/* Наведение подсвечивает сегмент тем, что гасит остальные. Индекс проставляет
   JS на обёртке; порядок сегментов задаёт сервер, он же строит список рядом.
   Правил ровно столько же, сколько цветов в ChartPalette::COUNT — CSS не умеет
   сравнивать [rel] с data-focus арифметически. Раньше их было пять при пяти
   цветах, и шестая строка списка гасила всё, не подсветив ничего. */
.spd-donut .apexcharts-pie-series { transition:opacity .16s ease; }
.spd-donut[data-focus] .apexcharts-pie-series { opacity:.25; }
.spd-donut[data-focus="0"] .apexcharts-pie-series[rel="1"],
.spd-donut[data-focus="1"] .apexcharts-pie-series[rel="2"],
.spd-donut[data-focus="2"] .apexcharts-pie-series[rel="3"],
.spd-donut[data-focus="3"] .apexcharts-pie-series[rel="4"],
.spd-donut[data-focus="4"] .apexcharts-pie-series[rel="5"],
.spd-donut[data-focus="5"] .apexcharts-pie-series[rel="6"],
.spd-donut[data-focus="6"] .apexcharts-pie-series[rel="7"],
.spd-donut[data-focus="7"] .apexcharts-pie-series[rel="8"] { opacity:1; }
.spd-rank__dot { width:9px; height:9px; border-radius:3px; flex:0 0 auto; }
.spd-rank__row { display:flex; align-items:baseline; gap:9px; margin-bottom:7px; }
.spd-rank__share { font-family:var(--mono); font-size:12px; color:var(--muted); }
.spd-rank__qty { font-family:var(--mono); font-size:11.5px; color:var(--faint); margin-left:auto; }
/* Полоса здесь идёт во всю ширину: слева от неё нет колонки с номером. */
.spd-split__list .spd-rank__track { margin-left:0; height:6px; }
.spd-split__list .spd-rank__track span { background:var(--accent); transition:filter .16s ease; }
.spd-split__list .spd-rank { padding:6px 9px; margin:-6px -9px; border-radius:11px;
                             transition:background .16s ease; cursor:default; }
.spd-split__list .spd-rank:hover { background:var(--chip); }
.spd-split__list .spd-rank:hover .spd-rank__track span { filter:brightness(1.18); }
@media (max-width:1180px) {
  .spd-split { flex-direction:column; align-items:stretch; gap:6px; }
  .spd-donut { flex:0 0 auto; }
}

/* ----- 11. Таблицы -------------------------------------------------------- */
.spd-table-wrap { margin-top:18px; border:1px solid var(--line); border-radius:16px; overflow:hidden; background:var(--card); box-shadow:var(--shadow); }
.spd-table { width:100%; border-collapse:collapse; font-size:14px; }
.spd-table thead th { text-align:left; font-family:var(--mono); font-size:10.5px; letter-spacing:0.1em; color:var(--faint); font-weight:400; padding:16px 18px; border-bottom:1px solid var(--line); }
.spd-table tbody td { padding:15px 18px; border-bottom:1px solid var(--line); vertical-align:middle; }
.spd-table tbody tr:last-child td { border-bottom:none; }
.spd-table tbody tr:hover { background:var(--hover); }
.spd-table td.num, .spd-table th.num { text-align:right; font-family:var(--mono); }
.spd-table__main { font-weight:600; }
.spd-table__sub { font-size:12px; color:var(--faint); margin-top:2px; }

/* Класс .bad стоял в разметке дашборда и цен с самого начала, но нигде не был
   объявлен: подсветка «ниже пола» и «долго в нуле» не работала ни разу. */
.bad { color:var(--bad); }
.warn { color:var(--warn); }

/* Подсветка ячеек цены. Заливка только там, где речь о деньгах — убыток и
   работа впритык к полу. Всё остальное помечается значком: если красить
   каждый сигнал, таблица становится нечитаемой и перестают замечать главный. */
.spd-cell { position:relative; }
/* Заливка намеренно очень слабая: сигнал несёт цвет самой цифры, а фон только
   помогает найти строку глазом. Плотная заливка на полтораста строк утомляет
   и перестаёт читаться как предупреждение. */
.spd-cell--below { background:color-mix(in oklab, var(--bad), transparent 86%); }
.spd-cell--tight { background:color-mix(in oklab, var(--warn), transparent 86%); }
.spd-table tbody tr:hover .spd-cell--below { background:color-mix(in oklab, var(--bad), transparent 78%); }
.spd-table tbody tr:hover .spd-cell--tight { background:color-mix(in oklab, var(--warn), transparent 78%); }
.spd-cell--below .spd-cell__price { color:var(--bad); font-weight:700; }
.spd-cell--tight .spd-cell__price { color:var(--warn); font-weight:700; }
.spd-cell__flag { font-size:10px; margin-right:5px; opacity:.75; }
.spd-cell__flag--max { color:var(--accent); }
.spd-cell__flag--min { color:var(--info); }
/* Пол по неподтверждённой ставке — оценка, а не граница. Пунктир об этом и говорит. */
.spd-cell__est { border-bottom:1px dashed var(--warn-line); cursor:help; }

.spd-cellkey { display:flex; flex-wrap:wrap; gap:16px; align-items:center; font-size:12px; color:var(--muted); }
.spd-cellkey b { display:inline-block; width:12px; height:12px; border-radius:3px; margin-right:6px; vertical-align:-2px; }
.spd-search { display:flex; align-items:center; gap:9px; margin-top:18px; padding:13px 16px; border-radius:12px; border:1px solid var(--line); background:var(--chip); color:var(--muted); font-size:14px; max-width:420px; }
.spd-search input { border:none; background:transparent; outline:none; color:var(--ink); font:inherit; flex:1; }
.spd-cellnum { display:inline-grid; place-items:center; width:26px; height:26px; border-radius:7px; background:var(--active); color:var(--accent); font-family:var(--mono); font-size:11px; font-weight:700; }
.spd-actions { display:flex; gap:10px; justify-content:flex-end; color:var(--faint); }
.spd-actions span { cursor:pointer; }
.spd-actions span:hover { color:var(--ink); }

/* ----- 12. Статусы / теги / пилюли ---------------------------------------- */
.spd-status { display:inline-flex; align-items:center; gap:6px; font-size:12px; font-weight:600; padding:5px 11px; border-radius:999px; border:1px solid; }
.spd-status--ok   { color:var(--accent); background:var(--active); border-color:var(--glow); }
.spd-status--warn { color:var(--warn); background:var(--warn-bg); border-color:var(--warn-line); }
.spd-status--bad  { color:var(--bad); background:var(--bad-bg); border-color:color-mix(in oklab,var(--bad),transparent 60%); }
.spd-status--idle { color:var(--muted); background:var(--chip); border-color:var(--line); }
.spd-status--info { color:var(--info); background:color-mix(in oklab,var(--info),transparent 86%); border-color:color-mix(in oklab,var(--info),transparent 60%); }
.spd-tag { font-family:var(--mono); font-size:11px; padding:4px 9px; border-radius:7px; border:1px solid var(--line); background:var(--chip); color:var(--muted); }
.spd-pill { font-family:var(--mono); font-size:11px; font-weight:700; padding:4px 9px; border-radius:7px; }
.spd-pill--up   { color:var(--accent); background:var(--active); }
.spd-pill--down { color:var(--bad); background:var(--bad-bg); }
.spd-pill--warn { color:var(--warn); background:var(--warn-bg); }

/* ----- 13. Movers (растут/падают) ----------------------------------------- */
.spd-movers { margin-top:20px; display:grid; grid-template-columns:1fr 1fr; gap:14px 26px; }
.spd-movers__head { font-family:var(--mono); font-size:11px; letter-spacing:0.06em; margin-bottom:6px; }
.spd-movers__head--up { color:var(--accent); }
.spd-movers__head--down { color:var(--bad); }
.spd-mover { display:flex; align-items:center; gap:10px; padding:9px 0; border-bottom:1px solid var(--line); }
.spd-mover:last-child { border-bottom:none; }
.spd-mover__name { flex:1; font-size:13.5px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.spd-mover__val { font-family:var(--mono); font-size:13px; font-weight:700; }
.spd-mover__val--up { color:var(--accent); }
.spd-mover__val--down { color:var(--bad); }
.spd-mover__bar { width:46px; height:4px; border-radius:3px; margin-left:4px; }
.spd-mover__bar--up { background:var(--accent); }
.spd-mover__bar--down { background:var(--bad); }

/* ----- 14. Сегментный бар + здоровье -------------------------------------- */
.spd-segbar { margin-top:16px; display:flex; height:12px; border-radius:6px; overflow:hidden; }
.spd-segbar span { height:100%; }
/* Нижний отступ — не воздух ради воздуха: под легендой почти всегда идёт
   .spd-note, а у неё сверху граница и нет внешнего отступа, поэтому
   последняя строка легенды упиралась прямо в черту. */
.spd-seglegend { margin-top:14px; display:flex; flex-direction:column; gap:9px; padding-bottom:20px; }
.spd-seglegend__row { display:flex; align-items:center; gap:9px; font-size:13.5px; }
.spd-seglegend__row b { margin-left:auto; font-family:var(--mono); }
/* Строка легенды ведёт туда, где эти позиции лежат списком. Ссылкой становится
   только та, которой есть куда вести: подчёркивание там, где переход никуда
   не ведёт, обещает больше, чем экран может дать. */
a.spd-seglegend__row { color:inherit; text-decoration:none; border-radius:8px;
                       margin:-3px -6px; padding:3px 6px; transition:background .14s; }
a.spd-seglegend__row:hover { background:var(--chip); }
a.spd-seglegend__row:hover > span:nth-child(2) { color:var(--accent); }
.spd-bignum { font-size:34px; font-weight:800; font-family:var(--mono); letter-spacing:-0.03em; }
.spd-bignum + .spd-bignum__sub { font-size:12px; color:var(--faint); }

/* ----- 15. Тепловая матрица ----------------------------------------------- */
.spd-heat { margin-top:18px; display:flex; flex-direction:column; gap:6px; }
.spd-heat__legend { display:flex; align-items:center; gap:9px; font-family:var(--mono); font-size:11px; color:var(--faint); }
.spd-heat__legend i { width:120px; height:9px; border-radius:5px; background:linear-gradient(90deg,var(--track),var(--accent)); }
.spd-heat__row { display:grid; grid-template-columns:var(--heat-label,150px) repeat(var(--heat-cols,12),1fr); gap:6px; align-items:center; }
.spd-heat__row--head { font-family:var(--mono); font-size:10.5px; color:var(--faint); }
.spd-heat__row--head span { text-align:center; }
.spd-heat__name { font-size:13px; color:var(--ink); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.spd-heat__cell { height:30px; border-radius:6px; display:grid; place-items:center; font-family:var(--mono); font-size:10px;
                  background:var(--track); color:var(--faint); }
/* Ступени, а не непрерывная заливка из данных. Заливка, посчитанная в разметке,
   означала бы color-mix прямо в атрибуте style — в светлой теме те же значения
   дают нечитаемый текст. Тема решает цвет, данные решают только ступень. */
.spd-heat__cell--0 { background:var(--inset); }
.spd-heat__cell--1 { background:color-mix(in oklab,var(--accent),transparent 88%); color:var(--muted); }
.spd-heat__cell--2 { background:color-mix(in oklab,var(--accent),transparent 72%); color:var(--ink); }
.spd-heat__cell--3 { background:color-mix(in oklab,var(--accent),transparent 52%); color:var(--ink); }
.spd-heat__cell--4 { background:color-mix(in oklab,var(--accent),transparent 28%); color:var(--ink); }
.spd-heat__cell--5 { background:var(--accent); color:var(--on-accent); }
/* Пусто ≠ ноль. Ровная заливка нулевой ступенью на месте «мерить было нечем»
   читается как «продаж не было» — штриховка говорит, что данных нет вовсе. */
.spd-heat__cell--none { background:repeating-linear-gradient(45deg,transparent,transparent 3px,var(--line) 3px,var(--line) 4px);
                        color:transparent; }
/* Легенда ступенчатая: заливка в ячейках дискретная, и плавный градиент рядом
   обещал бы точность, которой в матрице нет. */
.spd-heat__legend b { display:flex; gap:3px; }
.spd-heat__legend b span { width:20px; height:9px; border-radius:3px; }
.spd-heat__sub { font-size:11px; color:var(--faint); }

/* Кликабельная матрица. Ячейка и заголовок — настоящие ссылки, а не div с
   обработчиком: работает средняя кнопка, работает Tab, адрес виден в статусной
   строке. Ячейка без позиций ссылкой НЕ становится — клик, открывающий пустоту,
   читается как поломка, а не как «здесь ничего нет».
   Выбранное обводится, а не перекрашивается: заливка означает деньги, и менять
   её под выделение значит врать о сумме в ячейке. */
a.spd-heat__cell { text-decoration:none; transition:box-shadow .14s, transform .14s; }
a.spd-heat__cell:hover { box-shadow:0 0 0 2px var(--glow); transform:translateY(-1px); }
.spd-heat__cell--on { box-shadow:0 0 0 2px var(--accent); }
.spd-heat__head { display:block; color:inherit; text-decoration:none; border-radius:6px;
                  padding:3px 2px; transition:background .14s, color .14s; }
.spd-heat__head:hover { background:var(--chip); color:var(--ink); }
.spd-heat__head--on { background:var(--active); color:var(--accent); }
.spd-heat__row--head .spd-heat__head { text-align:center; }

/* ----- 18в. Модальное окно ------------------------------------------------
   Серверный <dialog open>. Без JS это обычная панель в потоке страницы — так
   она и выглядит по умолчанию, поэтому здесь НЕ ставится ни position, ни
   width: браузерный стиль для dialog[open] задаёт position:absolute, и его
   надо перебить, иначе панель выпадет из потока и наедет на таблицы.
   Всё модальное включается классом, который вешает spd-modal.js. */
.spd-modal { position:static; display:block; width:auto; max-width:none; margin:22px 0 0;
             padding:0; overflow:hidden; color:var(--ink);
             border:1px solid var(--glow); border-radius:18px; background:var(--card);
             box-shadow:var(--shadow); }

.spd-modal--on { position:fixed; inset:0; margin:auto;
                 width:min(1120px, calc(100vw - 40px));
                 max-height:min(78vh, 780px);
                 display:flex; flex-direction:column;
                 border-color:var(--line); box-shadow:0 24px 60px rgba(0,0,0,.45); }
.spd-modal--on::backdrop { background:rgba(0,0,0,.55); }
/* Шапка не уезжает вместе со списком: в ней стоит и название фильтра, и счёт,
   и кнопка закрытия — прокрутив сотню строк, человек теряет всё сразу. */
.spd-modal--on .spd-modal__head { flex:0 0 auto; border-bottom:1px solid var(--line);
                                  background:var(--card); }
.spd-modal--on .spd-modal__body { flex:1 1 auto; overflow:auto; }
.spd-modal__head { padding:16px 18px; }
/* Заголовки таблицы липнут к верху тела окна — без этого на сотне строк
   становится непонятно, какая колонка есть какая. */
.spd-modal--on .spd-modal__body .spd-table thead th { position:sticky; top:0; z-index:1;
                                                      background:var(--card); }
@media (max-width:760px) {
  .spd-modal--on { width:calc(100vw - 20px); max-height:88vh; }
}

/* Ссылка внутри содержимого: подчёркивание появляется по наведению, чтобы
   таблица на сто строк не превращалась в синее полотно. */
.spd-link { color:inherit; text-decoration:none; }
.spd-link:hover { color:var(--accent); text-decoration:underline; }
.spd-link--on { color:var(--accent); }

/* Плитка-ссылка. Без href компонент остаётся div и этих правил не касается. */
a.spd-kpi { display:block; color:inherit; text-decoration:none;
            transition:border-color .14s, transform .14s; }
a.spd-kpi:hover { border-color:var(--glow); transform:translateY(-1px); }
.spd-kpi--on { border-color:var(--accent); }
/* Клавиатура обязана видеть фокус: без этого кликабельность есть только у мыши. */
a.spd-kpi:focus-visible, a.spd-heat__cell:focus-visible,
.spd-heat__head:focus-visible, .spd-link:focus-visible,
.spd-seglegend__row a:focus-visible { outline:2px solid var(--accent); outline-offset:2px; }

/* ----- 16. Парето (бары + кумулятивная кривая) ---------------------------- */
.spd-pareto { position:relative; margin-top:16px; height:170px; }
.spd-pareto__bars { position:absolute; inset:0; display:flex; align-items:flex-end; gap:8px; }
.spd-pareto__bars > span { flex:1; border-radius:4px 4px 0 0; background:var(--track); }
.spd-pareto__bars > span.on { background:linear-gradient(180deg,var(--bright),var(--accent)); }
.spd-pareto__svg { position:absolute; inset:0; width:100%; height:100%; overflow:visible; }
/* Кривая тянется по обеим осям (preserveAspectRatio="none"), поэтому без
   non-scaling-stroke её толщина растёт вместе с растяжением по горизонтали. */
.spd-pareto__svg polyline { fill:none; stroke:var(--info); stroke-width:1.5;
                            stroke-linejoin:round; vector-effect:non-scaling-stroke; }
.spd-pareto__svg line { stroke:var(--line); stroke-width:1; stroke-dasharray:3 3;
                        vector-effect:non-scaling-stroke; }
.spd-pareto__threshold { position:absolute; right:0; font-family:var(--mono); font-size:10px; color:var(--faint); }
/* Вертикаль порога: где именно кривая доходит до 80%. Без неё подпись «203
   позиции» приходится сопоставлять с картинкой на глаз. */
.spd-pareto__mark { position:absolute; top:0; bottom:0; width:1px; background:var(--info); opacity:.45; }
.spd-pareto__mark b { position:absolute; top:-2px; left:6px; font-family:var(--mono); font-size:10px;
                      color:var(--info); white-space:nowrap; font-weight:500; }

/* ----- 17. Scatter / квадранты -------------------------------------------- */
/* Поле облака точек. Пропорция широкая, и высота ОБЯЗАНА быть ограничена:
   при aspect-ratio 1.5/1 панель во всю ширину контента вырастала до 1300 px —
   экран пролистывался мимо, а точки оказывались за краем окна. Пропорция
   работает на узких экранах, max-height держит широкие. */
.spd-scatter { position:relative; margin-top:16px; aspect-ratio:2.4/1;
               min-height:300px; max-height:440px; border-radius:14px;
               border:1px solid var(--line); background:var(--inset); overflow:hidden; }
/* Оси двигаются: делить поле пополам можно, только если середина шкалы и есть
   граница смысла. У «запаса до пола» граница — ноль, и он редко приходится
   ровно на середину диапазона. */
.spd-scatter__axv { position:absolute; left:var(--ax-x,50%); top:0; bottom:0; width:1px; background:var(--line); }
.spd-scatter__axh { position:absolute; top:var(--ax-y,50%); left:0; right:0; height:1px; background:var(--line); }
.spd-scatter__dot { position:absolute; border-radius:50%; transform:translate(-50%,-50%);
                    background:var(--accent); opacity:.65; transition:opacity .15s; }
.spd-scatter__dot--warn { background:var(--warn); }
.spd-scatter__dot--bad { background:var(--bad); opacity:.8; }
.spd-scatter__dot:hover { opacity:1; }
.spd-scatter__lbl { position:absolute; transform:translate(-50%,-50%); font-size:11px; color:var(--muted); white-space:nowrap; }
.spd-scatter__ax { position:absolute; font-size:10px; color:var(--faint); font-family:var(--mono); }
/* Углы подписаны решением, а не координатой. «Высокая скорость при низком
   запасе» человек обязан переводить в действие сам; «поднимать цену» —
   не обязан. */
.spd-scatter__corner { position:absolute; font-family:var(--mono); font-size:10px; letter-spacing:.06em;
                       color:var(--faint); text-transform:uppercase; pointer-events:none; max-width:44%; }
.spd-scatter__corner--tl { left:12px; top:10px; }
.spd-scatter__corner--tr { right:12px; top:10px; text-align:right; }
.spd-scatter__corner--bl { left:12px; bottom:10px; }
.spd-scatter__corner--br { right:12px; bottom:10px; text-align:right; }
.spd-scatter__tint { position:absolute; pointer-events:none; }
.spd-scatter__tint--bad { background:color-mix(in oklab,var(--bad),transparent 94%); }
.spd-scatter__tint--warn { background:color-mix(in oklab,var(--warn),transparent 94%); }

/* ----- 18. Формы ---------------------------------------------------------- */
.spd-field { display:flex; flex-direction:column; gap:7px; }
.spd-field__label { font-family:var(--mono); font-size:10.5px; letter-spacing:0.1em; color:var(--faint); }
.spd-input { padding:11px 14px; border-radius:11px; border:1px solid var(--line); background:var(--inset); color:var(--ink); font:inherit; font-size:14px; outline:none; transition:border-color .15s, box-shadow .15s; }
.spd-input::placeholder { color:var(--faint); }
.spd-input:focus { border-color:var(--accent); box-shadow:0 0 0 3px var(--active); }
.spd-toggle-btn { display:inline-flex; align-items:center; gap:8px; padding:9px 14px; border-radius:10px; border:1px solid var(--glow); background:var(--active); color:var(--accent); font-size:13px; font-weight:600; cursor:pointer; }

/* Форма-таблица (Планы) */
.spd-formtable { margin-top:18px; border:1px solid var(--line); border-radius:16px; overflow:hidden; background:var(--card); box-shadow:var(--shadow); }
.spd-formtable__head, .spd-formtable__row { display:grid; grid-template-columns:1.6fr 1fr 1fr 1fr; gap:14px; align-items:center; padding:13px 18px; }
.spd-formtable__head { font-family:var(--mono); font-size:10.5px; letter-spacing:0.1em; color:var(--faint); border-bottom:1px solid var(--line); }
.spd-formtable__head span:not(:first-child), .spd-formtable__row .spd-input { text-align:right; }
.spd-formtable__row { border-bottom:1px solid var(--line); }
.spd-formtable__row:last-child { border-bottom:none; }
.spd-formtable__name { font-size:14px; }

/* Лог-строки */
.spd-log { margin-top:16px; display:flex; flex-direction:column; }
.spd-log__row { display:grid; grid-template-columns:1.6fr auto 1fr auto; gap:18px; align-items:center; padding:13px 4px; border-bottom:1px solid var(--line); font-size:13px; }
.spd-log__row:last-child { border-bottom:none; }
.spd-log__src { display:flex; align-items:center; gap:9px; color:var(--ink); }
.spd-log__nums, .spd-log__when { font-family:var(--mono); font-size:12px; color:var(--faint); text-align:right; }

/* Аватар */
.spd-avatar { width:28px; height:28px; border-radius:8px; display:grid; place-items:center; font-family:var(--mono); font-size:11px; font-weight:700; color:var(--on-accent); background:linear-gradient(160deg,var(--bright),var(--accent)); flex:0 0 auto; }

/* Выбор периода — двухмесячный календарь (spd-daterange.js) */
.spd-daterange { position:relative; display:inline-block; }
.spd-daterange__trigger { display:flex; align-items:center; gap:10px; cursor:pointer; min-width:230px; text-align:left; background:var(--inset); }
.spd-daterange__trigger::after { content:"▾"; margin-left:auto; color:var(--faint); font-size:10px; }

.spd-daterange__popover { position:absolute; top:calc(100% + 8px); left:0; z-index:40; padding:22px; border-radius:18px; border:1px solid var(--line); background:var(--card); box-shadow:var(--shadow); width:max-content; }
.spd-daterange__popover[hidden] { display:none; }

.spd-daterange__nav { display:flex; align-items:center; justify-content:space-between; margin-bottom:16px; gap:40px; }
.spd-daterange__arrow { display:grid; place-items:center; width:28px; height:28px; border-radius:8px; border:1px solid var(--line); background:var(--chip); color:var(--muted); cursor:pointer; flex:0 0 auto; }
.spd-daterange__arrow:hover { background:var(--hover); color:var(--ink); }
.spd-daterange__arrow[disabled] { opacity:.35; cursor:default; pointer-events:none; }

.spd-daterange__months { display:flex; gap:28px; }
.spd-daterange__month-title { text-align:center; font-weight:700; font-size:14px; margin-bottom:12px; }

.spd-cal { border-collapse:collapse; }
.spd-cal th { font-family:var(--mono); font-size:10px; letter-spacing:0.08em; color:var(--faint); font-weight:500; padding:2px 5px 8px; }
.spd-cal td { padding:1px; text-align:center; }
.spd-day { display:grid; place-items:center; width:32px; height:32px; border-radius:9px; font-size:13px; color:var(--ink); cursor:pointer; }
.spd-day:hover { background:var(--hover); }
.spd-day--muted { color:var(--faint); }
.spd-day--disabled { color:var(--faint); opacity:.35; cursor:default; pointer-events:none; }
.spd-day--today { font-weight:700; color:var(--accent); }
.spd-day--in-range { background:var(--active); border-radius:0; }
.spd-day--start, .spd-day--end { background:var(--accent); color:var(--on-accent); font-weight:700; }
.spd-day--start { border-radius:9px 0 0 9px; }
.spd-day--end { border-radius:0 9px 9px 0; }
.spd-day--start.spd-day--end { border-radius:9px; }

.spd-daterange__foot { display:flex; justify-content:flex-end; gap:10px; margin-top:18px; padding-top:16px; border-top:1px solid var(--line); }

/* Пустое состояние */
.spd-empty { margin-top:40px; text-align:center; color:var(--faint); padding:60px 20px; border:1px dashed var(--line); border-radius:16px; }
.spd-empty__title { font-size:16px; font-weight:600; color:var(--muted); margin-top:12px; }
/* Внутри панели рамка не нужна — она уже есть у самой панели. Вариант вместо
   восьми копий одного и того же инлайна по шаблонам. */
.spd-empty--inline { margin-top:0; padding:40px 18px; border:0; line-height:1.6; }

/* ----- 18б. Мелкие примитивы ----------------------------------------------
   Приехали из инлайнового <style> в layouts/app.blade.php. Там им было не
   место: .spd-note стоит в десяти местах шести шаблонов, .spd-sub — почти в
   каждой таблице. Это система, а не добавка одного макета, и держать её в
   разметке значит однажды получить второй такой блок в другом layout. */

/* Сноска под панелью: КАК посчитано и чему нельзя верить. Не путать с
   .spd-hint — та прячется под иконкой у заголовка и объясняет, ЧТО за число. */
.spd-note { padding:14px 18px; border-top:1px solid var(--line); font-size:12.5px;
            color:var(--muted); line-height:1.55; }
/* Подпись под значением в ячейке таблицы. */
.spd-sub { font-size:11px; color:var(--faint); margin-top:2px; }
/* Широкую таблицу прокручиваем внутри её обёртки, а не страницей. */
.spd-scroll { overflow-x:auto; }
/* Вертикальный ряд панелей. */
.spd-stack { display:flex; flex-direction:column; gap:22px; }
/* Нейтральная таблетка: у .spd-pill без модификатора нет ни фона, ни цвета,
   а «нет в наличии» нужен спокойный вариант, а не красный. */
.spd-pill:not([class*="--"]) { color:var(--muted); background:var(--chip);
                               border:1px solid var(--line); font-weight:500; }
.spd-table td.bad { font-weight:600; }
.spd-input { width:100%; margin-top:7px; }
label > .spd-kpi__label { display:block; }

/* ----- 19. Ассистент -------------------------------------------------------- */
.spd-assistant-trace { border-top:1px solid var(--line); padding-top:14px; }
.spd-assistant-trace summary { cursor:pointer; font-family:var(--mono); font-size:11px; letter-spacing:0.1em; color:var(--faint); }
.spd-assistant-trace summary:hover { color:var(--muted); }
.spd-assistant-trace__list { margin-top:10px; display:flex; flex-direction:column; gap:6px; }
.spd-assistant-step { font-family:var(--mono); font-size:12.5px; color:var(--muted); }
.spd-assistant-answer { font-size:14.5px; line-height:1.6; color:var(--ink); white-space:pre-line; }
.spd-assistant-proposal { padding:16px 18px; background:var(--inset); box-shadow:none; }
.spd-assistant-spinner { display:flex; align-items:center; gap:10px; color:var(--muted); font-size:13.5px; }
.spd-assistant-spinner::before { content:""; width:14px; height:14px; border-radius:50%; border:2px solid var(--line); border-top-color:var(--accent); animation:spd-spin .8s linear infinite; flex:0 0 auto; }

/* ----- 20. Адаптив -------------------------------------------------------- */
@media (max-width:1240px){
  .spd-kpis { grid-template-columns:repeat(3,1fr); }
  .spd-charts, .spd-breakdown, .spd-grid-2, .spd-movers { grid-template-columns:1fr; }
  .spd-grid-4 { grid-template-columns:repeat(2,1fr); }
}
@media (max-width:760px){
  .spd-app { grid-template-columns:1fr; }
  .spd-sidebar { position:static; height:auto; }
  .spd-kpis, .spd-grid-3, .spd-grid-4 { grid-template-columns:repeat(2,1fr); }
  .spd-content { padding:20px; }
}
