/* ═══════════════════════════════════════════════════════════
   ShutUp — theming system

   Three independent axes, set as attributes on <html> and driven by
   the Appearance panel (Edit profile → Appearance), synced per account:
     data-palette   = veyon | aurora | obsidian | nord | cyber | plum
     data-theme     = dark | light        (cyber is dark-only)
     data-structure = floating | boxed

   Component CSS below never hard-codes a palette colour — it reads the
   --tokens defined per palette here. "veyon" is the flagship brand look
   and the shipped default; "plum" is the original Sugar Plum look, kept
   as an option. Adding a palette = one more block.
   ═══════════════════════════════════════════════════════════ */

/* ── Shared, palette- and mode-independent ──────────────────── */
:root {
  --r-sm: 10px;
  --r-md: 16px;
  --r-lg: 24px;
  --sidebar-w: 370px;
  --font: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;

  --danger:      #FF6B6B;
  --danger-rgb:  255, 107, 107;

  /* Structure axis — floating is the default; boxed zeroes these. */
  --app-pad: 14px;
  --app-gap: 14px;
  --panel-radius: 20px;
  --side-divider: none;
  --panel-shadow: 0 20px 55px rgba(0, 0, 0, 0.42);

  /* Dark-mode surface helpers (mode axis, not palette). The light block
     near the end of this section repaints these; palette blocks never do. */
  --hover-bg:    rgba(255, 255, 255, 0.07);
  --hover-bg-soft: rgba(255, 255, 255, 0.045);
  --hover-bg-strong: rgba(255, 255, 255, 0.14);
  --inset-bg:    rgba(0, 0, 0, 0.28);
  --scrollbar-thumb: rgba(255, 255, 255, 0.09);
  --scrollbar-thumb-hover: rgba(255, 255, 255, 0.16);
  --danger-bg:   rgba(255, 107, 107, 0.14);
  --danger-text: #FFC2C2;
  --warn-bg:     rgba(253, 203, 110, 0.14);
  --warn-text:   #FFD9A0;
  --shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.25);
  --shadow-md: 0 8px 30px rgba(0, 0, 0, 0.35);
  --shadow-lg: 0 24px 70px rgba(0, 0, 0, 0.5);
}

:root[data-structure="boxed"] {
  --app-pad: 0px;
  --app-gap: 0px;
  --panel-radius: 0px;
  --side-divider: 1px solid var(--border);
  --panel-shadow: none;
}

/* ═══ Palette: VEYON CURRENT (default) ══════════════════════════
   The Veyon brand identity — deep indigo glass cut through with an
   electric blue-to-cyan signal, distinct from every palette below. */
