/* ============================================================
   Цветовые темы через CSS-переменные.
   :root = тёмная тема (значения по умолчанию, из проекта hb1).
   Чтобы добавить светлую тему — переопредели эти же переменные
   в блоке :root[data-theme="light"] { ... } (следующий шаг).
   Структура классов — как во втором проекте (mail_admin).
   ============================================================ */
:root {
    /* Поверхности */
    --bg:              #2b3138;   /* фон страницы */
    --surface:         #333843;   /* панели, шапка, попапы, плеер, zag-строки */
    --surface-2:       #464f5a;   /* поля ввода, вторичные кнопки */
    --surface-2-hover: #555e69;
    --row-even:        #383f48;
    --row-odd:         #2f353d;
    --border:          #2b3138;   /* границы ячеек таблиц (в тёмной теме = фон) */
    --border-2:        #464f5a;   /* границы попапов/полей */

    /* Текст */
    --text:        #4db6bc;   /* основной текст (в тёмной теме совпадает с акцентом) */
    --text-muted:  #b4b5b6;   /* ссылки, вторичный текст */
    --text-dim:    #6a7078;   /* третичный/подсказки */
    --text-bright: #eeeeee;   /* текст в полях ввода */
    --text-strong: #ffffff;   /* самый яркий текст на поверхности (играющая строка) */

    /* Акценты */
    --accent:       #4db6bc;
    --accent-hover: #5cc7cd;
    --on-accent:    #ffffff;   /* текст на кнопках акцентного цвета */
    --share:        #4a7bc7;
    --share-hover:  #5a8bd7;
    --danger:       #b64d4d;
    --danger-hover: #c75c5c;

    /* Семантические подсветки (loudness / битрейт / overflow) */
    --hl-cool:       #6ba6d9;   /* тихо / низкий битрейт (синий) */
    --hl-cool-deep:  #3d5a8c;   /* очень низкий битрейт (тёмно-синий) */
    --hl-warm:       #e6a04d;   /* громко / высокий битрейт (оранжевый) */
    --hl-good:       #5cb85c;   /* битрейт в норме (зелёный) */
    --ok:            #5cb85c;   /* «свежо / в порядке» — пара к --danger */
    --hl-over:       #c74d5c;   /* переполнение / очень высокий битрейт (красный) */
    --hl-over-first: #c48a3a;   /* первый переполнивший (тусклый оранжевый) */
}

/* ── Светлая тема ──────────────────────────────────────────
   Значения применяются либо по явному выбору (data-theme="light"),
   либо по системной теме, если юзер не выбрал явно (data-theme отсутствует).
   Один и тот же набор переменных продублирован в двух селекторах. */
:root[data-theme="light"] {
    --bg:              #f4f5f7;
    --surface:         #ffffff;
    --surface-2:       #e7e9ee;
    --surface-2-hover: #dadde4;
    --row-even:        #eef0f3;
    --row-odd:         #f7f8fa;
    --border:          #e2e4e9;
    --border-2:        #d5d8df;

    --text:        #2b3138;
    --text-muted:  #5a6270;
    --text-dim:    #8a909a;
    --text-bright: #1f2430;
    --text-strong: #000000;

    --accent:       #2e9ba1;
    --accent-hover: #38aeb4;
    --on-accent:    #ffffff;
    --share:        #3f6fb8;
    --share-hover:  #4f80cb;
    --danger:       #c0392b;
    --danger-hover: #d0483a;

    --hl-cool:       #3b7fc4;
    --hl-cool-deep:  #2f4a78;
    --hl-warm:       #b9791b;
    --hl-good:       #3d9140;
    --ok:            #3d9140;
    --hl-over:       #c0392b;
    --hl-over-first: #b06f1e;
}
@media (prefers-color-scheme: light) {
    :root:not([data-theme]) {
        --bg:              #f4f5f7;
        --surface:         #ffffff;
        --surface-2:       #e7e9ee;
        --surface-2-hover: #dadde4;
        --row-even:        #eef0f3;
        --row-odd:         #f7f8fa;
        --border:          #e2e4e9;
        --border-2:        #d5d8df;

        --text:        #2b3138;
        --text-muted:  #5a6270;
        --text-dim:    #8a909a;
        --text-bright: #1f2430;
        --text-strong: #000000;

        --accent:       #2e9ba1;
        --accent-hover: #38aeb4;
        --on-accent:    #ffffff;
        --share:        #3f6fb8;
        --share-hover:  #4f80cb;
        --danger:       #c0392b;
        --danger-hover: #d0483a;

        --hl-cool:       #3b7fc4;
        --hl-cool-deep:  #2f4a78;
        --hl-warm:       #b9791b;
        --hl-good:       #3d9140;
        --ok:            #3d9140;
        --hl-over:       #c0392b;
        --hl-over-first: #b06f1e;
    }
}

