/* Stage 1: accounts + Messages. All new rules, additive to chat.css --
   nothing here overrides an existing chat.css selector. Uses the same
   generated --bg-*, --text-*, --border-* tokens (theme.css) as the rest
   of the site, so light/dark just work. */

/* Shared button component -- same shape and the same --btn-* tokens
   (theme.css) as the home page's own CTA buttons ("Открыть чат"/
   "Открыть раздел", .home-card a in index.html), instead of a
   separately-invented style. Dark mode is black, not a blue fill: see
   the --btn-primary-*, --btn-secondary-* dark overrides in theme.css.
   2026-07-27: the two lines above used to write this same "family of
   tokens" idea as --foo-(star)(slash)--bar-(star), an informal shorthand
   that happens to spell out the CSS close-comment token itself -- every
   real browser CSS parser read that as the actual end of THIS comment,
   parsed everything after it as garbage up to the next real close-
   comment token, and silently dropped the very next rule
   (.primary-button, .secondary-button itself, with its appearance/
   padding/border-radius/font-size) from the whole stylesheet. Same
   author habit already broke theme.css's :root{} block once before --
   never write (star)(slash) as two adjacent characters inside a comment,
   here or anywhere else in this codebase. */
.primary-button, .secondary-button {
    /* appearance:none -- without it, a real <button>/<input type=submit>
       (unlike the home page's own reference buttons, which are plain
       <a> tags and never have this problem) can keep native OS widget
       chrome in some browsers/platforms that ignores border-radius and
       lets a system font bleed through despite font:inherit below --
       this is why the auth modal's Sign-in/Create-account submit
       buttons could look square and off-font while every <a>-based
       button on the site looked fine. */
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: var(--btn-padding);
    border-radius: var(--btn-radius);
    font: inherit;
    font-size: var(--btn-font-size);
    cursor: pointer;
    transition: background-color .15s ease, border-color .15s ease;
}
.primary-button {
    color: var(--btn-primary-text);
    background: var(--btn-primary-bg);
    border: 1px solid var(--btn-primary-border);
    font-weight: var(--btn-primary-weight);
}
.primary-button:hover { background: var(--btn-primary-bg-hover); border-color: var(--btn-primary-border-hover); }
.primary-button:disabled, .secondary-button:disabled { opacity: .55; cursor: not-allowed; }

.secondary-button {
    color: var(--btn-secondary-text);
    background: var(--btn-secondary-bg);
    border: 1px solid var(--btn-secondary-border);
    font-weight: var(--btn-secondary-weight);
}
.secondary-button:hover { background: var(--btn-secondary-bg-hover); border-color: var(--btn-secondary-border-hover); }

.primary-button:focus-visible, .secondary-button:focus-visible, button:focus-visible, input:focus-visible {
    outline: 2px solid var(--blue);
    outline-offset: 2px;
}

/* Same colors/border/font-family as every other .primary-button/
   .secondary-button on the site -- just sized up for the guest gate's
   Sign-in/Create-account CTAs and the auth modal's own submit buttons,
   which are each effectively the one decision on their whole screen and
   looked too small/unprominent at the compact --btn-* size everywhere
   else (menus, modal Close, etc.) uses. A slightly bigger radius than
   --btn-radius (8px) so it still reads as "rounded" at this larger
   size, not a second style. */
.btn-lg.primary-button, .btn-lg.secondary-button {
    padding: 13px 26px;
    font-size: 15px;
    border-radius: 10px;
}

/* --- Header additions ------------------------------------------------- */

.msg-signin-button, .msg-uin-badge {
    display: inline-flex;
    align-items: center;
    min-height: 34px;
    padding: 6px 14px;
    border-radius: 999px;
    border: 1px solid var(--border-e1e5ea);
    background: var(--bg-fff);
    color: var(--text-20242a);
    font-weight: 700;
    font-size: 13px;
    cursor: pointer;
    flex-shrink: 0;
}
/* Same green tokens as the old Hall UI's own "uid-card" identity
   (chat.css --uid-card-border) -- the one thing that should always stay
   visible and recognizable regardless of where everything else (RU/EN,
   theme, Settings, Sign out) ends up. */
.msg-uin-badge { border-color: var(--uid-card-border); }
.msg-signin-button {
    background: var(--bg-0f62c9); color: var(--text-fff); border-color: transparent;
    /* Same corner radius/padding/font-size as every .primary-button on
       the site, overriding the pill shape the shared rule above gives
       .msg-uin-badge (that one stays a pill-shaped identity chip, not
       an action button -- "Войти" is the one that should read as a real
       button). Keeps its own solid blue fill rather than
       .primary-button's translucent-white one (theme.css), which is
       tuned for the colorful home-page cards it sits on and would be
       nearly invisible against this plain white header. */
    border-radius: var(--btn-radius);
    padding: var(--btn-padding);
    font-size: var(--btn-font-size);
}
.msg-signin-button:hover { background: var(--bg-0b56b3); }
.msg-uin-badge:hover { background: var(--bg-f2f5f9); }

/* --- Mobile header: two clean rows instead of one crowded one --------------
   Row 1: "Verstak", centered, with the UIN badge pinned to the top-LEFT
   corner (position:absolute against .messenger-topbar -- .header-actions
   itself is never position:relative, so this escapes through it
   correctly regardless of DOM nesting). Row 2: language switch + theme
   toggle, centered. Settings/Sign out ("other modules") no longer render
   in the header at all on ANY viewport -- they moved into the account
   drawer (tap the UIN badge), which is what actually stops them
   crowding the header/chat on a small screen. */
@media (max-width: 640px) {
    .messenger-topbar { position: relative; }
    .brand-link { width: 100%; justify-content: center; }
    .msg-uin-badge {
        position: absolute;
        top: 12px; left: 14px;
    }
    .header-actions {
        width: 100%;
        margin-left: 0;
        justify-content: center;
    }
}

/* --- Guest gate --------------------------------------------------------- */

