.chat-area{flex:1;display:flex;flex-direction:column;height:100vh;height:100dvh;height:var(--app-height,100dvh);overflow:hidden;overflow-x:hidden;position:relative;min-width:0;max-width:100vw}
.welcome-screen{flex:1;display:flex;align-items:center;justify-content:center}
.welcome-content{text-align:center;color:var(--text-secondary)}
.welcome-icon{font-size:80px;filter:drop-shadow(0 0 30px var(--accent))}
.welcome-content h2{font-size:28px;color:var(--accent);margin-bottom:8px}
.active-chat{display:flex;flex-direction:column;height:100vh;height:100dvh;height:var(--app-height,100dvh);position:relative;overflow:hidden;overflow-x:hidden;max-width:100vw}
.chat-header{display:flex;align-items:center;gap:12px;padding:12px 16px;background:var(--bg-secondary);border-bottom:1px solid var(--border);flex-shrink:0;z-index:5}
.back-btn{display:none;background:none;border:none;color:var(--accent);font-size:22px;cursor:pointer;padding:4px 8px}
.chat-header-info{flex:1;display:flex;align-items:center;gap:12px;cursor:pointer;min-width:0;overflow:hidden;user-select:none;-webkit-user-select:none;-webkit-touch-callout:none}
.chat-avatar-wrapper{position:relative;flex-shrink:0}
.chat-header-avatar{width:40px;height:40px;border-radius:50%;object-fit:cover;background:var(--bg-tertiary)}
/* Initials fallback shown in the chat header when the user/group/channel has no avatar
   — mirrors .chat-avatar-placeholder used in the chat list, so an empty <img> never
   shows a broken-image icon. */
