// ============================================================
// MOORE AI v13 — ROOT APP COMPONENT
// Holds all state, routing, nav shell, voice/AI engine
// No import/export — all globals loaded in order via index.html
// ============================================================

// =========================================================
// AUTH GATE — redirect to /login if not authenticated
// =========================================================
(function() {
  const auth = sessionStorage.getItem('moore_auth');
  if (!auth) {
    window.location.href = '/login';
  }
})();

const { useState, useEffect, useRef, useCallback, useMemo } = React;

// =========================================================
// ERROR BOUNDARY
// =========================================================
class ErrorBoundary extends React.Component {
  constructor(props) { super(props); this.state = { hasError: false, error: null }; }
  static getDerivedStateFromError(e) { return { hasError: true, error: e }; }
  render() {
    if (this.state.hasError) {
      return (
        <div className="h-full flex items-center justify-center p-8">
          <div className="bg-red-950/40 border border-red-800/50 rounded-2xl p-6 max-w-lg text-center">
            <div className="text-red-400 font-bold text-sm mb-2">Render Error</div>
            <div className="text-xs mono text-red-400/70">{this.state.error?.message || 'Unknown error'}</div>
            <button onClick={() => this.setState({ hasError: false, error: null })}
              className="mt-4 px-4 py-2 bg-red-600/20 border border-red-500/30 rounded-lg text-xs text-red-400 hover:bg-red-600/30 transition-colors">
              Retry
            </button>
          </div>
        </div>
      );
    }
    return this.props.children;
  }
}

// =========================================================
// =========================================================
// BALANCED JSON EXTRACTOR — handles nested objects in ACTION: {...}
// Also catches raw JSON blobs the AI emits without the ACTION: prefix.
// =========================================================
const parseActionBlocks = (text) => {
  const actions = [];
  const ranges = [];
  const ACTION_TYPES = /^(CREATE_CONTACT|UPDATE_CONTACT|ADD_NOTE|SEND_SMS|SEND_EMAIL|MAKE_CALL|SCHEDULE|UPDATE_OPP|MOVE_STAGE|ADD_TASK|CHAIN)$/;

  // Walk balanced braces starting at `start` (text[start] must be '{')
  const extractEnd = (src, start) => {
    let depth = 0, j = start, inStr = false, esc = false;
    while (j < src.length) {
      const c = src[j];
      if (esc) { esc = false; }
      else if (c === '\\' && inStr) { esc = true; }
      else if (c === '"') { inStr = !inStr; }
      else if (!inStr) {
        if (c === '{') depth++;
        else if (c === '}') { depth--; if (depth === 0) return j; }
      }
      j++;
    }
    return -1;
  };

  // Pass 1 — explicit "ACTION: {" blocks
  let i = 0;
  while (true) {
    const mi = text.indexOf('ACTION: {', i);
    if (mi === -1) break;
    const end = extractEnd(text, mi + 8);
    if (end === -1) break;
    try {
      const jsonStr = text.substring(mi + 8, end + 1);
      JSON.parse(jsonStr);
      actions.push(jsonStr);
      ranges.push([mi, end + 1]);
    } catch(e) {}
    i = end + 1;
  }

  // Remove pass-1 blocks from a working copy
  let clean = text;
  for (let k = ranges.length - 1; k >= 0; k--) {
    clean = clean.substring(0, ranges[k][0]) + clean.substring(ranges[k][1]);
  }

  // Pass 2 — raw JSON blobs the AI embedded without the "ACTION: " prefix
  const raw2Ranges = [];
  i = 0;
  while (i < clean.length) {
    const brace = clean.indexOf('{', i);
    if (brace === -1) break;
    const end = extractEnd(clean, brace);
    if (end === -1) { i = brace + 1; continue; }
    const candidate = clean.substring(brace, end + 1);
    try {
      const parsed = JSON.parse(candidate);
      if (parsed && typeof parsed.type === 'string' && ACTION_TYPES.test(parsed.type)) {
        actions.push(candidate);
        raw2Ranges.push([brace, end + 1]);
      }
    } catch(e) {}
    i = end + 1;
  }
  for (let k = raw2Ranges.length - 1; k >= 0; k--) {
    clean = clean.substring(0, raw2Ranges[k][0]) + clean.substring(raw2Ranges[k][1]);
  }

  // Tidy up leftover punctuation artefacts (trailing colons, double spaces, blank lines)
  clean = clean.replace(/:\s*$/gm, '').replace(/[ \t]{2,}/g, ' ').replace(/\n{3,}/g, '\n\n').trim();
  return { actions, clean };
};
const stripActionBlocks = (text) => parseActionBlocks(text).clean;

// =========================================================
// ACTION APPROVAL HELPERS
// =========================================================
const ACTION_META = {
  SEND_EMAIL:      { icon: 'mail',           label: 'Send Email',      color: '#06b6d4' },
  SEND_SMS:        { icon: 'message-circle', label: 'Send SMS',        color: '#10b981' },
  MAKE_CALL:       { icon: 'phone-call',     label: 'Call',            color: '#3b82f6' },
  CREATE_CONTACT:  { icon: 'user-plus',      label: 'Create Contact',  color: '#3b82f6' },
  UPDATE_CONTACT:  { icon: 'pencil',         label: 'Update Contact',  color: '#64748b' },
  ADD_NOTE:        { icon: 'file-text',      label: 'Add Note',        color: '#ec4899' },
  ADD_TASK:        { icon: 'check-square',   label: 'Create Task',     color: '#10b981' },
  SCHEDULE:        { icon: 'calendar-check', label: 'Schedule',        color: '#8b5cf6' },
  UPDATE_OPP:      { icon: 'trophy',         label: 'Update Deal',     color: '#f59e0b' },
  MOVE_STAGE:      { icon: 'git-merge',      label: 'Move Stage',      color: '#6366f1' },
  CHAIN:           { icon: 'zap',            label: 'Chain',           color: '#f59e0b' },
};

const describeAction = (action) => {
  const name = action.name || (action.data ? `${action.data.firstName||''} ${action.data.lastName||''}`.trim() : '') || '';
  switch (action.type) {
    case 'SEND_EMAIL': return `${name}${action.subject ? ` — "${action.subject.substring(0,45)}"` : ''}`;
    case 'SEND_SMS':   return `${name}${action.message ? ` — "${action.message.substring(0,45)}..."` : ''}`;
    case 'MAKE_CALL':  return `${name}${action.objective ? ` — ${action.objective.substring(0,45)}` : ''}`;
    case 'CREATE_CONTACT': return name || 'new contact';
    case 'ADD_NOTE':   return `${name}${action.body ? ` — "${action.body.substring(0,45)}"` : ''}`;
    case 'ADD_TASK':   return action.title ? `"${action.title.substring(0,55)}"` : '';
    case 'SCHEDULE':   return `${name}${action.title ? ` — ${action.title}` : ''}`;
    case 'UPDATE_OPP': return `mark ${action.status}`;
    case 'MOVE_STAGE': return 'move to new stage';
    default: return '';
  }
};

