{
  "version": 3,
  "sources": ["/cdn/shop/t/54/assets/mo-ai-chat.js"],
  "sourcesContent": ["/**\n * Shop AI Chat - Client-side implementation\n *\n * This module handles the chat interface for the Shopify AI Chat application.\n * It manages the UI interactions, API communication, and message rendering.\n */\n(function() {\n  'use strict';\n\n  /**\n   * Application namespace to prevent global scope pollution\n   */\n  const ShopAIChat = {\n    /**\n     * UI-related elements and functionality\n     */\n    UI: {\n      elements: {},\n      isMobile: false,\n\n      /**\n       * Initialize UI elements and event listeners\n       * @param {HTMLElement} container - The main container element\n       */\n      init: function(container) {\n        if (!container) return;\n\n        // Cache DOM elements\n        this.elements = {\n          container: container,\n          chatBubble: container.querySelector('.shop-ai-chat-bubble'),\n          chatWindow: container.querySelector('.shop-ai-chat-window'),\n          closeButton: container.querySelector('.shop-ai-chat-close'),\n          chatInput: container.querySelector('.shop-ai-chat-input input'),\n          sendButton: container.querySelector('.shop-ai-chat-send'),\n          messagesContainer: container.querySelector('.shop-ai-chat-messages')\n        };\n\n        // Detect mobile device\n        this.isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);\n\n        // ACCESSIBILITY (additive): mark the message stream as a polite live region so\n        // screen readers announce streamed assistant replies, quiz cards, and product\n        // results as they arrive. role=\"log\" gives it the right semantics; aria-busy is\n        // toggled by setBusy() during streaming. No markup change required in the snippet.\n        if (this.elements.messagesContainer) {\n          const mc = this.elements.messagesContainer;\n          if (!mc.getAttribute('role')) mc.setAttribute('role', 'log');\n          if (!mc.getAttribute('aria-live')) mc.setAttribute('aria-live', 'polite');\n          mc.setAttribute('aria-relevant', 'additions text');\n          mc.setAttribute('aria-atomic', 'false');\n          mc.setAttribute('aria-busy', 'false');\n        }\n\n        if (this.elements.chatWindow) {\n          const chatWindow = this.elements.chatWindow;\n          chatWindow.setAttribute('role', 'dialog');\n          chatWindow.setAttribute('aria-label', 'Mattress Overstock AI mattress guide');\n          chatWindow.setAttribute('aria-hidden', 'true');\n        }\n        if (this.elements.chatBubble) {\n          this.elements.chatBubble.setAttribute('aria-expanded', 'false');\n        }\n\n        // Set up event listeners\n        this.setupEventListeners();\n\n        // Fix for iOS Safari viewport height issues\n        if (this.isMobile) {\n          this.setupMobileViewport();\n        }\n      },\n\n      /**\n       * Set up all event listeners for UI interactions\n       */\n      setupEventListeners: function() {\n        const { chatBubble, closeButton, chatInput, sendButton, messagesContainer } = this.elements;\n\n        // Toggle chat window visibility\n        chatBubble.addEventListener('click', () => this.toggleChatWindow());\n        // Keyboard access for the launcher (the liquid gives it role=button tabindex=0)\n        chatBubble.addEventListener('keydown', (e) => {\n          if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); this.toggleChatWindow(); }\n        });\n\n        // Close chat window\n        closeButton.addEventListener('click', () => this.closeChatWindow(true));\n\n        document.addEventListener('keydown', (e) => {\n          if (e.key === 'Escape' && this.elements.chatWindow.classList.contains('active')) {\n            this.closeChatWindow(true);\n          }\n        });\n\n        // Send message when pressing Enter in input\n        chatInput.addEventListener('keypress', (e) => {\n          // Don't let Enter bypass the disabled send button mid-stream (double-send guard).\n          if (e.key === 'Enter' && sendButton && sendButton.disabled) { e.preventDefault(); return; }\n          if (e.key === 'Enter' && chatInput.value.trim() !== '') {\n            ShopAIChat.Message.send(chatInput, messagesContainer);\n\n            // On mobile, handle keyboard\n            if (this.isMobile) {\n              chatInput.blur();\n              setTimeout(() => chatInput.focus(), 300);\n            }\n          }\n        });\n\n        // Send message when clicking send button\n        sendButton.addEventListener('click', () => {\n          if (chatInput.value.trim() !== '') {\n            ShopAIChat.Message.send(chatInput, messagesContainer);\n\n            // On mobile, focus input after sending\n            if (this.isMobile) {\n              setTimeout(() => chatInput.focus(), 300);\n            }\n          }\n        });\n\n        // Handle window resize to adjust scrolling\n        window.addEventListener('resize', () => this.scrollToBottom());\n\n        // Reader-intent tracking for the scroll guard: _pinned means \"at/near the bottom\n        // the last time anyone (reader or us) actually scrolled\". Content appends don't\n        // fire scroll events, so this survives big appends (product grids, finale prose) \u2014\n        // measuring the gap after an append cannot tell \"reader scrolled up\" from \"tall\n        // content just landed\".\n        this._pinned = true;\n        messagesContainer.addEventListener('scroll', () => {\n          const gap = messagesContainer.scrollHeight - messagesContainer.scrollTop - messagesContainer.clientHeight;\n          this._pinned = gap <= 140;\n        });\n\n        // Add global click handler for auth links\n        document.addEventListener('click', function(event) {\n          if (event.target && event.target.classList.contains('shop-auth-trigger')) {\n            event.preventDefault();\n            if (window.shopAuthUrl) {\n              ShopAIChat.Auth.openAuthPopup(window.shopAuthUrl);\n            }\n          }\n        });\n      },\n\n      /**\n       * Setup mobile-specific viewport adjustments\n       */\n      setupMobileViewport: function() {\n        const setViewportHeight = () => {\n          document.documentElement.style.setProperty('--viewport-height', `${window.innerHeight}px`);\n        };\n        window.addEventListener('resize', setViewportHeight);\n        setViewportHeight();\n      },\n\n      /**\n       * Toggle chat window visibility\n       */\n      toggleChatWindow: function() {\n        const { chatWindow, chatInput, chatBubble } = this.elements;\n\n        chatWindow.classList.toggle('active');\n\n        if (chatWindow.classList.contains('active')) {\n          chatWindow.setAttribute('aria-hidden', 'false');\n          if (chatBubble) chatBubble.setAttribute('aria-expanded', 'true');\n          // A fresh chat should feel like the guide spoke first, not like a landing card\n          // happened to be sitting behind the launcher.\n          ShopAIChat.Quiz.showOpenGreeting(this.elements.messagesContainer);\n          // Opening the chat pauses the nudge for this page-load only;\n          // only the explicit \u2715 dismiss silences it for the whole session.\n          if (ShopAIChat.Nudge) ShopAIChat.Nudge.stop();\n          // Hide the launcher while the window is open (it overlapped the input on mobile).\n          if (chatBubble) chatBubble.classList.add('shop-ai-bubble-hidden');\n          ShopAIChat.Analytics.push('chat_opened');\n          // Identity-on-open: anonymous server-side touch + one silent signed-in\n          // check per session. Fire-and-forget; never blocks or alters the open.\n          if (ShopAIChat.Identity) ShopAIChat.Identity.onOpen();\n          // On mobile, prevent body scrolling and delay focus\n          if (this.isMobile) {\n            document.body.classList.add('shop-ai-chat-open');\n            setTimeout(() => chatInput.focus(), 500);\n          } else {\n            chatInput.focus();\n          }\n          // Always scroll messages to bottom when opening\n          this.scrollToBottom(true);\n        } else {\n          chatWindow.setAttribute('aria-hidden', 'true');\n          if (chatBubble) chatBubble.setAttribute('aria-expanded', 'false');\n          if (chatBubble) chatBubble.classList.remove('shop-ai-bubble-hidden');\n          // Remove body class when closing\n          document.body.classList.remove('shop-ai-chat-open');\n        }\n      },\n\n      /**\n       * Close chat window\n       */\n      closeChatWindow: function(returnFocus) {\n        const { chatWindow, chatInput, chatBubble } = this.elements;\n\n        chatWindow.classList.remove('active');\n        chatWindow.setAttribute('aria-hidden', 'true');\n        if (chatBubble) {\n          chatBubble.setAttribute('aria-expanded', 'false');\n          chatBubble.classList.remove('shop-ai-bubble-hidden');\n        }\n\n        // On mobile, blur input to hide keyboard and enable body scrolling\n        if (this.isMobile) {\n          chatInput.blur();\n          document.body.classList.remove('shop-ai-chat-open');\n        }\n        if (returnFocus && chatBubble) chatBubble.focus();\n      },\n\n      /**\n       * Scroll messages container to bottom.\n       * @param {boolean} force - true for user-initiated moments (open, send, form/quiz shown):\n       * always scroll. Streaming/layout events stay guarded: if the reader deliberately\n       * scrolled up (see _pinned), don't yank them back down.\n       */\n      scrollToBottom: function(force) {\n        const { messagesContainer } = this.elements;\n        setTimeout(() => {\n          if (!force && this._pinned === false) return;\n          messagesContainer.scrollTop = messagesContainer.scrollHeight;\n        }, 100);\n      },\n\n      /**\n       * Keep a conversion form fully in view. Product cards can land after the form,\n       * so bottom-scrolling alone can hide the fields above the visible pane.\n       * @param {HTMLElement} element\n       * @param {boolean} force\n       */\n      scrollOfferIntoView: function(element, force) {\n        const { messagesContainer } = this.elements;\n        if (!messagesContainer || !element) return;\n        setTimeout(() => {\n          if (!force && this._pinned === false) return;\n          const margin = 12;\n          const usableHeight = messagesContainer.clientHeight - (margin * 2);\n          const targetTop = element.offsetHeight > usableHeight\n            ? Math.max(0, element.offsetTop - margin)\n            : Math.max(0, element.offsetTop + element.offsetHeight + margin - messagesContainer.clientHeight);\n          messagesContainer.scrollTop = targetTop;\n          this._pinned = true;\n        }, 120);\n      },\n\n      /**\n       * Toggle the \"streaming in progress\" state on the composer.\n       * ADDITIVE: disables + spins the send button while a reply streams so the\n       * shopper can't double-send (which would interleave two SSE streams into\n       * the same message container). Also flags the live region as busy for AT.\n       * Idempotent and defensive \u2014 never throws, never blocks the stream.\n       * @param {boolean} busy\n       */\n      setBusy: function(busy) {\n        try {\n          const send = this.elements.sendButton;\n          const input = this.elements.chatInput;\n          const messages = this.elements.messagesContainer;\n          if (send) {\n            send.classList.toggle('is-busy', !!busy);\n            send.disabled = !!busy;\n            send.setAttribute('aria-busy', busy ? 'true' : 'false');\n          }\n          // Keep the input usable for typing the next question, but reflect state.\n          if (input) input.classList.toggle('shop-ai-input-busy', !!busy);\n          if (messages) messages.setAttribute('aria-busy', busy ? 'true' : 'false');\n        } catch (e) { /* non-fatal */ }\n      },\n\n      /**\n       * Show typing indicator in the chat\n       */\n      showTypingIndicator: function() {\n        const { messagesContainer } = this.elements;\n\n        const typingIndicator = document.createElement('div');\n        typingIndicator.classList.add('shop-ai-typing-indicator');\n        typingIndicator.innerHTML = '<span></span><span></span><span></span>';\n        messagesContainer.appendChild(typingIndicator);\n        this.scrollToBottom();\n      },\n\n      /**\n       * Remove typing indicator from the chat\n       */\n      removeTypingIndicator: function() {\n        const { messagesContainer } = this.elements;\n\n        const typingIndicator = messagesContainer.querySelector('.shop-ai-typing-indicator');\n        if (typingIndicator) {\n          typingIndicator.remove();\n        }\n      },\n\n      /**\n       * Display product results in the chat\n       * @param {Array} products - Array of product data objects\n       */\n      displayProductResults: function(products) {\n        const { messagesContainer } = this.elements;\n        const activeLeadForm = messagesContainer.querySelector('form.shop-ai-lead-form:not(.shop-ai-support-form):not(.shop-ai-lead-done)');\n\n        // Create a wrapper for the product section\n        const productSection = document.createElement('div');\n        productSection.classList.add('shop-ai-product-section');\n        messagesContainer.appendChild(productSection);\n\n        // Add a header for the product results\n        const header = document.createElement('div');\n        header.classList.add('shop-ai-product-header');\n        header.innerHTML = '<h4>Top matches for you</h4>';\n        productSection.appendChild(header);\n\n        // Create the product grid container\n        const productsContainer = document.createElement('div');\n        productsContainer.classList.add('shop-ai-product-grid');\n        productSection.appendChild(productsContainer);\n\n        if (!products || !Array.isArray(products) || products.length === 0) {\n          const noProductsMessage = document.createElement('p');\n          noProductsMessage.textContent = \"No products found\";\n          noProductsMessage.style.padding = \"10px\";\n          productsContainer.appendChild(noProductsMessage);\n        } else {\n          // Pull the just-finished assistant reply so each card can show the bot's own\n          // \"why it fits you\" line. product_results fires around end_turn, so the latest\n          // NON-EMPTY assistant bubble holds the complete reply with the per-bed lines\n          // (scan from the end, skipping any trailing empty/placeholder bubble).\n          const assistantEls = messagesContainer.querySelectorAll('.shop-ai-message.assistant');\n          let lastReply = '';\n          for (let k = assistantEls.length - 1; k >= 0; k--) {\n            const txt = assistantEls[k].dataset.rawText || assistantEls[k].textContent || '';\n            if (txt.trim()) { lastReply = txt; break; }\n          }\n          const reasons = ShopAIChat.Product.extractReasons(lastReply);\n          products.forEach(product => {\n            const reason = ShopAIChat.Product.reasonFor(product.title, reasons);\n            const productCard = ShopAIChat.Product.createCard(product, reason);\n            productsContainer.appendChild(productCard);\n          });\n          // Deterministic capture safety net: with a real shortlist on screen and no lead\n          // form anywhere in the thread (active OR submitted), offer to text the picks \u2014\n          // shopper-tap-initiated, so it works even when the model never offers.\n          if (products.length >= 1 && !sessionStorage.getItem('shopAiLeadDone') &&\n              !sessionStorage.getItem('shopAiSupportActive') &&\n              !messagesContainer.querySelector('form.shop-ai-lead-form:not(.shop-ai-support-form)') &&\n              !messagesContainer.querySelector('.shop-ai-support-form')) {\n            const financingContext = /financ|monthly[- ]payment|synchrony/i.test(lastReply);\n            const fallbackHook = financingContext ? 'financing' : 'save_cart';\n            const pickLabel = products.length === 1 ? 'Save this pick' : 'Save these picks';\n            const chip = document.createElement('button');\n            chip.type = 'button';\n            chip.className = 'shop-ai-text-picks-chip';\n            chip.textContent = financingContext ? 'See payment options' : pickLabel;\n            chip.addEventListener('click', () => {\n              ShopAIChat.Analytics.push('chip_text_picks_clicked');\n              chip.remove();\n              ShopAIChat.Lead.show(fallbackHook, messagesContainer);\n            });\n            productSection.appendChild(chip);\n          }\n\n          // Carousel scroll affordance (ADDITIVE, visual-only): toggle .is-scrollable\n          // on the section once the row actually overflows, and .at-end when scrolled\n          // to the end \u2014 the CSS shows/retracts the right-edge fade + \"\u2192\" cue off these.\n          // A single card (or a non-overflowing row) never gets .is-scrollable, so it\n          // shows no peek/fade. No-op on the mobile vertical stack (no x-overflow).\n          this.wireCarouselCue(productSection, productsContainer);\n        }\n\n        if (activeLeadForm) {\n          messagesContainer.appendChild(activeLeadForm);\n          ShopAIChat.Lead.applyCopy(activeLeadForm, activeLeadForm.dataset.hook || 'save_cart', {\n            productCount: Array.isArray(products) ? products.length : 0\n          });\n          activeLeadForm.classList.add('shop-ai-lead-form-reanchored');\n          ShopAIChat.UI.scrollOfferIntoView(activeLeadForm, true);\n          return;\n        }\n\n        this.scrollToBottom();\n      },\n\n      /**\n       * Toggle the scroll-affordance classes on a product section based on whether its\n       * grid overflows horizontally and where it's scrolled. Purely additive/visual \u2014\n       * never alters markup or card behavior.\n       * @param {HTMLElement} section - .shop-ai-product-section\n       * @param {HTMLElement} grid - .shop-ai-product-grid (the scroll container)\n       */\n      wireCarouselCue: function(section, grid) {\n        if (!section || !grid) return;\n\n        const SLOP = 4; // px tolerance for sub-pixel rounding\n        const update = function() {\n          const overflow = grid.scrollWidth - grid.clientWidth;\n          if (overflow > SLOP) {\n            section.classList.add('is-scrollable');\n            const atEnd = grid.scrollLeft >= (overflow - SLOP);\n            section.classList.toggle('at-end', atEnd);\n          } else {\n            // Not scrollable (single card / fits) \u2014 no cue at all.\n            section.classList.remove('is-scrollable');\n            section.classList.remove('at-end');\n          }\n        };\n\n        grid.addEventListener('scroll', update, { passive: true });\n\n        // Re-measure on viewport changes (window resize, mobile rotate). Stored on the\n        // grid so it can be cleaned conceptually; harmless if the node is removed.\n        if (typeof ResizeObserver === 'function') {\n          const ro = new ResizeObserver(update);\n          ro.observe(grid);\n        } else {\n          window.addEventListener('resize', update, { passive: true });\n        }\n\n        // Initial measure: now, and again after images load / layout settles.\n        update();\n        requestAnimationFrame(update);\n        setTimeout(update, 120);\n      }\n    },\n\n    /**\n     * Message handling and display functionality\n     */\n    Message: {\n      /**\n       * Send a message to the API\n       * @param {HTMLInputElement} chatInput - The input element\n       * @param {HTMLElement} messagesContainer - The messages container\n       */\n      send: async function(chatInput, messagesContainer) {\n        ShopAIChat.Quiz.removeWelcomeChip();\n        const userMessage = chatInput.value.trim();\n        const conversationId = sessionStorage.getItem('shopAiConversationId');\n\n        // Add user message to chat\n        this.add(userMessage, 'user', messagesContainer);\n\n        // Clear input\n        chatInput.value = '';\n\n        // Show typing indicator + lock the composer for the duration of the stream\n        ShopAIChat.UI.showTypingIndicator();\n        ShopAIChat.UI.setBusy(true);\n\n        try {\n          ShopAIChat.API.streamResponse(userMessage, conversationId, messagesContainer);\n        } catch (error) {\n          console.error('Error communicating with Claude API:', error);\n          ShopAIChat.UI.removeTypingIndicator();\n          ShopAIChat.UI.setBusy(false);\n          this.add(\"Sorry, I couldn't process your request at the moment. Please try again later.\", 'assistant', messagesContainer);\n        }\n      },\n\n      /**\n       * Add a message to the chat\n       * @param {string} text - Message content\n       * @param {string} sender - Message sender ('user' or 'assistant')\n       * @param {HTMLElement} messagesContainer - The messages container\n       * @returns {HTMLElement} The created message element\n       */\n      add: function(text, sender, messagesContainer) {\n        const messageElement = document.createElement('div');\n        messageElement.classList.add('shop-ai-message', sender);\n\n        if (sender === 'assistant') {\n          messageElement.dataset.rawText = text;\n          ShopAIChat.Formatting.formatMessageContent(messageElement);\n        } else {\n          messageElement.textContent = text;\n        }\n\n        messagesContainer.appendChild(messageElement);\n        ShopAIChat.UI.scrollToBottom(true);\n\n        return messageElement;\n      },\n\n      /**\n       * Add a tool use message to the chat with expandable arguments\n       * @param {string} toolMessage - Tool use message content\n       * @param {HTMLElement} messagesContainer - The messages container\n       */\n      addToolUse: function(toolMessage, messagesContainer) {\n        // Parse the tool message to extract tool name and arguments\n        const match = toolMessage.match(/Calling tool: (\\w+) with arguments: (.+)/);\n        if (!match) {\n          // Fallback for unexpected format\n          const toolUseElement = document.createElement('div');\n          toolUseElement.classList.add('shop-ai-message', 'tool-use');\n          toolUseElement.textContent = toolMessage;\n          messagesContainer.appendChild(toolUseElement);\n          ShopAIChat.UI.scrollToBottom();\n          return;\n        }\n\n        const toolName = match[1];\n        const argsString = match[2];\n\n        // Create the main tool use element\n        const toolUseElement = document.createElement('div');\n        toolUseElement.classList.add('shop-ai-message', 'tool-use');\n\n        // Create the header (always visible)\n        const headerElement = document.createElement('div');\n        headerElement.classList.add('shop-ai-tool-header');\n\n        const toolText = document.createElement('span');\n        toolText.classList.add('shop-ai-tool-text');\n        toolText.textContent = `Calling tool: ${toolName}`;\n\n        const toggleElement = document.createElement('span');\n        toggleElement.classList.add('shop-ai-tool-toggle');\n        toggleElement.textContent = '[+]';\n\n        headerElement.appendChild(toolText);\n        headerElement.appendChild(toggleElement);\n\n        // Create the arguments section (initially hidden)\n        const argsElement = document.createElement('div');\n        argsElement.classList.add('shop-ai-tool-args');\n\n        try {\n          // Try to format JSON arguments nicely\n          const parsedArgs = JSON.parse(argsString);\n          argsElement.textContent = JSON.stringify(parsedArgs, null, 2);\n        } catch (e) {\n          // If not valid JSON, just show as-is\n          argsElement.textContent = argsString;\n        }\n\n        // Add click handler to toggle arguments visibility\n        headerElement.addEventListener('click', function() {\n          const isExpanded = argsElement.classList.contains('expanded');\n          if (isExpanded) {\n            argsElement.classList.remove('expanded');\n            toggleElement.textContent = '[+]';\n          } else {\n            argsElement.classList.add('expanded');\n            toggleElement.textContent = '[-]';\n          }\n        });\n\n        // Assemble the complete element\n        toolUseElement.appendChild(headerElement);\n        toolUseElement.appendChild(argsElement);\n\n        messagesContainer.appendChild(toolUseElement);\n        ShopAIChat.UI.scrollToBottom();\n      }\n    },\n\n    /**\n     * Text formatting and markdown handling\n     */\n    Formatting: {\n      /**\n       * Format message content with markdown and links\n       * @param {HTMLElement} element - The element to format\n       */\n      /** Escape HTML so model/catalog text can never inject markup into innerHTML. */\n      escapeHtml: function(s) {\n        return String(s == null ? '' : s)\n          .replace(/&/g, '&amp;')\n          .replace(/</g, '&lt;')\n          .replace(/>/g, '&gt;')\n          .replace(/\"/g, '&quot;')\n          .replace(/'/g, '&#39;');\n      },\n\n      /** Only allow safe link schemes; anything else (javascript:, data:, etc.) -> '#'. */\n      safeUrl: function(url) {\n        const u = String(url == null ? '' : url).trim();\n        return /^(https?:\\/\\/|\\/|mailto:|#)/i.test(u) ? u : '#';\n      },\n\n      formatMessageContent: function(element) {\n        if (!element || !element.dataset.rawText) return;\n\n        // SECURITY: escape the raw model text FIRST so any HTML it (or injected catalog\n        // data) contains is neutralized before we add our own safe markup below.\n        let processedText = this.escapeHtml(element.dataset.rawText);\n\n        // Process Markdown links. Link text is already escaped; sanitize the URL scheme.\n        const markdownLinkRegex = /\\[([^\\]]+)\\]\\(([^)]+)\\)/g;\n        processedText = processedText.replace(markdownLinkRegex, (match, text, url) => {\n          const safe = this.safeUrl(url);\n          if (url.includes('shopify.com/authentication') &&\n             (url.includes('oauth/authorize') || url.includes('authentication'))) {\n            window.shopAuthUrl = safe;\n            return '<a href=\"#auth\" class=\"shop-auth-trigger\">' + text + '</a>';\n          } else if (url.includes('/cart') || url.includes('checkout')) {\n            return '<a href=\"' + safe + '\" target=\"_blank\" rel=\"noopener noreferrer\">click here to proceed to checkout</a>';\n          } else {\n            return '<a href=\"' + safe + '\" target=\"_blank\" rel=\"noopener noreferrer\">' + text + '</a>';\n          }\n        });\n\n        // Convert the (already-escaped) text to HTML with bold/list handling.\n        processedText = this.convertMarkdownToHtml(processedText);\n\n        element.innerHTML = processedText;\n      },\n\n      /**\n       * Convert Markdown text to HTML with list support\n       * @param {string} text - Markdown text to convert\n       * @returns {string} HTML content\n       */\n      convertMarkdownToHtml: function(text) {\n        text = text.replace(/(\\*\\*|__)(.*?)\\1/g, '<strong>$2</strong>');\n        const lines = text.split('\\n');\n        let currentList = null;\n        let listItems = [];\n        let htmlContent = '';\n        let startNumber = 1;\n\n        for (let i = 0; i < lines.length; i++) {\n          const line = lines[i];\n          const unorderedMatch = line.match(/^\\s*([-*])\\s+(.*)/);\n          const orderedMatch = line.match(/^\\s*(\\d+)[\\.)]\\s+(.*)/);\n\n          if (unorderedMatch) {\n            if (currentList !== 'ul') {\n              if (currentList === 'ol') {\n                htmlContent += `<ol start=\"${startNumber}\">` + listItems.join('') + '</ol>';\n                listItems = [];\n              }\n              currentList = 'ul';\n            }\n            listItems.push('<li>' + unorderedMatch[2] + '</li>');\n          } else if (orderedMatch) {\n            if (currentList !== 'ol') {\n              if (currentList === 'ul') {\n                htmlContent += '<ul>' + listItems.join('') + '</ul>';\n                listItems = [];\n              }\n              currentList = 'ol';\n              startNumber = parseInt(orderedMatch[1], 10);\n            }\n            listItems.push('<li>' + orderedMatch[2] + '</li>');\n          } else {\n            if (currentList) {\n              htmlContent += currentList === 'ul'\n                ? '<ul>' + listItems.join('') + '</ul>'\n                : `<ol start=\"${startNumber}\">` + listItems.join('') + '</ol>';\n              listItems = [];\n              currentList = null;\n            }\n\n            if (line.trim() === '') {\n              htmlContent += '<br>';\n            } else {\n              htmlContent += '<p>' + line + '</p>';\n            }\n          }\n        }\n\n        if (currentList) {\n          htmlContent += currentList === 'ul'\n            ? '<ul>' + listItems.join('') + '</ul>'\n            : `<ol start=\"${startNumber}\">` + listItems.join('') + '</ol>';\n        }\n\n        htmlContent = htmlContent.replace(/<\\/p><p>/g, '</p>\\n<p>');\n        return htmlContent;\n      }\n    },\n\n    PageContext: {\n      handle: function() {\n        const m = window.location.pathname.match(/\\/products\\/([a-z0-9-]+)/);\n        return m ? m[1] : null;\n      },\n      shortTitle: function() {\n        // Product headings can hydrate after the widget, while social metadata is ready in the initial HTML.\n        const heading = Array.from(document.querySelectorAll(\n          '.product__title h1, h1.product__title, [data-product-title], main h1'\n        )).map((el) => String(el.textContent || '').replace(/\\s+/g, ' ').trim()).find(Boolean);\n        const ogTitle = document.querySelector('meta[property=\"og:title\"]');\n        const twitterTitle = document.querySelector('meta[name=\"twitter:title\"]');\n        const handleTitle = (this.handle() || '').split('-').filter(Boolean)\n          .map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(' ');\n        const raw = heading ||\n          (ogTitle && ogTitle.getAttribute('content')) ||\n          (twitterTitle && twitterTitle.getAttribute('content')) ||\n          document.title ||\n          handleTitle;\n        let title = String(raw || '')\n          .replace(/\\s+/g, ' ')\n          .trim()\n          .split(/\\s*[|\u2013\u2014]\\s*Mattress Overstock.*$/i)[0]\n          .trim();\n        if (title.includes(':')) title = title.split(':')[0].trim();\n        if (title.length <= 54) return title.length > 1 ? title : null;\n        const clipped = title.slice(0, 55).replace(/\\s+\\S*$/, '').trim();\n        return clipped.length > 1 ? clipped : null;\n      },\n      safeUrl: function() {\n        try {\n          const url = new URL(window.location.href);\n          // Page path is enough for catalog/context. Query and fragment may carry\n          // a customer identifier and are never needed by the Chat model.\n          url.search = '';\n          url.hash = '';\n          return url.toString().slice(0, 512);\n        } catch (e) {\n          return window.location.origin + window.location.pathname;\n        }\n      },\n      get: function() {\n        return {\n          url: this.safeUrl(),\n          title: (document.title || '').slice(0, 256),\n          handle: this.handle()\n        };\n      }\n    },\n\n    Cookies: {\n      read: function(name) {\n        const m = document.cookie.match(new RegExp('(?:^|;\\\\s*)' + name + '=([^;]*)'));\n        return m ? decodeURIComponent(m[1]) : null;\n      },\n      // First-party analytics ids; stitch server-side events to this browser session.\n      collect: function() {\n        const out = {};\n        try {\n          const ga = this.read('_ga'); // \"GA1.1.123456.789\" -> \"123456.789\"\n          if (ga) { const m = ga.match(/GA\\d+\\.\\d+\\.(.+)$/); if (m) out.ga_client_id = m[1].slice(0, 64); }\n          const fbp = this.read('_fbp'); if (fbp) out.fbp = fbp.slice(0, 128);\n          const fbc = this.read('_fbc'); if (fbc) out.fbc = fbc.slice(0, 256);\n        } catch (e) { /* non-fatal */ }\n        return out;\n      }\n    },\n\n    Analytics: {\n      // GTM-ready funnel events. No PII, ever.\n      push: function(event, params) {\n        try {\n          window.dataLayer = window.dataLayer || [];\n          window.dataLayer.push(Object.assign({ event: event, pdp: !!ShopAIChat.PageContext.handle() }, params || {}));\n        } catch (e) { /* non-fatal */ }\n        if (ShopAIChat.Funnel) ShopAIChat.Funnel.track(event, params || {});\n      }\n    },\n\n    GoogleUserData: {\n      // Enhanced conversions for leads (2026-10-15). After a lead or quiz-gate\n      // submit succeeds, hand the Google tag the shopper's SHA-256 hashed email\n      // and phone, so Google can match later store-sale uploads to the ad click.\n      // Only hashes leave this function (gtag queues them in dataLayer, as it\n      // does every gtag call). The Google tag drops user data when ad_user_data\n      // consent is denied.\n      TAG: 'AW-310848106',\n      normEmail: function(value) {\n        const raw = String(value || '').trim().toLowerCase();\n        if (!/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(raw)) return null;\n        const at = raw.lastIndexOf('@');\n        let local = raw.slice(0, at);\n        const domain = raw.slice(at + 1);\n        if (domain === 'gmail.com' || domain === 'googlemail.com') local = local.replace(/\\./g, '');\n        return local + '@' + domain;\n      },\n      normPhone: function(value) {\n        const digits = String(value || '').replace(/\\D/g, '');\n        if (digits.length === 10) return '+1' + digits;\n        if (digits.length === 11 && digits.charAt(0) === '1') return '+' + digits;\n        return null;\n      },\n      sha256: async function(text) {\n        const bytes = new TextEncoder().encode(text);\n        const digest = await window.crypto.subtle.digest('SHA-256', bytes);\n        return Array.from(new Uint8Array(digest)).map((b) => b.toString(16).padStart(2, '0')).join('');\n      },\n      send: async function(email, phone, source) {\n        try {\n          if (typeof window.gtag !== 'function' || !window.crypto || !window.crypto.subtle) return false;\n          const userData = {};\n          const normEmail = this.normEmail(email);\n          const normPhone = this.normPhone(phone);\n          if (normEmail) userData.sha256_email_address = await this.sha256(normEmail);\n          if (normPhone) userData.sha256_phone_number = await this.sha256(normPhone);\n          if (!userData.sha256_email_address && !userData.sha256_phone_number) return false;\n          window.gtag('set', 'user_data', userData);\n          window.gtag('event', 'form_submit', { send_to: this.TAG, form_id: 'mo-chat-' + (source || 'lead') });\n          return true;\n        } catch (e) { return false; /* non-fatal */ }\n      }\n    },\n\n    Attribution: {\n      KEY: 'shopAiAttribution',\n      CLICK_IDS: ['gclid','gbraid','wbraid','fbclid'],\n      safeValue: function(value) {\n        const raw = String(value || '').trim().slice(0, 256);\n        if (!raw) return null;\n        let decoded = raw;\n        try { decoded = decodeURIComponent(raw); } catch (e) { /* keep raw */ }\n        const email = /[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,}/i;\n        const phone = /(?:\\+?1[ .-]?)?(?:\\([2-9]\\d{2}\\)|[2-9]\\d{2})[ .-]?\\d{3}[ .-]?\\d{4}/;\n        return email.test(decoded) || phone.test(decoded) ? null : raw;\n      },\n      safeCurrentUrl: function() {\n        try {\n          const url = new URL(window.location.href);\n          url.search = '';\n          url.hash = '';\n          return url.toString().slice(0, 512);\n        } catch (e) { return null; }\n      },\n      safeReferrerOrigin: function() {\n        try {\n          const url = new URL(document.referrer || '');\n          return url.protocol === 'https:' ? url.origin : null;\n        } catch (e) { return null; }\n      },\n      capture: function() {\n        try {\n          const p = new URLSearchParams(window.location.search);\n          const pick = ['gclid','gbraid','wbraid','fbclid','utm_source','utm_medium','utm_campaign','utm_term','utm_content'];\n          const cur = {};\n          pick.forEach((k) => { const v = this.safeValue(p.get(k)); if (v) cur[k] = v; });\n          const existing = this.get();\n          const hasStored = existing && Object.keys(existing).length > 0;\n          const storedHasClickId = this.CLICK_IDS.some((k) => existing && existing[k]);\n          const curHasClickId = this.CLICK_IDS.some((k) => cur[k]);\n          // Keep first-touch UTM/landing, but a click id ALWAYS wins: if this\n          // visit carries a gclid/gbraid/wbraid/fbclid and the stored record has\n          // none, upgrade it in place \u2014 otherwise a returning visitor whose FIRST\n          // touch was organic would permanently shadow a later ad click, and its\n          // sale would never export to the Google offline-conversion pipeline.\n          if (!hasStored) {\n            localStorage.setItem(this.KEY, JSON.stringify({ ...cur, landing_url: this.safeCurrentUrl(), referrer: this.safeReferrerOrigin(), ts: Date.now() }));\n          } else if (curHasClickId && !storedHasClickId) {\n            const merged = { ...existing };\n            this.CLICK_IDS.forEach((k) => { if (cur[k]) merged[k] = cur[k]; });\n            merged.click_url = this.safeCurrentUrl();\n            merged.click_ts = Date.now();\n            localStorage.setItem(this.KEY, JSON.stringify(merged));\n          }\n        } catch (e) { /* storage disabled \u2014 non-fatal */ }\n      },\n      get: function() {\n        try { return JSON.parse(localStorage.getItem(this.KEY) || '{}'); } catch (e) { return {}; }\n      }\n    },\n\n    Funnel: {\n      BACKEND: 'https://mo-shop-chat-agent-production.up.railway.app/funnel',\n      KEY: 'shopAiFunnelId',\n      EVENTS: ['quiz_started', 'quiz_question_shown', 'lead_gate_shown', 'lead_form_started', 'results_unlocked', 'brand_hint_revealed', 'timeline_selected'],\n      init: function() {\n        try {\n          if (sessionStorage.getItem(this.KEY)) return;\n          const token = window.crypto && typeof window.crypto.randomUUID === 'function'\n            ? window.crypto.randomUUID()\n            : Date.now().toString(36) + '-' + Math.random().toString(36).slice(2, 14);\n          sessionStorage.setItem(this.KEY, 'f-' + token);\n        } catch (e) { /* storage disabled - analytics remains non-fatal */ }\n      },\n      id: function() {\n        try { return sessionStorage.getItem(this.KEY); } catch (e) { return null; }\n      },\n      deviceType: function() {\n        const width = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0);\n        if (width <= 767) return 'mobile';\n        if (width <= 1024) return 'tablet';\n        return 'desktop';\n      },\n      trafficSource: function() {\n        const a = ShopAIChat.Attribution.get();\n        const source = String(a.utm_source || '').toLowerCase();\n        const medium = String(a.utm_medium || '').toLowerCase();\n        const referrer = String(a.referrer || '').toLowerCase();\n        if (a.gclid || a.gbraid || a.wbraid) return 'google_ads';\n        if (a.fbclid || /facebook|instagram|meta/.test(source)) return 'meta_ads';\n        if (/cpc|ppc|paid|display/.test(medium)) return 'other_paid';\n        if (/email/.test(medium) || /email|newsletter/.test(source)) return 'email';\n        if (/organic/.test(medium) || /google|bing|duckduckgo|yahoo/.test(referrer)) return 'organic_search';\n        if (/referral/.test(medium) || (referrer && !/mattressoverstock\\.us/.test(referrer))) return 'referral';\n        if (!source && !medium && !referrer) return 'direct';\n        return source || medium ? 'other' : 'unknown';\n      },\n      track: function(name, properties) {\n        if (!this.EVENTS.includes(name)) return;\n        const funnelId = this.id();\n        if (!funnelId) return;\n        try {\n          fetch(this.BACKEND, {\n            method: 'POST',\n            headers: { 'Content-Type': 'application/json' },\n            keepalive: true,\n            body: JSON.stringify({\n              funnel_id: funnelId,\n              conversation_id: sessionStorage.getItem('shopAiConversationId'),\n              traffic_source: this.trafficSource(),\n              device_type: this.deviceType(),\n              events: [{ name: name, properties: properties || {}, occurred_at: new Date().toISOString() }]\n            })\n          }).catch(function() {});\n        } catch (e) { /* non-fatal */ }\n      }\n    },\n\n    /**\n     * Identity-on-open. On a deliberate chat open this first persists the anonymous\n     * server-side Chat touch (the attribution bundle the widget already collects \u2014\n     * never a name/email/phone). Only after that write succeeds does it attempt ONE\n     * silent Shopify customer-account check per session through a\n     * hidden iframe (OAuth prompt=none). A signed-in shopper gets linked\n     * server-side; everyone else fails closed to anonymous chat with no login\n     * prompt, popup, or visible change. No PII ever enters widget code, storage,\n     * dataLayer, or analytics from this flow \u2014 the iframe's postMessage result is\n     * only {type:'mo-identity', ok, reason}.\n     */\n    Identity: {\n      TOUCH_BACKEND: 'https://mo-shop-chat-agent-production.up.railway.app/touch',\n      SILENT_BACKEND: 'https://mo-shop-chat-agent-production.up.railway.app/auth/silent',\n      BACKEND_ORIGIN: 'https://mo-shop-chat-agent-production.up.railway.app',\n      DONE_KEY: 'shopAiIdentityDone',\n      TRIED_KEY: 'shopAiIdentityTried',\n      _touchSentThisLoad: false,\n      _triedThisLoad: false,\n      onOpen: function() {\n        const self = this;\n        try {\n          Promise.resolve(this.touch()).then(function(saved) {\n            if (saved) self.silentAuth();\n          }).catch(function() {});\n        } catch (e) { /* non-fatal */ }\n      },\n      // Anonymous touch: one POST per page load; the server upserts one row per\n      // funnel session (repeat opens bump a counter server-side).\n      touch: function() {\n        if (this._touchSentThisLoad) return Promise.resolve(false);\n        try {\n          const funnelId = ShopAIChat.Funnel.id();\n          if (!funnelId) return Promise.resolve(false); // storage disabled: no durable key to record against\n          this._touchSentThisLoad = true;\n          let conversationId = null;\n          try { conversationId = sessionStorage.getItem('shopAiConversationId'); } catch (e) { /* non-fatal */ }\n          return fetch(this.TOUCH_BACKEND, {\n            method: 'POST',\n            headers: { 'Content-Type': 'application/json' },\n            keepalive: true,\n            body: JSON.stringify({\n              funnel_id: funnelId,\n              conversation_id: conversationId,\n              attribution: Object.assign({}, ShopAIChat.Attribution.get(), ShopAIChat.Cookies.collect()),\n              page: ShopAIChat.PageContext.get(),\n              device_type: ShopAIChat.Funnel.deviceType(),\n              widget_version: document.querySelector('.shop-ai-chat-container')?.dataset.moVersion || null\n            })\n          }).then(function(response) { return !!response.ok; }).catch(function() { return false; });\n        } catch (e) { return Promise.resolve(false); }\n      },\n      // Silent signed-in check: hidden iframe -> backend /auth/silent -> Shopify\n      // authorize (prompt=none) -> backend callback page -> postMessage back here.\n      // Once per session (sessionStorage guard; in-memory fallback when storage is\n      // blocked). The iframe is removed on the result message or a 12s timeout.\n      silentAuth: function() {\n        if (this._triedThisLoad) return;\n        this._triedThisLoad = true;\n        try {\n          const funnelId = ShopAIChat.Funnel.id();\n          if (!funnelId) return; // identity must key to the same funnel session as the touch\n          try {\n            if (sessionStorage.getItem(this.DONE_KEY) || sessionStorage.getItem(this.TRIED_KEY)) return;\n            sessionStorage.setItem(this.TRIED_KEY, '1');\n          } catch (e) { /* storage disabled \u2014 the per-page-load guard above still applies */ }\n          let conversationId = '';\n          try { conversationId = sessionStorage.getItem('shopAiConversationId') || ''; } catch (e) { /* non-fatal */ }\n          const src = this.SILENT_BACKEND +\n            '?funnel_id=' + encodeURIComponent(funnelId) +\n            (conversationId ? '&conversation_id=' + encodeURIComponent(conversationId) : '') +\n            '&shop=' + encodeURIComponent(window.location.origin);\n          const iframe = document.createElement('iframe');\n          iframe.src = src;\n          iframe.setAttribute('aria-hidden', 'true');\n          iframe.setAttribute('tabindex', '-1');\n          iframe.setAttribute('title', '');\n          iframe.style.cssText = 'display:none;width:0;height:0;border:0;position:absolute;';\n          const self = this;\n          let settled = false;\n          const onMessage = function(event) {\n            if (event.origin !== self.BACKEND_ORIGIN) return;\n            const data = event.data;\n            if (!data || data.type !== 'mo-identity') return;\n            if (data.ok) {\n              try { sessionStorage.setItem(self.DONE_KEY, '1'); } catch (e) { /* non-fatal */ }\n            }\n            cleanup();\n          };\n          const cleanup = function() {\n            if (settled) return;\n            settled = true;\n            window.removeEventListener('message', onMessage);\n            if (iframe.parentNode) iframe.parentNode.removeChild(iframe);\n          };\n          window.addEventListener('message', onMessage);\n          setTimeout(cleanup, 12000);\n          document.body.appendChild(iframe);\n        } catch (e) { /* non-fatal \u2014 the chat open is never affected */ }\n      }\n    },\n\n    Lead: {\n      BACKEND: 'https://mo-shop-chat-agent-production.up.railway.app/lead',\n      copy: {\n        save_cart:  {\n          title: \"Want to save this shortlist with exact size prices?\",\n          subtitle: \"We will send a clean copy for the size and beds you are considering.\",\n          btn: \"Save this shortlist\"\n        },\n        price_drop: {\n          title: \"Want our team to verify whether a current offer applies?\",\n          subtitle: \"They can confirm any verified promotion for the bed and size you are considering.\",\n          btn: \"Verify current offers\"\n        },\n        first_dibs: {\n          title: \"Want a heads-up when a verified offer becomes available?\",\n          subtitle: \"Our team can follow up if a confirmed offer matches the beds you are considering.\",\n          btn: \"Get verified updates\"\n        },\n        financing:  {\n          title: \"Want real monthly-payment options sent to you?\",\n          subtitle: \"A Mattress Overstock team member can send options for the bed you are considering.\",\n          btn: \"Send payment options\"\n        }\n      },\n      consentText: \"I agree to receive recurring automated marketing & cart-reminder texts from Mattress Overstock at the number above. Consent isn't a condition of purchase. Msg & data rates may apply. Reply STOP to opt out, HELP for help.\",\n      copyFor: function(hook, context) {\n        const key = this.copy[hook] ? hook : 'save_cart';\n        if (key === 'save_cart' && context && Number(context.productCount) > 0) {\n          const count = Number(context.productCount);\n          const label = count === 1 ? 'pick' : 'picks';\n          return {\n            title: \"Want these \" + label + \" saved with exact size prices?\",\n            subtitle: \"We will send a clean shortlist for the size and beds you are considering.\",\n            btn: \"Save these \" + label\n          };\n        }\n        return this.copy[key];\n      },\n      applyCopy: function(form, hook, context) {\n        if (!form) return;\n        const c = this.copyFor(hook, context);\n        const title = form.querySelector('.shop-ai-lead-title');\n        const subtitle = form.querySelector('.shop-ai-lead-subtitle');\n        const submit = form.querySelector('.shop-ai-lead-submit');\n        if (title) title.textContent = c.title;\n        if (subtitle) subtitle.textContent = c.subtitle || '';\n        if (submit) submit.textContent = c.btn;\n      },\n      show: function(hook, messagesContainer, opts) {\n        // Already captured this session (survives reloads) \u2014 never re-offer a form.\n        if (sessionStorage.getItem('shopAiLeadDone')) return;\n        // A support handoff owns the conversation; never stack a sales form beside it.\n        if (sessionStorage.getItem('shopAiSupportActive') || messagesContainer.querySelector('.shop-ai-support-form')) return;\n        // Already captured in this thread \u2014 never stack another form on a submitted one.\n        if (messagesContainer.querySelector('form.shop-ai-lead-form.shop-ai-lead-done')) return;\n        // An active (unsubmitted) lead form: re-anchor it under the newest offer instead of\n        // silently dropping the offer. (:not(.shop-ai-support-form) \u2014 the support form shares\n        // the lead-form class and used to block lead forms entirely.)\n        const existing = messagesContainer.querySelector('form.shop-ai-lead-form:not(.shop-ai-support-form):not(.shop-ai-lead-done)');\n        if (existing) {\n          existing.dataset.hook = hook;\n          this.applyCopy(existing, hook);\n          messagesContainer.appendChild(existing);\n          ShopAIChat.Analytics.push('lead_form_shown', { hook: hook, reshown: true });\n          try { sessionStorage.setItem('shopAiPendingLeadHook', hook); } catch (e) { /* non-fatal */ }\n          ShopAIChat.UI.scrollOfferIntoView(existing, true);\n          return;\n        }\n        // A form restored after a page reload is NOT a new offer \u2014 don't re-count it in\n        // the GA4 funnel denominator (one ignoring shopper browsing 5 PDPs is 1 offer, not 6).\n        if (!(opts && opts.restored)) {\n          ShopAIChat.Analytics.push('lead_form_shown', { hook: hook });\n        }\n        const form = document.createElement('form');\n        form.className = 'shop-ai-lead-form';\n        form.dataset.hook = hook;\n        form.innerHTML =\n          '<div class=\"shop-ai-lead-eyebrow\">Save it for later</div>' +\n          '<div class=\"shop-ai-lead-title\"></div>' +\n          '<div class=\"shop-ai-lead-subtitle\"></div>' +\n          '<label class=\"shop-ai-lead-field\"><span>First name <em>optional</em></span><input class=\"shop-ai-lead-name\" type=\"text\" placeholder=\"First name\" autocomplete=\"given-name\"></label>' +\n          '<label class=\"shop-ai-lead-field\"><span>Mobile number</span><input class=\"shop-ai-lead-phone\" type=\"tel\" placeholder=\"859-555-0142\" autocomplete=\"tel\" inputmode=\"tel\"></label>' +\n          '<button type=\"button\" class=\"shop-ai-lead-email-toggle\">Prefer email instead?</button>' +\n          '<label class=\"shop-ai-lead-field shop-ai-lead-email-field shop-ai-lead-email-hidden\"><span>Email</span><input class=\"shop-ai-lead-email\" type=\"email\" placeholder=\"you@example.com\" autocomplete=\"email\" inputmode=\"email\"></label>' +\n          '<label class=\"shop-ai-lead-consent\"><input type=\"checkbox\" class=\"shop-ai-lead-consent-box\"> <span></span></label>' +\n          '<div class=\"shop-ai-lead-hint\" role=\"status\"></div>' +\n          '<div class=\"shop-ai-lead-trust\"></div>' +\n          '<button type=\"submit\" class=\"shop-ai-lead-submit\"></button>' +\n          '<div class=\"shop-ai-lead-status\" role=\"status\"></div>';\n        this.applyCopy(form, hook);\n        form.querySelector('.shop-ai-lead-consent span').textContent = this.consentText;\n        // Trust microcopy \u2014 accurate consent and channel choices at the decision point.\n        // textContent-only assembly with dot separators keeps it injection-safe.\n        (function (trust) {\n          if (!trust) return;\n          const dot = function () {\n            const d = document.createElement('span');\n            d.className = 'shop-ai-trust-dot';\n            return d;\n          };\n          trust.appendChild(document.createTextNode('Consent is not required to buy'));\n          trust.appendChild(dot());\n          trust.appendChild(document.createTextNode('Reply STOP anytime'));\n          trust.appendChild(dot());\n          trust.appendChild(document.createTextNode('Phone or email works'));\n        })(form.querySelector('.shop-ai-lead-trust'));\n        form.querySelector('.shop-ai-lead-email-toggle').addEventListener('click', () => {\n          const emailField = form.querySelector('.shop-ai-lead-email-field');\n          const emailInput = form.querySelector('.shop-ai-lead-email');\n          if (emailField) emailField.classList.remove('shop-ai-lead-email-hidden');\n          form.querySelector('.shop-ai-lead-email-toggle').remove();\n          if (emailInput) emailInput.focus();\n        });\n        form.addEventListener('submit', (e) => { e.preventDefault(); this.submit(form, form.dataset.hook || hook); });\n        // Survive full page reloads (every product-card click reloads the page) \u2014 see fetchChatHistory.\n        try { sessionStorage.setItem('shopAiPendingLeadHook', hook); } catch (e) { /* non-fatal */ }\n        messagesContainer.appendChild(form);\n        ShopAIChat.UI.scrollOfferIntoView(form, true);\n      },\n      submit: async function(form, hook) {\n        const name = form.querySelector('.shop-ai-lead-name').value.trim();\n        const phone = form.querySelector('.shop-ai-lead-phone').value.trim();\n        const email = form.querySelector('.shop-ai-lead-email').value.trim();\n        const consent = form.querySelector('.shop-ai-lead-consent-box').checked;\n        const status = form.querySelector('.shop-ai-lead-status');\n        const btn = form.querySelector('.shop-ai-lead-submit');\n        const hint = form.querySelector('.shop-ai-lead-hint');\n        const consentLabel = form.querySelector('.shop-ai-lead-consent');\n        if (hint) hint.textContent = '';\n        if (consentLabel) consentLabel.classList.remove('shop-ai-consent-attention');\n        if (!phone && !email) { status.textContent = 'Please add a phone or email.'; return; }\n        if (phone && !consent) {\n          // Point at the fix instead of dead-ending at the bottom of the form.\n          if (hint) hint.textContent = 'Check the box above so we can legally text you.';\n          if (consentLabel) consentLabel.classList.add('shop-ai-consent-attention');\n          status.textContent = '';\n          return;\n        }\n        btn.disabled = true; status.textContent = 'Sending\u2026';\n        try {\n          const res = await fetch(this.BACKEND, {\n            method: 'POST',\n            headers: { 'Content-Type': 'application/json' },\n            body: JSON.stringify({\n              conversation_id: sessionStorage.getItem('shopAiConversationId'),\n              name, phone, email,\n              consent_sms: consent,\n              consent_text: this.consentText,\n              hooks: [hook],\n              funnel_id: ShopAIChat.Funnel.id(),\n              device_type: ShopAIChat.Funnel.deviceType(),\n              attribution: Object.assign({}, ShopAIChat.Attribution.get(), ShopAIChat.Cookies.collect()),\n            })\n          });\n          const data = await res.json();\n          if (data.ok) {\n            form.classList.add('shop-ai-lead-done');\n            try {\n              sessionStorage.removeItem('shopAiPendingLeadHook');\n              // Captured-state must survive reloads too, or the picks chip / a model\n              // re-offer re-renders a fresh form and a resubmit re-texts the shopper.\n              sessionStorage.setItem('shopAiLeadDone', '1');\n            } catch (e) { /* non-fatal */ }\n            form.querySelectorAll('input,button').forEach((el) => { el.disabled = true; });\n            status.textContent = phone ? '\u2713 Sent \u2014 check your texts!' : '\u2713 Sent \u2014 keep an eye on your inbox!';\n            ShopAIChat.Analytics.push('lead_form_submitted', { hook: hook });\n            ShopAIChat.GoogleUserData.send(email, phone, 'lead');\n          } else {\n            btn.disabled = false; status.textContent = data.error || 'Something went wrong. Try again.';\n          }\n        } catch (err) {\n          btn.disabled = false; status.textContent = 'Network error \u2014 please try again.';\n        }\n      }\n    },\n\n    QuizFinaleGate: {\n      TIMELINE_BACKEND: 'https://mo-shop-chat-agent-production.up.railway.app/lead/timeline',\n      state: null,\n\n      arm: function(data, messagesContainer) {\n        this.state = {\n          profile: Array.isArray(data && data.profile) ? data.profile.slice(0, 4) : [],\n          messagesContainer: messagesContainer,\n          requiredPhoneSms: true\n        };\n      },\n\n      cancel: function(messagesContainer) {\n        const root = messagesContainer || (this.state && this.state.messagesContainer);\n        if (root) root.querySelectorAll('.shop-ai-quiz-gate').forEach((el) => el.remove());\n        this.state = null;\n      },\n\n      show: function(data, messagesContainer, opts) {\n        if (!messagesContainer || !data) return;\n        const gateId = String(data.gate_id || data.id || '');\n        const matchCount = Math.max(1, Math.min(3, Number(data.match_count) || 1));\n        if (!gateId) return;\n\n        messagesContainer.querySelectorAll('.shop-ai-quiz-gate').forEach((el) => el.remove());\n        messagesContainer.querySelectorAll('form.shop-ai-lead-form:not(.shop-ai-support-form):not(.shop-ai-lead-done)')\n          .forEach((form) => form.remove());\n        try { sessionStorage.removeItem('shopAiPendingLeadHook'); } catch (e) { /* non-fatal */ }\n\n        this.state = Object.assign({}, this.state || {}, {\n          gateId: gateId,\n          profile: Array.isArray(data.profile) ? data.profile.slice(0, 4) : [],\n          matchCount: matchCount,\n          primaryBrand: typeof data.primary_brand === 'string' ? data.primary_brand : null,\n          requiredPhoneSms: true,\n          timelineEnabled: data.timeline_enabled === true,\n          leadId: data.lead_id || null,\n          timelineSelected: data.purchase_timeline || null,\n          messagesContainer: messagesContainer\n        });\n\n        const section = document.createElement('section');\n        section.className = 'shop-ai-quiz-gate';\n        section.setAttribute('aria-labelledby', 'shop-ai-quiz-gate-title');\n\n        const eyebrow = document.createElement('div');\n        eyebrow.className = 'shop-ai-quiz-gate-eyebrow';\n        eyebrow.textContent = 'YOUR FIT PROFILE';\n\n        const title = document.createElement('h3');\n        title.id = 'shop-ai-quiz-gate-title';\n        title.className = 'shop-ai-quiz-gate-title';\n        title.textContent = 'I found ' + matchCount + (matchCount === 1 ? ' strong match.' : ' strong matches.');\n\n        const profile = document.createElement('div');\n        profile.className = 'shop-ai-quiz-gate-profile';\n        this.state.profile.forEach(function(label) {\n          const tag = document.createElement('span');\n          tag.textContent = String(label);\n          profile.appendChild(tag);\n        });\n\n        const intro = document.createElement('p');\n        intro.className = 'shop-ai-quiz-gate-intro';\n        intro.textContent = \"Your exact models, links, and today's prices are ready. Add your mobile number to reveal them now and get the shortlist by text.\";\n\n        const hintArea = document.createElement('div');\n        hintArea.className = 'shop-ai-quiz-gate-hint-area';\n        if (this.state.primaryBrand) {\n          const hintButton = document.createElement('button');\n          hintButton.type = 'button';\n          hintButton.className = 'shop-ai-quiz-gate-hint-button';\n          hintButton.textContent = 'Give me one hint';\n          hintButton.addEventListener('click', () => {\n            const hint = document.createElement('p');\n            hint.className = 'shop-ai-quiz-gate-hint';\n            hint.tabIndex = -1;\n            hint.textContent = 'Your primary match comes from ' + this.state.primaryBrand + '.';\n            hintArea.replaceChildren(hint);\n            hint.focus();\n            ShopAIChat.Analytics.push('brand_hint_revealed', {});\n          }, { once: true });\n          hintArea.appendChild(hintButton);\n        }\n\n        const form = document.createElement('form');\n        form.className = 'shop-ai-quiz-gate-form';\n        form.innerHTML =\n          '<label class=\"shop-ai-quiz-gate-field\"><span>First name <em>optional</em></span><input class=\"shop-ai-quiz-gate-name\" type=\"text\" autocomplete=\"given-name\" placeholder=\"First name\"></label>' +\n          '<label class=\"shop-ai-quiz-gate-field\"><span>Mobile number</span><input class=\"shop-ai-quiz-gate-phone\" type=\"tel\" autocomplete=\"tel\" inputmode=\"tel\" placeholder=\"859-555-0142\" required></label>' +\n          '<label class=\"shop-ai-quiz-gate-consent\"><input class=\"shop-ai-quiz-gate-consent-box\" type=\"checkbox\" required><span></span></label>' +\n          '<p class=\"shop-ai-quiz-gate-note\">No verification code. Reply STOP anytime. Our local team replies during store hours.</p>' +\n          '<button class=\"shop-ai-quiz-gate-submit\" type=\"submit\">Reveal my matches</button>' +\n          '<div class=\"shop-ai-quiz-gate-status\" role=\"status\" aria-live=\"polite\"></div>';\n        form.querySelector('.shop-ai-quiz-gate-consent span').textContent = ShopAIChat.Lead.consentText;\n\n        let started = false;\n        const recordStart = function() {\n          if (started) return;\n          started = true;\n          ShopAIChat.Analytics.push('lead_form_started', { hook: 'save_cart' });\n        };\n        form.querySelector('.shop-ai-quiz-gate-phone').addEventListener('focus', recordStart, { once: true });\n        form.querySelector('.shop-ai-quiz-gate-phone').addEventListener('input', recordStart, { once: true });\n        form.addEventListener('submit', (event) => {\n          event.preventDefault();\n          this.submit(form);\n        });\n\n        section.appendChild(eyebrow);\n        section.appendChild(title);\n        if (this.state.profile.length) section.appendChild(profile);\n        section.appendChild(intro);\n        section.appendChild(hintArea);\n        section.appendChild(form);\n        messagesContainer.appendChild(section);\n        this.state.element = section;\n\n        if (!(opts && opts.restored)) {\n          ShopAIChat.Analytics.push('lead_gate_shown', { hook: 'save_cart', match_count: matchCount });\n        }\n        ShopAIChat.UI.scrollOfferIntoView(section, true);\n      },\n\n      submit: async function(form) {\n        const state = this.state;\n        if (!state || !form) return;\n        const name = form.querySelector('.shop-ai-quiz-gate-name').value.trim();\n        const phone = form.querySelector('.shop-ai-quiz-gate-phone').value.trim();\n        const consent = form.querySelector('.shop-ai-quiz-gate-consent-box').checked;\n        const status = form.querySelector('.shop-ai-quiz-gate-status');\n        const button = form.querySelector('.shop-ai-quiz-gate-submit');\n        if (phone.replace(/\\D/g, '').length < 10) {\n          status.textContent = 'Enter a valid mobile number.';\n          return;\n        }\n        if (!consent) {\n          status.textContent = 'Check the consent box so we can text your matches.';\n          return;\n        }\n\n        button.disabled = true;\n        button.textContent = 'Unlocking matches...';\n        status.textContent = '';\n        try {\n          const response = await fetch(ShopAIChat.Lead.BACKEND, {\n            method: 'POST',\n            headers: { 'Content-Type': 'application/json' },\n            body: JSON.stringify({\n              conversation_id: sessionStorage.getItem('shopAiConversationId'),\n              gate_id: state.gateId,\n              name: name,\n              phone: phone,\n              email: '',\n              consent_sms: true,\n              consent_text: ShopAIChat.Lead.consentText,\n              hooks: ['save_cart'],\n              funnel_id: ShopAIChat.Funnel.id(),\n              device_type: ShopAIChat.Funnel.deviceType(),\n              attribution: Object.assign({}, ShopAIChat.Attribution.get(), ShopAIChat.Cookies.collect())\n            })\n          });\n          const data = await response.json();\n          if (!response.ok || !data.ok || !data.quiz_gate_result) {\n            button.disabled = false;\n            button.textContent = 'Reveal my matches';\n            status.textContent = data.error || 'Something went wrong. Try again.';\n            return;\n          }\n          try {\n            sessionStorage.setItem('shopAiLeadDone', '1');\n            sessionStorage.removeItem('shopAiPendingLeadHook');\n          } catch (e) { /* non-fatal */ }\n          ShopAIChat.GoogleUserData.send('', phone, 'quiz_gate');\n          this.unlock(data.quiz_gate_result, state.messagesContainer, {\n            gateId: state.gateId,\n            leadId: data.lead_id,\n            timelineEnabled: data.timeline_enabled === true,\n            restored: false\n          });\n        } catch (error) {\n          button.disabled = false;\n          button.textContent = 'Reveal my matches';\n          status.textContent = 'Network error - please try again.';\n        }\n      },\n\n      unlock: function(result, messagesContainer, opts) {\n        if (!messagesContainer || !result || typeof result.assistantText !== 'string' || !Array.isArray(result.products)) return;\n        if (messagesContainer.querySelector('.shop-ai-quiz-result')) return;\n        const prior = this.state || {};\n        const meta = Object.assign({}, prior, opts || {});\n        messagesContainer.querySelectorAll('.shop-ai-quiz-gate').forEach((el) => el.remove());\n        try { sessionStorage.setItem('shopAiLeadDone', '1'); } catch (e) { /* non-fatal */ }\n\n        const success = document.createElement('div');\n        success.className = 'shop-ai-quiz-gate-success';\n        success.setAttribute('role', 'status');\n        success.tabIndex = -1;\n        success.textContent = 'Matches unlocked. Your shortlist is also headed to your phone.';\n        messagesContainer.appendChild(success);\n\n        const reply = ShopAIChat.Message.add(result.assistantText, 'assistant', messagesContainer);\n        reply.classList.add('shop-ai-quiz-result');\n        ShopAIChat.UI.displayProductResults(result.products);\n        ShopAIChat.Quiz.onProductResults();\n        this.state = Object.assign({}, meta, { unlocked: true, messagesContainer: messagesContainer });\n\n        if (!(opts && opts.restored)) {\n          ShopAIChat.Analytics.push('results_unlocked', { unlock_method: 'lead_submit' });\n        }\n        if (meta.timelineEnabled && !meta.timelineSelected && meta.leadId) {\n          this.showTimeline(messagesContainer, meta.leadId, meta.gateId);\n        }\n        if (!(opts && opts.restored)) success.focus();\n      },\n\n      showTimeline: function(messagesContainer, leadId, gateId) {\n        if (!messagesContainer || !leadId || !gateId || messagesContainer.querySelector('.shop-ai-lead-timeline')) return;\n        const section = document.createElement('section');\n        section.className = 'shop-ai-lead-timeline';\n        const title = document.createElement('h4');\n        title.textContent = 'When are you hoping to sleep on a new mattress?';\n        const copy = document.createElement('p');\n        copy.textContent = 'Optional - this helps our local team prioritize the right next step.';\n        const choices = document.createElement('div');\n        choices.className = 'shop-ai-lead-timeline-choices';\n        const status = document.createElement('div');\n        status.className = 'shop-ai-lead-timeline-status';\n        status.setAttribute('role', 'status');\n        const labels = [\n          ['today', 'Today'],\n          ['this_week', 'This week'],\n          ['this_month', 'This month'],\n          ['researching', 'Just researching']\n        ];\n        labels.forEach(([value, label]) => {\n          const button = document.createElement('button');\n          button.type = 'button';\n          button.textContent = label;\n          button.addEventListener('click', async () => {\n            choices.querySelectorAll('button').forEach((item) => { item.disabled = true; });\n            status.textContent = 'Saving...';\n            try {\n              const response = await fetch(this.TIMELINE_BACKEND, {\n                method: 'POST',\n                headers: { 'Content-Type': 'application/json' },\n                body: JSON.stringify({\n                  lead_id: leadId,\n                  conversation_id: sessionStorage.getItem('shopAiConversationId'),\n                  gate_id: gateId,\n                  timeline: value,\n                  funnel_id: ShopAIChat.Funnel.id(),\n                  device_type: ShopAIChat.Funnel.deviceType(),\n                  traffic_source: ShopAIChat.Funnel.trafficSource()\n                })\n              });\n              const data = await response.json();\n              if (!response.ok || !data.ok) throw new Error(data.error || 'Could not save timeline');\n              button.classList.add('selected');\n              status.textContent = 'Saved. You can keep chatting here anytime.';\n              ShopAIChat.Analytics.push('timeline_selected', { timeline: value });\n            } catch (error) {\n              choices.querySelectorAll('button').forEach((item) => { item.disabled = false; });\n              status.textContent = 'Could not save that yet. You can skip it or try again.';\n            }\n          });\n          choices.appendChild(button);\n        });\n        const skip = document.createElement('button');\n        skip.type = 'button';\n        skip.className = 'shop-ai-lead-timeline-skip';\n        skip.textContent = 'Not now';\n        skip.addEventListener('click', () => section.remove());\n        section.appendChild(title);\n        section.appendChild(copy);\n        section.appendChild(choices);\n        section.appendChild(skip);\n        section.appendChild(status);\n        messagesContainer.appendChild(section);\n        ShopAIChat.UI.scrollToBottom(true);\n      }\n    },\n\n    /**\n     * Attention nudge: a small dismissible \"speech bubble\" near the launcher that\n     * CYCLES through enticing messages to invite the shopper to chat. First message\n     * ~5s after load; each shows ~8s then hides ~20s before the next. Keeps looping\n     * while ignored; stops for the session on \u2715 dismiss or when the chat is opened.\n     * Static copy, no PII.\n     */\n    Nudge: {\n      START_DELAY_MS: 2500,\n      SHOW_MS: 8000,\n      GAP_MS: 20000,\n      MESSAGES: [\n        \"Not sure where to start? Take the 60-second sleep quiz and I'll narrow it down.\",\n        \"Tell me what your last mattress got wrong. I'll start there.\",\n        \"Side sleeper, hot sleeper, or sharing a bed? I can narrow it fast.\",\n        \"Have a budget and hate mattress jargon? Good. Let's narrow it.\",\n        \"Already comparing two beds? I'll give you the honest tradeoff.\",\n        \"Tell me how you sleep and I'll pick the best fit.\",\n      ],\n      el: null,\n      textEl: null,\n      _i: 0,\n      _timer: null,\n      _stopped: false,\n\n      init: function(container) {\n        if (!container) return;\n        // If the shopper already dismissed or engaged this session, don't loop again.\n        if (sessionStorage.getItem('shopAiNudgeStopped') === '1') return;\n\n        // PDP: lead with a product-aware invite, then continue the generic rotation.\n        const pdpTitle = ShopAIChat.PageContext.handle() && ShopAIChat.PageContext.shortTitle();\n        if (pdpTitle) {\n          this.MESSAGES = [\"Wondering if the \" + pdpTitle + \" fits how you sleep? I'll tell you where it fits and where it doesn't.\"].concat(this.MESSAGES);\n        }\n\n        const nudge = document.createElement('div');\n        nudge.className = 'shop-ai-nudge';\n        nudge.setAttribute('role', 'button');\n        nudge.setAttribute('tabindex', '0');\n        nudge.setAttribute('aria-label', 'Open chat');\n\n        const text = document.createElement('span');\n        text.className = 'shop-ai-nudge-text';\n\n        const close = document.createElement('button');\n        close.className = 'shop-ai-nudge-close';\n        close.setAttribute('type', 'button');\n        close.setAttribute('aria-label', 'Dismiss');\n        close.textContent = '\u00D7';\n\n        nudge.appendChild(text);\n        nudge.appendChild(close);\n        container.appendChild(nudge);\n        this.el = nudge;\n        this.textEl = text;\n\n        const open = () => {\n          this.dismiss();\n          const win = ShopAIChat.UI.elements && ShopAIChat.UI.elements.chatWindow;\n          if (!win || !win.classList.contains('active')) ShopAIChat.UI.toggleChatWindow();\n        };\n        nudge.addEventListener('click', (e) => { if (e.target === close) return; open(); });\n        nudge.addEventListener('keydown', (e) => {\n          if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); open(); }\n        });\n        close.addEventListener('click', (e) => { e.stopPropagation(); this.dismiss(); });\n\n        this._timer = setTimeout(() => this.cycle(), this.START_DELAY_MS);\n      },\n\n      // Show the next message, hide it after SHOW_MS, then loop again after GAP_MS.\n      cycle: function() {\n        if (this._stopped || !this.el) return;\n        // Never pop over an open chat \u2014 skip this beat, re-check after the gap.\n        const win = ShopAIChat.UI.elements && ShopAIChat.UI.elements.chatWindow;\n        if (win && win.classList.contains('active')) {\n          this._timer = setTimeout(() => this.cycle(), this.GAP_MS);\n          return;\n        }\n        const msg = this.MESSAGES[this._i % this.MESSAGES.length];\n        this.textEl.textContent = msg;\n        this.el.setAttribute('aria-label', 'Open chat \u2014 ' + msg);\n        this._i++;\n        this.el.classList.add('active');\n        this._timer = setTimeout(() => {\n          if (this.el) this.el.classList.remove('active');\n          if (!this._stopped) {\n            this._timer = setTimeout(() => this.cycle(), this.GAP_MS);\n          }\n        }, this.SHOW_MS);\n      },\n\n      // Stop the loop for this page load (in memory).\n      stop: function() {\n        this._stopped = true;\n        if (this._timer) { clearTimeout(this._timer); this._timer = null; }\n        if (this.el) this.el.classList.remove('active');\n      },\n\n      // Explicit dismiss (\u2715) or engagement (chat opened) \u2192 stop for the whole session.\n      dismiss: function() {\n        sessionStorage.setItem('shopAiNudgeStopped', '1');\n        this.stop();\n      },\n    },\n\n    Quiz: {\n      _count: 0,\n      _recsFired: false,\n\n      // Send text through the existing pipeline (same idiom as product-card buttons).\n      sendAsUser: function(text) {\n        const input = document.querySelector('.shop-ai-chat-input input');\n        const sendButton = document.querySelector('.shop-ai-chat-send');\n        if (!input || !sendButton) return;\n        input.value = text;\n        sendButton.click();\n      },\n\n      // Speak first on a genuinely fresh thread. This is local UI copy, not an LLM/API turn,\n      // so opening the widget stays instant and does not create a phantom conversation row.\n      showOpenGreeting: function(messagesContainer) {\n        if (!messagesContainer || messagesContainer.querySelector('.shop-ai-open-greeting')) return;\n        const hero = messagesContainer.querySelector('.shop-ai-welcome-hero');\n        if (!hero) return; // Returning conversations keep their real history, without a new hello.\n\n        const onPdp = !!ShopAIChat.PageContext.handle();\n        const pdpTitle = onPdp && ShopAIChat.PageContext.shortTitle();\n        const generic = (window.shopChatConfig && window.shopChatConfig.openingMessage) ||\n          \"What did your last mattress get wrong: too firm, too hot, or not supportive enough? I'll start there.\";\n        const text = pdpTitle\n          ? \"Want an honest fit check on \" + pdpTitle + \"? I'll ask a few quick questions and tell you where it fits and where it doesn't.\"\n          : generic;\n        const message = ShopAIChat.Message.add(text, 'assistant', messagesContainer);\n        message.classList.add('shop-ai-open-greeting');\n        messagesContainer.insertBefore(message, hero);\n      },\n\n      // Welcome hero: animated merchandising welcome state \u2014 kicker + greeting + quiz CTA\n      // as the hero button (PDP pages get the fit-verdict variant) plus two ghost-chip\n      // starters so openers never face a blank input. Keeps the function name so all\n      // call sites and the .shop-ai-quiz-chip render-once guard stay intact.\n      renderWelcomeChip: function(messagesContainer) {\n        if (!messagesContainer || messagesContainer.querySelector('.shop-ai-quiz-chip')) return;\n        const onPdp = !!ShopAIChat.PageContext.handle();\n        const pdpTitle = onPdp && ShopAIChat.PageContext.shortTitle();\n        const self = this;\n\n        const hero = document.createElement('div');\n        hero.className = 'shop-ai-welcome-hero';\n\n        const kicker = document.createElement('div');\n        kicker.className = 'shop-ai-hero-kicker';\n        kicker.textContent = 'YOUR LOCAL MATTRESS AUTHORITY';\n\n        const title = document.createElement('div');\n        title.className = 'shop-ai-hero-title';\n        title.textContent = pdpTitle ? \"Let's see if \" + pdpTitle + \" fits you.\" : \"Let's find your best-fit bed.\";\n\n        const sub = document.createElement('div');\n        sub.className = 'shop-ai-hero-sub';\n        sub.textContent = onPdp\n          ? \"Your exact matches and prices unlock by text after the quiz.\"\n          : ((window.shopChatConfig && window.shopChatConfig.heroSub) ||\n            \"Your exact matches and prices unlock by text after the quiz.\");\n\n        hero.appendChild(kicker);\n        hero.appendChild(title);\n        hero.appendChild(sub);\n\n        const mk = function(label, sendText, analyticsEvent, extraClass) {\n          const chip = document.createElement('button');\n          chip.type = 'button';\n          chip.className = 'shop-ai-quiz-chip' + (extraClass ? ' ' + extraClass : '');\n          chip.textContent = label;\n          chip.addEventListener('click', () => {\n            if (analyticsEvent) {\n              const analyticsParams = analyticsEvent === 'quiz_started'\n                ? { entry_point: onPdp ? 'pdp_cta' : 'home_cta' }\n                : undefined;\n              ShopAIChat.Analytics.push(analyticsEvent, analyticsParams);\n            }\n            self.sendAsUser(sendText);\n          });\n          return chip;\n        };\n\n        hero.appendChild(mk(\n          onPdp ? \"Check this bed's fit\" : 'Help me narrow it down',\n          onPdp ? 'Is this bed right for me? Start the sleep quiz' : 'Help me find my match \u2014 start the sleep quiz',\n          'quiz_started',\n          'shop-ai-chip-hero'\n        ));\n\n        const chipsRow = document.createElement('div');\n        chipsRow.className = 'shop-ai-hero-chips';\n        if (onPdp) {\n          chipsRow.appendChild(mk('Compare this bed', 'Compare this bed with the closest alternative and give me the honest tradeoff.', 'chip_compare_pdp_clicked', 'shop-ai-chip-ghost'));\n          chipsRow.appendChild(mk('Price & financing', 'What is the price for this bed, and what financing is available?', 'chip_financing_pdp_clicked', 'shop-ai-chip-ghost'));\n        } else {\n          chipsRow.appendChild(mk(\"What's on sale?\", \"Current mattress sale. Jamison, Casper, and Molecule.\", 'chip_deals_clicked', 'shop-ai-chip-ghost'));\n          chipsRow.appendChild(mk('Delivery & setup', 'How do delivery, setup, and old-mattress haul-away work?', 'chip_delivery_clicked', 'shop-ai-chip-ghost'));\n        }\n        hero.appendChild(chipsRow);\n\n        messagesContainer.appendChild(hero);\n        const chatWindow = ShopAIChat.UI.elements && ShopAIChat.UI.elements.chatWindow;\n        if (chatWindow && chatWindow.classList.contains('active')) this.showOpenGreeting(messagesContainer);\n      },\n\n      removeWelcomeChip: function() {\n        document.querySelectorAll('.shop-ai-quiz-chip, .shop-ai-welcome-hero, .shop-ai-text-picks-chip').forEach((el) => el.remove());\n      },\n\n      // Render one question with tappable option chips.\n      show: function(data, messagesContainer) {\n        if (!messagesContainer) return;\n        // Dedupe: if the model repeats the SAME question (server guard should prevent\n        // this, but belt-and-suspenders), don't render a second identical card.\n        const lastCard = messagesContainer.querySelector('.shop-ai-quiz-card:last-of-type .shop-ai-quiz-question');\n        if (lastCard && lastCard.textContent === (data.question || '')) return;\n        // Only one active card: retire any previous card's buttons first.\n        messagesContainer.querySelectorAll('.shop-ai-quiz-card:not(.answered)').forEach((card) => {\n          card.classList.add('answered');\n          card.querySelectorAll('button').forEach((b) => { b.disabled = true; });\n        });\n        const serverStep = Number.isInteger(data.step) && data.step >= 1 && data.step <= 8\n          ? data.step\n          : this._count + 1;\n        this._count = Math.max(this._count, serverStep);\n        if (serverStep === 1) ShopAIChat.Funnel.track('quiz_started', { entry_point: 'guided_text' });\n        ShopAIChat.Analytics.push('quiz_question_shown', { step: serverStep });\n        const card = document.createElement('div');\n        card.className = 'shop-ai-quiz-card';\n        const step = document.createElement('div');\n        step.className = 'shop-ai-quiz-step';\n        step.textContent = 'QUESTION ' + serverStep;\n        card.appendChild(step);\n        const q = document.createElement('div');\n        q.className = 'shop-ai-quiz-question';\n        q.textContent = data.question || '';\n        card.appendChild(q);\n        const opts = document.createElement('div');\n        opts.className = 'shop-ai-quiz-options';\n        (data.options || []).forEach((label) => {\n          const b = document.createElement('button');\n          b.type = 'button';\n          b.className = 'shop-ai-quiz-option';\n          b.textContent = label;\n          b.addEventListener('click', () => {\n            if (card.classList.contains('answered')) return;\n            card.classList.add('answered');\n            b.classList.add('selected');\n            card.querySelectorAll('button').forEach((x) => { x.disabled = true; });\n            ShopAIChat.Quiz.sendAsUser(label);\n          });\n          opts.appendChild(b);\n        });\n        card.appendChild(opts);\n        messagesContainer.appendChild(card);\n        ShopAIChat.UI.scrollToBottom(true);\n      },\n\n      // quiz_recs_shown: first product_results after at least one quiz question.\n      onProductResults: function() {\n        if (this._count > 0 && !this._recsFired) {\n          this._recsFired = true;\n          ShopAIChat.Analytics.push('quiz_recs_shown', { questions: this._count });\n        }\n      },\n    },\n\n    Support: {\n      BACKEND: 'https://mo-shop-chat-agent-production.up.railway.app/support',\n      show: function(category, contact, messagesContainer) {\n        // Support and sales capture are mutually exclusive. Remove an unsubmitted sales form\n        // and its reload marker before mounting the support handoff.\n        messagesContainer.querySelectorAll('form.shop-ai-lead-form:not(.shop-ai-support-form):not(.shop-ai-lead-done)')\n          .forEach((form) => form.remove());\n        try {\n          sessionStorage.removeItem('shopAiPendingLeadHook');\n          sessionStorage.setItem('shopAiSupportActive', '1');\n        } catch (e) { /* non-fatal */ }\n        if (messagesContainer.querySelector('.shop-ai-support-form')) return;\n        const c = contact || {};\n        const form = document.createElement('form');\n        form.className = 'shop-ai-lead-form shop-ai-support-form';\n        form.innerHTML =\n          '<div class=\"shop-ai-lead-title\">How can our team help?</div>' +\n          '<textarea class=\"shop-ai-support-issue\" rows=\"3\" placeholder=\"Briefly describe your issue (order, delivery, return, etc.)\"></textarea>' +\n          '<input class=\"shop-ai-lead-name\" type=\"text\" placeholder=\"Name (optional)\" autocomplete=\"name\">' +\n          '<input class=\"shop-ai-lead-phone\" type=\"tel\" placeholder=\"Phone\" autocomplete=\"tel\">' +\n          '<input class=\"shop-ai-lead-email\" type=\"email\" placeholder=\"Email\" autocomplete=\"email\">' +\n          '<button type=\"submit\" class=\"shop-ai-lead-submit\">Send to our team</button>' +\n          '<div class=\"shop-ai-lead-status\" role=\"status\"></div>' +\n          '<div class=\"shop-ai-support-contact\"></div>';\n        const contactParts = [c.email, c.phone, c.hours].filter(Boolean);\n        const contactEl = form.querySelector('.shop-ai-support-contact');\n        if (contactParts.length) {\n          contactEl.textContent = 'Or reach us directly: ' + contactParts.join(' \u00B7 ');\n        } else {\n          contactEl.style.display = 'none';\n        }\n        form.dataset.category = category || 'other';\n        form.addEventListener('submit', (e) => { e.preventDefault(); this.submit(form); });\n        messagesContainer.appendChild(form);\n        ShopAIChat.UI.scrollToBottom(true);\n      },\n      submit: async function(form) {\n        const issue = form.querySelector('.shop-ai-support-issue').value.trim();\n        const name = form.querySelector('.shop-ai-lead-name').value.trim();\n        const phone = form.querySelector('.shop-ai-lead-phone').value.trim();\n        const email = form.querySelector('.shop-ai-lead-email').value.trim();\n        const status = form.querySelector('.shop-ai-lead-status');\n        const btn = form.querySelector('.shop-ai-lead-submit');\n        if (!issue) { status.textContent = 'Please describe your issue.'; return; }\n        if (!phone && !email) { status.textContent = 'Add a phone or email so we can reach you.'; return; }\n        btn.disabled = true; status.textContent = 'Sending\u2026';\n        try {\n          const res = await fetch(this.BACKEND, {\n            method: 'POST',\n            headers: { 'Content-Type': 'application/json' },\n            body: JSON.stringify({\n              conversation_id: sessionStorage.getItem('shopAiConversationId'),\n              name, phone, email, issue, category: form.dataset.category,\n              attribution: Object.assign({}, ShopAIChat.Attribution.get(), ShopAIChat.Cookies.collect()),\n            }),\n          });\n          const data = await res.json();\n          if (data.ok) {\n            form.querySelectorAll('input,textarea,button').forEach((el) => { el.disabled = true; });\n            status.textContent = '\u2713 Sent \u2014 our team will follow up shortly.';\n            ShopAIChat.Analytics.push('support_form_submitted');\n          } else { btn.disabled = false; status.textContent = data.error || 'Something went wrong. Try again.'; }\n        } catch (err) { btn.disabled = false; status.textContent = 'Network error \u2014 please try again.'; }\n      }\n    },\n\n    /**\n     * API communication and data handling\n     */\n    API: {\n      /**\n       * Stream a response from the API\n       * @param {string} userMessage - User's message text\n       * @param {string} conversationId - Conversation ID for context\n       * @param {HTMLElement} messagesContainer - The messages container\n       */\n      streamResponse: async function(userMessage, conversationId, messagesContainer) {\n        let currentMessageElement = null;\n\n        try {\n          const promptType = window.shopChatConfig?.promptType || \"standardAssistant\";\n          const requestBody = JSON.stringify({\n            message: userMessage,\n            conversation_id: conversationId,\n            prompt_type: promptType,\n            // The greeting bubble is local-only for speed. Flag the first reply so the server can\n            // add its fixed question as ephemeral model context (never as persisted chat text).\n            opening_question: !conversationId && !!messagesContainer.querySelector('.shop-ai-open-greeting'),\n            // Cookie-derived ids are merged fresh on every send (e.g. _fbc can\n            // appear after first touch); stored first-touch click ids win nothing\n            // here because keys don't overlap.\n            attribution: Object.assign({}, ShopAIChat.Attribution.get(), ShopAIChat.Cookies.collect()),\n            page: ShopAIChat.PageContext.get(),\n            funnel_id: ShopAIChat.Funnel.id(),\n            device_type: ShopAIChat.Funnel.deviceType(),\n            widget_version: document.querySelector('.shop-ai-chat-container')?.dataset.moVersion || null\n          });\n\n          try {\n            if (!sessionStorage.getItem('shopAiFirstMsgSent')) {\n              sessionStorage.setItem('shopAiFirstMsgSent', '1');\n              ShopAIChat.Analytics.push('chat_first_message');\n            }\n          } catch (e) { /* non-fatal */ }\n\n          const streamUrl = 'https://mo-shop-chat-agent-production.up.railway.app/chat';\n          const shopId = window.shopId;\n\n          const response = await fetch(streamUrl, {\n            method: 'POST',\n            headers: {\n              'Content-Type': 'application/json',\n              'Accept': 'text/event-stream',\n              'X-Shopify-Shop-Id': shopId\n            },\n            body: requestBody\n          });\n\n          const reader = response.body.getReader();\n          const decoder = new TextDecoder();\n          let buffer = '';\n\n          // Create initial message element\n          let messageElement = document.createElement('div');\n          messageElement.classList.add('shop-ai-message', 'assistant');\n          messageElement.textContent = '';\n          messageElement.dataset.rawText = '';\n          messagesContainer.appendChild(messageElement);\n          currentMessageElement = messageElement;\n\n          // Process the stream\n          while (true) {\n            const { value, done } = await reader.read();\n            if (done) break;\n\n            buffer += decoder.decode(value, { stream: true });\n            const lines = buffer.split('\\n\\n');\n            buffer = lines.pop() || '';\n\n            for (const line of lines) {\n              if (line.startsWith('data: ')) {\n                try {\n                  const data = JSON.parse(line.slice(6));\n                  this.handleStreamEvent(data, currentMessageElement, messagesContainer, userMessage,\n                    (newElement) => { currentMessageElement = newElement; });\n                } catch (e) {\n                  console.error('Error parsing event data:', e, line);\n                }\n              }\n            }\n          }\n          // Safety net: if the stream ended without an explicit end_turn/message_complete\n          // (e.g. the server dropped the connection mid-reply), still release the composer.\n          ShopAIChat.UI.setBusy(false);\n        } catch (error) {\n          console.error('Error in streaming:', error);\n          ShopAIChat.UI.removeTypingIndicator();\n          ShopAIChat.UI.setBusy(false);\n          ShopAIChat.Message.add(\"Sorry, I couldn't process your request. Please try again later.\",\n            'assistant', messagesContainer);\n        }\n      },\n\n      /**\n       * Handle stream events from the API\n       * @param {Object} data - Event data\n       * @param {HTMLElement} currentMessageElement - Current message element being updated\n       * @param {HTMLElement} messagesContainer - The messages container\n       * @param {string} userMessage - The original user message\n       * @param {Function} updateCurrentElement - Callback to update the current element reference\n       */\n      handleStreamEvent: function(data, currentMessageElement, messagesContainer, userMessage, updateCurrentElement) {\n        switch (data.type) {\n          case 'id':\n            if (data.conversation_id) {\n              sessionStorage.setItem('shopAiConversationId', data.conversation_id);\n            }\n            break;\n\n          case 'chunk': {\n            ShopAIChat.UI.removeTypingIndicator();\n            const ts = messagesContainer.querySelector('.shop-ai-tool-status');\n            if (ts) { if (ts._moSpin) clearInterval(ts._moSpin); ts.remove(); }\n            currentMessageElement.dataset.rawText += data.chunk;\n            ShopAIChat.Formatting.formatMessageContent(currentMessageElement);\n            ShopAIChat.UI.scrollToBottom();\n            break;\n          }\n\n          case 'message_complete':\n            ShopAIChat.UI.removeTypingIndicator();\n            ShopAIChat.UI.setBusy(false);\n            ShopAIChat.Formatting.formatMessageContent(currentMessageElement);\n            ShopAIChat.UI.scrollToBottom();\n            break;\n\n          case 'end_turn': {\n            ShopAIChat.UI.removeTypingIndicator();\n            ShopAIChat.UI.setBusy(false);\n            const tsEnd = messagesContainer.querySelector('.shop-ai-tool-status');\n            if (tsEnd) { if (tsEnd._moSpin) clearInterval(tsEnd._moSpin); tsEnd.remove(); }\n            messagesContainer.querySelectorAll('.shop-ai-message.assistant').forEach((el) => {\n              if ((el.dataset.rawText || '').trim() === '' && el.textContent.trim() === '') el.remove();\n            });\n            break;\n          }\n\n          case 'error':\n            console.error('Stream error:', data.error);\n            ShopAIChat.UI.removeTypingIndicator();\n            ShopAIChat.UI.setBusy(false);\n            currentMessageElement.textContent = \"Sorry, I couldn't process your request. Please try again later.\";\n            break;\n\n          case 'rate_limit_exceeded':\n            console.error('Rate limit exceeded:', data.error);\n            ShopAIChat.UI.removeTypingIndicator();\n            ShopAIChat.UI.setBusy(false);\n            currentMessageElement.textContent = \"Sorry, our servers are currently busy. Please try again later.\";\n            break;\n\n          case 'auth_required':\n            // Save the last user message for resuming after authentication\n            sessionStorage.setItem('shopAiLastMessage', userMessage || '');\n            break;\n\n          case 'quiz_gate_arm':\n            if (currentMessageElement) {\n              currentMessageElement.dataset.rawText = '';\n              currentMessageElement.textContent = '';\n            }\n            ShopAIChat.QuizFinaleGate.arm(data, messagesContainer);\n            break;\n\n          case 'quiz_gate_ready': {\n            ShopAIChat.UI.removeTypingIndicator();\n            const gateStatus = messagesContainer.querySelector('.shop-ai-tool-status');\n            if (gateStatus) gateStatus.remove();\n            if (currentMessageElement) {\n              currentMessageElement.dataset.rawText = '';\n              currentMessageElement.textContent = '';\n            }\n            ShopAIChat.QuizFinaleGate.show(data, messagesContainer);\n            break;\n          }\n\n          case 'quiz_gate_cancel':\n            ShopAIChat.QuizFinaleGate.cancel(messagesContainer);\n            break;\n\n          case 'product_results':\n            ShopAIChat.UI.displayProductResults(data.products);\n            ShopAIChat.Quiz.onProductResults();\n            break;\n\n          case 'tool_use': {\n            ShopAIChat.UI.removeTypingIndicator();\n            const prevStatus = messagesContainer.querySelector('.shop-ai-tool-status');\n            if (prevStatus) { if (prevStatus._moSpin) clearInterval(prevStatus._moSpin); prevStatus.remove(); }\n\n            // A model may narrate a tool call despite the prompt. Remove only a short,\n            // standalone progress sentence; specific advice or empathy remains untouched.\n            const prelude = String(currentMessageElement && currentMessageElement.dataset.rawText || '').trim();\n            if (prelude.length <= 220 && !prelude.includes('\\n') &&\n                /^(?:i(?:'ll| will| am going to)|i'm going to|let me|checking|searching|finding|pulling up|i need .+\\bto (?:answer|check|compare|confirm))\\b/i.test(prelude)) {\n              currentMessageElement.dataset.rawText = '';\n              currentMessageElement.textContent = '';\n            }\n\n            const toolName = (String(data.tool_use_message || '').match(/Calling tool:\\s*(\\S+)/) || [])[1] || '';\n            const toolStatuses = {\n              search_catalog: 'Matching beds to your needs...',\n              get_product_details: \"Checking this bed's details...\",\n              search_shop_policies_and_faqs: 'Checking store details...',\n              cart_create: 'Preparing your cart...',\n              cart_update: 'Updating your cart...'\n            };\n            const statusEl = document.createElement('div');\n            statusEl.className = 'shop-ai-tool-status';\n            statusEl.textContent = toolStatuses[toolName] || 'Checking the details...';\n            messagesContainer.appendChild(statusEl);\n            ShopAIChat.UI.scrollToBottom();\n            break;\n          }\n\n          case 'request_lead': {\n            ShopAIChat.UI.removeTypingIndicator();\n            ShopAIChat.Lead.show(data.hook || 'save_cart', messagesContainer);\n            break;\n          }\n\n          case 'request_support':\n            ShopAIChat.UI.removeTypingIndicator();\n            ShopAIChat.Support.show(data.category || 'other', data.contact || {}, messagesContainer);\n            break;\n\n          case 'quiz_question':\n            ShopAIChat.UI.removeTypingIndicator();\n            // Quiz turns should be chip-only. If the model leaked stray prose before the\n            // chip (the \"thinking block\" glitch), clear that in-progress bubble so the\n            // shopper sees just the question card. (end_turn prunes the now-empty bubble.)\n            if (currentMessageElement) {\n              currentMessageElement.dataset.rawText = '';\n              currentMessageElement.textContent = '';\n            }\n            ShopAIChat.Quiz.show(data, messagesContainer);\n            break;\n\n          case 'new_message':\n            ShopAIChat.Formatting.formatMessageContent(currentMessageElement);\n            ShopAIChat.UI.showTypingIndicator();\n\n            // Create new message element for the next response\n            const newMessageElement = document.createElement('div');\n            newMessageElement.classList.add('shop-ai-message', 'assistant');\n            newMessageElement.textContent = '';\n            newMessageElement.dataset.rawText = '';\n            messagesContainer.appendChild(newMessageElement);\n\n            // Update the current element reference\n            updateCurrentElement(newMessageElement);\n            break;\n\n          case 'content_block_complete':\n            ShopAIChat.UI.showTypingIndicator();\n            break;\n        }\n      },\n\n      /**\n       * Fetch chat history from the server\n       * @param {string} conversationId - Conversation ID\n       * @param {HTMLElement} messagesContainer - The messages container\n       */\n      fetchChatHistory: async function(conversationId, messagesContainer) {\n        try {\n          // Show a loading message\n          const loadingMessage = document.createElement('div');\n          loadingMessage.classList.add('shop-ai-message', 'assistant');\n          loadingMessage.textContent = \"Loading conversation history...\";\n          messagesContainer.appendChild(loadingMessage);\n\n          // Fetch history from the server\n          const historyUrl = `https://mo-shop-chat-agent-production.up.railway.app/chat?history=true&conversation_id=${encodeURIComponent(conversationId)}`;\n          console.log('Fetching history from:', historyUrl);\n\n          const response = await fetch(historyUrl, {\n            method: 'GET',\n            headers: {\n              'Accept': 'application/json',\n              'Content-Type': 'application/json'\n            },\n            mode: 'cors'\n          });\n\n          if (!response.ok) {\n            console.error('History fetch failed:', response.status, response.statusText);\n            throw new Error('Failed to fetch chat history: ' + response.status);\n          }\n\n          const data = await response.json();\n\n          // Remove loading message\n          messagesContainer.removeChild(loadingMessage);\n\n          // No messages: fresh thread \u2014 show the welcome hero (it carries the greeting).\n          if ((!data.messages || data.messages.length === 0) && !data.quiz_gate) {\n            try {\n              sessionStorage.removeItem('shopAiPendingLeadHook');\n              sessionStorage.removeItem('shopAiSupportActive');\n            } catch (e) { /* non-fatal */ }\n            ShopAIChat.Quiz.renderWelcomeChip(messagesContainer);\n            return;\n          }\n\n          // Add messages to the UI - filter out tool results\n          (data.messages || []).forEach(message => {\n            try {\n              const messageContents = JSON.parse(message.content);\n              for (const contentBlock of messageContents) {\n                if (contentBlock.type === 'text') {\n                  ShopAIChat.Message.add(contentBlock.text, message.role, messagesContainer);\n                }\n              }\n            } catch (e) {\n              ShopAIChat.Message.add(message.content, message.role, messagesContainer);\n            }\n          });\n\n          if (data.quiz_gate) {\n            if (data.quiz_gate.status === 'unlocked' && data.quiz_gate_result) {\n              ShopAIChat.QuizFinaleGate.unlock(data.quiz_gate_result, messagesContainer, {\n                gateId: data.quiz_gate.id,\n                leadId: data.quiz_gate.lead_id,\n                timelineEnabled: data.quiz_gate.timeline_enabled === true,\n                timelineSelected: data.quiz_gate.purchase_timeline || null,\n                restored: true\n              });\n            } else if (data.quiz_gate.status === 'locked') {\n              ShopAIChat.QuizFinaleGate.show({\n                id: data.quiz_gate.id,\n                profile: data.quiz_gate.profile,\n                match_count: data.quiz_gate.match_count,\n                primary_brand: data.quiz_gate.primary_brand,\n                timeline_enabled: data.quiz_gate.timeline_enabled\n              }, messagesContainer, { restored: true });\n            }\n          }\n\n          // An unsubmitted lead form must survive page reloads (every product-card click is\n          // a full reload while the transcript still says \"pop your details into the box\").\n          try {\n            const pendingHook = sessionStorage.getItem('shopAiPendingLeadHook');\n            if (pendingHook && !data.quiz_gate) ShopAIChat.Lead.show(pendingHook, messagesContainer, { restored: true });\n          } catch (e) { /* non-fatal */ }\n\n          // Scroll to bottom\n          ShopAIChat.UI.scrollToBottom(true);\n\n        } catch (error) {\n          console.error('Error fetching chat history:', error);\n\n          // Remove loading message if it exists\n          const loadingMessage = messagesContainer.querySelector('.shop-ai-message.assistant');\n          if (loadingMessage && loadingMessage.textContent === \"Loading conversation history...\") {\n            messagesContainer.removeChild(loadingMessage);\n          }\n\n          ShopAIChat.Message.add(\n            \"I couldn't reload this conversation just now. Your session is still saved, so please try again.\",\n            'assistant',\n            messagesContainer\n          );\n        }\n      }\n    },\n\n    /**\n     * Authentication-related functionality\n     */\n    Auth: {\n      /**\n       * Opens an authentication popup window\n       * @param {string|HTMLElement} authUrlOrElement - The auth URL or link element that was clicked\n       */\n      openAuthPopup: function(authUrlOrElement) {\n        let authUrl;\n        if (typeof authUrlOrElement === 'string') {\n          // If a string URL was passed directly\n          authUrl = authUrlOrElement;\n        } else {\n          // If an element was passed\n          authUrl = authUrlOrElement.getAttribute('data-auth-url');\n          if (!authUrl) {\n            console.error('No auth URL found in element');\n            return;\n          }\n        }\n\n        // Open the popup window centered in the screen\n        const width = 600;\n        const height = 700;\n        const left = (window.innerWidth - width) / 2 + window.screenX;\n        const top = (window.innerHeight - height) / 2 + window.screenY;\n\n        const popup = window.open(\n          authUrl,\n          'ShopifyAuth',\n          `width=${width},height=${height},left=${left},top=${top},resizable=yes,scrollbars=yes`\n        );\n\n        // Focus the popup window\n        if (popup) {\n          popup.focus();\n        } else {\n          // If popup was blocked, show a message\n          alert('Please allow popups for this site to authenticate with Shopify.');\n        }\n\n        // Start polling for token availability\n        const conversationId = sessionStorage.getItem('shopAiConversationId');\n        if (conversationId) {\n          const messagesContainer = document.querySelector('.shop-ai-chat-messages');\n\n          // Add a message to indicate authentication is in progress\n          ShopAIChat.Message.add(\"Authentication in progress. Please complete the process in the popup window.\",\n            'assistant', messagesContainer);\n\n          this.startTokenPolling(conversationId, messagesContainer);\n        }\n      },\n\n      /**\n       * Start polling for token availability\n       * @param {string} conversationId - Conversation ID\n       * @param {HTMLElement} messagesContainer - The messages container\n       */\n      startTokenPolling: function(conversationId, messagesContainer) {\n        if (!conversationId) return;\n\n        console.log('Starting token polling for conversation:', conversationId);\n        const pollingId = 'polling_' + Date.now();\n        sessionStorage.setItem('shopAiTokenPollingId', pollingId);\n\n        let attemptCount = 0;\n        const maxAttempts = 30;\n\n        const poll = async () => {\n          if (sessionStorage.getItem('shopAiTokenPollingId') !== pollingId) {\n            console.log('Another polling session has started, stopping this one');\n            return;\n          }\n\n          if (attemptCount >= maxAttempts) {\n            console.log('Max polling attempts reached, stopping');\n            return;\n          }\n\n          attemptCount++;\n\n          try {\n            const tokenUrl = 'https://mo-shop-chat-agent-production.up.railway.app/auth/token-status?conversation_id=' +\n              encodeURIComponent(conversationId);\n            const response = await fetch(tokenUrl);\n\n            if (!response.ok) {\n              throw new Error('Token status check failed: ' + response.status);\n            }\n\n            const data = await response.json();\n\n            if (data.status === 'authorized') {\n              console.log('Token available, resuming conversation');\n              const message = sessionStorage.getItem('shopAiLastMessage');\n\n              if (message) {\n                sessionStorage.removeItem('shopAiLastMessage');\n                setTimeout(() => {\n                  ShopAIChat.Message.add(\"Authorization successful! I'm now continuing with your request.\",\n                    'assistant', messagesContainer);\n                  ShopAIChat.API.streamResponse(message, conversationId, messagesContainer);\n                  ShopAIChat.UI.showTypingIndicator();\n                }, 500);\n              }\n\n              sessionStorage.removeItem('shopAiTokenPollingId');\n              return;\n            }\n\n            console.log('Token not available yet, polling again in 10s');\n            setTimeout(poll, 10000);\n          } catch (error) {\n            console.error('Error polling for token status:', error);\n            setTimeout(poll, 10000);\n          }\n        };\n\n        setTimeout(poll, 2000);\n      }\n    },\n\n    /**\n     * Product-related functionality\n     */\n    Product: {\n      /**\n       * Transient add-to-cart request feedback. The actual cart action needs a size\n       * and is completed through chat, so this state never claims success early.\n       * @param {HTMLButtonElement} button\n       */\n      flashAddToCart: function(button) {\n        try {\n          if (!button || button.dataset.moFlashing === '1') return;\n          const original = button.dataset.moLabel || button.textContent || 'Add to Cart';\n          button.dataset.moLabel = original;\n          button.dataset.moFlashing = '1';\n          if (button._moFlashT1) clearTimeout(button._moFlashT1);\n          if (button._moFlashT2) clearTimeout(button._moFlashT2);\n\n          button.classList.add('is-loading');\n          button.disabled = true;\n          button.textContent = 'Requesting...';\n\n          button._moFlashT1 = setTimeout(function () {\n            button.textContent = 'Checking size...';\n          }, 520);\n\n          button._moFlashT2 = setTimeout(function () {\n            button.classList.remove('is-loading');\n            button.textContent = button.dataset.moLabel || original;\n            button.disabled = false;\n            button.dataset.moFlashing = '0';\n          }, 3000);\n        } catch (e) { /* non-fatal */ }\n      },\n\n      /**\n       * Parse the bot's per-bed reply lines into {name, reason} pairs. The RESPONSE STYLE\n       * contract emits one dash line per bed: \"- **Brand Model** \u2014 $price \u2014 plain reason.\"\n       * We take the clause AFTER the price as the reason. Pure, defensive, never throws.\n       * @param {string} replyText - the assistant's raw reply markdown\n       * @returns {Array<{name:string, reason:string}>}\n       */\n      extractReasons: function(replyText) {\n        const out = [];\n        if (!replyText) return out;\n        // em-dash / en-dash / space-padded hyphen all act as the segment separator\n        const SEP = /\\s[\u2014\u2013-]\\s/;\n        String(replyText).split('\\n').forEach(function(raw) {\n          const line = String(raw).replace(/^\\s*[-*\u2022]\\s+/, '').trim();\n          const bold = line.match(/\\*\\*(.+?)\\*\\*/);\n          if (!bold) return;\n          const segs = line.split(SEP);\n          if (segs.length < 3) return; // need name \u2014 price \u2014 reason\n          let reason = segs.slice(2).join(' \u2014 ')\n            .replace(/\\*\\*/g, '').replace(/\\s+/g, ' ').replace(/[\\s.;,]+$/, '').trim();\n          if (reason.length < 3) return;\n          if (reason.length > 90) reason = reason.slice(0, 88).replace(/\\s+\\S*$/, '') + '\u2026';\n          out.push({ name: bold[1].replace(/\\*/g, '').trim(), reason: reason });\n        });\n        return out;\n      },\n\n      /**\n       * Match a product title to its reason from extractReasons(), mirroring the server's\n       * name-matching (selectRecommendedProducts): a bolded name and the title match when\n       * one contains the other or they share a 2-word run. Returns the reason or null.\n       * @param {string} title\n       * @param {Array<{name:string, reason:string}>} reasons\n       * @returns {string|null}\n       */\n      reasonFor: function(title, reasons) {\n        if (!title || !Array.isArray(reasons) || !reasons.length) return null;\n        const norm = function(s) {\n          return String(s || '').toLowerCase().replace(/[^a-z0-9 ]+/g, ' ').replace(/\\s+/g, ' ').trim();\n        };\n        const t = norm(title);\n        if (!t) return null;\n        for (let i = 0; i < reasons.length; i++) {\n          const n = norm(reasons[i].name);\n          if (!n) continue;\n          if (t.indexOf(n) !== -1 || n.indexOf(t) !== -1) return reasons[i].reason;\n          const w = n.split(' ').filter(Boolean);\n          for (let j = 0; j < w.length - 1; j++) {\n            if (t.indexOf(w[j] + ' ' + w[j + 1]) !== -1) return reasons[i].reason;\n          }\n        }\n        return null;\n      },\n\n      /**\n       * Create a product card element\n       * @param {Object} product - Product data\n       * @param {string} [reason] - optional one-line \"why it fits\" note from the bot's reply\n       * @returns {HTMLElement} Product card element\n       */\n      createCard: function(product, reason) {\n        const card = document.createElement('div');\n        card.classList.add('shop-ai-product-card');\n\n        // Whole-card click \u2192 PDP (bigger tap target than the title link alone). Early-return\n        // for the add-to-cart button and real links so their own handlers keep working.\n        if (product.url) {\n          card.classList.add('clickable');\n          card.addEventListener('click', function(e) {\n            if (e.target.closest('.shop-ai-add-to-cart') || e.target.closest('a')) return;\n            window.open(ShopAIChat.Formatting.safeUrl(product.url), '_blank', 'noopener');\n          });\n        }\n\n        // Create image container\n        const imageContainer = document.createElement('div');\n        imageContainer.classList.add('shop-ai-product-image');\n\n        // Add product image (only http(s) URLs are allowed; otherwise placeholder)\n        const PLACEHOLDER_IMG = 'https://cdn.shopify.com/s/files/1/0533/2089/files/placeholder-images-image_large.png';\n        const image = document.createElement('img');\n        image.src = /^https?:\\/\\//i.test(product.image_url || '') ? product.image_url : PLACEHOLDER_IMG;\n        image.alt = product.title || 'Product';\n        image.onerror = function() {\n          this.src = PLACEHOLDER_IMG;\n        };\n        imageContainer.appendChild(image);\n        card.appendChild(imageContainer);\n\n        // Add product info\n        const info = document.createElement('div');\n        info.classList.add('shop-ai-product-info');\n\n        // Add product title\n        const title = document.createElement('h3');\n        title.classList.add('shop-ai-product-title');\n        title.textContent = product.title;\n\n        // If product has a URL, make the title a link\n        if (product.url) {\n          const titleLink = document.createElement('a');\n          titleLink.href = ShopAIChat.Formatting.safeUrl(product.url);\n          titleLink.target = '_blank';\n          titleLink.rel = 'noopener noreferrer';\n          titleLink.textContent = product.title;\n          title.textContent = '';\n          title.appendChild(titleLink);\n        }\n\n        info.appendChild(title);\n\n        // \"Why it fits you\" rationale, lifted verbatim from the bot's reply (textContent\n        // only \u2014 grounding-safe). Rendered only when a reason was matched for this bed.\n        if (reason) {\n          const reasonEl = document.createElement('p');\n          reasonEl.classList.add('shop-ai-product-reason');\n          reasonEl.textContent = reason;\n          info.appendChild(reasonEl);\n        }\n\n        // Add product price. \"from $X\" gets split typography (small \"from\", big price);\n        // the combined textContent stays byte-identical to product.price \u2014 grounding doctrine.\n        const price = document.createElement('p');\n        price.classList.add('shop-ai-product-price');\n        const priceStr = product.price == null ? '' : String(product.price);\n        if (/^from /.test(priceStr)) {\n          const from = document.createElement('span');\n          from.className = 'shop-ai-price-from';\n          from.textContent = 'from ';\n          const val = document.createElement('span');\n          val.className = 'shop-ai-price-value';\n          val.textContent = priceStr.slice(5);\n          price.appendChild(from);\n          price.appendChild(val);\n        } else {\n          price.textContent = priceStr;\n        }\n        info.appendChild(price);\n\n        // Add add-to-cart button\n        const button = document.createElement('button');\n        button.classList.add('shop-ai-add-to-cart');\n        button.textContent = 'Add to Cart';\n        button.dataset.productId = product.id;\n\n        // Add click handler for the button\n        button.addEventListener('click', function() {\n          // Add-to-cart routes through the chat send button. While a reply is streaming the\n          // send button is disabled (busy state), so a click would no-op \u2014 bail before\n          // showing request feedback without an actual action.\n          const sendButton = document.querySelector('.shop-ai-chat-send');\n          if (sendButton && sendButton.disabled) return;\n\n          // Show an honest pending state while the existing chat flow resolves size\n          // and performs the real cart action.\n          ShopAIChat.Product.flashAddToCart(button);\n\n          // Send message to add this product to cart\n          const input = document.querySelector('.shop-ai-chat-input input');\n          if (input) {\n            input.value = `Add ${product.title} to my cart`;\n            // Trigger a click on the send button\n            if (sendButton) {\n              sendButton.click();\n            }\n          }\n        });\n\n        info.appendChild(button);\n        card.appendChild(info);\n\n        return card;\n      }\n    },\n\n    /**\n     * Initialize the chat application\n     */\n    init: function() {\n      // Capture first-touch ad attribution (gclid/fbclid/utm) before anything else\n      ShopAIChat.Attribution.capture();\n      ShopAIChat.Funnel.init();\n\n      // Initialize UI\n      const container = document.querySelector('.shop-ai-chat-container');\n      if (!container) return;\n\n      this.UI.init(container);\n      this.Nudge.init(container);\n\n      // Check for existing conversation\n      const conversationId = sessionStorage.getItem('shopAiConversationId');\n\n      if (conversationId) {\n        // Fetch conversation history\n        this.API.fetchChatHistory(conversationId, this.UI.elements.messagesContainer);\n      } else {\n        // No previous conversation: the welcome hero carries the greeting\n        try {\n          sessionStorage.removeItem('shopAiPendingLeadHook');\n          sessionStorage.removeItem('shopAiSupportActive');\n        } catch (e) { /* non-fatal */ }\n        this.Quiz.renderWelcomeChip(this.UI.elements.messagesContainer);\n      }\n    }\n  };\n\n  // Initialize the application when DOM is ready\n  if (document.readyState === 'loading') {\n    document.addEventListener('DOMContentLoaded', function() {\n      ShopAIChat.init();\n    });\n  } else {\n    ShopAIChat.init();\n  }\n})();\n"],
  "mappings": "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",
  "names": ["toolUseElement", "form", "card"]
}
