ttp-agent-sdk 2.45.14 → 2.46.1

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.
@@ -11468,11 +11468,57 @@ var TextChatSDK = /*#__PURE__*/function (_EventEmitter) {
11468
11468
  }
11469
11469
 
11470
11470
  /**
11471
- * Send a raw JSON message immediately on the open WebSocket. Used by the
11472
- * client-tool registry bypasses the LLM-turn queue used by sendMessage().
11471
+ * In-call user settings on the text channel only responseLength applies
11472
+ * ("detailed"|"normal"|"brief"|"very_brief"; folded into the system prompt
11473
+ * from the next LLM turn). Mirrors VoiceSDK.setCallSettings().
11474
+ * @param {{responseLength?: string}} settings
11473
11475
  */
11474
11476
  _inherits(TextChatSDK, _EventEmitter);
11475
11477
  return _createClass(TextChatSDK, [{
11478
+ key: "setCallSettings",
11479
+ value: function setCallSettings() {
11480
+ var settings = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
11481
+ this._callSettings = _objectSpread(_objectSpread({}, this._callSettings || {}), settings);
11482
+ if (!this.connected || !this.ws || !this._callSettings.responseLength) {
11483
+ return false;
11484
+ }
11485
+ this.sendRaw({
11486
+ t: 'call_settings',
11487
+ responseLength: this._callSettings.responseLength
11488
+ });
11489
+ return true;
11490
+ }
11491
+
11492
+ /** Flush the persisted response-length preference right after hello_ack. */
11493
+ }, {
11494
+ key: "_syncCallSettingsAfterHello",
11495
+ value: function _syncCallSettingsAfterHello() {
11496
+ try {
11497
+ var s = this._callSettings;
11498
+ if (!s && typeof this.config.callSettingsProvider === 'function') {
11499
+ s = this.config.callSettingsProvider();
11500
+ }
11501
+ if (!s) return;
11502
+ this._callSettings = _objectSpread({}, s);
11503
+ var length = s.responseLength || 'normal';
11504
+ if (length === 'normal') return;
11505
+ this.sendRaw({
11506
+ t: 'call_settings',
11507
+ responseLength: length
11508
+ });
11509
+ console.log('⚙️ [TextChatSDK] Synced persisted call settings:', {
11510
+ responseLength: length
11511
+ });
11512
+ } catch (e) {
11513
+ console.warn('⚙️ [TextChatSDK] Failed to sync call settings (ignored):', e);
11514
+ }
11515
+ }
11516
+
11517
+ /**
11518
+ * Send a raw JSON message immediately on the open WebSocket. Used by the
11519
+ * client-tool registry — bypasses the LLM-turn queue used by sendMessage().
11520
+ */
11521
+ }, {
11476
11522
  key: "sendRaw",
11477
11523
  value: function sendRaw(message) {
11478
11524
  if (!this.connected || !this.ws) {
@@ -11626,6 +11672,9 @@ var TextChatSDK = /*#__PURE__*/function (_EventEmitter) {
11626
11672
  hash: data.disclaimersHash
11627
11673
  });
11628
11674
  }
11675
+ // Silently re-apply the user's persisted response-length preference
11676
+ // (voice speed is meaningless on the text channel).
11677
+ _this2._syncCallSettingsAfterHello();
11629
11678
  // Tools are loaded, connection is ready — drain any queued messages
11630
11679
  _this2.drainQueue();
11631
11680
  return;
@@ -11944,7 +11993,7 @@ var TextChatSDK = /*#__PURE__*/function (_EventEmitter) {
11944
11993
 
11945
11994
  // SDK build time for debugging
11946
11995
  if (true) {
11947
- helloMessage.lastBuildTime = "2026-06-29T06:35:24.857Z";
11996
+ helloMessage.lastBuildTime = "2026-07-13T08:30:44.984Z";
11948
11997
  }
11949
11998
  try {
11950
11999
  this.ws.send(JSON.stringify(helloMessage));
@@ -13531,6 +13580,8 @@ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e
13531
13580
  * strip.updateTimer(text)
13532
13581
  * strip.setMuted(bool) – legacy sync (prefer syncFromVoice)
13533
13582
  * strip.setHeld(bool) – legacy pause visual (maps to paused)
13583
+ * strip.updateRecipes(recipes) – refresh saved-recipes badge + popover
13584
+ * strip.pulseRecipeBadge() – celebrate animation after recipe modal closes
13534
13585
  * strip.destroy()
13535
13586
  */
13536
13587
 
@@ -13562,7 +13613,13 @@ var EcommerceVoiceStrip = /*#__PURE__*/function () {
13562
13613
  this.onToggleSpeaker = opts.onToggleSpeaker || function () {};
13563
13614
  this.onSendText = opts.onSendText || function () {};
13564
13615
  this.onAbout = typeof opts.onAbout === 'function' ? opts.onAbout : null;
13616
+ this.onSettings = typeof opts.onSettings === 'function' ? opts.onSettings : null;
13617
+ this.onRecipeSelect = typeof opts.onRecipeSelect === 'function' ? opts.onRecipeSelect : null;
13565
13618
  this._kbdOpen = false;
13619
+ this._recipePopoverOpen = false;
13620
+ this._recipes = [];
13621
+ this._recipeUnsub = null;
13622
+ this._pulseTimer = null;
13566
13623
  this._el = null;
13567
13624
  this._build();
13568
13625
  }
@@ -13650,7 +13707,7 @@ var EcommerceVoiceStrip = /*#__PURE__*/function () {
13650
13707
  key: "_footerHtml",
13651
13708
  value: function _footerHtml() {
13652
13709
  var brand = this.footer ? "<span>Powered by <a href=\"".concat(this.footer.brandUrl, "\" target=\"_blank\" rel=\"noopener noreferrer\"><b>").concat(this.footer.brandName, "</b></a></span>") : '';
13653
- return "\n <div id=\"ttp-ecom-footer\" class=\"ttp-ecom-footer\">\n ".concat(brand, "\n <button type=\"button\" class=\"ttp-ecom-info-btn\" id=\"ttp-ecom-btn-info\" title=\"About\" aria-label=\"About\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"9\"/>\n <line x1=\"12\" y1=\"11\" x2=\"12\" y2=\"16\"/>\n <circle cx=\"12\" cy=\"7.7\" r=\"1.05\" fill=\"currentColor\" stroke=\"none\"/>\n </svg>\n </button>\n </div>");
13710
+ return "\n <div id=\"ttp-ecom-footer\" class=\"ttp-ecom-footer\">\n ".concat(brand, "\n <div class=\"ttp-ecom-recipe-wrap\" id=\"ttp-ecom-recipe-wrap\" hidden>\n <button type=\"button\" class=\"ttp-ecom-recipe-btn\" id=\"ttp-ecom-btn-recipe\"\n title=\"Saved recipes\" aria-label=\"Saved recipes\" aria-expanded=\"false\" aria-haspopup=\"true\">\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.65\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M5.5 3V8\"/>\n <path d=\"M7.5 3V8\"/>\n <path d=\"M9.5 3V8\"/>\n <path d=\"M7.5 8V21\"/>\n <path d=\"M16.5 3V21\"/>\n <path d=\"M16.5 3C19 3 20.5 5.2 20.5 7.5V10\"/>\n </svg>\n <span class=\"ttp-ecom-recipe-count\" id=\"ttp-ecom-recipe-count\">0</span>\n </button>\n <div class=\"ttp-ecom-recipe-popover\" id=\"ttp-ecom-recipe-popover\" role=\"menu\" hidden></div>\n </div>\n <button type=\"button\" class=\"ttp-ecom-info-btn\" id=\"ttp-ecom-btn-settings\" title=\"Call settings\" aria-label=\"Call settings\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"3\"/>\n <path d=\"M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 1 1-4 0v-.09a1.65 1.65 0 0 0-1-1.51 1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 1 1 0-4h.09a1.65 1.65 0 0 0 1.51-1 1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33h.01a1.65 1.65 0 0 0 1-1.51V3a2 2 0 1 1 4 0v.09a1.65 1.65 0 0 0 1 1.51h.01a1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82v.01a1.65 1.65 0 0 0 1.51 1H21a2 2 0 1 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z\"/>\n </svg>\n </button>\n <button type=\"button\" class=\"ttp-ecom-info-btn\" id=\"ttp-ecom-btn-info\" title=\"About\" aria-label=\"About\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"9\"/>\n <line x1=\"12\" y1=\"11\" x2=\"12\" y2=\"16\"/>\n <circle cx=\"12\" cy=\"7.7\" r=\"1.05\" fill=\"currentColor\" stroke=\"none\"/>\n </svg>\n </button>\n </div>");
13654
13711
  }
13655
13712
  }, {
13656
13713
  key: "_wireEvents",
@@ -13685,6 +13742,13 @@ var EcommerceVoiceStrip = /*#__PURE__*/function () {
13685
13742
  if (_this.onAbout) _this.onAbout();
13686
13743
  });
13687
13744
  }
13745
+ var settingsBtn = $('ttp-ecom-btn-settings');
13746
+ if (settingsBtn) {
13747
+ settingsBtn.addEventListener('click', function () {
13748
+ if (_this.onSettings) _this.onSettings();
13749
+ });
13750
+ }
13751
+ this._wireRecipeBadge();
13688
13752
  $('ttp-ecom-btn-end').addEventListener('click', function () {
13689
13753
  return _this.onHangup();
13690
13754
  });
@@ -13701,10 +13765,141 @@ var EcommerceVoiceStrip = /*#__PURE__*/function () {
13701
13765
  if (e.key === 'Enter') sendFn();
13702
13766
  });
13703
13767
  }
13768
+
13769
+ /**
13770
+ * Subscribe to a recipe store; hydrates badge on mount.
13771
+ * @param {{ subscribe: Function, getAll: Function }} store
13772
+ */
13773
+ }, {
13774
+ key: "bindRecipeStore",
13775
+ value: function bindRecipeStore(store) {
13776
+ var _this2 = this;
13777
+ if (this._recipeUnsub) {
13778
+ this._recipeUnsub();
13779
+ this._recipeUnsub = null;
13780
+ }
13781
+ if (!store) return;
13782
+ this.updateRecipes(store.getAll());
13783
+ this._recipeUnsub = store.subscribe(function (recipes) {
13784
+ return _this2.updateRecipes(recipes);
13785
+ });
13786
+ }
13787
+
13788
+ /**
13789
+ * @param {Array<{ id: string, title: string }>} recipes
13790
+ */
13791
+ }, {
13792
+ key: "updateRecipes",
13793
+ value: function updateRecipes(recipes) {
13794
+ this._recipes = Array.isArray(recipes) ? recipes : [];
13795
+ if (!this._el) return;
13796
+ var wrap = this._el.querySelector('#ttp-ecom-recipe-wrap');
13797
+ var countEl = this._el.querySelector('#ttp-ecom-recipe-count');
13798
+ var btn = this._el.querySelector('#ttp-ecom-btn-recipe');
13799
+ if (!wrap || !countEl || !btn) return;
13800
+ var n = this._recipes.length;
13801
+ countEl.textContent = String(n);
13802
+ wrap.hidden = n === 0;
13803
+ if (this._recipePopoverOpen) this._renderRecipePopover();
13804
+ }
13805
+
13806
+ /** Play a short celebrate animation to draw attention after saving a recipe. */
13807
+ }, {
13808
+ key: "pulseRecipeBadge",
13809
+ value: function pulseRecipeBadge() {
13810
+ if (!this._el || this._recipes.length === 0) return;
13811
+ var btn = this._el.querySelector('#ttp-ecom-btn-recipe');
13812
+ var countEl = this._el.querySelector('#ttp-ecom-recipe-count');
13813
+ if (!btn) return;
13814
+ if (this._pulseTimer) {
13815
+ clearTimeout(this._pulseTimer);
13816
+ this._pulseTimer = null;
13817
+ }
13818
+ btn.classList.remove('ttp-ecom-recipe-btn--celebrate');
13819
+ countEl === null || countEl === void 0 || countEl.classList.remove('ttp-ecom-recipe-count--pop');
13820
+ // Force reflow so re-adding the class retriggers the animation.
13821
+ void btn.offsetWidth;
13822
+ btn.classList.add('ttp-ecom-recipe-btn--celebrate');
13823
+ countEl === null || countEl === void 0 || countEl.classList.add('ttp-ecom-recipe-count--pop');
13824
+ var _cleanup = function cleanup() {
13825
+ btn.classList.remove('ttp-ecom-recipe-btn--celebrate');
13826
+ countEl === null || countEl === void 0 || countEl.classList.remove('ttp-ecom-recipe-count--pop');
13827
+ btn.removeEventListener('animationend', _cleanup);
13828
+ };
13829
+ btn.addEventListener('animationend', _cleanup);
13830
+ this._pulseTimer = setTimeout(_cleanup, 1600);
13831
+ }
13832
+ }, {
13833
+ key: "_wireRecipeBadge",
13834
+ value: function _wireRecipeBadge() {
13835
+ var _this$_el,
13836
+ _this$_el2,
13837
+ _this3 = this;
13838
+ var btn = (_this$_el = this._el) === null || _this$_el === void 0 ? void 0 : _this$_el.querySelector('#ttp-ecom-btn-recipe');
13839
+ var popover = (_this$_el2 = this._el) === null || _this$_el2 === void 0 ? void 0 : _this$_el2.querySelector('#ttp-ecom-recipe-popover');
13840
+ if (!btn || !popover) return;
13841
+ btn.addEventListener('click', function (e) {
13842
+ e.stopPropagation();
13843
+ _this3._recipePopoverOpen = !_this3._recipePopoverOpen;
13844
+ btn.setAttribute('aria-expanded', _this3._recipePopoverOpen ? 'true' : 'false');
13845
+ if (_this3._recipePopoverOpen) {
13846
+ _this3._renderRecipePopover();
13847
+ popover.hidden = false;
13848
+ } else {
13849
+ popover.hidden = true;
13850
+ }
13851
+ });
13852
+ this._recipeOutsideClick = function (e) {
13853
+ if (!_this3._recipePopoverOpen) return;
13854
+ if (btn.contains(e.target) || popover.contains(e.target)) return;
13855
+ _this3._recipePopoverOpen = false;
13856
+ btn.setAttribute('aria-expanded', 'false');
13857
+ popover.hidden = true;
13858
+ };
13859
+ document.addEventListener('click', this._recipeOutsideClick);
13860
+ }
13861
+ }, {
13862
+ key: "_renderRecipePopover",
13863
+ value: function _renderRecipePopover() {
13864
+ var _this$_el3,
13865
+ _this4 = this;
13866
+ var popover = (_this$_el3 = this._el) === null || _this$_el3 === void 0 ? void 0 : _this$_el3.querySelector('#ttp-ecom-recipe-popover');
13867
+ if (!popover) return;
13868
+ var items = this._recipes.slice(0, 3);
13869
+ var rows = items.map(function (r) {
13870
+ return "\n <button type=\"button\" class=\"ttp-ecom-recipe-row\" data-recipe-id=\"".concat(_this4._escapeAttr(r.id), "\" role=\"menuitem\">\n <span class=\"ttp-ecom-recipe-row-title\">").concat(_this4._escapeHtml(r.title), "</span>\n </button>");
13871
+ }).join('');
13872
+ var more = this._recipes.length > 3 ? "<div class=\"ttp-ecom-recipe-more\">".concat(this._recipes.length - 3, " more saved</div>") : '';
13873
+ popover.innerHTML = "\n <span class=\"ttp-ecom-recipe-popover-label\">Saved recipes</span>\n ".concat(rows, "\n ").concat(more);
13874
+ popover.querySelectorAll('.ttp-ecom-recipe-row').forEach(function (row) {
13875
+ row.addEventListener('click', function (e) {
13876
+ var _this4$_el;
13877
+ e.stopPropagation();
13878
+ var id = row.getAttribute('data-recipe-id');
13879
+ var recipe = _this4._recipes.find(function (r) {
13880
+ return r.id === id;
13881
+ });
13882
+ if (recipe && _this4.onRecipeSelect) _this4.onRecipeSelect(recipe);
13883
+ _this4._recipePopoverOpen = false;
13884
+ popover.hidden = true;
13885
+ (_this4$_el = _this4._el) === null || _this4$_el === void 0 || (_this4$_el = _this4$_el.querySelector('#ttp-ecom-btn-recipe')) === null || _this4$_el === void 0 || _this4$_el.setAttribute('aria-expanded', 'false');
13886
+ });
13887
+ });
13888
+ }
13889
+ }, {
13890
+ key: "_escapeHtml",
13891
+ value: function _escapeHtml(str) {
13892
+ return String(str !== null && str !== void 0 ? str : '').replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
13893
+ }
13894
+ }, {
13895
+ key: "_escapeAttr",
13896
+ value: function _escapeAttr(str) {
13897
+ return this._escapeHtml(str).replace(/'/g, '&#39;');
13898
+ }
13704
13899
  }, {
13705
13900
  key: "show",
13706
13901
  value: function show() {
13707
- var _this2 = this;
13902
+ var _this5 = this;
13708
13903
  var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
13709
13904
  if (!this._el) return;
13710
13905
  if (opts.reset !== false) this._reset();
@@ -13712,7 +13907,7 @@ var EcommerceVoiceStrip = /*#__PURE__*/function () {
13712
13907
  this.syncFromVoice();
13713
13908
  // Now that the strip has real dimensions, clamp any restored drag position.
13714
13909
  if (this._dragEnabled) requestAnimationFrame(function () {
13715
- return _this2.reclampDrag();
13910
+ return _this5.reclampDrag();
13716
13911
  });
13717
13912
  }
13718
13913
  }, {
@@ -13768,8 +13963,8 @@ var EcommerceVoiceStrip = /*#__PURE__*/function () {
13768
13963
  }, {
13769
13964
  key: "_syncMiniWave",
13770
13965
  value: function _syncMiniWave(frozen) {
13771
- var _this$_el;
13772
- (_this$_el = this._el) === null || _this$_el === void 0 || _this$_el.querySelectorAll('.ttp-ecom-mini-wave .ttp-ecom-wb').forEach(function (b) {
13966
+ var _this$_el4;
13967
+ (_this$_el4 = this._el) === null || _this$_el4 === void 0 || _this$_el4.querySelectorAll('.ttp-ecom-mini-wave .ttp-ecom-wb').forEach(function (b) {
13773
13968
  b.classList.toggle('paused', frozen);
13774
13969
  });
13775
13970
  }
@@ -13859,7 +14054,7 @@ var EcommerceVoiceStrip = /*#__PURE__*/function () {
13859
14054
  }, {
13860
14055
  key: "enableDrag",
13861
14056
  value: function enableDrag() {
13862
- var _this3 = this;
14057
+ var _this6 = this;
13863
14058
  var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
13864
14059
  if (!this._el || this._dragEnabled) return;
13865
14060
  var wrapper = this._el; // fixed, centered via translateX(-50%)
@@ -13888,8 +14083,8 @@ var EcommerceVoiceStrip = /*#__PURE__*/function () {
13888
14083
  dy = e.clientY - startY;
13889
14084
  if (!moved && Math.hypot(dx, dy) < THRESHOLD) return;
13890
14085
  moved = true;
13891
- _this3._dragOffset = _this3._clampDragOffset(baseX + dx, baseY + dy, baseRect);
13892
- _this3._applyDragOffset();
14086
+ _this6._dragOffset = _this6._clampDragOffset(baseX + dx, baseY + dy, baseRect);
14087
+ _this6._applyDragOffset();
13893
14088
  };
13894
14089
  var _onUp = function onUp(e) {
13895
14090
  window.removeEventListener('pointermove', onMove);
@@ -13899,9 +14094,9 @@ var EcommerceVoiceStrip = /*#__PURE__*/function () {
13899
14094
  handle.releasePointerCapture(e.pointerId);
13900
14095
  } catch (err) {/* not captured */}
13901
14096
  dragging = false;
13902
- if (moved && _this3._dragPersist && _this3._dragKey) {
14097
+ if (moved && _this6._dragPersist && _this6._dragKey) {
13903
14098
  try {
13904
- window.localStorage.setItem(_this3._dragKey, JSON.stringify(_this3._dragOffset));
14099
+ window.localStorage.setItem(_this6._dragKey, JSON.stringify(_this6._dragOffset));
13905
14100
  } catch (err) {/* unavailable */}
13906
14101
  }
13907
14102
  };
@@ -13913,9 +14108,9 @@ var EcommerceVoiceStrip = /*#__PURE__*/function () {
13913
14108
  moved = false;
13914
14109
  startX = e.clientX;
13915
14110
  startY = e.clientY;
13916
- baseX = _this3._dragOffset.x;
13917
- baseY = _this3._dragOffset.y;
13918
- baseRect = _this3._dragBaseRect();
14111
+ baseX = _this6._dragOffset.x;
14112
+ baseY = _this6._dragOffset.y;
14113
+ baseRect = _this6._dragBaseRect();
13919
14114
  handle.style.setProperty('cursor', 'grabbing');
13920
14115
  try {
13921
14116
  handle.setPointerCapture(e.pointerId);
@@ -14019,6 +14214,18 @@ var EcommerceVoiceStrip = /*#__PURE__*/function () {
14019
14214
  }, {
14020
14215
  key: "destroy",
14021
14216
  value: function destroy() {
14217
+ if (this._recipeUnsub) {
14218
+ this._recipeUnsub();
14219
+ this._recipeUnsub = null;
14220
+ }
14221
+ if (this._recipeOutsideClick) {
14222
+ document.removeEventListener('click', this._recipeOutsideClick);
14223
+ this._recipeOutsideClick = null;
14224
+ }
14225
+ if (this._pulseTimer) {
14226
+ clearTimeout(this._pulseTimer);
14227
+ this._pulseTimer = null;
14228
+ }
14022
14229
  if (this._el && this._el.parentNode) this._el.parentNode.removeChild(this._el);
14023
14230
  this._el = null;
14024
14231
  var style = document.getElementById('ttp-ecom-strip-styles');
@@ -14027,12 +14234,12 @@ var EcommerceVoiceStrip = /*#__PURE__*/function () {
14027
14234
  }, {
14028
14235
  key: "_reset",
14029
14236
  value: function _reset() {
14030
- var _this4 = this,
14237
+ var _this7 = this,
14031
14238
  _$,
14032
14239
  _$2;
14033
14240
  this._kbdOpen = false;
14034
14241
  var $ = function $(id) {
14035
- return _this4._el.querySelector('#' + id);
14242
+ return _this7._el.querySelector('#' + id);
14036
14243
  };
14037
14244
  var tx = $('ttp-ecom-transcript');
14038
14245
  if (tx) {
@@ -14075,7 +14282,7 @@ var EcommerceVoiceStrip = /*#__PURE__*/function () {
14075
14282
  }
14076
14283
  }]);
14077
14284
  }();
14078
- EcommerceVoiceStrip._CSS = "\n#ttp-ecom-strip-wrapper {\n position: fixed;\n left: 50%;\n bottom: 0;\n transform: translateX(-50%);\n width: min(720px, calc(100vw - 40px));\n max-width: calc(100vw - 40px);\n z-index: 2147483647;\n box-sizing: border-box;\n padding: 0 14px max(14px, env(safe-area-inset-bottom, 0px));\n font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif;\n -webkit-font-smoothing: antialiased;\n font-feature-settings: \"kern\" 1, \"liga\" 1;\n pointer-events: none;\n}\n#ttp-ecom-strip-wrapper * { box-sizing: border-box; }\n\n/* Layered glass dock: rim light, soft lift, optional backdrop tint */\n#ttp-ecom-strip {\n pointer-events: auto;\n position: relative;\n isolation: isolate;\n margin: 0;\n border-radius: 26px;\n border: 1px solid rgba(255, 255, 255, 0.18);\n background: var(--ecom-strip-bg, linear-gradient(145deg, #5b21b6 0%, #3730a3 42%, #1e1b4b 100%));\n background-clip: padding-box;\n color: var(--ecom-fg, #ffffff);\n box-shadow:\n 0 0 0 1px rgba(0, 0, 0, 0.18) inset,\n 0 1px 0 rgba(255, 255, 255, 0.14) inset,\n 0 -20px 40px -8px rgba(0, 0, 0, 0.22) inset,\n 0 12px 32px rgba(0, 0, 0, 0.22),\n 0 28px 56px -16px rgba(0, 0, 0, 0.45),\n 0 0 80px -20px rgba(99, 102, 241, 0.35);\n overflow: visible;\n}\n\n#ttp-ecom-strip::before {\n content: \"\";\n position: absolute;\n inset: 0;\n border-radius: inherit;\n pointer-events: none;\n z-index: 0;\n background: linear-gradient(\n 165deg,\n rgba(255, 255, 255, 0.08) 0%,\n rgba(255, 255, 255, 0) 42%,\n rgba(0, 0, 0, 0.12) 100%\n );\n}\n\n/* LIVE chip \u2014 glass capsule (backdrop blur), straddles top edge */\n#ttp-ecom-live-edge {\n position: absolute;\n left: 50%;\n top: 0;\n transform: translate(-50%, -50%);\n z-index: 4;\n max-width: calc(100% - 52px);\n pointer-events: none;\n padding: 3px 14px 4px;\n border-radius: 999px;\n background: rgba(8, 6, 24, 0.78);\n border: 1px solid rgba(255, 255, 255, 0.28);\n box-shadow:\n 0 0 0 1px rgba(0, 0, 0, 0.35),\n 0 4px 16px rgba(0, 0, 0, 0.35),\n inset 0 1px 0 rgba(255, 255, 255, 0.12);\n}\n@supports (backdrop-filter: blur(1px)) {\n #ttp-ecom-live-edge {\n backdrop-filter: blur(14px) saturate(180%);\n -webkit-backdrop-filter: blur(14px) saturate(180%);\n }\n}\n\n#ttp-ecom-main-row {\n position: relative;\n z-index: 1;\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 18px 16px 12px;\n min-height: 48px;\n}\n\n#ttp-ecom-footer {\n position: relative;\n z-index: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 0 16px 10px;\n margin-top: -4px;\n border-top: 1px solid rgba(255, 255, 255, 0.06);\n padding-top: 6px;\n}\n.ttp-ecom-info-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n padding: 2px;\n margin: 0;\n background: none;\n border: none;\n cursor: pointer;\n line-height: 0;\n border-radius: 50%;\n color: rgba(255, 255, 255, 0.35);\n transition: color 0.2s, background 0.2s;\n}\n.ttp-ecom-info-btn:hover {\n color: rgba(255, 255, 255, 0.7);\n background: rgba(255, 255, 255, 0.08);\n}\n.ttp-ecom-info-btn svg { display: block; }\n#ttp-ecom-footer span {\n font-size: 10px;\n color: rgba(255, 255, 255, 0.35);\n white-space: nowrap;\n}\n#ttp-ecom-footer a {\n color: rgba(167, 139, 250, 0.95);\n text-decoration: none;\n font-weight: 600;\n}\n#ttp-ecom-footer a:hover { opacity: 0.85; }\n#ttp-ecom-footer a b { font-weight: 700; }\n\n#ttp-ecom-left-controls,\n#ttp-ecom-right-cluster {\n display: flex;\n align-items: center;\n gap: 7px;\n flex-shrink: 0;\n}\n\n/* Inset \u201Cmeter bridge\u201D for transcript \u2014 keeps one-line layout, adds depth */\n#ttp-ecom-center {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n align-items: stretch;\n justify-content: center;\n text-align: start;\n padding: 7px 12px 7px 14px;\n border-radius: 16px;\n background: rgba(0, 0, 0, 0.16);\n border: 1px solid rgba(255, 255, 255, 0.08);\n box-shadow:\n inset 0 1px 0 rgba(255, 255, 255, 0.07),\n inset 0 -1px 0 rgba(0, 0, 0, 0.12);\n}\n\n#ttp-ecom-live-row {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n min-width: 0;\n}\n\n#ttp-ecom-live-label {\n font-size: 9px;\n font-weight: 800;\n letter-spacing: 0.14em;\n text-transform: uppercase;\n color: var(--ecom-accent, #4ade80);\n opacity: 0.98;\n white-space: nowrap;\n}\n\n#ttp-ecom-dot {\n width: 7px;\n height: 7px;\n border-radius: 50%;\n background: var(--ecom-accent, #4ade80);\n flex-shrink: 0;\n box-shadow: 0 0 14px rgba(74, 222, 128, 0.45);\n animation: ttp-ecom-blink 1.35s ease-in-out infinite;\n}\n#ttp-ecom-dot.paused {\n animation: none;\n opacity: 0.45;\n background: rgba(255,255,255,0.35);\n box-shadow: none;\n}\n@keyframes ttp-ecom-blink {\n 0%, 100% { opacity: 1; transform: scale(1); }\n 50% { opacity: 0.25; transform: scale(0.92); }\n}\n\n#ttp-ecom-transcript {\n font-size: 12px;\n line-height: 1.42;\n letter-spacing: 0.015em;\n color: rgba(255, 255, 255, 0.94);\n width: 100%;\n max-width: 100%;\n overflow: hidden;\n word-break: break-word;\n overflow-wrap: anywhere;\n display: -webkit-box;\n -webkit-box-orient: vertical;\n -webkit-line-clamp: 2;\n line-clamp: 2;\n}\n\n#ttp-ecom-transcript.ttp-ecom-transcript--user {\n color: rgba(255, 244, 214, 0.98);\n font-weight: 600;\n letter-spacing: 0.02em;\n text-shadow: 0 0 28px rgba(251, 191, 36, 0.22);\n}\n\n/* Squircle glass controls */\n#ttp-ecom-strip .ttp-ecom-ctrl {\n width: 36px;\n height: 36px;\n border-radius: 12px;\n border: 1px solid rgba(255, 255, 255, 0.16);\n background: linear-gradient(165deg, rgba(255,255,255,0.14) 0%, rgba(255,255,255,0.05) 100%);\n color: rgba(255, 255, 255, 0.92);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 0;\n position: relative;\n box-shadow:\n inset 0 1px 0 rgba(255, 255, 255, 0.18),\n 0 2px 8px rgba(0, 0, 0, 0.15);\n transition: background 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease, transform 0.14s ease;\n outline: none;\n}\n#ttp-ecom-strip .ttp-ecom-ctrl:hover {\n background: linear-gradient(165deg, rgba(255,255,255,0.22) 0%, rgba(255,255,255,0.08) 100%);\n border-color: rgba(255, 255, 255, 0.32);\n box-shadow:\n inset 0 1px 0 rgba(255, 255, 255, 0.22),\n 0 4px 14px rgba(0, 0, 0, 0.2);\n}\n#ttp-ecom-strip .ttp-ecom-ctrl:active { transform: scale(0.94); }\n#ttp-ecom-strip .ttp-ecom-ctrl:focus-visible {\n outline: 2px solid rgba(165, 180, 252, 0.95);\n outline-offset: 2px;\n}\n#ttp-ecom-strip .ttp-ecom-ctrl-active {\n background: linear-gradient(165deg, rgba(129, 140, 248, 0.45) 0%, rgba(99, 102, 241, 0.28) 100%) !important;\n border-color: rgba(196, 181, 253, 0.65) !important;\n box-shadow:\n inset 0 1px 0 rgba(255, 255, 255, 0.2),\n 0 0 0 1px rgba(99, 102, 241, 0.35),\n 0 6px 18px rgba(79, 70, 229, 0.35) !important;\n}\n#ttp-ecom-strip .ttp-ecom-ctrl-warn {\n background: linear-gradient(165deg, rgba(252, 165, 165, 0.22) 0%, rgba(239, 68, 68, 0.2) 100%) !important;\n border-color: rgba(252, 165, 165, 0.55) !important;\n}\n\n#ttp-ecom-strip .ttp-ecom-ctrl-end {\n width: 40px;\n height: 40px;\n border-radius: 14px;\n background: linear-gradient(165deg, #fb7185 0%, var(--ecom-end, #ef4444) 52%, #991b1b 100%) !important;\n border: none !important;\n color: #fff !important;\n box-shadow:\n inset 0 1px 0 rgba(255, 255, 255, 0.25),\n 0 0 0 2px rgba(0, 0, 0, 0.12),\n 0 6px 22px rgba(239, 68, 68, 0.48);\n transform: rotate(135deg);\n transition: filter 0.18s ease, transform 0.14s ease, box-shadow 0.2s ease;\n}\n#ttp-ecom-strip .ttp-ecom-ctrl-end:hover {\n filter: brightness(1.06);\n transform: rotate(135deg) scale(1.05);\n box-shadow:\n inset 0 1px 0 rgba(255, 255, 255, 0.28),\n 0 0 0 2px rgba(0, 0, 0, 0.12),\n 0 10px 28px rgba(239, 68, 68, 0.52);\n}\n#ttp-ecom-strip .ttp-ecom-ctrl-end:active {\n transform: rotate(135deg) scale(0.94);\n}\n\n/* Mini waveform */\n#ttp-ecom-strip .ttp-ecom-mini-wave {\n display: flex;\n align-items: center;\n gap: 3px;\n height: 24px;\n padding: 0 4px;\n opacity: 0.9;\n}\n#ttp-ecom-strip .ttp-ecom-mini-wave .ttp-ecom-wb {\n width: 3px;\n border-radius: 3px;\n background: linear-gradient(180deg, #ddd6fe 0%, var(--ecom-accent, #a78bfa) 100%);\n animation: ttp-ecom-wa 0.78s ease-in-out infinite;\n}\n#ttp-ecom-strip .ttp-ecom-mini-wave .ttp-ecom-wb:nth-child(1) { height: 6px; animation-delay: 0s; }\n#ttp-ecom-strip .ttp-ecom-mini-wave .ttp-ecom-wb:nth-child(2) { height: 11px; animation-delay: 0.1s; }\n#ttp-ecom-strip .ttp-ecom-mini-wave .ttp-ecom-wb:nth-child(3) { height: 16px; animation-delay: 0.2s; }\n#ttp-ecom-strip .ttp-ecom-mini-wave .ttp-ecom-wb:nth-child(4) { height: 9px; animation-delay: 0.15s; }\n#ttp-ecom-strip .ttp-ecom-mini-wave .ttp-ecom-wb:nth-child(5) { height: 12px; animation-delay: 0.05s; }\n@keyframes ttp-ecom-wa {\n 0%, 100% { transform: scaleY(0.38); opacity: 0.5; }\n 50% { transform: scaleY(1); opacity: 1; }\n}\n#ttp-ecom-strip .ttp-ecom-mini-wave .ttp-ecom-wb.paused {\n animation-play-state: paused;\n transform: scaleY(0.32);\n opacity: 0.28;\n}\n\n/* Input row */\n#ttp-ecom-input-row {\n display: none;\n position: relative;\n z-index: 1;\n align-items: center;\n gap: 10px;\n padding: 0 14px 12px;\n margin-top: -2px;\n border-top: 1px solid rgba(255, 255, 255, 0.1);\n}\n#ttp-ecom-input-row.open {\n display: flex;\n}\n#ttp-ecom-input {\n flex: 1;\n height: 40px;\n border-radius: 12px;\n border: 1px solid rgba(255, 255, 255, 0.18);\n background: rgba(0, 0, 0, 0.22);\n color: #fff;\n font-size: 14px;\n padding: 0 14px;\n outline: none;\n box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.2);\n transition: border-color 0.18s ease, box-shadow 0.18s ease;\n}\n#ttp-ecom-input::placeholder { color: rgba(255, 255, 255, 0.42); }\n#ttp-ecom-input:focus {\n border-color: rgba(165, 180, 252, 0.65);\n box-shadow:\n inset 0 1px 2px rgba(0, 0, 0, 0.2),\n 0 0 0 3px rgba(99, 102, 241, 0.25);\n}\n\n#ttp-ecom-strip .ttp-ecom-send {\n width: 36px;\n height: 36px;\n border-radius: 12px;\n border: none;\n background: linear-gradient(165deg, rgba(255,255,255,0.22) 0%, rgba(255,255,255,0.1) 100%);\n color: #fff;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2);\n transition: background 0.18s ease, transform 0.12s ease;\n}\n#ttp-ecom-strip .ttp-ecom-send:hover {\n background: linear-gradient(165deg, rgba(255,255,255,0.3) 0%, rgba(255,255,255,0.14) 100%);\n}\n#ttp-ecom-strip .ttp-ecom-send:active { transform: scale(0.94); }\n\n@media (max-width: 520px) {\n #ttp-ecom-strip .ttp-ecom-mini-wave { display: none; }\n #ttp-ecom-main-row { gap: 7px; padding: 16px 12px 10px; }\n #ttp-ecom-strip .ttp-ecom-ctrl { width: 36px; height: 36px; }\n #ttp-ecom-strip .ttp-ecom-ctrl-end { width: 40px; height: 40px; }\n}\n\n@media (prefers-reduced-motion: reduce) {\n #ttp-ecom-dot { animation: none; opacity: 0.9; }\n #ttp-ecom-strip .ttp-ecom-mini-wave .ttp-ecom-wb { animation: none; opacity: 0.65; }\n}\n";
14285
+ EcommerceVoiceStrip._CSS = "\n#ttp-ecom-strip-wrapper {\n position: fixed;\n left: 50%;\n bottom: 0;\n transform: translateX(-50%);\n width: min(720px, calc(100vw - 40px));\n max-width: calc(100vw - 40px);\n z-index: 2147483647;\n box-sizing: border-box;\n padding: 0 14px max(14px, env(safe-area-inset-bottom, 0px));\n font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif;\n -webkit-font-smoothing: antialiased;\n font-feature-settings: \"kern\" 1, \"liga\" 1;\n pointer-events: none;\n}\n#ttp-ecom-strip-wrapper * { box-sizing: border-box; }\n\n/* Layered glass dock: rim light, soft lift, optional backdrop tint */\n#ttp-ecom-strip {\n pointer-events: auto;\n position: relative;\n isolation: isolate;\n margin: 0;\n border-radius: 26px;\n border: 1px solid rgba(255, 255, 255, 0.18);\n background: var(--ecom-strip-bg, linear-gradient(145deg, #5b21b6 0%, #3730a3 42%, #1e1b4b 100%));\n background-clip: padding-box;\n color: var(--ecom-fg, #ffffff);\n box-shadow:\n 0 0 0 1px rgba(0, 0, 0, 0.18) inset,\n 0 1px 0 rgba(255, 255, 255, 0.14) inset,\n 0 -20px 40px -8px rgba(0, 0, 0, 0.22) inset,\n 0 12px 32px rgba(0, 0, 0, 0.22),\n 0 28px 56px -16px rgba(0, 0, 0, 0.45),\n 0 0 80px -20px rgba(99, 102, 241, 0.35);\n overflow: visible;\n}\n\n#ttp-ecom-strip::before {\n content: \"\";\n position: absolute;\n inset: 0;\n border-radius: inherit;\n pointer-events: none;\n z-index: 0;\n background: linear-gradient(\n 165deg,\n rgba(255, 255, 255, 0.08) 0%,\n rgba(255, 255, 255, 0) 42%,\n rgba(0, 0, 0, 0.12) 100%\n );\n}\n\n/* LIVE chip \u2014 glass capsule (backdrop blur), straddles top edge */\n#ttp-ecom-live-edge {\n position: absolute;\n left: 50%;\n top: 0;\n transform: translate(-50%, -50%);\n z-index: 4;\n max-width: calc(100% - 52px);\n pointer-events: none;\n padding: 3px 14px 4px;\n border-radius: 999px;\n background: rgba(8, 6, 24, 0.78);\n border: 1px solid rgba(255, 255, 255, 0.28);\n box-shadow:\n 0 0 0 1px rgba(0, 0, 0, 0.35),\n 0 4px 16px rgba(0, 0, 0, 0.35),\n inset 0 1px 0 rgba(255, 255, 255, 0.12);\n}\n@supports (backdrop-filter: blur(1px)) {\n #ttp-ecom-live-edge {\n backdrop-filter: blur(14px) saturate(180%);\n -webkit-backdrop-filter: blur(14px) saturate(180%);\n }\n}\n\n#ttp-ecom-main-row {\n position: relative;\n z-index: 1;\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 18px 16px 12px;\n min-height: 48px;\n}\n\n#ttp-ecom-footer {\n position: relative;\n z-index: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 0 16px 10px;\n margin-top: -4px;\n border-top: 1px solid rgba(255, 255, 255, 0.06);\n padding-top: 6px;\n}\n.ttp-ecom-info-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n padding: 2px;\n margin: 0;\n background: none;\n border: none;\n cursor: pointer;\n line-height: 0;\n border-radius: 50%;\n color: rgba(255, 255, 255, 0.35);\n transition: color 0.2s, background 0.2s;\n}\n.ttp-ecom-info-btn:hover {\n color: rgba(255, 255, 255, 0.7);\n background: rgba(255, 255, 255, 0.08);\n}\n.ttp-ecom-info-btn svg { display: block; }\n#ttp-ecom-footer span {\n font-size: 10px;\n color: rgba(255, 255, 255, 0.35);\n white-space: nowrap;\n}\n#ttp-ecom-footer a {\n color: rgba(167, 139, 250, 0.95);\n text-decoration: none;\n font-weight: 600;\n}\n#ttp-ecom-footer a:hover { opacity: 0.85; }\n#ttp-ecom-footer a b { font-weight: 700; }\n\n.ttp-ecom-recipe-wrap {\n position: relative;\n display: inline-flex;\n align-items: center;\n}\n.ttp-ecom-recipe-btn {\n position: relative;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 3px 7px 3px 5px;\n margin: 0;\n background: rgba(255, 255, 255, 0.06);\n border: 1px solid rgba(167, 139, 250, 0.42);\n border-radius: 999px;\n cursor: pointer;\n line-height: 1;\n color: rgba(255, 255, 255, 0.92);\n transition: color 0.2s, background 0.2s, border-color 0.2s, box-shadow 0.2s;\n}\n.ttp-ecom-recipe-btn:hover {\n color: #fff;\n background: rgba(255, 255, 255, 0.1);\n border-color: rgba(196, 181, 253, 0.65);\n}\n.ttp-ecom-recipe-btn svg { display: block; flex-shrink: 0; }\n.ttp-ecom-recipe-count {\n font-size: 10px;\n font-weight: 800;\n min-width: 12px;\n text-align: center;\n letter-spacing: 0.02em;\n}\n.ttp-ecom-recipe-btn--celebrate {\n animation: ttp-ecom-recipe-celebrate 1.35s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;\n}\n.ttp-ecom-recipe-btn--celebrate::after {\n content: \"\";\n position: absolute;\n inset: -3px;\n border-radius: 999px;\n border: 2px solid rgba(74, 222, 128, 0.75);\n animation: ttp-ecom-recipe-ring 1.35s ease-out forwards;\n pointer-events: none;\n}\n.ttp-ecom-recipe-count--pop {\n animation: ttp-ecom-recipe-count-pop 1.35s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;\n}\n@keyframes ttp-ecom-recipe-celebrate {\n 0% {\n transform: scale(1);\n color: rgba(167, 139, 250, 0.92);\n background: rgba(255, 255, 255, 0.06);\n box-shadow: none;\n }\n 18% {\n transform: scale(1.22);\n color: #86efac;\n background: rgba(74, 222, 128, 0.24);\n border-color: rgba(74, 222, 128, 0.55);\n box-shadow: 0 0 22px rgba(74, 222, 128, 0.5), 0 0 40px rgba(74, 222, 128, 0.2);\n }\n 36% {\n transform: scale(1.06);\n color: #bbf7d0;\n box-shadow: 0 0 14px rgba(74, 222, 128, 0.35);\n }\n 54% {\n transform: scale(1.16);\n color: #4ade80;\n background: rgba(74, 222, 128, 0.3);\n box-shadow: 0 0 28px rgba(74, 222, 128, 0.55);\n }\n 72% {\n transform: scale(1.08);\n color: #86efac;\n }\n 100% {\n transform: scale(1);\n color: rgba(167, 139, 250, 0.95);\n background: rgba(74, 222, 128, 0.1);\n border-color: rgba(74, 222, 128, 0.28);\n box-shadow: 0 0 12px rgba(74, 222, 128, 0.15);\n }\n}\n@keyframes ttp-ecom-recipe-ring {\n 0% { transform: scale(0.9); opacity: 0.85; }\n 100% { transform: scale(2.1); opacity: 0; }\n}\n@keyframes ttp-ecom-recipe-count-pop {\n 0%, 100% { transform: scale(1); }\n 20% { transform: scale(1.45); color: #fff; }\n 45% { transform: scale(1.15); }\n 60% { transform: scale(1.35); color: #dcfce7; }\n}\n.ttp-ecom-recipe-popover {\n position: absolute;\n bottom: calc(100% + 8px);\n left: 50%;\n transform: translateX(-50%);\n min-width: 180px;\n max-width: 240px;\n padding: 8px 0 6px;\n border-radius: 14px;\n background: rgba(12, 10, 28, 0.94);\n border: 1px solid rgba(255, 255, 255, 0.14);\n box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45), 0 0 0 1px rgba(0, 0, 0, 0.2);\n z-index: 6;\n pointer-events: auto;\n}\n@supports (backdrop-filter: blur(1px)) {\n .ttp-ecom-recipe-popover {\n backdrop-filter: blur(16px) saturate(160%);\n -webkit-backdrop-filter: blur(16px) saturate(160%);\n }\n}\n.ttp-ecom-recipe-popover-label {\n display: block;\n padding: 2px 12px 6px;\n font-size: 9px;\n font-weight: 800;\n letter-spacing: 0.12em;\n text-transform: uppercase;\n color: rgba(255, 255, 255, 0.4);\n}\n.ttp-ecom-recipe-row {\n display: block;\n width: 100%;\n padding: 8px 12px;\n margin: 0;\n border: none;\n background: transparent;\n color: rgba(255, 255, 255, 0.92);\n font: 500 12px/1.35 ui-sans-serif, system-ui, sans-serif;\n text-align: start;\n cursor: pointer;\n transition: background 0.15s;\n}\n.ttp-ecom-recipe-row:hover {\n background: rgba(255, 255, 255, 0.08);\n}\n.ttp-ecom-recipe-row-title {\n display: block;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.ttp-ecom-recipe-more {\n padding: 4px 12px 2px;\n font-size: 10px;\n color: rgba(167, 139, 250, 0.85);\n text-align: center;\n}\n\n#ttp-ecom-left-controls,\n#ttp-ecom-right-cluster {\n display: flex;\n align-items: center;\n gap: 7px;\n flex-shrink: 0;\n}\n\n/* Inset \u201Cmeter bridge\u201D for transcript \u2014 keeps one-line layout, adds depth */\n#ttp-ecom-center {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n align-items: stretch;\n justify-content: center;\n text-align: start;\n padding: 7px 12px 7px 14px;\n border-radius: 16px;\n background: rgba(0, 0, 0, 0.16);\n border: 1px solid rgba(255, 255, 255, 0.08);\n box-shadow:\n inset 0 1px 0 rgba(255, 255, 255, 0.07),\n inset 0 -1px 0 rgba(0, 0, 0, 0.12);\n}\n\n#ttp-ecom-live-row {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n min-width: 0;\n}\n\n#ttp-ecom-live-label {\n font-size: 9px;\n font-weight: 800;\n letter-spacing: 0.14em;\n text-transform: uppercase;\n color: var(--ecom-accent, #4ade80);\n opacity: 0.98;\n white-space: nowrap;\n}\n\n#ttp-ecom-dot {\n width: 7px;\n height: 7px;\n border-radius: 50%;\n background: var(--ecom-accent, #4ade80);\n flex-shrink: 0;\n box-shadow: 0 0 14px rgba(74, 222, 128, 0.45);\n animation: ttp-ecom-blink 1.35s ease-in-out infinite;\n}\n#ttp-ecom-dot.paused {\n animation: none;\n opacity: 0.45;\n background: rgba(255,255,255,0.35);\n box-shadow: none;\n}\n@keyframes ttp-ecom-blink {\n 0%, 100% { opacity: 1; transform: scale(1); }\n 50% { opacity: 0.25; transform: scale(0.92); }\n}\n\n#ttp-ecom-transcript {\n font-size: 12px;\n line-height: 1.42;\n letter-spacing: 0.015em;\n color: rgba(255, 255, 255, 0.94);\n width: 100%;\n max-width: 100%;\n overflow: hidden;\n word-break: break-word;\n overflow-wrap: anywhere;\n display: -webkit-box;\n -webkit-box-orient: vertical;\n -webkit-line-clamp: 2;\n line-clamp: 2;\n}\n\n#ttp-ecom-transcript.ttp-ecom-transcript--user {\n color: rgba(255, 244, 214, 0.98);\n font-weight: 600;\n letter-spacing: 0.02em;\n text-shadow: 0 0 28px rgba(251, 191, 36, 0.22);\n}\n\n/* Squircle glass controls */\n#ttp-ecom-strip .ttp-ecom-ctrl {\n width: 36px;\n height: 36px;\n border-radius: 12px;\n border: 1px solid rgba(255, 255, 255, 0.16);\n background: linear-gradient(165deg, rgba(255,255,255,0.14) 0%, rgba(255,255,255,0.05) 100%);\n color: rgba(255, 255, 255, 0.92);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 0;\n position: relative;\n box-shadow:\n inset 0 1px 0 rgba(255, 255, 255, 0.18),\n 0 2px 8px rgba(0, 0, 0, 0.15);\n transition: background 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease, transform 0.14s ease;\n outline: none;\n}\n#ttp-ecom-strip .ttp-ecom-ctrl:hover {\n background: linear-gradient(165deg, rgba(255,255,255,0.22) 0%, rgba(255,255,255,0.08) 100%);\n border-color: rgba(255, 255, 255, 0.32);\n box-shadow:\n inset 0 1px 0 rgba(255, 255, 255, 0.22),\n 0 4px 14px rgba(0, 0, 0, 0.2);\n}\n#ttp-ecom-strip .ttp-ecom-ctrl:active { transform: scale(0.94); }\n#ttp-ecom-strip .ttp-ecom-ctrl:focus-visible {\n outline: 2px solid rgba(165, 180, 252, 0.95);\n outline-offset: 2px;\n}\n#ttp-ecom-strip .ttp-ecom-ctrl-active {\n background: linear-gradient(165deg, rgba(129, 140, 248, 0.45) 0%, rgba(99, 102, 241, 0.28) 100%) !important;\n border-color: rgba(196, 181, 253, 0.65) !important;\n box-shadow:\n inset 0 1px 0 rgba(255, 255, 255, 0.2),\n 0 0 0 1px rgba(99, 102, 241, 0.35),\n 0 6px 18px rgba(79, 70, 229, 0.35) !important;\n}\n#ttp-ecom-strip .ttp-ecom-ctrl-warn {\n background: linear-gradient(165deg, rgba(252, 165, 165, 0.22) 0%, rgba(239, 68, 68, 0.2) 100%) !important;\n border-color: rgba(252, 165, 165, 0.55) !important;\n}\n\n#ttp-ecom-strip .ttp-ecom-ctrl-end {\n width: 40px;\n height: 40px;\n border-radius: 14px;\n background: linear-gradient(165deg, #fb7185 0%, var(--ecom-end, #ef4444) 52%, #991b1b 100%) !important;\n border: none !important;\n color: #fff !important;\n box-shadow:\n inset 0 1px 0 rgba(255, 255, 255, 0.25),\n 0 0 0 2px rgba(0, 0, 0, 0.12),\n 0 6px 22px rgba(239, 68, 68, 0.48);\n transform: rotate(135deg);\n transition: filter 0.18s ease, transform 0.14s ease, box-shadow 0.2s ease;\n}\n#ttp-ecom-strip .ttp-ecom-ctrl-end:hover {\n filter: brightness(1.06);\n transform: rotate(135deg) scale(1.05);\n box-shadow:\n inset 0 1px 0 rgba(255, 255, 255, 0.28),\n 0 0 0 2px rgba(0, 0, 0, 0.12),\n 0 10px 28px rgba(239, 68, 68, 0.52);\n}\n#ttp-ecom-strip .ttp-ecom-ctrl-end:active {\n transform: rotate(135deg) scale(0.94);\n}\n\n/* Mini waveform */\n#ttp-ecom-strip .ttp-ecom-mini-wave {\n display: flex;\n align-items: center;\n gap: 3px;\n height: 24px;\n padding: 0 4px;\n opacity: 0.9;\n}\n#ttp-ecom-strip .ttp-ecom-mini-wave .ttp-ecom-wb {\n width: 3px;\n border-radius: 3px;\n background: linear-gradient(180deg, #ddd6fe 0%, var(--ecom-accent, #a78bfa) 100%);\n animation: ttp-ecom-wa 0.78s ease-in-out infinite;\n}\n#ttp-ecom-strip .ttp-ecom-mini-wave .ttp-ecom-wb:nth-child(1) { height: 6px; animation-delay: 0s; }\n#ttp-ecom-strip .ttp-ecom-mini-wave .ttp-ecom-wb:nth-child(2) { height: 11px; animation-delay: 0.1s; }\n#ttp-ecom-strip .ttp-ecom-mini-wave .ttp-ecom-wb:nth-child(3) { height: 16px; animation-delay: 0.2s; }\n#ttp-ecom-strip .ttp-ecom-mini-wave .ttp-ecom-wb:nth-child(4) { height: 9px; animation-delay: 0.15s; }\n#ttp-ecom-strip .ttp-ecom-mini-wave .ttp-ecom-wb:nth-child(5) { height: 12px; animation-delay: 0.05s; }\n@keyframes ttp-ecom-wa {\n 0%, 100% { transform: scaleY(0.38); opacity: 0.5; }\n 50% { transform: scaleY(1); opacity: 1; }\n}\n#ttp-ecom-strip .ttp-ecom-mini-wave .ttp-ecom-wb.paused {\n animation-play-state: paused;\n transform: scaleY(0.32);\n opacity: 0.28;\n}\n\n/* Input row */\n#ttp-ecom-input-row {\n display: none;\n position: relative;\n z-index: 1;\n align-items: center;\n gap: 10px;\n padding: 0 14px 12px;\n margin-top: -2px;\n border-top: 1px solid rgba(255, 255, 255, 0.1);\n}\n#ttp-ecom-input-row.open {\n display: flex;\n}\n#ttp-ecom-input {\n flex: 1;\n height: 40px;\n border-radius: 12px;\n border: 1px solid rgba(255, 255, 255, 0.18);\n background: rgba(0, 0, 0, 0.22);\n color: #fff;\n font-size: 14px;\n padding: 0 14px;\n outline: none;\n box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.2);\n transition: border-color 0.18s ease, box-shadow 0.18s ease;\n}\n#ttp-ecom-input::placeholder { color: rgba(255, 255, 255, 0.42); }\n#ttp-ecom-input:focus {\n border-color: rgba(165, 180, 252, 0.65);\n box-shadow:\n inset 0 1px 2px rgba(0, 0, 0, 0.2),\n 0 0 0 3px rgba(99, 102, 241, 0.25);\n}\n\n#ttp-ecom-strip .ttp-ecom-send {\n width: 36px;\n height: 36px;\n border-radius: 12px;\n border: none;\n background: linear-gradient(165deg, rgba(255,255,255,0.22) 0%, rgba(255,255,255,0.1) 100%);\n color: #fff;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2);\n transition: background 0.18s ease, transform 0.12s ease;\n}\n#ttp-ecom-strip .ttp-ecom-send:hover {\n background: linear-gradient(165deg, rgba(255,255,255,0.3) 0%, rgba(255,255,255,0.14) 100%);\n}\n#ttp-ecom-strip .ttp-ecom-send:active { transform: scale(0.94); }\n\n@media (max-width: 520px) {\n #ttp-ecom-strip .ttp-ecom-mini-wave { display: none; }\n #ttp-ecom-main-row { gap: 7px; padding: 16px 12px 10px; }\n #ttp-ecom-strip .ttp-ecom-ctrl { width: 36px; height: 36px; }\n #ttp-ecom-strip .ttp-ecom-ctrl-end { width: 40px; height: 40px; }\n}\n\n@media (prefers-reduced-motion: reduce) {\n #ttp-ecom-dot { animation: none; opacity: 0.9; }\n #ttp-ecom-strip .ttp-ecom-mini-wave .ttp-ecom-wb { animation: none; opacity: 0.65; }\n .ttp-ecom-recipe-btn--celebrate,\n .ttp-ecom-recipe-count--pop {\n animation: none !important;\n }\n .ttp-ecom-recipe-btn--celebrate::after { display: none; }\n}\n";
14079
14286
 
14080
14287
  /***/ }),
14081
14288
 
@@ -16015,6 +16222,529 @@ var ProductPicker = /*#__PURE__*/function () {
16015
16222
 
16016
16223
  /***/ }),
16017
16224
 
16225
+ /***/ "./src/ecommerce/RecipeHandler.js":
16226
+ /*!****************************************!*\
16227
+ !*** ./src/ecommerce/RecipeHandler.js ***!
16228
+ \****************************************/
16229
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
16230
+
16231
+ "use strict";
16232
+ __webpack_require__.r(__webpack_exports__);
16233
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
16234
+ /* harmony export */ buildRecipeHtml: () => (/* binding */ buildRecipeHtml),
16235
+ /* harmony export */ createRecipeHandler: () => (/* binding */ createRecipeHandler)
16236
+ /* harmony export */ });
16237
+ /* harmony import */ var _partnerLogo_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./partnerLogo.js */ "./src/ecommerce/partnerLogo.js");
16238
+ function _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = "function" == typeof Symbol ? Symbol : {}, n = r.iterator || "@@iterator", o = r.toStringTag || "@@toStringTag"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, "_invoke", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError("Generator is already running"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = "next"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError("iterator result is not an object"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i.return) && t.call(i), c < 2 && (u = TypeError("The iterator does not provide a '" + o + "' method"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, "GeneratorFunction")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, "constructor", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = "GeneratorFunction", _regeneratorDefine2(GeneratorFunctionPrototype, o, "GeneratorFunction"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, "Generator"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, "toString", function () { return "[object Generator]"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); }
16239
+ function _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, "", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o("next", 0), o("throw", 1), o("return", 2)); }, _regeneratorDefine2(e, r, n, t); }
16240
+ function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }
16241
+ function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; }
16242
+ function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
16243
+ /**
16244
+ * Recipe Handler — centered modal + self-contained HTML download.
16245
+ *
16246
+ * Backend wire: { t: 'show_recipe', recipe: { title, ingredients[], steps[], servings?, notes? } }
16247
+ * Triggered sync-to-speech when the agent calls send_recipe.
16248
+ */
16249
+
16250
+
16251
+ var STYLES_ID = 'ttp-recipe-handler-styles';
16252
+ function escapeHtml(str) {
16253
+ return String(str !== null && str !== void 0 ? str : '').replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
16254
+ }
16255
+ function slugify(title) {
16256
+ var slug = String(title !== null && title !== void 0 ? title : 'recipe').trim().toLowerCase().replace(/[^a-z0-9]+/gi, '-').replace(/^-+|-+$/g, '');
16257
+ return slug || 'recipe';
16258
+ }
16259
+ function normalizeRecipe(input) {
16260
+ var _recipe$title;
16261
+ if (!input || _typeof(input) !== 'object') return null;
16262
+ var recipe = input.recipe && _typeof(input.recipe) === 'object' ? input.recipe : input;
16263
+ var title = String((_recipe$title = recipe.title) !== null && _recipe$title !== void 0 ? _recipe$title : '').trim();
16264
+ var ingredients = Array.isArray(recipe.ingredients) ? recipe.ingredients.map(function (x) {
16265
+ return String(x).trim();
16266
+ }).filter(Boolean) : [];
16267
+ var steps = Array.isArray(recipe.steps) ? recipe.steps.map(function (x) {
16268
+ return String(x).trim();
16269
+ }).filter(Boolean) : [];
16270
+ if (!title || ingredients.length === 0 && steps.length === 0) {
16271
+ console.warn('[RecipeHandler] Invalid recipe payload — need title plus ingredients or steps', input);
16272
+ return null;
16273
+ }
16274
+ return {
16275
+ title: title,
16276
+ ingredients: ingredients,
16277
+ steps: steps,
16278
+ servings: recipe.servings ? String(recipe.servings).trim() : '',
16279
+ notes: recipe.notes ? String(recipe.notes).trim() : ''
16280
+ };
16281
+ }
16282
+
16283
+ /** Shared download-page CSS (self-contained, no external assets). */
16284
+ var RECIPE_PAGE_CSS = "\n :root {\n --bg: #f6f3ee;\n --paper: #fffcf8;\n --ink: #1c1917;\n --muted: #78716c;\n --accent: #2f6b52;\n --accent-soft: #e8f2ec;\n --accent-deep: #1e4d3a;\n --warm: #c4713a;\n --warm-soft: #fdf3eb;\n --line: rgba(28, 25, 23, 0.08);\n --shadow: 0 24px 60px rgba(28, 25, 23, 0.12);\n --radius: 20px;\n }\n * { box-sizing: border-box; }\n body {\n margin: 0;\n min-height: 100vh;\n font-family: \"Segoe UI\", system-ui, -apple-system, sans-serif;\n color: var(--ink);\n background:\n radial-gradient(circle at top right, rgba(47, 107, 82, 0.08), transparent 28rem),\n radial-gradient(circle at bottom left, rgba(196, 113, 58, 0.08), transparent 24rem),\n var(--bg);\n line-height: 1.55;\n padding: 32px 16px 48px;\n }\n .sheet {\n max-width: 760px;\n margin: 0 auto;\n background: var(--paper);\n border-radius: calc(var(--radius) + 4px);\n box-shadow: var(--shadow);\n overflow: hidden;\n border: 1px solid rgba(255, 255, 255, 0.7);\n }\n .hero {\n position: relative;\n padding: 36px 32px 28px;\n background:\n linear-gradient(135deg, var(--accent-deep) 0%, var(--accent) 55%, #3d8b67 100%);\n color: #fff;\n overflow: hidden;\n }\n .hero::before {\n content: \"\";\n position: absolute;\n inset: 0;\n background:\n radial-gradient(circle at 85% 15%, rgba(255,255,255,0.16), transparent 42%),\n radial-gradient(circle at 10% 100%, rgba(255,255,255,0.08), transparent 35%);\n pointer-events: none;\n }\n .hero-inner { position: relative; z-index: 1; }\n .hero-head {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 16px;\n margin-bottom: 12px;\n }\n .hero-head .eyebrow { margin-bottom: 0; }\n .partner-logo-wrap {\n flex-shrink: 0;\n background: rgba(255, 255, 255, 0.95);\n border-radius: 12px;\n padding: 8px 12px;\n max-width: 140px;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n .partner-logo {\n height: 28px;\n width: auto;\n max-width: 120px;\n object-fit: contain;\n display: block;\n }\n .eyebrow {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n font-size: 11px;\n font-weight: 700;\n letter-spacing: 0.14em;\n text-transform: uppercase;\n opacity: 0.88;\n margin-bottom: 12px;\n }\n .eyebrow-dot {\n width: 7px;\n height: 7px;\n border-radius: 50%;\n background: #ffd166;\n box-shadow: 0 0 0 4px rgba(255, 209, 102, 0.22);\n }\n h1 {\n margin: 0;\n font-family: \"Iowan Old Style\", \"Palatino Linotype\", \"Book Antiqua\", Georgia, serif;\n font-size: clamp(2rem, 5vw, 2.75rem);\n line-height: 1.08;\n font-weight: 600;\n letter-spacing: -0.02em;\n }\n .servings {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n margin-top: 16px;\n padding: 8px 14px;\n border-radius: 999px;\n background: rgba(255, 255, 255, 0.14);\n border: 1px solid rgba(255, 255, 255, 0.18);\n backdrop-filter: blur(8px);\n font-size: 14px;\n font-weight: 500;\n }\n .content { padding: 28px 28px 32px; }\n .grid {\n display: grid;\n grid-template-columns: minmax(0, 0.95fr) minmax(0, 1.05fr);\n gap: 24px;\n }\n .section { min-width: 0; }\n .section-title {\n display: flex;\n align-items: center;\n gap: 10px;\n margin: 0 0 14px;\n font-size: 12px;\n font-weight: 800;\n letter-spacing: 0.12em;\n text-transform: uppercase;\n color: var(--accent-deep);\n }\n .section-title::after {\n content: \"\";\n flex: 1;\n height: 1px;\n background: linear-gradient(to right, var(--line), transparent);\n }\n .ingredients-box {\n margin: 0;\n padding: 0;\n list-style: none;\n background: var(--accent-soft);\n border: 1px solid rgba(47, 107, 82, 0.12);\n border-radius: 16px;\n overflow: hidden;\n }\n .ingredients-box li {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n padding: 12px 16px;\n border-bottom: 1px solid rgba(47, 107, 82, 0.08);\n font-size: 15px;\n }\n .ingredients-box li:last-child { border-bottom: 0; }\n .ingredients-box li::before {\n content: \"\";\n flex-shrink: 0;\n width: 8px;\n height: 8px;\n margin-top: 0.45em;\n border-radius: 50%;\n background: var(--accent);\n box-shadow: 0 0 0 3px rgba(47, 107, 82, 0.14);\n }\n .steps { margin: 0; padding: 0; list-style: none; display: grid; gap: 12px; }\n .step {\n display: grid;\n grid-template-columns: 36px minmax(0, 1fr);\n gap: 14px;\n padding: 14px 16px;\n background: #fff;\n border: 1px solid var(--line);\n border-radius: 16px;\n box-shadow: 0 8px 24px rgba(28, 25, 23, 0.04);\n }\n .step-num {\n width: 36px;\n height: 36px;\n border-radius: 12px;\n display: grid;\n place-items: center;\n background: linear-gradient(180deg, var(--warm-soft), #fff);\n border: 1px solid rgba(196, 113, 58, 0.18);\n color: var(--warm);\n font-size: 14px;\n font-weight: 800;\n }\n .step-text { font-size: 15px; color: #292524; padding-top: 6px; }\n .notes {\n margin-top: 24px;\n padding: 16px 18px 16px 20px;\n border-radius: 16px;\n background: var(--warm-soft);\n border: 1px solid rgba(196, 113, 58, 0.16);\n color: #57534e;\n font-size: 14px;\n }\n .notes strong {\n display: block;\n margin-bottom: 6px;\n color: #9a3412;\n font-size: 12px;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n }\n .footer {\n margin-top: 28px;\n padding-top: 18px;\n border-top: 1px solid var(--line);\n text-align: center;\n font-size: 12px;\n color: var(--muted);\n letter-spacing: 0.04em;\n }\n @media (max-width: 720px) {\n .hero { padding: 28px 22px 22px; }\n .content { padding: 22px 18px 26px; }\n .grid { grid-template-columns: 1fr; gap: 22px; }\n }\n @media print {\n body { background: #fff; padding: 0; }\n .sheet { box-shadow: none; border: none; max-width: none; }\n }\n";
16285
+ function buildIngredientsHtml(ingredients) {
16286
+ if (!ingredients.length) return '';
16287
+ var items = ingredients.map(function (item) {
16288
+ return "<li>".concat(escapeHtml(item), "</li>");
16289
+ }).join('');
16290
+ return "\n <section class=\"section\">\n <h2 class=\"section-title\">Ingredients</h2>\n <ul class=\"ingredients-box\">".concat(items, "</ul>\n </section>");
16291
+ }
16292
+ function buildStepsHtml(steps) {
16293
+ if (!steps.length) return '';
16294
+ var items = steps.map(function (step, i) {
16295
+ return "\n <li class=\"step\">\n <div class=\"step-num\">".concat(i + 1, "</div>\n <div class=\"step-text\">").concat(escapeHtml(step), "</div>\n </li>");
16296
+ }).join('');
16297
+ return "\n <section class=\"section\">\n <h2 class=\"section-title\">Method</h2>\n <ol class=\"steps\">".concat(items, "</ol>\n </section>");
16298
+ }
16299
+ function buildNotesHtml(notes) {
16300
+ if (!notes) return '';
16301
+ return "<div class=\"notes\"><strong>Chef's tip</strong>".concat(escapeHtml(notes), "</div>");
16302
+ }
16303
+ function buildPartnerLogoHtml(logoUrl) {
16304
+ if (!logoUrl) return '';
16305
+ return "<div class=\"partner-logo-wrap\"><img class=\"partner-logo\" src=\"".concat(escapeHtml(logoUrl), "\" alt=\"\"></div>");
16306
+ }
16307
+ function buildHeroHeadHtml(logoUrl) {
16308
+ var logo = buildPartnerLogoHtml(logoUrl);
16309
+ var eyebrow = '<div class="eyebrow"><span class="eyebrow-dot"></span> Recipe card</div>';
16310
+ if (!logo) return eyebrow;
16311
+ return "<div class=\"hero-head\">".concat(eyebrow).concat(logo, "</div>");
16312
+ }
16313
+ function buildRecipeHtml(recipe) {
16314
+ var logoUrl = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
16315
+ var servings = recipe.servings ? "<div class=\"servings\"><span aria-hidden=\"true\">\uD83C\uDF7D</span> ".concat(escapeHtml(recipe.servings), "</div>") : '';
16316
+ return "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <title>".concat(escapeHtml(recipe.title), "</title>\n <style>").concat(RECIPE_PAGE_CSS, "</style>\n</head>\n<body>\n <article class=\"sheet\">\n <header class=\"hero\">\n <div class=\"hero-inner\">\n ").concat(buildHeroHeadHtml(logoUrl), "\n <h1>").concat(escapeHtml(recipe.title), "</h1>\n ").concat(servings, "\n </div>\n </header>\n <div class=\"content\">\n <div class=\"grid\">\n ").concat(buildIngredientsHtml(recipe.ingredients), "\n ").concat(buildStepsHtml(recipe.steps), "\n </div>\n ").concat(buildNotesHtml(recipe.notes), "\n <p class=\"footer\">Saved from TalkToPC \xB7 Your voice shopping assistant</p>\n </div>\n </article>\n</body>\n</html>");
16317
+ }
16318
+ function downloadRecipeHtml(recipe) {
16319
+ var logoUrl = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
16320
+ try {
16321
+ var html = buildRecipeHtml(recipe, logoUrl);
16322
+ var blob = new Blob([html], {
16323
+ type: 'text/html;charset=utf-8'
16324
+ });
16325
+ var url = URL.createObjectURL(blob);
16326
+ var a = document.createElement('a');
16327
+ a.href = url;
16328
+ a.download = "".concat(slugify(recipe.title), ".html");
16329
+ a.rel = 'noopener';
16330
+ document.body.appendChild(a);
16331
+ a.click();
16332
+ a.remove();
16333
+ setTimeout(function () {
16334
+ return URL.revokeObjectURL(url);
16335
+ }, 1000);
16336
+ console.log('[RecipeHandler] Downloaded recipe HTML:', a.download);
16337
+ } catch (e) {
16338
+ console.warn('[RecipeHandler] HTML download failed:', e);
16339
+ }
16340
+ }
16341
+ function injectStyles() {
16342
+ if (document.getElementById(STYLES_ID)) return;
16343
+ var s = document.createElement('style');
16344
+ s.id = STYLES_ID;
16345
+ s.textContent = "\n.ttp-rh-backdrop {\n position: fixed;\n inset: 0;\n z-index: 2147483640;\n background: rgba(15, 23, 20, 0);\n backdrop-filter: blur(0px);\n -webkit-backdrop-filter: blur(0px);\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 16px;\n box-sizing: border-box;\n transition: background 0.32s ease, backdrop-filter 0.32s ease;\n pointer-events: none;\n}\n.ttp-rh-backdrop.ttp-rh-visible {\n background: rgba(15, 23, 20, 0.52);\n backdrop-filter: blur(10px);\n -webkit-backdrop-filter: blur(10px);\n pointer-events: all;\n}\n.ttp-rh-card {\n position: relative;\n width: min(94vw, 680px);\n max-height: calc(100vh - 32px);\n background: #fffcf8;\n border-radius: 24px;\n box-shadow: 0 28px 80px rgba(15, 23, 20, 0.22), 0 8px 24px rgba(15, 23, 20, 0.08);\n display: flex;\n flex-direction: column;\n opacity: 0;\n transform: scale(0.94) translateY(12px);\n transition: opacity 0.32s cubic-bezier(0.23, 1, 0.32, 1),\n transform 0.32s cubic-bezier(0.23, 1, 0.32, 1);\n overflow: hidden;\n border: 1px solid rgba(255, 255, 255, 0.65);\n}\n.ttp-rh-backdrop.ttp-rh-visible .ttp-rh-card {\n opacity: 1;\n transform: scale(1) translateY(0);\n}\n.ttp-rh-hero {\n position: relative;\n padding: 28px 56px 22px 24px;\n background: linear-gradient(135deg, #1e4d3a 0%, #2f6b52 55%, #3d8b67 100%);\n color: #fff;\n flex-shrink: 0;\n}\n.ttp-rh-hero::before {\n content: \"\";\n position: absolute;\n inset: 0;\n background:\n radial-gradient(circle at 85% 15%, rgba(255,255,255,0.16), transparent 42%),\n radial-gradient(circle at 10% 100%, rgba(255,255,255,0.08), transparent 35%);\n pointer-events: none;\n}\n.ttp-rh-hero-inner { position: relative; z-index: 1; }\n.ttp-rh-hero-head {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 12px;\n margin-bottom: 10px;\n padding-right: 36px;\n}\n.ttp-rh-hero-head .ttp-rh-eyebrow { margin-bottom: 0; }\n.ttp-rh-partner-logo-wrap {\n flex-shrink: 0;\n background: rgba(255, 255, 255, 0.95);\n border-radius: 10px;\n padding: 6px 10px;\n max-width: 120px;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n.ttp-rh-partner-logo {\n height: 26px;\n width: auto;\n max-width: 100px;\n object-fit: contain;\n display: block;\n}\n.ttp-rh-eyebrow {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n font: 700 11px/1 \"Segoe UI\", system-ui, sans-serif;\n letter-spacing: 0.14em;\n text-transform: uppercase;\n opacity: 0.88;\n margin-bottom: 10px;\n}\n.ttp-rh-eyebrow-dot {\n width: 7px;\n height: 7px;\n border-radius: 50%;\n background: #ffd166;\n box-shadow: 0 0 0 4px rgba(255, 209, 102, 0.22);\n}\n.ttp-rh-title {\n font-family: \"Iowan Old Style\", \"Palatino Linotype\", \"Book Antiqua\", Georgia, serif;\n font-size: clamp(1.45rem, 4vw, 1.95rem);\n font-weight: 600;\n line-height: 1.1;\n letter-spacing: -0.02em;\n}\n.ttp-rh-servings {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n margin-top: 14px;\n padding: 7px 13px;\n border-radius: 999px;\n background: rgba(255, 255, 255, 0.14);\n border: 1px solid rgba(255, 255, 255, 0.18);\n font: 500 13px/1.2 \"Segoe UI\", system-ui, sans-serif;\n}\n.ttp-rh-hero-actions {\n display: flex;\n flex-wrap: wrap;\n gap: 10px;\n margin-top: 18px;\n}\n.ttp-rh-hero-actions .ttp-rh-btn {\n flex: 0 1 auto;\n padding: 10px 16px;\n border: none;\n border-radius: 12px;\n font: 600 13px/1 \"Segoe UI\", system-ui, sans-serif;\n cursor: pointer;\n transition: transform 0.12s ease, background 0.15s ease, box-shadow 0.15s ease;\n}\n.ttp-rh-hero-actions .ttp-rh-btn:active { transform: scale(0.98); }\n.ttp-rh-hero-actions .ttp-rh-btn-secondary {\n background: rgba(255, 255, 255, 0.14);\n color: #fff;\n border: 1px solid rgba(255, 255, 255, 0.28);\n}\n.ttp-rh-hero-actions .ttp-rh-btn-secondary:hover { background: rgba(255, 255, 255, 0.22); }\n.ttp-rh-hero-actions .ttp-rh-btn-primary {\n background: #fff;\n color: #1e4d3a;\n box-shadow: 0 8px 20px rgba(15, 23, 20, 0.18);\n}\n.ttp-rh-hero-actions .ttp-rh-btn-primary:hover { background: #f4faf7; }\n.ttp-rh-close {\n position: absolute;\n top: 16px;\n right: 16px;\n z-index: 2;\n width: 36px;\n height: 36px;\n padding: 0;\n border: 1px solid rgba(255, 255, 255, 0.22);\n border-radius: 50%;\n background: rgba(255, 255, 255, 0.12);\n color: #fff;\n cursor: pointer;\n font-size: 22px;\n line-height: 1;\n backdrop-filter: blur(8px);\n}\n.ttp-rh-close:hover { background: rgba(255, 255, 255, 0.2); }\n.ttp-rh-body {\n padding: 20px 22px 8px;\n overflow-y: auto;\n flex: 1;\n background: linear-gradient(180deg, #fffcf8 0%, #f8f5f0 100%);\n}\n.ttp-rh-grid {\n display: grid;\n grid-template-columns: minmax(0, 0.95fr) minmax(0, 1.05fr);\n gap: 18px;\n}\n.ttp-rh-section { min-width: 0; margin-bottom: 0; }\n.ttp-rh-section-title {\n display: flex;\n align-items: center;\n gap: 10px;\n margin: 0 0 12px;\n font: 800 11px/1 \"Segoe UI\", system-ui, sans-serif;\n letter-spacing: 0.12em;\n text-transform: uppercase;\n color: #1e4d3a;\n}\n.ttp-rh-section-title::after {\n content: \"\";\n flex: 1;\n height: 1px;\n background: linear-gradient(to right, rgba(28,25,23,0.08), transparent);\n}\n.ttp-rh-ingredients {\n margin: 0;\n padding: 0;\n list-style: none;\n background: #e8f2ec;\n border: 1px solid rgba(47, 107, 82, 0.12);\n border-radius: 14px;\n overflow: hidden;\n}\n.ttp-rh-ingredients li {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n padding: 10px 14px;\n border-bottom: 1px solid rgba(47, 107, 82, 0.08);\n font: 14px/1.45 \"Segoe UI\", system-ui, sans-serif;\n color: #292524;\n}\n.ttp-rh-ingredients li:last-child { border-bottom: 0; }\n.ttp-rh-ingredients li::before {\n content: \"\";\n flex-shrink: 0;\n width: 7px;\n height: 7px;\n margin-top: 0.45em;\n border-radius: 50%;\n background: #2f6b52;\n box-shadow: 0 0 0 3px rgba(47, 107, 82, 0.14);\n}\n.ttp-rh-steps {\n margin: 0;\n padding: 0;\n list-style: none;\n display: grid;\n gap: 10px;\n}\n.ttp-rh-step {\n display: grid;\n grid-template-columns: 32px minmax(0, 1fr);\n gap: 12px;\n padding: 12px 14px;\n background: #fff;\n border: 1px solid rgba(28, 25, 23, 0.08);\n border-radius: 14px;\n box-shadow: 0 6px 18px rgba(28, 25, 23, 0.04);\n}\n.ttp-rh-step-num {\n width: 32px;\n height: 32px;\n border-radius: 10px;\n display: grid;\n place-items: center;\n background: linear-gradient(180deg, #fdf3eb, #fff);\n border: 1px solid rgba(196, 113, 58, 0.18);\n color: #c4713a;\n font: 800 13px/1 \"Segoe UI\", system-ui, sans-serif;\n}\n.ttp-rh-step-text {\n font: 14px/1.45 \"Segoe UI\", system-ui, sans-serif;\n color: #292524;\n padding-top: 5px;\n}\n.ttp-rh-notes-wrap { margin-top: 16px; }\n.ttp-rh-notes {\n padding: 14px 16px;\n border-radius: 14px;\n background: #fdf3eb;\n border: 1px solid rgba(196, 113, 58, 0.16);\n font: 13px/1.5 \"Segoe UI\", system-ui, sans-serif;\n color: #57534e;\n}\n.ttp-rh-notes-label {\n display: block;\n margin-bottom: 6px;\n font: 800 11px/1 \"Segoe UI\", system-ui, sans-serif;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n color: #9a3412;\n}\n@media (max-width: 640px) {\n .ttp-rh-backdrop { padding: 10px; }\n .ttp-rh-card { width: calc(100vw - 20px); border-radius: 20px; }\n .ttp-rh-grid { grid-template-columns: 1fr; }\n .ttp-rh-hero { padding: 24px 52px 22px 20px; }\n .ttp-rh-hero-actions { margin-top: 14px; }\n .ttp-rh-hero-actions .ttp-rh-btn { flex: 1 1 calc(50% - 5px); min-width: 120px; }\n}\n";
16346
+ document.head.appendChild(s);
16347
+ }
16348
+ function _appendSectionTitle(parent, label) {
16349
+ var title = document.createElement('h3');
16350
+ title.className = 'ttp-rh-section-title';
16351
+ title.textContent = label;
16352
+ parent.appendChild(title);
16353
+ }
16354
+ function createRecipeHandler(widget) {
16355
+ var backdrop = null;
16356
+ var _dismissed = false;
16357
+ var _savedHostZIndex = null;
16358
+ var _printFrame = null;
16359
+ var _lastLogoUrl = null;
16360
+ var _openedAsReshow = false;
16361
+ function _minimizeWidget() {
16362
+ try {
16363
+ var _widget$shadowRoot;
16364
+ var host = widget === null || widget === void 0 || (_widget$shadowRoot = widget.shadowRoot) === null || _widget$shadowRoot === void 0 ? void 0 : _widget$shadowRoot.host;
16365
+ if (host) {
16366
+ _savedHostZIndex = host.style.zIndex || null;
16367
+ host.style.zIndex = String(2147483641);
16368
+ }
16369
+ } catch (e) {/* best-effort */}
16370
+ }
16371
+ function _restoreWidget() {
16372
+ try {
16373
+ var _widget$shadowRoot2;
16374
+ var host = widget === null || widget === void 0 || (_widget$shadowRoot2 = widget.shadowRoot) === null || _widget$shadowRoot2 === void 0 ? void 0 : _widget$shadowRoot2.host;
16375
+ if (host) {
16376
+ host.style.zIndex = _savedHostZIndex || '';
16377
+ _savedHostZIndex = null;
16378
+ }
16379
+ } catch (e) {/* best-effort */}
16380
+ }
16381
+ function _teardownFrame() {
16382
+ var _printFrame2;
16383
+ if ((_printFrame2 = _printFrame) !== null && _printFrame2 !== void 0 && _printFrame2.parentNode) {
16384
+ _printFrame.parentNode.removeChild(_printFrame);
16385
+ }
16386
+ _printFrame = null;
16387
+ }
16388
+ function _appendHeroHead(heroInner, logoUrl) {
16389
+ var eyebrow = document.createElement('div');
16390
+ eyebrow.className = 'ttp-rh-eyebrow';
16391
+ eyebrow.innerHTML = '<span class="ttp-rh-eyebrow-dot"></span> Recipe card';
16392
+ if (!logoUrl) {
16393
+ heroInner.appendChild(eyebrow);
16394
+ return;
16395
+ }
16396
+ var head = document.createElement('div');
16397
+ head.className = 'ttp-rh-hero-head';
16398
+ head.appendChild(eyebrow);
16399
+ var logoWrap = document.createElement('div');
16400
+ logoWrap.className = 'ttp-rh-partner-logo-wrap';
16401
+ var logoImg = document.createElement('img');
16402
+ logoImg.className = 'ttp-rh-partner-logo';
16403
+ logoImg.src = logoUrl;
16404
+ logoImg.alt = '';
16405
+ logoWrap.appendChild(logoImg);
16406
+ head.appendChild(logoWrap);
16407
+ heroInner.appendChild(head);
16408
+ }
16409
+ function _buildCard(recipe) {
16410
+ var logoUrl = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
16411
+ var card = document.createElement('div');
16412
+ card.className = 'ttp-rh-card';
16413
+ card.addEventListener('click', function (e) {
16414
+ return e.stopPropagation();
16415
+ });
16416
+ var closeBtn = document.createElement('button');
16417
+ closeBtn.className = 'ttp-rh-close';
16418
+ closeBtn.setAttribute('aria-label', 'Close recipe');
16419
+ closeBtn.textContent = "\xD7";
16420
+ closeBtn.addEventListener('click', dismissRecipe);
16421
+ var hero = document.createElement('header');
16422
+ hero.className = 'ttp-rh-hero';
16423
+ var heroInner = document.createElement('div');
16424
+ heroInner.className = 'ttp-rh-hero-inner';
16425
+ _appendHeroHead(heroInner, logoUrl);
16426
+ var titleEl = document.createElement('div');
16427
+ titleEl.className = 'ttp-rh-title';
16428
+ titleEl.textContent = recipe.title;
16429
+ heroInner.appendChild(titleEl);
16430
+ if (recipe.servings) {
16431
+ var servEl = document.createElement('div');
16432
+ servEl.className = 'ttp-rh-servings';
16433
+ servEl.innerHTML = "<span aria-hidden=\"true\">\uD83C\uDF7D</span> ".concat(recipe.servings);
16434
+ heroInner.appendChild(servEl);
16435
+ }
16436
+ var actions = document.createElement('div');
16437
+ actions.className = 'ttp-rh-hero-actions';
16438
+ var saveBtn = document.createElement('button');
16439
+ saveBtn.className = 'ttp-rh-btn ttp-rh-btn-secondary';
16440
+ saveBtn.textContent = 'Save again';
16441
+ saveBtn.addEventListener('click', function () {
16442
+ return downloadRecipeHtml(recipe, logoUrl);
16443
+ });
16444
+ var printBtn = document.createElement('button');
16445
+ printBtn.className = 'ttp-rh-btn ttp-rh-btn-primary';
16446
+ printBtn.textContent = 'Print recipe';
16447
+ printBtn.addEventListener('click', function () {
16448
+ return printRecipe(recipe, logoUrl);
16449
+ });
16450
+ actions.appendChild(saveBtn);
16451
+ actions.appendChild(printBtn);
16452
+ heroInner.appendChild(actions);
16453
+ hero.appendChild(heroInner);
16454
+ hero.appendChild(closeBtn);
16455
+ card.appendChild(hero);
16456
+ var body = document.createElement('div');
16457
+ body.className = 'ttp-rh-body';
16458
+ var grid = document.createElement('div');
16459
+ grid.className = 'ttp-rh-grid';
16460
+ if (recipe.ingredients.length) {
16461
+ var sec = document.createElement('section');
16462
+ sec.className = 'ttp-rh-section';
16463
+ _appendSectionTitle(sec, 'Ingredients');
16464
+ var ul = document.createElement('ul');
16465
+ ul.className = 'ttp-rh-ingredients';
16466
+ recipe.ingredients.forEach(function (item) {
16467
+ var li = document.createElement('li');
16468
+ li.textContent = item;
16469
+ ul.appendChild(li);
16470
+ });
16471
+ sec.appendChild(ul);
16472
+ grid.appendChild(sec);
16473
+ }
16474
+ if (recipe.steps.length) {
16475
+ var _sec = document.createElement('section');
16476
+ _sec.className = 'ttp-rh-section';
16477
+ _appendSectionTitle(_sec, 'Method');
16478
+ var ol = document.createElement('ol');
16479
+ ol.className = 'ttp-rh-steps';
16480
+ recipe.steps.forEach(function (step, index) {
16481
+ var li = document.createElement('li');
16482
+ li.className = 'ttp-rh-step';
16483
+ var num = document.createElement('div');
16484
+ num.className = 'ttp-rh-step-num';
16485
+ num.textContent = String(index + 1);
16486
+ var text = document.createElement('div');
16487
+ text.className = 'ttp-rh-step-text';
16488
+ text.textContent = step;
16489
+ li.appendChild(num);
16490
+ li.appendChild(text);
16491
+ ol.appendChild(li);
16492
+ });
16493
+ _sec.appendChild(ol);
16494
+ grid.appendChild(_sec);
16495
+ }
16496
+ body.appendChild(grid);
16497
+ if (recipe.notes) {
16498
+ var notesWrap = document.createElement('div');
16499
+ notesWrap.className = 'ttp-rh-notes-wrap';
16500
+ var notes = document.createElement('div');
16501
+ notes.className = 'ttp-rh-notes';
16502
+ var label = document.createElement('span');
16503
+ label.className = 'ttp-rh-notes-label';
16504
+ label.textContent = "Chef's tip";
16505
+ notes.appendChild(label);
16506
+ notes.appendChild(document.createTextNode(recipe.notes));
16507
+ notesWrap.appendChild(notes);
16508
+ body.appendChild(notesWrap);
16509
+ }
16510
+ card.appendChild(body);
16511
+ return card;
16512
+ }
16513
+ function printRecipe(recipe) {
16514
+ var _frame$contentWindow, _frame$contentWindow2, _frame$contentWindow3;
16515
+ var logoUrl = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
16516
+ _teardownFrame();
16517
+ var frame = document.createElement('iframe');
16518
+ frame.style.cssText = 'position:fixed;right:0;bottom:0;width:0;height:0;border:0;';
16519
+ document.body.appendChild(frame);
16520
+ _printFrame = frame;
16521
+ var doc = frame.contentDocument || ((_frame$contentWindow = frame.contentWindow) === null || _frame$contentWindow === void 0 ? void 0 : _frame$contentWindow.document);
16522
+ if (!doc) return;
16523
+ doc.open();
16524
+ doc.write(buildRecipeHtml(recipe, logoUrl !== null && logoUrl !== void 0 ? logoUrl : _lastLogoUrl));
16525
+ doc.close();
16526
+ (_frame$contentWindow2 = frame.contentWindow) === null || _frame$contentWindow2 === void 0 || _frame$contentWindow2.focus();
16527
+ (_frame$contentWindow3 = frame.contentWindow) === null || _frame$contentWindow3 === void 0 || _frame$contentWindow3.print();
16528
+ }
16529
+ function handleShowRecipe(_x) {
16530
+ return _handleShowRecipe.apply(this, arguments);
16531
+ }
16532
+ function _handleShowRecipe() {
16533
+ _handleShowRecipe = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee(msg) {
16534
+ var recipe, partnerId, _t;
16535
+ return _regenerator().w(function (_context) {
16536
+ while (1) switch (_context.n) {
16537
+ case 0:
16538
+ recipe = normalizeRecipe(msg);
16539
+ if (recipe) {
16540
+ _context.n = 1;
16541
+ break;
16542
+ }
16543
+ return _context.a(2);
16544
+ case 1:
16545
+ console.log('[RecipeHandler] show_recipe:', recipe.title);
16546
+ if (backdrop) _teardownImmediate();
16547
+ _dismissed = false;
16548
+ _openedAsReshow = !!(msg !== null && msg !== void 0 && msg.reshow);
16549
+ injectStyles();
16550
+ partnerId = (0,_partnerLogo_js__WEBPACK_IMPORTED_MODULE_0__.resolvePartnerId)(widget, msg);
16551
+ if (!partnerId) {
16552
+ _context.n = 3;
16553
+ break;
16554
+ }
16555
+ _context.n = 2;
16556
+ return (0,_partnerLogo_js__WEBPACK_IMPORTED_MODULE_0__.resolvePartnerLogoUrl)(partnerId);
16557
+ case 2:
16558
+ _t = _context.v;
16559
+ _context.n = 4;
16560
+ break;
16561
+ case 3:
16562
+ _t = null;
16563
+ case 4:
16564
+ _lastLogoUrl = _t;
16565
+ if (!_openedAsReshow) downloadRecipeHtml(recipe, _lastLogoUrl);
16566
+ backdrop = document.createElement('div');
16567
+ backdrop.className = 'ttp-rh-backdrop';
16568
+ backdrop.addEventListener('click', dismissRecipe);
16569
+ backdrop.appendChild(_buildCard(recipe, _lastLogoUrl));
16570
+ document.body.appendChild(backdrop);
16571
+ requestAnimationFrame(function () {
16572
+ requestAnimationFrame(function () {
16573
+ var _backdrop;
16574
+ return (_backdrop = backdrop) === null || _backdrop === void 0 ? void 0 : _backdrop.classList.add('ttp-rh-visible');
16575
+ });
16576
+ });
16577
+ backdrop._escHandler = function (e) {
16578
+ if (e.key === 'Escape') dismissRecipe();
16579
+ };
16580
+ document.addEventListener('keydown', backdrop._escHandler);
16581
+ _minimizeWidget();
16582
+ if (!_openedAsReshow) _saveRecipe(recipe);
16583
+ case 5:
16584
+ return _context.a(2);
16585
+ }
16586
+ }, _callee);
16587
+ }));
16588
+ return _handleShowRecipe.apply(this, arguments);
16589
+ }
16590
+ function _saveRecipe(recipe) {
16591
+ try {
16592
+ var store = typeof (widget === null || widget === void 0 ? void 0 : widget.getRecipeStore) === 'function' ? widget.getRecipeStore() : null;
16593
+ if (store) store.add(recipe);
16594
+ } catch (e) {
16595
+ console.warn('[RecipeHandler] Could not save recipe:', e);
16596
+ }
16597
+ }
16598
+ function _pulseStripBadge() {
16599
+ try {
16600
+ var _widget$_ecommerceVoi, _widget$_ecommerceVoi2;
16601
+ widget === null || widget === void 0 || (_widget$_ecommerceVoi = widget._ecommerceVoiceStrip) === null || _widget$_ecommerceVoi === void 0 || (_widget$_ecommerceVoi2 = _widget$_ecommerceVoi.pulseRecipeBadge) === null || _widget$_ecommerceVoi2 === void 0 || _widget$_ecommerceVoi2.call(_widget$_ecommerceVoi);
16602
+ } catch (e) {/* best-effort */}
16603
+ }
16604
+ function dismissRecipe() {
16605
+ var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
16606
+ var _opts$animate = opts.animate,
16607
+ animate = _opts$animate === void 0 ? true : _opts$animate;
16608
+ if (_dismissed || !backdrop) return;
16609
+ _dismissed = true;
16610
+ backdrop.classList.remove('ttp-rh-visible');
16611
+ var el = backdrop;
16612
+ setTimeout(function () {
16613
+ if (el.parentNode) el.parentNode.removeChild(el);
16614
+ if (el._escHandler) document.removeEventListener('keydown', el._escHandler);
16615
+ }, 320);
16616
+ backdrop = null;
16617
+ _restoreWidget();
16618
+ if (animate && !_openedAsReshow) {
16619
+ // Slight delay so the modal fade finishes before the strip badge celebrates.
16620
+ setTimeout(function () {
16621
+ return _pulseStripBadge();
16622
+ }, 180);
16623
+ }
16624
+ _openedAsReshow = false;
16625
+ }
16626
+ function _teardownImmediate() {
16627
+ if (backdrop) {
16628
+ if (backdrop._escHandler) document.removeEventListener('keydown', backdrop._escHandler);
16629
+ if (backdrop.parentNode) backdrop.parentNode.removeChild(backdrop);
16630
+ backdrop = null;
16631
+ }
16632
+ _restoreWidget();
16633
+ }
16634
+ return {
16635
+ handleShowRecipe: handleShowRecipe,
16636
+ dismissRecipe: dismissRecipe,
16637
+ buildRecipeHtml: buildRecipeHtml,
16638
+ downloadRecipeHtml: downloadRecipeHtml
16639
+ };
16640
+ }
16641
+
16642
+ /***/ }),
16643
+
16644
+ /***/ "./src/ecommerce/RecipeStore.js":
16645
+ /*!**************************************!*\
16646
+ !*** ./src/ecommerce/RecipeStore.js ***!
16647
+ \**************************************/
16648
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
16649
+
16650
+ "use strict";
16651
+ __webpack_require__.r(__webpack_exports__);
16652
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
16653
+ /* harmony export */ createRecipeStore: () => (/* binding */ createRecipeStore),
16654
+ /* harmony export */ recipeStorageKey: () => (/* binding */ recipeStorageKey)
16655
+ /* harmony export */ });
16656
+ /**
16657
+ * Persist saved recipes in localStorage (scoped by storage key).
16658
+ * In-memory list is the source of truth; subscribers get notified on change.
16659
+ */
16660
+
16661
+ var MAX_RECIPES = 50;
16662
+ function slugify(title) {
16663
+ var slug = String(title !== null && title !== void 0 ? title : 'recipe').trim().toLowerCase().replace(/[^a-z0-9]+/gi, '-').replace(/^-+|-+$/g, '');
16664
+ return slug || 'recipe';
16665
+ }
16666
+
16667
+ /**
16668
+ * @param {string} storageKey – e.g. origin + partner id
16669
+ */
16670
+ function createRecipeStore(storageKey) {
16671
+ var recipes = [];
16672
+ var listeners = new Set();
16673
+ function _notify() {
16674
+ var snapshot = recipes.slice();
16675
+ listeners.forEach(function (fn) {
16676
+ try {
16677
+ fn(snapshot);
16678
+ } catch (e) {/* subscriber */}
16679
+ });
16680
+ }
16681
+ function _load() {
16682
+ try {
16683
+ var raw = window.localStorage.getItem(storageKey);
16684
+ if (!raw) return;
16685
+ var parsed = JSON.parse(raw);
16686
+ if (Array.isArray(parsed)) recipes = parsed;
16687
+ } catch (e) {
16688
+ console.warn('[RecipeStore] Could not load recipes:', e);
16689
+ }
16690
+ }
16691
+ function _persist() {
16692
+ try {
16693
+ window.localStorage.setItem(storageKey, JSON.stringify(recipes));
16694
+ } catch (e) {
16695
+ console.warn('[RecipeStore] Could not persist recipes:', e);
16696
+ }
16697
+ }
16698
+ function getAll() {
16699
+ return recipes.slice();
16700
+ }
16701
+ function add(recipe) {
16702
+ if (!recipe || !recipe.title) return null;
16703
+ var entry = {
16704
+ id: "".concat(slugify(recipe.title), "-").concat(Date.now()),
16705
+ title: recipe.title,
16706
+ ingredients: Array.isArray(recipe.ingredients) ? recipe.ingredients.slice() : [],
16707
+ steps: Array.isArray(recipe.steps) ? recipe.steps.slice() : [],
16708
+ servings: recipe.servings || '',
16709
+ notes: recipe.notes || '',
16710
+ savedAt: Date.now()
16711
+ };
16712
+ recipes.unshift(entry);
16713
+ if (recipes.length > MAX_RECIPES) recipes.length = MAX_RECIPES;
16714
+ _persist();
16715
+ _notify();
16716
+ return entry;
16717
+ }
16718
+ function subscribe(fn) {
16719
+ listeners.add(fn);
16720
+ return function () {
16721
+ return listeners.delete(fn);
16722
+ };
16723
+ }
16724
+ _load();
16725
+ return {
16726
+ getAll: getAll,
16727
+ add: add,
16728
+ subscribe: subscribe,
16729
+ count: function count() {
16730
+ return recipes.length;
16731
+ }
16732
+ };
16733
+ }
16734
+
16735
+ /**
16736
+ * Build a scoped storage key for the current page + partner.
16737
+ * @param {string} [partnerId]
16738
+ */
16739
+ function recipeStorageKey() {
16740
+ var partnerId = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'default';
16741
+ var origin = typeof window !== 'undefined' ? window.location.origin : '';
16742
+ var path = typeof window !== 'undefined' ? window.location.pathname : '';
16743
+ return "".concat(origin).concat(path, "_ttp_recipes_").concat(partnerId);
16744
+ }
16745
+
16746
+ /***/ }),
16747
+
16018
16748
  /***/ "./src/ecommerce/TTPEcommerceWidget.js":
