/* ------------------------------------------------------------------------
   Beam — Messages + calls. Liquid-glass chrome, iMessage-faithful bubbles.
   ------------------------------------------------------------------------ */
:root {
  color-scheme: light dark;
  --font: -apple-system, BlinkMacSystemFont, "SF Pro Text", "SF Pro Display",
          "Helvetica Neue", "Segoe UI Variable", "Segoe UI", Inter, Roboto, sans-serif;

  --blue: #0b84fe;
  --blue-press: #0a6fd8;
  --green: #34c759;
  --red: #ff3b30;
  --gray: #8e8e93;
  --gray2: #aeaeb2;

  --window: #f4f4f7;
  --sidebar: rgba(246,246,248,.82);
  --sidebar-border: rgba(0,0,0,.07);
  --chat-bg: #ffffff;
  --chat-tint: radial-gradient(120% 80% at 100% 0%, rgba(11,132,254,.05), transparent 60%),
               radial-gradient(90% 70% at 0% 100%, rgba(52,199,89,.05), transparent 60%);
  --hairline: rgba(0,0,0,.08);
  --text: #1d1d1f;
  --text-2: #6e6e73;
  --text-3: #8e8e93;
  --field: rgba(255,255,255,.8);
  --field-border: rgba(0,0,0,.12);
  --search: rgba(120,120,128,.12);
  --row-hover: rgba(0,0,0,.045);
  --in-bubble: #e9e9eb;
  --in-text: #000000;
  --out-bubble: #0b84fe;
  --out-text: #ffffff;
  --sms-bubble: linear-gradient(180deg, #ff5f57 0%, #ff3b30 100%);
  --pill-shadow: 0 0 0 1.5px var(--chat-bg);
  --card: rgba(255,255,255,.86);
  --card-solid: #ffffff;
  --card-shadow: 0 20px 60px rgba(0,0,0,.16), 0 0 0 .5px rgba(0,0,0,.06);
  --avatar-a: #b8b8be;
  --avatar-b: #8e8e93;

  /* liquid glass */
  --glass: rgba(255,255,255,.58);
  --glass-2: rgba(255,255,255,.42);
  --glass-edge: rgba(255,255,255,.85);
  --glass-line: rgba(0,0,0,.06);
  --glass-shadow: 0 10px 34px rgba(0,0,0,.10), 0 1px 2px rgba(0,0,0,.05);
  --glass-blur: blur(28px) saturate(190%);

  /* Gear glyph for the Settings tab, drawn via mask so it takes the tab's colour. */
  --gear-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='3.4'/%3E%3Cpath d='M19.1 13.9a1.6 1.6 0 0 0 .3 1.8l.1.1a2 2 0 1 1-2.8 2.8l-.1-.1a1.6 1.6 0 0 0-1.8-.3 1.6 1.6 0 0 0-1 1.5v.2a2 2 0 1 1-4 0v-.1a1.6 1.6 0 0 0-1.1-1.5 1.6 1.6 0 0 0-1.8.3l-.1.1a2 2 0 1 1-2.8-2.8l.1-.1a1.6 1.6 0 0 0 .3-1.8 1.6 1.6 0 0 0-1.5-1H2.8a2 2 0 1 1 0-4h.1a1.6 1.6 0 0 0 1.5-1.1 1.6 1.6 0 0 0-.3-1.8l-.1-.1a2 2 0 1 1 2.8-2.8l.1.1a1.6 1.6 0 0 0 1.8.3h.1a1.6 1.6 0 0 0 1-1.5V2.8a2 2 0 1 1 4 0v.1a1.6 1.6 0 0 0 1 1.5 1.6 1.6 0 0 0 1.8-.3l.1-.1a2 2 0 1 1 2.8 2.8l-.1.1a1.6 1.6 0 0 0-.3 1.8v.1a1.6 1.6 0 0 0 1.5 1h.2a2 2 0 1 1 0 4h-.1a1.6 1.6 0 0 0-1.5 1z'/%3E%3C/svg%3E");

  /* motion */
  --ease-out: cubic-bezier(.23, 1, .32, 1);
  --ease-in-out: cubic-bezier(.77, 0, .175, 1);
  --ease-drawer: cubic-bezier(.32, .72, 0, 1);
  --dur-fast: 120ms;   /* press feedback */
  --dur-ui: 180ms;     /* popovers, rows, toggles */
  --dur-sheet: 260ms;  /* sheets, drawers */
}
@media (prefers-color-scheme: dark) {
  :root {
    --window: #101012;
    --sidebar: rgba(28,28,30,.82);
    --sidebar-border: rgba(255,255,255,.07);
    --chat-bg: #000000;
    --chat-tint: radial-gradient(120% 80% at 100% 0%, rgba(11,132,254,.10), transparent 60%),
                 radial-gradient(90% 70% at 0% 100%, rgba(52,199,89,.07), transparent 60%);
    --hairline: rgba(255,255,255,.09);
    --text: #f5f5f7;
    --text-2: #a1a1a6;
    --text-3: #8e8e93;
    --field: rgba(44,44,46,.8);
    --field-border: rgba(255,255,255,.14);
    --search: rgba(120,120,128,.24);
    --row-hover: rgba(255,255,255,.06);
    --in-bubble: #26252a;
    --in-text: #ffffff;
    --out-bubble: #0a84ff;
    --card: rgba(40,40,44,.86);
    --card-solid: #2a2a2c;
    --card-shadow: 0 20px 60px rgba(0,0,0,.6), 0 0 0 .5px rgba(255,255,255,.08);
    --avatar-a: #9a9aa0;
    --avatar-b: #66666b;
    --glass: rgba(40,40,46,.55);
    --glass-2: rgba(40,40,46,.4);
    --glass-edge: rgba(255,255,255,.22);
    --glass-line: rgba(255,255,255,.06);
    --glass-shadow: 0 10px 34px rgba(0,0,0,.45), 0 1px 2px rgba(0,0,0,.4);
  }
}

* { box-sizing: border-box; }
html[data-theme="light"] {
  color-scheme: light dark;
  --font: -apple-system, BlinkMacSystemFont, "SF Pro Text", "SF Pro Display",
          "Helvetica Neue", "Segoe UI Variable", "Segoe UI", Inter, Roboto, sans-serif;

  --blue: #0b84fe;
  --blue-press: #0a6fd8;
  --green: #34c759;
  --red: #ff3b30;
  --gray: #8e8e93;
  --gray2: #aeaeb2;

  --window: #f4f4f7;
  --sidebar: rgba(246,246,248,.82);
  --sidebar-border: rgba(0,0,0,.07);
  --chat-bg: #ffffff;
  --chat-tint: radial-gradient(120% 80% at 100% 0%, rgba(11,132,254,.05), transparent 60%),
               radial-gradient(90% 70% at 0% 100%, rgba(52,199,89,.05), transparent 60%);
  --hairline: rgba(0,0,0,.08);
  --text: #1d1d1f;
  --text-2: #6e6e73;
  --text-3: #8e8e93;
  --field: rgba(255,255,255,.8);
  --field-border: rgba(0,0,0,.12);
  --search: rgba(120,120,128,.12);
  --row-hover: rgba(0,0,0,.045);
  --in-bubble: #e9e9eb;
  --in-text: #000000;
  --out-bubble: #0b84fe;
  --out-text: #ffffff;
  --sms-bubble: linear-gradient(180deg, #ff5f57 0%, #ff3b30 100%);
  --pill-shadow: 0 0 0 1.5px var(--chat-bg);
  --card: rgba(255,255,255,.86);
  --card-solid: #ffffff;
  --card-shadow: 0 20px 60px rgba(0,0,0,.16), 0 0 0 .5px rgba(0,0,0,.06);
  --avatar-a: #b8b8be;
  --avatar-b: #8e8e93;

  /* liquid glass */
  --glass: rgba(255,255,255,.58);
  --glass-2: rgba(255,255,255,.42);
  --glass-edge: rgba(255,255,255,.85);
  --glass-line: rgba(0,0,0,.06);
  --glass-shadow: 0 10px 34px rgba(0,0,0,.10), 0 1px 2px rgba(0,0,0,.05);
  --glass-blur: blur(28px) saturate(190%);
}
html[data-theme="dark"] {
  --window: #101012;
  --sidebar: rgba(28,28,30,.82);
  --sidebar-border: rgba(255,255,255,.07);
  --chat-bg: #000000;
  --chat-tint: radial-gradient(120% 80% at 100% 0%, rgba(11,132,254,.10), transparent 60%),
               radial-gradient(90% 70% at 0% 100%, rgba(52,199,89,.07), transparent 60%);
  --hairline: rgba(255,255,255,.09);
  --text: #f5f5f7;
  --text-2: #a1a1a6;
  --text-3: #8e8e93;
  --field: rgba(44,44,46,.8);
  --field-border: rgba(255,255,255,.14);
  --search: rgba(120,120,128,.24);
  --row-hover: rgba(255,255,255,.06);
  --in-bubble: #26252a;
  --in-text: #ffffff;
  --out-bubble: #0a84ff;
  --card: rgba(40,40,44,.86);
  --card-solid: #2a2a2c;
  --card-shadow: 0 20px 60px rgba(0,0,0,.6), 0 0 0 .5px rgba(255,255,255,.08);
  --avatar-a: #9a9aa0;
  --avatar-b: #66666b;
  --glass: rgba(40,40,46,.55);
  --glass-2: rgba(40,40,46,.4);
  --glass-edge: rgba(255,255,255,.22);
  --glass-line: rgba(255,255,255,.06);
  --glass-shadow: 0 10px 34px rgba(0,0,0,.45), 0 1px 2px rgba(0,0,0,.4);
}
html { height: 100%; }
html, body, #app { height: var(--app-vh, 100%); margin: 0; }
body {
  font-family: var(--font);
  font-size: 14px;
  color: var(--text);
  background: var(--window);
  -webkit-font-smoothing: antialiased;
  overflow: hidden;
  -webkit-tap-highlight-color: transparent;
}
button { font: inherit; color: inherit; background: none; border: 0; padding: 0; cursor: pointer; }
input, textarea { font: inherit; color: inherit; }
input:focus, textarea:focus, button:focus { outline: none; }
button:focus-visible, input:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }
svg { display: block; }
::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-thumb { background: rgba(127,127,127,.35); border-radius: 8px; border: 2px solid transparent; background-clip: padding-box; }
::-webkit-scrollbar-track { background: transparent; }

/* Liquid glass: blurred, saturated, with a bright top edge and a soft sheen. */
.glass {
  position: relative;
  background: var(--glass);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  box-shadow: inset 0 1px 0 var(--glass-edge), inset 0 0 0 .5px var(--glass-line), var(--glass-shadow);
}
.glass::before {
  content: ""; position: absolute; inset: 0; border-radius: inherit; pointer-events: none;
  background: linear-gradient(135deg, rgba(255,255,255,.35) 0%, rgba(255,255,255,0) 38%, rgba(255,255,255,0) 70%, rgba(255,255,255,.14) 100%);
  mix-blend-mode: soft-light;
}
@media (prefers-reduced-transparency: reduce) {
  .glass { background: var(--card-solid); -webkit-backdrop-filter: none; backdrop-filter: none; }
}
/* Same as the OS-level query above, but as an in-app Settings toggle for people whose
   system preference doesn't say this but who want it anyway. */
.reduce-transparency .glass { background: var(--card-solid); -webkit-backdrop-filter: none; backdrop-filter: none; }

/* Denser message-list rows (Settings → Appearance → Compact message list). */
.compact-rows .row { padding: 5px 10px 5px 4px; }
.compact-rows .row .avatar { width: 38px; height: 38px; font-size: 13px; }
.compact-rows .row .r-preview { -webkit-line-clamp: 1; }

/* A manual "reduce motion" override, in addition to (not instead of) the real
   prefers-reduced-motion media query every animation already respects — see
   applyPrefs() for why near-zero durations beat display:none/animation:none: it
   keeps transitionend/animationend firing so nothing relying on them gets stuck. */
.force-reduce-motion, .force-reduce-motion * {
  animation-duration: .001ms !important; animation-delay: 0s !important;
  transition-duration: .001ms !important; transition-delay: 0s !important;
  scroll-behavior: auto !important;
}

/* ------------------------------------------------------------------------
   Auth
   ------------------------------------------------------------------------ */
.auth {
  height: 100%; display: grid; place-items: center; padding: 24px; overflow: auto;
  background:
    radial-gradient(60% 50% at 20% 10%, rgba(52,199,89,.20), transparent 60%),
    radial-gradient(50% 50% at 85% 90%, rgba(11,132,254,.22), transparent 60%),
    var(--window);
}
.auth-card {
  width: 100%; max-width: 440px; min-width: 0; border-radius: 28px; padding: 34px 30px 26px; text-align: center;
  animation: rise .5s cubic-bezier(.2,.8,.2,1);
}
@keyframes rise { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
.brand { display: flex; align-items: center; justify-content: center; gap: 12px; margin-bottom: 22px; }
.app-icon {
  width: 68px; height: 68px; border-radius: 17px; display: grid; place-items: center;
  box-shadow: 0 10px 26px rgba(20,185,63,.28), inset 0 1px 0 rgba(255,255,255,.45);
  background: linear-gradient(180deg, #62f97e 0%, #16b843 100%);
}
.app-icon.blue { background: linear-gradient(180deg, #5fb0ff 0%, #0b6ee8 100%); box-shadow: 0 10px 26px rgba(11,110,232,.3), inset 0 1px 0 rgba(255,255,255,.45); }
.app-icon.red { background: linear-gradient(180deg, #ff8a80 0%, var(--red) 100%); box-shadow: 0 10px 26px rgba(255,59,48,.3), inset 0 1px 0 rgba(255,255,255,.45); }
.app-icon svg { width: 42px; height: 42px; }
.lock-screen { text-align: center; }
.lock-screen .app-icon { margin: 0 auto 18px; }
/* The one door left open once you're banned: a small, self-contained two-way DM with
   the admin, right on the lock screen — see /banned/message(s) and wireBannedChat(). */
.banned-chat { margin-top: 18px; padding-top: 16px; border-top: 1px solid var(--field-border); text-align: left; }
.banned-chat-h { font-size: 13px; font-weight: 700; margin-bottom: 8px; color: var(--text-2); }
.banned-chat-list { max-height: 160px; overflow-y: auto; display: flex; flex-direction: column; gap: 8px; margin-bottom: 8px; padding-right: 2px; }
.bc-row { display: flex; flex-direction: column; align-items: flex-start; }
.bc-row.mine { align-items: flex-end; }
.bc-bubble { max-width: 85%; padding: 7px 11px; border-radius: 14px; background: var(--field); font-size: 13.5px; line-height: 1.35; word-break: break-word; }
.bc-row.mine .bc-bubble { background: var(--out-bubble, #0b84fe); color: #fff; }
.bc-time { font-size: 10.5px; color: var(--text-3); margin-top: 2px; padding: 0 3px; }
.banned-chat-input { display: flex; gap: 8px; align-items: flex-end; }
.banned-chat-input textarea { flex: 1; resize: none; border: none; background: var(--field); color: var(--text); border-radius: 12px; padding: 9px 12px; font: inherit; font-size: 14px; box-shadow: inset 0 0 0 1px var(--field-border); }
.banned-chat-input .mini { flex: none; height: 36px; }

/* App Lock: an Apple-style passcode dial pad. Used both full-screen (boot/resume,
   inside .lock-screen) and as a settings sheet (.lock-pad-sheet) — same component. */
.sheet.lock-pad-sheet { width: 320px; padding-top: 40px; }
.lock-pad { display: flex; flex-direction: column; align-items: center; }
.lock-pad .lp-icon { width: 52px; height: 52px; border-radius: 50%; display: grid; place-items: center; background: var(--field); color: var(--text-2); margin-bottom: 10px; box-shadow: inset 0 0 0 1px var(--field-border); }
.lock-pad .lp-icon svg { width: 24px; height: 24px; }
.lock-pad h2 { font-size: 18px; font-weight: 650; letter-spacing: -.01em; margin: 0 0 4px; }
.lock-pad .lp-sub { font-size: 13px; color: var(--text-3); margin: 0 0 22px; min-height: 16px; }
.lock-pad .lp-sub.err { color: var(--red); font-weight: 600; }
.lock-pad .lp-dots { display: flex; gap: 16px; margin-bottom: 28px; }
.lock-pad .lp-dot { width: 12px; height: 12px; border-radius: 50%; box-shadow: inset 0 0 0 1.5px var(--text-3); background: transparent; transition: background-color var(--dur-ui) var(--ease-out), transform var(--dur-fast) var(--ease-out); }
.lock-pad .lp-dot.filled { background: var(--text); box-shadow: inset 0 0 0 1.5px var(--text); transform: scale(1.1); }
.lock-pad.shake { animation: lpShake .4s var(--ease-in-out); }
@keyframes lpShake { 10%, 90% { transform: translateX(-1px); } 20%, 80% { transform: translateX(2px); } 30%, 50%, 70% { transform: translateX(-4px); } 40%, 60% { transform: translateX(4px); } }
@media (prefers-reduced-motion: reduce) { .lock-pad.shake { animation: fade .3s ease; } }
.lock-pad .lp-grid { display: grid; grid-template-columns: repeat(3, 62px); gap: 16px; justify-content: center; margin-bottom: 16px; }
.lock-pad .lp-key { width: 62px; height: 62px; border-radius: 50%; background: var(--field); color: var(--text); font-size: 23px; font-weight: 500; display: grid; place-items: center; box-shadow: inset 0 0 0 1px var(--field-border); transition: transform var(--dur-fast) var(--ease-out), background-color var(--dur-ui) var(--ease-out); }
.lock-pad .lp-key:active { transform: scale(.92); background: var(--search); }
.lock-pad .lp-grid.busy { opacity: .5; }
.lock-pad .lp-key:disabled { pointer-events: none; }
.lock-pad .lp-back { background: transparent; box-shadow: none; color: var(--text-3); }
.lock-pad .lp-back svg { width: 22px; height: 22px; }
.lock-pad .lp-back:active { background: transparent; }
.auth h1 { font-size: 26px; font-weight: 700; letter-spacing: -.025em; margin: 0 0 6px; }
.auth p.sub { color: var(--text-2); margin: 0 0 24px; font-size: 15px; line-height: 1.4; }
.stack { display: flex; flex-direction: column; border-radius: 14px; overflow: hidden; text-align: left; background: var(--field); box-shadow: inset 0 0 0 1px var(--field-border); }
.stack .field { position: relative; display: flex; align-items: center; height: 52px; border-top: 1px solid var(--field-border); }
.stack .field:first-child { border-top: 0; }
.stack input { flex: 1; height: 100%; padding: 0 16px; border: 0; background: transparent; font-size: 16px; min-width: 0; }
.stack input::placeholder { color: var(--text-3); }
.stack .go {
  width: 34px; height: 34px; margin-right: 9px; border-radius: 50%; flex: none;
  border: 1.5px solid var(--gray2); color: var(--gray2); display: grid; place-items: center; transition: .18s;
}
.stack .go svg { width: 16px; height: 16px; }
.stack .go.ready { background: var(--blue); border-color: var(--blue); color: #fff; box-shadow: 0 4px 12px rgba(11,132,254,.35); }
.stack .go:active { transform: scale(.94); }
.auth .err { color: var(--red); font-size: 13px; min-height: 18px; margin: 12px 0 0; }
.auth .links { margin-top: 20px; font-size: 14px; color: var(--text-2); }
.auth .links a, .auth a { color: var(--blue); text-decoration: none; }
.auth .links a:hover { text-decoration: underline; }
.auth .fine { color: var(--text-3); font-size: 12px; margin-top: 26px; line-height: 1.5; }
.steps { display: flex; justify-content: center; gap: 6px; margin: 0 0 18px; }
.steps i { width: 6px; height: 6px; border-radius: 3px; background: var(--gray2); opacity: .5; transition: .3s; }
.steps i.on { width: 20px; background: var(--blue); opacity: 1; }
.steps i.done { background: var(--blue); opacity: .6; }
.code { display: flex; gap: 8px; justify-content: center; margin: 4px 0 6px; }
.code input {
  width: 46px; height: 56px; text-align: center; font-size: 24px; font-weight: 600; border-radius: 12px; border: 0;
  background: var(--field); box-shadow: inset 0 0 0 1px var(--field-border); transition: box-shadow .15s;
}
.code input:focus { box-shadow: inset 0 0 0 2px var(--blue); }
.demo-code {
  margin: 12px auto 0; padding: 10px 14px; border-radius: 12px; font-size: 13px; color: var(--text-2);
  background: rgba(255,204,0,.14); box-shadow: inset 0 0 0 1px rgba(255,204,0,.35); text-align: left; line-height: 1.45;
}
.demo-code b { color: var(--text); font-size: 18px; letter-spacing: .12em; font-variant-numeric: tabular-nums; }
.primary {
  width: 100%; height: 48px; border-radius: 14px; background: var(--blue); color: #fff; font-weight: 600; font-size: 16px;
  box-shadow: 0 6px 18px rgba(11,132,254,.3), inset 0 1px 0 rgba(255,255,255,.3); transition: transform .12s, filter .12s;
}
.primary:active { transform: scale(.985); filter: brightness(.95); }
.primary, .secondary, .mini, .ft-new, .ft-btn, .send, .cbtn, .call .controls button, .tray .grid button, .tapbar button, .plusmenu button, .seg button, .tone-row button, .carriers button, .composer .plus { transition: transform var(--dur-fast) var(--ease-out), background-color var(--dur-ui) var(--ease-out), color var(--dur-ui) var(--ease-out), filter var(--dur-ui) var(--ease-out), opacity var(--dur-ui) var(--ease-out); }
.mini:active, .ft-new:active, .secondary:active, .seg button:active, .tone-row button:active, .carriers button:active, .plusmenu button:active { transform: scale(.96); }
.primary:disabled { opacity: .5; cursor: default; }
.primary svg, .secondary svg { width: 20px; height: 20px; flex: none; }
.primary.green { background: var(--green); box-shadow: 0 6px 18px rgba(52,199,89,.3), inset 0 1px 0 rgba(255,255,255,.3); }
.secondary { width: 100%; height: 44px; border-radius: 14px; font-weight: 600; font-size: 15px; color: var(--blue); background: rgba(11,132,254,.10); }
.terms-row { display: flex; gap: 10px; align-items: flex-start; text-align: left; font-size: 13px; color: var(--text-2); margin: 14px 2px 4px; line-height: 1.4; }
.terms-row input { width: 18px; height: 18px; margin: 1px 0 0; accent-color: var(--blue); flex: none; }

/* ID scanner */
.scan { border-radius: 22px; overflow: hidden; position: relative; background: #0d0d10; aspect-ratio: 4 / 3; }
.scan video, .scan img, .scan canvas { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.scan .frame {
  position: absolute; left: 10%; right: 10%; top: 22%; bottom: 22%; border-radius: 14px;
  box-shadow: 0 0 0 200vmax rgba(0,0,0,.45);
}
.scan .frame i { position: absolute; width: 26px; height: 26px; border: 3px solid #fff; border-radius: 6px; }
.scan .frame i:nth-child(1) { left: -3px; top: -3px; border-right: 0; border-bottom: 0; border-radius: 10px 0 0 0; }
.scan .frame i:nth-child(2) { right: -3px; top: -3px; border-left: 0; border-bottom: 0; border-radius: 0 10px 0 0; }
.scan .frame i:nth-child(3) { left: -3px; bottom: -3px; border-right: 0; border-top: 0; border-radius: 0 0 0 10px; }
.scan .frame i:nth-child(4) { right: -3px; bottom: -3px; border-left: 0; border-top: 0; border-radius: 0 0 10px 0; }
.scan .laser { position: absolute; left: 6%; right: 6%; height: 2px; top: 30%; background: linear-gradient(90deg, transparent, #34c759, transparent); box-shadow: 0 0 12px #34c759; animation: laser 1.6s ease-in-out infinite alternate; }
@keyframes laser { from { top: 26%; } to { top: 74%; } }
.scan .hint {
  position: absolute; left: 50%; top: 12px; transform: translateX(-50%); color: #fff; font-size: 13px; font-weight: 600;
  padding: 7px 12px; border-radius: 12px; white-space: nowrap;
}
.scan .shutter {
  position: absolute; left: 50%; bottom: 14px; transform: translateX(-50%); width: 62px; height: 62px; border-radius: 50%;
  border: 4px solid #fff; display: grid; place-items: center;
}
.scan .shutter::after { content: ""; width: 48px; height: 48px; border-radius: 50%; background: #fff; transition: transform .12s; }
.scan .shutter:active::after { transform: scale(.85); }
.scan .upload { position: absolute; right: 12px; bottom: 22px; color: #fff; font-size: 12px; font-weight: 600; padding: 8px 12px; border-radius: 12px; }
.scan .nocam { position: absolute; inset: 0; display: grid; place-content: center; text-align: center; color: rgba(255,255,255,.75); font-size: 14px; gap: 12px; padding: 20px; }
.scan .nocam button { color: #fff; background: rgba(255,255,255,.16); padding: 10px 16px; border-radius: 12px; font-weight: 600; }
.scan.busy .frame { animation: pulse 1s infinite; }
@keyframes pulse { 50% { opacity: .5; } }
.scan-result { text-align: left; margin-top: 14px; }
.scan-result .lbl { font-size: 12px; color: var(--text-3); font-weight: 600; margin: 12px 4px 6px; text-transform: uppercase; letter-spacing: .04em; }
.thumb { display: flex; align-items: center; gap: 12px; margin: 10px 0 2px; text-align: left; }
.thumb img { width: 84px; height: 56px; object-fit: cover; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,.2); }
.thumb .t { font-size: 13px; color: var(--text-2); line-height: 1.4; }
.thumb .t b { color: var(--text); }
.thumb button { color: var(--blue); font-weight: 600; font-size: 13px; }

/* ------------------------------------------------------------------------
   Shell
   ------------------------------------------------------------------------ */
.shell { position: relative; height: var(--app-vh, 100%); display: grid; grid-template-columns: 72px 330px 1fr; background: var(--window); }
/* A small persistent "Beam" corner mark on iPads, per request — decorative only,
   sits above ordinary content but below any sheet/overlay (those start at z-index
   60+) so it never competes with something the user actually needs to tap. */
#ipadMark {
  position: fixed; top: calc(10px + env(safe-area-inset-top)); right: calc(14px + env(safe-area-inset-right));
  z-index: 40; font-size: 11px; font-weight: 700; letter-spacing: .02em; color: var(--text-3);
  background: var(--glass); -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur);
  padding: 4px 10px; border-radius: 8px; box-shadow: inset 0 0 0 .5px var(--glass-line), var(--glass-shadow);
  pointer-events: none; user-select: none;
}
.invite-fab {
  position: fixed; right: calc(20px + env(safe-area-inset-right)); bottom: calc(20px + env(safe-area-inset-bottom));
  z-index: 36; width: 52px; height: 52px; border-radius: 50%; display: grid; place-items: center;
  background: var(--blue); color: #fff; box-shadow: 0 8px 22px rgba(11,132,254,.4), inset 0 1px 0 rgba(255,255,255,.3);
  transition: transform var(--dur-fast) var(--ease-out);
}
/* Collapse the list for more reading room; the pane takes over the space. */
.sb-collapse { flex: none; margin-right: 2px; color: var(--text-3); }
.sb-collapse svg { width: 19px; height: 19px; }
.sb-expand {
  position: absolute; left: 78px; top: 14px; z-index: 12; width: 30px; height: 30px; border-radius: 9px;
  display: none; place-items: center; color: var(--blue); background: var(--glass);
  -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur);
  box-shadow: inset 0 0 0 .5px var(--glass-line), var(--glass-shadow);
}
.sb-expand svg { width: 18px; height: 18px; }
body.sidebar-collapsed .shell { grid-template-columns: 72px 0 1fr; }
/* Not display:none — that drops the sidebar out of grid placement and the pane
   slides into the zero-width track. Keep the track, clip what's inside it. */
body.sidebar-collapsed .sidebar { overflow: hidden; border-right: 0; }
body.sidebar-collapsed .sb-expand { display: grid; }
/* Quick route to the invite QR without digging through Settings. */
.sb-invite {
  flex: none; margin: 0 8px calc(10px + env(safe-area-inset-bottom)); padding: 11px 12px; border-radius: 13px;
  display: flex; align-items: center; justify-content: center; gap: 8px; color: var(--blue);
  font-size: 13.5px; font-weight: 600; background: rgba(11,132,254,.10);
  transition: background-color var(--dur-ui) var(--ease-out), transform var(--dur-fast) var(--ease-out);
}
.sb-invite svg { width: 17px; height: 17px; }
.sb-invite:active { transform: scale(.98); }
@media (hover: hover) and (pointer: fine) { .sb-invite:hover { background: rgba(11,132,254,.16); } }

/* ---- archived chats: hidden from Recents, never deleted ------------------- */
.archived-toggle {
  width: 100%; display: flex; align-items: center; gap: 8px; padding: 10px 10px 10px 6px;
  margin-top: 4px; border-top: .5px solid var(--hairline); color: var(--text-2);
  font-size: 13.5px; font-weight: 600; transition: background-color var(--dur-fast) var(--ease-out);
}
.archived-toggle:active { background: var(--row-hover); }
.archived-toggle .at-ic { width: 22px; height: 22px; display: grid; place-items: center; color: var(--text-3); flex: none; }
.archived-toggle .at-ic svg { width: 16px; height: 16px; }
.archived-toggle .at-label { flex: 1; text-align: left; }
.archived-toggle .at-count { color: var(--text-3); font-weight: 500; font-variant-numeric: tabular-nums; }
.archived-toggle .chev { width: 13px; height: 13px; color: var(--text-3); transform: rotate(90deg); transition: transform var(--dur-ui) var(--ease-out); }
.archived-toggle .chev svg { width: 100%; height: 100%; display: block; }
.archived-toggle .chev.open { transform: rotate(-90deg); }
/* Archived rows read as quieter than active ones, but stay fully legible and tappable —
   this is "hidden from the list," not "half-deleted." */
.row.archived { opacity: .78; }
.row.archived:active { opacity: 1; }
.invite-fab svg { width: 24px; height: 24px; }
.invite-fab:active { transform: scale(.92); }
body.show-pane .invite-fab, body:has(#updBar) .invite-fab { display: none; }
#updBar {
  position: fixed; left: 12px; right: 12px; bottom: calc(16px + env(safe-area-inset-bottom));
  z-index: 145; border-radius: 16px; padding: 12px 12px 12px 16px; display: flex; align-items: center; gap: 10px;
  animation: rise .4s cubic-bezier(.2,1.1,.3,1);
}
#updBar span { flex: 1; font-size: 13.5px; font-weight: 600; }
#updBar .mini.ghost { background: var(--field); color: var(--text); box-shadow: inset 0 0 0 1px var(--field-border); }
@media (max-width: 760px) {
  #updBar { bottom: calc(96px + env(safe-area-inset-bottom)); }
  body.show-pane #updBar { bottom: calc(16px + env(safe-area-inset-bottom)); }
  /* Clear the taller tab bar so the + doesn't sit on top of it. */
  .invite-fab { bottom: calc(92px + env(safe-area-inset-bottom)); }
}
/* ---- Update animation: tapping "Update" gets a ~3s download -> install moment
   instead of the page just vanishing into a reload. Brand-green, simple, quick. ---- */
#updAnim {
  position: fixed; inset: 0; z-index: 9999;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 18px;
  background: linear-gradient(160deg, #16281b 0%, #0a120c 100%);
  opacity: 0; transition: opacity .3s var(--ease-out);
}
#updAnim.show { opacity: 1; }
.ua-badge {
  width: 84px; height: 84px; border-radius: 22px; flex: none;
  background: linear-gradient(180deg, #62f97e 0%, #16b843 100%);
  display: grid; place-items: center;
  box-shadow: 0 10px 30px rgba(22,184,67,.4);
  transform: scale(.9); opacity: 0;
  animation: uaPop .5s var(--ease-out) .15s both, uaPulse 1.6s ease-in-out .65s infinite;
}
.ua-badge svg { width: 42px; height: 42px; }
@keyframes uaPop { from { transform: scale(.9); opacity: 0; } to { transform: scale(1); opacity: 1; } }
@keyframes uaPulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.06); } }
.ua-label { color: #fff; font-size: 15px; font-weight: 600; letter-spacing: -.01em; opacity: .9; }
.ua-track { width: 180px; height: 5px; border-radius: 3px; background: rgba(255,255,255,.15); overflow: hidden; }
.ua-fill { height: 100%; width: 0%; border-radius: 3px; background: linear-gradient(90deg, #62f97e, #16b843); transition: width 1.4s var(--ease-out); }
#updAnim.show .ua-fill { width: 55%; }
#updAnim.phase2 .ua-fill { width: 100%; transition: width 1.3s var(--ease-out); }
@media (prefers-reduced-motion: reduce) {
  #updAnim { transition: none; opacity: 1; }
  .ua-badge { animation: none; opacity: 1; transform: none; }
  .ua-fill { transition: none; }
}
/* "You both know…" suggestion */
.sheet.suggest-sheet { width: 340px; text-align: center; position: relative; padding-top: 42px; display: flex; flex-direction: column; align-items: center; gap: 4px; }
.sheet.suggest-sheet .it-x { position: absolute; top: 12px; right: 12px; width: 30px; height: 30px; border-radius: 50%; display: grid; place-items: center; background: var(--search); color: var(--text-3); }
.sheet.suggest-sheet .avatar { margin-bottom: 10px; }
.sheet.suggest-sheet b { font-size: 19px; }
.sheet.suggest-sheet .fine { margin: 0 0 4px; }
.sheet.suggest-sheet .acts { width: 100%; margin-top: 10px; }
.sheet.suggest-sheet .mini.ghost { width: 100%; height: 38px; border-radius: 12px; margin-top: 8px; background: transparent; color: var(--text-2); box-shadow: none; font-size: 14px; }
/* The "don't swipe away" reminder reuses .suggest-sheet's layout with an icon bubble
   standing in for the avatar. */
.swipe-warn-ic { width: 84px; height: 84px; border-radius: 50%; display: grid; place-items: center; margin-bottom: 10px; background: rgba(10,132,255,.12); color: var(--blue); }
.swipe-warn-ic svg { width: 38px; height: 38px; }
.sheet.suggest-sheet .fine { line-height: 1.4; }

/* One-time callout pointing at the Stories rail icon — a speech bubble, not a modal,
   so it sits outside the sheet-wrap system entirely (see maybeShowStoriesTip()). */
.stories-tip {
  position: fixed; z-index: 200; width: 220px; padding: 12px 14px 14px; text-align: left;
  border-radius: 16px; background: var(--card-solid); box-shadow: 0 12px 30px rgba(0,0,0,.28), inset 0 0 0 1px var(--glass-line);
  transform: translateY(-50%) scale(.9); opacity: 0; transform-origin: left center;
  transition: transform var(--dur-sheet, .26s) var(--ease-out), opacity var(--dur-sheet, .26s) var(--ease-out);
}
.stories-tip.show { transform: translateY(-50%) scale(1); opacity: 1; }
.stories-tip::before {
  content: ""; position: absolute; left: -6px; top: 50%; transform: translateY(-50%) rotate(45deg);
  width: 12px; height: 12px; background: var(--card-solid); box-shadow: -1px 1px 0 var(--glass-line);
}
.stories-tip .st-x { position: absolute; top: 8px; right: 8px; width: 20px; height: 20px; border-radius: 50%; display: grid; place-items: center; color: var(--text-3); }
.stories-tip b { display: block; font-size: 13.5px; margin: 0 16px 3px 0; }
.stories-tip span { display: block; font-size: 12px; color: var(--text-2); line-height: 1.35; margin-bottom: 10px; }
.stories-tip .st-go { width: 100%; height: 32px; border-radius: 10px; background: var(--blue); color: #fff; font-weight: 700; font-size: 13px; }
@media (prefers-reduced-motion: reduce) { .stories-tip { transition: opacity var(--dur-ui, .15s) ease; transform: translateY(-50%) !important; } }

/* New chat / add friends */
.sheet.new-chat { width: 420px; position: relative; padding-top: 40px; display: flex; flex-direction: column; max-height: min(620px, calc(var(--app-vh, 100vh) - 60px)); }
.sheet.new-chat .it-x { position: absolute; top: 12px; right: 12px; width: 30px; height: 30px; border-radius: 50%; display: grid; place-items: center; background: var(--search); color: var(--text-3); }
.sheet.new-chat h3 { margin: 0 0 4px; }
.sheet.new-chat .fine { margin: 0 0 12px; }
.sheet.new-chat .search { margin: 0 0 10px; }
.nc-picked { display: flex; flex-wrap: wrap; gap: 6px; }
.nc-picked:not(:empty) { margin-bottom: 10px; }
.nc-list { flex: 1; min-height: 0; overflow-y: auto; margin: 0 -6px; padding: 0 6px; }
/* .row.person sets its own 2-column track later in this file, so match its
   specificity or the checkmark wraps onto a second line under the avatar. */
.nc-list .row.person { grid-template-columns: 46px 1fr 26px; gap: 0 10px; padding: 8px 8px 8px 6px; cursor: pointer; }
.nc-list .row.person.picked { background: rgba(11,132,254,.10); }
.nc-list .row.person.busy { opacity: .5; pointer-events: none; }
.nc-check { display: grid; place-items: center; color: var(--blue); }
.nc-check svg { width: 20px; height: 20px; }
.nc-go { width: 100%; height: 42px; border-radius: 13px; font-size: 14.5px; margin-top: 12px; flex: none; }
.nc-go:disabled { opacity: .45; cursor: default; }
.sheet.invite-sheet { width: 380px; text-align: center; position: relative; padding-top: 40px; }
.sheet.invite-sheet .it-x { position: absolute; top: 12px; right: 12px; width: 30px; height: 30px; border-radius: 50%; display: grid; place-items: center; background: var(--search); color: var(--text-3); }
.sheet.invite-sheet h3 { margin: 0 0 4px; }
.sheet.invite-sheet .fine { margin: 0 0 16px; }
.sheet.invite-sheet .qr-box { display: flex; justify-content: center; padding: 14px; background: #fff; border-radius: 16px; margin: 0 auto 16px; width: 204px; box-shadow: inset 0 0 0 1px rgba(0,0,0,.06); }
.sheet.invite-sheet #inviteQr img, .sheet.invite-sheet #inviteQr canvas { display: block; border-radius: 4px; }
.sheet.invite-sheet .acts { display: flex; gap: 8px; margin-top: 10px; }
.sheet.invite-sheet .acts button { flex: 1; height: 38px; border-radius: 11px; color: var(--text); background: var(--field); box-shadow: inset 0 0 0 1px var(--field-border); font-weight: 600; font-size: 14px; }
.rail {
  display: flex; flex-direction: column; align-items: center; padding: 16px 0 14px; gap: 14px;
  border-right: 1px solid var(--sidebar-border);
}
.dock-icon {
  width: 46px; height: 46px; border-radius: 13px; display: grid; place-items: center; position: relative;
  transition: transform var(--dur-ui) var(--ease-out), filter var(--dur-ui) var(--ease-out); filter: saturate(.2) opacity(.7);
}
.dock-icon.active { filter: none; }
@media (hover: hover) and (pointer: fine) { .dock-icon:hover { transform: translateY(-2px) scale(1.04); filter: none; } }
.dock-icon:active { transform: scale(.95); }
.dock-icon.messages { background: linear-gradient(180deg, #62f97e, #16b843); box-shadow: 0 4px 12px rgba(20,185,63,.28), inset 0 1px 0 rgba(255,255,255,.45); }
.dock-icon.calls { background: linear-gradient(180deg, #62f97e, #16b843); box-shadow: 0 4px 12px rgba(20,185,63,.28), inset 0 1px 0 rgba(255,255,255,.45); }
.dock-icon svg { width: 28px; height: 28px; }
.dock-icon::after { content: ""; position: absolute; left: 50%; bottom: -9px; transform: translateX(-50%); width: 4px; height: 4px; border-radius: 50%; background: var(--text-3); opacity: 0; transition: .2s; }
.dock-icon.active::after { opacity: 1; }
.badge {
  position: absolute; top: -6px; right: -6px; min-width: 20px; height: 20px; padding: 0 6px; border-radius: 10px;
  background: var(--red); color: #fff; font-size: 12px; font-weight: 700; display: grid; place-items: center; box-shadow: 0 0 0 2px var(--window);
}
.rail .spacer { flex: 1; }
.rail .me { width: 38px; height: 38px; font-size: 13px; box-shadow: 0 0 0 2px var(--window), 0 0 0 3.5px var(--hairline); }

.sidebar { background: var(--sidebar); border-right: 1px solid var(--sidebar-border); display: flex; flex-direction: column; min-width: 0; min-height: 0; -webkit-backdrop-filter: blur(20px); backdrop-filter: blur(20px); }
.sb-top { padding: 16px 14px 8px; display: flex; align-items: center; gap: 8px; }
.sb-top h2 { margin: 0; font-size: 24px; font-weight: 700; letter-spacing: -.025em; flex: 1; }
.icon-btn { width: 32px; height: 32px; border-radius: 9px; display: grid; place-items: center; color: var(--text-2); transition: background-color var(--dur-ui) var(--ease-out), transform var(--dur-fast) var(--ease-out); }
.icon-btn:hover { background: var(--row-hover); }
.icon-btn:active { transform: scale(.92); }
.icon-btn svg { width: 19px; height: 19px; }
.icon-btn.blue { color: var(--blue); }
.search { margin: 0 12px 8px; height: 32px; border-radius: 10px; background: var(--search); display: flex; align-items: center; gap: 6px; padding: 0 9px; color: var(--text-3); }
.search svg { width: 14px; height: 14px; flex: none; }
.search input { flex: 1; border: 0; background: transparent; height: 100%; font-size: 14px; color: var(--text); min-width: 0; }
.search input::placeholder { color: var(--text-3); }

.list { flex: 1; overflow-y: auto; padding: 0 8px 10px; }
.chip-filters { display: flex; gap: 6px; margin: 0 12px 8px; flex-wrap: wrap; }
.chip-filters button { padding: 5px 12px; border-radius: 10px; background: var(--search); color: var(--text-2); font-size: 12.5px; font-weight: 600; }
.chip-filters button.on { background: var(--blue); color: #fff; }
.row { display: grid; grid-template-columns: 14px 46px 1fr; gap: 0 10px; align-items: center; padding: 9px 10px 9px 4px; border-radius: 12px; cursor: default; user-select: none; position: relative; transition: background-color var(--dur-ui) var(--ease-out), transform var(--dur-fast) var(--ease-out); }
.row:active { transform: scale(.985); }
.row:hover { background: var(--row-hover); }
.row:active { background: var(--hairline); }
.row.active { background: var(--blue); color: #fff; box-shadow: inset 0 1px 0 rgba(255,255,255,.25); }
.row.active .r-time, .row.active .r-preview, .row.active .r-sub { color: rgba(255,255,255,.82); }
.row .dot { width: 10px; height: 10px; border-radius: 50%; background: var(--blue); opacity: 0; justify-self: center; transition: opacity .2s; }
.row.unread .dot { opacity: 1; }
.row.active .dot { background: #fff; }
.row .r-body { min-width: 0; }
.row .r-head { display: flex; align-items: baseline; gap: 8px; }
.row .r-name { font-weight: 600; font-size: 14.5px; flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.row .r-pin { display: inline-grid; place-items: center; width: 12px; height: 12px; margin-right: 4px; color: var(--text-3); vertical-align: -1px; }
.row .r-pin svg { width: 100%; height: 100%; }
.row.active .r-pin { color: rgba(255,255,255,.7); }
.row .r-muted { display: inline-grid; place-items: center; width: 13px; height: 13px; margin-left: 5px; color: var(--text-3); vertical-align: -2px; }
.row .r-muted svg { width: 100%; height: 100%; }
.row.active .r-muted { color: rgba(255,255,255,.7); }
/* A muted chat still counts unread, it just doesn't shout about it. */
.row .r-badge.muted { background: var(--gray2); }
.row .r-draft { color: var(--red); font-weight: 600; }
.row.active .r-draft { color: #ffd6d3; }
/* A search result row: same shape as a chat row, but the matched run is highlighted
   so it's obvious why it came back. */
.row.hit .r-preview mark { background: color-mix(in srgb, var(--blue) 26%, transparent); color: inherit; border-radius: 4px; padding: 0 2px; }
.row.hit .r-preview b { font-weight: 650; }
.hit-flash { animation: hitFlash 1.8s var(--ease-out); }
@keyframes hitFlash {
  0%, 55% { box-shadow: 0 0 0 3px color-mix(in srgb, var(--blue) 55%, transparent); }
  100% { box-shadow: 0 0 0 3px transparent; }
}
/* Back to the newest message from deep in the history. */
.jump-down {
  position: absolute; right: 18px; bottom: 84px; z-index: 6; width: 40px; height: 40px; border-radius: 50%;
  display: grid; place-items: center; background: var(--card-solid); color: var(--text);
  box-shadow: 0 6px 20px rgba(0,0,0,.18), inset 0 0 0 .5px var(--glass-line);
  opacity: 0; transform: translateY(8px) scale(.9); pointer-events: none;
  transition: opacity var(--dur-ui) var(--ease-out), transform var(--dur-ui) var(--ease-out);
}
.jump-down.show { opacity: 1; transform: none; pointer-events: auto; }
.jump-down svg { width: 18px; height: 18px; }
.jump-down:active { transform: scale(.92); }
/* Photos & videos grid inside a chat. */
.gal-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 4px; overflow-y: auto; min-height: 0; margin: 0 -6px; padding: 0 6px; }
.gal-item { position: relative; aspect-ratio: 1; border-radius: 10px; overflow: hidden; background: var(--search); }
.gal-item img { width: 100%; height: 100%; object-fit: cover; display: block; }
.gal-item .gal-play, .gal-item .gal-none { position: absolute; inset: 0; display: grid; place-items: center; color: #fff; }
.gal-item .gal-play { background: rgba(0,0,0,.25); }
.gal-item .gal-play svg, .gal-item .gal-none svg { width: 20px; height: 20px; }
.gal-item .gal-none { color: var(--text-3); }
@media (hover: hover) and (pointer: fine) { .gal-item:hover img { transform: scale(1.04); } .gal-item img { transition: transform var(--dur-ui) var(--ease-out); } }
/* Green when they're online, grey when they're not — direct chats only. */
.row .status-dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; margin-left: 6px; background: var(--gray2); vertical-align: 2px; flex: none; }
.row .status-dot.on { background: var(--green); box-shadow: 0 0 0 2px color-mix(in srgb, var(--green) 20%, transparent); }
.row .r-time { font-size: 12px; color: var(--text-3); white-space: nowrap; }
/* An unsized inline <svg> falls back to 300x150, which blew the rows apart on
   desktop. Size it here and only reveal it at phone widths. */
.row .r-time .chev { display: none; margin-left: 4px; color: var(--gray2); }
.row .r-time .chev svg { width: 15px; height: 15px; display: block; }
.row .r-preview { font-size: 13px; color: var(--text-2); line-height: 1.32; margin-top: 2px; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; word-break: break-word; }
.row .r-preview .missed { color: var(--red); }
.row.active .r-preview .missed { color: #ffd6d3; }
.empty { text-align: center; color: var(--text-3); padding: 60px 20px; font-size: 13px; line-height: 1.5; }

.avatar {
  width: 46px; height: 46px; border-radius: 50%; flex: none; display: grid; place-items: center;
  background: linear-gradient(180deg, var(--avatar-a), var(--avatar-b)); color: #fff; font-weight: 600; font-size: 17px; letter-spacing: .02em;
  text-shadow: 0 1px 1px rgba(0,0,0,.1); user-select: none; box-shadow: inset 0 1px 0 rgba(255,255,255,.35);
}
.avatar.unreg { background: linear-gradient(180deg, #ff7b73, #e0342b); }
.avatar.sm { width: 32px; height: 32px; font-size: 12px; }
.avatar.xs { width: 24px; height: 24px; font-size: 10px; }
.avatar.lg { width: 80px; height: 80px; font-size: 32px; }
.avatar.xl { width: 128px; height: 128px; font-size: 50px; }
.avatar.group { position: relative; background: none; box-shadow: none; }
.avatar.group .avatar { position: absolute; width: 62%; height: 62%; font-size: 11px; }
.avatar.group .avatar:first-child { top: 0; right: 0; }
.avatar.group .avatar:last-child { bottom: 0; left: 0; box-shadow: 0 0 0 2px var(--sidebar); }
/* Pinned chats get a quiet gold ring on the avatar — a static halo, not a loop. This
   list is seen dozens of times a day, so per the motion budget it stays a plain state
   indicator with no animation, not something drawing the eye on every render. */
.row.pinned .avatar { box-shadow: inset 0 1px 0 rgba(255,255,255,.35), 0 0 0 2px var(--gold, #e8b84b), 0 0 8px 1px rgba(232,184,75,.55); }
.row.pinned.active .avatar { box-shadow: inset 0 1px 0 rgba(255,255,255,.35), 0 0 0 2px #fff, 0 0 8px 1px rgba(255,255,255,.6); }

/* ------------------------------------------------------------------------
   Chat pane — thread scrolls beneath floating glass chrome
   ------------------------------------------------------------------------ */
.pane { position: relative; min-width: 0; min-height: 0; background: var(--chat-bg); display: flex; flex-direction: column; }
.pane::before { content: ""; position: absolute; inset: 0; background: var(--chat-tint); pointer-events: none; }
.pane-head {
  position: absolute; top: 10px; left: 12px; right: 12px; z-index: 5; height: 54px; border-radius: 18px;
  display: flex; align-items: center; gap: 10px; padding: 0 8px 0 10px;
}
.pane-head .who { flex: 1; min-width: 0; display: flex; align-items: center; gap: 10px; }
.pane-head .who .n { font-weight: 600; font-size: 14.5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; letter-spacing: -.01em; }
.pane-head .who .s { font-size: 11.5px; color: var(--text-3); margin-top: 1px; }
.pane-head .who .s.red { color: var(--red); }
/* @username and online/offline crossfading in place, right under their name at the top
   of the chat — driven entirely by CSS so it keeps looping without a JS timer. */
.pane-head .who .s-swap { position: relative; display: inline-block; height: 14px; min-width: 70px; vertical-align: top; }
.pane-head .who .s-swap span { position: absolute; left: 0; top: 0; white-space: nowrap; opacity: 0; animation: rowSwap 6s var(--ease-in-out, ease-in-out) infinite; }
.pane-head .who .s-swap span.on { color: var(--green); }
.pane-head .who .s-swap span:nth-child(2) { animation-delay: 3s; }
@keyframes rowSwap { 0%, 4% { opacity: 0; } 12%, 42% { opacity: 1; } 50%, 100% { opacity: 0; } }
@media (prefers-reduced-motion: reduce) {
  .pane-head .who .s-swap span { animation: none; opacity: 1; position: static; }
  .pane-head .who .s-swap span:nth-child(2) { display: none; }
}
/* WhatsApp/Snapchat-style "typing…" subtitle: italic, with a period count that cycles
   . -> .. -> ... instead of the usual bouncing-dots bubble used inside the thread itself. */
.pane-head .who .typing-status { font-style: italic; color: var(--text-2); animation: arriveIn var(--dur-sheet) var(--ease-out) both; }
.pane-head .who .typing-dots::after { content: "."; display: inline-block; width: 13px; text-align: left; animation: typingDotsCycle 1.2s steps(1, end) infinite; }
@keyframes typingDotsCycle { 0%, 100% { content: "."; } 33% { content: ".."; } 66% { content: "..."; } }
@media (prefers-reduced-motion: reduce) { .pane-head .who .typing-dots::after { animation: none; content: "..."; } }
.pane-head .actions { display: flex; gap: 2px; }
.back { display: none; }

.thread { flex: 1; overflow-y: auto; overflow-x: hidden; padding: 78px 16px calc(var(--composer-h, 92px) + 24px); display: flex; flex-direction: column; overscroll-behavior: contain; position: relative; }
.thread .grow { flex: 1; }
.stamp { text-align: center; font-size: 11px; color: var(--text-3); margin: 16px 0 8px; font-weight: 500; }
.stamp b { font-weight: 600; }
.sender { font-size: 11px; color: var(--text-3); margin: 6px 0 2px 12px; }
.sys { text-align: center; font-size: 12px; color: var(--text-3); margin: 10px 0; }
.sys.notice { background: var(--search); border-radius: 10px; padding: 5px 12px; align-self: center; margin: 6px 0; max-width: 80%; }
/* A quick admin notice, dropped straight into the thread — needs to read as clearly
   not-from-a-member at a glance, so it borrows the admin panel's red accent instead
   of the plain grey every other system notice uses. */
.sys.notice.admin-notice { background: rgba(255, 69, 58, .12); box-shadow: inset 0 0 0 1px rgba(255, 69, 58, .35); text-align: center; max-width: 90%; }
.sys.notice.admin-notice svg { width: 13px; height: 13px; vertical-align: -2px; margin-right: 4px; color: var(--red); }
.sys.notice.admin-notice b { color: var(--red); }

.msg { display: flex; align-items: flex-end; margin-top: 2px; max-width: 100%; }
.msg.gap { margin-top: 8px; }
.msg.out { justify-content: flex-end; }
.msg.in { justify-content: flex-start; }
.msg .col { display: flex; flex-direction: column; max-width: min(68%, 560px); position: relative; }
.msg.out .col { align-items: flex-end; }
.msg.in .col { align-items: flex-start; }
.msg .fail-badge {
  width: 22px; height: 22px; border-radius: 50%; background: var(--red); color: #fff; font-weight: 800; font-size: 14px;
  display: grid; place-items: center; margin: 0 8px 8px 0; flex: none; box-shadow: 0 2px 6px rgba(255,59,48,.4);
}

.bubble {
  position: relative; padding: 7px 12px 8px; border-radius: var(--bubble-radius, 18px); font-size: var(--msg-size, 15px); line-height: 1.32;
  white-space: pre-wrap; word-break: break-word; cursor: default; transition: transform .12s;
  will-change: transform;
}
.thread[data-bubble="red"] { --out-bubble: #ff453a; --out-text: #fff; }
.thread[data-bubble="blue"] { --out-bubble: #0a84ff; --out-text: #fff; }
.msg.out .bubble { background: var(--out-bubble); color: var(--out-text); }
.msg.in .bubble { background: var(--in-bubble); color: var(--in-text); }
.msg.out.sms .bubble { background: var(--sms-bubble); }
.bubble a { color: inherit; text-decoration: underline; }
.msg.in .bubble a { color: var(--blue); }
.bubble.tail::before, .bubble.tail::after { content: ""; position: absolute; bottom: 0; height: 20px; }
.msg.out .bubble.tail::before { right: -8px; width: 20px; background: var(--out-bubble); border-bottom-left-radius: 16px 14px; }
.msg.out.sms .bubble.tail::before { background: #ff3b30; }
.msg.out .bubble.tail::after { right: -26px; width: 26px; background: var(--chat-bg); border-bottom-left-radius: 10px; }
.msg.in .bubble.tail::before { left: -8px; width: 20px; background: var(--in-bubble); border-bottom-right-radius: 16px 14px; }
.msg.in .bubble.tail::after { left: -26px; width: 26px; background: var(--chat-bg); border-bottom-right-radius: 10px; }
.bubble.img { padding: 0; overflow: hidden; background: transparent !important; }
.bubble.img img { display: block; max-width: 100%; max-height: 360px; border-radius: 18px; }
.bubble.img.tail::before, .bubble.img.tail::after { display: none; }
.bubble.big { background: transparent !important; padding: 2px 4px; font-size: 44px; line-height: 1.1; }
.bubble.big.tail::before, .bubble.big.tail::after { display: none; }

.bubble.call { display: flex; align-items: center; gap: 10px; padding: 8px 14px 8px 10px; min-width: 200px; }
.bubble.call .ci { width: 34px; height: 34px; border-radius: 50%; display: grid; place-items: center; background: rgba(255,255,255,.22); }
.msg.in .bubble.call .ci { background: rgba(0,0,0,.06); }
@media (prefers-color-scheme: dark) { html:not([data-theme="light"]) .msg.in .bubble.call .ci { background: rgba(255,255,255,.1); } }
html[data-theme="dark"] .msg.in .bubble.call .ci { background: rgba(255,255,255,.1); }
.bubble.call .ci svg { width: 18px; height: 18px; }
.bubble.call .ct { font-size: 14px; font-weight: 600; line-height: 1.2; }
.bubble.call .cs { font-size: 12px; opacity: .8; margin-top: 1px; }
.bubble.call .cs.missed { color: var(--red); opacity: 1; }
.bubble.call .cb { margin-left: auto; font-size: 13px; font-weight: 600; padding-left: 8px; }
.msg.in .bubble.call .cb { color: var(--blue); }

.reacts { position: absolute; top: -12px; display: flex; gap: 2px; z-index: 1; }
.msg.out .reacts { left: -10px; }
.msg.in .reacts { right: -10px; }
.react { height: 24px; min-width: 24px; padding: 0 5px; border-radius: 13px; display: grid; place-items: center; font-size: 13px; line-height: 1; box-shadow: var(--pill-shadow); animation: pop .25s cubic-bezier(.2,1.4,.3,1); }
.react.mine { background: var(--out-bubble); }
.react.theirs { background: var(--in-bubble); }
.col.has-reacts { margin-top: 12px; }

.status { font-size: 11px; color: var(--text-3); margin: 3px 6px 0; font-weight: 500; }
.status b { font-weight: 600; }
.status.red { color: var(--red); font-weight: 600; }
.fx-label { font-size: 11px; color: var(--text-3); margin: 3px 6px 0; }
.fx-label button { color: var(--blue); font-weight: 600; font-size: 11px; margin-left: 4px; }

/* Arrival animations — new bubbles spring in from where they came. */
@keyframes arriveOut { from { opacity: 0; transform: translate(8px, 16px) scale(.92); } to { opacity: 1; transform: none; } }
@keyframes arriveIn  { from { opacity: 0; transform: translate(-8px, 12px) scale(.94); } to { opacity: 1; transform: none; } }
.msg.arrive.out .col { transform-origin: bottom right; animation: arriveOut var(--dur-sheet) var(--ease-out) both; }
.msg.arrive.in  .col { transform-origin: bottom left;  animation: arriveIn var(--dur-sheet) var(--ease-out) both; }

/* Bubble effects */
@keyframes fxSlam { 0% { transform: scale(3.2) rotate(-4deg); opacity: 0; } 30% { transform: scale(1) rotate(0); opacity: 1; } 40% { transform: translate(-3px, 2px) scale(1.02); } 50% { transform: translate(4px, -2px); } 60% { transform: translate(-2px, 1px); } 70% { transform: none; } }
@keyframes fxShake { 0%, 100% { transform: none; } 20% { transform: translate(-3px, 1px); } 40% { transform: translate(3px, -1px); } 60% { transform: translate(-2px, 1px); } 80% { transform: translate(2px, -1px); } }
@keyframes fxLoud { 0% { transform: scale(.4); } 35% { transform: scale(1.7); } 45% { transform: scale(1.62) rotate(1deg); } 55% { transform: scale(1.7) rotate(-1deg); } 65% { transform: scale(1.6); } 100% { transform: scale(1); } }
@keyframes fxGentle { 0% { transform: scale(.28); opacity: .4; } 100% { transform: scale(1); opacity: 1; } }
.bubble.fx-slam { animation: fxSlam .9s cubic-bezier(.2,.9,.3,1) both; }
.thread.fx-shake { animation: fxShake .35s .3s ease-in-out; }
.bubble.fx-loud { animation: fxLoud 1.3s cubic-bezier(.3,.8,.3,1) both; }
.msg.out .bubble.fx-loud { transform-origin: bottom right; }
.msg.in .bubble.fx-loud { transform-origin: bottom left; }
.bubble.fx-gentle { animation: fxGentle 1.6s cubic-bezier(.2,.8,.2,1) both; }
.msg.out .bubble.fx-gentle { transform-origin: bottom right; }
.msg.in .bubble.fx-gentle { transform-origin: bottom left; }
.bubble.ink { cursor: pointer; }
.bubble.ink .ink-text { filter: blur(6px); opacity: 0; transition: filter .6s, opacity .6s; }
.bubble.ink .ink-dust { position: absolute; inset: 0; border-radius: inherit; overflow: hidden; pointer-events: none;
  background-image: radial-gradient(rgba(255,255,255,.9) 1px, transparent 1.4px), radial-gradient(rgba(255,255,255,.55) 1px, transparent 1.5px);
  background-size: 9px 9px, 13px 13px; background-position: 0 0, 4px 6px; animation: dust 3s linear infinite; mix-blend-mode: screen; }
.msg.in .bubble.ink .ink-dust { background-image: radial-gradient(rgba(60,60,67,.8) 1px, transparent 1.4px), radial-gradient(rgba(60,60,67,.45) 1px, transparent 1.5px); mix-blend-mode: normal; }
@keyframes dust { from { background-position: 0 0, 4px 6px; } to { background-position: 9px 18px, -9px 19px; } }
.bubble.ink.revealed .ink-text { filter: none; opacity: 1; }
.bubble.ink.revealed .ink-dust { opacity: 0; transition: opacity .5s; }

/* Above .call (z 80) so effects show during a call, still below sheets/toasts. */
.fx-canvas { position: fixed; inset: 0; z-index: 86; pointer-events: none; }
.fx-spot { position: fixed; inset: 0; z-index: 85; pointer-events: none; background: rgba(0,0,0,.72); animation: fade .5s; }
.spotlit { position: relative; z-index: 87; }

.typing { display: flex; margin-top: 8px; animation: arriveIn .3s cubic-bezier(.2,1.1,.3,1); }
.typing .tb { position: relative; background: var(--in-bubble); border-radius: 18px; padding: 12px 14px; display: flex; gap: 5px; align-items: center; margin-left: 6px; }
.typing .tb::before, .typing .tb::after { content: ""; position: absolute; border-radius: 50%; background: var(--in-bubble); }
.typing .tb::before { width: 12px; height: 12px; left: -4px; bottom: -1px; }
.typing .tb::after { width: 6px; height: 6px; left: -10px; bottom: -6px; }
.typing i { width: 8px; height: 8px; border-radius: 50%; background: #8e8e93; animation: tdots 1.1s infinite cubic-bezier(.45,0,.2,1); }
.typing i:nth-child(2) { animation-delay: .15s; }
.typing i:nth-child(3) { animation-delay: .3s; }
@keyframes tdots { 0%, 62%, 100% { opacity: .3; transform: translateY(0) scale(.82); } 28% { opacity: 1; transform: translateY(-4px) scale(1); } }

.tapbar { position: absolute; z-index: 30; border-radius: 26px; padding: 6px 8px; display: flex; gap: 2px; animation: pop var(--dur-ui) var(--ease-out); transform-origin: bottom center; }
@keyframes pop { from { transform: scale(.94); opacity: 0; } to { transform: none; opacity: 1; } }
.tapbar button { width: 36px; height: 36px; border-radius: 50%; font-size: 20px; display: grid; place-items: center; transition: transform .12s, background .12s; }
@media (hover: hover) and (pointer: fine) { .tapbar button:hover { transform: scale(1.18); } }
.tapbar button.on { background: var(--blue); }

/* Composer — floating glass pill */
.composer-wrap { position: absolute; left: 12px; right: 12px; bottom: 12px; z-index: 5; display: flex; flex-direction: column; gap: 8px; }
.composer { display: flex; align-items: flex-end; gap: 8px; padding: 6px 6px 6px 8px; border-radius: 24px; }
.composer .plus { width: 34px; height: 34px; border-radius: 50%; display: grid; place-items: center; color: var(--text-2); background: var(--search); margin-bottom: 1px; transition: transform .1s; }
.composer .plus:active { transform: scale(.9); }
.composer .plus svg { width: 16px; height: 16px; }
.field-wrap { flex: 1; position: relative; border-radius: 18px; background: var(--field); box-shadow: inset 0 0 0 1px var(--field-border); display: flex; align-items: flex-end; min-height: 36px; }
.field-wrap textarea { flex: 1; resize: none; border: 0; background: transparent; padding: 8px 40px 8px 13px; max-height: 160px; font-size: 15px; line-height: 1.3; color: var(--text); }
.field-wrap textarea::placeholder { color: var(--text-3); }
.send { position: absolute; right: 4px; bottom: 4px; width: 28px; height: 28px; border-radius: 50%; background: var(--blue); color: #fff; display: grid; place-items: center; transform: scale(.5); opacity: 0; transition: .18s cubic-bezier(.2,1,.3,1); pointer-events: none; box-shadow: 0 2px 8px rgba(11,132,254,.35); }
.send.sms { background: var(--red); box-shadow: 0 2px 8px rgba(255,59,48,.35); }
.send svg { width: 14px; height: 14px; }
.send.on { transform: none; opacity: 1; pointer-events: auto; }
.send:active { transform: scale(.9); }
.attach-preview { display: flex; gap: 8px; padding: 10px 12px 4px; flex-wrap: wrap; border-radius: 18px; }
.attach-preview .ap { position: relative; }
.attach-preview img { height: 90px; border-radius: 12px; display: block; box-shadow: 0 2px 8px rgba(0,0,0,.15); }
.attach-preview .x { position: absolute; top: -6px; right: -6px; width: 20px; height: 20px; border-radius: 50%; background: #3a3a3c; color: #fff; display: grid; place-items: center; font-size: 12px; box-shadow: 0 0 0 2px var(--chat-bg); }

/* Effect picker */
.fx-pick { position: fixed; inset: 0; z-index: 75; display: flex; flex-direction: column; animation: fade .2s; background: rgba(0,0,0,.35); }
.fx-pick .fx-top { display: flex; justify-content: center; padding: 18px 16px 0; }
.seg { display: inline-flex; padding: 3px; border-radius: 12px; background: rgba(120,120,128,.24); }
.seg button { padding: 6px 18px; border-radius: 9px; font-size: 13px; font-weight: 600; color: #fff; transition: .15s; }
.seg button.on { background: rgba(255,255,255,.9); color: #111; box-shadow: 0 1px 4px rgba(0,0,0,.2); }
@media (prefers-color-scheme: dark) { .seg { background: rgba(120,120,128,.32); } }
.fx-stage { flex: 1; display: grid; place-items: center; padding: 20px; position: relative; }
.fx-stage .demo { max-width: 70%; }
.fx-stage .demo .bubble { font-size: 17px; }
.fx-list { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; padding: 0 16px 16px; }
.fx-list button { color: #fff; font-size: 13px; font-weight: 600; padding: 10px 16px; border-radius: 16px; background: rgba(255,255,255,.16); -webkit-backdrop-filter: blur(20px); backdrop-filter: blur(20px); box-shadow: inset 0 1px 0 rgba(255,255,255,.3); transition: transform .12s, background .12s; }
@media (hover: hover) and (pointer: fine) { .fx-list button:hover { transform: translateY(-2px); } }
.fx-list button.on { background: rgba(255,255,255,.92); color: #111; }
.fx-send { display: flex; justify-content: center; gap: 14px; align-items: center; padding: 0 0 26px; }
.fx-send .x { width: 44px; height: 44px; border-radius: 50%; background: rgba(255,255,255,.2); color: #fff; display: grid; place-items: center; }
.fx-send .x svg { width: 16px; height: 16px; }
.fx-send .go { width: 56px; height: 56px; border-radius: 50%; background: var(--blue); color: #fff; display: grid; place-items: center; box-shadow: 0 8px 24px rgba(11,132,254,.45); transition: transform .12s; }
.fx-send .go:active { transform: scale(.92); }
.fx-send .go svg { width: 24px; height: 24px; }

/* New message "To:" field */
.to-bar { position: absolute; top: 72px; left: 12px; right: 12px; z-index: 6; border-radius: 16px; padding: 8px 14px; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; min-height: 46px; }
.to-bar label { color: var(--text-3); font-size: 14px; }
.chip { background: rgba(11,132,254,.14); color: var(--blue); border-radius: 14px; padding: 3px 10px; font-size: 13px; font-weight: 500; display: flex; gap: 6px; align-items: center; animation: pop .2s cubic-bezier(.2,1.3,.3,1); }
.chip.bad { background: rgba(255,59,48,.14); color: var(--red); }
.chip button { opacity: .7; }
.to-bar input { flex: 1; min-width: 160px; border: 0; background: transparent; font-size: 14px; height: 26px; }
.suggest { position: absolute; left: 0; right: 0; top: calc(100% + 6px); border-radius: 14px; padding: 6px; max-height: 280px; overflow: auto; z-index: 40; }
.suggest .row, .picks .row { grid-template-columns: 36px 1fr; padding: 6px 8px; }
.suggest .row .r-name, .picks .row .r-name { font-size: 13px; }
.suggest .row .r-preview, .picks .row .r-preview { -webkit-line-clamp: 1; font-size: 12px; }
.suggest .row:hover, .picks .row:hover { background: var(--blue); color: #fff; }
.suggest .row:hover .r-preview, .picks .row:hover .r-preview { color: rgba(255,255,255,.85); }
.thread.compose { padding-top: 130px; }

.blank { flex: 1; display: grid; place-items: center; color: var(--text-3); font-size: 15px; text-align: center; position: relative; }
.blank .big-icon { width: 96px; height: 96px; margin: 0 auto 14px; opacity: .2; }
.blank .big-icon svg { width: 100%; height: 100%; }

/* Popovers, sheets */
/* This can hold a LOT — group photo, name field, the full member list, background
   swatches — and on a shorter viewport (iPad in split view, a phone in landscape) that
   total easily runs past the bottom of the screen. Without its own max-height/scroll
   the overflow just got cut off with no way to reach it (leave-conversation, the rest
   of the theme grid, etc. were literally unreachable). Bound to --app-vh, same pattern
   used by .sheet, so it accounts for the real visible viewport, not just 100vh. */
.popover { position: absolute; top: 70px; right: 12px; width: 290px; z-index: 50; border-radius: 20px; padding: 18px 16px 10px; animation: pop var(--dur-ui) var(--ease-out); transform-origin: top right; max-height: calc(var(--app-vh, 100vh) - 84px); overflow-y: auto; -webkit-overflow-scrolling: touch; overscroll-behavior: contain; }
.popover.left { right: auto; left: 12px; transform-origin: top left; top: 12px; max-height: calc(var(--app-vh, 100vh) - 24px); }
.popover .center { text-align: center; }
.popover .center .avatar { margin: 0 auto 10px; }
.popover .center .n { font-weight: 600; font-size: 16px; }
.popover .center .p { color: var(--text-3); font-size: 13px; margin-top: 2px; }
.popover .btns { display: flex; justify-content: center; gap: 12px; margin: 14px 0 10px; }
.popover .btns button { width: 60px; text-align: center; font-size: 11px; color: var(--blue); }
.popover .btns button span { display: grid; place-items: center; width: 42px; height: 42px; margin: 0 auto 4px; border-radius: 50%; background: rgba(11,132,254,.14); }
.popover .btns button svg { width: 20px; height: 20px; }
.popover .list-btn { width: 100%; text-align: left; padding: 11px 6px; font-size: 14px; border-top: 1px solid var(--hairline); display: flex; align-items: center; justify-content: space-between; }
.popover .list-btn.red { color: var(--red); }
.popover .members { max-height: 200px; overflow: auto; margin-top: 6px; }
.popover .members .row { grid-template-columns: 32px 1fr; padding: 5px 4px; }
.popover .members .r-name { font-size: 13px; }
.popover .field-line { display: flex; gap: 8px; margin-top: 6px; }
.popover .field-line input { flex: 1; height: 34px; border: 0; border-radius: 10px; padding: 0 10px; background: var(--field); box-shadow: inset 0 0 0 1px var(--field-border); font-size: 13px; min-width: 0; }
.popover .field-line button { color: var(--blue); font-weight: 600; font-size: 13px; padding: 0 6px; }
.toggle { width: 42px; height: 26px; border-radius: 13px; background: rgba(120,120,128,.32); position: relative; transition: background .2s; flex: none; }
.toggle::after { content: ""; position: absolute; top: 2px; left: 2px; width: 22px; height: 22px; border-radius: 50%; background: #fff; box-shadow: 0 2px 6px rgba(0,0,0,.25); transition: transform .22s cubic-bezier(.2,1,.3,1); }
.toggle.on { background: var(--green); }
.toggle.on::after { transform: translateX(16px); }
.scrim { position: fixed; inset: 0; z-index: 45; }
/* Sized by --app-vh (the VISIBLE viewport), not inset:0 — on iOS the layout viewport
   keeps its full height under the keyboard, so a sheet centred in it sat half under
   the keys. Now the pop-up always centres in the part of the screen you can see. */
.sheet-wrap { position: fixed; top: 0; left: 0; right: 0; height: var(--app-vh, 100%); z-index: 60; display: grid; place-items: center; background: rgba(0,0,0,.28); -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px); animation: fade .15s; }
.sheet { max-height: calc(var(--app-vh, 100vh) - 24px); overflow-y: auto; }
@keyframes fade { from { opacity: 0; } to { opacity: 1; } }
.sheet { width: 400px; max-width: calc(100vw - 32px); min-width: 0; border-radius: 22px; padding: 18px; animation: pop var(--dur-sheet) var(--ease-out); }
.sheet h3 { margin: 0 0 12px; font-size: 16px; font-weight: 600; text-align: center; letter-spacing: -.01em; }
.sheet .to { border-radius: 12px; background: var(--field); box-shadow: inset 0 0 0 1px var(--field-border); padding: 6px 10px; display: flex; gap: 6px; flex-wrap: wrap; align-items: center; min-height: 40px; }
.sheet .to label { color: var(--text-3); font-size: 14px; }
.sheet .to input { flex: 1; min-width: 120px; border: 0; background: transparent; font-size: 14px; height: 26px; }
.sheet .picks { max-height: 200px; overflow: auto; margin: 8px -6px 0; }
.sheet .acts { display: flex; gap: 10px; margin-top: 14px; }
.sheet .acts button { flex: 1; height: 40px; border-radius: 12px; font-weight: 600; font-size: 14px; display: flex; align-items: center; justify-content: center; gap: 7px; color: #fff; background: var(--green); box-shadow: inset 0 1px 0 rgba(255,255,255,.3); transition: transform .12s; }
.sheet .acts button:active { transform: scale(.97); }
.sheet .acts button svg { width: 18px; height: 18px; }
.sheet .acts button:disabled { opacity: .45; cursor: default; }
.sheet .cancel { display: block; margin: 10px auto 0; color: var(--blue); font-size: 14px; }
.sheet .warn { color: var(--red); font-size: 12px; min-height: 16px; margin-top: 8px; }
.action-sheet { position: fixed; left: 50%; bottom: 20px; transform: translateX(-50%); z-index: 95; width: 360px; max-width: calc(100vw - 24px); border-radius: 20px; padding: 6px; animation: rise .25s cubic-bezier(.2,1,.3,1); }
.action-sheet button { width: 100%; height: 50px; border-radius: 14px; font-size: 16px; color: var(--blue); }
.action-sheet button:hover { background: var(--row-hover); }
.action-sheet button.red { color: var(--red); }
.action-sheet button.b { font-weight: 600; }

/* ------------------------------------------------------------------------
   Calls app (the FaceTime side)
   ------------------------------------------------------------------------ */
.ft-new { margin: 4px 12px 10px; height: 36px; border-radius: 11px; background: var(--green); color: #fff; font-weight: 600; font-size: 14px; display: flex; align-items: center; justify-content: center; gap: 8px; box-shadow: inset 0 1px 0 rgba(255,255,255,.3), 0 4px 12px rgba(52,199,89,.25); transition: filter .12s, transform .12s; }
.ft-new:hover { filter: brightness(1.05); }
.ft-new:active { transform: scale(.985); filter: brightness(.95); }
.ft-new svg { width: 18px; height: 18px; }
.sec { font-size: 12px; font-weight: 600; color: var(--text-3); padding: 8px 10px 4px; }
.row.call-row { grid-template-columns: 46px 1fr auto; padding-left: 10px; }
.row.call-row .r-name.missed { color: var(--red); }
.row.call-row .r-sub { font-size: 12px; color: var(--text-3); display: flex; gap: 5px; align-items: center; margin-top: 1px; }
.row.call-row .r-sub svg { width: 13px; height: 13px; }
.row.call-row .r-right { display: flex; align-items: center; gap: 4px; color: var(--text-3); font-size: 12px; }
.row.call-row .r-right button { color: var(--blue); width: 30px; height: 30px; display: grid; place-items: center; border-radius: 8px; }
.row.call-row .r-right button svg { width: 18px; height: 18px; }
.row.call-row .r-right button:hover { background: var(--row-hover); }

.ft-stage { flex: 1; position: relative; overflow: hidden; background: #0c0c10; display: grid; place-items: center; margin: 12px; border-radius: 22px; box-shadow: 0 0 0 .5px rgba(255,255,255,.1) inset; }
.ft-stage video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; transform: scaleX(-1); background: #0c0c10; }
.ft-stage .ft-idle { position: relative; z-index: 1; text-align: center; color: #fff; padding: 26px 34px; border-radius: 22px; }
.ft-stage .ft-idle .app-icon { margin: 0 auto 18px; }
.ft-stage .ft-idle h3 { font-size: 24px; font-weight: 700; margin: 0 0 6px; letter-spacing: -.025em; }
.ft-stage .ft-idle p { margin: 0; color: rgba(255,255,255,.65); font-size: 14px; }
.ft-stage .ft-idle button { margin-top: 22px; height: 42px; padding: 0 22px; border-radius: 12px; background: var(--green); color: #fff; font-weight: 600; font-size: 15px; display: inline-flex; align-items: center; gap: 8px; box-shadow: inset 0 1px 0 rgba(255,255,255,.3); }
.ft-stage .ft-idle button svg { width: 20px; height: 20px; }
.ft-stage .me-tag { position: absolute; left: 14px; bottom: 14px; z-index: 1; color: #fff; font-size: 12px; font-weight: 600; padding: 6px 11px; border-radius: 10px; background: rgba(0,0,0,.4); -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px); }

/* Incoming banner */
.incoming-wrap { position: fixed; top: 16px; left: 50%; transform: translateX(-50%); z-index: 90; width: 400px; max-width: calc(100vw - 24px); display: flex; flex-direction: column; align-items: center; gap: 8px; }
.incoming { position: static; width: 100%; color: #fff; border-radius: 24px; padding: 14px 16px; display: flex; align-items: center; gap: 12px; background: rgba(36,36,40,.72); -webkit-backdrop-filter: blur(30px) saturate(180%); backdrop-filter: blur(30px) saturate(180%); box-shadow: 0 20px 60px rgba(0,0,0,.4), inset 0 1px 0 rgba(255,255,255,.2); animation: drop .4s cubic-bezier(.2,1.1,.3,1); }
.incoming .decline-reason { width: 30px; height: 30px; background: rgba(255,255,255,.14); color: rgba(255,255,255,.8); flex: none; }
.incoming .decline-reason svg { width: 14px; height: 14px; }
.incoming .decline-reason[aria-expanded="true"] svg { transform: rotate(180deg); }
.decline-reasons { width: 100%; border-radius: 18px; padding: 8px; display: flex; flex-direction: column; gap: 2px; background: rgba(36,36,40,.86); -webkit-backdrop-filter: blur(30px) saturate(180%); backdrop-filter: blur(30px) saturate(180%); box-shadow: 0 16px 40px rgba(0,0,0,.35), inset 0 1px 0 rgba(255,255,255,.15); animation: drop .25s var(--ease-out); }
.decline-reasons button { color: #fff; text-align: left; padding: 10px 12px; border-radius: 12px; font-size: 14px; transition: background-color var(--dur-fast) var(--ease-out); }
.decline-reasons button:active { background: rgba(255,255,255,.12); }
@keyframes drop { from { transform: translate(-50%, -130%); } to { transform: translate(-50%, 0); } }
.incoming .info { flex: 1; min-width: 0; }
.incoming .info .n { font-weight: 600; font-size: 16px; }
.incoming .info .k { font-size: 13px; color: rgba(255,255,255,.65); }
.incoming .cbtn { width: 46px; height: 46px; border-radius: 50%; display: grid; place-items: center; color: #fff; transition: transform .12s; box-shadow: inset 0 1px 0 rgba(255,255,255,.3); }
.incoming .cbtn:active { transform: scale(.92); }
.incoming .cbtn svg { width: 22px; height: 22px; }
.incoming .decline { background: var(--red); }
.incoming .accept { background: var(--green); animation: ringpulse 1.4s infinite; }
@keyframes ringpulse { 0% { box-shadow: 0 0 0 0 rgba(52,199,89,.55); } 70% { box-shadow: 0 0 0 12px rgba(52,199,89,0); } 100% { box-shadow: 0 0 0 0 rgba(52,199,89,0); } }

/* Play a game (Web Dashers, embedded), same visual tier as an active call */
.game-overlay { position: fixed; inset: 0; z-index: 80; background: #0c0c0f; overflow: hidden; animation: fade .2s; }
.game-overlay iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.game-overlay .game-close { position: absolute; top: calc(12px + env(safe-area-inset-top)); right: 18px; z-index: 3; width: 40px; height: 40px; border-radius: 50%; background: rgba(255,255,255,.14); color: #fff; display: grid; place-items: center; }
.game-overlay .game-close svg { width: 16px; height: 16px; }
.game-overlay .game-close:active { transform: scale(.94); }
.game-overlay .game-invite {
  position: absolute; top: calc(12px + env(safe-area-inset-top)); left: 18px; z-index: 3;
  height: 36px; padding: 0 14px 0 10px; border-radius: 18px; background: rgba(255,255,255,.14); color: #fff;
  display: flex; align-items: center; gap: 6px; font-size: 13px; font-weight: 600;
}
.game-overlay .game-invite svg { width: 16px; height: 16px; }
.game-overlay .game-invite:active { transform: scale(.94); }
.game-overlay .game-invite[hidden] { display: none; }

/* Active call */
.call { position: fixed; inset: 0; z-index: 80; background: #0c0c0f; color: #fff; overflow: hidden; animation: fade .2s; }
/* Split-screen: the call shrinks to a small floating video chip — video plus mute/
   camera/end only, nothing else — so the real conversation (the normal #pane, not a
   duplicate) shows underneath almost entirely unobstructed. See enableCallSplit().
   Draggable to any corner, same throw-to-corner-and-snap interaction as the in-call
   PiP video (see the #pip pointer handlers) — "morph" it wherever it's out of the way. */
.call.split {
  inset: auto; width: 148px; height: 208px; border-radius: 22px; touch-action: none;
  box-shadow: 0 16px 40px rgba(0,0,0,.45), 0 0 0 1px rgba(255,255,255,.12);
  transition: transform var(--dur-sheet, .28s) var(--ease-drawer, ease);
}
.call.split.dragging { transition: none; }
.call.split[data-corner="tr"] { top: calc(70px + env(safe-area-inset-top)); right: 12px; left: auto; bottom: auto; }
.call.split[data-corner="tl"] { top: calc(70px + env(safe-area-inset-top)); left: 12px; right: auto; bottom: auto; }
.call.split[data-corner="br"] { bottom: calc(96px + env(safe-area-inset-bottom)); right: 12px; left: auto; top: auto; }
.call.split[data-corner="bl"] { bottom: calc(96px + env(safe-area-inset-bottom)); left: 12px; right: auto; top: auto; }
/* Almost everything else about a normal call screen would be nonsense at 148x208 —
   name/status bar, minimise/add-person/captions buttons, the captions bar. Hide all of
   it; the tiny control row below and the self-camera bubble are the only chrome that
   still fits and still matters once this is this small. */
.call.split .top, .call.split .closex, .call.split .minx, .call.split .addx, .call.split .ccx, .call.split .cc-bar, .call.split .hint { display: none; }
/* The self-camera preview — "I want to still see my camera" — was being hidden
   entirely along with everything else. Keep it, just shrunk and re-anchored to fit
   inside a chip this small instead of at its normal full-call-screen size/position. */
.call.split .pip { width: 40px; height: 54px; right: 6px; bottom: 6px; border-radius: 9px; box-shadow: 0 4px 10px rgba(0,0,0,.4); }
/* Explicit way back to the full call screen — tapping the chip itself (anywhere that
   isn't a control or this button) does the same thing, but this is the discoverable,
   unambiguous one. */
.call.split .split-expand {
  position: absolute; top: 6px; left: 6px; z-index: 4; width: 26px; height: 26px; border-radius: 50%;
  background: rgba(255,255,255,.18); color: #fff; display: grid; place-items: center;
}
.call.split .split-expand svg { width: 12px; height: 12px; }
.call.split .controls { bottom: 8px; transform: scale(.58); transform-origin: bottom center; gap: 4px; padding: 6px 6px 4px; }
.call.split .controls .lb { display: none; }
.call.split .center-id { gap: 6px; }
.call.split .center-id .avatar.xl { width: 52px; height: 52px; font-size: 18px; }
.call.split .center-id .n { font-size: 12.5px; margin-top: 0; }
.call.split .center-id .s { display: none; }
/* The panel is small but still sits on top of the thread — give the messages behind
   it a little breathing room near whichever corner it's currently parked in, instead
   of letting the newest ones render right underneath it. */
body.call-split .thread { padding-top: calc(70px + env(safe-area-inset-top) + 208px + 10px); }
@media (prefers-reduced-motion: reduce) { .call.split { transition: none; } }
.call video.remote { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; background: #0c0c0f; }
.call video.remote.contain { object-fit: contain; }
.call .audio-bg { position: absolute; inset: 0; background: radial-gradient(120% 90% at 50% 0%, #4b4b57 0%, #1b1b21 55%, #0c0c0f 100%); }
.call .center-id { position: absolute; inset: 0; display: grid; place-content: center; justify-items: center; gap: 14px; text-align: center; }
.call .center-id .n { font-size: 30px; font-weight: 600; letter-spacing: -.02em; }
.call .center-id .s { font-size: 16px; color: rgba(255,255,255,.65); margin-top: -8px; }
.call .top { position: absolute; top: 18px; left: 18px; z-index: 2; border-radius: 14px; padding: 8px 12px; background: rgba(0,0,0,.35); -webkit-backdrop-filter: blur(16px); backdrop-filter: blur(16px); box-shadow: inset 0 1px 0 rgba(255,255,255,.15); }
.call .top .n { font-weight: 600; font-size: 14px; }
.call .top .s { font-size: 12px; color: rgba(255,255,255,.7); font-variant-numeric: tabular-nums; }
.call .pip { position: absolute; right: 18px; bottom: 112px; width: 160px; aspect-ratio: 3 / 4; z-index: 2; border-radius: 16px; overflow: hidden; background: #222; box-shadow: 0 10px 30px rgba(0,0,0,.5), 0 0 0 .5px rgba(255,255,255,.25); cursor: pointer; transition: transform .2s cubic-bezier(.2,1,.3,1); touch-action: none; }
.call .pip:active { transform: scale(.97); }
.call .pip video { width: 100%; height: 100%; object-fit: cover; transform: scaleX(-1); }
.call .pip video.noflip { transform: none; }
.call .pip.off { display: grid; place-items: center; color: rgba(255,255,255,.6); }
.call .controls { position: absolute; left: 50%; bottom: 26px; transform: translateX(-50%); z-index: 3; display: flex; gap: 12px; padding: 12px 14px; border-radius: 26px; background: rgba(30,30,34,.5); -webkit-backdrop-filter: blur(30px) saturate(180%); backdrop-filter: blur(30px) saturate(180%); box-shadow: 0 10px 40px rgba(0,0,0,.4), inset 0 1px 0 rgba(255,255,255,.18); }
.call .controls button { width: 54px; height: 54px; border-radius: 50%; display: grid; place-items: center; background: rgba(255,255,255,.16); color: #fff; transition: transform .12s, background .12s; position: relative; }
.call .controls button:active { transform: scale(.94); }
.call .controls button svg { width: 24px; height: 24px; }
.call .controls button.off { background: #fff; color: #111; }
.call .controls button.end { background: var(--red); }
.call .controls button.end:hover { background: #ff5148; }
.call .controls button.live { background: var(--green); }
.call .hint { position: absolute; left: 50%; bottom: 108px; transform: translateX(-50%); font-size: 13px; color: rgba(255,255,255,.8); background: rgba(0,0,0,.4); padding: 7px 12px; border-radius: 12px; -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px); }
.toast { position: fixed; left: 50%; bottom: 28px; transform: translate(-50%, 0); z-index: 200; color: var(--text); padding: 10px 16px; border-radius: 14px; font-size: 13px; max-width: calc(100vw - 40px); transition: transform var(--dur-sheet) var(--ease-out), opacity var(--dur-sheet) var(--ease-out); opacity: 1; }
.toast.enter { transform: translate(-50%, 12px); opacity: 0; }
.toast.leave { transform: translate(-50%, 12px); opacity: 0; }

/* ------------------------------------------------------------------------
   Mobile
   ------------------------------------------------------------------------ */
@media (max-width: 760px) {
  .shell { grid-template-columns: 1fr; grid-template-rows: 1fr; }
  /* The list and the thread already swap on phones, so collapsing means nothing. */
  .sb-collapse, .sb-expand { display: none !important; }
  body.sidebar-collapsed .shell { grid-template-columns: 1fr; }
  body.sidebar-collapsed .sidebar { overflow: hidden; }
  .sb-invite { margin-bottom: calc(72px + env(safe-area-inset-bottom)); }
  /* iOS-style tab bar: full width across the bottom, icon over label, frosted. */
  .rail {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
    flex-direction: row; justify-content: space-around; align-items: flex-start;
    padding: 8px 6px calc(6px + env(safe-area-inset-bottom)); gap: 0; border-right: 0;
    border-top: .5px solid var(--glass-line);
    background: var(--glass); -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur);
  }
  .rail .spacer { display: none; }
  .dock-icon, .rail .me {
    width: auto; min-width: 58px; height: auto; border-radius: 12px; background: none !important;
    box-shadow: none !important; filter: none; display: flex; flex-direction: column; align-items: center;
    gap: 3px; padding: 4px 6px 2px; color: var(--text-3); position: relative;
  }
  .dock-icon svg { width: 26px; height: 26px; }
  /* The tab glyphs are solid white shapes meant to sit on a colored tile; on a bare
     bar they'd vanish, so tint them with the tab's own state colour instead. */
  .dock-icon svg [fill="#fff"], .dock-icon svg path[fill="#fff"], .dock-icon svg circle[fill="#fff"], .dock-icon svg rect[fill="#fff"] { fill: currentColor; }
  .dock-icon.active { color: var(--blue); }
  body:has(.sheet.settings) .rail .me { color: var(--blue); }
  .dock-icon::after { display: none; }
  .dock-icon::before, .rail .me::before {
    content: attr(data-label); order: 2; font-size: 10.5px; font-weight: 600; letter-spacing: .01em;
    color: inherit; line-height: 1;
  }
  .dock-icon > svg, .rail .me { order: 1; }
  .rail .me {
    font-size: 0; justify-content: flex-start;
  }
  .rail .me::after {
    content: ""; width: 26px; height: 26px; order: 1; border-radius: 50%;
    background: currentColor; -webkit-mask: var(--gear-mask) center/contain no-repeat; mask: var(--gear-mask) center/contain no-repeat;
  }
  /* The Settings tab is a gear, not your face. This needs .avatar in the selector:
     ".avatar.pic img { display: block }" lives further down the file and would win the
     tie on source order, leaving the profile photo rendered at full size and shoving
     the gear and its label off the bar. */
  /* img AND svg: the Snapchat look renders the fallback avatar as an inline <svg>,
     which has no intrinsic size and expands to fill the tab if it stays visible. */
  .rail .avatar.me img, .rail .avatar.me svg { display: none; }
  .dock-icon .badge { top: 0; right: 8px; }
  .sidebar { border-right: 0; -webkit-backdrop-filter: none; backdrop-filter: none; background: var(--window); }
  .list { padding-bottom: 96px; }
  .pane { display: none; }
  body.show-pane .sidebar { display: none; }
  body.show-pane .pane { display: flex; }
  body.show-pane .rail { display: none; }
  .back { display: grid; color: var(--blue); }
  .back svg { width: 22px; height: 22px; }
  .msg .col { max-width: 78%; }
  .bubble { font-size: calc(var(--msg-size, 15px) + 1px); }
  .pane-head { left: 8px; right: 8px; top: calc(8px + env(safe-area-inset-top)); }
  .composer-wrap { left: 8px; right: 8px; bottom: calc(8px + env(safe-area-inset-bottom)); }
  /* Keyboard up = the home indicator is behind it, so reserving safe-area space
     just floats the composer above the keyboard with a dead gap. */
  body.kb-open .composer-wrap { bottom: 6px; }
  body.kb-open .thread { padding-bottom: calc(var(--composer-h, 76px) + 18px); }
  /* With the keyboard up there's nowhere sensible for these to sit — they used to
     get shoved up the screen. Hide them while typing, bring them back after. */
  body.kb-open .sb-invite, body.kb-open .rail { display: none; }
  body.kb-open .sidebar .list { padding-bottom: 12px; }
  .thread { padding-left: 12px; padding-right: 12px; }
  .call .pip { width: 112px; bottom: 124px; right: 12px; }
  .popover { right: 8px; left: 8px; width: auto; }
  .row .r-name { font-size: 16px; }
  .row .r-preview { font-size: 14px; }
  /* ---- iOS-style large title + flat list ---- */
  .sb-top { padding-top: calc(10px + env(safe-area-inset-top)); align-items: flex-end; }
  .sb-top h2 { font-size: 32px; font-weight: 800; letter-spacing: -.02em; }
  .sidebar .list { padding-left: 0; padding-right: 0; padding-bottom: calc(104px + env(safe-area-inset-bottom)); }
  .sidebar .list .row { border-radius: 0; margin: 0; padding: 11px 14px 11px 8px; }
  /* Hairline between rows, indented past the avatar like Messages does. */
  .sidebar .list .row + .row::before {
    content: ""; position: absolute; left: 70px; right: 0; top: 0; height: .5px; background: var(--hairline);
  }
  .sidebar .list .row.active::before, .sidebar .list .row.active + .row::before { display: none; }
  .row .r-time .chev { display: inline-flex; margin-left: 4px; color: var(--gray2); }
  .row .r-time .chev svg { width: 15px; height: 15px; }
  .row .r-head { align-items: center; }
  .sb-invite { margin-bottom: calc(72px + env(safe-area-inset-bottom)); }
  .row { padding: 10px 10px 10px 4px; }
  .avatar { width: 50px; height: 50px; }
  .sb-top h2 { font-size: 30px; }
  .ft-stage { margin: 8px 8px 96px; }
}
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; }
}

/* Labeled FaceTime button in the chat header */
.ft-btn { height: 32px; padding: 0 12px 0 9px; border-radius: 10px; background: var(--green); color: #fff; font-weight: 600; font-size: 13px; display: flex; align-items: center; gap: 6px; box-shadow: inset 0 1px 0 rgba(255,255,255,.3), 0 3px 10px rgba(52,199,89,.28); transition: transform .1s, filter .12s; }
.ft-btn:hover { filter: brightness(1.05); }
.ft-btn:active { transform: scale(.95); }
.ft-btn svg { width: 18px; height: 18px; }
@media (max-width: 420px) { .ft-btn span { display: none; } .ft-btn { padding: 0 8px; } }
/* A call already going in this group, state-indication tier — gentle enough to sit in
   a header without being obnoxious. */
.ft-btn.join-call { animation: joinCallPulse 1.8s ease-in-out infinite; }
@keyframes joinCallPulse { 0%, 100% { box-shadow: inset 0 1px 0 rgba(255,255,255,.3), 0 3px 10px rgba(52,199,89,.28); } 50% { box-shadow: inset 0 1px 0 rgba(255,255,255,.3), 0 3px 16px 3px rgba(52,199,89,.55); } }
@media (prefers-reduced-motion: reduce) { .ft-btn.join-call { animation: none; } }

/* ---- message actions: reply / forward / delete ---- */
.tapbar { flex-direction: column; gap: 4px; padding: 6px; min-width: 190px; }
.tb-emoji-row { display: flex; align-items: center; gap: 4px; }
.tb-emoji { display: flex; gap: 2px; }
.tb-more { flex: none; width: 30px; height: 30px; border-radius: 50%; display: grid; place-items: center; color: var(--text-2); background: var(--search); }
.tb-more svg { width: 14px; height: 14px; }
.tb-more[aria-expanded="true"] { background: var(--field-border); color: var(--text); }
.tb-expand { padding-top: 4px; }
.tb-tabs { display: flex; gap: 4px; overflow-x: auto; padding-bottom: 4px; }
.tb-tabs button { padding: 4px 10px; border-radius: 10px; background: var(--search); color: var(--text-2); font-size: 11.5px; font-weight: 600; white-space: nowrap; }
.tb-tabs button.on { background: var(--blue); color: #fff; }
.tb-grid { display: grid; grid-template-columns: repeat(6, 1fr); gap: 2px; max-height: 180px; overflow-y: auto; }
.tb-grid button { font-size: 20px; padding: 5px; border-radius: 8px; }
@media (hover: hover) and (pointer: fine) { .tb-grid button:hover, .tb-emoji button:hover { background: var(--row-hover); } }
.tb-acts { display: flex; flex-direction: column; gap: 1px; border-top: .5px solid var(--glass-line); padding-top: 4px; margin-top: 2px; }
.tb-acts button {
  display: flex; align-items: center; gap: 9px; width: 100%; padding: 8px 10px; border-radius: 9px;
  font-size: 13.5px; font-weight: 500; color: var(--text); text-align: left; white-space: nowrap;
}
.tb-acts button svg { width: 16px; height: 16px; flex: none; color: var(--text-2); }
.tb-acts button.danger { color: var(--red); }
.tb-acts button.danger svg { color: var(--red); }
@media (hover: hover) and (pointer: fine) { .tb-acts button:hover { background: var(--row-hover); } }

/* quoted message inside a bubble */
.quote {
  display: block; border-left: 3px solid currentColor; padding: 3px 0 3px 8px; margin: 0 0 5px;
  opacity: .78; font-size: 13px; line-height: 1.3; border-radius: 2px;
}
.quote .q-who { display: block; font-weight: 700; font-size: 12.5px; }
.quote .q-text { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }

/* deleted tombstone */
.bubble.gone {
  display: flex; align-items: center; gap: 7px; font-style: italic; color: var(--text-3) !important;
  background: transparent !important; box-shadow: inset 0 0 0 1px var(--hairline);
}
.bubble.gone svg { flex: none; opacity: .7; }
.bubble.gone::before, .bubble.gone::after { display: none !important; }

/* reply bar above the composer */
.reply-bar { display: flex; align-items: center; gap: 10px; padding: 8px 10px; border-radius: 14px; }
.reply-bar .rb-line { width: 3px; align-self: stretch; border-radius: 2px; background: var(--blue); flex: none; }
.reply-bar .rb-body { flex: 1; min-width: 0; }
.reply-bar .rb-who { font-size: 12.5px; font-weight: 700; color: var(--blue); }
.reply-bar .rb-text { font-size: 13px; color: var(--text-2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.reply-bar .x { width: 24px; height: 24px; border-radius: 50%; display: grid; place-items: center; background: var(--search); color: var(--text-3); flex: none; }

/* ---- touch / iPad ergonomics ---- */
@media (pointer: coarse) {
  /* Stop iOS's copy/look-up callout and text selection from fighting the long-press menu. */
  .thread .bubble, .thread .msg { -webkit-touch-callout: none; -webkit-user-select: none; user-select: none; }
  .icon-btn { width: 40px; height: 40px; }
  .tapbar .tb-emoji button { width: 42px; height: 42px; font-size: 22px; }
  .tb-grid button { font-size: 24px; padding: 8px; }
  .tb-more { width: 34px; height: 34px; }
  .tb-acts button { padding: 11px 12px; font-size: 15px; }
  .plusmenu button { padding: 12px; }
  .row { min-height: 60px; }
}
/* iPad and small laptops: the fixed 330px list is too greedy; leave the chat more room. */
@media (min-width: 761px) and (max-width: 1100px) {
  .shell { grid-template-columns: 72px 290px 1fr; }
  .msg .col { max-width: 74%; }
}
/* Small pop-ups (polls, schedule, profile…) centre on phones instead of docking to
   the bottom, so an open keyboard never covers them. Settings stays full-screen. */
@media (max-width: 760px) {
  .sheet-wrap:has(.poll-sheet), .sheet-wrap:has(.new-chat), .sheet-wrap:has(.profile-sheet),
  .sheet-wrap:has(.invite-sheet), .sheet-wrap:has(.suggest-sheet), .sheet-wrap:has(.moji-scan) { align-items: center; padding: 12px; }
  /* Extra specificity on purpose: each sheet's own width rule is declared later in
     this file at the same weight, so a plain .sheet.x here would lose. */
  .sheet-wrap .sheet.poll-sheet, .sheet-wrap .sheet.profile-sheet, .sheet-wrap .sheet.invite-sheet,
  .sheet-wrap .sheet.suggest-sheet, .sheet-wrap .sheet.moji-scan, .sheet-wrap .sheet.new-chat { width: 100%; max-width: 420px; }
}

.score-delta { font-size: 12px; font-weight: 700; color: var(--green); margin-left: 4px; }

/* ---- group presence caption ---- */
.presence-cap { font-size: 12px; color: var(--text-3); margin: 4px 0 0 8px; }

/* ---- edited marker ---- */
.bubble .edited { font-size: 10.5px; opacity: .65; margin-left: 6px; font-style: italic; }
.reply-bar.editing .rb-line, .reply-bar.editing .rb-who { background: #ff9500; color: #ff9500; }
.reply-bar.editing .rb-line { background: #ff9500; }

/* ---- polls & events ---- */
.bubble.poll { min-width: 240px; max-width: 320px; padding: 12px 12px 10px; display: flex; flex-direction: column; gap: 6px; }
.msg.out .bubble.poll { background: var(--out-bubble); }
.poll-head { font-weight: 700; font-size: 15px; line-height: 1.25; margin-bottom: 2px; }
.poll-sub { font-size: 12.5px; opacity: .8; margin-top: -4px; }
.poll-opt { position: relative; overflow: hidden; display: flex; align-items: center; gap: 8px; width: 100%; padding: 9px 10px; border-radius: 10px; background: rgba(0,0,0,.08); font-size: 14px; text-align: left; color: inherit; }
.msg.out .poll-opt { background: rgba(255,255,255,.18); }
.poll-opt i { position: absolute; left: 0; top: 0; bottom: 0; background: rgba(255,255,255,.28); transition: width var(--dur-sheet) var(--ease-out); pointer-events: none; }
.msg.in .poll-opt i { background: rgba(11,132,254,.18); }
.poll-opt.mine { box-shadow: inset 0 0 0 2px currentColor; }
.poll-opt .po-label { position: relative; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.poll-opt .po-n { position: relative; font-weight: 700; font-variant-numeric: tabular-nums; }
.poll-foot { font-size: 11.5px; opacity: .7; margin-top: 2px; }
.sheet.poll-sheet { width: 380px; position: relative; padding-top: 40px; display: flex; flex-direction: column; gap: 10px; }
.sheet.poll-sheet .it-x { position: absolute; top: 12px; right: 12px; width: 30px; height: 30px; border-radius: 50%; display: grid; place-items: center; background: var(--search); color: var(--text-3); }
.sheet.poll-sheet h3 { margin: 0; }
.sheet.poll-sheet .field-line { margin: 0; }
.sheet.poll-sheet .field-line input, .sheet.poll-sheet .field-line textarea { width: 100%; height: 42px; border: 0; border-radius: 11px; padding: 0 12px; background: var(--search); font-size: 15px; min-width: 0; font: inherit; }
.sheet.poll-sheet .field-line textarea { height: auto; padding: 10px 12px; resize: none; }
.sheet.poll-sheet #pollOpts { display: flex; flex-direction: column; gap: 8px; }
.sheet.poll-sheet .mini.ghost { background: var(--field); color: var(--blue); box-shadow: inset 0 0 0 1px var(--field-border); align-self: flex-start; }

/* ---- view-once ---- */
.bubble.once { display: inline-flex; align-items: center; gap: 8px; font-weight: 600; cursor: pointer; }
.bubble.once.opened { opacity: .6; cursor: default; font-weight: 500; }
.attach-preview .ap.once img { filter: blur(3px); }
.once-badge { position: absolute; left: 6px; bottom: 6px; display: inline-flex; align-items: center; gap: 4px; padding: 3px 7px; border-radius: 8px; background: rgba(0,0,0,.55); color: #fff; font-size: 11px; font-weight: 700; }
.once-viewer { position: fixed; inset: 0; z-index: 300; background: #000; display: grid; place-items: center; animation: fade .2s; }
.once-viewer img { max-width: 100vw; max-height: 100vh; object-fit: contain; }
.once-viewer .once-hint { position: absolute; bottom: calc(24px + env(safe-area-inset-bottom)); left: 50%; transform: translateX(-50%); display: flex; align-items: center; gap: 6px; color: #fff; font-size: 13px; padding: 8px 14px; border-radius: 12px; background: rgba(255,255,255,.14); white-space: nowrap; }

/* ---- chat backgrounds (shared per conversation) ---- */
.thread[data-theme="sky"] { background: linear-gradient(180deg, #dbeefe 0%, #f6fbff 100%); }
.thread[data-theme="water"] { background: linear-gradient(160deg, #cfeff5 0%, #e4f6fa 60%, #f3fbfd 100%); }
.thread[data-theme="aurora"] { background: linear-gradient(140deg, #d8f3e6 0%, #dfe6ff 50%, #f4dcf4 100%); }
.thread[data-theme="sunset"] { background: linear-gradient(180deg, #ffe0c2 0%, #ffd1e0 100%); }
.thread[data-theme="forest"] { background: linear-gradient(180deg, #dbeedd 0%, #eef7ec 100%); }
.thread[data-theme="midnight"] { background: linear-gradient(180deg, #1c2236 0%, #10131d 100%); }
.thread[data-theme="rose"] { background: linear-gradient(180deg, #ffdfe8 0%, #fff0f4 100%); }
.thread[data-theme="mint"] { background: linear-gradient(180deg, #d4f5ea 0%, #ecfaf5 100%); }
.thread[data-theme="lavender"] { background: linear-gradient(180deg, #e4dcff 0%, #f3efff 100%); }
.thread[data-theme="midnight"] .bubble:not(.gone) { box-shadow: 0 0 0 .5px rgba(255,255,255,.12); }
.thread[data-theme="midnight"] .msg.in .bubble { background: #2a3145; color: #f2f4fa; }
.thread[data-theme="midnight"] .stamp, .thread[data-theme="midnight"] .sender, .thread[data-theme="midnight"] .status { color: rgba(255,255,255,.6); }
.theme-row { display: flex; flex-wrap: wrap; gap: 8px; padding: 2px 0; }
.theme-sw { width: 34px; height: 34px; border-radius: 50%; padding: 3px; box-shadow: inset 0 0 0 1px var(--hairline); }
.theme-sw.on { box-shadow: 0 0 0 2px var(--blue); }
.theme-sw i { display: block; width: 100%; height: 100%; border-radius: 50%; background: var(--chat-bg); box-shadow: inset 0 0 0 1px var(--hairline); }
.theme-sw i[data-theme="sky"] { background: linear-gradient(180deg, #bfe0ff, #f6fbff); }
.theme-sw i[data-theme="water"] { background: linear-gradient(160deg, #a9e3ee, #f3fbfd); }
.theme-sw i[data-theme="aurora"] { background: linear-gradient(140deg, #b6ebd0, #c9d3ff, #f0c8f0); }
.theme-sw i[data-theme="sunset"] { background: linear-gradient(180deg, #ffc38f, #ffb3cc); }
.theme-sw i[data-theme="forest"] { background: linear-gradient(180deg, #b8dfbd, #eef7ec); }
.theme-sw i[data-theme="midnight"] { background: linear-gradient(180deg, #2a3350, #0f1119); }
.theme-sw i[data-theme="rose"] { background: linear-gradient(180deg, #ffc2d3, #fff0f4); }
.theme-sw i[data-theme="mint"] { background: linear-gradient(180deg, #a8ecd6, #ecfaf5); }
.theme-sw i[data-theme="lavender"] { background: linear-gradient(180deg, #cdbeff, #f3efff); }
.theme-sw i[data-bubble=""] { background: linear-gradient(180deg, #0b84fe, #0a84ff); }
.theme-sw i[data-bubble="red"] { background: linear-gradient(180deg, #ff5f57, #ff3b30); }
.theme-sw i[data-bubble="blue"] { background: linear-gradient(180deg, #64b5ff, #0a84ff); }

/* ---- nickname on profile ---- */
.pf-nick { display: flex; gap: 8px; width: 100%; margin-top: 12px; }
.pf-nick input { flex: 1; min-width: 0; height: 36px; border: 0; border-radius: 10px; padding: 0 12px; background: var(--search); font-size: 14px; }

/* ---- call effects ---- */
.call-fx { position: absolute; left: 50%; bottom: 104px; transform: translateX(-50%) translateY(10px); z-index: 4; display: none; flex-direction: column; gap: 6px; padding: 8px; border-radius: 22px; opacity: 0; transition: opacity var(--dur-ui) var(--ease-out), transform var(--dur-ui) var(--ease-out); max-width: calc(100vw - 24px); }
.call-fx.open { display: flex; opacity: 1; transform: translateX(-50%) translateY(0); animation: pop var(--dur-ui) var(--ease-out); }
.call-fx .cfx-row { display: flex; gap: 6px; justify-content: center; }
.call-fx .cfx-row.fx { border-top: .5px solid rgba(255,255,255,.18); padding-top: 6px; flex-wrap: wrap; }
.call-fx button { width: 46px; height: 46px; border-radius: 50%; font-size: 24px; display: grid; place-items: center; background: rgba(255,255,255,.14); color: #fff; transition: transform .12s; }
.call-fx .cfx-row.fx button { width: auto; height: 40px; border-radius: 13px; padding: 0 10px; font-size: 18px; display: flex; align-items: center; gap: 6px; }
.call-fx .cfx-row.fx button small { font-size: 12px; font-weight: 600; }
.call-fx button:active { transform: scale(.9); }
.call .controls button.live { background: var(--blue); }
/* floating reactions layer, inside the call view */
.call-reacts { position: absolute; inset: 0; pointer-events: none; z-index: 3; overflow: hidden; }
.call-reacts .cr { position: absolute; bottom: 150px; display: flex; flex-direction: column; align-items: center; gap: 2px; animation: cr-float 2.5s cubic-bezier(.2,.8,.3,1) forwards; will-change: transform, opacity; }
.call-reacts .cr b { font-size: 44px; line-height: 1; filter: drop-shadow(0 4px 10px rgba(0,0,0,.45)); }
.call-reacts .cr small { font-size: 12px; font-weight: 700; color: #fff; background: rgba(0,0,0,.45); padding: 2px 8px; border-radius: 8px; }
@keyframes cr-float {
  0% { transform: translate(0, 40px) scale(.6); opacity: 0; }
  12% { transform: translate(0, 0) scale(1.15); opacity: 1; }
  100% { transform: translate(var(--drift, 0), -420px) scale(.9); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) { .call-reacts .cr { animation-duration: 1.2s; } }

/* ---- Bitmoji scan ---- */
.sheet.moji-scan { width: 380px; position: relative; padding-top: 40px; display: flex; flex-direction: column; gap: 10px; text-align: center; align-items: center; }
.sheet.moji-scan .it-x { position: absolute; top: 12px; right: 12px; width: 30px; height: 30px; border-radius: 50%; display: grid; place-items: center; background: var(--search); color: var(--text-3); }
.sheet.moji-scan h3 { margin: 0; }
.sheet.moji-scan .fine { margin: 0; }
.moji-stage { position: relative; width: 240px; height: 240px; border-radius: 50%; overflow: hidden; background: #111; box-shadow: 0 10px 30px rgba(0,0,0,.18), inset 0 0 0 3px rgba(255,255,255,.6); }
.moji-stage video, .moji-stage img { width: 100%; height: 100%; object-fit: cover; display: block; }
.moji-stage video { transform: scaleX(-1); }
.moji-stage img.result { background: #fff; }
.moji-busy { position: absolute; inset: 0; display: grid; place-content: center; gap: 10px; color: #fff; font-size: 13px; font-weight: 600; background: rgba(0,0,0,.55); }
.moji-busy .spin { width: 28px; height: 28px; margin: 0 auto; border-radius: 50%; border: 3px solid rgba(255,255,255,.25); border-top-color: #fff; animation: mojispin .9s linear infinite; }
@keyframes mojispin { to { transform: rotate(360deg); } }
.sheet.moji-scan .acts { display: flex; gap: 8px; width: 100%; margin-top: 4px; }
.sheet.moji-scan .acts button { flex: 1; height: 42px; border-radius: 13px; font-weight: 600; font-size: 14.5px; background: var(--blue); color: #fff; }
.sheet.moji-scan .acts button.ghost { background: var(--field); color: var(--text); box-shadow: inset 0 0 0 1px var(--field-border); }
.avatar.moji img { width: 100%; height: 100%; object-fit: cover; border-radius: 50%; }

/* ---- Beam score ---- */
/* Snapscore-style: just the flame and the number, sitting inline next to a name. */
.beam-score { display: inline-flex; align-items: center; gap: 4px; vertical-align: baseline; }
.beam-score.full { margin-top: 4px; }
.bs-flame { font-size: 13px; line-height: 1; }
.bs-num { font-size: 14px; font-weight: 700; letter-spacing: -.01em; color: var(--text-2); font-variant-numeric: tabular-nums; }
.beam-score.full .bs-flame { font-size: 15px; }
.beam-score.full .bs-num { font-size: 16px; }

/* ---- profile card ---- */
.sheet.profile-sheet { width: 340px; text-align: center; position: relative; padding-top: 42px; display: flex; flex-direction: column; align-items: center; gap: 3px; }
.sheet.profile-sheet .it-x { position: absolute; top: 12px; right: 12px; width: 30px; height: 30px; border-radius: 50%; display: grid; place-items: center; background: var(--search); color: var(--text-3); }
.sheet.profile-sheet .avatar { margin-bottom: 10px; }
.pf-name { font-size: 21px; font-weight: 800; letter-spacing: -.02em; }
/* Admin-granted "OG" badge — shows on a profile card, the Messages list row, and the
   open chat's header, all three reading straight off the same pub()'d `og` flag. */
.og-badge { display: inline-block; margin-left: 5px; padding: 1px 6px; border-radius: 6px; font-size: 10px; font-weight: 800; letter-spacing: .02em; vertical-align: 1px; background: linear-gradient(180deg, #ffd76a, #f5a623); color: #4a3200; }
.pf-name .og-badge { font-size: 12px; padding: 2px 8px; vertical-align: 3px; }
.pf-sub { font-size: 13.5px; color: var(--text-2); }
.pf-acts { display: flex; gap: 10px; width: 100%; margin-top: 14px; }
.pf-acts button { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 5px; font-size: 11.5px; font-weight: 600; color: var(--blue); }
.pf-acts button span { width: 46px; height: 46px; border-radius: 50%; display: grid; place-items: center; background: rgba(11,132,254,.13); }
.pf-acts button span svg { width: 21px; height: 21px; }
.pf-meta { font-size: 12px; color: var(--text-3); margin-top: 12px; }
.pf-report { display: inline-flex; align-items: center; gap: 5px; margin-top: 10px; font-size: 12.5px; color: var(--text-3); }
.pf-report svg { width: 13px; height: 13px; }
.pf-report:active { color: var(--red); }
.who.tappable { cursor: pointer; border-radius: 12px; margin: -4px -6px; padding: 4px 6px; transition: background-color var(--dur-ui) var(--ease-out); }
.who.tappable:active { background: var(--row-hover); }

/* ---- FaceTime recents, as cards ---- */
.ft-grid { display: grid; grid-template-columns: 1fr; gap: 10px; padding: 0 8px 10px; align-content: start; }
.ft-card {
  position: relative; border-radius: 20px; padding: 14px; min-height: 150px; cursor: pointer;
  display: flex; flex-direction: column; background: var(--card-solid);
  box-shadow: inset 0 0 0 .5px var(--glass-line), 0 1px 3px rgba(0,0,0,.05);
  transition: transform var(--dur-fast) var(--ease-out);
}
.ft-card:active { transform: scale(.98); }
/* Two lines instead of one — at two cards per row a single ellipsised line cut
   off most real names. */
.ftc-name { font-size: 16px; font-weight: 700; letter-spacing: -.01em; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; word-break: break-word; line-height: 1.2; }
.ft-card.missed .ftc-name { color: var(--red); }
.ftc-av { flex: 1; display: grid; place-items: center; padding: 10px 0; }
.ftc-av .avatar { width: 74px; height: 74px; font-size: 26px; }
.ftc-av .avatar.grp { display: grid; place-items: center; }
.ftc-av .avatar.grp svg { width: 38px; height: 38px; }
.ftc-foot { display: flex; align-items: flex-end; justify-content: space-between; gap: 8px; }
.ftc-meta { display: flex; flex-direction: column; gap: 1px; font-size: 13.5px; color: var(--text-2); min-width: 0; }
.ftc-kind { display: flex; align-items: center; gap: 3px; font-weight: 500; color: var(--text); }
.ftc-kind svg { width: 14px; height: 14px; flex: none; }
.ft-card.missed .ftc-kind { color: var(--red); }
.ftc-when { color: var(--text-3); }
.ftc-btn { width: 40px; height: 40px; border-radius: 50%; flex: none; display: grid; place-items: center; background: var(--search); color: var(--text-2); }
.ftc-btn svg { width: 19px; height: 19px; }
@media (max-width: 760px) {
  .ft-grid { grid-template-columns: 1fr 1fr; gap: 12px; padding: 0 12px calc(104px + env(safe-area-inset-bottom)); }
  .ft-card { min-height: 190px; border-radius: 24px; }
  .ftc-av .avatar { width: 92px; height: 92px; font-size: 32px; }
}

/* ---- Settings, iOS grouped-inset style ---- */
.sheet.settings .sbody { background: var(--window); }
.sheet.settings .sec {
  font-size: 12.5px; font-weight: 600; text-transform: uppercase; letter-spacing: .05em;
  color: var(--text-3); padding: 18px 16px 7px 4px;
}
.sgroup { border-radius: 16px; background: var(--card-solid); box-shadow: inset 0 0 0 .5px var(--glass-line); margin-bottom: 4px; }
.srow { padding: 13px 14px; gap: 12px; }
.srow + .srow { border-top: .5px solid var(--hairline); }
.srow .sl { font-size: 15.5px; }
.srow .sl small { font-size: 12.5px; line-height: 1.35; margin-top: 2px; }
@media (max-width: 760px) {
  .sheet.settings .shead { padding: 14px 12px 12px 18px; }
  .sheet.settings .shead h3 { font-size: 26px; font-weight: 800; letter-spacing: -.02em; }
  .sheet.settings .sbody { padding: 4px 14px 18px; }
  .srow { padding: 14px; min-height: 52px; }
  .srow .sl { font-size: 16px; }
  /* Stack the label above wide controls instead of squeezing them side by side. */
  .srow:has(.seg) { flex-direction: column; align-items: stretch; gap: 10px; }
  .srow:has(.seg) .seg { width: 100%; justify-content: space-between; }
  .srow:has(.seg) .seg button { flex: 1; }
  .toggle { flex: none; }
}

/* ---- People directory ---- */
.dock-icon.people { background: linear-gradient(180deg, #6aa9ff, #0b6ee8); box-shadow: 0 4px 12px rgba(11,110,232,.28), inset 0 1px 0 rgba(255,255,255,.45); }
.dock-icon.stories { background: linear-gradient(160deg, #ff9a56, #d7263d); box-shadow: 0 4px 12px rgba(215,38,61,.28), inset 0 1px 0 rgba(255,255,255,.45); }
.badge.dot { min-width: 12px; width: 12px; height: 12px; padding: 0; top: -2px; right: -2px; }
.dock-icon.admin { background: linear-gradient(180deg, #7b7b86, #3a3a42); color: #fff; box-shadow: 0 4px 12px rgba(58,58,66,.3), inset 0 1px 0 rgba(255,255,255,.35); }

/* ---- Admin dashboard ---- */
/* Section nav in the sidebar, one button per section, jumps + highlights via scroll. */
.admin-nav { padding: 6px 10px; display: flex; flex-direction: column; gap: 2px; }
.admin-nav-btn { display: flex; align-items: center; gap: 10px; height: 40px; padding: 0 12px; border-radius: 11px; font-size: 14px; font-weight: 550; color: var(--text-2); text-align: left; transition: background-color var(--dur-ui) var(--ease-out), color var(--dur-ui) var(--ease-out); }
.admin-nav-btn:active { background: var(--row-hover); }
@media (hover: hover) and (pointer: fine) { .admin-nav-btn:hover { background: var(--row-hover); color: var(--text); } }
.admin-nav-ic { display: grid; place-items: center; width: 20px; height: 20px; flex: none; color: var(--text-3); }
.admin-nav-ic svg { width: 18px; height: 18px; }
.admin-nav-badge { margin-left: auto; min-width: 18px; height: 18px; padding: 0 5px; border-radius: 9px; background: var(--blue); color: #fff; font-size: 11px; font-weight: 700; display: grid; place-items: center; }

/* iPad's real bug: .admin-wrap's max-width is a viewport-relative constant, but the
   rail (72px) + sidebar (330px) eat into the space actually left for it — a 768px iPad
   in portrait passed old min-width media queries meant for "there's room," while the
   pane itself only had ~360px. Every grid below now sizes off its own rendered width
   (auto-fit/minmax, or a container query), not the viewport, so this can't recur at
   some other in-between width later. */
.admin-wrap { flex: 1; overflow-y: auto; padding: 78px 22px 40px; max-width: 780px; margin: 0 auto; width: 100%; box-sizing: border-box; container-type: inline-size; }
.admin-sec { margin-bottom: 36px; scroll-margin-top: 78px; }
.admin-sec-h { display: flex; align-items: center; gap: 9px; margin-bottom: 6px; }
.admin-sec-ic { display: grid; place-items: center; width: 30px; height: 30px; border-radius: 9px; background: var(--search); color: var(--text-2); flex: none; }
.admin-sec-ic svg { width: 16px; height: 16px; }
.admin-sec-h h2 { margin: 0; font-size: 19px; letter-spacing: -.01em; }
.admin-sec-count { font-size: 12.5px; color: var(--text-3); background: var(--search); padding: 2px 8px; border-radius: 8px; font-weight: 600; }
.admin-sec .fine { margin: 0 0 14px; }

.admin-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(100px, 1fr)); gap: 10px; margin: 2px 0 12px; }
.admin-stat { padding: 16px 10px; border-radius: 16px; background: var(--search); text-align: center; }
.admin-stat b { display: block; font-size: 24px; font-weight: 700; letter-spacing: -.02em; }
.admin-stat span { font-size: 11.5px; color: var(--text-3); }

.admin-card-grid { display: grid; grid-template-columns: 1fr; gap: 12px; }
@container (min-width: 640px) { .admin-card-grid { grid-template-columns: 1fr 1fr; align-items: start; } }
.admin-card { padding: 16px 18px; border-radius: 18px; background: var(--search); display: flex; flex-direction: column; gap: 9px; box-shadow: inset 0 0 0 1px var(--hairline); }
.admin-card.warn { background: color-mix(in srgb, var(--red) 9%, var(--search)); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--red) 30%, transparent); }
.admin-card.danger-zone { background: color-mix(in srgb, var(--red) 5%, var(--search)); box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--red) 45%, transparent); }
.admin-card-t { font-weight: 650; font-size: 14.5px; display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }
.admin-card .fine { margin: -3px 0 0; }
.admin-card input, .admin-card textarea, .admin-card select { border: 0; border-radius: 10px; padding: 9px 12px; background: var(--field); box-shadow: inset 0 0 0 1px var(--field-border); font-size: 13.5px; font-family: inherit; resize: vertical; width: 100%; box-sizing: border-box; color: var(--text); }
.admin-card-acts { display: flex; gap: 8px; margin-top: 2px; }
.admin-card-acts .mini { flex: none; }
.admin-ov-photo-preview { position: relative; width: 100%; border-radius: 10px; overflow: hidden; }
.admin-ov-photo-preview img { width: 100%; max-height: 160px; object-fit: cover; display: block; }
.admin-ov-photo-preview .it-x { position: absolute; top: 6px; right: 6px; width: 26px; height: 26px; border-radius: 50%; background: rgba(0,0,0,.5); color: #fff; display: grid; place-items: center; }
.admin-ov-poll-opts { display: flex; flex-direction: column; gap: 6px; }
.admin-ov-poll-tally { display: flex; flex-direction: column; gap: 2px; padding: 8px 10px; background: var(--field); border-radius: 10px; }

.admin-pill { display: inline-block; font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .03em; padding: 2px 7px; border-radius: 7px; background: var(--field); color: var(--text-3); vertical-align: middle; }
.admin-pill.on, .admin-pill.ok { background: color-mix(in srgb, var(--green) 18%, transparent); color: var(--green); }
.admin-pill.down { background: color-mix(in srgb, var(--red) 16%, transparent); color: var(--red); }

.admin-msg { font-size: 13px; margin-bottom: 10px; min-height: 0; }
.admin-msg:empty { display: none; }
.admin-msg.err { color: var(--red); }
.admin-msg.ok { color: var(--green); }

/* Multi-select "pick people" widget — chips + a live search dropdown, shared by the
   overlay and mass-message target pickers. */
.people-select { position: relative; }
.people-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.people-chips:empty { display: none; }
.people-chip { display: inline-flex; align-items: center; gap: 5px; height: 26px; padding: 0 6px 0 10px; border-radius: 13px; background: var(--field); box-shadow: inset 0 0 0 1px var(--field-border); font-size: 12.5px; font-weight: 600; }
.people-chip button { display: grid; place-items: center; width: 17px; height: 17px; border-radius: 50%; background: var(--hairline); color: var(--text-2); }
.people-chip button svg { width: 8px; height: 8px; }
.people-search { display: block; }
.people-dropdown { position: absolute; left: 0; right: 0; top: calc(100% + 4px); z-index: 5; max-height: 200px; overflow-y: auto; background: var(--card-solid); border-radius: 12px; box-shadow: 0 10px 30px rgba(0,0,0,.18), inset 0 0 0 1px var(--glass-line); padding: 4px; }
.people-opt { padding: 8px 10px; border-radius: 8px; font-size: 13.5px; cursor: pointer; }
.people-opt.empty { color: var(--text-3); cursor: default; }
@media (hover: hover) and (pointer: fine) { .people-opt:not(.empty):hover { background: var(--row-hover); } }

.admin-filter { display: flex; align-items: center; gap: 8px; height: 38px; padding: 0 12px; border-radius: 11px; background: var(--search); margin-bottom: 10px; color: var(--text-3); }
.admin-filter svg { width: 15px; height: 15px; flex: none; }
.admin-filter input { flex: 1; border: 0; background: transparent; font-size: 13.5px; color: var(--text); min-width: 0; }

.admin-table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; margin: 0 -4px; padding: 0 4px; }
.admin-table { width: 100%; min-width: 560px; border-collapse: collapse; font-size: 13.5px; }
.admin-table th { text-align: left; font-size: 11px; text-transform: uppercase; letter-spacing: .04em; color: var(--text-3); padding: 0 10px 8px; font-weight: 700; }
.admin-table td { padding: 11px 10px; border-top: 1px solid var(--hairline); vertical-align: middle; }
.admin-table tr td:first-child { font-weight: 600; }
.admin-table .yes { color: var(--green); font-weight: 700; }
.admin-table .no { color: var(--text-3); }
.admin-table tr.banned td:first-child { opacity: .8; }
@media (hover: hover) and (pointer: fine) { .admin-table tbody tr:hover { background: var(--row-hover); } }
.admin-new { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin-bottom: 12px; }
.admin-new input { height: 38px; border: 0; border-radius: 10px; padding: 0 12px; background: var(--search); font-size: 13.5px; min-width: 0; flex: 1; }
.admin-new .mini { height: 38px; padding: 0 16px; border-radius: 10px; font-size: 13.5px; }

.admin-acts { display: flex; gap: 6px; flex-wrap: wrap; }
.admin-check { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--text-2); cursor: pointer; }
.admin-check input { width: 15px; height: 15px; accent-color: var(--blue); flex: none; }

.admin-convo-acts { display: flex; align-items: center; gap: 6px; flex: none; }
.admin-convo-acts .icon-btn { background: var(--field); color: var(--text-2); }

/* ---- admin emergency chat preview ---------------------------------------- */
.sheet.acp-sheet { width: 420px; text-align: left; display: flex; flex-direction: column; max-height: min(600px, calc(var(--app-vh, 100vh) - 60px)); }
.sheet.convos-browser-sheet { width: 480px; text-align: left; display: flex; flex-direction: column; max-height: min(640px, calc(var(--app-vh, 100vh) - 60px)); }
/* Every .sheet-wrap shares z-index 60 and stacks by DOM order otherwise — opening the
   emergency preview (or its own password prompt) FROM WITHIN this browser appends
   its .sheet-wrap later in the same render, which would otherwise bury it behind
   this one instead of stacking on top. Pin this one a layer below the default so
   anything opened on top of it always wins, regardless of render order. This was
   the actual bug behind "the zoom button does nothing" — the preview WAS opening,
   just invisibly, behind this sheet's own backdrop. */
#convosBrowserWrap { z-index: 55; }
.cb-tabs { display: flex; gap: 6px; margin-bottom: 10px; }
.cb-tab { flex: 1; height: 32px; border-radius: 9px; background: var(--search); color: var(--text-2); font-size: 12.5px; font-weight: 600; }
.cb-tab.on { background: var(--blue); color: #fff; }
.cb-list { flex: 1; overflow-y: auto; margin: 0 -4px; }
.cb-row { display: flex; align-items: center; gap: 10px; padding: 10px 4px; border-bottom: 1px solid var(--hairline); }
.cb-row-main { flex: 1; min-width: 0; }
.cb-row-name { font-size: 13.5px; font-weight: 600; display: flex; align-items: center; gap: 5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cb-row-name svg { width: 13px; height: 13px; flex: none; color: var(--text-3); }
.cb-row-preview { font-size: 12px; color: var(--text-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-top: 2px; }
.cb-row-meta { flex: none; display: flex; flex-direction: column; align-items: flex-end; gap: 4px; }
.cb-row-meta .admin-convo-acts { display: flex; gap: 6px; }
.acp-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 6px; }
.acp-head b { font-size: 16px; }
.acp-warn { display: flex; align-items: center; gap: 6px; color: var(--red); font-weight: 600; margin: 0 0 10px; }
.acp-warn svg { width: 14px; height: 14px; flex: none; }
/* Same .msg/.col/.bubble the real thread uses (see style rules above under "Chat
   pane") — only the bubble color variables are overridden, to red, scoped to this
   container so nothing outside the preview is affected. */
.acp-thread {
  flex: 1; overflow-y: auto; overflow-x: hidden; display: flex; flex-direction: column;
  padding: 10px 8px; margin: 0 -4px; border-radius: 14px; background: var(--card-solid);
  box-shadow: inset 0 0 0 1px var(--hairline);
  --out-bubble: #ff453a; --out-text: #fff;
  --in-bubble: #c62b23; --in-text: #fff;
}
.acp-thread .bubble img { max-width: 100%; max-height: 220px; border-radius: 12px; display: block; }
.acp-thread .acp-time { font-size: 10.5px; color: var(--text-3); margin: 3px 2px 0; }
.acp-thread .msg.out .acp-time { text-align: right; }
.acp-thread .acp-del { margin-top: 4px; height: 24px; padding: 0 9px; font-size: 11px; }
.acp-thread .msg.out .acp-del { align-self: flex-end; }
.acp-foot { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--hairline); }
.acp-notify-line { display: flex; gap: 8px; margin-top: 10px; }
.acp-notify-line input { flex: 1; height: 36px; border: 0; border-radius: 10px; padding: 0 12px; background: var(--search); color: var(--text); font-size: 14px; min-width: 0; }
.acp-notify-line button { display: flex; align-items: center; gap: 5px; color: var(--red); font-weight: 600; font-size: 13px; padding: 0 10px; white-space: nowrap; }
.acp-notify-line button svg { width: 14px; height: 14px; }

.admin-id-list { display: flex; flex-direction: column; gap: 10px; }
.admin-id-card { display: flex; gap: 12px; padding: 12px; border-radius: 16px; background: var(--search); align-items: flex-start; }
.admin-id-card img { width: 84px; height: 84px; object-fit: cover; border-radius: 10px; flex: none; background: #ddd; cursor: zoom-in; transition: transform var(--dur-fast) var(--ease-out); }
.admin-id-card img:active { transform: scale(.94); }
@media (hover: hover) and (pointer: fine) { .admin-id-card img:hover { transform: scale(1.04); } }
/* Full-size ID photo, tapped open from the review queue's 84px thumbnail. */
.img-zoom { position: fixed; inset: 0; z-index: 600; background: rgba(0,0,0,.85); display: grid; place-items: center; padding: 24px; cursor: zoom-out; animation: fade var(--dur-ui) ease; }
.img-zoom img { max-width: min(90vw, 700px); max-height: 85vh; border-radius: 12px; box-shadow: 0 20px 60px rgba(0,0,0,.5); animation: pop var(--dur-sheet) var(--ease-out); }
.img-zoom-x { position: absolute; top: calc(16px + env(safe-area-inset-top)); right: 16px; width: 38px; height: 38px; border-radius: 50%; background: rgba(255,255,255,.14); color: #fff; display: grid; place-items: center; }
.img-zoom-x svg { width: 15px; height: 15px; }
@media (prefers-reduced-motion: reduce) { .img-zoom, .img-zoom img { animation: fade var(--dur-ui) ease; transform: none; } .admin-id-card img { transition: none; } }
.admin-id-body { min-width: 0; flex: 1; }
.admin-id-t { font-weight: 650; font-size: 14px; margin-bottom: 3px; }
.admin-audit-list { display: flex; flex-direction: column; gap: 4px; max-height: 320px; overflow-y: auto; }
.admin-audit-row { display: flex; align-items: baseline; gap: 8px; padding: 8px 10px; border-radius: 10px; background: var(--search); font-size: 12.5px; flex-wrap: wrap; }
.admin-audit-who { font-weight: 700; flex: none; }
.admin-audit-what { flex: 1; min-width: 0; }
.admin-audit-when { flex: none; white-space: nowrap; }
.block-pick-row { display: flex; align-items: center; gap: 8px; margin-top: 8px; flex-wrap: wrap; }
.single-pick { position: relative; flex: 1; min-width: 140px; }
.single-pick .people-search { width: 100%; height: 34px; border-radius: 10px; background: var(--field); box-shadow: inset 0 0 0 1px var(--field-border); padding: 0 10px; font-size: 13px; color: var(--text); }
.admin-report-list { display: flex; flex-direction: column; gap: 6px; margin-top: 10px; }
.admin-report-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 10px 12px; border-radius: 12px; background: var(--search); font-size: 13px; }
.admin-report-row.reviewed { opacity: .55; }
.admin-report-txt { min-width: 0; }
.online-dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; background: var(--green); margin-right: 4px; vertical-align: 1px; }
.row.person { grid-template-columns: 46px 1fr; padding-left: 10px; }
.person-card { flex: 1; display: grid; place-content: center; justify-items: center; gap: 6px; padding: 80px 20px 40px; text-align: center; position: relative; }
.person-card .pn { font-size: 24px; font-weight: 700; letter-spacing: -.02em; margin-top: 8px; }
.person-card .pn .og-badge { font-size: 12px; padding: 2px 8px; vertical-align: 3px; }
.person-card .pp { color: var(--text-2); font-size: 15px; }
.person-card .pacts { display: flex; gap: 14px; margin: 18px 0 6px; }
.person-card .pacts button { width: 72px; text-align: center; font-size: 12px; color: var(--blue); }
.person-card .pacts button span { display: grid; place-items: center; width: 54px; height: 54px; margin: 0 auto 6px; border-radius: 50%; background: rgba(11,132,254,.14); }
.person-card .pacts button svg { width: 24px; height: 24px; }
.person-card .pinfo { color: var(--text-3); font-size: 13px; }
.person-card .pinfo a { color: var(--blue); text-decoration: none; }
.mini { height: 28px; padding: 0 12px; border-radius: 9px; background: var(--blue); color: #fff; font-size: 12px; font-weight: 600; box-shadow: inset 0 1px 0 rgba(255,255,255,.3); display: inline-flex; align-items: center; justify-content: center; gap: 6px; }
/* An inline SVG with no width/height stretches to fill its button — every .mini
   that carries an icon (Add people, Photos & videos, Mute) needs this. */
.mini svg { width: 16px; height: 16px; flex: none; }
.mini.ghost { background: var(--field); color: var(--text); box-shadow: inset 0 0 0 1px var(--field-border); }
.mini.danger { background: var(--red); }
.mini.ghost.danger { background: var(--field); box-shadow: inset 0 0 0 1px var(--field-border); color: var(--red); }
.mini:active { transform: scale(.96); }

/* ---- Welcome sheet ---- */
.sheet.welcome { width: 440px; text-align: center; }
.sheet.welcome .brand { margin-bottom: 12px; }
.sheet.welcome .brand .app-icon { width: 54px; height: 54px; border-radius: 14px; }
.sheet.welcome .brand .app-icon svg { width: 32px; height: 32px; }
.sheet.welcome h3 { font-size: 20px; letter-spacing: -.02em; margin-bottom: 6px; }
.sheet.welcome .wsub { color: var(--text-2); font-size: 14px; line-height: 1.45; margin: 0 6px 14px; }
.search.big { margin: 0 0 6px; height: 40px; border-radius: 12px; }
.search.big input { font-size: 15px; }
.sheet.welcome .picks { text-align: left; max-height: 300px; }
.sheet.welcome .picks .row { grid-template-columns: 36px 1fr auto; }
.sheet.welcome .picks .row .mini { margin-left: 6px; }

/* ---- Settings sheet ---- */
.sheet.settings, .sheet.changelog { width: 460px; max-height: calc(100vh - 40px); display: flex; flex-direction: column; padding: 0; overflow: hidden; }
.sheet.settings .shead, .sheet.changelog .shead { display: flex; align-items: center; justify-content: space-between; padding: 16px 12px 10px 20px; border-bottom: 1px solid var(--hairline); }
.sheet.settings .shead h3, .sheet.changelog .shead h3 { margin: 0; font-size: 18px; }
.sheet.settings .sbody, .sheet.changelog .sbody { overflow-y: auto; padding: 8px 14px 18px; }
/* ---- What's new / changelog sheet ---- */
.sheet.changelog .sbody { padding-top: 14px; }
.cl-entry { margin-bottom: 20px; }
.cl-entry:last-child { margin-bottom: 0; }
.cl-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; margin-bottom: 8px; }
.cl-head b { font-size: 14px; font-weight: 700; }
.cl-entry.latest .cl-head b { color: var(--blue); }
.cl-head span { font-size: 11.5px; color: var(--text-3); flex: none; }
.cl-entry ul { margin: 0; padding-left: 18px; display: flex; flex-direction: column; gap: 7px; }
.cl-entry li { font-size: 13px; color: var(--text-2); line-height: 1.4; }
.sgroup { border-radius: 14px; background: var(--field); box-shadow: inset 0 0 0 1px var(--field-border); margin-bottom: 8px; overflow: hidden; }
/* min-width:0 on the flex child, or the name field's intrinsic width pushes the
   Save button off the right edge of a phone screen. */
.sgroup.profile { display: flex; align-items: center; gap: 12px; padding: 12px 14px; }
.sgroup.profile > .field-line { min-width: 0; }
.sgroup.profile .field-line input { min-width: 0; flex: 1; }
.sgroup.profile .field-line button { flex: none; }
.sec-pass-sheet .admin-overlay-ic { margin: 4px auto 10px; }
.sec-pass-sheet input { width: 100%; height: 40px; border: 0; border-radius: 12px; padding: 0 14px; background: var(--field); color: var(--text); font-size: 14px; margin-top: 14px; box-shadow: inset 0 0 0 1px var(--field-border); }
.sec-pass-sheet .acts { width: 100%; margin-top: 10px; }
.sec-pass-sheet .acts .mini { width: 100%; height: 38px; }
.msg-counter-card {
  display: flex; align-items: center; gap: 14px; padding: 14px 16px; margin: 2px 0 12px;
  border-radius: 16px; background: linear-gradient(135deg, rgba(11,132,254,.1), rgba(98,249,126,.12));
  box-shadow: inset 0 0 0 1px var(--field-border);
}
.mcc-icon {
  width: 46px; height: 46px; border-radius: 13px; flex: none; display: grid; place-items: center;
  background: linear-gradient(180deg, #62f97e, #16b843); box-shadow: 0 4px 10px rgba(20,185,63,.32);
}
.mcc-icon svg { width: 24px; height: 24px; color: #fff; }
.mcc-body { text-align: left; flex: 1; min-width: 0; }
.mcc-body b { display: block; font-size: 30px; font-weight: 800; letter-spacing: -.02em; line-height: 1.08; font-variant-numeric: tabular-nums; perspective: 260px; }
.mcc-body span { font-size: 12px; color: var(--text-2); }
/* Flip-clock digits — each one a separate 3D card so updateMsgCounter() can flip only
   the digits that actually changed, WAAPI-driven (see app.js) rather than CSS
   keyframes, since a burst of messages can retarget the same digit before it settles. */
.flip-digit, .fd-sep { display: inline-block; }
.flip-digit .fd-inner { display: inline-block; transform-style: preserve-3d; backface-visibility: hidden; }
.srow { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 11px 14px; border-top: 1px solid var(--hairline); min-height: 48px; font-size: 14px; width: 100%; text-align: left; color: inherit; text-decoration: none; }
.srow:first-child { border-top: 0; }
.srow .sl small { display: block; color: var(--text-3); font-size: 12px; margin-top: 2px; line-height: 1.3; }
.srow .sc { flex: none; display: flex; align-items: center; gap: 8px; color: var(--text-3); }
.srow .sv { font-size: 13px; color: var(--text-3); }
.srow .sv.ok { color: var(--green); font-weight: 600; }
.srow .sv.bad { color: var(--red); font-weight: 600; }
.srow.link:hover { background: var(--row-hover); }
.srow.link.red .sl { color: var(--red); }
.srow.col { flex-direction: column; align-items: stretch; gap: 8px; }
.pwgrid { display: grid; grid-template-columns: 1fr 1fr auto; gap: 8px; }
.pwgrid input { height: 34px; border: 0; border-radius: 9px; padding: 0 10px; background: var(--search); font-size: 13px; min-width: 0; }
.serr { color: var(--red); font-size: 12px; min-height: 14px; }
.seg.light { background: var(--search); }
.seg.light button { color: var(--text-2); padding: 5px 12px; font-size: 12.5px; }
.seg.light button.on { background: var(--card-solid); color: var(--text); }
.seg.light [data-pref="textSize"]:nth-child(1) { font-size: 11px; } .seg.light [data-pref="textSize"]:nth-child(2) { font-size: 13px; } .seg.light [data-pref="textSize"]:nth-child(3) { font-size: 15px; } .seg.light [data-pref="textSize"]:nth-child(4) { font-size: 17px; }
/* 100vh on iOS is the LARGE viewport — taller than what's actually visible. With the
   sheet bottom-anchored that pushed the header (and its close button) off the top of
   the screen, which is why Settings couldn't be exited on iPhone. Bound by --app-vh
   and keep the header pinned so the X is always reachable. */
@media (max-width: 760px) { .sheet.settings, .sheet.changelog { width: 100vw; max-width: 100vw; max-height: var(--app-vh, 100vh); height: var(--app-vh, 100vh); border-radius: 0; } .sheet-wrap { align-items: end; } .sheet.settings .sbody, .sheet.changelog .sbody { padding-bottom: calc(18px + env(safe-area-inset-bottom)); } .pwgrid { grid-template-columns: 1fr; } }
.sheet.settings .shead, .sheet.changelog .shead { position: sticky; top: 0; z-index: 2; background: var(--card-solid); }
.sheet.settings .shead .icon-btn, .sheet.changelog .shead .icon-btn { width: 36px; height: 36px; border-radius: 50%; background: var(--search); flex: none; }
.sgroup .field-line { display: flex; gap: 8px; }
.sgroup .field-line input { flex: 1; height: 36px; border: 0; border-radius: 10px; padding: 0 12px; background: var(--search); color: var(--text); font-size: 15px; font-weight: 600; min-width: 0; }
.sgroup .field-line button { color: var(--blue); font-weight: 600; font-size: 14px; padding: 0 8px; }

/* ---- voice + video messages ---- */
.bubble.voice { display: flex; align-items: center; gap: 8px; padding: 6px 12px 6px 6px; min-width: 210px; white-space: nowrap; }
.bubble.voice .vplay { width: 30px; height: 30px; border-radius: 50%; display: grid; place-items: center; background: rgba(255,255,255,.28); color: inherit; flex: none; transition: transform .1s; }
.bubble.voice .vplay:active { transform: scale(.92); }
.msg.in .bubble.voice .vplay { background: rgba(0,0,0,.08); }
.bubble.voice .vplay svg { width: 16px; height: 16px; }
.bubble.voice.playing .vplay svg path, .bubble.voice.playing .vplay svg rect { display: none; }
.bubble.voice.playing .vplay::after { content: ""; width: 10px; height: 10px; border-radius: 2px; background: currentColor; }
.bubble.voice .wave { display: flex; align-items: center; gap: 2px; height: 26px; flex: 1; }
.bubble.voice .wave i { width: 3px; border-radius: 2px; background: rgba(255,255,255,.55); transition: background .15s; }
.bubble.voice .wave i.done { background: #fff; }
.msg.in .bubble.voice .wave i { background: rgba(0,0,0,.28); }
.msg.in .bubble.voice .wave i.done { background: var(--blue); }
.bubble.voice .vdur { font-size: 12px; font-variant-numeric: tabular-nums; opacity: .85; min-width: 30px; text-align: right; }
.bubble.voice .upl { font-size: 11px; opacity: .8; }
.bubble.voice audio { display: none; }
.bubble.vid { padding: 0; overflow: hidden; background: #000 !important; width: min(300px, 78vw); aspect-ratio: var(--ar, 1.78); max-height: 420px; position: relative; }
.bubble.vid video { display: block; width: 100%; height: 100%; object-fit: cover; background: #000; }
.bubble.vid.tail::before, .bubble.vid.tail::after { display: none; }
.bubble.vid .vlen { position: absolute; left: 10px; top: 8px; font-size: 11px; font-weight: 600; color: #fff; background: rgba(0,0,0,.45); padding: 3px 7px; border-radius: 8px; pointer-events: none; }
.upl-ring { position: absolute; inset: 0; display: grid; place-items: center; background: rgba(0,0,0,.35); color: #fff; font-size: 14px; }
.bubble.file { display: flex; align-items: center; gap: 10px; min-width: 200px; max-width: 260px; text-decoration: none; color: inherit; }
.file-ic { width: 34px; height: 34px; border-radius: 9px; background: rgba(255,255,255,.22); display: grid; place-items: center; flex: none; }
.msg.in .file-ic { background: rgba(0,0,0,.08); }
.file-ic svg { width: 18px; height: 18px; }
.file-info { min-width: 0; }
.file-name { font-size: 13.5px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.file-size { font-size: 11.5px; opacity: .75; margin-top: 1px; }
.attach-preview .ap.vid { width: 140px; height: 90px; border-radius: 12px; overflow: hidden; background: #111; position: relative; }
.attach-preview .ap.vid img { width: 100%; height: 100%; object-fit: cover; }
.attach-preview .ap.vid .vph { position: absolute; inset: 0; display: grid; place-items: center; color: #fff; }
.attach-preview .ap.vid .vph svg { width: 28px; height: 28px; }
.attach-preview .ap.vid .vlen { position: absolute; left: 8px; bottom: 6px; font-size: 11px; color: #fff; background: rgba(0,0,0,.5); padding: 2px 6px; border-radius: 6px; }

/* mic button + recording state */
.field-wrap .mic { position: absolute; right: 4px; bottom: 4px; width: 28px; height: 28px; border-radius: 50%; color: var(--text-3); display: grid; place-items: center; transition: .18s; }
.field-wrap .mic svg { width: 16px; height: 16px; }
.field-wrap .mic.hide { opacity: 0; transform: scale(.5); pointer-events: none; }
.field-wrap .mic:hover { color: var(--blue); }
.composer.recording { background: var(--red); box-shadow: 0 8px 30px rgba(255,59,48,.35); color: #fff; align-items: center; padding: 6px; }
.composer.recording::before { display: none; }
.rec-x, .rec-send { width: 36px; height: 36px; border-radius: 50%; display: grid; place-items: center; color: #fff; flex: none; }
.rec-x { background: rgba(255,255,255,.22); }
.rec-send { background: #fff; color: var(--red); }
.rec-x svg { width: 14px; height: 14px; } .rec-send svg { width: 16px; height: 16px; }
.rec-wave { flex: 1; display: flex; align-items: center; gap: 8px; padding: 0 6px; height: 36px; }
.rec-dot { width: 10px; height: 10px; border-radius: 50%; background: #fff; animation: recblink 1s infinite; flex: none; }
@keyframes recblink { 50% { opacity: .25; } }
.rec-wave .bars { flex: 1; display: flex; align-items: center; justify-content: flex-end; gap: 2px; height: 28px; overflow: hidden; }
.rec-wave .bars i { width: 3px; border-radius: 2px; background: rgba(255,255,255,.9); flex: none; }
.rec-time { font-variant-numeric: tabular-nums; font-size: 13px; font-weight: 600; min-width: 36px; text-align: right; }

/* ---- group call grid ---- */
.call .top .n small { font-weight: 500; color: rgba(255,255,255,.7); }
.call .grid { position: absolute; inset: 70px 14px 110px; display: grid; gap: 12px; grid-template-columns: repeat(2, 1fr); align-content: center; overflow-y: auto; }
.call .grid.n1, .call .grid.n2 { grid-template-columns: 1fr; }
.call .grid.n2 { grid-template-rows: 1fr 1fr; }
.call .grid.n5, .call .grid.n6 { grid-template-columns: repeat(3, 1fr); }
.call .grid.n7, .call .grid.n8, .call .grid.n9 { grid-template-columns: repeat(3, 1fr); }
@media (min-width: 900px) { .call .grid.n2 { grid-template-columns: 1fr 1fr; grid-template-rows: 1fr; } .call .grid.n3, .call .grid.n4 { grid-template-columns: 1fr 1fr; } }
.call .tile { position: relative; border-radius: 18px; overflow: hidden; background: #1c1c22; aspect-ratio: 1 / 1; box-shadow: 0 0 0 .5px rgba(255,255,255,.18), 0 10px 30px rgba(0,0,0,.35); display: grid; place-items: center; }
.call .tile video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.call .tile video.contain { object-fit: contain; background: #000; }
.call video.mirror, .call .tile video.mirror { transform: scaleX(-1); }
/* Verified bug: with no width limit, a name plus the pending state's " · Ringing…"
   suffix (added via ::after below) wraps to a second line and spills past the tile's
   own edge into whatever sits beside it. max-width leaves room for the tile's own
   10px insets on both sides. */
.call .tile .tag { position: absolute; left: 10px; bottom: 10px; right: 10px; font-size: 12px; font-weight: 600; color: #fff; background: rgba(0,0,0,.45); padding: 4px 9px; border-radius: 9px; display: flex; align-items: center; gap: 5px; -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px); max-width: calc(100% - 20px); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.call .tile.self { box-shadow: 0 0 0 1.5px var(--green), 0 10px 30px rgba(0,0,0,.35); }

.avatar.grp { background: linear-gradient(180deg, #6aa9ff, #0b6ee8); }
.avatar.grp svg { width: 60%; height: 60%; color: #fff; }

/* ---- stickers ---- */
.bubble.sticker { background: transparent !important; padding: 4px; font-size: 92px; line-height: 1; filter: drop-shadow(0 6px 10px rgba(0,0,0,.18)); animation: stickerIn .45s cubic-bezier(.2,1.3,.3,1) both; }
.bubble.sticker.tail::before, .bubble.sticker.tail::after { display: none; }
.bubble.sticker img { width: 150px; height: 150px; object-fit: cover; border-radius: 22px; display: block; box-shadow: 0 8px 22px rgba(0,0,0,.2); }
@keyframes stickerIn { from { transform: scale(.4) rotate(-8deg); opacity: 0; } to { transform: none; opacity: 1; } }
.tray { position: absolute; left: 12px; right: 12px; bottom: 76px; z-index: 6; border-radius: 22px; padding: 10px 10px 8px; max-height: 320px; display: flex; flex-direction: column; animation: rise .22s cubic-bezier(.2,1,.3,1); }
.tray .tabs { display: flex; gap: 4px; overflow-x: auto; padding-bottom: 8px; scrollbar-width: none; }
.tray .tabs::-webkit-scrollbar { display: none; }
.tray .tabs button { flex: none; height: 30px; padding: 0 12px; border-radius: 10px; font-size: 13px; font-weight: 600; color: var(--text-2); }
.tray .tabs button.on { background: var(--search); color: var(--text); }
.tray .grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(56px, 1fr)); gap: 4px; overflow-y: auto; padding: 2px; }
.tray .grid button { aspect-ratio: 1; font-size: 34px; border-radius: 12px; display: grid; place-items: center; transition: transform .12s, background .12s; }
@media (hover: hover) and (pointer: fine) { .tray .grid button:hover { background: var(--row-hover); transform: scale(1.12); } }
.tray .grid button:active { transform: scale(.9); }
.tray .grid button img { width: 48px; height: 48px; border-radius: 10px; object-fit: cover; }
.tray .foot { display: flex; justify-content: space-between; align-items: center; padding: 8px 4px 0; font-size: 12px; color: var(--text-3); }
.tray .foot button { color: var(--blue); font-weight: 600; font-size: 12px; }
.plusmenu { position: absolute; left: 12px; bottom: 76px; z-index: 7; border-radius: 18px; padding: 6px; width: 220px; animation: pop .18s cubic-bezier(.2,1.2,.3,1); transform-origin: bottom left; }
/* Anchored to --composer-h, not a fixed offset — a reply bar or attachment preview
   changes the composer's height, and a fixed bottom value would either gap above it
   or sit underneath it (the same class of bug already fixed once for .thread). */
.mention-box { position: absolute; left: 12px; right: 12px; bottom: calc(var(--composer-h, 92px) + 8px); z-index: 6; border-radius: 16px; padding: 6px; max-height: 240px; overflow-y: auto; display: flex; flex-direction: column; gap: 2px; animation: rise .18s var(--ease-out); }
.mention-box button { display: flex; align-items: center; gap: 10px; padding: 7px 8px; border-radius: 10px; text-align: left; }
.mention-box button:active { background: var(--search); }
.mention-box .mn-name { flex: 1; min-width: 0; font-size: 14px; font-weight: 550; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mention-box .mn-add { font-size: 11px; font-weight: 700; color: var(--blue); text-transform: uppercase; letter-spacing: .02em; flex: none; }
.mention { color: var(--blue); font-weight: 650; }
.msg.out .bubble .mention { color: #dbeaff; }
.plusmenu button { width: 100%; display: flex; align-items: center; gap: 12px; padding: 10px 12px; border-radius: 12px; font-size: 14px; font-weight: 500; }
.plusmenu button:hover { background: var(--row-hover); }
.plusmenu button span { width: 34px; height: 34px; border-radius: 10px; display: grid; place-items: center; color: #fff; }
.plusmenu button span svg { width: 18px; height: 18px; }
.plusmenu .ph span { background: linear-gradient(180deg, #5fb0ff, #0b6ee8); } .plusmenu .st span { background: linear-gradient(180deg, #ffd166, #ff9500); } .plusmenu .vo span { background: linear-gradient(180deg, #ff6b6b, #ff3b30); }
.plusmenu .cam span { background: linear-gradient(180deg, #a0a0aa, #5a5a63); }
.plusmenu .fx span { background: linear-gradient(180deg, #d07bff, #8e34e0); }
.plusmenu .loc span { background: linear-gradient(180deg, #62f97e, #16b843); }
.plusmenu .ft span { background: linear-gradient(180deg, #62f97e, #16b843); }
.plusmenu .inv span { background: linear-gradient(180deg, #7f8b9c, #454c58); }
.plusmenu .doc span { background: linear-gradient(180deg, #8fa6c4, #4a6489); }
.plusmenu .game span { background: linear-gradient(180deg, #6ee7ff, #2178c9); }
/* Eight entries now — keep it on screen and let it scroll if the pane is short. */
.plusmenu { max-height: min(420px, calc(var(--app-vh, 100vh) - 140px)); overflow-y: auto; }
.composer .plus.open { transform: rotate(45deg); background: var(--blue); color: #fff; }

/* ---- call: pending tiles, close ---- */
.call .tile.pending { background: #17171c; }
.call .tile.pending .avatar { opacity: .7; animation: ringpulse 1.6s infinite; }
.call .tile.pending .tag::after { content: " · Ringing…"; font-weight: 500; color: rgba(255,255,255,.75); }
.call .tile.declined .tag::after { content: " · Declined"; }
.call .closex { position: absolute; top: 18px; right: 18px; z-index: 3; width: 40px; height: 40px; border-radius: 50%; background: rgba(255,255,255,.14); color: #fff; display: grid; place-items: center; }
.call .closex svg { width: 16px; height: 16px; }
.call .grid.n3, .call .grid.n4 { grid-template-columns: 1fr 1fr; }
.call .grid.n1 { align-content: stretch; }

/* missed call bubble */
.bubble.call .ct.missed { color: var(--red); }
.msg.in .bubble.call.was-missed .ci { background: rgba(255,59,48,.14); color: var(--red); }
.status.readby { color: var(--text-3); }
.tone-row { display: flex; gap: 6px; flex-wrap: wrap; justify-content: flex-end; }
.tone-row button { height: 28px; padding: 0 10px; border-radius: 9px; font-size: 12px; font-weight: 600; background: var(--search); color: var(--text-2); }
.tone-row button.on { background: var(--blue); color: #fff; }

.accent-row { display: flex; gap: 10px; }
.accent-row button { width: 30px; height: 30px; border-radius: 50%; box-shadow: inset 0 0 0 1px rgba(0,0,0,.06); }
.accent-row button.on { box-shadow: 0 0 0 2px var(--card-solid), 0 0 0 4px var(--text); }

/* ---- Add to Home Screen gate (full-screen, blocks the app until resolved) ---- */
.a2hs-gate {
  /* minmax(0,1fr): an auto track sizes itself to the card, so the card's
     max-width:100% resolved against its own width and never clamped. */
  position: fixed; inset: 0; z-index: 150; display: grid; grid-template-columns: minmax(0, 1fr); place-items: center; padding: 24px;
  background: rgba(20,20,24,.6); -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  animation: fade .25s ease;
}
.a2hs-card {
  width: 380px; max-width: min(100%, calc(100vw - 48px)); min-width: 0; border-radius: 26px; padding: 30px 26px; text-align: center;
  display: flex; flex-direction: column; align-items: center; gap: 6px; animation: rise .4s cubic-bezier(.2,.8,.2,1);
}
.a2hs-card .it-icon { width: 56px; height: 56px; border-radius: 15px; display: grid; place-items: center; margin-bottom: 8px; background: linear-gradient(180deg, #62f97e, #16b843); box-shadow: 0 8px 20px rgba(20,185,63,.32), inset 0 1px 0 rgba(255,255,255,.45); }
.a2hs-card .it-icon svg { width: 32px; height: 32px; }
.a2hs-card b { font-size: 17px; }
.a2hs-card span { font-size: 14px; line-height: 1.45; color: var(--text-2); margin-bottom: 6px; }
.a2hs-card span svg { vertical-align: -3px; color: var(--blue); }
.a2hs-card .mini { width: 100%; height: 42px; border-radius: 13px; font-size: 14.5px; margin-top: 4px; }
.a2hs-card .mini.ghost { background: var(--field); color: var(--text); box-shadow: inset 0 0 0 1px var(--field-border); }
.install-arrow {
  position: fixed; top: calc(-6px + env(safe-area-inset-top)); right: 64px; z-index: 151;
  display: flex; flex-direction: column; align-items: center; gap: 2px; color: var(--blue);
  animation: install-arrow-bounce 1.1s ease-in-out infinite; pointer-events: none;
}
.install-arrow svg { width: 26px; height: 26px; filter: drop-shadow(0 1px 3px rgba(0,0,0,.35)); }
.install-arrow span { display: flex; align-items: center; gap: 4px; font-size: 11px; font-weight: 700; color: #fff; background: var(--blue); padding: 3px 8px 3px 7px; border-radius: 8px; box-shadow: 0 2px 6px rgba(11,132,254,.4); }
@keyframes install-arrow-bounce { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-7px); } }

.carriers { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin: 4px 0; }
.carriers button { height: 44px; border-radius: 12px; background: var(--field); box-shadow: inset 0 0 0 1px var(--field-border); font-size: 14px; font-weight: 600; transition: transform .1s, background .12s; }
.carriers button:hover { background: var(--search); }
.carriers button:active { transform: scale(.96); }

/* ---- Google sign-in + photo avatars ---- */
.or { display: flex; align-items: center; gap: 10px; margin: 16px 0 12px; color: var(--text-3); font-size: 12px; }
.or::before, .or::after { content: ""; flex: 1; height: 1px; background: var(--hairline); }
.gsi { display: flex; justify-content: center; min-height: 44px; }
.avatar.pic { overflow: hidden; background: var(--search); }
.avatar.pic img { width: 100%; height: 100%; object-fit: cover; display: block; }
.pwgrid.two { grid-template-columns: 1fr auto; }
.pwgrid.pwchange { grid-template-columns: 1fr; }
.pwgrid.pwchange #pwSave { justify-self: start; padding: 0 20px; }

/* ---- ID verification ---- */
.sheet.verify { width: 440px; text-align: center; position: relative; }
.sheet.verify .it-x { position: absolute; top: 12px; right: 12px; width: 30px; height: 30px; border-radius: 50%; display: grid; place-items: center; background: var(--search); color: var(--text-3); }
.sheet.verify .it-x svg { width: 12px; height: 12px; }
.sheet.verify h3 { font-size: 20px; letter-spacing: -.02em; margin: 6px 0 6px; }
.sheet.verify .wsub { color: var(--text-2); font-size: 14px; line-height: 1.5; margin: 0 4px 16px; }
.sheet.verify .wsub b { color: var(--text); }
.sheet.verify .scan { margin-bottom: 6px; }
.sheet.verify .err { color: var(--red); font-size: 13px; min-height: 18px; margin: 6px 0; }
.vicon { width: 64px; height: 64px; border-radius: 18px; margin: 4px auto 12px; display: grid; place-items: center; color: #fff; background: linear-gradient(180deg, #6aa9ff, #0b6ee8); box-shadow: 0 10px 26px rgba(11,110,232,.3), inset 0 1px 0 rgba(255,255,255,.45); }
/* The admin panel's own lock screen — sits in the sidebar column, not a sheet, since
   nothing else in the panel should even be reachable behind it. */
.admin-lock { display: flex; flex-direction: column; align-items: center; text-align: center; padding: 60px 24px 0; }
.admin-lock h3 { font-size: 19px; letter-spacing: -.02em; margin: 6px 0 6px; }
.admin-lock .wsub { color: var(--text-2); font-size: 13.5px; line-height: 1.5; margin: 0 0 18px; }
.admin-lock input { width: 100%; height: 42px; border: 0; border-radius: 11px; padding: 0 14px; background: var(--field); box-shadow: inset 0 0 0 1px var(--field-border); font-size: 14.5px; color: var(--text); margin-bottom: 10px; text-align: center; }
.admin-lock .primary { width: 100%; }
.admin-lock .err { color: var(--red); font-size: 13px; min-height: 18px; margin-top: 8px; }
.vicon svg { width: 34px; height: 34px; }
.vicon.ok { background: linear-gradient(180deg, #62f97e, #16b843); box-shadow: 0 10px 26px rgba(20,185,63,.3), inset 0 1px 0 rgba(255,255,255,.45); }
.vicon.hold { background: linear-gradient(180deg, #ff9f0a, #ff6a00); box-shadow: 0 10px 26px rgba(255,120,0,.3), inset 0 1px 0 rgba(255,255,255,.45); }
.sheet-wrap.hold { background: rgba(0,0,0,.55); -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px); z-index: 120; }
@media (max-width: 760px) { body:has(.sheet-wrap) .rail, body:has(.fx-pick) .rail { opacity: 0; pointer-events: none; transition: opacity .2s; } .sheet.verify { border-bottom-left-radius: 0; border-bottom-right-radius: 0; width: 100vw; max-width: 100vw; padding-bottom: calc(18px + env(safe-area-inset-bottom)); } }

/* pane crossfade when switching apps */
.shell .sidebar, .shell .pane { transition: opacity var(--dur-ui) var(--ease-out); }
.shell.switching .sidebar, .shell.switching .pane { opacity: 0; transition: none; }
/* reduced motion: keep fades, drop movement */
@media (prefers-reduced-motion: reduce) {
  .msg.arrive .col { animation: fade var(--dur-ui) ease both; }
  .sheet, .popover, .tapbar, .incoming, .a2hs-gate, .a2hs-card, .toast, #updBar { animation: fade var(--dur-ui) ease; transform: none !important; }
  .toast.enter, .toast.leave { opacity: 0; }
  .install-arrow { animation: none; }
}
/* The call-log bubble shares the "call" class with the full-screen call overlay; keep it in the thread. */
.bubble.call { position: relative !important; inset: auto !important; z-index: auto !important; animation: none; overflow: visible; }

/* ---- avatar builder + moji avatars ---- */
.avatar.moji { overflow: hidden; text-shadow: none; }
.avatar.moji svg { width: 100%; height: 100%; display: block; }
.avbtn { position: relative; border-radius: 50%; }
.avbtn .edit { position: absolute; right: -2px; bottom: -2px; width: 26px; height: 26px; border-radius: 50%; background: var(--blue); color: #fff; display: grid; place-items: center; box-shadow: 0 0 0 2px var(--card-solid); }
.avbtn .edit svg { width: 14px; height: 14px; }
.sheet.moji-sheet { width: 480px; padding: 0; display: flex; flex-direction: column; max-height: calc(100vh - 40px); overflow: hidden; }
.sheet.moji-sheet .shead { display: flex; align-items: center; justify-content: space-between; padding: 16px 12px 10px 20px; }
.sheet.moji-sheet .shead h3 { margin: 0; font-size: 18px; }
.mprev { width: 148px; height: 148px; border-radius: 50%; margin: 0 auto 8px; overflow: hidden; box-shadow: inset 0 0 0 1px rgba(0,0,0,.06), 0 12px 30px rgba(0,0,0,.15); transition: background-color var(--dur-ui) var(--ease-out); }
.mprev svg { width: 100%; height: 100%; display: block; }
.mbody { overflow-y: auto; padding: 6px 16px 10px; display: flex; flex-direction: column; gap: 10px; }
.mrow .ml { font-size: 12px; font-weight: 600; color: var(--text-3); text-transform: uppercase; letter-spacing: .04em; margin: 0 2px 6px; }
.mopts { display: flex; gap: 8px; flex-wrap: wrap; }
.mopts .sw { width: 30px; height: 30px; border-radius: 50%; box-shadow: inset 0 0 0 1px rgba(0,0,0,.1); transition: transform var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out); }
.mopts .sw.on { box-shadow: 0 0 0 2px var(--card-solid), 0 0 0 4px var(--blue); transform: scale(1.05); }
.mopts .mp { height: 30px; padding: 0 12px; border-radius: 15px; background: var(--search); font-size: 13px; font-weight: 600; color: var(--text-2); transition: background-color var(--dur-ui) var(--ease-out), color var(--dur-ui) var(--ease-out), transform var(--dur-fast) var(--ease-out); }
.mopts .mp.on { background: var(--blue); color: #fff; }
.mopts .mp:active, .mopts .sw:active { transform: scale(.94); }
.macts { display: flex; gap: 10px; padding: 12px 16px 4px; }
.macts .secondary { flex: 1; } .macts .primary { flex: 1.4; }
.sheet.moji-sheet .cancel { margin-bottom: 14px; color: var(--red); }
@media (max-width: 760px) { .sheet.moji-sheet { width: 100vw; max-width: 100vw; max-height: 100vh; border-radius: 0; } }

/* Snapchat chat-list statuses */
.sc-status { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: .02em; color: var(--text-3); }
.sc-status i { width: 11px; height: 11px; border-radius: 2px; box-sizing: border-box; border: 2px solid currentColor; }
.sc-status.recv { color: #0eadff; } .sc-status.recv.new i { background: currentColor; }
.sc-status.sent { color: #f23c57; } .sc-status.sent i { border-radius: 0; border: 0; width: 0; height: 0; border-left: 10px solid currentColor; border-top: 6px solid transparent; border-bottom: 6px solid transparent; }
.sc-status.sent:not(.new) i { border-left-color: transparent; background: linear-gradient(currentColor, currentColor) left / 2px 100% no-repeat; width: 10px; height: 12px; clip-path: polygon(0 0, 100% 50%, 0 100%, 2px 100%, 2px 0); }
.sc-status:not(.new) { color: var(--text-3); }
.sc-status.fail { color: var(--red); }
.sc-status.idle { color: var(--text-3); }
[data-look="snapchat"] .row .r-preview { text-transform: none; }
[data-look="snapchat"] .row.unread .r-preview { color: inherit; }

/* live theme/look switching: a short crossfade instead of a hard cut */
html.theming, html.theming body, html.theming .shell, html.theming .sidebar, html.theming .pane, html.theming .pane-head, html.theming .composer, html.theming .bubble, html.theming .row, html.theming .rail, html.theming .sheet { transition: background-color var(--dur-sheet) var(--ease-out), color var(--dur-sheet) var(--ease-out), border-color var(--dur-sheet) var(--ease-out); }

.gsi-alt { margin-top: 6px; }
.gsi-alt a { color: var(--blue); text-decoration: none; }

/* ---- 3D avatar creator ---- */
.sheet.moji-sheet.threed { width: min(960px, 100vw - 24px); height: min(760px, 100vh - 24px); }
.sheet.moji-sheet .shead .seg.small { margin-left: auto; margin-right: 8px; }
.seg.small button { padding: 4px 12px; font-size: 12px; }
.rpm-frame { position: relative; flex: 1; margin: 0 12px 12px; border-radius: 16px; overflow: hidden; background: #0f0f14; min-height: 420px; }
.rpm-frame iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.rpm-loading { position: absolute; inset: 0; display: grid; place-items: center; color: rgba(255,255,255,.75); font-size: 14px; pointer-events: none; }
.rpm-done { display: flex; align-items: center; gap: 18px; padding: 10px 24px; flex: 1; }
.rpm-prev { width: 220px; height: 220px; border-radius: 50%; object-fit: cover; background: radial-gradient(circle at 50% 35%, #e9f2ff, #c7d6ea); box-shadow: 0 16px 40px rgba(0,0,0,.2); animation: pop var(--dur-sheet) var(--ease-out); }
.rpm-done small { color: var(--text-3); }
@media (max-width: 760px) { .sheet.moji-sheet.threed { width: 100vw; height: 100vh; border-radius: 0; } .rpm-done { flex-direction: column; text-align: center; } }

.rpm-paste { margin: 0 12px 12px; font-size: 12px; color: var(--text-3); display: flex; flex-direction: column; gap: 6px; }
.rpm-paste a { color: var(--blue); text-decoration: none; }
.rpm-paste input { height: 32px; border: 0; border-radius: 9px; padding: 0 10px; background: var(--search); font-size: 12px; min-width: 0; }

/* ---- default avatar (Snapchat look) ---- */
.avatar.default { background: #eef1f4; overflow: hidden; text-shadow: none; }
.avatar.default svg { width: 100%; height: 100%; display: block; }

/* ---- "in chat" presence + typing, anchored to the person's avatar ---- */
.presence-wrap { display: flex; flex-direction: column; gap: 6px; margin-top: 8px; align-items: flex-start; }
.presence { display: flex; align-items: flex-end; gap: 0; animation: arriveIn var(--dur-sheet) var(--ease-out) both; }
.presence .avatar { width: 22px; height: 22px; font-size: 9px; flex: none; }
.presence.typing .tb {
  position: relative; background: var(--in-bubble); border-radius: 16px; padding: 9px 11px;
  display: flex; gap: 4px; align-items: center; margin-left: 6px; box-shadow: 0 1px 2px rgba(0,0,0,.06);
}
.presence.typing .tb::before { content: ""; position: absolute; left: -5px; bottom: 2px; width: 10px; height: 10px; background: var(--in-bubble); border-radius: 50% 50% 50% 0; transform: rotate(45deg); }
.presence.typing .tb i { width: 6px; height: 6px; border-radius: 50%; background: #8e8e93; animation: tdots 1.1s infinite cubic-bezier(.45,0,.2,1); }
.presence.typing .tb i:nth-child(2) { animation-delay: .15s; }
.presence.typing .tb i:nth-child(3) { animation-delay: .3s; }
.presence.here { align-items: center; }
.presence.here .here-dot {
  width: 8px; height: 8px; border-radius: 50%; background: var(--green); margin-left: 8px;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--green) 22%, transparent);
  animation: herepulse 1.8s infinite ease-in-out;
}
@keyframes herepulse { 0%, 100% { opacity: .55; transform: scale(.85); } 50% { opacity: 1; transform: scale(1); } }
@media (prefers-reduced-motion: reduce) { .here-dot, .presence.typing .tb i { animation: none; } }

.uavail { position: absolute; right: 12px; top: 50%; transform: translateY(-50%); font-size: 12px; font-weight: 600; color: var(--text-3); pointer-events: none; }
.uavail.ok { color: var(--green); } .uavail.bad { color: var(--red); }
.stack .field { position: relative; }
#inviteLink { flex: 1; height: 34px; border: 0; border-radius: 9px; padding: 0 10px; background: var(--search); color: var(--text-2); font-size: 12px; min-width: 0; cursor: pointer; }

.pwgrid.three { grid-template-columns: 1fr auto auto; }
.pwgrid.four { grid-template-columns: 1fr auto auto auto; }

/* =========================================================================
   Motion pass (supermax) — extends the existing tokens, never forks them.
   Frequent actions stay near-imperceptible; the delight budget is spent on
   rare moments: connecting a call, ending one, a first hello.
   ========================================================================= */
:root {
  --dur-hero: 480ms;                                  /* rare-tier moments only */
  --ease-back: cubic-bezier(.34, 1.56, .64, 1);       /* easeOutBack: overshoot, momentum moments only */
}

/* ---- Occasional: opening a chat on a phone slides the thread in from the right.
   Back is deliberately instant — the list state also matches on first paint, so animating
   it would slide the whole app in on every cold load for no reason. ---- */
@media (max-width: 760px) {
  @keyframes pane-in { from { transform: translateX(6%); opacity: .6; } to { transform: none; opacity: 1; } }
  body.show-pane .pane { animation: pane-in var(--dur-sheet) var(--ease-drawer) both; }
}

/* ---- Occasional: sheets materialize. Blur and scale together read as a surface
   arriving, not a plain fade. ---- */
@keyframes materialize { from { opacity: 0; transform: scale(.96); filter: blur(6px); } to { opacity: 1; transform: none; filter: blur(0); } }
.sheet { animation: materialize var(--dur-sheet) var(--ease-out); }

/* ---- Occasional: unread count going up gets one bump (state indication). ---- */
@keyframes badge-bump { 0% { transform: scale(1); } 40% { transform: scale(1.28); } 100% { transform: scale(1); } }
.badge.bump { animation: badge-bump 220ms var(--ease-out); }

/* ---- Occasional: poll counts tick when a vote lands. ---- */
@keyframes count-pop { 0% { transform: scale(1); } 45% { transform: scale(1.35); } 100% { transform: scale(1); } }
.poll-opt .po-n { display: inline-block; animation: count-pop 240ms var(--ease-out); }

/* ---- Rare: a fresh, empty thread waves once. ---- */
@keyframes wave { 0%, 60%, 100% { transform: rotate(0); } 15% { transform: rotate(16deg); } 30% { transform: rotate(-10deg); } 45% { transform: rotate(12deg); } }
.sys .wave { display: inline-block; transform-origin: 70% 70%; animation: wave 1.3s var(--ease-in-out) 400ms 1 both; }

/* =========================================================================
   FaceTime call screen: cleaner hierarchy, labelled controls, ringing halo,
   connect burst, wind-down on end, throwable PiP.
   ========================================================================= */
/* Name + status centred at the top under a soft scrim so it reads over video. */
.call .top {
  left: 0; right: 0; top: 0; padding: calc(14px + env(safe-area-inset-top)) 72px 22px; border-radius: 0;
  background: linear-gradient(180deg, rgba(0,0,0,.55), rgba(0,0,0,0)); box-shadow: none;
  -webkit-backdrop-filter: none; backdrop-filter: none; text-align: center; pointer-events: none;
}
.call .top .n { font-size: 17px; letter-spacing: -.01em; text-shadow: 0 1px 8px rgba(0,0,0,.5); }
.call .top .s { font-size: 13px; color: rgba(255,255,255,.78); text-shadow: 0 1px 8px rgba(0,0,0,.5); }
.call .top small { font-weight: 500; color: rgba(255,255,255,.7); }
.call:not(.has-video) .top { background: none; }
.call:not(.has-video) .top .n, .call:not(.has-video) .top .s { text-shadow: none; }
.call .closex { top: calc(12px + env(safe-area-inset-top)); }

/* Audio call: the person is the focus. Bigger avatar, ringing halo around it. */
.call .center-id { gap: 10px; padding-bottom: 60px; }
.call .center-id .avatar.xl { width: 128px; height: 128px; font-size: 44px; box-shadow: 0 20px 60px rgba(0,0,0,.45), 0 0 0 4px rgba(255,255,255,.12); position: relative; z-index: 1; }
.call .center-id .n { font-size: 32px; font-weight: 700; letter-spacing: -.025em; margin-top: 6px; }
.call .center-id .s { font-size: 16px; margin-top: -4px; }
.call .halo { position: relative; width: 128px; height: 128px; display: grid; place-items: center; }
.call .halo i { position: absolute; inset: 0; border-radius: 50%; border: 1.5px solid rgba(255,255,255,.55); opacity: 0; transform: scale(1); }
/* Ringing: three rings breathe outward in sequence, someone is being reached. */
@keyframes halo-ring { 0% { transform: scale(1); opacity: .7; } 100% { transform: scale(2.1); opacity: 0; } }
.call .halo.live i { animation: halo-ring 1.8s var(--ease-out) infinite; }
.call .halo.live i:nth-child(2) { animation-delay: .6s; }
.call .halo.live i:nth-child(3) { animation-delay: 1.2s; }
/* Connected: one burst ring flies out and the avatar settles. The rare-tier moment. */
@keyframes halo-burst { 0% { transform: scale(.9); opacity: .9; } 100% { transform: scale(2.6); opacity: 0; } }
@keyframes avatar-land { 0% { transform: scale(1.08); } 100% { transform: scale(1); } }
.call .halo .burst { position: absolute; inset: 0; border-radius: 50%; border: 3px solid var(--green); animation: halo-burst var(--dur-hero) var(--ease-out) both; pointer-events: none; }
.call .center-id.active .avatar.xl { animation: avatar-land var(--dur-hero) var(--ease-back) both; }
.call .center-id.active .s { color: rgba(255,255,255,.85); }

/* Ending: the whole screen winds down instead of vanishing. */
.call.st-ended { animation: call-out 320ms var(--ease-in-out) forwards; }
@keyframes call-out { to { transform: scale(.985); filter: brightness(.6); } }
.call.st-ended .controls { opacity: 0; transform: translateX(-50%) translateY(12px); transition: opacity var(--dur-ui) var(--ease-out), transform var(--dur-ui) var(--ease-out); }

/* Controls: icon in a circle, label underneath, generous hit target. */
.call .controls { gap: 6px; padding: 10px 10px 8px; border-radius: 30px; bottom: calc(22px + env(safe-area-inset-bottom)); max-width: calc(100vw - 20px); flex-wrap: nowrap; justify-content: center; }
.call .controls button.ctl { width: auto; min-width: 64px; height: auto; border-radius: 16px; background: none; display: flex; flex-direction: column; align-items: center; gap: 5px; padding: 4px 2px 3px; transition: transform var(--dur-fast) var(--ease-out); }
.call .controls button.ctl .ic { width: 52px; height: 52px; border-radius: 50%; display: grid; place-items: center; background: rgba(255,255,255,.16); color: #fff; transition: background-color var(--dur-ui) var(--ease-out), color var(--dur-ui) var(--ease-out), transform var(--dur-fast) var(--ease-out); }
.call .controls button.ctl .ic svg { width: 24px; height: 24px; }
.call .controls button.ctl .lb { font-size: 11px; font-weight: 600; letter-spacing: .01em; color: rgba(255,255,255,.85); max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.call .controls button.ctl:active { transform: none; }
.call .controls button.ctl:active .ic { transform: scale(.92); }
.call .controls button.ctl.off .ic { background: #fff; color: #111; }
.call .controls button.ctl.live .ic { background: var(--blue); }
.call .controls button.ctl.end .ic { background: var(--red); }
.call .controls button.ctl:focus-visible .ic { outline: 2px solid #fff; outline-offset: 3px; }
/* On a phone the bar spans the screen and the controls divide it between them, so any
   number of them fits on one row without a table of per-count breakpoints. Wrapping is
   not an option: the bar is positioned from left:50%, so a wrap collapses it to half the
   viewport and leaves a ragged second row. */
@media (max-width: 760px) {
  .call .controls { gap: 0; padding: 10px 8px 8px; width: calc(100vw - 20px); }
  .call .controls button.ctl { flex: 1 1 0; min-width: 0; }
  .call .controls button.ctl .ic { width: 100%; max-width: 48px; height: auto; aspect-ratio: 1 / 1; }
  .call .controls button.ctl .lb { font-size: clamp(9px, 2.6vw, 11px); }
}

/* Group tiles arrive with a short stagger so eight people do not pop in as one wall. */
@keyframes tile-in { from { opacity: 0; transform: scale(.96); } to { opacity: 1; transform: none; } }
.call .tile { animation: tile-in var(--dur-ui) var(--ease-out) both; }
.call .tile:nth-child(2) { animation-delay: 40ms; } .call .tile:nth-child(3) { animation-delay: 80ms; } .call .tile:nth-child(4) { animation-delay: 120ms; }
.call .tile:nth-child(5) { animation-delay: 160ms; } .call .tile:nth-child(6) { animation-delay: 200ms; } .call .tile:nth-child(n+7) { animation-delay: 240ms; }

/* PiP: anchored to a corner; the drag is 1:1 and the landing is a transition from the
   live position to the new anchor. */
.call .pip { transition: transform 300ms var(--ease-drawer); border-radius: 18px; box-shadow: 0 14px 40px rgba(0,0,0,.55), 0 0 0 1px rgba(255,255,255,.28); }
.call .pip.dragging { transition: none; cursor: grabbing; box-shadow: 0 24px 60px rgba(0,0,0,.6), 0 0 0 1px rgba(255,255,255,.4); }
.call .pip[data-corner="br"] { right: 18px; bottom: calc(126px + env(safe-area-inset-bottom)); left: auto; top: auto; }
.call .pip[data-corner="bl"] { left: 18px; bottom: calc(126px + env(safe-area-inset-bottom)); right: auto; top: auto; }
.call .pip[data-corner="tr"] { right: 18px; top: calc(78px + env(safe-area-inset-top)); left: auto; bottom: auto; }
.call .pip[data-corner="tl"] { left: 18px; top: calc(78px + env(safe-area-inset-top)); right: auto; bottom: auto; }
.call .pip:active { transform: none; }

/* ---- Reduced motion & transparency: gentler, never zero. ---- */
@media (prefers-reduced-motion: reduce) {
  body.show-pane .pane { animation: fade var(--dur-ui) ease both; }
  .sheet { animation: fade var(--dur-ui) ease; transform: none !important; filter: none !important; }
  .badge.bump, .poll-opt .po-n, .sys .wave { animation: none; }
  .call .halo.live i { animation: none; opacity: .35; transform: scale(1.35); }
  .call .halo .burst { animation: fade var(--dur-ui) ease reverse both; }
  .call .center-id.active .avatar.xl { animation: none; }
  .call.st-ended { animation: none; filter: brightness(.6); }
  .call .tile { animation: fade var(--dur-ui) ease both; animation-delay: 0ms !important; }
  .call .pip { transition: none; }
}
@media (prefers-reduced-transparency: reduce) {
  .call .controls, .call-fx { background: rgba(20,20,24,.96); -webkit-backdrop-filter: none; backdrop-filter: none; }
  .glass { background: var(--card-solid); -webkit-backdrop-filter: none; backdrop-filter: none; }
}

/* Remount guards: renderOverlay() rebuilds the overlay on every state change, so
   entrance animations are skipped for surfaces that were already on screen. */
.call.remount:not(.st-ended) { animation: none; }
.call.settled.st-ended { animation-name: none; transform: scale(.985); filter: brightness(.6); }
.call.settled .halo .burst { animation: none; opacity: 0; }
.call.settled .center-id.active .avatar.xl { animation: none; }
.call .tile.remount { animation: none; }
.sheet.remount { animation: none; }

/* =========================================================================
   In-app notification banners. A message or missed call that lands while you
   are anywhere else in the app drops one of these from the top — the list is
   no longer the only screen that tells you something happened.
   ========================================================================= */
.banners {
  position: fixed; top: calc(8px + env(safe-area-inset-top)); left: 0; right: 0; z-index: 300;
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  padding: 0 10px; pointer-events: none;
}
/* An admin's full-screen announcement. Above absolutely everything — including an
   active call — since the whole point is that it can't be missed or worked around. */
.admin-overlay {
  position: fixed; inset: 0; z-index: 500; display: grid; place-items: center; padding: 20px;
  background: rgba(0,0,0,.55); -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  animation: fade var(--dur-ui) ease;
}
.admin-overlay-card { position: relative; width: min(380px, 100%); padding: 28px 24px 22px; border-radius: 24px; text-align: center; animation: pop var(--dur-sheet) var(--ease-out); }
.admin-overlay-x { position: absolute; top: 12px; right: 12px; width: 30px; height: 30px; border-radius: 50%; background: var(--search); color: var(--text-2); display: grid; place-items: center; }
.admin-overlay-x svg { width: 13px; height: 13px; }
.admin-overlay-ic { display: inline-grid; place-items: center; width: 52px; height: 52px; border-radius: 50%; background: var(--blue); color: #fff; margin-bottom: 14px; }
.admin-overlay-ic svg { width: 26px; height: 26px; }
.admin-overlay-card .n { font-size: 19px; font-weight: 700; letter-spacing: -.02em; margin-bottom: 6px; }
.admin-overlay-card p { font-size: 14.5px; color: var(--text-2); line-height: 1.4; margin: 0 0 20px; white-space: pre-wrap; }
.admin-overlay-card .primary { width: 100%; }
.admin-overlay-img { width: 100%; max-height: 220px; object-fit: cover; border-radius: 14px; margin: -4px 0 14px; display: block; }
.admin-overlay-poll { text-align: left; margin: 0 0 16px; display: flex; flex-direction: column; gap: 8px; }
.admin-overlay-poll b { font-size: 14.5px; }
.admin-overlay-poll-opt { width: 100%; height: 40px; border-radius: 11px; background: var(--field); box-shadow: inset 0 0 0 1px var(--field-border); font-size: 13.5px; font-weight: 600; text-align: center; }
.admin-overlay-poll-opt.picked { background: var(--blue); color: #fff; box-shadow: none; }
.admin-overlay-poll-status { min-height: 14px; text-align: center; }
.admin-overlay-msg { margin-top: 18px; padding-top: 16px; border-top: 1px solid var(--field-border); text-align: left; }
.admin-overlay-msg .banned-chat-list:not(:empty) { margin-bottom: 10px; }
.admin-overlay-msg textarea { width: 100%; resize: none; border: none; background: var(--field); color: var(--text); border-radius: 12px; padding: 10px 12px; font: inherit; font-size: 14px; box-shadow: inset 0 0 0 1px var(--field-border); }
.admin-overlay-msg .mini { width: 100%; height: 36px; margin-top: 8px; }
@media (prefers-reduced-motion: reduce) { .admin-overlay, .admin-overlay-card { animation: fade var(--dur-ui) ease; transform: none; } }
.nb {
  pointer-events: auto; width: min(430px, 100%); box-sizing: border-box;
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: 11px;
  padding: 10px 10px 10px 12px; border-radius: 20px; cursor: pointer; position: relative; overflow: hidden;
  /* Opaque, not glass. A banner sits over arbitrary app content — a green button, a
     photo, a video call — and it has to be readable over all of it. */
  background: var(--card-solid);
  box-shadow: 0 12px 34px rgba(0, 0, 0, .22), 0 2px 8px rgba(0, 0, 0, .1), inset 0 0 0 .5px var(--glass-line);
  animation: nb-in var(--dur-sheet) var(--ease-drawer) both;
  transition: transform var(--dur-ui) var(--ease-out), opacity var(--dur-ui) var(--ease-out);
  touch-action: none;
}
@keyframes nb-in { from { transform: translateY(-130%); opacity: 0; } to { transform: none; opacity: 1; } }
/* It came from the top, so it leaves through the top. */
.nb.out { transform: translateY(-130%); opacity: 0; }
.nb.held { transition: none; }
.nb .nb-body { min-width: 0; }
.nb .nb-t { font-size: 14px; font-weight: 650; letter-spacing: -.01em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.nb .nb-b { font-size: 13px; color: var(--text-2); line-height: 1.3; margin-top: 1px; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; word-break: break-word; }
.nb .nb-x { width: 26px; height: 26px; border-radius: 50%; display: grid; place-items: center; background: var(--search); color: var(--text-3); flex: none; }
.nb .nb-ic { width: 38px; height: 38px; border-radius: 50%; display: grid; place-items: center; color: #fff; flex: none; }
.nb .nb-ic.missed { background: var(--red); }
/* A call reads as a call before you have read a word of it: red edge, red glyph.
   The modifier is `nb-call`, not `call` — `.call` is the full-screen call surface. */
.nb.nb-call::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 4px; background: var(--red); z-index: 1; }
.nb.nb-call .nb-t { color: var(--red); }

/* =========================================================================
   Component polish
   ========================================================================= */
/* Hover states belong to real pointers. On a touch screen a tap fires :hover and
   the row stays highlighted after your finger has gone. .row's hover background is
   itself the :hover rule, so "none" is a real reset — but .nb's background is its
   OWN base style (opaque, always on), not a hover effect, so resetting it here made
   every banner transparent on touch devices. Only reset what :hover actually added. */
@media (hover: none) {
  .row:hover { background: none; }
}

/* Sheets on a phone are bottom sheets, so they get a grabber and can be thrown
   away downward. The grabber only appears when the drag is actually wired up. */
.sheet.draggable { position: relative; }
.sheet.draggable > .grabber {
  position: absolute; top: 7px; left: 50%; transform: translateX(-50%);
  width: 36px; height: 5px; border-radius: 3px; background: var(--text-3); opacity: .3; pointer-events: none;
}
.sheet.dragging { transition: none !important; }
.sheet.settling { transition: transform var(--dur-sheet) var(--ease-drawer); }

/* Toast: a pill that rises, rather than a box that appears. */
.toast { box-shadow: 0 10px 30px rgba(0, 0, 0, .2), inset 0 0 0 .5px var(--glass-line); font-weight: 550; letter-spacing: -.01em; }

/* One focus ring, everywhere, including the elements that only became focusable
   with the new components. */
button:focus-visible, input:focus-visible, textarea:focus-visible, [tabindex]:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; border-radius: 8px; }

@media (prefers-reduced-motion: reduce) {
  .nb { animation: fade var(--dur-ui) ease both; }
  .nb.out { transform: none; opacity: 0; }
  .sheet.settling { transition: opacity var(--dur-ui) ease; }
}

/* ---- Silent mode: camera only, no sound in or out ---------------------------
   The control reads like the other "off" states (white pill, dark glyph) because
   that is exactly what it is — the speaker turned off, alongside the mic. */
.call .controls button.ctl.hush .ic { background: #fff; color: #111; }
.call .top .s .hush { display: inline-flex; align-items: center; gap: 3px; margin-left: 6px; padding: 1px 7px 1px 5px; border-radius: 999px; font-size: 11px; font-weight: 700; letter-spacing: .01em; color: #fff; background: rgba(255, 255, 255, .22); vertical-align: 1px; }
.call:not(.has-video) .top .s .hush { color: var(--text); background: var(--search); }
.call .top .s .hush svg { display: block; }

/* ---- camera presence: someone has the camera open in this chat ---------------
   Shares the typing row's layout; a badge instead of dots. */
.presence.cam { align-items: center; }
.presence .cam-badge {
  display: grid; place-items: center; width: 22px; height: 22px; margin-left: -6px; border-radius: 50%;
  background: var(--blue); color: #fff; box-shadow: 0 0 0 2px var(--bg);
}
.presence .cam-badge svg { width: 13px; height: 13px; }

/* ---- Bitmoji scan sheet ---- */
.sheet.moji-scan .moji-stage img.cut {
  /* A cutout has no background of its own, so give it one that shows the edges. */
  background: repeating-conic-gradient(var(--search) 0% 25%, transparent 0% 50%) 50% / 18px 18px;
  object-fit: contain;
}
.sheet.moji-scan .moji-ai {
  display: block; width: 100%; margin-top: 10px; padding: 9px; border-radius: 12px;
  background: transparent; color: var(--text-2); font-size: 13px; font-weight: 600;
  box-shadow: inset 0 0 0 1px var(--field-border);
}
/* A thread whose messages failed to load says so, with a way out. */
.sys.load-err { display: flex; flex-direction: column; align-items: center; gap: 10px; color: var(--red); }
.sys.load-err .mini { background: var(--field); color: var(--text); box-shadow: inset 0 0 0 1px var(--field-border); }

/* =========================================================================
   Minimised call. The full-screen surface shrinks to a bar so the chat
   underneath stays readable and usable — the media elements are still in the
   DOM behind it, so nothing about the call is interrupted.
   ========================================================================= */
.call.mini {
  inset: auto 0 auto 0; top: 0; height: auto; background: none; overflow: visible;
  pointer-events: none; /* the app underneath is live again; only the bar takes taps */
  animation: none;
}
.call.mini .mini-bar {
  pointer-events: auto; margin: calc(8px + env(safe-area-inset-top)) auto 0; width: min(430px, calc(100% - 20px));
  box-sizing: border-box; display: grid; grid-template-columns: minmax(0, 1fr) auto auto; align-items: center; gap: 8px;
  padding: 7px 8px 7px 8px; border-radius: 18px; color: #fff;
  background: rgba(20, 20, 24, .82); -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur);
  box-shadow: 0 12px 34px rgba(0, 0, 0, .3), inset 0 0 0 .5px rgba(255, 255, 255, .16);
  animation: mb-in var(--dur-sheet) var(--ease-drawer) both;
}
@keyframes mb-in { from { transform: translateY(-130%); opacity: 0; } to { transform: none; opacity: 1; } }
.call.mini .mb-open {
  display: flex; align-items: center; gap: 9px; min-width: 0; color: inherit; background: none; padding: 0; text-align: left;
}
.call.mini .mb-vid { width: 42px; height: 34px; border-radius: 9px; object-fit: cover; background: #222; flex: none; }
.call.mini .mb-dot { width: 10px; height: 10px; border-radius: 50%; background: var(--green); flex: none; margin: 0 4px; animation: ringpulse 1.8s infinite; }
.call.mini .mb-txt { display: flex; flex-direction: column; min-width: 0; line-height: 1.25; }
.call.mini .mb-txt b { font-size: 14px; font-weight: 650; letter-spacing: -.01em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.call.mini .mb-txt i { font-size: 12px; font-style: normal; color: rgba(255, 255, 255, .72); font-variant-numeric: tabular-nums; }
.call.mini .mb-btn {
  width: 38px; height: 38px; border-radius: 50%; display: grid; place-items: center; flex: none;
  background: rgba(255, 255, 255, .16); color: #fff; transition: transform var(--dur-fast) var(--ease-out), background-color var(--dur-ui) var(--ease-out);
}
.call.mini .mb-btn svg { width: 18px; height: 18px; }
.call.mini .mb-btn:active { transform: scale(.92); }
.call.mini .mb-btn.off { background: #fff; color: #111; }
.call.mini .mb-btn.end { background: var(--red); }
.call.mini .mb-btn:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }

/* The button that shrinks the call, next to the close button. */
.call .minx {
  position: absolute; top: calc(12px + env(safe-area-inset-top)); left: 18px; z-index: 3;
  width: 40px; height: 40px; border-radius: 50%; background: rgba(255, 255, 255, .14); color: #fff;
  display: grid; place-items: center;
}
.call .minx svg { width: 20px; height: 20px; }
.call .minx:active { transform: scale(.94); }

/* Add-person button — same spot the close button uses, since the two never show
   at once (close only appears for states addx is hidden for: ended/waiting/etc). */
.call .addx {
  position: absolute; top: calc(12px + env(safe-area-inset-top)); right: 18px; z-index: 3;
  width: 40px; height: 40px; border-radius: 50%; background: rgba(255, 255, 255, .14); color: #fff;
  display: grid; place-items: center;
}
.call .addx svg { width: 20px; height: 20px; }
.call .addx:active { transform: scale(.94); }

/* Live captions toggle — sits next to minimise since top-right is already close/add. */
.call .ccx {
  position: absolute; top: calc(12px + env(safe-area-inset-top)); left: 66px; z-index: 3;
  width: 40px; height: 40px; border-radius: 50%; background: rgba(255, 255, 255, .14); color: #fff;
  display: grid; place-items: center; transition: background-color var(--dur-ui, .18s) var(--ease-out, ease);
}
.call .ccx.on { background: #fff; color: #111; }
.call .ccx svg { width: 20px; height: 20px; }
.call .ccx:active { transform: scale(.94); }

/* Captions text, above the controls bar. */
.call .cc-bar {
  position: absolute; left: 16px; right: 16px; bottom: 128px; z-index: 3; text-align: center;
  color: #fff; font-size: 15px; font-weight: 500; line-height: 1.35; max-height: 86px; overflow: hidden;
  padding: 8px 14px; border-radius: 14px; background: rgba(0,0,0,.55);
  -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px);
  animation: fade var(--dur-ui, .2s) ease both;
}
.call .cc-bar div + div { margin-top: 3px; }
.call .cc-bar b { font-weight: 700; opacity: .8; margin-right: 5px; }

@media (prefers-reduced-motion: reduce) {
  .call.mini .mini-bar { animation: fade var(--dur-ui) ease both; }
  .call.mini .mb-dot { animation: none; }
}

/* While the call is minimised the bar floats, centered, over the top of the whole
   window — not just the mobile single-column layout — so push every header under it
   at every width. (Previously the .pane-head/.thread push was scoped to <=760px only,
   which left the bar sitting on top of the conversation header on desktop, since the
   centered bar falls within the wide .pane column at any desktop window size.) */
body.call-mini .sb-top { padding-top: calc(66px + env(safe-area-inset-top)); }
body.call-mini .pane-head { top: calc(64px + env(safe-area-inset-top)); }
body.call-mini .thread { padding-top: 116px; }
/* Settings → Calls → Show call duration, off. visibility over display:none so the
   surrounding status text doesn't jump when the timer would otherwise appear. */
body.hide-call-timer #callTimer { visibility: hidden; }

/* =========================================================================
   STORIES
   ========================================================================= */
:root {
  --sb-sunset: linear-gradient(160deg, #ff9a56 0%, #ff6b81 100%);
  --sb-ocean: linear-gradient(160deg, #36d1dc 0%, #5b86e5 100%);
  --sb-grape: linear-gradient(160deg, #a18cd1 0%, #6a4cff 100%);
  --sb-forest: linear-gradient(160deg, #56ab2f 0%, #1f6f3f 100%);
  --sb-midnight: linear-gradient(160deg, #3a3f6e 0%, #0f1123 100%);
  --sb-flame: linear-gradient(160deg, #f7971e 0%, #d7263d 100%);
}
.bg-sunset { background: var(--sb-sunset); }
.bg-ocean { background: var(--sb-ocean); }
.bg-grape { background: var(--sb-grape); }
.bg-forest { background: var(--sb-forest); }
.bg-midnight { background: var(--sb-midnight); }
.bg-flame { background: var(--sb-flame); }

/* ---- sidebar rows: the ring around an avatar with an active story --------- */
/* .row's grid reserves a 46px avatar column; the ring is deliberately bigger
   than the avatar it wraps, so this row alone needs a wider middle column. */
.row.story-row { grid-template-columns: 14px 52px 1fr; cursor: pointer; }
.story-ring { position: relative; width: 52px; height: 52px; border-radius: 50%; display: grid; place-items: center; flex: none; }
/* Blue-toned ring for an unseen story — no red in it, so it never reads as an
   error/alert state the way a red ring would. Seen stays a plain grey. */
.story-ring.unseen { background: conic-gradient(from 45deg, #34c3ff, #0a84ff, #6a4cff, #34c3ff); }
.story-ring.seen { background: var(--field-border); }
.story-ring .avatar { width: 42px; height: 42px; box-shadow: 0 0 0 2.5px var(--sidebar); }
/* The Messages list's own row grid reserves exactly 46px for an avatar (shared by
   every row type — widening it would misalign every other row), so a ring wrapped
   around one of those avatars has to fit inside that same 46px, not the roomier 52px
   Stories' own sidebar uses. */
.row-story-ring { width: 46px; height: 46px; cursor: pointer; }
.row-story-ring .avatar { width: 36px; height: 36px; box-shadow: 0 0 0 2px var(--sidebar); }
/* The chat header's own avatar is "sm" (32px) everywhere else in the app — the ring
   wraps it the same way, just at that smaller scale. */
.story-ring-sm { width: 40px; height: 40px; cursor: pointer; }
.story-ring-sm .avatar { width: 32px; height: 32px; box-shadow: 0 0 0 2px var(--sidebar); }
.story-row.active .story-ring .avatar, .row.active .story-ring .avatar { box-shadow: 0 0 0 2.5px var(--blue); }
.story-plus { position: absolute; right: -1px; bottom: -1px; width: 18px; height: 18px; border-radius: 50%; background: var(--blue); color: #fff; display: grid; place-items: center; box-shadow: 0 0 0 2px var(--sidebar); }
.story-plus svg { width: 10px; height: 10px; }

/* ---- "Your Story" management pane ------------------------------------- */
/* .pane-head floats absolutely over the pane (same as every other pane), so this
   needs top padding to clear it — the exact bug class already fixed once for
   .thread vs. the composer; missed it here on a brand-new pane. */
.story-manage-list { padding: 80px 14px 20px; display: flex; flex-direction: column; gap: 8px; }
.story-manage-row { display: flex; align-items: center; gap: 12px; padding: 8px; border-radius: 14px; background: var(--search); }
/* Plain .story-thumb (no .bg-* class: image/video/sticker kinds) gets a neutral
   fallback fill; a text/announcement thumb always carries a .bg-* class too, and
   that needs to win over this rather than lose to it on source order, hence the
   doubled-up selector instead of relying on .bg-* alone. */
.story-thumb { width: 46px; height: 62px; border-radius: 10px; overflow: hidden; flex: none; position: relative; display: grid; place-items: center; background: var(--field); color: #fff; }
.story-thumb.bg-sunset { background: var(--sb-sunset); }
.story-thumb.bg-ocean { background: var(--sb-ocean); }
.story-thumb.bg-grape { background: var(--sb-grape); }
.story-thumb.bg-forest { background: var(--sb-forest); }
.story-thumb.bg-midnight { background: var(--sb-midnight); }
.story-thumb.bg-flame { background: var(--sb-flame); }
.story-thumb img { width: 100%; height: 100%; object-fit: cover; }
.story-thumb.sticker { font-size: 22px; background: var(--field); color: var(--text); }
.story-thumb svg { width: 18px; height: 18px; }
/* Video posters keep the source video's own aspect ratio, which is frequently
   landscape — cover-fit into this portrait box would crop off whatever was at the
   edges, so this one shows the whole frame on a plain dark backdrop instead. */
.story-thumb.video-thumb { background: #000; }
.story-thumb.video-thumb img { object-fit: contain; }
.story-thumb-play { position: absolute; inset: 0; display: grid; place-items: center; color: #fff; filter: drop-shadow(0 1px 2px rgba(0,0,0,.6)); }
.story-thumb-play svg { width: 16px; height: 16px; }
.admin-story-grid { display: flex; flex-direction: column; gap: 6px; }
.admin-story-card { display: flex; align-items: center; gap: 12px; padding: 8px; border-radius: 12px; background: var(--search); }
.admin-story-card.removed { opacity: .55; }
.admin-story-thumb { width: 40px; height: 54px; }
.admin-story-meta { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.admin-story-meta b { font-size: 13.5px; }
.admin-story-acts { display: flex; flex-direction: column; gap: 4px; flex: none; }
.story-eye-ic { display: inline-grid; vertical-align: -2px; }
.story-eye-ic svg { width: 13px; height: 13px; }

/* ---- composer sheet ----------------------------------------------------- */
.sheet.story-composer { position: relative; padding-top: 46px; }
.story-composer .sc-back { position: absolute; top: 14px; left: 14px; width: 30px; height: 30px; border-radius: 50%; display: grid; place-items: center; background: var(--search); color: var(--text-2); }
.story-composer .sc-back svg { width: 16px; height: 16px; }
.story-composer h3 { text-align: center; font-size: 17px; margin: 0 0 14px; }
.sc-pick-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
.sc-pick { display: flex; flex-direction: column; align-items: center; gap: 8px; padding: 16px 6px; border-radius: 16px; background: var(--field); box-shadow: inset 0 0 0 1px var(--field-border); font-size: 12.5px; font-weight: 600; color: var(--text); }
.sc-pick-ic { width: 30px; height: 30px; color: var(--text-2); }
.sc-pick-ic svg { width: 100%; height: 100%; }
.sc-body { margin: 0 0 10px; }
.sc-preview-img, .sc-preview-vid { width: 100%; max-height: 320px; border-radius: 14px; object-fit: contain; background: #000; display: block; }
.story-composer input, .story-composer textarea, .sc-poll-opt-input { width: 100%; border: none; background: var(--field); color: var(--text); border-radius: 12px; padding: 10px 12px; font: inherit; font-size: 14px; box-shadow: inset 0 0 0 1px var(--field-border); margin-bottom: 8px; }
.sc-text-card { border-radius: 16px; min-height: 220px; display: flex; align-items: center; justify-content: center; padding: 24px; margin-bottom: 10px; }
.sc-text-card textarea { background: transparent; box-shadow: none; color: #fff; text-align: center; font-size: 20px; font-weight: 650; resize: none; min-height: 140px; margin: 0; text-shadow: 0 1px 4px rgba(0,0,0,.25); }
.sc-text-card textarea::placeholder { color: rgba(255,255,255,.75); }
.sc-bg-row { display: flex; gap: 8px; justify-content: center; margin-bottom: 10px; }
.sc-bg-sw { width: 28px; height: 28px; border-radius: 50%; box-shadow: inset 0 0 0 2px rgba(255,255,255,.5); }
.sc-bg-sw.on { box-shadow: inset 0 0 0 2px #fff, 0 0 0 2px var(--blue); }
.sc-poll-opts { display: flex; flex-direction: column; }
.sc-poll-opt-input { margin-bottom: 6px; }
.sc-sticker-preview { display: grid; place-items: center; min-height: 220px; border-radius: 16px; background: var(--search); margin-bottom: 10px; }
.sc-sticker-preview span { font-size: 84px; }
.sc-sticker-preview img { max-width: 200px; max-height: 200px; }
.sc-sticker-picker .tabs { display: flex; gap: 4px; overflow-x: auto; margin-bottom: 8px; }
.sc-sticker-picker .tabs button { padding: 6px 12px; border-radius: 12px; background: var(--search); color: var(--text-2); font-size: 12.5px; font-weight: 600; white-space: nowrap; }
.sc-sticker-picker .tabs button.on { background: var(--blue); color: #fff; }
.sc-sticker-picker .grid { display: grid; grid-template-columns: repeat(6, 1fr); gap: 4px; max-height: 220px; overflow-y: auto; }
.sc-sticker-picker .grid button { font-size: 26px; padding: 6px; border-radius: 10px; }
.sc-sticker-picker .grid button:active { background: var(--search); }
.story-composer .primary { width: 100%; }

/* ---- Snapchat-style photo/video story editor ----
   Full-bleed camera-roll chrome for the two "capture" kinds — everything else
   (text card, poll, sticker-only, announcement) keeps the plain centered sheet
   above. No export/multi-destination step on purpose — just one Post button. */
.sheet-wrap.cam { background: #000; -webkit-backdrop-filter: none; backdrop-filter: none; }
.sheet.sc-cam { width: 100vw; max-width: 100vw; height: var(--app-vh, 100vh); max-height: var(--app-vh, 100vh); border-radius: 0; padding: 0; background: #000; display: flex; flex-direction: column; overflow: hidden; animation: fade var(--dur-ui, .15s) ease; }
.sc-cam-frame { position: relative; flex: 1; min-height: 0; display: flex; align-items: center; justify-content: center; background: #000; }
.sc-cam-media { position: relative; width: 100%; height: 100%; max-width: calc((100vh - 96px) * 9 / 16); aspect-ratio: 9 / 16; margin: 0 auto; overflow: hidden; background: #111; border-radius: 0; }
@media (min-width: 500px) { .sc-cam-media { border-radius: 18px; margin: 12px auto; height: calc(100% - 24px); } }
.sc-cam-media-el { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.sc-cam-loading { position: absolute; inset: 0; display: grid; place-items: center; color: rgba(255,255,255,.6); font-size: 13px; }
.sc-draw-canvas { position: absolute; inset: 0; width: 100%; height: 100%; touch-action: none; pointer-events: none; }
.sc-draw-canvas.active { pointer-events: auto; }
.sc-overlay-sticker { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); font-size: 84px; line-height: 1; background: none; filter: drop-shadow(0 2px 8px rgba(0,0,0,.35)); }
.sc-text-band { position: absolute; left: 0; right: 0; top: 22%; background: rgba(0,0,0,.55); padding: 10px 16px; }
.sc-text-band input { width: 100%; background: transparent; border: 0; text-align: center; font-size: 19px; font-weight: 700; font-style: italic; text-shadow: 0 1px 3px rgba(0,0,0,.4); }
.sc-text-band input::placeholder { color: rgba(255,255,255,.6); }
.sc-cam-done { position: absolute; top: calc(14px + env(safe-area-inset-top)); left: 14px; z-index: 3; display: flex; align-items: center; gap: 4px; padding: 7px 12px 7px 8px; border-radius: 20px; background: rgba(0,0,0,.4); color: #fff; font-size: 13.5px; font-weight: 600; }
.sc-cam-done svg { width: 16px; height: 16px; }
.sc-cam-rail { position: absolute; top: calc(18px + env(safe-area-inset-top)); right: 12px; z-index: 3; display: flex; flex-direction: column; gap: 14px; }
.sc-rail-btn { width: 38px; height: 38px; border-radius: 50%; display: grid; place-items: center; background: rgba(0,0,0,.4); color: #fff; }
.sc-rail-btn svg { width: 19px; height: 19px; }
.sc-rail-btn.on { background: var(--blue); }
.sc-draw-bar { position: absolute; left: 0; right: 0; bottom: 16px; z-index: 3; display: flex; align-items: center; gap: 10px; padding: 0 14px; }
.sc-draw-colors { display: flex; gap: 8px; flex: 1; overflow-x: auto; }
.sc-draw-sw { width: 26px; height: 26px; border-radius: 50%; flex: none; box-shadow: inset 0 0 0 1px rgba(255,255,255,.4); }
.sc-draw-sw.on { box-shadow: 0 0 0 2px #fff, 0 0 0 4px var(--blue); }
.sc-draw-bar .mini { flex: none; }
.sc-sticker-sheet.sc-sticker-picker { position: absolute; left: 10px; right: 10px; bottom: 14px; z-index: 4; background: rgba(20,20,20,.92); border-radius: 16px; padding: 10px; animation: rise var(--dur-sheet, .26s) var(--ease-out); }
.sc-sticker-sheet .tabs button { color: rgba(255,255,255,.7); }
.sc-sticker-sheet .tabs button.on { background: var(--blue); color: #fff; }
.sc-cam-bottom { flex: none; padding: 12px 16px calc(14px + env(safe-area-inset-bottom)); background: #000; }
.sc-post-btn { width: 100%; height: 46px; border-radius: 23px; background: var(--blue); color: #fff; font-size: 15.5px; font-weight: 700; }
.sc-post-btn:disabled { opacity: .6; }
@media (prefers-reduced-motion: reduce) { .sheet.sc-cam, .sc-sticker-sheet.sc-sticker-picker { animation: none; } }
.sc-music-picker { display: flex; flex-direction: column; gap: 6px; }
.sc-cam-frame .sc-music-picker { position: absolute; left: 10px; right: 10px; bottom: 14px; z-index: 4; background: rgba(20,20,20,.92); border-radius: 16px; padding: 10px; animation: rise var(--dur-sheet, .26s) var(--ease-out); }
.sc-music-opt { width: 100%; height: 38px; border-radius: 10px; background: rgba(255,255,255,.1); color: #fff; font-size: 13.5px; font-weight: 600; text-align: left; padding: 0 12px; }
.sc-music-opt.on { background: var(--blue); }
.sc-music-toggle { width: 100%; justify-content: flex-start; margin: 4px 0; }
.sc-music-toggle.on { color: var(--blue); }
@media (prefers-reduced-motion: reduce) { .sc-cam-frame .sc-music-picker { animation: none; } }

/* ---- full-screen viewer --------------------------------------------------
   No CSS-only entrance animation: this surface re-renders on every like, vote
   and reply while it's open, and renderOverlay() doesn't remount-track
   .story-viewer the way it does .call/.sheet — a replaying animation on every
   interaction would be far more distracting than a plain instant swap. The
   one true "opening" moment instead gets a one-shot .sv-enter class from JS
   (storyViewerHTML's `entering` flag, cleared right after use). */
.story-viewer { position: fixed; inset: 0; z-index: 120; background: #000; display: flex; flex-direction: column; color: #fff; }
.story-viewer.sv-enter { animation: svEnter 260ms var(--ease-out); }
@keyframes svEnter { from { opacity: 0; transform: scale(.96); } to { opacity: 1; transform: scale(1); } }
@media (prefers-reduced-motion: reduce) { .story-viewer.sv-enter { animation: svEnterReduced 200ms ease; } }
@keyframes svEnterReduced { from { opacity: 0; } to { opacity: 1; } }
.sv-bars { display: flex; gap: 4px; padding: calc(10px + env(safe-area-inset-top)) 12px 0; }
.sv-bar { flex: 1; height: 2.5px; border-radius: 2px; background: rgba(255,255,255,.3); overflow: hidden; }
.sv-bar.done { background: #fff; }
.sv-bar i { display: block; height: 100%; width: 0%; background: #fff; animation-name: sv-fill; animation-timing-function: linear; animation-fill-mode: forwards; }
@keyframes sv-fill { from { width: 0%; } to { width: 100%; } }
.sv-head { display: flex; align-items: center; gap: 10px; padding: 10px 14px; }
.sv-head .avatar { width: 32px; height: 32px; }
.sv-who { flex: 1; min-width: 0; display: flex; flex-direction: column; line-height: 1.25; }
.sv-who b { font-size: 14px; font-weight: 650; }
.sv-who span { font-size: 11.5px; color: rgba(255,255,255,.65); }
.sv-head .icon-btn { color: #fff; }
.sv-body { flex: 1; min-height: 0; position: relative; display: grid; place-items: center; overflow: hidden; }
.sv-body img, .sv-body video { max-width: 100%; max-height: 100%; width: 100%; height: 100%; object-fit: contain; }
.sv-caption { position: absolute; left: 0; right: 0; bottom: 90px; text-align: center; padding: 0 20px; font-size: 15px; font-weight: 600; text-shadow: 0 1px 6px rgba(0,0,0,.6); }
.sv-music-btn.off { opacity: .4; }
.sv-broken { display: flex; flex-direction: column; align-items: center; gap: 10px; color: rgba(255,255,255,.55); padding: 0 30px; text-align: center; }
.sv-broken svg { width: 34px; height: 34px; opacity: .6; }
.sv-broken p { font-size: 14px; }
.sv-sticker-bg { width: 100%; height: 100%; display: grid; place-items: center; background: linear-gradient(160deg, #232946, #0f1123); }
.sv-sticker-bg img { width: auto; height: auto; max-width: 60%; max-height: 60%; }
.sv-emoji { font-size: 120px; }
.sv-text-card { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; padding: 40px; text-align: center; }
.sv-text-card p { font-size: 26px; font-weight: 700; line-height: 1.3; text-shadow: 0 1px 6px rgba(0,0,0,.25); white-space: pre-wrap; }
.sv-ann-tag { position: absolute; top: 60px; left: 50%; transform: translateX(-50%); display: flex; align-items: center; gap: 6px; padding: 6px 14px; border-radius: 20px; background: rgba(0,0,0,.35); font-size: 12.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; }
.sv-ann-tag svg { width: 14px; height: 14px; }
.sv-poll-card { width: 100%; max-width: 340px; padding: 0 20px; }
.sv-poll-card h3 { font-size: 19px; font-weight: 700; text-align: center; margin: 0 0 16px; text-shadow: 0 1px 6px rgba(0,0,0,.4); }
.sv-poll-opt { position: relative; width: 100%; height: 46px; border-radius: 23px; background: rgba(255,255,255,.16); margin-bottom: 10px; overflow: hidden; display: flex; align-items: center; padding: 0 16px; font-weight: 600; font-size: 14px; }
.sv-poll-opt:disabled { opacity: 1; }
.sv-poll-fill { position: absolute; left: 0; top: 0; bottom: 0; background: rgba(255,255,255,.32); transition: width var(--dur-sheet) var(--ease-out); }
.sv-poll-label { position: relative; flex: 1; text-align: left; }
.sv-poll-pct { position: relative; font-weight: 700; }
.sv-poll-opt.picked { box-shadow: inset 0 0 0 2px #fff; }
.sv-zone { position: absolute; top: 60px; bottom: 90px; width: 35%; }
.sv-left { left: 0; }
.sv-right { right: 0; }
.sv-foot { display: flex; align-items: center; gap: 10px; padding: 10px 14px calc(14px + env(safe-area-inset-bottom)); }
.sv-foot input { flex: 1; height: 40px; border-radius: 20px; border: 1px solid rgba(255,255,255,.35); background: rgba(255,255,255,.1); color: #fff; padding: 0 16px; font-size: 14px; }
.sv-foot input::placeholder { color: rgba(255,255,255,.65); }
.sv-like, .sv-send { width: 40px; height: 40px; border-radius: 50%; display: grid; place-items: center; color: #fff; flex: none; }
.sv-like svg, .sv-send svg { width: 20px; height: 20px; }
.sv-like.on { color: var(--red); }
.sv-send { background: var(--blue); }
.sv-foot.sv-mine { justify-content: center; }
.sv-foot.sv-mine .mini { color: #fff; }
.sv-foot.sv-mine .mini.ghost { background: rgba(255,255,255,.14); box-shadow: none; }
.sv-foot.sv-mine .mini.ghost.danger { background: rgba(255,255,255,.14); color: #ff8a80; }
.story-viewers-sheet { text-align: left; }
.story-viewers-list { display: flex; flex-direction: column; gap: 2px; max-height: 320px; overflow-y: auto; width: 100%; }
/* This sheet opens ON TOP of the full-screen story viewer (.story-viewer, z-index
   120) — the generic .sheet-wrap's z-index of 60 sat behind it, so tapping "viewed"
   fetched the list and rendered it, just invisibly, behind an opaque black screen. */
#storyViewersWrap { z-index: 130; }

/* ---- a story reply, in the ordinary message thread ----------------------- */
.bubble.story-reply { display: flex; flex-direction: column; gap: 4px; }
.sr-tag { display: flex; align-items: center; gap: 5px; font-size: 11.5px; opacity: .75; }
.sr-tag svg { width: 13px; height: 13px; }
.sr-tag i { font-style: normal; font-weight: 600; }
.sr-img { width: 100%; max-width: 180px; max-height: 240px; object-fit: cover; border-radius: 12px; cursor: pointer; display: block; }

/* ========================================================================
   Beam 2.0 — Liquid Glass layer
   An ENHANCE pass over the existing token system, not a fork of it: the same
   variables every component already reads are re-tuned here (deeper, more
   saturated glass; a bright specular top edge; layered soft shadows; larger
   continuous-looking radii), then the handful of chrome surfaces that define
   the feel — tab bar, chat header, composer, sheets, bubbles, rows — get a
   targeted restyle. Motion stays on budget: this is an app people open a
   hundred times a day, so the spring lives on occasional surfaces (sheets,
   tab changes, presses), never on per-message work.
   ======================================================================== */
:root {
  --glass: rgba(255,255,255,.64);
  --glass-2: rgba(255,255,255,.48);
  --glass-edge: rgba(255,255,255,.96);
  --glass-line: rgba(20,30,60,.06);
  --glass-shadow: 0 18px 50px rgba(20,30,60,.13), 0 2px 6px rgba(20,30,60,.05);
  --glass-blur: blur(36px) saturate(220%);
  --window: #eef0f6;
  --sidebar: rgba(243,245,250,.72);
  --search: rgba(120,120,128,.10);
  --row-hover: rgba(20,30,60,.05);
  --card-shadow: 0 30px 80px rgba(20,30,60,.18), 0 0 0 .5px rgba(20,30,60,.06);
  --chat-tint: radial-gradient(120% 80% at 100% 0%, rgba(11,132,254,.09), transparent 60%),
               radial-gradient(90% 70% at 0% 100%, rgba(175,82,222,.06), transparent 60%);
  --bubble-radius: 20px;
  --ease-spring: cubic-bezier(.22, 1.2, .36, 1);   /* light overshoot: tab pill, presses */
}
@media (prefers-color-scheme: dark) {
  html:not([data-theme="light"]) {
    --glass: rgba(34,34,42,.58);
    --glass-2: rgba(34,34,42,.42);
    --glass-edge: rgba(255,255,255,.18);
    --glass-line: rgba(255,255,255,.07);
    --glass-shadow: 0 18px 50px rgba(0,0,0,.5), 0 2px 6px rgba(0,0,0,.4);
    --window: #0b0b0e;
    --sidebar: rgba(24,24,28,.72);
    --search: rgba(120,120,128,.22);
    --row-hover: rgba(255,255,255,.06);
    --chat-tint: radial-gradient(120% 80% at 100% 0%, rgba(11,132,254,.14), transparent 60%),
                 radial-gradient(90% 70% at 0% 100%, rgba(175,82,222,.10), transparent 60%);
  }
}
html[data-theme="dark"] {
  --glass: rgba(34,34,42,.58); --glass-2: rgba(34,34,42,.42); --glass-edge: rgba(255,255,255,.18); --glass-line: rgba(255,255,255,.07);
  --glass-shadow: 0 18px 50px rgba(0,0,0,.5), 0 2px 6px rgba(0,0,0,.4); --window: #0b0b0e; --sidebar: rgba(24,24,28,.72);
  --search: rgba(120,120,128,.22); --row-hover: rgba(255,255,255,.06);
  --chat-tint: radial-gradient(120% 80% at 100% 0%, rgba(11,132,254,.14), transparent 60%), radial-gradient(90% 70% at 0% 100%, rgba(175,82,222,.10), transparent 60%);
}

/* Ambient depth behind the translucent chrome — two soft colour fields the
   sidebar and tab bar refract, so glass has something to be glass over. */
.shell {
  background:
    radial-gradient(55% 45% at 8% 0%, rgba(11,132,254,.13), transparent 62%),
    radial-gradient(50% 40% at 100% 100%, rgba(175,82,222,.10), transparent 60%),
    radial-gradient(40% 35% at 60% 110%, rgba(52,199,89,.07), transparent 60%),
    var(--window);
}

/* Glass gets a stronger specular edge and a faint inner refraction line. */
.glass { box-shadow: inset 0 1px 0 var(--glass-edge), inset 0 0 0 .5px var(--glass-line), inset 0 -1px 0 rgba(255,255,255,.12), var(--glass-shadow); }

/* Desktop rail: a glass column with rounder, quieter tiles; the active tile
   gets a soft pill behind it instead of a 4px dot. */
.rail { border-right: 0; background: var(--glass-2); -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur); box-shadow: inset -1px 0 0 var(--glass-line); gap: 12px; }
.dock-icon { border-radius: 15px; transition: transform var(--dur-ui) var(--ease-spring), filter var(--dur-ui) var(--ease-out); }
.dock-icon::after { display: none; }
.dock-icon.active { box-shadow: 0 0 0 3px var(--glass-2), 0 0 0 4px var(--glass-line), 0 6px 18px rgba(20,30,60,.16); }
.dock-icon.games { background: linear-gradient(180deg, #b48cff, #6e3ff0); box-shadow: 0 4px 12px rgba(110,63,240,.3), inset 0 1px 0 rgba(255,255,255,.45); }
.dock-icon.games.active { box-shadow: 0 4px 12px rgba(110,63,240,.3), inset 0 1px 0 rgba(255,255,255,.45), 0 0 0 3px var(--glass-2), 0 0 0 4px var(--glass-line); }
.dock-icon.games svg { color: #fff; }
.rail .me { box-shadow: 0 0 0 2px var(--glass-2), 0 0 0 3px var(--glass-line); }

/* Sidebar: no hard border, rounder rows, a tinted glass active row. */
.sidebar { border-right: 0; box-shadow: inset -1px 0 0 var(--glass-line); }
.sb-top h2 { font-size: 26px; letter-spacing: -.03em; font-weight: 800; }
.search { height: 36px; border-radius: 13px; }
.chip-filters button { border-radius: 12px; padding: 6px 13px; transition: background-color var(--dur-ui) var(--ease-out), color var(--dur-ui) var(--ease-out), transform var(--dur-fast) var(--ease-spring); }
.chip-filters button:active { transform: scale(.94); }
.row { border-radius: 16px; transition: background-color var(--dur-ui) var(--ease-out), transform var(--dur-fast) var(--ease-spring); }
.row.active { background: linear-gradient(180deg, color-mix(in srgb, var(--blue) 90%, #fff) 0%, var(--blue) 100%); box-shadow: inset 0 1px 0 rgba(255,255,255,.35), 0 8px 22px rgba(11,132,254,.28); }
.avatar { box-shadow: inset 0 1px 0 rgba(255,255,255,.4), 0 0 0 .5px rgba(20,30,60,.08), 0 2px 6px rgba(20,30,60,.08); }

/* Chat header + composer: rounder capsules, floating a touch higher. */
.pane-head { border-radius: 22px; height: 56px; }
.composer { border-radius: 30px; padding: 7px 7px 7px 9px; }
.field-wrap { border-radius: 22px; box-shadow: inset 0 0 0 1px var(--field-border), inset 0 1px 0 rgba(255,255,255,.5); }
.composer .plus { width: 36px; height: 36px; transition: transform var(--dur-fast) var(--ease-spring), background-color var(--dur-ui) var(--ease-out); }
.send { box-shadow: 0 4px 12px rgba(11,132,254,.38), inset 0 1px 0 rgba(255,255,255,.3); }
.jump-down { border-radius: 50%; }

/* Bubbles: a hair rounder, and outgoing ones carry a subtle glass gradient with
   a lit top edge. The gradient ends on the flat --out-bubble colour so the
   CSS tail (which is drawn in that flat colour) still joins seamlessly. */
.msg.out .bubble { background: linear-gradient(180deg, color-mix(in srgb, var(--out-bubble) 86%, #fff) 0%, var(--out-bubble) 55%, var(--out-bubble) 100%); box-shadow: inset 0 1px 0 rgba(255,255,255,.26), 0 1px 2px rgba(20,30,60,.06); }
.msg.in .bubble { box-shadow: inset 0 1px 0 rgba(255,255,255,.55), 0 1px 2px rgba(20,30,60,.05); }
@media (prefers-color-scheme: dark) { html:not([data-theme="light"]) .msg.in .bubble { box-shadow: inset 0 1px 0 rgba(255,255,255,.07); } }
html[data-theme="dark"] .msg.in .bubble { box-shadow: inset 0 1px 0 rgba(255,255,255,.07); }
.bubble.img img { border-radius: 20px; }

/* Sheets and cards: bigger radii, a spring on the way in (the existing
   .sheet.remount guard still cancels this on re-render, so nothing strobes). */
.sheet { border-radius: 28px; animation: materialize var(--dur-sheet) var(--ease-spring); }
.popover { border-radius: 24px; }
.primary { border-radius: 16px; background: linear-gradient(180deg, color-mix(in srgb, var(--blue) 88%, #fff), var(--blue)); box-shadow: 0 8px 22px rgba(11,132,254,.32), inset 0 1px 0 rgba(255,255,255,.35); transition: transform var(--dur-fast) var(--ease-spring), filter var(--dur-ui) var(--ease-out), opacity var(--dur-ui) var(--ease-out); }
.primary:active { transform: scale(.97); }
.mini { transition: transform var(--dur-fast) var(--ease-spring), background-color var(--dur-ui) var(--ease-out), color var(--dur-ui) var(--ease-out), opacity var(--dur-ui) var(--ease-out); }
.mini:active { transform: scale(.95); }
.icon-btn { border-radius: 11px; transition: background-color var(--dur-ui) var(--ease-out), transform var(--dur-fast) var(--ease-spring); }
.icon-btn:active { transform: scale(.9); }
.nb { border-radius: 22px; }
.toast { border-radius: 16px; }
.ft-card { border-radius: 24px; }
.invite-fab { transition: transform var(--dur-fast) var(--ease-spring), box-shadow var(--dur-ui) var(--ease-out); }
.invite-fab:active { transform: scale(.94); }

/* Phone tab bar: a floating glass pill lifted off the bottom edge instead of a
   full-bleed bar, with a soft pill sliding behind the active tab. */
@media (max-width: 760px) {
  .rail {
    left: 12px; right: 12px; bottom: calc(10px + env(safe-area-inset-bottom));
    border-top: 0; border-radius: 30px; padding: 7px 8px;
    background: var(--glass); box-shadow: inset 0 1px 0 var(--glass-edge), inset 0 0 0 .5px var(--glass-line), var(--glass-shadow);
  }
  .dock-icon, .rail .me { border-radius: 20px; padding: 6px 8px 5px; min-width: 56px; transition: color var(--dur-ui) var(--ease-out), background-color var(--dur-ui) var(--ease-out), transform var(--dur-fast) var(--ease-spring); }
  .dock-icon.active { background: var(--glass-2) !important; box-shadow: inset 0 1px 0 var(--glass-edge), inset 0 0 0 .5px var(--glass-line) !important; }
  .dock-icon.active > svg { transform: translateY(-1px) scale(1.06); }
  .dock-icon > svg { transition: transform var(--dur-ui) var(--ease-spring); }
  .dock-icon:active { transform: scale(.92); }
  .dock-icon.games svg { color: inherit; }
  .list { padding-bottom: calc(96px + env(safe-area-inset-bottom)); }
  .sb-invite { margin-bottom: calc(82px + env(safe-area-inset-bottom)); }
  .invite-fab { bottom: calc(96px + env(safe-area-inset-bottom)); }
}
@media (prefers-reduced-motion: reduce) {
  .dock-icon, .dock-icon > svg, .row, .chip-filters button, .primary, .mini, .icon-btn, .composer .plus, .invite-fab { transition-duration: 0ms; }
  .sheet { animation: fade var(--dur-ui) ease; }
}

/* ---- Games tab ------------------------------------------------------------ */
.games-list .game-row { padding: 10px 10px 10px 4px; }
.game-ic { width: 46px; height: 46px; border-radius: 15px; display: grid; place-items: center; font-size: 24px; background: var(--glass); box-shadow: inset 0 1px 0 var(--glass-edge), inset 0 0 0 .5px var(--glass-line), 0 4px 12px rgba(20,30,60,.08); }
.row.active .game-ic { background: rgba(255,255,255,.22); box-shadow: inset 0 1px 0 rgba(255,255,255,.35); }
.game-stage { position: absolute; inset: 0; padding: calc(76px + env(safe-area-inset-top)) 14px calc(14px + env(safe-area-inset-bottom)); display: grid; place-items: start center; overflow-y: auto; touch-action: none; }
.game-stage.full { padding: 0; place-items: stretch; overflow: hidden; }
.game-frame { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; background: #0c0c0f; }
.g-wrap { width: min(100%, 420px); display: flex; flex-direction: column; align-items: center; gap: 12px; }
.g-score { display: flex; gap: 10px; width: 100%; }
.g-score span { flex: 1; text-align: center; padding: 10px 12px; border-radius: 16px; background: var(--glass); box-shadow: inset 0 1px 0 var(--glass-edge), inset 0 0 0 .5px var(--glass-line); font-size: 13px; color: var(--text-2); }
.g-score b { display: block; font-size: 20px; letter-spacing: -.02em; color: var(--text); margin-top: 2px; font-variant-numeric: tabular-nums; }
.g-hint { text-align: center; }
.restart-glyph { font-size: 19px; line-height: 1; font-weight: 700; }
.g2048 { width: 100%; aspect-ratio: 1; display: grid; grid-template-columns: repeat(4, 1fr); gap: 9px; padding: 9px; border-radius: 22px; background: var(--search); box-shadow: inset 0 0 0 .5px var(--glass-line); }
.g2048 .t { border-radius: 14px; display: grid; place-items: center; font-weight: 800; font-size: clamp(18px, 6vw, 30px); letter-spacing: -.02em; background: var(--field); color: var(--text); box-shadow: inset 0 1px 0 rgba(255,255,255,.5), 0 1px 2px rgba(20,30,60,.06); }
.g2048 .t.new { animation: t-in 160ms var(--ease-spring) both; }
@keyframes t-in { from { transform: scale(.6); opacity: 0; } to { transform: none; opacity: 1; } }
.g2048 .t.v2 { background: #f3f0ea; } .g2048 .t.v4 { background: #efe6d4; }
.g2048 .t.v8 { background: #f5b27a; color: #fff; } .g2048 .t.v16 { background: #f6955f; color: #fff; }
.g2048 .t.v32 { background: #f77c5f; color: #fff; } .g2048 .t.v64 { background: #f75f3b; color: #fff; }
.g2048 .t.v128, .g2048 .t.v256 { background: #edcf73; color: #fff; } .g2048 .t.v512 { background: #edc850; color: #fff; }
.g2048 .t.v1024, .g2048 .t.v2048 { background: linear-gradient(180deg, #ffd76a, #f5a623); color: #4a3200; }
@media (prefers-color-scheme: dark) { html:not([data-theme="light"]) .g2048 .t.v2, html:not([data-theme="light"]) .g2048 .t.v4 { color: #1d1d1f; } }
#snakeCv { width: 100%; height: auto; aspect-ratio: 1; border-radius: 22px; touch-action: none; }
.dpad { display: flex; flex-direction: column; align-items: center; gap: 6px; }
.dpad div { display: flex; gap: 6px; }
.dpad button { width: 56px; height: 48px; border-radius: 16px; background: var(--glass); color: var(--text); font-size: 16px; box-shadow: inset 0 1px 0 var(--glass-edge), inset 0 0 0 .5px var(--glass-line), 0 4px 12px rgba(20,30,60,.08); transition: transform var(--dur-fast) var(--ease-spring); }
.dpad button:active { transform: scale(.92); }
@media (hover: hover) and (pointer: fine) { .dpad { display: none; } }
.reflex-pad { width: 100%; aspect-ratio: 1; max-height: 420px; border-radius: 28px; font-size: 24px; font-weight: 800; letter-spacing: -.02em; color: #fff; background: linear-gradient(180deg, #6aa9ff, #0b6ee8); box-shadow: 0 18px 50px rgba(11,110,232,.3), inset 0 1px 0 rgba(255,255,255,.45); transition: transform var(--dur-fast) var(--ease-spring), background var(--dur-ui) var(--ease-out); }
.reflex-pad:active { transform: scale(.98); }
.reflex-pad.wait { background: linear-gradient(180deg, #ff8a80, #ff3b30); box-shadow: 0 18px 50px rgba(255,59,48,.3), inset 0 1px 0 rgba(255,255,255,.45); }
.reflex-pad.go { background: linear-gradient(180deg, #62f97e, #16b843); box-shadow: 0 18px 50px rgba(22,184,67,.35), inset 0 1px 0 rgba(255,255,255,.45); }
.reflex-pad.early { background: linear-gradient(180deg, #a0a0aa, #5a5a63); }
.reflex-pad.done { background: linear-gradient(180deg, #b48cff, #6e3ff0); box-shadow: 0 18px 50px rgba(110,63,240,.3), inset 0 1px 0 rgba(255,255,255,.45); }
@media (max-width: 760px) { .game-stage { padding-top: calc(74px + env(safe-area-inset-top)); } }

/* ========================================================================
   Liquid Glass — clear pass
   The glass above still read as frosted white plastic. Real glass is mostly
   transparent: what sells it is a very low-alpha fill so the colour behind
   refracts through, a heavier blur + saturation so that refraction reads as
   depth rather than mud, a bright lens-like RIM that fades from the lit
   top-left to the shadowed bottom-right, and a thin inner top highlight. The
   ambient fields behind everything are turned up so there's something worth
   seeing through the glass.
   ======================================================================== */
:root {
  --glass: linear-gradient(135deg, rgba(255,255,255,.42) 0%, rgba(255,255,255,.18) 55%, rgba(255,255,255,.26) 100%);
  --glass-2: linear-gradient(135deg, rgba(255,255,255,.30) 0%, rgba(255,255,255,.10) 100%);
  --glass-edge: rgba(255,255,255,1);
  --glass-line: rgba(255,255,255,.55);
  --glass-rim: linear-gradient(135deg, rgba(255,255,255,.95) 0%, rgba(255,255,255,.25) 40%, rgba(255,255,255,.08) 60%, rgba(255,255,255,.6) 100%);
  --glass-shadow: 0 24px 60px rgba(30,40,80,.14), 0 4px 10px rgba(30,40,80,.06);
  --glass-blur: blur(44px) saturate(240%) brightness(1.06);
  --sidebar: rgba(255,255,255,.34);
  --window: #e9ecf4;
}
@media (prefers-color-scheme: dark) {
  html:not([data-theme="light"]) {
    --glass: linear-gradient(135deg, rgba(60,60,75,.42) 0%, rgba(30,30,40,.22) 55%, rgba(50,50,65,.30) 100%);
    --glass-2: linear-gradient(135deg, rgba(60,60,75,.32) 0%, rgba(30,30,40,.14) 100%);
    --glass-edge: rgba(255,255,255,.45);
    --glass-line: rgba(255,255,255,.16);
    --glass-rim: linear-gradient(135deg, rgba(255,255,255,.55) 0%, rgba(255,255,255,.12) 40%, rgba(255,255,255,.04) 60%, rgba(255,255,255,.3) 100%);
    --glass-shadow: 0 24px 60px rgba(0,0,0,.55), 0 4px 10px rgba(0,0,0,.4);
    --glass-blur: blur(44px) saturate(220%) brightness(1.02);
    --sidebar: rgba(20,20,26,.36);
    --window: #08080b;
  }
}
html[data-theme="dark"] {
  --glass: linear-gradient(135deg, rgba(60,60,75,.42) 0%, rgba(30,30,40,.22) 55%, rgba(50,50,65,.30) 100%);
  --glass-2: linear-gradient(135deg, rgba(60,60,75,.32) 0%, rgba(30,30,40,.14) 100%);
  --glass-edge: rgba(255,255,255,.45); --glass-line: rgba(255,255,255,.16);
  --glass-rim: linear-gradient(135deg, rgba(255,255,255,.55) 0%, rgba(255,255,255,.12) 40%, rgba(255,255,255,.04) 60%, rgba(255,255,255,.3) 100%);
  --glass-shadow: 0 24px 60px rgba(0,0,0,.55), 0 4px 10px rgba(0,0,0,.4);
  --glass-blur: blur(44px) saturate(220%) brightness(1.02);
  --sidebar: rgba(20,20,26,.36); --window: #08080b;
}

/* The glass material itself: transparent gradient fill, lens rim via a masked
   gradient border, inner top highlight, faint bottom refraction line. */
.glass {
  background: var(--glass);
  -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur);
  box-shadow: inset 0 1px 0 var(--glass-edge), inset 0 -1px 0 rgba(255,255,255,.18), var(--glass-shadow);
}
.glass::before {
  background: radial-gradient(120% 90% at 15% 0%, rgba(255,255,255,.38), transparent 55%);
  mix-blend-mode: normal;
}
/* A 1px gradient rim that follows the corner radius — the lens edge. */
.glass::after {
  content: ""; position: absolute; inset: 0; border-radius: inherit; pointer-events: none; padding: 1px;
  background: var(--glass-rim);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); mask-composite: exclude;
}
@media (prefers-reduced-transparency: reduce) { .glass::after { display: none; } .glass { background: var(--card-solid); } }
.reduce-transparency .glass::after { display: none; }

/* Non-.glass chrome that should still read as clear glass. */
.rail { background: var(--glass-2); -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur); }
.sidebar { background: var(--sidebar); -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur); }
.field-wrap { background: rgba(255,255,255,.55); }
@media (prefers-color-scheme: dark) { html:not([data-theme="light"]) .field-wrap { background: rgba(255,255,255,.08); } }
html[data-theme="dark"] .field-wrap { background: rgba(255,255,255,.08); }
.search, .chip-filters button:not(.on), .composer .plus, .icon-btn:hover, .g-score span, .game-ic, .dpad button { background: rgba(255,255,255,.34); box-shadow: inset 0 1px 0 rgba(255,255,255,.8), inset 0 0 0 .5px rgba(255,255,255,.4), 0 2px 8px rgba(30,40,80,.06); }
@media (prefers-color-scheme: dark) { html:not([data-theme="light"]) .search, html:not([data-theme="light"]) .chip-filters button:not(.on), html:not([data-theme="light"]) .composer .plus, html:not([data-theme="light"]) .g-score span, html:not([data-theme="light"]) .game-ic, html:not([data-theme="light"]) .dpad button { background: rgba(255,255,255,.10); box-shadow: inset 0 1px 0 rgba(255,255,255,.18), inset 0 0 0 .5px rgba(255,255,255,.1); } }
html[data-theme="dark"] .search, html[data-theme="dark"] .chip-filters button:not(.on), html[data-theme="dark"] .composer .plus, html[data-theme="dark"] .g-score span, html[data-theme="dark"] .game-ic, html[data-theme="dark"] .dpad button { background: rgba(255,255,255,.10); box-shadow: inset 0 1px 0 rgba(255,255,255,.18), inset 0 0 0 .5px rgba(255,255,255,.1); }
.row:hover { background: rgba(255,255,255,.28); }
.dock-icon.active { box-shadow: 0 0 0 3px rgba(255,255,255,.35), 0 0 0 4px rgba(255,255,255,.55), 0 8px 22px rgba(30,40,80,.18); }
.dock-icon.games.active { box-shadow: 0 4px 12px rgba(110,63,240,.3), inset 0 1px 0 rgba(255,255,255,.45), 0 0 0 3px rgba(255,255,255,.35), 0 0 0 4px rgba(255,255,255,.55); }
.rail .me { box-shadow: 0 0 0 2px rgba(255,255,255,.35), 0 0 0 3px rgba(255,255,255,.55); }

/* More to refract: stronger, larger ambient colour fields behind everything,
   including the sign-in screen. */
.shell {
  background:
    radial-gradient(60% 50% at 6% 0%, rgba(11,132,254,.28), transparent 62%),
    radial-gradient(55% 45% at 100% 100%, rgba(175,82,222,.24), transparent 60%),
    radial-gradient(45% 40% at 55% 105%, rgba(52,199,89,.18), transparent 60%),
    radial-gradient(40% 35% at 90% 10%, rgba(255,159,10,.14), transparent 60%),
    var(--window);
}
.auth {
  background:
    radial-gradient(60% 50% at 15% 10%, rgba(52,199,89,.32), transparent 60%),
    radial-gradient(55% 55% at 88% 88%, rgba(11,132,254,.34), transparent 60%),
    radial-gradient(45% 40% at 70% 5%, rgba(175,82,222,.22), transparent 60%),
    var(--window);
}
.pane::before { opacity: 1; background: var(--chat-tint); }
@media (max-width: 760px) {
  .sidebar { background: var(--window); -webkit-backdrop-filter: none; backdrop-filter: none; }
  .rail { background: var(--glass); box-shadow: inset 0 1px 0 var(--glass-edge), inset 0 -1px 0 rgba(255,255,255,.18), var(--glass-shadow); }
  .rail::after { content: ""; position: absolute; inset: 0; border-radius: inherit; pointer-events: none; padding: 1px; background: var(--glass-rim); -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); -webkit-mask-composite: xor; mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); mask-composite: exclude; }
  .dock-icon.active { background: rgba(255,255,255,.42) !important; box-shadow: inset 0 1px 0 rgba(255,255,255,.95), inset 0 0 0 .5px rgba(255,255,255,.5) !important; }
}
/* Vibrancy: text on clear glass needs a hair more weight to stay legible. */
.pane-head .who .n, .sb-top h2, .composer, .rail .dock-icon::before, .rail .me::before { text-shadow: 0 1px 0 rgba(255,255,255,.35); }
@media (prefers-color-scheme: dark) { html:not([data-theme="light"]) .pane-head .who .n, html:not([data-theme="light"]) .sb-top h2, html:not([data-theme="light"]) .composer, html:not([data-theme="light"]) .rail .dock-icon::before, html:not([data-theme="light"]) .rail .me::before { text-shadow: 0 1px 2px rgba(0,0,0,.5); } }
html[data-theme="dark"] .pane-head .who .n, html[data-theme="dark"] .sb-top h2, html[data-theme="dark"] .composer, html[data-theme="dark"] .rail .dock-icon::before, html[data-theme="dark"] .rail .me::before { text-shadow: 0 1px 2px rgba(0,0,0,.5); }

/* ========================================================================
   Motion pass — slide, don't pop
   ======================================================================== */
/* Lighter blur: 44px on four stacked glass surfaces was costing frames on an iPad
   during loads and re-renders; 30px keeps the depth and drops most of the cost. */
:root { --glass-blur: blur(30px) saturate(220%) brightness(1.05); }
@media (prefers-color-scheme: dark) { html:not([data-theme="light"]) { --glass-blur: blur(30px) saturate(200%) brightness(1.02); } }
html[data-theme="dark"] { --glass-blur: blur(30px) saturate(200%) brightness(1.02); }

/* Panels arrive by sliding into place on transform/opacity only — no scale pop, no
   animated blur (that filter animation was the visibly janky part). The existing
   .sheet.remount guard still cancels the entrance on a re-render. */
@keyframes glass-slide { from { opacity: 0; transform: translateY(18px); } to { opacity: 1; transform: none; } }
@keyframes glass-slide-down { from { opacity: 0; transform: translateY(-10px); } to { opacity: 1; transform: none; } }
.sheet { animation: glass-slide 320ms var(--ease-out); }
.popover { animation: glass-slide-down 220ms var(--ease-out); }
.admin-overlay-card { animation: glass-slide 320ms var(--ease-out); }
.a2hs-card { animation: glass-slide 320ms var(--ease-out); }
.auth-card { animation: glass-slide 420ms var(--ease-out); }
@media (prefers-reduced-motion: reduce) { .sheet, .popover, .admin-overlay-card, .a2hs-card, .auth-card { animation: fade var(--dur-ui) ease; } }

/* The sliding selection slab. Rows keep their own transparent background and sit
   above it; #rowInd is the only thing painting the blue glass, and JS glides it. */
.list { position: relative; }
.list .row { position: relative; z-index: 1; }
.row.active, .row.active:hover, .row.active:active { background: transparent; box-shadow: none; }
.row.active:hover { background: rgba(255,255,255,.08); }
#rowInd {
  position: absolute; left: 8px; right: 8px; z-index: 0; border-radius: 16px; pointer-events: none;
  transform-origin: top center; will-change: transform;
  background: linear-gradient(180deg, color-mix(in srgb, var(--blue) 86%, #fff) 0%, var(--blue) 100%);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.45), inset 0 -1px 0 rgba(0,0,0,.06), 0 10px 26px rgba(11,132,254,.30);
}
#rowInd::after {
  content: ""; position: absolute; inset: 0; border-radius: inherit; padding: 1px; pointer-events: none;
  background: linear-gradient(135deg, rgba(255,255,255,.9), rgba(255,255,255,.15) 45%, rgba(255,255,255,.05) 60%, rgba(255,255,255,.5));
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); -webkit-mask-composite: xor; mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); mask-composite: exclude;
}

/* ========================================================================
   Glide everywhere + clean close
   ======================================================================== */
/* Generic sliding slab (syncGlide in app.js): tab bar pill, filter chips, admin nav. */
.glide-ind { position: absolute; z-index: 0; pointer-events: none; transform-origin: 0 0; will-change: transform; border-radius: 20px;
  background: var(--glass-2); box-shadow: inset 0 1px 0 var(--glass-edge), inset 0 0 0 .5px var(--glass-line), 0 6px 18px rgba(30,40,80,.10); }
.glide-ind::after { content: ""; position: absolute; inset: 0; border-radius: inherit; padding: 1px; pointer-events: none; background: var(--glass-rim);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); -webkit-mask-composite: xor; mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); mask-composite: exclude; }
/* Tab bar: the pill is the active state now; the tiles stop painting their own ring. */
.rail { position: relative; }
@media (max-width: 760px) { .rail { position: fixed; } }
.rail > .dock-icon, .rail > .me, .rail > .spacer { position: relative; z-index: 1; }
#railInd { border-radius: 18px; background: rgba(255,255,255,.55); box-shadow: inset 0 1px 0 rgba(255,255,255,.95), inset 0 0 0 .5px rgba(255,255,255,.5), 0 6px 18px rgba(30,40,80,.14); }
@media (prefers-color-scheme: dark) { html:not([data-theme="light"]) #railInd { background: rgba(255,255,255,.14); box-shadow: inset 0 1px 0 rgba(255,255,255,.3), inset 0 0 0 .5px rgba(255,255,255,.14), 0 6px 18px rgba(0,0,0,.35); } }
html[data-theme="dark"] #railInd { background: rgba(255,255,255,.14); box-shadow: inset 0 1px 0 rgba(255,255,255,.3), inset 0 0 0 .5px rgba(255,255,255,.14), 0 6px 18px rgba(0,0,0,.35); }
.dock-icon.active, .dock-icon.games.active { box-shadow: none; }
@media (max-width: 760px) { .dock-icon.active { background: none !important; box-shadow: none !important; } .rail .me { position: relative; z-index: 1; } }
/* Filter chips: the blue slab slides between them. */
.chip-filters { position: relative; }
.chip-filters > button { position: relative; z-index: 1; }
.chip-filters button.on { background: transparent; color: #fff; }
#chipInd { border-radius: 12px; background: linear-gradient(180deg, color-mix(in srgb, var(--blue) 86%, #fff), var(--blue)); box-shadow: inset 0 1px 0 rgba(255,255,255,.4), 0 6px 16px rgba(11,132,254,.28); }
/* Admin nav: glass slab behind the selected section. */
.admin-nav { position: relative; }
.admin-nav > .admin-nav-btn { position: relative; z-index: 1; }
.admin-nav-btn.on { color: var(--text); }
#adminInd { border-radius: 12px; }

/* Exit animations — the mirror of glass-slide, on transform/opacity only. */
@keyframes glass-out { to { opacity: 0; transform: translateY(18px); } }
@keyframes glass-out-fade { to { opacity: 0; } }
.sheet-wrap.closing { animation: glass-out-fade 240ms var(--ease-out) forwards; }
.sheet-wrap.closing .sheet { animation: glass-out 240ms var(--ease-out) forwards; }
.incoming-wrap.closing, .story-viewer.closing, .fx-pick.closing { animation: glass-out-fade 220ms var(--ease-out) forwards; }
.plusmenu.closing, .tray.closing { animation: glass-out 200ms var(--ease-out) forwards; }
@media (prefers-reduced-motion: reduce) { .sheet-wrap.closing, .sheet-wrap.closing .sheet, .incoming-wrap.closing, .story-viewer.closing, .fx-pick.closing, .plusmenu.closing, .tray.closing { animation: glass-out-fade 120ms ease forwards; } }
/* The × on every sheet: a proper pressable glass button, not a bare glyph. */
.sheet .it-x { transition: transform var(--dur-fast) var(--ease-spring), background-color var(--dur-ui) var(--ease-out); }
.sheet .it-x:active { transform: scale(.88); }

/* ---- Chat layout polish ------------------------------------------------ */
.thread { padding: 84px 18px calc(var(--composer-h, 92px) + 28px); gap: 0; }
.msg { margin-top: 3px; }
.msg.gap { margin-top: 16px; }
.msg .col { max-width: min(72%, 620px); gap: 0; }
.bubble { padding: 8px 13px 9px; line-height: 1.36; }
.stamp { margin: 18px auto 10px; font-size: 11px; font-weight: 600; letter-spacing: .01em; padding: 4px 11px; border-radius: 999px; background: var(--glass-2); box-shadow: inset 0 1px 0 var(--glass-edge), inset 0 0 0 .5px var(--glass-line); color: var(--text-2); width: fit-content; }
.sender { margin: 8px 0 3px 14px; font-weight: 600; letter-spacing: .01em; }
.msg .quote { border-radius: 12px; }
.sys { margin: 12px auto; }
@media (max-width: 760px) { .thread { padding-left: 12px; padding-right: 12px; } .msg .col { max-width: 80%; } }

/* ---- Admin panel: one section at a time, glass cards ------------------- */
#pane[data-section] .admin-sec { display: none; }
#pane[data-section="overview"] #admin-overview, #pane[data-section="reach"] #admin-reach, #pane[data-section="convos"] #admin-convos,
#pane[data-section="reports"] #admin-reports, #pane[data-section="stories"] #admin-stories, #pane[data-section="idscans"] #admin-idscans,
#pane[data-section="accounts"] #admin-accounts, #pane[data-section="security"] #admin-security { display: block; }
.admin-sec { margin-bottom: 20px; animation: glass-slide 260ms var(--ease-out); }
.admin-sec-h h2 { font-size: 24px; letter-spacing: -.02em; font-weight: 800; }
.admin-card { border-radius: 20px; background: var(--glass); -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur); box-shadow: inset 0 1px 0 var(--glass-edge), inset 0 0 0 .5px var(--glass-line), 0 10px 30px rgba(30,40,80,.08); position: relative; }
.admin-card::after { content: ""; position: absolute; inset: 0; border-radius: inherit; padding: 1px; pointer-events: none; background: var(--glass-rim); -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); -webkit-mask-composite: xor; mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); mask-composite: exclude; }
.admin-card > * { position: relative; z-index: 1; }
.admin-stats { gap: 12px; }
.admin-stat { border-radius: 18px; padding: 16px; background: var(--glass); box-shadow: inset 0 1px 0 var(--glass-edge), inset 0 0 0 .5px var(--glass-line); }
.admin-stat b { font-size: 28px; letter-spacing: -.03em; }
.admin-nav-btn { height: 44px; border-radius: 12px; font-size: 14.5px; }
.admin-table-wrap { border-radius: 16px; overflow: auto; background: var(--glass); box-shadow: inset 0 1px 0 var(--glass-edge), inset 0 0 0 .5px var(--glass-line); }
@media (prefers-reduced-motion: reduce) { .admin-sec { animation: none; } }

/* ========================================================================
   Join requests — admin card + the waiting screen
   ======================================================================== */
.join-card { margin: 0 0 14px; padding: 16px 18px; }
.join-head { display: flex; align-items: flex-start; gap: 14px; }
.join-head > div { flex: 1; min-width: 0; }
.join-head .fine { margin: 4px 0 0; }
.join-switch { flex: none; width: 52px; height: 32px; border-radius: 999px; border: 0; padding: 2px; margin-top: 2px; cursor: pointer;
  background: rgba(120,120,128,.24); transition: background-color var(--dur-ui) var(--ease-out), transform var(--dur-fast) var(--ease-spring); position: relative; }
.join-switch:active { transform: scale(.96); }
.join-switch span { display: block; width: 28px; height: 28px; border-radius: 50%; background: #fff; box-shadow: 0 3px 8px rgba(0,0,0,.22), 0 0 0 .5px rgba(0,0,0,.04);
  transform: translateX(0); transition: transform 260ms var(--ease-spring); }
.join-switch.on { background: linear-gradient(180deg, #4ade80, var(--green, #34c759)); }
.join-switch.on span { transform: translateX(20px); }
.join-switch:disabled { opacity: .5; cursor: default; }
.join-switch:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }
.join-list { margin-top: 14px; display: grid; gap: 6px; }
.join-row { display: flex; align-items: center; gap: 12px; padding: 10px 12px; border-radius: 14px; background: var(--glass-2); box-shadow: inset 0 1px 0 var(--glass-edge), inset 0 0 0 .5px var(--glass-line); animation: glass-slide 260ms var(--ease-out) backwards; }
.join-row .avatar.sm { width: 36px; height: 36px; font-size: 14px; flex: none; }
.join-who { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.join-who b { font-size: 14.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.join-who .fine { margin: 0; }
.join-row .admin-acts { margin: 0; flex: none; }
.join-empty { margin: 12px 0 0; }
.join-note { display: flex; gap: 8px; align-items: flex-start; text-align: left; padding: 10px 12px; border-radius: 12px; background: color-mix(in srgb, var(--blue) 10%, transparent); color: var(--text); }
.join-note svg { width: 16px; height: 16px; flex: none; margin-top: 1px; color: var(--blue); }
/* waiting screen: three breathing dots under the copy — it is checking, not stuck */
.pending-wait { display: flex; justify-content: center; gap: 7px; margin: 4px 0 18px; }
.pw-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--blue); opacity: .3; animation: pw-breathe 1.4s ease-in-out infinite; }
.pw-dot:nth-child(2) { animation-delay: .2s; } .pw-dot:nth-child(3) { animation-delay: .4s; }
@keyframes pw-breathe { 0%, 100% { opacity: .3; transform: scale(.85); } 50% { opacity: 1; transform: scale(1); } }
.lock-screen.pending .sub b { color: var(--text); }
@media (prefers-reduced-motion: reduce) { .pw-dot { animation: none; opacity: .7; } .join-row { animation: none; } }
@media (max-width: 480px) { .join-row { flex-wrap: wrap; } .join-row .admin-acts { width: 100%; justify-content: flex-end; } }

/* ========================================================================
   Layout fixes (2026-09-21f)
   ======================================================================== */
/* Phone tab bar: six tabs (seven for an admin) share the width instead of each
   demanding 56px — that pushed Settings clean off the right edge once Games landed. */
@media (max-width: 760px) {
  .rail { padding: 7px 4px; gap: 0; overflow: hidden; }
  .dock-icon, .rail .me { flex: 1 1 0; min-width: 0; width: auto; padding: 6px 2px 5px; }
  .dock-icon::before, .rail .me::before { font-size: 10px; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .dock-icon svg { width: 24px; height: 24px; }
  .rail .me::after { width: 24px; height: 24px; }
  @media (max-width: 360px) { .dock-icon::before, .rail .me::before { font-size: 9px; } }
  /* + menu: a two-column grid that never needs to scroll, so a finger dragging
     down on it is always a dismiss and never "trying to reach Poll". */
  .plusmenu { left: 12px; right: 12px; width: auto; max-height: none; overflow: visible; display: grid; grid-template-columns: 1fr 1fr; gap: 2px; padding: 8px; border-radius: 22px; }
  .plusmenu button { padding: 9px 8px; font-size: 13px; gap: 9px; min-width: 0; }
  .plusmenu button span { width: 30px; height: 30px; flex: none; }
  .plusmenu button span svg { width: 16px; height: 16px; }
}
/* The gamepad glyph is an outline drawn in currentColor. The default look puts it
   white on a purple tile; the WhatsApp/Telegram/Snapchat looks strip every tile,
   so white-on-nothing made the Games tab vanish. Follow the tab's own colour there. */
html[data-look] .dock-icon.games svg { color: inherit; }
