:root {
  /* Editor-inspired dark palette: a blue-cast slate, not flat black, with a
     small "syntax role" accent system instead of one flat brand color -
     each role borrows a real syntax-highlighting hue so the UI reads the
     way colored code does: violet = your own voice/primary actions, blue =
     links/functions, green = added/online/success, amber = pending/warn,
     red = removed/errors. */
  --bg-deepest: #0d1117;
  --bg-sidebar: #10151c;
  --bg-panel: #161b22;
  --bg-panel-alt: #1c2128;
  --bg-input: #0d1117;
  --line: #2b3038;
  --text: #c9d1d9;
  --text-muted: #8b949e;
  --text-faint: #6e7681;
  --accent: #c297ff;
  --accent-hover: #a879f0;
  --success: #56d364;
  --danger: #f85149;
  --danger-hover: #c93c33;
  --warning: #e3b341;
  --tok-function: #79c0ff;
  --on-accent: #0d1117;
  --font-display: 'Space Mono', monospace;
  --font-body: 'JetBrains Mono', monospace;
  --font-mono: 'JetBrains Mono', monospace;
}

/* Light theme - toggled via [data-theme="light"] on <html>, see the
   Preferences section of the profile/settings modal. */
:root[data-theme="light"] {
  --bg-deepest: #f6f8fa;
  --bg-sidebar: #ffffff;
  --bg-panel: #ffffff;
  --bg-panel-alt: #eaeef2;
  --bg-input: #ffffff;
  --line: #d0d7de;
  --text: #1f2328;
  --text-muted: #57606a;
  --text-faint: #8c959f;
  --accent: #8250df;
  --accent-hover: #6639ba;
  --success: #1a7f37;
  --danger: #cf222e;
  --danger-hover: #a40e26;
  --warning: #9a6700;
  --tok-function: #0969da;
  --on-accent: #ffffff;
}

* { box-sizing: border-box; }

/* Author CSS rules with their own `display` (grid/flex) always win over the
   browser's built-in `[hidden] { display: none }`, regardless of specificity.
   This override keeps `hidden` authoritative everywhere in the app. */
[hidden] { display: none !important; }

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
  overflow: hidden;
  background: var(--bg-deepest);
  color: var(--text);
  font-family: var(--font-body);
}

button, input, textarea {
  font-family: inherit;
  font-size: 14px;
  color: var(--text);
}

button { cursor: pointer; }

/* Splash screen - shown while the app checks auth / loads initial data */
.splash-screen {
  position: fixed;
  inset: 0;
  z-index: 500;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg-deepest);
  transition: opacity 0.25s ease;
}
.splash-screen.fade-out { opacity: 0; pointer-events: none; }
.splash-inner { display: flex; flex-direction: column; align-items: center; gap: 14px; }
.splash-logo {
  width: 40px;
  height: 40px;
  color: var(--accent);
  stroke-width: 1.3;
}
.splash-title {
  font-family: var(--font-display);
  font-size: 20px;
  font-weight: 700;
  color: var(--text);
  letter-spacing: 0.01em;
}
.splash-spinner { display: flex; gap: 5px; margin-top: 4px; }
.splash-spinner span {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent);
  animation: splash-bounce 1.1s infinite ease-in-out;
}
.splash-spinner span:nth-child(2) { animation-delay: 0.12s; }
.splash-spinner span:nth-child(3) { animation-delay: 0.24s; }
@keyframes splash-bounce {
  0%, 60%, 100% { transform: translateY(0); opacity: 0.4; }
  30% { transform: translateY(-5px); opacity: 1; }
}
.splash-error-icon { color: var(--danger); }
.splash-error-text {
  max-width: 280px;
  text-align: center;
  color: var(--text-muted);
  font-size: 14px;
  margin: 0;
}

button:focus-visible, input:focus-visible, textarea:focus-visible, .rail-btn:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}

.screen { height: 100vh; width: 100%; }

/* ---------- LOGIN ---------- */
#login-screen, #unlock-screen {
  display: flex;
  align-items: center;
  justify-content: center;
  background:
    linear-gradient(var(--line) 1px, transparent 1px) 0 0 / 100% 34px,
    radial-gradient(circle at 15% 10%, rgba(194,151,255,0.10), transparent 45%),
    var(--bg-deepest);
}

.login-card {
  width: min(380px, 90vw);
  background: var(--bg-sidebar);
  border-radius: 3px;
  padding: 40px 36px;
  text-align: center;
  box-shadow: 0 30px 80px rgba(0,0,0,0.6);
  border: 1px solid var(--line);
  border-top: 2px solid var(--accent);
}

.login-seal {
  width: 56px;
  height: 56px;
  margin: 0 auto 18px;
  border-radius: 3px;
  background: var(--bg-panel-alt);
  border: 1px solid var(--line);
  color: var(--accent);
  font-family: var(--font-display);
  font-size: 20px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
}

.wordmark {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 24px;
  letter-spacing: 0;
  margin: 0 0 6px;
}
.wordmark::before { content: '$ '; color: var(--text-faint); }
.wordmark::after {
  content: '_';
  color: var(--accent);
  animation: cursor-blink 1.1s step-start infinite;
}
.wordmark .accent-x { color: var(--accent); }
@keyframes cursor-blink { 50% { opacity: 0; } }

.login-sub {
  color: var(--text-muted);
  font-size: 12.5px;
  margin: 0 0 28px;
  line-height: 1.5;
}

#login-form, #unlock-form { text-align: left; display: flex; flex-direction: column; gap: 16px; }

label {
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
}
label > span:first-child::before { content: '// '; color: var(--text-faint); font-weight: 400; }

label em {
  font-style: normal;
  text-transform: none;
  letter-spacing: 0;
  color: var(--text-faint);
  font-size: 11px;
  font-weight: 400;
}

input[type="text"], input[type="password"], textarea {
  border: 1px solid var(--line);
  border-radius: 2px;
  padding: 10px 12px;
  background: var(--bg-deepest);
  color: var(--text);
  font-size: 14px;
  font-family: var(--font-mono);
}
input::placeholder, textarea::placeholder { color: var(--text-faint); }

/* Show/hide toggle - wraps every password input (see setupPasswordToggles
   in app.js). The button is visually inside the field, so the input needs
   right padding to keep typed text from running under it. */
.password-field { position: relative; width: 100%; }
.password-field input[type="password"], .password-field input[type="text"] {
  width: 100%;
  padding-right: 38px;
}
.password-toggle-btn {
  position: absolute;
  right: 4px;
  top: 50%;
  transform: translateY(-50%);
  background: none;
  border: none;
  padding: 6px;
  border-radius: 2px;
  color: var(--text-faint);
  display: flex;
  align-items: center;
  justify-content: center;
}
.password-toggle-btn:hover { color: var(--accent); background: var(--bg-panel-alt); }
.password-toggle-btn .icon { width: 16px; height: 16px; margin: 0; }
/* Edge/IE draw their own built-in reveal ("eye") and clear buttons inside
   password fields, sitting in the exact same spot as the custom toggle
   above and fighting it for clicks. We already give people a show/hide
   control, so switch the browser's off everywhere. */
.password-field input[type="password"]::-ms-reveal,
.password-field input[type="password"]::-ms-clear {
  display: none;
}

#settings-bio { resize: none; max-height: 96px; overflow-y: auto; }

/* Scoped to [type="submit"] specifically - these forms also contain the
   dynamically-inserted .password-toggle-btn (see setupPasswordToggles in
   app.js), and a bare "button" selector here would out-specificity the
   toggle's own class rule and paint it accent-colored too. */
#login-form button[type="submit"], #unlock-form button[type="submit"], .stacked-form button[type="submit"], #profile-form button[type="submit"], #group-form button[type="submit"] {
  background: var(--accent);
  color: var(--on-accent);
  border: none;
  padding: 11px 14px;
  border-radius: 2px;
  font-weight: 700;
  font-family: var(--font-mono);
  letter-spacing: 0.02em;
  transition: background 0.15s ease;
}
#login-form button[type="submit"]:hover, #unlock-form button[type="submit"]:hover, .stacked-form button[type="submit"]:hover, #profile-form button[type="submit"]:hover, #group-form button[type="submit"]:hover {
  background: var(--accent-hover);
}

.login-error {
  color: var(--danger);
  font-size: 13px;
  min-height: 18px;
  margin: 0;
}
.login-error:not(:empty)::before { content: '! '; }

.login-footnote {
  margin-top: 26px;
  font-size: 11px;
  color: var(--text-faint);
  border-top: 1px dashed var(--line);
  padding-top: 16px;
}

/* Shared `[ ]` / `[x]` bracket checkbox, used app-wide: the remember-device
   toggle below, and the group/forward member pickers further down (see
   .picker-checkbox). One visual language for every checkbox instead of a
   mix of native OS checkboxes and a bordered-square-with-checkmark style -
   the real <input type="checkbox"> stays in the document for click/
   keyboard/focus behavior, just visually hidden, with this sibling span
   standing in for it. Generic on `input[type="checkbox"]`, not scoped to
   any particular container, so any future checkbox picks this up for free
   just by following the same input+.checkbox-box markup pattern. */