16019
16749
  /*!*********************************************!*\
16020
16750
  !*** ./src/ecommerce/TTPEcommerceWidget.js ***!
@@ -16644,18 +17374,165 @@ var VariationDesktopPicker = /*#__PURE__*/function () {
16644
17374
  \******************************************/
16645
17375
  /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
16646
17376
 
16647
- "use strict";
16648
- __webpack_require__.r(__webpack_exports__);
16649
- /* harmony export */ __webpack_require__.d(__webpack_exports__, {
16650
- /* harmony export */ generateEcommerceCSS: () => (/* binding */ generateEcommerceCSS)
16651
- /* harmony export */ });
16652
- /**
16653
- * ecommerceStyles - CSS for e-commerce components.
16654
- * Returns a CSS string to be merged into the shadow root <style> tag.
16655
- * Follows the same pattern as Styles.generateCSS().
16656
- */
16657
- function generateEcommerceCSS() {
16658
- return "\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n ECOMMERCE AREA\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n\n/*\n * When products are visible, expand the panel dynamically.\n * Actual height is set via inline style by EcommerceManager._expandPanelToFit().\n * When the class is removed, the panel returns to its CSS-defined default.\n */\n#text-chat-panel.ttp-panel-expanded {\n transition: height 0.25s ease;\n}\n\n/*\n * Layout overrides ONLY when ecommerce/hotels overlay is active.\n * Scoped under .ttp-products-visible / .ttp-rooms-visible so the\n * normal voice CSS takes effect when products/rooms are hidden.\n *\n * voiceActiveState is a flex column: [voice-controls] [ecommerce] [conversation].\n * Ecommerce area fills the middle and scrolls; conversation panel stays at bottom.\n */\n#voiceActiveState.ttp-products-visible,\n#voiceActiveState.ttp-rooms-visible {\n flex: 1 1 0 !important;\n min-height: 0 !important;\n overflow: hidden !important;\n}\n\n.ttp-products-visible > .desktop-voice-section,\n.ttp-rooms-visible > .desktop-voice-section {\n flex-shrink: 1 !important;\n}\n\n/* Conversation + input always stick to bottom; ecommerce area above grows to fill. */\n.ttp-products-visible > .desktop-conversation-panel,\n.ttp-rooms-visible > .desktop-conversation-panel {\n flex: 0 0 auto !important;\n margin-top: auto !important;\n display: flex !important;\n flex-direction: column !important;\n}\n\n.ttp-products-visible .desktop-conversation-panel .live-transcript-collapsed,\n.ttp-rooms-visible .desktop-conversation-panel .live-transcript-collapsed {\n flex: 0 0 auto !important;\n min-height: 40px !important;\n overflow: hidden !important;\n}\n\n.ttp-products-visible .desktop-conversation-panel .voice-text-input-area,\n.ttp-rooms-visible .desktop-conversation-panel .voice-text-input-area {\n margin-top: auto !important;\n padding: 3px 16px !important;\n}\n\n.ttp-products-visible .desktop-conversation-panel .voice-input-hint,\n.ttp-rooms-visible .desktop-conversation-panel .voice-input-hint {\n display: none !important;\n}\n\n#voiceActiveState.ttp-products-visible .desktop-voice-section {\n display: none !important;\n}\n\n#voiceActiveState.ttp-products-visible .compact-voice-section {\n display: flex !important;\n}\n\n#voiceActiveState.ttp-products-visible .desktop-conversation-panel {\n flex: 0 0 auto !important;\n}\n\n#voiceActiveState.ttp-products-visible .conversation-history {\n display: none !important;\n}\n\n#voiceActiveState.ttp-products-visible .live-transcript-collapsed {\n flex: 0 0 auto !important;\n}\n\n.voice-text-input-area {\n flex-shrink: 0 !important;\n}\n\n.ttp-products-visible .conversation-history,\n.ttp-rooms-visible .conversation-history {\n flex: 1 1 0 !important;\n min-height: 0 !important;\n overflow-y: auto !important;\n}\n\n.ttp-ecommerce-area {\n display: flex !important;\n flex-direction: column !important;\n width: 100%;\n flex: 1 1 0 !important;\n min-height: 0 !important;\n position: relative !important;\n overflow-x: hidden !important;\n overflow-y: hidden !important;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n PRODUCT PICKER\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.ttp-product-picker {\n background: rgba(255,255,255,0.04);\n border-radius: 14px;\n border: 1px solid rgba(255,255,255,0.08);\n box-shadow: none;\n margin: 8px 10px;\n overflow-x: hidden !important;\n overflow-y: auto !important;\n animation: ttpSlideUp 0.25s ease;\n display: flex !important;\n flex-direction: column;\n flex: 1;\n min-height: 0 !important;\n}\n\n@keyframes ttpSlideUp {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n.ttp-picker-header {\n display: flex !important;\n justify-content: space-between;\n align-items: center;\n padding: 10px 14px;\n border-bottom: 1px solid rgba(255,255,255,0.06);\n flex-shrink: 0;\n}\n\n.ttp-picker-title {\n font-size: 12px;\n font-weight: 600;\n color: rgba(255,255,255,0.5);\n}\n\n.ttp-picker-close {\n background: none;\n border: none;\n cursor: pointer;\n color: rgba(255,255,255,0.4);\n font-size: 14px;\n padding: 2px 6px;\n border-radius: 4px;\n}\n.ttp-picker-close:hover {\n background: rgba(255,255,255,0.08);\n color: rgba(255,255,255,0.7);\n}\n\n/* Horizontal card layout */\n.ttp-products-horizontal {\n display: flex !important;\n align-items: flex-start;\n flex-shrink: 0;\n gap: 10px;\n padding: 12px;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n scrollbar-width: thin;\n}\n.ttp-products-horizontal::-webkit-scrollbar { height: 4px; }\n.ttp-products-horizontal::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.15); border-radius: 2px; }\n.ttp-products-horizontal::-webkit-scrollbar-track { background: transparent; }\n\n/* Vertical list layout */\n.ttp-products-vertical {\n padding: 8px 12px;\n overflow-y: auto !important;\n display: flex !important;\n flex-direction: column;\n gap: 6px;\n flex: 1;\n min-height: 0 !important;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n PRODUCT CARD (horizontal)\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.ttp-product-card {\n width: 170px !important;\n flex-shrink: 0 !important;\n background: rgba(255,255,255,0.04);\n border-radius: 12px;\n border: 1px solid rgba(255,255,255,0.08);\n overflow: hidden;\n transition: border-color 0.15s;\n}\n.ttp-product-card:hover { border-color: rgba(109,86,245,0.5); }\n\n.ttp-product-image {\n height: 110px !important;\n background: rgba(0,0,0,0.2);\n display: flex !important;\n align-items: center;\n justify-content: center;\n position: relative;\n overflow: hidden;\n}\n.ttp-product-image img {\n width: 100% !important;\n height: 100% !important;\n max-width: none !important;\n object-fit: contain !important;\n display: block !important;\n}\n\n.ttp-stock-badge {\n position: absolute;\n top: 6px; right: 6px;\n background: rgba(239,68,68,0.15);\n color: #ef4444;\n font-size: 9px;\n font-weight: 600;\n padding: 2px 6px;\n border-radius: 4px;\n}\n\n.ttp-product-details {\n padding: 10px 12px 14px;\n}\n\n.ttp-product-name {\n font-size: 13px;\n font-weight: 600;\n color: #e8e4f0;\n line-height: 1.3;\n min-height: 30px;\n}\n\n.ttp-product-page-link {\n display: inline-flex;\n align-items: center;\n margin-left: 5px;\n font-size: 10px;\n color: #a78bfa;\n text-decoration: none;\n opacity: 0.7;\n vertical-align: middle;\n}\n.ttp-product-page-link:hover { opacity: 1; text-decoration: underline; }\n\n.ttp-product-meta {\n font-size: 10px;\n color: rgba(255,255,255,0.35);\n margin-top: 2px;\n}\n\n.ttp-product-footer {\n display: flex !important;\n align-items: center;\n justify-content: space-between;\n margin-top: 6px;\n}\n\n.ttp-product-price {\n font-size: 14px;\n font-weight: 700;\n color: #e8e4f0;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n PRODUCT COMPACT (list view)\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.ttp-product-compact {\n display: flex !important;\n align-items: center;\n gap: 10px;\n padding: 8px 10px;\n background: rgba(255,255,255,0.04);\n border-radius: 10px;\n border: 1px solid rgba(255,255,255,0.08);\n transition: border-color 0.15s;\n}\n.ttp-product-compact:hover { border-color: rgba(109,86,245,0.5); }\n\n.ttp-product-image-sm {\n width: 72px !important; height: 72px !important;\n border-radius: 10px;\n background: rgba(0,0,0,0.2);\n display: flex !important;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n overflow: hidden;\n}\n.ttp-product-image-sm img {\n width: 100% !important;\n height: 100% !important;\n max-width: none !important;\n object-fit: contain !important;\n display: block !important;\n}\n\n.ttp-product-compact .ttp-product-info {\n flex: 1;\n min-width: 0;\n}\n.ttp-product-compact .ttp-product-name {\n min-height: auto;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n color: #e8e4f0;\n}\n.ttp-product-compact .ttp-product-action {\n text-align: right;\n flex-shrink: 0 !important;\n}\n.ttp-product-compact .ttp-product-price {\n font-size: 13px;\n}\n\n.ttp-out-of-stock {\n font-size: 10px;\n color: #f87171;\n font-weight: 500;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n ADD BUTTON\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.ttp-add-btn {\n padding: 6px 14px;\n border-radius: 8px;\n border: 1.5px solid rgba(109,86,245,0.6);\n font-size: 12px;\n font-weight: 600;\n cursor: pointer;\n background: transparent;\n color: #a78bfa;\n transition: all 0.15s;\n}\n.ttp-add-btn:hover { background: rgba(109,86,245,0.15); border-color: #a78bfa; }\n.ttp-add-btn:active { transform: scale(0.95); }\n.ttp-add-btn.ttp-added { background: rgba(109,86,245,0.6) !important; border-color: #7c3aed !important; color: #fff !important; }\n.ttp-add-btn:disabled { cursor: default; opacity: 0.5; }\n.ttp-add-btn.ttp-update-btn { background: rgba(245,158,11,0.15); border-color: rgba(245,158,11,0.5); color: #fbbf24; }\n.ttp-add-btn.ttp-update-btn:hover { background: rgba(245,158,11,0.25); border-color: #fbbf24; }\n\n.ttp-products-animating .ttp-add-btn {\n opacity: 0.4;\n pointer-events: none;\n cursor: not-allowed;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n CART TOAST\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n/* Toast replaces waveform in desktop voice section */\n.ttp-cart-toast {\n display: flex !important;\n align-items: center;\n gap: 10px;\n padding: 12px 16px;\n margin: 8px 12px;\n background: rgba(16,185,129,0.1);\n border-radius: 12px;\n border: 1px solid rgba(16,185,129,0.2);\n animation: ttpToastIn 0.25s ease;\n flex-shrink: 0;\n box-shadow: none;\n}\n\n@keyframes ttpToastIn {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n}\n.ttp-cart-toast.ttp-toast-hiding {\n animation: ttpSlideDown 0.3s ease forwards;\n}\n\n@keyframes ttpSlideDown {\n to { opacity: 0; transform: translateY(-8px); height: 0; padding: 0; margin: 0; overflow: hidden; }\n}\n\n.ttp-toast-icon {\n width: 24px !important; height: 24px !important;\n border-radius: 50%;\n background: #10b981;\n color: #fff;\n display: flex !important;\n align-items: center;\n justify-content: center;\n font-size: 12px;\n font-weight: 700;\n flex-shrink: 0;\n}\n\n.ttp-toast-content { flex: 1; min-width: 0; }\n.ttp-toast-title { font-size: 12px; font-weight: 600; color: #34d399; }\n.ttp-toast-product {\n font-size: 11px;\n color: rgba(52,211,153,0.7);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.ttp-toast-undo {\n background: none;\n border: 1px solid rgba(52,211,153,0.3);\n border-radius: 6px;\n padding: 3px 8px;\n font-size: 10px;\n color: #34d399;\n cursor: pointer;\n font-weight: 600;\n flex-shrink: 0;\n}\n.ttp-toast-undo:hover { background: rgba(52,211,153,0.1); }\n\n/* Fly-to-cart desktop target (when cart summary is hidden) */\n.ttp-desktop-fly-target {\n position: absolute;\n bottom: 12px;\n right: 80px;\n width: 24px;\n height: 24px;\n pointer-events: none;\n}\n\n/* Fly-to-cart ripple effect */\n.ttp-fly-ripple {\n position: absolute;\n left: 10px;\n top: 50%;\n width: 30px;\n height: 30px;\n margin-top: -15px;\n border-radius: 50%;\n border: 2px solid rgba(74,222,128,0.7);\n animation: ttp-ripple-expand 0.6s ease-out forwards;\n pointer-events: none;\n}\n\n@keyframes ttp-ripple-expand {\n 0% { transform: scale(0.5); opacity: 0.8; }\n 100% { transform: scale(3); opacity: 0; }\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n QUANTITY/WEIGHT STEPPER\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.ttp-stepper {\n display: flex !important;\n align-items: center;\n gap: 2px;\n margin: 4px 0;\n}\n\n.ttp-stepper-btn {\n width: 24px !important;\n height: 24px !important;\n border-radius: 6px;\n border: 1px solid rgba(255,255,255,0.15);\n background: rgba(255,255,255,0.06);\n color: rgba(255,255,255,0.6);\n font-size: 14px;\n font-weight: 600;\n cursor: pointer;\n display: flex !important;\n align-items: center;\n justify-content: center;\n padding: 0;\n line-height: 1;\n transition: all 0.15s;\n flex-shrink: 0;\n}\n\n.ttp-stepper-btn:hover:not(:disabled) {\n background: rgba(255,255,255,0.12);\n border-color: rgba(255,255,255,0.25);\n}\n\n.ttp-stepper-btn:active:not(:disabled) {\n transform: scale(0.9);\n}\n\n.ttp-stepper-btn:disabled,\n.ttp-stepper-disabled {\n opacity: 0.35;\n cursor: default;\n}\n\n.ttp-stepper-value {\n font-size: 12px;\n font-weight: 600;\n color: #e8e4f0;\n min-width: 36px;\n text-align: center;\n user-select: none;\n}\n\n.ttp-product-compact .ttp-stepper {\n justify-content: flex-end;\n}\n\n.ttp-product-compact .ttp-product-action {\n display: flex !important;\n flex-direction: column;\n align-items: flex-end;\n gap: 2px;\n}\n\n.ttp-product-card .ttp-stepper {\n justify-content: center;\n margin: 4px 0;\n}\n\n.ttp-product-card .ttp-product-footer {\n display: flex !important;\n flex-direction: column;\n align-items: center;\n gap: 4px;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n VARIATION DESKTOP PICKER\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.picker-relative {\n position: relative !important;\n flex: 1 1 0 !important;\n min-height: 0 !important;\n display: flex !important;\n flex-direction: column !important;\n overflow: hidden;\n}\n\n/* When variant picker is shown, center it horizontally and vertically */\n.picker-relative.ttp-var-picker-active {\n justify-content: center !important;\n align-items: center !important;\n}\n\n.ttp-var-picker {\n background: rgba(255,255,255,0.04);\n border-radius: 14px;\n border: 1px solid rgba(255,255,255,0.08);\n display: flex !important;\n flex-direction: column;\n animation: ttpVarFadeIn 0.2s ease;\n overflow: hidden;\n width: 100%;\n flex-shrink: 0 !important;\n}\n\n@keyframes ttpVarFadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n.ttp-var-header {\n display: flex !important;\n align-items: center;\n gap: 10px;\n padding: 10px 14px;\n border-bottom: 1px solid rgba(255,255,255,0.06);\n flex-shrink: 0;\n}\n\n.ttp-var-back-btn {\n background: none;\n border: none;\n cursor: pointer;\n color: rgba(255,255,255,0.5);\n font-size: 16px;\n padding: 0;\n display: flex;\n align-items: center;\n line-height: 1;\n font-family: inherit;\n}\n.ttp-var-back-btn:hover { color: #e8e4f0; }\n\n.ttp-var-title {\n font-size: 13px;\n font-weight: 700;\n color: #e8e4f0;\n flex: 1;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.ttp-var-page-link {\n font-size: 11px;\n color: #a78bfa;\n text-decoration: none;\n font-weight: 500;\n white-space: nowrap;\n flex-shrink: 0;\n}\n.ttp-var-page-link:hover { text-decoration: underline; }\n\n.ttp-var-body {\n flex: 1;\n overflow-y: auto;\n padding: 12px 14px;\n display: flex !important;\n flex-direction: column;\n gap: 14px;\n}\n\n.ttp-var-preview {\n display: flex !important;\n gap: 12px;\n align-items: flex-start;\n}\n\n.ttp-var-preview-img {\n width: 72px !important;\n height: 72px !important;\n border-radius: 12px;\n background: rgba(0,0,0,0.2);\n border: 1px solid rgba(255,255,255,0.08);\n display: flex !important;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n overflow: hidden;\n transition: all 0.2s ease;\n}\n\n.ttp-var-preview-img img {\n width: 100% !important;\n height: 100% !important;\n max-width: none !important;\n object-fit: contain !important;\n display: block !important;\n padding: 6px;\n}\n\n.ttp-var-preview-info { flex: 1; min-width: 0; }\n\n.ttp-var-preview-brand {\n font-size: 10px;\n color: rgba(255,255,255,0.4);\n font-weight: 500;\n text-transform: uppercase;\n letter-spacing: 0.3px;\n}\n\n.ttp-var-preview-name {\n font-size: 14px;\n font-weight: 700;\n color: #e8e4f0;\n margin: 2px 0 4px;\n line-height: 1.2;\n}\n\n.ttp-var-preview-price {\n font-size: 18px;\n font-weight: 700;\n color: #7c3aed;\n}\n\n.ttp-var-preview-selected {\n font-size: 11px;\n color: rgba(255,255,255,0.4);\n margin-top: 3px;\n}\n\n.ttp-var-section { display: flex !important; flex-direction: column; gap: 8px; }\n\n.ttp-var-section-label {\n font-size: 11px;\n font-weight: 700;\n color: rgba(255,255,255,0.5);\n text-transform: uppercase;\n letter-spacing: 0.4px;\n}\n\n.ttp-var-swatches { display: flex !important; flex-wrap: wrap; gap: 8px; }\n\n.ttp-var-swatch {\n display: block !important;\n width: 28px !important;\n height: 28px !important;\n border-radius: 50%;\n cursor: pointer;\n border: 3px solid transparent;\n transition: all 0.15s;\n position: relative;\n flex-shrink: 0;\n}\n.ttp-var-swatch:hover { transform: scale(1.1); }\n.ttp-var-swatch.selected {\n border-color: #7c3aed;\n box-shadow: 0 0 0 2px #fff, 0 0 0 4px #7c3aed;\n}\n.ttp-var-swatch.oos {\n opacity: 0.35;\n cursor: not-allowed;\n}\n\n.ttp-var-pills { display: flex !important; flex-wrap: wrap; gap: 6px; }\n\n.ttp-var-pill {\n padding: 5px 12px;\n border-radius: 8px;\n border: 1.5px solid rgba(255,255,255,0.15);\n background: rgba(255,255,255,0.04);\n font-size: 12px;\n font-weight: 600;\n color: rgba(255,255,255,0.6);\n cursor: pointer;\n transition: all 0.15s;\n font-family: inherit;\n}\n.ttp-var-pill:hover:not(:disabled) { border-color: #7c3aed; color: #a78bfa; }\n.ttp-var-pill.selected { border-color: #7c3aed; background: #7c3aed; color: #fff; }\n.ttp-var-pill.oos { opacity: 0.35; cursor: not-allowed; text-decoration: line-through; }\n\n.ttp-var-footer {\n padding: 10px 14px 12px;\n border-top: 1px solid rgba(255,255,255,0.06);\n flex-shrink: 0;\n}\n\n.ttp-var-add-btn {\n width: 100%;\n padding: 10px;\n background: linear-gradient(135deg, #7c3aed, #6d28d9);\n color: #fff;\n border: none;\n border-radius: 10px;\n font-size: 13px;\n font-weight: 600;\n cursor: pointer;\n font-family: inherit;\n box-shadow: 0 3px 10px rgba(124,58,237,0.3);\n transition: all 0.15s;\n}\n.ttp-var-add-btn:hover:not(:disabled) { transform: scale(0.99); }\n.ttp-var-add-btn:disabled {\n background: rgba(255,255,255,0.1);\n box-shadow: none;\n cursor: not-allowed;\n color: rgba(255,255,255,0.3);\n}\n\n.ttp-var-spinner {\n width: 24px;\n height: 24px;\n border: 3px solid rgba(255,255,255,0.1);\n border-top-color: #7c3aed;\n border-radius: 50%;\n animation: ttpVarSpin 0.7s linear infinite;\n}\n@keyframes ttpVarSpin {\n to { transform: rotate(360deg); }\n}\n\n/* Amber \"Choose\" button for variant products */\n.ttp-add-btn.ttp-choose-btn {\n background: linear-gradient(135deg, #f59e0b, #d97706);\n box-shadow: 0 2px 8px rgba(245,158,11,0.3);\n color: #fff;\n}\n\n ";
17377
+ "use strict";
17378
+ __webpack_require__.r(__webpack_exports__);
17379
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
17380
+ /* harmony export */ generateEcommerceCSS: () => (/* binding */ generateEcommerceCSS)
17381
+ /* harmony export */ });
17382
+ /**
17383
+ * ecommerceStyles - CSS for e-commerce components.
17384
+ * Returns a CSS string to be merged into the shadow root <style> tag.
17385
+ * Follows the same pattern as Styles.generateCSS().
17386
+ */
17387
+ function generateEcommerceCSS() {
17388
+ return "\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n ECOMMERCE AREA\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n\n/*\n * When products are visible, expand the panel dynamically.\n * Actual height is set via inline style by EcommerceManager._expandPanelToFit().\n * When the class is removed, the panel returns to its CSS-defined default.\n */\n#text-chat-panel.ttp-panel-expanded {\n transition: height 0.25s ease;\n}\n\n/*\n * Layout overrides ONLY when ecommerce/hotels overlay is active.\n * Scoped under .ttp-products-visible / .ttp-rooms-visible so the\n * normal voice CSS takes effect when products/rooms are hidden.\n *\n * voiceActiveState is a flex column: [voice-controls] [ecommerce] [conversation].\n * Ecommerce area fills the middle and scrolls; conversation panel stays at bottom.\n */\n#voiceActiveState.ttp-products-visible,\n#voiceActiveState.ttp-rooms-visible {\n flex: 1 1 0 !important;\n min-height: 0 !important;\n overflow: hidden !important;\n}\n\n.ttp-products-visible > .desktop-voice-section,\n.ttp-rooms-visible > .desktop-voice-section {\n flex-shrink: 1 !important;\n}\n\n/* Conversation + input always stick to bottom; ecommerce area above grows to fill. */\n.ttp-products-visible > .desktop-conversation-panel,\n.ttp-rooms-visible > .desktop-conversation-panel {\n flex: 0 0 auto !important;\n margin-top: auto !important;\n display: flex !important;\n flex-direction: column !important;\n}\n\n.ttp-products-visible .desktop-conversation-panel .live-transcript-collapsed,\n.ttp-rooms-visible .desktop-conversation-panel .live-transcript-collapsed {\n flex: 0 0 auto !important;\n min-height: 40px !important;\n overflow: hidden !important;\n}\n\n.ttp-products-visible .desktop-conversation-panel .voice-text-input-area,\n.ttp-rooms-visible .desktop-conversation-panel .voice-text-input-area {\n margin-top: auto !important;\n padding: 3px 16px !important;\n}\n\n.ttp-products-visible .desktop-conversation-panel .voice-input-hint,\n.ttp-rooms-visible .desktop-conversation-panel .voice-input-hint {\n display: none !important;\n}\n\n#voiceActiveState.ttp-products-visible .desktop-voice-section {\n display: none !important;\n}\n\n#voiceActiveState.ttp-products-visible .compact-voice-section {\n display: flex !important;\n}\n\n#voiceActiveState.ttp-products-visible .desktop-conversation-panel {\n flex: 0 0 auto !important;\n}\n\n#voiceActiveState.ttp-products-visible .conversation-history {\n display: none !important;\n}\n\n#voiceActiveState.ttp-products-visible .live-transcript-collapsed {\n flex: 0 0 auto !important;\n}\n\n.voice-text-input-area {\n flex-shrink: 0 !important;\n}\n\n.ttp-products-visible .conversation-history,\n.ttp-rooms-visible .conversation-history {\n flex: 1 1 0 !important;\n min-height: 0 !important;\n overflow-y: auto !important;\n}\n\n.ttp-ecommerce-area {\n display: flex !important;\n flex-direction: column !important;\n width: 100%;\n flex: 1 1 0 !important;\n min-height: 0 !important;\n position: relative !important;\n overflow-x: hidden !important;\n overflow-y: hidden !important;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n PRODUCT PICKER\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.ttp-product-picker {\n background: rgba(255,255,255,0.04);\n border-radius: 14px;\n border: 1px solid rgba(255,255,255,0.08);\n box-shadow: none;\n margin: 8px 10px;\n overflow-x: hidden !important;\n overflow-y: auto !important;\n animation: ttpSlideUp 0.25s ease;\n display: flex !important;\n flex-direction: column;\n flex: 1;\n min-height: 0 !important;\n}\n\n@keyframes ttpSlideUp {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n.ttp-picker-header {\n display: flex !important;\n justify-content: space-between;\n align-items: center;\n padding: 10px 14px;\n border-bottom: 1px solid rgba(255,255,255,0.06);\n flex-shrink: 0;\n}\n\n.ttp-picker-title {\n font-size: 12px;\n font-weight: 600;\n color: rgba(255,255,255,0.5);\n}\n\n.ttp-picker-close {\n background: none;\n border: none;\n cursor: pointer;\n color: rgba(255,255,255,0.4);\n font-size: 14px;\n padding: 2px 6px;\n border-radius: 4px;\n}\n.ttp-picker-close:hover {\n background: rgba(255,255,255,0.08);\n color: rgba(255,255,255,0.7);\n}\n\n/* Horizontal card layout */\n.ttp-products-horizontal {\n display: flex !important;\n align-items: flex-start;\n flex-shrink: 0;\n gap: 10px;\n padding: 12px;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n scrollbar-width: thin;\n}\n.ttp-products-horizontal::-webkit-scrollbar { height: 4px; }\n.ttp-products-horizontal::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.15); border-radius: 2px; }\n.ttp-products-horizontal::-webkit-scrollbar-track { background: transparent; }\n\n/* Vertical list layout */\n.ttp-products-vertical {\n padding: 8px 12px;\n overflow-y: auto !important;\n display: flex !important;\n flex-direction: column;\n gap: 6px;\n flex: 1;\n min-height: 0 !important;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n PRODUCT CARD (horizontal)\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.ttp-product-card {\n width: 170px !important;\n flex-shrink: 0 !important;\n background: rgba(255,255,255,0.04);\n border-radius: 12px;\n border: 1px solid rgba(255,255,255,0.08);\n overflow: hidden;\n transition: border-color 0.15s;\n}\n.ttp-product-card:hover { border-color: rgba(109,86,245,0.5); }\n\n.ttp-product-image {\n height: 110px !important;\n background: rgba(0,0,0,0.2);\n display: flex !important;\n align-items: center;\n justify-content: center;\n position: relative;\n overflow: hidden;\n}\n.ttp-product-image img {\n width: 100% !important;\n height: 100% !important;\n max-width: none !important;\n object-fit: contain !important;\n display: block !important;\n}\n\n.ttp-stock-badge {\n position: absolute;\n top: 6px; right: 6px;\n background: rgba(239,68,68,0.15);\n color: #ef4444;\n font-size: 9px;\n font-weight: 600;\n padding: 2px 6px;\n border-radius: 4px;\n}\n\n.ttp-product-details {\n padding: 10px 12px 14px;\n}\n\n.ttp-product-name {\n font-size: 13px;\n font-weight: 600;\n color: #e8e4f0;\n line-height: 1.3;\n min-height: 30px;\n}\n\n.ttp-product-page-link {\n display: inline-flex;\n align-items: center;\n margin-left: 5px;\n font-size: 10px;\n color: #a78bfa;\n text-decoration: none;\n opacity: 0.7;\n vertical-align: middle;\n}\n.ttp-product-page-link:hover { opacity: 1; text-decoration: underline; }\n\n.ttp-product-meta {\n font-size: 10px;\n color: rgba(255,255,255,0.35);\n margin-top: 2px;\n}\n\n.ttp-product-footer {\n display: flex !important;\n align-items: center;\n justify-content: space-between;\n margin-top: 6px;\n}\n\n.ttp-product-price {\n font-size: 14px;\n font-weight: 700;\n color: #e8e4f0;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n PRODUCT COMPACT (list view)\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.ttp-product-compact {\n display: flex !important;\n align-items: center;\n gap: 10px;\n padding: 8px 10px;\n background: rgba(255,255,255,0.04);\n border-radius: 10px;\n border: 1px solid rgba(255,255,255,0.08);\n transition: border-color 0.15s;\n}\n.ttp-product-compact:hover { border-color: rgba(109,86,245,0.5); }\n\n.ttp-product-image-sm {\n width: 72px !important; height: 72px !important;\n border-radius: 10px;\n background: rgba(0,0,0,0.2);\n display: flex !important;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n overflow: hidden;\n}\n.ttp-product-image-sm img {\n width: 100% !important;\n height: 100% !important;\n max-width: none !important;\n object-fit: contain !important;\n display: block !important;\n}\n\n.ttp-product-compact .ttp-product-info {\n flex: 1;\n min-width: 0;\n}\n.ttp-product-compact .ttp-product-name {\n min-height: auto;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n color: #e8e4f0;\n}\n.ttp-product-compact .ttp-product-action {\n text-align: right;\n flex-shrink: 0 !important;\n}\n.ttp-product-compact .ttp-product-price {\n font-size: 13px;\n}\n\n.ttp-out-of-stock {\n font-size: 10px;\n color: #f87171;\n font-weight: 500;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n ADD BUTTON\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.ttp-add-btn {\n padding: 6px 14px;\n border-radius: 8px;\n border: 1.5px solid rgba(109,86,245,0.6);\n font-size: 12px;\n font-weight: 600;\n cursor: pointer;\n background: transparent;\n color: #a78bfa;\n transition: all 0.15s;\n}\n.ttp-add-btn:hover { background: rgba(109,86,245,0.15); border-color: #a78bfa; }\n.ttp-add-btn:active { transform: scale(0.95); }\n.ttp-add-btn.ttp-added { background: rgba(109,86,245,0.6) !important; border-color: #7c3aed !important; color: #fff !important; }\n.ttp-add-btn:disabled { cursor: default; opacity: 0.5; }\n.ttp-add-btn.ttp-update-btn { background: rgba(245,158,11,0.15); border-color: rgba(245,158,11,0.5); color: #fbbf24; }\n.ttp-add-btn.ttp-update-btn:hover { background: rgba(245,158,11,0.25); border-color: #fbbf24; }\n\n.ttp-products-animating .ttp-add-btn {\n opacity: 0.4;\n pointer-events: none;\n cursor: not-allowed;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n CART TOAST\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n/* Toast replaces waveform in desktop voice section */\n.ttp-cart-toast {\n display: flex !important;\n align-items: center;\n gap: 10px;\n padding: 12px 16px;\n margin: 8px 12px;\n background: rgba(16,185,129,0.1);\n border-radius: 12px;\n border: 1px solid rgba(16,185,129,0.2);\n animation: ttpToastIn 0.25s ease;\n flex-shrink: 0;\n box-shadow: none;\n}\n\n@keyframes ttpToastIn {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n}\n.ttp-cart-toast.ttp-toast-hiding {\n animation: ttpSlideDown 0.3s ease forwards;\n}\n\n@keyframes ttpSlideDown {\n to { opacity: 0; transform: translateY(-8px); height: 0; padding: 0; margin: 0; overflow: hidden; }\n}\n\n.ttp-toast-icon {\n width: 24px !important; height: 24px !important;\n border-radius: 50%;\n background: #10b981;\n color: #fff;\n display: flex !important;\n align-items: center;\n justify-content: center;\n font-size: 12px;\n font-weight: 700;\n flex-shrink: 0;\n}\n\n.ttp-toast-content { flex: 1; min-width: 0; }\n.ttp-toast-title { font-size: 12px; font-weight: 600; color: #34d399; }\n.ttp-toast-product {\n font-size: 11px;\n color: rgba(52,211,153,0.7);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.ttp-toast-undo {\n background: none;\n border: 1px solid rgba(52,211,153,0.3);\n border-radius: 6px;\n padding: 3px 8px;\n font-size: 10px;\n color: #34d399;\n cursor: pointer;\n font-weight: 600;\n flex-shrink: 0;\n}\n.ttp-toast-undo:hover { background: rgba(52,211,153,0.1); }\n\n/* Fly-to-cart desktop target (when cart summary is hidden) */\n.ttp-desktop-fly-target {\n position: absolute;\n bottom: 12px;\n right: 80px;\n width: 24px;\n height: 24px;\n pointer-events: none;\n}\n\n/* Fly-to-cart ripple effect */\n.ttp-fly-ripple {\n position: absolute;\n left: 10px;\n top: 50%;\n width: 30px;\n height: 30px;\n margin-top: -15px;\n border-radius: 50%;\n border: 2px solid rgba(74,222,128,0.7);\n animation: ttp-ripple-expand 0.6s ease-out forwards;\n pointer-events: none;\n}\n\n@keyframes ttp-ripple-expand {\n 0% { transform: scale(0.5); opacity: 0.8; }\n 100% { transform: scale(3); opacity: 0; }\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n QUANTITY/WEIGHT STEPPER\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.ttp-stepper {\n display: flex !important;\n align-items: center;\n gap: 2px;\n margin: 4px 0;\n}\n\n.ttp-stepper-btn {\n width: 24px !important;\n height: 24px !important;\n border-radius: 6px;\n border: 1px solid rgba(255,255,255,0.15);\n background: rgba(255,255,255,0.06);\n color: rgba(255,255,255,0.6);\n font-size: 14px;\n font-weight: 600;\n cursor: pointer;\n display: flex !important;\n align-items: center;\n justify-content: center;\n padding: 0;\n line-height: 1;\n transition: all 0.15s;\n flex-shrink: 0;\n}\n\n.ttp-stepper-btn:hover:not(:disabled) {\n background: rgba(255,255,255,0.12);\n border-color: rgba(255,255,255,0.25);\n}\n\n.ttp-stepper-btn:active:not(:disabled) {\n transform: scale(0.9);\n}\n\n.ttp-stepper-btn:disabled,\n.ttp-stepper-disabled {\n opacity: 0.35;\n cursor: default;\n}\n\n.ttp-stepper-value {\n font-size: 12px;\n font-weight: 600;\n color: #e8e4f0;\n min-width: 36px;\n text-align: center;\n user-select: none;\n}\n\n.ttp-product-compact .ttp-stepper {\n justify-content: flex-end;\n}\n\n.ttp-product-compact .ttp-product-action {\n display: flex !important;\n flex-direction: column;\n align-items: flex-end;\n gap: 2px;\n}\n\n.ttp-product-card .ttp-stepper {\n justify-content: center;\n margin: 4px 0;\n}\n\n.ttp-product-card .ttp-product-footer {\n display: flex !important;\n flex-direction: column;\n align-items: center;\n gap: 4px;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n VARIATION DESKTOP PICKER\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.picker-relative {\n position: relative !important;\n flex: 1 1 0 !important;\n min-height: 0 !important;\n display: flex !important;\n flex-direction: column !important;\n overflow: hidden;\n}\n\n/* When variant picker is shown, center it horizontally and vertically */\n.picker-relative.ttp-var-picker-active {\n justify-content: center !important;\n align-items: center !important;\n}\n\n.ttp-var-picker {\n background: rgba(255,255,255,0.04);\n border-radius: 14px;\n border: 1px solid rgba(255,255,255,0.08);\n display: flex !important;\n flex-direction: column;\n animation: ttpVarFadeIn 0.2s ease;\n overflow: hidden;\n width: 100%;\n flex-shrink: 0 !important;\n}\n\n@keyframes ttpVarFadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n.ttp-var-header {\n display: flex !important;\n align-items: center;\n gap: 10px;\n padding: 10px 14px;\n border-bottom: 1px solid rgba(255,255,255,0.06);\n flex-shrink: 0;\n}\n\n.ttp-var-back-btn {\n background: none;\n border: none;\n cursor: pointer;\n color: rgba(255,255,255,0.5);\n font-size: 16px;\n padding: 0;\n display: flex;\n align-items: center;\n line-height: 1;\n font-family: inherit;\n}\n.ttp-var-back-btn:hover { color: #e8e4f0; }\n\n.ttp-var-title {\n font-size: 13px;\n font-weight: 700;\n color: #e8e4f0;\n flex: 1;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.ttp-var-page-link {\n font-size: 11px;\n color: #a78bfa;\n text-decoration: none;\n font-weight: 500;\n white-space: nowrap;\n flex-shrink: 0;\n}\n.ttp-var-page-link:hover { text-decoration: underline; }\n\n.ttp-var-body {\n flex: 1;\n overflow-y: auto;\n padding: 12px 14px;\n display: flex !important;\n flex-direction: column;\n gap: 14px;\n}\n\n.ttp-var-preview {\n display: flex !important;\n gap: 12px;\n align-items: flex-start;\n}\n\n.ttp-var-preview-img {\n width: 72px !important;\n height: 72px !important;\n border-radius: 12px;\n background: rgba(0,0,0,0.2);\n border: 1px solid rgba(255,255,255,0.08);\n display: flex !important;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n overflow: hidden;\n transition: all 0.2s ease;\n}\n\n.ttp-var-preview-img img {\n width: 100% !important;\n height: 100% !important;\n max-width: none !important;\n object-fit: contain !important;\n display: block !important;\n padding: 6px;\n}\n\n.ttp-var-preview-info { flex: 1; min-width: 0; }\n\n.ttp-var-preview-brand {\n font-size: 10px;\n color: rgba(255,255,255,0.4);\n font-weight: 500;\n text-transform: uppercase;\n letter-spacing: 0.3px;\n}\n\n.ttp-var-preview-name {\n font-size: 14px;\n font-weight: 700;\n color: #e8e4f0;\n margin: 2px 0 4px;\n line-height: 1.2;\n}\n\n.ttp-var-preview-price {\n font-size: 18px;\n font-weight: 700;\n color: #7c3aed;\n}\n\n.ttp-var-preview-selected {\n font-size: 11px;\n color: rgba(255,255,255,0.4);\n margin-top: 3px;\n}\n\n.ttp-var-section { display: flex !important; flex-direction: column; gap: 8px; }\n\n.ttp-var-section-label {\n font-size: 11px;\n font-weight: 700;\n color: rgba(255,255,255,0.5);\n text-transform: uppercase;\n letter-spacing: 0.4px;\n}\n\n.ttp-var-swatches { display: flex !important; flex-wrap: wrap; gap: 8px; }\n\n.ttp-var-swatch {\n display: block !important;\n width: 28px !important;\n height: 28px !important;\n border-radius: 50%;\n cursor: pointer;\n border: 3px solid transparent;\n transition: all 0.15s;\n position: relative;\n flex-shrink: 0;\n}\n.ttp-var-swatch:hover { transform: scale(1.1); }\n.ttp-var-swatch.selected {\n border-color: #7c3aed;\n box-shadow: 0 0 0 2px #fff, 0 0 0 4px #7c3aed;\n}\n.ttp-var-swatch.oos {\n opacity: 0.35;\n cursor: not-allowed;\n}\n\n.ttp-var-pills { display: flex !important; flex-wrap: wrap; gap: 6px; }\n\n.ttp-var-pill {\n padding: 5px 12px;\n border-radius: 8px;\n border: 1.5px solid rgba(255,255,255,0.15);\n background: rgba(255,255,255,0.04);\n font-size: 12px;\n font-weight: 600;\n color: rgba(255,255,255,0.6);\n cursor: pointer;\n transition: all 0.15s;\n font-family: inherit;\n}\n.ttp-var-pill:hover:not(:disabled) { border-color: #7c3aed; color: #a78bfa; }\n.ttp-var-pill.selected { border-color: #7c3aed; background: #7c3aed; color: #fff; }\n.ttp-var-pill.oos { opacity: 0.35; cursor: not-allowed; text-decoration: line-through; }\n\n.ttp-var-footer {\n padding: 10px 14px 12px;\n border-top: 1px solid rgba(255,255,255,0.06);\n flex-shrink: 0;\n}\n\n.ttp-var-add-btn {\n width: 100%;\n padding: 10px;\n background: linear-gradient(135deg, #7c3aed, #6d28d9);\n color: #fff;\n border: none;\n border-radius: 10px;\n font-size: 13px;\n font-weight: 600;\n cursor: pointer;\n font-family: inherit;\n box-shadow: 0 3px 10px rgba(124,58,237,0.3);\n transition: all 0.15s;\n}\n.ttp-var-add-btn:hover:not(:disabled) { transform: scale(0.99); }\n.ttp-var-add-btn:disabled {\n background: rgba(255,255,255,0.1);\n box-shadow: none;\n cursor: not-allowed;\n color: rgba(255,255,255,0.3);\n}\n\n.ttp-var-spinner {\n width: 24px;\n height: 24px;\n border: 3px solid rgba(255,255,255,0.1);\n border-top-color: #7c3aed;\n border-radius: 50%;\n animation: ttpVarSpin 0.7s linear infinite;\n}\n@keyframes ttpVarSpin {\n to { transform: rotate(360deg); }\n}\n\n/* Amber \"Choose\" button for variant products */\n.ttp-add-btn.ttp-choose-btn {\n background: linear-gradient(135deg, #f59e0b, #d97706);\n box-shadow: 0 2px 8px rgba(245,158,11,0.3);\n color: #fff;\n}\n\n ";
17389
+ }
17390
+
17391
+ /***/ }),
17392
+
17393
+ /***/ "./src/ecommerce/partnerLogo.js":
17394
+ /*!**************************************!*\
17395
+ !*** ./src/ecommerce/partnerLogo.js ***!
17396
+ \**************************************/
17397
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
17398
+
17399
+ "use strict";
17400
+ __webpack_require__.r(__webpack_exports__);
17401
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
17402
+ /* harmony export */ resolvePartnerId: () => (/* binding */ resolvePartnerId),
17403
+ /* harmony export */ resolvePartnerLogoUrl: () => (/* binding */ resolvePartnerLogoUrl),
17404
+ /* harmony export */ resolveSdkBaseUrl: () => (/* binding */ resolveSdkBaseUrl)
17405
+ /* harmony export */ });
17406
+ function _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = "function" == typeof Symbol ? Symbol : {}, n = r.iterator || "@@iterator", o = r.toStringTag || "@@toStringTag"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, "_invoke", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError("Generator is already running"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = "next"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError("iterator result is not an object"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i.return) && t.call(i), c < 2 && (u = TypeError("The iterator does not provide a '" + o + "' method"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, "GeneratorFunction")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, "constructor", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = "GeneratorFunction", _regeneratorDefine2(GeneratorFunctionPrototype, o, "GeneratorFunction"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, "Generator"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, "toString", function () { return "[object Generator]"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); }
17407
+ function _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, "", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o("next", 0), o("throw", 1), o("return", 2)); }, _regeneratorDefine2(e, r, n, t); }
17408
+ function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }
17409
+ function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; }
17410
+ function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
17411
+ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
17412
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
17413
+ /**
17414
+ * Partner logos on the SDK CDN:
17415
+ * {sdkBaseUrl}/partners/{partnerId}.svg (preferred — you provide)
17416
+ * {sdkBaseUrl}/partners/{partnerId}.png (optional fallback)
17417
+ *
17418
+ * Missing partnerId or missing file → no logo rendered.
17419
+ */
17420
+
17421
+ var LOGO_EXTENSIONS = ['svg', 'png'];
17422
+ function resolveSdkBaseUrl() {
17423
+ if (typeof document !== 'undefined') {
17424
+ var scripts = document.querySelectorAll('script[src]');
17425
+ var _iterator = _createForOfIteratorHelper(scripts),
17426
+ _step;
17427
+ try {
17428
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
17429
+ var script = _step.value;
17430
+ var src = script.getAttribute('src') || '';
17431
+ if (!src.includes('agent-widget') && !src.includes('ttp-agent-sdk')) continue;
17432
+ try {
17433
+ var url = new URL(src, window.location.href);
17434
+ return url.origin + url.pathname.replace(/\/[^/]*$/, '');
17435
+ } catch (e) {/* continue */}
17436
+ }
17437
+ } catch (err) {
17438
+ _iterator.e(err);
17439
+ } finally {
17440
+ _iterator.f();
17441
+ }
17442
+ }
17443
+ if (typeof window !== 'undefined') {
17444
+ var host = window.location.hostname || '';
17445
+ if (host.includes('cdn.talktopc.com') || host.includes('talktopc.com')) {
17446
+ return "".concat(window.location.origin, "/ttp-agent-sdk");
17447
+ }
17448
+ }
17449
+ return 'https://cdn.talktopc.com/ttp-agent-sdk';
17450
+ }
17451
+
17452
+ /** partnerId from wire message, else widget flavor.partnerId. */
17453
+ function resolvePartnerId(widget, message) {
17454
+ var _ref, _ref2, _message$partnerId, _message$recipe, _message$recipe2, _widget$config$flavor, _widget$config;
17455
+ var fromMsg = (_ref = (_ref2 = (_message$partnerId = message === null || message === void 0 ? void 0 : message.partnerId) !== null && _message$partnerId !== void 0 ? _message$partnerId : message === null || message === void 0 ? void 0 : message.partner_id) !== null && _ref2 !== void 0 ? _ref2 : message === null || message === void 0 || (_message$recipe = message.recipe) === null || _message$recipe === void 0 ? void 0 : _message$recipe.partnerId) !== null && _ref !== void 0 ? _ref : message === null || message === void 0 || (_message$recipe2 = message.recipe) === null || _message$recipe2 === void 0 ? void 0 : _message$recipe2.partner_id;
17456
+ if (fromMsg && String(fromMsg).trim()) {
17457
+ return String(fromMsg).trim();
17458
+ }
17459
+ var flavor = (_widget$config$flavor = widget === null || widget === void 0 || (_widget$config = widget.config) === null || _widget$config === void 0 ? void 0 : _widget$config.flavor) !== null && _widget$config$flavor !== void 0 ? _widget$config$flavor : widget === null || widget === void 0 ? void 0 : widget._flavorObj;
17460
+ var fromFlavor = flavor === null || flavor === void 0 ? void 0 : flavor.partnerId;
17461
+ if (fromFlavor && String(fromFlavor).trim()) {
17462
+ return String(fromFlavor).trim();
17463
+ }
17464
+ return null;
17465
+ }
17466
+ function probeImage(url) {
17467
+ return new Promise(function (resolve) {
17468
+ var img = new Image();
17469
+ img.onload = function () {
17470
+ return resolve(true);
17471
+ };
17472
+ img.onerror = function () {
17473
+ return resolve(false);
17474
+ };
17475
+ img.src = url;
17476
+ });
17477
+ }
17478
+
17479
+ /** @returns {Promise<string|null>} First logo URL that loads, or null. */
17480
+ function resolvePartnerLogoUrl(_x, _x2) {
17481
+ return _resolvePartnerLogoUrl.apply(this, arguments);
17482
+ }
17483
+ function _resolvePartnerLogoUrl() {
17484
+ _resolvePartnerLogoUrl = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee(partnerId, sdkBaseUrl) {
17485
+ var id, base, safeId, _iterator2, _step2, ext, url, _t;
17486
+ return _regenerator().w(function (_context) {
17487
+ while (1) switch (_context.p = _context.n) {
17488
+ case 0:
17489
+ id = partnerId === null || partnerId === void 0 ? void 0 : partnerId.trim();
17490
+ if (id) {
17491
+ _context.n = 1;
17492
+ break;
17493
+ }
17494
+ return _context.a(2, null);
17495
+ case 1:
17496
+ base = (sdkBaseUrl || resolveSdkBaseUrl()).replace(/\/$/, '');
17497
+ safeId = encodeURIComponent(id);
17498
+ _iterator2 = _createForOfIteratorHelper(LOGO_EXTENSIONS);
17499
+ _context.p = 2;
17500
+ _iterator2.s();
17501
+ case 3:
17502
+ if ((_step2 = _iterator2.n()).done) {
17503
+ _context.n = 6;
17504
+ break;
17505
+ }
17506
+ ext = _step2.value;
17507
+ url = "".concat(base, "/partners/").concat(safeId, ".").concat(ext); // eslint-disable-next-line no-await-in-loop
17508
+ _context.n = 4;
17509
+ return probeImage(url);
17510
+ case 4:
17511
+ if (!_context.v) {
17512
+ _context.n = 5;
17513
+ break;
17514
+ }
17515
+ return _context.a(2, url);
17516
+ case 5:
17517
+ _context.n = 3;
17518
+ break;
17519
+ case 6:
17520
+ _context.n = 8;
17521
+ break;
17522
+ case 7:
17523
+ _context.p = 7;
17524
+ _t = _context.v;
17525
+ _iterator2.e(_t);
17526
+ case 8:
17527
+ _context.p = 8;
17528
+ _iterator2.f();
17529
+ return _context.f(8);
17530
+ case 9:
17531
+ return _context.a(2, null);
17532
+ }
17533
+ }, _callee, null, [[2, 7, 8, 9]]);
17534
+ }));
17535
+ return _resolvePartnerLogoUrl.apply(this, arguments);
16659
17536
  }
