/* ============================================================
   Mesper CRM — SDR CRM (Phase 1). Firm-centric, mobile-first, EN/DE.
   Mounts at /crm (and /crm/*). Reuses global Icon/Button/Avatar/RichText etc.
   ============================================================ */

/* ---- i18n (EN/DE via switch, persisted) ---- */
const CRM_DICT = {
  en: {
    crm: "CRM", today: "Today", companies: "Companies", pipeline: "Pipeline",
    search: "Search companies…", newCompany: "New company", newContact: "New contact",
    name: "Name", website: "Website / domain", industry: "Industry", status: "Status", leadSource: "Lead source",
    save: "Save", cancel: "Cancel", create: "Create", add: "Add", edit: "Edit", remove: "Remove", delete: "Delete",
    contacts: "Contacts", activity: "Activity", notes: "Notes", tasks: "Tasks", deal: "Deal", nextStep: "Next step",
    addNextStep: "Add next step…", noContacts: "No contacts yet.", noActivity: "No activity yet.", noNotes: "No notes yet.", noTasks: "No open tasks.",
    logCall: "Log call", logEmail: "Log email", logLinkedin: "Log LinkedIn", logMeeting: "Log meeting",
    outcome: "Outcome", connected: "Connected", noAnswer: "No answer", voicemail: "Voicemail", meetingBooked: "Meeting booked", notInterested: "Not interested", callback: "Callback",
    addNote: "Write a note…", addTask: "Add follow-up…", due: "Due", call: "Call", email: "Email", general: "General",
    open: "Open", markDone: "Done", firstName: "First name", lastName: "Last name", role: "Role", phone: "Phone",
    primary: "Primary", dnc: "Do not contact", followUp: "Follow-up", noCompanies: "No companies yet — add your first.",
    quickAdd: "Quick add", value: "Value", stage: "Stage", won: "Won", lost: "Lost", createDeal: "Create deal", noDeal: "No deal yet.",
    overdue: "Overdue", dueToday: "Due today", upcoming: "Upcoming", allCaughtUp: "All caught up 🎉", back: "Back",
    statusNew: "New", statusWorking: "Working", statusQualified: "Qualified", statusCustomer: "Customer", statusLost: "Lost", statusDnc: "Do-not-contact",
    fetchLogo: "Fetch logo", freemailWarn: "Looks like a personal email — mark as non-business?",
    inDocs: "Mentioned in Docs",
    links: "Links", linkBoard: "Board", linkDocs: "Docs", asClient: "Add as customer (Board + Docs)", linkMissing: "Add missing link",
    reports: "Reports", settings: "Settings", sequences: "Sequences", templates: "Templates", more: "More", inbox: "Inbox",
    importCsv: "Import CSV", importLeads: "Import leads", mapColumns: "Map columns", pasteCsv: "Paste CSV (first row = headers) or choose a file",
    imported: "imported", skipped: "skipped (duplicate / on suppression list)", column: "Column", ignore: "— ignore —", startImport: "Start import", preview: "Preview",
    suppression: "Suppression list", suppressionDesc: "Hard block: addresses/domains here never receive emails, not even from sequences (checked fail-closed at send time).", addEmail: "Add email or domain…", reason: "Reason",
    impressum: "Email footer / Impressum", impressumDesc: "Attached automatically to every CRM email sent via Gmail (TMG §5 / GDPR). Supports {{firstName}}, {{company}}.", businessHours: "Sending window (local time)", businessHoursDesc: "Sequence emails are only sent within this window (recipient's local time is unknown – your time zone applies).", from: "From", to: "To",
    tracking: "Open / click tracking", trackingDesc: "Off by default (TDDDG §25). Only enable per-contact with a lawful basis. Applies as the default for the compose window.", trackingDefault: "Track new emails by default",
    gdpr: "Data & privacy", exportData: "Export data (Art. 15)", deleteCompany: "Delete company & all data (Art. 17)", deleteConfirm: "Permanently delete this company and every contact, note, activity, task, deal and email? This cannot be undone.",
    biBasis: "Lawful basis (berechtigtes Interesse)", biPlaceholder: "e.g. B2B, relevant service, existing business context…",
    compose: "Compose email", logEmailManual: "Log manually", sendViaGmail: "Send via Gmail", connectGmail: "Connect Google Workspace to send",  followUpOnly: "Email is follow-up only — calls are the primary motion.", useTemplate: "Template", noTemplate: "— blank —", trackThis: "Track opens/clicks", send: "Send", sent: "Sent",
    newTemplate: "New template", templateName: "Template name", subjectLine: "Subject", emailBody: "Body", category: "Category", saveTemplate: "Save template", noTemplates: "No templates yet.",
    newSequence: "New sequence", sequenceName: "Sequence name", steps: "Steps", addStep: "Add step", waitDays: "Wait (days)", stepEmail: "Email", stepCall: "Call task", stepLinkedin: "LinkedIn task", stepWait: "Wait", enroll: "Enroll", enrolled: "Enrolled", active: "Active", paused: "Paused", completed: "Completed", pause: "Pause", resume: "Resume", enrollments: "Enrollments", abTest: "A/B subject", variantB: "Variant B subject",
    repCalls: "Calls", repConnects: "Connects", repMeetings: "Meetings booked", repConnectRate: "Connect rate", repEmails: "Emails sent", repReplies: "Replies", repPipeline: "Open pipeline", repWon: "Won", repTemplatePerf: "Template performance", opened: "Opened", clicked: "Clicked", replied: "Replied", openRate: "Open %", replyRate: "Reply %", noData: "No data in this range.", range: "Range", r7: "7 days", r30: "30 days", r90: "90 days",
    integrations: "Integrations", notConnected: "Not connected", connected2: "Connected", setupRequired: "Owner setup required",
    tabOverview: "Overview", emails: "Emails", yesterday: "Yesterday", recentActivity: "Recent activity", viewAll: "View all",
    filterAll: "All", filterCalls: "Calls", filterMeetings: "Meetings", filterLinkedin: "LinkedIn",
    actEmailSent: "Email sent", actEmailReceived: "Email received", actCallLogged: "Call logged", actLinkedinLogged: "LinkedIn logged", actMeetingLogged: "Meeting logged", actNoteTitle: "Note",
    writeEmail: "Write email", gmailNotConnectedHint: "Gmail not connected – email will only be logged", noEmailsHist: "No emails yet. Write the first one – or log a past email via 'Log email' above.",
    needContactEmail: "Add a contact with an email address first.", templatePlaceholderHint: "{{firstName}}, {{company}} are replaced when sending", trackThisHint: "Only enable with a lawful basis (TDDDG §25).",
    openMailApp: "Open in mail app + log", logOnly: "Log only", outbound: "Outbound", inbound: "Inbound",
    suppressedMsg: "Contact is on the suppression list or has unsubscribed.", sendErrorGeneric: "Failed to send.", logged: "Logged.",
    templatesDesc: "Available in the compose window and in sequences. Placeholders: {{firstName}}, {{lastName}}, {{company}}.",
    gmailConnectedDesc: "Sends & tracks as", notSetUp: "Not set up", calNotSetupDesc: "Calendar sync is prepared but not yet enabled.", calConnectedDesc: "Syncs your calendar: meetings with CRM contacts appear on the company.", fromAccount: "From", slackDesc: "Posts new tasks & replies into the CRM channel.",
    gmailStep1: "Create a Google Cloud service account with domain-wide delegation for the Gmail scope.", gmailStep2: "Store the JSON key as the GOOGLE_SA_KEY secret in Supabase.", gmailStep3: "Set CRM_SDR_EMAIL.", gmailSetupDocsRef: "Full instructions: docs/CRM-GOOGLE-SETUP.md in the repo.",
  },
  de: {
    crm: "CRM", today: "Heute", companies: "Firmen", pipeline: "Pipeline",
    search: "Firmen suchen…", newCompany: "Neue Firma", newContact: "Neuer Kontakt",
    name: "Name", website: "Website / Domain", industry: "Branche", status: "Status", leadSource: "Lead-Quelle",
    save: "Speichern", cancel: "Abbrechen", create: "Anlegen", add: "Hinzufügen", edit: "Bearbeiten", remove: "Entfernen", delete: "Löschen",
    contacts: "Kontakte", activity: "Aktivität", notes: "Notizen", tasks: "Aufgaben", deal: "Deal", nextStep: "Nächster Schritt",
    addNextStep: "Nächsten Schritt eintragen…", noContacts: "Noch keine Kontakte.", noActivity: "Noch keine Aktivität.", noNotes: "Noch keine Notizen.", noTasks: "Keine offenen Aufgaben.",
    logCall: "Anruf loggen", logEmail: "E-Mail loggen", logLinkedin: "LinkedIn loggen", logMeeting: "Termin loggen",
    outcome: "Ergebnis", connected: "Erreicht", noAnswer: "Nicht erreicht", voicemail: "Mailbox", meetingBooked: "Termin vereinbart", notInterested: "Kein Interesse", callback: "Rückruf",
    addNote: "Notiz schreiben…", addTask: "Wiedervorlage hinzufügen…", due: "Fällig", call: "Anruf", email: "E-Mail", general: "Allgemein",
    open: "Offen", markDone: "Erledigt", firstName: "Vorname", lastName: "Nachname", role: "Rolle", phone: "Telefon",
    primary: "Primär", dnc: "Nicht kontaktieren", followUp: "Wiedervorlage", noCompanies: "Noch keine Firmen — leg die erste an.",
    quickAdd: "Schnell anlegen", value: "Wert", stage: "Phase", won: "Gewonnen", lost: "Verloren", createDeal: "Deal anlegen", noDeal: "Noch kein Deal.",
    overdue: "Überfällig", dueToday: "Heute fällig", upcoming: "Demnächst", allCaughtUp: "Alles erledigt 🎉", back: "Zurück",
    statusNew: "Neu", statusWorking: "In Bearbeitung", statusQualified: "Qualifiziert", statusCustomer: "Kunde", statusLost: "Verloren", statusDnc: "Nicht kontaktieren",
    fetchLogo: "Logo holen", freemailWarn: "Sieht nach privater E-Mail aus — als Nicht-Business markieren?",
    inDocs: "In Docs erwähnt",
    links: "Verknüpfungen", linkBoard: "Board", linkDocs: "Docs", asClient: "Als Kunde anlegen (Board + Docs)", linkMissing: "Fehlende Verknüpfung anlegen",
    reports: "Berichte", settings: "Einstellungen", sequences: "Sequenzen", templates: "Vorlagen", more: "Mehr", inbox: "Eingang",
    importCsv: "CSV importieren", importLeads: "Leads importieren", mapColumns: "Spalten zuordnen", pasteCsv: "CSV einfügen (erste Zeile = Überschriften) oder Datei wählen",
    imported: "importiert", skipped: "übersprungen (Duplikat / auf Sperrliste)", column: "Spalte", ignore: "— ignorieren —", startImport: "Import starten", preview: "Vorschau",
    suppression: "Sperrliste", suppressionDesc: "Harte Sperre: Adressen/Domains hier erhalten nie E-Mails, auch nicht aus Sequenzen (fail-closed geprüft beim Versand).", addEmail: "E-Mail oder Domain hinzufügen…", reason: "Grund",
    impressum: "E-Mail-Signatur / Impressum", impressumDesc: "Wird automatisch an jede über Gmail versendete CRM-E-Mail angehängt (TMG §5 / DSGVO). Unterstützt {{firstName}}, {{company}}.", businessHours: "Sendefenster (Ortszeit)", businessHoursDesc: "Sequenz-Mails werden nur innerhalb dieses Fensters verschickt (Ortszeit des Empfängers unbekannt – es gilt eure Zeit).", from: "Von", to: "Bis",
    tracking: "Öffnungs-/Klick-Tracking", trackingDesc: "Standardmäßig aus (TDDDG §25). Nur pro Kontakt mit Rechtsgrundlage aktivieren. Gilt als Voreinstellung für das Compose-Fenster.", trackingDefault: "Neue E-Mails standardmäßig tracken",
    gdpr: "Daten & Datenschutz", exportData: "Daten exportieren (Art. 15)", deleteCompany: "Firma & alle Daten löschen (Art. 17)", deleteConfirm: "Diese Firma und jeden Kontakt, Notiz, Aktivität, Aufgabe, Deal und E-Mail endgültig löschen? Nicht rückgängig zu machen.",
    biBasis: "Rechtsgrundlage (berechtigtes Interesse)", biPlaceholder: "z. B. B2B, passende Leistung, bestehender Geschäftskontext…",
    compose: "E-Mail verfassen", logEmailManual: "Manuell loggen", sendViaGmail: "Über Gmail senden", connectGmail: "Google Workspace verbinden zum Senden", followUpOnly: "E-Mail dient nur zum Nachfassen — Anrufe sind der Hauptkanal.", useTemplate: "Vorlage", noTemplate: "— leer —", trackThis: "Öffnungen/Klicks tracken", send: "Senden", sent: "Gesendet",
    newTemplate: "Neue Vorlage", templateName: "Vorlagenname", subjectLine: "Betreff", emailBody: "Text", category: "Kategorie", saveTemplate: "Vorlage speichern", noTemplates: "Noch keine Vorlagen.",
    newSequence: "Neue Sequenz", sequenceName: "Sequenzname", steps: "Schritte", addStep: "Schritt hinzufügen", waitDays: "Warten (Tage)", stepEmail: "E-Mail", stepCall: "Anruf-Aufgabe", stepLinkedin: "LinkedIn-Aufgabe", stepWait: "Warten", enroll: "Aufnehmen", enrolled: "Aufgenommen", active: "Aktiv", paused: "Pausiert", completed: "Abgeschlossen", pause: "Pausieren", resume: "Fortsetzen", enrollments: "Aufnahmen", abTest: "A/B-Betreff", variantB: "Variante-B-Betreff",
    repCalls: "Anrufe", repConnects: "Erreicht", repMeetings: "Termine vereinbart", repConnectRate: "Erreichbarkeit", repEmails: "E-Mails gesendet", repReplies: "Antworten", repPipeline: "Offene Pipeline", repWon: "Gewonnen", repTemplatePerf: "Vorlagen-Performance", opened: "Geöffnet", clicked: "Geklickt", replied: "Beantwortet", openRate: "Öffnung %", replyRate: "Antwort %", noData: "Keine Daten in diesem Zeitraum.", range: "Zeitraum", r7: "7 Tage", r30: "30 Tage", r90: "90 Tage",
    integrations: "Integrationen", notConnected: "Nicht verbunden", connected2: "Verbunden", setupRequired: "Einrichtung durch Owner nötig",
    tabOverview: "Übersicht", emails: "E-Mails", yesterday: "Gestern", recentActivity: "Letzte Aktivität", viewAll: "Alle anzeigen",
    filterAll: "Alle", filterCalls: "Anrufe", filterMeetings: "Termine", filterLinkedin: "LinkedIn",
    actEmailSent: "E-Mail gesendet", actEmailReceived: "E-Mail erhalten", actCallLogged: "Anruf geloggt", actLinkedinLogged: "LinkedIn geloggt", actMeetingLogged: "Termin geloggt", actNoteTitle: "Notiz",
    writeEmail: "E-Mail schreiben", gmailNotConnectedHint: "Gmail nicht verbunden – E-Mail wird nur geloggt", noEmailsHist: "Noch keine E-Mails. Schreib die erste – oder logge eine vergangene E-Mail über 'E-Mail loggen' oben.",
    needContactEmail: "Lege zuerst einen Kontakt mit E-Mail-Adresse an.", templatePlaceholderHint: "{{firstName}}, {{company}} werden beim Senden ersetzt", trackThisHint: "Nur mit Rechtsgrundlage aktivieren (TDDDG §25).",
    openMailApp: "In Mail-App öffnen + loggen", logOnly: "Nur loggen", outbound: "Ausgehend", inbound: "Eingehend",
    suppressedMsg: "Kontakt steht auf der Sperrliste oder hat abgemeldet.", sendErrorGeneric: "Senden fehlgeschlagen.", logged: "Geloggt.",
    templatesDesc: "Stehen im Compose-Fenster und in Sequenzen zur Verfügung. Platzhalter: {{firstName}}, {{lastName}}, {{company}}.",
    gmailConnectedDesc: "Sendet & trackt als", notSetUp: "Nicht eingerichtet", calNotSetupDesc: "Kalender-Sync ist vorbereitet, aber noch nicht aktiviert.", calConnectedDesc: "Synct deinen Kalender: Meetings mit CRM-Kontakten erscheinen beim Unternehmen.", fromAccount: "Von", slackDesc: "Postet neue Tasks & Antworten in den CRM-Channel.",
    gmailStep1: "Google-Cloud-Service-Account mit domainweiter Delegierung für den Gmail-Scope anlegen.", gmailStep2: "JSON-Key als Secret GOOGLE_SA_KEY in Supabase hinterlegen.", gmailStep3: "CRM_SDR_EMAIL setzen.", gmailSetupDocsRef: "Ausführliche Anleitung: docs/CRM-GOOGLE-SETUP.md im Repo.",
  },
};
const CrmI18n = React.createContext({ lang: "de", t: (k) => k, setLang: () => {} });
function useT() { return React.useContext(CrmI18n); }

