/* SER-OS — My Contacts
   ──────────────────────────────────────────────────────────────────
   Customer contact directory for funeral directors:
     - Search by name / phone / email / address
     - Tag-click filtering
     - Card and List view
     - Create contact via modal
     - Each contact shows linked-case count and can start a new case
   ────────────────────────────────────────────────────────────────── */

function ContactsScreen({
  contacts, cases,
  onOpenContact, onCreateContact, onEditContact, onDeleteContact,
  onStartCaseWith,
}) {
  const [view, setView] = React.useState("card");
  const [q, setQ] = React.useState("");
  const [activeTag, setActiveTag] = React.useState(null);

  const toggleTag = (tag) => setActiveTag((t) => t === tag ? null : tag);

  const filtered = contacts.filter((c) => {
    const matchesTag = !activeTag || (c.tags || []).includes(activeTag);
    if (!q) return matchesTag;
    const hay = `${c.name} ${c.phone || ""} ${c.mobile || ""} ${c.email || ""} ${c.address || ""} ${(c.tags||[]).join(" ")}`.toLowerCase();
    return hay.includes(q.toLowerCase()) && matchesTag;
  });

  return (
    <div style={{ padding: "32px 48px 64px", display: "flex", flexDirection: "column", gap: 24, maxWidth: 1440, margin: "0 auto", width: "100%", boxSizing: "border-box" }}>
      <header style={{ display: "flex", flexDirection: "column", gap: 4 }}>
        <h1 style={{ font: "500 28px/36px Inter", letterSpacing: "-0.5px", color: "#1F3A60", margin: 0 }}>My Contacts</h1>
        <p style={{ fontSize: 14, color: "#6B7A8F", letterSpacing: "-0.15px", margin: 0 }}>
          Customer contacts selected when starting a new arrangement.
        </p>
      </header>

      <div style={{ display: "flex", gap: 12, alignItems: "center", flexWrap: "wrap" }}>
        <Input
          icon={<SerIcons.Search size={18} />}
          placeholder="Search contacts by name, phone, email, or address..."
          value={q}
          onChange={(e) => setQ(e.target.value)}
          style={{ flex: "1 1 320px", minWidth: 240 }}
        />
        <div style={{ display: "inline-flex", border: "1px solid #E5E7EB", borderRadius: 8, overflow: "hidden", height: 48 }}>
          <ViewToggle isActive={view === "card"} onClick={() => setView("card")} icon={<SerIcons.Grid size={18} />} label="Card" />
          <ViewToggle isActive={view === "list"} onClick={() => setView("list")} icon={<SerIcons.List size={18} />} label="List" />
        </div>
        <Button variant="primary-navy" size="lg" icon={<SerIcons.Plus size={18} />} onClick={onCreateContact} style={{ height: 48 }}>
          Create Contact
        </Button>
      </div>

      {/* Active tag filter chip */}
      {activeTag && (
        <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
          <span style={{ fontSize: 13, color: "#6B7A8F" }}>Filtered by:</span>
          <button
            onClick={() => setActiveTag(null)}
            style={{
              all: "unset", cursor: "pointer",
              display: "inline-flex", alignItems: "center", gap: 6,
              padding: "4px 10px", borderRadius: 9999,
              fontSize: 12, fontWeight: 500,
              background: "#1F3A60", color: "#FFFFFF",
            }}
          >
            {activeTag}
            <SerIcons.Close size={10} />
          </button>
        </div>
      )}

      {filtered.length === 0 ? (
        <ContactsEmptyState
          hasSearch={!!q || !!activeTag}
          onCreate={onCreateContact}
          onClear={() => { setQ(""); setActiveTag(null); }}
        />
      ) : view === "card" ? (
        <div style={{ display: "grid", gridTemplateColumns: "repeat(3, minmax(0, 1fr))", gap: 16 }}>
          {filtered.map(c => (
            <ContactCard
              key={c.id}
              contact={c}
              cases={cases}
              onView={() => onOpenContact && onOpenContact(c.id)}
              onStartCase={() => onStartCaseWith && onStartCaseWith(c)}
              onTagClick={toggleTag}
              activeTag={activeTag}
            />
          ))}
        </div>
      ) : (
        <ContactListView
          contacts={filtered}
          cases={cases}
          onView={(c) => onOpenContact && onOpenContact(c.id)}
          onStartCase={(c) => onStartCaseWith && onStartCaseWith(c)}
        />
      )}
    </div>
  );
}

