/*
 * Стили веб-клиента EGO: каркас, типографика, компоненты экранов.
 *
 * Написан по дизайн-системе «Электро-спектр» (Р-107): docs/design.md — формы,
 * сетка и компоненты, docs/design-colors.md — цвет, docs/design-preview.html —
 * живой образец. Ни одного HEX здесь нет и быть не может: всё через токены
 * `tokens.css`, иначе тема перестанет переключаться.
 *
 * Каркас (docs/design.md §4.1): на ПК и планшете — верхняя панель и широкий
 * контент, на телефоне — нижняя панель вкладок. Прежняя колонка телефона
 * 480 px, растянутая на любой экран, отменена вместе со старой системой.
 *
 * Стили витрины (E-01) живут отдельно — `landing.css`: страница верстается
 * как документ и подключает свой файл только на себе.
 *
 * Связано: docs/egoweb/architecture.md §3, docs/screens.md §1
 */

/* ═══ 1. База и каркас ════════════════════════════════════════════════════ */

* { box-sizing: border-box; }

html {
    /* Ни один экран не уводит страницу вбок (docs/design.md §3.4, п. 1).
       Именно `clip`, а не `hidden`: `hidden` сделал бы элемент контейнером
       прокрутки, и липкие панели перестали бы липнуть */
    overflow-x: clip;
    /* Прокрутка к якорю не должна прятать заголовок под липкой панелью */
    scroll-padding-top: calc(var(--h-topbar) + var(--space-4));
}

body {
    margin: 0;
    min-height: 100dvh;
    background: var(--bg);
    color: var(--text);
    font-family: var(--font-family);
    font-size: var(--text-body);
    line-height: var(--lh-body);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    overflow-x: clip;
}

/* Оболочка: колонка во всю ширину. Подсветка лежит поверх фона и прибита к
   области просмотра — при прокрутке пятна остаются на месте, иначе они
   «едут» вместе с содержимым и мешают читать (docs/design-colors.md §6) */
.app {
    display: flex;
    flex-direction: column;
    min-height: 100dvh;
    position: relative;
    background: var(--ambient), var(--bg);
    background-attachment: fixed;
}

/* Чат — единственный экран с внутренней прокруткой: лента перевёрнута, а поле
   ввода прижато к низу. Остальные экраны прокручиваются документом, как
   обычные страницы. Признак берётся из `data-screen`, который сервер и так
   проставляет каждой странице (app/pages.py) */
html[data-screen="E-10"], html[data-screen="E-10"] body { height: 100%; overflow: hidden; }
html[data-screen="E-10"] .app { height: 100dvh; min-height: 0; overflow: hidden; }

/* Контентная область экрана.

   Ширина у всех экранов одна и та же — та, что у ленты чата (`--reading`).
   Решение оператора от 2026-08-17: разная ширина у разделов читалась как
   разные приложения, а растянутый на 27" список карточек — как недоделка.
   Полноширинными остаются только фон, липкие панели и их границы; само
   содержимое всегда стоит в одной колонке. */
.screen {
    flex: 1;
    width: 100%;
    max-width: calc(var(--reading) + var(--page-padding) * 2);
    margin: 0 auto;
    padding: calc(var(--h-topbar-mobile) + var(--space-3)) var(--page-padding) var(--space-10);
}
@media (min-width: 768px) {
    .screen { padding-top: var(--space-5); }
}

.screen--reading { max-width: calc(var(--reading) + var(--page-padding) * 2); }
/* Экраны-формы: ширину ограничивает только сама форма, а карточки, списки и
   заголовки живут во всю колонку. Ограничение на всех детей сжимало раздел
   ровно вдвое — так и выглядело: половина экрана пустая */
.screen--form form, .screen--form .form-column { max-width: var(--form); }

/* Телефон: под нижней панелью вкладок должно оставаться место, иначе она
   закрывает последнюю карточку списка */
@media (max-width: 767.98px) {
    .app { padding-bottom: calc(var(--h-tabbar) + env(safe-area-inset-bottom, 0)); }
    html[data-screen="E-10"] .app { padding-bottom: 0; }
}

.safe-bottom { padding-bottom: env(safe-area-inset-bottom, 0); }

/* Фокус виден всегда и одинаково (docs/design.md §12). Снимать `outline` без
   замены запрещено — этим правилом заменяем его один раз на всё приложение */
:focus-visible {
    outline: none;
    box-shadow: var(--focus-ring);
    border-radius: var(--radius-sm);
}

::selection { background: var(--violet-soft); color: var(--text); }

/* Полоса прокрутки в наших контейнерах: видимая, но неброская. Прятать нельзя —
   мышью прокрутить ряд без неё невозможно (docs/design.md §9.4) */
