/* ── css/footer.css ── */
/* Footer addons (Professional Footer) */

/* Ensure dashboards list can shrink when stream toggle is present */
.dashboards-footer .dashboards-list {
    flex: 1 1 auto;
    min-width: 0; /* allow overflow-x scroll inside */
}

/* Stream Toggle Button in Footer */
.footer-stream-toggle {
    position: relative;
    margin-left: auto;
    padding-left: 1rem;
    border-left: 1px solid var(--border);
    flex-shrink: 0;
}

.stream-toggle-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem 1rem;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--bg-primary);
    color: var(--text-primary, var(--dark));
    font-weight: 600;
    font-size: 0.875rem;
    cursor: pointer;
    transition: all 0.2s ease;
    user-select: none;
}

.stream-toggle-btn:hover {
    background: var(--bg-hover, var(--bg-tertiary));
    border-color: var(--primary, var(--color-primary));
}

.stream-toggle-btn:focus-visible {
    outline: 2px solid var(--primary, var(--color-primary));
    outline-offset: 2px;
}

.stream-toggle-icon {
    display: inline-flex;
    align-items: center;
}

/* Active/Recording State */
.stream-toggle-btn[aria-pressed="true"] {
    background: var(--danger);
    color: var(--text-inverse);
    border-color: var(--danger);
}

.stream-toggle-status {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--gray-400);
}

.stream-toggle-status.is-ready {
    background: var(--success);
}

.stream-toggle-status.is-recording {
    background: var(--danger);
    animation: streamPulse 1s infinite;
}

@keyframes streamPulse {
    0%, 100% { opacity: 1; transform: scale(1); }
    50% { opacity: 0.7; transform: scale(1.1); }
}
/* ── css/dashboard-themes.css ── */
/**
 * Dashboard Themes - CSS Variables and Common Styles
 * Version 1.0 - Unified theme system for all dashboards
 */

/* ========================================================================
   THEME VARIABLES - Dashboard-specific color schemes
   ======================================================================== */

/* Default dashboard theme (fallback for pages that explicitly set default/light) */
.dashboard-theme-default,
body[data-dashboard-theme="default"],
body[data-dashboard-theme="light"] {
    --dashboard-primary: var(--primary);
    --dashboard-primary-hover: var(--primary-dark);
    --dashboard-primary-light: var(--primary-light);
    --dashboard-primary-dark: var(--primary-dark);
}

/* Citizen Dashboard Theme */
.dashboard-theme-citizen,
body[data-dashboard-theme="citizen"] {
    --dashboard-primary: var(--color-primary);
    --dashboard-primary-hover: var(--primary);
    --dashboard-primary-light: var(--primary-lightest);
    --dashboard-primary-dark: var(--primary-dark);
    --primary: var(--dashboard-primary);
    --primary-hover: var(--dashboard-primary-hover);
    --primary-light: var(--dashboard-primary-light);
}

/* Analyst Dashboard Theme */
.dashboard-theme-analyst,
body[data-dashboard-theme="analyst"] {
    --dashboard-primary: var(--info-dark);
    --dashboard-primary-hover: var(--info-dark);
    --dashboard-primary-light: var(--info-lightest);
    --dashboard-primary-dark: var(--status-info);
    --primary: var(--dashboard-primary);
    --primary-hover: var(--dashboard-primary-hover);
    --primary-light: var(--dashboard-primary-light);
}

/* Researcher Dashboard Theme */
.dashboard-theme-researcher,
body[data-dashboard-theme="researcher"] {
    --dashboard-primary: var(--info);
    --dashboard-primary-hover: var(--info-dark);
    --dashboard-primary-light: var(--info-lightest);
    --dashboard-primary-dark: var(--status-info);
    --primary: var(--dashboard-primary);
    --primary-hover: var(--dashboard-primary-hover);
    --primary-light: var(--dashboard-primary-light);
}

/* Methodologist Dashboard Theme */
.dashboard-theme-methodologist,
body[data-dashboard-theme="methodologist"] {
    --dashboard-primary: var(--color-accent);
    --dashboard-primary-hover: var(--accent);
    --dashboard-primary-light: var(--accent-light);
    --dashboard-primary-dark: var(--accent);
    --primary: var(--dashboard-primary);
    --primary-hover: var(--dashboard-primary-hover);
    --primary-light: var(--dashboard-primary-light);
}

/* Academy Theme */
.dashboard-theme-academy,
body[data-dashboard-theme="academy"] {
    --dashboard-primary: var(--color-primary);
    --dashboard-primary-hover: var(--primary);
    --dashboard-primary-light: var(--primary-lightest);
    --dashboard-primary-dark: var(--primary-dark);
    --primary: var(--dashboard-primary);
    --primary-hover: var(--dashboard-primary-hover);
    --primary-light: var(--dashboard-primary-light);
}

