// crm-app.jsx — Login, Sidebar, Topbar, Dashboard, Admin
const { useState, useEffect, useRef } = React;

// ── SEARCHABLE SELECT — .field-style dropdown with type-to-filter ──
// options: array of strings OR {value,label} objects. Closed state looks like a
// normal .field input showing the selected label; on open the same input becomes
// the filter (starts empty, placeholder = current selection label).
function SearchableSelect({ value, onChange, options, placeholder, allowEmpty = true, emptyLabel = '— Select —' }) {
  const [open, setOpen]     = useState(false);
  const [filter, setFilter] = useState('');
  const [active, setActive] = useState(0);
  const wrapRef = useRef(null);
  const listRef = useRef(null);

  const norm = (options || []).map(o =>
    (o !== null && typeof o === 'object')
      ? { value: o.value, label: o.label != null ? String(o.label) : String(o.value) }
      : { value: o, label: String(o) });
  const selectedLabel = norm.find(o => String(o.value) === String(value ?? ''))?.label || '';

  const q = filter.trim().toLowerCase();
  const filtered = q ? norm.filter(o => o.label.toLowerCase().includes(q)) : norm;
  // Cap the rendered dropdown — thousands of options (e.g. every account)
  // would mount thousands of nodes on every open. Typing narrows further.
  const CAP = 200;
  const overflow = Math.max(0, filtered.length - CAP);
  const rows = allowEmpty
    ? [{ value: '', label: emptyLabel, empty: true }, ...filtered.slice(0, CAP)]
    : filtered.slice(0, CAP);

  const close  = () => { setOpen(false); setFilter(''); };
  const openUp = () => { setOpen(true); setFilter(''); setActive(0); };
  const pick   = (row) => { onChange(row.empty ? '' : row.value); close(); };

  useEffect(() => {
    if (!open) return;
    const onDoc = (e) => { if (wrapRef.current && !wrapRef.current.contains(e.target)) close(); };
    document.addEventListener('mousedown', onDoc);
    return () => document.removeEventListener('mousedown', onDoc);
  }, [open]);

  const move = (delta) => {
    const next = Math.max(0, Math.min(rows.length - 1, active + delta));
    setActive(next);
    const el = listRef.current && listRef.current.children[next];
    if (el && el.scrollIntoView) el.scrollIntoView({ block: 'nearest' });
  };

  const onKeyDown = (e) => {
    if (!open) {
      if (e.key === 'ArrowDown' || e.key === 'Enter') { e.preventDefault(); openUp(); }
      return;
    }
    if      (e.key === 'ArrowDown') { e.preventDefault(); move(1); }
    else if (e.key === 'ArrowUp')   { e.preventDefault(); move(-1); }
    else if (e.key === 'Enter')     { e.preventDefault(); if (rows[active]) pick(rows[active]); }
    else if (e.key === 'Escape' || e.key === 'Tab') close();
  };

  return (
    <div ref={wrapRef} style={{ position:'relative' }}>
      <input
        value={open ? filter : selectedLabel}
        placeholder={open ? (selectedLabel || placeholder || emptyLabel) : (placeholder || emptyLabel)}
        onChange={e => { setFilter(e.target.value); setActive(0); }}
        onFocus={openUp}
        onClick={() => { if (!open) openUp(); }}
        onKeyDown={onKeyDown}
        autoComplete="off"
      />
      <span style={{ position:'absolute', right:10, top:'50%', transform:'translateY(-50%)', fontSize:10, color:'var(--g400)', pointerEvents:'none' }}>▾</span>
      {open && (
        <div ref={listRef} style={{ position:'absolute', top:'100%', left:0, right:0, zIndex:700, marginTop:4, background:'white', border:'1px solid var(--g200)', borderRadius:8, boxShadow:'0 8px 24px rgba(15,26,43,.14)', maxHeight:260, overflowY:'auto' }}>
          {rows.map((r, i) => (
            <div key={(r.empty ? '␀empty' : String(r.value)) + '·' + i}
              onMouseDown={e => { e.preventDefault(); pick(r); }}
              onMouseEnter={() => setActive(i)}
              style={{ padding:'8px 12px', fontSize:13, cursor:'pointer',
                       color: r.empty ? 'var(--g400)' : 'var(--navy)',
                       fontWeight: !r.empty && String(r.value) === String(value ?? '') ? 600 : 400,
                       background: i === active ? 'var(--off)' : 'white' }}>
              {r.label}
            </div>
          ))}
          {overflow > 0 && (
            <div style={{ padding:'8px 12px', fontSize:12, color:'var(--g400)', fontStyle:'italic', borderTop:'1px solid var(--g100)' }}>
              +{overflow} more — keep typing to narrow
            </div>
          )}
          {filtered.length === 0 && (
            <div style={{ padding:'8px 12px', fontSize:13, color:'var(--g400)', fontStyle:'italic' }}>(no matches)</div>
          )}
        </div>
      )}
    </div>
  );
}

// ── LOGIN — Folio brand split-screen ──
function LoginScreen({ onLogin }) {
  const [email, setEmail] = useState('');
  const [pw, setPw] = useState('');
  const [err, setErr] = useState('');
  const [submitting, setSubmitting] = useState(false);

  const params = new URLSearchParams(window.location.search);
  const msError  = params.get('mserror');
  const msLink   = params.get('mslink') === 'pending';
  if (msError || msLink) window.history.replaceState({}, '', window.location.pathname);

  const [linkEmail, setLinkEmail] = useState('');
  const [linkPw, setLinkPw]       = useState('');
  const [linkErr, setLinkErr]     = useState('');
  const [linking, setLinking]     = useState(false);
  const [showLink, setShowLink]   = useState(msLink);

  const submitLink = async () => {
    setLinking(true); setLinkErr('');
    try {
      const res = await fetch('/api/auth/ms-link', {
        method:'POST', headers:{ 'Content-Type':'application/json' },
        body: JSON.stringify({ email: linkEmail, password: linkPw }),
        credentials:'include',
      });
      if (res.ok) onLogin(await res.json());
      else { const data = await res.json().catch(() => ({})); setLinkErr(data.message || 'We couldn’t verify those credentials.'); }
    } catch { setLinkErr('Connection error. Please try again.'); }
    finally { setLinking(false); }
  };

  const submit = async () => {
    setSubmitting(true); setErr('');
    try {
      const res = await fetch('/api/auth/login', {
        method:'POST', headers:{ 'Content-Type':'application/json' },
        body: JSON.stringify({ email, password: pw }), credentials:'include',
      });
      if (res.ok) onLogin(await res.json());
      else setErr('We couldn’t verify those credentials. Try again, or sign in with Microsoft.');
    } catch { setErr('Connection error. Please try again.'); }
    finally { setSubmitting(false); }
  };

  // ── Microsoft account link panel ──
  if (showLink) {
    return (
      <div className="crm-signin">
        <FolioBrandPanel />
        <main className="crm-main">
          <div className="crm-main-inner">
            <div className="crm-login">
              <header className="crm-login-head">
                <div className="crm-login-eyebrow">One-time link</div>
                <h1 className="crm-login-title">Link your Microsoft account.</h1>
                <p className="crm-login-sub">Enter your Folio CRM email and password once. From then on you can sign in with Microsoft.</p>
              </header>
              {linkErr && <div className="folio-login-err">{linkErr}</div>}
              <div className="f-field"><label className="f-label" htmlFor="link-email">Work email</label>
                <input className="f-input" id="link-email" type="email" value={linkEmail}
                  onChange={e => setLinkEmail(e.target.value)}
                  onKeyDown={e => e.key === 'Enter' && submitLink()} /></div>
              <div className="f-field"><label className="f-label" htmlFor="link-pw">Password</label>
                <input className="f-input" id="link-pw" type="password" value={linkPw}
                  onChange={e => setLinkPw(e.target.value)}
                  onKeyDown={e => e.key === 'Enter' && submitLink()} /></div>
              <button className="crm-submit" onClick={submitLink} disabled={linking}>
                {linking ? 'Linking…' : 'Link account & sign in'}
              </button>
              <div className="crm-meta">
                <a href="#" onClick={e => { e.preventDefault(); setShowLink(false); }}>Cancel — use password instead</a>
              </div>
            </div>
          </div>
          <footer className="crm-footer">© {new Date().getFullYear()} Folio CRM · Internal use only</footer>
        </main>
      </div>
    );
  }

  // ── Normal sign-in ──
  return (
    <div className="crm-signin">
      <FolioBrandPanel />
      <main className="crm-main">
        <div className="crm-main-inner">
          <div className="crm-login">
            <header className="crm-login-head">
              <div className="crm-login-eyebrow">Welcome back</div>
              <h1 className="crm-login-title">Sign in to your workspace.</h1>
              <p className="crm-login-sub">Pick the method you've set up — Microsoft Entra, or email &amp; password.</p>
            </header>

            <div className="crm-login-quick">
              <a className="crm-quickbtn" href="/api/auth/ms-login">
                <span className="ms-mark" aria-hidden="true">
                  <span style={{background:'#F25022'}}></span>
                  <span style={{background:'#7FBA00'}}></span>
                  <span style={{background:'#00A4EF'}}></span>
                  <span style={{background:'#FFB900'}}></span>
                </span>
                <span className="label">Continue with Microsoft</span>
                <svg className="chev" width="12" height="12" viewBox="0 0 24 24" fill="none"
                  stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
                  <polyline points="9 18 15 12 9 6"/>
                </svg>
              </a>
            </div>

            {msError && <div className="folio-login-err">{decodeURIComponent(msError)}</div>}

            <div className="crm-divider"><span>or with email</span></div>

            <div className="f-field">
              <label className="f-label" htmlFor="email">Work email</label>
              <input className="f-input" id="email" type="email" value={email}
                onChange={e => setEmail(e.target.value)}
                onKeyDown={e => e.key === 'Enter' && submit()} />
            </div>
            <div className="f-field">
              <div className="crm-field-row">
                <label className="f-label" htmlFor="password" style={{margin:0}}>Password</label>
              </div>
              <input className="f-input" id="password" type="password" value={pw}
                onChange={e => setPw(e.target.value)}
                onKeyDown={e => e.key === 'Enter' && submit()} />
            </div>

            {err && <div className="folio-login-err">{err}</div>}

            <button className="crm-submit" onClick={submit} disabled={submitting}>
              {submitting ? 'Signing in…' : 'Sign in'}
              <svg width="14" height="14" viewBox="0 0 24 24" fill="none"
                stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
                <line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/>
              </svg>
            </button>

            <div className="crm-trust">
              <svg width="14" height="14" viewBox="0 0 24 24" fill="none"
                stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"
                style={{marginTop:2,flexShrink:0}} aria-hidden="true">
                <rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/>
              </svg>
              <div className="copy">
                <div className="crm-trust-title">Your data, locked tight.</div>
                <div className="crm-trust-sub">AES-256 at rest, TLS 1.3 in transit, immutable audit log.</div>
              </div>
            </div>
          </div>
        </div>
        <footer className="crm-footer">© {new Date().getFullYear()} Folio CRM · Internal use only</footer>
      </main>
    </div>
  );
}

// ── Folio brand panel (left side of sign-in) ──
function FolioBrandPanel() {
  return (
    <aside className="crm-brand">
      {/* Background art: relationship constellation + rising pipeline —
          pure decoration, sits between the gradient and the content */}
      <svg className="crm-brand-art" viewBox="0 0 800 1100" preserveAspectRatio="xMidYMid slice" aria-hidden="true">
        {/* network edges */}
        <g stroke="rgba(255,255,255,0.10)" strokeWidth="1">
          <line x1="120" y1="180" x2="300" y2="120" />
          <line x1="300" y1="120" x2="470" y2="210" />
          <line x1="470" y1="210" x2="650" y2="140" />
          <line x1="470" y1="210" x2="560" y2="360" />
          <line x1="120" y1="180" x2="220" y2="330" />
          <line x1="220" y1="330" x2="470" y2="210" />
          <line x1="220" y1="330" x2="360" y2="470" />
          <line x1="560" y1="360" x2="360" y2="470" />
          <line x1="560" y1="360" x2="720" y2="430" />
          <line x1="360" y1="470" x2="250" y2="620" />
          <line x1="360" y1="470" x2="520" y2="580" />
          <line x1="650" y1="140" x2="720" y2="290" />
          <line x1="720" y1="290" x2="720" y2="430" />
        </g>
        {/* nodes */}
        <g fill="rgba(255,255,255,0.16)">
          <circle cx="120" cy="180" r="5" />
          <circle cx="300" cy="120" r="4" />
          <circle cx="650" cy="140" r="5" />
          <circle cx="720" cy="290" r="3.5" />
          <circle cx="220" cy="330" r="6" />
          <circle cx="720" cy="430" r="4" />
          <circle cx="250" cy="620" r="4" />
          <circle cx="520" cy="580" r="5" />
        </g>
        {/* hub node — the "one ledger" */}
        <circle cx="360" cy="470" r="9" fill="rgba(255,255,255,0.22)" />
        <circle cx="360" cy="470" r="17" fill="none" stroke="rgba(255,255,255,0.14)" strokeWidth="1.5" />
        {/* accent nodes echo the brand glows */}
        <circle cx="470" cy="210" r="6" fill="rgba(56,189,248,0.45)" />
        <circle cx="560" cy="360" r="5" fill="rgba(248,45,57,0.40)" />
        {/* rising pipeline */}
        <path d="M -20 940 C 140 920, 260 850, 400 790 S 660 660, 830 610"
          fill="none" stroke="rgba(56,189,248,0.20)" strokeWidth="2.5" />
        <path d="M -20 940 C 140 920, 260 850, 400 790 S 660 660, 830 610 L 830 1100 L -20 1100 Z"
          fill="rgba(56,189,248,0.05)" />
        <g fill="rgba(56,189,248,0.35)">
          <circle cx="180" cy="898" r="4" />
          <circle cx="400" cy="790" r="4" />
          <circle cx="640" cy="672" r="4" />
        </g>
      </svg>
      <div className="crm-brand-top">
        <span className="crm-brand-wordmark">
          <svg width="22" height="22" viewBox="0 0 24 24" fill="none" aria-hidden="true">
            <path d="M5 4 H17 A2 2 0 0 1 19 6 V20 L13 16 L7 20 V6 A2 2 0 0 1 9 4 Z"
              fill="currentColor" opacity="0.18" />
            <path d="M5 4 H17 A2 2 0 0 1 19 6 V20 L13 16 L7 20 V6 A2 2 0 0 1 9 4 Z"
              stroke="currentColor" strokeWidth="1.6" strokeLinejoin="round" />
            <line x1="9" y1="10" x2="15" y2="10" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" />
            <line x1="9" y1="13" x2="13" y2="13" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" />
          </svg>
          <span>Folio<small>CRM</small></span>
        </span>
      </div>

      <div className="crm-brand-bottom">
        <div className="crm-brand-quote">
          <i>Every relationship.<br/>Every conversation.<br/>One ledger.</i>
        </div>
        <div className="crm-brand-stats">
          <div><div className="crm-brand-stat">TLS 1.3</div><div className="crm-brand-stat-label">Encrypted</div></div>
          <div><div className="crm-brand-stat">AES-256</div><div className="crm-brand-stat-label">At rest</div></div>
        </div>
      </div>
    </aside>
  );
}

// ── SIDEBAR ──
const NAV_GROUPS = [
  {
    id: 'crm', label: 'CRM',
    items: [
      { id:'dashboard', icon:'📊', label:'Dashboard', roles:['rep'] },
      { id:'pipeline',  icon:'🔀', label:'Pipeline',  roles:['rep'] },
      { id:'leads',     icon:'👥', label:'Leads',     roles:['rep'] },
      { id:'accounts',  icon:'🏢', label:'Accounts',  roles:['rep'] },
      { id:'sales',     icon:'💰', label:'Sales',     roles:['rep'] },
      { id:'aod',       icon:'📊', label:'AoD Usage', roles:[], adminOnly:true, connector:'aod' },
      { id:'library',   icon:'📚', label:'Library',   roles:['rep'] },
    ],
  },
  {
    id: 'helpdesk', label: 'Helpdesk',
    items: [
      { id:'tickets', icon:'🎫', label:'Tickets', roles:['rep','tech'] },
      { id:'helpcenter', icon:'📚', label:'Help Center', roles:['rep','tech'] },
    ],
  },
  {
    id: 'reporting', label: 'Reporting',
    items: [
      { id:'reports', icon:'📈', label:'Reports', roles:['rep'] },
    ],
  },
  {
    id: 'imports', label: 'Data',
    items: [
      { id:'dataio', icon:'🔁', label:'Import / Export', roles:['rep','tech'] },
    ],
  },
  {
    id: 'settings', label: 'Settings', adminOnly: true,
    items: [
      { id:'admin:users',          icon:'👤', label:'User Accounts'   },
      { id:'admin:dropdowns',      icon:'🗂', label:'Field Options'   },
      { id:'admin:connectors',     icon:'🔌', label:'Connectors'      },
      { id:'admin:helpdesk',       icon:'🎧', label:'Helpdesk'        },
      { id:'admin:apikeys',        icon:'🔑', label:'API Keys'        },
      { id:'admin:portal',         icon:'⚙️', label:'Portal Settings' },
      { id:'admin:proposaltpl',    icon:'📝', label:'Proposal Template' },
      { id:'admin:settings',       icon:'🏢', label:'Company Settings'},
      { id:'admin:aiagents',       icon:'🤖', label:'AI Agents'      },
      { id:'admin:authentication', icon:'🔐', label:'Authentication'  },
      { id:'admin:email',          icon:'📧', label:'Email Settings'  },
      { id:'admin:eventlog',       icon:'📋', label:'Event Logs'      },
      { id:'admin:ordercatalog',      icon:'🛒', label:'Order Catalog'      },
      { id:'admin:salestax',          icon:'🧾', label:'Sales Tax Rates'    },
    ],
  },
];

function Sidebar({ view, setView, adminTab, setAdminTab, user, onLogout, isOpen, onClose, onMyEmail }) {
  const [appVersion, setAppVersion] = useState(null);
  useEffect(() => {
    fetch('/api/version', { credentials:'include' })
      .then(r => r.ok ? r.json() : null)
      .then(v => v && setAppVersion(v))
      .catch(() => {});
  }, []);
  const [collapsed, setCollapsed] = useState({ helpdesk:true, reporting:true, imports:true, settings:true });
  const toggle = id => setCollapsed(c => ({ ...c, [id]: !c[id] }));

  const canSeeItem = item => {
    // Connector-gated items disappear entirely when the connector is off
    if (item.connector && window.CRM?.connectors?.[item.connector] === false) return false;
    return item.adminOnly ? user.isAdmin : (user.isAdmin || (item.roles||[]).includes(user.role));
  };
  const canSeeGroup = g => {
    if (g.adminOnly && !user.isAdmin) return false;
    return g.items.some(canSeeItem);
  };

  const isActive = item => {
    if (item.id.startsWith('admin:')) return view === 'admin' && adminTab === item.id.slice(6);
    return view === item.id;
  };

  const handleNav = item => {
    if (item.id.startsWith('admin:')) {
      setView('admin');
      setAdminTab(item.id.slice(6));
    } else {
      setView(item.id);
    }
    onClose?.();
  };

  return (
    <div className={`sidebar${isOpen ? ' mob-open' : ''}`}>
      <div className="sidebar-logo">
        <span className="sidebar-wordmark">
          <svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true">
            <path d="M5 4 H17 A2 2 0 0 1 19 6 V20 L13 16 L7 20 V6 A2 2 0 0 1 9 4 Z"
              fill="currentColor" opacity="0.18" />
            <path d="M5 4 H17 A2 2 0 0 1 19 6 V20 L13 16 L7 20 V6 A2 2 0 0 1 9 4 Z"
              stroke="currentColor" strokeWidth="1.6" strokeLinejoin="round" />
            <line x1="9" y1="10" x2="15" y2="10" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" />
            <line x1="9" y1="13" x2="13" y2="13" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" />
          </svg>
          <span>Folio<small>CRM</small></span>
        </span>
        <div className="sidebar-logo-sub">Customer ledger</div>
      </div>
      <div className="sidebar-nav">
        {NAV_GROUPS.filter(canSeeGroup).map(group => {
          const open = !collapsed[group.id];
          return (
            <div key={group.id}>
              <div className="nav-section"
                onClick={() => toggle(group.id)}
                style={{cursor:'pointer',display:'flex',justifyContent:'space-between',alignItems:'center',userSelect:'none'}}>
                <span>{group.label}</span>
                <span style={{fontSize:9,opacity:.5,marginRight:2}}>{open ? '▼' : '▶'}</span>
              </div>
              {open && group.items.filter(canSeeItem).map(item => (
                <div key={item.id}
                  className={`nav-item${isActive(item) ? ' active' : ''}`}
                  onClick={() => handleNav(item)}>
                  <span className="nav-item-icon">{item.icon}</span>{item.label}
                </div>
              ))}
            </div>
          );
        })}
      </div>
      <div className="sidebar-user">
        <div className="user-avatar">{(user.name||'?').charAt(0)}</div>
        <div style={{flex:1,minWidth:0}}>
          <div className="user-name">{user.name}</div>
          <div className="user-role">{user.role}</div>
        </div>
        <button onClick={onMyEmail} title="My Email Settings"
          style={{background:'none',border:'none',cursor:'pointer',fontSize:16,padding:'4px 6px',borderRadius:6,opacity:.7,lineHeight:1}}
          onMouseEnter={e=>e.currentTarget.style.opacity=1}
          onMouseLeave={e=>e.currentTarget.style.opacity=.7}>📧</button>
        <button className="logout-btn" onClick={onLogout} title="Sign Out">⇥</button>
      </div>
      {appVersion && (
        <div title={`Built ${appVersion.buildDate}`}
          style={{fontSize:10,color:'rgba(255,255,255,.4)',textAlign:'center',padding:'8px 10px 12px',letterSpacing:'.04em'}}>
          v{appVersion.version}
        </div>
      )}
    </div>
  );
}

// ── APP FOOTER ──
// Visible to end users on every screen. Shows the CTR/NY
// copyright + the running app's version (fetched once from
// /api/version). Hovering the version reveals the build date.
function AppFooter() {
  const [appVersion, setAppVersion] = useState(null);
  useEffect(() => {
    fetch('/api/version', { credentials:'include' })
      .then(r => r.ok ? r.json() : null)
      .then(v => v && setAppVersion(v))
      .catch(() => {});
  }, []);
  const year = new Date().getFullYear();
  return (
    <div style={{
      borderTop:'1px solid var(--g200)', background:'var(--off)',
      padding:'10px 24px', display:'flex', alignItems:'center',
      justifyContent:'space-between', flexWrap:'wrap', gap:8,
      fontSize:11, color:'var(--g400)', letterSpacing:'.02em',
    }}>
      <div>Folio CRM &copy; {year}. All rights reserved.</div>
      {appVersion && (
        <div title={`Built ${appVersion.buildDate}`} style={{ fontFamily:'monospace' }}>
          {appVersion.product || 'CRM'} &middot; v{appVersion.version}
        </div>
      )}
    </div>
  );
}
window.AppFooter = AppFooter;

