The file /var/www/mssql.danmarcrm.com/dev1/extension2v1/public/assets/app.js has been updated successfully.

                        <button class="btn btn-sm btn-enrich" data-email="${escapeHtml(contact.email)}" data-contact-id="${contact.id}" onclick="event.stopPropagation(); enrichContactFromList(this);">
                            🔬 Enrich
                        </button>
                        <button class="btn btn-sm" style="color:#ef4444;border:1px solid #ef4444;background:none;margin-left:4px;" onclick="event.stopPropagation(); deleteContact(${contact.id}, '${escapeHtml(contact.name || contact.email).replace(/'/g, "\\'")}');" title="Delete contact and all data">
                            Del
                        </button>

/**
 * Email Intelligence Dashboard JavaScript
 *
 * Handles all frontend interactions including:
 * - Tab switching
 * - Data loading via API
 * - Modal management
 * - Filter management
 * - Sync/Analysis triggers
 *
 * CALLING MAP:
 * - Loaded by: index.php
 * - Calls: api.php for all data operations
 *
 * @package EmailIntelligence
 */

// ============================================
// Global State
// ============================================
const state = {
    contacts: {
        page: 1,
        search: '',
        type: ''
    },
    emails: {
        page: 1,
        contactId: null
    }
};

// ============================================
// Utility Functions (defined early for global access)
// ============================================
/**
 * Format number with commas
 * Defined in global scope for early access
 */
window.formatNumber = function(num) {
    if (num === null || num === undefined) return '0';
    return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
};

// Also define as regular function for compatibility
function formatNumber(num) {
    if (num === null || num === undefined) return '0';
    return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}

/**
 * Escape HTML to prevent XSS
 */
function escapeHtml(text) {
    if (!text) return '';
    const div = document.createElement('div');
    div.textContent = text;
    return div.innerHTML;
}

// ============================================
// Tab Management
// ============================================
function switchTab(tabName) {
    // Update tab buttons
    document.querySelectorAll('.tab-btn').forEach(btn => {
        btn.classList.toggle('active', btn.dataset.tab === tabName);
    });

    // Update tab content
    document.querySelectorAll('.tab-content').forEach(content => {
        content.classList.toggle('active', content.id === 'tab-' + tabName);
    });

    // Load data for the tab
    switch(tabName) {
        case 'contacts':
            loadContacts();
            break;
        case 'emails':
            loadEmails();
            break;
        case 'gmail-accounts':
            loadGmailAccounts();
            break;
        case 'patterns':
            loadPatternStats();
            break;
        case 'business':
            loadBusinessOpportunities();
            break;
        case 'alerts':
            loadAlerts();
            break;
        case 'quotes':
            loadQuotes();
            break;
        case 'filters':
            loadFilters();
            break;
        case 'business-ideas':
            loadBusinessIdeasProjects();
            loadOwnerStatus();
            break;
    }
}

// ============================================
// API Helpers
// ============================================
async function apiCall(action, params = {}, method = 'GET') {
    try {
        let url = 'api.php?action=' + action;

        const options = {
            method: method,
            headers: {}
        };

        if (method === 'GET') {
            // Add params to URL
            Object.keys(params).forEach(key => {
                url += '&' + encodeURIComponent(key) + '=' + encodeURIComponent(params[key]);
            });
        } else {
            // POST data
            const formData = new FormData();
            Object.keys(params).forEach(key => {
                formData.append(key, params[key]);
            });
            options.body = formData;
        }

        const response = await fetch(url, options);
        const data = await response.json();

        if (data.error) {
            throw new Error(data.message || 'API Error');
        }

        return data;
    } catch (error) {
        console.error('API Error:', error);
        throw error;
    }
}

// ============================================
// Loading Overlay
// ============================================
function showLoading(message = 'Processing...') {
    document.getElementById('loading-message').textContent = message;
    document.getElementById('loading-overlay').style.display = 'flex';
}

function hideLoading() {
    document.getElementById('loading-overlay').style.display = 'none';
}

// ============================================
// Contacts
// ============================================
async function loadContacts(page = 1) {
    const container = document.getElementById('contacts-list');
    container.innerHTML = '
Loading contacts...
';

    try {
        state.contacts.page = page;
        state.contacts.search = document.getElementById('contact-search')?.value || '';
        state.contacts.type = document.getElementById('contact-type-filter')?.value || '';

        const data = await apiCall('contacts', {
            page: page,
            search: state.contacts.search,
            type: state.contacts.type,
            limit: 10
        });

        if (data.contacts.length === 0) {
            container.innerHTML = '
No contacts found
';
            return;
        }

        let html = `

                        Contact
                        Type
                        Emails
                        Relationship
                        Risk
                        Last Contact
                        Actions

        `;

        data.contacts.forEach(contact => {
            const riskClass = contact.risk_level ? 'badge-' + contact.risk_level : 'badge-low';
            const relClass = contact.relationship_health ? 'badge-' + contact.relationship_health.replace('_', '-') : 'badge-new';
            const enrichedBadge = contact.enrichment_status === 'complete' ? '✓' : '';

            html += `

                        ${escapeHtml(contact.name || contact.email)} ${enrichedBadge}
                        ${contact.name ? '
' + escapeHtml(contact.email) + '' : ''}
                        ${contact.company ? '
' + escapeHtml(contact.company) + '' : ''}

                    ${contact.type}
                    ${contact.total_emails || 0}
                    ${contact.relationship_health || 'new'}
                    ${contact.risk_level || 'low'}
                    ${contact.last_contact ? formatDate(contact.last_contact) : '-'}

                        <button class="btn btn-sm btn-enrich" data-email="${escapeHtml(contact.email)}" data-contact-id="${contact.id}" onclick="event.stopPropagation(); enrichContactFromList(this);">
                            🔬 Enrich
                        </button>

            `;
        });

        html += '

';
        container.innerHTML = html;

        // Pagination
        renderPagination('contacts-pagination', data.pagination, loadContacts);

    } catch (error) {
        container.innerHTML = '
Error loading contacts: ' + escapeHtml(error.message) + '
';
    }
}

async function showContact(contactId) {
    showLoading('Loading contact...');

    try {
        const data = await apiCall('contact', { id: contactId });
        const contact = data.contact;

        let html = `

${escapeHtml(contact.name || contact.email)}

${escapeHtml(contact.email)}

                    ${contact.company ? '
' + escapeHtml(contact.company) + '
' : ''}

                    ${contact.type}
                    ${contact.risk_level ? '' + contact.risk_level + ' risk' : ''}

        `;

        // Profile metrics
        html += `

Profile Metrics

                        Relationship

${contact.relationship_health || 'New'}

                        Reliability Score

${contact.reliability_score ? Math.round(contact.reliability_score) + '%' : 'N/A'}

                        Pricing Sensitivity

${contact.pricing_sensitivity || 'Unknown'}

                        Avg Response Time

${contact.avg_response_time_hours ? Math.round(contact.avg_response_time_hours) + 'h' : 'N/A'}

                        Total Emails

${contact.total_emails || 0}

                        Communication Style

${contact.communication_style || 'Unknown'}

        `;

        // Psychological profile
        if (contact.psychological_profile) {
            html += `

Psychological Profile

${escapeHtml(contact.psychological_profile)}

            `;
        }

        // AI Recommendations
        if (contact.ai_recommendations) {
            html += `

AI Recommendations

${escapeHtml(contact.ai_recommendations)}

            `;
        }

        // Detected inconsistencies
        if (contact.detected_inconsistencies && contact.detected_inconsistencies.length > 0) {
            html += `

Detected Concerns (${contact.inconsistency_count || contact.detected_inconsistencies.length})

            `;
            contact.detected_inconsistencies.slice(0, 5).forEach(item => {
                html += `<li>${escapeHtml(typeof item === 'string' ? item : item.flag || JSON.stringify(item))}</li>`;
            });
            html += '

';
        }

        // Recent emails
        if (data.recent_emails && data.recent_emails.length > 0) {
            html += `

Recent Emails

            `;
            data.recent_emails.slice(0, 10).forEach(email => {
                const sentimentClass = email.sentiment_label ? 'badge-' + email.sentiment_label : '';
                html += `

                            ${email.direction}

                            ${escapeHtml(email.subject || '(No Subject)')}
                            ${email.summary ? '
' + escapeHtml(email.summary.substring(0, 100)) + '...' : ''}

                        ${formatDate(email.date)}

                `;
            });
            html += '

';
        }

        // Actions
        html += `

                <button class="btn btn-primary" onclick="analyzeContact(${contact.id})" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);">
                    🧠 Full AI Analysis (All Threads)
                </button>
                <button class="btn btn-small" onclick="showContactProfile(${contact.id})">View Stored Profile</button>
                <button class="btn btn-small" onclick="viewContactEmails(${contact.id})">View All Threads</button>
                <button class="btn btn-small" onclick="buildProfile(${contact.id})">Rebuild Legacy Profile</button>

        `;

        document.getElementById('contact-detail').innerHTML = html;
        openModal('contact-modal');

    } catch (error) {
        alert('Error loading contact: ' + error.message);
    } finally {
        hideLoading();
    }
}

function viewContactEmails(contactId) {
    closeModal('contact-modal');
    state.emails.contactId = contactId;
    switchTab('emails');
    loadEmails(1, contactId);
}

async function buildProfile(contactId) {
    if (!confirm('Rebuild profile for this contact? This may take a moment.')) return;

    showLoading('Building profile...');

    try {
        await apiCall('build_profile', { contact_id: contactId }, 'POST');
        alert('Profile rebuilt successfully!');
        showContact(contactId);
    } catch (error) {
        alert('Error building profile: ' + error.message);
    } finally {
        hideLoading();
    }
}

// ============================================
// Contact Multi-Thread Analysis
// ============================================
async function analyzeContact(contactId) {
    if (!confirm('Run comprehensive AI analysis on ALL communication with this contact?\n\nThis will analyze every email thread together to build a complete psychological and business profile.\n\nThis may take 2-5 minutes depending on the number of threads.')) return;

    showLoading('Analyzing all threads for this contact... This may take several minutes.');
    closeModal('contact-modal');

    try {
        const data = await apiCall('analyze_contact', { contact_id: contactId, include_raw: true }, 'POST');

        if (data.success) {
            alert(`Contact Analysis Complete!\n\n${data.message}\n\nVerdict: ${data.analysis?.verdict?.client_quality?.toUpperCase() || 'N/A'}\nRecommendation: ${data.analysis?.verdict?.recommended_action?.toUpperCase() || 'N/A'}`);
            // Show the full profile
            showContactProfile(contactId);
        } else {
            alert('Analysis failed: ' + (data.error || 'Unknown error'));
        }
    } catch (error) {
        alert('Analysis error: ' + error.message);
    } finally {
        hideLoading();
    }
}

async function showContactProfile(contactId) {
    showLoading('Loading contact profile...');

    try {
        const data = await apiCall('contact_profile', { contact_id: contactId });

        if (!data.has_analysis) {
            alert('No comprehensive analysis found for this contact.\n\nClick "Full AI Analysis" to analyze all threads.');
            hideLoading();
            return;
        }

        const contact = data.contact;
        const analysis = contact.full_analysis;
        const stats = data.thread_stats;

        let html = `

${escapeHtml(contact.name || contact.email)}

${escapeHtml(contact.email)} ${contact.company ? '| ' + escapeHtml(contact.company) : ''}

                    📧 ${stats.total_emails || 0} emails
                    💬 ${stats.total_threads || 0} threads
                    ✅ ${stats.analyzed_threads || 0} analyzed
                    🔢 ${contact.tokens_used || 0} tokens used

        `;

        // Executive Summary
        if (analysis?.executive_summary) {
            html += `

📋 Executive Summary

${escapeHtml(analysis.executive_summary)}

            `;
        }

        // Verdict Box
        if (analysis?.verdict) {
            const v = analysis.verdict;
            const qualityColor = v.client_quality === 'excellent' ? '#22c55e' : v.client_quality === 'good' ? '#84cc16' : v.client_quality === 'average' ? '#f59e0b' : v.client_quality === 'poor' ? '#ef4444' : '#dc2626';
            const actionColor = v.recommended_action === 'grow' ? '#22c55e' : v.recommended_action === 'maintain' ? '#3b82f6' : v.recommended_action === 'reduce' ? '#f59e0b' : '#ef4444';

            html += `

Client Quality

${(v.client_quality || 'N/A').toUpperCase()}

Recommended Action

${(v.recommended_action || 'N/A').toUpperCase()}

Confidence

${Math.round((v.confidence_level || 0) * 100)}%

            `;

            if (v.one_line_summary) {
                html += `<p style="font-style: italic; text-align: center; color: #666; margin-bottom: 20px;">"${escapeHtml(v.one_line_summary)}"</p>`;
            }
        }

        // Trust & Deception Scores
        if (analysis?.trust_assessment || analysis?.deception_analysis) {
            const trust = analysis.trust_assessment || {};
            const deception = analysis.deception_analysis || {};

            html += `

Trust Score

= 0.7 ? '#22c55e' : (trust.overall_trust_score || 0) >= 0.4 ? '#f59e0b' : '#ef4444'};">${Math.round((trust.overall_trust_score || 0) * 100)}%

Deception Score

                        <div style="font-size: 2em; font-weight: bold; color: ${(deception.overall_deception_score || 0) <= 0.3 ? '#22c55e' : (deception.overall_deception_score || 0) <= 0.6 ? '#f59e0b' : '#ef4444'};">${Math.round((deception.overall_deception_score || 0) * 100)}%

Reliability

= 0.7 ? '#22c55e' : (trust.reliability_score || 0) >= 0.4 ? '#f59e0b' : '#ef4444'};">${Math.round((trust.reliability_score || 0) * 100)}%

Honesty

= 0.7 ? '#22c55e' : (trust.honesty_score || 0) >= 0.4 ? '#f59e0b' : '#ef4444'};">${Math.round((trust.honesty_score || 0) * 100)}%

            `;
        }

        // Key Insights
        if (analysis?.key_insights && analysis.key_insights.length > 0) {
            html += `

💡 Key Insights

                        ${analysis.key_insights.map(i => `<li style="margin-bottom: 8px;">${escapeHtml(i)}</li>`).join('')}

            `;
        }

        // Warnings
        if (analysis?.warnings && analysis.warnings.length > 0) {
            html += `

⚠ Warnings

                        ${analysis.warnings.map(w => `<li style="margin-bottom: 8px; color: #dc2626; font-weight: 500;">${escapeHtml(w)}</li>`).join('')}

            `;
        }

        // Deception Analysis
        if (analysis?.deception_analysis) {
            const da = analysis.deception_analysis;
            html += `

🕵 Deception Analysis

                    ${da.lie_patterns && da.lie_patterns.length > 0 ? `<p><strong>Lie Patterns:</strong> ${da.lie_patterns.map(p => escapeHtml(p)).join(', ')}</p>` : ''}
                    ${da.story_changes && da.story_changes.length > 0 ? `<p><strong>Story Changes:</strong> ${da.story_changes.map(s => escapeHtml(s)).join(', ')}</p>` : ''}
                    ${da.manipulation_tactics_used && da.manipulation_tactics_used.length > 0 ? `<p><strong>Manipulation Tactics:</strong> ${da.manipulation_tactics_used.map(t => escapeHtml(t)).join(', ')}</p>` : ''}
                    ${da.information_hiding && da.information_hiding.length > 0 ? `<p><strong>Information Hiding:</strong> ${da.information_hiding.map(h => escapeHtml(h)).join(', ')}</p>` : ''}

            `;
        }

        // Business Risk Profile
        if (analysis?.business_risk_profile) {
            const br = analysis.business_risk_profile;
            html += `

📊 Business Risk Profile

Overall Risk Level: ${(br.overall_risk_level || 'unknown').toUpperCase()}

                    ${br.payment_risk ? `<p><strong>Payment Risk:</strong> ${Math.round((br.payment_risk.score || 0) * 100)}% - ${br.payment_risk.history || 'No history'}</p>` : ''}
                    ${br.project_risk ? `<p><strong>Project Risk:</strong> ${Math.round((br.project_risk.score || 0) * 100)}% | Scope Creep: ${br.project_risk.scope_creep_tendency || 'N/A'}</p>` : ''}
                    ${br.competitive_risk ? `<p><strong>Competitive Risk:</strong> ${Math.round((br.competitive_risk.score || 0) * 100)}% | Loyalty: ${br.competitive_risk.loyalty_level || 'N/A'}</p>` : ''}

            `;
        }

        // Psychological Profile
        if (analysis?.psychological_profile) {
            const pp = analysis.psychological_profile;
            html += `

🧠 Psychological Profile

Personality: ${pp.personality_type || 'N/A'}

Communication: ${pp.communication_style || 'N/A'}

Decision Making: ${pp.decision_making_pattern || 'N/A'}

Negotiation: ${pp.negotiation_style || 'N/A'}

                    ${pp.motivations && pp.motivations.length > 0 ? `<p style="margin-top: 10px;"><strong>Motivations:</strong> ${pp.motivations.join(', ')}</p>` : ''}
                    ${pp.fears_concerns && pp.fears_concerns.length > 0 ? `<p><strong>Fears/Concerns:</strong> ${pp.fears_concerns.join(', ')}</p>` : ''}

            `;
        }

        // Recommended Approach
        if (analysis?.recommended_approach) {
            const ra = analysis.recommended_approach;
            html += `

📝 Recommended Approach

                    ${ra.communication_strategy ? `<p><strong>Communication:</strong> ${escapeHtml(ra.communication_strategy)}</p>` : ''}
                    ${ra.pricing_strategy ? `<p><strong>Pricing:</strong> ${escapeHtml(ra.pricing_strategy)}</p>` : ''}
                    ${ra.negotiation_tips && ra.negotiation_tips.length > 0 ? `<p><strong>Negotiation Tips:</strong></p><ul style="margin: 0; padding-left: 20px;">${ra.negotiation_tips.map(t => `
${escapeHtml(t)}
`).join('')}</ul>` : ''}
                    ${ra.topics_to_avoid && ra.topics_to_avoid.length > 0 ? `<p><strong>Topics to Avoid:</strong> ${ra.topics_to_avoid.map(t => escapeHtml(t)).join(', ')}</p>` : ''}

            `;
        }

        // Opportunities
        if (analysis?.opportunities && analysis.opportunities.length > 0) {
            html += `

🎯 Opportunities

                        ${analysis.opportunities.map(o => `<li>${escapeHtml(o)}</li>`).join('')}

            `;
        }

        // Analysis metadata
        html += `

                Last analyzed: ${contact.analyzed_at ? formatDateTime(contact.analyzed_at) : 'Never'} |
                Threads: ${contact.threads_analyzed || 0} | Emails: ${contact.emails_analyzed || 0} | Tokens: ${contact.tokens_used || 0}

        `;

        // Actions
        html += `

                <button class="btn btn-primary" onclick="analyzeContact(${contactId})" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);">
                    🔄 Re-analyze Contact
                </button>
                <button class="btn btn-small" onclick="viewContactEmails(${contactId})">View Threads</button>

        `;

        document.getElementById('contact-detail').innerHTML = html;
        openModal('contact-modal');

    } catch (error) {
        alert('Error loading profile: ' + error.message);
    } finally {
        hideLoading();
    }
}

