/* murmur PWA — messenger layout (Telegram-inspired).
   Mobile-first: одна колонка; на ширине ≥720px — sidebar + chat panel рядом.
   Все ID привязаны к каркасу в index.html.
*/

:root {
    --bg: #17212b;           /* Telegram dark bg */
    --panel: #0e1621;        /* sidebar / header */
    --bubble-out: #2b5278;   /* my messages (blue) */
    --bubble-in: #182533;    /* peer messages (dark) */
    --text: #e1e9f0;
    --text-muted: #708499;
    --accent: #4fa3ff;
    --accent-hover: #6cb6ff;
    --border: #1f2c3a;
    --danger: #f06c6c;
    --safe-top: env(safe-area-inset-top, 0px);
    --safe-bottom: env(safe-area-inset-bottom, 0px);
    --safe-right: env(safe-area-inset-right, 0px);
}

* { box-sizing: border-box; margin: 0; padding: 0; }

/* HTML [hidden] attribute must beat any display:flex/block in this file.
   Without this, the messenger view (.messenger { display: flex }) is
   visible on top of the identity screen and blocks all clicks. */
[hidden] { display: none !important; }

html, body {
    height: 100%;
    width: 100%;
    /* v160x15 (Олег 10:15, пример Авито): position:fixed + overflow:hidden
    на html/body — ПРИЧИНА сталой 783px-поверхности: документ не скроллится,
    iOS после клавиатуры не пересчитывает viewport и не перерисовывает низ.
    У Авито документ скроллируемый (fixed нет) — композер честно доходит до
    физического низа (пиксель-разбор: pill bottom 794.7 = 844 - 49). Убираем
    fixed и overflow:hidden: body 100%, messenger = 100dvh обычно ничего не
    скроллит, но документ СКОЛЛИРУЕМ → iOS пересчитывает viewport и репейнтит,
    а settle() (scroll 0,1→0,0) наконец-то РАБОТАЕТ (на fixed body был no-op). */
}

body {
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
                 "Helvetica Neue", Arial, sans-serif;
    font-size: 15px;
    color: var(--text);
    background: var(--bg);
    -webkit-font-smoothing: antialiased;
    -webkit-tap-highlight-color: transparent;
    padding-right: var(--safe-right);
}

/* v160x9 (Олег 22:35): под .messenger не должно быть НИЧЕГО светлее/темнее.
   Залипшее --vvh (промежуточные высоты анимации клавиатуры) проявлялось как
   тёмная полоса фона body под input-area. html крашится в тот же --bg —
   любое расхождение высот становится невидимым. messenger поверх — шов исчез. */
html {
    background: var(--bg);
}

/* ================= SCREEN: IDENTITY (create / restore) ================= */
.screen {
    position: fixed;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 16px;
    background: var(--bg);
}

.identity-card {
    width: 100%;
    max-width: 380px;
    background: var(--panel);
    border-radius: 12px;
    padding: 28px 20px;
    box-shadow: 0 4px 24px rgba(0,0,0,0.4);
}

.brand {
    font-size: 32px;
    font-weight: 600;
    text-align: center;
    color: var(--accent);
    margin-bottom: 4px;
}

.subtitle {
    text-align: center;
    color: var(--text-muted);
    margin-bottom: 24px;
    font-size: 13px;
}

.identity-error {
    background: rgba(240, 108, 108, 0.15);
    border: 1px solid var(--danger);
    color: var(--danger);
    padding: 10px 12px;
    border-radius: 8px;
    margin-bottom: 16px;
    font-size: 13px;
    word-break: break-word;
}

.identity-section h2 {
    font-size: 13px;
    font-weight: 600;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.5px;
    margin-bottom: 10px;
}

.identity-section input {
    width: 100%;
    padding: 12px 14px;
    background: var(--bg);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: 8px;
    margin-bottom: 10px;
    font-size: 14px;
    font-family: inherit;
}

.identity-section input:focus {
    outline: none;
    border-color: var(--accent);
}

.identity-divider {
    text-align: center;
    margin: 20px 0;
    color: var(--text-muted);
    font-size: 12px;
    position: relative;
}
.identity-divider::before,
.identity-divider::after {
    content: "";
    position: absolute;
    top: 50%;
    width: calc(50% - 24px);
    height: 1px;
    background: var(--border);
}
.identity-divider::before { left: 0; }
.identity-divider::after { right: 0; }