.msg-guest-gate {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 60vh;
    padding: 24px;
}
.msg-guest-gate-card {
    max-width: 420px;
    width: 100%;
    text-align: center;
    padding: 32px 24px;
    border: 1px solid var(--chat-panel-border);
    border-radius: 16px;
    background: var(--bg-fff);
}
.msg-guest-gate-icon { width: 96px; height: 96px; border-radius: 50%; object-fit: cover; margin-bottom: 16px; }
.msg-guest-gate-card p { font-size: 15px; line-height: 22px; color: var(--text-20242a); margin: 0 0 20px; }
.msg-guest-gate-actions { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; }

/* --- Layout -------------------------------------------------------------- */

.msg-wrapper { flex: 1; min-height: 0; display: flex; flex-direction: column; }

.msg-mobile-tabs { display: none; }

.msg-layout {
    flex: 1;
    min-height: 0;
    display: grid;
    grid-template-columns: 320px 1fr;
}

.msg-sidebar {
    border-right: 1px solid var(--chat-panel-border);
    background: var(--chat-panel-bg-sidebar);
    display: flex;
    flex-direction: column;
    min-height: 0;
}

.msg-stage { min-height: 0; display: flex; flex-direction: column; }

.msg-pane { flex: 1; min-height: 0; display: flex; flex-direction: column; }
/* Same bug class as .msg-modal-overlay[hidden] / .msg-auth-form[hidden]
   below: .msg-pane{display:flex} and the [hidden] attribute that
   showPane() (messages.js) toggles on #jack-pane/#conversation-pane/
   #empty-pane are equal (0,1,0) specificity, so without this the LATER
   rule (this one) always won and every pane -- Jack, the open user
   conversation, and the empty state, each with its own composer/menu --
   rendered stacked on top of each other regardless of which one was
   supposed to be active. This is the actual fix for "multiple composers
   / Select-a-conversation showing while Jack is selected"; showPane()
   itself never needed to change. */
.msg-pane[hidden] { display: none; }

.msg-empty-pane {
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--muted);
    font-size: 14px;
}

/* --- Conversation list ----------------------------------------------------- */

.msg-find-uin {
    display: flex;
    gap: 8px;
    padding: 12px;
    border-bottom: 1px solid var(--chat-panel-border);
}
/* #find-uin-button ("Начать диалог") carries chat.css's .send-button
   class, but that class alone only sets color/background/min-width --
   the shared border-radius/padding/font-size/cursor actually come from
   chat.css's ".chat-input button, ..." compound selector, which
   #find-uin-button never matches (it lives in .msg-find-uin, not
   .chat-input). Replicated here rather than editing that chat.css
   selector, matching this file's own "additive, never overrides
   chat.css" rule.

   Deliberately a DIFFERENT, softer color from "Отправить"'s vivid blue
   -- a secondary, less attention-grabbing action ("start a new
   conversation" vs. the primary in-conversation "send") gets the site's
   existing --green/--green-soft tokens (theme.css) instead of a second,
   invented palette. */
.msg-find-uin .send-button {
    border-radius: 6px;
    padding: 9px 12px;
    font: inherit;
    font-size: 13px;
    cursor: pointer;
    background: var(--green-soft);
    border-color: var(--green);
    color: var(--green);
}
.msg-find-uin .send-button:hover {
    background: var(--green);
    color: var(--btn-primary-text);
}
.msg-find-uin input {
    flex: 1;
    min-width: 0;
    padding: 10px 12px;
    border: 1px solid var(--chat-input-border);
    border-radius: 8px;
    background: var(--bg-fff);
    color: var(--text-20242a);
    font-size: 16px;
}
.msg-inline-error { padding: 0 12px 10px; color: var(--red); font-size: 13px; }

/* Chats/Archived switcher -- lives directly under the UIN search box,
   inside this same left column (never a separate page/column/settings
   entry, per the exact placement the task fixes). Same is-active token
   pair as .msg-mobile-tab below, reused rather than inventing a second
   "active tab" color. */
.msg-list-tabs {
    display: flex;
    gap: 8px;
    padding: 0 12px 12px;
    border-bottom: 1px solid var(--chat-panel-border);
}
.msg-list-tab {
    flex: 1;
    min-height: 44px;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    border: 1px solid var(--chat-panel-border);
    border-radius: 8px;
    background: var(--bg-fff);
    color: var(--text-20242a);
    font-weight: 700;
    font-size: 13px;
    cursor: pointer;
    font-family: inherit;
}
.msg-list-tab.is-active { background: var(--bg-0f62c9); color: var(--text-fff); border-color: transparent; }
/* Deliberately NOT --red: that color is reserved for "needs your
   attention" (unread count, .msg-unread-badge) -- an archive count is
   just a neutral tally, not an alert, so it uses the same muted tone as
   the rest of the site's secondary text/icons instead. */
.msg-archive-badge {
    min-width: 18px; height: 18px; padding: 0 5px;
    border-radius: 999px; background: var(--muted); color: #fff;
    font-size: 10.5px; font-weight: 800; line-height: 1;
    display: inline-flex; align-items: center; justify-content: center;
}
.msg-archive-badge[hidden] { display: none; }
.msg-list-tab.is-active .msg-archive-badge { background: rgba(255,255,255,.3); }

.msg-conversation-list { flex: 1; overflow-y: auto; }

.msg-conversation-item {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 12px;
    border: none;
    border-bottom: 1px solid var(--chat-panel-border);
    background: transparent;
    text-align: left;
    cursor: pointer;
    color: var(--text-20242a);
    font-family: inherit;
}
.msg-conversation-item:hover { background: var(--bg-f2f5f9); }
.msg-conversation-item.is-active { background: var(--private-card-bg); }