const CO_COLORS = ["#5A58E7", "#3490EB", "#7D50CA", "#1FA36B", "#E0792B", "#C2410C", "#9A7B0A", "#E5484D"];
function coColor(s) { let h = 0; for (let i = 0; i < (s || "").length; i++) h = (h * 31 + s.charCodeAt(i)) >>> 0; return CO_COLORS[h % CO_COLORS.length]; }
function coInitials(name) { const s = (name || "").trim(); if (!s) return "?"; const p = s.split(/[\s\-_/.]+/).filter(Boolean); if (p.length >= 2) return (p[0][0] + p[1][0]).toUpperCase(); return s.length <= 3 ? s.toUpperCase() : s.slice(0, 2).toUpperCase(); }
function CoAvatar({ company, size = 32 }) {
  if (!company) return null;
  const r = Math.max(4, Math.round(size * 0.28));
  if (company.logoUrl) return <img src={company.logoUrl} alt="" style={{ width: size, height: size, borderRadius: r, objectFit: "cover", flex: "0 0 auto", background: "#fff", border: "1px solid var(--line)" }} />;
  const init = coInitials(company.name);
  return <span style={{ width: size, height: size, borderRadius: r, background: coColor(company.name), color: "#fff", display: "grid", placeItems: "center", fontSize: Math.round(size * (init.length >= 3 ? 0.34 : 0.42)), fontWeight: 700, fontFamily: "var(--font-display)", flex: "0 0 auto", userSelect: "none", lineHeight: 1 }}>{init}</span>;
}
const FREEMAIL = /@(gmail|googlemail|web|gmx|yahoo|hotmail|outlook|icloud|t-online|freenet|aol|mail|proton|protonmail)\.[a-z.]+$/i;
const domainFromEmail = (e) => { const m = (e || "").match(/@([^\s@]+)$/); return m ? m[1].toLowerCase() : ""; };
const domainFromUrl = (u) => (u || "").trim().toLowerCase().replace(/^[a-z]+:\/\//, "").replace(/^www\./, "").replace(/\/.*$/, "").replace(/:\d+$/, "");
const CRM_STATUSES = ["new", "working", "qualified", "customer", "lost", "dnc"];
const statusColor = (s) => ({ new: "#7F89A2", working: "#3490EB", qualified: "#5A58E7", customer: "#1FA36B", lost: "#E5484D", dnc: "#9A5B1B" }[s] || "#7F89A2");
const OUTCOMES = ["connected", "noAnswer", "voicemail", "meetingBooked", "notInterested", "callback"];

function crmFmtDate(d) { if (!d) return ""; try { return new Date(d).toLocaleDateString("en-GB", { day: "numeric", month: "short" }); } catch (_) { return ""; } }
function crmRel(iso) { if (!iso) return ""; const s = Math.round((Date.now() - new Date(iso).getTime()) / 1000); if (s < 60) return "just now"; const m = Math.round(s / 60); if (m < 60) return m + "m"; const h = Math.round(m / 60); if (h < 24) return h + "h"; const d = Math.round(h / 24); if (d < 7) return d + "d"; return crmFmtDate(iso); }
/* day/month grouping for the activity timeline + email history (pattern: Docs activity log) */
function crmDayKey(iso) { const d = new Date(iso); return d.getFullYear() + "-" + d.getMonth() + "-" + d.getDate(); }
function crmDayLabel(iso, lang, t) {
  const d = new Date(iso), now = new Date();
  const startOf = (x) => new Date(x.getFullYear(), x.getMonth(), x.getDate()).getTime();
  const diff = Math.round((startOf(now) - startOf(d)) / 864e5);
  if (diff === 0) return t("today");
  if (diff === 1) return t("yesterday");
  try { return d.toLocaleDateString(lang === "de" ? "de-DE" : "en-US", { day: "numeric", month: "long", year: "numeric" }); } catch (_) { return ""; }
}
function crmMonthKey(iso) { const d = new Date(iso); return d.getFullYear() + "-" + d.getMonth(); }
function crmMonthLabel(iso, lang) { try { return new Date(iso).toLocaleDateString(lang === "de" ? "de-DE" : "en-US", { month: "long", year: "numeric" }); } catch (_) { return ""; } }

/* ---- root CRM app ---- */
function CrmApp({ user, onHome, path, navigate }) {
  const [lang, setLangState] = useState(() => { try { return localStorage.getItem("mesper.lang") || "de"; } catch (_) { return "de"; } });
  const setLang = (l) => { setLangState(l); try { localStorage.setItem("mesper.lang", l); localStorage.setItem("mesper.docsLang", l); localStorage.setItem("mesper.crmLang", l); } catch (_) {} };
  const t = (k) => (CRM_DICT[lang] && CRM_DICT[lang][k]) || CRM_DICT.en[k] || k;
  const narrow = typeof useIsNarrow === "function" ? useIsNarrow(760) : false;

  // route parsing: /crm , /crm/companies , /crm/c/<id> , /crm/pipeline , /crm/today , /crm/sequences[/<id>] , /crm/reports , /crm/settings , /crm/more
  const sub = (path || "").replace(/^\/crm\/?/, "");
  let view = "today", companyId = null, seqId = null;
  if (sub === "" || sub === "today") view = "today";
  else if (sub === "companies") view = "companies";
  else if (sub === "pipeline") view = "pipeline";
  else if (sub === "reports") view = "reports";
  else if (sub === "settings") view = "settings";
  else if (sub === "more") view = "more";
  else if (sub.startsWith("sequences/")) { view = "sequence"; seqId = sub.slice(10); }
  else if (sub === "sequences") view = "sequences";
  else if (sub.startsWith("c/")) { view = "company"; companyId = sub.slice(2); }
  else view = "today";

  const primaryNav = [
    { id: "today", icon: "clock", label: t("today"), to: "/crm/today" },
    { id: "companies", icon: "inbox", label: t("companies"), to: "/crm/companies" },
    { id: "pipeline", icon: "chart", label: t("pipeline"), to: "/crm/pipeline" },
  ];
  const secondaryNav = [
    { id: "sequences", icon: "repeat", label: t("sequences"), to: "/crm/sequences" },
    { id: "reports", icon: "chart", label: t("reports"), to: "/crm/reports" },
    { id: "settings", icon: "settings", label: t("settings"), to: "/crm/settings" },
  ];
  const activeNav = view === "company" ? "companies" : view === "sequence" ? "sequences" : view;

  const header = (
    <header style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 14, padding: narrow ? "11px 16px" : "13px 28px", borderBottom: "1px solid var(--line)", background: "var(--surface)", flex: "0 0 auto" }}>
      <button type="button" onClick={onHome} className="vp-focusable" title="All tools" style={{ display: "flex", alignItems: "center", gap: 9, background: "transparent", border: "none", cursor: "pointer" }}>
        <Icon name="arrowLeft" size={18} style={{ color: "var(--ink-4)" }} />
        <BrandLockup height={22} sub="CRM" />
      </button>
      <div style={{ display: "flex", alignItems: "center", gap: 12 }}>
        <div className="lang-toggle" style={{ display: "inline-flex", border: "1px solid var(--line-2)", borderRadius: "var(--r-pill)", overflow: "hidden", flex: "0 0 auto" }}>
          {["de", "en"].map((l) => (
            <button key={l} type="button" onClick={() => setLang(l)} style={{ border: "none", background: lang === l ? "var(--accent)" : "transparent", color: lang === l ? "#fff" : "var(--ink-3)", padding: "8px 12px", fontSize: 12.5, fontWeight: 700, cursor: "pointer" }}>{l.toUpperCase()}</button>
          ))}
        </div>
        {typeof NotificationBell === "function" && <NotificationBell navigate={navigate} />}
        <AccountMenu user={user} onLogout={() => MesperDB.signOut()} />
      </div>
    </header>
  );

  const navItem = (n) => (
    <button key={n.id} type="button" onClick={() => navigate(n.to)} className="vp-focusable"
      style={{ display: "flex", alignItems: "center", gap: 11, padding: "9px 12px", borderRadius: "var(--r-sm)", border: "none", cursor: "pointer", fontWeight: 600, fontSize: 14, textAlign: "left", background: activeNav === n.id ? "var(--surface)" : "transparent", color: activeNav === n.id ? "var(--ink)" : "var(--ink-3)", boxShadow: activeNav === n.id ? "var(--sh-sm)" : "none" }}>
      <Icon name={n.icon} size={18} style={{ color: activeNav === n.id ? "var(--accent)" : "var(--ink-4)" }} /> {n.label}
    </button>
  );
  const sidebar = !narrow && (
    <aside style={{ width: 232, flex: "0 0 auto", borderRight: "1px solid var(--line)", background: "var(--surface-2)", padding: "18px 14px", display: "flex", flexDirection: "column", gap: 4 }}>
      {primaryNav.map(navItem)}
      <div style={{ height: 1, background: "var(--line)", margin: "10px 8px" }} />
      {secondaryNav.map(navItem)}
    </aside>
  );

  const mobileNav = [...primaryNav, { id: "more", icon: "menu", label: t("more"), to: "/crm/more" }];
  const bottomNav = narrow && (
    <nav style={{ flex: "0 0 auto", display: "flex", borderTop: "1px solid var(--line)", background: "var(--surface)", paddingBottom: "env(safe-area-inset-bottom, 0)" }}>
      {mobileNav.map((n) => {
        const on = activeNav === n.id || (n.id === "more" && ["reports", "settings", "sequences", "sequence"].includes(view));
        return (
          <button key={n.id} type="button" onClick={() => navigate(n.to)} style={{ flex: 1, border: "none", background: "transparent", padding: "9px 0 11px", display: "grid", justifyItems: "center", gap: 3, cursor: "pointer", color: on ? "var(--accent)" : "var(--ink-4)" }}>
            <Icon name={n.icon} size={20} /><span style={{ fontSize: 11, fontWeight: 700 }}>{n.label}</span>
          </button>
        );
      })}
    </nav>
  );

  return (
    <CrmI18n.Provider value={{ lang, t, setLang }}>
      <div style={{ height: "100vh", display: "flex", flexDirection: "column", background: "var(--bg)" }}>
        {header}
        <div style={{ flex: 1, display: "flex", minHeight: 0 }}>
          {sidebar}
          <main style={{ flex: 1, minWidth: 0, overflowY: "auto", padding: narrow ? "16px 16px 80px" : "24px 28px 60px" }}>
            {view === "today" && <CrmToday navigate={navigate} />}
            {view === "companies" && <CrmCompanies navigate={navigate} />}
            {view === "pipeline" && <CrmPipeline navigate={navigate} />}
            {view === "company" && <CrmCompanyDetail id={companyId} user={user} navigate={navigate} />}
            {view === "sequences" && <CrmSequences navigate={navigate} />}
            {view === "sequence" && <CrmSequenceDetail id={seqId} navigate={navigate} />}
            {view === "reports" && <CrmReports />}
            {view === "settings" && <CrmSettings user={user} />}
            {view === "more" && <CrmMore navigate={navigate} secondaryNav={secondaryNav} />}
          </main>
        </div>
        {bottomNav}
      </div>
    </CrmI18n.Provider>
  );
}

/* ---- Companies list + quick add ---- */
function CrmCompanies({ navigate }) {
  const { t } = useT();
  const [companies, setCompanies] = useState(null);
  const [q, setQ] = useState("");
  const [filter, setFilter] = useState("all");
  const [adding, setAdding] = useState(false);
  const [importing, setImporting] = useState(false);
  function reload() { MesperDB.crmListCompanies().then(setCompanies).catch(() => setCompanies([])); }
  useEffect(() => { reload(); }, []);
  const VIEWS = [["all", t("companies")], ["new", t("statusNew")], ["working", t("statusWorking")], ["qualified", t("statusQualified")], ["customer", t("statusCustomer")]];
  const list = (companies || [])
    .filter((c) => filter === "all" || c.status === filter)
    .filter((c) => !q.trim() || (c.name + " " + c.domain + " " + c.industry).toLowerCase().includes(q.trim().toLowerCase()));
  return (
    <div style={{ maxWidth: 900, margin: "0 auto" }}>
      <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 12, flexWrap: "wrap" }}>
        <h1 style={{ fontSize: 22, margin: 0 }}>{t("companies")} {companies && <span style={{ color: "var(--ink-4)", fontWeight: 600, fontSize: 16 }}>{companies.length}</span>}</h1>
        <div style={{ flex: 1 }} />
        <input value={q} onChange={(e) => setQ(e.target.value)} placeholder={t("search")} style={{ ...(typeof selectStyle !== "undefined" ? selectStyle : {}), maxWidth: 220, flex: "1 1 160px" }} />
        <Button variant="ghost" icon="upload" onClick={() => setImporting(true)}>{t("importCsv")}</Button>
        <Button icon="plus" onClick={() => setAdding(true)}>{t("newCompany")}</Button>
      </div>
      {/* smart views */}
      <div style={{ display: "flex", gap: 6, flexWrap: "wrap", marginBottom: 16 }}>
        {VIEWS.map(([v, lbl]) => {
          const n = v === "all" ? (companies || []).length : (companies || []).filter((c) => c.status === v).length;
          return <button key={v} type="button" onClick={() => setFilter(v)} style={{ padding: "5px 12px", borderRadius: "var(--r-pill)", border: `1px solid ${filter === v ? "transparent" : "var(--line-2)"}`, background: filter === v ? "var(--accent-soft)" : "var(--surface)", color: filter === v ? "var(--accent-ink)" : "var(--ink-3)", fontWeight: 600, fontSize: 12.5, cursor: "pointer" }}>{lbl} {companies && <span style={{ opacity: 0.6 }}>{n}</span>}</button>;
        })}
      </div>
      {companies === null && <div style={{ color: "var(--ink-4)", fontSize: 14 }}>…</div>}
      {companies && list.length === 0 && <div style={{ color: "var(--ink-4)", fontSize: 14.5, padding: "24px 0" }}>{q ? "—" : t("noCompanies")}</div>}
      <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
        {list.map((c) => (
          <button key={c.id} type="button" onClick={() => navigate("/crm/c/" + c.id)} className="vp-focusable"
            style={{ display: "flex", alignItems: "center", gap: 12, padding: "11px 14px", border: "1px solid var(--line)", borderRadius: "var(--r-md)", background: "var(--surface)", cursor: "pointer", textAlign: "left", boxShadow: "var(--sh-sm)" }}>
            <CoAvatar company={c} size={38} />
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ fontWeight: 700, fontSize: 15, color: "var(--ink)", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{c.name}</div>
              <div style={{ fontSize: 12.5, color: "var(--ink-4)", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{c.nextStep ? "→ " + c.nextStep : (c.industry || c.domain || "")}</div>
            </div>
            <span style={{ fontSize: 11, fontWeight: 700, color: statusColor(c.status), background: "var(--surface-2)", border: "1px solid var(--line)", borderRadius: "var(--r-pill)", padding: "2px 9px", flex: "0 0 auto" }}>{t("status" + c.status.charAt(0).toUpperCase() + c.status.slice(1))}</span>
          </button>
        ))}
      </div>
      {adding && <CrmCompanyModal onClose={() => setAdding(false)} onSaved={(c) => { setAdding(false); navigate("/crm/c/" + c.id); }} />}
      {importing && <CrmImportModal onClose={() => setImporting(false)} onDone={reload} />}
    </div>
  );
}