// ============================================
// Email Threads (grouped conversations)
// ============================================
async function loadEmails(page = 1, contactId = null) {
    const container = document.getElementById('emails-list');
    container.innerHTML = '
Loading threads...
';

    try {
        state.emails.page = page;
        if (contactId !== null) state.emails.contactId = contactId;

        const params = { page: page, limit: 10 };
        if (state.emails.contactId) params.contact = state.emails.contactId;

        const data = await apiCall('threads', params);

        if (data.threads.length === 0) {
            container.innerHTML = '
No threads found
';
            return;
        }

        let html = `

                💡 Thread View: Each row is a complete conversation (all back-and-forth emails grouped together).
                Click to see what Claude will analyze.

                        Subject
                        Contact
                        Emails
                        Status
                        Last Activity

        `;

        data.threads.forEach(thread => {
            const statusBadge = thread.is_analyzed
                ? (thread.needs_reanalysis ? 'Needs Re-analysis' : 'Analyzed')
                : 'Not Analyzed';

            const emailInfo = `${thread.email_count} (↓${thread.inbound_count} ↑${thread.outbound_count})`;

            html += `

                        ${escapeHtml(thread.subject || '(No Subject)')}

Started: ${formatDate(thread.first_date)}

                    ${escapeHtml(thread.contact_name || 'Unknown')}
                    ${emailInfo}
                    ${statusBadge}
                    ${formatDate(thread.last_date)}

            `;
        });

        html += '

';
        container.innerHTML = html;

        renderPagination('emails-pagination', data.pagination, (p) => loadEmails(p));

    } catch (error) {
        container.innerHTML = '
Error loading threads: ' + escapeHtml(error.message) + '
';
    }
}

async function showThread(threadId) {
    // Redirect to dedicated thread view page with AI capabilities
    window.location.href = 'thread_view.php?thread_id=' + encodeURIComponent(threadId);
}

// Legacy modal-based thread view (kept for reference, no longer used)
async function showThreadModal(threadId) {
    showLoading('Loading thread...');

    try {
        const data = await apiCall('thread', { thread_id: threadId });

        let html = `

Thread: ${escapeHtml(data.emails[0].subject || '(No Subject)')}

                    Thread ID: ${escapeHtml(threadId)}
                    Total Emails: ${data.email_count}
                    Date Range: ${formatDate(data.emails[0].date)} → ${formatDate(data.emails[data.email_count - 1].date)}
                    Status: ${data.status.is_analyzed ? '✓ Analyzed' : '⏳ Not analyzed'}

        `;

        // Show what Claude will see
        html += `

🤖 What Claude AI Will Analyze:

Claude will receive ALL ${data.email_count} emails below in chronological order to analyze the complete conversation.

        `;

        // Thread timeline
        html += '
';
        html += '
Email Timeline (as sent to Claude)
';

        data.emails.forEach((email, index) => {
            const num = index + 1;
            const directionLabel = email.direction === 'inbound' ? 'FROM THEM' : 'FROM US';
            const directionClass = email.direction === 'inbound' ? 'badge-inbound' : 'badge-outbound';

            // Truncate body for display
            let body = email.body_text || email.body_html || '(No content)';
            const isLong = body.length > 500;
            const displayBody = isLong ? body.substring(0, 500) + '...' : body;

            html += `

                        Email #${num}
                        ${directionLabel}
                        ${formatDateTime(email.date)}

${escapeHtml(displayBody)}

                    ${isLong ? '
... (email truncated for display, Claude sees full content)
' : ''}

            `;
        });

        html += '
';

        // Analysis results if available
        if (data.analysis && data.analysis.summary) {
            const analysis = data.analysis;
            // raw_analysis might be already parsed by PHP or still a string
            let rawAnalysis = {};
            if (analysis.raw_analysis) {
                rawAnalysis = typeof analysis.raw_analysis === 'string'
                    ? JSON.parse(analysis.raw_analysis)
                    : analysis.raw_analysis;
            }

            html += `

AI Analysis Results

${escapeHtml(analysis.summary || '')}

                            Sentiment
                            ${analysis.sentiment || 'unknown'}

                            Trust Score
                            = 0.7 ? '#22c55e' : (analysis.trust_score || 0) >= 0.4 ? '#f59e0b' : '#ef4444'}">
                                ${Math.round((analysis.trust_score || 0) * 100)}%

                            Deception Score
                            <span style="font-size: 1.3em; font-weight: bold; color: ${(analysis.deception_score || 0) <= 0.3 ? '#22c55e' : (analysis.deception_score || 0) <= 0.6 ? '#f59e0b' : '#ef4444'}">
                                ${Math.round((analysis.deception_score || 0) * 100)}%

            `;

            // Business Risks
            if (rawAnalysis.business_risks) {
                const risks = rawAnalysis.business_risks;
                html += `

Business Risks

                `;

                if (risks.payment_risk) {
                    const pr = risks.payment_risk;
                    html += `

Payment Risk:  0.6 ? 'high' : pr.score > 0.3 ? 'medium' : 'low'}">${Math.round(pr.score * 100)}%
                        ${pr.payment_delays ? `<br><small>${escapeHtml(pr.payment_delays)}</small>` : ''}
                        ${pr.excuses_used && pr.excuses_used.length > 0 ? `<br><small>Excuses: ${pr.excuses_used.map(e => escapeHtml(e)).join(', ')}</small>` : ''}

                    `;
                }

                if (risks.project_theft_risk) {
                    const pt = risks.project_theft_risk;
                    html += `

Project Theft Risk:  0.6 ? 'high' : pt.score > 0.3 ? 'medium' : 'low'}">${Math.round(pt.score * 100)}%
                        ${pt.knowledge_extraction ? `<br><small>${escapeHtml(pt.knowledge_extraction)}</small>` : ''}

                    `;
                }

                if (risks.relationship_risks && risks.relationship_risks.length > 0) {
                    html += '
Relationship Risks:
';
                    risks.relationship_risks.forEach(r => {
                        html += `<li><strong>${escapeHtml(r.type)}:</strong> ${escapeHtml(r.description)}</li>`;
                    });
                    html += '

';
                }

                html += '

';
            }

            // Deception Indicators
            if (rawAnalysis.deception_indicators) {
                const di = rawAnalysis.deception_indicators;
                html += `

Deception Analysis

Shady Behavior Score: ${Math.round((di.shady_behavior_score || 0) * 100)}%

                        ${di.hiding_information ? `<p><strong>Hiding Information:</strong> ${escapeHtml(di.hiding_information)}</p>` : ''}
                        ${di.manipulation_tactics && di.manipulation_tactics.length > 0 ? `

Manipulation Tactics:

${di.manipulation_tactics.map(t => `<li class="red-flag">${escapeHtml(t)}</li>`).join('')}

                        ` : ''}
                        ${di.red_flags && di.red_flags.length > 0 ? `

Red Flags:

${di.red_flags.map(f => `<li class="red-flag">${escapeHtml(f)}</li>`).join('')}

                        ` : ''}
                        ${di.reasoning ? `<p><strong>Reasoning:</strong> ${escapeHtml(di.reasoning)}</p>` : ''}

                `;
            }

            // Recommendations
            if (rawAnalysis.recommendations) {
                const rec = rawAnalysis.recommendations;
                html += `

Recommendations

                        ${rec.overall_risk_level ? `<p><strong>Overall Risk Level:</strong> <span class="risk-badge risk-${rec.overall_risk_level}">${rec.overall_risk_level.toUpperCase()}</span></p>` : ''}
                        ${rec.client_verdict ? `
Client Verdict: ${rec.client_verdict.toUpperCase()}
` : ''}
                        ${rec.how_to_approach ? `<p><strong>How to Approach:</strong> ${escapeHtml(rec.how_to_approach)}</p>` : ''}
                        ${rec.pricing_strategy ? `<p><strong>Pricing Strategy:</strong> ${escapeHtml(rec.pricing_strategy)}</p>` : ''}
                        ${rec.warnings && rec.warnings.length > 0 ? `

Warnings:

${rec.warnings.map(w => `<li class="red-flag">${escapeHtml(w)}</li>`).join('')}

                        ` : ''}
                        ${rec.next_steps && rec.next_steps.length > 0 ? `

Next Steps:

${rec.next_steps.map(s => `<li>${escapeHtml(s)}</li>`).join('')}

                        ` : ''}

                `;
            }

            // Key Points
            if (analysis.key_points && analysis.key_points.length > 0) {
                html += `

                        Key Points:

                            ${analysis.key_points.map(p => '
' + escapeHtml(p) + '
').join('')}

                `;
            }

            // Per-Email Analysis
            if (rawAnalysis.per_email_analysis && rawAnalysis.per_email_analysis.length > 0) {
                html += `

Per-Email Analysis

                `;
                rawAnalysis.per_email_analysis.forEach(emailAnalysis => {
                    const ea = emailAnalysis;
                    html += `

Email #${ea.email_number} (${ea.direction || 'unknown'})

Summary: ${escapeHtml(ea.summary || '')}

Sentiment: ${ea.sentiment || 'unknown'}

                            ${ea.key_info && ea.key_info.length > 0 ? `<div class="analysis-row"><span class="analysis-label">Key Info:</span> ${ea.key_info.map(i => escapeHtml(i)).join(', ')}</div>` : ''}
                            ${ea.red_flags_in_email && ea.red_flags_in_email.length > 0 ? `<div class="analysis-row"><span class="analysis-label">Red Flags:</span> <span class="red-flag">${ea.red_flags_in_email.map(f => escapeHtml(f)).join(', ')}</span></div>` : ''}
                            ${ea.quotes_mentioned && ea.quotes_mentioned.length > 0 ? `<div class="analysis-row"><span class="analysis-label">Quotes:</span> ${ea.quotes_mentioned.join(', ')}</div>` : ''}

                    `;
                });
                html += '
';
            }

            // Inconsistencies
            if (rawAnalysis.inconsistencies && rawAnalysis.inconsistencies.length > 0) {
                html += `

Inconsistencies Detected

                `;
                rawAnalysis.inconsistencies.forEach(inc => {
                    html += `
${escapeHtml(inc.type || 'Issue')}: ${escapeHtml(inc.description || '')} ${(inc.severity || 'medium').toUpperCase()}
`;
                });
                html += '

';
            }

            html += '
';
        } else if (!data.status.is_analyzed) {
            html += `

This thread has not been analyzed yet.

                    <button class="btn btn-primary mt-20" onclick="analyzeThread('${escapeHtml(threadId)}')">Analyze This Thread Now</button>

            `;
        }

        document.getElementById('email-detail').innerHTML = html;
        openModal('email-modal');

    } catch (error) {
        alert('Error loading thread: ' + error.message);
    } finally {
        hideLoading();
    }
}

async function analyzeThread(threadId) {
    if (!confirm('Analyze this thread with Claude AI?\n\nThis will send all emails in the thread to Claude for comprehensive analysis including deception detection, risk assessment, and psychological profiling.\n\nThis may take 30-90 seconds.')) return;

    showLoading('Analyzing thread with Claude AI... This may take up to 90 seconds.');
    closeModal('email-modal');

    try {
        const data = await apiCall('analyze_thread', { thread_id: threadId }, 'POST');

        if (data.success) {
            alert(`Thread Analyzed Successfully!\n\n${data.summary || 'Analysis complete.'}`);
            refreshAnalysisStatus();
            // Reopen the thread to show results
            showThread(threadId);
        } else {
            alert('Analysis failed: ' + (data.error || 'Unknown error'));
        }
    } catch (error) {
        alert('Analysis error: ' + error.message);
    } finally {
        hideLoading();
    }
}