/* Regular conversation rows (both Chats and Archived tabs) are a <div>
   wrapper -- not a single <button> like Jack's pinned item above -- since
   a real per-row ⋮ action menu needs a SEPARATE <button> next to the
   "open conversation" one (a <button> cannot itself contain another
   <button>). .msg-conversation-item's own padding/hover/is-active above
   stay exactly as-is (Jack's button still uses them directly); this
   variant only relocates the padding down onto the inner clickable area
   so the ⋮ button gets its own full-height, unpadded touch column. */
.msg-conversation-item.msg-conversation-row { padding: 0; align-items: stretch; }
.msg-conversation-item-main {
    display: flex;
    align-items: center;
    gap: 10px;
    flex: 1;
    min-width: 0;
    padding: 12px;
    border: none;
    background: transparent;
    text-align: left;
    cursor: pointer;
    color: inherit;
    font: inherit;
}
.msg-row-menu { position: relative; display: flex; align-items: center; flex-shrink: 0; padding-right: 4px; }
.msg-row-menu-button {
    width: 44px;
    height: 44px;
    display: flex;
    align-items: center;
    justify-content: center;
    border: none;
    background: transparent;
    cursor: pointer;
    font-size: 18px;
    line-height: 1;
    color: var(--muted);
    border-radius: 8px;
}
.msg-row-menu-button:hover { background: var(--bg-fff); color: var(--text-20242a); }
/* The row-level action menu reuses .msg-dropdown's own look (below)
   as-is -- .msg-row-menu is itself position:relative, so .msg-dropdown's
   existing top:100%/right:0 already anchors correctly under the row's own
   ⋮ without any override. */

.msg-conversation-avatar-wrap { position: relative; flex-shrink: 0; }

.msg-conversation-avatar {
    width: 40px; height: 40px; border-radius: 50%;
    display: flex; align-items: center; justify-content: center;
    overflow: hidden; flex-shrink: 0;
    background: var(--bg-eaf3ff);
}
.msg-conversation-avatar img { width: 100%; height: 100%; object-fit: cover; }
.msg-conversation-avatar-uin { font-weight: 800; font-size: 13px; color: var(--text-0f62c9); }

/* Four presence states (2026-07-24 online/away/offline/unavailable
   redesign) -- green/grey are the same tokens as the old Hall UI's
   .uid-status dot (chat.css); --amber is the site's existing warning/
   attention token (theme.css), reused here for Away rather than
   inventing a second yellow. Unavailable is deliberately NOT a solid
   color at all (a hollow/outlined ring) -- it means "the server
   couldn't determine a status", which must look visually distinct from
   a confident, solid Offline dot, never like a red/error state (the
   task spec explicitly rules out red for plain Offline). */
.msg-presence-corner-dot {
    position: absolute; right: -1px; bottom: -1px;
    width: 11px; height: 11px; border-radius: 50%;
    border: 2px solid var(--chat-panel-bg-sidebar);
    background: var(--bg-9ca3af);
}
.msg-presence-corner-dot.presence-online { background: var(--bg-22c55e); }
.msg-presence-corner-dot.presence-away { background: var(--amber); }
.msg-presence-corner-dot.presence-offline { background: var(--bg-9ca3af); }
.msg-presence-corner-dot.presence-unavailable { background: transparent; border-color: var(--bg-9ca3af); }

.msg-presence { display: inline-flex; align-items: center; gap: 5px; font-size: 12px; color: var(--muted); }
.msg-presence-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--bg-9ca3af); flex-shrink: 0; }
.msg-presence-dot.presence-online { background: var(--bg-22c55e); }
.msg-presence-dot.presence-away { background: var(--amber); }
.msg-presence-dot.presence-offline { background: var(--bg-9ca3af); }
.msg-presence-dot.presence-unavailable { background: transparent; border: 1.5px solid var(--bg-9ca3af); }
/* Same [hidden]-vs-class-specificity bug as .msg-pane/.msg-dropdown
   above -- openConversation() (messages.js) toggles this purely via
   .hidden. */
.msg-presence[hidden] { display: none; }

.panel-title-group { display: flex; flex-direction: column; align-items: center; gap: 2px; min-width: 0; flex: 1; }
.panel-title-group .panel-title { width: auto; }

