/* ===========================================================================
   Pingwire — Telegram-style APP skin (authenticated pages ONLY).

   Loaded via render_app_skin() AFTER style.css, and every rule is scoped
   under .app-body — the public marketing/SEO pages never load this file and
   never carry that class, so they are pixel-identical with or without it.

   ACCENT SWAP: --accent below is the single token that shifts the whole
   app's accent (buttons, FAB, badges, outgoing bubbles, links derive from
   it). Default is the Pingwire BRAND BLUE #2563EB — the same --primary the
   marketing pages and the logo use, so app and site share ONE palette
   (unified 2026-07-29). Swapping it to any other hue still re-accents
   everything in one line.
   =========================================================================== */

/* ---------- Tokens: dark (primary theme) ---------- */
.app-body {
  --accent: #2563EB;            /* ← the ONE swappable accent token (brand blue) */
  --accent-contrast: #ffffff;
  --accent-hover: #1D4ED8;
  --accent-text: #93C5FD;       /* accent for TEXT on dark surfaces (a11y) */
  --accent-soft: rgba(37, 99, 235, .16);

  /* Dark surfaces = the SAME slate ladder style.css uses for its dark
     sections, so app and marketing dark surfaces are one system. */
  --bg: #0B1220;
  --bg-dark: #0B1220;
  --surface: #111827;
  --surface-2: #1B2435;
  --surface-3: #243049;
  --surface-raised: #1B2435;
  --divider: rgba(255, 255, 255, .06);
  --border: var(--divider);
  --border-dark: var(--divider);
  --border-strong: rgba(255, 255, 255, .12);
  --text: #FFFFFF;
  --text-dark: #FFFFFF;
  --muted: #94A3B8;
  --text-muted: #94A3B8;
  --bubble-in: #1B2435;
  --bubble-out: #2E55B4;        /* accent, slightly desaturated */
  --bubble-out-text: #FFFFFF;

  /* Existing component rules key off these — remapping them inside .app-body
     re-skins every legacy rule without touching style.css. */
  --primary: var(--accent);
  --primary-dark: var(--accent-hover);
  --accent-ui: var(--accent);

  --r-sm: 10px; --r-md: 14px; --r-lg: 20px; --r-pill: 999px; --r-bubble: 16px;
  --radius-card: var(--r-md);
  --radius-bubble: var(--r-bubble);
  --radius-sm: var(--r-sm); --radius-md: var(--r-md); --radius-lg: var(--r-lg);

  --shadow-fab: 0 6px 18px rgba(0, 0, 0, .4);
  --motion: 180ms ease;

  /* Mobile shell: bottom tab bar metrics (includes/partials/bottom-nav.php). */
  --tabbar-h: 56px;
  --tabbar-total: calc(var(--tabbar-h) + env(safe-area-inset-bottom, 0px));
  --prem-cta-h: 76px;           /* 12px pad + 52px .btn--lg + 12px pad */

  font-family: -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
  font-size: 15px;
  line-height: 1.35;
  background: var(--bg);
  color: var(--text);
}

/* ---------- Tokens: light theme (data-theme flips it; toggle in app-skin.js) ---------- */
.app-body[data-theme="light"] {
  --bg: #FFFFFF;
  --bg-dark: #FFFFFF;
  --surface: #FFFFFF;
  --surface-2: #F1F5F9;
  --surface-3: #E2E8F0;
  --surface-raised: #FFFFFF;
  --divider: #E2E8F0;
  --border-strong: #CBD5E1;
  --text: #0F172A;
  --text-dark: #0F172A;
  --muted: #64748B;
  --text-muted: #64748B;
  --accent-text: #1D4ED8;
  --bubble-in: #F1F5F9;
  --bubble-out: #DBEAFE;        /* accent tint */
  --bubble-out-text: #0F172A;
  --shadow-fab: 0 6px 18px rgba(15, 23, 42, .18);
}
/* Legacy app rules hardcode white text in a few spots — bring them back to
   tokens so light theme stays readable. */
.app-body[data-theme="light"] .conv-item__name,
.app-body[data-theme="light"] .chat__header-name,
.app-body[data-theme="light"] .chat__list-head h2,
.app-body[data-theme="light"] .msg__title,
.app-body[data-theme="light"] h1, .app-body[data-theme="light"] h2, .app-body[data-theme="light"] h3,
.app-body[data-theme="light"] .app-topbar .brand__name,
.app-body[data-theme="light"] .app-topbar__nav a.is-active,
.app-body[data-theme="light"] .text-strong { color: var(--text); }
.app-body[data-theme="light"] .app-topbar__nav a:hover { color: var(--text); }
.app-body[data-theme="light"] .field input, .app-body[data-theme="light"] .field select,
.app-body[data-theme="light"] .field textarea { background: var(--surface-2); color: var(--text); border-color: var(--divider); }

/* ---------- Base rhythm ---------- */
.app-body h1 { font-size: 1.35rem; }
.app-body h2 { font-size: 1.05rem; font-weight: 600; }
.app-body .panel-lead, .app-body .card__desc { font-size: .9rem; }
.app-body .app-topbar { background: var(--surface); border-bottom: 1px solid var(--divider); }
.app-body a { color: var(--accent-text); }
.app-body .app-topbar__nav a { color: var(--muted); }

/* ---------- Buttons: pill, calm motion ---------- */
.app-body .btn { border-radius: var(--r-pill); transition: background var(--motion), transform var(--motion), box-shadow var(--motion), opacity var(--motion); }
.app-body .btn--primary { background: var(--accent); color: var(--accent-contrast); }
.app-body .btn--primary:hover { background: var(--accent-hover); box-shadow: none; }
.app-body .btn--ghost { color: var(--text); border-color: var(--border-strong); }
.app-body .btn:hover { transform: none; }
.app-body .btn:active { transform: scale(.97); }
.app-body :focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ---------- Cards → section cards ---------- */
.app-body .card, .app-body .modal-card {
  background: var(--surface); border: 1px solid var(--divider);
  border-radius: var(--r-md); box-shadow: none;
}