/* Patron Dashboard Theme */
.dashboard-theme-patron,
body[data-dashboard-theme="patron"] {
    --dashboard-primary: var(--warning);
    --dashboard-primary-hover: var(--warning-dark);
    --dashboard-primary-light: var(--warning-lightest);
    --dashboard-primary-dark: var(--status-warning);
    --primary: var(--dashboard-primary);
    --primary-hover: var(--dashboard-primary-hover);
    --primary-light: var(--dashboard-primary-light);
}

/* Moderator Dashboard Theme */
.dashboard-theme-moderator,
body[data-dashboard-theme="moderator"] {
    --dashboard-primary: var(--danger);
    --dashboard-primary-hover: var(--danger-dark);
    --dashboard-primary-light: var(--danger-lightest);
    --dashboard-primary-dark: var(--status-danger);
    --primary: var(--dashboard-primary);
    --primary-hover: var(--dashboard-primary-hover);
    --primary-light: var(--dashboard-primary-light);
}

/* Urbanist Dashboard Theme */
.dashboard-theme-urbanist,
body[data-dashboard-theme="urbanist"] {
    --dashboard-primary: var(--color-primary);
    --dashboard-primary-hover: var(--primary);
    --dashboard-primary-light: var(--primary-lightest);
    --dashboard-primary-dark: var(--primary-dark);
    --primary: var(--dashboard-primary);
    --primary-hover: var(--dashboard-primary-hover);
    --primary-light: var(--dashboard-primary-light);
}

/* Data Engineer Dashboard Theme */
.dashboard-theme-data_engineer,
body[data-dashboard-theme="data_engineer"] {
    --dashboard-primary: var(--success);
    --dashboard-primary-hover: var(--success-dark);
    --dashboard-primary-light: var(--success-lightest);
    --dashboard-primary-dark: var(--success-dark);
    --primary: var(--dashboard-primary);
    --primary-hover: var(--dashboard-primary-hover);
    --primary-light: var(--dashboard-primary-light);
}

/* Historian Dashboard Theme */
.dashboard-theme-historian,
body[data-dashboard-theme="historian"] {
    --dashboard-primary: var(--success-dark);
    --dashboard-primary-hover: var(--status-success);
    --dashboard-primary-light: var(--success-lightest);
    --dashboard-primary-dark: var(--status-success);
    --primary: var(--dashboard-primary);
    --primary-hover: var(--dashboard-primary-hover);
    --primary-light: var(--dashboard-primary-light);
}

/* Task Setter Dashboard Theme */
.dashboard-theme-task_setter,
body[data-dashboard-theme="task_setter"] {
    --dashboard-primary: var(--danger-dark);
    --dashboard-primary-hover: var(--status-danger);
    --dashboard-primary-light: var(--danger-lightest);
    --dashboard-primary-dark: var(--status-danger);
    --primary: var(--dashboard-primary);
    --primary-hover: var(--dashboard-primary-hover);
    --primary-light: var(--dashboard-primary-light);
}

/* Producer Dashboard Theme */
.dashboard-theme-producer,
body[data-dashboard-theme="producer"] {
    --dashboard-primary: var(--color-accent);
    --dashboard-primary-hover: var(--accent);
    --dashboard-primary-light: var(--accent-light);
    --dashboard-primary-dark: var(--accent);
    --primary: var(--dashboard-primary);
    --primary-hover: var(--dashboard-primary-hover);
    --primary-light: var(--dashboard-primary-light);
}

/* Cultural Map Theme */
.dashboard-theme-cultural_map,
body[data-dashboard-theme="cultural_map"] {
    --dashboard-primary: var(--accent-color);
    --dashboard-primary-hover: var(--accent-hover);
    --dashboard-primary-light: var(--accent-subtle);
    --dashboard-primary-dark: var(--accent-hover);
    --primary: var(--dashboard-primary);
    --primary-hover: var(--dashboard-primary-hover);
    --primary-light: var(--dashboard-primary-light);
}

/* Parser Sandbox Theme */
.dashboard-theme-parser_sandbox,
body[data-dashboard-theme="parser_sandbox"] {
    --dashboard-primary: var(--text-secondary);
    --dashboard-primary-hover: var(--gray-700);
    --dashboard-primary-light: var(--border);
    --dashboard-primary-dark: var(--gray-800);
    --primary: var(--dashboard-primary);
    --primary-hover: var(--dashboard-primary-hover);
    --primary-light: var(--dashboard-primary-light);
}

/* Strategist Dashboard Theme */
.dashboard-theme-strategist,
body[data-dashboard-theme="strategist"] {
    --dashboard-primary: var(--danger-dark);
    --dashboard-primary-hover: var(--status-danger);
    --dashboard-primary-light: var(--danger-lightest);
    --dashboard-primary-dark: var(--status-danger);
    /* Совместимость со старыми переменными */
    --primary: var(--dashboard-primary);
    --primary-hover: var(--dashboard-primary-hover);
    --primary-light: var(--dashboard-primary-light);
}