:root,
:root[data-palette="veyon"] {
  --bg:          #0A0E1F;
  --panel:       rgba(255, 255, 255, 0.055);
  --panel-2:     rgba(255, 255, 255, 0.03);
  --panel-3:     rgba(255, 255, 255, 0.09);
  --raised:      rgba(22, 28, 56, 0.94);

  --bubble-me:      #2F6BFF;
  --bubble-me-2:    #00C2FF;
  --bubble-me-text: #FFFFFF;
  --grad-bubble-me: linear-gradient(135deg, #2F6BFF, #00C2FF);
  --bubble-them:    rgba(255, 255, 255, 0.075);

  --accent:       #6FA1FF;
  --accent-rgb:   111, 161, 255;
  --accent-2:     #2F6BFF;
  --accent-2-rgb: 47, 107, 255;
  --grad-accent:  linear-gradient(135deg, #2F6BFF, #00C2FF);
  --on-accent:    #FFFFFF;

  --online:       #2EE6A6;
  --online-rgb:   46, 230, 166;
  --tick-read:    #7DE8FF;

  --text:        #EAF1FF;
  --text-dim:    #9FB0D9;
  --muted:       #6E7CA3;

  --border:      rgba(255, 255, 255, 0.09);
  --border-lit:  rgba(111, 161, 255, 0.45);

  --glass-blur:  blur(26px) saturate(1.35);
  --panel-shadow: 0 24px 60px rgba(3, 6, 16, 0.5);
  --ambient:
    radial-gradient(720px circle at 6% -8%,  rgba(47, 107, 255, 0.32), transparent 60%),
    radial-gradient(620px circle at 102% 4%, rgba(0, 194, 255, 0.20), transparent 55%),
    radial-gradient(760px circle at 82% 118%, rgba(46, 230, 166, 0.12), transparent 55%);
  --call-bg: linear-gradient(160deg, #101836, #05070F);
}

/* ═══ Palette: AURORA GLASS ══════════════════════════════════════
   Frosted panels floating over a teal–violet–pink aurora. */
:root[data-palette="aurora"] {
  --bg:          #08090F;
  --panel:       rgba(255, 255, 255, 0.055);
  --panel-2:     rgba(255, 255, 255, 0.03);
  --panel-3:     rgba(255, 255, 255, 0.09);
  --raised:      rgba(28, 30, 52, 0.94);

  --bubble-me:      #7C5CFF;
  --bubble-me-2:    #22D3EE;
  --bubble-me-text: #FFFFFF;
  --grad-bubble-me: linear-gradient(135deg, #7C5CFF, #22D3EE);
  --bubble-them:    rgba(255, 255, 255, 0.075);

  --accent:       #A99BFE;
  --accent-rgb:   169, 155, 254;
  --accent-2:     #7C5CFF;
  --accent-2-rgb: 124, 92, 255;
  --grad-accent:  linear-gradient(135deg, #7C5CFF, #22D3EE);
  --on-accent:    #FFFFFF;

  --online:       #34E4B0;
  --online-rgb:   52, 228, 176;
  --tick-read:    #9EF0FF;

  --text:        #ECF0FF;
  --text-dim:    #A6AFD0;
  --muted:       #7C85A8;

  --border:      rgba(255, 255, 255, 0.09);
  --border-lit:  rgba(169, 155, 254, 0.42);

  --glass-blur:  blur(26px) saturate(1.35);
  --panel-shadow: 0 24px 60px rgba(3, 4, 12, 0.5);
  --ambient:
    radial-gradient(720px circle at 6% -8%,  rgba(124, 92, 255, 0.30), transparent 60%),
    radial-gradient(620px circle at 102% 4%, rgba(34, 211, 238, 0.20), transparent 55%),
    radial-gradient(760px circle at 82% 118%, rgba(236, 72, 153, 0.16), transparent 55%);
  --call-bg: linear-gradient(160deg, #16172E, #05060D);
}

/* ═══ Palette: OBSIDIAN & GOLD ═════════════════════════════════ */
:root[data-palette="obsidian"] {
  --bg:          #0A0A0B;
  --panel:       #141416;
  --panel-2:     #0F0F10;
  --panel-3:     #1C1C1F;
  --raised:      #1C1C1F;

  --bubble-me:      #202024;
  --bubble-me-2:    #202024;
  --bubble-me-text: #F2F0EA;
  --grad-bubble-me: #202024;
  --bubble-them:    #161618;

  --accent:       #E8C77B;
  --accent-rgb:   232, 199, 123;
  --accent-2:     #E8C77B;
  --accent-2-rgb: 232, 199, 123;
  --grad-accent:  linear-gradient(135deg, #E8C77B, #C9A55E);
  --on-accent:    #1A1400;

  --online:       #8FB98A;
  --online-rgb:   143, 185, 138;
  --tick-read:    #E8C77B;

  --text:        #F2F0EA;
  --text-dim:    #9C978B;
  --muted:       #6E6A60;

  --border:      rgba(232, 199, 123, 0.14);
  --border-lit:  rgba(232, 199, 123, 0.50);

  --glass-blur:  none;
  --panel-shadow: 0 20px 50px rgba(0, 0, 0, 0.6);
  --ambient: radial-gradient(680px circle at 84% -20%, rgba(232, 199, 123, 0.06), transparent 60%);
  --call-bg: linear-gradient(160deg, #131311, #050504);
}

/* ═══ Palette: NORD FROST ══════════════════════════════════════ */
:root[data-palette="nord"] {
  --bg:          #2E3440;
  --panel:       #333A47;
  --panel-2:     #2B313C;
  --panel-3:     #3B4252;
  --raised:      #3B4252;

  --bubble-me:      #5E81AC;
  --bubble-me-2:    #5E81AC;
  --bubble-me-text: #ECEFF4;
  --grad-bubble-me: #5E81AC;
  --bubble-them:    #3B4252;

  --accent:       #88C0D0;
  --accent-rgb:   136, 192, 208;
  --accent-2:     #5E81AC;
  --accent-2-rgb: 94, 129, 172;
  --grad-accent:  linear-gradient(135deg, #5E81AC, #81A1C1);
  --on-accent:    #FFFFFF;

  --online:       #A3BE8C;
  --online-rgb:   163, 190, 140;
  --tick-read:    #A3BE8C;

  --text:        #ECEFF4;
  --text-dim:    #C2CAD8;
  --muted:       #8A93A6;

  --border:      rgba(255, 255, 255, 0.08);
  --border-lit:  rgba(136, 192, 208, 0.50);

  --glass-blur:  none;
  --panel-shadow: 0 20px 46px rgba(0, 0, 0, 0.4);
  --ambient: radial-gradient(680px circle at 10% -14%, rgba(136, 192, 208, 0.10), transparent 60%);
  --call-bg: linear-gradient(160deg, #2E3440, #21252E);
}

/* ═══ Palette: NEON CYBERPUNK (dark only) ══════════════════════ */
:root[data-palette="cyber"] {
  --bg:          #05060A;
  --panel:       rgba(11, 13, 20, 0.82);
  --panel-2:     rgba(6, 8, 14, 0.55);
  --panel-3:     rgba(0, 240, 255, 0.07);
  --raised:      #0C0F18;

  --bubble-me:      #0E1119;
  --bubble-me-2:    #0E1119;
  --bubble-me-text: #EAFEFF;
  --grad-bubble-me: rgba(0, 240, 255, 0.08);
  --bubble-them:    rgba(11, 13, 20, 0.90);

  --accent:       #FF2E97;
  --accent-rgb:   255, 46, 151;
  --accent-2:     #FF2E97;
  --accent-2-rgb: 255, 46, 151;
  --grad-accent:  linear-gradient(135deg, #FF2E97, #00F0FF);
  --on-accent:    #05060A;

  --online:       #39FF9E;
  --online-rgb:   57, 255, 158;
  --tick-read:    #00F0FF;

  --text:        #E8ECFF;
  --text-dim:    #8A93C4;
  --muted:       #5D6591;

  --border:      rgba(0, 240, 255, 0.20);
  --border-lit:  rgba(255, 46, 151, 0.60);

  --glass-blur:  blur(9px);
  --panel-shadow: 0 0 34px rgba(0, 240, 255, 0.14), 0 20px 55px rgba(0, 0, 0, 0.6);
  --ambient:
    linear-gradient(rgba(0, 240, 255, 0.045) 1px, transparent 1px) 0 0 / 100% 36px,
    linear-gradient(90deg, rgba(0, 240, 255, 0.045) 1px, transparent 1px) 0 0 / 36px 100%,
    radial-gradient(760px circle at 50% 120%, rgba(255, 46, 151, 0.14), transparent 60%);
  --call-bg: linear-gradient(160deg, #0A0C16, #05060A);
}

/* ═══ Palette: SUGAR PLUM (the original) ═══════════════════════ */
:root[data-palette="plum"] {
  --bg:          #211A30;
  --panel:       #2A2140;
  --panel-2:     #362A52;
  --panel-3:     #423464;
  --raised:      #4D3E76;

  --bubble-me:      #A76BC9;
  --bubble-me-2:    #D68FC0;
  --bubble-me-text: #FFFFFF;
  --grad-bubble-me: linear-gradient(135deg, #A76BC9, #D68FC0);
  --bubble-them:    var(--panel-2);

  --accent:       #E3AEE0;
  --accent-rgb:   227, 174, 224;
  --accent-2:     #A76BC9;
  --accent-2-rgb: 167, 107, 201;
  --grad-accent:  linear-gradient(135deg, #A76BC9, #E3AEE0);
  --on-accent:    #FFFFFF;

  --online:       #7EE8C0;
  --online-rgb:   126, 232, 192;
  --tick-read:    #8FD8F5;

  --text:        #F3ECFA;
  --text-dim:    #C7B7DE;
  --muted:       #9A88AE;

  --border:      rgba(255, 255, 255, 0.08);
  --border-lit:  rgba(227, 174, 224, 0.4);

  --glass-blur:  none;
  --panel-shadow: 0 20px 55px rgba(0, 0, 0, 0.42);
  --ambient:
    radial-gradient(700px circle at 12% -5%,  rgba(167, 107, 201, 0.16), transparent 60%),
    radial-gradient(600px circle at 92% 105%, rgba(126, 232, 192, 0.07), transparent 55%);
  --call-bg: linear-gradient(160deg, #2A2140, #150F1F);
}

/* ── Light mode: shared surface helpers (mode axis) ──────────
   Palette blocks below repaint only base colours; these pale
   overlay/shadow tokens are the same across every light palette. */
:root[data-theme="light"] {
  --hover-bg:    rgba(40, 34, 60, 0.055);
  --hover-bg-soft: rgba(40, 34, 60, 0.035);
  --hover-bg-strong: rgba(40, 34, 60, 0.10);
  --inset-bg:    rgba(40, 34, 60, 0.05);
  --scrollbar-thumb: rgba(40, 34, 60, 0.16);
  --scrollbar-thumb-hover: rgba(40, 34, 60, 0.26);

  --danger:      #D9536A;
  --danger-rgb:  217, 83, 106;
  --danger-bg:   rgba(217, 83, 106, 0.12);
  --danger-text: #B23F55;
  --warn-bg:     rgba(191, 128, 20, 0.14);
  --warn-text:   #8A5E12;

  --shadow-sm: 0 2px 8px rgba(50, 44, 70, 0.09);
  --shadow-md: 0 8px 30px rgba(50, 44, 70, 0.12);
  --shadow-lg: 0 24px 70px rgba(50, 44, 70, 0.18);
}
/* cyber has no light mode — hold the helpers dark if the attribute slips through */
:root[data-palette="cyber"][data-theme="light"] {
  --hover-bg: rgba(255,255,255,0.07);
  --inset-bg: rgba(0,0,0,0.28);
  --scrollbar-thumb: rgba(255,255,255,0.09);
}

:root[data-palette="veyon"][data-theme="light"] {
  --bg:          #F2F6FF;
  --panel:       rgba(255, 255, 255, 0.72);
  --panel-2:     rgba(255, 255, 255, 0.52);
  --panel-3:     rgba(16, 24, 54, 0.06);
  --raised:      rgba(255, 255, 255, 0.97);

  --grad-bubble-me: linear-gradient(135deg, #2F6BFF, #00A8D8);
  --bubble-me-text: #FFFFFF;
  --bubble-them:    #FFFFFF;

  --accent:       #2358DB;
  --accent-rgb:   35, 88, 219;
  --accent-2:     #2F6BFF;
  --accent-2-rgb: 47, 107, 255;
  --grad-accent:  linear-gradient(135deg, #2F6BFF, #00A8D8);

  --online:       #0EA579;
  --online-rgb:   14, 165, 121;
  --tick-read:    #1D7FB8;

  --text:        #0E1626;
  --text-dim:    #4C5B7D;
  --muted:       #8291B3;

  --border:      rgba(16, 24, 54, 0.10);
  --border-lit:  rgba(47, 107, 255, 0.45);

  --glass-blur:  blur(24px) saturate(1.3);
  --panel-shadow: 0 18px 46px rgba(30, 46, 90, 0.16);
  --ambient:
    radial-gradient(720px circle at 6% -8%,  rgba(47, 107, 255, 0.14), transparent 60%),
    radial-gradient(620px circle at 102% 4%, rgba(0, 194, 255, 0.12), transparent 55%),
    radial-gradient(760px circle at 82% 118%, rgba(46, 230, 166, 0.10), transparent 55%);
}

:root[data-palette="aurora"][data-theme="light"] {
  --bg:          #F3F3FB;
  --panel:       rgba(255, 255, 255, 0.72);
  --panel-2:     rgba(255, 255, 255, 0.52);
  --panel-3:     rgba(20, 16, 45, 0.06);
  --raised:      rgba(255, 255, 255, 0.97);

  --grad-bubble-me: linear-gradient(135deg, #7C5CFF, #22B8D8);
  --bubble-me-text: #FFFFFF;
  --bubble-them:    #FFFFFF;

  --accent:       #6A57DE;
  --accent-rgb:   106, 87, 222;
  --accent-2:     #7C5CFF;
  --accent-2-rgb: 124, 92, 255;
  --grad-accent:  linear-gradient(135deg, #7C5CFF, #22B8D8);

  --online:       #12B98A;
  --online-rgb:   18, 185, 138;
  --tick-read:    #2A93C4;

  --text:        #1A1B2E;
  --text-dim:    #55566E;
  --muted:       #8A8BA2;

  --border:      rgba(20, 16, 45, 0.10);
  --border-lit:  rgba(124, 92, 255, 0.45);

  --glass-blur:  blur(24px) saturate(1.3);
  --panel-shadow: 0 18px 46px rgba(60, 50, 110, 0.16);
  --ambient:
    radial-gradient(720px circle at 6% -8%,  rgba(124, 92, 255, 0.14), transparent 60%),
    radial-gradient(620px circle at 102% 4%, rgba(34, 211, 238, 0.12), transparent 55%),
    radial-gradient(760px circle at 82% 118%, rgba(236, 72, 153, 0.10), transparent 55%);
}

:root[data-palette="obsidian"][data-theme="light"] {
  --bg:          #F1EEE7;
  --panel:       #FBFAF6;
  --panel-2:     #F5F2EB;
  --panel-3:     rgba(30, 27, 20, 0.06);
  --raised:      #FFFFFF;

  --bubble-me:      #2A2A2E;
  --bubble-me-2:    #2A2A2E;
  --bubble-me-text: #F4F2EC;
  --grad-bubble-me: #2A2A2E;
  --bubble-them:    #FFFFFF;

  --accent:       #A9822F;
  --accent-rgb:   169, 130, 47;
  --accent-2:     #B98F3C;
  --accent-2-rgb: 185, 143, 60;
  --grad-accent:  linear-gradient(135deg, #B98F3C, #9A7430);
  --on-accent:    #FFFFFF;

  --online:       #5E8C57;
  --online-rgb:   94, 140, 87;
  --tick-read:    #A9822F;

  --text:        #1E1B14;
  --text-dim:    #5C5648;
  --muted:       #918A78;

  --border:      rgba(30, 27, 20, 0.13);
  --border-lit:  rgba(169, 130, 47, 0.50);

  --panel-shadow: 0 16px 42px rgba(60, 50, 30, 0.14);
  --ambient: radial-gradient(680px circle at 84% -20%, rgba(169, 130, 47, 0.07), transparent 60%);
}

:root[data-palette="nord"][data-theme="light"] {
  --bg:          #E5E9F0;
  --panel:       #FFFFFF;
  --panel-2:     #F5F7FA;
  --panel-3:     #E5E9F0;
  --raised:      #FFFFFF;

  --bubble-me:      #5E81AC;
  --bubble-me-2:    #5E81AC;
  --bubble-me-text: #FFFFFF;
  --grad-bubble-me: #5E81AC;
  --bubble-them:    #FFFFFF;

  --accent:       #5E81AC;
  --accent-rgb:   94, 129, 172;
  --accent-2:     #5E81AC;
  --accent-2-rgb: 94, 129, 172;
  --grad-accent:  linear-gradient(135deg, #5E81AC, #81A1C1);
  --on-accent:    #FFFFFF;

  --online:       #7A9E5E;
  --online-rgb:   122, 158, 94;
  --tick-read:    #B48EAD;

  --text:        #2E3440;
  --text-dim:    #4C566A;
  --muted:       #8892A2;

  --border:      #D8DEE9;
  --border-lit:  rgba(94, 129, 172, 0.50);

  --panel-shadow: 0 16px 42px rgba(46, 52, 64, 0.13);
  --ambient: radial-gradient(680px circle at 10% -14%, rgba(94, 129, 172, 0.10), transparent 60%);
}

/* Sugar Plum light — the original cream-and-candy mode. */
:root[data-palette="plum"][data-theme="light"] {
  --bg:          #FFF7FB;
  --panel:       #FFFFFF;
  --panel-2:     #FCEEF7;
  --panel-3:     #F7E1F0;
  --raised:      #F1D3EA;

  --bubble-them: #FCEEF7;

  --accent:       #9C4FB8;
  --accent-rgb:   156, 79, 184;
  --accent-2:     #A76BC9;
  --accent-2-rgb: 167, 107, 201;
  --grad-accent:  linear-gradient(135deg, #A76BC9, #9C4FB8);

  --online:       #3FAE87;
  --online-rgb:   63, 174, 135;
  --tick-read:    #2E9BCB;

  --text:        #3B2C4E;
  --text-dim:    #7C6790;
  --muted:       #A992BA;

  --border:      rgba(59, 44, 78, 0.09);
  --border-lit:  rgba(156, 79, 184, 0.35);

  --ambient:
    radial-gradient(700px circle at 12% -5%,  rgba(167, 107, 201, 0.10), transparent 60%),
    radial-gradient(600px circle at 92% 105%, rgba(63, 174, 135, 0.06), transparent 55%);
}

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

html, body { height: 100%; }

body {
  font-family: var(--font);
  background: var(--bg);
  color: var(--text);
  font-size: 15px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  overflow: hidden;
}

/* Ambient wash — the continuous canvas the panels float on. Per-palette
   via --ambient; fixed so it doesn't scroll with content. */
body::before {
  content: '';
  position: fixed;
  inset: 0;
  background: var(--ambient, transparent);
  pointer-events: none;
  z-index: 0;
}

body { transition: background 0.3s ease; }
@media (prefers-reduced-motion: reduce) { body { transition: none; } }

button, input, textarea { font: inherit; color: inherit; }
button { cursor: pointer; border: none; background: none; }

::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: var(--scrollbar-thumb);
  border-radius: 99px;
}
::-webkit-scrollbar-thumb:hover { background: var(--scrollbar-thumb-hover); }

.hidden { display: none !important; }

/* ═══════════════════════════════════════════════════════════
   Auth screen
   ═══════════════════════════════════════════════════════════ */

#auth-screen {
  position: relative;
  z-index: 1;
  height: 100%;
  display: grid;
  place-items: center;
  padding: 24px;
}

/* Sits in front of auth-screen AND app-screen (fixed + high z-index),
   since it has to gate whichever one would otherwise be visible. */
#applock-screen {
  position: fixed;
  inset: 0;
  z-index: 300;
  display: grid;
  place-items: center;
  padding: 24px;
  background: var(--bg);
}

.applock-card { text-align: center; }
.applock-card .brand { justify-content: center; }

.applock-input {
  width: 100%;
  margin-top: 22px;
  padding: 14px;
  text-align: center;
  font-size: 22px;
  letter-spacing: 6px;
  background: var(--inset-bg);
  border: 1.5px solid var(--border);
  border-radius: var(--r-sm);
  outline: none;
}

.applock-input:focus { border-color: var(--border-lit); }

.applock-reset-link {
  display: block;
  margin: 18px auto 0;
  font-size: 12px;
  color: var(--muted);
  text-decoration: underline;
}
.applock-reset-link:hover { color: var(--text-dim); }

.applock-method-tabs {
  display: flex;
  gap: 6px;
  margin: 18px 0 4px;
}
.applock-method-tabs .chat-filter-tab { flex: 1; padding: 9px 6px; }

.applock-biometric-btn {
  width: 100%;
  margin-top: 22px;
  padding: 16px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  background: var(--inset-bg);
  border: 1.5px dashed var(--border-lit);
  border-radius: var(--r-md);
  color: var(--text);
  font-weight: 650;
}
.applock-biometric-btn svg { width: 34px; height: 34px; color: var(--accent); }
.applock-biometric-btn:hover { background: var(--hover-bg); }

/* Pattern lock — 3×3 grid of dots; drag across them to draw a sequence.
   Lines are drawn on an absolutely-positioned SVG overlay so dot markup
   itself can stay simple divs (see wirePatternGrid() in app.js). */
.pattern-grid {
  position: relative;
  width: 220px;
  height: 220px;
  margin: 22px auto 0;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: repeat(3, 1fr);
  place-items: center;
  touch-action: none;
  user-select: none;
}

.pattern-dot {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--panel-3);
  border: 2px solid var(--border-lit);
  transition: background 0.1s, transform 0.1s;
  /* Needs real hit-testing, unlike most decorative elements here — dotAt()
     in app.js relies on document.elementFromPoint() landing ON a dot to
     detect which one the pointer is currently over during a drag. */
}

.pattern-dot.on {
  background: var(--accent-2);
  border-color: var(--accent-2);
  transform: scale(1.15);
}

.pattern-lines {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

.pattern-lines line { stroke: var(--accent-2); stroke-width: 3; stroke-linecap: round; }
.pattern-hint { text-align: center; font-size: 12px; color: var(--muted); margin-top: 10px; }

.auth-card {
  width: 100%;
  max-width: 420px;
  background: linear-gradient(160deg, var(--panel-2), var(--panel)), var(--raised);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-lg);
  padding: 42px 38px 34px;
  backdrop-filter: var(--glass-blur, none);
  -webkit-backdrop-filter: var(--glass-blur, none);
}

.brand {
  display: flex;
  align-items: center;
  gap: 13px;
  justify-content: center;
  margin-bottom: 8px;
}

.brand-mark {
  width: 46px;
  height: 46px;
  border-radius: 14px;
  background: var(--grad-accent);
  display: grid;
  place-items: center;
  box-shadow: 0 6px 20px rgba(var(--accent-2-rgb), 0.45);
  flex: none;
}

.brand-mark svg { width: 24px; height: 24px; }

.brand h1 {
  font-size: 27px;
  font-weight: 700;
  letter-spacing: -0.5px;
}

.auth-sub {
  text-align: center;
  color: var(--muted);
  font-size: 13.5px;
  margin-bottom: 30px;
}

.field { margin-bottom: 16px; }

/* Child combinator — a nested .picker-row is also a <label> and must NOT
   inherit this block's display:block (that's what stacked the member rows). */
.field > label {
  display: block;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--text-dim);
  letter-spacing: 0.1px;
  margin-bottom: 8px;
}

.field input,
.field textarea,
.field select {
  width: 100%;
  padding: 12px 15px;
  background: var(--inset-bg);
  border: 1.5px solid transparent;   /* filled, not outlined — the hard border on every field is what read as a grid of boxes */
  border-radius: var(--r-md);
  color: var(--text);
  font: inherit;
  transition: border-color 0.18s, box-shadow 0.18s, background 0.18s;
  outline: none;
}

.field input:focus,
.field textarea:focus,
.field select:focus {
  border-color: var(--border-lit);
  background: var(--panel-3);
  box-shadow: 0 0 0 3.5px rgba(var(--accent-2-rgb), 0.15);
}

.field input::placeholder,
.field textarea::placeholder { color: var(--muted); }

/* Light grounds need a hairline to define a filled field; dark stays borderless. */
:root[data-theme="light"] .field input,
:root[data-theme="light"] .field textarea,
:root[data-theme="light"] .field select,
:root[data-theme="light"] .modal-card .field input,
:root[data-theme="light"] .modal-card .field textarea,
:root[data-theme="light"] .modal-card .field select,
:root[data-theme="light"] .picker-list {
  border-color: var(--border);
}

.btn-primary {
  width: 100%;
  padding: 13px;
  margin-top: 10px;
  border-radius: var(--r-sm);
  background: var(--grad-accent);
  color: var(--on-accent);
  font-weight: 650;
  font-size: 15px;
  box-shadow: 0 6px 20px rgba(var(--accent-2-rgb), 0.35);
  transition: transform 0.14s, box-shadow 0.18s, filter 0.18s;
}

.btn-primary:hover:not(:disabled) {
  transform: translateY(-1px);
  box-shadow: 0 10px 28px rgba(var(--accent-2-rgb), 0.45);
}

.btn-primary:active:not(:disabled) { transform: translateY(0); }

.btn-primary:disabled { opacity: 0.55; cursor: not-allowed; }

.auth-toggle {
  text-align: center;
  margin-top: 20px;
  font-size: 13.5px;
  color: var(--muted);
}

.auth-toggle button {
  color: var(--accent);
  font-weight: 600;
}

.auth-toggle button:hover { text-decoration: underline; }

.auth-error {
  background: var(--danger-bg);
  border: 1px solid var(--danger);
  color: var(--danger-text);
  padding: 10px 13px;
  border-radius: var(--r-sm);
  font-size: 13.5px;
  margin-bottom: 16px;
}

.auth-note {
  display: flex;
  gap: 9px;
  align-items: flex-start;
  margin-top: 22px;
  padding-top: 18px;
  border-top: 1px solid var(--border);
  color: var(--muted);
  font-size: 12px;
  line-height: 1.55;
}

.auth-note svg { width: 14px; height: 14px; flex: none; margin-top: 2px; }

/* ═══════════════════════════════════════════════════════════
   App shell
   ═══════════════════════════════════════════════════════════ */

#app-screen {
  position: relative;
  z-index: 1;
  height: 100%;
  display: grid;
  grid-template-columns: var(--sidebar-w) 1fr;
  grid-template-rows: minmax(0, 1fr);
  /* Structure axis: floating leaves a gap so the ambient canvas shows
     between the panels; boxed zeroes these for edge-to-edge columns. */
  gap: var(--app-gap);
  padding: var(--app-pad);
  background: transparent;
}

/* Explicit placement matters: there are THREE children (sidebar, empty state,
   conversation) but only two columns. Left to auto-placement the third would
   wrap onto an implicit second row instead of stacking behind the second. */
.sidebar { grid-column: 1; grid-row: 1; }
.chat-pane,
.no-chat { grid-column: 2; grid-row: 1; }

/* The floating islands. Radius / shadow / edge come from the structure
   tokens, so "boxed" collapses them back to bordered full-bleed columns. */
.sidebar,
.chat-pane,
.no-chat {
  border-radius: var(--panel-radius);
  box-shadow: var(--panel-shadow);
  overflow: hidden;
}

/* ── Sidebar ─────────────────────────────────────────────── */

.sidebar {
  background: var(--panel);
  border-right: var(--side-divider);
  /* A column, not a fixed set of grid rows: the sidebar holds a varying
     number of rows (header, select bar, status tray, search, filter chips,
     list) and some hide themselves. With "auto auto 1fr" the filter chips
     fell into the 1fr row whenever the status tray was empty and stretched
     into giant pills. Here only the chat list takes the spare height. */
  display: flex;
  flex-direction: column;
  min-height: 0;
  backdrop-filter: var(--glass-blur, none);
  -webkit-backdrop-filter: var(--glass-blur, none);
}

.sidebar > * { flex: none; }
.sidebar > .chat-list { flex: 1 1 0; }

.sidebar-head {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 16px 12px;
  border-bottom: 1px solid var(--border);
}

/* Six icon buttons plus the avatar no longer fit at gap:12/padding:18 without
   spilling past the sidebar's fixed width (see icon-btn/avatar.sm below) —
   scoped to this row only, so nothing else in the app shrinks with it. */
.sidebar-head .icon-btn { width: 30px; height: 30px; }
.sidebar-head .avatar.sm { width: 32px; height: 32px; }

.sidebar-head .me-name {
  font-weight: 650;
  font-size: 15px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.sidebar-head .me-handle {
  font-size: 12.5px;
  color: var(--muted);
}

.sidebar-head .grow { flex: 1; min-width: 0; }

.icon-btn {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  color: var(--text-dim);
  transition: background 0.15s, color 0.15s;
  flex: none;
}

.icon-btn:hover { background: var(--hover-bg); color: var(--text); }
.icon-btn svg { width: 19px; height: 19px; }

.search-wrap { padding: 12px 14px; }

.search-box {
  display: flex;
  align-items: center;
  gap: 10px;
  background: var(--inset-bg);
  border: 1.5px solid transparent;
  border-radius: 11px;
  padding: 9px 13px;
  transition: border-color 0.18s;
}

.search-box:focus-within { border-color: var(--border-lit); }
.search-box svg { width: 16px; height: 16px; color: var(--muted); flex: none; }

.search-box input {
  flex: 1;
  background: none;
  border: none;
  outline: none;
  min-width: 0;
}

.search-box input::placeholder { color: var(--muted); }

.chat-list { overflow-y: auto; padding: 4px 8px 14px; min-height: 0; }

.list-label {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--muted);
  padding: 12px 12px 7px;
}

.chat-item {
  display: flex;
  align-items: center;
  gap: 13px;
  padding: 11px 12px;
  border-radius: var(--r-md);
  cursor: pointer;
  transition: background 0.15s;
  position: relative;
}

.chat-item:hover { background: var(--hover-bg-soft); }

.chat-item.active { background: rgba(var(--accent-2-rgb), 0.19); }

.chat-item.active::before {
  content: '';
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 3px;
  height: 26px;
  border-radius: 0 3px 3px 0;
  background: var(--accent);
}

.avatar {
  width: 47px;
  height: 47px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-weight: 650;
  font-size: 17px;
  color: #fff;
  flex: none;
  position: relative;
  user-select: none;
}

.avatar.sm { width: 38px; height: 38px; font-size: 14px; }
.avatar.lg { width: 54px; height: 54px; font-size: 20px; }

/* Absolute + inset:0 pins the photo to the avatar's exact box regardless of
   the grid layout used for the initials fallback — width/height:100% alone
   didn't always resolve, which let portrait photos spill past the circle. */
.avatar img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  border-radius: 50%;
  display: block;
}

.avatar .dot {
  position: absolute;
  right: -1px;
  bottom: -1px;
  width: 13px;
  height: 13px;
  border-radius: 50%;
  background: var(--muted);
  border: 2.5px solid var(--panel);
  transition: background 0.2s;
}

.avatar .dot.online {
  background: var(--online);
  box-shadow: 0 0 0 2px rgba(var(--online-rgb), 0.22);
}

.chat-item .body { flex: 1; min-width: 0; }

.chat-item .row-1 {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
}

.chat-item .name {
  font-weight: 600;
  font-size: 14.5px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.chat-item .time { font-size: 11.5px; color: var(--muted); flex: none; }

.row-1-right { display: flex; align-items: center; gap: 2px; flex: none; }

.chat-item .row-2 {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-top: 2px;
}

.chat-item .preview {
  font-size: 13px;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  display: flex;
  align-items: center;
  gap: 5px;
}

.chat-item .preview svg { width: 14px; height: 14px; flex: none; }

.badge {
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  border-radius: 99px;
  background: var(--accent-2);
  color: var(--on-accent);
  font-size: 11.5px;
  font-weight: 700;
  display: grid;
  place-items: center;
  flex: none;
}

/* Sidebar filter tabs — All / Unread / Favourites / Groups / Private */

.chat-filter-tabs {
  display: flex;
  gap: 6px;
  padding: 2px 12px 10px;
  overflow-x: auto;
  scrollbar-width: none;
}

.chat-filter-tabs::-webkit-scrollbar { display: none; }

.chat-filter-tab {
  flex: none;
  padding: 6px 13px;
  border-radius: 99px;
  font-size: 12.5px;
  font-weight: 650;
  color: var(--text-dim);
  background: var(--hover-bg-soft);
  border: 1px solid transparent;
  transition: background 0.15s, color 0.15s;
}

.chat-filter-tab:hover { background: var(--hover-bg); }

.chat-filter-tab.active {
  background: rgba(var(--accent-2-rgb), 0.22);
  color: var(--accent);
  border-color: rgba(var(--accent-2-rgb), 0.4);
}

/* Favourite star on each chat-list row — small and quiet unstarred, gold
   and always visible once starred (there's no hover state on touch, so a
   starred chat needs to stay visibly starred without hovering). */

.fav-star {
  flex: none;
  width: 22px;
  height: 22px;
  display: grid;
  place-items: center;
  color: var(--muted);
  opacity: 0;
  transition: opacity 0.15s, color 0.15s;
}

.chat-item:hover .fav-star,
.fav-star.active { opacity: 1; }

.fav-star.active { color: #FDCB6E; }
.fav-star svg { width: 16px; height: 16px; }

.empty-hint {
  padding: 34px 22px;
  text-align: center;
  color: var(--muted);
  font-size: 13.5px;
  line-height: 1.6;
}

/* ── Chat pane ───────────────────────────────────────────── */

/* A column (like .sidebar): header, optional bars (pinned message, call
   banner, notices), the message list, the composer. Only the message list
   takes the spare height, however many optional bars are showing. */
.chat-pane {
  display: flex;
  flex-direction: column;
  background: var(--panel-2);
  min-height: 0;
  min-width: 0;
  backdrop-filter: var(--glass-blur, none);
  -webkit-backdrop-filter: var(--glass-blur, none);
}
.chat-pane > * { flex: none; }
.chat-pane > .messages { flex: 1 1 0; min-height: 0; }

/* Grid items default to min-width:auto (content-based), not 0 — so without
   this, any one of these three rows (most often the composer's icon row)
   demanding more than the phone's actual width silently widened the whole
   shared grid column, dragging chat-head/messages/composer all out past
   the real viewport edge together. That's what made the send button, mic
   icon, and message bubbles all cut off at the same edge on mobile,
   needing a sideways swipe to reach them. min-width: 0 lets each row
   actually shrink to the real available width instead. */
.chat-head, .messages, .composer { min-width: 0; }

.chat-head {
  display: flex;
  align-items: center;
  gap: 13px;
  padding: 13px 20px;
  background: var(--panel);
  border-bottom: 1px solid var(--border);
  position: relative;
}

.chat-head .avatar .dot { border-color: var(--panel); }
.chat-head .peer-name { font-weight: 650; font-size: 15.5px; cursor: pointer; }
.chat-head #peer-avatar { cursor: pointer; transition: opacity 0.15s; }
.chat-head #peer-avatar:hover { opacity: 0.85; }

.chat-head .peer-status {
  font-size: 12.5px;
  color: var(--muted);
  transition: color 0.2s;
}

.chat-head .peer-status.online { color: var(--online); }
.chat-head .peer-status.typing { color: var(--accent); font-style: italic; }

.back-btn { display: none; }

/* Chat-head overflow menu (search, wallpaper) */

.chat-menu-wrap { position: relative; flex: none; }

.chat-menu {
  position: absolute;
  top: 44px;
  right: 0;
  background: var(--raised);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-md);
  padding: 6px;
  display: flex;
  flex-direction: column;
  min-width: 210px;
  max-height: min(70vh, 520px);
  overflow-y: auto;
  z-index: 20;
}

.chat-menu button {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border-radius: 8px;
  font-size: 13.5px;
  text-align: left;
  color: var(--text);
  transition: background 0.15s;
  flex: none;
}

.chat-menu button svg { width: 17px; height: 17px; color: var(--text-dim); flex: none; }
.chat-menu button:hover { background: var(--hover-bg); }

.chat-menu-divider { height: 1px; background: var(--border); margin: 6px 4px; flex: none; }

.chat-menu-danger { color: var(--danger); }
.chat-menu-danger svg { color: var(--danger) !important; }
.chat-menu-danger:hover { background: var(--danger-bg); }

/* Bulk-select bars — one shared look for both the sidebar's "select chats"
   mode and a chat's "select messages" mode (see #chat-select-bar /
   #msg-select-bar): a toolbar that replaces whatever's normally there
   (search box / composer) while the mode is active. */
.select-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 14px;
  background: var(--panel-2);
  border-bottom: 1px solid var(--border);
}

.select-bar-count { flex: 1; font-size: 13.5px; font-weight: 650; }
.select-bar .icon-btn:hover { color: var(--danger); }
.select-bar #chat-select-read:hover,
.select-bar #msg-select-star:hover { color: var(--online); }

.chat-item-checkbox {
  flex: none;
  width: 19px;
  height: 19px;
  accent-color: var(--accent-2);
  margin-right: -2px;
}

.chat-item.selected { background: rgba(var(--accent-2-rgb), 0.14); }

/* Label chips — small colored pills under a chat-item's preview text. */
.chat-item-labels { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 3px; }

.chat-label-chip {
  display: inline-flex;
  align-items: center;
  font-size: 10px;
  font-weight: 700;
  padding: 1px 7px;
  border-radius: 99px;
  color: #fff;
  margin-top: 3px;
  margin-right: 4px;
}

/* Bubble multi-select — a checkbox rail on the opposite side from the
   bubble's own tail, so it never overlaps the existing hover action chip. */
.bubble-row.select-mode { cursor: pointer; }
.bubble-select-check {
  flex: none;
  width: 19px;
  height: 19px;
  accent-color: var(--accent-2);
  align-self: center;
}
/* display:contents by default — this wrapper exists purely so a select-mode
   checkbox has somewhere to sit next to the bubble, but it's present in the
   DOM at all times (checkbox itself is conditional). Left as a real flex
   box unconditionally, this always-unconstrained flex row corrupted the
   bubble's own shrink-to-fit width calculation, collapsing every bubble to
   near its narrowest possible line break regardless of free space. contents
   removes it from the box tree entirely outside select mode, so .bubble
   goes back to being a normal flex-column child of .bubble-row. */
.bubble-select-row { display: contents; }
.bubble-row.select-mode .bubble-select-row { display: flex; align-items: center; gap: 8px; }
.bubble-row.me.select-mode .bubble-select-row { flex-direction: row-reverse; }

/* In-chat search — overlays the top of the messages pane rather than
   taking its own grid row, so it doesn't disturb .chat-pane's 3-row
   grid (head / messages / composer) when toggled on and off. */

.chat-search-bar {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 20px;
  background: var(--panel);
  border-bottom: 1px solid var(--border);
  box-shadow: var(--shadow-md);
  z-index: 15;
}

.chat-search-bar svg { width: 17px; height: 17px; color: var(--muted); flex: none; }

.chat-search-bar input {
  flex: 1;
  background: transparent;
  border: none;
  outline: none;
  color: var(--text);
  font-size: 13.5px;
  font-family: inherit;
}

.chat-search-count {
  font-size: 12px;
  color: var(--muted);
  flex: none;
  white-space: nowrap;
}

/* Messages */

.messages {
  overflow-y: auto;
  /* Without this, the CSS overflow spec auto-resolves overflow-x to "auto"
     just because overflow-y isn't visible — so the hover .bubble-actions
     toolbar (position:absolute, invisible until hover but still ~146px
     wide in its own layout box) was creating a real horizontal scrollbar
     here, most visible on narrow phone screens where bubbles cut off and
     needed a sideways swipe to read. Messages should only ever scroll
     vertically. */
  overflow-x: hidden;
  padding: 22px 26px 12px;
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-height: 0;
  scroll-behavior: smooth;
}

.day-sep {
  align-self: center;
  margin: 18px 0 12px;
  padding: 5px 14px;
  border-radius: 99px;
  background: rgba(0, 0, 0, 0.3);
  color: var(--text-dim);
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0.3px;
}

/* Column, not row: a bubble-row can hold an optional sender-name label above
   the bubble (group chats). align-items does the left/right job justify-content
   would do in a row layout — with a single child (direct chats, no label) the
   visual result is identical either way. */
.bubble-row { display: flex; flex-direction: column; align-items: flex-start; margin-top: 3px; }
.bubble-row.me { align-items: flex-end; }

/* Bigger gap between speakers than within one person's run — the visual
   grouping that makes a long thread readable. */
.bubble-row.first-of-run { margin-top: 13px; }

.bubble {
  max-width: min(65%, 560px);
  padding: 8px 12px 7px;
  border-radius: var(--r-md);
  position: relative;
  /* break-word (not anywhere): both wrap a too-long unbreakable string
     (e.g. a long URL) so it can't blow out the bubble, but "anywhere"
     additionally shrinks the browser's *preferred*-width calculation for
     the bubble itself down toward its narrowest possible break — which
     collapsed every bubble to ~55px regardless of free space. break-word
     only kicks in once the bubble is already at its natural width. */
  overflow-wrap: break-word;
  box-shadow: var(--shadow-sm);
  animation: bubble-in 0.22s ease-out;
}

@keyframes bubble-in {
  from { opacity: 0; transform: translateY(7px) scale(0.98); }
  to   { opacity: 1; transform: none; }
}

.bubble.them {
  background: var(--bubble-them);
  border-bottom-left-radius: 5px;
}

.bubble.me {
  background: var(--grad-bubble-me);
  border-bottom-right-radius: 5px;
  /* The "me" bubble's background is a committed colour per palette (not the
     theme surface), so its text is pinned per palette too via
     --bubble-me-text rather than inheriting body's --text. */
  color: var(--bubble-me-text, #fff);
}
:root[data-palette="obsidian"] .bubble.me { border: 1px solid rgba(232, 199, 123, 0.28); }
:root[data-palette="cyber"] .bubble.me { border: 1px solid rgba(0, 240, 255, 0.42); }
:root[data-palette="cyber"] .bubble.them { border: 1px solid rgba(255, 46, 151, 0.26); }

/* Only the last bubble in a run gets the tail. */
.bubble-row:not(.last-of-run) .bubble.them { border-bottom-left-radius: var(--r-md); }
.bubble-row:not(.last-of-run) .bubble.me   { border-bottom-right-radius: var(--r-md); }

.bubble .text { white-space: pre-wrap; font-size: 14.5px; }

.bubble .meta {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 4px;
  margin-top: 2px;
  font-size: 10.5px;
  line-height: 1;
  opacity: 0.72;
  user-select: none;
}

.bubble.me .meta { color: rgba(255, 255, 255, 0.9); }
.bubble.them .meta { color: var(--text-dim); }

.bubble .meta svg { width: 15px; height: 15px; }
.bubble .meta .read { color: var(--tick-read); opacity: 1; }

.bubble.pending { opacity: 0.62; }

/* Attachments */

.bubble .attach {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 8px 6px 8px 2px;
  min-width: 210px;
}

.attach-icon {
  width: 40px;
  height: 40px;
  border-radius: 10px;
  background: rgba(0, 0, 0, 0.28);
  display: grid;
  place-items: center;
  flex: none;
}

.attach-icon svg { width: 19px; height: 19px; }

.attach-info { min-width: 0; flex: 1; }

.attach-name {
  font-size: 13.5px;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.attach-size { font-size: 11.5px; opacity: 0.7; }

.bubble img.preview-img {
  max-width: 100%;
  max-height: 340px;
  border-radius: 10px;
  display: block;
  cursor: pointer;
}

/* Voice notes */

.voice-player {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 4px 4px 4px 2px;
  min-width: 220px;
}

.voice-play-btn {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.28);
  color: inherit;
  display: grid;
  place-items: center;
  flex: none;
  transition: background 0.15s;
}

.voice-play-btn:hover:not(:disabled) { background: rgba(0, 0, 0, 0.4); }
.voice-play-btn:disabled { opacity: 0.5; }
.voice-play-btn svg { width: 15px; height: 15px; }

.voice-wave { flex: 1; height: 34px; }
.voice-time { font-size: 11px; opacity: 0.75; flex: none; font-variant-numeric: tabular-nums; }

.voice-transcribe-btn {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  color: inherit;
  opacity: 0.7;
  display: grid;
  place-items: center;
  flex: none;
  transition: background 0.15s, opacity 0.15s;
}

.voice-transcribe-btn:hover:not(:disabled) { background: rgba(0, 0, 0, 0.28); opacity: 1; }
.voice-transcribe-btn:disabled { opacity: 0.4; }
.voice-transcribe-btn svg { width: 14px; height: 14px; }

.voice-transcript {
  font-size: 12.5px;
  font-style: italic;
  opacity: 0.85;
  padding: 0 4px 2px;
  max-width: 260px;
}

/* Location & contact cards — same footprint as .attach so they line up */

.location-card,
.contact-card {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 8px 6px 8px 2px;
  min-width: 210px;
  text-decoration: none;
  color: inherit;
}

.copy-contact-btn {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  color: inherit;
  display: grid;
  place-items: center;
  flex: none;
  transition: background 0.15s;
}

.copy-contact-btn:hover { background: rgba(0, 0, 0, 0.2); }
.copy-contact-btn svg { width: 15px; height: 15px; }

/* Sender name label — groups only, above the first bubble of an incoming run */

.sender-label {
  font-size: 12px;
  font-weight: 700;
  margin: 0 2px 3px 4px;
  align-self: flex-start;
}

/* Typing indicator */

.typing-bubble {
  display: flex;
  gap: 4px;
  padding: 12px 15px;
  background: var(--bubble-them);
  border-radius: var(--r-md);
  border-bottom-left-radius: 5px;
  width: fit-content;
  margin-top: 13px;
}

.typing-bubble span {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--text-dim);
  animation: typing-dot 1.3s infinite;
}

.typing-bubble span:nth-child(2) { animation-delay: 0.18s; }
.typing-bubble span:nth-child(3) { animation-delay: 0.36s; }

@keyframes typing-dot {
  0%, 60%, 100% { transform: translateY(0);    opacity: 0.4; }
  30%           { transform: translateY(-5px); opacity: 1; }
}

/* Composer */

.composer {
  padding: 13px 20px 17px;
  background: var(--panel);
  border-top: 1px solid var(--border);
  position: relative;
}

/* ── Emoji / GIF / sticker picker ─────────────────────────── */

.media-picker {
  position: absolute;
  left: 16px;
  bottom: calc(100% + 8px);
  width: 340px;
  max-width: calc(100vw - 32px);
  height: 380px;
  display: flex;
  flex-direction: column;
  background: linear-gradient(160deg, var(--panel-2), var(--panel)), var(--raised);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-lg);
  backdrop-filter: var(--glass-blur, none);
  -webkit-backdrop-filter: var(--glass-blur, none);
  overflow: hidden;
  z-index: 40;
  animation: modal-rise 0.2s cubic-bezier(0.2, 0.8, 0.25, 1);
}

.mp-tabs {
  display: flex;
  gap: 4px;
  padding: 8px 8px 6px;
  border-bottom: 1px solid var(--border);
}
.mp-tabs button {
  flex: 1;
  padding: 7px;
  border-radius: var(--r-sm);
  font-size: 12.5px;
  font-weight: 650;
  color: var(--text-dim);
  transition: background 0.14s, color 0.14s;
}
.mp-tabs button.active { background: rgba(var(--accent-2-rgb), 0.16); color: var(--accent); }

.mp-search {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 8px 10px 4px;
  padding: 8px 11px;
  border-radius: 99px;
  background: var(--inset-bg);
}
.mp-search svg { width: 15px; height: 15px; color: var(--muted); flex: none; }
.mp-search input { flex: 1; background: none; border: none; outline: none; color: var(--text); font: inherit; font-size: 13px; }

.mp-cats {
  display: flex;
  gap: 2px;
  padding: 6px 8px;
  border-bottom: 1px solid var(--border);
  overflow-x: auto;
  scrollbar-width: none;
}
.mp-cats::-webkit-scrollbar { display: none; }
.mp-cat {
  flex: none;
  width: 30px;
  height: 30px;
  border-radius: 8px;
  font-size: 16px;
  line-height: 1;
  opacity: 0.6;
  transition: background 0.14s, opacity 0.14s;
}
.mp-cat:hover { opacity: 1; background: var(--hover-bg); }
.mp-cat.active { opacity: 1; background: rgba(var(--accent-2-rgb), 0.16); }

.mp-body { flex: 1; overflow-y: auto; padding: 8px; }

.mp-emoji-grid { display: grid; grid-template-columns: repeat(8, 1fr); gap: 2px; }
.mp-emoji {
  aspect-ratio: 1;
  font-size: 20px;
  line-height: 1;
  border-radius: 8px;
  transition: background 0.1s, transform 0.1s;
}
.mp-emoji:hover { background: var(--hover-bg); transform: scale(1.15); }

.mp-sticker-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px; }
.mp-sticker {
  aspect-ratio: 1;
  font-size: 34px;
  line-height: 1;
  border-radius: var(--r-md);
  background: var(--hover-bg-soft);
  transition: background 0.12s, transform 0.12s;
}
.mp-sticker:hover { background: var(--hover-bg-strong); transform: scale(1.06); }

.mp-gif-grid { columns: 2; column-gap: 6px; }
.mp-gif {
  display: block;
  width: 100%;
  margin-bottom: 6px;
  border-radius: 10px;
  overflow: hidden;
  break-inside: avoid;
  background: var(--inset-bg);
}
.mp-gif img { display: block; width: 100%; }
.mp-gif:hover { outline: 2px solid var(--accent); }

.mp-empty {
  padding: 32px 20px;
  text-align: center;
  color: var(--muted);
  font-size: 12.5px;
  line-height: 1.7;
}
.mp-empty code {
  background: var(--inset-bg);
  padding: 1px 5px;
  border-radius: 5px;
  font-size: 11.5px;
}

@media (max-width: 820px) {
  .media-picker { left: 8px; right: 8px; width: auto; }
}

/* ── Inline media: stickers & GIFs (no bubble chrome) ─────── */

.bubble:has(.inline-media),
.bubble:has(.text.jumbo) {
  background: transparent !important;
  border: none !important;
  box-shadow: none !important;
  padding: 2px 0 0 !important;
}
.bubble:has(.inline-media) .meta,
.bubble:has(.text.jumbo) .meta { color: var(--text-dim) !important; }
.bubble:has(.inline-media) .bubble-actions,
.bubble:has(.text.jumbo) .bubble-actions { background: var(--raised); }

.text.jumbo {
  font-size: 3.3rem;
  line-height: 1.15;
  white-space: pre-wrap;
}
.bubble-row.me .text.jumbo { text-align: right; }

.inline-media {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 96px;
  min-height: 96px;
  border-radius: 14px;
}
.inline-media.is-sticker img { max-width: 168px; max-height: 168px; }
.inline-media.is-gif img { max-width: 260px; max-height: 300px; border-radius: 12px; cursor: pointer; }
.inline-media-ph {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.5px;
  color: var(--muted);
  padding: 24px;
  background: var(--inset-bg);
  border-radius: 12px;
}

.composer-normal {
  display: flex;
  align-items: flex-end;
  gap: 10px;
}

/* Smart compose — quick-reply chips / completion, above the input */
.composer-suggest {
  display: flex;
  gap: 8px;
  padding: 0 2px 9px;
  overflow-x: auto;
  scrollbar-width: none;
}
.composer-suggest::-webkit-scrollbar { display: none; }

.suggest-chip {
  flex: none;
  max-width: 78vw;
  padding: 7px 14px;
  border-radius: 99px;
  background: rgba(var(--accent-2-rgb), 0.12);
  border: 1px solid rgba(var(--accent-2-rgb), 0.28);
  color: var(--accent);
  font-size: 13px;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: background 0.14s;
}
.suggest-chip:hover { background: rgba(var(--accent-2-rgb), 0.2); }

.suggest-chip.suggest-complete {
  background: var(--hover-bg);
  border-color: transparent;
  color: var(--text);
  font-weight: 500;
}
.suggest-chip.suggest-complete .sc-dim { color: var(--muted); }
.suggest-chip.suggest-complete kbd {
  margin-left: 8px;
  padding: 1px 6px;
  border-radius: 5px;
  background: var(--inset-bg);
  color: var(--muted);
  font-size: 10.5px;
  font-family: inherit;
}
@media (pointer: coarse) {
  .suggest-chip.suggest-complete kbd { display: none; }
}

.attach-menu-wrap { position: relative; flex: none; }

.attach-menu {
  position: absolute;
  bottom: 48px;
  left: 0;
  background: var(--raised);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-md);
  padding: 6px;
  display: flex;
  flex-direction: column;
  min-width: 190px;
  z-index: 20;
}

.attach-menu button {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border-radius: 8px;
  font-size: 13.5px;
  text-align: left;
  color: var(--text);
  transition: background 0.15s;
}

.attach-menu button svg { width: 17px; height: 17px; color: var(--text-dim); flex: none; }
.attach-menu button:hover { background: var(--hover-bg); }

/* Recording bar — replaces the normal composer row while recording */

.recording-bar {
  display: flex;
  align-items: center;
  gap: 12px;
}

.rec-dot {
  width: 11px;
  height: 11px;
  border-radius: 50%;
  background: var(--danger);
  flex: none;
  animation: rec-pulse 1.2s ease-in-out infinite;
}

@keyframes rec-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50%      { opacity: 0.4; transform: scale(0.8); }
}

.rec-time { font-weight: 650; font-size: 14px; font-variant-numeric: tabular-nums; }
.rec-hint {
  color: var(--muted);
  font-size: 13px;
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.rec-hint.transcribing { color: var(--text); }

/* Voice-to-text language picker, next to the mic button. "Voice note" (the
   first option, empty value) means plain recording — completely unchanged
   behavior. Any other value turns on live SpeechRecognition transcription
   for the recording that follows. Hidden entirely when the browser has no
   SpeechRecognition support. */
.speech-lang-select {
  flex: none;
  max-width: 96px;
  padding: 7px 8px;
  font-size: 12px;
  background: var(--inset-bg);
  border: 1.5px solid var(--border);
  border-radius: var(--r-sm);
  color: var(--text-dim);
  outline: none;
}
.speech-lang-select:focus { border-color: var(--border-lit); }

.composer-box {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: flex-end;
  gap: 8px;
  background: var(--inset-bg);
  border: 1.5px solid transparent;
  border-radius: var(--r-md);
  padding: 6px 8px 6px 14px;
  transition: border-color 0.18s;
}

.composer-box:focus-within { border-color: var(--border-lit); }

.composer textarea {
  flex: 1;
  /* No cols attribute on the element, so its browser-default intrinsic
     min-content is ~20 characters wide — combined with flex:1 and no
     min-width override, it refused to shrink past that no matter how
     narrow the composer got, which was the last piece forcing the whole
     composer row (and with it the page) wider than a phone screen. */
  min-width: 0;
  background: none;
  border: none;
  outline: none;
  resize: none;
  max-height: 132px;
  padding: 7px 0;
  font-size: 14.5px;
  line-height: 1.45;
}

.composer textarea::placeholder { color: var(--muted); }

.send-btn {
  width: 45px;
  height: 45px;
  border-radius: 50%;
  background: var(--grad-accent);
  color: var(--on-accent);
  display: grid;
  place-items: center;
  flex: none;
  box-shadow: 0 5px 16px rgba(var(--accent-2-rgb), 0.4);
  transition: transform 0.14s, filter 0.18s;
}

.send-btn:hover { transform: scale(1.06); }
.send-btn:active { transform: scale(0.96); }
.send-btn:disabled { opacity: 0.4; cursor: not-allowed; transform: none; }
.send-btn svg { width: 20px; height: 20px; }

/* Empty state */

.no-chat {
  display: grid;
  place-items: center;
  text-align: center;
  padding: 40px;
  background: var(--panel-2);
}

.no-chat .mark {
  width: 92px;
  height: 92px;
  border-radius: 26px;
  background: var(--grad-accent);
  display: grid;
  place-items: center;
  margin: 0 auto 22px;
  box-shadow: 0 14px 42px rgba(var(--accent-2-rgb), 0.35);
  opacity: 0.92;
}

.no-chat .mark svg { width: 44px; height: 44px; }
.no-chat h2 { font-size: 21px; font-weight: 650; margin-bottom: 9px; }
.no-chat p { color: var(--muted); font-size: 14px; max-width: 380px; line-height: 1.65; }

.e2e-pill {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  margin-top: 22px;
  padding: 7px 15px;
  border-radius: 99px;
  background: rgba(var(--online-rgb), 0.1);
  border: 1px solid rgba(var(--online-rgb), 0.25);
  color: var(--online);
  font-size: 12.5px;
  font-weight: 600;
}

.e2e-pill svg { width: 14px; height: 14px; }

/* Toast */

.toast {
  position: fixed;
  bottom: 26px;
  left: 50%;
  transform: translateX(-50%) translateY(90px);
  background: var(--raised);
  border: 1px solid var(--border);
  padding: 12px 22px;
  border-radius: 99px;
  box-shadow: var(--shadow-md);
  font-size: 13.5px;
  /* Above every overlay (status viewer 220, modals 210, calls 230) so an
     error toast is never hidden behind whatever the user was doing. */
  z-index: 9999;
  transition: transform 0.3s cubic-bezier(0.2, 0.9, 0.3, 1.2);
}

.toast.show { transform: translateX(-50%) translateY(0); }
.toast.error { border-color: var(--danger); color: var(--danger-text); }

/* Lightbox */

.lightbox {
  position: fixed;
  inset: 0;
  background: rgba(6, 6, 14, 0.93);
  display: grid;
  place-items: center;
  z-index: 200;
  padding: 40px;
  cursor: zoom-out;
}

.lightbox img {
  /* Viewport units, not %: a % max-height has nothing to resolve against inside the grid, so tall photos
     (phone screenshots) overflowed the screen and looked cut off. */
  max-width: calc(100vw - 80px);
  max-height: calc(100vh - 80px);
  object-fit: contain;
  border-radius: var(--r-md);
  box-shadow: var(--shadow-lg);
}

/* ═══════════════════════════════════════════════════════════
   Modals — generic backdrop/card, reused by contact-share,
   group creation, group info, and status compose/view.
   ═══════════════════════════════════════════════════════════ */

.modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(6, 6, 14, 0.62);
  display: grid;
  place-items: center;
  z-index: 210;
  padding: 20px;
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  animation: modal-fade 0.18s ease-out;
}

@keyframes modal-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes modal-rise {
  from { opacity: 0; transform: translateY(14px) scale(0.985); }
  to   { opacity: 1; transform: none; }
}

.modal-card {
  width: 100%;
  max-width: 440px;
  max-height: 88vh;
  overflow-y: auto;
  background: linear-gradient(160deg, var(--panel-2), var(--panel)), var(--raised);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-lg);
  padding: 28px 28px 24px;
  backdrop-filter: var(--glass-blur, none);
  -webkit-backdrop-filter: var(--glass-blur, none);
  animation: modal-rise 0.26s cubic-bezier(0.2, 0.8, 0.25, 1);
}

@media (prefers-reduced-motion: reduce) {
  .modal-backdrop, .modal-card { animation: none; }
}

.modal-card h3 {
  font-size: 19px;
  font-weight: 700;
  letter-spacing: -0.2px;
  margin-bottom: 20px;
}

.modal-card .field { margin-bottom: 16px; }

.modal-card .field > label {
  display: block;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--text-dim);
  letter-spacing: 0.1px;
  margin-bottom: 8px;
}

.modal-card .field input,
.modal-card .field textarea,
.modal-card .field select {
  width: 100%;
  padding: 12px 15px;
  background: var(--inset-bg);
  border: 1.5px solid transparent;
  border-radius: var(--r-md);
  outline: none;
  color: var(--text);
  font: inherit;
  transition: border-color 0.18s, box-shadow 0.18s, background 0.18s;
}

.modal-card .field input:focus,
.modal-card .field textarea:focus,
.modal-card .field select:focus {
  border-color: var(--border-lit);
  background: var(--panel-3);
  box-shadow: 0 0 0 3.5px rgba(var(--accent-2-rgb), 0.15);
}

/* Textarea — a comfortable minimum, drag to grow, never sideways. */
.field textarea,
.modal-card .field textarea {
  min-height: 88px;
  line-height: 1.5;
  resize: vertical;
}

/* Native <select> — strip the OS chrome, draw our own chevron. */
.field select,
.modal-card .field select {
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  padding-right: 40px;
  cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%238b8b9c' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 14px center;
}
.field select option,
.modal-card .field select option { background: var(--panel); color: var(--text); }

/* File inputs — keep them functional, dress the button, calm the rest. */
.field input[type="file"],
.modal-card .field input[type="file"] {
  padding: 9px 12px;
  font-size: 12.5px;
  color: var(--muted);
  cursor: pointer;
}
.field input[type="file"]::file-selector-button,
.modal-card .field input[type="file"]::file-selector-button {
  margin-right: 12px;
  padding: 8px 15px;
  border: none;
  border-radius: calc(var(--r-sm) - 2px);
  background: rgba(var(--accent-2-rgb), 0.16);
  color: var(--accent);
  font: inherit;
  font-weight: 650;
  cursor: pointer;
  transition: background 0.15s;
}
.field input[type="file"]::file-selector-button:hover,
.modal-card .field input[type="file"]::file-selector-button:hover {
  background: rgba(var(--accent-2-rgb), 0.26);
}

/* Attach row — a strip of chip buttons instead of three stacked file boxes.
   The real <input type=file> elements are visually hidden; these trigger them. */
.attach-row { display: flex; flex-wrap: wrap; gap: 8px; }

.attach-chip {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 9px 14px;
  border-radius: 99px;
  background: var(--hover-bg);
  border: 1.5px solid transparent;
  color: var(--text-dim);
  font-size: 13px;
  font-weight: 600;
  transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.attach-chip:hover { background: var(--hover-bg-strong); color: var(--text); }
.attach-chip svg { width: 15px; height: 15px; }
.attach-chip.filled {
  background: rgba(var(--accent-2-rgb), 0.16);
  border-color: rgba(var(--accent-2-rgb), 0.4);
  color: var(--accent);
}
.attach-chip.filled::after {
  content: "✕";
  font-size: 11px;
  margin-left: 2px;
  opacity: 0.7;
}
.attach-chip .attach-chip-name {
  max-width: 130px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.visually-hidden-file {
  position: absolute;
  width: 1px; height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  opacity: 0;
}

.modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  margin-top: 24px;
}

.btn-secondary {
  padding: 10px 18px;
  border-radius: var(--r-md);
  background: var(--hover-bg);
  color: var(--text);
  font-weight: 600;
  font-size: 14px;
  transition: background 0.15s;
}
.btn-secondary:hover { background: var(--hover-bg-strong); }

.btn-danger {
  padding: 10px 18px;
  border-radius: var(--r-sm);
  background: var(--danger-bg);
  color: var(--danger-text);
  font-weight: 600;
  font-size: 14px;
  transition: background 0.15s;
}
.btn-danger:hover { background: var(--danger-bg); filter: brightness(1.15); }

/* Delete-choice / forward-target modals — a plain vertical stack of full-
   width option buttons reads clearer here than the usual right-aligned
   Cancel/Confirm pair in .modal-actions. */
.modal-choice-stack {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.modal-choice-stack .btn-secondary,
.modal-choice-stack .btn-danger { width: 100%; text-align: center; }

.modal-card .btn-primary {
  width: auto;
  margin-top: 0;
  padding: 10px 20px;
}

.modal-card .btn-primary:disabled { opacity: 0.5; cursor: not-allowed; }

.clickable-avatar { cursor: pointer; transition: opacity 0.15s; }
.clickable-avatar:hover { opacity: 0.85; }

.edit-profile-row { display: flex; align-items: center; gap: 18px; margin-bottom: 6px; }

.edit-profile-hint {
  font-size: 11.5px;
  color: var(--muted);
  margin-top: 8px;
  max-width: 220px;
  line-height: 1.5;
}

/* Picker list inside modals — contact multi-select for groups / status audience */

.picker-list {
  max-height: 264px;
  overflow-y: auto;
  margin: 0 -6px 4px;
  padding: 4px;
  border-radius: var(--r-md);
  background: var(--inset-bg);
  border: 1.5px solid transparent;
}

.picker-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 9px 10px;
  border-radius: calc(var(--r-sm) - 2px);
  cursor: pointer;
  transition: background 0.15s;
}

.picker-row:hover { background: var(--hover-bg); }
.picker-row:has(input:checked) { background: rgba(var(--accent-2-rgb), 0.14); }
.picker-row .name { font-size: 13.5px; font-weight: 600; flex: 1; min-width: 0; }

/* Custom checkbox — used by every .picker-row and any bare modal checkbox.
   The native control is common to all these modals and looked like a raw
   OS widget dropped into the design. */
.picker-row input[type="checkbox"],
.modal-card .field > input[type="checkbox"] {
  -webkit-appearance: none;
  appearance: none;
  flex: none;
  width: 20px;
  height: 20px;
  margin: 0;
  border: 2px solid var(--muted);
  border-radius: 6px;
  background: var(--panel);
  display: grid;
  place-content: center;
  cursor: pointer;
  transition: background 0.14s, border-color 0.14s;
}
.picker-row input[type="checkbox"]::before,
.modal-card .field > input[type="checkbox"]::before {
  content: "";
  width: 11px;
  height: 11px;
  transform: scale(0);
  transition: transform 0.12s ease-out;
  background: var(--on-accent);
  clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
}
.picker-row input[type="checkbox"]:checked,
.modal-card .field > input[type="checkbox"]:checked {
  background: var(--accent-2);
  border-color: var(--accent-2);
}
.picker-row input[type="checkbox"]:checked::before,
.modal-card .field > input[type="checkbox"]:checked::before { transform: scale(1); }
.picker-row input[type="checkbox"]:focus-visible,
.modal-card .field > input[type="checkbox"]:focus-visible {
  outline: 2px solid var(--border-lit);
  outline-offset: 2px;
}

/* ═══════════════════════════════════════════════════════════
   QR code — my code / scan
   ═══════════════════════════════════════════════════════════ */

.qr-tabs {
  display: flex;
  gap: 6px;
  background: var(--inset-bg);
  border-radius: 10px;
  padding: 4px;
  margin-bottom: 18px;
}

.qr-tab {
  flex: 1;
  padding: 8px;
  border-radius: 7px;
  font-size: 13px;
  font-weight: 650;
  color: var(--text-dim);
  transition: background 0.15s, color 0.15s;
}

.qr-tab.active { background: var(--accent-2); color: var(--on-accent); }

.qr-panel { text-align: center; }

.qr-image {
  width: 100%;
  max-width: 240px;
  border-radius: var(--r-md);
  background: #fff;
  padding: 12px;
}

.qr-hint {
  color: var(--muted);
  font-size: 12.5px;
  margin-top: 12px;
  line-height: 1.5;
}

/* Chat wallpaper picker */

.wallpaper-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
  margin-bottom: 4px;
}

.wallpaper-swatch {
  height: 64px;
  border-radius: var(--r-md);
  border: 2px solid transparent;
  color: #fff;
  font-size: 12px;
  font-weight: 650;
  display: flex;
  align-items: flex-end;
  justify-content: flex-start;
  padding: 8px;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.6);
  transition: border-color 0.15s, transform 0.1s;
}

.wallpaper-swatch:hover { transform: scale(1.02); }
.wallpaper-swatch.active { border-color: var(--accent); }

.wallpaper-photo-tile {
  cursor: pointer;
  background-color: var(--hover-bg-soft);
  background-size: cover;
  background-position: center;
  border: 2px dashed var(--border-lit);
}
.wallpaper-photo-tile.active { border-style: solid; border-color: var(--accent); }
.wallpaper-photo-tile span {
  background: rgba(0, 0, 0, 0.45);
  padding: 3px 7px;
  border-radius: 6px;
}

.wallpaper-fit-row { margin-top: 4px; }

.wallpaper-fit-label {
  display: block;
  font-size: 12px;
  font-weight: 600;
  color: var(--text-dim);
  letter-spacing: 0.4px;
  text-transform: uppercase;
  margin-bottom: 7px;
}

.wallpaper-fit-tabs { padding: 0; overflow: visible; align-items: center; flex-wrap: wrap; }

.theme-toggle-tabs { padding: 0; overflow: visible; }

.wallpaper-fit-apply { margin-left: auto; font-size: 13px; }

/* Reminders */

.reminder-notice {
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--warn-text);
  background: var(--warn-bg);
  border: 1px solid var(--warn-text);
  border-radius: var(--r-sm);
  padding: 9px 12px;
  margin-bottom: 14px;
}

.reminder-list {
  max-height: 260px;
  overflow-y: auto;
  margin: 18px -4px -4px;
  padding-top: 14px;
  border-top: 1px solid var(--border);
}

.reminder-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 4px;
  border-radius: 10px;
}

.reminder-item:hover { background: var(--hover-bg-soft); }

.reminder-item-emoji {
  flex: none;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: var(--hover-bg);
  display: grid;
  place-items: center;
  font-size: 16px;
}

.reminder-item-body { flex: 1; min-width: 0; }

.reminder-item-content {
  font-size: 13.5px;
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.reminder-item-when {
  font-size: 11.5px;
  color: var(--text-dim);
  margin-top: 2px;
}

.reminder-item-delete { width: 30px; height: 30px; flex: none; }
.reminder-item-delete:hover { color: var(--danger); }

#qr-scan-video {
  width: 100%;
  max-width: 280px;
  aspect-ratio: 1;
  border-radius: var(--r-md);
  background: #000;
  object-fit: cover;
}

/* ═══════════════════════════════════════════════════════════
   Status tray
   ═══════════════════════════════════════════════════════════ */

.status-tray {
  display: flex;
  gap: 14px;
  padding: 14px 16px 6px;
  overflow-x: auto;
  border-bottom: 1px solid var(--border);
}

.status-tray:empty { display: none; }

/* Many statuses: a thin scrollbar and a soft fade on the side that has more. */
.status-tray { scrollbar-width: thin; scroll-snap-type: x proximity; overscroll-behavior-x: contain; }
.status-tray::-webkit-scrollbar { height: 4px; }
.status-tray::-webkit-scrollbar-thumb { background: transparent; border-radius: 4px; }
.status-tray:hover::-webkit-scrollbar-thumb { background: var(--hover-bg-strong); }
.status-tray-item { scroll-snap-align: start; }
.status-tray.more-right { -webkit-mask-image: linear-gradient(90deg, #000 85%, transparent); mask-image: linear-gradient(90deg, #000 85%, transparent); }
.status-tray.more-left { -webkit-mask-image: linear-gradient(90deg, transparent, #000 15%); mask-image: linear-gradient(90deg, transparent, #000 15%); }
.status-tray.more-left.more-right {
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 15%, #000 85%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 15%, #000 85%, transparent);
}

.status-tray-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  cursor: pointer;
  flex: none;
  width: 58px;
}

.status-ring {
  width: 52px;
  height: 52px;
  border-radius: 50%;
  padding: 2.5px;
  background: var(--muted);
  display: grid;
  place-items: center;
  position: relative;
}

.status-ring.unseen { background: conic-gradient(var(--accent), var(--online), var(--accent)); }
.status-ring.mine { background: var(--accent); }

.status-ring .avatar { width: 100%; height: 100%; border: 2px solid var(--panel); }

.status-mention-dot {
  position: absolute;
  right: -2px;
  top: -2px;
  width: 17px;
  height: 17px;
  border-radius: 50%;
  background: var(--danger);
  color: #fff;
  font-size: 11px;
  font-weight: 700;
  display: grid;
  place-items: center;
  border: 2px solid var(--panel);
}

.status-tray-item .label {
  font-size: 10.5px;
  color: var(--text-dim);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 58px;
}

.status-add-badge {
  position: relative;
}

.status-add-badge::after {
  content: '+';
  position: absolute;
  right: -2px;
  bottom: -2px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--accent-2);
  color: var(--on-accent);
  font-size: 13px;
  font-weight: 700;
  display: grid;
  place-items: center;
  border: 2px solid var(--panel);
}

/* Same look as .status-add-badge::after, but a real button — shown once you
   already have a status, so there's still a way to post another one instead
   of the ring only ever reopening the viewer. */
.status-add-btn {
  position: absolute;
  right: -2px;
  bottom: -2px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--accent-2);
  color: var(--on-accent);
  font-size: 13px;
  font-weight: 700;
  display: grid;
  place-items: center;
  border: 2px solid var(--panel);
  padding: 0;
  line-height: 1;
  cursor: pointer;
}

/* Status viewer — .status-viewer is the full-screen dimmed backdrop;
   .status-card is the actual phone-story-shaped box centered inside it.
   Stories are inherently a phone-shaped format even on a wide desktop
   window (Instagram/WhatsApp Web both constrain it the same way) — a small
   photo full-bleed-stretched across an entire desktop monitor looked wrong. */

.status-viewer {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.88);
  z-index: 220;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 28px;
}

.status-card {
  position: relative;
  width: 100%;
  max-width: 420px;
  aspect-ratio: 9 / 16;
  max-height: 100%;
  background: #000;
  border-radius: var(--r-lg);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  box-shadow: var(--shadow-lg);
}

.status-viewer-bars {
  display: flex;
  gap: 5px;
  padding: 10px 12px 0;
}

.status-viewer-bars .bar {
  flex: 1;
  height: 3px;
  border-radius: 3px;
  background: rgba(255, 255, 255, 0.25);
  overflow: hidden;
}

.status-viewer-bars .bar > i {
  display: block;
  height: 100%;
  width: 0%;
  background: #fff;
  transition: width linear;
}

.status-viewer-bars .bar.done > i { width: 100%; transition: none; }

.status-viewer-head {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 16px;
  color: #fff;
}

.status-viewer-head .grow { flex: 1; }
.status-viewer-head .name { font-weight: 650; font-size: 14.5px; }
.status-viewer-head .time { font-size: 12px; opacity: 0.7; }

.status-viewer-body {
  position: relative; /* containing block for .status-viewer-tap-zones below */
  flex: 1;
  overflow: hidden;
}

#sv-content {
  width: 100%;
  height: 100%;
  display: grid;
  place-items: center;
  padding: 20px;
}

.status-viewer-body img,
.status-viewer-body video {
  max-width: 100%;
  max-height: 100%;
  border-radius: var(--r-md);
}

/* Sits above .status-viewer-tap-zones (z-index 1) so it stays clickable —
   native <video controls> would be trapped underneath that overlay instead. */
.status-video-mute-btn {
  position: absolute;
  right: 14px;
  bottom: 14px;
  z-index: 2;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.5);
  color: #fff;
  font-size: 15px;
  display: grid;
  place-items: center;
  cursor: pointer;
}

/* Single stacking container for both the "mentioned you" banner and the
   @name chip row — they used to each be position:absolute at top:0
   independently and silently overlapped whenever both were present at
   once (the exact case of a poster mentioning the person now viewing it). */
.status-top-overlay {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  z-index: 2;
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 14px 16px;
}

.status-mentions {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.status-mention-chip {
  background: rgba(0, 0, 0, 0.5);
  color: #fff;
  font-size: 12.5px;
  font-weight: 600;
  padding: 4px 10px;
  border-radius: 99px;
}

.status-mentioned-banner {
  padding: 6px 12px;
  background: rgba(0, 0, 0, 0.5);
  border-radius: 99px;
  color: #fff;
  font-size: 13px;
  font-weight: 600;
  text-align: center;
  align-self: center;
}

.status-viewer-body .status-text {
  color: #fff;
  font-size: 26px;
  font-weight: 650;
  text-align: center;
  line-height: 1.4;
  max-width: 480px;
}

.status-caption {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 24px 16px 14px;
  background: linear-gradient(transparent, rgba(0, 0, 0, 0.75));
  color: #fff;
  font-size: 14px;
  line-height: 1.4;
  text-align: center;
}

/* Scoped to the body's own rectangle only — NOT the whole card — so it can
   never sit on top of (and swallow clicks meant for) the seenby line or
   reply input, which are separate siblings below the body. */
.status-viewer-tap-zones {
  position: absolute;
  inset: 0;
  display: flex;
  z-index: 1;
}

.status-viewer-tap-zones > div { flex: 1; }

.status-seenby {
  color: rgba(255, 255, 255, 0.85);
  text-align: center;
  padding: 10px 16px;
  font-size: 12.5px;
  cursor: pointer;
}

.status-seenby:empty { display: none; }

.status-seenby-panel {
  max-height: 40%;
  overflow-y: auto;
  padding: 4px 14px 10px;
}

.seenby-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 0;
  color: #fff;
}

.seenby-row .name { flex: 1; font-size: 13px; font-weight: 600; }
.seenby-row .time { font-size: 11px; color: rgba(255, 255, 255, 0.6); }

.status-reply-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 14px 16px;
}

.status-reply-bar input {
  flex: 1;
  background: rgba(255, 255, 255, 0.12);
  border: 1px solid rgba(255, 255, 255, 0.2);
  border-radius: 99px;
  padding: 10px 16px;
  color: #fff;
  outline: none;
  font-size: 13.5px;
}

.status-reply-bar input::placeholder { color: rgba(255, 255, 255, 0.55); }
.status-reply-bar .icon-btn { color: #fff; flex: none; }

/* ═══════════════════════════════════════════════════════════
   Groups
   ═══════════════════════════════════════════════════════════ */

.member-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 9px 4px;
}

.member-row .name { flex: 1; font-size: 13.5px; font-weight: 600; }
.member-row .role-tag {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.3px;
  text-transform: uppercase;
  color: var(--muted);
}

.member-row .remove-member-btn {
  color: var(--danger);
  font-size: 12px;
  font-weight: 600;
}

/* ═══════════════════════════════════════════════════════════
   Bubble actions — react / reply / edit / delete on hover
   ═══════════════════════════════════════════════════════════ */

.bubble { position: relative; }

.bubble-actions {
  position: absolute;
  top: -16px;
  /* Anchored to extend TOWARD the screen center, not toward the near edge:
     "them" bubbles hug the left edge, so the bar grows rightward from
     their left corner; .bubble.me below flips this for bubbles hugging
     the right edge. Six icons is ~174px wide — on a phone-width screen,
     anchoring toward the near edge instead (which is what this used to
     do) ran the later icons (reply/forward/edit/delete) off-screen
     entirely, unreachable no matter how you scrolled. */
  left: 8px;
  display: flex;
  gap: 2px;
  background: var(--raised);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 3px;
  box-shadow: var(--shadow-sm);
  opacity: 0;
  transform: translateY(4px);
  pointer-events: none;
  transition: opacity 0.15s, transform 0.15s;
  z-index: 5;
}

.bubble.me .bubble-actions { left: auto; right: 8px; }

.bubble:hover .bubble-actions {
  opacity: 1;
  transform: none;
  pointer-events: auto;
}

.bubble-action-btn {
  width: 26px;
  height: 26px;
  border-radius: 7px;
  display: grid;
  place-items: center;
  color: var(--text-dim);
  transition: background 0.15s, color 0.15s;
}

.bubble-action-btn svg { width: 14px; height: 14px; }
.bubble-action-btn:hover { background: var(--hover-bg); color: var(--text); }
.bubble-action-btn.star-btn.active { color: #FDCB6E; }
.bubble-action-btn.star-btn.active svg { fill: currentColor; }

.reaction-picker {
  position: fixed;
  display: flex;
  gap: 4px;
  background: var(--raised);
  border: 1px solid var(--border);
  border-radius: 99px;
  padding: 6px 8px;
  box-shadow: var(--shadow-md);
  z-index: 220;
}

.reaction-picker button {
  font-size: 19px;
  padding: 3px;
  border-radius: 50%;
  transition: transform 0.12s, background 0.12s;
}

.reaction-picker button:hover { transform: scale(1.25); background: var(--hover-bg); }

.reaction-row {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-top: 4px;
}

.reaction-pill {
  display: flex;
  align-items: center;
  gap: 3px;
  padding: 2px 7px;
  border-radius: 99px;
  background: rgba(0, 0, 0, 0.22);
  font-size: 12px;
  line-height: 1.6;
  transition: background 0.15s, border-color 0.15s;
  border: 1px solid transparent;
}

.reaction-pill.mine {
  background: rgba(var(--accent-rgb), 0.18);
  border-color: rgba(var(--accent-rgb), 0.4);
}

.reaction-pill:hover { background: rgba(255, 255, 255, 0.12); }

/* ═══════════════════════════════════════════════════════════
   Reply quote (inside a bubble) & edited/deleted states
   ═══════════════════════════════════════════════════════════ */

.reply-quote {
  border-left: 3px solid var(--accent);
  background: rgba(0, 0, 0, 0.18);
  border-radius: 6px;
  padding: 5px 9px;
  margin-bottom: 6px;
  cursor: pointer;
  max-width: 100%;
  overflow: hidden;
}

.bubble.me .reply-quote { background: rgba(0, 0, 0, 0.14); border-left-color: rgba(255, 255, 255, 0.7); }

.reply-quote-name {
  font-size: 12px;
  font-weight: 700;
  color: var(--accent);
  margin-bottom: 1px;
}

.bubble.me .reply-quote-name { color: rgba(255, 255, 255, 0.85); }

.reply-quote-text {
  font-size: 12.5px;
  color: var(--text-dim);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.bubble.me .reply-quote-text { color: rgba(255, 255, 255, 0.75); }

.edited-tag {
  font-size: 10.5px;
  opacity: 0.65;
  font-style: italic;
}

.deleted-text { opacity: 0.55; font-style: italic; }

@keyframes bubble-flash {
  0%, 100% { box-shadow: var(--shadow-sm); }
  30%      { box-shadow: 0 0 0 3px var(--accent); }
}

.bubble.flash { animation: bubble-flash 1.2s ease; }

/* ═══════════════════════════════════════════════════════════
   Reply bar / edit bar — replace nothing, sit above the composer
   ═══════════════════════════════════════════════════════════ */

.reply-bar, .edit-bar {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  margin-bottom: 8px;
  background: rgba(0, 0, 0, 0.22);
  border-left: 3px solid var(--accent);
  border-radius: 8px;
}

.reply-bar-body { flex: 1; min-width: 0; }

.reply-bar .reply-bar-name { font-size: 12.5px; font-weight: 700; color: var(--accent); }
.reply-bar .reply-bar-text {
  font-size: 13px;
  color: var(--text-dim);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.edit-bar .reply-bar-name { font-size: 12.5px; font-weight: 700; color: var(--accent); }

/* ═══════════════════════════════════════════════════════════
   Message requests
   ═══════════════════════════════════════════════════════════ */

.chat-item.is-request .name { font-weight: 800; }
.chat-item.is-request .preview { color: var(--accent); font-weight: 700; }

.waiting-notice {
  padding: 10px 20px;
  text-align: center;
  font-size: 12.5px;
  color: var(--muted);
  background: var(--panel);
  border-top: 1px solid var(--border);
}

.request-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 14px 20px;
  background: var(--panel);
  border-top: 1px solid var(--border);
}

.request-bar span { font-size: 13.5px; color: var(--text-dim); }
.request-bar-actions { display: flex; gap: 10px; flex: none; }
.request-bar-actions .btn-primary,
.request-bar-actions .btn-secondary { padding: 9px 18px; }

/* ═══════════════════════════════════════════════════════════
   Calling
   ═══════════════════════════════════════════════════════════ */

.call-overlay {
  position: fixed;
  inset: 0;
  background: var(--call-bg);
  z-index: 230;
  display: flex;
  align-items: center;
  justify-content: center;
}

.call-remote-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  background: #000;
  display: none;
}

.call-overlay.video-call .call-remote-video { display: block; }

.call-content {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  padding: 40px;
  width: 100%;
  max-width: 420px;
}

.call-overlay.video-call .call-content {
  height: 100%;
  justify-content: space-between;
  padding-bottom: 56px;
}

.call-name { font-size: 22px; font-weight: 700; color: #fff; margin-top: 6px; }
.call-status {
  font-size: 14px;
  color: rgba(255, 255, 255, 0.65);
  margin-bottom: 20px;
}

.call-overlay.video-call .call-name,
.call-overlay.video-call .call-status { text-shadow: 0 2px 8px rgba(0, 0, 0, 0.6); }

.call-local-video {
  position: absolute;
  top: 24px;
  right: 24px;
  width: 120px;
  height: 160px;
  border-radius: var(--r-md);
  object-fit: cover;
  box-shadow: var(--shadow-lg);
  background: #000;
  transform: scaleX(-1); /* mirror, like a selfie camera */
  cursor: pointer;
  z-index: 2;
}

/* Tap the self-view to make it the full frame (tap again to shrink). */
.call-local-video.expanded {
  inset: 0;
  top: 0;
  right: 0;
  width: 100%;
  height: 100%;
  border-radius: 0;
  box-shadow: none;
  z-index: 1;
}

/* Keep the controls above an expanded self-view. */
.call-actions { position: relative; z-index: 6; }

.call-actions {
  display: flex;
  align-items: center;
  gap: 22px;
}

.call-in-progress-controls { display: flex; gap: 18px; }
.call-in-progress-controls.hidden { display: none; }

/* Video-call filter strip — sits just above the action buttons. */
.call-filter-bar {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 96px;
  display: flex;
  gap: 8px;
  padding: 8px 16px;
  overflow-x: auto;
  scrollbar-width: none;
  z-index: 3;
}
.call-filter-bar::-webkit-scrollbar { display: none; }
.call-filter-bar.hidden { display: none; }

.call-filter-chip {
  flex: none;
  padding: 8px 16px;
  border-radius: 99px;
  background: rgba(255, 255, 255, 0.16);
  color: #fff;
  font-size: 13px;
  font-weight: 600;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  transition: background 0.14s;
}
.call-filter-chip.active { background: var(--accent-2); color: var(--on-accent); }

#call-filter-btn.active,
#call-flip-btn.active { background: var(--accent-2); }

.call-btn {
  width: 58px;
  height: 58px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  color: #fff;
  transition: transform 0.14s, filter 0.15s;
}

.call-btn svg { width: 24px; height: 24px; }
.call-btn:hover { transform: scale(1.06); }
.call-btn:active { transform: scale(0.96); }

.call-btn.accept { background: var(--online); box-shadow: 0 8px 22px rgba(var(--online-rgb), 0.4); }
.call-btn.decline { background: var(--danger); box-shadow: 0 8px 22px rgba(var(--danger-rgb), 0.4); }

.call-btn.secondary {
  width: 50px;
  height: 50px;
  background: rgba(255, 255, 255, 0.14);
}

.call-btn.secondary.active { background: var(--danger); }
/* Screen-share "active" means ON (good), not muted/off — use the accent. */
#call-screen-btn.active { background: var(--accent-2); color: var(--on-accent); }
.call-btn.secondary svg { width: 20px; height: 20px; }

/* ═══════════════════════════════════════════════════════════
   Group calling — "start or join a room" banner + tile-grid overlay.
   Reuses .call-actions/.call-btn from 1:1 calling above as-is.
   ═══════════════════════════════════════════════════════════ */

.group-call-banner {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 20px;
  background: rgba(var(--online-rgb), 0.1);
  border-top: 1px solid rgba(var(--online-rgb), 0.25);
  border-bottom: 1px solid rgba(var(--online-rgb), 0.25);
  font-size: 13px;
}

.group-call-banner-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--online);
  flex: none;
  animation: rec-pulse 1.4s ease-in-out infinite;
}

#group-call-banner-text { flex: 1; font-weight: 600; }
#group-call-banner-join { padding: 7px 16px; font-size: 12.5px; }

.group-call-overlay {
  position: fixed;
  inset: 0;
  background: var(--call-bg);
  z-index: 230;
  display: flex;
  flex-direction: column;
  padding: 24px;
  gap: 20px;
}

.group-call-head { text-align: center; }
.group-call-title { font-size: 17px; font-weight: 700; color: #fff; }

.group-call-tiles {
  flex: 1;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  grid-auto-rows: minmax(140px, 1fr);
  gap: 12px;
  align-content: center;
  overflow-y: auto;
}

.group-call-tile {
  position: relative;
  border-radius: var(--r-md);
  background: rgba(255, 255, 255, 0.06);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}

.group-call-video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  background: #000;
}

.group-call-tile-name {
  position: absolute;
  left: 10px;
  bottom: 8px;
  font-size: 12.5px;
  font-weight: 650;
  color: #fff;
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.7);
}

.group-call-overlay .call-actions { justify-content: center; padding-bottom: 8px; }

/* ═══════════════════════════════════════════════════════════
   AI Chat — Ollama-backed assistant, deliberately NOT styled like
   an encrypted conversation (no ticks/reactions/replies — there's
   no delivery state or encryption to show here).
   ═══════════════════════════════════════════════════════════ */

.ai-chat-card {
  max-width: 560px;
  width: 100%;
  height: 78vh;
  max-height: 720px;
  padding: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  position: relative;
  background: var(--raised);
}

/* A living, animated colour mesh — the specific thing that separates
   "an AI feature" from "a form with a text box". Brand-toned (blue/
   cyan/violet from --grad-accent's palette) rather than a flat panel. */
.ai-chat-card::before {
  content: '';
  position: absolute;
  inset: -10%;
  background:
    radial-gradient(48% 40% at 18% 12%, rgba(124, 92, 255, 0.32), transparent 65%),
    radial-gradient(44% 38% at 88% 18%, rgba(0, 194, 255, 0.28), transparent 65%),
    radial-gradient(50% 45% at 50% 105%, rgba(46, 230, 166, 0.16), transparent 65%);
  filter: blur(50px);
  animation: mesh-drift 18s ease-in-out infinite alternate;
  z-index: 0;
}
@keyframes mesh-drift {
  from { transform: translate(0, 0) scale(1); }
  to   { transform: translate(-3%, 3%) scale(1.1); }
}
@media (prefers-reduced-motion: reduce) { .ai-chat-card::before { animation: none; } }

.ai-chat-head, .ai-chat-body, .ai-chat-composer { position: relative; z-index: 1; }

.ai-chat-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 18px 20px;
  flex: none;
}

.ai-chat-head .ai-chat-icon {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--grad-accent);
  color: var(--on-accent);
  flex: none;
}

