/* ═══════════════════════════════════════════════════════════════════════════
   Leadly PWA — "Sunset" theme
   Visually distinct from any major messaging app. Warm, alive, brand-owned.
   Primary aubergine + coral accent. RTL. Mobile-first.
   ═══════════════════════════════════════════════════════════════════════════ */

* { box-sizing: border-box; margin: 0; padding: 0; }
/* 100dvh = dynamic viewport height. Adjusts as Chrome's URL bar slides
   away on scroll, so the chat fills the actual visible viewport — not
   100vh which leaves a strip cut off below the URL bar. */
html, body { height: 100dvh; overflow: hidden; }
html { -webkit-text-size-adjust: 100%; }
body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Tahoma, "Helvetica Neue", Arial, sans-serif;
  font-size: 15px;
  color: var(--text);
  background: var(--app-bg);
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent;
  overscroll-behavior: none;
  /* Safe-area padding is applied directly by .header (top) and the
     composer area (bottom) via @supports(env(...)) below. Applying it
     on body too made body's content box smaller than 100dvh, while
     #root was sized to 100dvh — the overflow was then clipped by
     body's overflow:hidden, silently chopping the bottom of the
     layout on devices that report non-zero safe-area-insets. */
}
button { font-family: inherit; cursor: pointer; border: none; background: none; color: inherit; }
input, textarea { font-family: inherit; }

/* ─── LEADLY DASHBOARD PALETTE — flipped bubbles ───────────────────────────
   Matches the operator dashboard's identity:
     Navy 900        #0F172A   header (deepest)
     Navy 800        #1E293B   chat background, header gradient end
     Navy 700        #334155   card borders, dividers
     Emerald 500     #10B981   primary brand — Yasmin (incoming) bubbles, send button
     Emerald 400     #22C55E   gradient pair / hover accent
     White           #FFFFFF   patient (outgoing) bubbles
     Slate 50        #F8FAFC   incoming bubble text
     Slate 200       #E2E8F0   subtle text
     Slate 400       #94A3B8   muted text, ticks

   Bubbles flipped vs the original layout:
     - Yasmin (incoming)  = emerald  (the brand-colored side)
     - Patient (outgoing) = white    (the "your cards on dark bg" feel)
   ──────────────────────────────────────────────────────────────────────── */