16660
17537
 
16661
17538
  /***/ }),
@@ -16929,6 +17806,7 @@ __webpack_require__.r(__webpack_exports__);
16929
17806
  /* harmony export */ MESSAGE_TYPES: () => (/* binding */ MESSAGE_TYPES)
16930
17807
  /* harmony export */ });
16931
17808
  /* harmony import */ var _widget_galleryHandler_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../widget/galleryHandler.js */ "./src/widget/galleryHandler.js");
17809
+ /* harmony import */ var _ecommerce_RecipeHandler_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../ecommerce/RecipeHandler.js */ "./src/ecommerce/RecipeHandler.js");
16932
17810
  function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
16933
17811
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
16934
17812
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
@@ -16972,6 +17850,7 @@ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e
16972
17850
  */
16973
17851
 
16974
17852
 
17853
+
16975
17854
  /**
16976
17855
  * The universal message types that form the contract between widget and backend.
16977
17856
  * Flavors receive these incoming types and send these outgoing types.
@@ -16983,6 +17862,8 @@ var MESSAGE_TYPES = Object.freeze({
16983
17862
  CART_UPDATED: 'cart_updated',
16984
17863
  SHOW_MEDIA: 'show_media',
16985
17864
  DISMISS_MEDIA: 'dismiss_media',
17865
+ SHOW_RECIPE: 'show_recipe',
17866
+ DISMISS_RECIPE: 'dismiss_recipe',
16986
17867
  // Outgoing (widget → backend)
16987
17868
  PRODUCT_SELECTED: 'product_selected',
16988
17869
  PRODUCT_DESELECTED: 'product_deselected'
@@ -17068,6 +17949,14 @@ var BaseFlavor = /*#__PURE__*/function () {
17068
17949
  value: function buildTools() {
17069
17950
  return [];
17070
17951
  }
17952
+ }, {
17953
+ key: "_getRecipeHandler",
17954
+ value: function _getRecipeHandler() {
17955
+ if (!this._recipeHandler) {
17956
+ this._recipeHandler = (0,_ecommerce_RecipeHandler_js__WEBPACK_IMPORTED_MODULE_1__.createRecipeHandler)(this.widget);
17957
+ }
17958
+ return this._recipeHandler;
17959
+ }
17071
17960
 
17072
17961
  // ─── Shared helpers (available to subclasses) ─────────────────
17073
17962
 
@@ -17178,13 +18067,19 @@ var BaseFlavor = /*#__PURE__*/function () {
17178
18067
  };
17179
18068
 
17180
18069
  // Universal handlers — these are the contract
17181
- var universal = _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty({}, MESSAGE_TYPES.SHOW_ITEMS, routeShowItems), "show_products", routeShowItems), MESSAGE_TYPES.CART_UPDATED, function (msg) {
18070
+ var universal = _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty({}, MESSAGE_TYPES.SHOW_ITEMS, routeShowItems), "show_products", routeShowItems), MESSAGE_TYPES.CART_UPDATED, function (msg) {
17182
18071
  _this.manager.handleCartUpdated(msg);
17183
18072
  }), MESSAGE_TYPES.SHOW_MEDIA, function (msg) {
17184
18073
  _this.hideManagerUI();
17185
18074
  _this.galleryHandler.handleShowMedia(msg);
17186
18075
  }), MESSAGE_TYPES.DISMISS_MEDIA, function () {
17187
18076
  _this.galleryHandler.dismissGallery();
18077
+ }), MESSAGE_TYPES.SHOW_RECIPE, function (msg) {
18078
+ _this._getRecipeHandler().handleShowRecipe(msg);
18079
+ }), MESSAGE_TYPES.DISMISS_RECIPE, function (msg) {
18080
+ _this._getRecipeHandler().dismissRecipe({
18081
+ animate: (msg === null || msg === void 0 ? void 0 : msg.silent) !== true && (msg === null || msg === void 0 ? void 0 : msg.animate) !== false
18082
+ });
17188
18083
  });
