/* Messages-style look: blue sent bubbles, gray received, plain white / true
   black surfaces, gray monogram avatars. */
:root {
	--bg: #ffffff;
	--side: #f5f5f7;
	--fill: #e9e9eb;          /* received bubbles, inputs */
	--fill-2: #dcdce0;
	--line: #e3e3e6;
	--fg: #1d1d1f;
	--muted: #8a8a8e;
	--accent: #007aff;
	--accent-fg: #ffffff;
	--sel: #007aff;           /* selected chat row */
	--sel-fg: #ffffff;
	--danger: #ff3b30;
	--ok: #34c759;
	--mono-1: #a5a8b1;        /* monogram gradient */
	--mono-2: #868994;
	color-scheme: light;
}
@media (prefers-color-scheme: dark) {
	:root:not([data-theme="light"]) {
		--bg: #000000; --side: #121214; --fill: #262629; --fill-2: #323236; --line: #2c2c2e; --fg: #f5f5f7; --muted: #8e8e93;
		--accent: #0a84ff; --sel: #0a84ff; --mono-1: #6e717a; --mono-2: #4f525a; color-scheme: dark;
	}
}
:root[data-theme="dark"] {
	--bg: #000000; --side: #121214; --fill: #262629; --fill-2: #323236; --line: #2c2c2e; --fg: #f5f5f7; --muted: #8e8e93;
	--accent: #0a84ff; --sel: #0a84ff; --mono-1: #6e717a; --mono-2: #4f525a; color-scheme: dark;
}

* { box-sizing: border-box; }
html, body { height: 100%; margin: 0; }
body { background: var(--bg); color: var(--fg); font: 15px/1.4 -apple-system, BlinkMacSystemFont, "SF Pro Text", system-ui, "Segoe UI", Roboto, sans-serif; -webkit-font-smoothing: antialiased; }
#app { height: 100%; }
h1 { font-size: 26px; font-weight: 700; margin: 0; letter-spacing: -.02em; }
h2 { font-size: 19px; font-weight: 600; margin: 0; }
h3 { font-size: 13px; margin: 18px 0 6px; color: var(--muted); font-weight: 600; text-transform: uppercase; letter-spacing: .03em; }
p { margin: 0; }
hr { border: 0; border-top: 1px solid var(--line); margin: 16px 0; }
.muted { color: var(--muted); }
.small { font-size: 13px; }
.pad { padding: 14px 16px; }
.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
.noscript { padding: 24px; text-align: center; }
.ico { width: 20px; height: 20px; display: block; }

button, input, textarea, select { font: inherit; color: inherit; }
/* an app, not a document: long presses and drags don't select labels, taps don't flash blue
   (message text is drawn, so nothing readable is lost; Copy is in the message menu) */