.ai-chat-head .ai-chat-icon svg { width: 18px; height: 18px; }

.ai-chat-head h3 { font-size: 15.5px; font-weight: 700; margin: 0; }
.ai-chat-head .ai-chat-sub { font-size: 12px; color: var(--text-dim); margin-top: 1px; }

.ai-chat-close { margin-left: auto; }

.ai-chat-body {
  flex: 1;
  overflow-y: auto;
  padding: 18px 20px;
  display: flex;
  flex-direction: column;
}

.ai-chat-empty {
  margin: auto;
  text-align: center;
  color: var(--text);
  font-size: 23px;
  font-weight: 700;
  letter-spacing: -0.4px;
  max-width: 320px;
  line-height: 1.35;
}

.ai-chat-thinking {
  align-self: flex-start;
  font-size: 13px;
  color: var(--text-dim);
  font-style: italic;
  padding: 8px 12px;
}

.ai-chat-error {
  align-self: stretch;
  background: var(--danger-bg);
  border: 1px solid var(--danger);
  color: var(--danger-text);
  border-radius: var(--r-sm);
  padding: 10px 12px;
  font-size: 12.5px;
  margin-top: 6px;
  line-height: 1.5;
}

.ai-chat-composer {
  display: flex;
  align-items: flex-end;
  gap: 10px;
  padding: 14px 16px 18px;
  flex: none;
}