/* ───────────────────── Contact Card ───────────────────── */
function ContactCard({ contact, cases, onView, onStartCase, onTagClick, activeTag }) {
  const c = contact;
  const linked = getLinkedCasesForContact(c, cases);
  const updatedDisplay = c.updatedAt ? relativeTime(c.updatedAt) : "—";
  const addressLines = splitAddress(c.address);

  return (
    <article style={{
      background: "#FFFFFF", border: "1px solid #E5E7EB", borderRadius: 12,
      padding: 20, display: "flex", flexDirection: "column", gap: 16,
      height: "100%", boxSizing: "border-box",
    }}>

      {/* Top: Avatar + Name */}
      <header style={{ display: "flex", alignItems: "center", gap: 12 }}>
        <ContactAvatar contact={c} size={44} />
        <h2 style={{
          font: "500 17px/22px Inter", letterSpacing: "-0.35px", color: "#1F3A60", margin: 0,
          overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap",
        }}>
          {c.name}
        </h2>
      </header>

      {/* Middle: Phone, email, address — always rendered (–  when empty)
          so every card keeps the same three-row height and the footer
          stays pinned to a consistent baseline. */}
      <div style={{ display: "flex", flexDirection: "column", gap: 6, fontSize: 13, letterSpacing: "-0.15px" }}>
        <div style={{ display: "inline-flex", alignItems: "center", gap: 8 }}>
          <SerIcons.Phone size={13} color="#9CA8B8" />
          <span style={{ color: (c.mobile || c.phone) ? "#574F40" : "#C8D0DA" }}>
            {(c.mobile || c.phone) ? fmtPhone(c.mobile || c.phone) : "–"}
          </span>
        </div>
        <div style={{ display: "inline-flex", alignItems: "center", gap: 8 }}>
          <SerIcons.Mail size={13} color="#9CA8B8" />
          <span style={{ color: c.email ? "#574F40" : "#C8D0DA", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
            {c.email || "–"}
          </span>
        </div>
        <div style={{ display: "flex", alignItems: "flex-start", gap: 8 }}>
          <SerIcons.MapPin size={13} color="#9CA8B8" style={{ marginTop: 2, flexShrink: 0 }} />
          <span style={{ color: addressLines.length > 0 ? "#6B7A8F" : "#C8D0DA", lineHeight: "18px" }}>
            {addressLines.length > 0
              ? addressLines.slice(0, 2).join(", ") + (addressLines.length > 2 ? "…" : "")
              : "–"}
          </span>
        </div>
      </div>

      {/* Tags — always rendered (–  when none) to hold position */}
      <div style={{ display: "flex", flexWrap: "wrap", gap: 5, minHeight: 24, alignItems: "center" }}>
        {c.tags && c.tags.length > 0 ? (
          c.tags.map(t => (
            <TagChip key={t} active={activeTag === t} onClick={() => onTagClick && onTagClick(t)}>
              {t}
            </TagChip>
          ))
        ) : (
          <span style={{ fontSize: 13, color: "#C8D0DA", letterSpacing: "-0.15px" }}>–</span>
        )}
      </div>

      {/* Footer: meta + dual CTAs — pinned to the card bottom via marginTop:auto */}
      <div style={{ marginTop: "auto", borderTop: "1px solid #F0F1F4", paddingTop: 14, display: "flex", flexDirection: "column", gap: 10 }}>
        <span style={{ fontSize: 12, color: "#9CA8B8", letterSpacing: "-0.1px" }}>
          {linked.length === 0 ? "No linked cases" :
            linked.length === 1 ? "1 linked case" :
            `${linked.length} linked cases`}
          <span style={{ color: "#D1D5DB", margin: "0 5px" }}>·</span>
          {updatedDisplay}
        </span>
        <div style={{ display: "flex", gap: 8 }}>
          <Button
            variant="secondary" size="sm"
            onClick={onView}
            style={{ flex: 1 }}
          >
            View Details
          </Button>
          <Button
            variant="primary-navy" size="sm"
            iconRight={<SerIcons.Arrow size={14} />}
            onClick={onStartCase}
            style={{ flex: 1, whiteSpace: "nowrap" }}
          >
            Start Case
          </Button>
        </div>
      </div>
    </article>
  );
}

/* ───────────────────── List view ───────────────────── */
function ContactListView({ contacts, cases, onView, onStartCase }) {
  return (
    <div style={{ background: "#FFFFFF", border: "1px solid #E5E7EB", borderRadius: 12, overflow: "hidden" }}>
      <div style={{ display: "grid", gridTemplateColumns: "2fr 1.6fr 1.4fr 0.7fr 200px", padding: "12px 20px", borderBottom: "1px solid #E5E7EB", background: "#FAFAFA", fontSize: 12, color: "#6B7A8F", fontWeight: 500, textTransform: "uppercase", letterSpacing: "0.04em" }}>
        <span>Name</span><span>Phone / Email</span><span>Address</span><span>Cases</span><span></span>
      </div>
      {contacts.map((c, i) => {
        const linked = getLinkedCasesForContact(c, cases);
        return (
          <div key={c.id} style={{ display: "grid", gridTemplateColumns: "2fr 1.6fr 1.4fr 0.7fr 200px", padding: "14px 20px", borderBottom: i === contacts.length - 1 ? "none" : "1px solid #F0F1F4", alignItems: "center", fontSize: 14, color: "#1F3A60", letterSpacing: "-0.15px" }}>
            <button
              onClick={() => onView(c)}
              style={{ all: "unset", cursor: "pointer", display: "flex", alignItems: "center", gap: 12, minWidth: 0 }}
              aria-label={`View details for ${c.name}`}
            >
              <ContactAvatar contact={c} size={36} />
              <span style={{ fontWeight: 500, color: "#1F3A60", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
                {c.name}
              </span>
            </button>
            <div style={{ display: "flex", flexDirection: "column", fontSize: 13, color: "#574F40", overflow: "hidden" }}>
              <span style={{ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{fmtPhone(c.mobile || c.phone) || "—"}</span>
              {c.email && <span style={{ fontSize: 12, color: "#9CA8B8", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{c.email}</span>}
            </div>
            <span style={{ fontSize: 13, color: "#9CA8B8", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
              {splitAddress(c.address)[0] || "—"}
            </span>
            <span style={{ fontSize: 13, color: linked.length ? "#1F3A60" : "#9CA8B8", fontWeight: linked.length ? 500 : 400 }}>
              {linked.length === 0 ? "—" : `${linked.length}`}
            </span>
            <div style={{ display: "inline-flex", justifySelf: "end", gap: 6, alignItems: "center" }}>
              <Button variant="secondary" size="sm" onClick={() => onView(c)}>View Details</Button>
              <Button variant="primary-navy" size="sm" onClick={() => onStartCase(c)}>Start Case</Button>
            </div>
          </div>
        );
      })}
    </div>
  );
}

/* ───────────────────── Empty state ───────────────────── */
function ContactsEmptyState({ hasSearch, onCreate, onClear }) {
  return (
    <div style={{
      padding: "72px 32px", textAlign: "center",
      border: "1px dashed #E5E7EB", borderRadius: 12,
      background: "#FFFFFF",
      display: "flex", flexDirection: "column", alignItems: "center", gap: 12,
    }}>
      <h3 style={{ font: "500 18px/26px Inter", color: "#1F3A60", margin: 0, letterSpacing: "-0.3px" }}>
        {hasSearch ? "No contacts match your filters" : "No contacts yet"}
      </h3>
      <p style={{ fontSize: 14, color: "#6B7A8F", margin: 0, letterSpacing: "-0.15px", maxWidth: 380 }}>
        {hasSearch
          ? "Try a different name, phone number, or clear the tag filter."
          : "Add customer contacts here. They are selectable when starting a new arrangement."}
      </p>
      <div style={{ marginTop: 8 }}>
        {hasSearch ? (
          <Button variant="secondary" size="md" icon={<SerIcons.Close size={14} />} onClick={onClear}>Clear Filters</Button>
        ) : (
          <Button variant="primary-navy" size="md" icon={<SerIcons.Plus size={18} />} onClick={onCreate}>Create Contact</Button>
        )}
      </div>
    </div>
  );
}

/* ───────────────────── Create contact form ───────────────────── */
function ContactFormModal({ open, contact, onClose, onSave }) {
  /* Address is captured as discrete Odoo res.partner fields
     (street, street2, city, zip, country) and composed into a single
     `address` string on save so the rest of the app keeps working. */
  const blank = {
    type: "individual", category: "family",
    name: "", phone: "", mobile: "", email: "",
    street: "", street2: "", city: "", zip: "", country: "Singapore",
    address: "",
    tags: [], notes: "",
  };
  const [draft, setDraft] = React.useState(blank);
  const [tagDraft, setTagDraft] = React.useState("");

  React.useEffect(() => {
    if (open) {
      if (contact) {
        // Pre-fill structured address fields; fall back to splitting an
        // existing flat `address` string when the contact predates them.
        let seed = { ...blank, ...contact, tags: [...(contact.tags || [])] };
        const hasStructured = contact.street || contact.street2 || contact.zip || contact.city;
        if (!hasStructured && contact.address) {
          const parts = splitAddress(contact.address);
          seed.street  = parts[0] || "";
          seed.street2 = parts[1] || "";
          // Last part often "Singapore 560234" or "560234"
          const tail = parts.slice(2).join(" ");
          const zipMatch = tail.match(/\b(\d{5,6})\b/);
          seed.zip = zipMatch ? zipMatch[1] : "";
          seed.country = /singapore/i.test(tail) ? "Singapore" : (seed.country || "Singapore");
        }
        setDraft(seed);
      } else {
        setDraft(blank);
      }
      setTagDraft("");
    }
  }, [open, contact]); // eslint-disable-line react-hooks/exhaustive-deps

  if (!open) return null;

  const set = (patch) => setDraft((d) => ({ ...d, ...patch }));
  const canSave = draft.name.trim() && (draft.phone.trim() || draft.mobile.trim());

  // Compose the structured address fields into a single display string,
  // then hand the full draft (structured + composed) to onSave.
  const composeAddress = (d) => {
    return [d.street, d.street2, [d.zip, d.country].filter(Boolean).join(" ")]
      .map((s) => (s || "").trim())
      .filter(Boolean)
      .join("\n");
  };
  const handleSave = () => {
    if (!canSave) return;
    onSave({ ...draft, type: "individual", address: composeAddress(draft) });
  };

  const addTag = () => {
    const t = tagDraft.trim();
    if (!t || draft.tags.includes(t)) { setTagDraft(""); return; }
    set({ tags: [...draft.tags, t] });
    setTagDraft("");
  };
  const removeTag = (t) => set({ tags: draft.tags.filter((x) => x !== t) });

  return (
    <Modal
      open
      onClose={onClose}
      title="Create Contact"
      secondary={{ label: "Cancel", onClick: onClose }}
      primary={{ label: "Create Contact", onClick: handleSave, disabled: !canSave }}
      wide
    >
      {/* ── Contact Information ─────────────────────────────── */}
      <CCFieldGroup>
        <CCField label="Full Name" required>
          <Input value={draft.name} onChange={(e) => set({ name: e.target.value })}
            placeholder="Enter full name" />
        </CCField>

        {/* Phone + Mobile side by side — each gets full column width so the
            country code (88px) + number stay on one row, never wrapping. */}
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 14 }}>
          <CCField label="Phone" required>
            <PhoneInput value={draft.phone} onChange={(e) => set({ phone: e.target.value })} placeholder="81234567" />
          </CCField>
          <CCField label="Mobile">
            <PhoneInput value={draft.mobile} onChange={(e) => set({ mobile: e.target.value })} placeholder="81234567" />
          </CCField>
        </div>

        <CCField label="Email">
          <Input value={draft.email} onChange={(e) => set({ email: e.target.value })} placeholder="name@email.com" />
        </CCField>
      </CCFieldGroup>

      {/* ── Address — single label, placeholder-only inputs (Odoo style) ── */}
      <CCFieldGroup>
        <CCField label="Address">
          <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
            <Input value={draft.street} onChange={(e) => set({ street: e.target.value })} placeholder="Street" />
            <Input value={draft.street2} onChange={(e) => set({ street2: e.target.value })} placeholder="Street 2" />
            <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
              <Input value={draft.zip} onChange={(e) => set({ zip: e.target.value })} placeholder="Postal Code" />
              <Input value={draft.city} onChange={(e) => set({ city: e.target.value })} placeholder="City" />
            </div>
            <Input value={draft.country} onChange={(e) => set({ country: e.target.value })} placeholder="Country" />
          </div>
        </CCField>
      </CCFieldGroup>

      {/* ── Additional Information ──────────────────────────── */}
      <CCFieldGroup>
        <CCField label="Tags">
          <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
            <div style={{ display: "flex", gap: 8 }}>
              <Input
                value={tagDraft}
                onChange={(e) => setTagDraft(e.target.value)}
                placeholder="Add a tag and press Enter"
                style={{ flex: 1 }}
              />
              <Button variant="secondary" size="md" onClick={addTag}>Add</Button>
            </div>
            {draft.tags.length > 0 && (
              <div style={{ display: "flex", flexWrap: "wrap", gap: 6 }}>
                {draft.tags.map(t => <TagChip key={t} onRemove={() => removeTag(t)}>{t}</TagChip>)}
              </div>
            )}
          </div>
        </CCField>

        <CCField label="Notes">
          <Textarea value={draft.notes} onChange={(e) => set({ notes: e.target.value })}
            placeholder="Internal notes about this contact" rows={3} />
        </CCField>
      </CCFieldGroup>

      <p style={{ fontSize: 12, color: "#9CA8B8", margin: 0, letterSpacing: "-0.1px" }}>
        New contact will sync to Odoo (res.partner create).
      </p>
    </Modal>
  );
}

/* ── Modal field group — vertical stack with comfortable inner spacing.
   Modal body already separates direct children by 18px, so groups read
   as distinct clusters without heavy dividers or uppercase headers. */
function CCFieldGroup({ children }) {
  return <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>{children}</div>;
}

/* ── Label-above field. Subtle secondary label, input is the focus. */
function CCField({ label, required, children }) {
  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
      <span style={{
        fontSize: 13, fontWeight: 500, color: "#6B7A8F", letterSpacing: "-0.1px",
      }}>
        {label}
        {required && <span style={{ color: "#8F4A45", marginLeft: 3 }}>*</span>}
      </span>
      {children}
    </div>
  );
}

/* Lightweight segmented control used inside the form */
function SegmentedControl({ options, value, onChange }) {
  return (
    <div style={{
      display: "inline-flex", border: "1px solid #E5E7EB", borderRadius: 8,
      padding: 4, background: "#FAFAFA", gap: 4,
    }}>
      {options.map((opt) => {
        const active = value === opt.id;
        return (
          <button
            key={opt.id}
            onClick={() => onChange(opt.id)}
            style={{
              all: "unset", cursor: "pointer",
              padding: "8px 14px", borderRadius: 6,
              fontSize: 13, fontWeight: 500, letterSpacing: "-0.15px",
              color: active ? "#FFFFFF" : "#574F40",
              background: active ? "#574F40" : "transparent",
              display: "inline-flex", alignItems: "center", gap: 6,
              transition: "background 140ms ease, color 140ms ease",
            }}
          >
            {opt.icon}{opt.label}
          </button>
        );
      })}
    </div>
  );
}

/* ───────────────────── Contact Details Modal ─────────────────────
   Odoo-style read-only contact view. Opens from My Contacts cards,
   My Cases contact names, and any other contact link in the app.
   ────────────────────────────────────────────────────────────────── */
function ContactDetailsModal({ open, contact, cases, contactsById, onClose, onEdit, onOpenCase, onStartCase }) {
  if (!open || !contact) return null;

  const linked = getLinkedCasesForContact(contact, cases || SEED_CASES).map((c) => {
    const live = c.contactId && contactsById ? contactsById[c.contactId] : null;
    return {
      ...c,
      contact: live?.name  || c.contact || "—",
      phone:   live?.mobile || live?.phone || c.phone || "",
    };
  });

  const phone  = contact.mobile || contact.phone;
  const addressLines = splitAddress(contact.address);

  return (
    <Modal
      open
      wide
      onClose={onClose}
      title="Contact Details"
      secondary={{ label: "Close", onClick: onClose }}
      primary={{
        label: "Start New Case",
        icon: <SerIcons.Plus size={16} />,
        iconRight: false,
        onClick: onStartCase,
      }}
    >
      {/* ── Identity ──────────────────────────────────────────
           Avatar + name + inline contact methods (mobile / home / email).
           Per user feedback: no role pill, contact info lives under the
           name as a compact information strip. */}
      <div style={{ display: "flex", alignItems: "flex-start", gap: 18, paddingBottom: 2 }}>
        <ContactAvatar contact={contact} size={64} />
        <div style={{ flex: 1, minWidth: 0 }}>
          <h3 style={{
            font: "500 24px/30px Inter", letterSpacing: "-0.5px", color: "#1F3A60",
            margin: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap",
          }}>
            {contact.name}
          </h3>
          {(phone || contact.email) && (
            <div style={{
              marginTop: 10, display: "flex", flexDirection: "column", gap: 6,
            }}>
              {phone && (
                <ContactInlineMethod
                  icon={<SerIcons.Phone size={13} color="#1F3A60" />}
                  label={contact.mobile ? "Mobile" : "Phone"}
                  value={fmtPhone(phone)}
                />
              )}
              {contact.phone && contact.mobile && contact.phone !== contact.mobile && (
                <ContactInlineMethod
                  icon={<SerIcons.Phone size={13} color="#9CA8B8" />}
                  label="Home"
                  value={fmtPhone(contact.phone)}
                  muted
                />
              )}
              {contact.email && (
                <ContactInlineMethod
                  icon={<SerIcons.Mail size={13} color="#1F3A60" />}
                  label="Email"
                  value={contact.email}
                />
              )}
            </div>
          )}
        </div>
      </div>

      {/* ── Address — plain icon + text, no card ───────────── */}
      {addressLines.length > 0 && (
        <div>
          <SectionLabel>Address</SectionLabel>
          <div style={{ display: "flex", alignItems: "flex-start", gap: 12 }}>
            <SerIcons.MapPin size={14} color="#9CA8B8" style={{ marginTop: 4, flexShrink: 0 }} />
            <div style={{ display: "flex", flexDirection: "column", gap: 1 }}>
              {addressLines.map((line, i) => (
                <span key={i} style={{
                  fontSize: 14, color: "#574F40", lineHeight: "22px", letterSpacing: "-0.15px",
                }}>
                  {line}
                </span>
              ))}
            </div>
          </div>
        </div>
      )}

      {/* ── Tags ────────────────────────────────────────────── */}
      {contact.tags && contact.tags.length > 0 && (
        <div>
          <SectionLabel>Tags</SectionLabel>
          <div style={{ display: "flex", flexWrap: "wrap", gap: 6 }}>
            {contact.tags.map(t => <TagChip key={t}>{t}</TagChip>)}
          </div>
        </div>
      )}

      {/* ── Notes — plain paragraph, no card ────────────── */}
      {contact.notes && (
        <div>
          <SectionLabel>Notes</SectionLabel>
          <p style={{
            fontSize: 14, lineHeight: "22px", color: "#574F40",
            margin: 0, letterSpacing: "-0.15px", whiteSpace: "pre-wrap",
          }}>
            {contact.notes}
          </p>
        </div>
      )}

      {/* ── Linked Cases — headerless, no outer card ───────
           Most contacts have only 1 case so the column-header row
           added more chrome than value. Rows now read as a simple
           list with subtle dividers. */}
      {linked.length > 0 && (
      <div>
        <SectionLabel>Linked Cases ({linked.length})</SectionLabel>
          <div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
            {linked.map((c, i) => {
              const rel = c.primaryRelationship || c.relationship;
              return (
              <button
                key={c.caseNumber}
                onClick={() => onOpenCase && onOpenCase(c)}
                style={{
                  all: "unset", cursor: "pointer",
                  display: "grid", gridTemplateColumns: "1fr 120px 100px 110px 70px",
                  alignItems: "center", gap: 12,
                  padding: "12px 14px", width: "100%", boxSizing: "border-box",
                  fontSize: 13, color: "#1F3A60", letterSpacing: "-0.15px",
                  background: "#F9FAFB",
                  border: "1px solid #F0F1F4",
                  borderRadius: 10,
                  transition: "background 120ms ease, border-color 120ms ease",
                }}
                onMouseEnter={(e) => {
                  e.currentTarget.style.background = "#F4F6F9";
                  e.currentTarget.style.borderColor = "#E5E9F0";
                }}
                onMouseLeave={(e) => {
                  e.currentTarget.style.background = "#F9FAFB";
                  e.currentTarget.style.borderColor = "#F0F1F4";
                }}
              >
                <div style={{ display: "flex", flexDirection: "column", minWidth: 0 }}>
                  <span style={{
                    fontWeight: 500, color: "#1F3A60",
                    overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap",
                  }}>{c.deceased}</span>
                  <span style={{
                    fontSize: 12, color: "#9CA8B8",
                    fontFeatureSettings: "'tnum'", letterSpacing: "0",
                  }}>
                    {c.caseNumber}
                  </span>
                </div>
                <span style={{
                  color: rel ? "#574F40" : "#C8D0DA", fontSize: 12,
                  overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap",
                }}>
                  {rel || "—"}
                </span>
                <span style={{ justifySelf: "start" }}>
                  <StatusPill status={c.status} compact />
                </span>
                <span style={{ color: "#9CA8B8", fontSize: 12 }}>
                  {c.updatedAt ? relativeTime(c.updatedAt) : "—"}
                </span>
                <div style={{
                  display: "inline-flex", alignItems: "center", gap: 4,
                  color: "#1F3A60", justifySelf: "end",
                }}>
                  <span style={{ fontSize: 12, fontWeight: 500 }}>Open</span>
                  <SerIcons.Arrow size={14} color="#1F3A60" />
                </div>
              </button>
              );
            })}
          </div>
        </div>
      )}

      {/* ── Odoo sync footnote — discreet chip ─────────────── */}
      <div style={{ display: "flex", justifyContent: "flex-end", marginTop: 2 }}>
        <span style={{
          display: "inline-flex", alignItems: "center", gap: 6,
          padding: "4px 10px", borderRadius: 6,
          background: "#F4F6F9",
          fontSize: 11, fontWeight: 500, color: "#9CA8B8",
          letterSpacing: "0.01em", fontFeatureSettings: "'tnum'",
        }}>
          <SerIcons.Link size={11} color="#9CA8B8" />
          Synced from Odoo · res.partner #{contact.id}
        </span>
      </div>
    </Modal>
  );
}

/* ──────────────────────────────────────────────────────────────────
   ContactInlineMethod — compact inline row used in the modal header.
   Icon + small label + value, lined up so phone/email read as a
   tidy info strip directly under the name.
   ────────────────────────────────────────────────────────────────── */
function ContactInlineMethod({ icon, label, value, muted }) {
  return (
    <div style={{
      display: "inline-flex", alignItems: "center", gap: 10, minWidth: 0,
    }}>
      <span style={{
        width: 22, height: 22, borderRadius: 6, flexShrink: 0,
        background: muted ? "#F4F6F9" : "#EEF2F7",
        display: "inline-flex", alignItems: "center", justifyContent: "center",
      }}>{icon}</span>
      <span style={{
        fontSize: 11, fontWeight: 600, color: "#9CA8B8",
        letterSpacing: "0.06em", textTransform: "uppercase",
        minWidth: 52, flexShrink: 0,
      }}>{label}</span>
      <span style={{
        fontSize: 14, color: muted ? "#9CA8B8" : "#1F3A60",
        fontWeight: 500, letterSpacing: "-0.15px",
        overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap",
        fontFeatureSettings: "'tnum'",
      }}>{value}</span>
    </div>
  );
}

/* ──────────────────────────────────────────────────────────────────
   ContactMethodTile — kept for any future grid usage. Not currently
   used in the modal (we moved to the inline strip under the name).
   ────────────────────────────────────────────────────────────────── */
function ContactMethodTile({ icon, label, value, muted }) {
  return (
    <div style={{
      display: "flex", alignItems: "center", gap: 12,
      padding: "10px 14px", borderRadius: 10,
      background: muted ? "#FAFBFC" : "#F4F6F9",
      border: "1px solid " + (muted ? "#F0F1F4" : "#E5E9F0"),
      minWidth: 0,
    }}>
      <span style={{
        width: 28, height: 28, borderRadius: 8, flexShrink: 0,
        background: "#FFFFFF", border: "1px solid #E5E9F0",
        display: "inline-flex", alignItems: "center", justifyContent: "center",
      }}>{icon}</span>
      <div style={{ display: "flex", flexDirection: "column", minWidth: 0 }}>
        <span style={{
          fontSize: 10, fontWeight: 600, color: "#9CA8B8",
          letterSpacing: "0.06em", textTransform: "uppercase",
        }}>{label}</span>
        <span style={{
          fontSize: 13, color: muted ? "#9CA8B8" : "#1F3A60",
          fontWeight: 500, letterSpacing: "-0.1px",
          overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap",
          fontFeatureSettings: "'tnum'",
        }}>{value}</span>
      </div>
    </div>
  );
}

/* SectionLabel — shared uppercase eyebrow used across Contact Details */
function SectionLabel({ children }) {
  return (
    <div style={{
      fontSize: 11, fontWeight: 600, color: "#9CA8B8",
      letterSpacing: "0.06em", textTransform: "uppercase",
      marginBottom: 8,
    }}>
      {children}
    </div>
  );
}

/* ───────────────────── Shared primitives ───────────────────── */

function ContactLine({ icon, value }) {
  return (
    <React.Fragment>
      <span style={{ display: "inline-flex", alignItems: "flex-start", paddingTop: 2, color: "#9CA8B8" }}>{icon}</span>
      <span style={{ color: "#574F40", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", minWidth: 0 }}>
        {value}
      </span>
    </React.Fragment>
  );
}

function ContactAvatar({ contact, size = 36 }) {
  const isCompany = contact.type === "company";
  return (
    <div style={{
      width: size, height: size, borderRadius: isCompany ? 8 : "50%",
      background: isCompany ? "var(--surface-chip, #EFE7D4)" : "rgba(31,58,96,0.08)",
      color: isCompany ? "#574F40" : "#1F3A60",
      display: "inline-flex", alignItems: "center", justifyContent: "center",
      fontWeight: 500, fontSize: Math.round(size * 0.36), letterSpacing: "-0.2px",
      border: isCompany ? "1px solid var(--border-subtle, #E5E7EB)" : "none",
      flexShrink: 0,
    }}>
      {isCompany ? <SerIcons.Building size={Math.round(size * 0.46)} /> : contactInitials(contact.name)}
    </div>
  );
}

function ContactTypePill({ contact }) {
  const label = contactCategoryLabel(contact);
  const isFamily = contact.category === "family";
  return (
    <span style={{
      display: "inline-flex", alignItems: "center",
      padding: "3px 10px", borderRadius: 9999,
      fontSize: 12, fontWeight: 500, letterSpacing: "-0.1px",
      whiteSpace: "nowrap",
      background: isFamily ? "rgba(31,58,96,0.06)" : "var(--surface-chip, #EFE7D4)",
      color: isFamily ? "#1F3A60" : "#574F40",
    }}>
      {label}
    </span>
  );
}

/* TagChip — no icon, optionally clickable (for tag filtering) */
function TagChip({ children, onClick, onRemove, active }) {
  const base = {
    display: "inline-flex", alignItems: "center", gap: 4,
    padding: "4px 10px", borderRadius: 9999,
    fontSize: 12, fontWeight: 500, letterSpacing: "-0.1px",
    border: "1px solid var(--border-subtle, #E5E7EB)",
    transition: "background 120ms ease, color 120ms ease",
  };
  const colorStyle = active
    ? { background: "#1F3A60", color: "#FFFFFF", borderColor: "#1F3A60" }
    : { background: "var(--surface-chip, #EFE7D4)", color: "#574F40" };

  if (onClick && !onRemove) {
    return (
      <button
        onClick={onClick}
        style={{ all: "unset", cursor: "pointer", ...base, ...colorStyle }}
      >
        {children}
      </button>
    );
  }

  return (
    <span style={{ ...base, ...colorStyle }}>
      {children}
      {onRemove && (
        <button onClick={onRemove} style={{
          all: "unset", cursor: "pointer", marginLeft: 2,
          width: 14, height: 14, display: "inline-flex", alignItems: "center", justifyContent: "center",
          color: active ? "rgba(255,255,255,0.7)" : "#6B7A8F",
        }} aria-label="Remove tag">
          <SerIcons.Close size={10} />
        </button>
      )}
    </span>
  );
}

/* Splits an address string into clean lines for structured display */
function splitAddress(addr) {
  if (!addr) return [];
  return addr.split(/[,\n]+/).map(s => s.trim()).filter(Boolean);
}

Object.assign(window, {
  ContactsScreen, ContactCard, ContactListView, ContactFormModal, ContactDetailsModal,
  ContactAvatar, ContactTypePill, TagChip, SegmentedControl, splitAddress,
});