function CrmCompanyModal({ onClose, onSaved, company }) {
  const { t } = useT();
  const editing = !!company;
  const [name, setName] = useState(company?.name || "");
  const [website, setWebsite] = useState(company?.website || company?.domain || "");
  const [industry, setIndustry] = useState(company?.industry || "");
  const [status, setStatus] = useState(company?.status || "new");
  const [leadSource, setLeadSource] = useState(company?.leadSource || "");
  const [busy, setBusy] = useState(false);
  const ss = typeof selectStyle !== "undefined" ? selectStyle : { width: "100%", padding: "9px 12px", border: "1px solid var(--line-2)", borderRadius: 8, fontSize: 15 };
  async function save() {
    if (!name.trim() || busy) return;
    setBusy(true);
    const domain = domainFromUrl(website);
    const payload = { name: name.trim(), website: website.trim(), domain, industry: industry.trim(), status, leadSource: leadSource.trim() };
    try {
      const c = editing ? await MesperDB.crmUpdateCompany(company.id, payload) : await MesperDB.crmCreateCompany(payload);
      // auto-logo from domain (non-blocking)
      if (domain && !c.logoUrl && MesperDB.fetchLogo) {
        MesperDB.fetchLogo(domain).then((r) => { if (r && r.ok && r.dataUrl) MesperDB.crmUpdateCompany(c.id, { logoUrl: r.dataUrl }).catch(() => {}); }).catch(() => {});
      }
      onSaved(c);
    } catch (e) { setBusy(false); }
  }
  return (
    <Modal onClose={onClose} width={460}>
      <div style={{ padding: "20px 24px", borderBottom: "1px solid var(--line)" }}><h2 style={{ fontSize: 19, margin: 0 }}>{editing ? t("edit") : t("newCompany")}</h2></div>
      <div style={{ padding: "18px 24px", display: "grid", gap: 14 }}>
        <div><div style={fieldLabelC()}>{t("name")}</div><input autoFocus value={name} onChange={(e) => setName(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter") save(); }} style={{ ...ss, fontWeight: 600 }} placeholder="Acme GmbH" /></div>
        <div><div style={fieldLabelC()}>{t("website")}</div><input value={website} onChange={(e) => setWebsite(e.target.value)} style={ss} placeholder="acme.de" /></div>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}>
          <div><div style={fieldLabelC()}>{t("industry")}</div><input value={industry} onChange={(e) => setIndustry(e.target.value)} style={ss} /></div>
          <div><div style={fieldLabelC()}>{t("leadSource")}</div><input value={leadSource} onChange={(e) => setLeadSource(e.target.value)} style={ss} placeholder="LinkedIn / Referral…" /></div>
        </div>
        <div><div style={fieldLabelC()}>{t("status")}</div>
          <div style={{ display: "flex", gap: 6, flexWrap: "wrap" }}>
            {CRM_STATUSES.map((s) => <button key={s} type="button" onClick={() => setStatus(s)} style={{ padding: "6px 11px", borderRadius: "var(--r-pill)", border: `1px solid ${status === s ? "transparent" : "var(--line-2)"}`, background: status === s ? "var(--accent-soft)" : "var(--surface)", color: status === s ? "var(--accent-ink)" : "var(--ink-3)", fontWeight: 600, fontSize: 12.5, cursor: "pointer" }}>{t("status" + s.charAt(0).toUpperCase() + s.slice(1))}</button>)}
          </div>
        </div>
      </div>
      <div style={{ padding: "14px 24px", borderTop: "1px solid var(--line)", display: "flex", justifyContent: "flex-end", gap: 10 }}>
        <Button variant="ghost" onClick={onClose}>{t("cancel")}</Button>
        <Button icon="check" onClick={save} disabled={busy || !name.trim()}>{busy ? "…" : (editing ? t("save") : t("create"))}</Button>
      </div>
    </Modal>
  );
}
function fieldLabelC() { return { fontSize: 12.5, fontWeight: 600, color: "var(--ink-3)", marginBottom: 6 }; }

/* ---- Today / needs-attention (open tasks) ---- */
function CrmToday({ navigate }) {
  const { t } = useT();
  const [tasks, setTasks] = useState(null);
  function reload() { MesperDB.crmListOpenTasks().then(setTasks).catch(() => setTasks([])); }
  useEffect(() => { reload(); }, []);
  async function done(task) { try { await MesperDB.crmUpdateTask(task.id, { status: "done" }); reload(); } catch (e) {} }
  const now = Date.now();
  const groups = { overdue: [], dueToday: [], upcoming: [] };
  const todayEnd = new Date(); todayEnd.setHours(23, 59, 59, 999);
  (tasks || []).forEach((t2) => { if (!t2.dueAt) { groups.upcoming.push(t2); return; } const d = new Date(t2.dueAt).getTime(); if (d < now && new Date(t2.dueAt) < new Date(new Date().setHours(0, 0, 0, 0))) groups.overdue.push(t2); else if (d <= todayEnd.getTime()) groups.dueToday.push(t2); else groups.upcoming.push(t2); });
  return (
    <div style={{ maxWidth: 760, margin: "0 auto" }}>
      <h1 style={{ fontSize: 22, margin: "0 0 16px" }}>{t("today")}</h1>
      {tasks === null && <div style={{ color: "var(--ink-4)" }}>…</div>}
      {tasks && tasks.length === 0 && <div style={{ color: "var(--ink-4)", fontSize: 14.5, padding: "24px 0" }}>{t("allCaughtUp")}</div>}
      {[["overdue", "var(--st-declined)"], ["dueToday", "var(--st-pending)"], ["upcoming", "var(--ink-4)"]].map(([g, col]) => groups[g].length > 0 && (
        <div key={g} style={{ marginBottom: 18 }}>
          <div style={{ fontSize: 12, fontWeight: 700, letterSpacing: "0.05em", textTransform: "uppercase", color: col, marginBottom: 8 }}>{t(g)} · {groups[g].length}</div>
          <div style={{ display: "flex", flexDirection: "column", gap: 7 }}>
            {groups[g].map((tk) => (
              <div key={tk.id} style={{ display: "flex", alignItems: "center", gap: 11, padding: "10px 13px", border: "1px solid var(--line)", borderRadius: "var(--r-md)", background: "var(--surface)" }}>
                <button type="button" onClick={() => done(tk)} title={t("markDone")} style={{ width: 20, height: 20, borderRadius: "50%", border: "2px solid var(--line-strong)", background: "transparent", cursor: "pointer", flex: "0 0 auto" }} />
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontWeight: 600, fontSize: 14, color: "var(--ink)" }}>{tk.title}</div>
                  <div style={{ fontSize: 12, color: "var(--ink-4)" }} onClick={() => tk.companyId && navigate("/crm/c/" + tk.companyId)}>{tk.companyName} {tk.dueAt && "· " + crmFmtDate(tk.dueAt)}</div>
                </div>
                <span style={{ fontSize: 10.5, fontWeight: 700, color: "var(--ink-4)", textTransform: "uppercase" }}>{t(tk.kind)}</span>
              </div>
            ))}
          </div>
        </div>
      ))}
    </div>
  );
}

/* ---- Pipeline (Kanban, read + drag-to-move) ---- */
function CrmPipeline({ navigate }) {
  const { t } = useT();
  const narrow = typeof useIsNarrow === "function" ? useIsNarrow(760) : false;
  const [stages, setStages] = useState([]);
  const [opps, setOpps] = useState(null);
  const [drag, setDrag] = useState(null); // {id}
  const [over, setOver] = useState(null); // stageId
  function reload() { Promise.all([MesperDB.crmListStages(), MesperDB.crmListOpportunities()]).then(([s, o]) => { setStages(s); setOpps(o); }).catch(() => setOpps([])); }
  useEffect(() => { reload(); }, []);
  async function moveTo(oppId, stage) {
    setOpps((cur) => cur.map((o) => o.id === oppId ? { ...o, stageId: stage.id, status: stage.isWon ? "won" : stage.isLost ? "lost" : "open" } : o));
    setDrag(null); setOver(null);
    try { await MesperDB.crmUpdateOpportunity(oppId, { stageId: stage.id, ...(stage.isWon ? { status: "won" } : stage.isLost ? { status: "lost" } : { status: "open" }) }); } catch (e) { reload(); }
  }
  return (
    <div>
      <div style={{ display: "flex", alignItems: "baseline", gap: 10, marginBottom: 16 }}>
        <h1 style={{ fontSize: 22, margin: 0 }}>{t("pipeline")}</h1>
        {!narrow && opps && <span style={{ fontSize: 12.5, color: "var(--ink-4)" }}>{t("stage")} · drag &amp; drop</span>}
      </div>
      {opps === null ? <div style={{ color: "var(--ink-4)" }}>…</div> : (
        <div style={{ display: "flex", gap: 12, overflowX: "auto", paddingBottom: 10, alignItems: "flex-start" }}>
          {stages.map((st) => {
            const items = opps.filter((o) => o.stageId === st.id && (st.isWon ? o.status === "won" : st.isLost ? o.status === "lost" : o.status === "open"));
            const sum = items.reduce((a, o) => a + (o.value || 0), 0);
            const isOver = over === st.id;
            return (
              <div key={st.id}
                onDragOver={(e) => { if (drag) { e.preventDefault(); setOver(st.id); } }}
                onDragLeave={() => setOver((v) => v === st.id ? null : v)}
                onDrop={(e) => { e.preventDefault(); if (drag) moveTo(drag.id, st); }}
                style={{ flex: "0 0 240px", minWidth: 240, background: isOver ? "var(--accent-soft)" : "var(--surface-2)", border: `1px solid ${isOver ? "var(--accent)" : "var(--line)"}`, borderRadius: "var(--r-lg)", padding: 10, display: "flex", flexDirection: "column", gap: 8, transition: "background .12s, border-color .12s" }}>
                <div style={{ display: "flex", alignItems: "center", gap: 7, padding: "2px 4px" }}>
                  <span style={{ width: 9, height: 9, borderRadius: "50%", background: st.color }} />
                  <span style={{ fontWeight: 700, fontSize: 13.5, color: "var(--ink-2)" }}>{st.name}</span>
                  <span style={{ fontSize: 12, color: "var(--ink-4)", fontWeight: 600 }}>{items.length}</span>
                  <div style={{ flex: 1 }} />
                  {sum > 0 && <span style={{ fontSize: 11.5, color: "var(--ink-4)", fontWeight: 600 }}>€{Math.round(sum / 1000)}k</span>}
                </div>
                {items.map((o) => (
                  <div key={o.id}
                    draggable
                    onDragStart={(e) => { setDrag({ id: o.id }); try { e.dataTransfer.effectAllowed = "move"; e.dataTransfer.setData("text/plain", o.id); } catch (_) {} }}
                    onDragEnd={() => { setDrag(null); setOver(null); }}
                    onClick={() => o.companyId && navigate("/crm/c/" + o.companyId)}
                    style={{ background: "var(--surface)", border: "1px solid var(--line)", borderRadius: "var(--r-md)", padding: "10px 12px", cursor: "grab", boxShadow: "var(--sh-sm)", opacity: drag && drag.id === o.id ? 0.4 : 1 }}>
                    <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
                      <CoAvatar company={{ name: o.companyName, logoUrl: o.companyLogo }} size={22} />
                      <span style={{ fontWeight: 600, fontSize: 13.5, color: "var(--ink)", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{o.companyName}</span>
                    </div>
                    {o.title && <div style={{ fontSize: 12.5, color: "var(--ink-3)", marginTop: 4 }}>{o.title}</div>}
                    {o.value > 0 && <div style={{ fontSize: 12, color: "var(--ink-4)", fontWeight: 600, marginTop: 3 }}>€{o.value.toLocaleString("de-DE")}</div>}
                  </div>
                ))}
              </div>
            );
          })}
        </div>
      )}
    </div>
  );
}

/* ---- Company detail ---- */
function CrmCompanyDetail({ id, user, navigate }) {
  const { t } = useT();
  const [data, setData] = useState(null);
  const [emails, setEmails] = useState(null);
  const [gmail, setGmail] = useState({ configured: false, calendar: false, account: "" });
  const [tab, setTab] = useState("overview");
  const [editing, setEditing] = useState(false);
  const [composeMode, setComposeMode] = useState(null); // null (closed) | "auto" | "log"
  const [nextStep, setNextStep] = useState("");
  const [editNext, setEditNext] = useState(false);
  const ss = typeof selectStyle !== "undefined" ? selectStyle : { width: "100%", padding: "9px 12px", border: "1px solid var(--line-2)", borderRadius: 8, fontSize: 14 };
  function reload() {
    MesperDB.crmGetCompany(id).then((d) => { setData(d); setNextStep(d.company.nextStep || ""); }).catch(() => setData(false));
    MesperDB.crmListEmails(id).then(setEmails).catch(() => setEmails([]));
  }
  useEffect(() => { reload(); }, [id]);
  useEffect(() => { MesperDB.crmGmailStatus().then(setGmail).catch(() => {}); }, []);
  if (data === null) return <div style={{ color: "var(--ink-4)" }}>…</div>;
  if (data === false) return <div style={{ color: "var(--ink-4)" }}>—</div>;
  const c = data.company;
  async function saveNext() { setEditNext(false); try { await MesperDB.crmUpdateCompany(id, { nextStep: nextStep.trim() }); } catch (e) {} }
  async function setStatus(s) { setData((d) => ({ ...d, company: { ...d.company, status: s } })); try { await MesperDB.crmUpdateCompany(id, { status: s }); } catch (e) {} }

  const TABS = [
    ["overview", t("tabOverview"), null],
    ["activity", t("activity"), null],
    ["emails", t("emails"), emails ? emails.length : null],
    ["contacts", t("contacts"), data.contacts.length],
  ];

  return (
    <div style={{ maxWidth: 820, margin: "0 auto", display: "flex", flexDirection: "column", gap: 18 }}>
      <button type="button" onClick={() => navigate("/crm/companies")} style={{ alignSelf: "flex-start", border: "none", background: "transparent", color: "var(--ink-3)", cursor: "pointer", fontSize: 13, fontWeight: 600, display: "inline-flex", alignItems: "center", gap: 5, padding: 0 }}><Icon name="arrowLeft" size={14} /> {t("back")}</button>

      {/* header */}
      <div style={{ display: "flex", alignItems: "flex-start", gap: 14 }}>
        <CoAvatar company={c} size={52} />
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ display: "flex", alignItems: "center", gap: 10, flexWrap: "wrap" }}>
            <h1 style={{ fontSize: 24, margin: 0 }}>{c.name}</h1>
            <button type="button" onClick={() => setEditing(true)} style={{ border: "none", background: "transparent", color: "var(--accent)", fontWeight: 600, fontSize: 12.5, cursor: "pointer" }}>{t("edit")}</button>
          </div>
          <div style={{ display: "flex", gap: 12, flexWrap: "wrap", marginTop: 5, fontSize: 13, color: "var(--ink-4)" }}>
            {c.website && <a href={/^https?:/.test(c.website) ? c.website : "https://" + c.website} target="_blank" rel="noopener noreferrer" style={{ color: "var(--accent)", textDecoration: "none" }}>{c.domain || c.website}</a>}
            {c.industry && <span>{c.industry}</span>}
            {c.leadSource && <span>· {c.leadSource}</span>}
          </div>
          <div style={{ display: "flex", gap: 6, flexWrap: "wrap", marginTop: 10 }}>
            {CRM_STATUSES.map((s) => <button key={s} type="button" onClick={() => setStatus(s)} style={{ padding: "4px 10px", borderRadius: "var(--r-pill)", border: `1px solid ${c.status === s ? "transparent" : "var(--line-2)"}`, background: c.status === s ? statusColor(s) : "var(--surface)", color: c.status === s ? "#fff" : "var(--ink-4)", fontWeight: 600, fontSize: 11.5, cursor: "pointer" }}>{t("status" + s.charAt(0).toUpperCase() + s.slice(1))}</button>)}
          </div>
        </div>
      </div>

      <CompanyLinksCard companyId={id} navigate={navigate} />

      {/* next step */}
      <div style={{ background: "var(--accent-soft)", border: "1px solid var(--accent)", borderRadius: "var(--r-md)", padding: "12px 14px", display: "flex", alignItems: "center", gap: 10 }}>
        <Icon name="arrowRight" size={16} style={{ color: "var(--accent-ink)", flex: "0 0 auto" }} />
        {editNext ? (
          <input autoFocus value={nextStep} onChange={(e) => setNextStep(e.target.value)} onBlur={saveNext} onKeyDown={(e) => { if (e.key === "Enter") saveNext(); }} placeholder={t("addNextStep")} style={{ ...ss, flex: 1, border: "1px solid var(--accent)", background: "var(--surface)" }} />
        ) : (
          <div onClick={() => setEditNext(true)} style={{ flex: 1, cursor: "text", fontWeight: 600, fontSize: 14, color: nextStep ? "var(--accent-ink)" : "var(--ink-4)" }}>{nextStep || t("addNextStep")}</div>
        )}
      </div>

      <CrmLogBar companyId={id} contacts={data.contacts} onLogged={reload} onLogEmail={() => setComposeMode("log")} />

      {/* tab bar (segmented control, Tasks-Stil) */}
      <div style={{ display: "flex" }}>
        <div style={{ display: "inline-flex", background: "var(--surface-2)", border: "1px solid var(--line)", borderRadius: "var(--r-pill)", padding: 3 }}>
          {TABS.map(([tid, label, count]) => {
            const on = tab === tid;
            return (
              <button key={tid} type="button" onClick={() => setTab(tid)} className="vp-focusable"
                style={{ display: "inline-flex", alignItems: "center", gap: 6, padding: "7px 14px", borderRadius: "var(--r-pill)", border: "none", cursor: "pointer", fontWeight: 600, fontSize: 13, background: on ? "var(--surface)" : "transparent", color: on ? "var(--ink)" : "var(--ink-3)", boxShadow: on ? "var(--sh-sm)" : "none" }}>
                {label}
                {count != null && count > 0 && <span style={{ fontSize: 11, fontWeight: 700, color: "var(--ink-4)", opacity: 0.85 }}>{count}</span>}
              </button>
            );
          })}
        </div>
      </div>

      {tab === "overview" && (
        <div style={{ display: "grid", gridTemplateColumns: "1fr", gap: 18 }}>
          <CrmDealBox companyId={id} opps={data.opportunities} onChange={reload} />
          <CrmTasksBox companyId={id} contacts={data.contacts} user={user} tasks={data.tasks} onChange={reload} />
          <CrmNotesBox companyId={id} notes={data.notes} onChange={reload} />
          <CrmActivityPreview activities={data.activities} emails={emails || []} notes={data.notes} contacts={data.contacts} onViewAll={() => setTab("activity")} />
          <CrmDocMentions companyId={id} navigate={navigate} />
          <CrmComplianceBox company={c} onChange={reload} onDeleted={() => navigate("/crm/companies")} />
        </div>
      )}
      {tab === "activity" && (
        <CrmTimeline companyId={id} activities={data.activities} emails={emails || []} notes={data.notes} contacts={data.contacts} />
      )}
      {tab === "emails" && (
        <CrmEmailsHistory company={c} contacts={data.contacts} emails={emails} gmailConfigured={gmail.configured} onWriteEmail={() => setComposeMode("auto")} />
      )}
      {tab === "contacts" && (
        <CrmContacts companyId={id} contacts={data.contacts} onChange={reload} />
      )}

      {editing && <CrmCompanyModal company={c} onClose={() => setEditing(false)} onSaved={() => { setEditing(false); reload(); }} />}
      {composeMode && (
        <CrmComposeModal
          company={c}
          contacts={data.contacts}
          gmailConfigured={gmail.configured}
          gmailAccount={gmail.account}
          initialMode={composeMode === "auto" ? null : composeMode}
          onClose={() => setComposeMode(null)}
          onSent={() => { setComposeMode(null); reload(); }}
        />
      )}
    </div>
  );
}

