/* Reeve — IM tab surfaces: identity header, search, bottom tabbar,
   邮箱 (a Gmail-like client for re-checking Reeve missed nothing),
   文档库 (a document library Reeve references). All content traces to the
   real corpus (threads 6a222302 / Marcus / Joe Comerford / Adam, the quote &amp; brochure facts).
   Loaded BEFORE im.jsx so these globals are available to ReeveIMApp. */

/* ---------- icons ---------- */
function IconChat() {return <svg viewBox="0 0 24 24" width="23" height="23" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"><path d="M20 11.5a7.5 7.5 0 0 1-10.9 6.7L4 19l1-4.2A7.5 7.5 0 1 1 20 11.5Z" /></svg>;}
function IconMail() {return <svg viewBox="0 0 24 24" width="23" height="23" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"><rect x="3" y="5" width="18" height="14" rx="2.5" /><path d="m3.5 7 8.5 6 8.5-6" /></svg>;}
function IconBook() {return <svg viewBox="0 0 24 24" width="23" height="23" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"><path d="M4 5.5A1.5 1.5 0 0 1 5.5 4H17a1 1 0 0 1 1 1v14a1 1 0 0 1-1 1H5.5A1.5 1.5 0 0 1 4 18.5Z" /><path d="M8 8.5h6M8 12h4" /></svg>;}
function IconUser() {return <svg viewBox="0 0 24 24" width="23" height="23" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="8.5" r="3.6" /><path d="M5.5 19.5a6.5 6.5 0 0 1 13 0" /></svg>;}
function IconSearch() {return <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" strokeWidth="1.9" strokeLinecap="round"><circle cx="11" cy="11" r="6.5" /><path d="m16 16 4 4" /></svg>;}
function IconCompose() {return <svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M4 20h16" /><path d="M14.5 5.5 18.5 9.5 9 19l-4.5 1 1-4.5z" /></svg>;}
function IconMenu() {return <svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" strokeWidth="1.9" strokeLinecap="round"><path d="M4 7h16M4 12h16M4 17h10" /></svg>;}
function IconChevDown() {return <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="m6 9 6 6 6-6" /></svg>;}
function IconArrowUp() {return <svg viewBox="0 0 24 24" width="19" height="19" fill="none" stroke="currentColor" strokeWidth="2.1" strokeLinecap="round" strokeLinejoin="round"><path d="M12 19V6M6 12l6-6 6 6" /></svg>;}
function IconPaperclip() {return <svg viewBox="0 0 24 24" width="19" height="19" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"><path d="M21 11.5l-8.5 8.5a5 5 0 0 1-7-7l8.5-8.5a3.3 3.3 0 0 1 4.7 4.7l-8.5 8.5a1.6 1.6 0 0 1-2.3-2.3l7.8-7.8" /></svg>;}
function IconMicSm() {return <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><rect x="9" y="3" width="6" height="11" rx="3" /><path d="M5.5 11.5a6.5 6.5 0 0 0 13 0M12 18v3" /></svg>;}
function IconKeyboard() {return <svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"><rect x="2.5" y="6" width="19" height="12" rx="2.5" /><path d="M6 9.5h0M9.5 9.5h0M13 9.5h0M16.5 9.5h0M6 13h0M16.5 13h0M9 13h6" /></svg>;}
function IconCheckLg() {return <svg viewBox="0 0 24 24" width="19" height="19" fill="none" stroke="currentColor" strokeWidth="2.3" strokeLinecap="round" strokeLinejoin="round"><path d="m4 12.5 5 5 11-11" /></svg>;}

/* ---------- unified top bar: drawer ☰ · section title · account avatar ----------
   Replaces the old identity header. The pencil/compose action is gone — new
   conversations start from the persistent floating composer; sections live in
   the drawer; the avatar (right) opens 个人页. */
function IMTopBar({ title, onMenu, onMe }) {
  return (
    <div className="im-topbar">
      <button className="im-tb-btn" onClick={onMenu} aria-label="菜单"><IconMenu /></button>
      <span className="im-tb-title">{title}</span>
      <button className="im-tb-ava" onClick={onMe} aria-label="个人页"><Avatar dot /></button>
    </div>);

}

/* ---------- left drawer: primary navigation (消息 / 邮箱 / 文档库) + identity ---------- */
function IMDrawer({ open, tab, onClose, onNav, onMe, badge, mailUnread, count }) {
  const items = [
  { id: "messages", lb: "消息", sub: "Reeve 跟进的会话", ic: <IconChat />, n: badge },
  { id: "mail", lb: "邮箱", sub: "复核 Reeve 有没漏处理", ic: <IconMail />, n: mailUnread },
  { id: "wiki", lb: "文档库", sub: "Reeve 引用的资料", ic: <IconBook /> }];
  return (
    <div className={`im-drawer ${open ? "open" : ""}`} aria-hidden={!open}>
      <div className="im-drawer-scrim" onClick={onClose} />
      <aside className="im-drawer-panel">
        <button className="im-drawer-id" onClick={onMe}>
          <span className="im-drawer-av"><Avatar /></span>
          <span className="im-drawer-id-tx">
            <b>Demarco</b>
            <span>健康险 agent · 个人页</span>
          </span>
          <span className="im-drawer-chev">›</span>
        </button>
        <div className="im-drawer-nav">
          {items.map((it) =>
          <button key={it.id} className={`im-drawer-item ${tab === it.id ? "on" : ""}`} onClick={() => onNav(it.id)}>
              <span className="im-drawer-ic">{it.ic}</span>
              <span className="im-drawer-tx"><b>{it.lb}</b><span>{it.sub}</span></span>
              {it.n ? <span className="im-drawer-n">{it.n}</span> : <span className="im-drawer-chev sm">›</span>}
            </button>
          )}
        </div>
        <div className="im-drawer-foot">
          <span className="im-drawer-orb"><ReeveMark size={12} stroke={2.2} /></span>
          <span>Reeve 正在跟进 {count} 条线</span>
        </div>
      </aside>
    </div>);

}

/* ---------- persistent floating composer (home): type a new task to Reeve,
   collapse DOWN to a recall pill (slides back up on tap), or tap the mic to
   enter in-place listening mode. ---------- */
function HomeComposer({ onSubmit, onExpand }) {
  const [val, setVal] = React.useState("");
  const [hidden, setHidden] = React.useState(false);
  const [listening, setListening] = React.useState(false);
  const canSend = val.trim().length > 0;
  const submit = () => { if (!canSend) return; onSubmit(val.trim()); setVal(""); };
  const onKey = (e) => { if (e.key === "Enter") { e.preventDefault(); submit(); } };
  const sendVoice = () => { setListening(false); onSubmit("语音 · 帮我跟进一下这件事"); };
  return (
    <React.Fragment>
      <div className={`im-hc ${hidden ? "down" : ""}`}>
        <button className="im-hc-collapse" onClick={() => { setListening(false); setHidden(true); }} aria-label="收起"><IconChevDown /></button>
        {listening ?
          <div className="im-hc-bar voice">
            <button className="im-hc-kb" onClick={() => setListening(false)} aria-label="切回键盘"><IconKeyboard /></button>
            <div className="im-hc-wavewrap">
              <span className="im-hc-rec" />
              <div className="im-hc-wave">
                {Array.from({ length: 22 }).map((_, i) =>
                  <i key={i} style={{ animationDelay: (i * 0.06).toFixed(2) + "s", animationDuration: (0.7 + (i % 5) * 0.12).toFixed(2) + "s" }} />)}
              </div>
              <span className="im-hc-vtime">0:03</span>
            </div>
            <button className="im-hc-send" onClick={sendVoice} aria-label="完成"><IconCheckLg /></button>
          </div> :
          <div className="im-hc-bar">
            <button className="im-hc-tool" onClick={onExpand} aria-label="添加附件"><IconPaperclip /></button>
            <input className="im-hc-input" value={val} placeholder="把一件事交给 Reeve…"
              onChange={(e) => setVal(e.target.value)} onKeyDown={onKey} />
            {canSend ?
              <button className="im-hc-send" onClick={submit} aria-label="发送"><IconArrowUp /></button> :
              <button className="im-hc-mic" onClick={() => setListening(true)} aria-label="语音"><IconMicSm /></button>}
          </div>}
      </div>
      <button className={`im-hc-fab ${hidden ? "show" : ""}`} onClick={() => setHidden(false)} aria-label="展开输入">
        <ReeveMark size={16} stroke={2.2} /><span>交给 Reeve 一件事</span>
      </button>
    </React.Fragment>);

}
function IconCheck() {return <svg viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round"><path d="m4 12.5 5 5 11-11" /></svg>;}
function IconPlus() {return <svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round"><path d="M12 5v14M5 12h14" /></svg>;}

/* ---------- identity header (legacy; superseded by IMTopBar) ---------- */
function IMTopUser({ onMe }) {
  return (
    <div className="im-id">
      <button className="im-id-btn" onClick={onMe}>
        <Avatar dot />
        <span className="im-id-name">Demarco</span>
      </button>
    </div>);

}

/* ---------- shared search field ---------- */
function IMSearch({ placeholder }) {
  return <div className="im-search"><IconSearch /><span className="im-search-ph">{placeholder}</span></div>;
}

/* ---------- bottom tabbar ----------
   Center column is a proactive 「新建会话」action (not a tab) — lets the user
   open a thread themselves instead of only reacting to ones Reeve relays. */
function TabBar({ tab, onTab, badge, onNew }) {
  const left = [
  { id: "messages", lb: "消息", ic: <IconChat /> },
  { id: "mail", lb: "邮箱", ic: <IconMail /> }];
  const right = [
  { id: "wiki", lb: "文档库", ic: <IconBook /> },
  { id: "me", lb: "我", ic: <IconUser /> }];

  const Tab = (t) =>
    <button key={t.id} className={`im-tab ${tab === t.id ? "on" : ""}`} onClick={() => onTab(t.id)}>
      <span className="im-tab-ic">{t.ic}</span>
      <span className="im-tab-lb">{t.lb}</span>
      {t.id === "messages" && badge ? <span className="im-tab-dot">{badge}</span> : null}
    </button>;

  return (
    <div className="im-tabbar">
      {left.map(Tab)}
      <button className="im-tab im-tab-new" onClick={onNew} aria-label="新建会话">
        <span className="im-tab-newbtn"><IconPlus /></span>
        <span className="im-tab-lb">新建</span>
      </button>
      {right.map(Tab)}
    </div>);

}

/* ============================================================
   邮箱 — a professional mail client. Folders (收件箱/星标/已发送/草稿),
   rich rows, full detail with sender block + attachments + action bar,
   compose sheet. Grounded in the real corpus threads.
   ============================================================ */
function StarIcon({ filled }) {return <svg viewBox="0 0 24 24" width="17" height="17" fill={filled ? "currentColor" : "none"} stroke="currentColor" strokeWidth="1.7" strokeLinejoin="round"><path d="M12 3.5l2.6 5.3 5.9.9-4.3 4.1 1 5.8L12 17l-5.2 2.6 1-5.8L3.5 9.7l5.9-.9z" /></svg>;}
function ClipIcon() {return <svg viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M21 11.5l-8.5 8.5a5 5 0 0 1-7-7l8.5-8.5a3.3 3.3 0 0 1 4.7 4.7l-8.5 8.5a1.6 1.6 0 0 1-2.3-2.3l7.8-7.8" /></svg>;}
function ArchiveIcon() {return <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"><rect x="3" y="4" width="18" height="4" rx="1" /><path d="M5 8v11a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V8" /><path d="M10 12h4" /></svg>;}
function TrashIcon() {return <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"><path d="M4 7h16M9 7V5h6v2M6 7l1 13h10l1-13" /></svg>;}
function ReplyIcon() {return <svg viewBox="0 0 24 24" width="17" height="17" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M9 7L4 12l5 5" /><path d="M4 12h9a7 7 0 0 1 7 7" /></svg>;}
function ReplyAllIcon() {return <svg viewBox="0 0 24 24" width="17" height="17" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M8 7l-5 5 5 5" /><path d="M13 7L8 12l5 5" /><path d="M8 12h7a6 6 0 0 1 6 6" /></svg>;}
function ForwardIcon() {return <svg viewBox="0 0 24 24" width="17" height="17" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M15 7l5 5-5 5" /><path d="M20 12h-9a7 7 0 0 0-7 7" /></svg>;}
function ArrowLeftIcon() {return <svg viewBox="0 0 24 24" width="21" height="21" fill="none" stroke="currentColor" strokeWidth="1.9" strokeLinecap="round" strokeLinejoin="round"><path d="M19 12H5" /><path d="m11 6-6 6 6 6" /></svg>;}
function SendIcon() {return <svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M22 2 11 13" /><path d="M22 2 15 22l-4-9-9-4 20-7Z" /></svg>;}
function DotsIcon() {return <svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor" aria-hidden="true"><circle cx="12" cy="5" r="1.7" /><circle cx="12" cy="12" r="1.7" /><circle cx="12" cy="19" r="1.7" /></svg>;}
function AttachIcon() {return <svg viewBox="0 0 24 24" width="19" height="19" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"><path d="M21 11.5l-8.5 8.5a5 5 0 0 1-7-7l8.5-8.5a3.3 3.3 0 0 1 4.7 4.7l-8.5 8.5a1.6 1.6 0 0 1-2.3-2.3l7.8-7.8" /></svg>;}

const MAIL_TINT = {
  Sherrell: "oklch(58% .13 168)", Marcus: "oklch(56% .14 255)", "招生办 Hamad": "oklch(57% .15 305)",
  Demarco: "oklch(56% .03 255)"
};

function mailData() {
  const E = window.EMBER;
  const inbox = [
  { id: "sherrell", cid: "sherrell", from: "Sherrell", email: "sherrell.m@gmail.com",
    subject: "Quotes?", time: "今天 8:48", preview: "Good morning! Does 10:30 AM today still work for our call?",
    unread: true, star: true, labels: ["客户", "报价"],
    reeve: "客户来要报价 → 约了 3/10 10:30 通话。我已跟到「确认通话 + 问号码」，**草稿在对话里等你拍板**。", raw: E.thread.raw },
  { id: "marcus", cid: "marcus", from: "Marcus", email: "marcus.t@outlook.com",
    subject: "Enrollment", time: "3 天前", preview: "Sounds good — I'll get my enrollment info over to you.",
    unread: true, star: false, labels: ["客户", "投保"],
    reeve: "答应发投保信息却拖了 3 天，**投保临近截止**。提醒已拟，在对话里等你拍板。", raw: E.thread2.raw },
  { id: "comerford", cid: "comerford", from: "Joe Comerford", email: "jcomerford@aversalinn.com",
    subject: "RE: Policy #B6006396 — documents still outstanding", time: "5/22", preview: "Please advise status. Is the Bond still needed?",
    unread: true, star: true, labels: ["客户", "商业险"],
    reeve: "你追了**三次没回**，保单 **1/22 取消**。我拟好第 4 封 —— 点明截止 + 给电话台阶，在对话里等你拍板。", raw: E.threadComerford.raw },
  { id: "adam", cid: "adam", from: "Adam", email: "adam@—", 
    subject: "Re: 续约 / 方案", time: "昨天", preview: "Let's align on what you're looking for. Switching changes our workflow…",
    unread: true, star: false, labels: ["客户", "续约"],
    reeve: "客户**在考虑换一家**，但松口先对齐需求。约通话 + 发日历邀请的草稿已拟，在对话里等你拍板。", raw: E.threadAdam.raw }];

  const sent = [
  { id: "s-sherrell", from: "Demarco", to: "Sherrell", email: "sherrell.m@gmail.com",
    subject: "Re: Quotes?", time: "3/3 5:52 PM", preview: "Attaching PDFs of all the plans. UHC and Multiplan are very similar…",
    attachments: [{ name: "UHC Choice Plus PPO.pdf", ext: "pdf" }, { name: "Multiplan PHCS PPO.pdf", ext: "pdf" }, { name: "牙科.pdf", ext: "pdf" }, { name: "视力.pdf", ext: "pdf" }],
    raw: { subject: "Re: Quotes?", messages: [{ from: "Demarco", to: "Sherrell", date: "3/3 5:52 PM", body: [{ t: "Hi Sherrell — attaching PDFs of all the plans. The UnitedHealthcare Choice Plus PPO and the Multiplan PHCS PPO are very similar in coverage, just different networks — UHC is more widely accepted. Both: no copays, $0 deductible. Dental & vision brochures included below. — Demarco" }] }] } },
  { id: "s-confirm", from: "Demarco", to: "Sherrell", email: "sherrell.m@gmail.com",
    subject: "Re: Quotes?", time: "3/3 6:10 PM", preview: "Okay great — I'll put you down for Tuesday 10:30 AM, March 10th…",
    attachments: [{ name: "牙科.pdf", ext: "pdf" }, { name: "视力.pdf", ext: "pdf" }],
    raw: { subject: "Re: Quotes?", messages: [{ from: "Demarco", to: "Sherrell", date: "3/3 6:10 PM", body: [{ t: "Okay great — I'll put you down for Tuesday 10:30 AM, March 10th, and I'll send a confirmation the day before. Dental & vision brochures attached as well. Talk soon! — Demarco" }] }] } }];

  const drafts = [
  { id: "d-sherrell", cid: "sherrell", from: "Demarco", to: "Sherrell", email: "sherrell.m@gmail.com",
    subject: "Re: Quotes?", time: "草稿", preview: "Good morning Sherrell! Does 10:30 AM today still work for our call?",
    reeve: "Reeve 拟好的通话确认 —— 在对话里长按拍板即可发出。",
    raw: { subject: "Re: Quotes?", messages: [{ from: "Demarco", to: "Sherrell", date: "草稿", body: [{ t: "Good morning Sherrell! Does 10:30 AM today still work for our call? What number should I call? — Demarco" }] }] } },
  { id: "d-marcus", cid: "marcus", from: "Demarco", to: "Marcus", email: "marcus.t@outlook.com",
    subject: "Re: Enrollment", time: "草稿", preview: "Circling back on your enrollment before the deadline…",
    reeve: "Reeve 拟好的投保提醒 —— 在对话里拍板发出。",
    raw: { subject: "Re: Enrollment", messages: [{ from: "Demarco", to: "Marcus", date: "草稿", body: [{ t: "Hi Marcus — circling back on your enrollment. To lock in coverage starting the 1st, I'll need your info before the deadline (3 days out). Happy to walk you through it on a quick call if that's easier. — Demarco" }] }] } }];

  return { inbox, sent, drafts };
}

const MB_FOLDERS = [
{ id: "inbox", t: "收件箱" }, { id: "starred", t: "星标" }, { id: "sent", t: "已发送" }, { id: "drafts", t: "草稿" }];


function renderBody(parts) {
  return parts.map((p, i) => p.hi ? <mark key={i}>{p.t}</mark> : <React.Fragment key={i}>{p.t}</React.Fragment>);
}

/* tiny **bold** parser for Reeve notes */
function boldParts(s) {
  return s.split(/(\*\*[^*]+\*\*)/).map((seg, i) =>
  seg.startsWith("**") ? <b key={i}>{seg.slice(2, -2)}</b> : <React.Fragment key={i}>{seg}</React.Fragment>);
}

function AttachChip({ a }) {
  return <span className="mb-att"><FileIcon ext={a.ext} /><span className="mb-att-name">{a.name}</span></span>;
}

function MailDetail({ mail, onBack, onOpenChat, prefillReply, star, onStar }) {
  const [reply, setReply] = React.useState(prefillReply || "");
  const [replying, setReplying] = React.useState(!!prefillReply);
  const taRef = React.useRef(null);
  React.useEffect(() => {
    if (replying && taRef.current) {
      const el = taRef.current;el.focus();
      el.setSelectionRange(el.value.length, el.value.length);
    }
  }, [replying]);
  const sender = mail.from;
  return (
    <div className="mb-d">
      <div className="mb-d-nav">
        <button className="kb-back" onClick={onBack} aria-label="返回"><span className="kb-chev-l">‹</span>邮箱</button>
        <div className="mb-d-nav-act">
          <button className={`mb-icbtn ${star ? "on" : ""}`} onClick={onStar} aria-label="星标"><StarIcon filled={star} /></button>
          <button className="mb-icbtn" aria-label="归档"><ArchiveIcon /></button>
          <button className="mb-icbtn" aria-label="删除"><TrashIcon /></button>
        </div>
      </div>
      <div className="mb-d-scroll">
        <div className="mb-d-subj">{mail.raw.subject}</div>
        {mail.labels && mail.labels.length ? <div className="mb-d-labels">{mail.labels.map((l, i) => <span className="mb-label" key={i}>{l}</span>)}</div> : null}
        <div className="mb-d-sender">
          <span className="mb-d-av" style={{ "--pav": MAIL_TINT[sender] || "var(--ink-3)" }}>{sender.slice(0, 1)}</span>
          <div className="mb-d-sender-tx">
            <div className="mb-d-sender-top"><span className="mb-d-from">{sender}</span><span className="mb-d-time">{mail.time}</span></div>
            <div className="mb-d-to">{mail.to ? "发给 " + mail.to : "发给 我"} · {mail.email}</div>
          </div>
        </div>
        {mail.reeve ?
        <button className={`mb-reeve ${mail.cid ? "link" : ""}`} onClick={() => mail.cid && onOpenChat(mail.cid)}>
            <ReeveMark size={16} />
            <div className="mb-reeve-tx">{boldParts(mail.reeve)}{mail.cid ? <span className="mb-reeve-go">在对话里处理 ›</span> : null}</div>
          </button> :
        null}
        {mail.raw.messages.map((m, i) =>
        <div className="mb-msg" key={i}>
            {mail.raw.messages.length > 1 ?
          <div className="mb-msg-h"><span className="mb-msg-from"><b>{m.from}</b></span><span className="mb-msg-date">{m.date}</span></div> :
          null}
            <div className="mb-msg-body">{renderBody(m.body)}</div>
          </div>
        )}
        {mail.attachments && mail.attachments.length ?
        <div className="mb-atts">
            <div className="mb-atts-h"><ClipIcon />{mail.attachments.length} 个附件</div>
            <div className="mb-atts-row">{mail.attachments.map((a, i) => <AttachChip a={a} key={i} />)}</div>
          </div> :
        null}
      </div>

      {replying ?
      <div className="mb-reply">
          <div className={`mb-reply-tag ${prefillReply ? "" : "plain"}`}>{prefillReply ? <React.Fragment><ReeveMark size={11} />Reeve 拟的回复已填入 · 改完即可发送</React.Fragment> : "回复 " + mail.from}</div>
          <div className="mb-reply-row">
            <textarea ref={taRef} className="mb-reply-input" value={reply} rows={4} onChange={(e) => setReply(e.target.value)} placeholder="写回复…" />
            <button className={`mb-reply-send ${reply.trim() ? "" : "dim"}`} aria-label="发送">↑</button>
          </div>
        </div> :

      <div className="mb-actionbar">
          <button className="mb-action" onClick={() => setReplying(true)}><ReplyIcon />回复</button>
          <button className="mb-action" onClick={() => setReplying(true)}><ReplyAllIcon />全部回复</button>
          <button className="mb-action"><ForwardIcon />转发</button>
        </div>
      }
    </div>);

}

function MailRow({ m, onOpen, onStar }) {
  return (
    <button className={`mb-row ${m.unread ? "unread" : ""}`} onClick={() => onOpen(m)}>
      <span className="mb-av" style={{ "--pav": MAIL_TINT[m.from] || "var(--ink-3)" }}>{m.from.slice(0, 1)}</span>
      <span className="mb-tx">
        <span className="mb-top"><span className="mb-from">{m.to ? "发给 " + m.to : m.from}</span><span className="mb-time">{m.time}</span></span>
        <span className="mb-subjrow"><span className="mb-subj">{m.subject}</span>{m.attachments && m.attachments.length ? <span className="mb-clip"><ClipIcon /></span> : null}</span>
        <span className="mb-prev">{m.preview}</span>
        {m.labels && m.labels.length ? <span className="mb-labels">{m.labels.map((l, i) => <span className="mb-label sm" key={i}>{l}</span>)}</span> : null}
      </span>
      <span className="mb-side">
        <span className={`mb-star ${m.star ? "on" : ""}`} onClick={(e) => {e.stopPropagation();onStar(m.id);}} role="button" aria-label="星标"><StarIcon filled={m.star} /></span>
        {m.unread ? <span className="mb-dot" /> : null}
      </span>
    </button>);

}

function MailCompose({ onClose }) {
  return (
    <div className="mb-compose">
      <div className="mb-compose-top">
        <button className="mb-compose-x" onClick={onClose} aria-label="返回"><ArrowLeftIcon /></button>
        <div className="mb-compose-actions">
          <button className="mb-compose-ic" aria-label="添加附件"><AttachIcon /></button>
          <button className="mb-compose-send" aria-label="发送"><SendIcon /></button>
          <button className="mb-compose-ic" aria-label="更多选项"><DotsIcon /></button>
        </div>
      </div>
      <div className="mb-compose-body">
        <div className="mb-cfield"><span className="mb-cf-k">发件人</span><span className="mb-cf-from">demarco@gmail.com</span></div>
        <div className="mb-cfield"><span className="mb-cf-k">收件人</span><input className="mb-cf-in" placeholder="" /><span className="mb-cf-cc" aria-hidden="true">⌄</span></div>
        <div className="mb-cfield"><span className="mb-cf-k">主题</span><input className="mb-cf-in" placeholder="" /></div>
        <textarea className="mb-cf-body" placeholder="撰写邮件" />
      </div>
    </div>);

}

function MailboxScreen({ onOpenChat, initialOpen, prefillReply, onConsumeOpen, onMenu, onMe }) {
  const data = mailData();
  const all = [...data.inbox, ...data.sent, ...data.drafts];
  const [folder, setFolder] = React.useState("inbox");
  const [open, setOpen] = React.useState(initialOpen || null);
  const [compose, setCompose] = React.useState(false);
  const [stars, setStars] = React.useState(() => {const m = {};all.forEach((x) => {m[x.id] = !!x.star;});return m;});
  const toggleStar = (id) => setStars((s) => ({ ...s, [id]: !s[id] }));
  React.useEffect(() => {if (initialOpen) setOpen(initialOpen);}, [initialOpen]);

  if (open) {
    const m = all.find((x) => x.id === open) || data.inbox.find((x) => x.cid === open);
    if (m) {
      const pre = open === initialOpen || m.cid === initialOpen ? prefillReply : "";
      return <MailDetail key={m.id} mail={m} prefillReply={pre} star={stars[m.id]} onStar={() => toggleStar(m.id)}
      onBack={() => {setOpen(null);onConsumeOpen && onConsumeOpen();}}
      onOpenChat={(cid) => {onConsumeOpen && onConsumeOpen();onOpenChat(cid);}} />;
    }
  }

  const list = folder === "starred" ? all.filter((m) => stars[m.id]) : data[folder] || [];
  const counts = {
    inbox: data.inbox.filter((m) => m.unread).length,
    starred: all.filter((m) => stars[m.id]).length,
    sent: data.sent.length, drafts: data.drafts.length
  };

  return (
    <div className="im-tabscreen">
      <IMTopBar title="邮箱" onMenu={onMenu} onMe={onMe} />
      <div className="im-search-wrap"><IMSearch placeholder="在邮件里搜…" /></div>
      <div className="mb-folders">
        {MB_FOLDERS.map((f) =>
        <button key={f.id} className={`mb-folder ${folder === f.id ? "on" : ""}`} onClick={() => setFolder(f.id)}>
            {f.t}{counts[f.id] ? <span className="mb-folder-n">{counts[f.id]}</span> : null}
          </button>
        )}
      </div>
      <div className="mb-list">
        {list.length ? list.map((m) =>
        <MailRow key={m.id} m={{ ...m, star: stars[m.id] }} onOpen={(mm) => setOpen(mm.id)} onStar={toggleStar} />
        ) : <div className="mb-empty">这个文件夹是空的</div>}
      </div>
      <button className="mb-fab" onClick={() => setCompose(true)} aria-label="写邮件"><IconCompose /></button>
      {compose ? <MailCompose onClose={() => setCompose(false)} /> : null}
    </div>);

}

/* ============================================================
   文档库 — a STRUCTURED knowledge base Reeve maintains.
   客户档案: per-client dossiers (Reeve's read + follow-up timeline).
   保险档案: products / brochures / forms, each with Reeve's summary.
   All grounded in the real corpus 6a222302 (Demarco / Sherrell / Marcus).
   Mobile-optimized: home → category list → detail, progressive disclosure.
   ============================================================ */
function FileIcon({ ext }) {
  return <span className={`fl-ic ext-${ext}`}>{ext.toUpperCase()}</span>;
}

function kbCollections() {
  return [
  { id: "insurance", t: "保险知识库", sub: "Reeve 的领域知识 · 方案条款、产品手册、术语", owner: "Reeve 维护", ic: <IconBook /> },
  { id: "mine", t: "我的资料", sub: "你存进来的参考文档 · Reeve 必要时检索", owner: "你维护", ic: <IconUser /> }];

}

/* Stable reference documents. Reeve retrieves on demand; `retrievable` is the
   user-managed switch for whether Reeve may read each one. */
function kbDocs() {
  return [
  { id: "uhc", col: "insurance", name: "UnitedHealthcare Choice Plus PPO", ext: "pdf", kind: "方案条款",
    meta: "计划年度文件 · 24 页 · 1.2 MB", retrievable: true, hits: 6, lastUse: "3/3 报价给 Sherrell · 引用「网络与接受度」",
    summary: "全国性 PPO 方案。无 copay、$0 起付、$3,000 自付上限；medically-underwritten。网络广、接受度高——你的首选推荐项。",
    sections: [
    { h: "保障范围", b: "住院 / 门诊 / 处方全覆盖；预防性检查 100% 报销。" },
    { h: "自付结构", b: "Copay 无 · 起付额 $0 · 年度自付上限 $3,000。" },
    { h: "网络", b: "Choice Plus PPO 网络，全国接受度高；可看网络外，自付更高。" },
    { h: "附加险", b: "牙科 / 视力为独立附加，见对应手册。" }]
  },
  { id: "multiplan", col: "insurance", name: "Multiplan PHCS PPO", ext: "pdf", kind: "方案条款",
    meta: "计划年度文件 · 19 页 · 0.9 MB", retrievable: true, hits: 4, lastUse: "3/3 报价作对比项",
    summary: "与 UHC 保障几乎一致，差别只在网络（PHCS）。无 copay、$0 起付。作为对比项供客户选网络。",
    sections: [
    { h: "保障范围", b: "与 UHC 接近，主险全覆盖。" },
    { h: "网络", b: "PHCS PPO 网络，覆盖面与 UHC 略有差异。" },
    { h: "自付结构", b: "Copay 无 · 起付额 $0。" }]
  },
  { id: "dental", col: "insurance", name: "牙科附加险手册", ext: "pdf", kind: "产品手册",
    meta: "产品手册 · 0.4 MB", retrievable: true, hits: 3, lastUse: "随报价附给客户",
    summary: "牙科附加险产品说明。客户常在要报价时一并问起——已设为标准报价附件。",
    sections: [{ h: "承保", b: "洗牙 / 补牙 / 基础正畸，年度上限见条款。" }] },
  { id: "vision", col: "insurance", name: "视力附加险手册", ext: "pdf", kind: "产品手册",
    meta: "产品手册 · 0.4 MB", retrievable: true, hits: 2, lastUse: "随报价附给客户",
    summary: "视力附加险产品说明，常与牙科一并需要。",
    sections: [{ h: "承保", b: "验光 / 镜架 / 镜片年度补贴。" }] },
  { id: "terms", col: "insurance", name: "健康险术语表", ext: "note", kind: "术语",
    meta: "对齐口径用 · 持续维护", retrievable: true, hits: 9, lastUse: "拟报价措辞时检索",
    summary: "Reeve 写报价、向客户解释时对齐口径用的术语定义。",
    glossary: [
    { t: "PPO", d: "优选医疗机构网络，可不经转诊看专科；网络外仍可报销，自付更高。" },
    { t: "Copay", d: "每次就诊的固定自付额。本方案为「无」。" },
    { t: "Deductible 起付额", d: "保险开始报销前你需先自付的金额。本方案 $0。" },
    { t: "Out-of-pocket max 自付上限", d: "一年内你最多自付的封顶额，达到后保险全付。" },
    { t: "Medically underwritten", d: "按健康状况核保定价，非保证承保。" }]
  },
  { id: "policydoc", col: "mine", name: "Policy #B6006396 — Bond / Cancellation", ext: "pdf", kind: "保单文档",
    meta: "商业险 · Aversa & Linn PC · 取消生效 1/22/26", retrievable: true, hits: 3, lastUse: "追 Joe 时引用截止日与 Bond 条款",
    summary: "Joe Comerford 的商业险保单要点。Reeve 跟进催单据时据此核对截止日与 Bond 是否仍需。",
    sections: [
    { h: "保单号", b: "#B6006396 · 商业责任 + Bond。" },
    { h: "取消生效", b: "1/22/26 —— 单据/决定须在此前到位，否则取消生效。" },
    { h: "待办", b: "确认 Bond 是否仍需；已追 4/29、5/14、5/22 三次未回。" }]
  },
  { id: "renewaldoc", col: "mine", name: "续约方案对比 — Adam", ext: "pdf", kind: "参考文档",
    meta: "续约挽留 · 昨天 · 0.4 MB", retrievable: true, hits: 1, lastUse: "约通话挽留时附给客户",
    summary: "留下 vs 换一家的成本/条款对比。Reeve 在挽留草稿里据此对齐 Adam 真正在意的点。",
    sections: [
    { h: "客户顾虑", b: "需求当初没说清；换过来要改流程、加成本。" },
    { h: "留下的理由", b: "成本与条款对比，凸显维持现状的优势。" },
    { h: "下一步", b: "约 15 分钟通话 + 发日历邀请，先对齐需求。" }]
  }];

}

/* ---- internal nav header ---- */
function KbTop({ title, onBack }) {
  return (
    <div className="kb-top">
      <button className="kb-back" onClick={onBack} aria-label="返回"><span className="kb-chev-l">‹</span>文档库</button>
      <span className="kb-top-t">{title}</span>
      <span className="kb-top-sp" />
    </div>);

}

/* ---- user-managed retrieve switch ---- */
function KbSwitch({ on, onToggle }) {
  return <button className={`kb-sw ${on ? "on" : ""}`} role="switch" aria-checked={on} onClick={onToggle} aria-label="允许 Reeve 检索"><span className="kb-sw-knob" /></button>;
}

/* ---- level 2: documents in a collection ---- */
function KbDocList({ title, docs, retr, onBack, onOpen }) {
  return (
    <div className="im-tabscreen">
      <KbTop title={title} onBack={onBack} />
      <div className="kb-scroll">
        <div className="kb-doclist big">
          {docs.map((d) =>
          <button className="kb-docrow lg" key={d.id} onClick={() => onOpen(d.id)}>
              <FileIcon ext={d.ext} />
              <span className="kb-docrow-tx">
                <span className="kb-docrow-name">{d.name}</span>
                <span className="kb-docrow-meta">{d.kind} · {d.meta}</span>
                <span className="kb-docrow-tags">
                  {retr[d.id] ?
                <span className="kb-retr on"><ReeveMark size={9} />Reeve 检索 {d.hits || 0} 次</span> :
                <span className="kb-retr off">不检索 · 仅你可见</span>}
                </span>
              </span>
              <span className="kb-chev">›</span>
            </button>
          )}
        </div>
      </div>
    </div>);

}

/* ---- a rendered document page (faux PDF first page) ---- */
function PdfPage({ d }) {
  return (
    <div className="kb-pdf-page">
      <div className="kb-pdf-band"><span className="kb-pdf-dot" />{d.kind}</div>
      <div className="kb-pdf-title">{d.name}</div>
      <div className="kb-pdf-sub">{d.meta}</div>
      <div className="kb-pdf-rule" />
      {(d.sections || []).map((s, i) =>
      <div className="kb-pdf-sec" key={i}>
          <div className="kb-pdf-h">{s.h}</div>
          <div className="kb-pdf-b">{s.b}</div>
        </div>
      )}
      <div className="kb-pdf-foot">Demarco Insurance Services · 第 1 页</div>
    </div>);

}

/* ---- level 3: the document reader (stable content + manage + retrieval log) ---- */
function KbDocView({ d, on, onToggle, onBack, title }) {
  const [zoom, setZoom] = React.useState(false);
  const isPdf = d.ext === "pdf";
  const isImg = d.ext === "img";
  return (
    <div className="im-tabscreen">
      <KbTop title={title} onBack={onBack} />
      <div className="kb-scroll">
        <div className="kb-doc-head">
          <FileIcon ext={d.ext} />
          <div className="kb-doc-hid">
            <span className="kb-doc-name">{d.name}</span>
            <span className="kb-doc-kind">{d.kind} · {d.meta}</span>
          </div>
        </div>

        {isPdf ?
        <React.Fragment>
            <div className="kb-sec-h">预览</div>
            <button className="kb-pdf-thumb" onClick={() => setZoom(true)}>
              <PdfPage d={d} />
              <span className="kb-pdf-fade" />
              <span className="kb-pdf-open"><svg viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M15 3h6v6M9 21H3v-6M21 3l-8 8M3 21l8-8" /></svg>点开查看全文</span>
            </button>
          </React.Fragment> :
        null}

        {isImg ?
        <React.Fragment>
            <div className="kb-sec-h">预览</div>
            <button className="kb-imgprev" onClick={() => setZoom(true)}>
              <span className="kb-imgprev-ic"><svg viewBox="0 0 24 24" width="30" height="30" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"><rect x="3" y="4" width="18" height="16" rx="2" /><circle cx="8.5" cy="9.5" r="1.6" /><path d="m4 18 5-5 4 4 3-3 4 4" /></svg></span>
              <span className="kb-imgprev-tx">{d.name}</span>
              <span className="kb-imgprev-sub">扫描件 · 点开查看</span>
            </button>
          </React.Fragment> :
        null}

        <div className="kb-manage">
          <div className="kb-manage-tx">
            <span className="kb-manage-t"><ReeveMark size={12} />允许 Reeve 检索</span>
            <span className="kb-manage-sub">{on ? "需要时 Reeve 会引用本文档" : "已关闭 · Reeve 不会读取"}</span>
          </div>
          <KbSwitch on={on} onToggle={onToggle} />
        </div>
        {on && d.lastUse ? <div className="kb-retr-log"><ReeveMark size={11} />上次检索：{d.lastUse}</div> : null}

        <div className="kb-insight">
          <span className="kb-insight-h"><ReeveMark size={13} />Reeve 摘要</span>
          <p className="kb-insight-tx">{d.summary}</p>
        </div>

        {d.sections && !isPdf ?
        <React.Fragment>
            <div className="kb-sec-h">文档内容</div>
            <div className="kb-doc-body">
              {d.sections.map((s, i) =>
            <div className="kb-docsec" key={i}><span className="kb-docsec-h">{s.h}</span><p className="kb-docsec-b">{s.b}</p></div>
            )}
            </div>
          </React.Fragment> :
        null}

        {d.glossary ?
        <React.Fragment>
            <div className="kb-sec-h">术语</div>
            <div className="kb-gloss">
              {d.glossary.map((g, i) =>
            <div className="kb-gloss-item" key={i}><span className="kb-gloss-t">{g.t}</span><span className="kb-gloss-d">{g.d}</span></div>
            )}
            </div>
          </React.Fragment> :
        null}
      </div>

      {zoom ?
      <div className="kb-viewer">
          <div className="kb-viewer-top">
            <span className="kb-viewer-name">{d.name}</span>
            <button className="kb-viewer-x" onClick={() => setZoom(false)} aria-label="关闭">✕</button>
          </div>
          <div className="kb-viewer-scroll">
            {isImg ?
          <div className="kb-viewer-img"><span className="kb-imgprev-ic"><svg viewBox="0 0 24 24" width="40" height="40" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"><rect x="3" y="4" width="18" height="16" rx="2" /><circle cx="8.5" cy="9.5" r="1.6" /><path d="m4 18 5-5 4 4 3-3 4 4" /></svg></span><span className="kb-imgprev-sub">{d.name} · 扫描件</span></div> :
          <PdfPage d={d} />}
          </div>
        </div> :
      null}
    </div>);

}

/* ---- level 1: knowledge-base home ---- */
function WikiScreen({ onMenu, onMe }) {
  const docs = kbDocs();
  const cols = kbCollections();
  const [nav, setNav] = React.useState({ l: "home" });
  const [retr, setRetr] = React.useState(() => Object.fromEntries(docs.map((d) => [d.id, d.retrievable])));
  const toggle = (id) => setRetr((m) => ({ ...m, [id]: !m[id] }));

  if (nav.l === "col") {
    const col = cols.find((c) => c.id === nav.id);
    return <KbDocList title={col.t} docs={docs.filter((d) => d.col === nav.id)} retr={retr}
    onBack={() => setNav({ l: "home" })} onOpen={(id) => setNav({ l: "doc", id, col: nav.id })} />;
  }
  if (nav.l === "doc") {
    const d = docs.find((x) => x.id === nav.id);
    const col = cols.find((c) => c.id === nav.col);
    return <KbDocView d={d} title={col ? col.t : "文档"} on={!!retr[d.id]} onToggle={() => toggle(d.id)} onBack={() => setNav({ l: "col", id: nav.col })} />;
  }

  const counts = (cid) => docs.filter((d) => d.col === cid).length;
  return (
    <div className="im-tabscreen">
      <IMTopBar title="文档库" onMenu={onMenu} onMe={onMe} />
      <div className="im-search-wrap"><IMSearch placeholder="搜文档、条款、术语…" /></div>
      <div className="kb-scroll">
        <div className="kb-cats">
          {cols.map((c) =>
          <button className="kb-cat" key={c.id} onClick={() => setNav({ l: "col", id: c.id })}>
              <span className="kb-cat-ic">{c.ic}</span>
              <span className="kb-cat-tx">
                <span className="kb-cat-t">{c.t}</span>
                <span className="kb-cat-sub">{c.sub}</span>
              </span>
              <span className="kb-cat-meta"><b>{counts(c.id)} 篇</b><span className="kb-chev">›</span></span>
            </button>
          )}
        </div>
      </div>
    </div>);

}

Object.assign(window, {
  IconChat, IconMail, IconBook, IconUser, IconSearch, IconCompose, IconCheck, IconMenu,
  StarIcon, ClipIcon, ArchiveIcon, TrashIcon, ReplyIcon, ReplyAllIcon, ForwardIcon,
  IMTopUser, IMTopBar, IMDrawer, HomeComposer, IMSearch, TabBar, MailboxScreen, mailData, MailDetail, MailRow, MailCompose, AttachChip, WikiScreen, FileIcon, boldParts,
  KbTop, KbSwitch, KbDocList, KbDocView, PdfPage
});