/* A floating pill, not a bar flush with the edge — this is the "Ask
   anything..." reference: a clean surface resting ON the mesh rather
   than a form field sitting inside a bordered panel. */
.ai-chat-composer textarea {
  flex: 1;
  resize: none;
  max-height: 120px;
  padding: 13px 16px;
  background: var(--panel);
  backdrop-filter: var(--glass-blur, none);
  -webkit-backdrop-filter: var(--glass-blur, none);
  border: 1px solid var(--border);
  border-radius: 18px;
  outline: none;
  font: inherit;
  color: var(--text);
  box-shadow: var(--shadow-md);
  transition: border-color var(--dur-base) var(--ease-out), box-shadow var(--dur-base) var(--ease-out);
}

.ai-chat-composer textarea:focus {
  border-color: var(--border-lit);
  box-shadow: var(--shadow-md), 0 0 0 4px rgba(var(--accent-2-rgb), 0.14);
}

.ai-chat-send {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--accent-2);
  color: var(--on-accent);
  flex: none;
  transition: opacity 0.15s;
}

.ai-chat-send:disabled { opacity: 0.5; }
.ai-chat-send svg { width: 18px; height: 18px; }

/* Offline knowledge-quiz fallback, offered when the AI can't be reached. */

.ai-quiz-card {
  align-self: stretch;
  background: rgba(var(--accent-rgb), 0.08);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: 14px 16px;
  margin-top: 8px;
}