const ActionApprovalCard = ({ m, approveActions, cancelActions }) => (
  <div className="max-w-[82%] rounded-2xl rounded-tl-md overflow-hidden border border-amber-500/25 bg-slate-900/90 shadow-xl">
    <div className="px-4 py-3 border-b border-slate-700/40">
      <div className="flex items-center gap-1.5 text-[10px] mono text-amber-400 font-bold uppercase tracking-wider mb-3">
        <Icon name="shield-alert" size={11} />
        {m.actions.length} Action{m.actions.length !== 1 ? 's' : ''} Queued — Approve to Execute
      </div>
      <div className="space-y-2">
        {m.actions.map((action, ai) => {
          const meta = ACTION_META[action.type] || { icon: 'zap', label: action.type, color: '#94a3b8' };
          const desc = describeAction(action);
          return (
            <div key={ai} className="flex items-center gap-2 text-xs">
              <div className="w-6 h-6 rounded-lg flex items-center justify-center shrink-0"
                style={{ background: meta.color + '18', border: `1px solid ${meta.color}30` }}>
                <Icon name={meta.icon} size={11} style={{ color: meta.color }} />
              </div>
              <span className="mono font-bold text-[10px] shrink-0" style={{ color: meta.color }}>{action.type}</span>
              {desc && <span className="text-slate-500 truncate text-[11px]">{desc}</span>}
            </div>
          );
        })}
      </div>
    </div>
    <div className="px-4 py-3 flex items-center gap-2">
      <button onClick={() => approveActions(m.id)}
        className="flex-1 py-2 bg-emerald-600/20 hover:bg-emerald-600/35 border border-emerald-500/40 rounded-xl text-xs font-bold text-emerald-400 transition-all flex items-center justify-center gap-1.5">
        <Icon name="play" size={11} />
        Run {m.actions.length} Action{m.actions.length !== 1 ? 's' : ''}
      </button>
      <button onClick={() => cancelActions(m.id)}
        className="px-4 py-2 bg-slate-800/60 hover:bg-slate-700/60 border border-slate-700/40 rounded-xl text-xs font-bold text-slate-500 hover:text-slate-300 transition-all">
        Cancel
      </button>
    </div>
  </div>
);