body {
    background: var(--bg);
    color: var(--text);
    font-family: sans-serif;
    margin: 0;
}
h1, h2, h3, h4, p {
    background: transparent;
    color: var(--accent);
}
h2, h3 { padding: 6px 12px; }

A          { color: var(--text-muted); text-decoration: none; }
A:visited  { color: var(--text-muted); }
A:active   { color: var(--text-muted); }
A:hover    { color: var(--accent); }

/* ============ Формы ============ */
form input[type=text],
form input[type=url],
form input[type=password],
form input[type=number] {
    background: var(--surface-2);
    color: var(--text-bright);
    border: 1px solid var(--surface-2);
    border-radius: 5px;
    padding: 4px 8px;
}
form input[type=submit] {
    background: var(--accent);
    color: var(--on-accent);
    border: 1px solid var(--accent);
    border-radius: 5px;
    padding: 4px 12px;
    cursor: pointer;
}
form input[type=submit]:hover { background: var(--accent-hover); border-color: var(--accent-hover); }

select, form select {
    background: var(--accent);
    color: var(--on-accent);
    border: 1px solid var(--accent);
    border-radius: 5px;
    padding: 3px 6px;
}
form textarea, textarea {
    background: var(--surface-2);
    color: var(--text-bright);
    border: 1px solid var(--surface-2);
    border-radius: 5px;
    padding: 4px 8px;
}

/* ============ Таблицы ============ */
TR.even   { background: var(--row-even); color: var(--accent); }
TR.noeven { background: var(--row-odd);  color: var(--accent); }
TR.zag    { background: var(--surface);  color: var(--text-muted); font-size: large; font-weight: bold; }
TR.rr, TR.zh, TR.gg { background: var(--bg); }


/* ============ Кнопки, формы логина, общие классы (из образца) ============ */
/* ============ Bootstrap-подобные классы для login.php ============ */
body.text-center { text-align: center; }
.form-signin {
    display: inline-block;
    width: 100%;
    max-width: 340px;
    padding: 30px 20px;
    margin-top: 80px;
    background: var(--surface);
    border-radius: 8px;
    box-shadow: 0 4px 20px rgba(0,0,0,0.3);
    text-align: left;
}
.form-signin h1 {
    text-align: center;
    font-size: 18px;
    color: var(--accent);
    margin-bottom: 20px;
}
.form-control {
    width: 100%;
    box-sizing: border-box;
    margin-bottom: 10px;
    padding: 8px 10px;
    background: var(--surface-2) !important;
    color: var(--text-bright) !important;
    border: 1px solid var(--surface-2) !important;
    border-radius: 5px;
    font-size: 14px;
}
.btn { display: inline-block; padding: 6px 14px; border-radius: 5px; cursor: pointer; text-decoration: none; }
.btn-primary   { background: var(--accent); color: var(--on-accent) !important; border: 1px solid var(--accent); }
.btn-primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
.btn-danger    { background: var(--danger); color: var(--on-accent) !important; border: 1px solid var(--danger); }
.btn-danger:hover { background: var(--danger-hover); border-color: var(--danger-hover); }
.btn-secondary { background: var(--surface-2); color: var(--text-muted) !important; border: 1px solid var(--surface-2); }
.btn-secondary:hover { background: var(--surface-2-hover); border-color: var(--surface-2-hover); color: var(--on-accent) !important; }
/* Пара кнопок «подтвердить / закрыть». Именно flex, а не inline-block: <button> и <a>
   с одинаковым классом выравниваются по базовой линии по-разному, и ссылка встаёт выше
   кнопки на пару пикселей. */