.checkbox-box {
  font-family: var(--font-mono);
  font-weight: 700;
  color: var(--text-faint);
  letter-spacing: -1px;
  white-space: nowrap;
  display: inline-block;
}
.checkbox-box::before { content: '[ ]'; }
input[type="checkbox"]:checked + .checkbox-box { color: var(--accent); }
input[type="checkbox"]:checked + .checkbox-box::before { content: '[x]'; }
input[type="checkbox"]:focus-visible + .checkbox-box {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.remember-device-row {
  position: relative;
  flex-direction: row !important;
  align-items: center;
  gap: 7px;
  font-size: 13px;
  color: var(--text-faint);
  cursor: pointer;
}
.remember-device-row input.terminal-checkbox {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
  margin: 0;
}
.remember-device-row:hover .checkbox-box { color: var(--accent); }
.remember-device-row span:not(.checkbox-box) { font-weight: 400; }

.link-btn {
  background: none;
  border: none;
  color: var(--text-faint);
  font-size: 12px;
  margin-top: 18px;
  text-decoration: underline;
}
.link-btn:hover { color: var(--accent); }

/* ---------- APP SHELL ---------- */
.app-shell {
  display: grid;
  grid-template-columns: 88px 300px 1fr;
  height: 100vh;
}

.rail {
  background: var(--bg-deepest);
  color: var(--text);
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 18px 8px;
}

.rail-top { text-align: center; margin-bottom: 22px; }

.seal {
  width: 48px;
  height: 48px;
  border-radius: 3px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 18px;
  color: var(--on-accent);
  background: var(--accent);
  margin: 0 auto 8px;
  flex-shrink: 0;
}

.seal-self { background: var(--bg-sidebar); border: 2px solid var(--accent); color: var(--text); }

.rail-name {
  font-size: 12px;
  font-weight: 600;
  max-width: 78px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.rail-id {
  font-family: var(--font-mono);
  font-size: 9px;
  color: var(--text-faint);
  margin-top: 3px;
  max-width: 78px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.rail-nav {
  display: flex;
  flex-direction: column;
  gap: 6px;
  width: 100%;
  flex: 1;
}

.rail-btn {
  background: none;
  border: none;
  color: var(--text-muted);
  padding: 10px 4px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.02em;
  border-radius: 2px;
  text-align: center;
}
.rail-btn:hover { background: var(--bg-sidebar); color: var(--text); }
.rail-btn.active { background: rgba(194,151,255,0.18); color: var(--accent); }

.rail-logout {
  background: none;
  border: 1px solid var(--line);
  color: var(--text-muted);
  padding: 8px 6px;
  font-size: 11px;
  font-weight: 600;
  border-radius: 2px;
  width: 100%;
}
.rail-logout:hover { border-color: var(--danger); color: var(--danger); }

/* ---------- LIST PANEL ---------- */
.list-panel {
  background: var(--bg-sidebar);
  border-right: 1px solid var(--line);
  overflow-y: auto;
}

.panel { padding: 20px; }

.panel-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 14px;
}

.panel-header h2 {
  font-family: var(--font-display);
  font-size: 18px;
  font-weight: 700;
  margin: 0;
}

.icon-btn {
  background: var(--bg-panel-alt);
  border: 1px solid var(--line);
  border-radius: 2px;
  padding: 6px 10px;
  font-size: 12px;
  font-weight: 600;
  color: var(--text);
}
.icon-btn:hover { border-color: var(--accent); color: var(--accent); }

.sub-heading {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-faint);
  margin: 22px 0 10px;
}

.conv-list, .request-list { display: flex; flex-direction: column; gap: 4px; }

.conv-item {
  display: flex;
  gap: 12px;
  align-items: center;
  padding: 11px 10px;
  border-radius: 2px;
  cursor: pointer;
  border: 1px solid transparent;
}
.conv-item:hover { background: rgba(255,255,255,0.04); }
.conv-item.active { background: var(--bg-panel); }

.conv-meta { flex: 1; min-width: 0; }

.conv-name { font-weight: 700; font-size: 15px; display: flex; align-items: center; gap: 6px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.conv-tag {
  font-size: 9px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--accent);
  border: 1px solid var(--accent);
  border-radius: 2px;
  padding: 1px 5px;
}
.conv-tag.closed { color: var(--danger); border-color: var(--danger); }

.conv-preview {
  font-size: 12px;
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.conv-preview.disabled-note { color: var(--danger); font-style: italic; }

.request-item, .admin-user-item, .friend-picker-item {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 10px;
  background: var(--bg-panel);
  border: 1px solid var(--line);
  border-radius: 2px;
}
.admin-user-item, .friend-picker-item { flex-direction: row; align-items: center; gap: 10px; }

.request-item-row { display: flex; align-items: center; gap: 10px; }
.request-actions { display: flex; flex-wrap: wrap; gap: 6px; padding-left: 52px; }

.request-item .who, .admin-user-item .who { flex: 1; font-size: 13px; min-width: 0; }
.request-item .who span.name-link, .admin-user-item .who span.name-link { cursor: pointer; font-weight: 600; }
.request-item .who span.name-link:hover { text-decoration: underline; color: var(--accent); }
.request-item .username-tag, .admin-user-item .username-tag {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-faint);
  display: block;
}

.mini-btn {
  border: 1px solid var(--line);
  background: var(--bg-panel-alt);
  color: var(--text);
  border-radius: 2px;
  padding: 5px 9px;
  font-size: 12px;
  font-weight: 600;
  white-space: nowrap;
}
.mini-btn.accept { border-color: var(--success); color: var(--success); }
.mini-btn.accept:hover { background: var(--success); color: var(--on-accent); }
.mini-btn.decline, .mini-btn.remove, .mini-btn.deactivate, .mini-btn.block {
  border-color: var(--danger); color: var(--danger);
}
.mini-btn.decline:hover, .mini-btn.remove:hover, .mini-btn.deactivate:hover, .mini-btn.block:hover {
  background: var(--danger); color: #fff;
}
.mini-btn.unblock { border-color: var(--warning); color: var(--warning); }
.mini-btn.unblock:hover { background: var(--warning); color: var(--on-accent); }
.mini-btn.primary { border-color: var(--accent); color: var(--accent); }
.mini-btn.primary:hover { background: var(--accent); color: var(--on-accent); }

.inline-form { display: flex; gap: 8px; margin-bottom: 4px; }
.inline-form input { flex: 1; }
.inline-form button {
  background: var(--accent);
  color: var(--on-accent);
  border: none;
  border-radius: 2px;
  padding: 0 14px;
  font-weight: 600;
}
.inline-form button:hover { background: var(--accent-hover); }

.hint { font-size: 12px; color: var(--text-muted); min-height: 16px; }

.stacked-form { display: flex; flex-direction: column; gap: 12px; margin-bottom: 10px; }

/* ---------- THREAD ---------- */
.thread-panel { display: flex; flex-direction: column; background: var(--bg-panel); overflow: hidden; height: 100%; }

.thread-empty {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-faint);
  font-family: var(--font-display);
  font-size: 16px;
  font-weight: 600;
}

.thread { display: flex; flex-direction: column; height: 100%; position: relative; overflow: hidden; }

.thread-drop-overlay {
  position: absolute;
  inset: 0;
  z-index: 30;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(13,17,23,0.85);
  border: 2px dashed var(--accent);
  margin: 8px;
  border-radius: 3px;
  pointer-events: none;
}
.thread-drop-overlay-inner { display: flex; flex-direction: column; align-items: center; gap: 8px; color: var(--accent); }
.thread-drop-overlay-inner .icon { width: 32px; height: 32px; margin: 0; }
.thread-drop-overlay-inner p { font-family: var(--font-mono); font-weight: 700; margin: 0; }

.thread-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 16px 26px;
  border-bottom: 1px solid var(--line);
  position: relative;
}
.thread-header-actions { display: flex; align-items: center; gap: 6px; flex-shrink: 0; }

.thread-header h2 {
  font-family: var(--font-display);
  font-size: 17px;
  font-weight: 700;
  margin: 0;
  cursor: pointer;
}
.thread-header h2:hover { color: var(--accent); }
.thread-sub { font-size: 12px; color: var(--text-muted); margin: 2px 0 0; display: flex; align-items: center; gap: 6px; }