async function showEmail(emailId) {
    showLoading('Loading email...');

    try {
        const data = await apiCall('email', { id: emailId });
        const email = data.email;

        let html = `

${escapeHtml(email.subject || '(No Subject)')}

                    From: ${escapeHtml(email.from_name || email.from_email)} <${escapeHtml(email.from_email)}>
                    To: ${escapeHtml(email.to_email)}
                    Date: ${formatDateTime(email.date)}
                    Direction: ${email.direction}

${escapeHtml(email.body_text || email.body_html || '(No content)')}

        `;

        // Analysis section
        if (email.sentiment_label || email.summary) {
            html += `

AI Analysis

                            Sentiment

${email.sentiment_label || 'Not analyzed'} ${email.sentiment_score ? '(' + email.sentiment_score + ')' : ''}

                            Tone

${email.tone || '-'}

                            Urgency

${email.urgency_level ? email.urgency_level + '/5' : '-'}

                            Deception Score

 0.5 ? 'score-low' : 'score-high'}">${email.deception_score ? (email.deception_score * 100).toFixed(0) + '%' : '-'}

            `;

            if (email.summary) {
                html += `<div class="profile-text mt-20"><strong>Summary:</strong> ${escapeHtml(email.summary)}</div>`;
            }

            // Key entities
            if (email.key_entities) {
                const entities = email.key_entities;
                if (entities.amounts && entities.amounts.length > 0) {
                    html += '
Amounts mentioned: ';
                    html += entities.amounts.map(a => `${a.value} ${a.currency} (${escapeHtml(a.context || '')})`).join(', ');
                    html += '
';
                }
            }

            // Deception flags
            if (email.deception_flags && email.deception_flags.length > 0) {
                html += '
Concerns detected:
';
                email.deception_flags.forEach(flag => {
                    html += `<li>${escapeHtml(typeof flag === 'string' ? flag : flag.flag || JSON.stringify(flag))}</li>`;
                });
                html += '

';
            }

            html += '
';
        }

        document.getElementById('email-detail').innerHTML = html;
        openModal('email-modal');

    } catch (error) {
        alert('Error loading email: ' + error.message);
    } finally {
        hideLoading();
    }
}

// ============================================
// Quotes
// ============================================
async function loadQuotes() {
    const container = document.getElementById('quotes-list');
    container.innerHTML = '
Loading quotes...
';

    try {
        const outcome = document.getElementById('quote-outcome-filter')?.value || '';
        const data = await apiCall('quotes', { outcome: outcome });

        if (data.quotes.length === 0) {
            container.innerHTML = '
No quotes found
';
            return;
        }

        // Stats summary
        let html = `

                    Total Quotes

${data.stats.total}

                    Accepted

${data.stats.accepted}

                    Rejected

${data.stats.rejected}

                    Pending

${data.stats.pending}

                        Contact
                        Amount
                        Direction
                        Outcome
                        Date

        `;

        data.quotes.forEach(quote => {
            const outcomeClass = {
                'accepted': 'badge-positive',
                'rejected': 'badge-negative',
                'pending': 'badge-neutral',
                'negotiated': 'badge-medium'
            }[quote.outcome] || 'badge-neutral';

            html += `

                    ${escapeHtml(quote.contact_name || quote.contact_email)}
                    ${formatCurrency(quote.amount, quote.currency)}
                        ${quote.final_amount && quote.final_amount !== quote.amount ?
                            '
Final: ' + formatCurrency(quote.final_amount, quote.currency) + '' : ''}

                    ${quote.direction}
                    ${quote.outcome}
                    ${formatDate(quote.quote_date)}

            `;
        });

        html += '

';
        container.innerHTML = html;

    } catch (error) {
        container.innerHTML = '
Error loading quotes: ' + escapeHtml(error.message) + '
';
    }
}

// ============================================
// Business Opportunities
// ============================================
async function loadBusinessOpportunities() {
    const container = document.getElementById('business-opportunities-list');
    container.innerHTML = '
Loading business opportunities...
';

    try {
        // Get recently analyzed threads (analyze all threads, filter client-side)
        const threadsData = await apiCall('threads', { limit: 100 });

        let totalOpps = 0;
        let crossSellCount = 0;
        let quoteRelatedCount = 0;
        let totalValue = 0;
        let threadsProcessed = 0;

        let html = '
';

        for (const thread of threadsData.threads) {
            // Skip threads that haven't been analyzed
            if (!thread.is_analyzed) continue;

            // Get thread analysis
            try {
                const analysisResp = await apiCall('thread', { thread_id: thread.thread_id });
                const analysis = analysisResp.analysis;

                // Only process threads with business_opportunities
                if (!analysis || !analysis.business_opportunities) continue;

                threadsProcessed++;
                const bizOpp = analysis.business_opportunities;

                // Count opportunities
                if (bizOpp.cross_sell_products && bizOpp.cross_sell_products.length > 0) {
                    totalOpps += bizOpp.cross_sell_products.length;
                    crossSellCount += bizOpp.cross_sell_products.length;
                }

                if (bizOpp.quote_analysis && bizOpp.quote_analysis.is_quote_related) {
                    quoteRelatedCount++;
                    if (bizOpp.quote_analysis.estimated_value) {
                        totalValue += parseFloat(bizOpp.quote_analysis.estimated_value);
                    }
                }

                html += `

                                        ${escapeHtml(thread.subject || 'No subject')}

                                    ${escapeHtml(thread.participants_preview || '')}

                        ${bizOpp.cross_sell_products && bizOpp.cross_sell_products.length > 0 ? `

                                💡 Cross-sell Opportunities:

                                    ${bizOpp.cross_sell_products.map(product => `

                                            ${escapeHtml(product.product_idea || '')}
                                            ${product.reasoning ? '
' + escapeHtml(product.reasoning) + '' : ''}

                                    `).join('')}

                        ` : ''}

                        ${bizOpp.custom_software_ideas && bizOpp.custom_software_ideas.length > 0 ? `

                                💻 Software Ideas:

                                    ${bizOpp.custom_software_ideas.map(idea => `

                                            ${escapeHtml(idea.software_idea || '')}
                                            ${idea.problem_it_solves ? '
' + escapeHtml(idea.problem_it_solves) + '' : ''}

                                    `).join('')}

                        ` : ''}

                        ${bizOpp.quote_analysis && bizOpp.quote_analysis.is_quote_related ? `

                                💰 Quote/Deal Analysis:

                                    ${bizOpp.quote_analysis.estimated_value ? `<div>📊 Est. Value: <strong>$${parseFloat(bizOpp.quote_analysis.estimated_value).toLocaleString()}</strong></div>` : ''}
                                    ${bizOpp.quote_analysis.objection_handling ? `<div style="margin-top: 4px;">🎯 Objections: ${escapeHtml(bizOpp.quote_analysis.objection_handling)}</div>` : ''}
                                    ${bizOpp.quote_analysis.closing_strategy ? `<div style="margin-top: 4px;">✅ Close: ${escapeHtml(bizOpp.quote_analysis.closing_strategy)}</div>` : ''}

                        ` : ''}

                `;
            } catch (threadError) {
                // Skip threads that error during analysis retrieval
                console.warn('Error loading thread analysis:', thread.thread_id, threadError);
            }
        }

        html += '
';

        // Update stats
        document.getElementById('stat-total-opportunities').textContent = totalOpps;
        document.getElementById('stat-active-quotes').textContent = quoteRelatedCount;
        document.getElementById('stat-cross-sell').textContent = crossSellCount;
        document.getElementById('stat-avg-deal').textContent = quoteRelatedCount > 0 ?
            '$' + Math.round(totalValue / quoteRelatedCount).toLocaleString() : '-';

        if (totalOpps === 0) {
            container.innerHTML = `

💼

No Business Opportunities Found

                        Business opportunities are extracted from AI thread analysis.

                        Analyze more threads to find business opportunities.

                        Note: Only threads analyzed with the latest AI prompt will have business opportunities data.

            `;
        } else {
            container.innerHTML = html;
        }

    } catch (error) {
        container.innerHTML = '
Error loading business opportunities: ' + escapeHtml(error.message) + '
';
    }
}

// ============================================
// Filters
// ============================================
async function loadFilters() {
    const container = document.getElementById('filters-list');
    container.innerHTML = '
Loading filters...
';

    try {
        const data = await apiCall('filters');

        let html = '';

        // Database filters
        if (data.database_filters.length > 0) {
            html += '
Database Filters
';
            data.database_filters.forEach(filter => {
                const pattern = filter.is_domain ? '@' + filter.email_pattern : filter.email_pattern;
                html += `

                            ${escapeHtml(pattern)}
                            ${filter.description ? ' - ' + escapeHtml(filter.description) + '' : ''}

                        <button class="btn btn-small btn-danger" onclick="removeFilter('${escapeHtml(pattern)}')">Remove</button>

                `;
            });
        }

        // Config filters
        if (data.config_filters.length > 0) {
            html += '
Config Filters (read-only)
';
            data.config_filters.forEach(pattern => {
                html += `

                        ${escapeHtml(pattern)}
                        Defined in config.php

                `;
            });
        }

        if (!html) {
            html = '
No filters configured. Add filters above to limit which emails are synced.
';
        }

        container.innerHTML = html;

    } catch (error) {
        container.innerHTML = '
Error loading filters: ' + escapeHtml(error.message) + '
';
    }
}

async function addFilter() {
    const email = document.getElementById('new-filter').value.trim();
    const description = document.getElementById('filter-description').value.trim();

    if (!email) {
        alert('Please enter an email address or domain (e.g., @example.com)');
        return;
    }

    showLoading('Adding filter...');

    try {
        await apiCall('add_filter', { email: email, description: description }, 'POST');
        document.getElementById('new-filter').value = '';
        document.getElementById('filter-description').value = '';
        loadFilters();
        alert('Filter added: ' + email);
    } catch (error) {
        alert('Error adding filter: ' + error.message);
    } finally {
        hideLoading();
    }
}

async function removeFilter(email) {
    if (!confirm('Remove filter: ' + email + '?')) return;

    showLoading('Removing filter...');

    try {
        await apiCall('remove_filter', { email: email }, 'POST');
        loadFilters();
    } catch (error) {
        alert('Error removing filter: ' + error.message);
    } finally {
        hideLoading();
    }
}

// ============================================
// Sync & Analysis
// ============================================
async function triggerSync() {
    if (!confirm('Start email sync? This will download new emails from Gmail.')) return;

    showLoading('Syncing emails...');

    try {
        const data = await apiCall('sync', { mode: 'incremental' }, 'POST');
        alert(`Sync complete!\nSaved: ${data.stats.emails_saved}\nSkipped: ${data.stats.emails_skipped}`);
        location.reload();
    } catch (error) {
        alert('Sync error: ' + error.message);
    } finally {
        hideLoading();
    }
}

async function triggerAnalysis() {
    if (!confirm('Start AI thread analysis?\n\nThis will send complete email conversations to Claude AI for analysis.\n\nUp to 10 threads will be analyzed.')) return;

    showLoading('Analyzing email threads with Claude AI...');

    try {
        const data = await apiCall('analyze', { batch: 10 }, 'POST');
        alert(`Thread Analysis Complete!\n\n${data.message || ''}\n\nThreads analyzed: ${data.stats.analyzed}\nFailed: ${data.stats.failed}\nSkipped: ${data.stats.skipped}\nQuotes extracted: ${data.stats.quotes_extracted}`);
        location.reload();
    } catch (error) {
        alert('Analysis error: ' + error.message);
    } finally {
        hideLoading();
    }
}

// ============================================
// Modal Management
// ============================================
function openModal(modalId) {
    document.getElementById(modalId).classList.add('show');
    document.body.style.overflow = 'hidden';
}

function closeModal(modalId) {
    document.getElementById(modalId).classList.remove('show');
    document.body.style.overflow = '';
}

// Close modal on outside click
document.addEventListener('click', function(e) {
    if (e.target.classList.contains('modal')) {
        e.target.classList.remove('show');
        document.body.style.overflow = '';
    }
});

// Close modal on Escape key
document.addEventListener('keydown', function(e) {
    if (e.key === 'Escape') {
        document.querySelectorAll('.modal.show').forEach(modal => {
            modal.classList.remove('show');
        });
        document.body.style.overflow = '';
    }
});

// ============================================
// Pagination Helper
// ============================================
function renderPagination(containerId, pagination, loadFunction) {
    const container = document.getElementById(containerId);
    if (!container || pagination.pages <= 1) {
        if (container) container.innerHTML = '';
        return;
    }

    let html = '';

    // Previous button
    html += `<button ${pagination.page <= 1 ? 'disabled' : ''} onclick="${loadFunction.name}(${pagination.page - 1})">← Prev</button>`;

    // Page numbers
    const maxPages = 5;
    let startPage = Math.max(1, pagination.page - Math.floor(maxPages / 2));
    let endPage = Math.min(pagination.pages, startPage + maxPages - 1);

    if (endPage - startPage < maxPages - 1) {
        startPage = Math.max(1, endPage - maxPages + 1);
    }

    if (startPage > 1) {
        html += `<button onclick="${loadFunction.name}(1)">1</button>`;
        if (startPage > 2) html += '<button disabled>...</button>';
    }

    for (let i = startPage; i <= endPage; i++) {
        html += `<button class="${i === pagination.page ? 'active' : ''}" onclick="${loadFunction.name}(${i})">${i}</button>`;
    }

    if (endPage < pagination.pages) {
        if (endPage < pagination.pages - 1) html += '<button disabled>...</button>';
        html += `<button onclick="${loadFunction.name}(${pagination.pages})">${pagination.pages}</button>`;
    }

    // Next button
    html += `<button ${pagination.page >= pagination.pages ? 'disabled' : ''} onclick="${loadFunction.name}(${pagination.page + 1})">Next →</button>`;

    container.innerHTML = html;
}

// ============================================
// Utility Functions (duplicate removed - defined at top)
// ============================================

function formatDate(dateStr) {
    if (!dateStr) return '-';
    const date = new Date(dateStr);
    return date.toLocaleDateString('en-GB', { day: 'numeric', month: 'short', year: 'numeric' });
}

function formatDateTime(dateStr) {
    if (!dateStr) return '-';
    const date = new Date(dateStr);
    return date.toLocaleString('en-GB', {
        day: 'numeric', month: 'short', year: 'numeric',
        hour: '2-digit', minute: '2-digit'
    });
}

function formatCurrency(amount, currency = 'EUR') {
    if (amount === null || amount === undefined) return '-';
    return new Intl.NumberFormat('en-GB', {
        style: 'currency',
        currency: currency
    }).format(amount);
}

// ============================================
// Analysis Status Functions
// ============================================
async function refreshAnalysisStatus() {
    try {
        const data = await apiCall('analysis_status');

        // Update stats
        document.getElementById('stat-total-threads').textContent = data.total_threads || 0;
        document.getElementById('stat-analyzed').textContent = data.analyzed || 0;
        document.getElementById('stat-pending').textContent = data.pending || 0;
        document.getElementById('stat-progress').textContent = (data.percent_complete || 0) + '%';

        // Update analyze button
        const btn = document.getElementById('btn-analyze');
        if (btn) {
            if (data.pending > 0) {
                btn.textContent = `Analyze ${Math.min(10, data.pending)} Threads`;
                btn.disabled = false;
            } else {
                btn.textContent = 'All Analyzed';
                btn.disabled = true;
            }
        }

        // Show recent analysis
        const recentDiv = document.getElementById('recent-analysis');
        if (recentDiv && data.recent_analysis && data.recent_analysis.length > 0) {
            let html = '
Recent Analysis

';
            for (const item of data.recent_analysis) {
                const trustColor = item.trust_score >= 0.7 ? 'green' : (item.trust_score >= 0.4 ? 'orange' : 'red');
                const deceptionColor = item.deception_score <= 0.3 ? 'green' : (item.deception_score <= 0.6 ? 'orange' : 'red');
                html += `

                            ${item.contact_name || 'Unknown'}
                            ${formatDate(item.analyzed_at)}

${item.summary || 'No summary'}

                            Trust: ${Math.round((item.trust_score || 0) * 100)}%
                            Deception: ${Math.round((item.deception_score || 0) * 100)}%

                `;
            }
            html += '
';
            recentDiv.innerHTML = html;
        }

    } catch (error) {
        console.error('Failed to fetch analysis status:', error);
    }
}