* { scrollbar-width: thin; scrollbar-color: var(--border-strong) transparent; }
*::-webkit-scrollbar { width: 8px; height: 8px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb {
    background: var(--border-strong);
    border-radius: var(--radius-pill);
}
*::-webkit-scrollbar-thumb:hover { background: var(--text-muted); }

/* Пользовательский текст, адрес партнёра, имя модуля — рвутся, а не уезжают
   за край (docs/design.md §3.4, п. 7) */
.text-break, .bubble, .menu-row, .card-ego { overflow-wrap: anywhere; }

img, video { max-width: 100%; height: auto; }

a { color: var(--violet-text); text-decoration: none; }
a:hover { text-decoration: underline; }

.skip-link {
    position: absolute; left: -9999px; top: 0; z-index: 100;
    padding: var(--space-3) var(--space-4);
    background: var(--surface); border: 1px solid var(--border-strong);
    border-radius: var(--radius-md);
}
.skip-link:focus { left: var(--space-4); top: var(--space-4); }

/* Обёртка содержимого — только якорь для ссылки «К содержимому»: своего блока
   она не создаёт, поэтому колонка экрана остаётся прямым потомком оболочки и
   раскладка не меняется */
.layout-anchor { display: contents; }


/* ═══ 2. Навигация ════════════════════════════════════════════════════════ */

/* Верхняя панель приложения. На телефоне её заменяет нижняя панель вкладок,
   поэтому здесь она скрыта (docs/design.md §4.1) */
.appnav {
    display: none;
    position: sticky;
    top: 0;
    z-index: 30;
    align-items: center;
    gap: var(--space-4);
    height: var(--h-topbar);
    /* Панель тянется во всю ширину — фон и нижняя граница должны доходить до
       краёв окна, — а её содержимое стоит в той же колонке, что и контент.
       Отсюда отступ по краям, а не `max-width`: он держит колонку, не разрезая
       саму полосу */
    padding-inline: max(var(--page-padding),
                        calc((100% - var(--reading)) / 2));
    background: color-mix(in srgb, var(--surface) 76%, transparent);
    backdrop-filter: blur(18px) saturate(140%);
    border-bottom: 1px solid var(--border);
}

/* Без размытия слой обязан стать сплошным: сквозь полупрозрачную панель
   читался бы текст под ней (docs/design.md §5.4) */
@supports not (backdrop-filter: blur(2px)) {
    .appnav { background: var(--surface); }
}

/* Полоса цветом раздела — под панелью, а не в ней: так она не съедает высоту
   и видна на любом фоне (docs/design-colors.md §7) */
.appnav::after {
    content: "";
    position: absolute; left: 0; right: 0; bottom: -3px; height: 3px;
    background: var(--accent-gradient);
}

@media (min-width: 768px) {
    .appnav { display: flex; }
}

.appnav-brand {
    display: inline-flex; align-items: center; gap: var(--space-2);
    color: var(--text); text-decoration: none;
}
.appnav-brand:hover { text-decoration: none; }

/* Слово «EGO» набором, а не лок-апом: Comfortaa выведена вместе со старым
   знаком (docs/design.md §2, §7.2) */
.wordmark {
    font-weight: 800;
    font-size: var(--text-body);
    letter-spacing: var(--tracking-word);
}

/* Кнопка темы на экранах без шапки: форма входа, документы, гейт */
.theme-floating {
    position: fixed; z-index: 35;
    top: calc(var(--space-3) + env(safe-area-inset-top, 0));
    right: var(--space-3);
    background: color-mix(in srgb, var(--surface) 80%, transparent);
    backdrop-filter: blur(12px);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
}

.appnav-links { display: flex; align-items: center; gap: var(--space-1); }

.appnav-link {
    display: inline-flex; align-items: center; gap: var(--space-2);
    height: var(--h-button); padding: 0 var(--space-4);
    border-radius: var(--radius-pill);
    color: var(--text-secondary);
    font-size: var(--text-small); font-weight: 700;
    text-decoration: none;
    transition: background var(--motion-fast) var(--ease-out),
                color var(--motion-fast) var(--ease-out);
}
.appnav-link:hover { background: var(--fill); color: var(--text); text-decoration: none; }
.appnav-link.is-current { background: var(--accent-soft); color: var(--accent-text); }
.appnav-link svg { width: 20px; height: 20px; }

.appnav-side { margin-left: auto; display: flex; align-items: center; gap: var(--space-2); }

.appnav-balance {
    display: inline-flex; align-items: center; gap: var(--space-1);
    height: var(--h-button-sm); padding: 0 var(--space-3);
    border: 1px solid var(--border); border-radius: var(--radius-pill);
    color: var(--text-secondary); font-size: var(--text-small); font-weight: 700;
    font-variant-numeric: tabular-nums;
}

/* Шапка экрана: заголовок и действия. На телефоне липкая и с кнопкой «назад»,
   на ПК — обычная строка внутри контента (там навигация в верхней панели) */
.topbar {
    position: sticky; top: 0; z-index: 30;
    display: flex; align-items: center; gap: var(--space-3);
    min-height: var(--h-topbar-mobile);
    padding: var(--space-2) var(--page-padding);
    background: color-mix(in srgb, var(--surface) 76%, transparent);
    backdrop-filter: blur(18px) saturate(140%);
    border-bottom: 1px solid var(--border);
}
@supports not (backdrop-filter: blur(2px)) { .topbar { background: var(--surface); } }

/* Фикс перекрытия topbar с breadcrumb на страницах документов */
.topbar h1.t-screen-title {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: calc(100vw - 80px);
}


@media (min-width: 768px) {
    .topbar {
        top: var(--h-topbar);
        max-width: calc(var(--reading) + var(--page-padding) * 2);
        margin: 0 auto;
        width: 100%;
        min-height: var(--h-topbar);
        background: transparent;
        backdrop-filter: none;
        border-bottom: none;
        padding-top: var(--space-4);
    }
    @supports not (backdrop-filter: blur(2px)) { .topbar { background: transparent; } }
    /* Знак в шапке экрана — только на телефоне: на ПК он уже есть в панели,
       и второй читается как ошибка вёрстки */
    .topbar .appnav-brand { display: none; }
}

/* Нижняя панель вкладок — телефон (docs/design.md §9.4) */
.tabbar {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 30;
    display: flex;
    height: calc(var(--h-tabbar) + env(safe-area-inset-bottom, 0));
    padding-bottom: env(safe-area-inset-bottom, 0);
    background: color-mix(in srgb, var(--surface) 88%, transparent);
    backdrop-filter: blur(18px) saturate(140%);
    border-top: 1px solid var(--border);
}
@supports not (backdrop-filter: blur(2px)) { .tabbar { background: var(--surface); } }

@media (min-width: 768px) { .tabbar { display: none; } }

.tab {
    flex: 1;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 2px;
    color: var(--text-muted);
    font-size: var(--text-micro); font-weight: 700;
    text-decoration: none;
    position: relative;
}
.tab:hover { text-decoration: none; }
.tab svg { width: 22px; height: 22px; }

/* Активная вкладка красится акцентом раздела и помечается точкой: цвет —
   не единственный носитель смысла (docs/design.md §12) */
.tab[aria-current="page"] { color: var(--accent-text); }
.tab[aria-current="page"]::before {
    content: "";
    position: absolute; top: 6px;
    width: 4px; height: 4px; border-radius: 50%;
    background: var(--accent);
}


/* ═══ 3. Типографика ══════════════════════════════════════════════════════ */

h1, h2, h3, p { margin: 0; }
h1, h2, h3 { text-wrap: balance; }

.t-hero, .t-display {
    font-size: var(--text-display); line-height: var(--lh-display);
    font-weight: 800; letter-spacing: var(--tracking-display);
}
/* Шкала помощников подобрана под то, как они стоят в разметке, а не по
   порядку уровней документа: `t-title` в клиенте — это заголовок шторки и
   крупное число (имя в профиле, баланс токенов, счётчик приглашений), а не
   герой страницы. Поставленный сюда `h1` (32–48) ломал профиль: имя не
   помещалось в строку и переезжало на вторую */
.t-title {
    font-size: var(--text-h2); line-height: var(--lh-h2);
    font-weight: 800; letter-spacing: var(--tracking-heading);
}
.t-screen-title {
    font-size: var(--text-h3); line-height: var(--lh-h3);
    font-weight: 700; letter-spacing: var(--tracking-heading);
}
/* Подзаголовок внутри экрана: месяц в календаре, состояние live-режима,
   заголовок пустого состояния. Крупнее текста, мельче заголовка экрана */
.t-section {
    font-size: var(--text-body-lg); line-height: 1.3;
    font-weight: 700; letter-spacing: var(--tracking-heading);
}
.t-body-strong { font-size: var(--text-body); line-height: var(--lh-body); font-weight: 700; }
.t-body { font-size: var(--text-body); line-height: var(--lh-body); }
.t-caption { font-size: var(--text-small); line-height: var(--lh-small); }
.t-micro { font-size: var(--text-micro); line-height: 1.4; font-weight: 500; }
.t-mono { font-family: var(--font-mono); font-size: var(--text-small); }

.t-secondary { color: var(--text-secondary); }
.t-tertiary { color: var(--text-muted); }
.t-error { color: var(--danger-text); }
.t-accent { color: var(--accent-text); }
.t-success { color: var(--success-text); }

.t-label {
    font-size: var(--text-micro); line-height: 1.2; font-weight: 700;
    letter-spacing: var(--tracking-label); text-transform: uppercase;
    color: var(--text-muted);
}

/* Крупный заголовок заливкой спектра. Запасной `color` обязателен: без
   `background-clip: text` заголовок исчез бы целиком (docs/design.md §2.2) */
.t-gradient {
    color: var(--violet-text);
    background: var(--gradient-spectrum);
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
}

/* Числа, стоящие столбцом или меняющиеся на месте, — табличными цифрами */
.t-num, .appnav-balance, .metric-value { font-variant-numeric: tabular-nums; }

/* Длинная строка нечитаема: 68 знаков — предел (docs/design.md §2.2) */
.t-measure { max-width: var(--measure); }


/* ═══ 4. Кнопки ═══════════════════════════════════════════════════════════ */

.btn-ego, .btn-ego-secondary, .btn-ego-text, .btn-ego-danger {
    display: inline-flex; align-items: center; justify-content: center;
    gap: var(--space-2);
    height: var(--h-button-lg);
    padding: 0 var(--space-6);
    border: 1px solid transparent;
    border-radius: var(--radius-md);
    font-family: inherit; font-size: var(--text-button); font-weight: 700;
    letter-spacing: 0.01em;
    cursor: pointer;
    text-decoration: none;
    transition: transform var(--motion-fast) var(--ease-out),
                box-shadow var(--motion-fast) var(--ease-out),
                background var(--motion-fast) var(--ease-out),
                border-color var(--motion-fast) var(--ease-out);
}
.btn-ego:hover, .btn-ego-secondary:hover, .btn-ego-text:hover, .btn-ego-danger:hover {
    text-decoration: none;
}
.btn-ego:active, .btn-ego-secondary:active, .btn-ego-danger:active { transform: translateY(1px); }

/* Главная кнопка — заливка бренд-градиентом и свечение под ней. На экране
   такая одна: две градиентные рядом — ошибка ревью (docs/design.md §9.1) */
.btn-ego {
    background: var(--gradient-brand);
    color: var(--on-violet);
    box-shadow: var(--glow-primary);
}
.btn-ego:hover {
    box-shadow: var(--glow-primary),
                0 0 0 4px color-mix(in srgb, var(--violet) 18%, transparent);
}

/* Вторая по важности — контур, а не цвет: иначе на экране два «главных» */
.btn-ego-secondary {
    background: transparent;
    color: var(--text);
    border-color: var(--border-strong);
}
.btn-ego-secondary:hover { background: var(--fill); border-color: var(--text-muted); }

/* Текстовая кнопка — действие, а не навигация, поэтому фиолет, а не акцент
   раздела: иначе «Скопировать» в профиле становится янтарным и читается как
   предупреждение (docs/design-colors.md §7) */
.btn-ego-text {
    height: auto; padding: var(--space-2) var(--space-3);
    background: transparent; color: var(--violet-text);
    border-radius: var(--radius-sm);
}
.btn-ego-text:hover { background: var(--fill); }

.btn-ego-danger { background: var(--danger); color: var(--on-danger); }
.btn-ego-danger:hover { background: var(--danger-hover); }

.btn-ego[disabled], .btn-ego-secondary[disabled], .btn-ego-danger[disabled] {
    background: var(--fill); color: var(--text-muted);
    border-color: transparent; box-shadow: none; cursor: not-allowed;
}

.btn-sm {
    height: var(--h-button-sm); padding: 0 var(--space-4);
    font-size: var(--text-small); border-radius: var(--radius-sm);
}
.btn-md { height: var(--h-button); padding: 0 var(--space-5); }

/* Иконочная кнопка. Цель нажатия 44 независимо от размера иконки
   (docs/design.md §3.4, п. 2) */
.btn-icon {
    display: inline-flex; align-items: center; justify-content: center;
    width: var(--h-button); height: var(--h-button);
    flex: none;
    padding: 0;
    background: transparent; color: var(--text);
    border: none; border-radius: var(--radius-md);
    cursor: pointer;
    transition: background var(--motion-fast) var(--ease-out),
                color var(--motion-fast) var(--ease-out);
}
.btn-icon:hover { background: var(--fill); }
.btn-icon.is-accent { color: var(--accent-text); }

/* Переключатель темы показывает результат нажатия, а не текущее состояние:
   в светлой теме — луна, в тёмной — солнце. Обе иконки лежат в разметке,
   лишнюю прячет CSS — подмена скриптом ждала бы его загрузки
   (docs/design-themes.md §4) */
.theme-toggle-sun { display: none; }
.theme-toggle-moon { display: inline-flex; }

:root[data-theme="dark"] .theme-toggle-sun { display: inline-flex; }
:root[data-theme="dark"] .theme-toggle-moon { display: none; }

@media (prefers-color-scheme: dark) {
    :root[data-theme="auto"] .theme-toggle-sun { display: inline-flex; }
    :root[data-theme="auto"] .theme-toggle-moon { display: none; }
}

.d-grid > .btn-ego, .d-grid > .btn-ego-secondary, .d-grid > .btn-ego-danger { width: 100%; }


/* ═══ 5. Поля и формы ═════════════════════════════════════════════════════ */

/* `.field` — это САМО поле, а не обёртка вокруг него: так класс стоит в
   разметке всех форм (`<input class="field">`). Обёрткой его сделать нельзя —
   тогда поля остаются без стилей, и на экране появляются серые прямоугольники
   браузера по умолчанию. Проверено живой поломкой на E-41 */
.field-label {
    display: block;
    margin-bottom: 6px;
    font-size: var(--text-small); color: var(--text-secondary);
}

/* Плотность формы. Раньше расстояние между полями складывалось из отступа
   обёртки (16) и всегда занятого места под ошибку (18) — форма растягивалась
   вдвое против нужного. Теперь место под ошибку резервируется только тогда,
   когда ошибка есть, а ритм задаёт один отступ */
form .mb-3 { margin-bottom: var(--space-3) !important; }
.field-error:empty { display: none; }

.field,
input[type="text"], input[type="email"], input[type="password"],
input[type="tel"], input[type="search"], input[type="date"], input[type="time"],
input[type="number"], input[type="url"], input[type="datetime-local"], textarea, select {
    width: 100%;
    height: var(--h-input);
    padding: 0 var(--space-4);
    background: var(--surface);
    color: var(--text);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-md);
    font-family: inherit;
    /* 16 px — не про красоту: мельче iOS зумит страницу при фокусе и обратно
       не отпускает (docs/design.md §3.4, п. 4) */
    font-size: var(--text-body);
    transition: border-color var(--motion-fast) var(--ease-out),
                box-shadow var(--motion-fast) var(--ease-out);
}