button.primary, button.secondary {
    width: 100%;
    padding: 12px 16px;
    border: none;
    border-radius: 8px;
    font-size: 14px;
    font-weight: 600;
    cursor: pointer;
    font-family: inherit;
}
button.primary { background: var(--accent); color: #fff; }
button.primary:hover { background: var(--accent-hover); }
button.secondary { background: var(--bubble-in); color: var(--text); border: 1px solid var(--border); }
button.small { padding: 8px 12px; font-size: 13px; }

/* ================= MESSENGER (sidebar + chat) ================= */
.messenger {
    display: flex;
    flex-direction: column;  /* mobile-first: stack sidebar & chat vertically */
    /* 100dvh = dynamic visual viewport on iOS Safari (excludes URL bar).
       Falls back to 100vh on older browsers. Combined with viewport-fit=cover
       in <meta>, this gives full-screen layout without cutting off the input
       area below the home indicator. */
    height: 100vh;
    /* v160x4: --vvh ставит app.js из visualViewport.height (вкл. safe-зону).
       100dvh на iOS standalone исключает нижний safe-inset → не доходил
       до физического низа (пустота+светлая полоса body под input).
       Фолбэк 100dvh остаётся для старых браузеров без visualViewport. */
    height: var(--vvh, 100dvh);
    width: 100%;
    /* v160x (Олег 2026-09-20): padding-bottom safe-area УБРАН — он дублировал
       локальные резервы страниц: .input-area на чате (safe-bottom + 8px) и
       .chat-list в списке. Двойной резерв давал ~76px мёртвой зоны снизу
       на чате и тёмную полосу под списком. Каждая страница теперь резервирует
       safe-area сама. */
}

/* SIDEBAR */
.sidebar {
    width: 100%;
    /* call18 (Олег): серый низ списка = фон панели на пустом месте. Красим
       сайдбар в --bg, панельный цвет остаётся только у строк чатов. */
    background: var(--bg);
    display: flex;
    flex-direction: column;
    border-right: 1px solid var(--border);
    /* v160x2 (Олег 2026-09-20): без min-height:0 flex-item не сжимается ниже
       контента (min-height:auto) — сайдбар вылезал за низ вьюпорта на
       высоту переполнения списка, последние чаты недоскролливались.
       Тот же урок, что #234 для .chat-view. */
    min-height: 0;
}

.sidebar-header {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: calc(var(--safe-top) + 10px) 12px 10px 12px;
    border-bottom: 1px solid var(--border);
}

/* v160y18: вернули колокольчик 🔔 для ручного re-subscribe на macOS PWA */
.sidebar-header .notif-btn {
    display: inline-block;
}

.sidebar-footer {
    padding: 12px 12px calc(var(--safe-bottom) + 12px) 12px;
    border-top: 1px solid var(--border);
    background: var(--panel);
}

/* Storage/invite actions: compact two-column tile layout. */
.sidebar-actions-grid {
    width: 100%;
    max-width: 360px;
    margin: 0 auto;
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 7px;
}
.sidebar-actions-grid > button {
    width: 100%;
    min-width: 0;
    min-height: 42px;
    padding: 8px 6px;
    border-radius: 10px;
    font-size: 12px;
    line-height: 1.15;
}
.sidebar-actions-grid .invite-btn-green {
    max-width: none;
}
.sidebar-actions-grid #btn-add-by-link {
    background: #d6a500;
    border-color: #f3cf4a;
    color: #fff;
}
.sidebar-actions-grid #btn-add-by-link:hover {
    background: #edbd00;
    border-color: #ffe27a;
}
.sidebar-actions-grid #btn-logout {
    grid-column: 1 / -1;
}
.action-tile {
    background: #2b5278;
    color: #edf6ff;
    border: 1px solid rgba(125, 190, 235, .4);
    font-weight: 500;
    cursor: pointer;
    font-family: inherit;
    transition: background .2s, border-color .2s;
}
.action-tile:hover { background: #356890; border-color: #78c7f0; }
.action-tile.danger-tile {
    background: transparent;
    color: var(--danger);
    border-color: var(--danger);
}
.action-tile.danger-tile:hover { background: var(--danger); color: #fff; }
#backup-status {
    font-size: 11px;
    color: #8bd5a5;
    padding: 6px 4px 0;
    text-align: center;
}

.invite-btn-green {
    width: 100%;
    max-width: 240px;
    padding: 14px 16px;
    background: #28a745;
    color: #fff;
    border: none;
    border-radius: 12px;
    font-size: 15px;
    font-weight: 600;
    cursor: pointer;
    font-family: inherit;
    transition: background 0.2s;
    text-align: center;
}
.invite-btn-green:hover { background: #218838; }

.logout-btn-danger {
    width: 100%;
    max-width: 240px;
    padding: 10px 16px;
    background: transparent;
    color: var(--danger);
    border: 1px solid var(--danger);
    border-radius: 12px;
    font-size: 13px;
    font-weight: 500;
    cursor: pointer;
    font-family: inherit;
    transition: all 0.2s;
    text-align: center;
}
.logout-btn-danger:hover { background: var(--danger); color: #fff; }

.notif-btn {
    /* v160p (Олег 2026-09-08 21:45 «колокольчика нет и пуши не приходят»):
       вернули кнопку. Lesson #242 (26.08, скрыть по просьбе Олега) отменён —
       теперь колокольчик = индикатор состояния пушей + ручной re-subscribe. */
    background: var(--surface-2);
    border: 1px solid var(--border);
    color: var(--text);
    font-size: 14px;
    cursor: pointer;
    padding: 6px 10px;
    border-radius: 8px;
    margin-left: 6px;
    transition: background 0.15s ease;
}
.notif-btn:hover { border-color: var(--accent); }
.notif-btn.on { background: var(--accent); border-color: var(--accent); color: #fff; }
/* v157 (Олег 31.08 «стрелка назад совсем мелкая»): нормальный tap-target.
   Раньше у .back-btn не было собственных стилей размера — рендерился системным
   дефолтом браузера (мелкий текст-глиф без паддинга). iOS-стиль chevron SVG
   + 44×44px (Apple HIG minimal touch target). */
.back-btn {
    display: none;
    background: transparent;
    border: none;
    color: var(--accent);
    font-size: 26px;
    line-height: 1;
    width: 44px;
    height: 44px;
    padding: 0;
    margin-left: -10px; /* оптическое выравнивание к краю экрана */
    flex-shrink: 0;
    cursor: pointer;
    border-radius: 12px;
    -webkit-tap-highlight-color: transparent;
    align-items: center;
    justify-content: center;
}
.back-btn:active { background: var(--surface-2); }
/* Call action: keep a generous, isolated touch target so taps never land on
   rename/delete gestures. 44px is the iOS minimum; the visual button is
   intentionally larger and clearly separated from destructive actions. */
#btn-call, #btn-video-call {
    flex: 0 0 48px;
    width: 48px;
    height: 44px;
    margin-left: 4px;
    padding: 0;
    border: 1px solid rgba(88, 166, 255, .45);
    border-radius: 14px;
    background: linear-gradient(180deg, rgba(88,166,255,.24), rgba(88,166,255,.10));
    color: #8ec5ff;
    font-size: 24px;
    line-height: 1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    touch-action: manipulation;
    transition: transform .15s ease, background .15s ease, border-color .15s ease;
}
#btn-call:hover, #btn-video-call:hover { background: rgba(88,166,255,.34); border-color: #8ec5ff; }
#btn-call:active, #btn-video-call:active { transform: scale(.94); background: rgba(88,166,255,.42); }
#btn-call:focus-visible, #btn-video-call:focus-visible { outline: 3px solid rgba(142,197,255,.55); outline-offset: 2px; }
#btn-call { color: #ff6b6b; border-color: rgba(255, 88, 88, .58); background: linear-gradient(180deg, rgba(255, 72, 72, .25), rgba(255, 72, 72, .10)); }
#btn-video-call { color: #69a8ff; border-color: rgba(72, 145, 255, .62); background: linear-gradient(180deg, rgba(72, 145, 255, .28), rgba(72, 145, 255, .10)); }
#btn-call span, #btn-video-call span { filter: saturate(1.35); }

.delete-btn {
    margin-left: 4px;
    background: transparent;
    border: none;
    font-size: 20px;
    line-height: 1;
    cursor: pointer;
    width: 44px;
    height: 44px;
    padding: 0;
    border-radius: 12px;
    color: var(--muted);
    transition: background 0.15s, color 0.15s;
}
.delete-btn:hover { color: #d33; background: var(--border); }
.delete-btn:active { background: rgba(221,51,51,0.15); }

.me {
    flex: 1;
    min-width: 0;
}
.me-name {
    font-weight: 600;
    font-size: 15px;
    color: var(--text);
    margin-bottom: 2px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.me-npub {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 11px;
    color: var(--text-muted);
}
#my-npub {
    cursor: pointer;
    font-family: ui-monospace, "SF Mono", Menlo, monospace;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    text-decoration: underline dotted;
}
#my-npub:hover { color: var(--accent); }
.copy-btn {
    background: transparent;
    border: 1px solid var(--border);
    color: var(--text-muted);
    font-size: 10px;
    padding: 2px 6px;
    border-radius: 4px;
    cursor: pointer;
    font-family: inherit;
    flex-shrink: 0;
}
.copy-btn:hover { color: var(--accent); border-color: var(--accent); }

.chat-search-wrap {
    padding: 8px 12px;
    border-bottom: 1px solid var(--border);
}
.chat-search-wrap input {
    width: 100%;
    background: var(--bg);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 8px 12px;
    font-size: 13px;
    font-family: inherit;
}
.chat-search-wrap input:focus { outline: none; border-color: var(--accent); }

/* (.new-chat-wrap стили удалены 2026-08-31 вместе с полем) */

.chat-list {
    flex: 1;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    /* v160x2: flex-child с overflow — тоже нужен min-height:0, иначе не
       сжимается и тянет .sidebar за вьюпорт. */
    min-height: 0;
    /* v160x: последний чат не должен прятаться под home indicator
       при скролле в самый низ — свой safe-резерв вместо .messenger. */
    padding-bottom: max(calc(var(--safe-bottom) + 4px), 8px);
    overscroll-behavior: contain; /* скролл не «протекает» в body */
}

.chat-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 12px;
    background: transparent;
    border: none;
    border-bottom: 1px solid var(--border);
    color: var(--text);
    cursor: pointer;
    width: 100%;
    text-align: left;
    font-family: inherit;
    font-size: 14px;
    /* v160w5f (Олег 29.09 06:42 «не с первого раза тап внутрь чата»):
       touch-action:manipulation убирает double-tap-zoom/tap-delay; hover-стили
       (классический iOS double-tap: первый тап = hover, click = второй) —
       только на мышиных устройствах через @media (hover:hover). */
    touch-action: manipulation;
}
/* v160w5f: hover только на устройствах с мышью — на iOS первый тап
   применял hover-фон и click требовал второго тапа. */
@media (hover: hover) {
    .chat-item:hover { background: var(--border); }
}
.chat-item.active { background: var(--bubble-out); }
.chat-item-avatar {
    width: 40px; height: 40px;
    border-radius: 50%;
    background: var(--accent);
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 600;
    font-size: 16px;
    flex-shrink: 0;
}
.chat-item-body {
    flex: 1;
    min-width: 0;
}
.chat-item-name {
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.chat-item-preview {
    color: var(--text-muted);
    font-size: 12px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    margin-top: 2px;
}
.chat-item-meta {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 4px;
    flex-shrink: 0;
}
.chat-item-time { font-size: 11px; color: var(--text-muted); }
.chat-item-badge {
    background: var(--accent);
    color: #fff;
    font-size: 11px;
    padding: 2px 6px;
    border-radius: 10px;
    min-width: 20px;
    text-align: center;
}
.chat-item-peer-sub {
    display: block;
    font-size: 11px;
    color: var(--text-muted);
    font-weight: 400;
    margin-top: 1px;
}
.chat-item.active .chat-item-peer-sub { color: rgba(255,255,255,0.7); }
.chat-item.active .chat-item-preview { color: rgba(255,255,255,0.8); }
.chat-empty {
    padding: 40px 20px;
    color: var(--text-muted);
    text-align: center;
}
.chat-empty-title { font-size: 16px; margin-bottom: 8px; color: var(--text); }
.chat-empty-hint { font-size: 13px; line-height: 1.5; }
.chat-empty-hint code { background: var(--border); padding: 2px 6px; border-radius: 4px; font-size: 12px; }

/* CHAT VIEW */
.chat-view {
    display: none;
    flex-direction: column;
    flex: 1;
    background: var(--bg);
    min-width: 0;
    /* v160z9 (Олег 27.09 09:32 «при первом заходе в чат видно как пошагово
       страница собирается, буквально доли секунды; повторно — всё плавно»):
       первый заход = рендер-конвейер (decrypt не в кэше, bubbles всплывают
       по мере готовности), повторный — всё из IDB → мгновенно. Панель
       появляется мгновенно (display:none→flex) → сборка видна. Фейд 220ms
       маскирует сборку: один плавный переход вместо пошаговых прыжков.
       Animation рестартует при каждом display:none→flex — на повторных
       заходах 220ms незаметно. */
    animation: chatViewIn 220ms ease-out;
    /* Lesson #234 (Олег 2026-08-26 19:27): min-height: 0 ОБЯЗАТЕЛЬНО для
       flex-column item. Без этого min-height: auto = контент растёт сверх
       flex:1, flex:1 не ужимает → .chat-view выше viewport → body overflow
       → скролл не работает. Симптом: вертикальная ориентация — input bar
       не виден, bubbles обрезаны, scroll не работает. Горизонтальная
       работает случайно (там flex-direction: row, проблема не проявляется). */
    min-height: 0;
}

.chat-header {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: calc(var(--safe-top) + 10px) 12px 10px 12px;
    background: var(--panel);
    border-bottom: 1px solid var(--border);
    flex-shrink: 0;
    /* v160x17 (Telegram Web K): при открытой клавиатуре шапка скрыта —
       нижняя часть клавиатуры не пережимает низ колонки, и композер
       (обычный flex-child) прижимается к низу без дыры. */
    body.keyboard-visible & { display: none; }
}
.chat-peer-name {
    font-weight: 600;
    font-size: 15px;
    color: var(--text);
    display: flex;
    flex-direction: column;
    line-height: 1.15;
    min-width: 0;
    overflow: hidden;
}
.chat-peer-name .peer-name-main {
    font-weight: 600;
    font-size: 15px;
    color: var(--text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.chat-peer-name .peer-name-sub {
    font-weight: 400;
    font-size: 11px;
    color: var(--text-muted);
    font-family: ui-monospace, "SF Mono", Menlo, monospace;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    margin-top: 2px;
}

.chat-peer {
    flex: 1;
    min-width: 0;
    overflow: hidden;
}

.chat-panel {
    flex: 1;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    position: relative;
    /* v160x17: композер снова в потоке — резерв НЕ нужен, убираем. */
    padding-bottom: 0;
}

/* v160z21 (Олег 09.10.2026 11:01: «когда открыта клавиатура, нет возможности
   выйти из чата. Кнопка назад появляется только когда клавиатура закрыта»):
   v160x17 скрывает ВСЮ шапку при открытой клавиатуре (лечит дыру под
   композером) — btn-back живёт в шапке и исчезает. Решение: плавающая
   кнопка «назад» ВНУТРИ .chat-panel (position:absolute — layout ленты не
   трогает, дыру v160x17 не возвращает), видима ТОЛЬКО при открытой
   клавиатуре. Клик программно жмёт btnBack (blur input → клавиатура
   закрывается → вся логика выхода в одном месте). */
.kb-back-btn {
    /* v160z22b (Олег 11:16: «кнопка улетает вверх за границу экрана, видно
       в анимации»): absolute внутри .chat-panel двигался вместе с панелью
       при анимации клавиатуры (панель transform'ится) → кнопка уезжала за
       экран. position:fixed — координаты от вьюпорта, не зависит от
       transform предков. top учитывает высоту статус-бара iOS
       (env(safe-area-inset-top)). */
    position: fixed;
    top: calc(env(safe-area-inset-top, 0px) + 10px);
    left: 10px;
    z-index: 30;
    width: 38px;
    height: 38px;
    border-radius: 50%;
    border: 1px solid var(--border);
    background: var(--panel);
    color: var(--text);
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
    opacity: 0.92;
    -webkit-tap-highlight-color: transparent;
}
.kb-back-btn:active { opacity: 1; transform: scale(0.94); }
.kb-back-btn[hidden] { display: none; }
/* видимая только когда клавиатура открыта (шапка скрыта) И чат открыт */
body.keyboard-visible .kb-back-btn { display: flex; }
body:not(.keyboard-visible) .kb-back-btn { display: none !important; }
/* десктоп: клавиатуры/кнопки нет */
@media (min-width: 720px) { .kb-back-btn { display: none !important; } }

/* v160g4-kbd5 (Олег 11:40 «стабильная сборка без дерганий, классическое
   решение»): легаси w6h-блок (fixed-композер на --kb-inset + padding
   --composer-h) УДАЛЕН — он конфликтовал с классической механикой v160x17
   (Telegram Web K: контейнер по --vvh, композер flex-child, шапка скрыта
   при клавиатуре). Две механики одновременно = все залипания inset'ов.
   Пятничный снапшот без w6h подтверждён Олегом как «работает идеально».
   Писцы --kb-inset/--composer-h в app.js остаются, но потребители в CSS
   исчезли — класс багов устранён архитектурно. */

/* ================= AUDIO CALL UI ================= */
.call-overlay {
    position: fixed;
    inset: 0;
    z-index: 1200;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 12px 20px;
    overflow-y: auto;
    background: rgba(5, 11, 18, .78);
    backdrop-filter: blur(12px);
}
.call-card {
    width: min(360px, 100%);
    max-height: calc(100dvh - 24px);
    box-sizing: border-box;
    overflow-y: auto;
    padding: 26px 22px calc(30px + env(safe-area-inset-bottom, 0px));
    border: 1px solid rgba(120, 170, 220, .25);
    border-radius: 24px;
    background: linear-gradient(160deg, #203449, #111d29);
    box-shadow: 0 18px 60px rgba(0,0,0,.45);
    text-align: center;
}
.call-icon { font-size: 38px; margin-bottom: 12px; }
.call-kicker { color: var(--text-muted); font-size: 13px; }
.call-title { margin-top: 7px; font-size: 24px; color: var(--text); word-break: break-word; }
.call-state { min-height: 24px; margin-top: 8px; color: var(--text-muted); }
.call-video-stage {
    position: relative;
    width: 100%;
    aspect-ratio: 9 / 16;
    max-height: min(62vh, 620px);
    margin: 18px auto 4px;
    overflow: hidden;
    border-radius: 16px;
    background: #071018;
    border: 1px solid rgba(142,197,255,.2);
}
.call-video-stage video { display: block; width: 100%; height: 100%; object-fit: cover; background: #071018; }
@media (min-width: 720px) {
    .call-video-stage { aspect-ratio: 4 / 3; max-height: 58vh; }
}
@media (max-width: 719px) {
    /* iPhone: leave room for both the media controls and the end-call button. */
    .call-card { transform: translateY(-4px); padding-top: 22px; padding-bottom: 18px; }
    .call-video-stage { max-height: 45vh; margin-top: 12px; margin-bottom: 2px; }
    .call-actions { margin-top: 16px; }
}
.call-video-stage #call-local-video {
    position: absolute;
    right: 10px;
    bottom: 10px;
    width: 30%;
    height: 30%;
    border: 2px solid rgba(255,255,255,.78);
    border-radius: 10px;
    object-fit: cover;
    box-shadow: 0 4px 14px rgba(0,0,0,.4);
    background: transparent;
    /* call52: PiP невидим до первого отрисованного кадра (rVFC), затем
       fade-in. До вставки элемента в DOM слоя вообще не существует. */
    opacity: 0;
    transition: opacity .18s ease-out;
}
.call-video-stage #call-local-video.is-live { opacity: 1; }
/* v160g4-call12: круглые кнопки-иконки (WhatsApp/FaceTime style) */
.call-media-action {
    flex: 0 0 auto;
    width: 48px;
    height: 48px;
    min-width: 0;
    padding: 0;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    line-height: 1;
    border: 1px solid rgba(88,166,255,.5);
    background: rgba(88,166,255,.18);
    color: #7cc0ff;
}
.call-media-action svg { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.call-media-action.is-off { background: rgba(255,69,58,.15); border-color: rgba(255,69,58,.55); color: #ff453a; }
.call-decline svg, .call-accept svg, .call-icon svg, #btn-call svg, #btn-video-call svg {
  width: 28px; height: 28px; fill: none; stroke: currentColor; stroke-width: 1.8;
  stroke-linecap: round; stroke-linejoin: round;
}
#btn-call svg, #btn-video-call svg { width: 20px; height: 20px; }
.call-accept { background: #2fb344 !important; }
.call-accept:active { transform: scale(.96); }
.call-media-action:active { transform: scale(.94); }
.call-timer {
    margin-top: 10px;
    color: var(--text);
    font-size: 24px;
    font-variant-numeric: tabular-nums;
    letter-spacing: .04em;
}
.call-error { margin-top: 12px; color: #ff9b9b; font-size: 13px; line-height: 1.35; }
.call-actions { display: flex; gap: 12px; margin-top: 28px; align-items: center; }
/* call31: mute живёт в main-actions (слева от decline); в не-fs сжимаем до 48px */
.call-actions .call-media-action { flex: 0 0 auto; width: 48px; height: 48px; padding: 0; }
.call-action { flex: 1; border: 0; border-radius: 13px; padding: 13px 10px; color: #fff; font: inherit; font-weight: 600; cursor: pointer; }
.call-decline { background: #b94150; }
.call-accept { background: #28a66a; }
.call-action:active { transform: scale(.98); }

/* ================= EMPTY STATE (.no-chat) ================= */
.no-chat {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--text-muted);
}
.no-chat-inner { text-align: center; }
.no-chat h2 { color: var(--accent); margin-bottom: 8px; }

.messages-area {
    flex: 1;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    padding: 12px 8px;
    display: flex;
    flex-direction: column;
    gap: 4px;
    /* v160y34: fade-in при первом открытии чата (смягчает вспышку
       от messagesArea.innerHTML="" при перезапуске PWA). */
    animation: msgAreaIn 180ms ease-out;
    /* Allow messages to scroll past the last bubble; otherwise iOS Safari
       truncates the last message because content is taller than container. */
    min-height: 0;
}

.bubble {
    max-width: min(70%, 260px);
    padding: 7px 11px 6px 11px;
    border-radius: 14px;
    word-wrap: break-word;
    overflow-wrap: break-word;
    white-space: pre-wrap;
    line-height: 1.35;
    position: relative;
    box-shadow: 0 1px 1px rgba(0,0,0,0.15);
    display: flex;
    flex-direction: column;
}
.bubble.out {
    align-self: flex-end;
    background: var(--bubble-out);
    color: var(--text);
    border-bottom-right-radius: 4px;
    text-align: right;
}
.bubble.in {
    align-self: flex-start;
    background: var(--bubble-in);
    color: var(--text);
    border-bottom-left-radius: 4px;
    text-align: left;
}

.bubble.bubble-system {
    align-self: center;
    background: rgba(240, 108, 108, 0.18);
    color: var(--danger);
    border: 1px solid rgba(240, 108, 108, 0.5);
    border-radius: 10px;
    max-width: min(85%, 320px);
    text-align: center;
    font-size: 13px;
}
.bubble.bubble-system .bubble-time { color: rgba(240, 108, 108, 0.7); }
.call-event-text {
    align-self: center;
    margin: 8px 0;
    color: var(--text-muted);
    font-size: 12px;
    line-height: 1.35;
    text-align: center;
    white-space: nowrap;
}

.error-msg {
    padding: 12px;
    margin: 8px;
    background: var(--bubble-in);
    color: var(--text);
    border-radius: 8px;
    text-align: center;
    font-size: 13px;
}
.error-msg .link-btn {
    background: none;
    border: none;
    color: var(--accent);
    cursor: pointer;
    text-decoration: underline;
    padding: 4px 8px;
    font-size: 13px;
}
.bubble-time {
    display: block;
    font-size: 10px;
    color: var(--text-muted);
    margin-top: 3px;
    text-align: right;
}
.bubble.out .bubble-time { color: rgba(225,233,240,0.6); }

/* ═══ v160w6n (Олег 07.10 20:51 «безрамочный формат для фото и видео»): ═══
   Медиа-only бабл (фото/видео без текста) — без фона/паддинга/тени, как в
   Telegram: медиа вплотную, время — полупрозрачной плашкой на самом медиа.
   Сообщения «медиа + текст» остаются обычными баблами (рамка вокруг всего). */
.bubble.bubble-media {
    background: transparent;
    padding: 0;
    box-shadow: none;
    max-width: min(78%, 300px); /* чуть шире текстовых: медиа должно дышать */
    border-radius: 12px; /* скругление для overflow плашки времени по углам медиа */
}
.bubble.bubble-media.out { border-bottom-right-radius: 12px; }
.bubble.bubble-media.in { border-bottom-left-radius: 12px; }
/* Время/галочки поверх медиа — полупрозрачная плашка в углу (внутри бабла,
   поверх attach-списка). Показывается ТОЛЬКО когда текста нет: если текст
   есть — бабл обычный, штатная .bubble-time под текстом остаётся. */
.bubble.bubble-media .bubble-time {
    position: absolute;
    right: 6px;
    bottom: 6px;
    background: rgba(0, 0, 0, 0.45);
    color: rgba(255, 255, 255, 0.92);
    padding: 2px 7px;
    border-radius: 10px;
    font-size: 11px;
    line-height: 1.2;
    z-index: 3;
    pointer-events: none;
    text-shadow: 0 1px 2px rgba(0,0,0,0.6);
}
.bubble.bubble-media .msg-attach-list { margin: 0; } /* был 0 0 4px — лишний зазор снизу */

/* ═══ v160w6p (Олег 07.10 21:26 «рамки бабла с медиа тонкими по бокам»): ═══
   Бабл «медиа + подпись»: медиа вплотную к краям бабла (без боковых 11px),
   скругление только сверху (снизу — у текста). Текст с боковым паддингом.
   Время — штатное под текстом (не плашка — текст нужен фон). */
.bubble.bubble-media-cap {
    padding: 0; /* убираем боковые/верхний паддинг — медиа вплотную */
}
.bubble.bubble-media-cap .msg-attach-list { margin: 0; }
.bubble.bubble-media-cap .attach-image,
.bubble.bubble-media-cap .video-preview {
    border-radius: 14px 14px 0 0; /* верхние углы = радиус бабла, низ прямые */
}
.bubble.bubble-media-cap .msg-body {
    padding: 4px 11px 6px 11px; /* подпись: боковые отступы возвращаем тексту */
}
.bubble.bubble-media-cap .quoted {
    margin: 4px 8px 4px 8px; /* цитата над медиа — с боковым отступом */
}

.day-divider {
    align-self: center;
    background: rgba(0,0,0,0.3);
    color: var(--text-muted);
    padding: 3px 10px;
    border-radius: 10px;
    font-size: 11px;
    margin: 8px 0;
}

/* INPUT AREA */
.input-area {
    display: flex;
    flex-direction: column;
    gap: 6px;
    /* v160x17 (research: Telegram Web A/K source-verified): убрали fixed-композер
       — он красился на промежуточной фазе закрытия клавиатуры и НЕ перерисовывался
       (WebKit bug 254861, family). Telegram-подход: композер — ОБЫЧНЫЙ flex-child
       в конце колонки (tweb: flex: 0 0 auto), шапка ЧАТА СКРЫТА при открытой
       клавиатуре (body.keyboard-visible) — низ клавиатуры прижимается к низу
       колонки, высота освобождается, никаких fixed-элементов внизу вообще. */
    display: flex;
    flex-direction: column;
    gap: 6px;
    z-index: 30;
    flex-shrink: 0;
    /* iOS standalone уже включает нижнюю safe-area в высоту viewport.
       Дополнительный safe-bottom здесь оставлял пустую полосу около 42px
       даже сразу после открытия чата. Оставляем только визуальный отступ. */
    padding: 8px 8px 0 8px;
    /* v160x9: фон = цвет чата (как в Telegram iOS) — шов исчезает. */
    background: var(--bg);
    border-top: 1px solid var(--border);
}

/* call33f: Android — env(safe-area-inset-bottom)=0, композер впритык к клавиатуре.
   Небольшой нижний зазор, чтобы строка ввода не прилипала к клавиатуре. iOS не задет
   (html.android класс ставится только на Android). */
html.android .input-area {
    padding-bottom: 8px;
}

.input-row {
    display: flex;
    gap: 8px;
    align-items: flex-end;
}

#message-input {
    flex: 1;
    background: var(--bg);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: 18px;
    padding: 9px 14px;
    font-size: 15px;
    font-family: inherit;
    resize: none;
    max-height: 120px;
    line-height: 1.4;
}
#message-input:focus { outline: none; border-color: var(--accent); }

.send-btn {
    background: var(--accent);
    color: #fff;
    border: none;
    border-radius: 50%;
    width: 40px;
    height: 40px;
    font-size: 18px;
    cursor: pointer;
    flex-shrink: 0;
    font-family: inherit;
    /* call34/андроид: глиф → (U+2192) тонкий и визуально смещён вверх —
       центрируем флексом и утолщаем штрих жирным начертанием. */
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    font-weight: 700;
    line-height: 1;
}
.send-btn:hover { background: var(--accent-hover); }
.send-btn:disabled { opacity: 0.4; cursor: not-allowed; }

/* Attach button + preview (Олег 2026-08-24 11:00 MSK) */
.attach-btn {
    background: var(--bg);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: 50%;
    width: 40px;
    height: 40px;
    font-size: 18px;
    cursor: pointer;
    flex-shrink: 0;
    padding: 0;
}
.attach-btn:hover { border-color: var(--accent); }
.attachments-preview {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    padding: 0 4px;
    min-height: 0;
    max-height: 100px;
    overflow-y: auto;
}
.attachments-preview:empty { display: none; }
.attach-chip {
    background: var(--accent);
    color: #fff;
    padding: 4px 10px;
    border-radius: 12px;
    font-size: 12px;
    display: inline-flex;
    align-items: center;
    gap: 6px;
}
.attach-chip .x {
    cursor: pointer;
    opacity: 0.7;
}
.attach-chip .x:hover { opacity: 1; }
.attach-chip.rejected {
    background: rgba(255, 152, 0, 0.85); /* warning amber */
    color: #1a1a1a;
    font-weight: 600;
}
.attach-chip.uploading {
    background: var(--accent);
    opacity: 0.7;
    font-style: italic;
}
}
.attach-preview-img { max-width: 80px; max-height: 60px; border-radius: 6px; object-fit: cover; }

/* In-message attachment preview (Олег 2026-08-24 11:00 MSK, image/video/audio) */
.msg-attach-wrapper {
    width: 100%;
    margin-bottom: 4px;
}
.msg-attach-img { max-width: 100%; max-height: 220px; border-radius: 8px; display: block; cursor: pointer; }
.msg-attach-video { max-width: 100%; border-radius: 8px; display: block; }
.msg-attach-audio { width: 100%; }
.msg-attach-file {

    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 6px 10px;
    background: rgba(255,255,255,0.08);
    border-radius: 8px;
    margin-top: 4px;
    color: inherit;
    text-decoration: none;
}

/* ================= DESKTOP (≥720px): two panels side-by-side ================= */
@media (min-width: 720px) {
    .messenger { flex-direction: row; }
    .sidebar { width: 340px; flex-shrink: 0; }
    .chat-view { display: flex; }
    .back-btn { display: none; } /* no back on desktop — sidebar always visible */
    /* v160x17: на десктопе клавиатуры нет — keyboard-visible не ставится,
       шапка всегда видима; композер в потоке как раньше. */
    .input-area { position: static; padding-bottom: 8px; }
    .chat-panel { padding-bottom: 0; }
    .chat-header { display: flex !important; }
}

/* call32: стартовая страница (нет чатов) — список чатов скрыт,
   сайдбар компактный, no-chat занимает весь остаток экрана. */
.messenger.no-chats .chat-list { display: none; }

/* ================= MOBILE: stacked sidebar & chat-view, switch via JS ================= */
@media (max-width: 719px) {
    .messenger { flex-direction: column; }
    .messenger.no-chats .sidebar { flex: 0 0 auto; }
    /* When chat-open is set, hide the sidebar; when not set, hide the chat-view. */
    .messenger.chat-open .sidebar { display: none; }
    .messenger.chat-open .chat-view { display: flex; flex: 1; min-height: 0; }
    .messenger:not(.chat-open) .sidebar { display: flex; flex: 1; min-height: 0; }
    .messenger:not(.chat-open) .chat-view { display: none; }
    .messenger:not(.chat-open) .no-chat { display: flex; flex: 1; }
    .messenger.chat-open .no-chat { display: none; }
    .back-btn { display: flex; }
}

/* ================= STATUS (small text at bottom) ================= */
#status, .status {
    color: var(--text-muted);
    font-size: 11px;
    text-align: center;
    padding: 4px;
}

/* ================= PHASE 3 ATTACHMENTS ================= */
/* Telegram-style: image preview above text bubble, full-width, rounded.
   Bubble-attach shows the file as a separate figure above text. */

.msg-attach-list {
    display: flex;
    flex-direction: column;
    gap: 4px;
    margin: 0 0 4px 0;
    max-width: 100%;
}

.msg-attach-remote {
    background: rgba(255, 255, 255, 0.08);
    border: 1px solid var(--border);
    color: var(--text);
    padding: 6px 10px;
    border-radius: 8px;
    font-size: 12px;
    display: inline-flex;
    align-items: center;
    max-width: 100%;
}

.attach-figure {
    margin: 0;
    padding: 0;
    max-width: 100%;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.attach-image {
    max-width: 100%;
    max-height: 280px;
    width: auto;
    border-radius: 10px;
    display: block;
    cursor: pointer;
    background: #00000010;
}

.attach-video {
    max-width: 100%;
    max-height: 280px;
    border-radius: 10px;
    display: block;
}

/* v160: видео-превью в стиле WhatsApp — кадр + play-кнопка, тап → плеер */
.video-preview {
    position: relative;
    max-width: 100%;
    width: 260px;
    max-height: 280px;
    border-radius: 10px;
    overflow: hidden;
    cursor: pointer;
    background: #000;
    /* v160c: высота фиксируется aspect-ratio из JS (реальные пропорции);
       до загрузки JS ставит 16/9 — wrap не схлопывается */
    min-height: 60px;
}
.video-preview .video-thumb {
    display: block;
    width: 100%;
    height: 100%;
    /* v160y46 (Олег 21:48: «когда в вертикальном видео пытаются заполнить
       черные полоски по бокам и получается не очень. Можно просто оставлять
       как есть и вписывать в рамку превью, сохраняя пропорции?»): cover-кроп
       зумил кадр и портил вертикальные видео. contain — честный кадр целиком
       в рамке, полоски остаются чёрными (это норма). */
    object-fit: contain;
    max-height: 280px;
}
/* v160y34: fallback-чип «🎥 Видео» когда ни постера, ни декодированного
   кадра нет (пустой чёрный квадрат → подпись). Снимается video-frame-ready
   (rVFC в murmurSetupVideoPreview), когда кадр реально показан. */
.video-preview.video-fallback-chip::after {
    content: "🎥 Видео";
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    color: rgba(255,255,255,0.85);
    font-size: 14px;
    font-weight: 500;
    background: rgba(0,0,0,0.35);
    pointer-events: none;
}
.video-preview.video-frame-ready.video-fallback-chip::after {
    content: none; /* кадр готов — чип не нужен */
}
.video-preview .video-play-btn {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 52px;
    height: 52px;
    border-radius: 50%;
    background: rgba(0, 0, 0, 0.55);
    color: #fff;
    font-size: 22px;
    display: flex;
    align-items: center;
    justify-content: center;
    padding-left: 4px; /* оптическая центровка ▶ */
    pointer-events: none;
    box-shadow: 0 2px 8px rgba(0,0,0,0.35);
}
.fullscreen-video {
    max-width: 96vw;
    max-height: 96vh;
    border-radius: 6px;
    background: #000;
}

.attach-audio {
    width: 100%;
    margin: 2px 0;
}

.attach-file {
    display: inline-block;
    padding: 8px 12px;
    background: #ffffff14;
    border-radius: 10px;
    text-decoration: none;
    color: inherit;
    font-size: 14px;
    word-break: break-all;
}

.attach-figure figcaption {
    font-size: 11px;
    color: var(--text-muted);
    padding: 0 2px;
}

.attach-decrypting {
    color: var(--text-muted);
    font-size: 12px;
    padding: 6px 8px;
    background: #ffffff10;
    border-radius: 8px;
    font-style: italic;
}

.attach-error {
    color: #ff6b6b;
    font-size: 12px;
    padding: 6px 8px;
    background: #ff6b6b20;
    border-radius: 8px;
}

.fullscreen-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,0.92);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 9999;
    cursor: pointer;
}

.fullscreen-image {
    max-width: 96vw;
    max-height: 96vh;
    border-radius: 6px;
}

/* v160k8 (Олег 19:23): галочки красятся когда прочитано.
   ✓✓ (msg-read-check) — цветная (читано), ✓ (msg-sent-check) — muted (отправлено). */
.msg-read-check {
    color: #4ade80;
    font-weight: 600;
}
.msg-sent-check {
    color: var(--text-muted);
}

/* v160k9a (Олег 2026-09-05 23:48): Link Unfurl preview card */
.unfurl-card {
    display: flex;
    flex-direction: column;
    margin-top: 6px;
    border-radius: 10px;
    overflow: hidden;
    background: rgba(255, 255, 255, 0.06);
    border: 1px solid rgba(255, 255, 255, 0.10);
    color: inherit;
    text-decoration: none;
    max-width: 320px;
}
.unfurl-card:active {
    opacity: 0.85;
}
.unfurl-img {
    width: 100%;
    background: rgba(255, 255, 255, 0.04);
    aspect-ratio: 16 / 9;
    overflow: hidden;
}
.unfurl-img img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}
.unfurl-meta {
    padding: 8px 10px 10px;
}
.unfurl-site {
    font-size: 11px;
    opacity: 0.6;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    margin-bottom: 2px;
}
.unfurl-title {
    font-size: 14px;
    font-weight: 500;
    line-height: 1.3;
    margin-bottom: 4px;
    overflow: hidden;
    text-overflow: ellipsis;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
}
.unfurl-desc {
    font-size: 12px;
    opacity: 0.7;
    line-height: 1.35;
    overflow: hidden;
    text-overflow: ellipsis;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
}

/* ================= v160p8: LANDING-STYLE IDENTITY SCREEN =================
   Стиль лендинга v3 (Олег одобрил) — на identity-screen (PWA по invite-ссылке).
   Скоуп: #identity-screen — messenger не трогаем. btn-create/error сохранены. */
#identity-screen {
    background: #0f1620;
    background-image:
        radial-gradient(ellipse 60% 35% at 20% 0%, rgba(34,158,217,.13), transparent),
        radial-gradient(ellipse 50% 45% at 85% 100%, rgba(94,234,212,.08), transparent);
    padding: 24px 20px 16px;
    overflow-y: auto;
    align-items: flex-start;
    justify-content: center;
}
#identity-screen .identity-card {
    max-width: 460px;
    width: 100%;
    background: rgba(255,255,255,.045);
    border: 1px solid rgba(255,255,255,.08);
    border-radius: 18px;
    padding: 24px 18px 20px;
    backdrop-filter: blur(20px);
    -webkit-backdrop-filter: blur(20px);
    box-shadow: 0 12px 40px rgba(0,0,0,.35);
    margin-top: max(28px, env(safe-area-inset-top));
}
#identity-screen .brand {
    font-size: 34px;
    font-weight: 800;
    letter-spacing: -.5px;
    text-align: center;
    background: linear-gradient(90deg, #229ED9, #5eead4);
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    display: block;
}
#identity-screen .subtitle {
    text-align: center;
    color: #9fb0c3;
    font-size: 15px;
    margin-bottom: 18px;
}
/* why-private bullets внутри identity-card */
#identity-screen .identity-why {
    margin: 0 0 18px;
    display: flex;
    flex-direction: column;
    gap: 10px;
}
#identity-screen .identity-why .bullet {
    display: flex;
    gap: 10px;
    align-items: flex-start;
    font-size: 14px;
    line-height: 1.5;
    color: #9fb0c3;
}
#identity-screen .identity-why .bullet b { color: #e8eef5; font-weight: 600; }
#identity-screen .identity-why .ic { flex-shrink: 0; width: 22px; text-align: center; font-size: 15px; }
#identity-screen .identity-section h2 {
    color: #9fb0c3;
    text-transform: none;
    letter-spacing: 0;
    font-size: 14.5px;
}
#identity-screen button.primary,
#identity-screen .identity-restore {
    width: 100%;
    padding: 14px;
    border-radius: 14px;
    border: none;
    background: linear-gradient(135deg, #229ED9, #5eead4);
    color: #fff;
    font-size: 16px;
    font-weight: 600;
    box-shadow: 0 8px 24px rgba(34,158,217,.35);
    cursor: pointer;
    font-family: inherit;
}
#identity-screen .identity-restore {
    margin-top: 8px;
    background: linear-gradient(135deg, #1d86c0, #43cbbd);
}
#identity-screen button.primary:hover,
#identity-screen .identity-restore:hover {
    filter: brightness(1.08);
}
#identity-screen .identity-error {
    background: rgba(240, 108, 108, 0.12);
    border: 1px solid rgba(240, 108, 108, 0.5);
    color: #f0939f;
    border-radius: 12px;
}