.btn-row { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
/* Тумблер стороны кассеты — две кнопки без промежутка (segmented control) */
.side-switch { display: inline-flex; }
.side-switch .btn { border-radius: 0; padding: 4px 12px; }
.side-switch .btn:first-child { border-radius: 5px 0 0 5px; }
.side-switch .btn:last-child  { border-radius: 0 5px 5px 0; }
.btn:disabled, button.btn[disabled] { opacity: 0.4; cursor: not-allowed; }
.btn:disabled:hover { background: inherit; }
.btn.busy { opacity: 0.7; pointer-events: none; cursor: wait; }

/* Спиннер для «идёт долгая операция» — вращающееся кольцо в цвете кнопки */
.spinner {
    display: inline-block; width: 14px; height: 14px;
    border: 2px solid currentColor; border-top-color: transparent;
    border-radius: 50%; vertical-align: middle;
    animation: spin 0.9s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
/* Кнопка с одной иконкой (без текста) — убираем правый отступ у SVG */
.btn-icon      { padding: 4px 8px; }
.btn-icon svg  { margin-right: 0 !important; display: block; }

/* action-bar — все элементы на одной линии по центру */
.action-bar        { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.action-bar form   { margin: 0; }

/* ============ Таблица-список ============ */
.picked-list       { border-collapse: collapse; }
.picked-list td    { padding: 4px 10px; border-bottom: 1px solid var(--border); height: 22px; box-sizing: border-box; }
.picked-list tr.zag td { font-weight: bold; }
/* Столбец состояния: ширина по содержимому (одна точка), правый отступ узкий —
   иначе между точкой и названием зияет дыра в две ширины отступа. */
.picked-list td.dot-col { width: 1%; padding-right: 2px; text-align: center; }


/* ============ Всплывающее уведомление и мелочи ============ */
.flash-toast {
    position: fixed;
    bottom: 16px;
    right: 16px;
    padding: 5px 12px;
    background: var(--surface);
    color: var(--accent);
    border-left: 3px solid var(--accent);
    border-radius: 4px;
    font-size: 12px;
    box-shadow: 0 2px 8px rgba(0,0,0,0.4);
    z-index: 1000;
    transition: opacity 0.5s;
}
.flash-toast.fade-out { opacity: 0; pointer-events: none; }

.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); border: 0; }
.text-muted { color: var(--text-dim); text-align: center; font-size: 12px; }
.mt-5 { margin-top: 24px; }
.mb-3 { margin-bottom: 12px; }

/* ============================================================
   Своё для «Вышки»: шапка, раскладка, адаптив под телефон.
   Сайт ставится на телефон как приложение (PWA), поэтому:
   — учитываем «чёлку» iPhone через safe-area-inset (работает вместе
     с viewport-fit=cover в мета-теге viewport);
   — цели нажатия не меньше 40px;
   — широкие таблицы скроллятся внутри себя, страница по горизонтали не едет.
   ============================================================ */

.topbar {
    display: flex;
    align-items: center;
    gap: 8px;
    background: var(--surface);
    padding: 8px 12px;
    padding-top: calc(8px + env(safe-area-inset-top));
    padding-left: calc(12px + env(safe-area-inset-left));
    padding-right: calc(12px + env(safe-area-inset-right));
    position: sticky;
    top: 0;
    z-index: 10;
}
.topbar .brand {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    color: var(--accent);
    font-size: 17px;
    white-space: nowrap;
}
.topbar .brand svg { width: 28px; height: 28px; color: var(--accent); flex: 0 0 auto; }
.topbar nav { display: flex; align-items: center; gap: 4px; margin-left: 12px; }
.topbar nav a {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 40px;
    min-height: 40px;
    border-radius: 6px;
    color: var(--text-muted);
}
.topbar nav a svg { width: 20px; height: 20px; }
.topbar nav a:hover  { color: var(--accent); }
.topbar nav a.active { color: var(--accent); background: var(--bg); }
.topbar .spacer { flex: 1 1 auto; }
.topbar .who {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    min-height: 40px;
    padding: 0 8px;
    border-radius: 6px;
    color: var(--text-muted);
    font-size: 13px;
    white-space: nowrap;
    max-width: 40vw;
}
.topbar .who svg    { width: 18px; height: 18px; flex: 0 0 auto; }
.topbar .who span   { overflow: hidden; text-overflow: ellipsis; }
.topbar .who:hover  { color: var(--accent); }
.topbar .who.active { color: var(--accent); background: var(--bg); }

.container {
    max-width: 1100px;
    margin: 0 auto;
    padding: 12px 16px calc(24px + env(safe-area-inset-bottom));
}

/* Карточки будущих разделов (телеметрия, шлюз, отчёты) */
.cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 12px; }
/* Обзор («главная»): плитки на треть шире обычных — 320 px вместо 240. На ней плитки не
   однострочные, в них по несколько строк подробностей (здоровье сервера, шлюз), и на узкой
   колонке они рвались посреди значения. Модификатор отдельным классом, а не правкой .cards:
   в разделах сетка та же и должна остаться прежней. */