body { -webkit-user-select: none; user-select: none; -webkit-tap-highlight-color: transparent; -webkit-touch-callout: none; }
input, textarea, [contenteditable], .selectable, .code, .safety-number { -webkit-user-select: text; user-select: text; -webkit-touch-callout: default; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
dialog:focus-visible, input:focus-visible, textarea:focus-visible, select:focus-visible { outline: none; } /* inputs show focus by their border */
input, textarea, select { width: 100%; padding: 10px 12px; border: 1px solid transparent; border-radius: 10px; background: var(--fill); outline: none; }
input:focus, textarea:focus, select:focus { border-color: var(--accent); }
input::placeholder, textarea::placeholder { color: var(--muted); }
textarea { resize: none; }
button { cursor: pointer; padding: 9px 16px; border-radius: 10px; border: 0; background: var(--fill); }
button:hover { background: var(--fill-2); }
button:disabled { opacity: .45; cursor: default; }
button.primary { background: var(--accent); color: var(--accent-fg); font-weight: 600; }
button.primary:hover { filter: brightness(1.1); background: var(--accent); }
button.danger { color: var(--danger); }
button.link { background: none; color: var(--accent); padding: 4px 0; }
button.icon { width: 36px; height: 36px; padding: 0; display: grid; place-items: center; background: none; color: var(--accent); border-radius: 50%; flex: none; }
button.icon:hover { background: var(--fill); }

/* ---- forms / cards */
.center { min-height: 100%; display: grid; place-items: center; padding: 24px 16px; }
.card { width: 100%; max-width: 380px; display: grid; gap: 14px; }
.card.wide { max-width: 500px; }
.card h1 { text-align: center; margin-bottom: 6px; }
.field { display: grid; gap: 6px; }
.field > span { font-size: 13px; font-weight: 600; color: var(--muted); }
.field small { color: var(--muted); font-size: 12px; }
fieldset { border: 0; border-radius: 14px; padding: 14px; margin: 0; display: grid; gap: 8px; background: var(--side); }
legend { float: left; font-weight: 600; padding: 0; margin-bottom: 2px; }
fieldset .muted { font-size: 13px; clear: both; }
fieldset input { background: var(--bg); }
.check { display: flex; gap: 10px; align-items: flex-start; }
.check input { width: auto; margin-top: 3px; accent-color: var(--accent); }
.check small { display: block; margin-top: 2px; }
.row { display: flex; gap: 8px; justify-content: flex-end; align-items: center; }
.row.spread { justify-content: space-between; margin-top: 18px; }
.row input { flex: 1; }
.steps { display: flex; gap: 6px; list-style: none; margin: 0 0 4px; padding: 0; font-size: 12px; color: var(--muted); }
.steps li { flex: 1; border-top: 3px solid var(--fill); padding-top: 5px; }
.steps li.on { border-color: var(--accent); color: var(--fg); font-weight: 600; }
.steps li.done { border-color: color-mix(in srgb, var(--accent) 40%, var(--fill)); }
.code { font: 600 19px/1.4 ui-monospace, "SF Mono", Menlo, monospace; letter-spacing: .06em; text-align: center; padding: 16px; border-radius: 14px; background: var(--side); user-select: all; word-break: break-all; }

.profile-editor { display: grid; gap: 12px; }
.pfp-row { display: flex; gap: 14px; align-items: center; }
.pfp-row > div:last-of-type { display: grid; gap: 4px; justify-items: start; }
.pfp-row small { font-size: 12px; }

/* ---- phase 5a: photo crop dialog (profile + group photos) */
.crop-dialog { width: min(320px, calc(100vw - 32px)); }
.crop-frame { position: relative; width: 240px; height: 240px; margin: 0 auto; overflow: hidden; border-radius: 16px;
	background: var(--fill); cursor: grab; touch-action: none; outline: none; }
.crop-frame:active { cursor: grabbing; }
.crop-frame:focus-visible { box-shadow: 0 0 0 2px var(--accent); }
.crop-img { position: absolute; left: 0; top: 0; max-width: none; pointer-events: none; user-select: none; }
.crop-mask { position: absolute; inset: 10px; border-radius: 50%; box-shadow: 0 0 0 999px rgb(0 0 0 / .45); outline: 2px solid rgb(255 255 255 / .85); pointer-events: none; }
.crop-zoom-row { display: flex; align-items: center; gap: 8px; margin-top: 14px; }
.crop-zoom { flex: 1; }
.crop-zoom-icon { font-size: 15px; color: var(--muted); width: 12px; text-align: center; }
.crop-dialog > .row { margin-top: 14px; }

/* ---- avatars: photo, or a gray monogram like Contacts */
.avatar { width: 40px; height: 40px; border-radius: 50%; flex: none; object-fit: cover; }
.avatar.xs { width: 28px; height: 28px; font-size: 12px; }
.avatar.sm { width: 32px; height: 32px; font-size: 14px; }
.avatar.md { width: 46px; height: 46px; font-size: 19px; }
.avatar.lg { width: 84px; height: 84px; font-size: 34px; }
.avatar.xl { width: 96px; height: 96px; font-size: 40px; }
.avatar.initials { display: grid; place-items: center; font-weight: 600; color: #fff; background: linear-gradient(180deg, var(--mono-1), var(--mono-2)); }
/* phase 5b: the official system account and Saved Messages get their own
   distinct avatar colors, so they never look like an ordinary contact. */
.avatar.initials.system { background: linear-gradient(180deg, var(--accent), color-mix(in srgb, var(--accent) 70%, #000)); }
.avatar.initials.self { background: linear-gradient(180deg, #ffb020, #c47e00); }
.avatar-space { width: 28px; flex: none; }
button.avatar-btn { padding: 0; background: none; border-radius: 50%; flex: none; }

/* ---- app shell */
.shell { height: 100%; display: grid; grid-template-columns: 320px 1fr; }
.sidebar { background: var(--side); border-right: 1px solid var(--line); display: flex; flex-direction: column; min-height: 0; }
.sidebar > header { display: flex; align-items: center; gap: 4px; padding: 12px 12px 6px; }
.sidebar > header h1 { flex: 1; font-size: 22px; padding-left: 6px; }
button.me { padding: 0; background: none; border-radius: 50%; }
.filter { display: flex; align-items: center; gap: 6px; margin: 4px 12px 8px; padding: 0 10px; border-radius: 10px; background: var(--fill); color: var(--muted); }
.filter .ico { width: 16px; height: 16px; }
.filter input { background: none; padding: 8px 0; border: 0; }
.room-list { list-style: none; margin: 0; padding: 0 8px 8px; overflow-y: auto; flex: 1; }
button.room { width: 100%; display: flex; gap: 11px; align-items: center; background: none; padding: 9px 10px; text-align: left; border-radius: 12px; }
button.room:hover { background: var(--fill); }
button.room.active { background: var(--sel); color: var(--sel-fg); }
button.room.active .muted, button.room.active time, button.room.active .preview { color: color-mix(in srgb, var(--sel-fg) 80%, transparent); }
.room-text { flex: 1; min-width: 0; display: grid; gap: 1px; }
.room-top, .room-bottom { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
.room-top .title { flex: 1; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.room-top time { font-size: 13px; color: var(--muted); flex: none; }
.room-bottom .preview { flex: 1; font-size: 14px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
button.room.unread .preview { color: var(--fg); }
.badge { background: var(--accent); color: var(--accent-fg); border-radius: 999px; font-size: 12px; font-weight: 700; padding: 0 7px; line-height: 20px; flex: none; }
.badge.dot { width: 10px; height: 10px; padding: 0; align-self: center; }
button.room.active .badge { background: var(--sel-fg); color: var(--sel); }
/* phase 5b: the system account + Saved Messages placeholder rows. Visually
   identical to button.room's layout, but every class is deliberately its
   own (button.pinned, .pinned-*, not .room/.title/.preview/.badge/…): a few
   existing tests pick "the" room, count an empty account's rooms, or read
   "the" preview, by those bare classes, and these two rows aren't real
   conversations — so they must never match such a query. */
button.pinned { width: 100%; display: flex; gap: 11px; align-items: center; background: none; padding: 9px 10px; text-align: left; border-radius: 12px; }
button.pinned:hover { background: var(--fill); }
button.pinned.active { background: var(--sel); color: var(--sel-fg); }
button.pinned.active .muted, button.pinned.active time, button.pinned.active .pinned-preview { color: color-mix(in srgb, var(--sel-fg) 80%, transparent); }
.pinned-text { flex: 1; min-width: 0; display: grid; gap: 1px; }
.pinned-top, .pinned-bottom { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
.pinned-title { flex: 1; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pinned-top time { font-size: 13px; color: var(--muted); flex: none; }
.pinned-preview { flex: 1; font-size: 14px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
button.pinned.unread .pinned-preview { color: var(--fg); }
.pinned-badge { background: var(--accent); color: var(--accent-fg); border-radius: 999px; font-size: 12px; font-weight: 700; padding: 0 7px; line-height: 20px; flex: none; }
button.pinned.active .pinned-badge { background: var(--sel-fg); color: var(--sel); }

.chat-pane { display: flex; flex-direction: column; min-height: 0; min-width: 0; background: var(--bg); }
.empty { margin: auto; text-align: center; display: grid; gap: 4px; padding: 24px; color: var(--muted); }
.room-header { display: flex; align-items: center; gap: 6px; padding: 8px 10px; border-bottom: 1px solid var(--line);
	background: color-mix(in srgb, var(--bg) 85%, transparent); backdrop-filter: blur(20px); }
.room-header .back { display: none; }
button.room-title { flex: 1; display: flex; align-items: center; justify-content: center; gap: 10px; background: none; padding: 4px 8px; min-width: 0; border-radius: 12px; }
button.room-title:disabled { opacity: 1; cursor: default; } /* phase 5b: the system chat's header isn't clickable (no profile) */
.room-title-text { display: grid; text-align: left; min-width: 0; }
.room-title-text b, .room-title-text small { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.room-title-text small { font-size: 12px; }

/* ---- messages */
.messages { touch-action: pan-y; list-style: none; margin: 0; padding: 14px 16px; overflow-y: auto; overflow-x: hidden; flex: 1; display: flex; flex-direction: column; }
.messages > .time-header { align-self: center; font-size: 12px; font-weight: 600; color: var(--muted); padding: 14px 0 8px; }
.messages > .older { align-self: center; padding-bottom: 6px; }
.messages > .empty { margin: auto; }
.msg { display: flex; gap: 8px; align-items: flex-end; max-width: 70%; margin-top: 2px; }
.msg.first, .msg.single { margin-top: 10px; }
.msg.mine { align-self: flex-end; }
.msg-body { display: grid; gap: 2px; min-width: 0; }
.msg.mine .msg-body { justify-items: end; }
button.sender { background: none; padding: 0 12px; font-size: 12px; color: var(--muted); justify-self: start; border-radius: 6px; }
button.sender:hover { text-decoration: underline; background: none; }
.bubble { background: var(--fill); padding: 8px 13px; border-radius: 19px; white-space: pre-wrap; overflow-wrap: break-word; word-break: normal; min-width: 0; max-width: 100%; line-height: 1.35; } /* wraps at word boundaries; only a word longer than the line (a URL) is broken */
.bubble a.msg-link { overflow-wrap: anywhere; }
.msg.mine .bubble { background: var(--accent); color: var(--accent-fg); }
/* joined corners within a group */
.msg.theirs.first .bubble { border-bottom-left-radius: 6px; }
.msg.theirs.middle .bubble { border-top-left-radius: 6px; border-bottom-left-radius: 6px; }
.msg.theirs.last .bubble { border-top-left-radius: 6px; }
.msg.mine.first .bubble { border-bottom-right-radius: 6px; }
.msg.mine.middle .bubble { border-top-right-radius: 6px; border-bottom-right-radius: 6px; }
.msg.mine.last .bubble { border-top-right-radius: 6px; }
.bubble.broken { font-style: italic; color: var(--muted); background: none; border: 1px dashed var(--line); }

/* ---- phase 5a: swipe-to-reveal exact times (attachSwipeTimes in app.js) */
.bubble-row { display: flex; align-items: flex-end; position: relative; min-width: 0; }
.msg.mine .bubble-row { justify-content: flex-end; }
.msg.theirs .bubble-row { justify-self: start; }
/* Swipe-to-reveal times live in the outer gutters: dragging the list left slides sent rows left (time in the
   right margin) and received rows right (time in the left margin). The time is out of flow and centred on
   its own bubble, so a tall message can't push it around. */
.msg { transition: transform .25s ease; }
.messages.swiping .msg { transition: none; }
.msg.mine { transform: translateX(calc(var(--swipe, 0px) * -1)); }
.msg.theirs { transform: translateX(var(--swipe, 0px)); }
.msg-time-reveal { position: absolute; top: 50%; translate: 0 -50%; white-space: nowrap; font-size: 11px; line-height: 1; color: var(--muted); opacity: 0; pointer-events: none; transition: opacity .2s ease; }
.msg.mine .msg-time-reveal { left: calc(100% + 10px); }
.msg.theirs .msg-time-reveal { right: calc(100% + 10px); }
.msg.theirs.has-avatar .msg-time-reveal { right: calc(100% + 10px + 36px); } /* clear the 28px avatar and its gap */
.messages.swiping .msg-time-reveal { opacity: 1; transition: none; }

/* ---- phase 5a: reactions */
.msg-react-bar { position: absolute; top: -40px; left: 0; display: flex; gap: 2px; padding: 4px; border-radius: 20px;
	background: var(--bg); box-shadow: 0 4px 16px rgb(0 0 0 / .18); opacity: 0; pointer-events: none; transition: opacity .15s; z-index: 2; user-select: none; -webkit-user-select: none; }
/* invisible bridge down to the bubble so moving the cursor onto the bar never leaves the hover area */
.msg-react-bar::after { content: ""; position: absolute; left: 0; right: 0; top: 100%; height: 14px; }
.sheet-react::after { display: none; }
.msg.mine .msg-react-bar { left: auto; right: 0; }
.msg.show-react .msg-react-bar, .msg-react-bar:focus-within { opacity: 1; pointer-events: auto; }
.msg.snap .msg-react-bar { transition: none; }
.reaction-chips, button.icon.msg-more, .msg-time-reveal, .msg-meta { user-select: none; -webkit-user-select: none; }
@media (hover: none) { .msg-react-bar:not(.sheet-react) { display: none; } }
.msg-react-bar button { width: 30px; height: 30px; border-radius: 50%; font-size: 17px; display: grid; place-items: center; padding: 0; background: none; }
.msg-react-bar button:hover { background: var(--fill); }
.msg-react-bar button.mine { background: color-mix(in srgb, var(--accent) 22%, transparent); }
.msg-react-bar button.react-more { font-size: 19px; color: var(--muted); font-weight: 600; }
.sheet-react { position: static; opacity: 1; pointer-events: auto; box-shadow: none; justify-content: center; margin-bottom: 6px; }

.reaction-chips { display: flex; gap: 4px; flex-wrap: wrap; padding: 0 6px; }
.msg.mine .reaction-chips { justify-content: flex-end; }
.reaction-chip { display: flex; align-items: center; gap: 3px; padding: 2px 8px; border-radius: 12px; background: var(--fill); font-size: 13px; }
.reaction-chip.mine { background: color-mix(in srgb, var(--accent) 24%, var(--fill)); }
.reactor-head { font-size: 20px; padding: 8px 4px 2px; list-style: none; }
.reaction-chip .count { font-size: 11px; color: var(--muted); }


.composer { display: flex; gap: 8px; padding: 8px 12px 12px; align-items: flex-end; background: var(--bg); }
.composer textarea { max-height: 160px; border-radius: 20px; padding: 9px 16px; background: var(--bg); border: 1px solid var(--line); }
.composer textarea:focus { border-color: var(--fill-2); }
button.send { width: 36px; height: 36px; padding: 0; border-radius: 50%; display: grid; place-items: center; background: var(--accent); color: var(--accent-fg); flex: none; margin-bottom: 1px; }
button.send:hover { background: var(--accent); filter: brightness(1.1); }
button.send:disabled { background: var(--fill-2); color: var(--muted); opacity: 1; }

/* ---- phase 2: delete, timers, view once, read receipts */
.msg-meta { display: flex; gap: 8px; font-size: 11px; color: var(--muted); padding: 0 6px; }
.receipt { font-size: 11px; color: var(--muted); padding: 0 6px; }
button.receipt { background: none; border-radius: 6px; }
button.receipt:hover { background: none; text-decoration: underline; }
.msg .bubble.tomb { font-style: italic; color: var(--muted); background: none; border: 1px solid var(--line); }
.msg .bubble.once-btn { display: flex; align-items: center; gap: 7px; text-align: left; border: 1px dashed var(--muted); color: var(--fg); }
.msg .bubble.once-btn:hover { background: var(--fill-2); }
button.icon.msg-more { width: 28px; height: 28px; align-self: center; color: var(--muted); opacity: 0; }
.msg:hover .msg-more, .msg-more:focus-visible { opacity: 1; }
@media (hover: none) { button.icon.msg-more { display: none; } }
.composer-opts { display: flex; gap: 2px; align-items: center; margin-bottom: 1px; }
button.icon.composer-opt { width: auto; min-width: 36px; padding: 0 7px; border-radius: 18px; color: var(--muted); display: flex; gap: 3px; align-items: center; }
button.icon.composer-opt.on { color: var(--accent); background: color-mix(in srgb, var(--accent) 13%, transparent); }
.opt-label { font-size: 12px; font-weight: 600; }
.timer-choices { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; margin: 12px 0; }
.timer-choices button.on { background: var(--accent); color: var(--accent-fg); }
.dialog form.timer-custom { display: flex; gap: 8px; margin-bottom: 12px; }
.timer-custom input { flex: 1; }
.timer-custom select { width: auto; }
.once-text { white-space: pre-wrap; overflow-wrap: anywhere; font-size: 17px; padding: 6px 0 14px; }
.reader { display: flex; align-items: center; gap: 10px; padding: 6px 8px; }
.reader span { flex: 1; }

/* ---- phase 5b: Settings → Security */
.security-menu { display: grid; gap: 2px; }
.security-row { text-align: left; padding: 12px 6px; border-radius: 10px; font-size: 16px; }
.security-row:hover { background: var(--fill); }
.session-list { list-style: none; margin: 0 0 10px; padding: 0; display: grid; gap: 10px; }
.session-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 8px 0; border-bottom: 1px solid var(--line); }
.session-info { display: grid; gap: 1px; }

/* ---- phase 5b: notifications + mute */
.notif-settings h3 { margin-bottom: 2px; }
.notif-settings { display: grid; gap: 12px; justify-items: start; }
.notif-settings .check, .notif-settings label.field, .notif-settings .field { justify-self: stretch; }
.notif-hint { margin: 0; }
button.icon.on { color: var(--danger); } /* the sidebar bell, while "mute all" is on */
.muted-icon { display: inline-flex; color: var(--muted); flex: none; }
.muted-icon .ico { width: 14px; height: 14px; }
.room-top .muted-icon, .pinned-top .muted-icon { order: 1; }
.room-top time, .pinned-top time { order: 2; }

/* ---- dialogs */
.dialog { width: min(440px, calc(100vw - 32px)); max-height: calc(100vh - 48px); border: 0; border-radius: 18px; background: var(--bg); color: var(--fg); padding: 18px;
	box-shadow: 0 20px 60px rgb(0 0 0 / .3); }
.dialog::backdrop { background: rgb(0 0 0 / .35); backdrop-filter: blur(2px); }
.dialog > header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; min-height: 36px; }
.dialog form { display: grid; gap: 12px; }
.sheet { width: min(360px, calc(100vw - 32px)); padding: 8px; display: grid; gap: 2px; }
.sheet[open] { display: grid; }
.sheet button { background: none; padding: 13px; border-radius: 12px; font-size: 16px; color: var(--accent); }
.sheet button:hover { background: var(--fill); }
.sheet button.danger { color: var(--danger); }
.sheet button.cancel { font-weight: 600; margin-top: 4px; border-top: 1px solid var(--line); border-radius: 0 0 12px 12px; }
.sheet-title { text-align: center; font-size: 13px; padding: 8px 8px 6px; }
.user-search { display: grid; gap: 6px; }
.user-results, .member-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; }
button.user { width: 100%; display: flex; align-items: center; gap: 10px; text-align: left; background: none; padding: 7px 8px; border-radius: 10px; }
button.user:hover { background: var(--fill); }
.chips { display: flex; flex-wrap: wrap; gap: 6px; margin: 10px 0; }
.chip { display: inline-flex; align-items: center; gap: 2px; background: color-mix(in srgb, var(--accent) 15%, transparent); color: var(--accent); border-radius: 999px; padding: 3px 4px 3px 11px; font-size: 13px; font-weight: 600; }
.chip button { background: none; padding: 0 6px; color: inherit; }
.profile-card { display: grid; justify-items: center; gap: 6px; text-align: center; padding: 0 0 8px; }
.profile-card h2 { margin-top: 6px; }
.profile-card .bio { margin: 6px 0 4px; white-space: pre-wrap; max-width: 34ch; }
.profile-card button.primary { margin-top: 10px; min-width: 160px; }
.profile-card .row { justify-content: center; }

.toast { position: fixed; left: 50%; bottom: 24px; transform: translateX(-50%); z-index: 100; max-width: calc(100vw - 32px);
	padding: 11px 18px; border-radius: 14px; background: color-mix(in srgb, var(--fg) 92%, transparent); color: var(--bg); box-shadow: 0 8px 30px rgb(0 0 0 / .25); font-size: 14px; }
/* shown as a popover (top layer, above modal dialogs): undo the UA popover box */
.toast[popover] { inset: auto; left: 50%; bottom: 24px; margin: 0; border: 0; overflow: visible; }
.toast.error { background: var(--danger); color: #fff; }
.toast.ok { background: color-mix(in srgb, var(--fg) 92%, transparent); color: var(--bg); }

/* ---- phones */
@media (max-width: 720px) {
	.shell { grid-template-columns: 1fr; }
	.shell .chat-pane { display: none; }
	.shell.in-room .sidebar { display: none; }
	.shell.in-room .chat-pane { display: flex; }
	.room-header .back { display: grid; }
	.msg { max-width: 70%; }
}

/* ---- phase 3: attachments (media.js) */
.attach-wrap { display: contents; }
.composer button.attach { margin-bottom: 1px; }
.composer.dropping, .messages.dropping, .privacy-screen.dropping { outline: 2px dashed var(--accent); outline-offset: -6px; }
.tray { padding: 8px 12px 0; display: grid; gap: 6px; background: var(--bg); border-top: 1px solid var(--line); }
.tray-items { list-style: none; margin: 0; padding: 0; display: flex; gap: 8px; overflow-x: auto; }
.tray-item { display: flex; align-items: center; gap: 8px; flex: none; max-width: 240px; padding: 6px 4px 6px 6px; border-radius: 12px; background: var(--side); }
.tray-thumb { width: 44px; height: 44px; border-radius: 8px; object-fit: cover; flex: none; background: var(--fill); }
.icon-thumb { display: grid; place-items: center; color: var(--muted); }
.tray-text { display: grid; min-width: 0; font-size: 13px; }
.tray-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }
.tray-text small { font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tray-note { color: var(--danger); white-space: normal !important; }
.tray-item.processing .tray-thumb, .tray-item.encrypting, .tray-item.uploading { opacity: .7; }
.tray-remove { width: 28px; height: 28px; color: var(--muted); }
.tray-remove .ico { width: 16px; height: 16px; }
.tray-warning { display: flex; gap: 10px; align-items: baseline; font-size: 13px; padding: 8px 10px; border-radius: 10px;
	background: color-mix(in srgb, var(--danger) 12%, transparent); }
.tray-warning span { flex: 1; }
.tray-warning[hidden], .tray[hidden] { display: none; }

.media-msg { display: grid; gap: 4px; justify-items: start; }
.msg.mine .media-msg { justify-items: end; }
.media-items { display: grid; gap: 4px; justify-items: inherit; }
.media-tile { position: relative; display: block; padding: 0; border: 0; border-radius: 16px; overflow: hidden; background: var(--fill); max-width: 100%; }
button.media-tile:hover { background: var(--fill); }
.media-tile img, .media-tile video { display: block; max-width: 100%; height: auto; }
.media-tile canvas { display: block; max-width: 100%; } /* secureimage.js canvases (sized by inline width + aspect-ratio, or inset:0) */
.media-tile .media-ph { filter: blur(12px); transform: scale(1.1); }
.media-tile.image .media-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: 0; transition: opacity .2s; }
.media-tile.image.loaded .media-img { opacity: 1; }
.media-tile.image:not(:has(.media-ph)) { min-width: 60px; min-height: 40px; }
.media-tile.image:not(:has(.media-ph)) .media-img { position: static; }
.media-status { position: absolute; left: 8px; bottom: 8px; font-size: 12px; color: #fff; text-shadow: 0 1px 2px rgb(0 0 0 / .6); }
.media-status:empty { display: none; }
.media-tile.audio { background: none; overflow: visible; }
.media-video { border-radius: 16px; background: #000; }
audio.media-video { background: none; width: 260px; max-width: 100%; }
.media-video-ph { position: relative; display: grid; place-items: center; max-width: 100%; }
.media-video-ph .media-ph { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.media-tile.audio .media-video-ph { min-width: 200px; min-height: 54px; border-radius: 16px; background: var(--fill); }
button.media-play { position: relative; display: flex; align-items: center; gap: 6px; border-radius: 999px; padding: 8px 14px; background: rgb(0 0 0 / .55); color: #fff; font-size: 13px; }
button.media-play:hover { background: rgb(0 0 0 / .7); }
.media-video-ph .media-status { left: 0; right: 0; text-align: center; }

.file-chip { display: flex; align-items: center; gap: 10px; padding: 9px 10px 9px 12px; border-radius: 16px; background: var(--fill); min-width: 200px; max-width: min(320px, 100%); }
.msg.mine .file-chip { background: var(--accent); color: var(--accent-fg); }
.msg.mine .file-chip .muted { color: color-mix(in srgb, var(--accent-fg) 80%, transparent); }
.file-icon { flex: none; display: grid; place-items: center; }
.file-text { flex: 1; display: grid; min-width: 0; }
.file-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }
.file-size { font-size: 12px; }
a.file-download, button.file-download { flex: none; padding: 0; display: grid; place-items: center; width: 34px; height: 34px; border-radius: 50%; color: inherit; cursor: pointer;
	background: color-mix(in srgb, currentColor 14%, transparent); }
.bubble.caption { max-width: 100%; }

.viewer { border: 0; padding: 0; max-width: 100vw; max-height: 100vh; width: 100vw; height: 100vh; background: rgb(0 0 0 / .92); }
.viewer[open] { display: grid; place-items: center; }
.viewer::backdrop { background: rgb(0 0 0 / .6); }
.viewer-img { max-width: calc(100vw - 32px); max-height: calc(100vh - 96px); object-fit: contain; }
.viewer-close { position: fixed; top: 12px; right: 12px; color: #fff; background: rgb(255 255 255 / .12); }
.viewer-save { position: fixed; bottom: 16px; right: 16px; color: #fff; background: rgb(255 255 255 / .16); padding: 8px 16px; border-radius: 999px; text-decoration: none; font-weight: 600; }

/* ---- phase 5a: view-once photo/video viewer — no save link, by design */
.once-viewer-items { display: flex; flex-direction: column; gap: 12px; align-items: center; max-height: 100vh; overflow-y: auto; padding: 16px; }
.once-tile { display: grid; place-items: center; min-height: 120px; color: #fff; }
.once-tile .media-status { color: rgb(255 255 255 / .7); }
.viewer-img.once { max-width: calc(100vw - 32px); max-height: calc(100vh - 140px); object-fit: contain; }
.viewer-caption { color: #fff; max-width: calc(100vw - 64px); text-align: center; white-space: pre-wrap; }

/* ---- phase 4a/5a: privacy (public/js/privacy.js) */
/* Privacy screen: a modal <dialog>, so it sits in the top layer above any
   dialog already open and catches every click/tap as a reveal. Phase 5a: the
   app itself (and any open dialog) gets heavily blurred + dimmed in place —
   the layout stays recognisable but no text is legible — instead of being
   hidden behind a near-opaque sheet. clamp() keeps the blur radius large
   relative to the viewport at any zoom/DPI, so screenshots/thumbnails taken
   while away stay unreadable. */
.privacy-screen { position: fixed; inset: 0; width: 100%; height: 100%; max-width: none; max-height: none; margin: 0; padding: 0; border: 0;
	background: transparent; color: var(--fg); }
.privacy-screen[open] { display: grid; place-items: center; }
.privacy-screen::backdrop { background: transparent; }
.privacy-reveal { display: grid; gap: 2px; place-items: center; padding: 14px 22px; border-radius: 16px; font-size: 13px; font-weight: 600;
	background: color-mix(in srgb, var(--bg) 72%, transparent); box-shadow: 0 8px 30px rgb(0 0 0 / .18); pointer-events: none; }
.privacy-icon { font-size: 22px; line-height: 1; }
.privacy-reveal small { font-size: 12px; font-weight: 500; color: var(--muted); }
html.privacy-away #app, html.privacy-away .toast, html.privacy-away dialog:not(.privacy-screen),
html.privacy-watch #app, html.privacy-watch .toast, html.privacy-watch dialog:not(.watch-screen):not(.privacy-screen) {
	filter: blur(clamp(28px, 2vw, 60px)) saturate(.6) brightness(.85); pointer-events: none; }
html.privacy-gone body { visibility: hidden; }
html.privacy-gone body { visibility: hidden; }
.privacy-settings { display: grid; gap: 12px; }
.privacy-settings h3 { margin: 0; }
.shield-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.shield-result.good { color: var(--ok); }
.shield-result.bad { color: var(--danger); font-weight: 600; }

/* phase 4b: key epochs, safety numbers, key-change warnings */
.messages > .notice { align-self: center; font-size: 13px; color: var(--muted); text-align: center; padding: 10px 14px; margin: 6px 0; border: 1px dashed var(--line); border-radius: 12px; }
.key-banners:empty { display: none; }
.key-banner { display: grid; gap: 8px; padding: 10px 14px; border-bottom: 1px solid var(--line); background: color-mix(in srgb, var(--danger) 10%, var(--bg)); font-size: 14px; }
.key-banner.blocked { background: color-mix(in srgb, var(--danger) 18%, var(--bg)); }
.key-banner b { color: var(--danger); }
.key-banner .row { justify-content: flex-start; flex-wrap: wrap; }
.key-banner button { padding: 6px 12px; font-size: 14px; }
.verified-badge { display: inline-grid; place-items: center; width: 17px; height: 17px; margin-left: 6px; border-radius: 50%; background: var(--accent); color: var(--accent-fg); font-size: 11px; font-weight: 700; vertical-align: 2px; }
/* L1: deliberately unlike .verified-badge (a pill with a word, not a ✓ disc)
   — never reachable from a contact's name or safety-number verification. */
.official-badge { display: inline-flex; align-items: center; margin-left: 6px; padding: 2px 7px; border-radius: 9px; background: var(--ok); color: var(--accent-fg); font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .03em; vertical-align: 2px; }
.safety { display: grid; gap: 10px; margin-top: 8px; }
.safety h3 { margin-bottom: 0; }
.safety-number { display: grid; grid-template-columns: repeat(4, auto); justify-content: center; gap: 6px 14px; padding: 14px; border-radius: 14px; background: var(--side); font: 600 17px/1.3 ui-monospace, "SF Mono", Menlo, monospace; letter-spacing: .04em; user-select: all; }
.safety .row { justify-content: center; flex-wrap: wrap; }
.key-warning { color: var(--danger); font-weight: 600; }
/* phase 4c: a chat whose key is being replaced; settings rolled back */
.key-banner.fixing { background: color-mix(in srgb, var(--accent) 10%, var(--bg)); }
.key-banner.fixing b { color: inherit; }
.app-alerts:empty { display: none; }
.app-alerts .key-banner { border: 1px solid var(--line); border-radius: 12px; margin: 4px 12px 8px; }

/* ---- phase 5a: ⓘ details toggle (short headline, long text on demand) */
.info-toggle { display: inline-grid; place-items: center; width: 18px; height: 18px; margin-left: 4px; padding: 0; border-radius: 50%;
	background: none; color: var(--muted); font-size: 15px; line-height: 1; vertical-align: -3px; }
.info-toggle:hover { background: var(--fill); }
.more-info { display: block; margin-top: 4px; font-weight: 400; color: inherit; }
.more-info.hidden { display: none; }

/* ---- emoji picker */
.composer { position: relative; }
.emoji-picker { display: flex; flex-direction: column; width: 340px; max-width: 100%; height: 360px; max-height: 60vh; border-radius: 14px; background: var(--bg);
	border: 1px solid var(--line); box-shadow: 0 8px 32px rgb(0 0 0 / .22); overflow: hidden; }
.composer > .emoji-picker { position: absolute; right: 12px; bottom: calc(100% + 4px); z-index: 5; width: min(340px, calc(100vw - 24px)); }
.emoji-dialog .emoji-picker { width: 100%; box-shadow: none; border: 0; }
.emoji-search { margin: 8px 8px 4px; border-radius: 10px; padding: 7px 12px; width: auto; }
.emoji-tabs { display: flex; padding: 0 4px; border-bottom: 1px solid var(--line); }
.emoji-tab { flex: 1; padding: 4px 0 6px; font-size: 17px; background: none; border-radius: 8px 8px 0 0; filter: grayscale(1); opacity: .7; }
.emoji-tab:hover { filter: none; opacity: 1; }
.emoji-grid { flex: 1; overflow-y: auto; padding: 0 8px 8px; }
.emoji-section h4 { position: sticky; top: 0; margin: 0; padding: 8px 2px 4px; font-size: 12px; font-weight: 600; color: var(--muted); background: var(--bg); }
.emoji-section-body { display: grid; grid-template-columns: repeat(8, 1fr); gap: 2px; }
.emoji-cell { font-size: 22px; line-height: 1; padding: 5px 0; border-radius: 8px; background: none; }
.emoji-cell:hover { background: var(--fill); }
.emoji-status { padding: 24px 8px; text-align: center; }

/* ---- right-click menu on chats */
.ctx-menu { position: fixed; z-index: 50; min-width: 180px; padding: 5px; border-radius: 12px; background: var(--bg); border: 1px solid var(--line); box-shadow: 0 8px 32px rgb(0 0 0 / .25); }
.ctx-row { position: relative; }
.ctx-menu button { display: flex; justify-content: space-between; gap: 16px; width: 100%; text-align: left; padding: 8px 12px; border-radius: 8px; background: none; }
.ctx-menu button:hover, .ctx-menu button:focus-visible { background: var(--fill); }
.ctx-menu button.danger { color: var(--danger, #ff3b30); }
.ctx-arrow { color: var(--muted); }
.ctx-sub { display: none; position: absolute; left: 100%; top: -5px; min-width: 140px; padding: 5px; border-radius: 12px; background: var(--bg); border: 1px solid var(--line); box-shadow: 0 8px 32px rgb(0 0 0 / .25); }
.ctx-menu.flip .ctx-sub { left: auto; right: 100%; }
.ctx-row:hover > .ctx-sub, .ctx-row.open > .ctx-sub { display: block; }
.ctx-menu { outline: none; }

.bubble a.msg-link { color: inherit; text-decoration: underline; text-underline-offset: 2px; }
.dialog.confirm p { white-space: pre-line; overflow-wrap: anywhere; }

/* ---- settings tabs, small-screen fixes */
.pane { display: grid; gap: 12px; }
.pane[hidden] { display: none; }
.dialog.confirm .row { display: flex; gap: 8px; justify-content: flex-end; }
.dialog { height: fit-content; }
.sheet { height: fit-content; align-content: start; grid-auto-rows: min-content; max-height: min(80dvh, 520px); overflow-y: auto; }
@media (pointer: coarse) { input, textarea, select { font-size: 16px; } } /* iOS zooms the page on focus below 16px */

/* ---- message requests */
.list-tabs { display: flex; gap: 4px; padding: 3px; margin: 0 12px 6px; border-radius: 10px; background: var(--fill); }
.list-tabs button { flex: 1; padding: 6px 4px; border-radius: 8px; background: none; font-weight: 500; display: flex; gap: 6px; justify-content: center; align-items: center; }
.list-tabs button.on { background: var(--bg); font-weight: 600; box-shadow: 0 1px 3px rgb(0 0 0 / .12); }
.tab-badge { background: var(--accent); color: var(--accent-fg); border-radius: 9px; font-size: 11px; padding: 1px 6px; }
.request-bar { padding: 12px 16px 16px; border-top: 1px solid var(--line); display: grid; gap: 10px; text-align: center; }
.request-bar .row { display: flex; gap: 8px; justify-content: center; }
.member-list .grow { flex: 1; }

.viewer-img.once { -webkit-user-drag: none; user-drag: none; }

/* ---- group notices, fixed-size view-once placeholders */
.notice.group-event { text-align: center; font-size: 12px; color: var(--muted); padding: 6px 12px; list-style: none; }

.presence-dot { width: 8px; height: 8px; border-radius: 50%; flex: none; align-self: center; margin-left: 6px; background: #34c759; }
.presence-dot.away { background: #ff9f0a; }

/* ---- phase 6: replies, edits, pins */
.chat-pane { position: relative; }
.reply-quote { display: grid; gap: 1px; max-width: 100%; text-align: left; padding: 4px 10px; border-radius: 12px; border-left: 3px solid var(--accent); background: var(--fill); font-size: 12px; color: var(--muted); }
.reply-quote b { color: var(--fg); font-weight: 600; }
.reply-quote span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 36ch; }
.orig-row { display: flex; }
.msg.mine .orig-row { justify-content: flex-end; }
.bubble.original { opacity: .6; background: var(--fill); color: var(--fg); text-decoration: line-through; text-decoration-color: color-mix(in srgb, var(--fg) 40%, transparent); }
.msg.mine .bubble.original { background: var(--fill); color: var(--fg); }
button.edited-link { background: none; padding: 0 12px; font-size: 11px; color: var(--accent); border-radius: 6px; }
button.edited-link:hover { background: none; text-decoration: underline; }
.pin-flag { font-size: 11px; color: var(--muted); }
li.msg.flash .bubble { animation: msg-flash 1.8s ease-out; }
@keyframes msg-flash { 0%, 30% { box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 50%, transparent); } 100% { box-shadow: 0 0 0 0 transparent; } }
.compose-bar { display: flex; align-items: center; gap: 8px; padding: 6px 12px; border-top: 1px solid var(--line); background: var(--side); font-size: 13px; }
.compose-bar[hidden] { display: none; }
.compose-text { flex: 1; min-width: 0; display: grid; border-left: 3px solid var(--accent); padding-left: 8px; }
.compose-text span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--muted); }
button.pin-btn { position: relative; }
.pin-count { position: absolute; top: 2px; right: 0; min-width: 15px; height: 15px; padding: 0 3px; border-radius: 8px; background: var(--accent); color: var(--accent-fg); font-size: 10px; line-height: 15px; text-align: center; }
.pin-panel { position: absolute; top: 56px; right: 10px; z-index: 20; width: min(360px, calc(100% - 20px)); max-height: min(60vh, 460px); overflow-y: auto; border-radius: 14px;
	background: var(--bg); border: 1px solid var(--line); box-shadow: 0 10px 30px rgba(0, 0, 0, .25); }
.pin-panel > header { padding: 10px 14px; border-bottom: 1px solid var(--line); position: sticky; top: 0; background: var(--bg); }
.pin-panel ul { list-style: none; margin: 0; padding: 4px; }
.pin-item { display: flex; align-items: center; gap: 4px; border-radius: 10px; }
.pin-item.muted { padding: 12px; }
button.pin-jump { flex: 1; min-width: 0; display: grid; gap: 4px; text-align: left; background: none; padding: 8px 10px; }
button.pin-jump:hover { background: var(--fill); }
.pin-jump b { font-size: 12px; color: var(--muted); font-weight: 600; }
.pin-text { overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow-wrap: anywhere; }
.pin-thumbs { display: flex; gap: 4px; }
.pin-thumb { position: relative; width: 56px; height: 56px; border-radius: 8px; overflow: hidden; background: var(--fill); }
.pin-thumb img, .pin-thumb canvas { width: 100%; height: 100%; object-fit: cover; display: block; }
.pin-play { position: absolute; inset: 0; display: grid; place-items: center; color: #fff; font-size: 16px; background: rgba(0, 0, 0, .25); }

/* ---- phase 6: chat themes, spinners */
.swatches { display: flex; flex-wrap: wrap; gap: 10px; }
button.swatch { width: 36px; height: 36px; padding: 0; border-radius: 50%; border: 3px solid transparent; }
button.swatch.on { border-color: var(--fg); }
.bg-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(72px, 1fr)); gap: 8px; }
button.bg-tile { height: 56px; padding: 0; border-radius: 12px; border: 3px solid transparent; font-size: 12px; color: var(--fg); }
button.bg-tile.on { border-color: var(--accent); }
button.bg-tile.none, button.bg-tile.upload { background: var(--fill); }
.chat-pane.has-bg .messages > .time-header, .chat-pane.has-bg .messages > .notice { background: color-mix(in srgb, var(--bg) 78%, transparent); border-radius: 12px; padding: 4px 10px; align-self: center; backdrop-filter: blur(6px); }
.chat-pane.has-bg .messages > .time-header { margin: 8px 0 4px; }
/* on a chat background: deleted/opened placeholders and the ⋯ button get a dark backing so they stay readable on light pictures */
.chat-pane.has-bg .msg .bubble.tomb { background: rgba(0, 0, 0, .5); color: rgba(255, 255, 255, .9); border-color: transparent; backdrop-filter: blur(6px); }
.chat-pane.has-bg button.icon.msg-more { background: rgba(0, 0, 0, .4); color: #fff; border-radius: 50%; }
.chat-pane.has-bg .messages > .older button { background: color-mix(in srgb, var(--bg) 78%, transparent); padding: 4px 10px; border-radius: 12px; }
.spinner { display: inline-block; width: 16px; height: 16px; border-radius: 50%; border: 2px solid currentColor; border-right-color: transparent; vertical-align: -3px; margin-right: 8px; animation: spin .8s linear infinite; flex: none; }
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .spinner { animation-duration: 2.4s; } }
li.loading-row { position: relative; display: flex; align-items: center; justify-content: center; padding: 28px 16px; list-style: none; opacity: 0; animation: fade-in .3s .25s forwards; }
li.loading-row .loading-mid { display: flex; align-items: center; }
li.loading-row .loading-mid .spinner { margin-right: 10px; color: var(--accent); }
li.loading-row button.link { position: absolute; right: 12px; top: 50%; transform: translateY(-50%); }
@keyframes fade-in { to { opacity: 1; } }

/* the options (⋯) button sits on the outer side: right of received bubbles, left of sent ones */
.msg.theirs button.icon.msg-more { order: 3; }
.link-url { font-family: ui-monospace, monospace; font-size: 13px; overflow-wrap: anywhere; padding: 8px 10px; border-radius: 10px; background: var(--fill); margin: 8px 0; }

/* system lines (time headers, group and theme notices, key notices, "load earlier") aren't selectable/copyable: only chat messages are */
.messages > .time-header, .messages > .notice, .messages > .older, .messages > .empty, .notice.group-event, .key-notice, .reply-quote, button.edited-link, .pin-flag, .compose-bar { user-select: none; -webkit-user-select: none; }
details.reclaim { margin: 4px 0; }
details.reclaim summary { cursor: pointer; color: var(--accent); font-size: 14px; padding: 4px 0; }

/* ---- canvas rendering of decrypted text (securetext.js) */
.st { display: block; position: relative; }
.st-canvas { display: block; user-select: none; -webkit-user-select: none; -webkit-touch-callout: none; } /* no max-width: squeezing the bitmap warps text until the re-wrap lands */
canvas.tray-thumb, canvas.avatar { display: block; }

/* custom chat background: a canvas stuck behind the messages (setCustomBg in app.js) */
.messages { isolation: isolate; }
.bg-holder { position: sticky; top: 0; height: 0; margin: 0 -16px; z-index: -1; pointer-events: none; list-style: none; }
.bg-holder canvas { position: absolute; left: 0; top: -14px; display: block; }
.st.inline { display: inline-block; vertical-align: top; }

/* in-chat search (openSearch in app.js) */
.chat-search { display: grid; gap: 4px; padding: 6px 10px 8px; border-bottom: 1px solid var(--line); background: color-mix(in srgb, var(--bg) 92%, transparent); }
.chat-search[hidden] { display: none; }
.search-row { display: flex; align-items: center; gap: 4px; }
.search-row input { flex: 1; min-width: 0; padding: 7px 11px; }
.search-count { flex: none; min-width: 64px; text-align: center; }
.search-results { list-style: none; margin: 0; padding: 0; max-height: 34vh; overflow-y: auto; display: grid; gap: 1px; }
button.search-hit { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; width: 100%; text-align: left; background: none; padding: 6px 8px; border-radius: 8px; }
button.search-hit:hover, button.search-hit.on { background: var(--fill); }
.search-snippet { flex: 1; min-width: 0; }
.chat-search > .search-older { padding: 0 4px; }

/* monitoring detector (watch.js, privacy.js): badge by the lock, and the red-state warning */
button.watch-btn { display: none; }
button.watch-btn.watch-clear, button.watch-btn.watch-present, button.watch-btn.watch-active { display: grid; }
button.watch-btn.watch-clear { color: var(--ok); }
button.watch-btn.watch-present { color: #e0a000; }
button.watch-btn.watch-active { color: var(--danger); }
.watch-screen { position: fixed; inset: 0; width: 100%; height: 100%; max-width: none; max-height: none; margin: 0; padding: 0; border: 0; background: transparent; color: var(--fg); }
.watch-screen[open] { display: grid; place-items: center; }
.watch-screen::backdrop { background: transparent; }
.watch-box { display: grid; gap: 10px; max-width: 340px; padding: 20px; border-radius: 16px; text-align: center; background: var(--bg); box-shadow: 0 8px 40px rgb(0 0 0 / .3); }
.watch-box .row { justify-content: center; }
.watch-msg { display: grid; gap: 2px; padding: 4px 0 10px; }
.watch-msg b { font-size: 16px; }
.watch-msg.watch-clear b { color: var(--ok); }
.watch-msg.watch-present b { color: #e0a000; }
.watch-msg.watch-active b { color: var(--danger); }

/* ---------------------------------------------------------------- calls */
.call-row { display: flex; justify-content: center; }
.call-pill { display: inline-flex; align-items: center; gap: 8px; padding: 8px 14px; border-radius: 18px; border: 0; background: var(--fill); color: var(--fg); font: inherit; font-size: 13px; }
button.call-pill { cursor: pointer; }
.call-pill .ico { width: 16px; height: 16px; }
.call-pill small { color: var(--muted); font-size: 12px; }
.call-pill.missed .ico, .call-pill.missed span { color: var(--danger); }
.call-pill.live { background: color-mix(in srgb, #34c759 18%, var(--fill)); }
.call-pill.live .ico { color: #34c759; }
.call-join { border: 0; border-radius: 12px; padding: 4px 12px; background: #34c759; color: #fff; font: inherit; font-weight: 600; cursor: pointer; }

.call-screen, .call-incoming { position: fixed; inset: 0; z-index: 60; color: #fff; background: #000; display: flex; flex-direction: column; overscroll-behavior: contain; touch-action: none; }
.call-screen[hidden], .call-mini[hidden] { display: none; }
.call-top { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 10px; padding: calc(10px + env(safe-area-inset-top)) 14px 10px; position: absolute; top: 0; left: 0; right: 0; z-index: 2;
	background: linear-gradient(rgb(0 0 0 / .55), transparent); }
.call-top-side { display: flex; gap: 8px; }
.call-top-side.end { justify-content: flex-end; }
.call-title { display: flex; flex-direction: column; align-items: center; text-align: center; min-width: 0; }
.call-status { font-size: 13px; opacity: .8; font-variant-numeric: tabular-nums; }
.call-icon { width: 36px; height: 36px; padding: 0; border-radius: 50%; border: 0; background: rgb(255 255 255 / .16); backdrop-filter: blur(12px); color: #fff; display: grid; place-items: center; cursor: pointer; line-height: 0; }
.call-icon .ico { display: block; width: 20px; height: 20px; margin: 0; }

.call-grid { flex: 1; display: grid; gap: 4px; padding: 4px; grid-template-columns: 1fr; grid-auto-rows: 1fr; min-height: 0; }
.call-grid[data-n="2"] { grid-template-columns: 1fr; }
.call-grid[data-n="3"], .call-grid[data-n="4"] { grid-template-columns: 1fr 1fr; }
.call-grid[data-n="5"], .call-grid[data-n="6"], .call-grid[data-n="7"], .call-grid[data-n="8"], .call-grid[data-n="9"] { grid-template-columns: repeat(3, 1fr); }
@media (min-aspect-ratio: 1/1) { .call-grid[data-n="2"] { grid-template-columns: 1fr 1fr; } }
.call-tile { position: relative; border-radius: 14px; overflow: hidden; background: #1c1c1e; display: grid; place-items: center; min-height: 0; transition: box-shadow .15s; }
.call-tile.speaking { box-shadow: inset 0 0 0 3px #34c759; }
.call-tile video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: none; }
.call-tile.fit video { object-fit: contain; }
.call-tile video.call-tile-bg { object-fit: cover; filter: blur(28px) brightness(.6); transform: scale(1.15); }
.call-tile.has-video video { display: block; }
.call-tile.has-video:not(.fit) video.call-tile-bg { display: none; }
.call-tile.has-video .call-tile-face { visibility: hidden; }
.call-tile-name { position: absolute; left: 10px; bottom: 10px; display: flex; align-items: center; gap: 6px; padding: 3px 8px; border-radius: 10px; background: rgb(0 0 0 / .45); }
.call-tile-name .ico { width: 14px; height: 14px; color: #ff453a; display: none; }
.call-tile-name .call-vol-btn { display: inline-flex; padding: 0; width: 22px; height: 22px; align-items: center; justify-content: center; border-radius: 50%; background: transparent; color: #fff; opacity: 0; transition: opacity .15s; }
.call-tile-name .call-vol-btn .ico { display: block; width: 14px; height: 14px; color: inherit; }
.call-tile:hover .call-vol-btn, .call-vol-btn:focus-visible { opacity: .9; }
.call-tile.vol-off .call-vol-btn { opacity: .9; }
.call-tile.vol-off .call-tile-face { opacity: .55; }
.call-vol { position: absolute; left: 50%; bottom: 132px; transform: translateX(-50%); z-index: 6; width: min(320px, calc(100% - 32px)); box-sizing: border-box; padding: 14px 16px; border-radius: 18px;
	background: rgb(28 28 30 / .96); color: #fff; box-shadow: 0 8px 30px rgb(0 0 0 / .5); display: grid; gap: 10px; }
.call-vol-name { display: flex; justify-content: center; }
.call-vol-row { display: grid; grid-template-columns: 1fr auto; align-items: center; gap: 12px; }
.call-vol-row input { width: 100%; padding: 0; background: none; accent-color: var(--accent, #0a84ff); }
.call-vol-pct { min-width: 4ch; text-align: right; font-variant-numeric: tabular-nums; font-size: 14px; }
.call-vol-actions { display: flex; gap: 8px; justify-content: center; }
.call-vol-actions button { background: rgb(255 255 255 / .14); color: #fff; padding: 7px 12px; font-size: 14px; }
.call-tile.mic-off .call-tile-name .ico { display: block; }
.call-grid[data-n="1"] .call-tile { border-radius: 0; margin: -4px; }
.call-waiting { display: grid; place-items: center; }
.call-waiting .avatar { animation: call-pulse 1.6s ease-in-out infinite; }
@keyframes call-pulse { 50% { transform: scale(1.06); opacity: .8; } }

.call-self { position: absolute; right: 12px; bottom: calc(110px + env(safe-area-inset-bottom)); width: 112px; max-height: 34vh; aspect-ratio: var(--ar, 3 / 4); object-fit: cover; border-radius: 14px;
	transform: scaleX(-1); z-index: 3; background: #1c1c1e; box-shadow: 0 6px 24px rgb(0 0 0 / .5); cursor: grab; touch-action: none; }
.call-self.moved { left: var(--x); top: var(--y); right: auto; bottom: auto; }
@media (min-width: 700px) { .call-self { width: 220px; aspect-ratio: var(--ar, 4 / 3); } }

.call-controls { display: flex; justify-content: center; gap: 14px; padding: 16px 12px calc(18px + env(safe-area-inset-bottom)); position: absolute; left: 0; right: 0; bottom: 0; z-index: 2;
	background: linear-gradient(transparent, rgb(0 0 0 / .6)); }
.call-btn { display: flex; flex-direction: column; align-items: center; gap: 6px; border: 0; background: none; color: #fff; font: inherit; font-size: 11px; cursor: pointer; padding: 0; }
.call-btn .ico { width: 56px; height: 56px; padding: 16px; border-radius: 50%; background: rgb(255 255 255 / .18); backdrop-filter: blur(12px); transition: background .15s; }
.call-btn.off .ico { background: #fff; color: #000; }
.call-btn.on .ico { background: var(--accent); }
.call-btn.decline .ico { background: #ff3b30; }
.call-btn.accept .ico { background: #34c759; }
@media (hover: hover) { .call-btn:hover .ico { filter: brightness(1.15); } }

.call-mini { position: fixed; top: calc(8px + env(safe-area-inset-top)); left: 50%; transform: translateX(-50%); z-index: 60; display: flex; align-items: center; gap: 8px;
	padding: 8px 16px; border: 0; border-radius: 20px; background: #34c759; color: #fff; font: inherit; font-size: 13px; box-shadow: 0 4px 16px rgb(0 0 0 / .25); cursor: pointer; font-variant-numeric: tabular-nums; }
.call-mini .ico { width: 16px; height: 16px; }

.call-incoming { z-index: 61; background: rgb(10 10 12 / .92); backdrop-filter: blur(24px); align-items: center; justify-content: center; }
.call-incoming-card { display: flex; flex-direction: column; align-items: center; gap: 10px; text-align: center; }
.call-incoming .avatar { width: 112px; height: 112px; font-size: 46px; animation: call-pulse 1.6s ease-in-out infinite; }
.call-sub { opacity: .75; }
.call-answer { display: flex; gap: 72px; margin-top: 48px; }
.call-answer .call-btn { font-size: 13px; }
.call-answer .call-btn .ico { width: 68px; height: 68px; padding: 20px; }
.icon.call-start .ico { width: 21px; height: 21px; }
.notice.call-row { border: 0; padding: 2px 0; background: none; }
.call-grid[data-n="1"] .call-tile-name { display: none; }
/* optimistic send */
.msg.pending .bubble { opacity: .6; }
.msg.failed .bubble { opacity: .6; box-shadow: inset 0 0 0 1.5px var(--danger); }
.send-state { font: inherit; font-size: 11px; color: var(--muted); background: none; border: 0; padding: 0; }
button.send-state { cursor: pointer; text-decoration: underline; }
.send-state.failed { color: var(--danger); }
.call-screen.solo .call-self { left: 0; top: 0; right: 0; bottom: 0; width: 100%; height: 100%; max-height: none; aspect-ratio: auto; border-radius: 0; z-index: 0; box-shadow: none; cursor: default; }
.call-screen.solo .call-grid { position: relative; z-index: 1; }
.call-screen.solo .call-waiting { align-items: start; padding-top: 22vh; }
/* chat backgrounds: call rows are solid pills already (no backing); the small loose text gets one */
.chat-pane.has-bg .messages > .notice.call-row { background: none; backdrop-filter: none; padding: 2px 0; }
.chat-pane.has-bg .msg .sender, .chat-pane.has-bg .msg-time-reveal, .chat-pane.has-bg .msg-meta > *, .chat-pane.has-bg .receipt {
	background: color-mix(in srgb, var(--bg) 78%, transparent); backdrop-filter: blur(6px); border-radius: 8px; padding: 1px 6px; }

.call-muted-hint { position: absolute; left: 50%; top: calc(70px + env(safe-area-inset-top)); transform: translateX(-50%); z-index: 4; display: flex; align-items: center; gap: 8px;
	padding: 8px 14px; border: 0; border-radius: 18px; background: rgb(0 0 0 / .7); backdrop-filter: blur(12px); color: #fff; font: inherit; font-size: 13px; cursor: pointer; white-space: nowrap; }
.call-muted-hint[hidden] { display: none; }
.call-muted-hint .ico { width: 16px; height: 16px; color: #ff453a; }
body.call-pip { margin: 0; background: #000; overflow: hidden; }
body.call-pip .call-btn span, .call-screen.compact .call-btn span { display: none; }
body.call-pip .call-btn .ico, .call-screen.compact .call-btn .ico { width: 44px; height: 44px; padding: 12px; }
body.call-pip .call-controls, .call-screen.compact .call-controls { gap: 10px; padding: 10px; }
body.call-pip .call-self, .call-screen.compact .call-self { width: 96px; bottom: 74px; }
body.call-pip .call-top-side { display: none; }
body.call-pip .call-top { grid-template-columns: 1fr; }

/* typing indicator */
.typing-row { list-style: none; display: flex; align-items: center; gap: 8px; padding: 4px 0 2px; }
.typing-dots { display: inline-flex; gap: 4px; padding: 11px 13px; border-radius: 18px; background: var(--fill); }
.typing-dots i { width: 7px; height: 7px; border-radius: 50%; background: var(--muted); animation: typing-dot 1.2s ease-in-out infinite; }
.typing-dots i:nth-child(2) { animation-delay: .15s; }
.typing-dots i:nth-child(3) { animation-delay: .3s; }
@keyframes typing-dot { 0%, 60%, 100% { opacity: .35; transform: translateY(0); } 30% { opacity: 1; transform: translateY(-3px); } }
.typing-text { color: var(--muted); }
.chat-pane.has-bg .typing-text { background: color-mix(in srgb, var(--bg) 78%, transparent); backdrop-filter: blur(6px); border-radius: 8px; padding: 1px 6px; }

/* "New messages" divider */
.new-divider { list-style: none; display: flex; align-items: center; gap: 10px; margin: 10px 0 6px; color: var(--danger); font-size: 12px; font-weight: 600; }
.new-divider::before, .new-divider::after { content: ""; flex: 1; height: 1px; background: color-mix(in srgb, var(--danger) 45%, transparent); }
.chat-pane.has-bg .new-divider span { background: color-mix(in srgb, var(--bg) 78%, transparent); backdrop-filter: blur(6px); border-radius: 8px; padding: 1px 8px; }

/* jump to latest */
.jump-holder { list-style: none; position: sticky; bottom: 12px; height: 0; margin: 0; z-index: 5; }
.jump-btn { position: absolute; right: 4px; bottom: 0; width: 40px; height: 40px; border-radius: 50%; border: 1px solid var(--line); background: var(--bg); color: var(--fg);
	display: grid; place-items: center; padding: 0; box-shadow: 0 4px 16px rgb(0 0 0 / .18); cursor: pointer; opacity: 0; pointer-events: none; transform: translateY(8px); transition: opacity .15s, transform .15s; }
.jump-btn.show { opacity: 1; pointer-events: auto; transform: none; }
.jump-btn .ico { width: 20px; height: 20px; }
.jump-count { position: absolute; top: -6px; right: -4px; min-width: 18px; height: 18px; padding: 0 5px; border-radius: 9px; background: var(--accent); color: #fff; font-size: 11px; font-weight: 600; line-height: 18px; text-align: center; }
.jump-count[hidden] { display: none; }

/* voice messages */
.composer button[hidden] { display: none; }
.composer.recording > :not(.rec-bar) { display: none; }
.rec-bar { flex: 1; display: flex; align-items: center; gap: 10px; min-width: 0; height: 40px; }
.rec-dot { width: 10px; height: 10px; border-radius: 50%; background: var(--danger); animation: call-pulse 1.2s ease-in-out infinite; flex: none; }
.rec-time { font-variant-numeric: tabular-nums; font-size: 14px; flex: none; min-width: 40px; }
.rec-wave { flex: 1; display: flex; align-items: center; justify-content: flex-end; gap: 2px; height: 28px; overflow: hidden; min-width: 0; }
.rec-wave i, .voice-wave i { flex: none; width: 3px; height: var(--h); border-radius: 2px; background: currentColor; }
.rec-wave { color: var(--danger); }
.voice-msg { display: flex; align-items: center; gap: 10px; padding: 8px 12px 8px 8px; border-radius: 20px; width: 270px; max-width: 100%; background: var(--fill); color: var(--fg); overflow: visible; }
.msg.mine .voice-msg { background: var(--accent); color: var(--accent-fg); }
.voice-play { width: 34px; height: 34px; border-radius: 50%; border: 0; padding: 0; display: grid; place-items: center; background: color-mix(in srgb, currentColor 18%, transparent); color: inherit; cursor: pointer; flex: none; }
.voice-play .ico { width: 16px; height: 16px; fill: currentColor; }
.voice-wave { flex: 1; display: flex; align-items: center; gap: 2px; height: 30px; cursor: pointer; min-width: 0; }
.voice-wave i { width: auto; flex: 1; max-width: 3px; opacity: .45; }
.voice-wave i.on { opacity: 1; }
.voice-time { font-size: 12px; font-variant-numeric: tabular-nums; opacity: .85; flex: none; }
.voice-speed { border: 0; border-radius: 10px; padding: 2px 7px; font: inherit; font-size: 11px; font-weight: 600; background: color-mix(in srgb, currentColor 18%, transparent); color: inherit; cursor: pointer; flex: none; }

/* terms / privacy pages */
.doc { max-width: 680px; margin: 0 auto; padding: 32px 16px 64px; line-height: 1.55; }
.doc h1 { margin: 0 0 4px; }
.doc h2 { font-size: 17px; margin: 28px 0 8px; }
.doc ul { padding-left: 20px; }
.doc li { margin: 4px 0; }
.doc a, .check a { color: var(--accent); }

/* Settings: a grouped list on small screens (each row opens its own page),
   a sidebar beside the open page from 768px. .on-page = a page is open. */
.settings-dialog { width: min(820px, calc(100vw - 32px)); height: min(640px, calc(100vh - 48px)); padding: 0; overflow: hidden; }
.settings-dialog[open] { display: flex; flex-direction: column; }
.settings-dialog > header { padding: 14px 18px 0; margin-bottom: 6px; }
.dialog form.settings-form { flex: 1; min-height: 0; display: block; }
.settings-shell { height: 100%; display: grid; grid-template-columns: 240px minmax(0, 1fr); }
.settings-nav { background: var(--side); border-right: 1px solid var(--line); padding: 8px 10px 16px; overflow-y: auto; display: flex; flex-direction: column; gap: 10px; }
.settings-who { display: flex; gap: 10px; align-items: center; padding: 6px 6px 2px; min-width: 0; }
.settings-who > div { min-width: 0; }
.settings-who small { display: block; font-size: 12px; }
.settings-search { padding: 7px 10px; }
.settings-menu { display: grid; gap: 2px; }
.settings-menu[hidden] { display: none; }
.settings-item { display: flex; align-items: center; gap: 10px; width: 100%; padding: 6px 8px; border-radius: 9px; background: none; text-align: left; font-weight: 500; color: var(--fg); }
.settings-item:hover { background: var(--fill); }
.settings-item.on { background: var(--accent); color: var(--accent-fg); }
.settings-item > svg:last-child { width: 16px; height: 16px; color: var(--muted); flex: none; }
.settings-ico { width: 26px; height: 26px; border-radius: 7px; display: grid; place-items: center; color: #fff; flex: none; }
.settings-ico svg { width: 15px; height: 15px; }
.ico-profile { background: #5856d6; }
.ico-privacy { background: #0a84ff; }
.ico-notifications { background: #ff3b30; }
.ico-appearance { background: #af52de; }
.ico-security { background: #34c759; }
.ico-blocked { background: #ff9500; }
.ico-about { background: #8e8e93; }
.settings-label { flex: 1; min-width: 0; display: grid; }
.settings-label small { font-size: 12px; font-weight: 400; }
.settings-item.on small { color: inherit; opacity: .8; }
.settings-none { padding: 6px 8px; }
.settings-page { overflow-y: auto; padding: 4px 24px 0; display: flex; flex-direction: column; gap: 14px; min-width: 0; }
.settings-page-head { display: flex; align-items: center; gap: 8px; }
.settings-page-head h3 { margin: 0; font-size: 20px; }
.settings-back { display: none; }
.settings-save { position: sticky; bottom: 0; margin-top: auto; background: var(--bg); padding: 10px 0 16px; justify-content: flex-end; }
.settings-group { display: grid; gap: 10px; }
.settings-group > h4 { margin: 8px 0 0; font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); }
.settings-page .privacy-settings > h3, .settings-page .notif-settings > h3 { display: none; }
.about-link { color: var(--accent); }
.setting-flash { animation: settings-flash 1.4s ease-out; border-radius: 8px; }
@keyframes settings-flash { from { background: color-mix(in srgb, var(--accent) 22%, transparent); } to { background: transparent; } }
@media (prefers-reduced-motion: reduce) { .setting-flash { animation: none; outline: 2px solid var(--accent); outline-offset: 2px; } }
@media (min-width: 768px) {
	.settings-item > svg:last-child { display: none; }
}
@media (max-width: 767px) {
	.room-header .search-btn, .room-header .pin-btn { display: none; } /* in the ⋯ menu instead (showRoomMenu) */
	.settings-dialog { width: 100vw; max-width: 100vw; height: 100dvh; max-height: 100dvh; border-radius: 0; }
	.settings-shell { grid-template-columns: minmax(0, 1fr); }
	.settings-nav { border-right: 0; background: var(--bg); padding: 4px 16px 16px; }
	.settings-shell.on-page .settings-nav { display: none; }
	.settings-shell:not(.on-page) .settings-page { display: none; }
	.settings-page { padding: 0 16px; }
	.settings-back { display: inline-flex; align-items: center; gap: 2px; padding-left: 0; }
	.settings-back svg { width: 18px; height: 18px; }
	.settings-page-head { flex-direction: column; align-items: flex-start; gap: 4px; }
	.settings-page-head h3 { font-size: 24px; }
	.settings-menu { background: var(--side); border-radius: 12px; padding: 4px; }
	.settings-item { padding: 9px 8px; }
	.settings-item.on { background: none; color: var(--fg); }
	.settings-item.on small { opacity: 1; }
}
.settings-page-head h3 { text-transform: none; letter-spacing: normal; color: var(--fg); font-weight: 700; }
.settings-page[data-page="security"] .settings-save, .settings-page[data-page="about"] .settings-save, .settings-page[data-page="blocked"] .settings-save { display: none; }
.security-row.danger { color: var(--danger); }
.doc .caps { font-size: 14px; letter-spacing: .01em; }
.terms-gate { display: grid; gap: 12px; }
.terms-gate:not([open]) { display: none; }
.terms-gate h2 { margin: 0; font-size: 19px; }
.terms-gate p { margin: 0; }
.terms-gate a { color: var(--accent); }
.terms-gate .row { justify-content: flex-end; }
.dialog > header .dialog-back { margin-right: 4px; }
.dialog > header .dialog-back + h2 { flex: 1; }

/* ---- motion + menus (2026-10-17). Easing: ease-out for things opening/closing, ease-in-out for things
   already on screen moving, spring only for the fun bits (reactions, sends). */
:root { --ease-out: cubic-bezier(.2, .8, .2, 1); --ease-io: cubic-bezier(.45, 0, .55, 1); --spring: cubic-bezier(.34, 1.56, .64, 1); }
.ctx-menu button { justify-content: flex-start; align-items: center; gap: 10px; }
.ctx-menu button .ico { width: 16px; height: 16px; color: var(--muted); flex: none; }
.ctx-menu button.danger .ico, .ctx-menu button.blink .ico { color: inherit; }
.ctx-arrow { margin-left: auto; }
.ctx-menu button.blink { animation: ctx-blink 150ms steps(1, end); }
@keyframes ctx-blink { 0% { background: var(--accent); color: var(--accent-fg); } 33% { background: transparent; color: inherit; } 66%, 100% { background: var(--accent); color: var(--accent-fg); } }
.ctx-react { display: flex; gap: 2px; padding: 2px 2px 6px; margin-bottom: 4px; border-bottom: 1px solid var(--line); }
.ctx-menu .ctx-react button { width: 32px; height: 32px; padding: 0; justify-content: center; border-radius: 50%; font-size: 18px; line-height: 1; }
.ctx-menu .ctx-react button.mine { background: color-mix(in srgb, var(--accent) 22%, transparent); }
.ctx-menu .ctx-react .ico { width: 18px; height: 18px; }
.msg.menu-target .bubble-row { filter: brightness(.9); }
/* ⋯: the full menu, grown out of the button (a bottom sheet on phones) */
.dialog.msg-pop { margin: 0; inset: auto; width: 290px; padding: 8px; }
.dialog.msg-pop.bottom { inset: auto 8px calc(8px + env(safe-area-inset-bottom)) 8px; width: auto; max-width: none; }
.dialog.msg-pop::backdrop { background: rgb(0 0 0 / .12); backdrop-filter: none; }
.pop-preview { display: grid; gap: 2px; padding: 6px 10px 10px; border-bottom: 1px solid var(--line); margin-bottom: 4px; }
.pop-preview small { font-size: 12px; }
.sheet button.pop-item { display: flex; align-items: center; gap: 12px; text-align: left; color: var(--fg); padding: 11px 12px; font-size: 15px; }
.sheet button.pop-item .ico { width: 18px; height: 18px; color: var(--muted); }
.sheet button.pop-item.danger, .sheet button.pop-item.danger .ico { color: var(--danger); }
/* touch long press: shrinks while held, then the bubble lifts above a dim layer */
.bubble-row { transition: scale 140ms ease-out; }
.bubble-row.pressing { scale: .96; transition: scale 450ms var(--ease-out); }
.lift-dim { position: absolute; inset: 0; z-index: 40; background: rgb(0 0 0 / .35); }
.msg.lifted { position: relative; z-index: 41; }
.msg-react-bar.lift-react { position: absolute; top: auto; right: auto; z-index: 42; display: flex; opacity: 1; pointer-events: auto; }
.msg-react-bar.lift-react::after { display: none; }
.lift-sheet { position: absolute; left: 8px; right: 8px; bottom: calc(8px + env(safe-area-inset-bottom)); z-index: 42; background: var(--bg); border: 1px solid var(--line); border-radius: 16px; overflow: hidden; box-shadow: 0 8px 32px rgb(0 0 0 / .25); touch-action: none; }
.lift-grab { height: 20px; display: grid; place-items: center; }
.lift-grab::before { content: ""; width: 36px; height: 5px; border-radius: 3px; background: var(--fill-2); }
.lift-sheet button { display: flex; align-items: center; gap: 14px; width: 100%; padding: 14px 18px; border-radius: 0; background: none; font-size: 16px; text-align: left; border-top: 1px solid var(--line); }
.lift-sheet button:hover, .lift-sheet button:active { background: var(--fill); }
.lift-sheet button .ico { width: 20px; height: 20px; }
.lift-sheet button.danger { color: var(--danger); }
/* swipe right to reply */
.msg[data-reply] { position: relative; }
.swipe-reply { position: absolute; left: -36px; top: 50%; translate: 0 -50%; width: 28px; height: 28px; border-radius: 50%; background: var(--fill); color: var(--fg); display: grid; place-items: center; opacity: 0; pointer-events: none; transition: scale 160ms var(--spring), background-color 120ms; }
.swipe-reply .ico { width: 15px; height: 15px; }
.swipe-reply.armed { scale: 1.15; background: var(--accent); color: var(--accent-fg); }
@media (prefers-reduced-motion: reduce) { .bubble-row.pressing { scale: 1; } }
/* Press feedback, sized to the thing pressed: small controls sink a little (scale, not transform: some already use
   transform); rows, links, tiles and full-width buttons don't move at all (4% of a wide row is a visible jump),
   they light up instead, like iOS list rows. */
button { transition: scale 80ms ease-out; }
button:active:not(:disabled) { scale: .96; }
:is(button.room, .lift-sheet button, .bubble, .sidebar > button.me, .settings-item, button.link, .security-row, .sheet button, .ctx-menu button,
	button.user, .media-tile, .reply-quote, .edited-link, button.receipt, .send-state, .call-msg-peek, form.card > button.primary, .settings-back):active { scale: 1; }
:is(button.room, .sidebar > button.me, .settings-item, .security-row, .sheet button, .ctx-menu button, button.user):active:not(:disabled) { background: var(--fill-2); }
:is(button.link, .edited-link, button.receipt, .send-state, .settings-back):active { opacity: .6; }
form.card > button.primary:active:not(:disabled) { filter: brightness(.9); }
/* just sent: rises out of the message box with a little overshoot */
li.msg.sent-pop { animation: sent-pop 320ms cubic-bezier(.3, 1.25, .6, 1) both; transform-origin: 100% 100%; }
@keyframes sent-pop { from { translate: 0 28px; scale: .94; opacity: .3; } to { translate: 0 0; scale: 1; opacity: 1; } }
.reaction-chip.pop, .mode-chip.pop { animation: chip-pop 360ms var(--spring) both; }
@keyframes chip-pop { from { scale: .3; opacity: 0; } to { scale: 1; opacity: 1; } }
.poof-dot { position: fixed; z-index: 90; border-radius: 50%; pointer-events: none; }
/* what the next message will be (timer / view once): a strip above the box, not inside it */
.compose-modes { display: flex; flex-wrap: wrap; gap: 6px; padding: 6px 12px 0; background: var(--bg); }
.mode-chip { display: inline-flex; align-items: center; gap: 5px; padding: 4px 10px; border-radius: 14px; font-size: 13px; font-weight: 600;
	color: var(--accent); background: color-mix(in srgb, var(--accent) 13%, transparent); }
.mode-chip:hover { background: color-mix(in srgb, var(--accent) 20%, transparent); }
.mode-chip .ico { width: 15px; height: 15px; }
/* phones: + opens a tray with the options instead of four buttons squeezing the box */
button.icon.plus-btn { color: var(--muted); background: var(--fill); width: 36px; height: 36px; margin-bottom: 1px; transition: rotate 200ms var(--ease-out), scale 80ms ease-out; }
button.icon.plus-btn[aria-expanded="true"] { rotate: 45deg; color: var(--fg); }
.plus-tray { display: grid; grid-template-columns: repeat(4, 1fr); gap: 4px; padding: 12px 10px 4px; background: var(--bg); border-top: 1px solid var(--line); }
.plus-tray[hidden] { display: none; }
.plus-tile { display: grid; justify-items: center; gap: 6px; padding: 6px 0; border-radius: 12px; background: none; font-size: 12px; color: var(--fg); }
.plus-tile:hover { background: none; }
.plus-ic { width: 50px; height: 50px; border-radius: 50%; background: var(--fill); color: var(--accent); display: grid; place-items: center; transition: background-color 120ms, color 120ms; }
.plus-ic .ico { width: 22px; height: 22px; }
.plus-tile.on .plus-ic { background: var(--accent); color: var(--accent-fg); }
@media (min-width: 768px) { button.icon.plus-btn, .plus-tray { display: none; } }
@media (max-width: 767px) { .composer .composer-opts, .composer button.icon.emoji-btn { display: none; } }
@media (prefers-reduced-motion: reduce) { li.msg.sent-pop, .reaction-chip.pop, .mode-chip.pop { animation: none; } button:active:not(:disabled) { scale: 1; } }

/* forms laid out as a row (group rename) stay a row inside dialogs */
.dialog form.row { display: flex; }
/* phones: dialogs are full pages (sliding up), action sheets sit at the bottom; alerts stay small */
@media (max-width: 767px) {
	.dialog:not(.sheet):not(.confirm) { width: 100vw; max-width: 100vw; height: 100dvh; max-height: 100dvh; margin: 0; border-radius: 0;
		padding: calc(12px + env(safe-area-inset-top)) 16px calc(16px + env(safe-area-inset-bottom)); box-shadow: none; overflow-y: auto; }
	.dialog.sheet:not(.msg-pop) { width: calc(100vw - 16px); max-width: none; margin: auto auto calc(8px + env(safe-area-inset-bottom)); }
}
/* the message list stays selectable for mouse users: Chrome only runs Copy (securetext.js copies the drawn
   selection) inside selectable content. Touch screens keep it off so long presses don't highlight. */
.messages { -webkit-user-select: text; user-select: text; }
@media (pointer: coarse) { .messages { -webkit-user-select: none; user-select: none; } }
/* incoming call while in the app: a banner, not a full-screen takeover */
.call-banner { position: fixed; z-index: 61; top: calc(8px + env(safe-area-inset-top)); left: 50%; translate: -50% 0; width: min(420px, calc(100vw - 16px));
	display: flex; align-items: center; gap: 10px; padding: 10px; border-radius: 22px; background: #1c1c1e; color: #fff; box-shadow: 0 10px 30px rgb(0 0 0 / .35); }
.call-banner .avatar { animation: call-pulse 1.6s ease-in-out infinite; }
.call-banner-text { flex: 1; min-width: 0; display: grid; }
.call-banner .call-sub { font-size: 13px; opacity: .7; }
.call-banner-btn { width: 42px; height: 42px; border-radius: 50%; padding: 10px; border: 0; display: grid; place-items: center; color: #fff; flex: none; cursor: pointer; }
.call-banner-btn .ico { width: 20px; height: 20px; }
.call-banner-btn.decline { background: #ff3b30; }
.call-banner-btn.accept { background: #34c759; }
.call-pip-src { position: fixed; left: -10000px; top: 0; width: 160px; height: 120px; pointer-events: none; }

/* voice messages: drag along the waveform to seek; on phones half the bars, thicker and further apart */
.voice-wave { touch-action: pan-y; }
@media (max-width: 767px) {
	.voice-wave { gap: 3px; }
	.voice-wave i:nth-child(even) { display: none; }
	.voice-wave i { max-width: 4px; min-width: 3px; }
}
/* phones: dragging a chat back from the left edge shows the list underneath (sideways drags are the
   page's to handle, not the browser's) */
@media (max-width: 767px) {
	.shell, .room-list { touch-action: pan-y pinch-zoom; } /* (a scroll container needs its own: ancestors' stop there) */
	.shell.in-room.peek .sidebar { display: flex; position: fixed; inset: 0; z-index: 0; transform: translateX(-30%); opacity: .6; }
	.shell.in-room.peek .chat-pane { position: relative; z-index: 1; box-shadow: -8px 0 24px rgb(0 0 0 / .18); }
}

/* ---- spoilers: hidden until tapped (the text is drawn as ink by securetext.js, pictures show a blurred preview) */
.msg .bubble.spoiler, .media-msg.spoiler { cursor: pointer; }
.media-tile.spoiler-tile { position: relative; display: block; overflow: hidden; background: color-mix(in srgb, #000 40%, var(--fill)); max-width: 100%; }
.media-tile.spoiler-tile .media-ph { position: absolute; inset: 0; width: 100%; height: 100%; filter: blur(22px); transform: scale(1.25); }
.spoiler-ink { position: absolute; inset: 0; width: 100%; height: 100%; display: block; pointer-events: none; }
.ink-snippet { display: inline-flex; align-items: center; min-width: 0; max-width: 100%; }
.media-msg.spoiler .file-chip { pointer-events: none; }

/* ---- chat beside the call (split view): the call shrinks, the chat stays usable */
.call-icon { position: relative; }
.call-icon.on { background: rgb(255 255 255 / .34); }
.call-chat-badge { position: absolute; top: -4px; right: -4px; min-width: 18px; height: 18px; padding: 0 5px; border-radius: 9px; background: #ff3b30; color: #fff; font-size: 11px; font-weight: 700; line-height: 18px; text-align: center; box-sizing: border-box; }
.call-chat-badge[hidden] { display: none; }
.call-msg-peek { position: absolute; top: 64px; left: 50%; transform: translateX(-50%); z-index: 7; max-width: calc(100% - 32px); display: grid; gap: 2px; text-align: left; padding: 10px 14px; border-radius: 16px;
	background: rgb(44 44 46 / .94); color: #fff; box-shadow: 0 8px 24px rgb(0 0 0 / .45); }
.call-msg-peek:hover { background: rgb(58 58 60 / .96); }
.call-msg-peek b, .call-msg-peek span { display: block; }
@media (min-width: 721px) {
	html.call-split { --call-chat-w: clamp(340px, 38vw, 480px); }
	html.call-split .call-screen { right: var(--call-chat-w); }
	html.call-split .shell { position: fixed; inset: 0 0 0 auto; width: var(--call-chat-w); grid-template-columns: 1fr; border-left: 1px solid var(--line); background: var(--bg); }
	html.call-split .sidebar { display: none; }
	/* the chat beside a call is phone-narrow: phone composer (+ tray) and header (search + pins in ⋯) */
	html.call-split .composer .composer-opts, html.call-split .composer button.icon.emoji-btn,
	html.call-split .room-header .search-btn, html.call-split .room-header .pin-btn { display: none; }
	html.call-split button.icon.plus-btn { display: grid; }
	html.call-split .plus-tray:not([hidden]) { display: grid; }
}
@media (max-width: 720px) {
	html.call-split .call-screen { bottom: auto; height: 38dvh; }
	html.call-split .shell { position: fixed; inset: 38dvh 0 0 0; height: auto; } /* not 100%: that pushed the composer below the screen */
}

/* ---- forward: pick a chat */
dialog.forward-pick { width: min(400px, calc(100vw - 32px)); }
dialog.forward-pick .filter { margin: 0 0 8px; }
.forward-list { max-height: min(60vh, 480px); padding: 0; }

/* ---- call: a shared screen on a stage, cameras in a strip under it */
.call-grid.stage { display: flex; flex-direction: column; }
.call-screen.stage .call-controls { position: relative; } /* in the column, so the strip sits above them and stays tappable */
.call-screen.compact .call-strip, body.call-pip .call-strip { height: clamp(56px, 12vh, 84px); }
.call-stage { flex: 1; min-height: 0; display: grid; gap: 4px; grid-auto-rows: 1fr; }
@media (min-aspect-ratio: 1/1) { .call-stage[data-n="2"], .call-stage[data-n="3"], .call-stage[data-n="4"] { grid-template-columns: 1fr 1fr; } }
.call-stage .call-tile { background: #000; }
.call-strip { flex: none; display: flex; gap: 4px; height: clamp(84px, 18vh, 150px); overflow-x: auto; scrollbar-width: none; }
.call-strip .call-tile { flex: 0 0 auto; height: 100%; aspect-ratio: 4 / 3; border-radius: 10px; }
.call-strip .call-tile-face { transform: scale(.5); }
.call-strip .call-tile-name { left: 6px; bottom: 6px; padding: 2px 6px; }
.call-tile.screen .call-tile-name .ico { display: block; color: #fff; }
.call-strip .call-tile { cursor: pointer; transition: box-shadow .15s, scale 80ms ease-out; }
.call-strip .call-tile:active:not(:has(.call-vol-btn:active)) { scale: .97; }
.call-tile[tabindex]:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.call-stage .call-tile[tabindex] { cursor: pointer; }
@media (prefers-reduced-motion: reduce) { .call-strip .call-tile:active { scale: 1; } }
.call-screen.stage .call-self:not(.moved) { top: calc(64px + env(safe-area-inset-top)); bottom: auto; }
.forward-list button.room .fwd-check { flex: none; width: 22px; height: 22px; border-radius: 50%; border: 2px solid var(--line); display: grid; place-items: center; }
.forward-list button.room.picked .fwd-check { background: var(--accent); border-color: var(--accent); color: var(--accent-fg); }
.forward-list button.room .fwd-check .ico { width: 14px; height: 14px; stroke-width: 3; }
.forward-send { display: flex; gap: 8px; padding-top: 10px; border-top: 1px solid var(--line); margin-top: 6px; }
.forward-send input { flex: 1; min-width: 0; }
.forward-send button { flex: none; }
.fwd-tag { display: flex; align-items: center; gap: 4px; font-size: 12px; color: var(--muted); padding: 0 4px 2px; }
.fwd-tag .ico { width: 13px; height: 13px; }
/* small text over a chat background: a near-solid pill and near-body text (gray on a see-through pill read
   at about 2:1 over a dark picture in light mode; this is well over 7:1 on any picture) */
.fwd-tag > span { display: inline-flex; align-items: center; gap: 4px; }
.chat-pane.has-bg .msg .sender, .chat-pane.has-bg .msg-time-reveal, .chat-pane.has-bg .msg-meta > *, .chat-pane.has-bg .receipt,
.chat-pane.has-bg .fwd-tag > span, .chat-pane.has-bg button.edited-link {
	background: color-mix(in srgb, var(--bg) 90%, transparent); color: color-mix(in srgb, var(--fg) 80%, var(--bg)); backdrop-filter: blur(6px); border-radius: 8px; padding: 1px 6px; }
.chat-pane.has-bg .send-state.failed { color: var(--danger); }

/* ---- raised surfaces (dialogs, menus, sheets, pickers) get their own color: in dark mode a lighter gray
   than the page (black on black blended in); light mode keeps white on the dimmed page */
@media (prefers-color-scheme: dark) {
	:root:not([data-theme="light"]) :is(.dialog, .ctx-menu, .lift-sheet, .emoji-picker, .msg-react-bar, .pin-panel) {
		--bg: #1c1c1e; --fill: #2c2c2e; --fill-2: #3a3a3c; --line: #38383a; }
}
:root[data-theme="dark"] :is(.dialog, .ctx-menu, .lift-sheet, .emoji-picker, .msg-react-bar, .pin-panel) {
	--bg: #1c1c1e; --fill: #2c2c2e; --fill-2: #3a3a3c; --line: #38383a; }
.dialog::backdrop { background: rgb(0 0 0 / .45); }
.ctx-menu { box-shadow: 0 10px 34px rgb(0 0 0 / .28), 0 0 0 .5px rgb(0 0 0 / .08); }
.ctx-menu.dropdown { animation: drop-in 160ms var(--ease-out); }
@keyframes drop-in { from { opacity: 0; transform: scale(.92); } }
@media (prefers-reduced-motion: reduce) { .ctx-menu.dropdown { animation: none; } }
.search-note { margin: 6px 4px 0; }

/* ---- review round 1 (2026-10-31) */
/* the reaction row inside the message menu: emoji centred in their buttons (the generic .sheet button padding pushed them off) */
.sheet .msg-react-bar button { width: 36px; height: 36px; padding: 0; display: grid; place-items: center; font-size: 20px; line-height: 1; border-radius: 50%; color: var(--fg); }
.sheet .msg-react-bar { gap: 2px; margin-bottom: 2px; }
.dialog.msg-pop { padding: 6px; }
.pop-preview { padding: 4px 8px 8px; }
.sheet button.pop-item { padding: 8px 10px; }
/* confirm beside the message it's about */
.dialog.confirm.anchored { margin: 0; inset: auto; position: fixed; width: 300px; }
.dialog.confirm.anchored::backdrop { background: rgb(0 0 0 / .12); backdrop-filter: none; }
.dialog.confirm button.danger { background: var(--danger); color: #fff; font-weight: 600; }
.dialog:focus { outline: none; }
/* dialogs without a title (a profile): the × floats, no empty header row on top */
.dialog { position: fixed; }
.dialog > header:has(> h2:empty) { position: absolute; top: 12px; right: 12px; margin: 0; min-height: 0; z-index: 1; }
/* settings: me + a gear at the foot of the chat list (like Discord / Slack) */
.sidebar > button.me { display: flex; align-items: center; gap: 10px; flex: none; margin: 0 8px calc(8px + env(safe-area-inset-bottom)); padding: 8px 10px; border-radius: 12px; background: none; border-top: 1px solid var(--line); text-align: left; color: var(--fg); }
.sidebar > button.me:hover { background: var(--fill); }
.me-text { flex: 1; min-width: 0; display: grid; }
.me-text small { font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sidebar > button.me > .ico { width: 20px; height: 20px; color: var(--muted); flex: none; }
.sidebar > button.me:hover > .ico { color: var(--accent); }
.settings-who { padding-left: 2px; }
/* group header: faces, online first */
.member-line { display: flex; align-items: center; gap: 6px; }
.face-stack { display: inline-flex; }
.face-stack .face { position: relative; margin-left: -6px; border-radius: 50%; box-shadow: 0 0 0 2px var(--bg); }
.face-stack .face:first-child { margin-left: 0; }
.face-stack .avatar.xs { width: 18px; height: 18px; font-size: 9px; }
.face-stack .face.online::after, .face-stack .face.away::after { content: ""; position: absolute; right: -1px; bottom: -1px; width: 6px; height: 6px; border-radius: 50%; background: var(--ok); box-shadow: 0 0 0 1.5px var(--bg); }
.face-stack .face.away::after { background: #ff9f0a; }
.face-stack .face.off { opacity: .55; }
/* new chat: room between the group name and the button */
.dialog .field + .row { margin-top: 12px; }
/* forward: send is an arrow */
.forward-send button.icon-send { width: 40px; height: 40px; padding: 0; border-radius: 50%; display: grid; place-items: center; }
.forward-send button.icon-send .ico { width: 20px; height: 20px; }
/* theme on a phone: a bottom sheet over the live chat */
@media (max-width: 767px) {
	.dialog.theme-pick.theme-pick { inset: auto 0 0 0; margin: 0; width: 100%; max-width: none; height: auto; max-height: 58dvh; border-radius: 18px 18px 0 0; overflow-y: auto; padding-bottom: calc(18px + env(safe-area-inset-bottom)); }
	.dialog.theme-pick::backdrop { background: transparent; backdrop-filter: none; }
}
/* sign-up: decoy and panic each switch on separately */
fieldset.extra { gap: 10px; }
.extra-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; cursor: pointer; }
.extra-head > span { display: grid; gap: 2px; }
.extra-head small { font-size: 13px; }
.extra-fields { display: grid; gap: 8px; }
input.switch { appearance: none; -webkit-appearance: none; flex: none; width: 46px; height: 28px; margin: 0; border-radius: 14px; background: var(--fill-2); position: relative; cursor: pointer; transition: background .2s; border: 0; padding: 0; }
input.switch::before { content: ""; position: absolute; top: 3px; left: 3px; width: 22px; height: 22px; border-radius: 50%; background: #fff; box-shadow: 0 1px 3px rgb(0 0 0 / .25); transition: transform .2s var(--ease-out); }
input.switch:checked { background: var(--ok); }
input.switch:checked::before { transform: translateX(18px); }
input.switch:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { input.switch, input.switch::before { transition: none; } }
.extra-fields[hidden] { display: none; }
/* password boxes: an eye inside the box; live hints under a new password */
.pw-wrap { position: relative; display: block; }
.pw-wrap > input { width: 100%; padding-right: 44px; }
.pw-eye { position: absolute; right: 4px; top: 50%; transform: translateY(-50%); width: 36px; height: 36px; padding: 0; border: 0; border-radius: 8px; background: none; color: var(--muted); display: grid; place-items: center; cursor: pointer; }
.pw-eye .ico { width: 18px; height: 18px; }
.pw-eye:hover, .pw-eye.on { color: var(--accent); }
.pw-hint { display: block; min-height: 1.3em; font-size: 13px; color: var(--danger); }
.pw-hint.ok { color: var(--ok); }
.pw-hint:empty { min-height: 0; }