/* In-conversation message search - overlays the top of the message list */
.message-search-panel {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  z-index: 30;
  background: var(--bg-sidebar);
  border-bottom: 1px solid var(--line);
  box-shadow: 0 12px 24px rgba(0,0,0,0.35);
  max-height: min(60vh, 420px);
  display: flex;
  flex-direction: column;
}
.message-search-input-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 18px;
  border-bottom: 1px solid var(--line);
  flex-shrink: 0;
}
.message-search-input-row .icon { margin: 0; color: var(--text-faint); }
.message-search-input-row input {
  flex: 1;
  border: none;
  background: none;
  font-size: 14px;
  padding: 4px 0;
}
.message-search-input-row button {
  background: none; border: none; color: var(--text-faint); padding: 4px; border-radius: 2px;
}
.message-search-input-row button:hover { color: var(--danger); background: var(--bg-panel-alt); }
.message-search-results { overflow-y: auto; padding: 8px; }
.message-search-results .hint { padding: 14px 10px; text-align: center; }
.message-search-result {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  border-radius: 2px;
  padding: 8px 10px;
}
.message-search-result:hover { background: var(--bg-panel-alt); }
.message-search-result .msr-head { display: flex; align-items: baseline; gap: 8px; width: 100%; }
.msr-sender { font-size: 12px; font-weight: 700; color: var(--text); }
.msr-time { font-size: 10px; color: var(--text-faint); font-family: var(--font-mono); margin-left: auto; }
.msr-body { font-size: 13px; color: var(--text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%; }
.msr-body mark { background: rgba(194,151,255,0.35); color: var(--text); border-radius: 2px; }

/* "+" attach menu (Photo/Video vs File) */
.attach-menu {
  position: absolute;
  bottom: 56px;
  background: var(--bg-sidebar);
  border: 1px solid var(--line);
  border-radius: 2px;
  padding: 6px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 170px;
  box-shadow: 0 12px 32px rgba(0,0,0,0.5);
  z-index: 40;
}
.attach-menu button {
  display: flex;
  align-items: center;
  background: none;
  border: none;
  color: var(--text);
  font-size: 13px;
  font-weight: 600;
  padding: 9px 10px;
  border-radius: 2px;
  text-align: left;
}
.attach-menu button:hover { background: var(--bg-panel-alt); color: var(--accent); }

/* Search box in the conversation/friends list panels */
.panel-search-row {
  display: flex;
  align-items: center;
  gap: 8px;
  background: var(--bg-panel);
  border: 1px solid var(--line);
  border-radius: 2px;
  padding: 8px 12px;
  margin-bottom: 12px;
}
.panel-search-row .icon { margin: 0; color: var(--text-faint); flex-shrink: 0; }
.panel-search-row input {
  flex: 1;
  border: none;
  background: none;
  font-size: 13px;
  padding: 2px 0;
}

.message-list {
  flex: 1;
  overflow-y: auto;
  padding: 20px 26px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  position: relative;
}

.message-row { display: flex; flex-direction: column; max-width: 62%; animation: fade-in 0.15s ease; position: relative; touch-action: pan-y; }

/* Long-press on a message opens our own context menu - the browser's own
   text-selection/callout ("Copy", the blue highlight, the magnifier loupe)
   must stay out of the way or it visibly fights with it. Scoped via the
   .touch-device class (set in app.js on the first real touchstart) rather
   than a (hover: none)/(pointer: coarse) media query, since that
   detection isn't reliable across every WebView/OEM browser - this way
   desktop mouse users keep normal text selection, and it's driven by an
   actual touch happening rather than a guess about the device. */
/* Kills the blue selection highlight / "Copy" callout / tap-highlight
   flash on long-press or tap, everywhere in the app - not just messages,
   since it was showing up on buttons and other UI too. The only things
   that still need real text selection are actual typing fields, which
   are explicitly opted back in below. */
.touch-device *:not(input):not(textarea) {
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
  -webkit-tap-highlight-color: transparent;
}
.touch-device input,
.touch-device textarea {
  -webkit-tap-highlight-color: transparent;
}

/* Reply icon revealed behind a message as it's dragged right on touch. */
.swipe-reply-hint {
  position: absolute;
  left: -34px;
  top: 50%;
  transform: translateY(-50%);
  width: 26px;
  height: 26px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 2px;
  background: var(--bg-panel-alt);
  color: var(--accent);
  opacity: 0;
  pointer-events: none;
}
.message-row.own { align-self: flex-end; align-items: flex-end; }
.message-row.theirs { align-self: flex-start; align-items: flex-start; }

@keyframes fade-in { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }

.message-sender {
  font-size: 13px;
  font-weight: 700;
  color: var(--text-muted);
  margin-bottom: 3px;
  cursor: pointer;
}
.message-sender:hover { color: var(--accent); text-decoration: underline; }

/* Stacked/grouped messages: minimal gap between rows, with a bit of extra
   breathing room restored right before a new group (different sender, or
   a >=20min gap) starts. Continuation rows in a run have no avatar/name. */
.message-list { gap: 1px; }
.message-row.own:not(.grouped),
.message-row.theirs:not(.grouped) {
  margin-top: 8px;
}
.message-list > .message-row.own:first-child,
.message-list > .message-row.theirs:first-child,
.message-list > .day-divider:first-child + .message-row {
  margin-top: 0;
}
.message-avatar-spacer { visibility: hidden; }
.day-divider { margin: 12px 0; }
.message-row.system { margin: 8px 0; }

/* Timestamp + edited/delivery tag, tucked into the message's own
   bottom-right corner (WhatsApp-style) instead of sitting on its own line. */
.message-footer {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 10.5px;
  white-space: nowrap;
  vertical-align: bottom;
}

/* Text bubbles: float the footer at the end of the text so it rides on the
   last line when there's room, and tucks under the trailing words when
   there isn't - the classic corner-anchored timestamp. */
.message-bubble { overflow: hidden; }
.message-footer.float-right {
  float: right;
  margin: 2px 0 -2px 8px;
  color: var(--text-faint);
}

/* Media (images/gifs): a small translucent chip pinned to the corner of the
   image itself, like a photo timestamp. */
.message-media-wrap { position: relative; display: inline-block; line-height: 0; }
.message-footer.corner-overlay {
  position: absolute;
  right: 6px;
  bottom: 6px;
  background: rgba(0,0,0,0.5);
  color: rgba(255,255,255,0.92);
  padding: 2px 7px;
  border-radius: 2px;
}
.message-footer.corner-overlay .delivery-tag { color: rgba(255,255,255,0.75); }
.message-footer.corner-overlay .delivery-tag.seen { color: #fff; }

/* Video/file attachments: controls already occupy the media's own corner,
   so the footer sits just underneath instead of overlapping them. */
.message-footer.standalone {
  display: flex;
  justify-content: flex-end;
  width: 100%;
  margin-top: 3px;
  color: var(--text-faint);
}

/* Icons - inline SVGs (currentColor line icons), not emoji glyphs, so
   they render identically everywhere and follow the theme/hover color. */
.icon {
  width: 17px;
  height: 17px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
  vertical-align: -3px;
  flex-shrink: 0;
  margin-right: 5px;
  /* These are unfilled line icons (fill: none) - without this, a click
     landing on the empty space *inside* the icon's own box (anywhere that
     isn't exactly on top of a drawn stroke) doesn't count as a hit on the
     SVG, so it can fall through to whatever's actually behind that pixel
     instead of registering on the button. Buttons could end up feeling
     like only part of them was clickable, depending on exactly where the
     icon's strokes fall inside the box. This makes the SVG fully
     transparent to clicks so they always resolve to the parent <button>,
     across its whole area, regardless of where the icon's linework sits. */
  pointer-events: none;
}
.icon.icon-filled { fill: currentColor; stroke: none; }
.icon.icon-solo { margin-right: 0; }
.icon-lg { width: 21px; height: 21px; }

/* Messages read like log/diff lines rather than chat bubbles: a flat panel
   with a colored rail (your voice = violet "added" rail, theirs = blue
   "function" rail) and a prompt glyph, instead of a filled speech-bubble
   shape. Both sides use the same flat panel now (not a solid accent fill
   for "own") - the rail color plus the `$`/`>` prefix is what distinguishes
   them, so message text stays readable at the same contrast everywhere. */
.message-bubble {
  padding: 9px 13px 9px 15px;
  border-radius: 2px;
  font-size: 15px;
  line-height: 1.48;
  position: relative;
  word-wrap: break-word;
  border-left-width: 3px;
  border-left-style: solid;
}
.message-row.own .message-bubble { background: var(--bg-panel-alt); border: 1px solid var(--line); border-left: 3px solid var(--accent); }
.message-row.own .message-bubble::before { content: '$ '; color: var(--accent); font-weight: 700; }
.message-row.theirs .message-bubble { background: var(--bg-panel-alt); border: 1px solid var(--line); border-left: 3px solid var(--tok-function); }
.message-row.theirs .message-bubble::before { content: '> '; color: var(--tok-function); font-weight: 700; }
.message-row.deleted .message-bubble, .message-row.blocked .message-bubble {
  font-style: italic; color: var(--text-faint); background: transparent; border: 1px dashed var(--line); border-left-width: 1px;
}
.message-row.deleted .message-bubble::before, .message-row.blocked .message-bubble::before { content: '# '; color: var(--text-faint); }

.message-bubble strong { font-weight: 700; }
.message-bubble em { font-style: italic; }
.message-bubble del { text-decoration: line-through; opacity: 0.85; }
.message-bubble a { color: var(--tok-function); text-decoration: underline; word-break: break-all; }
.message-bubble a:hover { color: var(--accent); }
.message-bubble.undecryptable {
  font-style: italic; color: var(--text-faint); background: transparent; border: 1px dashed var(--line);
  display: flex; align-items: center; gap: 6px;
}
.message-bubble.undecryptable .icon { margin-right: 0; opacity: 0.7; }

/* Only the first bubble in a stack (the one the avatar sits next to) gets a
   tail - a squared-off corner on the avatar side, up top since the avatar
   is top-aligned. Every other bubble in the run, and every bubble once
   another sender/gap starts a new group, is fully rounded with no tail. */
.message-row.own:not(.grouped) .message-bubble,
.message-row.own:not(.grouped) .message-media-wrap img { border-top-right-radius: 2px; }
.message-row.theirs:not(.grouped) .message-bubble,
.message-row.theirs:not(.grouped) .message-media-wrap img { border-top-left-radius: 2px; }

.message-row.system {
  max-width: 100%;
  display: flex;
  justify-content: center;
  margin: 2px 0;
}
.system-message-text {
  font-size: 11px;
  font-weight: 600;
  color: var(--text-faint);
  background: var(--bg-panel-alt);
  padding: 4px 12px;
  border-radius: 2px;
  text-align: center;
}

.day-divider {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 4px 0;
  max-width: 100%;
}
.day-divider-line { flex: 1; height: 1px; background: var(--line); }
.day-divider-label {
  font-size: 11px;
  font-weight: 700;
  color: var(--text-faint);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  white-space: nowrap;
}
/* Same shape as the day divider, but flagged in the accent color and with
   a dashed rather than solid rule - a distinct, "new since last time"
   marker rather than just another date heading. */
.unread-divider .day-divider-line { height: 0; border-top: 1px dashed var(--accent); background: none; }
.unread-divider .day-divider-label { color: var(--accent); }

.message-edited-tag {
  color: var(--text-faint);
  font-size: 10px;
  font-weight: 600;
  cursor: default;
}

.delivery-tag {
  display: inline-flex;
  color: var(--text-faint);
}
.delivery-tag .icon { margin: 0; width: 13px; height: 13px; }
.delivery-tag.seen { color: var(--accent); }
.delivery-tag.sent, .delivery-tag.delivered { color: var(--text-faint); }

/* A message shown optimistically, before the server's confirmed it went
   through - slightly faded, with a clock instead of a delivery tick. */
.message-row.pending { opacity: 0.65; }

/* Inline edit-in-place (replaces the bubble's own content while editing) */
.message-bubble.editing { padding: 8px; }
.inline-edit-input {
  width: 100%;
  min-width: 160px;
  background: rgba(255,255,255,0.14);
  border: none;
  border-radius: 2px;
  color: inherit;
  font: inherit;
  font-size: 15.5px;
  padding: 7px 9px;
  resize: none;
  outline: none;
  font-family: inherit;
  line-height: inherit;
}
.inline-edit-actions { display: flex; gap: 6px; margin-top: 6px; justify-content: flex-end; }
.inline-edit-actions button {
  background: rgba(255,255,255,0.16);
  border: none;
  border-radius: 2px;
  color: inherit;
  padding: 5px 7px;
  display: flex;
  align-items: center;
}
.inline-edit-actions button:hover { background: rgba(255,255,255,0.32); }
.inline-edit-actions .inline-edit-cancel:hover { color: var(--danger); }

.message-forwarded-tag {
  font-size: 10px;
  color: var(--text-faint);
  font-style: italic;
  margin-bottom: 3px;
}
.message-row.own .message-forwarded-tag { text-align: right; }

.message-reply-quote {
  display: flex;
  flex-direction: column;
  gap: 1px;
  border-left: 3px solid var(--accent);
  padding: 3px 8px;
  margin-bottom: 5px;
  background: rgba(194,151,255,0.08);
  border-radius: 0 2px 2px 0;
  cursor: pointer;
  max-width: 100%;
}
.reply-quote-name { font-size: 11px; font-weight: 700; color: var(--accent); }
.reply-quote-body {
  font-size: 12px;
  color: var(--text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 320px;
}

@keyframes flash-highlight {
  0%, 100% { background: transparent; }
  30% { background: rgba(194,151,255,0.25); }
}
.message-row.flash-highlight .message-bubble,
.message-row.flash-highlight .message-attachment-img {
  animation: flash-highlight 1.2s ease;
  border-radius: 2px;
}

/* Reply-preview bar shown above the message input while composing a reply */
.reply-preview-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 14px 8px;
  padding: 7px 10px;
  background: var(--bg-panel-alt);
  border-left: 3px solid var(--accent);
  border-radius: 0 2px 2px 0;
  color: var(--accent);
}
.reply-preview-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 0; font-size: 12px; }
.reply-preview-text strong { font-size: 12px; color: var(--text); }
.reply-preview-text span { color: var(--text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.reply-preview-bar button {
  background: none; border: none; color: var(--text-faint); padding: 4px; border-radius: 2px; flex-shrink: 0;
}
.reply-preview-bar button:hover { color: var(--danger); background: var(--bg-panel); }

.queue-indicator {
  font-size: 11px;
  font-weight: 600;
  color: var(--text-faint);
  margin: 0 20px 4px;
  font-style: italic;
}

/* "X is typing..." strip above the message input */
.typing-indicator {
  font-size: 13px;
  font-weight: 700;
  color: var(--accent);
  font-style: normal;
  margin: 0 20px 8px;
  padding: 7px 13px;
  background: rgba(194,151,255,0.15);
  border: 1px solid rgba(194,151,255,0.4);
  border-radius: 2px;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  width: fit-content;
  max-width: calc(100% - 40px);
}
.typing-indicator span:first-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.typing-dots { display: inline-flex; gap: 3px; flex-shrink: 0; }
.typing-dots span {
  width: 6px; height: 6px; border-radius: 50%; background: var(--accent);
  animation: typing-bounce 1.2s infinite ease-in-out;
}
.typing-dots span:nth-child(2) { animation-delay: 0.15s; }
.typing-dots span:nth-child(3) { animation-delay: 0.3s; }
@keyframes typing-bounce {
  0%, 60%, 100% { transform: translateY(0); opacity: 0.5; }
  30% { transform: translateY(-4px); opacity: 1; }
}

.message-reactions {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 8px;
}
.reaction-pill {
  background: var(--bg-panel-alt);
  border: 1.5px solid var(--line);
  border-radius: 2px;
  padding: 5px 11px;
  font-size: 17px;
  line-height: 1;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--text);
}
.reaction-pill span { font-size: 13px; font-weight: 700; color: var(--text-muted); }
.reaction-pill:hover { border-color: var(--accent); transform: scale(1.06); }
.reaction-pill.mine { background: rgba(194,151,255,0.2); border-color: var(--accent); }
.reaction-pill.mine span { color: var(--accent); }

.reaction-popover {
  position: fixed;
  z-index: 210;
  background: var(--bg-sidebar);
  border: 1px solid var(--line);
  border-radius: 2px;
  padding: 6px 8px;
  display: flex;
  gap: 2px;
  box-shadow: 0 12px 32px rgba(0,0,0,0.5);
}
.reaction-popover button {
  background: none;
  border: none;
  font-size: 24px;
  padding: 6px;
  border-radius: 2px;
  line-height: 1;
}
.reaction-popover button:hover { background: var(--bg-panel-alt); transform: scale(1.15); }

/* Shared media / links / files panel */
.shared-media-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 6px;
  margin-top: 6px;
}
.shared-media-tile {
  position: relative;
  aspect-ratio: 1;
  border-radius: 2px;
  overflow: hidden;
  border: none;
  padding: 0;
  background: var(--bg-panel-alt);
  cursor: pointer;
}
.shared-media-tile img, .shared-media-tile video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.shared-media-play {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  background: rgba(0,0,0,0.25);
}
.shared-media-play .icon { margin: 0; width: 22px; height: 22px; }

.shared-file-list, .shared-link-list { display: flex; flex-direction: column; gap: 2px; margin-top: 6px; }
.shared-file-row, .shared-link-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px;
  border-radius: 2px;
  background: none;
  border: none;
  width: 100%;
  text-align: left;
  color: var(--text);
  text-decoration: none;
  cursor: pointer;
}
.shared-file-row:hover, .shared-link-row:hover { background: var(--bg-panel-alt); }
.shared-link-row { flex-direction: column; align-items: flex-start; gap: 2px; }
.shared-link-url { font-size: 13px; color: var(--accent); word-break: break-all; }
.shared-link-meta { font-size: 11px; color: var(--text-faint); }
#forward-modal-list .seal { width: 32px; height: 32px; font-size: 13px; flex-shrink: 0; }