/* ═══ v160r (Олег 2026-09-17): reply-to — тап на bubble → бар «Ответ» ═══ */
.reply-bar {
    display: flex;
    align-items: stretch;
    gap: 8px;
    padding: 6px 10px;
    margin: 0 10px 4px;
    background: rgba(128, 128, 128, 0.12);
    border-radius: 12px;
}
.reply-bar[hidden] { display: none; }
.reply-bar-accent {
    width: 3px;
    border-radius: 2px;
    background: currentColor;
    opacity: 0.6;
    flex-shrink: 0;
}
.reply-bar-body { flex: 1; min-width: 0; }
.reply-bar-label {
    font-size: 12px;
    font-weight: 600;
    opacity: 0.7;
    margin-bottom: 2px;
}
.reply-bar-preview {
    font-size: 13px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    opacity: 0.85;
}
.reply-bar-cancel {
    align-self: center;
    background: none;
    border: none;
    color: inherit;
    font-size: 14px;
    opacity: 0.6;
    padding: 6px;
    cursor: pointer;
    flex-shrink: 0;
}
/* Цитата внутри bubble адресата */
.bubble .quoted {
    border-left: 3px solid currentColor;
    opacity: 0.65;
    padding: 2px 8px;
    margin: 0 0 4px;
    font-size: 13px;
    border-radius: 6px;
    background: rgba(128, 128, 128, 0.10);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    cursor: pointer;
}
.bubble .quoted .quoted-name {
    font-weight: 600;
    font-size: 12px;
    display: block;
    margin-bottom: 1px;
}