/* client-links: Board/Docs cross-links + one-click customer provisioning */
function CompanyLinksCard({ companyId, navigate }) {
  const { t } = useT();
  const [links, setLinks] = useState(null);
  const [busy, setBusy] = useState(false);
  function reload() { MesperDB.clientLinks({ companyId }).then(setLinks).catch(() => setLinks(false)); }
  useEffect(() => { reload(); }, [companyId]);
  if (links === null || links === false) return null;
  const hasBoard = !!links.projectId;
  const hasDocs = !!links.spaceId;
  async function provision() {
    setBusy(true);
    try { await MesperDB.clientProvisionFromCompany(companyId); } catch (e) {}
    setBusy(false);
    reload();
  }
  return (
    <CrmSection title={t("links")}>
      <div style={{ display: "flex", flexWrap: "wrap", gap: 7, alignItems: "center" }}>
        {hasBoard && (
          <button type="button" onClick={() => navigate("/approvals/p/" + links.projectKey)} style={crmLinkChip()}>
            <Icon name="check" size={13} /> {t("linkBoard")}: {links.projectKey}
          </button>
        )}
        {hasDocs && (
          <button type="button" onClick={() => navigate("/docs/s/" + links.spaceId)} style={crmLinkChip()}>
            <Icon name="check" size={13} /> {t("linkDocs")}: {links.spaceName}
          </button>
        )}
        {(!hasBoard || !hasDocs) && (
          <Button size="sm" icon="plus" onClick={provision} disabled={busy}>
            {busy ? "…" : (!hasBoard && !hasDocs ? t("asClient") : t("linkMissing"))}
          </Button>
        )}
      </div>
    </CrmSection>
  );
}
function crmLinkChip() { return { display: "inline-flex", alignItems: "center", gap: 5, border: "1px solid var(--line-2)", background: "var(--surface)", borderRadius: "var(--r-pill)", padding: "5px 11px", fontSize: 12, fontWeight: 600, color: "var(--ink-2)", cursor: "pointer" }; }

/* reverse cross-link: docs that @mention this company (Docs ↔ CRM) */
function CrmDocMentions({ companyId, navigate }) {
  const { t } = useT();
  const [docs, setDocs] = useState(null);
  useEffect(() => { if (MesperDB.docsListBacklinks) MesperDB.docsListBacklinks("crm_company", companyId).then(setDocs).catch(() => setDocs([])); else setDocs([]); }, [companyId]);
  if (!docs || docs.length === 0) return null;
  return (
    <CrmSection title={t("inDocs")} count={docs.length}>
      <div style={{ display: "flex", flexWrap: "wrap", gap: 7 }}>
        {docs.map((d) => (
          <button key={d.pageId} type="button" onClick={() => navigate("/docs/p/" + d.pageId)} style={{ display: "inline-flex", alignItems: "center", gap: 6, border: "1px solid var(--line-2)", background: "var(--surface-2)", borderRadius: "var(--r-pill)", padding: "5px 11px", fontSize: 12.5, fontWeight: 600, color: "var(--ink-2)", cursor: "pointer" }}>
            <span>{d.icon || "📄"}</span> {d.title}
          </button>
        ))}
      </div>
    </CrmSection>
  );
}

/* compliance per-company (#12): berechtigtes Interesse doc + Art.15 export + Art.17 delete */
function CrmComplianceBox({ company, onChange, onDeleted }) {
  const { t } = useT();
  const [bi, setBi] = useState(company.biBasis || "");
  const [busy, setBusy] = useState(false);
  async function saveBi() { if (bi === (company.biBasis || "")) return; try { await MesperDB.crmUpdateCompany(company.id, { biBasis: bi }); } catch (e) {} onChange(); }
  async function exportData() {
    setBusy(true);
    try {
      const dump = await MesperDB.crmExportCompany(company.id);
      const blob = new Blob([JSON.stringify(dump, null, 2)], { type: "application/json" });
      const url = URL.createObjectURL(blob); const a = document.createElement("a");
      a.href = url; a.download = "crm-" + (company.domain || company.name || company.id).replace(/[^a-z0-9]+/gi, "-").toLowerCase() + ".json";
      document.body.appendChild(a); a.click(); a.remove(); setTimeout(() => URL.revokeObjectURL(url), 1000);
    } catch (e) {}
    setBusy(false);
  }
  async function del() {
    if (!window.confirm(t("deleteConfirm"))) return;
    try { await MesperDB.crmDeleteCompany(company.id); } catch (e) {}
    onDeleted();
  }
  const ss = typeof selectStyle !== "undefined" ? selectStyle : { width: "100%", padding: "8px 10px", border: "1px solid var(--line-2)", borderRadius: 8, fontSize: 13.5 };
  return (
    <CrmSection title={t("gdpr")}>
      <div style={{ marginBottom: 12 }}>
        <div style={fieldLabelC()}>{t("biBasis")}</div>
        <input defaultValue={bi} onChange={(e) => setBi(e.target.value)} onBlur={saveBi} placeholder={t("biPlaceholder")} style={ss} />
      </div>
      <div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
        <Button size="sm" variant="ghost" icon="download" onClick={exportData} disabled={busy}>{t("exportData")}</Button>
        <button type="button" onClick={del} style={{ display: "inline-flex", alignItems: "center", gap: 6, padding: "7px 13px", border: "1px solid var(--st-declined, #E5484D)", borderRadius: "var(--r-sm)", background: "transparent", cursor: "pointer", fontWeight: 600, fontSize: 13, color: "var(--st-declined, #E5484D)" }}><Icon name="trash" size={14} /> {t("deleteCompany")}</button>
      </div>
    </CrmSection>
  );
}

/* quick log bar: call disposition (+ auto follow-up), email (compose), linkedin, meeting */
function CrmLogBar({ companyId, contacts, onLogged, onLogEmail }) {
  const { t } = useT();
  const [open, setOpen] = useState(null); // 'call' | null
  const primary = contacts.find((x) => x.isPrimary) || contacts[0] || null;
  async function log(type, outcome) {
    try {
      await MesperDB.crmLogActivity({ companyId, contactId: primary ? primary.id : null, type, direction: "outbound", outcome: outcome || null });
      if (type === "call" && (outcome === "noAnswer" || outcome === "callback")) {
        const due = new Date(Date.now() + 24 * 3600 * 1000);
        await MesperDB.crmCreateTask({ companyId, contactId: primary ? primary.id : null, title: t("followUp") + (primary ? " · " + (primary.first || "") : ""), kind: "call", dueAt: due.toISOString() });
      }
    } catch (e) {}
    setOpen(null); onLogged();
  }
  const btn = { display: "inline-flex", alignItems: "center", gap: 6, padding: "8px 13px", border: "1px solid var(--line-2)", borderRadius: "var(--r-sm)", background: "var(--surface)", cursor: "pointer", fontWeight: 600, fontSize: 13, color: "var(--ink-2)" };
  return (
    <div>
      <div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
        <button type="button" onClick={() => setOpen(open === "call" ? null : "call")} style={{ ...btn, borderColor: "var(--accent)", color: "var(--accent-ink)" }}><Icon name="clock" size={14} /> {t("logCall")}</button>
        {primary && primary.phone && <a href={"tel:" + primary.phone} style={{ ...btn, textDecoration: "none" }}>📞 {primary.phone}</a>}
        <button type="button" onClick={onLogEmail} style={btn}><Icon name="mail" size={14} /> {t("logEmail")}</button>
        <button type="button" onClick={() => log("linkedin")} style={btn}>{t("logLinkedin")}</button>
        <button type="button" onClick={() => log("meeting", "meetingBooked")} style={btn}>{t("logMeeting")}</button>
      </div>
      {open === "call" && (
        <div style={{ marginTop: 8, display: "flex", gap: 6, flexWrap: "wrap", padding: 10, border: "1px solid var(--line)", borderRadius: "var(--r-md)", background: "var(--surface-2)" }}>
          <span style={{ fontSize: 12, fontWeight: 700, color: "var(--ink-4)", alignSelf: "center", marginRight: 4 }}>{t("outcome")}:</span>
          {OUTCOMES.map((o) => <button key={o} type="button" onClick={() => log("call", o)} style={{ padding: "6px 11px", borderRadius: "var(--r-pill)", border: "1px solid var(--line-2)", background: "var(--surface)", cursor: "pointer", fontSize: 12.5, fontWeight: 600, color: "var(--ink-2)" }}>{t(o)}</button>)}
        </div>
      )}
    </div>
  );
}

/* compose / send / log an email – three modes: send (Gmail), mailto (Gmail not
   connected: open mail app + log), log (log only, e.g. to backfill a past email).
   Gmail send is owner-gated (crmGmailStatus); manual logging always works. */
function CrmComposeModal({ company, contacts, onClose, onSent, gmailConfigured, gmailAccount, initialMode }) {
  const { t } = useT();
  const bizContacts = (contacts || []).filter((c) => c.email);
  const primary = bizContacts.find((x) => x.isPrimary) || bizContacts[0] || null;
  const [contactId, setContactId] = useState(primary ? primary.id : "");
  const [templates, setTemplates] = useState([]);
  const [settings, setSettings] = useState(null);
  const [templateId, setTemplateId] = useState("");
  const [subject, setSubject] = useState("");
  const [body, setBody] = useState("");
  const [track, setTrack] = useState(false);
  const [direction, setDirection] = useState("outbound");
  const [mode, setMode] = useState(initialMode === "log" ? "log" : (gmailConfigured ? "send" : "mailto"));
  const [busy, setBusy] = useState(false);
  const [note, setNote] = useState("");
  const ss = typeof selectStyle !== "undefined" ? selectStyle : { width: "100%", padding: "9px 12px", border: "1px solid var(--line-2)", borderRadius: 8, fontSize: 14 };
  useEffect(() => { MesperDB.crmListTemplates().then(setTemplates).catch(() => {}); MesperDB.crmGetSettings().then((s) => { setSettings(s); setTrack(!!s.defaultTracking); }).catch(() => setSettings({})); }, []);
  const contact = bizContacts.find((c) => c.id === contactId) || primary;

  if (bizContacts.length === 0) {
    return (
      <Modal onClose={onClose} width={440}>
        <div style={{ padding: "18px 22px", borderBottom: "1px solid var(--line)" }}><h2 style={{ fontSize: 18, margin: 0 }}>{t("compose")}</h2></div>
        <div style={{ padding: "18px 22px", fontSize: 13.5, color: "var(--ink-3)" }}>{t("needContactEmail")}</div>
        <div style={{ padding: "13px 22px", borderTop: "1px solid var(--line)", display: "flex", justifyContent: "flex-end" }}>
          <Button variant="ghost" onClick={onClose}>{t("cancel")}</Button>
        </div>
      </Modal>
    );
  }

  const merge = (s) => (s || "").replace(/\{\{\s*firstName\s*\}\}/gi, contact?.first || "").replace(/\{\{\s*company\s*\}\}/gi, company.name || "").replace(/\{\{\s*lastName\s*\}\}/gi, contact?.last || "");
  function applyTemplate(id) {
    setTemplateId(id);
    const tp = templates.find((x) => x.id === id);
    if (tp) { setSubject(tp.subject || ""); setBody(tp.body || ""); }
  }
  function finish(successKey) { setNote(successKey); setBusy(false); setTimeout(() => onSent(), 900); }
  async function sendIt() {
    if (busy || !contact) return; setBusy(true); setNote("");
    const res = await MesperDB.crmGmailSend({ companyId: company.id, contactId: contact.id, subject, body, templateId: templateId || null, trackingEnabled: track });
    if (res && res.ok) { finish("sent"); return; }
    setBusy(false);
    const reason = res && res.reason;
    setNote(reason === "suppressed" ? "suppressed" : reason === "not_connected" ? "notConnected" : "genericErr");
  }
  async function mailtoIt() {
    if (busy || !contact) return; setBusy(true); setNote("");
    const subj = merge(subject), bod = merge(body);
    try {
      window.open(`mailto:${contact.email}?subject=${encodeURIComponent(subj)}&body=${encodeURIComponent(bod)}`, "_blank");
      await MesperDB.crmLogEmail({ companyId: company.id, contactId: contact.id, direction: "outbound", subject: subj, body: bod, templateId: templateId || null, trackingEnabled: track });
      finish("logged");
    } catch (e) { setBusy(false); setNote("genericErr"); }
  }
  async function logOnlyIt() {
    if (busy || !contact) return; setBusy(true); setNote("");
    const subj = merge(subject), bod = merge(body);
    try {
      await MesperDB.crmLogEmail({ companyId: company.id, contactId: contact.id, direction, subject: subj, body: bod, templateId: templateId || null, trackingEnabled: track });
      finish("logged");
    } catch (e) { setBusy(false); setNote("genericErr"); }
  }
  const footer = settings && settings.impressum ? merge(settings.impressum) : "";
  const modeOptions = gmailConfigured ? [["send", t("send")], ["log", t("logOnly")]] : [["mailto", t("openMailApp")], ["log", t("logOnly")]];
  return (
    <Modal onClose={onClose} width={580}>
      <div style={{ padding: "18px 22px", borderBottom: "1px solid var(--line)" }}>
        <h2 style={{ fontSize: 18, margin: 0 }}>{t("compose")}</h2>
        {!gmailConfigured && <div style={{ fontSize: 12.5, color: "var(--ink-4)", marginTop: 3 }}>{t("gmailNotConnectedHint")}</div>}
      </div>
      <div style={{ padding: "16px 22px", display: "grid", gap: 12, maxHeight: "62vh", overflowY: "auto" }}>
        <div style={{ display: "inline-flex", background: "var(--surface-2)", border: "1px solid var(--line)", borderRadius: "var(--r-pill)", padding: 3, alignSelf: "flex-start" }}>
          {modeOptions.map(([mid, label]) => {
            const on = mode === mid;
            return <button key={mid} type="button" onClick={() => setMode(mid)} style={{ padding: "6px 13px", borderRadius: "var(--r-pill)", border: "none", cursor: "pointer", fontWeight: 600, fontSize: 12.5, background: on ? "var(--surface)" : "transparent", color: on ? "var(--ink)" : "var(--ink-3)", boxShadow: on ? "var(--sh-sm)" : "none" }}>{label}</button>;
          })}
        </div>
        {mode === "log" && (
          <div style={{ display: "inline-flex", gap: 6, alignSelf: "flex-start" }}>
            {[["outbound", t("outbound")], ["inbound", t("inbound")]].map(([did, label]) => (
              <button key={did} type="button" onClick={() => setDirection(did)} style={{ padding: "5px 11px", borderRadius: "var(--r-pill)", border: `1px solid ${direction === did ? "transparent" : "var(--line-2)"}`, background: direction === did ? "var(--accent-soft)" : "var(--surface)", color: direction === did ? "var(--accent-ink)" : "var(--ink-3)", fontWeight: 600, fontSize: 12, cursor: "pointer" }}>{label}</button>
            ))}
          </div>
        )}
        {mode === "send" && gmailAccount && <div style={{ fontSize: 12.5, color: "var(--ink-4)" }}>{t("fromAccount")}: {gmailAccount}</div>}
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
          <div><div style={fieldLabelC()}>{t("contacts")}</div>
            <select value={contactId} onChange={(e) => setContactId(e.target.value)} style={ss}>
              {bizContacts.map((c) => <option key={c.id} value={c.id}>{`${c.first} ${c.last}`.trim() || c.email}</option>)}
            </select>
          </div>
          <div><div style={fieldLabelC()}>{t("useTemplate")}</div>
            <select value={templateId} onChange={(e) => applyTemplate(e.target.value)} style={ss}>
              <option value="">{t("noTemplate")}</option>
              {templates.map((tp) => <option key={tp.id} value={tp.id}>{tp.name}</option>)}
            </select>
          </div>
        </div>
        <div style={{ fontSize: 11.5, color: "var(--ink-4)" }}>{t("templatePlaceholderHint")}</div>
        <div><div style={fieldLabelC()}>{t("subjectLine")}</div><input value={subject} onChange={(e) => setSubject(e.target.value)} style={ss} /></div>
        <div><div style={fieldLabelC()}>{t("emailBody")}</div><textarea value={body} onChange={(e) => setBody(e.target.value)} rows={8} style={{ ...ss, resize: "vertical", fontFamily: "var(--font-body)" }} /></div>
        {footer && <div style={{ fontSize: 11.5, color: "var(--ink-4)", whiteSpace: "pre-wrap", borderTop: "1px dashed var(--line)", paddingTop: 8 }}>{footer}</div>}
        <label style={{ display: "inline-flex", alignItems: "center", gap: 9, cursor: "pointer", fontSize: 13, color: "var(--ink-3)" }}>
          <input type="checkbox" checked={track} onChange={(e) => setTrack(e.target.checked)} style={{ width: 16, height: 16, accentColor: "var(--accent)" }} /> {t("trackThis")}
        </label>
        <div style={{ fontSize: 11.5, color: "var(--ink-4)", marginTop: -8 }}>{t("trackThisHint")}</div>
        {note && (
          <div style={{ fontSize: 12.5, color: (note === "suppressed" || note === "genericErr") ? "var(--st-declined, #E5484D)" : (note === "sent" || note === "logged") ? "#1FA36B" : "var(--ink-4)", background: "var(--surface-2)", border: "1px solid var(--line)", borderRadius: "var(--r-sm)", padding: "8px 11px", display: "flex", alignItems: "center", gap: 7 }}>
            <Icon name="info" size={14} /> {note === "suppressed" ? t("suppressedMsg") : note === "notConnected" ? t("connectGmail") : note === "genericErr" ? t("sendErrorGeneric") : note === "sent" ? t("sent") : note === "logged" ? t("logged") : ""}
          </div>
        )}
      </div>
      <div style={{ padding: "13px 22px", borderTop: "1px solid var(--line)", display: "flex", justifyContent: "flex-end", gap: 10 }}>
        <Button variant="ghost" onClick={onClose}>{t("cancel")}</Button>
        {mode === "send" && <Button icon="mail" onClick={sendIt} disabled={busy || !contact}>{busy ? "…" : t("send")}</Button>}
        {mode === "mailto" && <Button icon="mail" onClick={mailtoIt} disabled={busy || !contact}>{busy ? "…" : t("openMailApp")}</Button>}
        {mode === "log" && <Button icon="check" onClick={logOnlyIt} disabled={busy || !contact}>{busy ? "…" : t("logOnly")}</Button>}
      </div>
    </Modal>
  );
}

