ttp-agent-sdk 2.45.14 → 2.46.2

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-13T09:41:28.306Z";
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.2";
21209
+ var BUILD_TIME = "2026-07-13T09:41:28.306Z";
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
  };
@@ -27777,6 +28702,16 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
27777
28702
  // Release wake lock on disconnect
27778
28703
  _this5._releaseWakeLock();
27779
28704
 
28705
+ // Monitor (listen-only supervision): the socket closing means the call ended —
28706
+ // flush any agent audio still queued in the player so listening stops immediately.
28707
+ if (_this5.isMonitor() && _this5.audioPlayer) {
28708
+ try {
28709
+ _this5.stopAudioPlayback();
28710
+ } catch (e) {
28711
+ console.warn('VoiceSDK v2: monitor close flush failed:', e);
28712
+ }
28713
+ }
28714
+
27780
28715
  // Reject all pending data injections on disconnect
27781
28716
  if (_this5.pendingInjections.size > 0) {
27782
28717
  console.warn("VoiceSDK v2: Rejecting ".concat(_this5.pendingInjections.size, " pending data injections due to disconnect"));
@@ -27952,7 +28887,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
27952
28887
 
27953
28888
  // Include SDK build time for debugging
27954
28889
  if (true) {
27955
- helloMessage.lastBuildTime = "2026-06-29T06:35:24.857Z";
28890
+ helloMessage.lastBuildTime = "2026-07-13T09:41:28.306Z";
27956
28891
  }
27957
28892
 
27958
28893
  // Page context is intentionally NOT attached to the hello message.
@@ -28254,6 +29189,8 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
28254
29189
  break;
28255
29190
  case 'show_items':
28256
29191
  case 'show_products':
29192
+ case 'show_recipe':
29193
+ case 'dismiss_recipe':
28257
29194
  case 'cart_updated':
28258
29195
  case 'add_to_store_cart':
28259
29196
  case 'add_to_site_cart':
@@ -28519,6 +29456,11 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
28519
29456
  // VoiceInterface waits for this flag before starting recording,
28520
29457
  // so config.sampleRate must be finalized first.
28521
29458
  this.helloAckReceived = true;
29459
+
29460
+ // Silently re-apply the user's persisted in-call settings (voice speed /
29461
+ // response length) now that the session is ready — the backend applies
29462
+ // them without a spoken acknowledgement (announce:false).
29463
+ this._syncCallSettingsAfterHello();
28522
29464
  if (message.outputAudioFormat) {
28523
29465
  // v2 protocol: format was negotiated
28524
29466
 
@@ -29332,6 +30274,79 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
29332
30274
  }
29333
30275
  }
29334
30276
 
30277
+ /**
30278
+ * In-call user settings (voice speed / response length).
30279
+ *
30280
+ * Sends {t:"call_settings"} to the backend: `voiceSpeedFactor` multiplies the
30281
+ * agent's configured TTS speed (1.0 = agent default, clamped server-side to
30282
+ * 0.5–2.0) and applies from the next synthesized line; `responseLength`
30283
+ * ("detailed"|"normal"|"brief"|"very_brief") is folded into the system prompt
30284
+ * from the next LLM turn. `opts.announce` (default true) lets the agent speak
30285
+ * a short fixed confirmation when idle.
30286
+ *
30287
+ * Called before hello_ack, the values are kept and flushed silently by
30288
+ * _syncCallSettingsAfterHello() once the session is ready.
30289
+ *
30290
+ * @param {{voiceSpeedFactor?: number, responseLength?: string}} settings
30291
+ * @param {{announce?: boolean}} [opts]
30292
+ * @returns {boolean} true if the message was sent now
30293
+ */
30294
+ }, {
30295
+ key: "setCallSettings",
30296
+ value: function setCallSettings() {
30297
+ var settings = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
30298
+ var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
30299
+ this._callSettings = _objectSpread(_objectSpread({}, this._callSettings || {}), settings);
30300
+ if (!this.helloAckReceived || !this.websocket || this.websocket.readyState !== WebSocket.OPEN) {
30301
+ return false; // flushed after hello_ack
30302
+ }
30303
+ var msg = {
30304
+ t: 'call_settings',
30305
+ announce: opts.announce !== false
30306
+ };
30307
+ if (typeof this._callSettings.voiceSpeedFactor === 'number') {
30308
+ msg.voiceSpeedFactor = this._callSettings.voiceSpeedFactor;
30309
+ }
30310
+ if (this._callSettings.responseLength) {
30311
+ msg.responseLength = this._callSettings.responseLength;
30312
+ }
30313
+ return this.sendMessage(msg);
30314
+ }
30315
+
30316
+ /**
30317
+ * Flush persisted call settings right after hello_ack (silent — no spoken ack).
30318
+ * Values come from an earlier setCallSettings() call or, when none, from
30319
+ * config.callSettingsProvider (the widget wires this to its localStorage).
30320
+ * Defaults (factor 1.0 + "normal") are skipped — nothing to sync.
30321
+ */
30322
+ }, {
30323
+ key: "_syncCallSettingsAfterHello",
30324
+ value: function _syncCallSettingsAfterHello() {
30325
+ try {
30326
+ var s = this._callSettings;
30327
+ if (!s && typeof this.config.callSettingsProvider === 'function') {
30328
+ s = this.config.callSettingsProvider();
30329
+ }
30330
+ if (!s) return;
30331
+ this._callSettings = _objectSpread({}, s);
30332
+ var factor = typeof s.voiceSpeedFactor === 'number' ? s.voiceSpeedFactor : 1.0;
30333
+ var length = s.responseLength || 'normal';
30334
+ if (Math.abs(factor - 1.0) < 0.001 && length === 'normal') return;
30335
+ this.sendMessage({
30336
+ t: 'call_settings',
30337
+ voiceSpeedFactor: factor,
30338
+ responseLength: length,
30339
+ announce: false
30340
+ });
30341
+ console.log('⚙️ VoiceSDK v2: Synced persisted call settings:', {
30342
+ voiceSpeedFactor: factor,
30343
+ responseLength: length
30344
+ });
30345
+ } catch (e) {
30346
+ console.warn('⚙️ VoiceSDK v2: Failed to sync call settings (ignored):', e);
30347
+ }
30348
+ }
30349
+
29335
30350
  /**
29336
30351
  * Pause the call. Stops sending/receiving audio.
29337
30352
  */
@@ -29455,64 +30470,72 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
29455
30470
  while (1) switch (_context8.p = _context8.n) {
29456
30471
  case 0:
29457
30472
  existingMediaStream = _args8.length > 0 && _args8[0] !== undefined ? _args8[0] : null;
29458
- if (this.isConnected) {
30473
+ if (!this.isMonitor()) {
29459
30474
  _context8.n = 1;
29460
30475
  break;
29461
30476
  }
30477
+ // Listen-only supervision: never open the mic / never uplink audio.
30478
+ console.warn('VoiceSDK v2: startRecording ignored in monitor (listen-only) mode');
30479
+ return _context8.a(2, false);
30480
+ case 1:
30481
+ if (this.isConnected) {
30482
+ _context8.n = 2;
30483
+ break;
30484
+ }
29462
30485
  error = new Error('Not connected to voice server');
29463
30486
  this.emit('error', error);
29464
30487
  throw error;
29465
- case 1:
30488
+ case 2:
29466
30489
  if (!this.isRecording) {
29467
- _context8.n = 2;
30490
+ _context8.n = 3;
29468
30491
  break;
29469
30492
  }
29470
30493
  console.warn('VoiceSDK v2: Already recording');
29471
30494
  return _context8.a(2, true);
29472
- case 2:
30495
+ case 3:
29473
30496
  if (!this.disclaimersPending) {
29474
- _context8.n = 3;
30497
+ _context8.n = 4;
29475
30498
  break;
29476
30499
  }
29477
30500
  _error4 = new Error('Server disclaimers must be accepted before recording. Call sendDisclaimerAck(true) first.');
29478
30501
  _error4.code = 'DISCLAIMER_PENDING';
29479
30502
  this.emit('error', _error4);
29480
30503
  throw _error4;
29481
- case 3:
30504
+ case 4:
29482
30505
  console.log('🎤 VoiceSDK v2: Starting recording...');
29483
- _context8.p = 4;
30506
+ _context8.p = 5;
29484
30507
  // CRITICAL for mobile browsers: Request microphone permission FIRST
29485
30508
  // iOS: Pass existingMediaStream to avoid second getUserMedia (can fail on iOS)
29486
30509
  console.log('🎤 VoiceSDK v2: Requesting microphone permission (mobile-friendly)...');
29487
- _context8.n = 5;
30510
+ _context8.n = 6;
29488
30511
  return this.audioRecorder.start(existingMediaStream ? {
29489
30512
  existingMediaStream: existingMediaStream
29490
30513
  } : {});
29491
- case 5:
30514
+ case 6:
29492
30515
  if (!(!this.isConnected || !this.websocket || this.websocket.readyState !== WebSocket.OPEN)) {
29493
- _context8.n = 10;
30516
+ _context8.n = 11;
29494
30517
  break;
29495
30518
  }
29496
- _context8.p = 6;
29497
- _context8.n = 7;
30519
+ _context8.p = 7;
30520
+ _context8.n = 8;
29498
30521
  return this.audioRecorder.stop();
29499
- case 7:
29500
- _context8.n = 9;
29501
- break;
29502
30522
  case 8:
29503
- _context8.p = 8;
29504
- _t6 = _context8.v;
30523
+ _context8.n = 10;
30524
+ break;
29505
30525
  case 9:
30526
+ _context8.p = 9;
30527
+ _t6 = _context8.v;
30528
+ case 10:
29506
30529
  _error5 = new Error('Connection lost - server may have rejected the call');
29507
30530
  _error5.name = 'ServerRejected';
29508
30531
  _error5.isServerRejection = true;
29509
30532
  // Don't emit error event - this is expected when server rejects
29510
30533
  // The error handler in VoiceInterface will handle it appropriately
29511
30534
  throw _error5;
29512
- case 10:
29513
- _context8.n = 11;
29514
- return this.audioPlayer.resumeAudioContext();
29515
30535
  case 11:
30536
+ _context8.n = 12;
30537
+ return this.audioPlayer.resumeAudioContext();
30538
+ case 12:
29516
30539
  // After permission is granted, send start_continuous_mode message to server
29517
30540
  this.sendMessage({
29518
30541
  t: 'start_continuous_mode',
@@ -29522,29 +30545,29 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
29522
30545
  // Request screen wake lock to keep screen on during voice call (mobile)
29523
30546
  this._requestWakeLock();
29524
30547
  return _context8.a(2, true);
29525
- case 12:
29526
- _context8.p = 12;
30548
+ case 13:
30549
+ _context8.p = 13;
29527
30550
  _t7 = _context8.v;
29528
30551
  if (!(this.audioRecorder && this.audioRecorder.isRecording)) {
29529
- _context8.n = 16;
30552
+ _context8.n = 17;
29530
30553
  break;
29531
30554
  }
29532
- _context8.p = 13;
29533
- _context8.n = 14;
30555
+ _context8.p = 14;
30556
+ _context8.n = 15;
29534
30557
  return this.audioRecorder.stop();
29535
- case 14:
29536
- _context8.n = 16;
29537
- break;
29538
30558
  case 15:
29539
- _context8.p = 15;
29540
- _t8 = _context8.v;
30559
+ _context8.n = 17;
30560
+ break;
29541
30561
  case 16:
30562
+ _context8.p = 16;
30563
+ _t8 = _context8.v;
30564
+ case 17:
29542
30565
  if (!(_t7.isServerRejection || _t7.name === 'ServerRejected')) {
29543
- _context8.n = 17;
30566
+ _context8.n = 18;
29544
30567
  break;
29545
30568
  }
29546
30569
  return _context8.a(2, false);
29547
- case 17:
30570
+ case 18:
29548
30571
  // Log and emit other errors (permission denied, etc.)
29549
30572
  console.error('❌ VoiceSDK v2: Failed to start recording:', _t7);
29550
30573
 
@@ -29559,7 +30582,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
29559
30582
  }
29560
30583
  return _context8.a(2, false);
29561
30584
  }
29562
- }, _callee8, this, [[13, 15], [6, 8], [4, 12]]);
30585
+ }, _callee8, this, [[14, 16], [7, 9], [5, 13]]);
29563
30586
  }));