.message-form {
  display: flex;
  gap: 10px;
  padding: 16px 26px;
  border-top: 1px solid var(--line);
  align-items: center;
}
.message-form input { flex: 1; }
.terminal-input-wrap {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  border: 1px solid var(--line);
  border-radius: 2px;
  background: var(--bg-input);
  padding-left: 12px;
}
.terminal-input-wrap:focus-within { border-color: var(--accent); }
.terminal-prompt { color: var(--accent); font-weight: 700; flex-shrink: 0; font-family: var(--font-mono); }
/* Live markdown preview while typing: .terminal-input-highlight mirrors
   #message-input's text with double-asterisk bold, single-asterisk italic,
   and tilde strikethrough actually styled, and the marker characters
   themselves dimmed, updated on every keystroke
   (see updateComposerHighlight in app.js) - not a contenteditable, on
   purpose. A real <input> can't have part of its text bold and part not,
   so instead the real input stays a completely normal, fully-functional
   text input (every existing draft/typing-indicator/cursor-position/emoji-
   insertion code keeps working untouched) with its own text made
   invisible, and this absolutely-positioned div sits on top showing the
   styled version - pointer-events: none so clicks/focus pass straight
   through to the real input beneath it. */
.terminal-input-stack { position: relative; flex: 1; min-width: 0; }
/* `input.terminal-input`, not just `.terminal-input`: the base rule this
   overrides is `input[type="text"]`, which - somewhat surprisingly -
   out-specificities a bare class selector (an attribute selector and a
   type selector together edge out one class selector). Losing that fight
   silently left color: transparent never actually applying, so the real
   input's own plain text stayed visible on top of the styled overlay
   underneath instead of the other way around. font-size: 16px matches
   #message-input's actual rendered size (see the iOS-zoom-prevention rule
   further down that forces every text input to 16px) - not the 14px most
   inputs in this app use - so the overlay's text lines up with the real
   (invisible) text and caret instead of running a different size/height. */