/* Visionary Dashboard Theme */
.dashboard-theme-visionary,
body[data-dashboard-theme="visionary"] {
    --dashboard-primary: #8e44ad; /* §7.2: purple — WCAG AA (offwhite-on 5.6:1); было var(--art-visual) (4.47:1, провал) */
    --dashboard-primary-hover: #7d3c98;
    --dashboard-primary-light: var(--accent-light);
    --dashboard-primary-dark: #6c3486;
    /* Совместимость со старыми переменными */
    --primary: var(--dashboard-primary);
    --primary-hover: var(--dashboard-primary-hover);
    --primary-light: var(--dashboard-primary-light);
}

/* Developer Dashboard Theme */
.dashboard-theme-developer,
body[data-dashboard-theme="developer"] {
    --dashboard-primary: var(--success);
    --dashboard-primary-hover: var(--success-dark);
    --dashboard-primary-light: var(--success-lightest);
    --dashboard-primary-dark: var(--success-dark);
    /* Совместимость со старыми переменными */
    --primary: var(--dashboard-primary);
    --primary-hover: var(--dashboard-primary-hover);
    --primary-light: var(--dashboard-primary-light);
}

/* Nomad (Кочевник) Dashboard Theme — синий, как в исходных inline-стилях
   (аудит консистентности 2026-07-18: тема раньше не была зарегистрирована). */
.dashboard-theme-nomad,
body[data-dashboard-theme="nomad"] {
    --dashboard-primary: var(--info-dark);
    --dashboard-primary-hover: var(--info-dark);
    --dashboard-primary-light: var(--info-lightest);
    --dashboard-primary-dark: var(--status-info);
    /* Совместимость со старыми переменными */
    --primary: var(--dashboard-primary);
    --primary-hover: var(--dashboard-primary-hover);
    --primary-light: var(--dashboard-primary-light);
    --primary-dark: var(--dashboard-primary-dark);
}

/* Entrepreneur (Предприниматель) Dashboard Theme — янтарный акцент, ранее был
   зашит хексами в entrepreneur.css (var(--warning-dark)/#b45309); теперь через токены. */
.dashboard-theme-entrepreneur,
body[data-dashboard-theme="entrepreneur"] {
    --dashboard-primary: var(--warning-dark);
    --dashboard-primary-hover: var(--status-warning);
    --dashboard-primary-light: var(--warning-lightest);
    --dashboard-primary-dark: var(--status-warning);
    /* Совместимость со старыми переменными */
    --primary: var(--dashboard-primary);
    --primary-hover: var(--dashboard-primary-hover);
    --primary-light: var(--dashboard-primary-light);
    --primary-dark: var(--dashboard-primary-dark);
}

/* ========================================================================
   COMMON DASHBOARD COMPONENTS
   ======================================================================== */

/* Dashboard Header */
.dashboard-header {
    height: var(--header-height, 60px);
    background: var(--bg-primary);
    border-bottom: 1px solid var(--border);
    padding: 0 var(--spacing-lg);
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-shrink: 0;
    box-shadow: var(--shadow-xs);
    z-index: 100;
}

.dashboard-header .header-left {
    display: flex;
    align-items: center;
    gap: var(--spacing-md);
    /* §12 «длинные названия»: заголовок не выдавливает header-right за экран */
    min-width: 0;
}