textarea, textarea.field {
    height: auto; min-height: 96px;
    padding: var(--space-3) var(--space-4);
    resize: vertical;
}

/* Родная стрелка `select` съедает текст, если не оставить ей места */
select, select.field { padding-right: var(--space-8); }

input::placeholder, textarea::placeholder, .field::placeholder { color: var(--text-muted); }

.field:focus, input:focus, textarea:focus, select:focus {
    outline: none;
    border-color: var(--violet);
    box-shadow: var(--focus-ring);
}

.field:disabled, input:disabled, select:disabled, textarea:disabled {
    background: var(--fill);
    color: var(--text-muted);
    cursor: not-allowed;
}

.field-error {
    display: block;
    margin-top: var(--space-1);
    font-size: var(--text-micro); font-weight: 500;
    color: var(--danger-text);
}

.is-invalid, .field.is-invalid { border-color: var(--danger); }
.field.is-invalid:focus {
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--danger) 40%, transparent);
}

/* Чекбокс и переключатель */
input[type="checkbox"], input[type="radio"] {
    width: var(--size-checkbox); height: var(--size-checkbox);
    accent-color: var(--violet);
    cursor: pointer;
}

.switch {
    position: relative;
    width: 44px; height: 26px;
    flex: none;
    background: var(--fill-strong);
    border-radius: var(--radius-pill);
    border: none;
    cursor: pointer;
    transition: background var(--motion-fast) var(--ease-out);
}
.switch::after {
    content: "";
    position: absolute; top: 3px; left: 3px;
    width: 20px; height: 20px; border-radius: 50%;
    background: var(--surface);
    box-shadow: var(--shadow-1);
    transition: transform var(--motion-fast) var(--ease-out);
}
.switch[aria-checked="true"] { background: var(--gradient-brand); }
.switch[aria-checked="true"]::after { transform: translateX(18px); }


/* ═══ 6. Карточки, списки, чипы ═══════════════════════════════════════════ */

.card-ego {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: var(--space-4);
}

@media (min-width: 768px) { .card-ego { padding: var(--space-5); } }

/* Кликабельная карточка: поднимается и подсвечивает границу. На касании
   наведения нет, поэтому оно только усиливает то, что и так видно */
.card-link {
    display: block; color: inherit; text-decoration: none;
    transition: transform var(--motion-fast) var(--ease-out),
                border-color var(--motion-fast) var(--ease-out),
                background var(--motion-fast) var(--ease-out);
}
.card-link:hover {
    text-decoration: none;
    transform: translateY(-2px);
    border-color: var(--border-strong);
    background: var(--surface-raised);
}

/* Акцентная карточка — градиентная рамка двойным фоном. Одна на экран
   (docs/design.md §5.2) */
.card-accent {
    border: 1px solid transparent;
    border-radius: var(--radius-lg);
    padding: var(--space-5);
    background:
        linear-gradient(var(--surface), var(--surface)) padding-box,
        var(--accent-gradient) border-box;
}

/* Плашка под иконкой: подсвеченный квадрат вместо голой иконки на пустом */
.icon-plate {
    display: inline-flex; align-items: center; justify-content: center;
    width: 44px; height: 44px; flex: none;
    border-radius: var(--radius-sm);
    background: var(--accent-soft);
    border: 1px solid color-mix(in srgb, var(--accent) 24%, transparent);
    color: var(--accent-text);
}
.icon-plate svg { width: 22px; height: 22px; }
.icon-plate-lg { width: 56px; height: 56px; border-radius: var(--radius-md); }
.icon-plate-lg svg { width: 28px; height: 28px; }

/* Строка меню: иконка, подпись, стрелка. Из них собран профиль */
.menu-row {
    display: flex; align-items: center; gap: var(--space-3);
    min-height: 56px;
    padding: var(--space-3) var(--space-4);
    color: inherit; text-decoration: none;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    transition: background var(--motion-fast) var(--ease-out),
                border-color var(--motion-fast) var(--ease-out);
}
.menu-row:hover { text-decoration: none; background: var(--fill); border-color: var(--border-strong); }
.menu-row svg { flex: none; }
/* Подпись занимает всю свободную ширину и прижимает стрелку вправо. Растягивать
   первый элемент нельзя — первый здесь иконка, и подпись уезжала в центр */
.menu-row > span.flex-grow-1 { flex: 1; min-width: 0; }
.menu-row > span:first-child { display: inline-flex; color: var(--text-secondary); }

/* Список строк одной карточкой: разделители вместо отступов между ними.
   Карточка теряет внутренний отступ — его дают сами строки, иначе у списка
   появляется двойная рамка и лишний воздух по краям */