17189
18084
 
17190
18085
  // Extra handlers from subclass
@@ -17884,12 +18779,19 @@ var EcommerceFlavor = /*#__PURE__*/function (_BaseFlavor) {
17884
18779
  runAdapter: function runAdapter(action, args) {
17885
18780
  var _self$_partnerScriptR2;
17886
18781
  return (_self$_partnerScriptR2 = self._partnerScriptRunner) === null || _self$_partnerScriptR2 === void 0 ? void 0 : _self$_partnerScriptR2.runAdapter(action, args);
18782
+ },
18783
+ showRecipe: function showRecipe(recipe) {
18784
+ return self._getRecipeHandler().handleShowRecipe({
18785
+ recipe: recipe
18786
+ });
17887
18787
  }
17888
18788
  };
17889
18789
  }
17890
18790
  }, {
17891
18791
  key: "destroy",
17892
18792
  value: function destroy() {
18793
+ var _this$_recipeHandler;
18794
+ (_this$_recipeHandler = this._recipeHandler) === null || _this$_recipeHandler === void 0 || _this$_recipeHandler.dismissRecipe();
17893
18795
  delete window.__TTP_ECOMMERCE__;
17894
18796
  _superPropGet(EcommerceFlavor, "destroy", this, 3)([]);
17895
18797
  }
@@ -20303,8 +21205,8 @@ var VoiceSDK = _v2_VoiceSDK_js__WEBPACK_IMPORTED_MODULE_0__["default"];
20303
21205
 
20304
21206
 
20305
21207
  // Version - injected at build time from package.json via webpack DefinePlugin
20306
- var VERSION = "2.45.14";
20307
- var BUILD_TIME = "2026-06-29T06:35:24.857Z";
21208
+ var VERSION = "2.46.1";
21209
+ var BUILD_TIME = "2026-07-13T08:30:44.984Z";
20308
21210
  console.log("%c TTP Agent SDK v".concat(VERSION, " (").concat(BUILD_TIME, ") "), 'background: #4f46e5; color: white; font-size: 12px; font-weight: bold; padding: 2px 6px; border-radius: 4px;');
20309
21211
 
20310
21212
  // Named exports
@@ -27010,6 +27912,10 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
27010
27912
  websocketUrl: config.websocketUrl || 'wss://speech.talktopc.com/ws/conv',
27011
27913
  agentId: config.agentId,
27012
27914
  appId: config.appId,
27915
+ // Supervision monitor mode: listen-only to an existing call (no mic).
27916
+ // mode:'monitor' + a short-lived ticket authorize /ws/supervision.
27917
+ mode: config.mode || 'agent',
27918
+ ticket: config.ticket || null,
27013
27919
  // Input format (what we send to server)
27014
27920
 
27015
27921
  sampleRate: config.sampleRate || 16000,
@@ -27651,12 +28557,28 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
27651
28557
  /**
27652
28558
  * Build WebSocket URL with query parameters
27653
28559
  */
28560
+ /** True when this SDK instance is a listen-only supervision monitor (no mic). */
28561
+ }, {
28562
+ key: "isMonitor",
28563
+ value: function isMonitor() {
28564
+ return this.config.mode === 'monitor';
28565
+ }
27654
28566
  }, {
27655
28567
  key: "buildWebSocketUrl",
27656
28568
  value: function buildWebSocketUrl() {
27657
28569
  var baseUrl = this.config.websocketUrl;
27658
28570
  var url = new URL(baseUrl, 'wss://placeholder.com');
27659
28571
  var params = url.searchParams;
28572
+
28573
+ // Supervision monitor: authenticate by the single-use ticket only — no agentId/appId,
28574
+ // and never resume/attach a conversation of our own.
28575
+ if (this.isMonitor()) {
28576
+ if (this.config.ticket && !params.has('ticket')) {
28577
+ params.append('ticket', this.config.ticket);
28578
+ }
28579
+ var queryStringM = params.toString();
28580
+ return queryStringM ? "".concat(url.origin).concat(url.pathname, "?").concat(queryStringM) : "".concat(url.origin).concat(url.pathname);
28581
+ }
27660
28582
  if (this.config.agentId && !params.has('agentId')) {
27661
28583
  params.append('agentId', this.config.agentId);
27662
28584
  }
@@ -27738,9 +28660,12 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
27738
28660
  });