29564
30587
  function startRecording() {
29565
30588
  return _startRecording.apply(this, arguments);
@@ -31391,7 +32414,10 @@ var AgentSDK = /*#__PURE__*/function () {
31391
32414
  flavor: this.config.flavor || null,
31392
32415
  // Shared client-tool handler map (set by TTPChatWidget so voice + text SDKs
31393
32416
  // both look up handlers in the same registration site).
31394
- sharedToolHandlers: this.config.sharedToolHandlers || null
32417
+ sharedToolHandlers: this.config.sharedToolHandlers || null,
32418
+ // Persisted in-call settings provider (voice speed / response length) —
32419
+ // read fresh at hello_ack so localStorage changes survive reconnects.
32420
+ callSettingsProvider: this.config.callSettingsProvider || null
31395
32421
  }));
31396
32422
 
31397
32423
  // If we have stored resume info from checkResumeBeforeConnect(), use it
@@ -32387,6 +33413,7 @@ __webpack_require__.r(__webpack_exports__);
32387
33413
  /* harmony import */ var _widget_translations_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./widget-translations.js */ "./src/widget/widget-translations.js");
32388
33414
  /* harmony import */ var _flavors_index_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../flavors/index.js */ "./src/flavors/index.js");
32389
33415
  /* harmony import */ var _ecommerce_EcommerceVoiceStrip_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../ecommerce/EcommerceVoiceStrip.js */ "./src/ecommerce/EcommerceVoiceStrip.js");
33416
+ /* harmony import */ var _ecommerce_RecipeStore_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../ecommerce/RecipeStore.js */ "./src/ecommerce/RecipeStore.js");
32390
33417
  var _excluded = ["agentName"];
32391
33418
  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
33419
  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 +33442,7 @@ function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" ==
32415
33442
 
32416
33443
 
32417
33444
 
33445
+
32418
33446
  /** Fallback for `buildWebSocketUrl()` when no optional `websocketUrl` override is set (VoiceSDK v2 uses the same default). */
32419
33447
  var DEFAULT_VOICE_WEBSOCKET_URL = 'wss://speech.talktopc.com/ws/conv';
32420
33448
  function isNonEmptyConfigString(v) {
@@ -32507,7 +33535,8 @@ function talkToPcLinkColorForPanel(isLightPanel, mergedVoice, rawConfig, voiceUi
32507
33535
  }
32508
33536
  var TTPChatWidget = /*#__PURE__*/function () {
32509
33537
  function TTPChatWidget() {
32510
- var _this$config$behavior,
33538
+ var _this = this,
33539
+ _this$config$behavior,
32511
33540
  _this$config$panel,
32512
33541
  _this$config$flavor,
32513
33542
  _this$config$text,
@@ -32517,8 +33546,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
32517
33546
  _this$config$voice3,
32518
33547
  _this$config$voice4,
32519
33548
  _this$config$voice5,
32520
- _this$config$voice6,
32521
- _this = this;
33549
+ _this$config$voice6;
32522
33550
  var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
32523
33551
  _classCallCheck(this, TTPChatWidget);
32524
33552
  this._rawConfig = config;
@@ -32526,6 +33554,14 @@ var TTPChatWidget = /*#__PURE__*/function () {
32526
33554
  this.config = this.mergeWithDefaults(config);
32527
33555
  // Silently ignore allowOverride (now server-side only)
32528
33556
 
33557
+ // Persisted in-call settings (voice speed / response length). The provider is
33558
+ // read fresh by VoiceSDK/TextChatSDK right after each hello_ack, so the saved
33559
+ // values re-apply silently on every (re)connect. Rides on this.config so both
33560
+ // voiceConfig spreads and the TextChatSDK constructor pick it up.
33561
+ this.config.callSettingsProvider = function () {
33562
+ return _this._loadCallSettings();
33563
+ };
33564
+
32529
33565
  // Shared client-tool handler map. Both VoiceSDK and TextChatSDK look up
32530
33566
  // handlers in this Map, so a single registerToolHandler() call works for
32531
33567
  // whichever channel the LLM happens to be running on at tool-call time.
@@ -32847,11 +33883,21 @@ var TTPChatWidget = /*#__PURE__*/function () {
32847
33883
  if (flavor.messageHandlers['dismiss_media']) {
32848
33884
  flavor.messageHandlers['dismiss_media']();
32849
33885
  }
33886
+ if (flavor.messageHandlers['dismiss_recipe']) {
33887
+ flavor.messageHandlers['dismiss_recipe']({
33888
+ silent: true
33889
+ });
33890
+ }
32850
33891
  });
32851
33892
  this.voiceSDK.on('bargeIn', function () {
32852
33893
  if (flavor.messageHandlers['dismiss_media']) {
32853
33894
  flavor.messageHandlers['dismiss_media']();
32854
33895
  }
33896
+ if (flavor.messageHandlers['dismiss_recipe']) {
33897
+ flavor.messageHandlers['dismiss_recipe']({
33898
+ silent: true
33899
+ });
33900
+ }
32855
33901
  });
32856
33902
  console.log("[TTPChatWidget] Flavor \"".concat(flavorName, "\" message listeners attached (voice)"));
32857
33903
  }
@@ -33767,6 +34813,20 @@ var TTPChatWidget = /*#__PURE__*/function () {
33767
34813
  };
33768
34814
  }
33769
34815
 
34816
+ /**
34817
+ * Scoped localStorage-backed store for saved recipes (strip badge + popover).
34818
+ */
34819
+ }, {
34820
+ key: "getRecipeStore",
34821
+ value: function getRecipeStore() {
34822
+ if (!this._recipeStore) {
34823
+ var _this$_flavorObj2, _this$config$flavor2;
34824
+ 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';
34825
+ this._recipeStore = (0,_ecommerce_RecipeStore_js__WEBPACK_IMPORTED_MODULE_6__.createRecipeStore)((0,_ecommerce_RecipeStore_js__WEBPACK_IMPORTED_MODULE_6__.recipeStorageKey)(partnerId));
34826
+ }
34827
+ return this._recipeStore;
34828
+ }
34829
+
33770
34830
  /**
33771
34831
  * Generate footer branding HTML
33772
34832
  */
@@ -33854,7 +34914,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
33854
34914
  return;
33855
34915
  }
33856
34916
  this._ensureAboutStyles();
33857
- var version = true ? "2.45.14" : 0;
34917
+ var version = true ? "2.46.2" : 0;
33858
34918
  var convId = this._getLastConversationId();