.cards.wide-grid { grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); }
.card {
    background: var(--surface);
    border-radius: 8px;
    padding: 14px 16px;
    color: var(--text-muted);
}
/* Плитка в полтора раза шире соседних. Grid умеет только ЦЕЛЫЕ колонки, поэтому сетку
   режем на половинные: обычная плитка занимает две, широкая — три. Иначе «шире на 50 %»
   средствами grid не выразить вовсе.
   Только на широком экране: на телефоне колонка и так одна, и деление пополам сделало бы
   раскладку случайной. */
@media (min-width: 900px) {
    .cards.half-grid            { grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); }
    .cards.half-grid > .card    { grid-column: span 2; }
    .cards.half-grid > .card.wide { grid-column: span 3; }
}
.card h3 { margin: 0 0 6px; padding: 0; font-size: 16px; }
/* Иконки в заголовках плиток — svg в currentColor, красятся темой (в отличие от эмодзи,
   у которых свой фон внутри глифа и на тёмной теме он светится). */
.card h3 svg { width: 17px; height: 17px; vertical-align: middle; margin-right: 6px; }
.card .muted { color: var(--text-dim); font-size: 13px; }
.card.todo { opacity: 0.75; border: 1px dashed var(--border-2); background: transparent; }
/* Плитка-кат: заголовок сам раскрывает содержимое. Маркер оставлен штатный —
   он единственное, что отличает раскрываемую плитку от обычной. h3 делаем строчным,
   иначе блочный заголовок уезжает со строки маркера и стрелка висит сама по себе.
   Закрытому кату нижний отступ заголовка не нужен: под ним ничего нет. */
.card.cut > summary { cursor: pointer; }
.card.cut > summary h3 { display: inline-block; margin-bottom: 0; vertical-align: baseline; }
.card.cut[open] > summary h3 { margin-bottom: 6px; }
/* Плитка-ссылка (список разделов админки). Цвет текста у .card и у A совпадает, поэтому
   кликабельность показываем только на наведении — иначе плитка ничем не отличается от обычной. */
a.card:hover        { background: var(--row-even); }
a.card:hover h3     { color: var(--accent); }

/* ── Сканер QR с наклейки (страница регистрации датчика) ────────────────
   Оверлей намеренно ЧЁРНЫЙ в любой теме: под ним живое видео с камеры,
   и светлая подложка вокруг кадра только мешает целиться.
   z-index выше шапки (1000), иначе она проступает поверх камеры. */
.scan-ui {
    display: none;              /* показывается скриптом (flex) */
    position: fixed;
    top: 0; left: 0; right: 0; bottom: 0;
    width: 100%;
    height: 100%;               /* запасной вариант для iOS без dvh */
    height: 100dvh;             /* iOS 16+: высота С УЧЁТОМ панелей Safari, иначе низ уезжает за экран */
    z-index: 1100;              /* выше шапки (1000) — она не должна проступать поверх камеры */
    background: #000;           /* непрозрачный: под оверлеем не должно быть видно ничего лишнего */
    flex-direction: column;     /* 🔴 без него дети встают В СТРОКУ и подписи уезжают за правый край */
    align-items: center;
    justify-content: center;
    gap: 10px;
    padding: calc(10px + env(safe-area-inset-top)) 12px calc(10px + env(safe-area-inset-bottom));
    box-sizing: border-box;
    overflow: hidden;
}
/* Стойка обжимает видео вплотную: object-fit не задаём, поэтому размер элемента
   совпадает с картинкой — и рамка прицела ложится ровно туда, где её ждёт скрипт. */
.scan-stage { position: relative; line-height: 0; max-width: 100%; }
/* 🔴 Ограничения обязательны: поток приходит 1280x720, и без них видео растягивается
   в натуральную величину — на телефоне это в несколько раз шире экрана. */