27739
28661
  }
27740
28662
 
27741
- // Send hello message
27742
-
27743
- _this5.sendHelloMessage();
28663
+ // Send hello message.
28664
+ // Supervision monitor: the server sends hello_ack unprompted (it only needs to
28665
+ // advertise outputAudioFormat), so we skip the agent hello entirely.
28666
+ if (!_this5.isMonitor()) {
28667
+ _this5.sendHelloMessage();
28668
+ }
27744
28669
  _this5.emit('connected');
27745
28670
  resolve(true);
27746
28671
  };
@@ -27952,7 +28877,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
27952
28877
 
27953
28878
  // Include SDK build time for debugging
27954
28879
  if (true) {
27955
- helloMessage.lastBuildTime = "2026-06-29T06:35:24.857Z";
28880
+ helloMessage.lastBuildTime = "2026-07-13T08:30:44.984Z";
27956
28881
  }
27957
28882
 
27958
28883
  // Page context is intentionally NOT attached to the hello message.
@@ -28254,6 +29179,8 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
28254
29179
  break;
28255
29180
  case 'show_items':
28256
29181
  case 'show_products':
29182
+ case 'show_recipe':
29183
+ case 'dismiss_recipe':
28257
29184
  case 'cart_updated':
28258
29185
  case 'add_to_store_cart':
28259
29186
  case 'add_to_site_cart':
@@ -28519,6 +29446,11 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
28519
29446
  // VoiceInterface waits for this flag before starting recording,
28520
29447
  // so config.sampleRate must be finalized first.
28521
29448
  this.helloAckReceived = true;
29449
+
29450
+ // Silently re-apply the user's persisted in-call settings (voice speed /
29451
+ // response length) now that the session is ready — the backend applies
29452
+ // them without a spoken acknowledgement (announce:false).
29453
+ this._syncCallSettingsAfterHello();
28522
29454
  if (message.outputAudioFormat) {
28523
29455
  // v2 protocol: format was negotiated
28524
29456
 
@@ -29332,6 +30264,79 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
29332
30264
  }
29333
30265
  }
29334
30266
 
30267
+ /**
30268
+ * In-call user settings (voice speed / response length).
30269
+ *
30270
+ * Sends {t:"call_settings"} to the backend: `voiceSpeedFactor` multiplies the
30271
+ * agent's configured TTS speed (1.0 = agent default, clamped server-side to
30272
+ * 0.5–2.0) and applies from the next synthesized line; `responseLength`
30273
+ * ("detailed"|"normal"|"brief"|"very_brief") is folded into the system prompt
30274
+ * from the next LLM turn. `opts.announce` (default true) lets the agent speak
30275
+ * a short fixed confirmation when idle.
30276
+ *
30277
+ * Called before hello_ack, the values are kept and flushed silently by
30278
+ * _syncCallSettingsAfterHello() once the session is ready.
30279
+ *
30280
+ * @param {{voiceSpeedFactor?: number, responseLength?: string}} settings
30281
+ * @param {{announce?: boolean}} [opts]
30282
+ * @returns {boolean} true if the message was sent now
30283
+ */
30284
+ }, {
30285
+ key: "setCallSettings",
30286
+ value: function setCallSettings() {
30287
+ var settings = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
30288
+ var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
30289
+ this._callSettings = _objectSpread(_objectSpread({}, this._callSettings || {}), settings);
30290
+ if (!this.helloAckReceived || !this.websocket || this.websocket.readyState !== WebSocket.OPEN) {
30291
+ return false; // flushed after hello_ack
30292
+ }
30293
+ var msg = {
30294
+ t: 'call_settings',
30295
+ announce: opts.announce !== false
30296
+ };
30297
+ if (typeof this._callSettings.voiceSpeedFactor === 'number') {
30298
+ msg.voiceSpeedFactor = this._callSettings.voiceSpeedFactor;
30299
+ }
30300
+ if (this._callSettings.responseLength) {
30301
+ msg.responseLength = this._callSettings.responseLength;
30302
+ }
30303
+ return this.sendMessage(msg);
30304
+ }
30305
+
30306
+ /**
30307
+ * Flush persisted call settings right after hello_ack (silent — no spoken ack).
30308
+ * Values come from an earlier setCallSettings() call or, when none, from
30309
+ * config.callSettingsProvider (the widget wires this to its localStorage).
30310
+ * Defaults (factor 1.0 + "normal") are skipped — nothing to sync.
30311
+ */
30312
+ }, {
30313
+ key: "_syncCallSettingsAfterHello",
30314
+ value: function _syncCallSettingsAfterHello() {
30315
+ try {
30316
+ var s = this._callSettings;
30317
+ if (!s && typeof this.config.callSettingsProvider === 'function') {
30318
+ s = this.config.callSettingsProvider();
30319
+ }
30320
+ if (!s) return;
30321
+ this._callSettings = _objectSpread({}, s);
30322
+ var factor = typeof s.voiceSpeedFactor === 'number' ? s.voiceSpeedFactor : 1.0;
30323
+ var length = s.responseLength || 'normal';
30324
+ if (Math.abs(factor - 1.0) < 0.001 && length === 'normal') return;
30325
+ this.sendMessage({
30326
+ t: 'call_settings',
30327
+ voiceSpeedFactor: factor,
30328
+ responseLength: length,
30329
+ announce: false
30330
+ });
30331
+ console.log('⚙️ VoiceSDK v2: Synced persisted call settings:', {
30332
+ voiceSpeedFactor: factor,
30333
+ responseLength: length
30334
+ });
30335
+ } catch (e) {
30336
+ console.warn('⚙️ VoiceSDK v2: Failed to sync call settings (ignored):', e);
30337
+ }
30338
+ }
30339
+
29335
30340
  /**
29336
30341
  * Pause the call. Stops sending/receiving audio.
29337
30342
  */
@@ -29455,64 +30460,72 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
29455
30460
  while (1) switch (_context8.p = _context8.n) {
29456
30461
  case 0:
29457
30462
  existingMediaStream = _args8.length > 0 && _args8[0] !== undefined ? _args8[0] : null;
29458
- if (this.isConnected) {
30463
+ if (!this.isMonitor()) {
29459
30464
  _context8.n = 1;
29460
30465
  break;
29461
30466
  }
30467
+ // Listen-only supervision: never open the mic / never uplink audio.
30468
+ console.warn('VoiceSDK v2: startRecording ignored in monitor (listen-only) mode');
30469
+ return _context8.a(2, false);
30470
+ case 1:
30471
+ if (this.isConnected) {
30472
+ _context8.n = 2;
30473
+ break;
30474
+ }
29462
30475
  error = new Error('Not connected to voice server');
29463
30476
  this.emit('error', error);
29464
30477
  throw error;
29465
- case 1:
30478
+ case 2:
29466
30479
  if (!this.isRecording) {
29467
- _context8.n = 2;
30480
+ _context8.n = 3;
29468
30481
  break;
29469
30482
  }
29470
30483
  console.warn('VoiceSDK v2: Already recording');
29471
30484
  return _context8.a(2, true);
29472
- case 2:
30485
+ case 3:
29473
30486
  if (!this.disclaimersPending) {
29474
- _context8.n = 3;
30487
+ _context8.n = 4;
29475
30488
  break;
29476
30489
  }
29477
30490
  _error4 = new Error('Server disclaimers must be accepted before recording. Call sendDisclaimerAck(true) first.');
29478
30491
  _error4.code = 'DISCLAIMER_PENDING';
29479
30492
  this.emit('error', _error4);
29480
30493
  throw _error4;
29481
- case 3:
30494
+ case 4:
29482
30495
  console.log('🎤 VoiceSDK v2: Starting recording...');
29483
- _context8.p = 4;
30496
+ _context8.p = 5;
29484
30497
  // CRITICAL for mobile browsers: Request microphone permission FIRST
29485
30498
  // iOS: Pass existingMediaStream to avoid second getUserMedia (can fail on iOS)
29486
30499
  console.log('🎤 VoiceSDK v2: Requesting microphone permission (mobile-friendly)...');
29487
- _context8.n = 5;
30500
+ _context8.n = 6;
29488
30501
  return this.audioRecorder.start(existingMediaStream ? {
29489
30502
  existingMediaStream: existingMediaStream
29490
30503
  } : {});
29491
- case 5:
30504
+ case 6:
29492
30505
  if (!(!this.isConnected || !this.websocket || this.websocket.readyState !== WebSocket.OPEN)) {
29493
- _context8.n = 10;
30506
+ _context8.n = 11;
29494
30507
  break;
29495
30508
  }
29496
- _context8.p = 6;
29497
- _context8.n = 7;
30509
+ _context8.p = 7;
30510
+ _context8.n = 8;
29498
30511
  return this.audioRecorder.stop();
29499
- case 7:
29500
- _context8.n = 9;
29501
- break;
29502
30512
  case 8:
29503
- _context8.p = 8;
29504
- _t6 = _context8.v;
30513
+ _context8.n = 10;
30514
+ break;
29505
30515
  case 9:
30516
+ _context8.p = 9;
30517
+ _t6 = _context8.v;
30518
+ case 10:
29506
30519
  _error5 = new Error('Connection lost - server may have rejected the call');
29507
30520
  _error5.name = 'ServerRejected';
29508
30521
  _error5.isServerRejection = true;
29509
30522
  // Don't emit error event - this is expected when server rejects
29510
30523
  // The error handler in VoiceInterface will handle it appropriately
29511
30524
  throw _error5;
29512
- case 10:
29513
- _context8.n = 11;
29514
- return this.audioPlayer.resumeAudioContext();
29515
30525
  case 11:
30526
+ _context8.n = 12;
30527
+ return this.audioPlayer.resumeAudioContext();
30528
+ case 12:
29516
30529
  // After permission is granted, send start_continuous_mode message to server
29517
30530
  this.sendMessage({
29518
30531
  t: 'start_continuous_mode',
@@ -29522,29 +30535,29 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
29522
30535
  // Request screen wake lock to keep screen on during voice call (mobile)
29523
30536
  this._requestWakeLock();
29524
30537
  return _context8.a(2, true);
29525
- case 12:
29526
- _context8.p = 12;
30538
+ case 13:
30539
+ _context8.p = 13;
29527
30540
  _t7 = _context8.v;
29528
30541
  if (!(this.audioRecorder && this.audioRecorder.isRecording)) {
29529
- _context8.n = 16;
30542
+ _context8.n = 17;
29530
30543
  break;
29531
30544
  }
29532
- _context8.p = 13;
29533
- _context8.n = 14;
30545
+ _context8.p = 14;
30546
+ _context8.n = 15;
29534
30547
  return this.audioRecorder.stop();
29535
- case 14:
29536
- _context8.n = 16;
29537
- break;
29538
30548
  case 15:
29539
- _context8.p = 15;
29540
- _t8 = _context8.v;
30549
+ _context8.n = 17;
30550
+ break;
29541
30551
  case 16:
30552
+ _context8.p = 16;
30553
+ _t8 = _context8.v;
30554
+ case 17:
29542
30555
  if (!(_t7.isServerRejection || _t7.name === 'ServerRejected')) {
29543
- _context8.n = 17;
30556
+ _context8.n = 18;
29544
30557
  break;
29545
30558
  }
29546
30559
  return _context8.a(2, false);
29547
- case 17:
30560
+ case 18:
29548
30561
  // Log and emit other errors (permission denied, etc.)
29549
30562
  console.error('❌ VoiceSDK v2: Failed to start recording:', _t7);
29550
30563
 
@@ -29559,7 +30572,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
29559
30572
  }
29560
30573
  return _context8.a(2, false);
29561
30574
  }
29562
- }, _callee8, this, [[13, 15], [6, 8], [4, 12]]);
30575
+ }, _callee8, this, [[14, 16], [7, 9], [5, 13]]);
29563
30576
  }));
29564
30577
  function startRecording() {
29565
30578
  return _startRecording.apply(this, arguments);
@@ -31391,7 +32404,10 @@ var AgentSDK = /*#__PURE__*/function () {
31391
32404
  flavor: this.config.flavor || null,
31392
32405
  // Shared client-tool handler map (set by TTPChatWidget so voice + text SDKs
31393
32406
  // both look up handlers in the same registration site).
31394
- sharedToolHandlers: this.config.sharedToolHandlers || null
32407
+ sharedToolHandlers: this.config.sharedToolHandlers || null,
32408
+ // Persisted in-call settings provider (voice speed / response length) —
32409
+ // read fresh at hello_ack so localStorage changes survive reconnects.
32410
+ callSettingsProvider: this.config.callSettingsProvider || null
31395
32411
  }));
31396
32412
 
31397
32413
  // If we have stored resume info from checkResumeBeforeConnect(), use it
@@ -32387,6 +33403,7 @@ __webpack_require__.r(__webpack_exports__);
32387
33403
  /* harmony import */ var _widget_translations_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./widget-translations.js */ "./src/widget/widget-translations.js");
32388
33404
  /* harmony import */ var _flavors_index_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../flavors/index.js */ "./src/flavors/index.js");
32389
33405
  /* harmony import */ var _ecommerce_EcommerceVoiceStrip_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../ecommerce/EcommerceVoiceStrip.js */ "./src/ecommerce/EcommerceVoiceStrip.js");
33406
+ /* harmony import */ var _ecommerce_RecipeStore_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../ecommerce/RecipeStore.js */ "./src/ecommerce/RecipeStore.js");
32390
33407
  var _excluded = ["agentName"];
32391
33408
  function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var n = Object.getOwnPropertySymbols(e); for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; }
32392
33409
  function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
@@ -32415,6 +33432,7 @@ function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" ==
32415
33432
 
32416
33433
 
32417
33434
 
33435
+
32418
33436
  /** Fallback for `buildWebSocketUrl()` when no optional `websocketUrl` override is set (VoiceSDK v2 uses the same default). */
32419
33437
  var DEFAULT_VOICE_WEBSOCKET_URL = 'wss://speech.talktopc.com/ws/conv';
32420
33438
  function isNonEmptyConfigString(v) {
@@ -32507,7 +33525,8 @@ function talkToPcLinkColorForPanel(isLightPanel, mergedVoice, rawConfig, voiceUi
32507
33525
  }
32508
33526
  var TTPChatWidget = /*#__PURE__*/function () {
32509
33527
  function TTPChatWidget() {
32510
- var _this$config$behavior,
33528
+ var _this = this,
33529
+ _this$config$behavior,
32511
33530
  _this$config$panel,
32512
33531
  _this$config$flavor,
32513
33532
  _this$config$text,
@@ -32517,8 +33536,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
32517
33536
  _this$config$voice3,
32518
33537
  _this$config$voice4,
32519
33538
  _this$config$voice5,
32520
- _this$config$voice6,
32521
- _this = this;
33539
+ _this$config$voice6;
32522
33540
  var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
32523
33541
  _classCallCheck(this, TTPChatWidget);
32524
33542
  this._rawConfig = config;
@@ -32526,6 +33544,14 @@ var TTPChatWidget = /*#__PURE__*/function () {
32526
33544
  this.config = this.mergeWithDefaults(config);
32527
33545
  // Silently ignore allowOverride (now server-side only)
32528
33546
 
33547
+ // Persisted in-call settings (voice speed / response length). The provider is
33548
+ // read fresh by VoiceSDK/TextChatSDK right after each hello_ack, so the saved
33549
+ // values re-apply silently on every (re)connect. Rides on this.config so both
33550
+ // voiceConfig spreads and the TextChatSDK constructor pick it up.
33551
+ this.config.callSettingsProvider = function () {
33552
+ return _this._loadCallSettings();
33553
+ };
33554
+
32529
33555
  // Shared client-tool handler map. Both VoiceSDK and TextChatSDK look up
32530
33556
  // handlers in this Map, so a single registerToolHandler() call works for
32531
33557
  // whichever channel the LLM happens to be running on at tool-call time.
@@ -32847,11 +33873,21 @@ var TTPChatWidget = /*#__PURE__*/function () {
32847
33873
  if (flavor.messageHandlers['dismiss_media']) {
32848
33874
  flavor.messageHandlers['dismiss_media']();
32849
33875
  }
33876
+ if (flavor.messageHandlers['dismiss_recipe']) {
33877
+ flavor.messageHandlers['dismiss_recipe']({
33878
+ silent: true
33879
+ });
33880
+ }
32850
33881
  });
32851
33882
  this.voiceSDK.on('bargeIn', function () {
32852
33883
  if (flavor.messageHandlers['dismiss_media']) {
32853
33884
  flavor.messageHandlers['dismiss_media']();
32854
33885
  }
33886
+ if (flavor.messageHandlers['dismiss_recipe']) {
33887
+ flavor.messageHandlers['dismiss_recipe']({
33888
+ silent: true
33889
+ });
33890
+ }
32855
33891
  });
32856
33892
  console.log("[TTPChatWidget] Flavor \"".concat(flavorName, "\" message listeners attached (voice)"));
32857
33893
  }
@@ -33767,6 +34803,20 @@ var TTPChatWidget = /*#__PURE__*/function () {
33767
34803
  };
33768
34804
  }
33769
34805
 
34806
+ /**
34807
+ * Scoped localStorage-backed store for saved recipes (strip badge + popover).
34808
+ */
34809
+ }, {
34810
+ key: "getRecipeStore",
34811
+ value: function getRecipeStore() {
34812
+ if (!this._recipeStore) {
34813
+ var _this$_flavorObj2, _this$config$flavor2;
34814
+ var partnerId = ((_this$_flavorObj2 = this._flavorObj) === null || _this$_flavorObj2 === void 0 ? void 0 : _this$_flavorObj2.partnerId) || (_typeof(this.config.flavor) === 'object' ? (_this$config$flavor2 = this.config.flavor) === null || _this$config$flavor2 === void 0 ? void 0 : _this$config$flavor2.partnerId : null) || 'default';
34815
+ this._recipeStore = (0,_ecommerce_RecipeStore_js__WEBPACK_IMPORTED_MODULE_6__.createRecipeStore)((0,_ecommerce_RecipeStore_js__WEBPACK_IMPORTED_MODULE_6__.recipeStorageKey)(partnerId));
34816
+ }
34817
+ return this._recipeStore;
34818
+ }
34819
+
33770
34820
  /**
33771
34821
  * Generate footer branding HTML
33772
34822
  */
@@ -33854,7 +34904,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
33854
34904
  return;
33855
34905
  }
33856
34906
  this._ensureAboutStyles();
33857
- var version = true ? "2.45.14" : 0;
34907
+ var version = true ? "2.46.1" : 0;
33858
34908
  var convId = this._getLastConversationId();