input.terminal-input {
  width: 100%;
  border: none;
  background: none;
  padding: 10px 16px 10px 8px;
  color: transparent;
  caret-color: var(--text);
  font-size: 16px;
  line-height: 1.4;
}
.terminal-input-highlight {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  overflow: hidden;
  white-space: pre;
  display: flex;
  align-items: center;
  font-family: var(--font-mono);
  font-size: 16px;
  line-height: 1.4;
  padding: 10px 16px 10px 8px;
  color: var(--text);
}
.terminal-input-highlight .md-marker { color: var(--text-faint); }
.terminal-input-highlight strong { font-weight: 700; }
.terminal-input-highlight em { font-style: italic; }
.terminal-input-highlight del { text-decoration: line-through; opacity: 0.85; }
.terminal-input:focus { outline: none; }
.terminal-input:disabled { opacity: 0.5; }
.terminal-input-wrap:has(.terminal-input:disabled) { opacity: 0.5; }
.message-form button[type="submit"] {
  background: var(--accent);
  color: var(--on-accent);
  border: none;
  border-radius: 2px;
  width: 42px;
  height: 42px;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.message-form button[type="submit"][hidden] { display: none; }
.message-form button[type="submit"] .icon { margin: 0; }
.message-form button:hover { background: var(--accent-hover); }
.message-form input:disabled { opacity: 0.5; }
.message-form button:disabled { opacity: 0.5; cursor: not-allowed; }

.scroll-to-bottom-btn {
  position: absolute;
  right: 26px;
  bottom: 84px;
  width: 42px;
  height: 42px;
  border-radius: 2px;
  background: var(--accent);
  color: var(--on-accent);
  border: none;
  box-shadow: 0 6px 18px rgba(0,0,0,0.45);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 5;
  transition: background 0.15s ease, transform 0.15s ease;
}
.scroll-to-bottom-btn:hover { background: var(--accent-hover); transform: translateY(-2px); }
.scroll-to-bottom-btn .icon { width: 18px; height: 18px; }

#thread-disabled-bar { background: rgba(248,81,73,0.08); }
#thread-disabled-bar span { color: var(--danger); }

/* ---------- MODALS ---------- */
.modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.6);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 50;
}
/* Always above every other modal, regardless of DOM order - the crop tool
   can be triggered from inside more than one modal (settings, group
   members), so relying on where it happens to sit in the document isn't
   reliable the way it is for backdrops that only ever open on their own. */
#avatar-crop-modal-backdrop { z-index: 60; }
.modal {
  display: flex;
  flex-direction: column;
  background: var(--bg-sidebar);
  border: 1px solid var(--line);
  border-radius: 2px;
  width: min(420px, 90vw);
  max-height: 80vh;
  overflow: hidden;
}
.modal.modal-wide { width: min(480px, 92vw); }
.modal h3 { font-family: var(--font-display); margin: 0; font-weight: 700; }
.modal-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 16px; }
.modal-actions .secondary {
  background: var(--bg-panel-alt); color: var(--text); border: 1px solid var(--line);
  padding: 10px 16px; border-radius: 2px; font-weight: 600;
}

/* Picture crop/position tool - shared by the avatar and group-icon upload
   flows (see openAvatarCropModal in app.js). A plain <canvas> instead of
   e.g. CSS transforms on the <img> itself, since the same drawImage() math
   used to render the live preview is reused unchanged to render the final
   output at full resolution when saving - one source of truth for the pan/
   zoom math instead of a separate "preview" and "export" implementation
   that could drift out of sync with each other. */
.avatar-crop-viewport-wrap {
  display: flex;
  justify-content: center;
  margin-bottom: 16px;
}
.avatar-crop-canvas {
  border-radius: 3px;
  border: 1px solid var(--line);
  background: var(--bg-deepest);
  touch-action: none; /* dragging pans the image, not the page */
  cursor: grab;
}
.avatar-crop-canvas:active { cursor: grabbing; }
.avatar-crop-zoom-row {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 10px;
}
.avatar-crop-zoom-btn {
  flex-shrink: 0;
  width: 30px;
  height: 30px;
  border-radius: 2px;
  border: 1px solid var(--line);
  background: var(--bg-panel-alt);
  color: var(--text);
  font-family: var(--font-mono);
  font-size: 17px;
  font-weight: 700;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
}
.avatar-crop-zoom-btn:hover { border-color: var(--accent); color: var(--accent); }
.avatar-crop-zoom-slider {
  flex: 1;
  accent-color: var(--accent);
}

/* The header is a real, separate flex item above the scrolling body - not
   position:sticky - so it can never have scrolled content peek out above
   or behind it. That did happen with position:sticky on some mobile
   WebViews (rendering/clipping quirks with sticky + border-radius +
   overflow together), so this avoids the whole bug class instead of
   chasing it. */
.modal-header {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 20px 28px 16px;
}
.modal-scroll-body {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: 0 28px 28px;
  min-height: 0;
}
.modal-close-x {
  flex-shrink: 0;
  width: 30px;
  height: 30px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 2px;
  background: var(--bg-panel-alt);
  color: var(--text-muted);
  border: none;
  cursor: pointer;
}
.modal-close-x:hover { background: var(--line); color: var(--text); }

/* Phone-width: modals behave like a native bottom sheet instead of a
   centered dialog - slides up from the bottom, full width, rounded only
   at the top, header pinned above a scrollable body. */
@media (max-width: 640px) {
  .modal-backdrop { align-items: flex-end; }
  .modal, .modal.modal-wide {
    width: 100%;
    max-width: 100%;
    max-height: 86vh;
    border-radius: 2px 2px 0 0;
    border-left: none;
    border-right: none;
    border-bottom: none;
    animation: sheet-up 0.18s ease;
  }
  .modal-header { padding: 16px 20px 16px; position: relative; }
  .modal-scroll-body { padding: 0 20px calc(20px + env(safe-area-inset-bottom)); }
  /* A small grab-handle bar, like a native bottom sheet, above the title. */
  .modal-header::before {
    content: '';
    position: absolute;
    top: 6px;
    left: 50%;
    transform: translateX(-50%);
    width: 36px;
    height: 4px;
    border-radius: 2px;
    background: var(--line);
  }
}
@keyframes sheet-up {
  from { transform: translateY(24px); opacity: 0; }
  to { transform: translateY(0); opacity: 1; }
}

/* Tabs inside a modal (Group Settings: Members / Permissions) */
.settings-tabs { display: flex; gap: 6px; border-bottom: 1px solid var(--line); margin-bottom: 16px; }
.settings-tab-btn {
  background: none; border: none; color: var(--text-muted); font-weight: 700; font-size: 13px;
  padding: 8px 4px 10px; border-bottom: 2px solid transparent; margin-bottom: -1px;
}
.settings-tab-btn:hover { color: var(--text); }
.settings-tab-btn.active { color: var(--accent); border-bottom-color: var(--accent); }

/* Members list - single row (avatar, name, one action button) since it's
   rarely more than that one button; the multi-row wrap pattern used
   elsewhere (friends list) is for rows with several buttons, not this. */
.member-list { display: flex; flex-direction: column; gap: 6px; margin: 10px 0; }
.member-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  background: var(--bg-panel);
  border: 1px solid var(--line);
  border-radius: 2px;
}
.member-row .who { flex: 1; min-width: 0; }
.member-row .who .name-link { font-weight: 700; font-size: 13px; cursor: pointer; }
.member-row .who .name-link:hover { color: var(--accent); text-decoration: underline; }
.member-row .username-tag { display: block; font-size: 11px; color: var(--text-faint); font-family: var(--font-mono); }
.member-row .role-tag {
  font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em;
  color: var(--text-faint); background: var(--bg-panel-alt); border-radius: 2px; padding: 2px 6px; margin-left: 6px;
}
.member-row .mini-btn { flex-shrink: 0; }
.member-row-actions { display: flex; gap: 6px; flex-shrink: 0; }

.permission-row { margin-bottom: 14px; }
.permission-row label { display: block; font-size: 12px; font-weight: 600; color: var(--text-muted); margin-bottom: 6px; }
.permission-row select {
  width: 100%;
  background: var(--bg-panel);
  border: 1px solid var(--line);
  color: var(--text);
  border-radius: 2px;
  padding: 8px 10px;
  font-size: 13px;
}

.settings-section { border-top: 1px solid var(--line); margin-top: 18px; padding-top: 18px; }
.settings-section h4 { font-family: var(--font-display); font-size: 14px; margin: 0 0 12px; }

.friend-picker { display: flex; flex-direction: column; gap: 6px; margin: 10px 0 4px; max-height: 260px; overflow-y: auto; }