:root {
  /* Background — deep navy, matching dashboard body */
  --app-bg: #1E293B;
  --chat-bg: #1E293B;
  /* Pattern: subtle emerald + white micro-dots on the navy */
  --chat-pattern: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='60' height='60' viewBox='0 0 60 60'%3E%3Ccircle cx='30' cy='30' r='1' fill='%2310B981' opacity='0.08'/%3E%3Ccircle cx='10' cy='50' r='0.8' fill='%23ffffff' opacity='0.04'/%3E%3Ccircle cx='50' cy='10' r='0.8' fill='%2322C55E' opacity='0.06'/%3E%3C/svg%3E");

  /* Header — deepest navy gradient */
  --header-bg: linear-gradient(135deg, #0F172A 0%, #1E293B 100%);
  --header-bg-solid: #0F172A;
  --header-text: #ffffff;
  --header-icon: rgba(255,255,255,0.92);

  /* INCOMING (Yasmin) — emerald green, the brand-colored side */
  --bubble-in: linear-gradient(135deg, #10B981 0%, #22C55E 100%);
  --bubble-in-solid: #10B981;
  --bubble-in-text: #ffffff;
  --bubble-in-border: rgba(16, 185, 129, 0.25);

  /* OUTGOING (patient) — white cards, like the dashboard cards on dark bg */
  --bubble-out: linear-gradient(135deg, #ffffff 0%, #F8FAFC 100%);
  --bubble-out-solid: #ffffff;
  --bubble-out-text: #0F172A;
  --bubble-out-border: rgba(255, 255, 255, 0.4);

  /* Layered shadows — emerald glow on the incoming/Yasmin side now */
  --bubble-shadow:
    0 1px 2px rgba(16, 185, 129, 0.18),
    0 4px 12px rgba(16, 185, 129, 0.12),
    0 8px 24px rgba(16, 185, 129, 0.08);
  --bubble-shadow-out:
    0 1px 2px rgba(0, 0, 0, 0.18),
    0 4px 12px rgba(0, 0, 0, 0.14),
    0 8px 24px rgba(0, 0, 0, 0.08);

  /* Typography — white on navy by default */
  --text: #F8FAFC;
  --text-muted: #CBD5E1;
  --text-light: #94A3B8;

  /* Composer — darker navy card on the navy bg */
  --input-bg: #334155;
  --input-border: rgba(148, 163, 184, 0.18);
  --input-shadow:
    0 2px 8px rgba(0, 0, 0, 0.18),
    inset 0 0 0 1px rgba(148, 163, 184, 0.12);

  /* Send button — emerald, matching the dashboard's "+ حجز جديد" button */
  --send-btn: linear-gradient(135deg, #10B981 0%, #22C55E 100%);
  --send-btn-solid: #10B981;
  --send-btn-glow: 0 4px 16px rgba(16, 185, 129, 0.4);

  /* Read-receipt — emerald to match brand */
  --tick: #94A3B8;
  --tick-read: #10B981;

  --divider: rgba(148, 163, 184, 0.12);
  --backdrop: rgba(15, 23, 42, 0.7);
  --modal-bg: #1E293B;
  --link: #34D399;
}

@media (prefers-color-scheme: dark) {
  :root {
    /* Same palette — the dashboard scheme IS dark-mode by default */
    --app-bg: #0F172A;
    --chat-bg: #0F172A;
    --chat-pattern: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='60' height='60' viewBox='0 0 60 60'%3E%3Ccircle cx='30' cy='30' r='1' fill='%2310B981' opacity='0.1'/%3E%3Ccircle cx='10' cy='50' r='0.8' fill='%23ffffff' opacity='0.05'/%3E%3Ccircle cx='50' cy='10' r='0.8' fill='%2322C55E' opacity='0.08'/%3E%3C/svg%3E");

    --header-bg: linear-gradient(135deg, #020617 0%, #0F172A 100%);
    --header-bg-solid: #020617;

    --bubble-in: linear-gradient(135deg, #059669 0%, #16A34A 100%);
    --bubble-in-solid: #059669;
    --bubble-in-text: #ffffff;
    --bubble-in-border: rgba(16, 185, 129, 0.3);

    --bubble-out: linear-gradient(135deg, #F1F5F9 0%, #E2E8F0 100%);
    --bubble-out-solid: #F1F5F9;
    --bubble-out-text: #0F172A;
    --bubble-out-border: rgba(255, 255, 255, 0.3);

    --bubble-shadow:
      0 1px 2px rgba(0, 0, 0, 0.4),
      0 4px 12px rgba(0, 0, 0, 0.25),
      0 8px 24px rgba(16, 185, 129, 0.12);
    --bubble-shadow-out:
      0 1px 2px rgba(0, 0, 0, 0.4),
      0 4px 12px rgba(0, 0, 0, 0.25),
      0 8px 24px rgba(0, 0, 0, 0.15);

    --text: #F8FAFC;
    --text-muted: #CBD5E1;
    --text-light: #64748B;

    --input-bg: #1E293B;
    --input-border: rgba(148, 163, 184, 0.15);
    --input-shadow:
      0 2px 8px rgba(0, 0, 0, 0.3),
      inset 0 0 0 1px rgba(148, 163, 184, 0.1);

    --send-btn: linear-gradient(135deg, #10B981 0%, #22C55E 100%);
    --send-btn-solid: #10B981;
    --send-btn-glow: 0 4px 16px rgba(16, 185, 129, 0.45);

    --divider: rgba(148, 163, 184, 0.1);
    --modal-bg: #1E293B;
    --link: #34D399;
  }
}
[data-theme="light"] {
  /* "Light" mode reverts to white card-bg with green incoming + dark outgoing.
     Kept available but the dashboard's actual scheme is dark — most users
     should see the default. */
  --app-bg: #F8FAFC;
  --chat-bg: #F8FAFC;
  --bubble-in: linear-gradient(135deg, #10B981 0%, #22C55E 100%);
  --bubble-in-solid: #10B981;
  --bubble-in-text: #ffffff;
  --bubble-out: linear-gradient(135deg, #ffffff 0%, #F1F5F9 100%);
  --bubble-out-solid: #ffffff;
  --bubble-out-text: #0F172A;
  --text: #0F172A;
  --text-muted: #475569;
  --input-bg: #ffffff;
  --modal-bg: #ffffff;
}
[data-theme="dark"] {
  --app-bg: #0F172A;
  --chat-bg: #0F172A;
  --header-bg: linear-gradient(135deg, #020617 0%, #0F172A 100%);
  --header-bg-solid: #020617;
  --bubble-in: linear-gradient(135deg, #059669 0%, #16A34A 100%);
  --bubble-in-solid: #059669;
  --bubble-in-text: #ffffff;
  --bubble-out: linear-gradient(135deg, #F1F5F9 0%, #E2E8F0 100%);
  --bubble-out-solid: #F1F5F9;
  --bubble-out-text: #0F172A;
  --text: #F8FAFC;
  --text-muted: #CBD5E1;
  --text-light: #64748B;
  --input-bg: #1E293B;
  --modal-bg: #1E293B;
}

/* ─── BOOT LOADER (visible until React mounts) ─────────────────────────── */
.boot-loader {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  height: 100vh; background: var(--app-bg); gap: 16px;
}
.boot-spinner {
  width: 36px; height: 36px; border-radius: 50%;
  border: 3px solid rgba(16, 185, 129, 0.15);
  border-top-color: #10B981;
  animation: spin 0.8s linear infinite;
}
.boot-text { color: var(--text-muted); font-size: 14px; }
@keyframes spin { to { transform: rotate(360deg); } }

/* ─── ROOT APP CONTAINER ──────────────────────────────────────────────── */
#root {
  height: 100vh; height: 100dvh;
  display: flex; flex-direction: column;
}
.app {
  height: 100%;
  display: flex; flex-direction: column;
  background: var(--chat-bg);
  background-image: var(--chat-pattern);
  position: relative;
  overflow: hidden;
}

/* ─── AMBIENT FLOATING ORBS ────────────────────────────────────────────────
   Two soft blurred gradient blobs that drift slowly behind the chat. Gives
   the interface a "living" quality without animating any actual UI element.
   ::before is the coral orb, ::after is the aubergine orb. They move on
   different cycles so the composition never repeats exactly.
   ──────────────────────────────────────────────────────────────────────── */
.app::before {
  content: "";
  position: absolute;
  top: -10%;
  right: -15%;
  width: 50vw;
  height: 50vw;
  max-width: 400px;
  max-height: 400px;
  background: radial-gradient(circle at center,
    rgba(16, 185, 129, 0.22) 0%,
    rgba(34, 197, 94, 0.10) 40%,
    transparent 70%);
  filter: blur(40px);
  pointer-events: none;
  z-index: 0;
  animation: orbDriftA 22s ease-in-out infinite;
}
.app::after {
  content: "";
  position: absolute;
  bottom: 15%;
  left: -10%;
  width: 45vw;
  height: 45vw;
  max-width: 380px;
  max-height: 380px;
  background: radial-gradient(circle at center,
    rgba(30, 41, 59, 0.18) 0%,
    rgba(15, 23, 42, 0.08) 40%,
    transparent 70%);
  filter: blur(45px);
  pointer-events: none;
  z-index: 0;
  animation: orbDriftB 28s ease-in-out infinite;
}
@keyframes orbDriftA {
  0%, 100% { transform: translate(0, 0) scale(1); }
  33%      { transform: translate(-8%, 12%) scale(1.12); }
  66%      { transform: translate(5%, 6%) scale(0.92); }
}
@keyframes orbDriftB {
  0%, 100% { transform: translate(0, 0) scale(1); }
  33%      { transform: translate(10%, -8%) scale(0.95); }
  66%      { transform: translate(-6%, 10%) scale(1.1); }
}
@media (prefers-color-scheme: dark) {
  .app::before {
    background: radial-gradient(circle at center,
      rgba(16, 185, 129, 0.14) 0%,
      rgba(34, 197, 94, 0.06) 40%,
      transparent 70%);
  }
  .app::after {
    background: radial-gradient(circle at center,
      rgba(30, 41, 59, 0.25) 0%,
      rgba(15, 23, 42, 0.12) 40%,
      transparent 70%);
  }
}
@media (prefers-reduced-motion: reduce) {
  .app::before, .app::after { animation: none; }
}
/* The chat content needs to stay above the orbs */
.header, .messages, .composer-area, .typing-row { position: relative; z-index: 1; }

/* ─── HEADER ──────────────────────────────────────────────────────────── */
.header {
  flex-shrink: 0;
  background: var(--header-bg);
  color: var(--header-text);
  padding: 14px 16px;
  display: flex; align-items: center; gap: 12px;
  box-shadow:
    0 1px 3px rgba(15, 23, 42, 0.2),
    0 4px 16px rgba(15, 23, 42, 0.08);
  /* NO position: sticky here. The header is the first flex-shrink:0 child of
     the fixed-height .app flex column, so it is ALREADY pinned to the top by
     the flex layout — sticky added nothing. Worse: once the notifications
     ribbon was added as a second flex-shrink:0 child, `position: sticky`
     made the header detach from the flex flow when the mobile keyboard
     resized the viewport, and the browser scrolled the header+ribbon off
     the top of the screen on input focus. Plain flex layout keeps header
     and ribbon fixed at the top with no scroll. (Fixed May 23, 2026.)
     z-index kept so the header still layers above the ambient orbs. */
  z-index: 10;
  /* Backdrop-blur gives the "frosted glass app header" feel — content
     scrolling under shows through subtly, like iOS Mail / Telegram /
     iMessage. Falls back gracefully on browsers without support. */
  backdrop-filter: blur(20px) saturate(180%);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
}
.header-avatar {
  width: 44px; height: 44px; border-radius: 50%;
  background: linear-gradient(135deg, #10B981 0%, #22C55E 100%);
  display: flex; align-items: center; justify-content: center;
  font-weight: 800; font-size: 19px;
  font-family: 'Geist', -apple-system, sans-serif;
  color: #ffffff;
  flex-shrink: 0;
  overflow: hidden;
  box-shadow:
    0 0 0 2px rgba(16, 185, 129, 0.25),
    0 2px 8px rgba(16, 185, 129, 0.35),
    inset 0 1px 0 rgba(255, 255, 255, 0.2);
  letter-spacing: 0;
}
.header-avatar img { width: 100%; height: 100%; object-fit: cover; }
.header-info { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.header-name {
  font-weight: 700; font-size: 16px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  letter-spacing: 0.01em;
  line-height: 1.2;
}
.header-status {
  font-size: 12px;
  opacity: 0.85;
  display: flex; align-items: center; gap: 8px;
  flex-wrap: nowrap;
  overflow: hidden;
  white-space: nowrap;
}
.status-dot {
  display: inline-block;
  width: 7px; height: 7px;
  border-radius: 50%;
  flex-shrink: 0;
  /* RTL: the dot sits to the LEFT of "clinic name · status" inline flow.
     The animated outer glow extends ~4px past the dot's box, which used
     to clip into the clinic-name text on the start side. The extra
     inline-start margin gives the glow breathing room.  */
  margin-inline-start: 4px;
}
.status-dot.online {
  background: #4ade80;
  box-shadow: 0 0 0 2px rgba(74, 222, 128, 0.25),
              0 0 8px rgba(74, 222, 128, 0.5);
  animation: statusPulse 2.4s ease-in-out infinite;
}
.status-dot.offline {
  background: #94a3b8;
  opacity: 0.6;
}
@keyframes statusPulse {
  0%, 100% { box-shadow: 0 0 0 2px rgba(74, 222, 128, 0.25), 0 0 8px rgba(74, 222, 128, 0.5); }
  50%      { box-shadow: 0 0 0 4px rgba(74, 222, 128, 0.12), 0 0 12px rgba(74, 222, 128, 0.7); }
}
.status-sep { opacity: 0.5; }
.status-label { opacity: 0.85; }
.header-actions { display: flex; gap: 4px; }
.header-btn {
  width: 38px; height: 38px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  color: var(--header-icon);
  font-size: 18px;
  transition: background 0.18s, transform 0.12s;
}
.header-btn:hover { background: rgba(255,255,255,0.14); }
.header-btn:active { background: rgba(255,255,255,0.22); transform: scale(0.94); }

.clinic-switcher {
  cursor: pointer;
  padding: 1px 4px;
  border-radius: 4px;
  transition: background 0.15s;
  font-weight: 500;
}
.clinic-switcher:hover { background: rgba(255,255,255,0.1); }
.clinic-switcher-arrow {
  display: inline-block; transition: transform 0.2s;
  font-size: 10px; opacity: 0.6;
}

/* ─── MESSAGES AREA ─────────────────────────────────────────────────── */
.messages {
  flex: 1;
  /* CRITICAL: min-height: 0 lets this flex item shrink below its content's
     intrinsic size. Without it, flex items default to min-height: auto
     and refuse to shrink below content height — which means a tall chat
     of messages would push the composer (and any reply-strip rendered
     above it as a sibling flex item) below the viewport. With
     min-height: 0, .messages becomes scrollable when content overflows,
     and everything below it always stays in view. May 19, 2026 fix. */
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  /* The last bubble was kissing the composer. The REAL cause (found
     May 22, 2026): bottom padding on .messages does nothing useful —
     .messages is the SCROLL CONTAINER, and the autoscroll sets
     scrollTop = scrollHeight, which scrolls the container's own bottom
     padding out of the viewport. No matter how big this value was
     (12px → 18px → 24px), the last bubble / typing indicator still
     ended up flush against the input bar. The breathing room must live
     on the CONTENT (.messages-inner) instead, so it scrolls WITH the
     content and stays visible. Bottom value here is 0; top + sides
     stay. */
  padding: 12px 6% 0;
  display: flex; flex-direction: column;
  -webkit-overflow-scrolling: touch;
  scroll-behavior: smooth;
  /* Disable pull-down-to-refresh (web-y feel) and stop scroll-chaining
     to the parent. App-style: scrolling the chat doesn't bounce the
     whole page. */
  overscroll-behavior: contain;
}
.messages-inner {
  display: flex; flex-direction: column; gap: 2px;
  /* Bottom-anchor: messages stack from the bottom up, like WhatsApp /
     iMessage. With 1 message, the bubble sits just above the input
     area instead of leaving a huge void between the header and the
     message. As more messages come in, they fill upward. */
  margin-top: auto;
  /* flex-shrink: 0 — NOT min-height: 0.  .messages-inner is a flex child
     of .messages.  With the flex default (flex-shrink: 1), once the chat
     overflows, the flexbox algorithm shrinks THIS box down to the height
     of .messages while the bubbles inside it do not shrink — so the box
     becomes shorter than its own content and its padding-bottom ends up
     sitting in the MIDDLE of the bubble stack, not below it.  The last
     bubble then overflows straight past the padding, flush against the
     composer.  That is the "verification fine, everything after glued"
     bug — it only appears once the chat overflows.  flex-shrink: 0 forces
     .messages-inner to keep its full content height, so padding-bottom
     always sits genuinely below the last bubble.  (May 24, 2026 — the
     real fix; earlier passes only changed the padding VALUE, which could
     never work while the box was being shrunk under its content.)
     min-height: 0 stays on .messages itself — that one is correct and
     unrelated; do not copy it back here. */
  flex-shrink: 0;
  /* Breathing room between the last bubble / typing indicator and the
     composer. MUST be here, not on .messages — see the note above.
     This is part of the scrolled content, so a full scroll-to-bottom
     keeps it visible between the last bubble and the input bar.
     14px — 8px read as glued-to-the-composer; 16px as too loose. */
  padding-bottom: 14px;
}

/* Day divider
   The app's default is dark mode (it activates via @media
   prefers-color-scheme OR [data-theme="dark"]). Both paths need to be
   covered, otherwise the OS-dark-mode case ended up with a light pill
   AND light text — invisible. Pill background and text color now both
   adapt to the active theme. */
.day-divider {
  /* The app's base palette is ALREADY dark (see :root at line ~60). The
     previous attempt at light/dark theme branching produced an invisible
     pill in OS-dark-mode (background became the same colour as the chat
     bg). The app is effectively dark-only, so the pill is unconditional:
     bright opaque white pill, black text. Maximum contrast against the
     navy chat background, no theme media queries to misfire. */
  align-self: center;
  background: #FFFFFF;
  color: #000000;
  font-size: 12px;
  font-weight: 600;
  padding: 5px 14px;
  border-radius: 12px;
  margin: 14px auto 10px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
  border: 1px solid rgba(255, 255, 255, 0.4);
  letter-spacing: 0.02em;
}

/* ─── MESSAGE BUBBLES ─────────────────────────────────────────────────────
   Bubblier than WhatsApp: larger radius (18px), gradient fills, layered
   shadows. Spring-in animation gives every bubble a "pop into life" feel.
   ──────────────────────────────────────────────────────────────────────── */
.msg-row {
  display: flex; max-width: 100%;
  margin-bottom: 6px;
  /* Spring-in animation. The slight overshoot (1.08 → 1.0) is what creates
     the "bouncy, alive" feel — each new message lands with character. */
  animation: bubblePop 0.42s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.msg-row.outgoing { justify-content: flex-end; }
.msg-row.incoming { justify-content: flex-start; }
.msg-row.system { justify-content: center; margin: 10px 0; }

@keyframes bubblePop {
  0%   { transform: scale(0.82) translateY(8px); opacity: 0; }
  60%  { transform: scale(1.04) translateY(0); opacity: 1; }
  100% { transform: scale(1) translateY(0); opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .msg-row { animation: fadeIn 0.2s ease; }
}

.msg-bubble {
  max-width: 75%;
  padding: 10px 14px 12px 14px;
  border-radius: 20px;
  position: relative;
  word-wrap: break-word;
  overflow-wrap: break-word;
  line-height: 1.5;
  font-size: 14.5px;
  /* The "alive" element: very subtle vertical breathe on the gradient */
  background-attachment: scroll;
}

.msg-row.outgoing .msg-bubble {
  background: var(--bubble-out);
  color: var(--bubble-out-text);
  box-shadow: var(--bubble-shadow-out);
  /* Tail on the corner closest to the sender's side */
  border-top-right-radius: 6px;
  border: 1px solid var(--bubble-out-border);
}
.msg-row.incoming .msg-bubble {
  background: var(--bubble-in);
  color: var(--bubble-in-text);
  box-shadow: var(--bubble-shadow);
  border-top-left-radius: 6px;
  border: 1px solid var(--bubble-in-border);
}

/* RTL: tails flip to the inside */
[dir="rtl"] .msg-row.outgoing .msg-bubble {
  border-top-right-radius: 20px;
  border-top-left-radius: 6px;
}
[dir="rtl"] .msg-row.incoming .msg-bubble {
  border-top-left-radius: 20px;
  border-top-right-radius: 6px;
}

.msg-text { white-space: pre-wrap; }
.msg-text a { color: var(--link); text-decoration: none; word-break: break-all; font-weight: 500; }
.msg-text a:hover { text-decoration: underline; }
/* Make outgoing-bubble links readable on coral background */
.msg-row.outgoing .msg-text a { color: #ECFDF5; text-decoration: underline; }

.msg-meta {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  font-size: 10.5px;
  /* Dark base color so timestamps are readable on the white outgoing
     bubble. The incoming-bubble override below uses a different dark
     value that reads correctly on the green gradient. */
  color: rgba(15, 23, 42, 0.7);
  margin-right: 4px;
  margin-left: 4px;
  margin-top: 4px;
  float: left;
  margin-bottom: -4px;
  padding-top: 1px;
  font-weight: 600;
}
/* Incoming (green) bubble: use a dark slate that contrasts the green
   gradient. Pure black would be too harsh next to the white message text;
   ~75% slate is dark enough to be readable, soft enough not to fight the
   message body for attention. */
.msg-row.incoming .msg-meta { color: rgba(15, 23, 42, 0.85); }
[dir="rtl"] .msg-meta { float: left; }

.msg-time { font-size: 10.5px; }
.msg-ticks {
  display: inline-flex; align-items: center;
  margin-right: 2px; margin-left: 2px;
}
.msg-ticks svg { width: 16px; height: 11px; }
/* Outgoing bubble in this PWA is WHITE/light (--bubble-out is #F1F5F9 →
   #E2E8F0), NOT green like classic WhatsApp. That means ticks rendered
   in white were invisible on the white bubble. Stroke a neutral slate
   so the single/double tick reads cleanly, and use the brand emerald
   for the read state. */
.msg-ticks svg path { stroke: #64748B; }              /* slate-500 — visible on white bubble */
.msg-ticks.read svg path { stroke: #53BDEB; }         /* WhatsApp blue — classic read indicator */
/* Incoming bubbles never render ticks (the JSX only renders for
   `outgoing`), so the rules below are dead weight, but kept for any
   future system-message reuse. */
.msg-row.incoming .msg-ticks svg path { stroke: var(--tick); }
.msg-row.incoming .msg-ticks.read svg path { stroke: var(--tick-read); }

/* System / informational message */
.msg-row.system .msg-bubble {
  background: rgba(255, 255, 255, 0.7);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  color: var(--text-muted);
  font-size: 12.5px;
  padding: 6px 16px;
  border-radius: 14px;
  max-width: 85%;
  text-align: center;
  box-shadow: 0 2px 8px rgba(15, 23, 42, 0.06);
  border: 1px solid rgba(15, 23, 42, 0.05);
}
[data-theme="dark"] .msg-row.system .msg-bubble {
  background: rgba(15, 23, 42, 0.7);
  border-color: rgba(255, 255, 255, 0.04);
}

/* ─── INLINE VERIFY PROMPT ─────────────────────────────────────────────
   The OTP-verify card used to be a modal overlay. Now it renders inside
   a chat bubble. The widget below is the bordered actionable block that
   sits beneath the message text — same visual language as the install
   prompt but green-accented to feel like a "do this next" step.
*/
.verify-inline {
  margin-top: 10px;
  padding: 14px;
  background: rgba(255, 255, 255, 0.12);
  border: 1px solid rgba(255, 255, 255, 0.22);
  border-radius: 12px;
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}
.verify-inline-header {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 700;
  font-size: 14.5px;
  color: #ffffff;
  margin-bottom: 8px;
}
.verify-inline-icon {
  font-size: 18px;
  line-height: 1;
}
.verify-inline-body {
  font-size: 13.5px;
  line-height: 1.7;
  color: rgba(255, 255, 255, 0.96);
  margin-bottom: 10px;
}
.verify-inline-code-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 8px 12px;
  background: rgba(255, 255, 255, 0.18);
  border-radius: 8px;
  margin-bottom: 10px;
}
.verify-inline-code-label {
  font-size: 12.5px;
  color: rgba(255, 255, 255, 0.85);
}
.verify-inline-code {
  font-family: -apple-system, "Geist Mono", "SF Mono", Menlo, monospace;
  font-size: 18px;
  font-weight: 800;
  letter-spacing: 0.15em;
  color: #ffffff;
  direction: ltr;
}
.verify-inline-instructions {
  font-size: 13px;
  line-height: 1.7;
  color: rgba(255, 255, 255, 0.92);
  margin-bottom: 12px;
}
.verify-inline-instructions strong { color: #ffffff; font-weight: 700; }
.verify-inline-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  padding: 12px 16px;
  background: #ffffff;
  color: #128C7E;
  font-size: 15px;
  font-weight: 700;
  border-radius: 10px;
  text-decoration: none;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.15);
  transition: transform 0.12s;
}
.verify-inline-btn:hover { transform: translateY(-1px); }
.verify-inline-btn:active { transform: translateY(0) scale(0.98); }
.verify-inline-footnote {
  margin-top: 10px;
  text-align: center;
  font-size: 12px;
  color: rgba(255, 255, 255, 0.78);
  line-height: 1.6;
}

/* ─── INSTALL-PROMPT WIDGET ─────────────────────────────────────────────
   Renders inline below the install-prompt message body. Position:relative
   on the wrapper so the absolute-positioned X dismiss can sit in the corner.
*/
.install-prompt-widget {
  position: relative;
  margin-top: 10px;
  padding: 14px 14px 12px 14px;
  background: rgba(255, 255, 255, 0.12);
  border: 1px solid rgba(255, 255, 255, 0.22);
  border-radius: 12px;
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}
.install-prompt-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%;
  padding: 12px 18px;
  background: #ffffff;
  color: #059669;
  font-size: 15px;
  font-weight: 700;
  border: none;
  border-radius: 10px;
  cursor: pointer;
  transition: transform 0.12s, box-shadow 0.2s;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.12);
  font-family: inherit;
}
.install-prompt-btn:hover { transform: translateY(-1px); box-shadow: 0 6px 18px rgba(0, 0, 0, 0.18); }
.install-prompt-btn:active { transform: translateY(0) scale(0.98); }
.install-prompt-icon { font-size: 18px; line-height: 1; }

.install-prompt-ios-steps {
  color: #ffffff;
  font-size: 13.5px;
  line-height: 1.7;
  padding-left: 4px;
}
.install-prompt-ios-title {
  font-weight: 700;
  margin-bottom: 8px;
  color: #ffffff;
}
.install-prompt-ios-list {
  margin: 0;
  padding-right: 18px;
  list-style: arabic-indic;
}
.install-prompt-ios-list li { margin-bottom: 4px; }
.install-prompt-ios-list strong { font-weight: 700; }

.install-prompt-fallback {
  color: rgba(255, 255, 255, 0.92);
  font-size: 13.5px;
  line-height: 1.7;
}

.install-prompt-installed {
  margin-top: 10px;
  padding: 10px 14px;
  background: rgba(255, 255, 255, 0.18);
  border-radius: 10px;
  color: #ffffff;
  font-size: 13.5px;
  font-weight: 600;
  text-align: center;
}

.install-prompt-dismiss {
  position: absolute;
  top: 6px;
  left: 6px;       /* RTL: X sits at the start (top-left of the bubble in LTR terms,
                       which in RTL reads as top-right of the visual layout) */
  width: 28px;
  height: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: rgba(255, 255, 255, 0.18);
  color: #ffffff;
  border: none;
  border-radius: 50%;
  cursor: pointer;
  opacity: 0.85;
  transition: opacity 0.15s, background 0.15s;
}
.install-prompt-dismiss:hover {
  opacity: 1;
  background: rgba(255, 255, 255, 0.28);
}
.install-prompt-dismiss svg { width: 14px; height: 14px; }
[dir="rtl"] .install-prompt-dismiss { left: auto; right: 6px; }

/* Image in bubble */
.msg-image {
  max-width: 100%;
  max-height: 280px;
  border-radius: 6px;
  display: block;
  margin: -2px -3px 4px;
  cursor: pointer;
}

/* Voice message */
.msg-voice {
  display: flex; align-items: center; gap: 10px;
  padding: 4px 0;
  min-width: 180px;
}
.msg-voice-btn {
  width: 32px; height: 32px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: rgba(0,0,0,0.05);
  flex-shrink: 0;
  font-size: 14px;
}
[data-theme="dark"] .msg-voice-btn { background: rgba(255,255,255,0.1); }
.msg-voice-waveform {
  flex: 1;
  height: 24px;
  display: flex; align-items: center; gap: 2px;
}
.msg-voice-waveform div {
  width: 2px;
  background: var(--text-muted);
  border-radius: 1px;
}
.msg-voice-duration {
  font-size: 11px;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

/* Typing indicator */
.typing-row {
  display: flex; justify-content: flex-start;
  margin-bottom: 6px;
  animation: fadeIn 0.25s ease;
}
.typing-bubble {
  background: var(--bubble-in);
  border-radius: 20px;
  border-top-left-radius: 6px;
  padding: 12px 16px;
  display: flex; gap: 5px;
  box-shadow: var(--bubble-shadow);
  border: 1px solid var(--bubble-in-border);
}
[dir="rtl"] .typing-bubble {
  border-top-left-radius: 20px;
  border-top-right-radius: 6px;
}
.typing-dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: linear-gradient(135deg, #10B981 0%, #22C55E 100%);
  animation: typingBounce 1.3s infinite ease-in-out;
  box-shadow: 0 1px 3px rgba(16, 185, 129, 0.3);
}
.typing-dot:nth-child(1) { animation-delay: 0s; }
.typing-dot:nth-child(2) { animation-delay: 0.18s; }
.typing-dot:nth-child(3) { animation-delay: 0.36s; }
@keyframes typingBounce {
  0%, 60%, 100% { transform: translateY(0) scale(0.9); opacity: 0.5; }
  30% { transform: translateY(-7px) scale(1.1); opacity: 1; }
}
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }

/* ─── COMPOSER ────────────────────────────────────────────────────────── */
/* The composer is a defensive flex-row: input wrap on one side, action
   button on the other, always on a single line. The redundant
   `flex-direction: row` and `flex-wrap: nowrap` are stated explicitly so
   no cascade or browser quirk can flip the row into a column or wrap
   the button onto a new line. The action button has an explicit
   `flex: 0 0 48px` so it never grows or shrinks. The input wrap and the
   textarea inside it both get `min-width: 0` so they can shrink below
   their intrinsic content size (the textarea's default `cols=20` gives
   it a min-content width of ~200px otherwise, which on narrow viewports
   can push the action button off the row entirely). */
.composer {
  flex-shrink: 0;
  padding: 10px 12px 14px 12px;
  background: var(--app-bg);
  display: flex;
  flex-direction: row;
  flex-wrap: nowrap;
  align-items: flex-end;
  gap: 10px;
  position: relative;
}
.composer-input-wrap {
  flex: 1 1 auto;
  min-width: 0;
  background: var(--input-bg);
  border-radius: 26px;
  padding: 10px 16px;
  display: flex; align-items: center; gap: 8px;
  min-height: 46px;
  box-shadow: var(--input-shadow);
  transition: box-shadow 0.2s;
}
.composer-input-wrap:focus-within {
  box-shadow:
    0 2px 12px rgba(16, 185, 129, 0.12),
    inset 0 0 0 1.5px rgba(16, 185, 129, 0.35);
}
.composer-input {
  flex: 1 1 auto;
  min-width: 0;
  border: none;
  outline: none;
  background: transparent;
  color: var(--text);
  font-size: 15px;
  resize: none;
  max-height: 120px;
  line-height: 1.4;
  padding: 4px 0;
  font-family: inherit;
}
.composer-input::placeholder { color: var(--text-light); }
.composer-attach, .composer-emoji {
  width: 26px; height: 26px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  color: var(--text-muted);
  font-size: 20px;
  flex-shrink: 0;
  transition: color 0.15s, transform 0.12s;
}
.composer-attach:hover, .composer-emoji:hover {
  color: #10B981;
  transform: scale(1.08);
}

.composer-action {
  flex: 0 0 48px;
  width: 48px; height: 48px; border-radius: 50%;
  background: var(--send-btn);
  color: white;
  display: flex; align-items: center; justify-content: center;
  font-size: 20px;
  box-shadow: var(--send-btn-glow);
  transition: transform 0.12s, box-shadow 0.2s;
  /* Gentle "ready to send" breathing — barely-perceptible scale wobble.
     This is the most direct way to make the UI feel "alive". */
  animation: sendBreathe 3.5s ease-in-out infinite;
}
@keyframes sendBreathe {
  0%, 100% { transform: scale(1); box-shadow: var(--send-btn-glow); }
  50%      { transform: scale(1.04); box-shadow: 0 6px 20px rgba(16, 185, 129, 0.45); }
}
.composer-action:hover {
  transform: scale(1.06);
  box-shadow: 0 6px 24px rgba(16, 185, 129, 0.5);
}
.composer-action:active {
  transform: scale(0.92);
  animation: none;
}
.composer-action:disabled {
  opacity: 0.45;
  animation: none;
  box-shadow: none;
}
@media (prefers-reduced-motion: reduce) {
  .composer-action { animation: none; }
}

/* Recording state */
.composer.recording {
  background: linear-gradient(135deg, #ECFDF5 0%, #D1FAE5 100%);
}
[data-theme="dark"] .composer.recording {
  background: linear-gradient(135deg, #064E3B 0%, #022C22 100%);
}
.recording-indicator {
  flex: 1;
  display: flex; align-items: center; gap: 12px;
  padding: 12px;
  color: var(--text);
}
.recording-pulse {
  width: 12px; height: 12px; border-radius: 50%;
  background: #10B981;
  box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.7);
  animation: recordPulse 1.2s infinite;
}
@keyframes recordPulse {
  0%   { opacity: 1; box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.6); }
  70%  { opacity: 0.6; box-shadow: 0 0 0 12px rgba(16, 185, 129, 0); }
  100% { opacity: 1; box-shadow: 0 0 0 0 rgba(16, 185, 129, 0); }
}
.recording-time { font-variant-numeric: tabular-nums; }
.recording-hint { color: var(--text-muted); font-size: 13px; }

/* ─── ATTACHMENT MENU ────────────────────────────────────────────────── */
.attach-menu-backdrop {
  position: fixed; inset: 0;
  background: rgba(0,0,0,0.3);
  z-index: 50;
  animation: fadeIn 0.15s;
}
.attach-menu {
  position: fixed;
  bottom: 70px;
  left: 12px;
  right: 12px;
  background: var(--modal-bg);
  border-radius: 14px;
  padding: 14px;
  z-index: 51;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(70px, 1fr));
  gap: 8px;
  box-shadow: 0 4px 24px rgba(0,0,0,0.2);
  animation: slideUp 0.2s ease;
}
@keyframes slideUp {
  from { transform: translateY(20px); opacity: 0; }
  to { transform: translateY(0); opacity: 1; }
}
.attach-item {
  display: flex; flex-direction: column;
  align-items: center; gap: 6px;
  padding: 12px 6px;
  border-radius: 10px;
  transition: background 0.15s;
}
.attach-item:hover { background: rgba(0,0,0,0.04); }
[data-theme="dark"] .attach-item:hover { background: rgba(255,255,255,0.04); }
.attach-icon {
  width: 48px; height: 48px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-size: 22px;
  color: white;
}
.attach-icon.image    { background: linear-gradient(135deg, #10B981, #22C55E); }
.attach-icon.camera   { background: linear-gradient(135deg, #1E293B, #334155); }
.attach-icon.document { background: linear-gradient(135deg, #0F172A, #1E293B); }
.attach-item-label {
  font-size: 12px;
  color: var(--text);
}

/* ─── SWITCH CLINIC DROPDOWN ─────────────────────────────────────────── */
.clinic-dropdown-backdrop {
  position: fixed; inset: 0;
  background: rgba(0,0,0,0.3);
  z-index: 50;
  animation: fadeIn 0.15s;
}
.clinic-dropdown {
  position: fixed;
  top: 64px;
  left: 12px;
  right: 12px;
  background: var(--modal-bg);
  border-radius: 12px;
  padding: 8px 0;
  z-index: 51;
  box-shadow: 0 8px 32px rgba(0,0,0,0.2);
  animation: slideDown 0.2s ease;
  max-height: 60vh;
  overflow-y: auto;
}
@keyframes slideDown {
  from { transform: translateY(-10px); opacity: 0; }
  to { transform: translateY(0); opacity: 1; }
}
.clinic-dropdown-header {
  padding: 12px 16px 8px;
  font-size: 12px;
  color: var(--text-muted);
  font-weight: 500;
  letter-spacing: 0.3px;
  text-transform: uppercase;
}
.clinic-item {
  padding: 12px 16px;
  display: flex; align-items: center; gap: 12px;
  cursor: pointer;
  transition: background 0.15s;
}
.clinic-item:hover { background: rgba(15, 23, 42, 0.05); }
[data-theme="dark"] .clinic-item:hover { background: rgba(255,255,255,0.04); }
.clinic-item.active {
  background: rgba(16, 185, 129, 0.08);
}
.clinic-item-avatar {
  width: 40px; height: 40px; border-radius: 50%;
  background: linear-gradient(135deg, #10B981 0%, #22C55E 100%);
  color: white;
  display: flex; align-items: center; justify-content: center;
  font-weight: 600;
  flex-shrink: 0;
  overflow: hidden;
  box-shadow: 0 2px 6px rgba(16, 185, 129, 0.18);
}
.clinic-item-avatar img { width: 100%; height: 100%; object-fit: cover; }
.clinic-item-name { flex: 1; color: var(--text); }
.clinic-item-check { color: #10B981; font-size: 18px; }

/* ─── IMAGE PREVIEW MODAL ───────────────────────────────────────────── */
.image-modal {
  position: fixed; inset: 0;
  background: rgba(0,0,0,0.95);
  z-index: 100;
  display: flex; align-items: center; justify-content: center;
  animation: fadeIn 0.2s;
}
.image-modal img {
  max-width: 100%; max-height: 100%;
  object-fit: contain;
}
.image-modal-close {
  position: absolute;
  top: 12px; right: 12px;
  width: 40px; height: 40px;
  border-radius: 50%;
  background: rgba(0,0,0,0.5);
  color: white;
  display: flex; align-items: center; justify-content: center;
  font-size: 22px;
}

/* ─── ATTRIBUTION (very subtle) ─────────────────────────────────────── */
.powered-by {
  text-align: center;
  font-size: 10px;
  color: var(--text-light);
  opacity: 0.5;
  padding: 2px 0 4px;
  background: var(--app-bg);
  flex-shrink: 0;
}
.powered-by a { color: inherit; text-decoration: none; }

/* ─── CONNECTION STATUS BANNER ──────────────────────────────────────── */
.conn-banner {
  background: #fff3cd;
  color: #856404;
  text-align: center;
  font-size: 13px;
  padding: 6px;
  border-bottom: 1px solid #ffeaa7;
}
.conn-banner-error {
  background: #fee2e2;
  color: #991b1b;
  border-bottom: 1px solid #fecaca;
  cursor: pointer;
  font-weight: 600;
  padding: 10px 6px;
}
[data-theme="dark"] .conn-banner {
  background: #5a4a1f;
  color: #ffe082;
  border-color: #6e5a25;
}
[data-theme="dark"] .conn-banner-error {
  background: #5b1d1d;
  color: #fecaca;
  border-color: #6e2525;
}

/* ─── UPLOAD PROGRESS BAR ───────────────────────────────────────────── */
.upload-progress {
  height: 3px;
  background: rgba(0,0,0,0.06);
  position: relative;
  overflow: hidden;
}
.upload-progress-bar {
  position: absolute;
  inset: 0;
  background: var(--send-btn);
  width: 0%;
  transition: width 0.2s;
}

/* ─── INLINE LINKS IN MESSAGES ──────────────────────────────────────── */
.msg-text {
  /* word-break for very long URLs */
  overflow-wrap: anywhere;
}

/* ─── ACCESSIBILITY ─────────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

/* iOS safe-area insets */
@supports (padding: env(safe-area-inset-bottom)) {
  /* Apply safe-area-bottom on the .composer-area wrapper rather than the
     inner .composer, so the inset sits at the very bottom of the block.
     When a reply-strip is present the strip lives above the composer
     inside .composer-area, and the safe-area space stays where it
     belongs — between the composer's bottom edge and the device's
     gesture / home-indicator area. */
  .composer-area { padding-bottom: max(0px, env(safe-area-inset-bottom)); }
  .header { padding-top: max(10px, env(safe-area-inset-top)); }
}

/* ─── VERIFICATION MODAL ────────────────────────────────────────────── */
.verify-overlay {
  position: fixed; inset: 0;
  background: rgba(15, 23, 42, 0.85);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  animation: fadeIn 0.25s ease;
}
.verify-card {
  background: linear-gradient(135deg, #1E293B 0%, #334155 100%);
  border: 1px solid rgba(16, 185, 129, 0.2);
  border-radius: 24px;
  padding: 32px 28px;
  max-width: 420px;
  width: 100%;
  text-align: center;
  box-shadow:
    0 20px 60px rgba(0, 0, 0, 0.5),
    0 0 80px rgba(16, 185, 129, 0.08);
  animation: verifyCardIn 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
  position: relative;
}
/* Close (X) button — top-left in RTL. Lets the patient bail out of the
   verify flow if it's stuck or they want to retry with a different
   number. Without this they were trapped. */
.verify-close {
  position: absolute;
  top: 12px;
  left: 12px;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.08);
  color: rgba(255, 255, 255, 0.7);
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  cursor: pointer;
  transition: background 0.15s, transform 0.1s, color 0.15s;
  z-index: 1;
}
.verify-close:hover {
  background: rgba(255, 255, 255, 0.15);
  color: #fff;
}
.verify-close:active {
  transform: scale(0.92);
}
.verify-close svg { width: 16px; height: 16px; }
@keyframes verifyCardIn {
  0%   { transform: scale(0.85) translateY(20px); opacity: 0; }
  60%  { transform: scale(1.03) translateY(0); opacity: 1; }
  100% { transform: scale(1) translateY(0); opacity: 1; }
}
.verify-icon {
  width: 64px;
  height: 64px;
  border-radius: 50%;
  background: linear-gradient(135deg, #10B981, #22C55E);
  color: white;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 28px;
  font-weight: 700;
  margin: 0 auto 16px;
  box-shadow: 0 8px 24px rgba(16, 185, 129, 0.35);
  animation: verifyIconPulse 2.5s ease-in-out infinite;
}
@keyframes verifyIconPulse {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.06); box-shadow: 0 12px 32px rgba(16, 185, 129, 0.5); }
}
.verify-title {
  color: #F8FAFC;
  font-size: 20px;
  font-weight: 700;
  margin: 0 0 12px;
  line-height: 1.3;
}
.verify-body {
  color: #CBD5E1;
  font-size: 14.5px;
  line-height: 1.6;
  margin: 0 0 20px;
}
.verify-body strong {
  color: #34D399;
  font-weight: 600;
  font-family: -apple-system, "Geist Mono", monospace;
  direction: ltr;
  display: inline-block;
}
.verify-code-display {
  background: rgba(15, 23, 42, 0.6);
  border: 1.5px dashed rgba(16, 185, 129, 0.4);
  border-radius: 14px;
  padding: 16px 12px;
  margin: 0 0 20px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
}
.verify-code-label {
  color: #94A3B8;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-weight: 600;
}
.verify-code {
  color: #34D399;
  font-size: 28px;
  font-weight: 800;
  letter-spacing: 0.15em;
  font-family: -apple-system, "Geist Mono", "SF Mono", Menlo, monospace;
  direction: ltr;
}
.verify-wa-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%;
  background: linear-gradient(135deg, #25D366 0%, #128C7E 100%);
  color: white;
  font-size: 15.5px;
  font-weight: 700;
  padding: 14px 20px;
  border-radius: 14px;
  text-decoration: none;
  box-shadow: 0 6px 20px rgba(37, 211, 102, 0.35);
  transition: transform 0.12s, box-shadow 0.2s;
  margin-bottom: 16px;
}
.verify-instructions {
  color: #CBD5E1;
  font-size: 14px;
  line-height: 1.7;
  margin: 0 0 14px 0;
  padding: 12px 14px;
  background: rgba(37, 211, 102, 0.08);
  border: 1px solid rgba(37, 211, 102, 0.18);
  border-radius: 12px;
}
.verify-instructions strong {
  color: #34D399;
  font-weight: 700;
}
.verify-wa-btn:hover {
  transform: translateY(-1px);
  box-shadow: 0 8px 28px rgba(37, 211, 102, 0.45);
}
.verify-wa-btn:active {
  transform: translateY(0) scale(0.98);
}
.verify-status {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  color: #94A3B8;
  font-size: 13.5px;
  margin-bottom: 12px;
}
.verify-status-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: #10B981;
  animation: verifyStatusPulse 1.4s ease-in-out infinite;
  box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.4);
}
@keyframes verifyStatusPulse {
  0%   { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.5); }
  70%  { box-shadow: 0 0 0 8px rgba(16, 185, 129, 0); }
  100% { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0); }
}
.verify-footnote {
  color: #64748B;
  font-size: 12px;
  line-height: 1.55;
  margin: 0;
}
.verify-retry-btn {
  background: linear-gradient(135deg, #10B981, #22C55E);
  color: white;
  font-size: 15px;
  font-weight: 700;
  padding: 12px 32px;
  border-radius: 12px;
  box-shadow: 0 4px 16px rgba(16, 185, 129, 0.3);
  cursor: pointer;
  transition: transform 0.12s;
}
.verify-retry-btn:active {
  transform: scale(0.96);
}

/* ═══════════════════════════════════════════════════════════════════════════
   SUGGESTED REPLY CHIPS — empty-state nudges
   ───────────────────────────────────────────────────────────────────────────
   Sit just above the composer on the empty state (Yasmin's opener visible,
   patient hasn't typed). Horizontal row, scrollable if overflow. Tap a chip
   to send that text as a patient message — chips then disappear forever
   for this session. Keeps the chat from feeling dead/empty on first open.
   ═══════════════════════════════════════════════════════════════════════════ */
.suggested-chips {
  flex-shrink: 0;
  padding: 4px 6% 8px;
  display: flex;
  gap: 8px;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x proximity;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  position: relative;
  z-index: 1;
}
.suggested-chips::-webkit-scrollbar { display: none; }
.suggested-chip {
  flex-shrink: 0;
  padding: 9px 16px;
  background: rgba(255, 255, 255, 0.9);
  border: 1.5px solid rgba(16, 185, 129, 0.25);
  border-radius: 20px;
  color: #047857;
  font-size: 13.5px;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  transition: transform 0.12s, background 0.15s, border-color 0.15s, box-shadow 0.15s;
  white-space: nowrap;
  scroll-snap-align: start;
  box-shadow: 0 2px 8px rgba(15, 23, 42, 0.06);
}
.suggested-chip:hover {
  background: #ECFDF5;
  border-color: #10B981;
  box-shadow: 0 4px 12px rgba(16, 185, 129, 0.15);
}
.suggested-chip:active {
  transform: scale(0.96);
  background: #D1FAE5;
}
/* Dark mode — applies if either (a) the app has explicitly set
   data-theme="dark" on a parent, or (b) the user's OS is set to dark mode.
   Two separate blocks because you can't comma-combine a selector with an
   at-rule in CSS. */
[data-theme="dark"] .suggested-chip {
  background: rgba(30, 41, 59, 0.85);
  border-color: rgba(16, 185, 129, 0.35);
  color: #34D399;
}
[data-theme="dark"] .suggested-chip:hover {
  background: rgba(16, 185, 129, 0.18);
}
@media (prefers-color-scheme: dark) {
  .suggested-chip {
    background: rgba(30, 41, 59, 0.85);
    border-color: rgba(16, 185, 129, 0.35);
    color: #34D399;
  }
  .suggested-chip:hover {
    background: rgba(16, 185, 129, 0.18);
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   LOADING SKELETON — replaces spinner during initial chat load
   ═══════════════════════════════════════════════════════════════════════════ */
.skeleton-bubble {
  background: linear-gradient(
    90deg,
    rgba(255, 255, 255, 0.04) 0%,
    rgba(255, 255, 255, 0.10) 50%,
    rgba(255, 255, 255, 0.04) 100%
  );
  background-size: 200% 100%;
  animation: skeletonShimmer 1.4s ease-in-out infinite;
  border-radius: 18px;
  height: 56px;
  margin-bottom: 8px;
  max-width: 70%;
}
.skeleton-bubble:nth-child(1) { width: 55%; align-self: flex-start; }
.skeleton-bubble:nth-child(2) { width: 65%; align-self: flex-start; }
.skeleton-bubble:nth-child(3) { width: 45%; align-self: flex-end; }
@keyframes skeletonShimmer {
  0%   { background-position: 100% 0; }
  100% { background-position: -100% 0; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   PWA standalone-mode hardening
   ───────────────────────────────────────────────────────────────────────────
   Rules below apply only when running as an installed PWA. The body gets
   `is-standalone` from the React side (see App.jsx — body-class hook on
   isStandalone). They make the installed app feel less like a webpage:

   - Chrome elements (header, composer) don't get accidentally text-selected
     when the user long-presses to interact with them.
   - The whole document doesn't allow text dragging (which highlights random
     UI in a way that breaks immersion).
   - Message bubbles KEEP user-select: text — patients need to copy clinic
     phone numbers, instructions, etc. The override is explicit.
   - Tap highlight is already transparent globally (see body rule at top)
     but we double-down here for tappable buttons on standalone, in case
     a future style change loosens the global rule.
   ═══════════════════════════════════════════════════════════════════════════ */
body.is-standalone {
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}
body.is-standalone .messages,
body.is-standalone .bubble,
body.is-standalone .messages-inner,
body.is-standalone textarea,
body.is-standalone input {
  -webkit-user-select: text;
  user-select: text;
  -webkit-touch-callout: default;
}
body.is-standalone button,
body.is-standalone .chat-header,
body.is-standalone .composer-area,
body.is-standalone .composer {
  -webkit-tap-highlight-color: transparent;
  -webkit-user-select: none;
  user-select: none;
}

/* ═══════════════════════════════════════════════════════════════════════════
   SWIPE-TO-REPLY (May 18, 2026)
   Quote header on bubbles, reply strip above the composer, swipe affordance
   icon revealed under the bubble during the drag, scroll-to-quoted flash.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── Quote header inside a bubble (when msg.replyTo is set) ─────────────── */
.msg-quote {
  display: flex;
  align-items: stretch;
  gap: 8px;
  margin-bottom: 6px;
  padding: 6px 8px;
  border-radius: 6px;
  cursor: pointer;
  background: rgba(255, 255, 255, 0.16);
  transition: background 0.15s ease;
}
.msg-quote:hover { background: rgba(255, 255, 255, 0.24); }
.msg-row.outgoing .msg-quote {
  /* On the white outgoing bubble, use a dark-tinted quote background
     so the colored side-bar still pops against the bubble. */
  background: rgba(15, 23, 42, 0.06);
}
.msg-row.outgoing .msg-quote:hover { background: rgba(15, 23, 42, 0.10); }
.msg-quote-bar {
  width: 3px;
  border-radius: 2px;
  background: rgba(255, 255, 255, 0.7);
  flex-shrink: 0;
}
.msg-row.outgoing .msg-quote-bar { background: #10B981; }
.msg-quote-content {
  flex: 1;
  min-width: 0;
  overflow: hidden;
}
.msg-quote-label {
  font-size: 11px;
  font-weight: 600;
  margin-bottom: 2px;
  opacity: 0.85;
}
.msg-quote-text {
  font-size: 12.5px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  opacity: 0.95;
  line-height: 1.4;
}

/* ── Reply affordance: icon revealed under the bubble during swipe ──────── */
.msg-row {
  position: relative;
}
.reply-affordance {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 36px;
  height: 36px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: rgba(16, 185, 129, 0.18);
  color: #10B981;
  pointer-events: none;
  z-index: 0;
  animation: replyAffordancePulse 0.3s ease-out;
}
.reply-affordance.left  { left: 8px;  }
.reply-affordance.right { right: 8px; }
[dir="rtl"] .reply-affordance.left  { left: auto; right: 8px; }
[dir="rtl"] .reply-affordance.right { right: auto; left: 8px; }
@keyframes replyAffordancePulse {
  from { opacity: 0; transform: translateY(-50%) scale(0.6); }
  to   { opacity: 1; transform: translateY(-50%) scale(1); }
}

/* Slight visual feedback on long-press hold */
.msg-bubble.pressed {
  filter: brightness(0.95);
}

/* ── Composer area + reply strip ─────────────────────────────────────────
   .composer-area is the single bottom block that contains the optional
   reply-strip on top of the always-present .composer. By wrapping these
   in one container, the parent (.app) flex column allocates ONE stable
   block of space for the whole bottom — and .composer's own flex-row
   layout (input wrap on one side, action button on the other) is fully
   isolated from whether a reply strip is active. The strip can appear
   and disappear without ever touching the composer's internal flex.

   flex-shrink: 0 on .composer-area means the bottom area never gets
   squeezed by the messages list; min-height: 0 on .messages (above)
   ensures the messages list shrinks first when space is tight. */
.composer-area {
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  background: var(--app-bg);
  border-top: 1px solid var(--divider);
  position: relative;
  z-index: 1;
}
/* The composer's border-top now lives on .composer-area so the divider
   sits ABOVE the reply-strip when one is present. .composer itself no
   longer carries that border. */
.composer-area > .composer {
  border-top: none;
}

.reply-strip {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  margin: 8px 8px 0 8px;
  border-radius: 10px;
  background: var(--input-bg);
  border: 1px solid var(--input-border);
  box-shadow: var(--input-shadow);
  animation: replyStripIn 0.18s ease-out;
}
@keyframes replyStripIn {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}
.reply-strip-bar {
  width: 3px;
  align-self: stretch;
  border-radius: 2px;
  background: #10B981;
  flex-shrink: 0;
}
.reply-strip-content {
  flex: 1;
  min-width: 0;
  overflow: hidden;
}
.reply-strip-label {
  font-size: 11px;
  font-weight: 600;
  color: #10B981;
  margin-bottom: 2px;
}
.reply-strip-text {
  font-size: 13px;
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  line-height: 1.4;
}
.reply-strip-cancel {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border: none;
  background: rgba(148, 163, 184, 0.12);
  color: var(--text-muted);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  flex-shrink: 0;
  transition: background 0.15s ease;
}
.reply-strip-cancel:hover { background: rgba(148, 163, 184, 0.22); }
.reply-strip-cancel svg { width: 16px; height: 16px; }

/* ── Scroll-to-quoted flash highlight ───────────────────────────────────── */
/* When the user taps a quote header, the original message scrolls into view
   and gets a brief emerald glow so they know which one was being referenced. */
.msg-flash .msg-bubble {
  animation: msgFlash 1.2s ease-out;
}
@keyframes msgFlash {
  0%   { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.55); }
  30%  { box-shadow: 0 0 0 6px rgba(16, 185, 129, 0.35); }
  100% { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0); }
}

/* Reduce-motion users get an instant highlight, no animation */
@media (prefers-reduced-motion: reduce) {
  .reply-affordance { animation: none; }
  .reply-strip      { animation: none; }
  .msg-flash .msg-bubble { box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.45); }
}

/* ── Deleted-image placeholder (May 18, 2026) ──────────────────────────── */
/* Renders in place of an <img> when a doctor has hard-deleted the photo.
   Both the patient PWA history hydration and the live WS image_deleted
   event set msg.imageDeleted = true, which trips this branch in
   MessageBubble. Visual: a muted grey card with a trash icon + Arabic
   "deleted" text. Sized to roughly match the empty space a small image
   would occupy so the layout doesn't jump if the deletion happens while
   the chat is open. */
.msg-image-deleted {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 64px;
  padding: 16px 18px;
  margin: -2px -3px 4px;
  border-radius: 8px;
  background: rgba(0, 0, 0, 0.04);
  border: 1px dashed rgba(0, 0, 0, 0.18);
  color: rgba(0, 0, 0, 0.55);
  font-size: 14px;
  /* On dark bubbles the same colors get inverted by the parent bubble's
     theme rule below (.msg-row.assistant .msg-image-deleted etc). */
}
.msg-image-deleted-icon {
  font-size: 18px;
  line-height: 1;
  opacity: 0.85;
}
.msg-image-deleted-text {
  font-weight: 500;
}

/* Dark-bubble variant: user-side bubbles are emerald, the placeholder
   should stay legible against that background. We invert the colors. */
.msg-row.user .msg-image-deleted {
  background: rgba(255, 255, 255, 0.15);
  border-color: rgba(255, 255, 255, 0.30);
  color: rgba(255, 255, 255, 0.85);
}

/* ─── PUSH NOTIFICATIONS BANNER ─────────────────────────────────────────
   Shown inside the messages list (not as a sticky overlay) so it scrolls
   with chat history — patients reading older messages don't get a banner
   sitting over their content. Tone-matched to the dark chat surface.
   Appears only after the patient has been actively using the app and
   the permission state is still "default". */

/* Notifications ribbon — a thin bar directly under the header. The whole
   text area is the tap target (runs the subscribe flow); the × on the end
   dismisses for the current session only. Styled to read as a gentle invite,
   not an alarming alert — sits in the green accent family like conn-banner. */
.notif-ribbon {
  display: flex;
  align-items: stretch;
  background: rgba(74, 222, 128, 0.12);
  border-bottom: 1px solid rgba(74, 222, 128, 0.25);
  flex-shrink: 0;
}
.notif-ribbon-main {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 8px;
  background: transparent;
  border: none;
  cursor: pointer;
  font-family: inherit;
  text-align: start;
  padding: 9px 14px;
  color: #166534;
}
.notif-ribbon-main:disabled { cursor: default; opacity: 0.7; }
.notif-ribbon-icon {
  font-size: 16px;
  flex-shrink: 0;
  line-height: 1;
}
.notif-ribbon-text {
  font-size: 0.78rem;
  font-weight: 600;
  line-height: 1.4;
}
.notif-ribbon-x {
  flex-shrink: 0;
  background: transparent;
  border: none;
  color: #166534;
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  padding: 0 14px;
}
.notif-ribbon-x:hover { background: rgba(74, 222, 128, 0.18); }
[data-theme="dark"] .notif-ribbon {
  background: rgba(74, 222, 128, 0.10);
  border-bottom-color: rgba(74, 222, 128, 0.22);
}
[data-theme="dark"] .notif-ribbon-main,
[data-theme="dark"] .notif-ribbon-x { color: #4ADE80; }
[data-theme="dark"] .notif-ribbon-x:hover { background: rgba(74, 222, 128, 0.12); }