.dashboard-header h1 {
    font-size: 1.5rem;
    font-weight: 600;
    color: var(--text-primary);
    margin: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.dashboard-header .city-name {
    font-size: 0.875rem;
    color: var(--text-secondary);
    padding: 0.25rem 0.75rem;
    background: var(--bg-secondary);
    border-radius: var(--radius);
}

.dashboard-header .header-right {
    display: flex;
    gap: var(--spacing-sm);
    align-items: center;
}

/* Dashboard layout primitives are defined in dashboard-base.css */

/* ========================================================================
   THEME-SPECIFIC OVERRIDES
   ======================================================================== */

/* Strategist specific overrides */
.dashboard-theme-strategist .btn-primary,
body[data-dashboard-theme="strategist"] .btn-primary {
    background-color: var(--dashboard-primary);
    border-color: var(--dashboard-primary);
}

.dashboard-theme-strategist .btn-primary:hover,
body[data-dashboard-theme="strategist"] .btn-primary:hover {
    background-color: var(--dashboard-primary-hover);
    border-color: var(--dashboard-primary-hover);
}

/* Visionary specific overrides */
.dashboard-theme-visionary .btn-primary,
body[data-dashboard-theme="visionary"] .btn-primary {
    background-color: var(--dashboard-primary);
    border-color: var(--dashboard-primary);
}

.dashboard-theme-visionary .btn-primary:hover,
body[data-dashboard-theme="visionary"] .btn-primary:hover {
    background-color: var(--dashboard-primary-hover);
    border-color: var(--dashboard-primary-hover);
}

/* Developer specific overrides */
.dashboard-theme-developer .btn-primary,
body[data-dashboard-theme="developer"] .btn-primary {
    background-color: var(--dashboard-primary);
    border-color: var(--dashboard-primary);
}

.dashboard-theme-developer .btn-primary:hover,
body[data-dashboard-theme="developer"] .btn-primary:hover {
    background-color: var(--dashboard-primary-hover);
    border-color: var(--dashboard-primary-hover);
}
/* ── css/ds-shell.css ── */
/**
 * DS-02 — AppShell (opt-in каркас унификации).
 * Источник истины: docs/dashboard_ui_audit_and_tz_2026-07-16.md §6.2–6.4.
 *
 * Активация: экран объявляет {% set ds_layout = 'overview'|'workspace'|'console' %}
 * ДО extends — _base_dashboard.html вешает на <body> классы
 * `ds-shell ds-layout-<mode>`. Без ds_layout ни одно правило отсюда не
 * применяется — немигрированные экраны не меняются.
 *
 * Каркас (ТЗ §6.3, workspace):
 *   rail 56 | header 56 | left 288 | canvas minmax(0,1fr) | inspector 360.
 * Раскрытая навигация — ВСЕГДА overlay (не двигает контент).
 * Breakpoints (§6.4): ≥1680 полный; 1024–1679 inspector=drawer;
 * 768–1023 обе панели drawer; <768 header 48, мобильный режим базы.
 *
 * Файл грузится ПОСЛЕ dashboard-base.css — равная специфичность
 * перекрывается порядком, !important не используется без необходимости.
 */

/* ========================================================================
   1. Навигация: постоянный рельс 56px, раскрытие — overlay 272px
   ======================================================================== */

body.ds-shell {
    /* Header 56 (48 на mobile ниже). dashboard-base читает --header-height. */
    --header-height: var(--ds-header-h);
    --nav-sidebar-collapsed-width: var(--ds-nav-rail-w);
    /* Панели каркаса: перекрываем legacy 320px. */
    --sidebar-left-width: var(--ds-panel-left-w);
    --sidebar-right-width: var(--ds-panel-right-w);
}

/* Рельс: nav position:fixed (база). Раскрытая ширина — 272 overlay. */
body.ds-shell .nav-sidebar {
    width: var(--ds-nav-overlay-w);
    box-shadow: var(--shadow-xl);
}
body.ds-shell .nav-sidebar.collapsed {
    width: var(--ds-nav-rail-w);
    box-shadow: none;
}

/* Контент НЕ двигается при раскрытии: отступ всегда = рельс. */
body.ds-shell .dashboard-shell,
html.nav-sidebar-is-expanded body.ds-shell .dashboard-shell,
html.nav-sidebar-is-collapsed body.ds-shell .dashboard-shell {
    margin-left: var(--ds-nav-rail-w);
}

/* ========================================================================
   2. Хедер: 56px desktop / 48px mobile, типографика §6.5
   ======================================================================== */

body.ds-shell .dashboard-header .header-left h1 {
    font: var(--ds-font-card-title); /* 16/24·600 — компактный заголовок шапки */
}

body.ds-shell .dashboard-header {
    height: var(--ds-header-h);
    padding: 0 var(--ds-space-4);
}

/* Шапка — полоса ФИКСИРОВАННОЙ высоты: на --ds-header-h завязаны top и
   height боковых панелей (см. §«рельс» ниже: top: var(--ds-header-h),
   height: calc(100vh - var(--ds-header-h))). Поэтому содержимое обязано
   помещаться в один ряд — перенос здесь рисуется СНАРУЖИ полосы, поверх
   контента (overflow у шапки visible).
   Замерено до правки: контент левой группы 62-81px в полосе 56px, вылет
   до 13px на 1024px и до 47px на 390px — ровно то «наложение», которое
   видит пользователь. Растить шапку нельзя: панели остались бы на старом
   отступе и наложение просто переехало бы на них. */
/* Перебиваем dashboard-base.css:
     body[data-dashboard-theme] .dashboard-header .header-left:has(.dashboard-contextbar)
       { flex-wrap: wrap }
   Оно делало левую группу ДВУХРЯДНОЙ внутри однорядной полосы фиксированной
   высоты — вторая строка и есть «наложение». Специфичность там (0,4,0), поэтому
   короткое правило её не перебивает: добавлен [data-dashboard-theme] → (0,5,1).
   Проверено замером в браузере, а не чтением CSS: первая версия этой правки
   выглядела верной и НЕ применялась. */
body.ds-shell .dashboard-header .header-left,
body.ds-shell .dashboard-header .header-right,
body.ds-shell[data-dashboard-theme] .dashboard-header .header-left,
body.ds-shell[data-dashboard-theme] .dashboard-header .header-left:has(.dashboard-contextbar),
body.ds-shell[data-dashboard-theme] .dashboard-header .header-right {
    flex-wrap: nowrap;
    min-width: 0;
}
/* Пассивные показатели (погода/статус/жители) не переносятся внутри себя:
   двухстрочный «Статус: Черновик» растил правую группу до 59px в полосе 56px. */
body.ds-shell .dashboard-header .header-meta { flex-wrap: nowrap; }
body.ds-shell .dashboard-header .header-meta .meta-item { white-space: nowrap; }

/* Контролы шапки не сжимаются. Без этого nowrap даёт ХУДШИЙ исход, чем
   перенос: флексбокс давит элементы вместо переполнения — замерено на 1024px,
   «ИИ-Мэр» 102→35px, «На главную карту» 179→44px, тема 30→19px, то есть
   кнопки превращались в нечитаемые огрызки. Побочно это ослепляло свёртку в
   «⋯»: она включается по scrollWidth > clientWidth, а сжатие держит их
   равными, и меню не срабатывало никогда.
   Теперь элементы сохраняют размер, честно переполняют полосу — и лишние
   уезжают в «⋯». */
body.ds-shell .dashboard-header .header-right > *,
body.ds-shell .dashboard-header .ds-actions-slot > * {
    flex-shrink: 0;
}

/* Кто уступает место. Обе группы нерастяжимы — и левая начинала наезжать на
   правую (замерено: 3 перекрытия на 1280px, 7 на 1024px). Роли разведены:
   контекст слева УЖИМАЕТСЯ и обрезается, контролы справа держат размер, а
   лишние уезжают в «⋯». */
body.ds-shell .dashboard-header .header-left {
    flex: 1 1 auto;
    overflow: hidden;
    /* Контекст «кто я и где я» — приоритетнее кнопок. Без нижней границы
       right-группа (мета 470px + действия) съедала всё место, и на 1024px от
       левой группы оставалось «Па…»: ни роли, ни города. Проверено снимком —
       числа этого не показывали, обрезанный элемент по-прежнему имеет
       геометрию. Теперь при нехватке места уезжают ДЕЙСТВИЯ в «⋯», а не
       контекст в никуда. */
    min-width: 200px;
}
/* На узком экране 200px левой группе взять неоткуда: 200 + правая группа
   (3 контрола ≈ 140px) + отступы > 390px, и группы налезают друг на друга —
   замерено 10px пересечения, кнопка техданных при этом становится
   некликабельной (elementFromPoint возвращает соседа). Полосу контекста тут
   убираем: город уже назван переключателем «Палех ▾», а техданные доступны на
   широком экране. */
@media (max-width: 640px) {
    body.ds-shell .dashboard-header .header-left { min-width: 0; }
    body.ds-shell .dashboard-header .dashboard-contextbar { display: none; }
    /* Переключатель ужимается сам, иначе его коробка заезжает под кнопку темы
       на 10px и шеврон «▾» срезается (замерено; обе кнопки при этом остаются
       кликабельными, но выглядит как обрезка). */
    body.ds-shell .dashboard-header .survey-switcher-btn { max-width: 34vw; }
    /* Левой группе достаётся 80px, а кнопке с отступом нужно 90 — ровно эти
       10px и срезали шеврон. */
    body.ds-shell .dashboard-header .survey-switcher { margin-left: 0; }
}
body.ds-shell .dashboard-header .header-right {
    flex: 0 0 auto;
}
/* Заголовок роли тоже обрезается, а не выталкивает контекст — но не до
   одной буквы: на снимке 02.09 «Житель» ужался до «Ж», а свитчер
   исследования — до «…». Нижняя граница в 6ch держит слово читаемым; при
   нехватке места левая группа переполняется (scrollWidth > clientWidth),
   и JS-замер в _base_dashboard уводит кнопки справа в «⋯». */
body.ds-shell .dashboard-header .header-left h1 {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 6ch;
}
/* Полоса контекста ужимается и обрезается многоточием, а не переносится. */
body.ds-shell .dashboard-header .dashboard-contextbar {
    min-width: 0;
    overflow: hidden;
}
body.ds-shell .dashboard-header .dashboard-contextbar .cb-segs {
    flex-wrap: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* ========================================================================
   3. Компоновка OVERVIEW — центр ≤1440px, без постоянной правой панели
   ======================================================================== */

body.ds-shell.ds-layout-overview .dashboard-content {
    max-width: var(--ds-content-max);
    margin-left: auto;
    margin-right: auto;
    width: 100%;
    padding: var(--ds-space-5);
}

/* Правая панель в overview постоянной быть не может (ТЗ §6.3). Если
   легаси-разметка её отдала — рендерим как узкую секцию ПОД контентом,
   а не съедаем центр.

   flex-direction:column, НЕ flex-wrap:wrap. Прежний flex-wrap ломал скролл:
   в row-контейнере с wrap единственный `.dashboard-content` растягивался на
   ПОЛНУЮ высоту контента (align-items:stretch по высоте линии = высота
   содержимого), его overflow:auto не срабатывал, а `.dashboard-main`
   (overflow:hidden) просто обрезал низ — страница не скроллилась ВООБЩЕ
   (дашборды Менеджера ОКЦ и Предпринимателя, единственные overview без правой
   панели). Проверено рендером в Chromium: wrap → content.clientH == scrollH
   (не скроллится); column → content зажат по высоте вьюпорта, overflow:auto
   работает, а легаси-панель-справа (order:10, width:100%) встаёт колонкой ПОД
   контентом — та же цель без поломки высоты. */
body.ds-shell.ds-layout-overview .dashboard-main {
    flex-direction: column;
}
body.ds-shell.ds-layout-overview .dashboard-sidebar-right {
    width: 100%;
    max-height: 40vh;
    border-left: none;
    border-top: 1px solid var(--border);
    order: 10;
}

/* ========================================================================
   4. Компоновки WORKSPACE и CONSOLE — панели 288/360, canvas растёт
   (семантика различается контентом; каркас одинаковый)
   ======================================================================== */

body.ds-shell.ds-layout-workspace .dashboard-sidebar-left,
body.ds-shell.ds-layout-console .dashboard-sidebar-left {
    width: var(--ds-panel-left-w);
}

body.ds-shell.ds-layout-workspace .dashboard-sidebar-right,
body.ds-shell.ds-layout-console .dashboard-sidebar-right {
    width: var(--ds-panel-right-w);
    max-width: var(--ds-inspector-max-w);
}

/* Canvas: центр не сжимается ниже нуля и забирает всё остальное. */
body.ds-shell.ds-layout-workspace .dashboard-content,
body.ds-shell.ds-layout-console .dashboard-content {
    flex: 1 1 auto;
    min-width: 0;
}

/* ========================================================================
   5. Breakpoints (ТЗ §6.4)
   ======================================================================== */

/* 1024–1679: inspector (правая панель) — drawer. Открытие: .open
   (тот же контракт, что мобильный режим базы) либо ручка sidebar-collapse. */
@media (min-width: 1024px) and (max-width: 1679px) {
    body.ds-shell.ds-layout-workspace .dashboard-sidebar-right,
    body.ds-shell.ds-layout-console .dashboard-sidebar-right {
        position: fixed;
        top: var(--ds-header-h);
        right: 0;
        height: calc(100vh - var(--ds-header-h));
        transform: translateX(100%);
        transition: transform 0.25s ease;
        z-index: var(--z-fixed);
        box-shadow: var(--shadow-lg);
    }
    body.ds-shell.ds-layout-workspace .dashboard-sidebar-right.open,
    body.ds-shell.ds-layout-console .dashboard-sidebar-right.open {
        transform: translateX(0);
    }
}

/* 768–1023: обе панели — drawer, контент в один столбец.
   (Перекрывает базовый max-width:1200 column-режим, который панели
   складывал стопкой и съедал 60vh высоты.) */
@media (min-width: 768px) and (max-width: 1023px) {
    body.ds-shell.ds-layout-workspace .dashboard-main,
    body.ds-shell.ds-layout-console .dashboard-main {
        flex-direction: row; /* контент во всю ширину, панели off-canvas */
    }
    body.ds-shell.ds-layout-workspace .dashboard-sidebar-left,
    body.ds-shell.ds-layout-console .dashboard-sidebar-left,
    body.ds-shell.ds-layout-workspace .dashboard-sidebar-right,
    body.ds-shell.ds-layout-console .dashboard-sidebar-right {
        position: fixed;
        top: var(--ds-header-h);
        height: calc(100vh - var(--ds-header-h));
        width: min(var(--ds-panel-right-w), 85vw);
        max-height: none;
        border: none;
        transition: transform 0.25s ease;
        z-index: var(--z-fixed);
        box-shadow: var(--shadow-lg);
    }
    body.ds-shell.ds-layout-workspace .dashboard-sidebar-left,
    body.ds-shell.ds-layout-console .dashboard-sidebar-left {
        left: 0;
        transform: translateX(-100%);
    }
    body.ds-shell.ds-layout-workspace .dashboard-sidebar-right,
    body.ds-shell.ds-layout-console .dashboard-sidebar-right {
        right: 0;
        transform: translateX(100%);
    }
    body.ds-shell .dashboard-sidebar-left.open { transform: translateX(0); }
    body.ds-shell .dashboard-sidebar-right.open { transform: translateX(0); }
}

/* ── Edge-переключатели drawer-панелей (static/js/shared/ds-shell.js).
   Скрыты по умолчанию; показываются ТОЛЬКО в диапазонах, где панель —
   drawer (иначе она была бы недоступна — редукция). ── */
.ds-drawer-toggle {
    display: none;
    position: fixed;
    top: 50%;
    transform: translateY(-50%);
    z-index: calc(var(--z-fixed) + 1);
    width: 22px;
    height: 64px;
    border: 1px solid var(--border);
    background: var(--bg-primary);
    color: var(--text-muted);
    cursor: pointer;
    align-items: center;
    justify-content: center;
    font-size: 13px;
    box-shadow: var(--shadow-md);
    transition: right 0.25s ease, left 0.25s ease, background 0.15s;
}
.ds-drawer-toggle:hover { background: var(--bg-secondary); color: var(--text-primary); }
.ds-drawer-toggle--left  { left: 0;  border-radius: 0 var(--radius-md) var(--radius-md) 0; border-left: none; }
.ds-drawer-toggle--right { right: 0; border-radius: var(--radius-md) 0 0 var(--radius-md); border-right: none; }
/* Открытый drawer сдвигает свою кнопку к внутреннему краю панели. */
body.ds-shell.ds-drawer-open-left  .ds-drawer-toggle--left  { left: min(var(--ds-panel-right-w), 85vw); }
body.ds-shell.ds-drawer-open-right .ds-drawer-toggle--right { right: min(var(--ds-panel-right-w), 85vw); }

/* Правая панель — drawer на 768–1679 → кнопка видима. */
@media (min-width: 768px) and (max-width: 1679px) {
    body.ds-shell.ds-layout-workspace .ds-drawer-toggle--right,
    body.ds-shell.ds-layout-console .ds-drawer-toggle--right { display: flex; }
}
/* Левая панель — drawer только на 768–1023. */
@media (min-width: 768px) and (max-width: 1023px) {
    body.ds-shell.ds-layout-workspace .ds-drawer-toggle--left,
    body.ds-shell.ds-layout-console .ds-drawer-toggle--left { display: flex; }
}
/* СИСТЕМНЫЙ ФИКС (аудит мобилок 2026-07-18): на <768 ОБЕ панели off-canvas
   (dashboard-base.css:770 left/right:-100%, .open → 0), НО раньше кнопки-
   переключатели тут были display:none — панели становились НЕДОСТИЖИМЫ на
   телефоне. Именно поэтому канонические analyst/producer «ломались» на
   смартфоне (сайдбары есть, открыть нечем). Показываем оба тоггла и на
   телефоне — ds-shell.js их уже создал и подвязал (.open сдвигает панель). */
@media (max-width: 767px) {
    body.ds-shell.ds-layout-workspace .ds-drawer-toggle--left,
    body.ds-shell.ds-layout-console .ds-drawer-toggle--left,
    body.ds-shell.ds-layout-workspace .ds-drawer-toggle--right,
    body.ds-shell.ds-layout-console .ds-drawer-toggle--right { display: flex; }
}

/* <768: header 48; рельс скрыт (мобильный hamburger из базы работает как есть). */
@media (max-width: 767px) {
    body.ds-shell {
        --header-height: var(--ds-header-h-mobile);
    }
    body.ds-shell .dashboard-header {
        height: var(--ds-header-h-mobile);
    }
    body.ds-shell .dashboard-shell,
    html.nav-sidebar-is-expanded body.ds-shell .dashboard-shell,
    html.nav-sidebar-is-collapsed body.ds-shell .dashboard-shell {
        margin-left: 0;
    }
    /* Контролы — touch-высота (ТЗ §6.4). */
    body.ds-shell .dashboard-header .header-actions .btn,
    body.ds-shell .dashboard-header .header-actions button {
        min-height: var(--ds-control-touch);
    }
}
/* ── css/kalka-cards.css ── */
/* Калька D-4/D-5/D-6 — стили карточки данных/прогноза/качества.
   Ред. 1 · 17.08.2026. Канон вёрстки — gd-styleguide.html (.card/.src/.disc/
   .meter), классы новые (gd-card-*), легаси не задевают, поэтому файл
   грузится ВСЕГДА (не за флагом): каждое значение — var(--gd-*, фолбэк на
   легаси-переменную), без флага карточки живут в старой палитре.
   Ни одного hex. Скругления: 0 by design. */

.gd-card{
  position:relative;
  background:var(--gd-field, var(--bg-card, var(--bg-secondary)));
  border:1px solid var(--gd-ink, var(--text-primary));
  color:var(--gd-ink, var(--text-primary));
  padding:var(--gd-s3, 12px);
  border-radius:0;
}
.gd-card-mark{position:absolute;left:0;top:0;bottom:0;width:5px}
.gd-mark-ink .gd-card-mark{background:var(--gd-ink, var(--text-primary))}
.gd-mark-water .gd-card-mark{background:var(--gd-water, var(--info))}
.gd-mark-signal .gd-card-mark{background:var(--gd-signal, var(--danger))}
.gd-card h3{
  font-size:var(--gd-t-h2, 19px);line-height:1.2;letter-spacing:-.01em;
  padding-left:6px;margin:0;
}
.gd-eyebrow{display:flex;align-items:center;gap:8px;margin-bottom:6px;padding-left:6px}
.gd-card-b{padding-left:6px;margin-top:6px}
.gd-card-b ul{margin:6px 0 0;padding-left:18px}
.gd-card-b li{margin-bottom:4px}

.gd-scenario{display:flex;gap:8px;align-items:flex-start;margin-top:8px;padding-left:6px}
.gd-scenario .gd-conf{flex:0 0 auto;margin-top:2px}

.gd-src{
  border-top:1px solid var(--gd-hair, var(--border));
  margin-top:var(--gd-s3, 12px);padding-top:6px;padding-left:6px;
}
.gd-src ul{margin:4px 0 0;padding-left:18px}
.gd-src li{font-size:var(--gd-t-small, 13.5px);word-break:break-word;margin-bottom:2px}
.gd-src a{color:var(--gd-water, var(--info))}

.gd-disc{
  border-left:4px solid var(--gd-signal, var(--danger));
  padding:6px 10px;margin-top:var(--gd-s2, 8px);
  font-family:var(--gd-mono, var(--font-mono));font-size:9.5px;line-height:1.6;
  letter-spacing:.03em;color:var(--gd-muted, var(--text-muted));
}

.gd-quality{
  display:flex;flex-wrap:wrap;gap:12px;align-items:center;
  margin-top:var(--gd-s2, 8px);padding-left:6px;
}
.gd-quality-meta{
  font-family:var(--gd-mono, var(--font-mono));font-size:10px;
  color:var(--gd-muted, var(--text-muted));
}
.gd-meter{display:inline-flex;gap:2px}
.gd-meter span{
  width:9px;height:12px;display:block;
  border:1px solid var(--gd-edge, var(--border-dark, var(--border)));
}
.gd-meter span.on{
  background:var(--gd-ink, var(--text-primary));
  border-color:var(--gd-ink, var(--text-primary));
}

/* gd-lbl/gd-tag определены в kalka-components.css (за флагом) — дублируем
   минимум, чтобы карточка была самодостаточна и без флага. */
.gd-card .gd-lbl{
  display:block;font-family:var(--gd-mono, var(--font-mono));font-size:10px;
  letter-spacing:.12em;text-transform:uppercase;
  color:var(--gd-muted, var(--text-muted));margin-bottom:3px;
}
.gd-card .gd-tag{
  display:inline-block;font-family:var(--gd-mono, var(--font-mono));font-size:9px;
  letter-spacing:.07em;text-transform:uppercase;
  color:var(--gd-muted, var(--text-muted));
  border:1px solid var(--gd-hair, var(--border));padding:2px 5px;border-radius:0;
}
.gd-card .gd-conf{
  display:inline-flex;align-items:center;gap:5px;
  font-family:var(--gd-mono, var(--font-mono));font-size:9px;letter-spacing:.07em;
  text-transform:uppercase;color:var(--gd-muted, var(--text-muted));
  border:1px solid var(--gd-hair, var(--border));padding:2px 5px;
}
.gd-card .gd-conf b{
  width:22px;height:9px;display:block;
  border:1px solid var(--gd-edge, var(--border));
}
.gd-card .gd-conf.high b{background:repeating-linear-gradient(45deg,var(--gd-ink, var(--text-primary)) 0 1px,transparent 1px 3px)}
.gd-card .gd-conf.mid b{background:repeating-linear-gradient(45deg,var(--gd-ink, var(--text-primary)) 0 1px,transparent 1px 5px)}
.gd-card .gd-conf.low b{background:repeating-linear-gradient(45deg,var(--gd-edge, var(--border)) 0 1px,transparent 1px 8px)}

/* ── B-05 · уверенность вывода штриховкой, а не процентом ────────────────
   DESIGN-SYSTEM-kalka.md, строка 132, дословно: «штриховка вместо
   процентов: 3 градации, не 73%». Процент обещает измерение, которого нет:
   0.73 у вывода LLM — не «73 из 100 проверок», а внутреннее число модели.
   Три градации честнее ровно потому, что грубее.

   Блок ПЕРЕЕХАЛ сюда из kalka-components.css (26.08). Тот файл живёт под
   KALKA_ENABLED (дефолт OFF), а уверенность показывается на обычных
   дашбордах — там штриховка не отрисовалась бы вовсе (#17). Значения — с
   фолбэком на легаси-переменные, как весь этот файл: без флага токены
   --gd-* не объявлены, и без фолбэка плашка стала бы прозрачной, то есть
   заменила бы читаемый процент пустотой. */
.gd-conf{
  display:inline-flex;align-items:center;gap:5px;
  font-family:var(--gd-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size:9px;letter-spacing:.07em;text-transform:uppercase;
  color:var(--gd-muted, var(--text-secondary));
  border:1px solid var(--gd-hair, var(--border-color));padding:2px 5px;
}
.gd-conf b{width:22px;height:9px;display:block;
  border:1px solid var(--gd-edge, var(--border-color))}
.gd-conf.high b{background:repeating-linear-gradient(45deg,
  var(--gd-ink, var(--text-primary)) 0 1px,transparent 1px 3px)}
.gd-conf.mid b{background:repeating-linear-gradient(45deg,
  var(--gd-ink, var(--text-primary)) 0 1px,transparent 1px 5px)}
.gd-conf.low b{background:repeating-linear-gradient(45deg,
  var(--gd-edge, var(--border-color)) 0 1px,transparent 1px 8px)}

/* Легенда (DoD B-05: «легенда в подвале дашборда»). Без неё три плотности
   — шум: человек видит разную штриховку и не знает, что она кодирует. */
.gd-conf-legend{
  display:flex;flex-wrap:wrap;align-items:center;gap:var(--gd-s3, 12px);
  font-size:11px;color:var(--gd-muted, var(--text-secondary));
  padding:var(--gd-s3, 12px) 0;
  border-top:1px solid var(--gd-hair, var(--border-color));
}
.gd-conf-legend .gd-conf-legend-title{text-transform:none;letter-spacing:0}
