/* =========================================================
   Tappt — Personas (one card, many profiles) + Connections
   (tap memory: who you met, where, when)
   ========================================================= */
const {
  platById: xplat, BrandIcon: XBrand, AVATAR_COLORS: XAV, I: XI,
  PERSONA_PRESETS: XPRE, getPersonas: xGetPersonas, activePersona: xActive,
  syncActivePersona: xSync,
  personaIconKey: xPersonaIcon,
} = window;
const { useState: xUse } = React;

/* ===========================================================
   PERSONA SWITCHER  (chip on profile -> sheet)
   =========================================================== */
function PersonaSheet({ user, onClose, onSwitch, onNew, onEditPersona, onPro, toast }) {
  // mirror the active persona's live top-level edits (links/socials/contact) back into the
  // personas array first, so each card's "N links · …" summary reflects the real content
  // — not a stale snapshot from before the latest edit.
  const synced = xSync ? xSync(user) : user;
  const personas = xGetPersonas(synced);
  const activeId = xActive(synced).id;
  const [presetOpen, setPresetOpen] = xUse(false);

  const summarize = (p) => {
    const n = (p.links || []).filter((l) => l.active !== false).length;
    const s = (p.socials || []).length;
    const parts = [`${n} link${n === 1 ? '' : 's'}`, `${s} social${s === 1 ? '' : 's'}`];
    if (p.saveContact) parts.push('contact ✓');
    return parts.join(' · ');
  };
  const usedLabels = personas.map((p) => p.label.toLowerCase());

  return (
    <div className="sheet-bg" onClick={(e) => { if (e.target === e.currentTarget) onClose(); }}>
      <div className="sheet persona-sheet">
        <div className="sheet-grab" />
        <div className="sheet-head">
          <span style={{ width: 36 }} />
          <h3 style={{ fontSize: 16 }}>Switch persona</h3>
          <button className="icon-btn" onClick={onClose} style={{ width: 36, height: 36 }}>✕</button>
        </div>
        <div className="sheet-scroll">
          <p style={{ color: 'var(--fg-2)', fontSize: 13.5, margin: '0 2px 16px', lineHeight: 1.5 }}>
            Each persona is a saved version of your profile — its own bio, links and socials. Whatever's <b style={{ color: 'var(--fg)' }}>LIVE</b> is what people see when they open your link or tap a card.
          </p>

          <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
            {personas.map((p) => {
              const on = p.id === activeId;
              return (
                <div key={p.id} className={'persona-card' + (on ? ' on' : '')} onClick={() => { if (!on) { onSwitch(p.id); toast('Now live: ' + p.label); } }}>
                  <span className="persona-emoji" style={{ background: 'linear-gradient(135deg,var(--brand),var(--brand-2))', color: '#fff' }}>{XI[xPersonaIcon(p)]({ width: 22, height: 22 })}</span>
                  <div className="persona-meta">
                    <div className="persona-name">{p.label}{on && <span className="live-dot">● LIVE</span>}</div>
                    <div className="persona-sum">{summarize(p)}</div>
                  </div>
                  <button className="persona-edit" onClick={(e) => { e.stopPropagation(); onEditPersona(p.id); }} aria-label="Edit persona">{XI.edit({ width: 16, height: 16 })}</button>
                </div>
              );
            })}
          </div>

          {/* add new persona — 1 free, unlimited Pro */}
          {(!user.pro && personas.length >= 1) ? (
            <button className="persona-pro-nudge" style={{ marginTop: 14 }} onClick={() => { onClose(); onPro && onPro(); }}>
              <span className="pro-badge">PRO</span>
              <span>Add unlimited personas — a different profile for every side of you — with Tappt Pro</span>
              {XI.arrow({ width: 16, height: 16 })}
            </button>
          ) : !presetOpen ? (
            <button className="btn btn-ghost btn-block" style={{ marginTop: 14 }} onClick={() => setPresetOpen(true)}>{XI.plus({ width: 16, height: 16 })} New persona</button>
          ) : (
            <div className="es-block" style={{ marginTop: 14 }}>
              <div className="es-hint" style={{ marginBottom: 10, marginTop: 0 }}>Start a new persona — copies your current links, then edit freely.</div>
              <div className="persona-preset-row">
                {XPRE.filter((pr) => !usedLabels.includes(pr.label.toLowerCase())).map((pr) => (
                  <button key={pr.label} className="persona-preset" onClick={() => { onNew(pr); setPresetOpen(false); toast(pr.label + ' persona added'); }}>
                    <span className="pp-emoji">{XI[pr.icon]({ width: 18, height: 18 })}</span><span className="pp-label">{pr.label}</span>
                  </button>
                ))}
                <button className="persona-preset" onClick={() => { onNew({ icon: 'sparkles', label: 'Custom' }); setPresetOpen(false); }}>
                  <span className="pp-emoji">{XI.sparkles({ width: 18, height: 18 })}</span><span className="pp-label">Custom</span>
                </button>
              </div>
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

/* ===========================================================
   CONNECTIONS  (tap memory: map + people you met)
   =========================================================== */
const SEED_CONNECTIONS = [
  { id: 1, name: 'Sofia Marchetti', handle: 'sofiaa', icon: 'moon', accent: 'linear-gradient(135deg,#f58529,#dd2a7b)', place: 'Pacha · Ibiza', when: 'Tonight · 2:14am', persona: 'Personal', via: 'card', email: 'sofia.m@gmail.com', phone: '+34 612 044 891', tags: [] },
  { id: 2, name: 'Daniel Osei', handle: 'd.osei', icon: 'briefcase', accent: 'linear-gradient(135deg,#5CC8FF,#1f6a8a)', place: 'Web Summit · Lisbon', when: 'Yesterday · 4:30pm', persona: 'Work', via: 'card', email: 'daniel@osei.io', website: 'osei.io', tags: [] },
  { id: 3, name: 'Mia Chen', handle: 'miacreates', icon: 'palette', accent: 'linear-gradient(135deg,#2BB8C4,#176b6b)', place: 'Shoreditch · London', when: '2 days ago', persona: 'Creator', via: 'bio', phone: '+44 7700 900145', tags: [] },
  { id: 4, name: 'Jayden Brooks', handle: 'jaydn', icon: 'moon', accent: 'linear-gradient(135deg,#8AFF6B,#1a7a3a)', place: 'XOYO · London', when: 'Last week', persona: 'Personal', via: 'card', email: 'jaydn.b@icloud.com', tags: [] },
  { id: 5, name: 'Amara Okafor', handle: 'amara.o', icon: 'briefcase', accent: 'linear-gradient(135deg,#FF6BC8,#a82e7a)', place: 'The Ned · London', when: 'Last week', persona: 'Work', via: 'bio', email: 'amara@okafor.co', phone: '+44 7911 223344', website: 'okafor.co', tags: [] },
];

const SEED_LEADS = [
  { id: 'sl1', name: 'James Whitfield', email: 'james@apexventures.co', phone: '+44 7712 884419', company: 'Apex Ventures', jobTitle: 'Partner', linkedin: 'linkedin.com/in/jameswhitfield', answer: 'Looking to back NFC-first creators.', photo: null, ts: Date.now() - 3600000 * 5, source: 'Exchange · @maxxharland', stage: 'meeting', potential: '5000', notes: 'Met at the rooftop mixer — wants a follow-up deck.', nextAction: { text: 'Send pitch deck', done: false }, metAt: 'Shoreditch House · London', location: { place: 'Shoreditch House · London', lat: 51.5265, lng: -0.0789 } },
  { id: 'sl2', name: 'Priya Kapoor', email: 'priya.kapoor@gmail.com', phone: '+44 7884 220114', company: 'Bloom Studio', jobTitle: 'Creative Director', linkedin: '', answer: 'Keen to collab on a drop.', photo: null, ts: Date.now() - 3600000 * 28, source: 'Exchange · @maxxharland', stage: 'contacted', potential: '1200', notes: '', nextAction: null, metAt: 'Web Summit · Lisbon', location: { place: 'Web Summit · Lisbon', lat: 38.7223, lng: -9.1393 } },
  { id: 'sl3', name: 'Marcus Lee', email: 'marcus@leefitness.io', phone: '', company: 'Lee Fitness', jobTitle: 'Founder', linkedin: 'linkedin.com/in/marcuslee', answer: '', photo: null, ts: Date.now() - 86400000 * 2, source: 'Exchange · @maxxharland', stage: 'new', potential: '', notes: '', nextAction: null, metAt: 'PureGym · Manchester', location: null },
  { id: 'sl4', name: 'Sofia Marchetti', email: 'sofia@marchetti.design', phone: '+39 333 8841200', company: 'Marchetti Design', jobTitle: 'Brand Lead', linkedin: '', answer: 'Wants a Tappt card for the whole studio team.', photo: null, ts: Date.now() - 86400000 * 6, source: 'Exchange · @maxxharland', stage: 'client', potential: '3600', notes: 'Closed — ordered 12 cards.', nextAction: { text: 'Ship team cards', done: true }, metAt: 'Milan Design Week', location: { place: 'Milan Design Week', lat: 45.4642, lng: 9.19 } },
];

const CITY_LL = {
  'London': [-0.1276, 51.5074], 'Lisbon': [-9.1393, 38.7223], 'Ibiza': [1.4329, 38.9067],
  'Paris': [2.3522, 48.8566], 'Berlin': [13.4050, 52.5200], 'New York': [-74.0060, 40.7128],
  'Los Angeles': [-118.2437, 34.0522], 'Dubai': [55.2708, 25.2048], 'Tokyo': [139.6503, 35.6762],
  'Sydney': [151.2093, -33.8688], 'Shoreditch': [-0.0778, 51.5265], 'Default': [-2.0, 47.0],
};
function cityLL(place) {
  if (!place) return CITY_LL.Default;
  for (const city in CITY_LL) { if (city !== 'Default' && place.indexOf(city) !== -1) return CITY_LL[city]; }
  return CITY_LL.Default;
}
/* City coordinates on the stylised world map (% of map box) — fallback */
const CITY_COORDS = {
  'London': { x: 47, y: 33 }, 'Lisbon': { x: 43, y: 45 }, 'Ibiza': { x: 48, y: 43 },
  'Paris': { x: 48, y: 35 }, 'Berlin': { x: 52, y: 31 }, 'New York': { x: 26, y: 39 },
  'Los Angeles': { x: 15, y: 43 }, 'Dubai': { x: 63, y: 47 }, 'Tokyo': { x: 85, y: 40 },
  'Sydney': { x: 90, y: 75 }, 'Default': { x: 50, y: 41 },
};
function cityXY(place) {
  if (!place) return CITY_COORDS.Default;
  for (const city in CITY_COORDS) { if (city !== 'Default' && place.indexOf(city) !== -1) return CITY_COORDS[city]; }
  return CITY_COORDS.Default;
}
const MAP_PINS = [
  { x: 46, y: 38, big: true }, { x: 52, y: 35 }, { x: 49, y: 42 },
  { x: 30, y: 55 }, { x: 70, y: 30 }, { x: 58, y: 60 },
];

/* rich popup card for a tapped connection dot */
function connPopupHTML(c) {
  const esc = (s) => String(s == null ? '' : s).replace(/[&<>"]/g, (m) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' }[m]));
  const initial = (c.name || '?').trim().charAt(0).toUpperCase();
  const av = c.photo
    ? `<span class="cp-av" style="background-image:url('${esc(c.photo)}')"></span>`
    : `<span class="cp-av" style="background:${esc(c.accent || 'var(--accent)')}">${esc(initial)}</span>`;
  const chCount = (c.email ? 1 : 0) + (c.phone ? 1 : 0) + (c.website ? 1 : 0);
  const back = chCount
    ? '<span class="cp-back ok">' + chCount + (chCount === 1 ? ' channel' : ' channels') + '</span>'
    : '<span class="cp-back">Name only</span>';
  const got = (c.got || []).length
    ? `<div class="cp-shared">${(c.got || []).map((g) => `<span class="cp-chip">${esc(g)}</span>`).join('')}</div>` : '';
  const meta = [c.role, c.company].filter(Boolean).map(esc).join(' · ');
  const rows = [];
  if (c.place) rows.push(`<div class="cp-row"><span>📍</span>${esc(c.place)}</div>`);
  if (c.when) rows.push(`<div class="cp-row"><span>🕑</span>${esc(c.when)}</div>`);
  const tags = (c.tags || []).length
    ? `<div class="cp-tags">${(c.tags || []).map((t) => `<span class="cp-tag">${esc(t)}</span>`).join('')}</div>` : '';
  return `<div class="cp">
    <div class="cp-head">${av}<div class="cp-id"><div class="cp-name">${esc(c.name || 'Connection')}</div>
      <div class="cp-sub">${c.handle ? '@' + esc(c.handle) : (c.persona ? esc(c.persona) : '')}${meta ? ' · ' + meta : ''}</div></div></div>
    ${rows.length ? `<div class="cp-rows">${rows.join('')}</div>` : ''}
    ${got}
    ${tags}
    <div class="cp-foot">${back}<span class="cp-persona">${esc(c.persona || '')}</span></div>
  </div>`;
}

function ActionMap({ conns }) {
  const ref = React.useRef(null);
  const mapRef = React.useRef(null);
  const expRef = React.useRef(null);
  const expMap = React.useRef(null);
  const markersRef = React.useRef([]);   // [{ marker, el, c, ll }]
  const [expanded, setExpanded] = React.useState(false);
  const [filter, setFilter] = React.useState('all');   // all · back · week · <tag>
  const [filterOpen, setFilterOpen] = React.useState(false);
  const [search, setSearch] = React.useState('');
  const [searchOpen, setSearchOpen] = React.useState(false);

  const allTags = Array.from(new Set(conns.flatMap((c) => c.tags || [])));

  const buildMap = (container, opts) => {
    const dark = document.documentElement.dataset.theme !== 'light';
    const pts = conns.map((c) => ({ ll: cityLL(c.place), c }));
    const map = new window.maplibregl.Map({
      container,
      style: 'https://basemaps.cartocdn.com/gl/' + (dark ? 'dark-matter-gl-style' : 'positron-gl-style') + '/style.json',
      center: [2.0, 46.0], zoom: opts && opts.zoom || 2.6, attributionControl: false,
      interactive: true, dragRotate: false, pitchWithRotate: false,
      pixelRatio: Math.min(window.devicePixelRatio || 1, 2),
    });
    const markers = [];
    map.on('load', () => {
      pts.forEach(({ ll, c }) => {
        const el = document.createElement('div');
        el.className = 'am-marker';
        el.innerHTML = '<span class="amk-dot"></span><span class="amk-ring"></span>';
        const popup = new window.maplibregl.Popup({ offset: 18, closeButton: true, maxWidth: '250px', className: 'conn-pop' }).setHTML(connPopupHTML(c));
        const marker = new window.maplibregl.Marker({ element: el }).setLngLat(ll).setPopup(popup).addTo(map);
        el.addEventListener('click', () => { map.flyTo({ center: ll, zoom: 9, duration: 900, essential: true }); });
        markers.push({ marker, el, c, ll });
      });
      if (opts && opts.keepMarkers) markersRef.current = markers;
      if (pts.length) {
        const b = new window.maplibregl.LngLatBounds();
        pts.forEach((p) => b.extend(p.ll));
        map.fitBounds(b, { padding: opts && opts.padding || 48, maxZoom: 6, duration: 0 });
      }
    });
    return map;
  };

  React.useEffect(() => {
    if (!window.maplibregl || !ref.current) return;
    const map = buildMap(ref.current);
    mapRef.current = map;
    return () => map.remove();
  }, [conns.length]);

  // separate map instance for the expanded overlay
  React.useEffect(() => {
    if (!expanded || !window.maplibregl || !expRef.current) return;
    const map = buildMap(expRef.current, { padding: 60, keepMarkers: true });
    expMap.current = map;
    const t = setTimeout(() => map.resize(), 60);
    return () => { clearTimeout(t); map.remove(); expMap.current = null; markersRef.current = []; };
  }, [expanded, conns.length]);

  // apply filter + search to expanded markers
  const matches = (c) => {
    if (filter === 'back' && !(c.email || c.phone || c.website)) return false;
    if (filter === 'week' && !/today|tonight|yesterday|hour|min|day|just now/i.test(c.when || '')) return false;
    if (filter !== 'all' && filter !== 'back' && filter !== 'week' && !(c.tags || []).includes(filter)) return false;
    if (search) { const h = (c.name + ' ' + (c.place || '') + ' ' + (c.handle || '')).toLowerCase(); if (!h.includes(search.toLowerCase())) return false; }
    return true;
  };
  React.useEffect(() => {
    if (!expanded) return;
    const visible = [];
    markersRef.current.forEach(({ el, c, ll }) => {
      const ok = matches(c);
      el.style.display = ok ? '' : 'none';
      if (ok) visible.push(ll);
    });
    if (visible.length && expMap.current) {
      if (visible.length === 1) expMap.current.flyTo({ center: visible[0], zoom: 8, duration: 700 });
      else { const b = new window.maplibregl.LngLatBounds(); visible.forEach((l) => b.extend(l)); expMap.current.fitBounds(b, { padding: 80, maxZoom: 7, duration: 700 }); }
    }
  }, [filter, search, expanded]);

  const recenter = () => {
    if (!expMap.current) return;
    if (navigator.geolocation) {
      navigator.geolocation.getCurrentPosition(
        (pos) => expMap.current.flyTo({ center: [pos.coords.longitude, pos.coords.latitude], zoom: 11, duration: 1000 }),
        () => expMap.current.flyTo({ center: [-0.1276, 51.5074], zoom: 10, duration: 900 }),  // fallback London
        { timeout: 4000 }
      );
    } else { expMap.current.flyTo({ center: [-0.1276, 51.5074], zoom: 10, duration: 900 }); }
  };

  return (
    <div className="conn-map real-map">
      <div ref={ref} className="am-canvas" />
      <div className="am-head"><span className="am-live"><span className="am-live-dot" />LIVE</span></div>
      <button className="am-expand" onClick={(e) => { e.stopPropagation(); setExpanded(true); }} aria-label="Expand map">{(window.MI && MI.expand ? MI.expand({ width: 16, height: 16 }) : '⤢')}</button>

      {expanded && ReactDOM.createPortal((
        <div className="map-expand-scrim" onClick={() => setExpanded(false)}>
          <div className="map-expand" onClick={(e) => e.stopPropagation()}>
            <div ref={expRef} className="map-expand-canvas" />

            {/* top bar: live + search + close */}
            <div className="mx-top">
              <span className="am-live mx-live"><span className="am-live-dot" />LIVE</span>
              <div className={'mx-search' + (searchOpen ? ' open' : '')}>
                {searchOpen && <input autoFocus className="mx-search-input" placeholder="Search people or places…" value={search} onChange={(e) => setSearch(e.target.value)} />}
                <button className="mx-icon-btn" onClick={() => { if (searchOpen && search) setSearch(''); setSearchOpen((v) => !v); }} aria-label="Search">{searchOpen && !search ? (window.MI && MI.close ? MI.close({ width: 16, height: 16 }) : '✕') : (window.MI && MI.search ? MI.search({ width: 17, height: 17 }) : '🔍')}</button>
              </div>
              <button className="map-expand-x" onClick={() => setExpanded(false)} aria-label="Close">{(window.MI && MI.close ? MI.close({ width: 18, height: 18 }) : '✕')}</button>
            </div>

            {/* filter button + dropdown */}
            <div className="mx-filter-wrap">
              <button className={'mx-filter-btn' + (filter !== 'all' ? ' active' : '')} onClick={() => setFilterOpen((v) => !v)}>
                {(window.MI && MI.filter ? MI.filter({ width: 15, height: 15 }) : '⚲')}
                {filter === 'all' ? 'Filter' : (filter === 'back' ? 'Has contact' : filter === 'week' ? 'This week' : filter)}
                {filter !== 'all' && <span className="mx-filter-clear" onClick={(e) => { e.stopPropagation(); setFilter('all'); setFilterOpen(false); }}>{(window.MI && MI.close ? MI.close({ width: 12, height: 12 }) : '✕')}</span>}
              </button>
              {filterOpen && (
                <React.Fragment>
                  <div className="mx-filter-veil" onClick={() => setFilterOpen(false)} />
                  <div className="mx-filter-menu">
                    {[['all', 'All'], ['back', 'Has contact'], ['week', 'This week']].map(([id, lb]) => (
                      <button key={id} className={'mx-fm-item' + (filter === id ? ' on' : '')} onClick={() => { setFilter(id); setFilterOpen(false); }}>{lb}{filter === id && (window.MI && MI.check ? MI.check({ width: 14, height: 14 }) : '✓')}</button>
                    ))}
                    {allTags.length > 0 && <div className="mx-fm-sep">Tags</div>}
                    {allTags.map((t) => (
                      <button key={t} className={'mx-fm-item' + (filter === t ? ' on' : '')} onClick={() => { setFilter(t); setFilterOpen(false); }}>{t}{filter === t && (window.MI && MI.check ? MI.check({ width: 14, height: 14 }) : '✓')}</button>
                    ))}
                  </div>
                </React.Fragment>
              )}
            </div>

            {/* recenter on me */}
            <button className="mx-locate" onClick={recenter} aria-label="Recenter on my location">{(window.MI && MI.locate ? MI.locate({ width: 20, height: 20 }) : '◎')}</button>

            <div className="map-expand-cap">{conns.filter(matches).length} of {conns.length} taps · pan &amp; zoom</div>
          </div>
        </div>
      ), document.querySelector('.appframe') || document.body)}
    </div>
  );
}

function ConnectionsScreen({ user, toast, setUser, onPro }) {
  // Demo connections show ONLY for the demo account (@maxxharland). Real accounts start empty.
  const isDemo = user.handle === 'maxxharland';
  const conns = user.connections && user.connections.length ? user.connections : (isDemo ? SEED_CONNECTIONS : []);
  const total = conns.length;
  const back = conns.filter((c) => c.email || c.phone || c.website).length;
  const cities = new Set(conns.map((c) => (c.place || '').split('·').pop().trim())).size;
  const [q, setQ] = React.useState('');
  const [tag, setTag] = React.useState(null);
  const [viaTab, setViaTab] = React.useState('all');   // all | card | bio
  const [detail, setDetail] = React.useState(null);    // connection detail drawer
  const cardCount = conns.filter((c) => c.via === 'card').length;
  const bioCount = conns.filter((c) => c.via === 'bio').length;
  const [sheet, setSheet] = React.useState(null); // 'add' | 'tags'
  React.useEffect(() => {
    const frame = document.querySelector('.appframe');
    if (frame) frame.classList.toggle('nav-hidden', sheet !== null);
    return () => { if (frame) frame.classList.remove('nav-hidden'); };
  }, [sheet]);
  const isPro = !!user.pro;
  const customTags = user.connTags || [];
  const allTags = customTags;
  const filtered = conns.filter((c) => {
    const hay = (c.name + ' ' + (c.handle || '') + ' ' + (c.place || '') + ' ' + (c.tags || []).join(' ')).toLowerCase();
    const viaOk = viaTab === 'all' || (c.via || 'bio') === viaTab;
    return viaOk && (!q || hay.includes(q.toLowerCase())) && (!tag || (c.tags || []).includes(tag));
  });
  const FREE_CAP = 10;
  const capped = (!isPro && !q && !tag && filtered.length > FREE_CAP);
  const shown = capped ? filtered.slice(0, FREE_CAP) : filtered;
  const save = (patch) => { const next = { ...user, ...patch }; window.Store.saveUser(next); setUser && setUser(next); };
  const addConnection = (c) => {
    const conn = { ...c, id: (window.newId ? window.newId() : Date.now()), when: 'Just now', accent: 'linear-gradient(135deg,#6B8299,#3a4f63)' };
    const next = [conn, ...(user.connections || [])];
    save({ connections: next }); if (window.Store.saveConn) window.Store.saveConn(conn); setSheet(null); toast('Connection added');
  };
  const saveTags = (tags) => { save({ connTags: tags }); setSheet(null); toast('Tags saved'); };
  const isInLeads = (c) => {
    const leads = user.leads || [];
    return leads.some((l) => (c.email && l.email && l.email.toLowerCase() === c.email.toLowerCase()) || (c.name && l.name && l.name.toLowerCase() === c.name.toLowerCase()));
  };
  const moveToLeads = (c) => {
    const lead = {
      id: (window.newId ? window.newId() : 'l' + Date.now()),
      name: c.name || '', email: c.email || '', phone: c.phone || '', company: c.company || '',
      jobTitle: c.role || '', linkedin: '', website: c.website || '', photo: c.photo || null,
      ts: Date.now(), source: 'From Connections · ' + (c.via === 'card' ? 'card tap' : 'bio link'),
      via: c.via || 'bio', stage: (user.followupStage || 'new'), potential: '', notes: c.note || '',
      nextAction: null, metAt: c.place || '', location: c.location || null,
    };
    const nextLeads = [lead, ...(user.leads || [])];
    save({ leads: nextLeads }); if (window.Store.saveLead) window.Store.saveLead(lead);
    toast(c.name + ' moved to Leads');
  };

  return (
    <div className="appscroll"><div className="screen">
      <div className="screen-top">
        <div className="st-logo"><h3 style={{ fontFamily: 'var(--display)', fontWeight: 800, fontSize: 22, letterSpacing: '-0.02em' }}>Connections</h3></div>
      </div>

      <div className="an-source" style={{ marginTop: 14 }}>
        {[['all', 'All', null], ['card', 'NFC Card', 'card'], ['bio', 'Bio Link', 'link']].map(([id, lbl, ic]) => (
          <button key={id} className={viaTab === id ? 'on' : ''} onClick={() => setViaTab(id)}>
            {ic && XI[ic] ? XI[ic]({ width: 14, height: 14 }) : null}{lbl}
          </button>
        ))}
      </div>

      {/* map — Pro feature */}
      {isPro ? (
        <ActionMap conns={conns} />
      ) : (
        <button className="conn-map-locked" onClick={onPro}>
          <ActionMap conns={conns} />
          <div className="cml-veil">
            <span className="pro-badge">PRO</span>
            <div className="cml-t">See your taps on the map</div>
            <div className="cml-s">Unlock the live action map with Tappt Pro</div>
          </div>
        </button>
      )}

      <div className="an-mini" style={{ marginBottom: 16 }}>
        <div className="m"><div className="mv">{total}</div><div className="ml">People met</div></div>
        <div className="m"><div className="mv">{back}</div><div className="ml">Contactable</div></div>
        <div className="m"><div className="mv">{cities}</div><div className="ml">Cities</div></div>
      </div>

      {/* actions */}
      <div className="conn-actions">
        <button className="conn-act primary" onClick={() => setSheet('add')}>{XI.plus({ width: 17, height: 17 })} Add Connection</button>
        <button className="conn-act" onClick={() => setSheet('tags')}>{XI.edit({ width: 15, height: 15 })} Manage Tags</button>
        <button className="conn-act" onClick={() => setSheet('leads')}>{XI.mail ? XI.mail({ width: 15, height: 15 }) : null} Leads{(user.leads || []).length ? <span className="conn-leadbadge">{(user.leads || []).length}</span> : null}</button>
      </div>

      <div className="search-bar" style={{ marginBottom: 12 }}>
        {XI.search ? XI.search({ width: 17, height: 17 }) : null}
        <input placeholder="Search people, places, tags…" value={q} onChange={(e) => setQ(e.target.value)} />
      </div>
      {allTags.length > 0 && (
        <div className="conn-tags-row">
          <button className={'conn-tag' + (tag === null ? ' on' : '')} onClick={() => setTag(null)}>All</button>
          {allTags.map((t) => <button key={t} className={'conn-tag' + (tag === t ? ' on' : '')} onClick={() => setTag(tag === t ? null : t)}>{t}</button>)}
        </div>
      )}

      <div className="conn-list">
        {shown.map((c) => (
          <div className="conn-row conn-row-tap" key={c.id} onClick={() => setDetail(c)}>
            <span className="conn-av" style={{ background: c.accent }}>{c.photo ? <img src={c.photo} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover', borderRadius: '50%' }} /> : (c.name || '?').charAt(0)}</span>
            <div className="conn-info">
              <div className="conn-name">{c.name} <span className="conn-persona">{c.icon && XI[c.icon] ? <span style={{ display: 'inline-flex', verticalAlign: '-2px' }}>{XI[c.icon]({ width: 12, height: 12 })}</span> : null} {c.persona}</span>{isInLeads(c) && <span className="conn-inlead">{XI.check ? XI.check({ width: 11, height: 11 }) : '✓'} In Leads</span>}</div>
              <div className="conn-where">{c.place} · {c.when}</div>
              <div className="conn-got">
                {(() => {
                  const ch = [];
                  if (c.email) ch.push(['mail', 'email-tile']);
                  if (c.phone) ch.push(['phone', 'phone-tile']);
                  if (c.website) ch.push(['website', 'link-tile']);
                  if (!ch.length) return <span className="conn-back">Name only</span>;
                  return ch.map(([ic, cls]) => (
                    <span className={'conn-tile ' + cls} key={ic}>{XI[ic] ? XI[ic]({ width: 15, height: 15 }) : null}</span>
                  ));
                })()}
                {(c.tags || []).map((t) => <span key={t} className="conn-tag-pill">{t}</span>)}
              </div>
            </div>
            <span className="conn-follow">{XI.arrow({ width: 16, height: 16 })}</span>
          </div>
        ))}
        {filtered.length === 0 && <div style={{ textAlign: 'center', color: 'var(--fg-3)', fontSize: 13, padding: '28px 0' }}>No one matches that.</div>}
      </div>

      {capped && (
        <button className="conn-cap-nudge" onClick={onPro}>
          <span className="pro-badge">PRO</span>
          <span><b>{filtered.length - FREE_CAP} more</b> people you've met — unlock your full history with Pro</span>
          {XI.arrow({ width: 16, height: 16 })}
        </button>
      )}

      <button className="conn-export" onClick={exportCsv}>{XI.ext ? XI.ext({ width: 15, height: 15 }) : null} Save all to my phone</button>

      {sheet === 'add' && <AddConnectionSheet tags={allTags} onClose={() => setSheet(null)} onSave={addConnection} />}
      {sheet === 'tags' && <ManageTagsSheet tags={customTags} onClose={() => setSheet(null)} onSave={saveTags} />}
      {sheet === 'leads' && (() => { const eff = (user.leads && user.leads.length) ? user.leads : (user.handle === 'maxxharland' ? SEED_LEADS : []); return <LeadsSheet leads={eff} stages={(user.leadStages && user.leadStages.length) ? user.leadStages : (window.EX_STAGES || [])} onStagesChange={(next) => save({ leadStages: next })} onClose={() => setSheet(null)} onUpdate={(next) => { save({ leads: eff.map((l) => l.id === next.id ? next : l) }); if (window.Store.saveLead) window.Store.saveLead(next); }} onDelete={(id) => { save({ leads: eff.filter((l) => l.id !== id) }); if (window.Store.removeLead) window.Store.removeLead(id); }} onConvert={(lead) => { addConnection({ name: lead.name || (lead.email || 'Lead').split('@')[0], handle: '', persona: 'Work', place: lead.metAt || lead.source || 'Lead capture', got: lead.email ? ['email'] : [], sentBack: false, tags: ['lead'], email: lead.email, phone: lead.phone, photo: lead.photo }); toast('Lead saved as a connection'); }} onClear={() => { save({ leads: [] }); if (window.Store.clearLeads) window.Store.clearLeads(); setSheet(null); toast('Leads cleared'); }} />; })()}
      {detail && window.LeadDetailSheet && (
        <window.LeadDetailSheet
          lead={detail}
          mode="connection"
          inLeads={isInLeads(detail)}
          stages={(user.leadStages && user.leadStages.length) ? user.leadStages : undefined}
          onClose={() => setDetail(null)}
          onUpdate={(next) => { const nc = (user.connections || []).map((x) => x.id === next.id ? next : x); save({ connections: nc }); if (window.Store.saveConn) window.Store.saveConn(next); }}
          onDelete={(id) => { const nc = (user.connections || []).filter((x) => x.id !== id); save({ connections: nc }); if (window.Store.removeConn) window.Store.removeConn(id); }}
          onMoveToLeads={(c) => { moveToLeads(c); setDetail(null); }}
        />
      )}
    </div></div>
  );
  function exportCsv() {
    const rows = [['Name', 'Handle', 'Met at', 'When', 'Tags', 'Shared']]
      .concat(conns.map((c) => [c.name, '@' + (c.handle || ''), c.place, c.when, (c.tags || []).join('|'), (c.got || []).join('|')]));
    const csv = rows.map((r) => r.map((x) => '"' + String(x).replace(/"/g, '""') + '"').join(',')).join('\n');
    try {
      const a = document.createElement('a');
      a.href = URL.createObjectURL(new Blob([csv], { type: 'text/csv' }));
      a.download = 'tappt-connections.csv'; a.click();
      toast('Saved ' + conns.length + ' people');
    } catch (e) { toast('Export ready'); }
  }
}

/* ---- Leads sheet — captured "Stay in touch" form submissions ---- */
function LeadsSheet({ leads, stages: stagesProp, onStagesChange, onClose, onConvert, onClear, onUpdate, onDelete }) {
  const when = (ts) => { const d = new Date(ts); return d.toLocaleDateString(undefined, { day: 'numeric', month: 'short' }); };
  const [detail, setDetail] = React.useState(null);
  const [filter, setFilter] = React.useState('all');
  const [manage, setManage] = React.useState(false);
  const stages = (stagesProp && stagesProp.length) ? stagesProp : (window.EX_STAGES || []);
  const shown = filter === 'all' ? leads : leads.filter((l) => (l.stage || 'new') === filter);
  const pipeline = leads.reduce((s, l) => s + (parseInt(l.potential, 10) || 0), 0);
  const weekAgo = Date.now() - 7 * 864e5;
  const newThisWeek = leads.filter((l) => l.ts >= weekAgo).length;
  const wonId = stages.length ? stages[stages.length - 1].id : 'client';
  const won = leads.filter((l) => l.stage === 'client' || l.stage === wonId).length;
  const gbpK = (n) => n >= 1000 ? '£' + (n / 1000).toFixed(n % 1000 === 0 ? 0 : 1) + 'k' : '£' + n;
  return (
    <div className="sheet-bg" onClick={(e) => { if (e.target === e.currentTarget) onClose(); }}>
      <div className="sheet conn-sheet">
        <div className="sheet-head">
          <h3>Leads {leads.length ? <span className="leads-count">{leads.length}</span> : null}</h3>
          <button className="glass-pill" onClick={onClose}>{XI.close ? XI.close({ width: 16, height: 16 }) : '✕'}</button>
        </div>
        <div className="sheet-scroll">
          {leads.length === 0 ? (
            <div className="leads-empty">
              <span className="le-ic">{XI.mail ? XI.mail({ width: 26, height: 26 }) : '✉'}</span>
              <div className="le-t">No leads yet</div>
              <div className="le-s">Turn on <b>Contact exchange</b> in Edit Profile. When people exchange with you, they land here as leads you can track.</div>
            </div>
          ) : (
            <React.Fragment>
              <div className="leads-kpis">
                <div className="lk lk-hero"><div className="lk-l">Pipeline</div><div className="lk-v">{gbpK(pipeline)}</div></div>
                <div className="lk"><div className="lk-l">New · 7d</div><div className="lk-v">{newThisWeek}</div></div>
                <div className="lk"><div className="lk-l">Won</div><div className="lk-v">{won}</div></div>
              </div>
              <div className="leads-filterbar">
                <button className={'lf-chip' + (filter === 'all' ? ' on' : '')} onClick={() => setFilter('all')}>All</button>
                {stages.map((s) => <button key={s.id} className={'lf-chip' + (filter === s.id ? ' on' : '')} onClick={() => setFilter(s.id)}><span className="ld-dot" style={{ background: s.color }} />{s.label}</button>)}
                {onStagesChange && <button className="lf-chip lf-manage" onClick={() => setManage(true)}>{XI.edit ? XI.edit({ width: 13, height: 13 }) : '⚙'} Stages</button>}
              </div>
              <div className="leads-list">
                {shown.map((l) => {
                  const st = window.exStage ? window.exStage(l.stage) : { label: 'New', color: '#2ED573' };
                  return (
                    <button className="lead-row" key={l.id} onClick={() => setDetail(l)}>
                      <span className="lead-av">{l.photo ? <img src={l.photo} alt="" /> : (l.name || l.email || '?').charAt(0).toUpperCase()}</span>
                      <div className="lead-info">
                        <div className="lead-name-row"><span className="lead-name2">{l.name || (l.email || 'Lead').split('@')[0]}</span><span className="lead-stage-pill" style={{ color: st.color, borderColor: st.color }}>{st.label}</span></div>
                        <div className="lead-sub2">{[l.jobTitle, l.company].filter(Boolean).join(' · ') || l.email}</div>
                        <div className="lead-meta">{l.metAt ? l.metAt + ' · ' : ''}{when(l.ts)}{l.potential ? ' · £' + l.potential : ''}</div>
                      </div>
                      {XI.chevron ? <span className="lead-chev">{XI.chevron({ width: 18, height: 18 })}</span> : '›'}
                    </button>
                  );
                })}
                {shown.length === 0 && <div style={{ textAlign: 'center', color: 'var(--fg-3)', fontSize: 13, padding: '24px 0' }}>No leads in this stage.</div>}
              </div>
            </React.Fragment>
          )}
        </div>
        {leads.length > 0 && (
          <div className="sheet-foot" style={{ padding: '12px 18px' }}>
            <button className="conn-act" style={{ width: '100%' }} onClick={onClear}>Clear all leads</button>
          </div>
        )}
      </div>
      {detail && window.LeadDetailSheet && <window.LeadDetailSheet lead={detail} stages={stages} onClose={() => setDetail(null)} onUpdate={(next) => { setDetail(next); onUpdate && onUpdate(next); }} onDelete={(id) => onDelete && onDelete(id)} onConnection={(l) => { onConvert(l); setDetail(null); }} />}
      {manage && onStagesChange && <StageManagerSheet stages={stages} onClose={() => setManage(false)} onChange={onStagesChange} />}
    </div>
  );
}

/* ---- Stage manager — customise CRM stages like tags ---- */
const STAGE_COLORS = ['#2ED573', '#5CC8FF', '#6B8299', '#E8C75A', '#FF6BC8', '#FF8A3D', '#8A8A92'];
function StageManagerSheet({ stages, onClose, onChange }) {
  const [list, setList] = React.useState(stages.map((s) => ({ ...s })));
  const commit = (next) => { setList(next); onChange(next); };
  const rename = (id, label) => commit(list.map((s) => s.id === id ? { ...s, label } : s));
  const recolor = (id, color) => commit(list.map((s) => s.id === id ? { ...s, color } : s));
  const remove = (id) => { if (list.length <= 1) return; commit(list.filter((s) => s.id !== id)); };
  const add = () => commit(list.concat([{ id: 'st' + Date.now(), label: '', color: STAGE_COLORS[list.length % STAGE_COLORS.length] }]));
  return (
    <div className="sheet-bg" onClick={(e) => { if (e.target === e.currentTarget) onClose(); }}>
      <div className="sheet">
        <div className="sheet-grab" />
        <div className="sheet-head"><span style={{ width: 56 }} /><h3>Lead stages</h3><button className="glass-pill" style={{ background: 'var(--accent)', color: 'var(--accent-ink)', border: 'none' }} onClick={onClose}>Done</button></div>
        <div className="sheet-scroll">
          <div className="ex-hint" style={{ marginBottom: 14 }}>Build your own pipeline — name each stage and pick a colour. The last stage counts as "Won".</div>
          {list.map((s) => (
            <div className="stage-row" key={s.id}>
              <div className="stage-colors">
                {STAGE_COLORS.map((c) => <button key={c} className={'stage-sw' + (s.color === c ? ' on' : '')} style={{ background: c }} onClick={() => recolor(s.id, c)} />)}
              </div>
              <div className="stage-edit">
                <input className="stage-in" value={s.label} placeholder="Stage name" onChange={(e) => rename(s.id, e.target.value)} />
                {list.length > 1 && <button className="stage-x" onClick={() => remove(s.id)}>{XI.close ? XI.close({ width: 14, height: 14 }) : '✕'}</button>}
              </div>
            </div>
          ))}
          <button className="ex-addfield" onClick={add}>{XI.plus ? XI.plus({ width: 15, height: 15 }) : '+'} Add a stage</button>
          <div style={{ height: 18 }} />
        </div>
      </div>
    </div>
  );
}

/* ---- Add Connection sheet (manual, V1CE-style but warm) ---- */
function AddConnectionSheet({ tags, onClose, onSave }) {
  const [d, setD] = React.useState({ name: '', company: '', role: '', place: '', email: '', phone: '', website: '', note: '', persona: 'Work', tags: [], photo: null });
  const set = (p) => setD((s) => ({ ...s, ...p }));
  const toggleTag = (t) => set({ tags: d.tags.includes(t) ? d.tags.filter((x) => x !== t) : [...d.tags, t] });
  const photoRef = React.useRef(null);
  return (
    <div className="sheet-bg" onClick={(e) => { if (e.target === e.currentTarget) onClose(); }}>
      <div className="sheet">
        <div className="sheet-grab" />
        <div className="sheet-head">
          <button className="glass-pill" style={{ background: 'var(--bg-3)', color: 'var(--fg)', border: '1px solid var(--line)' }} onClick={onClose}>{XI.back({ width: 15, height: 15 })} Cancel</button>
          <h3>Add Connection</h3>
          <button className="glass-pill" style={{ background: 'var(--accent)', color: 'var(--accent-ink)', border: 'none' }}
            onClick={() => { if (!d.name) { onClose(); return; } onSave({ name: d.name, handle: '', persona: d.persona, place: d.place || (d.company || 'Added manually'), got: [], sentBack: false, tags: d.tags, company: d.company, role: d.role, email: d.email, phone: d.phone, website: d.website, note: d.note, icon: 'briefcase', photo: d.photo }); }}>
            {XI.check({ width: 15, height: 15 })} Save
          </button>
        </div>
        <div className="sheet-scroll">
          <div className="conn-photo-wrap">
            <button className="conn-photo" onClick={() => photoRef.current.click()} style={d.photo ? { background: 'none' } : null}>
              {d.photo ? <img src={d.photo} alt="" /> : (XI.camera ? XI.camera({ width: 30, height: 30 }) : '📷')}
              <span className="conn-photo-edit">{XI.image ? XI.image({ width: 14, height: 14 }) : '+'}</span>
            </button>
            <input ref={photoRef} type="file" accept="image/*" style={{ display: 'none' }} onChange={(e) => { const f = e.target.files && e.target.files[0]; if (!f) return; const r = new FileReader(); r.onload = () => set({ photo: r.result }); r.readAsDataURL(f); }} />
          </div>
          <div className="field"><label>Name</label><input className="input" placeholder="Who did you meet?" value={d.name} onChange={(e) => set({ name: e.target.value })} /></div>
          <div className="field"><label>Company</label><input className="input" placeholder="Where they work" value={d.company} onChange={(e) => set({ company: e.target.value })} /></div>
          <div className="field"><label>Role</label><input className="input" placeholder="What they do" value={d.role} onChange={(e) => set({ role: e.target.value })} /></div>
          <div className="field"><label>Where you met</label><input className="input" placeholder="e.g. Web Summit · Lisbon" value={d.place} onChange={(e) => set({ place: e.target.value })} /></div>
          <div style={{ height: 1, background: 'var(--line)', margin: '6px 0 16px' }} />
          <div className="field"><label>Email</label><input className="input" placeholder="their@email.com" value={d.email} onChange={(e) => set({ email: e.target.value })} /></div>
          <div className="field"><label>Phone</label><input className="input" placeholder="+44…" value={d.phone} onChange={(e) => set({ phone: e.target.value })} /></div>
          <div className="field"><label>Website</label><input className="input" placeholder="theirsite.com" value={d.website} onChange={(e) => set({ website: e.target.value })} /></div>
          <div className="field"><label>Note</label><textarea className="textarea" placeholder="How you met, what to follow up on…" value={d.note} onChange={(e) => set({ note: e.target.value })} /></div>
          {tags.length > 0 && (
            <div className="field" style={{ marginBottom: 0 }}><label>Tags</label>
              <div className="conn-tags-row" style={{ marginBottom: 0 }}>
                {tags.map((t) => <button key={t} type="button" className={'conn-tag' + (d.tags.includes(t) ? ' on' : '')} onClick={() => toggleTag(t)}>{t}</button>)}
              </div>
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

/* ---- Manage Tags sheet ---- */
function ManageTagsSheet({ tags, onClose, onSave }) {
  const [list, setList] = React.useState(tags.length ? [...tags] : []);
  const [val, setVal] = React.useState('');
  const add = () => { const v = val.trim(); if (v && !list.includes(v)) { setList([...list, v]); setVal(''); } };
  return (
    <div className="sheet-bg" onClick={(e) => { if (e.target === e.currentTarget) onClose(); }}>
      <div className="sheet">
        <div className="sheet-grab" />
        <div className="sheet-head">
          <button className="glass-pill" style={{ background: 'var(--bg-3)', color: 'var(--fg)', border: '1px solid var(--line)' }} onClick={onClose}>{XI.back({ width: 15, height: 15 })} Cancel</button>
          <h3>Manage Tags</h3>
          <button className="glass-pill" style={{ background: 'var(--accent)', color: 'var(--accent-ink)', border: 'none' }} onClick={() => onSave(list)}>{XI.check({ width: 15, height: 15 })} Save</button>
        </div>
        <div className="sheet-scroll">
          <div className="search-bar" style={{ marginBottom: 14 }}>
            <input placeholder="New tag (e.g. investor, mate, client)" value={val} onChange={(e) => setVal(e.target.value)} onKeyDown={(e) => { if (e.key === 'Enter') add(); }} />
            <button className="cp" onClick={add} style={{ color: 'var(--accent)', fontWeight: 700, fontSize: 13 }}>Add</button>
          </div>
          {list.length === 0
            ? <div style={{ textAlign: 'center', color: 'var(--fg-3)', fontSize: 13, padding: '28px 0' }}>No tags yet. Create your first above.</div>
            : <div className="conn-tags-row">
                {list.map((t) => <button key={t} className="conn-tag on" onClick={() => setList(list.filter((x) => x !== t))}>{t} ✕</button>)}
              </div>}
          <p style={{ fontSize: 12, color: 'var(--fg-3)', marginTop: 14, lineHeight: 1.5 }}>Tap a tag to remove it. Tags help you filter who you've met — "investor", "mate", "met at the gym".</p>
        </div>
      </div>
    </div>
  );
}

window.PersonaSheet = PersonaSheet;
window.ConnectionsScreen = ConnectionsScreen;