/* v160y34: fade-in ленты сообщений */
@keyframes msgAreaIn {
    from { opacity: 0.4; }
    to { opacity: 1; }
}

/* v160z9: fade-in всей chat-панели (маскирует пошаговую сборку первого
   захода: decrypt-конвейер дёргает layout, панель собирается на глазах) */
@keyframes chatViewIn {
    from { opacity: 0.35; }
    to { opacity: 1; }
}

/* v160z10 (Олег 27.09 10:32 «опять первый заход некрасивый, а второй идеально
   плавно»): фейд 0.35 сборку НЕ спрятал — просвечивает. СПЛОШНАЯ заглушка
   поверх панели на холодном заходе (кэша нет, decrypt-конвейер). bg = фон
   чата, заглушка неотличима от пустой панели. Скрывается после первого
   полного рендера (app.js, фейд 150ms). На тёплом заходе не ставится. */
.chat-splash {
    position: absolute;
    inset: 0;
    background: var(--bg);
    z-index: 5;
    pointer-events: none;
    /* v160z11: спиннер — тап в чат сразу даёт видимую реакцию (кадры 1-3
       видео Олега: 0.5с без отклика = «не работает»). */
    display: flex;
    align-items: center;
    justify-content: center;
}
.chat-splash::after {
    content: "";
    width: 22px;
    height: 22px;
    border: 2px solid var(--border);
    border-top-color: var(--accent);
    border-radius: 50%;
    animation: splashSpin 700ms linear infinite;
}
@keyframes splashSpin {
    to { transform: rotate(360deg); }
}

