@gengage/assistant-fe 0.3.4 → 0.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assistant-fe.css +1 -1
- package/dist/chat/components/AITopPicks.d.ts.map +1 -1
- package/dist/chat/index.d.ts +12 -0
- package/dist/chat/index.d.ts.map +1 -1
- package/dist/chat/types.d.ts +10 -3
- package/dist/chat/types.d.ts.map +1 -1
- package/dist/chat-CCSPwPmr.cjs +13 -0
- package/dist/chat-CCSPwPmr.cjs.map +1 -0
- package/dist/{chat-CeH1XU7z.js → chat-tG_XXqmm.js} +167 -91
- package/dist/chat-tG_XXqmm.js.map +1 -0
- package/dist/chat.cjs +1 -1
- package/dist/chat.iife.js +7 -7
- package/dist/chat.iife.js.map +1 -1
- package/dist/chat.js +1 -1
- package/dist/common/overlay.d.ts +2 -0
- package/dist/common/overlay.d.ts.map +1 -1
- package/dist/common/protocol-adapter.d.ts.map +1 -1
- package/dist/common/types.d.ts +1 -1
- package/dist/{common-CNFtu3-R.cjs → common-BQVcjB7s.cjs} +2 -2
- package/dist/{common-CNFtu3-R.cjs.map → common-BQVcjB7s.cjs.map} +1 -1
- package/dist/{common-IeimSfGM.js → common-BtEjhpH-.js} +3 -3
- package/dist/{common-IeimSfGM.js.map → common-BtEjhpH-.js.map} +1 -1
- package/dist/common.cjs +1 -1
- package/dist/common.js +4 -4
- package/dist/index.cjs +1 -1
- package/dist/index.js +6 -6
- package/dist/native-webview-CEgjvGWF.cjs +2 -0
- package/dist/native-webview-CEgjvGWF.cjs.map +1 -0
- package/dist/{native-webview-COsr8mln.js → native-webview-QLwuablV.js} +6 -6
- package/dist/native-webview-QLwuablV.js.map +1 -0
- package/dist/native.cjs +1 -1
- package/dist/native.iife.js +3 -3
- package/dist/native.iife.js.map +1 -1
- package/dist/native.js +1 -1
- package/dist/{qna-DaTZs616.js → qna-C7XoZSSw.js} +2 -2
- package/dist/{qna-DaTZs616.js.map → qna-C7XoZSSw.js.map} +1 -1
- package/dist/{qna-D_6abbH-.cjs → qna-cn9DHuBF.cjs} +2 -2
- package/dist/{qna-D_6abbH-.cjs.map → qna-cn9DHuBF.cjs.map} +1 -1
- package/dist/qna.cjs +1 -1
- package/dist/qna.css +1 -1
- package/dist/qna.iife.js +1 -1
- package/dist/qna.iife.js.map +1 -1
- package/dist/qna.js +1 -1
- package/dist/{schemas-hP7gXjYb.js → schemas-Dcfz5vXW.js} +7 -18
- package/dist/schemas-Dcfz5vXW.js.map +1 -0
- package/dist/{schemas-CV-kUJ0g.cjs → schemas-DeE-RHGa.cjs} +2 -2
- package/dist/schemas-DeE-RHGa.cjs.map +1 -0
- package/dist/simbut.css +1 -1
- package/dist/{simrel-BnyA9pb4.cjs → simrel-CVKjhHee.cjs} +2 -2
- package/dist/{simrel-BnyA9pb4.cjs.map → simrel-CVKjhHee.cjs.map} +1 -1
- package/dist/{simrel-9XPKS2My.js → simrel-R3mVoFQG.js} +2 -2
- package/dist/{simrel-9XPKS2My.js.map → simrel-R3mVoFQG.js.map} +1 -1
- package/dist/simrel.cjs +1 -1
- package/dist/simrel.css +1 -1
- package/dist/simrel.iife.js +1 -1
- package/dist/simrel.iife.js.map +1 -1
- package/dist/simrel.js +1 -1
- package/package.json +1 -1
- package/dist/chat-B_9Q8Q2Y.cjs +0 -13
- package/dist/chat-B_9Q8Q2Y.cjs.map +0 -1
- package/dist/chat-CeH1XU7z.js.map +0 -1
- package/dist/native-webview-C27jSZmz.cjs +0 -2
- package/dist/native-webview-C27jSZmz.cjs.map +0 -1
- package/dist/native-webview-COsr8mln.js.map +0 -1
- package/dist/schemas-CV-kUJ0g.cjs.map +0 -1
- package/dist/schemas-hP7gXjYb.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"simrel-9XPKS2My.js","names":[],"sources":["../src/simrel/api.ts","../src/simrel/components/ProductCard.ts","../src/simrel/components/ProductGrid.ts","../src/simrel/components/GroupTabs.ts","../src/simrel/components/renderUISpec.ts","../src/simrel/locales/tr.ts","../src/simrel/locales/en.ts","../src/simrel/locales/index.ts","../src/simrel/catalog.ts","../src/simrel/index.ts"],"sourcesContent":["import { buildChatEndpointUrl } from '../common/api-paths.js';\nimport { consumeStream } from '../common/streaming.js';\nimport {\n adaptBackendEvent,\n normalizeSimilarProductsResponse,\n normalizeProductGroupingsResponse,\n} from '../common/protocol-adapter.js';\nimport type { NormalizedProduct } from '../common/protocol-adapter.js';\nimport type { StreamEvent, UIElement } from '../common/types.js';\nimport type { ChatTransportConfig } from '../common/api-paths.js';\n\nexport interface SimilarProductsRequest {\n account_id: string;\n session_id: string;\n correlation_id: string;\n sku: string;\n domain?: string;\n limit?: number;\n output_language?: string;\n}\n\nexport interface ProductGroupingsRequest {\n account_id: string;\n session_id: string;\n correlation_id: string;\n skus: string[];\n output_language?: string;\n}\n\nexport interface ProductGroup {\n name: string;\n highlight?: string;\n products: NormalizedProduct[];\n}\n\nfunction extractProductCardsFromSpec(elements: Record<string, UIElement>): NormalizedProduct[] {\n const products: NormalizedProduct[] = [];\n for (const el of Object.values(elements)) {\n if (el.type === 'ProductCard' && el.props) {\n const product = (el.props['product'] ?? el.props) as Record<string, unknown>;\n if (typeof product['sku'] === 'string' && typeof product['name'] === 'string') {\n products.push(product as unknown as NormalizedProduct);\n }\n }\n }\n return products;\n}\n\nfunction isNDJSONResponse(response: Response): boolean {\n const ct = response.headers.get('Content-Type') ?? '';\n return ct.includes('application/x-ndjson') || ct.includes('text/event-stream');\n}\n\nasync function collectProductsFromStream(response: Response, signal?: AbortSignal): Promise<NormalizedProduct[]> {\n const products: NormalizedProduct[] = [];\n const opts: import('../common/streaming.js').StreamOptions = {\n onEvent: (event: StreamEvent) => {\n const normalized = adaptBackendEvent(event as unknown as Record<string, unknown>);\n if (!normalized || normalized.type !== 'ui_spec') return;\n\n products.push(...extractProductCardsFromSpec(normalized.spec.elements));\n },\n };\n if (signal !== undefined) opts.signal = signal;\n await consumeStream(response, opts);\n return products;\n}\n\nexport async function fetchSimilarProducts(\n request: SimilarProductsRequest,\n transport: ChatTransportConfig,\n signal?: AbortSignal,\n): Promise<NormalizedProduct[]> {\n const url = buildChatEndpointUrl('similar_products', transport);\n\n const fetchInit: RequestInit = {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(request),\n };\n if (signal !== undefined) fetchInit.signal = signal;\n const response = await fetch(url, fetchInit);\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n if (isNDJSONResponse(response)) {\n return collectProductsFromStream(response, signal);\n }\n\n const text = await response.text();\n if (!text) throw new Error('Empty response body from similar_products endpoint');\n try {\n return normalizeSimilarProductsResponse(JSON.parse(text));\n } catch {\n throw new Error(`Invalid JSON from similar_products endpoint`);\n }\n}\n\nasync function collectGroupingsFromStream(response: Response, signal?: AbortSignal): Promise<ProductGroup[]> {\n const groups: ProductGroup[] = [];\n let currentGroup: ProductGroup | null = null;\n\n const opts: import('../common/streaming.js').StreamOptions = {\n onEvent: (event: StreamEvent) => {\n const normalized = adaptBackendEvent(event as unknown as Record<string, unknown>);\n if (!normalized) return;\n\n if (normalized.type === 'metadata' && normalized.meta) {\n const name = normalized.meta['group_name'];\n if (typeof name === 'string') {\n currentGroup = { name, products: [] };\n const highlight = normalized.meta['highlight'];\n if (typeof highlight === 'string') currentGroup.highlight = highlight;\n groups.push(currentGroup);\n }\n }\n\n if (normalized.type === 'ui_spec' && currentGroup) {\n currentGroup.products.push(...extractProductCardsFromSpec(normalized.spec.elements));\n }\n },\n };\n if (signal !== undefined) opts.signal = signal;\n await consumeStream(response, opts);\n\n return groups;\n}\n\nexport async function fetchProductGroupings(\n request: ProductGroupingsRequest,\n transport: ChatTransportConfig,\n signal?: AbortSignal,\n): Promise<ProductGroup[]> {\n const url = buildChatEndpointUrl('product_groupings', transport);\n\n const fetchInit: RequestInit = {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(request),\n };\n if (signal !== undefined) fetchInit.signal = signal;\n const response = await fetch(url, fetchInit);\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n if (isNDJSONResponse(response)) {\n return collectGroupingsFromStream(response, signal);\n }\n\n const text = await response.text();\n if (!text) return [];\n try {\n return normalizeProductGroupingsResponse(JSON.parse(text));\n } catch {\n throw new Error(`Invalid JSON from product_groupings endpoint`);\n }\n}\n","import type { NormalizedProduct } from '../../common/protocol-adapter.js';\nimport type { SimRelI18n } from '../types.js';\nimport type { PriceFormatConfig } from '../../common/price-formatter.js';\nimport { formatPrice } from '../../common/price-formatter.js';\nimport { sanitizeHtml, isSafeImageUrl } from '../../common/safe-html.js';\nimport { clampDiscount, addImageErrorHandler, createStarRatingElement } from '../../common/product-utils.js';\n\nexport interface ProductCardOptions {\n product: NormalizedProduct;\n index: number;\n discountType?: 'strike-through' | 'badge';\n onClick: (product: NormalizedProduct) => void;\n onAddToCart: (params: { sku: string; quantity: number; cartCode: string }) => void;\n renderCard?: (product: NormalizedProduct, index: number) => string;\n renderCardElement?: (product: NormalizedProduct, index: number) => HTMLElement | null;\n i18n?: SimRelI18n;\n pricing?: PriceFormatConfig;\n}\n\nexport function renderProductCard(options: ProductCardOptions): HTMLElement {\n const { product, index, discountType, onClick, onAddToCart, renderCard } = options;\n const i18n = options.i18n;\n const pricing = options.pricing;\n\n // Custom card element renderer (returns full HTMLElement, takes precedence)\n if (options.renderCardElement) {\n const el = options.renderCardElement(product, index);\n if (el) return el;\n }\n\n // Custom card renderer (XSS warning: raw HTML injection)\n if (renderCard) {\n const wrapper = document.createElement('div');\n wrapper.className =\n 'gengage-simrel-card gengage-simrel-card--custom gds-card gds-product-card gds-card-interactive';\n wrapper.dataset['gengagePart'] = 'simrel-product-card';\n // Sanitize renderCard output to prevent XSS from user-provided renderers.\n wrapper.innerHTML = sanitizeHtml(renderCard(product, index));\n wrapper.addEventListener('click', (e) => {\n if ((e.target as HTMLElement).closest('.gengage-simrel-atc')) return;\n if ((e.target as HTMLElement).closest('.gengage-chat-product-card-atc')) return;\n onClick(product);\n });\n return wrapper;\n }\n\n const card = document.createElement('article');\n // Intentional class coupling: reuse chat product-card classes so SimRel and chat stay visually identical.\n card.className = 'gengage-simrel-card gengage-chat-product-card gds-card gds-product-card gds-card-interactive';\n card.dataset['gengagePart'] = 'simrel-product-card';\n if (product.inStock === false) {\n card.classList.add('gengage-simrel-card--out-of-stock');\n }\n card.setAttribute('role', 'listitem');\n card.dataset['sku'] = product.sku;\n\n // Image\n const imgWrapper = document.createElement('div');\n imgWrapper.className = 'gengage-simrel-card-image gengage-chat-product-card-img-wrapper';\n imgWrapper.dataset['gengagePart'] = 'simrel-product-card-image';\n if (product.imageUrl && isSafeImageUrl(product.imageUrl)) {\n const img = document.createElement('img');\n img.className = 'gengage-chat-product-card-img';\n img.src = product.imageUrl;\n img.alt = product.name;\n img.loading = 'lazy';\n addImageErrorHandler(img);\n imgWrapper.appendChild(img);\n }\n\n // Discount badge\n if (discountType === 'badge' && product.discountPercent && product.discountPercent > 0) {\n const badge = document.createElement('span');\n badge.className = 'gengage-simrel-badge gengage-chat-product-card-discount-badge';\n badge.textContent = `%${clampDiscount(product.discountPercent)}`;\n imgWrapper.appendChild(badge);\n }\n\n card.appendChild(imgWrapper);\n\n // Info section\n const info = document.createElement('div');\n info.className = 'gengage-simrel-card-info gengage-chat-product-card-body';\n info.dataset['gengagePart'] = 'simrel-product-card-body';\n\n // Brand\n if (product.brand) {\n const brandEl = document.createElement('div');\n brandEl.className = 'gengage-simrel-card-brand gengage-chat-product-card-brand';\n brandEl.textContent = product.brand;\n info.appendChild(brandEl);\n }\n\n // Name\n const nameEl = document.createElement('div');\n nameEl.className = 'gengage-simrel-card-name gengage-chat-product-card-name';\n nameEl.textContent = product.name;\n nameEl.title = product.name;\n info.appendChild(nameEl);\n\n // Rating\n if (product.rating != null && product.rating > 0) {\n const ratingEl = document.createElement('div');\n ratingEl.className = 'gengage-simrel-card-rating gengage-chat-product-card-rating';\n ratingEl.appendChild(createStarRatingElement(product.rating));\n if (product.reviewCount != null) {\n const count = document.createElement('span');\n count.className = 'gengage-simrel-card-review-count gengage-chat-product-card-review-count';\n count.textContent = ` (${product.reviewCount})`;\n ratingEl.appendChild(count);\n }\n info.appendChild(ratingEl);\n }\n\n // Price\n const priceContainer = document.createElement('div');\n priceContainer.className = 'gengage-simrel-card-price gengage-chat-product-card-price';\n\n if (product.originalPrice && product.originalPrice !== product.price) {\n if (discountType === 'strike-through' || !discountType) {\n const original = document.createElement('span');\n original.className = 'gengage-simrel-card-price-original gengage-chat-product-card-original-price';\n original.textContent = formatPrice(product.originalPrice, pricing);\n priceContainer.appendChild(original);\n }\n }\n\n if (product.price && parseFloat(product.price) > 0) {\n const current = document.createElement('span');\n current.className = 'gengage-simrel-card-price-current gengage-chat-product-card-price-current';\n current.textContent = formatPrice(product.price, pricing);\n priceContainer.appendChild(current);\n }\n\n info.appendChild(priceContainer);\n card.appendChild(info);\n\n // Keep SimRel cards aligned with chat product-card structure.\n const cta = document.createElement('button');\n cta.className = 'gengage-simrel-card-cta gengage-chat-product-card-cta gds-btn gds-btn-secondary';\n cta.type = 'button';\n cta.dataset['gengagePart'] = 'simrel-product-card-cta';\n cta.textContent = i18n?.ctaLabel ?? 'View';\n cta.addEventListener('click', (e) => {\n e.preventDefault();\n e.stopPropagation();\n onClick(product);\n });\n card.appendChild(cta);\n\n // Add to cart stepper or out-of-stock indicator\n if (product.inStock === false) {\n const oos = document.createElement('div');\n oos.className = 'gengage-simrel-card-oos';\n oos.textContent = i18n?.outOfStockLabel ?? 'Out of Stock';\n card.appendChild(oos);\n } else if (product.cartCode) {\n const cartBtn = document.createElement('button');\n cartBtn.className = 'gengage-simrel-atc gengage-simrel-atc-button gds-btn gds-btn-secondary';\n cartBtn.type = 'button';\n cartBtn.textContent = i18n?.addToCartButton ?? 'Add to Cart';\n cartBtn.addEventListener('click', (e) => {\n e.preventDefault();\n e.stopPropagation();\n onAddToCart({ sku: product.sku, quantity: 1, cartCode: product.cartCode! });\n });\n card.appendChild(cartBtn);\n }\n\n // Card click → navigate\n card.addEventListener('click', (e) => {\n if ((e.target as HTMLElement).closest('.gengage-simrel-atc')) return;\n if ((e.target as HTMLElement).closest('.gengage-chat-product-card-atc')) return;\n if ((e.target as HTMLElement).closest('.gengage-chat-product-card-cta')) return;\n onClick(product);\n });\n\n return card;\n}\n","import type { NormalizedProduct } from '../../common/protocol-adapter.js';\nimport type { SimRelI18n } from '../types.js';\nimport { renderProductCard } from './ProductCard.js';\nimport type { ProductCardOptions } from './ProductCard.js';\n\nexport interface ProductGridOptions {\n products: NormalizedProduct[];\n columns?: number;\n discountType?: 'strike-through' | 'badge';\n onClick: (product: NormalizedProduct) => void;\n onAddToCart: (params: { sku: string; quantity: number; cartCode: string }) => void;\n renderCard?: (product: NormalizedProduct, index: number) => string;\n renderCardElement?: (product: NormalizedProduct, index: number) => HTMLElement | null;\n i18n?: SimRelI18n;\n}\n\nexport function renderProductGrid(options: ProductGridOptions): HTMLElement {\n const grid = document.createElement('div');\n grid.className = 'gengage-simrel-grid';\n grid.setAttribute('role', 'list');\n grid.setAttribute('aria-label', options.i18n?.similarProductsAriaLabel ?? 'Similar products');\n\n if (options.columns) {\n grid.style.setProperty('--gengage-simrel-columns', String(options.columns));\n }\n\n for (let i = 0; i < options.products.length; i++) {\n const product = options.products[i]!;\n const cardOpts: ProductCardOptions = {\n product,\n index: i,\n onClick: options.onClick,\n onAddToCart: options.onAddToCart,\n };\n if (options.i18n !== undefined) cardOpts.i18n = options.i18n;\n if (options.discountType !== undefined) cardOpts.discountType = options.discountType;\n if (options.renderCard !== undefined) cardOpts.renderCard = options.renderCard;\n if (options.renderCardElement !== undefined) cardOpts.renderCardElement = options.renderCardElement;\n const card = renderProductCard(cardOpts);\n grid.appendChild(card);\n }\n\n if (options.products.length === 0) {\n grid.style.display = 'none';\n grid.dataset['empty'] = 'true';\n }\n\n return grid;\n}\n","import type { NormalizedProduct } from '../../common/protocol-adapter.js';\nimport type { ProductGroup } from '../api.js';\nimport type { SimRelI18n } from '../types.js';\nimport { renderProductGrid } from './ProductGrid.js';\n\nexport interface GroupTabsOptions {\n groups: ProductGroup[];\n discountType?: 'strike-through' | 'badge';\n onClick: (product: NormalizedProduct) => void;\n onAddToCart: (params: { sku: string; quantity: number; cartCode: string }) => void;\n renderCard?: (product: NormalizedProduct, index: number) => string;\n renderCardElement?: (product: NormalizedProduct, index: number) => HTMLElement | null;\n i18n?: SimRelI18n;\n /** ProductGrid `columns` — masaüstü satır başına kart sayısı. */\n columns?: number;\n}\n\nlet _groupTabsInstanceCounter = 0;\n\nexport function renderGroupTabs(options: GroupTabsOptions): HTMLElement {\n const instanceId = _groupTabsInstanceCounter++;\n const container = document.createElement('div');\n container.className = 'gengage-simrel-groups';\n container.dataset['gengagePart'] = 'simrel-groups';\n\n if (options.groups.length === 0) {\n container.style.display = 'none';\n container.dataset['empty'] = 'true';\n return container;\n }\n\n // Tab bar — WAI-ARIA tablist pattern\n const tabBar = document.createElement('div');\n tabBar.className = 'gengage-simrel-tabs gds-toolbar';\n tabBar.dataset['gengagePart'] = 'simrel-tab-bar';\n tabBar.setAttribute('role', 'tablist');\n\n const tabs: HTMLButtonElement[] = [];\n const panels: HTMLElement[] = [];\n\n const buildGridOptions = (group: ProductGroup): import('./ProductGrid.js').ProductGridOptions => {\n const gridOpts: import('./ProductGrid.js').ProductGridOptions = {\n products: group.products,\n onClick: options.onClick,\n onAddToCart: options.onAddToCart,\n };\n if (options.i18n !== undefined) gridOpts.i18n = options.i18n;\n if (options.discountType !== undefined) gridOpts.discountType = options.discountType;\n if (options.renderCard !== undefined) gridOpts.renderCard = options.renderCard;\n if (options.renderCardElement !== undefined) gridOpts.renderCardElement = options.renderCardElement;\n if (options.columns !== undefined) gridOpts.columns = options.columns;\n return gridOpts;\n };\n\n const activateTab = (index: number): void => {\n for (let j = 0; j < tabs.length; j++) {\n const isActive = j === index;\n tabs[j]!.classList.toggle('gengage-simrel-tab--active', isActive);\n tabs[j]!.setAttribute('aria-selected', String(isActive));\n tabs[j]!.tabIndex = isActive ? 0 : -1;\n }\n\n // Lazy-render the grid content for the active panel\n const group = options.groups[index]!;\n const panel = panels[index]!;\n panel.innerHTML = '';\n const grid = renderProductGrid(buildGridOptions(group));\n panel.appendChild(grid);\n\n // Show only the active panel and manage tabindex for keyboard access\n for (let j = 0; j < panels.length; j++) {\n const isActive = j === index;\n panels[j]!.style.display = isActive ? '' : 'none';\n panels[j]!.tabIndex = isActive ? 0 : -1;\n }\n };\n\n for (let i = 0; i < options.groups.length; i++) {\n const group = options.groups[i]!;\n const tabId = `gengage-simrel-tab-${instanceId}-${i}`;\n const panelId = `gengage-simrel-panel-${instanceId}-${i}`;\n\n // Tab button\n const tab = document.createElement('button');\n tab.className = 'gengage-simrel-tab gds-tab';\n tab.type = 'button';\n tab.dataset['gengagePart'] = 'simrel-tab';\n tab.id = tabId;\n tab.textContent = group.name;\n tab.setAttribute('role', 'tab');\n tab.setAttribute('aria-controls', panelId);\n tab.setAttribute('aria-selected', String(i === 0));\n tab.tabIndex = i === 0 ? 0 : -1;\n if (i === 0) tab.classList.add('gengage-simrel-tab--active');\n\n tab.addEventListener('click', () => activateTab(i));\n tab.addEventListener('keydown', (e: KeyboardEvent) => {\n let next = -1;\n if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {\n next = (i + 1) % options.groups.length;\n } else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {\n next = (i - 1 + options.groups.length) % options.groups.length;\n } else if (e.key === 'Home') {\n next = 0;\n } else if (e.key === 'End') {\n next = options.groups.length - 1;\n }\n if (next >= 0) {\n e.preventDefault();\n activateTab(next);\n tabs[next]!.focus();\n }\n });\n\n tabs.push(tab);\n tabBar.appendChild(tab);\n\n // Tab panel\n const panel = document.createElement('div');\n panel.className = 'gengage-simrel-tab-panel';\n panel.dataset['gengagePart'] = 'simrel-tab-panel';\n panel.id = panelId;\n panel.setAttribute('role', 'tabpanel');\n panel.setAttribute('aria-labelledby', tabId);\n panel.tabIndex = i === 0 ? 0 : -1;\n if (i !== 0) panel.style.display = 'none';\n\n panels.push(panel);\n }\n\n // Scroll arrow buttons for mouse-only users\n const scrollLeftBtn = document.createElement('button');\n scrollLeftBtn.type = 'button';\n scrollLeftBtn.className = 'gengage-simrel-tabs-arrow gengage-simrel-tabs-arrow--left';\n scrollLeftBtn.setAttribute('aria-label', options.i18n?.scrollTabsLeft ?? 'Scroll tabs left');\n scrollLeftBtn.innerHTML =\n '<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"15 18 9 12 15 6\"/></svg>';\n\n const scrollRightBtn = document.createElement('button');\n scrollRightBtn.type = 'button';\n scrollRightBtn.className = 'gengage-simrel-tabs-arrow gengage-simrel-tabs-arrow--right';\n scrollRightBtn.setAttribute('aria-label', options.i18n?.scrollTabsRight ?? 'Scroll tabs right');\n scrollRightBtn.innerHTML =\n '<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"9 18 15 12 9 6\"/></svg>';\n\n const SCROLL_AMOUNT = 200;\n\n scrollLeftBtn.addEventListener('click', () => {\n tabBar.scrollBy({ left: -SCROLL_AMOUNT, behavior: 'smooth' });\n });\n scrollRightBtn.addEventListener('click', () => {\n tabBar.scrollBy({ left: SCROLL_AMOUNT, behavior: 'smooth' });\n });\n\n const updateArrows = () => {\n const atLeft = tabBar.scrollLeft <= 4;\n const atRight = tabBar.scrollLeft + tabBar.clientWidth >= tabBar.scrollWidth - 4;\n scrollLeftBtn.style.display = atLeft ? 'none' : '';\n scrollRightBtn.style.display = atRight ? 'none' : '';\n tabBar.classList.toggle('gengage-simrel-tabs--peek-right', !atRight);\n tabBar.classList.toggle('gengage-simrel-tabs--peek-left', !atLeft);\n };\n\n tabBar.addEventListener('scroll', updateArrows, { passive: true });\n\n // Use ResizeObserver to update arrows when container size changes (not available in jsdom/tests)\n if (typeof ResizeObserver !== 'undefined') {\n const ro = new ResizeObserver(updateArrows);\n ro.observe(tabBar);\n }\n\n // Initial state — hide arrows until we know overflow state\n scrollLeftBtn.style.display = 'none';\n scrollRightBtn.style.display = 'none';\n // Check after initial render\n requestAnimationFrame(updateArrows);\n\n const tabBarWrapper = document.createElement('div');\n tabBarWrapper.className = 'gengage-simrel-tabs-wrapper';\n tabBarWrapper.appendChild(scrollLeftBtn);\n tabBarWrapper.appendChild(tabBar);\n tabBarWrapper.appendChild(scrollRightBtn);\n container.appendChild(tabBarWrapper);\n\n // Render the initial (first) tab content\n const firstPanel = panels[0]!;\n const firstGrid = renderProductGrid(buildGridOptions(options.groups[0]!));\n firstPanel.appendChild(firstGrid);\n\n for (const panel of panels) container.appendChild(panel);\n\n return container;\n}\n","import { renderUISpecWithRegistry } from '../../common/renderer/index.js';\nimport type { UISpecDomRegistry, UISpecDomUnknownRenderer } from '../../common/renderer/index.js';\nimport type { UISpec, ActionPayload } from '../../common/types.js';\nimport type { SimRelUISpecRenderContext, SimilarProduct } from '../types.js';\nimport type { ProductGroup } from '../api.js';\nimport { renderProductCard } from './ProductCard.js';\nimport { renderGroupTabs } from './GroupTabs.js';\n\nexport type SimRelUISpecRegistry = UISpecDomRegistry<SimRelUISpecRenderContext>;\n\nfunction toSimRelProduct(raw: unknown): SimilarProduct | null {\n if (!raw || typeof raw !== 'object') return null;\n const obj = raw as Record<string, unknown>;\n if (typeof obj['sku'] !== 'string' || typeof obj['name'] !== 'string' || typeof obj['url'] !== 'string') {\n return null;\n }\n\n const result: SimilarProduct = {\n sku: obj['sku'],\n name: obj['name'],\n url: obj['url'],\n };\n\n const imageUrl = obj['imageUrl'];\n if (typeof imageUrl === 'string') result.imageUrl = imageUrl;\n const price = obj['price'];\n if (typeof price === 'string') result.price = price;\n const originalPrice = obj['originalPrice'];\n if (typeof originalPrice === 'string') result.originalPrice = originalPrice;\n const discountPercent = obj['discountPercent'];\n if (typeof discountPercent === 'number') result.discountPercent = discountPercent;\n const brand = obj['brand'];\n if (typeof brand === 'string') result.brand = brand;\n const rating = obj['rating'];\n if (typeof rating === 'number') result.rating = rating;\n const reviewCount = obj['reviewCount'];\n if (typeof reviewCount === 'number') result.reviewCount = reviewCount;\n const cartCode = obj['cartCode'];\n if (typeof cartCode === 'string') result.cartCode = cartCode;\n const inStock = obj['inStock'];\n if (typeof inStock === 'boolean') result.inStock = inStock;\n const extras = obj['extras'];\n if (extras != null && typeof extras === 'object') result.extras = extras as Record<string, unknown>;\n\n return result;\n}\n\nfunction toActionPayload(raw: unknown): ActionPayload | null {\n if (!raw || typeof raw !== 'object') return null;\n const obj = raw as Record<string, unknown>;\n const title = obj['title'];\n const type = obj['type'];\n if (typeof title !== 'string' || typeof type !== 'string') return null;\n const action: ActionPayload = { title, type };\n if (obj['payload'] !== undefined) action.payload = obj['payload'];\n return action;\n}\n\nconst DEFAULT_SIMREL_UI_SPEC_REGISTRY: SimRelUISpecRegistry = {\n ProductGrid: ({ element, renderElement, context }) => {\n const grid = document.createElement('div');\n grid.className = 'gengage-simrel-grid';\n grid.setAttribute('role', 'list');\n\n const propCols = element.props?.['columns'];\n let columns: number | undefined;\n if (typeof propCols === 'number' && Number.isFinite(propCols) && propCols > 0) {\n columns = Math.floor(propCols);\n } else if (\n typeof context.gridColumns === 'number' &&\n Number.isFinite(context.gridColumns) &&\n context.gridColumns > 0\n ) {\n columns = Math.floor(context.gridColumns);\n }\n if (columns !== undefined) {\n grid.style.setProperty('--gengage-simrel-columns', String(columns));\n }\n\n for (const childId of element.children ?? []) {\n const rendered = renderElement(childId);\n if (rendered) grid.appendChild(rendered);\n }\n\n if (grid.children.length === 0) {\n const empty = document.createElement('div');\n empty.className = 'gengage-simrel-empty';\n empty.textContent = context.i18n.emptyStateMessage;\n grid.appendChild(empty);\n }\n\n return grid;\n },\n\n ProductCard: ({ element, context }) => {\n const productRaw = (element.props?.['product'] ?? element.props) as unknown;\n const product = toSimRelProduct(productRaw);\n if (!product) return null;\n\n const indexRaw = element.props?.['index'];\n const index = typeof indexRaw === 'number' && Number.isFinite(indexRaw) ? indexRaw : 0;\n const discountTypeRaw = element.props?.['discountType'];\n const discountType =\n discountTypeRaw === 'strike-through' || discountTypeRaw === 'badge' ? discountTypeRaw : context.discountType;\n\n const options: import('./ProductCard.js').ProductCardOptions = {\n product,\n index,\n onClick: context.onClick,\n onAddToCart: context.onAddToCart,\n i18n: context.i18n,\n };\n if (discountType !== undefined) options.discountType = discountType;\n if (context.renderCard !== undefined) options.renderCard = context.renderCard;\n if (context.renderCardElement !== undefined) options.renderCardElement = context.renderCardElement;\n if (context.pricing !== undefined) options.pricing = context.pricing;\n return renderProductCard(options);\n },\n\n GroupTabs: ({ element, context }) => {\n const groupsRaw = element.props?.['groups'];\n if (!Array.isArray(groupsRaw)) return null;\n const groups: ProductGroup[] = [];\n\n for (const entry of groupsRaw) {\n if (!entry || typeof entry !== 'object') continue;\n const obj = entry as Record<string, unknown>;\n if (typeof obj['name'] !== 'string') continue;\n\n const products: SimilarProduct[] = [];\n if (Array.isArray(obj['products'])) {\n for (const rawProduct of obj['products']) {\n const normalized = toSimRelProduct(rawProduct);\n if (normalized) products.push(normalized);\n }\n }\n\n const group: ProductGroup = {\n name: obj['name'],\n products,\n };\n if (typeof obj['highlight'] === 'string') group.highlight = obj['highlight'];\n groups.push(group);\n }\n\n const options: import('./GroupTabs.js').GroupTabsOptions = {\n groups,\n onClick: context.onClick,\n onAddToCart: context.onAddToCart,\n i18n: context.i18n,\n };\n const tabGridCols = element.props?.['columns'];\n if (typeof tabGridCols === 'number' && Number.isFinite(tabGridCols) && tabGridCols > 0) {\n options.columns = Math.floor(tabGridCols);\n } else if (\n typeof context.gridColumns === 'number' &&\n Number.isFinite(context.gridColumns) &&\n context.gridColumns > 0\n ) {\n options.columns = Math.floor(context.gridColumns);\n }\n if (context.discountType !== undefined) options.discountType = context.discountType;\n if (context.renderCard !== undefined) options.renderCard = context.renderCard;\n if (context.renderCardElement !== undefined) options.renderCardElement = context.renderCardElement;\n return renderGroupTabs(options);\n },\n\n EmptyState: ({ element, context }) => {\n const empty = document.createElement('div');\n empty.className = 'gengage-simrel-empty';\n const message = element.props?.['message'];\n empty.textContent = typeof message === 'string' ? message : context.i18n.emptyStateMessage;\n return empty;\n },\n\n AddToCartButton: ({ element, context }) => {\n const sku = element.props?.['sku'];\n const cartCode = element.props?.['cartCode'];\n if (typeof sku !== 'string' || typeof cartCode !== 'string') return null;\n\n const button = document.createElement('button');\n button.className = 'gengage-simrel-atc gengage-chat-product-card-cta';\n button.type = 'button';\n const label = element.props?.['label'];\n button.textContent = typeof label === 'string' ? label : context.i18n.addToCartButton;\n button.addEventListener('click', (e) => {\n e.preventDefault();\n e.stopPropagation();\n context.onAddToCart({ sku, quantity: 1, cartCode });\n });\n return button;\n },\n\n QuickActions: ({ element, context }) => {\n const wrapper = document.createElement('div');\n wrapper.className = 'gengage-simrel-quick-actions';\n const actions = element.props?.['actions'];\n if (!Array.isArray(actions) || !context.onAction) return wrapper;\n\n for (const raw of actions) {\n if (!raw || typeof raw !== 'object') continue;\n const actionObj = raw as Record<string, unknown>;\n const label = actionObj['label'];\n const action = toActionPayload(actionObj['action']);\n if (typeof label !== 'string' || !action) continue;\n\n const button = document.createElement('button');\n button.className = 'gengage-simrel-quick-action';\n button.type = 'button';\n button.textContent = label;\n button.addEventListener('click', (e) => {\n e.preventDefault();\n e.stopPropagation();\n context.onAction?.(action);\n });\n wrapper.appendChild(button);\n }\n return wrapper;\n },\n};\n\nexport const defaultSimRelUnknownUISpecRenderer: UISpecDomUnknownRenderer<SimRelUISpecRenderContext> = ({\n element,\n renderElement,\n}) => {\n if (import.meta.env?.DEV) {\n console.warn(`[gengage:simrel] Unknown ui_spec component type: ${element.type}`);\n }\n if (!element.children || element.children.length === 0) {\n return null;\n }\n const wrapper = document.createElement('div');\n for (const childId of element.children) {\n const rendered = renderElement(childId);\n if (rendered) wrapper.appendChild(rendered);\n }\n return wrapper;\n};\n\nexport function createDefaultSimRelUISpecRegistry(): SimRelUISpecRegistry {\n return { ...DEFAULT_SIMREL_UI_SPEC_REGISTRY };\n}\n\nexport function renderSimRelUISpec(\n spec: UISpec,\n context: SimRelUISpecRenderContext,\n registry = DEFAULT_SIMREL_UI_SPEC_REGISTRY,\n unknownRenderer: UISpecDomUnknownRenderer<SimRelUISpecRenderContext> = defaultSimRelUnknownUISpecRenderer,\n): HTMLElement {\n return renderUISpecWithRegistry({\n spec,\n context,\n registry,\n containerClassName: 'gengage-simrel-uispec',\n unknownRenderer,\n });\n}\n","import type { SimRelI18n } from '../types.js';\n\nexport const SIMREL_I18N_TR: SimRelI18n = {\n similarProductsAriaLabel: 'Benzer ürünler',\n emptyStateMessage: 'Benzer ürün bulunamadı.',\n addToCartButton: 'Sepete Ekle',\n ctaLabel: 'İncele',\n outOfStockLabel: 'Stokta Yok',\n decreaseLabel: 'Azalt',\n increaseLabel: 'Artır',\n errorLoadingMessage: 'Benzer ürünler yüklenemedi.',\n retryButtonText: 'Tekrar dene',\n priceSuffix: ' TL',\n scrollTabsLeft: 'Sola kaydır',\n scrollTabsRight: 'Sağa kaydır',\n};\n","import type { SimRelI18n } from '../types.js';\n\nexport const SIMREL_I18N_EN: SimRelI18n = {\n similarProductsAriaLabel: 'Similar products',\n emptyStateMessage: 'No similar products found.',\n addToCartButton: 'Add to cart',\n ctaLabel: 'View',\n outOfStockLabel: 'Out of Stock',\n decreaseLabel: 'Decrease',\n increaseLabel: 'Increase',\n errorLoadingMessage: 'Could not load similar products.',\n retryButtonText: 'Try again',\n priceSuffix: '',\n scrollTabsLeft: 'Scroll tabs left',\n scrollTabsRight: 'Scroll tabs right',\n};\n","import type { SimRelI18n } from '../types.js';\nimport { SIMREL_I18N_TR } from './tr.js';\nimport { SIMREL_I18N_EN } from './en.js';\n\nfunction normalizeLocale(locale?: string): string {\n if (!locale) return 'tr';\n return locale.toLowerCase().split('-')[0] ?? 'tr';\n}\n\nexport function resolveSimRelLocale(locale?: string): SimRelI18n {\n switch (normalizeLocale(locale)) {\n case 'en':\n return SIMREL_I18N_EN;\n default:\n return SIMREL_I18N_TR;\n }\n}\n\nexport { SIMREL_I18N_TR, SIMREL_I18N_EN };\n","/**\n * Similar Products (SimRel) widget — json-render catalog definition.\n *\n * Backend endpoints:\n * POST /chat/similar_products — primary product list\n * POST /chat/product_groupings — grouped/tabbed view\n *\n * The backend streams NDJSON events. `ui_spec` events reference\n * component names defined below. Implementations live in ./registry.\n */\n\nimport { z } from 'zod';\n\nconst SimilarProductSchema = z.object({\n sku: z.string(),\n name: z.string(),\n imageUrl: z.string().url().optional(),\n price: z.string().optional(),\n originalPrice: z.string().optional(),\n discountPercent: z.number().optional(),\n url: z.string().url(),\n brand: z.string().optional(),\n rating: z.number().min(0).max(5).optional(),\n reviewCount: z.number().int().nonnegative().optional(),\n});\n\nexport const ProductGridSchema = z.object({\n layout: z.enum(['grid', 'carousel']).optional(),\n columns: z.number().int().positive().optional(),\n});\n\nexport const ProductCardSchema = z.object({\n product: SimilarProductSchema,\n index: z.number().int().nonnegative(),\n discountType: z.enum(['strike-through', 'badge']).optional(),\n});\n\nexport const AddToCartButtonSchema = z.object({\n sku: z.string(),\n label: z.string().optional(),\n cartCode: z.string(),\n});\n\nexport const QuickActionsSchema = z.object({\n actions: z.array(\n z.object({\n label: z.string(),\n action: z.object({\n title: z.string(),\n type: z.string(),\n payload: z.unknown().optional(),\n }),\n }),\n ),\n});\n\nexport const EmptyStateSchema = z.object({\n message: z.string().optional(),\n});\n\nexport const simRelCatalog = {\n components: {\n ProductGrid: {\n schema: ProductGridSchema,\n description: 'Outer grid or carousel container for similar products.',\n },\n ProductCard: {\n schema: ProductCardSchema,\n description: 'A single product card with image, title, price, and actions.',\n },\n AddToCartButton: {\n schema: AddToCartButtonSchema,\n description: 'Add-to-cart CTA rendered inside or below a product card.',\n },\n QuickActions: {\n schema: QuickActionsSchema,\n description: 'A row of quick-action buttons below product info.',\n },\n EmptyState: {\n schema: EmptyStateSchema,\n description: 'Empty state shown when no similar products are available.',\n },\n },\n} as const;\n\nexport type SimRelCatalog = typeof simRelCatalog;\nexport type SimRelComponentName = keyof SimRelCatalog['components'];\n","/**\n * Similar Products (SimRel) widget — public entry point.\n *\n * Fetches and renders similar / related products for the current SKU.\n * Backend: POST /chat/similar_products + /chat/product_groupings\n */\n\nimport type { PageContext, UISpec, UIElement } from '../common/types.js';\nimport type { NormalizedProduct } from '../common/protocol-adapter.js';\nimport type { ChatTransportConfig } from '../common/api-paths.js';\nimport type { UISpecRenderHelpers } from '../common/renderer/index.js';\nimport { mergeUISpecRegistry } from '../common/renderer/index.js';\nimport { BaseWidget } from '../common/widget-base.js';\nimport { dispatch } from '../common/events.js';\nimport { trackConnectionWarningRequest } from '../common/connection-warning.js';\nimport { getGlobalErrorMessage } from '../common/global-error-toast.js';\nimport {\n streamStartEvent,\n streamDoneEvent,\n streamErrorEvent,\n basketAddEvent,\n widgetHistorySnapshotEvent,\n} from '../common/analytics-events.js';\nimport { fetchSimilarProducts, fetchProductGroupings } from './api.js';\nimport {\n createDefaultSimRelUISpecRegistry,\n defaultSimRelUnknownUISpecRenderer,\n renderSimRelUISpec,\n} from './components/renderUISpec.js';\nimport type { SimRelWidgetConfig, SimilarProduct, SimRelI18n, SimRelUISpecRenderContext } from './types.js';\nimport { SIMREL_I18N_TR, resolveSimRelLocale } from './locales/index.js';\nimport * as ga from '../common/ga-datalayer.js';\n\nimport './components/simrel.css';\n\n/**\n * Similar / related products widget for product pages.\n * Fetches AI-powered product recommendations and renders them as a scrollable grid.\n *\n * @example\n * ```ts\n * import { GengageSimRel, bootstrapSession } from '@gengage/assistant-fe';\n *\n * const simrel = new GengageSimRel();\n * await simrel.init({\n * accountId: 'mystore',\n * middlewareUrl: 'https://chat.gengage.ai',\n * sku: '12345',\n * mountTarget: '#similar-products',\n * session: { sessionId: bootstrapSession() },\n * onAddToCart: ({ sku, quantity }) => cart.add(sku, quantity),\n * });\n * ```\n */\nexport class GengageSimRel extends BaseWidget<SimRelWidgetConfig> {\n private _abortController: AbortController | null = null;\n private _contentEl: HTMLElement | null = null;\n private _lastSku: string | undefined;\n /** Number of products returned from the last successful fetch. Used to allow\n * retry when the same SKU previously produced an empty result set. */\n private _lastResultCount = -1;\n private _i18n: SimRelI18n = SIMREL_I18N_TR;\n\n protected async onInit(config: SimRelWidgetConfig): Promise<void> {\n this._i18n = this._resolveI18n(config);\n\n this._contentEl = document.createElement('div');\n this._contentEl.className = 'gengage-simrel-container';\n this._contentEl.dataset['gengagePart'] = 'simrel-container';\n const gridCols = this._clampGridColumns(config.gridColumns);\n if (gridCols !== undefined) {\n this._contentEl.style.setProperty('--gengage-simrel-columns', String(gridCols));\n }\n this.root.appendChild(this._contentEl);\n\n this._lastSku = config.sku;\n await this._fetchAndRender(config.sku);\n ga.trackInit('simrel');\n }\n\n protected onUpdate(context: Partial<PageContext>): void {\n const newSku = context.sku;\n // Allow retry for the same SKU when the previous fetch returned no products\n // (e.g. transient backend empty response). _lastResultCount === 0 means the\n // last fetch succeeded but produced nothing — worth retrying on the next\n // page interaction.\n if (!newSku || (newSku === this._lastSku && this._lastResultCount !== 0)) return;\n this._lastSku = newSku;\n void this._fetchAndRender(newSku);\n }\n\n protected onShow(): void {\n if (this._contentEl) {\n this._contentEl.style.opacity = '0';\n this._contentEl.style.transition = 'opacity 0.3s ease-in';\n requestAnimationFrame(() => {\n if (this._contentEl) this._contentEl.style.opacity = '1';\n });\n }\n }\n\n protected onHide(): void {\n // Preserve fetched products for re-show\n }\n\n protected onDestroy(): void {\n this._abort();\n if (this._contentEl) {\n this._contentEl.remove();\n this._contentEl = null;\n }\n }\n\n // ---------------------------------------------------------------------------\n // Internal event dispatchers\n // ---------------------------------------------------------------------------\n\n _handleProductClick(product: NormalizedProduct): void {\n const simRelProduct: SimilarProduct = {\n sku: product.sku,\n name: product.name,\n url: product.url,\n };\n if (product.imageUrl !== undefined) simRelProduct.imageUrl = product.imageUrl;\n if (product.price !== undefined) simRelProduct.price = product.price;\n if (product.originalPrice !== undefined) simRelProduct.originalPrice = product.originalPrice;\n if (product.discountPercent !== undefined) simRelProduct.discountPercent = product.discountPercent;\n if (product.brand !== undefined) simRelProduct.brand = product.brand;\n if (product.rating !== undefined) simRelProduct.rating = product.rating;\n if (product.reviewCount !== undefined) simRelProduct.reviewCount = product.reviewCount;\n if (product.cartCode !== undefined) simRelProduct.cartCode = product.cartCode;\n if (product.inStock !== undefined) simRelProduct.inStock = product.inStock;\n\n if (this.config.onProductClick?.(simRelProduct) === false) return;\n\n ga.trackProductDetail(product.sku, product.name);\n const sessionId = this.config.session?.sessionId ?? null;\n dispatch('gengage:similar:product-click', {\n sku: product.sku,\n url: product.url,\n sessionId,\n });\n\n this.config.onProductNavigate?.(product.url, product.sku, sessionId);\n }\n\n _handleAddToCart(params: { sku: string; quantity: number; cartCode: string }): void {\n ga.trackCartAdd(params.sku, params.quantity);\n this.config.onAddToCart?.(params);\n dispatch('gengage:similar:add-to-cart', params);\n\n this.track(\n basketAddEvent(this.analyticsContext(), {\n attribution_source: 'simrel',\n attribution_action_id: crypto.randomUUID(),\n cart_value: 0, // Host page should enrich via event listener\n currency: this.config.pricing?.currencyCode ?? 'TRY',\n line_items: params.quantity,\n sku: params.sku,\n }),\n );\n }\n\n // ---------------------------------------------------------------------------\n // Private\n // ---------------------------------------------------------------------------\n\n private _abort(): void {\n this._abortController?.abort();\n this._abortController = null;\n }\n\n private _isSuperseded(signal: AbortSignal): boolean {\n return this._abortController?.signal !== signal;\n }\n\n private async _fetchAndRender(sku: string): Promise<void> {\n this._abort();\n this._abortController = new AbortController();\n // Capture signal reference at invocation time to avoid race conditions:\n // if onUpdate fires between awaits, `this._abortController` gets swapped\n // but `signal` still refers to this invocation's controller.\n const signal = this._abortController.signal;\n // Auto-abort after 10s to prevent indefinite loading state\n const timeoutId = setTimeout(() => this._abortController?.abort(), 10_000);\n signal.addEventListener('abort', () => clearTimeout(timeoutId));\n\n if (!this._contentEl) return;\n this._contentEl.innerHTML = '';\n // Reset visibility in case a previous error set display:none\n this._contentEl.style.display = '';\n\n // Show loading spinner\n const loading = document.createElement('div');\n loading.className = 'gengage-simrel-loading';\n loading.dataset['gengagePart'] = 'simrel-loading';\n const spinner = document.createElement('div');\n spinner.className = 'gengage-simrel-spinner';\n spinner.dataset['gengagePart'] = 'simrel-loading-spinner';\n loading.appendChild(spinner);\n this._contentEl.appendChild(loading);\n\n const transport: ChatTransportConfig = {\n middlewareUrl: this.config.middlewareUrl,\n };\n\n const requestId = crypto.randomUUID();\n const fetchStart = Date.now();\n const releaseConnectionWarning = trackConnectionWarningRequest({\n source: 'simrel',\n locale: this.config.locale,\n });\n\n this.track(\n streamStartEvent(this.analyticsContext(), {\n endpoint: 'similar_products',\n request_id: requestId,\n widget: 'simrel',\n }),\n );\n\n try {\n // Fetch similar products\n const simReq: import('./api.js').SimilarProductsRequest = {\n account_id: this.config.accountId,\n session_id: this.config.session?.sessionId ?? '',\n correlation_id: this.config.session?.sessionId ?? '',\n sku,\n };\n if (this.config.domain !== undefined) simReq.domain = this.config.domain;\n const products = await fetchSimilarProducts(simReq, transport, signal);\n // Record result count so onUpdate can retry if last fetch was empty\n this._lastResultCount = products.length;\n\n if (!this._contentEl) return;\n this._contentEl.innerHTML = '';\n\n // Try to fetch product groupings for tabbed view\n if (products.length > 0) {\n try {\n const skus = products.map((p) => p.sku);\n const productsBySku = new Map(products.map((product) => [product.sku, product] as const));\n const groups = await fetchProductGroupings(\n {\n account_id: this.config.accountId,\n session_id: this.config.session?.sessionId ?? '',\n correlation_id: this.config.session?.sessionId ?? '',\n skus,\n },\n transport,\n signal,\n );\n const usableGroups = groups\n .map((group) => ({\n ...group,\n products: group.products\n .map((groupProduct) => {\n const fallbackProduct = productsBySku.get(groupProduct.sku);\n return {\n ...fallbackProduct,\n ...groupProduct,\n };\n })\n .filter(\n (groupProduct) =>\n typeof groupProduct.sku === 'string' &&\n typeof groupProduct.name === 'string' &&\n typeof groupProduct.url === 'string',\n ),\n }))\n .filter((group) => group.products.length > 0);\n\n if (usableGroups.length > 0 && this._contentEl) {\n const groupsSpec = this._buildGroupsSpec(usableGroups);\n const renderedGroups = this._renderUISpec(groupsSpec);\n this._contentEl.appendChild(renderedGroups);\n\n ga.trackShow('simrel');\n this.track(\n streamDoneEvent(this.analyticsContext(), {\n request_id: requestId,\n latency_ms: Date.now() - fetchStart,\n chunk_count: usableGroups.reduce((n, g) => n + g.products.length, 0),\n widget: 'simrel',\n }),\n );\n this.track(\n widgetHistorySnapshotEvent(this.analyticsContext(), {\n message_count: usableGroups.reduce((n, g) => n + g.products.length, 0),\n history_ref: requestId,\n redaction_level: 'none',\n widget: 'simrel',\n }),\n );\n return;\n }\n } catch {\n // Product groupings is optional; fall through to flat grid\n }\n }\n\n // Only bail out when a newer request superseded this one. If the optional\n // grouping call timed out, still render the flat-grid fallback from the\n // already-fetched similar products.\n if (this._isSuperseded(signal)) return;\n\n // Flat grid (no groupings or groupings failed)\n if (this._contentEl) {\n const gridSpec = this._buildProductsSpec(products);\n const renderedGrid = this._renderUISpec(gridSpec);\n this._contentEl.appendChild(renderedGrid);\n }\n\n if (products.length > 0) {\n ga.trackShow('simrel');\n }\n\n this.track(\n streamDoneEvent(this.analyticsContext(), {\n request_id: requestId,\n latency_ms: Date.now() - fetchStart,\n chunk_count: products.length,\n widget: 'simrel',\n }),\n );\n\n this.track(\n widgetHistorySnapshotEvent(this.analyticsContext(), {\n message_count: products.length,\n history_ref: requestId,\n redaction_level: 'none',\n widget: 'simrel',\n }),\n );\n } catch (err) {\n if (err instanceof DOMException && err.name === 'AbortError' && this._isSuperseded(signal)) return;\n\n dispatch('gengage:global:error', {\n source: 'simrel',\n code: 'FETCH_ERROR',\n message: getGlobalErrorMessage(this.config.locale, err),\n });\n\n this.track(\n streamErrorEvent(this.analyticsContext(), {\n request_id: requestId,\n error_code: 'FETCH_ERROR',\n error_message: err instanceof Error ? err.message : String(err),\n widget: 'simrel',\n }),\n );\n\n if (import.meta.env?.DEV) {\n console.error('[gengage:simrel] Failed to fetch similar products:', err);\n }\n // Show inline error with retry instead of hiding silently\n if (this._contentEl) {\n this._contentEl.innerHTML = '';\n const errorEl = document.createElement('div');\n errorEl.className = 'gengage-simrel-error';\n const msgEl = document.createElement('span');\n msgEl.textContent = this._i18n.errorLoadingMessage;\n errorEl.appendChild(msgEl);\n const retryBtn = document.createElement('button');\n retryBtn.className = 'gengage-simrel-retry';\n retryBtn.textContent = this._i18n.retryButtonText;\n retryBtn.addEventListener('click', () => {\n void this._fetchAndRender(this.config.sku);\n });\n errorEl.appendChild(retryBtn);\n this._contentEl.appendChild(errorEl);\n }\n } finally {\n releaseConnectionWarning();\n }\n }\n\n private _clampGridColumns(n: number | undefined): number | undefined {\n if (n === undefined || typeof n !== 'number' || !Number.isFinite(n)) return undefined;\n return Math.max(1, Math.min(12, Math.floor(n)));\n }\n\n private _resolveI18n(config: SimRelWidgetConfig): SimRelI18n {\n const base = resolveSimRelLocale(config.locale);\n return { ...base, ...config.i18n };\n }\n\n private _resolveUISpecRegistry() {\n const baseRegistry = createDefaultSimRelUISpecRegistry();\n return mergeUISpecRegistry(baseRegistry, this.config.renderer?.registry);\n }\n\n private _buildRenderContext(): SimRelUISpecRenderContext {\n const renderCard = this.config.renderCard as ((product: SimilarProduct, index: number) => string) | undefined;\n const context: SimRelUISpecRenderContext = {\n onClick: (product) => this._handleProductClick(product as unknown as NormalizedProduct),\n onAddToCart: (params) => this._handleAddToCart(params),\n i18n: this._i18n,\n };\n if (this.config.discountType !== undefined) context.discountType = this.config.discountType;\n if (renderCard !== undefined) context.renderCard = renderCard;\n if (this.config.renderCardElement !== undefined) {\n context.renderCardElement = this.config.renderCardElement as (\n product: SimilarProduct,\n index: number,\n ) => HTMLElement | null;\n }\n if (this.config.pricing !== undefined) context.pricing = this.config.pricing;\n const gridCols = this._clampGridColumns(this.config.gridColumns);\n if (gridCols !== undefined) context.gridColumns = gridCols;\n return context;\n }\n\n private _renderUISpec(spec: UISpec): HTMLElement {\n const registry = this._resolveUISpecRegistry();\n const context = this._buildRenderContext();\n const unknownRenderer = this.config.renderer?.unknownRenderer ?? defaultSimRelUnknownUISpecRenderer;\n const defaultRender = (inputSpec: UISpec, inputContext: SimRelUISpecRenderContext) =>\n renderSimRelUISpec(inputSpec, inputContext, registry, unknownRenderer);\n\n const override = this.config.renderer?.renderUISpec;\n if (!override) return defaultRender(spec, context);\n\n const helpers: UISpecRenderHelpers<SimRelUISpecRenderContext> = {\n registry,\n unknownRenderer,\n defaultRender,\n };\n return override(spec, context, helpers);\n }\n\n private _buildProductsSpec(products: NormalizedProduct[]): UISpec {\n const elements: Record<string, UIElement> = {};\n const children: string[] = [];\n for (let i = 0; i < products.length; i++) {\n const product = products[i]!;\n const id = `product-${i}`;\n children.push(id);\n elements[id] = {\n type: 'ProductCard',\n props: {\n product,\n index: i,\n discountType: this.config.discountType,\n },\n };\n }\n const gridProps: Record<string, unknown> = { layout: 'grid' };\n const gridCols = this._clampGridColumns(this.config.gridColumns);\n if (gridCols !== undefined) gridProps['columns'] = gridCols;\n\n elements['root'] = {\n type: 'ProductGrid',\n props: gridProps,\n children,\n };\n return {\n root: 'root',\n elements,\n };\n }\n\n private _buildGroupsSpec(\n groups: Array<{\n name: string;\n highlight?: string;\n products: NormalizedProduct[];\n }>,\n ): UISpec {\n return {\n root: 'root',\n elements: {\n root: {\n type: 'GroupTabs',\n props: { groups },\n },\n },\n };\n }\n}\n\nexport function createSimRelWidget(): GengageSimRel {\n return new GengageSimRel();\n}\n\nexport type {\n SimRelWidgetConfig,\n SimilarProduct,\n SimRelUIComponents,\n SimRelI18n,\n SimRelUISpecRenderContext,\n SimRelRendererConfig,\n} from './types.js';\nexport {\n renderSimRelUISpec,\n createDefaultSimRelUISpecRegistry,\n defaultSimRelUnknownUISpecRenderer,\n} from './components/renderUISpec.js';\nexport type { SimRelUISpecRegistry } from './components/renderUISpec.js';\nexport { simRelCatalog } from './catalog.js';\nexport type { SimRelCatalog, SimRelComponentName } from './catalog.js';\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"simrel-R3mVoFQG.js","names":[],"sources":["../src/simrel/api.ts","../src/simrel/components/ProductCard.ts","../src/simrel/components/ProductGrid.ts","../src/simrel/components/GroupTabs.ts","../src/simrel/components/renderUISpec.ts","../src/simrel/locales/tr.ts","../src/simrel/locales/en.ts","../src/simrel/locales/index.ts","../src/simrel/catalog.ts","../src/simrel/index.ts"],"sourcesContent":["import { buildChatEndpointUrl } from '../common/api-paths.js';\nimport { consumeStream } from '../common/streaming.js';\nimport {\n adaptBackendEvent,\n normalizeSimilarProductsResponse,\n normalizeProductGroupingsResponse,\n} from '../common/protocol-adapter.js';\nimport type { NormalizedProduct } from '../common/protocol-adapter.js';\nimport type { StreamEvent, UIElement } from '../common/types.js';\nimport type { ChatTransportConfig } from '../common/api-paths.js';\n\nexport interface SimilarProductsRequest {\n account_id: string;\n session_id: string;\n correlation_id: string;\n sku: string;\n domain?: string;\n limit?: number;\n output_language?: string;\n}\n\nexport interface ProductGroupingsRequest {\n account_id: string;\n session_id: string;\n correlation_id: string;\n skus: string[];\n output_language?: string;\n}\n\nexport interface ProductGroup {\n name: string;\n highlight?: string;\n products: NormalizedProduct[];\n}\n\nfunction extractProductCardsFromSpec(elements: Record<string, UIElement>): NormalizedProduct[] {\n const products: NormalizedProduct[] = [];\n for (const el of Object.values(elements)) {\n if (el.type === 'ProductCard' && el.props) {\n const product = (el.props['product'] ?? el.props) as Record<string, unknown>;\n if (typeof product['sku'] === 'string' && typeof product['name'] === 'string') {\n products.push(product as unknown as NormalizedProduct);\n }\n }\n }\n return products;\n}\n\nfunction isNDJSONResponse(response: Response): boolean {\n const ct = response.headers.get('Content-Type') ?? '';\n return ct.includes('application/x-ndjson') || ct.includes('text/event-stream');\n}\n\nasync function collectProductsFromStream(response: Response, signal?: AbortSignal): Promise<NormalizedProduct[]> {\n const products: NormalizedProduct[] = [];\n const opts: import('../common/streaming.js').StreamOptions = {\n onEvent: (event: StreamEvent) => {\n const normalized = adaptBackendEvent(event as unknown as Record<string, unknown>);\n if (!normalized || normalized.type !== 'ui_spec') return;\n\n products.push(...extractProductCardsFromSpec(normalized.spec.elements));\n },\n };\n if (signal !== undefined) opts.signal = signal;\n await consumeStream(response, opts);\n return products;\n}\n\nexport async function fetchSimilarProducts(\n request: SimilarProductsRequest,\n transport: ChatTransportConfig,\n signal?: AbortSignal,\n): Promise<NormalizedProduct[]> {\n const url = buildChatEndpointUrl('similar_products', transport);\n\n const fetchInit: RequestInit = {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(request),\n };\n if (signal !== undefined) fetchInit.signal = signal;\n const response = await fetch(url, fetchInit);\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n if (isNDJSONResponse(response)) {\n return collectProductsFromStream(response, signal);\n }\n\n const text = await response.text();\n if (!text) throw new Error('Empty response body from similar_products endpoint');\n try {\n return normalizeSimilarProductsResponse(JSON.parse(text));\n } catch {\n throw new Error(`Invalid JSON from similar_products endpoint`);\n }\n}\n\nasync function collectGroupingsFromStream(response: Response, signal?: AbortSignal): Promise<ProductGroup[]> {\n const groups: ProductGroup[] = [];\n let currentGroup: ProductGroup | null = null;\n\n const opts: import('../common/streaming.js').StreamOptions = {\n onEvent: (event: StreamEvent) => {\n const normalized = adaptBackendEvent(event as unknown as Record<string, unknown>);\n if (!normalized) return;\n\n if (normalized.type === 'metadata' && normalized.meta) {\n const name = normalized.meta['group_name'];\n if (typeof name === 'string') {\n currentGroup = { name, products: [] };\n const highlight = normalized.meta['highlight'];\n if (typeof highlight === 'string') currentGroup.highlight = highlight;\n groups.push(currentGroup);\n }\n }\n\n if (normalized.type === 'ui_spec' && currentGroup) {\n currentGroup.products.push(...extractProductCardsFromSpec(normalized.spec.elements));\n }\n },\n };\n if (signal !== undefined) opts.signal = signal;\n await consumeStream(response, opts);\n\n return groups;\n}\n\nexport async function fetchProductGroupings(\n request: ProductGroupingsRequest,\n transport: ChatTransportConfig,\n signal?: AbortSignal,\n): Promise<ProductGroup[]> {\n const url = buildChatEndpointUrl('product_groupings', transport);\n\n const fetchInit: RequestInit = {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(request),\n };\n if (signal !== undefined) fetchInit.signal = signal;\n const response = await fetch(url, fetchInit);\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n if (isNDJSONResponse(response)) {\n return collectGroupingsFromStream(response, signal);\n }\n\n const text = await response.text();\n if (!text) return [];\n try {\n return normalizeProductGroupingsResponse(JSON.parse(text));\n } catch {\n throw new Error(`Invalid JSON from product_groupings endpoint`);\n }\n}\n","import type { NormalizedProduct } from '../../common/protocol-adapter.js';\nimport type { SimRelI18n } from '../types.js';\nimport type { PriceFormatConfig } from '../../common/price-formatter.js';\nimport { formatPrice } from '../../common/price-formatter.js';\nimport { sanitizeHtml, isSafeImageUrl } from '../../common/safe-html.js';\nimport { clampDiscount, addImageErrorHandler, createStarRatingElement } from '../../common/product-utils.js';\n\nexport interface ProductCardOptions {\n product: NormalizedProduct;\n index: number;\n discountType?: 'strike-through' | 'badge';\n onClick: (product: NormalizedProduct) => void;\n onAddToCart: (params: { sku: string; quantity: number; cartCode: string }) => void;\n renderCard?: (product: NormalizedProduct, index: number) => string;\n renderCardElement?: (product: NormalizedProduct, index: number) => HTMLElement | null;\n i18n?: SimRelI18n;\n pricing?: PriceFormatConfig;\n}\n\nexport function renderProductCard(options: ProductCardOptions): HTMLElement {\n const { product, index, discountType, onClick, onAddToCart, renderCard } = options;\n const i18n = options.i18n;\n const pricing = options.pricing;\n\n // Custom card element renderer (returns full HTMLElement, takes precedence)\n if (options.renderCardElement) {\n const el = options.renderCardElement(product, index);\n if (el) return el;\n }\n\n // Custom card renderer (XSS warning: raw HTML injection)\n if (renderCard) {\n const wrapper = document.createElement('div');\n wrapper.className =\n 'gengage-simrel-card gengage-simrel-card--custom gds-card gds-product-card gds-card-interactive';\n wrapper.dataset['gengagePart'] = 'simrel-product-card';\n // Sanitize renderCard output to prevent XSS from user-provided renderers.\n wrapper.innerHTML = sanitizeHtml(renderCard(product, index));\n wrapper.addEventListener('click', (e) => {\n if ((e.target as HTMLElement).closest('.gengage-simrel-atc')) return;\n if ((e.target as HTMLElement).closest('.gengage-chat-product-card-atc')) return;\n onClick(product);\n });\n return wrapper;\n }\n\n const card = document.createElement('article');\n // Intentional class coupling: reuse chat product-card classes so SimRel and chat stay visually identical.\n card.className = 'gengage-simrel-card gengage-chat-product-card gds-card gds-product-card gds-card-interactive';\n card.dataset['gengagePart'] = 'simrel-product-card';\n if (product.inStock === false) {\n card.classList.add('gengage-simrel-card--out-of-stock');\n }\n card.setAttribute('role', 'listitem');\n card.dataset['sku'] = product.sku;\n\n // Image\n const imgWrapper = document.createElement('div');\n imgWrapper.className = 'gengage-simrel-card-image gengage-chat-product-card-img-wrapper';\n imgWrapper.dataset['gengagePart'] = 'simrel-product-card-image';\n if (product.imageUrl && isSafeImageUrl(product.imageUrl)) {\n const img = document.createElement('img');\n img.className = 'gengage-chat-product-card-img';\n img.src = product.imageUrl;\n img.alt = product.name;\n img.loading = 'lazy';\n addImageErrorHandler(img);\n imgWrapper.appendChild(img);\n }\n\n // Discount badge\n if (discountType === 'badge' && product.discountPercent && product.discountPercent > 0) {\n const badge = document.createElement('span');\n badge.className = 'gengage-simrel-badge gengage-chat-product-card-discount-badge';\n badge.textContent = `%${clampDiscount(product.discountPercent)}`;\n imgWrapper.appendChild(badge);\n }\n\n card.appendChild(imgWrapper);\n\n // Info section\n const info = document.createElement('div');\n info.className = 'gengage-simrel-card-info gengage-chat-product-card-body';\n info.dataset['gengagePart'] = 'simrel-product-card-body';\n\n // Brand\n if (product.brand) {\n const brandEl = document.createElement('div');\n brandEl.className = 'gengage-simrel-card-brand gengage-chat-product-card-brand';\n brandEl.textContent = product.brand;\n info.appendChild(brandEl);\n }\n\n // Name\n const nameEl = document.createElement('div');\n nameEl.className = 'gengage-simrel-card-name gengage-chat-product-card-name';\n nameEl.textContent = product.name;\n nameEl.title = product.name;\n info.appendChild(nameEl);\n\n // Rating\n if (product.rating != null && product.rating > 0) {\n const ratingEl = document.createElement('div');\n ratingEl.className = 'gengage-simrel-card-rating gengage-chat-product-card-rating';\n ratingEl.appendChild(createStarRatingElement(product.rating));\n if (product.reviewCount != null) {\n const count = document.createElement('span');\n count.className = 'gengage-simrel-card-review-count gengage-chat-product-card-review-count';\n count.textContent = ` (${product.reviewCount})`;\n ratingEl.appendChild(count);\n }\n info.appendChild(ratingEl);\n }\n\n // Price\n const priceContainer = document.createElement('div');\n priceContainer.className = 'gengage-simrel-card-price gengage-chat-product-card-price';\n\n if (product.originalPrice && product.originalPrice !== product.price) {\n if (discountType === 'strike-through' || !discountType) {\n const original = document.createElement('span');\n original.className = 'gengage-simrel-card-price-original gengage-chat-product-card-original-price';\n original.textContent = formatPrice(product.originalPrice, pricing);\n priceContainer.appendChild(original);\n }\n }\n\n if (product.price && parseFloat(product.price) > 0) {\n const current = document.createElement('span');\n current.className = 'gengage-simrel-card-price-current gengage-chat-product-card-price-current';\n current.textContent = formatPrice(product.price, pricing);\n priceContainer.appendChild(current);\n }\n\n info.appendChild(priceContainer);\n card.appendChild(info);\n\n // Keep SimRel cards aligned with chat product-card structure.\n const cta = document.createElement('button');\n cta.className = 'gengage-simrel-card-cta gengage-chat-product-card-cta gds-btn gds-btn-secondary';\n cta.type = 'button';\n cta.dataset['gengagePart'] = 'simrel-product-card-cta';\n cta.textContent = i18n?.ctaLabel ?? 'View';\n cta.addEventListener('click', (e) => {\n e.preventDefault();\n e.stopPropagation();\n onClick(product);\n });\n card.appendChild(cta);\n\n // Add to cart stepper or out-of-stock indicator\n if (product.inStock === false) {\n const oos = document.createElement('div');\n oos.className = 'gengage-simrel-card-oos';\n oos.textContent = i18n?.outOfStockLabel ?? 'Out of Stock';\n card.appendChild(oos);\n } else if (product.cartCode) {\n const cartBtn = document.createElement('button');\n cartBtn.className = 'gengage-simrel-atc gengage-simrel-atc-button gds-btn gds-btn-secondary';\n cartBtn.type = 'button';\n cartBtn.textContent = i18n?.addToCartButton ?? 'Add to Cart';\n cartBtn.addEventListener('click', (e) => {\n e.preventDefault();\n e.stopPropagation();\n onAddToCart({ sku: product.sku, quantity: 1, cartCode: product.cartCode! });\n });\n card.appendChild(cartBtn);\n }\n\n // Card click → navigate\n card.addEventListener('click', (e) => {\n if ((e.target as HTMLElement).closest('.gengage-simrel-atc')) return;\n if ((e.target as HTMLElement).closest('.gengage-chat-product-card-atc')) return;\n if ((e.target as HTMLElement).closest('.gengage-chat-product-card-cta')) return;\n onClick(product);\n });\n\n return card;\n}\n","import type { NormalizedProduct } from '../../common/protocol-adapter.js';\nimport type { SimRelI18n } from '../types.js';\nimport { renderProductCard } from './ProductCard.js';\nimport type { ProductCardOptions } from './ProductCard.js';\n\nexport interface ProductGridOptions {\n products: NormalizedProduct[];\n columns?: number;\n discountType?: 'strike-through' | 'badge';\n onClick: (product: NormalizedProduct) => void;\n onAddToCart: (params: { sku: string; quantity: number; cartCode: string }) => void;\n renderCard?: (product: NormalizedProduct, index: number) => string;\n renderCardElement?: (product: NormalizedProduct, index: number) => HTMLElement | null;\n i18n?: SimRelI18n;\n}\n\nexport function renderProductGrid(options: ProductGridOptions): HTMLElement {\n const grid = document.createElement('div');\n grid.className = 'gengage-simrel-grid';\n grid.setAttribute('role', 'list');\n grid.setAttribute('aria-label', options.i18n?.similarProductsAriaLabel ?? 'Similar products');\n\n if (options.columns) {\n grid.style.setProperty('--gengage-simrel-columns', String(options.columns));\n }\n\n for (let i = 0; i < options.products.length; i++) {\n const product = options.products[i]!;\n const cardOpts: ProductCardOptions = {\n product,\n index: i,\n onClick: options.onClick,\n onAddToCart: options.onAddToCart,\n };\n if (options.i18n !== undefined) cardOpts.i18n = options.i18n;\n if (options.discountType !== undefined) cardOpts.discountType = options.discountType;\n if (options.renderCard !== undefined) cardOpts.renderCard = options.renderCard;\n if (options.renderCardElement !== undefined) cardOpts.renderCardElement = options.renderCardElement;\n const card = renderProductCard(cardOpts);\n grid.appendChild(card);\n }\n\n if (options.products.length === 0) {\n grid.style.display = 'none';\n grid.dataset['empty'] = 'true';\n }\n\n return grid;\n}\n","import type { NormalizedProduct } from '../../common/protocol-adapter.js';\nimport type { ProductGroup } from '../api.js';\nimport type { SimRelI18n } from '../types.js';\nimport { renderProductGrid } from './ProductGrid.js';\n\nexport interface GroupTabsOptions {\n groups: ProductGroup[];\n discountType?: 'strike-through' | 'badge';\n onClick: (product: NormalizedProduct) => void;\n onAddToCart: (params: { sku: string; quantity: number; cartCode: string }) => void;\n renderCard?: (product: NormalizedProduct, index: number) => string;\n renderCardElement?: (product: NormalizedProduct, index: number) => HTMLElement | null;\n i18n?: SimRelI18n;\n /** ProductGrid `columns` — masaüstü satır başına kart sayısı. */\n columns?: number;\n}\n\nlet _groupTabsInstanceCounter = 0;\n\nexport function renderGroupTabs(options: GroupTabsOptions): HTMLElement {\n const instanceId = _groupTabsInstanceCounter++;\n const container = document.createElement('div');\n container.className = 'gengage-simrel-groups';\n container.dataset['gengagePart'] = 'simrel-groups';\n\n if (options.groups.length === 0) {\n container.style.display = 'none';\n container.dataset['empty'] = 'true';\n return container;\n }\n\n // Tab bar — WAI-ARIA tablist pattern\n const tabBar = document.createElement('div');\n tabBar.className = 'gengage-simrel-tabs gds-toolbar';\n tabBar.dataset['gengagePart'] = 'simrel-tab-bar';\n tabBar.setAttribute('role', 'tablist');\n\n const tabs: HTMLButtonElement[] = [];\n const panels: HTMLElement[] = [];\n\n const buildGridOptions = (group: ProductGroup): import('./ProductGrid.js').ProductGridOptions => {\n const gridOpts: import('./ProductGrid.js').ProductGridOptions = {\n products: group.products,\n onClick: options.onClick,\n onAddToCart: options.onAddToCart,\n };\n if (options.i18n !== undefined) gridOpts.i18n = options.i18n;\n if (options.discountType !== undefined) gridOpts.discountType = options.discountType;\n if (options.renderCard !== undefined) gridOpts.renderCard = options.renderCard;\n if (options.renderCardElement !== undefined) gridOpts.renderCardElement = options.renderCardElement;\n if (options.columns !== undefined) gridOpts.columns = options.columns;\n return gridOpts;\n };\n\n const activateTab = (index: number): void => {\n for (let j = 0; j < tabs.length; j++) {\n const isActive = j === index;\n tabs[j]!.classList.toggle('gengage-simrel-tab--active', isActive);\n tabs[j]!.setAttribute('aria-selected', String(isActive));\n tabs[j]!.tabIndex = isActive ? 0 : -1;\n }\n\n // Lazy-render the grid content for the active panel\n const group = options.groups[index]!;\n const panel = panels[index]!;\n panel.innerHTML = '';\n const grid = renderProductGrid(buildGridOptions(group));\n panel.appendChild(grid);\n\n // Show only the active panel and manage tabindex for keyboard access\n for (let j = 0; j < panels.length; j++) {\n const isActive = j === index;\n panels[j]!.style.display = isActive ? '' : 'none';\n panels[j]!.tabIndex = isActive ? 0 : -1;\n }\n };\n\n for (let i = 0; i < options.groups.length; i++) {\n const group = options.groups[i]!;\n const tabId = `gengage-simrel-tab-${instanceId}-${i}`;\n const panelId = `gengage-simrel-panel-${instanceId}-${i}`;\n\n // Tab button\n const tab = document.createElement('button');\n tab.className = 'gengage-simrel-tab gds-tab';\n tab.type = 'button';\n tab.dataset['gengagePart'] = 'simrel-tab';\n tab.id = tabId;\n tab.textContent = group.name;\n tab.setAttribute('role', 'tab');\n tab.setAttribute('aria-controls', panelId);\n tab.setAttribute('aria-selected', String(i === 0));\n tab.tabIndex = i === 0 ? 0 : -1;\n if (i === 0) tab.classList.add('gengage-simrel-tab--active');\n\n tab.addEventListener('click', () => activateTab(i));\n tab.addEventListener('keydown', (e: KeyboardEvent) => {\n let next = -1;\n if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {\n next = (i + 1) % options.groups.length;\n } else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {\n next = (i - 1 + options.groups.length) % options.groups.length;\n } else if (e.key === 'Home') {\n next = 0;\n } else if (e.key === 'End') {\n next = options.groups.length - 1;\n }\n if (next >= 0) {\n e.preventDefault();\n activateTab(next);\n tabs[next]!.focus();\n }\n });\n\n tabs.push(tab);\n tabBar.appendChild(tab);\n\n // Tab panel\n const panel = document.createElement('div');\n panel.className = 'gengage-simrel-tab-panel';\n panel.dataset['gengagePart'] = 'simrel-tab-panel';\n panel.id = panelId;\n panel.setAttribute('role', 'tabpanel');\n panel.setAttribute('aria-labelledby', tabId);\n panel.tabIndex = i === 0 ? 0 : -1;\n if (i !== 0) panel.style.display = 'none';\n\n panels.push(panel);\n }\n\n // Scroll arrow buttons for mouse-only users\n const scrollLeftBtn = document.createElement('button');\n scrollLeftBtn.type = 'button';\n scrollLeftBtn.className = 'gengage-simrel-tabs-arrow gengage-simrel-tabs-arrow--left';\n scrollLeftBtn.setAttribute('aria-label', options.i18n?.scrollTabsLeft ?? 'Scroll tabs left');\n scrollLeftBtn.innerHTML =\n '<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"15 18 9 12 15 6\"/></svg>';\n\n const scrollRightBtn = document.createElement('button');\n scrollRightBtn.type = 'button';\n scrollRightBtn.className = 'gengage-simrel-tabs-arrow gengage-simrel-tabs-arrow--right';\n scrollRightBtn.setAttribute('aria-label', options.i18n?.scrollTabsRight ?? 'Scroll tabs right');\n scrollRightBtn.innerHTML =\n '<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"9 18 15 12 9 6\"/></svg>';\n\n const SCROLL_AMOUNT = 200;\n\n scrollLeftBtn.addEventListener('click', () => {\n tabBar.scrollBy({ left: -SCROLL_AMOUNT, behavior: 'smooth' });\n });\n scrollRightBtn.addEventListener('click', () => {\n tabBar.scrollBy({ left: SCROLL_AMOUNT, behavior: 'smooth' });\n });\n\n const updateArrows = () => {\n const atLeft = tabBar.scrollLeft <= 4;\n const atRight = tabBar.scrollLeft + tabBar.clientWidth >= tabBar.scrollWidth - 4;\n scrollLeftBtn.style.display = atLeft ? 'none' : '';\n scrollRightBtn.style.display = atRight ? 'none' : '';\n tabBar.classList.toggle('gengage-simrel-tabs--peek-right', !atRight);\n tabBar.classList.toggle('gengage-simrel-tabs--peek-left', !atLeft);\n };\n\n tabBar.addEventListener('scroll', updateArrows, { passive: true });\n\n // Use ResizeObserver to update arrows when container size changes (not available in jsdom/tests)\n if (typeof ResizeObserver !== 'undefined') {\n const ro = new ResizeObserver(updateArrows);\n ro.observe(tabBar);\n }\n\n // Initial state — hide arrows until we know overflow state\n scrollLeftBtn.style.display = 'none';\n scrollRightBtn.style.display = 'none';\n // Check after initial render\n requestAnimationFrame(updateArrows);\n\n const tabBarWrapper = document.createElement('div');\n tabBarWrapper.className = 'gengage-simrel-tabs-wrapper';\n tabBarWrapper.appendChild(scrollLeftBtn);\n tabBarWrapper.appendChild(tabBar);\n tabBarWrapper.appendChild(scrollRightBtn);\n container.appendChild(tabBarWrapper);\n\n // Render the initial (first) tab content\n const firstPanel = panels[0]!;\n const firstGrid = renderProductGrid(buildGridOptions(options.groups[0]!));\n firstPanel.appendChild(firstGrid);\n\n for (const panel of panels) container.appendChild(panel);\n\n return container;\n}\n","import { renderUISpecWithRegistry } from '../../common/renderer/index.js';\nimport type { UISpecDomRegistry, UISpecDomUnknownRenderer } from '../../common/renderer/index.js';\nimport type { UISpec, ActionPayload } from '../../common/types.js';\nimport type { SimRelUISpecRenderContext, SimilarProduct } from '../types.js';\nimport type { ProductGroup } from '../api.js';\nimport { renderProductCard } from './ProductCard.js';\nimport { renderGroupTabs } from './GroupTabs.js';\n\nexport type SimRelUISpecRegistry = UISpecDomRegistry<SimRelUISpecRenderContext>;\n\nfunction toSimRelProduct(raw: unknown): SimilarProduct | null {\n if (!raw || typeof raw !== 'object') return null;\n const obj = raw as Record<string, unknown>;\n if (typeof obj['sku'] !== 'string' || typeof obj['name'] !== 'string' || typeof obj['url'] !== 'string') {\n return null;\n }\n\n const result: SimilarProduct = {\n sku: obj['sku'],\n name: obj['name'],\n url: obj['url'],\n };\n\n const imageUrl = obj['imageUrl'];\n if (typeof imageUrl === 'string') result.imageUrl = imageUrl;\n const price = obj['price'];\n if (typeof price === 'string') result.price = price;\n const originalPrice = obj['originalPrice'];\n if (typeof originalPrice === 'string') result.originalPrice = originalPrice;\n const discountPercent = obj['discountPercent'];\n if (typeof discountPercent === 'number') result.discountPercent = discountPercent;\n const brand = obj['brand'];\n if (typeof brand === 'string') result.brand = brand;\n const rating = obj['rating'];\n if (typeof rating === 'number') result.rating = rating;\n const reviewCount = obj['reviewCount'];\n if (typeof reviewCount === 'number') result.reviewCount = reviewCount;\n const cartCode = obj['cartCode'];\n if (typeof cartCode === 'string') result.cartCode = cartCode;\n const inStock = obj['inStock'];\n if (typeof inStock === 'boolean') result.inStock = inStock;\n const extras = obj['extras'];\n if (extras != null && typeof extras === 'object') result.extras = extras as Record<string, unknown>;\n\n return result;\n}\n\nfunction toActionPayload(raw: unknown): ActionPayload | null {\n if (!raw || typeof raw !== 'object') return null;\n const obj = raw as Record<string, unknown>;\n const title = obj['title'];\n const type = obj['type'];\n if (typeof title !== 'string' || typeof type !== 'string') return null;\n const action: ActionPayload = { title, type };\n if (obj['payload'] !== undefined) action.payload = obj['payload'];\n return action;\n}\n\nconst DEFAULT_SIMREL_UI_SPEC_REGISTRY: SimRelUISpecRegistry = {\n ProductGrid: ({ element, renderElement, context }) => {\n const grid = document.createElement('div');\n grid.className = 'gengage-simrel-grid';\n grid.setAttribute('role', 'list');\n\n const propCols = element.props?.['columns'];\n let columns: number | undefined;\n if (typeof propCols === 'number' && Number.isFinite(propCols) && propCols > 0) {\n columns = Math.floor(propCols);\n } else if (\n typeof context.gridColumns === 'number' &&\n Number.isFinite(context.gridColumns) &&\n context.gridColumns > 0\n ) {\n columns = Math.floor(context.gridColumns);\n }\n if (columns !== undefined) {\n grid.style.setProperty('--gengage-simrel-columns', String(columns));\n }\n\n for (const childId of element.children ?? []) {\n const rendered = renderElement(childId);\n if (rendered) grid.appendChild(rendered);\n }\n\n if (grid.children.length === 0) {\n const empty = document.createElement('div');\n empty.className = 'gengage-simrel-empty';\n empty.textContent = context.i18n.emptyStateMessage;\n grid.appendChild(empty);\n }\n\n return grid;\n },\n\n ProductCard: ({ element, context }) => {\n const productRaw = (element.props?.['product'] ?? element.props) as unknown;\n const product = toSimRelProduct(productRaw);\n if (!product) return null;\n\n const indexRaw = element.props?.['index'];\n const index = typeof indexRaw === 'number' && Number.isFinite(indexRaw) ? indexRaw : 0;\n const discountTypeRaw = element.props?.['discountType'];\n const discountType =\n discountTypeRaw === 'strike-through' || discountTypeRaw === 'badge' ? discountTypeRaw : context.discountType;\n\n const options: import('./ProductCard.js').ProductCardOptions = {\n product,\n index,\n onClick: context.onClick,\n onAddToCart: context.onAddToCart,\n i18n: context.i18n,\n };\n if (discountType !== undefined) options.discountType = discountType;\n if (context.renderCard !== undefined) options.renderCard = context.renderCard;\n if (context.renderCardElement !== undefined) options.renderCardElement = context.renderCardElement;\n if (context.pricing !== undefined) options.pricing = context.pricing;\n return renderProductCard(options);\n },\n\n GroupTabs: ({ element, context }) => {\n const groupsRaw = element.props?.['groups'];\n if (!Array.isArray(groupsRaw)) return null;\n const groups: ProductGroup[] = [];\n\n for (const entry of groupsRaw) {\n if (!entry || typeof entry !== 'object') continue;\n const obj = entry as Record<string, unknown>;\n if (typeof obj['name'] !== 'string') continue;\n\n const products: SimilarProduct[] = [];\n if (Array.isArray(obj['products'])) {\n for (const rawProduct of obj['products']) {\n const normalized = toSimRelProduct(rawProduct);\n if (normalized) products.push(normalized);\n }\n }\n\n const group: ProductGroup = {\n name: obj['name'],\n products,\n };\n if (typeof obj['highlight'] === 'string') group.highlight = obj['highlight'];\n groups.push(group);\n }\n\n const options: import('./GroupTabs.js').GroupTabsOptions = {\n groups,\n onClick: context.onClick,\n onAddToCart: context.onAddToCart,\n i18n: context.i18n,\n };\n const tabGridCols = element.props?.['columns'];\n if (typeof tabGridCols === 'number' && Number.isFinite(tabGridCols) && tabGridCols > 0) {\n options.columns = Math.floor(tabGridCols);\n } else if (\n typeof context.gridColumns === 'number' &&\n Number.isFinite(context.gridColumns) &&\n context.gridColumns > 0\n ) {\n options.columns = Math.floor(context.gridColumns);\n }\n if (context.discountType !== undefined) options.discountType = context.discountType;\n if (context.renderCard !== undefined) options.renderCard = context.renderCard;\n if (context.renderCardElement !== undefined) options.renderCardElement = context.renderCardElement;\n return renderGroupTabs(options);\n },\n\n EmptyState: ({ element, context }) => {\n const empty = document.createElement('div');\n empty.className = 'gengage-simrel-empty';\n const message = element.props?.['message'];\n empty.textContent = typeof message === 'string' ? message : context.i18n.emptyStateMessage;\n return empty;\n },\n\n AddToCartButton: ({ element, context }) => {\n const sku = element.props?.['sku'];\n const cartCode = element.props?.['cartCode'];\n if (typeof sku !== 'string' || typeof cartCode !== 'string') return null;\n\n const button = document.createElement('button');\n button.className = 'gengage-simrel-atc gengage-chat-product-card-cta';\n button.type = 'button';\n const label = element.props?.['label'];\n button.textContent = typeof label === 'string' ? label : context.i18n.addToCartButton;\n button.addEventListener('click', (e) => {\n e.preventDefault();\n e.stopPropagation();\n context.onAddToCart({ sku, quantity: 1, cartCode });\n });\n return button;\n },\n\n QuickActions: ({ element, context }) => {\n const wrapper = document.createElement('div');\n wrapper.className = 'gengage-simrel-quick-actions';\n const actions = element.props?.['actions'];\n if (!Array.isArray(actions) || !context.onAction) return wrapper;\n\n for (const raw of actions) {\n if (!raw || typeof raw !== 'object') continue;\n const actionObj = raw as Record<string, unknown>;\n const label = actionObj['label'];\n const action = toActionPayload(actionObj['action']);\n if (typeof label !== 'string' || !action) continue;\n\n const button = document.createElement('button');\n button.className = 'gengage-simrel-quick-action';\n button.type = 'button';\n button.textContent = label;\n button.addEventListener('click', (e) => {\n e.preventDefault();\n e.stopPropagation();\n context.onAction?.(action);\n });\n wrapper.appendChild(button);\n }\n return wrapper;\n },\n};\n\nexport const defaultSimRelUnknownUISpecRenderer: UISpecDomUnknownRenderer<SimRelUISpecRenderContext> = ({\n element,\n renderElement,\n}) => {\n if (import.meta.env?.DEV) {\n console.warn(`[gengage:simrel] Unknown ui_spec component type: ${element.type}`);\n }\n if (!element.children || element.children.length === 0) {\n return null;\n }\n const wrapper = document.createElement('div');\n for (const childId of element.children) {\n const rendered = renderElement(childId);\n if (rendered) wrapper.appendChild(rendered);\n }\n return wrapper;\n};\n\nexport function createDefaultSimRelUISpecRegistry(): SimRelUISpecRegistry {\n return { ...DEFAULT_SIMREL_UI_SPEC_REGISTRY };\n}\n\nexport function renderSimRelUISpec(\n spec: UISpec,\n context: SimRelUISpecRenderContext,\n registry = DEFAULT_SIMREL_UI_SPEC_REGISTRY,\n unknownRenderer: UISpecDomUnknownRenderer<SimRelUISpecRenderContext> = defaultSimRelUnknownUISpecRenderer,\n): HTMLElement {\n return renderUISpecWithRegistry({\n spec,\n context,\n registry,\n containerClassName: 'gengage-simrel-uispec',\n unknownRenderer,\n });\n}\n","import type { SimRelI18n } from '../types.js';\n\nexport const SIMREL_I18N_TR: SimRelI18n = {\n similarProductsAriaLabel: 'Benzer ürünler',\n emptyStateMessage: 'Benzer ürün bulunamadı.',\n addToCartButton: 'Sepete Ekle',\n ctaLabel: 'İncele',\n outOfStockLabel: 'Stokta Yok',\n decreaseLabel: 'Azalt',\n increaseLabel: 'Artır',\n errorLoadingMessage: 'Benzer ürünler yüklenemedi.',\n retryButtonText: 'Tekrar dene',\n priceSuffix: ' TL',\n scrollTabsLeft: 'Sola kaydır',\n scrollTabsRight: 'Sağa kaydır',\n};\n","import type { SimRelI18n } from '../types.js';\n\nexport const SIMREL_I18N_EN: SimRelI18n = {\n similarProductsAriaLabel: 'Similar products',\n emptyStateMessage: 'No similar products found.',\n addToCartButton: 'Add to cart',\n ctaLabel: 'View',\n outOfStockLabel: 'Out of Stock',\n decreaseLabel: 'Decrease',\n increaseLabel: 'Increase',\n errorLoadingMessage: 'Could not load similar products.',\n retryButtonText: 'Try again',\n priceSuffix: '',\n scrollTabsLeft: 'Scroll tabs left',\n scrollTabsRight: 'Scroll tabs right',\n};\n","import type { SimRelI18n } from '../types.js';\nimport { SIMREL_I18N_TR } from './tr.js';\nimport { SIMREL_I18N_EN } from './en.js';\n\nfunction normalizeLocale(locale?: string): string {\n if (!locale) return 'tr';\n return locale.toLowerCase().split('-')[0] ?? 'tr';\n}\n\nexport function resolveSimRelLocale(locale?: string): SimRelI18n {\n switch (normalizeLocale(locale)) {\n case 'en':\n return SIMREL_I18N_EN;\n default:\n return SIMREL_I18N_TR;\n }\n}\n\nexport { SIMREL_I18N_TR, SIMREL_I18N_EN };\n","/**\n * Similar Products (SimRel) widget — json-render catalog definition.\n *\n * Backend endpoints:\n * POST /chat/similar_products — primary product list\n * POST /chat/product_groupings — grouped/tabbed view\n *\n * The backend streams NDJSON events. `ui_spec` events reference\n * component names defined below. Implementations live in ./registry.\n */\n\nimport { z } from 'zod';\n\nconst SimilarProductSchema = z.object({\n sku: z.string(),\n name: z.string(),\n imageUrl: z.string().url().optional(),\n price: z.string().optional(),\n originalPrice: z.string().optional(),\n discountPercent: z.number().optional(),\n url: z.string().url(),\n brand: z.string().optional(),\n rating: z.number().min(0).max(5).optional(),\n reviewCount: z.number().int().nonnegative().optional(),\n});\n\nexport const ProductGridSchema = z.object({\n layout: z.enum(['grid', 'carousel']).optional(),\n columns: z.number().int().positive().optional(),\n});\n\nexport const ProductCardSchema = z.object({\n product: SimilarProductSchema,\n index: z.number().int().nonnegative(),\n discountType: z.enum(['strike-through', 'badge']).optional(),\n});\n\nexport const AddToCartButtonSchema = z.object({\n sku: z.string(),\n label: z.string().optional(),\n cartCode: z.string(),\n});\n\nexport const QuickActionsSchema = z.object({\n actions: z.array(\n z.object({\n label: z.string(),\n action: z.object({\n title: z.string(),\n type: z.string(),\n payload: z.unknown().optional(),\n }),\n }),\n ),\n});\n\nexport const EmptyStateSchema = z.object({\n message: z.string().optional(),\n});\n\nexport const simRelCatalog = {\n components: {\n ProductGrid: {\n schema: ProductGridSchema,\n description: 'Outer grid or carousel container for similar products.',\n },\n ProductCard: {\n schema: ProductCardSchema,\n description: 'A single product card with image, title, price, and actions.',\n },\n AddToCartButton: {\n schema: AddToCartButtonSchema,\n description: 'Add-to-cart CTA rendered inside or below a product card.',\n },\n QuickActions: {\n schema: QuickActionsSchema,\n description: 'A row of quick-action buttons below product info.',\n },\n EmptyState: {\n schema: EmptyStateSchema,\n description: 'Empty state shown when no similar products are available.',\n },\n },\n} as const;\n\nexport type SimRelCatalog = typeof simRelCatalog;\nexport type SimRelComponentName = keyof SimRelCatalog['components'];\n","/**\n * Similar Products (SimRel) widget — public entry point.\n *\n * Fetches and renders similar / related products for the current SKU.\n * Backend: POST /chat/similar_products + /chat/product_groupings\n */\n\nimport type { PageContext, UISpec, UIElement } from '../common/types.js';\nimport type { NormalizedProduct } from '../common/protocol-adapter.js';\nimport type { ChatTransportConfig } from '../common/api-paths.js';\nimport type { UISpecRenderHelpers } from '../common/renderer/index.js';\nimport { mergeUISpecRegistry } from '../common/renderer/index.js';\nimport { BaseWidget } from '../common/widget-base.js';\nimport { dispatch } from '../common/events.js';\nimport { trackConnectionWarningRequest } from '../common/connection-warning.js';\nimport { getGlobalErrorMessage } from '../common/global-error-toast.js';\nimport {\n streamStartEvent,\n streamDoneEvent,\n streamErrorEvent,\n basketAddEvent,\n widgetHistorySnapshotEvent,\n} from '../common/analytics-events.js';\nimport { fetchSimilarProducts, fetchProductGroupings } from './api.js';\nimport {\n createDefaultSimRelUISpecRegistry,\n defaultSimRelUnknownUISpecRenderer,\n renderSimRelUISpec,\n} from './components/renderUISpec.js';\nimport type { SimRelWidgetConfig, SimilarProduct, SimRelI18n, SimRelUISpecRenderContext } from './types.js';\nimport { SIMREL_I18N_TR, resolveSimRelLocale } from './locales/index.js';\nimport * as ga from '../common/ga-datalayer.js';\n\nimport './components/simrel.css';\n\n/**\n * Similar / related products widget for product pages.\n * Fetches AI-powered product recommendations and renders them as a scrollable grid.\n *\n * @example\n * ```ts\n * import { GengageSimRel, bootstrapSession } from '@gengage/assistant-fe';\n *\n * const simrel = new GengageSimRel();\n * await simrel.init({\n * accountId: 'mystore',\n * middlewareUrl: 'https://chat.gengage.ai',\n * sku: '12345',\n * mountTarget: '#similar-products',\n * session: { sessionId: bootstrapSession() },\n * onAddToCart: ({ sku, quantity }) => cart.add(sku, quantity),\n * });\n * ```\n */\nexport class GengageSimRel extends BaseWidget<SimRelWidgetConfig> {\n private _abortController: AbortController | null = null;\n private _contentEl: HTMLElement | null = null;\n private _lastSku: string | undefined;\n /** Number of products returned from the last successful fetch. Used to allow\n * retry when the same SKU previously produced an empty result set. */\n private _lastResultCount = -1;\n private _i18n: SimRelI18n = SIMREL_I18N_TR;\n\n protected async onInit(config: SimRelWidgetConfig): Promise<void> {\n this._i18n = this._resolveI18n(config);\n\n this._contentEl = document.createElement('div');\n this._contentEl.className = 'gengage-simrel-container';\n this._contentEl.dataset['gengagePart'] = 'simrel-container';\n const gridCols = this._clampGridColumns(config.gridColumns);\n if (gridCols !== undefined) {\n this._contentEl.style.setProperty('--gengage-simrel-columns', String(gridCols));\n }\n this.root.appendChild(this._contentEl);\n\n this._lastSku = config.sku;\n await this._fetchAndRender(config.sku);\n ga.trackInit('simrel');\n }\n\n protected onUpdate(context: Partial<PageContext>): void {\n const newSku = context.sku;\n // Allow retry for the same SKU when the previous fetch returned no products\n // (e.g. transient backend empty response). _lastResultCount === 0 means the\n // last fetch succeeded but produced nothing — worth retrying on the next\n // page interaction.\n if (!newSku || (newSku === this._lastSku && this._lastResultCount !== 0)) return;\n this._lastSku = newSku;\n void this._fetchAndRender(newSku);\n }\n\n protected onShow(): void {\n if (this._contentEl) {\n this._contentEl.style.opacity = '0';\n this._contentEl.style.transition = 'opacity 0.3s ease-in';\n requestAnimationFrame(() => {\n if (this._contentEl) this._contentEl.style.opacity = '1';\n });\n }\n }\n\n protected onHide(): void {\n // Preserve fetched products for re-show\n }\n\n protected onDestroy(): void {\n this._abort();\n if (this._contentEl) {\n this._contentEl.remove();\n this._contentEl = null;\n }\n }\n\n // ---------------------------------------------------------------------------\n // Internal event dispatchers\n // ---------------------------------------------------------------------------\n\n _handleProductClick(product: NormalizedProduct): void {\n const simRelProduct: SimilarProduct = {\n sku: product.sku,\n name: product.name,\n url: product.url,\n };\n if (product.imageUrl !== undefined) simRelProduct.imageUrl = product.imageUrl;\n if (product.price !== undefined) simRelProduct.price = product.price;\n if (product.originalPrice !== undefined) simRelProduct.originalPrice = product.originalPrice;\n if (product.discountPercent !== undefined) simRelProduct.discountPercent = product.discountPercent;\n if (product.brand !== undefined) simRelProduct.brand = product.brand;\n if (product.rating !== undefined) simRelProduct.rating = product.rating;\n if (product.reviewCount !== undefined) simRelProduct.reviewCount = product.reviewCount;\n if (product.cartCode !== undefined) simRelProduct.cartCode = product.cartCode;\n if (product.inStock !== undefined) simRelProduct.inStock = product.inStock;\n\n if (this.config.onProductClick?.(simRelProduct) === false) return;\n\n ga.trackProductDetail(product.sku, product.name);\n const sessionId = this.config.session?.sessionId ?? null;\n dispatch('gengage:similar:product-click', {\n sku: product.sku,\n url: product.url,\n sessionId,\n });\n\n this.config.onProductNavigate?.(product.url, product.sku, sessionId);\n }\n\n _handleAddToCart(params: { sku: string; quantity: number; cartCode: string }): void {\n ga.trackCartAdd(params.sku, params.quantity);\n this.config.onAddToCart?.(params);\n dispatch('gengage:similar:add-to-cart', params);\n\n this.track(\n basketAddEvent(this.analyticsContext(), {\n attribution_source: 'simrel',\n attribution_action_id: crypto.randomUUID(),\n cart_value: 0, // Host page should enrich via event listener\n currency: this.config.pricing?.currencyCode ?? 'TRY',\n line_items: params.quantity,\n sku: params.sku,\n }),\n );\n }\n\n // ---------------------------------------------------------------------------\n // Private\n // ---------------------------------------------------------------------------\n\n private _abort(): void {\n this._abortController?.abort();\n this._abortController = null;\n }\n\n private _isSuperseded(signal: AbortSignal): boolean {\n return this._abortController?.signal !== signal;\n }\n\n private async _fetchAndRender(sku: string): Promise<void> {\n this._abort();\n this._abortController = new AbortController();\n // Capture signal reference at invocation time to avoid race conditions:\n // if onUpdate fires between awaits, `this._abortController` gets swapped\n // but `signal` still refers to this invocation's controller.\n const signal = this._abortController.signal;\n // Auto-abort after 10s to prevent indefinite loading state\n const timeoutId = setTimeout(() => this._abortController?.abort(), 10_000);\n signal.addEventListener('abort', () => clearTimeout(timeoutId));\n\n if (!this._contentEl) return;\n this._contentEl.innerHTML = '';\n // Reset visibility in case a previous error set display:none\n this._contentEl.style.display = '';\n\n // Show loading spinner\n const loading = document.createElement('div');\n loading.className = 'gengage-simrel-loading';\n loading.dataset['gengagePart'] = 'simrel-loading';\n const spinner = document.createElement('div');\n spinner.className = 'gengage-simrel-spinner';\n spinner.dataset['gengagePart'] = 'simrel-loading-spinner';\n loading.appendChild(spinner);\n this._contentEl.appendChild(loading);\n\n const transport: ChatTransportConfig = {\n middlewareUrl: this.config.middlewareUrl,\n };\n\n const requestId = crypto.randomUUID();\n const fetchStart = Date.now();\n const releaseConnectionWarning = trackConnectionWarningRequest({\n source: 'simrel',\n locale: this.config.locale,\n });\n\n this.track(\n streamStartEvent(this.analyticsContext(), {\n endpoint: 'similar_products',\n request_id: requestId,\n widget: 'simrel',\n }),\n );\n\n try {\n // Fetch similar products\n const simReq: import('./api.js').SimilarProductsRequest = {\n account_id: this.config.accountId,\n session_id: this.config.session?.sessionId ?? '',\n correlation_id: this.config.session?.sessionId ?? '',\n sku,\n };\n if (this.config.domain !== undefined) simReq.domain = this.config.domain;\n const products = await fetchSimilarProducts(simReq, transport, signal);\n // Record result count so onUpdate can retry if last fetch was empty\n this._lastResultCount = products.length;\n\n if (!this._contentEl) return;\n this._contentEl.innerHTML = '';\n\n // Try to fetch product groupings for tabbed view\n if (products.length > 0) {\n try {\n const skus = products.map((p) => p.sku);\n const productsBySku = new Map(products.map((product) => [product.sku, product] as const));\n const groups = await fetchProductGroupings(\n {\n account_id: this.config.accountId,\n session_id: this.config.session?.sessionId ?? '',\n correlation_id: this.config.session?.sessionId ?? '',\n skus,\n },\n transport,\n signal,\n );\n const usableGroups = groups\n .map((group) => ({\n ...group,\n products: group.products\n .map((groupProduct) => {\n const fallbackProduct = productsBySku.get(groupProduct.sku);\n return {\n ...fallbackProduct,\n ...groupProduct,\n };\n })\n .filter(\n (groupProduct) =>\n typeof groupProduct.sku === 'string' &&\n typeof groupProduct.name === 'string' &&\n typeof groupProduct.url === 'string',\n ),\n }))\n .filter((group) => group.products.length > 0);\n\n if (usableGroups.length > 0 && this._contentEl) {\n const groupsSpec = this._buildGroupsSpec(usableGroups);\n const renderedGroups = this._renderUISpec(groupsSpec);\n this._contentEl.appendChild(renderedGroups);\n\n ga.trackShow('simrel');\n this.track(\n streamDoneEvent(this.analyticsContext(), {\n request_id: requestId,\n latency_ms: Date.now() - fetchStart,\n chunk_count: usableGroups.reduce((n, g) => n + g.products.length, 0),\n widget: 'simrel',\n }),\n );\n this.track(\n widgetHistorySnapshotEvent(this.analyticsContext(), {\n message_count: usableGroups.reduce((n, g) => n + g.products.length, 0),\n history_ref: requestId,\n redaction_level: 'none',\n widget: 'simrel',\n }),\n );\n return;\n }\n } catch {\n // Product groupings is optional; fall through to flat grid\n }\n }\n\n // Only bail out when a newer request superseded this one. If the optional\n // grouping call timed out, still render the flat-grid fallback from the\n // already-fetched similar products.\n if (this._isSuperseded(signal)) return;\n\n // Flat grid (no groupings or groupings failed)\n if (this._contentEl) {\n const gridSpec = this._buildProductsSpec(products);\n const renderedGrid = this._renderUISpec(gridSpec);\n this._contentEl.appendChild(renderedGrid);\n }\n\n if (products.length > 0) {\n ga.trackShow('simrel');\n }\n\n this.track(\n streamDoneEvent(this.analyticsContext(), {\n request_id: requestId,\n latency_ms: Date.now() - fetchStart,\n chunk_count: products.length,\n widget: 'simrel',\n }),\n );\n\n this.track(\n widgetHistorySnapshotEvent(this.analyticsContext(), {\n message_count: products.length,\n history_ref: requestId,\n redaction_level: 'none',\n widget: 'simrel',\n }),\n );\n } catch (err) {\n if (err instanceof DOMException && err.name === 'AbortError' && this._isSuperseded(signal)) return;\n\n dispatch('gengage:global:error', {\n source: 'simrel',\n code: 'FETCH_ERROR',\n message: getGlobalErrorMessage(this.config.locale, err),\n });\n\n this.track(\n streamErrorEvent(this.analyticsContext(), {\n request_id: requestId,\n error_code: 'FETCH_ERROR',\n error_message: err instanceof Error ? err.message : String(err),\n widget: 'simrel',\n }),\n );\n\n if (import.meta.env?.DEV) {\n console.error('[gengage:simrel] Failed to fetch similar products:', err);\n }\n // Show inline error with retry instead of hiding silently\n if (this._contentEl) {\n this._contentEl.innerHTML = '';\n const errorEl = document.createElement('div');\n errorEl.className = 'gengage-simrel-error';\n const msgEl = document.createElement('span');\n msgEl.textContent = this._i18n.errorLoadingMessage;\n errorEl.appendChild(msgEl);\n const retryBtn = document.createElement('button');\n retryBtn.className = 'gengage-simrel-retry';\n retryBtn.textContent = this._i18n.retryButtonText;\n retryBtn.addEventListener('click', () => {\n void this._fetchAndRender(this.config.sku);\n });\n errorEl.appendChild(retryBtn);\n this._contentEl.appendChild(errorEl);\n }\n } finally {\n releaseConnectionWarning();\n }\n }\n\n private _clampGridColumns(n: number | undefined): number | undefined {\n if (n === undefined || typeof n !== 'number' || !Number.isFinite(n)) return undefined;\n return Math.max(1, Math.min(12, Math.floor(n)));\n }\n\n private _resolveI18n(config: SimRelWidgetConfig): SimRelI18n {\n const base = resolveSimRelLocale(config.locale);\n return { ...base, ...config.i18n };\n }\n\n private _resolveUISpecRegistry() {\n const baseRegistry = createDefaultSimRelUISpecRegistry();\n return mergeUISpecRegistry(baseRegistry, this.config.renderer?.registry);\n }\n\n private _buildRenderContext(): SimRelUISpecRenderContext {\n const renderCard = this.config.renderCard as ((product: SimilarProduct, index: number) => string) | undefined;\n const context: SimRelUISpecRenderContext = {\n onClick: (product) => this._handleProductClick(product as unknown as NormalizedProduct),\n onAddToCart: (params) => this._handleAddToCart(params),\n i18n: this._i18n,\n };\n if (this.config.discountType !== undefined) context.discountType = this.config.discountType;\n if (renderCard !== undefined) context.renderCard = renderCard;\n if (this.config.renderCardElement !== undefined) {\n context.renderCardElement = this.config.renderCardElement as (\n product: SimilarProduct,\n index: number,\n ) => HTMLElement | null;\n }\n if (this.config.pricing !== undefined) context.pricing = this.config.pricing;\n const gridCols = this._clampGridColumns(this.config.gridColumns);\n if (gridCols !== undefined) context.gridColumns = gridCols;\n return context;\n }\n\n private _renderUISpec(spec: UISpec): HTMLElement {\n const registry = this._resolveUISpecRegistry();\n const context = this._buildRenderContext();\n const unknownRenderer = this.config.renderer?.unknownRenderer ?? defaultSimRelUnknownUISpecRenderer;\n const defaultRender = (inputSpec: UISpec, inputContext: SimRelUISpecRenderContext) =>\n renderSimRelUISpec(inputSpec, inputContext, registry, unknownRenderer);\n\n const override = this.config.renderer?.renderUISpec;\n if (!override) return defaultRender(spec, context);\n\n const helpers: UISpecRenderHelpers<SimRelUISpecRenderContext> = {\n registry,\n unknownRenderer,\n defaultRender,\n };\n return override(spec, context, helpers);\n }\n\n private _buildProductsSpec(products: NormalizedProduct[]): UISpec {\n const elements: Record<string, UIElement> = {};\n const children: string[] = [];\n for (let i = 0; i < products.length; i++) {\n const product = products[i]!;\n const id = `product-${i}`;\n children.push(id);\n elements[id] = {\n type: 'ProductCard',\n props: {\n product,\n index: i,\n discountType: this.config.discountType,\n },\n };\n }\n const gridProps: Record<string, unknown> = { layout: 'grid' };\n const gridCols = this._clampGridColumns(this.config.gridColumns);\n if (gridCols !== undefined) gridProps['columns'] = gridCols;\n\n elements['root'] = {\n type: 'ProductGrid',\n props: gridProps,\n children,\n };\n return {\n root: 'root',\n elements,\n };\n }\n\n private _buildGroupsSpec(\n groups: Array<{\n name: string;\n highlight?: string;\n products: NormalizedProduct[];\n }>,\n ): UISpec {\n return {\n root: 'root',\n elements: {\n root: {\n type: 'GroupTabs',\n props: { groups },\n },\n },\n };\n }\n}\n\nexport function createSimRelWidget(): GengageSimRel {\n return new GengageSimRel();\n}\n\nexport type {\n SimRelWidgetConfig,\n SimilarProduct,\n SimRelUIComponents,\n SimRelI18n,\n SimRelUISpecRenderContext,\n SimRelRendererConfig,\n} from './types.js';\nexport {\n renderSimRelUISpec,\n createDefaultSimRelUISpecRegistry,\n defaultSimRelUnknownUISpecRenderer,\n} from './components/renderUISpec.js';\nexport type { SimRelUISpecRegistry } from './components/renderUISpec.js';\nexport { simRelCatalog } from './catalog.js';\nexport type { SimRelCatalog, SimRelComponentName } from './catalog.js';\n"],"mappings":"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"}
|
package/dist/simrel.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./simrel-
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./simrel-CVKjhHee.cjs`);exports.GengageSimRel=e.t,exports.createDefaultSimRelUISpecRegistry=e.i,exports.createSimRelWidget=e.n,exports.defaultSimRelUnknownUISpecRenderer=e.a,exports.renderSimRelUISpec=e.o,exports.simRelCatalog=e.r;
|
package/dist/simrel.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
:where(:root,.gengage-chat-root,.gengage-qna-container,.gengage-simrel-container,.gengage-simbut-root){--ds-neutral-0:#fff;--ds-neutral-25:#fcfcfd;--ds-neutral-50:#f6f7fb;--ds-neutral-100:#f3f4f6;--ds-neutral-200:#e5e7eb;--ds-neutral-300:#d0d5dd;--ds-neutral-500:#667085;--ds-neutral-600:#475467;--ds-neutral-700:#344054;--ds-neutral-900:#111827;--ds-radius-2:8px;--ds-radius-3:12px;--ds-radius-4:16px;--ds-radius-5:20px;--ds-radius-6:24px;--ds-radius-pill:999px;--ds-shadow-1:0 1px 2px #1018280a, 0 1px 3px #1018280f;--ds-shadow-2:0 4px 12px #10182814;--ds-shadow-3:0 10px 24px #1018281f;--client-primary:var(--gengage-primary-color,#b7102a);--client-primary-hover:var(--gengage-primary-hover,color-mix(in srgb, var(--client-primary) 88%, black 12%));--client-primary-active:var(--gengage-primary-active,color-mix(in srgb, var(--client-primary) 78%, black 22%));--client-primary-subtle:color-mix(in srgb, var(--client-primary) 12%, white);--client-primary-soft:color-mix(in srgb, var(--client-primary) 20%, white);--client-on-primary:var(--gengage-primary-foreground,#fff);--client-focus-ring:color-mix(in srgb, var(--client-primary) 32%, transparent);--ai-accent-start:#0b24d6;--ai-accent-end:#f768f2;--ai-accent-soft:linear-gradient(135deg, #0b24d614, #f768f214);--surface-page:var(--gengage-page-bg,var(--ds-neutral-50));--surface-shell:var(--gengage-shell-bg,#10131a);--surface-card:var(--gengage-background-color,var(--ds-neutral-0));--surface-card-muted:var(--gengage-surface-soft,#f8fafc);--surface-card-soft:var(--surface-card-muted);--surface-elevated:var(--gengage-surface-elevated,var(--ds-neutral-0));--surface-input:var(--gengage-input-bg,var(--ds-neutral-0));--surface-overlay:var(--gengage-overlay-bg,#10131a85);--text-primary:var(--gengage-foreground-color,var(--ds-neutral-900));--text-secondary:var(--gengage-text-secondary,#4b5563);--text-muted:var(--gengage-text-muted,#6b7280);--text-inverse:var(--gengage-text-inverse,#f9fafb);--border-subtle:var(--gengage-border-subtle,#1118270f);--border-default:var(--gengage-border-color,#1118271a);--border-strong:var(--gengage-border-strong,#1118272e);--shadow-1:var(--gengage-shadow-1,var(--ds-shadow-1));--shadow-2:var(--gengage-shadow-2,var(--ds-shadow-2));--shadow-3:var(--gengage-shadow-3,var(--ds-shadow-3));--radius-control:var(--gengage-control-radius,var(--ds-radius-3));--radius-card:var(--gengage-card-radius,var(--ds-radius-4));--radius-panel:var(--gengage-panel-radius,var(--ds-radius-6));--radius-pill:var(--gengage-pill-radius,var(--ds-radius-pill));--success:var(--gengage-success-color,#16a34a);--warning:var(--gengage-warning-color,#d97706);--error:var(--gengage-error-color,#dc2626);--info:var(--gengage-info-color,#2563eb);--rating:var(--gengage-rating-color,#f5b301);--gengage-primary:var(--client-primary);--gengage-primary-soft:var(--client-primary-soft);--gengage-primary-subtle:var(--client-primary-subtle);--gengage-text-color:var(--text-primary);--gengage-text-secondary:var(--text-secondary);--gengage-hover-color:var(--surface-card-soft);--ds-button-primary-bg:var(--client-primary);--ds-button-primary-bg-hover:var(--client-primary-hover);--ds-button-primary-bg-active:var(--client-primary-active);--ds-button-primary-fg:var(--client-on-primary);--ds-button-primary-border:color-mix(in srgb, var(--client-primary) 82%, black 18%);--ds-button-secondary-bg:var(--surface-card);--ds-button-secondary-bg-hover:var(--client-primary-subtle);--ds-button-secondary-fg:var(--client-primary);--ds-button-secondary-border:color-mix(in srgb, var(--client-primary) 26%, white);--ds-button-ghost-bg:var(--surface-card-muted);--ds-button-ghost-bg-hover:var(--ds-button-ghost-bg);--ds-button-ghost-fg:var(--text-primary);--ds-button-ghost-fg-hover:var(--client-primary);--ds-button-ghost-border:var(--border-default);--ds-button-ghost-border-hover:color-mix(in srgb, var(--client-primary) 18%, white);--ds-panel-bg:var(--surface-card);--ds-panel-border:var(--border-default);--ds-panel-shadow:var(--shadow-1);--ds-panel-radius:var(--radius-panel);--ds-header-bg:var(--surface-card);--ds-header-fg:var(--text-primary);--ds-header-muted:var(--text-muted);--ds-header-border:var(--border-default);--ds-card-bg:var(--surface-card);--ds-card-bg-soft:var(--surface-card-muted);--ds-card-border:var(--border-default);--ds-card-shadow:var(--shadow-1);--ds-card-shadow-hover:var(--shadow-2);--ds-card-radius:var(--radius-card);--ds-card-border-hover:color-mix(in srgb, var(--client-primary) 14%, var(--border-default));--ds-card-bg-hover:color-mix(in srgb, var(--client-primary) 2%, var(--surface-card));--ds-product-card-media-bg:var(--surface-card-muted);--ds-product-card-media-border:var(--border-subtle);--ds-input-bg:var(--surface-input);--ds-input-border:var(--border-default);--ds-input-radius:16px;--ds-input-focus-border:color-mix(in srgb, var(--client-primary) 28%, white);--ds-input-focus-ring:0 0 0 4px var(--client-focus-ring);--ds-input-highlight:inset 0 1px 0 #fff9;--ds-chip-bg:var(--surface-card);--ds-chip-border:var(--border-default);--ds-chip-fg:var(--text-secondary);--ds-chip-bg-hover:var(--surface-card-muted);--ds-chip-bg-active:color-mix(in srgb, var(--client-primary) 8%, white);--ds-chip-border-active:color-mix(in srgb, var(--client-primary) 30%, white);--ds-chip-fg-active:var(--client-primary);--ds-tab-bg:var(--surface-card);--ds-tab-bg-hover:var(--surface-card-muted);--ds-tab-bg-active:color-mix(in srgb, var(--client-primary) 8%, white);--ds-tab-border:var(--border-default);--ds-tab-border-active:color-mix(in srgb, var(--client-primary) 26%, white);--ds-tab-fg:var(--text-secondary);--ds-tab-fg-active:var(--client-primary);--ds-badge-brand-bg:color-mix(in srgb, var(--client-primary) 10%, white);--ds-badge-brand-border:color-mix(in srgb, var(--client-primary) 18%, white);--ds-badge-brand-fg:var(--client-primary);--ds-message-user-bg:color-mix(in srgb, var(--client-primary) 10%, white);--ds-message-user-fg:var(--text-primary);--ds-message-user-border:color-mix(in srgb, var(--client-primary) 18%, var(--border-default));--ds-message-user-shadow:none;--ds-message-assistant-bg:var(--surface-card-soft);--ds-message-assistant-fg:var(--text-primary);--ds-message-assistant-border:var(--border-default);--ds-message-assistant-shadow:none;--ds-message-active-outline:color-mix(in srgb, var(--client-primary) 68%, white);--ds-progress-card-border:var(--border-subtle);--ds-progress-mark-bg:color-mix(in srgb, var(--ai-accent-start) 8%, var(--surface-card));--ds-progress-mark-border:color-mix(in srgb, var(--ai-accent-start) 12%, var(--border-default));--ds-progress-scrollbar-thumb:color-mix(in srgb, var(--text-primary) 18%, transparent);--ds-toast-error-bg:color-mix(in srgb, var(--error) 5%, var(--surface-card));--ds-toast-error-border:color-mix(in srgb, var(--error) 18%, var(--border-default));--ds-toast-error-accent:var(--error);--ds-toast-error-fg:color-mix(in srgb, var(--error) 22%, var(--text-primary));--ds-toast-error-shadow:var(--shadow-3);--ds-comparison-table-bg:var(--surface-card);--ds-comparison-table-border:var(--border-default);--ds-comparison-table-header-bg:var(--surface-card-muted);--ds-comparison-table-header-fg:var(--text-muted);--ds-comparison-table-row-border:var(--border-subtle);--ds-comparison-table-winner-bg:color-mix(in srgb, var(--client-primary) 6%, white);--ds-comparison-table-winner-fg:var(--client-primary)}:where(.gds-panel){background:var(--ds-panel-bg);border:1px solid var(--ds-panel-border);border-radius:var(--ds-panel-radius);box-shadow:var(--ds-panel-shadow)}:where(.gengage-qna-panel,.gengage-chat-panel-topbar,.gengage-chat-chat-header,.gengage-chat-panel-ai-zone>.gengage-chat-ai-top-picks,.gengage-chat-grounding-review){border-color:var(--ds-panel-border)}:where(.gds-btn){border-radius:var(--radius-control);min-height:46px;font:inherit;cursor:pointer;border:0;outline:none;justify-content:center;align-items:center;gap:10px;padding:0 18px;font-weight:700;transition:box-shadow .15s,border-color .15s,background-color .15s,color .15s;display:inline-flex}:where(.gds-btn:hover){transform:none}:where(.gds-btn:focus-visible){outline:2px solid var(--client-focus-ring);outline-offset:2px}:where(.gds-btn:disabled){cursor:not-allowed;opacity:.56;box-shadow:none}:where(.gds-btn-primary){background:var(--ds-button-primary-bg);color:var(--ds-button-primary-fg);border:1px solid var(--ds-button-primary-border)}:where(.gds-btn-primary:hover){background:var(--ds-button-primary-bg-hover)}:where(.gds-btn-secondary){background:var(--ds-button-secondary-bg);color:var(--ds-button-secondary-fg);border:1px solid var(--ds-button-secondary-border)}:where(.gds-btn-secondary:hover){background:var(--ds-button-secondary-bg-hover)}:where(.gds-btn-ghost){background:var(--ds-button-ghost-bg);color:var(--ds-button-ghost-fg);border:1px solid var(--ds-button-ghost-border)}:where(.gds-btn-ghost:hover){background:var(--ds-button-ghost-bg-hover);border-color:var(--ds-button-ghost-border-hover);color:var(--ds-button-ghost-fg-hover)}:where(.gds-chip){border-radius:var(--radius-pill);border:1px solid var(--ds-chip-border);background:var(--ds-chip-bg);color:var(--ds-chip-fg);align-items:center;gap:8px;padding:10px 14px;font-size:13px;font-weight:700;transition:background-color .15s,border-color .15s,color .15s,box-shadow .15s;display:inline-flex}:where(.gds-chip:hover){background:var(--ds-chip-bg-hover);border-color:color-mix(in srgb, var(--client-primary) 16%, var(--ds-chip-border))}:where(.gds-chip:focus-visible){outline:2px solid var(--client-focus-ring);outline-offset:2px}:where(.gds-chip-active){border-color:var(--ds-chip-border-active);background:var(--ds-chip-bg-active);color:var(--ds-chip-fg-active)}:where(.gds-badge){border-radius:var(--radius-pill);letter-spacing:.04em;align-items:center;min-height:28px;padding:0 10px;font-size:11px;font-weight:800;display:inline-flex}:where(.gds-badge-brand){background:var(--ds-badge-brand-bg);color:var(--ds-badge-brand-fg);border:1px solid var(--ds-badge-brand-border)}:where(.gds-shell-header){background:var(--ds-header-bg);color:var(--ds-header-fg);border-bottom:1px solid var(--ds-header-border);box-shadow:none;justify-content:space-between;align-items:center;gap:14px;padding:14px 16px;display:flex}:where(.gds-shell-header [data-gengage-part=chat-header-powered-by]){color:var(--ds-header-muted)}:where(.gds-icon-btn){border-radius:var(--radius-control);width:42px;min-width:42px;height:42px;min-height:42px;padding:0}:where(.gds-input-shell){border-radius:var(--ds-input-radius);border:1px solid var(--ds-input-border);background:var(--ds-input-bg);min-height:52px;box-shadow:var(--ds-input-highlight);align-items:center;gap:12px;padding:6px 6px 6px 14px;transition:border-color .15s,box-shadow .15s,background-color .15s;display:flex}:where(.gds-input-shell:focus-within){border-color:var(--ds-input-focus-border);box-shadow:var(--ds-input-focus-ring)}:where(.gds-tab){border-radius:var(--radius-pill);border:1px solid var(--ds-tab-border);background:var(--ds-tab-bg);color:var(--ds-tab-fg);align-items:center;gap:8px;padding:10px 14px;font-size:13px;font-weight:700;transition:background-color .15s,border-color .15s,color .15s;display:inline-flex}:where(.gds-tab:hover){background:var(--ds-tab-bg-hover)}:where(.gds-tab:focus-visible){outline:2px solid var(--client-focus-ring);outline-offset:2px}:where(.gds-tab[aria-selected=true],.gds-tab.is-active){color:var(--ds-tab-fg-active);border-color:var(--ds-tab-border-active);background:var(--ds-tab-bg-active)}:where(.gds-toolbar){flex-wrap:wrap;align-items:center;gap:10px;display:flex}:where(.gds-toolbar-compact){gap:8px}:where(.gds-card){background:var(--ds-card-bg);border:1px solid var(--ds-card-border);border-radius:var(--ds-card-radius);box-shadow:var(--ds-card-shadow);transition:border-color .15s,box-shadow .15s,background-color .15s}:where(.gds-card-interactive),:where(.gds-clickable){cursor:pointer}:where(.gds-card-interactive:hover){background:var(--ds-card-bg-hover);border-color:var(--ds-card-border-hover);box-shadow:var(--ds-card-shadow-hover)}:where(.gds-card-soft){background:var(--ds-card-bg-soft)}:where(.gds-product-card){border-radius:16px}:where(.gds-evidence-card){background:var(--surface-card);border:1px solid var(--border-default);border-radius:var(--radius-control);box-shadow:var(--shadow-1)}:where(.gds-evidence-card-success){background:color-mix(in srgb, var(--success) 10%, white);border-color:color-mix(in srgb, var(--success) 22%, var(--border-default))}:where(.gds-evidence-card-danger){background:color-mix(in srgb, var(--error) 8%, white);border-color:color-mix(in srgb, var(--error) 20%, var(--border-default))}:where(.gds-evidence-card-warning){background:color-mix(in srgb, var(--warning) 10%, white);border-color:color-mix(in srgb, var(--warning) 22%, var(--border-default))}:where(.gds-message){border-radius:calc(var(--radius-control) + 4px);word-wrap:break-word;overflow-wrap:break-word;border:1px solid #0000;max-width:85%;padding:14px 16px;font-size:14px;font-weight:400;line-height:1.55;animation:.3s cubic-bezier(.2,.7,.2,1) gengage-chat-msg-in}:where(.gds-message-user){background:var(--ds-message-user-bg);color:var(--ds-message-user-fg);border-color:var(--ds-message-user-border);box-shadow:var(--ds-message-user-shadow);border-bottom-right-radius:6px;align-self:flex-end}:where(.gds-message-assistant){background:var(--ds-message-assistant-bg);color:var(--ds-message-assistant-fg);border-color:var(--ds-message-assistant-border);box-shadow:var(--ds-message-assistant-shadow);border-bottom-left-radius:6px;align-self:flex-start;min-width:120px}:where(.gds-message-active){outline:2px solid var(--ds-message-active-outline);outline-offset:-2px}:where(.gds-ai-card){background:var(--surface-card);border:1px solid var(--ds-progress-card-border);border-radius:20px;gap:16px;padding:18px;display:grid}:where(.gds-ai-mark){background:var(--ds-progress-mark-bg);width:42px;height:42px;color:var(--ai-accent-start);border:1px solid var(--ds-progress-mark-border);border-radius:14px;place-items:center;display:grid}:where(.gds-ai-loader-row){align-items:center;gap:12px;display:flex}:where(.gds-progress-loader){border:1px solid color-mix(in srgb, var(--ai-accent-start) 10%, var(--border-default));background:linear-gradient(110deg, var(--surface-card-soft) 8%, color-mix(in srgb, var(--ai-accent-start) 5%, var(--surface-card)) 18%, var(--surface-card-soft) 33%);width:fit-content;max-width:100%;color:var(--text-secondary);background-size:220% 100%;border-radius:14px;align-items:center;gap:10px;padding:8px 14px;animation:1.8s linear infinite gds-progress-shimmer;display:inline-flex}:where(.gds-progress-loader-chat){align-self:flex-start;max-width:85%}:where(.gds-progress-loader-panel){justify-content:flex-start;width:100%}:where(.gds-progress-label){min-width:0;color:var(--text-secondary);word-break:break-word;font-size:13px;line-height:1.5}:where(.gds-progress-dots){flex-shrink:0;align-items:center;gap:6px;display:inline-flex}:where(.gds-progress-dot){background:color-mix(in srgb, var(--ai-accent-start) 20%, var(--text-muted));border-radius:50%;width:8px;height:8px;animation:1.2s ease-in-out infinite gds-progress-dot-pulse;display:inline-block}:where(.gds-progress-dot:nth-child(2)){animation-delay:.2s}:where(.gds-progress-dot:nth-child(3)){animation-delay:.4s}:where(.gds-ai-status){border-radius:calc(var(--radius-control) + 2px);border:1px solid var(--border-default);background:var(--surface-card-muted);max-width:85%;color:var(--text-secondary);border-bottom-left-radius:6px;align-self:flex-start;align-items:center;gap:8px;padding:10px 14px;display:flex}:where(.gds-ai-status-text){color:var(--text-secondary);font-size:13px;font-style:italic}:where(.gds-ai-dot-loader){align-items:center;gap:5px;display:inline-flex}:where(.gds-ai-dot-loader span){background:color-mix(in srgb, var(--ai-accent-start) 38%, var(--text-muted));border-radius:50%;width:6px;height:6px;animation:1.2s ease-in-out infinite gds-progress-dot-pulse;display:inline-block}:where(.gds-ai-dot-loader span:nth-child(2)){animation-delay:.2s}:where(.gds-ai-dot-loader span:nth-child(3)){animation-delay:.4s}:where(.gds-ai-spinner){border:2.5px solid var(--border-default);border-top-color:var(--ai-accent-start);border-radius:50%;flex-shrink:0;width:26px;height:26px;animation:.72s linear infinite gds-spin}@keyframes gds-spin{to{transform:rotate(360deg)}}@keyframes gds-progress-shimmer{0%{background-position:200% 0}to{background-position:-20% 0}}@keyframes gds-progress-dot-pulse{0%,to{opacity:.45;transform:scale(.7)}50%{opacity:1;transform:scale(1)}}@media (prefers-reduced-motion:reduce){:where(.gds-progress-loader){animation:none}:where(.gds-progress-dot){opacity:.85;animation:none;transform:scale(1)}:where(.gds-ai-spinner){animation:none}:where(.gds-ai-dot-loader span){opacity:.85;animation:none}}:where(.gds-menu){border-radius:var(--radius-card);border:1px solid var(--border-default);background:var(--surface-elevated);box-shadow:var(--shadow-3);padding:4px}:where(.gds-menu-option){border-radius:calc(var(--radius-control) - 4px);width:100%;color:var(--text-primary);font:inherit;text-align:left;cursor:pointer;background:0 0;border:0;align-items:center;gap:8px;padding:8px 10px;font-size:12px;font-weight:500;line-height:1.25;transition:background-color .15s,color .15s;display:flex}:where(.gds-menu-option:hover){background:var(--surface-card-muted)}:where(.gds-menu-option:focus-visible){outline:2px solid var(--client-focus-ring);outline-offset:2px}:where(.gds-menu-option-active){background:var(--client-primary-subtle);color:var(--client-primary);font-weight:600}:where(.gds-comparison-table){border-collapse:separate;border-spacing:0;border:1px solid var(--ds-comparison-table-border);background:var(--ds-comparison-table-bg);border-radius:20px;width:100%;overflow:hidden}:where(.gds-comparison-table th,.gds-comparison-table td){text-align:left;border-bottom:1px solid var(--ds-comparison-table-row-border);padding:14px 16px;font-size:13px}:where(.gds-comparison-table th){background:var(--ds-comparison-table-header-bg);text-transform:uppercase;letter-spacing:.08em;color:var(--ds-comparison-table-header-fg);font-size:11px}:where(.gds-comparison-table tr:last-child td){border-bottom:0}:where(.gds-comparison-table-winner-cell){background:var(--ds-comparison-table-winner-bg);color:var(--ds-comparison-table-winner-fg);font-weight:800}.gengage-simrel-container{font-family:var(--gengage-font-family,"Plus Jakarta Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif);font-size:var(--gengage-font-size,14px);color:var(--text-primary);--_gengage-border-color:var(--border-default);--_gengage-discount-color:var(--gengage-discount-color,var(--client-primary));--_gengage-text-primary:var(--text-primary);--_gengage-text-secondary:var(--text-secondary);--_gengage-surface-base:var(--surface-card);--_gengage-surface-muted:var(--surface-card-muted);--_gengage-surface-secondary:var(--surface-card-soft);--_gengage-rating-color:var(--rating);--_gengage-scrollbar-thumb:var(--ds-progress-scrollbar-thumb)}.gengage-simrel-uispec>*{animation:.2s cubic-bezier(.2,.72,.2,1) both gengage-simrel-enter}@keyframes gengage-simrel-enter{0%{opacity:0;transform:translateY(8px)scale(.99)}to{opacity:1;transform:translateY(0)scale(1)}}.gengage-simrel-grid{grid-template-columns:repeat(var(--gengage-simrel-columns,4), minmax(0, 1fr));gap:12px;padding:8px 0 72px;display:grid;overflow-x:visible}.gengage-simrel-grid>*{min-width:0}.gengage-simrel-grid .gengage-simrel-empty{grid-column:1/-1}.gengage-simrel-card{border:1px solid var(--_gengage-border-color);border-radius:var(--gengage-simrel-card-radius,var(--ds-card-radius));background:var(--ds-card-bg);cursor:pointer;box-shadow:var(--ds-card-shadow);flex-direction:column;width:100%;min-width:0;max-width:none;transition:box-shadow .2s,transform .1s;display:flex;position:relative;overflow:hidden}.gengage-simrel-card:hover{box-shadow:var(--ds-card-shadow-hover);transform:translateY(-2px)}.gengage-simrel-card-image{background:var(--_gengage-surface-base);position:relative;overflow:hidden}.gengage-simrel-card-image img{object-fit:contain;box-sizing:border-box;width:100%;height:120px;padding:8px;display:block}.gengage-simrel-badge{background:var(--_gengage-discount-color);color:var(--client-on-primary);border-radius:4px;padding:2px 8px;font-size:12px;font-weight:600;position:absolute;top:8px;left:8px}.gengage-simrel-card-info{text-align:center;flex-direction:column;gap:4px;padding:8px 10px 10px;display:flex}.gengage-simrel-card-brand{color:var(--_gengage-text-secondary);font-size:10px}.gengage-simrel-card-name{color:var(--_gengage-text-primary);-webkit-line-clamp:2;line-clamp:2;text-overflow:ellipsis;-webkit-box-orient:vertical;min-height:2.7em;font-size:13px;font-weight:600;line-height:1.35;display:-webkit-box;overflow:hidden}.gengage-simrel-card-rating{color:var(--_gengage-rating-color);font-size:12px}.gengage-simrel-card-rating .gengage-star-half{display:inline-block;position:relative}.gengage-simrel-card-rating .gengage-star-half>span{width:.5em;position:absolute;top:0;left:0;overflow:hidden}.gengage-simrel-card-review-count{color:var(--_gengage-text-secondary)}.gengage-simrel-card-price{flex-wrap:wrap;justify-content:center;align-items:baseline;gap:2px 4px;padding-top:4px;display:flex}.gengage-simrel-card-price-original{color:var(--_gengage-text-secondary);font-size:12px;font-weight:400;text-decoration:line-through}.gengage-simrel-card-price-current{color:var(--_gengage-text-primary);font-size:16px;font-weight:800}.gengage-simrel-card-cta{text-align:center;border:none;border-top:1px solid var(--_gengage-surface-muted);width:100%;color:var(--client-primary);letter-spacing:.01em;cursor:pointer;background:0 0;border-radius:0;margin-top:auto;padding:8px 10px;font-family:inherit;font-size:12px;font-weight:700;transition:color .15s,background .15s;display:block}.gengage-simrel-card-cta:hover{color:var(--client-primary-hover);background:var(--surface-card-soft)}.gengage-simrel-atc{border:1px solid var(--_gengage-border-color);border-radius:var(--radius-control,12px)}.gengage-simrel-atc-button{border:1.5px solid var(--client-primary);border-radius:var(--radius-control,12px);background:var(--surface-card);width:calc(100% - 20px);min-height:44px;color:var(--client-primary);letter-spacing:.01em;justify-content:center;align-items:center;margin:0 10px 10px;padding:10px 14px;font-size:13px;font-weight:700;display:flex}.gengage-simrel-atc-button:hover{background:var(--surface-card-soft);color:var(--client-primary-hover)}.gengage-simrel-card .gengage-simrel-atc{border-top:1px solid var(--_gengage-border-color);background:0 0;border-bottom:none;border-left:none;border-right:none;border-radius:0;justify-content:center;align-items:center;width:100%;margin:0;display:flex}.gengage-simrel-card-oos{text-align:center;border-top:1px solid var(--_gengage-surface-muted);width:100%;color:var(--_gengage-text-secondary);letter-spacing:.01em;padding:8px 10px;font-size:12px;font-weight:600}.gengage-simrel-card--out-of-stock{opacity:.55}.gengage-simrel-card--out-of-stock .gengage-simrel-card-image img{filter:grayscale(40%)}.gengage-simrel-quick-actions{flex-wrap:wrap;gap:8px;padding:6px 12px 12px;display:flex}.gengage-simrel-quick-action{border:1px solid var(--_gengage-border-color);background:var(--_gengage-surface-muted);color:var(--_gengage-text-primary);cursor:pointer;border-radius:999px;min-height:40px;padding:8px 10px;font-size:12px;font-weight:600;transition:transform .12s,box-shadow .12s,border-color .12s}.gengage-simrel-quick-action:hover{border-color:var(--client-primary);color:var(--client-primary);box-shadow:var(--shadow-2);transform:translateY(-1px)}.gengage-simrel-empty{text-align:center;width:100%;color:var(--_gengage-text-secondary);padding:40px 20px}.gengage-simrel-error{text-align:center;color:var(--_gengage-text-secondary);padding:24px 16px;font-size:13px;line-height:1.5}.gengage-simrel-retry{border:1px solid var(--_gengage-border-color);background:var(--_gengage-surface-base);cursor:pointer;color:var(--_gengage-text-primary);border-radius:6px;margin-top:8px;padding:6px 16px;font-size:13px;transition:background .15s;display:inline-block}.gengage-simrel-retry:hover{background:var(--_gengage-surface-secondary)}.gengage-simrel-tabs{-webkit-overflow-scrolling:touch;scrollbar-width:thin;scrollbar-color:var(--_gengage-scrollbar-thumb) transparent;border-bottom:none;gap:10px;margin-bottom:0;padding-left:0;padding-right:24px;transition:padding .15s;display:flex;overflow-x:auto}.gengage-simrel-tabs.gengage-simrel-tabs--peek-right{padding-right:6px}.gengage-simrel-tabs.gengage-simrel-tabs--peek-left{padding-left:6px}.gengage-simrel-tabs::-webkit-scrollbar{height:3px}.gengage-simrel-tabs::-webkit-scrollbar-thumb{background:var(--_gengage-scrollbar-thumb);border-radius:3px}.gengage-simrel-tab{border:1px solid var(--_gengage-border-color);background:var(--_gengage-surface-muted);min-height:40px;color:var(--_gengage-text-secondary);opacity:1;font-size:inherit;cursor:pointer;white-space:nowrap;box-shadow:var(--shadow-1);border-radius:999px;margin-bottom:0;padding:8px 14px;font-family:inherit;transition:color .2s,border-color .2s,background .2s,box-shadow .2s}.gengage-simrel-tab:hover{color:var(--client-primary);border-color:var(--client-primary);background:var(--_gengage-surface-secondary)}.gengage-simrel-tab--active{color:var(--client-primary);border-color:var(--client-primary);background:var(--_gengage-surface-secondary);box-shadow:var(--shadow-2);font-weight:600}.gengage-simrel-tabs-wrapper{border-bottom:1px solid var(--_gengage-border-color);align-items:center;margin-bottom:16px;padding-bottom:12px;display:flex;position:relative}.gengage-simrel-tabs-wrapper .gengage-simrel-tabs{flex:1;min-width:0}.gengage-simrel-tabs-arrow{background:var(--_gengage-surface-base);width:28px;height:40px;color:var(--_gengage-text-secondary);cursor:pointer;border:none;border-radius:4px;flex:none;justify-content:center;align-self:center;align-items:center;padding:0;transition:color .15s,background .15s;display:flex}.gengage-simrel-tabs-arrow:hover{color:var(--client-primary);background:var(--_gengage-surface-secondary)}.gengage-simrel-tabs-arrow--left{margin-right:4px}.gengage-simrel-tabs-arrow--right{margin-left:4px}.gengage-simrel-loading{justify-content:center;padding:40px;display:flex}.gengage-simrel-spinner{border:3px solid var(--_gengage-border-color);border-top-color:var(--client-primary);border-radius:50%;width:32px;height:32px;animation:.7s linear infinite gengage-simrel-spin}@keyframes gengage-simrel-spin{to{transform:rotate(360deg)}}@media (width<=768px){.gengage-simrel-grid{-webkit-overflow-scrolling:touch;scrollbar-width:none;scroll-snap-type:x mandatory;gap:10px;padding-top:8px;display:flex;overflow-x:auto}.gengage-simrel-grid::-webkit-scrollbar{display:none}.gengage-simrel-grid>*{scroll-snap-align:center;min-width:unset;flex:none}.gengage-simrel-card{width:200px;min-width:200px;max-width:200px}.gengage-simrel-grid .gengage-simrel-empty{flex:1 0 100%;min-width:100%}.gengage-simrel-card-image img{height:140px}.gengage-simrel-card-cta,.gengage-simrel-quick-action,.gengage-simrel-tab{min-height:44px}.gengage-simrel-card{border:1px solid var(--_gengage-border-color)}.gengage-simrel-card-price-current{font-size:14px}}.gengage-simrel-card:focus-visible,.gengage-simrel-tab:focus-visible,.gengage-simrel-quick-action:focus-visible,.gengage-simrel-atc-button:focus-visible{outline:2px solid var(--client-primary);outline-offset:2px}@media (prefers-reduced-motion:reduce){.gengage-simrel-uispec>*,.gengage-simrel-card,.gengage-simrel-card-image img,.gengage-simrel-quick-action,.gengage-simrel-spinner,.gengage-simrel-tabs-arrow{transition:none!important;animation:none!important;transform:none!important}}
|
|
1
|
+
:where(:root,.gengage-chat-root,.gengage-qna-container,.gengage-simrel-container,.gengage-simbut-root){--ds-neutral-0:#fff;--ds-neutral-25:#fcfcfd;--ds-neutral-50:#f6f7fb;--ds-neutral-100:#f3f4f6;--ds-neutral-200:#e5e7eb;--ds-neutral-300:#d0d5dd;--ds-neutral-500:#667085;--ds-neutral-600:#475467;--ds-neutral-700:#344054;--ds-neutral-900:#111827;--ds-radius-2:8px;--ds-radius-3:12px;--ds-radius-4:16px;--ds-radius-5:20px;--ds-radius-6:24px;--ds-radius-pill:999px;--ds-shadow-1:0 1px 2px #1018280a, 0 1px 3px #1018280f;--ds-shadow-2:0 4px 12px #10182814;--ds-shadow-3:0 10px 24px #1018281f;--client-primary:var(--gengage-primary-color,#b7102a);--client-primary-hover:var(--gengage-primary-hover,color-mix(in srgb, var(--client-primary) 88%, black 12%));--client-primary-active:var(--gengage-primary-active,color-mix(in srgb, var(--client-primary) 78%, black 22%));--client-primary-subtle:color-mix(in srgb, var(--client-primary) 12%, white);--client-primary-soft:color-mix(in srgb, var(--client-primary) 20%, white);--client-on-primary:var(--gengage-primary-foreground,#fff);--client-focus-ring:color-mix(in srgb, var(--client-primary) 32%, transparent);--ai-accent-start:#0b24d6;--ai-accent-end:#f768f2;--ai-accent-soft:linear-gradient(135deg, #0b24d614, #f768f214);--surface-page:var(--gengage-page-bg,var(--ds-neutral-50));--surface-shell:var(--gengage-shell-bg,#10131a);--surface-card:var(--gengage-background-color,var(--ds-neutral-0));--surface-card-muted:var(--gengage-surface-soft,#f8fafc);--surface-card-soft:var(--surface-card-muted);--surface-elevated:var(--gengage-surface-elevated,var(--ds-neutral-0));--surface-input:var(--gengage-input-bg,var(--ds-neutral-0));--surface-overlay:var(--gengage-overlay-bg,#10131a85);--text-primary:var(--gengage-foreground-color,var(--ds-neutral-900));--text-secondary:var(--gengage-text-secondary,#4b5563);--text-muted:var(--gengage-text-muted,#6b7280);--text-inverse:var(--gengage-text-inverse,#f9fafb);--border-subtle:var(--gengage-border-subtle,#1118270f);--border-default:var(--gengage-border-color,#1118271a);--border-strong:var(--gengage-border-strong,#1118272e);--shadow-1:var(--gengage-shadow-1,var(--ds-shadow-1));--shadow-2:var(--gengage-shadow-2,var(--ds-shadow-2));--shadow-3:var(--gengage-shadow-3,var(--ds-shadow-3));--radius-control:var(--gengage-control-radius,var(--ds-radius-3));--radius-card:var(--gengage-card-radius,var(--ds-radius-4));--radius-panel:var(--gengage-panel-radius,var(--ds-radius-6));--radius-pill:var(--gengage-pill-radius,var(--ds-radius-pill));--success:var(--gengage-success-color,#16a34a);--warning:var(--gengage-warning-color,#d97706);--error:var(--gengage-error-color,#dc2626);--info:var(--gengage-info-color,#2563eb);--rating:var(--gengage-rating-color,#f5b301);--gengage-primary:var(--client-primary);--gengage-primary-soft:var(--client-primary-soft);--gengage-primary-subtle:var(--client-primary-subtle);--gengage-text-color:var(--text-primary);--gengage-text-secondary:var(--text-secondary);--gengage-hover-color:var(--surface-card-soft);--ds-button-primary-bg:var(--client-primary);--ds-button-primary-bg-hover:var(--client-primary-hover);--ds-button-primary-bg-active:var(--client-primary-active);--ds-button-primary-fg:var(--client-on-primary);--ds-button-primary-border:color-mix(in srgb, var(--client-primary) 82%, black 18%);--ds-button-secondary-bg:var(--surface-card);--ds-button-secondary-bg-hover:var(--client-primary-subtle);--ds-button-secondary-fg:var(--client-primary);--ds-button-secondary-border:color-mix(in srgb, var(--client-primary) 26%, white);--ds-button-ghost-bg:var(--surface-card-muted);--ds-button-ghost-bg-hover:var(--ds-button-ghost-bg);--ds-button-ghost-fg:var(--text-primary);--ds-button-ghost-fg-hover:var(--client-primary);--ds-button-ghost-border:var(--border-default);--ds-button-ghost-border-hover:color-mix(in srgb, var(--client-primary) 18%, white);--ds-panel-bg:var(--surface-card);--ds-panel-border:var(--border-default);--ds-panel-shadow:var(--shadow-1);--ds-panel-radius:var(--radius-panel);--ds-header-bg:var(--surface-card);--ds-header-fg:var(--text-primary);--ds-header-muted:var(--text-muted);--ds-header-border:var(--border-default);--ds-card-bg:var(--surface-card);--ds-card-bg-soft:var(--surface-card-muted);--ds-card-border:var(--border-default);--ds-card-shadow:var(--shadow-1);--ds-card-shadow-hover:var(--shadow-2);--ds-card-radius:var(--radius-card);--ds-card-border-hover:color-mix(in srgb, var(--client-primary) 14%, var(--border-default));--ds-card-bg-hover:color-mix(in srgb, var(--client-primary) 2%, var(--surface-card));--ds-product-card-media-bg:var(--surface-card-muted);--ds-product-card-media-border:var(--border-subtle);--ds-input-bg:var(--surface-input);--ds-input-border:var(--border-default);--ds-input-radius:16px;--ds-input-focus-border:color-mix(in srgb, var(--client-primary) 28%, white);--ds-input-focus-ring:0 0 0 4px var(--client-focus-ring);--ds-input-highlight:inset 0 1px 0 #fff9;--ds-chip-bg:var(--surface-card);--ds-chip-border:var(--border-default);--ds-chip-fg:var(--text-secondary);--ds-chip-bg-hover:var(--surface-card-muted);--ds-chip-bg-active:color-mix(in srgb, var(--client-primary) 8%, white);--ds-chip-border-active:color-mix(in srgb, var(--client-primary) 30%, white);--ds-chip-fg-active:var(--client-primary);--ds-tab-bg:var(--surface-card);--ds-tab-bg-hover:var(--surface-card-muted);--ds-tab-bg-active:color-mix(in srgb, var(--client-primary) 8%, white);--ds-tab-border:var(--border-default);--ds-tab-border-active:color-mix(in srgb, var(--client-primary) 26%, white);--ds-tab-fg:var(--text-secondary);--ds-tab-fg-active:var(--client-primary);--ds-badge-brand-bg:color-mix(in srgb, var(--client-primary) 10%, white);--ds-badge-brand-border:color-mix(in srgb, var(--client-primary) 18%, white);--ds-badge-brand-fg:var(--client-primary);--ds-message-user-bg:color-mix(in srgb, var(--client-primary) 10%, white);--ds-message-user-fg:var(--text-primary);--ds-message-user-border:color-mix(in srgb, var(--client-primary) 18%, var(--border-default));--ds-message-user-shadow:none;--ds-message-assistant-bg:var(--surface-card-soft);--ds-message-assistant-fg:var(--text-primary);--ds-message-assistant-border:var(--border-default);--ds-message-assistant-shadow:none;--ds-message-active-outline:color-mix(in srgb, var(--client-primary) 68%, white);--ds-progress-card-border:var(--border-subtle);--ds-progress-mark-bg:color-mix(in srgb, var(--ai-accent-start) 8%, var(--surface-card));--ds-progress-mark-border:color-mix(in srgb, var(--ai-accent-start) 12%, var(--border-default));--ds-progress-scrollbar-thumb:color-mix(in srgb, var(--text-primary) 18%, transparent);--ds-toast-error-bg:color-mix(in srgb, var(--error) 5%, var(--surface-card));--ds-toast-error-border:color-mix(in srgb, var(--error) 18%, var(--border-default));--ds-toast-error-accent:var(--error);--ds-toast-error-fg:color-mix(in srgb, var(--error) 22%, var(--text-primary));--ds-toast-error-shadow:var(--shadow-3);--ds-comparison-table-bg:var(--surface-card);--ds-comparison-table-border:var(--border-default);--ds-comparison-table-header-bg:var(--surface-card-muted);--ds-comparison-table-header-fg:var(--text-muted);--ds-comparison-table-row-border:var(--border-subtle);--ds-comparison-table-winner-bg:color-mix(in srgb, var(--client-primary) 6%, white);--ds-comparison-table-winner-fg:var(--client-primary)}:where(.gds-panel){background:var(--ds-panel-bg);border:1px solid var(--ds-panel-border);border-radius:var(--ds-panel-radius);box-shadow:var(--ds-panel-shadow)}:where(.gengage-qna-panel,.gengage-chat-panel-topbar,.gengage-chat-chat-header,.gengage-chat-panel-ai-zone>.gengage-chat-ai-top-picks,.gengage-chat-grounding-review){border-color:var(--ds-panel-border)}:where(.gds-btn){border-radius:var(--radius-control);min-height:46px;font:inherit;cursor:pointer;border:0;outline:none;justify-content:center;align-items:center;gap:10px;padding:0 18px;font-weight:700;transition:box-shadow .15s,border-color .15s,background-color .15s,color .15s;display:inline-flex}:where(.gds-btn:hover){transform:none}:where(.gds-btn:focus-visible){outline:2px solid var(--client-focus-ring);outline-offset:2px}:where(.gds-btn:disabled){cursor:not-allowed;opacity:.56;box-shadow:none}:where(.gds-btn-primary){background:var(--ds-button-primary-bg);color:var(--ds-button-primary-fg);border:1px solid var(--ds-button-primary-border)}:where(.gds-btn-primary:hover){background:var(--ds-button-primary-bg-hover)}:where(.gds-btn-secondary){background:var(--ds-button-secondary-bg);color:var(--ds-button-secondary-fg);border:1px solid var(--ds-button-secondary-border)}:where(.gds-btn-secondary:hover){background:var(--ds-button-secondary-bg-hover)}:where(.gds-btn-ghost){background:var(--ds-button-ghost-bg);color:var(--ds-button-ghost-fg);border:1px solid var(--ds-button-ghost-border)}:where(.gds-btn-ghost:hover){background:var(--ds-button-ghost-bg-hover);border-color:var(--ds-button-ghost-border-hover);color:var(--ds-button-ghost-fg-hover)}:where(.gds-chip){border-radius:var(--radius-pill);border:1px solid var(--ds-chip-border);background:var(--ds-chip-bg);color:var(--ds-chip-fg);align-items:center;gap:8px;padding:10px 14px;font-size:13px;font-weight:700;transition:background-color .15s,border-color .15s,color .15s,box-shadow .15s;display:inline-flex}:where(.gds-chip:hover){background:var(--ds-chip-bg-hover);border-color:color-mix(in srgb, var(--client-primary) 16%, var(--ds-chip-border))}:where(.gds-chip:focus-visible){outline:2px solid var(--client-focus-ring);outline-offset:2px}:where(.gds-chip-active){border-color:var(--ds-chip-border-active);background:var(--ds-chip-bg-active);color:var(--ds-chip-fg-active)}:where(.gds-badge){border-radius:var(--radius-pill);letter-spacing:.04em;align-items:center;min-height:28px;padding:0 10px;font-size:11px;font-weight:800;display:inline-flex}:where(.gds-badge-brand){background:var(--ds-badge-brand-bg);color:var(--ds-badge-brand-fg);border:1px solid var(--ds-badge-brand-border)}:where(.gds-shell-header){background:var(--ds-header-bg);color:var(--ds-header-fg);border-bottom:1px solid var(--ds-header-border);box-shadow:none;justify-content:space-between;align-items:center;gap:14px;padding:14px 16px;display:flex}:where(.gds-shell-header [data-gengage-part=chat-header-powered-by]){color:var(--ds-header-muted)}:where(.gds-icon-btn){border-radius:var(--radius-control);width:42px;min-width:42px;height:42px;min-height:42px;padding:0}:where(.gds-input-shell){border-radius:var(--ds-input-radius);border:1px solid var(--ds-input-border);background:var(--ds-input-bg);min-height:52px;box-shadow:var(--ds-input-highlight);align-items:center;gap:12px;padding:6px 6px 6px 14px;transition:border-color .15s,box-shadow .15s,background-color .15s;display:flex}:where(.gds-input-shell:focus-within){border-color:var(--ds-input-focus-border);box-shadow:var(--ds-input-focus-ring)}:where(.gds-tab){border-radius:var(--radius-pill);border:1px solid var(--ds-tab-border);background:var(--ds-tab-bg);color:var(--ds-tab-fg);align-items:center;gap:8px;padding:10px 14px;font-size:13px;font-weight:700;transition:background-color .15s,border-color .15s,color .15s;display:inline-flex}:where(.gds-tab:hover){background:var(--ds-tab-bg-hover)}:where(.gds-tab:focus-visible){outline:2px solid var(--client-focus-ring);outline-offset:2px}:where(.gds-tab[aria-selected=true],.gds-tab.is-active){color:var(--ds-tab-fg-active);border-color:var(--ds-tab-border-active);background:var(--ds-tab-bg-active)}:where(.gds-toolbar){flex-wrap:wrap;align-items:center;gap:10px;display:flex}:where(.gds-toolbar-compact){gap:8px}:where(.gds-card){background:var(--ds-card-bg);border:1px solid var(--ds-card-border);border-radius:var(--ds-card-radius);box-shadow:var(--ds-card-shadow);transition:border-color .15s,box-shadow .15s,background-color .15s}:where(.gds-card-interactive),:where(.gds-clickable){cursor:pointer}:where(.gds-card-interactive:hover){background:var(--ds-card-bg-hover);border-color:var(--ds-card-border-hover);box-shadow:var(--ds-card-shadow-hover)}:where(.gds-card-soft){background:var(--ds-card-bg-soft)}:where(.gds-product-card){border-radius:16px}:where(.gds-evidence-card){background:var(--surface-card);border:1px solid var(--border-default);border-radius:var(--radius-control);box-shadow:var(--shadow-1)}:where(.gds-evidence-card-success){background:color-mix(in srgb, var(--success) 10%, white);border-color:color-mix(in srgb, var(--success) 22%, var(--border-default))}:where(.gds-evidence-card-danger){background:color-mix(in srgb, var(--error) 8%, white);border-color:color-mix(in srgb, var(--error) 20%, var(--border-default))}:where(.gds-evidence-card-warning){background:color-mix(in srgb, var(--warning) 10%, white);border-color:color-mix(in srgb, var(--warning) 22%, var(--border-default))}:where(.gds-message){border-radius:calc(var(--radius-control) + 4px);word-wrap:break-word;overflow-wrap:break-word;border:1px solid #0000;max-width:85%;padding:14px 16px;font-size:14px;font-weight:400;line-height:1.55;animation:.3s cubic-bezier(.2,.7,.2,1) gengage-chat-msg-in}:where(.gds-message-user){background:var(--ds-message-user-bg);color:var(--ds-message-user-fg);border-color:var(--ds-message-user-border);box-shadow:var(--ds-message-user-shadow);border-bottom-right-radius:6px;align-self:flex-end}:where(.gds-message-assistant){background:var(--ds-message-assistant-bg);color:var(--ds-message-assistant-fg);border-color:var(--ds-message-assistant-border);box-shadow:var(--ds-message-assistant-shadow);border-bottom-left-radius:6px;align-self:flex-start;min-width:120px}:where(.gds-message-active){outline:2px solid var(--ds-message-active-outline);outline-offset:-2px}:where(.gds-ai-card){background:var(--surface-card);border:1px solid var(--ds-progress-card-border);border-radius:20px;gap:16px;padding:18px;display:grid}:where(.gds-ai-mark){background:var(--ds-progress-mark-bg);width:42px;height:42px;color:var(--ai-accent-start);border:1px solid var(--ds-progress-mark-border);border-radius:14px;place-items:center;display:grid}:where(.gds-ai-loader-row){align-items:center;gap:12px;display:flex}:where(.gds-progress-loader){border:1px solid color-mix(in srgb, var(--ai-accent-start) 10%, var(--border-default));background:linear-gradient(110deg, var(--surface-card-soft) 8%, color-mix(in srgb, var(--ai-accent-start) 5%, var(--surface-card)) 18%, var(--surface-card-soft) 33%);width:fit-content;max-width:100%;color:var(--text-secondary);background-size:220% 100%;border-radius:14px;align-items:center;gap:10px;padding:8px 14px;animation:1.8s linear infinite gds-progress-shimmer;display:inline-flex}:where(.gds-progress-loader-chat){box-sizing:border-box;align-self:flex-start;width:100%;min-width:0;max-width:85%;display:flex}:where(.gds-progress-loader-chat) .gds-progress-label{overflow-wrap:anywhere;flex:auto;min-width:0}:where(.gds-progress-loader-panel){box-sizing:border-box;justify-content:flex-start;align-items:center;width:100%;min-width:0;max-width:100%;display:flex}:where(.gds-progress-loader-panel) .gds-progress-label{overflow-wrap:anywhere;flex:auto;min-width:0}:where(.gds-progress-label){min-width:0;color:var(--text-secondary);word-break:break-word;font-size:13px;line-height:1.5}:where(.gds-progress-dots){flex-shrink:0;align-items:center;gap:6px;display:inline-flex}:where(.gds-progress-dot){background:color-mix(in srgb, var(--ai-accent-start) 20%, var(--text-muted));border-radius:50%;width:8px;height:8px;animation:1.2s ease-in-out infinite gds-progress-dot-pulse;display:inline-block}:where(.gds-progress-dot:nth-child(2)){animation-delay:.2s}:where(.gds-progress-dot:nth-child(3)){animation-delay:.4s}:where(.gds-ai-status){border-radius:calc(var(--radius-control) + 2px);border:1px solid var(--border-default);background:var(--surface-card-muted);max-width:85%;color:var(--text-secondary);border-bottom-left-radius:6px;align-self:flex-start;align-items:center;gap:8px;padding:10px 14px;display:flex}:where(.gds-ai-status-text){color:var(--text-secondary);font-size:13px;font-style:italic}:where(.gds-ai-dot-loader){align-items:center;gap:5px;display:inline-flex}:where(.gds-ai-dot-loader span){background:color-mix(in srgb, var(--ai-accent-start) 38%, var(--text-muted));border-radius:50%;width:6px;height:6px;animation:1.2s ease-in-out infinite gds-progress-dot-pulse;display:inline-block}:where(.gds-ai-dot-loader span:nth-child(2)){animation-delay:.2s}:where(.gds-ai-dot-loader span:nth-child(3)){animation-delay:.4s}:where(.gds-ai-spinner){border:2.5px solid var(--border-default);border-top-color:var(--ai-accent-start);border-radius:50%;flex-shrink:0;width:26px;height:26px;animation:.72s linear infinite gds-spin}@keyframes gds-spin{to{transform:rotate(360deg)}}@keyframes gds-progress-shimmer{0%{background-position:200% 0}to{background-position:-20% 0}}@keyframes gds-progress-dot-pulse{0%,to{opacity:.45;transform:scale(.7)}50%{opacity:1;transform:scale(1)}}@media (prefers-reduced-motion:reduce){:where(.gds-progress-loader){animation:none}:where(.gds-progress-dot){opacity:.85;animation:none;transform:scale(1)}:where(.gds-ai-spinner){animation:none}:where(.gds-ai-dot-loader span){opacity:.85;animation:none}}:where(.gds-menu){border-radius:var(--radius-card);border:1px solid var(--border-default);background:var(--surface-elevated);box-shadow:var(--shadow-3);padding:4px}:where(.gds-menu-option){border-radius:calc(var(--radius-control) - 4px);width:100%;color:var(--text-primary);font:inherit;text-align:left;cursor:pointer;background:0 0;border:0;align-items:center;gap:8px;padding:8px 10px;font-size:12px;font-weight:500;line-height:1.25;transition:background-color .15s,color .15s;display:flex}:where(.gds-menu-option:hover){background:var(--surface-card-muted)}:where(.gds-menu-option:focus-visible){outline:2px solid var(--client-focus-ring);outline-offset:2px}:where(.gds-menu-option-active){background:var(--client-primary-subtle);color:var(--client-primary);font-weight:600}:where(.gds-comparison-table){border-collapse:separate;border-spacing:0;border:1px solid var(--ds-comparison-table-border);background:var(--ds-comparison-table-bg);border-radius:20px;width:100%;overflow:hidden}:where(.gds-comparison-table th,.gds-comparison-table td){text-align:left;border-bottom:1px solid var(--ds-comparison-table-row-border);padding:14px 16px;font-size:13px}:where(.gds-comparison-table th){background:var(--ds-comparison-table-header-bg);text-transform:uppercase;letter-spacing:.08em;color:var(--ds-comparison-table-header-fg);font-size:11px}:where(.gds-comparison-table tr:last-child td){border-bottom:0}:where(.gds-comparison-table-winner-cell){background:var(--ds-comparison-table-winner-bg);color:var(--ds-comparison-table-winner-fg);font-weight:800}.gengage-simrel-container{font-family:var(--gengage-font-family,"Plus Jakarta Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif);font-size:var(--gengage-font-size,14px);color:var(--text-primary);--_gengage-border-color:var(--border-default);--_gengage-discount-color:var(--gengage-discount-color,var(--client-primary));--_gengage-text-primary:var(--text-primary);--_gengage-text-secondary:var(--text-secondary);--_gengage-surface-base:var(--surface-card);--_gengage-surface-muted:var(--surface-card-muted);--_gengage-surface-secondary:var(--surface-card-soft);--_gengage-rating-color:var(--rating);--_gengage-scrollbar-thumb:var(--ds-progress-scrollbar-thumb)}.gengage-simrel-uispec>*{animation:.2s cubic-bezier(.2,.72,.2,1) both gengage-simrel-enter}@keyframes gengage-simrel-enter{0%{opacity:0;transform:translateY(8px)scale(.99)}to{opacity:1;transform:translateY(0)scale(1)}}.gengage-simrel-grid{grid-template-columns:repeat(var(--gengage-simrel-columns,4), minmax(0, 1fr));gap:12px;padding:8px 0 72px;display:grid;overflow-x:visible}.gengage-simrel-grid>*{min-width:0}.gengage-simrel-grid .gengage-simrel-empty{grid-column:1/-1}.gengage-simrel-card{border:1px solid var(--_gengage-border-color);border-radius:var(--gengage-simrel-card-radius,var(--ds-card-radius));background:var(--ds-card-bg);cursor:pointer;box-shadow:var(--ds-card-shadow);flex-direction:column;width:100%;min-width:0;max-width:none;transition:box-shadow .2s,transform .1s;display:flex;position:relative;overflow:hidden}.gengage-simrel-card:hover{box-shadow:var(--ds-card-shadow-hover);transform:translateY(-2px)}.gengage-simrel-card-image{background:var(--_gengage-surface-base);position:relative;overflow:hidden}.gengage-simrel-card-image img{object-fit:contain;box-sizing:border-box;width:100%;height:120px;padding:8px;display:block}.gengage-simrel-badge{background:var(--_gengage-discount-color);color:var(--client-on-primary);border-radius:4px;padding:2px 8px;font-size:12px;font-weight:600;position:absolute;top:8px;left:8px}.gengage-simrel-card-info{text-align:center;flex-direction:column;gap:4px;padding:8px 10px 10px;display:flex}.gengage-simrel-card-brand{color:var(--_gengage-text-secondary);font-size:10px}.gengage-simrel-card-name{color:var(--_gengage-text-primary);-webkit-line-clamp:2;line-clamp:2;text-overflow:ellipsis;-webkit-box-orient:vertical;min-height:2.7em;font-size:13px;font-weight:600;line-height:1.35;display:-webkit-box;overflow:hidden}.gengage-simrel-card-rating{color:var(--_gengage-rating-color);font-size:12px}.gengage-simrel-card-rating .gengage-star-half{display:inline-block;position:relative}.gengage-simrel-card-rating .gengage-star-half>span{width:.5em;position:absolute;top:0;left:0;overflow:hidden}.gengage-simrel-card-review-count{color:var(--_gengage-text-secondary)}.gengage-simrel-card-price{flex-wrap:wrap;justify-content:center;align-items:baseline;gap:2px 4px;padding-top:4px;display:flex}.gengage-simrel-card-price-original{color:var(--_gengage-text-secondary);font-size:12px;font-weight:400;text-decoration:line-through}.gengage-simrel-card-price-current{color:var(--_gengage-text-primary);font-size:16px;font-weight:800}.gengage-simrel-card-cta{text-align:center;border:none;border-top:1px solid var(--_gengage-surface-muted);width:100%;color:var(--client-primary);letter-spacing:.01em;cursor:pointer;background:0 0;border-radius:0;margin-top:auto;padding:8px 10px;font-family:inherit;font-size:12px;font-weight:700;transition:color .15s,background .15s;display:block}.gengage-simrel-card-cta:hover{color:var(--client-primary-hover);background:var(--surface-card-soft)}.gengage-simrel-atc{border:1px solid var(--_gengage-border-color);border-radius:var(--radius-control,12px)}.gengage-simrel-atc-button{border:1.5px solid var(--client-primary);border-radius:var(--radius-control,12px);background:var(--surface-card);width:calc(100% - 20px);min-height:44px;color:var(--client-primary);letter-spacing:.01em;justify-content:center;align-items:center;margin:0 10px 10px;padding:10px 14px;font-size:13px;font-weight:700;display:flex}.gengage-simrel-atc-button:hover{background:var(--surface-card-soft);color:var(--client-primary-hover)}.gengage-simrel-card .gengage-simrel-atc{border-top:1px solid var(--_gengage-border-color);background:0 0;border-bottom:none;border-left:none;border-right:none;border-radius:0;justify-content:center;align-items:center;width:100%;margin:0;display:flex}.gengage-simrel-card-oos{text-align:center;border-top:1px solid var(--_gengage-surface-muted);width:100%;color:var(--_gengage-text-secondary);letter-spacing:.01em;padding:8px 10px;font-size:12px;font-weight:600}.gengage-simrel-card--out-of-stock{opacity:.55}.gengage-simrel-card--out-of-stock .gengage-simrel-card-image img{filter:grayscale(40%)}.gengage-simrel-quick-actions{flex-wrap:wrap;gap:8px;padding:6px 12px 12px;display:flex}.gengage-simrel-quick-action{border:1px solid var(--_gengage-border-color);background:var(--_gengage-surface-muted);color:var(--_gengage-text-primary);cursor:pointer;border-radius:999px;min-height:40px;padding:8px 10px;font-size:12px;font-weight:600;transition:transform .12s,box-shadow .12s,border-color .12s}.gengage-simrel-quick-action:hover{border-color:var(--client-primary);color:var(--client-primary);box-shadow:var(--shadow-2);transform:translateY(-1px)}.gengage-simrel-empty{text-align:center;width:100%;color:var(--_gengage-text-secondary);padding:40px 20px}.gengage-simrel-error{text-align:center;color:var(--_gengage-text-secondary);padding:24px 16px;font-size:13px;line-height:1.5}.gengage-simrel-retry{border:1px solid var(--_gengage-border-color);background:var(--_gengage-surface-base);cursor:pointer;color:var(--_gengage-text-primary);border-radius:6px;margin-top:8px;padding:6px 16px;font-size:13px;transition:background .15s;display:inline-block}.gengage-simrel-retry:hover{background:var(--_gengage-surface-secondary)}.gengage-simrel-tabs{-webkit-overflow-scrolling:touch;scrollbar-width:thin;scrollbar-color:var(--_gengage-scrollbar-thumb) transparent;border-bottom:none;gap:10px;margin-bottom:0;padding-left:0;padding-right:24px;transition:padding .15s;display:flex;overflow-x:auto}.gengage-simrel-tabs.gengage-simrel-tabs--peek-right{padding-right:6px}.gengage-simrel-tabs.gengage-simrel-tabs--peek-left{padding-left:6px}.gengage-simrel-tabs::-webkit-scrollbar{height:3px}.gengage-simrel-tabs::-webkit-scrollbar-thumb{background:var(--_gengage-scrollbar-thumb);border-radius:3px}.gengage-simrel-tab{border:1px solid var(--_gengage-border-color);background:var(--_gengage-surface-muted);min-height:40px;color:var(--_gengage-text-secondary);opacity:1;font-size:inherit;cursor:pointer;white-space:nowrap;box-shadow:var(--shadow-1);border-radius:999px;margin-bottom:0;padding:8px 14px;font-family:inherit;transition:color .2s,border-color .2s,background .2s,box-shadow .2s}.gengage-simrel-tab:hover{color:var(--client-primary);border-color:var(--client-primary);background:var(--_gengage-surface-secondary)}.gengage-simrel-tab--active{color:var(--client-primary);border-color:var(--client-primary);background:var(--_gengage-surface-secondary);box-shadow:var(--shadow-2);font-weight:600}.gengage-simrel-tabs-wrapper{border-bottom:1px solid var(--_gengage-border-color);align-items:center;margin-bottom:16px;padding-bottom:12px;display:flex;position:relative}.gengage-simrel-tabs-wrapper .gengage-simrel-tabs{flex:1;min-width:0}.gengage-simrel-tabs-arrow{background:var(--_gengage-surface-base);width:28px;height:40px;color:var(--_gengage-text-secondary);cursor:pointer;border:none;border-radius:4px;flex:none;justify-content:center;align-self:center;align-items:center;padding:0;transition:color .15s,background .15s;display:flex}.gengage-simrel-tabs-arrow:hover{color:var(--client-primary);background:var(--_gengage-surface-secondary)}.gengage-simrel-tabs-arrow--left{margin-right:4px}.gengage-simrel-tabs-arrow--right{margin-left:4px}.gengage-simrel-loading{justify-content:center;padding:40px;display:flex}.gengage-simrel-spinner{border:3px solid var(--_gengage-border-color);border-top-color:var(--client-primary);border-radius:50%;width:32px;height:32px;animation:.7s linear infinite gengage-simrel-spin}@keyframes gengage-simrel-spin{to{transform:rotate(360deg)}}@media (width<=768px){.gengage-simrel-grid{-webkit-overflow-scrolling:touch;scrollbar-width:none;scroll-snap-type:x mandatory;gap:10px;padding-top:8px;display:flex;overflow-x:auto}.gengage-simrel-grid::-webkit-scrollbar{display:none}.gengage-simrel-grid>*{scroll-snap-align:center;min-width:unset;flex:none}.gengage-simrel-card{width:200px;min-width:200px;max-width:200px}.gengage-simrel-grid .gengage-simrel-empty{flex:1 0 100%;min-width:100%}.gengage-simrel-card-image img{height:140px}.gengage-simrel-card-cta,.gengage-simrel-quick-action,.gengage-simrel-tab{min-height:44px}.gengage-simrel-card{border:1px solid var(--_gengage-border-color)}.gengage-simrel-card-price-current{font-size:14px}}.gengage-simrel-card:focus-visible,.gengage-simrel-tab:focus-visible,.gengage-simrel-quick-action:focus-visible,.gengage-simrel-atc-button:focus-visible{outline:2px solid var(--client-primary);outline-offset:2px}@media (prefers-reduced-motion:reduce){.gengage-simrel-uispec>*,.gengage-simrel-card,.gengage-simrel-card-image img,.gengage-simrel-quick-action,.gengage-simrel-spinner,.gengage-simrel-tabs-arrow{transition:none!important;animation:none!important;transform:none!important}}
|