.card-ego.divide-rows { padding: 0; overflow: hidden; }
.card-ego.divide-rows > * {
    border: none;
    border-bottom: 1px solid var(--border);
    border-radius: 0;
    background: transparent;
}
.card-ego.divide-rows > *:last-child { border-bottom: none; }

/* Тот же список без карточки — например, внутри шторки */
.divide-rows:not(.card-ego) { display: grid; }
.divide-rows:not(.card-ego) > * { border-radius: 0; border-bottom-width: 0; }
.divide-rows:not(.card-ego) > *:first-child { border-radius: var(--radius-lg) var(--radius-lg) 0 0; }
.divide-rows:not(.card-ego) > *:last-child {
    border-radius: 0 0 var(--radius-lg) var(--radius-lg);
    border-bottom-width: 1px;
}

/* Чип выбора */
.chip {
    display: inline-flex; align-items: center; gap: var(--space-2);
    height: var(--h-button-sm);
    padding: 0 var(--space-4);
    background: var(--fill);
    color: var(--text-secondary);
    border: 1px solid transparent;
    border-radius: var(--radius-pill);
    font-size: var(--text-small); font-weight: 700;
    cursor: pointer;
    transition: background var(--motion-fast) var(--ease-out),
                color var(--motion-fast) var(--ease-out);
}
.chip:hover { background: var(--fill-strong); }

/* Выбор чипа скрипты помечают `aria-pressed`, а не классом: чип — кнопка со
   состоянием, и атрибут тут не украшение, а то, что читает screen reader.
   Класс поддержан рядом — им пользуется календарь (`tasks.js`) */
.chip[aria-pressed="true"], .chip.is-selected {
    background: var(--accent);
    color: var(--on-bright);
    border-color: transparent;
}
/* На фиолете текст белый, на остальном спектре — чернильный
   (docs/design-colors.md §3.1) */
.accent-violet .chip[aria-pressed="true"], .accent-violet .chip.is-selected {
    color: var(--on-violet);
}
.chip-sm { height: 28px; padding: 0 var(--space-3); font-size: var(--text-micro); }

/* Плашка статуса: подложка смысла, текст смыслом, точка слева */
.status-pill {
    display: inline-flex; align-items: center; gap: var(--space-2);
    padding: 5px var(--space-3);
    border-radius: var(--radius-pill);
    font-size: var(--text-micro); font-weight: 700;
    background: var(--fill); color: var(--text-secondary);
}
.status-dot, .dot {
    width: 7px; height: 7px; flex: none;
    border-radius: 50%;
    background: currentColor;
}

.status-pill.is-success { background: var(--success-soft); color: var(--success-text); }
.status-pill.is-warning { background: var(--warning-soft); color: var(--warning-text); }
.status-pill.is-error { background: var(--danger-soft); color: var(--danger-text); }
.status-pill.is-info { background: var(--info-soft); color: var(--info-text); }

/* Точки приоритетов задач (docs/design-colors.md §8) */
.dot-urgent { background: var(--priority-urgent); }
.dot-high { background: var(--priority-high); }
.dot-medium { background: var(--priority-medium); }
.dot-low { background: var(--priority-low); }

/* Бейдж «новое» — единственное место лайма в клиенте */
.badge-new {
    display: inline-flex; align-items: center;
    padding: 3px var(--space-2);
    background: var(--lime); color: var(--ink);
    border-radius: var(--radius-xs);
    font-size: var(--text-micro); font-weight: 700;
}

.alert-soft {
    display: flex; gap: var(--space-3);
    padding: var(--space-4);
    background: var(--info-soft);
    border: 1px solid color-mix(in srgb, var(--info) 40%, transparent);
    border-radius: var(--radius-lg);
    font-size: var(--text-small);
}
.alert-soft.is-warning {
    background: var(--warning-soft);
    border-color: color-mix(in srgb, var(--warning) 40%, transparent);
}
.alert-soft.is-error {
    background: var(--danger-soft);
    border-color: color-mix(in srgb, var(--danger) 40%, transparent);
}

.progress-ego {
    height: 8px;
    background: var(--fill);
    border-radius: var(--radius-pill);
    overflow: hidden;
}
.progress-ego > * {
    display: block; height: 100%;
    background: var(--gradient-brand);
    border-radius: inherit;
    transition: width var(--motion) var(--ease-out);
}

/* Метрика: число крупно, подпись капслоком */
.metric-value {
    font-size: var(--text-metric); font-weight: 800; line-height: 1;
    letter-spacing: var(--tracking-heading);
}


/* ═══ 7. Чат (E-10) ═══════════════════════════════════════════════════════ */

.chat-feed {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
    /* Перевёрнутая колонка: диалог открывается на последнем сообщении, а
       подгрузка истории сверху не сдвигает прочитанное */
    display: flex;
    flex-direction: column-reverse;
    padding: var(--space-4) var(--page-padding);
}

/* Перевёрнута и лента, и список внутри неё — это не дублирование, а пара.
   `chat.js` вставляет новое сообщение ПЕРВЫМ узлом (`insertBefore`), а историю
   дописывает в конец: при `column-reverse` новое оказывается внизу, а
   подгруженная история — сверху, не сдвигая прочитанное. Поставить здесь
   обычный `column` — значит перевернуть диалог: новые реплики уезжают наверх */
.chat-messages {
    width: 100%;
    max-width: var(--reading);
    margin: 0 auto;
    display: flex;
    flex-direction: column-reverse;
    gap: var(--space-3);
}

.bubble {
    max-width: 78%;
    padding: var(--space-3) var(--space-4);
    font-size: var(--text-body);
    line-height: var(--lh-body);
    white-space: pre-wrap;
    word-break: break-word;
    overflow-wrap: anywhere;
}

/* Длинные ответы ассистента (текст песни, статья) — ограничиваем высоту с прокруткой внутри */
.bubble-assistant {
    max-height: 60vh;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
}

/* Своё сообщение — единственное место в ленте, где появляется бренд-градиент:
   он отмечает «сказал человек» (docs/design.md §9.8) */
.bubble-user {
    align-self: flex-end;
    background: var(--gradient-brand);
    color: var(--on-violet);
    border-radius: var(--radius-lg) var(--radius-lg) 6px var(--radius-lg);
}

.bubble-assistant {
    align-self: flex-start;
    background: var(--surface);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg) var(--radius-lg) var(--radius-lg) 6px;
}

@media (prefers-color-scheme: dark) {
    :root[data-theme="auto"] .bubble-assistant { background: var(--surface-raised); }
}
:root[data-theme="dark"] .bubble-assistant { background: var(--surface-raised); }

/* Каретка стриминга: мигает, пока модель дописывает ответ. Текст при этом
   дописывается без анимации — анимировать каждое слово значит мешать читать */
.bubble-streaming::after {
    content: "";
    display: inline-block;
    width: 2px; height: 1em;
    margin-left: 2px;
    background: var(--accent-text);
    vertical-align: text-bottom;
    animation: ego-caret 1s steps(2) infinite;
}
@keyframes ego-caret { 50% { opacity: 0; } }

.typing-dots {
    display: inline-flex; gap: 4px; align-items: center;
    min-height: 20px;
}
.typing-dots::before, .typing-dots::after, .typing-dots > i {
    content: "";
    width: 6px; height: 6px; border-radius: 50%;
    background: var(--text-muted);
    animation: ego-typing 1.2s ease-in-out infinite;
}
.typing-dots::after { animation-delay: 0.4s; }
.typing-dots > i { animation-delay: 0.2s; }
@keyframes ego-typing {
    0%, 60%, 100% { opacity: .35; transform: translateY(0); }
    30% { opacity: 1; transform: translateY(-3px); }
}

.bubble-photo {
    display: block;
    max-height: 240px; max-width: 300px;
    border-radius: var(--radius-md);
    object-fit: cover;
    cursor: zoom-in;
}

/* Мозаика фотографий одного сообщения: квадраты, кроме одиночного снимка */
.bubble-mosaic {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-1);
    max-width: 300px;
}
.bubble-mosaic .bubble-photo { width: 100%; aspect-ratio: 1; max-width: none; }
.bubble-mosaic > :only-child { grid-column: span 2; aspect-ratio: auto; }
/* Нечётное число: первый снимок занимает оба столбца — иначе внизу дыра */
.bubble-mosaic > :first-child:nth-last-child(odd) { grid-column: span 2; }

.bubble-audio { width: 100%; max-width: 280px; }

/* Строка действия: «спрашиваю у сервиса…». Без неё пауза на вызов чужого
   сервиса выглядит зависанием (docs/screens.md §1.3) */