.msg-conversation-meta { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.msg-conversation-title { font-weight: 700; font-size: 14px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.msg-conversation-subtitle { font-size: 12px; color: var(--muted); }

.msg-unread-badge {
    min-width: 20px; height: 20px; padding: 0 6px;
    border-radius: 999px; background: var(--red); color: #fff;
    font-size: 11px; font-weight: 800;
    display: flex; align-items: center; justify-content: center;
    flex-shrink: 0;
}

/* white-space:pre-line -- msg_empty_state/msg_archive_empty_state carry a
   real "\n" (section 17's two-line copy); plain HTML text flow would
   otherwise collapse it to a single space like any other whitespace. */
.msg-empty-state { padding: 20px 16px; color: var(--muted); font-size: 13px; text-align: center; white-space: pre-line; }

/* --- Open conversation / Jack pane ----------------------------------------- */

.send-button:disabled { opacity: .55; cursor: not-allowed; }
.chat-input input:disabled { opacity: .6; cursor: not-allowed; }

/* "Jack is thinking" indicator: sendJackQuestion()/askJackPrivately()
   (messages.js) insert this bubble right after the question and remove
   it the instant a reply (or error) arrives -- previously nothing was
   shown at all while awaiting /api/v1/jack/ask, which was
   indistinguishable from the page having frozen. */
.msg-typing-row { display: flex; align-items: center; gap: 8px; color: var(--muted); }
.msg-typing-dots { display: inline-flex; align-items: center; gap: 4px; }
.msg-typing-dots span {
    width: 6px; height: 6px; border-radius: 50%;
    background: var(--muted);
    animation: msg-typing-bounce 1.2s infinite ease-in-out;
}
.msg-typing-dots span:nth-child(2) { animation-delay: .15s; }
.msg-typing-dots span:nth-child(3) { animation-delay: .3s; }
@keyframes msg-typing-bounce {
    0%, 60%, 100% { transform: translateY(0); opacity: .5; }
    30% { transform: translateY(-4px); opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
    .msg-typing-dots span { animation: none; opacity: .8; }
}

.msg-back-button {
    display: none;
    border: none; background: transparent; font-size: 20px; cursor: pointer;
    color: var(--text-20242a); padding: 4px 8px;
}

/* Restores the Hall-era "Очистить" button, missing from the Stage 1
   rewrite despite the backend endpoint (/api/v1/jack/clear) still being
   there. margin-left:auto so it sits at the far right of the header with
   the title staying put on the left instead of both fighting over
   .panel-header's own justify-content:center. #clear-conversation-button
   (the open-conversation pane's own Clear chat -- Block/Report/Hide now
   live exclusively in each row's own ⋮ menu, sidebar) uses the same
   single-direct-button shape, not a dropdown for one item. */
#jack-clear-button, #clear-conversation-button { margin-left: auto; }
.msg-icon-menu-button {
    border: 1px solid var(--chat-panel-border); background: var(--bg-fff);
    border-radius: 8px; padding: 6px 10px; cursor: pointer; font-size: 13px;
    color: var(--text-20242a);
}
.msg-dropdown {
    position: absolute; top: 100%; right: 0; margin-top: 4px; z-index: 20;
    background: var(--bg-fff); border: 1px solid var(--chat-panel-border);
    border-radius: 10px; box-shadow: 0 12px 30px rgba(15,23,42,.15);
    display: flex; flex-direction: column; min-width: 160px; overflow: hidden;
}
/* Same bug class again: this is the Block/Delete action menu that
   messages.js opens/closes purely via dropdown.hidden -- without this
   override it rendered permanently open (and, being position:absolute,
   floating over the panel header) regardless of that flag. */
.msg-dropdown[hidden] { display: none; }
.msg-dropdown button {
    border: none; background: transparent; text-align: left; padding: 10px 14px;
    cursor: pointer; color: var(--text-20242a); font-size: 13px; font-family: inherit;
}
.msg-dropdown button:hover { background: var(--bg-f2f5f9); }
/* Delete is the one destructive, irreversible action in this menu (unlike
   Archive/Block, both fully reversible) -- same red used everywhere else
   for danger (.msg-drawer-item-danger, .msg-danger-button), just as plain
   dropdown-row text/hover instead of its own button chrome. */
.msg-row-dropdown-danger { color: var(--red); }

.msg-blocked-notice { padding: 10px 14px; color: var(--red); font-size: 13px; border-top: 1px solid var(--chat-panel-border); }

/* Section 3/12: "offer to restore" banner for an already-open archived
   conversation -- neutral (--amber, same "worth noticing but not an
   error" token .msg-key-status already uses), never red/error-styled. */
.msg-archived-notice {
    display: flex; align-items: center; gap: 10px; padding: 10px 14px;
    color: var(--amber); font-size: 13px; background: var(--bg-f2f5f9);
    border-bottom: 1px solid var(--chat-panel-border);
}
.msg-archived-notice[hidden] { display: none; }
.msg-archived-notice span { flex: 1; white-space: pre-line; }
.msg-archived-notice button {
    border: 1px solid var(--chat-panel-border); background: transparent; border-radius: 6px;
    padding: 6px 12px; cursor: pointer; color: inherit; font-size: 13px; font-family: inherit;
    white-space: nowrap; flex-shrink: 0;
}
.msg-archived-notice button:hover { background: var(--bg-fff); }

/* Explicit E2EE-readiness banner (2026-07-24 Chrome/multi-device fix) --
   replaces what used to be a bare "…" or a fully silent no-op when a
   device has no key envelope yet for a conversation. .is-error is a
   genuine failure (retry offered); without it, it's just the transient
   "still syncing" state. */
.msg-key-status {
    display: flex; align-items: center; gap: 10px; padding: 10px 14px;
    color: var(--amber); font-size: 13px; border-top: 1px solid var(--chat-panel-border);
}
.msg-key-status[hidden] { display: none; }
.msg-key-status.is-error { color: var(--red); }
.msg-key-status #key-status-text { flex: 1; }
.msg-key-status button {
    border: 1px solid var(--chat-panel-border); background: transparent; border-radius: 6px;
    padding: 5px 12px; cursor: pointer; color: inherit; font-size: 13px; font-family: inherit;
}
.msg-key-status button:hover { background: var(--bg-f2f5f9); }

/* Same bug class again: applyBlockedState() (messages.js) hides
   #conversation-input-row (chat.css's .chat-input, display:flex) via
   .hidden when the other side is blocked -- without this it kept
   rendering as a second, always-visible composer under the blocked
   notice. Lives here (not chat.css) since it's a messages-page-only use
   of the hidden attribute on this shared class. */
.chat-input[hidden] { display: none; }

/* --- Chat bubbles ----------------------------------------------------------- */

/* Same border-width/radius/padding for every bubble regardless of who
   sent it -- only background/border-color ever differ between the
   variants below, never the outline's size or shape.

   max-width (not width): a fixed width forced EVERY bubble -- a 2-word
   "ok" and a full paragraph alike -- to the same wide box regardless of
   content, which is what actually read as "sent is wide, received is
   narrow" (sent bubbles happened to carry the status row/longer text in
   testing; received didn't, but both were rendered at the identical
   forced width either way). width:fit-content + max-width is the
   standard chat-bubble shape (WhatsApp/Telegram/iMessage all do this):
   the box hugs its own content up to the cap, so short and long
   messages both size consistently BY THE SAME RULE regardless of who
   sent them, instead of every bubble being an identical wide rectangle. */
.chat-message {
    width: fit-content;
    max-width: min(82%, 620px);
    padding: 10px 12px;
    margin-bottom: 10px;
    border: 1px solid var(--border-e1e5ea);
    border-radius: 8px;
    background: var(--bg-fff);
    color: var(--text-20242a);
}
.chat-message.my-message { margin-left: auto; border-color: var(--border-cfe0f8); background: var(--bg-f4f8ff); }
.chat-message.jack-message { border-color: var(--border-d8e7de); background: var(--bg-f6fbf7); }
/* The other party's messages in a UIN<->UIN conversation previously got
   no distinct styling at all (bare .chat-message, i.e. identical to the
   unstyled default) -- explicit now so "mine" vs "theirs" is visibly
   two different colors, not just position. */
.chat-message.their-message { border-color: var(--border-e1e5ea); background: var(--bg-f2f5f9); }
.chat-message.system-message { color: var(--muted); font-style: italic; background: transparent; border: none; }

/* Dark mode: --bg-f4f8ff/--bg-f6fbf7 (theme.css) resolve to #111213/
   #111311 there -- practically indistinguishable from plain --bg-fff's
   #121212 next to them. Fine for those tokens' other consumers; scoped
   overrides here instead of changing the shared tokens themselves,
   specifically so "which bubble is mine/Jack's/theirs" reads at a
   glance in dark mode too, not just light. */
:root[data-theme="dark"] .chat-message.my-message { background: #16233a; }
:root[data-theme="dark"] .chat-message.jack-message { background: #10231a; }
.msg-shared-label { font-size: 11px; font-weight: 700; color: var(--muted); margin-bottom: 4px; }
.msg-status-row { margin-top: 4px; text-align: right; }
.msg-status { font-size: 11px; color: var(--muted); }
.msg-status-read { color: var(--green); }

/* --- Modals ------------------------------------------------------------------ */

.msg-modal-overlay {
    position: fixed; inset: 0; z-index: 100;
    background: rgba(15,23,42,.45);
    display: flex; align-items: center; justify-content: center;
    padding: 16px;
}
/* This is the actual fix for the "two modals visible at once" bug: the
   [hidden] attribute and the .msg-modal-overlay class both carry the
   same (0,1,0) specificity, so without this rule the LATER one in this
   file's cascade (display:flex, above) silently won regardless of the
   hidden attribute being set -- every modal using `hidden` (auth,
   settings, ask-jack, report) was therefore ALWAYS visually shown. An
   attribute-qualified selector on the same class outranks the bare
   class rule (0,2,0 > 0,1,0), so this one wins regardless of source
   order, with no !important needed. */
.msg-modal-overlay[hidden] { display: none; }

/* --- Account drawer: docked to the side, not a centered modal -- Settings
   and Sign out ("other modules") used to sit directly in the header,
   crowding it (especially on mobile); now behind the UIN badge tap. */
.msg-drawer-overlay {
    position: fixed; inset: 0; z-index: 100;
    background: rgba(15,23,42,.45);
    display: flex; align-items: stretch; justify-content: flex-end;
}
/* Same [hidden]-vs-class-specificity bug as .msg-modal-overlay above. */
.msg-drawer-overlay[hidden] { display: none; }
.msg-drawer {
    width: min(320px, 88vw);
    height: 100%;
    max-height: 100dvh;
    overflow-y: auto;
    background: var(--bg-fff);
    box-shadow: -20px 0 50px rgba(15,23,42,.25);
    padding: 20px 16px;
    padding-top: max(20px, env(safe-area-inset-top));
    padding-bottom: max(20px, env(safe-area-inset-bottom));
    display: flex;
    flex-direction: column;
    gap: 4px;
}
.msg-drawer-header {
    display: flex; align-items: center; justify-content: space-between;
    padding-bottom: 14px; margin-bottom: 10px;
    border-bottom: 1px solid var(--chat-panel-border);
}
.msg-drawer-uin { font-weight: 800; font-size: 16px; color: var(--text-20242a); }
.msg-drawer-item {
    appearance: none; -webkit-appearance: none; -moz-appearance: none;
    display: block; width: 100%; text-align: left;
    border: none; background: transparent; border-radius: 8px;
    padding: 13px 12px; font: inherit; font-size: 14px; font-weight: 600;
    color: var(--text-20242a); cursor: pointer;
}
.msg-drawer-item:hover { background: var(--bg-f2f5f9); }
.msg-drawer-item-danger { color: var(--red); margin-top: auto; }
.msg-drawer-item-danger:hover { background: var(--bg-f2f5f9); }

/* Applied to <html> for as long as any modal overlay is open (see the
   shared openModal()/closeModal() helpers in auth.js) -- without this,
   the page behind a fixed, full-viewport overlay could still scroll,
   which is what let content "exceed the viewport height" look worse
   than it already did while two overlays were stacked. */
html.msg-no-scroll { overflow: hidden; }

.msg-modal {
    position: relative;
    width: 100%; max-width: 460px;
    /* A min-height taller than the guest gate card behind it (icon +
       text + actions) -- without this, the shorter Sign-in tab's modal
       (just two fields) could center at a height shorter than that
       background card, leaving its top -- including the guest gate's
       own circular cat icon -- visible around the modal's edges through
       the translucent overlay. Only a floor: min() keeps it from ever
       forcing scroll on a short viewport, and the Create-account tab's
       taller content still grows past this naturally. */
    min-height: min(380px, calc(100dvh - 32px));
    max-height: calc(100dvh - 32px);
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    justify-content: center;
    background: var(--bg-fff);
    border-radius: 16px;
    /* Top padding specifically needs to clear .msg-modal-close, an
       absolutely positioned (top:12px;right:12px;font-size:22px) × that
       does NOT reserve space in the flow -- at the old 32px top padding
       there was only ~6px of clearance below its ~26px-tall glyph
       before content started, and the create-account form's very FIRST
       row (.msg-uin-propose-row) has its own button pushed to the same
       right edge by justify-content:space-between, so it visibly
       collided with the × (worse yet on mobile -- see below). */
    padding: 40px 28px 32px;
    box-shadow: 0 30px 80px rgba(15,23,42,.3);
}
/* .msg-modal's own justify-content:center (above) is fine for the SHORT
   auth-form content it was designed for, but is exactly what made a tall
   modal's own top become unreachable: centering a flex column that's
   TALLER than its max-height means half the content overflows above the
   visible box, and a plain (unsafe) `center` leaves that overflow side
   unscrollable in most browsers -- "растягивается и нет возможности
   посмотреть что сверху". Settings is the one modal long enough to hit
   this (devices + push + prefs + account/danger zone), so it opts out
   and lays out top-down instead, where overflow-y:auto (inherited from
   .msg-modal) can always reach the top by simply scrolling to 0. */
.msg-modal-wide { max-width: 560px; justify-content: flex-start; }
/* Same fix, same reasoning -- the delete-account confirmation is also
   long enough (an 8-line explainer + 3 fields + a checkbox) to risk the
   identical "top unreachable" bug on a short/mobile viewport. */
#delete-account-modal .msg-modal { justify-content: flex-start; }
.msg-modal h3 { margin: 0 0 12px; font-size: 18px; }
.msg-modal h4 { margin: 20px 0 8px; font-size: 14px; }
.msg-modal h5 { margin: 0 0 6px; font-size: 13px; color: var(--red); }
/* .msg-modal is a column flexbox (above) with no align-items override,
   so its default (stretch) forces any BARE direct-child button to fill
   the modal's full width -- every other button on this screen is saved
   from that by sitting inside its own row (.msg-device-actions,
   .msg-push-actions, .msg-modal-actions) or block container
   (.msg-danger-zone), but this one has neither, so it alone rendered as
   a full-width flat bar instead of a normal button. */
#settings-sign-out-button { align-self: flex-start; }

/* Section 14 (account deletion): a visually separate, bordered "danger
   zone" -- NOT the same prominence as the account's own Sign-out button
   right above it, and never rendered next to the site's ordinary
   primary/secondary buttons without this separation. */
.msg-danger-zone {
    margin-top: 16px; padding: 14px; border: 1px solid var(--red);
    border-radius: 10px; background: var(--bg-fff0f0);
}
.msg-danger-zone p { margin: 0 0 10px; font-size: 13px; color: var(--muted); }
/* Same shape as every .primary-button/.secondary-button on the site
   (shared --btn-* tokens, theme.css) -- only the color is swapped for
   red, never a separately-invented size/radius/padding. */
.msg-danger-outline-button, .msg-danger-button {
    appearance: none; -webkit-appearance: none; -moz-appearance: none;
    display: inline-flex; align-items: center; justify-content: center;
    padding: var(--btn-padding); border-radius: var(--btn-radius);
    font: inherit; font-size: var(--btn-font-size); cursor: pointer;
    transition: background-color .15s ease, border-color .15s ease;
}
.msg-danger-outline-button {
    color: var(--red); background: transparent; border: 1px solid var(--red);
    font-weight: var(--btn-secondary-weight);
}
.msg-danger-outline-button:hover { background: var(--bg-fff0f0); }
.msg-danger-button {
    color: #fff; background: var(--red); border: 1px solid var(--red);
    font-weight: var(--btn-primary-weight);
}
.msg-danger-button:hover { filter: brightness(0.9); }
.msg-danger-button:disabled, .msg-danger-outline-button:disabled { opacity: .55; cursor: not-allowed; filter: none; }
.msg-modal-close {
    position: absolute; top: 12px; right: 12px;
    border: none; background: transparent; font-size: 22px; line-height: 1;
    cursor: pointer; color: var(--muted);
}
.msg-modal-actions { display: flex; gap: 10px; justify-content: flex-end; margin-top: 16px; flex-wrap: wrap; }
/* Same bug class again: #ask-jack-actions (the Keep-private/Share row)
   is toggled purely via .hidden by messages.js. */
.msg-modal-actions[hidden] { display: none; }
.msg-textarea {
    width: 100%; min-height: 90px; padding: 10px 12px;
    border: 1px solid var(--chat-input-border); border-radius: 8px;
    background: var(--bg-fff); color: var(--text-20242a); font-family: inherit;
    resize: vertical;
}
.msg-privacy-notice { font-size: 13px; color: var(--muted); margin: 0 0 10px; }
/* #hide-confirm-modal's copy (msg_confirm_hide_unread) carries a real
   "\n" -- same reasoning as .msg-empty-state's own pre-line above. */
.msg-modal p { white-space: pre-line; }

/* Undo snackbar (section 16) -- explicitly NOT alert(): a plain success
   toast must be dismissible on its own and able to host a real, clickable
   Undo action, neither of which a blocking alert() can do. Colors are the
   existing --text-20242a/--bg-fff PAIR, deliberately swapped (bg gets the
   TEXT token, text gets the BG token) -- in light mode that's dark text
   color (#20242a) as background, white as text = a dark chip; in dark
   mode the same two tokens invert (see theme.css) to a light chip with
   dark text -- correct contrast in both themes from the SAME two
   variables, no new dark-mode override block needed. */
.msg-toast {
    position: fixed;
    left: 50%;
    bottom: max(20px, calc(env(safe-area-inset-bottom) + 12px));
    transform: translateX(-50%);
    display: flex; align-items: center; gap: 14px;
    background: var(--text-20242a); color: var(--bg-fff);
    padding: 12px 16px; border-radius: 10px;
    box-shadow: 0 12px 30px rgba(15,23,42,.25);
    z-index: 150;
    font-size: 13px;
    max-width: calc(100vw - 32px);
}
.msg-toast[hidden] { display: none; }
.msg-toast button {
    border: none; background: transparent; color: var(--bg-eaf3ff);
    font-weight: 800; cursor: pointer; font-size: 13px; font-family: inherit;
    padding: 6px; flex-shrink: 0; min-height: 44px;
}
.msg-toast button:hover { text-decoration: underline; }

/* --- Auth modal --------------------------------------------------------------- */

.msg-auth-form { display: flex; flex-direction: column; gap: 14px; }
/* Same bug class as .msg-modal-overlay[hidden] above, recurring on the
   INNER panels: .msg-auth-form{display:flex} and the [hidden] attribute
   are equal specificity, so without this override EVERY panel
   (login/register/recovery/success) stayed visible at once, stacked
   into one long scrolling form, regardless of which data-auth-tab was
   marked active. This is the actual fix; setAuthPanel() below never
   needed to change. */
.msg-auth-form[hidden] { display: none; }
[data-auth-panel][hidden] { display: none; }
.msg-auth-form label { display: flex; flex-direction: column; gap: 6px; font-size: 13px; font-weight: 700; color: var(--muted); }
.msg-auth-form input[type="text"], .msg-auth-form input[type="password"], .msg-auth-form input:not([type]) {
    padding: 11px 12px; border: 1px solid var(--chat-input-border); border-radius: 8px;
    background: var(--bg-fff); color: var(--text-20242a); font-size: 16px; font-weight: 400;
}
.msg-checkbox-row { flex-direction: row !important; align-items: center; gap: 8px !important; }
.msg-checkbox-row input { width: 18px; height: 18px; }
.msg-form-error { color: var(--red); font-size: 13px; }
#uin-propose-error { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
/* Same bug class again, worse here: an id selector's specificity beats
   [hidden] even by source order, so this row (proposeUin()'s error text
   + retry button, auth.js) rendered as a permanent empty strip on the
   Create-UIN tab regardless of the hidden attribute auth.js sets. */
#uin-propose-error[hidden] { display: none; }
.msg-uin-propose-row { flex-wrap: wrap; }
.msg-link-button {
    border: none; background: transparent; color: var(--btn-secondary-text);
    font-size: var(--btn-font-size); text-decoration: underline; cursor: pointer; padding: 4px 0;
    align-self: flex-start; font-family: inherit;
}
.msg-link-button:hover { color: var(--blue); }
.msg-uin-propose-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; font-size: 14px; }
.msg-uin-propose-row strong { font-size: 18px; letter-spacing: 1px; }

/* --- Registration success step (single step INSIDE #auth-modal, not a
   second overlay -- see the [hidden] fix above for why a second overlay
   was the root cause in the first place) ------------------------------ */

/* Hides the × close button only while the success step is showing --
   requirement 9: the single show of the recovery code must not be
   dismissible by accident via the corner close button. auth.js toggles
   this class; it never removes the button, so a later, fresh sign-in
   open still has it. (There is no tab bar to also hide here anymore --
   removed entirely, see the auth-modal-inner HTML comment.) */
#auth-modal-inner.is-success #auth-modal-close {
    display: none;
}

.msg-success-panel h3 { text-align: center; }
.msg-success-block { display: flex; flex-direction: column; gap: 8px; }
.msg-success-label { font-size: 12px; font-weight: 700; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; }
.msg-success-value {
    display: block;
    font-family: "DejaVu Sans Mono", Consolas, "Liberation Mono", Menlo, monospace;
    font-size: 20px; font-weight: 800; letter-spacing: 1px; text-align: center;
    padding: 14px; border: 1px solid var(--chat-panel-border); border-radius: 10px;
    background: var(--chat-panel-bg-sidebar);
    color: var(--text-20242a);
    overflow-wrap: anywhere;
    user-select: all;
}
.msg-recovery-code { border-style: dashed; }
.msg-copy-button { align-self: flex-start; }
.msg-success-notice { font-size: 13px; color: var(--muted); margin: 4px 0 0; }

/* --- Settings ------------------------------------------------------------------ */

.msg-devices-list { display: flex; flex-direction: column; gap: 10px; margin-bottom: 8px; }

/* 2026-07-24 push/device fix -- a real, readable device card (section 7)
   instead of a bare "Другое устройство — Отозвать" row with nothing to
   actually identify it by. */
.msg-device-card {
    display: flex; flex-direction: column; gap: 6px;
    padding: 12px; border: 1px solid var(--chat-panel-border); border-radius: 10px;
    font-size: 13px; min-width: 0;
}
.msg-device-card-header { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.msg-device-name { font-weight: 600; font-size: 14px; word-break: break-word; }
.msg-device-current-badge {
    font-size: 11px; font-weight: 600; color: var(--bg-22c55e);
    border: 1px solid var(--bg-22c55e); border-radius: 999px; padding: 1px 8px;
}
.msg-device-subtitle { color: var(--muted); }
.msg-device-meta, .msg-device-status-row {
    display: flex; flex-wrap: wrap; gap: 4px 14px; color: var(--muted); font-size: 12px;
}
/* Rename/Revoke used to render smaller than every other button on the
   site (font-size:12px/padding:6px 12px here vs. the shared --btn-*
   tokens .secondary-button uses everywhere else, including "Выйти с
   этого устройства" right below this same list) -- removed so device
   cards use the exact same button size as the rest of Settings. */
.msg-device-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 4px; }
.msg-toggle-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 8px 0; font-size: 14px; }
/* Notification prefs (Звук/Скрывать текст/Не беспокоить) used to be bare
   OS checkboxes, the one control on this screen not drawn in the site's
   own style -- restyled as a track+knob switch using the same accent
   (--bg-0f62c9) as every other "active" state on this screen (the
   Архив tab, .msg-list-tab.is-active). Still a real <input
   type="checkbox">, just appearance:none -- no JS/markup change needed,
   :checked keeps working exactly as before. */
.msg-toggle-row input[type="checkbox"] {
    appearance: none; -webkit-appearance: none; -moz-appearance: none;
    width: 40px; height: 24px; min-width: 40px; flex-shrink: 0;
    border-radius: 999px; border: none; background: var(--chat-panel-border);
    position: relative; cursor: pointer; transition: background-color .15s ease;
}
.msg-toggle-row input[type="checkbox"]::before {
    content: ""; position: absolute; top: 3px; left: 3px;
    width: 18px; height: 18px; border-radius: 50%; background: #fff;
    box-shadow: 0 1px 3px rgba(15,23,42,.3); transition: transform .15s ease;
}
.msg-toggle-row input[type="checkbox"]:checked { background: var(--bg-0f62c9); }
.msg-toggle-row input[type="checkbox"]:checked::before { transform: translateX(16px); }
.msg-toggle-row input[type="checkbox"]:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }

/* 2026-07-24 Android push fix: exact subscription state (section 7) --
   replaces what used to be a single bare "Push notifications" checkbox
   that silently did nothing if browser permission hadn't been granted
   yet via a real user gesture. */
.msg-push-status-row { font-size: 13px; color: var(--muted); margin-bottom: 8px; }
.msg-push-status-row .is-enabled { color: var(--bg-22c55e); }
.msg-push-status-row .is-error { color: var(--red); }
.msg-push-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 10px; }
.msg-push-actions button[hidden] { display: none; }
.msg-push-test-result { font-size: 13px; color: var(--muted); margin: -4px 0 10px; white-space: pre-line; }
.msg-push-test-result[hidden] { display: none; }