/* v160y35: error-чип в fullscreen-плеере — видео декодируется, но кадры
   не доставлены (бегунок бежит, экран чёрный). Retry → re-resolve из сервера. */
.fullscreen-overlay .fs-error-chip {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    background: rgba(0,0,0,0.75);
    color: #fff;
    padding: 14px 18px;
    border-radius: 12px;
    font-size: 14px;
    text-align: center;
    z-index: 10;
    pointer-events: auto;
}
.fullscreen-overlay .fs-error-retry {
    display: inline-block;
    margin-top: 8px;
    padding: 6px 14px;
    background: rgba(255,255,255,0.15);
    border: none;
    border-radius: 8px;
    color: #fff;
    font-size: 13px;
    cursor: pointer;
}

/* v160y36: seek-приводимое canvas-превью (iOS Low Power Mode — autoplay
   заблокирован). Кадры шагаются вручную и рисуются в canvas поверх видео. */
.video-preview .video-seek-preview {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: contain;
    pointer-events: none;
    z-index: 1;
}
.video-preview .video-play-btn { z-index: 2; } /* play поверх canvas */

/* v160y55c (Олег 25.09 13:51: «когда открываешь чат, всё плавно появлялось,
   а то сейчас загружается рывками»): плавное появление новых баблов.
   .msg-enter вешается в renderMessages на каждый НОВЫЙ бабл; каскад — через
   animation-delay var(--enter-delay) (40ms * индекс, cap 400ms).
   Инкрементальные поллы получают delay=0 → одно сообщение «выплывает» мягко. */