.chat-header-avatar-fallback{width:40px;height:40px;border-radius:50%;background:linear-gradient(135deg,var(--accent),var(--accent-hover));display:flex;align-items:center;justify-content:center;font-size:16px;font-weight:700;color:#fff;flex-shrink:0}

/* ===== In-chat search: bar with prev/next + a results panel listing matches ===== */
#chat-search-bar{background:var(--bg-secondary);border-bottom:1px solid var(--border)}
.chat-search-row{display:flex;align-items:center;gap:4px;padding:8px 12px}
.chat-search-row .search-input{flex:1;min-width:0}
.chat-search-count{font-size:12px;color:var(--text-secondary);min-width:36px;text-align:center;white-space:nowrap}
.chat-search-nav{background:none;border:none;color:var(--text-secondary);font-size:16px;width:32px;height:32px;border-radius:8px;cursor:pointer;flex-shrink:0}
.chat-search-nav:hover{background:var(--bg-tertiary);color:var(--text-primary)}
.chat-search-results{max-height:44vh;overflow-y:auto;border-top:1px solid var(--border)}
.chat-search-empty{padding:18px;text-align:center;color:var(--text-muted);font-size:14px}
.chat-search-item{display:block;width:100%;text-align:left;background:none;border:none;border-bottom:1px solid var(--border);padding:10px 14px;cursor:pointer;font-family:inherit;transition:background .12s}
.chat-search-item:hover{background:var(--bg-tertiary)}
.chat-search-item.active{background:rgba(176,110,243,.18)}
.chat-search-item-top{display:flex;justify-content:space-between;gap:8px;margin-bottom:3px}
.chat-search-item-name{font-size:13px;font-weight:600;color:var(--accent);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.chat-search-item-date{font-size:11px;color:var(--text-muted);flex-shrink:0}
.chat-search-item-snippet{font-size:13px;color:var(--text-secondary);line-height:1.4;overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}
.chat-search-item-snippet mark{background:var(--accent);color:#fff;border-radius:3px;padding:0 2px}
.chat-search-media{color:var(--text-muted);font-style:italic}
.online-dot{position:absolute;bottom:0;right:0;width:10px;height:10px;background:var(--success);border-radius:50%;border:2px solid var(--bg-secondary)}
.chat-header-title{font-size:16px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.chat-header-status{font-size:12px;color:var(--text-secondary)}
.chat-header-actions{display:flex;gap:4px;flex-shrink:0}
/* Flex children default to min-width:auto (content size), which lets a long title push
   the search/call buttons off-screen or overlap them. Forcing min-width:0 on the title's
   wrapper lets it actually shrink/ellipsize instead of overflowing the row. */
.chat-header-info>div:not(.chat-avatar-wrapper){min-width:0}
.messages-container{flex:1;overflow-y:auto;overflow-x:hidden;padding:16px 16px 8px;background:var(--bg-primary);overscroll-behavior:contain;-webkit-overflow-scrolling:touch;max-width:100%}
.messages-list{display:flex;flex-direction:column;gap:4px;overflow-x:hidden;max-width:100%}
.message-wrapper{display:flex;flex-direction:column;max-width:75%;min-width:0;position:relative;margin-bottom:4px}
.message-wrapper.outgoing{align-self:flex-end;align-items:flex-end}
/* Entrance animation for a single newly sent/received message. Applied one-shot in
   appendMessage/appendGhost only — NOT on full history re-render — so opening a chat
   with many messages doesn't animate every row. Honors reduced-motion. */
@keyframes msgEnter{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}
.message-wrapper.msg-enter{animation:msgEnter .2s cubic-bezier(.22,.61,.36,1)}
/* Subtle fade/slide when switching into a chat (one-shot class on #active-chat). */
@keyframes chatOpenFade{from{opacity:.55;transform:translateX(10px)}to{opacity:1;transform:none}}
.chat-open-anim{animation:chatOpenFade .18s ease-out}
@media (prefers-reduced-motion:reduce){.message-wrapper.msg-enter,.chat-open-anim{animation:none}}
.message-wrapper.incoming{align-self:flex-start;align-items:flex-start}
.message-sender-name{font-size:12px;color:var(--accent);font-weight:600;margin-bottom:2px;padding:0 12px}
.message-bubble{padding:8px 12px;position:relative}
.message-wrapper.outgoing .message-bubble{background:var(--bg-message-out);border-radius:18px 18px 4px 18px;color:white}
.message-wrapper.incoming .message-bubble{background:var(--bg-message-in);border-radius:18px 18px 18px 4px;color:var(--text-primary);border:1px solid var(--border)}
.message-wrapper .sticker-message,.message-wrapper .message-image,.message-wrapper .message-video{margin:0;display:block}
.msg-text{white-space:pre-wrap;word-break:break-word}
.msg-text a{color:#a78bfa;text-decoration:underline;word-break:break-all}
/* ===== Photo/video staging preview modal (attach-menu → picker → send) ===== */
.media-preview-modal .modal-body{gap:14px}
.media-preview-strip{display:flex;gap:8px;overflow-x:auto;padding-bottom:4px}
.media-preview-item{position:relative;width:84px;height:84px;border-radius:10px;overflow:hidden;flex-shrink:0;background:#000}
.media-preview-item img,.media-preview-item video{width:100%;height:100%;object-fit:cover;display:block}
.media-preview-remove{position:absolute;top:3px;right:3px;width:20px;height:20px;border-radius:50%;background:rgba(0,0,0,.6);color:#fff;border:none;font-size:11px;cursor:pointer;display:flex;align-items:center;justify-content:center;line-height:1}
.media-preview-add{width:84px;height:84px;border-radius:10px;border:2px dashed var(--border);display:flex;align-items:center;justify-content:center;font-size:26px;color:var(--text-muted);cursor:pointer;flex-shrink:0}
.media-preview-add:hover{border-color:var(--accent);color:var(--accent)}

/* ===== Polls ===== */
.poll-card{min-width:220px;max-width:280px}
.poll-question{font-size:15px;font-weight:700;margin-bottom:10px;white-space:pre-wrap;word-break:break-word}
.poll-options-render{display:flex;flex-direction:column;gap:8px}
.poll-option-choice{display:flex;align-items:center;gap:8px;font-size:13px;cursor:pointer;padding:2px 0}
.poll-option-choice input{accent-color:var(--accent);width:16px;height:16px;flex-shrink:0}
.poll-vote-btn{margin-top:4px;background:var(--accent);color:#fff;border:none;border-radius:8px;padding:8px;font-size:13px;font-weight:600;cursor:pointer}
.poll-vote-btn:hover{background:var(--accent-hover)}
.poll-option-result{cursor:pointer;padding:2px 0}
.poll-option-result-row{display:flex;justify-content:space-between;gap:8px;font-size:13px;margin-bottom:3px}
.poll-option-result.mine .poll-option-text{color:var(--accent);font-weight:700}
.poll-option-percent{font-weight:700;flex-shrink:0}
.poll-option-bar-bg{height:5px;border-radius:3px;background:rgba(255,255,255,.12);overflow:hidden}
.poll-option-bar{height:100%;background:var(--accent);border-radius:3px;transition:width .3s ease}
.poll-meta{margin-top:8px;font-size:11px;color:rgba(255,255,255,.55)}
.poll-option-row{display:flex;align-items:center;gap:8px;margin-bottom:6px}
.poll-option-row .auth-input{flex:1}

/* ===== Link preview card (OG image/title), sent messages + compose bar ===== */
.msg-link-preview{display:block;margin-top:6px;border-left:3px solid var(--accent);border-radius:6px;background:rgba(0,0,0,.15);overflow:hidden;text-decoration:none;color:inherit;max-width:280px}
.msg-link-preview-img{width:100%;max-height:160px;object-fit:cover;display:block}
.msg-link-preview-body{padding:6px 10px}
.msg-link-preview-title{font-size:13px;font-weight:700;color:var(--accent);margin-bottom:2px}
.msg-link-preview-desc{font-size:12px;color:rgba(255,255,255,.75);display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}
.link-preview-compose{align-items:center}
.link-preview-thumb{width:44px;height:44px;border-radius:8px;object-fit:cover;flex-shrink:0}
.link-preview-desc{font-size:12px;color:var(--text-secondary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:260px}
/* .message-wrapper blocks selection everywhere (needed so touch long-press/swipe gestures
   don't fight with text selection on mobile) — but a mouse has no such gesture conflict, so
   re-enable selecting the actual message text on desktop/pointer:fine devices. */
@media (hover:hover) and (pointer:fine){
.msg-text{-webkit-user-select:text;user-select:text;cursor:text}
}
.msg-search-highlight{background:rgba(176,110,243,.3);border-radius:4px;padding:0 2px}
.message-meta{display:flex;align-items:center;gap:4px;margin-top:2px;padding:0 4px;font-size:11px;color:var(--text-muted)}
.message-edited{font-style:italic}
.message-status{display:inline-flex;align-items:center;margin-left:1px;line-height:1;color:var(--text-muted);transition:color .15s ease}
.message-status svg{display:block}
.message-status.read{color:var(--accent)}
.message-reply-bubble{display:flex;background:rgba(0,0,0,.15);border-radius:10px;margin-bottom:6px;cursor:pointer;overflow:hidden}
.reply-bubble-line{width:3px;background:var(--accent);flex-shrink:0;border-radius:2px}
.reply-bubble-content{display:flex;flex-direction:column;padding:6px 10px;min-width:0}
.reply-bubble-name{font-size:12px;font-weight:700;color:var(--accent);margin-bottom:2px}
.reply-bubble-text{font-size:12px;color:rgba(255,255,255,.7);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:200px}
.reply-sticker{width:40px;height:40px;object-fit:contain;border-radius:4px}
.message-reactions{display:flex;flex-wrap:wrap;gap:4px;margin-top:4px}
.reaction-badge{background:var(--bg-secondary);border:1px solid var(--border);border-radius:12px;padding:2px 8px;font-size:13px;cursor:pointer;transition:all .15s;display:flex;align-items:center;gap:3px;color:var(--text-primary);user-select:none}
.reaction-badge:hover{background:var(--bg-tertiary);transform:scale(1.05)}
.reaction-picker{position:fixed;display:flex;gap:2px;background:var(--bg-secondary);border:1px solid var(--border);border-radius:30px;padding:6px 10px;box-shadow:0 8px 30px rgba(0,0,0,.5);z-index:2000}
.reaction-picker-btn{background:none;border:none;font-size:22px;cursor:pointer;padding:4px 5px;border-radius:50%;transition:transform .15s,background .15s;line-height:1}
.reaction-picker-btn:hover{transform:scale(1.4);background:var(--bg-tertiary)}
.dbltap-heart-pop{position:fixed;transform:translate(-50%,-50%);font-size:52px;pointer-events:none;z-index:5000;animation:heartPop .65s cubic-bezier(.17,.89,.32,1.49) forwards;filter:drop-shadow(0 4px 12px rgba(0,0,0,.35))}
@keyframes heartPop{0%{opacity:0;transform:translate(-50%,-50%) scale(.3)}30%{opacity:1;transform:translate(-50%,-50%) scale(1.15)}70%{opacity:1;transform:translate(-50%,-58%) scale(1)}100%{opacity:0;transform:translate(-50%,-80%) scale(.9)}}
/* Hover reaction bar for images/stickers on desktop */
/* Positioned in fixed viewport coordinates (via JS, clamped to screen edges) and
   shown only while hovering the actual media element (.hover-reaction-anchor) — not
   the full-width message row. Visibility is JS-driven (.visible class) rather than
   plain CSS :hover, so there's a short grace period to reach the bar across the gap
   between it and the media before it hides (see showHoverReactionBar in messages.js). */
.hover-reaction-bar{position:fixed;left:var(--hrb-left,-9999px);top:var(--hrb-top,-9999px);display:flex;gap:2px;background:var(--bg-secondary);border:1px solid var(--border);border-radius:20px;padding:4px 8px;box-shadow:0 4px 20px rgba(0,0,0,.5);z-index:1000;opacity:0;transition:opacity .15s;pointer-events:none;white-space:nowrap}
.hover-reaction-bar.visible{opacity:1;pointer-events:auto}
.hover-reaction-bar button{background:none;border:none;font-size:18px;cursor:pointer;padding:2px 4px;border-radius:50%;transition:transform .15s;line-height:1}
.hover-reaction-bar button:hover{transform:scale(1.3)}
.delete-menu{position:fixed;background:var(--bg-secondary);border:1px solid var(--border);border-radius:14px;z-index:2000;box-shadow:0 8px 30px rgba(0,0,0,.5);overflow:hidden;min-width:180px}
.delete-menu-btn{display:block;width:100%;padding:13px 18px;background:none;border:none;border-bottom:1px solid var(--border);text-align:left;cursor:pointer;font-size:14px;color:var(--text-primary);transition:background .15s;font-family:inherit}
.delete-menu-btn:last-child{border-bottom:none}
.delete-menu-btn:hover{background:var(--bg-tertiary)}
.delete-menu-btn.danger{color:var(--danger)}
.confirm-delete-text{padding:13px 18px 8px;font-size:13px;color:var(--text-secondary)}
.message-image{max-width:280px;max-height:320px;border-radius:12px;object-fit:cover;cursor:pointer}
.message-video{max-width:300px;max-height:360px;border-radius:12px;background:#000}
.sticker-message{width:140px;height:140px;object-fit:contain}
.sticker-wrap,.msg-image-wrap{display:inline-block}
.voice-message-wrap{display:flex;align-items:center;gap:10px;padding:4px 2px;min-width:180px;max-width:260px}
.voice-play-btn{width:42px;height:42px;border-radius:50%;border:none;background:var(--accent);color:white;cursor:pointer;display:flex;align-items:center;justify-content:center;flex-shrink:0}
.voice-waveform{display:flex;align-items:center;gap:2px;height:28px}
.wave-bar{width:3px;border-radius:2px;background:rgba(255,255,255,.3);flex-shrink:0;min-height:3px}
.voice-duration{font-size:11px;color:rgba(255,255,255,.6)}
.message-file{display:flex;align-items:center;gap:12px;padding:4px 0;cursor:pointer;min-width:180px;max-width:260px}
.file-icon-wrap{width:42px;height:42px;background:rgba(176,110,243,.2);border-radius:12px;display:flex;flex-direction:column;align-items:center;justify-content:center;flex-shrink:0}
.file-icon{font-size:20px}.file-ext{font-size:9px;font-weight:700;color:var(--accent);text-transform:uppercase}
.file-info{flex:1;min-width:0}
.file-name{font-size:13px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.file-size{font-size:11px;color:rgba(255,255,255,.6);margin-top:2px}
.message-input-area{display:flex;align-items:flex-end;gap:8px;padding:12px;background:var(--bg-secondary);border-top:1px solid var(--border);position:relative;z-index:40;flex-shrink:0;width:100%;max-width:100%;overflow:visible}
/* ===== Multi-select messages (long-press → "Выбрать", then tap more to add checkmarks) ===== */
.select-mode-bar{display:flex;align-items:center;gap:12px;padding:12px 16px;background:var(--bg-secondary);border-top:1px solid var(--border);flex-shrink:0}
.select-mode-cancel{background:none;border:none;color:var(--text-primary);font-size:20px;cursor:pointer;padding:4px 8px;border-radius:8px;flex-shrink:0}
.select-mode-cancel:hover{background:var(--bg-tertiary)}
.select-mode-count{flex:1;font-size:15px;font-weight:600;color:var(--text-secondary)}
.select-mode-actions{display:flex;gap:6px;flex-shrink:0}
.select-mode-actions button{background:none;border:none;font-size:19px;cursor:pointer;padding:6px 10px;border-radius:10px;color:var(--text-primary);transition:background .15s}
.select-mode-actions button:hover{background:var(--bg-tertiary)}
.messages-list.select-mode .message-wrapper{cursor:pointer}
.message-select-check{position:absolute;top:-6px;width:22px;height:22px;border-radius:50%;background:var(--bg-tertiary);border:2px solid var(--border);display:none;align-items:center;justify-content:center;font-size:12px;font-weight:700;color:transparent;z-index:2;transition:background .15s,border-color .15s;pointer-events:none}
.message-wrapper.outgoing .message-select-check{right:-6px}
.message-wrapper.incoming .message-select-check{left:-6px}
.messages-list.select-mode .message-select-check{display:flex}
.message-wrapper.selected .message-select-check{background:var(--accent);border-color:var(--accent);color:#fff}
.message-wrapper.selected .message-bubble,.messages-list.select-mode .message-wrapper.selected .media-content{outline:2px solid var(--accent);outline-offset:3px;border-radius:14px}
.attach-btn{background:none;border:none;outline:none;cursor:pointer;font-size:20px;padding:8px;border-radius:50%;transition:background .2s;flex-shrink:0;color:var(--text-secondary);line-height:1}
.attach-btn:hover{background:var(--bg-tertiary)}
.attach-btn:focus,.attach-btn:focus-visible{outline:none;box-shadow:none}
.attach-menu{position:absolute;bottom:70px;left:12px;background:var(--bg-secondary);border:1px solid var(--border);border-radius:14px;z-index:100;box-shadow:0 8px 30px var(--shadow);min-width:200px}
/* ===== @Mention autocomplete (group chats only) ===== */
.mention-autocomplete{position:absolute;bottom:70px;left:12px;right:12px;background:var(--bg-secondary);border:1px solid var(--border);border-radius:14px;z-index:100;box-shadow:0 8px 30px var(--shadow);max-height:220px;overflow-y:auto}
.mention-item{display:flex;align-items:center;gap:10px;padding:10px 14px;cursor:pointer;transition:background .12s}
.mention-item.active,.mention-item:hover{background:var(--bg-tertiary)}
.mention-avatar{width:32px;height:32px;border-radius:50%;background:linear-gradient(135deg,var(--accent),var(--accent-hover));display:flex;align-items:center;justify-content:center;font-size:13px;font-weight:700;color:#fff;flex-shrink:0;overflow:hidden}
.mention-avatar img{width:100%;height:100%;object-fit:cover}
.mention-name{font-size:14px;font-weight:600;flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.mention-username{font-size:12px;color:var(--text-muted);flex-shrink:0}
.mention-highlight{color:var(--accent);font-weight:600;cursor:pointer}
.mention-highlight:hover{text-decoration:underline}
.attach-item{display:flex;align-items:center;gap:10px;padding:13px 18px;cursor:pointer;font-size:14px;color:var(--text-primary);transition:background .15s}
.attach-item:hover{background:var(--bg-tertiary)}
.message-input{flex:1;background:var(--bg-tertiary);border:1px solid transparent;border-radius:20px;padding:10px 16px;color:var(--text-primary);font-size:15px;outline:none;resize:none;max-height:120px;line-height:1.45;font-family:inherit;scrollbar-width:none;-ms-overflow-style:none}
/* No scrollbar on the input itself — Android renders a thin (purple, pre-fix) gutter at
   the right edge of the textarea even on one line; Telegram's input shows none either. */
.message-input::-webkit-scrollbar{width:0;height:0;display:none}
.message-input:focus{border-color:transparent}
.send-btn{background:linear-gradient(135deg,var(--accent),var(--accent-hover));border:none;border-radius:50%;width:42px;height:42px;cursor:pointer;color:white;transition:all .2s;flex-shrink:0;display:flex;align-items:center;justify-content:center}
.voice-btn{background:var(--bg-tertiary);border:1px solid var(--border);border-radius:50%;width:42px;height:42px;font-size:18px;cursor:pointer;transition:all .2s,transform .15s;flex-shrink:0;display:flex;align-items:center;justify-content:center;color:var(--text-primary);touch-action:none;user-select:none}
.voice-btn.recording{background:var(--danger)!important;border-color:var(--danger)!important;animation:recPulse 1s infinite}
.voice-btn.mode-flip{animation:modeFlip .25s ease}
@keyframes recPulse{0%,100%{box-shadow:0 0 0 0 rgba(239,68,68,.4)}50%{box-shadow:0 0 0 8px rgba(239,68,68,0)}}
@keyframes modeFlip{0%{transform:scale(1) rotate(0)}50%{transform:scale(.75) rotate(-12deg)}100%{transform:scale(1) rotate(0)}}
/* While recording, lift the input bar (which hosts the floating lock hint) above the
   record bar so the drag-up-to-lock indicator isn't hidden behind it. */
.message-input-area.recording-active{z-index:60}
/* Keyboard open → drop the home-indicator safe-area padding so there's no gap
   between the input field and the keyboard. */
body.kb-open .message-input-area{padding-bottom:2px}
.record-btn-wrap{position:relative;flex-shrink:0;display:flex}
/* Kept close to the button on purpose: with the record bar (its own send button included)
   already open above the input row the moment recording starts, anything floating much
   higher than ~20px here used to drift up and sit right on top of that send button — the
   further it travelled (extra lift while dragging up, then the locked scale-up), the worse
   the overlap got. Cheap, robust fix: never let it travel far enough to reach the bar above;
   the durable "still recording hands-free" indicator lives in the bar itself instead —
   see #voice-lock-badge / .voice-record-bar.locked below, which has no collision risk. */
.record-lock-hint{position:absolute;bottom:8px;left:50%;transform:translateX(-50%) scale(.7);width:34px;height:34px;border-radius:50%;background:var(--bg-tertiary);border:1px solid var(--border);display:flex;align-items:center;justify-content:center;font-size:15px;opacity:0;pointer-events:none;overflow:hidden;box-shadow:0 4px 14px rgba(0,0,0,.35);transition:opacity .15s,transform .15s,background .15s;--lock-progress:0}
/* The lock is no longer shown just for holding to record (it read as an ugly icon
   "hanging" over the send button). It now fades in ONLY as the finger actually drags
   upward — subtle feedback for the Telegram-style drag-up-to-lock gesture — and is fully
   invisible during a normal hold/tap recording. */
.record-btn-wrap.holding .record-lock-hint{opacity:var(--lock-progress);transform:translateX(-50%) translateY(calc(var(--lock-progress) * -5px)) scale(calc(.85 + var(--lock-progress) * 0.35))}
.record-lock-hint::before{content:'';position:absolute;inset:0;border-radius:50%;background:var(--accent);opacity:var(--lock-progress)}
.record-lock-icon{position:relative;z-index:1;display:block}
.record-lock-hint.locked{opacity:1;background:var(--accent);border-color:var(--accent);animation:none!important;transform:translateX(-50%) scale(1.05)}
@keyframes lockFloat{0%,100%{margin-bottom:0}50%{margin-bottom:2px}}
/* Small bouncing up-chevron right above the lock — kept short for the same reason as above. */
.record-btn-wrap.holding::before{content:'';position:absolute;bottom:46px;left:50%;width:11px;height:11px;border-left:2.5px solid var(--accent);border-top:2.5px solid var(--accent);transform:translateX(-50%) rotate(45deg);pointer-events:none;animation:lockChevron 1s ease-in-out infinite}
@keyframes lockChevron{0%,100%{opacity:.35;margin-bottom:0}50%{opacity:.9;margin-bottom:4px}}
.record-btn-wrap.holding:has(.record-lock-hint.locked)::before{display:none}
/* Persistent "still recording hands-free" indicator, shown inside the record bar itself
   (next to the timer) once locked — unlike the floating hint above the button, this has
   plenty of free space and can never overlap the bar's own cancel/pause/send buttons. */
.voice-lock-badge{display:none;font-size:13px;opacity:.9}
.voice-record-bar.locked .voice-lock-badge{display:inline}
.voice-record-bar.cancel-armed{background:rgba(239,68,68,.14)}
.voice-record-bar.cancel-armed .voice-record-info{color:var(--danger)}
.reply-preview,.edit-preview{display:flex;align-items:center;gap:10px;padding:8px 16px;background:var(--bg-secondary);border-top:1px solid var(--border)}
.reply-bar{width:3px;height:36px;background:var(--accent);border-radius:2px;flex-shrink:0}
.reply-content{flex:1;min-width:0}
.reply-name{font-size:12px;font-weight:700;color:var(--accent);display:block;margin-bottom:2px}
.cancel-reply{background:none;border:none;color:var(--text-muted);cursor:pointer;font-size:18px;padding:4px 8px;border-radius:6px;flex-shrink:0}
.cancel-reply:hover{color:var(--text-primary);background:var(--bg-tertiary)}
.voice-record-bar{display:flex;align-items:center;justify-content:space-between;padding:10px 16px;background:var(--bg-secondary);border-top:1px solid var(--border)}
.voice-record-info{display:flex;align-items:center;gap:10px;color:var(--danger);min-width:0;flex:1}
.voice-record-info span{font-size:14px;font-weight:600}
.voice-record-actions{display:flex;gap:8px;flex-shrink:0}
.voice-record-btn{width:42px;height:42px;border-radius:50%;border:none;font-size:20px;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .2s}
.voice-record-btn svg,.voice-btn svg{display:block}
.voice-record-btn.send svg{margin-left:2px}
.voice-record-btn.cancel{background:var(--bg-tertiary);color:var(--text-primary)}
.voice-record-btn.stop{background:rgba(239,68,68,.16);color:var(--danger);border:1px solid rgba(239,68,68,.35);font-size:16px}
.voice-record-btn.stop:hover{background:rgba(239,68,68,.25)}
.voice-record-btn.send{background:var(--accent);color:white}
/* Hands-free lock engaged: the send button is now the only way to finish, so make it
   impossible to miss instead of relying on the user remembering a hidden gesture. */
.voice-record-bar.locked .voice-record-btn.send{animation:sendPulse 1.2s ease-in-out infinite}
@keyframes sendPulse{0%,100%{box-shadow:0 0 0 0 rgba(176,110,243,.5)}50%{box-shadow:0 0 0 8px rgba(176,110,243,0)}}
.voice-preview{display:flex;align-items:center;gap:8px;background:var(--bg-tertiary);border:1px solid var(--border);border-radius:18px;padding:4px 10px;color:var(--text-primary);min-width:120px;flex:1}
.voice-preview-play{width:28px;height:28px;border-radius:50%;border:none;background:var(--accent);color:white;display:flex;align-items:center;justify-content:center;cursor:pointer;font-size:13px;flex-shrink:0}
/* Voice trim bar: drag the two handles to cut start/end before sending. */
.voice-trim{position:relative;flex:1;height:26px;min-width:100px;touch-action:none;cursor:pointer;display:flex;align-items:center}
.voice-trim-track{position:absolute;left:0;right:0;top:50%;transform:translateY(-50%);height:6px;border-radius:3px;background:rgba(255,255,255,.15)}
.voice-trim-sel{position:absolute;top:50%;transform:translateY(-50%);height:6px;border-radius:3px;background:var(--accent);left:0;width:100%}
.voice-trim-playhead{position:absolute;top:50%;transform:translate(-50%,-50%);width:2px;height:16px;background:#fff;border-radius:1px;left:0;pointer-events:none;box-shadow:0 0 3px rgba(0,0,0,.6)}
.voice-trim-handle{position:absolute;top:50%;transform:translate(-50%,-50%);width:12px;height:22px;border-radius:5px;background:#fff;box-shadow:0 1px 4px rgba(0,0,0,.5);cursor:ew-resize;touch-action:none}
.voice-trim-handle::after{content:'';position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:2px;height:10px;background:var(--accent);border-radius:1px}
#voice-preview-time{flex-shrink:0;font-size:12px;font-variant-numeric:tabular-nums}
#voice-status{color:var(--text-primary);font-weight:600}

/* Round video messages ("кружки") */
.video-note-wrap{display:inline-flex;flex-direction:column;align-items:center;gap:4px}
.video-note-circle{position:relative;width:200px;height:200px;border-radius:50%;background:#000;cursor:pointer;box-shadow:0 4px 20px var(--shadow);flex-shrink:0;isolation:isolate}
/* border-radius directly on the <video> (plus a webkit mask) so it clips to a circle
   even on iOS Safari, where overflow:hidden on the parent does NOT clip video. */
.video-note-video{width:100%;height:100%;object-fit:cover;display:block;border-radius:50%;-webkit-mask-image:radial-gradient(circle,#000 99%,transparent 100%);background:#000}
.video-note-mute{position:absolute;top:9px;right:9px;width:24px;height:24px;border-radius:50%;background:rgba(0,0,0,.45);color:#fff;display:flex;align-items:center;justify-content:center;pointer-events:none}
.video-note-play-icon{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:56px;height:56px;border-radius:50%;background:rgba(0,0,0,.45);color:#fff;display:flex;align-items:center;justify-content:center;pointer-events:none}
.video-note-play-icon svg{display:block;margin-left:3px}
.video-note-badge{position:absolute;bottom:9px;left:50%;transform:translateX(-50%);background:rgba(0,0,0,.55);color:#fff;font-size:12px;font-weight:600;line-height:1;padding:4px 9px;border-radius:11px;pointer-events:none;font-variant-numeric:tabular-nums}
@media (max-width:768px){.video-note-circle{width:170px;height:170px}}

/* White "not yet heard/seen" dot. Incoming: I haven't played it. Outgoing: the
   other side hasn't yet (disappears once they do). Same dot on both sides. */
.media-unheard-dot{width:9px;height:9px;border-radius:50%;background:#fff;flex-shrink:0;box-shadow:0 0 0 2px rgba(255,255,255,.2)}
.voice-unheard{margin-left:2px;align-self:center;background:var(--accent);box-shadow:0 0 0 2px rgba(176,110,243,.25)}
.vnote-unheard{position:absolute;bottom:11px;right:16px;background:#fff;box-shadow:0 0 6px rgba(0,0,0,.6)}

/* Big video-note preview overlay (recording / review before send) */
.video-note-overlay{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:20px;background:rgba(13,0,26,.72);z-index:35;padding-bottom:80px}
.video-note-trim{position:relative;width:min(280px,68vw);height:26px;touch-action:none;cursor:pointer;display:flex;align-items:center}
.video-note-big-wrap{position:relative;width:min(280px,68vw);aspect-ratio:1;isolation:isolate}
.video-note-big-wrap video,.video-note-big-wrap canvas{position:absolute;inset:0;width:100%;height:100%;border-radius:50%;object-fit:cover;background:#000;cursor:pointer;display:block;border:3px solid rgba(255,255,255,.14);box-shadow:0 12px 48px rgba(0,0,0,.6)}
.video-note-big-wrap video.mirrored,.video-note-big-wrap canvas.mirrored{transform:scaleX(-1)}
.video-note-overlay.recording .video-note-big-wrap video,.video-note-overlay.recording .video-note-big-wrap canvas{border-color:var(--danger);animation:vnRecBorder 1.2s ease-in-out infinite}
@keyframes vnRecBorder{0%,100%{box-shadow:0 12px 48px rgba(0,0,0,.6),0 0 0 0 rgba(239,68,68,.35)}50%{box-shadow:0 12px 48px rgba(0,0,0,.6),0 0 0 10px rgba(239,68,68,0)}}
.video-note-big-wrap .video-note-play-icon{pointer-events:none;z-index:2}
.video-note-reframe-hint{position:absolute;bottom:-34px;left:50%;transform:translateX(-50%);white-space:nowrap;font-size:12px;color:rgba(255,255,255,.75);background:rgba(0,0,0,.4);padding:4px 12px;border-radius:12px;pointer-events:none}
.video-note-flip-btn{position:absolute;top:6px;right:6px;width:38px;height:38px;border-radius:50%;border:none;background:rgba(0,0,0,.45);color:#fff;display:flex;align-items:center;justify-content:center;cursor:pointer;z-index:3;transition:background .15s,transform .15s}
.video-note-flip-btn:hover{background:rgba(0,0,0,.6)}
.video-note-flip-btn:active{transform:scale(.9)}
.voice-record-bar{position:relative;z-index:41}

/* Enlarged video-note viewer with circular scrubber */
.vnote-viewer{position:fixed;inset:0;z-index:10001;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:18px;background:rgba(8,0,18,.92);animation:fadeIn .2s ease}
.vnote-viewer-close{position:absolute;top:calc(env(safe-area-inset-top,0px) + 16px);right:18px;background:rgba(255,255,255,.1);border:none;color:#fff;font-size:22px;width:42px;height:42px;border-radius:50%;cursor:pointer}
.vnote-viewer-close:hover{background:rgba(255,255,255,.2)}
.vnote-viewer-download{position:absolute;top:calc(env(safe-area-inset-top,0px) + 16px);right:68px;background:rgba(255,255,255,.1);border:none;color:#fff;width:42px;height:42px;border-radius:50%;cursor:pointer;display:flex;align-items:center;justify-content:center}
.vnote-viewer-download:hover{background:rgba(255,255,255,.2)}
.vnote-viewer-stage{position:relative;width:min(78vw,78vh);aspect-ratio:1;isolation:isolate}
.vnote-viewer-stage video{position:absolute;inset:14px;width:calc(100% - 28px);height:calc(100% - 28px);border-radius:50%;object-fit:cover;background:#000;display:block}
.vnote-viewer-ring{position:absolute;inset:0;width:100%;height:100%;transform:rotate(-90deg);pointer-events:none}
.vnote-viewer-ring-bg{fill:none;stroke:rgba(255,255,255,.18);stroke-width:2}
.vnote-viewer-ring-fill{fill:none;stroke:var(--accent);stroke-width:3;stroke-linecap:round;stroke-dasharray:304.73;stroke-dashoffset:304.73;filter:drop-shadow(0 0 4px rgba(176,110,243,.6))}
.vnote-viewer-scrub{position:absolute;inset:0;border-radius:50%;cursor:pointer;touch-action:none;z-index:3}
.vnote-viewer-stage .video-note-play-icon{width:72px;height:72px;z-index:2}
.vnote-viewer-time{color:#fff;font-size:15px;font-weight:600;font-variant-numeric:tabular-nums;letter-spacing:.03em}
.sticker-picker{position:absolute;bottom:70px;right:12px;width:320px;max-height:380px;background:var(--bg-secondary);border:1px solid var(--border);border-radius:16px;z-index:100;box-shadow:0 8px 30px var(--shadow);display:flex;flex-direction:column}
.sticker-header{display:flex;justify-content:space-between;align-items:center;padding:12px 16px;border-bottom:1px solid var(--border);font-weight:600;font-size:14px}
.sticker-content{overflow-y:auto;padding:12px;flex:1}
.sticker-pack-name{font-size:11px;color:var(--text-secondary);margin-bottom:8px;font-weight:600;text-transform:uppercase;letter-spacing:.5px}
.stickers-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:6px;margin-bottom:16px}
.sticker-item{cursor:pointer;border-radius:10px;padding:6px;transition:background .15s;aspect-ratio:1;display:flex;align-items:center;justify-content:center}
.sticker-item:hover{background:var(--bg-tertiary)}
/* Emoji panel sits just above the input row (same anchor as .sticker-picker/.attach-menu:
   absolute against .message-input-area, bottom:70px ≈ the input row's own height) so the
   text field and its buttons stay visible and usable — only the OS keyboard is swapped
   out (see media.js showEmojiPicker(), which blurs the field first). A position:fixed
   full-viewport sheet used to sit here and covered the input row entirely. */
#emoji-picker{position:absolute;left:0;right:0;bottom:70px;top:auto;width:100%;max-width:100%;height:280px;max-height:42vh;border-radius:16px 16px 0 0;z-index:150}
.emoji-backspace-btn{font-size:18px}
.emoji-header-actions{display:flex;gap:4px;flex-shrink:0}
/* The real #message-input gets physically moved in here while the emoji panel is open
   (see media.js moveInputIntoEmojiPanel) — capped short so a multi-line draft doesn't
   blow up the panel's compact header. */
#emoji-picker .sticker-header{gap:8px}
.message-input.in-emoji-panel{max-height:36px;overflow-y:auto;margin:0}
.sticker-item:hover{background:var(--bg-tertiary)}
.sticker-item img{width:60px;height:60px;object-fit:contain}
.typing-indicator{display:none}
/* Telegram-like: "печатает" lives in the chat header status and the chat-list preview */
.chat-header-status.typing{color:var(--accent)}
.chat-header-status.typing .typing-dots{margin-left:4px}
.chat-item-preview.typing{color:var(--accent)}
.typing-dots{display:inline-flex;gap:3px}
.typing-dots span{width:4px;height:4px;border-radius:50%;background:var(--accent);animation:typingBounce 1.2s infinite ease-in-out}
.typing-dots span:nth-child(2){animation-delay:.15s}
.typing-dots span:nth-child(3){animation-delay:.3s}
@keyframes typingBounce{0%,60%,100%{transform:translateY(0);opacity:.5}30%{transform:translateY(-3px);opacity:1}}
.post-bubble{min-width:190px;overflow:visible}.post-bubble .msg-image-wrap,.post-bubble .message-image,.post-bubble .message-video{max-width:min(420px,70vw)}.post-bubble .sticker-message{width:160px;height:160px}
/* Media-only posts (photo/video/sticker/circle, no caption) skip the padded, coloured
   text-message background — Telegram shows the media floating on its own, not framed
   in a square bubble. Without this, a round video-note circle sat inside a padded
   rounded-rect bubble and visually read as "a square" instead of a circle. */
.message-wrapper.outgoing .message-bubble.post-bubble.media-post,
.message-wrapper.incoming .message-bubble.post-bubble.media-post{background:transparent;border:none;padding:0;color:var(--text-primary)}
.post-bubble.media-post .post-meta-footer{border-top:1px solid var(--border)}
.post-meta-footer{display:flex;justify-content:space-between;align-items:center;margin-top:8px;padding-top:8px;border-top:1px solid rgba(255,255,255,.1);font-size:13px;gap:16px}
.post-comments-btn{background:none;border:none;color:var(--accent);cursor:pointer;font-weight:600;white-space:nowrap}
.post-comments-btn:hover{text-decoration:underline}
.post-reaction-menu{position:fixed;left:var(--reaction-left,8px);top:var(--reaction-top,8px);display:flex;gap:2px;background:var(--bg-secondary);border:1px solid var(--border);border-radius:24px;padding:5px 8px;box-shadow:0 8px 24px rgba(0,0,0,.45);opacity:0;transform:translateY(6px) scale(.98);pointer-events:none;transition:opacity .16s,transform .16s;z-index:3000;white-space:nowrap}
.post-bubble:hover .post-reaction-menu,.post-bubble.show-reactions .post-reaction-menu{opacity:1;transform:translateY(0) scale(1);pointer-events:auto}
.post-reaction-menu button{background:none;border:none;font-size:20px;cursor:pointer;padding:3px 5px;border-radius:50%;line-height:1;transition:transform .15s,background .15s}
.post-reaction-menu button:hover{transform:scale(1.28);background:var(--bg-tertiary)}
.post-reactions{display:flex;flex-wrap:wrap;gap:4px;margin-top:6px}
.post-reaction-badge{background:rgba(176,110,243,.15);border:1px solid var(--border);border-radius:12px;padding:2px 8px;font-size:13px;cursor:pointer;transition:all .15s;display:flex;align-items:center;gap:3px;color:var(--text-primary);user-select:none}
.post-reaction-badge:hover{background:var(--bg-tertiary);transform:scale(1.05)}
@media(max-width:768px){
.back-btn{display:block}.message-wrapper{max-width:88%}.sticker-picker{width:calc(100vw - 24px);right:12px;left:12px}.chat-area,.active-chat{width:100vw;max-width:100vw}.messages-container{padding:12px 10px 6px}.message-input-area{position:relative;bottom:auto;left:auto;right:auto;padding:8px 10px calc(8px + env(safe-area-inset-bottom));gap:6px;background:var(--bg-secondary);box-shadow:0 -2px 12px rgba(0,0,0,.25)}.message-input{min-height:40px;max-height:96px;font-size:16px;border-radius:18px;padding:9px 12px}.attach-btn,.send-btn,.voice-btn{width:40px;height:40px;min-width:40px;padding:0;display:flex;align-items:center;justify-content:center}.message-image{max-width:min(280px,82vw)}.message-video{max-width:min(300px,82vw)}.post-bubble .msg-image-wrap,.post-bubble .message-image,.post-bubble .message-video{max-width:min(360px,82vw)}
}

/* Swipe-to-reply visual hint */
.swipe-reply-icon{position:absolute;left:-34px;top:50%;width:28px;height:28px;border-radius:50%;background:var(--accent);color:#fff;display:flex;align-items:center;justify-content:center;font-size:16px;font-weight:800;opacity:0;transform:translateX(-26px) translateY(-50%) scale(.9);box-shadow:0 6px 18px rgba(176,110,243,.35);pointer-events:none;z-index:2}
.swipe-reply-icon.active{background:linear-gradient(135deg,var(--accent),var(--accent-hover));box-shadow:0 8px 24px rgba(176,110,243,.5)}
.message-wrapper.outgoing .swipe-reply-icon{left:auto;right:-34px}
.forwarded-label{font-size:12px;color:var(--accent);font-style:italic;margin-bottom:5px;opacity:.9}
.swipe-menu-icon{position:absolute;right:-34px;top:50%;width:28px;height:28px;border-radius:50%;background:var(--bg-tertiary);border:1px solid var(--accent);color:#fff;display:flex;align-items:center;justify-content:center;font-size:18px;font-weight:800;opacity:0;transform:translateX(26px) translateY(-50%) scale(.9);box-shadow:0 6px 18px rgba(176,110,243,.25);pointer-events:none;z-index:2}.swipe-menu-icon.active{background:linear-gradient(135deg,var(--accent),var(--accent-hover));box-shadow:0 8px 24px rgba(176,110,243,.5)}.message-wrapper.outgoing .swipe-menu-icon{right:auto;left:-34px}.forwarded-label{display:block;background:none;border:none;padding:0;margin:0 0 5px;color:var(--accent);font-size:12px;font-style:italic;cursor:pointer;text-align:left}.forwarded-label:hover{text-decoration:underline}.context-reactions{display:flex;gap:3px;padding:7px 8px;border-bottom:1px solid var(--border);background:rgba(176,110,243,.08)}.context-reactions button{background:none;border:none;border-radius:50%;font-size:20px;line-height:1;cursor:pointer;padding:5px;transition:transform .12s,background .12s}.context-reactions button:hover{transform:scale(1.22);background:var(--bg-tertiary)}
/* Left swipe on message = reply; the reply icon appears on the right side. */
.swipe-reply-icon.left-swipe{left:auto;right:-34px;transform:translateX(26px) translateY(-50%) scale(.9)}
.message-wrapper.outgoing .swipe-reply-icon.left-swipe{left:auto;right:-34px}

/* Mobile-friendly full-row gestures: the row spans the chat width, while bubbles keep Telegram-like width. */
.message-wrapper{width:100%;max-width:100%;-webkit-user-select:none;user-select:none;-webkit-touch-callout:none;touch-action:pan-y}
.message-wrapper.incoming{align-self:stretch;align-items:flex-start}.message-wrapper.outgoing{align-self:stretch;align-items:flex-end}
.message-wrapper .message-bubble{max-width:75%}.message-wrapper .message-sender-name,.message-wrapper .message-meta,.message-wrapper .message-reactions{max-width:75%}
.message-wrapper .sticker-message,.message-wrapper .msg-image-wrap,.message-wrapper .message-video,.message-wrapper .message-file{max-width:75%}
.message-wrapper .media-content{max-width:75%;min-width:0;position:relative}
.message-wrapper .media-content .sticker-message,.message-wrapper .media-content .msg-image-wrap,.message-wrapper .media-content .message-video,.message-wrapper .media-content .message-file{max-width:100%}
.message-reply-bubble{cursor:pointer}.message-reply-bubble:hover{background:rgba(176,110,243,.16)}
.message-jump-highlight .message-bubble,.message-jump-highlight .sticker-message,.message-jump-highlight .msg-image-wrap,.message-jump-highlight .message-video,.message-jump-highlight .message-file{animation:messageJumpPulse 1.35s ease;border-color:var(--accent)!important;box-shadow:0 0 0 2px rgba(176,110,243,.38),0 0 28px rgba(176,110,243,.35)}
@keyframes messageJumpPulse{0%{transform:scale(1);filter:brightness(1)}25%{transform:scale(1.025);filter:brightness(1.25)}100%{transform:scale(1);filter:brightness(1)}}
@media(max-width:768px){.message-wrapper .message-bubble,.message-wrapper .message-sender-name,.message-wrapper .message-meta,.message-wrapper .message-reactions{max-width:88%}.message-wrapper .sticker-message,.message-wrapper .msg-image-wrap,.message-wrapper .message-video,.message-wrapper .message-file,.message-wrapper .media-content{max-width:88%}}

/* ===== Date separators ===== */
.date-separator{display:flex;justify-content:center;margin:10px 0 6px;pointer-events:none}
.date-separator span{background:rgba(26,10,46,.85);border:1px solid var(--border);color:var(--text-secondary);font-size:12px;font-weight:600;padding:4px 14px;border-radius:14px;box-shadow:0 2px 8px rgba(0,0,0,.25)}
/* .message-wrapper (base rule) sets flex-direction:column, which this inherits unless
   overridden — that silently broke centering here: justify-content:center then centers
   along the (wrong, vertical) main axis, and with no align-items the notice stretches
   to its own max-width instead of sizing to its text, landing flush instead of centered.
   Explicit row + align-items:center fixes both axes regardless of what the base rule does. */
.system-notice-wrapper{display:flex;flex-direction:row;align-items:center;justify-content:center;margin:4px 0;max-width:100%}
/* Plain dim centered text, no pill/bubble — intentionally unobtrusive, not meant to draw
   the eye like an actual message would. */
.system-notice{background:none;border:none;box-shadow:none;color:var(--text-muted);font-size:12px;padding:2px 10px;cursor:pointer;text-align:center;max-width:80%;opacity:.8;transition:opacity .15s}
.system-notice:hover{opacity:1}
.system-notice b{color:var(--text-secondary);font-weight:600}

/* ===== Pinned message bar ===== */
.pinned-bar{display:flex;align-items:center;gap:10px;padding:7px 14px;background:var(--bg-secondary);border-bottom:1px solid var(--border);cursor:pointer;flex-shrink:0;user-select:none;-webkit-user-select:none;-webkit-touch-callout:none}
.pinned-bar:hover{background:var(--bg-tertiary)}
.pinned-bar-line{width:3px;height:30px;background:var(--accent);border-radius:2px;flex-shrink:0}
.pinned-bar-content{flex:1;min-width:0}
.pinned-bar-title{display:block;font-size:11px;font-weight:700;color:var(--accent)}
.pinned-bar-text{font-size:13px;color:var(--text-secondary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.pinned-bar-close{background:none;border:none;color:var(--text-muted);cursor:pointer;font-size:16px;padding:4px 8px;border-radius:6px;flex-shrink:0}
.pinned-bar-close:hover{color:var(--text-primary);background:var(--bg-tertiary)}

/* ===== Floating "to bottom" button with unread counter ===== */
.scroll-down-btn{position:absolute;right:14px;bottom:86px;width:44px;height:44px;border-radius:50%;border:1px solid var(--border);background:var(--bg-secondary);color:var(--text-primary);cursor:pointer;display:flex;align-items:center;justify-content:center;box-shadow:0 6px 20px rgba(0,0,0,.45);z-index:30;transition:transform .15s,background .15s}
.scroll-down-btn:hover{background:var(--bg-tertiary);transform:translateY(-2px)}
.scroll-down-count{position:absolute;top:-7px;left:50%;transform:translateX(-50%);background:var(--accent);color:#fff;font-size:11px;font-weight:700;min-width:20px;height:20px;border-radius:10px;display:flex;align-items:center;justify-content:center;padding:0 6px;box-shadow:0 2px 8px rgba(0,0,0,.4)}

/* ===== Image loading skeleton + fade-in ===== */
/* ===== Media groups (albums): multi-select photo/video send merged into one grid,
   see mergeMediaGroups in messages.js. Deliberately a plain 2-column grid rather than
   Telegram's full dynamic aspect-ratio layout — a much smaller surface to get wrong
   without being able to see it rendered, still reads clearly as "one message". ===== */
.media-group-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:2px;border-radius:12px;overflow:hidden;max-width:320px}
.media-group-grid .msg-image-wrap,.media-group-grid .msg-video-wrap{max-width:none;width:100%;height:100%;border-radius:0;border:none;aspect-ratio:1;display:block}
.media-group-grid .message-image,.media-group-grid .message-video{width:100%;height:100%;max-width:none;max-height:none;object-fit:cover;border-radius:0;display:block}
.media-group-grid[data-count="3"]>.msg-image-wrap:last-child,.media-group-grid[data-count="3"]>.msg-video-wrap:last-child{grid-column:span 2;aspect-ratio:2/1}
.media-group-grid[data-count="1"]{grid-template-columns:1fr}
@media(max-width:768px){.media-group-grid{max-width:min(280px,72vw)}}
.msg-image-wrap{position:relative;border-radius:12px;overflow:hidden}
.msg-image-wrap.loading{min-width:180px;min-height:140px;background:linear-gradient(110deg,var(--bg-tertiary) 8%,rgba(255,255,255,.08) 18%,var(--bg-tertiary) 33%);background-size:200% 100%;animation:imgShimmer 1.3s linear infinite}
@keyframes imgShimmer{to{background-position-x:-200%}}
.msg-image-wrap .message-image{opacity:0;transition:opacity .3s ease}
.msg-image-wrap.img-loaded .message-image{opacity:1}

/* ===== Ghost (sending) messages, upload progress, failures ===== */
.message-wrapper.pending{opacity:.85}
.message-status.pending{color:var(--text-muted)}
.status-failed{color:var(--danger);font-weight:800;font-size:13px;line-height:1}
.upload-overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,.35);border-radius:12px;z-index:3}
.message-bubble .upload-overlay{border-radius:inherit}
.upload-ring{width:52px;height:52px;transform:rotate(-90deg)}
.upload-ring-bg{fill:none;stroke:rgba(255,255,255,.25);stroke-width:3}
.upload-ring-fill{fill:none;stroke:#fff;stroke-width:3;stroke-linecap:round;stroke-dasharray:106.8;stroke-dashoffset:106.8;transition:stroke-dashoffset .2s linear}
.upload-cancel{position:absolute;width:26px;height:26px;border-radius:50%;border:none;background:rgba(0,0,0,.55);color:#fff;cursor:pointer;font-size:12px;display:flex;align-items:center;justify-content:center}
.send-failed-row{display:flex;align-items:center;gap:8px;margin-top:3px;font-size:12px;color:var(--danger)}
.send-failed-row button{background:rgba(239,68,68,.12);border:1px solid rgba(239,68,68,.4);border-radius:10px;color:var(--danger);font-size:12px;padding:3px 10px;cursor:pointer;font-family:inherit}
.send-failed-row button:hover{background:rgba(239,68,68,.25)}

/* ===== Fullscreen media viewer (zoom / pan / download) ===== */
.image-viewer{flex-direction:column}
.image-viewer-topbar{position:absolute;top:calc(env(safe-area-inset-top,0px) + 12px);right:14px;display:flex;gap:8px;z-index:5}
.image-viewer-btn{width:42px;height:42px;border-radius:50%;border:none;background:rgba(255,255,255,.1);color:#fff;font-size:18px;cursor:pointer;display:flex;align-items:center;justify-content:center}
.image-viewer-btn:hover{background:rgba(255,255,255,.22)}
.image-viewer-stage{width:100vw;height:100vh;height:100dvh;display:flex;align-items:center;justify-content:center;overflow:hidden;touch-action:none}
#image-viewer-img{max-width:92vw;max-height:88vh;border-radius:8px;object-fit:contain;transition:transform .12s ease-out;cursor:zoom-in;user-select:none;-webkit-user-drag:none}
#image-viewer-img.zoomed{cursor:grab;transition:none}
#image-viewer-video{max-width:92vw;max-height:88vh;border-radius:8px;background:#000}

/* ===== Video-note viewer speed button ===== */
.vnote-speed-btn{position:absolute;top:calc(env(safe-area-inset-top,0px) + 16px);left:18px;background:rgba(255,255,255,.1);border:none;color:#fff;font-size:14px;font-weight:700;min-width:42px;height:42px;padding:0 12px;border-radius:21px;cursor:pointer;font-variant-numeric:tabular-nums}
.vnote-speed-btn:hover{background:rgba(255,255,255,.2)}

/* ===== Thin video-message wrapper, Telegram-like (tap to open fullscreen) ===== */
.msg-video-wrap{position:relative;border-radius:12px;overflow:hidden;cursor:pointer;display:inline-block;line-height:0;border:1px solid rgba(255,255,255,.06)}
.msg-video-wrap .message-video{max-width:300px;max-height:360px;display:block;background:#000}
.video-play-overlay{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:52px;height:52px;border-radius:50%;background:rgba(0,0,0,.5);color:#fff;display:flex;align-items:center;justify-content:center;pointer-events:none}
.video-play-overlay svg{margin-left:3px}
.msg-image-wrap{border:1px solid rgba(255,255,255,.06)}
@media(max-width:768px){.msg-video-wrap .message-video{max-width:min(300px,82vw)}}

/* ===== Voice waveform: seekable, with a "played" overlay recoloring the same bars ===== */
.voice-progress{position:relative;cursor:pointer;touch-action:none}
.voice-waveform-played{position:absolute;top:0;left:0;overflow:hidden;width:0%;pointer-events:none}
.voice-waveform-played .wave-bar{background:#fff}

/* ===== Generic circular "✕" close button — used by every modal-header / sticker-header ===== */
.modal-header button,.sticker-header button{background:none;border:none;color:var(--text-muted);font-size:20px;width:34px;height:34px;border-radius:50%;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:background .15s,color .15s;flex-shrink:0;line-height:1}
.modal-header button:hover,.sticker-header button:hover{background:var(--bg-tertiary);color:var(--text-primary)}