// Search debounce
let searchTimeout;
document.addEventListener('DOMContentLoaded', function() {
    const contactSearch = document.getElementById('contact-search');
    if (contactSearch) {
        contactSearch.addEventListener('input', function() {
            clearTimeout(searchTimeout);
            searchTimeout = setTimeout(() => loadContacts(1), 300);
        });
    }

    const typeFilter = document.getElementById('contact-type-filter');
    if (typeFilter) {
        typeFilter.addEventListener('change', () => loadContacts(1));
    }

    const outcomeFilter = document.getElementById('quote-outcome-filter');
    if (outcomeFilter) {
        outcomeFilter.addEventListener('change', () => loadQuotes());
    }

    // Load analysis status on page load (disabled - slow N+1 queries, shows Unknown)
    // refreshAnalysisStatus();

    // Auto-refresh analysis status every 30 seconds (disabled)
    // setInterval(refreshAnalysisStatus, 30000);

    // Set up semantic search input
    const semanticInput = document.getElementById('semantic-search-input');
    if (semanticInput) {
        semanticInput.addEventListener('keypress', function(e) {
            if (e.key === 'Enter') {
                semanticSearch();
            }
        });
    }
});

// ============================================
// Semantic Search (Vector Memory)
// ============================================

/**
 * Perform semantic search across emails and patterns
 */
async function semanticSearch() {
    const query = document.getElementById('semantic-search-input')?.value?.trim();
    const searchType = document.getElementById('search-type')?.value || '';
    const resultsContainer = document.getElementById('search-results');

    if (!query) {
        resultsContainer.innerHTML = '
Please enter a search query.
';
        return;
    }

    showLoading('Searching with AI...');

    try {
        const params = { q: query, limit: 20 };
        if (searchType) {
            params.type = searchType;
        }

        const data = await apiCall('semantic_search', params);

        if (!data.results || data.results.length === 0) {
            resultsContainer.innerHTML = `

No results found for "${escapeHtml(query)}"

Try different keywords or index more emails first.

            `;
            return;
        }

        let html = `

                Found ${data.results.length} results for "${escapeHtml(query)}"

        `;

        data.results.forEach((result, index) => {
            const meta = result.metadata || {};
            const similarity = result.similarity !== undefined ? (result.similarity * 100).toFixed(0) + '%' : 'N/A';
            const typeIcon = meta.type === 'email' ? '📧' : meta.type === 'thread_summary' ? '📋' : meta.type === 'pattern' ? '🔍' : '📄';
            const typeLabel = meta.type || 'document';

            html += `

                            ${typeIcon}
                            ${typeLabel}
                            ${meta.contact_name ? `<strong>${escapeHtml(meta.contact_name)}</strong>` : ''}
                            ${meta.contact_email ? `<span class="text-muted"><${escapeHtml(meta.contact_email)}></span>` : ''}

                                Similarity: ${similarity}

                    ${meta.subject ? `<div style="font-weight: 500; margin-bottom: 8px;">${escapeHtml(meta.subject)}</div>` : ''}

                        ${escapeHtml(result.content?.substring(0, 400) || '(No content)')}${result.content?.length > 400 ? '...' : ''}

                        ${meta.date ? `Date: ${formatDate(meta.date)}` : ''}
                        ${meta.sentiment ? ` | Sentiment: ${meta.sentiment}` : ''}
                        ${meta.deception_score ? ` | Deception: ${Math.round(meta.deception_score * 100)}%` : ''}
                        ${meta.trust_score ? ` | Trust: ${Math.round(meta.trust_score * 100)}%` : ''}

                    ${meta.thread_id ? `

                            <button class="btn btn-small" onclick="showThread('${escapeHtml(meta.thread_id)}')">View Thread</button>

                    ` : ''}

            `;
        });

        resultsContainer.innerHTML = html;

    } catch (error) {
        resultsContainer.innerHTML = `

                Search Error: ${escapeHtml(error.message)}

Make sure the vector memory service is running on port 5050.

        `;
    } finally {
        hideLoading();
    }
}

/**
 * Quick search with preset query
 */
function quickSearch(query) {
    const input = document.getElementById('semantic-search-input');
    if (input) {
        input.value = query;
        semanticSearch();
    }
}

/**
 * Index emails to vector memory
 */
async function indexVectors(type = 'all') {
    const statusDiv = document.getElementById('vector-status');

    if (!confirm(`Index ${type} emails to vector memory?\n\nThis will create embeddings for searchable content.\nThis may take several minutes depending on the number of emails.`)) {
        return;
    }

    showLoading('Indexing emails to vector memory...');
    statusDiv.innerHTML = 'Indexing in progress...';

    try {
        const data = await apiCall('index_vectors', { type: type }, 'POST');

        if (data.success) {
            statusDiv.innerHTML = `
                ✓ Indexing complete!

                    Emails indexed: ${data.emails_indexed || 0}

                    Threads indexed: ${data.threads_indexed || 0}

            `;
            alert(`Indexing Complete!\n\nEmails indexed: ${data.emails_indexed || 0}\nThreads indexed: ${data.threads_indexed || 0}`);
        } else {
            statusDiv.innerHTML = `<span style="color: #dc2626;">Indexing failed: ${escapeHtml(data.error || 'Unknown error')}</span>`;
        }

    } catch (error) {
        statusDiv.innerHTML = `<span style="color: #dc2626;">Error: ${escapeHtml(error.message)}</span>`;
    } finally {
        hideLoading();
    }
}

/**
 * Check vector memory service status
 */
async function checkVectorStatus() {
    const statusDiv = document.getElementById('vector-status');
    statusDiv.innerHTML = 'Checking vector service...';

    try {
        const data = await apiCall('vector_status');

        if (data.healthy) {
            let html = `
                ✓ Vector service is healthy

            `;

            if (data.stats) {
                html += `
                    Total documents: ${data.stats.total_documents || 0}

                    Collections: ${data.stats.collections?.join(', ') || 'None'}
                `;
            }

            html += '
';
            statusDiv.innerHTML = html;
        } else {
            statusDiv.innerHTML = `
                ✗ Vector service unavailable

                    Make sure the memory service is running on port 5050

            `;
        }

    } catch (error) {
        statusDiv.innerHTML = `
            ✗ Cannot reach vector service

                Error: ${escapeHtml(error.message)}

        `;
    }
}

// ============================================
// Pattern Detection Functions
// ============================================

/**
 * Load pattern statistics
 */
async function loadPatternStats(forceRefresh = false) {
    const grid = document.getElementById('pattern-stats-grid');
    if (!grid) return;

    // Show loading indicator
    grid.innerHTML = '
Loading patterns' + (forceRefresh ? ' (refreshing cache)...' : '...') + '
';

    try {
        const params = forceRefresh ? { refresh: '1' } : {};
        const data = await apiCall('pattern_stats', params);

        if (!data.stats || Object.keys(data.stats).length === 0) {
            grid.innerHTML = '
No patterns detected yet. Analyze some threads first.
';
            return;
        }

        let html = '';
        const categoryColors = {
            financial: '#ef4444',
            communication: '#f59e0b',
            negotiation: '#3b82f6',
            deception: '#dc2626',
            project: '#8b5cf6'
        };

        for (const [key, stat] of Object.entries(data.stats)) {
            const color = categoryColors[stat.category] || '#6b7280';
            const riskBadge = stat.risk_level === 'high' || stat.risk_level === 'critical' ?
                `<span style="background: #fee2e2; color: #dc2626; padding: 4px 8px; border-radius: 12px; font-size: 11px; font-weight: 600;">⚠ ${stat.risk_level.toUpperCase()}</span>` :
                `<span style="background: #f3f4f6; color: #6b7280; padding: 4px 8px; border-radius: 12px; font-size: 11px; font-weight: 600;">${stat.risk_level.toUpperCase()}</span>`;

            html += `

${stat.name}

                        ${riskBadge}

Total Matches

${stat.total_matches}

Contacts Affected

${stat.contacts_affected}

                        <button class="btn btn-small" onclick="viewPatternDetails('${key}')">View Details</button>

            `;
        }

        // Add cached indicator and refresh button
        const cacheInfo = data.cached ?
            `

                ⚡ Loaded from cache.
                Refresh now

` : '';
        grid.innerHTML = html + cacheInfo;

    } catch (error) {
        grid.innerHTML = `<p style="color: #dc2626;">Error loading patterns: ${escapeHtml(error.message)}</p>`;
    }
}

/**
 * View pattern details
 */
async function viewPatternDetails(patternKey) {
    const details = document.getElementById('pattern-details');
    if (!details) return;

    showLoading('Loading pattern matches...');

    try {
        const data = await apiCall('detect_pattern', {pattern: patternKey});

        hideLoading();

        if (!data.matches || data.matches.length === 0) {
            details.innerHTML = '
No matches found for this pattern.
';
            return;
        }

        let html = `<h4 style="margin-bottom: 16px;">Pattern Matches: ${patternKey.replace(/_/g, ' ').toUpperCase()}</h4>`;

        data.matches.forEach((match, index) => {
            const similarity = (match.similarity * 100).toFixed(0);
            const meta = match.metadata || {};

            html += `

                            ${meta.subject || 'No Subject'}

                                ${meta.contact_name || 'Unknown'} • ${meta.date || ''}

                            ${similarity}% match

                        "${match.content.substring(0, 200)}..."

                    ${meta.thread_id ? `
                        <button class="btn btn-small" onclick="showThread('${meta.thread_id}')">View Thread</button>
                    ` : ''}

            `;
        });

        details.innerHTML = html;
        details.scrollIntoView({behavior: 'smooth'});

    } catch (error) {
        hideLoading();
        details.innerHTML = `<p style="color: #dc2626;">Error: ${escapeHtml(error.message)}</p>`;
    }
}

// ============================================
// Alerts Functions
// ============================================

/**
 * Load alerts
 */
async function loadAlerts() {
    const list = document.getElementById('alerts-list');
    const stats = document.getElementById('alert-stats');
    if (!list) return;

    const severity = document.getElementById('alert-severity-filter')?.value || '';
    const status = document.getElementById('alert-status-filter')?.value || 'unread';

    try {
        // Load stats
        if (stats) {
            const statsData = await apiCall('alert_stats');
            renderAlertStats(statsData);
        }

        // Load alerts
        const params = {severity: severity};
        if (status === 'unread') params.is_read = false;
        else if (status === 'read') params.is_read = true;

        const data = await apiCall('alerts', params);

        if (!data.alerts || data.alerts.length === 0) {
            list.innerHTML = '
No alerts found ✓
';
            return;
        }

        renderAlerts(data.alerts);

    } catch (error) {
        list.innerHTML = `<p style="color: #dc2626;">Error loading alerts: ${escapeHtml(error.message)}</p>`;
    }
}

/**
 * Render alert stats
 */
function renderAlertStats(stats) {
    const container = document.getElementById('alert-stats');
    if (!container) return;

    const statCards = [
        {label: 'Total Alerts', value: stats.total || 0, color: '#3b82f6'},
        {label: 'Unread', value: stats.unread || 0, color: '#f59e0b'},
        {label: 'Critical', value: stats.critical || 0, color: '#dc2626'},
        {label: 'High', value: stats.high || 0, color: '#ef4444'}
    ];

    let html = '';
    statCards.forEach(card => {
        html += `

${card.label}

${card.value}

        `;
    });

    container.innerHTML = html;
}

/**
 * Render alerts list
 */
function renderAlerts(alerts) {
    const list = document.getElementById('alerts-list');
    if (!list) return;

    const severityColors = {
        critical: '#dc2626',
        high: '#ef4444',
        medium: '#f59e0b',
        low: '#6b7280'
    };

    let html = '';
    alerts.forEach(alert => {
        const color = severityColors[alert.severity] || '#6b7280';
        const isRead = alert.is_read == 1;

        html += `

                                ${alert.severity.toUpperCase()}

                            ${!isRead ? 'NEW' : ''}

${alert.contact_name || 'Unknown Contact'}

${alert.message}

                            ${new Date(alert.created_at).toLocaleString()}

                    ${!isRead ? `<button class="btn btn-small" onclick="markAlertRead(${alert.id})">Mark Read</button>` : ''}
                    <button class="btn btn-small" onclick="dismissAlert(${alert.id})">Dismiss</button>
                    ${alert.thread_id ? `<button class="btn btn-small" onclick="showThread('${alert.thread_id}')">View Thread</button>` : ''}

        `;
    });

    list.innerHTML = html;
}

/**
 * Mark alert as read
 */
async function markAlertRead(alertId) {
    try {
        await apiCall('alert_read', {alert_id: alertId}, 'POST');
        loadAlerts();
    } catch (error) {
        alert('Error marking alert as read: ' + error.message);
    }
}

/**
 * Dismiss alert
 */
async function dismissAlert(alertId) {
    if (!confirm('Dismiss this alert?')) return;

    try {
        await apiCall('alert_dismiss', {alert_id: alertId}, 'POST');
        loadAlerts();
    } catch (error) {
        alert('Error dismissing alert: ' + error.message);
    }
}

// ============================================
// Gmail Account Management
// ============================================

/**
 * Load Gmail accounts list
 */
async function loadGmailAccounts() {
    const listEl = document.getElementById('gmail-accounts-list');
    if (!listEl) return;

    try {
        listEl.innerHTML = '
Loading accounts...
';

        const data = await apiCall('gmail_accounts');

        // Update stats
        if (document.getElementById('stat-total-accounts')) {
            document.getElementById('stat-total-accounts').textContent = data.total || 0;
            document.getElementById('stat-active-accounts').textContent = data.active || 0;
            document.getElementById('stat-authenticated').textContent = data.authenticated || 0;
            // Use formatNumber if available, otherwise just use the number
            const totalEmails = data.total_emails || 0;
            document.getElementById('stat-total-emails').textContent = (typeof formatNumber !== 'undefined')
                ? formatNumber(totalEmails)
                : totalEmails.toString();
        }

        if (!data.accounts || data.accounts.length === 0) {
            listEl.innerHTML = '

No Gmail accounts configured. Add one above to get started.

';
            return;
        }

        let html = '';
        data.accounts.forEach(account => {
            const authStatus = account.is_authenticated ?
                '✓ Authenticated' :
                '✗ Not Authenticated';

            const primaryBadge = account.is_primary ?
                'PRIMARY' : '';

            const activeStatus = account.is_active ?
                '● Active' :
                '○ Inactive';

            const lastSync = account.last_sync ?
                new Date(account.last_sync).toLocaleString() : 'Never';

            html += `

                                ${escapeHtml(account.email)}${primaryBadge}

                            ${account.name ? `<p style="margin: 0; color: #666; font-size: 0.9em;">${escapeHtml(account.name)}</p>` : ''}

                            ${authStatus}

                            ${activeStatus}

Emails Synced

${(typeof formatNumber !== 'undefined' ? formatNumber(account.email_count || 0) : (account.email_count || 0).toString())}

Sync Status

${account.sync_status || 'idle'}

Last Sync

${lastSync}

                        ${!account.is_authenticated ?
                            `<button class="btn btn-primary btn-small" onclick="authorizeAccount(${account.id}, '${escapeHtml(account.email)}')">🔐 Authorize</button>` :
                            `<button class="btn btn-small" onclick="syncAccount(${account.id}, '${escapeHtml(account.email)}')">🔄 Sync Now</button>`
                        }
                        ${!account.is_primary ?
                            `<button class="btn btn-small" onclick="setPrimaryAccount(${account.id})">⭐ Set Primary</button>` : ''
                        }
                        <button class="btn btn-small" onclick="toggleAccountActive(${account.id}, ${account.is_active})">
                            ${account.is_active ? '⏸ Deactivate' : '▶ Activate'}
                        </button>
                        <button class="btn btn-small" style="background: #ea4335; color: white;" onclick="removeAccount(${account.id}, '${escapeHtml(account.email)}')">
                            🗑 Remove
                        </button>

            `;
        });

        listEl.innerHTML = html;

    } catch (error) {
        listEl.innerHTML = `<div class="alert alert-error">Error loading accounts: ${escapeHtml(error.message)}</div>`;
    }
}