@keyframes murmurFadeInUp {
    from { opacity: 0; transform: translateY(10px); }
    to   { opacity: 1; transform: translateY(0); }
}
.msg-enter {
    animation: murmurFadeInUp 0.28s cubic-bezier(0.22, 0.61, 0.36, 1) both;
    animation-delay: var(--enter-delay, 0ms);
}
@media (prefers-reduced-motion: reduce) {
    .msg-enter { animation: none; }
}

/* v160g4-call11: fullscreen-UX как в WhatsApp/FaceTime.
   Класс call-fs на ОВЕРЛЕЕ: видео fixed на весь экран, поверх - таймер
   сверху и плавающие полупрозрачные кнопки снизу. Нативный iOS fullscreen
   не используется (прятал PiP и замирал после выхода). */
/* call24: iOS standalone paints the html background (grey --bg) inside the
   bottom safe-area zone where no fixed overlay can reach. The class is
   toggled from call-audio.js (htmlFs): :has() proved unreliable on WebKit. */
html.call-fs-active,
html.call-fs-active body {
    background: #000;
}
.call-overlay.call-fs {
    background: #000 !important;
    padding: 0;
    z-index: 12000;
    backdrop-filter: none !important;
    -webkit-backdrop-filter: none !important;
    height: 100%;
    min-height: 100dvh;
    bottom: 0;
}
.call-overlay.call-fs .call-card {
    background: #000;
    border: none;
    box-shadow: none;
    max-width: none;
    width: 100%;
    height: 100%;
    min-height: 100%;
    display: flex;
    flex-direction: column;
    /* call23 FIX: the portrait media rule puts transform:translateY(-4px) on
       .call-card. A transformed ancestor becomes the containing block for
       position:fixed descendants, so the fullscreen stage inset:0 resolved
       against the card box instead of the viewport (bottom gap + rounded
       corners). Landscape >=720px had no transform - fullscreen was correct
       there only. */
    transform: none;
}
.call-overlay.call-fs .call-icon,
.call-overlay.call-fs .call-kicker,
.call-overlay.call-fs .call-title { display: none; }
.call-overlay.call-fs .call-video-stage {
    position: fixed;
    inset: 0;
    /* call25/28: edge-to-edge attempt; JS (fsFitStage) sets explicit px height.
       call30: paint PROBES (left edge) will tell whether fixed elements can
       paint the bottom safe-zone at all. */
    height: calc(100% + env(safe-area-inset-bottom, 0px));
    z-index: 1;
    aspect-ratio: auto;
    max-height: none;
    margin: 0;
    border-radius: 0;
    border: none;
}
.call-overlay.call-fs .call-video-stage video {
    border-radius: 0;
    background: #000;
}
.call-overlay.call-fs .call-state,
.call-overlay.call-fs .call-timer {
    position: fixed;
    top: calc(env(safe-area-inset-top, 0px) + 14px);
    left: 0;
    right: 0;
    z-index: 3;
    text-align: center;
    margin: 0;
}
/* call34: state-надпись ("На связи"/"Соединение…") и таймер живут в одной
   точке — таймер перекрывал текст без фона и "На связи" просвечивала под
   цифрами. В fs смысл "звонок идёт" уже передан таймером — прячем state. */