.tool-line {
    display: flex; align-items: center; gap: var(--space-2);
    align-self: flex-start;
    font-size: var(--text-micro); font-weight: 700;
    color: var(--cyan-text);
}
.tool-line svg { width: 14px; height: 14px; }

/* Панель ввода */
.composer {
    position: sticky; bottom: 0;
    padding: var(--space-3) var(--page-padding)
             calc(var(--space-3) + env(safe-area-inset-bottom, 0));
    background: color-mix(in srgb, var(--surface) 88%, transparent);
    backdrop-filter: blur(18px) saturate(140%);
    border-top: 1px solid var(--border);
}
@supports not (backdrop-filter: blur(2px)) { .composer { background: var(--surface); } }

@media (max-width: 767.98px) {
    .composer { margin-bottom: calc(var(--h-tabbar) + env(safe-area-inset-bottom, 0)); }
}
/* На экране чата (E-10) padding-bottom уже 0, composer не двигаем */
html[data-screen="E-10"] .composer { margin-bottom: 0; }
/* Tabbar на чате перекрывает composer — поднимаем его */
html[data-screen="E-10"] .app { padding-bottom: calc(var(--h-tabbar) + env(safe-area-inset-bottom, 0)); }

.composer-row {
    display: flex; align-items: flex-end; gap: var(--space-2);
    width: 100%; max-width: var(--reading); margin: 0 auto;
}

.composer-input {
    flex: 1;
    min-height: var(--h-button); max-height: 140px;
    padding: var(--space-3) var(--space-4);
    background: var(--fill);
    color: var(--text);
    border: 1px solid transparent;
    border-radius: var(--radius-lg);
    font-family: inherit; font-size: var(--text-body); line-height: 1.4;
    resize: none;
}
.composer-input:focus {
    outline: none;
    background: var(--surface);
    border-color: var(--violet);
    box-shadow: var(--focus-ring);
}

.composer-send {
    display: inline-flex; align-items: center; justify-content: center;
    width: var(--h-button); height: var(--h-button); flex: none;
    background: var(--gradient-brand); color: var(--on-violet);
    border: none; border-radius: var(--radius-md);
    box-shadow: var(--glow-primary);
    cursor: pointer;
}

/* Полоса выбранных фотографий над полем: пока они здесь, сообщение не ушло */
.attach-strip {
    display: flex; gap: var(--space-2);
    max-width: var(--reading); margin: 0 auto var(--space-2);
    overflow-x: auto;
    padding-bottom: var(--space-1);
}
.attach-item {
    position: relative;
    width: 64px; height: 64px; flex: none;
    border-radius: var(--radius-sm);
    overflow: hidden;
    border: 1px solid var(--border);
}
.attach-item img { width: 100%; height: 100%; object-fit: cover; }
/* Крестик поверх угла: палец попадает по нему, не задевая саму карточку */
.attach-remove {
    position: absolute; top: 2px; right: 2px;
    width: 22px; height: 22px;
    display: inline-flex; align-items: center; justify-content: center;
    background: var(--overlay); color: #FFFFFF;
    border: none; border-radius: 50%;
    cursor: pointer;
}
.attach-remove svg { width: 14px; height: 14px; }

.composer-recording {
    display: flex; align-items: center; gap: var(--space-3);
    max-width: var(--reading); margin: 0 auto;
    padding: var(--space-2) 0;
}
.rec-dot {
    width: 10px; height: 10px; border-radius: 50%;
    background: var(--danger);
    animation: ego-pulse 1.2s ease-in-out infinite;
}
@keyframes ego-pulse { 50% { opacity: .3; transform: scale(.8); } }


/* ═══ 8. Live-режим (E-11) и просмотр фото (E-12) ═════════════════════════ */

.live-overlay {
    position: fixed; inset: 0; z-index: 60;
    display: flex; flex-direction: column; align-items: center;
    padding: var(--space-6) var(--page-padding)
             calc(var(--space-6) + env(safe-area-inset-bottom, 0));
    background: var(--ambient), var(--bg);
}
.live-overlay[hidden] { display: none; }

.live-top {
    display: flex; align-items: center; justify-content: space-between;
    width: 100%; max-width: var(--reading);
    margin-bottom: auto;
}

/* Волна из пяти столбиков: высоту задаёт скрипт по громкости */
.live-wave { display: flex; align-items: flex-end; gap: var(--space-2); height: 120px; }
.live-wave i {
    display: block;
    width: 12px; height: 24px;
    background: var(--gradient-brand);
    border-radius: var(--radius-pill);
    transition: height var(--motion-fast) var(--ease-out);
}

.photo-viewer {
    position: fixed; inset: 0; z-index: 70;
    display: flex; align-items: center; justify-content: center;
    background: var(--overlay);
    backdrop-filter: blur(8px);
}
.photo-viewer[hidden] { display: none; }
.photo-viewer img { max-width: 92vw; max-height: 86vh; border-radius: var(--radius-md); }
.photo-close {
    position: absolute; top: var(--space-4); right: var(--space-4);
    background: var(--surface);
}


/* ═══ 9. Задачи и календарь (E-20…E-23) ═══════════════════════════════════ */

@media (min-width: 1200px) {
    /* На ПК календарь и список стоят рядом: месяц целиком виден постоянно,
       и переключаться между «когда» и «что» не нужно (docs/design.md §4.1) */
    .tasks-layout {
        display: grid;
        grid-template-columns: 320px minmax(0, 1fr);
        gap: var(--space-6);
        align-items: start;
    }
    .tasks-layout > .calendar { position: sticky; top: calc(var(--h-topbar) + var(--space-4)); }
    /* Список задач в правой колонке начинается с фильтров, а не с отступа */
    .tasks-list-side > :first-child { margin-top: 0 !important; }
}

/* Список задач: карточки одна под другой, промежуток из шкалы */
#task-list { display: grid; gap: var(--space-3); }

.calendar {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: var(--space-4);
}

.calendar-head {
    display: flex; align-items: center; justify-content: space-between;
    gap: var(--space-2);
    margin-bottom: var(--space-3);
}

.calendar-weekdays, .calendar-grid {
    display: grid;
    grid-template-columns: repeat(7, minmax(0, 1fr));
    gap: 2px;
}
.calendar-weekdays {
    margin-bottom: var(--space-1);
    font-size: var(--text-micro); font-weight: 700;
    color: var(--text-muted); text-align: center;
    text-transform: uppercase; letter-spacing: 0.06em;
}

.calendar-day {
    position: relative;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 3px;
    min-height: 44px;
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--text);
    font-family: inherit; font-size: var(--text-small); font-weight: 700;
    cursor: pointer;
    font-variant-numeric: tabular-nums;
}
.calendar-day:hover { background: var(--fill); }
.calendar-day.is-muted { color: var(--text-muted); font-weight: 400; }
.calendar-day.is-today { border-color: var(--accent); }
.calendar-day.is-selected { background: var(--accent); color: var(--on-bright); }
.calendar-day.is-selected .dot { box-shadow: 0 0 0 1px var(--accent); }

.calendar-dots { display: flex; gap: 3px; height: 6px; }
.calendar-dots .dot { width: 5px; height: 5px; }

.task-row {
    display: flex; align-items: flex-start; gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
}
.task-row.is-urgent { border-left: 3px solid var(--danger); }
.task-row.is-done .task-title { color: var(--text-muted); text-decoration: line-through; }

.task-check {
    width: var(--size-checkbox); height: var(--size-checkbox);
    flex: none; margin-top: 2px;
    border: 2px solid var(--border-strong);
    border-radius: var(--radius-xs);
    background: transparent;
    cursor: pointer;
    display: inline-flex; align-items: center; justify-content: center;
    color: transparent;
}
.task-check[aria-checked="true"] {
    background: var(--success); border-color: var(--success); color: var(--ink);
}
.task-check svg { width: 14px; height: 14px; }

.step-row {
    display: flex; align-items: center; gap: var(--space-3);
    padding: var(--space-2) 0;
    border-bottom: 1px solid var(--border);
}
.step-row:last-child { border-bottom: none; }


/* ═══ 10. Модули (E-30…E-35) ══════════════════════════════════════════════ */

/* Каталог сервисов. `auto-fill` вместо фиксированного числа колонок —
   промежуточные ширины не требуют своего медиазапроса (docs/design.md §3.4) */
.module-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--space-3);
}

@media (min-width: 600px) {
    .module-grid { grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); }
}