.ai-quiz-intro { font-size: 13.5px; line-height: 1.5; margin-bottom: 12px; }

.ai-quiz-progress {
  font-size: 11.5px;
  font-weight: 650;
  letter-spacing: 0.3px;
  text-transform: uppercase;
  color: var(--text-dim);
  margin-bottom: 8px;
}

.ai-quiz-question { font-size: 14.5px; font-weight: 650; margin-bottom: 12px; }

.ai-quiz-options { display: flex; flex-direction: column; gap: 8px; }

.ai-quiz-option {
  text-align: left;
  padding: 10px 13px;
  border-radius: var(--r-sm);
  background: var(--hover-bg-soft);
  border: 1.5px solid var(--border);
  font-size: 13.5px;
  color: var(--text);
  transition: background 0.15s, border-color 0.15s;
}

.ai-quiz-option:not(:disabled):hover { background: var(--hover-bg); }
.ai-quiz-option:disabled { cursor: default; }
.ai-quiz-option.correct { border-color: var(--online); background: rgba(var(--online-rgb), 0.14); }
.ai-quiz-option.wrong { border-color: var(--danger); background: rgba(var(--danger-rgb), 0.14); }

.ai-quiz-next, .ai-quiz-again, .ai-quiz-start {
  margin-top: 12px;
  width: 100%;
}