.call-overlay.call-fs .call-state { display: none; }
.call-overlay.call-fs .call-media-action {
    flex: 0 0 auto;
    width: 50px;
    height: 50px;
    min-width: 0;
    min-height: 0;
    padding: 0;
    background: rgba(10, 16, 24, .45);
    -webkit-backdrop-filter: blur(10px);
    backdrop-filter: blur(10px);
    border: 1px solid rgba(88,166,255,.5);
    color: #7cc0ff;
}
.call-overlay.call-fs .call-media-action.is-off { background: rgba(255,69,58,.15); border-color: rgba(255,69,58,.55); color: #ff453a; }
.call-overlay.call-fs .call-media-action:active { transform: scale(.94); background: rgba(40, 52, 68, .7); }
.call-overlay.call-fs .call-main-actions {
    position: fixed;
    bottom: calc(env(safe-area-inset-bottom, 0px) + 14px);
    left: 0;
    right: 0;
    z-index: 3;
    justify-content: center;
    /* call31: кнопки звука и видео по бокам от красной (Олег). */
    align-items: center;
    gap: 24px;
    padding: 0;
    margin: 0;
}
/* call31: в fs главные кнопки = mic | decline | camera. accept скрыт,
   media-actions-блок сверху больше не нужен — переносим кнопки сюда. */
.call-overlay.call-fs .call-main-actions #call-mute,
.call-overlay.call-fs .call-main-actions #call-camera {
    display: inline-flex;
    width: 50px;
    height: 50px;
    min-width: 0;
    padding: 0;
    border-radius: 50%;
    background: rgba(10, 16, 24, .45);
    -webkit-backdrop-filter: blur(10px);
    backdrop-filter: blur(10px);
    border: 1px solid rgba(88,166,255,.5);
    color: #7cc0ff;
}
.call-overlay.call-fs .call-main-actions #call-mute.is-off,
.call-overlay.call-fs .call-main-actions #call-camera.is-off {
    background: rgba(255,69,58,.15);
    border-color: rgba(255,69,58,.55);
    color: #ff453a;
}
.call-overlay.call-fs .call-main-actions #call-mute:active,
.call-overlay.call-fs .call-main-actions #call-camera:active { transform: scale(.94); }
.call-overlay.call-fs .call-decline {
    flex: 0 0 auto;
    width: 56px;
    height: 56px;
    min-width: 0;
    padding: 0;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: #e5484d;
    color: #fff;
}
.call-overlay.call-fs .call-accept { display: none; }
.call-overlay.call-fs .call-video-stage #call-local-video {
    top: calc(env(safe-area-inset-top, 0px) + 52px);
    right: 12px;
    bottom: auto;
    width: 24%;
    max-width: 180px;
    height: auto;
    aspect-ratio: 3 / 4;
    border-radius: 12px;
    background: transparent;
}
@media (orientation: landscape) {
    /* call31: Олег — сдвинуть второстепенное окно (PiP) к границе экрана
       в ландшафте: right:0 + скругление только с левой стороны, верх под
       safe-top (чтобы Dynamic Island/чёлку не задевать). */
    .call-overlay.call-fs .call-video-stage #call-local-video {
        width: 20%;
        max-width: 160px;
        top: calc(env(safe-area-inset-top, 0px) + 8px);
        /* call31b: Олег — небольшой зазор до границы экрана. */
        right: 8px;
        border-radius: 12px;
    }
    .call-overlay.call-fs .call-main-actions {
        bottom: calc(env(safe-area-inset-bottom, 0px) + 6px);
    }
}
