/* ============================================================================
   Unify Church Messenger — Blue / White / Light-Orange visual system
   A clean, professional, mobile-first church communication platform. Sora for
   headings (modern, geometric), Plus Jakarta Sans for body. Church Blue on white
   and light-gray surfaces, with light orange reserved for create actions, alerts,
   and notification badges. Designed for real phone screens (360–430px), large
   touch targets, and safe spacing around system bars. No purple anywhere.
   ========================================================================== */

@import url("https://fonts.googleapis.com/css2?family=Sora:wght@500;600;700;800&family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap");

/* ---- Design tokens -------------------------------------------------------- */
:root {
  --font-display: "Sora", system-ui, -apple-system, sans-serif;
  --font-body: "Plus Jakarta Sans", system-ui, -apple-system, sans-serif;

  /* Brand */
  --blue: #1565ff;           /* Church Blue */
  --blue-strong: #0d4fd6;
  --navy: #0f1b3d;           /* Deep Navy */
  --orange: #ffb347;         /* Light Orange — actions, alerts, badges */
  --orange-strong: #f59324;

  --primary: var(--blue);
  --primary-strong: var(--blue-strong);
  --accent-grad: linear-gradient(135deg, #1f6dff 0%, #1565ff 55%, #0d4fd6 100%);
  --navy-grad: linear-gradient(150deg, #16294f 0%, #0f1b3d 70%);
  --orange-grad: linear-gradient(135deg, #ffc163 0%, #ffb347 60%, #f59324 100%);

  /* Calendar / category accents */
  --c-blue: #1565ff;         /* Standard church events */
  --c-orange: #f59324;       /* Important events */
  --c-teal: #0e9aa7;
  --c-green: #16a34a;
  --c-rose: #e0507a;

  --radius-xl: 20px;
  --radius-lg: 20px;
  --radius-md: 14px;
  --radius-sm: 10px;

  --touch: 48px;            /* minimum interactive height (Android 48dp / iOS ≥44) */
  --maxw: 640px;
  --nav-h: 68px;
  /* The app shell's side padding. One token so every band that has to line up with that inset — a chat
     header, a message thread, a composer — reads the same value instead of re-hard-coding 16px, and so
     the chat shell can hand its inset to those bands (see .container.chat-open) without any of them
     reaching past the shell's clip box and losing an edge. */
  --shell-pad: 16px;

  /* Message rows in every chat reserve one fixed avatar column, so the sender avatar, the bubble
     beside it, and anything that lines up under the bubble (reactions, acknowledgements) all share a
     single source of truth. Changing these two values moves the whole column consistently — nothing
     is left behind on a hard-coded 46px. */
  --chat-ava: 38px;         /* sender avatar box in a message row */
  --chat-ava-gap: 6px;      /* space between that avatar and the message card */
  --chat-ava-col: calc(var(--chat-ava) + var(--chat-ava-gap));
  --chat-edge: 16px;        /* side padding of the scrolling thread. The thread IS the scroll box, so
                               anything reaching past this padding is cut off by it: an avatar is 38px
                               of box plus up to 8px of soft shadow, so a smaller edge shaved the left
                               side of every sender avatar. At --shell-pad the thread's own padding
                               replaces the shell's (see .container.chat-open .chat-thread), so the
                               avatars, the header's Back button and the composer all line up on one
                               left edge with room for their shadows. */

  --ease: cubic-bezier(.22, .61, .36, 1);
  --shadow-sm: 0 1px 2px rgba(15, 27, 61, .06), 0 2px 8px rgba(15, 27, 61, .05);
  --shadow-md: 0 10px 26px rgba(15, 50, 120, .12);
  --shadow-lg: 0 20px 50px rgba(15, 50, 120, .20);
  --shadow-blue: 0 12px 26px rgba(21, 101, 255, .28);
  --shadow-orange: 0 12px 26px rgba(245, 147, 36, .34);
}

/* Light theme (default) */
:root, [data-theme="light"] {
  --bg: #f5f7fb;            /* Light Gray */
  --bg-grad-1: rgba(21, 101, 255, .07);
  --bg-grad-2: rgba(21, 101, 255, .05);
  --surface: #ffffff;
  --surface-2: #f5f7fb;
  --surface-inset: #eef2fa;
  --ink: #0f1b3d;          /* Navy text */
  --ink-soft: #33405e;
  --muted: #6b7793;
  --line: #e6ebf5;
  --line-strong: #d3dbeb;
  --soft: #e7efff;         /* soft blue tint */
  --soft-ink: #0d4fd6;
  --orange-soft: #fff3e1;
  --danger: #d23f57;
  --danger-soft: #fdebee;
  --success: #128a5b;
  --success-soft: #e3f6ee;
  --bubble-mine: var(--accent-grad);
  --bubble-mine-ink: #ffffff;
  --bubble-theirs: #eef2fa;
  --bubble-theirs-ink: #14223f;
}

/* Dark theme (navy-based, same brand hues) */
[data-theme="dark"] {
  --bg: #070c1a;
  --bg-grad-1: rgba(21, 101, 255, .20);
  --bg-grad-2: rgba(21, 101, 255, .12);
  --surface: #0f1830;
  --surface-2: #131e3a;
  --surface-inset: #182343;
  --ink: #eef3ff;
  --ink-soft: #c2ccea;
  --muted: #8d99bd;
  --line: #1e2a4b;
  --line-strong: #2a3964;
  --soft: #14254d;
  --soft-ink: #8fb6ff;
  --orange-soft: #3a2a14;
  --danger: #ff859a;
  --danger-soft: #3a1620;
  --success: #44d39c;
  --success-soft: #0d2b22;
  --bubble-mine: var(--accent-grad);
  --bubble-mine-ink: #ffffff;
  --bubble-theirs: #1a2748;
  --bubble-theirs-ink: #eef3ff;
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, .4);
  --shadow-md: 0 12px 28px rgba(0, 0, 0, .5);
  --shadow-lg: 0 22px 55px rgba(0, 0, 0, .6);
}

/* ---- Reset / base --------------------------------------------------------- */
* { box-sizing: border-box; }
/* `overscroll-behavior: none` is set on both html and body: the document scroller is the root
   element in some mobile browsers and body in others, so pinning both is what reliably stops the
   rubber-band overscroll that would otherwise let the sticky composer and fixed nav bounce or be
   dragged out of position. Part of the permanent fixed-UI rule (header, composer and bottom nav
   never move) — see initViewportPin() in app.js. */
html { -webkit-text-size-adjust: 100%; overscroll-behavior: none; }
html.chat-active,
html.chat-active body { height: 100%; overflow: hidden; }
body {
  margin: 0;
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.5;
  color: var(--ink);
  background: var(--bg);
  background-image:
    radial-gradient(1100px 560px at 0% -10%, var(--bg-grad-1), transparent 60%),
    radial-gradient(900px 560px at 110% 0%, var(--bg-grad-2), transparent 55%);
  background-attachment: scroll;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;             /* never allow sideways scroll on a phone */
  overscroll-behavior: none;      /* no rubber-band bounce / pull-to-refresh — feel like an app, not a page */
  transition: background-color .35s var(--ease), color .35s var(--ease);
}

h1, h2, h3 { font-family: var(--font-display); font-weight: 700; line-height: 1.18; margin: 0; letter-spacing: -.015em; overflow-wrap: break-word; }
h1 { font-size: 1.45rem; }
h2 { font-size: 1.2rem; }
h3 { font-size: 1.02rem; }
p { margin: 0; overflow-wrap: break-word; }
a { color: var(--primary); }

button, input, select, textarea { font: inherit; color: inherit; }
::selection { background: rgba(21, 101, 255, .22); }

/* Replaced elements must never push the layout wider than the phone screen. */
img, svg, video, canvas { max-width: 100%; }

/* Base icon size. Every icon ships with no intrinsic width, so without this an icon in a
   container that doesn't size it (e.g. the empty-state badge) balloons to fill its box. A
   small default keeps stray icons phone-sized; context rules below override where needed. */
.ico { width: 20px; height: 20px; flex: 0 0 auto; }

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: .001ms !important; transition-duration: .001ms !important; }
}

/* ---- Layout --------------------------------------------------------------- */
.container {
  max-width: var(--maxw); margin: 0 auto; min-height: 100vh; min-height: 100dvh;
  padding: calc(20px + env(safe-area-inset-top)) var(--shell-pad) calc(var(--nav-h) + env(safe-area-inset-bottom) + 40px);
  padding-bottom: calc(var(--nav-h) + env(safe-area-inset-bottom) + 56px);
}
.auth-wrap {
  max-width: 440px; margin: 0 auto; min-height: 100vh; min-height: 100dvh;
  padding: calc(28px + env(safe-area-inset-top)) 18px calc(28px + env(safe-area-inset-bottom));
  display: flex; flex-direction: column; justify-content: center;
}
.stack { display: grid; gap: 18px; }
.row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.row.space { justify-content: space-between; }
.row.nowrap { flex-wrap: nowrap; }
.grow { flex: 1; min-width: 0; }
.muted { color: var(--muted); }
.tiny { font-size: .8rem; }
.center { text-align: center; }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hide { display: none !important; }

/* ---- Cards ---------------------------------------------------------------- */
.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 22px;
  box-shadow: var(--shadow-sm);
  transition: transform .2s var(--ease), box-shadow .2s var(--ease), border-color .2s var(--ease), background-color .35s var(--ease);
}
.card.pad-lg { padding: 26px; }
.card.flat { box-shadow: none; }
.card.tappable { cursor: pointer; }
.card.tappable:hover { transform: translateY(-2px); box-shadow: var(--shadow-md); border-color: var(--line-strong); }
.card.tappable:active { transform: translateY(0); }

/* Settings → Developer Diagnostics. A collapsed card: only Owners/Admins receive the markup, and it
   stays shut until it is tapped, so the normal Settings page shows no technical read-out at all. */