.ai-games-menu { display: flex; flex-direction: column; gap: 8px; }
.ai-games-menu button { width: 100%; }

.ai-game-back {
  display: block;
  font-size: 12px;
  font-weight: 650;
  color: var(--accent);
  margin-bottom: 10px;
}
.ai-game-back:hover { text-decoration: underline; }

/* Chess — pass-and-play */

.ai-chess-status {
  text-align: center;
  font-size: 13px;
  font-weight: 650;
  margin-bottom: 10px;
}

.ai-chess-board {
  display: grid;
  grid-template-columns: repeat(8, 1fr);
  width: min(336px, 100%);
  aspect-ratio: 1;
  margin: 0 auto;
  border: 2px solid var(--border);
  border-radius: 6px;
  overflow: hidden;
}

.ai-chess-sq {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 1;
  cursor: pointer;
  user-select: none;
}

.ai-chess-sq.light { background: rgba(255, 255, 255, 0.10); }
.ai-chess-sq.dark  { background: rgba(0, 0, 0, 0.26); }
.ai-chess-sq.selected { background: rgba(var(--accent-rgb), 0.55) !important; }
.ai-chess-sq.legal { background: rgba(var(--online-rgb), 0.32) !important; }
.ai-chess-sq.king-check { background: rgba(255, 90, 90, 0.5) !important; }

.ai-chess-piece { font-size: min(6vw, 26px); line-height: 1; }
.ai-chess-piece.w { color: #fff; text-shadow: 0 0 2px #000, 0 0 4px #000; }
.ai-chess-piece.b { color: #0B0B14; text-shadow: 0 0 2px #fff, 0 0 3px #fff; }

.ai-chess-new { width: 100%; margin-top: 12px; }

/* Sliding puzzle */

.ai-puzzle-board {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  width: min(280px, 100%);
  gap: 6px;
  margin: 0 auto;
}

.ai-puzzle-tile {
  aspect-ratio: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 20px;
  font-weight: 700;
  border-radius: 8px;
  background: var(--hover-bg);
  border: 1px solid var(--border);
  color: var(--text);
  transition: background 0.15s;
}

.ai-puzzle-tile:not(.blank):hover { background: var(--hover-bg-strong); }
.ai-puzzle-tile.blank { background: transparent; border-color: transparent; cursor: default; }

.ai-puzzle-new { width: 100%; margin-top: 12px; }

/* Win celebration — a staggered little pop-and-spin ripple across the
   board (see the per-tile animation-delay set in puzzleGame()'s render()),
   plus a color swap so a solved board reads as solved at a glance instead
   of just the status line changing. */
.ai-puzzle-board.solved .ai-puzzle-tile:not(.blank) {
  background: rgba(var(--online-rgb), 0.22);
  border-color: var(--online);
  animation: ai-puzzle-win 0.5s ease both;
}

@keyframes ai-puzzle-win {
  0%   { transform: scale(1) rotate(0deg); }
  45%  { transform: scale(1.18) rotate(-6deg); }
  75%  { transform: scale(0.94) rotate(3deg); }
  100% { transform: scale(1) rotate(0deg); }
}

.ai-quiz-progress.solved-status { color: var(--online); font-weight: 700; }

@media (max-width: 820px) {
  .ai-chat-card { height: 100%; max-height: none; border-radius: 0; }
}

/* ═══════════════════════════════════════════════════════════
   Live games — invite banner + Ludo + Candy Crush duel
   ═══════════════════════════════════════════════════════════ */

/* "Hey ARM" voice-command indicator — a small always-on-top pill, distinct
   from the game-invite-banner below (also fixed+bottom) so the two never
   collide: this one sits higher and to the side rather than dead-center. */
.voice-command-badge {
  position: fixed;
  bottom: 26px;
  right: 26px;
  max-width: 280px;
  padding: 9px 16px;
  background: var(--raised);
  border: 1px solid var(--border-lit);
  border-radius: 999px;
  box-shadow: var(--shadow-md);
  font-size: 12.5px;
  font-weight: 600;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  z-index: 235;
  animation: voice-badge-pulse 1.6s ease-in-out infinite;
}

@keyframes voice-badge-pulse {
  0%, 100% { box-shadow: var(--shadow-md); }
  50% { box-shadow: 0 0 0 4px rgba(var(--accent-2-rgb), 0.18), var(--shadow-md); }
}

.game-invite-banner {
  position: fixed;
  bottom: 26px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: 14px;
  background: var(--raised);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 10px 12px 10px 18px;
  box-shadow: var(--shadow-lg);
  z-index: 240;
}

.game-invite-emoji { font-size: 24px; flex: none; }
.game-invite-body { flex: none; }
.game-invite-title { font-size: 13.5px; font-weight: 650; }
.game-invite-sub { font-size: 11.5px; color: var(--text-dim); }
.game-invite-banner .btn-secondary,
.game-invite-banner .btn-primary { width: auto; margin-top: 0; padding: 9px 16px; font-size: 13px; }

.live-game-card { max-width: 520px; }

/* Ludo */

.ludo-board {
  display: grid;
  grid-template-columns: repeat(14, 1fr);
  grid-template-rows: repeat(14, 1fr);
  gap: 1px;
  aspect-ratio: 1;
  background: var(--inset-bg);
  border-radius: var(--r-sm);
  padding: 4px;
  margin-bottom: 14px;
}

.ludo-cell {
  background: var(--panel-2);
  border-radius: 3px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1px;
  flex-wrap: wrap;
}

.ludo-cell.safe { background: rgba(var(--online-rgb), 0.28); }

.ludo-token {
  width: 40%;
  height: 40%;
  border-radius: 50%;
  border: 1.5px solid rgba(255, 255, 255, 0.5);
}

.ludo-token.host { background: var(--danger); }
.ludo-token.guest { background: var(--tick-read); }

.ludo-home {
  display: grid;
  place-items: center;
  font-size: 22px;
  background: var(--panel-3);
  border-radius: 6px;
}

.ludo-status {
  text-align: center;
  font-size: 13.5px;
  font-weight: 600;
  margin-bottom: 10px;
}

.ludo-players-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

.ludo-player-tokens {
  flex: 1;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 6px;
}

.ludo-chip {
  padding: 6px 4px;
  border-radius: var(--r-sm);
  background: var(--hover-bg-soft);
  border: 1.5px solid transparent;
  font-size: 10.5px;
  font-weight: 650;
  text-align: center;
  color: var(--text-dim);
}

.ludo-chip.host { border-color: rgba(var(--danger-rgb), 0.4); }
.ludo-chip.guest { border-color: rgba(143, 216, 245, 0.4); }
.ludo-chip.clickable { color: var(--text); background: var(--hover-bg); cursor: pointer; }
.ludo-chip:disabled { cursor: default; }

.ludo-roll-btn {
  width: auto;
  flex: none;
  margin-top: 0;
  padding: 14px 18px;
  font-size: 16px;
}

.live-chess-hint {
  text-align: center;
  font-size: 12px;
  color: var(--text-dim);
}

/* Candy Crush duel */

.candy-board {
  display: grid;
  grid-template-columns: repeat(8, 1fr);
  gap: 4px;
  margin-bottom: 14px;
}

.candy-cell {
  aspect-ratio: 1;
  border-radius: 8px;
  background: var(--hover-bg-soft);
  font-size: 22px;
  display: grid;
  place-items: center;
  transition: background 0.12s, transform 0.12s;
}

.candy-cell:hover:not(:disabled) { background: var(--hover-bg); transform: scale(1.05); }
.candy-cell.selected { background: rgba(var(--accent-2-rgb), 0.3); box-shadow: 0 0 0 2px var(--accent-2) inset; }

.candy-scores {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  text-align: center;
  margin-bottom: 8px;
}

.candy-score { flex: 1; font-size: 12px; color: var(--text-dim); }
.candy-score b { font-size: 20px; color: var(--text); display: block; }
.candy-timer { flex: none; font-size: 15px; font-variant-numeric: tabular-nums; }

/* Pong — width:100% + aspect-ratio (no fixed px anywhere) is what makes
   the canvas itself responsive; the actual game logic then reads back
   whatever size that resolves to on this device (see resizePongCanvas).
   min-width:0 matters here for the exact same reason it mattered on the
   composer today — without it this can refuse to shrink below some
   intrinsic size and blow out the modal on a narrow phone. */
.pong-board {
  width: 100%;
  min-width: 0;
  aspect-ratio: 16 / 9;
  margin-bottom: 14px;
  border-radius: var(--r-sm);
  overflow: hidden;
  background: var(--inset-bg);
}
.pong-board canvas {
  width: 100%;
  height: 100%;
  display: block;
  /* Without this, a touch-drag to move the paddle also triggers the
     browser's own scroll/pan gesture on mobile, fighting the game. */
  touch-action: none;
  cursor: ns-resize;
}

/* ═══════════════════════════════════════════════════════════
   Mobile — one pane at a time, like a phone messaging app
   ═══════════════════════════════════════════════════════════ */

/* Small windows (a half-screen browser, a tablet): keep the chat list AND the open chat side by side,
   with a slimmer list, instead of switching to the one-pane phone layout. */
@media (min-width: 601px) and (max-width: 820px) {
  #app-screen { --sidebar-w: clamp(250px, 38vw, 330px); }

  /* The message box shares this narrower column with the chat list, so tighten the composer like on a phone
     (otherwise the voice-language picker squeezes the text box down to a few letters). */
  .composer { padding: 8px 8px 12px; }
  .composer-normal { gap: 4px; }
  .composer .icon-btn { width: 34px; height: 34px; flex: none; }
  .composer .icon-btn svg { width: 20px; height: 20px; }
  .speech-lang-select { display: none; }
  .composer-box { padding: 5px 6px 5px 12px; }
  .send-btn { width: 42px; height: 42px; flex: none; }
}

@media (max-width: 600px) {
  /* One pane fills the screen — the floating gap/radius only makes sense
     on a wide layout, so collapse back to edge-to-edge here. */
  #app-screen { grid-template-columns: 1fr; padding: 0; gap: 0; }

  .sidebar, .chat-pane, .no-chat { border-radius: 0; box-shadow: none; }

  .sidebar { grid-column: 1; grid-row: 1; }

  .chat-pane, .no-chat {
    grid-column: 1;
    grid-row: 1;
    display: none;
  }

  #app-screen.viewing-chat .sidebar { display: none; }
  #app-screen.viewing-chat .chat-pane:not(.hidden) { display: flex; }

  .back-btn { display: grid; }

  .bubble { max-width: 80%; }
  .messages { padding: 16px 14px 10px; }

  /* Composer on a phone: every fixed-width control was stealing room from the
     text box until "Type a message" wrapped one word per line. Tighten the
     row, shrink the icon buttons, and drop the voice-language <select>
     (it belongs on the recording bar, not competing for space here). */
  .composer { padding: 8px 8px 12px; }
  .composer-normal { gap: 4px; }
  .composer .icon-btn { width: 34px; height: 34px; flex: none; }
  .composer .icon-btn svg { width: 20px; height: 20px; }
  .speech-lang-select { display: none; }
  .composer-box { padding: 5px 6px 5px 12px; }
  .composer textarea { font-size: 16px; }   /* ≥16px stops iOS zoom-on-focus */
  .send-btn { width: 42px; height: 42px; flex: none; }
  .media-picker { left: 6px; right: 6px; width: auto; height: 320px; }

  /* On an actual phone, the story "card" should just be the screen. */
  .status-viewer { padding: 0; }
  .status-card { max-width: none; aspect-ratio: auto; height: 100%; border-radius: 0; }

  /* Call controls: more buttons now (mute / camera / flip / filter), so
     tighten spacing and shrink the secondaries to fit a phone width. */
  .call-actions { gap: 14px; }
  .call-in-progress-controls { gap: 12px; }
  .call-btn { width: 54px; height: 54px; }
  .call-btn.secondary { width: 46px; height: 46px; }
  .call-filter-bar { bottom: 88px; }
}

/* ═══════════════════════════════════════════════════════════
   Per-palette component nudges — a few near-transparent fills
   need an outline to read as what they are.
   ═══════════════════════════════════════════════════════════ */
:root[data-palette="cyber"] .send-btn,
:root[data-palette="cyber"] .chat-item.active::before {
  box-shadow: 0 0 16px rgba(0, 240, 255, 0.5);
}
:root[data-palette="cyber"] .typing-bubble { border: 1px solid rgba(255, 46, 151, 0.26); }

/* ═══════════════════════════════════════════════════════════
   Appearance panel (Edit profile → Appearance)
   ═══════════════════════════════════════════════════════════ */

.appearance-group { margin-top: 14px; }

.appearance-group > .appearance-label {
  display: block;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--text-dim);
  margin-bottom: 8px;
}

.palette-options { display: flex; flex-direction: column; gap: 8px; }

.palette-option {
  display: flex;
  align-items: center;
  gap: 13px;
  width: 100%;
  text-align: left;
  padding: 10px 12px;
  border-radius: var(--r-md);
  background: var(--hover-bg);
  border: 1.5px solid transparent;
  color: var(--text);
  transition: background 0.15s, border-color 0.15s;
}

.palette-option:hover { background: var(--hover-bg-strong); }

.palette-option[aria-checked="true"] {
  border-color: var(--accent);
  background: rgba(var(--accent-2-rgb), 0.16);
}

.palette-option .po-sw { display: flex; gap: 3px; flex: none; }
.palette-option .po-sw i {
  width: 13px;
  height: 22px;
  border-radius: 3px;
  border: 1px solid var(--border);
}

.palette-option .po-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.palette-option .po-name { font-size: 13.5px; font-weight: 650; }
.palette-option .po-desc { font-size: 11.5px; color: var(--muted); }

.appearance-seg {
  display: flex;
  gap: 6px;
  background: var(--inset-bg);
  border-radius: var(--r-sm);
  padding: 4px;
}

.appearance-seg button {
  flex: 1;
  padding: 9px;
  border-radius: calc(var(--r-sm) - 4px);
  font-size: 13px;
  font-weight: 650;
  color: var(--text-dim);
  transition: background 0.15s, color 0.15s;
}

.appearance-seg button[aria-pressed="true"] {
  background: var(--grad-accent);
  color: var(--on-accent);
}

.appearance-seg button:disabled { opacity: 0.4; cursor: not-allowed; }

.appearance-note {
  font-size: 11.5px;
  color: var(--muted);
  margin-top: 8px;
  min-height: 1em;
}

/* ═══════════════════════════════════════════════════════════
   Contact info (tap the peer's photo/name in the chat header)
   ═══════════════════════════════════════════════════════════ */

.contact-info-card { padding-top: 22px; }

.contact-info-hero {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding-bottom: 18px;
}
.contact-info-hero .avatar { margin-bottom: 8px; }
.contact-info-name {
  font-size: 20px;
  font-weight: 700;
  letter-spacing: -0.2px;
  text-align: center;
}
.contact-info-handle { font-size: 13px; color: var(--muted); }

.contact-info-block {
  padding: 14px 0;
  border-top: 1px solid var(--border);
}
.contact-info-label {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--text-dim);
  margin-bottom: 6px;
}
.contact-info-about { font-size: 14px; line-height: 1.5; }

