// ===== m8-messages.jsx =====
// M8 · Messages — replaces window.OrgMessages
// Triage inbox + reading pane. Combines email + SMS into one stream.
// Covers:
//   FR8  communication logs / activity history per volunteer
//   FR9  inbound reply routing + threaded text views
//   FR10 SMS sending with opt-in, STOP, throttling
//   FR16 private-contact protection
//   FR18 sender identity / reply-to behavior

const {
  React,
  TopBar, Card, SectionHead, Btn, IconBtn, StatusChip, Avatar, AvatarStack, Kpi,
  Field, Input, Textarea, Select, Checkbox, Toggle, Tabs,
  IconPlus, IconCheck, IconX, IconArrow, IconArrowL, IconDownload, IconSearch,
  IconMail, IconMsg, IconUsers, IconShield, IconMore, IconEdit, IconBell, IconStar,
  IconExternal, IconChevron,
  VOLUNTEERS,
} = window;
const { useState: useM, useMemo: useMM, useRef: useMR, useEffect: useME } = React;

const m8m_over = { fontSize: 11, letterSpacing: "0.12em", textTransform: "uppercase", color: "var(--fg-3)", fontWeight: 500 };
const m8m_meta = { fontSize: 11, color: "var(--fg-3)" };

// ============================================================
// THREAD DATA
// ============================================================
const THREADS = [
  // SMS · weather cancellation broadcast — replies trickling in
  {
    id: "t-broadcast-jun14",
    channel: "sms",
    kind: "inbound-replies",
    subject: "Cherry Creek cleanup · weather cancellation",
    counterparty: { name: "Roster broadcast · Jun 14 cleanup", avatar: null },
    relatedOp: "Cherry Creek shoreline cleanup · Jun 14",
    site: "Cherry Creek SP",
    senderIdentity: "Priya Sandoval · NE Region",
    repliesRouteTo: "Priya Sandoval (originator)",
    last: "5 m ago",
    unread: 4,
    priority: "high",
    pinned: true,
    auditRef: "sms-broadcast-2026-06-13-2k",
    messages: [
      { from: "out", who: "Priya Sandoval", t: "Sat Jun 13, 4:18 pm", body: "Hi all — heavy storms forecast tomorrow morning. The Cherry Creek shoreline cleanup is moved to Sun Jun 22, same time. Reply Y to confirm new date, N if you can't. — Priya, CPW NE Region", recipients: 28, delivered: 28, audit: "sms-2026-06-13-4:18" },
      { from: "in", who: "Sasha Arenas", t: "Sat Jun 13, 4:24 pm", body: "Y — see you Sunday!", v: "v-arenas" },
      { from: "in", who: "Maya Okonkwo", t: "Sat Jun 13, 4:28 pm", body: "Y", v: "v-okonkwo" },
      { from: "in", who: "Esteban Mendez", t: "Sat Jun 13, 4:31 pm", body: "N can't make Sunday, hope to catch the next one", v: "v-mendez" },
      { from: "in", who: "Ben Liu", t: "Sat Jun 13, 4:34 pm", body: "Y — bringing my brother, OK?", v: "v-liu" },
      { from: "in", who: "Rae Kowalski", t: "Sat Jun 13, 4:42 pm", body: "STOP", v: "v-kowalski", system: "Opt-out recorded. Future SMS suppressed for this number." },
    ],
  },
  // SMS · 1:1 with volunteer
  {
    id: "t-sms-okonkwo",
    channel: "sms",
    kind: "direct",
    subject: "Driver's check expiring + Saturday shift",
    counterparty: { name: "Maya Okonkwo", id: "v-okonkwo" },
    relatedOp: null,
    site: "Boyd Lake SP",
    senderIdentity: "Priya Sandoval · NE Region",
    repliesRouteTo: "Priya Sandoval (originator)",
    last: "Today, 11:42 am",
    unread: 1,
    priority: "high",
    auditRef: "sms-dm-okonkwo-3f",
    messages: [
      { from: "out", who: "Priya Sandoval", t: "Today, 9:14 am", body: "Hi Maya — heads up your driver's check expires Apr 30. Can you swing by the NE office this week to redo? — Priya", audit: "sms-2026-04-23-9:14" },
      { from: "in",  who: "Maya Okonkwo", t: "Today, 9:48 am", body: "Yes! Can I come Thursday around 2 pm?", v: "v-okonkwo" },
      { from: "out", who: "Priya Sandoval", t: "Today, 10:02 am", body: "Perfect, Thursday at 2 works. Also — your Saturday trail crew shift is confirmed for 8 am.", audit: "sms-2026-04-23-10:02" },
      { from: "in",  who: "Maya Okonkwo", t: "Today, 11:42 am", body: "Got it, thank you. One thing — can I bring my kid for part of it? He's 14 and has done a couple before.", v: "v-okonkwo" },
    ],
  },
  // Email · Inbound from volunteer to monitored central inbox (no originator → routed centrally)
  {
    id: "t-em-walsh",
    channel: "email",
    kind: "inbound-routed",
    subject: "Re: April 2026 hours statement — discrepancy",
    counterparty: { name: "Tara Walsh", id: "v-walsh" },
    relatedOp: null,
    site: "Lory SP",
    senderIdentity: "CPW Volunteers (no-reply automated send)",
    repliesRouteTo: "Central inbox — assigned to Marcus Chen",
    last: "Yest, 4:12 pm",
    unread: 2,
    priority: "med",
    auditRef: "em-route-walsh-3a",
    messages: [
      { from: "out", who: "CPW Volunteers (automated)", t: "Apr 22, 6:01 am", body: "Hi Tara, here's your April 2026 hours statement. Lifetime: 1,824. Apr: 32. Year to date: 142. — CPW Volunteers", audit: "em-2026-04-22-6:01", template: "tpl-monthly-hours · v4" },
      { from: "in", who: "Tara Walsh", t: "Yest, 3:48 pm", body: "Hi — the Apr 19 trail crew is missing. I was there 8 am to 1 pm. Could you check? Thanks, Tara", v: "v-walsh" },
      { from: "in", who: "Tara Walsh", t: "Yest, 4:12 pm", body: "Also forgot to mention — Bob Fischer was there too, in case his hours are missing as well.", v: "v-walsh" },
    ],
  },
  // SMS thread visible from a roster (one-tap broadcast)
  {
    id: "t-roster-jun22",
    channel: "sms",
    kind: "broadcast",
    subject: "Sun Jun 22 · Cherry Creek (rescheduled) confirmation",
    counterparty: { name: "Cherry Creek Jun 22 roster", avatar: null },
    relatedOp: "Cherry Creek shoreline cleanup · Jun 22",
    site: "Cherry Creek SP",
    senderIdentity: "Priya Sandoval · NE Region",
    repliesRouteTo: "Priya Sandoval (originator)",
    last: "3 h ago",
    unread: 0,
    auditRef: "sms-broadcast-2026-06-13-4w",
    messages: [
      { from: "out", who: "Priya Sandoval", t: "3 h ago", body: "Quick reminder — Cherry Creek shoreline cleanup is Sun Jun 22, 9–11:30 am. Meet at the volunteer tent (south swim beach). Coffee provided. STOP to opt out.", recipients: 24, delivered: 24, audit: "sms-2026-06-13-3pm" },
    ],
  },
  // Email · Help Needed campaign reply
  {
    id: "t-help-needed-mendez",
    channel: "email",
    kind: "direct",
    subject: "Re: Help needed · Lake Pueblo trash haul Sat",
    counterparty: { name: "Esteban Mendez", id: "v-mendez" },
    relatedOp: "Lake Pueblo trash haul · Jun 28",
    site: "Lake Pueblo SP",
    senderIdentity: "Priya Sandoval · NE Region",
    repliesRouteTo: "Priya Sandoval (originator)",
    last: "Mon, 8:22 am",
    unread: 0,
    auditRef: "em-helpneeded-mendez-1b",
    messages: [
      { from: "out", who: "Priya Sandoval", t: "Mon, 7:14 am", body: "Hey Esteban — Lake Pueblo crew is short for Saturday. Based on your past trips it's a fit (3 mi from your address, you've done 4 of these). Would you be in?", audit: "em-2026-06-08-7:14", template: "tpl-help-needed · v5" },
      { from: "in",  who: "Esteban Mendez", t: "Mon, 8:22 am", body: "I'm in. Bringing my truck — happy to pick up bags if you need to coordinate.", v: "v-mendez" },
    ],
  },
  // SMS thread with red-flagged volunteer (blocked send + reason)
  {
    id: "t-sms-blocked",
    channel: "sms",
    kind: "direct",
    subject: "Send blocked — recipient red-flagged",
    counterparty: { name: "Robert Fischer", id: "v-fischer" },
    relatedOp: null,
    site: "Lory SP",
    senderIdentity: "Priya Sandoval · NE Region",
    repliesRouteTo: "n/a",
    last: "Tue · blocked at send",
    unread: 0,
    priority: "low",
    blocked: true,
    auditRef: "sms-blocked-fischer-2x",
    messages: [
      { from: "out", who: "Priya Sandoval", t: "Tue, 11:30 am", body: "Hi Bob — checking in about your Saturday shift, are you still planning to come?", blocked: true, blockReason: "Recipient red-flagged · pending HR review. Sender saw a clear warning before confirming. Send suppressed; logged for audit." },
    ],
  },
  // Email · staff DM
  {
    id: "t-em-marcus",
    channel: "email",
    kind: "staff-dm",
    subject: "Coordinator handoff · Boyd Lake",
    counterparty: { name: "Marcus Chen", staff: true },
    senderIdentity: "Priya Sandoval",
    repliesRouteTo: "Priya Sandoval",
    last: "Yest",
    unread: 0,
    messages: [
      { from: "out", who: "Priya Sandoval", t: "Yest, 2:14 pm", body: "FYI handing Boyd Lake trail crew over to you for Jun 22 — I'll be at Cherry Creek. Roster is in the system." },
      { from: "in",  who: "Marcus Chen", t: "Yest, 2:42 pm", body: "Got it. I'll do the 24-hr check-in text Friday morning." },
    ],
  },
];