// ── TOPBAR ──
// ── GLOBAL SEARCH (topbar, Ctrl+K) ──
function GlobalSearch({ onOpenAccount, onOpenLead }) {
  const [q, setQ]             = useState('');
  const [results, setResults] = useState(null);
  const [open, setOpen]       = useState(false);
  const [busy, setBusy]       = useState(false);
  const boxRef   = useRef(null);
  const inputRef = useRef(null);
  const timer    = useRef(null);
  const seq      = useRef(0);

  // Ctrl+K / Cmd+K focuses the box from anywhere
  useEffect(() => {
    const key = (e) => {
      if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'k') {
        e.preventDefault();
        inputRef.current?.focus();
        inputRef.current?.select();
      }
      if (e.key === 'Escape') setOpen(false);
    };
    const click = (e) => { if (boxRef.current && !boxRef.current.contains(e.target)) setOpen(false); };
    document.addEventListener('keydown', key);
    document.addEventListener('mousedown', click);
    return () => { document.removeEventListener('keydown', key); document.removeEventListener('mousedown', click); };
  }, []);

  // Debounced fetch; sequence guard drops stale responses
  const search = (value) => {
    setQ(value);
    clearTimeout(timer.current);
    if (value.trim().length < 2) { setResults(null); setOpen(false); return; }
    timer.current = setTimeout(async () => {
      const mySeq = ++seq.current;
      setBusy(true);
      try {
        const r = await CRM.SearchAPI.query(value.trim());
        if (mySeq === seq.current) { setResults(r); setOpen(true); }
      } catch { /* ignore */ }
      finally { if (mySeq === seq.current) setBusy(false); }
    }, 250);
  };

  const go = (fn, arg) => { setOpen(false); setQ(''); setResults(null); fn(arg); };

  const GROUPS = results ? [
    { label:'Leads',       icon:'👥', items: results.leads,     text: l => l.companyName + (l.contactName ? ` — ${l.contactName}` : '') + (l.isActive === false ? ' (inactive)' : ''), act: l => go(onOpenLead, l.id) },
    { label:'Accounts',    icon:'🏢', items: results.accounts,  text: a => a.companyName + (a.city ? ` — ${a.city}${a.state ? ', ' + a.state : ''}` : '') + (a.isActive === false ? ' (closed)' : ''), act: a => go(onOpenAccount, a.id) },
    { label:'Contacts',    icon:'👤', items: results.contacts,  text: c => c.name + (c.companyName ? ` — ${c.companyName}` : ''), act: c => c.accountId ? go(onOpenAccount, c.accountId) : go(onOpenLead, c.leadId) },
    { label:'Proposals',   icon:'📄', items: results.proposals, text: p => `#${typeof toPropNumber === 'function' ? toPropNumber(p.createdAt) : ''} — ${p.companyName || p.contactName || ''}`, act: p => p.accountId ? go(onOpenAccount, p.accountId) : go(onOpenLead, p.leadId) },
    { label:'Order Forms', icon:'🛒', items: results.orders,    text: o => `#${typeof toOrderNumber === 'function' ? toOrderNumber(o.createdAt) : ''} — ${o.companyName || ''}${o.custPo ? ` (PO ${o.custPo})` : ''}`, act: o => go(onOpenAccount, o.accountId) },
  ].filter(g => g.items && g.items.length > 0) : [];

  return (
    <div ref={boxRef} style={{position:'relative', flex:'0 1 340px', margin:'0 16px'}}>
      <input ref={inputRef} value={q} onChange={e => search(e.target.value)}
        onFocus={() => { if (results) setOpen(true); }}
        placeholder="🔍  Search everything…  (Ctrl+K)"
        style={{width:'100%', padding:'7px 12px', fontSize:13, fontFamily:'var(--font)',
                border:'1px solid var(--g200)', borderRadius:'var(--folio-radius-md)',
                background:'var(--off)', color:'var(--folio-fg)', outline:'none'}} />
      {open && (
        <div style={{position:'absolute', top:'calc(100% + 6px)', left:0, right:0, background:'#fff',
                     border:'1px solid var(--g200)', borderRadius:8, boxShadow:'0 12px 32px rgba(0,0,0,.14)',
                     zIndex:600, maxHeight:420, overflowY:'auto'}}>
          {busy && <div style={{padding:'10px 14px', fontSize:12, color:'var(--g400)'}}>Searching…</div>}
          {!busy && GROUPS.length === 0 && <div style={{padding:'12px 14px', fontSize:13, color:'var(--g400)'}}>No matches.</div>}
          {GROUPS.map(g => (
            <div key={g.label}>
              <div style={{padding:'8px 14px 4px', fontSize:10, fontWeight:700, letterSpacing:'.08em',
                           textTransform:'uppercase', color:'var(--g400)', background:'var(--off)'}}>{g.label}</div>
              {g.items.map(it => (
                <button key={it.id}
                  style={{display:'flex', alignItems:'center', gap:8, width:'100%', padding:'8px 14px',
                          border:'none', background:'none', cursor:'pointer', textAlign:'left',
                          fontFamily:'var(--font)', fontSize:13, color:'var(--navy)'}}
                  onMouseEnter={e => e.currentTarget.style.background='var(--off)'}
                  onMouseLeave={e => e.currentTarget.style.background='none'}
                  onClick={() => g.act(it)}>
                  <span style={{flexShrink:0}}>{g.icon}</span>
                  <span style={{overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap'}}>{g.text(it)}</span>
                </button>
              ))}
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

// ── TASKS: shared helpers ──
function localToday() {
  const d = new Date();
  return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
}

// Small inline popover for adding a task from a lead/account detail modal.
// Defined here (crm-app.jsx loads first) and used from crm-leads.jsx / crm-accounts.jsx.
function QuickTaskAdd({ leadId, accountId, showToast }) {
  const [open, setOpen]   = useState(false);
  const [title, setTitle] = useState('');
  const [due, setDue]     = useState('');
  const [busy, setBusy]   = useState(false);
  const ref = useRef(null);

  useEffect(() => {
    if (!open) return;
    const click = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', click);
    return () => document.removeEventListener('mousedown', click);
  }, [open]);

  const save = async () => {
    if (!title.trim() || busy) return;
    setBusy(true);
    try {
      await CRM.TasksAPI.create({ title: title.trim(), dueDate: due || null, leadId: leadId || null, accountId: accountId || null });
      setTitle(''); setDue(''); setOpen(false);
      showToast?.('Task added.');
    } catch (e) {
      showToast?.('Failed to add task: ' + e.message, 'error');
    } finally { setBusy(false); }
  };

  return (
    <div ref={ref} style={{position:'relative'}}>
      <button className="btn btn-sm" style={{background:'var(--off)',color:'var(--navy)',border:'1px solid var(--g200)'}}
        onClick={() => setOpen(o => !o)}>＋ Task</button>
      {open && (
        <div style={{position:'absolute', right:0, top:'calc(100% + 6px)', background:'#fff',
                     border:'1px solid var(--g200)', borderRadius:8, boxShadow:'0 8px 24px rgba(0,0,0,.12)',
                     padding:12, width:260, zIndex:600}}>
          <input autoFocus value={title} onChange={e => setTitle(e.target.value)}
            onKeyDown={e => { if (e.key === 'Enter') save(); if (e.key === 'Escape') setOpen(false); }}
            placeholder="Task title…"
            style={{width:'100%', padding:'6px 10px', fontSize:13, fontFamily:'var(--font)',
                    border:'1px solid var(--g200)', borderRadius:6, marginBottom:8, outline:'none'}} />
          <div style={{display:'flex', gap:8}}>
            <input type="date" value={due} onChange={e => setDue(e.target.value)}
              style={{flex:1, padding:'5px 8px', fontSize:12, fontFamily:'var(--font)',
                      border:'1px solid var(--g200)', borderRadius:6, outline:'none'}} />
            <button className="btn btn-primary btn-sm" onClick={save} disabled={busy || !title.trim()}>Save</button>
          </div>
        </div>
      )}
    </div>
  );
}

// ── TASK BELL (topbar) ──
function TaskBell({ onOpenAccount, onOpenLead }) {
  const [tasks, setTasks] = useState([]);
  const [open, setOpen]   = useState(false);
  const [title, setTitle] = useState('');
  const [due, setDue]     = useState('');
  const [busy, setBusy]   = useState(false);
  const ref = useRef(null);

  const refresh = async () => {
    try { setTasks(await CRM.TasksAPI.my()); } catch { /* ignore */ }
  };

  useEffect(() => {
    refresh();
    const iv = setInterval(refresh, 5 * 60 * 1000);
    const click = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', click);
    return () => { clearInterval(iv); document.removeEventListener('mousedown', click); };
  }, []);

  const today = localToday();
  const openTasks = tasks.filter(t => !t.isDone);
  const dueCount  = openTasks.filter(t => t.dueDate && t.dueDate <= today).length;

  const GROUPS = [
    { label:'Overdue',     color:'var(--red)',  items: openTasks.filter(t => t.dueDate && t.dueDate < today) },
    { label:'Today',       color:'var(--navy)', items: openTasks.filter(t => t.dueDate === today) },
    { label:'Upcoming',    color:'var(--g600)', items: openTasks.filter(t => t.dueDate && t.dueDate > today) },
    { label:'No due date', color:'var(--g400)', items: openTasks.filter(t => !t.dueDate) },
  ].filter(g => g.items.length > 0);

  const add = async () => {
    if (!title.trim() || busy) return;
    setBusy(true);
    try {
      await CRM.TasksAPI.create({ title: title.trim(), dueDate: due || null });
      setTitle(''); setDue('');
      await refresh();
    } catch { /* ignore */ }
    finally { setBusy(false); }
  };

  const complete = async (id) => {
    try { await CRM.TasksAPI.done(id); await refresh(); } catch { /* ignore */ }
  };

  const remove = async (t) => {
    if (!confirm(`Delete task "${t.title}"?`)) return;
    try { await CRM.TasksAPI.delete(t.id); await refresh(); } catch { /* ignore */ }
  };

  const openLink = (t) => {
    setOpen(false);
    if (t.accountId) onOpenAccount?.(t.accountId);
    else if (t.leadId) onOpenLead?.(t.leadId);
  };

  const fmtDue = (d) => d ? new Date(d + 'T00:00:00').toLocaleDateString() : '';

  return (
    <div ref={ref} style={{position:'relative', marginRight:12}}>
      <button onClick={() => setOpen(o => !o)} aria-label="Tasks" title="My tasks"
        style={{position:'relative', border:'none', background:'none', cursor:'pointer',
                fontSize:18, padding:'4px 6px', lineHeight:1}}>
        🔔
        {dueCount > 0 && (
          <span style={{position:'absolute', top:-2, right:-4, background:'var(--red)', color:'#fff',
                        fontSize:10, fontWeight:700, borderRadius:9, minWidth:16, height:16,
                        display:'inline-flex', alignItems:'center', justifyContent:'center',
                        padding:'0 4px', fontFamily:'var(--font)'}}>{dueCount}</span>
        )}
      </button>
      {open && (
        <div style={{position:'absolute', right:0, top:'calc(100% + 6px)', width:380, maxHeight:480,
                     overflowY:'auto', background:'#fff', border:'1px solid var(--g200)', borderRadius:8,
                     boxShadow:'0 12px 32px rgba(0,0,0,.14)', zIndex:600}}>
          {/* Quick add */}
          <div style={{display:'flex', gap:6, padding:'10px 12px', borderBottom:'1px solid var(--g200)',
                       position:'sticky', top:0, background:'#fff', zIndex:1}}>
            <input value={title} onChange={e => setTitle(e.target.value)}
              onKeyDown={e => { if (e.key === 'Enter') add(); }}
              placeholder="Add a task…"
              style={{flex:1, minWidth:0, padding:'6px 10px', fontSize:13, fontFamily:'var(--font)',
                      border:'1px solid var(--g200)', borderRadius:6, outline:'none'}} />
            <input type="date" value={due} onChange={e => setDue(e.target.value)}
              style={{width:118, padding:'5px 6px', fontSize:12, fontFamily:'var(--font)',
                      border:'1px solid var(--g200)', borderRadius:6, outline:'none'}} />
            <button className="btn btn-primary btn-sm" onClick={add} disabled={busy || !title.trim()}>Add</button>
          </div>

          {openTasks.length === 0 && (
            <div style={{padding:'24px 14px', textAlign:'center', fontSize:13, color:'var(--g400)'}}>🎉 No open tasks</div>
          )}

          {GROUPS.map(g => (
            <div key={g.label}>
              <div style={{padding:'8px 14px 4px', fontSize:10, fontWeight:700, letterSpacing:'.08em',
                           textTransform:'uppercase', color:g.color, background:'var(--off)'}}>{g.label}</div>
              {g.items.map(t => (
                <div key={t.id} style={{display:'flex', alignItems:'flex-start', gap:10, padding:'8px 14px',
                                        borderBottom:'1px solid var(--off)'}}>
                  <input type="checkbox" checked={false} onChange={() => complete(t.id)}
                    style={{marginTop:3, cursor:'pointer'}} title="Mark done" />
                  <div style={{flex:1, minWidth:0}}>
                    <div style={{fontSize:13, fontWeight:700, color:'var(--navy)', overflow:'hidden',
                                 textOverflow:'ellipsis', whiteSpace:'nowrap'}}>{t.title}</div>
                    <div style={{fontSize:11, color: g.label==='Overdue' ? 'var(--red)' : 'var(--g400)', marginTop:1}}>
                      {(t.accountId || t.leadId) ? (
                        <button onClick={() => openLink(t)}
                          style={{border:'none', background:'none', padding:0, cursor:'pointer',
                                  fontFamily:'var(--font)', fontSize:11, color:'#1D4ED8',
                                  textDecoration:'underline'}}>{t.companyName || 'Open record'}</button>
                      ) : (t.companyName || null)}
                      {t.dueDate ? <span>{(t.accountId || t.leadId || t.companyName) ? ' · ' : ''}{fmtDue(t.dueDate)}</span> : null}
                    </div>
                  </div>
                  <button onClick={() => remove(t)} title="Delete task"
                    style={{border:'none', background:'none', cursor:'pointer', color:'var(--g400)',
                            fontSize:14, lineHeight:1, padding:'2px 4px'}}>×</button>
                </div>
              ))}
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

function Topbar({ view, adminTab, user, onMenuClick, onQuickAdd, onOpenAccount, onOpenLead }) {
  const [dropOpen, setDropOpen] = useState(false);
  const dropRef = useRef(null);
  const ADMIN_TITLES = { users:'User Accounts', dropdowns:'Field Options', connectors:'Connectors', helpdesk:'Helpdesk', apikeys:'API Keys', portal:'Portal Settings', proposaltpl:'Proposal Template', settings:'Company Settings', aiagents:'AI Agents', authentication:'Authentication', email:'Email Settings', eventlog:'Event Logs', ordercatalog:'Order Catalog', salestax:'Sales Tax Rates' };
  const titles = { dashboard:'Dashboard', pipeline:'Pipeline', leads:'Leads', accounts:'Accounts', sales:'Sales', aod:'AoD Usage', library:'Document Library', tickets:'Tickets', helpcenter:'Help Center', reports:'Reports', admin: ADMIN_TITLES[adminTab] || 'Settings', dataio:'Import / Export' };

  useEffect(() => {
    const handler = (e) => { if (dropRef.current && !dropRef.current.contains(e.target)) setDropOpen(false); };
    document.addEventListener('mousedown', handler);
    return () => document.removeEventListener('mousedown', handler);
  }, []);

  const ACTIONS = [
    { type:'lead',    icon:'👥', label:'New Lead'    },
    { type:'account', icon:'🏢', label:'New Account' },
    { type:'sale',    icon:'💰', label:'New Sale'    },
    { type:'ticket',  icon:'🎫', label:'New Ticket'  },
  ].filter(a => user.isAdmin || user.role !== 'tech' || a.type === 'ticket');

  return (
    <div className="topbar">
      <div style={{display:'flex',alignItems:'center'}}>
        <button className="topbar-menu-btn" onClick={onMenuClick} aria-label="Menu">☰</button>
        <div className="topbar-title">{titles[view] || view}</div>
      </div>
      {(user.isAdmin || user.role !== 'tech') && <GlobalSearch onOpenAccount={onOpenAccount} onOpenLead={onOpenLead} />}
      <TaskBell onOpenAccount={onOpenAccount} onOpenLead={onOpenLead} />
      <div style={{position:'relative'}} ref={dropRef}>
        <button className="btn btn-primary btn-sm" onClick={() => setDropOpen(o => !o)}>
          + New <span style={{fontSize:10,opacity:.8}}>▾</span>
        </button>
        {dropOpen && (
          <div style={{position:'absolute',right:0,top:'calc(100% + 6px)',background:'white',border:'1px solid var(--g200)',borderRadius:8,boxShadow:'0 8px 24px rgba(0,0,0,.12)',minWidth:168,zIndex:500,overflow:'hidden'}}>
            {ACTIONS.map(a => (
              <button key={a.type}
                style={{display:'flex',alignItems:'center',gap:10,width:'100%',padding:'11px 16px',border:'none',background:'none',cursor:'pointer',fontFamily:'var(--font)',fontSize:13,fontWeight:500,color:'var(--navy)',textAlign:'left'}}
                onMouseEnter={e => e.currentTarget.style.background='var(--off)'}
                onMouseLeave={e => e.currentTarget.style.background='none'}
                onClick={() => { setDropOpen(false); onQuickAdd(a.type); }}>
                <span>{a.icon}</span>{a.label}
              </button>
            ))}
          </div>
        )}
      </div>
    </div>
  );
}

// ── DASHBOARD ──
function Dashboard({ user, setView, showToast, onOpenAccount, onOpenLead }) {
  const [leads, setLeads] = useState([]);
  const [forecast, setForecast] = useState({});
  const [pipeline, setPipeline] = useState([]);
  const [activities, setActivities] = useState([]);
  const [recentProposals, setRecentProposals] = useState([]);
  const [recentOrders, setRecentOrders] = useState([]);
  const [myTasks, setMyTasks] = useState([]);
  const [loading, setLoading] = useState(true);
  const [tab, setTab] = useState('sales');

  // Date range — applies to every chart and hero card on both tabs.
  // Default: last 12 months.
  const iso = d => d.toISOString().split('T')[0];
  const defFrom = (() => { const d = new Date(); d.setMonth(d.getMonth() - 12); return iso(d); })();
  const [rangeFrom, setRangeFrom] = useState(defFrom);
  const [rangeTo, setRangeTo] = useState(iso(new Date()));
  const [analyzing, setAnalyzing] = useState(false);
  const range = { from: rangeFrom || null, to: rangeTo || null };
  const applyPreset = (months) => {
    const d = new Date(); d.setMonth(d.getMonth() - months);
    setRangeFrom(iso(d)); setRangeTo(iso(new Date()));
  };

  useEffect(() => {
    (async () => {
      setLoading(true);
      try {
        const [allLeads, fc, pipe, acts, props, orders, tasks] = await Promise.all([
          CRM.LeadsAPI.getAll(user.role === 'rep' && !user.isAdmin ? { repId: user.id } : {}),
          CRM.ReportsAPI.getForecast(range),
          CRM.ReportsAPI.getPipelineSummary(range),
          CRM.ActivitiesAPI.getRecent(8),
          CRM.ProposalFormsAPI.getRecent(10).catch(() => []),
          CRM.OrderFormsAPI.getRecent(10).catch(() => []),
          CRM.TasksAPI.my().catch(() => []),
        ]);
        // Leads scoped to the range (createdAt) so cards/charts agree
        setLeads(allLeads.filter(l => (!rangeFrom || (l.createdAt || '') >= rangeFrom)
                                   && (!rangeTo || (l.createdAt || '') <= rangeTo)));
        setForecast(fc);
        setPipeline(pipe);
        setActivities(acts);
        setRecentProposals(props);
        setRecentOrders(orders);
        setMyTasks(tasks);
      } finally {
        setLoading(false);
      }
    })();
  }, [rangeFrom, rangeTo]);

  // Widget rows link to the owning account (or the Leads view for
  // lead-stage proposals that have no account yet).
  const openDoc = (item) => {
    if (item.accountId) onOpenAccount?.(item.accountId);
    else if (item.leadId) setView('leads');
  };

  const fmt = v => '$' + (v||0).toLocaleString();
  const activeLeads = leads.filter(l => !['won','lost'].includes(l.stage));
  const wonLeads    = leads.filter(l => l.stage === 'won');
  const today       = new Date().toISOString().split('T')[0];
  const followUpToday = leads.filter(l => l.followUpDate === today);
  const overdue       = leads.filter(l => l.followUpDate && l.followUpDate < today && !['won','lost'].includes(l.stage));

  const ACT_ICONS = { note:'📝', call:'📞', email:'📧', meeting:'🤝', proposal:'📄' };

  if (loading) return <div style={{padding:40,color:'var(--g400)',textAlign:'center'}}>Loading dashboard…</div>;

  return (
    <div>
      {/* Sales | Helpdesk tabs */}
      <div style={{display:'flex',gap:4,marginBottom:20,borderBottom:'1px solid var(--g200)'}}>
        {[{id:'sales',label:'📊 Sales'},{id:'helpdesk',label:'🎫 Helpdesk'}].map(t => (
          <button key={t.id} onClick={() => setTab(t.id)}
            style={{border:'none',background:'none',cursor:'pointer',fontFamily:'var(--font)',fontSize:14,
                    fontWeight:700,padding:'10px 18px',color: tab===t.id ? 'var(--navy)' : 'var(--g400)',
                    borderBottom: tab===t.id ? '3px solid var(--red)' : '3px solid transparent',marginBottom:-1}}>
            {t.label}
          </button>
        ))}
        <div style={{marginLeft:'auto',display:'flex',alignItems:'center',gap:8,padding:'6px 0',flexWrap:'wrap'}}>
          <select className="search-input" style={{width:150,fontSize:12,padding:'5px 8px'}} value=""
            onChange={e => { const v = e.target.value; if (v === 'ytd') { setRangeFrom(new Date().getFullYear() + '-01-01'); setRangeTo(iso(new Date())); } else if (v) applyPreset(parseInt(v, 10)); }}>
            <option value="">Range presets</option>
            <option value="1">Last 30 days</option>
            <option value="3">Last 3 months</option>
            <option value="12">Last 12 months</option>
            <option value="ytd">Year to date</option>
            <option value="120">All time</option>
          </select>
          <input type="date" className="search-input" style={{width:135,fontSize:12,padding:'5px 8px'}}
            value={rangeFrom} onChange={e => setRangeFrom(e.target.value)} />
          <span style={{fontSize:12,color:'var(--g400)'}}>to</span>
          <input type="date" className="search-input" style={{width:135,fontSize:12,padding:'5px 8px'}}
            value={rangeTo} onChange={e => setRangeTo(e.target.value)} />
          <button className="btn btn-primary btn-sm" onClick={() => setAnalyzing(true)}>✨ Analyze</button>
        </div>
      </div>

      {analyzing && (
        <AnalyzeModal
          title={tab === 'helpdesk' ? 'Helpdesk Analysis' : 'Sales Analysis'}
          payload={{ type: tab === 'helpdesk' ? 'helpdesk' : 'sales', from: rangeFrom || null, to: rangeTo || null }}
          onClose={() => setAnalyzing(false)}
        />
      )}

      {tab === 'helpdesk' && <HelpdeskDashboard showToast={showToast} onOpenAccount={onOpenAccount} setView={setView} range={range} />}

      {tab === 'sales' && <React.Fragment>
      {/* Stats */}
      <div className="stat-grid">
        <div className="stat-card">
          <div className="stat-icon">🔥</div>
          <div className="stat-label">Active Pipeline</div>
          <div className="stat-val">{activeLeads.length}</div>
          <div className="stat-sub">{fmt(forecast.pipelineValue)} total value</div>
        </div>
        <div className="stat-card">
          <div className="stat-icon">🎯</div>
          <div className="stat-label">Weighted Forecast</div>
          <div className="stat-val">{fmt(Math.round(forecast.weightedForecast||0))}</div>
          <div className="stat-sub">Probability-adjusted</div>
        </div>
        <div className="stat-card">
          <div className="stat-icon">🏆</div>
          <div className="stat-label">Closed Revenue</div>
          <div className="stat-val" style={{color:'var(--green)'}}>{fmt(forecast.closedRevenue)}</div>
          <div className="stat-sub">{(forecast.wonLeadsCount||wonLeads.length) + (forecast.accountSalesCount||0)} deals won</div>
        </div>
        <div className="stat-card">
          <div className="stat-icon">⚠️</div>
          <div className="stat-label">Follow-Ups Overdue</div>
          <div className="stat-val" style={{color: overdue.length ? 'var(--red)' : 'var(--navy)'}}>{overdue.length}</div>
          <div className="stat-sub">{followUpToday.length} due today</div>
        </div>
      </div>

      <div style={{display:'grid',gridTemplateColumns:'1fr 1fr',gap:20,marginBottom:20}}>
        {/* Pipeline by stage */}
        <div className="card card-pad">
          <div style={{fontWeight:700,fontSize:14,color:'var(--navy)',marginBottom:16}}>Pipeline by Stage</div>
          {pipeline.filter(s => !['won','lost'].includes(s.id)).map(s => (
            <div key={s.id} style={{marginBottom:10}}>
              <div style={{display:'flex',justifyContent:'space-between',marginBottom:4}}>
                <span style={{fontSize:12,fontWeight:600,color:'var(--g600)'}}>{s.label}</span>
                <span style={{fontSize:12,fontWeight:700,color:'var(--navy)'}}>{s.count} · {fmt(s.value)}</span>
              </div>
              <div style={{background:'var(--g100)',borderRadius:4,height:6}}>
                <div style={{background:s.color,borderRadius:4,height:6,width: leads.length ? `${Math.round(s.count/leads.length*100)}%` : '0%',transition:'width .4s'}}/>
              </div>
            </div>
          ))}
        </div>

        {/* Recent Activity */}
        <div className="card card-pad">
          <div style={{fontWeight:700,fontSize:14,color:'var(--navy)',marginBottom:16}}>Recent Activity</div>
          <div className="activity-list">
            {activities.length === 0 && <div style={{color:'var(--g400)',fontSize:13}}>No activity yet.</div>}
            {activities.slice(0,6).map(a => (
              <div key={a.id} className="activity-item">
                <div className={`act-icon act-${a.type}`}>{ACT_ICONS[a.type]||'📌'}</div>
                <div className="act-content">
                  <div className="act-text" style={{fontSize:12}}>{a.content.substring(0,80)}{a.content.length>80?'…':''}</div>
                  <div className="act-meta">{a.companyName||'Unknown'} · {a.userName} · {new Date(a.createdAt).toLocaleDateString()}</div>
                </div>
              </div>
            ))}
          </div>
        </div>
      </div>

      {/* My Tasks (due today or overdue) */}
      {(() => {
        const t0 = localToday();
        const dueTasks = myTasks.filter(t => !t.isDone && t.dueDate && t.dueDate <= t0).slice(0, 8);
        const completeTask = async (t) => {
          setMyTasks(prev => prev.filter(x => x.id !== t.id)); // optimistic
          try { await CRM.TasksAPI.done(t.id); }
          catch (e) {
            setMyTasks(prev => [...prev, t]);
            showToast?.('Failed to complete task: ' + e.message, 'error');
          }
        };
        return (
          <div className="card card-pad" style={{marginBottom:20}}>
            <div style={{fontWeight:700,fontSize:14,color:'var(--navy)',marginBottom:12}}>✅ My Tasks</div>
            {dueTasks.length === 0
              ? <div style={{color:'var(--g400)',fontSize:13}}>No tasks due — nice.</div>
              : dueTasks.map(t => (
                  <div key={t.id} style={{display:'flex',alignItems:'center',gap:10,padding:'7px 0',
                                          borderBottom:'1px solid var(--off)'}}>
                    <input type="checkbox" checked={false} onChange={() => completeTask(t)}
                      style={{cursor:'pointer'}} title="Mark done" />
                    <span style={{fontSize:13,fontWeight:600,color:'var(--navy)',flex:1,minWidth:0,
                                  overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap'}}>{t.title}</span>
                    {t.companyName && (
                      (t.accountId || t.leadId)
                        ? <button onClick={() => t.accountId ? onOpenAccount?.(t.accountId) : onOpenLead?.(t.leadId)}
                            style={{border:'none',background:'none',padding:0,cursor:'pointer',
                                    fontFamily:'var(--font)',fontSize:12,color:'#1D4ED8',
                                    textDecoration:'underline'}}>{t.companyName}</button>
                        : <span style={{fontSize:12,color:'var(--g400)'}}>{t.companyName}</span>
                    )}
                    <span style={{fontSize:12,fontWeight:600,
                                  color: t.dueDate < t0 ? 'var(--red)' : 'var(--g600)'}}>
                      {new Date(t.dueDate + 'T00:00:00').toLocaleDateString()}
                    </span>
                  </div>
                ))}
          </div>
        );
      })()}

      {/* Recent proposals + order forms */}
      <div style={{display:'grid',gridTemplateColumns:'1fr 1fr',gap:20,marginBottom:20}}>
        <div className="card card-pad">
          <div style={{fontWeight:700,fontSize:14,color:'var(--navy)',marginBottom:12}}>📄 Recent Proposals</div>
          {recentProposals.length === 0
            ? <div style={{color:'var(--g400)',fontSize:13}}>No proposals yet.</div>
            : <table className="tbl">
                <thead><tr><th>#</th><th>Company</th><th>Date</th><th>By</th></tr></thead>
                <tbody>
                  {recentProposals.map(p => (
                    <tr key={p.id} onClick={() => openDoc(p)} style={{cursor:'pointer'}}
                      title={p.accountId ? 'Open account' : 'Open leads'}>
                      <td style={{fontFamily:'monospace',fontWeight:600}}>{typeof toPropNumber==='function' ? toPropNumber(p.createdAt) : ''}</td>
                      <td style={{color:'var(--navy)',fontWeight:600}}>
                        {p.companyName || '—'}
                        {!p.accountId && p.leadId && <span className="badge badge-blue" style={{marginLeft:6,fontSize:10}}>lead</span>}
                      </td>
                      <td style={{fontWeight:'normal',color:'var(--g600)'}}>{p.proposalDate ? new Date(p.proposalDate + 'T00:00:00').toLocaleDateString() : '—'}</td>
                      <td style={{fontWeight:'normal',color:'var(--g400)'}}>{p.createdByName || '—'}</td>
                    </tr>
                  ))}
                </tbody>
              </table>}
        </div>
        <div className="card card-pad">
          <div style={{fontWeight:700,fontSize:14,color:'var(--navy)',marginBottom:12}}>🛒 Recent Order Forms</div>
          {recentOrders.length === 0
            ? <div style={{color:'var(--g400)',fontSize:13}}>No order forms yet.</div>
            : <table className="tbl">
                <thead><tr><th>Order #</th><th>Company</th><th>Date</th><th>By</th></tr></thead>
                <tbody>
                  {recentOrders.map(o => (
                    <tr key={o.id} onClick={() => openDoc(o)} style={{cursor:'pointer'}} title="Open account">
                      <td style={{fontFamily:'monospace',fontWeight:600}}>{typeof toOrderNumber==='function' ? toOrderNumber(o.createdAt) : ''}</td>
                      <td style={{color:'var(--navy)',fontWeight:600}}>{o.companyName || o.billToCompany || '—'}</td>
                      <td style={{fontWeight:'normal',color:'var(--g600)'}}>{o.orderDate ? new Date(o.orderDate + 'T00:00:00').toLocaleDateString() : '—'}</td>
                      <td style={{fontWeight:'normal',color:'var(--g400)'}}>{o.createdByName || '—'}</td>
                    </tr>
                  ))}
                </tbody>
              </table>}
        </div>
      </div>

      {/* Overdue follow-ups */}
      {overdue.length > 0 && (
        <div className="card card-pad" style={{borderLeft:'3px solid var(--red)'}}>
          <div style={{fontWeight:700,fontSize:14,color:'var(--red)',marginBottom:12}}>⚠️ Overdue Follow-Ups ({overdue.length})</div>
          <table className="tbl">
            <thead><tr><th>Company</th><th>Contact</th><th>Stage</th><th>Due Date</th><th>Rep</th></tr></thead>
            <tbody>
              {overdue.map(l => {
                const rep = CRM.UsersAPI.getById(l.assignedRepId);
                return (
                  <tr key={l.id} onClick={() => setView('leads')}>
                    <td>{l.companyName}</td>
                    <td style={{fontWeight:'normal'}}>{l.contactName}</td>
                    <td><StageBadge stage={l.stage}/></td>
                    <td style={{color:'var(--red)',fontWeight:600}}>{l.followUpDate}</td>
                    <td style={{fontWeight:'normal'}}>{rep?.name||l.assignedRep?.name||'—'}</td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        </div>
      )}
      </React.Fragment>}
    </div>
  );
}

// ── HELPDESK DASHBOARD ──
// Reads /api/tickets/stats: status cards, 12-week created/resolved bars,
// open-by-technician, and breakdowns per custom dropdown field.
function HelpdeskDashboard({ showToast, onOpenAccount, setView, range }) {
  const [stats, setStats] = useState(null);
  const [loading, setLoading] = useState(true);
  const [hoverWeek, setHoverWeek] = useState(null);   // volume-chart hover tooltip

  useEffect(() => {
    setLoading(true);
    CRM.TicketsAPI.stats(range)
      .then(setStats)
      .catch(e => showToast?.('Failed to load helpdesk stats: ' + e.message, 'error'))
      .finally(() => setLoading(false));
  }, [range && range.from, range && range.to]);

  if (loading) return <div style={{padding:40,color:'var(--g400)',textAlign:'center'}}>Loading helpdesk stats…</div>;
  if (!stats) return <div style={{padding:40,color:'var(--g400)',textAlign:'center'}}>No ticket data yet.</div>;

  const STAT_COLORS = { new:'#6366F1', open:'#2563EB', 'in-progress':'#8B5CF6', pending:'#D97706', closed:'#64748B' };
  const statusOrder = ['new','open','in-progress','pending','closed'];
  const statusMap = {};
  (stats.byStatus||[]).forEach(s => { statusMap[s.status] = s.count; });
  const maxWeekly = Math.max(1, ...(stats.weekly||[]).flatMap(w => [w.created, w.resolved]));

  return (
    <div>
      {/* Status summary cards */}
      <div className="stat-grid">
        <div className="stat-card">
          <div className="stat-icon">🎫</div>
          <div className="stat-label">Total Tickets</div>
          <div className="stat-val">{stats.total||0}</div>
          <div className="stat-sub">{stats.open||0} currently open</div>
        </div>
        <div className="stat-card">
          <div className="stat-icon">📬</div>
          <div className="stat-label">Open / In Progress</div>
          <div className="stat-val" style={{color:'#2563EB'}}>{stats.open||0}</div>
          <div className="stat-sub">Needs attention</div>
        </div>
        <div className="stat-card">
          <div className="stat-icon">✅</div>
          <div className="stat-label">Resolved</div>
          <div className="stat-val" style={{color:'var(--green)'}}>{stats.resolvedLast90||0}</div>
          <div className="stat-sub">In selected range</div>
        </div>
        <div className="stat-card">
          <div className="stat-icon">⏱️</div>
          <div className="stat-label">Avg Resolution</div>
          <div className="stat-val">{stats.avgResolutionHours != null ? `${stats.avgResolutionHours}h` : '—'}</div>
          <div className="stat-sub">Created → resolved</div>
        </div>
      </div>

      <div style={{display:'grid',gridTemplateColumns:'1fr 1fr',gap:20,marginBottom:20}}>
        {/* Status breakdown bars */}
        <div className="card card-pad">
          <div style={{fontWeight:700,fontSize:14,color:'var(--navy)',marginBottom:16}}>Tickets by Status</div>
          {statusOrder.filter(s => statusMap[s]).map(s => (
            <div key={s} style={{marginBottom:10}}>
              <div style={{display:'flex',justifyContent:'space-between',marginBottom:4}}>
                <span style={{fontSize:12,fontWeight:600,color:'var(--g600)',textTransform:'capitalize'}}>{s.replace('-',' ')}</span>
                <span style={{fontSize:12,fontWeight:700,color:'var(--navy)'}}>{statusMap[s]}</span>
              </div>
              <div style={{background:'var(--g100)',borderRadius:4,height:6}}>
                <div style={{background:STAT_COLORS[s]||'var(--g400)',borderRadius:4,height:6,
                             width: stats.total ? `${Math.round(statusMap[s]/stats.total*100)}%` : '0%',transition:'width .4s'}}/>
              </div>
            </div>
          ))}
        </div>

        {/* Open by technician */}
        <div className="card card-pad">
          <div style={{fontWeight:700,fontSize:14,color:'var(--navy)',marginBottom:16}}>Open Tickets by Technician</div>
          {(stats.openByTech||[]).length === 0
            ? <div style={{color:'var(--g400)',fontSize:13}}>No open tickets.</div>
            : (stats.openByTech||[]).map(t => {
                const max = Math.max(1, ...stats.openByTech.map(x => x.count));
                return (
                  <div key={t.name} style={{marginBottom:10}}>
                    <div style={{display:'flex',justifyContent:'space-between',marginBottom:4}}>
                      <span style={{fontSize:12,fontWeight:600,color:'var(--g600)'}}>{t.name}</span>
                      <span style={{fontSize:12,fontWeight:700,color:'var(--navy)'}}>{t.count}</span>
                    </div>
                    <div style={{background:'var(--g100)',borderRadius:4,height:6}}>
                      <div style={{background: t.name==='Unassigned' ? '#BC141E' : 'var(--navy)',borderRadius:4,height:6,
                                   width:`${Math.round(t.count/max*100)}%`,transition:'width .4s'}}/>
                    </div>
                  </div>
                );
              })}
        </div>
      </div>

      {/* 12-week created vs resolved */}
      <div className="card card-pad" style={{marginBottom:20}}>
        <div style={{fontWeight:700,fontSize:14,color:'var(--navy)',marginBottom:16}}>
          Ticket volume
          <span style={{marginLeft:12,fontSize:11,fontWeight:600,color:'#2563EB'}}>■ Created</span>
          <span style={{marginLeft:8,fontSize:11,fontWeight:600,color:'#16A34A'}}>■ Resolved</span>
        </div>
        <div style={{display:'flex',alignItems:'flex-end',gap:6,height:148,paddingTop:18}}>
          {(stats.weekly||[]).map((w,i) => (
            <div key={i}
              onMouseEnter={() => setHoverWeek(i)} onMouseLeave={() => setHoverWeek(x => x === i ? null : x)}
              style={{flex:1,display:'flex',flexDirection:'column',alignItems:'center',gap:3,position:'relative',
                      cursor:'default',background: hoverWeek===i ? 'var(--off)' : 'transparent',borderRadius:6}}>
              {hoverWeek===i && (
                <div style={{position:'absolute',bottom:'100%',left:'50%',transform:'translateX(-50%)',marginBottom:4,
                             background:'var(--navy)',color:'white',borderRadius:6,padding:'6px 10px',fontSize:11,
                             whiteSpace:'nowrap',zIndex:20,boxShadow:'0 4px 12px rgba(16,30,46,.25)',pointerEvents:'none'}}>
                  <div style={{fontWeight:700,marginBottom:2}}>Week of {new Date(w.weekStart+'T00:00:00').toLocaleDateString()}</div>
                  <span style={{color:'#93C5FD',fontWeight:600}}>{w.created} created</span>
                  <span style={{opacity:.6}}> · </span>
                  <span style={{color:'#86EFAC',fontWeight:600}}>{w.resolved} resolved</span>
                </div>
              )}
              <div style={{display:'flex',alignItems:'flex-end',gap:2,height:110,width:'100%',justifyContent:'center'}}>
                <div style={{width:'42%',background:'#2563EB',borderRadius:'3px 3px 0 0',opacity: hoverWeek==null||hoverWeek===i ? 1 : .45,
                             height:`${Math.round(w.created/maxWeekly*100)}%`,minHeight: w.created?2:0,transition:'height .4s, opacity .15s'}}/>
                <div style={{width:'42%',background:'#16A34A',borderRadius:'3px 3px 0 0',opacity: hoverWeek==null||hoverWeek===i ? 1 : .45,
                             height:`${Math.round(w.resolved/maxWeekly*100)}%`,minHeight: w.resolved?2:0,transition:'height .4s, opacity .15s'}}/>
              </div>
              <div style={{fontSize:9,color:'var(--g400)'}}>{w.label || new Date(w.weekStart+'T00:00:00').toLocaleDateString(undefined,{month:'numeric',day:'numeric'})}</div>
            </div>
          ))}
        </div>
      </div>

      {/* Custom field breakdowns */}
      {(stats.fieldStats||[]).length > 0 && (
        <div style={{display:'grid',gridTemplateColumns:'repeat(auto-fit, minmax(320px, 1fr))',gap:20}}>
          {stats.fieldStats.map(f => (
            <div key={f.key} className="card card-pad">
              <div style={{fontWeight:700,fontSize:14,color:'var(--navy)',marginBottom:16}}>By {f.label}</div>
              <FieldPie values={f.values} />
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

// ── Percentage pie for a custom-field breakdown (pure SVG, no libs) ──
const PIE_COLORS = ['#162534','#2563EB','#8B5CF6','#D97706','#16A34A','#BC141E','#0891B2','#64748B','#DB2777','#84CC16'];

function FieldPie({ values, size = 150 }) {
  const total = values.reduce((s, v) => s + v.total, 0);
  if (!total) return <div style={{color:'var(--g400)',fontSize:13}}>No data.</div>;
  const cx = size/2, cy = size/2, r = size/2 - 2;
  let angle = -90;
  const slices = values.map((v, i) => {
    const frac = v.total / total;
    const a0 = angle * Math.PI/180;
    angle += frac * 360;
    const a1 = angle * Math.PI/180;
    const mid = (a0 + a1) / 2;
    const path = frac >= 0.999
      ? `M ${cx} ${cy - r} A ${r} ${r} 0 1 1 ${cx - 0.01} ${cy - r} Z`   // full circle
      : `M ${cx} ${cy} L ${cx + r*Math.cos(a0)} ${cy + r*Math.sin(a0)}` +
        ` A ${r} ${r} 0 ${frac > 0.5 ? 1 : 0} 1 ${cx + r*Math.cos(a1)} ${cy + r*Math.sin(a1)} Z`;
    return { ...v, frac, path, mid, color: PIE_COLORS[i % PIE_COLORS.length] };
  });
  const pct = f => (f * 100 >= 9.5 ? Math.round(f * 100) : (f * 100).toFixed(1)) + '%';
  return (
    <div style={{display:'flex',gap:18,alignItems:'center',flexWrap:'wrap'}}>
      <svg width={size} height={size} style={{flexShrink:0}}>
        {slices.map(s => <path key={s.value} d={s.path} fill={s.color} stroke="#fff" strokeWidth="1.5" />)}
        {/* in-slice % labels for slices big enough to hold them */}
        {slices.filter(s => s.frac >= 0.08).map(s => (
          <text key={'t'+s.value} x={cx + r*0.62*Math.cos(s.mid)} y={cy + r*0.62*Math.sin(s.mid)}
            textAnchor="middle" dominantBaseline="middle"
            fontSize="11" fontWeight="700" fill="#fff" style={{pointerEvents:'none'}}>
            {Math.round(s.frac*100)}%
          </text>
        ))}
      </svg>
      <div style={{flex:1,minWidth:140}}>
        {slices.map(s => (
          <div key={s.value} style={{display:'flex',alignItems:'center',gap:8,marginBottom:6}}>
            <span style={{width:10,height:10,borderRadius:3,background:s.color,flexShrink:0}} />
            <span style={{fontSize:12,fontWeight:600,color:'var(--g600)',flex:1,minWidth:0,overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap'}} title={s.value}>{s.value}</span>
            <span style={{fontSize:12,fontWeight:800,color:'var(--navy)'}}>{pct(s.frac)}</span>
            <span style={{fontSize:11,color:'var(--g400)'}}>({s.total})</span>
          </div>
        ))}
      </div>
    </div>
  );
}

// ── STAGE BADGE ──
function StageBadge({ stage }) {
  const LABELS = {
    lead:'Lead',contacted:'Contacted',demo:'Demo',
    proposal:'Proposal Sent',negotiation:'Negotiation',
    won:'Won',lost:'Lost'
  };
  const CLASSES = {
    lead:'badge-gray',contacted:'badge-blue',demo:'badge-purple',
    proposal:'badge-amber',negotiation:'badge-red',
    won:'badge-green',lost:'badge-gray'
  };
  return <span className={`badge ${CLASSES[stage]||'badge-gray'}`}>{LABELS[stage]||stage}</span>;
}

// ── ADMIN VIEW ──
// ── CONNECTORS ──
// Industry-specific integrations, each toggleable per deployment so the
// CRM ships clean for customers who don't use them. Config lives in
// SystemConfig connector.* keys; the frontend reads CRM.connectors
// (populated by DropdownsAPI.load) and hides gated UI when off.
function ConnectorsTab({ showToast }) {
  const [cfg, setCfg]       = useState(null);
  const [saving, setSaving] = useState(false);

  useEffect(() => {
    CRM.ConfigAPI.getAll().then(all => setCfg({
      aodEnabled: all['connector.aod.enabled'] !== 'false',
      aodApiUri:  all['connector.aod.apiUri'] || '',
      aodHasKey:  !!all['connector.aod.apiKey'],
      aodApiKey:  '',
    })).catch(() => setCfg({ aodEnabled: true, aodApiUri: '', aodHasKey: false, aodApiKey: '' }));
  }, []);

  const save = async () => {
    setSaving(true);
    try {
      await CRM.ConfigAPI.set('connector.aod.enabled', cfg.aodEnabled ? 'true' : 'false');
      await CRM.ConfigAPI.set('connector.aod.apiUri', cfg.aodApiUri.trim());
      if (cfg.aodApiKey.trim()) await CRM.ConfigAPI.set('connector.aod.apiKey', cfg.aodApiKey.trim());
      window.CRM.connectors = { ...(window.CRM.connectors || {}), aod: cfg.aodEnabled };
      setCfg(c => ({ ...c, aodHasKey: c.aodHasKey || !!c.aodApiKey.trim(), aodApiKey: '' }));
      showToast('Connector settings saved. Reload the page to apply everywhere.', 'success');
    } catch (e) { showToast('Save failed: ' + e.message, 'error'); }
    finally { setSaving(false); }
  };

  if (!cfg) return <div style={{padding:40,color:'var(--g400)',textAlign:'center'}}>Loading connectors…</div>;

  return (
    <div>
      <div style={{fontSize:13,color:'var(--g600)',marginBottom:16}}>
        Industry integrations. Disabled connectors hide all of their fields, tabs, and menu items across the CRM.
      </div>

      <div className="card card-pad" style={{marginBottom:16, maxWidth:640}}>
        <div style={{display:'flex',justifyContent:'space-between',alignItems:'center',marginBottom:6}}>
          <div style={{fontWeight:700,fontSize:14,color:'var(--navy)'}}>📊 Attendance on Demand (AoD)</div>
          <label style={{display:'flex',alignItems:'center',gap:7,fontSize:13,cursor:'pointer'}}>
            <input type="checkbox" checked={cfg.aodEnabled} onChange={e => setCfg(c => ({...c, aodEnabled: e.target.checked}))} />
            Enabled
          </label>
        </div>
        <div style={{fontSize:12,color:'var(--g400)',marginBottom:12,lineHeight:1.6}}>
          Time &amp; attendance billing integration: the AoD Usage view, monthly billing import,
          per-account AoD Usage tab, and the AoD URI field on accounts.
          Turn off for deployments that don't resell AoD.
        </div>
        {cfg.aodEnabled && (
          <div className="fg fg-2" style={{marginBottom:0}}>
            <div className="field" style={{marginBottom:0}}>
              <label>API Base URI <span style={{fontWeight:400,color:'var(--g400)',fontSize:11}}>(for upcoming live AoD pulls — optional)</span></label>
              <input value={cfg.aodApiUri} onChange={e => setCfg(c => ({...c, aodApiUri: e.target.value}))} placeholder="https://…attendanceondemand.com" />
            </div>
            <div className="field" style={{marginBottom:0}}>
              <label>API Key <span style={{fontWeight:400,color:'var(--g400)',fontSize:11}}>{cfg.aodHasKey ? '(saved — enter to replace)' : '(optional)'}</span></label>
              <input type="password" value={cfg.aodApiKey} onChange={e => setCfg(c => ({...c, aodApiKey: e.target.value}))} placeholder={cfg.aodHasKey ? '••••••••' : ''} />
            </div>
          </div>
        )}
      </div>

      <button className="btn btn-primary" onClick={save} disabled={saving}>{saving ? 'Saving…' : 'Save'}</button>
    </div>
  );
}

// ── HELPDESK SETTINGS ──
// Email-to-ticket ingestion from the support mailbox (Zendesk-style).
// Runs in parallel with Zendesk: auto-ack stays OFF until cutover so
// customers never hear from two systems.
function HelpdeskTab({ showToast }) {
  const [cfg, setCfg]       = useState(null);
  const [saving, setSaving] = useState(false);
  const [runningNow, setRunningNow] = useState(false);
  const [runResult, setRunResult]   = useState(null);   // IngestSummary from Run now

  const load = () => CRM.ConfigAPI.getAll().then(all => {
    let customFields = [];
    try {
      const parsed = JSON.parse(all['ticket.customFields'] || '[]');
      if (Array.isArray(parsed)) customFields = parsed;
    } catch { /* corrupt config — start fresh */ }
    setCfg({
      supportMailbox:  all['ticket.supportMailbox'] || '',
      ingestEnabled:   all['ticket.ingestEnabled'] === 'true',
      autoAck:         all['ticket.autoAck'] === 'true',
      ackText:         all['ticket.ackText'] || '',
      outboundEnabled: all['ticket.outboundEnabled'] === 'true',
      testDomain:      all['ticket.testDomain'] || '',
      defaultCategory: all['ticket.defaultCategory'] || 'Technical',
      lastSync:        all['ticket.lastSync'] || '',
      spamSenders:     (() => { try { const v = JSON.parse(all['ticket.spamSenders'] || '[]'); return Array.isArray(v) ? v : []; } catch { return []; } })(),
      lastError:       all['ticket.lastError'] || '',
      customFields:    customFields.map(f => ({ key: f.key || '', label: f.label || '', optionsText: (f.options || []).join(', '), required: !!f.required })),
    });
  }).catch(() => {
    // NEVER fall back to editable defaults: saving them would overwrite the
    // real config (this is how ingestion once got silently disabled).
    setCfg({ loadFailed: true });
    showToast('Failed to load helpdesk settings — fix the connection and reload before saving.', 'error');
  });

  useEffect(() => { load(); }, []);

  const save = async () => {
    if (cfg.loadFailed)
      return showToast('Settings failed to load — reload the page before saving.', 'error');
    if (cfg.ingestEnabled && !cfg.supportMailbox.trim())
      return showToast('Enter the support mailbox address first.', 'error');
    setSaving(true);
    try {
      await CRM.ConfigAPI.set('ticket.supportMailbox', cfg.supportMailbox.trim());
      await CRM.ConfigAPI.set('ticket.ingestEnabled', cfg.ingestEnabled ? 'true' : 'false');
      await CRM.ConfigAPI.set('ticket.autoAck', cfg.autoAck ? 'true' : 'false');
      await CRM.ConfigAPI.set('ticket.ackText', cfg.ackText || '');
      await CRM.ConfigAPI.set('ticket.outboundEnabled', cfg.outboundEnabled ? 'true' : 'false');
      await CRM.ConfigAPI.set('ticket.testDomain', (cfg.testDomain || '').trim().replace(/^[@.*]+/, '').toLowerCase());
      await CRM.ConfigAPI.set('ticket.defaultCategory', cfg.defaultCategory);
      // Custom ticket fields: drop empty labels, derive a unique key from the
      // label when the row doesn't already have one (keeps stored ticket
      // values linked when a label is merely re-worded).
      const usedKeys = {};
      const defs = [];
      for (const f of (cfg.customFields || [])) {
        const label = (f.label || '').trim();
        if (!label) continue;
        let base = (f.key || '').trim() || label.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '') || 'field';
        let key = base, n = 2;
        while (usedKeys[key]) key = base + '-' + (n++);
        usedKeys[key] = true;
        defs.push({ key, label, options: (f.optionsText || '').split(',').map(s => s.trim()).filter(Boolean), required: !!f.required });
      }
      await CRM.ConfigAPI.set('ticket.customFields', JSON.stringify(defs));
      setCfg(c => ({ ...c, customFields: defs.map(f => ({ key: f.key, label: f.label, optionsText: f.options.join(', '), required: !!f.required })) }));
      showToast('Helpdesk settings saved. Ingestion checks every 2 minutes.', 'success');
    } catch (e) { showToast('Save failed: ' + e.message, 'error'); }
    finally { setSaving(false); }
  };

  const setCF = (i, k, v) => setCfg(c => ({ ...c, customFields: c.customFields.map((f, j) => j === i ? { ...f, [k]: v } : f) }));
  const removeCF = (i) => setCfg(c => ({ ...c, customFields: c.customFields.filter((_, j) => j !== i) }));
  const addCF = () => setCfg(c => ({ ...c, customFields: [...(c.customFields || []), { key:'', label:'', optionsText:'', required:false }] }));

  if (!cfg) return <div style={{padding:40,color:'var(--g400)',textAlign:'center'}}>Loading helpdesk settings…</div>;
  if (cfg.loadFailed) return (
    <div style={{padding:40,textAlign:'center'}}>
      <div style={{color:'#991B1B',fontSize:14,fontWeight:600,marginBottom:12}}>⚠️ Helpdesk settings failed to load.</div>
      <div style={{color:'var(--g600)',fontSize:13,marginBottom:16}}>Editing is disabled so a save can't overwrite your real configuration.</div>
      <button className="btn btn-primary btn-sm" onClick={load}>↻ Retry</button>
    </div>
  );

  return (
    <div style={{maxWidth:680}}>
      <div style={{fontSize:13,color:'var(--g600)',marginBottom:16}}>
        Customer emails to the support mailbox become tickets automatically: replies thread into the
        existing ticket, senders are matched to accounts (exact contact email, then company domain),
        and unmatched mail lands unassigned for triage. Every technician is emailed on new tickets.
      </div>

      <div className="card card-pad" style={{marginBottom:16}}>
        <div style={{display:'flex',justifyContent:'space-between',alignItems:'center',marginBottom:12}}>
          <div style={{fontWeight:700,fontSize:14,color:'var(--navy)'}}>📥 Email Ingestion</div>
          <label style={{display:'flex',alignItems:'center',gap:7,fontSize:13,cursor:'pointer'}}>
            <input type="checkbox" checked={cfg.ingestEnabled} onChange={e => setCfg(c => ({...c, ingestEnabled: e.target.checked}))} />
            Enabled
          </label>
        </div>
        <div className="fg fg-2" style={{marginBottom:10}}>
          <div className="field">
            <label>Support Mailbox</label>
            <input value={cfg.supportMailbox} onChange={e => setCfg(c => ({...c, supportMailbox: e.target.value}))} placeholder="support@ctrny.com" />
          </div>
          <div className="field">
            <label>Default Category for email tickets</label>
            <select value={cfg.defaultCategory} onChange={e => setCfg(c => ({...c, defaultCategory: e.target.value}))}>
              {(CRM.TICKET_CATEGORIES || ['Technical']).map(c => <option key={c}>{c}</option>)}
            </select>
          </div>
        </div>
        <div style={{fontSize:12,color:'var(--g400)',lineHeight:1.6}}>
          The mailbox must be in the Graph access-policy group
          (<code>Add-DistributionGroupMember "CRM Mail Sync" -Member support@…</code>) and Microsoft 365
          sync must be configured in Email Settings. Do <strong>not</strong> also tick this mailbox in the
          mail-sync list — the helpdesk owns it.
        </div>
        <div style={{fontSize:12,color:'var(--g600)',marginTop:10,display:'flex',alignItems:'center',gap:12,flexWrap:'wrap'}}>
          <span>Last check: <strong>{cfg.lastSync ? new Date(cfg.lastSync).toLocaleString() : 'never'}</strong></span>
          <button className="btn btn-ghost btn-sm" disabled={runningNow} onClick={async () => {
            setRunningNow(true); setRunResult(null);
            try {
              const r = await CRM.ConfigAPI.runIngestNow();
              setRunResult(r);
              load();
            } catch (e) { showToast('Run failed: ' + e.message, 'error'); }
            finally { setRunningNow(false); }
          }}>{runningNow ? 'Running…' : '▶ Run ingestion now'}</button>
          <button className="btn btn-ghost btn-sm" onClick={async () => {
            if (!confirm('Re-scan the support inbox?\n\nThe next ingestion cycle re-walks the mailbox and picks up any emails missed in the last 72 hours (e.g. after an error). Already-ticketed emails are skipped, so no duplicates.')) return;
            try {
              const r = await CRM.ConfigAPI.rescanInbox();
              showToast(r.message || 'Re-scan queued.', 'success');
              load();
            } catch (e) { showToast('Re-scan failed: ' + e.message, 'error'); }
          }}>↻ Re-scan inbox</button>
        </div>
        {runResult && (
          <div style={{marginTop:8,padding:'9px 12px',borderRadius:6,fontSize:12,border:'1px solid var(--g200)',background:'var(--off)',color:'var(--g600)'}}>
            {!runResult.ran
              ? <span>⏸ Didn't run: {runResult.error}</span>
              : <span>
                  ✅ Cycle finished ({runResult.pages} page{runResult.pages===1?'':'s'} walked):&nbsp;
                  <strong style={{color:'var(--navy)'}}>{runResult.created} ticket{runResult.created===1?'':'s'} created</strong>, {runResult.threaded} repl{runResult.threaded===1?'y':'ies'} threaded
                  {runResult.skippedInternal ? <span> · <strong>{runResult.skippedInternal} skipped as internal senders</strong> (mail from CRM users never tickets — test from an outside address)</span> : ''}
                  {runResult.skippedOld ? ` · ${runResult.skippedOld} older than the baseline` : ''}
                  {runResult.deduped ? ` · ${runResult.deduped} already ticketed` : ''}
                  {runResult.spam ? ` · ${runResult.spam} spam-blocked` : ''}
                  {runResult.error ? <span style={{color:'#991B1B'}}> · stopped on error: {runResult.error}</span> : ''}
                  {runResult.inboxCount != null && (
                    <span> · Inbox holds <strong>{runResult.inboxCount}</strong> message{runResult.inboxCount===1?'':'s'}
                      {runResult.newestReceived ? <span>; newest anywhere: {new Date(runResult.newestReceived).toLocaleString()} — “{runResult.newestSubject || ''}”</span> : ''}
                    </span>
                  )}
                </span>}
            {runResult.folders && runResult.folders.length > 0 && (
              <div style={{marginTop:6,fontSize:11,color:'var(--g600)'}}>
                Folders: {runResult.folders.map(f => `${f.name} (${f.count})`).join(' · ')}
              </div>
            )}
          </div>
        )}
        {cfg.lastError && (
          <div style={{marginTop:8,padding:'9px 12px',borderRadius:6,fontSize:12,background:'#FEF2F2',color:'#991B1B',border:'1px solid #FECACA'}}>
            ⚠️ {cfg.lastError}
          </div>
        )}
      </div>

      <div className="card card-pad" style={{marginBottom:16}}>
        <div style={{fontWeight:700,fontSize:14,color:'var(--navy)',marginBottom:6}}>🧩 Custom Ticket Fields</div>
        <div style={{fontSize:12,color:'var(--g400)',marginBottom:12,lineHeight:1.6}}>
          These appear as searchable dropdowns on every ticket. Examples: Software, Hardware, Environment.
        </div>
        {(cfg.customFields || []).length > 0 && (
          <div style={{display:'grid',gridTemplateColumns:'180px 1fr 76px 30px',gap:'6px 8px',alignItems:'center',marginBottom:10}}>
            <div style={{fontSize:11,fontWeight:700,letterSpacing:'.05em',textTransform:'uppercase',color:'var(--g400)'}}>Label</div>
            <div style={{fontSize:11,fontWeight:700,letterSpacing:'.05em',textTransform:'uppercase',color:'var(--g400)'}}>Options (comma-separated)</div>
            <div style={{fontSize:11,fontWeight:700,letterSpacing:'.05em',textTransform:'uppercase',color:'var(--g400)'}}>Required</div>
            <div />
            {cfg.customFields.map((f, i) => (
              <React.Fragment key={i}>
                <div className="field" style={{margin:0}}>
                  <input value={f.label} onChange={e => setCF(i, 'label', e.target.value)} placeholder="e.g. Software" />
                </div>
                <div className="field" style={{margin:0}}>
                  <input value={f.optionsText} onChange={e => setCF(i, 'optionsText', e.target.value)} placeholder="TimeSuite, AoD, Portal" />
                </div>
                <label style={{display:'flex',alignItems:'center',justifyContent:'center',cursor:'pointer'}}>
                  <input type="checkbox" checked={!!f.required} onChange={e => setCF(i, 'required', e.target.checked)} />
                </label>
                <button className="btn btn-ghost btn-xs" title="Remove field" onClick={() => removeCF(i)}>×</button>
              </React.Fragment>
            ))}
          </div>
        )}
        <button className="btn btn-ghost btn-sm" onClick={addCF}>+ Add Field</button>
        <div style={{fontSize:12,color:'var(--g400)',marginTop:8}}>Rows with an empty label are dropped on save.</div>
      </div>

      <div className="card card-pad" style={{marginBottom:16}}>
        <div style={{fontWeight:700,fontSize:14,color:'var(--navy)',marginBottom:6}}>🚫 Blocked senders (spam)</div>
        <div style={{fontSize:12,color:'var(--g400)',marginBottom:10,lineHeight:1.6}}>
          Emails from these addresses/domains never become tickets. Add senders from a ticket's 🚫 action.
          The company's own domain (ctrny.com) can never be blocked.
        </div>
        {(cfg.spamSenders || []).length === 0
          ? <div style={{fontSize:13,color:'var(--g400)'}}>Nothing blocked yet.</div>
          : (cfg.spamSenders || []).map(sd => (
              <span key={sd} style={{display:'inline-flex',alignItems:'center',gap:6,background:'var(--off)',border:'1px solid var(--g200)',
                borderRadius:999,padding:'3px 10px',fontSize:12,fontWeight:600,color:'var(--navy)',margin:'0 6px 6px 0'}}>
                {sd}
                <button title="Unblock" onClick={async () => {
                  const next = cfg.spamSenders.filter(x => x !== sd);
                  try {
                    await CRM.ConfigAPI.set('ticket.spamSenders', JSON.stringify(next));
                    setCfg(c => ({ ...c, spamSenders: next }));
                    showToast(sd + ' unblocked.', 'success');
                  } catch (e) { showToast('Failed: ' + e.message, 'error'); }
                }} style={{border:'none',background:'none',cursor:'pointer',color:'var(--g400)',fontSize:13,padding:0,lineHeight:1}}>×</button>
              </span>
            ))}
      </div>

      <div className="card card-pad" style={{marginBottom:16, borderLeft:'3px solid var(--amber)'}}>
        <label style={{display:'flex',alignItems:'center',gap:7,fontSize:13,cursor:'pointer',fontWeight:600,color:'var(--navy)'}}>
          <input type="checkbox" checked={cfg.autoAck} onChange={e => setCfg(c => ({...c, autoAck: e.target.checked}))} />
          Send auto-acknowledgment to customers on new tickets
        </label>
        <div className="field" style={{marginTop:10}}>
          <label>Acknowledgment text</label>
          <textarea rows={3} value={cfg.ackText}
            onChange={e => setCfg(c => ({...c, ackText: e.target.value}))}
            placeholder={"Thanks for reaching out — we've logged your request as ticket {ticket} and a technician will follow up shortly."} />
          <div style={{fontSize:12,color:'var(--g400)',marginTop:4}}>{'{ticket}'} inserts the ticket number. Leave blank to use the default text.</div>
        </div>
        <label style={{display:'flex',alignItems:'center',gap:7,fontSize:13,cursor:'pointer',fontWeight:600,color:'var(--navy)',marginTop:10}}>
          <input type="checkbox" checked={cfg.outboundEnabled} onChange={e => setCfg(c => ({...c, outboundEnabled: e.target.checked}))} />
          Allow replying to customers from tickets
        </label>
        <div style={{fontSize:12,color:'#92400E',marginTop:8,lineHeight:1.6}}>
          ⚠️ Keep both of these <strong>OFF while running in parallel with Zendesk</strong> — otherwise customers
          receive responses from two systems for the same request. Flipping them on <strong>is</strong> the cutover switch.
        </div>
        <div className="field" style={{marginTop:14}}>
          <label>Test mode domain {cfg.testDomain?.trim() ? <span className="badge badge-amber" style={{marginLeft:6}}>ACTIVE</span> : null}</label>
          <input value={cfg.testDomain || ''} placeholder="e.g. aphy.io"
            onChange={e => setCfg(c => ({...c, testDomain: e.target.value}))} />
          <div style={{fontSize:12,color:'var(--g400)',marginTop:4,lineHeight:1.6}}>
            Full email communications (auto-acknowledgment + ticket replies) are enabled for senders at this
            domain — and its subdomains — even while the switches above are off. Real customers stay untouched.
            Clear the field to end the test.
          </div>
        </div>
      </div>

      <button className="btn btn-primary" onClick={save} disabled={saving}>{saving ? 'Saving…' : 'Save'}</button>
      <button className="btn btn-ghost" style={{marginLeft:8}} onClick={load}>↻ Refresh status</button>
    </div>
  );
}

// ── SCHEMA HEALTH BANNER ──
// Shown on every Settings screen when the database is missing schema the
// code expects (the app's SQL login can't run DDL, so fixes are manual
// db-update scripts — this names the exact one to run).
function SchemaHealthBanner() {
  const [health, setHealth] = useState(null);
  useEffect(() => { CRM.SchemaHealthAPI.get().then(setHealth).catch(() => {}); }, []);
  if (!health || health.ok) return null;
  return (
    <div className="card card-pad" style={{ marginBottom:16, borderLeft:'4px solid var(--red)', background:'#FEF2F2' }}>
      <div style={{ fontWeight:700, fontSize:14, color:'#991B1B', marginBottom:8 }}>
        ⚠ Database schema is behind this app version
      </div>
      <div style={{ fontSize:13, color:'var(--g600)', marginBottom:10 }}>
        Features touching the items below will fail until the matching script is run against the CTRNYCrm database (SSMS, admin credentials):
      </div>
      <ul style={{ margin:0, paddingLeft:20, fontSize:13, color:'#991B1B' }}>
        {health.missing.map((m, i) => (
          <li key={i} style={{ marginBottom:4 }}>
            Missing <strong>{m.what}</strong> — run <code style={{ background:'#fff', padding:'1px 6px', borderRadius:4, border:'1px solid #FECACA' }}>{m.fixScript}</code>
          </li>
        ))}
      </ul>
    </div>
  );
}

function AdminView({ user, showToast, tab }) {
  const [users, setUsers] = useState([]);
  const [adding, setAdding] = useState(false);
  const [newUser, setNewUser] = useState({ name:'', email:'', phone:'', role:'rep', isAdmin:false });
  const [editingId, setEditingId] = useState(null);
  const [editForm, setEditForm] = useState({});
  const [pwUserId, setPwUserId] = useState(null);
  const [newPw, setNewPw] = useState('');
  const [saving, setSaving] = useState(false);

  useEffect(() => {
    CRM.UsersAPI.fetch().then(setUsers).catch(() => {});
  }, []);

  const reload = () => setUsers(CRM.UsersAPI.getAll());

  const addUser = async () => {
    if (!newUser.name || !newUser.email) return;
    setSaving(true);
    try {
      await CRM.UsersAPI.create(newUser);
      reload();
      setNewUser({ name:'', email:'', phone:'', role:'rep' });
      setAdding(false);
      showToast('User created — default password: ctrny2024', 'success');
    } catch(e) { showToast('Error: ' + (e.message || 'Could not add user.'), 'error'); }
    finally { setSaving(false); }
  };

  const startEdit = (u) => {
    setEditingId(u.id);
    setEditForm({ name: u.name, email: u.email, phone: u.phone||'', role: u.role, isAdmin: u.isAdmin||false, restrictTicketView: u.restrictTicketView||false });
    setPwUserId(null);
  };

  const saveEdit = async () => {
    setSaving(true);
    try {
      await CRM.UsersAPI.update(editingId, editForm);
      reload();
      setEditingId(null);
      showToast('User updated!', 'success');
    } catch(e) { showToast('Save failed: ' + e.message, 'error'); }
    finally { setSaving(false); }
  };

  const resetPw = async (id, name) => {
    if (!confirm(`Reset ${name}'s password to "ctrny2024"?`)) return;
    try {
      await CRM.UsersAPI.resetPassword(id);
      showToast(`Password reset to ctrny2024 for ${name}.`, 'success');
    } catch(e) { showToast('Reset failed: ' + e.message, 'error'); }
  };

  const setCustomPw = async (id) => {
    if (!newPw || newPw.length < 6) { showToast('Password must be at least 6 characters.', 'error'); return; }
    setSaving(true);
    try {
      await CRM.UsersAPI.setPassword(id, newPw);
      setNewPw('');
      setPwUserId(null);
      showToast('Password updated!', 'success');
    } catch(e) { showToast('Failed: ' + e.message, 'error'); }
    finally { setSaving(false); }
  };

  const removeUser = async (id) => {
    if (id === user.id) { showToast('Cannot remove yourself.'); return; }
    if (!confirm('Deactivate this user? They will no longer be able to sign in.')) return;
    try {
      await CRM.UsersAPI.delete(id);
      reload();
      showToast('User deactivated.', 'success');
    } catch(e) { showToast('Error: ' + (e.message || 'Could not remove user.'), 'error'); }
  };

  const ROLE_BADGE = { tech:'badge-purple', rep:'badge-blue' };

  return (
    <div>
      <SchemaHealthBanner />
      {tab === 'users' && (
        <div>
          <div style={{display:'flex',justifyContent:'space-between',alignItems:'center',marginBottom:16}}>
            <div style={{fontSize:14,color:'var(--g600)'}}>Manage CRM users, roles, and passwords.</div>
            <button className="btn btn-primary btn-sm" onClick={() => { setAdding(!adding); setEditingId(null); }}>+ Add User</button>
          </div>

          {/* Add user form */}
          {adding && (
            <div className="card card-pad" style={{marginBottom:16,borderLeft:'3px solid var(--red)'}}>
              <div style={{fontSize:12,fontWeight:700,letterSpacing:'.1em',textTransform:'uppercase',color:'var(--red)',marginBottom:12}}>New User</div>
              <div className="fg fg-2">
                <div className="field"><label>Full Name *</label><input value={newUser.name} onChange={e=>setNewUser({...newUser,name:e.target.value})} /></div>
                <div className="field"><label>Email *</label><input type="email" value={newUser.email} onChange={e=>setNewUser({...newUser,email:e.target.value})} /></div>
              </div>
              <div className="fg fg-2">
                <div className="field"><label>Phone</label><input value={newUser.phone} onChange={e=>setNewUser({...newUser,phone:e.target.value})} /></div>
                <div className="field"><label>Role</label>
                  <select value={newUser.role} onChange={e=>setNewUser({...newUser,role:e.target.value})}>
                    <option value="rep">Sales Rep</option>
                    <option value="tech">Tech Support</option>
                  </select>
                </div>
              </div>
              <div style={{display:'flex',alignItems:'center',gap:8,marginBottom:10}}>
                <input type="checkbox" id="new-admin-chk" checked={newUser.isAdmin} onChange={e=>setNewUser({...newUser,isAdmin:e.target.checked})} />
                <label htmlFor="new-admin-chk" style={{fontSize:13,color:'var(--g600)',cursor:'pointer'}}>Grant admin privileges (Settings, user management, delete access)</label>
              </div>
              <div style={{fontSize:12,color:'var(--g400)',marginBottom:10}}>Default password will be <strong>ctrny2024</strong> — user can change it after first login.</div>
              <div style={{display:'flex',gap:8}}>
                <button className="btn btn-primary btn-sm" onClick={addUser} disabled={saving}>{saving?'Creating…':'Create User'}</button>
                <button className="btn btn-ghost btn-sm" onClick={() => setAdding(false)}>Cancel</button>
              </div>
            </div>
          )}

          {/* Users table */}
          <div className="card">
            {users.map(u => (
              <div key={u.id} style={{borderBottom:'1px solid var(--g100)',padding:'16px 20px'}}>
                {editingId === u.id ? (
                  /* ── Edit mode ── */
                  <div>
                    <div className="fg fg-2" style={{marginBottom:10}}>
                      <div className="field"><label>Full Name</label><input value={editForm.name} onChange={e=>setEditForm(f=>({...f,name:e.target.value}))} /></div>
                      <div className="field"><label>Email</label><input type="email" value={editForm.email} onChange={e=>setEditForm(f=>({...f,email:e.target.value}))} /></div>
                    </div>
                    <div className="fg fg-2" style={{marginBottom:10}}>
                      <div className="field"><label>Phone</label><input value={editForm.phone} onChange={e=>setEditForm(f=>({...f,phone:e.target.value}))} /></div>
                      <div className="field"><label>Role</label>
                        <select value={editForm.role} onChange={e=>setEditForm(f=>({...f,role:e.target.value}))}>
                          <option value="rep">Sales Rep</option>
                          <option value="tech">Tech Support</option>
                        </select>
                      </div>
                    </div>

                    <div style={{display:'flex',alignItems:'center',gap:8,marginBottom:10}}>
                      <input type="checkbox" id={`admin-chk-${u.id}`} checked={editForm.isAdmin||false} onChange={e=>setEditForm(f=>({...f,isAdmin:e.target.checked}))} />
                      <label htmlFor={`admin-chk-${u.id}`} style={{fontSize:13,color:'var(--g600)',cursor:'pointer'}}>Admin privileges</label>
                    </div>

                    <div style={{display:'flex',alignItems:'center',gap:8,marginBottom:10}}>
                      <input type="checkbox" id={`restrict-chk-${u.id}`} checked={editForm.restrictTicketView||false} onChange={e=>setEditForm(f=>({...f,restrictTicketView:e.target.checked}))} />
                      <label htmlFor={`restrict-chk-${u.id}`} style={{fontSize:13,color:'var(--g600)',cursor:'pointer'}}>🔒 Only own tickets (hide other techs' tickets from this user)</label>
                    </div>

                    {/* Inline password change */}
                    {pwUserId === u.id ? (
                      <div style={{background:'var(--off)',borderRadius:6,padding:12,marginBottom:10}}>
                        <div style={{fontSize:12,fontWeight:600,color:'var(--g600)',marginBottom:8}}>Set New Password</div>
                        <div style={{display:'flex',gap:8,alignItems:'center'}}>
                          <input type="password" placeholder="Min 6 characters" value={newPw}
                            onChange={e=>setNewPw(e.target.value)}
                            style={{flex:1,border:'1.5px solid var(--g200)',borderRadius:5,padding:'8px 12px',fontSize:13,fontFamily:'var(--font)',outline:'none'}} />
                          <button className="btn btn-primary btn-sm" onClick={() => setCustomPw(u.id)} disabled={saving}>Save</button>
                          <button className="btn btn-ghost btn-sm" onClick={() => { setPwUserId(null); setNewPw(''); }}>✕</button>
                        </div>
                      </div>
                    ) : (
                      <button className="btn btn-ghost btn-sm" style={{marginBottom:10}} onClick={() => setPwUserId(u.id)}>🔑 Change Password</button>
                    )}

                    <div style={{display:'flex',gap:8}}>
                      <button className="btn btn-primary btn-sm" onClick={saveEdit} disabled={saving}>{saving?'Saving…':'Save Changes'}</button>
                      <button className="btn btn-ghost btn-sm" onClick={() => { setEditingId(null); setPwUserId(null); setNewPw(''); }}>Cancel</button>
                    </div>
                  </div>
                ) : (
                  /* ── View mode ── */
                  <div style={{display:'flex',alignItems:'center',gap:14}}>
                    <div style={{width:40,height:40,borderRadius:'50%',background:'var(--navy)',color:'white',display:'flex',alignItems:'center',justifyContent:'center',fontSize:15,fontWeight:700,flexShrink:0}}>{u.name.charAt(0)}</div>
                    <div style={{flex:1}}>
                      <div style={{display:'flex',alignItems:'center',gap:8,marginBottom:2}}>
                        <span style={{fontSize:14,fontWeight:700,color:'var(--navy)'}}>{u.name}</span>
                        <span className={`badge ${ROLE_BADGE[u.role]||'badge-gray'}`}>{u.role === 'rep' ? 'Sales Rep' : u.role === 'tech' ? 'Tech' : u.role}</span>
                        {u.isAdmin && <span className="badge badge-red">Admin</span>}
                        {u.restrictTicketView && <span className="badge badge-gray">🔒 own tickets</span>}
                      </div>
                      <div style={{fontSize:12,color:'var(--g400)'}}>{u.email}{u.phone?` · ${u.phone}`:''}</div>
                    </div>
                    <div style={{display:'flex',gap:6,flexShrink:0}}>
                      <button className="btn btn-ghost btn-xs" onClick={() => startEdit(u)}>✏️ Edit</button>
                      <button className="btn btn-ghost btn-xs" onClick={() => resetPw(u.id, u.name)}>🔑 Reset PW</button>
                      {u.id !== user.id && (
                        <button className="btn btn-xs" style={{background:'#fee2e2',color:'var(--red)',border:'none'}} onClick={() => removeUser(u.id)}>Remove</button>
                      )}
                    </div>
                  </div>
                )}
              </div>
            ))}
          </div>
        </div>
      )}

      {tab === 'dropdowns' && <FieldOptionsTab showToast={showToast} />}

      {tab === 'connectors' && <ConnectorsTab showToast={showToast} />}

      {tab === 'helpdesk' && <HelpdeskTab showToast={showToast} />}

      {tab === 'apikeys' && <ApiKeysTab showToast={showToast} />}

      {tab === 'eventlog'     && <EventLogTab       showToast={showToast} />}
      {tab === 'ordercatalog'    && <OrderCatalogAdmin    showToast={showToast} />}
      {tab === 'salestax'        && <SalesTaxTab          showToast={showToast} />}

      {tab === 'settings'        && <CompanySettingsTab    showToast={showToast} />}
      {tab === 'aiagents'        && <AiAgentsTab          showToast={showToast} />}
      {tab === 'authentication'  && <AuthenticationTab    showToast={showToast} />}
      {tab === 'email'           && <EmailSettingsTab     showToast={showToast} />}

      {tab === 'proposaltpl' && <ProposalTemplateEditor showToast={showToast} />}

      {tab === 'portal' && (
        <div className="card card-pad">
          <div style={{fontSize:14,color:'var(--g600)',marginBottom:16}}>Manage proposal portal settings, branding, hardware options, and pricing.</div>
          <button className="btn btn-navy" onClick={() => window.open('Proposal Admin.html','_blank')}>Open Proposal Admin →</button>
        </div>
      )}
    </div>
  );
}

// ── FIELD OPTIONS (Admin dropdown editor) ──
const _DD_GROUPS = [
  { key:'industries',       label:'Industries',            desc:'Used on Lead and Account forms.' },
  { key:'employeeCounts',   label:'Employee Count Ranges', desc:'Used on Lead and Account forms.' },
  { key:'leadSources',      label:'Lead Sources',          desc:'How the lead found us — used on Lead forms.' },
  { key:'ticketCategories', label:'Ticket Categories',     desc:'Used when creating and filtering Help Desk tickets.' },
  { key:'hardwareOptions',  label:'Hardware / Products',   desc:'Shown in Lead hardware interest and the proposal generator.' },
  { key:'orderTerms',       label:'Order Terms',           desc:'Payment-terms options on the Order Form.' },
];

function DropdownGroupEditor({ group, values, onSave, saving }) {
  const isHw = group.key === 'hardwareOptions';
  // For hardware we keep the WHOLE rich object alongside; otherwise just the label.
  const normalize = vs => isHw
    ? vs.map(v => ({ raw: v, id: v.id, label: v.name }))
    : vs.map(v => ({ label: v }));

  const [items, setItems] = useState(() => normalize(values));
  const [editIdx, setEditIdx] = useState(null);
  const [editVal, setEditVal] = useState('');
  const [newVal, setNewVal] = useState('');
  const [contentEditIdx, setContentEditIdx] = useState(null); // hardware-only modal

  useEffect(() => { setItems(normalize(values)); }, [JSON.stringify(values)]);

  const toSave = items => isHw
    ? items.map(it => ({ ...(it.raw || {}), id: it.id, name: it.label }))
    : items.map(it => it.label);

  const commit = updated => { onSave(group.key, toSave(updated)); setItems(updated); };
  const deleteItem = idx  => commit(items.filter((_, i) => i !== idx));
  const startEdit  = idx  => { setEditIdx(idx); setEditVal(items[idx].label); };

  const finishEdit = () => {
    if (!editVal.trim() || editIdx === null) { setEditIdx(null); return; }
    commit(items.map((v, i) => i === editIdx ? { ...v, label: editVal.trim() } : v));
    setEditIdx(null);
  };

  const addItem = () => {
    const t = newVal.trim();
    if (!t) return;
    const newItem = isHw
      ? { raw: { id: CRM.DropdownsAPI.slugify(t), name: t }, id: CRM.DropdownsAPI.slugify(t), label: t }
      : { label: t };
    commit([...items, newItem]);
    setNewVal('');
  };

  const saveContent = (idx, patched) => {
    const next = items.map((v, i) =>
      i === idx
        ? { ...v, raw: { ...(v.raw || {}), ...patched, id: v.id, name: v.label } }
        : v
    );
    commit(next);
    setContentEditIdx(null);
  };

  return (
    <div className="card card-pad" style={{marginBottom:16}}>
      <div style={{display:'flex',alignItems:'flex-start',justifyContent:'space-between',marginBottom:12}}>
        <div>
          <div style={{fontSize:14,fontWeight:700,color:'var(--navy)'}}>{group.label}</div>
          <div style={{fontSize:12,color:'var(--g400)',marginTop:2}}>{group.desc}</div>
        </div>
        {saving && <span style={{fontSize:11,color:'var(--g400)',alignSelf:'center'}}>Saving…</span>}
      </div>

      <div style={{display:'flex',flexWrap:'wrap',gap:8,marginBottom:12,minHeight:32}}>
        {items.length === 0 && <span style={{fontSize:13,color:'var(--g400)',fontStyle:'italic'}}>No options yet.</span>}
        {items.map((item, idx) => {
          const raw = item.raw || {};
          const hasContent = isHw && (raw.purchasePrice != null || raw.rentalPrice != null || raw.imagePath || raw.badge || (Array.isArray(raw.specs) && raw.specs.length));
          return editIdx === idx ? (
            <div key={idx} style={{display:'flex',gap:4,alignItems:'center'}}>
              <input autoFocus value={editVal} onChange={e => setEditVal(e.target.value)}
                onKeyDown={e => { if (e.key === 'Enter') finishEdit(); if (e.key === 'Escape') setEditIdx(null); }}
                style={{border:'1.5px solid var(--navy)',borderRadius:5,padding:'5px 10px',fontSize:13,fontFamily:'var(--font)',outline:'none',minWidth:140}} />
              <button className="btn btn-primary btn-xs" onClick={finishEdit}>✓</button>
              <button className="btn btn-ghost btn-xs" onClick={() => setEditIdx(null)}>✕</button>
            </div>
          ) : (
            <div key={idx} style={{display:'flex',alignItems:'center',gap:2,background:'var(--off)',border:'1px solid var(--g200)',borderRadius:20,padding:'4px 8px 4px 12px'}}>
              <span style={{fontSize:13,color:'var(--navy)',fontWeight:500}}>{item.label}</span>
              {isHw && hasContent && (
                <span title="Has proposal content" style={{fontSize:9,color:'#10B981',marginLeft:4}}>●</span>
              )}
              <button onClick={() => startEdit(idx)} title="Rename"
                style={{background:'none',border:'none',cursor:'pointer',padding:'0 4px',color:'var(--g400)',fontSize:11,lineHeight:1}}>✏</button>
              {isHw && (
                <button onClick={() => setContentEditIdx(idx)} title="Edit proposal content"
                  style={{background:'none',border:'none',cursor:'pointer',padding:'0 4px',color:'var(--navy)',fontSize:13,lineHeight:1}}>⚙</button>
              )}
              <button onClick={() => deleteItem(idx)} title="Delete"
                style={{background:'none',border:'none',cursor:'pointer',padding:'0 4px',color:'var(--red)',fontSize:16,fontWeight:700,lineHeight:1}}>×</button>
            </div>
          );
        })}
      </div>

      <div style={{display:'flex',gap:8,alignItems:'center'}}>
        <input value={newVal} onChange={e => setNewVal(e.target.value)}
          onKeyDown={e => e.key === 'Enter' && newVal.trim() && addItem()}
          placeholder="Add new option…"
          style={{border:'1.5px solid var(--g200)',borderRadius:5,padding:'7px 12px',fontSize:13,fontFamily:'var(--font)',outline:'none',background:'var(--off)',width:220}} />
        <button className="btn btn-ghost btn-sm" onClick={addItem} disabled={!newVal.trim()}>+ Add</button>
      </div>

      {isHw && contentEditIdx !== null && (
        <HardwareContentModal
          item={items[contentEditIdx]}
          onCancel={() => setContentEditIdx(null)}
          onSave={patched => saveContent(contentEditIdx, patched)}
        />
      )}
    </div>
  );
}

// ── Hardware proposal-content editor modal ────────────────
function HardwareContentModal({ item, onSave, onCancel }) {
  const r = item.raw || {};
  const [form, setForm] = useState({
    badge:         r.badge         || '',
    badgeType:     r.badgeType     || 'standard',
    imagePath:     r.imagePath     || '',
    purchasePrice: r.purchasePrice != null ? String(r.purchasePrice) : '',
    rentalPrice:   r.rentalPrice   != null ? String(r.rentalPrice)   : '',
    rentalUnit:    r.rentalUnit    || '/mo',
    specs:         Array.isArray(r.specs) ? r.specs.join('\n') : (r.specs || ''),
  });
  const [uploading, setUploading] = useState(false);
  const fileInputRef = React.useRef(null);
  const setF = (k, v) => setForm(f => ({ ...f, [k]: v }));

  const handleUpload = async (e) => {
    const file = e.target.files?.[0];
    if (!file) return;
    setUploading(true);
    try {
      const res = await CRM.UploadsAPI.image(file);
      setF('imagePath', res.path);
    } catch (err) {
      alert('Upload failed: ' + err.message);
    } finally {
      setUploading(false);
      if (fileInputRef.current) fileInputRef.current.value = '';
    }
  };

  const handleSave = () => {
    onSave({
      badge:         form.badge.trim() || null,
      badgeType:     form.badgeType || 'standard',
      imagePath:     form.imagePath.trim() || null,
      purchasePrice: form.purchasePrice !== '' ? parseFloat(form.purchasePrice) : null,
      rentalPrice:   form.rentalPrice   !== '' ? parseFloat(form.rentalPrice)   : null,
      rentalUnit:    form.rentalUnit.trim() || null,
      specs:         form.specs.trim()
                       ? form.specs.split('\n').map(s => s.trim()).filter(Boolean)
                       : [],
    });
  };

  return (
    <div className="modal-overlay" {...overlayClose(onCancel)}
      style={{position:'fixed',inset:0,background:'rgba(0,0,0,.45)',zIndex:9000,display:'flex',alignItems:'center',justifyContent:'center',padding:20}}>
      <div className="card" style={{background:'#fff',borderRadius:8,maxWidth:560,width:'100%',maxHeight:'90vh',overflow:'auto',padding:24}}>
        <div style={{fontSize:16,fontWeight:700,color:'var(--navy)',marginBottom:4}}>Edit Proposal Content</div>
        <div style={{fontSize:13,color:'var(--g600)',marginBottom:18}}>{item.label}</div>

        <div className="fg fg-2" style={{marginBottom:10}}>
          <div className="field"><label>Badge <span style={{color:'var(--g400)',fontWeight:400}}>(e.g. "Face + Finger")</span></label>
            <input value={form.badge} onChange={e => setF('badge', e.target.value)} /></div>
          <div className="field"><label>Badge Type</label>
            <select value={form.badgeType} onChange={e => setF('badgeType', e.target.value)}>
              <option value="standard">Standard (red)</option>
              <option value="free">Free / Included (green)</option>
            </select>
          </div>
        </div>

        <div className="field" style={{marginBottom:10}}>
          <label>Image <span style={{color:'var(--g400)',fontWeight:400}}>(saved to wwwroot/uploads)</span></label>
          <div style={{display:'flex',gap:8,alignItems:'center'}}>
            <input value={form.imagePath} onChange={e => setF('imagePath', e.target.value)}
              placeholder="uploads/example.png" style={{flex:1}} />
            <button type="button" className="btn btn-ghost btn-sm"
              onClick={() => fileInputRef.current?.click()} disabled={uploading}>
              {uploading ? 'Uploading…' : '⬆ Upload'}
            </button>
            <input ref={fileInputRef} type="file" accept="image/*" style={{display:'none'}} onChange={handleUpload} />
          </div>
          {form.imagePath && (
            <div style={{marginTop:8,padding:6,border:'1px solid var(--g200)',borderRadius:4,background:'var(--off)',display:'inline-block'}}>
              <img src={form.imagePath} alt="" style={{maxHeight:80,maxWidth:160,display:'block'}}
                onError={e => { e.target.style.display='none'; }} />
            </div>
          )}
        </div>

        <div className="fg fg-3" style={{marginBottom:10}}>
          <div className="field"><label>Purchase Price</label>
            <input type="number" min="0" step="0.01" value={form.purchasePrice}
              onChange={e => setF('purchasePrice', e.target.value)} placeholder="0.00" />
          </div>
          <div className="field"><label>Rental Price</label>
            <input type="number" min="0" step="0.01" value={form.rentalPrice}
              onChange={e => setF('rentalPrice', e.target.value)} placeholder="0.00" />
          </div>
          <div className="field"><label>Rental Unit</label>
            <input value={form.rentalUnit} onChange={e => setF('rentalUnit', e.target.value)} placeholder="/mo" />
          </div>
        </div>

        <div className="field" style={{marginBottom:18}}>
          <label>Specs <span style={{color:'var(--g400)',fontWeight:400}}>(one bullet per line)</span></label>
          <textarea rows={6} value={form.specs} onChange={e => setF('specs', e.target.value)}
            style={{width:'100%',resize:'vertical',padding:8,border:'1px solid var(--g200)',borderRadius:4,fontSize:13,fontFamily:'inherit'}}
            placeholder={'Touchless face recognition\nDual verification supported\n10,000+ user capacity'} />
        </div>

        <div style={{display:'flex',gap:10,justifyContent:'flex-end'}}>
          <button className="btn btn-ghost" onClick={onCancel}>Cancel</button>
          <button className="btn btn-primary" onClick={handleSave}>Save Content</button>
        </div>
      </div>
    </div>
  );
}

function FieldOptionsTab({ showToast }) {
  const [saving, setSaving] = useState('');
  const [dropdowns, setDropdowns] = useState({
    industries:       [...CRM.INDUSTRIES],
    employeeCounts:   [...CRM.EMPLOYEE_COUNTS],
    leadSources:      [...CRM.LEAD_SOURCES],
    ticketCategories: [...CRM.TICKET_CATEGORIES],
    hardwareOptions:  [...CRM.HW_OPTIONS],
    orderTerms:       [...(CRM.ORDER_TERMS || [])],
  });

  const handleSave = async (key, values) => {
    setSaving(key);
    try {
      await CRM.DropdownsAPI.save(key, values);
      setDropdowns(d => ({ ...d, [key]: values }));
      showToast('Saved!', 'success');
    } catch (e) {
      showToast('Save failed: ' + e.message, 'error');
    } finally { setSaving(''); }
  };

  return (
    <div>
      <div style={{fontSize:13,color:'var(--g600)',marginBottom:20}}>
        Customize dropdown options used throughout the CRM. Click <strong>✏</strong> to rename, <strong>×</strong> to delete, or type a new value to add. Changes take effect immediately.
      </div>
      {_DD_GROUPS.map(g => (
        <DropdownGroupEditor key={g.key} group={g} values={dropdowns[g.key]}
          onSave={handleSave} saving={saving === g.key} />
      ))}
    </div>
  );
}

// ── ENDPOINT REFERENCE ──
const METHOD_COLOR = { GET:'#3B82F6', POST:'#10B981', PUT:'#F59E0B', PATCH:'#F97316', DELETE:'#EF4444', SSE:'#8B5CF6' };
const EP_SECTIONS = [
  { label:'🔐 Auth', base:'/api/auth', rows:[
    ['POST',  '/login',           'Sign in with email + password'],
    ['POST',  '/logout',          'End current session'],
    ['GET',   '/me',              'Current user profile & role'],
    ['GET',   '/ms-login',        'Initiate Microsoft OIDC flow'],
    ['POST',  '/ms-link',         'Link Microsoft account to CRM user'],
    ['POST',  '/change-password', 'Change own password'],
  ]},
  { label:'👥 Leads', base:'/api/leads', rows:[
    ['GET',    '',                    'List leads  ?stage  ?repId  ?search'],
    ['GET',    '/{id}',               'Get lead by ID'],
    ['POST',   '',                    'Create lead'],
    ['PUT',    '/{id}',               'Update lead'],
    ['PATCH',  '/{id}/stage',         'Move to new stage'],
    ['DELETE', '/{id}',               'Delete lead', 'admin'],
    ['GET',    '/{id}/activities',    'Activity log for lead'],
    ['POST',   '/{id}/activities',    'Add activity (note/call/email…)'],
    ['GET',    '/api/activities/recent', 'Recent activity feed  ?limit', '', true],
  ]},
  { label:'🏢 Accounts', base:'/api/accounts', rows:[
    ['GET',    '',                           'List accounts  ?repId  ?showClosed'],
    ['GET',    '/{id}',                      'Get account (with contacts & tickets)'],
    ['POST',   '',                           'Create account'],
    ['PUT',    '/{id}',                      'Update account'],
    ['PATCH',  '/{id}/close',                'Mark account closed', 'admin'],
    ['PATCH',  '/{id}/reopen',               'Reopen closed account', 'admin'],
    ['DELETE', '/{id}',                      'Delete account', 'admin'],
    ['GET',    '/{id}/contacts',             'List contacts for account'],
    ['POST',   '/{id}/contacts',             'Add contact'],
    ['PUT',    '/{id}/contacts/{contactId}', 'Update contact'],
    ['DELETE', '/{id}/contacts/{contactId}', 'Delete contact'],
    ['GET',    '/{id}/sales',                'List sales records'],
    ['POST',   '/{id}/sales',                'Record a sale'],
    ['DELETE', '/{id}/sales/{saleId}',       'Delete sale record'],
    ['GET',    '/{id}/activities',           'Activity log for account'],
    ['POST',   '/{id}/activities',           'Add activity (note/call/email…)'],
    ['GET',    '/{id}/aod',                  'AoD billing usage for account'],
  ]},
  { label:'🎫 Tickets', base:'/api/tickets', rows:[
    ['GET',    '',                  'List tickets  ?status  ?priority  ?accountId'],
    ['GET',    '/{id}',             'Get ticket with comments'],
    ['POST',   '',                  'Create ticket'],
    ['PUT',    '/{id}',             'Update ticket'],
    ['PATCH',  '/{id}/status',      'Update status only'],
    ['DELETE', '/{id}',             'Delete ticket', 'admin'],
    ['GET',    '/{id}/comments',    'List comments'],
    ['POST',   '/{id}/comments',    'Add comment'],
  ]},
  { label:'👤 Users', base:'/api/users', rows:[
    ['GET',    '',                      'List all users'],
    ['GET',    '/{id}',                 'Get user by ID'],
    ['POST',   '',                      'Create user', 'admin'],
    ['PUT',    '/{id}',                 'Update user (role, admin flag, active)', 'admin'],
    ['DELETE', '/{id}',                 'Delete user', 'admin'],
    ['POST',   '/{id}/reset-password',  'Reset password to default', 'admin'],
    ['POST',   '/{id}/set-password',    'Set specific password', 'admin'],
  ]},
  { label:'📈 Reports', base:'/api/reports', rows:[
    ['GET', '/pipeline',    'Pipeline funnel by stage'],
    ['GET', '/forecast',    'Revenue forecast & closed YTD'],
    ['GET', '/leaderboard', 'Rep leaderboard by won revenue', 'admin'],
    ['GET', '/tickets',     'Ticket stats by status & priority'],
  ]},
  { label:'📎 Files', base:'/api/files', rows:[
    ['GET',    '/lead/{leadId}',        'List files for a lead'],
    ['GET',    '/account/{accountId}',  'List files for an account'],
    ['POST',   '/lead/{leadId}',        'Upload file to lead (25 MB max)'],
    ['POST',   '/account/{accountId}',  'Upload file to account (25 MB max)'],
    ['GET',    '/{id}/download',        'Download file'],
    ['DELETE', '/{id}',                 'Delete file'],
  ]},
  { label:'⬆ Import', base:'/api/import', rows:[
    ['POST', '/leads',    'Bulk import leads from CSV or Excel'],
    ['POST', '/accounts', 'Bulk import accounts from CSV or Excel'],
    ['POST', '/tickets',  'Bulk import tickets from CSV or Excel'],
    ['POST', '/aod',      'Import AoD billing data (Excel or TSV) — upserts by URI + Invoice Date + Description', 'admin'],
  ]},
  { label:'🔑 API Keys', base:'/api/apikeys', note:'Admin only', rows:[
    ['GET',   '',              'List all API keys', 'admin'],
    ['POST',  '',              'Create API key', 'admin'],
    ['PATCH', '/{id}/revoke',  'Revoke key (keeps record)', 'admin'],
    ['DELETE','/{id}',         'Permanently delete key', 'admin'],
  ]},
  { label:'📋 Audit Log', base:'/api/auditlog', rows:[
    ['GET',    '',        'Query log  ?category  ?severity  ?limit  ?offset', 'admin'],
    ['DELETE', '/purge',  'Purge entries older than N days  ?daysOld', 'admin'],
  ]},
  { label:'⚙️ Config', base:'/api/config', rows:[
    ['GET', '',       'Get all portal config values'],
    ['PUT', '/{key}', 'Set a config value', 'admin'],
  ]},
  { label:'📧 Email', base:'/api/email', rows:[
    ['POST', '/send',        'Send email (multipart/form-data; supports attachments)'],
    ['POST', '/test',        'Send test email  ?system=true forces catch-all config'],
    ['GET',  '/my-settings', "Get current user's personal SMTP settings"],
    ['PUT',  '/my-settings', "Save current user's personal SMTP settings"],
  ]},
  { label:'🌐 Public API', base:'/api/public', note:'Requires API Key header', rows:[
    ['GET',  '/pipeline',    'Pipeline funnel (read scope)'],
    ['GET',  '/forecast',    'Revenue forecast (read scope)'],
    ['GET',  '/leads',       'Lead list  ?stage  ?search  ?limit (read scope)'],
    ['GET',  '/accounts',    'Active account list  ?limit (read scope)'],
    ['GET',  '/leaderboard', 'Rep leaderboard (read scope)'],
    ['POST', '/leads',       'Create lead (write scope)'],
  ]},
  { label:'🤖 MCP Server', base:'', rows:[
    ['SSE', '/mcp', 'AI agent endpoint — all CRM tools (API key required)'],
  ]},
];

function EndpointReference() {
  const [open, setOpen] = React.useState(false);
  return (
    <div className="card card-pad" style={{marginTop:20,background:'var(--off)',border:'1px solid var(--g200)'}}>
      <div style={{display:'flex',alignItems:'center',justifyContent:'space-between',cursor:'pointer'}}
        onClick={() => setOpen(o => !o)}>
        <div style={{fontWeight:700,fontSize:13,color:'var(--navy)'}}>REST Endpoints &amp; MCP Server</div>
        <span style={{fontSize:12,color:'var(--g400)'}}>{open ? '▲ Collapse' : '▼ Expand all endpoints'}</span>
      </div>
      {open && (
        <div style={{marginTop:14,display:'flex',flexDirection:'column',gap:16}}>
          {EP_SECTIONS.map(sec => (
            <div key={sec.label}>
              <div style={{display:'flex',alignItems:'center',gap:8,marginBottom:6}}>
                <span style={{fontWeight:700,fontSize:12,color:'var(--navy)'}}>{sec.label}</span>
                <span style={{fontSize:11,fontFamily:'monospace',color:'var(--g400)'}}>{sec.base}</span>
                {sec.note && <span style={{fontSize:11,background:'#FEF3C7',color:'#92400E',padding:'1px 7px',borderRadius:10,fontWeight:600}}>{sec.note}</span>}
              </div>
              <div style={{fontSize:12,fontFamily:'monospace',lineHeight:1,display:'flex',flexDirection:'column',gap:1}}>
                {sec.rows.map(([method, path, desc, auth, absPath]) => (
                  <div key={method+path} style={{display:'grid',gridTemplateColumns:'56px 1fr auto',gap:8,padding:'4px 8px',borderRadius:4,background:'white',border:'1px solid var(--g100)'}}>
                    <span style={{fontWeight:700,color:METHOD_COLOR[method]||'var(--g600)',letterSpacing:'.5px'}}>{method}</span>
                    <span style={{color:'var(--navy)'}}>{absPath ? path : sec.base + path}</span>
                    <span style={{color:'var(--g400)',fontFamily:'var(--font)',fontSize:11,textAlign:'right',whiteSpace:'nowrap'}}>
                      {auth === 'admin' && <span style={{marginRight:6,background:'#fee2e2',color:'#b91c1c',padding:'1px 6px',borderRadius:10,fontWeight:700,fontSize:10}}>admin</span>}
                      {desc}
                    </span>
                  </div>
                ))}
              </div>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

// ── API KEYS ──
function ApiKeysTab({ showToast }) {
  const [keys, setKeys]         = useState([]);
  const [loading, setLoading]   = useState(true);
  const [showForm, setShowForm] = useState(false);
  const [form, setForm]         = useState({ name:'', description:'', scopes:'read' });
  const [saving, setSaving]     = useState(false);
  const [newKey, setNewKey]     = useState(null); // raw key shown once after creation

  const load = async () => {
    setLoading(true);
    try { setKeys(await CRM.ApiKeyAPI.getAll()); }
    catch (e) { showToast('Failed to load API keys: ' + e.message, 'error'); }
    finally { setLoading(false); }
  };

  useEffect(() => { load(); }, []);

  const create = async () => {
    if (!form.name.trim()) { showToast('Name is required.', 'error'); return; }
    setSaving(true);
    try {
      const result = await CRM.ApiKeyAPI.create(form);
      setNewKey(result.rawKey);
      setShowForm(false);
      setForm({ name:'', description:'', scopes:'read' });
      await load();
    } catch (e) { showToast('Failed to create key: ' + e.message, 'error'); }
    finally { setSaving(false); }
  };

  const revoke = async (id, name) => {
    if (!confirm(`Revoke key "${name}"? It will stop working immediately.`)) return;
    try {
      await CRM.ApiKeyAPI.revoke(id);
      showToast('Key revoked.', 'success');
      await load();
    } catch (e) { showToast('Failed to revoke: ' + e.message, 'error'); }
  };

  const del = async (id, name) => {
    if (!confirm(`Permanently delete key "${name}"?`)) return;
    try {
      await CRM.ApiKeyAPI.delete(id);
      showToast('Key deleted.', 'success');
      await load();
    } catch (e) { showToast('Failed to delete: ' + e.message, 'error'); }
  };

  const copyKey = () => {
    if (!newKey) return;
    navigator.clipboard.writeText(newKey).then(
      () => showToast('Key copied to clipboard!', 'success'),
      () => showToast('Copy failed — please select and copy manually.', 'error')
    );
  };

  const SCOPE_BADGES = {
    read:  { label:'Read',  bg:'#EFF6FF', color:'#3B82F6' },
    write: { label:'Write', bg:'#ECFDF5', color:'#10B981' },
    admin: { label:'Admin', bg:'#FEF3C7', color:'#D97706' },
  };

  return (
    <div>
      {/* One-time key display */}
      {newKey && (
        <div className="card card-pad" style={{marginBottom:20,borderLeft:'3px solid var(--green)',background:'#F0FDF4'}}>
          <div style={{fontWeight:700,fontSize:14,color:'#15803d',marginBottom:8}}>🔑 API Key Created — Copy It Now</div>
          <div style={{fontSize:12,color:'#166534',marginBottom:12}}>
            This key will <strong>not</strong> be shown again. Store it securely.
          </div>
          <div style={{display:'flex',gap:8,alignItems:'center'}}>
            <code style={{flex:1,fontFamily:'monospace',fontSize:13,padding:'10px 14px',background:'white',border:'1.5px solid #86efac',borderRadius:6,wordBreak:'break-all'}}>
              {newKey}
            </code>
            <button className="btn btn-primary btn-sm" onClick={copyKey}>Copy</button>
          </div>
          <div style={{marginTop:12,fontSize:12,color:'#166534'}}>
            <strong>Usage:</strong> Send as <code>X-Api-Key: {newKey}</code> or <code>Authorization: Bearer {newKey}</code>
          </div>
          <button className="btn btn-ghost btn-sm" style={{marginTop:10}} onClick={() => setNewKey(null)}>Dismiss</button>
        </div>
      )}

      <div style={{display:'flex',justifyContent:'space-between',alignItems:'center',marginBottom:16}}>
        <div>
          <div style={{fontSize:14,fontWeight:600,color:'var(--navy)'}}>API Keys</div>
          <div style={{fontSize:12,color:'var(--g400)',marginTop:2}}>
            Keys authenticate external integrations and AI agents. Use <code>X-Api-Key</code> or <code>Authorization: Bearer</code> headers.
          </div>
        </div>
        <button className="btn btn-primary btn-sm" onClick={() => { setShowForm(s => !s); setNewKey(null); }}>+ Generate Key</button>
      </div>

      {/* Create form */}
      {showForm && (
        <div className="card card-pad" style={{marginBottom:16,borderLeft:'3px solid var(--red)'}}>
          <div style={{fontSize:12,fontWeight:700,letterSpacing:'.1em',textTransform:'uppercase',color:'var(--red)',marginBottom:12}}>New API Key</div>
          <div className="fg fg-2">
            <div className="field"><label>Name *</label>
              <input value={form.name} onChange={e=>setForm(f=>({...f,name:e.target.value}))} placeholder="e.g. AI Agent, Reporting Bot" />
            </div>
            <div className="field"><label>Scopes</label>
              <select value={form.scopes} onChange={e=>setForm(f=>({...f,scopes:e.target.value}))}>
                <option value="read">Read — view data only</option>
                <option value="read,write">Read + Write — view and modify data</option>
              </select>
            </div>
          </div>
          <div className="fg fg-1">
            <div className="field"><label>Description (optional)</label>
              <input value={form.description} onChange={e=>setForm(f=>({...f,description:e.target.value}))} placeholder="What is this key used for?" />
            </div>
          </div>
          <div style={{display:'flex',gap:8,marginTop:4}}>
            <button className="btn btn-primary btn-sm" onClick={create} disabled={saving}>{saving?'Creating…':'Create Key'}</button>
            <button className="btn btn-ghost btn-sm" onClick={() => setShowForm(false)}>Cancel</button>
          </div>
        </div>
      )}

      {/* Keys table */}
      {loading ? (
        <div style={{padding:40,textAlign:'center',color:'var(--g400)'}}>Loading…</div>
      ) : keys.length === 0 ? (
        <div className="empty">No API keys yet. Generate one to allow external access.</div>
      ) : (
        <div className="card">
          <table className="tbl">
            <thead>
              <tr>
                <th>Name</th>
                <th>Scopes</th>
                <th>Status</th>
                <th>Requests</th>
                <th>Last Used</th>
                <th>Created</th>
                <th></th>
              </tr>
            </thead>
            <tbody>
              {keys.map(k => (
                <tr key={k.id} style={!k.isActive ? {opacity:.5} : {}}>
                  <td>
                    <div style={{fontWeight:600,color:'var(--navy)'}}>{k.name}</div>
                    {k.description && <div style={{fontSize:11,color:'var(--g400)'}}>{k.description}</div>}
                  </td>
                  <td>
                    {k.scopes.split(',').map(s => {
                      const b = SCOPE_BADGES[s.trim()] || { label:s, bg:'#f3f4f6', color:'#6b7280' };
                      return <span key={s} style={{fontSize:11,fontWeight:600,padding:'2px 8px',borderRadius:20,background:b.bg,color:b.color,marginRight:4}}>{b.label}</span>;
                    })}
                  </td>
                  <td>
                    <span className={`badge ${k.isActive ? 'badge-green' : 'badge-gray'}`}>{k.isActive ? 'Active' : 'Revoked'}</span>
                  </td>
                  <td style={{fontFamily:'monospace',fontSize:12}}>{k.requestCount.toLocaleString()}</td>
                  <td style={{fontSize:12,color:'var(--g400)'}}>{k.lastUsedAt ? new Date(k.lastUsedAt).toLocaleDateString() : '—'}</td>
                  <td style={{fontSize:12,color:'var(--g400)'}}>{new Date(k.createdAt).toLocaleDateString()}</td>
                  <td>
                    <div style={{display:'flex',gap:6,justifyContent:'flex-end'}}>
                      {k.isActive && (
                        <button className="btn btn-ghost btn-xs" onClick={() => revoke(k.id, k.name)}>Revoke</button>
                      )}
                      <button className="btn btn-xs" style={{background:'#fee2e2',color:'var(--red)',border:'none'}}
                        onClick={() => del(k.id, k.name)}>Delete</button>
                    </div>
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      )}

      {/* Endpoint reference */}
      <EndpointReference />
    </div>
  );
}

// ── EVENT LOG ──
const LOG_CATEGORIES = ['login','user','lead','account','ticket','error','system'];
const CAT_COLORS = {
  login:'#3B82F6', user:'#8B5CF6', lead:'#F59E0B',
  account:'#10B981', ticket:'#6B7280', error:'#EF4444', system:'#9AA3B0'
};
const SEV_ICONS = { info:'✅', warning:'⚠️', error:'❌' };

function EventLogTab({ showToast }) {
  const [logs, setLogs]           = useState([]);
  const [loading, setLoading]     = useState(true);
  const [catFilter, setCat]       = useState('all');
  const [sevFilter, setSev]       = useState('all');
  const [detailLog, setDetailLog] = useState(null); // log entry shown in drill-down

  const load = async (cat, sev) => {
    setLoading(true);
    try {
      const data = await CRM.AuditLogAPI.getAll({ category: cat, severity: sev });
      setLogs(data);
    } catch (e) {
      showToast('Failed to load logs: ' + e.message, 'error');
    } finally { setLoading(false); }
  };

  useEffect(() => { load(catFilter, sevFilter); }, [catFilter, sevFilter]);

  const purge = async () => {
    if (!confirm('Delete all audit log entries older than 90 days?')) return;
    try {
      const r = await CRM.AuditLogAPI.purge(90);
      showToast(`Purged ${r.deleted} old log entries.`, 'success');
      load(catFilter, sevFilter);
    } catch (e) { showToast('Purge failed: ' + e.message, 'error'); }
  };

  return (
    <div>
      <div style={{display:'flex',gap:10,marginBottom:16,alignItems:'center',flexWrap:'wrap'}}>
        <select className="search-input" style={{maxWidth:170,flex:'unset'}} value={catFilter} onChange={e=>setCat(e.target.value)}>
          <option value="all">All Categories</option>
          {LOG_CATEGORIES.map(c => <option key={c} value={c}>{c.charAt(0).toUpperCase()+c.slice(1)}</option>)}
        </select>
        <select className="search-input" style={{maxWidth:145,flex:'unset'}} value={sevFilter} onChange={e=>setSev(e.target.value)}>
          <option value="all">All Severity</option>
          <option value="info">Info</option>
          <option value="warning">Warning</option>
          <option value="error">Error</option>
        </select>
        <button className="btn btn-ghost btn-sm" onClick={() => load(catFilter, sevFilter)}>↻ Refresh</button>
        <div style={{flex:1}} />
        <span style={{fontSize:12,color:'var(--g400)'}}>{logs.length} entries</span>
        <button className="btn btn-xs" style={{background:'#fee2e2',color:'var(--red)',border:'none'}} onClick={purge}>
          🗑 Purge &gt;90 days
        </button>
      </div>

      {loading ? (
        <div style={{padding:40,textAlign:'center',color:'var(--g400)'}}>Loading…</div>
      ) : logs.length === 0 ? (
        <div className="empty">No log entries found.</div>
      ) : (
        <div className="card" style={{overflowX:'auto'}}>
          <table className="tbl" style={{minWidth:700}}>
            <thead>
              <tr>
                <th style={{width:28}}></th>
                <th style={{whiteSpace:'nowrap'}}>Timestamp</th>
                <th>Category</th>
                <th>Action</th>
                <th>User</th>
                <th>Detail</th>
                <th>IP</th>
              </tr>
            </thead>
            <tbody>
              {logs.map(log => (
                <tr key={log.id}
                  style={log.severity==='error' ? {background:'#fef2f2'} : log.severity==='warning' ? {background:'#fffbeb'} : {}}>
                  <td style={{textAlign:'center',fontSize:14}}>{SEV_ICONS[log.severity]||'✅'}</td>
                  <td style={{whiteSpace:'nowrap',fontFamily:'monospace',fontSize:11,color:'var(--g600)'}}>
                    {new Date(log.timestamp).toLocaleString(undefined, CRM.timezone ? { timeZone: CRM.timezone } : {})}
                  </td>
                  <td>
                    <span style={{fontSize:11,fontWeight:600,padding:'2px 8px',borderRadius:20,
                      background:(CAT_COLORS[log.category]||'#9AA3B0')+'22',
                      color:CAT_COLORS[log.category]||'#9AA3B0'}}>
                      {log.category}
                    </span>
                  </td>
                  <td style={{fontWeight:600,fontSize:12,color:'var(--navy)'}}>{log.action}</td>
                  <td style={{fontSize:12,color:'var(--g600)'}}>{log.userName||'—'}</td>
                  <td style={{fontSize:12,color:'var(--g600)',maxWidth:320,overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap'}}>
                    {log.detail ? (
                      <span style={{display:'flex',alignItems:'center',gap:6}}>
                        <span style={{overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap',flex:1}} title={log.detail}>
                          {log.detail}
                        </span>
                        <button onClick={() => setDetailLog(log)}
                          style={{flexShrink:0,background:'none',border:'1px solid var(--g200)',borderRadius:4,
                            padding:'1px 6px',fontSize:10,cursor:'pointer',color:'var(--g400)',lineHeight:'18px',
                            fontFamily:'var(--font)',whiteSpace:'nowrap'}}
                          title="View full detail">
                          ⤢ expand
                        </button>
                      </span>
                    ) : '—'}
                  </td>
                  <td style={{fontSize:11,color:'var(--g400)',fontFamily:'monospace'}}>{log.ipAddress||'—'}</td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      )}

      {/* Detail drill-down modal */}
      {detailLog && (
        <div className="modal-overlay" {...overlayClose(() => setDetailLog(null))}>
          <div className="modal-panel" style={{width:'min(580px,96%)'}} onClick={e => e.stopPropagation()}>
            <div className="modal-hd">
              <div>
                <div className="modal-title" style={{display:'flex',alignItems:'center',gap:8}}>
                  {SEV_ICONS[detailLog.severity]||'✅'}
                  <span>{detailLog.action}</span>
                  <span style={{fontSize:11,fontWeight:400,padding:'2px 8px',borderRadius:20,
                    background:(CAT_COLORS[detailLog.category]||'#9AA3B0')+'22',
                    color:CAT_COLORS[detailLog.category]||'#9AA3B0'}}>
                    {detailLog.category}
                  </span>
                </div>
                <div style={{fontSize:11,color:'var(--g400)',marginTop:4,fontFamily:'monospace'}}>
                  {new Date(detailLog.timestamp).toLocaleString(undefined, CRM.timezone ? { timeZone: CRM.timezone } : {})}
                  {detailLog.userName ? ` · ${detailLog.userName}` : ''}
                  {detailLog.ipAddress ? ` · ${detailLog.ipAddress}` : ''}
                </div>
              </div>
              <button className="modal-close" onClick={() => setDetailLog(null)}>×</button>
            </div>
            <div className="modal-body">
              <div style={{
                background: detailLog.severity === 'error' ? '#fef2f2' : detailLog.severity === 'warning' ? '#fffbeb' : 'var(--off)',
                border: '1px solid var(--g200)',
                borderRadius: 7,
                padding: '14px 16px',
                fontSize: 13,
                color: 'var(--g800)',
                fontFamily: 'monospace',
                lineHeight: 1.7,
                whiteSpace: 'pre-wrap',
                wordBreak: 'break-word',
                maxHeight: 400,
                overflowY: 'auto',
              }}>
                {detailLog.detail || '(no detail)'}
              </div>
              <div style={{display:'flex',justifyContent:'flex-end',marginTop:14}}>
                <button className="btn btn-ghost" onClick={() => setDetailLog(null)}>Close</button>
              </div>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

// ── BOTTOM NAV (mobile only) ──
function BottomNav({ view, setView, user, onMore }) {
  const items = [
    { id:'dashboard', icon:'📊', label:'Home',     roles:['rep'] },
    { id:'leads',     icon:'👥', label:'Leads',    roles:['rep'] },
    { id:'accounts',  icon:'🏢', label:'Accounts', roles:['rep'] },
    { id:'tickets',   icon:'🎫', label:'Tickets',  roles:['rep','tech'] },
  ].filter(i => user.isAdmin || i.roles.includes(user.role));

  return (
    <div className="bottom-nav">
      {items.map(i => (
        <button key={i.id} className={`bnav-item${view===i.id?' active':''}`} onClick={() => setView(i.id)}>
          <span className="bnav-icon">{i.icon}</span>
          <span className="bnav-label">{i.label}</span>
        </button>
      ))}
      <button className="bnav-item" onClick={onMore}>
        <span className="bnav-icon">☰</span>
        <span className="bnav-label">More</span>
      </button>
    </div>
  );
}

// ── COMPANY SETTINGS ──
const COMMON_TIMEZONES = [
  { value: 'America/New_York',    label: 'Eastern (ET) — New York' },
  { value: 'America/Chicago',     label: 'Central (CT) — Chicago' },
  { value: 'America/Denver',      label: 'Mountain (MT) — Denver' },
  { value: 'America/Los_Angeles', label: 'Pacific (PT) — Los Angeles' },
  { value: 'America/Phoenix',     label: 'Arizona (no DST)' },
  { value: 'America/Anchorage',   label: 'Alaska (AKT)' },
  { value: 'Pacific/Honolulu',    label: 'Hawaii (HST)' },
  { value: 'UTC',                 label: 'UTC' },
];

function CompanySettingsTab({ showToast }) {
  const [timezone,     setTimezone]     = useState(CRM.timezone    || 'America/New_York');
  const [companyName,  setCompanyName]  = useState(CRM.companyName || '');
  const [logoPreview,  setLogoPreview]  = useState(CRM.companyLogo || null);
  const [logoDataUrl,  setLogoDataUrl]  = useState(null); // pending upload
  const [saving,       setSaving]       = useState(false);
  const logoRef = useRef(null);

  const preview = () => {
    try {
      return new Date().toLocaleString(undefined, { timeZone: timezone, hour12: true, dateStyle: 'medium', timeStyle: 'short' });
    } catch { return 'Invalid timezone'; }
  };

  const handleLogoFile = (e) => {
    const file = e.target.files?.[0];
    if (!file) return;
    if (!file.type.startsWith('image/'))
      return showToast('Please select an image file (PNG, JPG, SVG, etc.).', 'error');
    if (file.size > 2 * 1024 * 1024)
      return showToast('Logo must be under 2 MB.', 'error');
    const reader = new FileReader();
    reader.onload = (ev) => {
      setLogoDataUrl(ev.target.result);
      setLogoPreview(ev.target.result);
    };
    reader.readAsDataURL(file);
  };

  const clearLogo = async () => {
    if (!confirm('Remove the company logo?')) return;
    try {
      await CRM.ConfigAPI.set('company.logo', '');
      CRM.companyLogo = null;
      setLogoPreview(null);
      setLogoDataUrl(null);
      showToast('Logo removed.', 'success');
    } catch (e) { showToast('Failed to remove logo: ' + e.message, 'error'); }
  };

  const save = async () => {
    setSaving(true);
    try {
      await CRM.ConfigAPI.set('company.timezone', timezone);
      CRM.timezone = timezone;

      const trimName = companyName.trim();
      await CRM.ConfigAPI.set('company.name', trimName);
      CRM.companyName = trimName || null;

      if (logoDataUrl) {
        await CRM.ConfigAPI.set('company.logo', logoDataUrl);
        CRM.companyLogo = logoDataUrl;
        setLogoDataUrl(null);
      }

      showToast('Company settings saved!', 'success');
    } catch (e) {
      showToast('Save failed: ' + e.message, 'error');
    } finally { setSaving(false); }
  };

  return (
    <div style={{ maxWidth: 580 }}>
      {/* Branding */}
      <div className="card card-pad" style={{ marginBottom: 16 }}>
        <div style={{ fontSize: 14, fontWeight: 700, color: 'var(--navy)', marginBottom: 4 }}>Branding</div>
        <div style={{ fontSize: 12, color: 'var(--g400)', marginBottom: 16 }}>
          Company name and logo appear on generated order forms. Leave blank to use the built-in defaults.
        </div>

        <div className="field" style={{ marginBottom: 16 }}>
          <label>Company Name</label>
          <input value={companyName} onChange={e => setCompanyName(e.target.value)}
            placeholder="e.g. Acme Time Solutions" style={{ maxWidth: 340 }} />
        </div>

        <div style={{ marginBottom: 8, fontSize: 13, fontWeight: 600, color: 'var(--g600)' }}>Company Logo</div>
        <div style={{ display: 'flex', alignItems: 'flex-start', gap: 16, marginBottom: 8 }}>
          {logoPreview ? (
            <div style={{ position: 'relative', display: 'inline-block' }}>
              <img src={logoPreview} alt="Company logo"
                style={{ maxHeight: 80, maxWidth: 220, border: '1px solid var(--g200)', borderRadius: 6, padding: 6, background: '#fff', objectFit: 'contain' }} />
              <button onClick={clearLogo} title="Remove logo"
                style={{ position: 'absolute', top: -8, right: -8, background: '#EF4444', color: '#fff', border: 'none', borderRadius: '50%', width: 20, height: 20, cursor: 'pointer', fontSize: 12, lineHeight: '20px', textAlign: 'center', padding: 0 }}>
                ×
              </button>
            </div>
          ) : (
            <div style={{ width: 120, height: 60, border: '2px dashed var(--g200)', borderRadius: 6, display: 'flex', alignItems: 'center', justifyContent: 'center', color: 'var(--g400)', fontSize: 11 }}>
              No logo
            </div>
          )}
          <div>
            <input ref={logoRef} type="file" accept="image/*" style={{ display: 'none' }} onChange={handleLogoFile} />
            <button className="btn btn-ghost btn-sm" onClick={() => logoRef.current?.click()}>
              📁 {logoPreview ? 'Replace Logo' : 'Upload Logo'}
            </button>
            <div style={{ fontSize: 11, color: 'var(--g400)', marginTop: 6 }}>PNG, JPG, or SVG — max 2 MB</div>
            {logoDataUrl && (
              <div style={{ fontSize: 11, color: 'var(--green)', marginTop: 4 }}>✓ New logo ready — click Save to apply</div>
            )}
          </div>
        </div>
      </div>

      {/* Timezone */}
      <div className="card card-pad" style={{ marginBottom: 16 }}>
        <div style={{ fontSize: 14, fontWeight: 700, color: 'var(--navy)', marginBottom: 4 }}>Timezone</div>
        <div style={{ fontSize: 12, color: 'var(--g400)', marginBottom: 12 }}>
          Used for timestamps in the Event Log and other date displays.
        </div>
        <div className="field" style={{ marginBottom: 10 }}>
          <label>Select Timezone</label>
          <select value={timezone} onChange={e => setTimezone(e.target.value)}>
            {COMMON_TIMEZONES.map(tz => (
              <option key={tz.value} value={tz.value}>{tz.label}</option>
            ))}
          </select>
        </div>
        <div style={{ fontSize: 12, color: 'var(--g600)', padding: '6px 10px', background: 'var(--off)', borderRadius: 5 }}>
          Current time in selected timezone: <strong>{preview()}</strong>
        </div>
      </div>

      <button className="btn btn-primary btn-sm" onClick={save} disabled={saving}>
        {saving ? 'Saving…' : 'Save Settings'}
      </button>
    </div>
  );
}

// ── SALES TAX TAB ──────────────────────────────────────────
function SalesTaxTab({ showToast }) {
  const [rates,    setRates]    = useState([]);
  const [loading,  setLoading]  = useState(true);
  const [showAll,  setShowAll]  = useState(false);
  const [editing,  setEditing]  = useState(null);  // rate id or 'new'
  const [saving,   setSaving]   = useState(false);
  const [deleting, setDeleting] = useState(null);
  const [importing, setImporting] = useState(false);
  const importRef = useRef(null);

  const blankForm = () => ({ jurisdiction: '', rate: '', isActive: true });
  const [form, setForm] = useState(blankForm());

  const load = async () => {
    setLoading(true);
    try { setRates(await CRM.SalesTaxAPI.getAll(true)); }
    catch (e) { showToast('Failed to load rates: ' + e.message, 'error'); }
    finally { setLoading(false); }
  };

  useEffect(() => { load(); }, []);

  const visible = showAll ? rates : rates.filter(r => r.isActive);

  const startNew = () => { setForm(blankForm()); setEditing('new'); };
  const startEdit = (r) => {
    setForm({ jurisdiction: r.jurisdiction, rate: String(r.rate), isActive: r.isActive });
    setEditing(r.id);
  };
  const cancelEdit = () => { setEditing(null); setForm(blankForm()); };

  const doSave = async () => {
    if (!form.jurisdiction.trim()) return showToast('Jurisdiction is required.', 'error');
    if (form.rate === '' || isNaN(parseFloat(form.rate))) return showToast('A valid rate is required.', 'error');
    setSaving(true);
    try {
      if (editing === 'new') {
        const created = await CRM.SalesTaxAPI.create(form);
        setRates(prev => [...prev, created].sort((a, b) => a.jurisdiction.localeCompare(b.jurisdiction)));
        showToast('Rate added.', 'success');
      } else {
        const updated = await CRM.SalesTaxAPI.update(editing, form);
        setRates(prev => prev.map(r => r.id === editing ? updated : r));
        showToast('Rate saved.', 'success');
      }
      cancelEdit();
    } catch (e) { showToast('Save failed: ' + e.message, 'error'); }
    finally { setSaving(false); }
  };

  const doDelete = async (r) => {
    if (!confirm(`Delete "${r.jurisdiction}"?`)) return;
    setDeleting(r.id);
    try {
      await CRM.SalesTaxAPI.delete(r.id);
      setRates(prev => prev.filter(x => x.id !== r.id));
      showToast('Rate deleted.', 'success');
    } catch (e) { showToast('Delete failed: ' + e.message, 'error'); }
    finally { setDeleting(null); }
  };

  const doImport = async (e) => {
    const file = e.target.files?.[0];
    if (!file) return;
    e.target.value = '';
    setImporting(true);
    try {
      const result = await CRM.SalesTaxAPI.importCsv(file);
      await load();
      showToast(
        `Import complete — ${result.imported} added, ${result.updated} updated` +
        (result.skipped > 0 ? `, ${result.skipped} skipped` : '') + '.',
        'success'
      );
      if (result.errors?.length > 0)
        showToast('Some rows had errors: ' + result.errors.slice(0, 3).join('; '), 'error');
    } catch (e) { showToast('Import failed: ' + e.message, 'error'); }
    finally { setImporting(false); }
  };

  const set = (k, v) => setForm(f => ({ ...f, [k]: v }));

  return (
    <div>
      <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', marginBottom:18 }}>
        <div style={{ display:'flex', alignItems:'center', gap:12 }}>
          <h3 style={{ margin:0, fontSize:16, fontWeight:700, color:'var(--navy)' }}>Sales Tax Rates</h3>
          <label style={{ display:'flex', alignItems:'center', gap:5, fontSize:12, color:'var(--g400)', cursor:'pointer', fontWeight:400 }}>
            <input type="checkbox" checked={showAll} onChange={e => setShowAll(e.target.checked)} />
            Show inactive
          </label>
        </div>
        <div style={{ display:'flex', gap:8 }}>
          <input ref={importRef} type="file" accept=".csv,text/csv" style={{ display:'none' }} onChange={doImport} />
          <button className="btn btn-ghost btn-sm" onClick={() => importRef.current?.click()} disabled={importing}>
            {importing ? '⏳ Importing…' : '⬆ Import CSV'}
          </button>
          {editing !== 'new' && (
            <button className="btn btn-primary btn-sm" onClick={startNew}>+ Add Rate</button>
          )}
        </div>
      </div>

      <div style={{ fontSize:11, color:'var(--g400)', marginBottom:14, padding:'8px 12px', background:'var(--off)', borderRadius:5 }}>
        CSV format: <strong>Jurisdiction,Rate</strong> — e.g. <em>New York City,8.875</em> (one row per line, header row optional).
        Importing an existing jurisdiction updates its rate.
      </div>

      {/* New/Edit form */}
      {editing && (
        <div className="card card-pad" style={{ marginBottom:20, background:'var(--off)' }}>
          <div style={{ fontWeight:700, fontSize:13, color:'var(--navy)', marginBottom:12 }}>
            {editing === 'new' ? 'New Tax Rate' : 'Edit Rate'}
          </div>
          <div className="fg fg-3" style={{ marginBottom:12 }}>
            <div className="field" style={{ gridColumn:'span 2' }}>
              <label>Jurisdiction *</label>
              <input value={form.jurisdiction} onChange={e => set('jurisdiction', e.target.value)}
                placeholder="e.g. New York City, New York State" />
            </div>
            <div className="field">
              <label>Tax Rate %  *</label>
              <input type="number" step="0.001" min="0" max="100" value={form.rate}
                onChange={e => set('rate', e.target.value)} placeholder="e.g. 8.875" />
            </div>
          </div>
          <div style={{ display:'flex', alignItems:'center', justifyContent:'space-between' }}>
            <label style={{ display:'flex', alignItems:'center', gap:6, fontSize:13, cursor:'pointer' }}>
              <input type="checkbox" checked={form.isActive} onChange={e => set('isActive', e.target.checked)} />
              Active
            </label>
            <div style={{ display:'flex', gap:8 }}>
              <button className="btn btn-ghost btn-sm" onClick={cancelEdit} disabled={saving}>Cancel</button>
              <button className="btn btn-primary btn-sm" onClick={doSave} disabled={saving}>
                {saving ? 'Saving…' : '✓ Save'}
              </button>
            </div>
          </div>
        </div>
      )}

      {loading ? (
        <div className="empty"><div className="empty-icon">⏳</div><div className="empty-text">Loading…</div></div>
      ) : visible.length === 0 ? (
        <div className="empty">
          <div className="empty-icon">🧾</div>
          <div className="empty-text">No tax rates yet. Add one above or import a CSV.</div>
        </div>
      ) : (
        <div className="card">
          <table className="tbl">
            <thead>
              <tr>
                <th>Jurisdiction</th>
                <th style={{ textAlign:'right', width:120 }}>Rate</th>
                <th style={{ textAlign:'center', width:70 }}>Active</th>
                <th style={{ width:100 }}></th>
              </tr>
            </thead>
            <tbody>
              {visible.map(r => (
                <tr key={r.id} style={{ opacity: r.isActive ? 1 : 0.5 }}>
                  <td style={{ fontWeight:500, color:'var(--navy)' }}>{r.jurisdiction}</td>
                  <td style={{ textAlign:'right', fontFamily:'monospace' }}>{parseFloat(r.rate).toFixed(3)}%</td>
                  <td style={{ textAlign:'center' }}>{r.isActive ? '✅' : '—'}</td>
                  <td>
                    <div style={{ display:'flex', gap:5, justifyContent:'flex-end' }}>
                      <button className="btn btn-ghost btn-xs" onClick={() => startEdit(r)}>✏ Edit</button>
                      <button className="btn btn-xs" disabled={deleting === r.id}
                        style={{ background:'#FEF2F2', color:'#991B1B', border:'1px solid #FECACA' }}
                        onClick={() => doDelete(r)}>
                        {deleting === r.id ? '…' : '🗑'}
                      </button>
                    </div>
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      )}
    </div>
  );
}

// ── AI AGENTS TAB ──────────────────────────────────────────
const AI_PROVIDERS = [
  { id: 'anthropic', label: 'Claude (Anthropic)', icon: '🟠', defaultModel: 'claude-3-5-haiku-20241022',
    modelOptions: ['claude-3-5-haiku-20241022','claude-3-5-sonnet-20241022','claude-opus-4-5','claude-sonnet-4-5'],
    docsUrl: 'https://console.anthropic.com/', keyPlaceholder: 'sk-ant-api03-…' },
  { id: 'openai',    label: 'OpenAI (ChatGPT)',   icon: '🟢', defaultModel: 'gpt-4o-mini',
    modelOptions: ['gpt-4o-mini','gpt-4o','gpt-4-turbo','gpt-3.5-turbo'],
    docsUrl: 'https://platform.openai.com/api-keys', keyPlaceholder: 'sk-…' },
  { id: 'xai',       label: 'xAI (Grok)',          icon: '⚫', defaultModel: 'grok-2-1212',
    modelOptions: ['grok-2-1212','grok-3-mini','grok-beta'],
    docsUrl: 'https://console.x.ai/', keyPlaceholder: 'xai-…' },
];

function AiAgentsTab({ showToast }) {
  const [config,   setConfig]   = useState({});   // key → { value, hasValue, isSensitive }
  const [loading,  setLoading]  = useState(true);
  const [saving,   setSaving]   = useState(false);
  const [testing,  setTesting]  = useState('');   // provider id being tested
  const [newKeys,  setNewKeys]  = useState({});   // provider id → new key text
  const [showKeyInput, setShowKeyInput] = useState({});  // provider id → bool
  const [models,   setModels]   = useState({});   // provider id → model string
  const [activeProvider, setActiveProvider] = useState('anthropic');

  const load = async () => {
    setLoading(true);
    try {
      const entries = await CRM.SystemConfigAPI.getAll();
      const map = {};
      entries.forEach(e => { map[e.key] = e; });
      setConfig(map);
      setActiveProvider(map['ai.provider']?.value || 'anthropic');
      const m = {};
      AI_PROVIDERS.forEach(p => {
        m[p.id] = map[`ai.${p.id}.model`]?.value || p.defaultModel;
      });
      setModels(m);
    } catch (e) { showToast('Failed to load AI config: ' + e.message, 'error'); }
    finally { setLoading(false); }
  };

  useEffect(() => { load(); }, []);

  const save = async () => {
    setSaving(true);
    try {
      const entries = [
        { key: 'ai.provider', value: activeProvider, isSensitive: false },
        ...AI_PROVIDERS.flatMap(p => {
          const list = [{ key: `ai.${p.id}.model`, value: models[p.id] || p.defaultModel, isSensitive: false }];
          if (newKeys[p.id]) list.push({ key: `ai.${p.id}.key`, value: newKeys[p.id], isSensitive: true });
          return list;
        }),
      ];
      await CRM.SystemConfigAPI.save(entries);
      setNewKeys({});
      setShowKeyInput({});
      await load();
      showToast('AI settings saved!', 'success');
    } catch (e) { showToast('Save failed: ' + e.message, 'error'); }
    finally { setSaving(false); }
  };

  const testProvider = async (pid) => {
    setTesting(pid);
    try {
      // Save any pending new key first
      if (newKeys[pid]) {
        await CRM.SystemConfigAPI.save([{ key: `ai.${pid}.key`, value: newKeys[pid], isSensitive: true }]);
      }
      const result = await CRM.SystemConfigAPI.testAi(pid);
      if (result.ok) showToast(`✅ ${AI_PROVIDERS.find(p=>p.id===pid)?.label} connected! Response: "${result.reply}"`, 'success');
      else showToast(`❌ Connection failed: ${result.error}`, 'error');
    } catch (e) { showToast('Test failed: ' + e.message, 'error'); }
    finally { setTesting(''); }
  };

  if (loading) return <div style={{padding:24,color:'var(--g400)'}}>Loading…</div>;

  return (
    <div style={{maxWidth:720}}>
      {/* Active provider selector */}
      <div className="card card-pad" style={{marginBottom:20}}>
        <div style={{fontWeight:700,fontSize:14,color:'var(--navy)',marginBottom:4}}>Active AI Provider</div>
        <div style={{fontSize:12,color:'var(--g400)',marginBottom:16}}>
          This provider powers the 🤖 CRM Assistant chatbot. Configure each provider below, then select which one to use.
        </div>
        <div style={{display:'flex',gap:10,flexWrap:'wrap'}}>
          {AI_PROVIDERS.map(p => {
            const hasKey = config[`ai.${p.id}.key`]?.hasValue || !!newKeys[p.id];
            return (
              <button key={p.id} onClick={() => setActiveProvider(p.id)}
                style={{
                  padding:'10px 18px', borderRadius:8, cursor:'pointer', fontFamily:'var(--font)',
                  fontWeight:600, fontSize:13, transition:'all .15s',
                  background: activeProvider === p.id ? 'var(--navy)' : 'white',
                  color:      activeProvider === p.id ? 'white' : 'var(--g600)',
                  border:     activeProvider === p.id ? '2px solid var(--navy)' : '2px solid var(--g200)',
                }}>
                {p.icon} {p.label}
                {!hasKey && <span style={{marginLeft:6,fontSize:10,opacity:.6,fontWeight:400}}>(no key)</span>}
                {activeProvider === p.id && <span style={{marginLeft:6,fontSize:10,background:'rgba(255,255,255,.2)',padding:'1px 6px',borderRadius:10}}>Active</span>}
              </button>
            );
          })}
        </div>
      </div>

      {/* Per-provider configuration */}
      {AI_PROVIDERS.map(p => {
        const keyEntry = config[`ai.${p.id}.key`];
        const hasKey   = keyEntry?.hasValue;
        const maskedKey= keyEntry?.value;
        const isEditing= !!showKeyInput[p.id];
        const isTesting= testing === p.id;
        return (
          <div key={p.id} className="card card-pad" style={{marginBottom:16,borderLeft: activeProvider===p.id ? '3px solid var(--navy)' : '3px solid var(--g200)'}}>
            <div style={{display:'flex',alignItems:'center',gap:10,marginBottom:16}}>
              <span style={{fontSize:20}}>{p.icon}</span>
              <div style={{flex:1}}>
                <div style={{fontWeight:700,fontSize:14,color:'var(--navy)'}}>{p.label}</div>
                <a href={p.docsUrl} target="_blank" rel="noreferrer"
                  style={{fontSize:11,color:'var(--g400)',textDecoration:'none'}}>
                  Get API key →
                </a>
              </div>
              {activeProvider === p.id && <span className="badge badge-blue">Active Provider</span>}
            </div>

            <div className="fg fg-2" style={{marginBottom:0}}>
              {/* API Key */}
              <div className="field" style={{marginBottom:0}}>
                <label>API Key {hasKey && <span style={{fontWeight:400,color:'var(--green)',fontSize:11}}>✓ Configured</span>}</label>
                {isEditing ? (
                  <div style={{display:'flex',gap:6}}>
                    <input type="password" placeholder={p.keyPlaceholder}
                      value={newKeys[p.id]||''}
                      onChange={e => setNewKeys(k => ({...k,[p.id]:e.target.value}))}
                      style={{flex:1,border:'1.5px solid var(--navy)',borderRadius:6,padding:'8px 10px',fontSize:13,fontFamily:'var(--font)',outline:'none'}}
                      autoFocus />
                    <button className="btn btn-ghost btn-sm" onClick={() => setShowKeyInput(s => ({...s,[p.id]:false}))}>Cancel</button>
                  </div>
                ) : (
                  <div style={{display:'flex',gap:6,alignItems:'center'}}>
                    <div style={{flex:1,padding:'8px 10px',background:'var(--off)',borderRadius:6,fontSize:13,color:hasKey?'var(--g600)':'var(--g400)',fontFamily:'monospace',overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap',minWidth:0}}>
                      {newKeys[p.id] ? '••••••••••••••••' : hasKey ? ('••••••••••••' + (maskedKey||'').slice(-4)) : 'Not configured'}
                    </div>
                    <button className="btn btn-ghost btn-sm"
                      onClick={() => { setShowKeyInput(s => ({...s,[p.id]:true})); setNewKeys(k => ({...k,[p.id]:''})); }}>
                      {hasKey ? '🔄 Change' : '+ Set Key'}
                    </button>
                  </div>
                )}
              </div>

              {/* Model */}
              <div className="field" style={{marginBottom:0}}>
                <label>Model</label>
                <select value={models[p.id]||p.defaultModel} onChange={e => setModels(m => ({...m,[p.id]:e.target.value}))}>
                  {p.modelOptions.map(m => <option key={m} value={m}>{m}</option>)}
                  <option value="__custom__">Custom…</option>
                </select>
                {models[p.id] === '__custom__' && (
                  <input style={{marginTop:6}} placeholder="Enter model name"
                    value={models[p.id]==='__custom__'?'':models[p.id]}
                    onChange={e => setModels(m => ({...m,[p.id]:e.target.value}))} />
                )}
              </div>
            </div>

            <div style={{display:'flex',gap:8,marginTop:14}}>
              <button className="btn btn-ghost btn-sm" onClick={() => testProvider(p.id)} disabled={isTesting || (!hasKey && !newKeys[p.id])}>
                {isTesting ? '⏳ Testing…' : '🔌 Test Connection'}
              </button>
            </div>
          </div>
        );
      })}

      <div style={{display:'flex',gap:10,marginTop:8}}>
        <button className="btn btn-primary" onClick={save} disabled={saving}>{saving?'Saving…':'Save AI Settings'}</button>
        <button className="btn btn-ghost" onClick={load} disabled={loading}>↺ Reload</button>
      </div>
    </div>
  );
}

// ── AUTHENTICATION TAB ─────────────────────────────────────
function AuthenticationTab({ showToast }) {
  const [config,  setConfig]  = useState({});
  const [loading, setLoading] = useState(true);
  const [saving,  setSaving]  = useState(false);
  const [form,    setForm]    = useState({ enabled: false, tenantId: '', clientId: '', clientSecret: '' });
  const [showSecret, setShowSecret] = useState(false);
  const [newSecret,  setNewSecret]  = useState('');
  const [editSecret, setEditSecret] = useState(false);

  const load = async () => {
    setLoading(true);
    try {
      const entries = await CRM.SystemConfigAPI.getAll();
      const map = {};
      entries.forEach(e => { map[e.key] = e; });
      setConfig(map);
      setForm({
        enabled:      map['oidc.enabled']?.value === 'true',
        tenantId:     map['oidc.tenantid']?.value  || '',
        clientId:     map['oidc.clientid']?.value  || '',
        clientSecret: map['oidc.clientsecret']?.value || '',
      });
    } catch (e) { showToast('Failed to load auth config: ' + e.message, 'error'); }
    finally { setLoading(false); }
  };

  useEffect(() => { load(); }, []);

  const save = async () => {
    setSaving(true);
    try {
      const entries = [
        { key: 'oidc.enabled',  value: form.enabled ? 'true' : 'false', isSensitive: false },
        { key: 'oidc.tenantid', value: form.tenantId,  isSensitive: false },
        { key: 'oidc.clientid', value: form.clientId,  isSensitive: false },
      ];
      if (newSecret) entries.push({ key: 'oidc.clientsecret', value: newSecret, isSensitive: true });
      await CRM.SystemConfigAPI.save(entries);
      setNewSecret('');
      setEditSecret(false);
      await load();
      showToast('Authentication settings saved! Restart the application for changes to take effect.', 'success');
    } catch (e) { showToast('Save failed: ' + e.message, 'error'); }
    finally { setSaving(false); }
  };

  const set = (k, v) => setForm(f => ({...f,[k]:v}));
  const hasSecret = config['oidc.clientsecret']?.hasValue;
  const maskedSecret = config['oidc.clientsecret']?.value;

  if (loading) return <div style={{padding:24,color:'var(--g400)'}}>Loading…</div>;

  return (
    <div style={{maxWidth:640}}>
      {/* Restart warning */}
      <div style={{background:'#FEF3C7',border:'1px solid #FCD34D',borderRadius:8,padding:'12px 16px',marginBottom:20,display:'flex',gap:10,alignItems:'flex-start'}}>
        <span style={{fontSize:18,flexShrink:0}}>⚠️</span>
        <div>
          <div style={{fontWeight:700,fontSize:13,color:'#92400E',marginBottom:2}}>Application restart required</div>
          <div style={{fontSize:12,color:'#B45309',lineHeight:1.5}}>
            OIDC authentication is configured at application startup. Changes saved here take effect after the next app restart. Current session and password logins are not affected.
          </div>
        </div>
      </div>

      {/* Microsoft Entra ID / Azure AD */}
      <div className="card card-pad" style={{marginBottom:16}}>
        <div style={{display:'flex',alignItems:'center',gap:12,marginBottom:16}}>
          <div style={{width:36,height:36,borderRadius:8,background:'#0078D4',display:'flex',alignItems:'center',justifyContent:'center',fontSize:18}}>Ⓜ️</div>
          <div>
            <div style={{fontWeight:700,fontSize:14,color:'var(--navy)'}}>Microsoft Entra ID (Azure AD)</div>
            <div style={{fontSize:12,color:'var(--g400)'}}>Single sign-on via Microsoft 365 / Entra ID accounts</div>
          </div>
          <div style={{marginLeft:'auto'}}>
            <label style={{display:'flex',alignItems:'center',gap:8,cursor:'pointer'}}>
              <div style={{
                width:42,height:24,borderRadius:12,transition:'background .2s',
                background: form.enabled ? 'var(--navy)' : 'var(--g200)',
                position:'relative',cursor:'pointer',
              }} onClick={() => set('enabled', !form.enabled)}>
                <div style={{
                  position:'absolute',top:3,width:18,height:18,borderRadius:'50%',background:'white',
                  transition:'left .2s', left: form.enabled ? 21 : 3,
                }} />
              </div>
              <span style={{fontSize:13,fontWeight:600,color: form.enabled ? 'var(--navy)' : 'var(--g400)'}}>
                {form.enabled ? 'Enabled' : 'Disabled'}
              </span>
            </label>
          </div>
        </div>

        <div className="fg fg-1" style={{marginBottom:0}}>
          <div className="field">
            <label>Tenant ID
              <span style={{fontWeight:400,color:'var(--g400)',fontSize:11,marginLeft:8}}>Your Azure AD directory (tenant) ID</span>
            </label>
            <input placeholder="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
              value={form.tenantId} onChange={e => set('tenantId', e.target.value)} />
          </div>
        </div>

        <div className="fg fg-2">
          <div className="field">
            <label>Client (Application) ID</label>
            <input placeholder="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
              value={form.clientId} onChange={e => set('clientId', e.target.value)} />
          </div>
          <div className="field">
            <label>Client Secret {hasSecret && <span style={{fontWeight:400,color:'var(--green)',fontSize:11}}>✓ Configured</span>}</label>
            {editSecret ? (
              <div style={{display:'flex',gap:6}}>
                <input type="password" placeholder="Paste new client secret value"
                  value={newSecret} onChange={e => setNewSecret(e.target.value)}
                  style={{flex:1}} autoFocus />
                <button className="btn btn-ghost btn-sm" onClick={() => { setEditSecret(false); setNewSecret(''); }}>Cancel</button>
              </div>
            ) : (
              <div style={{display:'flex',gap:6,alignItems:'center'}}>
                <div style={{flex:1,padding:'8px 10px',background:'var(--off)',borderRadius:6,fontSize:13,color:hasSecret?'var(--g600)':'var(--g400)',fontFamily:'monospace'}}>
                  {hasSecret ? maskedSecret : 'Not configured'}
                </div>
                <button className="btn btn-ghost btn-sm" onClick={() => setEditSecret(true)}>
                  {hasSecret ? '🔄 Change' : '+ Set Secret'}
                </button>
              </div>
            )}
          </div>
        </div>

        <div style={{marginTop:16,padding:'12px 14px',background:'var(--off)',borderRadius:6,fontSize:12,color:'var(--g600)',lineHeight:1.6}}>
          <strong>Redirect URI</strong> to register in Azure App Registration:<br />
          <code style={{background:'white',padding:'2px 8px',borderRadius:4,fontSize:12,color:'var(--navy)',border:'1px solid var(--g200)'}}>
            {window.location.origin}/signin-oidc
          </code>
        </div>
      </div>

      {/* Password auth info */}
      <div className="card card-pad" style={{marginBottom:20,opacity:.8}}>
        <div style={{display:'flex',alignItems:'center',gap:10}}>
          <span style={{fontSize:18}}>🔑</span>
          <div>
            <div style={{fontWeight:600,fontSize:13,color:'var(--navy)'}}>Password Authentication</div>
            <div style={{fontSize:12,color:'var(--g400)'}}>Always enabled — users can always sign in with email + password regardless of OIDC settings.</div>
          </div>
          <span className="badge badge-green" style={{marginLeft:'auto'}}>Always On</span>
        </div>
      </div>

      <div style={{display:'flex',gap:10}}>
        <button className="btn btn-primary" onClick={save} disabled={saving}>{saving?'Saving…':'Save Auth Settings'}</button>
        <button className="btn btn-ghost" onClick={load}>↺ Reload</button>
      </div>
    </div>
  );
}

// ── EMAIL SETTINGS TAB ────────────────────────────────────
function EmailSettingsTab({ showToast }) {
  const PROVIDERS = [
    { id: 'smtp365', label: 'Microsoft 365', icon: '🟦', host: 'smtp.office365.com', port: '587', useSsl: false,
      note: 'Use your Microsoft 365 email address and password. If your org requires Modern Auth, generate an App Password in your M365 account settings.' },
    { id: 'gmail',   label: 'Gmail',         icon: '🔴', host: 'smtp.gmail.com',      port: '587', useSsl: false,
      note: 'Use your Gmail address and a Google App Password — not your regular password. Create one at myaccount.google.com → Security → App Passwords.' },
    { id: 'custom',  label: 'Custom SMTP',   icon: '⚙️', host: '',                    port: '587', useSsl: false,
      note: 'Enter your mail server host, port, and credentials manually.' },
  ];

  const [loading,    setLoading]    = useState(true);
  const [saving,     setSaving]     = useState(false);
  const [testing,    setTesting]    = useState(false);
  const [provider,   setProvider]   = useState('smtp365');
  const [form,       setForm]       = useState({ host: 'smtp.office365.com', port: '587', username: '', fromName: '', fromAddress: '', useSsl: false });
  const [editPass,   setEditPass]   = useState(false);
  const [newPass,    setNewPass]    = useState('');
  const [hasPass,    setHasPass]    = useState(false);
  const [testEmail,  setTestEmail]  = useState('');
  const [testResult, setTestResult] = useState(null);

  const load = async () => {
    setLoading(true);
    try {
      const entries = await CRM.SystemConfigAPI.getAll();
      const map = {};
      entries.forEach(e => { map[e.key] = e; });
      const p = map['email.provider']?.value || 'smtp365';
      setProvider(p);
      setHasPass(map['email.password']?.hasValue || false);
      setForm({
        host:        map['email.host']?.value        || (PROVIDERS.find(x => x.id === p)?.host || ''),
        port:        map['email.port']?.value        || '587',
        username:    map['email.username']?.value    || '',
        fromName:    map['email.fromName']?.value    || '',
        fromAddress: map['email.fromAddress']?.value || '',
        useSsl:      map['email.useSsl']?.value      === 'true',
      });
    } catch (e) { showToast('Failed to load email config: ' + e.message, 'error'); }
    finally { setLoading(false); }
  };

  useEffect(() => { load(); }, []);

  const selectProvider = (pid) => {
    const p = PROVIDERS.find(x => x.id === pid);
    setProvider(pid);
    if (pid !== 'custom') {
      setForm(f => ({ ...f, host: p.host, port: p.port, useSsl: p.useSsl }));
    }
  };

  const set = (k, v) => setForm(f => ({ ...f, [k]: v }));

  const save = async () => {
    setSaving(true);
    try {
      const entries = [
        { key: 'email.provider',    value: provider,          isSensitive: false },
        { key: 'email.host',        value: form.host,         isSensitive: false },
        { key: 'email.port',        value: form.port,         isSensitive: false },
        { key: 'email.username',    value: form.username,     isSensitive: false },
        { key: 'email.fromName',    value: form.fromName,     isSensitive: false },
        { key: 'email.fromAddress', value: form.fromAddress || form.username, isSensitive: false },
        { key: 'email.useSsl',      value: form.useSsl ? 'true' : 'false', isSensitive: false },
      ];
      if (newPass) entries.push({ key: 'email.password', value: newPass, isSensitive: true });
      await CRM.SystemConfigAPI.save(entries);
      setNewPass('');
      setEditPass(false);
      await load();
      showToast('Email settings saved!', 'success');
    } catch (e) { showToast('Save failed: ' + e.message, 'error'); }
    finally { setSaving(false); }
  };

  const sendTest = async () => {
    if (!testEmail) return showToast('Enter a recipient email address for the test.', 'error');
    setTesting(true);
    setTestResult(null);
    try {
      await CRM.EmailAPI.test(testEmail);
      setTestResult({ ok: true, msg: `Test email sent to ${testEmail}` });
      showToast('Test email sent!', 'success');
    } catch (e) {
      setTestResult({ ok: false, msg: e.message });
    } finally { setTesting(false); }
  };

  const curProvider = PROVIDERS.find(p => p.id === provider) || PROVIDERS[0];
  const isCustom    = provider === 'custom';

  if (loading) return <div style={{padding:24,color:'var(--g400)'}}>Loading…</div>;

  return (
    <div style={{maxWidth:660}}>

      {/* Provider picker */}
      <div className="card card-pad" style={{marginBottom:16}}>
        <div style={{fontWeight:700,fontSize:13,color:'var(--navy)',marginBottom:12}}>Mail Provider</div>
        <div style={{display:'flex',gap:10,flexWrap:'wrap'}}>
          {PROVIDERS.map(p => (
            <button key={p.id} onClick={() => selectProvider(p.id)}
              style={{display:'flex',alignItems:'center',gap:8,padding:'10px 16px',borderRadius:8,cursor:'pointer',fontFamily:'var(--font)',fontSize:13,fontWeight:600,transition:'all .15s',
                border: provider === p.id ? '2px solid var(--navy)' : '2px solid var(--g200)',
                background: provider === p.id ? 'var(--navy)' : 'white',
                color: provider === p.id ? 'white' : 'var(--g600)'}}>
              <span>{p.icon}</span>{p.label}
            </button>
          ))}
        </div>
        <div style={{marginTop:10,fontSize:12,color:'var(--g500)',lineHeight:1.6,padding:'10px 12px',background:'var(--off)',borderRadius:6}}>
          ℹ️ {curProvider.note}
        </div>
      </div>

      {/* SMTP Settings */}
      <div className="card card-pad" style={{marginBottom:16}}>
        <div style={{fontWeight:700,fontSize:13,color:'var(--navy)',marginBottom:14}}>SMTP Settings</div>

        <div className="fg fg-2" style={{marginBottom:0}}>
          <div className="field">
            <label>SMTP Host</label>
            <input value={form.host} onChange={e => set('host', e.target.value)}
              readOnly={!isCustom}
              style={{background: !isCustom ? 'var(--off)' : undefined, color: !isCustom ? 'var(--g500)' : undefined}}
              placeholder="smtp.example.com" />
          </div>
          <div className="field">
            <label>Port</label>
            <input value={form.port} onChange={e => set('port', e.target.value)}
              readOnly={!isCustom}
              style={{background: !isCustom ? 'var(--off)' : undefined, color: !isCustom ? 'var(--g500)' : undefined}}
              placeholder="587" />
          </div>
        </div>

        {isCustom && (
          <div style={{marginBottom:14}}>
            <label style={{display:'flex',alignItems:'center',gap:8,cursor:'pointer',fontSize:13,color:'var(--g600)'}}>
              <input type="checkbox" checked={form.useSsl} onChange={e => set('useSsl', e.target.checked)} />
              Use SSL (port 465) — leave unchecked for STARTTLS (port 587)
            </label>
          </div>
        )}

        <div className="fg fg-2">
          <div className="field">
            <label>Username / Email</label>
            <input type="email" value={form.username} onChange={e => set('username', e.target.value)} placeholder="you@example.com" />
          </div>
          <div className="field">
            <label>Password / App Password {hasPass && <span style={{fontWeight:400,color:'var(--green)',fontSize:11}}>✓ Saved</span>}</label>
            {editPass ? (
              <div style={{display:'flex',gap:6}}>
                <input type="password" placeholder="Enter password or app password" value={newPass}
                  onChange={e => setNewPass(e.target.value)} style={{flex:1}} autoFocus />
                <button className="btn btn-ghost btn-sm" onClick={() => { setEditPass(false); setNewPass(''); }}>Cancel</button>
              </div>
            ) : (
              <div style={{display:'flex',gap:6,alignItems:'center'}}>
                <div style={{flex:1,padding:'8px 10px',background:'var(--off)',borderRadius:6,fontSize:13,color:'var(--g400)',fontFamily:'monospace'}}>
                  {hasPass ? '••••••••••••' : 'Not set'}
                </div>
                <button className="btn btn-ghost btn-sm" onClick={() => setEditPass(true)}>
                  {hasPass ? '🔄 Change' : '+ Set Password'}
                </button>
              </div>
            )}
          </div>
        </div>

        <div className="fg fg-2" style={{marginTop:4}}>
          <div className="field">
            <label>From Name <span style={{fontWeight:400,color:'var(--g400)',fontSize:11}}>shown to recipients</span></label>
            <input value={form.fromName} onChange={e => set('fromName', e.target.value)} placeholder="Folio CRM" />
          </div>
          <div className="field">
            <label>From Address <span style={{fontWeight:400,color:'var(--g400)',fontSize:11}}>leave blank to use username</span></label>
            <input type="email" value={form.fromAddress} onChange={e => set('fromAddress', e.target.value)} placeholder="same as username" />
          </div>
        </div>
      </div>

      {/* Test email */}
      <div className="card card-pad" style={{marginBottom:20}}>
        <div style={{fontWeight:700,fontSize:13,color:'var(--navy)',marginBottom:12}}>Send Test Email</div>
        <div style={{display:'flex',gap:8,alignItems:'flex-end'}}>
          <div className="field" style={{flex:1,marginBottom:0}}>
            <label>Recipient Address</label>
            <input type="email" value={testEmail} onChange={e => setTestEmail(e.target.value)}
              placeholder="yourname@example.com" />
          </div>
          <button className="btn btn-primary" onClick={sendTest} disabled={testing} style={{flexShrink:0,marginBottom:0}}>
            {testing ? 'Sending…' : '📨 Send Test'}
          </button>
        </div>
        {testResult && (
          <div style={{marginTop:10,padding:'10px 14px',borderRadius:6,fontSize:13,fontWeight:500,
            background: testResult.ok ? '#F0FDF4' : '#FEF2F2',
            color:      testResult.ok ? '#166534' : '#991B1B',
            border:     `1px solid ${testResult.ok ? '#BBF7D0' : '#FECACA'}`}}>
            {testResult.ok ? '✅ ' : '❌ '}{testResult.msg}
          </div>
        )}
      </div>

      <div style={{display:'flex',gap:10}}>
        <button className="btn btn-primary" onClick={save} disabled={saving}>{saving ? 'Saving…' : 'Save Email Settings'}</button>
        <button className="btn btn-ghost" onClick={load}>↺ Reload</button>
      </div>

      {/* Microsoft 365 / Graph mail sync */}
      <GraphSettingsCard showToast={showToast} />
    </div>
  );
}

// ── MICROSOFT 365 MAIL SYNC (GRAPH) CARD (admin) ─────────
function GraphSettingsCard({ showToast }) {
  const [loading,   setLoading]   = useState(true);
  const [saving,    setSaving]    = useState(false);
  const [testing,   setTesting]   = useState(false);
  const [enabled,   setEnabled]   = useState(false);
  const [tenantId,  setTenantId]  = useState('');
  const [clientId,  setClientId]  = useState('');
  const [secret,    setSecret]    = useState('');
  const [hasSecret, setHasSecret] = useState(false);
  const [mailboxes, setMailboxes] = useState([]);
  const [lastSync,  setLastSync]  = useState('');
  const [lastError, setLastError] = useState('');
  const [customMailbox, setCustomMailbox] = useState('');

  const users = CRM.UsersAPI.getAll().filter(u => u.email);
  const userEmails = users.map(u => (u.email || '').toLowerCase());

  const load = async () => {
    setLoading(true);
    try {
      const s = await CRM.GraphAPI.getSettings();
      setEnabled(!!s.enabled);
      setTenantId(s.tenantId || '');
      setClientId(s.clientId || '');
      setHasSecret(!!s.hasSecret);
      setMailboxes(s.syncMailboxes || []);
      setLastSync(s.lastSync || '');
      setLastError(s.lastError || '');
      setSecret('');
    } catch (e) { showToast('Failed to load Microsoft 365 sync settings: ' + e.message, 'error'); }
    finally { setLoading(false); }
  };

  useEffect(() => { load(); }, []);

  const isSynced = (upn) => mailboxes.some(m => m.toLowerCase() === (upn || '').toLowerCase());

  const toggleMailbox = (upn) => {
    setMailboxes(list => list.some(m => m.toLowerCase() === upn.toLowerCase())
      ? list.filter(m => m.toLowerCase() !== upn.toLowerCase())
      : [...list, upn]);
  };

  const removeMailbox = (upn) => {
    setMailboxes(list => list.filter(m => m.toLowerCase() !== upn.toLowerCase()));
  };

  const addCustomMailbox = () => {
    const v = customMailbox.trim();
    if (!v) return;
    if (!v.includes('@')) return showToast('Enter a valid mailbox address.', 'error');
    setMailboxes(list => list.some(m => m.toLowerCase() === v.toLowerCase()) ? list : [...list, v]);
    setCustomMailbox('');
  };

  // Entries in syncMailboxes that don't belong to a CRM user (shared mailboxes)
  const customEntries = mailboxes.filter(m => !userEmails.includes(m.toLowerCase()));

  const save = async () => {
    setSaving(true);
    try {
      const payload = {
        enabled,
        tenantId: tenantId.trim(),
        clientId: clientId.trim(),
        clientSecret: secret.trim() ? secret : '',   // blank = keep existing
        syncMailboxes: mailboxes,
      };
      await CRM.GraphAPI.saveSettings(payload);
      setSecret('');
      await load();
      showToast('Microsoft 365 sync settings saved!', 'success');
    } catch (e) { showToast('Save failed: ' + e.message, 'error'); }
    finally { setSaving(false); }
  };

  const [testResult, setTestResult] = useState(null);
  const testConnection = async () => {
    setTesting(true);
    setTestResult(null);
    try {
      const res = await CRM.GraphAPI.test('');   // server tests every configured mailbox
      setTestResult(res);
      showToast(res.ok ? 'Connection OK — all mailboxes readable.' : 'Test found problems — see details below.', res.ok ? 'success' : 'error');
    } catch (e) { setTestResult({ ok: false, message: e.message, hints: [e.message] }); }
    finally { setTesting(false); }
  };

  if (loading) return (
    <div className="card card-pad" style={{marginTop:20}}>
      <div style={{fontWeight:700,fontSize:13,color:'var(--navy)'}}>Microsoft 365 Mail Sync (Graph)</div>
      <div style={{marginTop:10,fontSize:13,color:'var(--g400)'}}>Loading…</div>
    </div>
  );

  return (
    <div className="card card-pad" style={{marginTop:20}}>
      <div style={{display:'flex',alignItems:'center',justifyContent:'space-between',marginBottom:14}}>
        <div style={{fontWeight:700,fontSize:13,color:'var(--navy)'}}>Microsoft 365 Mail Sync (Graph)</div>
        <label style={{display:'flex',alignItems:'center',gap:8,cursor:'pointer',fontSize:13,fontWeight:600,color:enabled?'var(--navy)':'var(--g400)'}}>
          <input type="checkbox" checked={enabled} onChange={e => setEnabled(e.target.checked)} />
          Enabled
        </label>
      </div>

      <div style={{fontSize:12,color:'var(--g500)',lineHeight:1.6,padding:'10px 12px',background:'var(--off)',borderRadius:6,marginBottom:14}}>
        ℹ️ Syncs sent and received email from Microsoft 365 mailboxes into lead and account timelines automatically — no BCC required.
      </div>

      <div className="fg fg-2">
        <div className="field">
          <label>Tenant ID</label>
          <input value={tenantId} onChange={e => setTenantId(e.target.value)} placeholder="00000000-0000-0000-0000-000000000000" />
        </div>
        <div className="field">
          <label>Client ID</label>
          <input value={clientId} onChange={e => setClientId(e.target.value)} placeholder="00000000-0000-0000-0000-000000000000" />
        </div>
      </div>

      <div className="fg fg-2">
        <div className="field">
          <label>Client Secret {hasSecret && <span style={{fontWeight:400,color:'var(--green)',fontSize:11}}>✓ Saved</span>}</label>
          <input type="password" value={secret} onChange={e => setSecret(e.target.value)}
            placeholder={hasSecret ? '(unchanged)' : '(not set)'} autoComplete="new-password" />
        </div>
        <div />
      </div>

      {/* Mailboxes to sync */}
      <div style={{marginBottom:14}}>
        <div style={{fontWeight:600,fontSize:12,color:'var(--g600)',marginBottom:8}}>Mailboxes to sync</div>
        {users.length === 0 && <div style={{fontSize:12,color:'var(--g400)'}}>No CRM users with an email address.</div>}
        {users.map(u => (
          <label key={u.id || u.email} style={{display:'flex',alignItems:'center',gap:8,padding:'5px 2px',cursor:'pointer',fontSize:13,color:'var(--g600)'}}>
            <input type="checkbox" checked={isSynced(u.email)} onChange={() => toggleMailbox(u.email)} />
            <span><strong style={{color:'var(--navy)',fontWeight:600}}>{u.name}</strong> — {u.email}</span>
          </label>
        ))}
        {customEntries.length > 0 && (
          <div style={{display:'flex',flexWrap:'wrap',gap:6,marginTop:8}}>
            {customEntries.map(m => (
              <span key={m} style={{display:'inline-flex',alignItems:'center',gap:6,padding:'3px 8px',background:'var(--off)',border:'1px solid var(--g200)',borderRadius:12,fontSize:12,color:'var(--g600)'}}>
                📫 {m}
                <button onClick={() => removeMailbox(m)} title="Remove"
                  style={{border:'none',background:'none',cursor:'pointer',color:'var(--g400)',fontSize:13,padding:0,lineHeight:1,fontFamily:'var(--font)'}}>×</button>
              </span>
            ))}
          </div>
        )}
        <div style={{display:'flex',gap:8,alignItems:'center',marginTop:10}}>
          <input value={customMailbox} onChange={e => setCustomMailbox(e.target.value)}
            onKeyDown={e => { if (e.key === 'Enter') addCustomMailbox(); }}
            placeholder="Add another mailbox (e.g. sales@ctrny.com)"
            style={{flex:1,maxWidth:320,border:'1.5px solid var(--g200)',borderRadius:5,padding:'7px 10px',fontSize:12,fontFamily:'var(--font)',color:'var(--navy)',outline:'none'}} />
          <button className="btn btn-ghost btn-sm" onClick={addCustomMailbox}>+ Add</button>
        </div>
      </div>

      {/* Status */}
      <div style={{fontSize:12,color:'var(--g500)',marginBottom:10}}>
        Last sync: <strong style={{color:'var(--g600)'}}>{lastSync ? new Date(lastSync).toLocaleString() : 'never'}</strong>
      </div>
      {lastError && (
        <div style={{marginBottom:12,padding:'10px 14px',borderRadius:6,fontSize:13,fontWeight:500,
          background:'#FEF2F2',color:'#991B1B',border:'1px solid #FECACA'}}>
          ⚠️ Last sync error: {lastError}
        </div>
      )}

      <div style={{display:'flex',gap:10,marginBottom:14}}>
        <button className="btn btn-primary" onClick={save} disabled={saving}>{saving ? 'Saving…' : 'Save'}</button>
        <button className="btn btn-ghost" onClick={testConnection} disabled={testing}
          title="Save first — the test runs against the saved settings">{testing ? 'Testing…' : '🔌 Test Connection'}</button>
      </div>

      {testResult && (
        <div style={{marginBottom:14,padding:'12px 14px',borderRadius:6,fontSize:13,
          background: testResult.ok ? '#ECFDF5' : '#FEF2F2',
          border: '1px solid ' + (testResult.ok ? '#A7F3D0' : '#FECACA'),
          color: testResult.ok ? '#065F46' : '#991B1B'}}>
          <div style={{fontWeight:700,marginBottom:6}}>{testResult.ok ? '✓ Connection test passed' : '✗ Connection test found problems'}</div>
          {testResult.tokenRoles && (
            <div style={{marginBottom:6}}>
              App permissions in token:{' '}
              {testResult.tokenRoles.length > 0
                ? testResult.tokenRoles.join(', ')
                : <strong>NONE — permissions were likely added as Delegated instead of Application, or admin consent wasn't granted</strong>}
            </div>
          )}
          {(testResult.mailboxes || []).map((m, i) => (
            <div key={i} style={{marginBottom:2}}>
              {m.ok ? '✓' : '✗'} <strong>{m.mailbox}</strong>{!m.ok && m.error ? ` — ${m.error}` : ' — readable'}
            </div>
          ))}
          {(testResult.hints || []).length > 0 && (
            <ul style={{margin:'8px 0 0',paddingLeft:18}}>
              {testResult.hints.map((h, i) => <li key={i} style={{marginBottom:3}}>{h}</li>)}
            </ul>
          )}
          {!testResult.tokenRoles && testResult.message && <div>{testResult.message}</div>}
        </div>
      )}

      {/* Setup instructions */}
      <details>
        <summary style={{cursor:'pointer',fontSize:13,fontWeight:600,color:'var(--navy)'}}>Setup instructions</summary>
        <ol style={{margin:'10px 0 0',paddingLeft:20,fontSize:13,color:'var(--g600)',lineHeight:1.7,display:'flex',flexDirection:'column',gap:8}}>
          <li>Entra admin center → App registrations → New registration: name "Folio CRM Mail", single tenant, no redirect URI.</li>
          <li>API permissions → Add → Microsoft Graph → Application permissions → Mail.Read and Mail.Send → Grant admin consent.</li>
          <li>Certificates &amp; secrets → New client secret → paste the VALUE here (it is shown only once).</li>
          <li>Overview page → copy Directory (tenant) ID and Application (client) ID into the fields above.</li>
          <li>
            RECOMMENDED — limit the app to sales mailboxes only. In Exchange Online PowerShell:
            <pre style={{marginTop:6,padding:'10px 12px',background:'var(--off)',borderRadius:6,fontSize:11,lineHeight:1.6,overflowX:'auto',whiteSpace:'pre'}}>{
`New-DistributionGroup -Name "CRM Mail Sync" -Type Security -PrimarySmtpAddress crm-mail-sync@ctrny.com
Add-DistributionGroupMember "CRM Mail Sync" -Member rep@ctrny.com
New-ApplicationAccessPolicy -AppId <Client ID> -PolicyScopeGroupId crm-mail-sync@ctrny.com -AccessRight RestrictAccess -Description "Folio CRM mail sync"`
            }</pre>
          </li>
          <li>Enable the toggle, tick each mailbox to sync, Save, then Test Connection.</li>
        </ol>
      </details>
    </div>
  );
}

// ── MY EMAIL SETTINGS MODAL (per-user) ───────────────────
function MyEmailSettingsModal({ user, onClose, showToast }) {
  const PROVIDERS = [
    { id: 'smtp365', label: 'Microsoft 365', icon: '🟦', host: 'smtp.office365.com', port: '587' },
    { id: 'gmail',   label: 'Gmail',         icon: '🔴', host: 'smtp.gmail.com',      port: '587' },
    { id: 'custom',  label: 'Custom SMTP',   icon: '⚙️', host: '',                    port: '587' },
  ];

  const [loading,    setLoading]    = useState(true);
  const [saving,     setSaving]     = useState(false);
  const [testing,    setTesting]    = useState(false);
  const [isEnabled,  setIsEnabled]  = useState(false);
  const [digestEnabled, setDigestEnabled] = useState(true);
  const [provider,   setProvider]   = useState('smtp365');
  const [form,       setForm]       = useState({ host: 'smtp.office365.com', port: '587', username: '', fromName: '', fromAddress: '', useSsl: false });
  const [signature,  setSignature]  = useState('');
  const [editPass,   setEditPass]   = useState(false);
  const [newPass,    setNewPass]    = useState('');
  const [hasPass,    setHasPass]    = useState(false);
  const [testEmail,  setTestEmail]  = useState('');
  const [testResult, setTestResult] = useState(null);

  const load = async () => {
    setLoading(true);
    try {
      const s = await CRM.EmailAPI.getMySettings();
      setIsEnabled(s.isEnabled);
      setDigestEnabled(s.digestEnabled !== false);
      setHasPass(s.hasPassword);
      // Detect provider from host
      const detectedProvider = s.host?.includes('office365') ? 'smtp365'
        : s.host?.includes('gmail') ? 'gmail' : 'custom';
      setProvider(detectedProvider);
      setForm({
        host:        s.host        || 'smtp.office365.com',
        port:        String(s.port || 587),
        username:    s.username    || '',
        fromName:    s.fromName    || '',
        fromAddress: s.fromAddress || '',
        useSsl:      s.useSsl      || false,
      });
      setSignature(s.signature || '');
    } catch (e) { showToast('Failed to load settings: ' + e.message, 'error'); }
    finally { setLoading(false); }
  };

  useEffect(() => { load(); }, []);

  const selectProvider = (pid) => {
    const p = PROVIDERS.find(x => x.id === pid);
    setProvider(pid);
    if (pid !== 'custom')
      setForm(f => ({ ...f, host: p.host, port: p.port, useSsl: false }));
  };

  const set = (k, v) => setForm(f => ({ ...f, [k]: v }));

  const save = async () => {
    setSaving(true);
    try {
      await CRM.EmailAPI.saveMySettings({
        isEnabled,
        digestEnabled,
        host:        form.host,
        port:        parseInt(form.port) || 587,
        username:    form.username,
        password:    newPass || undefined,
        fromName:    form.fromName,
        fromAddress: form.fromAddress || form.username,
        useSsl:      form.useSsl,
        signature,
      });
      setNewPass('');
      setEditPass(false);
      await load();
      showToast('Your email settings saved!', 'success');
    } catch (e) { showToast('Save failed: ' + e.message, 'error'); }
    finally { setSaving(false); }
  };

  const sendTest = async () => {
    if (!testEmail) return showToast('Enter a recipient address for the test.', 'error');
    setTesting(true);
    setTestResult(null);
    try {
      const r = await CRM.EmailAPI.test(testEmail, false);
      setTestResult({ ok: true, msg: `Test sent to ${testEmail} (via ${r.source === 'user' ? 'your personal settings' : 'system catch-all'})` });
      showToast('Test email sent!', 'success');
    } catch (e) { setTestResult({ ok: false, msg: e.message }); }
    finally { setTesting(false); }
  };

  const isCustom = provider === 'custom';

  return (
    <div className="modal-overlay" {...overlayClose(onClose)}>
      <div className="modal-panel" style={{width:'min(600px,100%)'}} onClick={e => e.stopPropagation()}>
        <div className="modal-hd">
          <div>
            <div className="modal-title">📧 My Email Settings</div>
            <div style={{fontSize:12,color:'var(--g400)',marginTop:2}}>Personal SMTP — emails you send will come from your own account</div>
          </div>
          <button className="modal-close" onClick={onClose}>×</button>
        </div>

        {loading ? <div style={{padding:32,textAlign:'center',color:'var(--g400)'}}>Loading…</div> : (
        <div className="modal-body" style={{maxHeight:'80vh',overflowY:'auto'}}>

          {/* Daily digest opt-in */}
          <label className="card card-pad" style={{marginBottom:16,display:'flex',alignItems:'center',gap:10,cursor:'pointer'}}>
            <input type="checkbox" checked={digestEnabled} onChange={e => setDigestEnabled(e.target.checked)}
              style={{width:16,height:16,accentColor:'var(--navy)',flexShrink:0}} />
            <span style={{fontSize:13,fontWeight:600,color:'var(--navy)'}}>📬 Send me the daily 7:30am task &amp; follow-up digest email</span>
          </label>

          {/* Enable toggle */}
          <div className="card card-pad" style={{marginBottom:16,display:'flex',alignItems:'center',gap:14}}>
            <div style={{flex:1}}>
              <div style={{fontWeight:700,fontSize:13,color:'var(--navy)'}}>Use personal SMTP</div>
              <div style={{fontSize:12,color:'var(--g500)',marginTop:2}}>
                {isEnabled
                  ? 'Emails will send from your personal account below.'
                  : 'Emails will use the system catch-all account. Enable to send from your own address.'}
              </div>
            </div>
            <div style={{
              width:42,height:24,borderRadius:12,transition:'background .2s',flexShrink:0,
              background: isEnabled ? 'var(--navy)' : 'var(--g200)',
              position:'relative',cursor:'pointer',
            }} onClick={() => setIsEnabled(e => !e)}>
              <div style={{
                position:'absolute',top:3,width:18,height:18,borderRadius:'50%',background:'white',
                transition:'left .2s', left: isEnabled ? 21 : 3,
              }} />
            </div>
          </div>

          {isEnabled && (<>
            {/* Provider */}
            <div style={{display:'flex',gap:8,marginBottom:14,flexWrap:'wrap'}}>
              {PROVIDERS.map(p => (
                <button key={p.id} onClick={() => selectProvider(p.id)}
                  style={{display:'flex',alignItems:'center',gap:6,padding:'8px 14px',borderRadius:7,cursor:'pointer',fontFamily:'var(--font)',fontSize:12,fontWeight:600,transition:'all .15s',
                    border: provider === p.id ? '2px solid var(--navy)' : '2px solid var(--g200)',
                    background: provider === p.id ? 'var(--navy)' : 'white',
                    color: provider === p.id ? 'white' : 'var(--g600)'}}>
                  {p.icon} {p.label}
                </button>
              ))}
            </div>

            {provider === 'gmail' && (
              <div style={{marginBottom:12,padding:'10px 12px',background:'#FEF3C7',borderRadius:6,fontSize:12,color:'#92400E',lineHeight:1.5}}>
                ⚠️ Gmail requires a <strong>Google App Password</strong> (not your regular password). Create one at <strong>myaccount.google.com → Security → App Passwords</strong>.
              </div>
            )}
            {provider === 'smtp365' && (
              <div style={{marginBottom:12,padding:'10px 12px',background:'#EFF6FF',borderRadius:6,fontSize:12,color:'#1E40AF',lineHeight:1.5}}>
                ℹ️ Use your Microsoft 365 email and password. If your org requires Modern Auth, generate an <strong>App Password</strong> in your M365 account settings.
              </div>
            )}

            {/* SMTP fields */}
            <div className="fg fg-2" style={{marginBottom:0}}>
              <div className="field">
                <label>SMTP Host</label>
                <input value={form.host} onChange={e => set('host', e.target.value)} readOnly={!isCustom}
                  style={{background: !isCustom ? 'var(--off)' : undefined, color: !isCustom ? 'var(--g500)' : undefined}} />
              </div>
              <div className="field">
                <label>Port</label>
                <input value={form.port} onChange={e => set('port', e.target.value)} readOnly={!isCustom}
                  style={{background: !isCustom ? 'var(--off)' : undefined, color: !isCustom ? 'var(--g500)' : undefined}} />
              </div>
            </div>

            {isCustom && (
              <div style={{marginBottom:10}}>
                <label style={{display:'flex',alignItems:'center',gap:8,cursor:'pointer',fontSize:13,color:'var(--g600)'}}>
                  <input type="checkbox" checked={form.useSsl} onChange={e => set('useSsl', e.target.checked)} />
                  Use SSL (port 465) — leave unchecked for STARTTLS (port 587)
                </label>
              </div>
            )}

            <div className="fg fg-2">
              <div className="field">
                <label>Your Email / Username</label>
                <input type="email" value={form.username} onChange={e => set('username', e.target.value)} placeholder="you@company.com" />
              </div>
              <div className="field">
                <label>Password / App Password {hasPass && <span style={{fontWeight:400,color:'var(--green)',fontSize:11}}>✓ Saved</span>}</label>
                {editPass ? (
                  <div style={{display:'flex',gap:6}}>
                    <input type="password" placeholder="Enter password" value={newPass}
                      onChange={e => setNewPass(e.target.value)} style={{flex:1}} autoFocus />
                    <button className="btn btn-ghost btn-sm" onClick={() => { setEditPass(false); setNewPass(''); }}>Cancel</button>
                  </div>
                ) : (
                  <div style={{display:'flex',gap:6,alignItems:'center'}}>
                    <div style={{flex:1,padding:'8px 10px',background:'var(--off)',borderRadius:6,fontSize:13,color:'var(--g400)',fontFamily:'monospace'}}>
                      {hasPass ? '••••••••••••' : 'Not set'}
                    </div>
                    <button className="btn btn-ghost btn-sm" onClick={() => setEditPass(true)}>{hasPass ? '🔄 Change' : '+ Set'}</button>
                  </div>
                )}
              </div>
            </div>

            <div className="fg fg-2" style={{marginTop:4}}>
              <div className="field">
                <label>Display Name <span style={{fontWeight:400,color:'var(--g400)',fontSize:11}}>shown to recipients</span></label>
                <input value={form.fromName} onChange={e => set('fromName', e.target.value)} placeholder={user?.name || ''} />
              </div>
              <div className="field">
                <label>From Address <span style={{fontWeight:400,color:'var(--g400)',fontSize:11}}>defaults to username</span></label>
                <input type="email" value={form.fromAddress} onChange={e => set('fromAddress', e.target.value)} placeholder="same as username" />
              </div>
            </div>
          </>)}

            {/* Signature — applies to every send path (personal SMTP,
                company account, or Microsoft 365), so always editable */}
            <div style={{marginTop:14,paddingTop:14,borderTop:'1px solid var(--g100)'}}>
              <div style={{fontWeight:600,fontSize:13,color:'var(--navy)',marginBottom:6}}>Email Signature</div>
              <div style={{fontSize:12,color:'var(--g500)',marginBottom:8,lineHeight:1.5}}>
                Automatically appended to every email you send from the CRM. Plain text or HTML — paste your Outlook signature directly (right-click your signature in Outlook → Inspect / Copy as HTML, or just copy the visible text).
                Put <code style={{background:'var(--off)',padding:'1px 5px',borderRadius:4,border:'1px solid var(--g200)'}}>{'{logo}'}</code> anywhere in it to embed the company logo (it ships inside the email, so it displays for recipients without loading anything external).
              </div>
              {!signature.includes('{logo}') && (
                <button className="btn btn-ghost btn-sm" style={{marginBottom:8}}
                  onClick={() => setSignature(s => (s ? s.replace(/\s*$/, '\n') : '') + '{logo}')}>
                  🖼 Insert company logo
                </button>
              )}
              <textarea value={signature} onChange={e => setSignature(e.target.value)} rows={8}
                placeholder={`Best regards,\nYour Name\nYour Title · Your Company\n555-555-1234 · you@example.com`}
                style={{width:'100%',resize:'vertical',padding:10,border:'1px solid var(--g200)',borderRadius:6,fontSize:13,fontFamily:'inherit',lineHeight:1.5}} />
              {signature && (
                <div style={{marginTop:8,padding:10,border:'1px dashed var(--g200)',borderRadius:6,background:'var(--off)'}}>
                  <div style={{fontSize:10,fontWeight:700,letterSpacing:'.1em',textTransform:'uppercase',color:'var(--g400)',marginBottom:6}}>Preview</div>
                  {/* If signature looks like HTML render it; otherwise preserve newlines.
                      {logo} previews as the actual company logo (sent as an inline image). */}
                  {(() => {
                    const logoImg = CRM.companyLogo
                      ? `<img src="${CRM.companyLogo}" alt="logo" style="max-height:60px;border:0;display:block" />`
                      : '<em style="color:var(--g400)">[company logo — none uploaded yet]</em>';
                    const withLogo = signature.split('{logo}').join(logoImg);
                    return /<[a-z][^>]*>/i.test(withLogo)
                      ? <div style={{fontSize:13,color:'var(--g700)'}} dangerouslySetInnerHTML={{ __html: withLogo }} />
                      : <div style={{fontSize:13,color:'var(--g700)',whiteSpace:'pre-wrap',fontFamily:'sans-serif'}}>{signature}</div>;
                  })()}
                </div>
              )}
            </div>

            {/* Test */}
            <div style={{marginTop:14,paddingTop:14,borderTop:'1px solid var(--g100)'}}>
              <div style={{fontWeight:600,fontSize:13,color:'var(--navy)',marginBottom:10}}>Send Test Email</div>
              <div style={{display:'flex',gap:8,alignItems:'flex-end'}}>
                <div className="field" style={{flex:1,marginBottom:0}}>
                  <label>Recipient</label>
                  <input type="email" value={testEmail} onChange={e => setTestEmail(e.target.value)} placeholder="you@example.com" />
                </div>
                <button className="btn btn-primary" onClick={sendTest} disabled={testing} style={{flexShrink:0}}>
                  {testing ? 'Sending…' : '📨 Test'}
                </button>
              </div>
              {testResult && (
                <div style={{marginTop:8,padding:'8px 12px',borderRadius:6,fontSize:13,
                  background: testResult.ok ? '#F0FDF4' : '#FEF2F2',
                  color:      testResult.ok ? '#166534' : '#991B1B',
                  border:     `1px solid ${testResult.ok ? '#BBF7D0' : '#FECACA'}`}}>
                  {testResult.ok ? '✅ ' : '❌ '}{testResult.msg}
                </div>
              )}
            </div>

          <div style={{display:'flex',gap:8,marginTop:16,paddingTop:14,borderTop:'1px solid var(--g100)'}}>
            <button className="btn btn-primary" onClick={save} disabled={saving}>{saving ? 'Saving…' : 'Save Settings'}</button>
            <button className="btn btn-ghost" onClick={onClose}>Cancel</button>
          </div>
        </div>
        )}
      </div>
    </div>
  );
}

// ── SEND EMAIL MODAL (shared) ─────────────────────────────
// Rasterize on-screen document pages (proposal / order form previews)
// into a Letter-size PDF File for email attachment. Each element matched
// by pageSelector becomes one PDF page. Pages are laid out at 816×1056px
// (96dpi Letter), which maps exactly onto 612×792pt.
async function captureDocumentPdf(pageSelector, fileName) {
  const pages = Array.from(document.querySelectorAll(pageSelector));
  if (pages.length === 0) throw new Error('Nothing to capture.');
  const { jsPDF } = window.jspdf;
  const pdf = new jsPDF({ unit:'pt', format:'letter', orientation:'portrait' });
  for (let i = 0; i < pages.length; i++) {
    const canvas = await html2canvas(pages[i], { scale: 2, useCORS: true, backgroundColor: '#ffffff', logging: false });
    const img = canvas.toDataURL('image/jpeg', 0.85);
    if (i > 0) pdf.addPage('letter', 'portrait');
    // Preserve aspect ratio; fit width, letting shorter pages leave whitespace
    const h = Math.min(792, 612 * (canvas.height / canvas.width));
    pdf.addImage(img, 'JPEG', 0, 0, 612, h);
  }
  return new File([pdf.output('blob')], fileName, { type: 'application/pdf' });
}

// initialFiles: File objects pre-attached (e.g. a captured proposal PDF).
// zIndex: raise the overlay above full-screen document previews (z 9000).
function SendEmailModal({ toEmail, toName, leadId, accountId, onClose, showToast, initialFiles, initialSubject, zIndex }) {
  const [form,         setForm]         = useState({ to: toEmail || '', cc: '', bcc: '', subject: initialSubject || '', body: '' });
  const [sending,      setSending]      = useState(false);
  const [localFiles,   setLocalFiles]   = useState(initialFiles || []);   // File objects from disk
  const [crmSelected,  setCrmSelected]  = useState([]);   // {id, fileName, fileSize} from CRM
  const [crmFiles,     setCrmFiles]     = useState([]);   // available CRM files
  const [showCrmPick,  setShowCrmPick]  = useState(false);
  const [libSelected,  setLibSelected]  = useState([]);   // {id, name, fileName, fileSize} from library
  const [libDocs,      setLibDocs]      = useState([]);   // all library docs (loaded once)
  const [libLoaded,    setLibLoaded]    = useState(false);
  const [showLibPick,  setShowLibPick]  = useState(false);
  const [libTab,       setLibTab]       = useState('literature');
  const fileInputRef = useRef(null);

  // Which identity this email goes out under (personal vs company SMTP)
  const [sendAs, setSendAs] = useState(null);
  useEffect(() => { CRM.EmailAPI.sendAs().then(setSendAs).catch(() => {}); }, []);

  const set = (k, v) => setForm(f => ({ ...f, [k]: v }));

  // Load CRM files when the picker is opened for the first time
  const openCrmPicker = async () => {
    if (!showCrmPick && crmFiles.length === 0) {
      try {
        const files = leadId
          ? await CRM.FilesAPI.getByLead(leadId)
          : await CRM.FilesAPI.getByAccount(accountId);
        setCrmFiles(files || []);
      } catch { setCrmFiles([]); }
    }
    setShowCrmPick(v => !v);
  };

  const toggleCrmFile = (f) => {
    setCrmSelected(sel =>
      sel.find(s => s.id === f.id)
        ? sel.filter(s => s.id !== f.id)
        : [...sel, { id: f.id, fileName: f.fileName, fileSize: f.fileSize }]
    );
  };

  const openLibPicker = async () => {
    if (!libLoaded) {
      try {
        const docs = await CRM.LibraryAPI.getAll();
        setLibDocs(docs || []);
        setLibLoaded(true);
      } catch { setLibDocs([]); setLibLoaded(true); }
    }
    setShowLibPick(v => !v);
    if (showCrmPick) setShowCrmPick(false);
  };

  const toggleLibDoc = (d) => {
    setLibSelected(sel =>
      sel.find(s => s.id === d.id)
        ? sel.filter(s => s.id !== d.id)
        : [...sel, { id: d.id, name: d.name, fileName: d.fileName, fileSize: d.fileSize }]
    );
  };

  const addLocalFiles = (e) => {
    const added = Array.from(e.target.files);
    setLocalFiles(prev => {
      const existing = new Set(prev.map(f => f.name + f.size));
      return [...prev, ...added.filter(f => !existing.has(f.name + f.size))];
    });
    e.target.value = '';
  };

  const removeLocal  = (idx)  => setLocalFiles(prev => prev.filter((_, i) => i !== idx));
  const removeCrm    = (id)   => setCrmSelected(prev => prev.filter(f => f.id !== id));

  const fmtSz = (bytes) => {
    if (!bytes) return '';
    if (bytes < 1024)       return bytes + ' B';
    if (bytes < 1048576)    return (bytes / 1024).toFixed(1) + ' KB';
    return (bytes / 1048576).toFixed(1) + ' MB';
  };

  const hasCrmSource = !!(leadId || accountId);
  const totalAttachments = localFiles.length + crmSelected.length + libSelected.length;

  const send = async () => {
    if (!form.to)      return showToast('Please enter a recipient address.', 'error');
    if (!form.subject) return showToast('Please enter a subject.', 'error');
    if (!form.body)    return showToast('Please enter a message body.', 'error');
    setSending(true);
    try {
      await CRM.EmailAPI.send(
        form.to, form.subject, form.body,
        localFiles,
        crmSelected.map(f => f.id),
        leadId || null,
        accountId || null,
        libSelected.map(d => d.id),
        form.cc.trim(),
        form.bcc.trim()
      );
      showToast(`Email sent${totalAttachments > 0 ? ` with ${totalAttachments} attachment${totalAttachments > 1 ? 's' : ''}` : ''}!`, 'success');
      onClose();
    } catch (e) { showToast(e.message, 'error'); }
    finally { setSending(false); }
  };

  return (
    <div className="modal-overlay" style={zIndex ? { zIndex } : undefined} {...overlayClose(onClose)}>
      <div className="modal-panel" style={{width:'min(620px,100%)'}} onClick={e => e.stopPropagation()}>
        <div className="modal-hd">
          <div>
            <div className="modal-title">✉ Send Email</div>
            {toName && <div style={{fontSize:12,color:'var(--g400)',marginTop:2}}>To: {toName}</div>}
            {sendAs && sendAs.configured && (
              <div style={{fontSize:11,color:'var(--g400)',marginTop:2}}>
                Sending as <strong style={{color:'var(--g600)'}}>{sendAs.fromName} &lt;{sendAs.fromAddress}&gt;</strong>
                {sendAs.source === 'user'
                  ? <span className="badge badge-green" style={{marginLeft:6,fontSize:10}}>your email</span>
                  : sendAs.source === 'graph'
                  ? <span className="badge badge-blue" style={{marginLeft:6,fontSize:10}}>your mailbox (M365)</span>
                  : <span className="badge badge-gray" style={{marginLeft:6,fontSize:10}} title="Configure your own in the sidebar → My Email Settings">company account</span>}
              </div>
            )}
          </div>
          <button className="modal-close" onClick={onClose}>×</button>
        </div>

        <div className="modal-body">
          <div className="field">
            <label>To <span style={{fontWeight:400,color:'var(--g400)',fontSize:11}}>(comma-separated for multiple)</span></label>
            <input value={form.to} onChange={e => set('to', e.target.value)} placeholder="recipient@example.com, another@example.com" />
          </div>
          <div className="fg fg-2" style={{marginBottom:0}}>
            <div className="field">
              <label>CC <span style={{fontWeight:400,color:'var(--g400)',fontSize:11}}>(optional)</span></label>
              <input value={form.cc} onChange={e => set('cc', e.target.value)} placeholder="cc@example.com" />
            </div>
            <div className="field">
              <label>BCC <span style={{fontWeight:400,color:'var(--g400)',fontSize:11}}>(optional)</span></label>
              <input value={form.bcc} onChange={e => set('bcc', e.target.value)} placeholder="bcc@example.com" />
            </div>
          </div>
          <div className="field">
            <label>Subject</label>
            <input value={form.subject} onChange={e => set('subject', e.target.value)} placeholder="Subject" />
          </div>
          <div className="field">
            <label>Message</label>
            <textarea rows={8} value={form.body} onChange={e => set('body', e.target.value)}
              placeholder="Type your message here…"
              style={{resize:'vertical',fontFamily:'var(--font)',fontSize:13,lineHeight:1.6}} />
          </div>

          {/* ── Attachments ── */}
          <div style={{borderTop:'1px solid var(--g100)',paddingTop:12,marginTop:4}}>
            <div style={{display:'flex',alignItems:'center',gap:8,marginBottom:10}}>
              <span style={{fontSize:13,fontWeight:600,color:'var(--navy)'}}>
                📎 Attachments {totalAttachments > 0 && <span style={{color:'var(--g400)',fontWeight:400}}>({totalAttachments})</span>}
              </span>
              <input ref={fileInputRef} type="file" multiple style={{display:'none'}} onChange={addLocalFiles} />
              <button className="btn btn-ghost btn-sm" onClick={() => fileInputRef.current?.click()}>
                📁 From computer
              </button>
              {hasCrmSource && (
                <button className="btn btn-ghost btn-sm"
                  onClick={() => { openCrmPicker(); if (showLibPick) setShowLibPick(false); }}
                  style={showCrmPick ? {background:'var(--navy)',color:'white',border:'1px solid var(--navy)'} : {}}>
                  🗂 From CRM {showCrmPick ? '▴' : '▾'}
                </button>
              )}
              <button className="btn btn-ghost btn-sm"
                onClick={openLibPicker}
                style={showLibPick ? {background:'var(--navy)',color:'white',border:'1px solid var(--navy)'} : {}}>
                📚 Library {showLibPick ? '▴' : '▾'}
              </button>
            </div>

            {/* Library picker panel */}
            {showLibPick && (
              <div style={{background:'var(--off)',border:'1px solid var(--g200)',borderRadius:8,marginBottom:10}}>
                {/* Library tabs */}
                <div style={{display:'flex',borderBottom:'1px solid var(--g200)',padding:'0 8px'}}>
                  {[{id:'literature',label:'📄 Product Literature'},{id:'forms',label:'📋 Blank Forms'}].map(t => (
                    <button key={t.id} onClick={() => setLibTab(t.id)}
                      style={{padding:'8px 12px',fontSize:12,fontWeight:600,border:'none',background:'none',cursor:'pointer',fontFamily:'var(--font)',
                        color: libTab === t.id ? 'var(--navy)' : 'var(--g400)',
                        borderBottom: libTab === t.id ? '2px solid var(--red)' : '2px solid transparent',
                        marginBottom:-1}}>
                      {t.label} <span style={{color:'var(--g400)',fontWeight:400}}>({libDocs.filter(d=>d.category===t.id).length})</span>
                    </button>
                  ))}
                </div>
                <div style={{maxHeight:180,overflowY:'auto'}}>
                  {libDocs.filter(d => d.category === libTab).length === 0 ? (
                    <div style={{padding:'16px',fontSize:13,color:'var(--g400)',textAlign:'center'}}>
                      No {libTab === 'literature' ? 'product literature' : 'blank forms'} in library yet.
                    </div>
                  ) : libDocs.filter(d => d.category === libTab).map(d => {
                    const isSelected = !!libSelected.find(s => s.id === d.id);
                    return (
                      <div key={d.id} onClick={() => toggleLibDoc(d)}
                        style={{display:'flex',alignItems:'center',gap:10,padding:'9px 14px',cursor:'pointer',
                          borderBottom:'1px solid var(--g100)',
                          background: isSelected ? '#FDF4FF' : 'transparent'}}
                        onMouseEnter={e => { if (!isSelected) e.currentTarget.style.background = 'white'; }}
                        onMouseLeave={e => { e.currentTarget.style.background = isSelected ? '#FDF4FF' : 'transparent'; }}>
                        <input type="checkbox" readOnly checked={isSelected} style={{pointerEvents:'none',flexShrink:0}} />
                        <div style={{flex:1,minWidth:0}}>
                          <div style={{fontSize:13,color:'var(--navy)',overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap'}}>{d.name}</div>
                          {d.description && <div style={{fontSize:11,color:'var(--g400)',overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap'}}>{d.description}</div>}
                        </div>
                        <span style={{fontSize:11,color:'var(--g400)',flexShrink:0}}>{d.fileSize ? (d.fileSize/1024).toFixed(0)+' KB' : ''}</span>
                      </div>
                    );
                  })}
                </div>
              </div>
            )}

            {/* CRM file picker panel */}
            {showCrmPick && (
              <div style={{background:'var(--off)',border:'1px solid var(--g200)',borderRadius:8,marginBottom:10,maxHeight:180,overflowY:'auto'}}>
                {crmFiles.length === 0 ? (
                  <div style={{padding:'16px',fontSize:13,color:'var(--g400)',textAlign:'center'}}>
                    No files attached to this {leadId ? 'lead' : 'account'} yet.
                  </div>
                ) : crmFiles.map(f => {
                  const isSelected = !!crmSelected.find(s => s.id === f.id);
                  return (
                    <div key={f.id} onClick={() => toggleCrmFile(f)}
                      style={{display:'flex',alignItems:'center',gap:10,padding:'9px 14px',cursor:'pointer',
                        borderBottom:'1px solid var(--g100)',
                        background: isSelected ? '#EFF6FF' : 'transparent'}}
                      onMouseEnter={e => { if (!isSelected) e.currentTarget.style.background = 'white'; }}
                      onMouseLeave={e => { e.currentTarget.style.background = isSelected ? '#EFF6FF' : 'transparent'; }}>
                      <input type="checkbox" readOnly checked={isSelected} style={{pointerEvents:'none',flexShrink:0}} />
                      <span style={{flex:1,fontSize:13,color:'var(--navy)',overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap'}}>{f.fileName}</span>
                      <span style={{fontSize:11,color:'var(--g400)',flexShrink:0}}>{fmtSz(f.fileSize)}</span>
                    </div>
                  );
                })}
              </div>
            )}

            {/* Selected attachment chips */}
            {totalAttachments > 0 && (
              <div style={{display:'flex',flexWrap:'wrap',gap:6}}>
                {localFiles.map((f, i) => (
                  <div key={i} style={{display:'flex',alignItems:'center',gap:5,background:'#EFF6FF',border:'1px solid #BFDBFE',borderRadius:5,padding:'3px 8px 3px 10px',fontSize:12,color:'#1E40AF',maxWidth:220}}>
                    <span style={{overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap'}}>{f.name}</span>
                    <span style={{color:'#93C5FD',fontSize:10,flexShrink:0}}>{fmtSz(f.size)}</span>
                    <button onClick={() => removeLocal(i)}
                      style={{background:'none',border:'none',cursor:'pointer',color:'#93C5FD',fontSize:14,padding:'0 0 0 2px',lineHeight:1,flexShrink:0}}
                      title="Remove">×</button>
                  </div>
                ))}
                {crmSelected.map(f => (
                  <div key={f.id} style={{display:'flex',alignItems:'center',gap:5,background:'#F0FDF4',border:'1px solid #BBF7D0',borderRadius:5,padding:'3px 8px 3px 10px',fontSize:12,color:'#166534',maxWidth:220}}>
                    <span style={{overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap'}}>{f.fileName}</span>
                    <span style={{color:'#86EFAC',fontSize:10,flexShrink:0}}>{fmtSz(f.fileSize)}</span>
                    <button onClick={() => removeCrm(f.id)}
                      style={{background:'none',border:'none',cursor:'pointer',color:'#86EFAC',fontSize:14,padding:'0 0 0 2px',lineHeight:1,flexShrink:0}}
                      title="Remove">×</button>
                  </div>
                ))}
                {libSelected.map(d => (
                  <div key={d.id} style={{display:'flex',alignItems:'center',gap:5,background:'#FDF4FF',border:'1px solid #E9D5FF',borderRadius:5,padding:'3px 8px 3px 10px',fontSize:12,color:'#6B21A8',maxWidth:220}}>
                    <span style={{overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap'}}>📚 {d.name}</span>
                    <span style={{color:'#C4B5FD',fontSize:10,flexShrink:0}}>{d.fileSize ? (d.fileSize/1024).toFixed(0)+' KB' : ''}</span>
                    <button onClick={() => setLibSelected(prev => prev.filter(s => s.id !== d.id))}
                      style={{background:'none',border:'none',cursor:'pointer',color:'#C4B5FD',fontSize:14,padding:'0 0 0 2px',lineHeight:1,flexShrink:0}}
                      title="Remove">×</button>
                  </div>
                ))}
              </div>
            )}
          </div>

          <div style={{display:'flex',gap:8,marginTop:14}}>
            <button className="btn btn-primary" onClick={send} disabled={sending}>
              {sending ? 'Sending…' : `✉ Send${totalAttachments > 0 ? ` (+${totalAttachments})` : ''}`}
            </button>
            <button className="btn btn-ghost" onClick={onClose}>Cancel</button>
          </div>
        </div>
      </div>
    </div>
  );
}

// ── AOD IMPORT TAB ────────────────────────────────────────
function AodImportTab({ showToast }) {
  const [importing, setImporting] = useState(false);
  const [result,    setResult]    = useState(null);
  const fileRef = React.useRef();

  const doImport = async (e) => {
    const file = e.target.files[0];
    if (!file) return;
    e.target.value = '';
    setImporting(true);
    setResult(null);
    try {
      const r = await CRM.AodAPI.importFile(file);
      setResult(r);
      const msg = `Import complete: ${r.inserted} new, ${r.updated} updated${r.unmatched ? `, ${r.unmatched} unmatched URIs` : ''}.`;
      showToast(msg, r.errors?.length ? 'error' : 'success');
    } catch (e) {
      showToast('Import failed: ' + e.message, 'error');
    } finally { setImporting(false); }
  };

  return (
    <div className="card card-pad">
      <div style={{fontWeight:700,fontSize:14,color:'var(--navy)',marginBottom:4}}>AoD Billing Import</div>
      <div style={{fontSize:13,color:'var(--g600)',marginBottom:20,lineHeight:1.6}}>
        Import monthly billing data from the AoD invoicing spreadsheet (Excel or tab-delimited).
        Rows are matched to accounts by their <strong>URI</strong> field. Re-importing the same period updates existing records.
      </div>

      <div style={{background:'var(--off)',borderRadius:8,padding:'14px 16px',marginBottom:20,fontSize:12,color:'var(--g600)'}}>
        <div style={{fontWeight:700,marginBottom:6,color:'var(--navy)'}}>Required columns:</div>
        <div style={{fontFamily:'monospace',lineHeight:2}}>
          URI · Invoice Date · Period From · Period To · Descr · Count · Value · Total
        </div>
        <div style={{marginTop:8}}>
          Accounts must have their <strong>AoD URI</strong> set (editable in each account's Overview tab) for billing rows to link correctly.
          Unmatched URIs are still imported and can be linked later by setting the URI on the account.
        </div>
      </div>

      <label className="btn btn-primary" style={{cursor:'pointer',display:'inline-block'}}>
        {importing ? '⏳ Importing…' : '📂 Choose File & Import'}
        <input ref={fileRef} type="file" accept=".xlsx,.xls,.xlsm,.tsv,.txt,.csv,.tab"
          onChange={doImport} style={{display:'none'}} disabled={importing} />
      </label>

      {result && (
        <div style={{marginTop:20}}>
          <div style={{display:'grid',gridTemplateColumns:'repeat(4,1fr)',gap:12,marginBottom:16}}>
            {[
              ['New Records',   result.inserted,  '#10B981'],
              ['Updated',       result.updated,   '#3B82F6'],
              ['Unmatched URI', result.unmatched, result.unmatched > 0 ? '#F59E0B' : '#9AA3B0'],
              ['Skipped',       result.skipped,   '#9AA3B0'],
            ].map(([label, val, color]) => (
              <div key={label} style={{background:'var(--off)',borderRadius:6,padding:'12px 14px',textAlign:'center'}}>
                <div style={{fontSize:24,fontWeight:800,color}}>{val}</div>
                <div style={{fontSize:11,color:'var(--g400)',marginTop:2}}>{label}</div>
              </div>
            ))}
          </div>
          {result.unmatched > 0 && (
            <div style={{background:'#FEF3C7',borderRadius:6,padding:'10px 14px',fontSize:12,color:'#92400E',marginBottom:12}}>
              ⚠️ {result.unmatched} row{result.unmatched!==1?'s':''} had URIs that don't match any account.
              Open the account and set its AoD URI in the Overview tab, then re-import.
            </div>
          )}
          {result.errors?.length > 0 && (
            <div style={{background:'#FEE2E2',borderRadius:6,padding:'10px 14px',fontSize:12,color:'#991B1B'}}>
              <div style={{fontWeight:700,marginBottom:4}}>Row errors:</div>
              {result.errors.map((e,i) => <div key={i}>{e}</div>)}
            </div>
          )}
        </div>
      )}
    </div>
  );
}

// ── EXPORT ──
// ── EXPORT ACCOUNTS PAGE ──
// ── AI Analyze: one-shot summary modal (accounts / tickets / dashboards) ──
function AnalyzeModal({ title, payload, onClose }) {
  const [state, setState] = useState({ loading: true });
  const run = () => {
    setState({ loading: true });
    CRM.AnalyzeAPI.run(payload)
      .then(r => setState({ summary: r.summary, model: r.model }))
      .catch(e => setState({ error: e.message }));
  };
  useEffect(run, []);
  const copy = () => navigator.clipboard?.writeText(state.summary || '');

  return (
    <div className="modal-overlay" {...overlayClose(onClose)}>
      <div className="modal-panel" style={{width:'min(640px,100%)'}} onClick={e => e.stopPropagation()}>
        <div className="modal-hd">
          <div className="modal-title">✨ {title}</div>
          <button className="modal-close" onClick={onClose}>×</button>
        </div>
        <div className="modal-body">
          {state.loading && (
            <div style={{padding:'32px 0',textAlign:'center',color:'var(--g400)',fontSize:13}}>
              <div style={{fontSize:22,marginBottom:10}}>✨</div>
              Analyzing…
            </div>
          )}
          {state.error && (
            <div style={{background:'#FEF2F2',color:'#991B1B',border:'1px solid #FECACA',borderRadius:8,padding:'10px 14px',fontSize:13}}>
              {state.error}
            </div>
          )}
          {state.summary && (
            <div style={{fontSize:13,color:'var(--g600)',lineHeight:1.7,whiteSpace:'pre-wrap',background:'var(--off)',borderRadius:8,padding:'14px 16px'}}>
              {state.summary}
            </div>
          )}
          {state.model && (
            <div style={{fontSize:11,color:'var(--g400)',marginTop:8}}>
              Generated by {state.model} from live CRM data — verify anything critical.
            </div>
          )}
        </div>
        <div className="modal-footer">
          <button className="btn btn-ghost" onClick={run} disabled={state.loading}>↻ Re-run</button>
          {state.summary && <button className="btn btn-ghost" onClick={copy}>⧉ Copy</button>}
          <button className="btn btn-primary" onClick={onClose}>Close</button>
        </div>
      </div>
    </div>
  );
}

// ── Import / Export hub — mirrors the Reports shell (tabs + library nav) ──
function CsvExportPane({ source, label, desc, showToast }) {
  const [working, setWorking] = useState(false);
  const doExport = async () => {
    setWorking(true);
    try {
      const sources = await CRM.ReportsAPI.getReportSources();
      const src = sources.find(s => s.key === source);
      if (!src) throw new Error('This data source is unavailable.');
      const result = await CRM.ReportsAPI.runReport({ source, columns: src.columns.map(c => c.key), limit: 5000 });
      const lines = [result.columns.map(c => c.label).join(','), ...result.rows.map(r =>
        r.map(v => `"${String(v ?? '').replace(/"/g,'""')}"`).join(','))];
      const blob = new Blob([lines.join('\r\n')], { type:'text/csv' });
      const a = document.createElement('a');
      a.href = URL.createObjectURL(blob);
      a.download = source + '.csv'; a.click(); URL.revokeObjectURL(a.href);
      showToast(`Exported ${result.rows.length.toLocaleString()} row(s).`, 'success');
    } catch (e) { showToast('Export failed: ' + e.message, 'error'); }
    finally { setWorking(false); }
  };
  return (
    <div className="card card-pad" style={{maxWidth:580}}>
      <div style={{fontSize:15,fontWeight:700,color:'var(--navy)',marginBottom:6}}>Export {label}</div>
      <div style={{fontSize:13,color:'var(--g600)',marginBottom:18}}>{desc}</div>
      <button className="btn btn-primary" onClick={doExport} disabled={working}>
        {working ? 'Exporting…' : '⬇ Download CSV'}
      </button>
      <div style={{fontSize:12,color:'var(--g400)',marginTop:12}}>
        Exports up to 5,000 rows with every available column. Need filters, a date range, or specific columns? Use the Report Builder and export from there.
      </div>
    </div>
  );
}

function DataIO({ user, showToast }) {
  const crmUser = user.isAdmin || user.role !== 'tech';
  const aodOn = user.isAdmin && window.CRM?.connectors?.aod !== false;

  const IMPORTS = [
    crmUser && { key:'leads',    label:'Leads',     group:'CRM' },
    crmUser && { key:'accounts', label:'Accounts',  group:'CRM' },
    crmUser && { key:'sales',    label:'Sales',     group:'CRM' },
    { key:'tickets', label:'Tickets', group:'Helpdesk' },
    aodOn && { key:'aod', label:'AoD Usage', group:'Connectors' },
  ].filter(Boolean);

  const EXPORTS = [
    crmUser && { key:'accounts-full', label:'Accounts (Excel / CSV)', group:'CRM' },
    crmUser && { key:'leads',      label:'Leads',      group:'CRM', desc:'Every lead you can see — contact info, stage, value, source, rep, and dates.' },
    crmUser && { key:'contacts',   label:'Contacts',   group:'CRM', desc:'All contacts with their company, title, email, and phone.' },
    crmUser && { key:'sales',      label:'Sales',      group:'CRM', desc:'Logged account sales with amount, date, description, and who recorded them.' },
    crmUser && { key:'activities', label:'Activities', group:'CRM', desc:'The activity log — notes, calls, emails, and meetings across leads and accounts.' },
    { key:'tickets', label:'Tickets', group:'Helpdesk', desc:'All tickets including status, priority, account, technician, and custom fields.' },
  ].filter(Boolean);

  const [tab, setTab] = useState('imports');
  const [activeImport, setActiveImport] = useState(IMPORTS[0]?.key);
  const [activeExport, setActiveExport] = useState(EXPORTS[0]?.key);

  const items  = tab === 'imports' ? IMPORTS : EXPORTS;
  const active = tab === 'imports' ? activeImport : activeExport;
  const setActive = tab === 'imports' ? setActiveImport : setActiveExport;
  const groups = ['CRM','Helpdesk','Connectors']
    .map(g => ({ title:g, items: items.filter(i => i.group === g) }))
    .filter(g => g.items.length > 0);
  const sel = items.find(i => i.key === active) || items[0];

  return (
    <div>
      <div style={{display:'flex',gap:4,marginBottom:20,borderBottom:'1px solid var(--g200)'}}>
        {[{id:'imports',label:'📥 Imports'},{id:'exports',label:'📤 Exports'}].map(t => (
          <button key={t.id} onClick={() => setTab(t.id)}
            style={{border:'none',background:'none',cursor:'pointer',fontFamily:'var(--font)',fontSize:14,fontWeight:700,padding:'10px 18px',
                    color: tab===t.id ? 'var(--navy)' : 'var(--g400)',
                    borderBottom: tab===t.id ? '3px solid var(--red)' : '3px solid transparent', marginBottom:-1}}>
            {t.label}
          </button>
        ))}
      </div>
      <div style={{display:'grid',gridTemplateColumns:'230px 1fr',gap:20,alignItems:'start'}}>
        <div className="card" style={{padding:'12px 0'}}>
          {groups.map(g => (
            <div key={g.title}>
              <div style={{fontSize:11,fontWeight:700,letterSpacing:'.06em',textTransform:'uppercase',color:'var(--g400)',padding:'8px 16px 4px'}}>{g.title}</div>
              {g.items.map(i => (
                <div key={i.key} onClick={() => setActive(i.key)}
                  style={{display:'flex',alignItems:'center',padding:'7px 16px',cursor:'pointer',fontSize:13,fontWeight:600,
                          color: sel?.key === i.key ? 'var(--red)' : 'var(--navy)',
                          background: sel?.key === i.key ? 'var(--off)' : 'transparent',
                          borderLeft: sel?.key === i.key ? '3px solid var(--red)' : '3px solid transparent'}}>
                  <span style={{flex:1,minWidth:0,overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap'}}>{i.label}</span>
                </div>
              ))}
            </div>
          ))}
        </div>
        <div>
          <div style={{fontSize:16,fontWeight:800,color:'var(--navy)',marginBottom:10}}>
            {tab === 'imports' ? 'Import' : 'Export'} {sel?.label}
          </div>
          {tab === 'imports' && sel && sel.key !== 'aod' && (
            <div style={{maxWidth:580}}>
              <ImportModal key={sel.key} type={sel.key} inline={true} onClose={() => {}}
                onDone={() => showToast('Import complete!', 'success')} />
            </div>
          )}
          {tab === 'imports' && sel?.key === 'aod' && <AodImportTab showToast={showToast} />}
          {tab === 'exports' && sel?.key === 'accounts-full' && <ExportAccountsPage user={user} showToast={showToast} />}
          {tab === 'exports' && sel && sel.key !== 'accounts-full' && (
            <CsvExportPane key={sel.key} source={sel.key} label={sel.label} desc={sel.desc} showToast={showToast} />
          )}
        </div>
      </div>
    </div>
  );
}

function ExportAccountsPage({ user, showToast }) {
  const [format, setFormat]         = useState('xlsx');
  const [showClosed, setShowClosed] = useState(false);
  const [working, setWorking]       = useState(false);

  const doExport = async () => {
    setWorking(true);
    try {
      const qs = new URLSearchParams({ format, showClosed: String(showClosed) }).toString();
      const res = await fetch('/api/accounts/export?' + qs, { credentials: 'include' });
      if (!res.ok) throw new Error(`Export failed (${res.status})`);
      const blob = await res.blob();
      const cd   = res.headers.get('Content-Disposition') || '';
      const m    = /filename\*?=(?:UTF-8'')?\"?([^\";]+)\"?/i.exec(cd);
      const name = (m && decodeURIComponent(m[1])) || `accounts.${format}`;
      const url  = URL.createObjectURL(blob);
      const a    = document.createElement('a');
      a.href = url; a.download = name; document.body.appendChild(a); a.click();
      a.remove(); URL.revokeObjectURL(url);
      showToast('Export downloaded.', 'success');
    } catch (e) { showToast('Export failed: ' + e.message, 'error'); }
    finally { setWorking(false); }
  };

  return (
    <div style={{ maxWidth:580 }}>
      <div className="card card-pad">
        <div style={{ fontSize:15, fontWeight:700, color:'var(--navy)', marginBottom:6 }}>Export Accounts</div>
        <div style={{ fontSize:13, color:'var(--g600)', marginBottom:18 }}>
          Download every account you can see — including assigned rep, primary contact, contract values, and AoD URI — as Excel or CSV.
        </div>

        <div style={{ marginBottom:14 }}>
          <div style={{ fontSize:11, fontWeight:700, letterSpacing:'.1em', textTransform:'uppercase', color:'var(--g500)', marginBottom:8 }}>Format</div>
          <div style={{ display:'flex', gap:8 }}>
            {[
              { id:'xlsx', label:'📊 Excel (.xlsx)' },
              { id:'csv',  label:'📄 CSV (.csv)'   },
            ].map(f => (
              <button key={f.id} onClick={() => setFormat(f.id)}
                style={{ padding:'8px 14px', borderRadius:6, fontSize:13, fontWeight:600, cursor:'pointer',
                  border: format === f.id ? '2px solid var(--navy)' : '2px solid var(--g200)',
                  background: format === f.id ? 'var(--navy)' : '#fff',
                  color:      format === f.id ? '#fff' : 'var(--g600)' }}>
                {f.label}
              </button>
            ))}
          </div>
        </div>

        <label style={{ display:'flex', alignItems:'center', gap:8, fontSize:13, color:'var(--g600)', marginBottom:18, cursor:'pointer' }}>
          <input type="checkbox" checked={showClosed} onChange={e => setShowClosed(e.target.checked)} />
          Include closed accounts
        </label>

        <button className="btn btn-primary" onClick={doExport} disabled={working}>
          {working ? 'Exporting…' : '⬇ Download Export'}
        </button>
      </div>
    </div>
  );
}
window.ExportAccountsPage = ExportAccountsPage;

Object.assign(window, { LoginScreen, Sidebar, Topbar, Dashboard, StageBadge, AdminView, BottomNav, DropdownGroupEditor, HardwareContentModal, FieldOptionsTab, ApiKeysTab, EventLogTab, CompanySettingsTab, AiAgentsTab, AuthenticationTab, EmailSettingsTab, SendEmailModal, MyEmailSettingsModal, AodImportTab, ExportAccountsPage, DataIO, AnalyzeModal, captureDocumentPdf, ConnectorsTab, HelpdeskTab, SearchableSelect });