/* Строка поиска над каталогом. Появляется всегда, а не «когда модулей много»:
   поле, которое то есть, то нет, человек не запоминает и искать им не начинает */
.mod-search {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: 0 var(--space-3);
    margin-bottom: var(--space-3);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    color: var(--text-muted);
}

.mod-search:focus-within { border-color: var(--accent); box-shadow: var(--focus-ring); }

.mod-search__input {
    flex: 1;
    min-width: 0;
    /* Рамку и фон рисует полоса вокруг, а не само поле: иначе получается
       рамка в рамке. Класс `.field` при этом остаётся — он задаёт размер
       текста и высоту, общие у всех полей ввода */
    border: none;
    background: none;
    box-shadow: none;
    color: var(--text);
    font-size: var(--text-body);
    padding: var(--space-3) 0;
    outline: none;
}

/* Ячейка каталога: карточка-ссылка и звёздочка рядом с ней. Кнопка внутри
   ссылки — недопустимая разметка, поэтому они соседи, а не вложены */
.module-cell { position: relative; display: flex; }
.module-cell > .module-card { flex: 1; min-width: 0; }

.module-star {
    position: absolute;
    top: var(--space-3);
    right: var(--space-3);
    width: 32px;
    height: 32px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: none;
    border-radius: var(--radius-sm);
    background: none;
    color: var(--text-muted);
    cursor: pointer;
    transition: color var(--motion-fast) var(--ease-out),
                background var(--motion-fast) var(--ease-out);
}

.module-star:hover { background: var(--surface-2, var(--surface)); color: var(--text); }
.module-star.is-on { color: var(--amber); }
.module-star:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.module-star .mod-icon { width: 18px; height: 18px; }

/* Название не должно заезжать под звёздочку: она стоит поверх карточки */
.module-cell .module-card__head { padding-right: 30px; }

/* Полоса разделов над каталогом. Разделы приходят из манифестов, а не зашиты:
   партнёр выбирает свой из закрытого списка (docs/modules-ui.md §3.6) */
.module-filters {
    display: flex;
    gap: var(--space-2);
    overflow-x: auto;
    padding-bottom: var(--space-2);
    margin-bottom: var(--space-4);
    scrollbar-width: none;
}

.module-filters::-webkit-scrollbar { display: none; }
.module-filters .chip { flex: none; }

/* Карточка сервиса: знак, название, что умеет, состояние. Цвет знака мы
   выбираем сами по коду модуля — партнёр цветов не задаёт (CLAUDE.md §11) */
.module-card {
    position: relative;
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    grid-template-rows: auto auto;
    gap: var(--space-1) var(--space-3);
    padding: var(--space-4);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    color: inherit;
    text-decoration: none;
    overflow: hidden;
    transition: transform var(--motion-fast) var(--ease-out),
                border-color var(--motion-fast) var(--ease-out),
                box-shadow var(--motion-fast) var(--ease-out);
}

/* Тонкая цветная полоса сверху — единственное место, где виден «цвет сервиса».
   Она отличает карточки друг от друга, не раскрашивая интерфейс целиком */
.module-card::before {
    content: "";
    position: absolute;
    inset: 0 0 auto;
    height: 3px;
    background: var(--tile, var(--violet));
    opacity: .9;
}

.module-card:hover {
    text-decoration: none;
    transform: translateY(-2px);
    border-color: var(--tile, var(--violet));
    box-shadow: var(--shadow-1);
}

.module-card:focus-visible { outline: none; box-shadow: var(--focus-ring); }

.module-card__head { grid-column: 2; display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.module-card__title { font-size: var(--text-body-lg); font-weight: 700; letter-spacing: var(--tracking-heading); }
.module-card__summary {
    font-size: var(--text-small);
    color: var(--text-secondary);
    line-height: var(--lh-small);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.module-card__foot {
    grid-column: 1 / -1;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin-top: var(--space-3);
    padding-top: var(--space-3);
    border-top: 1px solid var(--border);
    font-size: var(--text-micro);
    color: var(--text-muted);
}

.module-card__state { display: inline-flex; align-items: center; gap: var(--space-2); }

.module-logo {
    width: 44px; height: 44px; flex: none;
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: var(--radius-sm);
    background: color-mix(in srgb, var(--tile, var(--cyan)) 14%, transparent);
    border: 1px solid color-mix(in srgb, var(--tile, var(--cyan)) 26%, transparent);
    color: var(--tile, var(--cyan-text));
    object-fit: cover;
}

.module-logo .mod-icon { width: 22px; height: 22px; }

/* Недоступный по плану модуль гасим, но оставляем читаемым: исчезновение
   читается как потеря данных (docs/screens.md §1.5) */
.module-card.is-locked { opacity: .6; }

/* ─── Полоса управления модулем (E-31) ───────────────────────────────────── */

.mod-bar {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-3);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    margin-bottom: var(--space-3);
}

.mod-bar__text { flex: 1 1 auto; min-width: 0; }
.mod-bar__name {
    font-size: var(--text-body-lg);
    font-weight: 700;
    letter-spacing: var(--tracking-heading);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.mod-bar__state {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    font-size: var(--text-micro);
    color: var(--text-muted);
}
.mod-bar__tools { display: flex; align-items: center; gap: var(--space-1); flex: none; }

/* Свёрнутая панель: права и умения. Развёрнутой она бывает редко, поэтому
   стоит под полосой, а не занимает место постоянно */
.mod-panel {
    background: var(--bg-subtle);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: var(--space-4);
    margin-bottom: var(--space-3);
    display: grid;
    gap: var(--space-4);
}

.mod-panel__title {
    font-size: var(--text-micro);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: var(--tracking-label);
    color: var(--text-muted);
    margin: 0 0 var(--space-2);
}

.mod-panel__hint { font-size: var(--text-small); color: var(--text-secondary); margin: 0 0 var(--space-3); }

.mod-panel__row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-2) 0;
    border-bottom: 1px solid var(--border);
}
.mod-panel__row:last-child { border-bottom: none; }
.mod-panel__label { font-size: var(--text-small); color: var(--text); }

/* Строка памяти модуля: подпись с объяснением слева, значение справа. На узком
   экране длинный токен рядом с объяснением не помещается — поэтому колонкой */
.mod-panel__row--stack { align-items: flex-start; flex-wrap: wrap; }
.mod-panel__row--stack .mod-panel__label { display: flex; flex-direction: column; gap: 2px; }
.mod-panel__note { font-size: var(--text-micro); color: var(--text-muted); }

/* Звёздочка в полосе управления модулем: тот же смысл, что в каталоге */
#module-star.is-on { color: var(--amber); }

.mod-panel__list { margin: 0; padding-left: var(--space-5); font-size: var(--text-small); color: var(--text-secondary); }

/* Переключатель права: текст на кнопке, а не безымянный тумблер — человек
   должен читать, что именно он разрешает (docs/security.md §6) */
.mod-switch {
    flex: none;
    height: var(--h-button-sm);
    padding: 0 var(--space-3);
    border-radius: var(--radius-pill);
    border: 1px solid var(--border-strong);
    background: var(--surface);
    color: var(--text-secondary);
    font-family: inherit;
    font-size: var(--text-micro);
    cursor: pointer;
}

.mod-switch.is-on { background: var(--success-soft); border-color: transparent; color: var(--success-text); font-weight: 600; }
.mod-switch:focus-visible { outline: none; box-shadow: var(--focus-ring); }

/* Одна строка «нужно разрешение» вместо панели на пол-экрана */
.mod-ask {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    flex-wrap: wrap;
    padding: var(--space-3);
    border-radius: var(--radius-md);
    background: var(--accent-soft);
    color: var(--accent-text);
    font-size: var(--text-small);
    margin-bottom: var(--space-3);
}

.mod-ask__text { flex: 1 1 220px; }

.btn-ego-sm { height: var(--h-button-sm); padding: 0 var(--space-4); font-size: var(--text-micro); }

/* Ответ партнёра — в помеченном блоке: человек всегда видит, где кончаемся
   мы и начинается чужой сервис (docs/design.md §9.10) */
.partner-block {
    padding: var(--space-4);
    border: 1px solid color-mix(in srgb, var(--cyan) 40%, transparent);
    border-radius: var(--radius-lg);
    background: var(--cyan-soft);
}


/* ═══ 11. Профиль (E-40…E-50) ═════════════════════════════════════════════ */

/* Карточка человека: аватар, под ним имя и город. Имя сбоку от аватара в
   узкой колонке ломалось на две строки и распирало карточку */