.friend-picker-item.selected { border-color: var(--accent); background: rgba(194,151,255,0.1); }
#group-friend-picker .friend-picker-item { cursor: pointer; }
#group-friend-picker .friend-picker-item:hover { border-color: var(--accent); }
.friend-picker-item .who { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.friend-picker-item .who .name { font-weight: 700; font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.friend-picker-item .username-tag {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-faint);
}


/* Custom-styled checkbox (native input hidden but still focusable/clickable).
   Width is wider than the glyph looks like it needs at a glance - "[ ]"/"[x]"
   in monospace at 15px runs close to 30px, and a narrower box was clipping/
   overlapping it against the row's avatar. */
.picker-checkbox {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 32px;
  height: 20px;
  cursor: pointer;
}
.picker-checkbox input {
  position: absolute;
  inset: 0;
  opacity: 0;
  margin: 0;
  cursor: pointer;
}
.picker-checkbox .checkbox-box { font-size: 15px; pointer-events: none; }

/* ---------- PROFILE VIEW MODAL ---------- */
.profile-view-header { display: flex; align-items: center; gap: 14px; margin-bottom: 8px; }
.profile-view-header .seal { width: 68px; height: 68px; font-size: 24px; margin: 0; }
.profile-view-name { font-family: var(--font-display); font-size: 18px; font-weight: 700; }
.profile-view-username { font-size: 13px; color: var(--text-muted); }
.profile-view-id {
  font-family: var(--font-mono); font-size: 11px; color: var(--text-faint);
  margin-top: 18px; padding-top: 12px; border-top: 1px solid var(--line);
}
.profile-view-bio-label { font-family: var(--font-display); font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-faint); margin: 16px 0 6px; }
.profile-view-bio { font-size: 13px; color: var(--text); margin: 0 0 14px; white-space: pre-wrap; }
.profile-view-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 16px; }
.profile-view-status {
  font-size: 12px; font-weight: 600; padding: 6px 10px; border-radius: 2px;
  background: rgba(255,255,255,0.06); color: var(--text-muted); margin-top: 10px;
}
.profile-view-status.blocked { background: rgba(248,81,73,0.12); color: var(--danger); }

/* ============================================================
   v2 additions: no more slim rail; wider list panel with a
   header tab-strip + bottom profile bar; generic modal system;
   notification badges; avatars; timestamps; attachments.
   ============================================================ */

.app-shell {
  grid-template-columns: 480px 1fr;
}

.list-panel {
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.list-panel-scroll {
  flex: 1;
  overflow-y: auto;
  padding: 18px 18px 8px;
}

.panel-header-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 10px;
}

.panel-header-row h2 {
  font-family: var(--font-display);
  font-size: 19px;
  font-weight: 700;
  margin: 0;
  display: flex;
  align-items: center;
}

.panel-header-actions {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 14px;
}

.tab-btn {
  position: relative;
  background: var(--bg-panel-alt);
  border: 1px solid var(--line);
  color: var(--text);
  border-radius: 2px;
  padding: 6px 10px;
  font-size: 12px;
  font-weight: 600;
}
.tab-btn:hover { border-color: var(--accent); color: var(--accent); }
.tab-btn.active { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }

.panel-header-actions .tab-btn,
.panel-header-actions .icon-btn {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 9px 10px;
}

.tab-badge {
  position: absolute;
  top: -5px;
  right: -5px;
  background: var(--danger);
  color: #fff;
  font-size: 9px;
  font-weight: 700;
  min-width: 15px;
  height: 15px;
  border-radius: 2px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 3px;
  border: 2px solid var(--bg-sidebar);
}

.panel-view[hidden] { display: none; }

/* Bottom fixed profile bar */
.profile-bar {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 14px;
  border-top: 1px solid var(--line);
  background: var(--bg-deepest);
  border-left: none; border-right: none; border-bottom: none;
  width: 100%;
  text-align: left;
  cursor: pointer;
}
.profile-bar:hover { background: var(--bg-panel-alt); }
.profile-bar-meta { flex: 1; min-width: 0; }
.profile-bar-name { font-size: 13px; font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.profile-bar-id { font-family: var(--font-mono); font-size: 10px; color: var(--text-faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.profile-bar-gear { color: var(--text-muted); font-size: 16px; }

/* Avatars: image or initials fallback, same box everywhere */
.seal { overflow: hidden; position: relative; }
.seal img { width: 100%; height: 100%; object-fit: cover; border-radius: inherit; }

/* Presence indicator as a standalone dot next to text (name/status word),
   never on top of the profile picture. Red = offline, green = online. */
.status-dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--danger);
  flex-shrink: 0;
}
.status-dot.online { background: var(--success); }

.status-label {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-weight: 600;
}
.status-label.online { color: var(--success); }
.status-label.offline { color: var(--danger); }
.status-label .status-dot { width: 9px; height: 9px; }

.name-status-dot { margin-left: 6px; }

/* Corner-of-avatar presence dot, for the friends list and conversation
   list specifically - sits on the bottom-right of the picture itself. */
.status-dot.corner {
  position: absolute;
  right: -1px;
  bottom: -1px;
  width: 12px;
  height: 12px;
  border: 2px solid var(--bg-sidebar);
  box-sizing: content-box;
}
.request-item .seal .status-dot.corner,
.friend-picker-item .seal .status-dot.corner { border-color: var(--bg-panel); }

/* Conversation list badges */
.conv-item { position: relative; }
.conv-name-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
}
.conv-time {
  font-size: 10px;
  color: var(--text-faint);
  font-family: var(--font-mono);
  flex-shrink: 0;
}
.conv-unread-badge {
  background: var(--danger);
  color: #fff;
  font-size: 10px;
  font-weight: 700;
  min-width: 18px;
  height: 18px;
  border-radius: 2px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 5px;
  flex-shrink: 0;
}
/* Absolutely positioned so it never steals flex space from conv-meta - that
   was the actual cause of timestamps landing at different x positions
   between rows (private rows reserved space for this, group rows didn't). */
.conv-item .conv-close-btn {
  position: absolute;
  top: 12px;
  right: 12px;
  background: none; border: none; color: var(--text-faint);
  padding: 3px; opacity: 0; border-radius: 2px; line-height: 1;
}
html:not(.touch-device) .conv-item:hover .conv-close-btn { opacity: 1; background: var(--bg-panel-alt); }
.conv-item .conv-close-btn:hover { color: var(--danger); }
html:not(.touch-device) .conv-item:has(.conv-close-btn):hover .conv-time { opacity: 0; }
/* Hover has no real meaning on a phone (see .touch-device, set in app.js
   on the first real touchstart) - a WebView can leave :hover stuck "on"
   after a tap, which would otherwise strand this button visible. It's a
   desktop-only affordance; on touch, closing a DM is reached through the
   conversation's own context menu (long-press) instead. */
.touch-device .conv-item .conv-close-btn { display: none; }

/* Message timestamps + avatar layout */
.message-row-inner { display: flex; gap: 10px; align-items: flex-start; max-width: 100%; }
.message-row.own .message-row-inner { flex-direction: row-reverse; }
.message-row { max-width: 72%; }
.message-avatar { width: 44px; height: 44px; border-radius: 3px; flex-shrink: 0; margin-top: 2px; font-size: 15px; }
.message-sender-line { display: flex; align-items: baseline; margin-bottom: 3px; }
.message-timestamp { font-size: 11px; color: var(--text-faint); font-family: var(--font-mono); }

/* Attachments */
.message-attachment-img {
  max-width: 260px;
  max-height: 260px;
  border-radius: 2px;
  margin-top: 6px;
  display: block;
  cursor: pointer;
}
.message-attachment-video {
  max-width: 300px;
  max-height: 300px;
  border-radius: 2px;
  margin-top: 6px;
  display: block;
  background: #000;
}
.message-attachment-file {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 6px;
  padding: 8px 10px;
  background: var(--bg-panel-alt);
  border: 1px solid var(--line);
  border-radius: 2px;
  font-size: 12px;
  text-decoration: none;
  color: var(--text);
}
.message-attachment-file:hover { border-color: var(--accent); color: var(--accent); }
.attachment-file-info, .attachment-chip-info {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  overflow: hidden;
}
.attachment-file-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.attachment-file-size {
  font-size: 10.5px;
  color: var(--text-faint);
  font-family: var(--font-mono);
}
.message-attachment-caption {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 4px;
  font-size: 11px;
  color: var(--text-faint);
}
.message-attachment-caption .attachment-file-name { color: var(--text-muted); max-width: 180px; }

.attachment-preview-chip {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 10px;
  margin: 0 26px;
  background: var(--bg-panel-alt);
  border: 1px solid var(--line);
  border-radius: 2px 2px 0 0;
  font-size: 12px;
}
.attachment-preview-chip img { width: 28px; height: 28px; object-fit: cover; border-radius: 2px; flex-shrink: 0; }
.attachment-preview-chip button { background: none; border: none; color: var(--danger); font-weight: 700; margin-left: auto; flex-shrink: 0; }
.upload-progress-track {
  width: 100%;
  height: 4px;
  border-radius: 2px;
  background: var(--bg-deepest);
  overflow: hidden;
}
.upload-progress-fill {
  display: block;
  height: 100%;
  width: 0%;
  background: var(--accent);
  border-radius: 2px;
  transition: width 0.15s ease;
}