33859
34909
  var t = function t(k, fb) {
33860
34910
  try {
@@ -33911,6 +34961,190 @@ var TTPChatWidget = /*#__PURE__*/function () {
33911
34961
  this._aboutOverlay = null;
33912
34962
  }
33913
34963
  }
34964
+
34965
+ // ===================================================================================
34966
+ // In-call settings (voice speed / response length) — gear button + dialog
34967
+ // ===================================================================================
34968
+
34969
+ /** localStorage key for the persisted call settings, scoped per agent + origin. */
34970
+ }, {
34971
+ key: "_callSettingsStorageKey",
34972
+ value: function _callSettingsStorageKey() {
34973
+ var agent = this.config.agentId || this.config.appId || 'default';
34974
+ return "ttp_call_settings_".concat(agent);
34975
+ }
34976
+
34977
+ /**
34978
+ * Load persisted call settings. Defaults: agent-configured speed (factor 1.0)
34979
+ * and "normal" response length.
34980
+ * @returns {{voiceSpeedFactor: number, responseLength: string}}
34981
+ */
34982
+ }, {
34983
+ key: "_loadCallSettings",
34984
+ value: function _loadCallSettings() {
34985
+ var defaults = {
34986
+ voiceSpeedFactor: 1.0,
34987
+ responseLength: 'normal'
34988
+ };
34989
+ try {
34990
+ var raw = localStorage.getItem(this._callSettingsStorageKey());
34991
+ if (!raw) return defaults;
34992
+ var s = JSON.parse(raw);
34993
+ var factor = typeof s.voiceSpeedFactor === 'number' ? Math.max(0.5, Math.min(2.0, s.voiceSpeedFactor)) : 1.0;
34994
+ var length = ['detailed', 'normal', 'brief', 'very_brief'].includes(s.responseLength) ? s.responseLength : 'normal';
34995
+ return {
34996
+ voiceSpeedFactor: factor,
34997
+ responseLength: length
34998
+ };
34999
+ } catch (_) {
35000
+ return defaults;
35001
+ }
35002
+ }
35003
+ }, {
35004
+ key: "_saveCallSettings",
35005
+ value: function _saveCallSettings(settings) {
35006
+ try {
35007
+ localStorage.setItem(this._callSettingsStorageKey(), JSON.stringify(settings));
35008
+ } catch (_) {/* storage unavailable — settings still apply for this session */}
35009
+ }
35010
+
35011
+ /**
35012
+ * Persist + push a settings change to whichever channel is live. The backend
35013
+ * applies voice speed from the next spoken line and response length from the
35014
+ * next LLM turn — no tool call involved. announce=true lets the agent speak
35015
+ * its short fixed confirmation (only when idle).
35016
+ */
35017
+ }, {
35018
+ key: "_applyCallSettings",
35019
+ value: function _applyCallSettings(partial) {
35020
+ var _this$voiceInterface4, _this$sdk2;
35021
+ var announce = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
35022
+ var merged = _objectSpread(_objectSpread({}, this._loadCallSettings()), partial);
35023
+ this._saveCallSettings(merged);
35024
+ var voice = (_this$voiceInterface4 = this.voiceInterface) === null || _this$voiceInterface4 === void 0 || (_this$voiceInterface4 = _this$voiceInterface4.sdk) === null || _this$voiceInterface4 === void 0 ? void 0 : _this$voiceInterface4.voiceSDK;
35025
+ if (voice !== null && voice !== void 0 && voice.setCallSettings) {
35026
+ voice.setCallSettings({
35027
+ voiceSpeedFactor: merged.voiceSpeedFactor,
35028
+ responseLength: merged.responseLength
35029
+ }, {
35030
+ announce: announce
35031
+ });
35032
+ }
35033
+ if ((_this$sdk2 = this.sdk) !== null && _this$sdk2 !== void 0 && _this$sdk2.setCallSettings && this.sdk.connected) {
35034
+ this.sdk.setCallSettings({
35035
+ responseLength: merged.responseLength
35036
+ });
35037
+ }
35038
+ }
35039
+
35040
+ /** Inject the settings-dialog styles into document.head once (global, above the strip). */
35041
+ }, {
35042
+ key: "_ensureCallSettingsStyles",
35043
+ value: function _ensureCallSettingsStyles() {
35044
+ if (document.getElementById('ttp-callsettings-dialog-styles')) return;
35045
+ this._ensureAboutStyles(); // reuse the overlay/card/close/row styles
35046
+ var style = document.createElement('style');
35047
+ style.id = 'ttp-callsettings-dialog-styles';
35048
+ style.textContent = "\n .ttp-cs-row { text-align: start; }\n .ttp-cs-head { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 6px; }\n .ttp-cs-val {\n font-size: 13px; font-weight: 700; color: #6d28d9;\n font-variant-numeric: tabular-nums;\n }\n .ttp-cs-slider {\n -webkit-appearance: none; appearance: none; width: 100%; height: 6px;\n border-radius: 3px; background: #e5e7eb; outline: none; cursor: pointer;\n accent-color: #6d28d9; margin: 4px 0 2px;\n }\n .ttp-cs-slider::-webkit-slider-thumb {\n -webkit-appearance: none; appearance: none; width: 18px; height: 18px;\n border-radius: 50%; background: #6d28d9; border: 2px solid #ffffff;\n box-shadow: 0 1px 4px rgba(0,0,0,0.3); cursor: pointer;\n }\n .ttp-cs-slider::-moz-range-thumb {\n width: 18px; height: 18px; border-radius: 50%; background: #6d28d9;\n border: 2px solid #ffffff; box-shadow: 0 1px 4px rgba(0,0,0,0.3); cursor: pointer;\n }\n .ttp-cs-ends {\n display: flex; justify-content: space-between;\n font-size: 10.5px; color: #9ca3af; letter-spacing: 0.2px;\n }\n ";
35049
+ document.head.appendChild(style);
35050
+ }
35051
+
35052
+ /**
35053
+ * Open the in-call settings dialog: two sliders — voice speed (higher = faster)
35054
+ * and response length (very brief on the left → detailed on the right). Values
35055
+ * persist to localStorage and apply from the next line/turn (backend `call_settings`).
35056
+ */
35057
+ }, {
35058
+ key: "_openCallSettingsModal",
35059
+ value: function _openCallSettingsModal() {
35060
+ var _this9 = this,
35061
+ _overlay$querySelecto2;
35062
+ if (this._callSettingsOverlay) {
35063
+ this._closeCallSettingsModal();
35064
+ return;
35065
+ }
35066
+ this._ensureCallSettingsStyles();
35067
+ var t = function t(k, fb) {
35068
+ try {
35069
+ var _this9$voiceInterface, _this9$voiceInterface2, _this9$textInterface, _this9$textInterface$;
35070
+ return ((_this9$voiceInterface = _this9.voiceInterface) === null || _this9$voiceInterface === void 0 || (_this9$voiceInterface2 = _this9$voiceInterface.t) === null || _this9$voiceInterface2 === void 0 ? void 0 : _this9$voiceInterface2.call(_this9$voiceInterface, k)) || ((_this9$textInterface = _this9.textInterface) === null || _this9$textInterface === void 0 || (_this9$textInterface$ = _this9$textInterface.t) === null || _this9$textInterface$ === void 0 ? void 0 : _this9$textInterface$.call(_this9$textInterface, k)) || fb;
35071
+ } catch (_) {
35072
+ return fb;
35073
+ }
35074
+ };
35075
+ var title = t('settingsTitle', 'Call settings');
35076
+ var speedLabel = t('settingsVoiceSpeed', 'Voice speed');
35077
+ var lengthLabel = t('settingsResponseLength', 'Response length');
35078
+ // Slider order: very brief (left) → detailed (right); higher = more detail.
35079
+ var LEVELS = ['very_brief', 'brief', 'normal', 'detailed'];
35080
+ var levelLabels = {
35081
+ detailed: t('lengthDetailed', 'Detailed'),
35082
+ normal: t('lengthNormal', 'Normal'),
35083
+ brief: t('lengthBrief', 'Brief'),
35084
+ very_brief: t('lengthVeryBrief', 'Very brief')
35085
+ };
35086
+ var saved = this._loadCallSettings();
35087
+ var levelIdx = Math.max(0, LEVELS.indexOf(saved.responseLength));
35088
+ var overlay = document.createElement('div');
35089
+ overlay.className = 'ttp-about-overlay';
35090
+ overlay.innerHTML = "\n <div class=\"ttp-about-card\" role=\"dialog\" aria-modal=\"true\" aria-label=\"".concat(title, "\">\n <button type=\"button\" class=\"ttp-about-close\" aria-label=\"Close\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 12 12\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\">\n <path d=\"M10 2L2 10M2 2l8 8\"/>\n </svg>\n </button>\n <div class=\"ttp-about-title\">").concat(title, "</div>\n <div class=\"ttp-about-rows\">\n <div class=\"ttp-about-row ttp-cs-row\">\n <div class=\"ttp-cs-head\">\n <span class=\"ttp-about-key\">").concat(speedLabel, "</span>\n <span class=\"ttp-cs-val\" data-cs-speed-val></span>\n </div>\n <input type=\"range\" class=\"ttp-cs-slider\" data-cs-speed\n min=\"0.5\" max=\"2\" step=\"0.05\" value=\"").concat(saved.voiceSpeedFactor, "\"\n aria-label=\"").concat(speedLabel, "\">\n <div class=\"ttp-cs-ends\"><span>0.5\xD7</span><span>2\xD7</span></div>\n </div>\n <div class=\"ttp-about-row ttp-cs-row\">\n <div class=\"ttp-cs-head\">\n <span class=\"ttp-about-key\">").concat(lengthLabel, "</span>\n <span class=\"ttp-cs-val\" data-cs-length-val></span>\n </div>\n <input type=\"range\" class=\"ttp-cs-slider\" data-cs-length\n min=\"0\" max=\"3\" step=\"1\" value=\"").concat(levelIdx, "\"\n aria-label=\"").concat(lengthLabel, "\">\n <div class=\"ttp-cs-ends\"><span>").concat(levelLabels.very_brief, "</span><span>").concat(levelLabels.detailed, "</span></div>\n </div>\n </div>\n </div>");
35091
+ var speedSlider = overlay.querySelector('[data-cs-speed]');
35092
+ var speedVal = overlay.querySelector('[data-cs-speed-val]');
35093
+ var lengthSlider = overlay.querySelector('[data-cs-length]');
35094
+ var lengthVal = overlay.querySelector('[data-cs-length-val]');
35095
+ var fmtSpeed = function fmtSpeed(v) {
35096
+ return "".concat(Number(v).toFixed(2).replace(/0$/, ''), "\xD7");
35097
+ };
35098
+ var renderVals = function renderVals() {
35099
+ if (speedVal) speedVal.textContent = fmtSpeed(speedSlider.value);
35100
+ if (lengthVal) lengthVal.textContent = levelLabels[LEVELS[Number(lengthSlider.value)]] || '';
35101
+ };
35102
+ renderVals();
35103
+
35104
+ // Live label updates while dragging; persist + push on release only.
35105
+ speedSlider === null || speedSlider === void 0 || speedSlider.addEventListener('input', renderVals);
35106
+ lengthSlider === null || lengthSlider === void 0 || lengthSlider.addEventListener('input', renderVals);
35107
+ speedSlider === null || speedSlider === void 0 || speedSlider.addEventListener('change', function () {
35108
+ _this9._applyCallSettings({
35109
+ voiceSpeedFactor: Number(speedSlider.value)
35110
+ }, true);
35111
+ });
35112
+ lengthSlider === null || lengthSlider === void 0 || lengthSlider.addEventListener('change', function () {
35113
+ _this9._applyCallSettings({
35114
+ responseLength: LEVELS[Number(lengthSlider.value)]
35115
+ }, true);
35116
+ });
35117
+ var close = function close() {
35118
+ return _this9._closeCallSettingsModal();
35119
+ };
35120
+ overlay.addEventListener('click', function (e) {
35121
+ if (e.target === overlay) close();
35122
+ });
35123
+ (_overlay$querySelecto2 = overlay.querySelector('.ttp-about-close')) === null || _overlay$querySelecto2 === void 0 || _overlay$querySelecto2.addEventListener('click', function (e) {
35124
+ e.stopPropagation();
35125
+ close();
35126
+ });
35127
+ this._callSettingsKeyHandler = function (e) {
35128
+ if (e.key === 'Escape') close();
35129
+ };
35130
+ document.addEventListener('keydown', this._callSettingsKeyHandler);
35131
+ document.body.appendChild(overlay);
35132
+ this._callSettingsOverlay = overlay;
35133
+ }
35134
+
35135
+ /** Close the in-call settings dialog if open. */
35136
+ }, {
35137
+ key: "_closeCallSettingsModal",
35138
+ value: function _closeCallSettingsModal() {
35139
+ if (this._callSettingsKeyHandler) {
35140
+ document.removeEventListener('keydown', this._callSettingsKeyHandler);
35141
+ this._callSettingsKeyHandler = null;
35142
+ }
35143
+ if (this._callSettingsOverlay) {
35144
+ this._callSettingsOverlay.remove();
35145
+ this._callSettingsOverlay = null;
35146
+ }
35147
+ }
33914
35148
  }, {
33915
35149
  key: "generatePositionStyles",
33916
35150
  value: function generatePositionStyles() {
@@ -34047,7 +35281,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
34047
35281
  }, {
34048
35282
  key: "setupWidgetEvents",
34049
35283
  value: function setupWidgetEvents() {
34050
- var _this9 = this,
35284
+ var _this0 = this,
34051
35285
  _this$config$behavior8,
34052
35286
  _this$config$position5;
34053
35287
  if (!this.shadowRoot) return;
@@ -34061,17 +35295,17 @@ var TTPChatWidget = /*#__PURE__*/function () {
34061
35295
  launcherBtn.onclick = function (e) {
34062
35296
  e.stopPropagation();
34063
35297
  // A drag just ended — don't treat the trailing click as "open panel".
34064
- if (_this9._suppressLauncherClick) return;
34065
- if (_this9.config.promptAnimation.hideAfterClick) {
34066
- _this9.hidePrompt();
35298
+ if (_this0._suppressLauncherClick) return;
35299
+ if (_this0.config.promptAnimation.hideAfterClick) {
35300
+ _this0.hidePrompt();
34067
35301
  }
34068
- _this9.togglePanel();
35302
+ _this0.togglePanel();
34069
35303
  };
34070
35304
  }
34071
35305
  var closeBtn = this.shadowRoot.getElementById('closeBtn');
34072
35306
  if (closeBtn) {
34073
35307
  closeBtn.onclick = function () {
34074
- _this9._doTogglePanel();
35308
+ _this0._doTogglePanel();
34075
35309
  };
34076
35310
  }
34077
35311
 
@@ -34079,23 +35313,34 @@ var TTPChatWidget = /*#__PURE__*/function () {
34079
35313
  var closeIdle = this.shadowRoot.getElementById('ttpCloseIdle');
34080
35314
  if (closeIdle) {
34081
35315
  closeIdle.onclick = function () {
34082
- return _this9._doTogglePanel();
35316
+ return _this0._doTogglePanel();
34083
35317
  };
34084
35318
  }
34085
35319
  var closeActive = this.shadowRoot.getElementById('ttpCloseActive');
34086
35320
  if (closeActive) {
34087
35321
  closeActive.onclick = function () {
34088
- return _this9._doTogglePanel();
35322
+ return _this0._doTogglePanel();
34089
35323
  };
34090
35324
  }
34091
35325
 
34092
35326
  // "About" info buttons (present in every view: idle / active / compact /
34093
35327
  // mobile idle / mobile active / text top-bar). One shared dialog.
34094
- this.shadowRoot.querySelectorAll('.ttp-info-btn').forEach(function (btn) {
35328
+ // Settings gear buttons share the .ttp-info-btn styling class — skip them here.
35329
+ this.shadowRoot.querySelectorAll('.ttp-info-btn:not(.ttp-settings-btn)').forEach(function (btn) {
35330
+ btn.onclick = function (e) {
35331
+ e.stopPropagation();
35332
+ e.preventDefault();
35333
+ _this0._openAboutModal();
35334
+ };
35335
+ });
35336
+
35337
+ // In-call settings gear buttons (active-call views) — one shared dialog with
35338
+ // the voice-speed / response-length sliders.
35339
+ this.shadowRoot.querySelectorAll('.ttp-settings-btn').forEach(function (btn) {
34095
35340
  btn.onclick = function (e) {
34096
35341
  e.stopPropagation();
34097
35342
  e.preventDefault();
34098
- _this9._openAboutModal();
35343
+ _this0._openCallSettingsModal();
34099
35344
  };
34100
35345
  });
34101
35346
 
@@ -34103,17 +35348,17 @@ var TTPChatWidget = /*#__PURE__*/function () {
34103
35348
  var startTextBtn = this.shadowRoot.getElementById('startTextBtn');
34104
35349
  if (startTextBtn) {
34105
35350
  startTextBtn.onclick = function () {
34106
- return _this9.showText();
35351
+ return _this0.showText();
34107
35352
  };
34108
35353
  }
34109
35354
  var textUnifiedBackBtn = this.shadowRoot.getElementById('textUnifiedBackBtn');
34110
35355
  if (textUnifiedBackBtn) {
34111
35356
  textUnifiedBackBtn.onclick = function () {
34112
- var _this9$config$behavio;
34113
- var widgetMode = ((_this9$config$behavio = _this9.config.behavior) === null || _this9$config$behavio === void 0 ? void 0 : _this9$config$behavio.mode) || 'unified';
35357
+ var _this0$config$behavio;
35358
+ var widgetMode = ((_this0$config$behavio = _this0.config.behavior) === null || _this0$config$behavio === void 0 ? void 0 : _this0$config$behavio.mode) || 'unified';
34114
35359
  if (widgetMode !== 'unified') return;
34115
- _this9.showLanding();
34116
- _this9._openMobileCallTextLanding();
35360
+ _this0.showLanding();
35361
+ _this0._openMobileCallTextLanding();
34117
35362
  };
34118
35363
  }
34119
35364
 
@@ -34143,30 +35388,30 @@ var TTPChatWidget = /*#__PURE__*/function () {
34143
35388
  // Setup back button handler
34144
35389
  if (backBtn) {
34145
35390
  backBtn.onclick = function () {
34146
- var _this9$voiceInterface, _this9$voiceInterface3;
35391
+ var _this0$voiceInterface, _this0$voiceInterface3;
34147
35392
  // Check if we're showing a domain error — clear it and return to voice hero
34148
- if ((_this9$voiceInterface = _this9.voiceInterface) !== null && _this9$voiceInterface !== void 0 && _this9$voiceInterface.isShowingDomainError) {
34149
- var _this9$voiceInterface2;
35393
+ if ((_this0$voiceInterface = _this0.voiceInterface) !== null && _this0$voiceInterface !== void 0 && _this0$voiceInterface.isShowingDomainError) {
35394
+ var _this0$voiceInterface2;
34150
35395
  console.log('🔙 Back button clicked - clearing domain error and returning to voice hero');
34151
35396
  // Clear the domain error flag
34152
- _this9.voiceInterface.isShowingDomainError = false;
35397
+ _this0.voiceInterface.isShowingDomainError = false;
34153
35398
  // Reset the connecting state (this will restore normal UI)
34154
- if ((_this9$voiceInterface2 = _this9.voiceInterface) !== null && _this9$voiceInterface2 !== void 0 && _this9$voiceInterface2.resetConnectingState) {
34155
- _this9.voiceInterface.resetConnectingState();
35399
+ if ((_this0$voiceInterface2 = _this0.voiceInterface) !== null && _this0$voiceInterface2 !== void 0 && _this0$voiceInterface2.resetConnectingState) {
35400
+ _this0.voiceInterface.resetConnectingState();
34156
35401
  }
34157
- _this9.showLanding();
34158
- _this9._openMobileCallTextLanding();
35402
+ _this0.showLanding();
35403
+ _this0._openMobileCallTextLanding();
34159
35404
  return;
34160
35405
  }
34161
- if ((_this9$voiceInterface3 = _this9.voiceInterface) !== null && _this9$voiceInterface3 !== void 0 && _this9$voiceInterface3.isActive) {
35406
+ if ((_this0$voiceInterface3 = _this0.voiceInterface) !== null && _this0$voiceInterface3 !== void 0 && _this0$voiceInterface3.isActive) {
34162
35407
  // If call is active, show message inside widget instead of modal
34163
- _this9.showBackButtonWarning();
35408
+ _this0.showBackButtonWarning();
34164
35409
  } else if (window.innerWidth <= 768) {
34165
- _this9.showLanding();
34166
- _this9._openMobileCallTextLanding();
35410
+ _this0.showLanding();
35411
+ _this0._openMobileCallTextLanding();
34167
35412
  } else {
34168
35413
  // Desktop: return to voice hero (unified home)
34169
- _this9.showLanding();
35414
+ _this0.showLanding();
34170
35415
  }
34171
35416
  };
34172
35417
  }
@@ -34209,7 +35454,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
34209
35454
  // Setup header button handlers
34210
35455
  if (newChatBtn) {
34211
35456
  newChatBtn.onclick = function () {
34212
- return _this9.textInterface.startNewChat();
35457
+ return _this0.textInterface.startNewChat();
34213
35458
  };
34214
35459
  // Hide new chat on unified home (voice hero) initially
34215
35460
  if (isUnified && this.currentView !== 'text') {
@@ -34355,7 +35600,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
34355
35600
  }, {
34356
35601
  key: "_restoreUnitOffset",
34357
35602
  value: function _restoreUnitOffset(key) {
34358
- var _this0 = this;
35603
+ var _this1 = this;
34359
35604
  var saved;
34360
35605
  try {
34361
35606
  saved = JSON.parse(window.localStorage.getItem(key) || 'null');
@@ -34364,13 +35609,13 @@ var TTPChatWidget = /*#__PURE__*/function () {
34364
35609
  }
34365
35610
  if (!saved || typeof saved.x !== 'number' || typeof saved.y !== 'number') return;
34366
35611
  requestAnimationFrame(function () {
34367
- _this0._unitOffset = {
35612
+ _this1._unitOffset = {
34368
35613
  x: 0,
34369
35614
  y: 0
34370
35615
  }; // ensure DOM matches before measuring
34371
- var bases = _this0._unitBaseRects();
34372
- _this0._unitOffset = _this0._clampUnitOffset(saved.x, saved.y, bases);
34373
- _this0._applyUnitOffset();
35616
+ var bases = _this1._unitBaseRects();
35617
+ _this1._unitOffset = _this1._clampUnitOffset(saved.x, saved.y, bases);
35618
+ _this1._applyUnitOffset();
34374
35619
  });
34375
35620
  }
34376
35621
 
@@ -34387,7 +35632,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
34387
35632
  }, {
34388
35633
  key: "_attachUnitDrag",
34389
35634
  value: function _attachUnitDrag(handle, opts) {
34390
- var _this1 = this;
35635
+ var _this10 = this;
34391
35636
  var persist = opts.persist,
34392
35637
  key = opts.key,
34393
35638
  excludeSelector = opts.excludeSelector,
@@ -34408,8 +35653,8 @@ var TTPChatWidget = /*#__PURE__*/function () {
34408
35653
  var dy = e.clientY - startY;
34409
35654
  if (!moved && Math.hypot(dx, dy) < DRAG_THRESHOLD) return;
34410
35655
  moved = true;
34411
- _this1._unitOffset = _this1._clampUnitOffset(baseX + dx, baseY + dy, bases);
34412
- _this1._applyUnitOffset();
35656
+ _this10._unitOffset = _this10._clampUnitOffset(baseX + dx, baseY + dy, bases);
35657
+ _this10._applyUnitOffset();
34413
35658
  };
34414
35659
  var _onUp = function onUp(e) {
34415
35660
  window.removeEventListener('pointermove', onMove);
@@ -34423,7 +35668,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
34423
35668
  if (typeof onMoved === 'function') onMoved();
34424
35669
  if (persist && key) {
34425
35670
  try {
34426
- window.localStorage.setItem(key, JSON.stringify(_this1._unitOffset));
35671
+ window.localStorage.setItem(key, JSON.stringify(_this10._unitOffset));
34427
35672
  } catch (err) {/* unavailable */}
34428
35673
  }
34429
35674
  }
@@ -34434,7 +35679,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
34434
35679
  if (excludeSelector && e.target && e.target.closest && e.target.closest(excludeSelector)) return;
34435
35680
  dragging = true;
34436
35681
  moved = false;
34437
- var o = _this1._unitOffset || (_this1._unitOffset = {
35682
+ var o = _this10._unitOffset || (_this10._unitOffset = {
34438
35683
  x: 0,
34439
35684
  y: 0
34440
35685
  });
@@ -34442,7 +35687,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
34442
35687
  startY = e.clientY;
34443
35688
  baseX = o.x;
34444
35689
  baseY = o.y;
34445
- bases = _this1._unitBaseRects();
35690
+ bases = _this10._unitBaseRects();
34446
35691
  handle.style.setProperty('cursor', 'grabbing');
34447
35692
  try {
34448
35693
  handle.setPointerCapture(e.pointerId);
@@ -34464,7 +35709,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
34464
35709
  key: "setupDraggableWidget",
34465
35710
  value: function setupDraggableWidget() {
34466
35711
  var _this$config$position6,
34467
- _this10 = this;
35712
+ _this11 = this;
34468
35713
  var root = this.shadowRoot;
34469
35714
  if (!root) return;
34470
35715
  var container = root.getElementById('text-chat-button-container');
@@ -34480,14 +35725,14 @@ var TTPChatWidget = /*#__PURE__*/function () {
34480
35725
 
34481
35726
  // Launcher handles: pill + mobile FAB (suppress the click after a drag).
34482
35727
  [root.getElementById('text-chat-button'), root.querySelector('.ttp-mobile-fab')].filter(Boolean).forEach(function (h) {
34483
- return _this10._attachUnitDrag(h, {
35728
+ return _this11._attachUnitDrag(h, {
34484
35729
  persist: persist,
34485
35730
  key: key,
34486
35731
  restCursor: 'grab',
34487
35732
  onMoved: function onMoved() {
34488
- _this10._suppressLauncherClick = true;
35733
+ _this11._suppressLauncherClick = true;
34489
35734
  setTimeout(function () {
34490
- _this10._suppressLauncherClick = false;
35735
+ _this11._suppressLauncherClick = false;
34491
35736
  }, 0);
34492
35737
  }
34493
35738
  });
@@ -34498,7 +35743,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
34498
35743
  ['.ttp-header', '.ttp-call-topbar', '.text-interface-top-bar'].map(function (sel) {
34499
35744
  return panel.querySelector(sel);
34500
35745
  }).filter(Boolean).forEach(function (h) {
34501
- return _this10._attachUnitDrag(h, {
35746
+ return _this11._attachUnitDrag(h, {
34502
35747
  persist: persist,
34503
35748
  key: key,
34504
35749
  restCursor: 'move',
@@ -34533,13 +35778,13 @@ var TTPChatWidget = /*#__PURE__*/function () {
34533
35778
  }, {
34534
35779
  key: "_bindDragResize",
34535
35780
  value: function _bindDragResize() {
34536
- var _this11 = this;
35781
+ var _this12 = this;
34537
35782
  if (this._dragResizeBound) return;
34538
35783
  this._dragResizeBound = true;
34539
35784
  window.addEventListener('resize', function () {
34540
- var _this11$_ecommerceVoi, _this11$_ecommerceVoi2;
34541
- _this11._reclampUnit();
34542
- (_this11$_ecommerceVoi = _this11._ecommerceVoiceStrip) === null || _this11$_ecommerceVoi === void 0 || (_this11$_ecommerceVoi2 = _this11$_ecommerceVoi.reclampDrag) === null || _this11$_ecommerceVoi2 === void 0 || _this11$_ecommerceVoi2.call(_this11$_ecommerceVoi);
35785
+ var _this12$_ecommerceVoi, _this12$_ecommerceVoi2;
35786
+ _this12._reclampUnit();
35787
+ (_this12$_ecommerceVoi = _this12._ecommerceVoiceStrip) === null || _this12$_ecommerceVoi === void 0 || (_this12$_ecommerceVoi2 = _this12$_ecommerceVoi.reclampDrag) === null || _this12$_ecommerceVoi2 === void 0 || _this12$_ecommerceVoi2.call(_this12$_ecommerceVoi);
34543
35788
  });
34544
35789
  }
34545
35790
  }, {
@@ -34588,9 +35833,9 @@ var TTPChatWidget = /*#__PURE__*/function () {
34588
35833
  }, {
34589
35834
  key: "_isDomainErrorVisible",
34590
35835
  value: function _isDomainErrorVisible() {
34591
- var _this$voiceInterface4, _this$voiceInterface5, _idleState$dataset;
34592
- if ((_this$voiceInterface4 = this.voiceInterface) !== null && _this$voiceInterface4 !== void 0 && _this$voiceInterface4.isShowingDomainError) return true;
34593
- var idleState = (_this$voiceInterface5 = this.voiceInterface) === null || _this$voiceInterface5 === void 0 || (_this$voiceInterface5 = _this$voiceInterface5.shadowRoot) === null || _this$voiceInterface5 === void 0 ? void 0 : _this$voiceInterface5.getElementById('voiceIdleState');
35836
+ var _this$voiceInterface5, _this$voiceInterface6, _idleState$dataset;
35837
+ if ((_this$voiceInterface5 = this.voiceInterface) !== null && _this$voiceInterface5 !== void 0 && _this$voiceInterface5.isShowingDomainError) return true;
35838
+ var idleState = (_this$voiceInterface6 = this.voiceInterface) === null || _this$voiceInterface6 === void 0 || (_this$voiceInterface6 = _this$voiceInterface6.shadowRoot) === null || _this$voiceInterface6 === void 0 ? void 0 : _this$voiceInterface6.getElementById('voiceIdleState');
34594
35839
  return idleState && ((_idleState$dataset = idleState.dataset) === null || _idleState$dataset === void 0 ? void 0 : _idleState$dataset.domainError) === 'true';
34595
35840
  }
34596
35841
  }, {
@@ -34618,7 +35863,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
34618
35863
  }, {
34619
35864
  key: "_openMobileCallTextLanding",
34620
35865
  value: function _openMobileCallTextLanding() {
34621
- var _this12 = this;
35866
+ var _this13 = this;
34622
35867
  if (!this.shadowRoot || window.innerWidth > 768) return;
34623
35868
  var mobileFab = this.shadowRoot.querySelector('.ttp-mobile-fab');
34624
35869
  var mobileLanding = this.shadowRoot.getElementById('ttpMobileLanding');
@@ -34629,30 +35874,30 @@ var TTPChatWidget = /*#__PURE__*/function () {
34629
35874
  // "Back" button: composedPath includes the panel but not #ttpMobileLanding, so it treated
34630
35875
  // the click as "outside" and closed the landing immediately (user only saw the FAB again).
34631
35876
  var open = function open() {
34632
- if (!_this12.shadowRoot || window.innerWidth > 768) return;
34633
- var panel = _this12.shadowRoot.getElementById('text-chat-panel');
35877
+ if (!_this13.shadowRoot || window.innerWidth > 768) return;
35878
+ var panel = _this13.shadowRoot.getElementById('text-chat-panel');
34634
35879
  if (panel) {
34635
35880
  panel.classList.remove('open');
34636
- _this12.isOpen = false;
35881
+ _this13.isOpen = false;
34637
35882
  }
34638
- if (_this12.config.promptAnimation.hideAfterClick) {
34639
- _this12.hidePrompt();
35883
+ if (_this13.config.promptAnimation.hideAfterClick) {
35884
+ _this13.hidePrompt();
34640
35885
  }
34641
- var errorBanner = _this12.shadowRoot.getElementById('ttpMobileLandingError');
35886
+ var errorBanner = _this13.shadowRoot.getElementById('ttpMobileLandingError');
34642
35887
  if (errorBanner) errorBanner.style.display = 'none';
34643
- var fab = _this12.shadowRoot.querySelector('.ttp-mobile-fab');
34644
- var landing = _this12.shadowRoot.getElementById('ttpMobileLanding');
35888
+ var fab = _this13.shadowRoot.querySelector('.ttp-mobile-fab');
35889
+ var landing = _this13.shadowRoot.getElementById('ttpMobileLanding');
34645
35890
  if (!fab || !landing) return;
34646
35891
  fab.style.display = 'none';
34647
35892
  landing.classList.add('active');
34648
- _this12.isMobileLandingOpen = true;
35893
+ _this13.isMobileLandingOpen = true;
34649
35894
  };
34650
35895
  queueMicrotask(open);
34651
35896
  }
34652
35897
  }, {
34653
35898
  key: "setupMobileWidgetEvents",
34654
35899
  value: function setupMobileWidgetEvents() {
34655
- var _this13 = this;
35900
+ var _this14 = this;
34656
35901
  if (!this.shadowRoot) return;
34657
35902
  var mobileFab = this.shadowRoot.querySelector('.ttp-mobile-fab');
34658
35903
  var mobileLanding = this.shadowRoot.getElementById('ttpMobileLanding');
@@ -34660,12 +35905,12 @@ var TTPChatWidget = /*#__PURE__*/function () {
34660
35905
  var closeMobileLanding = function closeMobileLanding() {
34661
35906
  mobileLanding.classList.remove('active');
34662
35907
  mobileFab.style.display = '';
34663
- _this13.isMobileLandingOpen = false;
35908
+ _this14.isMobileLandingOpen = false;
34664
35909
  };
34665
35910
  mobileFab.onclick = function () {
34666
35911
  // A drag just ended — don't open the landing on the trailing click.
34667
- if (_this13._suppressLauncherClick) return;
34668
- _this13._openMobileCallTextLanding();
35912
+ if (_this14._suppressLauncherClick) return;
35913
+ _this14._openMobileCallTextLanding();
34669
35914
  };
34670
35915
  var mobileClose = mobileLanding.querySelector('.ttp-mobile-landing__close');
34671
35916
  if (mobileClose) {
@@ -34675,18 +35920,18 @@ var TTPChatWidget = /*#__PURE__*/function () {
34675
35920
  if (mobileCallBtn) {
34676
35921
  mobileCallBtn.onclick = function () {
34677
35922
  closeMobileLanding();
34678
- var panel = _this13.shadowRoot.getElementById('text-chat-panel');
35923
+ var panel = _this14.shadowRoot.getElementById('text-chat-panel');
34679
35924
  if (panel) {
34680
35925
  panel.style.removeProperty('display');
34681
35926
  panel.classList.add('open');
34682
- _this13.isOpen = true;
35927
+ _this14.isOpen = true;
34683
35928
  }
34684
35929
  mobileFab.style.display = 'none';
34685
- _this13.showVoice();
35930
+ _this14.showVoice();
34686
35931
  // Landing already chose "Voice call" — start the call (mic permission / pre-prompt) instead of
34687
35932
  // leaving users on a second "Start Call" tap that feels broken.
34688
35933
  queueMicrotask(function () {
34689
- _this13.startVoiceCall().catch(function (err) {
35934
+ _this14.startVoiceCall().catch(function (err) {
34690
35935
  console.error('[TTPChatWidget] Mobile landing voice start failed:', err);
34691
35936
  });
34692
35937
  });
@@ -34696,20 +35941,20 @@ var TTPChatWidget = /*#__PURE__*/function () {
34696
35941
  if (mobileChatBtn) {
34697
35942
  mobileChatBtn.onclick = function () {
34698
35943
  closeMobileLanding();
34699
- var panel = _this13.shadowRoot.getElementById('text-chat-panel');
35944
+ var panel = _this14.shadowRoot.getElementById('text-chat-panel');
34700
35945
  if (panel) {
34701
35946
  panel.style.removeProperty('display');
34702
35947
  panel.classList.add('open');
34703
- _this13.isOpen = true;
35948
+ _this14.isOpen = true;
34704
35949
  }
34705
35950
  mobileFab.style.display = 'none';
34706
- _this13.showText();
35951
+ _this14.showText();
34707
35952
  };
34708
35953
  }
34709
35954
 
34710
35955
  // Close mobile landing on outside click
34711
35956
  document.addEventListener('click', function (e) {
34712
- if (!_this13.isMobileLandingOpen) return;
35957
+ if (!_this14.isMobileLandingOpen) return;
34713
35958
  var path = e.composedPath();
34714
35959
  if (!path.includes(mobileLanding) && !path.includes(mobileFab)) {
34715
35960
  closeMobileLanding();
@@ -34718,7 +35963,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
34718
35963
 
34719
35964
  // Close mobile landing on Escape
34720
35965
  document.addEventListener('keydown', function (e) {
34721
- if (e.key === 'Escape' && _this13.isMobileLandingOpen) {
35966
+ if (e.key === 'Escape' && _this14.isMobileLandingOpen) {
34722
35967
  closeMobileLanding();
34723
35968
  }
34724
35969
  });
@@ -34734,19 +35979,19 @@ var TTPChatWidget = /*#__PURE__*/function () {
34734
35979
  _prevIsMobile = nowMobile;
34735
35980
  if (nowMobile) {
34736
35981
  // Switched desktop → mobile: close desktop panel so only mobile FAB shows
34737
- if (_this13.isOpen) {
34738
- var _this13$shadowRoot, _this13$shadowRoot2;
34739
- var panel = (_this13$shadowRoot = _this13.shadowRoot) === null || _this13$shadowRoot === void 0 ? void 0 : _this13$shadowRoot.getElementById('text-chat-panel');
35982
+ if (_this14.isOpen) {
35983
+ var _this14$shadowRoot, _this14$shadowRoot2;
35984
+ var panel = (_this14$shadowRoot = _this14.shadowRoot) === null || _this14$shadowRoot === void 0 ? void 0 : _this14$shadowRoot.getElementById('text-chat-panel');
34740
35985
  if (panel) {
34741
35986
  panel.classList.remove('open');
34742
- _this13.isOpen = false;
35987
+ _this14.isOpen = false;
34743
35988
  }
34744
- var pill = (_this13$shadowRoot2 = _this13.shadowRoot) === null || _this13$shadowRoot2 === void 0 ? void 0 : _this13$shadowRoot2.getElementById('text-chat-button');
35989
+ var pill = (_this14$shadowRoot2 = _this14.shadowRoot) === null || _this14$shadowRoot2 === void 0 ? void 0 : _this14$shadowRoot2.getElementById('text-chat-button');
34745
35990
  if (pill) pill.classList.remove('open');
34746
35991
  }
34747
35992
  } else {
34748
35993
  // Switched mobile → desktop: close mobile landing/FAB so only desktop pill shows
34749
- if (_this13.isMobileLandingOpen) {
35994
+ if (_this14.isMobileLandingOpen) {
34750
35995
  closeMobileLanding();
34751
35996
  }
34752
35997
  // Ensure mobile FAB is reset to CSS-controlled state (media query hides it on desktop)
@@ -34758,10 +36003,10 @@ var TTPChatWidget = /*#__PURE__*/function () {
34758
36003
  }, {
34759
36004
  key: "setupKeyboardNavigation",
34760
36005
  value: function setupKeyboardNavigation() {
34761
- var _this14 = this;
36006
+ var _this15 = this;
34762
36007
  document.addEventListener('keydown', function (e) {
34763
- if (e.key === 'Escape' && _this14.isOpen) {
34764
- _this14.togglePanel();
36008
+ if (e.key === 'Escape' && _this15.isOpen) {
36009
+ _this15.togglePanel();
34765
36010
  }
34766
36011
  });
34767
36012
  }
@@ -34772,7 +36017,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
34772
36017
  }, {
34773
36018
  key: "setupPromptAnimation",
34774
36019
  value: function setupPromptAnimation() {
34775
- var _this15 = this;
36020
+ var _this16 = this;
34776
36021
  if (!this.shadowRoot) return;
34777
36022
  var promptConfig = this.config.promptAnimation || {};
34778
36023
  // Default to disabled if not specified (enabled === true means explicitly enabled)
@@ -34802,7 +36047,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
34802
36047
  // Auto-hide after configured seconds (only if widget is closed)
34803
36048
  if (!this.isOpen && promptConfig.hideAfterSeconds !== null && promptConfig.hideAfterSeconds > 0) {
34804
36049
  this.promptAutoHideTimer = setTimeout(function () {
34805
- _this15.hidePrompt();
36050
+ _this16.hidePrompt();
34806
36051
  }, promptConfig.hideAfterSeconds * 1000);
34807
36052
  }
34808
36053
 
@@ -34814,7 +36059,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
34814
36059
  // Wrap the callback to also hide pulse rings
34815
36060
  this.config.onConversationStart = function () {
34816
36061
  // Hide pulse rings when call starts
34817
- var pulseRings = _this15.shadowRoot.getElementById('prompt-pulse-rings');
36062
+ var pulseRings = _this16.shadowRoot.getElementById('prompt-pulse-rings');
34818
36063
  if (pulseRings) pulseRings.style.display = 'none';
34819
36064
 
34820
36065
  // Call original callback if it exists
@@ -34826,7 +36071,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
34826
36071
  // Also listen to recordingStarted event from SDK if available
34827
36072
  if (this.voiceInterface.sdk.voiceSDK) {
34828
36073
  this.voiceInterface.sdk.voiceSDK.on('recordingStarted', function () {
34829
- var pulseRings = _this15.shadowRoot.getElementById('prompt-pulse-rings');
36074
+ var pulseRings = _this16.shadowRoot.getElementById('prompt-pulse-rings');
34830
36075
  if (pulseRings) pulseRings.style.display = 'none';
34831
36076
  });
34832
36077
  }
@@ -34874,7 +36119,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
34874
36119
  }, {
34875
36120
  key: "showPrompt",
34876
36121
  value: function showPrompt() {
34877
- var _this16 = this;
36122
+ var _this17 = this;
34878
36123
  if (!this.shadowRoot) return;
34879
36124
  var promptConfig = this.config.promptAnimation || {};
34880
36125
  // Default to enabled if not specified
@@ -34894,8 +36139,8 @@ var TTPChatWidget = /*#__PURE__*/function () {
34894
36139
  // Function to actually show the elements
34895
36140
  var doShow = function doShow() {
34896
36141
  // Show prompt bubble (check both shadow root and document for compatibility)
34897
- var promptBubble = _this16.shadowRoot.getElementById('prompt-bubble');
34898
- if (!promptBubble && _this16.shadowRoot !== document) {
36142
+ var promptBubble = _this17.shadowRoot.getElementById('prompt-bubble');
36143
+ if (!promptBubble && _this17.shadowRoot !== document) {
34899
36144
  promptBubble = document.getElementById('prompt-bubble');
34900
36145
  }
34901
36146
  if (promptBubble) {
@@ -34909,8 +36154,8 @@ var TTPChatWidget = /*#__PURE__*/function () {
34909
36154
 
34910
36155
  // Show pulse rings if enabled
34911
36156
  if (promptConfig.showPulseRings !== false) {
34912
- var _pulseRings = _this16.shadowRoot.getElementById('prompt-pulse-rings');
34913
- if (!_pulseRings && _this16.shadowRoot !== document) {
36157
+ var _pulseRings = _this17.shadowRoot.getElementById('prompt-pulse-rings');
36158
+ if (!_pulseRings && _this17.shadowRoot !== document) {
34914
36159
  _pulseRings = document.getElementById('prompt-pulse-rings');
34915
36160
  }
34916
36161
  if (_pulseRings) {
@@ -34947,7 +36192,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
34947
36192
  }
34948
36193
  // Start new timer
34949
36194
  this.promptAutoHideTimer = setTimeout(function () {
34950
- _this16.hidePrompt();
36195
+ _this17.hidePrompt();
34951
36196
  }, promptConfig.hideAfterSeconds * 1000);
34952
36197
  }
34953
36198
  }
@@ -34963,7 +36208,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
34963
36208
  }, {
34964
36209
  key: "_doTogglePanel",
34965
36210
  value: function _doTogglePanel() {
34966
- var _this17 = this;
36211
+ var _this18 = this;
34967
36212
  if (!this.shadowRoot) return;
34968
36213
  this.isOpen = !this.isOpen;
34969
36214
  var panel = this.shadowRoot.getElementById('text-chat-panel');
@@ -34977,7 +36222,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
34977
36222
  if ((_this$config$position8 = this.config.position) !== null && _this$config$position8 !== void 0 && _this$config$position8.draggable) {
34978
36223
  this._applyUnitOffset();
34979
36224
  requestAnimationFrame(function () {
34980
- return _this17._reclampUnit();
36225
+ return _this18._reclampUnit();
34981
36226
  });
34982
36227
  }
34983
36228
  }
@@ -34994,7 +36239,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
34994
36239
  // Panel is opening - hide prompt
34995
36240
  this.hidePrompt();
34996
36241
  setTimeout(function () {
34997
- var input = _this17.shadowRoot.getElementById('messageInput');
36242
+ var input = _this18.shadowRoot.getElementById('messageInput');
34998
36243
  if (input) input.focus();
34999
36244
  }, 100);
35000
36245
  } else {
@@ -35006,13 +36251,13 @@ var TTPChatWidget = /*#__PURE__*/function () {
35006
36251
  // Panel is closing - show prompt (if enabled)
35007
36252
  this.showPrompt();
35008
36253
  setTimeout(function () {
35009
- var _this17$config$prompt;
35010
- var promptBubble = _this17.shadowRoot.getElementById('prompt-bubble');
36254
+ var _this18$config$prompt;
36255
+ var promptBubble = _this18.shadowRoot.getElementById('prompt-bubble');
35011
36256
  if (promptBubble && promptBubble.style.display === 'none') {
35012
36257
  promptBubble.style.display = 'block';
35013
36258
  }
35014
- var pulseRings = _this17.shadowRoot.getElementById('prompt-pulse-rings');
35015
- if (pulseRings && ((_this17$config$prompt = _this17.config.promptAnimation) === null || _this17$config$prompt === void 0 ? void 0 : _this17$config$prompt.showPulseRings) !== false) {
36259
+ var pulseRings = _this18.shadowRoot.getElementById('prompt-pulse-rings');
36260
+ if (pulseRings && ((_this18$config$prompt = _this18.config.promptAnimation) === null || _this18$config$prompt === void 0 ? void 0 : _this18$config$prompt.showPulseRings) !== false) {
35016
36261
  if (pulseRings.style.display === 'none') {
35017
36262
  pulseRings.style.display = 'block';
35018
36263
  }
@@ -35204,8 +36449,8 @@ var TTPChatWidget = /*#__PURE__*/function () {
35204
36449
  }, {
35205
36450
  key: "showBackButtonWarning",
35206
36451
  value: function showBackButtonWarning() {
35207
- var _this$voiceInterface6, _this$voiceInterface$;
35208
- if (!((_this$voiceInterface6 = this.voiceInterface) !== null && _this$voiceInterface6 !== void 0 && _this$voiceInterface6.isActive)) return;
36452
+ var _this$voiceInterface7, _this$voiceInterface$;
36453
+ if (!((_this$voiceInterface7 = this.voiceInterface) !== null && _this$voiceInterface7 !== void 0 && _this$voiceInterface7.isActive)) return;
35209
36454
 
35210
36455
  // Show warning message in the voice interface
35211
36456
  if ((_this$voiceInterface$ = this.voiceInterface.desktop) !== null && _this$voiceInterface$ !== void 0 && _this$voiceInterface$.showBackButtonWarning) {
@@ -35280,13 +36525,13 @@ var TTPChatWidget = /*#__PURE__*/function () {
35280
36525
  value: function updateConfig(newConfig) {
35281
36526
  var _this$config3,
35282
36527
  _this$config4,
35283
- _this$config$flavor2,
36528
+ _this$config$flavor3,
35284
36529
  _this$config$voice8,
35285
36530
  _this$config$voice9,
35286
36531
  _this$config$voice0,
35287
36532
  _this$config$voice1,
35288
36533
  _this$config$voice10,
35289
- _this18 = this;
36534
+ _this19 = this;
35290
36535
  // Deep merge nested objects
35291
36536
  var mergedConfig = _objectSpread({}, this.config);
35292
36537
 
@@ -35484,7 +36729,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
35484
36729
  // Recreate interfaces with new config (after mergeWithDefaults, so language is current)
35485
36730
  // Ensure language is correctly passed - use the merged config value
35486
36731
  var currentLanguage = this.config.language || 'en';
35487
- var desktopMinimizedEcommerceStripUi = typeof window !== 'undefined' && window.innerWidth > 768 && ((_this$config$flavor2 = this.config.flavor) === null || _this$config$flavor2 === void 0 ? void 0 : _this$config$flavor2.callView) === 'minimized';
36732
+ var desktopMinimizedEcommerceStripUi = typeof window !== 'undefined' && window.innerWidth > 768 && ((_this$config$flavor3 = this.config.flavor) === null || _this$config$flavor3 === void 0 ? void 0 : _this$config$flavor3.callView) === 'minimized';
35488
36733
  var voiceConfig = _objectSpread(_objectSpread(_objectSpread({}, this.config), this.config.voice), {}, {
35489
36734
  agentName: this.config.agentName,
35490
36735
  language: currentLanguage,
@@ -35504,20 +36749,20 @@ var TTPChatWidget = /*#__PURE__*/function () {
35504
36749
  outputBitDepth: ((_this$config$voice10 = this.config.voice) === null || _this$config$voice10 === void 0 ? void 0 : _this$config$voice10.outputBitDepth) || 16,
35505
36750
  desktopMinimizedEcommerceStripUi: desktopMinimizedEcommerceStripUi,
35506
36751
  onDesktopMinimizedStripCallStart: desktopMinimizedEcommerceStripUi ? function () {
35507
- var _this18$_ecommerceVoi;
35508
- (_this18$_ecommerceVoi = _this18._ecommerceVoiceStrip) === null || _this18$_ecommerceVoi === void 0 || _this18$_ecommerceVoi.show();
35509
- if (_this18.isOpen) _this18.minimize();
35510
- _this18._setPanelFooterVisible(false);
35511
- _this18._setEcommerceStripLauncherVisible(false);
36752
+ var _this19$_ecommerceVoi;
36753
+ (_this19$_ecommerceVoi = _this19._ecommerceVoiceStrip) === null || _this19$_ecommerceVoi === void 0 || _this19$_ecommerceVoi.show();
36754
+ if (_this19.isOpen) _this19.minimize();
36755
+ _this19._setPanelFooterVisible(false);
36756
+ _this19._setEcommerceStripLauncherVisible(false);
35512
36757
  } : undefined,
35513
36758
  onDesktopMinimizedStripLauncherRestore: desktopMinimizedEcommerceStripUi ? function () {
35514
- return _this18._restoreDesktopEcommerceStripChrome();
36759
+ return _this19._restoreDesktopEcommerceStripChrome();
35515
36760
  } : undefined,
35516
36761
  // Pass event callbacks to voice interface (forward only; panel is not auto-minimized on call start)
35517
36762
  onConversationStart: function onConversationStart() {
35518
- if (typeof _this18.config.onConversationStart === 'function') {
35519
- var _this18$config;
35520
- (_this18$config = _this18.config).onConversationStart.apply(_this18$config, arguments);
36763
+ if (typeof _this19.config.onConversationStart === 'function') {
36764
+ var _this19$config;
36765
+ (_this19$config = _this19.config).onConversationStart.apply(_this19$config, arguments);
35521
36766
  }
35522
36767
  },
35523
36768
  onConversationEnd: this.config.onConversationEnd,
@@ -35526,48 +36771,48 @@ var TTPChatWidget = /*#__PURE__*/function () {
35526
36771
  onAudioStoppedPlaying: this.config.onAudioStoppedPlaying,
35527
36772
  onSubtitleDisplay: this.config.onSubtitleDisplay,
35528
36773
  onCallEnd: function onCallEnd() {
35529
- var _this18$config$behavi;
35530
- if (_this18._isDomainErrorVisible()) return;
35531
- var widgetMode = ((_this18$config$behavi = _this18.config.behavior) === null || _this18$config$behavi === void 0 ? void 0 : _this18$config$behavi.mode) || 'unified';
36774
+ var _this19$config$behavi;
36775
+ if (_this19._isDomainErrorVisible()) return;
36776
+ var widgetMode = ((_this19$config$behavi = _this19.config.behavior) === null || _this19$config$behavi === void 0 ? void 0 : _this19$config$behavi.mode) || 'unified';
35532
36777
  if (widgetMode === 'unified') {
35533
36778
  if (window.innerWidth <= 768) {
35534
- var _this18$shadowRoot, _this18$shadowRoot2;
35535
- var panel = (_this18$shadowRoot = _this18.shadowRoot) === null || _this18$shadowRoot === void 0 ? void 0 : _this18$shadowRoot.getElementById('text-chat-panel');
36779
+ var _this19$shadowRoot, _this19$shadowRoot2;
36780
+ var panel = (_this19$shadowRoot = _this19.shadowRoot) === null || _this19$shadowRoot === void 0 ? void 0 : _this19$shadowRoot.getElementById('text-chat-panel');
35536
36781
  if (panel) {
35537
36782
  panel.classList.remove('open');
35538
- _this18.isOpen = false;
36783
+ _this19.isOpen = false;
35539
36784
  }
35540
- var fab = (_this18$shadowRoot2 = _this18.shadowRoot) === null || _this18$shadowRoot2 === void 0 ? void 0 : _this18$shadowRoot2.querySelector('.ttp-mobile-fab');
36785
+ var fab = (_this19$shadowRoot2 = _this19.shadowRoot) === null || _this19$shadowRoot2 === void 0 ? void 0 : _this19$shadowRoot2.querySelector('.ttp-mobile-fab');
35541
36786
  if (fab) fab.style.display = '';
35542
36787
  }
35543
- _this18.showLanding();
36788
+ _this19.showLanding();
35544
36789
  }
35545
36790
  },
35546
36791
  onShowVoice: function onShowVoice() {
35547
- _this18.showVoice();
36792
+ _this19.showVoice();
35548
36793
  },
35549
36794
  onShowLanding: function onShowLanding() {
35550
- _this18.showLanding();
36795
+ _this19.showLanding();
35551
36796
  },
35552
36797
  onDisclaimerDeclined: function onDisclaimerDeclined() {
35553
- var _this18$config$behavi2;
35554
- if (_this18._isDomainErrorVisible()) return;
35555
- var widgetMode = ((_this18$config$behavi2 = _this18.config.behavior) === null || _this18$config$behavi2 === void 0 ? void 0 : _this18$config$behavi2.mode) || 'unified';
36798
+ var _this19$config$behavi2;
36799
+ if (_this19._isDomainErrorVisible()) return;
36800
+ var widgetMode = ((_this19$config$behavi2 = _this19.config.behavior) === null || _this19$config$behavi2 === void 0 ? void 0 : _this19$config$behavi2.mode) || 'unified';
35556
36801
  if (widgetMode === 'voice-only') {
35557
- _this18.showVoice();
36802
+ _this19.showVoice();
35558
36803
  return;
35559
36804
  }
35560
36805
  if (widgetMode !== 'unified') return;
35561
36806
  if (window.innerWidth <= 768) {
35562
- var _this18$shadowRoot3;
35563
- var panel = (_this18$shadowRoot3 = _this18.shadowRoot) === null || _this18$shadowRoot3 === void 0 ? void 0 : _this18$shadowRoot3.getElementById('text-chat-panel');
36807
+ var _this19$shadowRoot3;
36808
+ var panel = (_this19$shadowRoot3 = _this19.shadowRoot) === null || _this19$shadowRoot3 === void 0 ? void 0 : _this19$shadowRoot3.getElementById('text-chat-panel');
35564
36809
  if (panel) {
35565
36810
  panel.classList.remove('open');
35566
- _this18.isOpen = false;
36811
+ _this19.isOpen = false;
35567
36812
  }
35568
- _this18._openMobileCallTextLanding();
36813
+ _this19._openMobileCallTextLanding();
35569
36814
  } else {
35570
- _this18.showLanding();
36815
+ _this19.showLanding();
35571
36816
  }
35572
36817
  },
35573
36818
  direction: this.getEffectiveTextDirection()
@@ -35614,9 +36859,9 @@ var TTPChatWidget = /*#__PURE__*/function () {
35614
36859
  }, {
35615
36860
  key: "_initEcommerceVoiceStrip",
35616
36861
  value: function _initEcommerceVoiceStrip() {
35617
- var _this19 = this,
36862
+ var _this20 = this,
35618
36863
  _this$config$position9,
35619
- _this$voiceInterface7;
36864
+ _this$voiceInterface8;
35620
36865
  var flavor = this.config.flavor && _typeof(this.config.flavor) === 'object' ? this.config.flavor : null;
35621
36866
  if (window.innerWidth <= 768) return;
35622
36867
  if (!flavor || flavor.callView !== 'minimized') return;
@@ -35637,34 +36882,44 @@ var TTPChatWidget = /*#__PURE__*/function () {
35637
36882
  endCallButtonColor: v.endCallButtonColor || '#ef4444'
35638
36883
  },
35639
36884
  getVoiceUiState: function getVoiceUiState() {
35640
- var _this19$voiceInterfac, _this19$voiceInterfac2, _this19$voiceInterfac3;
36885
+ var _this20$voiceInterfac, _this20$voiceInterfac2, _this20$voiceInterfac3;
35641
36886
  return {
35642
- isMicMuted: !!((_this19$voiceInterfac = _this19.voiceInterface) !== null && _this19$voiceInterfac !== void 0 && _this19$voiceInterfac.isMicMuted),
35643
- isPaused: !!((_this19$voiceInterfac2 = _this19.voiceInterface) !== null && _this19$voiceInterfac2 !== void 0 && _this19$voiceInterfac2.isPaused),
35644
- isSpeakerMuted: !!((_this19$voiceInterfac3 = _this19.voiceInterface) !== null && _this19$voiceInterfac3 !== void 0 && _this19$voiceInterfac3.isSpeakerMuted)
36887
+ isMicMuted: !!((_this20$voiceInterfac = _this20.voiceInterface) !== null && _this20$voiceInterfac !== void 0 && _this20$voiceInterfac.isMicMuted),
36888
+ isPaused: !!((_this20$voiceInterfac2 = _this20.voiceInterface) !== null && _this20$voiceInterfac2 !== void 0 && _this20$voiceInterfac2.isPaused),
36889
+ isSpeakerMuted: !!((_this20$voiceInterfac3 = _this20.voiceInterface) !== null && _this20$voiceInterfac3 !== void 0 && _this20$voiceInterfac3.isSpeakerMuted)
35645
36890
  };
35646
36891
  },
35647
36892
  onAbout: function onAbout() {
35648
- return _this19._openAboutModal();
36893
+ return _this20._openAboutModal();
36894
+ },
36895
+ onSettings: function onSettings() {
36896
+ return _this20._openCallSettingsModal();
36897
+ },
36898
+ onRecipeSelect: function onRecipeSelect(recipe) {
36899
+ var _this20$_flavor, _this20$_flavor$_getR, _this20$_flavor$_getR2;
36900
+ (_this20$_flavor = _this20._flavor) === null || _this20$_flavor === void 0 || (_this20$_flavor$_getR = _this20$_flavor._getRecipeHandler) === null || _this20$_flavor$_getR === void 0 || (_this20$_flavor$_getR = _this20$_flavor$_getR.call(_this20$_flavor)) === null || _this20$_flavor$_getR === void 0 || (_this20$_flavor$_getR2 = _this20$_flavor$_getR.handleShowRecipe) === null || _this20$_flavor$_getR2 === void 0 || _this20$_flavor$_getR2.call(_this20$_flavor$_getR, {
36901
+ recipe: recipe,
36902
+ reshow: true
36903
+ });
35649
36904
  },
35650
36905
  onHangup: function onHangup() {
35651
- if (_this19.voiceInterface) _this19.voiceInterface.endVoiceCall();
36906
+ if (_this20.voiceInterface) _this20.voiceInterface.endVoiceCall();
35652
36907
  },
35653
36908
  onToggleMute: function onToggleMute() {
35654
- if (_this19.voiceInterface) _this19.voiceInterface.toggleMute();
36909
+ if (_this20.voiceInterface) _this20.voiceInterface.toggleMute();
35655
36910
  strip.syncFromVoice();
35656
36911
  },
35657
36912
  onTogglePause: function onTogglePause() {
35658
- if (_this19.voiceInterface) _this19.voiceInterface.togglePause();
36913
+ if (_this20.voiceInterface) _this20.voiceInterface.togglePause();
35659
36914
  strip.syncFromVoice();
35660
36915
  },
35661
36916
  onToggleSpeaker: function onToggleSpeaker() {
35662
- if (_this19.voiceInterface) _this19.voiceInterface.toggleSpeaker();
36917
+ if (_this20.voiceInterface) _this20.voiceInterface.toggleSpeaker();
35663
36918
  strip.syncFromVoice();
35664
36919
  },
35665
36920
  onSendText: function onSendText(text) {
35666
- var _this19$voiceInterfac4;
35667
- var voiceSDK = (_this19$voiceInterfac4 = _this19.voiceInterface) === null || _this19$voiceInterfac4 === void 0 || (_this19$voiceInterfac4 = _this19$voiceInterfac4.sdk) === null || _this19$voiceInterfac4 === void 0 ? void 0 : _this19$voiceInterfac4.voiceSDK;
36921
+ var _this20$voiceInterfac4;
36922
+ var voiceSDK = (_this20$voiceInterfac4 = _this20.voiceInterface) === null || _this20$voiceInterfac4 === void 0 || (_this20$voiceInterfac4 = _this20$voiceInterfac4.sdk) === null || _this20$voiceInterfac4 === void 0 ? void 0 : _this20$voiceInterfac4.voiceSDK;
35668
36923
  if (voiceSDK && typeof voiceSDK.sendMessage === 'function') {
35669
36924
  voiceSDK.sendMessage({
35670
36925
  t: 'text_inject',
@@ -35674,6 +36929,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
35674
36929
  }
35675
36930
  });
35676
36931
  this._ecommerceVoiceStrip = strip;
36932
+ strip.bindRecipeStore(this.getRecipeStore());
35677
36933
 
35678
36934
  // Make the strip draggable too when position.draggable is enabled.
35679
36935
  if ((_this$config$position9 = this.config.position) !== null && _this$config$position9 !== void 0 && _this$config$position9.draggable) {
@@ -35681,7 +36937,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
35681
36937
  }
35682
36938
 
35683
36939
  // Show strip when conversation starts
35684
- var agentSDK = (_this$voiceInterface7 = this.voiceInterface) === null || _this$voiceInterface7 === void 0 ? void 0 : _this$voiceInterface7.sdk;
36940
+ var agentSDK = (_this$voiceInterface8 = this.voiceInterface) === null || _this$voiceInterface8 === void 0 ? void 0 : _this$voiceInterface8.sdk;
35685
36941
  if (agentSDK) {
35686
36942
  var origConvStart = agentSDK.onConversationStart;
35687
36943
  agentSDK.onConversationStart = function () {
@@ -35719,7 +36975,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
35719
36975
  var _this$voiceInterface$2;
35720
36976
  var origOnCallEnd = this.voiceInterface.config.onCallEnd;
35721
36977
  this.voiceInterface.config.onCallEnd = function () {
35722
- _this19._restoreDesktopEcommerceStripChrome();
36978
+ _this20._restoreDesktopEcommerceStripChrome();
35723
36979
  if (typeof origOnCallEnd === 'function') origOnCallEnd();
35724
36980
  };
35725
36981
 
@@ -35843,7 +37099,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
35843
37099
  key: "injectData",
35844
37100
  value: (function () {
35845
37101
  var _injectData = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee6(data) {
35846
- var _this$voiceInterface8;
37102
+ var _this$voiceInterface9;
35847
37103
  var options,
35848
37104
  voiceSDK,
35849
37105
  ack,
@@ -35868,7 +37124,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
35868
37124
  throw new Error('Voice call is not active');
35869
37125
  case 2:
35870
37126
  // Check if SDK is available
35871
- voiceSDK = (_this$voiceInterface8 = this.voiceInterface) === null || _this$voiceInterface8 === void 0 ? void 0 : _this$voiceInterface8.getVoiceSDK();
37127
+ voiceSDK = (_this$voiceInterface9 = this.voiceInterface) === null || _this$voiceInterface9 === void 0 ? void 0 : _this$voiceInterface9.getVoiceSDK();
35872
37128
  if (voiceSDK) {
35873
37129
  _context6.n = 3;
35874
37130
  break;
@@ -36902,7 +38158,9 @@ var VoiceInterface = /*#__PURE__*/function () {
36902
38158
  flavor: config.flavor,
36903
38159
  // Shared client-tool handler map from TTPChatWidget (so the same handler
36904
38160
  // map serves both voice and text channels).
36905
- sharedToolHandlers: config.sharedToolHandlers
38161
+ sharedToolHandlers: config.sharedToolHandlers,
38162
+ // Persisted in-call settings provider (voice speed / response length).
38163
+ callSettingsProvider: config.callSettingsProvider
36906
38164
  });
36907
38165
 
36908
38166
  // Initialize modules with context (delegation pattern)
@@ -41278,6 +42536,22 @@ var Templates = /*#__PURE__*/function () {
41278
42536
  return "<button type=\"button\" class=\"".concat(cls, "\" aria-label=\"").concat(escapeHtmlAttr(label), "\" title=\"").concat(escapeHtmlAttr(label), "\">\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"9\"/>\n <line x1=\"12\" y1=\"11\" x2=\"12\" y2=\"16\"/>\n <circle cx=\"12\" cy=\"7.7\" r=\"1.05\" fill=\"currentColor\" stroke=\"none\"/>\n </svg>\n </button>");
41279
42537
  }
41280
42538
 
42539
+ /**
42540
+ * Shared in-call settings gear button markup. Opens the call-settings dialog
42541
+ * (voice speed / response length sliders) wired by TTPChatWidget. Shares the
42542
+ * .ttp-info-btn styling class; .ttp-settings-btn is the wiring discriminator.
42543
+ * @param {string} [extraClass] - extra class(es) for positioning per view
42544
+ * @returns {string}
42545
+ */
42546
+ }, {
42547
+ key: "settingsButton",
42548
+ value: function settingsButton() {
42549
+ var extraClass = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
42550
+ var cls = extraClass ? "ttp-info-btn ttp-settings-btn ".concat(extraClass) : 'ttp-info-btn ttp-settings-btn';
42551
+ var label = this.t('settingsTitle') || 'Call settings';
42552
+ return "<button type=\"button\" class=\"".concat(cls, "\" aria-label=\"").concat(escapeHtmlAttr(label), "\" title=\"").concat(escapeHtmlAttr(label), "\">\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"3\"/>\n <path d=\"M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 1 1-4 0v-.09a1.65 1.65 0 0 0-1-1.51 1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 1 1 0-4h.09a1.65 1.65 0 0 0 1.51-1 1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33h.01a1.65 1.65 0 0 0 1-1.51V3a2 2 0 1 1 4 0v.09a1.65 1.65 0 0 0 1 1.51h.01a1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82v.01a1.65 1.65 0 0 0 1.51 1H21a2 2 0 1 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z\"/>\n </svg>\n </button>");
42553
+ }
42554
+
41281
42555
  /**
41282
42556
  * Generate HTML for voice interface.
41283
42557
  * Automatically detects mobile vs desktop based on voiceInterface.isMobile
@@ -41308,7 +42582,7 @@ var Templates = /*#__PURE__*/function () {
41308
42582
  var transcriptPlaceholder = this.t('transcriptWillAppear') || '';
41309
42583
  var transcriptDir = (0,_rtlText_js__WEBPACK_IMPORTED_MODULE_0__.dirForTranscriptSnippet)(transcriptPlaceholder, this.config);
41310
42584
  var voiceInputDir = (0,_rtlText_js__WEBPACK_IMPORTED_MODULE_0__.dirForAssistantStreamingText)(this.config);
41311
- return "<div class=\"voice-interface\" id=\"voiceInterface\">\n <!-- Idle View -->\n <div id=\"voiceIdleState\">\n <div class=\"ttp-hero\">\n <div class=\"ttp-header\">\n <div class=\"ttp-agent\">\n <div class=\"ttp-avatar-wrap\">\n <div class=\"".concat(idleAvatarClass, "\">").concat(idleAvatarInner, "</div>\n <div class=\"ttp-online-dot\"></div>\n </div>\n <div>\n <div class=\"ttp-agent-name\">").concat(agentName, "</div>\n <div class=\"ttp-agent-role\">").concat(agentRole, "</div>\n </div>\n </div>\n <div class=\"ttp-header-actions\">\n ").concat(this.infoButton(), "\n <button type=\"button\" class=\"ttp-close-btn\" id=\"ttpCloseIdle\" aria-label=\"Close widget\">\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 12 12\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M10 2L2 10M2 2l8 8\"/>\n </svg>\n </button>\n </div>\n </div>\n <div class=\"ttp-headline\">").concat(headline, "</div>\n <div class=\"ttp-subline\">").concat(subline, "</div>\n </div>\n\n <div class=\"ttp-body\">\n <button type=\"button\" class=\"ttp-btn-primary\" id=\"startCallBtn\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"").concat(this.config.startCallButtonTextColor || 'white', "\" stroke-width=\"2\">\n <path d=\"M12 2a3 3 0 0 1 3 3v7a3 3 0 0 1-6 0V5a3 3 0 0 1 3-3z\"/>\n <path d=\"M19 10v2a7 7 0 0 1-14 0v-2M12 19v3M8 22h8\"/>\n </svg>\n ").concat(this.getText('startCallButtonText', 'startCall') || 'Start Voice Call', "\n </button>\n ").concat((((_this$config$behavior = this.config.behavior) === null || _this$config$behavior === void 0 ? void 0 : _this$config$behavior.mode) || 'unified') !== 'voice-only' ? "\n <button type=\"button\" class=\"ttp-btn-secondary\" id=\"startTextBtn\">\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\"/>\n </svg>\n ".concat(this.config.sendMessageText || this.getText('sendMessageText', 'sendMessage') || 'Send a Message', "\n </button>\n ") : '', "\n ").concat((_this$config$whatsapp = this.config.whatsapp) !== null && _this$config$whatsapp !== void 0 && _this$config$whatsapp.number ? "\n <a class=\"ttp-btn-wa\" id=\"ttpWhatsAppBtn\"\n href=\"https://wa.me/".concat(this.config.whatsapp.number).concat(this.config.whatsapp.text ? '?text=' + encodeURIComponent(this.config.whatsapp.text) : '', "\"\n target=\"_blank\" rel=\"noopener\">\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"#25d366\">\n <path d=\"M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.655-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413z\"/>\n </svg>\n WhatsApp\n </a>\n ") : '', "\n </div>\n </div>\n\n <!-- Active Call View -->\n <div id=\"voiceActiveState\" style=\"display: none;\">\n <!-- Full voice section (hidden when items/rooms are shown) -->\n <div class=\"desktop-voice-section\" id=\"desktopVoiceSection\">\n <div class=\"ttp-call-hero\">\n <div class=\"ttp-call-topbar\">\n <div class=\"ttp-rec\">\n <div class=\"ttp-rec-dot\"></div>\n <span class=\"ttp-timer\" id=\"desktopTimerText\">00:00</span>\n </div>\n <span class=\"ttp-call-label\">Voice call active</span>\n <div class=\"ttp-header-actions\">\n ").concat(this.infoButton(), "\n <button type=\"button\" class=\"ttp-close-btn\" id=\"ttpCloseActive\" aria-label=\"Close widget\">\n <svg width=\"9\" height=\"9\" viewBox=\"0 0 12 12\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M10 2L2 10M2 2l8 8\"/>\n </svg>\n </button>\n </div>\n </div>\n\n <div class=\"ttp-orb-wrap\">\n <div class=\"ttp-orb-rings\">\n <div class=\"ttp-ring ttp-ring-1\"></div>\n <div class=\"ttp-ring ttp-ring-2\"></div>\n <div class=\"ttp-orb\" id=\"desktopMainWaveform\">\n <div class=\"ttp-wave\" id=\"ttp-wave\"></div>\n </div>\n </div>\n <div class=\"ttp-listen-label\" id=\"desktopStatusText\">").concat(this.t('listening') || 'Listening to you...', "</div>\n </div>\n </div>\n\n <div class=\"ttp-controls\">\n <button type=\"button\" class=\"ttp-ctrl-btn\" id=\"desktopMuteBtn\" title=\"").concat(this.getTooltip('mute'), "\" aria-label=\"Mute microphone\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" class=\"desktop-mic-icon\">\n <path d=\"M12 1a3 3 0 0 1 3 3v7a3 3 0 0 1-6 0V4a3 3 0 0 1 3-3z\"/>\n <path d=\"M19 10v2a7 7 0 0 1-14 0v-2\"/>\n </svg>\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" class=\"desktop-mic-off-icon\" style=\"display: none; position: absolute;\">\n <path d=\"M12 1a3 3 0 0 1 3 3v7a3 3 0 0 1-6 0V4a3 3 0 0 1 3-3z\" stroke=\"currentColor\" stroke-width=\"1.8\" fill=\"none\"/>\n <path d=\"M19 10v2a7 7 0 0 1-14 0v-2\" stroke=\"currentColor\" stroke-width=\"1.8\" fill=\"none\"/>\n <line x1=\"5\" y1=\"5\" x2=\"19\" y2=\"19\" stroke=\"#ef4444\" stroke-width=\"1.5\" stroke-linecap=\"round\" opacity=\"0.85\"/>\n </svg>\n </button>\n <button type=\"button\" class=\"ttp-ctrl-btn\" id=\"desktopPauseBtn\" title=\"Pause call\" aria-label=\"Pause call\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" class=\"desktop-pause-icon\">\n <rect x=\"6\" y=\"4\" width=\"4\" height=\"16\" rx=\"1\"/>\n <rect x=\"14\" y=\"4\" width=\"4\" height=\"16\" rx=\"1\"/>\n </svg>\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\" stroke=\"none\" class=\"desktop-play-icon\" style=\"display: none; position: absolute;\">\n <polygon points=\"6,4 20,12 6,20\"/>\n </svg>\n </button>\n <button type=\"button\" class=\"ttp-ctrl-btn\" id=\"desktopSpeakerBtn\" title=\"").concat(this.getTooltip('speaker'), "\" aria-label=\"Toggle speaker\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" class=\"desktop-speaker-icon\">\n <polygon points=\"11 5 6 9 2 9 2 15 6 15 11 19 11 5\"/>\n <path d=\"M15.54 8.46a5 5 0 0 1 0 7.07\"/>\n </svg>\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" class=\"desktop-speaker-off-icon\" style=\"display: none; position: absolute;\">\n <polygon points=\"11 5 6 9 2 9 2 15 6 15 11 19 11 5\" stroke=\"currentColor\" stroke-width=\"1.8\" fill=\"none\"/>\n <path d=\"M15.54 8.46a5 5 0 0 1 0 7.07\" stroke=\"currentColor\" stroke-width=\"1.8\" fill=\"none\"/>\n <line x1=\"5\" y1=\"5\" x2=\"19\" y2=\"19\" stroke=\"#ef4444\" stroke-width=\"1.5\" stroke-linecap=\"round\" opacity=\"0.85\"/>\n </svg>\n </button>\n <button type=\"button\" class=\"ttp-end-btn\" id=\"desktopEndCallBtn\" title=\"").concat(this.getTooltip('endCall'), "\" aria-label=\"End call\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07A19.5 19.5 0 0 1 4.99 12 19.79 19.79 0 0 1 1.97 3.4 2 2 0 0 1 3.95 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z\"/>\n </svg>\n </button>\n </div>\n </div>\n\n <!-- Compact voice section (shown when items/rooms are visible) -->\n <div class=\"compact-voice-section\" id=\"compactVoiceSection\" style=\"display: none;\">\n <div class=\"compact-left\">\n <div class=\"compact-avatar\" id=\"desktopAvatarCompact\">\n <div class=\"ttp-orb compact-orb\" id=\"desktopMainWaveformCompact\">\n <div class=\"ttp-wave\"></div>\n </div>\n </div>\n <div class=\"compact-info\">\n <div class=\"compact-timer\" id=\"desktopTimerCompact\">\n <span class=\"ttp-rec-dot\"></span>\n <span id=\"compactTimerText\">00:00</span>\n </div>\n <div class=\"compact-status\" id=\"desktopStatusCompact\">\n <span id=\"compactStatusText\">").concat(this.t('listening') || 'Listening...', "</span>\n </div>\n </div>\n </div>\n <div class=\"compact-controls\">\n ").concat(this.infoButton('ttp-info-btn--onctrl'), "\n <button type=\"button\" class=\"compact-control-btn secondary\" id=\"desktopMuteBtnCompact\" title=\"").concat(this.getTooltip('mute'), "\" aria-label=\"Mute microphone\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" class=\"desktop-mic-icon\">\n <path d=\"M12 1a3 3 0 0 1 3 3v7a3 3 0 0 1-6 0V4a3 3 0 0 1 3-3z\"/>\n <path d=\"M19 10v2a7 7 0 0 1-14 0v-2\"/>\n </svg>\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" class=\"desktop-mic-off-icon\" style=\"display: none; position: absolute;\">\n <path d=\"M12 1a3 3 0 0 1 3 3v7a3 3 0 0 1-6 0V4a3 3 0 0 1 3-3z\" stroke=\"currentColor\" stroke-width=\"1.8\" fill=\"none\"/>\n <path d=\"M19 10v2a7 7 0 0 1-14 0v-2\" stroke=\"currentColor\" stroke-width=\"1.8\" fill=\"none\"/>\n <line x1=\"5\" y1=\"5\" x2=\"19\" y2=\"19\" stroke=\"#ef4444\" stroke-width=\"1.5\" stroke-linecap=\"round\" opacity=\"0.85\"/>\n </svg>\n </button>\n <button type=\"button\" class=\"compact-control-btn secondary\" id=\"desktopPauseBtnCompact\" title=\"Pause call\" aria-label=\"Pause call\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" class=\"desktop-pause-icon\">\n <rect x=\"6\" y=\"4\" width=\"4\" height=\"16\" rx=\"1\"/>\n <rect x=\"14\" y=\"4\" width=\"4\" height=\"16\" rx=\"1\"/>\n </svg>\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\" stroke=\"none\" class=\"desktop-play-icon\" style=\"display: none; position: absolute;\">\n <polygon points=\"6,4 20,12 6,20\"/>\n </svg>\n </button>\n <button type=\"button\" class=\"compact-control-btn secondary\" id=\"desktopSpeakerBtnCompact\" title=\"").concat(this.getTooltip('speaker'), "\" aria-label=\"Toggle speaker\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" class=\"desktop-speaker-icon\">\n <polygon points=\"11 5 6 9 2 9 2 15 6 15 11 19 11 5\"/>\n <path d=\"M15.54 8.46a5 5 0 0 1 0 7.07\"/>\n </svg>\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" class=\"desktop-speaker-off-icon\" style=\"display: none; position: absolute;\">\n <polygon points=\"11 5 6 9 2 9 2 15 6 15 11 19 11 5\" stroke=\"currentColor\" stroke-width=\"1.8\" fill=\"none\"/>\n <path d=\"M15.54 8.46a5 5 0 0 1 0 7.07\" stroke=\"currentColor\" stroke-width=\"1.8\" fill=\"none\"/>\n <line x1=\"5\" y1=\"5\" x2=\"19\" y2=\"19\" stroke=\"#ef4444\" stroke-width=\"1.5\" stroke-linecap=\"round\" opacity=\"0.85\"/>\n </svg>\n </button>\n <button type=\"button\" class=\"compact-control-btn danger\" id=\"desktopEndCallBtnCompact\" title=\"").concat(this.getTooltip('endCall'), "\" aria-label=\"End call\">\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" style=\"overflow: visible;\">\n <path d=\"M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07A19.5 19.5 0 0 1 4.99 12 19.79 19.79 0 0 1 1.97 3.4 2 2 0 0 1 3.95 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z\" transform=\"rotate(135 12 12)\"/>\n </svg>\n </button>\n </div>\n </div>\n\n <!-- Transcript -->\n <div class=\"ttp-transcript desktop-conversation-panel\" id=\"desktopConversationPanel\" dir=\"").concat(textDir, "\">\n <div class=\"ttp-transcript-hdr\" id=\"conversationHeader\">\n <span class=\"ttp-live-label\">Live</span>\n <span class=\"ttp-toggle-text\" id=\"conversationToggleText\">Show history</span>\n <div class=\"ttp-dots\">\n <div class=\"ttp-dot\"></div>\n <div class=\"ttp-dot\"></div>\n <div class=\"ttp-dot\"></div>\n </div>\n </div>\n <div class=\"live-transcript-collapsed\" id=\"liveTranscriptCollapsed\">\n <div class=\"ttp-transcript-text\" id=\"liveTextCollapsed\" dir=\"").concat(transcriptDir, "\">\n ").concat(this.t('transcriptWillAppear') || 'Transcript will appear here...', "<span class=\"ttp-cursor\"></span>\n </div>\n </div>\n <div class=\"conversation-history\" id=\"conversationHistory\">\n <div class=\"conversation-messages\" id=\"conversationMessages\"></div>\n <div class=\"live-message-row\" id=\"liveMessageRow\" style=\"display: none;\">\n <div class=\"message-bubble\" dir=\"").concat(transcriptDir, "\">\n <span class=\"live-badge\">Live</span>\n <span id=\"liveMessageText\"></span><span class=\"ttp-cursor\"></span>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Type row -->\n <div class=\"ttp-type-row voice-text-input-area\">\n <input class=\"ttp-input\" type=\"text\" id=\"voiceTextInput\" dir=\"").concat(voiceInputDir, "\" placeholder=\"").concat(this.getText('inputPlaceholder', 'typeMessage') || 'Or type here…', "\" aria-label=\"Type a message\" />\n <button type=\"button\" class=\"ttp-send-btn\" id=\"voiceSendBtn\" aria-label=\"Send message\">\n <svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"white\" stroke-width=\"2\">\n <line x1=\"22\" y1=\"2\" x2=\"11\" y2=\"13\"/>\n <polygon points=\"22 2 15 22 11 13 2 9 22 2\"/>\n </svg>\n </button>\n </div>\n </div>\n </div>");
42585
+ return "<div class=\"voice-interface\" id=\"voiceInterface\">\n <!-- Idle View -->\n <div id=\"voiceIdleState\">\n <div class=\"ttp-hero\">\n <div class=\"ttp-header\">\n <div class=\"ttp-agent\">\n <div class=\"ttp-avatar-wrap\">\n <div class=\"".concat(idleAvatarClass, "\">").concat(idleAvatarInner, "</div>\n <div class=\"ttp-online-dot\"></div>\n </div>\n <div>\n <div class=\"ttp-agent-name\">").concat(agentName, "</div>\n <div class=\"ttp-agent-role\">").concat(agentRole, "</div>\n </div>\n </div>\n <div class=\"ttp-header-actions\">\n ").concat(this.infoButton(), "\n <button type=\"button\" class=\"ttp-close-btn\" id=\"ttpCloseIdle\" aria-label=\"Close widget\">\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 12 12\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M10 2L2 10M2 2l8 8\"/>\n </svg>\n </button>\n </div>\n </div>\n <div class=\"ttp-headline\">").concat(headline, "</div>\n <div class=\"ttp-subline\">").concat(subline, "</div>\n </div>\n\n <div class=\"ttp-body\">\n <button type=\"button\" class=\"ttp-btn-primary\" id=\"startCallBtn\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"").concat(this.config.startCallButtonTextColor || 'white', "\" stroke-width=\"2\">\n <path d=\"M12 2a3 3 0 0 1 3 3v7a3 3 0 0 1-6 0V5a3 3 0 0 1 3-3z\"/>\n <path d=\"M19 10v2a7 7 0 0 1-14 0v-2M12 19v3M8 22h8\"/>\n </svg>\n ").concat(this.getText('startCallButtonText', 'startCall') || 'Start Voice Call', "\n </button>\n ").concat((((_this$config$behavior = this.config.behavior) === null || _this$config$behavior === void 0 ? void 0 : _this$config$behavior.mode) || 'unified') !== 'voice-only' ? "\n <button type=\"button\" class=\"ttp-btn-secondary\" id=\"startTextBtn\">\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\"/>\n </svg>\n ".concat(this.config.sendMessageText || this.getText('sendMessageText', 'sendMessage') || 'Send a Message', "\n </button>\n ") : '', "\n ").concat((_this$config$whatsapp = this.config.whatsapp) !== null && _this$config$whatsapp !== void 0 && _this$config$whatsapp.number ? "\n <a class=\"ttp-btn-wa\" id=\"ttpWhatsAppBtn\"\n href=\"https://wa.me/".concat(this.config.whatsapp.number).concat(this.config.whatsapp.text ? '?text=' + encodeURIComponent(this.config.whatsapp.text) : '', "\"\n target=\"_blank\" rel=\"noopener\">\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"#25d366\">\n <path d=\"M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.655-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413z\"/>\n </svg>\n WhatsApp\n </a>\n ") : '', "\n </div>\n </div>\n\n <!-- Active Call View -->\n <div id=\"voiceActiveState\" style=\"display: none;\">\n <!-- Full voice section (hidden when items/rooms are shown) -->\n <div class=\"desktop-voice-section\" id=\"desktopVoiceSection\">\n <div class=\"ttp-call-hero\">\n <div class=\"ttp-call-topbar\">\n <div class=\"ttp-rec\">\n <div class=\"ttp-rec-dot\"></div>\n <span class=\"ttp-timer\" id=\"desktopTimerText\">00:00</span>\n </div>\n <span class=\"ttp-call-label\">Voice call active</span>\n <div class=\"ttp-header-actions\">\n ").concat(this.settingsButton(), "\n ").concat(this.infoButton(), "\n <button type=\"button\" class=\"ttp-close-btn\" id=\"ttpCloseActive\" aria-label=\"Close widget\">\n <svg width=\"9\" height=\"9\" viewBox=\"0 0 12 12\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M10 2L2 10M2 2l8 8\"/>\n </svg>\n </button>\n </div>\n </div>\n\n <div class=\"ttp-orb-wrap\">\n <div class=\"ttp-orb-rings\">\n <div class=\"ttp-ring ttp-ring-1\"></div>\n <div class=\"ttp-ring ttp-ring-2\"></div>\n <div class=\"ttp-orb\" id=\"desktopMainWaveform\">\n <div class=\"ttp-wave\" id=\"ttp-wave\"></div>\n </div>\n </div>\n <div class=\"ttp-listen-label\" id=\"desktopStatusText\">").concat(this.t('listening') || 'Listening to you...', "</div>\n </div>\n </div>\n\n <div class=\"ttp-controls\">\n <button type=\"button\" class=\"ttp-ctrl-btn\" id=\"desktopMuteBtn\" title=\"").concat(this.getTooltip('mute'), "\" aria-label=\"Mute microphone\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" class=\"desktop-mic-icon\">\n <path d=\"M12 1a3 3 0 0 1 3 3v7a3 3 0 0 1-6 0V4a3 3 0 0 1 3-3z\"/>\n <path d=\"M19 10v2a7 7 0 0 1-14 0v-2\"/>\n </svg>\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" class=\"desktop-mic-off-icon\" style=\"display: none; position: absolute;\">\n <path d=\"M12 1a3 3 0 0 1 3 3v7a3 3 0 0 1-6 0V4a3 3 0 0 1 3-3z\" stroke=\"currentColor\" stroke-width=\"1.8\" fill=\"none\"/>\n <path d=\"M19 10v2a7 7 0 0 1-14 0v-2\" stroke=\"currentColor\" stroke-width=\"1.8\" fill=\"none\"/>\n <line x1=\"5\" y1=\"5\" x2=\"19\" y2=\"19\" stroke=\"#ef4444\" stroke-width=\"1.5\" stroke-linecap=\"round\" opacity=\"0.85\"/>\n </svg>\n </button>\n <button type=\"button\" class=\"ttp-ctrl-btn\" id=\"desktopPauseBtn\" title=\"Pause call\" aria-label=\"Pause call\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" class=\"desktop-pause-icon\">\n <rect x=\"6\" y=\"4\" width=\"4\" height=\"16\" rx=\"1\"/>\n <rect x=\"14\" y=\"4\" width=\"4\" height=\"16\" rx=\"1\"/>\n </svg>\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\" stroke=\"none\" class=\"desktop-play-icon\" style=\"display: none; position: absolute;\">\n <polygon points=\"6,4 20,12 6,20\"/>\n </svg>\n </button>\n <button type=\"button\" class=\"ttp-ctrl-btn\" id=\"desktopSpeakerBtn\" title=\"").concat(this.getTooltip('speaker'), "\" aria-label=\"Toggle speaker\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" class=\"desktop-speaker-icon\">\n <polygon points=\"11 5 6 9 2 9 2 15 6 15 11 19 11 5\"/>\n <path d=\"M15.54 8.46a5 5 0 0 1 0 7.07\"/>\n </svg>\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" class=\"desktop-speaker-off-icon\" style=\"display: none; position: absolute;\">\n <polygon points=\"11 5 6 9 2 9 2 15 6 15 11 19 11 5\" stroke=\"currentColor\" stroke-width=\"1.8\" fill=\"none\"/>\n <path d=\"M15.54 8.46a5 5 0 0 1 0 7.07\" stroke=\"currentColor\" stroke-width=\"1.8\" fill=\"none\"/>\n <line x1=\"5\" y1=\"5\" x2=\"19\" y2=\"19\" stroke=\"#ef4444\" stroke-width=\"1.5\" stroke-linecap=\"round\" opacity=\"0.85\"/>\n </svg>\n </button>\n <button type=\"button\" class=\"ttp-end-btn\" id=\"desktopEndCallBtn\" title=\"").concat(this.getTooltip('endCall'), "\" aria-label=\"End call\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07A19.5 19.5 0 0 1 4.99 12 19.79 19.79 0 0 1 1.97 3.4 2 2 0 0 1 3.95 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z\"/>\n </svg>\n </button>\n </div>\n </div>\n\n <!-- Compact voice section (shown when items/rooms are visible) -->\n <div class=\"compact-voice-section\" id=\"compactVoiceSection\" style=\"display: none;\">\n <div class=\"compact-left\">\n <div class=\"compact-avatar\" id=\"desktopAvatarCompact\">\n <div class=\"ttp-orb compact-orb\" id=\"desktopMainWaveformCompact\">\n <div class=\"ttp-wave\"></div>\n </div>\n </div>\n <div class=\"compact-info\">\n <div class=\"compact-timer\" id=\"desktopTimerCompact\">\n <span class=\"ttp-rec-dot\"></span>\n <span id=\"compactTimerText\">00:00</span>\n </div>\n <div class=\"compact-status\" id=\"desktopStatusCompact\">\n <span id=\"compactStatusText\">").concat(this.t('listening') || 'Listening...', "</span>\n </div>\n </div>\n </div>\n <div class=\"compact-controls\">\n ").concat(this.settingsButton('ttp-info-btn--onctrl'), "\n ").concat(this.infoButton('ttp-info-btn--onctrl'), "\n <button type=\"button\" class=\"compact-control-btn secondary\" id=\"desktopMuteBtnCompact\" title=\"").concat(this.getTooltip('mute'), "\" aria-label=\"Mute microphone\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" class=\"desktop-mic-icon\">\n <path d=\"M12 1a3 3 0 0 1 3 3v7a3 3 0 0 1-6 0V4a3 3 0 0 1 3-3z\"/>\n <path d=\"M19 10v2a7 7 0 0 1-14 0v-2\"/>\n </svg>\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" class=\"desktop-mic-off-icon\" style=\"display: none; position: absolute;\">\n <path d=\"M12 1a3 3 0 0 1 3 3v7a3 3 0 0 1-6 0V4a3 3 0 0 1 3-3z\" stroke=\"currentColor\" stroke-width=\"1.8\" fill=\"none\"/>\n <path d=\"M19 10v2a7 7 0 0 1-14 0v-2\" stroke=\"currentColor\" stroke-width=\"1.8\" fill=\"none\"/>\n <line x1=\"5\" y1=\"5\" x2=\"19\" y2=\"19\" stroke=\"#ef4444\" stroke-width=\"1.5\" stroke-linecap=\"round\" opacity=\"0.85\"/>\n </svg>\n </button>\n <button type=\"button\" class=\"compact-control-btn secondary\" id=\"desktopPauseBtnCompact\" title=\"Pause call\" aria-label=\"Pause call\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" class=\"desktop-pause-icon\">\n <rect x=\"6\" y=\"4\" width=\"4\" height=\"16\" rx=\"1\"/>\n <rect x=\"14\" y=\"4\" width=\"4\" height=\"16\" rx=\"1\"/>\n </svg>\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\" stroke=\"none\" class=\"desktop-play-icon\" style=\"display: none; position: absolute;\">\n <polygon points=\"6,4 20,12 6,20\"/>\n </svg>\n </button>\n <button type=\"button\" class=\"compact-control-btn secondary\" id=\"desktopSpeakerBtnCompact\" title=\"").concat(this.getTooltip('speaker'), "\" aria-label=\"Toggle speaker\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" class=\"desktop-speaker-icon\">\n <polygon points=\"11 5 6 9 2 9 2 15 6 15 11 19 11 5\"/>\n <path d=\"M15.54 8.46a5 5 0 0 1 0 7.07\"/>\n </svg>\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" class=\"desktop-speaker-off-icon\" style=\"display: none; position: absolute;\">\n <polygon points=\"11 5 6 9 2 9 2 15 6 15 11 19 11 5\" stroke=\"currentColor\" stroke-width=\"1.8\" fill=\"none\"/>\n <path d=\"M15.54 8.46a5 5 0 0 1 0 7.07\" stroke=\"currentColor\" stroke-width=\"1.8\" fill=\"none\"/>\n <line x1=\"5\" y1=\"5\" x2=\"19\" y2=\"19\" stroke=\"#ef4444\" stroke-width=\"1.5\" stroke-linecap=\"round\" opacity=\"0.85\"/>\n </svg>\n </button>\n <button type=\"button\" class=\"compact-control-btn danger\" id=\"desktopEndCallBtnCompact\" title=\"").concat(this.getTooltip('endCall'), "\" aria-label=\"End call\">\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" style=\"overflow: visible;\">\n <path d=\"M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07A19.5 19.5 0 0 1 4.99 12 19.79 19.79 0 0 1 1.97 3.4 2 2 0 0 1 3.95 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z\" transform=\"rotate(135 12 12)\"/>\n </svg>\n </button>\n </div>\n </div>\n\n <!-- Transcript -->\n <div class=\"ttp-transcript desktop-conversation-panel\" id=\"desktopConversationPanel\" dir=\"").concat(textDir, "\">\n <div class=\"ttp-transcript-hdr\" id=\"conversationHeader\">\n <span class=\"ttp-live-label\">Live</span>\n <span class=\"ttp-toggle-text\" id=\"conversationToggleText\">Show history</span>\n <div class=\"ttp-dots\">\n <div class=\"ttp-dot\"></div>\n <div class=\"ttp-dot\"></div>\n <div class=\"ttp-dot\"></div>\n </div>\n </div>\n <div class=\"live-transcript-collapsed\" id=\"liveTranscriptCollapsed\">\n <div class=\"ttp-transcript-text\" id=\"liveTextCollapsed\" dir=\"").concat(transcriptDir, "\">\n ").concat(this.t('transcriptWillAppear') || 'Transcript will appear here...', "<span class=\"ttp-cursor\"></span>\n </div>\n </div>\n <div class=\"conversation-history\" id=\"conversationHistory\">\n <div class=\"conversation-messages\" id=\"conversationMessages\"></div>\n <div class=\"live-message-row\" id=\"liveMessageRow\" style=\"display: none;\">\n <div class=\"message-bubble\" dir=\"").concat(transcriptDir, "\">\n <span class=\"live-badge\">Live</span>\n <span id=\"liveMessageText\"></span><span class=\"ttp-cursor\"></span>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Type row -->\n <div class=\"ttp-type-row voice-text-input-area\">\n <input class=\"ttp-input\" type=\"text\" id=\"voiceTextInput\" dir=\"").concat(voiceInputDir, "\" placeholder=\"").concat(this.getText('inputPlaceholder', 'typeMessage') || 'Or type here…', "\" aria-label=\"Type a message\" />\n <button type=\"button\" class=\"ttp-send-btn\" id=\"voiceSendBtn\" aria-label=\"Send message\">\n <svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"white\" stroke-width=\"2\">\n <line x1=\"22\" y1=\"2\" x2=\"11\" y2=\"13\"/>\n <polygon points=\"22 2 15 22 11 13 2 9 22 2\"/>\n </svg>\n </button>\n </div>\n </div>\n </div>");
41312
42586
  }
41313
42587
 
41314
42588
  /**
@@ -41325,7 +42599,7 @@ var Templates = /*#__PURE__*/function () {
41325
42599
  var headerAvatarUrl = resolveIdleHeaderAvatarUrl(this.config);
41326
42600
  var mobileAvatarInner = headerAvatarUrl ? "<img src=\"".concat(escapeHtmlAttr(headerAvatarUrl), "\" alt=\"\" class=\"voice-avatar-img\" />") : '🤖';
41327
42601
  var mobileAvatarClass = headerAvatarUrl ? 'voice-avatar voice-avatar--image' : 'voice-avatar';
41328
- return "<div class=\"voice-interface mobile-voice-interface\" id=\"voiceInterface\" dir=\"".concat(textDir, "\">\n <!-- Before Call State (same as desktop) -->\n <div id=\"voiceIdleState\">\n ").concat(this.infoButton('ttp-info-btn--floating'), "\n <div class=\"").concat(mobileAvatarClass, "\" id=\"voiceAvatar\">").concat(mobileAvatarInner, "</div>\n <div class=\"voice-status\">\n <div class=\"voice-status-title\">").concat(this.getText('startCallTitle', 'clickToStartCall'), "</div>\n <div class=\"voice-status-subtitle\">").concat(this.getText('startCallSubtitle', 'realTimeVoice'), "</div>\n </div>\n <button type=\"button\" class=\"start-call-btn\" id=\"startCallBtn\">\n <svg width=\"32\" height=\"32\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M20.01 15.38c-1.23 0-2.42-.2-3.53-.56-.35-.12-.74-.03-1.01.24l-1.57 1.97c-2.83-1.35-5.48-3.9-6.89-6.83l1.95-1.66c.27-.28.35-.67.24-1.02-.37-1.11-.56-2.3-.56-3.53 0-.54-.45-.99-.99-.99H4.19C3.65 3 3 3.24 3 3.99 3 13.28 10.73 21 20.01 21c.71 0 .99-.63.99-1.18v-3.45c0-.54-.45-.99-.99-.99z\"/>\n </svg>\n <span>").concat(this.getText('startCallButtonText', 'startCall'), "</span>\n </button>\n </div>\n \n <!-- Mobile Active Call State - Hidden placeholder (actual bar will be appended to body) -->\n <div id=\"voiceActiveState\" class=\"mobile-active-state\" style=\"display: none;\">\n <!-- Duration Badge -->\n <div class=\"mobile-duration-badge\" id=\"mobileDurationBadge\">\n <div class=\"mobile-duration-dot\"></div>\n <span id=\"mobileDurationText\">00:00</span>\n ").concat(this.infoButton('ttp-info-btn--onbadge'), "\n </div>\n\n <!-- Main Bar -->\n <div class=\"mobile-voice-bar\">\n <!-- Top Row: Status, Waveform, Controls -->\n <div class=\"mobile-top-row\">\n <!-- Status Indicator -->\n <div class=\"mobile-status-indicator\">\n <div class=\"mobile-status-dot\" id=\"mobileStatusDot\"></div>\n <span class=\"mobile-status-text\" id=\"mobileStatusText\">Listening...</span>\n </div>\n \n <!-- Centered Waveform -->\n <div class=\"mobile-waveform-center\" id=\"mobileWaveform\">\n ").concat(Array(7).fill(0).map(function (_, i) {
42602
+ return "<div class=\"voice-interface mobile-voice-interface\" id=\"voiceInterface\" dir=\"".concat(textDir, "\">\n <!-- Before Call State (same as desktop) -->\n <div id=\"voiceIdleState\">\n ").concat(this.infoButton('ttp-info-btn--floating'), "\n <div class=\"").concat(mobileAvatarClass, "\" id=\"voiceAvatar\">").concat(mobileAvatarInner, "</div>\n <div class=\"voice-status\">\n <div class=\"voice-status-title\">").concat(this.getText('startCallTitle', 'clickToStartCall'), "</div>\n <div class=\"voice-status-subtitle\">").concat(this.getText('startCallSubtitle', 'realTimeVoice'), "</div>\n </div>\n <button type=\"button\" class=\"start-call-btn\" id=\"startCallBtn\">\n <svg width=\"32\" height=\"32\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M20.01 15.38c-1.23 0-2.42-.2-3.53-.56-.35-.12-.74-.03-1.01.24l-1.57 1.97c-2.83-1.35-5.48-3.9-6.89-6.83l1.95-1.66c.27-.28.35-.67.24-1.02-.37-1.11-.56-2.3-.56-3.53 0-.54-.45-.99-.99-.99H4.19C3.65 3 3 3.24 3 3.99 3 13.28 10.73 21 20.01 21c.71 0 .99-.63.99-1.18v-3.45c0-.54-.45-.99-.99-.99z\"/>\n </svg>\n <span>").concat(this.getText('startCallButtonText', 'startCall'), "</span>\n </button>\n </div>\n \n <!-- Mobile Active Call State - Hidden placeholder (actual bar will be appended to body) -->\n <div id=\"voiceActiveState\" class=\"mobile-active-state\" style=\"display: none;\">\n <!-- Duration Badge -->\n <div class=\"mobile-duration-badge\" id=\"mobileDurationBadge\">\n <div class=\"mobile-duration-dot\"></div>\n <span id=\"mobileDurationText\">00:00</span>\n ").concat(this.settingsButton('ttp-info-btn--onbadge'), "\n ").concat(this.infoButton('ttp-info-btn--onbadge'), "\n </div>\n\n <!-- Main Bar -->\n <div class=\"mobile-voice-bar\">\n <!-- Top Row: Status, Waveform, Controls -->\n <div class=\"mobile-top-row\">\n <!-- Status Indicator -->\n <div class=\"mobile-status-indicator\">\n <div class=\"mobile-status-dot\" id=\"mobileStatusDot\"></div>\n <span class=\"mobile-status-text\" id=\"mobileStatusText\">Listening...</span>\n </div>\n \n <!-- Centered Waveform -->\n <div class=\"mobile-waveform-center\" id=\"mobileWaveform\">\n ").concat(Array(7).fill(0).map(function (_, i) {
41329
42603
  return "<div class=\"mobile-waveform-bar\" data-index=\"".concat(i, "\"></div>");
41330
42604
  }).join(''), "\n </div>\n \n <!-- Controls -->\n <div class=\"mobile-controls\">\n <button type=\"button\" class=\"mobile-control-btn\" id=\"mobileMuteBtn\" aria-label=\"Mute microphone\">\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"mobile-mic-icon\">\n <path d=\"M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z\"/>\n <path d=\"M19 10v2a7 7 0 0 1-14 0v-2\"/>\n <line x1=\"12\" x2=\"12\" y1=\"19\" y2=\"22\"/>\n </svg>\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"mobile-mic-off-icon\" style=\"display: none;\">\n <path d=\"M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z\"/>\n <path d=\"M19 10v2a7 7 0 0 1-14 0v-2\"/>\n <line x1=\"12\" x2=\"12\" y1=\"19\" y2=\"22\"/>\n <line x1=\"4\" y1=\"4\" x2=\"20\" y2=\"20\" stroke=\"#ef4444\" stroke-width=\"1.5\" opacity=\"0.85\"/>\n </svg>\n </button>\n \n <button type=\"button\" class=\"mobile-control-btn\" id=\"mobilePauseBtn\" aria-label=\"Pause call\">\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\" class=\"mobile-pause-icon\">\n <rect x=\"6\" y=\"4\" width=\"4\" height=\"16\" rx=\"1\"/>\n <rect x=\"14\" y=\"4\" width=\"4\" height=\"16\" rx=\"1\"/>\n </svg>\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"#fff\" stroke=\"none\" class=\"mobile-play-icon\" style=\"display: none;\">\n <polygon points=\"6,4 20,12 6,20\"/>\n </svg>\n </button>\n\n <button type=\"button\" class=\"mobile-control-btn\" id=\"mobileSpeakerBtn\" aria-label=\"Toggle speaker\">\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"mobile-speaker-icon\">\n <polygon points=\"11 5 6 9 2 9 2 15 6 15 11 19 11 5\"/>\n <path d=\"M15.54 8.46a5 5 0 0 1 0 7.07\"/>\n </svg>\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"mobile-speaker-off-icon\" style=\"display: none;\">\n <polygon points=\"11 5 6 9 2 9 2 15 6 15 11 19 11 5\"/>\n <path d=\"M15.54 8.46a5 5 0 0 1 0 7.07\"/>\n <line x1=\"4\" y1=\"4\" x2=\"20\" y2=\"20\" stroke=\"#ef4444\" stroke-width=\"1.5\" opacity=\"0.85\"/>\n </svg>\n </button>\n\n <button type=\"button\" class=\"mobile-end-call-btn\" id=\"mobileEndCallBtn\" aria-label=\"End call\">\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M10.68 13.31a16 16 0 0 0 3.41 2.6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7 2 2 0 0 1 1.72 2v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.42 19.42 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.63A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91\"/>\n <line x1=\"22\" x2=\"2\" y1=\"2\" y2=\"22\"/>\n </svg>\n </button>\n </div>\n </div>\n \n <!-- Bottom Row: Transcript (expandable) -->\n <button type=\"button\" class=\"mobile-transcript-row\" id=\"mobileTranscriptRow\">\n <p class=\"mobile-transcript-text\" id=\"mobileTranscriptText\" dir=\"").concat(transcriptDir, "\">").concat(this.t('transcriptWillAppear'), "</p>\n <div class=\"mobile-transcript-footer\">\n <span class=\"mobile-expand-hint\">tap to expand & type</span>\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\"opacity: 0.6;\">\n <path d=\"M7 17L17 7M17 7H7M17 7V17\"/>\n </svg>\n </div>\n </button>\n </div>\n </div>\n \n <!-- Expanded Transcript Overlay -->\n <div class=\"mobile-transcript-overlay\" id=\"mobileTranscriptOverlay\">\n <div class=\"mobile-expanded-transcript\" id=\"mobileExpandedTranscript\">\n <!-- Header -->\n <div class=\"mobile-transcript-header\">\n <div class=\"mobile-header-left\">\n <div class=\"mobile-header-status\">\n <div class=\"mobile-header-status-dot\"></div>\n <span class=\"mobile-header-status-text\" id=\"mobileHeaderStatusText\">Listening...</span>\n <span class=\"mobile-header-duration\" id=\"mobileHeaderDuration\">00:00</span>\n </div>\n <span class=\"mobile-transcript-label\">CONVERSATION</span>\n </div>\n <button type=\"button\" class=\"mobile-close-transcript-btn\" id=\"mobileCloseTranscriptBtn\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M18 6L6 18M6 6l12 12\"/>\n </svg>\n </button>\n </div>\n \n <!-- Messages Container -->\n <div class=\"mobile-messages-container\" id=\"mobileMessagesContainer\">\n <!-- Messages will be dynamically added here -->\n </div>\n \n <!-- Text Input Area -->\n <div class=\"mobile-input-area\">\n <div class=\"mobile-input-wrapper\">\n <input\n type=\"text\"\n placeholder=\"Type a message...\"\n id=\"mobileTextInput\"\n class=\"mobile-text-input\"\n dir=\"").concat(voiceInputDir, "\"\n />\n <button type=\"button\" class=\"mobile-send-btn\" id=\"mobileSendBtn\" disabled>\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M22 2L11 13M22 2l-7 20-4-9-9-4 20-7z\"/>\n </svg>\n </button>\n </div>\n <p class=\"mobile-input-hint\">\uD83C\uDF99\uFE0F Voice is still active \u2022 Type or speak</p>\n </div>\n </div>\n </div>\n </div>");
41331
42605
  }
@@ -42174,7 +43448,14 @@ __webpack_require__.r(__webpack_exports__);
42174
43448
  "aboutTitle": "About",
42175
43449
  "aboutVersion": "SDK version",
42176
43450
  "aboutConversationId": "Last conversation ID",
42177
- "aboutNoConversation": "No conversation yet"
43451
+ "aboutNoConversation": "No conversation yet",
43452
+ "settingsTitle": "Call settings",
43453
+ "settingsVoiceSpeed": "Voice speed",
43454
+ "settingsResponseLength": "Response length",
43455
+ "lengthDetailed": "Detailed",
43456
+ "lengthNormal": "Normal",
43457
+ "lengthBrief": "Brief",
43458
+ "lengthVeryBrief": "Very brief"
42178
43459
  },
42179
43460
  "he": {
42180
43461
  "landingTitle": "איך תרצה לתקשר?",
@@ -42211,7 +43492,14 @@ __webpack_require__.r(__webpack_exports__);
42211
43492
  "aboutTitle": "אודות",
42212
43493
  "aboutVersion": "גרסת SDK",
42213
43494
  "aboutConversationId": "מזהה שיחה אחרון",
42214
- "aboutNoConversation": "אין שיחה עדיין"
43495
+ "aboutNoConversation": "אין שיחה עדיין",
43496
+ "settingsTitle": "הגדרות שיחה",
43497
+ "settingsVoiceSpeed": "מהירות דיבור",
43498
+ "settingsResponseLength": "אורך תשובות",
43499
+ "lengthDetailed": "מפורט",
43500
+ "lengthNormal": "רגיל",
43501
+ "lengthBrief": "קצר",
43502
+ "lengthVeryBrief": "קצר מאוד"
42215
43503
  },
42216
43504
  "ar": {
42217
43505
  "landingTitle": "كيف تريد التواصل؟",
@@ -42248,7 +43536,14 @@ __webpack_require__.r(__webpack_exports__);
42248
43536
  "aboutTitle": "حول",
42249
43537
  "aboutVersion": "إصدار SDK",
42250
43538
  "aboutConversationId": "معرّف آخر محادثة",
42251
- "aboutNoConversation": "لا توجد محادثة بعد"
43539
+ "aboutNoConversation": "لا توجد محادثة بعد",
43540
+ "settingsTitle": "إعدادات المكالمة",
43541
+ "settingsVoiceSpeed": "سرعة الصوت",
43542
+ "settingsResponseLength": "طول الإجابات",
43543
+ "lengthDetailed": "مفصّل",
43544
+ "lengthNormal": "عادي",
43545
+ "lengthBrief": "مختصر",
43546
+ "lengthVeryBrief": "مختصر جداً"
42252
43547
  },
42253
43548
  "ru": {
42254
43549
  "landingTitle": "Как вы хотите общаться?",
@@ -42285,7 +43580,14 @@ __webpack_require__.r(__webpack_exports__);
42285
43580
  "aboutTitle": "О приложении",
42286
43581
  "aboutVersion": "Версия SDK",
42287
43582
  "aboutConversationId": "ID последнего разговора",
42288
- "aboutNoConversation": "Разговоров пока нет"
43583
+ "aboutNoConversation": "Разговоров пока нет",
43584
+ "settingsTitle": "Настройки звонка",
43585
+ "settingsVoiceSpeed": "Скорость речи",
43586
+ "settingsResponseLength": "Длина ответов",
43587
+ "lengthDetailed": "Подробно",
43588
+ "lengthNormal": "Обычно",
43589
+ "lengthBrief": "Кратко",
43590
+ "lengthVeryBrief": "Очень кратко"
42289
43591
  },
42290
43592
  "es": {
42291
43593
  "landingTitle": "¿Cómo te gustaría comunicarte?",
@@ -42322,7 +43624,14 @@ __webpack_require__.r(__webpack_exports__);
42322
43624
  "aboutTitle": "Acerca de",
42323
43625
  "aboutVersion": "Versión del SDK",
42324
43626
  "aboutConversationId": "ID de última conversación",
42325
- "aboutNoConversation": "Aún no hay conversación"
43627
+ "aboutNoConversation": "Aún no hay conversación",
43628
+ "settingsTitle": "Ajustes de llamada",
43629
+ "settingsVoiceSpeed": "Velocidad de voz",
43630
+ "settingsResponseLength": "Longitud de respuesta",
43631
+ "lengthDetailed": "Detallada",
43632
+ "lengthNormal": "Normal",
43633
+ "lengthBrief": "Breve",
43634
+ "lengthVeryBrief": "Muy breve"
42326
43635
  },
42327
43636
  "fr": {
42328
43637
  "landingTitle": "Comment souhaitez-vous communiquer?",
@@ -42359,7 +43668,14 @@ __webpack_require__.r(__webpack_exports__);
42359
43668
  "aboutTitle": "À propos",
42360
43669
  "aboutVersion": "Version du SDK",
42361
43670
  "aboutConversationId": "ID de la dernière conversation",
42362
- "aboutNoConversation": "Aucune conversation pour l'instant"
43671
+ "aboutNoConversation": "Aucune conversation pour l'instant",
43672
+ "settingsTitle": "Paramètres d'appel",
43673
+ "settingsVoiceSpeed": "Vitesse de la voix",
43674
+ "settingsResponseLength": "Longueur des réponses",
43675
+ "lengthDetailed": "Détaillée",
43676
+ "lengthNormal": "Normale",
43677
+ "lengthBrief": "Brève",
43678
+ "lengthVeryBrief": "Très brève"
42363
43679
  },
42364
43680
  "de": {
42365
43681
  "landingTitle": "Wie möchten Sie kommunizieren?",
@@ -42396,7 +43712,14 @@ __webpack_require__.r(__webpack_exports__);
42396
43712
  "aboutTitle": "Über",
42397
43713
  "aboutVersion": "SDK-Version",
42398
43714
  "aboutConversationId": "Letzte Konversations-ID",
42399
- "aboutNoConversation": "Noch keine Konversation"
43715
+ "aboutNoConversation": "Noch keine Konversation",
43716
+ "settingsTitle": "Anrufeinstellungen",
43717
+ "settingsVoiceSpeed": "Sprechgeschwindigkeit",
43718
+ "settingsResponseLength": "Antwortlänge",
43719
+ "lengthDetailed": "Ausführlich",
43720
+ "lengthNormal": "Normal",
43721
+ "lengthBrief": "Kurz",
43722
+ "lengthVeryBrief": "Sehr kurz"
42400
43723
  }
42401
43724
  });
42402
43725