.profile-head {
    display: flex; flex-direction: column; align-items: center;
    gap: var(--space-2);
    text-align: center;
    margin-bottom: var(--space-4);
    padding: var(--space-6) var(--space-5);
}
.profile-head .avatar { margin-bottom: var(--space-2); }
.profile-head > .t-screen-title { overflow-wrap: anywhere; }

@media (min-width: 992px) {
    .profile-layout {
        display: grid;
        grid-template-columns: 300px minmax(0, 1fr);
        gap: var(--space-6);
        align-items: start;
    }
    /* Карточка человека и подписка остаются на виду, пока листаются разделы */
    .profile-aside { position: sticky; top: calc(var(--h-topbar) + var(--space-4)); }
}

/* Аватар — контейнер, а не картинка: пока фотографии нет, в нём стоит буква
   имени, и подменять один элемент другим ради этого незачем */
.avatar {
    position: relative;
    display: inline-flex; align-items: center; justify-content: center;
    width: var(--avatar-profile); height: var(--avatar-profile);
    flex: none;
    border-radius: 50%;
    background: var(--accent-soft);
    border: 1px solid var(--border);
    color: var(--accent-text);
    font-size: var(--text-h2); font-weight: 800;
    text-transform: uppercase;
}
/* Круг режет сама картинка, а не контейнер: `overflow: hidden` на контейнере
   срезал бы значок «сменить фото», который висит на его краю */
.avatar img {
    position: absolute; inset: 0;
    width: 100%; height: 100%;
    object-fit: cover;
    border-radius: 50%;
}
.avatar-sm { width: var(--avatar-chat); height: var(--avatar-chat); font-size: var(--text-small); }

.avatar-editable { cursor: pointer; }

/* Значок «сменить фото». Иконка приходит разметкой, а не псевдоэлементом:
   набор один — Lucide, и рисовать камеру фоном значило бы завести второй */
.avatar-badge {
    position: absolute; right: -2px; bottom: -2px;
    display: inline-flex; align-items: center; justify-content: center;
    width: 30px; height: 30px; border-radius: 50%;
    background: var(--gradient-brand);
    color: var(--on-violet);
    border: 2px solid var(--bg);
    box-shadow: var(--glow-primary);
}

/* Автодополнение города: список поверх формы */
.city-picker { position: relative; }
.city-suggest {
    position: absolute; left: 0; right: 0; top: calc(100% + 4px); z-index: 40;
    max-height: 260px; overflow-y: auto;
    background: var(--surface-raised);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-2);
}
.city-option {
    display: block; width: 100%;
    padding: var(--space-3) var(--space-4);
    background: transparent; border: none; text-align: left;
    color: var(--text); font-family: inherit; font-size: var(--text-small);
    cursor: pointer;
}
.city-option:hover { background: var(--fill); }

/* Выбор места на карте (E-41).

   Окно, а не полноэкранный слой: на ПК карта во весь монитор выглядит как
   отдельное приложение, а вернуться в форму хочется одним взглядом. Ширина —
   та же колонка, что у всего контента. На телефоне окно занимает экран
   целиком: там иначе не бывает. */
.map-pick {
    position: fixed; inset: 0; z-index: 60;
    display: flex; align-items: center; justify-content: center;
    padding: 0;
    background: var(--overlay);
    backdrop-filter: blur(2px);
}

.map-pick-dialog {
    position: relative;
    display: flex; flex-direction: column;
    width: 100%; height: 100%;
    background: var(--surface);
    overflow: hidden;
}

@media (min-width: 768px) {
    .map-pick { padding: var(--space-6); }
    .map-pick-dialog {
        width: min(var(--reading), 100%);
        height: min(640px, 100%);
        border: 1px solid var(--border);
        border-radius: var(--radius-xl);
        box-shadow: var(--shadow-2);
    }
}

/* Карта занимает всё окно, кроме нижней панели: раньше между ними оставалась
   белая полоса — контейнер карты не тянулся, а `min-height` держал высоту */
.map-pick-map { flex: 1; min-height: 0; width: 100%; }
.map-pick-map .maplibregl-map { height: 100%; }

.map-pick-foot {
    display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap;
    padding: var(--space-4);
    padding-bottom: calc(var(--space-4) + env(safe-area-inset-bottom, 0));
    border-top: 1px solid var(--border);
    background: var(--surface);
}
.map-pick-hint { flex: 1; min-width: 160px; font-size: var(--text-small); color: var(--text-secondary); }
/* Кнопка подтверждения не на всю ширину: рядом с подсказкой она читается как
   действие, а не как дно экрана */
.map-pick-foot .btn-ego { flex: none; width: auto; }

/* Кнопки поверх карты: круглые, на подложке — иначе иконка теряется на тайлах */
.map-pick-btn {
    position: absolute; z-index: 2;
    width: var(--h-button); height: var(--h-button);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-pill);
    box-shadow: var(--shadow-1);
}
.map-pick-btn:hover { background: var(--fill); }
.map-pick-close { top: var(--space-3); right: var(--space-3); }
.map-pick-locate { top: var(--space-3); left: var(--space-3); }
/* Пульсирующая точка: видно, что место выбрано именно здесь */
.pick-dot {
    width: 18px; height: 18px; border-radius: 50%;
    background: var(--violet);
    box-shadow: 0 0 0 6px color-mix(in srgb, var(--violet) 25%, transparent);
}


/* ═══ 12. Публичные страницы и гайд ═══════════════════════════════════════ */

.doc-notice {
    padding: var(--space-4);
    background: var(--warning-soft);
    border: 1px solid color-mix(in srgb, var(--warning) 40%, transparent);
    border-radius: var(--radius-lg);
    font-size: var(--text-small);
}

.doc-body { max-width: var(--measure); }
.doc-body h2 { margin: var(--space-8) 0 var(--space-3); }
.doc-body p { margin-bottom: var(--space-3); color: var(--text-secondary); }

/* Вопрос-ответ: стрелка складывается вместе с ответом */
details.faq-item {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: var(--space-4);
}
details.faq-item + details.faq-item { margin-top: var(--space-3); }
details.faq-item summary {
    display: flex; align-items: center; justify-content: space-between;
    gap: var(--space-3);
    font-weight: 700; cursor: pointer; list-style: none;
}
details.faq-item summary::-webkit-details-marker { display: none; }
details.faq-item summary::after {
    content: "";
    width: 10px; height: 10px; flex: none;
    border-right: 2px solid var(--text-muted);
    border-bottom: 2px solid var(--text-muted);
    transform: rotate(45deg);
    transition: transform var(--motion-fast) var(--ease-out);
}
details.faq-item[open] summary::after { transform: rotate(-135deg); }

.guide-slide { text-align: center; max-width: var(--form); margin: 0 auto; }
.guide-glyph {
    display: inline-flex; align-items: center; justify-content: center;
    width: 96px; height: 96px;
    border-radius: var(--radius-2xl);
    background: var(--accent-soft);
    color: var(--accent-text);
    margin-bottom: var(--space-6);
}
.guide-glyph svg { width: 44px; height: 44px; }
.guide-dots { display: flex; gap: var(--space-2); justify-content: center; }
.guide-dot {
    width: 8px; height: 8px; border-radius: var(--radius-pill);
    background: var(--fill-strong);
    transition: width var(--motion) var(--ease-out), background var(--motion) var(--ease-out);
}
/* Текущий слайд — не другой цвет, а другая длина: точки остаются точками */
.guide-dot.is-current { width: 24px; background: var(--accent); }


/* ═══ 13. Экраны входа (E-02, E-05…E-08, E-57) ════════════════════════════ */

.screen--auth {
    display: flex; flex-direction: column; justify-content: center;
    min-height: 100dvh;
    max-width: none;
    padding: var(--space-6) var(--page-padding);
}

.auth-card {
    width: 100%;
    max-width: var(--auth-card);
    margin: 0 auto;
    padding: var(--space-6);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-1);
}
/* Регистрация шире: поля стоят в две колонки, и в 420 они не помещаются */
.auth-card--wide { max-width: 560px; }

.auth-title { margin-bottom: var(--space-2); }
.auth-sub { margin-bottom: var(--space-6); color: var(--text-secondary); font-size: var(--text-small); }

.auth-field { margin-bottom: var(--space-4); }
.auth-field-head {
    display: flex; align-items: baseline; justify-content: space-between;
    gap: var(--space-3);
    margin-bottom: var(--space-2);
}