/**
 * Add new Gmail account
 */
async function addGmailAccount() {
    const emailEl = document.getElementById('new-account-email');
    const nameEl = document.getElementById('new-account-name');

    const email = emailEl.value.trim();
    const name = nameEl.value.trim();

    if (!email) {
        alert('Please enter an email address');
        return;
    }

    if (!email.includes('@')) {
        alert('Please enter a valid email address');
        return;
    }

    try {
        showLoading('Adding account...');
        const data = await apiCall('gmail_account_add', {
            email: email,
            name: name
        }, 'POST');

        if (data.success) {
            // Clear form
            emailEl.value = '';
            nameEl.value = '';

            // Show auth URL
            if (confirm(`Account created! Click OK to authorize "${email}" via OAuth.`)) {
                window.open(data.auth_url, '_blank');
            }

            // Reload accounts
            loadGmailAccounts();
        }
    } catch (error) {
        alert('Error adding account: ' + error.message);
    } finally {
        hideLoading();
    }
}

/**
 * Authorize Gmail account
 */
async function authorizeAccount(accountId, email) {
    try {
        const data = await apiCall('gmail_auth_url', {account_id: accountId});

        if (confirm(`Click OK to authorize "${email}" via OAuth.`)) {
            window.open(data.auth_url, '_blank');

            // Poll for auth status
            setTimeout(() => {
                loadGmailAccounts();
            }, 2000);
        }
    } catch (error) {
        alert('Error getting auth URL: ' + error.message);
    }
}

/**
 * Sync specific account
 */
async function syncAccount(accountId, email) {
    if (!confirm(`Sync emails for "${email}"?`)) return;

    try {
        showLoading(`Syncing ${email}...`);
        const data = await apiCall('sync', {
            account_id: accountId,
            mode: 'incremental'
        }, 'POST');

        if (data.success) {
            alert(`Sync completed!\n\nEmails saved: ${data.stats.emails_saved || 0}\nEmails skipped: ${data.stats.emails_skipped || 0}`);
            loadGmailAccounts();
        }
    } catch (error) {
        alert('Error syncing account: ' + error.message);
    } finally {
        hideLoading();
    }
}

/**
 * Sync all accounts
 */
async function syncAllAccounts() {
    if (!confirm('Sync all active Gmail accounts? This may take a while.')) return;

    try {
        showLoading('Syncing all accounts...');
        const data = await apiCall('sync', {
            all: 1,
            mode: 'incremental'
        }, 'POST');

        if (data.success) {
            let message = `Multi-account sync completed!\n\n`;
            message += `Accounts synced: ${data.stats.accounts_synced || 0}\n`;
            message += `Total emails saved: ${data.stats.total_emails_saved || 0}\n`;
            message += `Total errors: ${data.stats.total_errors || 0}\n\n`;

            if (data.stats.per_account) {
                message += 'Per-account results:\n';
                Object.keys(data.stats.per_account).forEach(email => {
                    const stats = data.stats.per_account[email];
                    if (stats.error) {
                        message += `  ✗ ${email}: ${stats.error}\n`;
                    } else {
                        message += `  ✓ ${email}: ${stats.emails_saved || 0} emails\n`;
                    }
                });
            }

            alert(message);
            loadGmailAccounts();
        }
    } catch (error) {
        alert('Error syncing accounts: ' + error.message);
    } finally {
        hideLoading();
    }
}

/**
 * Set primary account
 */
async function setPrimaryAccount(accountId) {
    if (!confirm('Set this account as the primary account?')) return;

    try {
        await apiCall('gmail_account_primary', {account_id: accountId}, 'POST');
        loadGmailAccounts();
    } catch (error) {
        alert('Error setting primary account: ' + error.message);
    }
}

/**
 * Toggle account active status
 */
async function toggleAccountActive(accountId, currentStatus) {
    const action = currentStatus ? 'deactivate' : 'activate';
    if (!confirm(`${action.charAt(0).toUpperCase() + action.slice(1)} this account?`)) return;

    try {
        await apiCall('gmail_account_toggle', {
            account_id: accountId,
            is_active: !currentStatus
        }, 'POST');
        loadGmailAccounts();
    } catch (error) {
        alert('Error updating account status: ' + error.message);
    }
}

/**
 * Remove Gmail account
 */
async function removeAccount(accountId, email) {
    if (!confirm(`Remove account "${email}"?\n\nThis will NOT delete synced emails, but the account will no longer be synced.`)) {
        return;
    }

    try {
        showLoading('Removing account...');
        await apiCall('gmail_account_remove', {account_id: accountId}, 'POST');
        loadGmailAccounts();
    } catch (error) {
        alert('Error removing account: ' + error.message);
    } finally {
        hideLoading();
    }
}

// ============================================
// DATA ENRICHMENT FUNCTIONS
// ============================================

/**
 * Load enrichment queue status
 */
async function loadEnrichmentStatus() {
    try {
        const data = await apiCall('enrichment_queue');

        if (data.success && data.queue) {
            document.getElementById('enrichment-queue-pending').textContent = data.queue.pending || 0;
            document.getElementById('enrichment-queue-processing').textContent = data.queue.processing || 0;
            document.getElementById('enrichment-queue-completed').textContent = data.queue.completed || 0;
            document.getElementById('enrichment-queue-failed').textContent = data.queue.failed || 0;
        }

        // Also load recent logs
        await loadEnrichmentLogs();
    } catch (error) {
        console.error('Error loading enrichment status:', error);
        showEnrichmentMessage('Error loading status: ' + error.message, 'error');
    }
}

/**
 * Load enrichment logs
 */
async function loadEnrichmentLogs() {
    try {
        const data = await apiCall('enrichment_logs', {limit: 20});
        const listEl = document.getElementById('enrichment-logs-list');

        if (!listEl) return;

        if (data.success && data.logs && data.logs.length > 0) {
            let html = '
';
            html += '

Time	Type	Entity	Source	Status

';
            html += '
';

            data.logs.forEach(log => {
                const time = log.created_at ? new Date(log.created_at).toLocaleString() : 'N/A';
                const statusColor = log.status === 'success' ? '#10b981' : '#ef4444';
                const statusIcon = log.status === 'success' ? '✅' : '❌';

                html += `

                    ${time}
                    ${log.entity_type || 'N/A'}
                    ${log.entity_id || 'N/A'}
                    ${log.source || 'N/A'}
                    ${statusIcon} ${log.status || 'N/A'}

`;
            });

            html += '

';
            listEl.innerHTML = html;
        } else {
            listEl.innerHTML = '
No enrichment logs yet
';
        }
    } catch (error) {
        console.error('Error loading enrichment logs:', error);
    }
}

/**
 * Enrich single contact
 */
async function enrichSingleContact() {
    const email = document.getElementById('enrich-single-email').value.trim();
    if (!email) {
        showEnrichmentMessage('Please enter an email address', 'error');
        return;
    }

    try {
        showEnrichmentMessage('Enriching contact...', 'info');

        const data = await apiCall('enrich_contact', {email: email}, 'POST');

        if (data.success) {
            const sources = data.sources_used ? data.sources_used.join(', ') : 'none';
            showEnrichmentMessage(`✅ Contact enriched successfully! Sources used: ${sources}`, 'success');
            document.getElementById('enrich-single-email').value = '';
            loadEnrichmentStatus();
        } else {
            showEnrichmentMessage('❌ ' + (data.error || 'Enrichment failed'), 'error');
        }
    } catch (error) {
        showEnrichmentMessage('❌ Error: ' + error.message, 'error');
    }
}

/**
 * Enrich single company
 */
async function enrichSingleCompany() {
    const domain = document.getElementById('enrich-single-domain').value.trim();
    if (!domain) {
        showEnrichmentMessage('Please enter a company domain', 'error');
        return;
    }

    try {
        showEnrichmentMessage('Enriching company...', 'info');

        const data = await apiCall('enrich_company', {domain: domain}, 'POST');

        if (data.success) {
            const sources = data.sources_used ? data.sources_used.join(', ') : 'none';
            showEnrichmentMessage(`✅ Company enriched successfully! Sources used: ${sources}`, 'success');
            document.getElementById('enrich-single-domain').value = '';
            loadEnrichmentStatus();
        } else {
            showEnrichmentMessage('❌ ' + (data.error || 'Enrichment failed'), 'error');
        }
    } catch (error) {
        showEnrichmentMessage('❌ Error: ' + error.message, 'error');
    }
}

/**
 * Enrich all contacts (batch)
 */
async function enrichAllContacts() {
    if (!confirm('This will queue all contacts for enrichment. This may take a while and use API credits. Continue?')) {
        return;
    }

    try {
        showEnrichmentMessage('Queuing all contacts for enrichment...', 'info');

        const data = await apiCall('enrich_all_contacts', {priority: 3}, 'POST');

        if (data.success) {
            showEnrichmentMessage(`✅ ${data.queued || 0} contacts queued for enrichment`, 'success');
            loadEnrichmentStatus();
        } else {
            showEnrichmentMessage('❌ ' + (data.error || 'Failed to queue contacts'), 'error');
        }
    } catch (error) {
        showEnrichmentMessage('❌ Error: ' + error.message, 'error');
    }
}

/**
 * Enrich all companies (batch)
 */
async function enrichAllCompanies() {
    if (!confirm('This will queue all companies for enrichment. This may take a while and use API credits. Continue?')) {
        return;
    }

    try {
        showEnrichmentMessage('Queuing all companies for enrichment...', 'info');

        const data = await apiCall('enrich_all_companies', {priority: 3}, 'POST');

        if (data.success) {
            showEnrichmentMessage(`✅ ${data.queued || 0} companies queued for enrichment`, 'success');
            loadEnrichmentStatus();
        } else {
            showEnrichmentMessage('❌ ' + (data.error || 'Failed to queue companies'), 'error');
        }
    } catch (error) {
        showEnrichmentMessage('❌ Error: ' + error.message, 'error');
    }
}

/**
 * Process enrichment queue
 */
async function processEnrichmentQueue() {
    try {
        showEnrichmentMessage('Processing enrichment queue...', 'info');

        const data = await apiCall('process_enrichment_queue', {batch_size: 10}, 'POST');

        if (data.success) {
            const results = data.results || {};
            showEnrichmentMessage(
                `✅ Queue processed: ${results.processed || 0} items, ${results.succeeded || 0} succeeded, ${results.failed || 0} failed`,
                'success'
            );
            loadEnrichmentStatus();
        } else {
            showEnrichmentMessage('❌ ' + (data.error || 'Failed to process queue'), 'error');
        }
    } catch (error) {
        showEnrichmentMessage('❌ Error: ' + error.message, 'error');
    }
}

/**
 * Enrich contact from the contacts list table
 */
async function enrichContactFromList(btn) {
    const email = btn.dataset.email;
    const contactId = btn.dataset.contactId;

    const originalText = btn.innerHTML;
    btn.innerHTML = '⏳ Enriching...';
    btn.disabled = true;

    try {
        const data = await apiCall('enrich_contact', {
            email: email,
            contact_id: contactId
        }, 'POST');

        if (data.success) {
            const sources = data.sources_used ? data.sources_used.join(', ') : 'Apollo';
            btn.innerHTML = '✅ Done';
            btn.classList.add('btn-success');
            btn.classList.remove('btn-enrich');

            // Show toast notification
            showToast(`Contact enriched! Sources: ${sources}`, 'success');

            // Reload contacts after a short delay to show enriched badge
            setTimeout(() => loadContacts(state.contacts.page), 2000);
        } else {
            btn.innerHTML = '❌ Failed';
            btn.classList.add('btn-danger');
            showToast(data.error || 'Enrichment failed', 'error');

            setTimeout(() => {
                btn.innerHTML = originalText;
                btn.disabled = false;
                btn.classList.remove('btn-danger');
            }, 3000);
        }
    } catch (error) {
        btn.innerHTML = '❌ Error';
        btn.classList.add('btn-danger');
        showToast('Error: ' + error.message, 'error');

        setTimeout(() => {
            btn.innerHTML = originalText;
            btn.disabled = false;
            btn.classList.remove('btn-danger');
        }, 3000);
    }
}

/**
 * Show a toast notification
 */
function showToast(message, type = 'info') {
    // Remove existing toast if any
    const existingToast = document.querySelector('.toast-notification');
    if (existingToast) existingToast.remove();

    const toast = document.createElement('div');
    toast.className = `toast-notification toast-${type}`;
    toast.innerHTML = message;
    toast.style.cssText = `
        position: fixed;
        bottom: 20px;
        right: 20px;
        padding: 12px 20px;
        border-radius: 6px;
        color: white;
        font-weight: 500;
        z-index: 10000;
        animation: slideIn 0.3s ease;
        background: ${type === 'success' ? '#16a34a' : type === 'error' ? '#dc2626' : '#3b82f6'};
    `;

    document.body.appendChild(toast);

    setTimeout(() => {
        toast.style.animation = 'slideOut 0.3s ease';
        setTimeout(() => toast.remove(), 300);
    }, 4000);
}

/**
 * Show enrichment status message
 */
function showEnrichmentMessage(message, type = 'info') {
    const msgEl = document.getElementById('enrichment-status-message');
    if (!msgEl) return;

    msgEl.style.display = 'block';
    msgEl.innerHTML = message;

    switch (type) {
        case 'success':
            msgEl.style.background = '#f0fdf4';
            msgEl.style.color = '#166534';
            msgEl.style.border = '1px solid #bbf7d0';
            break;
        case 'error':
            msgEl.style.background = '#fef2f2';
            msgEl.style.color = '#991b1b';
            msgEl.style.border = '1px solid #fecaca';
            break;
        default:
            msgEl.style.background = '#eff6ff';
            msgEl.style.color = '#1e40af';
            msgEl.style.border = '1px solid #bfdbfe';
    }

    // Auto-hide after 5 seconds for non-error messages
    if (type !== 'error') {
        setTimeout(() => {
            msgEl.style.display = 'none';
        }, 5000);
    }
}

// ============================================
// Business Ideas Tab
// ============================================

/**
 * Load business ideas projects list
 */
async function loadBusinessIdeasProjects() {
    const container = document.getElementById('business-ideas-projects-list');
    container.innerHTML = '
Loading research projects...
';

    try {
        const data = await apiCall('business_ideas_projects');
        const queueStatus = await apiCall('business_ideas_queue_status');

        // Update queue stats
        document.getElementById('bi-queue-pending').textContent = queueStatus.pending || 0;
        document.getElementById('bi-queue-running').textContent = queueStatus.running || 0;
        document.getElementById('bi-queue-completed').textContent = queueStatus.completed || 0;
        document.getElementById('bi-queue-failed').textContent = queueStatus.failed || 0;
        document.getElementById('bi-total-findings').textContent = queueStatus.total_findings || 0;

        if (!data.projects || data.projects.length === 0) {
            container.innerHTML = `

💡

No Research Projects Yet

                        Create a new research project above to discover business opportunities.

            `;
            return;
        }

        let html = '
';

        for (const project of data.projects) {
            const statusColors = {
                'pending': '#fbbf24',
                'running': '#3b82f6',
                'paused': '#9ca3af',
                'completed': '#10b981',
                'failed': '#ef4444'
            };

            const progressPercent = project.progress?.percentage || 0;
            const taskStats = project.task_stats || {};

            html += `

                                    ${escapeHtml(project.title || 'Untitled Project')}

                                ${escapeHtml(project.query ? project.query.substring(0, 150) + (project.query.length > 150 ? '...' : '') : '')}

                            ${project.status}

                            Progress
                            ${progressPercent}%

                        📊 ${taskStats.completed || 0}/${taskStats.total || 0} tasks
                        💡 ${project.results?.verticals?.length || 0} verticals
                        📅 ${formatDate(project.created_at || project._created_at)}

                        ${project.status === 'pending' ?
                            `<button class="btn btn-small" onclick="startBusinessIdeasProject('${project._id}')" style="background: #10b981; color: white;">▶ Start</button>` : ''}
                        ${project.status === 'running' ?
                            `<button class="btn btn-small" onclick="pauseBusinessIdeasProject('${project._id}')">⏸ Pause</button>` : ''}
                        ${project.status === 'paused' ?
                            `<button class="btn btn-small" onclick="resumeBusinessIdeasProject('${project._id}')" style="background: #3b82f6; color: white;">▶ Resume</button>` : ''}
                        ${project.status === 'completed' ?
                            `<button class="btn btn-small" onclick="viewBusinessIdeasReport('${project._id}')" style="background: #8b5cf6; color: white;">📊 View Report</button>` : ''}
                        <button class="btn btn-small" onclick="showBusinessIdeasProject('${project._id}')">Details</button>

            `;
        }

        html += '
';
        container.innerHTML = html;

    } catch (error) {
        container.innerHTML = '
Error loading projects: ' + escapeHtml(error.message) + '
';
    }
}

