PageSourceSearch

https://bot.aim.media/widget.js

js aim.media collected 2026-10-08 03:27:48 UTC 97,626 bytes, 3,071 lines download raw bytes

1// Chat Widget v6 - Dynamic Base URL Detection
2// Version: 6 - Automatically detects base URL from widget script source
3(function() {
4  'use strict';
5
6  // Dynamically detect base URL from where widget.js is loaded
7  const scriptTag = document.currentScript || document.querySelector('script[src*="widget.js"]');
8  const LOVABLE_BASE_URL = scriptTag ? new URL(scriptTag.src).origin : window.location.origin;
9
10  const SUPABASE_URL = 'https://sleuzjmoofdaxzqtcljc.supabase.co';
11  const SUPABASE_ANON_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InNsZXV6am1vb2ZkYXh6cXRjbGpjIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjQ5Nzg5NzUsImV4cCI6MjA4MDU1NDk3NX0.qpoc45oXbVr_gzpQ8XG_9_DRrrPLEe7fcqe9JUVsYiI';
12
13  let widgetId = null;
14  let settings = null;
15  let isOpen = false;
16  let messages = [];
17  let conversationId = null;
18  let visitorId = null;
19  let visitorInfo = null;
20  let hasCompletedForm = false;
21  let teaserShown = false;
22  let teaserTimer = null;
23  let aiAgentInfo = null;
24  let heartbeatInterval = null;
25  let visitorLocation = null;
26  let conversationStatus = 'ai_handling';
27  let realtimeChannel = null;
28  let typingChannel = null;
29  let isAgentTyping = false;
30  let typingTimeout = null;
31  let messageReactions = {}; // Store reactions by message_id
32  let messageIdMap = {}; // Map message index to actual message IDs
33
34  // Random AI agent name generator with animated avatars
35  const AI_FIRST_NAMES = ['Nova', 'Atlas', 'Zara', 'Orion', 'Luna', 'Echo', 'Kai', 'Aria', 'Neo', 'Sage', 'Phoenix', 'Ember', 'Astra', 'Cipher', 'Volt'];
36  const AI_TITLES = ['AI', 'Bot', 'Assistant', 'Guide', 'Helper', 'Agent'];
37  const AI_AVATARS = ['🤖', '🦊', '🐱', '🦉', '🐼', '🦋', '🐬', '🦄', '🐙', '🦜', '🐨', '🦁', '🐯', '🐻', '🦝'];
38  const AI_COLORS = ['#6366f1', '#8b5cf6', '#ec4899', '#f43f5e', '#f97316', '#eab308', '#22c55e', '#14b8a6', '#06b6d4', '#3b82f6'];
39
40  function getAiAgentInfo() {
41    if (aiAgentInfo) return aiAgentInfo;
42    
43    // Use widgetId + visitorId as seed for consistent avatar per user
44    const seed = (widgetId || '') + (visitorId || '') + Date.now().toString().slice(0, -6);
45    let hash = 0;
46    for (let i = 0; i < seed.length; i++) {
47      hash = ((hash << 5) - hash) + seed.charCodeAt(i);
48      hash = hash & hash;
49    }
50    hash = Math.abs(hash);
51    
52    const firstName = AI_FIRST_NAMES[hash % AI_FIRST_NAMES.length];
53    const title = AI_TITLES[(hash >> 4) % AI_TITLES.length];
54    const avatar = AI_AVATARS[(hash >> 8) % AI_AVATARS.length];
55    const color = AI_COLORS[(hash >> 12) % AI_COLORS.length];
56    
57    aiAgentInfo = {
58      name: `${firstName} ${title}`,
59      avatar,
60      color
61    };
62    
63    return aiAgentInfo;
64  }
65
66  // Visitor info storage key
67  function getVisitorInfoKey() {
68    return 'chatbot_visitor_info_' + widgetId;
69  }
70
71  function getVisitorId() {
72    let id = localStorage.getItem('chatbot_visitor_id');
73    if (!id) {
74      id = 'visitor_' + Math.random().toString(36).substr(2, 9) + '_' + Date.now();
75      localStorage.setItem('chatbot_visitor_id', id);
76    }
77    return id;
78  }
79
80  function loadVisitorInfo() {
81    try {
82      const stored = localStorage.getItem(getVisitorInfoKey());
83      if (stored) {
84        visitorInfo = JSON.parse(stored);
85        hasCompletedForm = true;
86        return visitorInfo;
87      }
88    } catch (e) {
89      console.error('[ChatBot] Error loading visitor info:', e);
90    }
91    return null;
92  }
93
94  function saveVisitorInfo(info) {
95    try {
96      visitorInfo = info;
97      hasCompletedForm = true;
98      localStorage.setItem(getVisitorInfoKey(), JSON.stringify(info));
99    } catch (e) {
100      console.error('[ChatBot] Error saving visitor info:', e);
101    }
102  }
103
104  // Conversation ID storage functions
105  function getConversationIdKey() {
106    return 'chatbot_conversation_id_' + widgetId;
107  }
108
109  function loadConversationId() {
110    try {
111      return localStorage.getItem(getConversationIdKey());
112    } catch (e) {
113      console.error('[ChatBot] Error loading conversation ID:', e);
114      return null;
115    }
116  }
117
118  function saveConversationId(id) {
119    try {
120      localStorage.setItem(getConversationIdKey(), id);
121    } catch (e) {
122      console.error('[ChatBot] Error saving conversation ID:', e);
123    }
124  }
125
126
127  async function fetchExistingMessages() {
128    if (!conversationId) return [];
129    
130    try {
131      const url = new URL('/rest/v1/messages', SUPABASE_URL);
132      url.searchParams.set('conversation_id', 'eq.' + conversationId);
133      url.searchParams.set('select', '*');
134      url.searchParams.set('order', 'created_at.asc');
135      
136      const response = await fetch(url.toString(), {
137        headers: {
138          'apikey': SUPABASE_ANON_KEY,
139          'Authorization': 'Bearer ' + SUPABASE_ANON_KEY
140        }
141      });
142      
143      const data = await response.json();
144      console.log('[ChatBot] Fetched existing messages:', data.length);
145      
146      // Store message ID mapping
147      data.forEach((msg, index) => {
148        messageIdMap[index] = msg.id;
149      });
150      
151      // Fetch reactions for these messages
152      await fetchMessageReactions(data.map(m => m.id));
153      
154      return data.map(msg => ({
155        id: msg.id,
156        role: msg.sender_type === 'visitor' ? 'visitor' : 'ai',
157        content: msg.content
158      }));
159    } catch (error) {
160      console.error('[ChatBot] Failed to fetch messages:', error);
161      return [];
162    }
163  }
164
165  async function markMessagesRead() {
166    if (!conversationId || !visitorId) return;
167    try {
168      await fetch(SUPABASE_URL + '/functions/v1/mark-messages-read', {
169        method: 'POST',
170        headers: {
171          'apikey': SUPABASE_ANON_KEY,
172          'Authorization': 'Bearer ' + SUPABASE_ANON_KEY,
173          'Content-Type': 'application/json'
174        },
175        body: JSON.stringify({ conversationId: conversationId, visitorId: visitorId })
176      });
177    } catch (error) {
178      console.error('[ChatBot] Failed to mark messages read:', error);
179    }
180  }
181
182  async function fetchMessageReactions(messageIds) {
183    if (!messageIds || messageIds.length === 0 || !conversationId || !visitorId) return;
184
185    try {
186      const response = await fetch(SUPABASE_URL + '/functions/v1/visitor-reactions', {
187        method: 'POST',
188        headers: {
189          'apikey': SUPABASE_ANON_KEY,
190          'Authorization': 'Bearer ' + SUPABASE_ANON_KEY,
191          'Content-Type': 'application/json'
192        },
193        body: JSON.stringify({ action: 'list', conversationId: conversationId, visitorId: visitorId })
194      });
195
196      const result = await response.json();
197      const data = (result && result.reactions) || [];
198
199      messageReactions = {};
200      data.forEach(r => {
201        if (!messageReactions[r.message_id]) {
202          messageReactions[r.message_id] = [];
203        }
204        messageReactions[r.message_id].push(r);
205      });
206
207      console.log('[ChatBot] Fetched reactions:', Object.keys(messageReactions).length, 'messages have reactions');
208    } catch (error) {
209      console.error('[ChatBot] Failed to fetch reactions:', error);
210    }
211  }
212
213  async function toggleReaction(messageId, emoji) {
214    if (!messageId || !visitorId || !conversationId) return;
215
216    const existing = (messageReactions[messageId] || []).find(
217      r => r.emoji === emoji && r.reactor_type === 'visitor' && r.reactor_id === visitorId
218    );
219
220    try {
221      const response = await fetch(SUPABASE_URL + '/functions/v1/visitor-reactions', {
222        method: 'POST',
223        headers: {
224          'apikey': SUPABASE_ANON_KEY,
225          'Authorization': 'Bearer ' + SUPABASE_ANON_KEY,
226          'Content-Type': 'application/json'
227        },
228        body: JSON.stringify({
229          action: 'toggle',
230          conversationId: conversationId,
231          visitorId: visitorId,
232          messageId: messageId,
233          emoji: emoji
234        })
235      });
236
237      const result = await response.json();
238
239      if (result && result.removed) {
240        messageReactions[messageId] = (messageReactions[messageId] || []).filter(r => r.id !== result.removed);
241      } else if (result && result.reaction) {
242        if (!messageReactions[messageId]) {
243          messageReactions[messageId] = [];
244        }
245        messageReactions[messageId].push(result.reaction);
246      } else if (existing) {
247        messageReactions[messageId] = (messageReactions[messageId] || []).filter(r => r.id !== existing.id);
248      }
249
250      renderMessages();
251    } catch (error) {
252      console.error('[ChatBot] Failed to toggle reaction:', error);
253    }
254  }
255
256  async function fetchSettings(id) {
257    try {
258      const url = new URL('/rest/v1/public_widget_config', SUPABASE_URL);
259      url.searchParams.set('id', 'eq.' + id);
260      url.searchParams.set('select', '*');
261      
262      console.log('[ChatBot] Fetching settings from:', url.toString());
263      
264      const response = await fetch(url.toString(), {
265        headers: {
266          'apikey': SUPABASE_ANON_KEY,
267          'Authorization': 'Bearer ' + SUPABASE_ANON_KEY
268        }
269      });
270      const data = await response.json();
271      console.log('[ChatBot] Raw API response:', data);
272      return data[0] || null;
273    } catch (error) {
274      console.error('[ChatBot] Failed to fetch widget settings:', error);
275      return null;
276    }
277  }
278
279  // Track page history
280  let pageHistory = [];
281  
282  function trackPageVisit() {
283    const currentPage = {
284      url: window.location.href,
285      title: document.title,
286      timestamp: new Date().toISOString()
287    };
288    
289    // Avoid duplicate consecutive entries
290    const lastPage = pageHistory[pageHistory.length - 1];
291    if (!lastPage || lastPage.url !== currentPage.url) {
292      pageHistory.push(currentPage);
293      // Keep only last 20 pages
294      if (pageHistory.length > 20) {
295        pageHistory = pageHistory.slice(-20);
296      }
297    }
298  }
299
300  function getDeviceInfo() {
301    const ua = navigator.userAgent;
302    let browser = 'Unknown';
303    let os = 'Unknown';
304    let device = 'Desktop';
305
306    // Detect browser
307    if (ua.includes('Firefox')) browser = 'Firefox';
308    else if (ua.includes('Edg')) browser = 'Edge';
309    else if (ua.includes('Chrome')) browser = 'Chrome';
310    else if (ua.includes('Safari')) browser = 'Safari';
311    else if (ua.includes('Opera') || ua.includes('OPR')) browser = 'Opera';
312
313    // Detect OS
314    if (ua.includes('Windows')) os = 'Windows';
315    else if (ua.includes('Mac OS')) os = 'macOS';
316    else if (ua.includes('Linux')) os = 'Linux';
317    else if (ua.includes('Android')) os = 'Android';
318    else if (ua.includes('iOS') || ua.includes('iPhone') || ua.includes('iPad')) os = 'iOS';
319
320    // Detect device type
321    if (/Mobi|Android|iPhone|iPad|iPod/i.test(ua)) {
322      device = /iPad/i.test(ua) ? 'Tablet' : 'Mobile';
323    }
324
325    return { browser, os, device, userAgent: ua.substring(0, 200) };
326  }
327
328  async function sendMessage(message) {
329    // Track the current page before sending
330    trackPageVisit();
331    
332    try {
333      const url = new URL('/functions/v1/chat', SUPABASE_URL);
334      
335      const response = await fetch(url.toString(), {
336        method: 'POST',
337        headers: {
338          'Content-Type': 'application/json',
339          'Authorization': 'Bearer ' + SUPABASE_ANON_KEY
340        },
341        body: JSON.stringify({
342          message: message,
343          conversation_id: conversationId,
344          widget_id: widgetId,
345          visitor_id: visitorId,
346          page_url: window.location.href,
347          device_info: getDeviceInfo(),
348          page_history: pageHistory,
349          visitor_info: visitorInfo
350        })
351      });
352      const data = await response.json();
353      if (data.conversation_id) {
354        conversationId = data.conversation_id;
355        saveConversationId(conversationId);
356      }
357      return {
358        response: data.response || 'Sorry, I encountered an error. Please try again.',
359        quick_replies: data.quick_replies || []
360      };
361    } catch (error) {
362      console.error('[ChatBot] Failed to send message:', error);
363      return {
364        response: 'Sorry, I encountered an error. Please try again.',
365        quick_replies: []
366      };
367    }
368  }
369
370  // Auto-discovery: collect page data and send to backend
371  async function discoverPageData() {
372    const discoveryKey = 'chatbot_discovered_' + widgetId + '_' + window.location.hostname;
373    const lastDiscovery = localStorage.getItem(discoveryKey);
374    const oneDayAgo = Date.now() - (24 * 60 * 60 * 1000);
375    
376    // Only discover once per day per domain
377    if (lastDiscovery && parseInt(lastDiscovery) > oneDayAgo) {
378      console.log('[ChatBot] Discovery already done recently, skipping');
379      return;
380    }
381
382    try {
383      // Collect current page data
384      const currentPage = {
385        url: window.location.href,
386        title: document.title,
387        description: document.querySelector('meta[name="description"]')?.getAttribute('content') || '',
388        content: extractPageContent(),
389        sitemapUrl: findSitemapUrl()
390      };
391
392      // Collect internal links
393      const links = Array.from(document.querySelectorAll('a[href]'))
394        .map(a => {
395          try {
396            const url = new URL(a.href, window.location.origin);
397            if (url.hostname === window.location.hostname) {
398              return { url: url.href, title: a.textContent?.trim() || '' };
399            }
400          } catch (e) {}
401          return null;
402        })
403        .filter(Boolean)
404        .filter((link, index, self) => 
405          self.findIndex(l => l.url === link.url) === index
406        )
407        .slice(0, 100);
408
409      console.log('[ChatBot] Discovered', links.length, 'internal links');
410
411      // Send discovery data to backend
412      const response = await fetch(SUPABASE_URL + '/functions/v1/auto-discover', {
413        method: 'POST',
414        headers: {
415          'Content-Type': 'application/json',
416          'Authorization': 'Bearer ' + SUPABASE_ANON_KEY
417        },
418        body: JSON.stringify({
419          widgetId: widgetId,
420          domain: window.location.hostname,
421          currentPage: currentPage,
422          pages: links
423        })
424      });
425
426      if (response.ok) {
427        localStorage.setItem(discoveryKey, Date.now().toString());
428        console.log('[ChatBot] Discovery data sent successfully');
429      } else {
430        console.error('[ChatBot] Discovery failed:', await response.text());
431      }
432    } catch (error) {
433      console.error('[ChatBot] Discovery error:', error);
434    }
435  }
436
437  // Visitor presence tracking
438  async function trackVisitorPresence() {
439    if (!widgetId || !visitorId) return;
440
441    try {
442      const deviceInfo = getDeviceInfo();
443      const payload = {
444        visitor_id: visitorId,
445        widget_id: widgetId,
446        current_page_url: window.location.href,
447        page_title: document.title,
448        device_info: deviceInfo,
449        location: visitorLocation || {},
450        last_seen_at: new Date().toISOString()
451      };
452
453      const url = new URL('/rest/v1/active_visitors', SUPABASE_URL);
454
455      await fetch(url.toString(), {
456        method: 'POST',
457        headers: {
458          'apikey': SUPABASE_ANON_KEY,
459          'Authorization': 'Bearer ' + SUPABASE_ANON_KEY,
460          'Content-Type': 'application/json',
461          'Prefer': 'return=minimal'
462        },
463        body: JSON.stringify(payload)
464      });
465
466
467      console.log('[ChatBot] Visitor presence updated');
468    } catch (error) {
469      console.error('[ChatBot] Failed to track visitor:', error);
470    }
471  }
472
473  async function fetchVisitorLocation() {
474    try {
475      // Use a free IP geolocation API
476      const response = await fetch('https://ipapi.co/json/');
477      if (response.ok) {
478        const data = await response.json();
479        visitorLocation = {
480          country: data.country_name,
481          city: data.city,
482          region: data.region
483        };
484      }
485    } catch (error) {
486      console.log('[ChatBot] Could not fetch location:', error);
487    }
488  }
489
490  function startHeartbeat() {
491    // Initial presence update
492    trackVisitorPresence();
493    
494    // Update every 30 seconds
495    heartbeatInterval = setInterval(trackVisitorPresence, 30000);
496    
497    // Track page changes via visibility
498    document.addEventListener('visibilitychange', () => {
499      if (document.visibilityState === 'visible') {
500        trackVisitorPresence();
501      }
502    });
503
504    // Track before unload (best effort)
505    window.addEventListener('beforeunload', () => {
506      // Use sendBeacon for reliability
507      const payload = JSON.stringify({
508        visitor_id: visitorId,
509        widget_id: widgetId,
510        last_seen_at: new Date(Date.now() - 120000).toISOString() // Mark as 2 min ago = offline
511      });
512      
513      const url = new URL('/rest/v1/active_visitors', SUPABASE_URL);
514      url.searchParams.set('visitor_id', 'eq.' + visitorId);
515      url.searchParams.set('widget_id', 'eq.' + widgetId);
516      
517      navigator.sendBeacon && navigator.sendBeacon(
518        url.toString() + '&apikey=' + SUPABASE_ANON_KEY,
519        new Blob([payload], { type: 'application/json' })
520      );
521    });
522  }
523
524  // Subscribe to conversation status changes
525  function subscribeToConversation() {
526    if (!conversationId) return;
527    
528    // Simple polling approach for status changes (every 10 seconds)
529    const pollInterval = setInterval(async () => {
530      if (!conversationId) {
531        clearInterval(pollInterval);
532        return;
533      }
534      
535      try {
536        const url = new URL('/rest/v1/conversations', SUPABASE_URL);
537        url.searchParams.set('id', 'eq.' + conversationId);
538        url.searchParams.set('select', 'status,resolved_at');
539        
540        const response = await fetch(url.toString(), {
541          headers: {
542            'apikey': SUPABASE_ANON_KEY,
543            'Authorization': 'Bearer ' + SUPABASE_ANON_KEY
544          }
545        });
546        
547        if (isOpen) markMessagesRead();
548
549        const data = await response.json();
550        if (data[0]) {
551          const newStatus = data[0].status;
552          if (newStatus !== conversationStatus) {
553            conversationStatus = newStatus;
554            if (newStatus === 'resolved') {
555              showFeedbackOverlay();
556            }
557          }
558        }
559      } catch (error) {
560        console.error('[ChatBot] Failed to poll conversation status:', error);
561      }
562    }, 10000);
563  }
564
565  // Typing indicator functions
566  function subscribeToTyping() {
567    if (!conversationId || typingChannel) return;
568    
569    console.log('[ChatBot] Subscribing to typing channel');
570    
571    // Use Supabase Realtime channel for presence
572    const channelName = 'typing:' + conversationId;
573    
574    // Create a simple WebSocket-based approach using broadcast
575    typingChannel = {
576      lastBroadcast: 0
577    };
578    
579    // Poll for agent typing status (simple approach)
580    const typingPollInterval = setInterval(async () => {
581      if (!conversationId) {
582        clearInterval(typingPollInterval);
583        return;
584      }
585      
586      try {
587        // Check if there's any recent agent activity (last 5 seconds)
588        const url = new URL('/rest/v1/messages', SUPABASE_URL);
589        url.searchParams.set('conversation_id', 'eq.' + conversationId);
590        url.searchParams.set('sender_type', 'eq.agent');
591        url.searchParams.set('select', 'created_at');
592        url.searchParams.set('order', 'created_at.desc');
593        url.searchParams.set('limit', '1');
594        
595        const response = await fetch(url.toString(), {
596          headers: {
597            'apikey': SUPABASE_ANON_KEY,
598            'Authorization': 'Bearer ' + SUPABASE_ANON_KEY
599          }
600        });
601        
602        const data = await response.json();
603        if (data[0]) {
604          const lastMessageTime = new Date(data[0].created_at).getTime();
605          const now = Date.now();
606          // If last message was within 3 seconds, agent might have just sent
607          if (now - lastMessageTime < 3000 && isAgentTyping) {
608            hideAgentTypingIndicator();
609          }
610        }
611      } catch (error) {
612        // Silently fail
613      }
614    }, 5000);
615  }
616
617  function broadcastVisitorTyping(isTyping) {
618    // Clear existing timeout
619    if (typingTimeout) {
620      clearTimeout(typingTimeout);
621    }
622    
623    // Auto-clear typing after 3 seconds
624    if (isTyping) {
625      typingTimeout = setTimeout(() => {
626        // Typing stopped
627      }, 3000);
628    }
629    
630    // Note: Full realtime presence would require Supabase JS client
631    // For now, this is a stub that could be expanded
632    console.log('[ChatBot] Visitor typing:', isTyping);
633  }
634
635  function showAgentTypingIndicator() {
636    if (isAgentTyping) return;
637    isAgentTyping = true;
638    
639    const messagesContainer = document.querySelector('.chatbot-messages');
640    if (!messagesContainer) return;
641    
642    // Remove existing typing indicator
643    const existing = messagesContainer.querySelector('.chatbot-typing-indicator');
644    if (existing) existing.remove();
645    
646    const agent = getAiAgentInfo();
647    const typingHtml = document.createElement('div');
648    typingHtml.className = 'chatbot-typing-indicator';
649    typingHtml.innerHTML = `
650      <div class="chatbot-message-row ai">
651        <div class="chatbot-avatar-wrapper">
652          <div class="chatbot-ai-avatar" style="background: ${agent.color}">
653            ${agent.avatar}
654          </div>
655        </div>
656        <div class="chatbot-typing-bubble">
657          <span class="chatbot-typing-dot"></span>
658          <span class="chatbot-typing-dot"></span>
659          <span class="chatbot-typing-dot"></span>
660        </div>
661      </div>
662    `;
663    
664    messagesContainer.appendChild(typingHtml);
665    messagesContainer.scrollTop = messagesContainer.scrollHeight;
666  }
667
668  function hideAgentTypingIndicator() {
669    isAgentTyping = false;
670    const indicator = document.querySelector('.chatbot-typing-indicator');
671    if (indicator) indicator.remove();
672  }
673
674  // End chat and show feedback
675  async function endChat() {
676    if (!conversationId) return;
677    
678    try {
679      const url = new URL('/rest/v1/conversations', SUPABASE_URL);
680      url.searchParams.set('id', 'eq.' + conversationId);
681      
682      await fetch(url.toString(), {
683        method: 'PATCH',
684        headers: {
685          'apikey': SUPABASE_ANON_KEY,
686          'Authorization': 'Bearer ' + SUPABASE_ANON_KEY,
687          'Content-Type': 'application/json',
688          'Prefer': 'return=minimal'
689        },
690        body: JSON.stringify({
691          status: 'resolved',
692          resolved_at: new Date().toISOString(),
693          resolved_by: 'visitor'
694        })
695      });
696      
697      conversationStatus = 'resolved';
698      showFeedbackOverlay();
699    } catch (error) {
700      console.error('[ChatBot] Failed to end chat:', error);
701    }
702  }
703
704  // Submit feedback
705  async function submitFeedback(rating, comment) {
706    if (!conversationId) return;
707    
708    try {
709      const url = new URL('/rest/v1/conversations', SUPABASE_URL);
710      url.searchParams.set('id', 'eq.' + conversationId);
711      
712      await fetch(url.toString(), {
713        method: 'PATCH',
714        headers: {
715          'apikey': SUPABASE_ANON_KEY,
716          'Authorization': 'Bearer ' + SUPABASE_ANON_KEY,
717          'Content-Type': 'application/json',
718          'Prefer': 'return=minimal'
719        },
720        body: JSON.stringify({
721          feedback_rating: rating,
722          feedback_comment: comment || null
723        })
724      });
725      
726      console.log('[ChatBot] Feedback submitted');
727    } catch (error) {
728      console.error('[ChatBot] Failed to submit feedback:', error);
729    }
730  }
731
732  // Show feedback overlay
733  function showFeedbackOverlay() {
734    const chatView = document.querySelector('.chatbot-chat-view');
735    if (!chatView) return;
736    
737    // Remove existing overlay if any
738    const existingOverlay = chatView.querySelector('.chatbot-feedback-overlay');
739    if (existingOverlay) existingOverlay.remove();
740    
741    const overlay = document.createElement('div');
742    overlay.className = 'chatbot-feedback-overlay';
743    overlay.innerHTML = `
744      <div class="chatbot-feedback-icon">
745        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
746          <path d="M20 6L9 17l-5-5"/>
747        </svg>
748      </div>
749      <h3 class="chatbot-feedback-title">Chat Ended</h3>
750      <p class="chatbot-feedback-subtitle">How was your experience?</p>
751      <div class="chatbot-rating-stars">
752        <button class="chatbot-star" data-rating="1">★</button>
753        <button class="chatbot-star" data-rating="2">★</button>
754        <button class="chatbot-star" data-rating="3">★</button>
755        <button class="chatbot-star" data-rating="4">★</button>
756        <button class="chatbot-star" data-rating="5">★</button>
757      </div>
758      <textarea class="chatbot-feedback-textarea" placeholder="Any additional comments? (optional)" rows="3"></textarea>
759      <div class="chatbot-feedback-actions">
760        <button class="chatbot-feedback-submit" disabled>Submit Feedback</button>
761        <button class="chatbot-feedback-skip">Skip & Start New Chat</button>
762      </div>
763    `;
764    
765    chatView.appendChild(overlay);
766    
767    let selectedRating = 0;
768    const stars = overlay.querySelectorAll('.chatbot-star');
769    const submitBtn = overlay.querySelector('.chatbot-feedback-submit');
770    const skipBtn = overlay.querySelector('.chatbot-feedback-skip');
771    const textarea = overlay.querySelector('.chatbot-feedback-textarea');
772    
773    // Star rating
774    stars.forEach(star => {
775      star.addEventListener('click', () => {
776        selectedRating = parseInt(star.dataset.rating);
777        stars.forEach((s, i) => {
778          s.classList.toggle('active', i < selectedRating);
779        });
780        submitBtn.disabled = false;
781      });
782      
783      star.addEventListener('mouseenter', () => {
784        const rating = parseInt(star.dataset.rating);
785        stars.forEach((s, i) => {
786          s.style.color = i < rating ? '#fbbf24' : '#d1d5db';
787        });
788      });
789      
790      star.addEventListener('mouseleave', () => {
791        stars.forEach((s, i) => {
792          s.style.color = i < selectedRating ? '#fbbf24' : '#d1d5db';
793        });
794      });
795    });
796    
797    // Submit feedback
798    submitBtn.addEventListener('click', async () => {
799      submitBtn.disabled = true;
800      submitBtn.textContent = 'Submitting...';
801      
802      await submitFeedback(selectedRating, textarea.value.trim());
803      showFeedbackThanks(overlay);
804    });
805    
806    // Skip feedback
807    skipBtn.addEventListener('click', () => {
808      startNewChat();
809    });
810  }
811
812  function showFeedbackThanks(overlay) {
813    overlay.innerHTML = `
814      <div class="chatbot-feedback-icon">
815        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
816          <path d="M20 6L9 17l-5-5"/>
817        </svg>
818      </div>
819      <div class="chatbot-feedback-thanks">
820        <h3>Thank you!</h3>
821        <p>Your feedback helps us improve.</p>
822        <button class="chatbot-new-chat-btn">Start New Chat</button>
823      </div>
824    `;
825    
826    overlay.querySelector('.chatbot-new-chat-btn').addEventListener('click', startNewChat);
827  }
828
829  function startNewChat() {
830    // Clear conversation data
831    localStorage.removeItem(getConversationIdKey());
832    conversationId = null;
833    messages = [];
834    conversationStatus = 'ai_handling';
835    aiAgentInfo = null; // Reset AI agent for new personality
836    
837    // Remove feedback overlay
838    const overlay = document.querySelector('.chatbot-feedback-overlay');
839    if (overlay) overlay.remove();
840    
841    // Reset chat view
842    const chatView = document.querySelector('.chatbot-chat-view');
843    const messagesContainer = chatView.querySelector('.chatbot-messages');
844    const inputArea = chatView.querySelector('.chatbot-input-area');
845    
846    if (messagesContainer) messagesContainer.remove();
847    if (inputArea) inputArea.remove();
848    
849    // Show chat interface with welcome message
850    showChatInterface(chatView);
851    
852    // Switch to home view
853    switchToHome();
854  }
855
856  function extractPageContent() {
857    // Clone the body to manipulate
858    const clone = document.body.cloneNode(true);
859    
860    // Remove script, style, nav, footer, header elements
861    const removeSelectors = ['script', 'style', 'nav', 'footer', 'header', 'aside', '.chatbot-widget-container'];
862    removeSelectors.forEach(selector => {
863      clone.querySelectorAll(selector).forEach(el => el.remove());
864    });
865
866    // Get text content
867    let text = clone.textContent || '';
868    
869    // Clean up whitespace
870    text = text.replace(/\s+/g, ' ').trim();
871    
872    // Limit to first 10000 characters
873    return text.substring(0, 10000);
874  }
875
876  function findSitemapUrl() {
877    // Check common sitemap locations
878    const possibleSitemaps = [
879      '/sitemap.xml',
880      '/sitemap_index.xml',
881      '/sitemap-index.xml'
882    ];
883    
884    // Also check robots.txt link if present
885    const robotsLink = document.querySelector('link[rel="robots"]');
886    if (robotsLink) {
887      return window.location.origin + '/sitemap.xml';
888    }
889    
890    return window.location.origin + '/sitemap.xml';
891  }
892
893  function createStyles() {
894    const style = document.createElement('style');
895    style.textContent = `
896      @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
897      
898      .chatbot-widget-container * {
899        box-sizing: border-box;
900        font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
901      }
902      .chatbot-bubble {
903        position: fixed;
904        bottom: 24px;
905        width: 64px;
906        height: 64px;
907        border-radius: 50%;
908        cursor: pointer;
909        display: flex;
910        align-items: center;
911        justify-content: center;
912        box-shadow: 0 4px 24px rgba(0, 0, 0, 0.18), 0 0 0 1px rgba(0, 0, 0, 0.05);
913        transition: transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow 0.25s ease;
914        z-index: 999998;
915        background: linear-gradient(135deg, #1a1a1a 0%, #333 100%);
916      }
917      .chatbot-bubble:hover {
918        transform: scale(1.08);
919        box-shadow: 0 8px 32px rgba(0, 0, 0, 0.22), 0 0 0 1px rgba(0, 0, 0, 0.08);
920      }
921      .chatbot-bubble svg {
922        width: 28px;
923        height: 28px;
924      }
925      .chatbot-bubble.open svg.chat-icon {
926        display: none;
927      }
928      .chatbot-bubble.open svg.close-icon {
929        display: block;
930      }
931      .chatbot-bubble svg.close-icon {
932        display: none;
933      }
934      
935      /* Teaser styles */
936      .chatbot-teaser {
937        position: fixed;
938        bottom: 100px;
939        background: #fff;
940        border: none;
941        border-radius: 16px;
942        padding: 14px 18px;
943        max-width: 240px;
944        box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12), 0 0 0 1px rgba(0, 0, 0, 0.04);
945        z-index: 999997;
946        animation: chatbot-teaser-in 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
947        cursor: pointer;
948      }
949      .chatbot-teaser.left {
950        left: 24px;
951      }
952      .chatbot-teaser.right {
953        right: 24px;
954      }
955      .chatbot-teaser::after {
956        content: '';
957        position: absolute;
958        bottom: -8px;
959        width: 0;
960        height: 0;
961        border-left: 8px solid transparent;
962        border-right: 8px solid transparent;
963        border-top: 8px solid #fff;
964      }
965      .chatbot-teaser.left::after {
966        left: 24px;
967      }
968      .chatbot-teaser.right::after {
969        right: 24px;
970      }
971      .chatbot-teaser-text {
972        font-size: 14px;
973        font-weight: 500;
974        color: #1a1a1a;
975        line-height: 1.5;
976        margin: 0;
977        padding-right: 20px;
978      }
979      .chatbot-teaser-close {
980        position: absolute;
981        top: 10px;
982        right: 10px;
983        width: 20px;
984        height: 20px;
985        border: none;
986        background: #f3f4f6;
987        border-radius: 50%;
988        cursor: pointer;
989        display: flex;
990        align-items: center;
991        justify-content: center;
992        font-size: 14px;
993        color: #6b7280;
994        transition: all 0.2s ease;
995      }
996      .chatbot-teaser-close:hover {
997        background: #e5e7eb;
998        color: #374151;
999      }
1000      @keyframes chatbot-teaser-in {
1001        from {
1002          opacity: 0;
1003          transform: translateY(12px) scale(0.92);
1004        }
1005        to {
1006          opacity: 1;
1007          transform: translateY(0) scale(1);
1008        }
1009      }
1010      @keyframes chatbot-teaser-out {
1011        from {
1012          opacity: 1;
1013          transform: translateY(0) scale(1);
1014        }
1015        to {
1016          opacity: 0;
1017          transform: translateY(12px) scale(0.92);
1018        }
1019      }
1020      
1021      
1022      .chatbot-window {
1023        position: fixed;
1024        bottom: 100px;
1025        width: 400px;
1026        height: 620px;
1027        border-radius: 24px;
1028        overflow: hidden;
1029        display: none;
1030        flex-direction: column;
1031        box-shadow: 0 20px 60px rgba(0, 0, 0, 0.2), 0 0 0 1px rgba(0, 0, 0, 0.05);
1032        z-index: 999999;
1033        background: #fff;
1034      }
1035      .chatbot-window.open {
1036        display: flex;
1037        animation: chatbot-slide-up 0.35s cubic-bezier(0.34, 1.56, 0.64, 1);
1038      }
1039      @keyframes chatbot-slide-up {
1040        from {
1041          opacity: 0;
1042          transform: translateY(24px) scale(0.96);
1043        }
1044        to {
1045          opacity: 1;
1046          transform: translateY(0) scale(1);
1047        }
1048      }
1049      
1050      /* Home view styles */
1051      .chatbot-home-view {
1052        flex: 1;
1053        display: flex;
1054        flex-direction: column;
1055        background: #fafafa;
1056        min-height: 0;
1057        overflow: hidden;
1058      }
1059      .chatbot-home-header {
1060        background: linear-gradient(180deg, #1a1a1a 0%, #2a2a2a 50%, #3d3d3d 100%);
1061        padding: 20px 20px 36px;
1062        color: #fff;
1063        position: relative;
1064        flex-shrink: 0;
1065      }
1066      .chatbot-home-header-top {
1067        display: flex;
1068        align-items: center;
1069        justify-content: space-between;
1070        margin-bottom: 20px;
1071      }
1072      .chatbot-home-logo {
1073        width: 44px;
1074        height: 44px;
1075        border-radius: 12px;
1076        background: #fff;
1077        display: flex;
1078        align-items: center;
1079        justify-content: center;
1080        overflow: hidden;
1081        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
1082      }
1083      .chatbot-home-logo img {
1084        width: 100%;
1085        height: 100%;
1086        object-fit: cover;
1087      }
1088      .chatbot-home-logo svg {
1089        width: 24px;
1090        height: 24px;
1091      }
1092      .chatbot-home-close {
1093        background: rgba(255, 255, 255, 0.1);
1094        border: none;
1095        cursor: pointer;
1096        padding: 8px;
1097        color: rgba(255,255,255,0.8);
1098        transition: all 0.2s ease;
1099        border-radius: 10px;
1100      }
1101      .chatbot-home-close:hover {
1102        background: rgba(255, 255, 255, 0.2);
1103        color: #fff;
1104      }
1105      .chatbot-home-close svg {
1106        width: 20px;
1107        height: 20px;
1108      }
1109      .chatbot-home-greeting {
1110        font-size: 26px;
1111        font-weight: 700;
1112        line-height: 1.25;
1113        margin: 0;
1114        letter-spacing: -0.02em;
1115      }
1116      .chatbot-home-greeting .wave {
1117        display: inline-block;
1118        animation: wave-animation 2.5s infinite;
1119        transform-origin: 70% 70%;
1120        margin-left: 4px;
1121      }
1122      @keyframes wave-animation {
1123        0% { transform: rotate(0deg); }
1124        10% { transform: rotate(14deg); }
1125        20% { transform: rotate(-8deg); }
1126        30% { transform: rotate(14deg); }
1127        40% { transform: rotate(-4deg); }
1128        50% { transform: rotate(10deg); }
1129        60% { transform: rotate(0deg); }
1130        100% { transform: rotate(0deg); }
1131      }
1132      
1133      .chatbot-home-content {
1134        flex: 1;
1135        padding: 0 16px 16px;
1136        margin-top: -20px;
1137        overflow-y: auto;
1138        overflow-x: hidden;
1139        min-height: 0;
1140        -webkit-overflow-scrolling: touch;
1141      }
1142      
1143      .chatbot-powered-by {
1144        text-align: center;
1145        padding: 14px 0 6px;
1146        font-size: 11px;
1147        color: #9ca3af;
1148      }
1149      .chatbot-powered-by a {
1150        color: #6b7280;
1151        text-decoration: none;
1152        font-weight: 600;
1153        transition: color 0.2s ease;
1154      }
1155      .chatbot-powered-by a:hover {
1156        color: #374151;
1157      }
1158      
1159      .chatbot-action-card {
1160        background: #fff;
1161        border-radius: 16px;
1162        padding: 16px 18px;
1163        margin-bottom: 12px;
1164        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04), 0 0 0 1px rgba(0, 0, 0, 0.03);
1165        cursor: pointer;
1166        display: flex;
1167        align-items: center;
1168        justify-content: space-between;
1169        transition: all 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);
1170      }
1171      .chatbot-action-card:hover {
1172        transform: translateY(-2px);
1173        box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08), 0 0 0 1px rgba(0, 0, 0, 0.04);
1174      }
1175      .chatbot-action-card-content {
1176        flex: 1;
1177      }
1178      .chatbot-action-card-title {
1179        font-size: 15px;
1180        font-weight: 600;
1181        color: #1a1a1a;
1182        margin: 0 0 3px 0;
1183        letter-spacing: -0.01em;
1184      }
1185      .chatbot-action-card-subtitle {
1186        font-size: 13px;
1187        color: #6b7280;
1188        margin: 0;
1189      }
1190      .chatbot-action-card-icon {
1191        width: 40px;
1192        height: 40px;
1193        display: flex;
1194        align-items: center;
1195        justify-content: center;
1196        color: #1a1a1a;
1197        background: #f5f5f5;
1198        border-radius: 12px;
1199        transition: all 0.2s ease;
1200      }
1201      .chatbot-action-card:hover .chatbot-action-card-icon {
1202        background: #ebebeb;
1203      }
1204      .chatbot-action-card-icon svg {
1205        width: 20px;
1206        height: 20px;
1207      }
1208      
1209      
1210      .chatbot-status-card {
1211        background: #fff;
1212        border-radius: 14px;
1213        padding: 14px 16px;
1214        margin-bottom: 12px;
1215        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04), 0 0 0 1px rgba(0, 0, 0, 0.03);
1216        display: flex;
1217        align-items: center;
1218        gap: 12px;
1219      }
1220      .chatbot-status-icon {
1221        width: 32px;
1222        height: 32px;
1223        border-radius: 50%;
1224        background: linear-gradient(135deg, #dcfce7 0%, #bbf7d0 100%);
1225        display: flex;
1226        align-items: center;
1227        justify-content: center;
1228        flex-shrink: 0;
1229      }
1230      .chatbot-status-icon svg {
1231        width: 16px;
1232        height: 16px;
1233        color: #22c55e;
1234      }
1235      .chatbot-status-text {
1236        flex: 1;
1237        min-width: 0;
1238      }
1239      .chatbot-status-title {
1240        font-size: 13px;
1241        font-weight: 600;
1242        color: #1a1a1a;
1243        margin: 0 0 2px 0;
1244      }
1245      .chatbot-status-subtitle {
1246        font-size: 12px;
1247        color: #6b7280;
1248        margin: 0;
1249      }
1250      
1251      /* Section labels */
1252      .chatbot-section-label {
1253        font-size: 11px;
1254        font-weight: 700;
1255        color: #9ca3af;
1256        text-transform: uppercase;
1257        letter-spacing: 0.08em;
1258        margin: 16px 0 10px 4px;
1259      }
1260      
1261      /* Quick Questions */
1262      .chatbot-quick-questions {
1263        display: flex;
1264        flex-direction: column;
1265        gap: 8px;
1266        margin-bottom: 14px;
1267      }
1268      .chatbot-quick-question {
1269        display: flex;
1270        align-items: center;
1271        gap: 12px;
1272        background: #fff;
1273        border: 1px solid #e5e7eb;
1274        border-radius: 14px;
1275        padding: 14px 16px;
1276        cursor: pointer;
1277        text-align: left;
1278        font-size: 14px;
1279        font-weight: 500;
1280        color: #1a1a1a;
1281        transition: all 0.2s ease;
1282      }
1283      .chatbot-quick-question:hover {
1284        border-color: #1a1a1a;
1285        background: #fafafa;
1286        transform: translateX(4px);
1287      }
1288      .chatbot-qq-icon {
1289        font-size: 16px;
1290        flex-shrink: 0;
1291        width: 28px;
1292        height: 28px;
1293        display: flex;
1294        align-items: center;
1295        justify-content: center;
1296        background: #f5f5f5;
1297        border-radius: 8px;
1298      }
1299      
1300      /* Useful Links */
1301      .chatbot-links-section {
1302        display: flex;
1303        flex-wrap: wrap;
1304        gap: 8px;
1305        margin-bottom: 14px;
1306      }
1307      .chatbot-link-item {
1308        display: flex;
1309        align-items: center;
1310        gap: 6px;
1311        background: #f3f4f6;
1312        border-radius: 20px;
1313        padding: 8px 14px;
1314        font-size: 12px;
1315        font-weight: 500;
1316        color: #374151;
1317        text-decoration: none;
1318        transition: all 0.2s ease;
1319      }
1320      .chatbot-link-item:hover {
1321        background: #e5e7eb;
1322        color: #1a1a1a;
1323        transform: translateY(-1px);
1324      }
1325      .chatbot-link-item svg {
1326        width: 14px;
1327        height: 14px;
1328      }
1329      
1330      /* Bottom navigation */
1331      .chatbot-bottom-nav {
1332        display: flex;
1333        border-top: 1px solid #e5e7eb;
1334        background: #fff;
1335        padding: 4px 0;
1336      }
1337      .chatbot-nav-item {
1338        flex: 1;
1339        display: flex;
1340        flex-direction: column;
1341        align-items: center;
1342        padding: 12px 8px;
1343        background: none;
1344        border: none;
1345        cursor: pointer;
1346        color: #9ca3af;
1347        font-size: 12px;
1348        font-weight: 500;
1349        gap: 4px;
1350        transition: color 0.2s ease;
1351        position: relative;
1352      }
1353      .chatbot-nav-item:hover {
1354        color: #6b7280;
1355      }
1356      .chatbot-nav-item.active {
1357        color: #1a1a1a;
1358      }
1359      .chatbot-nav-item.active::after {
1360        content: '';
1361        position: absolute;
1362        bottom: 4px;
1363        left: 50%;
1364        transform: translateX(-50%);
1365        width: 4px;
1366        height: 4px;
1367        background: #1a1a1a;
1368        border-radius: 50%;
1369      }
1370      .chatbot-nav-item svg {
1371        width: 24px;
1372        height: 24px;
1373      }
1374      .chatbot-nav-badge {
1375        position: absolute;
1376        top: 6px;
1377        right: calc(50% - 22px);
1378        background: #ef4444;
1379        color: #fff;
1380        font-size: 10px;
1381        font-weight: 700;
1382        padding: 2px 6px;
1383        border-radius: 10px;
1384        min-width: 18px;
1385        text-align: center;
1386      }
1387      
1388      /* Chat view (messages) */
1389      .chatbot-chat-view {
1390        flex: 1;
1391        display: none;
1392        flex-direction: column;
1393        background: #fff;
1394        min-height: 0;
1395        overflow: hidden;
1396      }
1397      .chatbot-chat-view.active {
1398        display: flex;
1399      }
1400      .chatbot-home-view.hidden {
1401        display: none;
1402      }
1403      
1404      .chatbot-chat-header {
1405        padding: 18px 20px;
1406        display: flex;
1407        align-items: center;
1408        gap: 14px;
1409        border-bottom: 1px solid #f0f0f0;
1410        background: #fff;
1411        flex-shrink: 0;
1412      }
1413      .chatbot-chat-back {
1414        background: #f5f5f5;
1415        border: none;
1416        cursor: pointer;
1417        padding: 8px;
1418        color: #6b7280;
1419        display: flex;
1420        align-items: center;
1421        justify-content: center;
1422        border-radius: 10px;
1423        transition: all 0.2s ease;
1424      }
1425      .chatbot-chat-back:hover {
1426        background: #e5e5e5;
1427        color: #1a1a1a;
1428      }
1429      .chatbot-chat-back svg {
1430        width: 18px;
1431        height: 18px;
1432      }
1433      .chatbot-chat-header-title {
1434        font-size: 16px;
1435        font-weight: 600;
1436        color: #1a1a1a;
1437        margin: 0;
1438        flex: 1;
1439        letter-spacing: -0.01em;
1440      }
1441      
1442      .chatbot-messages {
1443        flex: 1;
1444        overflow-y: auto;
1445        overflow-x: hidden;
1446        padding: 20px;
1447        display: flex;
1448        flex-direction: column;
1449        gap: 14px;
1450        background: linear-gradient(180deg, #fafafa 0%, #f5f5f5 100%);
1451        min-height: 0;
1452        -webkit-overflow-scrolling: touch;
1453      }
1454      .chatbot-message {
1455        max-width: 85%;
1456        padding: 14px 18px;
1457        border-radius: 20px;
1458        font-size: 14px;
1459        line-height: 1.55;
1460        word-wrap: break-word;
1461      }
1462      .chatbot-message a {
1463        color: inherit;
1464        text-decoration: underline;
1465        font-weight: 600;
1466      }
1467      .chatbot-message a:hover {
1468        opacity: 0.85;
1469      }
1470      .chatbot-message.visitor {
1471        align-self: flex-end;
1472        background: linear-gradient(135deg, #1a1a1a 0%, #2d2d2d 100%);
1473        color: #fff;
1474        border-bottom-right-radius: 6px;
1475        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
1476      }
1477      .chatbot-message.ai, .chatbot-message.agent {
1478        align-self: flex-start;
1479        background: #fff;
1480        color: #1f2937;
1481        border: none;
1482        border-bottom-left-radius: 6px;
1483        padding: 14px 16px;
1484        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
1485      }
1486      .chatbot-message.ai a {
1487        color: #1a1a1a;
1488      }
1489      .chatbot-ai-header {
1490        display: flex;
1491        align-items: center;
1492        gap: 10px;
1493        margin-bottom: 8px;
1494      }
1495      .chatbot-ai-avatar {
1496        width: 30px;
1497        height: 30px;
1498        border-radius: 50%;
1499        display: flex;
1500        align-items: center;
1501        justify-content: center;
1502        font-size: 15px;
1503        animation: chatbot-avatar-wiggle 2s ease-in-out infinite;
1504        box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
1505      }
1506      @keyframes chatbot-avatar-wiggle {
1507        0%, 100% { transform: rotate(-3deg) scale(1); }
1508        25% { transform: rotate(3deg) scale(1.05); }
1509        50% { transform: rotate(-3deg) scale(1); }
1510        75% { transform: rotate(3deg) scale(1.02); }
1511      }
1512      .chatbot-ai-name {
1513        font-size: 13px;
1514        font-weight: 600;
1515        color: #374151;
1516      }
1517      .chatbot-ai-content {
1518        font-size: 14px;
1519        line-height: 1.55;
1520      }
1521      .chatbot-ai-content .chatbot-md-p { margin: 0 0 8px 0; }
1522      .chatbot-ai-content .chatbot-md-p:last-child { margin-bottom: 0; }
1523      .chatbot-ai-content .chatbot-md-list { margin: 6px 0 8px 0; padding-left: 20px; }
1524      .chatbot-ai-content .chatbot-md-list li { margin: 2px 0; }
1525      .chatbot-ai-content .chatbot-md-h { font-size: 14px; font-weight: 700; margin: 10px 0 6px 0; line-height: 1.3; }
1526      .chatbot-ai-content strong { font-weight: 700; }
1527      .chatbot-ai-content em { font-style: italic; }
1528      .chatbot-ai-content .chatbot-code-inline {
1529        font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
1530        font-size: 12.5px;
1531        background: rgba(0,0,0,0.06);
1532        padding: 1px 5px;
1533        border-radius: 4px;
1534      }
1535      .chatbot-ai-content .chatbot-code-block {
1536        background: #0f172a;
1537        color: #f8fafc;
1538        padding: 10px 12px;
1539        border-radius: 8px;
1540        overflow-x: auto;
1541        margin: 8px 0;
1542        font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
1543        font-size: 12.5px;
1544        line-height: 1.5;
1545      }
1546      .chatbot-link-buttons {
1547        display: flex;
1548        flex-wrap: wrap;
1549        gap: 8px;
1550        margin-top: 12px;
1551      }
1552      .chatbot-link-btn {
1553        display: inline-flex;
1554        align-items: center;
1555        gap: 6px;
1556        padding: 10px 14px;
1557        background: #f5f5f5;
1558        border: none;
1559        border-radius: 10px;
1560        font-size: 13px;
1561        font-weight: 500;
1562        color: #374151;
1563        text-decoration: none;
1564        transition: all 0.2s ease;
1565        max-width: 100%;
1566        word-break: break-all;
1567      }
1568      .chatbot-link-btn:hover {
1569        background: #e5e5e5;
1570        color: #1a1a1a;
1571        transform: translateY(-1px);
1572      }
1573      .chatbot-link-btn svg {
1574        width: 14px;
1575        height: 14px;
1576        flex-shrink: 0;
1577      }
1578      .chatbot-typing {
1579        align-self: flex-start;
1580        background: #fff;
1581        padding: 14px 18px;
1582        border-radius: 20px;
1583        border-bottom-left-radius: 6px;
1584        display: flex;
1585        gap: 5px;
1586        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
1587      }
1588      .chatbot-typing span {
1589        width: 8px;
1590        height: 8px;
1591        border-radius: 50%;
1592        background: #9ca3af;
1593        animation: chatbot-bounce 1.4s infinite ease-in-out;
1594      }
1595      .chatbot-typing span:nth-child(1) { animation-delay: -0.32s; }
1596      .chatbot-typing span:nth-child(2) { animation-delay: -0.16s; }
1597      @keyframes chatbot-bounce {
1598        0%, 80%, 100% { transform: scale(0.8); }
1599        40% { transform: scale(1.2); }
1600      }
1601      
1602      /* Typing indicator for agent */
1603      .chatbot-typing-indicator {
1604        animation: chatbot-fade-in 0.25s ease-out;
1605      }
1606      @keyframes chatbot-fade-in {
1607        from { opacity: 0; transform: translateY(6px); }
1608        to { opacity: 1; transform: translateY(0); }
1609      }
1610      .chatbot-typing-bubble {
1611        background: #fff;
1612        padding: 12px 16px;
1613        border-radius: 20px;
1614        border-bottom-left-radius: 6px;
1615        display: flex;
1616        gap: 5px;
1617        align-items: center;
1618        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
1619      }
1620      .chatbot-typing-dot {
1621        width: 8px;
1622        height: 8px;
1623        border-radius: 50%;
1624        background: #9ca3af;
1625        animation: chatbot-bounce 1.4s infinite ease-in-out;
1626      }
1627      .chatbot-typing-dot:nth-child(1) { animation-delay: -0.32s; }
1628      .chatbot-typing-dot:nth-child(2) { animation-delay: -0.16s; }
1629      .chatbot-typing-dot:nth-child(3) { animation-delay: 0s; }
1630      .chatbot-input-area {
1631        padding: 16px 20px;
1632        background: #fff;
1633        border-top: 1px solid #f0f0f0;
1634        display: flex;
1635        gap: 10px;
1636        align-items: center;
1637      }
1638      .chatbot-input {
1639        flex: 1;
1640        padding: 14px 18px;
1641        border: 1px solid #e5e7eb;
1642        border-radius: 28px;
1643        font-size: 14px;
1644        outline: none;
1645        transition: all 0.2s ease;
1646        background: #fafafa;
1647        color: #1a1a1a;
1648      }
1649      .chatbot-input:focus {
1650        border-color: #1a1a1a;
1651        background: #fff;
1652        box-shadow: 0 0 0 3px rgba(26, 26, 26, 0.08);
1653      }
1654      .chatbot-input::placeholder {
1655        color: #9ca3af;
1656      }
1657      .chatbot-send {
1658        width: 48px;
1659        height: 48px;
1660        border: none;
1661        border-radius: 50%;
1662        cursor: pointer;
1663        display: flex;
1664        align-items: center;
1665        justify-content: center;
1666        transition: all 0.2s ease;
1667        background: linear-gradient(135deg, #1a1a1a 0%, #333 100%);
1668        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
1669      }
1670      .chatbot-send:hover {
1671        transform: scale(1.05);
1672        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
1673      }
1674      .chatbot-send:disabled {
1675        opacity: 0.5;
1676        cursor: not-allowed;
1677        transform: none;
1678        box-shadow: none;
1679      }
1680      .chatbot-send svg {
1681        width: 20px;
1682        height: 20px;
1683        fill: #fff;
1684      }
1685      
1686      /* Pre-chat form styles */
1687      .chatbot-prechat-form {
1688        flex: 1;
1689        padding: 24px 20px;
1690        display: flex;
1691        flex-direction: column;
1692        gap: 18px;
1693        background: #fafafa;
1694        overflow-y: auto;
1695      }
1696      .chatbot-prechat-title {
1697        font-size: 20px;
1698        font-weight: 700;
1699        color: #1a1a1a;
1700        margin: 0 0 4px 0;
1701        letter-spacing: -0.02em;
1702      }
1703      .chatbot-prechat-subtitle {
1704        font-size: 14px;
1705        color: #6b7280;
1706        margin: 0 0 8px 0;
1707      }
1708      .chatbot-form-group {
1709        display: flex;
1710        flex-direction: column;
1711        gap: 8px;
1712      }
1713      .chatbot-form-label {
1714        font-size: 13px;
1715        font-weight: 600;
1716        color: #374151;
1717      }
1718      .chatbot-form-label .optional {
1719        font-weight: 400;
1720        color: #9ca3af;
1721        margin-left: 4px;
1722      }
1723      .chatbot-form-input {
1724        padding: 14px 16px;
1725        border: 1px solid #e5e7eb;
1726        border-radius: 14px;
1727        font-size: 14px;
1728        outline: none;
1729        transition: all 0.2s ease;
1730        background: #fff;
1731        color: #1f2937;
1732      }
1733      .chatbot-form-input::placeholder {
1734        color: #9ca3af;
1735      }
1736      .chatbot-form-input:focus {
1737        border-color: #1a1a1a;
1738        box-shadow: 0 0 0 3px rgba(26, 26, 26, 0.08);
1739      }
1740      .chatbot-form-input.error {
1741        border-color: #ef4444;
1742      }
1743      .chatbot-form-error {
1744        font-size: 12px;
1745        color: #ef4444;
1746        margin-top: 2px;
1747      }
1748      .chatbot-form-actions {
1749        display: flex;
1750        flex-direction: column;
1751        gap: 12px;
1752        margin-top: 12px;
1753      }
1754      .chatbot-form-submit {
1755        padding: 14px 24px;
1756        border: none;
1757        border-radius: 14px;
1758        font-size: 15px;
1759        font-weight: 600;
1760        cursor: pointer;
1761        transition: all 0.2s ease;
1762        background: linear-gradient(135deg, #1a1a1a 0%, #333 100%);
1763        color: #fff;
1764        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
1765      }
1766      .chatbot-form-submit:hover {
1767        transform: translateY(-1px);
1768        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
1769      }
1770      .chatbot-form-skip {
1771        padding: 12px 24px;
1772        background: transparent;
1773        border: none;
1774        color: #6b7280;
1775        font-size: 14px;
1776        font-weight: 500;
1777        cursor: pointer;
1778        transition: color 0.2s ease;
1779      }
1780      .chatbot-form-skip:hover {
1781        color: #374151;
1782      }
1783      
1784      /* Quick replies styles */
1785      .chatbot-quick-replies {
1786        display: flex;
1787        flex-wrap: wrap;
1788        gap: 8px;
1789        padding: 10px 0;
1790        animation: chatbot-fade-in 0.3s ease-out;
1791      }
1792      @keyframes chatbot-fade-in {
1793        from { opacity: 0; transform: translateY(6px); }
1794        to { opacity: 1; transform: translateY(0); }
1795      }
1796      .chatbot-quick-reply {
1797        background: #fff;
1798        border: 1.5px solid #1a1a1a;
1799        color: #1a1a1a;
1800        border-radius: 20px;
1801        padding: 10px 16px;
1802        font-size: 13px;
1803        font-weight: 500;
1804        cursor: pointer;
1805        transition: all 0.2s ease;
1806        white-space: nowrap;
1807        box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
1808      }
1809      .chatbot-quick-reply:hover {
1810        background: #1a1a1a;
1811        color: #fff;
1812        transform: translateY(-2px);
1813        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
1814      }
1815      .chatbot-quick-reply:active {
1816        transform: translateY(0);
1817      }
1818      
1819      /* Message reactions */
1820      .chatbot-message-wrapper {
1821        display: flex;
1822        flex-direction: column;
1823        gap: 4px;
1824      }
1825      .chatbot-message-wrapper.visitor {
1826        align-items: flex-end;
1827      }
1828      .chatbot-message-wrapper.ai {
1829        align-items: flex-start;
1830      }
1831      .chatbot-reactions {
1832        display: flex;
1833        flex-wrap: wrap;
1834        gap: 4px;
1835      }
1836      .chatbot-reaction-badge {
1837        display: flex;
1838        align-items: center;
1839        gap: 2px;
1840        padding: 2px 6px;
1841        font-size: 12px;
1842        border-radius: 12px;
1843        background: #fff;
1844        border: 1px solid #e5e7eb;
1845        cursor: pointer;
1846        transition: all 0.2s;
1847      }
1848      .chatbot-reaction-badge:hover {
1849        border-color: #d1d5db;
1850        background: #f9fafb;
1851      }
1852      .chatbot-reaction-badge.active {
1853        background: rgba(99, 102, 241, 0.1);
1854        border-color: rgba(99, 102, 241, 0.3);
1855      }
1856      .chatbot-reaction-badge .count {
1857        font-size: 10px;
1858        color: #6b7280;
1859      }
1860      .chatbot-reaction-picker {
1861        display: none;
1862        position: absolute;
1863        bottom: 100%;
1864        right: 0;
1865        background: #fff;
1866        border: 1px solid #e5e7eb;
1867        border-radius: 20px;
1868        padding: 4px 6px;
1869        box-shadow: 0 2px 10px rgba(0,0,0,0.1);
1870        gap: 2px;
1871        margin-bottom: 4px;
1872        z-index: 10;
1873      }
1874      .chatbot-reaction-picker.show {
1875        display: flex;
1876      }
1877      .chatbot-reaction-btn {
1878        background: none;
1879        border: none;
1880        font-size: 16px;
1881        padding: 4px;
1882        cursor: pointer;
1883        border-radius: 4px;
1884        transition: background 0.2s;
1885      }
1886      .chatbot-reaction-btn:hover {
1887        background: #f3f4f6;
1888      }
1889      .chatbot-add-reaction {
1890        background: none;
1891        border: none;
1892        cursor: pointer;
1893        padding: 4px;
1894        opacity: 0;
1895        transition: opacity 0.2s;
1896        color: #9ca3af;
1897        display: flex;
1898        align-items: center;
1899        justify-content: center;
1900      }
1901      .chatbot-message-wrapper:hover .chatbot-add-reaction {
1902        opacity: 1;
1903      }
1904      .chatbot-add-reaction:hover {
1905        color: #6b7280;
1906      }
1907      .chatbot-add-reaction svg {
1908        width: 16px;
1909        height: 16px;
1910      }
1911      .chatbot-message-footer {
1912        display: flex;
1913        align-items: center;
1914        gap: 4px;
1915        position: relative;
1916      }
1917      
1918      /* Loading spinner */
1919      .chatbot-loading {
1920        display: flex;
1921        flex-direction: column;
1922        align-items: center;
1923        justify-content: center;
1924        padding: 40px 20px;
1925        gap: 12px;
1926        color: #6b7280;
1927        font-size: 14px;
1928      }
1929      .chatbot-loading-spinner {
1930        width: 28px;
1931        height: 28px;
1932        border: 3px solid #e5e7eb;
1933        border-top-color: #1a1a1a;
1934        border-radius: 50%;
1935        animation: chatbot-spin 0.8s linear infinite;
1936      }
1937      @keyframes chatbot-spin {
1938        to { transform: rotate(360deg); }
1939      }
1940      
1941      /* End Chat button */
1942      .chatbot-end-chat-btn {
1943        background: none;
1944        border: none;
1945        cursor: pointer;
1946        padding: 6px;
1947        color: #6b7280;
1948        display: flex;
1949        align-items: center;
1950        justify-content: center;
1951        border-radius: 6px;
1952        transition: all 0.2s;
1953      }
1954      .chatbot-end-chat-btn:hover {
1955        background: #f3f4f6;
1956        color: #ef4444;
1957      }
1958      .chatbot-end-chat-btn svg {
1959        width: 20px;
1960        height: 20px;
1961      }
1962      
1963      /* Feedback overlay */
1964      .chatbot-feedback-overlay {
1965        position: absolute;
1966        inset: 0;
1967        background: rgba(255, 255, 255, 0.98);
1968        display: flex;
1969        flex-direction: column;
1970        align-items: center;
1971        justify-content: center;
1972        padding: 24px;
1973        z-index: 100;
1974        animation: chatbot-fade-in 0.3s ease-out;
1975      }
1976      .chatbot-feedback-icon {
1977        width: 64px;
1978        height: 64px;
1979        background: #dcfce7;
1980        border-radius: 50%;
1981        display: flex;
1982        align-items: center;
1983        justify-content: center;
1984        margin-bottom: 16px;
1985      }
1986      .chatbot-feedback-icon svg {
1987        width: 32px;
1988        height: 32px;
1989        color: #22c55e;
1990      }
1991      .chatbot-feedback-title {
1992        font-size: 18px;
1993        font-weight: 600;
1994        color: #1f2937;
1995        margin: 0 0 8px 0;
1996        text-align: center;
1997      }
1998      .chatbot-feedback-subtitle {
1999        font-size: 14px;
2000        color: #6b7280;
2001        margin: 0 0 24px 0;
2002        text-align: center;
2003      }
2004      .chatbot-rating-stars {
2005        display: flex;
2006        gap: 8px;
2007        margin-bottom: 16px;
2008      }
2009      .chatbot-star {
2010        background: none;
2011        border: none;
2012        cursor: pointer;
2013        padding: 4px;
2014        font-size: 28px;
2015        color: #d1d5db;
2016        transition: all 0.2s;
2017      }
2018      .chatbot-star:hover,
2019      .chatbot-star.active {
2020        color: #fbbf24;
2021        transform: scale(1.1);
2022      }
2023      .chatbot-feedback-textarea {
2024        width: 100%;
2025        padding: 12px;
2026        border: 1px solid #e5e7eb;
2027        border-radius: 10px;
2028        font-size: 14px;
2029        resize: none;
2030        margin-bottom: 16px;
2031        outline: none;
2032        transition: border-color 0.2s;
2033      }
2034      .chatbot-feedback-textarea:focus {
2035        border-color: #1a1a1a;
2036      }
2037      .chatbot-feedback-actions {
2038        display: flex;
2039        flex-direction: column;
2040        gap: 10px;
2041        width: 100%;
2042      }
2043      .chatbot-feedback-submit {
2044        padding: 12px 20px;
2045        border: none;
2046        border-radius: 10px;
2047        font-size: 14px;
2048        font-weight: 500;
2049        cursor: pointer;
2050        background: #1a1a1a;
2051        color: #fff;
2052        transition: opacity 0.2s;
2053      }
2054      .chatbot-feedback-submit:hover {
2055        opacity: 0.9;
2056      }
2057      .chatbot-feedback-submit:disabled {
2058        opacity: 0.5;
2059        cursor: not-allowed;
2060      }
2061      .chatbot-feedback-skip {
2062        padding: 10px 20px;
2063        background: transparent;
2064        border: none;
2065        color: #6b7280;
2066        font-size: 13px;
2067        cursor: pointer;
2068        transition: color 0.2s;
2069      }
2070      .chatbot-feedback-skip:hover {
2071        color: #374151;
2072      }
2073      .chatbot-feedback-thanks {
2074        text-align: center;
2075      }
2076      .chatbot-feedback-thanks h3 {
2077        font-size: 18px;
2078        font-weight: 600;
2079        color: #1f2937;
2080        margin: 0 0 8px 0;
2081      }
2082      .chatbot-feedback-thanks p {
2083        font-size: 14px;
2084        color: #6b7280;
2085        margin: 0 0 24px 0;
2086      }
2087      .chatbot-new-chat-btn {
2088        padding: 12px 24px;
2089        border: 1px solid #e5e7eb;
2090        border-radius: 10px;
2091        font-size: 14px;
2092        font-weight: 500;
2093        cursor: pointer;
2094        background: #fff;
2095        color: #1f2937;
2096        transition: all 0.2s;
2097      }
2098      .chatbot-new-chat-btn:hover {
2099        background: #f9fafb;
2100        border-color: #d1d5db;
2101      }
2102      
2103      /* Chat resolved banner */
2104      .chatbot-resolved-banner {
2105        background: #fef3c7;
2106        border-bottom: 1px solid #fcd34d;
2107        padding: 8px 16px;
2108        text-align: center;
2109        font-size: 13px;
2110        color: #92400e;
2111      }
2112      
2113      
2114      @media (max-width: 480px) {
2115        .chatbot-window {
2116          width: calc(100% - 24px);
2117          height: calc(100% - 120px);
2118          bottom: 92px;
2119          left: 12px !important;
2120          right: 12px !important;
2121          border-radius: 20px;
2122        }
2123        .chatbot-teaser {
2124          max-width: calc(100% - 100px);
2125        }
2126        .chatbot-quick-reply {
2127          font-size: 13px;
2128          padding: 8px 14px;
2129        }
2130        .chatbot-home-greeting {
2131          font-size: 22px;
2132        }
2133        .chatbot-bubble {
2134          width: 60px;
2135          height: 60px;
2136          bottom: 20px;
2137        }
2138        .chatbot-messages {
2139          padding: 16px;
2140        }
2141        .chatbot-message {
2142          padding: 12px 16px;
2143        }
2144        .chatbot-input-area {
2145          padding: 14px 16px;
2146        }
2147        .chatbot-input {
2148          padding: 12px 16px;
2149        }
2150        .chatbot-send {
2151          width: 44px;
2152          height: 44px;
2153        }
2154      }
2155    `;
2156    document.head.appendChild(style);
2157  }
2158
2159
2160  let currentView = 'home'; // 'home' or 'messages'
2161
2162  function toggleWindow() {
2163    isOpen = !isOpen;
2164    const chatWin = document.querySelector('.chatbot-window');
2165    const bubble = document.querySelector('.chatbot-bubble');
2166    if (isOpen) {
2167      chatWin.classList.add('open');
2168      bubble.classList.add('open');
2169      hideTeaser();
2170      markMessagesRead();
2171    } else {
2172      chatWin.classList.remove('open');
2173      bubble.classList.remove('open');
2174    }
2175  }
2176
2177  function switchToMessages() {
2178    currentView = 'messages';
2179    const homeView = document.querySelector('.chatbot-home-view');
2180    const chatView = document.querySelector('.chatbot-chat-view');
2181    const homeNav = document.querySelector('.chatbot-nav-item[data-view="home"]');
2182    const messagesNav = document.querySelector('.chatbot-nav-item[data-view="messages"]');
2183    
2184    if (homeView) homeView.classList.add('hidden');
2185    if (chatView) chatView.classList.add('active');
2186    if (homeNav) homeNav.classList.remove('active');
2187    if (messagesNav) messagesNav.classList.add('active');
2188    
2189    // Focus on input
2190    const input = document.querySelector('.chatbot-input');
2191    if (input) input.focus();
2192  }
2193
2194  function switchToHome() {
2195    currentView = 'home';
2196    const homeView = document.querySelector('.chatbot-home-view');
2197    const chatView = document.querySelector('.chatbot-chat-view');
2198    const homeNav = document.querySelector('.chatbot-nav-item[data-view="home"]');
2199    const messagesNav = document.querySelector('.chatbot-nav-item[data-view="messages"]');
2200    
2201    if (homeView) homeView.classList.remove('hidden');
2202    if (chatView) chatView.classList.remove('active');
2203    if (homeNav) homeNav.classList.add('active');
2204    if (messagesNav) messagesNav.classList.remove('active');
2205  }
2206
2207  // Teaser functions
2208  function playTeaserSound() {
2209    try {
2210      // Create a subtle notification sound using Web Audio API
2211      const audioContext = new (window.AudioContext || window.webkitAudioContext)();
2212      const oscillator = audioContext.createOscillator();
2213      const gainNode = audioContext.createGain();
2214      
2215      oscillator.connect(gainNode);
2216      gainNode.connect(audioContext.destination);
2217      
2218      oscillator.frequency.setValueAtTime(800, audioContext.currentTime);
2219      oscillator.frequency.setValueAtTime(1000, audioContext.currentTime + 0.1);
2220      
2221      gainNode.gain.setValueAtTime(0.1, audioContext.currentTime);
2222      gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.3);
2223      
2224      oscillator.start(audioContext.currentTime);
2225      oscillator.stop(audioContext.currentTime + 0.3);
2226    } catch (e) {
2227      console.log('[ChatBot] Sound playback not supported');
2228    }
2229  }
2230
2231  function showTeaser() {
2232    if (teaserShown || isOpen) return;
2233    
2234    const teaserDismissed = sessionStorage.getItem('chatbot_teaser_dismissed_' + widgetId);
2235    if (teaserDismissed) return;
2236    
2237    const position = settings?.position || 'right';
2238    const message = settings?.teaser_message || 'How can I help you?';
2239    const soundEnabled = settings?.teaser_sound_enabled !== false;
2240    
2241    const teaser = document.createElement('div');
2242    teaser.className = 'chatbot-teaser ' + position;
2243    teaser.innerHTML = `
2244      <p class="chatbot-teaser-text">${message}</p>
2245      <button class="chatbot-teaser-close">×</button>
2246    `;
2247    
2248    document.querySelector('.chatbot-widget-container').appendChild(teaser);
2249    teaserShown = true;
2250    
2251    // Play sound if enabled
2252    if (soundEnabled) {
2253      playTeaserSound();
2254    }
2255    
2256    // Click on teaser opens chat
2257    teaser.addEventListener('click', (e) => {
2258      if (!e.target.classList.contains('chatbot-teaser-close')) {
2259        hideTeaser();
2260        if (!isOpen) toggleWindow();
2261      }
2262    });
2263    
2264    // Close button
2265    teaser.querySelector('.chatbot-teaser-close').addEventListener('click', (e) => {
2266      e.stopPropagation();
2267      hideTeaser();
2268      sessionStorage.setItem('chatbot_teaser_dismissed_' + widgetId, 'true');
2269    });
2270  }
2271
2272  function hideTeaser() {
2273    const teaser = document.querySelector('.chatbot-teaser');
2274    if (teaser) {
2275      teaser.style.animation = 'chatbot-teaser-out 0.3s ease-out forwards';
2276      setTimeout(() => teaser.remove(), 300);
2277    }
2278    if (teaserTimer) {
2279      clearTimeout(teaserTimer);
2280      teaserTimer = null;
2281    }
2282  }
2283
2284  function startTeaserTimer() {
2285    const teaserEnabled = settings?.teaser_enabled !== false;
2286    const delay = (settings?.teaser_delay || 5) * 1000;
2287    
2288    if (!teaserEnabled) return;
2289    
2290    teaserTimer = setTimeout(() => {
2291      showTeaser();
2292    }, delay);
2293  }
2294
2295  function extractLinks(text) {
2296    const urlRegex = /(https?:\/\/[^\s<>"{}|\\^`\[\]]+)/g;
2297    const links = [];
2298    let match;
2299    while ((match = urlRegex.exec(text)) !== null) {
2300      links.push(match[1]);
2301    }
2302    return links;
2303  }
2304
2305  function removeLinksFromText(text) {
2306    const urlRegex = /(https?:\/\/[^\s<>"{}|\\^`\[\]]+)/g;
2307    // Remove URLs and clean up extra whitespace/punctuation
2308    return text.replace(urlRegex, '').replace(/\s+/g, ' ').replace(/\s+([.,!?])/g, '$1').trim();
2309  }
2310
2311  function getLinkDomain(url) {
2312    try {
2313      const u = new URL(url);
2314      return u.hostname.replace('www.', '');
2315    } catch {
2316      return url;
2317    }
2318  }
2319
2320  function isInternalLink(url) {
2321    try {
2322      const linkHost = new URL(url).hostname;
2323      const currentHost = window.location.hostname;
2324      return linkHost === currentHost || linkHost === 'www.' + currentHost || 'www.' + linkHost === currentHost;
2325    } catch {
2326      return false;
2327    }
2328  }
2329
2330  function renderLinkButtons(links) {
2331    if (!links || links.length === 0) return '';
2332    
2333    return '<div class="chatbot-link-buttons">' + links.map(url => {
2334      const isInternal = isInternalLink(url);
2335      const domain = getLinkDomain(url);
2336      const target = isInternal ? '_self' : '_blank';
2337      const rel = isInternal ? '' : 'rel="noopener noreferrer"';
2338      const icon = isInternal 
2339        ? '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M5 12h14M12 5l7 7-7 7"/></svg>'
2340        : '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6M15 3h6v6M10 14L21 3"/></svg>';
2341      return '<a href="' + url + '" target="' + target + '" ' + rel + ' class="chatbot-link-btn">' + icon + '<span>' + domain + '</span></a>';
2342    }).join('') + '</div>';
2343  }
2344
2345  function escapeHtml(text) {
2346    const div = document.createElement('div');
2347    div.textContent = text;
2348    return div.innerHTML;
2349  }
2350
2351  // Lightweight, safe markdown formatter. Escapes HTML first, then applies
2352  // a small subset of markdown: headings, bold, italic, inline code, code blocks,
2353  // unordered/ordered lists, and paragraph/line breaks.
2354  function formatMarkdown(text) {
2355    if (!text) return '';
2356    let src = String(text).replace(/\r\n/g, '\n');
2357    const codeBlocks = [];
2358    src = src.replace(/```([\s\S]*?)```/g, function(_, code){
2359      codeBlocks.push(code);
2360      return '\u0000CB' + (codeBlocks.length - 1) + '\u0000';
2361    });
2362    src = escapeHtml(src);
2363    src = src.replace(/`([^`\n]+)`/g, '<code class="chatbot-code-inline">$1</code>');
2364    src = src.replace(/\*\*([^*\n]+)\*\*/g, '<strong>$1</strong>');
2365    src = src.replace(/(^|[^*])\*([^*\n]+)\*/g, '$1<em>$2</em>');
2366    src = src.replace(/^### (.+)$/gm, '<h4 class="chatbot-md-h">$1</h4>');
2367    src = src.replace(/^## (.+)$/gm, '<h3 class="chatbot-md-h">$1</h3>');
2368    src = src.replace(/^# (.+)$/gm, '<h2 class="chatbot-md-h">$1</h2>');
2369    const lines = src.split('\n');
2370    const out = [];
2371    let i = 0;
2372    while (i < lines.length) {
2373      const ul = /^\s*[-*]\s+(.*)$/.exec(lines[i]);
2374      const ol = /^\s*\d+\.\s+(.*)$/.exec(lines[i]);
2375      if (ul || ol) {
2376        const tag = ul ? 'ul' : 'ol';
2377        const re = ul ? /^\s*[-*]\s+(.*)$/ : /^\s*\d+\.\s+(.*)$/;
2378        const items = [];
2379        while (i < lines.length && re.test(lines[i])) {
2380          items.push('<li>' + lines[i].replace(re, '$1') + '</li>');
2381          i++;
2382        }
2383        out.push('<' + tag + ' class="chatbot-md-list">' + items.join('') + '</' + tag + '>');
2384      } else {
2385        out.push(lines[i]);
2386        i++;
2387      }
2388    }
2389    src = out.join('\n');
2390    src = src.split(/\n{2,}/).map(function(block){
2391      if (/^\s*<(ul|ol|h2|h3|h4)/.test(block)) return block;
2392      if (/^\u0000CB\d+\u0000$/.test(block.trim())) return block;
2393      return '<p class="chatbot-md-p">' + block.replace(/\n/g, '<br>') + '</p>';
2394    }).join('');
2395    src = src.replace(/\u0000CB(\d+)\u0000/g, function(_, n){
2396      return '<pre class="chatbot-code-block"><code>' + escapeHtml(codeBlocks[+n]) + '</code></pre>';
2397    });
2398    return src;
2399  }
2400
2401  const REACTION_EMOJIS = ['\u{1F44D}', '\u2764\uFE0F', '\u{1F60A}', '\u{1F602}', '\u{1F62E}', '\u{1F622}'];
2402
2403  function renderReactions(messageId) {
2404    const reactions = messageReactions[messageId] || [];
2405    if (reactions.length === 0) return '';
2406    
2407    // Group by emoji
2408    const grouped = {};
2409    reactions.forEach(r => {
2410      if (!grouped[r.emoji]) grouped[r.emoji] = { count: 0, hasMyReaction: false };
2411      grouped[r.emoji].count++;
2412      if (r.reactor_type === 'visitor' && r.reactor_id === visitorId) {
2413        grouped[r.emoji].hasMyReaction = true;
2414      }
2415    });
2416    
2417    return '<div class="chatbot-reactions">' + Object.entries(grouped).map(([emoji, data]) => 
2418      `<button class="chatbot-reaction-badge${data.hasMyReaction ? ' active' : ''}" data-emoji="${emoji}" data-message-id="${messageId}">
2419        <span>${emoji}</span>${data.count > 1 ? `<span class="count">${data.count}</span>` : ''}
2420      </button>`
2421    ).join('') + '</div>';
2422  }
2423
2424  function renderReactionPicker(messageId) {
2425    return `
2426      <div class="chatbot-message-footer">
2427        <button class="chatbot-add-reaction" data-message-id="${messageId}">
2428          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
2429            <circle cx="12" cy="12" r="10"/>
2430            <path d="M8 14s1.5 2 4 2 4-2 4-2"/>
2431            <line x1="9" y1="9" x2="9.01" y2="9"/>
2432            <line x1="15" y1="9" x2="15.01" y2="9"/>
2433          </svg>
2434        </button>
2435        <div class="chatbot-reaction-picker" data-message-id="${messageId}">
2436          ${REACTION_EMOJIS.map(e => `<button class="chatbot-reaction-btn" data-emoji="${e}" data-message-id="${messageId}">${e}</button>`).join('')}
2437        </div>
2438      </div>
2439    `;
2440  }
2441
2442  function renderMessages() {
2443    const container = document.querySelector('.chatbot-messages');
2444    if (!container) return;
2445
2446    const agent = getAiAgentInfo();
2447
2448    container.innerHTML = messages.map((msg, index) => {
2449      const safeContent = escapeHtml(msg.content);
2450      const messageId = msg.id || messageIdMap[index];
2451      const reactionsHtml = messageId ? renderReactions(messageId) : '';
2452      const pickerHtml = messageId ? renderReactionPicker(messageId) : '';
2453      
2454      if (msg.role === 'ai') {
2455        // Extract links and render as buttons
2456        const links = extractLinks(msg.content);
2457        const textWithoutLinks = formatMarkdown(removeLinksFromText(msg.content));
2458        const linkButtons = renderLinkButtons(links);
2459        
2460        return `
2461          <div class="chatbot-message-wrapper ${msg.role}">
2462            <div class="chatbot-message ${msg.role}">
2463              <div class="chatbot-ai-header">
2464                <div class="chatbot-ai-avatar" style="background: ${agent.color};">${agent.avatar}</div>
2465                <span class="chatbot-ai-name">${agent.name}</span>
2466              </div>
2467              <div class="chatbot-ai-content">${textWithoutLinks}${linkButtons}</div>
2468              ${pickerHtml}
2469            </div>
2470            ${reactionsHtml}
2471          </div>
2472        `;
2473      }
2474      return `
2475        <div class="chatbot-message-wrapper ${msg.role}">
2476          <div class="chatbot-message ${msg.role}">${safeContent}${pickerHtml}</div>
2477          ${reactionsHtml}
2478        </div>
2479      `;
2480    }).join('');
2481
2482    // Add event listeners for reaction buttons
2483    container.querySelectorAll('.chatbot-add-reaction').forEach(btn => {
2484      btn.addEventListener('click', (e) => {
2485        e.stopPropagation();
2486        const messageId = btn.dataset.messageId;
2487        const picker = container.querySelector(`.chatbot-reaction-picker[data-message-id="${messageId}"]`);
2488        // Hide all other pickers
2489        container.querySelectorAll('.chatbot-reaction-picker.show').forEach(p => p.classList.remove('show'));
2490        picker?.classList.toggle('show');
2491      });
2492    });
2493
2494    container.querySelectorAll('.chatbot-reaction-btn').forEach(btn => {
2495      btn.addEventListener('click', (e) => {
2496        e.stopPropagation();
2497        const messageId = btn.dataset.messageId;
2498        const emoji = btn.dataset.emoji;
2499        toggleReaction(messageId, emoji);
2500        container.querySelector(`.chatbot-reaction-picker[data-message-id="${messageId}"]`)?.classList.remove('show');
2501      });
2502    });
2503
2504    container.querySelectorAll('.chatbot-reaction-badge').forEach(btn => {
2505      btn.addEventListener('click', (e) => {
2506        e.stopPropagation();
2507        const messageId = btn.dataset.messageId;
2508        const emoji = btn.dataset.emoji;
2509        toggleReaction(messageId, emoji);
2510      });
2511    });
2512
2513    // Close picker when clicking outside
2514    document.addEventListener('click', () => {
2515      container.querySelectorAll('.chatbot-reaction-picker.show').forEach(p => p.classList.remove('show'));
2516    });
2517
2518    container.scrollTop = container.scrollHeight;
2519  }
2520
2521  function renderQuickReplies(quickReplies) {
2522    // Remove existing quick replies
2523    const existing = document.querySelector('.chatbot-quick-replies');
2524    if (existing) existing.remove();
2525    
2526    if (!quickReplies || quickReplies.length === 0) return;
2527    
2528    const container = document.querySelector('.chatbot-messages');
2529    if (!container) return;
2530    
2531    const primaryColor = settings?.primary_color || '#6366f1';
2532    
2533    const wrapper = document.createElement('div');
2534    wrapper.className = 'chatbot-quick-replies';
2535    wrapper.innerHTML = quickReplies.map(reply => 
2536      `<button class="chatbot-quick-reply" style="border-color: ${primaryColor}; color: ${primaryColor};">${escapeHtml(reply)}</button>`
2537    ).join('');
2538    
2539    container.appendChild(wrapper);
2540    container.scrollTop = container.scrollHeight;
2541    
2542    // Add click handlers
2543    wrapper.querySelectorAll('.chatbot-quick-reply').forEach(btn => {
2544      btn.addEventListener('click', () => {
2545        const input = document.querySelector('.chatbot-input');
2546        if (input) {
2547          input.value = btn.textContent;
2548          handleSend(input);
2549        }
2550      });
2551    });
2552  }
2553
2554  function showTyping() {
2555    const container = document.querySelector('.chatbot-messages');
2556    if (!container) return;
2557
2558    const typing = document.createElement('div');
2559    typing.className = 'chatbot-typing';
2560    typing.innerHTML = '<span></span><span></span><span></span>';
2561    container.appendChild(typing);
2562    container.scrollTop = container.scrollHeight;
2563  }
2564
2565  function hideTyping() {
2566    const typing = document.querySelector('.chatbot-typing');
2567    if (typing) typing.remove();
2568  }
2569
2570  async function handleSend(input) {
2571    const message = input.value.trim();
2572    if (!message) return;
2573
2574    input.value = '';
2575    messages.push({ role: 'visitor', content: message });
2576    renderMessages();
2577
2578    const primaryColor = settings?.primary_color || '#6366f1';
2579    const textColor = settings?.text_color || '#ffffff';
2580    const visitorMsg = document.querySelector('.chatbot-message.visitor:last-child');
2581    if (visitorMsg) {
2582      visitorMsg.style.backgroundColor = primaryColor;
2583      visitorMsg.style.color = textColor;
2584    }
2585
2586    // Remove existing quick replies before sending
2587    const existingReplies = document.querySelector('.chatbot-quick-replies');
2588    if (existingReplies) existingReplies.remove();
2589    
2590    showTyping();
2591    const result = await sendMessage(message);
2592    hideTyping();
2593
2594    messages.push({ role: 'ai', content: result.response });
2595    renderMessages();
2596    
2597    // Show quick reply suggestions
2598    if (result.quick_replies && result.quick_replies.length > 0) {
2599      renderQuickReplies(result.quick_replies);
2600    }
2601    
2602  }
2603
2604  async function showChatInterface(chatView) {
2605    const welcomeMessage = settings?.welcome_message || 'Hello! How can I help you today?';
2606
2607    // Add messages container and input area to chat view
2608    const messagesDiv = document.createElement('div');
2609    messagesDiv.className = 'chatbot-messages';
2610    
2611    const inputArea = document.createElement('div');
2612    inputArea.className = 'chatbot-input-area';
2613    inputArea.innerHTML = `
2614      <input type="text" class="chatbot-input" placeholder="Type a message..." />
2615      <button class="chatbot-send">
2616        <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
2617          <path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/>
2618        </svg>
2619      </button>
2620    `;
2621
2622    chatView.appendChild(messagesDiv);
2623    chatView.appendChild(inputArea);
2624
2625    // Load existing messages if we have a conversation, otherwise show wel
2625come
2626    if (conversationId) {
2627      // Show loading spinner while fetching
2628      messagesDiv.innerHTML = `
2629        <div class="chatbot-loading">
2630          <div class="chatbot-loading-spinner"></div>
2631          <span>Loading messages...</span>
2632        </div>
2633      `;
2634      
2635      const existingMessages = await fetchExistingMessages();
2636      if (existingMessages.length > 0) {
2637        messages = existingMessages;
2638      } else {
2639        messages = [{ role: 'ai', content: welcomeMessage }];
2640      }
2641      
2642      // Subscribe to conversation status changes
2643      subscribeToConversation();
2644    } else {
2645      messages = [{ role: 'ai', content: welcomeMessage }];
2646    }
2647    renderMessages();
2648
2649    // Set up input listeners
2650    const input = inputArea.querySelector('.chatbot-input');
2651    const sendBtn = inputArea.querySelector('.chatbot-send');
2652
2653    sendBtn.addEventListener('click', () => handleSend(input));
2654    input.addEventListener('keypress', (e) => {
2655      if (e.key === 'Enter') handleSend(input);
2656    });
2657    
2658    // Track visitor typing
2659    input.addEventListener('input', (e) => {
2660      const value = e.target.value;
2661      broadcastVisitorTyping(value.trim().length > 0);
2662    });
2663    
2664    // Subscribe to typing indicators
2665    subscribeToTyping();
2666  }
2667
2668  function showPrechatForm(container) {
2669    const formHtml = `
2670      <div class="chatbot-prechat-form">
2671        <div>
2672          <h3 class="chatbot-prechat-title">Welcome! 👋</h3>
2673          <p class="chatbot-prechat-subtitle">Please share some details so we can help you better.</p>
2674        </div>
2675        
2676        <div class="chatbot-form-group">
2677          <label class="chatbot-form-label">Name <span class="optional">(required)</span></label>
2678          <input type="text" class="chatbot-form-input" id="chatbot-name" placeholder="Your name" required />
2679        </div>
2680        
2681        <div class="chatbot-form-group">
2682          <label class="chatbot-form-label">Email <span class="optional">(required)</span></label>
2683          <input type="email" class="chatbot-form-input" id="chatbot-email" placeholder="[email protected]" required />
2684        </div>
2685        
2686        <div class="chatbot-form-group">
2687          <label class="chatbot-form-label">Phone <span class="optional">(optional)</span></label>
2688          <input type="tel" class="chatbot-form-input" id="chatbot-phone" placeholder="+1 (555) 000-0000" />
2689        </div>
2690        
2691        <div class="chatbot-form-group">
2692          <label class="chatbot-form-label">Company <span class="optional">(optional)</span></label>
2693          <input type="text" class="chatbot-form-input" id="chatbot-company" placeholder="Company name" />
2694        </div>
2695        
2696        <div class="chatbot-form-group">
2697          <label class="chatbot-form-label">Title/Role <span class="optional">(optional)</span></label>
2698          <input type="text" class="chatbot-form-input" id="chatbot-designation" placeholder="Your role" />
2699        </div>
2700        
2701        <div class="chatbot-form-actions">
2702          <button class="chatbot-form-submit" id="chatbot-submit">Start Chat</button>
2703        </div>
2704      </div>
2705    `;
2706
2707    container.insertAdjacentHTML('beforeend', formHtml);
2708
2709    // Form submission handler
2710    const submitBtn = container.querySelector('#chatbot-submit');
2711    const nameInput = container.querySelector('#chatbot-name');
2712    const emailInput = container.querySelector('#chatbot-email');
2713    const phoneInput = container.querySelector('#chatbot-phone');
2714    const companyInput = container.querySelector('#chatbot-company');
2715    const designationInput = container.querySelector('#chatbot-designation');
2716
2717    submitBtn.addEventListener('click', async () => {
2718      // Validate required fields
2719      let hasError = false;
2720      
2721      // Clear previous errors
2722      container.querySelectorAll('.chatbot-form-error').forEach(el => el.remove());
2723      container.querySelectorAll('.chatbot-form-input.error').forEach(el => el.classList.remove('error'));
2724
2725      if (!nameInput.value.trim()) {
2726        nameInput.classList.add('error');
2727        nameInput.insertAdjacentHTML('afterend', '<span class="chatbot-form-error">Name is required</span>');
2728        hasError = true;
2729      }
2730
2731      if (!emailInput.value.trim()) {
2732        emailInput.classList.add('error');
2733        emailInput.insertAdjacentHTML('afterend', '<span class="chatbot-form-error">Email is required</span>');
2734        hasError = true;
2735      } else if (!isValidEmail(emailInput.value.trim())) {
2736        emailInput.classList.add('error');
2737        emailInput.insertAdjacentHTML('afterend', '<span class="chatbot-form-error">Please enter a valid email</span>');
2738        hasError = true;
2739      }
2740
2741      if (hasError) return;
2742
2743      // Save visitor info
2744      const info = {
2745        name: nameInput.value.trim(),
2746        email: emailInput.value.trim(),
2747        phone: phoneInput.value.trim() || null,
2748        company: companyInput.value.trim() || null,
2749        designation: designationInput.value.trim() || null
2750      };
2751      saveVisitorInfo(info);
2752
2753      // Remove form and show chat interface
2754      const form = container.querySelector('.chatbot-prechat-form');
2755      if (form) form.remove();
2756      await showChatInterface(container);
2757      
2758      // Update greeting with name
2759      const greeting = document.querySelector('.chatbot-home-greeting');
2760      if (greeting) {
2761        greeting.innerHTML = `Hi ${info.name.split(' ')[0]} <span class="wave">👋</span><br/>How can we help?`;
2762      }
2763    });
2764
2765  }
2766
2767  function isValidEmail(email) {
2768    return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
2769  }
2770
2771  function createWidget() {
2772    if (!document.body) {
2773      console.error('[ChatBot] document.body not available');
2774      return;
2775    }
2776    
2777    console.log('[ChatBot] Creating widget...');
2778    
2779    // Load any existing visitor info
2780    loadVisitorInfo();
2781    
2782    const container = document.createElement('div');
2783    container.className = 'chatbot-widget-container';
2784
2785    const position = settings?.position || 'right';
2786    const name = settings?.name || 'Support';
2787    const logoUrl = settings?.logo_url;
2788    const visitorName = visitorInfo?.name || 'there';
2789    const firstName = visitorName.split(' ')[0];
2790    
2791    // Custom links with fallback defaults
2792    const customLinks = settings?.custom_links || [
2793      { label: 'Homepage', url: '/', icon: 'home' },
2794      { label: 'About Us', url: '/about', icon: 'info' },
2795      { label: 'Contact', url: '/contact', icon: 'mail' }
2796    ];
2797    
2798    // Icon SVGs for links
2799    const linkIcons = {
2800      home: '<path d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"/>',
2801      info: '<path d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>',
2802      mail: '<path d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/>',
2803      phone: '<path d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 
280311.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z"/>',
2804      help: '<path d="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>',
2805      book: '<path d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"/>',
2806      link: '<path d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1"/>'
2807    };
2808    
2809    // Generate links HTML
2810    const linksHtml = customLinks.map(link => {
2811      const iconPath = linkIcons[link.icon] || linkIcons.link;
2812      const href = link.url.startsWith('http') ? link.url : window.location.origin + link.url;
2813      return `
2814        <a href="${href}" target="_blank" class="chatbot-link-item">
2815          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
2816            ${iconPath}
2817          </svg>
2818          <span>${link.label}</span>
2819        </a>
2820      `;
2821    }).join('');
2822
2823    // Create bubble with chat and close icons
2824    const bubble = document.createElement('div');
2825    bubble.className = 'chatbot-bubble';
2826    bubble.style[position] = '20px';
2827    bubble.innerHTML = `
2828      <svg class="chat-icon" fill="#fff" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
2829        <path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H6l-2 2V4h16v12z"/>
2830      </svg>
2831      <svg class="close-icon" fill="#fff" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
2832        <path d="M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z"/>
2833      </svg>
2834    `;
2835
2836    // Create window
2837    const chatWindow = document.createElement('div');
2838    chatWindow.className = 'chatbot-window';
2839    chatWindow.style[position] = '20px';
2840
2841    // Logo HTML
2842    const logoHtml = logoUrl 
2843      ? `<img src="${logoUrl}" alt="${name}" />`
2844      : `<svg viewBox="0 0 24 24" fill="#1a1a1a"><path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2z"/></svg>`;
2845
2846    // Build the home view
2847    chatWindow.innerHTML = `
2848      <!-- Home View -->
2849      <div class="chatbot-home-view">
2850        <div class="chatbot-home-header">
2851          <div class="chatbot-home-header-top">
2852            <div class="chatbot-home-logo">${logoHtml}</div>
2853            <button class="chatbot-home-close">
2854              <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
2855                <path d="M6 18L18 6M6 6l12 12"/>
2856              </svg>
2857            </button>
2858          </div>
2859          <h2 class="chatbot-home-greeting">Hi ${firstName} <span class="wave">👋</span><br/>How can we help?</h2>
2860        </div>
2861        
2862        <div class="chatbot-home-content">
2863          <div class="chatbot-action-card" id="chatbot-send-message">
2864            <div class="chatbot-action-card-content">
2865              <h4 class="chatbot-action-card-title">Send us a message</h4>
2866              <p class="chatbot-action-card-subtitle">We typically reply in a few minutes</p>
2867            </div>
2868            <div class="chatbot-action-card-icon">
2869              <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
2870                <path d="M5 12h14M12 5l7 7-7 7"/>
2871              </svg>
2872            </div>
2873          </div>
2874          
2875          
2876          <!-- Quick Questions Section -->
2877          <div class="chatbot-section-label">Popular questions</div>
2878          <div class="chatbot-quick-questions">
2879            <button class="chatbot-quick-question" data-question="What services do you offer?">
2880              <span class="chatbot-qq-icon">💼</span>
2881              <span>What services do you offer?</span>
2882            </button>
2883            <button class="chatbot-quick-question" data-question="How can I get started?">
2884              <span class="chatbot-qq-icon">🚀</span>
2885              <span>How can I get started?</span>
2886            </button>
2887            <button class="chatbot-quick-question" data-question="What are your pricing plans?">
2888              <span class="chatbot-qq-icon">💰</span>
2889              <span>What are your pricing plans?</span>
2890            </button>
2891            <button class="chatbot-quick-question" data-question="How do I contact support?">
2892              <span class="chatbot-qq-icon">📞</span>
2893              <span>How do I contact support?</span>
2894            </button>
2895          </div>
2896          
2897          <!-- Useful Links Section -->
2898          <div class="chatbot-section-label">Useful links</div>
2899          <div class="chatbot-links-section">
2900            ${linksHtml}
2901          </div>
2902          
2903          <div class="chatbot-status-card">
2904            <div class="chatbot-status-icon">
2905              <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
2906                <path d="M20 6L9 17l-5-5"/>
2907              </svg>
2908            </div>
2909            <div class="chatbot-status-text">
2910              <h5 class="chatbot-status-title">Status: All Systems Operational</h5>
2911              <p class="chatbot-status-subtitle">Updated ${new Date().toLocaleDateString('en-US', { month: 'short', day: 'numeric' })}</p>
2912            </div>
2913          </div>
2914          
2915          <div class="chatbot-powered-by">
2916            Created by <a href="https://aimmediahouse.com" target="_blank" rel="noopener">AIM Media House</a>
2917          </div>
2918        </div>
2919        
2920        <div class="chatbot-bottom-nav">
2921          <button class="chatbot-nav-item active" data-view="home">
2922            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
2923              <path d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"/>
2924            </svg>
2925            Home
2926          </button>
2927          <button class="chatbot-nav-item" data-view="messages">
2928            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
2929              <path d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"/>
2930            </svg>
2931            Messages
2932            ${messages.length > 0 ? '<span class="chatbot-nav-badge">' + messages.length + '</span>' : ''}
2933          </button>
2934        </div>
2935      </div>
2936      
2937      <!-- Chat/Messages View -->
2938      <div class="chatbot-chat-view">
2939        <div class="chatbot-chat-header">
2940          <button class="chatbot-chat-back">
2941            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
2942              <path d="M15 19l-7-7 7-7"/>
2943            </svg>
2944          </button>
2945          <h3 class="chatbot-chat-header-title">${name}</h3>
2946          <button class="chatbot-end-chat-btn" title="End Chat">
2947            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
2948              <path d="M9 21H5a2 2 0 01-2-2V5a2 2 0 012-2h4M16 17l5-5-5-5M21 12H9"/>
2949            </svg>
2950          </button>
2951        </div>
2952      </div>
2953    `;
2954
2955    // Get the chat view and set up chat interface
2956    const chatView = chatWindow.querySelector('.chatbot-chat-view');
2957    
2958    // Check if we need pre-chat form or direct chat
2959    if (hasCompletedForm && visitorInfo) {
2960      showChatInterface(chatView).catch(err => console.error('[ChatBot] Error loading chat:', err));
2961    } else {
2962      // Show pre-chat form in chat view
2963      showPrechatForm(chatView);
2964    }
2965
2966    container.appendChild(bubble);
2967    container.appendChild(chatWindow);
2968    document.body.appendChild(container);
2969    
2970    console.log('[ChatBot] Widget appended to body');
2971
2972    // Event listeners
2973    bubble.addEventListener('click', toggleWindow);
2974    chatWindow.querySelector('.chatbot-home-close').addEventListener('click', toggleWindow);
2975    
2976    // Navigation
2977    chatWindow.querySelector('#chatbot-send-message').addEventListener('click', switchToMessages);
2978    chatWindow.querySelector('.chatbot-nav-item[data-view="home"]').addEventListener('click', switchToHome);
2979    chatWindow.querySelector('.chatbot-nav-item[data-view="messages"]').addEventListener('click', switchToMessages);
2980    chatWindow.querySelector('.chatbot-chat-back').addEventListener('click', switchToHome);
2981    chatWindow.querySelector('.chatbot-end-chat-btn').addEventListener('click', endChat);
2982    
2983    
2984    // Quick Questions - click to send as message
2985    chatWindow.querySelectorAll('.chatbot-quick-question').forEach(btn => {
2986      btn.addEventListener('click', async () => {
2987        const question = btn.getAttribute('data-question');
2988        if (question) {
2989          // Switch to messages view
2990          switchToMessages();
2991          
2992          // Wait a bit for the chat interface to be ready
2993          setTimeout(async () => {
2994            const input = document.querySelector('.chatbot-input');
2995            if (input) {
2996              input.value = question;
2997              handleSend(input);
2998            }
2999          }, 100);
3000        }
3001      });
3002    });
3003
3004    // Trigger auto-discovery after widget loads
3005    setTimeout(() => {
3006      discoverPageData();
3007    }, 2000);
3008
3009    // Start teaser timer
3010    startTeaserTimer();
3011  }
3012
3013  function processCommand(action, id) {
3014    if (action === 'init') {
3015      console.log('[ChatBot] Init called with ID:', id);
3016      
3017      function init() {
3018        console.log('[ChatBot] Initializing...');
3019        widgetId = id;
3020        visitorId = getVisitorId();
3021        conversationId = loadConversationId();
3022        console.log('[ChatBot] Loaded conversation ID:', conversationId);
3023
3024        createStyles();
3025        
3026
3027        // Fetch location first (async)
3028        fetchVisitorLocation().then(() => {
3029          fetchSettings(id).then(data => {
3030            console.log('[ChatBot] Settings loaded:', data);
3031            if (!data) {
3032              console.error('[ChatBot] Failed to load widget settings - check widget ID');
3033              return;
3034            }
3035            settings = data;
3036            createWidget();
3037            
3038            // Start visitor presence heartbeat
3039            startHeartbeat();
3040          }).catch(error => {
3041            console.error('[ChatBot] Error fetching settings:', error);
3042          });
3043        });
3044      }
3045      
3046      // Wait for DOM to be ready
3047      if (document.readyState === 'loading') {
3048        console.log('[ChatBot] Waiting for DOM...');
3049        document.addEventListener('DOMContentLoaded', init);
3050      } else {
3051        init();
3052      }
3053    }
3054  }
3055
3056  // Process any queued commands from the embed snippet
3057  const queue = window.chatbot && window.chatbot.q;
3058  if (queue && Array.isArray(queue)) {
3059    console.log('[ChatBot] Processing queued commands:', queue.length);
3060    queue.forEach(function(args) {
3061      processCommand.apply(null, args);
3062    });
3063  }
3064
3065  // Set up chatbot function for future calls
3066  window.chatbot = function(action, id) {
3067    processCommand(action, id);
3068  };
3069  
3070  console.log('[ChatBot] Script loaded');
3071})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.