.diag-details { padding: 6px 14px; margin-top: 12px; }
.diag-details > summary {
  position: relative;
  display: flex; flex-direction: column; gap: 2px;
  padding: 12px 54px 12px 2px; cursor: pointer; list-style: none;
}
.diag-details > summary::-webkit-details-marker { display: none; }
.diag-details > summary::after {
  content: "Show"; position: absolute; right: 2px; top: 12px;
  font-size: .78rem; color: var(--muted);
}
.diag-details[open] > summary::after { content: "Hide"; }
.diag-details > summary .v { font-weight: 650; color: var(--ink); }
.diag-details > summary .k { color: var(--muted); font-size: .78rem; }
.diag-details > summary:focus-visible { outline: 2px solid var(--brand, #3b82f6); outline-offset: 2px; }
.diag-details .diag-body { border-top: 1px solid var(--line); padding-top: 6px; }

.section-title { display: flex; align-items: center; gap: 9px; margin: 30px 2px 15px; }
.section-title:first-child { margin-top: 8px; }
.section-title h2 { font-size: 1.12rem; }
.section-title .ico { color: var(--primary); }
.section-sub { color: var(--muted); font-size: .9rem; margin-top: 2px; }

/* ---- Buttons -------------------------------------------------------------- */
.btn, button.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: var(--touch); padding: 0 18px;
  border: 0; border-radius: var(--radius-md);
  background: var(--accent-grad); color: #fff; font-weight: 700; font-size: .98rem;
  cursor: pointer; box-shadow: var(--shadow-blue);
  transition: transform .14s var(--ease), box-shadow .2s var(--ease), opacity .2s var(--ease), filter .2s var(--ease);
  -webkit-tap-highlight-color: transparent;
}
.btn:hover { transform: translateY(-1px); filter: brightness(1.03); }
.btn:active { transform: translateY(0) scale(.99); }
.btn:disabled { opacity: .55; cursor: not-allowed; transform: none; box-shadow: none; }
.btn.block { width: 100%; }
/* Orange = create / primary action highlight */
.btn.orange { background: var(--orange-grad); color: #4a2e05; box-shadow: var(--shadow-orange); }
.btn.ghost { background: var(--soft); color: var(--soft-ink); box-shadow: none; }
.btn.ghost:hover { background: var(--surface-inset); }
.btn.outline { background: var(--surface); color: var(--ink); box-shadow: none; border: 1px solid var(--line-strong); }
.btn.outline:hover { border-color: var(--primary); color: var(--primary); }
.btn.danger { background: var(--danger-soft); color: var(--danger); box-shadow: none; }
.btn.sm { min-height: 40px; padding: 0 14px; font-size: .88rem; border-radius: var(--radius-sm); }
.btn.lg { min-height: 56px; font-size: 1.04rem; border-radius: var(--radius-lg); }
.btn .ico { width: 18px; height: 18px; }

/* Icon-only round button */
.icon-btn {
  display: inline-grid; place-items: center; width: 46px; height: 46px; flex: 0 0 auto;
  border: 1px solid var(--line); border-radius: 50%; background: var(--surface);
  color: var(--ink-soft); cursor: pointer; box-shadow: var(--shadow-sm);
  transition: transform .14s var(--ease), color .2s, border-color .2s, background-color .2s;
}
.icon-btn:hover { color: var(--primary); border-color: var(--primary); transform: translateY(-1px); }
.icon-btn:active { transform: translateY(0); }

/* Floating action button — orange create action */
.fab {
  position: fixed; right: 16px; bottom: calc(var(--nav-h) + 18px + env(safe-area-inset-bottom)); z-index: 30;
  width: 58px; height: 58px; border-radius: 20px; border: 0;
  background: var(--orange-grad); color: #4a2e05; box-shadow: var(--shadow-orange);
  display: grid; place-items: center; cursor: pointer;
  transition: transform .16s var(--ease), box-shadow .2s var(--ease);
}
.fab:hover { transform: translateY(-2px) scale(1.04); }
.fab:active { transform: translateY(0) scale(.98); }
.fab .ico { width: 26px; height: 26px; }
.mobile-refresh {
  color: var(--primary);
}
.mobile-refresh .ico { width: 22px; height: 22px; }
.mobile-refresh.spin .ico { animation: unify-spin .8s linear infinite; }
.mobile-refresh.refreshing::after {
  content: "Refreshing...";
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

/* ---- Connection indicator -------------------------------------------------- */
/* A slim status banner that appears just above the bottom nav only when the connection is not
   fully healthy. Hidden (translated off-screen) while online, so it never clutters normal use. */
.conn-banner {
  position: fixed; left: 50%; transform: translate(-50%, 140%);
  bottom: calc(var(--nav-h) + 14px + env(safe-area-inset-bottom)); z-index: 45;
  max-width: min(560px, calc(100% - 24px)); width: max-content;
  display: flex; align-items: center; gap: 9px;
  padding: 9px 15px; border-radius: 999px; font-size: .82rem; font-weight: 600;
  box-shadow: var(--shadow); pointer-events: none; opacity: 0;
  transition: transform .25s var(--ease), opacity .25s var(--ease);
}
.conn-banner.show { transform: translate(-50%, 0); opacity: 1; }
.conn-banner .conn-dot { width: 9px; height: 9px; border-radius: 50%; flex: 0 0 9px; }
.conn-banner[data-state="offline"] { background: #fdecec; color: #b42318; }
.conn-banner[data-state="offline"] .conn-dot { background: #e5484d; }
.conn-banner[data-state="connecting"] { background: #fef6e7; color: #b45309; }
.conn-banner[data-state="connecting"] .conn-dot { background: #f59e0b; animation: conn-pulse 1s ease-in-out infinite; }
.conn-banner[data-state="online"] { background: #e7f7ec; color: #15803d; }
.conn-banner[data-state="online"] .conn-dot { background: #22c55e; }
@keyframes conn-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .3; } }

/* ---- Performance Health Dashboard (Admin-only) ----------------------------- */
.health-dot { width: 11px; height: 11px; border-radius: 50%; flex: 0 0 11px; display: inline-block; }
.health-dot.green { background: #22c55e; }
.health-dot.yellow { background: #f59e0b; }
.health-dot.red { background: #e5484d; }
.health-overall {
  display: flex; align-items: center; gap: 9px; font-weight: 700; font-size: 1.02rem;
  padding: 4px 0 12px; border-bottom: 1px solid var(--line); margin-bottom: 12px;
}
.health-overall.green { color: #15803d; }
.health-overall.yellow { color: #b45309; }
.health-overall.red { color: #b42318; }
.health-overall .health-dot { width: 13px; height: 13px; flex-basis: 13px; }
.health-issues {
  margin: 0 0 12px; padding: 11px 14px 11px 30px; border-radius: 12px;
  background: rgba(245, 158, 11, .1); color: var(--ink); font-size: .82rem;
}
.health-issues li { margin: 2px 0; }
.health-grid { display: flex; flex-direction: column; gap: 2px; }
.sync-summary {
  display: grid; gap: 8px; margin: 0 0 14px; padding: 10px;
  border: 1px solid var(--line-strong); border-radius: 16px; background: var(--surface-inset);
}
.sync-summary-row { display: flex; align-items: center; gap: 11px; padding: 8px 7px; }
.sync-summary-row > span:last-child { display: flex; flex-direction: column; gap: 2px; }
.sync-summary-row b { color: var(--ink); font-size: .9rem; }
.sync-summary-row small { color: var(--muted); font-size: .78rem; }
[data-theme="dark"] .sync-summary { background: #0b1630; border-color: #2a3f6f; }
[data-theme="dark"] .sync-summary-row b { color: #f7f9ff; }
[data-theme="dark"] .sync-summary-row small { color: #b8c4e3; }
[data-theme="dark"] .sync-summary-row .health-dot.green { background: #4ee3a2; }
[data-theme="dark"] #healthSync { background: #1565ff; border-color: #4386ff; color: #fff; }
.health-row { display: flex; align-items: center; gap: 11px; padding: 9px 2px; border-bottom: 1px solid var(--line); }
.health-row:last-child { border-bottom: 0; }
.health-row .h-k { font-weight: 600; color: var(--ink); }
.health-row .h-v { display: block; font-size: .82rem; color: var(--muted); margin-top: 1px; }

/* ---- Forms ---------------------------------------------------------------- */
label.field { display: block; font-size: .82rem; font-weight: 700; color: var(--ink-soft); margin: 14px 2px 6px; }
input, select, textarea {
  width: 100%; min-height: var(--touch); max-width: 100%;
  border: 1px solid var(--line-strong); border-radius: var(--radius-md);
  padding: 12px 14px; background: var(--surface); color: var(--ink);
  transition: border-color .18s, box-shadow .18s, background-color .2s;
}
textarea { min-height: 96px; resize: vertical; line-height: 1.5; }
input:focus, select:focus, textarea:focus {
  outline: none; border-color: var(--primary);
  box-shadow: 0 0 0 4px rgba(21, 101, 255, .15);
}
input::placeholder, textarea::placeholder { color: var(--muted); }
select { appearance: none; background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='20' height='20' fill='none' stroke='%236b7793' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='M6 9l6 6 6-6'/></svg>"); background-repeat: no-repeat; background-position: right 12px center; padding-right: 40px; }

/* Search field with leading icon */
.search { position: relative; }
.search .ico { position: absolute; left: 15px; top: 50%; transform: translateY(-50%); color: var(--muted); pointer-events: none; }
.search input { padding-left: 44px; border-radius: 999px; background: var(--surface); }
.dm-search { margin-bottom: 10px; }
.dm-search .ico { left: 14px; width: 18px; height: 18px; }
.dm-search input { min-height: 42px; padding-top: 9px; padding-bottom: 9px; padding-left: 40px; background: var(--surface-2); }

/* Checkbox picker list */
.check-list { display: grid; gap: 6px; max-height: 260px; overflow: auto; border: 1px solid var(--line); border-radius: var(--radius-md); padding: 8px; background: var(--surface-2); }
.check-row { display: flex; align-items: center; gap: 12px; padding: 10px 12px; border-radius: var(--radius-sm); cursor: pointer; min-height: var(--touch); transition: background-color .15s; }
.check-row:hover { background: var(--surface-inset); }
.check-row input { width: 20px; height: 20px; min-height: 0; flex: 0 0 auto; accent-color: var(--primary); }

/* Inline checkbox (toggle-style options like "never ends") */
.opt-row { display: flex; align-items: center; gap: 10px; margin-top: 12px; }
.opt-row input { width: 20px; height: 20px; min-height: 0; flex: 0 0 auto; accent-color: var(--primary); }
.opt-row label { font-weight: 600; font-size: .9rem; }

/* ---- Pills / badges ------------------------------------------------------- */
.pill { display: inline-flex; align-items: center; gap: 5px; border-radius: 999px; padding: 5px 11px; background: var(--soft); color: var(--soft-ink); font-size: .76rem; font-weight: 700; line-height: 1; }
.pill.tone-blue { background: var(--soft); color: var(--soft-ink); }
.pill.tone-orange, .pill.tone-amber { background: var(--orange-soft); color: var(--orange-strong); }
.pill.tone-navy, .pill.tone-violet { background: rgba(15, 27, 61, .08); color: var(--navy); }
.pill.tone-teal { background: rgba(14, 154, 167, .14); color: var(--c-teal); }
.pill.tone-rose { background: rgba(224, 80, 122, .14); color: var(--c-rose); }
.pill.tone-success { background: var(--success-soft); color: var(--success); }
[data-theme="dark"] .pill.tone-navy, [data-theme="dark"] .pill.tone-violet { color: var(--ink); }
[data-theme="dark"] .pill.tone-teal, [data-theme="dark"] .pill.tone-rose { color: var(--ink); }

.badge { display: inline-grid; place-items: center; min-width: 22px; height: 22px; padding: 0 7px; border-radius: 999px; background: var(--orange-grad); color: #4a2e05; font-size: .72rem; font-weight: 800; }
.dot { width: 9px; height: 9px; border-radius: 50%; background: var(--primary); flex: 0 0 auto; }

/* Owner badge — protected first Admin */
.owner-badge { display: inline-flex; align-items: center; gap: 5px; border-radius: 999px; padding: 5px 11px; background: var(--orange-grad); color: #4a2e05; font-size: .74rem; font-weight: 800; line-height: 1; }
.owner-badge .ico { width: 14px; height: 14px; }

/* ---- Avatars -------------------------------------------------------------- */
.avatar {
  display: inline-grid; place-items: center; flex: 0 0 auto;
  width: 46px; height: 46px; border-radius: 16px;
  color: #fff; font-weight: 800; font-size: 1rem; letter-spacing: .02em;
  background: var(--accent-grad); box-shadow: var(--shadow-sm); position: relative;
}
.avatar.sm { width: 38px; height: 38px; border-radius: 13px; font-size: .85rem; }
.avatar.lg { width: 64px; height: 64px; border-radius: 22px; font-size: 1.4rem; }
.avatar.g1 { background: linear-gradient(135deg, #1f6dff, #0d4fd6); }
.avatar.g2 { background: linear-gradient(135deg, #1565ff, #16294f); }
.avatar.g3 { background: linear-gradient(135deg, #0e9aa7, #1565ff); }
.avatar.g4 { background: linear-gradient(135deg, #ffb347, #f59324); color: #4a2e05; }
.avatar.g5 { background: linear-gradient(135deg, #16a34a, #0e9aa7); }

/* ---- For Me — premium mobile dashboard ------------------------------------ */
/* No full-bleed header: the page opens straight into useful, layered cards that
   float on the tinted app background, the way a native iOS/Android app would. */

/* Identity header: photo · greeting/name · live date + time, with the action icons on their own
   row. Laid out with flex-wrap so nothing ever has to fight for horizontal space — the greeting and
   name always keep their room, and the icons drop to a full-width row beneath on phones. */
.fm-id { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; margin: 2px 2px 20px; }
.fm-id-photo { flex: 0 0 auto; }
.fm-id-photo .avatar {
  width: 50px; height: 50px; border-radius: 50%; font-size: 1.12rem;
  box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px rgba(21, 101, 255, .16), var(--shadow-sm);
}
.fm-id-text { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
.fm-id-greet { font-size: .88rem; font-weight: 600; color: var(--muted); white-space: nowrap; }
/* The member's first name is always shown in full — never clipped to an ellipsis or a lone initial.
   It scales with the viewport and, only as a last resort on the narrowest devices, wraps to a second
   line rather than overflowing or being cut off. */
.fm-id-name {
  font-family: var(--font-display); font-weight: 800;
  font-size: clamp(1.12rem, 4.6vw, 1.32rem);
  line-height: 1.14; letter-spacing: -.02em; color: var(--ink);
  max-width: 100%; overflow-wrap: anywhere;
}
.fm-id-when { flex: 0 0 auto; display: flex; flex-direction: column; align-items: flex-end; text-align: right; }
.fm-id-date { font-size: .8rem; font-weight: 600; color: var(--muted); white-space: nowrap; }
.fm-id-time {
  font-family: var(--font-display); font-weight: 700; font-size: 1.04rem;
  color: var(--ink-soft); font-variant-numeric: tabular-nums; letter-spacing: -.01em; white-space: nowrap;
}
/* Action icons (Gallery · Notifications · Reminders · Refresh) sit on their own full-width row below
   the greeting and clock, evenly spaced so they never crowd or overlap the name on narrow phones.
   Every button is the same size for consistent alignment. On wider screens they tuck up beside the
   date/time (see the min-width rule below). */
.fm-id-actions { flex: 1 0 100%; display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.fm-hdr-btn {
  flex: 0 0 auto; width: 44px; height: 44px; display: grid; place-items: center;
  border: 1px solid var(--line-strong); border-radius: 13px; background: var(--surface);
  color: var(--ink-soft); cursor: pointer; box-shadow: var(--shadow-sm);
  transition: transform .14s var(--ease), color .2s, border-color .2s, background-color .2s;
}
.fm-hdr-btn .ico { width: 20px; height: 20px; }
.fm-hdr-btn:hover { color: var(--primary); border-color: var(--primary); transform: translateY(-1px); }
.fm-hdr-btn:active { transform: translateY(0); }
/* Refresh keeps the round icon-btn shape but carries the blue accent shown in the design. */
.fm-id-actions .mobile-refresh.fm-hdr-refresh {
  width: 44px; height: 44px; border-radius: 50%; margin: 0; flex: 0 0 auto;
  color: var(--primary); border-color: rgba(21, 101, 255, .4);
}
.fm-id-actions .mobile-refresh.fm-hdr-refresh:hover { background: var(--soft); border-color: var(--primary); }
/* Roomier layout: the header fits on a single row, so the icons align right beside the date/time. */
@media (min-width: 560px) {
  .fm-id-actions { flex: 0 0 auto; justify-content: flex-end; }
}

/* Verse of the Day — a compact card directly under the header: text on the left, an inspirational
   image on the right. The image has a soft gradient backdrop so it still reads well before it loads
   or if the network is unavailable. */
.fm-verse {
  display: flex; align-items: stretch; overflow: hidden;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm); margin: 0 2px 26px; min-height: 132px;
}
.fm-verse-body { flex: 1 1 auto; min-width: 0; padding: 16px 4px 16px 18px; display: flex; flex-direction: column; }
.fm-verse-label {
  display: inline-flex; align-items: center; gap: 7px; text-transform: uppercase;
  letter-spacing: .1em; font-size: .68rem; font-weight: 800; color: var(--ink);
}
.fm-verse-label .ico { width: 15px; height: 15px; color: var(--orange-strong); }
/* The verse is shown in full — never clamped, never abbreviated with an ellipsis. The card grows to
   fit long passages so the whole scripture is always readable. */
.fm-verse-text {
  position: relative; margin: 10px 0 0; padding-left: 16px; flex: 1 1 auto;
  font-family: var(--font-display); font-weight: 700; font-size: .98rem; line-height: 1.34;
  letter-spacing: -.01em; color: var(--ink); overflow-wrap: break-word;
}
.fm-verse-text::before {
  content: "\201C"; position: absolute; left: -2px; top: -6px;
  font-family: Georgia, "Times New Roman", serif; font-size: 2rem; line-height: 1;
  color: var(--orange-strong); font-weight: 700;
}
.fm-verse-ref {
  margin-top: 10px; padding-left: 16px; font-style: normal; font-weight: 800; font-size: .9rem;
  color: var(--primary);
}
.fm-verse-ref::after {
  content: ""; display: block; width: 34px; height: 3px; margin-top: 9px; border-radius: 3px;
  background: var(--orange-strong);
}
.fm-verse-media {
  position: relative;
  flex: 0 0 40%; max-width: 220px; align-self: stretch;
  background-color: var(--soft);
  background-image: linear-gradient(120deg, rgba(255, 179, 71, .35), rgba(21, 101, 255, .18));
  overflow: hidden;
}
.fm-verse-media img { display: block; width: 100%; height: 100%; object-fit: cover; }
.fm-verse-media::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg, rgba(11, 24, 39, .2), rgba(11, 24, 39, .05));
  pointer-events: none;
}
/* Keep the verse card compact and the action buttons comfortably tappable on phones. The icons have
   their own row now, so they no longer need to shrink to avoid crowding the name. */
@media (max-width: 480px) {
  .fm-id { gap: 10px; }
  .fm-hdr-btn, .fm-id-actions .mobile-refresh.fm-hdr-refresh { width: 42px; height: 42px; }
  .fm-hdr-btn .ico { width: 19px; height: 19px; }
  .fm-id-date { font-size: .76rem; }
  .fm-id-time { font-size: .98rem; }
  .fm-verse { min-height: 120px; }
  .fm-verse-body { padding: 14px 4px 14px 15px; }
  .fm-verse-text { font-size: .92rem; }
}
@media (max-width: 380px) {
  .fm-id-photo .avatar { width: 44px; height: 44px; }
  .fm-hdr-btn, .fm-id-actions .mobile-refresh.fm-hdr-refresh { width: 40px; height: 40px; }
  .fm-hdr-btn .ico { width: 18px; height: 18px; }
  .fm-verse-media { flex-basis: 34%; }
}


/* Featured event — the hero card. Gradient fill, layered sheen, deep soft shadow. */
.fm-feature {
  position: relative; display: block; width: 100%; text-align: left;
  border: 0; cursor: pointer; overflow: hidden; color: #fff;
  border-radius: var(--radius-xl); padding: 22px 22px 24px; margin-bottom: 30px;
  transition: transform .22s var(--ease), box-shadow .22s var(--ease);
}
.fm-feature:active { transform: scale(.985); }
.fm-feature.feat-blue {
  background: radial-gradient(125% 135% at 0% 0%, #2f7bff 0%, #1565ff 50%, #0c47c2 100%);
  box-shadow: 0 20px 44px -16px rgba(21, 101, 255, .6), var(--shadow-md);
}
.fm-feature.feat-amber {
  background: radial-gradient(125% 135% at 0% 0%, #ffc062 0%, #f7991f 52%, #e9760c 100%);
  box-shadow: 0 20px 44px -16px rgba(233, 118, 12, .5), var(--shadow-md);
}
.fm-feature-sheen {
  position: absolute; inset: 0; pointer-events: none;
  background:
    radial-gradient(75% 65% at 88% -12%, rgba(255, 255, 255, .32), transparent 60%),
    linear-gradient(180deg, rgba(255, 255, 255, .07), rgba(0, 0, 0, .12));
}
.fm-feature-top { position: relative; z-index: 1; display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px; }
.fm-feature-kicker { text-transform: uppercase; letter-spacing: .15em; font-size: .72rem; font-weight: 800; color: rgba(255, 255, 255, .82); }
.fm-feature-badge {
  width: 42px; height: 42px; border-radius: 14px; display: grid; place-items: center;
  background: rgba(255, 255, 255, .18); box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .25);
}
.fm-feature-badge .ico { width: 22px; height: 22px; color: #fff; }
.fm-feature-title {
  position: relative; z-index: 1; display: block;
  font-family: var(--font-display); font-weight: 800; font-size: 1.6rem; line-height: 1.14;
  letter-spacing: -.02em; color: #fff; margin-bottom: 16px;
}
.fm-feature-rows { position: relative; z-index: 1; display: flex; flex-wrap: wrap; gap: 8px; }
.fm-feature-row {
  display: inline-flex; align-items: center; gap: 7px;
  background: rgba(255, 255, 255, .17); border-radius: 999px; padding: 7px 13px 7px 11px;
  font-size: .92rem; font-weight: 600; color: #fff; box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .16);
}
.fm-feature-row b { font-weight: 800; }
.fm-feature-row .ico { width: 16px; height: 16px; opacity: .92; }
/* Calm fallback when nothing is scheduled */
.fm-feature.feat-quiet {
  background: linear-gradient(160deg, var(--surface) 0%, var(--soft) 130%);
  color: var(--ink); border: 1px solid var(--line); box-shadow: var(--shadow-sm);
}
.fm-feature.feat-quiet .fm-feature-kicker { color: var(--muted); }
.fm-feature.feat-quiet .fm-feature-title { color: var(--ink); margin-bottom: 6px; }
.fm-feature-empty { position: relative; z-index: 1; display: block; color: var(--muted); font-weight: 500; }

/* Section headings */
.fm-sec { margin-bottom: 34px; }
.fm-sec-head { display: flex; align-items: baseline; justify-content: space-between; margin: 0 4px 15px; }
.fm-sec-title { font-family: var(--font-display); font-size: 1.14rem; font-weight: 700; color: var(--ink); letter-spacing: -.01em; }
.fm-sec-link { background: none; border: 0; padding: 4px 2px; cursor: pointer; color: var(--primary); font-weight: 700; font-size: .9rem; }
.fm-sec-link:hover { text-decoration: underline; }
.fm-list { display: flex; flex-direction: column; gap: 16px; }

/* Layered list cards (events + announcements) */
.fm-card {
  display: flex; align-items: center; gap: 14px; width: 100%; text-align: left; cursor: pointer;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg);
  padding: 16px; box-shadow: var(--shadow-sm);
  transition: transform .18s var(--ease), box-shadow .18s var(--ease), border-color .18s var(--ease);
}
.fm-card:hover { transform: translateY(-2px); box-shadow: var(--shadow-md); border-color: var(--line-strong); }
.fm-card:active { transform: translateY(0) scale(.995); }
.fm-card-tile { width: 48px; height: 48px; border-radius: 15px; flex: 0 0 auto; display: grid; place-items: center; }
.fm-card-tile .ico { width: 23px; height: 23px; }
.fm-card-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.fm-card-title { font-family: var(--font-display); font-weight: 700; font-size: 1.06rem; color: var(--ink); }
.fm-card-meta { font-size: .9rem; font-weight: 500; color: var(--ink-soft); }
.fm-card-sub { font-size: .86rem; color: var(--muted); }
.fm-card-chev { flex: 0 0 auto; color: var(--muted); display: grid; place-items: center; }
.fm-card-chev .ico { width: 20px; height: 20px; }

/* Announcement card stacks its preview text and aligns to the top */
.fm-ann-card { align-items: flex-start; }
.fm-ann-card .fm-card-tile { margin-top: 1px; }
.fm-ann-text {
  font-size: .92rem; color: var(--muted); line-height: 1.45; margin: 1px 0 2px;
  display: -webkit-box; -webkit-line-clamp: 2; line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}

/* Bible study reminder — a gently tinted gradient card so it reads as a standout */
.fm-bible {
  display: flex; align-items: center; gap: 14px; width: 100%; text-align: left; cursor: pointer;
  border: 1px solid rgba(21, 101, 255, .18); border-radius: var(--radius-lg); padding: 15px 16px;
  background: linear-gradient(135deg, var(--soft) 0%, var(--surface) 78%);
  box-shadow: var(--shadow-sm);
  transition: transform .18s var(--ease), box-shadow .18s var(--ease);
}
.fm-bible:hover { transform: translateY(-2px); box-shadow: var(--shadow-md); }
.fm-bible:active { transform: translateY(0) scale(.995); }
.fm-bible-tile {
  width: 48px; height: 48px; border-radius: 15px; flex: 0 0 auto; display: grid; place-items: center;
  background: var(--accent-grad); color: #fff; box-shadow: var(--shadow-blue);
}
.fm-bible-tile .ico { width: 23px; height: 23px; }

/* Shared tile tones for cards + quick actions */
.fmt-blue { background: var(--soft); color: var(--soft-ink); }
.fmt-amber { background: var(--orange-soft); color: var(--orange-strong); }

/* Empty-state line */
.fm-empty {
  color: var(--muted); font-size: .94rem; padding: 16px; text-align: center;
  background: var(--surface); border: 1px dashed var(--line-strong); border-radius: var(--radius-lg);
}

/* Quick actions — soft cards in a 4-up grid */
.fm-qa-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 11px; }
.fm-qa {
  display: flex; flex-direction: column; align-items: center; gap: 9px; cursor: pointer;
  background: var(--surface); border: 1px solid var(--line); border-radius: 18px; padding: 15px 6px 12px;
  box-shadow: var(--shadow-sm);
  transition: transform .16s var(--ease), box-shadow .16s var(--ease), border-color .16s var(--ease);
}
.fm-qa:hover { transform: translateY(-3px); box-shadow: var(--shadow-md); border-color: var(--line-strong); }
.fm-qa:active { transform: translateY(0) scale(.97); }
.fm-qa-ic { width: 44px; height: 44px; border-radius: 14px; display: grid; place-items: center; }
.fm-qa-ic .ico { width: 22px; height: 22px; }
.fm-qa-label { font-size: .74rem; font-weight: 700; color: var(--ink-soft); text-align: center; line-height: 1.2; }

/* ---- Event cards (planner day/week views) --------------------------------- */
.event-card {
  display: flex; gap: 0; align-items: stretch; overflow: hidden;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm); transition: transform .16s var(--ease), box-shadow .18s var(--ease), border-color .18s;
}
.event-card.tappable { cursor: pointer; }
.event-card.tappable:hover { transform: translateY(-2px); box-shadow: var(--shadow-md); border-color: var(--line-strong); }
.event-card .stripe { width: 6px; flex: 0 0 auto; background: var(--c-blue); }
.event-card.important .stripe { background: var(--c-orange); }
.event-card .body { padding: 15px 16px; flex: 1; min-width: 0; }
.event-card h3 { margin-bottom: 8px; }
.event-card .meta-row { display: flex; flex-wrap: wrap; gap: 6px 14px; color: var(--muted); font-size: .86rem; }
.event-card .meta-row span { display: inline-flex; align-items: center; gap: 5px; }
.event-card .meta-row .ico { width: 15px; height: 15px; }

/* ---- Generic list items / rows -------------------------------------------- */
.list { display: grid; gap: 16px; }
.row-item {
  display: flex; align-items: center; gap: 14px; padding: 17px 16px;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm); cursor: pointer; text-align: left; width: 100%;
  transition: transform .16s var(--ease), box-shadow .18s var(--ease), border-color .18s;
}
.row-item:hover { transform: translateY(-2px); box-shadow: var(--shadow-md); border-color: var(--line-strong); }
.row-item .title { font-weight: 700; font-size: 1rem; display: block; }
.row-item .sub { color: var(--muted); font-size: .86rem; margin-top: 3px; display: block; }
.row-item .meta { margin-left: auto; text-align: right; display: flex; flex-direction: column; align-items: flex-end; gap: 6px; flex: 0 0 auto; }
.row-item .chev { color: var(--muted); flex: 0 0 auto; }

/* ---- Feed cards (announcements) ------------------------------------------- */
.feed-card { position: relative; }
.feed-card .head { display: flex; align-items: center; gap: 11px; margin-bottom: 12px; }
.feed-card .by { font-weight: 700; font-size: .92rem; }
.feed-card .when { color: var(--muted); font-size: .8rem; }
.feed-card h3 { margin-bottom: 8px; }
.feed-card .preview { color: var(--ink-soft); display: -webkit-box; -webkit-line-clamp: 3; line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
.feed-card .foot { display: flex; align-items: center; gap: 10px; margin-top: 15px; }
/* Pin state is a pure CSS toggle so Pin/Unpin can flip instantly without re-rendering the card:
   the header flag and the "Pinned" pill are always in the DOM and simply hidden until the
   `.pinnable` container gains `.is-pinned`. Applies to the announcement list card, the detail page
   and any future pinnable surface. */
.pinnable:not(.is-pinned) .pin-flag,
.pinnable:not(.is-pinned) .pinned-pill { display: none; }
.star-btn { background: none; border: 0; cursor: pointer; color: var(--muted); padding: 6px; border-radius: 10px; display: inline-grid; place-items: center; transition: color .18s, transform .15s; }
.star-btn:hover { transform: scale(1.12); }
.star-btn.on { color: var(--orange-strong); }
.unread-rail { position: absolute; left: 0; top: 16px; bottom: 16px; width: 4px; border-radius: 4px; background: var(--orange-grad); }

/* ---- Segmented control / filter chips ------------------------------------- */
.segmented {
  display: inline-flex; gap: 4px; padding: 5px; border-radius: 999px;
  background: var(--surface-inset); border: 1px solid var(--line); width: 100%;
  overflow-x: auto; scrollbar-width: none;
}
.segmented::-webkit-scrollbar { display: none; }
.segmented button {
  flex: 1 0 auto; min-height: 40px; padding: 0 14px; border: 0; border-radius: 999px; background: transparent;
  color: var(--muted); font-weight: 700; font-size: .86rem; cursor: pointer; white-space: nowrap;
  transition: color .2s, background-color .2s, box-shadow .2s;
}
.segmented button.active { background: var(--surface); color: var(--primary); box-shadow: var(--shadow-sm); }

.chips { display: flex; gap: 8px; overflow-x: auto; padding-bottom: 2px; scrollbar-width: none; }
.chips::-webkit-scrollbar { display: none; }
.chip {
  flex: 0 0 auto; min-height: 40px; padding: 0 16px; border-radius: 999px; cursor: pointer;
  border: 1px solid var(--line-strong); background: var(--surface); color: var(--ink-soft);
  font-weight: 700; font-size: .86rem; display: inline-flex; align-items: center; gap: 7px;
  transition: all .18s var(--ease);
}
.chip .ico { width: 16px; height: 16px; }
.chip.active { background: var(--accent-grad); color: #fff; border-color: transparent; box-shadow: var(--shadow-blue); }

/* ---- Page header (back bar) ----------------------------------------------- */
.page-head { display: flex; align-items: center; gap: 12px; margin-bottom: 24px; max-width: 100%; }
.page-head > .grow { min-width: 0; }
.page-head .mobile-refresh { margin-left: auto; flex: 0 0 46px; }
.page-head h1 { font-size: 1.35rem; }
.page-head .sub { color: var(--muted); font-size: .85rem; margin-top: 2px; }
.page-head.dm-head { margin-bottom: 14px; }
.dm-head .icon-btn { width: 42px; height: 42px; }

/* Chat screens (group chat, direct messages) pin their header — the Back button, avatar and
   title — to the top of the viewport so it stays reachable no matter how far up the message
   history is scrolled. The message list keeps scrolling underneath it. */
.page-head.chat-head {
  position: sticky;
  top: 0;
  z-index: 20;
  /* Span the FULL viewport width, not just the app's centered container. The container is capped at
     max-width:640px and centred, so on any screen wider than that (large phones, foldables, tablets,
     landscape) a bar pulled out only to the container edges left the page's blue background showing
     in the top corners — the "blue piece behind the header". Pulling the bar to the true viewport
     edges with 100vw + calc(50% - 50vw) side margins makes it reach both screen edges everywhere, so
     nothing shows beside or behind it. Top margin still cancels the container's top padding so the
     bar meets the status bar; inner padding re-adds room to clear the notch and side safe areas.
     body has overflow-x:hidden, so 100vw never introduces a horizontal scrollbar. The side padding is
     --shell-pad, the same inset the rest of the app uses, so the Back button and the right-hand
     controls sit comfortably inside both screen edges instead of grazing them. */
  width: 100vw;
  max-width: 100vw;
  margin: calc(-1 * (20px + env(safe-area-inset-top))) calc(50% - 50vw) 8px;
  padding: calc(10px + env(safe-area-inset-top)) calc(var(--shell-pad) + env(safe-area-inset-right)) 8px calc(var(--shell-pad) + env(safe-area-inset-left));
  background: var(--bg);
  background-image:
    radial-gradient(1100px 560px at 0% -10%, var(--bg-grad-1), transparent 60%),
    radial-gradient(900px 560px at 110% 0%, var(--bg-grad-2), transparent 55%);
  background-attachment: scroll;
  border-bottom: 1px solid var(--line);
}

/* Group screens sit inside the app's centered container, but their top bars must behave
   like app chrome: full viewport width with safe-area padding inside the bar, never as an outer
   width constraint. */
.container.group-screen .page-head {
  position: sticky;
  top: 0;
  z-index: 20;
  width: 100vw;
  width: 100dvw;
  max-width: 100vw;
  max-width: 100dvw;
  box-sizing: border-box;
  margin: calc(-1 * (20px + env(safe-area-inset-top))) calc(50% - 50vw) 16px;
  padding: calc(20px + env(safe-area-inset-top)) calc(16px + env(safe-area-inset-right)) 12px calc(16px + env(safe-area-inset-left));
  background: var(--bg);
  background-image:
    radial-gradient(1100px 560px at 0% -10%, var(--bg-grad-1), transparent 60%),
    radial-gradient(900px 560px at 110% 0%, var(--bg-grad-2), transparent 55%);
  background-attachment: scroll;
  border-bottom: 1px solid var(--line);
}
.container.group-screen .page-head.chat-head {
  margin-right: calc(50% - 50vw);
  margin-left: calc(50% - 50vw);
  margin-bottom: 8px;
  padding-top: calc(10px + env(safe-area-inset-top));
  padding-right: calc(var(--shell-pad) + env(safe-area-inset-right));
  padding-bottom: 8px;
  padding-left: calc(var(--shell-pad) + env(safe-area-inset-left));
}

/* ---- Compact chat header (permanent layout rule) ---------------------------
   Every chat header — group chat and direct messages — is ONE short bar, never a tall block:

     [back][avatar] Group name stretched across the free width   [members][refresh]
                    35 members · Group Chat

   The title owns every pixel the avatar and the right-hand controls do not need, so a name like
   "Group Leader & Unit Leader" runs across the bar instead of breaking after two words. When a name
   genuinely cannot fit the bar it wraps onto a SECOND line and that line uses the same full width
   (only a name longer than two full lines is ellipsised), so nothing breaks early or disappears
   behind a "…". "35 members · Group Chat" stays on its own single line underneath. The right-hand
   controls are small and tucked together at the top-right so they can never make the bar taller than
   its text. Everything is vertically centred on one baseline grid, so the bar reads as a straight
   line on every phone, tablet, and desktop width. */
.page-head.chat-head { gap: 5px; align-items: center; }
/* The controls are deliberately smaller than the app's default 46px circle: at 46px each, two of them
   plus the back button and avatar ate over half a phone's width, which is what forced the title to
   wrap. They stay at/above a comfortable 38px tap target. */
.page-head.chat-head .icon-btn { width: 38px; height: 38px; }
.page-head.chat-head .icon-btn .ico { width: 19px; height: 19px; }
/* Members / Refresh (and any future header control) share one tight cluster pinned to the right.
   Sized to its contents and never stretched, so it adds width — not height — to the bar. The small
   left padding is the breathing room between the title and the first control, so the title can run
   right up to that gap and no further. */
.page-head.chat-head .head-actions {
  display: flex; align-items: center; gap: 3px; flex: 0 0 auto; margin-left: auto; padding-left: 4px;
}
.page-head.chat-head .head-actions .mobile-refresh { margin-left: 0; flex: 0 0 auto; }
.page-head.chat-head .mobile-refresh { flex: 0 0 38px; }
.page-head.chat-head .mobile-refresh .ico { width: 19px; height: 19px; }
/* The title column takes every pixel the avatar and controls don't need. */
.page-head.chat-head > .grow { flex: 1 1 auto; min-width: 0; max-width: 100%; }
/* Full available width, on one line where it fits and a clean two lines where it doesn't. The
   line-clamp keeps the wrap at two lines (a third would start pushing the bar into the thread) and
   `overflow-wrap: anywhere` only ever applies to a single unbroken word wider than the bar, so normal
   names break at their spaces and each line runs the whole width of the column. */
.page-head.chat-head h1 {
  font-size: clamp(.88rem, 3.75vw, 1.1rem);
  line-height: 1.18; letter-spacing: -.02em;
  white-space: normal; overflow-wrap: anywhere;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden;
}
/* "35 members · Group Chat" stays one continuous line, ellipsised only if the device is too narrow
   for it — it must never wrap into a second line and lengthen the header. */
.page-head.chat-head .sub {
  margin-top: 1px; font-size: .78rem; line-height: 1.25;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.page-head.chat-head .grp-chat-ava,
.page-head.chat-head .avatar.sm { width: 32px; height: 32px; border-radius: 10px; font-size: .74rem; }
.page-head.chat-head .grp-chat-ava .ico { width: 17px; height: 17px; }

/* ---- Conversation list + chat -------------------------------------------- */
/* width:100% + text-align:left match the other card-buttons (.row-item, .grp-card, .cat-card): a
   <button> with display:flex but no width shrink-wraps to its content, so a long preview line would
   set the button's width and stretch the whole row. Pinning it to 100% keeps every row the same
   width and lets the text column (min-width:0) clamp the preview instead. */
.conv-row { display: flex; align-items: center; gap: 13px; padding: 16px 12px; width: 100%; max-width: 100%; overflow: hidden; text-align: left; border-radius: var(--radius-lg); cursor: pointer; transition: background-color .16s; }
.conv-row:not(:last-child) { border-bottom: 1px solid var(--line); }
.conv-row:hover { background: var(--surface-2); }
/* The text column: a min-width:0 flex column so its children can shrink below their content width
   and truncate (a flex/grid item defaults to min-width:auto, which would otherwise refuse to shrink
   and push the row wider). */
.conv-row .grow { display: flex; flex: 1 1 0; flex-direction: column; min-width: 0; max-width: 100%; overflow: hidden; }
/* min-width:0 lets a long name truncate inside its space-between row instead of shoving the time off. */
.conv-row .name { font-weight: 700; min-width: 0; }
/* Block-level so overflow/text-overflow actually apply: an inline span ignores them, letting a long
   message render full-width on one nowrap line and stretch the row. As a block it stays inside the
   flex column (min-width:0) and truncates to a single line with an ellipsis — the full text is only
   read after opening the conversation. */
.conv-row .last { display: block; width: 100%; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--muted); font-size: .88rem; margin-top: 2px; }
.conv-row .last.unread { color: var(--ink); font-weight: 600; }
.conv-row .when { color: var(--muted); font-size: .76rem; white-space: nowrap; }
.conv-row .badge { background: var(--orange-grad); color: #4a2e05; }
.dm-list {
  display: grid;
  gap: 8px;
  width: 100%;
  max-width: 100%;
}
.dm-list .conv-row {
  min-height: 70px;
  gap: 11px;
  padding: 11px 12px;
  border: 1px solid var(--line);
  background: var(--surface);
  box-shadow: var(--shadow-sm);
}
.dm-list .conv-row:not(:last-child) { border-bottom: 1px solid var(--line); }
.dm-list .conv-row:hover { background: var(--surface-2); border-color: var(--line-strong); }
.dm-list .conv-row > .avatar {
  width: 42px;
  height: 42px;
  border-radius: 14px;
  font-size: .92rem;
  box-shadow: 0 0 0 1px rgba(255, 255, 255, .08), var(--shadow-sm);
}
.dm-list .conv-row .name {
  color: var(--ink);
  font-weight: 800;
  font-size: .94rem;
}
.dm-list .conv-row .last {
  color: var(--ink-soft);
  font-size: .83rem;
  margin-top: 1px;
}
.dm-list .conv-row .last.unread { color: var(--ink); font-weight: 700; }
.dm-list .conv-row .when { flex: 0 0 auto; color: var(--muted); font-size: .72rem; }
[data-theme="dark"] .dm-list .conv-row {
  background: linear-gradient(180deg, #121d39 0%, #0f1830 100%);
  border-color: #243151;
}
[data-theme="dark"] .dm-list .conv-row:hover {
  background: linear-gradient(180deg, #172442 0%, #121d39 100%);
  border-color: #31416a;
}
[data-theme="dark"] .dm-search input {
  background: #111c36;
  border-color: #243151;
}

/* The thread is deliberately dense (GroupMe-style): consecutive messages sit close together so a few
   messages don't fill the whole screen, while bubbles keep enough padding to stay readable and every
   control keeps a comfortable tap target. The side padding is not decoration — the thread is the
   scroll container, so anything that reaches past its padding box (an avatar's soft shadow, a
   bubble's) is clipped by the scroller. --chat-edge keeps every avatar and card fully inside it. */
.chat-thread { display: flex; flex-direction: column; gap: 3px; padding: 5px var(--chat-edge) 20px; }
.day-sep { text-align: center; margin: 8px 0 5px; }
.day-sep span { background: var(--surface-inset); color: var(--muted); font-size: .74rem; font-weight: 700; padding: 3px 10px; border-radius: 999px; }
.bubble-row { display: flex; margin-top: 0; }
.bubble-row.mine { justify-content: flex-end; }
.bubble {
  max-width: 82%; padding: 8px 12px; border-radius: 17px; font-size: .98rem; line-height: 1.38;
  word-wrap: break-word; overflow-wrap: anywhere; box-shadow: var(--shadow-sm);
  animation: pop .22s var(--ease);
  /* A press-and-hold on a bubble opens Unify's reaction/action menu (see attachReactionLongPress),
     so the browser must not hijack that gesture with its own text-selection handles or callout.
     Suppress selection, dragging, and the OS long-press menu on the bubble and its text. Vertical
     scrolling is untouched (no touch-action override) and links stay tappable (see .bubble a below). */
  -webkit-user-select: none; user-select: none;
  -webkit-user-drag: none; -webkit-touch-callout: none;
}
.bubble[data-reply-id] { position: relative; transition: transform .18s var(--ease), box-shadow .18s var(--ease); }
.bubble[data-reply-id]::before {
  content: "↩"; position: absolute; right: calc(100% + 12px); top: 50%; transform: translateY(-50%) scale(.75);
  width: 30px; height: 30px; border-radius: 999px; display: grid; place-items: center;
  background: var(--primary); color: #fff; font-size: 1.05rem; font-weight: 900; opacity: 0;
  box-shadow: var(--shadow-sm); transition: opacity .12s ease, transform .12s ease;
}
.bubble.reply-swiping::before { opacity: .55; transform: translateY(-50%) scale(.9); }
.bubble.reply-armed::before { opacity: 1; transform: translateY(-50%) scale(1); }
.reply-ref {
  width: 100%; min-width: 0; margin: 0 0 4px; padding: 5px 8px; border: 0; border-left: 3px solid currentColor;
  border-radius: 8px; background: rgba(255,255,255,.2); color: inherit; text-align: left; cursor: pointer;
  display: grid; gap: 2px;
}
.reply-ref b { font-size: .73rem; }
.reply-ref span { font-size: .76rem; opacity: .82; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Links inside a bubble stay fully tappable — a short tap follows the link, a long-press still
   opens the action menu. They opt back out of the callout suppression only enough to behave as
   normal links; selection stays off so a hold never drops selection handles on the URL text. */
.bubble a { -webkit-touch-callout: none; }
.bubble-row.theirs .bubble { background: var(--bubble-theirs); color: var(--bubble-theirs-ink); border-bottom-left-radius: 7px; }
.bubble-row.mine .bubble { background: var(--bubble-mine); color: var(--bubble-mine-ink); border-bottom-right-radius: 7px; }
/* The sender-change gap belongs to the message wrapper (.msg.tail-gap) — the last remnant of the old
   8px band on .bubble-row is gone, so nothing can reintroduce the pre-compact spacing. */
.bubble .stamp { display: block; font-size: .67rem; margin-top: 0; opacity: .72; }
.bubble-row.mine .stamp { text-align: right; }
/* Small "Edited" marker shown in a message's stamp once its text has been changed by its author. */
.edited-tag { font-style: italic; opacity: .85; }
.edited-tag::before { content: "· "; }
.read-tick { display: inline-flex; align-items: center; gap: 3px; }
.read-tick .ico { width: 14px; height: 14px; }
/* Direct-message delivery status ticks (Sending → Sent → Delivered → Read). */
.msg-status { display: inline-flex; align-items: center; vertical-align: middle; margin-left: 3px; padding: 0; border: 0; background: none; }
.msg-status .ico { width: 14px; height: 14px; }
.msg-status.pending { opacity: .6; }
.msg-status.sent, .msg-status.delivered { opacity: .85; }
.msg-status.read { color: #3b82f6; opacity: 1; }
.msg-status.failed { color: #e5484d; opacity: 1; cursor: pointer; }

/* ---- Message wrapper + emoji reactions ------------------------------------ */
/* Group chat and DMs wrap each message in a .msg column so the reaction row sits directly under the
   bubble. The column stretches full width (like the old bare .bubble-row did) so the bubble's
   max-width:82% still resolves correctly; the bubble-row and reaction-row align themselves left or
   right via justify-content. (Category chat reuses its existing .bubble-group wrapper.) */
.msg { display: flex; flex-direction: column; }
.msg.tail-gap { margin-top: 4px; }

/* The row under a bubble holding reaction-count chips and the small add-reaction button. It stays
   hidden-until-useful: the + button is faint until hovered/focused so the thread doesn't feel busy.
   It hugs the bubble above it — a reaction always reads as belonging to that message, never as a
   floating control in a blank gap. */
.rxn-row { display: flex; flex-wrap: wrap; align-items: center; gap: 3px; margin-top: 1px; }
.rxn-row.mine { justify-content: flex-end; }
.rxn-row.theirs { justify-content: flex-start; }
/* Line up with the bubble above by clearing exactly the avatar column — one shared value, so the
   reaction row can never drift out of alignment when the avatar or its gap is retuned. */
.bubble-group.theirs .rxn-row, .msg.theirs .rxn-row { padding-left: var(--chat-ava-col); }
.bubble-group.mine .rxn-row { justify-content: flex-end; }
/* A message nobody has reacted to still keeps its add-reaction button (the same control, same
   behaviour) but claims almost no vertical space, so an unreacted message costs a couple of pixels
   instead of a whole empty row. Its touch target is unchanged — see .rxn-add::after. */
.rxn-row.rxn-empty { margin-top: -2px; }
.rxn-row.rxn-empty .rxn-add { width: 24px; height: 22px; }
.rxn-row.rxn-empty .rxn-add .ico { width: 16px; height: 16px; }
/* Keep the ~44px finger target on the compact variant by expanding further past its smaller box. */
.rxn-row.rxn-empty .rxn-add::after { inset: -11px -10px; }

.rxn-chip {
  display: inline-flex; align-items: center; gap: 4px; cursor: pointer;
  background: var(--surface); border: 1px solid var(--line); border-radius: 999px;
  padding: 4px 9px; font-size: .8rem; font-weight: 700; color: var(--ink-soft);
  box-shadow: var(--shadow-sm); transition: transform .1s var(--ease), border-color .12s var(--ease);
  /* Reactions must respond to a finger as reliably as a mouse: opt out of the 300ms tap delay and
     double-tap zoom so a single tap registers at once. The count keeps its own hit target (a tap
     there opens "who reacted"), so the chip carries no covering overlay that could steal that tap. */
  touch-action: manipulation; -webkit-tap-highlight-color: transparent;
}
.rxn-chip:active { transform: scale(.93); }
.rxn-chip.mine { background: var(--soft); border-color: var(--soft-ink); color: var(--soft-ink); }
.rxn-chip .rxn-e { font-size: .95rem; line-height: 1; }
/* A little padding around the count enlarges its tap area (opens the who-reacted list) without
   shifting the layout. */
.rxn-chip .rxn-n { font-variant-numeric: tabular-nums; padding: 2px 3px; margin: -2px -1px; }
.rxn-add {
  display: inline-flex; align-items: center; justify-content: center; cursor: pointer;
  width: 28px; height: 28px; border-radius: 50%; padding: 0;
  background: transparent; border: 1px solid transparent; color: var(--muted);
  opacity: .55; transition: opacity .12s var(--ease), background .12s var(--ease);
  position: relative; touch-action: manipulation; -webkit-tap-highlight-color: transparent;
}
/* Expand the smiley's touch target to ~44px (30px button + 7px all round) so it is easy to hit on
   a phone even though it stays visually small. */
.rxn-add::after { content: ""; position: absolute; inset: -7px; }
.rxn-add:hover, .rxn-add:focus-visible { opacity: 1; background: var(--surface-inset); }
.rxn-add .ico { width: 18px; height: 18px; pointer-events: none; }
/* On touch devices there is no hover, so the faint 55% opacity made the smiley look disabled — the
   member sees it but assumes it does nothing. Keep it clearly visible and obviously tappable there. */
@media (hover: none) { .rxn-add { opacity: .9; } }

/* The floating reaction picker shown above a tapped / long-pressed bubble. */
.rxn-pop {
  position: fixed; z-index: 60; display: flex; gap: 4px; padding: 7px 9px;
  background: var(--surface); border: 1px solid var(--line); border-radius: 999px;
  box-shadow: var(--shadow-md); animation: pop .16s var(--ease);
}
.rxn-opt {
  border: 0; background: transparent; cursor: pointer; border-radius: 50%;
  width: var(--touch); height: var(--touch); font-size: 1.5rem; line-height: 1; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  transition: background .12s var(--ease), transform .1s var(--ease);
}
.rxn-opt:hover { background: var(--soft); }
.rxn-opt:active { transform: scale(.88); }
/* When the long-press menu also offers Delete actions it becomes a small stacked card: the emoji
   row on top, the Delete buttons beneath a divider. */
.rxn-pop.has-del { flex-direction: column; align-items: stretch; gap: 6px; padding: 8px; border-radius: 16px; }
.rxn-pop-emojis { display: flex; gap: 4px; justify-content: center; }
.rxn-pop-del { display: flex; flex-direction: column; gap: 2px; border-top: 1px solid var(--line); padding-top: 6px; }
.rxn-del {
  display: flex; align-items: center; gap: 10px; width: 100%; text-align: left;
  border: 0; background: transparent; cursor: pointer; border-radius: 10px;
  padding: 10px 12px; font-size: .95rem; font-weight: 600; color: var(--ink);
}
.rxn-del:hover { background: var(--soft); }
.rxn-del:active { transform: scale(.97); }
/* The "+" that opens the full emoji picker sits at the end of the quick-reaction row. */
.rxn-more { color: var(--muted); }
.rxn-more svg { width: 22px; height: 22px; }
.rxn-more:hover { background: var(--soft); color: var(--ink); }

/* ---- Full emoji picker (opened by the reaction "+") -------------------------------------
   A centered sheet over a dim backdrop, matching the lightbox pattern: a pure overlay that never
   navigates or refreshes. Scrolls through categorised emoji; tapping one applies the reaction. */
.emoji-sheet-ov {
  position: fixed; inset: 0; z-index: 2000; display: flex; align-items: flex-end;
  justify-content: center; background: rgba(15, 23, 42, .5);
  padding: 0;
}
.emoji-sheet {
  width: 100%; max-width: 520px; max-height: 70vh; display: flex; flex-direction: column;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: 20px 20px 0 0; box-shadow: var(--shadow-md); animation: pop .18s var(--ease);
  padding-bottom: env(safe-area-inset-bottom);
}
@media (min-width: 560px) {
  .emoji-sheet-ov { align-items: center; padding: 16px; }
  .emoji-sheet { border-radius: 18px; }
}
.emoji-sheet-head {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 14px 16px 10px; font-weight: 700; border-bottom: 1px solid var(--line);
}
.emoji-sheet-scroll { overflow-y: auto; padding: 6px 12px 14px; -webkit-overflow-scrolling: touch; }
.emoji-sheet-group { margin-top: 8px; }
.emoji-sheet-label {
  font-size: .72rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  color: var(--muted); padding: 8px 4px 4px;
}
.emoji-sheet-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(44px, 1fr)); gap: 2px;
}
.emoji-sheet-cell {
  border: 0; background: transparent; cursor: pointer; border-radius: 10px;
  height: 44px; font-size: 1.5rem; line-height: 1; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  transition: background .12s var(--ease), transform .1s var(--ease);
}
.emoji-sheet-cell:hover { background: var(--soft); }
.emoji-sheet-cell:active { transform: scale(.88); }
.rxn-del svg, .rxn-del .ico { width: 18px; height: 18px; flex: 0 0 auto; }
.rxn-del.danger { color: var(--danger); }
.rxn-del.danger:hover { background: var(--danger-soft); }
@keyframes pop { from { opacity: 0; transform: translateY(6px) scale(.98); } to { opacity: 1; transform: none; } }

/* Typing indicator */
.typing { display: inline-flex; gap: 4px; align-items: center; padding: 12px 16px; }
.typing i { width: 7px; height: 7px; border-radius: 50%; background: var(--muted); animation: typing 1.2s infinite; }
.typing i:nth-child(2) { animation-delay: .2s; }
.typing i:nth-child(3) { animation-delay: .4s; }
@keyframes typing { 0%, 60%, 100% { opacity: .3; transform: translateY(0); } 30% { opacity: 1; transform: translateY(-3px); } }

.composer { position: sticky; bottom: calc(var(--nav-h) + env(safe-area-inset-bottom)); display: flex; gap: 10px; align-items: flex-end; padding: 10px 0 10px; background: linear-gradient(to top, var(--bg) 72%, transparent); }
/* The text field is the only flexible element: it grows to fill ALL horizontal space left of the
   action and send buttons. `flex: 1 1 0` + `width: auto` make its size come purely from the grow
   factor (NOT from the base `width:100%` form rule, which otherwise turns the flex-basis into the
   full container width and skews the layout) and `min-width: 0` lets it shrink instead of forcing
   its text to wrap a word or two at a time. The buttons stay a fixed compact size so the field —
   not the buttons — owns the row, like Signal / WhatsApp / Messenger. Height is one comfortable line
   when empty and grows with the message up to a cap (managed in JS). Explicitly no fixed width and
   no aspect-ratio, so the field can never collapse into a square. */
.composer textarea { flex: 1 1 0%; width: auto; min-width: 0; min-height: 42px; max-height: 120px; resize: none; overflow-y: auto; border-radius: 20px; padding: 10px 14px; line-height: 1.4; }
.composer .send { width: 42px; height: 42px; border-radius: 50%; flex: 0 0 auto; padding: 0; }

/* ---- Group-chat attachments, mentions & GIPHY ----------------------------- */
/* The wrap becomes the sticky element so the absolutely-positioned mention/GIPHY menus anchor to
   the composer exactly where it is rendered (the inner composer is reset to static flow). */
.composer-wrap {
  position: sticky; bottom: calc(var(--nav-h) + env(safe-area-inset-bottom)); z-index: 5;
  background: linear-gradient(to top, var(--bg) 72%, transparent);
}
.reply-compose {
  display: flex; align-items: center; gap: 9px; padding: 9px 12px; margin: 0 0 6px;
  border-left: 3px solid var(--primary); border-radius: 10px; background: var(--surface-2);
}
.reply-compose[hidden] { display: none; }
.reply-compose-icon { color: var(--primary); display: grid; place-items: center; }
.reply-compose-icon .ico { width: 18px; height: 18px; }
.reply-compose-copy { min-width: 0; flex: 1; display: grid; gap: 2px; }
.reply-compose-copy b { color: var(--primary); font-size: .78rem; }
.reply-compose-copy span { color: var(--muted); font-size: .78rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.reply-compose-close { border: 0; background: none; color: var(--muted); cursor: pointer; padding: 5px; display: grid; place-items: center; }
.reply-compose-close .ico { width: 17px; height: 17px; }
.gc-composer { position: static; bottom: auto; background: none; gap: 8px; }
/* The composer a chat paints in its opening shell, before the messages have arrived. It occupies
   exactly the space the live composer will, so nothing moves when the thread fills in; it is only
   softened and made inert until it is wired up. It never animates — this bar is fixed furniture. */
.composer-wrap.is-loading { opacity: .55; }
.composer-wrap.is-loading .gc-composer { pointer-events: none; }

/* ---- Chat / comment screen: three locked sections ------------------------- */
/* Direct and group conversations are three locked bands: header, independently scrolling message
   history, and the shared composer. The shell follows the VisualViewport height supplied by app.js,
   reserves the bottom-nav height while the keyboard is closed, and drops that reserve while the
   keyboard is open so the composer meets the keyboard edge. */
.container.chat-open {
  position: fixed;
  top: var(--vv-top, 0px);
  left: 50%;
  width: 100%;
  height: var(--vv-height, 100dvh);
  min-height: 0;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  padding-bottom: calc(var(--nav-h) + env(safe-area-inset-bottom));
  /* The chat shell carries NO side padding of its own. It clips (overflow:hidden here and on #page),
     so any band that tried to reach past that padding — the full-width chat header — had its outer
     strip sliced off, and that strip is where the Back button and the members / Refresh controls sit.
     Each band now owns its horizontal inset instead (header, thread, composer below), which is both
     unclippable and a single place to tune the edge spacing. */
  padding-left: 0;
  padding-right: 0;
}
.kb-open .container.chat-open { padding-bottom: 0; }
.container.chat-open #page {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
}
/* Let the message region consume all space between the fixed header and composer. */
.container.chat-open .chat-thread,
.container.chat-open .gal-comments {
  flex: 1 1 auto;
  min-height: 0;
}
/* The shell hands its side padding to each band, so a comment list that ends up inside the chat shell
   keeps the same inset it has everywhere else instead of running to the screen edges. */
.container.chat-open .gal-comments {
  padding-left: calc(var(--shell-pad) + env(safe-area-inset-left));
  padding-right: calc(var(--shell-pad) + env(safe-area-inset-right));
}
/* Chat threads own all vertical scrolling. The header and composer remain normal, non-scrolling flex
   children, so iOS cannot move either one while focusing the textarea or rubber-banding history. */
.container.chat-open .chat-thread {
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  padding-bottom: 18px;
  /* The shell no longer adds side padding, so --chat-edge is the ONLY side inset — and it is inside the
     scroll box, where a sender avatar's soft shadow needs the room. Stacked insets used to push the
     leftmost avatar against the scroller's edge and shave it; one generous inset renders it whole and
     gives the bubbles the width back. */
  margin-left: 0;
  margin-right: 0;
  padding-left: calc(var(--chat-edge) + env(safe-area-inset-left));
  padding-right: calc(var(--chat-edge) + env(safe-area-inset-right));
}
.container.chat-open .composer-wrap,
.container.chat-open .composer:not(.gc-composer) {
  position: relative;
  bottom: auto;
  flex: 0 0 auto;
  margin-top: 0;
  transform: none !important;
}
.container.chat-open .composer-wrap { background: var(--bg); }
/* The chat shell is a fixed, clipping frame, so a header that pulls itself out to 100vw is cut off BY
   that frame — the sliced strip is exactly where the Back button and the members / Refresh controls
   live. Inside the shell the bar spans the frame's full width (the shell has no side padding now) and
   carries its own comfortable inner padding, so every control sits --shell-pad inside both edges with
   nothing clipped. Vertical padding is untouched, so no button moves up or down. */
.container.chat-open .page-head.chat-head {
  flex: 0 0 auto;
  width: auto;
  max-width: none;
  margin-left: 0;
  margin-right: 0;
  padding-left: calc(var(--shell-pad) + env(safe-area-inset-left));
  padding-right: calc(var(--shell-pad) + env(safe-area-inset-right));
}
/* The composer keeps sitting exactly where it always has: a margin (not padding) so the mention /
   GIPHY / "+" popovers, which anchor to this wrapper, stay aligned with the field they belong to. */
.container.chat-open .composer-wrap {
  margin-left: calc(var(--shell-pad) + env(safe-area-inset-left));
  margin-right: calc(var(--shell-pad) + env(safe-area-inset-right));
}
.kb-open .nav { visibility: hidden; pointer-events: none; }
/* Photo / GIF action buttons in the composer — compact fixed-size circles so the text field keeps
   the bulk of the row width on a phone. */
.comp-act {
  flex: 0 0 auto; width: 42px; height: 42px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center; padding: 0;
  background: var(--surface-inset); color: var(--ink-soft); border: 1px solid var(--line);
  font-weight: 800; font-size: .82rem; cursor: pointer; transition: background .15s var(--ease), transform .1s var(--ease);
}
.comp-act:hover { background: var(--soft); color: var(--soft-ink); }
.comp-act:active { transform: scale(.94); }
.comp-act .ico { width: 20px; height: 20px; }
/* The single "+" attachment button. One compact circle replaces the old photo / emoji / GIF trio so
   the message field keeps the bulk of the row width and stays a wide, readable rectangle. */
.comp-plus .ico { width: 22px; height: 22px; }

/* The "+" attachment menu — a small popover anchored just above the "+" button (left-aligned, sized
   to its contents rather than the full row). Photo / Emoji / GIF each run the same flow as before. */
.plus-menu {
  position: absolute; left: 0; bottom: calc(100% + 6px); z-index: 30; min-width: 168px;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-md);
  box-shadow: var(--shadow-md); padding: 6px;
}
/* Author display:flex would otherwise beat the UA [hidden]{display:none}; keep `hidden` authoritative
   so the closed menu never renders as a stray box above the composer. */
.plus-menu[hidden] { display: none; }
.plus-item {
  display: flex; align-items: center; gap: 12px; width: 100%; text-align: left;
  padding: 10px 12px; border-radius: var(--radius-sm); background: transparent; border: 0;
  cursor: pointer; color: var(--ink); font-weight: 600; font-size: .95rem;
}
.plus-item:hover, .plus-item:active { background: var(--soft); color: var(--soft-ink); }
.plus-item .ico { width: 20px; height: 20px; flex: 0 0 auto; }
.plus-gif {
  flex: 0 0 auto; width: 20px; text-align: center; font-weight: 800; font-size: .72rem;
  letter-spacing: .02em;
}

/* Images inside chat bubbles */
.chat-img {
  display: block; max-width: 100%; width: auto; max-height: 280px; border-radius: 14px;
  margin: 2px 0 4px; cursor: zoom-in; background: var(--surface-inset);
  /* A tap opens the in-app viewer. Suppress the OS image callout so the device never offers to
     save, download, share, or open the GIF/photo externally. */
  -webkit-user-select: none; user-select: none;
  -webkit-user-drag: none; -webkit-touch-callout: none;
}
.chat-media-grid {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 4px;
  max-width: min(300px, 74vw); margin: 2px 0 4px;
  overflow: hidden; border-radius: 14px; background: var(--surface-inset);
}
.chat-media-grid.count-1 { display: block; max-width: min(280px, 72vw); }
.chat-media-grid.count-1 .chat-media-cell { display: block; width: auto; }
.chat-media-grid.count-1 .chat-img { width: auto; max-height: 280px; object-fit: contain; border-radius: 14px; }
.chat-media-grid.count-2 .chat-media-cell { aspect-ratio: 1 / 1; }
.chat-media-grid.count-3 .chat-media-cell:first-child { grid-row: span 2; }
.chat-media-grid.count-3 .chat-media-cell { aspect-ratio: 1 / 1; }
.chat-media-grid.count-4 .chat-media-cell,
.chat-media-grid.total-5 .chat-media-cell { aspect-ratio: 1 / 1; }
.chat-media-cell {
  position: relative; display: block; min-width: 0; padding: 0; border: 0; background: var(--surface-inset);
  overflow: hidden; cursor: zoom-in;
}
.chat-media-cell .chat-img {
  width: 100%; height: 100%; max-height: none; object-fit: cover; margin: 0; border-radius: 0;
}
.chat-more {
  position: absolute; inset: 0; display: grid; place-items: center;
  background: rgba(8, 11, 18, .58); color: #fff; font-weight: 900; font-size: 1.15rem;
  text-shadow: 0 1px 2px rgba(0,0,0,.3);
}
.chat-video {
  display: block; width: min(320px, 76vw); max-height: 360px; border-radius: 14px;
  margin: 2px 0 4px; background: #000; box-shadow: var(--shadow-sm);
}
.bubble.has-media { padding: 5px 5px 6px; }
.bubble.has-media .bubble-name { padding: 3px 7px 0; }
.bubble.has-media .stamp { padding: 0 7px; }
.bubble-text { display: block; white-space: pre-wrap; }
.bubble.has-media .bubble-text { padding: 2px 7px 0; }

/* ---- Category chat: acknowledge button + seen / acknowledged / pending receipts ---------- */
/* Each post is wrapped in a .bubble-group so the acknowledge strip lines up under its bubble. */
.bubble-group { display: flex; flex-direction: column; }
.bubble-group.mine { align-items: flex-end; }
.bubble-group.theirs { align-items: stretch; }
.ack-bar {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  margin: 4px 0 6px; max-width: 80%;
}
.bubble-group.theirs .ack-bar { padding-left: var(--chat-ava-col); } /* clear the avatar column */
.bubble-group.mine .ack-bar { justify-content: flex-end; align-self: flex-end; }
/* The green Acknowledge button recipients tap to confirm they have read a post. */
.ack-btn {
  display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
  background: var(--c-green); color: #fff; border: 0; font-weight: 800; font-size: .82rem;
  padding: 8px 16px; border-radius: 999px; box-shadow: var(--shadow-sm);
  transition: filter .15s var(--ease), transform .1s var(--ease);
}
.ack-btn:hover { filter: brightness(1.06); }
.ack-btn:active { transform: scale(.95); }
.ack-btn:disabled { opacity: .6; cursor: default; }
.ack-btn .ico { width: 16px; height: 16px; }
/* The confirmed state once a recipient has acknowledged. */
.ack-done {
  display: inline-flex; align-items: center; gap: 6px; font-weight: 800; font-size: .82rem;
  color: var(--c-green); background: rgba(22, 163, 74, .12); border: 1px solid rgba(22, 163, 74, .3);
  padding: 7px 14px; border-radius: 999px;
}
.ack-done .ico { width: 16px; height: 16px; }
/* The poster's tappable seen / acknowledged / pending tally. */
.ack-stats {
  display: inline-flex; align-items: center; gap: 10px; cursor: pointer;
  background: var(--surface-inset); border: 1px solid var(--line); color: var(--ink-soft);
  padding: 6px 12px; border-radius: 999px; font-size: .76rem; font-weight: 700;
}
.ack-stats:hover { border-color: var(--line-strong); }
.ack-stat { display: inline-flex; align-items: center; gap: 4px; }
.ack-stat .ico { width: 14px; height: 14px; }
.ack-stat.seen { color: var(--muted); }
.ack-stat.ackd { color: var(--c-green); }
.ack-stat.pend { color: #b45309; }
/* The expanded receipts list under a post. */
.receipts {
  width: 100%; margin-top: 8px; background: var(--surface); border: 1px solid var(--line);
  border-radius: 16px; padding: 8px 12px 12px; box-shadow: var(--shadow-sm);
}
.rcpt-sec + .rcpt-sec { margin-top: 8px; border-top: 1px solid var(--line); padding-top: 8px; }
.rcpt-h { display: flex; align-items: center; gap: 6px; font-weight: 800; font-size: .8rem; color: var(--ink-soft); margin: 2px 0 6px; }
.rcpt-h .ico { width: 15px; height: 15px; }
.rcpt-row { display: flex; align-items: center; gap: 10px; padding: 5px 2px; }
.rcpt-empty { color: var(--muted); font-size: .8rem; padding: 2px 2px 4px; }
.rcpt-pill { flex: 0 0 auto; font-size: .68rem; font-weight: 800; padding: 3px 9px; border-radius: 999px; }
.rcpt-pill.ok { color: var(--c-green); background: rgba(22, 163, 74, .12); }
.rcpt-pill.seen { color: var(--muted); background: var(--surface-inset); }
.rcpt-pill.pend { color: #b45309; background: rgba(180, 83, 9, .12); }

/* Staged attachment preview above the composer */
.gc-attach { padding: 8px 0 2px; }
.gc-attach-list { display: flex; flex-wrap: wrap; gap: 8px; align-items: flex-start; }
.gc-attach-card { position: relative; display: inline-block; }
.gc-attach-card img { max-height: 120px; max-width: 200px; border-radius: 12px; display: block; box-shadow: var(--shadow-sm); }
.gc-attach-card video { width: 180px; max-width: 54vw; max-height: 130px; border-radius: 12px; display: block; background: #000; box-shadow: var(--shadow-sm); }
.gc-attach-x {
  position: absolute; top: -8px; right: -8px; width: 26px; height: 26px; border-radius: 50%;
  background: var(--ink); color: #fff; border: 2px solid var(--surface); display: inline-flex;
  align-items: center; justify-content: center; cursor: pointer; padding: 0;
}
.gc-attach-x .ico { width: 14px; height: 14px; }
.upload-progress {
  display: inline-flex; align-items: center; gap: 8px; margin-top: 8px;
  color: var(--muted); font-size: .82rem; font-weight: 700;
}
.upload-progress i {
  width: 80px; height: 6px; border-radius: 999px; overflow: hidden; background: var(--surface-inset);
  position: relative; display: inline-block;
}
.upload-progress i::after {
  content: ""; position: absolute; inset: 0; width: 40%; border-radius: inherit; background: var(--primary);
  animation: uploadbar 1s infinite ease-in-out;
}
@keyframes uploadbar { 0% { transform: translateX(-100%); } 100% { transform: translateX(260%); } }

/* Determinate upload progress (video chunk upload) — a real fill that grows 0→100%. */
.vid-progress {
  display: flex; align-items: center; gap: 8px; margin-top: 8px;
  color: var(--muted); font-size: .82rem; font-weight: 700;
}
.vid-progress .track {
  flex: 1 1 auto; max-width: 160px; height: 6px; border-radius: 999px;
  overflow: hidden; background: var(--surface-inset);
}
.vid-progress .track > i {
  display: block; height: 100%; width: 0; border-radius: inherit;
  background: var(--primary); transition: width .2s ease;
}
/* A failed upload keeps its place in the composer and offers Retry (which resumes from the chunks
   that already arrived) rather than dropping the member back to an empty message. */
.vid-progress.vid-failed { flex-wrap: wrap; }
.vid-progress .vid-error { flex: 1 1 100%; color: var(--danger); font-weight: 700; }
.vid-progress.vid-failed .btn { flex: 0 0 auto; }

/* @mention autocomplete */
.mention-menu {
  position: absolute; left: 0; right: 0; bottom: calc(100% + 6px); z-index: 30;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-md);
  box-shadow: var(--shadow-md); max-height: 260px; overflow-y: auto; padding: 6px;
}
.mention-item {
  display: flex; align-items: center; gap: 10px; width: 100%; text-align: left;
  padding: 8px 10px; border-radius: var(--radius-sm); background: transparent; border: 0;
  cursor: pointer; color: var(--ink);
}
.mention-item.active, .mention-item:hover { background: var(--soft); }
.mention-name { display: block; font-weight: 700; font-size: .92rem; }
.mention-sub { display: block; font-size: .74rem; color: var(--muted); }
.mention-all {
  flex: 0 0 auto; width: 32px; height: 32px; border-radius: 50%; display: inline-flex;
  align-items: center; justify-content: center; font-weight: 900; color: #fff; background: var(--accent-grad);
}

/* GIPHY picker — modeled on Messenger / Discord / GroupMe / Signal: a compact panel with a
   pinned search bar at the top, a smooth-scrolling grid of small, separated GIF cards capped to
   about two visible rows in the middle, and a pinned "Powered by GIPHY" footer. A flex column keeps
   the head and footer fixed while only the grid scrolls. Every dimension is in px / fr / vh so
   Android, iPhone, tablets and
   the installed PWA all render the layout identically. */
.giphy-panel {
  position: absolute; left: 0; right: 0; bottom: calc(100% + 8px); z-index: 30;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
  /* Even padding on all four sides frames the grid away from the panel edges. */
  padding: 12px;
  /* The panel is sized by its contents (pinned search bar + ~2 rows of GIFs + attribution), not by a
     tall fixed height. The cap is a FIXED pixel max-height — deliberately NOT a screen percentage —
     so the picker renders at the same short height on phones, Android tablets, iPads and the
     installed PWA and never grows with the screen. Reduced from the previous 340px to keep the
     composer (text box, send / photo / emoji buttons) on-screen and easy to use. */
  max-height: 232px;
  display: flex; flex-direction: column; min-height: 0;
}
/* The panel sets display:flex, which would otherwise defeat the `hidden` attribute (author rules
   beat the UA `[hidden]{display:none}`). Without this guard the empty panel renders permanently as
   a blank white bar above the composer. */
.giphy-panel[hidden] { display: none; }
.giphy-head { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; flex: 0 0 auto; }
.giphy-head .search { margin: 0; }
/* The scrolling area: a responsive grid of small thumbnails with equal horizontal (column-gap) and
   vertical (row-gap) spacing, so no two GIFs can ever touch, stack, or overlap. Row height is fixed
   (grid-auto-rows) and the grid is capped to two rows + the inner padding, so exactly ~2 rows show
   at a time and everything past that scrolls vertically. The grid is sized by its tracks, not by the
   images, so it lays out correctly before the GIFs load and never reflows once they arrive.
   `min-height: 0` lets only this region scroll; `overscroll-behavior: contain` keeps scroll momentum
   inside the picker. Cells and the cap were both shrunk (~30% shorter overall) so the whole picker
   is far less tall while still showing two full rows. */
.giphy-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(64px, 1fr));
  grid-auto-rows: 62px;
  column-gap: 10px; row-gap: 10px;
  /* Two 62px rows + one 10px row-gap + 4px top / 6px bottom inner padding = 134px. */
  flex: 0 1 auto; min-height: 0; max-height: 134px; overflow-y: auto;
  -webkit-overflow-scrolling: touch; scroll-behavior: smooth; overscroll-behavior: contain;
  /* Inner padding so the first and last rows don't sit flush against the scroll edges. */
  padding: 4px 4px 6px; align-content: start;
}
/* Each GIF is its own small square card: rounded corners, a subtle border and a soft shadow make it
   read as a distinct, easy-to-tap tile. The fixed row track sizes the card; the whole card is the
   tap target. */
.giphy-cell {
  padding: 0; border: 1px solid var(--line); border-radius: 12px; overflow: hidden; cursor: pointer;
  background: var(--surface-inset); box-shadow: var(--shadow-sm);
  -webkit-tap-highlight-color: transparent; transition: transform .1s var(--ease);
}
.giphy-cell:active { transform: scale(.94); }
.giphy-cell img { width: 100%; height: 100%; object-fit: cover; display: block; }
.giphy-msg { grid-column: 1 / -1; text-align: center; color: var(--muted); padding: 28px 0; font-size: .9rem; }
.giphy-attr { text-align: center; color: var(--muted); font-size: .68rem; font-weight: 700; padding-top: 6px; letter-spacing: .04em; flex: 0 0 auto; }

/* Emoji keyboard — same anchored panel as the GIPHY picker, with a scrolling grid of emojis. */
.emoji-panel {
  position: absolute; left: 0; right: 0; bottom: calc(100% + 6px); z-index: 30;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-md);
  box-shadow: var(--shadow-md); padding: 10px; max-height: min(46vh, 320px);
  display: flex; flex-direction: column; min-height: 0;
}
/* Same fix as .giphy-panel: keep the `hidden` attribute authoritative so the empty emoji panel
   doesn't render as a blank white bar above the composer. */
.emoji-panel[hidden] { display: none; }
.emoji-grid {
  display: grid; grid-template-columns: repeat(6, 1fr); gap: 4px;
  overflow-y: auto; -webkit-overflow-scrolling: touch; min-height: 0; padding: 2px;
}
.emoji-cell {
  border: 0; background: transparent; cursor: pointer; border-radius: 12px;
  font-size: 1.5rem; line-height: 1; padding: 8px 0; aspect-ratio: 1 / 1;
  display: inline-flex; align-items: center; justify-content: center;
  transition: background .12s var(--ease), transform .1s var(--ease);
}
.emoji-cell:hover { background: var(--soft); }
.emoji-cell:active { transform: scale(.9); }

/* Direct-messages search results section label */
.dm-sec { font-size: .78rem; font-weight: 800; color: var(--muted); text-transform: uppercase; letter-spacing: .03em; margin: 16px 2px 8px; }


/* ---- Calendar (Planner) --------------------------------------------------- */
.cal { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-xl); padding: 14px; box-shadow: var(--shadow-sm); }
.cal-nav { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; }
.cal-nav h2 { flex: 1; text-align: center; font-size: 1.16rem; }
.cal-nav .icon-btn { width: 42px; height: 42px; }

/* Planner Day View — a duration-aware daily timeline. Hours run down the left; each timed event is a
   block spanning its full Start–End range. Overlapping events sit side by side and split the width
   evenly, so cards never overlap. Each block shows only title + Start–End time and truncates with "…"
   when long; tapping it opens the full event detail. */
.day-timeline {
  position: relative; border: 1px solid var(--line); border-radius: var(--radius-lg);
  background: var(--surface); box-shadow: var(--shadow-sm); overflow: hidden;
}
.tl-grid { position: absolute; inset: 0; }
.tl-hour { position: absolute; left: 0; right: 0; border-top: 1px solid var(--line); }
.tl-hour:first-child { border-top: 0; }
.tl-hour-label {
  position: absolute; top: -7px; left: 0; width: 52px; text-align: right; padding-right: 8px;
  font-size: .72rem; font-weight: 700; color: var(--muted); background: var(--surface); line-height: 14px;
}
/* Event layer sits to the right of the hour gutter; blocks are positioned absolutely within it. */
.tl-events { position: absolute; top: 0; bottom: 0; left: 64px; right: 6px; }
.tl-slot { position: absolute; padding: 2px 3px; box-sizing: border-box; }
.tl-slot .sched-ev { height: 100%; width: 100%; }

/* All-day lane above the timeline. */
.day-allday {
  display: flex; align-items: stretch; gap: 6px; margin-bottom: 10px; padding: 6px;
  border: 1px solid var(--line); border-radius: var(--radius-lg); background: var(--surface); box-shadow: var(--shadow-sm);
}
.day-allday-label { flex: 0 0 58px; align-self: center; text-align: right; padding-right: 10px; font-size: .72rem; font-weight: 700; color: var(--muted); }
.day-allday-lane { flex: 1 1 auto; min-width: 0; display: flex; flex-wrap: wrap; gap: 6px; }
.day-allday-lane .sched-ev { flex: 1 1 140px; }
.sched-ev {
  min-width: 0; text-align: left; cursor: pointer; border: 0; border-radius: 10px;
  padding: 6px 10px; display: flex; flex-direction: column; gap: 2px; color: #fff; background: var(--c-blue);
  box-shadow: var(--shadow-sm); transition: filter .15s var(--ease), transform .1s var(--ease); overflow: hidden;
}
.sched-ev.important { background: var(--c-orange); }
.sched-ev.personal { background: #7c3aed; }
.sched-ev:hover { filter: brightness(1.06); }
.sched-ev:active { transform: scale(.98); }
/* Title: the CSS values here are the all-day-lane default (two lines). Positioned timeline blocks
   override font-size, line-height and -webkit-line-clamp inline, fitted to each block's real height
   so a line is never cut in half; the last visible line ends in "…". Full details open on tap. */
.sched-ev-title {
  font-size: .82rem; font-weight: 700; line-height: 1.2; overflow-wrap: anywhere;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden;
}
.sched-ev-time { font-size: .7rem; font-weight: 600; line-height: 13px; opacity: .92; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sched-private { display: inline-flex; align-items: center; gap: 3px; margin-left: 6px; }
.sched-private .ico, .legend .ico { width: 12px; height: 12px; }
.sched-empty { text-align: center; color: var(--muted); font-size: .86rem; margin: 12px 0 4px; }
.cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; }
.cal-dows span { text-align: center; font-size: .68rem; font-weight: 800; color: var(--muted); text-transform: uppercase; letter-spacing: .03em; padding: 4px 0; }
.cal-cell {
  position: relative; aspect-ratio: 1 / 0.9; min-height: 36px; border: 1px solid transparent; border-radius: 12px;
  background: var(--surface-2); cursor: pointer; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  font-weight: 700; font-size: .9rem; color: var(--ink); transition: background-color .15s, border-color .15s, transform .1s;
}
.cal-cell:hover { background: var(--surface-inset); }
.cal-cell:active { transform: scale(.96); }
.cal-cell.other { color: var(--muted); opacity: .45; }
.cal-cell.today { color: var(--primary); }
.cal-cell.today::after { content: ""; position: absolute; bottom: 5px; width: 4px; height: 4px; border-radius: 50%; background: var(--primary); }
.cal-cell.selected { background: var(--accent-grad); color: #fff; border-color: transparent; box-shadow: var(--shadow-blue); }
.cal-cell.selected.today { color: #fff; }
.cal-cell.selected .cal-dot { box-shadow: 0 0 0 1.5px rgba(255, 255, 255, .65); }
.cal-cell .cal-dots { position: absolute; bottom: 6px; display: flex; gap: 2.5px; }
.cal-cell.today::after { display: none; }
.cal-dot { width: 5px; height: 5px; border-radius: 50%; background: var(--c-blue); }
.cal-dot.orange { background: var(--c-orange); }
.cal-dot.violet { background: #7c5cff; }

/* Day panel under month view */
.day-panel { margin-top: 16px; }
.day-panel .day-panel-head { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; }
.day-panel .day-panel-head h2 { flex: 1; font-size: 1.1rem; }

/* Year view — 12 mini month tiles */
.year-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
.year-cell {
  border: 1px solid var(--line); border-radius: var(--radius-md); background: var(--surface); cursor: pointer;
  padding: 14px 8px; display: flex; flex-direction: column; align-items: center; gap: 7px; min-height: 88px; justify-content: center;
  transition: transform .14s var(--ease), border-color .2s, box-shadow .2s;
}
.year-cell:hover { transform: translateY(-2px); border-color: var(--primary); box-shadow: var(--shadow-md); }
.year-cell .ym { font-family: var(--font-display); font-weight: 700; }
.year-cell.now .ym { color: var(--primary); }
.year-cell .count { font-size: .74rem; color: var(--muted); font-weight: 700; }
.year-cell .count.has { color: var(--orange-strong); }

/* Legend */
.legend { display: flex; gap: 16px; flex-wrap: wrap; margin-top: 12px; padding: 0 2px; }
.legend span { display: inline-flex; align-items: center; gap: 6px; font-size: .8rem; font-weight: 600; color: var(--muted); }
.legend i { width: 10px; height: 10px; border-radius: 50%; }
.legend i.blue { background: var(--c-blue); }
.legend i.orange { background: var(--c-orange); }

/* ---- Bible study cards ---------------------------------------------------- */
.personal-todos { margin-bottom: 18px; padding: 16px; }
.personal-todo-head { display: flex; align-items: center; justify-content: space-between; gap: 14px; }
.personal-todo-head h2 { font-size: 1.05rem; }
.personal-todo-list { display: grid; gap: 8px; margin-top: 12px; }
.personal-todo-row { display: flex; align-items: center; gap: 10px; padding: 10px; border: 1px solid var(--line); border-radius: 14px; background: var(--surface-inset); }
.personal-todo-row.completed { opacity: .62; }
.personal-todo-row.completed .todo-title { text-decoration: line-through; }
.todo-check { width: 28px; height: 28px; flex: 0 0 28px; display: grid; place-items: center; border-radius: 50%; border: 2px solid #f97316; background: var(--surface); color: #fff; cursor: pointer; }
.todo-check[data-completed="1"] { background: #f97316; }
.todo-check .ico { width: 15px; height: 15px; }
.todo-main { min-width: 0; flex: 1; border: 0; background: none; color: inherit; text-align: left; cursor: pointer; }
.todo-title { display: block; font-weight: 800; overflow-wrap: anywhere; }
.todo-meta { display: flex; align-items: center; gap: 4px; margin-top: 3px; color: var(--muted); font-size: .76rem; }
.todo-meta .ico { width: 12px; height: 12px; }
.todo-form { margin-top: 14px; padding: 14px; border-radius: 14px; background: var(--surface-inset); border: 1px solid var(--line); }
.study-card { position: relative; }
.study-card .study-head { display: flex; align-items: flex-start; gap: 12px; }
.study-card .study-head .avatar { border-radius: 14px; }
.study-card h3 { margin-bottom: 4px; }
.study-card .study-meta { display: grid; gap: 9px; margin-top: 15px; }
.study-card .study-meta .mrow { display: flex; align-items: center; gap: 9px; color: var(--ink-soft); font-size: .88rem; }
.study-card .study-meta .mrow .ico { width: 16px; height: 16px; color: var(--primary); flex: 0 0 auto; }
.reminder-pill { display: inline-flex; align-items: center; gap: 5px; border-radius: 999px; padding: 5px 11px; background: var(--orange-soft); color: var(--orange-strong); font-size: .74rem; font-weight: 700; }
.reminder-pill.off { background: var(--surface-inset); color: var(--muted); }
.reminder-pill .ico { width: 13px; height: 13px; }
.conflict-note { display: flex; align-items: center; gap: 8px; margin-top: 12px; padding: 9px 12px; border-radius: var(--radius-sm); background: var(--orange-soft); color: var(--orange-strong); font-size: .82rem; font-weight: 700; }
.conflict-note .ico { width: 16px; height: 16px; flex: 0 0 auto; }

/* Featured study banner */
.featured { position: relative; overflow: hidden; border: 0; color: #fff; background: var(--navy-grad); border-radius: var(--radius-xl); padding: 16px; box-shadow: var(--shadow-md); }
.featured::after { content: ""; position: absolute; inset: 0; pointer-events: none; background: radial-gradient(320px 180px at 100% 0%, rgba(21, 101, 255, .6), transparent 60%), radial-gradient(220px 150px at -10% 120%, rgba(255, 179, 71, .28), transparent 60%); }
.featured > * { position: relative; }
.featured .tag { display: inline-flex; align-items: center; gap: 6px; font-size: .7rem; font-weight: 800; letter-spacing: .05em; text-transform: uppercase; background: var(--orange-grad); color: #4a2e05; padding: 5px 11px; border-radius: 999px; }
.featured h2 { margin: 12px 0 6px; color: #fff; }
.featured .f-meta { color: rgba(255, 255, 255, .82); font-size: .9rem; }
.featured .f-meta .mrow { display: inline-flex; align-items: center; gap: 6px; margin-right: 14px; }
.featured .f-meta .ico { width: 15px; height: 15px; }

/* ---- Auth / brand --------------------------------------------------------- */
.brand { display: flex; gap: 13px; align-items: center; justify-content: center; margin-bottom: 22px; }
.brand .mark { width: 56px; height: 56px; border-radius: 18px; background: var(--accent-grad); color: #fff; display: grid; place-items: center; box-shadow: var(--shadow-blue); }
.brand .mark .ico { width: 30px; height: 30px; }
.brand h1 { font-size: 1.6rem; }
.brand .muted { font-size: .9rem; }

.segmented + h2 { margin-top: 4px; }
.divider { display: flex; align-items: center; gap: 12px; margin: 18px 0; color: var(--muted); font-size: .82rem; font-weight: 600; }
.divider::before, .divider::after { content: ""; flex: 1; height: 1px; background: var(--line-strong); }
.google-btn-wrap { position: relative; display: flex; justify-content: center; min-height: 44px; }
/* The real "Continue with Google" control.
   This used to carry `pointer-events: none`, which — together with the absolutely positioned GIS host
   layered over it — is why tapping Continue with Google did nothing on iPhone. The button was purely
   decorative, and Google's own iframe was expected to cover it and receive the tap instead. On iOS
   Safari that iframe is blocked by Prevent Cross-Site Tracking (on by default), so nothing ever
   covered it and nothing could receive the tap. The button is now genuinely interactive and sits
   above the decorative layers; app.js binds a real click handler to it on every platform. */
.google-first-frame-btn {
  width: 199px; min-height: 44px; display: inline-flex; align-items: center; justify-content: center; gap: 10px;
  border: 1px solid var(--line-strong); border-radius: 999px; background: var(--surface); color: var(--ink);
  font: 600 .9rem/1 system-ui, sans-serif; box-shadow: var(--shadow-sm);
  position: relative; z-index: 2; pointer-events: auto; cursor: pointer;
  -webkit-tap-highlight-color: rgba(21, 101, 255, .16); touch-action: manipulation;
}
.google-first-frame-btn:active { transform: translateY(1px); }
.google-first-frame-btn[disabled] { opacity: .7; cursor: default; }
.google-first-frame-btn svg { width: 18px; height: 18px; flex: 0 0 auto; }
.google-first-frame-btn[hidden] { display: none; }
/* Transparent to taps until Google's iframe has actually mounted into it. An empty overlay must never
   intercept a tap meant for the button underneath — that is the "invisible overlay" failure mode. */
.google-gsi-host { position: absolute; inset: 0; display: flex; justify-content: center; z-index: 1; pointer-events: none; }
.google-gsi-host[data-gsi-active] { pointer-events: auto; z-index: 3; }
.google-gsi-host iframe { pointer-events: auto; }

/* The neutral screen shown while a stored session is being restored. Never an error, never the
   sign-in form — it makes no claim about whether anybody is signed in. */
.auth-restoring { text-align: center; }
.auth-restoring h2 { font-size: 1.15rem; }

/* ---- Pre-sign-in onboarding (welcome → install → sign-in) ----------------- */
.ob-card { text-align: center; }
.ob-steps { display: flex; justify-content: center; gap: 8px; margin-bottom: 20px; }
.ob-dot { width: 8px; height: 8px; border-radius: 999px; background: var(--line-strong); transition: width .22s var(--ease), background-color .22s; }
.ob-dot.on { width: 24px; background: var(--primary); }
.ob-ico {
  width: 66px; height: 66px; margin: 4px auto 18px;
  display: grid; place-items: center; border-radius: 20px;
  background: var(--soft); color: var(--primary);
}
.ob-ico .ico { width: 32px; height: 32px; }
.ob-card h2 { font-size: 1.32rem; }
.ob-lead { margin: 12px 0 24px; line-height: 1.55; }

/* ---- Notices / feedback --------------------------------------------------- */
.error { color: var(--danger); font-weight: 700; margin-top: 12px; }
.success { color: var(--success); font-weight: 700; margin-top: 12px; }
.notice { padding: 13px 15px; border-radius: var(--radius-md); background: var(--soft); color: var(--soft-ink); font-size: .9rem; font-weight: 600; }
.notice.warn { background: var(--orange-soft); color: var(--orange-strong); }
.locked { opacity: .6; }

.toast {
  position: fixed; left: 50%; bottom: calc(var(--nav-h) + 18px + env(safe-area-inset-bottom)); transform: translateX(-50%);
  z-index: 80; max-width: 92%; background: var(--navy); color: #fff;
  font-weight: 700; padding: 13px 20px; border-radius: 999px; box-shadow: var(--shadow-lg);
  display: flex; align-items: center; gap: 9px; animation: toast-in .24s var(--ease);
}
.toast .ico { color: var(--orange); }
/* Error variant of the toast (e.g. a reaction that couldn't be added): a non-blocking notice that
   reads as a warning rather than a success confirmation. */
.toast.toast-error { background: var(--danger, #b3261e); }
.toast.toast-error .ico { color: #fff; }
/* Informational variant: the action completed and there is nothing wrong, but nothing is confirmed
   either — used for "Sent to Firebase — No device receipt", where a success tick would be a lie. */
.toast.toast-info { background: var(--navy); }
.toast.toast-info .ico { color: #fff; }
@keyframes toast-in { from { opacity: 0; transform: translate(-50%, 12px); } to { opacity: 1; transform: translate(-50%, 0); } }

/* ---- Empty + loading states ----------------------------------------------- */
.empty { text-align: center; padding: 22px 18px; color: var(--muted); }
.empty .ico-wrap { width: 44px; height: 44px; border-radius: 14px; background: var(--soft); color: var(--primary); display: grid; place-items: center; margin: 0 auto 10px; }
.empty .ico-wrap .ico { width: 22px; height: 22px; }
.empty h3 { color: var(--ink); margin-bottom: 4px; font-size: 1.02rem; }
.empty p { font-size: .9rem; }

.loading { color: var(--muted); padding: 24px; text-align: center; }
.skeleton { position: relative; overflow: hidden; background: var(--surface-inset); border-radius: var(--radius-md); }
.skeleton::after { content: ""; position: absolute; inset: 0; transform: translateX(-100%); background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .18), transparent); animation: shimmer 1.3s infinite; }
[data-theme="dark"] .skeleton::after { background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .06), transparent); }
@keyframes shimmer { 100% { transform: translateX(100%); } }
.sk-line { height: 12px; margin: 9px 0; }
.sk-card { height: 92px; }

/* ---- Bottom navigation ---------------------------------------------------- */
.nav {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 40; height: calc(var(--nav-h) + env(safe-area-inset-bottom));
  display: flex; padding: 6px max(6px, env(safe-area-inset-left)) calc(6px + env(safe-area-inset-bottom)) max(6px, env(safe-area-inset-right));
  background: color-mix(in srgb, var(--surface) 96%, transparent);
  border-top: 1px solid var(--line);
  transform: translate3d(0, 0, 0);
  will-change: transform;
  contain: layout paint;
}
.nav-inner { display: flex; gap: 1px; width: 100%; max-width: var(--maxw); margin: 0 auto; }
.nav-btn {
  flex: 1 1 0; min-width: 0; border: 0; background: transparent; cursor: pointer;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  padding: 4px 2px; color: var(--muted); font-size: .6rem; font-weight: 700; border-radius: 14px;
  transition: color .2s var(--ease); -webkit-tap-highlight-color: transparent;
}
.nav-btn span:last-child { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nav-btn .ico { width: 23px; height: 23px; transition: transform .2s var(--ease); }
.nav-btn .ico-wrap { position: relative; display: grid; place-items: center; padding: 5px 13px; border-radius: 999px; transition: background-color .2s var(--ease); }
.nav-btn.active { color: var(--primary); }
.nav-btn.active .ico-wrap { background: var(--soft); }
.nav-btn.active .ico { transform: translateY(-1px); }
.nav-btn .nav-badge { position: absolute; top: -2px; right: 5px; min-width: 16px; height: 16px; padding: 0 4px; border-radius: 999px; background: var(--orange-strong); color: #fff; font-size: .58rem; font-weight: 800; display: grid; place-items: center; }

@supports (-webkit-touch-callout: none) {
  .nav {
    position: fixed;
    bottom: 0;
    padding-bottom: max(6px, calc(6px + env(safe-area-inset-bottom)));
  }
  .container {
    padding-bottom: calc(var(--nav-h) + env(safe-area-inset-bottom) + 64px);
  }
}

/* ---- Reaction details + message edit modal -------------------------------- */
.rxn-detail-backdrop,
.edit-modal-backdrop {
  position: fixed; inset: 0; z-index: 2100;
  display: grid; place-items: end center;
  padding: calc(env(safe-area-inset-top) + 16px) 12px calc(env(safe-area-inset-bottom) + 12px);
  background: rgba(8, 11, 18, .42);
}
.rxn-detail,
.edit-modal {
  width: min(560px, 100%);
  max-height: min(86vh, 720px);
  max-height: min(86dvh, 720px);
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: 16px;
  box-shadow: 0 24px 70px rgba(8, 11, 18, .28);
  overflow: hidden;
}
.rxn-detail-head,
.edit-modal-head {
  display: flex; align-items: center; gap: 10px; justify-content: space-between;
  padding: 14px 16px; border-bottom: 1px solid var(--line);
}
.rxn-detail-list {
  max-height: min(62vh, 520px);
  max-height: min(62dvh, 520px);
  overflow-y: auto;
  padding: 8px 16px 16px;
  -webkit-overflow-scrolling: touch;
}
.rxn-detail-row { display: flex; align-items: center; gap: 12px; padding: 10px 0; border-bottom: 1px solid var(--line); }
.rxn-detail-row:last-child { border-bottom: 0; }
.rxn-detail-emoji { font-size: 1.1rem; }
/* One block per emoji in the "who reacted" sheet: the reaction and how many chose it, then the
   people who chose it in the order they reacted. */
.rxn-detail-group + .rxn-detail-group { margin-top: 14px; }
.rxn-detail-group-head {
  display: flex; align-items: center; gap: 8px;
  padding: 6px 0; border-bottom: 1px solid var(--line);
  position: sticky; top: 0; background: var(--surface); z-index: 1;
}
.rxn-detail-group .rxn-detail-row:last-child { border-bottom: 0; }
.edit-modal {
  display: flex; flex-direction: column;
}
.edit-modal-text {
  width: calc(100% - 32px);
  min-height: 240px;
  max-height: 58vh;
  max-height: 58dvh;
  margin: 16px;
  resize: vertical;
  overflow-y: auto;
  white-space: pre-wrap;
  overflow-wrap: break-word;
  line-height: 1.55;
  -webkit-overflow-scrolling: touch;
}
.edit-modal-actions {
  position: sticky; bottom: 0;
  display: flex; gap: 10px; justify-content: flex-end;
  padding: 12px 16px calc(12px + env(safe-area-inset-bottom));
  background: var(--surface);
  border-top: 1px solid var(--line);
}
@media (max-width: 560px) {
  .edit-modal-backdrop { place-items: stretch; padding: env(safe-area-inset-top) 0 0; }
  .edit-modal {
    width: 100%;
    min-height: 100%;
    max-height: 100%;
    border-radius: 16px 16px 0 0;
  }
  .edit-modal-text {
    flex: 1 1 auto;
    min-height: 0;
    max-height: none;
  }
  .rxn-detail-backdrop { padding-left: 0; padding-right: 0; }
  .rxn-detail { border-radius: 16px 16px 0 0; }
}

/* ---- Settings rows -------------------------------------------------------- */
.set-row { display: flex; align-items: center; gap: 14px; padding: 18px 4px; border-bottom: 1px solid var(--line); }
.set-row:last-child { border-bottom: 0; }
.set-row .ico-wrap { width: 42px; height: 42px; border-radius: 12px; display: grid; place-items: center; background: var(--soft); color: var(--primary); flex: 0 0 auto; }
.set-row .k { font-size: .8rem; color: var(--muted); font-weight: 600; line-height: 1.4; margin-top: 3px; display: inline-block; }
.set-row .v { font-weight: 700; }
/* Notification Preview: a radio-group of three options for how much shows on the lock screen. The
   label row drops its own divider since the options below carry it, and the selected option reads
   as a filled radio + check, matching the app's blue accent. */
.prev-row { border-bottom: 0; padding-bottom: 4px; }
.prev-opts { display: flex; flex-direction: column; gap: 8px; padding: 2px 4px 16px; border-bottom: 1px solid var(--line); }
.prev-opt {
  display: flex; align-items: center; gap: 12px; width: 100%; text-align: left;
  padding: 12px 14px; border-radius: 14px; border: 1px solid var(--line-strong);
  background: var(--surface); color: var(--ink-soft); cursor: pointer; transition: all .18s var(--ease);
}
.prev-opt:hover { border-color: var(--primary); }
.prev-opt.active { border-color: var(--primary); background: var(--soft); box-shadow: var(--shadow-sm); }
.prev-radio { width: 18px; height: 18px; border-radius: 999px; border: 2px solid var(--line-strong); flex: 0 0 auto; position: relative; transition: border-color .18s var(--ease); }
.prev-opt.active .prev-radio { border-color: var(--primary); }
.prev-opt.active .prev-radio::after { content: ""; position: absolute; inset: 3px; border-radius: 999px; background: var(--primary); }
.prev-text { display: flex; flex-direction: column; flex: 1 1 auto; gap: 2px; min-width: 0; }
.prev-label { font-weight: 700; font-size: .92rem; color: var(--ink); }
.prev-sub { font-size: .78rem; color: var(--muted); font-weight: 600; }
.prev-check { color: var(--primary); opacity: 0; flex: 0 0 auto; transition: opacity .18s var(--ease); }
.prev-check .ico { width: 18px; height: 18px; }
.prev-opt.active .prev-check { opacity: 1; }
/* The Push Notifications row carries the most controls (status pill, refresh, toggle). Let the row
   wrap so the control cluster drops to its own line on narrow phones instead of overlapping the note. */
.push-row { flex-wrap: wrap; row-gap: 10px; }
.push-row > .grow { min-width: 150px; }
.push-ctrls { display: inline-flex; align-items: center; gap: 12px; margin-left: auto; flex: 0 0 auto; }
.push-row .pill { white-space: nowrap; }
.switch { position: relative; width: 52px; height: 30px; flex: 0 0 auto; cursor: pointer; }
.switch input { position: absolute; opacity: 0; width: 100%; height: 100%; margin: 0; cursor: pointer; }
.switch .track { position: absolute; inset: 0; border-radius: 999px; background: var(--line-strong); transition: background-color .2s; }
.switch .knob { position: absolute; top: 3px; left: 3px; width: 24px; height: 24px; border-radius: 50%; background: #fff; box-shadow: var(--shadow-sm); transition: transform .2s var(--ease); }
.switch input:checked + .track { background: var(--primary); }
.switch input:checked + .track + .knob { transform: translateX(22px); }
.device-limit-row { display: flex; justify-content: flex-end; padding: 12px 4px 0; }
.device-row { align-items: flex-start; }
.device-row .ico-wrap { margin-top: 1px; }
.device-row b { overflow-wrap: anywhere; }

/* ---- Detail panels (group/study/event tabs) ------------------------------- */
.member-row { display: flex; align-items: center; gap: 12px; padding: 15px 4px; border-bottom: 1px solid var(--line); }
.member-row:last-child { border-bottom: 0; }
.group-members-list { padding: 4px 14px; overflow: hidden; }
.group-member-row { align-items: flex-start; min-width: 0; padding: 16px 2px; }
.group-member-row > .avatar { flex: 0 0 auto; }
.group-member-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; align-items: flex-start; gap: 5px; }
.group-member-name { min-width: 0; max-width: 100%; line-height: 1.28; overflow-wrap: anywhere; }
.group-member-email { display: block; width: 100%; max-width: 100%; line-height: 1.35; overflow-wrap: anywhere; word-break: break-word; }
.group-member-role { align-self: flex-start; max-width: 100%; white-space: normal; line-height: 1.2; }
.group-member-move { display: grid; gap: 5px; width: min(100%, 280px); margin-top: 5px; color: var(--muted); font-size: .72rem; font-weight: 800; }
.group-member-move .role-select { width: 100%; min-height: 44px; max-width: 100%; }
.group-member-remove { flex: 0 0 44px; width: 44px; height: 44px; margin: 0 0 0 4px !important; align-self: flex-start; }
.group-member-add-row { align-items: center; }
.group-member-add-row .btn { min-height: 44px; flex: 0 0 auto; }
.group-members-bottom-space { height: calc(var(--nav-h) + env(safe-area-inset-bottom) + 24px); }
.group-planner-settings { margin-top: 18px; padding-top: 4px; border-top: 1px solid var(--line); }
.planner-selected-members { display: grid; gap: 8px; margin-top: 12px; }
.planner-member-choice { display: block; cursor: pointer; }
.planner-member-choice > input { position: absolute; opacity: 0; pointer-events: none; }
.planner-member-choice > span { display: flex; align-items: center; gap: 10px; min-height: 52px; padding: 8px 10px; border: 1px solid var(--line); border-radius: 12px; background: var(--surface-inset); }
.planner-member-choice > input:checked + span { border-color: var(--primary); background: var(--soft); }
.planner-member-choice small { display: block; margin-top: 2px; color: var(--muted); overflow-wrap: anywhere; }
.planner-posting-group { display: flex; align-items: center; gap: 9px; min-height: 46px; padding: 10px 12px; border: 1px solid var(--line); border-radius: 12px; background: var(--surface-inset); color: var(--ink-soft); font-weight: 700; }
.planner-posting-group .ico { width: 19px; height: 19px; color: var(--primary); }
@media (max-width: 390px) {
  .group-members-list { padding-right: 10px; padding-left: 10px; }
  .group-member-row { gap: 10px; }
  .group-member-remove { flex-basis: 42px; width: 42px; height: 42px; }
}
.settings-member { align-items: flex-start; min-width: 0; max-width: 100%; }
.settings-member > .avatar, .settings-member > img, .settings-member .av { flex: 0 0 auto; }
.settings-member > .grow { min-width: min(220px, 100%); }
.settings-actions { display: flex; align-items: center; justify-content: flex-end; gap: 8px; flex-wrap: wrap; min-width: 0; }
.settings-actions .role-select { max-width: 100%; }
.manage-user-row { height: auto; min-height: 0; }
.manage-user-row .settings-actions { flex: 0 0 auto; }
.role-row {
  display: flex;
  width: 100%;
  max-width: 100%;
  margin-top: 10px;
  clear: both;
}
.role-row .role-select {
  width: min(210px, 100%) !important;
  min-width: 0 !important;
}
.user-card-main { display: block; overflow-wrap: break-word; }
.settings-checks,
.membership-picker {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px 12px;
  min-width: 0;
  max-width: 100%;
  margin-top: 8px;
}
.membership-heading { flex: 1 0 100%; min-width: 0; }
.settings-check {
  display: inline-flex;
  align-items: flex-start;
  gap: 7px;
  flex: 0 1 auto;
  min-width: min(150px, 100%);
  max-width: 100%;
  line-height: 1.35;
  cursor: pointer;
  white-space: normal;
  overflow-wrap: normal;
  word-break: normal;
}
.settings-check input {
  width: 18px;
  height: 18px;
  min-height: 0;
  flex: 0 0 18px;
  margin: 1px 0 0;
  accent-color: var(--primary);
}
.settings-check span {
  display: inline;
  min-width: 0;
  overflow-wrap: break-word;
  word-break: normal;
  hyphens: none;
}
.membership-check { min-width: min(128px, 100%); }
.progress-track { height: 8px; border-radius: 999px; background: var(--surface-inset); overflow: hidden; }
.progress-fill { height: 100%; border-radius: 999px; background: var(--accent-grad); transition: width .5s var(--ease); }
.cat-stripe { width: 5px; align-self: stretch; border-radius: 999px; flex: 0 0 auto; }

/* ---- Entrance animation --------------------------------------------------- */
.fade-up { animation: fadeUp .24s var(--ease) both; }
@keyframes fadeUp { from { transform: translateY(4px); } to { transform: none; } }
/* A quiet background revalidation repaints in place (only when the data actually changed). It must
   not replay the entrance animation — otherwise the whole screen fades/slides in again on a routine
   refresh, which is exactly the flicker members reported. The element is shown at its natural, settled
   state instead. Fresh navigations don't carry this class, so they still animate in. */
#page.quiet-refresh .fade-up { animation: none; opacity: 1; transform: none; }

/* ---- Responsive ----------------------------------------------------------- */
/* Small phones (≤390px): tighten so nothing crowds or overflows */
@media (max-width: 390px) {
  body { font-size: 15.5px; }
  .container { --shell-pad: 13px; }
  .card { padding: 16px; border-radius: 16px; }
  .card.pad-lg { padding: 18px; }
  .page-head { gap: 10px; margin-bottom: 18px; }
  .page-head h1 { font-size: 1.24rem; }
  /* Chat headers stay compact on the narrowest phones: the title keeps as much width as possible so
     it only ever shortens when the name genuinely cannot fit, never because of padding. The size is
     already viewport-scaled in the base rule, so it is not re-fixed here. */
  .page-head.chat-head { gap: 4px; margin-bottom: 8px; }
  .page-head.chat-head .sub { font-size: .74rem; }
  .page-head.chat-head .head-actions { gap: 2px; padding-left: 3px; }
  .btn { max-width: 100%; padding-left: 14px; padding-right: 14px; }
  .btn.lg { min-height: 52px; font-size: .98rem; border-radius: 16px; }
  .segmented { padding: 4px; border-radius: 16px; }
  .segmented button { min-height: 38px; padding: 0 11px; font-size: .8rem; }
  .row > .grow { flex-basis: 100%; }
  .row.nowrap > .grow { flex-basis: 0; }
  .settings-form-row input.grow { flex-basis: 100%; }
  .settings-form-row .btn { width: 100%; }
  .set-row { gap: 10px; padding: 15px 2px; }
  .set-row .ico-wrap { width: 36px; height: 36px; border-radius: 10px; }
  .settings-member {
    align-items: flex-start; flex-wrap: wrap; gap: 10px; padding: 14px 2px;
  }
  .settings-member > .grow { flex: 1 1 calc(100% - 46px); min-width: 0; }
  .settings-actions {
    display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
    width: 100%; padding-left: 46px; min-width: 0;
  }
  .settings-actions .btn { flex: 1 1 132px; min-width: 0; }
  .settings-actions .fm-act { margin-left: 0; }
  .manage-user-row .role-row { padding-right: 0; }
  .manage-user-row .role-select { width: min(210px, 100%) !important; min-width: 0 !important; }
  .manage-user-row .settings-check { flex: 1 1 132px; min-width: min(132px, 100%); }
  .manage-user-row .membership-check { flex-basis: calc(50% - 8px); }
  .org-membership-row .settings-actions,
  .pending-member .settings-actions { padding-left: 0; }
  #orgMemberships, #userAdmin { overflow-wrap: break-word; }
  .fm-id-name { font-size: 1.36rem; }
  .fm-feature-title { font-size: 1.46rem; }
  .fm-qa-ic { width: 42px; height: 42px; }
  .fm-qa-label { font-size: .7rem; }
  .nav-btn { font-size: .56rem; }
  .nav-btn .ico-wrap { padding: 5px 9px; }
  .cal-cell { font-size: .82rem; min-height: 32px; }
  .year-grid { gap: 8px; }
}
@media (max-width: 360px) {
  .nav-btn .ico-wrap { padding: 5px 7px; }
  .cal-grid { gap: 3px; }
}

/* =====================================================================================
   Phase 3 — For Me inbox, Groups management hub, News categories, group chat
   ===================================================================================== */

/* Tone tiles shared by the inbox feed. Background on the tile, matching colour on the kicker. */
.fm-feed-tile.fm-blue, .fm-act { color: var(--primary); }
.fm-feed-tile.fm-blue   { background: var(--soft); }
.fm-feed-tile.fm-amber  { background: var(--orange-soft); color: var(--orange-strong); }
.fm-feed-tile.fm-violet { background: rgba(124, 92, 255, .14); color: #7c5cff; }
.fm-feed-tile.fm-green  { background: rgba(22, 163, 74, .14); color: #16a34a; }
.fm-feed-tile.fm-pink   { background: rgba(236, 72, 153, .14); color: #ec4899; }
[data-theme="dark"] .fm-feed-tile.fm-violet { color: #b3a0ff; }
[data-theme="dark"] .fm-feed-tile.fm-green  { color: #4ade80; }
[data-theme="dark"] .fm-feed-tile.fm-pink   { color: #f472b6; }
.fm-feed-kicker.fm-blue   { color: var(--primary); }
.fm-feed-kicker.fm-amber  { color: var(--orange-strong); }
.fm-feed-kicker.fm-violet { color: #7c5cff; }
.fm-feed-kicker.fm-green  { color: #16a34a; }
.fm-feed-kicker.fm-pink   { color: #ec4899; }
[data-theme="dark"] .fm-feed-kicker.fm-violet { color: #b3a0ff; }
[data-theme="dark"] .fm-feed-kicker.fm-green  { color: #4ade80; }
[data-theme="dark"] .fm-feed-kicker.fm-pink   { color: #f472b6; }

/* "What's for you" header + filter chips */
.fm-feed-head { display: flex; align-items: center; justify-content: space-between; margin: 2px 2px 12px; }
.fm-feed-head-actions { display: inline-flex; align-items: center; gap: 4px; }
.fm-feed-title { font-family: var(--font-display); font-size: 1.32rem; font-weight: 800; letter-spacing: -.02em; color: var(--ink); }
.icon-btn.ghost { background: transparent; border-color: transparent; box-shadow: none; }
.fm-chips { display: flex; gap: 8px; overflow-x: auto; margin-bottom: 16px; padding-bottom: 2px; -webkit-overflow-scrolling: touch; }
.fm-chips::-webkit-scrollbar { display: none; }
.fm-chip {
  flex: 0 0 auto; border: 1px solid var(--line-strong); background: var(--surface);
  color: var(--ink-soft); border-radius: 999px; padding: 8px 15px; font-size: .86rem;
  font-weight: 700; cursor: pointer; transition: all .15s var(--ease); white-space: nowrap;
}
.fm-chip.active { background: var(--accent-grad); color: #fff; border-color: transparent; box-shadow: var(--shadow-sm); }

/* Inbox feed card */
.fm-feed-card {
  display: flex; align-items: flex-start; gap: 13px; width: 100%; text-align: left; cursor: pointer;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg);
  padding: 17px; box-shadow: var(--shadow-sm);
  transition: transform .18s var(--ease), box-shadow .18s var(--ease), border-color .18s var(--ease);
}
.fm-feed-card:hover { transform: translateY(-2px); box-shadow: var(--shadow-md); border-color: var(--line-strong); }
.fm-feed-card:active { transform: translateY(0) scale(.995); }
.fm-feed-card.notification-target-highlight { outline: 2px solid rgba(21,101,255,.58); box-shadow: 0 0 0 5px rgba(21,101,255,.14), var(--shadow-md); }
.fm-feed-tile { width: 44px; height: 44px; border-radius: 14px; flex: 0 0 auto; display: grid; place-items: center; }
.fm-feed-tile .ico { width: 22px; height: 22px; }
.fm-feed-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.fm-feed-top { display: flex; align-items: center; gap: 8px; }
.fm-feed-kicker { text-transform: uppercase; letter-spacing: .08em; font-size: .68rem; font-weight: 800; flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Title and sub-line wrap to a couple of lines and only then truncate, so dates, times, titles and
   locations stay readable instead of being clipped to a single line. overflow-wrap keeps very long
   unbroken strings from overflowing the card. */
.fm-feed-heading {
  font-weight: 700; font-size: .98rem; color: var(--ink); line-height: 1.3; overflow-wrap: anywhere;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden;
}
.fm-feed-sub {
  font-size: .86rem; color: var(--muted); line-height: 1.4; overflow-wrap: anywhere;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; overflow: hidden;
}
.fm-feed-time { font-size: .76rem; color: var(--muted); font-weight: 600; white-space: nowrap; flex: 0 0 auto; }
.fm-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--primary); box-shadow: 0 0 0 3px rgba(21, 101, 255, .15); flex: 0 0 auto; }
.fm-feed-actions { display: flex; gap: 6px; margin-top: 4px; justify-content: flex-end; }
.fm-act {
  width: 36px; height: 36px; border-radius: 10px; border: 1px solid var(--line);
  background: var(--surface-2); display: grid; place-items: center; cursor: pointer;
  color: var(--ink-soft); transition: all .15s var(--ease); flex: 0 0 auto;
}
.fm-act .ico { width: 16px; height: 16px; }
.fm-act:hover { background: var(--surface-inset); }
.fm-act.danger { color: #e0507a; }
.fm-act.danger:hover { background: rgba(224, 80, 122, .12); }

/* The green Acknowledge button (and its confirmed state) shown on announcement and mention cards in
   the For Me feed. Mirrors the channel-chat Acknowledge control so the action reads consistently. */
.fm-ack-row { display: flex; margin-top: 9px; }
.fm-ack-btn {
  display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
  background: var(--c-green); color: #fff; border: 0; font-weight: 800; font-size: .8rem;
  padding: 7px 15px; border-radius: 999px; box-shadow: var(--shadow-sm);
  transition: filter .15s var(--ease), transform .1s var(--ease);
}
.fm-ack-btn:hover { filter: brightness(1.06); }
.fm-ack-btn:active { transform: scale(.95); }
.fm-ack-btn:disabled { opacity: .6; cursor: default; }
.fm-ack-btn .ico { width: 15px; height: 15px; }
.fm-ack-done {
  display: inline-flex; align-items: center; gap: 6px; font-weight: 800; font-size: .8rem;
  color: var(--c-green); background: rgba(22, 163, 74, .12); border: 1px solid rgba(22, 163, 74, .3);
  padding: 6px 13px; border-radius: 999px;
}
.fm-ack-done .ico { width: 15px; height: 15px; }

/* Kitchen Count meal RSVP buttons in the For Me feed. Neutral by default; the member's chosen
   answer fills in green (coming) or red (not coming) so their reply is unmistakable. */
.fm-rsvp-row { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 9px; }
.fm-rsvp-btn {
  display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
  background: var(--surface, #fff); color: var(--ink, #333); border: 1.5px solid var(--line, #e5e7eb);
  font-weight: 800; font-size: .8rem; padding: 7px 14px; border-radius: 999px;
  transition: filter .15s var(--ease), transform .1s var(--ease);
}
.fm-rsvp-btn .ico { width: 15px; height: 15px; }
.fm-rsvp-btn:hover { filter: brightness(1.03); }
.fm-rsvp-btn:active { transform: scale(.95); }
.fm-rsvp-btn:disabled { opacity: .6; cursor: default; }
.fm-rsvp-btn.yes.active { background: #16a34a; color: #fff; border-color: #16a34a; }
.fm-rsvp-btn.no.active { background: #dc2626; color: #fff; border-color: #dc2626; }
.fm-rsvp-status { display: block; margin-top: 6px; font-size: .72rem; color: var(--muted, #888); font-weight: 600; }

/* Swipe-to-act feedback on feed cards: right = delete, left = archive. The card follows the finger
   (transform set inline by the handler) and tints toward the pending action once past the cue. */
.fm-feed-card.swiping { box-shadow: var(--shadow-md); cursor: grabbing; user-select: none; touch-action: pan-y; }
.fm-feed-card.swipe-del { border-color: #e0507a; background: linear-gradient(90deg, rgba(224, 80, 122, .14), var(--surface) 60%); }
.fm-feed-card.swipe-arch { border-color: var(--primary); background: linear-gradient(270deg, rgba(21, 101, 255, .14), var(--surface) 60%); }

/* Planner / Reminders tab — each day of the current week with its planner-event reminders. */
.fm-week { display: flex; flex-direction: column; gap: 12px; }
.fm-day { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg); box-shadow: var(--shadow-sm); overflow: hidden; }
.fm-day.is-today { border-color: var(--primary); box-shadow: 0 0 0 1px var(--primary), var(--shadow-sm); }
.fm-day-head { display: flex; align-items: center; gap: 9px; padding: 12px 15px; border-bottom: 1px solid var(--line); background: var(--surface-2); }
.fm-day-dow { font-weight: 800; color: var(--ink); }
.fm-day-date { font-size: .82rem; color: var(--muted); }
.fm-day-today { margin-left: auto; font-size: .68rem; font-weight: 800; text-transform: uppercase; letter-spacing: .06em; color: #fff; background: var(--primary); padding: 3px 9px; border-radius: 999px; }
.fm-day-body { padding: 8px 10px; display: flex; flex-direction: column; gap: 6px; }
.fm-day-empty { color: var(--muted); font-size: .82rem; padding: 4px 5px; margin: 0; }
.fm-ev {
  display: flex; align-items: flex-start; gap: 12px; width: 100%; text-align: left; cursor: pointer;
  background: var(--surface-2); border: 1px solid var(--line); border-radius: 12px; padding: 10px 12px;
  transition: all .15s var(--ease);
}
.fm-ev:hover { border-color: var(--line-strong); background: var(--surface-inset); }
.fm-ev-time { flex: 0 0 auto; font-weight: 800; font-size: .8rem; color: var(--primary); min-width: 64px; }
.fm-ev-main { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.fm-ev-name { font-weight: 700; font-size: .9rem; color: var(--ink); overflow-wrap: anywhere; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; }
.fm-ev-flag { font-size: .64rem; font-weight: 800; text-transform: uppercase; letter-spacing: .05em; color: #b45309; background: rgba(180, 83, 9, .12); padding: 1px 7px; border-radius: 999px; vertical-align: middle; }
.fm-ev-loc { display: inline-flex; align-items: flex-start; gap: 5px; font-size: .8rem; color: var(--muted); overflow-wrap: anywhere; }
.fm-ev-loc .ico { width: 14px; height: 14px; flex: 0 0 auto; margin-top: 2px; }

/* Archive / Recently Deleted entry rows + sub-view header */
.fm-entries { display: flex; flex-direction: column; gap: 14px; margin: 20px 0 30px; }
.fm-entry {
  display: flex; align-items: center; gap: 13px; width: 100%; text-align: left; cursor: pointer;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg);
  padding: 17px; box-shadow: var(--shadow-sm); transition: all .15s var(--ease);
}
.fm-entry:hover { border-color: var(--line-strong); transform: translateY(-1px); }
.fm-entry-ic { width: 42px; height: 42px; border-radius: 13px; flex: 0 0 auto; display: grid; place-items: center; background: var(--surface-inset); color: var(--ink-soft); }
.fm-entry-ic .ico { width: 21px; height: 21px; }
.fm-entry-title { display: block; font-weight: 700; color: var(--ink); }
.fm-entry-sub { display: block; font-size: .82rem; color: var(--muted); }
.fm-entry-count { font-size: .82rem; font-weight: 700; color: var(--muted); white-space: nowrap; }
.fm-subhead { display: flex; align-items: center; gap: 12px; margin: 2px 2px 16px; }
.fm-subhead h2 { font-family: var(--font-display); font-size: 1.3rem; font-weight: 800; }

/* Groups — list + management hub */
.grp-list, .hub-list { display: flex; flex-direction: column; gap: 18px; }
.cat-list { display: grid; grid-template-columns: repeat(auto-fit, minmax(132px, 1fr)); gap: 18px; }
.grp-card, .cat-card {
  display: flex; align-items: center; gap: 14px; width: 100%; text-align: left; cursor: pointer;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg);
  padding: 17px; box-shadow: var(--shadow-sm); transition: all .18s var(--ease);
}
.grp-card:hover, .cat-card:hover { transform: translateY(-2px); box-shadow: var(--shadow-md); border-color: var(--line-strong); }
.cat-card.cat-circle {
  aspect-ratio: 1; min-height: 132px; justify-content: center; flex-direction: column; text-align: center;
  border-radius: 999px; padding: 20px 14px; gap: 10px;
}
.cat-card.cat-circle .cat-ic { width: 54px; height: 54px; border-radius: 50%; }
.cat-card.cat-circle .cat-name { max-width: 110px; line-height: 1.15; }
.ann-browse-title { margin-top: 2px; }
.ann-filter-list { grid-template-columns: repeat(5, minmax(132px, 1fr)); gap: 12px; overflow-x: auto; padding: 2px 2px 12px; }
.ann-filter-list .cat-card { min-width: 132px; min-height: 88px; border-radius: 20px; padding: 14px; flex-direction: column; align-items: flex-start; justify-content: space-between; gap: 10px; }
.ann-filter-list .cat-card .cat-ic { width: 38px; height: 38px; border-radius: 12px; }
.ann-filter-list .cat-card .cat-name { max-width: none; line-height: 1.2; text-align: left; }
.ann-filter-list .cat-card.selected { border-color: var(--primary); background: var(--soft); box-shadow: 0 0 0 3px rgba(21, 101, 255, .12); }
.ann-filter-list .cat-card.selected .cat-name { color: var(--primary); }
.ann-feed-title { justify-content: space-between; margin-top: 24px; }
.ann-feed-title h2 { flex: 1; }
/* The feed heading carries up to two actions (Recipients, Show all). Keep each one intact rather
   than letting the row squeeze them on a narrow phone. */
.ann-feed-title .btn { flex: 0 0 auto; white-space: nowrap; }
.news-feedback-btn { flex: 0 0 auto; }
/* Search box above the category's Current Recipients / Add Recipients lists. */
.recipient-search { margin: 0 0 12px; }
.category-menu-wrap { position: relative; flex: 0 0 auto; order: 4; }
.category-menu {
  position: absolute; top: calc(100% + 8px); right: 0; z-index: 40;
  display: grid; min-width: 230px; padding: 7px;
  border: 1px solid var(--line); border-radius: 15px;
  background: var(--surface); box-shadow: var(--shadow-lg);
}
.category-menu[hidden] { display: none; }
.category-menu button {
  display: flex; align-items: center; gap: 11px; width: 100%; min-height: 44px;
  padding: 10px 12px; border: 0; border-radius: 10px;
  background: transparent; color: var(--ink); font: inherit; font-size: .88rem; font-weight: 700;
  text-align: left; cursor: pointer;
}
.category-menu button:hover, .category-menu button:focus-visible { outline: none; background: var(--surface-inset); }
.category-menu button .ico { width: 19px; height: 19px; color: var(--primary); }
.category-menu button.danger-text, .category-menu button.danger-text .ico { color: var(--danger); }
@media (max-width: 640px) {
  .ann-filter-list { grid-template-columns: repeat(5, 132px); margin-right: -18px; padding-right: 18px; }
  .news-feedback-btn { padding: 0 12px; }
  .category-menu { min-width: min(230px, calc(100vw - 36px)); }
}
.feedback-form { display: grid; gap: 16px; padding: 20px; }
.feedback-form label, .feedback-status-control { display: grid; gap: 7px; color: var(--ink-soft); font-size: .86rem; font-weight: 700; }
.feedback-upload input { padding: 11px; background: var(--surface-inset); }
.feedback-history-title { margin-top: 26px; }
.feedback-list { display: grid; gap: 14px; }
.feedback-item { display: grid; gap: 12px; padding: 18px; }
.feedback-item-head { display: flex; align-items: flex-start; gap: 12px; }
.feedback-item-head h3 { font-size: 1rem; }
.feedback-message { color: var(--ink-soft); line-height: 1.55; white-space: pre-wrap; overflow-wrap: anywhere; }
.feedback-shot { width: min(260px, 100%); padding: 0; overflow: hidden; border: 1px solid var(--line); border-radius: var(--radius-md); background: var(--surface-inset); cursor: zoom-in; }
.feedback-shot img { display: block; width: 100%; max-height: 210px; object-fit: cover; }
.feedback-status { text-transform: capitalize; }
.feedback-status.status-new { background: var(--danger-soft); color: var(--danger); }
.feedback-status.status-reviewing { background: var(--warning-soft); color: var(--warning-ink); }
.feedback-status.status-resolved { background: var(--success-soft); color: var(--success); }
.feedback-status.status-closed { background: var(--surface-inset); color: var(--muted); }
.feedback-status-control { grid-template-columns: auto minmax(150px, 220px); align-items: center; justify-content: end; }
.feedback-thanks { max-width: 520px; margin: 12vh auto 0; padding: 32px 24px; text-align: center; display: grid; gap: 16px; }
.feedback-thanks h1 { font-size: 1.65rem; }
.feedback-thanks p { color: var(--muted); line-height: 1.55; }
.feedback-thanks-icon { width: 62px; height: 62px; margin: 0 auto; display: grid; place-items: center; border-radius: 20px; background: var(--success-soft); color: var(--success); }
.feedback-thanks-icon .ico { width: 30px; height: 30px; }
.category-summary {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; margin: 0 0 16px;
}
.category-summary > span {
  display: flex; flex-direction: column; gap: 3px; padding: 14px; border: 1px solid var(--line);
  border-radius: var(--radius-md); background: var(--surface-inset);
}
.category-summary b { color: var(--ink); font-size: .95rem; }
.category-summary small { color: var(--muted); font-size: .72rem; }
.grp-ava { width: 48px; height: 48px; border-radius: 16px; flex: 0 0 auto; display: grid; place-items: center; background: linear-gradient(135deg, #16a34a, #0e9aa7); color: #fff; }
.grp-ava .ico { width: 24px; height: 24px; }
.grp-name, .cat-name { display: block; font-weight: 700; font-size: 1.02rem; color: var(--ink); }
.grp-meta, .cat-meta { display: block; font-size: .85rem; color: var(--muted); margin-top: 2px; }
.grp-card .chev, .cat-card .chev, .hub-card .chev { color: var(--muted); flex: 0 0 auto; }
.group-order-actions { display: flex; gap: 8px; flex-wrap: wrap; margin: 0 0 14px; }
.grp-pin {
  width: 38px; height: 38px; border-radius: 12px; flex: 0 0 auto; display: grid; place-items: center;
  color: var(--muted); background: transparent; transition: all .16s var(--ease);
}
.grp-pin:hover { background: var(--surface-inset); color: var(--ink); }
.grp-pin.active { background: var(--orange-soft); color: var(--orange-strong); }
.grp-pin .ico { width: 18px; height: 18px; }
.group-order-card { padding: 8px 14px; }
.order-list { display: flex; flex-direction: column; }
.order-row {
  display: flex; align-items: center; gap: 12px; padding: 12px 0; border-bottom: 1px solid var(--line);
  transition: background .14s var(--ease), box-shadow .14s var(--ease), opacity .14s var(--ease);
}
.order-row:last-child { border-bottom: 0; }
.order-number-field {
  flex: 0 0 auto; display: grid; gap: 4px; min-width: 72px;
  color: var(--muted); font-size: .68rem; font-weight: 800; text-transform: uppercase;
}
.order-number-field select {
  width: 72px; min-height: 42px; border: 1px solid var(--line); border-radius: 10px;
  background: var(--surface-inset); color: var(--ink); font: inherit; font-size: 1rem;
  font-weight: 800; padding: 8px 10px;
}

/* Announcement mode chooser + standalone post list (News / Announcements) */
.ann-mode-grid { display: flex; flex-direction: column; gap: 10px; margin-top: 12px; }
.ann-mode-opt {
  display: flex; align-items: center; gap: 13px; width: 100%; text-align: left; cursor: pointer;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg);
  padding: 14px; transition: all .16s var(--ease);
}
.ann-mode-opt:hover { border-color: var(--line-strong); box-shadow: var(--shadow-sm); transform: translateY(-1px); }
.ann-mode-ic { width: 42px; height: 42px; border-radius: 13px; flex: 0 0 auto; display: grid; place-items: center; background: var(--soft); color: var(--soft-ink); }
.ann-mode-ic.tone-amber { background: var(--orange-soft); color: var(--orange-strong); }
.ann-mode-ic.tone-green { background: var(--success-soft); color: var(--success); }
.ann-mode-ic .ico, .ann-mode-ic svg { width: 21px; height: 21px; }
.ann-mode-title { display: block; font-weight: 700; color: var(--ink); }
.ann-mode-sub { display: block; font-size: .82rem; color: var(--muted); margin-top: 3px; line-height: 1.4; }
.ann-post-list { display: flex; flex-direction: column; gap: 14px; }

/* Announcement media: an attached photo or GIF on a card / detail, and the composer's
   Photo / GIF / Emoji strip. Inside the composer card the GIF and emoji panels flow in-line
   (position: static) below the buttons rather than floating above a chat composer. */
.ann-card-img { display: block; width: 100%; max-height: 320px; object-fit: cover; border-radius: 14px; margin: 4px 0 10px; }
.ann-detail-img { display: block; width: 100%; max-height: 460px; object-fit: contain; border-radius: 14px; margin: 14px 0 4px; background: var(--surface-2, rgba(0,0,0,.04)); }
.ann-media { position: relative; }
.ann-media-preview { margin-top: 12px; }
.ann-media-preview[hidden] { display: none; }
.ann-media .giphy-panel,
.ann-media .emoji-panel { position: static; inset: auto; margin-top: 12px; max-height: 260px; }

/* Manage Group hero */
.grp-hero {
  text-align: center; border-radius: var(--radius-xl); padding: 20px 18px; margin-bottom: 16px; color: #fff;
  background: radial-gradient(135% 135% at 0% 0%, #1f6dff 0%, #1565ff 50%, #0c47c2 100%);
  box-shadow: 0 20px 44px -18px rgba(21, 101, 255, .6), var(--shadow-md);
}
.grp-hero-ava { width: 58px; height: 58px; border-radius: 20px; margin: 0 auto 10px; display: grid; place-items: center; background: linear-gradient(135deg, #1fae5f, #0e9aa7); box-shadow: inset 0 0 0 1px rgba(255,255,255,.25); }
.grp-hero-ava .ico { width: 28px; height: 28px; color: #fff; }
.grp-hero-name { font-family: var(--font-display); font-size: 1.3rem; font-weight: 800; letter-spacing: -.02em; overflow-wrap: break-word; }
.grp-hero-meta { color: rgba(255,255,255,.82); font-weight: 600; margin-top: 4px; }
.grp-hero-edit { margin-top: 14px; display: inline-flex; align-items: center; gap: 7px; border: 0; cursor: pointer; border-radius: 999px; padding: 9px 18px; background: rgba(255,255,255,.18); color: #fff; font-weight: 700; font-size: .9rem; box-shadow: inset 0 0 0 1px rgba(255,255,255,.25); }
.grp-hero-edit .ico { width: 17px; height: 17px; }

/* Hub cards (Members, Recipients, Chat, Studies) */
.hub-card {
  display: flex; align-items: center; gap: 14px; width: 100%; text-align: left; cursor: pointer;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg);
  padding: 17px; box-shadow: var(--shadow-sm); transition: all .18s var(--ease);
}
.hub-card:hover { transform: translateY(-2px); box-shadow: var(--shadow-md); border-color: var(--line-strong); }
.hub-ic { width: 44px; height: 44px; border-radius: 14px; flex: 0 0 auto; display: grid; place-items: center; background: var(--soft); color: var(--primary); }
.hub-ic .ico { width: 22px; height: 22px; }
.hub-title { display: block; font-weight: 700; color: var(--ink); }
.hub-sub { display: flex; align-items: center; gap: 4px; font-size: .83rem; color: var(--muted); margin-top: 2px; }
.hub-sub .avatar.sm { width: 24px; height: 24px; border-radius: 8px; font-size: .62rem; margin-right: -6px; box-shadow: 0 0 0 2px var(--surface); }
.hub-count { font-size: .8rem; font-weight: 700; color: var(--muted); white-space: nowrap; }
.ava-extra { display: inline-grid; place-items: center; width: 24px; height: 24px; border-radius: 8px; background: var(--surface-inset); color: var(--ink-soft); font-size: .62rem; font-weight: 800; box-shadow: 0 0 0 2px var(--surface); }

/* News categories */
.cat-ic { width: 48px; height: 48px; border-radius: 16px; flex: 0 0 auto; display: grid; place-items: center; background: var(--orange-soft); color: var(--orange-strong); }
.cat-ic .ico { width: 24px; height: 24px; }
.pin-flag { color: var(--orange-strong); }
.pin-flag .ico { width: 18px; height: 18px; }

/* Group chat extras */
.grp-chat-ava { width: 40px; height: 40px; border-radius: 13px; flex: 0 0 auto; display: grid; place-items: center; background: linear-gradient(135deg, #16a34a, #0e9aa7); color: #fff; }
.grp-chat-ava .ico { width: 21px; height: 21px; }
/* The sender's avatar aligns with the TOP of their bubble, next to their name — the same alignment
   GroupMe uses. It keeps the name, message text, and timestamp on one consistent left edge no matter
   how tall the bubble grows. */
.bubble-row { gap: var(--chat-ava-gap); align-items: flex-start; }
/* The avatar is its own rigid column: an exact box that never shrinks, never gets squeezed by a wide
   bubble, and is never overlapped, so no edge of the square/circle can be cut off. box-sizing keeps
   the declared size honest if a border is ever added; overflow:visible keeps its soft shadow whole. */
.bubble-row.theirs .avatar.sm {
  flex: 0 0 var(--chat-ava);
  width: var(--chat-ava); height: var(--chat-ava);
  min-width: var(--chat-ava); min-height: var(--chat-ava);
  box-sizing: border-box; align-self: flex-start; overflow: visible; margin-bottom: 0;
}
/* The card beside it may use at most the width left after that column, so a long message can never
   push back into the avatar (the 82% cap still governs on wide screens). Scoped to the bubble that
   actually follows an avatar, so avatar-less rows — direct messages — keep their full width. */
.bubble-row.theirs .avatar.sm + .bubble { min-width: 0; max-width: min(82%, calc(100% - var(--chat-ava-col))); }
.bubble-name { display: block; font-size: .76rem; font-weight: 800; color: var(--primary); margin-bottom: 1px; }
.mention-chip { color: var(--primary); font-weight: 700; }
.bubble-row.mine .mention-chip { color: #dce7ff; }
/* Auto-detected links inside messages and announcements: a single tap opens them, so long URLs
   never need selection handles. Break long URLs so they don't overflow the bubble. */
.msg-link { color: var(--primary); font-weight: 600; text-decoration: underline; text-underline-offset: 2px; word-break: break-word; overflow-wrap: anywhere; }
.bubble-row.mine .msg-link { color: #dce7ff; }

/* Misc */
.danger-text { color: #e0507a; }
.member-row .fm-act { margin-left: 8px; }


/* ---- Profile & group photos -----------------------------------------------------------
   A saved photo (a small client-resized data URL) fills the avatar as a cover background; the
   coloured-initials / glyph fallback is unchanged when no photo is set. */
.avatar.has-photo,
.grp-ava.has-photo,
.grp-hero-ava.has-photo,
.grp-chat-ava.has-photo,
.cat-ic.has-photo,
.cat-hero-ava.has-photo {
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  color: transparent;
}
.grp-hero-ava.has-photo { box-shadow: inset 0 0 0 1px rgba(255,255,255,.25); }

/* Announcement-category picture. The hero variant is the editable preview in the manage screen;
   cat-ic-sm is the compact glyph shown in the category chat header. Both fall back to the orange
   megaphone glyph when no picture is set, matching the category card. */
.cat-hero-ava { width: 58px; height: 58px; border-radius: 18px; flex: 0 0 auto; display: grid; place-items: center; background: var(--orange-soft); color: var(--orange-strong); }
.cat-hero-ava .ico { width: 28px; height: 28px; }
.cat-ic-sm { width: 44px; height: 44px; }
.photo-edit .photo-preview .cat-hero-ava { margin: 0; }

/* Photo picker used in the profile and group edit forms: preview thumbnail beside the
   upload / remove controls. Wraps cleanly at narrow mobile widths. */
.photo-edit {
  display: flex; align-items: center; gap: 14px;
  margin-bottom: 14px; flex-wrap: wrap;
}
.photo-edit .photo-preview { flex: 0 0 auto; }
.photo-edit .photo-preview .avatar,
.photo-edit .photo-preview .grp-hero-ava { margin: 0; }

.group-media-section {
  margin: 0 0 16px;
  display: grid;
  gap: 12px;
}
.group-media-grid,
.group-media-edit-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(92px, 1fr));
  gap: 8px;
}
.group-media-tile,
.group-media-edit-tile {
  position: relative;
  border: 0;
  padding: 0;
  overflow: hidden;
  border-radius: 8px;
  background: var(--chip, #f2f4f8);
  aspect-ratio: 1;
}
.group-media-tile img,
.group-media-edit-tile img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.group-media-edit-actions {
  position: absolute;
  top: 6px;
  right: 6px;
  display: flex;
  gap: 4px;
}
.group-media-edit-actions .icon-btn {
  width: 32px;
  height: 32px;
  background: rgba(255,255,255,.92);
  box-shadow: 0 8px 18px rgba(10, 20, 40, .18);
}
.group-video-card {
  display: grid;
  gap: 8px;
}
.group-video-card video {
  width: 100%;
  max-height: min(62vh, 520px);
  border-radius: 8px;
  background: #05070b;
  object-fit: contain;
}

@media (max-width: 430px) {
  .group-media-grid,
  .group-media-edit-grid { grid-template-columns: repeat(3, 1fr); }
}

/* A disabled notification toggle reads as locked, never as a clickable control that does
   nothing: the switch is dimmed and the knob stays in the off position. */
.set-row.is-disabled .v { opacity: .85; }
.switch.disabled { cursor: not-allowed; opacity: .55; }
.switch.disabled input { cursor: not-allowed; }

/* ---- Image viewer (app-wide zoom) ----------------------------------------------------- */
/* Any photo can be tapped to open here: a full-screen dark overlay that floats above the app
   and restores the exact screen underneath when dismissed. Feels like Signal / Photos. */
.has-photo { cursor: zoom-in; touch-action: manipulation; }
img.zoomable, img[data-zoom] { cursor: zoom-in; touch-action: manipulation; }
/* Every tappable photo/GIF opens the in-app viewer; never the device's save/share callout. */
.has-photo, img.zoomable, img[data-zoom] {
  -webkit-user-select: none; user-select: none;
  -webkit-user-drag: none; -webkit-touch-callout: none;
}

body.modal-open {
  overflow: hidden;
  overscroll-behavior: none;
  touch-action: none;
}

/* Centered dialog used by the notification setup prompt and the iPhone install guide. */
.modal-overlay {
  position: fixed; inset: 0; z-index: 2100;
  display: grid; place-items: center;
  background: rgba(8, 11, 18, .55);
  -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px);
  padding: 24px calc(24px + env(safe-area-inset-right)) 24px calc(24px + env(safe-area-inset-left));
}
.modal-card {
  background: var(--card, #fff); color: var(--text, #111);
  width: 100%; max-width: 360px;
  border-radius: var(--radius, 18px);
  padding: 26px 24px 22px;
  box-shadow: 0 24px 60px rgba(8, 11, 18, .35);
  text-align: center;
}
.modal-card h2 { font-size: 1.2rem; margin: 6px 0 8px; }
.modal-card .modal-ico {
  width: 54px; height: 54px; margin: 0 auto 6px;
  display: grid; place-items: center; border-radius: 16px;
  background: rgba(21, 101, 255, .12); color: var(--primary, #1565ff);
}
.modal-card .modal-ico .ico { width: 28px; height: 28px; }
.modal-card .modal-ico.app-update-icon { width: 42px; height: 42px; border-radius: 13px; }
.modal-card .modal-ico.app-update-icon .ico { width: 21px; height: 21px; }
.update-version { margin-top: 2px; color: #f97316; font-weight: 850; }
.update-notes { margin-top: 16px; padding: 14px; text-align: left; border-radius: 14px; background: var(--surface-inset); border: 1px solid var(--line); }
.update-notes[hidden] { display: none; }
.update-notes ul { margin: 9px 0 0 20px; color: var(--ink-soft); }
.update-notes li + li { margin-top: 6px; }

/* Settings → Push Notifications: the enable action is present only where it can do something, so it
   has to be able to disappear. `.btn` sets display:inline-flex, which would otherwise beat the UA's
   `[hidden] { display: none }` and leave a permanently visible button — the same guard the reply
   composer, plus menu, and update notes above already carry. */
.push-enable-btn[hidden] { display: none; }

/* Spinner state for the notification Refresh button while it re-checks and re-registers. */
@keyframes unify-spin { to { transform: rotate(360deg); } }
.icon-btn.spin .ico { animation: unify-spin .8s linear infinite; }

/* Ring spinner for the "Signing you in…" state shown after a Google account is chosen. */
.gsi-spinner {
  width: 38px; height: 38px; margin: 0 auto; border-radius: 50%;
  border: 3px solid var(--line); border-top-color: var(--primary);
  animation: unify-spin .8s linear infinite;
}

.lightbox {
  position: fixed; inset: 0; z-index: 2000;
  display: grid; place-items: center;
  background: rgba(8, 11, 18, .94);
  -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px);
  opacity: 0; transition: opacity .2s ease;
  touch-action: none;
  overscroll-behavior: contain;
  padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left);
}
.lightbox.show { opacity: 1; }
.lightbox.closing { opacity: 0; }
.lightbox-stage {
  width: 100%; height: 100%;
  display: grid; place-items: center; overflow: hidden;
  touch-action: none;
  overscroll-behavior: contain;
}
.lightbox-img {
  max-width: 100vw; max-height: 100vh;
  max-width: 100dvw; max-height: 100dvh;
  object-fit: contain;
  transform: scale(.94);
  transition: transform .22s cubic-bezier(.2, .8, .2, 1);
  will-change: transform;
  user-select: none; -webkit-user-select: none; -webkit-touch-callout: none;
  touch-action: none;
}
.lightbox.show .lightbox-img { transform: scale(1); }
.lightbox-img.zoomed { cursor: grab; transition: none; }
.lightbox-bar {
  position: absolute; z-index: 2; top: 0; left: 0; right: 0;
  display: flex; justify-content: flex-end; align-items: center; gap: 10px;
  padding: calc(env(safe-area-inset-top) + 12px) calc(env(safe-area-inset-right) + 12px) 12px;
}
.lightbox-count {
  margin-right: auto; margin-left: 4px; color: #fff; font-weight: 800; font-size: .95rem;
  background: rgba(255,255,255,.14); border-radius: 999px; padding: 8px 12px;
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
}
.lightbox-icon-btn {
  width: 44px; height: 44px; border-radius: 50%; flex: 0 0 auto;
  display: grid; place-items: center;
  background: rgba(255, 255, 255, .15); color: #fff; border: none; cursor: pointer;
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  transition: background .15s ease;
}
.lightbox-icon-btn:hover { background: rgba(255, 255, 255, .26); }
.lightbox-icon-btn .ico { width: 24px; height: 24px; }
.lightbox-nav {
  position: absolute; z-index: 2; top: 50%; transform: translateY(-50%);
  width: 48px; height: 48px; border-radius: 50%; border: 0; cursor: pointer;
  display: grid; place-items: center; color: #fff; background: rgba(255,255,255,.14);
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
}
.lightbox-nav .ico { width: 26px; height: 26px; }
.lightbox-prev { left: calc(env(safe-area-inset-left) + 14px); }
.lightbox-next { right: calc(env(safe-area-inset-right) + 14px); }
.lightbox-nav:active { transform: translateY(-50%) scale(.96); }
.lightbox-menu-wrap { position: relative; }
/* The optional actions, hidden until the member opens the ⋮ menu. */
.lightbox-menu {
  position: fixed; top: 64px; right: 12px; z-index: 3;
  min-width: 184px; padding: 6px;
  max-width: calc(100vw - 24px);
  background: var(--card, #fff); color: var(--text, #111);
  border-radius: 14px; box-shadow: 0 16px 40px rgba(8, 11, 18, .4);
  display: flex; flex-direction: column;
}
.lightbox-menu[hidden] { display: none; }
.lightbox-menu-item {
  display: flex; align-items: center; gap: 12px;
  padding: 11px 12px; border: none; background: none; cursor: pointer;
  font: inherit; color: inherit; text-align: left; border-radius: 10px;
}
.lightbox-menu-item:hover { background: var(--surface-inset, rgba(0, 0, 0, .06)); }
.lightbox-menu-item .ico { width: 20px; height: 20px; color: var(--primary, #1565ff); }

@media (max-width: 560px) {
  .lightbox-nav { display: none; }
  .lightbox-menu {
    left: max(16px, env(safe-area-inset-left));
    right: max(16px, env(safe-area-inset-right));
    top: auto;
    bottom: calc(16px + env(safe-area-inset-bottom));
    width: auto;
    min-width: 0;
    border-radius: 16px;
    padding: 8px;
  }
  .lightbox-menu-item { min-height: 48px; font-size: 1rem; }
}

@media (prefers-reduced-motion: reduce) {
  .lightbox, .lightbox-img { transition: none; }
}

/* ---- Gallery (church photo albums) ----------------------------------------------------
   Compact, Instagram-inspired but deliberately smaller: small thumbnails, generous spacing,
   vertical scrolling, no oversized images. Photos only expand (to the full-screen lightbox) when
   tapped. Reuses the app's surface / line / shadow tokens so it matches every other screen. */
.gal-grid { display: flex; flex-direction: column; gap: 12px; }
.gal-cat {
  display: flex; align-items: center; gap: 14px; width: 100%; text-align: left; cursor: pointer;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg);
  padding: 12px 14px; box-shadow: var(--shadow-sm); transition: transform .1s var(--ease), border-color .15s var(--ease);
}
.gal-cat:active { transform: scale(.99); }
.gal-cat-cover {
  flex: 0 0 auto; width: 60px; height: 60px; border-radius: 14px; overflow: hidden;
  background: var(--surface-inset); color: var(--muted); display: grid; place-items: center;
}
.gal-cat-cover img { width: 100%; height: 100%; object-fit: cover; display: block; }
.gal-cat-cover .ico { width: 26px; height: 26px; }
.gal-cat-body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.gal-cat-name { font-weight: 800; font-size: 1rem; color: var(--ink); }
.gal-cat-meta { font-size: .8rem; color: var(--muted); }
.gal-cat .chev { flex: 0 0 auto; color: var(--muted); width: 20px; height: 20px; }

.gal-cap-input {
  width: 100%; margin: 0 0 10px; border: 1px solid var(--line); border-radius: 14px;
  padding: 11px 14px; background: var(--surface); color: var(--ink); font: inherit;
}

/* Gallery compose: each picked photo previewed with a caption box right next to it before sending. */
.gal-compose { display: flex; flex-direction: column; gap: 12px; }
.gal-compose-row {
  display: flex; align-items: center; gap: 12px; background: var(--surface);
  border: 1px solid var(--line); border-radius: 16px; padding: 10px; box-shadow: var(--shadow-sm);
}
.gal-compose-thumb {
  width: 64px; height: 64px; flex: 0 0 64px; border-radius: 12px; object-fit: cover;
  background: var(--chip, #eef1f6); display: block;
}
.gal-compose-cap {
  flex: 1 1 auto; min-width: 0; border: 1px solid var(--line); border-radius: 12px;
  padding: 10px 12px; background: var(--bg, var(--surface)); color: var(--ink); font: inherit;
}
.gal-compose-actions { display: flex; gap: 10px; margin: 16px 0 8px; }

/* The photo feed: small thumbnails in compact cards, vertical scroll, never oversized. */
.gal-feed { display: flex; flex-direction: column; gap: 10px; }
.gal-photo {
  display: flex; align-items: center; gap: 12px; width: 100%; text-align: left; cursor: pointer;
  background: var(--surface); border: 1px solid var(--line); border-radius: 16px;
  padding: 10px; box-shadow: var(--shadow-sm); transition: transform .1s var(--ease);
}
.gal-photo:active { transform: scale(.99); }
.gal-photo-thumb {
  flex: 0 0 auto; width: 72px; height: 72px; border-radius: 12px; overflow: hidden;
  background: var(--surface-inset);
}
.gal-photo-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.gal-photo-body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.gal-photo-cap { font-weight: 700; font-size: .92rem; color: var(--ink); }
.gal-photo-meta { font-size: .78rem; color: var(--muted); }
.gal-photo-stats { display: flex; align-items: center; gap: 12px; font-size: .78rem; color: var(--muted); }
.gal-photo-cc { display: inline-flex; align-items: center; gap: 4px; }
.gal-photo-cc .ico { width: 15px; height: 15px; }
.gal-rxn-sum { display: inline-flex; align-items: center; gap: 4px; }

/* The single-photo screen: the full image (tap to open the full-screen viewer), caption, reactions,
   and the comment thread below. The image is capped so it never dominates the screen. */
.gal-view {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg);
  padding: 10px; box-shadow: var(--shadow-sm); margin-bottom: 16px;
}
.gal-view-img {
  display: block; width: 100%; max-height: 62vh; object-fit: contain; border-radius: 14px;
  background: var(--surface-inset); cursor: zoom-in;
  -webkit-user-select: none; user-select: none; -webkit-touch-callout: none;
}
.gal-view-meta { padding: 10px 4px 2px; }
.gal-view-cap { font-weight: 600; color: var(--ink); line-height: 1.5; margin-bottom: 4px; white-space: pre-wrap; }

/* Comments */
.gal-comments { display: flex; flex-direction: column; gap: 14px; margin-bottom: 8px; }
.gal-cmt { display: flex; gap: 10px; align-items: flex-start; }
.gal-cmt-main { flex: 1 1 auto; min-width: 0; }
.gal-cmt-head { display: flex; align-items: center; gap: 8px; }
.gal-cmt-head b { font-size: .9rem; color: var(--ink); }
.gal-cmt-del { flex: 0 0 auto; margin-left: auto; width: 26px; height: 26px; color: var(--muted); }
.gal-cmt-del .ico { width: 14px; height: 14px; }
.gal-cmt-body { font-size: .92rem; color: var(--ink-soft); line-height: 1.45; margin-top: 2px; white-space: pre-wrap; word-break: break-word; }
/* Keep GIF / photo replies small. */
.gal-cmt-img {
  display: block; max-width: 150px; max-height: 130px; width: auto; border-radius: 12px;
  margin-top: 6px; cursor: zoom-in; background: var(--surface-inset);
  -webkit-user-select: none; user-select: none; -webkit-touch-callout: none;
}

.news-filter-switch {
  display: inline-flex; padding: 4px; margin: 0 0 18px; border: 1px solid var(--line);
  border-radius: 999px; background: var(--surface-inset); gap: 3px;
}
.news-filter-switch button {
  border: 0; background: transparent; color: var(--muted); font: inherit; font-weight: 700;
  padding: 8px 14px; border-radius: 999px; cursor: pointer;
}
.news-filter-switch button.active { background: var(--surface); color: var(--ink); box-shadow: var(--shadow-sm); }
.news-filter-switch span { display: inline-grid; place-items: center; min-width: 20px; height: 20px; padding: 0 5px; border-radius: 999px; background: var(--brand); color: white; font-size: .72rem; }
.news-filter-switch button.active span { background: #e3e8f2; color: #111827; }
[data-theme="dark"] .news-filter-switch button.active span { background: #293858; color: #fff; }
.news-compose-actions { display: flex; gap: 10px; margin-bottom: 16px; align-items: center; flex-wrap: wrap; }
.news-compose-actions > .btn { flex: 1 1 210px; margin: 0; }
.news-compose-tools { display: inline-flex; align-items: center; gap: 10px; margin-left: auto; }
.news-toolbar-btn { width: 46px; height: 46px; padding: 0; border-radius: 50%; }
.news-toolbar-btn .ico { width: 20px; height: 20px; }
.announcement-composer-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.announcement-composer-head h2 { margin: 0; }
.announcement-people-btn { width: 42px; height: 42px; flex: 0 0 auto; border-radius: 50%; }
.announcement-people-btn .ico { width: 20px; height: 20px; }
.recipient-groups-overlay{padding:18px}
.recipient-groups-modal{width:min(560px,100%);max-height:min(82vh,720px);display:flex;flex-direction:column;padding:0;overflow:hidden;text-align:left}
.recipient-groups-head{display:flex;align-items:center;gap:12px;padding:18px;border-bottom:1px solid var(--line)}
.recipient-groups-head h2{margin:0}.recipient-groups-head p{margin:3px 0 0}
.recipient-group-list{display:grid;gap:10px;padding:14px 18px;overflow-y:auto;-webkit-overflow-scrolling:touch}
.recipient-group-choice{width:100%;display:flex;align-items:center;gap:12px;padding:12px;border:1px solid var(--line);border-radius:14px;background:var(--surface);color:var(--ink);text-align:left;cursor:pointer;transition:transform .14s var(--ease),border-color .2s var(--ease),background-color .2s var(--ease),box-shadow .2s var(--ease);-webkit-tap-highlight-color:transparent}
.recipient-group-choice:hover{border-color:var(--line-strong);background:var(--surface-inset)}
.recipient-group-choice:active{transform:scale(.99)}
.recipient-group-choice:focus-visible{outline:3px solid rgba(21,101,255,.24);outline-offset:2px}
.recipient-group-choice>span:not(.recipient-group-check){display:grid;gap:2px;min-width:0}.recipient-group-choice small{color:var(--muted)}
.recipient-group-choice.is-selected{border-color:var(--primary);background:var(--primary-soft);box-shadow:0 0 0 1px var(--primary)}
.recipient-group-check{display:grid;place-items:center;width:28px;height:28px;border:1px solid var(--line-strong);border-radius:50%;color:transparent;background:var(--surface);flex:0 0 auto}
.recipient-group-check .ico{width:16px;height:16px}.recipient-group-choice.is-selected .recipient-group-check{border-color:var(--primary);background:var(--primary);color:#fff}
.recipient-groups-actions{display:flex;gap:10px;padding:14px 18px calc(14px + env(safe-area-inset-bottom));border-top:1px solid var(--line);background:var(--surface)}
.announcement-primary-actions{display:grid;grid-template-columns:repeat(auto-fit,minmax(110px,1fr));gap:10px;margin-top:18px}
.announcement-primary-actions .btn{width:100%;height:var(--touch);min-height:var(--touch);padding:0 14px;align-items:center;justify-content:center}
.announcement-primary-actions .btn .ico{align-self:center}
.announcement-delete-actions{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-top:10px}
.announcement-members-modal{width:min(620px,100%)}
.announcement-member-lists{padding:0 18px 18px;overflow-y:auto;-webkit-overflow-scrolling:touch}
.announcement-member-title{margin-top:18px}
.announcement-member-title h2{font-size:1rem}
.announcement-member-section{display:grid;gap:8px}
.announcement-member-section>.muted{padding:14px;margin:0;text-align:center;border:1px dashed var(--line);border-radius:14px}
.announcement-member-row{padding:10px 12px;border:1px solid var(--line);border-radius:14px;background:var(--surface)}
.member-added-status{display:inline-flex;align-items:center;gap:5px;color:var(--green,#16803c);font-size:.76rem;font-weight:800;white-space:nowrap}
.member-added-status .ico{width:15px;height:15px}
@media(max-width:560px){.recipient-groups-overlay{align-items:flex-end;padding:0}.recipient-groups-modal{width:100%;max-height:92dvh;border-radius:18px 18px 0 0}}
.announcement-audience-confirm { display: flex; align-items: center; gap: 11px; margin-top: 12px; padding: 12px 14px; border-radius: 14px; background: var(--soft); color: var(--ink); }
.announcement-audience-confirm > .ico { flex: 0 0 auto; color: var(--primary); }
.announcement-audience-confirm span { display: grid; gap: 2px; }
.announcement-audience-confirm small { color: var(--muted); }
.announcement-audience { display: inline-flex; align-items: center; gap: 5px; color: var(--muted); font-size: .78rem; font-weight: 700; }
.announcement-audience .ico { width: 14px; height: 14px; }
@media (max-width: 620px) {
  .news-compose-actions > .btn { min-width: min(100%, 190px); }
}
.category-unread-count {
  position: absolute; top: 7px; right: 7px; min-width: 22px; height: 22px; padding: 0 6px;
  display: grid; place-items: center; border-radius: 999px; background: #f97316; color: #fff;
  font-size: .72rem; font-weight: 800; box-shadow: 0 2px 8px rgba(249,115,22,.36);
}
.cat-card { position: relative; }
.bubble.notification-target-highlight {
  outline: 2px solid rgba(21,101,255,.58);
  box-shadow: 0 0 0 5px rgba(21,101,255,.14), 0 8px 24px rgba(21,101,255,.12);
}
[data-ann-detail].notification-target-highlight,
[data-feedback-id].notification-target-highlight,
[data-group-detail].notification-target-highlight,
[data-category-detail].notification-target-highlight,
[data-study-detail].notification-target-highlight,
[data-meal-detail].notification-target-highlight,
[data-event-detail].notification-target-highlight,
[data-gallery-photo-detail].notification-target-highlight {
  outline: 3px solid rgba(21,101,255,.48);
  box-shadow: 0 0 0 7px rgba(21,101,255,.12);
}
.managed-group-links .set-row:last-child { border-bottom: 0; }
.notification-retry {
  position: fixed; z-index: 10020; left: 50%; bottom: calc(86px + env(safe-area-inset-bottom));
  transform: translateX(-50%); width: min(92vw, 520px); display: flex; align-items: center;
  gap: 12px; padding: 12px 14px; border-radius: 14px; background: #172033; color: white;
  box-shadow: 0 16px 44px rgba(15,23,42,.34); font-size: .88rem;
}
.notification-retry span { flex: 1; }
.notification-retry button { border: 0; border-radius: 999px; padding: 8px 13px; background: white; color: #172033; font: inherit; font-weight: 800; }
.app-update-prompt .modal-card{max-width:390px}
.app-update-actions{display:grid;gap:9px;margin-top:20px}
.app-update-spinner{border:4px solid rgba(21,101,255,.18);border-top-color:var(--primary,#1565ff);border-radius:50%!important;background:transparent!important;animation:unify-spin .8s linear infinite}
.btn-spinner{display:inline-block;width:16px;height:16px;margin-right:8px;vertical-align:-3px;border:2px solid currentColor;border-right-color:transparent;border-radius:50%;animation:unify-spin .7s linear infinite}

/* ---- Settings navigation drawer -------------------------------------------
   A left slide-out menu over the existing Settings page. Presentation only: it
   chooses which of the already-rendered sections is on screen. Built from
   Unify's own tokens, so it follows the light/dark appearance system with no
   theme of its own. */
.set-drawer {
  position: fixed; inset: 0; z-index: 2200;
  background: rgba(8, 11, 18, .48);
  -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
  opacity: 0; transition: opacity .2s var(--ease);
  touch-action: none; overscroll-behavior: contain;
}
.set-drawer.show { opacity: 1; }
.set-drawer.closing { opacity: 0; }
.set-drawer-panel {
  position: absolute; top: 0; bottom: 0; left: 0;
  width: min(84vw, 310px); display: flex; flex-direction: column;
  background: var(--surface); color: var(--ink);
  border-right: 1px solid var(--line);
  box-shadow: 12px 0 40px rgba(15, 27, 61, .22);
  padding-left: env(safe-area-inset-left);
  transform: translateX(-101%); transition: transform .24s var(--ease);
  touch-action: auto;
}
.set-drawer.show .set-drawer-panel { transform: translateX(0); }
.set-drawer-head {
  display: flex; align-items: center; gap: 12px;
  padding: calc(12px + env(safe-area-inset-top)) 14px 12px;
  border-bottom: 1px solid var(--line); flex: 0 0 auto;
}
.set-drawer-head .icon-btn { width: 42px; height: 42px; }
.set-drawer-title { font-family: var(--font-display); font-weight: 700; font-size: 1.06rem; }
.set-drawer-list {
  flex: 1 1 auto; overflow-y: auto; overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  padding: 10px 10px calc(16px + env(safe-area-inset-bottom));
}
.set-drawer-item {
  display: flex; align-items: center; gap: 12px; width: 100%;
  min-height: var(--touch); padding: 11px 12px; margin-bottom: 2px;
  border: 0; border-radius: var(--radius-sm); background: none;
  color: var(--ink-soft); font: inherit; font-size: .94rem; font-weight: 600;
  text-align: left; cursor: pointer;
  transition: background-color .16s, color .16s;
}
.set-drawer-item .ico-wrap { display: grid; place-items: center; flex: 0 0 auto; color: var(--muted); }
.set-drawer-item .grow { min-width: 0; }
.set-drawer-item:hover { background: var(--surface-inset); color: var(--ink); }
.set-drawer-item.active { background: var(--soft); color: var(--soft-ink); font-weight: 750; }
.set-drawer-item.active .ico-wrap { color: var(--soft-ink); }
@media (prefers-reduced-motion: reduce) {
  .set-drawer, .set-drawer-panel { transition: none; }
}

/* Settings page-head hamburger, ahead of the "Settings" title. */
.page-head .set-menu-btn { width: 44px; height: 44px; }

/* One settings section at a time. The drawer is the table of contents, so every
   section the member is not currently viewing is taken off screen while staying
   in the DOM, with its ids, state, loaded content and wired handlers intact.
   `!important` because the blocks it hides carry their own display rules. */
.set-section-off { display: none !important; }

/* Profile-area organization switch (only rendered for accounts the server has
   authorized in more than one organization). */
.profile-org { margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--line); }
.profile-org > .k { color: var(--muted); font-size: .8rem; font-weight: 600; }
.profile-org-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 5px; }
.profile-org-row > .v { font-family: var(--font-display); font-weight: 700; min-width: 0; }
.profile-org-list:not([hidden]) { margin-top: 6px; border-top: 1px solid var(--line); }