// ============================================================
// ROOT — three-pane (filters · list · reading)
// ============================================================
const OrgMessages = ({ go }) => {
  const [activeId, setActiveId] = useM("t-broadcast-jun14");
  const [filter, setFilter] = useM("inbox");
  const [channel, setChannel] = useM("all");
  const [search, setSearch] = useM("");
  const [composing, setComposing] = useM(false);

  const filtered = THREADS.filter(t => {
    if (channel !== "all" && t.channel !== channel) return false;
    if (filter === "unread" && !t.unread) return false;
    if (filter === "pinned" && !t.pinned) return false;
    if (filter === "blocked" && !t.blocked) return false;
    if (filter === "broadcast" && t.kind !== "broadcast" && t.kind !== "inbound-replies") return false;
    if (search && !(t.subject + " " + t.counterparty.name).toLowerCase().includes(search.toLowerCase())) return false;
    return true;
  });

  const active = THREADS.find(t => t.id === activeId);
  const totalUnread = THREADS.reduce((a, t) => a + t.unread, 0);

  return (
    <>
      <TopBar
        title="Messages"
        subtitle={`${totalUnread} unread · ${THREADS.filter(t => t.channel === "sms").length} SMS threads · ${THREADS.filter(t => t.channel === "email").length} email threads · audit log immutable`}
        primary={<Btn icon={IconPlus} onClick={() => setComposing(true)}>New message</Btn>}
        secondary={<Btn kind="secondary" icon={IconEdit}>Templates</Btn>}
      />
      <div style={{ flex: 1, display: "grid", gridTemplateColumns: "220px 340px 1fr", gap: 0, minHeight: 0, borderTop: "1px solid var(--border-soft)" }}>
        {/* Left: filters */}
        <div style={{ borderRight: "1px solid var(--border-soft)", background: "var(--paper-soft)", padding: "16px 12px", overflow: "auto" }}>
          <div style={{ ...m8m_over, marginBottom: 8, padding: "0 6px" }}>Channel</div>
          <ChanRow active={channel === "all"}    onClick={() => setChannel("all")}    label="All channels" count={THREADS.length} />
          <ChanRow active={channel === "email"}  onClick={() => setChannel("email")}  label="Email" count={THREADS.filter(t => t.channel === "email").length} icon={IconMail} />
          <ChanRow active={channel === "sms"}    onClick={() => setChannel("sms")}    label="SMS" count={THREADS.filter(t => t.channel === "sms").length} icon={IconMsg} />

          <div style={{ ...m8m_over, margin: "20px 0 8px", padding: "0 6px" }}>Filter</div>
          <ChanRow active={filter === "inbox"}     onClick={() => setFilter("inbox")} label="Inbox" count={THREADS.length} />
          <ChanRow active={filter === "unread"}    onClick={() => setFilter("unread")} label="Unread" count={totalUnread} />
          <ChanRow active={filter === "pinned"}    onClick={() => setFilter("pinned")} label="Pinned" count={THREADS.filter(t => t.pinned).length} />
          <ChanRow active={filter === "broadcast"} onClick={() => setFilter("broadcast")} label="Broadcasts" count={THREADS.filter(t => t.kind === "broadcast" || t.kind === "inbound-replies").length} />
          <ChanRow active={filter === "blocked"}   onClick={() => setFilter("blocked")} label="Blocked" count={THREADS.filter(t => t.blocked).length} />

          <div style={{ ...m8m_over, margin: "20px 0 8px", padding: "0 6px" }}>Routing</div>
          <div style={{ padding: "6px 8px", borderRadius: 8, background: "var(--paper)", border: "1px solid var(--border-soft)", marginBottom: 6 }}>
            <div style={{ fontSize: 12, fontWeight: 500 }}>You (Priya Sandoval)</div>
            <div style={{ ...m8m_meta }}>4 threads route to you</div>
          </div>
          <div style={{ padding: "6px 8px", borderRadius: 8, background: "var(--paper)", border: "1px solid var(--border-soft)", marginBottom: 6 }}>
            <div style={{ fontSize: 12, fontWeight: 500 }}>NE central inbox</div>
            <div style={{ ...m8m_meta }}>2 threads, 1 unassigned</div>
          </div>

          <div style={{ ...m8m_over, margin: "20px 0 8px", padding: "0 6px" }}>Saved searches</div>
          {["With opt-outs", "Help Needed replies", "Hour discrepancies", "Roster broadcasts last 7d"].map(s => (
            <div key={s} style={{ padding: "6px 8px", fontSize: 12, color: "var(--fg-2)", cursor: "pointer" }}>{s}</div>
          ))}
        </div>

        {/* Middle: list */}
        <div style={{ borderRight: "1px solid var(--border-soft)", display: "flex", flexDirection: "column", minHeight: 0 }}>
          <div style={{ padding: 12, borderBottom: "1px solid var(--border-soft)" }}>
            <div style={{ position: "relative" }}>
              <Input placeholder="Search messages, volunteers, opportunities…" value={search} onChange={(e) => setSearch(e.target.value)} style={{ width: "100%", paddingLeft: 32 }} />
              <IconSearch size={14} style={{ position: "absolute", left: 12, top: 12, color: "var(--fg-3)" }} />
            </div>
          </div>
          <div style={{ flex: 1, overflow: "auto" }}>
            {filtered.map(t => (
              <ThreadRow key={t.id} t={t} active={t.id === activeId} onClick={() => setActiveId(t.id)} />
            ))}
          </div>
        </div>

        {/* Right: reading */}
        <div style={{ display: "flex", flexDirection: "column", minHeight: 0 }}>
          {active && <ThreadReader t={active} />}
        </div>
      </div>

      {composing && <ComposeDrawer onClose={() => setComposing(false)} />}
    </>
  );
};