function CrmSection({ title, count, action, children }) {
  return (
    <div style={{ border: "1px solid var(--line)", borderRadius: "var(--r-md)", background: "var(--surface)", padding: "14px 16px" }}>
      <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 10 }}>
        <div style={{ fontSize: 13.5, fontWeight: 700, color: "var(--ink)" }}>{title}</div>
        {count != null && count > 0 && <span style={{ fontSize: 11.5, fontWeight: 600, color: "var(--ink-4)", background: "var(--surface-2)", borderRadius: "var(--r-pill)", padding: "1px 8px" }}>{count}</span>}
        <div style={{ flex: 1 }} />
        {action}
      </div>
      {children}
    </div>
  );
}

function CrmContacts({ companyId, contacts, onChange }) {
  const { t } = useT();
  const [adding, setAdding] = useState(false);
  const [f, setF] = useState({ first: "", last: "", title: "", email: "", phone: "" });
  const ss = typeof selectStyle !== "undefined" ? selectStyle : { width: "100%", padding: "8px 10px", border: "1px solid var(--line-2)", borderRadius: 8, fontSize: 13.5 };
  async function add() {
    if (!f.first.trim() && !f.email.trim()) return;
    const isBiz = f.email ? !FREEMAIL.test(f.email) : true;
    try { await MesperDB.crmCreateContact({ companyId, first: f.first.trim(), last: f.last.trim(), title: f.title.trim(), email: f.email.trim(), phone: f.phone.trim(), isPrimary: contacts.length === 0, isBusiness: isBiz }); } catch (e) {}
    setF({ first: "", last: "", title: "", email: "", phone: "" }); setAdding(false); onChange();
  }
  return (
    <CrmSection title={t("contacts")} count={contacts.length} action={<button type="button" onClick={() => setAdding((a) => !a)} style={{ border: "none", background: "transparent", color: "var(--accent)", fontWeight: 600, fontSize: 12.5, cursor: "pointer" }}>+ {t("add")}</button>}>
      {contacts.length === 0 && !adding && <div style={{ fontSize: 13, color: "var(--ink-4)" }}>{t("noContacts")}</div>}
      <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
        {contacts.map((ct) => (
          <div key={ct.id} style={{ display: "flex", alignItems: "center", gap: 10 }}>
            <div style={{ width: 30, height: 30, borderRadius: "50%", background: coColor(ct.first + ct.last), color: "#fff", display: "grid", placeItems: "center", fontSize: 12, fontWeight: 700, flex: "0 0 auto" }}>{((ct.first[0] || "") + (ct.last[0] || "")).toUpperCase() || "?"}</div>
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ fontWeight: 600, fontSize: 14, color: "var(--ink)" }}>{`${ct.first} ${ct.last}`.trim() || ct.email} {ct.isPrimary && <span style={{ fontSize: 10, color: "var(--accent-ink)", background: "var(--accent-soft)", borderRadius: 4, padding: "0 5px", marginLeft: 4 }}>{t("primary")}</span>} {!ct.isBusiness && <span style={{ fontSize: 10, color: "var(--st-declined)", marginLeft: 4 }}>⚠︎</span>}</div>
              <div style={{ fontSize: 12, color: "var(--ink-4)" }}>{[ct.title, ct.email, ct.phone].filter(Boolean).join(" · ")}</div>
            </div>
            {ct.phone && <a href={"tel:" + ct.phone} style={{ textDecoration: "none", fontSize: 15 }} title="Call">📞</a>}
          </div>
        ))}
      </div>
      {adding && (
        <div style={{ marginTop: 10, display: "grid", gap: 7 }}>
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 7 }}>
            <input value={f.first} onChange={(e) => setF({ ...f, first: e.target.value })} placeholder={t("firstName")} style={ss} autoFocus />
            <input value={f.last} onChange={(e) => setF({ ...f, last: e.target.value })} placeholder={t("lastName")} style={ss} />
          </div>
          <input value={f.title} onChange={(e) => setF({ ...f, title: e.target.value })} placeholder={t("role")} style={ss} />
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 7 }}>
            <input value={f.email} onChange={(e) => setF({ ...f, email: e.target.value })} placeholder={t("email")} style={ss} />
            <input value={f.phone} onChange={(e) => setF({ ...f, phone: e.target.value })} placeholder={t("phone")} style={ss} />
          </div>
          <div style={{ display: "flex", gap: 8 }}><Button size="sm" icon="check" onClick={add}>{t("add")}</Button><Button size="sm" variant="ghost" onClick={() => setAdding(false)}>{t("cancel")}</Button></div>
        </div>
      )}
    </CrmSection>
  );
}

function CrmDealBox({ companyId, opps, onChange }) {
  const { t } = useT();
  const open = opps.find((o) => o.status === "open");
  const [stages, setStages] = useState([]);
  const [val, setVal] = useState("");
  useEffect(() => { MesperDB.crmListStages().then(setStages).catch(() => {}); }, []);
  async function create() { const first = stages[0]; try { await MesperDB.crmCreateOpportunity({ companyId, title: "", value: Number(val) || 0, stageId: first ? first.id : null }); } catch (e) {} setVal(""); onChange(); }
  async function moveStage(sid) { if (!open) return; try { await MesperDB.crmUpdateOpportunity(open.id, { stageId: sid }); } catch (e) {} onChange(); }
  async function close(status) { if (!open) return; try { await MesperDB.crmUpdateOpportunity(open.id, { status }); } catch (e) {} onChange(); }
  return (
    <CrmSection title={t("deal")}>
      {!open ? (
        <div style={{ display: "flex", gap: 8, alignItems: "center", flexWrap: "wrap" }}>
          <span style={{ fontSize: 13, color: "var(--ink-4)" }}>{t("noDeal")}</span>
          <input value={val} onChange={(e) => setVal(e.target.value)} placeholder={"€ " + t("value")} style={{ ...(typeof selectStyle !== "undefined" ? selectStyle : {}), maxWidth: 120 }} />
          <Button size="sm" icon="plus" onClick={create}>{t("createDeal")}</Button>
        </div>
      ) : (
        <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
          <div style={{ display: "flex", gap: 6, flexWrap: "wrap" }}>
            {stages.filter((s) => !s.isWon && !s.isLost).map((s) => <button key={s.id} type="button" onClick={() => moveStage(s.id)} style={{ padding: "5px 10px", borderRadius: "var(--r-pill)", border: `1px solid ${open.stageId === s.id ? "transparent" : "var(--line-2)"}`, background: open.stageId === s.id ? s.color : "var(--surface)", color: open.stageId === s.id ? "#fff" : "var(--ink-4)", fontWeight: 600, fontSize: 11.5, cursor: "pointer" }}>{s.name}</button>)}
          </div>
          <div style={{ display: "flex", gap: 8, alignItems: "center" }}>
            {open.value > 0 && <span style={{ fontWeight: 700, color: "var(--ink)" }}>€{open.value.toLocaleString("de-DE")}</span>}
            <div style={{ flex: 1 }} />
            <Button size="sm" variant="ghost" onClick={() => close("won")}>✓ {t("won")}</Button>
            <Button size="sm" variant="ghost" onClick={() => close("lost")}>✕ {t("lost")}</Button>
          </div>
        </div>
      )}
    </CrmSection>
  );
}