/**
 * Create a new business ideas project
 */
async function createBusinessIdeasProject() {
    const query = document.getElementById('business-ideas-query').value.trim();
    if (!query) {
        alert('Please enter a research query');
        return;
    }

    const parameters = {
        verticals_count: parseInt(document.getElementById('param-verticals')?.value) || 50,
        horizontals_per_vertical: parseInt(document.getElementById('param-horizontals')?.value) || 25,
        depth: document.getElementById('param-depth')?.value || 'medium',
        scenario_depth: document.getElementById('param-scenario-depth')?.value || 'high_level'
    };

    showLoading('Creating research project...');

    try {
        const result = await apiCall('create_business_ideas_project', {
            query: query,
            parameters: JSON.stringify(parameters)
        }, 'POST');

        hideLoading();

        if (result.success) {
            document.getElementById('business-ideas-query').value = '';
            loadBusinessIdeasProjects();

            if (confirm('Project created! Would you like to start it now? This will decompose the query into research tasks.')) {
                await startBusinessIdeasProject(result.project._id);
            }
        } else {
            alert('Error: ' + (result.message || 'Unknown error'));
        }
    } catch (error) {
        hideLoading();
        alert('Error creating project: ' + error.message);
    }
}

/**
 * Start a business ideas project (decompose into tasks)
 */
async function startBusinessIdeasProject(projectId) {
    showLoading('Decomposing project into research tasks...');

    try {
        const result = await apiCall('start_business_ideas_project', { id: projectId }, 'POST');
        hideLoading();

        if (result.success) {
            alert(`Project started! Created ${result.tasks_created} research tasks for ${result.verticals} verticals.`);
            loadBusinessIdeasProjects();
        } else {
            alert('Error: ' + (result.error || 'Unknown error'));
        }
    } catch (error) {
        hideLoading();
        alert('Error starting project: ' + error.message);
    }
}

/**
 * Pause a business ideas project
 */
async function pauseBusinessIdeasProject(projectId) {
    try {
        await apiCall('pause_business_ideas_project', { id: projectId }, 'POST');
        loadBusinessIdeasProjects();
    } catch (error) {
        alert('Error pausing project: ' + error.message);
    }
}

/**
 * Resume a business ideas project
 */
async function resumeBusinessIdeasProject(projectId) {
    try {
        await apiCall('resume_business_ideas_project', { id: projectId }, 'POST');
        loadBusinessIdeasProjects();
    } catch (error) {
        alert('Error resuming project: ' + error.message);
    }
}

/**
 * Process business ideas queue
 */
async function processBusinessIdeasQueue() {
    showLoading('Processing research queue... This may take a while.');

    try {
        const result = await apiCall('process_business_ideas_queue', {
            max_tasks: 5,
            delay_ms: 2000
        }, 'POST');

        hideLoading();
        loadBusinessIdeasProjects();

        if (result.processed > 0) {
            alert(`Processed ${result.processed} tasks.\n✅ Completed: ${result.completed}\n❌ Failed: ${result.failed}\n⏳ Remaining: ${result.pending}`);
        } else {
            alert('No pending tasks to process.');
        }
    } catch (error) {
        hideLoading();
        alert('Error processing queue: ' + error.message);
    }
}

/**
 * Show business ideas project details
 */
async function showBusinessIdeasProject(projectId) {
    showLoading('Loading project details...');

    try {
        const projectData = await apiCall('business_ideas_project', { id: projectId });
        const findingsData = await apiCall('business_ideas_findings', { project_id: projectId });
        const scenariosData = await apiCall('business_ideas_scenarios', { project_id: projectId });

        hideLoading();

        const project = projectData.project;
        const findings = findingsData.findings || [];
        const scenarios = scenariosData.scenarios || [];

        let html = `

${escapeHtml(project.title || 'Research Project')}

${escapeHtml(project.query || '')}

                        Status: ${project.status}
                        Progress: ${project.progress?.percentage || 0}%
                        Tasks: ${project.progress?.completed_tasks || 0}/${project.progress?.total_tasks || 0}

📊 Research Findings (${findings.length})

                    ${findings.length === 0 ? '
No findings yet. Process the queue to generate findings.
' : ''}

        `;

        // Group findings by type
        const verticals = findings.filter(f => f.type === 'vertical');
        const horizontals = findings.filter(f => f.type === 'horizontal');

        if (verticals.length > 0) {
            html += `<h4 style="margin: 10px 0;">Verticals (${verticals.length})</h4>`;
            for (const finding of verticals.slice(0, 20)) {
                const score = finding.relevance?.score || 0;
                const scoreColor = score >= 70 ? '#10b981' : score >= 40 ? '#f59e0b' : '#6b7280';

                html += `

                            ${escapeHtml(finding.name || 'Unknown')}

                                ${score}% relevance

                            ${escapeHtml(finding.ai_insights?.summary || finding.description || '').substring(0, 200)}...

                `;
            }
        }

        html += `

🔮 Business Scenarios (${scenarios.length})

                    ${scenarios.length === 0 ? `

No scenarios generated yet.

                        <button class="btn btn-small" onclick="generateBusinessScenarios('${projectId}')" style="background: #8b5cf6; color: white;">
                            Generate Scenarios
                        </button>
                    ` : ''}

        `;

        const scenarioColors = {
            'optimistic': '#10b981',
            'realistic': '#3b82f6',
            'pessimistic': '#f59e0b',
            'disruptive': '#8b5cf6'
        };

        for (const scenario of scenarios) {
            const color = scenarioColors[scenario.scenario_type] || '#6b7280';

            html += `

${escapeHtml(scenario.title || scenario.scenario_type)}

                        ${scenario.timeframe}

${escapeHtml(scenario.description || '').substring(0, 300)}...

                    ${scenario.action_plan?.length > 0 ? `

                            Key Actions:

                                ${scenario.action_plan.slice(0, 3).map(a => `<li>${escapeHtml(a.action || a)}</li>`).join('')}

                    ` : ''}

            `;
        }

        html += `

                    ${project.status === 'running' || project.status === 'pending' ?
                        `<button class="btn btn-primary" onclick="processBusinessIdeasQueue(); closeModal('business-ideas-modal');" style="background: #8b5cf6;">
                            ⚡ Process More Tasks
                        </button>` : ''}
                    ${project.status === 'completed' ?
                        `<button class="btn btn-primary" onclick="viewBusinessIdeasReport('${projectId}')" style="background: #10b981;">
                            📊 View Full Report
                        </button>` : ''}
                    <button class="btn btn-small" onclick="closeModal('business-ideas-modal')">Close</button>

        `;

        document.getElementById('business-ideas-detail').innerHTML = html;
        openModal('business-ideas-modal');

    } catch (error) {
        hideLoading();
        alert('Error loading project: ' + error.message);
    }
}

/**
 * Generate scenarios for a project
 */
async function generateBusinessScenarios(projectId) {
    showLoading('Generating business scenarios...');

    try {
        const depth = document.getElementById('param-scenario-depth')?.value || 'high_level';
        const result = await apiCall('generate_business_scenarios', { id: projectId, depth: depth }, 'POST');

        hideLoading();

        if (result.success) {
            alert(`Generated ${result.count} business scenarios!`);
            showBusinessIdeasProject(projectId);
        } else {
            alert('Error: ' + (result.error || 'Unknown error'));
        }
    } catch (error) {
        hideLoading();
        alert('Error generating scenarios: ' + error.message);
    }
}

/**
 * View full report for a project
 */
async function viewBusinessIdeasReport(projectId) {
    showLoading('Generating report...');

    try {
        const result = await apiCall('generate_business_ideas_report', { id: projectId }, 'POST');

        hideLoading();

        if (!result.success) {
            alert('Error: ' + (result.error || 'Unknown error'));
            return;
        }

        const report = result.report;

        let html = `

📊 Research Report

${escapeHtml(report.project.title || 'Business Research')}

                        Findings: ${report.summary.total_findings}
                        Scenarios: ${report.summary.total_scenarios}
                        Tasks: ${report.summary.tasks_completed}/${report.summary.tasks_total}

🎯 Top Opportunities

        `;

        for (const opp of (report.top_opportunities || []).slice(0, 10)) {
            const score = opp.relevance?.score || 0;

            html += `

                        ${escapeHtml(opp.name)}
                        ${score}%

${escapeHtml(opp.ai_insights?.summary || opp.description || '')}

            `;
        }

        html += `

🔮 Business Scenarios

        `;

        for (const scenario of report.scenarios || []) {
            const colors = {
                'optimistic': { bg: '#f0fdf4', border: '#10b981', text: '#166534' },
                'realistic': { bg: '#eff6ff', border: '#3b82f6', text: '#1e40af' },
                'pessimistic': { bg: '#fefce8', border: '#f59e0b', text: '#92400e' },
                'disruptive': { bg: '#faf5ff', border: '#8b5cf6', text: '#6b21a8' }
            };
            const style = colors[scenario.scenario_type] || colors.realistic;

            html += `

${escapeHtml(scenario.title || scenario.scenario_type)}

${escapeHtml(scenario.description || '').substring(0, 200)}...

            `;
        }

        html += `

Report generated: ${report.generated_at}

                    <button class="btn btn-small" onclick="closeModal('business-ideas-modal')">Close</button>

        `;

        document.getElementById('business-ideas-detail').innerHTML = html;
        openModal('business-ideas-modal');

    } catch (error) {
        hideLoading();
        alert('Error generating report: ' + error.message);
    }
}

/**
 * Load owner status
 */
async function loadOwnerStatus() {
    try {
        const result = await apiCall('get_owner_status');
        const status = result.owner_status;

        if (status.configured && status.financial_goals) {
            document.getElementById('owner-revenue-target').value = status.financial_goals.revenue_target || '';
            document.getElementById('owner-investment-capacity').value = status.financial_goals.investment_capacity || '';
            document.getElementById('owner-growth-objectives').value = status.financial_goals.growth_objectives || '';
            document.getElementById('owner-timeline').value = status.financial_goals.timeline || '';
            document.getElementById('owner-notes').value = status.notes || '';
        }
    } catch (error) {
        console.error('Error loading owner status:', error);
    }
}

/**
 * Save owner status
 */
async function saveOwnerStatus() {
    const data = {
        revenue_target: document.getElementById('owner-revenue-target').value,
        investment_capacity: document.getElementById('owner-investment-capacity').value,
        growth_objectives: document.getElementById('owner-growth-objectives').value,
        timeline: document.getElementById('owner-timeline').value,
        notes: document.getElementById('owner-notes').value
    };

    try {
        const result = await apiCall('save_owner_status', data, 'POST');

        const msgEl = document.getElementById('owner-status-message');
        if (result.success) {
            msgEl.textContent = '✓ Saved successfully';
            msgEl.style.color = '#059669';
        } else {
            msgEl.textContent = '✗ Error saving';
            msgEl.style.color = '#dc2626';
        }

        setTimeout(() => { msgEl.textContent = ''; }, 3000);
    } catch (error) {
        alert('Error saving: ' + error.message);
    }
}

// ============================================
// AI CHAT PANEL
// ============================================

const aiChat = {
    isOpen: false,
    mode: 'general',
    threadId: null,
    contactId: null,
    emailId: null,
    threadSubject: null,
    contactName: null,
    history: [],
    isDragging: false,
    dragOffsetX: 0,
    dragOffsetY: 0,
    // Context builder
    contextThreads: [],      // [{thread_id, subject, email_count, contact_name}]
    contextContact: null,    // {id, name, email}
    _availableThreads: [],   // temp storage for thread picker
    // Session persistence
    sessionId: null,
    // Agent
    agent: 'general',
    // Contact memories
    contactMemories: [],
};

// Provider/Model config — synced with Chrome extension contentScript.js
const AI_PROVIDER_MODELS = {
    claude: [
        { id: 'claude-sonnet-4-5-20250929', name: 'Sonnet 4.5' },
        { id: 'claude-opus-4-5-20251101', name: 'Opus 4.5 (Best)' },
        { id: 'claude-haiku-4-5-20251001', name: 'Haiku 4.5 (Fast)' },
    ],
    openai: [
        { id: 'gpt-5.2', name: 'GPT-5.2 (Latest)' },
        { id: 'gpt-5.2-pro', name: 'GPT-5.2 Pro (Best)' },
        { id: 'gpt-5-mini', name: 'GPT-5 Mini' },
        { id: 'gpt-5-nano', name: 'GPT-5 Nano (Fast)' },
    ],
    gemini: [
        { id: 'gemini-2.5-flash', name: 'Gemini 2.5 Flash' },
        { id: 'gemini-3-pro', name: 'Gemini 3 Pro' },
        { id: 'gemini-2.5-flash-lite', name: 'Flash-Lite (Fast)' },
    ],
    grok: [
        { id: 'grok-4', name: 'Grok 4 (Latest)' },
        { id: 'grok-3', name: 'Grok 3' },
        { id: 'grok-3-mini', name: 'Grok 3 Mini (Fast)' },
    ],
    deepseek: [
        { id: 'deepseek-chat', name: 'DeepSeek V3.2' },
        { id: 'deepseek-reasoner', name: 'Reasoner (Thinking)' },
    ],
    qwen: [
        { id: 'qwen-max-latest', name: 'Qwen Max' },
        { id: 'qwen-turbo-latest', name: 'Qwen Turbo (Fast)' },
    ],
    mistral: [
        { id: 'mistral-large-latest', name: 'Mistral Large' },
        { id: 'mistral-small-latest', name: 'Mistral Small (Fast)' },
    ],
    cohere: [
        { id: 'command-a-03-2025', name: 'Command A' },
        { id: 'command-r7b-12-2024', name: 'Command R7B (Fast)' },
    ],
};

const AI_PROVIDER_NAMES = {
    claude: 'Claude', openai: 'OpenAI', gemini: 'Gemini', grok: 'Grok',
    deepseek: 'DeepSeek', qwen: 'Qwen', mistral: 'Mistral', cohere: 'Cohere',
};