33859
34919
  var t = function t(k, fb) {
33860
34920
  try {
@@ -33911,6 +34971,190 @@ var TTPChatWidget = /*#__PURE__*/function () {
33911
34971
  this._aboutOverlay = null;
33912
34972
  }
33913
34973
  }
34974
+
34975
+ // ===================================================================================
34976
+ // In-call settings (voice speed / response length) — gear button + dialog
34977
+ // ===================================================================================
34978
+
34979
+ /** localStorage key for the persisted call settings, scoped per agent + origin. */
34980
+ }, {
34981
+ key: "_callSettingsStorageKey",
34982
+ value: function _callSettingsStorageKey() {
34983
+ var agent = this.config.agentId || this.config.appId || 'default';
34984
+ return "ttp_call_settings_".concat(agent);
34985
+ }
34986
+
34987
+ /**
34988
+ * Load persisted call settings. Defaults: agent-configured speed (factor 1.0)
34989
+ * and "normal" response length.
34990
+ * @returns {{voiceSpeedFactor: number, responseLength: string}}
34991
+ */
34992
+ }, {
34993
+ key: "_loadCallSettings",
34994
+ value: function _loadCallSettings() {
34995
+ var defaults = {
34996
+ voiceSpeedFactor: 1.0,
34997
+ responseLength: 'normal'
34998
+ };
34999
+ try {
35000
+ var raw = localStorage.getItem(this._callSettingsStorageKey());
35001
+ if (!raw) return defaults;
35002
+ var s = JSON.parse(raw);
35003
+ var factor = typeof s.voiceSpeedFactor === 'number' ? Math.max(0.5, Math.min(2.0, s.voiceSpeedFactor)) : 1.0;
35004
+ var length = ['detailed', 'normal', 'brief', 'very_brief'].includes(s.responseLength) ? s.responseLength : 'normal';
35005
+ return {
35006
+ voiceSpeedFactor: factor,
35007
+ responseLength: length
35008
+ };
35009
+ } catch (_) {
35010
+ return defaults;
35011
+ }
35012
+ }
35013
+ }, {
35014
+ key: "_saveCallSettings",
35015
+ value: function _saveCallSettings(settings) {
35016
+ try {
35017
+ localStorage.setItem(this._callSettingsStorageKey(), JSON.stringify(settings));
35018
+ } catch (_) {/* storage unavailable — settings still apply for this session */}
35019
+ }
35020
+
35021
+ /**
35022
+ * Persist + push a settings change to whichever channel is live. The backend
35023
+ * applies voice speed from the next spoken line and response length from the
35024
+ * next LLM turn — no tool call involved. announce=true lets the agent speak
35025
+ * its short fixed confirmation (only when idle).
35026
+ */
35027
+ }, {
35028
+ key: "_applyCallSettings",
35029
+ value: function _applyCallSettings(partial) {
35030
+ var _this$voiceInterface4, _this$sdk2;
35031
+ var announce = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
35032
+ var merged = _objectSpread(_objectSpread({}, this._loadCallSettings()), partial);
35033
+ this._saveCallSettings(merged);
35034
+ 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;
35035
+ if (voice !== null && voice !== void 0 && voice.setCallSettings) {
35036
+ voice.setCallSettings({
35037
+ voiceSpeedFactor: merged.voiceSpeedFactor,
35038
+ responseLength: merged.responseLength
35039
+ }, {
35040
+ announce: announce
35041
+ });
35042
+ }
35043
+ if ((_this$sdk2 = this.sdk) !== null && _this$sdk2 !== void 0 && _this$sdk2.setCallSettings && this.sdk.connected) {
35044
+ this.sdk.setCallSettings({
35045
+ responseLength: merged.responseLength
35046
+ });
35047
+ }
35048
+ }
35049
+
35050
+ /** Inject the settings-dialog styles into document.head once (global, above the strip). */
35051
+ }, {
35052
+ key: "_ensureCallSettingsStyles",
35053
+ value: function _ensureCallSettingsStyles() {
35054
+ if (document.getElementById('ttp-callsettings-dialog-styles')) return;
35055
+ this._ensureAboutStyles(); // reuse the overlay/card/close/row styles
35056
+ var style = document.createElement('style');
35057
+ style.id = 'ttp-callsettings-dialog-styles';
35058
+ 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 ";
35059
+ document.head.appendChild(style);
35060
+ }
35061
+
35062
+ /**
35063
+ * Open the in-call settings dialog: two sliders — voice speed (higher = faster)
35064
+ * and response length (very brief on the left → detailed on the right). Values
35065
+ * persist to localStorage and apply from the next line/turn (backend `call_settings`).
35066
+ */
35067
+ }, {
35068
+ key: "_openCallSettingsModal",
35069
+ value: function _openCallSettingsModal() {
35070
+ var _this9 = this,
35071
+ _overlay$querySelecto2;
35072
+ if (this._callSettingsOverlay) {
35073
+ this._closeCallSettingsModal();
35074
+ return;
35075
+ }
35076
+ this._ensureCallSettingsStyles();
35077
+ var t = function t(k, fb) {
35078
+ try {
35079
+ var _this9$voiceInterface, _this9$voiceInterface2, _this9$textInterface, _this9$textInterface$;
35080
+ 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;
35081
+ } catch (_) {
35082
+ return fb;
35083
+ }
35084
+ };
35085
+ var title = t('settingsTitle', 'Call settings');
35086
+ var speedLabel = t('settingsVoiceSpeed', 'Voice speed');
35087
+ var lengthLabel = t('settingsResponseLength', 'Response length');
35088
+ // Slider order: very brief (left) → detailed (right); higher = more detail.
35089
+ var LEVELS = ['very_brief', 'brief', 'normal', 'detailed'];
35090
+ var levelLabels = {
35091
+ detailed: t('lengthDetailed', 'Detailed'),
35092
+ normal: t('lengthNormal', 'Normal'),
35093
+ brief: t('lengthBrief', 'Brief'),
35094
+ very_brief: t('lengthVeryBrief', 'Very brief')
35095
+ };
35096
+ var saved = this._loadCallSettings();
35097
+ var levelIdx = Math.max(0, LEVELS.indexOf(saved.responseLength));
35098
+ var overlay = document.createElement('div');
35099
+ overlay.className = 'ttp-about-overlay';
35100
+ 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>");
35101
+ var speedSlider = overlay.querySelector('[data-cs-speed]');
35102
+ var speedVal = overlay.querySelector('[data-cs-speed-val]');
35103
+ var lengthSlider = overlay.querySelector('[data-cs-length]');
35104
+ var lengthVal = overlay.querySelector('[data-cs-length-val]');
35105
+ var fmtSpeed = function fmtSpeed(v) {
35106
+ return "".concat(Number(v).toFixed(2).replace(/0$/, ''), "\xD7");
35107
+ };
35108
+ var renderVals = function renderVals() {
35109
+ if (speedVal) speedVal.textContent = fmtSpeed(speedSlider.value);
35110
+ if (lengthVal) lengthVal.textContent = levelLabels[LEVELS[Number(lengthSlider.value)]] || '';
35111
+ };
35112
+ renderVals();
35113
+
35114
+ // Live label updates while dragging; persist + push on release only.
35115
+ speedSlider === null || speedSlider === void 0 || speedSlider.addEventListener('input', renderVals);
35116
+ lengthSlider === null || lengthSlider === void 0 || lengthSlider.addEventListener('input', renderVals);
35117
+ speedSlider === null || speedSlider === void 0 || speedSlider.addEventListener('change', function () {
35118
+ _this9._applyCallSettings({
35119
+ voiceSpeedFactor: Number(speedSlider.value)
35120
+ }, true);
35121
+ });
35122
+ lengthSlider === null || lengthSlider === void 0 || lengthSlider.addEventListener('change', function () {
35123
+ _this9._applyCallSettings({
35124
+ responseLength: LEVELS[Number(lengthSlider.value)]
35125
+ }, true);
35126
+ });
35127
+ var close = function close() {
35128
+ return _this9._closeCallSettingsModal();
35129
+ };
35130
+ overlay.addEventListener('click', function (e) {
35131
+ if (e.target === overlay) close();
35132
+ });
35133
+ (_overlay$querySelecto2 = overlay.querySelector('.ttp-about-close')) === null || _overlay$querySelecto2 === void 0 || _overlay$querySelecto2.addEventListener('click', function (e) {
35134
+ e.stopPropagation();
35135
+ close();
35136
+ });
35137
+ this._callSettingsKeyHandler = function (e) {
35138
+ if (e.key === 'Escape') close();
35139
+ };
35140
+ document.addEventListener('keydown', this._callSettingsKeyHandler);
35141
+ document.body.appendChild(overlay);
35142
+ this._callSettingsOverlay = overlay;
35143
+ }
35144
+
35145
+ /** Close the in-call settings dialog if open. */
35146
+ }, {
35147
+ key: "_closeCallSettingsModal",
35148
+ value: function _closeCallSettingsModal() {
35149
+ if (this._callSettingsKeyHandler) {
35150
+ document.removeEventListener('keydown', this._callSettingsKeyHandler);
35151
+ this._callSettingsKeyHandler = null;
35152
+ }
35153
+ if (this._callSettingsOverlay) {
35154
+ this._callSettingsOverlay.remove();
35155
+ this._callSettingsOverlay = null;
35156
+ }
35157
+ }
33914
35158
  }, {
33915
35159
  key: "generatePositionStyles",
33916
35160
  value: function generatePositionStyles() {
@@ -34047,7 +35291,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
34047
35291
  }, {
34048
35292
  key: "setupWidgetEvents",
34049
35293
  value: function setupWidgetEvents() {
34050
- var _this9 = this,
35294
+ var _this0 = this,
34051
35295
  _this$config$behavior8,
34052
35296
  _this$config$position5;
34053
35297
  if (!this.shadowRoot) return;
@@ -34061,17 +35305,17 @@ var TTPChatWidget = /*#__PURE__*/function () {
34061
35305
  launcherBtn.onclick = function (e) {
34062
35306
  e.stopPropagation();
34063
35307
  // 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();
35308
+ if (_this0._suppressLauncherClick) return;
35309
+ if (_this0.config.promptAnimation.hideAfterClick) {
35310
+ _this0.hidePrompt();
34067
35311
  }
34068
- _this9.togglePanel();
35312
+ _this0.togglePanel();
34069
35313
  };
34070
35314
  }
34071
35315
  var closeBtn = this.shadowRoot.getElementById('closeBtn');
34072
35316
  if (closeBtn) {
34073
35317
  closeBtn.onclick = function () {
34074
- _this9._doTogglePanel();
35318
+ _this0._doTogglePanel();
34075
35319
  };
34076
35320
  }
34077
35321
 
@@ -34079,23 +35323,34 @@ var TTPChatWidget = /*#__PURE__*/function () {
34079
35323
  var closeIdle = this.shadowRoot.getElementById('ttpCloseIdle');
34080
35324
  if (closeIdle) {
34081
35325
  closeIdle.onclick = function () {
34082
- return _this9._doTogglePanel();
35326
+ return _this0._doTogglePanel();
34083
35327
  };
34084
35328
  }
34085
35329
  var closeActive = this.shadowRoot.getElementById('ttpCloseActive');
34086
35330
  if (closeActive) {
34087
35331
  closeActive.onclick = function () {
34088
- return _this9._doTogglePanel();
35332
+ return _this0._doTogglePanel();
34089
35333
  };
34090
35334
  }
34091
35335
 
34092
35336
  // "About" info buttons (present in every view: idle / active / compact /
34093
35337
  // mobile idle / mobile active / text top-bar). One shared dialog.
34094
- this.shadowRoot.querySelectorAll('.ttp-info-btn').forEach(function (btn) {
35338
+ // Settings gear buttons share the .ttp-info-btn styling class — skip them here.
35339
+ this.shadowRoot.querySelectorAll('.ttp-info-btn:not(.ttp-settings-btn)').forEach(function (btn) {
35340
+ btn.onclick = function (e) {
35341
+ e.stopPropagation();
35342
+ e.preventDefault();
35343
+ _this0._openAboutModal();
35344
+ };
35345
+ });
35346
+
35347
+ // In-call settings gear buttons (active-call views) — one shared dialog with
35348
+ // the voice-speed / response-length sliders.
35349
+ this.shadowRoot.querySelectorAll('.ttp-settings-btn').forEach(function (btn) {
34095
35350
  btn.onclick = function (e) {
34096
35351
  e.stopPropagation();
34097
35352
  e.preventDefault();
34098
- _this9._openAboutModal();
35353
+ _this0._openCallSettingsModal();
34099
35354
  };
34100
35355
  });
34101
35356
 
@@ -34103,17 +35358,17 @@ var TTPChatWidget = /*#__PURE__*/function () {
34103
35358
  var startTextBtn = this.shadowRoot.getElementById('startTextBtn');
34104
35359
  if (startTextBtn) {
34105
35360
  startTextBtn.onclick = function () {
34106
- return _this9.showText();
35361
+ return _this0.showText();
34107
35362
  };
34108
35363
  }
34109
35364
  var textUnifiedBackBtn = this.shadowRoot.getElementById('textUnifiedBackBtn');
34110
35365
  if (textUnifiedBackBtn) {
34111
35366
  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';
35367
+ var _this0$config$behavio;
35368
+ var widgetMode = ((_this0$config$behavio = _this0.config.behavior) === null || _this0$config$behavio === void 0 ? void 0 : _this0$config$behavio.mode) || 'unified';
34114
35369
  if (widgetMode !== 'unified') return;
34115
- _this9.showLanding();
34116
- _this9._openMobileCallTextLanding();
35370
+ _this0.showLanding();
35371
+ _this0._openMobileCallTextLanding();
34117
35372
  };
34118
35373
  }
34119
35374
 
@@ -34143,30 +35398,30 @@ var TTPChatWidget = /*#__PURE__*/function () {
34143
35398
  // Setup back button handler
34144
35399
  if (backBtn) {
34145
35400
  backBtn.onclick = function () {
34146
- var _this9$voiceInterface, _this9$voiceInterface3;
35401
+ var _this0$voiceInterface, _this0$voiceInterface3;
34147
35402
  // 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;
35403
+ if ((_this0$voiceInterface = _this0.voiceInterface) !== null && _this0$voiceInterface !== void 0 && _this0$voiceInterface.isShowingDomainError) {
35404
+ var _this0$voiceInterface2;
34150
35405
  console.log('🔙 Back button clicked - clearing domain error and returning to voice hero');
34151
35406
  // Clear the domain error flag
34152
- _this9.voiceInterface.isShowingDomainError = false;
35407
+ _this0.voiceInterface.isShowingDomainError = false;
34153
35408
  // 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();
35409
+ if ((_this0$voiceInterface2 = _this0.voiceInterface) !== null && _this0$voiceInterface2 !== void 0 && _this0$voiceInterface2.resetConnectingState) {
35410
+ _this0.voiceInterface.resetConnectingState();
34156
35411
  }
34157
- _this9.showLanding();
34158
- _this9._openMobileCallTextLanding();
35412
+ _this0.showLanding();
35413
+ _this0._openMobileCallTextLanding();
34159
35414
  return;
34160
35415
  }
34161
- if ((_this9$voiceInterface3 = _this9.voiceInterface) !== null && _this9$voiceInterface3 !== void 0 && _this9$voiceInterface3.isActive) {
35416
+ if ((_this0$voiceInterface3 = _this0.voiceInterface) !== null && _this0$voiceInterface3 !== void 0 && _this0$voiceInterface3.isActive) {
34162
35417
  // If call is active, show message inside widget instead of modal
34163
- _this9.showBackButtonWarning();
35418
+ _this0.showBackButtonWarning();
34164
35419
  } else if (window.innerWidth <= 768) {
34165
- _this9.showLanding();
34166
- _this9._openMobileCallTextLanding();
35420
+ _this0.showLanding();
35421
+ _this0._openMobileCallTextLanding();
34167
35422
  } else {
34168
35423
  // Desktop: return to voice hero (unified home)
34169
- _this9.showLanding();
35424
+ _this0.showLanding();
34170
35425
  }
34171
35426
  };
34172
35427
  }
@@ -34209,7 +35464,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
34209
35464
  // Setup header button handlers
34210
35465
  if (newChatBtn) {
34211
35466
  newChatBtn.onclick = function () {
34212
- return _this9.textInterface.startNewChat();
35467
+ return _this0.textInterface.startNewChat();
34213
35468
  };
34214
35469
  // Hide new chat on unified home (voice hero) initially
34215
35470
  if (isUnified && this.currentView !== 'text') {
@@ -34355,7 +35610,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
34355
35610
  }, {
34356
35611
  key: "_restoreUnitOffset",
34357
35612
  value: function _restoreUnitOffset(key) {
34358
- var _this0 = this;
35613
+ var _this1 = this;
34359
35614
  var saved;
34360
35615
  try {
34361
35616
  saved = JSON.parse(window.localStorage.getItem(key) || 'null');
@@ -34364,13 +35619,13 @@ var TTPChatWidget = /*#__PURE__*/function () {
34364
35619
  }
34365
35620
  if (!saved || typeof saved.x !== 'number' || typeof saved.y !== 'number') return;
34366
35621
  requestAnimationFrame(function () {
34367
- _this0._unitOffset = {
35622
+ _this1._unitOffset = {
34368
35623
  x: 0,
34369
35624
  y: 0
34370
35625
  }; // ensure DOM matches before measuring
34371
- var bases = _this0._unitBaseRects();
34372
- _this0._unitOffset = _this0._clampUnitOffset(saved.x, saved.y, bases);
34373
- _this0._applyUnitOffset();
35626
+ var bases = _this1._unitBaseRects();
35627
+ _this1._unitOffset = _this1._clampUnitOffset(saved.x, saved.y, bases);
35628
+ _this1._applyUnitOffset();
34374
35629
  });
34375
35630
  }
34376
35631
 
@@ -34387,7 +35642,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
34387
35642
  }, {
34388
35643
  key: "_attachUnitDrag",
34389
35644
  value: function _attachUnitDrag(handle, opts) {
34390
- var _this1 = this;
35645
+ var _this10 = this;
34391
35646
  var persist = opts.persist,
34392
35647
  key = opts.key,
34393
35648
  excludeSelector = opts.excludeSelector,
@@ -34408,8 +35663,8 @@ var TTPChatWidget = /*#__PURE__*/function () {
34408
35663
  var dy = e.clientY - startY;
34409
35664
  if (!moved && Math.hypot(dx, dy) < DRAG_THRESHOLD) return;
34410
35665
  moved = true;
34411
- _this1._unitOffset = _this1._clampUnitOffset(baseX + dx, baseY + dy, bases);
34412
- _this1._applyUnitOffset();
35666
+ _this10._unitOffset = _this10._clampUnitOffset(baseX + dx, baseY + dy, bases);
35667
+ _this10._applyUnitOffset();
34413
35668
  };
34414
35669
  var _onUp = function onUp(e) {
34415
35670
  window.removeEventListener('pointermove', onMove);
@@ -34423,7 +35678,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
34423
35678
  if (typeof onMoved === 'function') onMoved();
34424
35679
  if (persist && key) {
34425
35680
  try {
34426
- window.localStorage.setItem(key, JSON.stringify(_this1._unitOffset));
35681
+ window.localStorage.setItem(key, JSON.stringify(_this10._unitOffset));
34427
35682
  } catch (err) {/* unavailable */}
34428
35683
  }
34429
35684
  }
@@ -34434,7 +35689,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
34434
35689
  if (excludeSelector && e.target && e.target.closest && e.target.closest(excludeSelector)) return;
34435
35690
  dragging = true;
34436
35691
  moved = false;
34437
- var o = _this1._unitOffset || (_this1._unitOffset = {
35692
+ var o = _this10._unitOffset || (_this10._unitOffset = {
34438
35693
  x: 0,
34439
35694
  y: 0
34440
35695
  });
@@ -34442,7 +35697,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
34442
35697
  startY = e.clientY;
34443
35698
  baseX = o.x;
34444
35699
  baseY = o.y;
34445
- bases = _this1._unitBaseRects();
35700
+ bases = _this10._unitBaseRects();
34446
35701
  handle.style.setProperty('cursor', 'grabbing');
34447
35702
  try {
34448
35703
  handle.setPointerCapture(e.pointerId);
@@ -34464,7 +35719,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
34464
35719
  key: "setupDraggableWidget",
34465
35720
  value: function setupDraggableWidget() {
34466
35721
  var _this$config$position6,
34467
- _this10 = this;
35722
+ _this11 = this;
34468
35723
  var root = this.shadowRoot;
34469
35724
  if (!root) return;
34470
35725
  var container = root.getElementById('text-chat-button-container');
@@ -34480,14 +35735,14 @@ var TTPChatWidget = /*#__PURE__*/function () {
34480
35735
 
34481
35736
  // Launcher handles: pill + mobile FAB (suppress the click after a drag).
34482
35737
  [root.getElementById('text-chat-button'), root.querySelector('.ttp-mobile-fab')].filter(Boolean).forEach(function (h) {
34483
- return _this10._attachUnitDrag(h, {
35738
+ return _this11._attachUnitDrag(h, {
34484
35739
  persist: persist,
34485
35740
  key: key,
34486
35741
  restCursor: 'grab',
34487
35742
  onMoved: function onMoved() {
34488
- _this10._suppressLauncherClick = true;
35743
+ _this11._suppressLauncherClick = true;
34489
35744
  setTimeout(function () {
34490
- _this10._suppressLauncherClick = false;
35745
+ _this11._suppressLauncherClick = false;
34491
35746
  }, 0);
34492
35747
  }
34493
35748
  });
@@ -34498,7 +35753,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
34498
35753
  ['.ttp-header', '.ttp-call-topbar', '.text-interface-top-bar'].map(function (sel) {
34499
35754
  return panel.querySelector(sel);
34500
35755
  }).filter(Boolean).forEach(function (h) {
34501
- return _this10._attachUnitDrag(h, {
35756
+ return _this11._attachUnitDrag(h, {
34502
35757
  persist: persist,
34503
35758
  key: key,
34504
35759
  restCursor: 'move',
@@ -34533,13 +35788,13 @@ var TTPChatWidget = /*#__PURE__*/function () {
34533
35788
  }, {
34534
35789
  key: "_bindDragResize",
34535
35790
  value: function _bindDragResize() {
34536
- var _this11 = this;
35791
+ var _this12 = this;
34537
35792
  if (this._dragResizeBound) return;
34538
35793
  this._dragResizeBound = true;
34539
35794
  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);
35795
+ var _this12$_ecommerceVoi, _this12$_ecommerceVoi2;
35796
+ _this12._reclampUnit();
35797
+ (_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
35798
  });
34544
35799
  }
34545
35800
  }, {
@@ -34588,9 +35843,9 @@ var TTPChatWidget = /*#__PURE__*/function () {
34588
35843
  }, {
34589
35844
  key: "_isDomainErrorVisible",
34590
35845
  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');
35846
+ var _this$voiceInterface5, _this$voiceInterface6, _idleState$dataset;
35847
+ if ((_this$voiceInterface5 = this.voiceInterface) !== null && _this$voiceInterface5 !== void 0 && _this$voiceInterface5.isShowingDomainError) return true;
35848
+ 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
35849
  return idleState && ((_idleState$dataset = idleState.dataset) === null || _idleState$dataset === void 0 ? void 0 : _idleState$dataset.domainError) === 'true';
34595
35850
  }
34596
35851
  }, {
@@ -34618,7 +35873,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
34618
35873
  }, {
34619
35874
  key: "_openMobileCallTextLanding",
34620
35875
  value: function _openMobileCallTextLanding() {
34621
- var _this12 = this;
35876
+ var _this13 = this;
34622
35877
  if (!this.shadowRoot || window.innerWidth > 768) return;
34623
35878
  var mobileFab = this.shadowRoot.querySelector('.ttp-mobile-fab');
34624
35879
  var mobileLanding = this.shadowRoot.getElementById('ttpMobileLanding');
@@ -34629,30 +35884,30 @@ var TTPChatWidget = /*#__PURE__*/function () {
34629
35884
  // "Back" button: composedPath includes the panel but not #ttpMobileLanding, so it treated
34630
35885
  // the click as "outside" and closed the landing immediately (user only saw the FAB again).
34631
35886
  var open = function open() {
34632
- if (!_this12.shadowRoot || window.innerWidth > 768) return;
34633
- var panel = _this12.shadowRoot.getElementById('text-chat-panel');
35887
+ if (!_this13.shadowRoot || window.innerWidth > 768) return;
35888
+ var panel = _this13.shadowRoot.getElementById('text-chat-panel');
34634
35889
  if (panel) {
34635
35890
  panel.classList.remove('open');
34636
- _this12.isOpen = false;
35891
+ _this13.isOpen = false;
34637
35892
  }
34638
- if (_this12.config.promptAnimation.hideAfterClick) {
34639
- _this12.hidePrompt();
35893
+ if (_this13.config.promptAnimation.hideAfterClick) {
35894
+ _this13.hidePrompt();
34640
35895
  }
34641
- var errorBanner = _this12.shadowRoot.getElementById('ttpMobileLandingError');
35896
+ var errorBanner = _this13.shadowRoot.getElementById('ttpMobileLandingError');
34642
35897
  if (errorBanner) errorBanner.style.display = 'none';
34643
- var fab = _this12.shadowRoot.querySelector('.ttp-mobile-fab');
34644
- var landing = _this12.shadowRoot.getElementById('ttpMobileLanding');
35898
+ var fab = _this13.shadowRoot.querySelector('.ttp-mobile-fab');
35899
+ var landing = _this13.shadowRoot.getElementById('ttpMobileLanding');
34645
35900
  if (!fab || !landing) return;
34646
35901
  fab.style.display = 'none';
34647
35902
  landing.classList.add('active');
34648
- _this12.isMobileLandingOpen = true;
35903
+ _this13.isMobileLandingOpen = true;
34649
35904
  };
34650
35905
  queueMicrotask(open);
34651
35906
  }
34652
35907
  }, {
34653
35908
  key: "setupMobileWidgetEvents",
34654
35909
  value: function setupMobileWidgetEvents() {
34655
- var _this13 = this;
35910
+ var _this14 = this;
34656
35911
  if (!this.shadowRoot) return;
34657
35912
  var mobileFab = this.shadowRoot.querySelector('.ttp-mobile-fab');
34658
35913
  var mobileLanding = this.shadowRoot.getElementById('ttpMobileLanding');
@@ -34660,12 +35915,12 @@ var TTPChatWidget = /*#__PURE__*/function () {
34660
35915
  var closeMobileLanding = function closeMobileLanding() {
34661
35916
  mobileLanding.classList.remove('active');
34662
35917
  mobileFab.style.display = '';
34663
- _this13.isMobileLandingOpen = false;
35918
+ _this14.isMobileLandingOpen = false;
34664
35919
  };
34665
35920
  mobileFab.onclick = function () {
34666
35921
  // A drag just ended — don't open the landing on the trailing click.
34667
- if (_this13._suppressLauncherClick) return;
34668
- _this13._openMobileCallTextLanding();
35922
+ if (_this14._suppressLauncherClick) return;
35923
+ _this14._openMobileCallTextLanding();
34669
35924
  };
34670
35925
  var mobileClose = mobileLanding.querySelector('.ttp-mobile-landing__close');
34671
35926
  if (mobileClose) {
@@ -34675,18 +35930,18 @@ var TTPChatWidget = /*#__PURE__*/function () {
34675
35930
  if (mobileCallBtn) {
34676
35931
  mobileCallBtn.onclick = function () {
34677
35932
  closeMobileLanding();
34678
- var panel = _this13.shadowRoot.getElementById('text-chat-panel');
35933
+ var panel = _this14.shadowRoot.getElementById('text-chat-panel');
34679
35934
  if (panel) {
34680
35935
  panel.style.removeProperty('display');
34681
35936
  panel.classList.add('open');
34682
- _this13.isOpen = true;
35937
+ _this14.isOpen = true;
34683
35938
  }
34684
35939
  mobileFab.style.display = 'none';
34685
- _this13.showVoice();
35940
+ _this14.showVoice();
34686
35941
  // Landing already chose "Voice call" — start the call (mic permission / pre-prompt) instead of
34687
35942
  // leaving users on a second "Start Call" tap that feels broken.
34688
35943
  queueMicrotask(function () {
34689
- _this13.startVoiceCall().catch(function (err) {
35944
+ _this14.startVoiceCall().catch(function (err) {
34690
35945
  console.error('[TTPChatWidget] Mobile landing voice start failed:', err);
34691
35946
  });
34692
35947
  });
@@ -34696,20 +35951,20 @@ var TTPChatWidget = /*#__PURE__*/function () {
34696
35951
  if (mobileChatBtn) {
34697
35952
  mobileChatBtn.onclick = function () {
34698
35953
  closeMobileLanding();
34699
- var panel = _this13.shadowRoot.getElementById('text-chat-panel');
35954
+ var panel = _this14.shadowRoot.getElementById('text-chat-panel');
34700
35955
  if (panel) {
34701
35956
  panel.style.removeProperty('display');
34702
35957
  panel.classList.add('open');
34703
- _this13.isOpen = true;
35958
+ _this14.isOpen = true;
34704
35959
  }
34705
35960
  mobileFab.style.display = 'none';
34706
- _this13.showText();
35961
+ _this14.showText();
34707
35962
  };
34708
35963
  }
34709
35964
 
34710
35965
  // Close mobile landing on outside click
34711
35966
  document.addEventListener('click', function (e) {
34712
- if (!_this13.isMobileLandingOpen) return;
35967
+ if (!_this14.isMobileLandingOpen) return;
34713
35968
  var path = e.composedPath();
34714
35969
  if (!path.includes(mobileLanding) && !path.includes(mobileFab)) {
34715
35970
  closeMobileLanding();
@@ -34718,7 +35973,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
34718
35973
 
34719
35974
  // Close mobile landing on Escape
34720
35975
  document.addEventListener('keydown', function (e) {
34721
- if (e.key === 'Escape' && _this13.isMobileLandingOpen) {
35976
+ if (e.key === 'Escape' && _this14.isMobileLandingOpen) {
34722
35977
  closeMobileLanding();
34723
35978
  }
34724
35979
  });
@@ -34734,19 +35989,19 @@ var TTPChatWidget = /*#__PURE__*/function () {
34734
35989
  _prevIsMobile = nowMobile;
34735
35990
  if (nowMobile) {
34736
35991
  // 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');
35992
+ if (_this14.isOpen) {
35993
+ var _this14$shadowRoot, _this14$shadowRoot2;
35994
+ var panel = (_this14$shadowRoot = _this14.shadowRoot) === null || _this14$shadowRoot === void 0 ? void 0 : _this14$shadowRoot.getElementById('text-chat-panel');
34740
35995
  if (panel) {
34741
35996
  panel.classList.remove('open');
34742
- _this13.isOpen = false;
35997
+ _this14.isOpen = false;
34743
35998
  }
34744
- var pill = (_this13$shadowRoot2 = _this13.shadowRoot) === null || _this13$shadowRoot2 === void 0 ? void 0 : _this13$shadowRoot2.getElementById('text-chat-button');
35999
+ var pill = (_this14$shadowRoot2 = _this14.shadowRoot) === null || _this14$shadowRoot2 === void 0 ? void 0 : _this14$shadowRoot2.getElementById('text-chat-button');
34745
36000
  if (pill) pill.classList.remove('open');
34746
36001
  }
34747
36002
  } else {
34748
36003
  // Switched mobile → desktop: close mobile landing/FAB so only desktop pill shows
34749
- if (_this13.isMobileLandingOpen) {
36004
+ if (_this14.isMobileLandingOpen) {
34750
36005
  closeMobileLanding();
34751
36006
  }
34752
36007
  // Ensure mobile FAB is reset to CSS-controlled state (media query hides it on desktop)
@@ -34758,10 +36013,10 @@ var TTPChatWidget = /*#__PURE__*/function () {
34758
36013
  }, {
34759
36014
  key: "setupKeyboardNavigation",
34760
36015
  value: function setupKeyboardNavigation() {
34761
- var _this14 = this;
36016
+ var _this15 = this;
34762
36017
  document.addEventListener('keydown', function (e) {
34763
- if (e.key === 'Escape' && _this14.isOpen) {
34764
- _this14.togglePanel();
36018
+ if (e.key === 'Escape' && _this15.isOpen) {
36019
+ _this15.togglePanel();
34765
36020
  }
34766
36021
  });
34767
36022
  }
@@ -34772,7 +36027,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
34772
36027
  }, {
34773
36028
  key: "setupPromptAnimation",
34774
36029
  value: function setupPromptAnimation() {
34775
- var _this15 = this;
36030
+ var _this16 = this;
34776
36031
  if (!this.shadowRoot) return;
34777
36032
  var promptConfig = this.config.promptAnimation || {};
34778
36033
  // Default to disabled if not specified (enabled === true means explicitly enabled)
@@ -34802,7 +36057,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
34802
36057
  // Auto-hide after configured seconds (only if widget is closed)
34803
36058
  if (!this.isOpen && promptConfig.hideAfterSeconds !== null && promptConfig.hideAfterSeconds > 0) {
34804
36059
  this.promptAutoHideTimer = setTimeout(function () {
34805
- _this15.hidePrompt();
36060
+ _this16.hidePrompt();
34806
36061
  }, promptConfig.hideAfterSeconds * 1000);
34807
36062
  }
34808
36063
 
@@ -34814,7 +36069,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
34814
36069
  // Wrap the callback to also hide pulse rings
34815
36070
  this.config.onConversationStart = function () {
34816
36071
  // Hide pulse rings when call starts
34817
- var pulseRings = _this15.shadowRoot.getElementById('prompt-pulse-rings');
36072
+ var pulseRings = _this16.shadowRoot.getElementById('prompt-pulse-rings');
34818
36073
  if (pulseRings) pulseRings.style.display = 'none';
34819
36074
 
34820
36075
  // Call original callback if it exists
@@ -34826,7 +36081,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
34826
36081
  // Also listen to recordingStarted event from SDK if available
34827
36082
  if (this.voiceInterface.sdk.voiceSDK) {
34828
36083
  this.voiceInterface.sdk.voiceSDK.on('recordingStarted', function () {
34829
- var pulseRings = _this15.shadowRoot.getElementById('prompt-pulse-rings');
36084
+ var pulseRings = _this16.shadowRoot.getElementById('prompt-pulse-rings');
34830
36085
  if (pulseRings) pulseRings.style.display = 'none';
34831
36086
  });
34832
36087
  }
@@ -34874,7 +36129,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
34874
36129
  }, {
34875
36130
  key: "showPrompt",
34876
36131
  value: function showPrompt() {
34877
- var _this16 = this;
36132
+ var _this17 = this;
34878
36133
  if (!this.shadowRoot) return;
34879
36134
  var promptConfig = this.config.promptAnimation || {};
34880
36135
  // Default to enabled if not specified
@@ -34894,8 +36149,8 @@ var TTPChatWidget = /*#__PURE__*/function () {
34894
36149
  // Function to actually show the elements
34895
36150
  var doShow = function doShow() {
34896
36151
  // 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) {
36152
+ var promptBubble = _this17.shadowRoot.getElementById('prompt-bubble');
36153
+ if (!promptBubble && _this17.shadowRoot !== document) {
34899
36154
  promptBubble = document.getElementById('prompt-bubble');
34900
36155
  }
34901
36156
  if (promptBubble) {
@@ -34909,8 +36164,8 @@ var TTPChatWidget = /*#__PURE__*/function () {
34909
36164
 
34910
36165
  // Show pulse rings if enabled
34911
36166
  if (promptConfig.showPulseRings !== false) {
34912
- var _pulseRings = _this16.shadowRoot.getElementById('prompt-pulse-rings');
34913
- if (!_pulseRings && _this16.shadowRoot !== document) {
36167
+ var _pulseRings = _this17.shadowRoot.getElementById('prompt-pulse-rings');
36168
+ if (!_pulseRings && _this17.shadowRoot !== document) {
34914
36169
  _pulseRings = document.getElementById('prompt-pulse-rings');
34915
36170
  }
34916
36171
  if (_pulseRings) {
@@ -34947,7 +36202,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
34947
36202
  }
34948
36203
  // Start new timer
34949
36204
  this.promptAutoHideTimer = setTimeout(function () {
34950
- _this16.hidePrompt();
36205
+ _this17.hidePrompt();
34951
36206
  }, promptConfig.hideAfterSeconds * 1000);
34952
36207
  }
34953
36208
  }
@@ -34963,7 +36218,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
34963
36218
  }, {
34964
36219
  key: "_doTogglePanel",
34965
36220
  value: function _doTogglePanel() {
34966
- var _this17 = this;
36221
+ var _this18 = this;
34967
36222
  if (!this.shadowRoot) return;
34968
36223
  this.isOpen = !this.isOpen;
34969
36224
  var panel = this.shadowRoot.getElementById('text-chat-panel');
@@ -34977,7 +36232,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
34977
36232
  if ((_this$config$position8 = this.config.position) !== null && _this$config$position8 !== void 0 && _this$config$position8.draggable) {
34978
36233
  this._applyUnitOffset();
34979
36234
  requestAnimationFrame(function () {
34980
- return _this17._reclampUnit();
36235
+ return _this18._reclampUnit();
34981
36236
  });
34982
36237
  }
34983
36238
  }
@@ -34994,7 +36249,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
34994
36249
  // Panel is opening - hide prompt
34995
36250
  this.hidePrompt();
34996
36251
  setTimeout(function () {
34997
- var input = _this17.shadowRoot.getElementById('messageInput');
36252
+ var input = _this18.shadowRoot.getElementById('messageInput');
34998
36253
  if (input) input.focus();
34999
36254
  }, 100);
35000
36255
  } else {
@@ -35006,13 +36261,13 @@ var TTPChatWidget = /*#__PURE__*/function () {
35006
36261
  // Panel is closing - show prompt (if enabled)
35007
36262
  this.showPrompt();
35008
36263
  setTimeout(function () {
35009
- var _this17$config$prompt;
35010
- var promptBubble = _this17.shadowRoot.getElementById('prompt-bubble');
36264
+ var _this18$config$prompt;
36265
+ var promptBubble = _this18.shadowRoot.getElementById('prompt-bubble');
35011
36266
  if (promptBubble && promptBubble.style.display === 'none') {
35012
36267
  promptBubble.style.display = 'block';
35013
36268
  }
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) {
36269
+ var pulseRings = _this18.shadowRoot.getElementById('prompt-pulse-rings');
36270
+ if (pulseRings && ((_this18$config$prompt = _this18.config.promptAnimation) === null || _this18$config$prompt === void 0 ? void 0 : _this18$config$prompt.showPulseRings) !== false) {
35016
36271
  if (pulseRings.style.display === 'none') {
35017
36272
  pulseRings.style.display = 'block';
35018
36273
  }
@@ -35204,8 +36459,8 @@ var TTPChatWidget = /*#__PURE__*/function () {
35204
36459
  }, {
35205
36460
  key: "showBackButtonWarning",
35206
36461
  value: function showBackButtonWarning() {
35207
- var _this$voiceInterface6, _this$voiceInterface$;
35208
- if (!((_this$voiceInterface6 = this.voiceInterface) !== null && _this$voiceInterface6 !== void 0 && _this$voiceInterface6.isActive)) return;
36462
+ var _this$voiceInterface7, _this$voiceInterface$;
36463
+ if (!((_this$voiceInterface7 = this.voiceInterface) !== null && _this$voiceInterface7 !== void 0 && _this$voiceInterface7.isActive)) return;
35209
36464
 
35210
36465
  // Show warning message in the voice interface
35211
36466
  if ((_this$voiceInterface$ = this.voiceInterface.desktop) !== null && _this$voiceInterface$ !== void 0 && _this$voiceInterface$.showBackButtonWarning) {
@@ -35280,13 +36535,13 @@ var TTPChatWidget = /*#__PURE__*/function () {
35280
36535
  value: function updateConfig(newConfig) {
35281
36536
  var _this$config3,
35282
36537
  _this$config4,
35283
- _this$config$flavor2,
36538
+ _this$config$flavor3,
35284
36539
  _this$config$voice8,
35285
36540
  _this$config$voice9,
35286
36541
  _this$config$voice0,
35287
36542
  _this$config$voice1,
35288
36543
  _this$config$voice10,
35289
- _this18 = this;
36544
+ _this19 = this;
35290
36545
  // Deep merge nested objects
35291
36546
  var mergedConfig = _objectSpread({}, this.config);
35292
36547
 
@@ -35484,7 +36739,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
35484
36739
  // Recreate interfaces with new config (after mergeWithDefaults, so language is current)
35485
36740
  // Ensure language is correctly passed - use the merged config value
35486
36741
  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';
36742
+ 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
36743
  var voiceConfig = _objectSpread(_objectSpread(_objectSpread({}, this.config), this.config.voice), {}, {
35489
36744
  agentName: this.config.agentName,
35490
36745
  language: currentLanguage,
@@ -35504,20 +36759,20 @@ var TTPChatWidget = /*#__PURE__*/function () {
35504
36759
  outputBitDepth: ((_this$config$voice10 = this.config.voice) === null || _this$config$voice10 === void 0 ? void 0 : _this$config$voice10.outputBitDepth) || 16,
35505
36760
  desktopMinimizedEcommerceStripUi: desktopMinimizedEcommerceStripUi,
35506
36761
  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);
36762
+ var _this19$_ecommerceVoi;
36763
+ (_this19$_ecommerceVoi = _this19._ecommerceVoiceStrip) === null || _this19$_ecommerceVoi === void 0 || _this19$_ecommerceVoi.show();
36764
+ if (_this19.isOpen) _this19.minimize();
36765
+ _this19._setPanelFooterVisible(false);
36766
+ _this19._setEcommerceStripLauncherVisible(false);
35512
36767
  } : undefined,
35513
36768
  onDesktopMinimizedStripLauncherRestore: desktopMinimizedEcommerceStripUi ? function () {
35514
- return _this18._restoreDesktopEcommerceStripChrome();
36769
+ return _this19._restoreDesktopEcommerceStripChrome();
35515
36770
  } : undefined,
35516
36771
  // Pass event callbacks to voice interface (forward only; panel is not auto-minimized on call start)
35517
36772
  onConversationStart: function onConversationStart() {
35518
- if (typeof _this18.config.onConversationStart === 'function') {
35519
- var _this18$config;
35520
- (_this18$config = _this18.config).onConversationStart.apply(_this18$config, arguments);
36773
+ if (typeof _this19.config.onConversationStart === 'function') {
36774
+ var _this19$config;
36775
+ (_this19$config = _this19.config).onConversationStart.apply(_this19$config, arguments);
35521
36776
  }
35522
36777
  },
35523
36778
  onConversationEnd: this.config.onConversationEnd,
@@ -35526,48 +36781,48 @@ var TTPChatWidget = /*#__PURE__*/function () {
35526
36781
  onAudioStoppedPlaying: this.config.onAudioStoppedPlaying,
35527
36782
  onSubtitleDisplay: this.config.onSubtitleDisplay,
35528
36783
  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';
36784
+ var _this19$config$behavi;
36785
+ if (_this19._isDomainErrorVisible()) return;
36786
+ var widgetMode = ((_this19$config$behavi = _this19.config.behavior) === null || _this19$config$behavi === void 0 ? void 0 : _this19$config$behavi.mode) || 'unified';
35532
36787
  if (widgetMode === 'unified') {
35533
36788
  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');
36789
+ var _this19$shadowRoot, _this19$shadowRoot2;
36790
+ var panel = (_this19$shadowRoot = _this19.shadowRoot) === null || _this19$shadowRoot === void 0 ? void 0 : _this19$shadowRoot.getElementById('text-chat-panel');
35536
36791
  if (panel) {
35537
36792
  panel.classList.remove('open');
35538
- _this18.isOpen = false;
36793
+ _this19.isOpen = false;
35539
36794
  }
35540
- var fab = (_this18$shadowRoot2 = _this18.shadowRoot) === null || _this18$shadowRoot2 === void 0 ? void 0 : _this18$shadowRoot2.querySelector('.ttp-mobile-fab');
36795
+ var fab = (_this19$shadowRoot2 = _this19.shadowRoot) === null || _this19$shadowRoot2 === void 0 ? void 0 : _this19$shadowRoot2.querySelector('.ttp-mobile-fab');
35541
36796
  if (fab) fab.style.display = '';
35542
36797
  }
35543
- _this18.showLanding();
36798
+ _this19.showLanding();
35544
36799
  }
35545
36800
  },
35546
36801
  onShowVoice: function onShowVoice() {
35547
- _this18.showVoice();
36802
+ _this19.showVoice();
35548
36803
  },
35549
36804
  onShowLanding: function onShowLanding() {
35550
- _this18.showLanding();
36805
+ _this19.showLanding();
35551
36806
  },
35552
36807
  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';
36808
+ var _this19$config$behavi2;
36809
+ if (_this19._isDomainErrorVisible()) return;
36810
+ var widgetMode = ((_this19$config$behavi2 = _this19.config.behavior) === null || _this19$config$behavi2 === void 0 ? void 0 : _this19$config$behavi2.mode) || 'unified';
35556
36811
  if (widgetMode === 'voice-only') {
35557
- _this18.showVoice();
36812
+ _this19.showVoice();
35558
36813
  return;
35559
36814
  }
35560
36815
  if (widgetMode !== 'unified') return;
35561
36816
  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');
36817
+ var _this19$shadowRoot3;
36818
+ var panel = (_this19$shadowRoot3 = _this19.shadowRoot) === null || _this19$shadowRoot3 === void 0 ? void 0 : _this19$shadowRoot3.getElementById('text-chat-panel');
35564
36819
  if (panel) {
35565
36820
  panel.classList.remove('open');
35566
- _this18.isOpen = false;
36821
+ _this19.isOpen = false;
35567
36822
  }
35568
- _this18._openMobileCallTextLanding();
36823
+ _this19._openMobileCallTextLanding();
35569
36824
  } else {
35570
- _this18.showLanding();
36825
+ _this19.showLanding();
35571
36826
  }
35572
36827
  },
35573
36828
  direction: this.getEffectiveTextDirection()
@@ -35614,9 +36869,9 @@ var TTPChatWidget = /*#__PURE__*/function () {
35614
36869
  }, {
35615
36870
  key: "_initEcommerceVoiceStrip",
35616
36871
  value: function _initEcommerceVoiceStrip() {
35617
- var _this19 = this,
36872
+ var _this20 = this,
35618
36873
  _this$config$position9,
35619
- _this$voiceInterface7;
36874
+ _this$voiceInterface8;
35620
36875
  var flavor = this.config.flavor && _typeof(this.config.flavor) === 'object' ? this.config.flavor : null;
35621
36876
  if (window.innerWidth <= 768) return;
35622
36877
  if (!flavor || flavor.callView !== 'minimized') return;
@@ -35637,34 +36892,44 @@ var TTPChatWidget = /*#__PURE__*/function () {
35637
36892
  endCallButtonColor: v.endCallButtonColor || '#ef4444'
35638
36893
  },
35639
36894
  getVoiceUiState: function getVoiceUiState() {
35640
- var _this19$voiceInterfac, _this19$voiceInterfac2, _this19$voiceInterfac3;
36895
+ var _this20$voiceInterfac, _this20$voiceInterfac2, _this20$voiceInterfac3;
35641
36896
  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)
36897
+ isMicMuted: !!((_this20$voiceInterfac = _this20.voiceInterface) !== null && _this20$voiceInterfac !== void 0 && _this20$voiceInterfac.isMicMuted),
36898
+ isPaused: !!((_this20$voiceInterfac2 = _this20.voiceInterface) !== null && _this20$voiceInterfac2 !== void 0 && _this20$voiceInterfac2.isPaused),
36899
+ isSpeakerMuted: !!((_this20$voiceInterfac3 = _this20.voiceInterface) !== null && _this20$voiceInterfac3 !== void 0 && _this20$voiceInterfac3.isSpeakerMuted)
35645
36900
  };
35646
36901
  },
35647
36902
  onAbout: function onAbout() {
35648
- return _this19._openAboutModal();
36903
+ return _this20._openAboutModal();
36904
+ },
36905
+ onSettings: function onSettings() {
36906
+ return _this20._openCallSettingsModal();
36907
+ },
36908
+ onRecipeSelect: function onRecipeSelect(recipe) {
36909
+ var _this20$_flavor, _this20$_flavor$_getR, _this20$_flavor$_getR2;
36910
+ (_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, {
36911
+ recipe: recipe,
36912
+ reshow: true
36913
+ });
35649
36914
  },
35650
36915
  onHangup: function onHangup() {
35651
- if (_this19.voiceInterface) _this19.voiceInterface.endVoiceCall();
36916
+ if (_this20.voiceInterface) _this20.voiceInterface.endVoiceCall();
35652
36917
  },
35653
36918
  onToggleMute: function onToggleMute() {
35654
- if (_this19.voiceInterface) _this19.voiceInterface.toggleMute();
36919
+ if (_this20.voiceInterface) _this20.voiceInterface.toggleMute();
35655
36920
  strip.syncFromVoice();
35656
36921
  },
35657
36922
  onTogglePause: function onTogglePause() {
35658
- if (_this19.voiceInterface) _this19.voiceInterface.togglePause();
36923
+ if (_this20.voiceInterface) _this20.voiceInterface.togglePause();
35659
36924
  strip.syncFromVoice();
35660
36925
  },
35661
36926
  onToggleSpeaker: function onToggleSpeaker() {
35662
- if (_this19.voiceInterface) _this19.voiceInterface.toggleSpeaker();
36927
+ if (_this20.voiceInterface) _this20.voiceInterface.toggleSpeaker();
35663
36928
  strip.syncFromVoice();
35664
36929
  },
35665
36930
  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;
36931
+ var _this20$voiceInterfac4;
36932
+ 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
36933
  if (voiceSDK && typeof voiceSDK.sendMessage === 'function') {
35669
36934
  voiceSDK.sendMessage({
35670
36935
  t: 'text_inject',
@@ -35674,6 +36939,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
35674
36939
  }
35675
36940
  });
35676
36941
  this._ecommerceVoiceStrip = strip;
36942
+ strip.bindRecipeStore(this.getRecipeStore());
35677
36943
 
35678
36944
  // Make the strip draggable too when position.draggable is enabled.
35679
36945
  if ((_this$config$position9 = this.config.position) !== null && _this$config$position9 !== void 0 && _this$config$position9.draggable) {
@@ -35681,7 +36947,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
35681
36947
  }
35682
36948
 
35683
36949
  // Show strip when conversation starts
35684
- var agentSDK = (_this$voiceInterface7 = this.voiceInterface) === null || _this$voiceInterface7 === void 0 ? void 0 : _this$voiceInterface7.sdk;
36950
+ var agentSDK = (_this$voiceInterface8 = this.voiceInterface) === null || _this$voiceInterface8 === void 0 ? void 0 : _this$voiceInterface8.sdk;
35685
36951
  if (agentSDK) {
35686
36952
  var origConvStart = agentSDK.onConversationStart;
35687
36953
  agentSDK.onConversationStart = function () {
@@ -35719,7 +36985,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
35719
36985
  var _this$voiceInterface$2;
35720
36986
  var origOnCallEnd = this.voiceInterface.config.onCallEnd;
35721
36987
  this.voiceInterface.config.onCallEnd = function () {
35722
- _this19._restoreDesktopEcommerceStripChrome();
36988
+ _this20._restoreDesktopEcommerceStripChrome();
35723
36989
  if (typeof origOnCallEnd === 'function') origOnCallEnd();
35724
36990
  };
35725
36991
 
@@ -35843,7 +37109,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
35843
37109
  key: "injectData",
35844
37110
  value: (function () {
35845
37111
  var _injectData = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee6(data) {
35846
- var _this$voiceInterface8;
37112
+ var _this$voiceInterface9;
35847
37113
  var options,
35848
37114
  voiceSDK,
35849
37115
  ack,
@@ -35868,7 +37134,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
35868
37134
  throw new Error('Voice call is not active');
35869
37135
  case 2:
35870
37136
  // Check if SDK is available
35871
- voiceSDK = (_this$voiceInterface8 = this.voiceInterface) === null || _this$voiceInterface8 === void 0 ? void 0 : _this$voiceInterface8.getVoiceSDK();
37137
+ voiceSDK = (_this$voiceInterface9 = this.voiceInterface) === null || _this$voiceInterface9 === void 0 ? void 0 : _this$voiceInterface9.getVoiceSDK();
35872
37138
  if (voiceSDK) {
35873
37139
  _context6.n = 3;
35874
37140
  break;
@@ -36902,7 +38168,9 @@ var VoiceInterface = /*#__PURE__*/function () {
36902
38168
  flavor: config.flavor,
36903
38169
  // Shared client-tool handler map from TTPChatWidget (so the same handler
36904
38170
  // map serves both voice and text channels).
36905
- sharedToolHandlers: config.sharedToolHandlers
38171
+ sharedToolHandlers: config.sharedToolHandlers,
38172
+ // Persisted in-call settings provider (voice speed / response length).
38173
+ callSettingsProvider: config.callSettingsProvider
36906
38174
  });
36907
38175
 
36908
38176
  // Initialize modules with context (delegation pattern)
@@ -41278,6 +42546,22 @@ var Templates = /*#__PURE__*/function () {
41278
42546
  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
42547
  }
41280
42548
 
42549
+ /**
42550
+ * Shared in-call settings gear button markup. Opens the call-settings dialog
42551
+ * (voice speed / response length sliders) wired by TTPChatWidget. Shares the
42552
+ * .ttp-info-btn styling class; .ttp-settings-btn is the wiring discriminator.
42553
+ * @param {string} [extraClass] - extra class(es) for positioning per view
42554
+ * @returns {string}
42555
+ */
42556
+ }, {
42557
+ key: "settingsButton",
42558
+ value: function settingsButton() {
42559
+ var extraClass = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
42560
+ var cls = extraClass ? "ttp-info-btn ttp-settings-btn ".concat(extraClass) : 'ttp-info-btn ttp-settings-btn';
42561
+ var label = this.t('settingsTitle') || 'Call settings';
42562
+ 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>");
42563
+ }
42564
+
41281
42565
  /**
41282
42566
  * Generate HTML for voice interface.
41283
42567
  * Automatically detects mobile vs desktop based on voiceInterface.isMobile
@@ -41308,7 +42592,7 @@ var Templates = /*#__PURE__*/function () {
41308
42592
  var transcriptPlaceholder = this.t('transcriptWillAppear') || '';
41309
42593
  var transcriptDir = (0,_rtlText_js__WEBPACK_IMPORTED_MODULE_0__.dirForTranscriptSnippet)(transcriptPlaceholder, this.config);
41310
42594
  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>");
42595
+ 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
42596
  }
41313
42597
 
41314
42598
  /**
@@ -41325,7 +42609,7 @@ var Templates = /*#__PURE__*/function () {
41325
42609
  var headerAvatarUrl = resolveIdleHeaderAvatarUrl(this.config);
41326
42610
  var mobileAvatarInner = headerAvatarUrl ? "<img src=\"".concat(escapeHtmlAttr(headerAvatarUrl), "\" alt=\"\" class=\"voice-avatar-img\" />") : '🤖';
41327
42611
  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) {
42612
+ 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
42613
  return "<div class=\"mobile-waveform-bar\" data-index=\"".concat(i, "\"></div>");
41330
42614
  }).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
42615
  }
@@ -42174,7 +43458,14 @@ __webpack_require__.r(__webpack_exports__);
42174
43458
  "aboutTitle": "About",
42175
43459
  "aboutVersion": "SDK version",
42176
43460
  "aboutConversationId": "Last conversation ID",
42177
- "aboutNoConversation": "No conversation yet"
43461
+ "aboutNoConversation": "No conversation yet",
43462
+ "settingsTitle": "Call settings",
43463
+ "settingsVoiceSpeed": "Voice speed",
43464
+ "settingsResponseLength": "Response length",
43465
+ "lengthDetailed": "Detailed",
43466
+ "lengthNormal": "Normal",
43467
+ "lengthBrief": "Brief",
43468
+ "lengthVeryBrief": "Very brief"
42178
43469
  },
42179
43470
  "he": {
42180
43471
  "landingTitle": "איך תרצה לתקשר?",
@@ -42211,7 +43502,14 @@ __webpack_require__.r(__webpack_exports__);
42211
43502
  "aboutTitle": "אודות",
42212
43503
  "aboutVersion": "גרסת SDK",
42213
43504
  "aboutConversationId": "מזהה שיחה אחרון",
42214
- "aboutNoConversation": "אין שיחה עדיין"
43505
+ "aboutNoConversation": "אין שיחה עדיין",
43506
+ "settingsTitle": "הגדרות שיחה",
43507
+ "settingsVoiceSpeed": "מהירות דיבור",
43508
+ "settingsResponseLength": "אורך תשובות",
43509
+ "lengthDetailed": "מפורט",
43510
+ "lengthNormal": "רגיל",
43511
+ "lengthBrief": "קצר",
43512
+ "lengthVeryBrief": "קצר מאוד"
42215
43513
  },
42216
43514
  "ar": {
42217
43515
  "landingTitle": "كيف تريد التواصل؟",
@@ -42248,7 +43546,14 @@ __webpack_require__.r(__webpack_exports__);
42248
43546
  "aboutTitle": "حول",
42249
43547
  "aboutVersion": "إصدار SDK",
42250
43548
  "aboutConversationId": "معرّف آخر محادثة",
42251
- "aboutNoConversation": "لا توجد محادثة بعد"
43549
+ "aboutNoConversation": "لا توجد محادثة بعد",
43550
+ "settingsTitle": "إعدادات المكالمة",
43551
+ "settingsVoiceSpeed": "سرعة الصوت",
43552
+ "settingsResponseLength": "طول الإجابات",
43553
+ "lengthDetailed": "مفصّل",
43554
+ "lengthNormal": "عادي",
43555
+ "lengthBrief": "مختصر",
43556
+ "lengthVeryBrief": "مختصر جداً"
42252
43557
  },
42253
43558
  "ru": {
42254
43559
  "landingTitle": "Как вы хотите общаться?",
@@ -42285,7 +43590,14 @@ __webpack_require__.r(__webpack_exports__);
42285
43590
  "aboutTitle": "О приложении",
42286
43591
  "aboutVersion": "Версия SDK",
42287
43592
  "aboutConversationId": "ID последнего разговора",
42288
- "aboutNoConversation": "Разговоров пока нет"
43593
+ "aboutNoConversation": "Разговоров пока нет",
43594
+ "settingsTitle": "Настройки звонка",
43595
+ "settingsVoiceSpeed": "Скорость речи",
43596
+ "settingsResponseLength": "Длина ответов",
43597
+ "lengthDetailed": "Подробно",
43598
+ "lengthNormal": "Обычно",
43599
+ "lengthBrief": "Кратко",
43600
+ "lengthVeryBrief": "Очень кратко"
42289
43601
  },
42290
43602
  "es": {
42291
43603
  "landingTitle": "¿Cómo te gustaría comunicarte?",
@@ -42322,7 +43634,14 @@ __webpack_require__.r(__webpack_exports__);
42322
43634
  "aboutTitle": "Acerca de",
42323
43635
  "aboutVersion": "Versión del SDK",
42324
43636
  "aboutConversationId": "ID de última conversación",
42325
- "aboutNoConversation": "Aún no hay conversación"
43637
+ "aboutNoConversation": "Aún no hay conversación",
43638
+ "settingsTitle": "Ajustes de llamada",
43639
+ "settingsVoiceSpeed": "Velocidad de voz",
43640
+ "settingsResponseLength": "Longitud de respuesta",
43641
+ "lengthDetailed": "Detallada",
43642
+ "lengthNormal": "Normal",
43643
+ "lengthBrief": "Breve",
43644
+ "lengthVeryBrief": "Muy breve"
42326
43645
  },
42327
43646
  "fr": {
42328
43647
  "landingTitle": "Comment souhaitez-vous communiquer?",
@@ -42359,7 +43678,14 @@ __webpack_require__.r(__webpack_exports__);
42359
43678
  "aboutTitle": "À propos",
42360
43679
  "aboutVersion": "Version du SDK",
42361
43680
  "aboutConversationId": "ID de la dernière conversation",
42362
- "aboutNoConversation": "Aucune conversation pour l'instant"
43681
+ "aboutNoConversation": "Aucune conversation pour l'instant",
43682
+ "settingsTitle": "Paramètres d'appel",
43683
+ "settingsVoiceSpeed": "Vitesse de la voix",
43684
+ "settingsResponseLength": "Longueur des réponses",
43685
+ "lengthDetailed": "Détaillée",
43686
+ "lengthNormal": "Normale",
43687
+ "lengthBrief": "Brève",
43688
+ "lengthVeryBrief": "Très brève"
42363
43689
  },
42364
43690
  "de": {
42365
43691
  "landingTitle": "Wie möchten Sie kommunizieren?",
@@ -42396,7 +43722,14 @@ __webpack_require__.r(__webpack_exports__);
42396
43722
  "aboutTitle": "Über",
42397
43723
  "aboutVersion": "SDK-Version",
42398
43724
  "aboutConversationId": "Letzte Konversations-ID",
42399
- "aboutNoConversation": "Noch keine Konversation"
43725
+ "aboutNoConversation": "Noch keine Konversation",
43726
+ "settingsTitle": "Anrufeinstellungen",
43727
+ "settingsVoiceSpeed": "Sprechgeschwindigkeit",
43728
+ "settingsResponseLength": "Antwortlänge",
43729
+ "lengthDetailed": "Ausführlich",
43730
+ "lengthNormal": "Normal",
43731
+ "lengthBrief": "Kurz",
43732
+ "lengthVeryBrief": "Sehr kurz"
42400
43733
  }
42401
43734
  });
42402
43735