.scan-ui video {
    display: block;
    width: auto; height: auto;
    max-width: 92vw;
    max-height: 46vh;
    max-height: 46dvh;
    border-radius: 8px;
    background: #000;
}
/* Рамка прицела. Сторона = 60% меньшей стороны кадра — ровно тот вырез, который уходит
   в распознаватель. Размер в процентах нужен на первые кадры, дальше скрипт ставит точный
   в пикселях. Огромная тень затемняет всё вокруг рамки: видно, что именно разбирается. */
.scan-box {
    position: absolute;
    left: 50%; top: 50%;
    transform: translate(-50%, -50%);
    height: 60%;
    aspect-ratio: 1 / 1;
    max-width: 60%;
    border: 2px solid var(--accent);
    border-radius: 6px;
    box-shadow: 0 0 0 9999px #00000059;
    pointer-events: none;
}
.scan-msg  { color: #fff;    font-size: 15px; text-align: center; margin: 0; }
.scan-hint { color: #b4b5b6; font-size: 13px; text-align: center; max-width: 420px; margin: 0; }
/* Закрытие — в углу поверх кадра: занимать им строку под видео жалко, высоты на телефоне
   и так впритык. Отступ сверху с учётом «чёлки». */
.scan-close {
    position: absolute;
    top: calc(10px + env(safe-area-inset-top));
    right: 12px;
    z-index: 1;
}

/* ── Карта датчика ────────────────────────────────────────────────────── */
/* Оверлей по образцу сканера, но фон НЕ чёрный: карта светлая сама по себе,
   и чёрные поля вокруг неё смотрелись бы дырой в странице. */
.map-ui {
    display: none;              /* показывается скриптом (flex) */
    position: fixed;
    top: 0; left: 0; right: 0; bottom: 0;
    width: 100%;
    height: 100%;               /* запасной вариант для iOS без dvh */
    height: 100dvh;             /* иначе низ уезжает под панели Safari */
    z-index: 1100;              /* выше шапки (1000) */
    background: var(--bg);
    flex-direction: column;     /* без него дети встают в строку */
    gap: 10px;
    padding: calc(10px + env(safe-area-inset-top)) 12px calc(10px + env(safe-area-inset-bottom));
    box-sizing: border-box;
}
.map-ui .map-head {
    display: flex; align-items: center; gap: 10px;
    padding-right: 44px;        /* место под кнопку закрытия, иначе имя лезет под неё */
    color: var(--text-muted); font-size: 14px;
}
/* 🔴 Карте нужна ЯВНАЯ высота: контейнер нулевой высоты Яндекс отрисует пустым,
   и это выглядит как «ключ не подошёл». flex:1 отдаёт ей весь остаток оверлея. */
.map-stage {
    flex: 1 1 auto;
    width: 100%; min-height: 200px;
    border-radius: 8px; overflow: hidden;
    background: var(--surface-2);
}
.map-close { position: absolute; top: calc(10px + env(safe-area-inset-top)); right: 12px; z-index: 1; }
/* Легенда общей карты: кружок цвета метки + подпись типа. Переносится по строкам —
   на телефоне четыре типа в одну строку не помещаются. */
.map-legend {
    display: flex; flex-wrap: wrap; gap: 6px 16px;
    font-size: 13px; color: var(--text-muted);
}
.map-legend span { display: inline-flex; align-items: center; gap: 6px; }
/* Кружок цвета в таблице типов датчиков (админка). Тот же вид, что у легенды карты —
   человек должен узнавать один и тот же цвет в обоих местах. */
.kind-dot {
    display: inline-block; width: 10px; height: 10px; border-radius: 50%;
    margin-right: 8px; vertical-align: middle;
    box-shadow: 0 0 0 1px var(--border);
}
.map-legend i {
    width: 10px; height: 10px; border-radius: 50%;
    /* Обводка нужна на светлой теме: жёлтая метка на белом почти не видна. */
    box-shadow: 0 0 0 1px var(--border);
}

/* ── Раздел «Датчики»: список слева, карточка справа ──────────────────── */
.split { display: grid; grid-template-columns: minmax(200px, 280px) 1fr; gap: 12px; align-items: start; }
.split-list  { min-width: 0; }
.split-detail { min-width: 0; }
/* Высота графика задаётся контейнером: canvas у Chart.js сам по себе тянется
   бесконечно при maintainAspectRatio: false. */
.chartwrap { position: relative; height: 300px; margin-top: 8px; }

/* Любая широкая таблица оборачивается в <div class="tablewrap"> */
.tablewrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }

/* ── Телефон ───────────────────────────────────────────── */
@media (max-width: 640px) {
    .topbar .brand span { display: none; }   /* оставляем только логотип */
    .topbar nav { margin-left: 4px; gap: 2px; }
    .topbar .who { font-size: 12px; max-width: 32vw; padding: 0 4px; }
    .container { padding: 10px 12px calc(20px + env(safe-area-inset-bottom)); }
    h2, h3 { padding: 4px 0; }

    /* Формы: подписи над полями, поля во всю ширину.
       16px у input — чтобы iOS не зумил страницу при фокусе. */
    .form-grid td { display: block; text-align: left !important; padding: 2px 0 !important; }
    .form-grid input[type=text],
    .form-grid input[type=email],
    .form-grid input[type=password],
    .form-grid input[type=number] { width: 100% !important; box-sizing: border-box; font-size: 16px; }
    .btn { padding: 8px 16px; }
    .btn-icon { padding: 8px 12px; }

    /* На узком экране высоты мало: видео поменьше, подписи компактнее — иначе кнопки
       «Сфотографировать» и «Закрыть» оказываются ниже нижней кромки. */
    .scan-ui video { max-height: 42vh; max-height: 42dvh; }
    .scan-msg  { font-size: 14px; }
    .scan-hint { font-size: 12px; }

    /* Две колонки раздела «Датчики» складываются в одну: список сверху, карточка под ним. */
    .split { grid-template-columns: 1fr; }
    .chartwrap { height: 240px; }
    .form-grid select { width: 100% !important; box-sizing: border-box; font-size: 16px; }
}

/* В установленном приложении (standalone) шапка чуть плотнее — адресной строки нет */
@media (display-mode: standalone) {
    .topbar { padding-top: calc(6px + env(safe-area-inset-top)); }
}

/* ── Файлы клиента: список слева, карточка файла справа ───────────────── */
.split.files { grid-template-columns: minmax(0, 1fr) minmax(260px, 340px); }
.split.files .split-detail .card { position: sticky; top: 8px; }
.split.files .split-detail dl { margin: 0; }
.split.files .split-detail dt { color: var(--text-dim); font-size: 12px; margin-top: 8px; }
.split.files .split-detail dd { margin: 0; color: var(--text-muted); word-break: break-word; }
/* Фильтр по типу: кнопки переносятся, на телефоне тоже */
.type-filter { display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: 12px; }
.type-filter .btn { padding: 4px 10px; font-size: 13px; }
@media (max-width: 640px) {
    /* Одна колонка; выбранный файл — над списком, чтобы после тыка не искать его внизу */
    .split.files { grid-template-columns: 1fr; }
    .split.files .split-detail { order: -1; }
    .split.files .split-detail .card { position: static; }
}

/* ── Чат с Claude (desk/claude.php) ──────────────────────────────────────── */
.chat-layout { display: grid; grid-template-columns: 240px minmax(0, 1fr); gap: 12px; align-items: start; }
.chat-side .chat-list { display: flex; flex-direction: column; gap: 4px; max-height: calc(100vh - 220px); overflow-y: auto; }
.chat-item { display: block; padding: 6px 10px; border-radius: 6px; background: var(--surface); color: var(--text-muted); }
.chat-item:hover { background: var(--surface-2); color: var(--text-muted); }
.chat-item.active { background: var(--surface-2); border-left: 3px solid var(--accent); }
.chat-item-client { font-size: 13px; color: var(--accent); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.chat-item-title { font-size: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.chat-client { color: var(--accent); font-size: 15px; }
.chat-item-sub { font-size: 11px; color: var(--text-dim); }
.chat-main { background: var(--surface); border-radius: 8px; padding: 10px 12px; display: flex; flex-direction: column; min-height: calc(100vh - 200px); }
.chat-top { display: flex; align-items: center; gap: 10px; padding-bottom: 8px; border-bottom: 1px solid var(--border-2); margin-bottom: 8px; }
.chat-log { flex: 1; overflow-y: auto; max-height: calc(100vh - 340px); padding: 4px 2px; display: flex; flex-direction: column; gap: 8px; }
.msg { max-width: 92%; }
.msg-user { align-self: flex-end; }
.msg-user .msg-body { background: var(--accent); color: var(--on-accent); border-radius: 10px 10px 2px 10px; padding: 8px 12px; white-space: pre-wrap; }
.msg-ai { align-self: flex-start; }
.msg-ai .msg-body { background: var(--bg); color: var(--text-muted); border-radius: 10px 10px 10px 2px; padding: 8px 12px; }
.msg-ai .msg-body p:first-child { margin-top: 0; } .msg-ai .msg-body p:last-child { margin-bottom: 0; }
.msg-ai .msg-body p, .msg-ai .msg-body li { color: var(--text-muted); }
.msg-ai .msg-body h1, .msg-ai .msg-body h2, .msg-ai .msg-body h3 { padding: 0; margin: 8px 0 4px; font-size: 15px; }
.msg-ai .msg-body pre { background: var(--surface-2); padding: 8px; border-radius: 6px; overflow-x: auto; font-size: 12px; }
.msg-ai .msg-body code { background: var(--surface-2); padding: 1px 4px; border-radius: 3px; font-size: 12px; }
.msg-ai .msg-body pre code { background: none; padding: 0; }
.msg-ai .msg-body table { border-collapse: collapse; } .msg-ai .msg-body td, .msg-ai .msg-body th { border: 1px solid var(--border-2); padding: 3px 8px; }
.msg-tool { align-self: flex-start; font-size: 12px; color: var(--text-dim); }
.msg-tool summary { cursor: pointer; font-family: monospace; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 70vw; }
.msg-tool .tool-name { color: var(--accent); }
.msg-tool pre { margin: 4px 0 0 14px; font-size: 11px; white-space: pre-wrap; max-height: 200px; overflow: auto; color: var(--text-dim); }
.msg-result { align-self: flex-start; font-size: 11px; color: var(--text-dim); padding-left: 4px; }
.msg-result.err { color: var(--danger); }
.chat-input { margin-top: 8px; border-top: 1px solid var(--border-2); padding-top: 8px; }
.chat-input textarea { width: 100%; box-sizing: border-box; resize: vertical; font-size: 14px; margin-bottom: 6px; font-family: inherit; }

/* ── Шапка карточки клиента: во всю ширину, свойства в несколько колонок ─── */
.client-head { margin-bottom: 4px; }
.client-head h3 { font-size: 18px; }
/* Обязательные три — строкой на всю ширину плитки, поровну */
.props-row { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px 24px; }
/* Остальные свойства — «название | значение», значение растягивается на всю ширину */
.props-list { display: grid; grid-template-columns: max-content 1fr; gap: 6px 18px; margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--border-2); align-items: baseline; }
.props-list .prop-k { max-width: 220px; }
.prop-k { color: var(--text-dim); font-size: 12px; }
.prop-v { color: var(--text-muted); font-size: 15px; word-break: break-word; min-width: 0; }
/* вложения в чате */
.chat-attach { display: none; flex-wrap: wrap; gap: 6px; margin-bottom: 6px; }
.chip { display: inline-flex; align-items: center; gap: 4px; background: var(--surface-2); color: var(--text-muted); border-radius: 12px; padding: 2px 10px; font-size: 12px; }
.chip svg { width: 12px; height: 12px; margin-right: 2px !important; }
.chip-x { cursor: pointer; color: var(--text-dim); font-size: 14px; margin-left: 2px; }
.chip-x:hover { color: var(--danger); }
.msg-user .msg-files { margin-top: 6px; }
.msg-user .chip { background: rgba(255,255,255,0.2); color: var(--on-accent); }
.chat-main.drop { outline: 2px dashed var(--accent); outline-offset: -4px; }
/* карточки файлов в чате (результаты хода) */
.msg-filecards { align-self: flex-start; display: flex; flex-wrap: wrap; gap: 8px; max-width: 92%; }
.file-card { display: flex; align-items: center; gap: 10px; background: var(--bg); border: 1px solid var(--border-2); border-radius: 8px; padding: 8px 10px; min-width: 260px; }
.file-ext { background: var(--accent); color: var(--on-accent); font-size: 11px; font-weight: bold; border-radius: 5px; padding: 8px 6px; min-width: 34px; text-align: center; }
.file-info { min-width: 0; flex: 1; }
.file-name { color: var(--text-muted); font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 260px; }
.file-sub { color: var(--text-dim); font-size: 11px; }
.file-btns { display: flex; gap: 4px; white-space: nowrap; }