/* 2026-07-24 read/iOS-push fix -- iOS install instructions (section 9)
   and the diagnostics panel (section 24). white-space:pre-line lets the
   translated instructions string use plain \n line breaks server-side
   instead of building HTML for them. */
.msg-push-ios-install {
    white-space: pre-line; font-size: 13px; line-height: 20px; color: var(--text-20242a);
    background: var(--bg-f2f5f9); border-radius: 8px; padding: 12px 14px; margin-bottom: 10px;
}
.msg-push-ios-install[hidden] { display: none; }
.msg-push-diagnostics { font-size: 13px; color: var(--muted); margin-bottom: 10px; }
.msg-push-diagnostics summary { cursor: pointer; user-select: none; }
.msg-push-diagnostics-body { display: flex; flex-direction: column; gap: 4px; padding: 8px 0 0 2px; }
.msg-push-diagnostics-body div { display: flex; justify-content: space-between; gap: 10px; }

/* --- Mobile ---------------------------------------------------------------------- */

@media (max-width: 1120px) {
    .msg-layout { grid-template-columns: 280px 1fr; }
}

@media (max-width: 720px) {
    .msg-mobile-tabs {
        display: flex; gap: 8px; padding: 10px 12px;
        border-bottom: 1px solid var(--chat-panel-border);
    }
    .msg-mobile-tab {
        flex: 1; min-height: 40px; border-radius: 8px; border: 1px solid var(--chat-panel-border);
        background: var(--bg-fff); color: var(--text-20242a); font-weight: 700; cursor: pointer; font-family: inherit;
    }
    .msg-mobile-tab.is-active { background: var(--bg-0f62c9); color: var(--text-fff); border-color: transparent; }

    .msg-layout { grid-template-columns: 1fr; }
    /* Sidebar ("list" tab) is visible by default, matching the
       .msg-mobile-tab.is-active default already set in the HTML -- JS
       (messages.js) toggles msg-mobile-active on whichever panel should
       be shown from then on, always exactly one of the two at a time. */
    .msg-sidebar { display: flex; }
    .msg-stage { display: none; }
    .msg-sidebar.msg-mobile-active { display: flex; }
    .msg-sidebar:not(.msg-mobile-active) { display: none; }
    .msg-stage.msg-mobile-active { display: flex; }

    .msg-back-button { display: inline-block; }
    .msg-modal {
        max-width: 100%;
        width: 100%;
        max-height: calc(100dvh - 24px);
        /* Top padding bumped for the SAME reason as the desktop rule
           above (clear .msg-modal-close) -- mobile actually needs MORE
           room than desktop, not less: the 22px this used to be gave
           .msg-uin-propose-row's right-aligned "Обновить" button real,
           reported overlap with the × on a phone screen. */
        padding: 44px 18px 22px;
        padding-bottom: calc(22px + env(safe-area-inset-bottom));
    }
    .msg-modal-overlay { padding: 12px; }

    /* Touch targets >= 44px on mobile (desktop keeps the exact height
       of the home page's own reference button -- this is specifically
       the touch-target rule, not a general resize). */
    .primary-button, .secondary-button { min-height: 44px; }
    .msg-checkbox-row { min-height: 44px; }
    .msg-checkbox-row input { width: 22px; height: 22px; flex-shrink: 0; }
    .msg-success-value { font-size: 17px; }
}