const ChanRow = ({ active, onClick, label, count, icon: Icon }) => (
  <button onClick={onClick} style={{
    display: "flex", justifyContent: "space-between", alignItems: "center",
    width: "100%", padding: "8px 10px", borderRadius: 8,
    background: active ? "var(--ink)" : "transparent",
    color: active ? "var(--paper)" : "var(--fg-1)",
    border: "none", cursor: "pointer", fontFamily: "inherit", fontSize: 13, marginBottom: 2,
  }}>
    <span style={{ display: "flex", alignItems: "center", gap: 8 }}>{Icon && <Icon size={14} />}{label}</span>
    <span style={{ fontSize: 11, opacity: 0.7, fontVariantNumeric: "tabular-nums" }}>{count}</span>
  </button>
);

// ============================================================
// THREAD LIST ROW
// ============================================================
const ThreadRow = ({ t, active, onClick }) => {
  const last = t.messages[t.messages.length - 1];
  return (
    <div onClick={onClick} style={{
      padding: "14px 14px",
      borderBottom: "1px solid var(--border-soft)",
      cursor: "pointer",
      background: active ? "var(--paper-soft)" : "transparent",
      borderLeft: active ? "3px solid var(--coral-600)" : "3px solid transparent",
      display: "grid", gridTemplateColumns: "32px 1fr 60px", gap: 10, alignItems: "start",
    }}>
      <ThreadAvatar t={t} />
      <div style={{ minWidth: 0 }}>
        <div style={{ display: "flex", gap: 6, alignItems: "baseline", marginBottom: 2 }}>
          <span style={{ fontSize: 13, fontWeight: t.unread ? 600 : 500, color: "var(--ink)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{t.counterparty.name}</span>
          {t.channel === "sms" && <span style={{ fontSize: 9, padding: "1px 6px", borderRadius: 4, background: "var(--iris-100)", color: "var(--iris-600)", fontWeight: 500 }}>SMS</span>}
          {t.kind === "inbound-replies" && <span style={{ fontSize: 9, padding: "1px 6px", borderRadius: 4, background: "var(--coral-100)", color: "var(--coral-700)", fontWeight: 500 }}>Replies</span>}
          {t.kind === "broadcast" && <span style={{ fontSize: 9, padding: "1px 6px", borderRadius: 4, background: "var(--sage-100)", color: "var(--sage-700)", fontWeight: 500 }}>Broadcast</span>}
          {t.blocked && <span style={{ fontSize: 9, padding: "1px 6px", borderRadius: 4, background: "var(--crimson-100)", color: "var(--crimson-600)", fontWeight: 500 }}>Blocked</span>}
        </div>
        <div style={{ fontSize: 12, color: "var(--fg-1)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", marginBottom: 2, fontWeight: t.unread ? 500 : 400 }}>{t.subject}</div>
        <div style={{ ...m8m_meta, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{last?.body || "—"}</div>
        {t.relatedOp && (
          <div style={{ ...m8m_meta, marginTop: 4, color: "var(--iris-600)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
            <IconExternal size={9} style={{ marginRight: 4, verticalAlign: "middle" }} />{t.relatedOp}
          </div>
        )}
      </div>
      <div style={{ textAlign: "right", display: "flex", flexDirection: "column", alignItems: "flex-end", gap: 4 }}>
        <span style={{ fontSize: 10, color: "var(--fg-3)" }}>{t.last}</span>
        {t.unread > 0 && <span style={{ background: "var(--coral-600)", color: "#FFFFFF", fontSize: 10, fontWeight: 600, padding: "1px 6px", borderRadius: 999, minWidth: 18, textAlign: "center" }}>{t.unread}</span>}
        {t.pinned && <IconStar size={12} style={{ color: "var(--amber-600)" }} />}
      </div>
    </div>
  );
};

const ThreadAvatar = ({ t }) => {
  if (t.counterparty.id) {
    const v = VOLUNTEERS.find(x => x.id === t.counterparty.id);
    if (v) return <Avatar {...v} />;
  }
  if (t.counterparty.staff) {
    return <div style={{ width: 32, height: 32, borderRadius: 999, background: "var(--iris-500)", color: "#FFFFFF", display: "flex", alignItems: "center", justifyContent: "center", fontSize: 12, fontWeight: 600 }}>MC</div>;
  }
  // broadcast / roster
  return <div style={{ width: 32, height: 32, borderRadius: 999, background: "var(--coral-100)", color: "var(--coral-700)", display: "flex", alignItems: "center", justifyContent: "center" }}><IconUsers size={16} /></div>;
};

// ============================================================
// THREAD READER
// ============================================================
const ThreadReader = ({ t }) => {
  const [reply, setReply] = useM("");
  const v = t.counterparty.id ? VOLUNTEERS.find(x => x.id === t.counterparty.id) : null;
  return (
    <>
      <div style={{ padding: "16px 24px", borderBottom: "1px solid var(--border-soft)", display: "flex", justifyContent: "space-between", alignItems: "start", gap: 16 }}>
        <div style={{ flex: 1 }}>
          <div style={{ display: "flex", gap: 10, alignItems: "center", marginBottom: 8 }}>
            <ThreadAvatar t={t} />
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ fontSize: 16, fontWeight: 500, color: "var(--ink)" }}>{t.counterparty.name}</div>
              <div style={{ ...m8m_meta, display: "flex", gap: 10, alignItems: "center", flexWrap: "wrap" }}>
                <span>{t.channel === "sms" ? "SMS thread" : "Email thread"}</span>
                {t.site && <><span>·</span><span>{t.site}</span></>}
                {t.kind === "inbound-replies" && <><span>·</span><span style={{ color: "var(--coral-700)" }}>{t.messages.filter(m => m.from === "in").length} replies to broadcast</span></>}
              </div>
            </div>
          </div>
          <div style={{ fontSize: 14, fontWeight: 500, color: "var(--ink)" }}>{t.subject}</div>
        </div>
        <div style={{ display: "flex", gap: 6 }}>
          {v && <Btn size="sm" kind="ghost" icon={IconExternal}>Open record</Btn>}
          <IconBtn icon={IconStar} size={32} />
          <IconBtn icon={IconMore} size={32} />
        </div>
      </div>

      {/* Routing banner */}
      <div style={{ padding: "10px 24px", background: "var(--paper-soft)", borderBottom: "1px solid var(--border-soft)", display: "flex", gap: 14, fontSize: 11, color: "var(--fg-2)", flexWrap: "wrap" }}>
        <span><span style={{ color: "var(--fg-3)" }}>Sender:</span> {t.senderIdentity}</span>
        <span><span style={{ color: "var(--fg-3)" }}>Replies route to:</span> {t.repliesRouteTo}</span>
        {t.auditRef && <span><span style={{ color: "var(--fg-3)" }}>Audit:</span> <code style={{ fontFamily: "var(--font-mono)" }}>{t.auditRef}</code></span>}
        {t.relatedOp && <span style={{ color: "var(--iris-600)" }}><IconExternal size={10} style={{ marginRight: 3, verticalAlign: "middle" }} />{t.relatedOp}</span>}
      </div>

      {/* Messages */}
      <div style={{ flex: 1, overflow: "auto", padding: "20px 24px" }}>
        {t.channel === "sms" ? <SmsThread t={t} /> : <EmailThread t={t} />}
      </div>

      {/* Compose */}
      <div style={{ padding: "12px 24px 18px", borderTop: "1px solid var(--border-soft)", background: "var(--paper-soft)" }}>
        {t.blocked ? (
          <div style={{ padding: 12, background: "var(--crimson-100)", color: "var(--crimson-600)", borderRadius: 10, fontSize: 12, lineHeight: 1.5 }}>
            <strong>Sending to this recipient is blocked.</strong> {t.messages[0].blockReason}
          </div>
        ) : (
          <>
            <div style={{ display: "flex", gap: 8, marginBottom: 8, alignItems: "center", flexWrap: "wrap" }}>
              <span style={{ ...m8m_over }}>Reply as</span>
              <span style={{ fontSize: 12, color: "var(--ink)" }}>{t.senderIdentity}</span>
              {t.channel === "sms" && <span style={{ fontSize: 11, color: "var(--fg-3)" }}>· {160 - reply.length} chars left in segment</span>}
              <span style={{ marginLeft: "auto", display: "flex", gap: 6 }}>
                <Btn size="sm" kind="ghost">Insert token</Btn>
                <Btn size="sm" kind="ghost">From template</Btn>
              </span>
            </div>
            <Textarea
              rows={t.channel === "sms" ? 2 : 4}
              placeholder={t.channel === "sms" ? "Type a text. STOP / HELP keywords are reserved for opt-out / help." : "Write a reply…"}
              value={reply} onChange={(e) => setReply(e.target.value)} />
            <div style={{ display: "flex", gap: 8, marginTop: 8 }}>
              <Btn size="sm" kind="ghost" icon={IconCheck}>Mark resolved</Btn>
              <Btn size="sm" kind="ghost">Save draft</Btn>
              <div style={{ marginLeft: "auto", display: "flex", gap: 6 }}>
                <Btn size="sm" kind="secondary">Preview</Btn>
                <Btn size="sm" icon={IconArrow}>Send {t.channel === "sms" ? "text" : "reply"}</Btn>
              </div>
            </div>
            <div style={{ ...m8m_meta, marginTop: 8 }}>
              {t.channel === "sms"
                ? "SMS will check phone verification + channel consent at send. STOP from this number suppresses future SMS automatically."
                : "Email applies DKIM/SPF/DMARC. Body is captured in audit. Sensitive content visibility is permissioned."}
            </div>
          </>
        )}
      </div>
    </>
  );
};

// ---- Email-style rendering ----
const EmailThread = ({ t }) => (
  <div style={{ display: "flex", flexDirection: "column", gap: 14, maxWidth: 760, margin: "0 auto" }}>
    {t.messages.map((m, i) => (
      <Card key={i} padded={false} style={{ background: m.from === "out" ? "var(--paper-soft)" : "var(--paper)" }}>
        <div style={{ padding: "12px 16px", borderBottom: "1px solid var(--border-soft)", display: "flex", justifyContent: "space-between", alignItems: "center", gap: 10 }}>
          <div style={{ display: "flex", gap: 10, alignItems: "center" }}>
            {m.from === "in" && m.v ? <Avatar {...VOLUNTEERS.find(v => v.id === m.v)} /> : <div style={{ width: 28, height: 28, borderRadius: 999, background: "var(--iris-500)", color: "#FFFFFF", display: "flex", alignItems: "center", justifyContent: "center", fontSize: 11, fontWeight: 600 }}>{m.who.split(" ").map(w => w[0]).join("").slice(0, 2)}</div>}
            <div>
              <div style={{ fontSize: 13, fontWeight: 500 }}>{m.who}</div>
              <div style={{ ...m8m_meta }}>{m.t}{m.template && <span> · <code style={{ fontFamily: "var(--font-mono)" }}>{m.template}</code></span>}</div>
            </div>
          </div>
          <div style={{ display: "flex", gap: 6 }}>
            {m.from === "out" && <span style={{ fontSize: 10, padding: "2px 7px", borderRadius: 999, background: "var(--sage-100)", color: "var(--sage-700)", fontWeight: 500 }}>Sent · delivered</span>}
            {m.audit && <code style={{ fontSize: 10, fontFamily: "var(--font-mono)", color: "var(--fg-3)" }}>{m.audit}</code>}
          </div>
        </div>
        <div style={{ padding: "16px 18px", fontSize: 14, lineHeight: 1.6, color: "var(--ink)", whiteSpace: "pre-wrap" }}>
          {m.body}
        </div>
      </Card>
    ))}
  </div>
);

// ---- SMS-style rendering ----
const SmsThread = ({ t }) => (
  <div style={{ display: "flex", flexDirection: "column", gap: 8, maxWidth: 640, margin: "0 auto", paddingBottom: 12 }}>
    {t.messages.map((m, i) => {
      const isOut = m.from === "out";
      return (
        <React.Fragment key={i}>
          <div style={{ display: "flex", justifyContent: isOut ? "flex-end" : "flex-start", alignItems: "end", gap: 8 }}>
            {!isOut && m.v && <Avatar {...VOLUNTEERS.find(v => v.id === m.v)} />}
            <div style={{ maxWidth: "70%" }}>
              {m.recipients && (
                <div style={{ ...m8m_meta, marginBottom: 4, textAlign: "right" }}>
                  Broadcast · {m.recipients} recipients · {m.delivered} delivered · {m.audit && <code style={{ fontFamily: "var(--font-mono)" }}>{m.audit}</code>}
                </div>
              )}
              <div style={{
                background: isOut ? (m.blocked ? "var(--crimson-100)" : "var(--coral-600)") : "var(--paper-deep)",
                color: isOut ? (m.blocked ? "var(--crimson-600)" : "#FFFFFF") : "var(--ink)",
                padding: "10px 14px", borderRadius: 16,
                borderBottomRightRadius: isOut ? 4 : 16,
                borderBottomLeftRadius: isOut ? 16 : 4,
                fontSize: 14, lineHeight: 1.5,
                opacity: m.blocked ? 0.85 : 1,
              }}>
                {m.body}
              </div>
              <div style={{ ...m8m_meta, marginTop: 4, textAlign: isOut ? "right" : "left", display: "flex", gap: 8, justifyContent: isOut ? "flex-end" : "flex-start" }}>
                <span>{m.who} · {m.t}</span>
                {!isOut && m.v && <span style={{ color: "var(--iris-600)" }}>Routed to originator</span>}
                {isOut && !m.recipients && !m.blocked && <span style={{ color: "var(--sage-700)" }}>· Sent</span>}
              </div>
              {m.system && (
                <div style={{ marginTop: 6, padding: "8px 10px", background: "var(--amber-100)", color: "var(--amber-600)", borderRadius: 8, fontSize: 11, lineHeight: 1.5, maxWidth: 360 }}>
                  <IconShield size={11} style={{ verticalAlign: "middle", marginRight: 4 }} />{m.system}
                </div>
              )}
              {m.blockReason && (
                <div style={{ marginTop: 6, padding: "8px 10px", background: "var(--crimson-100)", color: "var(--crimson-600)", borderRadius: 8, fontSize: 11, lineHeight: 1.5, maxWidth: 360 }}>
                  <IconShield size={11} style={{ verticalAlign: "middle", marginRight: 4 }} />{m.blockReason}
                </div>
              )}
            </div>
          </div>
        </React.Fragment>
      );
    })}
  </div>
);

// ============================================================
// COMPOSE DRAWER (new message)
// ============================================================
const ComposeDrawer = ({ onClose }) => {
  const [chan, setChan] = useM("sms");
  const [audience, setAudience] = useM("one");
  return (
    <div style={overlay} onClick={onClose}>
      <div style={drawer} onClick={(e) => e.stopPropagation()}>
        <div style={drawerHead}>
          <div>
            <div style={{ ...m8m_over }}>New message</div>
            <div style={{ fontSize: 19, fontWeight: 500, color: "var(--ink)", marginTop: 4 }}>
              Send a {chan === "sms" ? "text" : "email"} to {audience === "one" ? "one volunteer" : audience === "roster" ? "an event roster" : "a saved segment"}
            </div>
          </div>
          <IconBtn icon={IconX} size={32} onClick={onClose} aria-label="Close" />
        </div>
        <div style={{ flex: 1, overflow: "auto", padding: 24 }}>
          <Field label="Channel">
            <div style={{ display: "flex", gap: 8 }}>
              {[
                { id: "email", t: "Email", d: "Long-form, rich content" },
                { id: "sms",   t: "SMS",   d: "Short, fast, opt-in required" },
              ].map(c => (
                <Card key={c.id} onClick={() => setChan(c.id)} style={{ padding: 14, cursor: "pointer", flex: 1, borderColor: chan === c.id ? "var(--coral-500)" : "var(--border-soft)", borderWidth: chan === c.id ? 2 : 1 }}>
                  <div style={{ fontSize: 13, fontWeight: 500 }}>{c.t}</div>
                  <div style={{ ...m8m_meta, marginTop: 3 }}>{c.d}</div>
                </Card>
              ))}
            </div>
          </Field>
          <Field label="Audience">
            <div style={{ display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: 8 }}>
              {[
                { id: "one", t: "One volunteer" },
                { id: "roster", t: "Event roster" },
                { id: "segment", t: "Saved segment" },
              ].map(o => (
                <Card key={o.id} onClick={() => setAudience(o.id)} style={{ padding: 12, cursor: "pointer", borderColor: audience === o.id ? "var(--coral-500)" : "var(--border-soft)", borderWidth: audience === o.id ? 2 : 1 }}>
                  <div style={{ fontSize: 13, fontWeight: 500 }}>{o.t}</div>
                </Card>
              ))}
            </div>
          </Field>

          {audience === "one" && <Field label="To"><Input placeholder="Search a volunteer…" defaultValue="Maya Okonkwo" /></Field>}
          {audience === "roster" && <Field label="Event roster"><Select defaultValue="cherry" options={["Cherry Creek shoreline · Jun 22 (24)", "Boyd Lake trail crew · Jun 28 (18)", "Hunter ed class · Jul 5 (8 instr, 22 students)"]} /></Field>}
          {audience === "segment" && <Field label="Saved segment"><Select defaultValue="ne-active" options={["All active NE volunteers (968)", "Camp hosts (84)", "Lapsed >12 mo (642)"]} /></Field>}

          {chan === "email" && (
            <>
              <Field label="Subject"><Input placeholder="Subject line…" /></Field>
              <Field label="From / Reply-to"><Select defaultValue="priya" options={["Priya Sandoval (priya.s@cpw.state.co.us)", "CPW Volunteers (central inbox)"]} /></Field>
            </>
          )}
          <Field label="Message">
            <Textarea rows={chan === "sms" ? 3 : 8} placeholder={chan === "sms" ? "Hi {{first_name}}, …" : "Write your email…"} />
          </Field>

          <div style={{ display: "flex", gap: 6, flexWrap: "wrap" }}>
            <Btn size="sm" kind="ghost">Insert token</Btn>
            <Btn size="sm" kind="ghost">From template</Btn>
            <Btn size="sm" kind="ghost">Preview personalization</Btn>
          </div>

          {chan === "sms" && (
            <div style={{ marginTop: 16, padding: 12, background: "var(--amber-100)", color: "var(--amber-600)", borderRadius: 10, fontSize: 12, lineHeight: 1.5 }}>
              SMS requires verified phone + channel consent. Recipients without both are excluded automatically. STOP suppresses the channel.
            </div>
          )}
        </div>
        <div style={drawerFoot}>
          <Btn kind="ghost" onClick={onClose}>Cancel</Btn>
          <div style={{ marginLeft: "auto", display: "flex", gap: 8 }}>
            <Btn kind="secondary" icon={IconMail}>Send test</Btn>
            <Btn icon={IconArrow}>Send</Btn>
          </div>
        </div>
      </div>
    </div>
  );
};

const overlay = { position: "fixed", inset: 0, background: "rgba(31,27,22,0.45)", backdropFilter: "blur(4px)", zIndex: 80, display: "flex", justifyContent: "flex-end" };
const drawer = { width: "min(720px, 100%)", height: "100%", background: "var(--paper)", display: "flex", flexDirection: "column", boxShadow: "var(--shadow-lg)" };
const drawerHead = { padding: "20px 24px 16px", display: "flex", justifyContent: "space-between", alignItems: "start", gap: 14, borderBottom: "1px solid var(--border-soft)" };
const drawerFoot = { padding: "14px 24px", borderTop: "1px solid var(--border-soft)", display: "flex", alignItems: "center", gap: 8, background: "var(--paper-soft)" };

// ============================================================
Object.assign(window, { OrgMessages });