/* Shown while an image/gif attachment is still loading, instead of just
   empty space that pops in once it's done - a fixed-size placeholder
   since the real size isn't known until the image finishes loading. */
.message-media-wrap.media-loading {
  min-width: 160px;
  min-height: 120px;
  border-radius: 2px;
  background: linear-gradient(90deg, var(--bg-panel-alt) 25%, var(--bg-deepest) 37%, var(--bg-panel-alt) 63%);
  background-size: 400% 100%;
  animation: media-skeleton 1.4s ease infinite;
}
.message-media-wrap.media-loading img { opacity: 0; }
@keyframes media-skeleton {
  0% { background-position: 100% 0; }
  100% { background-position: -100% 0; }
}

.attach-btn {
  background: var(--bg-panel-alt);
  border: 1px solid var(--line);
  color: var(--text-muted);
  border-radius: 2px;
  width: 42px;
  height: 42px;
  font-size: 16px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
}
.attach-btn .icon { margin: 0; }
.attach-btn:hover { border-color: var(--accent); color: var(--accent); }

/* ---------- GENERIC MODAL SYSTEM (replaces alert/confirm) ---------- */
.generic-modal .modal { text-align: left; }
.generic-modal-title { font-family: var(--font-display); font-size: 17px; font-weight: 700; margin: 0 0 10px; }
.generic-modal-message { font-size: 13px; color: var(--text-muted); line-height: 1.5; margin: 0 0 20px; white-space: pre-wrap; }
.generic-modal-actions { display: flex; justify-content: flex-end; gap: 10px; }
.generic-modal.error .generic-modal-title { color: var(--danger); }

/* ---------- Profile settings modal ---------- */
.avatar-edit-row { display: flex; align-items: center; gap: 16px; margin-bottom: 18px; }
.avatar-edit-row .seal { width: 76px; height: 76px; font-size: 28px; }
.avatar-edit-actions { display: flex; flex-direction: column; gap: 6px; }

.settings-header {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 4px;
  padding-bottom: 20px;
  margin-bottom: 4px;
  border-bottom: 1px solid var(--line);
}
.settings-header .seal { margin: 0 0 8px; }
.settings-header-name { font-family: var(--font-display); font-size: 19px; font-weight: 700; }
.settings-header-id { font-size: 12px; color: var(--text-faint); font-family: var(--font-mono); margin-bottom: 12px; }
.settings-avatar-actions { display: flex; align-items: center; gap: 12px; }
.settings-link-btn {
  background: none; border: none; color: var(--text-faint); font-size: 12px; font-weight: 600;
  text-decoration: underline; padding: 4px;
}
.settings-link-btn:hover { color: var(--danger); }

.settings-section-first { border-top: none; margin-top: 4px; padding-top: 4px; }
.settings-section h4 { display: flex; align-items: center; }
.settings-section form label { margin-bottom: 4px; }
.settings-section form { display: flex; flex-direction: column; gap: 12px; }
.settings-submit-btn { margin-top: 8px; }

.preference-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 0;
}
.preference-row + .preference-row { border-top: 1px solid var(--line); }
.preference-row > span { font-weight: 600; font-size: 14px; }
.preference-row select {
  background: var(--bg-input); color: var(--text); border: 1px solid var(--line);
  border-radius: 2px; padding: 6px 10px; font-size: 13px;
}
.theme-toggle { display: flex; border: 1px solid var(--line); border-radius: 2px; overflow: hidden; }
.theme-toggle-btn {
  background: var(--bg-input); color: var(--text-muted); border: none; padding: 6px 14px;
  font-size: 13px; font-weight: 600; cursor: pointer;
}
.theme-toggle-btn.active { background: var(--accent); color: var(--on-accent); }

.danger-zone-btn {
  background: none; border: 1px solid var(--danger); color: var(--danger);
  padding: 10px 14px; border-radius: 2px; font-weight: 600; width: 100%;
}
.danger-zone-btn:hover { background: var(--danger); color: #fff; }

.device-list { display: flex; flex-direction: column; gap: 8px; margin-top: 10px; }
.device-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px;
  background: var(--bg-panel);
  border: 1px solid var(--line);
  border-radius: 2px;
}
.device-item-icon { color: var(--text-muted); flex-shrink: 0; }
.device-item-meta { flex: 1; min-width: 0; }
.device-item-label {
  font-size: 13px;
  font-weight: 600;
  display: flex;
  align-items: center;
  gap: 8px;
}
.device-item-sub {
  font-size: 11px;
  color: var(--text-faint);
  font-family: var(--font-mono);
  margin-top: 2px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.device-item-current-badge {
  font-size: 9px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--success);
  background: rgba(86,211,100,0.15);
  border-radius: 2px;
  padding: 2px 6px;
}

/* ---------- Encryption verification (safety-number fingerprint) ---------- */
.verify-section { margin-top: 16px; }
.fingerprint-block { margin-bottom: 14px; }
.fingerprint-block-label {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-faint);
  margin-bottom: 6px;
}
.fingerprint-value {
  font-family: var(--font-mono);
  font-size: 13px;
  line-height: 1.6;
  letter-spacing: 0.02em;
  color: var(--text);
  background: var(--bg-input);
  border: 1px solid var(--line);
  border-radius: 2px;
  padding: 10px 12px;
  word-break: break-all;
}
.fingerprint-value.self { border-left: 3px solid var(--accent); }
.fingerprint-value.theirs { border-left: 3px solid var(--tok-function); }
.verify-status-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-top: 12px;
}
.verify-badge {
  font-size: 11px;
  font-weight: 700;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 9px;
  border-radius: 2px;
}
.verify-badge.ok { background: rgba(86,211,100,0.15); color: var(--success); }
.verify-badge.warn { background: rgba(248,81,73,0.15); color: var(--danger); }
.verify-key-changed-warning {
  font-size: 12px;
  color: var(--danger);
  background: rgba(248,81,73,0.08);
  border: 1px dashed var(--danger);
  border-radius: 2px;
  padding: 8px 10px;
  margin-top: 10px;
  line-height: 1.5;
}

/* ============================================================
   v3 additions: unified button classes, blocked-chat bar, emoji
   picker, gif embeds, admin page, conv-preview format.
   ============================================================ */

/* Reliable, reusable button styling - explicit classes instead of long
   ID-based selector lists, so new forms never end up unstyled by accident. */
.btn-primary {
  background: var(--accent);
  color: var(--on-accent);
  border: none;
  padding: 11px 14px;
  border-radius: 2px;
  font-weight: 600;
  letter-spacing: 0.01em;
}
.btn-primary:hover { background: var(--accent-hover); }
.btn-primary:disabled { opacity: 0.5; cursor: not-allowed; }

.btn-secondary {
  background: var(--bg-panel-alt);
  color: var(--text);
  border: 1px solid var(--line);
  padding: 10px 16px;
  border-radius: 2px;
  font-weight: 600;
}
.btn-secondary:hover { border-color: var(--accent); color: var(--accent); }

.btn-danger {
  background: none;
  border: 1px solid var(--danger);
  color: var(--danger);
  padding: 10px 14px;
  border-radius: 2px;
  font-weight: 600;
}
.btn-danger:hover { background: var(--danger); color: #fff; }

/* Blocked-chat bar (replaces the message form entirely when you've blocked
   the other person in a private conversation) */
.thread-blocked-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 14px 26px;
  border-top: 1px solid var(--line);
  background: rgba(248,81,73,0.08);
}
.thread-blocked-bar span { font-size: 13px; color: var(--danger); font-weight: 600; }

/* Conversation preview: "Display name: message" left, unread badge right.
   The timestamp itself now lives in .conv-name-row (see above) so it's
   always flush with the name, independent of preview text length. */
.conv-preview-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
.conv-preview-text {
  font-size: 13px;
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
  flex: 1;
}
.conv-preview-text.is-draft .draft-prefix { color: var(--accent); font-weight: 700; }
.conv-preview-text.is-draft .draft-text { color: var(--text-muted); }
.conv-preview-text strong { font-weight: 700; }
.conv-preview-text em { font-style: italic; }
.conv-preview-text del { text-decoration: line-through; opacity: 0.85; }

/* Emoji / GIFs picker */
.emoji-picker {
  position: absolute;
  left: 0;
  bottom: 64px;
  background: var(--bg-sidebar);
  border: 1px solid var(--line);
  border-radius: 2px;
  padding: 0;
  width: min(360px, calc(100vw - 24px));
  max-height: min(420px, 64vh);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  box-shadow: 0 12px 32px rgba(0,0,0,0.5);
  z-index: 40;
}

.picker-tabs {
  display: flex;
  gap: 4px;
  padding: 8px 8px 0;
  border-bottom: 1px solid var(--line);
  flex-shrink: 0;
}
.picker-tab {
  background: none;
  border: none;
  color: var(--text-muted);
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  padding: 8px 10px;
  border-bottom: 2px solid transparent;
}
.picker-tab:hover { color: var(--text); }
.picker-tab.active { color: var(--accent); border-bottom-color: var(--accent); }

.picker-body {
  flex: 1;
  overflow-y: auto;
  padding: 0 8px 8px;
  min-height: 0;
}