/* ---------- Avatars: circular, deterministic gradient fills ---------- */
.app-body .avatar { border-radius: 50%; background: var(--surface-3); font-size: .95rem; }
.app-body .avatar--g0 { background: linear-gradient(135deg, #FF885E, #FF516A); }
.app-body .avatar--g1 { background: linear-gradient(135deg, #FFCD6A, #FFA85C); }
.app-body .avatar--g2 { background: linear-gradient(135deg, #82B1FF, #665FFF); }
.app-body .avatar--g3 { background: linear-gradient(135deg, #A0DE7E, #54CB68); }
.app-body .avatar--g4 { background: linear-gradient(135deg, #53EDD6, #28C9B7); }
.app-body .avatar--g5 { background: linear-gradient(135deg, #72D5FD, #2A9EF1); }
.app-body .avatar--g6 { background: linear-gradient(135deg, #E0A2F3, #D669ED); }

/* ---------- Chat list: search pill + breathing two-line rows ---------- */
.app-body .chat__list { border-right: 1px solid var(--divider); }
.app-body .chat__list-head { border-bottom: 0; padding-bottom: var(--s2); }
.app-body .chat__search input {
  min-height: 40px; border-radius: var(--r-pill); background: var(--surface-2);
  border: 1px solid transparent; padding: var(--s2) var(--s4); text-align: left;
}
.app-body .chat__search input:focus { border-color: var(--accent); box-shadow: none; background: var(--bg); }
.app-body .conv-item {
  align-items: center; min-height: 70px; padding: var(--s3) var(--s4);
  border-bottom: 0; position: relative; transition: background var(--motion);
}
.app-body .conv-item::after { /* inset divider that starts after the avatar */
  content: ""; position: absolute; left: 68px; right: 0; bottom: 0; height: 1px; background: var(--divider);
}
.app-body .conv-item:last-child::after { display: none; }
.app-body .conv-item .avatar { width: 48px; height: 48px; font-size: 1.1rem; }
.app-body .conv-item__name { font-size: 16px; color: var(--text); }
.app-body .conv-item__time { font-size: 12px; }
.app-body .conv-item__preview { font-size: 14px; margin-top: 2px; }
.app-body .conv-item.is-active { background: var(--accent); }
.app-body .conv-item.is-active::after { display: none; }
.app-body .conv-item.is-active .conv-item__name,
.app-body .conv-item.is-active .conv-item__preview,
.app-body .conv-item.is-active .conv-item__time { color: var(--accent-contrast); }
.app-body .conv-item__unread {
  background: var(--accent); align-self: center; min-width: 22px; height: 22px;
  border-radius: var(--r-pill); font-size: 12px;
}
.app-body .conv-item.is-active .conv-item__unread { background: rgba(255,255,255,.28); }
.app-body .conv-type-pill { border-radius: var(--r-pill); border-color: transparent; background: var(--accent-soft); color: var(--accent-text); }
.app-body .conv-item.is-active .conv-type-pill { background: rgba(255,255,255,.22); color: #fff; }

/* ---------- Thread: date pill, bubbles with tails, ticks, composer ---------- */
.app-body .chat__header { border-bottom: 1px solid var(--divider); }
.app-body .date-pill {
  align-self: center; margin: var(--s2) 0; padding: 3px 12px;
  background: rgba(0, 0, 0, .28); color: #fff;
  border-radius: var(--r-pill); font-size: 13px; user-select: none;
}
.app-body[data-theme="light"] .date-pill { background: rgba(15, 23, 42, .35); }

.app-body .msg { max-width: 78%; }
/* ⚠️ CHANNEL FEED messages are BROADCAST content, not a two-sided conversation, so
   they span the FULL width with ONE consistent left edge — not right-aligned "mine"
   bubbles whose left edge shifts with content length (the inconsistency found
   2026-08-08: a shorter middle bubble indented further than its neighbours). This
   restores style.css's feed-msg full-width intent, which the app-skin's 78% + mine
   right-align had overridden. */
.app-body .feed-msg { max-width: 100%; margin-left: 0; flex-direction: row; }
.app-body .feed-msg .msg__bubble { width: 100%; border-bottom-right-radius: var(--r-bubble); border-bottom-left-radius: 4px; }
.app-body .feed-msg .msg__bubble::before { left: -5px; right: auto; transform: none; }
.app-body .msg__bubble {
  position: relative; background: var(--bubble-in); border: 0;
  border-radius: var(--r-bubble); border-bottom-left-radius: 4px;
  padding: var(--s2) var(--s3) 6px;
}
.app-body .msg__bubble::before { /* tail on the outer bottom corner */
  content: ""; position: absolute; bottom: 0; left: -5px; width: 10px; height: 12px;
  background: inherit; clip-path: path("M10 0 C10 7 6 11 0 12 L10 12 Z");
}
.app-body .msg--mine .msg__bubble {
  background: var(--bubble-out); border-radius: var(--r-bubble); border-bottom-right-radius: 4px;
}
.app-body .msg--mine .msg__bubble::before { left: auto; right: -5px; transform: scaleX(-1); }
.app-body .msg--system .msg__bubble::before, .app-body .feed-msg .msg__bubble::before { display: none; }
.app-body .msg--mine .msg__body, .app-body .msg--mine .msg__title { color: var(--bubble-out-text); }
.app-body[data-theme="light"] .msg--mine .msg__meta { color: rgba(15, 23, 42, .55); }
.app-body .msg__sender { color: var(--accent-text); }
.app-body .msg__body a { color: var(--accent-text); }
.app-body .msg--mine .msg__body a { color: var(--bubble-out-text); text-decoration: underline; }
.app-body .msg__meta { justify-content: flex-end; margin-top: 2px; font-size: 11px; }
.app-body .msg__ticks { letter-spacing: -2px; }
.app-body .msg__ticks--read { color: #6FE0F6; }
.app-body[data-theme="light"] .msg__ticks--read { color: var(--accent-hover); }

/* Composer (Telegram-style): [ pill: emoji | field | GIF | attach ] [ round send ].
   One flex row pinned at the bottom of the thread column, in-flow (never an overlay),
   with bottom safe-area clearance for the phone's system nav bar. */
.app-body .composer {
  padding: 8px 10px calc(10px + env(safe-area-inset-bottom, 0px));
  background: transparent; border-top: 0;
  display: flex; flex-direction: row; align-items: flex-end; gap: 8px;
  position: static;
}
.app-body .composer__field {
  flex: 1 1 auto; min-width: 0;
  display: flex; align-items: flex-end; gap: 2px;
  background: var(--surface); border: 1px solid var(--divider); border-radius: 24px;
  padding: 0 4px 0 2px; box-shadow: 0 2px 10px rgba(0,0,0,.25);
}
.app-body .composer textarea {
  flex: 1 1 auto; min-width: 0;
  background: transparent; border: 0; border-radius: 0;
  padding: 13px 6px; color: var(--text); font-size: 16px; /* 16px: iOS won't zoom the page on focus */
  min-height: 46px; line-height: 20px;
  max-height: 104px; overflow-y: auto;
}
.app-body .composer textarea:focus, .app-body .composer textarea:focus-visible { outline: none; box-shadow: none; }
.app-body .composer__field:focus-within { border-color: var(--accent); }
.app-body .composer textarea::placeholder { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--muted); }
.app-body .composer__icon {
  flex: 0 0 auto; width: 44px; height: 46px; min-height: 46px; margin: 0;
  display: grid; place-items: center; color: var(--muted); background: transparent; border: 0;
  border-radius: 50%; cursor: pointer; transition: color var(--motion);
}
.app-body .composer__icon:hover { color: var(--text); }
.app-body .composer__gif { width: auto; padding: 0 8px; font-size: 12px; font-weight: 700; letter-spacing: .04em; border: 1.5px solid currentColor; border-radius: 6px; height: 22px; min-height: 22px; align-self: center; }
.app-body .composer__gif[hidden] { display: none; }
.app-body .composer__attach { width: 44px; height: 46px; min-height: 46px; }
.app-body .composer__btn {
  flex: 0 0 auto; width: 48px; height: 48px; min-width: 48px; min-height: 48px;
  position: static; border-radius: 50%; border: 0;
  background: var(--accent); color: var(--accent-contrast);
  display: grid; place-items: center; cursor: pointer; box-shadow: 0 2px 10px rgba(0,0,0,.3);
  transition: background var(--motion), transform var(--motion);
}
.app-body .composer__btn svg { margin-left: -2px; margin-top: 2px; }
.app-body .composer__btn:hover { background: var(--accent-hover); }
.app-body .composer__btn:active { transform: scale(.94); }

/* ---------- Emoji / GIF picker panels (in-flow above the composer) ---------- */
.app-body .picker { flex: 0 0 auto; background: var(--surface); border-top: 1px solid var(--divider); max-height: 46vh; display: flex; flex-direction: column; }
.app-body .picker[hidden] { display: none; }
.app-body .picker__tabs { display: flex; gap: 2px; padding: 6px 8px 0; overflow-x: auto; scrollbar-width: none; }
.app-body .picker__tabs button { flex: 0 0 auto; width: 38px; height: 34px; border: 0; background: transparent; border-radius: 10px; font-size: 18px; cursor: pointer; opacity: .55; }
.app-body .picker__tabs button.is-active { background: var(--surface-2); opacity: 1; }
.app-body .picker__grid { overflow-y: auto; padding: 8px; }
.app-body .picker__grid--emoji { display: grid; grid-template-columns: repeat(auto-fill, minmax(40px, 1fr)); gap: 2px; }
.app-body .picker__emoji { border: 0; background: transparent; font-size: 24px; line-height: 1; height: 40px; border-radius: 8px; cursor: pointer; }
.app-body .picker__emoji:hover { background: var(--surface-2); }
.app-body .picker__search { display: flex; align-items: center; gap: 8px; padding: 8px 10px 4px; }
.app-body .picker__search input { flex: 1; min-width: 0; height: 36px; padding: 0 12px; border-radius: var(--r-pill); border: 1px solid transparent; background: var(--surface-2); color: var(--text); font: inherit; }
.app-body .picker__search input:focus { outline: none; border-color: var(--accent); }
.app-body .picker__credit { font-size: 11px; color: var(--muted); }
.app-body .picker__grid--gif { display: grid; grid-template-columns: repeat(auto-fill, minmax(110px, 1fr)); gap: 6px; }
.app-body .picker__gif { border: 0; padding: 0; background: var(--surface-2); border-radius: 8px; overflow: hidden; aspect-ratio: 4 / 3; cursor: pointer; }
.app-body .picker__gif img { width: 100%; height: 100%; object-fit: cover; display: block; }
.app-body .msg__gif { max-width: 260px; border-radius: 12px; display: block; }

/* ---------- FAB ---------- */
.app-body .fab {
  position: fixed; right: max(16px, env(safe-area-inset-right, 0px));
  bottom: calc(20px + env(safe-area-inset-bottom, 0px));
  width: 56px; height: 56px; border-radius: 50%; border: 0;
  background: var(--accent); color: var(--accent-contrast);
  box-shadow: var(--shadow-fab); display: none; place-items: center;
  cursor: pointer; z-index: 40;
  transition: background var(--motion), transform var(--motion);
}
.app-body .fab:hover { background: var(--accent-hover); }
.app-body .fab:active { transform: scale(.94); }
@media (max-width: 860px) { .app-body .fab { display: grid; } }
/* Per-screen scoping: the FAB belongs to the conversations LIST only. When a
   thread is open (#chat carries .show-thread, set by the existing app.js
   navigation), the FAB is hidden so it can never overlap the composer. */
.app-body .chat.show-thread ~ .fab { display: none; }

/* Chat shell viewport fit: the page is a flex column filling the visible
   viewport, so the composer is ALWAYS on screen — no hard-coded topbar
   height (style.css assumed 56px; the skinned topbar + optional push banner
   are taller, which pushed the composer below the fold). Scoped to app.php's
   chat shell only; every other app page scrolls normally. */
.app-body.chat-page {
  height: 100vh; height: 100dvh;
  /* --app-vh = window.visualViewport.height (app.js initViewportFit): when the
     on-screen keyboard is up, only the VISUAL viewport shrinks (meta
     interactive-widget=resizes-visual; iOS always) — dvh does not follow, so
     the composer at the bottom of a 100dvh box hid under the keyboard. */
  height: var(--app-vh, 100dvh);
  /* ⚠️ RESET the inherited `min-height: 100vh` (style.css:.app-body) — on mobile
     Chrome with the URL bar SHOWN, 100vh is the LARGE (bar-hidden) height, so the
     min-height forced this shell TALLER than its own `height: 100dvh` (the real
     visible height), overflowing and pushing the composer below the fold (found
     2026-08-08, real device). Without this reset, height:100dvh is meaningless
     because min-height:100vh overrides the box size. */
  min-height: 0;
  display: flex; flex-direction: column; overflow: hidden;
}
.app-body.chat-page .app-topbar,
.app-body.chat-page .push-banner { flex: 0 0 auto; }
/* flex-basis 0 (not auto): the chat's size comes from the viewport, never from
   its content, so a long message history can't stretch the page. */
.app-body.chat-page .chat { flex: 1 1 0; min-height: 0; height: auto; }
/* THE pin: .chat is a grid, and its implicit `auto` row grows to fit the whole
   message history, pushing the composer past the viewport. minmax(0, 1fr)
   locks the single row to exactly the container height, so the columns fill
   the screen and ONLY .chat__messages scrolls — the composer, as the last
   flex child of the thread column, is always on screen. */
.app-body.chat-page .chat { grid-template-rows: minmax(0, 1fr); }
.app-body.chat-page .chat__list,
.app-body.chat-page .chat__main { min-height: 0; height: 100%; overflow: hidden; }
.app-body.chat-page .chat__thread { display: flex; flex-direction: column; min-height: 0; flex: 1; }
/* `hidden` must win: an inline display:flex used to override it, so the empty
   thread pane (header + composer) rendered UNDER the placeholder on desktop. */
.app-body.chat-page .chat__thread[hidden] { display: none; }
.app-body.chat-page .chat__messages { flex: 1 1 auto; min-height: 0; overflow-y: auto; }
.app-body.chat-page .composer { flex: 0 0 auto; }

/* ---------- Settings rows (account) ---------- */
.app-body .settings-ico {
  width: 30px; height: 30px; border-radius: 8px; display: inline-grid; place-items: center;
  background: var(--tile, var(--accent)); color: #fff; margin-right: var(--s3);
  flex-shrink: 0; vertical-align: -8px;
}
.app-body .settings-ico svg { width: 17px; height: 17px; }
.app-body .card > h2, .app-body .card .row-between h2 { display: flex; align-items: center; font-size: 16px; }

/* ---------- Reminders: list rows replace the table ---------- */
.app-body .rem-row {
  display: flex; align-items: center; gap: var(--s3);
  min-height: 68px; padding: var(--s3) var(--s2); position: relative;
}
.app-body .rem-row::after { content: ""; position: absolute; left: 60px; right: 0; bottom: 0; height: 1px; background: var(--divider); }
.app-body .rem-row:last-child::after { display: none; }
.app-body .rem-row__body { flex: 1; min-width: 0; }
.app-body .rem-row__top { display: flex; justify-content: space-between; align-items: baseline; gap: var(--s2); }
.app-body .rem-row__title { font-weight: 600; font-size: 15px; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.app-body .rem-row__when { font-size: 12px; color: var(--muted); flex-shrink: 0; text-align: right; }
.app-body .rem-row__sub { font-size: 13px; color: var(--muted); margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; display: flex; gap: var(--s2); align-items: center; }
.app-body .rem-chip {
  font-size: 12px; font-weight: 600; padding: 2px 10px; border-radius: var(--r-pill);
  background: var(--surface-2); color: var(--muted); flex-shrink: 0;
}
.app-body .rem-chip--scheduled { background: var(--accent-soft); color: var(--accent-text); }
.app-body .rem-chip--sent { background: rgba(16, 185, 129, .16); color: #34D399; }
.app-body[data-theme="light"] .rem-chip--sent { color: #047857; }
.app-body .rem-chip--failed { background: rgba(239, 68, 68, .16); color: #F87171; }
.app-body[data-theme="light"] .rem-chip--failed { color: #B91C1C; }
.app-body .rem-row .btn--danger {
  background: transparent; border: 1px solid rgba(239, 68, 68, .45); color: #F87171; border-radius: var(--r-pill);
}
.app-body[data-theme="light"] .rem-row .btn--danger { color: #B91C1C; }
.app-body .rem-row .btn--danger:hover { background: rgba(239, 68, 68, .12); }
.app-body .rem-scroll { border: 0; }

/* ---------- Tabs, dialogs, toasts, tables (admin light touch) ---------- */
.app-body .tabs button { border-radius: var(--r-pill); transition: background var(--motion), color var(--motion); }
.app-body .tabs button.is-active { background: var(--accent); color: var(--accent-contrast); }
.app-body dialog.modal-card { border-radius: var(--r-lg); }
.app-body .toast { border-radius: var(--r-md); background: rgba(23, 33, 43, .92); backdrop-filter: blur(6px); }
.app-body[data-theme="light"] .toast { background: rgba(255, 255, 255, .95); color: var(--text); }
.app-body .table th { color: var(--muted); font-weight: 600; font-size: 13px; }
.app-body .table td, .app-body .table th { border-color: var(--divider); }
.app-body .kpi { border-radius: var(--r-md); }
.app-body .tag-chip { border-radius: var(--r-pill); }

/* ---------- Theme toggle (injected by app-skin.js) ---------- */
.app-body .theme-toggle {
  background: none; border: 0; color: var(--muted); cursor: pointer;
  width: 36px; height: 36px; border-radius: 50%; display: grid; place-items: center;
  font-size: 1.05rem; transition: background var(--motion), color var(--motion);
}
.app-body .theme-toggle:hover { background: var(--surface-2); color: var(--text); }

/* ---------- Admin nav: always-visible wrapping row (2026-07-30) ----------
   Replaces the JS hamburger drawer (banned pattern: transform-hidden links
   stayed keyboard-focusable while invisible, and with JS disabled the admin
   had NO navigation). There is deliberately NO open/close state: at ≤768px
   the links WRAP below the brand — nothing off-screen, nothing focusable-
   but-invisible, fully functional with JS disabled. Scoped to .admin-page
   so user-page topbars are untouched. AA both themes (verified 2026-07-30):
   rest --muted 6.92/4.76:1, hover --text 17.74/17.85:1, active
   --accent-text 9.84/6.70:1 on --surface. */
.app-body.admin-page .app-topbar__nav a { position: relative; }
.app-body.admin-page .app-topbar__nav a.is-active { color: var(--accent-text); }
/* Active = accent color AND a 3px underline pill — never color alone. */
.app-body.admin-page .app-topbar__nav a.is-active::after {
  content: ""; position: absolute; bottom: 0; left: 0; right: 0;
  height: 3px; border-radius: 3px 3px 0 0; background: var(--accent-text);
}
.app-body.admin-page .app-topbar__nav a:focus-visible,
.app-body.admin-page .app-topbar__nav .theme-toggle:focus-visible {
  outline: 2px solid var(--accent-text); outline-offset: -2px; border-radius: 8px;
}

@media (max-width: 768px) {
  /* Static, not sticky, on the mobile shell: the wrapped nav is 3 rows of
     48px links at 320-414px (measured 2026-07-30) — pinning ~200px of
     chrome to the top of a phone would eat a third of the screen. Desktop
     (>768px) keeps the sticky 56px single-row topbar. Static positioning
     also means NO offset math anywhere: everything is in normal flow. */
  .app-body.admin-page .app-topbar {
    position: static; height: auto; min-height: 56px;
    flex-wrap: wrap; padding-bottom: var(--s2);
  }
  /* Full-width wrapping row under the brand. NO overflow container on
     either axis (overflow stays visible), so focus outlines are never
     clipped and the active link can never sit off-screen. */
  .app-body.admin-page .app-topbar__nav {
    flex: 1 1 100%; flex-wrap: wrap; gap: var(--s1) var(--s2);
  }
  .app-body.admin-page .app-topbar__nav a,
  .app-body.admin-page .app-topbar__nav .theme-toggle {
    min-height: 48px; min-width: 48px; width: auto;
    display: inline-flex; align-items: center; justify-content: center;
    padding: 0 var(--s3); border-radius: 8px;
  }
  .app-body.admin-page .app-topbar__nav a:hover { background: var(--surface-2); color: var(--text); }
}

/* ---------- Forms: dark inputs (light overrides above win via [data-theme]) ---------- */
.app-body .field label { color: var(--muted); font-size: 13px; }
.app-body .field input, .app-body .field select, .app-body .field textarea {
  background: var(--surface-2); border: 1px solid transparent; color: var(--text);
  border-radius: var(--r-sm); transition: border-color var(--motion), background var(--motion);
}
.app-body .field input:focus, .app-body .field select:focus, .app-body .field textarea:focus {
  border-color: var(--accent); box-shadow: none; background: var(--bg);
}
.app-body .field input:disabled { opacity: .55; }
/* Native color input: the generic .field input rule made it a FULL-WIDTH box with
   the tiny OS swatch floating inside — looked broken (found 2026-08-08). Style it as
   a proper compact swatch: fixed size, rounded, the chosen color filling the whole
   control with no chrome. */
.app-body .field input[type="color"] {
  -webkit-appearance: none; -moz-appearance: none; appearance: none;
  width: 56px; height: 40px; padding: 4px; cursor: pointer; background: var(--surface-2);
}
.app-body .field input[type="color"]::-webkit-color-swatch-wrapper { padding: 0; }
.app-body .field input[type="color"]::-webkit-color-swatch { border: 1px solid var(--border); border-radius: var(--r-sm); }
.app-body .field input[type="color"]::-moz-color-swatch { border: 1px solid var(--border); border-radius: var(--r-sm); }
.app-body .field input[type="color"]:disabled { cursor: not-allowed; }

/* ---------- Auth (login/register): centered card, same skin ---------- */
.app-body.auth-body { background: var(--bg); }
.app-body .auth-card { background: var(--surface); border: 1px solid var(--divider); border-radius: var(--r-lg); box-shadow: none; }
.app-body .auth-card h1 { color: var(--text); font-size: 1.3rem; }
.app-body .auth-sub, .app-body .auth-alt { color: var(--muted); }
.app-body .auth-brand span { color: var(--text); }
.app-body .auth-alt a, .app-body .auth-card a { color: var(--accent-text); }

/* ---------- Tap targets: phone-first, everything ≥44px ---------- */
.app-body .btn--sm { min-height: 44px; padding: var(--s2) var(--s4); }
.app-body .chat__back { min-width: 44px; min-height: 44px; }

/* ===========================================================================
   LAYOUT PASS — mockup alignment. Presentational only.
   Placed LAST so these rules win over the base skin above.
   =========================================================================== */

/* --- 1. Buttons: one system, ~48px, pill, clear separation. Canonical hooks
       stay the existing .btn--primary/.btn--ghost/.btn--danger (renaming them
       would churn every template/JS); .btn-primary/.btn-secondary/.btn-danger
       are first-class aliases of the same three styles. --- */
.app-body .btn, .app-body .btn-primary, .app-body .btn-secondary, .app-body .btn-danger {
  min-height: 48px; padding: 12px 20px; border-radius: var(--r-pill);
  display: inline-flex; align-items: center; justify-content: center;
  font-weight: 600; font-size: 15px; border: 1px solid transparent; cursor: pointer;
}
.app-body .btn-primary { background: var(--accent); color: var(--accent-contrast); }
.app-body .btn-primary:hover { background: var(--accent-hover); }
.app-body .btn-secondary, .app-body .btn--ghost { background: var(--surface-2); color: var(--text); border-color: transparent; }
.app-body .btn-secondary:hover, .app-body .btn--ghost:hover { background: var(--surface-3); color: var(--text); }
/* ⚠️ A BARE `.btn` (no --primary/--ghost/--danger) previously had NO background or
   color in the app skin — it rendered gray-on-gray and read as DISABLED (e.g. the
   /incidents Filter button, found 2026-08-08). A bare .btn now defaults to the
   secondary treatment so it always looks like a real, tappable secondary action. */
.app-body .btn:not(.btn--primary):not(.btn-primary):not(.btn--ghost):not(.btn-secondary):not(.btn--danger):not(.btn-danger):not(.btn--light):not(.btn--danger-ghost) {
  background: var(--surface-2); color: var(--text);
}
.app-body .btn:not(.btn--primary):not(.btn-primary):not(.btn--ghost):not(.btn-secondary):not(.btn--danger):not(.btn-danger):not(.btn--light):not(.btn--danger-ghost):hover {
  background: var(--surface-3);
}
/* ⚠️ DISABLED = LEGIBLE **AND** INERT (owner 2026-08-08: "illegible is a third state
   worse than either"). An earlier `opacity:.45` dimmed the whole button including the
   label, so a disabled .btn--primary was unreadable grey-on-blue. Instead we REPLACE
   the fill with a muted surface and keep the label at full readable contrast, plus a
   not-allowed cursor. Reads clearly as "off" without ever going illegible. The !important
   overrides the variant's own background/color (which win on specificity otherwise). */
.app-body .btn:disabled,
.app-body .btn[disabled],
.app-body .btn[aria-disabled="true"] {
  background: var(--surface-3) !important;
  color: var(--muted) !important;
  border-color: transparent !important;
  cursor: not-allowed; box-shadow: none; opacity: 1;
}
.app-body .btn:disabled:hover,
.app-body .btn[disabled]:hover { transform: none; background: var(--surface-3) !important; }
.app-body .btn-danger, .app-body .btn--danger { background: #EF4444; color: #fff; border-color: transparent; }
.app-body .btn-danger:hover, .app-body .btn--danger:hover { background: #DC2626; }
.app-body .btn--sm { min-height: 44px; padding: 8px 16px; font-size: 14px; }

/* Separation: a button following form content gets clear air (rule #1). */
.app-body form button[type="submit"], .app-body form > .btn { margin-top: 20px; }
.app-body .field + .btn, .app-body .field + button { margin-top: 20px; }
.app-body .card > .btn, .app-body .card__desc + .btn, .app-body p + .btn { margin-top: 16px; }
/* Aligned control contexts are exempt (headers, list rows, composer, banners, dialogs). */
.app-body .row-between .btn, .app-body .rem-row .btn, .app-body .push-banner .btn,
.app-body .composer button, .app-body .tabs button { margin-top: 0; }

/* --- 2. Conversations list: hard width containment + truncation --- */
.app-body { overflow-x: clip; }
.app-body .chat, .app-body .chat__list, .app-body .chat__main, .app-body .conv-list { min-width: 0; max-width: 100%; }
.app-body .conv-item { max-width: 100%; overflow: hidden; min-height: 70px; }
.app-body .conv-item__body { min-width: 0; flex: 1 1 0; }
.app-body .conv-item__top { min-width: 0; }
.app-body .conv-item__name { min-width: 0; flex: 1 1 auto; }
.app-body .conv-item__time { flex: 0 0 auto; }
.app-body .conv-item__preview { min-width: 0; display: flex; align-items: center; gap: 6px; overflow: hidden; }
.app-body .conv-item__preview .badge, .app-body .conv-item__preview .conv-type-pill,
.app-body .conv-item__preview .muted { flex: 0 0 auto; } /* tags never pushed off; preview text truncates */
.app-body .conv-item__ptext { flex: 1 1 auto; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* FAB never covers the last rows: scroll containers get clearance where it shows. */
@media (max-width: 860px) {
  .app-body .conv-list { padding-bottom: 96px; }
  .app-body .chat__messages { padding-bottom: var(--s6); }
}

/* --- 3. Section nav: horizontal scrollable pill tab bar (mockup Option A) --- */
.app-body .tabs {
  display: flex; flex-wrap: nowrap; gap: 8px; overflow-x: auto;
  border-bottom: 0; margin: 16px 0; padding: 2px 2px 6px;
  -webkit-overflow-scrolling: touch; scrollbar-width: none;
}
.app-body .tabs::-webkit-scrollbar { display: none; }
.app-body .tabs button {
  flex: 0 0 auto; min-height: 44px; padding: 10px 18px; border-radius: var(--r-pill);
  background: var(--surface-2); color: var(--muted); border: 0; font-weight: 500; font-size: 14px;
}
.app-body .tabs button:hover { background: var(--surface-3); color: var(--text); }
.app-body .tabs button.is-active { background: var(--accent); color: var(--accent-contrast); }

/* --- 4. Rhythm: page padding, card gaps, form spacing --- */
.app-body .panel-wrap { padding: 16px 12px 40px; }
.app-body .panel-lead { margin-bottom: 16px; }
.app-body .card { padding: 16px; margin-bottom: 16px; box-shadow: none; }
.app-body .field { margin-bottom: 14px; }
.app-body .grid-2 { gap: 12px 16px; }
@media (max-width: 560px) { .app-body .grid-2 { grid-template-columns: 1fr; } }
@media (min-width: 700px) {
  .app-body .panel-wrap { padding: 24px 16px 48px; }
  .app-body .card { padding: 20px; }
}

/* ===========================================================================
   SETTINGS LIST (Telegram vertical pattern) — account home + premium page.
   =========================================================================== */
.app-body .avatar-edit { display: flex; align-items: center; gap: var(--s3); }
.app-body .avatar-edit .avatar { width: 56px; height: 56px; font-size: 1.2rem; }
.app-body .acct-hero { text-align: center; padding: var(--s4) 0 var(--s5); }
.app-body .acct-hero .avatar { width: 84px; height: 84px; font-size: 2rem; margin: 0 auto var(--s3); }
.app-body .acct-hero__name { font-size: 1.25rem; font-weight: 700; color: var(--text); }
.app-body .acct-hero__handle { font-size: .9rem; color: var(--muted); margin-top: 2px; }

.app-body .set-group {
  background: var(--surface); border: 1px solid var(--divider); border-radius: var(--r-md);
  padding: 4px 16px; margin-bottom: 16px;
}
.app-body .set-label {
  font-size: 13px; font-weight: 600; color: var(--accent-text);
  text-transform: uppercase; letter-spacing: .04em; margin: 20px 4px 6px;
}
.app-body .set-row {
  display: flex; align-items: center; gap: 14px; width: 100%;
  min-height: 58px; padding: 8px 0; position: relative;
  background: none; border: 0; color: var(--text); text-align: left;
  font: inherit; cursor: pointer; text-decoration: none;
}
.app-body .set-row::after { /* inset divider, starts after the icon tile */
  content: ""; position: absolute; left: 46px; right: 0; bottom: 0; height: 1px; background: var(--divider);
}
.app-body .set-row:last-child::after { display: none; }
.app-body .set-row:hover { text-decoration: none; }
.app-body .set-row:hover .set-row__title { color: var(--text); }
.app-body .set-row__tile {
  width: 32px; height: 32px; border-radius: 8px; flex-shrink: 0;
  background: var(--tile, var(--accent)); color: #fff; display: grid; place-items: center;
}
.app-body .set-row__tile svg { width: 18px; height: 18px; }
.app-body .set-row__main { flex: 1; min-width: 0; }
.app-body .set-row__title { font-size: 16px; font-weight: 500; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.app-body .set-row__sub { font-size: 13px; color: var(--muted); margin-top: 1px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.app-body .set-row__value { color: var(--muted); font-size: 14px; flex-shrink: 0; }
.app-body .set-row__chev { color: var(--muted); flex-shrink: 0; font-size: 1.15rem; line-height: 1; opacity: .7; }
.app-body .set-row--danger .set-row__title { color: #F87171; }
.app-body[data-theme="light"] .set-row--danger .set-row__title { color: #B91C1C; }

/* Drill-in panel header (back to the list) */
.app-body .set-back {
  display: inline-flex; align-items: center; gap: 6px; min-height: 44px;
  background: none; border: 0; color: var(--accent-text); font: inherit; font-weight: 600;
  cursor: pointer; padding: 0 4px; margin-bottom: 8px;
}

/* ---------- Premium page ---------- */
.app-body .prem-hero { text-align: center; padding: var(--s5) 0; }
.app-body .prem-hero__emblem {
  width: 96px; height: 96px; margin: 0 auto var(--s4); border-radius: 28px;
  background: linear-gradient(135deg, var(--accent), #22D3EE);
  display: grid; place-items: center; box-shadow: var(--shadow-fab);
}
.app-body .prem-hero h1 { font-size: 1.5rem; margin-bottom: var(--s2); }
.app-body .prem-hero p { color: var(--muted); max-width: 40ch; margin: 0 auto; }
.app-body .plan-row { min-height: 64px; }
.app-body .plan-row__radio {
  width: 22px; height: 22px; border-radius: 50%; border: 2px solid var(--border-strong);
  flex-shrink: 0; display: grid; place-items: center; transition: border-color var(--motion);
}
.app-body .plan-row[aria-checked="true"] .plan-row__radio { border-color: var(--accent); }
.app-body .plan-row[aria-checked="true"] .plan-row__radio::before {
  content: ""; width: 12px; height: 12px; border-radius: 50%; background: var(--accent);
}
.app-body .plan-row__save {
  background: var(--accent); color: #fff; font-size: 11px; font-weight: 700;
  border-radius: var(--r-pill); padding: 2px 8px; margin-left: 8px; vertical-align: 2px;
}
.app-body .plan-row__strike { text-decoration: line-through; opacity: .6; margin-right: 6px; }
.app-body .prem-cta-space { height: 96px; } /* keeps content clear of the sticky CTA */
.app-body .prem-cta {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 50;
  padding: 12px 16px calc(12px + env(safe-area-inset-bottom, 0px));
  background: linear-gradient(to top, var(--bg) 65%, transparent);
}
.app-body .prem-cta .btn {
  width: 100%; max-width: 560px; margin: 0 auto; display: flex;
  background: linear-gradient(90deg, var(--accent), #22D3EE); color: #fff; border: 0;
}

/* ---------- Motion discipline ---------- */
@media (prefers-reduced-motion: reduce) {
  .app-body *, .app-body *::before, .app-body *::after {
    transition-duration: .01ms !important; animation-duration: .01ms !important; animation-iteration-count: 1 !important;
  }
}

/* ---------- Bottom tab bar (mobile shell) ----------
   Markup: includes/partials/bottom-nav.php (single source). Present only on
   user-facing authenticated pages (body carries .has-tabbar); admin pages
   keep their hamburger drawer — it IS the admin sub-nav.
   z-index 30: below the FAB (40), drawer (39), sticky topbar (40),
   .prem-cta (50), toasts (200) and the native <dialog> top layer.
   NOTE: these rules must stay AFTER the 860px FAB/clearance block above —
   the 768px overrides win by source order at equal specificity. */
.app-body .tabbar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 30;
  height: var(--tabbar-total);
  padding-bottom: env(safe-area-inset-bottom, 0px);
  display: none;
  background: var(--surface); border-top: 1px solid var(--divider);
}
.app-body .tabbar__tab {
  position: relative; flex: 1 1 0; min-width: 48px; min-height: 48px;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 2px; color: var(--muted); font-size: 11px; font-weight: 600; text-decoration: none;
}
.app-body .tabbar__tab:hover { color: var(--text); text-decoration: none; }
.app-body .tabbar__tab:focus-visible { outline: 2px solid var(--accent-text); outline-offset: -2px; border-radius: 10px; }
.app-body .tabbar__tab svg { width: 24px; height: 24px; flex-shrink: 0; }
/* 5 tabs (Incidents added 2026-08-08): keep labels on ONE line and let them shrink
   rather than wrap-and-clip on the narrowest phones. At 320px each 1/5 tab is ~64px;
   the longest labels ("Incidents"/"Reminders") fit at 11px, and this caps them so a
   future longer label degrades to an ellipsis instead of breaking the row height. */
.app-body .tabbar__label {
  max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Active = accent color AND a 3px top-edge pill — never color alone. */
.app-body .tabbar__tab.is-active { color: var(--accent-text); }
.app-body .tabbar__tab.is-active::before {
  content: ""; position: absolute; top: 0; left: 50%; transform: translateX(-50%);
  width: 32px; height: 3px; border-radius: 0 0 3px 3px; background: var(--accent-text);
}

/* Mobile-shell breakpoint: 768px — the SAME point where the topbar links
   collapse (style.css), so exactly one nav is visible at every width.
   The FAB's own 860px breakpoint is deliberately untouched: in the
   769-860px band there is a FAB but no tab bar, and .conv-list keeps its
   FAB-only 96px clearance from the 860px block above. */
@media (max-width: 768px) {
  .app-body.has-tabbar .tabbar { display: flex; }
  /* The tab bar replaces the hamburger drawer on these pages. */
  .app-body.has-tabbar .app-nav-toggle,
  .app-body.has-tabbar .app-topbar__nav { display: none; }
  /* Global reserve so no page's content hides behind the bar. The chat
     shell (.chat-page) is a locked 100dvh viewport and is excluded: its
     clearance lives in .conv-list below, because body padding would float
     the composer above the bottom while the bar is hidden in thread view.
     ⚠️ bar height + a GUTTER (not just the height): reserving exactly
     --tabbar-total left the last line flush against / tucked under the bar
     (found 2026-08-08, /incidents last card clipped). The +16px gutter gives
     the final content visible breathing room above the bar. */
  .app-body.has-tabbar:not(.chat-page) { padding-bottom: calc(var(--tabbar-total) + 16px); }
  /* Thread open = back arrow + composer take over (same sibling pattern as
     the FAB). With JS off a thread can never open, so the bar stays. */
  .app-body .chat.show-thread ~ .tabbar { display: none; }
  /* FAB clears the bar: bar + safe-area + 16px gutter. */
  .app-body .fab { bottom: calc(var(--tabbar-total) + 16px); }
  /* Last conversation row scrolls clear of bar + FAB:
     bar + safe-area + 56px FAB + 16px gutter. */
  .app-body .conv-list { padding-bottom: calc(var(--tabbar-total) + 72px); }
  /* premium.php sticky CTA rides ABOVE the bar. The bar consumes the
     safe-area inset, so the CTA drops back to a plain 12px bottom pad.
     In-flow reserve = body padding (--tabbar-total) + this spacer
     (--prem-cta-h) = exactly nav height + CTA height + safe-area inset. */
  .app-body .prem-cta { bottom: var(--tabbar-total); padding-bottom: 12px; }
  .app-body .prem-cta-space { height: var(--prem-cta-h); }
}

/* ---------- Sidebar tabs (Chats | Contacts) ---------- */
.app-body .side-tabs { display: flex; gap: 4px; padding: 0 12px 8px; }
.app-body .side-tab {
  flex: 1; min-height: 36px; border: 0; border-radius: var(--r-pill); background: transparent;
  color: var(--muted); font: inherit; font-size: .9rem; font-weight: 600; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
}
.app-body .side-tab:hover { color: var(--text); background: var(--surface-2); }
.app-body .side-tab.is-active { background: var(--accent-soft); color: var(--accent-text); }
.app-body .side-tab__badge {
  min-width: 18px; height: 18px; padding: 0 5px; border-radius: 9px; background: var(--accent);
  color: var(--accent-contrast); font-size: 11px; line-height: 18px; text-align: center;
}
.app-body .side-tab__badge[hidden] { display: none; }

/* ---------- Contacts panel ---------- */
.app-body .contacts-panel { flex: 1; min-height: 0; overflow-y: auto; padding: 0 12px 16px; }
.app-body .contacts-panel[hidden] { display: none; }
.app-body .contacts-add { display: flex; gap: 8px; align-items: center; padding: 4px 0 8px; }
.app-body .contacts-add .btn { margin-top: 0; height: 38px; }
.app-body .contacts-add input {
  flex: 1; min-width: 0; height: 38px; min-height: 0; padding: 0 12px; border-radius: var(--r-pill);
  border: 1px solid transparent; background: var(--surface-2); color: var(--text); font: inherit;
}
.app-body .contacts-add input:focus { outline: none; border-color: var(--accent); background: var(--bg); }
.app-body .contacts-add__error { display: block; min-height: 1em; margin-bottom: 4px; }
.app-body .contacts-h { margin: 12px 4px 6px; font-size: .75rem; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; color: var(--muted); }
.app-body .contact-row { display: flex; align-items: center; gap: 10px; padding: 8px 4px; border-bottom: 1px solid var(--divider); }
.app-body .contact-row:last-child { border-bottom: 0; }
.app-body .contact-row .avatar { width: 40px; height: 40px; }
.app-body .contact-row__body { flex: 1; min-width: 0; }
.app-body .contact-row__name { color: var(--text); font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.app-body .contact-row__user { color: var(--muted); font-size: .8rem; }
.app-body .contact-row__actions { display: flex; gap: 6px; flex-shrink: 0; }
.app-body .contact-row__actions .btn { margin-top: 0; }
@media (max-width: 768px) {
  .app-body .contacts-panel { padding-bottom: calc(var(--tabbar-total) + 72px); }
}

/* ---------- Upload progress row (above the composer) ---------- */
.app-body .upload-row {
  position: relative; display: grid; grid-template-columns: 1fr auto auto; align-items: center;
  column-gap: 10px; padding: 8px 12px 12px; background: var(--surface); border-top: 1px solid var(--divider);
  font-size: .85rem; color: var(--text); flex: 0 0 auto;
}
.app-body .upload-row[hidden] { display: none; }
.app-body .upload-row__name { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.app-body .upload-row__pct { color: var(--muted); font-variant-numeric: tabular-nums; }
.app-body .upload-row__cancel { background: none; border: 0; color: var(--muted); font-size: 1.2rem; line-height: 1; padding: 0 4px; cursor: pointer; }
.app-body .upload-row__cancel:hover { color: var(--danger, #ef4444); }
.app-body .upload-row__bar { grid-column: 1 / -1; height: 4px; margin-top: 6px; border-radius: 2px; background: var(--surface-3); overflow: hidden; }
.app-body .upload-row__fill { height: 100%; width: 0; background: var(--accent); transition: width .15s linear; }

/* ---------- Per-message delete (own messages) ---------- */
.app-body .msg { position: relative; }
.app-body .msg__del {
  order: 1; align-self: center; margin-right: 6px; width: 32px; /* row-reverse bubble: order 1 = leftmost, outside the bubble */ height: 32px; border: 0; border-radius: 50%;
  background: transparent; color: var(--muted); font-size: .9rem; cursor: pointer; opacity: 0; transition: opacity var(--motion);
  display: grid; place-items: center; flex-shrink: 0;
}
.app-body .msg:hover .msg__del, .app-body .msg__del:focus-visible { opacity: 1; }
.app-body .msg__del:hover { color: var(--danger, #ef4444); background: var(--surface-2); }
@media (hover: none) { .app-body .msg__del { opacity: .55; } } /* touch: no hover, keep it visible */

/* ---------- Subscribed channels (dashboard/qr) ---------- */
.app-body .qr-card--sub { color: var(--text); }
.app-body .qr-card--sub h3 a { color: var(--text); text-decoration: none; }
.app-body .qr-sub-mark { width: 64px; height: 64px; border-radius: 14px; display: grid; place-items: center; color: #fff; font-size: 26px; font-weight: 700; }
.app-body .qr-card--sub .qr-card__stats { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-top: 8px; }
.app-body .qr-card--sub .qr-card__stats .btn { margin-top: 0; }
.app-body .readonly-row { flex: 0 0 auto; padding: 14px 16px calc(14px + env(safe-area-inset-bottom, 0px)); text-align: center; color: var(--muted); font-size: .9rem; background: var(--surface); border-top: 1px solid var(--divider); }
.app-body .readonly-row[hidden], .app-body .composer[hidden] { display: none; }

/* ---------- Thread header: Telegram-style pills ---------- */
.app-body .chat__header {
  height: auto; min-height: 64px; padding: 8px 10px; gap: 8px;
  background: transparent; border-bottom: 0;
}
.app-body .chat__back {
  display: grid; place-items: center; width: 44px; height: 44px; border-radius: 50%;
  background: var(--surface); border: 1px solid var(--divider); color: var(--text); font-size: 1.5rem; line-height: 1; padding: 0 0 3px;
}
.app-body .chat__header-pill {
  flex: 1 1 auto; min-width: 0; display: flex; align-items: center; gap: 10px;
  background: var(--surface); border: 1px solid var(--divider); border-radius: 28px; padding: 6px 14px 6px 6px; height: 52px;
}
.app-body .chat__header-pill .avatar { width: 40px; height: 40px; }
.app-body .chat__header-name { font-size: 17px; font-weight: 700; }
.app-body .chat__header-meta { font-size: 13px; }
.app-body .chat__delete {
  margin-left: 0; width: 44px; height: 44px; min-width: 44px; min-height: 44px; border-radius: 50%;
  background: var(--surface); border: 1px solid var(--divider); color: var(--muted);
}
.app-body .chat__delete:hover { color: var(--danger, #ef4444); }
@media (min-width: 769px) { .app-body .chat__back { display: none; } }

/* Doodle wallpaper behind the messages (inline SVG, tiles, no external request). */
.app-body .chat__messages {
  background-color: var(--bg-dark);
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='220' height='220' viewBox='0 0 220 220' fill='none' stroke='%23ffffff' stroke-opacity='0.045' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'><circle cx='30' cy='30' r='9'/><path d='M60 20l3 7 7 1-5 5 1 7-6-3-6 3 1-7-5-5 7-1z'/><path d='M120 30c0-6 10-6 10 0 0 5-10 10-10 10s-10-5-10-10c0-6 10-6 10 0z'/><path d='M170 22h28v20h-28zM170 22l14 11 14-11'/><path d='M40 90c8-10 20-10 28 0'/><circle cx='100' cy='95' r='12'/><path d='M96 92l2 2 5-5'/><path d='M150 80l6 18M150 98l6-18M144 89h18'/><path d='M190 90c0 10 8 18 18 18'/><path d='M22 150l16-16M22 134l16 16'/><path d='M70 150a12 12 0 1 0 24 0 12 12 0 1 0-24 0'/><path d='M120 160l10-20 10 20z'/><path d='M165 145h30M165 155h22M165 165h26'/><path d='M40 200c10-8 20-8 30 0'/><path d='M100 190v20M90 200h20'/><path d='M150 195l4 8 8 1-6 6 2 8-8-4-8 4 2-8-6-6 8-1z'/><path d='M200 190c6 0 10 4 10 10s-4 10-10 10'/></svg>");
  background-size: 220px 220px;
}
.app-body[data-theme="light"] .chat__messages {
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='220' height='220' viewBox='0 0 220 220' fill='none' stroke='%230f172a' stroke-opacity='0.08' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'><circle cx='30' cy='30' r='9'/><path d='M60 20l3 7 7 1-5 5 1 7-6-3-6 3 1-7-5-5 7-1z'/><path d='M120 30c0-6 10-6 10 0 0 5-10 10-10 10s-10-5-10-10c0-6 10-6 10 0z'/><path d='M170 22h28v20h-28zM170 22l14 11 14-11'/><path d='M40 90c8-10 20-10 28 0'/><circle cx='100' cy='95' r='12'/><path d='M96 92l2 2 5-5'/><path d='M150 80l6 18M150 98l6-18M144 89h18'/><path d='M190 90c0 10 8 18 18 18'/><path d='M22 150l16-16M22 134l16 16'/><path d='M70 150a12 12 0 1 0 24 0 12 12 0 1 0-24 0'/><path d='M120 160l10-20 10 20z'/><path d='M165 145h30M165 155h22M165 165h26'/><path d='M40 200c10-8 20-8 30 0'/><path d='M100 190v20M90 200h20'/><path d='M150 195l4 8 8 1-6 6 2 8-8-4-8 4 2-8-6-6 8-1z'/><path d='M200 190c6 0 10 4 10 10s-4 10-10 10'/></svg>");
}

/* Bubbles: bigger radius, time tucked bottom-right inside, comfortable text size. */
.app-body .msg__bubble { border-radius: 18px; border-bottom-left-radius: 6px; padding: 8px 12px 6px; box-shadow: 0 1px 2px rgba(0,0,0,.25); }
.app-body .msg--mine .msg__bubble { border-radius: 18px; border-bottom-right-radius: 6px; }
.app-body .msg__body { font-size: 16px; line-height: 1.35; word-break: break-word; }
.app-body .msg__meta { font-size: 11px; opacity: .75; margin-top: 2px; }
.app-body .msg--mine .msg__meta { color: rgba(255,255,255,.8); }
.app-body .msg__sender { font-size: 13px; font-weight: 600; margin-bottom: 2px; }
.app-body .msg--mine .msg__bubble::before, .app-body .msg__bubble::before { display: none; } /* no tails: cleaner, Telegram-like */
.app-body .typing-row { padding: 0 16px 4px; }
.app-body .readonly-row { background: transparent; border-top: 0; }

/* ---------- Channel feed (Telegram-style posts) ---------- */
/* Posts are broadcast content: ONE left edge, neutral (incoming) bubble even for the
   owner, wide but not edge-to-edge, time + ticks bottom-right. */
.app-body .feed-msg { max-width: 88%; }
.app-body .feed-msg .msg__bubble,
.app-body .msg--mine.feed-msg .msg__bubble { width: auto; background: var(--bubble-in); border-radius: 18px; border-bottom-left-radius: 6px; }
.app-body .msg--mine.feed-msg .msg__body, .app-body .msg--mine.feed-msg .msg__title { color: var(--text); }
.app-body .msg--mine.feed-msg .msg__meta { color: var(--muted); }
.app-body .feed-msg .msg__del { order: 1; margin: 0 0 0 4px; }
.app-body .mute-bar {
  display: block; width: 100%; height: 48px; border: 0; border-radius: 24px;
  background: var(--surface); color: var(--accent-text); font: inherit; font-weight: 700; font-size: 15px;
  cursor: pointer; box-shadow: 0 2px 10px rgba(0,0,0,.25);
}
.app-body .mute-bar:hover { background: var(--surface-2); }
.app-body .readonly-row { padding: 8px 10px calc(10px + env(safe-area-inset-bottom, 0px)); }
