The file /var/www/mssql.danmarcrm.com/dev1/extension2v1/public/thread_view.php has been updated successfully.

<?php
/**
 * Thread View Page
 *
 * Dedicated page for viewing email threads and sending to AI API.
 * Features:
 * - View thread emails with actual sender names
 * - Select additional threads from the same contact
 * - Send to AI API with provider/model selection
 * - Add custom questions and context
 * - Save and load AI responses
 *
 * @package EmailIntelligence
 */

require_once dirname(__DIR__) . '/config/config.php';
require_once dirname(__DIR__) . '/vendor/autoload.php';

use EmailIntelligence\MongoDatabase;
use EmailIntelligence\QueryAdapter;

session_start();

// Get thread ID from URL
$threadId = $_GET['thread_id'] ?? '';
if (empty($threadId)) {
    header('Location: index.php');
    exit;
}

// Get data via QueryAdapter (which uses MongoDB)
$adapter = QueryAdapter::getInstance();
$mongo = MongoDatabase::getInstance();

// Get thread details
$threadData = $adapter->getThreadDetails($threadId);
$emails = $threadData['emails'] ?? [];
$analysis = $threadData['analysis'] ?? null;

if (empty($emails)) {
    header('Location: index.php?error=thread_not_found');
    exit;
}

// Get the primary contact from this thread
// Priority: 1) Most frequent external contact, 2) First inbound email sender, 3) Any contact
$primaryContactId = null;
$primaryContactEmail = null;
$primaryContactName = null;
$userEmail = defined('GMAIL_USER_EMAIL') ? strtolower(GMAIL_USER_EMAIL) : '';

// Count contacts to find the most frequent one (excluding user)
$contactCounts = [];
$contactInfo = [];

foreach ($emails as $email) {
    if (!empty($email['contact_id'])) {
        $contactId = $email['contact_id'];
        $contactEmail = strtolower($email['contact_email'] ?? $email['from_email'] ?? '');

        // Skip if this is the user's own email
        if ($contactEmail === $userEmail) {
            continue;
        }

        if (!isset($contactCounts[$contactId])) {
            $contactCounts[$contactId] = 0;
            $contactInfo[$contactId] = [
                'email' => $email['contact_email'] ?? $email['from_email'],
                'name' => $email['contact_name'] ?? ''
            ];
        }
        $contactCounts[$contactId]++;
    }
}

// Select the most frequent contact
if (!empty($contactCounts)) {
    arsort($contactCounts);
    $primaryContactId = array_key_first($contactCounts);
    $primaryContactEmail = $contactInfo[$primaryContactId]['email'];
    $primaryContactName = $contactInfo[$primaryContactId]['name'];
} else {
    // Fallback: try to find any inbound email sender
    foreach ($emails as $email) {
        if (($email['direction'] ?? '') === 'inbound' && !empty($email['from_email'])) {
            $senderEmail = strtolower($email['from_email']);
            if ($senderEmail !== $userEmail) {
                $primaryContactId = $email['contact_id'] ?? null;
                $primaryContactEmail = $email['from_email'];
                $primaryContactName = $email['from_name'] ?? $email['contact_name'] ?? '';
                break;
            }
        }
    }
}

// Get other threads from the same contact for selection
$otherThreads = [];
if ($primaryContactId) {
    $contactThreads = $adapter->getThreads($primaryContactId, '', 50, 0);
    foreach ($contactThreads['threads'] as $t) {
        if ($t['thread_id'] !== $threadId) {
            $otherThreads[] = $t;
        }
    }
}

// Get saved AI responses for this thread from MongoDB
$savedResponsesLimit = 5; // Initial load limit
$savedResponses = $mongo->findAll('ai_query_responses',
    ['thread_ids' => $threadId],
    ['sort' => ['created_at' => -1], 'limit' => $savedResponsesLimit]
);

// Get total count of saved responses for this thread
$totalSavedResponses = $mongo->count('ai_query_responses', ['thread_ids' => $threadId]);

// Get first email's subject for page title
$threadSubject = $emails[0]['subject'] ?? '(No Subject)';

// Build participant map for displaying real names
$participantMap = [];
// $userEmail already defined above

foreach ($emails as $email) {
    $fromEmail = strtolower($email['from_email'] ?? '');
    if ($fromEmail && !isset($participantMap[$fromEmail])) {
        $name = '';
        if ($fromEmail === $userEmail) {
            $name = 'You';
        } elseif (!empty($email['from_name'])) {
            $name = $email['from_name'];
        } elseif (!empty($email['contact_name'])) {
            $name = $email['contact_name'];
        } else {
            // Extract name from email
            $parts = explode('@', $fromEmail);
            $nameParts = explode('.', $parts[0]);
            $name = ucfirst($nameParts[0]);
        }
        $participantMap[$fromEmail] = $name;
    }
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Thread: <?= htmlspecialchars($threadSubject) ?> - Email Intelligence</title>
    <link rel="stylesheet" href="assets/style.css">
    <style>
        :root {
            --bg-primary: #f5f7fa;
            --bg-secondary: #ffffff;
            --accent-primary: #667eea;
            --accent-secondary: #764ba2;
            --text-primary: #333;
            --text-secondary: #666;
            --border-color: #e0e0e0;
            --success: #10b981;
            --warning: #f59e0b;
            --error: #ef4444;
        }

        body {
            background: var(--bg-primary);
            margin: 0;
            padding: 0;
        }

        .page-container {
            max-width: 100%;
            margin: 0 auto;
            padding: 20px 30px;
        }

        .page-header {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            padding: 20px 30px;
            border-radius: 12px;
            margin-bottom: 20px;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .page-header h1 {
            margin: 0 0 5px 0;
            font-size: 1.5em;
        }

        .page-header .subtitle {
            opacity: 0.9;
            font-size: 0.9em;
        }

        .back-link {
            color: white;
            text-decoration: none;
            padding: 10px 20px;
            background: rgba(255,255,255,0.2);
            border-radius: 8px;
            transition: background 0.2s;
        }

        .back-link:hover {
            background: rgba(255,255,255,0.3);
        }

        .main-grid {
            display: grid;
            grid-template-columns: 1fr 500px;
            gap: 20px;
        }

        @media (max-width: 1200px) {
            .main-grid {
                grid-template-columns: 1fr;
            }
        }

        .card {
            background: var(--bg-secondary);
            border-radius: 12px;
            padding: 20px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.08);
            margin-bottom: 20px;
        }

        .card-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 15px;
            padding-bottom: 10px;
            border-bottom: 1px solid var(--border-color);
        }

        .card-header h2 {
            margin: 0;
            font-size: 1.2em;
            color: var(--text-primary);
        }

        /* Email Timeline */
        .email-timeline {
            max-height: 600px;
            overflow-y: auto;
        }

        .email-item {
            border-left: 4px solid #3b82f6;
            padding: 15px;
            margin-bottom: 15px;
            background: #f9fafb;
            border-radius: 0 8px 8px 0;
        }

        .email-item.inbound {
            border-left-color: #10b981;
        }

        .email-item.outbound {
            border-left-color: #3b82f6;
        }

        .email-meta {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 10px;
        }

        .sender-name {
            font-weight: 600;
            color: var(--text-primary);
        }

        .sender-email {
            color: var(--text-secondary);
            font-size: 0.85em;
        }

        .email-date {
            color: var(--text-secondary);
            font-size: 0.85em;
        }

        .email-body {
            font-size: 0.95em;
            line-height: 1.6;
            color: var(--text-primary);
            white-space: pre-wrap;
            word-wrap: break-word;
            max-height: 200px;
            overflow-y: auto;
            background: white;
            padding: 10px;
            border-radius: 6px;
        }

        /* Thread Selection */
        .thread-selection {
            max-height: 300px;
            overflow-y: auto;
        }

        .thread-checkbox {
            display: flex;
            align-items: flex-start;
            padding: 10px;
            margin-bottom: 8px;
            background: #f9fafb;
            border-radius: 8px;
            cursor: pointer;
            transition: background 0.2s;
        }

        .thread-checkbox:hover {
            background: #f0f0f5;
        }

        .thread-checkbox input {
            margin-right: 12px;
            margin-top: 3px;
        }

        .thread-checkbox-content {
            flex: 1;
        }

        .thread-checkbox-subject {
            font-weight: 500;
            margin-bottom: 4px;
        }

        .thread-checkbox-meta {
            font-size: 0.85em;
            color: var(--text-secondary);
        }

        /* AI Query Form */
        .ai-form-section {
            margin-bottom: 20px;
        }

        .ai-form-section label {
            display: block;
            font-weight: 600;
            margin-bottom: 8px;
            color: var(--text-primary);
        }

        .ai-form-section textarea,
        .ai-form-section input[type="text"],
        .ai-form-section select {
            width: 100%;
            padding: 12px;
            border: 1px solid var(--border-color);
            border-radius: 8px;
            font-family: inherit;
            font-size: 0.95em;
            box-sizing: border-box;
        }

        .ai-form-section textarea {
            min-height: 100px;
            resize: vertical;
        }

        .ai-form-section textarea:focus,
        .ai-form-section input:focus,
        .ai-form-section select:focus {
            outline: none;
            border-color: var(--accent-primary);
        }

        /* Provider Selection */
        .provider-grid {
            display: grid;
            grid-template-columns: repeat(2, 1fr);
            gap: 10px;
        }

        .provider-option {
            display: flex;
            align-items: center;
            padding: 10px;
            background: #f9fafb;
            border: 1px solid var(--border-color);
            border-radius: 8px;
            cursor: pointer;
            transition: all 0.2s;
        }

        .provider-option:hover {
            border-color: var(--accent-primary);
        }

        .provider-option.selected {
            background: rgba(102, 126, 234, 0.1);
            border-color: var(--accent-primary);
        }

        .provider-option input {
            margin-right: 8px;
        }

        .provider-option .provider-name {
            font-weight: 500;
            font-size: 0.9em;
        }

        .model-select {
            margin-top: 8px;
            padding: 6px 10px;
            font-size: 0.85em;
            background: white;
        }

        /* Buttons */
        .btn {
            padding: 12px 24px;
            border: none;
            border-radius: 8px;
            font-size: 1em;
            font-weight: 500;
            cursor: pointer;
            transition: all 0.2s;
            display: inline-flex;
            align-items: center;
            gap: 8px;
        }

        .btn-primary {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
        }

        .btn-primary:hover {
            transform: translateY(-2px);
            box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
        }

        .btn-primary:disabled {
            opacity: 0.6;
            cursor: not-allowed;
            transform: none;
        }

        .btn-secondary {
            background: #e5e7eb;
            color: var(--text-primary);
        }

        .btn-secondary:hover {
            background: #d1d5db;
        }

        /* AI Response */
        .ai-response-card {
            background: #f0fdf4;
            border: 2px solid #22c55e;
            border-radius: 12px;
            padding: 20px;
            margin-bottom: 15px;
        }

        .ai-response-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 10px;
        }

        .ai-response-provider {
            font-weight: 600;
            color: #166534;
        }

        .ai-response-meta {
            font-size: 0.85em;
            color: #166534;
        }

        .ai-response-content {
            white-space: pre-wrap;
            line-height: 1.7;
        }

        /* Saved Responses */
        .saved-response {
            background: #f9fafb;
            border: 2px solid var(--border-color);
            border-radius: 8px;
            padding: 15px;
            margin-bottom: 10px;
            transition: all 0.2s ease;
            cursor: pointer;
        }

        .saved-response:hover {
            border-color: var(--accent-primary);
            background: #f0f4ff;
        }

        .saved-response.last-viewed {
            border-color: var(--accent-primary);
            background: linear-gradient(135deg, rgba(102, 126, 234, 0.08) 0%, rgba(118, 75, 162, 0.08) 100%);
            box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.15);
        }

        .saved-response.last-viewed::before {
            content: '← Last Viewed';
            position: absolute;
            right: 10px;
            top: -10px;
            background: var(--accent-primary);
            color: white;
            font-size: 0.7em;
            padding: 2px 8px;
            border-radius: 4px;
        }

        .saved-response {
            position: relative;
        }

        .saved-response-header {
            display: flex;
            justify-content: space-between;
            margin-bottom: 8px;
        }

        .saved-response-question {
            font-weight: 500;
            color: var(--text-primary);
            flex: 1;
            margin-right: 10px;
        }

        .saved-response-date {
            font-size: 0.85em;
            color: var(--text-secondary);
            white-space: nowrap;
        }

        .saved-response-preview {
            font-size: 0.85em;
            color: var(--accent-primary);
            font-weight: 600;
            margin-bottom: 8px;
        }

        .saved-response-excerpt {
            font-size: 0.85em;
            color: var(--text-secondary);
            background: rgba(255,255,255,0.7);
            padding: 8px;
            border-radius: 4px;
            margin-bottom: 10px;
            border-left: 3px solid #ddd;
            font-style: italic;
            line-height: 1.4;
        }

        .saved-response-actions {
            display: flex;
            gap: 8px;
            margin-top: 10px;
        }

        .saved-response-actions .btn-view,
        .saved-response-actions .btn-expand {
            padding: 6px 12px;
            border: none;
            border-radius: 4px;
            font-size: 0.8em;
            cursor: pointer;
            transition: all 0.2s;
        }

        .saved-response-actions .btn-view {
            background: #e0e7ff;
            color: #4338ca;
        }

        .saved-response-actions .btn-view:hover {
            background: #c7d2fe;
        }

        .saved-response-actions .btn-expand {
            background: #dbeafe;
            color: #1d4ed8;
        }

        .saved-response-actions .btn-expand:hover {
            background: #bfdbfe;
        }

        /* Quick View Modal */
        .quick-view-overlay {
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: rgba(0,0,0,0.7);
            z-index: 2000;
            display: flex;
            justify-content: center;
            align-items: flex-start;
            padding: 40px 20px;
            overflow-y: auto;
        }

        .quick-view-modal {
            background: white;
            border-radius: 12px;
            max-width: 900px;
            width: 100%;
            max-height: calc(100vh - 80px);
            overflow-y: auto;
            box-shadow: 0 20px 60px rgba(0,0,0,0.3);
        }

        .quick-view-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 15px 20px;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            border-radius: 12px 12px 0 0;
            position: sticky;
            top: 0;
            z-index: 10;
        }

        .quick-view-header h3 {
            margin: 0;
            font-size: 1.1em;
        }

        .quick-view-close {
            background: rgba(255,255,255,0.2);
            border: none;
            color: white;
            padding: 8px 16px;
            border-radius: 6px;
            cursor: pointer;
            font-size: 0.9em;
        }

        .quick-view-close:hover {
            background: rgba(255,255,255,0.3);
        }

        .quick-view-content {
            padding: 20px;
        }

        .quick-view-meta {
            display: flex;
            gap: 20px;
            margin-bottom: 15px;
            padding-bottom: 15px;
            border-bottom: 1px solid #e5e7eb;
            flex-wrap: wrap;
        }

        .quick-view-meta-item {
            font-size: 0.9em;
        }

        .quick-view-meta-item strong {
            color: var(--accent-primary);
        }

        .quick-view-question {
            background: #f0f9ff;
            padding: 15px;
            border-radius: 8px;
            margin-bottom: 15px;
            border-left: 4px solid #3b82f6;
        }

        .quick-view-response {
            background: #f0fdf4;
            padding: 15px;
            border-radius: 8px;
            border-left: 4px solid #22c55e;
            white-space: pre-wrap;
            line-height: 1.7;
        }

        /* Loading */
        .loading-spinner {
            display: inline-block;
            width: 20px;
            height: 20px;
            border: 3px solid rgba(255,255,255,0.3);
            border-radius: 50%;
            border-top-color: white;
            animation: spin 1s linear infinite;
        }

        @keyframes spin {
            to { transform: rotate(360deg); }
        }

        .loading-overlay {
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: rgba(0,0,0,0.5);
            display: flex;
            justify-content: center;
            align-items: center;
            z-index: 1000;
            display: none;
        }

        .loading-overlay.active {
            display: flex;
        }

        .loading-box {
            background: white;
            padding: 30px 50px;
            border-radius: 12px;
            text-align: center;
        }

        .loading-box .spinner {
            width: 40px;
            height: 40px;
            border: 4px solid #e5e7eb;
            border-top-color: var(--accent-primary);
            border-radius: 50%;
            animation: spin 1s linear infinite;
            margin: 0 auto 15px;
        }

        /* Direction badge */
        .badge-direction {
            display: inline-block;
            padding: 2px 8px;
            border-radius: 4px;
            font-size: 0.75em;
            font-weight: 600;
            text-transform: uppercase;
        }

        .badge-inbound {
            background: #d1fae5;
            color: #065f46;
        }

        .badge-outbound {
            background: #dbeafe;
            color: #1e40af;
        }

        /* Tabs for responses */
        .response-tabs {
            display: flex;
            gap: 10px;
            margin-bottom: 15px;
            border-bottom: 2px solid var(--border-color);
            padding-bottom: 10px;
        }

        .response-tab {
            padding: 8px 16px;
            background: none;
            border: none;
            cursor: pointer;
            font-size: 0.9em;
            color: var(--text-secondary);
            border-bottom: 2px solid transparent;
            margin-bottom: -12px;
        }

        .response-tab.active {
            color: var(--accent-primary);
            border-bottom-color: var(--accent-primary);
            font-weight: 600;
        }

        .response-content {
            display: none;
        }

        .response-content.active {
            display: block;
        }
        /* Agent Buttons */
        .agent-buttons {
            display: flex;
            flex-wrap: wrap;
            gap: 6px;
            margin-bottom: 8px;
        }

        .agent-btn {
            padding: 6px 12px;
            border: 2px solid var(--border-color);
            border-radius: 8px;
            background: #f9fafb;
            cursor: pointer;
            font-size: 0.85em;
            font-weight: 500;
            transition: all 0.2s;
            display: flex;
            align-items: center;
            gap: 4px;
        }

        .agent-btn:hover {
            border-color: var(--accent-primary);
            background: #f0f4ff;
        }

        .agent-btn.active {
            border-color: var(--accent-primary);
            background: rgba(102, 126, 234, 0.15);
            color: var(--accent-primary);
            font-weight: 600;
        }

        .agent-quick-prompts {
            display: flex;
            flex-wrap: wrap;
            gap: 4px;
            margin-top: 6px;
        }

        .quick-prompt-btn {
            padding: 4px 10px;
            border: 1px solid #d1d5db;
            border-radius: 12px;
            background: white;
            cursor: pointer;
            font-size: 0.78em;
            color: var(--text-secondary);
            transition: all 0.2s;
        }

        .quick-prompt-btn:hover {
            border-color: var(--accent-primary);
            color: var(--accent-primary);
            background: #f0f4ff;
        }

        /* Context Builder */
        .context-builder-toggle {
            padding: 6px 14px;
            border: 1px solid var(--border-color);
            border-radius: 6px;
            background: white;
            cursor: pointer;
            font-size: 0.85em;
            transition: all 0.2s;
        }

        .context-builder-toggle:hover {
            border-color: var(--accent-primary);
        }

        #context-search {
            width: 100%;
            padding: 10px 12px;
            border: 1px solid var(--border-color);
            border-radius: 8px;
            font-size: 0.9em;
            box-sizing: border-box;
            margin-bottom: 10px;
        }

        #context-search:focus {
            outline: none;
            border-color: var(--accent-primary);
        }

        .context-search-results {
            max-height: 400px;
            overflow-y: auto;
        }

        .context-result-item {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 8px 10px;
            border-radius: 6px;
            margin-bottom: 4px;
            background: #f9fafb;
            font-size: 0.85em;
        }

        .context-result-item:hover {
            background: #f0f4ff;
        }

        .context-result-info {
            flex: 1;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
            margin-right: 8px;
        }

        .context-result-meta {
            font-size: 0.8em;
            color: var(--text-secondary);
        }

        .context-add-btn {
            padding: 3px 10px;
            border: 1px solid var(--success);
            border-radius: 4px;
            background: white;
            color: var(--success);
            cursor: pointer;
            font-size: 0.8em;
            font-weight: 600;
            white-space: nowrap;
        }

        .context-add-btn:hover {
            background: var(--success);
            color: white;
        }

        .context-cart {
            background: #f0fdf4;
            border: 1px solid #86efac;
            border-radius: 8px;
            padding: 10px;
            margin-top: 8px;
        }

        .context-cart-item {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 4px 0;
            font-size: 0.85em;
        }

        .context-cart-remove {
            color: #ef4444;
            cursor: pointer;
            font-weight: bold;
            padding: 2px 6px;
            border: none;
            background: none;
        }

        .context-cart-remove:hover {
            background: #fef2f2;
            border-radius: 4px;
        }

        /* Token Estimate */
        .token-estimate {
            font-size: 0.85em;
            padding: 8px 12px;
            border-radius: 6px;
            margin-bottom: 10px;
        }

        .token-estimate.ok {
            background: #f0fdf4;
            color: #166534;
        }

        .token-estimate.warning {
            background: #fffbeb;
            color: #92400e;
        }

        .token-estimate.danger {
            background: #fef2f2;
            color: #991b1b;
        }

        /* Contact Memory Pills */
        .memory-pills {
            display: flex;
            flex-wrap: wrap;
            gap: 4px;
            margin-top: 6px;
        }

        .memory-pill {
            display: inline-flex;
            align-items: center;
            gap: 4px;
            padding: 3px 8px;
            background: #ede9fe;
            border-radius: 12px;
            font-size: 0.75em;
            color: #5b21b6;
        }

        .btn-small {
            padding: 6px 12px;
            font-size: 0.85em;
        }

        /* Two-Round & Follow-up Controls */
        .round-options {
            background: #f0f4ff;
            border: 1px solid #c7d2fe;
            border-radius: 8px;
            padding: 10px 14px;
            margin-bottom: 12px;
        }

        .round-options label {
            font-weight: 500 !important;
            font-size: 0.9em;
            cursor: pointer;
            display: flex;
            align-items: flex-start;
            gap: 8px;
        }

        .round-options label input[type="checkbox"] {
            margin-top: 3px;
        }

        .round-options .option-desc {
            font-size: 0.8em;
            color: var(--text-secondary);
            margin-top: 2px;
        }

        .response-select-bar {
            display: flex;
            align-items: center;
            gap: 8px;
            padding: 10px 15px;
            background: #ede9fe;
            border-radius: 8px;
            margin-top: 10px;
        }

        .response-select-bar .btn {
            padding: 6px 14px;
            font-size: 0.85em;
        }

        .response-checkbox {
            display: flex;
            align-items: center;
            gap: 6px;
            margin-bottom: 8px;
            padding: 4px 8px;
            background: rgba(102, 126, 234, 0.06);
            border-radius: 6px;
        }

        .response-checkbox input[type="checkbox"] {
            width: 16px;
            height: 16px;
            cursor: pointer;
        }

        .response-checkbox label {
            font-size: 0.85em;
            font-weight: 500;
            cursor: pointer;
        }

        .round-badge {
            display: inline-block;
            padding: 2px 8px;
            border-radius: 4px;
            font-size: 0.7em;
            font-weight: 700;
            text-transform: uppercase;
            margin-left: 6px;
        }

        .round-badge.round-1 {
            background: #dbeafe;
            color: #1e40af;
        }

        .round-badge.round-2 {
            background: #d1fae5;
            color: #065f46;
        }

        .provider-timeout {
            background: #fffbeb;
            border: 1px solid #f59e0b;
            color: #92400e;
            padding: 10px;
            border-radius: 8px;
            font-size: 0.85em;
        }

    </style>
    <script src="https://d3js.org/d3.v7.min.js"></script>