/* Row of small category icons (Recent + the 8 standard groups) sitting
   above the grid - sticky so it stays reachable while scrolling through a
   long category like Smileys & People or Objects.
   Top padding lives on this element itself (12px = the old 8px from
   .picker-body's top padding + this bar's own 4px), not on .picker-body -
   position:sticky sticks an element flush with its scroll container's
   *padding* edge, but overflow clips at the *border* edge, so a sticky
   child inside a top-padded scroll container leaves a gap the size of
   that padding where scrolled-past content peeks through above it. */
.emoji-category-tabs {
  position: sticky;
  top: 0;
  z-index: 1;
  display: flex;
  gap: 2px;
  overflow-x: auto;
  padding: 12px 2px 8px;
  margin-bottom: 4px;
  background: var(--bg-sidebar);
  border-bottom: 1px solid var(--line);
  scrollbar-width: none;
}
.emoji-category-tabs::-webkit-scrollbar { display: none; }
.emoji-category-tab {
  flex-shrink: 0;
  background: none;
  border: none;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 6px;
  border-radius: 2px;
  opacity: 0.55;
  color: var(--text);
}
.emoji-category-tab .icon { width: 17px; height: 17px; margin: 0; }
.emoji-category-tab:hover { background: var(--bg-panel-alt); opacity: 0.85; }
.emoji-category-tab.active { background: var(--bg-panel-alt); opacity: 1; color: var(--accent); }

.emoji-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(34px, 1fr));
  gap: 2px;
}
.emoji-grid button {
  background: none;
  border: none;
  font-size: 20px;
  padding: 4px;
  border-radius: 2px;
  line-height: 1;
}
.emoji-grid button:hover { background: var(--bg-panel-alt); }
.emoji-grid .hint { grid-column: 1 / -1; }

/* Country flags render as PNGs (public/flags/<code>.png) instead of the
   regional-indicator emoji pair - stock Windows has no flag glyphs and
   shows the bare two-letter code otherwise. See emojiOrFlagHtml in app.js.
   Sized to sit on the emoji baseline wherever it's dropped in: 1em square
   at the surrounding font-size, so it scales with the picker grid (20px)
   and with message-bubble text without a separate rule for each. */
.flag-emoji {
  width: 1em;
  height: 1em;
  object-fit: cover;
  vertical-align: -0.15em;
  border-radius: 1px;
}
.emoji-grid button .flag-emoji { width: 20px; height: 20px; }
.flag-emoji-inline { border-radius: 2px; }

.gif-search-form { margin: 8px 0 8px; }
.gif-search-form input {
  width: 100%;
  border: 1px solid var(--line);
  border-radius: 2px;
  padding: 8px 10px;
  background: var(--bg-deepest);
  font-size: 13px;
}
.gif-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 6px;
}
.gif-thumb {
  background: var(--bg-panel-alt);
  border: 1px solid var(--line);
  border-radius: 2px;
  padding: 0;
  overflow: hidden;
  aspect-ratio: 1 / 1;
  display: block;
}
.gif-thumb:hover { border-color: var(--accent); }
.gif-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.gif-grid .hint, .picker-body > .hint { grid-column: 1 / -1; text-align: center; padding: 20px 4px; }

/* Right-click user action menu */
.context-menu {
  position: fixed;
  z-index: 200;
  background: var(--bg-sidebar);
  border: 1px solid var(--line);
  border-radius: 2px;
  padding: 6px;
  min-width: 180px;
  max-width: min(240px, calc(100vw - 16px));
  box-shadow: 0 12px 32px rgba(0,0,0,0.5);
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.context-menu-header {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-faint);
  padding: 6px 10px 8px;
  border-bottom: 1px solid var(--line);
  margin-bottom: 4px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.context-menu-item {
  background: none;
  border: none;
  color: var(--text);
  text-align: left;
  font-size: 13px;
  font-weight: 600;
  padding: 8px 10px;
  border-radius: 2px;
}
.context-menu-item:hover { background: var(--bg-panel-alt); }
.context-menu-item.danger { color: var(--danger); }
.context-menu-item.danger:hover { background: rgba(248,81,73,0.14); }

/* Mobile back button in the thread header (hidden on wide screens) */
.thread-back-btn {
  display: none;
  background: var(--bg-panel-alt);
  border: 1px solid var(--line);
  color: var(--text);
  border-radius: 2px;
  width: 34px;
  height: 34px;
  font-size: 16px;
  flex-shrink: 0;
  align-items: center;
  justify-content: center;
}
.thread-back-btn:hover { border-color: var(--accent); color: var(--accent); }

/* Inline gif/image embed detected from a message that's just a media link */
.message-gif-embed {
  max-width: 280px;
  max-height: 280px;
  border-radius: 2px;
  display: block;
  margin-top: 2px;
}

/* Admin page (separate from the normal app, served at /admin) */
#admin-page-screen {
  display: flex;
  flex-direction: column;
  align-items: center;
  background: var(--bg-deepest);
  overflow-y: auto;
  padding: 40px 20px;
}
.admin-page-inner { width: min(680px, 100%); }
.admin-page-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 24px;
}
.admin-page-header h1 { font-family: var(--font-display); font-size: 22px; font-weight: 700; margin: 0; }
.admin-page-section {
  background: var(--bg-sidebar);
  border: 1px solid var(--line);
  border-radius: 2px;
  padding: 24px;
  margin-bottom: 20px;
}
.admin-page-section h3 { font-family: var(--font-display); margin: 0 0 16px; font-size: 15px; }

/* ============================================================
   RESPONSIVE / MOBILE
   Single, consolidated mobile pass - the app is a web wrapper
   candidate (Android APK), so this targets phone-sized viewports:
   larger tap targets, no input-triggered zoom, panels that stack
   instead of squeezing, and a back button to return to the list.
   ============================================================ */

/* Prevent iOS Safari's auto-zoom-on-focus: any focusable text input
   needs a >=16px font size. Applies everywhere (not just mobile) since
   it doesn't hurt desktop and keeps behavior consistent for an APK
   wrapper that may render at phone density even on a tablet. */
input[type="text"], input[type="password"], textarea, #message-input, #gif-search-input {
  font-size: 16px;
}

@media (max-width: 900px) {
  html, body { overflow-x: hidden; }

  .app-shell {
    grid-template-columns: 1fr;
    height: 100dvh;
  }
  .screen { height: 100dvh; }

  .list-panel { display: flex; width: 100%; }
  .thread-panel { display: none; width: 100%; }
  .app-shell.thread-open .thread-panel { display: flex; }
  .app-shell.thread-open .list-panel { display: none; }

  .thread-back-btn { display: flex; }

  .list-panel-scroll { padding: 14px 14px 8px; }
  .panel-header-row h2 { font-size: 17px; }
  .tab-btn, .icon-btn { padding: 8px 10px; font-size: 12px; }

  .thread-header { padding: 12px 14px; }
  .message-list { padding: 14px; gap: 12px; }
  .message-row { max-width: 84%; }
  .message-form { padding: 10px 14px; gap: 8px; }
  .attachment-preview-chip { margin: 0 14px; }
  .thread-disabled-bar, .thread-blocked-bar { padding: 10px 14px; }

  .mini-btn { padding: 8px 12px; font-size: 13px; }
  .attach-btn, #emoji-btn { width: 42px; height: 42px; font-size: 18px; }
  .conv-item { padding: 12px 8px; }
  .request-item, .admin-user-item { padding: 10px; }

  .modal { width: min(94vw, 420px); padding: 20px; max-height: 88vh; }
  .modal-actions { flex-wrap: wrap; }

  .emoji-picker { width: min(94vw, 360px); left: 50%; transform: translateX(-50%); }

  #admin-page-screen { padding: 20px 12px; }
  .admin-page-header { flex-wrap: wrap; gap: 10px; }
}

@media (max-width: 420px) {
  .gif-grid { grid-template-columns: repeat(2, 1fr); }
  .conv-time, .message-timestamp { font-size: 9px; }
  .profile-bar-id { display: none; }
}

/* Safe-area support for notched phones / an APK wrapper without browser chrome */
.message-form {
  padding-bottom: max(16px, env(safe-area-inset-bottom));
}
.profile-bar {
  padding-bottom: max(10px, env(safe-area-inset-bottom));
}
@media (max-width: 900px) {
  .message-form {
    padding-bottom: max(10px, env(safe-area-inset-bottom));
  }
}

/* ---------- Themed scrollbars (Firefox + WebKit/Blink) ---------- */
* {
  scrollbar-width: thin;
  scrollbar-color: var(--line) transparent;
}

*::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}
*::-webkit-scrollbar-track {
  background: transparent;
}
*::-webkit-scrollbar-thumb {
  background-color: var(--line);
  border-radius: 2px;
  border: 2px solid transparent;
  background-clip: padding-box;
}
*::-webkit-scrollbar-thumb:hover {
  background-color: var(--accent);
}
*::-webkit-scrollbar-corner {
  background: transparent;
}

/* Emoji/gif picker and dropdown-style scroll areas can afford to be a
   touch slimmer since they're small, dense panels */
.emoji-picker::-webkit-scrollbar, .gif-grid::-webkit-scrollbar {
  width: 8px;
}