.contact-info-list {
  display: flex;
  flex-direction: column;
  border-top: 1px solid var(--border);
  padding-top: 6px;
  margin: 0 -6px;
}
.contact-info-row {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 12px 12px;
  border-radius: var(--r-md);
  font-size: 14px;
  font-weight: 550;
  color: var(--text);
  text-align: left;
  transition: background 0.14s;
}
.contact-info-row:hover { background: var(--hover-bg); }
.contact-info-row svg { width: 19px; height: 19px; color: var(--text-dim); flex: none; }
.contact-info-row.danger { color: var(--danger-text); }
.contact-info-row.danger svg { color: var(--danger); }

.storage-stat {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 13px 2px;
  border-top: 1px solid var(--border);
  font-size: 14px;
}
.storage-stat b { font-variant-numeric: tabular-nums; }
.storage-kind span { display: flex; flex-direction: column; gap: 2px; }
.storage-kind small { font-size: 12px; color: var(--text-dim); font-variant-numeric: tabular-nums; }
.storage-clear-one {
  border: 1px solid var(--border);
  background: transparent;
  color: var(--danger-text);
  border-radius: 999px;
  padding: 6px 14px;
  font-size: 13px;
  cursor: pointer;
}
.storage-clear-one:hover:not(:disabled) { background: var(--danger-bg); }
.storage-clear-one:disabled { opacity: 0.4; cursor: default; }

/* ═══════════════════════════════════════════════════════════
   Linked devices
   ═══════════════════════════════════════════════════════════ */

.auth-link-device {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  width: 100%;
  margin-top: 14px;
  padding: 11px;
  border-radius: var(--r-md);
  background: var(--hover-bg);
  color: var(--text-dim);
  font-size: 13.5px;
  font-weight: 600;
  transition: background 0.15s, color 0.15s;
}
.auth-link-device:hover { background: var(--hover-bg-strong); color: var(--text); }
.auth-link-device svg { width: 16px; height: 16px; }

.link-qr-slot {
  width: 240px;
  max-width: 100%;
  aspect-ratio: 1;
  margin: 0 auto;
  border-radius: var(--r-md);
  background: #fff;
  display: grid;
  place-items: center;
  overflow: hidden;
}
.link-qr-slot .qr-image { padding: 12px; }

.link-spinner {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  border: 3px solid rgba(0, 0, 0, 0.12);
  border-top-color: var(--accent-2);
  animation: link-spin 0.8s linear infinite;
}
@keyframes link-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .link-spinner { animation-duration: 2s; } }

.linked-this-device {
  display: flex;
  align-items: center;
  gap: 13px;
  padding: 13px 14px;
  border-radius: var(--r-md);
  background: rgba(var(--online-rgb), 0.12);
  border: 1px solid rgba(var(--online-rgb), 0.28);
}
.linked-this-device svg { width: 22px; height: 22px; color: var(--online); flex: none; }
.ltd-name { font-size: 13.5px; font-weight: 650; }
.ltd-sub { font-size: 12px; color: var(--muted); }

/* ═══════════════════════════════════════════════════════════
   Creative polish — Veyon flagship refinements

   A motion + depth layer on top of the component rules above. Every
   rule here targets a selector that already exists — nothing in this
   section introduces a class name or touches markup, so it's pure
   restyling: this whole block could be deleted and everything above
   still works exactly as it did before.
   ═══════════════════════════════════════════════════════════ */

:root {
  --ease-out:    cubic-bezier(0.16, 1, 0.3, 1);
  --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
  --dur-fast: 0.14s;
  --dur-base: 0.22s;
  --dur-slow: 0.4s;
}

/* ── Auth screen: a real product moment, not a template login ──
   Split shell: a dark brand panel (visual, muted mesh + grain, never
   neon-bright) beside the actual form, unified under one shadow so it
   reads as ONE considered surface instead of a card floating on a
   loud gradient. Collapses to the form alone under 860px — see the
   responsive block at the end of this section. */
body::after {
  /* Fine film-grain, app-wide, very low opacity — the single cheapest
     trick for making a flat gradient/glass surface stop looking like a
     flat digital render. Fixed + mix-blend so it never fights contrast. */
  content: '';
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  opacity: 0.05;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

#auth-screen { padding: 32px; }

.auth-shell {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 920px;
  display: grid;
  grid-template-columns: 1fr 1fr;
  border-radius: var(--r-lg);
  overflow: hidden;
  box-shadow: var(--shadow-lg);
  animation: auth-card-in 0.55s var(--ease-out);
  background: var(--raised);
  /* On a short phone screen the card is taller than the screen: it must keep its full height (and
     scroll inside #auth-screen) instead of shrinking and clipping the "Create an account" / "Log in" buttons. */
  flex-shrink: 0;
  margin-block: auto;
}
@keyframes auth-card-in {
  from { opacity: 0; transform: translateY(14px) scale(0.98); }
  to   { opacity: 1; transform: none; }
}

/* ── Left: brand panel ───────────────────────────────────── */
.auth-visual {
  position: relative;
  overflow: hidden;
  padding: 48px 40px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  background:
    radial-gradient(120% 140% at 100% 0%, rgba(var(--accent-2-rgb), 0.35), transparent 60%),
    radial-gradient(120% 120% at 0% 100%, rgba(var(--accent-rgb), 0.18), transparent 55%),
    linear-gradient(165deg, #0A0E1F 0%, #060811 100%);
  color: #EDF1FF;
}

/* A faint structural grid — reads as "product", not "poster". */
.auth-visual-grid {
  position: absolute;
  inset: 0;
  opacity: 0.5;
  background-image:
    linear-gradient(rgba(255, 255, 255, 0.05) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 255, 255, 0.05) 1px, transparent 1px);
  background-size: 34px 34px;
  mask-image: radial-gradient(120% 100% at 20% 20%, black 30%, transparent 85%);
}

/* One soft, slow-moving glow — singular and restrained, not two bright blobs. */
.auth-visual-glow {
  position: absolute;
  width: 60%;
  aspect-ratio: 1;
  right: -20%;
  top: -20%;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(0, 194, 255, 0.28), transparent 70%);
  filter: blur(40px);
  animation: auth-drift 24s ease-in-out infinite alternate;
}
@keyframes auth-drift {
  from { transform: translate(0, 0) scale(1); }
  to   { transform: translate(-3vmax, 3vmax) scale(1.15); }
}

.auth-visual-content { position: relative; z-index: 1; }

.auth-visual-mark {
  width: 52px;
  height: 52px;
  border-radius: 15px;
  background: var(--grad-accent);
  display: grid;
  place-items: center;
  box-shadow: 0 10px 28px rgba(var(--accent-2-rgb), 0.45);
  margin-bottom: 26px;
  animation: brand-float 5s ease-in-out infinite;
}
.auth-visual-mark svg { width: 27px; height: 27px; }
@keyframes brand-float {
  0%, 100% { transform: translateY(0) rotate(0deg); }
  50%      { transform: translateY(-4px) rotate(-3deg); }
}

.auth-visual-wordmark {
  font-size: 30px;
  font-weight: 750;
  letter-spacing: -0.6px;
  margin-bottom: 14px;
}

.auth-visual-tagline {
  font-size: 15px;
  line-height: 1.6;
  color: rgba(237, 241, 255, 0.68);
  max-width: 30ch;
  margin-bottom: 32px;
}

.auth-visual-points { display: flex; flex-direction: column; gap: 13px; }
.auth-visual-points li {
  display: flex;
  align-items: center;
  gap: 11px;
  font-size: 13.5px;
  color: rgba(237, 241, 255, 0.82);
}
.auth-visual-points svg {
  width: 20px; height: 20px; flex: none;
  padding: 3px;
  border-radius: 50%;
  background: rgba(0, 194, 255, 0.16);
  color: #6FE0FF;
}

/* ── Right: the form panel ───────────────────────────────── */
.auth-card {
  width: 100%;
  max-width: none;
  background: var(--panel);
  border: none;
  border-radius: 0;
  box-shadow: none;
  padding: 48px 44px;
  backdrop-filter: var(--glass-blur, none);
  -webkit-backdrop-filter: var(--glass-blur, none);
  display: flex;
  flex-direction: column;
  justify-content: center;
}

/* Small brand lockup shown only when the visual panel is hidden (mobile). */
.auth-card-brand-sm { display: none; align-items: center; gap: 10px; margin-bottom: 18px; }
.brand-mark-sm {
  width: 34px; height: 34px; border-radius: 10px; flex: none;
  background: var(--grad-accent); display: grid; place-items: center;
}
.brand-mark-sm svg { width: 18px; height: 18px; }
.auth-card-brand-sm span { font-weight: 700; font-size: 16px; letter-spacing: -0.3px; }

.auth-card-heading {
  font-size: 24px;
  font-weight: 750;
  letter-spacing: -0.4px;
  margin-bottom: 6px;
}

.auth-card .auth-sub { text-align: left; margin-bottom: 28px; }

@media (max-width: 860px) {
  .auth-shell { grid-template-columns: 1fr; max-width: 440px; }
  .auth-visual { display: none; }
  .auth-card { padding: 40px 32px 32px; border-radius: var(--r-lg); }
  .auth-card-brand-sm { display: flex; }
  .auth-card .auth-sub, .auth-card-heading { text-align: center; }
  .auth-card-brand-sm { justify-content: center; }
}

/* The primary button's gradient is 200% wide and slides on hover
   instead of just lifting — a moving highlight rather than a flat state swap. */
.btn-primary {
  background-size: 200% 100%;
  background-position: 0% 0%;
  transition: transform var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-base) var(--ease-out),
              background-position var(--dur-slow) var(--ease-out);
}
.btn-primary:hover:not(:disabled) { background-position: 100% 0%; }

@media (prefers-reduced-motion: reduce) {
  .auth-visual-glow, .auth-shell, .auth-visual-mark { animation: none; }
}

/* ── Sidebar chat list: lift + depth instead of a flat colour swap ── */
.chat-item {
  transition: background var(--dur-fast) var(--ease-out),
              transform var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-fast) var(--ease-out);
}
.chat-item:hover { transform: translateX(2px); }
.chat-item.active { box-shadow: 0 4px 14px rgba(0, 0, 0, 0.12); }
.chat-item.active::before { animation: active-bar-in var(--dur-base) var(--ease-spring); }
@keyframes active-bar-in { from { height: 0; opacity: 0; } to { height: 26px; opacity: 1; } }

/* Online dot breathes gently instead of sitting on a static halo. */
.avatar .dot.online { animation: online-pulse 2.4s ease-in-out infinite; }
@keyframes online-pulse {
  0%, 100% { box-shadow: 0 0 0 2px rgba(var(--online-rgb), 0.22); }
  50%      { box-shadow: 0 0 0 4px rgba(var(--online-rgb), 0.1); }
}

/* Unread badge pops in on arrival rather than appearing instantly. */
.badge { animation: badge-pop var(--dur-base) var(--ease-spring); }
@keyframes badge-pop { from { transform: scale(0); opacity: 0; } to { transform: scale(1); opacity: 1; } }

.icon-btn {
  transition: background var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out),
              transform var(--dur-fast) var(--ease-out);
}
.icon-btn:active { transform: scale(0.9); }

/* ── Composer: a focus glow instead of just a border colour change,
   and a send button with more character on hover/press ── */
.composer-box {
  transition: border-color var(--dur-base) var(--ease-out), box-shadow var(--dur-base) var(--ease-out);
}
.composer-box:focus-within { box-shadow: 0 0 0 4px rgba(var(--accent-2-rgb), 0.14); }

.send-btn { transition: transform var(--dur-fast) var(--ease-spring), box-shadow var(--dur-base) var(--ease-out); }
.send-btn:hover:not(:disabled) { transform: scale(1.08) rotate(-8deg); box-shadow: 0 8px 22px rgba(var(--accent-2-rgb), 0.5); }
.send-btn:active:not(:disabled) { transform: scale(0.92) rotate(0deg); }

/* ── Message bubbles: springier entrance, real lift on hover ── */
.bubble { transition: box-shadow var(--dur-base) var(--ease-out); animation: bubble-in var(--dur-base) var(--ease-spring); }
.bubble:hover { box-shadow: var(--shadow-md); }

@media (prefers-reduced-motion: reduce) {
  .chat-item, .chat-item.active::before, .avatar .dot.online,
  .badge, .bubble, .send-btn, .icon-btn { animation: none !important; transition: none !important; }
}

/* ── App shell: carrying the auth screen's brand language (subtle
   gradient underglow, structural grid texture, floating mark) into the
   main shell, so it reads as one designed product instead of "a nice
   login screen bolted onto a default chat template". ── */

.sidebar-head, .chat-head { position: relative; }
.sidebar-head::after, .chat-head::after {
  content: '';
  position: absolute; left: 0; right: 0; bottom: -1px; height: 1px;
  background: linear-gradient(90deg, transparent, rgba(var(--accent-2-rgb), 0.45), transparent);
}
.sidebar-head, .chat-head { border-bottom: none; }

.sidebar-head .avatar.sm {
  box-shadow: 0 0 0 2px var(--panel), 0 0 0 3.5px rgba(var(--accent-2-rgb), 0.35);
}

.search-box {
  transition: border-color var(--dur-base) var(--ease-out),
              box-shadow var(--dur-base) var(--ease-out),
              background var(--dur-base) var(--ease-out);
}
.search-box:focus-within {
  box-shadow: 0 0 0 4px rgba(var(--accent-2-rgb), 0.12);
  background: var(--panel-3);
}
.search-box svg { transition: color var(--dur-base) var(--ease-out); }
.search-box:focus-within svg { color: var(--accent); }

/* Active filter tab reads as a real selection (brand gradient), not a
   flat tint — matches the weight of .btn-primary/.badge elsewhere. */
.chat-filter-tab {
  transition: background var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-fast) var(--ease-out),
              transform var(--dur-fast) var(--ease-out);
}
.chat-filter-tab.active {
  background: var(--grad-accent);
  color: var(--on-accent);
  border-color: transparent;
  box-shadow: 0 4px 14px rgba(var(--accent-2-rgb), 0.3);
}
.chat-filter-tab:active { transform: scale(0.96); }

/* Empty state gets the same structural grid + floating mark as the
   auth screen's brand panel — the two screens a new user always sees
   back to back should feel like one place, not two different apps. */
.no-chat { position: relative; overflow: hidden; }
.no-chat::before {
  content: '';
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(var(--border) 1px, transparent 1px),
    linear-gradient(90deg, var(--border) 1px, transparent 1px);
  background-size: 34px 34px;
  mask-image: radial-gradient(65% 55% at 50% 42%, black 20%, transparent 80%);
  -webkit-mask-image: radial-gradient(65% 55% at 50% 42%, black 20%, transparent 80%);
  pointer-events: none;
}
.no-chat > * { position: relative; z-index: 1; }
.no-chat .mark { animation: brand-float 6s ease-in-out infinite; }

@media (prefers-reduced-motion: reduce) {
  .no-chat .mark { animation: none; }
}

/* ═══════════════════════════════════════════════════════════
   Final pass — glass-edge depth + smoother motion curve

   Two things separate "flat colour with a drop shadow" from "a pane of
   glass actually catching light": (1) a hairline highlight along the
   TOP edge only, not a uniform border, and (2) easing that decelerates
   the way real objects do rather than linear/ease-out's abrupt stop.
   Both applied to every floating surface already in the app — no new
   elements, no markup changes.
   ═══════════════════════════════════════════════════════════ */

:root {
  /* A slower, rounder deceleration than --ease-out — reads as "settling
     into place" rather than "stopping". Used for anything with real
     physical presence (panels, modals); --ease-out stays for quick
     micro-feedback (buttons, icons) where snappier is correct. */
  --ease-glide: cubic-bezier(0.22, 1, 0.36, 1);
}

.sidebar, .chat-pane, .no-chat, .auth-shell, .modal-card, .chat-menu, .media-picker {
  box-shadow: var(--panel-shadow), inset 0 1px 0 rgba(255, 255, 255, 0.06);
}
:root[data-theme="light"] .sidebar,
:root[data-theme="light"] .chat-pane,
:root[data-theme="light"] .no-chat,
:root[data-theme="light"] .auth-shell,
:root[data-theme="light"] .modal-card,
:root[data-theme="light"] .chat-menu,
:root[data-theme="light"] .media-picker {
  box-shadow: var(--panel-shadow), inset 0 1px 0 rgba(255, 255, 255, 0.55);
}
.modal-card, .chat-menu, .media-picker { box-shadow: var(--shadow-lg), inset 0 1px 0 rgba(255, 255, 255, 0.06); }
:root[data-theme="light"] .modal-card,
:root[data-theme="light"] .chat-menu,
:root[data-theme="light"] .media-picker {
  box-shadow: var(--shadow-lg), inset 0 1px 0 rgba(255, 255, 255, 0.6);
}

.auth-shell, .modal-card, .media-picker, .chat-menu {
  animation-timing-function: var(--ease-glide);
}
.chat-item, .icon-btn, .badge, .bubble { animation-timing-function: var(--ease-glide); }

/* Chat backup (features/backup.js) */
.backup-status {
  padding: 12px 14px;
  border-radius: var(--r-md);
  background: var(--hover-bg);
  font-size: 13.5px;
  color: var(--text);
  margin-bottom: 14px;
}
.backup-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 2px;
  border-top: 1px solid var(--border);
  font-size: 14px;
}
.backup-row select {
  background: var(--hover-bg);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: 7px 10px;
  font: inherit;
}
.backup-actions { flex-wrap: wrap; justify-content: flex-start; }

/* Storage & data → Automatic download */
.storage-subhead { margin: 20px 0 4px; font-size: 14px; font-weight: 700; }
.storage-auto {
  background: var(--hover-bg);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: 6px 8px;
  font: inherit;
  font-size: 13px;
}
/* A photo / video shown inline instead of the file row */
.attach.attach-media { padding: 0; background: none; display: block; }
.attach-video { max-width: 100%; max-height: 340px; border-radius: 10px; display: block; }

/* ── "Post a status" popup (openStatusComposer in app.js) ── */
.status-composer { max-width: 480px; }
.status-composer .sc-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 18px;
}
.status-composer .sc-head h3 { margin-bottom: 2px; }
.status-composer .sc-sub { font-size: 12.5px; color: var(--muted); }
.status-composer .sc-close {
  width: 34px;
  height: 34px;
  flex: none;
  display: grid;
  place-items: center;
  border-radius: 50%;
  color: var(--text-dim);
  background: var(--hover-bg);
  transition: background 0.15s, color 0.15s;
}
.status-composer .sc-close:hover { background: var(--hover-bg-strong); color: var(--text); }
.status-composer .sc-close svg { width: 17px; height: 17px; }