</head>
<body>
    <div class="page-container">
        <!-- Header -->
        <div class="page-header">
            <div>
                <h1><?= htmlspecialchars($threadSubject) ?></h1>
                <div class="subtitle">
                    <?= count($emails) ?> emails
                    <?php if ($primaryContactName): ?>
                        with <?= htmlspecialchars($primaryContactName) ?>
                    <?php endif; ?>
                    | Thread ID: <?= htmlspecialchars($threadId) ?>
                </div>
            </div>
            <a href="index.php" class="back-link">Back to Dashboard</a>
        </div>

        <div class="main-grid">
            <!-- Left Column: Email Thread -->
            <div class="left-column">
                <!-- Email Timeline -->
                <div class="card">
                    <div class="card-header">
                        <h2>Email Conversation</h2>
                        <span class="email-count"><?= count($emails) ?> emails</span>
                    </div>
                    <div class="email-timeline" id="email-timeline">
                        <?php foreach ($emails as $index => $email):
                            $senderEmail = strtolower($email['from_email'] ?? '');
                            $senderName = $participantMap[$senderEmail] ?? 'Unknown';
                            $isInbound = ($email['direction'] ?? '') === 'inbound';
                            $body = $email['body_text'] ?? $email['body_html'] ?? '(No content)';
                            // Truncate for display
                            $displayBody = strlen($body) > 1500 ? substr($body, 0, 1500) . '...' : $body;
                        ?>
                        <div class="email-item <?= $isInbound ? 'inbound' : 'outbound' ?>">
                            <div class="email-meta">
                                <div>
                                    <span class="sender-name"><?= htmlspecialchars($senderName) ?></span>
                                    <span class="badge-direction <?= $isInbound ? 'badge-inbound' : 'badge-outbound' ?>">
                                        <?= $isInbound ? 'Received' : 'Sent' ?>
                                    </span>
                                    <br>
                                    <span class="sender-email"><?= htmlspecialchars($email['from_email'] ?? '') ?></span>
                                </div>
                                <span class="email-date"><?= date('M j, Y g:i A', strtotime($email['date'])) ?></span>
                            </div>
                            <div class="email-body"><?= htmlspecialchars($displayBody) ?></div>
                        </div>
                        <?php endforeach; ?>
                    </div>
                </div>

                <!-- AI Response Display -->
                <div class="card" id="ai-response-container" style="display: none;">
                    <div class="card-header">
                        <h2>AI Response <span class="round-badge round-1" id="response-round-badge">Round 1</span></h2>
                        <button class="btn btn-secondary" onclick="closeResponse()">Close</button>
                    </div>
                    <div class="response-tabs" id="response-tabs"></div>
                    <div id="ai-responses-content"></div>
                    <!-- Follow-up bar: select responses + ask follow-up -->
                    <div class="response-select-bar" id="response-select-bar" style="display:none;">
                        <span style="font-size:0.85em;font-weight:600;">Use responses as context:</span>
                        <button class="btn btn-small" onclick="selectAllResponses()">All</button>
                        <button class="btn btn-small" onclick="deselectAllResponses()">None</button>
                        <button class="btn btn-primary btn-small" onclick="askFollowUp()" style="margin-left:auto;">Ask Follow-up</button>
                        <button class="btn btn-small" onclick="generateChart()" style="background:#8b5cf6;color:white;">Generate Chart</button>
                    </div>
                </div>

                <!-- Round 2 Response Display -->
                <div class="card" id="ai-response-round2-container" style="display: none;">
                    <div class="card-header">
                        <h2>AI Response <span class="round-badge round-2">Round 2 - Summary</span></h2>
                        <button class="btn btn-secondary" onclick="document.getElementById('ai-response-round2-container').style.display='none'">Close</button>
                    </div>
                    <div class="response-tabs" id="response-tabs-round2"></div>
                    <div id="ai-responses-content-round2"></div>
                </div>

                <!-- D3.js Chart Container -->
                <div id="d3-chart-container" style="display:none; margin-bottom:20px;">
                    <div class="card">
                        <div class="card-header">
                            <h2>Relationship Chart</h2>
                            <button class="btn btn-secondary" onclick="document.getElementById('d3-chart-container').style.display='none'">Close</button>
                        </div>
                        <div id="d3-chart" style="width:100%;height:600px;background:#fafafa;border-radius:0 0 8px 8px;position:relative;"></div>
                        <div id="chart-tooltip" style="display:none;position:absolute;padding:8px 12px;background:rgba(0,0,0,0.85);color:white;border-radius:6px;font-size:0.85em;pointer-events:none;z-index:10;max-width:300px;"></div>
                    </div>
                </div>

                <!-- Saved Responses -->
                <?php if (!empty($savedResponses)): ?>
                <div class="card">
                    <div class="card-header">
                        <h2>Previous AI Analyses <span style="font-weight: normal; font-size: 0.85em; color: var(--text-secondary);">(<?= $totalSavedResponses ?> total)</span></h2>
                    </div>
                    <div id="saved-responses">
                        <?php foreach ($savedResponses as $saved):
                            // Determine what to display as the "question"
                            $displayQuestion = $saved['question'] ?? 'General Analysis';
                            $hasContext = !empty($saved['context']);
                            $isDefaultQuestion = strpos($displayQuestion, 'Please analyze this email conversation') === 0;

                            // If question is the default and user provided context, show context instead
                            if ($isDefaultQuestion && $hasContext) {
                                $displayQuestion = '[Context] ' . $saved['context'];
                            } elseif ($hasContext && !$isDefaultQuestion) {
                                // If both custom question and context exist, show question + indicator
                                $displayQuestion = $displayQuestion . ' (+context)';
                            }

                            // Truncate to 200 chars instead of 100
                            $displayQuestion = strlen($displayQuestion) > 200
                                ? substr($displayQuestion, 0, 200) . '...'
                                : $displayQuestion;

                            // Get response excerpt for easier identification
                            $responseText = $saved['response']['response'] ?? '';
                            $responseExcerpt = strlen($responseText) > 120
                                ? substr($responseText, 0, 120) . '...'
                                : $responseText;
                        ?>
                        <div class="saved-response" id="saved-response-<?= $saved['_id'] ?>" data-id="<?= $saved['_id'] ?>">
                            <div class="saved-response-header">
                                <span class="saved-response-question">
                                    <?= htmlspecialchars($displayQuestion) ?>
                                </span>
                                <span class="saved-response-date">
                                    <?= date('M j, Y g:i A', strtotime($saved['created_at'] ?? 'now')) ?>
                                </span>
                            </div>
                            <div class="saved-response-preview">
                                <strong><?= htmlspecialchars($saved['provider'] ?? 'Unknown') ?></strong> /
                                <?= htmlspecialchars($saved['model'] ?? 'Unknown') ?>
                            </div>
                            <?php if ($responseExcerpt): ?>
                            <div class="saved-response-excerpt">
                                <?= htmlspecialchars($responseExcerpt) ?>
                            </div>
                            <?php endif; ?>
                            <div class="saved-response-actions">
                                <button class="btn-view" onclick="event.stopPropagation(); quickViewResponse('<?= $saved['_id'] ?>')">
                                    👁 Quick View
                                </button>
                                <button class="btn-expand" onclick="event.stopPropagation(); loadSavedResponse('<?= $saved['_id'] ?>')">
                                    📄 Full View
                                </button>
                                <button style="color:#ef4444;background:none;border:1px solid #ef4444;padding:4px 8px;border-radius:4px;cursor:pointer;font-size:0.85em;" onclick="event.stopPropagation(); deleteAiResponse('<?= $saved['_id'] ?>')">
                                    Del
                                </button>
                            </div>
                        </div>
                        <?php endforeach; ?>
                    </div>
                    <?php if ($totalSavedResponses > $savedResponsesLimit): ?>
                    <div id="load-more-container" style="text-align: center; margin-top: 15px;">
                        <button class="btn btn-secondary" id="load-more-btn" onclick="loadMoreResponses()"
                                data-offset="<?= $savedResponsesLimit ?>"
                                data-total="<?= $totalSavedResponses ?>">
                            Load More (<?= $totalSavedResponses - $savedResponsesLimit ?> remaining)
                        </button>
                    </div>
                    <?php endif; ?>
                </div>
                <?php endif; ?>
            </div>

            <!-- Right Column: AI Query Panel -->
            <div class="right-column">
                <!-- Additional Threads Selection -->
                <?php if (!empty($otherThreads)): ?>
                <div class="card">
                    <div class="card-header">
                        <h2>Include Other Threads</h2>
                    </div>
                    <p style="font-size: 0.9em; color: var(--text-secondary); margin-bottom: 15px;">
                        Select additional threads from <?= htmlspecialchars($primaryContactName ?: 'this contact') ?> to include in the AI analysis:
                    </p>
                    <div class="thread-selection">
                        <?php foreach (array_slice($otherThreads, 0, 10) as $thread): ?>
                        <label class="thread-checkbox">
                            <input type="checkbox" name="additional_threads[]" value="<?= htmlspecialchars($thread['thread_id']) ?>">
                            <div class="thread-checkbox-content">
                                <div class="thread-checkbox-subject"><?= htmlspecialchars($thread['subject'] ?? '(No Subject)') ?></div>
                                <div class="thread-checkbox-meta">
                                    <?= $thread['email_count'] ?? 0 ?> emails |
                                    <?= date('M j, Y', strtotime($thread['last_date'] ?? 'now')) ?>
                                </div>
                            </div>
                        </label>
                        <?php endforeach; ?>
                    </div>
                </div>
                <?php endif; ?>

                <!-- Context Builder: Search & add threads from any contact -->
                <div class="card">
                    <div class="card-header">
                        <h2>+ Add More Context</h2>
                        <button class="context-builder-toggle" onclick="toggleContextBuilder()">Search</button>
                    </div>
                    <div id="context-builder" style="display:none;">
                        <input type="text" id="context-search" placeholder="Search contacts or threads..." oninput="debounceContextSearch()">
                        <div class="context-search-results" id="context-search-results"></div>
                    </div>
                    <div id="context-cart"></div>
                    <div id="contact-memories"></div>
                </div>

                <!-- AI Query Form -->
                <div class="card">
                    <div class="card-header">
                        <h2>Ask AI</h2>
                    </div>

                    <!-- Agent Selector -->
                    <div class="ai-form-section">
                        <label>AI Agent</label>
                        <div class="agent-buttons" id="agent-buttons">
                            <!-- Populated by JS on load -->
                        </div>
                        <div class="agent-quick-prompts" id="agent-quick-prompts" style="display:none;">
                            <!-- Quick prompt buttons per agent -->
                        </div>
                    </div>

                    <!-- Provider Selection -->
                    <div class="ai-form-section">
                        <label>Select AI Provider</label>
                        <div class="provider-grid" id="provider-grid">
                            <!-- Loaded via JavaScript -->
                            <p style="color: var(--text-secondary);">Loading providers...</p>
                        </div>
                    </div>

                    <!-- Content Processing Mode -->
                    <div class="ai-form-section">
                        <label for="content-mode">Email Content Processing</label>
                        <select id="content-mode" style="width: 100%;" onchange="updateTokenEstimate()">
                            <option value="full">Full Content (include all quoted replies)</option>
                            <option value="stripped" selected>Stripped (remove quoted replies - saves tokens)</option>
                            <option value="smart">Smart Deduplication (quotes only in last email)</option>
                        </select>
                    </div>

                    <!-- Question -->
                    <div class="ai-form-section">
                        <label for="ai-question">Your Question</label>
                        <textarea id="ai-question" placeholder="What would you like to know about this conversation?" oninput="updateTokenEstimate()"></textarea>
                    </div>

                    <!-- Additional Context -->
                    <div class="ai-form-section">
                        <label for="ai-context">Additional Context (Optional)</label>
                        <textarea id="ai-context" placeholder="Add any additional context the AI should know..." oninput="updateTokenEstimate()"></textarea>
                    </div>

                    <!-- Token Estimation -->
                    <div class="token-estimate ok" id="token-estimate">
                        Estimated: ~0 tokens
                    </div>

                    <!-- Two-Round & Timeout Options -->
                    <div class="round-options">
                        <label>
                            <input type="checkbox" id="two-round-mode">
                            <div>
                                Auto Two-Round AI
                                <div class="option-desc">Round 1: all providers answer independently. Round 2: all providers receive everyone's Round 1 answers and produce a unified summary.</div>
                            </div>
                        </label>
                        <div style="margin-top:8px;">
                            <label style="font-size:0.85em;">
                                Provider timeout:
                                <select id="provider-timeout" style="padding:3px 6px;border-radius:4px;border:1px solid #ccc;font-size:0.9em;">
                                    <option value="60">60s</option>
                                    <option value="90">90s</option>
                                    <option value="120" selected>120s</option>
                                    <option value="180">180s</option>
                                    <option value="300">300s</option>
                                </select>
                                <span class="option-desc" style="display:inline;">(skip slow providers after this)</span>
                            </label>
                        </div>
                    </div>

                    <!-- Submit Button -->
                    <button class="btn btn-primary" id="submit-ai-btn" onclick="submitToAI()" style="width: 100%;">
                        Send to AI
                    </button>
                </div>
            </div>
        </div>
    </div>

    <!-- Loading Overlay -->
    <div class="loading-overlay" id="loading-overlay">
        <div class="loading-box">
            <div class="spinner"></div>
            <p id="loading-message">Analyzing with AI...</p>
        </div>
    </div>

    <!-- Quick View Modal -->
    <div class="quick-view-overlay" id="quick-view-overlay" style="display: none;" onclick="closeQuickView(event)">
        <div class="quick-view-modal" onclick="event.stopPropagation()">
            <div class="quick-view-header">
                <h3 id="quick-view-title">AI Response</h3>
                <button class="quick-view-close" onclick="closeQuickView()">✕ Close (Esc)</button>
            </div>
            <div class="quick-view-content">
                <div class="quick-view-meta" id="quick-view-meta"></div>
                <div id="quick-view-question-section"></div>
                <div class="quick-view-response" id="quick-view-response"></div>
            </div>
        </div>
    </div>

    <script>
        // Current thread data
        const currentThreadId = '<?= addslashes($threadId) ?>';
        const primaryContactId = <?= $primaryContactId ? $primaryContactId : 'null' ?>;

        // Build email content for AI from PHP
        const emailContent = <?= json_encode(array_map(function($email) use ($participantMap) {
            $senderEmail = strtolower($email['from_email'] ?? '');
            $senderName = $participantMap[$senderEmail] ?? 'Unknown';
            return [
                'from' => $senderName,
                'from_email' => $email['from_email'] ?? '',
                'date' => $email['date'] ?? '',
                'direction' => $email['direction'] ?? '',
                'body' => $email['body_text'] ?? $email['body_html'] ?? ''
            ];
        }, $emails)) ?>;

        // Provider data
        let providers = {};
        let selectedProvider = null;
        let selectedModel = null;

        // Primary contact info from PHP
        const primaryContactName = '<?= addslashes($primaryContactName ?: '') ?>';
        const primaryContactEmail = '<?= addslashes($primaryContactEmail ?: '') ?>';

        // ============================================
        // AI AGENTS
        // ============================================
        const AI_AGENTS = {
            general: {
                name: 'General',
                icon: '\uD83D\uDCAC',
                systemPrompt: 'You are a highly capable business intelligence AI assistant. Analyze the provided email conversations thoroughly. Provide clear, actionable insights. Be direct and specific.',
                quickPrompts: [
                    'Summarize the key points',
                    'What are the action items?',
                    'What is the overall sentiment?',
                    'What should I reply?'
                ]
            },
            quote_analyst: {
                name: 'Quotes',
                icon: '\uD83D\uDCB0',
                systemPrompt: 'You are a pricing and quotation analyst. Focus on:\n- All quoted prices, amounts, and financial figures\n- Price changes over time and negotiation patterns\n- Scope of work vs pricing alignment\n- Competitor pricing if mentioned\n- Negotiation leverage points\n- Budget signals and willingness to pay\nAlways present findings with specific numbers and dates.',
                quickPrompts: [
                    'What prices have been quoted?',
                    'Show the negotiation history',
                    'What is the pricing trend?',
                    'What should we quote next?'
                ]
            },
            deception_detector: {
                name: 'Deception',
                icon: '\uD83D\uDD0D',
                systemPrompt: 'You are a deception and inconsistency detection specialist. Analyze the emails for:\n- Contradictions between different messages\n- Promises made but not kept\n- Vague or evasive responses to direct questions\n- Sudden changes in tone or position\n- Pressure tactics or urgency manipulation\n- Information that doesn\'t add up\n- Omissions of relevant details\nRate the overall deception risk as LOW/MEDIUM/HIGH with evidence.',
                quickPrompts: [
                    'Any contradictions detected?',
                    'Rate the deception risk level',
                    'Are there broken promises?',
                    'Any pressure tactics used?'
                ]
            },
            contact_profiler: {
                name: 'Profiler',
                icon: '\uD83D\uDC64',
                systemPrompt: 'You are a contact profiling specialist. Build a psychological and business profile of the contact based on their emails:\n- Communication style (formal/casual, verbose/concise)\n- Decision-making patterns\n- Negotiation style and tendencies\n- Responsiveness and reliability\n- Priorities and motivations\n- Relationship health indicators\n- Loyalty signals\nProvide specific examples from the emails to support your assessment.',
                quickPrompts: [
                    'Build a full profile',
                    'Communication style analysis',
                    'How loyal is this contact?',
                    'What motivates them?'
                ]
            },
            email_composer: {
                name: 'Composer',
                icon: '\u270D\uFE0F',
                systemPrompt: 'You are a professional email composer. Based on the conversation context:\n- Draft a professional, well-structured reply\n- Match the appropriate tone (formal/casual based on the thread)\n- Address all open questions or action items\n- Be concise but thorough\n- Include appropriate greetings and sign-offs\nOutput ONLY the email draft, ready to send.',
                quickPrompts: [
                    'Draft a reply',
                    'Write a follow-up email',
                    'Compose a polite decline',
                    'Draft a meeting request'
                ]
            }
        };

        let selectedAgent = 'general';

        // ============================================
        // TOKEN ESTIMATION
        // ============================================
        const MODEL_LIMITS = {
            'claude-opus-4-6': 200000,
            'claude-sonnet-4-6': 200000,
            'claude-sonnet-4-5-20250929': 200000,
            'claude-3-5-sonnet-20241022': 200000,
            'claude-opus-4-5-20250929': 200000,
            'claude-3-opus-20240229': 200000,
            'claude-3-5-haiku-20241022': 200000,
            'gpt-4o': 128000,
            'gpt-4o-mini': 128000,
            'gpt-4-turbo': 128000,
            'gpt-4.1': 1000000,
            'gpt-4.1-mini': 1000000,
            'o3': 200000,
            'o3-mini': 200000,
            'o4-mini': 200000,
            'gemini-2.0-flash': 1048576,
            'gemini-2.5-pro-preview-05-06': 1048576,
            'gemini-2.5-flash-preview-04-17': 1048576,
            'grok-3': 131072,
            'grok-3-mini': 131072,
            'deepseek-chat': 64000,
            'deepseek-reasoner': 64000,
            'qwen-max': 32000,
            'qwen-plus': 131000,
            'mistral-large-latest': 128000,
            'command-a-03-2025': 256000,
            'command-r-plus': 128000
        };

        function getSmallestModelLimit() {
            var selectedProviders = getSelectedProviders();
            var minLimit = Infinity;
            selectedProviders.forEach(function(prov) {
                var modelSelect = document.getElementById('model_' + prov);
                var model = modelSelect ? modelSelect.value : '';
                var limit = MODEL_LIMITS[model] || 128000;
                if (limit < minLimit) minLimit = limit;
            });
            return minLimit === Infinity ? 128000 : minLimit;
        }

        function estimateCurrentTokens() {
            var content = buildEmailContent();
            var question = (document.getElementById('ai-question').value || '').trim();
            var context = (document.getElementById('ai-context').value || '').trim();
            var agent = AI_AGENTS[selectedAgent];
            var total = content.length;
            if (agent) total += agent.systemPrompt.length;
            if (question) total += question.length;
            if (context) total += context.length;
            // Add conversation history estimate
            total += buildConversationContext().length;
            // Add context threads + checkbox threads estimate
            // Use emailCount * 500 chars per stripped email (realistic average)
            var checkboxThreads = getSelectedThreads();
            contextThreads.forEach(function(ct) { total += ct.emailCount * 500; });
            total += checkboxThreads.length * 5000; // rough estimate for checkbox threads
            return Math.ceil(total / 3.5);
        }

        function updateTokenEstimate() {
            var el = document.getElementById('token-estimate');
            if (!el) return;
            var tokens = estimateCurrentTokens();
            var limit = getSmallestModelLimit();
            var pct = (tokens / limit) * 100;
            var formatted = tokens.toLocaleString();

            if (pct < 60) {
                el.className = 'token-estimate ok';
                el.textContent = 'Estimated: ~' + formatted + ' tokens (OK)';
            } else if (pct < 90) {
                el.className = 'token-estimate warning';
                el.textContent = 'Estimated: ~' + formatted + ' tokens (approaching limit)';
            } else {
                el.className = 'token-estimate danger';
                el.textContent = 'Estimated: ~' + formatted + ' tokens (EXCEEDS limit!)';
            }
        }

        // ============================================
        // CONTEXT BUILDER
        // ============================================
        var contextThreads = []; // {threadId, subject, emailCount}
        var contextSearchTimeout = null;
        var contactMemories = [];

        function toggleContextBuilder() {
            var el = document.getElementById('context-builder');
            if (el.style.display === 'none') {
                el.style.display = 'block';
                document.getElementById('context-search').focus();
            } else {
                el.style.display = 'none';
            }
        }

        function debounceContextSearch() {
            clearTimeout(contextSearchTimeout);
            contextSearchTimeout = setTimeout(function() {
                var q = document.getElementById('context-search').value.trim();
                if (q.length >= 2) {
                    searchContext(q);
                } else {
                    document.getElementById('context-search-results').innerHTML = '';
                }
            }, 300);
        }

        function searchContext(query) {
            var resultsEl = document.getElementById('context-search-results');
            resultsEl.innerHTML = '<p style="color:var(--text-secondary);font-size:0.85em;">Searching...</p>';

            // Search contacts and threads in parallel — higher limit for threads
            Promise.all([
                fetch('api.php?action=contacts&search=' + encodeURIComponent(query) + '&limit=10').then(function(r) { return r.json(); }),
                fetch('api.php?action=threads&search=' + encodeURIComponent(query) + '&limit=100').then(function(r) { return r.json(); })
            ]).then(function(results) {
                var contacts = results[0].contacts || [];
                var threads = results[1].threads || [];
                var threadTotal = (results[1].pagination && results[1].pagination.total) || threads.length;
                var html = '';

                if (contacts.length > 0) {
                    html += '<div style="font-size:0.8em;font-weight:600;color:var(--text-secondary);margin:8px 0 4px;">CONTACTS (' + contacts.length + ')</div>';
                    contacts.forEach(function(c) {
                        html += '<div class="context-result-item">';
                        html += '<div class="context-result-info">';
                        html += '<strong>' + escapeHtml(c.name || c.email) + '</strong>';
                        html += '<div class="context-result-meta">' + escapeHtml(c.email || '') + ' | ' + (c.email_count || 0) + ' emails</div>';
                        html += '</div>';
                        html += '<button class="context-add-btn" onclick="loadContactThreads(' + (c.id || c.contact_id) + ', \'' + escapeHtml(c.name || '') + '\')">View Threads</button>';
                        html += '</div>';
                    });
                }

                if (threads.length > 0) {
                    // Store for "Add All" functionality
                    window._lastSearchThreads = threads;

                    html += '<div style="font-size:0.8em;font-weight:600;color:var(--text-secondary);margin:8px 0 4px;">THREADS (' + threadTotal + ' found' + (threadTotal > threads.length ? ', showing ' + threads.length : '') + ')</div>';

                    // "Add All" button
                    var notAddedCount = threads.filter(function(t) {
                        return t.thread_id !== currentThreadId && !contextThreads.some(function(ct) { return ct.threadId === t.thread_id; });
                    }).length;
                    if (notAddedCount > 1) {
                        html += '<div style="margin-bottom:6px;"><button class="context-add-btn" onclick="addAllSearchThreads()" style="width:100%;text-align:center;padding:6px;font-weight:600;">+ Add All ' + notAddedCount + ' Threads</button></div>';
                    }

                    threads.forEach(function(t) {
                        var already = contextThreads.some(function(ct) { return ct.threadId === t.thread_id; });
                        var isCurrent = t.thread_id === currentThreadId;
                        html += '<div class="context-result-item">';
                        html += '<div class="context-result-info">';
                        html += '<strong>' + escapeHtml(t.subject || '(No Subject)') + '</strong>';
                        html += '<div class="context-result-meta">' + (t.email_count || 0) + ' emails | ' + escapeHtml(t.contact_name || '') + '</div>';
                        html += '</div>';
                        if (isCurrent) {
                            html += '<span style="font-size:0.75em;color:var(--text-secondary);">Current</span>';
                        } else if (already) {
                            html += '<span style="font-size:0.75em;color:var(--success);">Added</span>';
                        } else {
                            html += '<button class="context-add-btn" onclick="addThreadToContext(\'' + t.thread_id + '\', \'' + escapeHtml(t.subject || '').replace(/'/g, "\\'") + '\', ' + (t.email_count || 0) + ')">+ Add</button>';
                        }
                        html += '</div>';
                    });

                    // If more threads exist, show "Load More" button
                    if (threadTotal > threads.length) {
                        html += '<div style="margin-top:6px;"><button class="context-add-btn" onclick="loadMoreSearchThreads(\'' + escapeHtml(query).replace(/'/g, "\\'") + '\', ' + threads.length + ', ' + threadTotal + ')" style="width:100%;text-align:center;padding:6px;color:var(--text-secondary);">Load More (' + (threadTotal - threads.length) + ' remaining)</button></div>';
                    }
                }

                if (!html) {
                    html = '<p style="color:var(--text-secondary);font-size:0.85em;">No results found</p>';
                }

                resultsEl.innerHTML = html;
            }).catch(function(err) {
                resultsEl.innerHTML = '<p style="color:#ef4444;font-size:0.85em;">Search failed</p>';
            });
        }

        function addAllSearchThreads() {
            var threads = window._lastSearchThreads || [];
            var added = 0;
            threads.forEach(function(t) {
                if (t.thread_id !== currentThreadId && !contextThreads.some(function(ct) { return ct.threadId === t.thread_id; })) {
                    contextThreads.push({
                        threadId: t.thread_id,
                        subject: t.subject || '(No Subject)',
                        emailCount: t.email_count || 0
                    });
                    added++;
                }
            });
            updateContextCart();
            updateTokenEstimate();
            // Refresh to show "Added" labels
            var q = document.getElementById('context-search').value.trim();
            if (q) searchContext(q);
        }

        function loadMoreSearchThreads(query, currentCount, total) {
            var page = Math.floor(currentCount / 100) + 1;
            fetch('api.php?action=threads&search=' + encodeURIComponent(query) + '&limit=100&page=' + (page + 1))
                .then(function(r) { return r.json(); })
                .then(function(data) {
                    var newThreads = data.threads || [];
                    if (newThreads.length > 0) {
                        window._lastSearchThreads = (window._lastSearchThreads || []).concat(newThreads);
                        // Re-render with all threads
                        searchContext(query);
                    }
                });
        }

        function loadContactThreads(contactId, contactName) {
            var resultsEl = document.getElementById('context-search-results');
            resultsEl.innerHTML = '<p style="color:var(--text-secondary);font-size:0.85em;">Loading threads for ' + escapeHtml(contactName) + '...</p>';

            fetch('api.php?action=threads&contact=' + contactId + '&limit=20')
                .then(function(r) { return r.json(); })
                .then(function(data) {
                    var threads = data.threads || [];
                    var html = '<div style="font-size:0.8em;font-weight:600;color:var(--text-secondary);margin:4px 0;">Threads from ' + escapeHtml(contactName) + '</div>';

                    if (threads.length > 1) {
                        html += '<div style="margin-bottom:6px;"><button class="context-add-btn" onclick="addAllContactThreads()" style="width:100%;text-align:center;">Add All ' + threads.length + ' Threads</button></div>';
                    }

                    // Store for "Add All"
                    window._lastContactThreads = threads;

                    threads.forEach(function(t) {
                        var already = contextThreads.some(function(ct) { return ct.threadId === t.thread_id; });
                        var isCurrent = t.thread_id === currentThreadId;
                        html += '<div class="context-result-item">';
                        html += '<div class="context-result-info">';
                        html += '<strong>' + escapeHtml(t.subject || '(No Subject)') + '</strong>';
                        html += '<div class="context-result-meta">' + (t.email_count || 0) + ' emails</div>';
                        html += '</div>';
                        if (isCurrent) {
                            html += '<span style="font-size:0.75em;color:var(--text-secondary);">Current</span>';
                        } else if (already) {
                            html += '<span style="font-size:0.75em;color:var(--success);">Added</span>';
                        } else {
                            html += '<button class="context-add-btn" onclick="addThreadToContext(\'' + t.thread_id + '\', \'' + escapeHtml(t.subject || '').replace(/'/g, "\\'") + '\', ' + (t.email_count || 0) + ')">+ Add</button>';
                        }
                        html += '</div>';
                    });

                    resultsEl.innerHTML = html;

                    // Also load contact memories
                    loadContactMemories(contactId);
                });
        }

        function addAllContactThreads() {
            var threads = window._lastContactThreads || [];
            threads.forEach(function(t) {
                if (t.thread_id !== currentThreadId && !contextThreads.some(function(ct) { return ct.threadId === t.thread_id; })) {
                    contextThreads.push({
                        threadId: t.thread_id,
                        subject: t.subject || '(No Subject)',
                        emailCount: t.email_count || 0
                    });
                }
            });
            updateContextCart();
            updateTokenEstimate();
            // Refresh search results to show "Added" labels
            var q = document.getElementById('context-search').value.trim();
            if (q) searchContext(q);
        }

        function addThreadToContext(threadId, subject, emailCount) {
            if (threadId === currentThreadId) return;
            if (contextThreads.some(function(ct) { return ct.threadId === threadId; })) return;
            contextThreads.push({
                threadId: threadId,
                subject: subject,
                emailCount: emailCount
            });
            updateContextCart();
            updateTokenEstimate();
            // Refresh search results
            var q = document.getElementById('context-search').value.trim();
            if (q) searchContext(q);
        }

        function removeThreadFromContext(threadId) {
            contextThreads = contextThreads.filter(function(ct) { return ct.threadId !== threadId; });
            updateContextCart();
            updateTokenEstimate();
        }

        function updateContextCart() {
            var el = document.getElementById('context-cart');
            if (contextThreads.length === 0) {
                el.innerHTML = '';
                return;
            }
            var totalEmails = contextThreads.reduce(function(sum, ct) { return sum + ct.emailCount; }, 0);
            var html = '<div class="context-cart">';
            html += '<div style="font-size:0.85em;font-weight:600;margin-bottom:6px;">' + contextThreads.length + ' additional thread(s), ~' + totalEmails + ' emails</div>';
            contextThreads.forEach(function(ct) {
                html += '<div class="context-cart-item">';
                html += '<span style="overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">' + escapeHtml(ct.subject) + ' (' + ct.emailCount + ')</span>';
                html += '<button class="context-cart-remove" onclick="removeThreadFromContext(\'' + ct.threadId + '\')">×</button>';
                html += '</div>';
            });
            html += '</div>';
            el.innerHTML = html;
        }

        // ============================================
        // CONTACT MEMORY
        // ============================================
        function loadContactMemories(contactId) {
            fetch('api.php?action=ai_contact_memories&contact_id=' + contactId)
                .then(function(r) { return r.json(); })
                .then(function(data) {
                    contactMemories = data.memories || [];
                    renderContactMemories();
                })
                .catch(function() { contactMemories = []; });
        }

        function renderContactMemories() {
            var el = document.getElementById('contact-memories');
            if (!contactMemories.length) {
                el.innerHTML = '';
                return;
            }
            var html = '<div style="margin-top:8px;font-size:0.8em;font-weight:600;color:var(--text-secondary);">Known Facts</div>';
            html += '<div class="memory-pills">';
            contactMemories.forEach(function(m) {
                html += '<span class="memory-pill">' + escapeHtml(m.fact || m) + '</span>';
            });
            html += '</div>';
            el.innerHTML = html;
        }

        // ============================================
        // AGENT SELECTION
        // ============================================
        function initAgentButtons() {
            var container = document.getElementById('agent-buttons');
            if (!container) return;
            var html = '';
            for (var id in AI_AGENTS) {
                var agent = AI_AGENTS[id];
                html += '<button class="agent-btn' + (id === selectedAgent ? ' active' : '') + '" data-agent="' + id + '" onclick="selectAgent(\'' + id + '\')">';
                html += agent.icon + ' ' + agent.name;
                html += '</button>';
            }
            container.innerHTML = html;
            updateAgentQuickPrompts();
        }

        function selectAgent(agentId) {
            selectedAgent = agentId;
            // Update button styles
            document.querySelectorAll('.agent-btn').forEach(function(btn) {
                btn.classList.toggle('active', btn.dataset.agent === agentId);
            });
            updateAgentQuickPrompts();
            updateTokenEstimate();
        }

        function updateAgentQuickPrompts() {
            var container = document.getElementById('agent-quick-prompts');
            if (!container) return;
            var agent = AI_AGENTS[selectedAgent];
            if (!agent || !agent.quickPrompts || !agent.quickPrompts.length) {
                container.style.display = 'none';
                return;
            }
            container.style.display = 'flex';
            var html = '';
            agent.quickPrompts.forEach(function(prompt) {
                html += '<button class="quick-prompt-btn" onclick="useQuickPrompt(\'' + prompt.replace(/'/g, "\\'") + '\')">' + escapeHtml(prompt) + '</button>';
            });
            container.innerHTML = html;
        }

        function useQuickPrompt(prompt) {
            document.getElementById('ai-question').value = prompt;
            updateTokenEstimate();
        }

        // ============================================
        // DEBUG LOGGING
        // ============================================
        function logAiDebug(data) {
            try {
                fetch('api.php?action=log_ai_debug', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify(data)
                });
            } catch (e) {
                console.error('Debug log failed:', e);
            }
        }

        // ============================================
        // TWO-ROUND AI & FOLLOW-UP
        // ============================================
        var lastRound1Responses = {}; // {providerName: {success, response, model, ...}}
        var lastRound1Providers = [];
        var lastRound1Models = {};
        var lastRound1Question = '';
        var lastFullContent = '';

        // Send Round 2 with all Round 1 responses as context
        function sendRound2(round1Responses, providersList, modelsMap, originalQuestion) {
            var successfulResponses = [];
            for (var prov in round1Responses) {
                var r = round1Responses[prov];
                if (r && r.success && r.response) {
                    successfulResponses.push({
                        provider: prov,
                        model: r.model || 'unknown',
                        response: r.response
                    });
                }
            }

            if (successfulResponses.length === 0) {
                console.warn('No successful Round 1 responses for Round 2');
                return;
            }

            // Build Round 2 prompt
            var round2Content = '=== ROUND 2: UNIFIED ANALYSIS ===\n';
            round2Content += 'You previously analyzed an email conversation. Below are the analyses from ' + successfulResponses.length + ' different AI models.\n';
            round2Content += 'Your task: Synthesize ALL analyses into a single comprehensive summary. Highlight where models AGREE and where they DISAGREE. Provide a final unified assessment.\n\n';
            round2Content += 'Original question: ' + originalQuestion + '\n\n';

            successfulResponses.forEach(function(sr, idx) {
                round2Content += '--- ANALYSIS FROM ' + sr.provider.toUpperCase() + ' (' + sr.model + ') ---\n';
                // Cap each response to 4000 chars to stay within limits
                var respText = sr.response;
                if (respText.length > 4000) {
                    respText = respText.substring(0, 4000) + '\n...(truncated)';
                }
                round2Content += respText + '\n\n';
            });

            round2Content += '=== END OF ROUND 1 ANALYSES ===\n\n';
            round2Content += 'Please provide a unified, comprehensive summary that synthesizes all the above analyses. Note agreements, disagreements, and your overall assessment.';

            // Check token budget
            var r2Tokens = Math.ceil(round2Content.length / 3.5);
            var modelLimit = getSmallestModelLimit();
            var maxChars = Math.floor(modelLimit * 3.5 * 0.85);
            if (round2Content.length > maxChars) {
                round2Content = round2Content.substring(0, maxChars) + '\n\n...(Content truncated to fit model limit)';
                r2Tokens = Math.ceil(round2Content.length / 3.5);
            }

            console.log('Round 2: ' + round2Content.length + ' chars, ~' + r2Tokens + ' tokens, ' + providersList.length + ' providers');

            document.getElementById('loading-overlay').classList.add('active');
            document.getElementById('loading-message').textContent = 'Round 2: Synthesizing with ' + providersList.length + ' AI(s)...';

            var requestBody = {
                user_content: round2Content,
                providers: providersList
            };
            for (var p in modelsMap) {
                requestBody['model_' + p] = modelsMap[p];
            }

            var timeoutSec = parseInt(document.getElementById('provider-timeout').value) || 120;

            logAiDebug({
                type: 'request_round2',
                thread_id: currentThreadId,
                agent: selectedAgent,
                providers: providersList,
                content_length: round2Content.length,
                estimated_tokens: r2Tokens,
                question: 'Round 2 synthesis of ' + successfulResponses.length + ' responses'
            });

            fetchWithTimeout('../aiapi/api_opus/api/query.php', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(requestBody)
            }, timeoutSec * 1000)
            .then(function(response) {
                if (!response.ok) throw new Error('HTTP ' + response.status);
                return response.text();
            })
            .then(function(text) {
                var data;
                try { data = JSON.parse(text); } catch(e) { throw new Error('Invalid JSON from AI API'); }

                if (data.success) {
                    displayRound2Response(data);

                    // Save Round 2 responses
                    providersList.forEach(function(prov) {
                        var pr = data.responses[prov + '_response'];
                        if (pr && pr.success) {
                            saveAIResponse({
                                thread_ids: [currentThreadId],
                                contact_id: primaryContactId,
                                question: 'Round 2 Synthesis: ' + originalQuestion,
                                agent: selectedAgent,
                                content_mode: 'round2',
                                content_length: round2Content.length,
                                estimated_tokens: r2Tokens,
                                provider: prov,
                                model: modelsMap[prov],
                                query_id: data.query_id,
                                response: pr,
                                total_cost: pr.cost || 0,
                                all_providers: providersList,
                                round: 2
                            });
                        }
                    });

                    logAiDebug({
                        type: 'response_round2',
                        thread_id: currentThreadId,
                        providers: providersList,
                        question: 'Round 2 synthesis'
                    });
                } else {
                    alert('Round 2 failed: ' + (data.error || 'Unknown error'));
                }
            })
            .catch(function(err) {
                console.error('Round 2 error:', err);
                alert('Round 2 error: ' + err.message);
            })
            .finally(function() {
                document.getElementById('loading-overlay').classList.remove('active');
                document.getElementById('submit-ai-btn').disabled = false;
            });
        }

        // Display Round 2 responses in separate container
        function displayRound2Response(data) {
            var container = document.getElementById('ai-response-round2-container');
            var tabsContainer = document.getElementById('response-tabs-round2');
            var contentContainer = document.getElementById('ai-responses-content-round2');

            container.style.display = 'block';
            tabsContainer.innerHTML = '';
            contentContainer.innerHTML = '';

            var tabIndex = 0;
            for (var key in data.responses) {
                var response = data.responses[key];
                var providerName = key.replace('_response', '');
                var providerConfig = providers[providerName] || { name: providerName };

                var tab = document.createElement('button');
                tab.className = 'response-tab' + (tabIndex === 0 ? ' active' : '');
                tab.textContent = providerConfig.name;
                tab.dataset.index = tabIndex;
                (function(idx) {
                    tab.onclick = function() { switchRound2Tab(idx); };
                })(tabIndex);
                tabsContainer.appendChild(tab);

                var content = document.createElement('div');
                content.className = 'response-content' + (tabIndex === 0 ? ' active' : '');
                content.id = 'response-tab-r2-' + tabIndex;

                if (response.success) {
                    content.innerHTML = '<div class="ai-response-card" style="border-color:#10b981;">' +
                        '<div class="ai-response-header">' +
                        '<span class="ai-response-provider">' + escapeHtml(providerConfig.name) + ' <span class="round-badge round-2">Round 2</span></span>' +
                        '<span class="ai-response-meta">Model: ' + escapeHtml(response.model || '?') + ' | Time: ' + (response.request_time ? response.request_time.toFixed(2) : '?') + 's | Cost: $' + (response.cost ? response.cost.toFixed(6) : '0') + '</span>' +
                        '</div>' +
                        '<div class="ai-response-content">' + escapeHtml(response.response) + '</div>' +
                        '</div>';
                } else {
                    content.innerHTML = '<div style="background:#fef2f2;border:1px solid #ef4444;padding:15px;border-radius:8px;">' +
                        '<strong style="color:#dc2626;">Error:</strong>' +
                        '<p style="margin:5px 0 0;color:#991b1b;">' + escapeHtml(response.error || 'Unknown error') + '</p></div>';
                }

                contentContainer.appendChild(content);
                tabIndex++;
            }

            container.scrollIntoView({ behavior: 'smooth' });
        }

        function switchRound2Tab(index) {
            var tabs = document.getElementById('response-tabs-round2');
            var content = document.getElementById('ai-responses-content-round2');
            if (!tabs || !content) return;
            tabs.querySelectorAll('.response-tab').forEach(function(t) {
                t.classList.toggle('active', parseInt(t.dataset.index) === index);
            });
            content.querySelectorAll('.response-content').forEach(function(c, i) {
                c.classList.toggle('active', i === index);
            });
        }

        // Manual follow-up: user selects responses and asks a new question
        function askFollowUp() {
            var question = prompt('Enter your follow-up question:');
            if (!question || !question.trim()) return;

            // Collect selected responses
            var selected = getSelectedResponseTexts();
            if (selected.length === 0) {
                alert('Please select at least one response to include as context.');
                return;
            }

            // Build follow-up content
            var followUpContent = '=== FOLLOW-UP QUESTION ===\n';
            followUpContent += 'The user previously asked about an email conversation and received the following AI analyses.\n';
            followUpContent += 'Now they have a follow-up question based on these analyses.\n\n';

            selected.forEach(function(sel) {
                followUpContent += '--- Previous analysis from ' + sel.provider.toUpperCase() + ' (' + sel.model + ') ---\n';
                var text = sel.response;
                if (text.length > 4000) text = text.substring(0, 4000) + '\n...(truncated)';
                followUpContent += text + '\n\n';
            });

            followUpContent += '=== END PREVIOUS ANALYSES ===\n\n';
            followUpContent += 'FOLLOW-UP QUESTION: ' + question.trim();

            // Use currently selected providers
            var providersList = getSelectedProviders();
            if (providersList.length === 0) {
                alert('No AI providers selected.');
                return;
            }

            var modelsMap = {};
            providersList.forEach(function(prov) {
                var sel = document.getElementById('model_' + prov);
                modelsMap[prov] = sel ? sel.value : '';
            });

            // Token check
            var tokens = Math.ceil(followUpContent.length / 3.5);
            var modelLimit = getSmallestModelLimit();
            var maxChars = Math.floor(modelLimit * 3.5 * 0.85);
            if (followUpContent.length > maxChars) {
                followUpContent = followUpContent.substring(0, maxChars) + '\n\n...(truncated)';
            }

            console.log('Follow-up: ' + followUpContent.length + ' chars, ' + providersList.length + ' providers');

            document.getElementById('loading-overlay').classList.add('active');
            document.getElementById('loading-message').textContent = 'Sending follow-up to ' + providersList.length + ' AI(s)...';
            document.getElementById('submit-ai-btn').disabled = true;

            var requestBody = {
                user_content: followUpContent,
                providers: providersList
            };
            for (var p in modelsMap) {
                requestBody['model_' + p] = modelsMap[p];
            }

            var timeoutSec = parseInt(document.getElementById('provider-timeout').value) || 120;

            logAiDebug({
                type: 'request_followup',
                thread_id: currentThreadId,
                providers: providersList,
                content_length: followUpContent.length,
                question: question.trim()
            });

            fetchWithTimeout('../aiapi/api_opus/api/query.php', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(requestBody)
            }, timeoutSec * 1000)
            .then(function(response) {
                if (!response.ok) throw new Error('HTTP ' + response.status);
                return response.text();
            })
            .then(function(text) {
                var data;
                try { data = JSON.parse(text); } catch(e) { throw new Error('Invalid JSON'); }
                if (data.success) {
                    // Show in Round 2 container
                    displayRound2Response(data);

                    // Add to conversation history
                    for (var key in data.responses) {
                        var r = data.responses[key];
                        if (r && r.success && r.response) {
                            var pName = key.replace('_response', '');
                            addToConversation(question.trim(), r.response, pName, modelsMap[pName] || '', '');
                            break;
                        }
                    }

                    // Save
                    providersList.forEach(function(prov) {
                        var pr = data.responses[prov + '_response'];
                        if (pr && pr.success) {
                            saveAIResponse({
                                thread_ids: [currentThreadId],
                                contact_id: primaryContactId,
                                question: 'Follow-up: ' + question.trim(),
                                agent: selectedAgent,
                                content_mode: 'followup',
                                content_length: followUpContent.length,
                                provider: prov,
                                model: modelsMap[prov],
                                query_id: data.query_id,
                                response: pr,
                                total_cost: pr.cost || 0,
                                round: 'followup'
                            });
                        }
                    });
                } else {
                    alert('Follow-up failed: ' + (data.error || 'Unknown'));
                }
            })
            .catch(function(err) {
                alert('Follow-up error: ' + err.message);
            })
            .finally(function() {
                document.getElementById('loading-overlay').classList.remove('active');
                document.getElementById('submit-ai-btn').disabled = false;
            });
        }

        // Get selected response texts for follow-up
        function getSelectedResponseTexts() {
            var result = [];
            document.querySelectorAll('.response-select-cb:checked').forEach(function(cb) {
                result.push({
                    provider: cb.dataset.provider,
                    model: cb.dataset.model || '',
                    response: cb.dataset.responseText || ''
                });
            });
            return result;
        }

        function selectAllResponses() {
            document.querySelectorAll('.response-select-cb').forEach(function(cb) { cb.checked = true; });
        }

        function deselectAllResponses() {
            document.querySelectorAll('.response-select-cb').forEach(function(cb) { cb.checked = false; });
        }

        // Fetch with timeout using AbortController
        function fetchWithTimeout(url, options, timeoutMs) {
            var controller = new AbortController();
            options.signal = controller.signal;
            var timeoutId = setTimeout(function() { controller.abort(); }, timeoutMs);

            return fetch(url, options).then(function(response) {
                clearTimeout(timeoutId);
                return response;
            }).catch(function(err) {
                clearTimeout(timeoutId);
                if (err.name === 'AbortError') {
                    throw new Error('Request timed out after ' + (timeoutMs / 1000) + 's');
                }
                throw err;
            });
        }

        // ============================================
        // D3.JS CHART GENERATION
        // ============================================
        async function generateChart() {
            // Collect AI response text from the displayed response
            var responseEls = document.querySelectorAll('#ai-responses-content .ai-response-content, #ai-responses-content-round2 .ai-response-content');
            var responseText = '';
            responseEls.forEach(function(el) {
                if (el.innerText) responseText += el.innerText + '\n\n';
            });
            if (!responseText.trim()) {
                alert('No AI response to chart. Submit a query first.');
                return;
            }

            var chartPrompt = 'Based on the following analysis of an email conversation, generate a JSON object for a relationship/entity chart.\n' +
                'Return ONLY valid JSON with this exact structure (no other text, no markdown):\n' +
                '{"nodes": [{"id": "unique_id", "label": "Display Name", "type": "person|company|topic|event|action", "details": "brief description"}], ' +
                '"links": [{"source": "node_id", "target": "node_id", "label": "relationship description", "strength": 1}]}\n' +
                'Extract key people, companies, topics, events, and actions mentioned. Show relationships between them.\n' +
                'Use strength 1-10 where 10 is strongest relationship.\n\n' +
                'Analysis:\n' + responseText.substring(0, 6000);

            document.getElementById('d3-chart-container').style.display = 'block';
            document.getElementById('d3-chart').innerHTML = '<p style="text-align:center;padding:40px;color:#6b7280;">Generating chart data with AI...</p>';

            try {
                // Send to AI proxy to generate structured JSON
                var providersList = getSelectedProviders();
                var provider = providersList.length > 0 ? providersList[0] : 'claude';
                var modelSel = document.getElementById('model_' + provider);
                var model = modelSel ? modelSel.value : 'claude-sonnet-4-6';

                var requestBody = {
                    user_content: chartPrompt,
                    providers: [provider]
                };
                requestBody['model_' + provider] = model;

                var response = await fetch('../aiapi/api_opus/api/query.php', {
                    method: 'POST',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify(requestBody)
                });
                var data = await response.json();

                if (!data.success) throw new Error(data.error || 'AI request failed');

                // Extract the response text
                var aiResponse = '';
                for (var key in data.responses) {
                    var r = data.responses[key];
                    if (r && r.success && r.response) {
                        aiResponse = r.response;
                        break;
                    }
                }
                if (!aiResponse) throw new Error('No AI response received');

                // Parse JSON from response
                var jsonMatch = aiResponse.match(/\{[\s\S]*\}/);
                if (!jsonMatch) throw new Error('AI did not return valid JSON. Response: ' + aiResponse.substring(0, 200));

                var chartData = JSON.parse(jsonMatch[0]);
                if (!chartData.nodes || !chartData.links) throw new Error('Invalid chart data: missing nodes or links');

                renderD3Chart(chartData);
            } catch(e) {
                document.getElementById('d3-chart').innerHTML = '<p style="color:#ef4444;padding:20px;text-align:center;">Error: ' + escapeHtml(e.message) + '</p>';
            }
        }

        function renderD3Chart(data) {
            var container = document.getElementById('d3-chart');
            container.innerHTML = '';
            var width = container.clientWidth || 800;
            var height = 600;

            var typeColors = {
                person: '#3b82f6',
                company: '#10b981',
                topic: '#f59e0b',
                event: '#ef4444',
                action: '#8b5cf6'
            };

            var svg = d3.select('#d3-chart').append('svg')
                .attr('width', width)
                .attr('height', height)
                .style('background', '#fafafa');

            var g = svg.append('g');

            // Zoom
            svg.call(d3.zoom().scaleExtent([0.3, 4]).on('zoom', function(event) {
                g.attr('transform', event.transform);
            }));

            // Arrow markers
            svg.append('defs').selectAll('marker')
                .data(['arrow'])
                .enter().append('marker')
                .attr('id', 'chart-arrow')
                .attr('viewBox', '0 -5 10 10')
                .attr('refX', 28)
                .attr('refY', 0)
                .attr('markerWidth', 6)
                .attr('markerHeight', 6)
                .attr('orient', 'auto')
                .append('path')
                .attr('d', 'M0,-5L10,0L0,5')
                .attr('fill', '#94a3b8');

            var simulation = d3.forceSimulation(data.nodes)
                .force('link', d3.forceLink(data.links).id(function(d) { return d.id; }).distance(140))
                .force('charge', d3.forceManyBody().strength(-400))
                .force('center', d3.forceCenter(width / 2, height / 2))
                .force('collision', d3.forceCollide().radius(45));

            // Links
            var link = g.append('g').selectAll('line')
                .data(data.links)
                .enter().append('line')
                .attr('stroke', '#94a3b8')
                .attr('stroke-width', function(d) { return Math.max(1, (d.strength || 5) / 3); })
                .attr('marker-end', 'url(#chart-arrow)');

            // Link labels
            var linkLabel = g.append('g').selectAll('text')
                .data(data.links)
                .enter().append('text')
                .attr('text-anchor', 'middle')
                .attr('font-size', '9px')
                .attr('fill', '#64748b')
                .text(function(d) { return d.label || ''; });

            // Node groups
            var node = g.append('g').selectAll('g')
                .data(data.nodes)
                .enter().append('g')
                .call(d3.drag()
                    .on('start', function(event, d) {
                        if (!event.active) simulation.alphaTarget(0.3).restart();
                        d.fx = d.x; d.fy = d.y;
                    })
                    .on('drag', function(event, d) {
                        d.fx = event.x; d.fy = event.y;
                    })
                    .on('end', function(event, d) {
                        if (!event.active) simulation.alphaTarget(0);
                        d.fx = null; d.fy = null;
                    })
                );

            // Node circles
            node.append('circle')
                .attr('r', 22)
                .attr('fill', function(d) { return typeColors[d.type] || '#6b7280'; })
                .attr('stroke', '#fff')
                .attr('stroke-width', 2.5)
                .style('cursor', 'grab')
                .style('filter', 'drop-shadow(0 1px 2px rgba(0,0,0,0.15))');

            // Type icon inside circle
            node.append('text')
                .attr('text-anchor', 'middle')
                .attr('dy', '5px')
                .attr('font-size', '14px')
                .attr('fill', 'white')
                .style('pointer-events', 'none')
                .text(function(d) {
                    var icons = {person: '\u{1F464}', company: '\u{1F3E2}', topic: '\u{1F4AC}', event: '\u{1F4C5}', action: '\u26A1'};
                    return icons[d.type] || '\u2022';
                });

            // Node labels below
            node.append('text')
                .attr('dy', 38)
                .attr('text-anchor', 'middle')
                .attr('font-size', '11px')
                .attr('font-weight', '600')
                .attr('fill', '#1e293b')
                .style('pointer-events', 'none')
                .text(function(d) {
                    var label = d.label || '';
                    return label.length > 20 ? label.substring(0, 18) + '..' : label;
                });

            // Tooltip
            var tooltip = d3.select('#chart-tooltip');
            node.on('mouseover', function(event, d) {
                tooltip.style('display', 'block')
                    .html('<strong>' + escapeHtml(d.label) + '</strong><br><em>' + escapeHtml(d.type) + '</em>' +
                        (d.details ? '<br>' + escapeHtml(d.details) : ''))
                    .style('left', (event.pageX + 12) + 'px')
                    .style('top', (event.pageY - 10) + 'px');
            }).on('mousemove', function(event) {
                tooltip.style('left', (event.pageX + 12) + 'px')
                    .style('top', (event.pageY - 10) + 'px');
            }).on('mouseout', function() {
                tooltip.style('display', 'none');
            });

            // Tick
            simulation.on('tick', function() {
                link.attr('x1', function(d) { return d.source.x; })
                    .attr('y1', function(d) { return d.source.y; })
                    .attr('x2', function(d) { return d.target.x; })
                    .attr('y2', function(d) { return d.target.y; });

                linkLabel.attr('x', function(d) { return (d.source.x + d.target.x) / 2; })
                    .attr('y', function(d) { return (d.source.y + d.target.y) / 2; });

                node.attr('transform', function(d) { return 'translate(' + d.x + ',' + d.y + ')'; });
            });

            // Legend
            var legend = svg.append('g').attr('transform', 'translate(15, 15)');
            var types = Object.keys(typeColors);
            types.forEach(function(type, i) {
                var lg = legend.append('g').attr('transform', 'translate(0, ' + (i * 22) + ')');
                lg.append('circle').attr('r', 7).attr('fill', typeColors[type]);
                lg.append('text').attr('x', 14).attr('dy', '4px').attr('font-size', '11px').attr('fill', '#475569').text(type);
            });
        }

        // ============================================
        // CONVERSATION MEMORY - ChatGPT-like history
        // ============================================
        // Conversation history - stores Q&A pairs for context
        let conversationHistory = [];
        const MAX_CONVERSATION_HISTORY = 20; // Maximum Q&A pairs to keep

        // Load conversation history from sessionStorage (persists during page session)
        function loadConversationHistory() {
            const stored = sessionStorage.getItem(`conversation_${currentThreadId}`);
            if (stored) {
                try {
                    conversationHistory = JSON.parse(stored);
                    updateConversationDisplay();
                } catch (e) {
                    conversationHistory = [];
                }
            }
        }

        // Save conversation history to sessionStorage
        function saveConversationHistory() {
            sessionStorage.setItem(`conversation_${currentThreadId}`, JSON.stringify(conversationHistory));
        }

        // Add a Q&A pair to conversation history
        function addToConversation(question, answer, provider, model, context = '') {
            const entry = {
                timestamp: new Date().toISOString(),
                question: question,
                context: context,
                answer: answer,
                provider: provider,
                model: model
            };

            conversationHistory.push(entry);

            // Keep only the last MAX_CONVERSATION_HISTORY entries
            if (conversationHistory.length > MAX_CONVERSATION_HISTORY) {
                conversationHistory = conversationHistory.slice(-MAX_CONVERSATION_HISTORY);
            }

            saveConversationHistory();
            updateConversationDisplay();
        }

        // Clear conversation history
        function clearConversationHistory() {
            if (confirm('Clear all conversation history for this thread?')) {
                conversationHistory = [];
                saveConversationHistory();
                updateConversationDisplay();
            }
        }

        // Build conversation context string for AI
        function buildConversationContext() {
            if (conversationHistory.length === 0) return '';

            let context = '\n\n=== PREVIOUS CONVERSATION HISTORY ===\n';
            context += '(The user has asked previous questions about this email thread. Use this context for continuity.)\n\n';

            conversationHistory.forEach((entry, index) => {
                context += `--- Exchange ${index + 1} (${new Date(entry.timestamp).toLocaleString()}) ---\n`;
                context += `USER QUESTION: ${entry.question}\n`;
                context += `AI RESPONSE (${entry.provider}/${entry.model}):\n${entry.answer}\n\n`;
            });

            context += '=== END OF PREVIOUS CONVERSATION ===\n\n';
            context += 'Please consider the above conversation history when answering the new question. ';
            context += 'If the user refers to something discussed previously, use that context.\n\n';

            return context;
        }

        // Update the conversation history display in UI
        function updateConversationDisplay() {
            let historyContainer = document.getElementById('conversation-history');

            if (conversationHistory.length === 0) {
                if (historyContainer) {
                    historyContainer.style.display = 'none';
                }
                return;
            }

            if (!historyContainer) {
                // Create history container if it doesn't exist
                const aiFormCard = document.querySelector('.right-column .card:last-child');
                if (aiFormCard) {
                    historyContainer = document.createElement('div');
                    historyContainer.id = 'conversation-history';
                    historyContainer.className = 'card';
                    historyContainer.style.marginTop = '20px';
                    aiFormCard.after(historyContainer);
                }
            }

            if (historyContainer) {
                historyContainer.style.display = 'block';
                historyContainer.innerHTML = `
                    <div class="card-header">
                        <h2>💬 Conversation History (${conversationHistory.length})</h2>
                        <button class="btn btn-secondary" onclick="clearConversationHistory()" style="padding: 6px 12px; font-size: 0.85em;">
                            Clear History
                        </button>
                    </div>
                    <p style="font-size: 0.85em; color: var(--text-secondary); margin-bottom: 10px;">
                        Previous Q&A pairs are included as context for new questions (like ChatGPT memory).
                    </p>
                    <div style="max-height: 300px; overflow-y: auto;">
                        ${conversationHistory.slice().reverse().map((entry, i) => {
                            // Determine display text - show context if question is default
                            const isDefaultQuestion = entry.question && entry.question.startsWith('Please analyze this email conversation');
                            const hasContext = entry.context && entry.context.length > 0;
                            let displayQ = entry.question || 'General Analysis';

                            if (isDefaultQuestion && hasContext) {
                                displayQ = '[Context] ' + entry.context;
                            } else if (hasContext) {
                                displayQ = entry.question + ' (+context)';
                            }

                            return `
                            <div style="background: ${i % 2 === 0 ? '#f9fafb' : '#fff'}; padding: 10px; border-radius: 6px; margin-bottom: 8px; border-left: 3px solid var(--accent-primary);">
                                <div style="font-size: 0.8em; color: var(--text-secondary); margin-bottom: 5px;">
                                    ${new Date(entry.timestamp).toLocaleString()} | ${entry.provider}/${entry.model}
                                </div>
                                <div style="font-weight: 600; color: var(--accent-primary); margin-bottom: 5px;">
                                    Q: ${escapeHtml(displayQ.substring(0, 200))}${displayQ.length > 200 ? '...' : ''}
                                </div>
                                <div style="font-size: 0.9em; color: var(--text-primary); max-height: 60px; overflow: hidden;">
                                    A: ${escapeHtml(entry.answer.substring(0, 150))}${entry.answer.length > 150 ? '...' : ''}
                                </div>
                            </div>`;
                        }).join('')}
                    </div>
                `;
            }
        }

        // Load providers on page load
        document.addEventListener('DOMContentLoaded', function() {
            loadProviders();
            loadConversationHistory();
            initAgentButtons();
            updateTokenEstimate();
            // Load contact memories if we have a primary contact
            if (primaryContactId) {
                loadContactMemories(primaryContactId);
            }
        });

        // Load AI providers from API
        async function loadProviders() {
            try {
                const response = await fetch('../aiapi/api_opus/api/query.php?action=providers');
                const data = await response.json();

                if (data.success) {
                    providers = data.providers;
                    renderProviders();
                }
            } catch (error) {
                console.error('Failed to load providers:', error);
                document.getElementById('provider-grid').innerHTML =
                    '<p style="color: #ef4444;">Failed to load AI providers</p>';
            }
        }

        // Render provider selection (multi-select with checkboxes)
        function renderProviders() {
            const container = document.getElementById('provider-grid');
            container.innerHTML = '';

            // Add select all / deselect all buttons
            const buttonsDiv = document.createElement('div');
            buttonsDiv.style.cssText = 'grid-column: 1 / -1; display: flex; gap: 10px; margin-bottom: 10px;';
            buttonsDiv.innerHTML = `
                <button type="button" class="btn btn-small" onclick="selectAllProviders()">Select All</button>
                <button type="button" class="btn btn-small" onclick="deselectAllProviders()">Deselect All</button>
                <span style="margin-left: auto; color: var(--text-secondary); font-size: 0.85em;" id="selected-count">0 selected</span>
            `;
            container.appendChild(buttonsDiv);

            for (const [key, provider] of Object.entries(providers)) {
                if (!provider.has_key) continue;

                const div = document.createElement('div');
                div.className = 'provider-option';
                div.dataset.provider = key;
                div.onclick = (e) => {
                    if (e.target.tagName !== 'SELECT' && e.target.tagName !== 'OPTION') {
                        toggleProvider(key);
                    }
                };

                let modelsHtml = '<select class="model-select" id="model_' + key + '" onclick="event.stopPropagation()">';
                for (const [modelKey, modelName] of Object.entries(provider.models)) {
                    const selected = modelKey === provider.default_model ? 'selected' : '';
                    modelsHtml += `<option value="${modelKey}" ${selected}>${modelName}</option>`;
                }
                modelsHtml += '</select>';

                div.innerHTML = `
                    <input type="checkbox" name="providers[]" value="${key}" id="provider_${key}" onclick="event.stopPropagation(); toggleProvider('${key}', this.checked)">
                    <div style="flex: 1;">
                        <div class="provider-name">${provider.name}</div>
                        ${modelsHtml}
                    </div>
                `;

                container.appendChild(div);
            }

            // Select Claude by default if available
            if (providers['claude']?.has_key) {
                toggleProvider('claude', true);
            } else {
                // Select first available provider
                const firstProvider = Object.keys(providers).find(k => providers[k].has_key);
                if (firstProvider) {
                    toggleProvider(firstProvider, true);
                }
            }
        }

        // Toggle a provider selection
        function toggleProvider(providerKey, forceState = null) {
            const checkbox = document.getElementById('provider_' + providerKey);
            const option = document.querySelector(`.provider-option[data-provider="${providerKey}"]`);

            if (!checkbox || !option) return;

            // Determine new state
            const newState = forceState !== null ? forceState : !checkbox.checked;
            checkbox.checked = newState;
            option.classList.toggle('selected', newState);

            updateSelectedCount();
        }

        // Select all providers
        function selectAllProviders() {
            for (const key of Object.keys(providers)) {
                if (providers[key].has_key) {
                    toggleProvider(key, true);
                }
            }
        }

        // Deselect all providers
        function deselectAllProviders() {
            for (const key of Object.keys(providers)) {
                toggleProvider(key, false);
            }
        }

        // Update selected count display
        function updateSelectedCount() {
            const count = document.querySelectorAll('input[name="providers[]"]:checked').length;
            const countEl = document.getElementById('selected-count');
            if (countEl) {
                countEl.textContent = count + ' selected';
                countEl.style.color = count === 0 ? '#ef4444' : 'var(--text-secondary)';
            }
        }

        // Get selected providers
        function getSelectedProviders() {
            const checkboxes = document.querySelectorAll('input[name="providers[]"]:checked');
            return Array.from(checkboxes).map(cb => cb.value);
        }

        // Get selected additional threads
        function getSelectedThreads() {
            const checkboxes = document.querySelectorAll('input[name="additional_threads[]"]:checked');
            return Array.from(checkboxes).map(cb => cb.value);
        }

        /**
         * Strip quoted content from email body
         * Removes:
         * - Lines starting with ">" (quoted text)
         * - "On [date] [name] wrote:" patterns
         * - "From: ... Sent: ... To: ... Subject:" forwarded headers
         * - Lines starting with "-----Original Message-----"
         */
        function stripQuotedContent(body) {
            if (!body) return '';

            let lines = body.split('\n');
            let result = [];
            let inQuoteBlock = false;
            let consecutiveEmptyLines = 0;

            for (let i = 0; i < lines.length; i++) {
                let line = lines[i];
                let trimmedLine = line.trim();

                // Detect start of quoted section
                if (/^On\s+.+wrote:\s*$/i.test(trimmedLine) ||
                    /^On\s+\d+.+at\s+\d+.+wrote:\s*$/i.test(trimmedLine) ||
                    /^-{3,}\s*Original Message\s*-{3,}/i.test(trimmedLine) ||
                    /^-{3,}\s*Forwarded message\s*-{3,}/i.test(trimmedLine) ||
                    /^From:\s*.+\s+Sent:\s*.+\s+To:/i.test(trimmedLine)) {
                    inQuoteBlock = true;
                    continue;
                }

                // Skip quoted lines (starting with >)
                if (trimmedLine.startsWith('>')) {
                    inQuoteBlock = true;
                    continue;
                }

                // If we were in a quote block and hit non-quoted content, exit quote mode
                if (inQuoteBlock && trimmedLine && !trimmedLine.startsWith('>')) {
                    // Check if this is still part of quote (continuation)
                    if (i + 1 < lines.length && lines[i + 1].trim().startsWith('>')) {
                        continue; // Skip this line too
                    }
                    inQuoteBlock = false;
                }

                // Skip if still in quote block
                if (inQuoteBlock) continue;

                // Handle consecutive empty lines (max 2)
                if (!trimmedLine) {
                    consecutiveEmptyLines++;
                    if (consecutiveEmptyLines <= 2) {
                        result.push('');
                    }
                    continue;
                }

                consecutiveEmptyLines = 0;
                result.push(line);
            }

            // Trim trailing empty lines
            while (result.length > 0 && !result[result.length - 1].trim()) {
                result.pop();
            }

            return result.join('\n').trim();
        }

        /**
         * Build email content string for AI
         * Supports different processing modes:
         * - full: Include all content as-is
         * - stripped: Remove quoted replies from all emails
         * - smart: Keep quotes only in the last email
         */
        function buildEmailContent() {
            const mode = document.getElementById('content-mode').value;
            let content = 'EMAIL THREAD CONVERSATION:\n';
            content += `(Content mode: ${mode})\n\n`;

            const totalEmails = emailContent.length;

            emailContent.forEach((email, index) => {
                content += `--- Email ${index + 1} of ${totalEmails} ---\n`;
                content += `From: ${email.from} <${email.from_email}>\n`;
                content += `Date: ${email.date}\n`;
                content += `Direction: ${email.direction}\n\n`;

                let emailBody = email.body;

                if (mode === 'stripped') {
                    // Strip quoted content from all emails
                    emailBody = stripQuotedContent(emailBody);
                    if (!emailBody.trim()) {
                        emailBody = '(Only quoted content - no new text in this email)';
                    }
                } else if (mode === 'smart') {
                    // Strip quotes from all emails except the last one
                    if (index < totalEmails - 1) {
                        emailBody = stripQuotedContent(emailBody);
                        if (!emailBody.trim()) {
                            emailBody = '(Only quoted content - no new text in this email)';
                        }
                    }
                    // Last email keeps full content for context
                }
                // mode === 'full' keeps everything as-is

                content += emailBody + '\n\n';
            });

            return content;
        }

        /**
         * Process additional thread content with the selected mode
         */
        function processAdditionalThreadContent(emails, mode) {
            let content = '';
            const totalEmails = emails.length;

            emails.forEach((email, idx) => {
                const senderName = email.from_name || email.from_email || 'Unknown';
                content += `Email ${idx + 1} from ${senderName}:\n`;

                let emailBody = email.body_text || email.body_html || '';

                if (mode === 'stripped') {
                    emailBody = stripQuotedContent(emailBody);
                    if (!emailBody.trim()) {
                        emailBody = '(Only quoted content)';
                    }
                } else if (mode === 'smart' && idx < totalEmails - 1) {
                    emailBody = stripQuotedContent(emailBody);
                    if (!emailBody.trim()) {
                        emailBody = '(Only quoted content)';
                    }
                }

                content += emailBody + '\n\n';
            });

            return content;
        }

        // Submit to AI
        async function submitToAI() {
            var selectedProvidersList = getSelectedProviders();

            if (selectedProvidersList.length === 0) {
                alert('Please select at least one AI provider');
                return;
            }

            var question = document.getElementById('ai-question').value.trim();
            var context = document.getElementById('ai-context').value.trim();
            var contentMode = document.getElementById('content-mode').value;
            var additionalThreads = getSelectedThreads();
            // Also include context builder threads
            var contextThreadIds = contextThreads.map(function(ct) { return ct.threadId; });
            var allAdditionalThreads = additionalThreads.concat(contextThreadIds);

            // Build models map for each selected provider
            var modelsMap = {};
            selectedProvidersList.forEach(function(provider) {
                var modelSelect = document.getElementById('model_' + provider);
                modelsMap[provider] = modelSelect ? modelSelect.value : '';
            });

            // Token check
            var estimatedTokens = estimateCurrentTokens();
            var modelLimit = getSmallestModelLimit();
            if (estimatedTokens > modelLimit * 0.95) {
                if (!confirm('Estimated tokens (' + estimatedTokens.toLocaleString() + ') may exceed model limit (' + modelLimit.toLocaleString() + '). Continue anyway?')) {
                    return;
                }
            }

            // Show loading
            document.getElementById('loading-overlay').classList.add('active');
            document.getElementById('submit-ai-btn').disabled = true;

            try {
                // Build the full content - start with agent system prompt
                var agentPrompt = '';
                if (selectedAgent && AI_AGENTS[selectedAgent]) {
                    agentPrompt = '=== AI AGENT INSTRUCTIONS ===\n' + AI_AGENTS[selectedAgent].systemPrompt + '\n=== END AGENT INSTRUCTIONS ===\n\n';
                }

                // Inject contact memories if available
                var memoryBlock = '';
                if (contactMemories.length > 0 && primaryContactName) {
                    memoryBlock = '=== KNOWN FACTS ABOUT ' + primaryContactName.toUpperCase() + ' ===\n';
                    contactMemories.forEach(function(m) {
                        memoryBlock += '- ' + (m.fact || m) + '\n';
                    });
                    memoryBlock += '=== END KNOWN FACTS ===\n\n';
                }

                var fullContent = agentPrompt + memoryBlock + buildEmailContent();

                // ADD CONVERSATION HISTORY FOR CONTEXT (ChatGPT-like memory)
                var conversationContext = buildConversationContext();
                if (conversationContext) {
                    fullContent += conversationContext;
                    console.log('Including conversation history:', conversationHistory.length, 'previous exchanges');
                }

                // Add question if provided
                var actualQuestion = question || 'Please analyze this email conversation and provide insights on:\n1. Key points and summary\n2. Sentiment and tone analysis\n3. Any potential concerns or red flags\n4. Recommended next steps';

                if (question) {
                    fullContent += '\n\nCURRENT USER QUESTION:\n' + question;
                } else {
                    fullContent += '\n\n' + actualQuestion;
                }

                // Add context if provided
                if (context) {
                    fullContent += '\n\nADDITIONAL CONTEXT:\n' + context;
                }

                // Fetch additional thread content (checkboxes + context builder threads)
                // Uses thread_for_ai endpoint (lightweight, stripped bodies, no analysis)
                if (allAdditionalThreads.length > 0) {
                    document.getElementById('loading-message').textContent = 'Loading additional threads (0/' + allAdditionalThreads.length + ')...';

                    for (var i = 0; i < allAdditionalThreads.length; i++) {
                        var tid = allAdditionalThreads[i];
                        document.getElementById('loading-message').textContent = 'Loading thread ' + (i + 1) + '/' + allAdditionalThreads.length + '...';
                        try {
                            var threadResp = await fetch('api.php?action=thread_for_ai&thread_id=' + tid);
                            if (!threadResp.ok) {
                                console.warn('Failed to fetch thread ' + tid + ': HTTP ' + threadResp.status);
                                fullContent += '\n\n--- THREAD ' + (i + 1) + ': FAILED TO LOAD (HTTP ' + threadResp.status + ') ---\n';
                                continue;
                            }
                            var thread = await threadResp.json();

                            if (thread.emails && thread.emails.length > 0) {
                                fullContent += '\n\n--- ADDITIONAL THREAD ' + (i + 1) + ' OF ' + allAdditionalThreads.length + ' ---\n';
                                fullContent += 'Subject: ' + (thread.emails[0].subject || '(No Subject)') + '\n\n';
                                // Bodies already stripped by thread_for_ai endpoint
                                fullContent += processAdditionalThreadContent(thread.emails, 'full');
                            }
                        } catch (fetchErr) {
                            console.warn('Error fetching thread ' + tid + ':', fetchErr);
                            fullContent += '\n\n--- THREAD ' + (i + 1) + ': FETCH ERROR ---\n';
                        }
                    }
                }

                // Enforce content budget: truncate to fit smallest model limit
                var maxChars = Math.floor(modelLimit * 3.5 * 0.85); // 85% of model limit in chars
                var wasTruncated = false;
                if (fullContent.length > maxChars) {
                    wasTruncated = true;
                    fullContent = fullContent.substring(0, maxChars) + '\n\n...(Content truncated to fit model context limit of ' + modelLimit.toLocaleString() + ' tokens)';
                }

                var finalTokens = Math.ceil(fullContent.length / 3.5);

                // Log to debug file BEFORE sending
                logAiDebug({
                    type: 'request',
                    thread_id: currentThreadId,
                    agent: selectedAgent,
                    providers: selectedProvidersList,
                    models: modelsMap,
                    content_length: fullContent.length,
                    estimated_tokens: finalTokens,
                    question: actualQuestion,
                    context_threads: allAdditionalThreads,
                    truncated: wasTruncated,
                    full_content: fullContent.substring(0, 10000) + (fullContent.length > 10000 ? '\n...(truncated in log, full was ' + fullContent.length + ' chars)' : '')
                });

                // Console log
                console.log('=== AI REQUEST LOG ===');
                console.log('Agent:', selectedAgent);
                console.log('Providers:', selectedProvidersList);
                console.log('Models:', modelsMap);
                console.log('Content Mode:', contentMode);
                console.log('Context Threads:', allAdditionalThreads.length);
                console.log('Conversation History:', conversationHistory.length);
                console.log('Content Length:', fullContent.length, 'chars');
                console.log('Estimated Tokens:', finalTokens);
                console.log('======================');

                document.getElementById('loading-message').textContent = 'Analyzing with ' + selectedProvidersList.length + ' AI provider(s)...';

                // Build request body with all selected providers and their models
                var requestBody = {
                    user_content: fullContent,
                    providers: selectedProvidersList
                };

                // Add model for each provider
                for (var provider in modelsMap) {
                    requestBody['model_' + provider] = modelsMap[provider];
                }

                // Show truncation notice if applicable
                if (wasTruncated) {
                    document.getElementById('loading-message').textContent = 'Content truncated to ' + finalTokens.toLocaleString() + ' tokens. Sending to ' + selectedProvidersList.length + ' AI(s)...';
                } else {
                    document.getElementById('loading-message').textContent = 'Sending ~' + finalTokens.toLocaleString() + ' tokens to ' + selectedProvidersList.length + ' AI provider(s)...';
                }

                // Send to AI API with timeout
                var timeoutSec = parseInt(document.getElementById('provider-timeout').value) || 120;
                var response = await fetchWithTimeout('../aiapi/api_opus/api/query.php', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify(requestBody)
                }, timeoutSec * 1000);

                if (!response.ok) {
                    var errText = '';
                    try { errText = await response.text(); } catch(e) {}
                    throw new Error('AI API returned HTTP ' + response.status + (errText ? ': ' + errText.substring(0, 200) : ''));
                }

                var responseText = await response.text();
                var data;
                try {
                    data = JSON.parse(responseText);
                } catch (parseErr) {
                    throw new Error('AI API returned invalid JSON: ' + responseText.substring(0, 200));
                }

                // Log response to debug file
                var responsePreview = '';
                if (data.responses) {
                    for (var rk in data.responses) {
                        if (data.responses[rk] && data.responses[rk].response) {
                            responsePreview = data.responses[rk].response.substring(0, 500);
                            break;
                        }
                    }
                }
                logAiDebug({
                    type: 'response',
                    thread_id: currentThreadId,
                    agent: selectedAgent,
                    providers: selectedProvidersList,
                    models: modelsMap,
                    estimated_tokens: finalTokens,
                    question: actualQuestion,
                    response: responsePreview
                });

                console.log('=== AI RESPONSE LOG ===');
                console.log('Success:', data.success);
                console.log('Query ID:', data.query_id);
                console.log('Total Cost:', data.total_cost);
                console.log('=======================');

                if (data.success) {
                    var firstSuccessfulProvider = null;
                    var firstSuccessfulResponse = null;

                    // Save to MongoDB for each provider
                    for (var pi = 0; pi < selectedProvidersList.length; pi++) {
                        var prov = selectedProvidersList[pi];
                        var providerResponse = data.responses[prov + '_response'];
                        if (providerResponse && providerResponse.success) {
                            if (!firstSuccessfulProvider) {
                                firstSuccessfulProvider = prov;
                                firstSuccessfulResponse = providerResponse;
                            }

                            // Save to MongoDB via our API
                            await saveAIResponse({
                                thread_ids: [currentThreadId].concat(allAdditionalThreads),
                                contact_id: primaryContactId,
                                question: actualQuestion,
                                context: context,
                                agent: selectedAgent,
                                content_mode: contentMode,
                                content_sent: fullContent,
                                content_length: fullContent.length,
                                estimated_tokens: finalTokens,
                                provider: prov,
                                model: modelsMap[prov],
                                query_id: data.query_id,
                                response: providerResponse,
                                total_cost: providerResponse.cost || 0,
                                all_providers: selectedProvidersList,
                                conversation_history_count: conversationHistory.length
                            });
                        }
                    }

                    // ADD TO CONVERSATION HISTORY (first successful response)
                    if (firstSuccessfulProvider && firstSuccessfulResponse && firstSuccessfulResponse.response) {
                        addToConversation(
                            actualQuestion,
                            firstSuccessfulResponse.response,
                            firstSuccessfulProvider,
                            modelsMap[firstSuccessfulProvider],
                            context
                        );
                    }

                    // Clear the question field for next question
                    document.getElementById('ai-question').value = '';
                    updateTokenEstimate();

                    // Store Round 1 info for potential Round 2 / follow-ups
                    lastRound1Providers = selectedProvidersList.slice();
                    lastRound1Models = Object.assign({}, modelsMap);
                    lastRound1Question = actualQuestion;
                    lastFullContent = fullContent;

                    // Display Round 1 response
                    displayAIResponse(data, 'Round 1');

                    // Check if Two-Round mode is enabled → auto-trigger Round 2
                    var twoRoundCheckbox = document.getElementById('two-round-mode');
                    if (twoRoundCheckbox && twoRoundCheckbox.checked) {
                        // Count successful responses
                        var r1SuccessCount = 0;
                        for (var rk in lastRound1Responses) {
                            if (lastRound1Responses[rk] && lastRound1Responses[rk].success) r1SuccessCount++;
                        }
                        if (r1SuccessCount > 0) {
                            // Keep loading overlay active — sendRound2 will manage it
                            document.getElementById('loading-message').textContent = 'Round 1 complete (' + r1SuccessCount + ' responses). Starting Round 2 synthesis...';
                            sendRound2(lastRound1Responses, lastRound1Providers, lastRound1Models, lastRound1Question);
                            return; // Don't hide overlay — sendRound2's finally will do it
                        }
                    }
                } else {
                    alert('AI request failed: ' + (data.error || 'Unknown error'));
                }
            } catch (error) {
                console.error('AI request error:', error);
                alert('Error: ' + error.message);
            } finally {
                document.getElementById('loading-overlay').classList.remove('active');
                document.getElementById('submit-ai-btn').disabled = false;
            }
        }

        // Save AI response to MongoDB
        async function saveAIResponse(responseData) {
            try {
                await fetch('api.php?action=save_ai_response', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify(responseData)
                });
            } catch (error) {
                console.error('Failed to save response:', error);
            }
        }

        // Display AI response
        function displayAIResponse(data, roundLabel) {
            var container = document.getElementById('ai-response-container');
            var tabsContainer = document.getElementById('response-tabs');
            var contentContainer = document.getElementById('ai-responses-content');
            var badge = document.getElementById('response-round-badge');

            container.style.display = 'block';
            tabsContainer.innerHTML = '';
            contentContainer.innerHTML = '';

            // Update badge
            if (badge) {
                badge.textContent = roundLabel || 'Round 1';
                badge.className = 'round-badge round-1';
            }

            // Hide Round 2 container (fresh start)
            document.getElementById('ai-response-round2-container').style.display = 'none';

            // Store responses for follow-up
            lastRound1Responses = {};

            var tabIndex = 0;
            var successCount = 0;
            var failCount = 0;

            for (var key in data.responses) {
                var response = data.responses[key];
                var providerName = key.replace('_response', '');
                var providerConfig = providers[providerName] || { name: providerName };

                // Store for Round 2 / follow-up
                lastRound1Responses[providerName] = response;

                // Create tab
                var tab = document.createElement('button');
                tab.className = 'response-tab' + (tabIndex === 0 ? ' active' : '');
                tab.textContent = providerConfig.name + (response.success ? '' : ' (fail)');
                tab.dataset.index = tabIndex;
                (function(idx) {
                    tab.onclick = function() { switchResponseTab(idx); };
                })(tabIndex);
                tabsContainer.appendChild(tab);

                // Create content with selection checkbox
                var contentDiv = document.createElement('div');
                contentDiv.className = 'response-content' + (tabIndex === 0 ? ' active' : '');
                contentDiv.id = 'response-tab-' + tabIndex;

                if (response.success) {
                    successCount++;
                    var cbId = 'resp-cb-' + providerName;
                    contentDiv.innerHTML = '<div class="response-checkbox">' +
                        '<input type="checkbox" class="response-select-cb" id="' + cbId + '" checked ' +
                        'data-provider="' + escapeHtml(providerName) + '" ' +
                        'data-model="' + escapeHtml(response.model || '') + '" ' +
                        'data-response-text="' + escapeHtml(response.response || '').replace(/"/g, '"') + '">' +
                        '<label for="' + cbId + '">Include in follow-up</label>' +
                        '</div>' +
                        '<div class="ai-response-card">' +
                        '<div class="ai-response-header">' +
                        '<span class="ai-response-provider">' + escapeHtml(providerConfig.name) + '</span>' +
                        '<span class="ai-response-meta">' +
                        'Model: ' + escapeHtml(response.model || 'Unknown') + ' | ' +
                        'Time: ' + (response.request_time ? response.request_time.toFixed(2) : '?') + 's | ' +
                        'Cost: $' + (response.cost ? response.cost.toFixed(6) : '0') +
                        '</span></div>' +
                        '<div class="ai-response-content">' + escapeHtml(response.response) + '</div>' +
                        '</div>';
                } else {
                    failCount++;
                    contentDiv.innerHTML = '<div style="background:#fef2f2;border:1px solid #ef4444;padding:15px;border-radius:8px;">' +
                        '<strong style="color:#dc2626;">Error (' + escapeHtml(providerConfig.name) + '):</strong>' +
                        '<p style="margin:5px 0 0;color:#991b1b;">' + escapeHtml(response.error || 'Unknown error / timeout') + '</p></div>';
                }

                contentContainer.appendChild(contentDiv);
                tabIndex++;
            }

            // Show the follow-up bar
            var selectBar = document.getElementById('response-select-bar');
            if (selectBar && successCount > 0) {
                selectBar.style.display = 'flex';
            }

            // Add summary line if mixed results
            if (failCount > 0 && successCount > 0) {
                var summaryDiv = document.createElement('div');
                summaryDiv.className = 'provider-timeout';
                summaryDiv.style.marginTop = '10px';
                summaryDiv.innerHTML = successCount + ' provider(s) responded, ' + failCount + ' failed/timed out. Successful responses are shown above.';
                contentContainer.appendChild(summaryDiv);
            }

            container.scrollIntoView({ behavior: 'smooth' });
        }

        // Switch response tabs - scoped to response container only
        function switchResponseTab(index) {
            const tabsContainer = document.getElementById('response-tabs');
            const contentContainer = document.getElementById('ai-responses-content');

            if (!tabsContainer || !contentContainer) return;

            // Only target tabs within our container
            tabsContainer.querySelectorAll('.response-tab').forEach(tab => {
                const tabIndex = parseInt(tab.dataset.index);
                tab.classList.remove('active');
                if (tabIndex === index) {
                    tab.classList.add('active');
                }
            });

            // Only target content within our container
            contentContainer.querySelectorAll('.response-content').forEach((content, i) => {
                content.classList.remove('active');
                if (i === index) {
                    content.classList.add('active');
                }
            });
        }

        // Close response
        function closeResponse() {
            document.getElementById('ai-response-container').style.display = 'none';
        }

        // Mark a saved response row as last viewed
        function markAsLastViewed(responseId) {
            // Remove last-viewed from all rows
            document.querySelectorAll('.saved-response.last-viewed').forEach(el => {
                el.classList.remove('last-viewed');
            });
            // Add to the current row
            const row = document.getElementById('saved-response-' + responseId);
            if (row) {
                row.classList.add('last-viewed');
            }
        }

        // Load saved response
        async function loadSavedResponse(responseId) {
            try {
                // Mark this row as last viewed
                markAsLastViewed(responseId);

                document.getElementById('loading-overlay').classList.add('active');
                document.getElementById('loading-message').textContent = 'Loading saved response...';

                const response = await fetch(`api.php?action=get_ai_response&id=${responseId}`);
                const data = await response.json();

                if (data.success && data.response) {
                    const saved = data.response;

                    // Display it
                    const container = document.getElementById('ai-response-container');
                    const tabsContainer = document.getElementById('response-tabs');
                    const contentContainer = document.getElementById('ai-responses-content');

                    container.style.display = 'block';
                    tabsContainer.innerHTML = `<button class="response-tab active">${saved.provider}</button>`;

                    const aiResponse = saved.response || {};

                    // Determine how to display question/context
                    const isDefaultQuestion = saved.question && saved.question.startsWith('Please analyze this email conversation');
                    const hasContext = saved.context && saved.context.length > 0;

                    let questionHtml = '';
                    if (isDefaultQuestion && hasContext) {
                        // Show context as the main input if question was default
                        questionHtml = `
                            <p><strong>User Input (Context):</strong></p>
                            <p style="background: #f5f5f5; padding: 10px; border-radius: 6px; white-space: pre-wrap;">${escapeHtml(saved.context)}</p>
                            <p style="font-size: 0.85em; color: #666;"><em>Default question was used for analysis</em></p>
                        `;
                    } else if (hasContext) {
                        // Show both question and context
                        questionHtml = `
                            <p><strong>Question:</strong> ${escapeHtml(saved.question)}</p>
                            <p><strong>Additional Context:</strong></p>
                            <p style="background: #f5f5f5; padding: 10px; border-radius: 6px; white-space: pre-wrap;">${escapeHtml(saved.context)}</p>
                        `;
                    } else {
                        // Just show question
                        questionHtml = `<p><strong>Question:</strong> ${escapeHtml(saved.question)}</p>`;
                    }

                    contentContainer.innerHTML = `
                        <div class="response-content active">
                            <div class="ai-response-card">
                                <div class="ai-response-header">
                                    <span class="ai-response-provider">${saved.provider} - ${saved.model}</span>
                                    <span class="ai-response-meta">Saved: ${saved.created_at}</span>
                                </div>
                                ${questionHtml}
                                <hr style="margin: 15px 0; border: none; border-top: 1px solid #e0e0e0;">
                                <div class="ai-response-content">${escapeHtml(aiResponse.response || 'No response content')}</div>
                            </div>
                        </div>
                    `;

                    container.scrollIntoView({ behavior: 'smooth' });
                }
            } catch (error) {
                console.error('Failed to load saved response:', error);
                alert('Failed to load saved response');
            } finally {
                document.getElementById('loading-overlay').classList.remove('active');
            }
        }

        // Delete a saved AI response
        async function deleteAiResponse(responseId) {
            if (!confirm('Delete this AI response? This cannot be undone.')) return;
            try {
                var response = await fetch('api.php?action=delete_ai_response', {
                    method: 'POST',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify({response_id: responseId})
                });
                var data = await response.json();
                if (data.success) {
                    var el = document.getElementById('saved-response-' + responseId);
                    if (el) el.remove();
                } else {
                    alert('Error: ' + (data.error || 'Delete failed'));
                }
            } catch(e) {
                alert('Error deleting response: ' + e.message);
            }
        }

        // Quick view response in modal (no page scroll)
        async function quickViewResponse(responseId) {
            try {
                // Mark this row as last viewed
                markAsLastViewed(responseId);

                const overlay = document.getElementById('quick-view-overlay');
                overlay.style.display = 'flex';
                document.getElementById('quick-view-response').textContent = 'Loading...';
                document.getElementById('quick-view-meta').innerHTML = '';
                document.getElementById('quick-view-question-section').innerHTML = '';

                const response = await fetch(`api.php?action=get_ai_response&id=${responseId}`);
                const data = await response.json();

                if (data.success && data.response) {
                    const saved = data.response;
                    const aiResponse = saved.response || {};

                    // Update title
                    document.getElementById('quick-view-title').textContent = `${saved.provider} / ${saved.model}`;

                    // Update meta
                    document.getElementById('quick-view-meta').innerHTML = `
                        <div class="quick-view-meta-item"><strong>Provider:</strong> ${escapeHtml(saved.provider)}</div>
                        <div class="quick-view-meta-item"><strong>Model:</strong> ${escapeHtml(saved.model)}</div>
                        <div class="quick-view-meta-item"><strong>Date:</strong> ${escapeHtml(saved.created_at)}</div>
                        <div class="quick-view-meta-item"><strong>Cost:</strong> $${(saved.total_cost || 0).toFixed(6)}</div>
                    `;

                    // Update question/context section
                    const isDefaultQuestion = saved.question && saved.question.startsWith('Please analyze this email conversation');
                    const hasContext = saved.context && saved.context.length > 0;

                    let questionHtml = '';
                    if (isDefaultQuestion && hasContext) {
                        questionHtml = `
                            <div class="quick-view-question">
                                <strong>User Context:</strong><br>
                                ${escapeHtml(saved.context)}
                                <div style="font-size: 0.85em; color: #666; margin-top: 8px;"><em>Default analysis question was used</em></div>
                            </div>
                        `;
                    } else if (hasContext) {
                        questionHtml = `
                            <div class="quick-view-question">
                                <strong>Question:</strong> ${escapeHtml(saved.question)}<br><br>
                                <strong>Context:</strong> ${escapeHtml(saved.context)}
                            </div>
                        `;
                    } else {
                        questionHtml = `
                            <div class="quick-view-question">
                                <strong>Question:</strong> ${escapeHtml(saved.question)}
                            </div>
                        `;
                    }
                    document.getElementById('quick-view-question-section').innerHTML = questionHtml;

                    // Update response
                    document.getElementById('quick-view-response').textContent = aiResponse.response || 'No response content';
                } else {
                    document.getElementById('quick-view-response').textContent = 'Error loading response';
                }
            } catch (error) {
                console.error('Failed to quick view response:', error);
                document.getElementById('quick-view-response').textContent = 'Error: ' + error.message;
            }
        }

        // Close quick view modal
        function closeQuickView(event) {
            if (event && event.target !== event.currentTarget) return;
            document.getElementById('quick-view-overlay').style.display = 'none';
        }

        // Close quick view on Escape key
        document.addEventListener('keydown', function(e) {
            if (e.key === 'Escape') {
                closeQuickView();
            }
        });

        // Load more saved responses
        async function loadMoreResponses() {
            const btn = document.getElementById('load-more-btn');
            if (!btn) return;

            const currentOffset = parseInt(btn.dataset.offset) || 0;
            const total = parseInt(btn.dataset.total) || 0;
            const limit = 5; // Load 5 more at a time

            btn.disabled = true;
            btn.textContent = 'Loading...';

            try {
                const response = await fetch(`api.php?action=list_ai_responses&thread_id=${currentThreadId}&limit=${limit}&offset=${currentOffset}`);
                const data = await response.json();

                if (data.success && data.responses && data.responses.length > 0) {
                    const container = document.getElementById('saved-responses');

                    // Append new responses
                    data.responses.forEach(saved => {
                        // Determine what to display as the "question"
                        let displayQuestion = saved.question || 'General Analysis';
                        const hasContext = saved.context && saved.context.length > 0;
                        const isDefaultQuestion = displayQuestion.startsWith('Please analyze this email conversation');

                        if (isDefaultQuestion && hasContext) {
                            displayQuestion = '[Context] ' + saved.context;
                        } else if (hasContext) {
                            displayQuestion = displayQuestion + ' (+context)';
                        }

                        // Truncate to 200 chars
                        if (displayQuestion.length > 200) {
                            displayQuestion = displayQuestion.substring(0, 200) + '...';
                        }

                        // Get response excerpt
                        const responseText = saved.response?.response || '';
                        let responseExcerpt = responseText.length > 120
                            ? responseText.substring(0, 120) + '...'
                            : responseText;

                        const responseEl = document.createElement('div');
                        responseEl.className = 'saved-response';
                        responseEl.id = 'saved-response-' + saved._id;
                        responseEl.dataset.id = saved._id;
                        responseEl.innerHTML = `
                            <div class="saved-response-header">
                                <span class="saved-response-question">${escapeHtml(displayQuestion)}</span>
                                <span class="saved-response-date">${saved.created_at || 'Unknown date'}</span>
                            </div>
                            <div class="saved-response-preview">
                                <strong>${escapeHtml(saved.provider || 'Unknown')}</strong> /
                                ${escapeHtml(saved.model || 'Unknown')}
                            </div>
                            ${responseExcerpt ? `<div class="saved-response-excerpt">${escapeHtml(responseExcerpt)}</div>` : ''}
                            <div class="saved-response-actions">
                                <button class="btn-view" onclick="event.stopPropagation(); quickViewResponse('${saved._id}')">
                                    👁 Quick View
                                </button>
                                <button class="btn-expand" onclick="event.stopPropagation(); loadSavedResponse('${saved._id}')">
                                    📄 Full View
                                </button>
                                <button style="color:#ef4444;background:none;border:1px solid #ef4444;padding:4px 8px;border-radius:4px;cursor:pointer;font-size:0.85em;" onclick="event.stopPropagation(); deleteAiResponse('${saved._id}')">
                                    Del
                                </button>
                            </div>
                        `;

                        container.appendChild(responseEl);
                    });

                    // Update offset
                    const newOffset = currentOffset + data.responses.length;
                    btn.dataset.offset = newOffset;

                    // Update button text or hide if no more
                    const remaining = total - newOffset;
                    if (remaining > 0) {
                        btn.textContent = `Load More (${remaining} remaining)`;
                        btn.disabled = false;
                    } else {
                        btn.parentElement.style.display = 'none';
                    }
                } else {
                    btn.parentElement.style.display = 'none';
                }
            } catch (error) {
                console.error('Failed to load more responses:', error);
                btn.textContent = 'Error - Try Again';
                btn.disabled = false;
            }
        }

        // Escape HTML
        function escapeHtml(text) {
            if (!text) return '';
            const div = document.createElement('div');
            div.textContent = text;
            return div.innerHTML;
        }
    </script>
</body>
</html>

resultsEl.innerHTML = '
Loading threads for ' + escapeHtml(contactName) + '...
';

-            fetch('api.php?action=threads&contact=' + contactId + '&limit=20')

+            fetch('api.php?action=threads&contact=' + contactId + '&limit=100')