/* Две колонки: имя и фамилия, дата и пол. На телефоне — одна */
.auth-two { display: grid; gap: var(--space-3); }
@media (min-width: 480px) { .auth-two { grid-template-columns: 1fr 1fr; } }

.auth-alert {
    padding: var(--space-3) var(--space-4);
    margin-bottom: var(--space-4);
    background: var(--danger-soft);
    border: 1px solid color-mix(in srgb, var(--danger) 40%, transparent);
    border-radius: var(--radius-md);
    font-size: var(--text-small);
    color: var(--danger-text);
}
.auth-alert--ok {
    background: var(--success-soft);
    border-color: color-mix(in srgb, var(--success) 40%, transparent);
    color: var(--success-text);
}

.auth-links {
    display: flex; flex-wrap: wrap; gap: var(--space-3);
    justify-content: center;
    margin-top: var(--space-5);
    font-size: var(--text-small);
}

.auth-consent { display: flex; gap: var(--space-3); font-size: var(--text-small); }

/* Капча: картинка над полем, а не сбоку — узкая колонка сжимает её до
   нечитаемости (docs/design.md §9.2) */
.captcha { margin-bottom: var(--space-4); }
.captcha-frame {
    display: flex; align-items: center; gap: var(--space-2);
    margin-bottom: var(--space-2);
}
/* Картинку рисует сервер на светлом фоне, тёмного варианта у ручки нет: в
   тёмной теме она показывается на светлом острове, иначе читается как
   непрогруженный элемент (docs/design-themes.md §5.5, долг Д-125) */
.captcha-frame img {
    flex: 1;
    max-width: 260px;
    border-radius: var(--radius-sm);
    background: #FFFFFF;
    padding: var(--space-1);
    border: 1px solid var(--border);
}
.captcha-loading { opacity: .4; }


/* ═══ 14. Слои: шторка, тосты, скелетоны, пустые состояния ════════════════ */

.sheet-backdrop {
    position: fixed; inset: 0; z-index: 50;
    background: var(--overlay);
    backdrop-filter: blur(2px);
}
.sheet-backdrop[hidden], .sheet[hidden] { display: none; }

.sheet {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 51;
    max-height: 90dvh; overflow-y: auto;
    padding: var(--space-3) var(--page-padding)
             calc(var(--space-6) + env(safe-area-inset-bottom, 0));
    background: var(--surface-raised);
    border-top: 1px solid var(--border);
    border-radius: var(--radius-xl) var(--radius-xl) 0 0;
    box-shadow: var(--shadow-2);
    animation: ego-sheet-in var(--motion-layer) var(--ease-emphasized);
}
@keyframes ego-sheet-in { from { transform: translateY(16px); opacity: 0; } }

.sheet-handle {
    width: 36px; height: 4px;
    margin: 0 auto var(--space-4);
    background: var(--border-strong);
    border-radius: var(--radius-pill);
}

/* На ПК шторка снизу неуместна: диалог показывается по центру окном
   (docs/design.md §3.4, п. 10 — правило читается и в обратную сторону) */
@media (min-width: 768px) {
    .sheet {
        left: 50%; bottom: auto; top: 50%;
        transform: translate(-50%, -50%);
        width: min(560px, calc(100vw - var(--space-16)));
        max-height: 80dvh;
        border: 1px solid var(--border);
        border-radius: var(--radius-xl);
        padding: var(--space-6);
        animation: ego-modal-in var(--motion-layer) var(--ease-emphasized);
    }
    @keyframes ego-modal-in {
        from { transform: translate(-50%, calc(-50% + 12px)); opacity: 0; }
    }
    .sheet-handle { display: none; }
}

.toast-stack {
    position: fixed; z-index: 80;
    left: var(--page-padding); right: var(--page-padding);
    bottom: calc(var(--h-tabbar) + var(--space-4) + env(safe-area-inset-bottom, 0));
    display: flex; flex-direction: column; gap: var(--space-2);
    align-items: center;
    pointer-events: none;
}
@media (min-width: 768px) {
    .toast-stack {
        left: auto; right: var(--space-6); bottom: var(--space-6);
        align-items: flex-end;
    }
}

.toast {
    max-width: 380px;
    padding: var(--space-3) var(--space-4);
    background: var(--surface-raised);
    color: var(--text);
    border: 1px solid var(--border);
    border-left: 3px solid var(--text-muted);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-2);
    font-size: var(--text-small);
    animation: ego-toast-in var(--motion) var(--ease-out);
}
@keyframes ego-toast-in { from { transform: translateY(8px); opacity: 0; } }
.toast-success { border-left-color: var(--success); }
.toast-error { border-left-color: var(--danger); }

.skeleton {
    position: relative;
    overflow: hidden;
    background: var(--fill);
    border-radius: var(--radius-sm);
}
.skeleton::after {
    content: "";
    position: absolute; inset: 0;
    background: linear-gradient(90deg, transparent,
        color-mix(in srgb, var(--surface) 70%, transparent), transparent);
    animation: ego-shimmer 1.2s linear infinite;
}
@keyframes ego-shimmer { from { transform: translateX(-100%); } to { transform: translateX(100%); } }
.skeleton-card { height: 72px; border-radius: var(--radius-lg); }
.skeleton-line { height: 12px; }

.empty-state {
    display: flex; flex-direction: column; align-items: center;
    gap: var(--space-3);
    padding: var(--space-12) var(--space-4);
    text-align: center;
}
.empty-state > svg {
    width: 32px; height: 32px;
    padding: 16px;
    box-sizing: content-box;
    background: var(--accent-soft);
    border-radius: var(--radius-2xl);
    color: var(--accent-text);
}

.install-hint {
    position: fixed; z-index: 40;
    left: var(--page-padding); right: var(--page-padding);
    bottom: calc(var(--h-tabbar) + var(--space-3) + env(safe-area-inset-bottom, 0));
    display: flex; align-items: center; gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    background: var(--surface-raised);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-2);
}
@media (min-width: 768px) {
    .install-hint { left: auto; right: var(--space-6); bottom: var(--space-6); max-width: 420px; }
}


/* ═══ 15. Знак ════════════════════════════════════════════════════════════ */

/* Знак берётся мастером из brand/logo (генератор кладёт его в static/icons).
   Версий две: у знака свои цвета в каждой теме, и перекрасить одну в другую
   нельзя — это разные картинки. Знак статичен и временный (docs/design.md §7.2) */
.logo-mark { display: inline-block; position: relative; flex: none; }
.logo-mark img { display: block; width: 100%; height: auto; }

.logo-mark .on-dark { display: none; }

:root[data-theme="dark"] .logo-mark .on-light,
.theme-dark .logo-mark .on-light { display: none; }
:root[data-theme="dark"] .logo-mark .on-dark,
.theme-dark .logo-mark .on-dark { display: block; }

@media (prefers-color-scheme: dark) {
    :root[data-theme="auto"] .logo-mark .on-light { display: none; }
    :root[data-theme="auto"] .logo-mark .on-dark { display: block; }
}
/* Светлый остров возвращает светлую версию знака внутри тёмной страницы */
.theme-light .logo-mark .on-light { display: block; }
.theme-light .logo-mark .on-dark { display: none; }


/* ═══ 16. Утилиты ═════════════════════════════════════════════════════════ */

[hidden] { display: none !important; }
/* `.is-hidden` намеренно не объявлен глобально: на витрине этот класс означает
   «ещё не появился» (прозрачность и сдвиг), а не «убран из потока», и
   display: none там сломал бы появление секций — landing.css §движение */
/* Стрелка «вперёд» — та же иконка, отражённая: отдельная в наборе не нужна */
.flip { transform: scaleX(-1); }

/* Уважаем системную настройку «меньше движения» (docs/design.md §8) */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 1ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 1ms !important;
        scroll-behavior: auto !important;
    }
    .skeleton::after { display: none; }
}

/* Live-режим: аватар */
.live-avatar {
    width: 72px; height: 72px;
    display: flex; align-items: center; justify-content: center;
    background: var(--gradient-brand);
    border-radius: 50%;
    margin-bottom: var(--space-4);
    box-shadow: 0 0 0 8px rgba(124, 58, 237, .15);
    animation: live-pulse 2s ease-in-out infinite;
}
@keyframes live-pulse {
    0%, 100% { box-shadow: 0 0 0 8px rgba(124, 58, 237, .15); }
    50% { box-shadow: 0 0 0 16px rgba(124, 58, 237, .05); }
}
