/* css-bundle.php: 28 файлов css/ в порядке index.php */
/* === VARIABLES.CSS === */
/* demo/assets/css/variables.css */
/* НАЗНАЧЕНИЕ: дизайн-токены демо. Каркас и плотность взяты из рабочего кабинета
   Ozonteh (biomass), чтобы демо и боевой продукт читались одной системой.
   Светлая тема сделана ДЕФОЛТНОЙ: Дмитрию понравился светлый Strimix, а демо
   показывают с проектора, где темный фон теряет контраст. Палитра при этом
   своя - индиго вместо бирюзы Strimix, чтобы не выглядеть его копией. */
/* СВЯЗИ: main.css, components.css, theme-init.js. Никакой другой файл не объявляет :root */
/* РАЗМЕР: ~240 строк */

/* ============================================================
   СВЕТЛАЯ ТЕМА - ДЕФОЛТ (:root)
   Темная живет ниже в html.dark-theme и переопределяет те же имена.
   ============================================================ */

:root {
    /* --- Поверхности (страница -> панель -> строка -> наведение) ---
       Фон страницы чуть холоднее белого, панель чистый белый: так карточка
       сама отделяется от фона и не нужны лишние рамки. Прием Strimix. */
    --bg:            oklch(0.964 0.005 260);
    --bg-0:          oklch(1.00 0 0);
    --surface:       oklch(1.00 0 0);
    --surface-2:     oklch(0.982 0.003 265);
    --surface-hover: oklch(0.955 0.006 265);

    /* --- Линии. Мягкие: на светлом фоне жесткая рамка рвет композицию --- */
    --border:        oklch(0.915 0.005 265);
    --border-strong: oklch(0.845 0.010 265);

    /* --- Текст. Яскравість підібрана за контрастом, а не на око.
           Виміряно в браузері 28.09 (wave F1): text 16.46, dim 7.77,
           muted 5.51 на surface; muted проходить AA 4.5 на всіх трьох
           підкладках (surface 5.51, surface-2 5.23, bg 4.96). До правки
           muted на bg давав 4.37 - провал AA, тому L 0.55 -> 0.52. --- */
    --text:  oklch(0.24 0.020 265);
    --dim:   oklch(0.44 0.015 265);
    --muted: oklch(0.52 0.014 265);

    /* --- Акцент: синій у ролі бірюзи Strimix (#00BCD4 -> синій за вимогою
           Дмитра). Активна вкладка, кнопки, посилання, перша серія графіків.
           Білий текст на заливці проходить, кольоровий текст по білому теж.
           F1: L 0.55 -> 0.52 - текст-посилання на --bg давав 4.31, провал AA.
           --blue і --c-1 тримаємо синхронно: перша серія = акцент. --- */
    --accent:      oklch(0.52 0.18 250);
    --accent-soft: oklch(0.52 0.18 250 / 0.10);
    --accent-line: oklch(0.52 0.18 250 / 0.28);

    /* --- Спокійні акценти каркаса: синій і жовтий. Синій - головний маркер
           (активний розділ, сегмент періоду, смуга заголовка). Жовтий - тиха
           підсвітка (плашка демо, лічильник уваги). Обидва приглушені, щоб не
           сперечатися з семафором ok/warn/err і не кричати з проектора. --- */
    --blue:        oklch(0.52 0.18 250);
    --blue-soft:   oklch(0.52 0.18 250 / 0.10);
    --blue-line:   oklch(0.52 0.18 250 / 0.30);
    --yellow:      oklch(0.74 0.15 82);
    --yellow-soft: oklch(0.74 0.15 82 / 0.18);
    --yellow-line: oklch(0.74 0.15 82 / 0.42);

    /* --- Семантика: зеленый - желтый - оранжевый. КРАСНОГО НЕТ (Николай 02.10:
           красный читается "российским", акценты сине-желтые как на флаге).
           Метка (полоса/точка/столбик) - яркий токен; текст - --*-text (AA). --- */
    --ok:    oklch(0.52 0.15 145);
    --warn:  #FFD500;   /* флаговый желтый: метки "увага" (= темная тема) */
    --err:   #F97316;   /* чистый оранжевый: метки "критично" (Николай: #EA580C тянул в красный) */
    --warn-text: #7A5A00; /* текст "увага" на белом и на плашке */
    --err-text:  #C2410C; /* текст "критично" на белом и на плашке */
    --ok-soft:   oklch(0.52 0.15 145 / 0.11);
    --warn-soft: rgba(255,213,0,.24);
    --err-soft:  rgba(249,115,22,.13);
    --ok-line:   oklch(0.52 0.15 145 / 0.30);
    --warn-line: rgba(255,213,0,.60);
    --err-line:  rgba(249,115,22,.42);

    /* Текст ПОВЕРХ заливки цветом. Заливки в светлой теме темные,
       поэтому сверху белый (ok/err/accent); warn-заливка яркая - сверху темный --on-warn. */
    --on-ok:     oklch(0.99 0.005 145);
    --on-warn:   oklch(0.25 0.03 75);
    --on-err:    #fff;
    --on-accent: oklch(0.99 0.005 250);
    --on-blue:   oklch(0.99 0.005 250);
    --on-yellow: oklch(0.25 0.03 82);

    /* --- SUPERDESIGN п.2: живые Strimix-токены, имена 1:1. Канонический источник
           для sd-*.css/js. Легаси-имена выше (--bg/--surface/--text/...) НЕ тронуты:
           старый демо-CSS продолжает работать до переезда потоков S2-S4. --- */
    /* Бренд = спокійний синій --blue (oklch 0.52 0.18 250 = #0069CA), не бірюза Strimix:
       Дмитро 25.09 - "спокійні синій і жовтий в акцентах". Один синій на весь сайт
       (Tabler primary теж --blue, brand.css:34). */
    --brand: #0069CA; --brand-hover: #007CD9; --brand-active: #005AB5; --brand-contrast: #fff;
    --brand-tint-8: rgba(0,105,202,.08); --brand-tint-12: rgba(0,105,202,.12); --brand-tint-30: rgba(0,105,202,.3);
    --cyan-50: #E0F7FA; --cyan-100: #B2EBF2; --cyan-200: #80DEEA; --cyan-300: #4DD0E1; --cyan-400: #26C6DA;
    --cyan-500: #00BCD4; --cyan-600: #00ACC1; --cyan-700: #0097A7; --cyan-800: #00838F; --cyan-900: #006064;
    --text-strong: #16202E; --text-body: #334155; --text-muted: #64748B; --text-faint: #94A3B8;
    --text-brand: #0054AA; --text-link: #0069CA;
    --surface-app: #F5F7F9; --surface-card: #FFFFFF; --surface-sunken: #F8FAFC;
    --surface-auth: #F1F5F9; --surface-tint: #E4F2FF;
    --border-subtle: #EDF1F5; --border-default: #DCE3EA; --border-strong: #C4CDD6;
    --positive: #12A76D; --positive-bg: #E7F6F0; --negative: #F97316; --negative-bg: #FEF0E6;
    --warning: #FFD500; --warning-bg: #FFF6CC; --info: #2563EB; --info-bg: #EBF1FE;
    --viz-1: #0069CA; --viz-2: #7A5AF8; --viz-3: #12A76D; --viz-4: #FFD500; /* 02.10: жовтий як на прапорі (#FFD700), той самий, що в темній (Николай: #F4C000 на білому брудний) */
    --viz-5: #F97316; --viz-6: #2F6B7C; --viz-7: #B26AF8; --viz-8: #5AC8FA;
    /* Синяя шкала для донатов (AUDIT_02_10 "Цвет"): многоцветные сегменты уходят,
       каналы различаются глубиной синего. JS берет через Charts.token('--shade-blue-N').
       --viz-* НЕ тронуты: смена viz-2/viz-4 перекрасила бы вторые серии всех графиков. */
    --shade-blue-1: #0069CA; --shade-blue-2: #2F8BD8; --shade-blue-3: #5FA8E2;
    --shade-blue-4: #93C4EB; --shade-blue-5: #C2DFF4;
    --plan-bar: #93C4EB; /* бары "План" (views.js:2790 cPlan): в светлой = shade-blue-4, в темной приглушен. */
    /* Категорії донатів (Задание 6): 5 спокійних розрізнюваних кольорів.
       Синій бренд першим; без фіолетового/червоного; золото приглушене
       (не читається як warn), сланець синюватий (не "вимкнений" сірий). */
    --cat-1: #0069CA; --cat-2: #1B9AAA; --cat-3: #FFD500; --cat-4: #3DA35D; --cat-5: #7E93B8;
    --band-funnel: #159A7D; --band-lead: #2F6B7C; --band-sales: #EF9D17;
    --radius-xs: 6px; --radius-sm: 6px; --radius-md: 10px; --radius-lg: 10px; --radius-card: 10px; --radius-pill: 999px;
    --shadow-xs: 0 1px 2px rgba(20,32,46,.05); --shadow-sm: 0 2px 6px rgba(20,32,46,.06);
    --shadow-md: 0 4px 14px rgba(20,32,46,.08); --shadow-lg: 0 10px 30px rgba(20,32,46,.14);
    --shadow-xl: 0 18px 50px rgba(20,32,46,.18); --shadow-brand: 0 6px 18px rgba(0,105,202,.25);
    --ring-focus: 0 0 0 3px rgba(0,105,202,.25);
    /* Шрифты: --font-mono НЕ переопределяем (остается IBM Plex Mono по клаузе
       "Roboto НЕ подключать", контракт п.2). --font-sans ниже - fallback-порядок
       подписей графиков (Plex первым ставит charts.js поверх этого списка). */
    --font-sans: -apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
    --fs-overline: 11px; --fs-caption: 12px; --fs-sm: 13px; --fs-body: 14px; --fs-body-lg: 15px;
    --fs-h4: 18px; --fs-h3: 22px; --fs-h2: 26px; --fw-regular: 400; --fw-medium: 500; --fw-bold: 700;
    /* ВНИМАНИЕ: --lh-body/--lh-snug переведены на шкалу Strimix (1.5/1.3, было
       1.45/1.35) - контракт требует имена 1:1, потоки S2-S4 верстают по новым. */
    --lh-body: 1.5; --lh-snug: 1.3; --ls-overline: .06em;
    --dur-fast: 120ms; --dur-base: 200ms; --dur-slow: 320ms;
    --ease-standard: cubic-bezier(.2,.6,.2,1); --ease-emphasized: cubic-bezier(.2,.8,.2,1);

    /* --- Палитра графиков = алиасы на --viz-1..6 (контракт п.2). Старые вызовы
           Charts.palette() и любой CSS на --c-N автоматически получают Strimix. --- */
    --c-1: var(--viz-1);
    --c-2: var(--viz-2);
    --c-3: var(--viz-3);
    --c-4: var(--viz-4);
    --c-5: var(--viz-5);
    --c-6: var(--viz-6);

    /* --- Шкала шрифта. Вісім сходинок, чотири рівні ієрархії:
           підписи 12-13 / текст 14-16 / заголовки 18-24 / цифри clamp.
           F1: fs-1 11 -> 12 (проектор не читає 11px), fs-2 12 -> 13,
           fs-4 15 -> 16 (сходинка 14 -> 15 не давала ієрархії).
           rem в проекті не використовується, масштабуються тільки споживачі токенів. --- */
    --fs-1: 12px;   /* підписи під цифрою, легенди */
    --fs-2: 13px;   /* шапки таблиць, чипи, мітки */
    --fs-3: 14px;   /* основний текст інтерфейсу */
    --fs-4: 16px;   /* акцентний рядок, комірка з числом */
    --fs-5: 18px;   /* заголовок панелі */
    --fs-6: 24px;   /* заголовок сторінки */
    --fs-7: clamp(26px, 1.6vw + 14px, 36px);    /* велика цифра KPI, текуча */
    --fs-8: clamp(18px, 0.7vw + 13px, 24px);    /* цифра другого плану */

    /* --- Висота рядка. Чотири сходинки замість розкиданих 1.2-1.6 по файлах:
           tight - великі цифри KPI (Manrope, display-кегль, свій режим);
           snug - заголовки панелей і дворядкові комірки таблиць;
           body - база інтерфейсу, збігається з body в main.css;
           read - абзаци для читання (знахідки, чат, пояснення в панелях).
           Значення ті самі, що були літералами: вигляд не змінюється,
           але наступна правка щільності робиться в одному місці. --- */
    --lh-tight: 1.2;
    /* --lh-snug/--lh-body заданы выше в Strimix-блоке (1.3/1.5) - дубль удален,
       иначе каскад молча откатывал бы шкалу Strimix к старым 1.35/1.45. */
    --lh-read: 1.55;
    /* F1: display - тільки великі цифри KPI (fs-7/fs-8, Manrope).
       Значення збігається з літералом 1.1 у planfact.css - вигляд не міняється,
       наступна правка щільності цифр робиться тут. */
    --lh-display: 1.1;

    /* --- Табличні цифри. F1: один токен замість розкиданого літерала
           tabular-nums по ~10 селекторах. Вживання:
           font-variant-numeric: var(--tnum); --- */
    --tnum: tabular-nums;

    /* --- Чорнило графіків. F1: charts.js читає токени через Charts.token(),
           тож окремі імена не ламають оркестратор - прохання перемкнути
           text/grid на них лежить у звіті _wave-f/reports/f1-tokens.md.
           ink темніше за muted: підписи осей кеглем 10-11px дрібніші за текст,
           їм потрібен вищий контраст (dim-рівень: 7.77 проти 5.51 у muted). --- */
    --chart-ink:  var(--text-muted);
    --chart-grid: var(--border-subtle);

    /* --- Отступы. Подняты относительно кабинета: демо смотрят с проектора
           метров с трех, там плотность 10px читается как каша. Шкала осталась
           кратной четырем, чтобы верстка не поехала. --- */
    --sp-1: 4px;
    --sp-2: 8px;
    --sp-3: 12px;
    --sp-4: 16px;
    --sp-5: 24px;
    --sp-6: 32px;
    /* Ритм вертикали. Один --gap на все давал плоскую стену одинаковых
       интервалов. Теперь три ступени: внутри группы, между блоками,
       между смысловыми разделами страницы. */
    --gap-xs: var(--sp-1);   /* 4  - внутри строки */
    --gap-s:  var(--sp-2);   /* 8  - элементы одной группы */
    --gap:    var(--sp-3);   /* 12 - между карточками в сетке */
    --gap-l:  var(--sp-5);   /* 24 - между смысловыми блоками раздела */
    --gap-xl: var(--sp-6);   /* 32 - перед новой темой на странице */

    /* --- Скругления. 02.10 (Николай): одна шкала на весь продукт -
           элементы управления 6px, карточки и окна 10px. Было две шкалы
           (--radius-* 6/8/10/12/16 и --r-* 6/10/16) - углы гуляли от экрана
           к экрану. 16px карточек читались мягко-"шаблонно". --- */
    --r-1: 6px;
    --r-2: 10px;
    --r-3: 10px;
    --r-pill: 999px;
    --radius: var(--r-2);

    /* --- Тени. На светлой теме тень заменяет часть рамок --- */
    --shadow:   0 1px 2px oklch(0.30 0.03 265 / 0.06), 0 1px 1px oklch(0.30 0.03 265 / 0.04);
    --shadow-2: 0 8px 24px oklch(0.30 0.03 265 / 0.10), 0 2px 6px oklch(0.30 0.03 265 / 0.06);

    /* --- Шрифты и движение --- */
    /* Пара Manrope + Roboto (оба OFL, грузятся из Fontsource через jsdelivr,
       подключение в <head> index.php). Roboto - лицо Strimix (замер one-shot-ui,
       confidence 0.95). 29.09, волна "дизайн на максимум": ушли от копии
       Strimix (Manrope+Roboto) на одну семью IBM Plex Sans Variable - Roboto
       шаблонный шрифт ИИ-интерфейсов (запрет frontend-design), а два рисунка
       цифр в соседних ячейках спорили. Display и body - одна гарнитура,
       иерархию дают размер и вес (роли в b-type.css), а не смена шрифта. */
    --font-display: 'Geologica Variable', 'IBM Plex Sans Variable', 'IBM Plex Sans', -apple-system, sans-serif;
    --font-body: 'IBM Plex Sans Variable', 'IBM Plex Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
    --font-mono: 'IBM Plex Mono', ui-monospace, Menlo, Consolas, monospace;
    --ease-out: cubic-bezier(0.25, 1, 0.5, 1);
    --transition: 140ms var(--ease-out);
}

/* ============================================================
   ТЕМНАЯ ТЕМА - альтернатива (html.dark-theme)
   Смысловые цвета светлеют: на темном фоне темная заливка не читается,
   поэтому текст поверх заливки становится темным, а не белым.
   ============================================================ */

html.dark-theme {
    --bg:            oklch(0.18 0.012 265);
    --bg-0:          oklch(0.15 0.012 265);
    --surface:       oklch(0.205 0.012 265);
    --surface-2:     oklch(0.235 0.014 265);
    --surface-hover: oklch(0.265 0.018 265);

    --border:        oklch(0.32 0.012 265);
    --border-strong: oklch(0.42 0.016 265);

    --text:  oklch(0.93 0.006 265);
    --dim:   oklch(0.77 0.012 265);
    --muted: oklch(0.70 0.012 265);

    /* Акцент светлее и чуть менее насыщенный: чистое индиго на темном фоне
       вибрирует. Как текст на панели дает 6.98, как заливка под темный текст 7.07. */
    --accent:      oklch(0.72 0.15 252);
    --accent-soft: oklch(0.72 0.15 252 / 0.16);
    --accent-line: oklch(0.72 0.15 252 / 0.36);

    --blue:        oklch(0.72 0.15 252);
    --blue-soft:   oklch(0.72 0.15 252 / 0.16);
    --blue-line:   oklch(0.72 0.15 252 / 0.38);
    --yellow:      oklch(0.80 0.14 85);
    --yellow-soft: oklch(0.80 0.14 85 / 0.16);
    --yellow-line: oklch(0.80 0.14 85 / 0.40);

    --ok:    oklch(0.74 0.16 145);
    --warn:  #FFD500;
    --err:   #FF8A3D;
    --warn-text: #FFD500; /* на темном яркие метки читаются и текстом */
    --err-text:  #FF9A52;
    --ok-soft:   oklch(0.74 0.16 145 / 0.15);
    --warn-soft: rgba(255,213,0,.16);
    --err-soft:  rgba(255,138,61,.16);
    --ok-line:   oklch(0.74 0.16 145 / 0.36);
    --warn-line: rgba(255,213,0,.40);
    --err-line:  rgba(255,138,61,.42);

    /* Заливки светлые - сверху темный текст: 8.44 / 9.05 / 5.88 / 7.07. */
    --on-ok:     oklch(0.19 0.03 145);
    --on-warn:   oklch(0.19 0.03 75);
    --on-err:    oklch(0.19 0.03 25);
    --on-accent: oklch(0.19 0.03 252);
    --on-blue:   oklch(0.19 0.03 252);
    --on-yellow: oklch(0.22 0.03 85);

    /* --- SUPERDESIGN dark: все surface/text/border/viz/semantic переопределены.
           Замерено скриптом (relative luminance, WCAG): text-strong #F1F5F9 на
           surface-card #1E293B = 13.35 (требование >= 7); text-body #CBD5E1 = 9.85;
           text-muted #94A3B8 = 5.71 (требование >= 4.5); text-muted на sunken = 7.30.
           Светлая --viz-6 (#2F6B7C) на темном не читалась бы - осветлена до #2DD4BF. --- */
    --text-strong: #F1F5F9; --text-body: #CBD5E1; --text-muted: #94A3B8; --text-faint: #64748B;
    /* Синій бренд у темній: --blue dark (oklch 0.72 0.15 252 = #57A8FF). Білий текст
       на ньому нечитабельний (2,6:1) - на активних вкладках темний --brand-contrast. */
    --brand: #57A8FF; --brand-hover: #77BCFF; --brand-active: #3F95F0; --brand-contrast: #0B1220;
    --brand-tint-8: rgba(87,168,255,.10); --brand-tint-12: rgba(87,168,255,.16); --brand-tint-30: rgba(87,168,255,.32);
    --text-brand: #77BCFF; --text-link: #77BCFF;
    --surface-app: #0F172A; --surface-card: #14171D; --surface-sunken: #0B1220;
    --surface-auth: #111C2E; --surface-tint: #0F2F50;
    --border-subtle: #223047; --border-default: #334155; --border-strong: #475569;
    --positive: #34D399; --positive-bg: rgba(18,167,109,.18);
    --negative: #FF8A3D; --negative-bg: rgba(255,138,61,.18);
    --warning: #FFD500; --warning-bg: rgba(255,213,0,.16);
    --info: #60A5FA; --info-bg: rgba(37,99,235,.20);
    --viz-1: #57A8FF; --viz-2: #A78BFA; --viz-3: #34D399; --viz-4: #FFD500;
    --viz-5: #FF8A3D; --viz-6: #2DD4BF; --viz-7: #C084FC; --viz-8: #7DD3FC;
    /* Темная пара синей шкалы донатов: осветлены под карту #1E293B. */
    --shade-blue-1: #57A8FF; --shade-blue-2: #74B4FF; --shade-blue-3: #93C6FF;
    --shade-blue-4: #B3D8FF; --shade-blue-5: #D2E9FF;
    --plan-bar: #5B7FA6; /* приглушен: 4.30 на карте против 7.22 у "Факт" (Задание 7 п.3). */
    /* Темна пара категорій: бірюзовий холодніший за зелений (була пара
       #2DD4BF/#34D399, що зливалася), золото бліде, сланець світлий. */
    --cat-1: #57A8FF; --cat-2: #3FC1C9; --cat-3: #FFD500; --cat-4: #4CC38A; --cat-5: #9DB1D4;
    --band-funnel: #34D399; --band-lead: #2DD4BF; --band-sales: #FBBF24;
    --shadow-xs: 0 1px 2px rgba(0,0,0,.30); --shadow-sm: 0 2px 6px rgba(0,0,0,.34);
    --shadow-md: 0 4px 14px rgba(0,0,0,.38); --shadow-lg: 0 10px 30px rgba(0,0,0,.44);
    --shadow-xl: 0 18px 50px rgba(0,0,0,.50); --shadow-brand: 0 6px 18px rgba(87,168,255,.28);
    --ring-focus: 0 0 0 3px rgba(87,168,255,.30);

    --c-1: var(--viz-1);
    --c-2: var(--viz-2);
    --c-3: var(--viz-3);
    --c-4: var(--viz-4);
    --c-5: var(--viz-5);
    --c-6: var(--viz-6);

    /* F1: темні значення нових токенів. Текст у темній темі проходить AA
       і без правок (muted 6.24-7.04), тому міняємо тільки додані токени.
       --tnum і --lh-display від теми не залежать, перевизначення не треба. */
    --chart-ink:  var(--text-muted);
    --chart-grid: var(--border-subtle);

    --shadow:   0 1px 2px oklch(0 0 0 / 0.28);
    --shadow-2: 0 8px 24px oklch(0 0 0 / 0.38), 0 2px 6px oklch(0 0 0 / 0.24);
}

/* Без этого при переключении темы мигает вся палитра */
html.no-transition,
html.no-transition * {
    transition: none !important;
}
/* === SD-TOKENS.CSS (S1, НОВЫЙ) === */
/* demo/assets/css/sd-tokens.css */
/* НАЗНАЧЕНИЕ: супер-дизайн Strimix+, поток S1. Слой поверх variables.css:
   замеренные значения компонентов (таб/ячейка/легенда/каналы), базовые
   карточка/KPI/пилюля по контракту п.3.1 и мосты к классам Tabler.
   Канонические токены п.2 (--brand/--viz/--surface/...) живут в variables.css
   и здесь НЕ дублируются - только ссылаются через var() с фолбэком. */
/* СВЯЗИ: variables.css (канон), index.php (подключение после variables),
   charts.js (читает --viz), S2-S4 (строят таблицы/графики/вьюхи поверх). */
/* ГРАНИЦЫ: табличные/фильтровые/графиковые стили - владения S2/S3, здесь
   только база. Селекторы префикса sd- + точечные .status-мосты Tabler. */
/* РАЗМЕР: ~300 строк */

/* ============ 1. Замеренные значения компонентов (контракт п.2, 1:1) ============ */
:root {
    /* Таб неактивный: transparent/#64748B/13px-500/pad 9x16;
       активный: #00BCD4/#fff/13px-500/pad 9x16/radius-top 8px */
    --sd-tab-fs: var(--fs-sm, 13px);
    --sd-tab-fw: var(--fw-medium, 500);
    --sd-tab-pad: 9px 16px;
    --sd-tab-idle-fg: var(--text-muted, #64748B);
    --sd-tab-active-bg: var(--brand, #00BCD4);
    --sd-tab-active-fg: var(--brand-contrast, #fff);
    --sd-tab-radius-top: var(--radius-sm, 8px);

    /* Ячейка: 12px/#334155/pad 7x14; шапка: bg #F8FAFC, текст #64748B */
    --sd-cell-fs: var(--fs-caption, 12px);
    --sd-cell-fg: var(--text-body, #334155);
    --sd-cell-pad: 7px 14px;
    --sd-cell-head-bg: var(--surface-sunken, #F8FAFC);
    --sd-cell-head-fg: var(--text-muted, #64748B);

    /* Date-range кнопка h=32px; легенда-точка 10x10/radius 3px/var(--viz-N) */
    --sd-daterange-h: 32px;
    --sd-legend-dot: 10px;
    --sd-legend-dot-r: 3px;

    /* Каналы (таб 2 hbar): Meta синий, Google желтый,
       TikTok красно-розовый, Others серый */
    --ch-meta: #1877F2;
    --ch-google: #FBBC05;
    --ch-tiktok: #FE2C55;
    --ch-other: #94A3B8;
}

/* ============ 2. Базовая карточка (Tabler .card + токены Strimix) ============ */
.sd-card {
    background: var(--surface-card, #FFFFFF);
    border: 1px solid var(--border-subtle, #EDF1F5);
    border-radius: var(--radius-card, 10px);
    box-shadow: var(--shadow-sm, 0 2px 6px rgba(20,32,46,.06));
    transition: border-color var(--dur-base, 200ms) var(--ease-standard, ease-out),
        box-shadow var(--dur-base, 200ms) var(--ease-standard, ease-out);
}

.sd-card:hover { border-color: var(--border-default, #DCE3EA); }

.sd-card-head {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 14px 16px 0;
}

.sd-card-title {
    margin: 0;
    font-size: var(--fs-sm, 13px);
    font-weight: var(--fw-bold, 700);
    text-transform: uppercase;
    letter-spacing: var(--ls-overline, .06em);
    color: var(--text-muted, #64748B);
}

.sd-card-body { padding: 12px 16px 16px; }

/* Мост к Tabler: нативные .card получают радиус/тень Strimix.
   Рамку Tabler не трогаем - ее цвет ведет data-bs-theme. */
.card { border-radius: var(--radius-card, 10px); }
.card.sd-flat { box-shadow: var(--shadow-sm, 0 2px 6px rgba(20,32,46,.06)); }

/* ============ 3. KPI-карточка (контракт п.3.1) ============
   OVERLINE 11px/.06em + info(формула) + значение fs-h2/tnum +
   Δ-пилюля + "було ..." caption/muted. Разметка:
   <div class="card sd-kpi"><div class="sd-kpi-overline">ДОХІД
   <button class="sd-kpi-info">ⓘ</button></div>
   <div class="sd-kpi-value">59,9 <span class="sd-kpi-cur">млн грн</span></div>
   <div class="sd-kpi-compare"><span class="sd-delta is-up">+12,4%</span>
   <span class="sd-kpi-was">було 53,3</span></div></div> */
.sd-kpi { padding: 16px; }

.sd-kpi-overline {
    display: flex;
    align-items: center;
    gap: 6px;
    margin: 0;
    font-size: var(--fs-overline, 11px);
    font-weight: var(--fw-bold, 700);
    text-transform: uppercase;
    letter-spacing: var(--ls-overline, .06em);
    color: var(--text-muted, #64748B);
}

.sd-kpi-info {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 16px;
    height: 16px;
    padding: 0;
    border: 1px solid var(--border-default, #DCE3EA);
    border-radius: 50%;
    background: transparent;
    color: var(--text-faint, #94A3B8);
    font-size: 10px;
    line-height: 1;
    cursor: help;
}

.sd-kpi-info:hover {
    border-color: var(--brand, #00BCD4);
    color: var(--text-brand, #00838F);
}

.sd-kpi-value {
    margin-top: 4px;
    font-size: var(--fs-h2, 26px);
    font-weight: var(--fw-bold, 700);
    line-height: var(--lh-snug, 1.3);
    letter-spacing: -0.01em;
    color: var(--text-strong, #16202E);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.sd-kpi-cur {
    font-size: var(--fs-body, 14px);
    font-weight: var(--fw-regular, 400);
    color: var(--text-muted, #64748B);
    white-space: nowrap;
}

.sd-kpi-compare {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 4px 8px;
    margin-top: 8px;
}

.sd-kpi-was {
    font-size: var(--fs-caption, 12px);
    color: var(--text-muted, #64748B);
    font-variant-numeric: tabular-nums;
}

/* Кликабельная KPI-метрика (прием Plausible, контракт п.3/S3-п.6):
   S3 вешает клик "метрика -> главный график", здесь только affordance. */
.sd-kpi.is-clickable { cursor: pointer; }
.sd-kpi.is-clickable:hover { border-color: var(--brand, #00BCD4); }
.sd-kpi.is-clickable.is-active {
    border-color: var(--brand, #00BCD4);
    box-shadow: var(--shadow-brand, 0 6px 18px rgba(0,188,212,.25));
}

/* ============ 4. Δ-пилюли (семантика Strimix + мост к Tabler .status) ============ */
.sd-delta {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 8px;
    border-radius: var(--radius-pill, 999px);
    font-size: var(--fs-caption, 12px);
    font-weight: var(--fw-medium, 500);
    line-height: 1.5;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.sd-delta.is-up { background: var(--positive-bg, #E7F6F0); color: var(--positive, #12A76D); }
.sd-delta.is-down { background: var(--negative-bg, #FCECEC); color: var(--err-text, #C2410C); }
.sd-delta.is-flat { background: var(--surface-auth, #F1F5F9); color: var(--text-muted, #64748B); }
.sd-delta.is-warn { background: var(--warning-bg, #FDF1DC); color: var(--warn-text, #A95000); }
.sd-delta.is-info { background: var(--info-bg, #EBF1FE); color: var(--info, #2563EB); }

/* Мосты: Tabler-пилюли .status в цветах Strimix (контракт п.3.1:
   "Δ-пилюля (.status pill: +positive-bg/+negative-bg)"). */
.status-green { background: var(--positive-bg, #E7F6F0) !important; color: var(--positive, #12A76D) !important; }
.status-red { background: var(--negative-bg, #FCECEC) !important; color: var(--err-text, #C2410C) !important; }
.status-yellow { background: var(--warning-bg, #FDF1DC) !important; color: var(--warn-text, #A95000) !important; }
.status-azure, .status-blue { background: var(--info-bg, #EBF1FE) !important; color: var(--info, #2563EB) !important; }
.status-teal { background: var(--brand-tint-12, rgba(0,188,212,.12)) !important; color: var(--text-brand, #00838F) !important; }

/* ============ 5. Мелочи: легенда-точка, date-кнопка, фокус, цифры ============ */
.sd-legend-dot {
    display: inline-block;
    flex: 0 0 auto;
    width: var(--sd-legend-dot, 10px);
    height: var(--sd-legend-dot, 10px);
    border-radius: var(--sd-legend-dot-r, 3px);
    background: var(--viz-1, #00BCD4);
}

.sd-daterange-btn { min-height: var(--sd-daterange-h, 32px); }

.sd-tnum { font-variant-numeric: tabular-nums; }

/* Видимый фокус везде в sd-зоне (WIG: focus-visible обязателен). */
.sd-card:focus-visible,
.sd-kpi:focus-visible,
.sd-kpi-info:focus-visible {
    outline: none;
    box-shadow: var(--ring-focus, 0 0 0 3px rgba(0,188,212,.25));
}

/* ============ 6. Темная тема: точечные правки поверх токенов ============
   Цвета уже переопределены в html.dark-theme (variables.css) - здесь только
   то, что токенами не выражается: табличный .status-teal в темной теме
   читается по --text-brand (#4DD0E1), задавать нечего. Блок оставлен якорем
   для S5/WIG-правок, пустым кроме motion. */
html.dark-theme .sd-card { box-shadow: var(--shadow-sm); }

/* ============ 7. Motion-уважение (контракт п.3.20, canAnimate) ============ */
@media (prefers-reduced-motion: reduce) {
    .sd-card,
    .sd-kpi {
        transition: none;
    }
}
/* === MAIN.CSS === */
/* demo/assets/css/main.css */
/* НАЗНАЧЕНИЕ: базовый слой и каркас демо - шапка, сетки, переключатель
   периода, строка источников. Раскладка меню (сайдбар + подменю) живет
   в nav.css и перебивает базовые правила .tabs ниже. */
/* СВЯЗИ: variables.css (токены), components.css, nav.css, app.js, sources.js */
/* РАЗМЕР: ~590 строк */

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

/* Одна ось композиции на весь макет. Эталон Strimix - контейнер 1306px;
   берем 1320 (1306 + воздух). По этой оси выравнивается и контент страницы,
   и содержимое шапки с табами - иначе логотип стоит левее первой карточки. */
:root {
    --shell: 1320px;
    --shell-pad: max(var(--sp-5), calc((100% - var(--shell)) / 2));
}

html {
    font-family: var(--font-display);
    font-size: var(--fs-3);
    line-height: 1.45;
    color: var(--text);
    background: var(--bg);
    -webkit-text-size-adjust: 100%;
}

body {
    margin: 0;
    min-height: 100vh;
    background: var(--bg);
    transition: background var(--transition), color var(--transition);
}

/* Skip-link (01.10.2026, a11y): скрыта вне фокуса, в фокусе - пилюля
   поверх всего. Мишень - #main в index.php. */
.skip-link {
    position: absolute;
    left: 12px;
    top: -48px;
    z-index: 2000;
    padding: 8px 14px;
    border-radius: var(--r-pill);
    background: var(--accent);
    color: var(--on-accent);
    font-size: var(--fs-2);
    font-weight: 500;
    text-decoration: none;
    transition: top var(--transition);
}

.skip-link:focus-visible {
    top: 12px;
    outline: none;
    box-shadow: var(--ring-focus, 0 0 0 3px var(--accent-line));
}

/* Заголовки. Без этого браузер считает размер сам и выдает дробные значения,
   которые не попадают ни в одну ступень шкалы. */
h1, h2, h3, h4 {
    margin: 0;
    font-weight: 600;
    line-height: 1.3;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--sp-2);
}

/* Трекинг по размеру (Apple HIG): отрицательный только на крупном кегле.
   h4 - это --fs-4 = 13px, на таком размере минус в трекинге сжимает текст
   и читается хуже, поэтому отрицательный трекинг оставлен h1-h3 (>=15px). */
h1, h2, h3 { letter-spacing: -0.01em; }
h4 { letter-spacing: 0; }
h1 { font-size: var(--fs-7); }
h2 { font-size: var(--fs-6); }
h3 { font-size: var(--fs-5); }
h4 { font-size: var(--fs-4); }

/* Формы не наследуют шрифт - браузер подставляет свой */
button, input, select, textarea {
    font-family: inherit;
    font-size: inherit;
    line-height: inherit;
    color: inherit;
}

a { color: inherit; text-decoration: none; }
button { cursor: pointer; }

/* Моноширинный НЕ на таблице целиком. Правило table/td/th делало моноширинными
   имена людей и контрагентов - текст в таблице начинал выглядеть как лог сервера.
   Выравнивание разрядов дает tabular-nums на обычном шрифте, моно тут не нужен.
   Моно остается точечно: .mono, code и ячейки с числами (.rank .val, .kpi-value). */
table, td, th {
    font-variant-numeric: tabular-nums;
}

.mono, code {
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
}

::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: var(--r-pill); }
::-webkit-scrollbar-thumb:hover { background: var(--muted); }

/* ============ ВЕРХНЯЯ ПАНЕЛЬ ============ */
/* 02.10: :not([role="tablist"]) - класс .nav есть и у вкладок Tabler
   (Продажі, Маркетинг, звіти); без исключения они становились липкой
   полупрозрачной шапкой 56px с "призраком" контента под собой. */

.nav:not([role="tablist"]) {
    position: sticky;
    top: 0;
    z-index: 50;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    row-gap: var(--sp-2);
    gap: var(--sp-3);
    min-height: 56px;
    /* Фон и нижняя линия идут до края экрана, а содержимое встает на ось
       --shell, ту же что у .page. max() не дает паддингу упасть ниже 24px
       на узких экранах. */
    padding: var(--sp-2) var(--shell-pad);
    /* Полупрозрачность с размытием вместо сплошной плашки с рамкой снизу:
       липкая шапка перестает быть отрезанной полосой и читается как слой,
       под которым едет документ. Рамка снята - две жесткие линии подряд
       (под шапкой и под рядом разделов) резали страницу на три куска. */
    background: color-mix(in oklab, var(--bg-0) 82%, transparent);
    -webkit-backdrop-filter: blur(18px) saturate(180%);
    backdrop-filter: blur(18px) saturate(180%);
}

/* Где размытия нет - непрозрачный фон, иначе текст ляжет на текст */
@supports not (backdrop-filter: blur(1px)) {
    .nav:not([role="tablist"])  { background: var(--bg-0); }
    .tabs { background: var(--bg); }
}

.nav-brand {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    font-size: var(--fs-4);
    font-weight: 800;
    letter-spacing: 0.01em;
    min-width: 0;
}

/* Имя продукта может ужаться и обрезаться, лишь бы кнопка темы осталась
   на экране. Раньше nowrap на бренде и на мете вместе давали 399px
   неразрывной ширины - на 390px кнопка уезжала за край. */
.nav-brand-name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.nav-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--blue);
    box-shadow: 0 0 0 3px var(--blue-soft);
}

/* Плашка демо. Была моноширинным капсом в акцентной рамке - кричала громче
   любой цифры на экране. Стала служебной пометкой: без рамки, без капса,
   обычным шрифтом, приглушенным цветом. */
.nav-demo {
    flex: 0 0 auto;
    padding: 1px 6px;
    border-radius: var(--r-1);
    background: var(--yellow-soft);
    border: 1px solid var(--yellow-line);
    color: var(--dim);
    font-size: var(--fs-2);
    font-weight: 500;
    letter-spacing: 0;
}

.nav-spacer { flex: 1 1 auto; }

.nav-meta {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    color: var(--muted);
    font-size: var(--fs-2);
    flex: 0 0 auto;
    margin-left: auto;
}

.icon-btn {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    width: 30px;
    height: 30px;
    padding: 0;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    color: var(--dim);
    transition: background var(--transition), color var(--transition);
}

.icon-btn:hover { background: var(--surface-hover); color: var(--text); }
.icon-btn:focus-visible { outline: 2px solid var(--blue); outline-offset: 1px; }

/* Дефолт светлый - предлагаем уйти в темную, значит видна луна.
   В темной теме наоборот: показываем солнце как выход обратно. */
.icon-dark { display: none; }
.icon-light { display: block; }
html.dark-theme .icon-dark { display: block; }
html.dark-theme .icon-light { display: none; }

/* ============ РАЗДЕЛЫ ============ */

.tabs {
    position: sticky;
    top: 56px;
    z-index: 40;
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    padding: 0 var(--shell-pad);
    background: color-mix(in oklab, var(--bg) 82%, transparent);
    -webkit-backdrop-filter: blur(18px) saturate(180%);
    backdrop-filter: blur(18px) saturate(180%);
    /* inset-тень вместо border: линия остается волосяной, но не добавляет
       высоты и гасится до 70% - край раздела, а не шов. */
    box-shadow: inset 0 -1px 0 color-mix(in oklab, var(--border) 70%, transparent);
    overflow-x: auto;
    scrollbar-width: none;
}

.tabs::-webkit-scrollbar { display: none; }

/* Классический таб-бар вместо набора пилюль. Восемь разделов имеют разную
   длину имени (70..142px), и пока у каждого была своя рамка с заливкой, ряд
   читался рваным: глаз ловил границы плашек, а не слова. Без рамки и без
   заливки разная длина слов перестает читаться как разная ширина блоков -
   остается ровная строка текста. Активный раздел держится подчеркиванием
   снизу: это тот же прием, что у вкладок браузера, и он не добавляет
   на экран очередное цветное пятно. */
.tab {
    position: relative;
    display: flex;
    align-items: center;
    gap: 6px;
    flex: 0 0 auto;
    padding: 12px 10px;
    background: none;
    border: 0;
    border-radius: 0;
    color: var(--muted);
    font-size: var(--fs-4);
    font-weight: 500;
    white-space: nowrap;
    transition: color var(--transition);
}

/* Подчеркивание рисуется всегда, но прозрачное - иначе при переходе
   в активное состояние строка табов дергается по высоте на 2px. */
.tab::after {
    content: '';
    position: absolute;
    left: var(--sp-2);
    right: var(--sp-2);
    bottom: -1px;
    height: 2px;
    border-radius: var(--r-pill) var(--r-pill) 0 0;
    background: transparent;
    transition: background var(--transition);
}

/* Текст первого раздела должен стоять ровно на оси --shell, как логотип
   и первая карточка. Собственный паддинг таба сдвигал бы его на 10px вправо,
   поэтому крайние табы подтянуты назад отрицательным полем. */
.tabs > .tab:first-child { margin-left: -10px; }
.tabs > .tab:last-child { margin-right: -10px; }

.tab:hover { color: var(--text); }
.tab:hover::after { background: var(--border-strong); }

.tab.active {
    color: var(--blue);
    font-weight: 600;
}

.tab.active::after { background: var(--blue); }

/* Обводка фокуса без outline-offset: у таба нет своей рамки, и отступ
   уводил бы прямоугольник фокуса на соседний раздел. */
.tab:focus-visible {
    outline: 2px solid var(--blue);
    outline-offset: -2px;
    border-radius: var(--r-1);
}

/* Счетчик у раздела. Был красным на каждой вкладке - ряд разделов читался
   как пять непрочитанных чатов и перебивал цифры под ним. Теперь это
   нейтральная справка: сколько записей в разделе. Цветом кричит только
   активный раздел - тот, который человек сейчас смотрит. */
.tab-badge {
    display: grid;
    place-items: center;
    min-width: 17px;
    height: 17px;
    padding: 0 5px;
    border-radius: var(--r-pill);
    background: var(--surface-hover);
    color: var(--muted);
    font-family: var(--font-mono);
    font-size: var(--fs-1);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

.tab:hover .tab-badge { color: var(--dim); }

/* Раньше активный таб был залит акцентом, и счетчику приходилось
   выворачиваться наизнанку (белый фон, акцентный текст). Заливки больше нет,
   поэтому счетчик активного раздела читается как обычная плашка в акцентных
   тонах - контраст держится на своем фоне, а не на фоне кнопки. */
.tab.active .tab-badge {
    background: color-mix(in srgb, var(--blue) 14%, var(--bg));
    color: color-mix(in srgb, var(--blue) 88%, var(--text));
}

.tab.active .tab-badge.is-warn { background: var(--yellow); color: var(--on-yellow); }

/* ============ СТРАНИЦА ============ */

.page {
    padding: var(--sp-5);
    max-width: calc(var(--shell) + var(--sp-5) * 2);
    margin: 0 auto;
}

.view { display: none; }
.view.active {
    display: block;
    animation: viewIn 220ms var(--ease-out) both;
}

@keyframes viewIn {
    from { opacity: 0; transform: translateY(6px); }
    to { opacity: 1; transform: none; }
}

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

/* Заголовок раздела. Моноширинный капс из кабинета Ozonteh тут снят:
   демо смотрит собственник, а не инженер, и «ОБЗОР» капсом читается тяжелее.
   Акцентная черта слева сохраняет узнаваемость кабинета без крика. */
.page-title {
    position: relative;
    padding-left: var(--sp-3);
    font-size: var(--fs-6);
    font-weight: 700;
    letter-spacing: -0.01em;
}

.page-title::before {
    content: '';
    position: absolute;
    left: 0;
    top: 0.18em;
    bottom: 0.18em;
    width: 3px;
    border-radius: var(--r-pill);
    background: var(--blue);
}

.page-sub {
    color: var(--muted);
    font-size: var(--fs-3);
}

/* Сетки */
/* align-items:start - колонки живут своей высотой. Раньше обе тянулись под
   высоту старшей, и у короткой панели (Воронка) снизу висело ~180px пустоты. */
.grid { display: grid; gap: var(--gap); align-items: start; }
.grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid-2-1 { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); }
.grid-1-2 { grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); }
.grid-auto { grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }

.stack { display: flex; flex-direction: column; gap: var(--gap); }

/* Ритм страницы. Вместо одного margin-top:var(--gap) на каждом блоке -
   три ступени: продолжение мысли, новый блок, новая тема. Взгляд получает
   опору и перестает читать страницу как одну сплошную стену карточек. */
.mt-s  { margin-top: var(--gap-s); }
.mt    { margin-top: var(--gap); }
.mt-l  { margin-top: var(--gap-l); }
.mt-xl { margin-top: var(--gap-xl); }

/* Смысловой разрыв между темами раздела: воздух плюс тонкая линия.
   Линия рисуется псевдоэлементом, отдельного <hr> в разметке не нужно. */
.section-break {
    margin-top: var(--gap-xl);
    padding-top: var(--gap-l);
    border-top: 1px solid var(--border);
}

/* Асимметрия под содержимое, а не под красивую дробь. Бухгалтерия: таблица
   типов документов просит 453px, таблица первички 780px - при 1fr/2fr узкая
   уезжала под прокрутку, при 1fr/1fr переносилась каждая строка соседней.
   Введено агентом W3 в разметке (views.js), объявлено здесь. */
.v3-grid-3-5 { grid-template-columns: minmax(0, 3fr) minmax(0, 5fr); }

/* Раскрытый аккордеон занимает обе колонки ряда: в половине ширины таблицы
   источников (652px) и норм (744px) не помещаются в 616px и дают прокрутку.
   Свернутый сосед при этом остается на своей оси. */
.v3-fold:has(details[open]) { grid-column: 1 / -1; }

@media (max-width: 1240px) {
    /* minmax(0,1fr), а не 1fr: по умолчанию 1fr = minmax(auto,1fr), и колонка
       не может стать уже своего содержимого. Таблица с неразрывными ячейками
       распирала единственную колонку до 613px при экране 390. */
    .grid-3, .grid-2-1, .grid-1-2,
    .v3-grid-3-5 { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 780px) {
    .page { padding: var(--sp-3); }
    .nav:not([role="tablist"]), .tabs { padding-left: var(--sp-3); padding-right: var(--sp-3); }
    .grid-2 { grid-template-columns: minmax(0, 1fr); }
    .nav-meta .hide-sm { display: none; }
}

/* ---- Телефон. Ничего не должно вылезать за 100vw ---- */
@media (max-width: 560px) {
    html, body { overflow-x: hidden; }

    .nav:not([role="tablist"]) {
        gap: var(--sp-2);
        padding-left: var(--sp-3);
        padding-right: var(--sp-3);
    }

    .nav-brand { font-size: var(--fs-3); }
    .nav-brand-name { max-width: 42vw; }
    .nav-meta { gap: var(--sp-2); font-size: var(--fs-1); max-width: 46vw; }

    /* Боковой отступ страницы совпадает с отступом шапки и табов (--sp-3),
       иначе на телефоне логотип стоит на 4px правее первой карточки -
       ровно тот же разъезд осей, что чинили на десктопе. */
    .page { padding: var(--sp-3); }
    .page-head { gap: var(--sp-1); margin-bottom: var(--sp-3); }

    /* Источники данных. На телефоне это справка, а не показатель: ряд
       систем сворачивается в прокручиваемую строку, чтобы не резаться
       по краю экрана и не растягивать документ (было 631px при 390). */
    .src-line {
        flex-wrap: nowrap;
        overflow-x: auto;
        scrollbar-width: none;
        row-gap: var(--sp-1);
        -webkit-mask-image: linear-gradient(90deg, #000 88%, transparent 100%);
        mask-image: linear-gradient(90deg, #000 88%, transparent 100%);
    }

    .src-line::-webkit-scrollbar { display: none; }
    .src-line > span { flex: 0 0 auto; }
}

/* Утилиты */
.muted { color: var(--muted); }
.dim { color: var(--dim); }
.num { font-variant-numeric: tabular-nums; }
.nowrap { white-space: nowrap; }
/* Сборщик Tabler-редизайна (C2-2/C3-2/C4-1/C5-1): старое правило .row
   (display:flex + gap 8px + align center) ломало математику 12 колонок Tabler.
   Старых потребителей голого .row не осталось (sources.js переведен на
   .row-between, новая разметка - настоящий Tabler-грид), правило погашено.
   Имя .row теперь принадлежит сетке Tabler/Bootstrap. */
.row-between {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-2);
    flex-wrap: wrap;
}

/* ============ ПЕРЕКЛЮЧАТЕЛЬ ПЕРИОДА ============ */
/* Сегментный контрол, как в кабинете Ozonteh: 7д / 30д / 90д / 365д.
   Сделан одной капсулой, а не рядом кнопок - так видно, что выбор
   взаимоисключающий, и активный сегмент не надо подписывать словом. */

.period-switch {
    display: inline-flex;
    align-items: center;
    gap: 2px;
    padding: 3px;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--r-pill);
}

.period-switch a,
.period-switch button {
    display: inline-flex;
    align-items: center;
    padding: 5px 12px;
    background: none;
    border: none;
    border-radius: var(--r-pill);
    color: var(--muted);
    font-family: var(--font-mono);
    font-size: var(--fs-2);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    transition: background var(--transition), color var(--transition);
}

.period-switch a:hover,
.period-switch button:hover {
    background: var(--surface-hover);
    color: var(--text);
}

/* Активный сегмент заливается синим. Контраст текста на заливке проходит норму */
.period-switch a.active,
.period-switch button.active {
    background: var(--blue);
    color: var(--on-blue);
}

.period-switch a.active:hover,
.period-switch button.active:hover {
    background: var(--blue);
    color: var(--on-blue);
}

.period-switch a:focus-visible,
.period-switch button:focus-visible {
    outline: 2px solid var(--blue);
    outline-offset: 2px;
}

/* ============ СТРОКА ИСТОЧНИКОВ ДАННЫХ ============ */
/* Главный аргумент демо: цифра не придумана, под ней конкретная система.
   Поэтому источники стоят прямо в шапке раздела, а не в сноске внизу.
   Разделители-точки рисуются псевдоэлементом, чтобы не плодить разметку. */

.src-line {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0 var(--sp-2);
    color: var(--muted);
    font-size: var(--fs-2);
    line-height: 1.6;
    max-width: 100%;
    min-width: 0;
}

.src-line > span {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    white-space: nowrap;
    min-width: 0;
    max-width: 100%;
}

.src-line > span + span::before {
    content: '';
    width: 3px;
    height: 3px;
    border-radius: 50%;
    background: var(--border-strong);
}

/* Имя системы выделяется весом, а не цветом: цветов на экране и так хватает */
.src-line b {
    color: var(--dim);
    font-weight: 600;
}

/* Метка свежести. Зеленая точка = синхронизация прошла */
.src-line i {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--ok);
    font-style: normal;
    flex: 0 0 auto;
}

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

/* Дубль заголовка раздела снят (пункт 7 эталона Strimix).
   Слово "Обзор" стояло в активной вкладке и тут же ниже в h2 - одно и то же
   дважды подряд на каждом из восьми разделов. У Strimix заголовка страницы
   как блока нет вообще, его роль выполняет активная вкладка, это экономит
   вертикаль. h2 оставлен в разметке для скринридеров и структуры документа,
   но скрыт визуально. Подзаголовок-пояснение остается: он несет смысл. */
.page-title {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

.page-sub {
    font-size: var(--fs-3);
    font-weight: 600;
    color: var(--text);
}
/* ============ ИКОНКИ ============
   Спрайт Lucide, инлайнится в index.php. Иконки контурные, цвет берется
   от CSS-свойства color через stroke="currentColor" - красить надо color,
   НЕ fill. За границу <use> наследуются только color, stroke-width и
   stroke-linecap/linejoin, покрасить отдельный path внутри нельзя.

   stroke-width снижен со штатных 2 до 1.6: в паке иконки нарисованы под
   24px, а у нас они живут на 15-16px в плотном интерфейсе, где двойка
   читается заметно жирнее текста рядом и начинает с ним спорить. */
.ico {
    width: 16px;
    height: 16px;
    flex: none;
    stroke-width: 1.6;
    vertical-align: -0.15em;
}

/* Иконка - спутник текста, а не его соперник. Приглушена на ступень
   относительно заголовка: ведет взгляд к строке, но читается строка. */
.panel-title .ico,
.tab .ico {
    color: var(--muted);
    transition: color var(--transition);
}

.tab:hover .ico { color: var(--dim); }
.tab.active .ico { color: var(--accent); }

.panel-head .ico { margin-right: var(--sp-2); }

/* Шеврон аккордеона: поворачивается при раскрытии, направление = состояние */
.v3-fold .ico-fold {
    margin-left: var(--sp-2);
    color: var(--muted);
    transition: transform var(--transition), color var(--transition);
}

.v3-fold > details[open] .ico-fold { transform: rotate(180deg); }
.v3-fold > details:hover .ico-fold { color: var(--dim); }

@media (prefers-reduced-motion: reduce) {
    .v3-fold .ico-fold { transition: none; }
}

/* === COMPONENTS.CSS === */
/* demo/assets/css/components.css */
/* НАЗНАЧЕНИЕ: панели, KPI со сравнением к прошлому периоду, индикаторы,
   таблицы-рейтинги, воронка, графики, карта локаций, чат */
/* СВЯЗИ: variables.css (токены), main.css, views.js, charts.js, chat.js */
/* РАЗМЕР: ~760 строк */

/* ============ ПАНЕЛЬ ============ */

/* Карточка на светлом фоне держится тенью, рамка только чтобы не расплывалась
   на белой панели поверх белой панели. Отступ 18px вместо 14: демо показывают
   с проектора, тесная карточка там читается как сплошное пятно. */
/* Тени на панелях СНЯТЫ. В системе Apple тень в интерфейсе ровно одна и
   лежит под тем, что действительно парит над плоскостью (у нас это точки
   карты и их подписи). Разделение соседних блоков делается волосяной
   рамкой, а не тенью: восемь карточек, каждая со своей тенью, дают рябь
   по всей странице и читаются как набор наклеек, а не как один документ. */
.panel {
    padding: var(--sp-4) 18px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-3);
}

/* ---- ДВА ПЛАНА КАРТОЧЕК ----
   Одинаковость - главный маркер сгенерированной верстки: восемь панелей
   одного веса, с одной рамкой, одной тенью и одним синим капсом сверху
   не дают взгляду точки входа, и страница читается как таблица таблиц.
   Вводим ровно два уровня, различие сдержанное - вес, не цвет:

   первый план  - панель, ради которой открыли раздел (находки системы,
                  главная таблица раздела): белая поверхность, тень, рамка;
   второй план  - справочная панель (индикаторы раздела, содержимое
                  свернутых блоков): та же геометрия, но без тени и на
                  --surface-2. Она не всплывает над фоном, а лежит в нем.

   Классов для этого не заводим: разметка чужая (views.js, зона W3),
   а структура уже несет смысл - .section-break стоит на справочных
   панелях индикаторов, <details> на свернутом справочном материале. */
.panel.section-break,
/* Свернутый аккордеон = карточка, а не голая строка.
   Пока эти блоки лежали по одному на всю ширину, отсутствие рамки читалось
   как разделитель внизу страницы. Поставленные в ряд по двое, они слиплись:
   заголовок второго начинался сразу за словом «развернуть» первого, и пара
   читалась как одна строка текста. Рамка возвращает границу блока.
   Уровень - второй план: без тени, на приглушенном фоне. */
.v3-fold > details {
    height: 100%;
    padding: var(--sp-2) var(--sp-3);
    background: var(--surface-2);
    border: 1px solid color-mix(in oklab, var(--border) 60%, transparent);
    border-radius: var(--r-3);
    transition: border-color var(--transition);
}

.v3-fold > details:hover { border-color: var(--border); }

/* Раскрытый занимает обе колонки (правило в main.css) и поднимается
   на первый план: внутри уже содержательная таблица, а не подпись. */
.v3-fold > details[open] {
    background: var(--surface);
    border-color: var(--border);
}

/* Слово «розгорнути» меняется на «згорнути» без JS */
.v3-fold > details > summary .v3-toggle::after { content: 'розгорнути'; }
.v3-fold > details[open] > summary .v3-toggle::after { content: 'згорнути'; }

details .panel {
    background: var(--surface-2);
    border-color: color-mix(in oklab, var(--border) 60%, transparent);
    box-shadow: none;
}

/* Заголовок и подпись рядом не должны слипаться - gap нужен у родителя.
   Отступ до тела 12, а не 16: шапка панели - часть той же группы, что и
   ее содержимое, а 16 уже читается как расстояние между блоками. */
.panel-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: var(--sp-3);
    margin-bottom: var(--gap);
}

.panel-title {
    font-size: var(--fs-5);
    font-weight: 700;
}

/* Заголовок панели. Был 12px/700 uppercase цветом акцента на КАЖДОЙ панели -
   и на экране Обзора получалось пять синих капсов, конкурирующих между собой
   и с цифрами KPI. У Strimix акцент в интерфейсе один (активная вкладка),
   а заголовок панели живет на служебном уровне.
   Стало: рядовой заголовок - обычный текст 15px/600 приглушенного цвета,
   без капса и без акцента. Капс дороже обычного текста по вниманию, тратить
   его восемь раз на одном экране нельзя. */
.panel-title.is-label {
    font-size: var(--fs-5);
    font-weight: 600;
    line-height: var(--lh-snug);
    letter-spacing: -0.005em;
    text-transform: none;
    color: var(--dim);
}

/* Класс .v3-lead удален из разметки вместе с блоком «Ранок власника»
   (его нет в ТЗ) - правило капса одной панели раздела убрано 28.09.2026.
   Роль «начни отсюда» теперь у цветных акцентов wave-d.css. */

/* У панели второго плана заголовок еще на ступень тише: это подпись
   к справочному материалу, а не название блока */
.panel.section-break > .panel-head .panel-title.is-label,
details .panel > .panel-head .panel-title.is-label {
    font-size: var(--fs-4);
    color: var(--muted);
}

.panel-sub {
    color: var(--muted);
    font-size: var(--fs-3);
    font-weight: 400;
}

/* ---- ДЛИНА СТРОКИ В АБЗАЦАХ ПАНЕЛИ ----
   Замер до правки: пояснение под «Утренней сводкой» шло строками по 191 знаку,
   вводный абзац находок - по 118. Читать такое нельзя: на строке длиннее
   ~85 знаков возврат каретки промахивается, и абзац перечитывают дважды.
   Ограничение ставим в ch (ширина знака), а не в px - переживет смену шрифта.
   72ch для вводного абзаца 15px (он крупнее, знаков в строке должно быть
   меньше) и 78ch для служебных пояснений 11-13px. */
.panel > p {
    max-width: 72ch;
    /* Абзац для читання, а не службовий рядок: інтерліньяж як у знахідок
       і чату (був успадкований 1.45 з body). pretty прибирає висячі слова. */
    line-height: var(--lh-read);
    text-wrap: pretty;
}

.panel > p.muted,
.panel p.footnote {
    max-width: 78ch;
}

/* ============ KPI ============ */

/* 240px вместо 210: при 210 крупная цифра с единицей не влезала в колонку
   на 1280 и 768, "грн" падало на вторую строку и карточка вырастала
   до 71px против 44 у соседей - подписи теряли общую базовую линию. */
.kpi-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: var(--gap);
    align-items: stretch;
}

/* Пять карточек в ряду, где помещается четыре, давали одинокую пятую
   во всю ширину - она читалась как отдельный блок, а не как часть ряда.
   auto-fill вместо auto-fit оставляет пустые колонки: последняя карточка
   встает по сетке и сохраняет ширину соседей. */
@media (min-width: 781px) and (max-width: 1500px) {
    .kpi-row { grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); }
}

/* KPI-карточка. Белая поверхность вместо серой: на светлой теме серая плитка
   на сером фоне сливается, а тень отделяет ее без лишней рамки. */
.kpi-card {
    position: relative;
    padding: 18px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-3);
    transition: border-color var(--transition), background var(--transition);
}

/* Отклик на наведение - не подъем тенью, а уплотнение рамки: карточка
   остается в плоскости страницы, но показывает, что она живая. */
.kpi-card:hover { border-color: var(--border-strong); }

/* Цветные рамки по статусу СНЯТЫ намеренно (эталон Strimix, раздел 10 п.8).
   Было: зеленая рамка у выручки, красная у дебиторки, красная цифра, плюс
   красные строки в 11 случаях из 15 ниже. Когда красным помечено все,
   не помечено ничего - рамка переставала работать как сигнал и оставалась
   просто шумом по периметру. У Strimix карточки одинаковые, а статус несет
   дельта: она и так стоит в каждой карточке и говорит направление.
   Цвет цифры оставлен: это ОДИН сигнал на карточку, а не два. */
.kpi-card.is-main,
.kpi-card.is-warn,
.kpi-card.is-alert { border-color: var(--border); }

/* Цифра и ее единица - всегда одна строка. flex-wrap отсюда убран
   намеренно: перенос ломал общую базовую линию всего ряда KPI.
   Размер текучий (--fs-7 = clamp), поэтому вместо переноса цифра
   ужимается вместе с шириной колонки. */
.kpi-value {
    display: flex;
    align-items: baseline;
    gap: 6px;
    min-height: calc(var(--fs-7) * 1.25);
    font-family: var(--font-display);
    font-size: var(--fs-7);
    font-weight: 700;
    line-height: var(--lh-tight);
    letter-spacing: -0.02em;
    font-variant-numeric: var(--tnum);
    white-space: nowrap;
    /* Roboto рендерить звичайний пробіл нормально, стискання з моноширинної
       ери прибираємо: з ним "570 500" злипалося в "570500". */
    word-spacing: normal;
}

.kpi-card.is-main .kpi-value { color: var(--text); }
.kpi-card.is-alert .kpi-value { color: var(--err-text); }

.kpi-cur {
    flex: 0 0 auto;
    font-size: var(--fs-3);
    font-weight: 400;
    color: var(--muted);
    white-space: nowrap;
}

/* ---- РИТМ ВНУТРИ KPI-КАРТОЧКИ: ТРИ СТУПЕНИ, НЕ ОДНА ----
   Было 8 / 6 / 5 px - три произвольных числа, которые глаз читает как один
   и тот же отступ, то есть как четыре равнозначные строки без иерархии.
   Стало по лестнице токенов: 4 внутри пары «подпись-цифра», 8 между цифрой
   и ее сравнением (это одна смысловая группа: сколько и относительно чего),
   12 перед пояснением - оно уже другая группа, дополнительные факты.
   Ровно этим Strimix и отличается: 12 внутри группы, 16 в сетке, 20 между
   блоками. Один отступ на все уровни - маркер набора по месту. */
.kpi-label {
    margin-top: 0;   /* метка - первый элемент карточки, отступ дает padding */
    font-size: var(--fs-2);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--muted);
}

/* Пара «метка - цифра» держится вместе: самая тесная ступень лестницы */
.kpi-value { margin-top: var(--gap-xs); }

.kpi-sub {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--gap-s);
    margin-top: var(--gap);
    color: var(--muted);
    font-size: var(--fs-3);
    /* У поясненні живуть числа ("сьогодні 584 700 грн") - табличні,
       щоб розряди не стрибали між картками ряду. */
    font-variant-numeric: var(--tnum);
}

/* ---- Сравнение с прошлым периодом ----
   Ключевой прием Strimix: цифра сама по себе ничего не говорит, пока рядом нет
   ответа «по сравнению с чем». Одна строка: бейдж дельты + база сравнения.
   Ставится под .kpi-value вместо .kpi-sub или вместе с ним. */

.kpi-compare {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: var(--gap-xs) var(--gap-s);
    margin-top: var(--gap-s);
    min-height: 22px;
}

/* База сравнения. Серым и мельче: это справка, а не показатель.
   Цифра моноширинная, чтобы выравнивалась с цифрой выше по разрядам. */
.kpi-was {
    color: var(--muted);
    font-size: var(--fs-2);
    white-space: nowrap;
}

.kpi-was b {
    font-family: var(--font-display);
    font-weight: 400;
    font-variant-numeric: var(--tnum);
    color: var(--muted);
}

/* Слово "було" перед базой сравнения: без него цифра рядом с дельтой
   читается как второй показатель, а не как точка отсчета */
.kpi-was::before {
    content: 'було ';
    color: var(--muted);
    font-weight: 400;
}

/* Когда база не влезает в строку с дельтой - переносится целиком,
   а не рвется посередине числа. Поэтому white-space:nowrap выше. */
@media (max-width: 400px) {
    .kpi-compare { gap: var(--sp-1) var(--sp-2); }
}

/* ============ ДЕЛЬТА ============ */

.delta {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    padding: 0;
    font-family: var(--font-display);
    font-size: var(--fs-2);
    font-weight: 500;
    font-variant-numeric: var(--tnum);
    white-space: nowrap;
}

.delta.up { color: var(--ok); }
.delta.down { color: var(--err-text); }
.delta.flat { color: var(--muted); }

/* Статус внутри таблицы - цветная точка плюс текст, а не залитая плашка.
   Та же причина, что и у дельт (эталон п.8): в колонке из десяти строк
   десять заливок весят больше, чем данные рядом. Точка несет цвет,
   текст остается спокойным. */
.rank .badge {
    padding: 0;
    background: none;
    color: var(--dim);
    font-weight: 500;
    gap: 6px;
}

.rank .badge::before {
    content: '';
    width: 7px;
    height: 7px;
    border-radius: 50%;
    flex: 0 0 auto;
    background: currentColor;
}

.rank .badge.ok::before { background: var(--ok); }
.rank .badge.warn::before { background: var(--warn); }
.rank .badge.alert::before { background: var(--err); }
.rank .badge.alert { color: var(--err-text); font-weight: 600; }

/* Внутри таблицы дельта - цветной текст, а не плашка. В колонке из десяти
   строк десять залитых плашек превращают столбец в светофор и весят больше,
   чем сами цифры рядом. Плашка остается там, где дельта одна на блок:
   в KPI-карточке и в заголовках. */
.rank .delta {
    background: none;
    padding: 0;
    font-weight: 600;
}

/* Дельта внутри строки сравнения крупнее обычной: это второе по важности
   число на карточке после самого показателя */
.kpi-compare .delta {
    padding: 0;
    font-size: var(--fs-3);
}

/* ============ ЧИПЫ ============ */

.chips {
    display: flex;
    flex-wrap: wrap;
    gap: var(--sp-2);
}

.chip {
    flex: 0 0 auto;
    white-space: nowrap;
    display: flex;
    align-items: baseline;
    gap: 6px;
    padding: 9px 12px;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    font-size: var(--fs-4);
}

.chip-val {
    font-family: var(--font-mono);
    font-weight: 600;
    font-variant-numeric: var(--tnum);
    word-spacing: -0.2em;
}

/* Сжатие пробела нужно ЧИСЛАМ, но .chip-val переиспользуют и под обычный
   текст - в блоке «что еще умеет второй мозг» заголовки схлопывались в
   «Транскрибациязвонков». Класс .br-skill-t помечает текстовый случай.
   Тот же прием уже стоит на .rank .nm для имен в таблицах. */
.br-skill-t {
    font-family: var(--font-display);
    word-spacing: normal;
    font-weight: 700;
    color: var(--text);
}

.chip-label {
    color: var(--muted);
    font-size: var(--fs-2);
}

/* ============ БЕЙДЖ ============ */

.badge {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 8px;
    border-radius: var(--r-pill);
    font-size: var(--fs-2);
    font-weight: 600;
    font-variant-numeric: var(--tnum);
    white-space: nowrap;
}

.badge.ok { background: var(--ok-soft); color: var(--ok); }
.badge.warn { background: var(--warn-soft); color: var(--warn-text); }
.badge.alert { background: var(--err-soft); color: var(--err-text); }
.badge.neutral { background: var(--surface-hover); color: var(--muted); }

/* ============ ИНДИКАТОРЫ ============ */

.ind-list {
    display: flex;
    flex-direction: column;
    gap: 0;
}

/* Строка индикатора. Была карточкой внутри карточки: своя рамка, свой фон,
   свое скругление - и все это лежало внутри .panel, которая уже карточка.
   Плюс цветная полоска слева 3px, повторенная около двадцати раз подряд.
   Стало: строка списка без рамки и фона, разделенная линией от соседней.
   Уровень показывает цветная точка слева и цвет числа справа - двух
   сигналов достаточно, третий (полоска) был шумом. */
/* align-items: start вместо center. Пока текст находки был однострочным,
   разницы не было; после ограничения строки в 78ch находка занимает три-четыре
   строки, и центрирование уводило точку уровня и число на середину абзаца -
   строка переставала читаться как «заголовок, пояснение, число».
   Ступени отступа: 12 по вертикали между строками списка (это соседние
   элементы одной группы), 8 по горизонтали до края панели. */
.ind {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: start;
    gap: var(--gap-s) var(--gap);
    padding: var(--gap) var(--gap-s);
    border-radius: var(--r-1);
    transition: background var(--transition);
}

/* Точка уровня стоит на базовой линии названия, а не в самом верху строки */
.ind > .ind-dot { margin-top: 6px; }

.ind + .ind { box-shadow: 0 -1px 0 var(--border); }

.ind:hover { background: var(--surface-2); }

/* Критичное вытаскиваем фоном, а не рамкой: заливка работает и в списке,
   и в темной теме, и не добавляет еще одну карточку в карточку.
   Заливка снята совсем (эталон п.8: когда красным помечено все, не помечено
   ничего). В списке из 15 строк 11 критичных - сплошная заливка красила
   три четверти блока и переставала быть сигналом. Красное осталось там, где
   оно единично и по делу: цифра справа и точка слева. */
.ind.is-alert { background: none; }
.ind.is-alert:hover { background: var(--surface-2); }

/* Вторая и последующие находки в списке. Первая находка - главная, она
   может нести акцент; остальные идут ровным списком. Правило живет здесь,
   чтобы views.js не гасил фон инлайном ради перебивания моей специфичности. */
.ind.is-flat,
.ind.is-alert.is-flat { background: none; }



.ind-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    flex: 0 0 auto;
}

.ind.is-ok .ind-dot { background: var(--ok); box-shadow: 0 0 0 3px var(--ok-soft); }
.ind.is-warn .ind-dot { background: var(--warn); box-shadow: 0 0 0 3px var(--warn-soft); }
/* Пульсация СНЯТА. Четыре мигающие точки подряд шумят ровно там, где мы
   убирали крик: в списке находок критичных строк большинство, и пульсировали
   они все сразу. На статике и на скриншоте эффект не виден вовсе, а в живом
   показе с проектора отвлекает от чтения текста находки.
   Цвет точки остается - он и есть сигнал. */
.ind.is-alert .ind-dot {
    background: var(--err);
    box-shadow: 0 0 0 3px var(--err-soft);
}

.ind-body { min-width: 0; }

.ind-name {
    font-size: var(--fs-4);
    font-weight: 600;
}

/* Текст находки. Главная правка: длина строки.
   На контейнере 1320px абзац находки шел строками по 95-100 знаков - глаз,
   дочитав до правого края, не находит начало следующей строки и перечитывает
   предыдущую. Типографическая норма 60-80 знаков; берем 78ch как верхнюю
   границу, чтобы короткие находки не превращались в узкую колонку.
   Мера в ch, а не в px: ch привязан к ширине знака текущего шрифта, поэтому
   при смене Manrope на любой другой длина строки останется той же в знаках.
   Интерлиньяж поднят с 1.4 до 1.55 - на трех-четырех строках подряд 1.4
   склеивает абзац в серое пятно. */
.ind-text {
    display: block;
    max-width: 78ch;
    margin-top: 3px;
    color: var(--muted);
    font-size: var(--fs-3);
    line-height: var(--lh-read);
    text-wrap: pretty;
}

.ind-num {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    font-family: var(--font-mono);
    font-variant-numeric: var(--tnum);
    white-space: nowrap;
    word-spacing: -0.2em;
}

/* Число индикатора - вторая по величине цифра на странице после KPI.
   Apple HIG: трекинг обратно пропорционален кеглю, у крупного жирного
   набора межбуквенное растет оптически и его надо подбирать. При 15px/700
   в моноширинном без отрицательного трекинга цифры стоят разреженно и
   строка читается как таблица кодов, а не как показатель. */
.ind-cur {
    font-size: var(--fs-5);
    font-weight: 700;
    letter-spacing: -0.015em;
}

.ind.is-alert .ind-cur { color: var(--err-text); }
.ind.is-warn .ind-cur { color: var(--warn-text); }
.ind.is-ok .ind-cur { color: var(--ok); }

/* Подпись нормы, 11px. По той же шкале - у мелкого кегля трекинг
   положительный, иначе знаки слипаются */
.ind-norm {
    color: var(--muted);
    font-size: var(--fs-2);
    letter-spacing: 0.01em;
}

/* ============ РЕЙТИНГ-ТАБЛИЦА ============ */

.rank {
    width: 100%;
    border-collapse: collapse;
}

/* Разделитель ТОЛЬКО снизу и только у ячеек. Раньше линия висела на <tr>,
   из-за чего у последней строки ее приходилось снимать отдельным правилом. */
.rank tr { border-bottom: none; }

/* Зебры нет. У Strimix ее нет ни в одной таблице: чередование фона - это
   второй разделитель поверх линии, глаз начинает читать полосы вместо
   данных. Строку подсвечиваем только под курсором, и то едва заметно. */
.rank tbody tr {
    transition: background var(--transition);
    background: none;
}

.rank tbody tr:hover { background: var(--surface-2); }

/* Липка перша колонка таблиць (.stick на td/th): фон живе в класі, а не в
   інлайні, щоб hover рядка перекривав frozen-комірку разом з рештою.
   Без цього комірка лишалась surface на підсвіченому surface-2 рядку. */
.rank td.stick, .rank th.stick { position: sticky; left: 0; background: var(--surface); }
.rank td.stick { box-shadow: 1px 0 0 var(--border); z-index: 1; }
.rank th.stick { box-shadow: 1px 0 0 var(--border-strong); z-index: 2; }
.rank tbody tr:hover td.stick { background: var(--surface-2); }

.rank th {
    height: 40px;
    padding: 8px 14px;
    /* Шапка відділена міцнішою лінійкою, ніж рядки: border-strong проти
       border у td. Інакше заголовок зливається з першим рядком і таблиця
       читається як стіна без початку (замір: однакової ваги лінії). */
    border-bottom: 1px solid var(--border-strong);
    color: var(--muted);
    /* Було голе 11px. fs-2 за шкалою F1 - саме шапки таблиць (13px);
       той самий голос, що в капс-міток KPI. У рядок 40px влізає. */
    font-size: var(--fs-2);
    font-weight: 700;
    line-height: 1.5;
    text-align: left;
    text-transform: uppercase;
    letter-spacing: 0.06em;   /* 0.66px при 11px, но переживет смену кегля */
    white-space: nowrap;
}

/* Строка ровно 40px. height на td при border-collapse работает как
   минимальная высота строки - именно то, что нужно: перенос в длинном
   названии строку растянет, короткая останется 40. */
.rank td {
    height: 40px;
    padding: 7px 14px;
    border-bottom: 1px solid var(--border);
    vertical-align: middle;
    font-size: var(--fs-3);
    color: var(--dim);
}

.rank tbody tr:last-child td { border-bottom: none; }

/* Крайние ячейки прижимаем к краю панели, чтобы padding 14px не давал
   двойного отступа рядом с padding самой панели */
.rank th:first-child, .rank td:first-child { padding-left: 0; }
.rank th:last-child, .rank td:last-child { padding-right: 0; }

.rank .rk {
    width: 24px;
    padding-right: 0;
    color: var(--muted);
    font-size: var(--fs-2);
    text-align: right;
}

.rank .nm {
    font-size: var(--fs-3);
    font-weight: 500;
    color: var(--text);
}

/* Вторая строка в ячейке имени. Плотная line-height нужна, чтобы строка
   с подписью укладывалась в 44-48px, а не разъезжалась до 56: эталонные
   40px держат только однострочные таблицы, двухстрочная честно выше. */
.rank .nm .cnt {
    display: block;
    margin-top: 1px;
    color: var(--muted);
    font-size: var(--fs-2);
    line-height: 1.3;
    font-weight: 400;
}

.rank .nm { line-height: 1.3; }

.rank .val {
    font-family: var(--font-mono);
    font-variant-numeric: var(--tnum);
    letter-spacing: -0.01em;
    word-spacing: -0.2em;
    position: relative;
    min-width: 108px;
    padding-right: 8px;
    font-weight: 700;
    text-align: right;
    white-space: nowrap;
}

.rank .val .bar {
    display: block;
    height: 3px;
    margin-top: 4px;
    margin-left: auto;
    border-radius: var(--r-1);
    background: var(--accent);
    opacity: 0.55;
    transition: width 600ms var(--ease-out);
}

/* Итоговая строка. Отделена двойным воздухом сверху и линией потолще:
   это не очередная строка данных, а сумма над ними. Фона нет намеренно -
   заливка вернула бы зебру, от которой только что избавились. */
.rank tfoot td,
.rank tr.is-total td {
    height: 44px;
    padding-top: 10px;
    border-top: 2px solid var(--border-strong);
    border-bottom: none;
    color: var(--text);
    font-weight: 700;
}

.rank tfoot tr:hover,
.rank tr.is-total:hover { background: none; }

/* У предпоследней строки своя линия не нужна: над итогом уже есть своя */
.rank tbody tr:last-child td { border-bottom: none; }

.rank .r {
    text-align: right;
    font-family: var(--font-mono);
    font-variant-numeric: var(--tnum);
    word-spacing: -0.2em;
}

/* Имя в таблице - обычный шрифт. Это текст, а не показание прибора */
.rank .nm, .rank th { font-family: var(--font-display); word-spacing: normal; }

/* Числова шапка тим самим голосом, що й текстова. Правило .rank .r (0,2,0)
   перебивало .rank th (0,1,1) - і "ДОХІД" набирався моноширинним, а "КАНАЛ"
   поруч Manrope: два голоси в одному рядку. Специфічність 0,2,1 повертає
   шапці display-шрифт, вирівнювання праворуч з .r лишається. */
.rank th.r { font-family: var(--font-display); word-spacing: normal; }

/* Числа в комірках - повним кольором тексту, а не dim. Імена (.nm) уже
   text, а дані тьмяніші за підписи - для таблиці даних це навпаки:
   значення головне. Прямі кольори всередині (delta, badge) не чіпає -
   пряме правило завжди сильніше за успадковане. */
.rank td.val, .rank td.r { color: var(--text); }

.tbl-wrap {
    overflow-x: auto;
    max-width: 100%;
}

/* ============ ВОРОНКА ============ */

.funnel { display: flex; flex-direction: column; gap: var(--sp-1); }

/* Колонки: подпись / полоса / цифры. minmax(0,1fr) на полосе обязателен -
   с голым 1fr длинная подпись справа ("55,6% при норме 68%") выдавливала
   дорожку в ноль, и на 390px этап Переговоры оставался вообще без полосы
   (замерено: trackW 0 при fillW 18). minmax запрещает колонке схлопнуться
   ниже заданного минимума. */
.funnel-step {
    display: grid;
    grid-template-columns: minmax(96px, 160px) minmax(60px, 1fr) auto;
    align-items: center;
    gap: var(--sp-3);
    padding: 5px 0;
}

/* Хвост с нормой переносить нельзя: он должен читаться одной мыслью */
.funnel-conv .norm {
    display: block;
    color: var(--muted);
}

.funnel-label { font-size: var(--fs-4); color: var(--dim); }

.funnel-track {
    height: 28px;
    background: var(--surface-2);
    border-radius: var(--radius);
    overflow: hidden;
}

.funnel-fill {
    word-spacing: -0.2em;
    display: flex;
    align-items: center;
    height: 100%;
    padding: 0 9px;
    border-radius: var(--radius);
    background: var(--accent);
    color: var(--on-accent);
    font-family: var(--font-mono);
    font-size: var(--fs-4);
    font-weight: 700;
    font-variant-numeric: var(--tnum);
    transition: width 700ms var(--ease-out);
}

.funnel-fill.is-alert { background: var(--err); color: var(--on-err); }

.funnel-conv {
    font-family: var(--font-mono);
    font-size: var(--fs-3);
    font-variant-numeric: var(--tnum);
    text-align: right;
    white-space: nowrap;
}

/* ============ СТОЛБЧАТЫЙ ГРАФИК (без библиотеки) ============ */

.chart {
    display: flex;
    align-items: flex-end;
    gap: var(--sp-1);
    height: 170px;
    padding-top: var(--sp-3);
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: thin;
}

.chart-col {
    flex: 1 1 0;
    min-width: 34px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-end;
    height: 100%;
    gap: var(--sp-1);
}

.chart-bar {
    width: 100%;
    max-width: 46px;
    border-radius: var(--r-1) var(--r-1) 0 0;
    background: var(--accent);
    transition: height 700ms var(--ease-out), opacity var(--transition);
}

.chart-col:hover .chart-bar { opacity: 0.8; }
.chart-bar.is-alert { background: var(--err); }
.chart-bar.is-warn { background: var(--warn); }

.chart-val {
    font-family: var(--font-mono);
    font-size: var(--fs-1);
    color: var(--dim);
    font-variant-numeric: var(--tnum);
}

.chart-lbl {
    font-family: var(--font-mono);
    word-spacing: -0.15em;
    font-size: var(--fs-1);
    color: var(--muted);
    white-space: nowrap;
}

/* Canvas-график.
   max-width обязателен: Chart.js меряет холст в момент отрисовки, а разделы
   кроме активного лежат в display:none. В скрытом разделе ширина родителя
   равна нулю, библиотека ставит на canvas свою inline-ширину от последнего
   известного размера - и при показе раздела холст оказывается шире экрана
   (замерено 566px на телефоне 390px). responsive:true это не лечит,
   лечит потолок в CSS. */
.chart-box {
    position: relative;
    width: 100%;
    max-width: 100%;
    height: 240px;
    overflow: hidden;
}

.chart-box.sm { height: 180px; }

.chart-box > canvas,
canvas {
    max-width: 100% !important;
}

/* Легенда */
.legend {
    display: flex;
    flex-wrap: wrap;
    gap: var(--sp-3);
    margin-top: var(--sp-2);
}

.legend-item {
    display: flex;
    align-items: center;
    gap: 5px;
    color: var(--muted);
    font-size: var(--fs-2);
}

.legend-dot {
    width: 8px;
    height: 8px;
    border-radius: 2px;
}

/* ============ ЧАТ ============ */

.chat {
    display: flex;
    flex-direction: column;
    gap: var(--sp-3);
}

.chat-log {
    display: flex;
    flex-direction: column;
    gap: var(--sp-2);
    padding: var(--sp-4);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
}

.msg {
    display: flex;
    flex-direction: column;
    max-width: 82%;
    padding: 9px 11px;
    border-radius: var(--radius);
    animation: msgIn 220ms var(--ease-out) both;
}

@keyframes msgIn {
    from { opacity: 0; transform: translateY(5px); }
    to { opacity: 1; transform: none; }
}

/* Вопрос владельца - справа, ответ мозга - слева */
.msg-q {
    align-self: flex-end;
    background: var(--accent);
    color: var(--on-accent);
    border-bottom-right-radius: 2px;
}

.msg-a {
    align-self: flex-start;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-bottom-left-radius: 2px;
    max-width: 88%;
}

.msg-sender {
    margin-bottom: 3px;
    font-size: var(--fs-1);
    /* Було 0.6: підпис 11px на межі зникнення, особливо на кольоровій
       бульбашці. 0.75 лишає його службовим, але читаним. */
    opacity: 0.75;
}

.msg-text {
    /* Відповідь джина йшла рядками по ~130 знаків на широкому екрані -
       та сама хвороба, що вже вилікувана в .ind-text і .panel > p.
       72ch, бо кегль тут 16px (fs-4), як у абзаца панелі. */
    max-width: 72ch;
    font-size: var(--fs-4);
    line-height: var(--lh-read);
    text-wrap: pretty;
    white-space: pre-wrap;
}

.msg-src {
    margin-top: 6px;
    padding-top: 5px;
    border-top: 1px solid var(--border);
    color: var(--muted);
    font-family: var(--font-mono);
    font-size: var(--fs-1);
}

/* Поле вопроса - главный элемент раздела: ради него сюда и заходят.
   Было 42px и мелким шрифтом, в ряду прочих блоков оно читалось как
   служебная строка поиска и терялось. Подняли до 56px, шрифт на две
   ступени крупнее, рамка в два пикселя и подложка акцентом - блок
   сам говорит «печатать здесь», без подписи-инструкции. */
.chat-input {
    display: flex;
    gap: var(--sp-3);
    padding: var(--sp-3);
    background: var(--accent-soft);
    border: 1px solid var(--accent-line);
    border-radius: var(--r-3);
}

.chat-field {
    flex: 1 1 auto;
    min-height: 56px;
    padding: 16px 18px;
    background: var(--surface);
    border: 2px solid var(--border-strong);
    border-radius: var(--r-2);
    color: var(--text);
    font-size: var(--fs-5);
    line-height: 1.4;
    resize: none;
    transition: border-color var(--transition), box-shadow var(--transition);
}

.chat-field::placeholder { color: var(--muted); }

.chat-field:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 4px var(--accent-soft);
}

/* На акцентной заливке иконка идет цветом текста кнопки, а не приглушенным:
   правило .panel-title .ico / .tab .ico сюда не достает, но подстрахуемся явно */
/* Сборщик Tabler-редизайна (C2-3/C5-2): старый .btn (solid, height 42px)
   перебивал контурные/sm/btn-group кнопки Tabler в новых вьюхах. Старых
   потребителей .btn осталось два (чат #br-send и нормы #src-norms-save) -
   правила заскоуплены на их id, имя .btn отдано Tabler. */
#br-send .ico { color: currentColor; }

/* Кнопка догоняет поле по росту, иначе пара смотрится ступенькой */
.chat-input .btn {
    /* height:auto + align-items:stretch у флекса: кнопка тянется по фактической
       высоте поля. Фиксированные 56px давали ступеньку, когда поле в две строки
       выросло до 78px. */
    height: auto;
    align-self: stretch;
    padding: 0 26px;
    font-size: var(--fs-5);
}

@media (max-width: 560px) {
    .chat-input { flex-direction: column; }
    .chat-input .btn { width: 100%; }
}

#br-send,
#src-norms-save {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    height: 42px;
    padding: 0 18px;
    background: var(--accent);
    border: 1px solid var(--accent);
    border-radius: var(--radius);
    color: var(--on-accent);
    font-size: var(--fs-4);
    font-weight: 700;
    transition: opacity var(--transition);
}

#br-send:hover,
#src-norms-save:hover { opacity: 0.88; }
#br-send:focus-visible,
#src-norms-save:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* .btn.ghost погашен: единственный потребитель (старая модалка контрагента
   cp-close) удален splice W4, новая модалка - .btn.tb4-modal-close (Tabler). */

.suggests { display: flex; flex-wrap: wrap; gap: var(--sp-2); }

.suggest {
    padding: 6px 11px;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--r-pill);
    color: var(--dim);
    font-size: var(--fs-3);
    text-align: left;
    transition: background var(--transition), color var(--transition), border-color var(--transition);
}

.suggest:hover {
    background: var(--accent-soft);
    border-color: var(--accent-line);
    color: var(--text);
}

/* Підказка з клавіатури: той самий стан, що й hover, плюс кільце.
   У сусідів (.btn, .map-pin, .float-nav button) focus-visible вже є -
   suggest був єдиною інтерактивною плямою без нього. */
.suggest:focus-visible {
    background: var(--accent-soft);
    border-color: var(--accent-line);
    color: var(--text);
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.typing { display: inline-flex; gap: 4px; padding: 3px 0; }

.typing i {
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: var(--muted);
    animation: typeBlink 1.2s infinite;
}

.typing i:nth-child(2) { animation-delay: 0.2s; }
.typing i:nth-child(3) { animation-delay: 0.4s; }

@keyframes typeBlink {
    0%, 60%, 100% { opacity: 0.25; }
    30% { opacity: 1; }
}

/* ============ КАРТА ЛОКАЦИЙ ============ */
/* Пины расставляются в процентах через inline left/top, а не в координатах SVG:
   так views.js может двигать точку, не пересчитывая viewBox, и карта переживает
   замену подложки на другую страну без правки JS. */

/* Соотношение сторон карты = соотношение viewBox контура (100 x 68.58).
   Пины стоят в процентах от КОНТЕЙНЕРА (inline left/top из views.js), а SVG
   рисуется с preserveAspectRatio="none" - он растягивается ровно по рамке
   контейнера без полей, поэтому проценты пина и координаты viewBox
   совпадают один в один. Пропорция здесь нужна уже не как компенсация,
   а чтобы страна не выглядела сплюснутой. */
.map-wrap {
    position: relative;
    width: 100%;
    aspect-ratio: 100 / 68.58;
    /* Ограничиваем по ШИРИНЕ: на широком экране карта иначе уходит
       за 900px высоты и раздел снова читается как плита пустоты. */
    max-width: 820px;
    margin: 0 auto;
    /* Море: тон панели плюс мягкое свечение сверху, чтобы полоса
       не читалась как пустая плита. */
    background:
        radial-gradient(120% 90% at 50% 15%, var(--accent-soft) 0%, transparent 62%),
        var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-3);
    overflow: hidden;
}

/* Подложка растягивается целиком: по ней позиционируются пины,
   поэтому она обязана совпадать с рамкой контейнера пиксель в пиксель */
.map-svg {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    display: block;
}

/* Контур. Раньше заливка контура и фон панели были одним и тем же
   --surface-2: страна растворялась в подложке, и раздел читался как
   пустая серая плита с точками. Контур - отдельная поверхность поверх
   фона панели, с видимой линией границы. */
.map-svg path {
    /* Суша заметно плотнее моря: --surface-2 в светлой теме почти совпадал
       с фоном панели и контур растворялся. color-mix даeт разницу в обеих
       темах, не заводя новую переменную. */
    fill: color-mix(in oklch, var(--surface-2) 72%, var(--border) 28%);
    stroke: var(--border-strong);
    stroke-width: 1.25;
    vector-effect: non-scaling-stroke;
}

.map-pin {
    position: absolute;
    z-index: 2;
    display: grid;
    place-items: center;
    width: 22px;
    height: 22px;
    padding: 0;
    background: none;
    border: none;
    transform: translate(-50%, -50%);
    transition: transform var(--transition), z-index 0s;
}

.map-pin:hover,
.map-pin:focus-visible,
.map-pin.is-active { z-index: 6; }

.map-pin:hover,
.map-pin:focus-visible {
    z-index: 5;
    transform: translate(-50%, -50%) scale(1.12);
}

.map-pin:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 4px;
    border-radius: var(--r-2);
}

/* Точка. Белое кольцо вокруг обязательно: без него пин на пестрой подложке
   теряется, а с кольцом читается на любом фоне. */
.map-pin-dot {
    position: relative;
    width: 14px;
    height: 14px;
    border-radius: 50%;
    background: var(--muted);
    border: 2px solid var(--surface);
    box-shadow: var(--shadow);
    transition: background var(--transition), box-shadow var(--transition);
}

/* Подпись города под точкой. Плашка, а не голый текст: над картой
   текст без фона нечитаем.

   Главная правка: подписи больше не висят все разом. В сети девять точек,
   но городов пять - Киев, Львов, Днепр идут парами (розница плюс склад
   рядом), и подписи ложились друг на друга, читаясь как дубли "Киев Киев".
   Теперь подпись видна у активной точки и у той, на которую навели или
   попали табом. Остальные точки - только кружок, название в title и в
   таблице ниже. Наложений нет по построению, а не по подбору координат. */
.map-pin-label {
    position: absolute;
    top: calc(100% + 4px);
    left: 50%;
    transform: translateX(-50%);
    padding: 2px 8px;
    border-radius: var(--r-pill);
    background: var(--surface);
    border: 1px solid var(--border);
    box-shadow: var(--shadow-2);
    color: var(--dim);
    font-size: var(--fs-2);
    font-weight: 600;
    white-space: nowrap;
    pointer-events: none;
    opacity: 0;
    transition: opacity var(--transition), background var(--transition), color var(--transition), border-color var(--transition);
}

.map-pin.is-active .map-pin-label,
.map-pin:hover .map-pin-label,
.map-pin:focus-visible .map-pin-label {
    opacity: 1;
}

/* Подписи постоянно видны от 1200px. Демо показывают через демонстрацию
   экрана и по статичным скриншотам: девять безымянных кружков не дают
   сравнивать точки, а весь смысл раздела именно в сравнении. Ниже 1200px
   подписи остаются по hover - там ширины на них нет. */
@media (min-width: 1200px) {
    .map-pin-label {
        opacity: 1;
        /* Приглушены, чтобы карта не превращалась в стену текста.
           Наведение и активная точка выводят подпись на первый план. */
        background: color-mix(in oklch, var(--surface) 82%, transparent);
        color: var(--muted);
        box-shadow: none;
    }

    .map-pin:hover .map-pin-label,
    .map-pin:focus-visible .map-pin-label,
    .map-pin.is-active .map-pin-label {
        background: var(--surface);
        color: var(--text);
        box-shadow: var(--shadow-2);
    }

    .map-pin.is-active .map-pin-label {
        background: var(--accent);
        border-color: var(--accent);
        color: var(--on-accent);
    }

    /* Развод подписей по диагонали. В парах Киев/Позняки, Львов/склад,
       Днепр/склад пины стоят в 10-21px друг от друга: подпись под каждым
       налезала бы на соседнюю. Верхний объект пары подписан сверху,
       нижний снизу, плюс разные стороны по горизонтали. */
    .map-pin.lbl-tl .map-pin-label,
    .map-pin.lbl-tr .map-pin-label {
        top: auto;
        bottom: calc(100% + 4px);
    }

    .map-pin.lbl-tl .map-pin-label,
    .map-pin.lbl-bl .map-pin-label {
        left: auto;
        right: calc(50% + 6px);
        transform: none;
    }

    .map-pin.lbl-tr .map-pin-label,
    .map-pin.lbl-br .map-pin-label {
        left: calc(50% + 6px);
        right: auto;
        transform: none;
    }
}

/* Статус локации. Цветом красим только точку и ореол вокруг нее:
   если красить и подпись, карта превращается в светофор и теряет читаемость. */
.map-pin.is-ok .map-pin-dot { background: var(--ok); box-shadow: 0 0 0 4px var(--ok-soft); }
.map-pin.is-warn .map-pin-dot { background: var(--warn); box-shadow: 0 0 0 4px var(--warn-soft); }

/* Ореол у alert постоянный, ровно такой же, как у ok и warn: отличается
   только цвет. Раньше он был кадром анимации mapPulse (0%/100% - кольцо,
   50% - transparent), поэтому большую часть цикла Харьков стоял голой
   точкой и на статичном скриншоте читался как недоделка. Пульсацию унесли
   на отдельный слой ::after, база теперь не мигает. */
.map-pin.is-alert .map-pin-dot {
    background: var(--err);
    box-shadow: 0 0 0 4px var(--err-soft);
}

/* Пульсирующее кольцо поверх постоянного ореола. Отдельный слой, а не
   box-shadow самой точки: так анимация не может погасить базовое кольцо.
   Пульсация только на alert: если пульсируют все пины, не пульсирует ни один. */
.map-pin.is-alert .map-pin-dot::after {
    content: '';
    position: absolute;
    inset: -2px;
    border-radius: 50%;
    pointer-events: none;
    animation: mapPulse 2.2s var(--ease-out) infinite;
}

@keyframes mapPulse {
    0%, 100% { box-shadow: 0 0 0 0 var(--err-soft); }
    50% { box-shadow: 0 0 0 10px transparent; }
}

/* Активная локация - та, на которую пересчитан весь экран. Ее подпись
   заливается акцентом, чтобы было видно с проектора, какой срез открыт. */
.map-pin.is-active {
    z-index: 4;
    transform: translate(-50%, -50%) scale(1.08);
}

.map-pin.is-active .map-pin-label {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--on-accent);
}

.map-pin.is-active .map-pin-dot {
    border-color: var(--accent);
}

.map-pin.is-active:hover {
    transform: translate(-50%, -50%) scale(1.16);
}

/* Легенда идет под картой и по ее ширине, а не растягивается на всю панель */
.map-wrap + .legend {
    max-width: 900px;
    margin: var(--sp-3) auto 0;
    justify-content: center;
}

/* На телефоне воронка перестраивается в две строки: подпись с цифрами
   сверху, полоса под ними во всю ширину. Три колонки в 390px не живут. */
@media (max-width: 560px) {
    .funnel-step {
        grid-template-columns: 1fr auto;
        gap: var(--sp-1) var(--sp-2);
        padding: var(--sp-2) 0;
    }

    .funnel-track { grid-column: 1 / -1; grid-row: 2; height: 22px; }
    .funnel-label { grid-row: 1; }
    .funnel-conv { grid-row: 1; text-align: right; }

    .map-wrap { aspect-ratio: 4 / 3; max-height: 340px; }
    .map-pin-label { font-size: var(--fs-1); }
}

/* ============ СНОСКА ============ */

.footnote {
    margin-top: var(--sp-5);
    color: var(--muted);
    font-size: var(--fs-2);
    text-align: center;
}

/* Пульсации пинов и точек индикаторов - раздражители для тех, кто просил
   убрать движение. Глобальное правило в main.css гасит длительность,
   здесь снимаем анимацию целиком, чтобы не осталось дрожащего кадра. */
@media (prefers-reduced-motion: reduce) {
    .ind.is-alert .ind-dot,
    .map-pin.is-alert .map-pin-dot::after,
    .typing i {
        animation: none !important;
    }

    .map-pin:hover,
    .map-pin:focus-visible,
    .map-pin.is-active,
    .map-pin.is-active:hover {
        transform: translate(-50%, -50%);
    }
}

/* Статус в таблице настройки индикаторов: цветной текст вместо плашки.
   В развернутой таблице 15 строк, из них 11 со словом "критично" - ряд
   красных плашек читался как сплошное полотно и глушил сам показатель.
   У Strimix внутри таблиц статусы и дельты набраны цветным текстом,
   плашка остается только снаружи таблиц. Бейджи в других местах демо
   не трогаем: правило действует лишь внутри колонки .src-level. */
.src-level .badge {
    background: none;
    padding: 0;
    border: 0;
    font-weight: 600;
}

.src-level .badge.alert { color: var(--err-text); }
.src-level .badge.warn  { color: var(--warn-text); }
.src-level .badge.ok    { color: var(--ok); }
.src-level .badge.neutral { color: var(--muted); }

/* ============ KPI-СТРІЧКА (горизонтальне гортання) ============ */

/* Контракт R6: ім'я .kpi-scroll зафіксоване, його чекає інший потік.
   Рядок карток з прокручуванням замість стискання: на вузькому екрані
   картки їдуть пальцем, на широкому лежать усі разом. Скролбар сховано,
   зупинка магнітиться до початку картки. */
.kpi-scroll {
    display: flex;
    flex-wrap: nowrap;
    gap: var(--gap);
    overflow-x: auto;
    overflow-y: hidden;
    scroll-snap-type: x mandatory;
    scrollbar-width: none;
    -ms-overflow-style: none;
    -webkit-overflow-scrolling: touch;
}

.kpi-scroll::-webkit-scrollbar {
    display: none;
}

.kpi-scroll > * {
    flex: 0 0 auto;
    scroll-snap-align: start;
}

/* ============ ІНФРАСТРУКТУРА: сітка 2x2, камери, СЕС (потік ІНФРА) ============
   Екран 8 за ескізом клієнта: карта, камери 3x3, SVG-схема СЕС, індикатори.
   Префікси зони .if-* (плитки, сітка) і .ses-* (схема станції): з класами
   infra.css (.infra-*, .ig-*) не перетинаються, каскад не ламають. */

/* Індикатори всередині вузької комірки 2x2: дві колонки indPanel тут
   дають рядки по ~300px, текст кришиться. Одна колонка читається краще.
   Правило б'є тільки в .if-inds, інші виклики indPanel не чіпає. */
.if-inds .grid-2 {
    grid-template-columns: minmax(0, 1fr);
}

/* ---- Камери 3x3 ---- */

.if-cam-grid {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--sp-2);
}

.if-cam {
    min-width: 0;
    overflow: hidden;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-top-width: 2px;
    border-radius: var(--r-2);
}

.if-cam.is-ok { border-top-color: var(--ok); }
.if-cam.is-warn { border-top-color: var(--warn); }
.if-cam.is-alert { border-top-color: var(--err); }

/* Відеоекран-заглушка: темний в обох темах (як живе відео),
   рядки розгортки і віньєтка поверх. */
.if-cam-screen {
    position: relative;
    aspect-ratio: 16 / 10;
    overflow: hidden;
    background: linear-gradient(135deg, #232b33, #12181e 60%, #1c2620);
}

.if-cam-screen::before {
    content: '';
    position: absolute;
    inset: 0;
    background: repeating-linear-gradient(0deg, rgba(255, 255, 255, 0.05) 0 1px, transparent 1px 3px);
}

.if-cam-screen::after {
    content: '';
    position: absolute;
    inset: 0;
    background: radial-gradient(ellipse at center, transparent 55%, rgba(0, 0, 0, 0.45));
}

.if-cam.is-alert .if-cam-screen {
    background: linear-gradient(135deg, #2a2323, #171212 65%, #201a1a);
}

.if-cam-rec {
    position: absolute;
    top: 7px;
    left: 8px;
    z-index: 2;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--err);
    box-shadow: 0 0 6px var(--err);
    animation: if-blink 1.6s ease-in-out infinite;
}

.if-cam.is-alert .if-cam-rec {
    background: var(--muted);
    box-shadow: none;
    animation: none;
}

.if-cam-live {
    position: absolute;
    top: 5px;
    right: 8px;
    z-index: 2;
    color: #fff;
    font-size: 10px;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
    opacity: 0.85;
}

.if-cam-meta {
    display: flex;
    flex-direction: column;
    gap: 1px;
    padding: 6px 8px 7px;
}

.if-cam-name {
    overflow: hidden;
    font-size: 12px;
    font-weight: 600;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.if-cam-state {
    font-size: 11px;
    color: var(--muted);
    white-space: nowrap;
}

@keyframes if-blink {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.25; }
}

/* ---- Схема СЕС ---- */

.ses-svg {
    display: block;
    width: 100%;
    height: auto;
}

.ses-node rect {
    fill: var(--surface-2);
    stroke: var(--border-strong);
    stroke-width: 1.5;
}

.ses-node.is-core rect {
    stroke: var(--warn);
    stroke-width: 2;
}

.ses-name {
    fill: var(--text);
    font-size: 13px;
    font-weight: 700;
}

.ses-sub {
    fill: var(--muted);
    font-size: 11px;
}

/* Потік енергії: пунктир біжить за напрямком струму (шляхи в infraSesSvg
   намальовано від джерела до споживача). Довжина такту 6+5=11. */
.ses-flow {
    fill: none;
    stroke: var(--warn);
    stroke-width: 2;
    stroke-dasharray: 6 5;
    opacity: 0.85;
    animation: if-flow 0.9s linear infinite;
}

@keyframes if-flow {
    to { stroke-dashoffset: -11; }
}

/* Підпис кВт на потоці: ореол кольору панелі, щоб лінія не била по тексту. */
.ses-kw {
    fill: var(--dim);
    font-family: var(--font-mono, monospace);
    font-size: 11px;
    paint-order: stroke;
    stroke: var(--surface);
    stroke-width: 3px;
}

.ses-batt-track { fill: var(--border); }
.ses-batt-fill { fill: var(--ok); }

@media (max-width: 560px) {
    .if-cam-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

@media (prefers-reduced-motion: reduce) {
    .if-cam-rec,
    .ses-flow {
        animation: none !important;
    }
}

/* ============ ТИПОГРАФИКА: ПАРА MANROPE + ROBOTO ============ */

/* База - Roboto. main.css ставит на html var(--font-display), переопределяем
   здесь: этот файл грузится позже, специфичность та же, побеждает порядок.
   main.css не трогаем. Размер базы едет из токена --fs-3 (14px). */
html {
    font-family: var(--font-body);
    /* macOS за замовчуванням малює текст важчим, ніж задумано в шрифті.
       Згладжування один раз на корені (gesso BASE font-smoothing);
       інші платформи обидва правила ігнорують. */
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

/* Заголовки панелей - Manrope, ступень шкалы --fs-5 (18px).
   Вес и цвет не трогаем: у .is-label свои 600/dim (.v3-lead удален 28.09.2026). */
.panel-title {
    font-family: var(--font-display);
    font-size: var(--fs-5);
    /* Дворядковий заголовок рівняється симетрично, а не рветься
       з одним словом у другому рядку (gesso BASE text-wrap). */
    text-wrap: balance;
}

/* Подпись рядом с заголовком панели - Roboto, база --fs-3. */
.panel-sub {
    font-family: var(--font-body);
    font-size: var(--fs-3);
}

/* Крупная цифра KPI - Manrope, текучая ступень --fs-7 (26-36px).
   tabular-nums из правила выше Manrope не поддерживает - цифры
   пропорциональные, это нормально для display-шрифта. */
.kpi-value {
    font-family: var(--font-display);
    font-size: var(--fs-7);
}

/* Единица рядом с цифрой - Roboto, --fs-3. */
.kpi-cur {
    font-family: var(--font-body);
    font-size: var(--fs-3);
}

/* Метка карточки - Roboto, --fs-2. Капс и вес из правила выше. */
.kpi-label {
    font-family: var(--font-body);
    font-size: var(--fs-2);
}

/* Пояснение под цифрой и база сравнения - Roboto, --fs-3 и --fs-2. */
.kpi-sub {
    font-family: var(--font-body);
    font-size: var(--fs-3);
}

.kpi-was {
    font-family: var(--font-body);
    font-size: var(--fs-2);
}

/* Подзаголовок раздела (видимый, .page-title скрыт для скринридеров) - Roboto. */
.page-sub {
    font-family: var(--font-body);
}

/* Сноска внизу страницы - Roboto, --fs-2. */
.footnote {
    font-family: var(--font-body);
    font-size: var(--fs-2);
}

/* ============ ДЕ-СЛОП ХВИЛЯ (зони sales/finance/infra, префікс vw-) ============
   Умови хвилі: CSS тільки аппендом сюди, чужі CSS-файли не чіпати.
   components.css підключено РАНІШЕ за sales/counterparty/infra.css,
   тому перевизначення їхніх правил - через #id розділу: специфічність
   1-1-0 бє їхні 0-1-0 незалежно від порядку підключення. */

/* ---- Сітка 3-2 для продажів (таблиця дня + донат) ----
   Системна 2-1 повторювалася двічі в одному розділі (воронка і дзвінки).
   3-2 - не краса заради дробу: таблиці з 2 колонок забагато 2fr,
   а донату з правою легендою замало 1fr. Брейкпоінт 1240 - той самий,
   що в main.css для інших асиметричних сіток. */
.vw-grid-3-2 { grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); }

@media (max-width: 1240px) {
    .vw-grid-3-2 { grid-template-columns: minmax(0, 1fr); }
}

/* ---- Плоский список дзвінків (K11: бокси в боксі) ----
   Шість .sl-call з рамкою+радіусом всередині .panel давали три рівні
   вкладеності разом з боксом розшифровки. Стало як в ind-list: один
   рівень вкладеності, межа - волосяний розділювач. Бокс розшифровки
   (.sl-trans-body) не чіпаємо - це контент, а не контейнер. */
#sales-body .vw-calls-flat .sl-call {
    padding: var(--sp-3) 0;
    background: none;
    border: 0;
    border-radius: 0;
}

#sales-body .vw-calls-flat .sl-call + .sl-call {
    margin-top: 0;
    box-shadow: 0 -1px 0 var(--border);
}

/* ---- Фінанси: токени замість голих px (файл counterparty.css чужий) ----
   Мапінг до найближчого токена: 20->sp-5, 16->sp-4, 14->sp-4, 12->sp-3,
   10->sp-3 (падінги) / r-2 (радіус 12->10), 8->sp-2, 6->sp-2, 4->sp-1,
   2->sp-1, 28->gap-xl. Тінь модалки - var(--shadow-2): вона для цього
   і заведена. Вигляд навмисно майже не змінюється - хірургія, не редизайн. */
#finance-body .cp-modal { padding: var(--sp-5); }

#finance-body .cp-card {
    padding: var(--sp-5) var(--sp-5) var(--sp-4);
    border-radius: var(--r-2);
    box-shadow: var(--shadow-2);
}

#finance-body .cp-name { margin: var(--sp-1) 0 var(--sp-3); }

#finance-body .cp-meta {
    gap: var(--sp-2) var(--sp-4);
    margin-bottom: var(--sp-3);
}

#finance-body .cp-close { margin-top: var(--sp-3); }

#finance-body .bal-grid { gap: var(--sp-4) var(--gap-xl); }

#finance-body .bal-col {
    padding: var(--sp-3) var(--sp-4);
    border-radius: var(--r-2);
}

#finance-body .bal-cap { margin-bottom: var(--sp-2); }

#finance-body .bal-row {
    gap: var(--sp-3);
    padding: var(--sp-2) 0;
}

#finance-body .bal-row.is-total {
    margin-top: var(--sp-1);
    padding-top: var(--sp-3);
}

@media (max-width: 560px) {
    #finance-body .cp-card { padding: var(--sp-4) var(--sp-4) var(--sp-3); }
}

/* ---- Інфра: гало СЕС до мови системи ----
   Було 0 0 0 3px color-mix(warn 35%): ні токен, ні системне кільце.
   Кільця статусів у системи - 4px + soft-токен (див. .map-pin-dot). */
#infrastructure-body .infra-pin.is-type-ses .map-pin-dot {
    box-shadow: 0 0 0 4px var(--warn-soft);
}

/* ---- Огляд: контентні ширини колонок (wave-E, еталон s1) ----
   Рівні третини душили текст індикаторів і залишали чарти вузькими.
   Чартам виручки/прибутку - ширше, цифрам структури - вужче. */
#overview-body .ov-grid {
    grid-template-columns: 1.25fr 1.25fr 1fr;
}

/* ---- Лого ширше і більше (28.09, наказ + фото) ----
   Перевизначення поверх main.css: кегль, трекінг, крапка, бейдж. */
.nav-brand {
    font-size: var(--fs-6);
    letter-spacing: 0.06em;
    gap: var(--sp-3);
}
.nav-dot {
    width: 12px;
    height: 12px;
}
.nav-demo {
    padding: 2px 10px;
    font-size: var(--fs-3);
}

/* ---- Підменю-якорі приховано (28.09): навігація - плаваюча кнопка ----
   Виняток - маркетинг: вкладки mk-pane перемикаються САМЕ підменю
   (зона Tolya, його перебудова це зніме). Видимістю керує motion.js
   по хешу маршруту, тут лише дефолт. */
#subnav {
    display: none;
}

/* ---- Плаваюча кнопка вгору/вниз: єдина на всі розділи ---- */
.float-nav {
    position: fixed;
    right: 18px;
    bottom: 18px;
    z-index: 60;
    display: flex;
    flex-direction: column;
    border: 1px solid var(--border-strong);
    border-radius: var(--r-2);
    background: var(--surface);
    box-shadow: 0 4px 16px rgb(0 0 0 / 0.14);
    overflow: hidden;
}
.float-nav button {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 42px;
    height: 38px;
    border: 0;
    background: transparent;
    color: var(--dim);
    cursor: pointer;
}
.float-nav button:hover {
    background: var(--surface-hover);
    color: var(--text);
}
.float-nav button + button {
    border-top: 1px solid var(--border);
}
.float-nav button:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: -2px;
}
/* === NAV.CSS === */
/* demo/assets/css/nav.css */
/* НАЗНАЧЕНИЕ: раскладка меню как в Strimix - главное меню слева (сайдбар),
   сверху строка подменю с якорями к блокам. Перебивает базовые .tabs
   из main.css: этот файл подключен позже, те же селекторы побеждают. */
/* СВЯЗИ: variables.css (только токены - обе темы без отдельных правил),
   index.php (#tabs, #subnav), app.js (buildNav, renderSubnav, tagAnchors) */

:root {
    --nav-w: 216px;
}

/* ============ САЙДБАР ============ */

.tabs {
    position: fixed;
    top: 56px;
    left: 0;
    bottom: 0;
    z-index: 40;
    width: var(--nav-w);
    flex-direction: column;
    align-items: stretch;
    gap: 2px;
    padding: var(--sp-3) var(--sp-2);
    background: var(--surface);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    border-right: 1px solid var(--border);
    box-shadow: none;
    overflow-x: hidden;
    overflow-y: auto;
}

.tabs > .tab:first-child,
.tabs > .tab:last-child {
    margin-left: 0;
    margin-right: 0;
}

.tab {
    width: 100%;
    padding: 9px 12px;
    gap: 10px;
    border-radius: var(--r-2);
    color: var(--dim);
    font-size: var(--fs-4);
    text-align: left;
}

/* Подчеркивание снизу превращается в планку слева */
.tab::after {
    left: 0;
    right: auto;
    top: 8px;
    bottom: 8px;
    width: 3px;
    height: auto;
    border-radius: 0 var(--r-pill) var(--r-pill) 0;
}

.tab:hover {
    color: var(--text);
    background: var(--surface-hover);
}

.tab:hover::after {
    background: var(--border-strong);
}

.tab.active {
    color: var(--on-blue);
    font-weight: 600;
    background: var(--blue);
}

.tab.active::after {
    background: transparent;
}

.tab .ico {
    width: 18px;
    height: 18px;
    flex: 0 0 auto;
}

.tab-badge {
    margin-left: auto;
}

/* Контент уступает место сайдбару */
.page {
    margin-left: var(--nav-w);
}

/* ============ ПОДМЕНЮ ============ */

.subnav {
    position: sticky;
    top: 56px;
    z-index: 30;
    display: flex;
    align-items: center;
    gap: 8px;
    margin-left: var(--nav-w);
    padding: var(--sp-2) var(--shell-pad);
    background: color-mix(in oklab, var(--bg) 82%, transparent);
    -webkit-backdrop-filter: blur(18px) saturate(180%);
    backdrop-filter: blur(18px) saturate(180%);
    box-shadow: inset 0 -1px 0 color-mix(in oklab, var(--border) 70%, transparent);
    overflow-x: auto;
    scrollbar-width: none;
}

.subnav::-webkit-scrollbar {
    display: none;
}

.subnav-link {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    min-height: 40px;
    padding: 10px 20px;
    background: none;
    border: 0;
    border-radius: var(--r-pill);
    color: var(--muted);
    font-size: 14px;
    line-height: 1.25;
    font-weight: 500;
    white-space: nowrap;
    transition: background var(--transition), color var(--transition);
}

.subnav-link:hover {
    background: var(--surface-hover);
    color: var(--text);
}

.subnav-link.active {
    background: var(--blue);
    color: var(--on-blue);
    font-weight: 600;
}

.subnav-link:focus-visible {
    outline: 2px solid var(--blue);
    outline-offset: -2px;
}

/* Якорь не должен прятаться под липкое подменю (шапки-топбара больше нет:
   было 138px = 56 топбар + подменю + запас, стало 82px). */
#mk-results,
#mk-channels,
#mk-funnel,
#sl-overview,
#sl-calls {
    scroll-margin-top: 82px;
}

/* ============ МОБИЛЬНЫЙ ============ */

@media (max-width: 900px) {
    /* Сайдбар складывается обратно в верхнюю строку */
    .tabs {
        position: sticky;
        top: 56px;
        width: auto;
        flex-direction: row;
        align-items: center;
        gap: var(--sp-2);
        padding: 0 var(--sp-3);
        background: color-mix(in oklab, var(--bg) 82%, transparent);
        -webkit-backdrop-filter: blur(18px) saturate(180%);
        backdrop-filter: blur(18px) saturate(180%);
        border-right: 0;
        box-shadow: inset 0 -1px 0 color-mix(in oklab, var(--border) 70%, transparent);
        overflow-x: auto;
        overflow-y: hidden;
    }

    .tab {
        width: auto;
        padding: 12px 10px;
        border-radius: 0;
    }

    .tab::after {
        left: var(--sp-2);
        right: var(--sp-2);
        top: auto;
        bottom: -1px;
        width: auto;
        height: 2px;
        border-radius: var(--r-pill) var(--r-pill) 0 0;
    }

    .tab:hover {
        background: none;
    }

    .tab.active {
        background: none;
    }

    .tab-badge {
        margin-left: 0;
    }

    .page,
    .subnav {
        margin-left: 0;
    }

    .subnav {
        top: 101px;
        padding-left: var(--sp-3);
        padding-right: var(--sp-3);
    }
}
/* === PLANFACT.CSS === */
/* demo/assets/css/planfact.css */
/* НАЗНАЧЕНИЕ: стили потока ПЛАН-ФАКТ и ДЖИН: план-факт KPI,
   динамика, страница целей, шапка Джина. Подключается из views.js/chat.js
   динамически (ensurePFcss), чтобы не трогать index.php чужого потока. */

/* ---------- План-факт KPI (клик открывает динамику) ---------- */
.pf-kpi {
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--r-3);
    padding: var(--sp-4);
    cursor: pointer;
    text-align: left;
    font: inherit;
    color: inherit;
    display: flex;
    flex-direction: column;
    gap: var(--sp-1);
    min-width: 0;
}
.pf-kpi:hover { border-color: var(--accent); }
.pf-kpi.is-active { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.pf-kpi .pf-name { font-size: var(--fs-2); font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); }
.pf-kpi .pf-fact { font-size: var(--fs-6); font-weight: 800; line-height: 1.1; }
.pf-kpi .pf-fact small { font-size: var(--fs-3); font-weight: 600; color: var(--muted); }
.pf-kpi .pf-plan { font-size: var(--fs-3); color: var(--muted); }
.pf-kpi .pf-row { display: flex; align-items: center; gap: var(--sp-2); margin-top: var(--sp-1); }
.pf-kpi .pf-pct { font-size: var(--fs-3); font-weight: 700; white-space: nowrap; }
.pf-kpi.is-ok .pf-pct { color: var(--ok); }
.pf-kpi.is-warn .pf-pct { color: var(--warn-text); }
.pf-kpi.is-alert .pf-pct { color: var(--err-text); }
.pf-bar { flex: 1 1 auto; height: 6px; border-radius: 3px; background: var(--border); overflow: hidden; min-width: 40px; }
.pf-fill { display: block; height: 100%; border-radius: 3px; background: var(--accent); }
.is-ok .pf-fill { background: var(--ok); }
.is-warn .pf-fill { background: var(--warn); }
.is-alert .pf-fill { background: var(--err); }

/* ---------- Таблица целей ---------- */
.pf-input {
    width: 130px;
    max-width: 100%;
    font: inherit;
    font-size: var(--fs-4);
    color: var(--text);
    background: var(--bg-0);
    border: 1px solid var(--border);
    border-radius: var(--r-1);
    padding: 4px 8px;
    text-align: right;
}
.pf-input:focus { outline: none; border-color: var(--accent); }
.pf-goal-pct { display: flex; align-items: center; gap: var(--sp-2); justify-content: flex-end; }
.pf-goal-pct .pf-bar { width: 90px; flex: 0 0 auto; }
.pf-goal-pct .pf-num { font-weight: 700; min-width: 52px; text-align: right; }

/* ---------- Джин: структура відповідей (wave-E, hero знесено 28.09) ---------- */
.msg-text p { margin: 0 0 var(--sp-2); }
.msg-text p:last-child { margin-bottom: 0; }
.br-list { margin: 0 0 var(--sp-2); padding-left: 18px; }
.br-list li { margin-bottom: 2px; }

/* ---------- Сітка Екрана 1 (wireframe ТЗ) ---------- */
.ov-stats { display: flex; flex-direction: column; gap: var(--sp-3); }
.ov-stat { display: flex; flex-direction: column; line-height: 1.35; }
.ov-stat b { font-size: var(--fs-6); font-weight: 800; }
.ov-stat span { font-size: var(--fs-3); color: var(--muted); }
.ov-sums { display: flex; flex-direction: column; gap: 2px; margin-top: var(--sp-2); font-size: var(--fs-3); color: var(--muted); }
.ov-sums b { color: var(--text); }
.ov-lead { margin: 0 0 var(--sp-3); font-size: var(--fs-4); }
.ov-lead b { font-size: var(--fs-6); font-weight: 800; }
.ov-link { margin-top: var(--sp-3); padding: 0; border: 0; background: none; font: inherit; font-size: var(--fs-4); font-weight: 700; color: var(--accent); cursor: pointer; text-align: left; }
.ov-link:hover { text-decoration: underline; }
/* === COUNTERPARTY.CSS === */
/* demo/assets/css/counterparty.css */
/* НАЗНАЧЕНИЕ: строки контрагентов и модалка карточки в разделе финансов.
   Подключается из finance() в views.js (index.php трогать запрещено).
   Все классы с префиксом cp-, чужие правила не переопределяем. */
/* СВЯЗИ: views.js (finance), variables.css (токены). Темная тема - через те же токены. */

/* Кликабельная строка контрагента. Ховер уже есть у .rank, тут только курсор и фокус. */
.cp-row { cursor: pointer; }
.cp-row:focus { outline: none; }
.cp-row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* Модалка поверх всего: нав 50, табы 40 - берем с запасом. */
.cp-modal {
    position: fixed;
    inset: 0;
    z-index: 200;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
    background: rgb(0 0 0 / 0.45);
}
.cp-modal[hidden] { display: none; }

.cp-card {
    width: min(640px, 100%);
    max-height: 85vh;
    overflow: auto;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-3);
    padding: 20px 20px 16px;
    box-shadow: 0 24px 64px rgb(0 0 0 / 0.28);
}

.cp-kind {
    font-size: var(--fs-2);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--muted);
}

.cp-name {
    font-size: var(--fs-6);
    font-weight: 800;
    letter-spacing: -0.01em;
    color: var(--text);
    margin: 2px 0 10px;
}

.cp-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px 16px;
    font-size: var(--fs-3);
    color: var(--dim);
    margin-bottom: 12px;
}
.cp-meta b { color: var(--text); font-variant-numeric: tabular-nums; }

.cp-close { margin-top: 12px; width: 100%; }

@media (max-width: 560px) {
    .cp-card { padding: 16px 14px 12px; }
    .cp-name { font-size: var(--fs-5); }
}

/* === БАЛАНС (потік БАЛАНС-ВИРОБНИЦТВО) ===
   Панель "Баланс" у finance(): активи і зобов'язання двома колонками.
   Класи з префіксом bal-, чужі правила не чіпаємо. */
.bal-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 16px 28px;
}
.bal-col {
    min-width: 0;
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 12px 14px;
    background: var(--surface);
}
.bal-cap {
    font-size: var(--fs-2);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--muted);
    margin-bottom: 8px;
}
.bal-row {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 12px;
    padding: 6px 0;
    border-top: 1px dashed var(--border);
    font-size: var(--fs-3);
    color: var(--dim);
}
.bal-row b {
    color: var(--text);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}
.bal-row.is-total {
    border-top: 2px solid var(--border);
    margin-top: 4px;
    padding-top: 10px;
    color: var(--text);
    font-weight: 700;
}
.bal-row.is-total b { font-size: var(--fs-4); }

@media (max-width: 760px) {
    .bal-grid { grid-template-columns: 1fr; }
}

/* === ФІНАНСИ: hero, прапор, акценти панелей, фікс модалки (хвиля C) ===
   counterparty.css - точка стилів розділу фінансів (інші CSS чіпати
   заборонено), тому класи хвилі живуть тут з префіксом fin-. */

/* --- 0) МОДАЛКА ПО ЦЕНТРУ: fixed всередині трансформованого предка ---
   section.view.active має animation viewIn з transform-ключем (main.css).
   Трансформований предок стає containing block для position:fixed, і модалка
   центрується всередині високої секції, а не вьюпорта - картка їде під низ
   екрана (див. /tmp/polbase/modal-repro-1512.png). main.css правити не можна,
   тому для розділу фінансів перекриваємо анімацію через #id (специфічність
   id+клас б'є два класи): той самий фейд 220мс, але без трансформа. */
@keyframes finViewIn { from { opacity: 0; } to { opacity: 1; } }
#view-finance.active { animation: finViewIn 220ms var(--ease-out) both; }

/* Посилення позиціонування через #id: повний набір координат, щоб жодне
   класове правило не звело модалку до шматка екрана. */
#cp-modal {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    transform: none;
}

/* --- 1) 4 РІВНІ БОКСИ за еталоном s5 (wave-E, hero знесено) --- */
.fin-kpis { grid-template-columns: repeat(4, minmax(0, 1fr)); }
@media (max-width: 1100px) {
    .fin-kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* Компактні картки: менший падінг, цифра другого плану. */
.fin-kpis .fin-compact { padding: var(--sp-3) var(--sp-4); }
.fin-kpis .fin-compact .kpi-value {
    font-size: var(--fs-8);
    min-height: 0;
}

/* --- 3) БАЛАНС / P&L / CASH FLOW: три акценти, порядок той самий ---
   Баланс - висока колонка ліворуч (розмір уже відрізняє), плюс кожен блок
   має свою кольорову кромку зверху і крапку в заголовку. */
.fin-bal { border-top-width: 3px; }
.fin-bal { border-top-color: var(--accent); }
.fin-bal > .panel-head .panel-title::before {
    content: '';
    display: inline-block;
    width: 8px;
    height: 8px;
    margin-right: 8px;
    border-radius: 2px;
    vertical-align: baseline;
}
.fin-bal > .panel-head .panel-title::before { background: var(--accent); }

/* --- 4) ПІДЛОГА КЕГЛЯ 13px: таблиці, підписи, легенди, бейджі розділу ---
   Той самий набір, що у продажах, плюс балансові рядки і мета модалки.
   Дельту в kpi-compare не чіпаємо - там свої 14px. */
#finance-body .rank th,
#finance-body .rank .rk,
#finance-body .rank .nm .cnt,
#finance-body .legend-item,
#finance-body .muted,
#finance-body .badge,
#finance-body .delta,
#finance-body .kpi-was,
#finance-body .bal-row,
#finance-body .bal-cap,
#finance-body .cp-meta,
#finance-body .cp-kind,
#finance-body .cp-inv { font-size: 13px; }
#finance-body .kpi-compare .delta { font-size: var(--fs-3); }

/* Телефон: 4 бокси в одну колонку. */
@media (max-width: 760px) {
    .fin-kpis { grid-template-columns: minmax(0, 1fr); }
}
/* === MARKETING.CSS === */
/* demo/assets/css/marketing.css */
/* НАЗНАЧЕНИЕ: розділ маркетингу під Strimix - фільтр-ряд, шість панелей-вкладок,
   chevron-воронка, картки каналів трафіку, донати з підписом у центрі. */
/* СВЯЗИ: assets/js/views.js (marketing()), index.php (підключення) */

.mk-pane {
    display: none;
}

.mk-pane.is-active {
    display: block;
}

/* Фільтр-ряд як у Strimix: пігулки ліворуч, період по центру, атрибуція праворуч */
.mk-filter {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--sp-2);
    margin: var(--sp-3) 0 var(--sp-4);
}

.mk-select,
.mk-pill {
    appearance: none;
    padding: 7px 14px;
    border: 1px solid var(--border);
    border-radius: var(--r-pill);
    background: var(--surface);
    color: var(--text);
    font-size: var(--fs-3);
    font-weight: 500;
    line-height: 1.4;
    cursor: pointer;
    transition: border-color var(--transition), background var(--transition);
}

.mk-select {
    padding-right: 28px;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%23888' stroke-width='1.5' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 12px center;
}

.mk-select:hover,
.mk-pill:hover {
    border-color: var(--border-strong);
    background-color: var(--surface-2);
}

.mk-select:focus-visible,
.mk-pill:focus-visible {
    outline: 2px solid var(--blue);
    outline-offset: 1px;
}

.mk-pill {
    border-style: dashed;
    color: var(--dim);
}

.mk-pill.is-on {
    border-style: solid;
    border-color: var(--blue-line);
    background: var(--blue-soft);
    color: var(--blue);
}

/* Рядок-примітка під фільтром: пояснення для UTM і атрибуції */
.mk-note {
    margin: calc(var(--sp-3) * -1) 0 var(--sp-3);
    font-size: var(--fs-2);
    color: var(--muted);
}

.mk-note[hidden] {
    display: none;
}

/* Мітка основного каналу поруч із назвою в таблиці */
.mk-primary {
    display: inline-block;
    margin-left: var(--sp-2);
    padding: 1px 8px;
    border-radius: var(--r-pill);
    background: var(--blue-soft);
    color: var(--blue);
    font-size: var(--fs-2);
    font-weight: 600;
    vertical-align: 1px;
}

/* Рядки таблиці порівняння клікабельні: клік ставить фільтр каналу */
tr[data-ch] {
    cursor: pointer;
}

tr[data-ch].is-active td:first-child {
    box-shadow: inset 3px 0 0 var(--blue);
}

/* Картки каналів трафіку: клік ставить фільтр каналу */
.mk-fcard {
    display: block;
    width: 100%;
    margin: 0 0 var(--sp-2);
    padding: var(--sp-3) var(--sp-4);
    border: 1px solid var(--border);
    border-radius: var(--r-2);
    background: var(--surface);
    text-align: left;
    cursor: pointer;
    transition: border-color var(--transition), background var(--transition);
}

.mk-fcard:last-child {
    margin-bottom: 0;
}

.mk-fcard:hover {
    border-color: var(--border-strong);
    background: var(--surface-2);
}

.mk-fcard.is-active {
    border-color: var(--blue-line);
    background: var(--blue-soft);
}

.mk-fcard-top {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--sp-2);
}

.mk-fcard-name {
    font-size: var(--fs-4);
    font-weight: 600;
    color: var(--text);
}

.mk-fcard-top b {
    font-size: var(--fs-4);
    font-variant-numeric: tabular-nums;
    color: var(--text);
}

.mk-fcard-nums {
    display: block;
    margin-top: var(--sp-1);
    font-size: var(--fs-3);
    color: var(--muted);
}

.mk-fcard-nums b {
    color: var(--dim);
    font-variant-numeric: tabular-nums;
}

/* Chevron-воронка: горизонтальні стрілки як у Strimix.
   Рамка і clip-path несумісні, тому облямівка імітується тінню. */
.mk-chev-row {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 6px;
    margin-top: var(--sp-3);
}

.mk-chev {
    position: relative;
    padding: var(--sp-3) var(--sp-3) var(--sp-3) var(--sp-4);
    background: var(--surface-2);
    clip-path: polygon(0 0, calc(100% - 18px) 0, 100% 50%, calc(100% - 18px) 100%, 0 100%, 18px 50%);
}

.mk-chev:first-child {
    padding-left: var(--sp-3);
    clip-path: polygon(0 0, calc(100% - 18px) 0, 100% 50%, calc(100% - 18px) 100%, 0 100%);
}

.mk-chev:last-child {
    clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%, 18px 50%);
}

.mk-chev-head {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
}

.mk-chev-n {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 20px;
    height: 20px;
    padding: 0 5px;
    border-radius: var(--r-pill);
    background: var(--blue-soft);
    color: var(--blue);
    font-size: var(--fs-2);
    font-weight: 700;
}

.mk-chev-t {
    font-size: var(--fs-3);
    font-weight: 600;
    color: var(--dim);
}

.mk-chev b.mk-chev-v {
    display: block;
    margin: var(--sp-2) 0 var(--sp-1);
    font-size: var(--fs-8);
    font-variant-numeric: tabular-nums;
    color: var(--text);
}

.mk-chev-bar {
    display: block;
    height: 8px;
    border-radius: var(--r-pill);
    background: var(--border);
    overflow: hidden;
}

.mk-chev-bar i {
    display: block;
    height: 100%;
    border-radius: var(--r-pill);
    background: var(--blue);
}

.mk-chev-p {
    display: block;
    margin-top: var(--sp-1);
    font-size: var(--fs-2);
    color: var(--muted);
}

.mk-chev-drop {
    display: inline-block;
    margin-top: var(--sp-2);
    padding: 2px 10px;
    border-radius: var(--r-pill);
    background: var(--warn-soft);
    color: var(--warn-text);
    font-size: var(--fs-2);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

.mk-chev-drop small {
    display: block;
    font-weight: 400;
    color: var(--muted);
}

/* Перемикач масштабу смуг воронки */
.mk-scale {
    display: inline-flex;
    border: 1px solid var(--border);
    border-radius: var(--r-pill);
    overflow: hidden;
}

.mk-scale button {
    padding: 3px 12px;
    border: 0;
    background: none;
    color: var(--muted);
    font-size: var(--fs-2);
    font-weight: 600;
    cursor: pointer;
}

.mk-scale button.is-on {
    background: var(--text);
    color: var(--bg-0);
}

/* Донат з підписом у центрі. Легенда Chart.js праворуч займає близько
   третини ширини, тому центр рахуємо від зони графіка, а не панелі. */
.mk-donut-wrap {
    position: relative;
}

.mk-donut-center {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    width: 62%;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    pointer-events: none;
}

.mk-donut-center b {
    font-size: var(--fs-6);
    font-variant-numeric: tabular-nums;
    color: var(--text);
}

.mk-donut-center span {
    font-size: var(--fs-2);
    color: var(--muted);
}

.mk-donut-wrap.tall .chart-box {
    height: 280px;
}

/* Смуга плану продажів під фільтром */
.mk-plan {
    display: flex;
    flex-wrap: wrap;
    gap: var(--gap);
    margin: 0 0 var(--sp-4);
    padding: var(--sp-3) var(--sp-4);
    border: 1px solid var(--blue-line);
    border-radius: var(--r-2);
    background: var(--blue-soft);
}

.mk-plan-item {
    min-width: 140px;
}

.mk-plan-item span {
    display: block;
    font-size: var(--fs-2);
    color: var(--muted);
}

.mk-plan-item b {
    font-size: var(--fs-5);
    font-variant-numeric: tabular-nums;
    color: var(--text);
}

/* KPI-скролл як у Strimix: одна горизонтальна стрічка, картки фіксованої ширини.
   Контейнер .kpi-scroll (flex + overflow-x) живе в components.css, тут лише
   ширина карток, обгортка і стрілки по краях ряду. */
.kpi-scroll .kpi-card {
    flex: 0 0 234px;
}

.kpi-scroll-wrap {
    position: relative;
}

.kpi-scroll-arrow {
    position: absolute;
    top: 50%;
    z-index: 2;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    border: 1px solid var(--border);
    border-radius: 50%;
    background: var(--surface);
    color: var(--dim);
    font-size: 18px;
    line-height: 1;
    cursor: pointer;
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.12);
    transform: translateY(-50%);
    transition: border-color var(--transition), color var(--transition);
}

.kpi-scroll-arrow:hover {
    border-color: var(--border-strong);
    color: var(--text);
}

.kpi-scroll-arrow:focus-visible {
    outline: 2px solid var(--blue);
    outline-offset: 1px;
}

.kpi-scroll-arrow[data-mkscroll="-1"] {
    left: 0;
}

.kpi-scroll-arrow[data-mkscroll="1"] {
    right: 0;
}

@media (max-width: 1100px) {
    .mk-chev-row {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    .mk-chev,
    .mk-chev:first-child,
    .mk-chev:last-child {
        clip-path: none;
        border-radius: var(--r-2);
    }
}

@media (max-width: 640px) {
    .mk-chev-row {
        grid-template-columns: minmax(0, 1fr);
    }

    .mk-donut-center {
        width: 100%;
    }
}
/* === SALES.CSS === */
/* demo/assets/css/sales.css */
/* ПРИЗНАЧЕННЯ: розділ продажів - клікабельні KPI (динаміка) і картки дзвінків
   з розшифровками. Підключено з index.php. */
/* ЗВ'ЯЗКИ: views.js (sales), variables.css (токени). Темна тема - через ті самі токени. */
/* Усі класи з префіксом sl-, чужі правила не переозначаємо. */

/* Клікабельна картка KPI. Базу дає .kpi-card, тут лише курсор, ховер і фокус. */
.sl-click { cursor: pointer; }
.sl-click:hover { border-color: var(--accent-line); }
.sl-click:focus { outline: none; }
.sl-click:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.sl-click.is-active {
    border-color: var(--accent-line);
    box-shadow: inset 0 0 0 1px var(--accent-line);
}

/* Верх продажів: зліва сітка KPI 2х2, справа липка Динаміка.
   7/5: лівій колонці треба ~490px, щоб картка тримала 240px
   і одиниця («грн») не падала на другий рядок. */
.sl-top {
    display: grid;
    grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
    gap: var(--gap);
    align-items: start;
}
/* 2х2 поверх базового .kpi-row, тільки всередині .sl-top:
   інші ряди KPI (дзвінки) не чіпаємо. */
.sl-top .sl-kpis {
    grid-template-columns: repeat(2, minmax(0, 1fr));
}
/* Липка колонка: 116px = шапка 56 + підменю ~48 + 12 повітря. */
.sl-dyn {
    position: sticky;
    top: 116px;
}

/* Картка дзвінка з розшифровкою. */
.sl-call {
    border: 1px solid var(--border);
    border-radius: var(--r-2);
    padding: var(--sp-3) var(--sp-4);
    background: var(--surface);
}
.sl-call + .sl-call { margin-top: var(--sp-3); }

.sl-call-head {
    display: flex;
    align-items: baseline;
    gap: var(--sp-3);
    flex-wrap: wrap;
}
.sl-call-title { font-size: var(--fs-4); }
.sl-call-meta {
    margin-left: auto;
    font-size: var(--fs-2);
    color: var(--muted);
}

/* Розшифровка на нативному details: без JS, працює скрізь. */
.sl-trans { margin-top: var(--sp-2); }
.sl-trans > summary {
    cursor: pointer;
    display: inline-block;
    font-size: var(--fs-3);
    font-weight: 600;
    color: var(--blue);
    border-radius: var(--r-1);
}
.sl-trans > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.sl-trans-body {
    margin-top: var(--sp-2);
    padding: var(--sp-3) var(--sp-4);
    border-left: 2px solid var(--blue-line);
    background: var(--surface-2);
    border-radius: 0 var(--r-1) var(--r-1) 0;
}
.sl-line {
    margin: 0 0 var(--sp-2);
    font-size: var(--fs-3);
    line-height: 1.55;
    color: var(--text);
}
.sl-line:last-child { margin-bottom: 0; }
.sl-who { font-weight: 700; }
.sl-line.is-c .sl-who { color: var(--dim); }

@media (max-width: 760px) {
    .sl-call-meta { margin-left: 0; width: 100%; }
}

/* Вузький екран: колонки складаються одна під одну, липкість вимкнена -
   у стосі їй ніде працювати, лише ризик перекриття. */
@media (max-width: 1150px) {
    .sl-top { grid-template-columns: minmax(0, 1fr); }
    .sl-dyn { position: static; }
}

/* Телефон: картки 2х2 не влізають у 240px - в одну колонку. */
@media (max-width: 520px) {
    .sl-top .sl-kpis { grid-template-columns: minmax(0, 1fr); }
}

/* ===== WAVE-E 28.09: еталон s4 - 2х2 рівні бокси, вкладки, клікабельна воронка ----
   Знесено: hero-якір (.sl-hero), спарклайн (.sl-spark), прапор (.sl-flag),
   пігулки каналів (.sl-chan), 3-колонковий оверрайд .sl-kpis. Базове правило
   .sl-top .sl-kpis 2х2 вище знову активне. */

/* Клікабельний етап воронки: активний підсвічено рамкою. */
/* Вкладки Огляд | Дзвінки: відступ до контенту. */
#sales-body .sl-tabs { margin: var(--sp-3) 0 var(--sp-4); }

/* 02.10: внутренний отступ - рамка выбранного этапа не липнет к тексту
   (у всех кликабельных строк, чтобы колонки не прыгали при выборе). */
#sales-body .funnel-step[data-sl-stage] { cursor: pointer; border-radius: var(--r-1); padding: 4px 8px; }
#sales-body .funnel-step[data-sl-stage]:hover { background: var(--surface-hover); }
#sales-body .funnel-step[data-active="1"] { box-shadow: inset 0 0 0 2px var(--accent-line); }

/* Тонкий прогрес-бар у рядку таблиці менеджерів. */
.sl-pbar {
    display: block;
    height: 4px;
    min-width: 64px;
    margin-top: 6px;
    border-radius: var(--r-pill);
    background: var(--surface-hover);
}
.sl-pbar-fill {
    display: block;
    height: 100%;
    border-radius: var(--r-pill);
    background: var(--accent);
}
.sl-pbar-fill.is-ok { background: var(--ok); }
.sl-pbar-fill.is-warn { background: var(--warn); }
.sl-pbar-fill.is-bad { background: var(--err); }

/* Підлога кегля 13px: таблиці, підписи, легенди, бейджі розділу.
   Дельту в kpi-compare не чіпаємо - там свої 14px (див. нижче). */
#sales-body .rank th,
#sales-body .rank .rk,
#sales-body .rank .nm .cnt,
#sales-body .legend-item,
#sales-body .muted,
#sales-body .badge,
#sales-body .delta,
#sales-body .kpi-was,
#sales-body .sl-call-meta { font-size: 13px; }
#sales-body .kpi-compare .delta { font-size: var(--fs-3); }
#sales-body .sl-call-title { font-size: var(--fs-4); }

/* Телефон: спарклайн під цифрою, а не праворуч - у 360px поруч тісно.
   Компактні - в одну колонку: правило стоїть тут, а не тільки вище,
   бо пізніше .sl-top .sl-kpis на 3 колонки його перекриває. */
@media (max-width: 520px) {
    .sl-top .sl-kpis { grid-template-columns: minmax(0, 1fr); }
}
/* === INFRA.CSS === */
/* demo/assets/css/infra.css */
/* НАЗНАЧЕНИЕ: разделы Инфраструктура и Интеграции - карта объектов, камеры, СЕС, SVG-схема потоков */
/* СВЯЗИ: views.js (infrastructure, integrations), variables.css (только токены) */

#if-map,
#if-cams,
#if-ses,
#ig-sources,
#ig-dash {
    scroll-margin-top: 128px;
}

/* ============ КАРТА ОБЪЕКТОВ ============ */

.infra-map .map-pin-label {
    display: inline;
}

.infra-pin {
    cursor: default;
}

.infra-pin.is-type-gps .map-pin-dot {
    border-radius: 3px;
}

.infra-pin.is-type-cam .map-pin-dot {
    border-radius: 50% 50% 50% 0;
    transform: rotate(-45deg);
}

/* Гало СЕС - мовою системи: 4px + soft-токен, як у кілець статусів
   (.map-pin-dot в components.css). Дубль правила з кінця components.css:
   там вища специфічність, це - запасний шар. */
.infra-pin.is-type-ses .map-pin-dot {
    border-radius: 50%;
    box-shadow: 0 0 0 4px var(--warn-soft);
}

/* ============ КАМЕРЫ ============ */

.infra-tiles {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: var(--sp-2);
}

.infra-cam {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    padding: var(--sp-2) var(--sp-3);
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--r-2);
    font-size: var(--fs-3);
}

.infra-cam-dot {
    width: 8px;
    height: 8px;
    flex: 0 0 auto;
    border-radius: 50%;
    background: var(--muted);
}

.infra-cam.is-ok .infra-cam-dot {
    background: var(--ok);
    animation: infra-blink 2.4s ease-in-out infinite;
}

.infra-cam.is-warn .infra-cam-dot {
    background: var(--warn);
}

.infra-cam-name {
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.infra-cam-state {
    margin-left: auto;
    color: var(--muted);
    white-space: nowrap;
}

@keyframes infra-blink {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.35; }
}

/* ============ СЕС ============ */

.infra-ses {
    display: flex;
    flex-direction: column;
    gap: var(--sp-2);
}

.infra-ses-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--sp-3);
    padding: var(--sp-2) var(--sp-3);
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--r-2);
}

.infra-ses-name {
    font-weight: 600;
}

.infra-ses-val {
    font-family: var(--font-mono, monospace);
    font-weight: 600;
    white-space: nowrap;
}

.infra-ses-row .cnt {
    display: block;
    color: var(--muted);
    font-size: var(--fs-3);
    font-weight: 400;
}

/* ============ СХЕМА ИНТЕГРАЦИЙ ============ */

.ig-schema {
    overflow-x: auto;
}

.ig-svg {
    display: block;
    width: 100%;
    min-width: 640px;
    height: auto;
}

.ig-flow {
    fill: none;
    stroke: var(--blue);
    stroke-width: 2;
    stroke-dasharray: 7 6;
    opacity: 0.75;
    animation: ig-flow 1.1s linear infinite;
}

.ig-flow.is-out {
    stroke: var(--ok);
}

.ig-flow.is-ret {
    stroke: var(--warn);
    stroke-dasharray: 4 6;
    opacity: 0.6;
}

.ig-arr-head { fill: var(--dim); }

@keyframes ig-flow {
    to { stroke-dashoffset: -13; }
}

.ig-dot.is-ok { fill: var(--ok); }
.ig-dot.is-warn { fill: var(--warn); }

.ig-node.is-core rect {
    animation: ig-pulse 2.4s ease-in-out infinite;
}

@keyframes ig-pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.82; }
}

.ig-details > summary {
    cursor: pointer;
    font-size: var(--fs-3);
    font-weight: 600;
    color: var(--dim);
    margin-bottom: var(--sp-2);
}

.ig-details .panel { margin-top: 0; }

.ig-node rect {
    fill: var(--surface-2);
    stroke: var(--border-strong);
    stroke-width: 1.5;
}

.ig-node.is-core rect {
    fill: var(--blue-soft);
    stroke: var(--blue);
    stroke-width: 2;
}

.ig-node.is-dash rect {
    stroke: var(--ok);
}

.ig-name {
    fill: var(--text);
    font-size: 14px;
    font-weight: 700;
}

.ig-sub {
    fill: var(--muted);
    font-size: 11px;
}

@media (max-width: 900px) {
    .infra-tiles {
        grid-template-columns: 1fr 1fr;
    }
}

@media (max-width: 560px) {
    .infra-tiles {
        grid-template-columns: 1fr;
    }
}

/* ============ HERO-КАРТА 60/40 (хвиля C) ============ */

/* Ліва колонка 60%: карта-hero зліва, камери 40% справа; другий ряд
   (СЕС + індикатори) ділить ті ж осі. Специфічність вища за .grid-2
   з main.css, тому мобільне складання дублюємо своїм запитом нижче. */
#infrastructure-body .if-grid {
    grid-template-columns: minmax(0, 3fr) minmax(0, 2fr);
}

/* ============ ТЕМНИЙ ЯКІР (хвиля C) ============ */

/* Темна смуга під KPI - якір екрана: чотири статуси видно за 5 секунд.
   Темна в обох темах, як відеоекрани камер (.if-cam-screen). */
.if-anchor {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sp-2) var(--sp-5);
    margin-top: var(--gap-l);
    padding: var(--sp-3) var(--sp-4);
    background: linear-gradient(135deg, #1c242c, #10151a 70%);
    border: 1px solid #2b343e;
    border-radius: var(--r-2);
    color: #f2f5f7;
}

.if-anchor-item {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    font-size: var(--fs-3);
    font-weight: 600;
    white-space: nowrap;
}

.if-anchor-dim {
    color: rgba(255, 255, 255, 0.62);
    font-weight: 400;
}

.if-anchor-dot {
    width: 9px;
    height: 9px;
    flex: none;
    border-radius: 50%;
}

.if-anchor-dot.is-ok {
    background: #3fd08c;
    box-shadow: 0 0 0 3px rgba(63, 208, 140, 0.22);
}

.if-anchor-dot.is-warn {
    background: #f0b429;
    box-shadow: 0 0 0 3px rgba(240, 180, 41, 0.22);
}

.if-anchor-dot.is-alert {
    background: #ff5d5d;
    box-shadow: 0 0 0 3px rgba(255, 93, 93, 0.25);
    animation: if-blink 1.6s ease-in-out infinite;
}

/* ============ КОЛЬОРОВІ АВАРІЙНІ КАНАЛИ (хвиля C) ============ */

/* Іконки брендів - заливка currentColor, тому фарбуємо color.
   Кольори офіційні: Telegram #26A5E4, Viber #665CAC, Gmail #EA4335. */
#infrastructure-body .if-chan .ico {
    width: 17px;
    height: 17px;
}

#infrastructure-body .if-chan.is-telegram .ico { color: #26a5e4; }
#infrastructure-body .if-chan.is-viber .ico { color: #665cac; }
#infrastructure-body .if-chan.is-gmail .ico { color: #ea4335; }

/* ============ ПІДПИСИ МІНІМУМ 13px (хвиля C) ============ */

/* Легенди, підписи пінів, назви і стани камер, підписи схеми СЕС -
   не менше 13px. Жирність лишаємо як була: ієрархія тримається нею. */
#infrastructure-body .legend-item,
#infrastructure-body .map-pin-label,
#infrastructure-body .if-cam-name,
#infrastructure-body .if-cam-state,
#infrastructure-body .if-cam-live {
    font-size: 13px;
}

#infrastructure-body .if-cam-live {
    letter-spacing: 0.06em;
}

#infrastructure-body .ses-sub,
#infrastructure-body .ses-kw {
    font-size: 13px;
}

/* Планшет: у 40% колонки плитки камер стають ~90px і підписи ріжуться,
   тому сітку складаємо в одну колонку раніше за системні 780px. */
@media (max-width: 1100px) {
    #infrastructure-body .if-grid {
        grid-template-columns: minmax(0, 1fr);
    }
}

@media (max-width: 780px) {
    #infrastructure-body .if-grid {
        grid-template-columns: minmax(0, 1fr);
    }

    .if-anchor {
        gap: var(--sp-2) var(--sp-4);
    }
}

@media (prefers-reduced-motion: reduce) {
    .ig-flow,
    .ig-node.is-core rect,
    .infra-cam.is-ok .infra-cam-dot,
    .if-anchor-dot.is-alert {
        animation: none;
    }
}

/* ---- СЕС як панель застосунку (wave-E, еталон s8) ---- */
.ses-app {
    margin-top: var(--sp-3);
    padding: var(--sp-3) var(--sp-4);
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--r-2);
}
.ses-app-row {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: var(--sp-3);
    padding: 3px 0;
    font-size: var(--fs-3);
}
.ses-app-row b { font-variant-numeric: tabular-nums; }
.ses-app-bar {
    height: 6px;
    margin-top: var(--sp-2);
    border-radius: var(--r-pill);
    background: var(--surface-hover);
}
.ses-app-bar > span {
    display: block;
    height: 100%;
    border-radius: var(--r-pill);
    background: var(--warn);
}
/* === WAVE-D.CSS (W2-ACCENTS) === */
/* demo/assets/css/wave-d.css */
/* НАЗНАЧЕНИЕ: спокойные сине-желтые акценты главных блоков - против
   "единого серого полотна" (главный дефект по Дмитрию, транскрипт 25.09
   строки 34-42, 354-357; followup "Акценты цветом"). Только существующие
   токены blue-soft и yellow-soft из variables.css, новых оттенков нет.
   ТОЛЬКО overrides поверх: существующие id/классы, :has() и структурные
   селекторы. Разметка, меню, app.js, safe.php не тронуты.
   Зоны Tolya (menu, overview, marketing, operations) - строго через #id.
   Gesso-гарды: без градиентов (gradient-*, multicolor-*), без неона
   (dark-glow), без толстых цветных кромок (edge-stripe: только полная
   рамка 1px + тихая заливка, как у штатного .mk-fcard.is-active),
   без перекраски заголовков (multicolor-heading), без теней
   (heavy-box-shadow), без transition (transition-all). Синий oklch hue 250 -
   вне violet-полосы 252-296 (purple-violet-wash) и не из hex-списка
   indigo-accent. Текст не перекрашивается - контраст не меняется.
   02.10 правка №4: заливки панелей сняты везде (белые карты); в файле
   живы только активы меню :23-30. */
/* ПОДКЛЮЧЕНИЕ (одна строка, ставит оркестратор после infra.css):
   <link rel="stylesheet" href="assets/css/wave-d.css?v=<?php echo $v; ?>"> */

/* ---------- Меню: активный раздел (#tabs - зона Tolya) ---------- */
/* Подчеркивание уже синее; добавляем тихую заливку, чтобы активный
   раздел держал взгляд и работал заголовком экрана (h2 скрыт визуально). */
#tabs .tab.active {
    background: var(--blue-soft);
    border-radius: var(--r-2) var(--r-2) 0 0;
}
#subnav .subnav-link.active {
    background: var(--blue-soft);
    border-radius: var(--r-2);
}

/* ---------- Экран 1. План-факт - СНЯТО 02.10 правка №4 ----------
   Селекторы мертвы: .panel ушел в .card, .debts нет в разметке. */
/* ---------- Цели - СНЯТО 02.10 правка №4 ----------
   Селектор мертв: .pf-goal-pct нет в разметке. */

/* ---------- Экран 3. Маркетинг - СНЯТО 28.09 wave-E ----------
   Панели Strimix белые по эталону, наша заливка противоречила референсу.
   Зона Tolya: перестройка - его пачкой K2. */

/* ---------- Экран 4. Продажи (#sales-body) ---------- */
/* Главные ТЗ: 4 бокса 2х2 (без заливки - форма держит).
   Заливку дзвінків знято: білі панелі за референсом. */

/* ---------- Экран 5. Финансы (#finance-body) ---------- */
/* Пара подвала ТЗ: кредиторы слева (первая панель грида с #fin-debtors) -
   синим, дебиторы справа - желтым. */
/* Панели кредиторки/дебіторки - білі за референсом Finmap (заливки знято). */

/* ---------- Экран 6. Бухгалтерия - СНЯТО 28.09 wave-E ----------
   Экран оставляем как есть (референс Finmap самодостаточен). */

/* ---------- Экран 7. Производство - СНЯТО 28.09 wave-E ----------
   Зона Tolya: позиционный селектор поплыл бы при его перестройке K5. */

/* ---------- Экран 8. Инфраструктура - БЕЛЫЕ 02.10 правка №4 ----------
   Заливки панелей сняты (AUDIT_02_10 "Цвет"): белые карты как соседи.
   Цвет несут контент - пины и легенда карты, бары СЕС. */

/* ---------- Экран 9. Интеграции - СНЯТО 02.10 правка №4 ----------
   Схема: селектор мертв (.panel ушел в .card). Дашборди: желтая заливка
   снята заодно с инфрой - то же декоративное пятно (AUDIT_02_10 "Цвет"). */

/* ---------- Экран 2. Джин - СНЯТО 02.10 правка №4 ----------
   Правило мертво: перебито sd-views (#brain-body .msg-a позже в бандле).
   Ответы сейчас sunken-серые - зона Джина, не трогать. */
/* === B-COLOR.CSS === */
/* _variants/B/assets/css/b-color.css */
/* НАЗНАЧЕНИЕ: цвет как смысл и иерархия (волна "дизайн на максимум", 29.09,
   методика Impeccable colorize, режим Operate + Read: цвет кодирует факт,
   состояние и навигацию, а не украшает).
   СВЯЗИ: variables.css (базовые токены), подключается после b-w3.css.
   РАЗМЕР: ~190 строк

   ПРОБЛЕМА (созвон 25.09, Дмитрий): "все сіре, не розумієш, де акценти,
   який блок основний", "виглядає як єдине полотно".

   СТРАТЕГИЯ. Температура: спокойная, деловая. Доминанта: синий = факт
   и "то, куда смотреть". Серый = план и фон. Семафор = только отклонение
   от нормы, и только там, где оно есть. Доза: одна синяя заливка на экран
   (ведущая карточка полосы плана), остальное - тонкие акценты.

   РОЛИ
   | роль               | токен              | где                                     |
   |--------------------|--------------------|-----------------------------------------|
   | факт, главное      | --role-fact        | заливка ведущей карточки, серия "Факт"  |
   | план, база         | --role-plan        | серия "План", полосы плана              |
   | фон страницы       | --bg               | холодный светло-серый                   |
   | главная панель     | --role-panel-main  | белая, тень, синяя кромка сверху        |
   | справочная панель  | --role-panel-ref   | тише фона на ступень, без тени          |
   | заголовок группы   | --role-group       | синяя метка слева от h3.ov-group        |
   | итог в таблице     | --role-total       | тонкая заливка строки итога             |
   | текущий период     | --role-current     | колонка "вересень" в отчетах            |
   | хорошо/внимание/плохо | --ok/--warn/--err | только статус и отклонение             |

   ГЛАВНЫЙ БЛОК ПО РАЗДЕЛАМ (на чем глаз должен остановиться первым)
   | раздел        | главный блок                         | как выделен                       |
   |---------------|--------------------------------------|-----------------------------------|
   | План-факт     | ведущая карточка "Виручка"           | синяя заливка (уже), кромки групп |
   | Цілі          | ведущая цель полосы                  | синяя заливка                     |
   | Маркетинг     | KPI .is-main                         | синяя заливка (b-w1)              |
   | Продажі       | ведущая карточка полосы              | синяя заливка                     |
   | Фінанси       | P&L, колонка текущего месяца, итоги  | кромка, --role-current, --role-total |
   | Бухгалтерія   | полоса + проблемные документы        | кромка                            |
   | Виробництво   | график план-факт + брак              | кромка                            |
   | Інфраструктура| "Потребує уваги"                     | кромка + статус                   |
   | Інтеграції    | полоса уровней                       | синяя заливка                     |
   | Джин          | ответ ассистента                     | кромка сообщения                  |
*/

:root {
    /* Фон на ступень холоднее и темнее: белые панели отделяются от него
       сами, без жирных рамок. Было L 0.964 - панели сливались с фоном
       на проекторе ("єдине полотно"). */
    --bg:            oklch(0.952 0.008 255);
    --surface-2:     oklch(0.975 0.005 255);
    --border:        oklch(0.905 0.007 258);
    --border-strong: oklch(0.83 0.012 258);

    --role-fact:        var(--blue);
    --role-plan:        oklch(0.66 0.03 258);   /* 3:1 к белой панели - порог графики */
    --c-plan:           var(--role-plan);
    --role-panel-main:  var(--surface);
    --role-panel-ref:   var(--surface-2);
    --role-group:       var(--blue);
    --role-total:       oklch(0.52 0.18 250 / 0.045);
    --role-current:     oklch(0.52 0.18 250 / 0.075);
    --role-edge:        oklch(0.52 0.18 250 / 0.85);

    /* Панель первого плана: мягкая двухслойная тень вместо одной линии -
       карточка "поднимается" над холодным фоном. */
    --shadow:   0 1px 2px oklch(0.30 0.04 255 / 0.06), 0 2px 8px oklch(0.30 0.04 255 / 0.05);
}

html.dark-theme {
    --role-plan:        oklch(0.52 0.025 262);
    --c-plan:           var(--role-plan);
    --role-total:       oklch(0.72 0.15 252 / 0.07);
    --role-current:     oklch(0.72 0.15 252 / 0.11);
    --role-edge:        oklch(0.72 0.15 252 / 0.9);
    --shadow:   0 1px 2px oklch(0 0 0 / 0.35), 0 2px 8px oklch(0 0 0 / 0.25);
}

/* ---------- 1. Панели: два плана ---------- */
.panel {
    background: var(--role-panel-main);
    box-shadow: var(--shadow);
}
/* Справочные панели (индикаторы, свернутое) - второй план: лежат в фоне */
.panel.section-break,
details .panel {
    background: var(--role-panel-ref);
    box-shadow: none;
}

/* ---------- 2. Заголовок группы - синяя метка слева ----------
   Группы ("План на вересень", "Показники вересня", "Прибутки і збитки")
   делят страницу на смысловые куски. Короткая синяя черта перед
   заголовком дает глазу опору при скролле, не заливая блок цветом. */
.ov-group {
    position: relative;
    padding-left: 12px;
}
.ov-group::before {
    content: '';
    position: absolute;
    left: 0;
    top: 0.22em;
    bottom: 0.22em;
    width: 3px;
    border-radius: 2px;
    background: var(--role-group);
}

/* ---------- 3. Главная панель раздела: синяя кромка сверху ----------
   Первая панель после заголовка группы - то, ради чего открыли раздел.
   Кромка 2px внутри скругления: видна с проектора, не кричит. */
.ov-group + .panel,
.ov-group + .grid > .panel:first-child,
.ov-group + .grid-2-1 > .panel:first-child,
#if-issues,
#view-operations .w3-main-chart {
    box-shadow: inset 0 2px 0 var(--role-edge), var(--shadow);
}

/* ---------- 4. Отчеты: текущий период и итоги ---------- */
#view-finance .rank .w2-cur,
.rank td.w2-cur,
.rank th.w2-cur { background: var(--role-current); }
.rank tr.w2-tot td,
.rank tr.is-total td,
.rank tfoot td {
    background: var(--role-total);
    color: var(--text);
}
.rank tr.w2-tot td.w2-cur { background: color-mix(in oklab, var(--role-current) 100%, var(--role-total)); }
.rank tr.w2-tot td.stick { background: color-mix(in oklab, var(--surface) 94%, var(--blue)); }
/* Строки таблиц при наведении - тот же синий, едва заметно */
.rank tbody tr:hover td { background: oklch(0.52 0.18 250 / 0.035); }
html.dark-theme .rank tbody tr:hover td { background: oklch(0.72 0.15 252 / 0.06); }

/* ---------- 5. Полоса плана: не-ведущие карточки ----------
   Ведущая залита синим (planfact.css). Остальные - белые с цветной
   полосой прогресса; их статус читается по полосе и слову уровня.
   Полоса "фон" = план (серый), заливка = факт в цвете уровня. */
.ov-goal:not(.is-lead) .pf-bar { background: color-mix(in oklab, var(--role-plan) 28%, var(--surface-2)); }

/* ---------- 6. Индикаторы: статус полосой слева, а не только точкой ----------
   Точка 8px на проекторе теряется. Полоса 3px слева у строки индикатора
   держит тот же цвет уровня и читается издалека. */
.ind {
    border-left: 3px solid transparent;
    padding-left: calc(var(--gap-s) + 2px);
}
.ind.is-alert { border-left-color: var(--err-line); }
.ind.is-warn  { border-left-color: var(--warn-line); }
.ind.is-ok    { border-left-color: var(--ok-line); }

/* ---------- 7. Бейджи статуса: заливка мягче, текст контрастнее ---------- */
.badge.ok    { background: var(--ok-soft); color: var(--ok); }
.badge.warn  { background: var(--warn-soft); color: var(--warn-text); }
.badge.alert { background: var(--err-soft); color: var(--err-text); }
/* === B-TYPE.CSS === */
/* _variants/B/assets/css/b-type.css */
/* НАЗНАЧЕНИЕ: единый шаблон текста всего кабинета (волна "дизайн на максимум", 29.09).
   Одна гарнитура IBM Plex Sans + Plex Mono для служебных меток. Каждая роль текста
   описана один раз и выглядит одинаково во всех 10 разделах.
   СВЯЗИ: variables.css (--font-*, --fs-*), подключается после b-w3.css и b-color.css.
   РАЗМЕР: ~200 строк

   ЗАМЕР ДО (Playwright, 10 разделов, 1600px): 40 разных сочетаний
   размер/вес/шрифт, 3 семейства вперемешку, веса 400/500/600/700/800/900.

   ШКАЛА РОЛЕЙ (веса только 400 / 500 / 600; 700 - только крупные цифры)
   | роль                         | размер  | вес | высота | трекинг | где                           |
   |------------------------------|---------|-----|--------|---------|-------------------------------|
   | цифра-герой (KPI, план)      | fs-7    | 600 | 1.1    | -0.02em | .kpi-value, .ov-goal.is-lead  |
   | цифра второго плана          | fs-8    | 600 | 1.1    | -0.015em| .ov-goal-fact, .ov-stat b     |
   | заголовок группы на странице | 20px    | 600 | 1.25   | -0.01em | h3.ov-group                   |
   | заголовок панели             | 16px    | 600 | 1.3    | -0.005em| .panel-title                  |
   | подпись к заголовку панели   | 13px    | 400 | 1.35   | 0       | .panel-sub, .page-sub         |
   | основной текст               | 14px    | 400 | 1.5    | 0       | body, td, .ind-text, чат      |
   | название строки / метрики    | 14px    | 500 | 1.35   | 0       | .ov-goal-name, .ind-name, .nm |
   | цифра в строке / таблице     | 14px    | 500 | 1.35   | 0, tnum | .rank .r, .ind-cur            |
   | метка (капсом)               | 11.5px  | 600 | 1.2    | 0.06em  | .kpi-label, th, .pf-name      |
   | подпись, легенда, дельта     | 12.5px  | 400 | 1.35   | 0       | .kpi-sub, .ov-goal-plan       |
   | служебное (нормы, коды)      | 12.5px  | 400 | 1.35   | 0, mono | .ind-norm, .mono              |
   | бейдж статуса                | 12px    | 500 | 1      | 0.01em  | .badge                        |
*/

/* ---------- 1. Токены шкалы ----------
   Сдержанная шкала для плотного BI-экрана: шаг ~1.15 в тексте, крупнее
   только цифры. Текучие цифры не растут выше 34px: на проекторе 36px Plex
   уже кричит, а 26px на ноутбуке 1280 держит вес. */
:root {
    --fs-1: 12.5px;
    --fs-2: 13px;
    --fs-3: 14px;
    --fs-4: 15px;
    --fs-5: 16px;
    --fs-6: 20px;
    --fs-7: clamp(26px, 1.3vw + 12px, 34px);
    --fs-8: clamp(19px, 0.55vw + 13px, 23px);
    --fs-label: 11.5px;

    --fw-regular: 400;
    --fw-medium: 500;
    --fw-semi: 600;
    --fw-num: 600;

    --lh-display: 1.1;
    --lh-tight: 1.2;
    --lh-snug: 1.35;
    --lh-body: 1.5;
    --lh-read: 1.55;

    --ls-label: 0.06em;
    --ls-display: -0.02em;
}

/* ---------- 2. База ---------- */
body {
    font-family: var(--font-body);
    font-size: var(--fs-3);
    font-weight: var(--fw-regular);
    line-height: var(--lh-body);
    font-optical-sizing: auto;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    text-rendering: optimizeLegibility;
}

/* Цифры везде табличные: в финансовом экране соседние суммы должны
   вставать разряд под разряд, а не "прыгать" по ширине цифр. */
body, td, th, .kpi-value, .ov-goal-fact, .ov-goal-pct, .ov-stat b, .ind-cur,
.chip-val, .delta, .pf-num, b, strong {
    font-variant-numeric: tabular-nums;
}

/* Жирность в тексте (b/strong внутри абзацев, подписей, чата) - 600, не 700-900:
   у Plex 600 уже читается акцентом, 800-900 превращали цифру в кляксу. */
b, strong { font-weight: var(--fw-semi); }

/* ---------- 3. Цифры ---------- */
.kpi-value,
.pf-kpi .pf-fact {
    font-family: var(--font-display);
    font-size: var(--fs-7);
    font-weight: var(--fw-num);
    line-height: var(--lh-display);
    letter-spacing: var(--ls-display);
}
.ov-goal-fact,
.ov-stat b,
.ov-lead b,
.ov-cl .ov-stat b {
    font-family: var(--font-display);
    font-size: var(--fs-8);
    font-weight: var(--fw-num);
    line-height: var(--lh-display);
    letter-spacing: -0.015em;
}
/* Ведущая карточка полосы плана - цифра-герой */
.ov-goal.is-lead .ov-goal-fact { font-size: var(--fs-7); }
.ov-goal-pct,
.ov-mix-center b,
.chip-val {
    font-family: var(--font-display);
    font-size: var(--fs-4);
    font-weight: var(--fw-semi);
    letter-spacing: 0;
}
/* Единица рядом с цифрой (грн, од, %) - тихая, тем же шрифтом */
.ov-goal-fact small,
.kpi-cur {
    font-family: var(--font-body);
    font-size: var(--fs-2);
    font-weight: var(--fw-regular);
    letter-spacing: 0;
}

/* ---------- 4. Заголовки ---------- */
.ov-group {
    font-family: var(--font-display);
    font-size: var(--fs-6);
    font-weight: var(--fw-semi);
    line-height: 1.25;
    letter-spacing: -0.01em;
}
.panel-title,
#view-goals .gl-periods .panel-title {
    font-family: var(--font-display);
    font-size: var(--fs-5);
    font-weight: var(--fw-semi);
    line-height: 1.3;
    letter-spacing: -0.005em;
}
.panel-sub,
.page-sub {
    font-family: var(--font-body);
    font-size: var(--fs-2);
    font-weight: var(--fw-regular);
    line-height: var(--lh-snug);
    letter-spacing: 0;
}
/* Подзаголовок страницы - первая строка раздела, чуть плотнее подписи панели */
.page-sub { font-weight: var(--fw-medium); }

/* ---------- 5. Названия строк и метрик ---------- */
.ov-goal-name,
.ind-name,
.rank .nm,
.rank td.nm,
.funnel-label {
    font-family: var(--font-body);
    font-size: var(--fs-3);
    font-weight: var(--fw-medium);
    line-height: var(--lh-snug);
    letter-spacing: 0;
}
.ind-cur,
.rank .r,
.rank .val,
.rank td.val {
    font-size: var(--fs-3);
    font-weight: var(--fw-medium);
    line-height: var(--lh-snug);
}
/* Цифры в таблицах и индикаторах - Plex Sans с табличными цифрами, не Mono:
   моно делал колонку сумм похожей на лог сервера. Mono оставлен служебному. */
.rank .r,
.rank .val,
.ind-cur,
.funnel-conv,
.chart-val {
    font-family: var(--font-body);
    /* Сжатие пробела -0.2em было для моно (широкий пробел Plex Mono).
       У Plex Sans пробел узкий - с ним "6 147 400" слипался в "6147400". */
    word-spacing: normal;
}

/* Все места, где под моно стояло сжатие пробела, теперь на Plex Sans */
.chip-val, .ind-num, .rank .val, .rank .r, .funnel-fill { word-spacing: normal; }
.chip-val, .ind-num, .funnel-fill { font-family: var(--font-body); }

/* ---------- 6. Основной текст ---------- */
.ind-text,
.msg-text,
.rank td,
.w3-side-note,
p {
    font-size: var(--fs-3);
    line-height: var(--lh-body);
}
.ind-text,
.msg-text { line-height: var(--lh-read); }

/* ---------- 7. Метки капсом ---------- */
.kpi-label,
.rank th,
.pf-kpi .pf-name {
    font-family: var(--font-body);
    font-size: var(--fs-label);
    font-weight: var(--fw-semi);
    line-height: var(--lh-tight);
    letter-spacing: var(--ls-label);
    text-transform: uppercase;
}

/* ---------- 8. Подписи, дельты, служебное ---------- */
.kpi-sub,
#view-marketing .kpi-sub,
.ov-goal-plan,
.w2-goal-sub,
.delta,
.ov-goal-lvl,
.cnt,
small {
    font-size: var(--fs-1);
    line-height: var(--lh-snug);
    letter-spacing: 0;
}
.delta { font-weight: var(--fw-medium); }
.ov-goal-lvl { font-weight: var(--fw-semi); }
.ind-norm,
.mono,
code,
.msg-src,
.gl-hint {
    font-family: var(--font-mono);
    font-size: var(--fs-1);
    font-weight: var(--fw-regular);
    letter-spacing: 0;
}

/* ---------- 9. Бейджи и кнопки ---------- */
.badge,
.rank .badge {
    font-size: 12px;
    font-weight: var(--fw-medium);
    line-height: 1;
    letter-spacing: 0.01em;
}
.btn,
button.suggest,
.mk-pill,
.period-switch button {
    font-family: var(--font-body);
    font-size: var(--fs-2);
    font-weight: var(--fw-medium);
    letter-spacing: 0;
}

/* ---------- 9б. Точечные перебивки ----------
   Правила разделов (b-w1/b-w2/counterparty, специфичность 0,2,1 и выше)
   держали свои 700-800 и 13px. Приводим их к ролям шаблона выше. */
.rank th.r,
.rank th.stick,
.rank th.w2-cur {
    font-family: var(--font-body);
    font-size: var(--fs-label);
    font-weight: var(--fw-semi);
    letter-spacing: var(--ls-label);
}
.delta,
.delta.up,
.delta.down,
.rank .delta,
.kpi-compare .delta { font-size: var(--fs-1); font-weight: var(--fw-medium); }
.page-sub { font-size: var(--fs-3); }
/* h2.page-title визуально скрыт (main.css:601, роль у активной вкладки), но
   держит роль заголовка страницы для скринридера - вес как у остальных. */
.page-title { font-weight: var(--fw-semi); }
.panel-title.is-label { font-size: var(--fs-5); font-weight: var(--fw-semi); }
.rank .badge.alert,
.src-level .badge,
.badge.alert { font-size: 12px; font-weight: var(--fw-medium); }
.rank td.r b,
.rank td.nm b,
.bal-row b,
#view-finance .bal-row b,
.pf-goal-pct .pf-num,
#view-goals .gl-gap b,
#view-brain .msg-a .msg-text b.num,
#view-marketing .rank td.mk-low,
li b, p b, li b.num, p b.num { font-weight: var(--fw-semi); }
#view-sales .w2-mix-row b,
#view-marketing .mk-dn-center b { font-size: var(--fs-4); font-weight: var(--fw-semi); }
.bal-cap { font-size: var(--fs-label); font-weight: var(--fw-semi); letter-spacing: var(--ls-label); }
#view-brain .br-label { font-size: var(--fs-4); font-weight: var(--fw-semi); }
/* Карточка контрагента (открывается кликом по строке долгов): имя - заголовок панели */
/* counterparty.css views.js:2305 догружает второй раз уже после b-type.css - нужна специфичность выше */
body .cp-name { font-size: var(--fs-6); font-weight: var(--fw-semi); }
@media (max-width: 560px) { body .cp-name { font-size: var(--fs-5); } }

/* ---------- 10. Отступы и поля ----------
   Плотность BI: панель 16px по вертикали и горизонтали (было 16/18 -
   несимметричный отступ читается как ошибка), строки таблиц 40px держим
   (палец и проектор), шапка таблицы ниже строки - это метка, не данные. */
.panel { padding: var(--sp-4); }
.rank th { height: 34px; padding-top: 6px; padding-bottom: 6px; }

/* ---------- 11. Мобильная 390 ---------- */
@media (max-width: 520px) {
    :root {
        --fs-6: 18px;
        --fs-7: 26px;
        --fs-8: 19px;
    }
    .panel { padding: var(--sp-3); }
}
/* === B-BRAND.CSS === */
/* _variants/B/assets/css/b-brand.css */
/* НАЗНАЧЕНИЕ: знак и словесный знак (волна "дизайн на максимум", 29.09,
   зона L-brand). Знак - концепция 1 "Дві півкулі" из 3, аудит
   scripts/svg_audit.py (скилл logo-design): 100/100, 2 цвета, контраст
   AA. Круг с вертикальной щелью: левая половина сплошная (интуиция
   и опыт собственника), правая - строчная B (другий мозок на даних).
   СВЯЗИ: variables.css (--blue, --text), index.php .nav-brand (разметка
   .nav-mark), favicon.svg (тот же знак, срез для малых размеров).
   РАЗМЕР: ~45 строк */

.nav-mark {
    width: 22px;
    height: 22px;
    flex: 0 0 auto;
}
/* Темная половина знака - основной текстовый цвет (не хардкод #222f3c):
   на светлой теме темно-синий, на темной теме сам светлеет вместе
   с --text, знак остается читаемым на обеих темах. */
.nav-mark-ink    { fill: var(--text); }
.nav-mark-accent { fill: var(--blue); }

/* Словесный знак: было 800 (крикливее любой цифры на экране, отмечено
   evaluator при проверке типографики). BUSINESS BRAIN - служебная
   подпись рядом со знаком, не заголовок: снижаем до 700 (не через
   --fw-semi=600 из b-type - это отдельная, чуть более тяжелая ступень
   именно для словесного знака). Капс и трекинг держат вес не хуже
   прежнего 800. */
.nav-brand {
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.02em;
}

/* Телефон: имя обрезалось до "BUSINESS BRA..." (main.css:440, 42vw
   на всю строку с эмблемой демо). После 700 вместо 800 слово короче,
   но щель для мелких экранов оставляем шире - знак+текст+плашка "демо"
   должны влезать без обрезки на 390px. */
@media (max-width: 560px) {
    .nav-mark { width: 20px; height: 20px; }
    .nav-brand-name { max-width: 50vw; }
}
/* === BRAND.CSS === */
/* demo/assets/css/brand.css */
/* НАЗНАЧЕНИЕ: OKLCH-палитра демо поверх Tabler + каркас шелла W1 (сайдбар,
   топбар, подменю). Подключается последним, все свои классы с префиксом tb1-.
   Старые классы (.panel, .grid, .stack, .tabs) в новой разметке не используются;
   старые правила .tabs/.subnav мертвы (классы сняты с контейнеров в index.php),
   а кнопки .tab/.subnav-link рисует app.js - их вид доводится здесь. */
/* СВЯЗИ: variables.css (токены --blue/--text/..., обе темы), index.php (шелл),
   Tabler 1.6.1 (переменные --tblr-*), контракт /tmp/tabler-build-CONTRACT.md */
/* РАЗМЕР: ~250 строк */

/* ============ 1. МОСТ ПАЛИТРЫ: наши OKLCH-токены -> переменные Tabler ============
   Tabler ест любой CSS-цвет в обычных vars, но полупрозрачные композиции
   строит через -rgb-тройки (rgba(var(--tblr-primary-rgb), ...)) - туда OKLCH
   не встанет, поэтому тройки посчитаны из тех же OKLCH скриптом /tmp/oklch2srgb.py:
   blue light oklch(0.52 0.18 250) -> 0,105,202; blue dark oklch(0.72 0.15 252) ->
   87,168,255; ok/warn/err/yellow аналогично из variables.css. Темная тема
   продублирована под оба селектора: html.dark-theme (наш тогл, работает уже
   сейчас) и html[data-bs-theme="dark"] (нативный Tabler, заработает когда
   сборщик добавит однострочную синхронизацию data-bs-theme в theme-init.js
   и app.js initTheme - см. отчет /tmp/tabler-W1.md). */

:root {
    color-scheme: light;
    --tblr-font-sans-serif: var(--font-body);
    --tblr-font-monospace: var(--font-mono);
    --tblr-body-bg: var(--bg);
    --tblr-body-color: var(--text);
    --tblr-bg-surface: var(--surface);
    --tblr-border-color: var(--border);
    --tblr-secondary-color: var(--muted);
    --tblr-link-color: var(--blue);
    --tblr-link-hover-color: var(--blue);
    --tblr-primary: var(--blue);
    --tblr-primary-rgb: 0, 105, 202;
    --tblr-success: var(--ok);
    --tblr-success-rgb: 27, 126, 42;
    --tblr-warning: var(--warn);
    --tblr-warning-rgb: 255, 213, 0;
    --tblr-danger: var(--err);
    --tblr-danger-rgb: 249, 115, 22;
    --tblr-card-bg: var(--surface);
    --tblr-card-color: var(--text);
    --tblr-card-border-color: var(--border);
    --tblr-border-radius: var(--r-2);
    --tblr-border-radius-sm: var(--radius-sm);
    --tblr-border-radius-lg: var(--r-3);
}

html.dark-theme,
html[data-bs-theme="dark"] {
    color-scheme: dark;
    --tblr-primary-rgb: 87, 168, 255;
    --tblr-success-rgb: 97, 197, 104;
    --tblr-warning-rgb: 255, 213, 0;
    --tblr-danger-rgb: 255, 138, 61;
}

/* ============ 2. КОРЕНЬ TABLER: снять старые контент-правила ============
   Класс .page нужен ОБОИМ: Tabler - как flex-корень раскладки, а App.fail() -
   как контейнер для ошибки загрузки (querySelector('.page')). Поэтому main
   оставляет class="page", а с внешнего корня старые контент-правила
   (main.css .page: padding/max-width/margin auto; nav.css .page: margin-left
   под старый сайдбар) снимаются здесь более специфичным селектором. */

body > .page {
    max-width: none;
    margin: 0;
    padding: 0;
}

.tb1-wrap {
    background: var(--bg);
}

.tb1-body {
    background: var(--bg);
}

/* ============ 3. САЙДБАР ============ */

.tb1-side {
    background: var(--surface);
    border-right: 1px solid var(--border);
}

/* Внутренние поля колонки сайдбара: бренд и табы стоят на одной оси 12px,
   активный таб не клеится к краям. */
.tb1-side .container-fluid {
    padding-left: 12px;
    padding-right: 12px;
}

/* Бренд в одну строку: знак + имя + плашка "демо" чипом справа (по слову
   Романа 30.09 плашка второй строкой между лого и меню - ошибка). Имя при
   нехватке места режется точками, плашка всегда видна. */
.tb1-side .tb1-brand {
    flex: 1 1 auto;
    min-width: 0;
    flex-wrap: nowrap;
    align-items: center;
    margin-right: 0;
    padding-top: 0;
    padding-bottom: 6px;
    font-size: var(--fs-3);
}

.tb1-side .tb1-brand .nav-brand-name {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.tb1-side .tb1-brand .nav-demo {
    flex: 0 0 auto;
    align-self: center;
    font-size: 10px;
    line-height: 1.5;
    padding: 0 5px;
    letter-spacing: 0.04em;
}

/* Контейнер разделов: вертикальный столбец вместо старого таб-бара.
   Кнопки внутрь рисует app.js buildNav (button.tab + .tab-badge). */
#tabs.tb1-tabs {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 2px;
    width: 100%;
    /* Tabler растягивает .navbar-collapse на всю ширину колонки, поэтому
       свои 12px полей табам даем здесь - иначе заливка активного таба
       клеится к краям сайдбара. */
    padding: 4px 12px;
}

/* Вид кнопки раздела. Специфичность с id перебивает базовый .tab из main.css
   и сайдбарный из nav.css (включая его мобильный блок @media 900px) - таб
   выглядит одинаково во всех вьюпортах, в свернутом на телефоне меню тоже. */
#tabs .tab {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 9px 12px;
    background: none;
    border: 0;
    border-radius: var(--r-2);
    color: var(--dim);
    font-size: var(--fs-4);
    font-weight: 500;
    text-align: left;
    white-space: nowrap;
    cursor: pointer;
    transition: background var(--transition), color var(--transition);
}

#tabs .tab::after {
    content: none;
}

#tabs .tab:hover {
    background: var(--surface-hover);
    color: var(--text);
}

#tabs .tab.active {
    background: var(--blue);
    color: var(--on-blue);
    font-weight: 600;
}

#tabs .tab:focus-visible {
    outline: 2px solid var(--blue);
    outline-offset: -2px;
}

#tabs .tab .ico {
    width: 18px;
    height: 18px;
    flex: 0 0 auto;
}

#tabs .tab-badge {
    margin-left: auto;
}

/* ============ 4. ТОПБАР (удален 30.09) ============
   Был: липкая строка 56px с #loc-current ("Уся група") и #mode-btn.
   Снесен по слову Романа; кнопка темы переехала в низ сайдбара
   (.tb1-side-foot), метка локации удалена. Липкое подменю .tb1-subbar
   теперь клеится к top: 0 (было 56px). */

/* ============ 5. ПОДМЕНЮ ============
   01.10.2026: мертво - motion.js syncSubnav всегда прячет #subnav и .tb1-subbar
   (якоря mk-/sl- не существуют в S4/S5-вьюхах). Правила ниже оставлены как
   страховка: если подменю вернут, вид кнопок живет в nav.css. */

.tb1-subbar {
    position: sticky;
    top: 0;
    z-index: 30;
    background: color-mix(in oklab, var(--bg) 82%, transparent);
    -webkit-backdrop-filter: blur(18px) saturate(180%);
    backdrop-filter: blur(18px) saturate(180%);
    box-shadow: inset 0 -1px 0 color-mix(in oklab, var(--border) 70%, transparent);
}

#subnav.tb1-subnav {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: var(--sp-2) var(--shell-pad);
    overflow-x: auto;
    scrollbar-width: none;
}

#subnav.tb1-subnav::-webkit-scrollbar {
    display: none;
}

#subnav.tb1-subnav:empty {
    padding-top: 0;
    padding-bottom: 0;
}

/* ============ 6. КОНТЕНТ ============
   main.page.tb1-main: центрирование и отступы дает старый .page из main.css,
   здесь только страховка от горизонтального вылезания на телефоне. */

main.tb1-main {
    min-width: 0;
}

/* ============ 7. СОВМЕСТИМОСТЬ СО СТАРОЙ РАЗМЕТКОЙ ДО СПЛАЙСА ============
   Bootstrap 5.3+ (внутри Tabler) завел свой .grid с grid-template-rows/
   columns на 12 колонок - у старого .grid из main.css этих свойств нет,
   и они протекают: первый ряд тянется. Обнуляем только строки; колонки
   задают старые .grid-2/.grid-3/... (они позже Tabler и побеждают).
   Новая разметка W2-W5 класс .grid не использует (запрет контракта),
   поэтому правило бьет только в старый views.js до сплайса. */

.tb1-main .grid {
    grid-template-rows: none;
}

/* App.card() собирает .card/.card-head/.card-title/.card-note, у которых НЕТ
   своих стилей в demo/assets/css (проверено grep 29.09). Раньше это был голый
   div, теперь Bootstrap дает ему рамку карточки - добавляем только шапку,
   чтобы карточка ошибки App.fail() читалась. Новую разметку не затрагивает:
   W2-W5 используют .card Tabler напрямую с .card-body. */

.tb1-main .card-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--sp-3);
    margin-bottom: var(--sp-2);
}

.tb1-main .card-title {
    font-size: var(--fs-5);
    font-weight: 600;
}

.tb1-main .card-note {
    color: var(--muted);
    font-size: var(--fs-2);
}

/* ============ 8. ТЕЛЕФОН ============ */

@media (max-width: 991.98px) {
    /* Свернутый сайдбар Tabler раскрывается поверх контента - бренд и тоглер
       уже в шапке aside, здесь только подтяжка отступов подменю. */
    #subnav.tb1-subnav {
        padding-left: var(--sp-3);
        padding-right: var(--sp-3);
    }
}

@media (max-width: 560px) {
    /* C1-1: Tabler ставит body{height:100%}, а main.css:431 в этом брейкпоинте
       дает html,body{overflow-x:hidden} - computed overflow-y у body становится
       auto, body превращается в скролл-контейнер вместо документа: window-scroll
       (float-кнопки, go() наверх) бьет вхолостую. height:auto возвращает скролл
       документу, min-height:100% от Tabler не трогаем. */
    html, body {
        height: auto;
    }
}

/* Без движения - без анимации появления разделов (старый .view.active
   анимируется в main.css, сюда дублировать не стали - см. отчет). */

@media (prefers-reduced-motion: reduce) {
    #tabs .tab {
        transition: none;
    }
}

/* ============ 9. FINISH: единый шрифт и плотность (30.09, по замечаниям Романа) ============
   Шрифт: везде один Plex (тело, сайдбар, топбар, контент); графики берут Plex
   из charts.js. Плотность: тихо и плотно, без таблеровского воздуха. */

.tb1-side, .tb1-subbar, .tb1-main {
    font-family: var(--font-body);
}

/* Сайдбар: убрать воздух - бренд ближе к меню, пункты плотнее. */
.tb1-side .container-fluid {
    padding: 6px 10px 10px;
}
.tb1-side .tb1-brand {
    padding-top: 0;
    padding-bottom: 4px;
}
#tabs.tb1-tabs {
    padding: 0 4px 2px;
}
#tabs .tab {
    padding: 7px 10px;
    gap: 8px;
}

/* ============ 10. БРЕНД ВВЕРХУ + НИЗ САЙДБАРА (30.09, по слову Романа) ============
   Логотип в самом верху и крупнее (знак 22->30px, имя 14->17px);
   меню подтянуто к верху; кнопка темы живет в низу сайдбара. */
.tb1-side .tb1-brand .nav-mark {
    width: 30px;
    height: 30px;
}
.tb1-side .tb1-brand .nav-brand-name {
    font-size: 17px;
}
.tb1-side-foot {
    margin-top: auto;
    /* 02.10: низ 16px - кнопка темы стояла в 2px от края окна и пропадала,
       если низ окна перекрыт панелью задач / окно выше экрана. */
    padding: 10px 4px 16px;
    display: flex;
    align-items: center;
    gap: 10px;
}
@media (min-width: 992px) {
    /* Десктоп (navbar-expand-lg): колонка тянется на высоту сайдбара,
       margin-top: auto прижимает кнопку темы к низу. На мобильном
       правило не применяется - иначе сломает скрытие .collapse. */
    .tb1-side .navbar-collapse {
        display: flex;
        flex-direction: column;
    }
    /* 02.10: бренд не растягивается (flex 1 1 auto делил высоту с меню -
       блок 247px, лого на y~106, под ним ~130px пустоты). Лого вверху,
       меню сразу под ним - как и просил Роман 30.09. */
    .tb1-side .tb1-brand {
        flex: 0 0 auto;
        padding-top: 14px;
        padding-bottom: 18px;
    }
}
@media (max-width: 560px) {
    /* Телефон: знак чуть меньше десктопного, но крупнее прежних 20px. */
    .tb1-side .tb1-brand .nav-mark {
        width: 24px;
        height: 24px;
    }
}

/* Контент: main.page НЕ покрывался правилом body>.page (он вложен глубже),
   поэтому на нем жили старые .page из main.css (max-width + центрирование) -
   отсюда широкая пустота слева. Снимаем, даем свой желоб 16px. */
main.tb1-main {
    max-width: none;
    margin: 0;
    padding: 0 16px 24px;
}
.tb1-body {
    padding-top: 12px;
}
.tb1-main .page-head {
    margin: 0 0 10px;
}

/* Плотные карточки и сетка вместо таблеровского воздуха. */
.tb1-main .row {
    --bs-gutter-x: 1.25rem;
    --bs-gutter-y: 1.25rem;
}
.tb1-main .card {
    --tblr-card-spacer-y: 12px;
    --tblr-card-spacer-x: 14px;
}
/* === V2.CSS === */
/* demo/assets/css/v2.css */
/* НАЗНАЧЕНИЕ: стили зоны W2 (План-факт + Цілі) поверх Tabler. Только tb2- классы. */
/* Темы: цвета тянутся из Tabler-переменных с откатом на токены демо, поэтому
   light/dark работают без отдельных веток. :root здесь нет (см. CONTRACT). */

/* ---------- Строка периода ---------- */
.tb2-period {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 4px 10px;
    font-size: 13px;
    margin: 0 0 12px;
}
.tb2-period strong {
    font-weight: 700;
    color: var(--tblr-body-color, var(--text));
}

/* ---------- Шапка карточки: заголовок + подпись столбиком ---------- */
.tb2-card-head {
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
}
.tb2-card-sub {
    font-size: 12px;
}

/* ---------- Графики: Chart.js с maintainAspectRatio:false берет высоту родителя ---------- */
.tb2-chart {
    position: relative;
    height: 220px;
}
.tb2-chart-sm {
    height: 170px;
}

/* ---------- Роспись цифр (клієнти, борги) ---------- */
.tb2-stats {
    display: flex;
    flex-direction: column;
    gap: 12px;
}
.tb2-stat {
    line-height: 1.35;
}
.tb2-stat-v {
    font-size: 22px;
    font-weight: 700;
    line-height: 1.2;
    font-variant-numeric: tabular-nums;
}
.tb2-stat .text-muted {
    font-size: 12px;
}

/* ---------- Суммы под графиком (частки, ROAS) ---------- */
.tb2-sums {
    display: flex;
    flex-direction: column;
    gap: 3px;
    margin-top: 10px;
    font-size: 13px;
    color: var(--tblr-muted, var(--muted));
}
.tb2-sum {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 8px;
}
.tb2-sum b {
    color: var(--tblr-body-color, var(--text));
    font-variant-numeric: tabular-nums;
}

/* ---------- Ліди: цифра + дельта ---------- */
.tb2-lead {
    margin: 0 0 10px;
    font-size: 14px;
}
.tb2-lead b {
    font-size: 22px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

/* ---------- Таблица целей ---------- */
.tb2-goals td,
.tb2-goals th {
    vertical-align: middle;
}
.tb2-input {
    width: 130px;
    max-width: 100%;
    margin-left: auto;
    text-align: right;
    font-variant-numeric: tabular-nums;
}
.tb2-num {
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}
td.tb2-num {
    font-weight: 600;
}
.tb2-goal-pct {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 8px;
}
.tb2-goal-pct .tb2-bar {
    width: 90px;
    flex: 0 0 auto;
}
.tb2-goal-pct .tb2-num {
    font-weight: 700;
    min-width: 52px;
    text-align: right;
}
.tb2-note {
    font-size: 12px;
}

/* Статус строки красит только полосу прогресса. Цвета - семантика Tabler
   с откатом на токены демо: работают и в light, и в dark. */
.tb2-is-ok .tb2-fill { background: var(--ok, #2fb344); }
.tb2-is-warn .tb2-fill { background: var(--warn, #FFD500); }
.tb2-is-alert .tb2-fill { background: var(--err, #F97316); }
.tb2-is-neutral .tb2-fill { background: var(--tblr-muted, var(--muted, #929dab)); }

/* ---------- Аккордеон <details> ---------- */
.tb2-fold-sum {
    cursor: pointer;
    list-style: none;
    display: flex;
    align-items: baseline;
    gap: 8px;
    padding: 4px 2px;
    border-radius: 4px;
}
.tb2-fold-sum::-webkit-details-marker {
    display: none;
}
.tb2-fold-title {
    font-weight: 700;
    font-size: 14px;
}
.tb2-fold-chev {
    margin-left: auto;
    color: var(--tblr-muted, var(--muted));
    display: inline-flex;
}
.tb2-fold[open] .tb2-fold-chev {
    transform: rotate(180deg);
}
.tb2-fold-body {
    margin-top: 8px;
}

/* ---------- Узкие экраны: таблица целей не давит инпут ---------- */
@media (max-width: 576px) {
    .tb2-input {
        width: 96px;
    }
    .tb2-goal-pct .tb2-bar {
        width: 56px;
    }
}
/* === V3.CSS === */
/* demo/assets/css/v3.css */
/* НАЗНАЧЕНИЕ: маркетинг на Tabler - только tb3-классы поверх card/row/col/table */
/* СВЯЗИ: views/w3.js (разметка), brand.css W1 (тема), variables.css (токены) */
/* Темы: все цвета только var(--*), темная/светлая переключаются токенами сами */

.tb3-pane { display: none; contain: inline-size; }
.tb3-pane.tb3-on { display: block; }

/* Ритм между блоками: 20px, как у Strimix между смысловыми группами */
.tb3-sect { margin-top: var(--sp-5); }

/* Заголовок панели набором как метка: 12/700 uppercase, цвет акцента */
.tb3-pt {
    font-size: 12px !important;
    font-weight: 700 !important;
    text-transform: uppercase;
    letter-spacing: 0.72px;
    color: var(--blue);
}
.tb3-ps {
    font-size: var(--fs-2);
    color: var(--muted);
}

/* ---------- Фильтр-ряд ---------- */

.tb3-filter {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--sp-2);
    margin: var(--sp-3) 0 var(--sp-4);
}

.tb3-sel {
    width: auto;
    max-width: 100%;
    border-radius: var(--r-pill);
    font-size: var(--fs-3);
    font-weight: 500;
}

.tb3-pill {
    border-style: dashed;
    border-radius: var(--r-pill);
    color: var(--dim);
    font-size: var(--fs-3);
    font-weight: 500;
}
.tb3-pill.tb3-on {
    border-style: solid;
    border-color: var(--blue-line);
    background: var(--blue-soft);
    color: var(--blue);
}

.tb3-note {
    margin: calc(var(--sp-3) * -1) 0 var(--sp-3);
    font-size: var(--fs-2);
    color: var(--muted);
}
.tb3-note[hidden] { display: none; }

/* ---------- KPI-стрічка ---------- */

.tb3-scrollwrap { position: relative; }

.tb3-scroll {
    display: flex;
    gap: var(--sp-3);
    overflow-x: auto;
    padding-bottom: var(--sp-1);
    scroll-behavior: smooth;
}

.tb3-arrow {
    position: absolute;
    top: 50%;
    z-index: 2;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    border: 1px solid var(--border);
    border-radius: 50%;
    background: var(--surface);
    color: var(--dim);
    font-size: 18px;
    line-height: 1;
    cursor: pointer;
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.12);
    transform: translateY(-50%);
}
.tb3-arrow:hover { border-color: var(--border-strong); color: var(--text); }
.tb3-arrow[data-mkscroll="-1"] { left: 0; }
.tb3-arrow[data-mkscroll="1"] { right: 0; }

/* KPI-картка Strimix: 234px, 4 рівні - мітка, цифра, дельта, «було X» */
.tb3-kpi { flex: 0 0 234px; }
/* Ведуча метрика панелі: рамка трохи сильніша, без кольорової заливки */
.tb3-kpi.tb3-main { border-color: var(--border-strong); }

.tb3-kbi { padding: 20px; }

.tb3-klabel {
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.66px;
    color: var(--muted);
}

.tb3-kval {
    margin-top: var(--sp-1);
    font-size: 26px;
    font-weight: 700;
    line-height: 1.1;
    color: var(--text);
    font-variant-numeric: var(--tnum);
    white-space: nowrap;
}

.tb3-kcur {
    margin-left: var(--sp-1);
    font-size: 18px;
    font-weight: 500;
    color: var(--muted);
}

.tb3-ksub {
    margin-top: var(--sp-1);
    font-size: var(--fs-2);
    color: var(--muted);
    font-variant-numeric: var(--tnum);
}

.tb3-compare {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: var(--gap-xs) var(--gap-s);
    margin-top: var(--gap-s);
    min-height: 22px;
}

.tb3-was {
    color: var(--muted);
    font-size: var(--fs-2);
    white-space: nowrap;
}
.tb3-was b {
    font-weight: 400;
    font-variant-numeric: var(--tnum);
    color: var(--muted);
}
.tb3-was::before { content: 'було '; font-weight: 400; }

/* Дельта - кольоровий текст без плашки, стрілка показує оцінку */
.tb3-delta {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    font-size: var(--fs-3);
    font-weight: 500;
    font-variant-numeric: var(--tnum);
    white-space: nowrap;
}
.tb3-delta.tb3-up { color: var(--ok); }
.tb3-delta.tb3-down { color: var(--err-text); }
.tb3-delta.tb3-flat { color: var(--muted); }

/* ---------- Таблиця порівняння ---------- */

.tb3-table th {
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.66px;
    color: var(--muted);
    padding: 8px 14px;
    border-bottom: 1px solid var(--border);
    background: none;
}
.tb3-table td {
    font-size: 12px;
    color: var(--dim);
    padding: 7px 14px;
    height: 40px;
    border-bottom: 1px solid var(--border);
    background: none;
    font-variant-numeric: var(--tnum);
}
.tb3-table .tb3-nm { font-weight: 400; }
.tb3-table .tb3-r, .tb3-table .tb3-val { text-align: right; white-space: nowrap; }

/* Смуга доходу під числом: частка від максимуму каналу */
.tb3-bar {
    display: block;
    height: 3px;
    margin-top: 3px;
    margin-left: auto;
    border-radius: var(--r-pill);
    background: var(--blue);
    opacity: 0.7;
}

.tb3-primary {
    display: inline-block;
    margin-left: var(--sp-2);
    padding: 1px 8px;
    border-radius: var(--r-pill);
    background: var(--blue-soft);
    color: var(--blue);
    font-size: var(--fs-2);
    font-weight: 600;
    vertical-align: 1px;
}

.tb3-table tr[data-ch] { cursor: pointer; }
.tb3-table tr[data-ch]:hover td { background: var(--surface-hover); }
.tb3-table tr[data-ch].tb3-on td:first-child { box-shadow: inset 3px 0 0 var(--blue); }

/* ---------- Графіки ---------- */

.tb3-chart {
    position: relative;
    width: 100%;
    max-width: 100%;
    height: 240px;
    overflow: hidden;
}
.tb3-chart.tb3-tall { height: 280px; }
.tb3-chart > canvas { max-width: 100%; }

/* ---------- Донат з підписом у центрі ---------- */

.tb3-donut { position: relative; }

.tb3-donut-c {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    width: 62%;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    pointer-events: none;
}
.tb3-donut-c b {
    font-size: var(--fs-6);
    font-variant-numeric: var(--tnum);
    color: var(--text);
}
.tb3-donut-c span { font-size: var(--fs-2); color: var(--muted); }

/* ---------- Воронка: картки каналів ---------- */

.tb3-fcard {
    display: block;
    width: 100%;
    margin: 0 0 var(--sp-2);
    padding: var(--sp-3) var(--sp-4);
    border: 1px solid var(--border);
    border-radius: var(--r-2);
    background: var(--surface);
    text-align: left;
    cursor: pointer;
}
.tb3-fcard:last-child { margin-bottom: 0; }
.tb3-fcard:hover { border-color: var(--border-strong); background: var(--surface-2); }
.tb3-fcard.tb3-on { border-color: var(--blue-line); background: var(--blue-soft); }

.tb3-fcard-top {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--sp-2);
}
.tb3-fcard-name { font-size: var(--fs-4); font-weight: 600; color: var(--text); }
.tb3-fcard-top b { font-size: var(--fs-4); font-variant-numeric: var(--tnum); color: var(--text); }
.tb3-fcard-nums { display: block; margin-top: var(--sp-1); font-size: var(--fs-3); color: var(--muted); }
.tb3-fcard-nums b { color: var(--dim); font-variant-numeric: var(--tnum); }

/* ---------- Воронка: шеврони ---------- */

.tb3-scalerow { display: flex; justify-content: flex-end; }

.tb3-scale { display: inline-flex; border: 1px solid var(--border); border-radius: var(--r-pill); overflow: hidden; }
.tb3-scale button {
    padding: 3px 12px;
    border: 0;
    background: none;
    color: var(--muted);
    font-size: var(--fs-2);
    font-weight: 600;
    cursor: pointer;
}
.tb3-scale button.tb3-on { background: var(--text); color: var(--bg-0); }

.tb3-chev-row {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 6px;
    margin-top: var(--sp-3);
}
.tb3-chev {
    position: relative;
    padding: var(--sp-3) var(--sp-3) var(--sp-3) var(--sp-4);
    background: var(--surface-2);
    clip-path: polygon(0 0, calc(100% - 18px) 0, 100% 50%, calc(100% - 18px) 100%, 0 100%, 18px 50%);
}
.tb3-chev:first-child {
    padding-left: var(--sp-3);
    clip-path: polygon(0 0, calc(100% - 18px) 0, 100% 50%, calc(100% - 18px) 100%, 0 100%);
}
.tb3-chev:last-child {
    clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%, 18px 50%);
}
.tb3-chev-head { display: flex; align-items: center; gap: var(--sp-2); }
.tb3-chev-n {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 20px;
    height: 20px;
    padding: 0 5px;
    border-radius: var(--r-pill);
    background: var(--blue-soft);
    color: var(--blue);
    font-size: var(--fs-2);
    font-weight: 700;
}
.tb3-chev-t { font-size: var(--fs-3); font-weight: 600; color: var(--dim); }
.tb3-chev-v {
    display: block;
    margin: var(--sp-2) 0 var(--sp-1);
    font-size: var(--fs-8);
    font-variant-numeric: var(--tnum);
    color: var(--text);
}
.tb3-chev-bar { display: block; height: 8px; border-radius: var(--r-pill); background: var(--border); overflow: hidden; }
.tb3-chev-bar i { display: block; height: 100%; border-radius: var(--r-pill); background: var(--blue); }
.tb3-chev-p { display: block; margin-top: var(--sp-1); font-size: var(--fs-2); color: var(--muted); }
.tb3-chev-drop {
    display: inline-block;
    margin-top: var(--sp-2);
    padding: 2px 10px;
    border-radius: var(--r-pill);
    background: var(--warn-soft);
    color: var(--warn-text);
    font-size: var(--fs-2);
    font-weight: 700;
    font-variant-numeric: var(--tnum);
}
.tb3-chev-drop small { display: block; font-weight: 400; color: var(--muted); }

/* ---------- Смуга плану продажів ---------- */

.tb3-plan {
    display: flex;
    flex-wrap: wrap;
    gap: var(--gap);
    margin: 0 0 var(--sp-4);
    padding: var(--sp-3) var(--sp-4);
    border: 1px solid var(--blue-line);
    border-radius: var(--r-2);
    background: var(--blue-soft);
}
.tb3-plan-item { min-width: 140px; }
.tb3-plan-item span { display: block; font-size: var(--fs-2); color: var(--muted); }
.tb3-plan-item b { font-size: var(--fs-5); font-variant-numeric: var(--tnum); color: var(--text); }

@media (max-width: 1100px) {
    .tb3-chev-row { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .tb3-chev, .tb3-chev:first-child, .tb3-chev:last-child { clip-path: none; border-radius: var(--r-2); }
}

@media (max-width: 640px) {
    .tb3-chev-row { grid-template-columns: minmax(0, 1fr); }
    .tb3-donut-c { width: 100%; }
}
/* === V4.CSS (Tabler-редизайн, воркер W4) === */
/* demo/assets/css/v4.css */
/* НАЗНАЧЕНИЕ: стили зоны W4 (продажи, финансы, бухгалтерия), только tb4- */
/* СВЯЗИ: views/w4-a.js, views/w4-b.js; темы через var(--tblr-*) с фолбэком */
/*   на наши токены - работает и в light, и в dark (data-bs-theme + наш тогл) */

/* Вертикальный ритм между смысловыми блоками: 20px (Strimix, ступень 3). */
.tb4-gap { margin-top: 20px; }

/* Вертикальный стек карточек (P&L + Cash Flow справа от баланса). */
.tb4-stack { display: flex; flex-direction: column; gap: 20px; min-width: 0; }

/* --- KPI-карточка (схема Strimix: метка, цифра, дельта, было, пояснение) --- */
.tb4-kpi-label {
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .66px;
    color: var(--tblr-secondary, var(--dim));
}
.tb4-kpi-top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
}
.tb4-kpi-value {
    font-size: 26px;
    font-weight: 700;
    line-height: 1.15;
    color: var(--tblr-body-color, var(--text));
    font-variant-numeric: tabular-nums;
    margin-top: 4px;
}
.tb4-kpi-cur {
    font-size: 18px;
    font-weight: 500;
    color: var(--tblr-secondary, var(--dim));
    margin-left: 6px;
}
.tb4-kpi-sub {
    font-size: 12px;
    color: var(--tblr-secondary, var(--dim));
    margin-top: 4px;
    line-height: 1.5;
}
/* Кликабельная KPI-карта продаж: ховер и активное состояние. */
.tb4-kpi[data-sl] { cursor: pointer; }
.tb4-kpi[data-sl]:hover { border-color: var(--tblr-primary, var(--accent)); }
.tb4-kpi[data-sl].is-active {
    border-color: var(--tblr-primary, var(--accent));
    box-shadow: 0 0 0 1px var(--tblr-primary, var(--accent));
}
.tb4-kpi[data-sl]:focus-visible {
    outline: 2px solid var(--tblr-primary, var(--accent));
    outline-offset: 2px;
}

/* --- Контейнер графика (высота как .chart-box, charts.js без aspect ratio) --- */
.tb4-chart { position: relative; width: 100%; max-width: 100%; height: 240px; }
.tb4-chart > canvas { max-width: 100%; }
.tb4-dyn-name { margin: 0 0 12px; font-size: 12px; }
.tb4-note { margin: 12px 0 0; font-size: 13px; line-height: 1.55; }
.tb4-num { font-variant-numeric: tabular-nums; }
.tb4-wrap { white-space: normal; }

/* --- Таблицы --- */
.tb4-cell-main { font-weight: 600; }
.tb4-cell-sub {
    font-size: 12px;
    color: var(--tblr-secondary, var(--dim));
    margin-top: 2px;
}
/* Тонкий прогресс-бар в ячейке: факт против нормы. */
.tb4-pbar { display: block; margin-top: 4px; min-width: 80px; }
/* Выбранный ряд менеджеров (клик - динамика справа). */
tr.tb4-row-active > td {
    background: var(--tblr-active-bg, var(--surface-2));
}
tr[data-sl-mgr], tr.cp-row { cursor: pointer; }
tr[data-sl-mgr]:focus-visible, tr.cp-row:focus-visible {
    outline: 2px solid var(--tblr-primary, var(--accent));
    outline-offset: -2px;
}
/* Липкая первая колонка широких таблиц (P&L, CF, контрагенты, учет). */
.tb4-stick {
    position: sticky;
    left: 0;
    z-index: 1;
    background: var(--tblr-card-bg, var(--surface));
    box-shadow: 1px 0 0 var(--tblr-border-color, var(--border));
}
thead .tb4-stick { z-index: 2; }

/* --- Подвкладки продаж Огляд | Дзвінки --- */
.tb4-subtabs { margin: 12px 0 16px; }
.tb4-subtabs .nav-link { border-radius: var(--radius-sm); }

/* --- Звонки --- */
.tb4-calls > .tb4-call + .tb4-call { margin-top: 12px; }
.tb4-call-head {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
}
.tb4-call-title { font-size: 14px; }
.tb4-call-meta {
    font-size: 13px;
    color: var(--tblr-secondary, var(--dim));
    margin-left: auto;
}
.tb4-trans { margin-top: 8px; }
.tb4-trans > summary {
    cursor: pointer;
    font-size: 13px;
    font-weight: 600;
    color: var(--tblr-primary, var(--accent));
}
.tb4-trans > summary:focus-visible {
    outline: 2px solid var(--tblr-primary, var(--accent));
    outline-offset: 2px;
}
.tb4-trans-body {
    margin-top: 8px;
    padding: 12px 14px;
    border-radius: 8px;
    background: var(--tblr-bg-surface-secondary, var(--surface-2));
}
.tb4-line { margin: 0 0 8px; font-size: 13px; line-height: 1.55; }
.tb4-line:last-child { margin-bottom: 0; }
.tb4-line.is-c .tb4-who { color: var(--tblr-secondary, var(--dim)); }

/* --- Баланс --- */
.tb4-bal-cap {
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .66px;
    color: var(--tblr-secondary, var(--dim));
    margin-bottom: 8px;
}
.tb4-bal-row {
    display: flex;
    justify-content: space-between;
    gap: 12px;
    padding: 7px 0;
    font-size: 13px;
    border-bottom: 1px solid var(--tblr-border-color, var(--border));
}
.tb4-bal-row.is-total { border-bottom: none; border-top: 2px solid var(--tblr-border-color, var(--border)); }

/* --- Модалка карточки контрагента (механика hidden 1в1) --- */
.tb4-modal {
    position: fixed;
    inset: 0;
    z-index: 1050;
    display: flex;
    align-items: flex-start;
    justify-content: center;
    padding: 48px 16px 16px;
    background: rgba(0, 0, 0, .5);
    overflow-y: auto;
}
.tb4-modal[hidden] { display: none; }
.tb4-modal-card {
    width: 100%;
    max-width: 640px;
    border-radius: var(--r-3);
    padding: 20px;
    background: var(--tblr-card-bg, var(--surface));
    border: 1px solid var(--tblr-border-color, var(--border));
    color: var(--tblr-body-color, var(--text));
}
.tb4-modal-close { margin-top: 16px; }
.tb4-cp-kind { font-size: 12px; color: var(--tblr-secondary, var(--dim)); }
.tb4-cp-name { font-size: 20px; font-weight: 700; margin: 2px 0 8px; }
.tb4-cp-meta {
    display: flex;
    gap: 16px;
    flex-wrap: wrap;
    align-items: center;
    font-size: 13px;
    margin-bottom: 12px;
}
.tb4-cp-chans {
    display: flex;
    gap: 12px;
    align-items: center;
    font-size: 13px;
    margin-bottom: 12px;
}

/* --- Бухгалтерия: пропорция 3fr/5fr (типы/документы) --- */
.tb4-ac-cols {
    display: grid;
    grid-template-columns: 3fr 5fr;
    gap: 20px;
    align-items: start;
}
.tb4-comment {
    font-size: 12px;
    color: var(--tblr-secondary, var(--dim));
    line-height: 1.55;
}

@media (max-width: 992px) {
    .tb4-ac-cols { grid-template-columns: 1fr; }
    .tb4-call-meta { margin-left: 0; width: 100%; }
}
/* === V5.CSS === */
/* demo/assets/css/v5.css */
/* НАЗНАЧЕНИЕ: стили зоны W5 (Производство, Инфраструктура, Интеграции) поверх Tabler */
/* СВЯЗИ: views/w5.js (разметка tb5-*), variables.css (только токены, новых :root нет) */
/* Темы: цвета только через токены --* (светлая :root, темная html.dark-theme) - */
/* обе темы работают без привязки к data-bs-theme. Tabler-компоненты (.card, */
/* .table, .btn, .badge) красит сам Tabler. */

/* Вертикальный ритм зоны: 20px между смысловыми блоками (ступень Strimix). */
.tb5-block {
    margin-bottom: 20px;
}
.tb5-block:last-child {
    margin-bottom: 0;
}

/* Якоря подменю не уезжают под шапку. */
#if-map,
#if-cams,
#if-ses,
#if-inds,
#ig-sources,
#ig-dash {
    scroll-margin-top: 128px;
}

/* ============ Шапка карточки и KPI ============ */

/* Заголовок панели набором как метка: 12/700 uppercase (пункт 10 эталона). */
.tb5-card-title {
    font-size: 12px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    margin: 0;
}
.tb5-card-sub {
    margin-left: auto;
    padding-left: 12px;
    color: var(--muted);
    font-size: 12px;
    font-weight: 400;
    text-align: right;
}

.tb5-kpi {
    height: 100%;
    border-left: 3px solid transparent;
}
.tb5-kpi-alert {
    border-left-color: var(--err);
}
.tb5-kpi-label {
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--muted);
}
.tb5-kpi-value {
    font-size: 26px;
    font-weight: 700;
    line-height: 1.15;
    color: var(--text);
    font-variant-numeric: tabular-nums;
    margin-top: 2px;
}
.tb5-kpi-cur {
    font-size: 14px;
    font-weight: 500;
    color: var(--muted);
    margin-left: 6px;
}
.tb5-kpi-sub {
    margin-top: 4px;
    font-size: 12px;
    color: var(--muted);
}
/* Строка сравнения из общего compareHtml: дельта + "было" (слово "было"
/ дорисовывает components.css через ::before). */
.tb5-kpi .kpi-compare {
    display: flex;
    align-items: baseline;
    gap: 8px;
    margin-top: 4px;
    font-size: 12px;
}
.tb5-kpi .kpi-was {
    color: var(--muted);
}
.tb5-kpi .kpi-was b {
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

/* ============ График ============ */

.tb5-chart {
    position: relative;
    width: 100%;
    max-width: 100%;
    height: 240px;
    overflow: hidden;
}
.tb5-chart > canvas {
    max-width: 100%;
}

/* ============ Таблицы ============ */

/* Строка 40px, шапка 11/700 uppercase, ячейки 12px, разделитель только снизу. */
.tb5-table thead th {
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.02em;
    color: var(--muted);
    border-bottom: 1px solid var(--border);
    padding: 8px 8px;
    white-space: nowrap;
}
.tb5-table tbody td {
    font-size: 12px;
    color: var(--text);
    border-bottom: 1px solid var(--border);
    padding: 7px 8px;
    height: 40px;
    vertical-align: middle;
}
.tb5-table tbody tr:last-child td {
    border-bottom: none;
}
.tb5-cell-main {
    font-weight: 600;
    color: var(--text);
}
.tb5-cell-sub {
    display: block;
    font-size: 12px;
    font-weight: 400;
    color: var(--muted);
}
.tb5-num {
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}
.tb5-rk {
    color: var(--muted);
    width: 1%;
    font-variant-numeric: tabular-nums;
}

/* ============ Инфраструктура: легенда и каналы ============ */

.tb5-legend-chans {
    margin-top: 8px;
}
.tb5-legend-label {
    color: var(--muted);
}
/* Иконки брендов залиты currentColor, поэтому красим color.
   Цвета официальные: Telegram #26A5E4, Viber #665CAC, Gmail #EA4335. */
.tb5-chan .ico {
    width: 17px;
    height: 17px;
}
.tb5-chan.is-telegram .ico { color: #26a5e4; }
.tb5-chan.is-viber .ico { color: #665cac; }
.tb5-chan.is-gmail .ico { color: #ea4335; }

/* ============ Инфраструктура: камеры ============ */

.tb5-cam-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 8px;
}
.tb5-cam {
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 10px 12px;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-top-width: 3px;
    border-radius: var(--radius-sm);
    min-width: 0;
}
.tb5-cam.is-ok { border-top-color: var(--ok); }
.tb5-cam.is-warn { border-top-color: var(--warn); }
.tb5-cam.is-alert { border-top-color: var(--err); }
.tb5-cam-meta {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}
.tb5-cam-name {
    font-size: 13px;
    font-weight: 600;
    color: var(--text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.tb5-cam-state {
    font-size: 13px;
    color: var(--muted);
}

/* ============ Инфраструктура: СЕС ============ */

.tb5-ses {
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.tb5-ses-station {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    padding: 8px 12px;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
}
.tb5-ses-name {
    font-weight: 600;
    font-size: 13px;
}
.tb5-ses-val {
    font-weight: 600;
    font-size: 13px;
    white-space: nowrap;
    text-align: right;
    font-variant-numeric: tabular-nums;
}
.tb5-ses-app {
    margin-top: 12px;
    padding: 10px 14px;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
}
.tb5-ses-row {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 12px;
    padding: 3px 0;
    font-size: 13px;
    color: var(--text);
}
.tb5-ses-row b {
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}
.tb5-ses-bar {
    height: 6px;
    margin-top: 8px;
    border-radius: 9999px;
    background: var(--surface-hover);
}
.tb5-ses-bar > span {
    display: block;
    height: 100%;
    border-radius: 9999px;
    /* Полоса - метр заполнения (самоспоживання), не статус: бренд, не warn */
    background: var(--brand);
}

/* ============ Интеграции: схема ============ */

.tb5-ig-bar {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px 16px;
    margin-bottom: 12px;
}
.tb5-ig-note {
    color: var(--muted);
    font-size: 13px;
}
.tb5-ig-schema {
    overflow-x: auto;
}
.tb5-ig-svg {
    display: block;
    width: 100%;
    min-width: 640px;
    /* 02.10: viewBox 900 растягивался на всю карточку (x1,8 на 1920) - блоки,
       скругления и линии выглядели жирными. Потолок ~x1,2, по центру. */
    max-width: 1080px;
    margin: 0 auto;
    height: auto;
}
.tb5-ig-flow {
    fill: none;
    stroke: var(--blue);
    stroke-width: 1.5;
    stroke-dasharray: 7 6;
    opacity: 0.75;
    animation: tb5-ig-flow 1.1s linear infinite;
}
.tb5-ig-flow.is-out {
    stroke: var(--ok);
}
.tb5-ig-flow.is-ret {
    stroke: var(--warn);
    stroke-dasharray: 4 6;
    opacity: 0.6;
}
.tb5-ig-arr-head {
    fill: var(--dim);
}
@keyframes tb5-ig-flow {
    to { stroke-dashoffset: -13; }
}
.tb5-ig-dot.is-ok { fill: var(--ok); }
.tb5-ig-dot.is-warn { fill: var(--warn); }
.tb5-ig-node rect {
    fill: var(--surface-2);
    stroke: var(--border-strong);
    stroke-width: 1;
}
.tb5-ig-node.is-core rect {
    fill: var(--blue-soft);
    stroke: var(--blue);
    stroke-width: 1.5;
    animation: tb5-ig-pulse 2.4s ease-in-out infinite;
}
@keyframes tb5-ig-pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.82; }
}
.tb5-ig-node.is-dash rect {
    stroke: var(--ok);
}
.tb5-ig-name {
    fill: var(--text);
    font-size: 14px;
    font-weight: 700;
}
.tb5-ig-sub {
    fill: var(--muted);
    font-size: 12px;
}
.tb5-ig-details > summary {
    cursor: pointer;
    font-size: 13px;
    font-weight: 600;
    color: var(--dim);
    margin-bottom: 8px;
}

/* ============ Адаптив ============ */

@media (max-width: 1100px) {
    .tb5-cam-grid {
        grid-template-columns: 1fr 1fr;
    }
}
@media (max-width: 560px) {
    .tb5-cam-grid {
        grid-template-columns: 1fr;
    }
    .tb5-card-sub {
        flex-basis: 100%;
        margin-left: 0;
        padding-left: 0;
        text-align: left;
    }
}

/* Уважение к prefers-reduced-motion: потоки и пульс ядра останавливаются. */
@media (prefers-reduced-motion: reduce) {
    .tb5-ig-flow,
    .tb5-ig-node.is-core rect {
        animation: none;
    }
}
/* =====================================================================
   vs-finish.css - внедрение pick-list визуала (/tmp/visual-top.md).
   Все новое - с префиксом vs-. Строго через токены var(--*) и --tblr-*.
   ===================================================================== */

/* ---------- A-1: scroll-reveals на чистом CSS (30.09) ----------
   animation-timeline: view() - прогрессивное улучшение: без поддержки
   блоки просто видны сразу. Уважает prefers-reduced-motion. */
@supports (animation-timeline: view()) {
    .tb1-main .view.active .card {
        animation: vs-a-in 480ms ease-out both;
        animation-timeline: view();
        animation-range: entry 0% entry 30%;
    }
    /* Ручной каскад для не-карточек: class="vs-a-reveal" data-vs-d="1..4". */
    .vs-a-reveal {
        animation: vs-a-in 480ms ease-out both;
        animation-timeline: view();
        animation-range: entry 0% entry 30%;
    }
    .vs-a-reveal[data-vs-d="1"] { animation-range: entry 5% entry 35%; }
    .vs-a-reveal[data-vs-d="2"] { animation-range: entry 10% entry 40%; }
    .vs-a-reveal[data-vs-d="3"] { animation-range: entry 15% entry 45%; }
    .vs-a-reveal[data-vs-d="4"] { animation-range: entry 20% entry 50%; }
}

@keyframes vs-a-in {
    from { opacity: 0; transform: translateY(14px); }
    to { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
    .tb1-main .view.active .card,
    .vs-a-reveal {
        animation: none;
    }
}

/* ---------- A-2: View Transitions при смене разделов (30.09) ----------
   Вызывается из app.js go(); без API - мгновенное переключение как раньше. */
::view-transition-old(root),
::view-transition-new(root) {
    animation-duration: 220ms;
}

@media (prefers-reduced-motion: reduce) {
    ::view-transition-old(root),
    ::view-transition-new(root) {
        animation: none;
    }
}

/* ---------- B-3: иконки брендов в ячейках таблиц (30.09) ----------
   Наследуют цвет текста (currentColor), размер под строку таблицы. */
.tb3-nm .ico,
.tb5-cell-main .ico {
    width: 15px;
    height: 15px;
    vertical-align: -2px;
    margin-right: 6px;
}

/* ---------- C-1 SmallBox: квадрат иконки в KPI-карточке (30.09) ----------
   Тонированный квадрат, глиф цветом акцента. Полоса-ссылка не ставится:
   целевых подразделов у KPI нет, мертвые ссылки хуже отсутствия. */
.tb3-kpi {
    position: relative;
}
.tb3-kpi .vs-c-ico {
    position: absolute;
    top: 10px;
    right: 10px;
    width: 30px;
    height: 30px;
    border-radius: 9px;
    background: color-mix(in srgb, var(--accent) 16%, transparent);
    color: var(--accent);
    display: flex;
    align-items: center;
    justify-content: center;
    pointer-events: none;
}
.tb3-kpi .vs-c-ico .ico {
    width: 16px;
    height: 16px;
}

/* ---------- B-5: display-шрифт Geologica (30.09, замена мертвого Ficus) ----------
   Система: Plex - текст, Geologica - заголовки карточек и цифры KPI.
   Кириллица: cyrillic woff2 подтверждены в пакете. */
.tb3-kval,
.tb3-pt {
    font-family: var(--font-display);
    letter-spacing: -0.01em;
}
/* === SD-TABLES.CSS (S2) === */
/* НАЗНАЧЕНИЕ: Strimix-таблицы поверх Tabler: DataTable, DrillTable, CohortHeat,
   BarlistRow, Scorecard4, KpiCard, Pagination, FunnelSteps-разметка.
   Замеры 1:1 из strimix-full п.5: ячейка 12px/#334155/pad 7x14, шапка #F8FAFC/#64748B,
   Δ-пилюли positive #E7F6F0/#12A76D negative #FCECEC/#F97316, ссылка #0097A7.
   Токены НЕ объявляем (владелец S1 variables.css): все var() с литеральным fallback. */
/* СВЯЗИ: sd-tables.js (разметка), views/marketing|sales|finance|accounting|operations.js */
/* ПРИЕМКА S2: п.1 (живые числа), п.2 (drill 3 уровня), п.4 (mobile 390), п.6 (тултипы) */

/* ============ 1. DataTable ============ */
.sd-table-card .table {
    --tblr-table-color: var(--text-body, #334155);
    margin-bottom: 0;
}
.sd-table-card .table > :not(caption) > * > * {
    padding: 7px 14px; /* замер Strimix 1:1 */
    font-size: 12px;
    color: var(--text-body, #334155);
    border-bottom-color: var(--border-subtle, #EDF1F5);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}
.sd-table-card thead th {
    background: var(--surface-sunken, #F8FAFC); /* замер 1:1 */
    color: var(--text-muted, #64748B);
    font-size: 11px;
    font-weight: 500;
    letter-spacing: .04em;
    text-transform: uppercase;
    border-bottom: 1px solid var(--border-default, #DCE3EA);
    position: sticky;
    top: 0;
    z-index: 2;
}
.sd-table-card thead th .sd-th-tip {
    cursor: help;
    border-bottom: 1px dotted currentColor;
}
.sd-table-card tbody tr:hover > * {
    background: var(--brand-tint-8, rgba(0, 188, 212, .08));
}
/* Итог «Разом»: жирная верхняя граница, полужирный текст */
.sd-table-card tr.sd-total > * {
    font-weight: 700;
    border-top: 2px solid var(--border-strong, #C4CDD6);
    background: var(--surface-sunken, #F8FAFC);
}
/* Числовые колонки вправо, первая колонка-метка влево и шире */
.sd-table-card td.sd-num, .sd-table-card th.sd-num { text-align: right; }
.sd-table-card td.sd-label {
    font-weight: 500;
    color: var(--text-strong, #16202E);
    white-space: normal;
    min-width: 140px;
}
/* Ссылка «Перейти до детального звіту» */
.sd-detail-link {
    display: inline-block;
    padding: 10px 14px;
    font-size: 13px;
    font-weight: 500;
    color: var(--text-link, #0097A7);
    text-decoration: none;
}
.sd-detail-link:hover { color: var(--text-brand, #00838F); text-decoration: underline; }
/* Сортировка: th[data-sort] кликабельна, стрелка в ::after */
.sd-table-card th[data-sort] { cursor: pointer; user-select: none; }
.sd-table-card th[data-sort]:hover { color: var(--text-strong, #16202E); }
.sd-table-card th[data-sort]::after {
    content: "";
    display: inline-block;
    margin-left: 6px;
    border: 4px solid transparent;
    border-top-color: var(--text-faint, #94A3B8);
    vertical-align: middle;
}
.sd-table-card th[data-sort="asc"]::after { border-top-color: transparent; border-bottom-color: var(--brand, #00BCD4); }
.sd-table-card th[data-sort="desc"]::after { border-top-color: var(--brand, #00BCD4); }

/* ============ 2. Δ-пилюли (замер Strimix 1:1) ============ */
.sd-delta {
    display: inline-block;
    padding: 1px 8px;
    font-size: 11px;
    font-weight: 500;
    border-radius: var(--radius-pill, 999px);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}
.sd-delta.sd-up { background: var(--positive-bg, #E7F6F0); color: var(--positive, #12A76D); }
.sd-delta.sd-down { background: var(--negative-bg, #FCECEC); color: var(--err-text, #C2410C); }
.sd-delta.sd-flat { background: var(--surface-auth, #F1F5F9); color: var(--text-muted, #64748B); }
/* Желтая/светло-зеленая ступени для вороночных конверсий (таб 3: 60,4/78,0/94,8) */
.sd-delta.sd-warn { background: var(--warning-bg, #FDF1DC); color: var(--warn-text, #A95000); }
.sd-was { color: var(--text-muted, #64748B); font-size: 11px; white-space: nowrap; }
.sd-cell-stack { display: flex; flex-direction: column; gap: 2px; align-items: flex-end; }

/* ============ 3. DrillTable (▶/▼, 3 уровня, sticky-первая) ============ */
.sd-drill .sd-twisty {
    display: inline-block;
    width: 20px;
    color: var(--text-faint, #94A3B8);
    cursor: pointer;
    user-select: none;
    font-size: 10px;
}
.sd-drill tr.sd-lvl-1 td.sd-label { padding-left: 14px; }
.sd-drill tr.sd-lvl-2 td.sd-label { padding-left: 34px; }
.sd-drill tr.sd-lvl-3 td.sd-label { padding-left: 54px; font-weight: 400; }
.sd-drill tr.sd-child { display: none; }
.sd-drill tr.sd-child.sd-open { display: table-row; }
.sd-drill tr.sd-loading td { color: var(--text-faint, #94A3B8); font-style: italic; }
.sd-drill tr.sd-selected > * { background: var(--brand-tint-12, rgba(0, 188, 212, .12)); }
/* Кнопка «Метрики N» + expand-иконки в шапке таблицы */
.sd-table-tools { display: flex; gap: 8px; align-items: center; margin-left: auto; }
.sd-table-tools .btn { height: 32px; font-size: 13px; }

/* ============ 4. CohortHeat (таб 4: heatmap синій→зелений, «–» будущие) ============ */
.sd-cohort td.sd-heat-0 { background: var(--surface-sunken, #F8FAFC); color: var(--text-faint, #94A3B8); }
.sd-cohort td.sd-heat-1 { background: #E0F1FF; }
.sd-cohort td.sd-heat-2 { background: #B5DCFF; }
.sd-cohort td.sd-heat-3 { background: #80BDFB; }
.sd-cohort td.sd-heat-4 { background: #449DF0; }
.sd-cohort td.sd-heat-5 { background: #0069CA; color: #fff; }
.sd-cohort td.sd-heat-6 { background: #12A76D; color: #fff; }
.sd-cohort td.sd-future { color: var(--text-faint, #94A3B8); background: transparent; }
.sd-cohort td:first-child, .sd-cohort th:first-child {
    position: sticky; left: 0; z-index: 1;
    background: var(--surface-card, #fff);
    box-shadow: 1px 0 0 var(--border-default, #DCE3EA);
}
/* Условное форматирование sales КОНВЕРСІЯ ЕТАПІВ (таб 6 п.14.5) */
.sd-cond-good { background: var(--positive-bg, #E7F6F0) !important; }
.sd-cond-mid { background: var(--warning-bg, #FDF1DC) !important; }
.sd-cond-bad { background: var(--negative-bg, #FCECEC) !important; }
.sd-sticky-col {
    position: sticky; left: 0; z-index: 1;
    background: var(--surface-card, #fff);
    box-shadow: 1px 0 0 var(--border-default, #DCE3EA);
}

/* ============ 5. KpiCard + KpiCarousel (формат п.3.3 full) ============ */
.sd-kpi .card-body { padding: 14px 16px; }
.sd-kpi-overline {
    font-size: 11px; font-weight: 500;
    letter-spacing: .06em; text-transform: uppercase;
    color: var(--text-muted, #64748B);
    display: flex; align-items: center; gap: 4px;
}
.sd-kpi-info {
    display: inline-block; width: 14px; height: 14px;
    line-height: 14px; text-align: center;
    font-size: 10px; font-style: normal;
    border: 1px solid var(--border-strong, #C4CDD6);
    border-radius: 50%; color: var(--text-faint, #94A3B8);
    cursor: help;
}
.sd-kpi-value {
    font-size: 26px; font-weight: 700;
    color: var(--text-strong, #16202E);
    font-variant-numeric: tabular-nums;
    line-height: 1.3; margin: 4px 0;
}
.sd-kpi-value small { font-size: 13px; font-weight: 400; color: var(--text-muted, #64748B); }
.sd-kpi-compare { display: flex; align-items: center; gap: 8px; }
/* Карусель: горизонтальный скролл + стрелки; mobile видно 1.5 карточки */
.sd-kpi-track {
    display: flex; gap: 12px; overflow-x: auto;
    scroll-snap-type: x mandatory; padding-bottom: 4px;
}
.sd-kpi-track > * { flex: 0 0 220px; scroll-snap-align: start; }
@media (max-width: 576px) {
    .sd-kpi-track > * { flex-basis: 62%; }
}
.sd-kpi-nav { display: flex; gap: 4px; margin-left: auto; }
.sd-kpi-nav .btn { padding: 2px 10px; }

/* ============ 6. BarlistRow (Plausible: фон-полоса ∝ значению) ============ */
.sd-barlist { display: flex; flex-direction: column; gap: 4px; }
.sd-barlist-row {
    position: relative; display: flex; align-items: center; gap: 10px;
    padding: 7px 14px; font-size: 12px;
    border-radius: var(--radius-sm, 8px); overflow: hidden;
}
.sd-barlist-fill {
    position: absolute; inset: 0 auto 0 0;
    background: var(--brand-tint-12, rgba(0, 188, 212, .12));
    pointer-events: none;
}
.sd-barlist-row > * { position: relative; }
.sd-barlist-name { font-weight: 500; color: var(--text-strong, #16202E); flex: 1 1 auto; }
.sd-barlist-val { font-variant-numeric: tabular-nums; color: var(--text-body, #334155); }
.sd-barlist-ico { width: 20px; height: 20px; flex: 0 0 20px; }

/* ============ 7. Scorecard4 (Looker: 4 метрики в одной карточке) ============ */
.sd-score4 { display: grid; grid-template-columns: repeat(4, 1fr); gap: 0; }
.sd-score4-cell { padding: 12px 16px; border-left: 1px solid var(--border-subtle, #EDF1F5); }
.sd-score4-cell:first-child { border-left: 0; }
.sd-score4-ico {
    display: inline-flex; align-items: center; justify-content: center;
    width: 28px; height: 28px; border-radius: 8px;
    background: var(--info-bg, #EBF1FE); color: var(--info, #2563EB);
    font-size: 14px; margin-bottom: 6px;
}
.sd-score4-val { font-size: 22px; font-weight: 700; font-variant-numeric: tabular-nums; }
.sd-score4-cap { font-size: 11px; color: var(--text-muted, #64748B); text-transform: uppercase; letter-spacing: .04em; }
@media (max-width: 768px) { .sd-score4 { grid-template-columns: repeat(2, 1fr); } }

/* ============ 8. FunnelSteps-разметка S2 (без анимации — S5) ============ */
.sd-funnel-card { padding: 7px 14px; border: 1px solid var(--border-default, #DCE3EA); border-radius: 10px; }
.sd-funnel-card.sd-active {
    border-color: var(--brand, #00BCD4);
    background: var(--brand-tint-8, rgba(0, 188, 212, .08));
}
.sd-funnel-conv { font-size: 11px; color: var(--text-muted, #64748B); }
.sd-chevron-num {
    display: inline-flex; align-items: center; justify-content: center;
    width: 22px; height: 22px; border-radius: 50%;
    background: var(--brand, #00BCD4); color: #fff;
    font-size: 12px; font-weight: 700;
}

/* ============ 9. SalesBlocks: прогресс ВИКОНАННЯ + dot-bar КОНВЕРСІЯ ============ */
.sd-exec { display: flex; align-items: center; gap: 8px; min-width: 110px; }
.sd-exec .progress { flex: 1 1 auto; height: 8px; }
.sd-exec .progress-bar { background: var(--positive, #12A76D); }
.sd-dotbar { display: inline-flex; gap: 2px; }
.sd-dotbar i { width: 6px; height: 12px; border-radius: 2px; background: var(--border-default, #DCE3EA); }
.sd-dotbar i.sd-on { background: var(--brand, #00BCD4); }
.sd-mgr-row { cursor: pointer; }
.sd-planfact-neg { color: var(--err-text, #C2410C); font-weight: 500; }

/* ============ 10. Pagination (компонент dashboard/Pagination) ============ */
.sd-pager { display: flex; align-items: center; gap: 8px; padding: 10px 14px; font-size: 12px; color: var(--text-muted, #64748B); }
.sd-pager .pagination { margin: 0 0 0 auto; }
.sd-pager .page-link { font-size: 12px; min-width: 30px; text-align: center; }
.sd-pager .page-item.active .page-link { background: var(--brand, #00BCD4); border-color: var(--brand, #00BCD4); }

/* ============ 11. Mobile 390: строки складываются (data-label) ============ */
@media (max-width: 768px) {
    .sd-table-card.table-mobile-md thead { display: none; }
    .sd-table-card.table-mobile-md tbody tr {
        display: block; border-bottom: 2px solid var(--border-default, #DCE3EA);
        padding: 4px 0;
    }
    .sd-table-card.table-mobile-md tbody td {
        display: flex; justify-content: space-between; align-items: center;
        gap: 12px; border-bottom: 0; white-space: normal; text-align: right;
    }
    .sd-table-card.table-mobile-md tbody td::before {
        content: attr(data-label);
        font-size: 11px; text-transform: uppercase; letter-spacing: .04em;
        color: var(--text-muted, #64748B); text-align: left;
    }
    .sd-table-card.table-mobile-md tbody td.sd-label { font-size: 13px; }
    .sd-table-card.table-mobile-md tr.sd-total { background: var(--surface-sunken, #F8FAFC); }
    .sd-cohort td:first-child, .sd-cohort th:first-child, .sd-sticky-col { position: static; box-shadow: none; }
}
/* Dark-theme: поверхности из токенов S1, heat читается на темном */
html.dark-theme .sd-cohort td.sd-heat-1 { background: #173758; color: #E0F1FF; }
html.dark-theme .sd-cohort td.sd-heat-2 { background: #17497B; color: #E0F1FF; }
html.dark-theme .sd-cohort td.sd-heat-3 { background: #1F66AD; color: #fff; }
html.dark-theme .sd-cohort td.sd-heat-4 { background: #2F80D0; color: #fff; }
html.dark-theme .sd-table-card thead th,
html.dark-theme .sd-cohort th:first-child,
html.dark-theme .sd-sticky-col { background: var(--surface-sunken, #1a2430); }
/* === SD-FILTERS.CSS (S2) === */
/* НАЗНАЧЕНИЕ: ReportTabs, FilterBar (Канал/UTM/date-range/атрибуция),
   segmented-control, mobile offcanvas. Замеры 1:1 из strimix-full п.5+п.15:
   таб 13px/500/pad 9x16, активный #00BCD4/#fff/radius-top 8px, чип h=32px. */
/* СВЯЗИ: sd-tables.js (FilterBar.render, попапы), views/marketing.js (6 табов) */
/* ПРИЕМКА S2: п.3 (фильтры по спекам, Apply меняет данные), п.4 (offcanvas 390) */

/* ============ 1. ReportTabs: 6 кнопок + collapse ^^ ============ */
.sd-report-tabs {
    display: flex; align-items: flex-end; gap: 2px;
    border-bottom: 1px solid var(--border-default, #DCE3EA);
    overflow-x: auto; scrollbar-width: thin;
}
.sd-report-tabs .nav-link {
    font-size: 13px; font-weight: 500;
    padding: 9px 16px; /* замер 1:1 */
    color: var(--text-muted, #64748B);
    border-radius: 8px 8px 0 0; /* замер 1:1 */
    white-space: nowrap;
}
.sd-report-tabs .nav-link:hover { color: var(--text-strong, #16202E); }
.sd-report-tabs .nav-link.active {
    background: var(--brand, #00BCD4); /* замер 1:1 */
    color: var(--brand-contrast, #fff);
}
.sd-tabs-collapse {
    margin-left: auto; flex: 0 0 auto;
    border: 0; background: transparent;
    color: var(--text-faint, #94A3B8);
    font-size: 13px; padding: 9px 10px; cursor: pointer;
}
.sd-tabs-collapse:hover { color: var(--text-strong, #16202E); }
.sd-tabs-collapsed .sd-report-tabs .nav-link span.sd-tab-label { display: none; }

/* ============ 2. FilterBar: чип-кнопки h=32px ============ */
.sd-filterbar {
    display: flex; flex-wrap: wrap; gap: 8px; align-items: center;
    padding: 10px 0;
}
.sd-chip {
    display: inline-flex; align-items: center; gap: 6px;
    height: 32px; /* замер 1:1 */
    padding: 0 12px;
    font-size: 13px; font-weight: 500;
    color: var(--text-body, #334155);
    background: var(--surface-card, #fff);
    border: 1px solid var(--border-default, #DCE3EA);
    border-radius: var(--radius-sm, 8px);
    cursor: pointer; white-space: nowrap;
}
.sd-chip:hover { border-color: var(--border-strong, #C4CDD6); }
.sd-chip.sd-on {
    border-color: var(--brand, #00BCD4);
    background: var(--brand-tint-8, rgba(0, 188, 212, .08));
    color: var(--text-brand, #00838F);
}
.sd-chip .sd-chev { font-size: 10px; color: var(--text-faint, #94A3B8); }
.sd-chip .sd-attr-ico {
    display: inline-flex; align-items: center; justify-content: center;
    width: 18px; height: 18px; border-radius: 50%;
    background: var(--brand-tint-12, rgba(0, 188, 212, .12));
    color: var(--text-brand, #00838F); font-size: 11px; font-style: normal;
}
/* Попапы фильтров: поверх Tabler dropdown, ширина по контенту */
.sd-pop {
    min-width: 240px; max-width: 340px;
    padding: 10px 12px; font-size: 13px;
}
.sd-pop .form-check { margin: 6px 0; }
.sd-pop-search { margin-bottom: 8px; }
.sd-pop-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 10px; }
/* UTM-панель: 5 суб-дропдаунов */
.sd-utm-grid { display: flex; flex-direction: column; gap: 8px; min-width: 260px; }
.sd-utm-row { display: flex; align-items: center; gap: 8px; }
.sd-utm-row label { flex: 0 0 80px; color: var(--text-muted, #64748B); font-size: 12px; }
.sd-utm-row .form-select { flex: 1 1 auto; font-size: 13px; }
/* Date-range: двухпанельный попап (пресеты | периоды+календарь) */
.sd-dr-pop { gap: 0; min-width: 560px; max-width: 620px; padding: 0; }
.sd-dr-pop.show { display: flex; }
.sd-dr-presets {
    flex: 0 0 210px; border-right: 1px solid var(--border-subtle, #EDF1F5);
    padding: 10px; max-height: 340px; overflow-y: auto;
}
.sd-dr-presets .btn {
    display: block; width: 100%; text-align: left;
    font-size: 13px; padding: 5px 10px; border: 0; background: transparent;
    color: var(--text-body, #334155); border-radius: 6px;
}
.sd-dr-presets .btn:hover { background: var(--surface-sunken, #F8FAFC); }
.sd-dr-presets .btn.sd-on { background: var(--brand-tint-12, rgba(0, 188, 212, .12)); color: var(--text-brand, #00838F); }
.sd-dr-body { flex: 1 1 auto; padding: 12px 14px; font-size: 13px; }
.sd-dr-body .form-control { font-size: 13px; }
.sd-dr-sec { font-size: 11px; text-transform: uppercase; letter-spacing: .04em; color: var(--text-muted, #64748B); margin: 10px 0 6px; }
.sd-dr-compare { display: flex; gap: 8px; align-items: center; margin-top: 6px; }
@media (max-width: 768px) { .sd-dr-pop { min-width: 0; flex-direction: column; } .sd-dr-presets { border-right: 0; border-bottom: 1px solid var(--border-subtle, #EDF1F5); max-height: 180px; } }

/* ============ 3. Segmented-control (День/Тиждень/Місяць/Квартал/Рік, ABS/LOG) ============ */
.sd-segmented {
    display: inline-flex; gap: 2px;
    background: var(--surface-auth, #F1F5F9);
    border-radius: var(--radius-sm, 8px);
    padding: 2px;
}
.sd-segmented .btn {
    border: 0; background: transparent;
    font-size: 12px; font-weight: 500; padding: 4px 12px;
    color: var(--text-muted, #64748B); border-radius: 6px;
    box-shadow: none;
}
.sd-segmented .btn.sd-on, .sd-segmented .btn.active {
    background: var(--surface-card, #fff);
    color: var(--text-strong, #16202E);
    box-shadow: var(--shadow-xs, 0 1px 2px rgba(20, 32, 46, .05));
}

/* ============ 4. Mobile: фильтры в offcanvas-end ============ */
.sd-filters-toggle { display: none; }
@media (max-width: 768px) {
    .sd-filters-toggle { display: inline-flex; }
    .sd-filterbar.sd-in-offcanvas {
        flex-direction: column; align-items: stretch;
        padding: 0;
    }
    .sd-filterbar.sd-in-offcanvas .sd-chip { justify-content: space-between; width: 100%; }
    .sd-filterbar.sd-in-offcanvas .dropdown-menu {
        position: static !important; transform: none !important;
        border: 0; box-shadow: none; padding: 0 0 8px;
    }
    .sd-dr-pop { min-width: 0; }
}
/* Вкладки Канали/UTM и За місяцем/За каналом внутри карточек (Plausible-прием) */
.sd-card-tabs { display: flex; gap: 4px; }
.sd-card-tabs .btn {
    font-size: 12px; font-weight: 500; padding: 4px 12px;
    border-radius: var(--radius-pill, 999px);
    border: 1px solid transparent; background: transparent; color: var(--text-muted, #64748B);
}
.sd-card-tabs .btn.sd-on { background: var(--brand-tint-12, rgba(0, 188, 212, .12)); color: var(--text-brand, #00838F); }
.sd-expand-btn { border: 0; background: transparent; color: var(--text-faint, #94A3B8); padding: 4px 6px; cursor: pointer; }
.sd-expand-btn:hover { color: var(--text-strong, #16202E); }
/* === SD-CHARTS.CSS === */
/* НОВЫЙ ФАЙЛ S3: легенды/подписи/центры donut/высоты канвасов/воронка/сегменты. */
/* Контракт: легенда-точка 10x10/radius 3px/var(--viz-N); таб 13px/500; ячейка 12px. */
/* Подключает S1 одной строкой (см. PATCHES.md п.0): владелец index.php — S1. */

/* --- Слоты канвасов: фиксированная высота, Chart.js responsive тянется --- */
.sd-chart { position: relative; height: 260px; }
.sd-chart.tall { height: 320px; }
.sd-chart.short { height: 200px; }
.sd-chart.donut { height: 220px; }
.sd-chart canvas { width: 100% !important; height: 100% !important; }

/* --- HTML-легенда: кнопки-серии, точка 10x10 r3 --- */
.sd-legend { display: flex; flex-wrap: wrap; gap: 6px 14px; margin-top: 10px; }
.sd-legend-item {
    display: inline-flex; align-items: center; gap: 7px;
    background: transparent; border: 0; padding: 4px 6px; border-radius: 6px;
    font-size: 13px; font-weight: 500; line-height: 1.3;
    color: var(--text-body, var(--dim, #334155));
    cursor: pointer;
}
.sd-legend-item:hover { background: var(--surface-sunken, var(--surface-hover, #F8FAFC)); }
.sd-legend-item:focus-visible {
    outline: none;
    box-shadow: var(--ring-focus, 0 0 0 3px rgba(0, 188, 212, .25));
}
.sd-legend-item.off { opacity: .4; }
.sd-legend-item.off .sd-legend-dot { background: transparent !important; box-shadow: inset 0 0 0 2px #C4CDD6; }
.sd-legend-dot { width: 10px; height: 10px; border-radius: 3px; flex: none; }
.sd-legend-info {
    display: inline-flex; align-items: center; justify-content: center;
    width: 16px; height: 16px; border-radius: 999px;
    font-size: 10px; font-style: italic; font-weight: 700;
    color: var(--text-muted, var(--muted, #64748B));
    border: 1px solid var(--border-default, var(--border-strong, #C4CDD6));
    cursor: help;
}

/* --- Сегмент-переключатель (День/Тиждень/.../ABS/LOG) --- */
.sd-seg { display: inline-flex; gap: 2px; padding: 2px; border-radius: 8px;
    background: var(--surface-sunken, var(--surface-hover, #F1F5F9)); }
.sd-seg-btn {
    border: 0; background: transparent; border-radius: 6px;
    padding: 5px 12px; font-size: 13px; font-weight: 500;
    color: var(--text-muted, var(--muted, #64748B)); cursor: pointer;
    min-height: 32px;
}
.sd-seg-btn.active {
    background: var(--brand, var(--accent, #00BCD4));
    color: var(--brand-contrast, var(--on-accent, #fff));
}
.sd-seg-btn:focus-visible {
    outline: none;
    box-shadow: var(--ring-focus, 0 0 0 3px rgba(0, 188, 212, .25));
}

/* --- Кликабельная KPI-метрика (Plausible-прием S3-6) --- */
.sd-kpi-click { cursor: pointer; }
.sd-kpi-click:hover { box-shadow: var(--shadow-sm, 0 2px 6px rgba(20, 32, 46, .06)); }
.sd-kpi-click.active {
    box-shadow: inset 0 0 0 2px var(--brand, var(--accent, #00BCD4));
}
.sd-kpi-click:focus-visible {
    outline: none;
    box-shadow: var(--ring-focus, 0 0 0 3px rgba(0, 188, 212, .25));
}

/* --- Воронка: подпись конверсии между барами --- */
.sd-funnel-note { font-size: 12px; color: var(--text-muted, var(--muted, #64748B)); }

/* --- Mobile 390: легенда в столбец, канвас ниже --- */
@media (max-width: 576px) {
    .sd-chart { height: 220px; }
    .sd-chart.tall { height: 260px; }
    .sd-legend { gap: 4px 10px; }
}
/* === S4-MARKETING-SALES.CSS (wave-s4, NEW FILE) ===
 * НАЗНАЧЕНИЕ: S4-компоновка view-marketing + view-sales: панель вкладок,
 *   строка фильтров, KPI-лента, гриды 3-col/2-col/4-каналов, responsive 390px.
 * КОНТРАКТ: superdesign п.2 (токены --brand/--viz/--fs/--radius), п.3.3-3.4.
 * ГРАНИЦЫ: только s4-* селекторы. Селекторы потоков S2 и S3
 *   НЕ трогать. Цвета/шрифты - только var(--*) из S1.
 * Подключение: списком в S1 (index.php, владелец S1), см. PATCH-INSTRUCTIONS.md.
 */

/* --- Панель вкладок (замер 1:1: 13px/500/pad 9x16, активная teal-пилюля) --- */
.s4-tabsbar { display: flex; align-items: center; gap: 8px; margin-bottom: 12px; }
.s4-tabs { display: flex; flex-wrap: nowrap; overflow-x: auto; gap: 4px; flex: 1 1 auto; }
.s4-tabs .s4-tab {
    color: var(--text-muted); font-size: 13px; font-weight: 500;
    padding: 9px 16px; border-radius: var(--radius-sm); white-space: nowrap;
    background: transparent; border: 0;
}
.s4-tabs .s4-tab:hover { color: var(--text-body); background: var(--brand-tint-8); }
.s4-tabs .s4-tab.active { background: var(--brand); color: var(--brand-contrast); }
.s4-tabs .s4-tab:focus-visible { outline: 0; box-shadow: var(--ring-focus); }
.s4-collapse { flex: 0 0 auto; }
.s4-pane { display: none; }
.s4-pane.s4-on { display: block; }

/* --- Строка фильтров: чипы h=32px --- */
.s4-filterbar { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
.s4-chip {
    height: 32px; padding: 0 12px; font-size: 13px;
    background: var(--surface-card); border: 1px solid var(--border-default);
    border-radius: var(--radius-sm); color: var(--text-body);
    display: inline-flex; align-items: center; gap: 6px;
}
.s4-chip:hover { border-color: var(--border-strong); }
.s4-chip:focus-visible { outline: 0; box-shadow: var(--ring-focus); }
.s4-chev { color: var(--text-faint); font-size: 11px; }
.s4-dr { font-variant-numeric: tabular-nums; }
.s4-fnote { margin: -4px 0 12px; }

/* --- Заголовки панелей. 02.10: был 12px/700 uppercase серый (Strimix
   OVERLINE) - единственный экран с таким стилем. Приведен к общему
   заголовку карточки (overview): 16px/600, обычный регистр (AUDIT_02_10 п.2). --- */
.s4-pt {
    font-size: 16px; font-weight: 600;
    letter-spacing: -0.01em; text-transform: none;
    color: var(--text-strong, var(--text));
}
.s4-block {
    font-size: 16px; font-weight: 600; letter-spacing: -0.01em;
    text-transform: none; color: var(--text-strong, var(--text));
    margin: 20px 0 12px;
}
.s4-sect { margin-top: 16px; }

/* --- KPI-лента: на десктопе карточки делят ширину поровну (все 6 видны
   сразу, 01.10.2026 - было flex 0 0 210px и 6-я карточка резалась краем
   вьюпорта без подсказки про скролл). Скролл остается страховкой для
   узких ширин: ниже min-width ряд едет, а не давит цифры. --- */
.s4-kpis { display: flex; gap: 12px; overflow-x: auto; padding-bottom: 4px; margin-bottom: 4px; }
.s4-kpis .s4-kpi { flex: 1 1 160px; min-width: 150px; cursor: pointer; }
/* 01.10.2026: значения чуть плотнее - 26px/паддинг 20 резали "570 500 грн"
   (136px при 119px). 22px + паддинг 14 + gap 10 дают запас под "X,X млн грн",
   иерархия не страдает (овервью крупнее и остается доминантой). */
.s4-kpis { gap: 10px; }
.s4-kpis .tb3-kbi { padding: 14px; }
.s4-kpis .tb3-kval { font-size: 22px; }
/* 21st #15024: спарклайн тренда в KPI-карте. Нейтральный accent в обеих темах. */
.s4-spark { display: block; width: 100%; height: 26px; margin-top: 6px; }
.s4-spark-line { fill: none; stroke: var(--accent); stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
.s4-spark-area { fill: var(--accent); opacity: 0.12; stroke: none; }
.s4-kpis .s4-kpi:hover { border-color: var(--brand-tint-30); }
.s4-kpis .s4-kpi:focus-visible { outline: 0; box-shadow: var(--ring-focus); }
.s4-info { color: var(--text-faint); font-size: 11px; cursor: help; }
.s4-kpis-sales .s4-kpi { flex: 1 1 145px; min-width: 140px; }

/* --- Ряды: отступы секций --- */
.s4-row3, .s4-row2 { margin-top: 16px; }
.s4-chgrid { margin-top: 0; }

/* --- Карточки каналов --- */
.s4-chcard .card-stamp-icon { font-size: 22px; }
.s4-chrev { font-size: 22px; font-weight: 700; color: var(--text-strong); font-variant-numeric: tabular-nums; }

/* --- Канвасы: фиксированная высота (S3 sd-charts перекроет классы) --- */
.s4-chart { height: 240px; position: relative; }
.s4-chart-lg { height: 300px; position: relative; }
.s4-chart canvas, .s4-chart-lg canvas { width: 100% !important; height: 100% !important; }

/* --- Ссылка детального отчета --- */
.s4-detail { color: var(--text-link); font-size: 13px; display: inline-block; padding: 8px 16px 12px; }
.s4-detail:hover { color: var(--text-brand); }

/* --- Expand-кнопки карточек (Plausible) --- */
.s4-expand { margin-left: auto; }

/* --- Segmented День/Тиждень/.../Рік (S2 перекроет, база здесь) --- */
.s4-segmented { display: inline-flex; gap: 2px; margin-left: auto; }
.s4-segmented .btn { border-radius: 0; }
.s4-segmented .btn:first-child { border-radius: var(--radius-sm) 0 0 var(--radius-sm); }
.s4-segmented .btn:last-child { border-radius: 0 var(--radius-sm) var(--radius-sm) 0; }

/* --- Таблица менеджеров: выбор строки, прогресс, dot-bar --- */
tr[data-s4mgr] { cursor: pointer; }
tr[data-s4mgr]:hover { background: var(--brand-tint-8); }
tr[data-s4mgr].s4-sel { background: var(--brand-tint-12); }
tr[data-s4mgr]:focus-visible { outline: 0; box-shadow: inset var(--ring-focus); }
.s4-pbar { min-width: 90px; vertical-align: middle; }
.s4-pbar .progress-bar { background: var(--brand); }
.s4-dotbar { letter-spacing: 1px; color: var(--brand); font-size: 10px; }

/* --- Конверсия этапов: sticky ЭТАП + условное форматирование --- */
.s4-sticky {
    position: sticky; left: 0; z-index: 2;
    background: var(--surface-card) !important;
}
thead .s4-sticky { background: var(--surface-sunken) !important; }
.s4-cf-g { background: var(--positive-bg) !important; color: var(--positive); }
.s4-cf-y { background: var(--warning-bg) !important; color: var(--warn-text); }
.s4-cf-r { background: var(--negative-bg) !important; color: var(--err-text, #C2410C); }

/* --- Responsive: 390px без горизонтального скролла страницы --- */
@media (max-width: 576px) {
    .s4-kpis .s4-kpi { flex-basis: 62vw; }
    .s4-tabsbar { position: sticky; top: 0; z-index: 5; background: var(--surface-app); padding: 8px 0; }
    .s4-filterbar .s4-chip { flex: 1 1 auto; justify-content: center; }
    .s4-chart { height: 200px; }
    .s4-chart-lg { height: 240px; }
    .s4-segmented { flex-wrap: wrap; }
}
@media (prefers-reduced-motion: reduce) {
    .s4-tabs .s4-tab, .s4-chip, tr[data-s4mgr] { transition: none; }
}

/* --- Dark: шапка s4-таблиц (Bootstrap thead иначе белый на темной теме) --- */
html.dark-theme .s4-table thead th {
    background: var(--surface-card, #1E293B);
    color: var(--text-muted, #94A3B8);
    border-color: var(--border-default, #334155);
}
/* === SD-VIEWS.CSS (поток S5: остальные вьюхи) === */
/* Финальный путь: demo/assets/css/sd-views.css */
/* НАЗНАЧЕНИЕ: Strimix-компоненты 8 вьюх S5 (overview/goals/finance/accounting/ */
/*   operations/infrastructure/integrations/brain) по контракту /tmp/superdesign-contract.md */
/*   Карта вьюха->донор: контракт п.1, строки 40/41/44-49. */
/* СВЯЗИ: sd-s5.js (ставит .sd5-* классы и разметку), sd-mobile.css (390px), */
/*   variables.css (токены; Strimix-имена --viz/--brand берет у S1, ниже - */
/*   fallback-цепочки на --c-1..6/--ok/--warn/--err, чтобы файл работал и до S1). */
/* ГРАНИЦЫ: только .sd5-* селекторы + #brain-body рескин чата. Селекторы S2/S3/S4 */
/*   (sd-tables/sd-charts/sd-layout/tb-*) здесь НЕ переопределяются. */

/* ---------- 0. Алиасы токенов с fallback (S1 поверх, если уже есть) ---------- */
:root {
    --sd5-brand: var(--brand, var(--c-1));
    --sd5-brand-ink: var(--text-brand, var(--accent));
    --sd5-link: var(--text-link, var(--accent));
    --sd5-ok: var(--positive, var(--ok));
    --sd5-ok-bg: var(--positive-bg, var(--ok-soft));
    --sd5-err: var(--negative, var(--err));
    --sd5-err-bg: var(--negative-bg, var(--err-soft));
    --sd5-warn: var(--warn); /* 02.10: один янтарь системы (был --warning #EF9D17) */
    --sd5-warn-bg: var(--warning-bg, var(--warn-soft));
    --sd5-info-bg: var(--info-bg, var(--accent-soft));
    --sd5-card: var(--surface-card, var(--surface));
    --sd5-sunken: var(--surface-sunken, var(--surface-2));
    --sd5-line: var(--border-subtle, var(--border));
    --sd5-muted: var(--text-muted, var(--muted));
    --sd5-body: var(--text-body, var(--dim));
    --sd5-strong: var(--text-strong, var(--text));
    --sd5-v1: var(--viz-1, var(--c-1));
    --sd5-v2: var(--viz-2, var(--c-4));
    --sd5-v3: var(--viz-3, var(--c-3));
    --sd5-v4: var(--viz-4, var(--c-2));
    --sd5-v5: var(--viz-5, var(--c-6));
    --sd5-v6: var(--viz-6, var(--c-5));
}

/* ---------- 1. OVERVIEW: stat-панели с заливкой (Grafana-прием, п.1 строка 40) ---------- */
/* Четыре панели План/Факт/Виконання; заливка интенсивностью = % выполнения. */
.sd5-stats {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 12px;
    margin-bottom: 16px;
}
.sd5-stat {
    position: relative;
    overflow: hidden;
    border: 1px solid var(--sd5-line);
    border-radius: var(--radius-card, var(--r-3));
    background: var(--sd5-card);
    padding: 12px 14px 10px;
    box-shadow: var(--shadow-xs, var(--shadow));
}
.sd5-stat-fill {
    position: absolute;
    inset: 0 auto 0 0;
    width: 0;
    background: var(--sd5-info-bg);
    pointer-events: none;
}
.sd5-stat.is-ok .sd5-stat-fill { background: var(--sd5-ok-bg); }
.sd5-stat.is-warn .sd5-stat-fill { background: var(--sd5-warn-bg); }
.sd5-stat.is-alert .sd5-stat-fill { background: var(--sd5-err-bg); }
.sd5-stat > * { position: relative; }
.sd5-stat-label {
    font-size: var(--fs-overline, 11px);
    font-weight: 700;
    letter-spacing: var(--ls-overline, .06em);
    text-transform: uppercase;
    color: var(--sd5-muted);
}
.sd5-stat-value {
    font-size: var(--fs-h4, 18px);
    font-weight: 700;
    color: var(--sd5-strong);
    font-variant-numeric: var(--tnum, tabular-nums);
    line-height: 1.3;
    margin-top: 2px;
}
.sd5-stat-sub {
    font-size: var(--fs-caption, 12px);
    color: var(--sd5-muted);
    margin-top: 2px;
    font-variant-numeric: var(--tnum, tabular-nums);
}
.sd5-stat-sub b { color: var(--sd5-body); }
.sd5-stat.is-ok .sd5-stat-sub b { color: var(--sd5-ok); }
.sd5-stat.is-warn .sd5-stat-sub b { color: var(--warn-text); }
.sd5-stat.is-alert .sd5-stat-sub b { color: var(--err-text); }

/* ---------- 2. GOALS: hbar на сером треке 100% (таб 6 блок 2, п.1 строка 41) ---------- */
/* Трек рисует второй (серый) датасет stacked-бара из sd-s5.js; здесь рамка/подпись. */
.sd5-goals-chart { position: relative; height: 220px; }
.sd5-goals-chart > canvas { max-width: 100%; }
.sd5-kpi-strip {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 12px;
    margin-bottom: 16px;
}
/* KPI-полоса с дельтами (Plausible-прием): компактнее .tb4-kpi, дельта та же. */
.sd5-kpi {
    border: 1px solid var(--sd5-line);
    border-radius: var(--radius-md, var(--r-2));
    background: var(--sd5-card);
    padding: 10px 12px;
}
.sd5-kpi-label {
    font-size: var(--fs-overline, 11px);
    font-weight: 700;
    letter-spacing: var(--ls-overline, .06em);
    text-transform: uppercase;
    color: var(--sd5-muted);
}
.sd5-kpi-value {
    font-size: var(--fs-h3, 22px);
    font-weight: 700;
    color: var(--sd5-strong);
    font-variant-numeric: var(--tnum, tabular-nums);
    line-height: 1.3;
}
.sd5-kpi-foot {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 4px;
    font-size: var(--fs-caption, 12px);
    color: var(--sd5-muted);
}

/* ---------- 3. FINANCE: scorecard 4-в-1 (Looker, контракт п.3.16) ---------- */
.sd5-score4 {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 0;
    border: 1px solid var(--sd5-line);
    border-radius: var(--radius-card, var(--r-3));
    background: var(--sd5-card);
    overflow: hidden;
    margin-bottom: 16px;
    box-shadow: var(--shadow-xs, var(--shadow));
}
.sd5-score4-cell { padding: 12px 14px; }
.sd5-score4-cell + .sd5-score4-cell { border-left: 1px solid var(--sd5-line); }
.sd5-score4-top { display: flex; align-items: center; gap: 8px; }
.sd5-score4-ico {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border-radius: 8px;
    background: var(--sd5-info-bg);
    color: var(--sd5-brand-ink);
    flex: 0 0 auto;
}
.sd5-score4-ico .ico { width: 16px; height: 16px; }
.sd5-score4-label {
    font-size: var(--fs-overline, 11px);
    font-weight: 700;
    letter-spacing: var(--ls-overline, .06em);
    text-transform: uppercase;
    color: var(--sd5-muted);
}
.sd5-score4-value {
    font-size: var(--fs-h4, 18px);
    font-weight: 700;
    color: var(--sd5-strong);
    font-variant-numeric: var(--tnum, tabular-nums);
    margin-top: 4px;
}
.sd5-score4-delta { margin-top: 2px; font-size: var(--fs-caption, 12px); }
/* Зеленая/красная дельта под каждой метрикой (Looker-прием, без пилюли). */
.sd5-delta-up { color: var(--sd5-ok); font-weight: 700; }
.sd5-delta-down { color: var(--err-text); font-weight: 700; }
.sd5-delta-flat { color: var(--sd5-muted); font-weight: 700; }
.sd5-was { color: var(--sd5-muted); font-size: var(--fs-caption, 12px); }

/* ---------- 4. FINANCE: heatmap-ячейки P&L/CF (Grafana, п.1 строка 44) ---------- */
/* Классы ставит sd-s5.js по нормализованной величине; 5 ступеней + отрицательные. */
.sd5-ht-0 { background-color: transparent; }
.sd5-ht-1 { background-color: color-mix(in oklab, var(--sd5-v1) 7%, transparent); }
.sd5-ht-2 { background-color: color-mix(in oklab, var(--sd5-v1) 13%, transparent); }
.sd5-ht-3 { background-color: color-mix(in oklab, var(--sd5-v3) 13%, transparent); }
.sd5-ht-4 { background-color: color-mix(in oklab, var(--sd5-v3) 22%, transparent); }
.sd5-ht-neg-1 { background-color: color-mix(in oklab, var(--sd5-err) 6%, transparent); }
.sd5-ht-neg-2 { background-color: color-mix(in oklab, var(--sd5-err) 12%, transparent); }
.sd5-ht-neg-3 { background-color: color-mix(in oklab, var(--sd5-err) 18%, transparent); }

/* ---------- 5. ACCOUNTING: барлисты (Plausible, п.1 строка 45) ---------- */
/* Фон-полоса шириной ∝ % проведения; ставит sd-s5.js инлайном, цвет - токен. */
.sd5-barline { position: relative; }
.sd5-total-row > td {
    background: var(--sd5-sunken);
    font-weight: 700;
    border-top: 2px solid var(--border-default, var(--border-strong, var(--sd5-line)));
}
.sd5-total-row .tb4-cell-main,
.sd5-total-row .tb5-cell-main { font-weight: 700; }

/* ---------- 6. OPERATIONS: donut-центр + stacked-запасы ---------- */
/* Центр кольца - оверлей поверх canvas (Charts.donut опций центра не дает). */
.sd5-donut-wrap { position: relative; }
.sd5-donut-center {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    pointer-events: none;
    padding: 0 24%;
}
.sd5-donut-center b {
    font-size: var(--fs-h4, 18px);
    color: var(--sd5-strong);
    font-variant-numeric: var(--tnum, tabular-nums);
    line-height: 1.2;
}
.sd5-donut-center span {
    font-size: var(--fs-caption, 12px);
    color: var(--sd5-muted);
}
.sd5-stock-chart { position: relative; height: 180px; }

/* ---------- 7. INFRASTRUCTURE: карточки-фильтры (таб 3, п.1 строка 47) ---------- */
/* Аналог КАНАЛИ ТРАФІКУ: клик фильтрует пины карты. Выбранная - рамка+заливка. */
.sd5-if-filters {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 8px;
    margin-bottom: 12px;
}
.sd5-if-filter {
    display: flex;
    align-items: center;
    gap: 8px;
    text-align: left;
    border: 1px solid var(--sd5-line);
    border-radius: var(--radius-md, var(--r-2));
    background: var(--sd5-card);
    padding: 8px 10px;
    cursor: pointer;
    color: var(--sd5-body);
    font-size: var(--fs-sm, 13px);
    line-height: 1.3;
}
.sd5-if-filter:hover { border-color: var(--border-default, var(--sd5-brand)); }
.sd5-if-filter.active {
    border-color: var(--sd5-brand);
    background: var(--brand-tint-8, var(--sd5-info-bg));
}
.sd5-if-filter b {
    font-variant-numeric: var(--tnum, tabular-nums);
    color: var(--sd5-strong);
}
.sd5-if-filter .sd5-if-sub { display: block; font-size: var(--fs-caption, 12px); color: var(--sd5-muted); }
.sd5-if-dot { width: 10px; height: 10px; border-radius: 3px; flex: 0 0 auto; }
.sd5-if-dot.is-ok { background: var(--sd5-ok); }
.sd5-if-dot.is-warn { background: var(--sd5-warn); }
.sd5-if-dot.is-alert { background: var(--sd5-err); }
/* Отфильтрованные пины гаснут, а не исчезают: карта не прыгает. */
.infra-pin.sd5-dim { opacity: .15; pointer-events: none; }
.infra-pin { transition: opacity var(--dur-base, 200ms) var(--ease-standard, ease); }

/* ---------- 8. INTEGRATIONS: таксономия + мини-карточки (таб 1 блок 5) ---------- */
/* Umami-прием: секции Traffic/Behavior/Growth маппятся на Джерела/Система/Дашборди. */
.sd5-taxonomy {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: var(--fs-overline, 11px);
    font-weight: 700;
    letter-spacing: var(--ls-overline, .06em);
    text-transform: uppercase;
    color: var(--sd5-brand-ink);
    background: var(--brand-tint-8, var(--sd5-info-bg));
    border-radius: var(--radius-pill, 999px);
    padding: 3px 10px;
    margin-bottom: 8px;
}
.card > .sd5-taxonomy-card {
    margin: 12px 16px 0;
    align-self: flex-start;
}
.sd5-ig-cards {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 12px;
    margin-bottom: 16px;
}
.sd5-ig-card {
    border: 1px solid var(--sd5-line);
    border-radius: var(--radius-md, var(--r-2));
    background: var(--sd5-card);
    padding: 10px 12px;
    min-width: 0;
}
.sd5-ig-card-top { display: flex; align-items: center; gap: 8px; min-width: 0; }
.sd5-ig-card-top .ico { width: 18px; height: 18px; flex: 0 0 auto; color: var(--sd5-brand-ink); }
.sd5-ig-card-name {
    font-weight: 700;
    font-size: var(--fs-body, 14px);
    color: var(--sd5-strong);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.sd5-ig-card-sub { font-size: var(--fs-caption, 12px); color: var(--sd5-muted); margin-top: 2px; }
.sd5-ig-card-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    margin-top: 6px;
    font-size: var(--fs-caption, 12px);
    color: var(--sd5-muted);
}

/* ---------- 9. BRAIN: рескин чата в токенах Strimix (п.1 строка 49) ---------- */
/* Логику chat.js не трогаем: только цвета/радиусы/типографика поверх .chat/.msg. */
#brain-body .chat {
    border: 1px solid var(--sd5-line);
    border-radius: var(--radius-card, var(--r-3));
    background: var(--sd5-card);
    box-shadow: var(--shadow-xs, var(--shadow));
    overflow: hidden;
}
#brain-body .chat-log {
    background: var(--sd5-card);
    max-height: min(56vh, 520px);
    overflow-y: auto;
}
#brain-body .msg {
    border-radius: var(--radius-md, var(--r-2));
    font-size: var(--fs-body, 14px);
    line-height: var(--lh-body, 1.5);
}
#brain-body .msg-a {
    background: var(--sd5-sunken);
    border: 1px solid var(--sd5-line);
}
#brain-body .msg-q {
    background: var(--brand-tint-8, var(--sd5-info-bg));
    border: 1px solid var(--brand-tint-30, var(--sd5-line));
}
#brain-body .msg-sender {
    font-size: var(--fs-overline, 11px);
    letter-spacing: var(--ls-overline, .06em);
    text-transform: uppercase;
    color: var(--sd5-muted);
}
#brain-body .msg-a .msg-sender { color: var(--sd5-brand-ink); }
#brain-body .chat-input {
    background: var(--sd5-card);
    border-top: 1px solid var(--sd5-line);
}
#brain-body .chat-field {
    border: 1px solid var(--border-default, var(--sd5-line));
    border-radius: var(--radius-md, var(--r-2));
    font-size: var(--fs-body, 14px);
}
#brain-body .chat-field:focus {
    outline: none;
    border-color: var(--sd5-brand);
    box-shadow: var(--ring-focus, 0 0 0 3px rgba(0, 188, 212, .25));
}
#brain-body .br-intro { padding: 12px 16px 0; }
#brain-body #br-suggests { padding: 0 16px 8px; }

/* ---------- 10. Expand-кнопки карточек (Plausible-прием, сквозной) ---------- */
.sd5-expand {
    margin-left: auto;
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border: 1px solid transparent;
    border-radius: 6px;
    background: transparent;
    color: var(--sd5-muted);
    cursor: pointer;
    font-size: 14px;
    line-height: 1;
}
.sd5-expand:hover { background: var(--surface-hover, var(--sd5-sunken)); color: var(--sd5-strong); }
/* Шапка бывает и строкой (Tabler row), и колонкой (.tb2-card-head): кнопку */
/* ставим абсолютом в правый верхний угол - раскладку шапки не трогаем. */
#overview-body .card-header, #goals-body .card-header, #finance-body .card-header,
#accounting-body .card-header, #operations-body .card-header,
#infrastructure-body .card-header, #integrations-body .card-header { position: relative; }
.card-header > .sd5-expand {
    position: absolute;
    top: 8px;
    right: 8px;
    margin: 0;
}
.sd5-expand:focus-visible { outline: 2px solid var(--sd5-brand); outline-offset: 1px; }
.card.sd5-expanded {
    position: fixed;
    inset: 16px;
    z-index: 120;
    overflow: auto;
    box-shadow: var(--shadow-xl, var(--shadow-2));
}
body.sd5-lock { overflow: hidden; }
.sd5-backdrop {
    position: fixed;
    inset: 0;
    z-index: 110;
    background: rgba(20, 32, 46, .45);
}

/* ---------- 11. Общее: фокус и reduced-motion ---------- */
.sd5-if-filter:focus-visible,
.sd5-ig-card:focus-visible {
    outline: 2px solid var(--sd5-brand);
    outline-offset: 1px;
}
@media (prefers-reduced-motion: reduce) {
    .infra-pin { transition: none; }
    .card.sd5-expanded { inset: 16px; }
}
/* === SD-MOBILE.CSS (поток S5: 390px адаптив 8 вьюх) === */
/* Финальный путь: demo/assets/css/sd-mobile.css */
/* НАЗНАЧЕНИЕ: контракт п.S4-приемка 2-3 для вьюх S5: гриды 3-col->1-col, */
/*   4-каналов->2->1, 2-col->1 на 390px без горизонтального скролла страницы */
/*   (скролл только внутри .table-responsive / .tb5-ig-schema); сайдбар 232px */
/*   desktop / drawer-панель <992px; мобильные таблицы через Tabler */
/*   .table-mobile-md (класс + data-label ставит sd-s5.js). */
/* СВЯЗИ: sd-views.css (десктоп .sd5-*), sd-s5.js, index.php (.tb1-side/.tb1-body). */
/* ГРАНИЦЫ: только @media-блоки + .sd5-* адаптив. Десктоп-правила чужих */
/*   селекторов (tb-*, .chat вне @media) здесь ЗАПРЕЩЕНЫ. */

/* ---------- 1. Планшет <992px: сайдбар-drawer (Tabler collapse - панель) ---------- */
@media (max-width: 991.98px) {
    /* Tabler .navbar-expand-lg складывает .navbar-collapse; делаем из него */
    /* аккуратную выпадающую панель, а не голый список во всю ширину. */
    .tb1-side { position: sticky; top: 0; z-index: 100; }
    .tb1-side .navbar-collapse {
        position: absolute;
        top: 100%;
        left: 8px;
        right: 8px;
        max-height: min(70vh, 560px);
        overflow-y: auto;
        background: var(--surface-card, var(--surface));
        border: 1px solid var(--border-subtle, var(--border));
        border-radius: var(--radius-card, 10px);
        box-shadow: var(--shadow-lg, var(--shadow-2));
        padding: 8px;
    }
    .tb1-side .tb1-tabs { display: flex; flex-direction: column; gap: 2px; }
    .tb1-body { padding-left: 12px; padding-right: 12px; }
    /* Сетки S5: 4 колонки складываются в 2 уже на планшете. */
    .sd5-stats,
    .sd5-kpi-strip,
    .sd5-ig-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .sd5-score4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .sd5-score4-cell:nth-child(3) { border-left: 0; }
    .sd5-score4-cell:nth-child(n+3) { border-top: 1px solid var(--border-subtle, var(--border)); }
    .sd5-if-filters { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ---------- 2. Телефон <=576px: 390px без горизонтального скролла ---------- */
@media (max-width: 576px) {
    /* Каркас: узкие поля, ничего шире вьюпорта. min-width:0 рвет цепочку */
    /* минимальных ширин flex/grid-детей (таблица 613px при экране 390). */
    .tb1-body { padding-left: 8px; padding-right: 8px; min-width: 0; }
    .tb1-main { min-width: 0; }
    .tb1-main > .view { min-width: 0; }
    .card, .card-body, .card-header, .table-responsive { min-width: 0; max-width: 100%; }
    .page-head { margin-bottom: 12px; }
    .page-head .page-title { font-size: 20px; }
    .page-head .page-sub { font-size: 12px; }

    /* Сетки S5: все в одну колонку (контракт: 3-col->1, 4->2->1, 2->1). */
    .sd5-stats,
    .sd5-kpi-strip,
    .sd5-ig-cards,
    .sd5-if-filters { grid-template-columns: minmax(0, 1fr); }
    .sd5-score4 { grid-template-columns: minmax(0, 1fr); }
    .sd5-score4-cell + .sd5-score4-cell { border-left: 0; border-top: 1px solid var(--border-subtle, var(--border)); }

    /* KPI-ряды Bootstrap col-sm-6: ниже sm они и так в 1 колонку; */
    /* ужимаем внутренние отступы, чтобы цифра не переносилась. */
    .tb4-kpi .card-body,
    .tb5-kpi .card-body { padding: 10px 12px; }
    .tb4-kpi-value,
    .tb5-kpi-value { font-size: 20px; white-space: nowrap; }

    /* Таблицы: кегль и паддинги по замеру Strimix (12px/7x14), */
    /* липкая первая колонка уже, чтобы увидеть 2-3 колонки данных. */
    #finance-body .table,
    #accounting-body .table,
    #operations-body .table { font-size: 12px; }
    #finance-body .table th, #finance-body .table td,
    #accounting-body .table th, #accounting-body .table td,
    #operations-body .table th, #operations-body .table td { padding: 7px 10px; }
    .tb4-stick { max-width: 150px; }
    .tb4-cell-main { white-space: normal; }
    /* P&L/CF остаются горизонтальным скроллом внутри карточки (исключение */
    /* из table-mobile: там 7 месячных колонок, карточная раскладка их убьет). */
    #fin-pnl-stmt .table-responsive,
    #fin-cf-stmt .table-responsive { overflow-x: auto; -webkit-overflow-scrolling: touch; }

    /* Цели: инпут плана и прогресс уже (дополняет v2.css 576-блок). */
    .tb2-goals td:first-child { max-width: 130px; }
    .tb2-note { font-size: 12px; }

    /* Канвасы: фиксированные высоты меньше, donut-центр компактнее. */
    .tb2-chart { height: 190px; }
    .tb2-chart-sm { height: 150px; }
    .tb5-chart { height: 200px; }
    .sd5-goals-chart { height: 190px; }
    .sd5-stock-chart { height: 150px; }
    .sd5-donut-center { padding: 0 30%; }
    .sd5-donut-center b { font-size: 15px; }

    /* Инфраструктура: карта ниже, подписи пинов прячем (тайтл остается). */
    .infra-map { min-height: 220px; }
    .infra-pin .map-pin-label { display: none; }
    .tb5-ses-row { font-size: 13px; }
    .tb5-cam-grid { grid-template-columns: minmax(0, 1fr); }

    /* Интеграции: схема скроллится внутри карточки (min-width 640 уже в v5). */
    .tb5-ig-schema { margin: 0 -12px; padding: 0 12px; }
    .tb5-ig-levels { display: flex; width: 100%; }
    .tb5-ig-levels .btn { flex: 1 1 0; padding-left: 4px; padding-right: 4px; font-size: 13px; }
    .tb5-ig-bar { flex-direction: column; align-items: stretch; gap: 8px; }

    /* Джин: лог ниже, поле и кнопка в столбец (дополняет components 560). */
    #brain-body .chat-log { max-height: 48vh; padding: 12px; }
    #brain-body .msg { font-size: 13px; }
    #brain-body .br-intro { font-size: 12px; }

    /* Плавающая навигация не перекрывает контент на узком экране. */
    .float-nav { right: 8px; bottom: 12px; }
    .float-nav button { width: 36px; height: 36px; }

    /* Expand-оверлей почти во весь экран телефона. */
    .card.sd5-expanded { inset: 8px; }
}

/* ---------- 3. Очень узкие <=360px: страховка от переносов цифр ---------- */
@media (max-width: 360px) {
    .tb4-kpi-value,
    .tb5-kpi-value,
    .sd5-kpi-value,
    .sd5-stat-value { font-size: 17px; }
    .tb4-stick { max-width: 120px; }
    .sd5-score4-cell { padding: 10px 12px; }
}

/* ---------- 4. Тач: кликабельные зоны не меньше 40px ---------- */
@media (pointer: coarse) {
    .sd5-if-filter { min-height: 44px; }
    .sd5-expand { width: 32px; height: 32px; }
    .tb5-ig-levels .btn { min-height: 40px; }
}
/* === W1-LAYOUT.CSS === */
/* demo/assets/css/w1-layout.css */
/* НАЗНАЧЕНИЕ: волна 1 полировки 02.10 (AUDIT_02_10, сквозные пункты):
   единая шапка экрана, единый заголовок карточки, сетки без провалов. */
/* СВЯЗИ: views.js pageHead() (разметка шапки), sources.js line() (чип источников).
   Подключается в index.php перед nd-accents.css. Цвет здесь только из токенов:
   палитру ведет Muse (variables.css, b-color.css). */
/* РАЗМЕР: ~100 строк */

/* ---------- 1. Шапка экрана: название + период слева, источники справа ----------
   Было: h2 скрыт (дублировал вкладку топбара, а топбар снят 30.09), период и
   источники стояли в теле каждого экрана в своем порядке. */
.tb1-main .page-head {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 6px 16px;
    margin: 4px 0 14px;
}

.page-head-main {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.page-head .page-title {
    position: static;
    width: auto;
    height: auto;
    margin: 0;
    padding: 0;
    overflow: visible;
    clip: auto;
    clip-path: none;
    white-space: normal;
    font-family: var(--font-display, inherit);
    font-size: 24px;
    font-weight: 650;
    line-height: 1.2;
    letter-spacing: -0.015em;
    color: var(--text-strong, var(--text));
}

.page-head .page-title::before {
    content: none;
}

.page-period {
    font-size: 13px;
    color: var(--muted);
}

.page-period b {
    font-weight: 600;
    color: var(--text);
}

/* Чип источников: одна строка, точка статуса, мелко - это справка, не показатель */
.page-head .src-line {
    margin: 0 !important;
    padding: 4px 10px;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--surface, transparent);
    font-size: 12px;
}

/* Точка статуса - не разделитель: у первого текста после нее точку-разделитель
   не рисуем (было "● · 1С") */
.src-line > .src-dot + span::before {
    content: none;
}

/* ---------- 2. Заголовок карточки: один стиль на все экраны ----------
   Было три: обычный 16/600 (overview, finance), мелкий серый капс 12/700
   (.s4-pt marketing), капс с разрядкой (.tb5-card-title infra/integrations).
   Эталон - overview: 16px, 600, обычный регистр. .s4-pt/.s4-block правлены
   в источнике (s4-marketing-sales.css, там был !important). */
.tb1-main .card .card-title.tb5-card-title {
    font-size: 16px;
    font-weight: 600;
    text-transform: none;
    letter-spacing: -0.01em;
    color: var(--text-strong, var(--text));
}

/* ---------- 3. Плавающая навигация: одна "наверх", только после прокрутки ----------
   Было: ↑↓ всегда поверх правого нижнего угла и перекрывали статусы/цифры.
   Видимость - класс is-shown из motion.js. "Вниз" скрыта: у дашборда она
   не нужна, вниз и так листают колесом. */
.float-nav {
    opacity: 0;
    visibility: hidden;
    transform: translateY(8px);
    transition: opacity 0.2s ease, transform 0.2s ease, visibility 0s linear 0.2s;
    border-radius: 999px;
}
.float-nav.is-shown {
    opacity: 1;
    visibility: visible;
    transform: none;
    transition: opacity 0.2s ease, transform 0.2s ease;
}
.float-nav #float-down {
    display: none;
}
.float-nav button {
    width: 40px;
    height: 40px;
}

/* Иконка разворота .sd5-expand стоит абсолютом в углу шапки (sd-views.css)
   и наезжала на подзаголовок справа ("СЕС", "30 діб"). Резервируем место. */
.card-header:has(> .sd5-expand) {
    padding-right: 44px;
}

/* ---------- 4. Сетки без провалов ----------
   Короткая колонка рядом с длинной: липкая, а не растянутая пустая карточка
   (finance: Баланс рядом с P&L + рух грошей). */
@media (min-width: 992px) {
    .w1-sticky,
    .tb4-ac-cols > .card:first-child {
        position: sticky;
        top: 12px;
    }
}

/* ---------- 5. Индикатор: название, вывод одной фразой, разбор по клику ----------
   Разметка - app.js renderIndicators/indSplit. Цвет уровня (точка, полоса,
   число) не трогаем - b-color.css, зона Muse. */
.ind-name {
    display: block;
}
.ind .ind-lead {
    color: var(--text);
}
.ind-more {
    margin-top: 2px;
}
.ind-more > summary {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 12px;
    color: var(--muted);
    cursor: pointer;
    list-style: none;
}
.ind-more > summary::-webkit-details-marker {
    display: none;
}
.ind-more > summary::after {
    content: '';
    width: 6px;
    height: 6px;
    border-right: 1.5px solid currentColor;
    border-bottom: 1.5px solid currentColor;
    transform: rotate(45deg) translateY(-2px);
    transition: transform 0.15s ease;
}
.ind-more[open] > summary::after {
    transform: rotate(-135deg) translateY(-1px);
}
.ind-more > summary:hover {
    color: var(--text);
}
.ind-more > summary:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: 4px;
}

/* ---------- 6. Маркетинг: легенда доната и карточки каналов (волна 2) ---------- */
.s4-donut-key {
    display: flex;
    flex-direction: column;
    gap: 4px;
    margin-top: 10px;
}
.s4-key-row {
    display: grid;
    grid-template-columns: 10px 1fr auto;
    align-items: center;
    gap: 8px;
    font-size: 13px;
}
.s4-key-dot {
    width: 10px;
    height: 10px;
    border-radius: 3px;
}
.s4-key-name { color: var(--text); }
.s4-key-val {
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    color: var(--text-strong, var(--text));
}

/* Все каналы в ряд: ширина считается из количества, не хардкод 4 колонок */
.s4-chgrid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
    gap: 10px;
}
.s4-chcol { min-width: 0; }
.s4-chcard { height: 100%; }
.s4-chname {
    display: flex;
    align-items: center;
    gap: 8px;
}
.s4-chname .ico {
    width: 18px;
    height: 18px;
    flex: 0 0 auto;
}
.s4-chcard .datagrid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 8px 12px;
    margin-top: 10px;
}

/* ---------- 7. Бухгалтерия: проблемный документ одной ячейкой ---------- */
.tb4-doc {
    min-width: 260px;
    white-space: normal;
}
.card:not(.is-docs-open) .tb4-row-more {
    display: none;
}
.tb4-more-foot {
    display: flex;
    justify-content: center;
    padding: 8px;
}
.tb4-doc-issue {
    margin-top: 4px;
    font-size: 13px;
    line-height: 1.45;
    color: var(--text);
    max-width: 62ch;
}

/* ---------- 8. Огляд: вывод над графиком план-факт ---------- */
.tb2-insight {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 2px 4px;
    margin: 0 0 8px;
    padding: 6px 10px;
    border-left: 3px solid var(--border-strong, var(--border));
    border-radius: 0 6px 6px 0;
    background: var(--surface-2, transparent);
    font-size: 13px;
    color: var(--muted);
}
.tb2-insight b { color: var(--text-strong, var(--text)); font-weight: 600; }
.tb2-insight.is-ok { border-left-color: var(--ok); }
.tb2-insight.is-warn { border-left-color: var(--warn); }
.tb2-insight.is-alert { border-left-color: var(--err); }
.tb2-insight.is-alert b:last-child { color: var(--err-text); }

/* ---------- 8b. Продажи: KPI 2x2 заполняют высоту рядом с "Динамікою" ----------
   Колонка тянулась до высоты графика (373px), карточки нет - лишнее уходило
   щелью ~53px между рядами, карточки разной высоты (150/132). */
#sl-overview > .row > [class*="col-"] > .row > [class*="col-"] > .card {
    height: 100%;
}

/* ---------- 8c. Финансы/бухгалтерия: ровный ряд KPI, P&L и Баланс без переносов ----------
   Было: KPI 140/158/132/120px ("4 разных низа"); P&L "Валовий / прибуток"
   в две строки; Баланс двумя узкими колонками по ~180px - "Грошові / кошти". */
#finance-body .row.row-cards > .col-lg-3 > .card,
#accounting-body .row.row-cards > .col-lg-3 > .card {
    height: 100%;
}
/* nowrap выталкивал "Вересень" за край (6 месяцев + статья не влезают в
   col-lg-8) - поэтому минимум ширины статьи + чуть уже отступы цифр:
   короткие статьи в одну строку, самая длинная может перенестись. */
#fin-pnl-stmt th:first-child,
#fin-pnl-stmt td:first-child,
#fin-cf-stmt th:first-child,
#fin-cf-stmt td:first-child {
    min-width: 160px;
}
#fin-pnl-stmt th:not(:first-child),
#fin-pnl-stmt td:not(:first-child),
#fin-cf-stmt th:not(:first-child),
#fin-cf-stmt td:not(:first-child) {
    padding-left: 8px;
    padding-right: 8px;
}
@media (min-width: 992px) {
    /* Баланс в колонке col-lg-4: активы и пассивы друг под другом */
    .tb4-bal-grid > .col-md-6 {
        flex: 0 0 100%;
        max-width: 100%;
    }
    .tb4-bal-row > span {
        white-space: nowrap;
    }
}

/* ---------- 9. Джин: нижний край лога затуханием, а не срезанной строкой ---------- */
#brain-body #br-log {
    padding-bottom: 28px;
    -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 28px), transparent);
    mask-image: linear-gradient(to bottom, #000 calc(100% - 28px), transparent);
}

/* ---------- 10. Телефон (QA 02.10, скрины q_M_*) ---------- */
@media (max-width: 575.98px) {
    /* Индикатор: число и норма под текстом, тексту - вся ширина
       (было: моно-число справа занимало полкарточки, текст по 2-3 слова) */
    .ind {
        grid-template-columns: auto minmax(0, 1fr);
    }
    .ind-num {
        grid-column: 2;
        flex-direction: row;
        align-items: baseline;
        justify-content: flex-start;
        gap: 8px;
    }
    /* Цілі: план "18 200 000" целиком (96px резал до "18 200 00") */
    .tb2-input {
        width: 116px;
        padding-left: 6px;
        padding-right: 6px;
        font-size: 13px;
    }
    /* Интеграции: схема шире экрана, прокрутка была без подсказки */
    .tb5-ig-schema::before {
        content: 'Гортайте схему вбік →';
        display: block;
        margin-bottom: 6px;
        font-size: 12px;
        color: var(--muted);
    }
    /* Джин: окно чата выше, ответ с плитками помещается */
    #brain-body #br-log {
        max-height: 68vh;
    }
}

/* Маркетинг на телефоне: строка таблицы = компактная карточка (канал сверху,
   метрики сеткой 2x2 с подписями data-label), а не 5 строк по 40px */
@media (max-width: 767.98px) {
    .s4-table.table-mobile-md tbody tr {
        display: grid;
        grid-template-columns: 1fr 1fr;
        gap: 6px 12px;
        padding: 10px 12px;
        border-bottom: 1px solid var(--border);
    }
    .s4-table.table-mobile-md tbody td {
        display: block;
        padding: 0 !important;
        border: 0 !important;
        text-align: left !important;
    }
    .s4-table.table-mobile-md tbody td:first-child {
        grid-column: 1 / -1;
        font-weight: 600;
    }
    .s4-table.table-mobile-md tbody td:first-child::before {
        display: none;
    }
}

@media (max-width: 767.98px) {
    .page-head .src-line { flex-wrap: nowrap !important; }
    .tb1-main .page-head { align-items: flex-start; }
    .page-head .page-title { font-size: 20px; }
    .page-head .src-line { white-space: nowrap; overflow-x: auto; max-width: 100%; }
}

/* Воронка на телефоне: views.js задает колонки инлайн-стилем, и он перебивал
   телефонную раскладку components.css (две строки). Числа зажимались в
   узкую колонку и рвались по слову ("876 ·/→/64,2%"). */
@media (max-width: 560px) {
    .funnel-step { grid-template-columns: 1fr auto !important; }
    .funnel-conv { white-space: nowrap !important; }
}

/* Норма червоного етапу воронки - підпис у порожній частині смуги (views.js funnelHtml) */
.funnel-track { position: relative; }
.funnel-norm {
    position: absolute; right: 8px; top: 50%; transform: translateY(-50%);
    font-size: 11px; line-height: 1; color: var(--muted); white-space: nowrap; pointer-events: none;
}

/* Цілі: план у полі вводу читався тонким сірим (Рома 02.10) - жирніше, як факт поруч */
.tb2-input { font-weight: 600; color: var(--text-strong); }

/* ---- 02.10 Иконки: Інтеграції (схема, таблица, карточки) и пины карты Інфраструктури ---- */
.tb5-ig-ico { color: var(--brand); }
.tb5-ig-node.is-dash .tb5-ig-ico { color: var(--ok); }
.tb5-src-ico { width: 16px; height: 16px; color: var(--brand); vertical-align: -3px; }
.sd5-ig-card-top .ico { color: var(--brand); }
/* Пин карты: кружок цвета статуса 22px, белый значок типа 12px внутри */
#infrastructure-body .infra-pin .map-pin-dot {
    width: 22px; height: 22px; border-radius: 50%; transform: none;
    display: inline-flex; align-items: center; justify-content: center;
}
#infrastructure-body .infra-pin .infra-pin-ico { width: 12px; height: 12px; color: #fff; stroke-width: 2.4; }
#infrastructure-body .infra-pin.is-warn .infra-pin-ico { color: var(--on-warn); }

/* 02.10 (Николай): флаговый желтый как фирменный акцент, не в данных -
   тонкая метка у активного пункта меню: синяя плашка + желтая черта = флаг. */
#tabs .tab { position: relative; }
#tabs .tab.active::before {
    content: ""; position: absolute; left: 3px; top: 50%; transform: translateY(-50%);
    width: 3px; height: 56%; border-radius: 2px; background: var(--viz-4);
}
/* Значок активного пункта красился --accent = цвет плашки и пропадал (синее на синем) */
#tabs .tab.active .ico { color: var(--on-blue); }

/* Цілі: три таблицы (місяць/тиждень/рік) на одной сетке колонок - план под планом */
@media (min-width: 768px) {
    .tb2-goals { table-layout: fixed; }
    .tb2-goals .tb2-c-goal { width: 30%; }
    .tb2-goals .tb2-c-plan { width: 16%; }
    .tb2-goals .tb2-c-fact { width: 16%; }
    .tb2-goals .tb2-c-done { width: 22%; }
    .tb2-goals .tb2-c-st { width: 16%; }
}

/* ---- 02.10 Виробництво: полосы по линиям с отметкой нормы + донат простоя ---- */
.op-bars { display: flex; flex-direction: column; gap: 12px; }
.op-bar-row { display: grid; grid-template-columns: minmax(96px, 38%) 1fr 52px; align-items: center; gap: 10px; }
.op-bar-lbl { display: flex; flex-direction: column; min-width: 0; line-height: 1.25; }
.op-bar-lbl b { font-size: 13px; font-weight: 600; color: var(--text-strong); }
.op-bar-lbl span { font-size: 11px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.op-bar-track { position: relative; height: 10px; border-radius: 5px; background: var(--surface-hover); }
.op-bar-fill { display: block; height: 100%; border-radius: 5px; background: var(--ok); }
.op-bar-fill.is-warn { background: var(--warn); }
.op-bar-fill.is-alert { background: var(--err); }
.op-bar-norm { position: absolute; top: -4px; bottom: -4px; width: 2px; margin-left: -1px; background: var(--text-strong); border-radius: 1px; opacity: .55; }
.op-bar-val { text-align: right; font-size: 13px; font-weight: 600; font-variant-numeric: tabular-nums; color: var(--text-strong); }
.op-bar-legend { display: flex; align-items: center; gap: 6px; font-size: 11px; color: var(--muted); margin-top: 2px; }
.op-bar-norm-key { display: inline-block; width: 2px; height: 12px; background: var(--text-strong); opacity: .55; border-radius: 1px; }
.op-donut { position: relative; height: 170px; }
.op-donut-legend { display: grid; grid-template-columns: 1fr 1fr; gap: 6px 14px; margin-top: 12px; font-size: 12px; }
.op-dl-row { display: flex; justify-content: space-between; gap: 6px; }
.op-dl-row .s4-key-dot { display: inline-block; margin-right: 6px; }
.op-dl-v, .op-cell { font-weight: 600; font-variant-numeric: tabular-nums; }
.op-dl-v.is-warn, .op-cell.is-warn { color: var(--warn-text); }
.op-dl-v.is-alert, .op-cell.is-alert { color: var(--err-text); }

/* 02.10: развернутая карточка (sd5-expanded, position: fixed) оказывалась ПОД
   затемнением: transform от анимаций появления (.view viewIn, vs-a-in) у предков
   делал их containing block и stacking context. Пока карточка открыта - transform
   снят с нее и всех ее предков. */
body.sd5-lock :has(.sd5-expanded),
.card.sd5-expanded { transform: none !important; filter: none !important; }
/* Уточнение: удержанный последний кадр viewIn (fill both) у .view держал
   containing block и при transform: none. Удержание снято только пока открыто -
   финальный кадр = обычный вид, перезапуска анимации нет. */
body.sd5-lock :has(.sd5-expanded) { animation-fill-mode: none !important; }
/* Липкие карточки/колонки (w1-sticky, tb4-ac-cols) при развороте: sticky создавал
   свой слой (карточка под затемнением) или перебивал fixed (карточка узкая). */
body.sd5-lock .w1-sticky:has(.sd5-expanded) { position: static !important; }
.card.sd5-expanded { position: fixed !important; }
/* Над сайдбаром Tabler (z 1030): затемнение и карточка выше. Блок с графиком в
   развернутой карточке растет - Chart.js (responsive) перерисует под размер. */
.sd5-backdrop { z-index: 1040; }
.card.sd5-expanded { z-index: 1050; }
.card.sd5-expanded div:has(> canvas) { height: min(62vh, 600px) !important; }
/* 07.10 (Дмитрий): развернутая карточка на весь экран слишком растянута -
   компактное окно по центру, высота по содержимому. */
.card.sd5-expanded {
    width: min(1100px, calc(100vw - 32px)) !important;
    height: fit-content !important;
    max-height: calc(100vh - 32px);
    margin: auto !important;
}

/* Виробництво: фильтр по линии */
.op-filter { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.op-filter-lbl { font-size: 13px; font-weight: 600; color: var(--text-strong); }
.op-dim { opacity: .32; transition: opacity var(--transition, .2s); }
tr.op-pick td { background: var(--brand-tint-8); }
@media (prefers-reduced-motion: reduce) { .op-dim { transition: none; } }
/* === JIN-VIZ.CSS === */
/* demo/assets/css/jin-viz.css */
/* НАЗНАЧЕНИЕ: инфографика в ответах Джина (плитки цифр, полосы с нормой, тренд).
   Требование Дмитрия: Джин запоминающийся, с инфографикой, не обычный чат. */
/* СВЯЗИ: chat.js vizHtml() (разметка .jv-*), data/chat.json dialogs[].viz.
   Цвета только токенами (--brand, --err, --warn, --ok): палитру ведет Muse. */
/* РАЗМЕР: ~170 строк */

/* Ответ с инфографикой шире обычного пузыря: графику нужно место */
.msg.msg-a.has-viz {
    max-width: min(860px, 100%);
    width: 100%;
}

.jv {
    display: flex;
    flex-direction: column;
    gap: 10px;
    margin: 6px 0 12px;
}

/* ---------- Плитки: 2-3 главные цифры ответа ---------- */
.jv-tiles {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
    gap: 10px;
}

.jv-tile {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 10px 12px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--surface, #fff);
}

.jv-tile-v {
    font-family: var(--font-display, inherit);
    font-size: 22px;
    font-weight: 650;
    line-height: 1.15;
    letter-spacing: -0.015em;
    color: var(--text-strong, var(--text));
    font-variant-numeric: tabular-nums;
}

.jv-tile.is-bad .jv-tile-v { color: var(--err-text); }
.jv-tile.is-warn .jv-tile-v { color: var(--warn-text); }
.jv-tile.is-good .jv-tile-v { color: var(--ok); }

.jv-tile-l {
    font-size: 13px;
    color: var(--text);
}

.jv-tile-d {
    font-size: 12px;
    color: var(--muted);
}

/* ---------- Карточка графика ---------- */
.jv-chart {
    padding: 12px 14px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--surface, #fff);
}

.jv-chart-t {
    margin-bottom: 8px;
    font-size: 13px;
    font-weight: 600;
    color: var(--text);
}

.jv-legend {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-top: 6px;
    font-size: 12px;
    color: var(--muted);
}

.jv-norm-key {
    width: 14px;
    height: 0;
    border-top: 2px dashed var(--muted);
}

/* ---------- Полосы ---------- */
.jv-bars {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.jv-bar {
    display: grid;
    grid-template-columns: minmax(110px, 170px) 1fr auto;
    align-items: center;
    gap: 10px;
    font-size: 13px;
}

.jv-bar-l {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--text);
}

.jv-bar-track {
    position: relative;
    height: 12px;
    border-radius: 6px;
    background: var(--surface-2, rgba(0, 0, 0, 0.05));
}

.jv-bar-fill {
    position: absolute;
    inset: 0 auto 0 0;
    border-radius: 6px;
    background: var(--brand, var(--accent));
    transform-origin: left center;
    animation: jv-grow 0.6s cubic-bezier(0.25, 1, 0.5, 1) both;
}

.jv-bar.is-warn .jv-bar-fill { background: var(--warn); }
.jv-bar.is-bad .jv-bar-fill { background: var(--err); }

.jv-norm {
    position: absolute;
    top: -3px;
    bottom: -3px;
    width: 0;
    border-left: 2px dashed var(--muted);
}

.jv-bar-v {
    min-width: 64px;
    text-align: right;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    color: var(--text-strong, var(--text));
}

.jv-bar.is-bad .jv-bar-v { color: var(--err-text); }

@keyframes jv-grow {
    from { transform: scaleX(0); }
    to { transform: scaleX(1); }
}

/* ---------- Тренд (SVG) ---------- */
.jv-trend {
    display: block;
    width: 100%;
    height: auto;
    max-height: 190px;
    overflow: visible;
}

.jv-area { fill: var(--brand, var(--accent)); opacity: 0.08; }
.jv-line { fill: none; stroke: var(--brand, var(--accent)); stroke-width: 2.5; stroke-linejoin: round; stroke-linecap: round; }
.jv-norm-line { stroke: var(--muted); stroke-width: 1.5; stroke-dasharray: 5 4; }
.jv-dot { fill: var(--surface, #fff); stroke: var(--brand, var(--accent)); stroke-width: 2; }
.jv-dot.is-last { fill: var(--brand, var(--accent)); }
.jv-val { font-size: 12px; font-weight: 600; fill: var(--text); font-variant-numeric: tabular-nums; }
.jv-val.is-last { fill: var(--brand, var(--accent)); font-size: 13px; }
.jv-lbl { font-size: 11px; fill: var(--muted); }

@media (prefers-reduced-motion: reduce) {
    .jv-bar-fill { animation: none; }
}

@media (max-width: 575.98px) {
    .jv-tiles { grid-template-columns: 1fr 1fr; }
    /* нечетная последняя плитка - на всю ширину, без дыры справа */
    .jv-tile:last-child:nth-child(odd) { grid-column: 1 / -1; }
    .jv-tile-v { font-size: 19px; }
    .jv-bar { grid-template-columns: 96px 1fr auto; gap: 8px; font-size: 12px; }
    .jv-bar-v { min-width: 0; }
}
/* === ND-ACCENTS.CSS === */
/* demo/assets/css/nd-accents.css */
/* НАЗНАЧЕНИЕ: смислові акценти синім/жовтим на екранах Николая (фінанси,
   бухгалтерія). Відповідь на "все сіре, не розумієш де акценти" (Дмитро 25.09):
   синій = підсумок і поточний місяць; жовтий - лише увага (див. main.css). */
/* СВЯЗИ: variables.css + b-color.css (--brand, --brand-tint-*, --role-current, --role-edge, --text-brand),
   views.js finance() (tr.fin-total, .tb4-bal-row.is-total), v4.css (.tb4-ac-cols).
   Підключається ОСТАННІМ в index.php. */
/* РАЗМЕР: ~50 строк */

/* ---------- Фінанси: P&L і Cash Flow ---------- */

/* Підсумкові рядки (Валовий прибуток, EBITDA, ...) - тиха синя смуга */
#finance-body tr.fin-total > * {
    background: var(--brand-tint-8);
}

/* Фінальний рядок звіту (Чистий прибуток / Залишок на кінець) - синій текст */
#finance-body tr.fin-total:last-child > * {
    background: var(--brand-tint-12);
    color: var(--text-brand);
}

/* Поточний місяць (остання колонка) - синій --role-current: сюди дивитися першим. Ліва кромка 2px відрізняє колонку від рядків підсумків. */
#fin-pnl-stmt th:last-child,
#fin-pnl-stmt td:last-child,
#fin-cf-stmt th:last-child,
#fin-cf-stmt td:last-child {
    background-image: linear-gradient(var(--role-current), var(--role-current));
    box-shadow: inset 2px 0 0 var(--role-edge);
}

#fin-pnl-stmt thead th:last-child,
#fin-cf-stmt thead th:last-child {
    color: var(--text-strong);
    box-shadow: inset 2px 0 0 var(--role-edge), inset 0 -2px 0 var(--role-edge);
}

/* Баланс: "Разом активів / пасивів" - синя подложка + сильный темный текст
   (синий текст читался как ссылка; Задание 7 п.5). */
#finance-body .tb4-bal-row.is-total {
    background: var(--brand-tint-12);
    color: var(--text-strong);
    font-weight: 700;
    border-radius: var(--radius-xs);
}

/* Темна тема: Tabler фарбує шапку таблиці світлим (--tblr-bg-surface-tertiary),
   бо тема в проєкті через html.dark-theme, а не data-bs-theme - шапки були білі
   з сірим текстом на всіх екранах. */
html.dark-theme .table thead th {
    background-color: var(--surface-sunken);
}

/* ---------- Продажі: вкладки Огляд | Дзвінки ---------- */

/* Активна вкладка була блідо-сірою і не відрізнялась - синя, як вкладки маркетингу */
.tb4-subtabs .nav-link.active {
    background: var(--brand);
    border-color: var(--brand);
    color: var(--brand-contrast);
}

/* ---------- Джин ---------- */

/* Питання власника: Strimix+ (sd-views.css:345) зробив фон 8% синього, а колір
   тексту лишився --on-accent (білий, під суцільну заливку) - текст не читався */
#brain-body .msg-q {
    color: var(--text-strong);
}

/* ---------- Інфраструктура: підписи точок у скупченні ---------- */

/* Київ / Біла Церква: 5 точок поруч, підпис під кожною налазив на сусідні.
   Сторону підпису задає lbl у INFRA_OBJECTS (views.js). */
.map-pin.lbl-t .map-pin-label {
    top: auto;
    bottom: calc(100% + 4px);
}

.map-pin.lbl-r .map-pin-label {
    top: 50%;
    left: calc(100% + 4px);
    transform: translateY(-50%);
}

.map-pin.lbl-l .map-pin-label {
    top: 50%;
    left: auto;
    right: calc(100% + 4px);
    transform: translateY(-50%);
}

/* Дубль підпису (другий "Київ") - лише при наведенні */
.map-pin.lbl-hide:not(:hover):not(:focus-visible):not(.is-active) .map-pin-label {
    opacity: 0;
}

/* ---------- Бухгалтерія ---------- */

/* Таблиця "За типами" (5 колонок) не влазила в 3fr з 8 - бейдж статусу
   обрізався праворуч. Рівні колонки; на мобільній лишається 1fr з v4.css. */
@media (min-width: 993px) {
    .tb4-ac-cols {
        grid-template-columns: 1fr 1fr;
    }
}
/* === PERIOD.CSS === */
/* demo/assets/css/period.css */
/* НАЗНАЧЕНИЕ: правки Дмитрия 06.10 (видео 01:59-02:38, 02:40-03:31):
   1) выбор периода в шапке раздела (period.js);
   2) три разных вида управления, чтобы не путались:
      вкладки = текст с синей чертой снизу (раздел внутри раздела),
      переключатель = серая дорожка с белой "пилюлей" (как List/Board/Calendar в treel),
      кнопка = сплошная синяя (действие: "Запитати", "Застосувати");
   3) фильтры маркетинга - справа в строке вкладок. */
/* СВЯЗИ: period.js headHtml(), views.js pageHead(), s4-marketing.js tabsHtml().
   Подключается последним в css-bundle.php (после nd-accents.css). Только токены. */
/* РАЗМЕР: ~200 строк */

/* ---------- 1. Шапка: заголовок, под ним выбор периода ----------
   Источники остаются справа в строке заголовка; если места мало,
   подпись "порівняно з" уходит под выбор, а не чип источников под период. */
.tb1-main .page-head { align-items: flex-start; flex-wrap: nowrap; }
.page-head-main { gap: 8px; flex: 1 1 auto; }
.page-head .src-line { flex: 0 0 auto; white-space: nowrap; margin-top: 4px !important; }
@media (max-width: 900px) {
    .tb1-main .page-head { flex-wrap: wrap; }
    .page-head .src-line { flex: 1 1 100%; white-space: normal; }
}

.pd {
    position: relative;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px 10px;
}

.pd-nav {
    display: inline-flex;
    align-items: stretch;
    height: 34px;
    border: 1px solid var(--border-default);
    border-radius: var(--radius-sm);
    background: var(--surface-card);
    overflow: hidden;
}

.pd-arrow,
.pd-current {
    border: 0;
    background: transparent;
    color: var(--text-body);
    font: inherit;
    cursor: pointer;
}

.pd-arrow {
    width: 32px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--text-muted);
}

.pd-arrow:hover:not(:disabled) { background: var(--brand-tint-8); color: var(--brand); }
.pd-arrow:disabled { opacity: 0.35; cursor: default; }

.pd-current {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 0 12px;
    border-left: 1px solid var(--border-default);
    border-right: 1px solid var(--border-default);
    font-size: 13px;
    white-space: nowrap;
}

.pd-current svg { color: var(--brand); }
.pd-current b { font-weight: 600; color: var(--text-strong, var(--text)); font-variant-numeric: tabular-nums; }
.pd-current:hover { background: var(--brand-tint-8); }
.pd-chev { color: var(--text-faint, var(--text-muted)); font-size: 11px; }

.pd-arrow:focus-visible,
.pd-current:focus-visible,
.pd-seg-btn:focus-visible,
.pd-preset:focus-visible { outline: 0; box-shadow: var(--ring-focus); }

.pd-cmp {
    font-size: 12px;
    color: var(--muted);
    white-space: nowrap;
}

/* ---------- 2. Переключатель: дорожка + белая "пилюля" ----------
   Один вид для всех переключателей демо: шаг периода, Місяць/Рік в Фінансах,
   День..Рік на графике маркетинга, лінії на Виробництві, рівні в Інтеграціях. */
.pd-seg,
.s4-segmented,
.tb5-ig-levels,
.btn-group:has(> [data-fin-mode]) {
    display: inline-flex;
    gap: 2px;
    padding: 3px;
    border-radius: var(--radius-sm);
    background: var(--surface-sunken);
    border: 1px solid var(--border-default);
}

.pd-seg-btn,
.s4-segmented .btn,
.tb5-ig-levels .btn,
.btn-group > [data-fin-mode] {
    height: 26px;
    padding: 0 10px;
    border: 0 !important;
    border-radius: calc(var(--radius-sm) - 2px) !important;
    background: transparent !important;
    color: var(--text-muted) !important;
    font-size: 12.5px;
    font-weight: 500;
    box-shadow: none !important;
    white-space: nowrap;
    cursor: pointer;
}

.pd-seg-btn:hover,
.s4-segmented .btn:hover,
.tb5-ig-levels .btn:hover,
.btn-group > [data-fin-mode]:hover { color: var(--text-body) !important; }

.pd-seg-btn.is-on,
.s4-segmented .btn.btn-primary,
.s4-segmented .btn.active,
.tb5-ig-levels .btn.active,
.tb5-ig-levels .btn.btn-primary,
.btn-group > [data-fin-mode].active {
    background: var(--surface-card) !important;
    color: var(--text-strong, var(--text)) !important;
    font-weight: 600;
    box-shadow: 0 1px 2px rgba(15, 23, 42, 0.12), 0 0 0 1px var(--border-default) !important;
}

/* ---------- 3. Выпадающее окно выбора ---------- */
.pd-pop {
    position: absolute;
    z-index: 40;
    top: calc(100% + 6px);
    left: 0;
    width: 430px;
    max-width: calc(100vw - 32px);
    padding: 12px;
    border: 1px solid var(--border-default);
    border-radius: var(--radius-md);
    background: var(--surface-card);
    box-shadow: 0 12px 32px rgba(15, 23, 42, 0.16);
}

.pd-pop[hidden] { display: none; }

.pd-pop-h {
    margin: 10px 0 6px;
    font-size: 11.5px;
    font-weight: 600;
    color: var(--muted);
}

.pd-pop-h:first-child { margin-top: 0; }

.pd-presets {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 4px;
}

.pd-preset {
    padding: 6px 10px;
    border: 0;
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--text-body);
    font-size: 13px;
    text-align: left;
    cursor: pointer;
}

.pd-preset:hover { background: var(--brand-tint-8); color: var(--brand); }

.pd-custom {
    display: flex;
    align-items: center;
    gap: 6px;
}

.pd-custom input { flex: 1 1 0; min-width: 0; }
.pd-custom span { color: var(--muted); }

.pd-seg-wide { display: flex; }
.pd-seg-wide .pd-seg-btn { flex: 1 1 0; padding: 0 6px; }

.pd-pop-note {
    margin-top: 10px;
    font-size: 11.5px;
    color: var(--muted);
}

/* ---------- 4. Вкладки: текст + синяя черта снизу (не кнопки) ---------- */
.s4-tabsbar {
    border-bottom: 1px solid var(--border-default);
    gap: 12px;
}

.s4-tabs { gap: 2px; scrollbar-width: none; }
.s4-tabs::-webkit-scrollbar { display: none; }
/* "^^" сворачивал вкладки - с фильтрами в той же строке не нужен */
.s4-tabsbar .s4-collapse { display: none; }

.s4-tabs .s4-tab,
.tb4-subtabs .nav-link {
    padding: 10px 10px !important;
    border: 0 !important;
    border-radius: 0 !important;
    background: transparent !important;
    color: var(--text-muted) !important;
    box-shadow: inset 0 -2px 0 transparent;
    font-weight: 500;
}

.s4-tabs .s4-tab:hover,
.tb4-subtabs .nav-link:hover {
    color: var(--text-body) !important;
    box-shadow: inset 0 -2px 0 var(--border-strong);
}

.s4-tabs .s4-tab.active,
.tb4-subtabs .nav-link.active {
    color: var(--brand) !important;
    font-weight: 600;
    box-shadow: inset 0 -2px 0 var(--brand);
}

.tb4-subtabs {
    border-bottom: 1px solid var(--border-default) !important;
    gap: 2px;
}

/* ---------- 5. Фильтры маркетинга справа в строке вкладок ---------- */
.s4-tbf { flex: 0 0 auto; margin-left: auto; }
.s4-tbf .s4-filterbar { flex-wrap: nowrap; gap: 6px; margin: 0; }
.s4-tbf .s4-chip { height: 30px; padding: 0 10px; font-size: 12.5px; }
/* 07.10: по ширине контента, а не окна - открытый Джин сужает контент
   при широком окне (вкладкам ~830px + фильтрам ~250px) */
@container tbmain (max-width: 1120px) {
    .s4-tabsbar { flex-wrap: wrap; border-bottom: 0; }
    .s4-tabs { flex: 1 1 100%; order: 1; border-bottom: 1px solid var(--border-default); }
    .s4-tbf { order: 2; margin-left: 0; padding-bottom: 8px; }
    .s4-tbf .s4-filterbar { flex-wrap: wrap; }
}
/* совсем узко (1280 + Джин): вкладки переносом, а не спрятанной прокруткой */
@container tbmain (max-width: 860px) {
    .s4-tabs { flex-wrap: wrap; overflow-x: visible; }
}

@media (max-width: 600px) {
    .pd { gap: 6px; }
    .pd-nav { flex: 1 1 100%; }
    .pd-current { flex: 1 1 auto; justify-content: center; }
    .pd-seg { flex: 1 1 100%; }
    .pd-seg .pd-seg-btn { flex: 1 1 0; padding: 0 4px; }
    .pd-cmp { white-space: normal; }
    .pd-pop { position: fixed; left: 16px; right: 16px; top: 120px; width: auto; }
}

/* ---------- 6. P&L / Cash Flow за 12 місяців ----------
   Подсвечена не последняя колонка, а месяцы выбранного периода (.fin-hl).
   Суммы в тис грн, плотнее, чтобы 12 колонок помещались на 1440. */
#fin-pnl-stmt .fin-wide th:last-child,
#fin-pnl-stmt .fin-wide td:last-child,
#fin-cf-stmt .fin-wide th:last-child,
#fin-cf-stmt .fin-wide td:last-child {
    background-image: none;
    box-shadow: none;
}

#fin-pnl-stmt .fin-wide .fin-hl,
#fin-cf-stmt .fin-wide .fin-hl {
    background-image: linear-gradient(var(--role-current), var(--role-current));
}

#fin-pnl-stmt .fin-wide thead th.fin-hl,
#fin-cf-stmt .fin-wide thead th.fin-hl {
    color: var(--text-strong);
    box-shadow: inset 0 -2px 0 var(--role-edge);
}

.fin-wide th,
.fin-wide td {
    padding-left: 8px !important;
    padding-right: 8px !important;
    font-size: 13px;
}

.fin-wide thead th { font-size: 11.5px; }

@media (prefers-reduced-motion: reduce) {
    .pd-pop { box-shadow: none; }
}
/* === RIBBON.CSS === */
/* demo/assets/css/ribbon.css */
/* НАЗНАЧЕНИЕ: лента "Джин радить звернути увагу" (ribbon.js) - правки Дмитрия
   06.10: отдельный блок другим цветом под верхними цифрами, карточки листаются
   вбок. Цвет - бледный теплый желтый (Николай 07.10: желтого в демо почти нет,
   насыщенный не берем, чтобы не спорил со статусом "увага"). */
/* СВЯЗИ: ribbon.js; подключается в css-bundle.php после period.css. Только токены. */
/* РАЗМЕР: ~150 строк */

.rb {
    --rb-bg: #FFF8DC;
    --rb-line: rgba(232, 180, 0, 0.45);
    --rb-ink: #6B5200;
    margin: 0 0 16px;
    padding: 12px 12px 10px;
    border: 1px solid var(--rb-line);
    border-radius: var(--radius-md);
    background: var(--rb-bg);
}

html.dark-theme .rb {
    --rb-bg: rgba(255, 213, 0, 0.08);
    --rb-line: rgba(255, 213, 0, 0.30);
    --rb-ink: #FFD500;
}

/* В строке Bootstrap-сетки лента занимает всю ширину */
.row > .rb { width: 100%; margin-left: 0; margin-right: 0; }

.rb-head {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 10px;
    padding: 0 2px;
}

.rb-ico { display: inline-flex; color: var(--rb-ink); }
.rb-head b { font-size: 14px; font-weight: 650; color: var(--text-strong, var(--text)); }
.rb-count { font-size: 12px; color: var(--rb-ink); opacity: 0.85; }

.rb-nav { margin-left: auto; display: inline-flex; gap: 4px; }

.rb-arrow {
    width: 28px;
    height: 28px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--rb-line);
    border-radius: 999px;
    background: var(--surface-card);
    color: var(--text-body);
    cursor: pointer;
}

.rb-arrow:hover { color: var(--brand); border-color: var(--brand); }
.rb-arrow:focus-visible { outline: 0; box-shadow: var(--ring-focus); }

.rb-track {
    display: flex;
    gap: 10px;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scrollbar-width: thin;
    padding-bottom: 4px;
}

.rb-track:focus-visible { outline: 0; box-shadow: var(--ring-focus); border-radius: var(--radius-sm); }

.rb-card {
    flex: 0 0 300px;
    scroll-snap-align: start;
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 12px 14px;
    border: 1px solid var(--border-default);
    border-left: 3px solid var(--ok-line);
    border-radius: var(--radius-sm);
    background: var(--surface-card);
}

.rb-card.is-alert { border-left-color: var(--err); }
.rb-card.is-warn { border-left-color: var(--warn); }
.rb-card.is-ok { border-left-color: var(--ok); }

.rb-card-top { display: flex; align-items: center; gap: 7px; min-width: 0; }

.rb-dot {
    flex: 0 0 8px;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--ok);
}

.rb-card.is-alert .rb-dot { background: var(--err); }
.rb-card.is-warn .rb-dot { background: var(--warn); }

.rb-name {
    font-size: 13px;
    font-weight: 600;
    color: var(--text-strong, var(--text));
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.rb-val { display: flex; align-items: baseline; gap: 8px; }

.rb-val b {
    font-family: var(--font-display, inherit);
    font-size: 20px;
    font-weight: 650;
    font-variant-numeric: tabular-nums;
    color: var(--text-strong, var(--text));
}

.rb-card.is-alert .rb-val b { color: var(--err-text); }
.rb-card.is-warn .rb-val b { color: var(--warn-text); }
.rb-val span { font-size: 12px; color: var(--muted); }

.rb-lead {
    margin: 0;
    font-size: 13px;
    line-height: 1.4;
    color: var(--text-body);
}

.rb-more { font-size: 12.5px; color: var(--text-body); }
.rb-more summary { cursor: pointer; color: var(--brand); font-weight: 500; }
.rb-more p { margin: 6px 0 0; line-height: 1.45; }

@media (max-width: 600px) {
    .rb-card { flex-basis: 82%; }
    .rb-nav { display: none; }
}

@media (prefers-reduced-motion: reduce) {
    .rb-track { scroll-behavior: auto; }
}

/* "Запитати Джина" - вопрос по этой карточке в правую панель */
.rb-ask {
    align-self: flex-start;
    margin-top: auto;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 5px 10px;
    border: 1px solid var(--brand-tint-30);
    border-radius: 999px;
    background: var(--brand-tint-8);
    color: var(--brand);
    font-size: 12.5px;
    font-weight: 600;
    cursor: pointer;
}
.rb-ask svg { width: 14px; height: 14px; }
.rb-ask:hover { background: var(--brand-tint-12); }
.rb-ask:focus-visible { outline: 0; box-shadow: var(--ring-focus); }
/* === CAMS.CSS === */
/* demo/assets/css/cams.css */
/* НАЗНАЧЕНИЕ: кадр камеры в плитке Інфраструктури (Дмитро 06.10: "важко
   зрозуміти, що це камери"). Картинка 16:9 (assets/img/cams/*.svg, Muse
   Задание 10), поверх - номер камеры, REC, живое время, "Немає сигналу". */
/* СВЯЗИ: views.js infraCamTile(), camClockStart(); v5.css (.tb5-cam).
   Подключается в css-bundle.php после ribbon.css. */
/* РАЗМЕР: ~80 строк */

.tb5-cam { gap: 6px; padding: 6px 6px 8px; }

.tb5-cam-frame {
    position: relative;
    aspect-ratio: 16 / 9;
    overflow: hidden;
    border-radius: calc(var(--radius-sm) - 2px);
    background: #1B2230;
}

.tb5-cam-frame img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* Подписи поверх кадра - белые на полупрозрачной подложке, как в регистраторе */
.tb5-cam-tag,
.tb5-cam-rec,
.tb5-cam-time {
    position: absolute;
    padding: 1px 5px;
    border-radius: 3px;
    background: rgba(8, 12, 20, 0.55);
    color: #F1F5F9;
    font-family: var(--font-mono, ui-monospace, monospace);
    font-size: 10px;
    line-height: 1.5;
    letter-spacing: 0.02em;
    white-space: nowrap;
}

.tb5-cam-tag { top: 5px; left: 5px; }
.tb5-cam-rec { top: 5px; right: 5px; display: inline-flex; align-items: center; gap: 4px; }
.tb5-cam-time { bottom: 5px; left: 5px; font-variant-numeric: tabular-nums; }

/* Точка записи - оранжевая "критично" палитры (красного в демо нет) */
.tb5-cam-rec::before {
    content: '';
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--err);
    animation: tb5-rec 1.4s ease-in-out infinite;
}

@keyframes tb5-rec { 50% { opacity: 0.25; } }

.tb5-cam-nosig {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #E2E8F0;
    font-size: 12px;
    font-weight: 600;
    letter-spacing: 0.02em;
    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
}

.tb5-cam-meta { padding: 0 4px; }

/* 08.10: ролик складу (views.js videoPanel) - пропорції файлу 1600x922,
   не ширше 996px (висота ~574px), по центру */
.if-video {
    display: block;
    width: 100%;
    max-width: 996px;
    aspect-ratio: 1600 / 922;
    margin: 0 auto;
    object-fit: contain;
    border-radius: calc(var(--radius-sm) - 2px);
    background: #1B2230;
}

@media (prefers-reduced-motion: reduce) {
    .tb5-cam-rec::before { animation: none; }
}
/* === JIN-PANEL.CSS === */
/* demo/assets/css/jin-panel.css */
/* НАЗНАЧЕНИЕ: правая панель Джина (jin-panel.js). Закрыта - полоса 56px
   справа на всю высоту; открыта - панель 420px, контент сдвигается влево
   (а не перекрывается). Телефон: круглая кнопка в углу, панель на весь экран. */
/* СВЯЗИ: jin-panel.js, chat.js (.chat, .msg, .chat-input). Только токены.
   Подключается в css-bundle.php после cams.css. */
/* РАЗМЕР: ~170 строк */

:root { --jp-rail: 56px; --jp-w: 420px; }

/* ---------- место под Джина: контент сдвигается, а не прячется ---------- */
@media (min-width: 992px) {
    html.jp-ready .page-wrapper { margin-right: var(--jp-rail); transition: margin-right 0.25s ease; }
    html.jp-open .page-wrapper { margin-right: var(--jp-w); }
    html.jp-ready .float-nav { right: calc(var(--jp-rail) + 16px); }
    html.jp-open .float-nav { right: calc(var(--jp-w) + 16px); }
}

.jp { position: fixed; z-index: 1030; top: 0; right: 0; bottom: 0; pointer-events: none; }
.jp > * { pointer-events: auto; }

/* ---------- полоса (закрыто) ---------- */
.jp-rail {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    width: var(--jp-rail);
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 14px;
    padding: 18px 0;
    border: 0;
    border-left: 1px solid var(--border-default);
    background: var(--surface-card);
    color: var(--text-body);
    cursor: pointer;
}

.jp-rail:hover { background: var(--brand-tint-8); }
.jp-rail:focus-visible { outline: 0; box-shadow: inset var(--ring-focus); }

.jp-rail-ico {
    width: 36px;
    height: 36px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 999px;
    background: var(--brand);
    color: var(--brand-contrast);
}

.jp-rail-ico svg, .jp-head-ico svg { width: 18px; height: 18px; }

.jp-rail-name,
.jp-rail-hint {
    writing-mode: vertical-rl;
    transform: rotate(180deg);
    white-space: nowrap;
}

.jp-rail-name { font-weight: 650; font-size: 14px; color: var(--text-strong, var(--text)); }
.jp-rail-hint { font-size: 12px; color: var(--muted); }

html.jp-open .jp-rail { display: none; }

/* ---------- панель (открыто) ---------- */
.jp-panel {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    width: var(--jp-w);
    display: flex;
    flex-direction: column;
    border-left: 1px solid var(--border-default);
    background: var(--surface-card);
    box-shadow: -8px 0 24px rgba(15, 23, 42, 0.08);
    transform: translateX(100%);
    visibility: hidden;
    transition: transform 0.25s ease, visibility 0s linear 0.25s;
}

html.jp-open .jp-panel { transform: none; visibility: visible; transition: transform 0.25s ease; }

.jp-head {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 14px 14px 12px 16px;
    border-bottom: 1px solid var(--border-default);
}

.jp-head-ico {
    width: 32px;
    height: 32px;
    flex: 0 0 32px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 999px;
    background: var(--brand);
    color: var(--brand-contrast);
}

.jp-head-t { display: flex; flex-direction: column; min-width: 0; line-height: 1.25; }
.jp-head-t b { font-size: 15px; color: var(--text-strong, var(--text)); }
.jp-ctx { font-size: 12px; color: var(--muted); }

.jp-close {
    margin-left: auto;
    width: 32px;
    height: 32px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 0;
    border-radius: 999px;
    background: transparent;
    color: var(--text-muted);
    cursor: pointer;
}

.jp-close:hover { background: var(--brand-tint-8); color: var(--text-body); }
.jp-close:focus-visible { outline: 0; box-shadow: var(--ring-focus); }

/* Тело: лог чата растягивается, поле ввода прижато к низу */
.jp-body { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; padding: 12px 14px 14px; }
.jp-body .chat { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; gap: 10px; }
.jp-body .chat-log { flex: 1 1 auto; min-height: 0; max-height: none; overflow-y: auto; }
.jp-body .br-intro { font-size: 12.5px; margin: 0; }
.jp-body .suggests { display: flex; flex-wrap: wrap; gap: 6px; }
.jp-body .chat-input { margin: 0; gap: 8px; padding: 8px; align-items: stretch; }
.jp-body .chat-field { font-size: 13.5px !important; line-height: 1.4; min-height: 56px; padding: 8px 10px !important; }
.jp-body #br-send { height: auto; min-height: 40px; padding: 0 12px; font-size: 13.5px; align-self: flex-end; }
.jp-body .suggest { font-size: 12.5px; padding: 6px 10px; text-align: left; }
/* "Що ще вміє Джин" - для отдельного экрана, в узкой панели лишнее */
.jp-body .br-skills-panel { display: none; }

/* ---------- телефон и узкий экран: кнопка в углу, панель поверх ---------- */
@media (max-width: 991px) {
    .jp-rail {
        top: auto;
        bottom: 16px;
        right: 16px;
        width: 56px;
        height: 56px;
        padding: 0;
        justify-content: center;
        border: 0;
        border-radius: 999px;
        background: var(--brand);
        box-shadow: 0 6px 18px rgba(0, 105, 202, 0.35);
    }
    .jp-rail-name, .jp-rail-hint { display: none; }
    .jp-rail-ico { background: transparent; }
    .jp-panel { left: 0; width: auto; }
    .jp { left: auto; }
    html.jp-open { overflow: hidden; }
}

@media (prefers-reduced-motion: reduce) {
    .jp-panel, html.jp-ready .page-wrapper { transition: none; }
}
/* === LAYOUT.CSS === */
/* demo/assets/css/layout.css */
/* НАЗНАЧЕНИЕ: волна 5 правок Дмитрия 06.10 - "вирівняти екрани, щоб не було
   пустих місць" (видео 00:17-01:10) + замечание Николая 07.10: плитки во
   вкладках неровные, текст не влазит или обрезается, особенно с открытым
   Джином. Здесь только выравнивание, без смены палитры и шрифтов:
   1) карточки одного ряда одной высоты, графики заполняют карточку;
   2) большие цифры KPI не переносятся - размер под ширину карточки
      (container queries), "грн" рядом с числом;
   3) подписи переносятся, а не обрезаются многоточием. */
/* СВЯЗИ: s4-marketing.js / s4-mk-panes.js (ряды .s4-row2/.s4-row3), views.js
   (tb4-kpi, tb5-kpi, op-bar, tb4-bal-row, tb5-cam). Последним в css-bundle.php. */
/* РАЗМЕР: ~200 строк */

/* ---------- 1. Ряды карточек: одна высота ---------- */
/* Маркетинг: колонка - колонка-флекс, карточка тянется, тело тянется,
   график (maintainAspectRatio:false) заполняет остаток. Минимум высоты
   графика ниже прежней фиксированной, чтобы ряд определяла таблица/текст. */
.s4-row2 > [class*=col],
.s4-row3 > [class*=col] {
    display: flex;
    flex-direction: column;
}

.s4-row2 > [class*=col] > .card,
.s4-row3 > [class*=col] > .card {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
}

.s4-row2 > [class*=col] > .card > .card-body,
.s4-row3 > [class*=col] > .card > .card-body {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    min-height: 0;
}

.s4-row2 .card-body > .s4-chart,
.s4-row3 .card-body > .s4-chart,
.s4-row2 .card-body > .s4-chart-lg,
.s4-row3 .card-body > .s4-chart-lg {
    flex: 1 1 auto;
    height: auto;
    min-height: 220px;
}
/* 07.10: canvas вне потока. В потоке Chart.js подгонял canvas под ячейку, ячейка
   росла от canvas - петля, ряд с открытым Джином вытягивался до 1000+ px */
.s4-row2 .card-body > .s4-chart > canvas,
.s4-row3 .card-body > .s4-chart > canvas,
.s4-row2 .card-body > .s4-chart-lg > canvas,
.s4-row3 .card-body > .s4-chart-lg > canvas {
    position: absolute;
    inset: 0;
}

/* Ссылка "Перейти до детального звіту" и легенда - к низу карточки */
.s4-row2 .card > .s4-detail,
.s4-row3 .card > .s4-detail { margin-top: auto; }

/* Остальные экраны: карточки в обычном ряду Tabler одной высоты.
   Ряды с align-items-start (Баланс, липкие колонки) не трогаем. */
.tb1-main .row-cards:not(.align-items-start) > [class*=col] > .card:only-child {
    height: 100%;
}

/* ---------- 2. Цифры KPI: не переносить, размер под карточку ---------- */
.tb4-kpi .card-body,
.tb5-kpi .card-body,
.s4-kpi,
.sd5-stat,
.rb-card {
    container-type: inline-size;
}

.tb4-kpi-value,
.tb5-kpi-value {
    white-space: nowrap;
    font-size: clamp(17px, 10.5cqi, 26px);
}

.s4-kpis .tb3-kval {
    white-space: nowrap;
    font-size: clamp(16px, 11cqi, 22px);
}

.sd5-stat-value {
    white-space: nowrap;
    font-size: clamp(15px, 9.5cqi, 18px);
}

.rb-val b { font-size: clamp(16px, 7.5cqi, 20px); white-space: nowrap; }

/* "грн"/"год"/"од" - ближе и мельче, чтобы число влезало целиком */
.tb4-kpi-cur,
.tb5-kpi-cur { margin-left: 0.25em; font-size: 0.62em; }

/* ---------- 3. Подписи: перенос вместо многоточия ---------- */
/* Камеры: название точки в 2 строки, а не "Київ, централь..." */
.tb5-cam-name {
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
    line-height: 1.25;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
}

/* Сетка камер: столько колонок, сколько влезает (с Джином - 2, без - 3) */
.tb5-cam-grid {
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
}

/* Время на кадре не вылезает: кадр узкий - время мельче */
.tb5-cam-frame { container-type: inline-size; }
.tb5-cam-time { font-size: clamp(8px, 5.6cqi, 10px); max-width: calc(100% - 10px); overflow: hidden; }

/* Виробництво: подпись линии ("Фасування сипучих") переносится */
.op-bar-lbl,
.op-bar-lbl * {
    white-space: normal !important;
    overflow: visible !important;
    text-overflow: clip !important;
}

.op-bar-row { grid-template-columns: minmax(110px, 38%) 1fr auto; }

/* Баланс: сумма не вылезает - подпись сжимается и переносится */
.tb4-bal-row { gap: 10px; }
.tb4-bal-row > span { min-width: 0; white-space: normal; overflow-wrap: anywhere; }
.tb4-bal-row > b,
.tb4-bal-row > .tb4-num { flex: 0 0 auto; white-space: nowrap; }

/* ---------- 4. Маркетинг: Конверсія воронки 1/3 + вартість кроку ---------- */
.s4-f3body { gap: 14px; }
.s4-f3body #s4-funnel3 { flex: 1 1 auto; display: flex; flex-direction: column; justify-content: center; }
.s4-f3body #s4-funnel3 .fs-row { margin-bottom: 14px; }
.s4-fecon {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 8px;
    padding-top: 12px;
    border-top: 1px solid var(--border-default);
}
.s4-fe-h { grid-column: 1 / -1; font-size: 12px; color: var(--muted); }
.s4-fe { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.s4-fe-l { font-size: 12px; color: var(--muted); }
.s4-fe b { font-size: 15px; font-weight: 650; font-variant-numeric: tabular-nums; white-space: nowrap; color: var(--text-strong, var(--text)); }
/* Шаги воронки (SDCharts.funnelSteps): подпись | полоса | цифра. В узкой
   карточке (открыт Джин) конверсия уходит под цифру, а не вылезает. */
.fs-row { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; container-type: inline-size; }
.fs-lbl { flex: 0 0 auto; min-width: 52px; max-width: 30%; overflow-wrap: anywhere; }
.fs-track { flex: 1 1 auto; min-width: 40px; }
.fs-fill { height: 14px; border-radius: 6px; background: var(--viz-1, #0069CA); }
.fs-val { flex: 0 0 auto; text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
.fs-val .text-muted { margin-left: 4px; }
@container (max-width: 300px) {
    .fs-val { display: flex; flex-direction: column; align-items: flex-end; line-height: 1.25; }
    .fs-val .text-muted { margin-left: 0; font-size: 12px; }
}

/* ---------- 5. Інфраструктура: карта | камеры одной высоты, СЕС на всю ширину ---------- */
#if-cams > .card-body { display: flex; flex-direction: column; }
#if-cams .tb5-cam-grid { flex: 1 1 auto; grid-auto-rows: 1fr; }
#if-cams .tb5-cam { display: flex; flex-direction: column; }
/* кадр заполняет высоту плитки, когда ряд выше (картинка cover - обрезка краев) */
#if-cams .tb5-cam-frame { flex: 1 1 auto; aspect-ratio: auto; min-height: 84px; }
.if-ses-wide .card-body {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 10px 24px;
    align-items: stretch;
}
/* 07.10: станции | метрики - верх по одной линии, одна высота (станции тянутся) */
.if-ses-wide .tb5-ses-app { margin-top: 0; }
.if-ses-wide .tb5-ses-station { flex: 1 1 auto; align-items: center; }
@media (max-width: 767px) {
    .if-ses-wide .card-body { grid-template-columns: minmax(0, 1fr); }
}

/* ---------- 6. Цілі: не на всю ширину (видео 07:37-08:03) ----------
   "Виконання плану зробив би меншу, цілі - меншими віконечками, не розтягував
   на весь екран". Две колонки: Виконання плану | Цілі на місяць,
   Цілі на тиждень | Цілі на рік. Узко (открыт Джин, ноутбук) - одна колонка. */
#view-goals { container-type: inline-size; }
@container (min-width: 980px) {
    #goals-body {
        display: grid;
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
        gap: 16px;
        align-items: stretch;
    }
    #goals-body > .sd5-kpi-strip,
    #goals-body > .row { grid-column: 1 / -1; }
    #goals-body > .card { margin: 0 !important; }
    #goals-body > .sd5-kpi-strip,
    #goals-body > .row { margin: 0 !important; }
    /* таблица в половине ширины: план и факт шире, полоса резиновая */
    #goals-body .tb2-goals th, #goals-body .tb2-goals td { padding-left: 8px; padding-right: 8px; }
    #goals-body .tb2-goals th { white-space: normal; line-height: 1.2; }
    #goals-body .tb2-goals .tb2-c-goal { width: 21%; }
    #goals-body .tb2-goals .tb2-c-plan { width: 23%; }
    #goals-body .tb2-goals .tb2-c-fact { width: 20%; }
    #goals-body .tb2-goals .tb2-c-done { width: 20%; }
    #goals-body .tb2-goals .tb2-c-st { width: 16%; }
    #goals-body .tb2-input { width: 100%; padding-left: 6px; padding-right: 8px; font-size: 13px; }
    #goals-body .tb2-goal-pct { gap: 6px; }
    #goals-body .tb2-goal-pct .tb2-bar { flex: 1 1 auto; width: auto; min-width: 24px; }
    #goals-body .tb2-goal-pct .tb2-num { min-width: 36px; }
    #goals-body .tb2-goals td:first-child .text-muted { display: block; font-size: 11.5px; }
}

/* ---------- 7. Большие экраны (видео 07:17-07:33, iMac) ----------
   "На великому екрані розтягнуто, розкидано". Контент не шире 1680px,
   по центру свободной области (между меню и Джином). До 1920 - как было. */
/* brand.css снимал ограничение (main.tb1-main max-width:none) - перебиваем точнее;
   width:100% обязателен: во flex-родителе auto-поля сжимали main до ширины
   содержимого, и лента рекомендаций раздувала его до 4700px */
body > .page main.tb1-main.page,
main#main.tb1-main {
    width: 100%;
    max-width: 1680px !important;
    margin-left: auto !important;
    margin-right: auto !important;
}
@media (min-width: 2200px) {
    /* на 4K/iMac чуть крупнее базовый текст, чтобы плотность совпадала с ноутбуком */
    html { font-size: 17px; }
}

/* ---------- 8. План-факт: три колонки заканчиваются на одной линии ----------
   (видео 00:23 "знизу під кількість лідів, маркетинговий бюджет - пусто").
   Последняя карточка каждой колонки тянется до низа, ее график растет. */
#overview-body .row-cards > [class*=col] > .d-flex.flex-column { height: 100%; }
#overview-body .row-cards > [class*=col] > .d-flex.flex-column > .card:last-child {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
}
#overview-body .row-cards > [class*=col] > .d-flex.flex-column > .card:last-child > .card-body {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
}
#overview-body .row-cards > [class*=col] > .d-flex.flex-column > .card:last-child .tb2-chart {
    flex: 1 1 auto;
    height: auto;
    min-height: 150px;
}

/* ---------- 9. Узкий контент (открыт Джин на ноутбуке 1280-1366) ----------
   Ширина считается по контенту, а не по окну: container на main. */
main#main.tb1-main { container: tbmain / inline-size; }
@container tbmain (max-width: 900px) {
    /* 4 KPI в ряд -> 2+2 (Фінанси, Бухгалтерія, План-факт) */
    .row-cards > .col-lg-3 { flex: 0 0 50%; max-width: 50%; }
    /* фильтры карты Інфраструктури в 2 колонки, текст переносится */
    .sd5-if-filters { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    /* подзаголовок карточки под заголовок, а не за край */
    .card-header { flex-wrap: wrap; row-gap: 2px; }
    /* схема Інтеграцій: масштаб вместо прокрутки */
    .tb5-ig-svg { min-width: 520px; }
}
/* Шапка: если выбор периода и плашка источников не помещаются в строку -
   плашка уходит вниз, выбор периода не сжимается (стрелка "вперед" была обрезана) */
@container tbmain (max-width: 1100px) {
    .tb1-main .page-head { flex-wrap: wrap; }
    .page-head .src-line { white-space: normal; }
}
.pd-nav { flex: 0 0 auto; }
.sd5-if-filter { min-width: 0; overflow-wrap: anywhere; }

/* Итог "Разом" в Порівнянні каналів */
.s4-table tfoot .s4-total td { border-top: 2px solid var(--border-default); background: var(--brand-tint-8); }
/* Таблица в растянутой карточке ряда занимает высоту, строки распределяются ровно */
.s4-row2 > [class*=col] > .card > .table-responsive,
.s4-row3 > [class*=col] > .card > .table-responsive { flex: 1 1 auto; display: flex; }
.s4-row2 > [class*=col] > .card > .table-responsive > table,
.s4-row3 > [class*=col] > .card > .table-responsive > table { height: 100%; }
.s4-row2 > [class*=col] > .card > .s4-detail,
.s4-row3 > [class*=col] > .card > .s4-detail { margin-top: 0; padding: 10px 16px 14px; }
.s4-row2 > [class*=col] > .card > .table-responsive td,
.s4-row3 > [class*=col] > .card > .table-responsive td { vertical-align: middle; }

/* ---------- 10. Узкий контент: таблицы друг под другом, а не с прокруткой ----------
   (скриншоты Николая 07.10, открыт Джин): Продажі - Менеджери, Фінанси -
   контрагенты, Бухгалтерія - две таблицы, Цілі - свернутые блоки. */
@container tbmain (max-width: 1000px) {
    #sales-body .row-cards > .col-lg-8,
    #sales-body .row-cards > .col-lg-4,
    #finance-body .row-cards > .col-lg-6,
    #goals-body .row-cards > .col-lg-6 { flex: 0 0 100%; max-width: 100%; }
    .tb4-ac-cols { grid-template-columns: minmax(0, 1fr) !important; }
    /* P&L / Cash Flow 12 месяцев: плотнее, чтобы влезали без прокрутки */
    .fin-wide th, .fin-wide td { padding-left: 5px !important; padding-right: 5px !important; font-size: 12px; }
    .fin-wide thead th { font-size: 10.5px; }
    .fin-wide .tb4-stick { white-space: normal; min-width: 110px; }
}
/* Баланс на всю ширину: Активи | Зобов'язання всегда в две колонки */
.fin-bal-wide .tb4-bal-grid > .col-md-6 { flex: 0 0 50%; max-width: 50%; }
@container tbmain (max-width: 600px) {
    .fin-bal-wide .tb4-bal-grid > .col-md-6 { flex: 0 0 100%; max-width: 100%; }
}
/* липкая шапка таблицы Бухгалтерії не перекрывает заголовок при прокрутке */
#accounting-body .card { position: relative !important; top: auto !important; }
/* Цілі: "Підключені джерела" и "Налаштування індикаторів" - таблицы на 7 колонок,
   в половине ширины уходили в прокрутку - всегда друг под другом */
#goals-body > .row-cards > .col-md-6 { flex: 0 0 100%; max-width: 100%; }
@container tbmain (max-width: 1000px) {
    .src-panel .tbl-wrap th, .src-panel .tbl-wrap td { padding-left: 6px; padding-right: 6px; font-size: 12.5px; }
}

/* ---------- 11. Совсем узко (ноутбук 1280 + открытый Джин, контент < 720) ----------
   Все карточки друг под другом, как на планшете; Джин чуть уже. */
@media (min-width: 992px) and (max-width: 1399px) {
    :root { --jp-w: 380px; }
}
@container tbmain (max-width: 720px) {
    .row-cards > [class*=col-lg-]:not(.col-lg-3):not(.col-sm-6),
    .row-cards > [class*=col-xl-],
    .row-cards > .col-md-6:not(.col-sm-6) { flex: 0 0 100%; max-width: 100%; }
    .s4-chgrid { grid-template-columns: repeat(auto-fill, minmax(170px, 1fr)); }
}
@container tbmain (max-width: 760px) {
    #sales-body .table th, #sales-body .table td { padding-left: 6px !important; padding-right: 6px !important; font-size: 13px; }
    #sales-body .tb4-pbar { min-width: 48px; }
    .src-panel .tbl-wrap th, .src-panel .tbl-wrap td { padding-left: 4px; padding-right: 4px; font-size: 12px; }
}
/* Лента рекомендаций после ряда карточек - тот же зазор, что между рядами */
.row-cards + .rb, .row + .rb { margin-top: 16px; }
/* Маркетинг при открытом Джине: Порівняння каналів на всю ширину,
   под ним Ефективність | Структура; Топ кампаній и Динаміка - друг под другом */
@container tbmain (max-width: 900px) {
    .s4-row3 > .col-xl-5,
    .s4-row2 > .col-xl-6 { flex: 0 0 100%; max-width: 100%; }
    .s4-row3 > .col-xl-4,
    .s4-row3 > .col-xl-3 { flex: 0 0 50%; max-width: 50%; }
}
@container tbmain (max-width: 1000px) {
    .fin-wide .tb4-stick { min-width: 0; width: 118px; }
}