// MAIN APP
// =========================================================
const App = () => {
  // ── Theme ──
  const [theme, setTheme] = useState('dark');

  // ── Core state ──
  const [activeTab, setActiveTab] = useState('dashboard');
  const [loading, setLoading] = useState(true);
  const [crmData, setCrmData] = useState({ stats: { pipelineValue: 0, totalLeads: 0, winRate: 0, aiActions: 0 }, opportunities: [] });
  const [pipelines, setPipelines] = useState([]);
  const [contacts, setContacts] = useState([]);
  const [calEvents, setCalEvents] = useState([]);
  const [conversations, setConversations] = useState([]);
  const [analytics, setAnalytics] = useState(null);
  const [selectedLead, setSelectedLead] = useState(null);
  const [selectedContact, setSelectedContact] = useState(null);
  const [searchQuery, setSearchQuery] = useState('');
  const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
  const [notifications, setNotifications] = useState([]);
  const [loadingTabs, setLoadingTabs] = useState({});

  // ── Tasks (localStorage) ──
  const [tasks, setTasks] = useState(() => {
    try { return JSON.parse(localStorage.getItem('moore_tasks') || '[]'); } catch(e) { return []; }
  });

  // ── AI Chat state ──
  const [chatOpen, setChatOpen] = useState(false);
  const [chatInput, setChatInput] = useState('');
  const [messages, setMessages] = useState([
    { role: 'ai', text: "Systems online. Registry synced. I'm ready to architect your pipeline — type a command or use voice." }
  ]);
  const [isTyping, setIsTyping] = useState(false);
  const [isListening, setIsListening] = useState(false);
  const [chatMode, setChatMode] = useState('minimized');

  // ── Pipeline view ──
  const [pipelineView, setPipelineView] = useState('kanban');
  const [pipelineFilter, setPipelineFilter] = useState('all');
  const [selectedPipelineId, setSelectedPipelineId] = useState('');

  // ── Calendar state ──
  const [calView, setCalView] = useState('month');
  const [calDate, setCalDate] = useState(new Date());
  const [calendars, setCalendars] = useState([]);
  const [selectedCalendarId, setSelectedCalendarId] = useState('');

  // ── Contact form ──
  const [showContactForm, setShowContactForm] = useState(false);
  const [contactForm, setContactForm] = useState({ firstName: '', lastName: '', email: '', phone: '' });

  // ── Notes ──
  const [selectedLeadNotes, setSelectedLeadNotes] = useState([]);
  const [newNote, setNewNote] = useState('');

  // ── Refs ──
  const audioRef = useRef(null);
  const recognitionRef = useRef(null);
  const chatEndRef = useRef(null);
  const dragItemRef = useRef(null);
  const handleSendMsgRef = useRef(null);
  const pendingActionsRef = useRef({});
  const transcriptRef = useRef('');
  const silenceTimerRef = useRef(null);
  const isVoiceEnabledRef = useRef(true);
  const [isVoiceEnabled, setIsVoiceEnabled] = useState(true);
  const [geminiKey, setGeminiKey] = useState('');
  const [blandKey, setBlandKey] = useState('');
  const [locationName, setLocationName] = useState('BigBlueCollar');
  const [liveTranscript, setLiveTranscript] = useState('');
  const [actionItems, setActionItems] = useState([]);
  const [activeCalls, setActiveCalls] = useState([]);

  // ── Theme persistence ──
  useEffect(() => { localStorage.setItem('moore_theme', theme); }, [theme]);

  // ── AUDIO: Gemini TTS + browser Speech Synthesis fallback ──
  const stopAudio = () => {
    if (audioRef.current) { audioRef.current.pause(); audioRef.current.currentTime = 0; }
    window.speechSynthesis?.cancel();
  };

  const speakBrowser = (text) => {
    if (!isVoiceEnabledRef.current) return;
    const clean = text.replace(/[*_#`~]/g, '').trim();
    const utterance = new SpeechSynthesisUtterance(clean);
    const voices = window.speechSynthesis?.getVoices?.() || [];
    const maleVoice = voices.find(v =>
      v.name.includes('Google US English Male') || v.name.includes('Google UK English Male') ||
      v.name.includes('Daniel') || v.name.includes('Arthur') || v.name.includes('Alex') ||
      (v.name.toLowerCase().includes('male') && v.lang.startsWith('en'))
    );
    if (maleVoice) utterance.voice = maleVoice;
    utterance.rate = 1.05;
    window.speechSynthesis.speak(utterance);
  };

  const speak = async (text) => {
    if (!isVoiceEnabledRef.current || !text || text.length < 5) return;
    stopAudio();
    const clean = stripActionBlocks(text).replace(/[*_#`~]/g, '').trim().substring(0, 1000);
    if (!clean) return;
    if (geminiKey) {
      try {
        const res = await fetch(
          `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-tts:generateContent?key=${geminiKey}`,
          {
            method: 'POST', headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({
              contents: [{ parts: [{ text: 'Say professionally and conversationally: ' + clean }] }],
              generationConfig: {
                responseModalities: ['AUDIO'],
                speechConfig: { voiceConfig: { prebuiltVoiceConfig: { voiceName: 'Algieba' } } }
              },
              model: 'gemini-2.5-flash-preview-tts'
            })
          }
        );
        const data = await res.json();
        if (data.error) throw new Error(data.error.message);
        const audioPart = data.candidates?.[0]?.content?.parts?.[0]?.inlineData;
        if (audioPart?.data) {
          const sampleRateMatch = (audioPart.mimeType || '').match(/rate=(\d+)/);
          const sampleRate = sampleRateMatch ? parseInt(sampleRateMatch[1]) : 24000;
          const binaryStr = window.atob(audioPart.data);
          const bytes = new Uint8Array(binaryStr.length);
          for (let i = 0; i < binaryStr.length; i++) bytes[i] = binaryStr.charCodeAt(i);
          const wavBlob = pcmToWav(bytes.buffer, sampleRate);
          const url = URL.createObjectURL(wavBlob);
          const audio = new Audio(url);
          audioRef.current = audio;
          audio.play();
          return;
        }
      } catch (e) {
        console.warn('Gemini TTS failed, using browser voice:', e.message);
      }
    }
    speakBrowser(clean);
  };

  // ── VOICE RECOGNITION — continuous with silence timer ──
  useEffect(() => {
    const SR = window.SpeechRecognition || window.webkitSpeechRecognition;
    if (SR) {
      const rec = new SR();
      rec.continuous = true;
      rec.interimResults = true;
      rec.lang = 'en-US';

      rec.onresult = (event) => {
        let finalTranscript = '';
        let interimTranscript = '';
        for (let i = 0; i < event.results.length; ++i) {
          if (event.results[i].isFinal) finalTranscript += event.results[i][0].transcript;
          else interimTranscript += event.results[i][0].transcript;
        }
        const currentText = finalTranscript + interimTranscript;
        transcriptRef.current = currentText;
        setLiveTranscript(currentText);
        setChatInput(currentText);

        clearTimeout(silenceTimerRef.current);
        silenceTimerRef.current = setTimeout(() => {
          if (transcriptRef.current.trim()) {
            rec.stop();
            setIsListening(false);
            if (handleSendMsgRef.current) {
              handleSendMsgRef.current(null, transcriptRef.current);
            }
            transcriptRef.current = '';
            setLiveTranscript('');
            setChatInput('');
          }
        }, 2000);
      };

      rec.onerror = (e) => {
        console.warn('Speech error:', e.error);
        setIsListening(false);
        clearTimeout(silenceTimerRef.current);
      };

      rec.onend = () => {
        setIsListening(false);
        clearTimeout(silenceTimerRef.current);
      };

      recognitionRef.current = rec;
    }
  }, []);

  useEffect(() => { isVoiceEnabledRef.current = isVoiceEnabled; }, [isVoiceEnabled]);

  const toggleListening = () => {
    if (!recognitionRef.current) {
      alert('Voice input requires Chrome or Edge browser.');
      return;
    }
    if (isListening) {
      recognitionRef.current.stop();
      setIsListening(false);
      clearTimeout(silenceTimerRef.current);
      if (transcriptRef.current.trim() && handleSendMsgRef.current) {
        handleSendMsgRef.current(null, transcriptRef.current);
        transcriptRef.current = '';
        setLiveTranscript('');
        setChatInput('');
      }
    } else {
      try {
        setChatInput('');
        transcriptRef.current = '';
        setLiveTranscript('');
        if (audioRef.current) { audioRef.current.pause(); audioRef.current.currentTime = 0; }
        window.speechSynthesis?.cancel();
        recognitionRef.current.start();
        setIsListening(true);
      } catch(e) { console.error('Mic start error:', e); }
    }
  };

  // ── DATA FETCHING ──
  const syncOpportunities = async (pipelineId = '') => {
    try {
      const data = await API.syncOpportunities(pipelineId);
      if (data) setCrmData(data);
    } catch (e) { console.warn('Sync failed', e); }
  };

  const fetchPipelines = async () => {
    try {
      const data = await API.fetchPipelines();
      if (data.success) setPipelines(data.pipelines);
    } catch (e) {}
  };

  const fetchContacts = async () => {
    setLoadingTabs(p => ({ ...p, contacts: true }));
    try {
      const data = await API.fetchContacts();
      if (data.success) setContacts(data.contacts);
    } catch (e) {}
    setLoadingTabs(p => ({ ...p, contacts: false }));
  };

  const fetchCalendar = async (calId) => {
    setLoadingTabs(p => ({ ...p, calendar: true }));
    try {
      const data = await API.fetchCalendar(calId || selectedCalendarId);
      if (data.success) setCalEvents(data.events);
    } catch (e) {}
    setLoadingTabs(p => ({ ...p, calendar: false }));
  };

  const fetchCalendars = async () => {
    try {
      const [calsData, settingsData] = await Promise.all([API.fetchCalendars(), API.fetchSettings()]);
      if (calsData.success && calsData.calendars.length > 0) {
        setCalendars(calsData.calendars);
        const saved = settingsData.calendarId || calsData.calendars[0].id;
        setSelectedCalendarId(saved);
        return saved;
      }
    } catch (e) {}
    return null;
  };

  const fetchConversations = async () => {
    setLoadingTabs(p => ({ ...p, inbox: true }));
    try {
      const data = await API.fetchConversations();
      if (data.success) setConversations(data.conversations);
    } catch (e) {}
    setLoadingTabs(p => ({ ...p, inbox: false }));
  };

  const fetchAnalytics = async () => {
    setLoadingTabs(p => ({ ...p, analytics: true }));
    try {
      const data = await API.fetchAnalytics();
      if (data.success) setAnalytics(data);
    } catch (e) {}
    setLoadingTabs(p => ({ ...p, analytics: false }));
  };

  const fetchLeadNotes = async (contactId) => {
    if (!contactId) return;
    try {
      const data = await API.fetchContactNotes(contactId);
      if (data.success) setSelectedLeadNotes(data.notes);
    } catch (e) {}
  };

  useEffect(() => {
    (async () => {
      try {
        const cfg = await API.fetchConfig();
        if (cfg.geminiKey) setGeminiKey(cfg.geminiKey);
        if (cfg.blandKey) setBlandKey(cfg.blandKey);
        if (cfg.locationName) setLocationName(cfg.locationName);
      } catch(e) {}
      await Promise.all([syncOpportunities(), fetchPipelines()]);
      setLoading(false);
    })();
  }, []);

  useEffect(() => {
    if (activeTab === 'contacts' && contacts.length === 0) fetchContacts();
    if (activeTab === 'calendar') { fetchCalendars().then(calId => fetchCalendar(calId)); }
    if (activeTab === 'inbox' && conversations.length === 0) fetchConversations();
    if (activeTab === 'analytics' && !analytics) fetchAnalytics();
  }, [activeTab]);

  useEffect(() => {
    if (window.lucide) window.lucide.createIcons();
  });

  useEffect(() => {
    chatEndRef.current?.scrollIntoView({ behavior: 'smooth' });
  }, [messages, isTyping]);

  useEffect(() => {
    if (selectedLead?.contactId) fetchLeadNotes(selectedLead.contactId);
    else setSelectedLeadNotes([]);
  }, [selectedLead]);

  // ── AI CHAT — calls Gemini directly from browser ──
  const searchContactByName = async (name) => {
    try {
      const data = await API.searchContacts(name);
      return (data.contacts || [])[0] || null;
    } catch(e) { return null; }
  };

  const pollBlandCall = async (callId, contactId, contactName) => {
    const done = ['completed','no-answer','failed','busy','canceled'];
    for (let i = 0; i < 60; i++) {
      await new Promise(r => setTimeout(r, 5000));
      try {
        const data = await API.pollCall(callId);
        if (done.includes(data.status)) {
          if (data.status === 'completed' && (data.transcript || data.summary)) {
            await API.addNote(contactId, `[AI CALL TRANSCRIPT]\n${data.transcript}\n\n[SUMMARY]: ${data.summary}`);
            const aiFollowUp = await callGeminiDirect(
              `[SYSTEM] Call with ${contactName} completed.\nSUMMARY: ${data.summary}\nTRANSCRIPT: ${data.transcript}\n\nExtract 1-3 action items as a JSON array: [{"title":"...","priority":"high|medium|low","contact":"${contactName}"}]. Respond ONLY with the JSON array.`,
              geminiKey
            );
            try {
              const items = JSON.parse(aiFollowUp.replace(/```json|```/g,'').trim());
              setActionItems(prev => [...items.map(it => ({ ...it, id: Date.now() + Math.random(), done: false, createdAt: new Date().toISOString() })), ...prev]);
            } catch(e) {}
          }
          return data;
        }
        setActiveCalls(prev => prev.map(c => c.callId === callId ? { ...c, status: data.status } : c));
      } catch(e) { break; }
    }
    return null;
  };

  const callGeminiDirect = async (prompt, key) => {
    const models = ['gemini-2.5-flash', 'gemini-2.0-flash-001', 'gemini-2.0-flash', 'gemini-1.5-flash'];
    for (const model of models) {
      try {
        const res = await fetch(
          `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${key}`,
          {
            method: 'POST', headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({
              contents: [{ parts: [{ text: prompt }] }],
              generationConfig: { maxOutputTokens: 4096, temperature: 0.85 }
            })
          }
        );
        const data = await res.json();
        if (data.error) throw new Error(data.error.message);
        const text = data.candidates?.[0]?.content?.parts?.[0]?.text;
        if (text) { console.log(`[Gemini] ${model} OK`); return text; }
      } catch (e) { console.warn(`[Gemini] ${model}:`, e.message); }
    }
    throw new Error('All Gemini models failed — check API key');
  };

  const executeCrmAction = async (action, allContacts) => {
    const type = action.type;

    if (type === 'ADD_TASK') {
      const newTask = {
        id: Date.now(),
        title: action.title,
        priority: action.priority || 'medium',
        contact: action.contact || '',
        status: 'todo',
        source: 'AI Generated',
        createdAt: new Date().toISOString()
      };
      setTasks(prev => {
        const updated = [newTask, ...prev];
        localStorage.setItem('moore_tasks', JSON.stringify(updated));
        return updated;
      });
      return { ok: true, msg: `✓ Task added to Tasks board: **${action.title}**` };
    }

    if (type === 'CREATE_CONTACT') {
      const data = await API.createContact(action.data);
      if (!data.success) throw new Error(data.error);
      setTimeout(fetchContacts, 1500);
      return { ok: true, msg: `✓ Contact **${action.data.firstName} ${action.data.lastName||''}** added to CRM (ID: ${data.id})` };
    }

    if (type === 'UPDATE_CONTACT') {
      let contactId = action.contactId;
      if (!contactId && action.name) {
        const found = await searchContactByName(action.name);
        contactId = found?.id;
        if (!contactId) throw new Error(`Contact "${action.name}" not found`);
      }
      if (action.data && Object.keys(action.data).length) {
        await API.updateContact(contactId, action.data);
      }
      if (action.note) {
        await API.addNote(contactId, action.note);
      }
      setTimeout(fetchContacts, 1500);
      return { ok: true, msg: `✓ Contact updated` };
    }

    if (type === 'ADD_NOTE') {
      let contactId = action.contactId;
      if (!contactId && action.name) {
        const found = await searchContactByName(action.name);
        contactId = found?.id;
        if (!contactId) throw new Error(`Contact "${action.name}" not found`);
      }
      await API.addNote(contactId, action.body);
      return { ok: true, msg: `✓ Note added to contact` };
    }

    if (type === 'SEND_SMS' || type === 'SEND_EMAIL') {
      let contactId = action.contactId;
      let contactName = action.name || 'Contact';
      if (!contactId && action.name) {
        const found = await searchContactByName(action.name);
        contactId = found?.id;
        if (!contactId) throw new Error(`Contact "${action.name}" not found`);
      }
      const msgType = type === 'SEND_EMAIL' ? 'Email' : 'SMS';
      const data = await API.sendMessage(contactId, msgType, action.message, action.subject);
      if (!data.success) throw new Error(data.error);
      return { ok: true, msg: `✓ ${msgType} sent to **${contactName}**` };
    }

    if (type === 'MAKE_CALL') {
      let contactId = action.contactId;
      let phone = action.phone;
      let contactName = action.name || 'Contact';
      if (!contactId && action.name) {
        const found = await searchContactByName(action.name);
        contactId = found?.id;
        phone = phone || found?.phone;
        contactName = found?.name || contactName;
      }
      if (!phone) throw new Error(`No phone number for "${action.name}". Add one in Contacts.`);
      if (!blandKey) throw new Error('BLAND_API_KEY not set in .env');
      const data = await API.makeCall(contactId, phone, contactName, action.objective || 'follow up');
      if (!data.success) throw new Error(data.error);
      setActiveCalls(prev => [...prev, { callId: data.callId, contactName, status: 'ringing', startedAt: new Date() }]);
      pollBlandCall(data.callId, contactId, contactName).then(result => {
        if (result) {
          setMessages(prev => [...prev, { role: 'ai', text: `📞 Call with **${contactName}** ${result.status}. ${result.summary ? 'Summary: ' + result.summary : ''} Transcript saved to contact notes.`, type: 'call-result' }]);
          setActiveCalls(prev => prev.filter(c => c.callId !== data.callId));
        }
      });
      return { ok: true, msg: `📞 Calling **${contactName}** now — transcript will be saved to their notes when done` };
    }

    if (type === 'SCHEDULE') {
      let contactId = action.contactId;
      if (!contactId && action.name) {
        const found = await searchContactByName(action.name);
        contactId = found?.id;
        if (!contactId) throw new Error(`Contact "${action.name}" not found`);
      }
      const data = await API.scheduleAppointment({ contactId, startTime: action.startTime, endTime: action.endTime, title: action.title, notes: action.notes });
      if (!data.success) throw new Error(data.error);
      setTimeout(fetchCalendar, 1500);
      return { ok: true, msg: `✓ Appointment scheduled: **${action.title}**` };
    }

    if (type === 'UPDATE_OPP') {
      await API.updateOpportunity(action.id, { status: action.status });
      setTimeout(() => syncOpportunities(selectedPipelineId), 1200);
      return { ok: true, msg: `✓ Deal status → **${action.status}**` };
    }

    if (type === 'MOVE_STAGE') {
      await API.moveStage(action.id, action.stageId);
      setTimeout(() => syncOpportunities(selectedPipelineId), 1200);
      return { ok: true, msg: `✓ Deal moved to new stage` };
    }

    return { ok: false, msg: `Unknown action: ${type}` };
  };

  const approveActions = async (approvalId) => {
    const actionStrs = pendingActionsRef.current[approvalId];
    if (!actionStrs) return;
    delete pendingActionsRef.current[approvalId];
    setMessages(prev => prev.filter(m => !(m.type === 'action-approval' && m.id === approvalId)));
    for (const actionStr of actionStrs) {
      try {
        const action = JSON.parse(actionStr);
        const pendingId = Date.now() + Math.random();
        setMessages(prev => [...prev, { role: 'ai', text: `⏳ Executing: ${action.type.replace(/_/g,' ')}...`, id: pendingId, type: 'action-pending' }]);
        const result = await executeCrmAction(action, contacts);
        setMessages(prev => prev.map(m => m.id === pendingId ? { ...m, text: result.msg, type: result.ok ? 'action-success' : 'action-error' } : m));
      } catch (ae) {
        setMessages(prev => [...prev, { role: 'ai', text: `✗ Action failed: ${ae.message}`, type: 'action-error' }]);
      }
    }
  };

  const cancelActions = (approvalId) => {
    delete pendingActionsRef.current[approvalId];
    setMessages(prev => prev.filter(m => !(m.type === 'action-approval' && m.id === approvalId)));
    setMessages(prev => [...prev, { role: 'ai', text: 'Got it — actions cancelled.' }]);
  };

  const handleSendMessage = async (e, voiceText = null) => {
    if (e) e.preventDefault();
    const userMsg = voiceText || chatInput;
    if (!userMsg.trim()) return;
    setChatInput('');
    setLiveTranscript('');
    setMessages(prev => [...prev, { role: 'user', text: userMsg }]);
    setIsTyping(true);
    stopAudio();

    const key = geminiKey;
    if (!key) {
      setMessages(prev => [...prev, { role: 'ai', text: 'No Gemini API key loaded. Check Settings → Test Gemini AI.' }]);
      setIsTyping(false);
      return;
    }

    try {
      const leadCtx = selectedLead ? `\nFOCUSED LEAD: ${selectedLead.name} | ${selectedLead.status} | $${selectedLead.value} | Phone: ${selectedLead.phone} | Contact ID: ${selectedLead.contactId}` : '';
      const now = new Date();

      const systemPrompt = `You are Moore AI (Jake Moore), sharp Pipeline Architect for BigBlueCollar.
CURRENT TIME: ${now.toLocaleString()}
PIPELINE: ${crmData.stats.totalLeads} leads · $${crmData.stats.pipelineValue} · ${crmData.stats.winRate}% win rate${leadCtx}

TOP OPPORTUNITIES:
${JSON.stringify((crmData.opportunities||[]).slice(0,25).map(o=>({id:o.id,name:o.name,status:o.status,stage:o.stageName,value:o.value,contact:o.contact,contactId:o.contactId,email:o.email,phone:o.phone})))}

CAPABILITIES — output ACTION blocks at the END of your response, each on its OWN LINE:
ACTION: {"type":"CREATE_CONTACT","data":{"firstName":"","lastName":"","email":"","phone":""}}
ACTION: {"type":"UPDATE_CONTACT","name":"Contact Name","contactId":"","data":{"email":"","phone":"","tags":[]},"note":"note text"}
ACTION: {"type":"ADD_NOTE","name":"Contact Name","contactId":"","body":"note text"}
ACTION: {"type":"SEND_SMS","name":"Contact Name","contactId":"","message":"..."}
ACTION: {"type":"SEND_EMAIL","name":"Contact Name","contactId":"","subject":"...","message":"..."}
ACTION: {"type":"MAKE_CALL","name":"Contact Name","contactId":"","phone":"","objective":"what to accomplish on the call"}
ACTION: {"type":"SCHEDULE","name":"Contact Name","contactId":"","title":"Appointment title","startTime":"ISO datetime","endTime":"ISO datetime","notes":""}
ACTION: {"type":"UPDATE_OPP","id":"EXACT_ID","status":"won|lost|open"}
ACTION: {"type":"MOVE_STAGE","id":"EXACT_ID","stageId":"STAGE_ID"}
ACTION: {"type":"ADD_TASK","title":"Task title","priority":"high|medium|low","contact":"Contact Name"}
RULE: ACTION lines must start with exactly "ACTION: {" — never inline JSON inside sentences. Write your human-readable summary first, then list all ACTION lines after.

PERSONA: Sharp, decisive. Say "Yo", "Lock it in", "Done." Keep it tight. State what action you took. If you can't find a contact ID, use "name" field — system will search.`;

      let responseText = await callGeminiDirect(`${systemPrompt}\n\nUser: ${userMsg}`, key);

      const { actions: matches, clean: displayText } = parseActionBlocks(responseText);

      if (displayText) {
        setMessages(prev => [...prev, { role: 'ai', text: displayText }]);
        speak(displayText);
      }

      if (matches.length > 0) {
        const approvalId = Date.now() + Math.random();
        const parsedActions = matches.map(s => { try { return JSON.parse(s); } catch(e) { return null; } }).filter(Boolean);
        pendingActionsRef.current[approvalId] = matches;
        setMessages(prev => [...prev, { role: 'ai', type: 'action-approval', id: approvalId, actions: parsedActions }]);
      } else if (!displayText) {
        setMessages(prev => [...prev, { role: 'ai', text: 'Done.' }]);
      }

    } catch (err) {
      const errMsg = `Neural link error: ${err.message}`;
      setMessages(prev => [...prev, { role: 'ai', text: errMsg }]);
      speakBrowser('Error connecting to AI.');
    } finally {
      setIsTyping(false);
    }
  };

  // Keep ref updated for voice recognition stale closure fix
  useEffect(() => { handleSendMsgRef.current = handleSendMessage; });

  // ── CRM ACTIONS ──
  const updateOppStatus = async (id, status) => {
    await API.updateOpportunity(id, { status });
    syncOpportunities();
    addNotification(`Deal status updated to ${status}`, 'success');
  };

  const addNote = async () => {
    if (!newNote.trim() || !selectedLead?.contactId) return;
    await API.addNote(selectedLead.contactId, newNote);
    setSelectedLeadNotes(prev => [{ body: newNote, dateAdded: new Date().toISOString() }, ...prev]);
    setNewNote('');
    addNotification('Note added', 'success');
  };

  const createContact = async () => {
    if (!contactForm.firstName) return;
    await API.createContact(contactForm);
    setShowContactForm(false);
    setContactForm({ firstName: '', lastName: '', email: '', phone: '' });
    fetchContacts();
    addNotification('Contact created', 'success');
  };

  const addNotification = (msg, type = 'info') => {
    const id = Date.now();
    setNotifications(prev => [...prev, { id, msg, type }]);
    setTimeout(() => setNotifications(prev => prev.filter(n => n.id !== id)), 4000);
  };

  const filteredOpps = useMemo(() => {
    let opps = crmData.opportunities;
    if (searchQuery && activeTab === 'pipeline') {
      opps = opps.filter(o =>
        (o.name || '').toLowerCase().includes(searchQuery.toLowerCase()) ||
        (o.contact || '').toLowerCase().includes(searchQuery.toLowerCase())
      );
    }
    return opps;
  }, [crmData.opportunities, searchQuery, activeTab]);

  const openTaskCount = tasks.filter(t => t.status !== 'done').length;

  // ── NAV ITEMS ──
  const navItems = [
    { group: 'CORE', items: [
      { id: 'dashboard', icon: 'layout-dashboard', label: 'Dashboard' },
      { id: 'tasks', icon: 'kanban', label: 'Tasks', badge: openTaskCount || null },
      { id: 'contacts', icon: 'users', label: 'Contacts' },
      { id: 'pipeline', icon: 'git-merge', label: 'Pipeline', badge: crmData.stats.totalLeads || null },
      { id: 'calendar', icon: 'calendar-days', label: 'Calendar' },
      { id: 'inbox', icon: 'inbox', label: 'Inbox', badge: conversations.filter(c => c.unreadCount > 0).length || null },
    ]},
    { group: 'INTELLIGENCE', items: [
      { id: 'ai-command', icon: 'brain-circuit', label: 'AI Strategy', accent: true },
      { id: 'action-items', icon: 'bell', label: 'Reminders', badge: actionItems.filter(a=>!a.done).length || null },
      { id: 'action-reference', icon: 'zap', label: 'Action Reference' },
      { id: 'analytics', icon: 'bar-chart-2', label: 'Analytics' },
    ]},
    { group: 'SYSTEM', items: [
      { id: 'settings', icon: 'settings', label: 'Settings' },
    ]}
  ];

  // ── LOADING SCREEN ──
  if (loading) return (
    <div className="h-screen bg-[#030712] flex flex-col items-center justify-center gap-6">
      <div className="relative">
        <div className="w-20 h-20 rounded-2xl border-2 border-blue-500/30 overflow-hidden">
          <img src={JAKE_AVATAR} className="w-full h-full object-cover" />
        </div>
        <div className="absolute -inset-2 rounded-3xl border border-blue-500/20 animate-ping" />
      </div>
      <div className="text-center">
        <div className="text-white font-black text-2xl italic tracking-tighter">Moore AI</div>
        <div className="text-blue-500 text-xs mono mt-1 animate-pulse">INITIALIZING NEURAL CORE...</div>
      </div>
      <div className="w-48 h-0.5 bg-slate-800 rounded-full overflow-hidden">
        <div className="h-full bg-gradient-to-r from-blue-500 to-indigo-500 rounded-full" style={{ width: '60%', animation: 'slideInRight 1s ease infinite alternate' }} />
      </div>
    </div>
  );

  // ── RENDER ──
  return (
    <div className="flex h-screen overflow-hidden text-slate-200" style={{ background: '#030712' }}>
      <audio ref={audioRef} hidden />

      {/* NOTIFICATIONS */}
      <div className="fixed top-4 right-4 z-[9999] flex flex-col gap-2">
        {notifications.map(n => (
          <div key={n.id} className={`slide-in-up px-4 py-3 rounded-xl text-sm font-bold mono flex items-center gap-2 shadow-2xl border ${
            n.type === 'success' ? 'bg-emerald-950 border-emerald-700/50 text-emerald-300' : 'bg-slate-900 border-slate-700 text-white'
          }`}>
            <Icon name={n.type === 'success' ? 'check-circle-2' : 'info'} size={14} />
            {n.msg}
          </div>
        ))}
      </div>

      {/* ========== SIDEBAR ========== */}
      <div className={`${sidebarCollapsed ? 'w-16' : 'w-64'} flex flex-col bg-[#0a0f1a] border-r border-slate-800/60 transition-all duration-300 z-20 shrink-0`}>
        {/* Brand */}
        <div className={`flex items-center ${sidebarCollapsed ? 'justify-center px-2' : 'px-5'} py-5 border-b border-slate-800/40`}>
          <div className="w-9 h-9 rounded-xl border border-blue-500/40 overflow-hidden shrink-0 cursor-pointer" onClick={() => setActiveTab('dashboard')}>
            <img src={JAKE_AVATAR} className="w-full h-full object-cover" />
          </div>
          {!sidebarCollapsed && (
            <div className="ml-3 flex-1 min-w-0">
              <div className="font-black text-white text-lg italic tracking-tighter leading-none">Moore AI</div>
              <div className="text-[9px] mono text-blue-400/70 uppercase tracking-[0.15em]">v13 Enterprise</div>
            </div>
          )}
          {!sidebarCollapsed && (
            <button onClick={() => setSidebarCollapsed(true)} className="text-slate-600 hover:text-slate-400 transition-colors">
              <Icon name="panel-left-close" size={16} />
            </button>
          )}
        </div>

        {/* Nav */}
        <nav className="flex-1 py-4 overflow-y-auto">
          {navItems.map(group => (
            <div key={group.group} className="mb-5">
              {!sidebarCollapsed && (
                <div className="px-5 mb-2 text-[9px] font-black mono text-slate-600 uppercase tracking-[0.2em]">{group.group}</div>
              )}
              {group.items.map(item => {
                const isActive = activeTab === item.id;
                return (
                  <button key={item.id}
                    onClick={() => { setActiveTab(item.id); if (item.id !== 'pipeline') setSelectedLead(null); }}
                    className={`w-full flex items-center ${sidebarCollapsed ? 'justify-center px-2 py-3' : 'px-3 py-2.5 gap-3'} mx-2 rounded-xl transition-all relative
                      ${isActive ? `bg-blue-600/10 text-blue-400 border border-blue-500/20 ${sidebarCollapsed ? '' : 'nav-active'}` : 'text-slate-500 hover:text-slate-300 hover:bg-slate-800/40'}`}
                    style={{ width: 'calc(100% - 16px)' }}
                  >
                    <Icon name={item.icon} size={16} />
                    {!sidebarCollapsed && (
                      <>
                        <span className={`text-sm font-bold flex-1 text-left ${item.accent && !isActive ? 'text-indigo-400' : ''}`}>{item.label}</span>
                        {item.badge > 0 && (
                          <span className="text-[10px] mono bg-blue-600 text-white px-1.5 py-0.5 rounded-md font-bold">{item.badge}</span>
                        )}
                      </>
                    )}
                    {sidebarCollapsed && item.badge > 0 && (
                      <div className="absolute top-1 right-1 w-2 h-2 bg-blue-500 rounded-full" />
                    )}
                  </button>
                );
              })}
            </div>
          ))}
        </nav>

        {sidebarCollapsed && (
          <div className="p-2 border-t border-slate-800/40">
            <button onClick={() => setSidebarCollapsed(false)} className="w-full p-3 flex justify-center text-slate-600 hover:text-slate-400 transition-colors rounded-xl hover:bg-slate-800/40">
              <Icon name="panel-left-open" size={16} />
            </button>
          </div>
        )}

        {!sidebarCollapsed && (
          <div className="p-4 border-t border-slate-800/40">
            <div className="flex items-center gap-2 bg-slate-900/60 rounded-xl px-3 py-2.5">
              <div className="w-2 h-2 rounded-full bg-emerald-500 animate-pulse shrink-0" />
              <div>
                <div className="text-[10px] mono text-white font-bold">GHL CONNECTED</div>
                <div className="text-[9px] mono text-slate-600">Depth: 100 records</div>
              </div>
            </div>
          </div>
        )}
      </div>

      {/* ========== MAIN ========== */}
      <div className="flex-1 flex flex-col overflow-hidden">
        {/* Header */}
        <header className="h-14 border-b border-slate-800/50 flex items-center justify-between px-6 bg-[#030712]/80 backdrop-blur-xl shrink-0">
          <div className="flex items-center gap-3 text-xs mono text-slate-500">
            <Icon name="globe" size={12} className="text-blue-500" />
            <span className="uppercase tracking-[0.2em]">Secure Node // {activeTab.toUpperCase()}</span>
            <span className="text-slate-700">|</span>
            <span className="text-blue-500/70">{new Date().toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric' })}</span>
          </div>

          <div className="flex items-center gap-3">
            {/* Search */}
            <div className="relative">
              <Icon name="search" size={13} className="absolute left-3 top-1/2 -translate-y-1/2 text-slate-600" />
              <input value={searchQuery} onChange={e => setSearchQuery(e.target.value)} placeholder="Search pipeline..."
                className="bg-slate-900/60 border border-slate-800 rounded-lg pl-8 pr-4 py-1.5 text-xs text-slate-300 focus:border-blue-500/50 outline-none w-52 placeholder:text-slate-700 transition-colors" />
            </div>

            {/* Sync */}
            <button onClick={() => { syncOpportunities(); addNotification('Pipeline synced', 'success'); }}
              className="flex items-center gap-1.5 px-3 py-1.5 bg-slate-900/80 border border-slate-800 rounded-lg text-xs text-slate-400 hover:text-white hover:border-slate-600 transition-all group">
              <Icon name="refresh-cw" size={12} className="group-hover:rotate-180 transition-transform duration-500 text-blue-500" />
              <span className="mono">Sync</span>
            </button>

            {/* Theme toggle */}
            <button onClick={() => setTheme(t => t === 'dark' ? 'light' : 'dark')} title="Toggle theme"
              className="p-2 bg-slate-900/60 border border-slate-800 rounded-lg text-slate-400 hover:text-white hover:border-slate-600 transition-all">
              <Icon name={theme === 'dark' ? 'sun' : 'moon'} size={14} />
            </button>

            {/* Avatar */}
            <div className="relative cursor-pointer" onClick={() => { setChatOpen(true); setChatMode('open'); }}>
              <div className="w-8 h-8 rounded-xl border border-blue-500/30 overflow-hidden hover:border-blue-500/60 transition-colors">
                <img src={JAKE_AVATAR} className="w-full h-full object-cover" />
              </div>
              <div className="absolute -bottom-0.5 -right-0.5 w-2.5 h-2.5 bg-emerald-500 rounded-full border-2 border-[#030712]" />
            </div>
          </div>
        </header>

        {/* Tab Content */}
        <main className="flex-1 overflow-y-auto relative" style={{ background: '#030712' }}>
          <ErrorBoundary>
            {activeTab === 'dashboard' && <DashboardView crmData={crmData} setActiveTab={setActiveTab} setSelectedLead={setSelectedLead} setChatOpen={setChatOpen} setChatMode={setChatMode} calEvents={calEvents} fetchCalendar={fetchCalendar} handleSendMessage={handleSendMessage} actionItems={actionItems} activeCalls={activeCalls} />}
            {activeTab === 'tasks' && <TasksView tasks={tasks} setTasks={setTasks} />}
            {activeTab === 'pipeline' && <PipelineView crmData={crmData} filteredOpps={filteredOpps} pipelines={pipelines} pipelineView={pipelineView} setPipelineView={setPipelineView} pipelineFilter={pipelineFilter} setPipelineFilter={setPipelineFilter} setSelectedLead={setSelectedLead} updateOppStatus={updateOppStatus} searchQuery={searchQuery} setSearchQuery={setSearchQuery} syncOpportunities={syncOpportunities} dragItemRef={dragItemRef} selectedPipelineId={selectedPipelineId} setSelectedPipelineId={setSelectedPipelineId} />}
            {activeTab === 'contacts' && <ContactsView contacts={contacts} loadingTabs={loadingTabs} fetchContacts={fetchContacts} setSelectedContact={setSelectedContact} showContactForm={showContactForm} setShowContactForm={setShowContactForm} contactForm={contactForm} setContactForm={setContactForm} createContact={createContact} />}
            {activeTab === 'calendar' && <CalendarView calEvents={calEvents} calView={calView} setCalView={setCalView} calDate={calDate} setCalDate={setCalDate} loadingTabs={loadingTabs} fetchCalendar={fetchCalendar} calendars={calendars} selectedCalendarId={selectedCalendarId} setSelectedCalendarId={setSelectedCalendarId} />}
            {activeTab === 'inbox' && <InboxView conversations={conversations} loadingTabs={loadingTabs} fetchConversations={fetchConversations} />}
            {activeTab === 'ai-command' && <AICommandView messages={messages} isTyping={isTyping} chatInput={chatInput} setChatInput={setChatInput} handleSendMessage={handleSendMessage} toggleListening={toggleListening} isListening={isListening} chatEndRef={chatEndRef} jakeAvatar={JAKE_AVATAR} crmData={crmData} isVoiceEnabled={isVoiceEnabled} toggleMute={() => { const nv = !isVoiceEnabled; setIsVoiceEnabled(nv); isVoiceEnabledRef.current = nv; stopAudio(); }} liveTranscript={liveTranscript} activeCalls={activeCalls} approveActions={approveActions} cancelActions={cancelActions} />}
            {activeTab === 'analytics' && <AnalyticsView analytics={analytics} crmData={crmData} loadingTabs={loadingTabs} fetchAnalytics={fetchAnalytics} />}
            {activeTab === 'action-items' && <ActionItemsView actionItems={actionItems} setActionItems={setActionItems} crmData={crmData} setActiveTab={setActiveTab} handleSendMessage={handleSendMessage} activeCalls={activeCalls} />}
            {activeTab === 'action-reference' && <ActionReferenceView />}
            {activeTab === 'settings' && <SettingsView />}
          </ErrorBoundary>
        </main>
      </div>

      {/* ========== LEAD DETAIL PANEL ========== */}
      {selectedLead && (
        <div className="fixed inset-y-0 right-0 w-[440px] bg-[#0a0f1a] border-l border-slate-800/60 z-[200] flex flex-col slide-in-right shadow-2xl">
          <div className="p-6 border-b border-slate-800/50 flex items-start justify-between">
            <div className="flex items-center gap-4">
              <Avatar name={selectedLead.name} size={48} />
              <div>
                <div className="text-xs mono text-blue-400 uppercase tracking-wider mb-0.5">Lead Detail</div>
                <div className="font-black text-white text-xl italic tracking-tight leading-tight">{selectedLead.name}</div>
                <div className="text-sm text-slate-500">{selectedLead.contact}</div>
              </div>
            </div>
            <button onClick={() => setSelectedLead(null)} className="p-2 hover:bg-slate-800 rounded-lg transition-colors">
              <Icon name="x" size={16} className="text-slate-400" />
            </button>
          </div>

          <div className="flex-1 overflow-y-auto p-6 space-y-6">
            <div className="grid grid-cols-2 gap-3">
              {[
                { label: 'Value', value: fmt$(selectedLead.value) },
                { label: 'Status', value: <StatusBadge status={selectedLead.status} /> },
                { label: 'Stage', value: selectedLead.stageName || '—' },
                { label: 'Source', value: selectedLead.source || 'Direct' },
              ].map((s, i) => (
                <div key={i} className="bg-slate-900/60 border border-slate-800/50 rounded-xl p-3">
                  <div className="text-[10px] mono text-slate-600 uppercase tracking-wider mb-1">{s.label}</div>
                  <div className="text-sm font-bold text-white">{s.value}</div>
                </div>
              ))}
            </div>

            <div className="bg-slate-900/40 border border-slate-800/50 rounded-xl p-4 space-y-3">
              <div className="text-[10px] mono text-slate-500 uppercase tracking-widest font-bold">Contact Info</div>
              {selectedLead.email && (
                <div className="flex items-center gap-3 text-sm">
                  <Icon name="mail" size={13} className="text-slate-600 shrink-0" />
                  <a href={`mailto:${selectedLead.email}`} className="text-blue-400 hover:text-blue-300 mono text-xs truncate">{selectedLead.email}</a>
                </div>
              )}
              {selectedLead.phone && (
                <div className="flex items-center gap-3 text-sm">
                  <Icon name="phone" size={13} className="text-slate-600 shrink-0" />
                  <a href={`tel:${selectedLead.phone}`} className="text-blue-400 hover:text-blue-300 mono text-xs">{selectedLead.phone}</a>
                </div>
              )}
              <div className="flex items-center gap-3 text-sm">
                <Icon name="hash" size={13} className="text-slate-600 shrink-0" />
                <span className="mono text-[10px] text-slate-600 truncate">{selectedLead.id}</span>
              </div>
            </div>

            <div className="space-y-2">
              <div className="text-[10px] mono text-slate-500 uppercase tracking-widest font-bold">Quick Actions</div>
              <div className="grid grid-cols-3 gap-2">
                {[
                  { label: 'Mark Won', icon: 'trophy', color: '#10b981', action: () => updateOppStatus(selectedLead.id, 'won') },
                  { label: 'Mark Lost', icon: 'x-circle', color: '#ef4444', action: () => updateOppStatus(selectedLead.id, 'lost') },
                  { label: 'AI Analyze', icon: 'brain-circuit', color: '#8b5cf6', action: () => { setChatOpen(true); setChatMode('open'); handleSendMessage(null, `Analyze this lead: ${selectedLead.name}. Value: $${selectedLead.value}. Status: ${selectedLead.status}. Give me a 3-point strategic recommendation.`); } },
                ].map((btn, i) => (
                  <button key={i} onClick={btn.action}
                    className="flex flex-col items-center gap-1.5 p-3 bg-slate-900/60 border border-slate-800/50 rounded-xl hover:border-slate-600 transition-all text-center group">
                    <Icon name={btn.icon} size={15} style={{ color: btn.color }} />
                    <span className="text-[10px] font-bold text-slate-400 group-hover:text-white leading-tight">{btn.label}</span>
                  </button>
                ))}
              </div>
            </div>

            <div className="space-y-3">
              <div className="text-[10px] mono text-slate-500 uppercase tracking-widest font-bold">Notes</div>
              <div className="flex gap-2">
                <input value={newNote} onChange={e => setNewNote(e.target.value)}
                  onKeyDown={e => e.key === 'Enter' && addNote()}
                  placeholder="Add a note..."
                  className="flex-1 bg-slate-900/60 border border-slate-800 rounded-lg px-3 py-2 text-xs text-white outline-none focus:border-blue-500/50 placeholder:text-slate-700" />
                <button onClick={addNote} className="px-3 py-2 bg-blue-600 rounded-lg text-xs text-white hover:bg-blue-500 transition-colors font-bold">Add</button>
              </div>
              <div className="space-y-2 max-h-48 overflow-y-auto">
                {selectedLeadNotes.length === 0 ? (
                  <div className="text-xs text-slate-700 mono text-center py-4">No notes yet</div>
                ) : selectedLeadNotes.map((note, i) => (
                  <div key={i} className="bg-slate-900/40 border border-slate-800/40 rounded-lg p-3">
                    <div className="text-xs text-slate-400 leading-relaxed">{note.body}</div>
                    <div className="text-[10px] mono text-slate-700 mt-1">{fmtDate(note.dateAdded || note.createdAt)}</div>
                  </div>
                ))}
              </div>
            </div>
          </div>
        </div>
      )}

      {/* ========== FLOATING AI CHAT ========== */}
      <div className="fixed bottom-6 right-6 z-[300] flex flex-col items-end gap-4">
        {chatOpen && (
          <div className={`${chatMode === 'fullscreen' ? 'w-[600px] h-[80vh]' : 'w-[420px] h-[560px]'} bg-[#0a0f1a] border border-slate-800/80 rounded-2xl shadow-[0_30px_80px_rgba(0,0,0,0.8)] overflow-hidden flex flex-col slide-in-up`}>
            <div className="px-5 py-4 bg-gradient-to-r from-blue-700/80 to-indigo-700/80 flex items-center justify-between shrink-0 border-b border-white/10">
              <div className="flex items-center gap-3">
                <div className="w-10 h-10 rounded-xl overflow-hidden border border-white/20 shrink-0">
                  <img src={JAKE_AVATAR} className="w-full h-full object-cover" />
                </div>
                <div>
                  <div className="font-black text-white text-sm italic tracking-tight leading-none">Jake Moore</div>
                  <div className="flex items-center gap-1.5 mt-0.5">
                    <div className="w-1.5 h-1.5 bg-emerald-400 rounded-full animate-pulse" />
                    <span className="text-[9px] mono text-emerald-300 uppercase tracking-[0.15em]">Neural Architect Online</span>
                  </div>
                </div>
              </div>
              <div className="flex items-center gap-2">
                <button onClick={() => setChatMode(chatMode === 'fullscreen' ? 'open' : 'fullscreen')} className="p-1.5 hover:bg-white/10 rounded-lg transition-colors">
                  <Icon name={chatMode === 'fullscreen' ? 'minimize-2' : 'maximize-2'} size={13} className="text-white/60" />
                </button>
                <button onClick={() => { setChatOpen(false); setIsListening(false); recognitionRef.current?.stop(); }} className="p-1.5 hover:bg-white/10 rounded-lg transition-colors">
                  <Icon name="x" size={14} className="text-white/70" />
                </button>
              </div>
            </div>

            <div className="flex-1 overflow-y-auto p-5 space-y-4 bg-[#030712]/60">
              {messages.map((m, i) => {
                if (m.type === 'action-approval') {
                  return (
                    <div key={i} className="flex gap-3 justify-start">
                      <div className="w-8 h-8 rounded-xl overflow-hidden shrink-0 mt-1 border border-slate-700/60">
                        <img src={JAKE_AVATAR} className="w-full h-full object-cover" />
                      </div>
                      <ActionApprovalCard m={m} approveActions={approveActions} cancelActions={cancelActions} />
                    </div>
                  );
                }
                return (
                  <div key={i} className={`flex gap-3 ${m.role === 'user' ? 'justify-end' : 'justify-start'}`}>
                    {m.role === 'ai' && (
                      <div className="w-8 h-8 rounded-xl overflow-hidden shrink-0 mt-1 border border-slate-700/60">
                        <img src={JAKE_AVATAR} className="w-full h-full object-cover" />
                      </div>
                    )}
                    <div className={`max-w-[82%] px-4 py-3 rounded-2xl text-sm leading-relaxed
                      ${m.role === 'user' ? 'bg-blue-600/80 text-white rounded-tr-md'
                        : 'bg-slate-800/80 text-slate-200 border border-slate-700/40 rounded-tl-md ai-prose'}`}>
                      {m.role === 'ai' && window.marked
                        ? <div dangerouslySetInnerHTML={{ __html: window.marked.parse(m.text) }} />
                        : m.text
                      }
                    </div>
                  </div>
                );
              })}
              {isTyping && (
                <div className="flex gap-3">
                  <div className="w-8 h-8 rounded-xl overflow-hidden shrink-0 border border-slate-700/60">
                    <img src={JAKE_AVATAR} className="w-full h-full object-cover" />
                  </div>
                  <div className="bg-slate-800/80 border border-slate-700/40 rounded-2xl rounded-tl-md px-4 py-3 flex gap-1.5 items-center">
                    {[0, 150, 300].map(d => (
                      <div key={d} className="w-2 h-2 bg-blue-500 rounded-full animate-bounce" style={{ animationDelay: `${d}ms` }} />
                    ))}
                  </div>
                </div>
              )}
              <div ref={chatEndRef} />
            </div>

            <div className="p-4 border-t border-slate-800/50 bg-[#0a0f1a] space-y-2 shrink-0">
              {isListening && liveTranscript && (
                <div className="px-3 py-2 bg-blue-600/10 border border-blue-500/20 rounded-lg text-xs text-blue-300/80 italic">
                  🎤 {liveTranscript}
                </div>
              )}
              <form onSubmit={handleSendMessage} className="flex gap-2">
                <input value={chatInput} onChange={e => setChatInput(e.target.value)}
                  placeholder={isListening ? '🎤 Listening — speak now...' : 'Command Jake...'}
                  className="flex-1 rounded-xl px-4 py-2.5 text-sm font-medium outline-none transition-all bg-slate-900/80 border border-slate-700/60 text-white placeholder:text-slate-600 focus:border-blue-500/50" />
                <button type="submit" disabled={!chatInput.trim()}
                  className="bg-blue-600 hover:bg-blue-500 disabled:opacity-40 w-10 h-10 rounded-xl flex items-center justify-center transition-all btn-glow shrink-0">
                  <Icon name="send" size={15} className="text-white" />
                </button>
              </form>
              <button onClick={toggleListening}
                className={`w-full py-2.5 rounded-xl text-xs font-bold mono uppercase tracking-widest flex items-center justify-center gap-2 transition-all relative overflow-hidden
                  ${isListening ? 'bg-red-600/90 text-white border border-red-500' : 'bg-slate-900/60 border border-slate-700/60 text-slate-400 hover:text-blue-400 hover:border-blue-500/30'}`}>
                {isListening && <span className="absolute inset-0 bg-red-500/20 sonar-ring rounded-xl" />}
                <Icon name={isListening ? 'mic-off' : 'mic'} size={13} />
                {isListening ? '■ Stop — auto-sends in 2s' : '⬤ Voice Command'}
              </button>
            </div>
          </div>
        )}

        <button onClick={() => { setChatOpen(!chatOpen); if (!chatOpen) setChatMode('open'); }}
          className={`w-14 h-14 rounded-2xl overflow-hidden border-2 transition-all shadow-[0_8px_32px_rgba(0,0,0,0.6)] relative
            ${chatOpen ? 'border-blue-500 scale-95' : 'border-blue-500/30 hover:border-blue-500/60 btn-glow'}`}>
          <img src={JAKE_AVATAR} className="w-full h-full object-cover" />
          <div className="absolute bottom-1 right-1 w-3 h-3 bg-emerald-500 rounded-full border-2 border-[#030712] animate-pulse" />
        </button>
      </div>
    </div>
  );
};

// =========================================================
// MOUNT
// =========================================================
const container = document.getElementById('root');
const root = ReactDOM.createRoot(container);
root.render(<App />);