@media (max-width: 480px) {
    .msg-guest-gate-actions { flex-direction: column; }
    .msg-guest-gate-actions button { width: 100%; }
    .msg-find-uin { flex-direction: column; }
}

/* iPhone safe areas: the fixed input row must not sit under the home
   indicator, and the topbar must not sit under the notch when this page
   is opened as an installed PWA (display: standalone). */
.messenger-shell { padding-bottom: env(safe-area-inset-bottom); }
.messenger-topbar { padding-top: max(12px, env(safe-area-inset-top)); }

/* --- Whole-page scroll containment (logged-in /chat only) ------------------
   chat.css's body{min-height:100vh} and .messenger-shell{min-height:100vh}
   are shared by every page on the site (index, docs, login, ...), where
   letting the page grow taller than the viewport and scroll as a whole
   is the CORRECT, desired behavior -- so those shared rules are
   deliberately left untouched. Only /chat's own <body class="messages-
   page"> (chat.html, logged-in only -- the guest gate keeps normal
   page scroll) gets pinned to exactly the viewport height with
   overflow:hidden, which is what actually makes .msg-conversation-list
   and .chat-window's own overflow-y:auto (already correctly set up
   below) the ONLY things that scroll, instead of the whole page.
   min-height is set to the SAME value as height, not left at chat.css's
   100vh, because min-height wins over a smaller height when they
   conflict -- on mobile browsers where a shrinking toolbar makes 100vh
   momentarily taller than 100dvh, leaving min-height:100vh in place
   would have silently defeated this fix. */
body.messages-page {
    height: 100dvh;
    min-height: 100dvh;
    overflow: hidden;
}
body.messages-page .messenger-shell {
    height: 100dvh;
    min-height: 100dvh;
    overflow: hidden;
}