// Agent presets
const AI_AGENTS = {
    general:             { id: 'general',             name: 'General',    icon: '\u{1F4AC}', preferredProvider: null,    preferredModel: null, quickPrompts: ['What are my most at-risk clients?', 'Which threads need urgent attention?', 'Summarize recent activity'] },
    quote_analyst:       { id: 'quote_analyst',       name: 'Quotes',     icon: '\u{1F4B0}', preferredProvider: 'claude', preferredModel: 'claude-sonnet-4-5-20250929', quickPrompts: ['How much have we quoted across these threads?', 'What is the price sensitivity?', 'Suggest optimal pricing', 'What discounts were given?'] },
    deception_detector:  { id: 'deception_detector',  name: 'Deception',  icon: '\u{1F50D}', preferredProvider: 'claude', preferredModel: 'claude-opus-4-5-20251101', quickPrompts: ['What is the deception level?', 'Any contradictions between statements?', 'Red flags or pressure tactics?', 'Rate honesty 1-10 with evidence'] },
    contact_profiler:    { id: 'contact_profiler',    name: 'Profiler',   icon: '\u{1F464}', preferredProvider: 'claude', preferredModel: 'claude-sonnet-4-5-20250929', quickPrompts: ['Build a complete profile', 'Communication style analysis', 'Decision-making patterns', 'How should I approach them?'] },
    email_composer:      { id: 'email_composer',      name: 'Composer',   icon: '\u{270D}\u{FE0F}', preferredProvider: 'openai', preferredModel: 'gpt-5.2', quickPrompts: ['Write a professional reply', 'Write a firm but polite response', 'Write a follow-up email', 'Write a brief acknowledgment'] },
};

const AI_QUICK_PROMPTS = {
    general: ['What are my most at-risk clients?', 'Which threads need urgent attention?', 'Summarize recent activity'],
    thread: ['Summarize this thread', 'What are the key risks?', 'What action should I take next?', 'Is this client trustworthy?', 'What are they really asking for?'],
    contact: ['What is their communication style?', 'Are there any red flags?', 'How should I approach them?', 'What opportunities exist with them?'],
    quote: ['How much should I quote for this project?', 'What is the scope of work?', 'What are the risks?', 'Suggest a project timeline'],
    reply: ['Write a professional reply', 'Write a firm but polite response', 'Write a follow-up email', 'Write a brief acknowledgment'],
    multithread: ['Summarize all conversations', 'How much have we quoted?', 'What is the deception level?', 'Build a complete profile', 'What patterns emerge across these threads?'],
};

function toggleAiChat() {
    const panel = document.getElementById('ai-chat-panel');
    aiChat.isOpen = !aiChat.isOpen;
    panel.style.display = aiChat.isOpen ? 'flex' : 'none';
    if (aiChat.isOpen) { document.getElementById('ai-chat-input').focus(); renderQuickPrompts(); }
}

function openAiChatForThread(threadId, subject, contactId) {
    aiChat.threadId = threadId; aiChat.contactId = contactId || null;
    aiChat.threadSubject = subject || threadId; aiChat.emailId = null;
    setAiChatMode('thread', document.querySelector('.ai-mode-btn[data-mode="thread"]'));
    if (!aiChat.isOpen) toggleAiChat(); else document.getElementById('ai-chat-input').focus();
}

function openAiChatForContact(contactId, contactName) {
    aiChat.contactId = contactId; aiChat.contactName = contactName || 'Contact';
    aiChat.threadId = null; aiChat.emailId = null;
    setAiChatMode('contact', document.querySelector('.ai-mode-btn[data-mode="contact"]'));
    if (!aiChat.isOpen) toggleAiChat(); else document.getElementById('ai-chat-input').focus();
}

function openAiChatForReply(threadId, emailId, subject) {
    aiChat.threadId = threadId; aiChat.emailId = emailId; aiChat.threadSubject = subject || threadId;
    setAiChatMode('reply', document.querySelector('.ai-mode-btn[data-mode="reply"]'));
    if (!aiChat.isOpen) toggleAiChat(); else document.getElementById('ai-chat-input').focus();
}

function setAiChatMode(mode, btn) {
    aiChat.mode = mode;
    document.querySelectorAll('.ai-mode-btn').forEach(b => { b.style.background = '#2d2d4e'; b.style.color = '#94a3b8'; });
    if (btn) { btn.style.background = '#6366f1'; btn.style.color = '#fff'; }
    updateAiChatContextLabel(); renderQuickPrompts();
}

function updateAiChatContextLabel() {
    const label = document.getElementById('ai-chat-context-label');
    if (!label) return;

    // Multi-thread context takes priority
    if (aiChat.contextThreads.length > 0) {
        const totalEmails = aiChat.contextThreads.reduce((sum, t) => sum + (t.email_count || 0), 0);
        const contactLabel = aiChat.contextContact ? aiChat.contextContact.name : '';
        label.textContent = `${aiChat.contextThreads.length} threads, ~${totalEmails} emails` + (contactLabel ? ` from ${contactLabel}` : '');
        label.style.color = '#10b981';
        return;
    }

    const modeLabels = { general: 'General Chat', thread: 'Thread Analysis', contact: 'Contact Intelligence', quote: 'Quote Assistant', reply: 'Reply Composer' };
    let ctx = modeLabels[aiChat.mode] || 'General Chat';
    if (aiChat.mode === 'thread' && aiChat.threadSubject) ctx += ' \u00B7 ' + aiChat.threadSubject.substring(0, 30);
    if (aiChat.mode === 'contact' && aiChat.contactName) ctx += ' \u00B7 ' + aiChat.contactName;
    label.textContent = ctx;
    label.style.color = 'rgba(255,255,255,0.7)';
}

function renderQuickPrompts() {
    const container = document.getElementById('ai-quick-prompts');
    if (!container) return;
    // Use agent prompts if agent selected, then multithread if context loaded, then mode
    var prompts;
    var agent = AI_AGENTS[aiChat.agent];
    if (agent && agent.id !== 'general') {
        prompts = agent.quickPrompts;
    } else if (aiChat.contextThreads.length > 0) {
        prompts = AI_QUICK_PROMPTS.multithread;
    } else {
        prompts = AI_QUICK_PROMPTS[aiChat.mode] || AI_QUICK_PROMPTS.general;
    }
    container.innerHTML = prompts.map(function(p) {
        return '<button onclick="sendAiChatPrompt(' + JSON.stringify(p).replace(/"/g, '"') + ')" style="background:#2d2d4e;color:#94a3b8;border:1px solid #3d3d5c;border-radius:12px;padding:3px 10px;font-size:0.68em;cursor:pointer;white-space:nowrap;" onmouseover="this.style.background=\'#6366f1\';this.style.color=\'#fff\'" onmouseout="this.style.background=\'#2d2d4e\';this.style.color=\'#94a3b8\'">' + escapeHtml(p) + '</button>';
    }).join('');
}

function sendAiChatPrompt(prompt) {
    document.getElementById('ai-chat-input').value = prompt;
    sendAiChat();
}

function handleAiChatKey(e) {
    if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendAiChat(); }
}

// --- Provider/Model Dropdown ---
function initAiProviderDropdown() {
    var providerSelect = document.getElementById('ai-chat-provider');
    if (!providerSelect) return;
    providerSelect.innerHTML = '';
    Object.keys(AI_PROVIDER_MODELS).forEach(function(code) {
        var opt = document.createElement('option');
        opt.value = code;
        opt.textContent = AI_PROVIDER_NAMES[code] || code;
        providerSelect.appendChild(opt);
    });
    providerSelect.value = 'claude';
    updateAiModelDropdown();
}

function updateAiModelDropdown() {
    var providerSelect = document.getElementById('ai-chat-provider');
    var modelSelect = document.getElementById('ai-chat-model');
    if (!providerSelect || !modelSelect) return;
    var provider = providerSelect.value;
    var models = AI_PROVIDER_MODELS[provider] || [];
    modelSelect.innerHTML = '';
    models.forEach(function(m) {
        var opt = document.createElement('option');
        opt.value = m.id;
        opt.textContent = m.name;
        modelSelect.appendChild(opt);
    });
}

// --- Agent Selector ---
function selectAgent(agentId) {
    aiChat.agent = agentId;
    var agent = AI_AGENTS[agentId];
    // Highlight active agent button
    document.querySelectorAll('.ai-agent-btn').forEach(function(b) {
        b.style.background = b.dataset.agent === agentId ? '#6366f1' : '#2d2d4e';
        b.style.color = b.dataset.agent === agentId ? '#fff' : '#94a3b8';
    });
    // Set preferred provider/model
    if (agent && agent.preferredProvider) {
        var providerSelect = document.getElementById('ai-chat-provider');
        if (providerSelect) { providerSelect.value = agent.preferredProvider; updateAiModelDropdown(); }
        if (agent.preferredModel) {
            var modelSelect = document.getElementById('ai-chat-model');
            if (modelSelect) modelSelect.value = agent.preferredModel;
        }
    }
    updateAiChatContextLabel();
    renderQuickPrompts();
}

// --- Context Builder ---
function toggleContextBuilder() {
    var area = document.getElementById('ai-context-search-area');
    var icon = document.getElementById('ai-context-toggle-icon');
    if (!area) return;
    var isVisible = area.style.display !== 'none';
    area.style.display = isVisible ? 'none' : 'block';
    icon.textContent = isVisible ? '+' : '-';
}