function CrmTasksBox({ companyId, contacts, user, tasks, onChange }) {
  const { t } = useT();
  const [title, setTitle] = useState("");
  const [due, setDue] = useState("");
  const openTasks = tasks.filter((x) => x.status === "open");
  async function add() { if (!title.trim()) return; try { await MesperDB.crmCreateTask({ companyId, title: title.trim(), kind: "call", dueAt: due ? new Date(due).toISOString() : null, assignee: user ? user.id : null }); } catch (e) {} setTitle(""); setDue(""); onChange(); }
  async function done(tk) { try { await MesperDB.crmUpdateTask(tk.id, { status: "done" }); } catch (e) {} onChange(); }
  const ss = typeof selectStyle !== "undefined" ? selectStyle : { padding: "8px 10px", border: "1px solid var(--line-2)", borderRadius: 8, fontSize: 13.5 };
  return (
    <CrmSection title={t("tasks")} count={openTasks.length}>
      {openTasks.length === 0 && <div style={{ fontSize: 13, color: "var(--ink-4)", marginBottom: 8 }}>{t("noTasks")}</div>}
      <div style={{ display: "flex", flexDirection: "column", gap: 7, marginBottom: 10 }}>
        {openTasks.map((tk) => (
          <div key={tk.id} style={{ display: "flex", alignItems: "center", gap: 10 }}>
            <button type="button" onClick={() => done(tk)} style={{ width: 18, height: 18, borderRadius: "50%", border: "2px solid var(--line-strong)", background: "transparent", cursor: "pointer", flex: "0 0 auto" }} />
            <span style={{ flex: 1, fontSize: 13.5, color: "var(--ink)" }}>{tk.title}</span>
            {tk.dueAt && <span style={{ fontSize: 12, color: new Date(tk.dueAt) < new Date() ? "var(--st-declined)" : "var(--ink-4)", fontWeight: 600 }}>{crmFmtDate(tk.dueAt)}</span>}
          </div>
        ))}
      </div>
      <div style={{ display: "flex", gap: 7 }}>
        <input value={title} onChange={(e) => setTitle(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter") add(); }} placeholder={t("addTask")} style={{ ...ss, flex: 1 }} />
        <input type="date" value={due} onChange={(e) => setDue(e.target.value)} style={{ ...ss, flex: "0 0 auto" }} />
        <Button size="sm" icon="plus" onClick={add} />
      </div>
    </CrmSection>
  );
}

function CrmNotesBox({ companyId, notes, onChange }) {
  const { t } = useT();
  const [val, setVal] = useState("");
  async function add() { if (!val.trim()) return; try { await MesperDB.crmAddNote({ companyId, body: val.trim() }); } catch (e) {} setVal(""); onChange(); }
  async function del(n) { try { await MesperDB.crmDeleteNote(n.id); } catch (e) {} onChange(); }
  return (
    <CrmSection title={t("notes")} count={notes.length}>
      <div style={{ display: "flex", gap: 7, marginBottom: notes.length ? 12 : 0 }}>
        <textarea value={val} onChange={(e) => setVal(e.target.value)} rows={2} placeholder={t("addNote")} style={{ ...(typeof selectStyle !== "undefined" ? selectStyle : {}), flex: 1, resize: "vertical", minHeight: 42, fontFamily: "var(--font-body)" }} />
        <Button size="sm" icon="check" onClick={add} />
      </div>
      <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
        {notes.map((n) => (
          <div key={n.id} style={{ background: "var(--surface-2)", border: "1px solid var(--line)", borderRadius: "var(--r-sm)", padding: "9px 11px" }}>
            <div style={{ display: "flex", alignItems: "baseline", gap: 8, marginBottom: 3 }}>
              <span style={{ fontSize: 11.5, fontWeight: 600, color: "var(--ink-3)" }}>{VP_DATA.byId && n.createdBy && VP_DATA.byId[n.createdBy] ? `${VP_DATA.byId[n.createdBy].first || ""}`.trim() : ""}</span>
              <span style={{ fontSize: 11, color: "var(--ink-4)" }}>{crmRel(n.at)}</span>
              <div style={{ flex: 1 }} />
              <button type="button" onClick={() => del(n)} style={{ border: "none", background: "transparent", cursor: "pointer", color: "var(--ink-4)", padding: 0 }}><Icon name="x" size={12} /></button>
            </div>
            {typeof RichText === "function" ? <RichText md={n.body} style={{ fontSize: 13.5 }} /> : <div style={{ fontSize: 13.5, whiteSpace: "pre-wrap", color: "var(--ink-2)" }}>{n.body}</div>}
          </div>
        ))}
      </div>
    </CrmSection>
  );
}

/* ---- Activity timeline: merges activities + emails + notes into one chronological feed ---- */
const CRM_ACT_ICON = { call: "clock", email: "mail", linkedin: "users", meeting: "calendar", note: "file" };
function crmBadge(color) { return { fontSize: 11, fontWeight: 700, color: color || "var(--ink-3)", background: "var(--surface-2)", border: "1px solid var(--line)", borderRadius: "var(--r-pill)", padding: "1px 8px" }; }

// Merges the three CRM data sources into one sorted feed. Emails are surfaced
// directly (with open/click/reply badges); a matching activities row of type
// "email" (created server-side alongside a real send) is dropped to avoid a
// duplicate entry – matched by same direction within a ±2min window.
function crmMergeTimeline({ activities, emails, notes }) {
  const emailItems = (emails || []).map((e) => ({
    id: "email-" + e.id, kind: "email", direction: e.direction || "outbound",
    subject: e.subject || "", body: e.body || "", at: e.sentAt || e.at, contactId: e.contactId,
    openCount: e.openCount || 0, clickCount: e.clickCount || 0, repliedAt: e.repliedAt || null, senderEmail: e.senderEmail || "",
  }));
  const isDupe = (a) => {
    const at = new Date(a.at).getTime();
    return emailItems.some((e) => e.direction === (a.direction || "outbound") && Math.abs(new Date(e.at).getTime() - at) <= 2 * 60 * 1000);
  };
  const activityItems = (activities || [])
    .filter((a) => !(a.type === "email" && isDupe(a)))
    .map((a) => ({ id: "act-" + a.id, kind: a.type, direction: a.direction || "", outcome: a.outcome || "", subject: a.subject || "", body: a.body || "", at: a.at, contactId: a.contactId }));
  const noteItems = (notes || []).map((n) => ({ id: "note-" + n.id, kind: "note", body: n.body || "", at: n.at, contactId: n.contactId }));
  return [...emailItems, ...activityItems, ...noteItems].filter((x) => x.at).sort((a, b) => new Date(b.at) - new Date(a.at));
}
function crmTimelineTitle(item, t) {
  if (item.kind === "email") return (item.direction === "inbound" ? t("actEmailReceived") : t("actEmailSent")) + (item.subject ? ": " + item.subject : "");
  if (item.kind === "call") return t("actCallLogged") + (item.outcome ? " · " + t(item.outcome) : "");
  if (item.kind === "linkedin") return t("actLinkedinLogged");
  if (item.kind === "meeting") return t("actMeetingLogged") + (item.outcome ? " · " + t(item.outcome) : "");
  if (item.kind === "note") return t("actNoteTitle");
  return item.kind;
}
function CrmTimelineRow({ item, cById }) {
  const { t } = useT();
  const contact = item.contactId && cById[item.contactId];
  const who = contact ? `${contact.first || ""} ${contact.last || ""}`.trim() : "";
  const color = item.kind === "call" ? "var(--accent)" : item.kind === "email" ? "#3490EB" : item.kind === "meeting" ? "#1FA36B" : item.kind === "linkedin" ? "#0A66C2" : "var(--ink-4)";
  return (
    <div style={{ display: "flex", gap: 10, alignItems: "flex-start" }}>
      <span style={{ width: 26, height: 26, borderRadius: "50%", background: "var(--surface-2)", display: "grid", placeItems: "center", flex: "0 0 auto" }}>
        <Icon name={CRM_ACT_ICON[item.kind] || "dot"} size={13} style={{ color }} />
      </span>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ fontSize: 13, color: "var(--ink-2)", display: "flex", flexWrap: "wrap", gap: 6, alignItems: "baseline" }}>
          <span style={{ fontWeight: 600, color: "var(--ink)" }}>{crmTimelineTitle(item, t)}</span>
          {who && <span style={{ color: "var(--ink-4)" }}>· {who}</span>}
          {item.kind === "email" && item.direction === "outbound" && item.senderEmail && <span style={{ color: "var(--ink-4)" }}>· {item.senderEmail}</span>}
          <div style={{ flex: 1 }} />
          <span style={{ color: "var(--ink-4)", fontSize: 12 }}>{crmRel(item.at)}</span>
        </div>
        {item.kind === "email" && (item.openCount > 0 || item.clickCount > 0 || item.repliedAt) && (
          <div style={{ display: "flex", gap: 6, marginTop: 4, flexWrap: "wrap" }}>
            {item.openCount > 0 && <span style={crmBadge()}>{t("opened")} ({item.openCount})</span>}
            {item.clickCount > 0 && <span style={crmBadge()}>{t("clicked")}</span>}
            {item.repliedAt && <span style={crmBadge("#1FA36B")}>{t("replied")}</span>}
          </div>
        )}
        {item.kind !== "email" && item.body && <div style={{ fontSize: 12.5, color: "var(--ink-3)", marginTop: 3 }}>{item.body}</div>}
      </div>
    </div>
  );
}
const CRM_TL_FILTERS = [["all", "filterAll"], ["call", "filterCalls"], ["email", "emails"], ["meeting", "filterMeetings"], ["note", "notes"], ["linkedin", "filterLinkedin"]];
function CrmTimeline({ companyId, activities, emails, notes, contacts }) {
  const { t, lang } = useT();
  const [filter, setFilter] = useState("all");
  const cById = Object.fromEntries((contacts || []).map((c) => [c.id, c]));
  const merged = crmMergeTimeline({ activities, emails, notes });
  const filtered = filter === "all" ? merged : merged.filter((x) => x.kind === filter);
  const groups = [];
  let curKey = null;
  filtered.forEach((x) => { const k = crmDayKey(x.at); if (k !== curKey) { curKey = k; groups.push({ key: k, label: crmDayLabel(x.at, lang, t), items: [] }); } groups[groups.length - 1].items.push(x); });
  const ss = typeof selectStyle !== "undefined" ? selectStyle : { padding: "8px 12px", border: "1px solid var(--line-2)", borderRadius: 8, fontSize: 13.5 };
  return (
    <div>
      <div style={{ marginBottom: 16 }}>
        <select value={filter} onChange={(e) => setFilter(e.target.value)} style={{ ...ss, maxWidth: 200 }}>
          {CRM_TL_FILTERS.map(([f, key]) => <option key={f} value={f}>{t(key)}</option>)}
        </select>
      </div>
      {groups.length === 0 && <div style={{ fontSize: 13, color: "var(--ink-4)" }}>{t("noActivity")}</div>}
      <div style={{ display: "flex", flexDirection: "column", gap: 22 }}>
        {groups.map((g) => (
          <div key={g.key}>
            <div style={{ fontSize: 11.5, fontWeight: 700, letterSpacing: "0.05em", textTransform: "uppercase", color: "var(--ink-4)", marginBottom: 10 }}>{g.label}</div>
            <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
              {g.items.map((it) => <CrmTimelineRow key={it.id} item={it} cById={cById} />)}
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

/* compact "recent activity" preview for the Overview tab – top 3, no grouping */
function CrmActivityPreview({ activities, emails, notes, contacts, onViewAll }) {
  const { t } = useT();
  const cById = Object.fromEntries((contacts || []).map((c) => [c.id, c]));
  const top = crmMergeTimeline({ activities, emails, notes }).slice(0, 3);
  return (
    <CrmSection title={t("recentActivity")} action={<button type="button" onClick={onViewAll} style={{ border: "none", background: "transparent", color: "var(--accent)", fontWeight: 600, fontSize: 12.5, cursor: "pointer" }}>{t("viewAll")}</button>}>
      {top.length === 0 ? <div style={{ fontSize: 13, color: "var(--ink-4)" }}>{t("noActivity")}</div> : (
        <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
          {top.map((it) => <CrmTimelineRow key={it.id} item={it} cById={cById} />)}
        </div>
      )}
    </CrmSection>
  );
}

/* ---- Email history tab: full crm_emails log, grouped by month, expandable body ---- */
function CrmEmailsHistory({ company, contacts, emails, gmailConfigured, onWriteEmail }) {
  const { t, lang } = useT();
  const [openId, setOpenId] = useState(null);
  const cById = Object.fromEntries((contacts || []).map((c) => [c.id, c]));
  const groups = [];
  let curKey = null;
  (emails || []).forEach((e) => { const at = e.sentAt || e.at; const k = crmMonthKey(at); if (k !== curKey) { curKey = k; groups.push({ key: k, label: crmMonthLabel(at, lang), items: [] }); } groups[groups.length - 1].items.push(e); });
  return (
    <div>
      <div style={{ display: "flex", alignItems: "center", justifyContent: "flex-end", gap: 10, marginBottom: 6, flexWrap: "wrap" }}>
        <Button icon="mail" onClick={onWriteEmail}>{t("writeEmail")}</Button>
      </div>
      {!gmailConfigured && <div style={{ fontSize: 12, color: "var(--ink-4)", textAlign: "right", marginBottom: 14 }}>{t("gmailNotConnectedHint")}</div>}
      {emails === null ? <div style={{ color: "var(--ink-4)", marginTop: 14 }}>…</div> : emails.length === 0 ? (
        <div style={{ fontSize: 13.5, color: "var(--ink-4)", padding: "20px 0" }}>{t("noEmailsHist")}</div>
      ) : (
        <div style={{ display: "flex", flexDirection: "column", gap: 22, marginTop: gmailConfigured ? 14 : 0 }}>
          {groups.map((g) => (
            <div key={g.key}>
              <div style={{ fontSize: 11.5, fontWeight: 700, letterSpacing: "0.05em", textTransform: "uppercase", color: "var(--ink-4)", marginBottom: 8 }}>{g.label}</div>
              <div style={{ display: "flex", flexDirection: "column", borderTop: "1px solid var(--line)" }}>
                {g.items.map((e) => {
                  const open = openId === e.id;
                  const contact = e.contactId && cById[e.contactId];
                  const who = contact ? (`${contact.first || ""} ${contact.last || ""}`.trim() || contact.email) : "";
                  return (
                    <div key={e.id} style={{ borderBottom: "1px solid var(--line)" }}>
                      <button type="button" onClick={() => setOpenId(open ? null : e.id)} style={{ display: "flex", alignItems: "center", gap: 10, width: "100%", padding: "11px 4px", border: "none", background: "transparent", cursor: "pointer", textAlign: "left" }}>
                        <span title={e.direction === "outbound" ? t("outbound") : t("inbound")} style={{ fontSize: 14, fontWeight: 700, color: e.direction === "outbound" ? "var(--accent)" : "var(--ink-4)", flex: "0 0 auto", width: 16, textAlign: "center" }}>{e.direction === "outbound" ? "↑" : "↓"}</span>
                        <div style={{ flex: 1, minWidth: 0 }}>
                          <div style={{ fontSize: 13.5, fontWeight: 700, color: "var(--ink)", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{e.subject || "—"}</div>
                          <div style={{ fontSize: 12, color: "var(--ink-4)" }}>{who}{who ? " · " : ""}{crmFmtDate(e.sentAt || e.at)}{e.direction === "outbound" && e.senderEmail ? " · " + e.senderEmail : ""}</div>
                        </div>
                        <div style={{ display: "flex", gap: 6, flex: "0 0 auto" }}>
                          {e.openCount > 0 && <span style={crmBadge()}>{t("opened")} ({e.openCount})</span>}
                          {e.clickCount > 0 && <span style={crmBadge()}>{t("clicked")}</span>}
                          {e.repliedAt && <span style={crmBadge("#1FA36B")}>{t("replied")}</span>}
                        </div>
                        <Icon name={open ? "chevDown" : "chevRight"} size={14} style={{ color: "var(--ink-4)", flex: "0 0 auto" }} />
                      </button>
                      {open && <div style={{ padding: "0 4px 14px 30px", fontSize: 13, color: "var(--ink-2)", whiteSpace: "pre-wrap" }}>{e.body || "—"}</div>}
                    </div>
                  );
                })}
              </div>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

/* ============================================================
   Phase-1 increments: Import, Reports, Settings, Sequences, Compose
   ============================================================ */
const crmPct = (x) => (x || x === 0 ? Math.round(x * 100) + "%" : "—");
function crmCsvParse(text) {
  // minimal CSV: handles quoted fields, commas, and \r\n
  const rows = []; let row = [], cur = "", q = false;
  for (let i = 0; i < text.length; i++) {
    const ch = text[i];
    if (q) { if (ch === '"') { if (text[i + 1] === '"') { cur += '"'; i++; } else q = false; } else cur += ch; }
    else if (ch === '"') q = true;
    else if (ch === ",") { row.push(cur); cur = ""; }
    else if (ch === "\n") { row.push(cur); rows.push(row); row = []; cur = ""; }
    else if (ch === "\r") { /* skip */ }
    else cur += ch;
  }
  if (cur.length || row.length) { row.push(cur); rows.push(row); }
  return rows.filter((r) => r.some((c) => (c || "").trim() !== ""));
}

/* ---- Mobile "More" menu ---- */
function CrmMore({ navigate, secondaryNav }) {
  const { t } = useT();
  return (
    <div style={{ maxWidth: 520, margin: "0 auto", display: "flex", flexDirection: "column", gap: 8 }}>
      <h1 style={{ fontSize: 22, margin: "0 0 8px" }}>{t("more")}</h1>
      {secondaryNav.map((n) => (
        <button key={n.id} type="button" onClick={() => navigate(n.to)} style={{ display: "flex", alignItems: "center", gap: 12, padding: "14px 16px", border: "1px solid var(--line)", borderRadius: "var(--r-md)", background: "var(--surface)", cursor: "pointer", textAlign: "left", fontSize: 15, fontWeight: 600, color: "var(--ink)", boxShadow: "var(--sh-sm)" }}>
          <Icon name={n.icon} size={19} style={{ color: "var(--accent)" }} /> {n.label}
          <div style={{ flex: 1 }} /><Icon name="chevRight" size={16} style={{ color: "var(--ink-4)" }} />
        </button>
      ))}
    </div>
  );
}

/* ---- CSV Import (#7): paste/upload → map → dedup + suppression + freemail flag ---- */
function CrmImportModal({ onClose, onDone }) {
  const { t } = useT();
  const [rows, setRows] = useState(null);      // parsed rows incl header
  const [map, setMap] = useState({});          // field -> column index
  const [busy, setBusy] = useState(false);
  const [result, setResult] = useState(null);  // {imported, skipped}
  const ss = typeof selectStyle !== "undefined" ? selectStyle : { width: "100%", padding: "9px 12px", border: "1px solid var(--line-2)", borderRadius: 8, fontSize: 14 };
  const FIELDS = [["name", t("name")], ["website", t("website")], ["industry", t("industry")], ["leadSource", t("leadSource")], ["first", t("firstName")], ["last", t("lastName")], ["title", t("role")], ["email", t("email")], ["phone", t("phone")]];
  function ingest(text) {
    const parsed = crmCsvParse(text);
    if (parsed.length < 2) { setRows(null); return; }
    setRows(parsed);
    const hdr = parsed[0].map((h) => (h || "").toLowerCase().trim());
    const guess = {}; const alias = { name: ["name", "company", "firma", "unternehmen", "account"], website: ["website", "url", "domain", "web"], industry: ["industry", "branche"], leadSource: ["source", "lead source", "quelle", "leadquelle"], first: ["first", "first name", "vorname"], last: ["last", "last name", "nachname"], title: ["title", "role", "position", "rolle"], email: ["email", "e-mail", "mail"], phone: ["phone", "telefon", "tel", "mobile"] };
    FIELDS.forEach(([f]) => { const idx = hdr.findIndex((h) => (alias[f] || [f]).some((a) => h === a || h.includes(a))); if (idx >= 0) guess[f] = idx; });
    setMap(guess);
  }
  async function run() {
    if (!rows || busy) return; setBusy(true);
    const body = rows.slice(1);
    const col = (r, f) => (map[f] != null && map[f] >= 0 ? (r[map[f]] || "").trim() : "");
    const emails = body.map((r) => col(r, "email")).filter(Boolean);
    let suppressed = new Set();
    try { suppressed = await MesperDB.crmCheckSuppressed(emails); } catch (_) {}
    let imported = 0, skipped = 0;
    const seenDomains = {};
    for (const r of body) {
      const name = col(r, "name"); const website = col(r, "website"); const email = col(r, "email");
      const domain = domainFromUrl(website) || domainFromEmail(email);
      if (!name && !domain && !email) { skipped++; continue; }
      if (email && suppressed.has(email.toLowerCase())) { skipped++; continue; }
      try {
        let companyId = null;
        if (domain) {
          if (seenDomains[domain]) companyId = seenDomains[domain];
          else { const ex = await MesperDB.crmFindCompanyByDomain(domain); if (ex) companyId = ex.id; }
        }
        if (!companyId) {
          const c = await MesperDB.crmCreateCompany({ name: name || domain || email, website, domain, industry: col(r, "industry"), leadSource: col(r, "leadSource") || "Import", status: "new" });
          companyId = c.id; if (domain) seenDomains[domain] = companyId;
          if (domain && MesperDB.fetchLogo) MesperDB.fetchLogo(domain).then((x) => { if (x && x.ok && x.dataUrl) MesperDB.crmUpdateCompany(companyId, { logoUrl: x.dataUrl }).catch(() => {}); }).catch(() => {});
        } else if (domain) { seenDomains[domain] = companyId; }
        const first = col(r, "first"), last = col(r, "last");
        if (email || first || last) {
          const isBiz = email ? !FREEMAIL.test(email) : true;
          await MesperDB.crmCreateContact({ companyId, first, last, title: col(r, "title"), email, phone: col(r, "phone"), isBusiness: isBiz });
        }
        imported++;
      } catch (e) { skipped++; }
    }
    setResult({ imported, skipped }); setBusy(false);
  }
  return (
    <Modal onClose={onClose} width={620}>
      <div style={{ padding: "20px 24px", borderBottom: "1px solid var(--line)" }}><h2 style={{ fontSize: 19, margin: 0 }}>{t("importLeads")}</h2></div>
      <div style={{ padding: "18px 24px", display: "grid", gap: 14, maxHeight: "62vh", overflowY: "auto" }}>
        {result ? (
          <div style={{ textAlign: "center", padding: "20px 0" }}>
            <div style={{ fontSize: 40, marginBottom: 8 }}>✓</div>
            <div style={{ fontSize: 16, fontWeight: 700 }}>{result.imported} {t("imported")}</div>
            {result.skipped > 0 && <div style={{ fontSize: 13.5, color: "var(--ink-4)", marginTop: 4 }}>{result.skipped} {t("skipped")}</div>}
          </div>
        ) : rows ? (
          <>
            <div style={{ fontSize: 13, color: "var(--ink-3)" }}>{t("mapColumns")} · {rows.length - 1} {t("preview")}</div>
            <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
              {FIELDS.map(([f, lbl]) => (
                <div key={f}>
                  <div style={fieldLabelC()}>{lbl}</div>
                  <select value={map[f] ?? -1} onChange={(e) => setMap({ ...map, [f]: Number(e.target.value) })} style={ss}>
                    <option value={-1}>{t("ignore")}</option>
                    {rows[0].map((h, i) => <option key={i} value={i}>{h || t("column") + " " + (i + 1)}</option>)}
                  </select>
                </div>
              ))}
            </div>
          </>
        ) : (
          <>
            <div style={{ fontSize: 13, color: "var(--ink-3)" }}>{t("pasteCsv")}</div>
            <textarea autoFocus onChange={(e) => e.target.value.includes(",") && ingest(e.target.value)} rows={7} placeholder={"Company,Website,Email,First,Last,Phone\nAcme GmbH,acme.de,anna@acme.de,Anna,Berg,+49…"} style={{ ...ss, resize: "vertical", fontFamily: "var(--font-mono, monospace)", fontSize: 12.5 }} />
            <label style={{ display: "inline-flex", alignItems: "center", gap: 8, fontSize: 13, color: "var(--accent)", cursor: "pointer", fontWeight: 600 }}>
              <Icon name="upload" size={15} /> {t("importCsv")}
              <input type="file" accept=".csv,text/csv" style={{ display: "none" }} onChange={(e) => { const file = e.target.files[0]; if (!file) return; const fr = new FileReader(); fr.onload = () => ingest(String(fr.result)); fr.readAsText(file); }} />
            </label>
          </>
        )}
      </div>
      <div style={{ padding: "14px 24px", borderTop: "1px solid var(--line)", display: "flex", justifyContent: "flex-end", gap: 10 }}>
        {result ? <Button icon="check" onClick={() => { onDone && onDone(); onClose(); }}>OK</Button> : (
          <>
            <Button variant="ghost" onClick={onClose}>{t("cancel")}</Button>
            {rows && <Button icon="upload" onClick={run} disabled={busy}>{busy ? "…" : t("startImport")}</Button>}
          </>
        )}
      </div>
    </Modal>
  );
}

/* ---- Reports (#10): activity KPIs + template performance ---- */
function CrmReports() {
  const { t } = useT();
  const [range, setRange] = useState(30);
  const [data, setData] = useState(null);
  const [tpl, setTpl] = useState(null);
  useEffect(() => {
    const to = new Date(); const from = new Date(Date.now() - range * 864e5);
    setData(null);
    MesperDB.crmReportSummary(from.toISOString(), to.toISOString()).then(setData).catch(() => setData({ activities: [], tasks: [], opps: [], emails: [] }));
    MesperDB.crmReportTemplates().then(setTpl).catch(() => setTpl([]));
  }, [range]);
  const A = (data && data.activities) || [];
  const calls = A.filter((a) => a.type === "call").length;
  const connects = A.filter((a) => a.type === "call" && a.outcome === "connected").length;
  const meetings = A.filter((a) => a.type === "meeting" || a.outcome === "meetingBooked").length;
  const emails = (data && data.emails) || [];
  const emailsSent = emails.length;
  const replies = emails.filter((e) => e.replied_at).length;
  const opps = (data && data.opps) || [];
  const openPipe = opps.filter((o) => o.status === "open").reduce((s, o) => s + (Number(o.value) || 0), 0);
  const won = opps.filter((o) => o.status === "won").reduce((s, o) => s + (Number(o.value) || 0), 0);
  const byRep = {}; A.forEach((a) => { const k = a.created_by || "—"; (byRep[k] = byRep[k] || { calls: 0, connects: 0, meetings: 0 }); if (a.type === "call") byRep[k].calls++; if (a.type === "call" && a.outcome === "connected") byRep[k].connects++; if (a.type === "meeting" || a.outcome === "meetingBooked") byRep[k].meetings++; });
  const repName = (id) => { const p = (typeof VP_DATA !== "undefined" && VP_DATA.byId && VP_DATA.byId[id]) || null; return p ? `${p.first || ""} ${p.last || ""}`.trim() || p.email : "—"; };
  const tile = (label, val, sub) => (
    <div style={{ border: "1px solid var(--line)", borderRadius: "var(--r-md)", background: "var(--surface)", padding: "14px 16px" }}>
      <div style={{ fontSize: 12, fontWeight: 700, letterSpacing: "0.03em", textTransform: "uppercase", color: "var(--ink-4)" }}>{label}</div>
      <div style={{ fontSize: 26, fontWeight: 800, color: "var(--ink)", fontFamily: "var(--font-display)", marginTop: 4 }}>{val}</div>
      {sub && <div style={{ fontSize: 12, color: "var(--ink-4)", marginTop: 2 }}>{sub}</div>}
    </div>
  );
  const eur = (n) => "€" + (n >= 1000 ? Math.round(n / 1000) + "k" : Math.round(n));
  return (
    <div style={{ maxWidth: 920, margin: "0 auto" }}>
      <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 16, flexWrap: "wrap" }}>
        <h1 style={{ fontSize: 22, margin: 0 }}>{t("reports")}</h1>
        <div style={{ flex: 1 }} />
        <div style={{ display: "inline-flex", border: "1px solid var(--line-2)", borderRadius: "var(--r-pill)", overflow: "hidden" }}>
          {[[7, t("r7")], [30, t("r30")], [90, t("r90")]].map(([r, lbl]) => (
            <button key={r} type="button" onClick={() => setRange(r)} style={{ border: "none", background: range === r ? "var(--accent)" : "transparent", color: range === r ? "#fff" : "var(--ink-3)", padding: "6px 12px", fontSize: 12.5, fontWeight: 700, cursor: "pointer" }}>{lbl}</button>
          ))}
        </div>
      </div>
      {data === null ? <div style={{ color: "var(--ink-4)" }}>…</div> : (
        <div style={{ display: "grid", gap: 18 }}>
          <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(140px, 1fr))", gap: 12 }}>
            {tile(t("repCalls"), calls, t("repConnects") + ": " + connects)}
            {tile(t("repConnectRate"), calls ? crmPct(connects / calls) : "—")}
            {tile(t("repMeetings"), meetings)}
            {tile(t("repEmails"), emailsSent, t("repReplies") + ": " + replies)}
            {tile(t("repPipeline"), eur(openPipe))}
            {tile(t("repWon"), eur(won))}
          </div>

          {Object.keys(byRep).length > 0 && (
            <div style={{ border: "1px solid var(--line)", borderRadius: "var(--r-md)", background: "var(--surface)", overflow: "hidden" }}>
              <div style={{ padding: "12px 16px", borderBottom: "1px solid var(--line)", fontSize: 13.5, fontWeight: 700 }}>{t("companies")} · {t("repCalls")}</div>
              <div style={{ overflowX: "auto" }}>
                <table style={{ width: "100%", borderCollapse: "collapse", fontSize: 13 }}>
                  <thead><tr style={{ color: "var(--ink-4)", textAlign: "left" }}>{["", t("repCalls"), t("repConnects"), t("repMeetings")].map((h, i) => <th key={i} style={{ padding: "8px 16px", fontWeight: 600, fontSize: 11.5, textTransform: "uppercase", letterSpacing: "0.03em", textAlign: i === 0 ? "left" : "right" }}>{h}</th>)}</tr></thead>
                  <tbody>
                    {Object.entries(byRep).sort((a, b) => b[1].calls - a[1].calls).map(([id, r]) => (
                      <tr key={id} style={{ borderTop: "1px solid var(--line)" }}>
                        <td style={{ padding: "9px 16px", fontWeight: 600 }}>{repName(id)}</td>
                        <td style={{ padding: "9px 16px", textAlign: "right" }}>{r.calls}</td>
                        <td style={{ padding: "9px 16px", textAlign: "right" }}>{r.connects}</td>
                        <td style={{ padding: "9px 16px", textAlign: "right" }}>{r.meetings}</td>
                      </tr>
                    ))}
                  </tbody>
                </table>
              </div>
            </div>
          )}

          <div style={{ border: "1px solid var(--line)", borderRadius: "var(--r-md)", background: "var(--surface)", overflow: "hidden" }}>
            <div style={{ padding: "12px 16px", borderBottom: "1px solid var(--line)", fontSize: 13.5, fontWeight: 700 }}>{t("repTemplatePerf")}</div>
            {tpl === null ? <div style={{ padding: 16, color: "var(--ink-4)" }}>…</div> : tpl.length === 0 ? <div style={{ padding: 16, color: "var(--ink-4)", fontSize: 13.5 }}>{t("noData")}</div> : (
              <div style={{ overflowX: "auto" }}>
                <table style={{ width: "100%", borderCollapse: "collapse", fontSize: 13, minWidth: 520 }}>
                  <thead><tr style={{ color: "var(--ink-4)" }}>{[t("templates"), t("sent"), t("openRate"), t("replyRate")].map((h, i) => <th key={i} style={{ padding: "8px 16px", fontWeight: 600, fontSize: 11.5, textTransform: "uppercase", letterSpacing: "0.03em", textAlign: i === 0 ? "left" : "right" }}>{h}</th>)}</tr></thead>
                  <tbody>
                    {tpl.map((r) => (
                      <tr key={r.id} style={{ borderTop: "1px solid var(--line)" }}>
                        <td style={{ padding: "9px 16px", fontWeight: 600 }}>{r.name}{r.category && <span style={{ color: "var(--ink-4)", fontWeight: 500 }}> · {r.category}</span>}</td>
                        <td style={{ padding: "9px 16px", textAlign: "right" }}>{r.sent}</td>
                        <td style={{ padding: "9px 16px", textAlign: "right", color: r.openRate >= 0.4 ? "#1FA36B" : "var(--ink-2)" }}>{crmPct(r.openRate)}</td>
                        <td style={{ padding: "9px 16px", textAlign: "right", color: r.replyRate >= 0.1 ? "#1FA36B" : "var(--ink-2)" }}>{crmPct(r.replyRate)}</td>
                      </tr>
                    ))}
                  </tbody>
                </table>
              </div>
            )}
          </div>
        </div>
      )}
    </div>
  );
}

/* ---- Settings + Compliance (#12) + Templates (#8) ---- */
function CrmCard({ title, desc, children }) {
  return (
    <div style={{ border: "1px solid var(--line)", borderRadius: "var(--r-md)", background: "var(--surface)", padding: "16px 18px" }}>
      <div style={{ fontSize: 14.5, fontWeight: 700, color: "var(--ink)" }}>{title}</div>
      {desc && <div style={{ fontSize: 12.5, color: "var(--ink-4)", marginTop: 3, marginBottom: 12, lineHeight: 1.45 }}>{desc}</div>}
      {!desc && <div style={{ height: 12 }} />}
      {children}
    </div>
  );
}
function CrmSettings({ user }) {
  const { t } = useT();
  const [s, setS] = useState(null);
  const [saved, setSaved] = useState(false);
  const [gmail, setGmail] = useState({ configured: false, calendar: false, account: "" });
  const ss = typeof selectStyle !== "undefined" ? selectStyle : { width: "100%", padding: "9px 12px", border: "1px solid var(--line-2)", borderRadius: 8, fontSize: 14 };
  useEffect(() => { MesperDB.crmGetSettings().then(setS).catch(() => setS({ impressum: "", defaultTracking: false, calBookingUrl: "", businessStart: "09:00", businessEnd: "18:00" })); }, []);
  useEffect(() => { MesperDB.crmGmailStatus().then(setGmail).catch(() => {}); }, []);
  async function save(patch) { const next = { ...s, ...patch }; setS(next); try { await MesperDB.crmUpdateSettings(patch); setSaved(true); setTimeout(() => setSaved(false), 1500); } catch (e) {} }
  if (!s) return <div style={{ color: "var(--ink-4)" }}>…</div>;
  return (
    <div style={{ maxWidth: 720, margin: "0 auto", display: "grid", gap: 16 }}>
      <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
        <h1 style={{ fontSize: 22, margin: 0 }}>{t("settings")}</h1>
        {saved && <span style={{ fontSize: 12.5, color: "#1FA36B", fontWeight: 600 }}>✓</span>}
      </div>

      <CrmCard title={t("impressum")} desc={t("impressumDesc")}>
        <textarea defaultValue={s.impressum} onBlur={(e) => save({ impressum: e.target.value })} rows={4} placeholder={"Mesper GmbH · Musterstr. 1 · 70173 Stuttgart\nGF: … · HRB … · {{firstName}}"} style={{ ...ss, resize: "vertical", fontFamily: "var(--font-body)" }} />
      </CrmCard>

      <CrmCard title={t("businessHours")} desc={t("businessHoursDesc")}>
        <div style={{ display: "flex", gap: 14, alignItems: "center" }}>
          <label style={{ fontSize: 13, color: "var(--ink-3)" }}>{t("from")} <input type="time" defaultValue={s.businessStart} onBlur={(e) => save({ businessStart: e.target.value })} style={{ ...ss, width: 120, display: "inline-block", marginLeft: 6 }} /></label>
          <label style={{ fontSize: 13, color: "var(--ink-3)" }}>{t("to")} <input type="time" defaultValue={s.businessEnd} onBlur={(e) => save({ businessEnd: e.target.value })} style={{ ...ss, width: 120, display: "inline-block", marginLeft: 6 }} /></label>
        </div>
      </CrmCard>

      <CrmCard title={t("tracking")} desc={t("trackingDesc")}>
        <label style={{ display: "inline-flex", alignItems: "center", gap: 10, cursor: "pointer", fontSize: 14, fontWeight: 600, color: "var(--ink-2)" }}>
          <input type="checkbox" checked={!!s.defaultTracking} onChange={(e) => save({ defaultTracking: e.target.checked })} style={{ width: 17, height: 17, accentColor: "var(--accent)" }} />
          {t("trackingDefault")}
        </label>
      </CrmCard>

      <CrmSuppression />
      <CrmTemplates />
      <CrmIntegrations calUrl={s.calBookingUrl} onCal={(v) => save({ calBookingUrl: v })} gmail={gmail} />
    </div>
  );
}

function CrmSuppression() {
  const { t } = useT();
  const [list, setList] = useState(null);
  const [v, setV] = useState("");
  const ss = typeof selectStyle !== "undefined" ? selectStyle : { padding: "9px 12px", border: "1px solid var(--line-2)", borderRadius: 8, fontSize: 14 };
  function reload() { MesperDB.crmListSuppression().then(setList).catch(() => setList([])); }
  useEffect(() => { reload(); }, []);
  async function add() { const val = v.trim().toLowerCase(); if (!val) return; const isDomain = !val.includes("@"); try { await MesperDB.crmAddSuppression(isDomain ? { domain: val, reason: "manual" } : { email: val, reason: "manual" }); } catch (e) {} setV(""); reload(); }
  async function del(id) { try { await MesperDB.crmRemoveSuppression(id); } catch (e) {} reload(); }
  return (
    <CrmCard title={t("suppression")} desc={t("suppressionDesc")}>
      <div style={{ display: "flex", gap: 8, marginBottom: (list && list.length) ? 12 : 0 }}>
        <input value={v} onChange={(e) => setV(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter") add(); }} placeholder={t("addEmail")} style={{ ...ss, flex: 1 }} />
        <Button size="sm" icon="plus" onClick={add} />
      </div>
      <div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
        {(list || []).map((r) => (
          <div key={r.id} style={{ display: "flex", alignItems: "center", gap: 10, padding: "7px 11px", background: "var(--surface-2)", border: "1px solid var(--line)", borderRadius: "var(--r-sm)" }}>
            <Icon name="x" size={13} style={{ color: "var(--st-declined, #E5484D)" }} />
            <span style={{ fontSize: 13, fontWeight: 600, color: "var(--ink-2)" }}>{r.email || r.domain}</span>
            {r.reason && <span style={{ fontSize: 11.5, color: "var(--ink-4)" }}>· {r.reason}</span>}
            <div style={{ flex: 1 }} />
            <button type="button" onClick={() => del(r.id)} style={{ border: "none", background: "transparent", cursor: "pointer", color: "var(--ink-4)", padding: 0 }}><Icon name="trash" size={14} /></button>
          </div>
        ))}
      </div>
    </CrmCard>
  );
}

function CrmTemplates() {
  const { t } = useT();
  const [list, setList] = useState(null);
  const [edit, setEdit] = useState(null); // template obj or {} for new
  function reload() { MesperDB.crmListTemplates().then(setList).catch(() => setList([])); }
  useEffect(() => { reload(); }, []);
  return (
    <CrmCard title={t("templates")} desc={t("templatesDesc")}>
      <div style={{ display: "flex", flexDirection: "column", gap: 7 }}>
        {(list || []).length === 0 && <div style={{ fontSize: 13, color: "var(--ink-4)", marginBottom: 8 }}>{t("noTemplates")}</div>}
        {(list || []).map((tp) => (
          <button key={tp.id} type="button" onClick={() => setEdit(tp)} style={{ display: "flex", alignItems: "center", gap: 10, padding: "10px 12px", border: "1px solid var(--line)", borderRadius: "var(--r-sm)", background: "var(--surface-2)", cursor: "pointer", textAlign: "left" }}>
            <Icon name="mail" size={15} style={{ color: "var(--accent)" }} />
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ fontSize: 13.5, fontWeight: 700, color: "var(--ink)" }}>{tp.name} {tp.category && <span style={{ fontWeight: 500, color: "var(--ink-4)" }}>· {tp.category}</span>}</div>
              <div style={{ fontSize: 12, color: "var(--ink-4)", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{tp.subject}</div>
            </div>
          </button>
        ))}
      </div>
      <div style={{ marginTop: 10 }}><Button size="sm" variant="ghost" icon="plus" onClick={() => setEdit({})}>{t("newTemplate")}</Button></div>
      {edit && <CrmTemplateModal template={edit.id ? edit : null} onClose={() => setEdit(null)} onSaved={() => { setEdit(null); reload(); }} />}
    </CrmCard>
  );
}
function CrmTemplateModal({ template, onClose, onSaved }) {
  const { t } = useT();
  const [name, setName] = useState(template?.name || "");
  const [category, setCategory] = useState(template?.category || "");
  const [subject, setSubject] = useState(template?.subject || "");
  const [body, setBody] = useState(template?.body || "");
  const [busy, setBusy] = useState(false);
  const ss = typeof selectStyle !== "undefined" ? selectStyle : { width: "100%", padding: "9px 12px", border: "1px solid var(--line-2)", borderRadius: 8, fontSize: 14 };
  async function save() { if (!name.trim() || busy) return; setBusy(true); try { if (template) await MesperDB.crmUpdateTemplate(template.id, { name, category, subject, body }); else await MesperDB.crmCreateTemplate({ name, category, subject, body }); onSaved(); } catch (e) { setBusy(false); } }
  async function del() { if (!template) return; try { await MesperDB.crmDeleteTemplate(template.id); } catch (e) {} onSaved(); }
  return (
    <Modal onClose={onClose} width={560}>
      <div style={{ padding: "18px 22px", borderBottom: "1px solid var(--line)" }}><h2 style={{ fontSize: 18, margin: 0 }}>{template ? t("edit") : t("newTemplate")}</h2></div>
      <div style={{ padding: "16px 22px", display: "grid", gap: 12 }}>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 160px", gap: 10 }}>
          <div><div style={fieldLabelC()}>{t("templateName")}</div><input autoFocus value={name} onChange={(e) => setName(e.target.value)} style={ss} /></div>
          <div><div style={fieldLabelC()}>{t("category")}</div><input value={category} onChange={(e) => setCategory(e.target.value)} style={ss} placeholder="Follow-up" /></div>
        </div>
        <div><div style={fieldLabelC()}>{t("subjectLine")}</div><input value={subject} onChange={(e) => setSubject(e.target.value)} style={ss} placeholder="Kurze Frage, {{firstName}}" /></div>
        <div><div style={fieldLabelC()}>{t("emailBody")}</div><textarea value={body} onChange={(e) => setBody(e.target.value)} rows={7} style={{ ...ss, resize: "vertical", fontFamily: "var(--font-body)" }} placeholder={"Hi {{firstName}},\n\n…\n\nViele Grüße"} /></div>
      </div>
      <div style={{ padding: "13px 22px", borderTop: "1px solid var(--line)", display: "flex", gap: 10, alignItems: "center" }}>
        {template && <button type="button" onClick={del} style={{ border: "none", background: "transparent", color: "var(--st-declined, #E5484D)", cursor: "pointer", fontWeight: 600, fontSize: 13, display: "inline-flex", alignItems: "center", gap: 5 }}><Icon name="trash" size={14} /> {t("delete")}</button>}
        <div style={{ flex: 1 }} />
        <Button variant="ghost" onClick={onClose}>{t("cancel")}</Button>
        <Button icon="check" onClick={save} disabled={busy || !name.trim()}>{t("saveTemplate")}</Button>
      </div>
    </Modal>
  );
}

function CrmIntegrations({ calUrl, onCal, gmail }) {
  const { t } = useT();
  const [showSetup, setShowSetup] = useState(false);
  const ss = typeof selectStyle !== "undefined" ? selectStyle : { width: "100%", padding: "9px 12px", border: "1px solid var(--line-2)", borderRadius: 8, fontSize: 14 };
  const gmailOk = !!(gmail && gmail.configured);
  const calOk = !!(gmail && gmail.calendar);
  const rowBox = { border: "1px solid var(--line)", borderRadius: "var(--r-sm)", background: "var(--surface-2)", overflow: "hidden" };
  const staticRow = { display: "flex", alignItems: "center", gap: 10, padding: "9px 11px", border: "1px solid var(--line)", borderRadius: "var(--r-sm)", background: "var(--surface-2)" };
  return (
    <CrmCard title={t("integrations")}>
      <div style={{ display: "flex", flexDirection: "column", gap: 7, marginBottom: 12 }}>
        {/* Gmail — real status ping via crmGmailStatus() */}
        <div style={rowBox}>
          {gmailOk ? (
            <div style={{ display: "flex", alignItems: "center", gap: 10, padding: "9px 11px" }}>
              <Icon name="mail" size={16} style={{ color: "var(--ink-3)" }} />
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ fontSize: 13.5, fontWeight: 600, color: "var(--ink-2)" }}>Google Workspace (Gmail)</div>
                <div style={{ fontSize: 12, color: "var(--ink-4)", marginTop: 1 }}>{t("gmailConnectedDesc")} {gmail.account}</div>
              </div>
              <span style={{ fontSize: 11.5, fontWeight: 700, color: "#1FA36B", flex: "0 0 auto" }}>{t("connected2")}</span>
            </div>
          ) : (
            <button type="button" onClick={() => setShowSetup((v) => !v)} style={{ display: "flex", alignItems: "center", gap: 10, padding: "9px 11px", width: "100%", border: "none", background: "transparent", cursor: "pointer", textAlign: "left" }}>
              <Icon name="mail" size={16} style={{ color: "var(--ink-3)" }} />
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ fontSize: 13.5, fontWeight: 600, color: "var(--ink-2)" }}>Google Workspace (Gmail)</div>
                <div style={{ fontSize: 12, color: "var(--ink-4)", marginTop: 1 }}>{t("setupRequired")}</div>
              </div>
              <span style={{ fontSize: 11.5, fontWeight: 700, color: "var(--ink-4)", flex: "0 0 auto" }}>{t("notConnected")}</span>
              <Icon name={showSetup ? "chevDown" : "chevRight"} size={14} style={{ color: "var(--ink-4)", flex: "0 0 auto" }} />
            </button>
          )}
          {!gmailOk && showSetup && (
            <div style={{ padding: "0 11px 12px 41px", fontSize: 12, color: "var(--ink-3)", lineHeight: 1.6 }}>
              <ol style={{ margin: 0, paddingLeft: 16 }}>
                <li>{t("gmailStep1")}</li>
                <li>{t("gmailStep2")}</li>
                <li>{t("gmailStep3")}</li>
              </ol>
              <div style={{ marginTop: 6, color: "var(--ink-4)" }}>{t("gmailSetupDocsRef")}</div>
            </div>
          )}
        </div>

        {/* Calendar — real status ping via crmGmailStatus() (calendar delegation for own account) */}
        <div style={staticRow}>
          <Icon name="calendar" size={16} style={{ color: "var(--ink-3)" }} />
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ fontSize: 13.5, fontWeight: 600, color: "var(--ink-2)" }}>Google Calendar</div>
            <div style={{ fontSize: 12, color: "var(--ink-4)", marginTop: 1 }}>{calOk ? t("calConnectedDesc") : t("calNotSetupDesc")}</div>
          </div>
          <span style={{ fontSize: 11.5, fontWeight: 700, color: calOk ? "#1FA36B" : "var(--ink-4)", flex: "0 0 auto" }}>{calOk ? t("connected2") : t("notSetUp")}</span>
        </div>

        {/* Slack — real, deployed */}
        <div style={staticRow}>
          <Icon name="inbox" size={16} style={{ color: "var(--ink-3)" }} />
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ fontSize: 13.5, fontWeight: 600, color: "var(--ink-2)" }}>Slack</div>
            <div style={{ fontSize: 12, color: "var(--ink-4)", marginTop: 1 }}>{t("slackDesc")}</div>
          </div>
          <span style={{ fontSize: 11.5, fontWeight: 700, color: "#1FA36B", flex: "0 0 auto" }}>{t("connected2")}</span>
        </div>
      </div>
      <div><div style={fieldLabelC()}>Cal.com / Reclaim URL</div><input defaultValue={calUrl} onBlur={(e) => onCal(e.target.value)} style={ss} placeholder="https://cal.com/mesper/intro" /></div>
    </CrmCard>
  );
}

/* ---- Sequences (#9): list + builder + enrollments ---- */
function CrmSequences({ navigate }) {
  const { t } = useT();
  const [list, setList] = useState(null);
  const [adding, setAdding] = useState(false);
  const [name, setName] = useState("");
  function reload() { MesperDB.crmListSequences().then(setList).catch(() => setList([])); }
  useEffect(() => { reload(); }, []);
  async function create() { if (!name.trim()) return; try { const s = await MesperDB.crmCreateSequence({ name: name.trim() }); setAdding(false); setName(""); navigate("/crm/sequences/" + s.id); } catch (e) {} }
  return (
    <div style={{ maxWidth: 760, margin: "0 auto" }}>
      <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 8 }}>
        <h1 style={{ fontSize: 22, margin: 0 }}>{t("sequences")}</h1>
        <div style={{ flex: 1 }} />
        <Button icon="plus" onClick={() => setAdding(true)}>{t("newSequence")}</Button>
      </div>
      <div style={{ fontSize: 12.5, color: "var(--ink-4)", marginBottom: 16 }}>{t("followUpOnly")}</div>
      {adding && (
        <div style={{ display: "flex", gap: 8, marginBottom: 14 }}>
          <input autoFocus value={name} onChange={(e) => setName(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter") create(); }} placeholder={t("sequenceName")} style={{ ...(typeof selectStyle !== "undefined" ? selectStyle : {}), flex: 1 }} />
          <Button icon="check" onClick={create}>{t("create")}</Button>
          <Button variant="ghost" onClick={() => setAdding(false)}>{t("cancel")}</Button>
        </div>
      )}
      {list === null ? <div style={{ color: "var(--ink-4)" }}>…</div> : list.length === 0 && !adding ? <div style={{ color: "var(--ink-4)", fontSize: 14.5, padding: "20px 0" }}>—</div> : (
        <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
          {list.map((s) => (
            <button key={s.id} type="button" onClick={() => navigate("/crm/sequences/" + s.id)} style={{ display: "flex", alignItems: "center", gap: 12, padding: "13px 15px", border: "1px solid var(--line)", borderRadius: "var(--r-md)", background: "var(--surface)", cursor: "pointer", textAlign: "left", boxShadow: "var(--sh-sm)" }}>
              <Icon name="repeat" size={18} style={{ color: s.active ? "var(--accent)" : "var(--ink-4)" }} />
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ fontSize: 15, fontWeight: 700, color: "var(--ink)" }}>{s.name}</div>
                {s.description && <div style={{ fontSize: 12.5, color: "var(--ink-4)" }}>{s.description}</div>}
              </div>
              <span style={{ fontSize: 11, fontWeight: 700, color: s.active ? "#1FA36B" : "var(--ink-4)", textTransform: "uppercase" }}>{s.active ? t("active") : t("paused")}</span>
            </button>
          ))}
        </div>
      )}
    </div>
  );
}

function CrmSequenceDetail({ id, navigate }) {
  const { t } = useT();
  const [seq, setSeq] = useState(null);
  const [steps, setSteps] = useState([]);
  const [enr, setEnr] = useState([]);
  const [templates, setTemplates] = useState([]);
  const ss = typeof selectStyle !== "undefined" ? selectStyle : { padding: "8px 10px", border: "1px solid var(--line-2)", borderRadius: 8, fontSize: 13.5 };
  function reload() {
    Promise.all([MesperDB.crmListSequences(), MesperDB.crmListSteps(id), MesperDB.crmListEnrollments(id), MesperDB.crmListTemplates()])
      .then(([seqs, st, en, tp]) => { setSeq(seqs.find((s) => s.id === id) || false); setSteps(st); setEnr(en); setTemplates(tp); })
      .catch(() => setSeq(false));
  }
  useEffect(() => { reload(); }, [id]);
  async function addStep(kind) { try { await MesperDB.crmCreateStep({ sequenceId: id, kind, sort: steps.length, waitDays: steps.length ? 2 : 0 }); } catch (e) {} reload(); }
  async function patchStep(sid, p) { setSteps((cur) => cur.map((s) => s.id === sid ? { ...s, ...p } : s)); try { await MesperDB.crmUpdateStep(sid, p); } catch (e) {} }
  async function delStep(sid) { try { await MesperDB.crmDeleteStep(sid); } catch (e) {} reload(); }
  async function toggleActive() { if (!seq) return; try { await MesperDB.crmUpdateSequence(id, { active: !seq.active }); } catch (e) {} reload(); }
  if (seq === null) return <div style={{ color: "var(--ink-4)" }}>…</div>;
  if (seq === false) return <div style={{ color: "var(--ink-4)" }}>—</div>;
  const KINDS = [["email", t("stepEmail"), "mail"], ["call_task", t("stepCall"), "clock"], ["linkedin_task", t("stepLinkedin"), "users"], ["wait", t("stepWait"), "clock"]];
  const kindLabel = (k) => (KINDS.find((x) => x[0] === k) || [null, k])[1];
  const active = enr.filter((e) => e.status === "active").length;
  return (
    <div style={{ maxWidth: 760, margin: "0 auto", display: "grid", gap: 18 }}>
      <button type="button" onClick={() => navigate("/crm/sequences")} style={{ alignSelf: "flex-start", border: "none", background: "transparent", color: "var(--ink-3)", cursor: "pointer", fontSize: 13, fontWeight: 600, display: "inline-flex", alignItems: "center", gap: 5, padding: 0 }}><Icon name="arrowLeft" size={14} /> {t("sequences")}</button>
      <div style={{ display: "flex", alignItems: "center", gap: 12, flexWrap: "wrap" }}>
        <h1 style={{ fontSize: 23, margin: 0 }}>{seq.name}</h1>
        <button type="button" onClick={toggleActive} style={{ border: "1px solid var(--line-2)", background: "var(--surface)", borderRadius: "var(--r-pill)", padding: "5px 12px", fontSize: 12, fontWeight: 700, cursor: "pointer", color: seq.active ? "#1FA36B" : "var(--ink-4)", display: "inline-flex", alignItems: "center", gap: 6 }}>
          <Icon name={seq.active ? "pause" : "play"} size={13} /> {seq.active ? t("active") : t("paused")}
        </button>
        <div style={{ flex: 1 }} />
        <span style={{ fontSize: 13, color: "var(--ink-4)" }}>{active} {t("active").toLowerCase()} · {enr.length} {t("enrollments").toLowerCase()}</span>
      </div>

      {/* steps */}
      <CrmCard title={t("steps")}>
        <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
          {steps.map((st, i) => (
            <div key={st.id} style={{ display: "flex", alignItems: "flex-start", gap: 10, padding: "11px 12px", border: "1px solid var(--line)", borderRadius: "var(--r-md)", background: "var(--surface-2)" }}>
              <span style={{ width: 24, height: 24, borderRadius: "50%", background: "var(--accent-soft)", color: "var(--accent-ink)", display: "grid", placeItems: "center", fontSize: 12, fontWeight: 700, flex: "0 0 auto", marginTop: 2 }}>{i + 1}</span>
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" }}>
                  <span style={{ fontSize: 13.5, fontWeight: 700, color: "var(--ink)" }}>{kindLabel(st.kind)}</span>
                  <label style={{ fontSize: 12, color: "var(--ink-4)" }}>· {t("waitDays")}: <input type="number" min={0} value={st.waitDays} onChange={(e) => patchStep(st.id, { waitDays: Number(e.target.value) })} style={{ ...ss, width: 62, padding: "3px 7px", marginLeft: 4 }} /></label>
                </div>
                {st.kind === "email" && (
                  <div style={{ display: "grid", gap: 6, marginTop: 8 }}>
                    <select value={st.templateId || ""} onChange={(e) => patchStep(st.id, { templateId: e.target.value || null })} style={ss}>
                      <option value="">{t("useTemplate")}: {t("noTemplate")}</option>
                      {templates.map((tp) => <option key={tp.id} value={tp.id}>{tp.name}</option>)}
                    </select>
                    {!st.templateId && <input defaultValue={st.subject} onBlur={(e) => patchStep(st.id, { subject: e.target.value })} placeholder={t("subjectLine")} style={ss} />}
                    <input defaultValue={st.subjectB} onBlur={(e) => patchStep(st.id, { subjectB: e.target.value })} placeholder={t("variantB") + " (A/B)"} style={{ ...ss, borderStyle: "dashed" }} />
                  </div>
                )}
              </div>
              <button type="button" onClick={() => delStep(st.id)} style={{ border: "none", background: "transparent", cursor: "pointer", color: "var(--ink-4)", padding: 0, marginTop: 2 }}><Icon name="trash" size={15} /></button>
            </div>
          ))}
        </div>
        <div style={{ display: "flex", gap: 7, flexWrap: "wrap", marginTop: 12 }}>
          {KINDS.map(([k, lbl, icon]) => <button key={k} type="button" onClick={() => addStep(k)} style={{ display: "inline-flex", alignItems: "center", gap: 6, padding: "7px 12px", border: "1px dashed var(--line-2)", borderRadius: "var(--r-pill)", background: "var(--surface)", cursor: "pointer", fontSize: 12.5, fontWeight: 600, color: "var(--ink-3)" }}><Icon name={icon} size={13} /> {lbl}</button>)}
        </div>
      </CrmCard>

      {/* enrollments */}
      <CrmCard title={t("enrollments")}>
        {enr.length === 0 ? <div style={{ fontSize: 13, color: "var(--ink-4)" }}>—</div> : (
          <div style={{ display: "flex", flexDirection: "column", gap: 7 }}>
            {enr.map((e) => (
              <div key={e.id} style={{ display: "flex", alignItems: "center", gap: 10, padding: "9px 11px", border: "1px solid var(--line)", borderRadius: "var(--r-sm)" }}>
                <CoAvatar company={{ name: e.companyName, logoUrl: e.companyLogo }} size={26} />
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontSize: 13.5, fontWeight: 600, color: "var(--ink)" }}>{e.companyName || e.contactName}</div>
                  <div style={{ fontSize: 12, color: "var(--ink-4)" }}>{e.contactName} {e.abVariant && "· " + e.abVariant.toUpperCase()}</div>
                </div>
                <span style={{ fontSize: 11, fontWeight: 700, textTransform: "uppercase", color: e.status === "active" ? "#1FA36B" : e.status === "paused" ? "#E0792B" : "var(--ink-4)" }}>{t(e.status) || e.status}</span>
                {e.status === "active" ? <button type="button" onClick={() => MesperDB.crmPauseEnrollment(e.id).then(reload)} style={{ border: "none", background: "transparent", cursor: "pointer", color: "var(--ink-4)" }}><Icon name="pause" size={14} /></button>
                  : e.status === "paused" ? <button type="button" onClick={() => MesperDB.crmResumeEnrollment(e.id).then(reload)} style={{ border: "none", background: "transparent", cursor: "pointer", color: "var(--ink-4)" }}><Icon name="play" size={14} /></button> : null}
              </div>
            ))}
          </div>
        )}
      </CrmCard>
    </div>
  );
}