.status-composer .sc-text { position: relative; }
:root .modal-card.status-composer .field.sc-text textarea { resize: none; min-height: 104px; padding-bottom: 26px; font-size: 15px; line-height: 1.45; }
.status-composer .sc-count {
  position: absolute;
  right: 12px;
  bottom: 8px;
  font-size: 11px;
  color: var(--muted);
  pointer-events: none;
  font-variant-numeric: tabular-nums;
}

.status-composer .sc-preview {
  position: relative;
  margin-top: 12px;
  border-radius: var(--r-md);
  overflow: hidden;
  background: var(--inset-bg);
  max-height: 260px;
  display: grid;
  place-items: center;
}
.status-composer .sc-preview img,
.status-composer .sc-preview video { width: 100%; max-height: 260px; object-fit: contain; display: block; }
.status-composer .sc-preview-remove {
  position: absolute;
  top: 8px;
  right: 8px;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
  font-size: 13px;
  display: grid;
  place-items: center;
}
.status-composer .sc-preview-remove:hover { background: rgba(0, 0, 0, 0.75); }

.status-composer .modal-actions { margin-top: 8px; padding-top: 16px; border-top: 1px solid var(--border); }
.status-composer #status-post { min-width: 96px; }
.status-composer #status-post:disabled { opacity: 0.45; cursor: not-allowed; box-shadow: none; transform: none; }
/* As a sidebar page (like Settings): the page header has the title and ←,
   so the popup's own title and ✕ step aside; the subtitle stays. */
.as-panel .status-composer .sc-head h3,
.as-panel .status-composer .sc-close { display: none; }
.as-panel .status-composer .sc-head { margin: -4px 0 14px; }
.as-panel .status-composer .modal-actions {
  position: sticky;
  bottom: -20px;
  margin: 8px -18px -20px;
  padding: 14px 18px 18px;
  background: var(--panel);
}

/* ── Status privacy (openStatusPrivacy / openStatusContactPicker in app.js) ── */
.sp-row {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 11px 14px;
  border-radius: 14px;
  background: var(--inset-bg);
  color: var(--text);
  text-align: left;
  transition: background 0.15s;
}
.sp-row:hover { background: var(--hover-bg-strong); }
.sp-row-icon { width: 34px; height: 34px; flex: none; border-radius: 50%; display: grid; place-items: center;
  background: rgba(var(--accent-2-rgb), 0.14); color: var(--accent); }
.sp-row-icon svg { width: 18px; height: 18px; }
.sp-row-text { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.sp-row-title { font-size: 14px; font-weight: 600; }
.sp-row-sub { font-size: 12.5px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sp-row-chev { width: 18px; height: 18px; color: var(--muted); flex: none; }

.sp-lead { font-size: 13.5px; font-weight: 600; color: var(--text-dim); margin: 6px 4px 10px; }
.sp-options { display: flex; flex-direction: column; gap: 2px; }
.sp-option {
  display: flex;
  align-items: center;
  gap: 16px;
  width: 100%;
  padding: 12px 10px;
  border-radius: 12px;
  text-align: left;
  color: var(--text);
  transition: background 0.15s;
}
.sp-option:hover { background: var(--hover-bg); }
.sp-radio { width: 20px; height: 20px; flex: none; border-radius: 50%; border: 2px solid var(--muted); display: grid; place-items: center; }
.sp-option.on .sp-radio { border-color: var(--accent-2); }
.sp-option.on .sp-radio::after { content: ''; width: 10px; height: 10px; border-radius: 50%; background: var(--accent-2); }
.sp-option-text { display: flex; flex-direction: column; min-width: 0; }
.sp-option-title { font-size: 15px; }
.sp-option-sub { font-size: 13px; color: var(--muted); }
.sp-note { font-size: 12.5px; color: var(--muted); margin: 14px 10px 0; }

:root .modal-backdrop > .modal-card.sp-picker {
  max-width: 460px;
  height: min(640px, 86vh);
  padding: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.sp-picker-head { display: flex; align-items: center; gap: 10px; padding: 16px 16px 10px 12px; }
.sp-picker-title { flex: 1; font-size: 16.5px; font-weight: 600; }
.sp-search {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0 18px 8px;
  padding: 10px 14px;
  border-radius: 99px;
  border: 1.5px solid var(--accent-2);
  background: var(--panel);
}
.sp-search svg { width: 18px; height: 18px; color: var(--muted); flex: none; }
.sp-search input { flex: 1; border: none; outline: none; background: none; color: var(--text); font: inherit; font-size: 14.5px; }
.sp-list { flex: 1; overflow-y: auto; padding: 4px 10px 8px; }
.sp-list-label { font-size: 13px; color: var(--muted); padding: 10px 10px 6px; }
.sp-contact {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 9px 10px;
  border-radius: 12px;
  cursor: pointer;
  transition: background 0.15s;
}
.sp-contact:hover { background: var(--hover-bg); }
.sp-contact.hidden { display: none; }
.sp-contact input { width: 18px; height: 18px; flex: none; accent-color: var(--accent-2); cursor: pointer; }
.sp-contact .avatar { width: 42px; height: 42px; flex: none; }
.sp-contact-text { display: flex; flex-direction: column; min-width: 0; }
.sp-contact-name { font-size: 14.5px; font-weight: 600; }
.sp-contact-sub { font-size: 12.5px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sp-empty { text-align: center; color: var(--muted); padding: 24px; font-size: 13.5px; }
.sp-picker-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 14px 12px 22px;
  background: var(--panel-3);
  border-top: 1px solid var(--border);
  font-size: 14.5px;
}
.sp-done {
  width: 50px;
  height: 50px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--accent-2);
  color: #fff;
  box-shadow: 0 6px 16px -6px rgba(var(--accent-2-rgb), 0.7);
  transition: transform 0.15s;
}
.sp-done:hover { transform: scale(1.05); }
.sp-done svg { width: 22px; height: 22px; }

/* Owner-supplied logo (js/branding.js): fill the mark, no gradient tile behind it. */
.auth-visual-mark.has-logo, .brand-mark.has-logo, .brand-mark-sm.has-logo,
:root[data-palette] .auth-visual-mark.has-logo, :root[data-palette] .brand-mark.has-logo,
:root[data-palette] .brand-mark-sm.has-logo { background: none; box-shadow: none; overflow: hidden; }
.has-logo > img { width: 100%; height: 100%; object-fit: cover; border-radius: inherit; display: block; }

/* First screen: just two ways in. The form shows once one is chosen. */
#auth-screen.welcome .auth-card > .auth-card-heading, #auth-screen.welcome #auth-sub, #auth-screen.welcome #auth-form,
#auth-screen.welcome #auth-toggle, #auth-screen.welcome #auth-link-device, #auth-screen.welcome #auth-error,
#auth-screen.welcome .auth-back { display: none; }
#auth-screen:not(.welcome) .auth-welcome { display: none; }
.auth-welcome { display: flex; flex-direction: column; gap: 14px; }
.auth-welcome .auth-card-heading { margin: 0 0 4px; }
.welcome-qr-row { display: flex; align-items: center; gap: 22px; flex-wrap: wrap; }
.welcome-steps { list-style: none; margin: 0; padding: 0; flex: 1 1 180px; display: flex; flex-direction: column; gap: 16px;
  font-size: 14.5px; color: var(--text); }
.welcome-steps li { display: flex; align-items: center; gap: 12px; }
.welcome-qr { flex: none; width: 188px; height: 188px; border-radius: 18px; background: #fff; display: grid; place-items: center;
  overflow: hidden; margin: 0 auto; border: 1px solid var(--border); }
.welcome-qr img { width: 100%; height: 100%; object-fit: contain; }
.auth-welcome .qr-hint { text-align: center; margin: 0; }
.welcome-alt { align-self: center; background: none; border: 0; color: var(--accent); font: inherit; font-weight: 600;
  cursor: pointer; padding: 8px 4px; }
.welcome-alt:hover { text-decoration: underline; }
.auth-welcome .auth-toggle { text-align: center; margin: 0; }
.auth-back { display: none; }
#auth-screen:not(.welcome) .auth-welcome { display: none; }
.auth-welcome { display: flex; flex-direction: column; gap: 14px; }
.auth-welcome .auth-card-heading { margin: 0; }
.auth-welcome .auth-sub { margin: 0 0 8px; }
.auth-welcome .btn-primary, .auth-welcome .auth-link-device { width: 100%; padding: 15px 20px; }
.auth-welcome .auth-toggle { text-align: center; margin: 4px 0 0; }
.auth-back { align-self: flex-start; margin-bottom: 10px; }

/* "Can't connect" screen (network or server down). */
.conn-problem { position: fixed; inset: 0; z-index: 400; display: grid; place-items: center; padding: 24px;
  background: var(--bg, #0e0f11); color: var(--text, #ececee); }
.conn-card { max-width: 380px; text-align: center; }
.conn-icon { width: 72px; height: 72px; margin: 0 auto 20px; border-radius: 22px; display: grid; place-items: center;
  background: rgba(var(--accent-2-rgb, 76, 110, 245), .14); color: var(--accent, #91a7ff); }
.conn-icon svg { width: 34px; height: 34px; }
.conn-card h2 { margin: 0 0 8px; font-size: 22px; letter-spacing: -.02em; }
.conn-card p { margin: 0 0 22px; color: var(--text-dim, #a1a1aa); font-size: 14.5px; line-height: 1.5; }
.conn-card .btn-primary { min-width: 160px; }

/* ── First page: one wide card — instructions left, QR right, quiet footer. ── */
.welcome-brand { display: none; }
#auth-screen.welcome { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 24px;
  width: 100%; min-height: 100vh; padding: 28px 20px 40px; box-sizing: border-box; overflow-y: auto; }
#auth-screen.welcome .welcome-brand { display: flex; align-items: center; gap: 10px; font-weight: 700; font-size: 18px;
  letter-spacing: -.02em; width: 100%; max-width: 980px; }
#auth-screen.welcome .welcome-brand .brand-mark-sm { width: 34px; height: 34px; }
#auth-screen.welcome .auth-shell { width: 100%; max-width: 980px; grid-template-columns: 1fr; margin: 0;
  border: 1px solid var(--border); border-radius: 28px; box-shadow: var(--shadow-md); }
#auth-screen.welcome .auth-visual, #auth-screen.welcome .auth-card-brand-sm { display: none; }
#auth-screen.welcome .auth-card { padding: 0; max-width: none; width: 100%; }
#auth-screen.welcome .auth-note { margin: 0; padding: 18px 56px 22px; border-top: 1px solid var(--border);
  background: var(--panel-2); border-radius: 0 0 28px 28px; }

.auth-welcome { display: flex; flex-direction: column; gap: 0; }
.welcome-main { display: flex; align-items: center; justify-content: space-between; gap: 56px; padding: 56px 56px 40px; }
.welcome-copy { flex: 1 1 auto; min-width: 0; }
.welcome-eyebrow { display: block; font-size: 12.5px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  color: var(--accent); margin-bottom: 12px; }
.auth-welcome .auth-card-heading { font-size: 36px; font-weight: 700; letter-spacing: -.035em; margin: 0 0 12px; }
.welcome-lead { margin: 0 0 30px; max-width: 440px; font-size: 15.5px; line-height: 1.55; color: var(--text-dim); }
.welcome-steps { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 18px; }
.welcome-steps li { position: relative; display: flex; align-items: center; gap: 16px; font-size: 16px; color: var(--text); }
.welcome-steps .t { flex: 1; min-width: 0; }
.welcome-steps .n { flex: none; width: 30px; height: 30px; border-radius: 50%; display: grid; place-items: center;
  background: rgba(var(--accent-2-rgb), .12); color: var(--accent); font-size: 13px; font-weight: 700; }
.welcome-steps li:not(:last-child)::after { content: ''; position: absolute; left: 14.5px; top: 36px; height: 20px;
  border-left: 2px solid rgba(var(--accent-2-rgb), .22); }
.welcome-qr-wrap { flex: none; display: flex; flex-direction: column; align-items: center; gap: 14px; }
.welcome-qr { width: 252px; height: 252px; padding: 14px; box-sizing: border-box; border-radius: 22px; background: #fff;
  display: grid; place-items: center; border: 1px solid var(--border); box-shadow: var(--shadow-md); }
.welcome-qr img { width: 100%; height: 100%; object-fit: contain; }
.welcome-qr .link-spinner { width: 38px; height: 38px; }
.auth-welcome .qr-hint { margin: 0; text-align: center; font-size: 13.5px; color: var(--muted); }
.welcome-foot { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap;
  padding: 0 56px 28px; }
.welcome-foot .auth-toggle { margin: 0; font-size: 14.5px; }
.welcome-alt { background: var(--panel-3); border: 0; border-radius: 99px; padding: 12px 22px; color: var(--text);
  font: inherit; font-weight: 600; font-size: 14.5px; cursor: pointer; transition: background .15s; }
.welcome-alt:hover { background: var(--hover-bg-strong); }
@media (max-width: 800px) {
  .welcome-main { flex-direction: column-reverse; align-items: stretch; gap: 28px; padding: 32px 24px 24px; }
  .welcome-foot { padding: 0 24px 22px; flex-direction: column; align-items: stretch; text-align: center; }
  #auth-screen.welcome .auth-note { padding: 16px 24px 20px; }
  .auth-welcome .auth-card-heading { font-size: 28px; }
}

/* "See more" under a long message: reveals the next part each time it is clicked */
.see-more-btn {
  display: block;
  margin: 6px 0 0;
  padding: 0;
  background: none;
  border: 0;
  color: var(--accent, #3b6ef5);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}
.see-more-btn:hover { text-decoration: underline; }


/* ── E-Visiting card (Settings → E-Visiting card) ── */
.vc-card{max-width:560px;width:calc(100vw - 24px);max-height:92vh;overflow:auto}
.vc-prev{width:100%;height:auto;aspect-ratio:1050/600;border-radius:14px;border:1px solid var(--border);display:block}
.vc-designs{display:flex;gap:10px;overflow-x:auto;padding:10px 2px 12px}
.vc-design{flex:0 0 120px;padding:3px;border-radius:12px;border:2px solid transparent;background:none;cursor:pointer}
.vc-design.on{border-color:var(--accent,#4c6ef5)}
.vc-design canvas{width:100%;height:auto;aspect-ratio:1050/600;border-radius:8px;display:block}
.vc-grid{display:grid;grid-template-columns:1fr 1fr;gap:4px 12px}
.vc-grid .field:nth-child(4),.vc-grid .field:nth-child(6){grid-column:1/-1}
.vc-actions{flex-wrap:wrap;gap:8px}
@media (max-width:600px){.vc-grid{grid-template-columns:1fr}}

/* ── E-Visiting card: your own pictures ── */
.vc-section{font-size:12.5px;font-weight:600;color:var(--text-dim);margin:4px 0 8px}
.vc-section span{font-weight:400;opacity:.7}
.vc-pics{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;margin-bottom:16px}
.vc-pic{position:relative}
.vc-pic-pick{display:block;cursor:pointer;text-align:center}
.vc-pic-thumb{width:100%;height:auto;aspect-ratio:3/2;border-radius:12px;border:1px dashed var(--border);background:var(--panel-2);display:block}
.vc-pic.filled .vc-pic-thumb{border-style:solid}
.vc-pic-label{display:block;margin-top:6px;font-size:12.5px;color:var(--text-dim)}
.vc-pic-x{position:absolute;top:-7px;right:-7px;width:24px;height:24px;border-radius:50%;border:0;background:#222;color:#fff;font-size:16px;line-height:24px;cursor:pointer;display:none}
.vc-pic.has .vc-pic-x{display:block}

/* ═══════════════════════════════════════════════════════════
   Photo viewer (WhatsApp-style): header with sender + tools,
   the photo with arrows, and a strip of the chat's photos.
   ═══════════════════════════════════════════════════════════ */
.media-viewer {
  position: fixed; inset: 0; z-index: 205;
  display: flex; flex-direction: column;
  background: var(--panel, #fff); color: var(--text);
}
.mv-head { display: flex; align-items: center; gap: 12px; padding: 10px 16px; border-bottom: 1px solid var(--border); }
.mv-avatar { width: 42px; height: 42px; flex: none; }
.mv-who { min-width: 0; flex: 1; }
.mv-name { font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mv-when { font-size: 13px; color: var(--muted); }
.mv-tools { display: flex; gap: 2px; flex-wrap: wrap; justify-content: flex-end; }
.mv-btn {
  width: 40px; height: 40px; border: 0; border-radius: 50%; background: transparent; color: var(--text);
  display: grid; place-items: center; cursor: pointer;
}
.mv-btn svg { width: 22px; height: 22px; }
.mv-btn:hover { background: var(--hover-bg); }
.mv-btn:disabled { opacity: .35; cursor: default; background: transparent; }
.mv-btn.active { color: #F5B83D; }
.mv-btn[data-act="star"].active svg { fill: currentColor; }
.mv-stage { position: relative; flex: 1; min-height: 0; display: grid; place-items: center; overflow: auto; padding: 16px 64px; }
.mv-img {
  max-width: 100%; max-height: calc(100vh - 210px); object-fit: contain;
  transition: transform .15s ease; transform-origin: center; cursor: zoom-in;
}
.mv-img.zoomed { cursor: zoom-out; }
.mv-nav {
  position: absolute; top: 50%; transform: translateY(-50%);
  width: 46px; height: 46px; border-radius: 50%; border: 0; cursor: pointer;
  background: rgba(40, 40, 50, .75); color: #fff; font-size: 30px; line-height: 1; z-index: 1;
}
.mv-prev { left: 14px; }
.mv-next { right: 14px; }
.mv-nav:hover { background: rgba(20, 20, 30, .9); }
.mv-caption {
  position: absolute; bottom: 12px; left: 50%; transform: translateX(-50%); max-width: 80%;
  background: rgba(0, 0, 0, .6); color: #fff; padding: 6px 12px; border-radius: 10px; font-size: 14px;
}
.mv-strip {
  display: flex; gap: 8px; padding: 10px 16px; overflow-x: auto; border-top: 1px solid var(--border); flex: none;
}
.mv-thumb {
  flex: none; width: 86px; height: 86px; border-radius: 6px; overflow: hidden; cursor: pointer;
  border: 3px solid transparent; padding: 0; background: var(--panel-3, #ddd); display: grid; place-items: center;
}
.mv-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.mv-thumb.current { border-color: #25A35A; }
@media (max-width: 640px) {
  .mv-btn { width: 34px; height: 34px; }
  .mv-btn svg { width: 19px; height: 19px; }
  .mv-stage { padding: 8px; }
  .mv-thumb { width: 60px; height: 60px; }
}