async function searchContextContacts() {
    var query = document.getElementById('ai-context-search-input').value.trim();
    if (!query) return;
    // Strip wildcard chars — backend already does partial matching
    var cleanQuery = query.replace(/[*%]/g, '').trim();
    if (!cleanQuery) return;

    var resultsDiv = document.getElementById('ai-context-search-results');
    resultsDiv.innerHTML = '
Searching contacts & threads...
';

    try {
        // Search both contacts and threads in parallel
        var results = await Promise.all([
            apiCall('contacts', { search: cleanQuery, limit: 10 }),
            apiCall('threads', { search: cleanQuery, limit: 10 }),
        ]);
        var contactsData = results[0];
        var threadsData = results[1];

        var html = '';
        var contacts = contactsData.contacts || [];
        var threads = threadsData.threads || [];

        if (contacts.length === 0 && threads.length === 0) {
            resultsDiv.innerHTML = '
No contacts or threads found for "' + escapeHtml(cleanQuery) + '"
';
            return;
        }

        // Contacts section
        if (contacts.length > 0) {
            html += '
CONTACTS
';
            html += contacts.map(function(c) {
                var name = c.name || c.email;
                return '
' +
                    '\u{1F464} ' + escapeHtml(name) + ' ' + escapeHtml(c.email) + '' +
                    'show threads \u25B6' +
                '
';
            }).join('');
        }

        // Threads section — directly addable
        if (threads.length > 0) {
            html += '
THREADS
';
            html += threads.map(function(t) {
                var isAdded = aiChat.contextThreads.some(function(ct) { return ct.thread_id === t.thread_id; });
                var subject = t.subject || '(No Subject)';
                return '
' +
                    '' +
                        '\u{1F4E7} ' + escapeHtml(subject.substring(0, 45)) +
                        ' (' + (t.email_count || '?') + ')' +
                    '' +
                    '<button onclick="event.stopPropagation();addThreadToContext(\'' + t.thread_id + '\', \'' + escapeHtml(subject.substring(0,50).replace(/'/g,"")) + '\', ' + (t.email_count||0) + ', \'\')"' +
                            ' style="background:' + (isAdded ? '#10b981' : '#6366f1') + '; color:#fff; border:none; border-radius:4px; padding:2px 8px; font-size:0.85em; cursor:pointer; margin-left:4px; white-space:nowrap;"' +
                            (isAdded ? ' disabled' : '') + '>' +
                        (isAdded ? '\u2713 Added' : '+ Add') +
                    '</button>' +
                '
';
            }).join('');
        }

        resultsDiv.innerHTML = html;
    } catch (err) {
        resultsDiv.innerHTML = '
Error: ' + escapeHtml(err.message) + '
';
    }
}

async function selectContextContact(contactId, name, email) {
    aiChat.contextContact = { id: contactId, name: name, email: email };
    aiChat.contactId = contactId;
    aiChat.contactName = name;
    document.getElementById('ai-context-search-results').innerHTML = '';

    var picker = document.getElementById('ai-context-thread-picker');
    picker.style.display = 'block';
    picker.innerHTML = '
Loading threads...
';

    // Load contact memories
    loadContactMemories(contactId);

    try {
        var data = await apiCall('threads', { contact: contactId, limit: 50 });
        if (!data.threads || data.threads.length === 0) {
            picker.innerHTML = '
No threads for this contact
';
            return;
        }
        aiChat._availableThreads = data.threads;
        var html = '
' +
            '' + escapeHtml(name) + ' - ' + data.threads.length + ' threads' +
            '<button onclick="addAllContactThreads()" style="background:#10b981; color:#fff; border:none; border-radius:4px; padding:2px 8px; font-size:0.68em; cursor:pointer;">+ Add All</button>' +
        '
';
        html += data.threads.map(function(t) {
            var isAdded = aiChat.contextThreads.some(function(ct) { return ct.thread_id === t.thread_id; });
            return '
' +
                '' +
                    escapeHtml((t.subject || '(No Subject)').substring(0, 40)) +
                    ' (' + (t.email_count || '?') + ' emails)' +
                '' +
                '<button onclick="addThreadToContext(\'' + t.thread_id + '\', \'' + escapeHtml((t.subject||'').substring(0,50).replace(/'/g,"")) + '\', ' + (t.email_count||0) + ', \'' + escapeHtml(name.replace(/'/g,"")) + '\')"' +
                        ' style="background:' + (isAdded ? '#4b5563' : '#6366f1') + '; color:#fff; border:none; border-radius:3px; padding:1px 6px; font-size:0.95em; cursor:pointer; margin-left:4px;"' +
                        (isAdded ? ' disabled' : '') + '>' +
                    (isAdded ? '\u2713' : '+') +
                '</button>' +
            '
';
        }).join('');
        picker.innerHTML = html;
    } catch (err) {
        picker.innerHTML = '
Error: ' + escapeHtml(err.message) + '
';
    }
}

function addThreadToContext(threadId, subject, emailCount, contactName) {
    if (aiChat.contextThreads.some(function(t) { return t.thread_id === threadId; })) return;
    aiChat.contextThreads.push({ thread_id: threadId, subject: subject, email_count: emailCount, contact_name: contactName });
    updateContextCart();
    updateAiChatContextLabel();
    renderQuickPrompts();
    // Refresh picker to update button states
    if (aiChat.contextContact) selectContextContact(aiChat.contextContact.id, aiChat.contextContact.name, aiChat.contextContact.email);
}

function removeThreadFromContext(threadId) {
    aiChat.contextThreads = aiChat.contextThreads.filter(function(t) { return t.thread_id !== threadId; });
    updateContextCart();
    updateAiChatContextLabel();
    renderQuickPrompts();
}

function addAllContactThreads() {
    var threads = aiChat._availableThreads || [];
    threads.forEach(function(t) {
        if (!aiChat.contextThreads.some(function(ct) { return ct.thread_id === t.thread_id; })) {
            aiChat.contextThreads.push({
                thread_id: t.thread_id, subject: t.subject || '(No Subject)',
                email_count: t.email_count || 0, contact_name: (aiChat.contextContact && aiChat.contextContact.name) || 'Unknown'
            });
        }
    });
    updateContextCart();
    updateAiChatContextLabel();
    renderQuickPrompts();
    if (aiChat.contextContact) selectContextContact(aiChat.contextContact.id, aiChat.contextContact.name, aiChat.contextContact.email);
}

function clearContextCart() {
    aiChat.contextThreads = [];
    aiChat.contextContact = null;
    aiChat.contactMemories = [];
    aiChat._availableThreads = [];
    updateContextCart();
    updateAiChatContextLabel();
    renderQuickPrompts();
    var picker = document.getElementById('ai-context-thread-picker');
    if (picker) picker.style.display = 'none';
    var memDiv = document.getElementById('ai-context-memories');
    if (memDiv) memDiv.style.display = 'none';
}

function updateContextCart() {
    var cartDiv = document.getElementById('ai-context-cart');
    if (!cartDiv) return;
    if (aiChat.contextThreads.length === 0) { cartDiv.style.display = 'none'; return; }
    cartDiv.style.display = 'block';
    var totalEmails = aiChat.contextThreads.reduce(function(sum, t) { return sum + (t.email_count || 0); }, 0);
    cartDiv.innerHTML =
        '
' +
            'Context: ' + aiChat.contextThreads.length + ' thread' + (aiChat.contextThreads.length > 1 ? 's' : '') + ', ~' + totalEmails + ' emails' +
            '<button onclick="clearContextCart()" style="background:#7f1d1d; color:#fca5a5; border:none; border-radius:3px; padding:1px 6px; font-size:0.65em; cursor:pointer;">Clear All</button>' +
        '
' +
        '
' +
            aiChat.contextThreads.map(function(t) {
                return '
' +
                    '' + escapeHtml(t.subject.substring(0, 35)) + '' +
                    '<button onclick="removeThreadFromContext(\'' + t.thread_id + '\')" style="background:none; border:none; color:#f87171; cursor:pointer; font-size:1.1em; padding:0 2px;">\u00D7</button>' +
                '
';
            }).join('') +
        '
';
}

// --- Contact Memory ---
async function loadContactMemories(contactId) {
    var memDiv = document.getElementById('ai-context-memories');
    if (!memDiv) return;
    try {
        var data = await apiCall('ai_contact_memories', { contact_id: contactId });
        aiChat.contactMemories = data.memories || [];
        renderContactMemories();
    } catch (e) {
        aiChat.contactMemories = [];
    }
}

function renderContactMemories() {
    var memDiv = document.getElementById('ai-context-memories');
    if (!memDiv) return;
    if (aiChat.contactMemories.length === 0 && !aiChat.contextContact) { memDiv.style.display = 'none'; return; }
    memDiv.style.display = 'block';
    memDiv.innerHTML =
        '
' +
            'Memories (' + aiChat.contactMemories.length + ')' +
            '<button onclick="promptAddMemory()" style="background:#2d2d4e; color:#94a3b8; border:1px solid #3d3d5c; border-radius:3px; padding:1px 6px; font-size:0.63em; cursor:pointer;">+ Add</button>' +
        '
' +
        '
' +
            aiChat.contactMemories.map(function(m) {
                return '' +
                    (m.source === 'auto' ? '\u{1F916}' : '\u{1F4CC}') + ' ' + escapeHtml(m.fact.substring(0, 40)) + (m.fact.length > 40 ? '...' : '') +
                    ' <button onclick="deleteContactMemory(\'' + m.id + '\')" style="background:none;border:none;color:#f87171;cursor:pointer;font-size:1em;padding:0;line-height:1;">\u00D7</button>' +
                '';
            }).join('') +
        '
';
}

function promptAddMemory() {
    var fact = prompt('Enter a fact about this contact:');
    if (!fact || !aiChat.contextContact) return;
    fetch('api.php?action=ai_contact_memory_add', {
        method: 'POST', headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ contact_id: aiChat.contextContact.id, fact: fact })
    }).then(function(r) { return r.json(); }).then(function(data) {
        if (data.success && data.memory) {
            aiChat.contactMemories.push(data.memory);
            renderContactMemories();
        }
    });
}

function deleteContactMemory(memoryId) {
    if (!aiChat.contextContact) return;
    fetch('api.php?action=ai_contact_memory_delete', {
        method: 'POST', headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ contact_id: aiChat.contextContact.id, memory_id: memoryId })
    }).then(function(r) { return r.json(); }).then(function(data) {
        if (data.success) {
            aiChat.contactMemories = aiChat.contactMemories.filter(function(m) { return m.id !== memoryId; });
            renderContactMemories();
        }
    });
}

// --- Session Management ---
async function loadChatSessions() {
    var overlay = document.getElementById('ai-session-overlay');
    if (!overlay) return;
    overlay.style.display = 'flex';
    overlay.innerHTML = '
Loading sessions...
';
    try {
        var data = await apiCall('ai_chat_sessions', { limit: 20 });
        var sessions = data.sessions || [];
        if (sessions.length === 0) {
            overlay.innerHTML = '

No saved sessions
<button onclick="closeSessionOverlay()" style="margin-top:10px;background:#2d2d4e;color:#94a3b8;border:none;border-radius:6px;padding:5px 12px;font-size:0.75em;cursor:pointer;">Close</button>
';
            return;
        }
        var agentIcons = {}; Object.values(AI_AGENTS).forEach(function(a) { agentIcons[a.id] = a.icon; });
        overlay.innerHTML =
            '
' +
                'Chat History' +
                '<button onclick="closeSessionOverlay()" style="background:none;border:none;color:#94a3b8;cursor:pointer;font-size:1.1em;">\u2715</button>' +
            '
' +
            '
' +
                sessions.map(function(s) {
                    return '
' +
                        '
' +
                            '
' +
                                (agentIcons[s.agent] || '\u{1F4AC}') + ' ' + escapeHtml(s.title) +
                            '
' +
                            '
' + s.message_count + ' messages ' + (s.contact_name ? '\u00B7 ' + escapeHtml(s.contact_name) : '') + '
' +
                        '
' +
                        '
' +
                            '' + (s.updated_at ? s.updated_at.substring(0,10) : '') + '' +
                            '<button onclick="event.stopPropagation();deleteChatSession(\'' + s.session_id + '\')" style="background:none;border:none;color:#f87171;cursor:pointer;font-size:0.8em;" title="Delete">\u{1F5D1}</button>' +
                        '
' +
                    '
';
                }).join('') +
            '
';
    } catch (err) {
        overlay.innerHTML = '
Error: ' + escapeHtml(err.message) + '
';
    }
}

function closeSessionOverlay() {
    var overlay = document.getElementById('ai-session-overlay');
    if (overlay) overlay.style.display = 'none';
}

async function loadChatSession(sessionId) {
    closeSessionOverlay();
    try {
        var data = await apiCall('ai_chat_session', { session_id: sessionId });
        var session = data.session;
        if (!session) return;

        // Restore state
        aiChat.sessionId = session.session_id;
        aiChat.agent = session.agent || 'general';
        aiChat.contactId = session.contact_id || null;
        aiChat.contactName = session.contact_name || null;
        aiChat.contextThreads = (session.context_thread_ids || []).map(function(tid) { return { thread_id: tid, subject: '', email_count: 0, contact_name: session.contact_name || '' }; });
        aiChat.history = [];

        // Restore provider/model
        if (session.provider) {
            var ps = document.getElementById('ai-chat-provider');
            if (ps) { ps.value = session.provider; updateAiModelDropdown(); }
        }
        if (session.model) {
            var ms = document.getElementById('ai-chat-model');
            if (ms) ms.value = session.model;
        }

        // Restore agent button
        selectAgent(aiChat.agent);

        // Render messages
        var msgContainer = document.getElementById('ai-chat-messages');
        msgContainer.innerHTML = '';
        (session.messages || []).forEach(function(m) {
            if (m.role === 'user' || m.role === 'assistant') {
                appendAiChatMessage(m.role, m.content, m.model || null);
                aiChat.history.push({ role: m.role, content: m.content });
            }
        });

        updateContextCart();
        updateAiChatContextLabel();
        renderQuickPrompts();
        if (!aiChat.isOpen) toggleAiChat();
    } catch (err) {
        console.error('Failed to load session:', err);
    }
}

function startNewSession() {
    aiChat.sessionId = null;
    aiChat.history = [];
    aiChat.contextThreads = [];
    aiChat.contextContact = null;
    aiChat.contactMemories = [];
    aiChat._availableThreads = [];
    aiChat.threadId = null;
    aiChat.contactId = null;
    aiChat.emailId = null;
    aiChat.agent = 'general';
    selectAgent('general');
    clearAiChat();
    var picker = document.getElementById('ai-context-thread-picker');
    if (picker) { picker.style.display = 'none'; picker.innerHTML = ''; }
    var cart = document.getElementById('ai-context-cart');
    if (cart) cart.style.display = 'none';
    var memDiv = document.getElementById('ai-context-memories');
    if (memDiv) memDiv.style.display = 'none';
    updateAiChatContextLabel();
    renderQuickPrompts();
}

async function deleteChatSession(sessionId) {
    try {
        await fetch('api.php?action=ai_chat_session_delete', {
            method: 'POST', headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ session_id: sessionId })
        });
        loadChatSessions(); // refresh list
    } catch (e) { console.error(e); }
}

// --- Send AI Chat (enhanced) ---
async function sendAiChat() {
    var input = document.getElementById('ai-chat-input');
    var message = input.value.trim();
    if (!message) return;
    var model = document.getElementById('ai-chat-model').value;
    var providerSelect = document.getElementById('ai-chat-provider');
    var provider = providerSelect ? providerSelect.value : 'openai';
    var sendBtn = document.getElementById('ai-chat-send-btn');

    appendAiChatMessage('user', message);
    input.value = '';
    sendBtn.disabled = true; sendBtn.style.opacity = '0.5';
    aiChat.history.push({ role: 'user', content: message });
    var typingId = appendAiChatTyping();

    try {
        var payload = {
            message: message, model: model, provider: provider,
            mode: aiChat.mode,
            agent: aiChat.agent !== 'general' ? aiChat.agent : null,
            history: aiChat.history.slice(-10),
            session_id: aiChat.sessionId
        };

        // Multi-thread context
        if (aiChat.contextThreads.length > 0) {
            payload.thread_ids = aiChat.contextThreads.map(function(t) { return t.thread_id; });
        } else if (aiChat.threadId) {
            payload.thread_id = aiChat.threadId;
        }

        if (aiChat.contactId) payload.contact_id = aiChat.contactId;
        if (aiChat.emailId) payload.email_id = aiChat.emailId;

        var resp = await fetch('api.php?action=ai_chat', {
            method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload)
        });
        var data = await resp.json();
        removeAiChatTyping(typingId);

        if (data.success && data.answer) {
            // Store session ID from server
            if (data.session_id) aiChat.sessionId = data.session_id;

            aiChat.history.push({ role: 'assistant', content: data.answer });
            appendAiChatMessage('assistant', data.answer, data.model);

            // Show context stats if truncated
            if (data.context_stats && data.context_stats.truncated) {
                appendAiChatMessage('error', 'Note: Context was trimmed to fit model token limit (' + data.context_stats.estimated_tokens + ' tokens used)');
            }

            // Show extracted memories
            if (data.extracted_memories && data.extracted_memories.length > 0) {
                data.extracted_memories.forEach(function(m) { aiChat.contactMemories.push({ id: 'new_' + Date.now(), fact: m, source: 'auto' }); });
                renderContactMemories();
            }
        } else {
            appendAiChatMessage('error', (data.message || data.error || 'Unknown error'));
        }
    } catch (err) {
        removeAiChatTyping(typingId);
        appendAiChatMessage('error', 'Network error: ' + err.message);
    } finally {
        sendBtn.disabled = false; sendBtn.style.opacity = '1';
    }
}

function appendAiChatMessage(role, content, model) {
    var container = document.getElementById('ai-chat-messages');
    var isUser = role === 'user', isError = role === 'error';
    var wrapper = document.createElement('div');
    wrapper.style.cssText = 'display:flex;flex-direction:column;align-items:' + (isUser ? 'flex-end' : 'flex-start') + ';gap:4px;';
    var bubble = document.createElement('div');
    bubble.style.cssText = 'max-width:88%;padding:10px 13px;border-radius:' + (isUser ? '14px 14px 4px 14px' : '14px 14px 14px 4px') + ';font-size:0.82em;line-height:1.5;word-break:break-word;background:' + (isUser ? 'linear-gradient(135deg,#6366f1,#8b5cf6)' : isError ? '#3d1515' : '#252540') + ';color:' + (isUser ? '#fff' : isError ? '#fca5a5' : '#e2e8f0') + ';' + (isError ? 'border:1px solid #7f1d1d;' : '');
    if (!isUser && !isError) bubble.innerHTML = renderAiMarkdown(content);
    else bubble.textContent = content;
    wrapper.appendChild(bubble);

    if (!isUser && !isError) {
        var meta = document.createElement('div');
        meta.style.cssText = 'display:flex;gap:6px;align-items:center;';
        if (model) {
            var badge = document.createElement('span');
            badge.style.cssText = 'font-size:0.65em;color:#4a4a6a;background:#1e1e2e;padding:2px 6px;border-radius:4px;';
            badge.textContent = model; meta.appendChild(badge);
        }
        var copyBtn = document.createElement('button');
        copyBtn.style.cssText = 'font-size:0.65em;color:#6366f1;background:none;border:none;cursor:pointer;padding:2px 4px;';
        copyBtn.textContent = 'Copy';
        copyBtn.onclick = function() { navigator.clipboard.writeText(content).then(function() { copyBtn.textContent = 'Copied!'; setTimeout(function() { copyBtn.textContent = 'Copy'; }, 2000); }); };
        meta.appendChild(copyBtn);
        wrapper.appendChild(meta);
    }
    container.appendChild(wrapper);
    container.scrollTop = container.scrollHeight;
    return wrapper;
}

function appendAiChatTyping() {
    var container = document.getElementById('ai-chat-messages');
    var id = 'typing-' + Date.now();
    var el = document.createElement('div');
    el.id = id;
    el.innerHTML = '
...
';
    if (!document.getElementById('ai-chat-style')) {
        var s = document.createElement('style'); s.id = 'ai-chat-style';
        s.textContent = '@keyframes aiBounce{0%,80%,100%{opacity:0.3;transform:translateY(0)}40%{opacity:1;transform:translateY(-4px)}}';
        document.head.appendChild(s);
    }
    container.appendChild(el); container.scrollTop = container.scrollHeight;
    return id;
}

function removeAiChatTyping(id) { var el = document.getElementById(id); if (el) el.remove(); }

function renderAiMarkdown(text) {
    return text
        .replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
        .replace(/```([\s\S]*?)```/g, '
$1
')
        .replace(/`([^`]+)`/g, '$1')
        .replace(/\*\*([^*]+)\*\*/g, '$1').replace(/\*([^*]+)\*/g, '$1')
        .replace(/^### (.+)$/gm, '
$1
')
        .replace(/^## (.+)$/gm, '
$1
')
        .replace(/^# (.+)$/gm, '
$1
')
        .replace(/^[-*] (.+)$/gm, '
. $1
')
        .replace(/\n/g, '
');
}

function clearAiChat() {
    aiChat.history = [];
    document.getElementById('ai-chat-messages').innerHTML = '

AI

Chat cleared. Ask me anything!

';
}

// Draggable panel + initialization
document.addEventListener('DOMContentLoaded', function() {
    initAiProviderDropdown();
    var header = document.getElementById('ai-chat-header');
    var panel = document.getElementById('ai-chat-panel');
    if (!header || !panel) return;
    header.addEventListener('mousedown', function(e) {
        if (e.target.tagName === 'BUTTON') return;
        aiChat.isDragging = true;
        var rect = panel.getBoundingClientRect();
        aiChat.dragOffsetX = e.clientX - rect.left;
        aiChat.dragOffsetY = e.clientY - rect.top;
        panel.style.transition = 'none';
    });
    document.addEventListener('mousemove', function(e) {
        if (!aiChat.isDragging) return;
        panel.style.right = 'auto'; panel.style.bottom = 'auto';
        panel.style.left = Math.max(0, Math.min(e.clientX - aiChat.dragOffsetX, window.innerWidth - panel.offsetWidth)) + 'px';
        panel.style.top = Math.max(0, Math.min(e.clientY - aiChat.dragOffsetY, window.innerHeight - panel.offsetHeight)) + 'px';
    });
    document.addEventListener('mouseup', function() { aiChat.isDragging = false; });
});

+                        <button class="btn btn-sm" style="color:#ef4444;border:1px solid #ef4444;background:none;margin-left:4px;" onclick="event.stopPropagation(); deleteContact(${contact.id}, '${escapeHtml(contact.name || contact.email).replace(/'/g, "\\'")}');" title="Delete contact and all data">