ttp-agent-sdk 2.35.0 → 2.38.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8349,8 +8349,18 @@ function _inherits(t, e) { if ("function" != typeof e && null !== e) throw new T
8349
8349
  function _setPrototypeOf(t, e) { return _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function (t, e) { return t.__proto__ = e, t; }, _setPrototypeOf(t, e); }
8350
8350
  /**
8351
8351
  * AudioRecorder - Handles audio recording using AudioWorklet
8352
+ *
8353
+ * iOS FIX: Uses a module-level shared AudioContext to avoid the iOS WebKit bug
8354
+ * where closing and immediately recreating an AudioContext causes the new one
8355
+ * to fail silently (getUserMedia returns a "live" stream that produces no audio).
8356
+ * iOS shares a single audio session for all input/output, and AudioContext.close()
8357
+ * doesn't release the hardware synchronously.
8352
8358
  */
8353
8359
 
8360
+ var _sharedRecorderContext = null;
8361
+ var _sharedRecorderSampleRate = null;
8362
+ var _sharedRecorderWorkletLoaded = false;
8363
+ var _sharedRecorderWorkletPath = null;
8354
8364
  var AudioRecorder = /*#__PURE__*/function (_EventEmitter) {
8355
8365
  function AudioRecorder(config) {
8356
8366
  var _this;
@@ -8361,6 +8371,7 @@ var AudioRecorder = /*#__PURE__*/function (_EventEmitter) {
8361
8371
  _this.audioWorkletNode = null;
8362
8372
  _this.mediaStream = null;
8363
8373
  _this.isRecording = false;
8374
+ _this._starting = false;
8364
8375
  return _this;
8365
8376
  }
8366
8377
 
@@ -8527,20 +8538,28 @@ var AudioRecorder = /*#__PURE__*/function (_EventEmitter) {
8527
8538
  value: (function () {
8528
8539
  var _start = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee3() {
8529
8540
  var _this2 = this;
8530
- var permissionState, sampleRate, defaultConstraints, audioConstraints, isConnected, error, _isConnected, _error, processorPath, _isConnected2, _error2, source, userMessage, _permissionState, enhancedError, _t3;
8541
+ var permissionState, sampleRate, defaultConstraints, audioConstraints, isConnected, error, canReuseContext, _isConnected, _error, processorPath, _isConnected2, _error2, source, userMessage, _permissionState, enhancedError, _t3, _t4;
8531
8542
  return _regenerator().w(function (_context3) {
8532
8543
  while (1) switch (_context3.p = _context3.n) {
8533
8544
  case 0:
8534
- _context3.p = 0;
8545
+ if (!this._starting) {
8546
+ _context3.n = 1;
8547
+ break;
8548
+ }
8549
+ console.warn('⚠️ AudioRecorder: start() already in progress, ignoring duplicate call');
8550
+ return _context3.a(2);
8551
+ case 1:
8552
+ this._starting = true;
8553
+ _context3.p = 2;
8535
8554
  // Always try to request permission - we call getUserMedia every time
8536
8555
  // This ensures the prompt appears if the permission state allows it
8537
8556
  // Even if previously denied, the user might have changed settings, so we always try
8538
8557
  console.log('🎤 AudioRecorder: Requesting microphone access...');
8539
8558
 
8540
8559
  // Check permission state for logging/debugging (but don't block the request)
8541
- _context3.n = 1;
8560
+ _context3.n = 3;
8542
8561
  return this.checkPermissionState();
8543
- case 1:
8562
+ case 3:
8544
8563
  permissionState = _context3.v;
8545
8564
  if (permissionState) {
8546
8565
  console.log("\uD83D\uDCCB AudioRecorder: Current permission state: ".concat(permissionState));
@@ -8558,22 +8577,22 @@ var AudioRecorder = /*#__PURE__*/function (_EventEmitter) {
8558
8577
  autoGainControl: true
8559
8578
  };
8560
8579
  audioConstraints = this.config.audioConstraints ? _objectSpread(_objectSpread({}, defaultConstraints), this.config.audioConstraints) : defaultConstraints;
8561
- _context3.n = 2;
8580
+ _context3.n = 4;
8562
8581
  return navigator.mediaDevices.getUserMedia({
8563
8582
  audio: _objectSpread({
8564
8583
  sampleRate: sampleRate,
8565
8584
  channelCount: 1
8566
8585
  }, audioConstraints)
8567
8586
  });
8568
- case 2:
8587
+ case 4:
8569
8588
  this.mediaStream = _context3.v;
8570
8589
  if (!(this.config.checkConnection && typeof this.config.checkConnection === 'function')) {
8571
- _context3.n = 3;
8590
+ _context3.n = 5;
8572
8591
  break;
8573
8592
  }
8574
8593
  isConnected = this.config.checkConnection();
8575
8594
  if (isConnected) {
8576
- _context3.n = 3;
8595
+ _context3.n = 5;
8577
8596
  break;
8578
8597
  }
8579
8598
  // Silent cleanup - server rejected, this is expected
@@ -8586,81 +8605,121 @@ var AudioRecorder = /*#__PURE__*/function (_EventEmitter) {
8586
8605
  error.name = 'ServerRejected';
8587
8606
  error.isServerRejection = true;
8588
8607
  throw error;
8589
- case 3:
8590
- // Create AudioContext with the same sample rate
8608
+ case 5:
8609
+ // Reuse shared AudioContext if available and compatible (iOS fix)
8610
+ // iOS WebKit has a bug where closing and immediately recreating an AudioContext
8611
+ // causes the new one to fail silently - the audio hardware isn't released in time.
8612
+ // By reusing the same AudioContext across calls, we avoid this race condition entirely.
8613
+ canReuseContext = _sharedRecorderContext && _sharedRecorderContext.state !== 'closed' && _sharedRecorderSampleRate === sampleRate;
8614
+ if (!canReuseContext) {
8615
+ _context3.n = 7;
8616
+ break;
8617
+ }
8618
+ console.log('♻️ AudioRecorder: Reusing shared AudioContext (iOS-safe)');
8619
+ this.audioContext = _sharedRecorderContext;
8620
+ if (!(this.audioContext.state === 'suspended')) {
8621
+ _context3.n = 6;
8622
+ break;
8623
+ }
8624
+ _context3.n = 6;
8625
+ return this.audioContext.resume();
8626
+ case 6:
8627
+ _context3.n = 13;
8628
+ break;
8629
+ case 7:
8630
+ if (!(_sharedRecorderContext && _sharedRecorderContext.state !== 'closed')) {
8631
+ _context3.n = 12;
8632
+ break;
8633
+ }
8634
+ console.log('🔄 AudioRecorder: Sample rate changed, closing old shared AudioContext');
8635
+ _context3.p = 8;
8636
+ _context3.n = 9;
8637
+ return _sharedRecorderContext.close();
8638
+ case 9:
8639
+ _context3.n = 11;
8640
+ break;
8641
+ case 10:
8642
+ _context3.p = 10;
8643
+ _t3 = _context3.v;
8644
+ console.warn('⚠️ AudioRecorder: Error closing old shared context:', _t3);
8645
+ case 11:
8646
+ _sharedRecorderWorkletLoaded = false;
8647
+ _sharedRecorderWorkletPath = null;
8648
+ case 12:
8591
8649
  this.audioContext = new (window.AudioContext || window.webkitAudioContext)({
8592
8650
  sampleRate: sampleRate
8593
8651
  });
8594
-
8595
- // Resume AudioContext if suspended
8652
+ _sharedRecorderContext = this.audioContext;
8653
+ _sharedRecorderSampleRate = sampleRate;
8654
+ _sharedRecorderWorkletLoaded = false;
8655
+ console.log('🎵 AudioRecorder: Created new shared AudioContext at', sampleRate, 'Hz');
8656
+ case 13:
8596
8657
  if (!(this.audioContext.state === 'suspended')) {
8597
- _context3.n = 4;
8658
+ _context3.n = 14;
8598
8659
  break;
8599
8660
  }
8600
- _context3.n = 4;
8661
+ _context3.n = 14;
8601
8662
  return this.audioContext.resume();
8602
- case 4:
8663
+ case 14:
8603
8664
  if (!(this.config.checkConnection && typeof this.config.checkConnection === 'function')) {
8604
- _context3.n = 6;
8665
+ _context3.n = 15;
8605
8666
  break;
8606
8667
  }
8607
8668
  _isConnected = this.config.checkConnection();
8608
8669
  if (_isConnected) {
8609
- _context3.n = 6;
8670
+ _context3.n = 15;
8610
8671
  break;
8611
8672
  }
8612
8673
  // Silent cleanup - server rejected, this is expected
8613
- // Clean up media stream and AudioContext
8674
+ // Stop media stream but DON'T close the shared AudioContext
8614
8675
  this.mediaStream.getTracks().forEach(function (track) {
8615
8676
  return track.stop();
8616
8677
  });
8617
8678
  this.mediaStream = null;
8618
- if (!(this.audioContext && this.audioContext.state !== 'closed')) {
8619
- _context3.n = 5;
8620
- break;
8621
- }
8622
- _context3.n = 5;
8623
- return this.audioContext.close();
8624
- case 5:
8625
8679
  this.audioContext = null;
8626
8680
  _error = new Error('Connection lost - server may have rejected the call');
8627
8681
  _error.name = 'ServerRejected';
8628
8682
  _error.isServerRejection = true;
8629
8683
  throw _error;
8630
- case 6:
8684
+ case 15:
8631
8685
  // Get the audio processor path
8632
- processorPath = this.getAudioProcessorPath(); // Load AudioWorklet module
8633
- _context3.n = 7;
8686
+ processorPath = this.getAudioProcessorPath(); // Load AudioWorklet module (skip if already loaded on this context)
8687
+ if (!(!_sharedRecorderWorkletLoaded || _sharedRecorderWorkletPath !== processorPath)) {
8688
+ _context3.n = 17;
8689
+ break;
8690
+ }
8691
+ _context3.n = 16;
8634
8692
  return this.audioContext.audioWorklet.addModule(processorPath);
8635
- case 7:
8693
+ case 16:
8694
+ _sharedRecorderWorkletLoaded = true;
8695
+ _sharedRecorderWorkletPath = processorPath;
8696
+ console.log('✅ AudioRecorder: AudioWorklet module loaded');
8697
+ _context3.n = 18;
8698
+ break;
8699
+ case 17:
8700
+ console.log('♻️ AudioRecorder: AudioWorklet module already loaded, skipping');
8701
+ case 18:
8636
8702
  if (!(this.config.checkConnection && typeof this.config.checkConnection === 'function')) {
8637
- _context3.n = 9;
8703
+ _context3.n = 19;
8638
8704
  break;
8639
8705
  }
8640
8706
  _isConnected2 = this.config.checkConnection();
8641
8707
  if (_isConnected2) {
8642
- _context3.n = 9;
8708
+ _context3.n = 19;
8643
8709
  break;
8644
8710
  }
8645
8711
  // Silent cleanup - server rejected, this is expected
8646
- // Clean up everything
8712
+ // Stop media stream but DON'T close the shared AudioContext
8647
8713
  this.mediaStream.getTracks().forEach(function (track) {
8648
8714
  return track.stop();
8649
8715
  });
8650
8716
  this.mediaStream = null;
8651
- if (!(this.audioContext && this.audioContext.state !== 'closed')) {
8652
- _context3.n = 8;
8653
- break;
8654
- }
8655
- _context3.n = 8;
8656
- return this.audioContext.close();
8657
- case 8:
8658
8717
  this.audioContext = null;
8659
8718
  _error2 = new Error('Connection lost - server may have rejected the call');
8660
8719
  _error2.name = 'ServerRejected';
8661
8720
  _error2.isServerRejection = true;
8662
8721
  throw _error2;
8663
- case 9:
8722
+ case 19:
8664
8723
  // Create AudioWorklet node with processor options (sampleRate will be used by processor)
8665
8724
  this.audioWorkletNode = new AudioWorkletNode(this.audioContext, 'audio-processor', {
8666
8725
  processorOptions: {
@@ -8696,26 +8755,26 @@ var AudioRecorder = /*#__PURE__*/function (_EventEmitter) {
8696
8755
  console.log('✅ AudioRecorder: Microphone access granted');
8697
8756
  this.isRecording = true;
8698
8757
  this.emit('recordingStarted');
8699
- _context3.n = 15;
8758
+ _context3.n = 25;
8700
8759
  break;
8701
- case 10:
8702
- _context3.p = 10;
8703
- _t3 = _context3.v;
8704
- if (!(_t3.isServerRejection || _t3.name === 'ServerRejected')) {
8705
- _context3.n = 11;
8760
+ case 20:
8761
+ _context3.p = 20;
8762
+ _t4 = _context3.v;
8763
+ if (!(_t4.isServerRejection || _t4.name === 'ServerRejected')) {
8764
+ _context3.n = 21;
8706
8765
  break;
8707
8766
  }
8708
- throw _t3;
8709
- case 11:
8767
+ throw _t4;
8768
+ case 21:
8710
8769
  // Provide helpful error messages for permission issues
8711
- userMessage = _t3.message;
8712
- if (!(_t3.name === 'NotAllowedError' || _t3.name === 'PermissionDeniedError')) {
8713
- _context3.n = 13;
8770
+ userMessage = _t4.message;
8771
+ if (!(_t4.name === 'NotAllowedError' || _t4.name === 'PermissionDeniedError')) {
8772
+ _context3.n = 23;
8714
8773
  break;
8715
8774
  }
8716
- _context3.n = 12;
8775
+ _context3.n = 22;
8717
8776
  return this.checkPermissionState();
8718
- case 12:
8777
+ case 22:
8719
8778
  _permissionState = _context3.v;
8720
8779
  if (_permissionState === 'denied') {
8721
8780
  // Permission was explicitly blocked - browser won't show prompt again
@@ -8730,29 +8789,33 @@ var AudioRecorder = /*#__PURE__*/function (_EventEmitter) {
8730
8789
  userMessage = 'Microphone permission was denied. Click the button again to retry, or enable microphone access in your browser settings (lock icon 🔒 in address bar).';
8731
8790
  console.error('❌ AudioRecorder: Permission denied. User can try again or change in browser settings.');
8732
8791
  }
8733
- _context3.n = 14;
8792
+ _context3.n = 24;
8734
8793
  break;
8735
- case 13:
8736
- if (_t3.name === 'NotFoundError' || _t3.name === 'DevicesNotFoundError') {
8794
+ case 23:
8795
+ if (_t4.name === 'NotFoundError' || _t4.name === 'DevicesNotFoundError') {
8737
8796
  userMessage = 'No microphone found. Please connect a microphone and try again.';
8738
8797
  console.error('❌ AudioRecorder: No microphone device found.');
8739
- } else if (_t3.name === 'NotReadableError' || _t3.name === 'TrackStartError') {
8798
+ } else if (_t4.name === 'NotReadableError' || _t4.name === 'TrackStartError') {
8740
8799
  userMessage = 'Microphone is already in use by another application. Please close other apps using the microphone and try again.';
8741
8800
  console.error('❌ AudioRecorder: Microphone is busy or not readable.');
8742
- } else if (_t3.name === 'SecurityError' || _t3.name === 'PermissionDeniedError') {
8801
+ } else if (_t4.name === 'SecurityError' || _t4.name === 'PermissionDeniedError') {
8743
8802
  userMessage = 'Microphone access blocked. On mobile devices, please tap the recording button directly (not from an iframe or popup).';
8744
8803
  console.error('❌ AudioRecorder: User agent blocked microphone access. This often happens on mobile when not called from direct user interaction.');
8745
8804
  }
8746
- case 14:
8805
+ case 24:
8747
8806
  enhancedError = new Error(userMessage);
8748
- enhancedError.name = _t3.name;
8749
- enhancedError.originalError = _t3;
8807
+ enhancedError.name = _t4.name;
8808
+ enhancedError.originalError = _t4;
8750
8809
  this.emit('error', enhancedError);
8751
8810
  throw enhancedError;
8752
- case 15:
8811
+ case 25:
8812
+ _context3.p = 25;
8813
+ this._starting = false;
8814
+ return _context3.f(25);
8815
+ case 26:
8753
8816
  return _context3.a(2);
8754
8817
  }
8755
- }, _callee3, this, [[0, 10]]);
8818
+ }, _callee3, this, [[8, 10], [2, 20, 25, 26]]);
8756
8819
  }));
8757
8820
  function start() {
8758
8821
  return _start.apply(this, arguments);
@@ -8767,7 +8830,7 @@ var AudioRecorder = /*#__PURE__*/function (_EventEmitter) {
8767
8830
  key: "stop",
8768
8831
  value: (function () {
8769
8832
  var _stop = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4() {
8770
- var _t4;
8833
+ var _t5;
8771
8834
  return _regenerator().w(function (_context4) {
8772
8835
  while (1) switch (_context4.p = _context4.n) {
8773
8836
  case 0:
@@ -8833,18 +8896,22 @@ var AudioRecorder = /*#__PURE__*/function (_EventEmitter) {
8833
8896
  this.mediaStream = null;
8834
8897
  }
8835
8898
 
8836
- // Close AudioContext (this fully stops all audio processing)
8837
- if (!(this.audioContext && this.audioContext.state !== 'closed')) {
8899
+ // iOS FIX: Suspend AudioContext instead of closing it.
8900
+ // Closing and recreating AudioContexts on iOS causes the new one to fail silently
8901
+ // because iOS doesn't release the audio hardware synchronously.
8902
+ // The shared AudioContext will be reused on the next start() call.
8903
+ if (!(this.audioContext && this.audioContext.state === 'running')) {
8838
8904
  _context4.n = 6;
8839
8905
  break;
8840
8906
  }
8841
- console.log('🛑 AudioRecorder: Closing AudioContext...');
8907
+ console.log('🛑 AudioRecorder: Suspending AudioContext (kept alive for reuse)...');
8842
8908
  _context4.n = 5;
8843
- return this.audioContext.close();
8909
+ return this.audioContext.suspend();
8844
8910
  case 5:
8845
- this.audioContext = null;
8846
- console.log('✅ AudioRecorder: AudioContext closed');
8911
+ console.log('✅ AudioRecorder: AudioContext suspended');
8847
8912
  case 6:
8913
+ // Release instance reference but keep the shared context alive
8914
+ this.audioContext = null;
8848
8915
  this.audioWorkletNode = null;
8849
8916
  this.isRecording = false;
8850
8917
  this.emit('recordingStopped');
@@ -8853,9 +8920,9 @@ var AudioRecorder = /*#__PURE__*/function (_EventEmitter) {
8853
8920
  break;
8854
8921
  case 7:
8855
8922
  _context4.p = 7;
8856
- _t4 = _context4.v;
8857
- this.emit('error', _t4);
8858
- throw _t4;
8923
+ _t5 = _context4.v;
8924
+ this.emit('error', _t5);
8925
+ throw _t5;
8859
8926
  case 8:
8860
8927
  return _context4.a(2);
8861
8928
  }
@@ -8875,7 +8942,7 @@ var AudioRecorder = /*#__PURE__*/function (_EventEmitter) {
8875
8942
  key: "resumeAudioContext",
8876
8943
  value: (function () {
8877
8944
  var _resumeAudioContext = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee5() {
8878
- var audioTracks, activeTracks, _t5;
8945
+ var audioTracks, activeTracks, _t6;
8879
8946
  return _regenerator().w(function (_context5) {
8880
8947
  while (1) switch (_context5.p = _context5.n) {
8881
8948
  case 0:
@@ -8913,9 +8980,9 @@ var AudioRecorder = /*#__PURE__*/function (_EventEmitter) {
8913
8980
  break;
8914
8981
  case 4:
8915
8982
  _context5.p = 4;
8916
- _t5 = _context5.v;
8917
- console.error('❌ AudioRecorder: Failed to resume AudioContext:', _t5);
8918
- this.emit('error', _t5);
8983
+ _t6 = _context5.v;
8984
+ console.error('❌ AudioRecorder: Failed to resume AudioContext:', _t6);
8985
+ this.emit('error', _t6);
8919
8986
  case 5:
8920
8987
  return _context5.a(2);
8921
8988
  }
@@ -8940,13 +9007,32 @@ var AudioRecorder = /*#__PURE__*/function (_EventEmitter) {
8940
9007
  }
8941
9008
 
8942
9009
  /**
8943
- * Cleanup resources
9010
+ * Cleanup resources.
9011
+ * Note: Does NOT close the shared AudioContext - it's reused across instances
9012
+ * to avoid the iOS AudioContext close/create race condition.
8944
9013
  */
8945
9014
  }, {
8946
9015
  key: "destroy",
8947
9016
  value: function destroy() {
8948
9017
  this.stop();
8949
9018
  }
9019
+
9020
+ /**
9021
+ * Fully close the shared AudioContext.
9022
+ * Only call this when the widget is being completely removed from the page.
9023
+ */
9024
+ }], [{
9025
+ key: "closeSharedContext",
9026
+ value: function closeSharedContext() {
9027
+ if (_sharedRecorderContext && _sharedRecorderContext.state !== 'closed') {
9028
+ console.log('🗑️ AudioRecorder: Closing shared AudioContext');
9029
+ _sharedRecorderContext.close();
9030
+ }
9031
+ _sharedRecorderContext = null;
9032
+ _sharedRecorderSampleRate = null;
9033
+ _sharedRecorderWorkletLoaded = false;
9034
+ _sharedRecorderWorkletPath = null;
9035
+ }
8950
9036
  }]);
8951
9037
  }(_EventEmitter_js__WEBPACK_IMPORTED_MODULE_0__["default"]);
8952
9038
 
@@ -10332,6 +10418,7 @@ __webpack_require__.r(__webpack_exports__);
10332
10418
  /* harmony import */ var _CartToast_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./CartToast.js */ "./src/ecommerce/CartToast.js");
10333
10419
  /* harmony import */ var _CartSummary_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./CartSummary.js */ "./src/ecommerce/CartSummary.js");
10334
10420
  /* harmony import */ var _ecommerceStyles_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./ecommerceStyles.js */ "./src/ecommerce/ecommerceStyles.js");
10421
+ /* harmony import */ var _MobileProductCarousel_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./MobileProductCarousel.js */ "./src/ecommerce/MobileProductCarousel.js");
10335
10422
  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); }
10336
10423
  function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
10337
10424
  function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
@@ -10351,14 +10438,17 @@ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e
10351
10438
 
10352
10439
 
10353
10440
 
10441
+
10354
10442
  var EcommerceManager = /*#__PURE__*/function () {
10355
10443
  function EcommerceManager() {
10356
10444
  var _this = this;
10357
10445
  var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
10358
10446
  _classCallCheck(this, EcommerceManager);
10359
10447
  this.shadowRoot = config.shadowRoot;
10448
+ this.primaryColor = config.primaryColor || '#3b82f6';
10360
10449
  this.onProductSelected = config.onProductSelected || function () {};
10361
10450
  this.onProductDeselected = config.onProductDeselected || function () {};
10451
+ this.mobileCarousel = null;
10362
10452
  this.productPicker = new _ProductPicker_js__WEBPACK_IMPORTED_MODULE_0__.ProductPicker({
10363
10453
  onProductAdd: function onProductAdd(product) {
10364
10454
  return _this._handleAdd(product);
@@ -10445,7 +10535,6 @@ var EcommerceManager = /*#__PURE__*/function () {
10445
10535
  }, {
10446
10536
  key: "handleShowProducts",
10447
10537
  value: function handleShowProducts(message) {
10448
- this._ensureReady();
10449
10538
  var products = message.products,
10450
10539
  title = message.title,
10451
10540
  layout = message.layout;
@@ -10453,6 +10542,12 @@ var EcommerceManager = /*#__PURE__*/function () {
10453
10542
  console.warn('[EcommerceManager] show_products with no products');
10454
10543
  return;
10455
10544
  }
10545
+ this._currencySymbol = products[0].currency === 'ILS' ? '₪' : '$';
10546
+ if (this._isMobileCall()) {
10547
+ this._showMobileCarousel(products, title);
10548
+ return;
10549
+ }
10550
+ this._ensureReady();
10456
10551
  this._switchToCompactMode();
10457
10552
  this._setContainerMode('products');
10458
10553
  this.productPicker.hide();
@@ -10461,6 +10556,67 @@ var EcommerceManager = /*#__PURE__*/function () {
10461
10556
  this.container.insertBefore(pickerEl, this.container.firstChild);
10462
10557
  this._expandPanelToFit(products.length, effectiveLayout);
10463
10558
  }
10559
+
10560
+ /**
10561
+ * Detect if we're in a mobile voice call (minimized bar is present on body).
10562
+ */
10563
+ }, {
10564
+ key: "_isMobileCall",
10565
+ value: function _isMobileCall() {
10566
+ return !!document.getElementById('mobile-voice-call-bar-container');
10567
+ }
10568
+
10569
+ /**
10570
+ * Show 3D product carousel above the mobile minimized voice bar.
10571
+ */
10572
+ }, {
10573
+ key: "_showMobileCarousel",
10574
+ value: function _showMobileCarousel(products, title) {
10575
+ var _this2 = this;
10576
+ if (!this.mobileCarousel) {
10577
+ this.mobileCarousel = new _MobileProductCarousel_js__WEBPACK_IMPORTED_MODULE_4__.MobileProductCarousel({
10578
+ primaryColor: this.primaryColor,
10579
+ onProductAdd: function onProductAdd(product) {
10580
+ return _this2._handleMobileAdd(product);
10581
+ },
10582
+ onClose: function onClose() {}
10583
+ });
10584
+ }
10585
+ this.mobileCarousel.show(products, title);
10586
+ }
10587
+ }, {
10588
+ key: "_handleMobileAdd",
10589
+ value: function _handleMobileAdd(product) {
10590
+ var price = product.price || 0;
10591
+ var qty = product.selectedAmount || 1;
10592
+ var currency = product.currency === 'ILS' ? '₪' : '$';
10593
+ this.cartSummary.update(this.cartSummary.itemCount + 1, this.cartSummary.total + price * qty, currency);
10594
+ var cartInfo = {
10595
+ itemCount: this.cartSummary.itemCount,
10596
+ total: this.cartSummary.total,
10597
+ currency: this.cartSummary.currency
10598
+ };
10599
+ this.mobileCarousel.showToast(product.name, cartInfo);
10600
+ this._updateMobileCartRow(cartInfo);
10601
+ this.onProductSelected(product);
10602
+ }
10603
+ }, {
10604
+ key: "_updateMobileCartRow",
10605
+ value: function _updateMobileCartRow(_ref) {
10606
+ var itemCount = _ref.itemCount,
10607
+ total = _ref.total,
10608
+ currency = _ref.currency;
10609
+ var row = document.getElementById('mobileCartRow');
10610
+ var textEl = document.getElementById('mobileCartText');
10611
+ if (!row || !textEl) return;
10612
+ if (itemCount <= 0) {
10613
+ row.style.display = 'none';
10614
+ return;
10615
+ }
10616
+ var itemText = "".concat(itemCount, " item").concat(itemCount !== 1 ? 's' : '');
10617
+ textEl.textContent = "".concat(itemText, " \xB7 ").concat(currency).concat(total.toFixed(2));
10618
+ row.style.display = 'flex';
10619
+ }
10464
10620
  }, {
10465
10621
  key: "handleCartUpdated",
10466
10622
  value: function handleCartUpdated(message) {
@@ -10474,7 +10630,7 @@ var EcommerceManager = /*#__PURE__*/function () {
10474
10630
  this.container.insertBefore(toastEl, this.container.firstChild);
10475
10631
  }
10476
10632
  if (cartTotal !== undefined && cartItemCount !== undefined) {
10477
- this.cartSummary.update(cartItemCount, cartTotal);
10633
+ this.cartSummary.update(cartItemCount, cartTotal, this._currencySymbol);
10478
10634
  }
10479
10635
  }
10480
10636
 
@@ -10529,15 +10685,20 @@ var EcommerceManager = /*#__PURE__*/function () {
10529
10685
  var chrome = 62 + 40 + 20 + 150;
10530
10686
  var productsHeight;
10531
10687
  if (layout === 'cards') {
10532
- // Card: image 85 + details ~195 + border 2 = ~282px
10533
- // Container padding: 24px (12px * 2) + breathing room
10534
10688
  productsHeight = 360;
10535
10689
  } else {
10536
- // List item: ~58px each, container padding 20px
10537
- productsHeight = Math.min(productCount * 58 + 20, 400);
10690
+ // Each list item ~58px + 6px gap between items + container padding
10691
+ productsHeight = Math.min(productCount * 64 + 20, 500);
10692
+ }
10693
+
10694
+ // For 3+ products, ensure enough height for at least 3 full items + 4th peeking
10695
+ if (productCount > 2) {
10696
+ var minForThreeAndHalf = Math.ceil(3.5 * 64) + 20;
10697
+ productsHeight = Math.max(productsHeight, minForThreeAndHalf);
10538
10698
  }
10539
10699
  var totalHeight = chrome + productsHeight;
10540
- var clampedHeight = Math.min(Math.max(totalHeight, 450), 800);
10700
+ var minHeight = productCount > 2 ? 700 : 450;
10701
+ var clampedHeight = Math.min(Math.max(totalHeight, minHeight), 850);
10541
10702
  panel.classList.add('ttp-panel-expanded');
10542
10703
  panel.style.height = "".concat(clampedHeight, "px");
10543
10704
  }
@@ -10585,48 +10746,586 @@ var EcommerceManager = /*#__PURE__*/function () {
10585
10746
  }
10586
10747
 
10587
10748
  // ═══════════════════════════════════════════
10588
- // INTERNAL HANDLERS
10749
+ // INTERNAL HANDLERS
10750
+ // ═══════════════════════════════════════════
10751
+ }, {
10752
+ key: "_handleAdd",
10753
+ value: function _handleAdd(product) {
10754
+ var _this3 = this;
10755
+ var price = product.price || 0;
10756
+ var qty = product.selectedAmount || 1;
10757
+ var currency = this._currencySymbol || (product.currency === 'ILS' ? '₪' : '$');
10758
+ this.cartSummary.update(this.cartSummary.itemCount + 1, this.cartSummary.total + price * qty, currency);
10759
+ var toastEl = this.cartToast.show(product, 'added');
10760
+ this.container.insertBefore(toastEl, this.container.firstChild);
10761
+ setTimeout(function () {
10762
+ _this3.productPicker.hide();
10763
+ _this3._shrinkPanel();
10764
+ _this3._restoreFullVoiceMode();
10765
+ _this3._setContainerMode('summary');
10766
+ }, 500);
10767
+ this.onProductSelected(product);
10768
+ }
10769
+ }, {
10770
+ key: "_handleUndo",
10771
+ value: function _handleUndo(product) {
10772
+ this.onProductDeselected(product);
10773
+ }
10774
+
10775
+ // ═══════════════════════════════════════════
10776
+ // CLEANUP
10777
+ // ═══════════════════════════════════════════
10778
+ }, {
10779
+ key: "destroy",
10780
+ value: function destroy() {
10781
+ if (this.mobileCarousel) {
10782
+ this.mobileCarousel.destroy();
10783
+ this.mobileCarousel = null;
10784
+ }
10785
+ this.productPicker.hide();
10786
+ this.cartToast.hide();
10787
+ this._shrinkPanel();
10788
+ this._restoreFullVoiceMode();
10789
+ if (this.voiceActiveState) {
10790
+ this.voiceActiveState.classList.remove('ttp-products-visible');
10791
+ }
10792
+ if (this.container && this.container.parentNode) {
10793
+ this.container.parentNode.removeChild(this.container);
10794
+ }
10795
+ this.isAttached = false;
10796
+ }
10797
+ }]);
10798
+ }();
10799
+
10800
+ /***/ }),
10801
+
10802
+ /***/ "./src/ecommerce/MobileProductCarousel.js":
10803
+ /*!************************************************!*\
10804
+ !*** ./src/ecommerce/MobileProductCarousel.js ***!
10805
+ \************************************************/
10806
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
10807
+
10808
+ "use strict";
10809
+ __webpack_require__.r(__webpack_exports__);
10810
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
10811
+ /* harmony export */ MobileProductCarousel: () => (/* binding */ MobileProductCarousel)
10812
+ /* harmony export */ });
10813
+ 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); }
10814
+ 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; }
10815
+ 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; }
10816
+ function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
10817
+ function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
10818
+ function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
10819
+ function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
10820
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
10821
+ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
10822
+ /**
10823
+ * MobileProductCarousel — 3D carousel for product display on mobile during voice calls.
10824
+ *
10825
+ * Floats above the minimized voice bar (appended to document.body).
10826
+ * Uses CSS 3D perspective transforms for a rotating wheel effect.
10827
+ * Supports swipe/drag navigation, quantity/weight steppers, out-of-stock states.
10828
+ *
10829
+ * Options:
10830
+ * primaryColor: hex string (e.g. '#3b82f6')
10831
+ * onProductAdd(product): fired when user taps Add
10832
+ * onClose(): fired when user taps close button
10833
+ */
10834
+ var MobileProductCarousel = /*#__PURE__*/function () {
10835
+ function MobileProductCarousel() {
10836
+ var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
10837
+ _classCallCheck(this, MobileProductCarousel);
10838
+ this.onProductAdd = options.onProductAdd || function () {};
10839
+ this.onClose = options.onClose || function () {};
10840
+ this.primaryColor = options.primaryColor || '#3b82f6';
10841
+ this.products = [];
10842
+ this.current = 0;
10843
+ this.amounts = [];
10844
+ this.sellByModes = [];
10845
+ this.container = null;
10846
+ this.wheel = null;
10847
+ this.dotsEl = null;
10848
+ this.anglePerCard = 42;
10849
+ this.radius = 240;
10850
+ this.dragging = false;
10851
+ this.startX = 0;
10852
+ this.swiped = false;
10853
+ this.dragThreshold = 40;
10854
+ this._boundTouchMove = this._onTouchMove.bind(this);
10855
+ this._boundTouchEnd = this._onTouchEnd.bind(this);
10856
+ this._boundMouseMove = this._onMouseMove.bind(this);
10857
+ this._boundMouseUp = this._onMouseUp.bind(this);
10858
+ }
10859
+
10860
+ // ═══════════════════════════════════════════
10861
+ // PUBLIC API
10862
+ // ═══════════════════════════════════════════
10863
+ return _createClass(MobileProductCarousel, [{
10864
+ key: "show",
10865
+ value: function show(products, title) {
10866
+ this.hide();
10867
+ if (!products || products.length === 0) return;
10868
+ this._normalizeProducts(products);
10869
+ this.current = 0;
10870
+ this._injectStyles();
10871
+ this._buildDOM(title);
10872
+ this._buildCards();
10873
+ this._buildDots();
10874
+ this._setupDragEvents();
10875
+ this._updateWheel();
10876
+ document.body.appendChild(this.container);
10877
+ this._positionAboveMobileBar();
10878
+ }
10879
+ }, {
10880
+ key: "hide",
10881
+ value: function hide() {
10882
+ this._removeEventListeners();
10883
+ if (this.container && this.container.parentNode) {
10884
+ this.container.classList.add('ttp-carousel--hiding');
10885
+ var c = this.container;
10886
+ setTimeout(function () {
10887
+ if (c.parentNode) c.parentNode.removeChild(c);
10888
+ }, 280);
10889
+ }
10890
+ this.container = null;
10891
+ this.wheel = null;
10892
+ this.dotsEl = null;
10893
+ }
10894
+ }, {
10895
+ key: "destroy",
10896
+ value: function destroy() {
10897
+ this._removeEventListeners();
10898
+ if (this.container && this.container.parentNode) {
10899
+ this.container.parentNode.removeChild(this.container);
10900
+ }
10901
+ this.container = null;
10902
+ var toast = document.getElementById('ttpCarouselToast');
10903
+ if (toast) toast.remove();
10904
+ var style = document.getElementById('ttp-mobile-carousel-styles');
10905
+ if (style) style.remove();
10906
+ }
10907
+ }, {
10908
+ key: "showToast",
10909
+ value: function showToast(productName, cartInfo) {
10910
+ var existing = document.getElementById('ttpCarouselToast');
10911
+ if (existing) existing.remove();
10912
+ var toast = document.createElement('div');
10913
+ toast.id = 'ttpCarouselToast';
10914
+ toast.className = 'ttp-carousel-toast';
10915
+ var cartLine = cartInfo ? "<div class=\"ttp-carousel-toast__cart\">".concat(cartInfo.itemCount, " item").concat(cartInfo.itemCount !== 1 ? 's' : '', " \xB7 ").concat(cartInfo.currency).concat(cartInfo.total.toFixed(2), "</div>") : '';
10916
+ toast.innerHTML = "\n <div class=\"ttp-carousel-toast__icon\">\u2713</div>\n <div class=\"ttp-carousel-toast__text\">\n <strong>".concat(productName, "</strong> added to cart\n ").concat(cartLine, "\n </div>\n ");
10917
+ toast.style.setProperty('--ttp-carousel-primary', this.primaryColor);
10918
+ document.body.appendChild(toast);
10919
+ var mobileBar = document.getElementById('mobile-voice-call-bar-container');
10920
+ var cartPill = document.getElementById('ttpMobileCartSummary');
10921
+ if (cartPill) {
10922
+ toast.style.bottom = "".concat(parseInt(cartPill.style.bottom) + cartPill.offsetHeight + 8, "px");
10923
+ } else if (mobileBar) {
10924
+ toast.style.bottom = "".concat(mobileBar.offsetHeight + 12, "px");
10925
+ } else {
10926
+ toast.style.bottom = '100px';
10927
+ }
10928
+ setTimeout(function () {
10929
+ toast.classList.add('ttp-carousel-toast--hiding');
10930
+ setTimeout(function () {
10931
+ return toast.remove();
10932
+ }, 300);
10933
+ }, 3000);
10934
+ }
10935
+
10936
+ // ═══════════════════════════════════════════
10937
+ // DOM CONSTRUCTION
10938
+ // ═══════════════════════════════════════════
10939
+ }, {
10940
+ key: "_normalizeProducts",
10941
+ value: function _normalizeProducts(products) {
10942
+ this.products = products.map(function (p) {
10943
+ return _objectSpread(_objectSpread({}, p), {}, {
10944
+ imageUrl: p.imageUrl || p.image_url || '',
10945
+ inStock: p.inStock !== undefined ? p.inStock : p.in_stock !== undefined ? p.in_stock : true,
10946
+ sellBy: p.sellBy || p.sell_by || 'quantity',
10947
+ cartQuantity: p.cartQuantity || p.cart_quantity || 0,
10948
+ brand: p.brand || '',
10949
+ unit: p.unit || '',
10950
+ currency: p.currency || 'USD'
10951
+ });
10952
+ });
10953
+ this.amounts = this.products.map(function (p) {
10954
+ var min = p.sellBy === 'weight' ? 0.5 : 1;
10955
+ return p.cartQuantity > 0 ? p.cartQuantity : min;
10956
+ });
10957
+ this.sellByModes = this.products.map(function (p) {
10958
+ return p.sellBy;
10959
+ });
10960
+ }
10961
+ }, {
10962
+ key: "_buildDOM",
10963
+ value: function _buildDOM(title) {
10964
+ var _this = this;
10965
+ this.container = document.createElement('div');
10966
+ this.container.className = 'ttp-mobile-carousel';
10967
+ this.container.id = 'ttpMobileCarousel';
10968
+ this.container.style.setProperty('--ttp-carousel-primary', this.primaryColor);
10969
+ this.container.style.setProperty('--ttp-carousel-primary-light', this._hexToRgba(this.primaryColor, 0.12));
10970
+ this.container.style.setProperty('--ttp-carousel-primary-dark', this._darkenHex(this.primaryColor, 30));
10971
+ this.container.style.setProperty('--ttp-carousel-primary-shadow', this._hexToRgba(this.primaryColor, 0.3));
10972
+ var header = document.createElement('div');
10973
+ header.className = 'ttp-carousel-header';
10974
+ header.innerHTML = "\n <span class=\"ttp-carousel-title\">".concat(title || 'Products', "</span>\n <button class=\"ttp-carousel-close\" aria-label=\"Close\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/>\n </svg>\n </button>\n ");
10975
+ header.querySelector('.ttp-carousel-close').addEventListener('click', function () {
10976
+ _this.hide();
10977
+ _this.onClose();
10978
+ });
10979
+ var area = document.createElement('div');
10980
+ area.className = 'ttp-carousel-area';
10981
+ this.wheel = document.createElement('div');
10982
+ this.wheel.className = 'ttp-carousel-wheel';
10983
+ area.appendChild(this.wheel);
10984
+ this.dotsEl = document.createElement('div');
10985
+ this.dotsEl.className = 'ttp-carousel-dots';
10986
+ this.container.appendChild(header);
10987
+ this.container.appendChild(area);
10988
+ this.container.appendChild(this.dotsEl);
10989
+ }
10990
+ }, {
10991
+ key: "_buildCards",
10992
+ value: function _buildCards() {
10993
+ var _this2 = this;
10994
+ this.products.forEach(function (p, i) {
10995
+ var card = document.createElement('div');
10996
+ card.className = 'ttp-carousel-card' + (!p.inStock ? ' ttp-carousel-card--oos' : '');
10997
+ card.dataset.index = i;
10998
+ var sym = p.currency === 'ILS' ? '₪' : '$';
10999
+ var sellBy = _this2.sellByModes[i];
11000
+ var displayVal = sellBy === 'weight' ? "".concat(_this2.amounts[i].toFixed(1), " kg") : "".concat(_this2.amounts[i]);
11001
+ var inCart = p.cartQuantity > 0;
11002
+ card.innerHTML = "\n <div class=\"ttp-carousel-card__image\">\n <img src=\"".concat(p.imageUrl, "\" alt=\"").concat(p.name, "\" onerror=\"this.style.display='none'\" />\n ").concat(!p.inStock ? '<div class="ttp-carousel-card__badge">Out of Stock</div>' : '', "\n </div>\n <div class=\"ttp-carousel-card__info\">\n ").concat(p.brand ? "<div class=\"ttp-carousel-card__brand\">".concat(p.brand, "</div>") : '', "\n <div class=\"ttp-carousel-card__name\">").concat(p.name, "</div>\n ").concat(p.unit ? "<div class=\"ttp-carousel-card__unit\">".concat(p.unit, "</div>") : '', "\n <div class=\"ttp-carousel-card__price\">").concat(sym).concat(p.price.toFixed(2), "</div>\n </div>\n <div class=\"ttp-carousel-card__actions\">\n <div class=\"ttp-carousel-card__stepper\">\n <button class=\"ttp-carousel-card__qty-btn ttp-carousel-minus\" data-idx=\"").concat(i, "\" ").concat(!p.inStock ? 'disabled' : '', ">\u2212</button>\n <span class=\"ttp-carousel-card__qty-val\" id=\"ttpCQty").concat(i, "\">").concat(displayVal, "</span>\n <button class=\"ttp-carousel-card__qty-btn ttp-carousel-plus\" data-idx=\"").concat(i, "\" ").concat(!p.inStock ? 'disabled' : '', ">+</button>\n </div>\n <button class=\"ttp-carousel-card__add-btn").concat(inCart ? ' ttp-carousel-card__add-btn--update' : '', "\" data-idx=\"").concat(i, "\" ").concat(!p.inStock ? 'disabled' : '', ">\n ").concat(p.inStock ? inCart ? 'Update' : 'Add to Cart' : 'Unavailable', "\n </button>\n </div>\n ");
11003
+ card.style.transform = "rotateY(".concat(i * _this2.anglePerCard, "deg) translateZ(").concat(_this2.radius, "px)");
11004
+ _this2.wheel.appendChild(card);
11005
+ });
11006
+ this._setupCardHandlers();
11007
+ }
11008
+
11009
+ /**
11010
+ * Area-level tap detection — bypasses browser 3D hit-testing entirely.
11011
+ *
11012
+ * Instead of attaching handlers to individual buttons (which requires
11013
+ * the browser to correctly identify which 3D-transformed button was
11014
+ * tapped), we capture ALL touches at the carousel-area level and
11015
+ * manually match the touch coordinates against the current card's
11016
+ * buttons using getBoundingClientRect.
11017
+ */
11018
+ }, {
11019
+ key: "_setupCardHandlers",
11020
+ value: function _setupCardHandlers() {
11021
+ var _this3 = this;
11022
+ var area = this.container.querySelector('.ttp-carousel-area');
11023
+ if (!area) return;
11024
+ var tapOrigin = null;
11025
+ area.addEventListener('touchstart', function (e) {
11026
+ tapOrigin = {
11027
+ x: e.touches[0].clientX,
11028
+ y: e.touches[0].clientY
11029
+ };
11030
+ }, {
11031
+ passive: true
11032
+ });
11033
+ area.addEventListener('touchend', function (e) {
11034
+ if (!tapOrigin || _this3.swiped) {
11035
+ tapOrigin = null;
11036
+ return;
11037
+ }
11038
+ var touch = e.changedTouches[0];
11039
+ var dx = Math.abs(touch.clientX - tapOrigin.x);
11040
+ var dy = Math.abs(touch.clientY - tapOrigin.y);
11041
+ tapOrigin = null;
11042
+ if (dx > 15 || dy > 15) return;
11043
+ e.preventDefault();
11044
+ _this3._handleTapAt(touch.clientX, touch.clientY);
11045
+ });
11046
+ area.addEventListener('click', function (e) {
11047
+ var target = e.target;
11048
+ var card = target.closest('.ttp-carousel-card');
11049
+ if (!card || parseInt(card.dataset.index) !== _this3.current) return;
11050
+ var minus = target.closest('.ttp-carousel-minus');
11051
+ var plus = target.closest('.ttp-carousel-plus');
11052
+ var addBtn = target.closest('.ttp-carousel-card__add-btn');
11053
+ if (minus && !minus.disabled) {
11054
+ e.stopPropagation();
11055
+ _this3._handleMinus();
11056
+ } else if (plus && !plus.disabled) {
11057
+ e.stopPropagation();
11058
+ _this3._handlePlus();
11059
+ } else if (addBtn && !addBtn.disabled) {
11060
+ e.stopPropagation();
11061
+ _this3._handleAdd();
11062
+ }
11063
+ });
11064
+ }
11065
+ }, {
11066
+ key: "_handleTapAt",
11067
+ value: function _handleTapAt(x, y) {
11068
+ var cards = this.wheel.querySelectorAll('.ttp-carousel-card');
11069
+ var currentCard = cards[this.current];
11070
+ if (!currentCard) return;
11071
+ var minus = currentCard.querySelector('.ttp-carousel-minus');
11072
+ var plus = currentCard.querySelector('.ttp-carousel-plus');
11073
+ var addBtn = currentCard.querySelector('.ttp-carousel-card__add-btn');
11074
+ if (this._hitTest(x, y, minus) && !(minus !== null && minus !== void 0 && minus.disabled)) {
11075
+ this._handleMinus();
11076
+ } else if (this._hitTest(x, y, plus) && !(plus !== null && plus !== void 0 && plus.disabled)) {
11077
+ this._handlePlus();
11078
+ } else if (this._hitTest(x, y, addBtn) && !(addBtn !== null && addBtn !== void 0 && addBtn.disabled)) {
11079
+ this._handleAdd();
11080
+ }
11081
+ }
11082
+ }, {
11083
+ key: "_hitTest",
11084
+ value: function _hitTest(x, y, el) {
11085
+ if (!el) return false;
11086
+ var r = el.getBoundingClientRect();
11087
+ return x >= r.left && x <= r.right && y >= r.top && y <= r.bottom;
11088
+ }
11089
+ }, {
11090
+ key: "_handleMinus",
11091
+ value: function _handleMinus() {
11092
+ var idx = this.current;
11093
+ var sellBy = this.sellByModes[idx];
11094
+ var min = sellBy === 'weight' ? 0.5 : 1;
11095
+ var step = sellBy === 'weight' ? 0.5 : 1;
11096
+ if (this.amounts[idx] > min) {
11097
+ this.amounts[idx] = Math.round((this.amounts[idx] - step) * 10) / 10;
11098
+ this._refreshStepper(idx);
11099
+ }
11100
+ }
11101
+ }, {
11102
+ key: "_handlePlus",
11103
+ value: function _handlePlus() {
11104
+ var idx = this.current;
11105
+ var step = this.sellByModes[idx] === 'weight' ? 0.5 : 1;
11106
+ this.amounts[idx] = Math.round((this.amounts[idx] + step) * 10) / 10;
11107
+ this._refreshStepper(idx);
11108
+ }
11109
+ }, {
11110
+ key: "_handleAdd",
11111
+ value: function _handleAdd() {
11112
+ var _cards$idx,
11113
+ _this4 = this;
11114
+ var idx = this.current;
11115
+ var product = this.products[idx];
11116
+ if (!product.inStock) return;
11117
+ var cards = this.wheel.querySelectorAll('.ttp-carousel-card');
11118
+ var btn = (_cards$idx = cards[idx]) === null || _cards$idx === void 0 ? void 0 : _cards$idx.querySelector('.ttp-carousel-card__add-btn');
11119
+ if (btn) {
11120
+ btn.textContent = '✓ Added!';
11121
+ btn.classList.add('ttp-carousel-card__add-btn--added');
11122
+ btn.disabled = true;
11123
+ }
11124
+ this.onProductAdd(_objectSpread(_objectSpread({}, product), {}, {
11125
+ selectedAmount: this.amounts[idx],
11126
+ sellBy: this.sellByModes[idx]
11127
+ }));
11128
+ setTimeout(function () {
11129
+ return _this4.hide();
11130
+ }, 800);
11131
+ }
11132
+ }, {
11133
+ key: "_refreshStepper",
11134
+ value: function _refreshStepper(idx) {
11135
+ var sellBy = this.sellByModes[idx];
11136
+ var min = sellBy === 'weight' ? 0.5 : 1;
11137
+ var el = this.container.querySelector("#ttpCQty".concat(idx));
11138
+ var minusBtn = this.container.querySelector(".ttp-carousel-minus[data-idx=\"".concat(idx, "\"]"));
11139
+ if (el) {
11140
+ el.textContent = sellBy === 'weight' ? "".concat(this.amounts[idx].toFixed(1), " kg") : "".concat(this.amounts[idx]);
11141
+ }
11142
+ if (minusBtn) {
11143
+ minusBtn.disabled = this.amounts[idx] <= min;
11144
+ }
11145
+ }
11146
+ }, {
11147
+ key: "_buildDots",
11148
+ value: function _buildDots() {
11149
+ var _this5 = this;
11150
+ if (this.products.length <= 1) return;
11151
+ this.products.forEach(function (_, i) {
11152
+ var dot = document.createElement('div');
11153
+ dot.className = 'ttp-carousel-dot';
11154
+ dot.addEventListener('click', function () {
11155
+ return _this5.goTo(i);
11156
+ });
11157
+ _this5.dotsEl.appendChild(dot);
11158
+ });
11159
+ }
11160
+
11161
+ // ═══════════════════════════════════════════
11162
+ // 3D WHEEL
11163
+ // ═══════════════════════════════════════════
11164
+ }, {
11165
+ key: "_updateWheel",
11166
+ value: function _updateWheel() {
11167
+ var _this6 = this;
11168
+ var rotation = -(this.current * this.anglePerCard);
11169
+ this.wheel.style.transform = "translateZ(-".concat(this.radius, "px) rotateY(").concat(rotation, "deg)");
11170
+ var cards = this.wheel.querySelectorAll('.ttp-carousel-card');
11171
+ cards.forEach(function (card, i) {
11172
+ var eff = i * _this6.anglePerCard + rotation;
11173
+ while (eff > 180) eff -= 360;
11174
+ while (eff < -180) eff += 360;
11175
+ if (Math.abs(eff) > 90) {
11176
+ card.style.visibility = 'hidden';
11177
+ card.style.opacity = '0';
11178
+ } else {
11179
+ card.style.visibility = 'visible';
11180
+ card.style.opacity = _this6.products[i].inStock ? '1' : '0.7';
11181
+ }
11182
+ });
11183
+ var dots = this.dotsEl.querySelectorAll('.ttp-carousel-dot');
11184
+ dots.forEach(function (dot, i) {
11185
+ return dot.classList.toggle('ttp-carousel-dot--active', i === _this6.current);
11186
+ });
11187
+ }
11188
+ }, {
11189
+ key: "goTo",
11190
+ value: function goTo(i) {
11191
+ this.current = Math.max(0, Math.min(this.products.length - 1, i));
11192
+ this._updateWheel();
11193
+ }
11194
+
11195
+ // ═══════════════════════════════════════════
11196
+ // DRAG / SWIPE
11197
+ // ═══════════════════════════════════════════
11198
+ }, {
11199
+ key: "_setupDragEvents",
11200
+ value: function _setupDragEvents() {
11201
+ var _this7 = this;
11202
+ var area = this.container.querySelector('.ttp-carousel-area');
11203
+ if (!area) return;
11204
+ area.addEventListener('touchstart', function (e) {
11205
+ _this7.dragging = true;
11206
+ _this7.startX = e.touches[0].clientX;
11207
+ _this7.swiped = false;
11208
+ }, {
11209
+ passive: true
11210
+ });
11211
+ area.addEventListener('mousedown', function (e) {
11212
+ _this7.dragging = true;
11213
+ _this7.startX = e.clientX;
11214
+ _this7.swiped = false;
11215
+ });
11216
+ document.addEventListener('touchmove', this._boundTouchMove, {
11217
+ passive: true
11218
+ });
11219
+ document.addEventListener('touchend', this._boundTouchEnd);
11220
+ document.addEventListener('mousemove', this._boundMouseMove);
11221
+ document.addEventListener('mouseup', this._boundMouseUp);
11222
+ }
11223
+ }, {
11224
+ key: "_onTouchMove",
11225
+ value: function _onTouchMove(e) {
11226
+ if (!this.dragging) return;
11227
+ var diff = e.touches[0].clientX - this.startX;
11228
+ if (Math.abs(diff) > this.dragThreshold) {
11229
+ this.goTo(this.current + (diff > 0 ? -1 : 1));
11230
+ this.startX = e.touches[0].clientX;
11231
+ this.swiped = true;
11232
+ }
11233
+ }
11234
+ }, {
11235
+ key: "_onTouchEnd",
11236
+ value: function _onTouchEnd() {
11237
+ var _this8 = this;
11238
+ this.dragging = false;
11239
+ if (this.swiped) {
11240
+ setTimeout(function () {
11241
+ _this8.swiped = false;
11242
+ }, 400);
11243
+ }
11244
+ }
11245
+ }, {
11246
+ key: "_onMouseMove",
11247
+ value: function _onMouseMove(e) {
11248
+ if (!this.dragging) return;
11249
+ var diff = e.clientX - this.startX;
11250
+ if (Math.abs(diff) > this.dragThreshold) {
11251
+ this.goTo(this.current + (diff > 0 ? -1 : 1));
11252
+ this.startX = e.clientX;
11253
+ this.swiped = true;
11254
+ }
11255
+ }
11256
+ }, {
11257
+ key: "_onMouseUp",
11258
+ value: function _onMouseUp() {
11259
+ var _this9 = this;
11260
+ this.dragging = false;
11261
+ if (this.swiped) {
11262
+ setTimeout(function () {
11263
+ _this9.swiped = false;
11264
+ }, 400);
11265
+ }
11266
+ }
11267
+ }, {
11268
+ key: "_removeEventListeners",
11269
+ value: function _removeEventListeners() {
11270
+ document.removeEventListener('touchmove', this._boundTouchMove);
11271
+ document.removeEventListener('touchend', this._boundTouchEnd);
11272
+ document.removeEventListener('mousemove', this._boundMouseMove);
11273
+ document.removeEventListener('mouseup', this._boundMouseUp);
11274
+ }
11275
+
11276
+ // ═══════════════════════════════════════════
11277
+ // POSITIONING
11278
+ // ═══════════════════════════════════════════
11279
+ }, {
11280
+ key: "_positionAboveMobileBar",
11281
+ value: function _positionAboveMobileBar() {
11282
+ var _this0 = this;
11283
+ requestAnimationFrame(function () {
11284
+ if (!_this0.container) return;
11285
+ var bar = document.getElementById('mobile-voice-call-bar-container');
11286
+ if (bar) {
11287
+ var barRect = bar.getBoundingClientRect();
11288
+ _this0.container.style.bottom = "".concat(window.innerHeight - barRect.top + 8, "px");
11289
+ }
11290
+ });
11291
+ }
11292
+
11293
+ // ═══════════════════════════════════════════
11294
+ // COLOR HELPERS
10589
11295
  // ═══════════════════════════════════════════
10590
11296
  }, {
10591
- key: "_handleAdd",
10592
- value: function _handleAdd(product) {
10593
- var _this2 = this;
10594
- var price = product.price || 0;
10595
- var qty = product.selectedAmount || 1;
10596
- this.cartSummary.update(this.cartSummary.itemCount + 1, this.cartSummary.total + price * qty);
10597
- var toastEl = this.cartToast.show(product, 'added');
10598
- this.container.insertBefore(toastEl, this.container.firstChild);
10599
- setTimeout(function () {
10600
- _this2.productPicker.hide();
10601
- _this2._shrinkPanel();
10602
- _this2._restoreFullVoiceMode();
10603
- _this2._setContainerMode('summary');
10604
- }, 500);
10605
- this.onProductSelected(product);
11297
+ key: "_hexToRgba",
11298
+ value: function _hexToRgba(hex, alpha) {
11299
+ var r = parseInt(hex.slice(1, 3), 16);
11300
+ var g = parseInt(hex.slice(3, 5), 16);
11301
+ var b = parseInt(hex.slice(5, 7), 16);
11302
+ return "rgba(".concat(r, ", ").concat(g, ", ").concat(b, ", ").concat(alpha, ")");
10606
11303
  }
10607
11304
  }, {
10608
- key: "_handleUndo",
10609
- value: function _handleUndo(product) {
10610
- this.onProductDeselected(product);
11305
+ key: "_darkenHex",
11306
+ value: function _darkenHex(hex, amount) {
11307
+ var r = Math.max(0, parseInt(hex.slice(1, 3), 16) - amount);
11308
+ var g = Math.max(0, parseInt(hex.slice(3, 5), 16) - amount);
11309
+ var b = Math.max(0, parseInt(hex.slice(5, 7), 16) - amount);
11310
+ return "#".concat(r.toString(16).padStart(2, '0')).concat(g.toString(16).padStart(2, '0')).concat(b.toString(16).padStart(2, '0'));
10611
11311
  }
10612
11312
 
10613
11313
  // ═══════════════════════════════════════════
10614
- // CLEANUP
11314
+ // STYLES
10615
11315
  // ═══════════════════════════════════════════
10616
11316
  }, {
10617
- key: "destroy",
10618
- value: function destroy() {
10619
- this.productPicker.hide();
10620
- this.cartToast.hide();
10621
- this._shrinkPanel();
10622
- this._restoreFullVoiceMode();
10623
- if (this.voiceActiveState) {
10624
- this.voiceActiveState.classList.remove('ttp-products-visible');
10625
- }
10626
- if (this.container && this.container.parentNode) {
10627
- this.container.parentNode.removeChild(this.container);
10628
- }
10629
- this.isAttached = false;
11317
+ key: "_injectStyles",
11318
+ value: function _injectStyles() {
11319
+ if (document.getElementById('ttp-mobile-carousel-styles')) return;
11320
+ var s = document.createElement('style');
11321
+ s.id = 'ttp-mobile-carousel-styles';
11322
+ s.textContent = this._generateCSS();
11323
+ document.head.appendChild(s);
11324
+ }
11325
+ }, {
11326
+ key: "_generateCSS",
11327
+ value: function _generateCSS() {
11328
+ return "\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 MOBILE PRODUCT CAROUSEL\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-mobile-carousel {\n position: fixed;\n bottom: 120px;\n left: 0;\n right: 0;\n top: 0;\n z-index: 100000;\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n animation: ttpCarouselIn 0.35s cubic-bezier(0.23, 1, 0.32, 1);\n -webkit-tap-highlight-color: transparent;\n display: flex;\n flex-direction: column;\n justify-content: flex-end;\n padding-bottom: 16px;\n}\n\n.ttp-mobile-carousel > .ttp-carousel-header,\n.ttp-mobile-carousel > .ttp-carousel-area,\n.ttp-mobile-carousel > .ttp-carousel-dots {\n position: relative;\n z-index: 1;\n}\n\n.ttp-mobile-carousel::after {\n content: '';\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n height: 420px;\n background: rgba(0, 0, 0, 0.2);\n backdrop-filter: blur(4px);\n -webkit-backdrop-filter: blur(4px);\n border-radius: 24px 24px 0 0;\n pointer-events: none;\n z-index: 0;\n}\n\n.ttp-mobile-carousel.ttp-carousel--hiding {\n animation: ttpCarouselOut 0.28s ease forwards;\n pointer-events: none;\n}\n\n@keyframes ttpCarouselIn {\n from { opacity: 0; transform: translateY(30px); }\n to { opacity: 1; transform: translateY(0); }\n}\n@keyframes ttpCarouselOut {\n from { opacity: 1; transform: translateY(0); }\n to { opacity: 0; transform: translateY(30px); }\n}\n\n/* \u2500\u2500 Header \u2500\u2500 */\n\n.ttp-carousel-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 0 24px 8px;\n flex-shrink: 0;\n}\n\n.ttp-carousel-title {\n font-size: 13px;\n font-weight: 600;\n color: #fff;\n text-shadow: 0 1px 4px rgba(0,0,0,0.4);\n}\n\n.ttp-carousel-close {\n width: 32px;\n height: 32px;\n border: none;\n border-radius: 0;\n background: none;\n color: #fff;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n pointer-events: auto;\n padding: 0;\n opacity: 0.85;\n}\n.ttp-carousel-close:active {\n opacity: 1;\n}\n\n/* \u2500\u2500 3D Carousel Area \u2500\u2500 */\n\n.ttp-carousel-area {\n position: relative;\n height: 300px;\n flex-shrink: 0;\n perspective: 800px;\n perspective-origin: 50% 55%;\n cursor: grab;\n user-select: none;\n -webkit-user-select: none;\n}\n.ttp-carousel-area:active {\n cursor: grabbing;\n}\n\n.ttp-carousel-wheel {\n position: absolute;\n width: 100%;\n height: 100%;\n transform-style: preserve-3d;\n transition: transform 0.5s cubic-bezier(0.23, 1, 0.32, 1);\n}\n\n/* \u2500\u2500 Card \u2500\u2500 */\n\n.ttp-carousel-card {\n position: absolute;\n width: 150px;\n height: 285px;\n left: 50%;\n top: 50%;\n margin-left: -75px;\n margin-top: -142px;\n background: #fff;\n border-radius: 18px;\n overflow: hidden;\n box-shadow: 0 8px 32px rgba(0,0,0,0.14);\n transform-style: preserve-3d;\n transition: visibility 0.3s, opacity 0.3s;\n display: flex;\n flex-direction: column;\n}\n\n.ttp-carousel-card--oos {\n opacity: 0.7;\n}\n\n/* Card Image */\n.ttp-carousel-card__image {\n width: 100%;\n height: 100px;\n background: linear-gradient(145deg, #f0f4ff, #e8ecf8);\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n position: relative;\n overflow: hidden;\n}\n\n.ttp-carousel-card__image img {\n max-width: 80%;\n max-height: 80%;\n object-fit: contain;\n}\n\n.ttp-carousel-card__badge {\n position: absolute;\n top: 6px;\n right: 6px;\n background: #ef4444;\n color: #fff;\n font-size: 8px;\n font-weight: 700;\n padding: 2px 6px;\n border-radius: 4px;\n text-transform: uppercase;\n letter-spacing: 0.3px;\n}\n\n/* Card Info */\n.ttp-carousel-card__info {\n padding: 8px 10px 4px;\n flex-shrink: 0;\n}\n\n.ttp-carousel-card__brand {\n font-size: 9px;\n font-weight: 500;\n color: #94a3b8;\n margin-bottom: 1px;\n text-transform: uppercase;\n letter-spacing: 0.3px;\n}\n\n.ttp-carousel-card__name {\n font-size: 12px;\n font-weight: 600;\n color: #1e1b4b;\n line-height: 1.25;\n margin-bottom: 1px;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n.ttp-carousel-card__unit {\n font-size: 9px;\n color: #64748b;\n margin-bottom: 3px;\n}\n\n.ttp-carousel-card__price {\n font-size: 16px;\n font-weight: 700;\n color: var(--ttp-carousel-primary, #3b82f6);\n}\n\n/* Card Actions */\n.ttp-carousel-card__actions {\n padding: 6px 10px 10px;\n display: flex;\n flex-direction: column;\n gap: 6px;\n margin-top: auto;\n}\n\n/* Stepper */\n.ttp-carousel-card__stepper {\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--ttp-carousel-primary-light, rgba(59,130,246,0.12));\n border-radius: 10px;\n padding: 3px;\n}\n\n.ttp-carousel-card__qty-btn {\n width: 30px;\n height: 30px;\n border: none;\n background: transparent;\n color: var(--ttp-carousel-primary, #3b82f6);\n font-size: 17px;\n font-weight: 600;\n cursor: pointer;\n border-radius: 8px;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: background 0.15s;\n -webkit-tap-highlight-color: transparent;\n position: relative;\n z-index: 10;\n touch-action: manipulation;\n}\n.ttp-carousel-card__qty-btn:active:not(:disabled) {\n background: var(--ttp-carousel-primary-light, rgba(59,130,246,0.12));\n}\n.ttp-carousel-card__qty-btn:disabled {\n opacity: 0.3;\n cursor: default;\n}\n\n.ttp-carousel-card__qty-val {\n min-width: 40px;\n text-align: center;\n font-size: 14px;\n font-weight: 600;\n color: #1e1b4b;\n user-select: none;\n}\n\n/* Add Button */\n.ttp-carousel-card__add-btn {\n width: 100%;\n padding: 9px 12px;\n background: linear-gradient(135deg, var(--ttp-carousel-primary, #3b82f6), var(--ttp-carousel-primary-dark, #2563eb));\n color: #fff;\n border: none;\n border-radius: 10px;\n font-size: 12px;\n font-weight: 600;\n cursor: pointer;\n box-shadow: 0 4px 12px var(--ttp-carousel-primary-shadow, rgba(59,130,246,0.3));\n transition: transform 0.15s, box-shadow 0.15s;\n -webkit-tap-highlight-color: transparent;\n position: relative;\n z-index: 10;\n touch-action: manipulation;\n}\n.ttp-carousel-card__add-btn:active:not(:disabled) {\n transform: scale(0.97);\n}\n.ttp-carousel-card__add-btn:disabled {\n background: #94a3b8;\n cursor: default;\n box-shadow: none;\n}\n\n.ttp-carousel-card__add-btn--added {\n background: linear-gradient(135deg, #10b981, #059669) !important;\n box-shadow: 0 4px 12px rgba(16, 185, 129, 0.3) !important;\n}\n\n.ttp-carousel-card__add-btn--update {\n background: linear-gradient(135deg, #f59e0b, #d97706);\n box-shadow: 0 4px 12px rgba(245, 158, 11, 0.3);\n}\n\n/* \u2500\u2500 Dots \u2500\u2500 */\n\n.ttp-carousel-dots {\n display: flex;\n justify-content: center;\n gap: 8px;\n padding: 8px 0 0;\n flex-shrink: 0;\n}\n\n.ttp-carousel-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: rgba(255,255,255,0.35);\n cursor: pointer;\n transition: all 0.3s;\n -webkit-tap-highlight-color: transparent;\n}\n\n.ttp-carousel-dot--active {\n width: 24px;\n border-radius: 4px;\n background: #fff;\n}\n\n/* \u2500\u2500 Toast \u2500\u2500 */\n\n.ttp-carousel-toast {\n position: fixed;\n left: 16px;\n right: 16px;\n bottom: 100px;\n z-index: 100001;\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 12px 16px;\n background: #fff;\n border-radius: 14px;\n box-shadow: 0 8px 32px rgba(0,0,0,0.15);\n animation: ttpCarouselToastIn 0.3s ease;\n pointer-events: auto;\n}\n\n.ttp-carousel-toast--hiding {\n animation: ttpCarouselOut 0.3s ease forwards;\n}\n\n@keyframes ttpCarouselToastIn {\n from { opacity: 0; transform: translateY(12px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n.ttp-carousel-toast__icon {\n width: 28px;\n height: 28px;\n border-radius: 50%;\n background: #10b981;\n color: #fff;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 14px;\n font-weight: 700;\n flex-shrink: 0;\n}\n\n.ttp-carousel-toast__text {\n font-size: 13px;\n color: #1e293b;\n line-height: 1.3;\n}\n.ttp-carousel-toast__text strong {\n font-weight: 600;\n}\n.ttp-carousel-toast__cart {\n font-size: 11px;\n color: #64748b;\n margin-top: 2px;\n font-weight: 500;\n}\n";
10630
11329
  }
10631
11330
  }]);
10632
11331
  }();
@@ -10897,7 +11596,9 @@ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e
10897
11596
 
10898
11597
  var TTPEcommerceWidget = /*#__PURE__*/function () {
10899
11598
  function TTPEcommerceWidget() {
10900
- var _this = this;
11599
+ var _this$widget$config,
11600
+ _config$colors,
11601
+ _this = this;
10901
11602
  var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
10902
11603
  _classCallCheck(this, TTPEcommerceWidget);
10903
11604
  console.log('[TTPEcommerceWidget] v2.35 initialized');
@@ -10906,6 +11607,7 @@ var TTPEcommerceWidget = /*#__PURE__*/function () {
10906
11607
  this.widget = new _widget_TTPChatWidget_js__WEBPACK_IMPORTED_MODULE_0__.TTPChatWidget(config);
10907
11608
  this.ecommerce = new _EcommerceManager_js__WEBPACK_IMPORTED_MODULE_1__.EcommerceManager({
10908
11609
  shadowRoot: this.widget.shadowRoot,
11610
+ primaryColor: ((_this$widget$config = this.widget.config) === null || _this$widget$config === void 0 || (_this$widget$config = _this$widget$config.colors) === null || _this$widget$config === void 0 ? void 0 : _this$widget$config.primary) || ((_config$colors = config.colors) === null || _config$colors === void 0 ? void 0 : _config$colors.primary) || '#3b82f6',
10909
11611
  onProductSelected: function onProductSelected(product) {
10910
11612
  return _this._handleProductSelected(product);
10911
11613
  },
@@ -10931,6 +11633,9 @@ var TTPEcommerceWidget = /*#__PURE__*/function () {
10931
11633
  console.warn('[TTPEcommerceWidget] AgentSDK not available yet');
10932
11634
  return;
10933
11635
  }
11636
+
11637
+ // Disable the unsupported-action warning — this widget handles e-commerce actions
11638
+ agentSDK.onUnsupportedAction = null;
10934
11639
  var self = this;
10935
11640
  var originalConnect = agentSDK.connect.bind(agentSDK);
10936
11641
  agentSDK.connect = /*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
@@ -10949,6 +11654,8 @@ var TTPEcommerceWidget = /*#__PURE__*/function () {
10949
11654
  self._handleCartUpdatedFromServer(message);
10950
11655
  } else if (type === 'add_to_store_cart') {
10951
11656
  self._handleAddToStoreCart(message);
11657
+ } else if (type === 'get_store_cart') {
11658
+ self._handleGetStoreCart();
10952
11659
  }
10953
11660
  });
10954
11661
  console.log('[TTPEcommerceWidget] E-commerce message listener attached');
@@ -10971,6 +11678,9 @@ var TTPEcommerceWidget = /*#__PURE__*/function () {
10971
11678
  var originalOnCallEnd = vi.config.onCallEnd;
10972
11679
  var ecommerce = this.ecommerce;
10973
11680
  vi.config.onCallEnd = function () {
11681
+ if (ecommerce.mobileCarousel) {
11682
+ ecommerce.mobileCarousel.hide();
11683
+ }
10974
11684
  ecommerce.productPicker.hide();
10975
11685
  ecommerce._setContainerMode('summary');
10976
11686
  ecommerce._shrinkPanel();
@@ -11048,26 +11758,101 @@ var TTPEcommerceWidget = /*#__PURE__*/function () {
11048
11758
  // ═══════════════════════════════════════════
11049
11759
 
11050
11760
  /**
11051
- * Handle add_to_store_cart message from the backend.
11052
- * Calls Shopify's /cart/add.js from the browser so the item goes into
11053
- * the store's session-based cart (not a headless Storefront API cart).
11054
- *
11055
- * We don't attempt to refresh the theme's cart UI (icon badge, drawer)
11056
- * because there's no universal mechanism — every theme is different.
11057
- * The user will see the updated cart when they navigate to /cart or refresh.
11058
- * Our own CartToast + CartSummary provide immediate visual feedback.
11761
+ * Fetch cart contents from Shopify's AJAX Cart API and send back to backend.
11762
+ * Triggered when the LLM calls get_cart for a Shopify store.
11763
+ * Only the browser can read the store's cart because it requires the session cookie.
11059
11764
  */
11060
11765
  }, {
11061
- key: "_handleAddToStoreCart",
11766
+ key: "_handleGetStoreCart",
11062
11767
  value: function () {
11063
- var _handleAddToStoreCart2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4(data) {
11064
- var variantId, quantity, productName, price, _cartResponse$items, _cartResponse$items2, response, cartResponse, cartData, _t3;
11768
+ var _handleGetStoreCart2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4() {
11769
+ var response, cart, _t3;
11065
11770
  return _regenerator().w(function (_context4) {
11066
11771
  while (1) switch (_context4.p = _context4.n) {
11067
11772
  case 0:
11068
- variantId = data.variantId, quantity = data.quantity, productName = data.productName, price = data.price;
11069
- _context4.p = 1;
11070
- _context4.n = 2;
11773
+ _context4.p = 0;
11774
+ _context4.n = 1;
11775
+ return fetch('/cart.js', {
11776
+ method: 'GET',
11777
+ headers: {
11778
+ 'Content-Type': 'application/json'
11779
+ }
11780
+ });
11781
+ case 1:
11782
+ response = _context4.v;
11783
+ if (response.ok) {
11784
+ _context4.n = 2;
11785
+ break;
11786
+ }
11787
+ console.error('[TTP Ecommerce] Failed to fetch /cart.js:', response.status);
11788
+ this._sendToVoiceSDK({
11789
+ t: 'cart_state_result',
11790
+ success: false,
11791
+ error: "Failed to fetch cart: HTTP ".concat(response.status)
11792
+ });
11793
+ return _context4.a(2);
11794
+ case 2:
11795
+ _context4.n = 3;
11796
+ return response.json();
11797
+ case 3:
11798
+ cart = _context4.v;
11799
+ this._sendToVoiceSDK({
11800
+ t: 'cart_state_result',
11801
+ success: true,
11802
+ items: (cart.items || []).map(function (item) {
11803
+ return {
11804
+ title: item.title,
11805
+ variant_title: item.variant_title,
11806
+ quantity: item.quantity,
11807
+ price: item.price,
11808
+ line_price: item.line_price,
11809
+ image: item.image,
11810
+ variant_id: item.variant_id,
11811
+ product_id: item.product_id
11812
+ };
11813
+ }),
11814
+ item_count: cart.item_count,
11815
+ total_price: cart.total_price,
11816
+ currency: cart.currency
11817
+ });
11818
+ console.log("[TTP Ecommerce] Cart state sent: ".concat(cart.item_count, " items, ").concat(cart.currency, " ").concat((cart.total_price / 100).toFixed(2)));
11819
+ _context4.n = 5;
11820
+ break;
11821
+ case 4:
11822
+ _context4.p = 4;
11823
+ _t3 = _context4.v;
11824
+ console.error('[TTP Ecommerce] Error fetching cart:', _t3);
11825
+ this._sendToVoiceSDK({
11826
+ t: 'cart_state_result',
11827
+ success: false,
11828
+ error: _t3.message || 'Failed to fetch cart'
11829
+ });
11830
+ case 5:
11831
+ return _context4.a(2);
11832
+ }
11833
+ }, _callee4, this, [[0, 4]]);
11834
+ }));
11835
+ function _handleGetStoreCart() {
11836
+ return _handleGetStoreCart2.apply(this, arguments);
11837
+ }
11838
+ return _handleGetStoreCart;
11839
+ }()
11840
+ /**
11841
+ * Add product to Shopify store cart via AJAX Cart API, then report result back to backend.
11842
+ * Must be done from the browser because only the browser has the store's session cookie.
11843
+ */
11844
+ }, {
11845
+ key: "_handleAddToStoreCart",
11846
+ value: (function () {
11847
+ var _handleAddToStoreCart2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee5(data) {
11848
+ var variantId, quantity, productName, addResponse, errorText, addResult, cartResponse, cart, cartRefreshed, _t4;
11849
+ return _regenerator().w(function (_context5) {
11850
+ while (1) switch (_context5.p = _context5.n) {
11851
+ case 0:
11852
+ variantId = data.variantId, quantity = data.quantity;
11853
+ productName = '';
11854
+ _context5.p = 1;
11855
+ _context5.n = 2;
11071
11856
  return fetch('/cart/add.js', {
11072
11857
  method: 'POST',
11073
11858
  headers: {
@@ -11075,63 +11860,130 @@ var TTPEcommerceWidget = /*#__PURE__*/function () {
11075
11860
  },
11076
11861
  body: JSON.stringify({
11077
11862
  items: [{
11078
- id: parseInt(variantId, 10),
11863
+ id: parseInt(variantId),
11079
11864
  quantity: quantity || 1
11080
11865
  }]
11081
11866
  })
11082
11867
  });
11083
11868
  case 2:
11084
- response = _context4.v;
11085
- if (response.ok) {
11086
- _context4.n = 3;
11869
+ addResponse = _context5.v;
11870
+ if (addResponse.ok) {
11871
+ _context5.n = 4;
11087
11872
  break;
11088
11873
  }
11089
- console.error('[TTPEcommerceWidget] Failed to add to Shopify cart:', response.status, response.statusText);
11090
- return _context4.a(2);
11874
+ _context5.n = 3;
11875
+ return addResponse.text();
11091
11876
  case 3:
11092
- _context4.n = 4;
11093
- return response.json();
11094
- case 4:
11095
- cartResponse = _context4.v;
11096
- console.log('[TTPEcommerceWidget] Added to Shopify store cart:', cartResponse);
11097
- _context4.n = 5;
11098
- return fetch('/cart.js').then(function (r) {
11099
- return r.json();
11877
+ errorText = _context5.v;
11878
+ console.error('[TTP Ecommerce] /cart/add.js failed:', addResponse.status, errorText);
11879
+ this._sendToVoiceSDK({
11880
+ t: 'cart_add_result',
11881
+ success: false,
11882
+ error: "Cart add failed: HTTP ".concat(addResponse.status),
11883
+ productName: productName || String(variantId),
11884
+ quantity: quantity || 1
11100
11885
  });
11886
+ return _context5.a(2);
11887
+ case 4:
11888
+ _context5.n = 5;
11889
+ return addResponse.json();
11101
11890
  case 5:
11102
- cartData = _context4.v;
11891
+ addResult = _context5.v;
11892
+ if (addResult.items && addResult.items.length > 0) {
11893
+ productName = addResult.items[0].title || '';
11894
+ }
11895
+ _context5.n = 6;
11896
+ return fetch('/cart.js');
11897
+ case 6:
11898
+ cartResponse = _context5.v;
11899
+ _context5.n = 7;
11900
+ return cartResponse.json();
11901
+ case 7:
11902
+ cart = _context5.v;
11903
+ cartRefreshed = false;
11904
+ try {
11905
+ cartRefreshed = this._refreshCartCount(cart.item_count);
11906
+ } catch (e) {
11907
+ console.warn('[TTP Ecommerce] Cart count refresh failed (non-critical):', e);
11908
+ }
11103
11909
  this._handleCartUpdatedFromServer({
11104
11910
  action: 'added',
11105
11911
  product: {
11106
- id: variantId,
11107
- name: productName || ((_cartResponse$items = cartResponse.items) === null || _cartResponse$items === void 0 || (_cartResponse$items = _cartResponse$items[0]) === null || _cartResponse$items === void 0 ? void 0 : _cartResponse$items.title) || 'Product',
11108
- price: price || ((_cartResponse$items2 = cartResponse.items) === null || _cartResponse$items2 === void 0 || (_cartResponse$items2 = _cartResponse$items2[0]) === null || _cartResponse$items2 === void 0 ? void 0 : _cartResponse$items2.price) / 100 || 0
11912
+ name: productName,
11913
+ id: variantId
11109
11914
  },
11110
- cartTotal: cartData.total_price / 100,
11111
- cartItemCount: cartData.item_count
11915
+ cartTotal: cart.total_price / 100,
11916
+ cartItemCount: cart.item_count,
11917
+ currency: cart.currency
11918
+ });
11919
+ this._sendToVoiceSDK({
11920
+ t: 'cart_add_result',
11921
+ success: true,
11922
+ cartRefreshed: cartRefreshed,
11923
+ productName: productName,
11924
+ quantity: quantity || 1,
11925
+ cartTotal: cart.total_price,
11926
+ cartItemCount: cart.item_count,
11927
+ currency: cart.currency
11112
11928
  });
11113
- _context4.n = 7;
11929
+ console.log("[TTP Ecommerce] Cart add result sent: ".concat(productName, ", refreshed=").concat(cartRefreshed, ", ").concat(cart.item_count, " items"));
11930
+ _context5.n = 9;
11114
11931
  break;
11115
- case 6:
11116
- _context4.p = 6;
11117
- _t3 = _context4.v;
11118
- console.error('[TTPEcommerceWidget] Error adding to Shopify cart:', _t3);
11119
- case 7:
11120
- return _context4.a(2);
11932
+ case 8:
11933
+ _context5.p = 8;
11934
+ _t4 = _context5.v;
11935
+ console.error('[TTP Ecommerce] Error in _handleAddToStoreCart:', _t4);
11936
+ this._sendToVoiceSDK({
11937
+ t: 'cart_add_result',
11938
+ success: false,
11939
+ error: _t4.message || 'Failed to add to cart',
11940
+ productName: productName || String(variantId),
11941
+ quantity: quantity || 1
11942
+ });
11943
+ case 9:
11944
+ return _context5.a(2);
11121
11945
  }
11122
- }, _callee4, this, [[1, 6]]);
11946
+ }, _callee5, this, [[1, 8]]);
11123
11947
  }));
11124
11948
  function _handleAddToStoreCart(_x2) {
11125
11949
  return _handleAddToStoreCart2.apply(this, arguments);
11126
11950
  }
11127
11951
  return _handleAddToStoreCart;
11128
- }() // ═══════════════════════════════════════════
11952
+ }()
11953
+ /**
11954
+ * Simple cart count refresh — update common counter selectors.
11955
+ * Theme-specific refresh (Section Rendering API, etc.) can be added later per-theme.
11956
+ * @returns {boolean} Whether any element was updated
11957
+ */
11958
+ )
11959
+ }, {
11960
+ key: "_refreshCartCount",
11961
+ value: function _refreshCartCount(itemCount) {
11962
+ var refreshed = false;
11963
+ var selectors = ['.cart-count', '.cart-count-bubble', '[data-cart-count]', '.js-cart-count', '.cart-item-count', '#CartCount', '.site-header__cart-count', '.cart-count-tag'];
11964
+ selectors.forEach(function (selector) {
11965
+ document.querySelectorAll(selector).forEach(function (el) {
11966
+ el.textContent = itemCount;
11967
+ refreshed = true;
11968
+ });
11969
+ });
11970
+ return refreshed;
11971
+ }
11972
+
11973
+ // ═══════════════════════════════════════════
11129
11974
  // PRODUCT SELECTION (server manages cart)
11130
11975
  // ═══════════════════════════════════════════
11131
11976
  }, {
11132
11977
  key: "_handleProductSelected",
11133
11978
  value: function _handleProductSelected(product) {
11134
11979
  var quantity = product.selectedAmount || 1;
11980
+ try {
11981
+ var _this$widget$voiceInt2;
11982
+ var voiceSDK = (_this$widget$voiceInt2 = this.widget.voiceInterface) === null || _this$widget$voiceInt2 === void 0 || (_this$widget$voiceInt2 = _this$widget$voiceInt2.sdk) === null || _this$widget$voiceInt2 === void 0 ? void 0 : _this$widget$voiceInt2.voiceSDK;
11983
+ if (voiceSDK !== null && voiceSDK !== void 0 && voiceSDK.stopAudioPlayback) {
11984
+ voiceSDK.stopAudioPlayback();
11985
+ }
11986
+ } catch (e) {/* best-effort */}
11135
11987
  this._sendToVoiceSDK({
11136
11988
  t: 'product_selected',
11137
11989
  productId: product.id,
@@ -11159,8 +12011,8 @@ var TTPEcommerceWidget = /*#__PURE__*/function () {
11159
12011
  key: "_sendToVoiceSDK",
11160
12012
  value: function _sendToVoiceSDK(message) {
11161
12013
  try {
11162
- var _this$widget$voiceInt2;
11163
- var voiceSDK = (_this$widget$voiceInt2 = this.widget.voiceInterface) === null || _this$widget$voiceInt2 === void 0 || (_this$widget$voiceInt2 = _this$widget$voiceInt2.sdk) === null || _this$widget$voiceInt2 === void 0 ? void 0 : _this$widget$voiceInt2.voiceSDK;
12014
+ var _this$widget$voiceInt3;
12015
+ var voiceSDK = (_this$widget$voiceInt3 = this.widget.voiceInterface) === null || _this$widget$voiceInt3 === void 0 || (_this$widget$voiceInt3 = _this$widget$voiceInt3.sdk) === null || _this$widget$voiceInt3 === void 0 ? void 0 : _this$widget$voiceInt3.voiceSDK;
11164
12016
  if (voiceSDK && voiceSDK.sendMessage) {
11165
12017
  voiceSDK.sendMessage(message);
11166
12018
  }
@@ -11181,45 +12033,45 @@ var TTPEcommerceWidget = /*#__PURE__*/function () {
11181
12033
  self.ecommerce.handleMessage(payload);
11182
12034
  },
11183
12035
  showProducts: function () {
11184
- var _showProducts = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee5(query) {
12036
+ var _showProducts = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee6(query) {
11185
12037
  var limit,
11186
12038
  sessionId,
11187
12039
  baseUrl,
11188
12040
  response,
11189
12041
  data,
11190
- _args5 = arguments,
11191
- _t4;
11192
- return _regenerator().w(function (_context5) {
11193
- while (1) switch (_context5.p = _context5.n) {
12042
+ _args6 = arguments,
12043
+ _t5;
12044
+ return _regenerator().w(function (_context6) {
12045
+ while (1) switch (_context6.p = _context6.n) {
11194
12046
  case 0:
11195
- limit = _args5.length > 1 && _args5[1] !== undefined ? _args5[1] : 5;
11196
- sessionId = _args5.length > 2 && _args5[2] !== undefined ? _args5[2] : 'test';
12047
+ limit = _args6.length > 1 && _args6[1] !== undefined ? _args6[1] : 5;
12048
+ sessionId = _args6.length > 2 && _args6[2] !== undefined ? _args6[2] : 'test';
11197
12049
  baseUrl = window.__TTP_MOCK_API__ || 'https://backend.talktopc.com';
11198
- _context5.p = 1;
11199
- _context5.n = 2;
12050
+ _context6.p = 1;
12051
+ _context6.n = 2;
11200
12052
  return fetch("".concat(baseUrl, "/api/partner/mock-store/products/search?q=").concat(encodeURIComponent(query), "&limit=").concat(limit, "&sessionId=").concat(encodeURIComponent(sessionId)));
11201
12053
  case 2:
11202
- response = _context5.v;
11203
- _context5.n = 3;
12054
+ response = _context6.v;
12055
+ _context6.n = 3;
11204
12056
  return response.json();
11205
12057
  case 3:
11206
- data = _context5.v;
12058
+ data = _context6.v;
11207
12059
  self.ecommerce.handleShowProducts({
11208
12060
  t: 'show_products',
11209
12061
  products: data.products,
11210
12062
  title: "".concat(data.totalCount, " results for \"").concat(query, "\""),
11211
12063
  layout: data.products.length <= 4 ? 'cards' : 'list'
11212
12064
  });
11213
- _context5.n = 5;
12065
+ _context6.n = 5;
11214
12066
  break;
11215
12067
  case 4:
11216
- _context5.p = 4;
11217
- _t4 = _context5.v;
11218
- console.error('[TTPEcommerceWidget] showProducts failed:', _t4);
12068
+ _context6.p = 4;
12069
+ _t5 = _context6.v;
12070
+ console.error('[TTPEcommerceWidget] showProducts failed:', _t5);
11219
12071
  case 5:
11220
- return _context5.a(2);
12072
+ return _context6.a(2);
11221
12073
  }
11222
- }, _callee5, null, [[1, 4]]);
12074
+ }, _callee6, null, [[1, 4]]);
11223
12075
  }));
11224
12076
  function showProducts(_x3) {
11225
12077
  return _showProducts.apply(this, arguments);
@@ -11330,7 +12182,7 @@ __webpack_require__.r(__webpack_exports__);
11330
12182
  * Follows the same pattern as Styles.generateCSS().
11331
12183
  */
11332
12184
  function generateEcommerceCSS() {
11333
- 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 * 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 when ecommerce layer is active.\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 {\n flex: 1 1 0 !important;\n min-height: 0 !important;\n overflow: hidden !important;\n}\n\n.desktop-voice-section {\n flex-shrink: 1 !important;\n}\n\n.desktop-conversation-panel {\n flex: 1 1 0 !important;\n min-height: 0 !important;\n display: flex !important;\n flex-direction: column !important;\n}\n\n.desktop-conversation-panel .live-transcript-collapsed {\n flex: 1 1 0 !important;\n min-height: 40px !important;\n overflow: hidden !important;\n}\n\n.desktop-conversation-panel .voice-text-input-area {\n margin-top: auto !important;\n padding: 3px 16px !important;\n}\n\n.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.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;\n flex-direction: column;\n width: 100%;\n flex: 1 1 0;\n min-height: 0;\n overflow-x: hidden;\n overflow-y: auto;\n position: relative;\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: #fff;\n border-radius: 14px;\n border: 1px solid #e5e7eb;\n box-shadow: 0 6px 24px rgba(0,0,0,0.08);\n margin: 8px 12px;\n overflow: hidden;\n animation: ttpSlideUp 0.25s ease;\n display: flex;\n flex-direction: column;\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;\n justify-content: space-between;\n align-items: center;\n padding: 10px 14px;\n border-bottom: 1px solid #f1f5f9;\n flex-shrink: 0;\n}\n\n.ttp-picker-title {\n font-size: 12px;\n font-weight: 600;\n color: #64748b;\n}\n\n.ttp-picker-close {\n background: none;\n border: none;\n cursor: pointer;\n color: #94a3b8;\n font-size: 14px;\n padding: 2px 6px;\n border-radius: 4px;\n}\n.ttp-picker-close:hover {\n background: #f1f5f9;\n color: #475569;\n}\n\n/* Horizontal card layout */\n.ttp-products-horizontal {\n display: flex;\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: #cbd5e1; border-radius: 2px; }\n\n/* Vertical list layout */\n.ttp-products-vertical {\n padding: 8px 12px;\n overflow-y: auto;\n display: flex;\n flex-direction: column;\n gap: 6px;\n flex: 1;\n min-height: 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 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: 140px;\n flex-shrink: 0;\n background: #fff;\n border-radius: 12px;\n border: 1px solid #e5e7eb;\n overflow: hidden;\n transition: border-color 0.15s;\n}\n.ttp-product-card:hover { border-color: #3b82f6; }\n\n.ttp-product-image {\n height: 85px;\n background: #f8fafc;\n display: flex;\n align-items: center;\n justify-content: center;\n position: relative;\n overflow: hidden;\n}\n.ttp-product-image img {\n max-width: 90%;\n max-height: 75px;\n object-fit: contain;\n}\n\n.ttp-stock-badge {\n position: absolute;\n top: 4px; right: 4px;\n background: #fef2f2;\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: 8px 10px 10px;\n}\n\n.ttp-product-name {\n font-size: 12px;\n font-weight: 600;\n color: #1e293b;\n line-height: 1.3;\n min-height: 30px;\n}\n\n.ttp-product-meta {\n font-size: 10px;\n color: #94a3b8;\n margin-top: 2px;\n}\n\n.ttp-product-footer {\n display: flex;\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: #1e293b;\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;\n align-items: center;\n gap: 10px;\n padding: 8px 10px;\n background: #fff;\n border-radius: 10px;\n border: 1px solid #e5e7eb;\n transition: border-color 0.15s;\n}\n.ttp-product-compact:hover { border-color: #3b82f6; }\n\n.ttp-product-image-sm {\n width: 40px; height: 40px;\n border-radius: 8px;\n background: #f8fafc;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n overflow: hidden;\n}\n.ttp-product-image-sm img {\n max-width: 36px;\n max-height: 36px;\n object-fit: contain;\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}\n.ttp-product-compact .ttp-product-action {\n text-align: right;\n flex-shrink: 0;\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: #ef4444;\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: 4px 14px;\n border-radius: 8px;\n border: none;\n font-size: 12px;\n font-weight: 600;\n cursor: pointer;\n background: #3b82f6;\n color: #fff;\n transition: all 0.15s;\n}\n.ttp-add-btn:hover { background: #2563eb; }\n.ttp-add-btn:active { transform: scale(0.95); }\n.ttp-add-btn.ttp-added { background: #10b981 !important; }\n.ttp-add-btn:disabled { cursor: default; }\n.ttp-add-btn.ttp-update-btn { background: #f59e0b; }\n.ttp-add-btn.ttp-update-btn:hover { background: #d97706; }\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.ttp-cart-toast {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 14px;\n margin: 0 12px;\n background: #f0fdf4;\n border-radius: 12px;\n border: 1px solid #bbf7d0;\n animation: ttpToastIn 0.25s ease;\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n z-index: 10;\n box-shadow: 0 4px 12px rgba(0,0,0,0.1);\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; height: 24px;\n border-radius: 50%;\n background: #10b981;\n color: #fff;\n display: flex;\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: #166534; }\n.ttp-toast-product {\n font-size: 11px;\n color: #15803d;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.ttp-toast-undo {\n background: none;\n border: 1px solid #86efac;\n border-radius: 6px;\n padding: 3px 8px;\n font-size: 10px;\n color: #15803d;\n cursor: pointer;\n font-weight: 600;\n flex-shrink: 0;\n}\n.ttp-toast-undo:hover { background: #dcfce7; }\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 SUMMARY BAR\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-cart-summary {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 8px 14px;\n margin: 4px 12px 8px;\n background: #f8fafc;\n border-radius: 10px;\n border: 1px solid #e2e8f0;\n}\n\n.ttp-cart-info {\n display: flex;\n align-items: center;\n gap: 6px;\n}\n.ttp-cart-icon { font-size: 14px; }\n.ttp-cart-count { font-size: 12px; font-weight: 600; color: #475569; }\n.ttp-cart-separator { font-size: 12px; color: #94a3b8; }\n.ttp-cart-total { font-size: 13px; font-weight: 700; color: #1e293b; }\n.ttp-cart-view {\n font-size: 12px;\n font-weight: 600;\n color: #6366f1;\n text-decoration: none;\n cursor: pointer;\n white-space: nowrap;\n}\n.ttp-cart-view:hover {\n color: #4f46e5;\n text-decoration: underline;\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;\n align-items: center;\n gap: 2px;\n margin: 4px 0;\n}\n\n.ttp-stepper-btn {\n width: 24px;\n height: 24px;\n border-radius: 6px;\n border: 1px solid #e2e8f0;\n background: #f8fafc;\n color: #475569;\n font-size: 14px;\n font-weight: 600;\n cursor: pointer;\n display: flex;\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: #e2e8f0;\n border-color: #cbd5e1;\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: #1e293b;\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;\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;\n flex-direction: column;\n align-items: center;\n gap: 4px;\n}\n\n ";
12185
+ 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 * 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 when ecommerce layer is active.\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 {\n flex: 1 1 0 !important;\n min-height: 0 !important;\n overflow: hidden !important;\n}\n\n.desktop-voice-section {\n flex-shrink: 1 !important;\n}\n\n.desktop-conversation-panel {\n flex: 1 1 0 !important;\n min-height: 0 !important;\n display: flex !important;\n flex-direction: column !important;\n}\n\n.desktop-conversation-panel .live-transcript-collapsed {\n flex: 1 1 0 !important;\n min-height: 40px !important;\n overflow: hidden !important;\n}\n\n.desktop-conversation-panel .voice-text-input-area {\n margin-top: auto !important;\n padding: 3px 16px !important;\n}\n\n.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.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;\n flex-direction: column;\n width: 100%;\n flex: 1 1 0;\n min-height: 0;\n overflow-x: hidden;\n overflow-y: auto;\n position: relative;\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: #fff;\n border-radius: 14px;\n border: 1px solid #e5e7eb;\n box-shadow: 0 6px 24px rgba(0,0,0,0.08);\n margin: 8px 12px;\n overflow: hidden;\n animation: ttpSlideUp 0.25s ease;\n display: flex;\n flex-direction: column;\n flex: 1;\n min-height: 0;\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;\n justify-content: space-between;\n align-items: center;\n padding: 10px 14px;\n border-bottom: 1px solid #f1f5f9;\n flex-shrink: 0;\n}\n\n.ttp-picker-title {\n font-size: 12px;\n font-weight: 600;\n color: #64748b;\n}\n\n.ttp-picker-close {\n background: none;\n border: none;\n cursor: pointer;\n color: #94a3b8;\n font-size: 14px;\n padding: 2px 6px;\n border-radius: 4px;\n}\n.ttp-picker-close:hover {\n background: #f1f5f9;\n color: #475569;\n}\n\n/* Horizontal card layout */\n.ttp-products-horizontal {\n display: flex;\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: #cbd5e1; border-radius: 2px; }\n\n/* Vertical list layout */\n.ttp-products-vertical {\n padding: 8px 12px;\n overflow-y: auto;\n display: flex;\n flex-direction: column;\n gap: 6px;\n flex: 1;\n min-height: 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 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: 140px;\n flex-shrink: 0;\n background: #fff;\n border-radius: 12px;\n border: 1px solid #e5e7eb;\n overflow: hidden;\n transition: border-color 0.15s;\n}\n.ttp-product-card:hover { border-color: #3b82f6; }\n\n.ttp-product-image {\n height: 85px;\n background: #f8fafc;\n display: flex;\n align-items: center;\n justify-content: center;\n position: relative;\n overflow: hidden;\n}\n.ttp-product-image img {\n max-width: 90%;\n max-height: 75px;\n object-fit: contain;\n}\n\n.ttp-stock-badge {\n position: absolute;\n top: 4px; right: 4px;\n background: #fef2f2;\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: 8px 10px 14px;\n}\n\n.ttp-product-name {\n font-size: 12px;\n font-weight: 600;\n color: #1e293b;\n line-height: 1.3;\n min-height: 30px;\n}\n\n.ttp-product-meta {\n font-size: 10px;\n color: #94a3b8;\n margin-top: 2px;\n}\n\n.ttp-product-footer {\n display: flex;\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: #1e293b;\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;\n align-items: center;\n gap: 10px;\n padding: 8px 10px;\n background: #fff;\n border-radius: 10px;\n border: 1px solid #e5e7eb;\n transition: border-color 0.15s;\n}\n.ttp-product-compact:hover { border-color: #3b82f6; }\n\n.ttp-product-image-sm {\n width: 40px; height: 40px;\n border-radius: 8px;\n background: #f8fafc;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n overflow: hidden;\n}\n.ttp-product-image-sm img {\n max-width: 36px;\n max-height: 36px;\n object-fit: contain;\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}\n.ttp-product-compact .ttp-product-action {\n text-align: right;\n flex-shrink: 0;\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: #ef4444;\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: 4px 14px;\n border-radius: 8px;\n border: none;\n font-size: 12px;\n font-weight: 600;\n cursor: pointer;\n background: #3b82f6;\n color: #fff;\n transition: all 0.15s;\n}\n.ttp-add-btn:hover { background: #2563eb; }\n.ttp-add-btn:active { transform: scale(0.95); }\n.ttp-add-btn.ttp-added { background: #10b981 !important; }\n.ttp-add-btn:disabled { cursor: default; }\n.ttp-add-btn.ttp-update-btn { background: #f59e0b; }\n.ttp-add-btn.ttp-update-btn:hover { background: #d97706; }\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.ttp-cart-toast {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 14px;\n margin: 0 12px;\n background: #f0fdf4;\n border-radius: 12px;\n border: 1px solid #bbf7d0;\n animation: ttpToastIn 0.25s ease;\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n z-index: 10;\n box-shadow: 0 4px 12px rgba(0,0,0,0.1);\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; height: 24px;\n border-radius: 50%;\n background: #10b981;\n color: #fff;\n display: flex;\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: #166534; }\n.ttp-toast-product {\n font-size: 11px;\n color: #15803d;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.ttp-toast-undo {\n background: none;\n border: 1px solid #86efac;\n border-radius: 6px;\n padding: 3px 8px;\n font-size: 10px;\n color: #15803d;\n cursor: pointer;\n font-weight: 600;\n flex-shrink: 0;\n}\n.ttp-toast-undo:hover { background: #dcfce7; }\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 SUMMARY BAR\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-cart-summary {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 8px 14px;\n margin: 4px 12px 8px;\n background: #f8fafc;\n border-radius: 10px;\n border: 1px solid #e2e8f0;\n}\n\n.ttp-cart-info {\n display: flex;\n align-items: center;\n gap: 6px;\n}\n.ttp-cart-icon { font-size: 14px; }\n.ttp-cart-count { font-size: 12px; font-weight: 600; color: #475569; }\n.ttp-cart-separator { font-size: 12px; color: #94a3b8; }\n.ttp-cart-total { font-size: 13px; font-weight: 700; color: #1e293b; }\n.ttp-cart-view {\n font-size: 12px;\n font-weight: 600;\n color: #6366f1;\n text-decoration: none;\n cursor: pointer;\n white-space: nowrap;\n}\n.ttp-cart-view:hover {\n color: #4f46e5;\n text-decoration: underline;\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;\n align-items: center;\n gap: 2px;\n margin: 4px 0;\n}\n\n.ttp-stepper-btn {\n width: 24px;\n height: 24px;\n border-radius: 6px;\n border: 1px solid #e2e8f0;\n background: #f8fafc;\n color: #475569;\n font-size: 14px;\n font-weight: 600;\n cursor: pointer;\n display: flex;\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: #e2e8f0;\n border-color: #cbd5e1;\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: #1e293b;\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;\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;\n flex-direction: column;\n align-items: center;\n gap: 4px;\n}\n\n ";
11334
12186
  }
11335
12187
 
11336
12188
  /***/ }),
@@ -11720,19 +12572,23 @@ var MicPermissionModal = /*#__PURE__*/function () {
11720
12572
  }();
11721
12573
  /**
11722
12574
  * Pre-prompt modal - shown before requesting browser permission
12575
+ * Uses a transparent overlay (no blur) so the call view is visible behind it
11723
12576
  */
11724
12577
  function showPrePromptModal(onAccept, onCancel) {
11725
12578
  var modal = new MicPermissionModal();
11726
12579
  var overlay = modal.createOverlay();
11727
12580
  var deviceInfo = (0,_MicPermissionUtils_js__WEBPACK_IMPORTED_MODULE_0__.getDeviceInfo)();
12581
+
12582
+ // Remove blur and darken so the call view is visible behind
12583
+ overlay.style.background = 'rgba(0, 0, 0, 0.3)';
12584
+ overlay.style.backdropFilter = 'none';
12585
+ overlay.style.webkitBackdropFilter = 'none';
11728
12586
  var modalDiv = document.createElement('div');
11729
12587
  modalDiv.className = 'mic-permission-modal';
11730
12588
  modalDiv.innerHTML = "\n <div class=\"mic-permission-modal__icon\">\uD83C\uDFA4</div>\n <h2 class=\"mic-permission-modal__title\">Enable Your Microphone</h2>\n <p class=\"mic-permission-modal__description\">\n To start a voice call with our agent, we need access to your microphone.\n </p>\n <div class=\"mic-permission-modal__highlight\">\n <span class=\"mic-permission-modal__highlight-icon\">\uD83D\uDC46</span>\n <span>Please click <strong>\"Allow\"</strong> on the next popup</span>\n </div>\n ".concat(deviceInfo.isIOS ? "\n <div class=\"mic-permission-modal__note\">\n <span>\u2139\uFE0F</span>\n <span>On iPhone/iPad, you may need to allow microphone access in your device Settings.</span>\n </div>\n " : '', "\n <div class=\"mic-permission-modal__actions\">\n <button class=\"mic-permission-modal__btn mic-permission-modal__btn--primary\" data-action=\"accept\">\n Continue\n </button>\n <button class=\"mic-permission-modal__btn mic-permission-modal__btn--secondary\" data-action=\"cancel\">\n Not Now\n </button>\n </div>\n ");
11731
12589
  overlay.appendChild(modalDiv);
11732
12590
  modal.overlay = overlay;
11733
12591
  modal.modal = modalDiv;
11734
-
11735
- // Event handlers
11736
12592
  modalDiv.querySelector('[data-action="accept"]').addEventListener('click', function () {
11737
12593
  modal.close();
11738
12594
  onAccept();
@@ -11741,8 +12597,6 @@ function showPrePromptModal(onAccept, onCancel) {
11741
12597
  modal.close();
11742
12598
  onCancel();
11743
12599
  });
11744
-
11745
- // Close on overlay click
11746
12600
  overlay.addEventListener('click', function (e) {
11747
12601
  if (e.target === overlay) {
11748
12602
  modal.close();
@@ -14321,6 +15175,12 @@ function _setPrototypeOf(t, e) { return _setPrototypeOf = Object.setPrototypeOf
14321
15175
 
14322
15176
 
14323
15177
 
15178
+ // iOS FIX: Shared AudioContext for playback that persists across AudioPlayer instances.
15179
+ // iOS WebKit doesn't release audio hardware synchronously when AudioContext.close() is called,
15180
+ // so creating a new AudioContext immediately after closing the old one can fail silently.
15181
+ var _sharedPlayerContext = null;
15182
+ var _sharedPlayerSampleRate = null;
15183
+
14324
15184
  /**
14325
15185
 
14326
15186
  * AudioPlayer v2 - Handles multiple audio formats with codec support
@@ -14357,8 +15217,6 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
14357
15217
 
14358
15218
  // Version identification log
14359
15219
  console.log('%c🔊 AUDIO PLAYER - MIC/SPEAKER RESET ON NEW CALL ENABLED', 'background: #10b981; color: white; font-size: 14px; font-weight: bold; padding: 4px 8px; border-radius: 4px;');
14360
- console.log('%cFix: Mic/speaker mute state + UI fully reset on call end and call start', 'background: #d1fae5; color: #065f46; font-size: 12px; padding: 2px 6px; border-radius: 3px;');
14361
- console.log('%cBuild: 2026-02-26-MIC-SPEAKER-RESET', 'background: #e0e7ff; color: #1e40af; font-size: 11px; padding: 2px 6px; border-radius: 3px;');
14362
15220
  console.log('');
14363
15221
  _this.config = config;
14364
15222
  _this.audioContext = null;
@@ -15692,43 +16550,84 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
15692
16550
  }, {
15693
16551
  key: "initializeAudioContext",
15694
16552
  value: function initializeAudioContext() {
15695
- var _this$outputFormat5, _this$outputFormat6;
16553
+ var _this$outputFormat5,
16554
+ _this7 = this,
16555
+ _this$outputFormat6;
15696
16556
  // Use negotiated sample rate, default to 44100Hz to match server output
15697
- // This ensures AudioContext matches server output before format negotiation completes
15698
16557
  var desiredSampleRate = ((_this$outputFormat5 = this.outputFormat) === null || _this$outputFormat5 === void 0 ? void 0 : _this$outputFormat5.sampleRate) || 44100;
15699
-
15700
- // If backend sends non-standard rate (like 22050Hz), check if browser supports it
15701
- // If not, prefer 44100Hz (default) or 48000Hz to avoid resampling issues
15702
16558
  if (desiredSampleRate !== 48000 && desiredSampleRate !== 44100) {
15703
- // For non-standard rates, try to use browser's native rate to avoid resampling
15704
- // This prevents tiny cuts caused by browser resampling
15705
16559
  console.log("\u2139\uFE0F AudioPlayer: Backend requested ".concat(desiredSampleRate, "Hz, but browser may resample"));
15706
16560
  console.log(" Consider requesting 44100Hz or 48000Hz from backend to avoid resampling and improve quality");
15707
16561
  }
15708
16562
 
15709
- // Check if AudioContext exists and if it matches the desired sample rate
16563
+ // Check if current instance AudioContext exists and matches
15710
16564
  if (this.audioContext) {
15711
16565
  var currentSampleRate = this.audioContext.sampleRate;
15712
- // If sample rate differs significantly, recreate the AudioContext
15713
16566
  if (Math.abs(currentSampleRate - desiredSampleRate) > 100) {
15714
16567
  console.warn("\u26A0\uFE0F AudioPlayer: AudioContext sample rate (".concat(currentSampleRate, "Hz) doesn't match format (").concat(desiredSampleRate, "Hz), recreating..."));
15715
16568
  this.stopImmediate();
15716
16569
  this._cleanupAudioContext();
15717
16570
  } else {
15718
- // Sample rate matches (or close enough), no need to recreate
15719
16571
  return;
15720
16572
  }
15721
16573
  }
16574
+
16575
+ // iOS FIX: Reuse shared AudioContext if available and compatible.
16576
+ // iOS WebKit doesn't release audio hardware synchronously on AudioContext.close(),
16577
+ // causing newly created AudioContexts to fail silently.
16578
+ var canReuseShared = _sharedPlayerContext && _sharedPlayerContext.state !== 'closed' && _sharedPlayerSampleRate === desiredSampleRate;
16579
+ if (canReuseShared) {
16580
+ console.log("\u267B\uFE0F AudioPlayer: Reusing shared AudioContext at ".concat(desiredSampleRate, "Hz (iOS-safe)"));
16581
+ this.audioContext = _sharedPlayerContext;
16582
+ var setupAfterResume = function setupAfterResume() {
16583
+ _this7.setupAudioContextStateMonitoring();
16584
+ if (_this7.gainNode) {
16585
+ try {
16586
+ _this7.gainNode.disconnect();
16587
+ } catch (e) {
16588
+ console.warn('⚠️ AudioPlayer: Error disconnecting old GainNode:', e);
16589
+ }
16590
+ }
16591
+ _this7.gainNode = _this7.audioContext.createGain();
16592
+ _this7.gainNode.gain.value = 1.0;
16593
+ _this7.gainNode.connect(_this7.audioContext.destination);
16594
+ if (!_this7._audioContextPrimed) {
16595
+ _this7._primeAudioContext();
16596
+ }
16597
+ };
16598
+ if (this.audioContext.state === 'suspended') {
16599
+ this.audioContext.resume().then(function () {
16600
+ return setupAfterResume();
16601
+ }).catch(function (e) {
16602
+ console.warn('⚠️ AudioPlayer: Error resuming shared context:', e);
16603
+ setupAfterResume();
16604
+ });
16605
+ } else {
16606
+ setupAfterResume();
16607
+ }
16608
+ return;
16609
+ }
15722
16610
  console.log("\uD83C\uDFB5 AudioPlayer: Creating AudioContext at ".concat(desiredSampleRate, "Hz (from outputFormat: ").concat(((_this$outputFormat6 = this.outputFormat) === null || _this$outputFormat6 === void 0 ? void 0 : _this$outputFormat6.sampleRate) || 'not set', ")"));
16611
+
16612
+ // Close old shared context if sample rate changed
16613
+ if (_sharedPlayerContext && _sharedPlayerContext.state !== 'closed') {
16614
+ console.log('🔄 AudioPlayer: Closing old shared AudioContext (sample rate changed)');
16615
+ try {
16616
+ _sharedPlayerContext.close();
16617
+ } catch (e) {
16618
+ console.warn('⚠️ AudioPlayer: Error closing old shared context:', e);
16619
+ }
16620
+ }
15723
16621
  try {
15724
- // Try to create with specific sample rate
15725
16622
  this.audioContext = new (window.AudioContext || window.webkitAudioContext)({
15726
16623
  sampleRate: desiredSampleRate,
15727
- latencyHint: 'playback' // ✅ Better for streaming audio - prioritizes smooth playback over low latency
16624
+ latencyHint: 'playback'
15728
16625
  });
15729
- console.log("\u2705 AudioContext created at ".concat(this.audioContext.sampleRate, "Hz (requested: ").concat(desiredSampleRate, "Hz)"));
15730
16626
 
15731
- // CRITICAL: If browser can't create requested sample rate, we have a problem
16627
+ // Store as shared context
16628
+ _sharedPlayerContext = this.audioContext;
16629
+ _sharedPlayerSampleRate = desiredSampleRate;
16630
+ console.log("\u2705 AudioContext created at ".concat(this.audioContext.sampleRate, "Hz (requested: ").concat(desiredSampleRate, "Hz)"));
15732
16631
  if (Math.abs(this.audioContext.sampleRate - desiredSampleRate) > 100) {
15733
16632
  console.error("\u274C CRITICAL: Browser sample rate mismatch!");
15734
16633
  console.error(" Requested: ".concat(desiredSampleRate, "Hz"));
@@ -15739,34 +16638,23 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
15739
16638
  console.warn("\u26A0\uFE0F Browser adjusted sample rate: ".concat(desiredSampleRate, "Hz \u2192 ").concat(this.audioContext.sampleRate, "Hz"));
15740
16639
  console.warn(" Browser will automatically resample audio.");
15741
16640
  }
15742
-
15743
- // ✅ NEW: Monitor AudioContext state changes (handles mic permission, tab switching, etc.)
15744
16641
  this.setupAudioContextStateMonitoring();
15745
-
15746
- // Create GainNode for volume/mute control
15747
16642
  this.gainNode = this.audioContext.createGain();
15748
- this.gainNode.gain.value = 1.0; // Default to full volume
16643
+ this.gainNode.gain.value = 1.0;
15749
16644
  this.gainNode.connect(this.audioContext.destination);
15750
16645
  console.log('✅ AudioPlayer: GainNode created for volume control');
15751
-
15752
- // Prime audio hardware for Android
15753
16646
  this._primeAudioContext();
15754
16647
  } catch (error) {
15755
- // Fallback to default if browser doesn't support custom sample rate
15756
16648
  console.error("\u274C Failed to create AudioContext:", error);
15757
16649
  this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
16650
+ _sharedPlayerContext = this.audioContext;
16651
+ _sharedPlayerSampleRate = this.audioContext.sampleRate;
15758
16652
  console.log("\u2139\uFE0F Using browser default: ".concat(this.audioContext.sampleRate, "Hz"));
15759
-
15760
- // Setup monitoring for fallback AudioContext too
15761
16653
  this.setupAudioContextStateMonitoring();
15762
-
15763
- // Create GainNode for volume/mute control
15764
16654
  this.gainNode = this.audioContext.createGain();
15765
- this.gainNode.gain.value = 1.0; // Default to full volume
16655
+ this.gainNode.gain.value = 1.0;
15766
16656
  this.gainNode.connect(this.audioContext.destination);
15767
16657
  console.log('✅ AudioPlayer: GainNode created for volume control (fallback)');
15768
-
15769
- // Prime audio hardware for Android
15770
16658
  this._primeAudioContext();
15771
16659
  }
15772
16660
  }
@@ -15827,7 +16715,7 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
15827
16715
  }, {
15828
16716
  key: "_cleanupAudioContext",
15829
16717
  value: function _cleanupAudioContext() {
15830
- console.log('[TTP AudioPlayer] 🧹 Cleaning up AudioContext (refactored method)');
16718
+ console.log('[TTP AudioPlayer] 🧹 Cleaning up AudioContext (iOS-safe: keeping shared context alive)');
15831
16719
 
15832
16720
  // Disconnect and cleanup gainNode
15833
16721
  if (this.gainNode) {
@@ -15851,24 +16739,34 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
15851
16739
  this._waitForReadyStateHandler = null;
15852
16740
  }
15853
16741
 
15854
- // Close context if open
15855
- if (this.audioContext && this.audioContext.state !== 'closed') {
15856
- this.audioContext.close();
16742
+ // iOS FIX: Suspend instead of closing the AudioContext.
16743
+ // The shared context will be reused by the next AudioPlayer instance.
16744
+ // Closing it would cause the next instance to create a new one,
16745
+ // which fails on iOS due to the audio hardware not being released in time.
16746
+ if (this.audioContext && this.audioContext.state === 'running') {
16747
+ this.audioContext.suspend().catch(function (e) {
16748
+ console.warn('⚠️ AudioPlayer: Error suspending AudioContext during cleanup:', e);
16749
+ });
15857
16750
  }
15858
16751
 
15859
- // Set to null after cleanup
16752
+ // Release instance reference but keep shared context alive
15860
16753
  this.audioContext = null;
15861
16754
  this._audioContextPrimed = false;
15862
16755
  }
15863
16756
 
15864
16757
  /**
15865
- * Setup AudioContext state change monitoring
15866
- * Handles mic permission grants, tab switching, browser suspension, etc.
16758
+ * Fully close the shared AudioContext.
16759
+ * Only call this when the widget is being completely removed from the page.
15867
16760
  */
15868
16761
  }, {
15869
16762
  key: "setupAudioContextStateMonitoring",
15870
- value: function setupAudioContextStateMonitoring() {
15871
- var _this7 = this;
16763
+ value:
16764
+ /**
16765
+ * Setup AudioContext state change monitoring
16766
+ * Handles mic permission grants, tab switching, browser suspension, etc.
16767
+ */
16768
+ function setupAudioContextStateMonitoring() {
16769
+ var _this8 = this;
15872
16770
  if (!this.audioContext) {
15873
16771
  return;
15874
16772
  }
@@ -15881,35 +16779,35 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
15881
16779
  // Create handler that references this.audioContext dynamically
15882
16780
  this._audioContextStateChangeHandler = function () {
15883
16781
  // Null check required because audioContext may be cleaned up while handler is queued
15884
- if (!_this7.audioContext) {
16782
+ if (!_this8.audioContext) {
15885
16783
  console.warn('⚠️ AudioPlayer: State change handler fired but AudioContext is null');
15886
16784
  return;
15887
16785
  }
15888
- console.log("\uD83C\uDFB5 AudioPlayer: AudioContext state changed to: ".concat(_this7.audioContext.state));
15889
- if (_this7.audioContext.state === 'suspended' && _this7.isPlaying) {
16786
+ console.log("\uD83C\uDFB5 AudioPlayer: AudioContext state changed to: ".concat(_this8.audioContext.state));
16787
+ if (_this8.audioContext.state === 'suspended' && _this8.isPlaying) {
15890
16788
  // AudioContext was suspended during playback (tab switch, mic permission, etc.)
15891
16789
  console.warn('⚠️ AudioPlayer: AudioContext suspended during playback');
15892
16790
  // Note: Playback will pause automatically, but we should handle queue processing
15893
16791
  // The state change will be handled when we try to process next frame
15894
- } else if (_this7.audioContext.state === 'running' && !_this7.isPlaying && (_this7.audioQueue.length > 0 || _this7.pcmChunkQueue.length > 0 || _this7.preparedBuffer.length > 0)) {
16792
+ } else if (_this8.audioContext.state === 'running' && !_this8.isPlaying && (_this8.audioQueue.length > 0 || _this8.pcmChunkQueue.length > 0 || _this8.preparedBuffer.length > 0)) {
15895
16793
  // AudioContext resumed and we have queued frames
15896
16794
  // This handles: mic permission grant, tab switching back, browser resume, etc.
15897
16795
  console.log('✅ AudioPlayer: AudioContext resumed - resuming queue processing');
15898
16796
 
15899
16797
  // Resume queue processing if we have frames
15900
- if (_this7.audioQueue.length > 0 && !_this7.isProcessingQueue) {
16798
+ if (_this8.audioQueue.length > 0 && !_this8.isProcessingQueue) {
15901
16799
  setTimeout(function () {
15902
- return _this7.processQueue();
16800
+ return _this8.processQueue();
15903
16801
  }, 50);
15904
16802
  }
15905
- if (_this7.pcmChunkQueue.length > 0 && !_this7.isProcessingPcmQueue) {
16803
+ if (_this8.pcmChunkQueue.length > 0 && !_this8.isProcessingPcmQueue) {
15906
16804
  setTimeout(function () {
15907
- return _this7.processPcmQueue();
16805
+ return _this8.processPcmQueue();
15908
16806
  }, 50);
15909
16807
  }
15910
- if (_this7.preparedBuffer.length > 0 && !_this7.isSchedulingFrames) {
16808
+ if (_this8.preparedBuffer.length > 0 && !_this8.isSchedulingFrames) {
15911
16809
  setTimeout(function () {
15912
- return _this7.scheduleFrames();
16810
+ return _this8.scheduleFrames();
15913
16811
  }, 50);
15914
16812
  }
15915
16813
  }
@@ -15928,7 +16826,7 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
15928
16826
  key: "processQueue",
15929
16827
  value: (function () {
15930
16828
  var _processQueue = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee6() {
15931
- var _this8 = this;
16829
+ var _this9 = this;
15932
16830
  var audioBlob, wasFirstPlay, audioContext, arrayBuffer, audioBuffer, shouldEmitStart, source, _t4;
15933
16831
  return _regenerator().w(function (_context7) {
15934
16832
  while (1) switch (_context7.p = _context7.n) {
@@ -15994,22 +16892,22 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
15994
16892
  // Handle end
15995
16893
 
15996
16894
  source.onended = function () {
15997
- _this8.currentSource = null;
15998
- _this8.isProcessingQueue = false;
16895
+ _this9.currentSource = null;
16896
+ _this9.isProcessingQueue = false;
15999
16897
 
16000
16898
  // Process next chunk
16001
16899
 
16002
- if (_this8.audioQueue.length > 0) {
16900
+ if (_this9.audioQueue.length > 0) {
16003
16901
  setTimeout(function () {
16004
- return _this8.processQueue();
16902
+ return _this9.processQueue();
16005
16903
  }, 50);
16006
16904
  } else {
16007
16905
  // No more chunks - stop after delay
16008
16906
 
16009
16907
  setTimeout(function () {
16010
- if (_this8.audioQueue.length === 0 && !_this8.currentSource) {
16011
- _this8.isPlaying = false;
16012
- _this8.emit('playbackStopped');
16908
+ if (_this9.audioQueue.length === 0 && !_this9.currentSource) {
16909
+ _this9.isPlaying = false;
16910
+ _this9.emit('playbackStopped');
16013
16911
  }
16014
16912
  }, 100);
16015
16913
  }
@@ -16032,7 +16930,7 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
16032
16930
  if (this.audioQueue.length > 0) {
16033
16931
  this.isProcessingQueue = false;
16034
16932
  setTimeout(function () {
16035
- return _this8.processQueue();
16933
+ return _this9.processQueue();
16036
16934
  }, 100);
16037
16935
  } else {
16038
16936
  this.isPlaying = false;
@@ -16181,7 +17079,7 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
16181
17079
  }, {
16182
17080
  key: "markNewSentence",
16183
17081
  value: function markNewSentence(text) {
16184
- var _this9 = this;
17082
+ var _this0 = this;
16185
17083
  var wasStopped = this._isStopped;
16186
17084
  var isCurrentlyPlaying = this.isPlaying || this.scheduledSources.size > 0;
16187
17085
 
@@ -16237,20 +17135,20 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
16237
17135
  var sentenceText = text; // Capture for timeout callback
16238
17136
  this._emptySentenceTimeout = setTimeout(function () {
16239
17137
  // Check if this sentence still has no chunks after timeout
16240
- if (_this9.pendingSentenceText === sentenceText && _this9.scheduledBuffers === 0 && _this9.preparedBuffer.length === 0 && _this9.pcmChunkQueue.length === 0 && !_this9._isStopped) {
17138
+ if (_this0.pendingSentenceText === sentenceText && _this0.scheduledBuffers === 0 && _this0.preparedBuffer.length === 0 && _this0.pcmChunkQueue.length === 0 && !_this0._isStopped) {
16241
17139
  console.warn("\u26A0\uFE0F AudioPlayer: Empty sentence detected after 5s timeout - no chunks received for: \"".concat(sentenceText.substring(0, 40), "...\""));
16242
17140
  // Clear pending sentence to unblock next sentence
16243
- if (_this9.pendingSentenceText === sentenceText) {
16244
- _this9.pendingSentenceText = null;
17141
+ if (_this0.pendingSentenceText === sentenceText) {
17142
+ _this0.pendingSentenceText = null;
16245
17143
  }
16246
17144
  // Emit playbackStopped to allow next sentence to start
16247
17145
  // Only if we're not currently playing (to avoid interrupting real playback)
16248
- if (!_this9.isPlaying && _this9.scheduledSources.size === 0) {
17146
+ if (!_this0.isPlaying && _this0.scheduledSources.size === 0) {
16249
17147
  console.log('🛑 AudioPlayer: Emitting playbackStopped for empty sentence timeout');
16250
- _this9.emit('playbackStopped');
17148
+ _this0.emit('playbackStopped');
16251
17149
  }
16252
17150
  }
16253
- _this9._emptySentenceTimeout = null;
17151
+ _this0._emptySentenceTimeout = null;
16254
17152
  }, 5000); // 5 second timeout - adjust based on expected chunk arrival rate
16255
17153
  }
16256
17154
 
@@ -16260,14 +17158,14 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
16260
17158
  }, {
16261
17159
  key: "startTranscriptChecker",
16262
17160
  value: function startTranscriptChecker() {
16263
- var _this0 = this;
17161
+ var _this1 = this;
16264
17162
  if (this.isCheckingTranscripts) return;
16265
17163
  this.isCheckingTranscripts = true;
16266
17164
  console.log('📝 AudioPlayer: Transcript checker started');
16267
17165
  var _checkLoop = function checkLoop() {
16268
- if (!_this0.isCheckingTranscripts || !_this0.audioContext) return;
16269
- var currentTime = _this0.audioContext.currentTime;
16270
- var _iterator = _createForOfIteratorHelper(_this0.sentenceTimings),
17166
+ if (!_this1.isCheckingTranscripts || !_this1.audioContext) return;
17167
+ var currentTime = _this1.audioContext.currentTime;
17168
+ var _iterator = _createForOfIteratorHelper(_this1.sentenceTimings),
16271
17169
  _step;
16272
17170
  try {
16273
17171
  for (_iterator.s(); !(_step = _iterator.n()).done;) {
@@ -16275,7 +17173,7 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
16275
17173
  if (!timing.displayed && currentTime >= timing.startTime) {
16276
17174
  timing.displayed = true;
16277
17175
  console.log("\uD83D\uDCDD AudioPlayer: Display transcript at ".concat(currentTime.toFixed(3), "s: \"").concat(timing.text.substring(0, 40), "...\""));
16278
- _this0.emit('transcriptDisplay', {
17176
+ _this1.emit('transcriptDisplay', {
16279
17177
  text: timing.text
16280
17178
  });
16281
17179
  }
@@ -16285,10 +17183,10 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
16285
17183
  } finally {
16286
17184
  _iterator.f();
16287
17185
  }
16288
- if (_this0.isPlaying || _this0.scheduledBuffers > 0) {
17186
+ if (_this1.isPlaying || _this1.scheduledBuffers > 0) {
16289
17187
  requestAnimationFrame(_checkLoop);
16290
17188
  } else {
16291
- _this0.isCheckingTranscripts = false;
17189
+ _this1.isCheckingTranscripts = false;
16292
17190
  console.log('📝 AudioPlayer: Transcript checker stopped');
16293
17191
  }
16294
17192
  };
@@ -16409,6 +17307,16 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
16409
17307
  this._cleanupAudioContext();
16410
17308
  this.removeAllListeners();
16411
17309
  }
17310
+ }], [{
17311
+ key: "closeSharedContext",
17312
+ value: function closeSharedContext() {
17313
+ if (_sharedPlayerContext && _sharedPlayerContext.state !== 'closed') {
17314
+ console.log('🗑️ AudioPlayer: Closing shared AudioContext');
17315
+ _sharedPlayerContext.close();
17316
+ }
17317
+ _sharedPlayerContext = null;
17318
+ _sharedPlayerSampleRate = null;
17319
+ }
16412
17320
  }]);
16413
17321
  }(_shared_EventEmitter_js__WEBPACK_IMPORTED_MODULE_0__["default"]);
16414
17322
  /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (AudioPlayer);
@@ -19112,6 +20020,10 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
19112
20020
  this.audioRecorder.destroy();
19113
20021
  }
19114
20022
 
20023
+ // Close shared AudioContexts so they don't leak on SPA route changes
20024
+ _AudioPlayer_js__WEBPACK_IMPORTED_MODULE_1__["default"].closeSharedContext();
20025
+ _core_AudioRecorder_js__WEBPACK_IMPORTED_MODULE_2__["default"].closeSharedContext();
20026
+
19115
20027
  // Cleanup client tools registry
19116
20028
  if (this.clientToolsRegistry) {
19117
20029
  var tools = this.clientToolsRegistry.getRegisteredTools();
@@ -20425,6 +21337,7 @@ var AgentSDK = /*#__PURE__*/function () {
20425
21337
  this.onAudioStartPlaying = config.onAudioStartPlaying || function () {};
20426
21338
  this.onAudioStoppedPlaying = config.onAudioStoppedPlaying || function () {};
20427
21339
  this.onSubtitleDisplay = config.onSubtitleDisplay || function () {};
21340
+ this.onUnsupportedAction = null;
20428
21341
  }
20429
21342
 
20430
21343
  /**
@@ -20696,6 +21609,13 @@ var AgentSDK = /*#__PURE__*/function () {
20696
21609
  case 'error':
20697
21610
  this.onError(new Error(message.message || message.error || 'Unknown error'));
20698
21611
  break;
21612
+ case 'show_products':
21613
+ case 'cart_updated':
21614
+ case 'add_to_store_cart':
21615
+ if (typeof this.onUnsupportedAction === 'function') {
21616
+ this.onUnsupportedAction(messageType, message);
21617
+ }
21618
+ break;
20699
21619
  default:
20700
21620
  // Handle other message types
20701
21621
  break;
@@ -21136,6 +22056,17 @@ var TTPChatWidget = /*#__PURE__*/function () {
21136
22056
  }
21137
22057
  var widgetMode = ((_this$config$behavior = _this.config.behavior) === null || _this$config$behavior === void 0 ? void 0 : _this$config$behavior.mode) || 'unified';
21138
22058
  if (widgetMode === 'unified') {
22059
+ if (window.innerWidth <= 768) {
22060
+ var _this$shadowRoot, _this$shadowRoot2;
22061
+ // Mobile: close panel and show the FAB so user returns to normal state
22062
+ var panel = (_this$shadowRoot = _this.shadowRoot) === null || _this$shadowRoot === void 0 ? void 0 : _this$shadowRoot.getElementById('text-chat-panel');
22063
+ if (panel) {
22064
+ panel.classList.remove('open');
22065
+ _this.isOpen = false;
22066
+ }
22067
+ var fab = (_this$shadowRoot2 = _this.shadowRoot) === null || _this$shadowRoot2 === void 0 ? void 0 : _this$shadowRoot2.querySelector('.ttp-mobile-fab');
22068
+ if (fab) fab.style.display = '';
22069
+ }
21139
22070
  _this.showLanding();
21140
22071
  }
21141
22072
  },
@@ -21150,6 +22081,9 @@ var TTPChatWidget = /*#__PURE__*/function () {
21150
22081
  // Callback to show error toast (for domain errors, etc.)
21151
22082
  onErrorToast: function onErrorToast(message, type) {
21152
22083
  _this.showErrorToast(message, type);
22084
+ },
22085
+ onMobileError: function onMobileError(message) {
22086
+ _this.showMobileLandingError(message);
21153
22087
  }
21154
22088
  });
21155
22089
  this.voiceInterface = new _VoiceInterface_js__WEBPACK_IMPORTED_MODULE_1__.VoiceInterface(voiceConfig);
@@ -21176,13 +22110,25 @@ var TTPChatWidget = /*#__PURE__*/function () {
21176
22110
  this.createWidget();
21177
22111
  this.setupEventHandlers();
21178
22112
 
21179
- // Start open if configured (immediately)
22113
+ // Start open if configured
21180
22114
  if (this.config.behavior.startOpen || this.config.behavior.autoOpen) {
21181
- var _this$shadowRoot;
21182
- var panel = (_this$shadowRoot = this.shadowRoot) === null || _this$shadowRoot === void 0 ? void 0 : _this$shadowRoot.getElementById('text-chat-panel');
21183
- if (panel) {
21184
- this.isOpen = true;
21185
- panel.classList.add('open');
22115
+ if (window.innerWidth <= 768) {
22116
+ var _this$shadowRoot3, _this$shadowRoot4;
22117
+ // Mobile: show the mobile landing overlay instead of the desktop panel
22118
+ var mobileFab = (_this$shadowRoot3 = this.shadowRoot) === null || _this$shadowRoot3 === void 0 ? void 0 : _this$shadowRoot3.querySelector('.ttp-mobile-fab');
22119
+ var mobileLanding = (_this$shadowRoot4 = this.shadowRoot) === null || _this$shadowRoot4 === void 0 ? void 0 : _this$shadowRoot4.getElementById('ttpMobileLanding');
22120
+ if (mobileFab && mobileLanding) {
22121
+ mobileFab.style.display = 'none';
22122
+ mobileLanding.classList.add('active');
22123
+ this.isMobileLandingOpen = true;
22124
+ }
22125
+ } else {
22126
+ var _this$shadowRoot5;
22127
+ var panel = (_this$shadowRoot5 = this.shadowRoot) === null || _this$shadowRoot5 === void 0 ? void 0 : _this$shadowRoot5.getElementById('text-chat-panel');
22128
+ if (panel) {
22129
+ this.isOpen = true;
22130
+ panel.classList.add('open');
22131
+ }
21186
22132
  }
21187
22133
  }
21188
22134
 
@@ -21236,9 +22182,9 @@ var TTPChatWidget = /*#__PURE__*/function () {
21236
22182
  }, {
21237
22183
  key: "open",
21238
22184
  value: function open() {
21239
- var _this$shadowRoot2;
22185
+ var _this$shadowRoot6;
21240
22186
  if (this.isOpen) return;
21241
- var panel = (_this$shadowRoot2 = this.shadowRoot) === null || _this$shadowRoot2 === void 0 ? void 0 : _this$shadowRoot2.getElementById('text-chat-panel');
22187
+ var panel = (_this$shadowRoot6 = this.shadowRoot) === null || _this$shadowRoot6 === void 0 ? void 0 : _this$shadowRoot6.getElementById('text-chat-panel');
21242
22188
  if (panel) {
21243
22189
  this.isOpen = true;
21244
22190
  panel.classList.add('open');
@@ -21442,7 +22388,10 @@ var TTPChatWidget = /*#__PURE__*/function () {
21442
22388
  voiceCardIcon: cleanLandingConfig.voiceCardIcon || '🎤',
21443
22389
  textCardIcon: cleanLandingConfig.textCardIcon || '💬',
21444
22390
  voiceCardTitle: cleanLandingConfig.voiceCardTitle || null,
21445
- textCardTitle: cleanLandingConfig.textCardTitle || null
22391
+ textCardTitle: cleanLandingConfig.textCardTitle || null,
22392
+ callButtonText: cleanLandingConfig.callButtonText || null,
22393
+ chatButtonText: cleanLandingConfig.chatButtonText || null,
22394
+ statusText: cleanLandingConfig.statusText || null
21446
22395
  }, cleanLandingConfig);
21447
22396
  }(),
21448
22397
  // Header Configuration (top of panel)
@@ -21472,7 +22421,8 @@ var TTPChatWidget = /*#__PURE__*/function () {
21472
22421
  backgroundColor: cleanHeaderConfig.backgroundColor || '#7C3AED',
21473
22422
  // Default purple
21474
22423
  textColor: cleanHeaderConfig.textColor || '#FFFFFF',
21475
- showCloseButton: cleanHeaderConfig.showCloseButton !== false
22424
+ showCloseButton: cleanHeaderConfig.showCloseButton !== false,
22425
+ mobileLabel: cleanHeaderConfig.mobileLabel || 'Chat'
21476
22426
  }, cleanHeaderConfig);
21477
22427
  return finalHeader;
21478
22428
  }(),
@@ -21678,10 +22628,10 @@ var TTPChatWidget = /*#__PURE__*/function () {
21678
22628
  var resetCSS = '';
21679
22629
  if (this.config.useShadowDOM) {
21680
22630
  // Shadow DOM mode - use :host selector
21681
- resetCSS = "\n :host {\n all: initial;\n display: block;\n pointer-events: none;\n }\n img {\n max-width: none !important;\n }\n #text-chat-button img {\n width: 28px !important;\n height: 28px !important;\n }\n #text-chat-button {\n pointer-events: auto;\n }\n #text-chat-panel {\n pointer-events: auto;\n }\n ";
22631
+ resetCSS = "\n :host {\n all: initial;\n display: block;\n pointer-events: none;\n }\n img {\n max-width: none !important;\n }\n #text-chat-button img {\n width: 28px !important;\n height: 28px !important;\n }\n #text-chat-button {\n pointer-events: auto;\n }\n #text-chat-panel {\n pointer-events: auto;\n }\n .ttp-mobile-fab {\n pointer-events: auto;\n }\n #ttpMobileLanding {\n pointer-events: auto;\n }\n ";
21682
22632
  } else {
21683
22633
  // Regular DOM mode - targeted reset without breaking widget styles
21684
- resetCSS = "\n /* Reset only problematic inherited styles, not all styles */\n #text-chat-button,\n #text-chat-panel,\n #text-chat-panel * {\n box-sizing: border-box;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n }\n \n /* Prevent theme CSS from affecting images */\n #text-chat-button img,\n #text-chat-panel img {\n max-width: none !important;\n width: auto;\n height: auto;\n }\n \n #text-chat-button img {\n width: 28px !important;\n height: 28px !important;\n }\n \n /* Ensure button and panel can receive clicks */\n #text-chat-button,\n #text-chat-panel {\n pointer-events: auto;\n }\n \n /* Reset any inherited text styles that might break layout */\n #text-chat-panel {\n text-align: left;\n line-height: normal;\n letter-spacing: normal;\n word-spacing: normal;\n text-transform: none;\n text-indent: 0;\n text-shadow: none;\n white-space: normal;\n }\n \n /* Ensure flexbox works properly */\n #text-chat-panel .widget-shell,\n #text-chat-panel .panel-inner,\n #text-chat-panel .widget-container,\n #text-chat-panel .landing-screen,\n #text-chat-panel .voice-interface,\n #text-chat-panel .text-interface,\n #text-chat-panel .widget-header,\n #text-chat-panel .mode-selection {\n display: flex;\n }\n \n #text-chat-panel .widget-shell,\n #text-chat-panel .panel-inner,\n #text-chat-panel .widget-container,\n #text-chat-panel .landing-screen,\n #text-chat-panel .voice-interface,\n #text-chat-panel .text-interface {\n flex-direction: column;\n }\n \n #text-chat-panel .mode-selection {\n flex-direction: row;\n }\n \n /* Ensure proper stacking */\n #text-chat-button {\n z-index: 10001 !important;\n }\n \n #text-chat-panel {\n z-index: 10000 !important;\n }\n ";
22634
+ resetCSS = "\n /* Reset only problematic inherited styles, not all styles */\n #text-chat-button,\n #text-chat-panel,\n #text-chat-panel * {\n box-sizing: border-box;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n }\n \n /* Prevent theme CSS from affecting images */\n #text-chat-button img,\n #text-chat-panel img {\n max-width: none !important;\n width: auto;\n height: auto;\n }\n \n #text-chat-button img {\n width: 28px !important;\n height: 28px !important;\n }\n \n /* Ensure button and panel can receive clicks */\n #text-chat-button,\n #text-chat-panel,\n .ttp-mobile-fab,\n #ttpMobileLanding {\n pointer-events: auto;\n }\n \n /* Reset any inherited text styles that might break layout */\n #text-chat-panel {\n text-align: left;\n line-height: normal;\n letter-spacing: normal;\n word-spacing: normal;\n text-transform: none;\n text-indent: 0;\n text-shadow: none;\n white-space: normal;\n }\n \n /* Ensure flexbox works properly */\n #text-chat-panel .widget-shell,\n #text-chat-panel .panel-inner,\n #text-chat-panel .widget-container,\n #text-chat-panel .landing-screen,\n #text-chat-panel .voice-interface,\n #text-chat-panel .text-interface,\n #text-chat-panel .widget-header,\n #text-chat-panel .mode-selection {\n display: flex;\n }\n \n #text-chat-panel .widget-shell,\n #text-chat-panel .panel-inner,\n #text-chat-panel .widget-container,\n #text-chat-panel .landing-screen,\n #text-chat-panel .voice-interface,\n #text-chat-panel .text-interface {\n flex-direction: column;\n }\n \n #text-chat-panel .mode-selection {\n flex-direction: row;\n }\n \n /* Ensure proper stacking */\n #text-chat-button {\n z-index: 10001 !important;\n }\n \n #text-chat-panel {\n z-index: 10000 !important;\n }\n ";
21685
22635
  }
21686
22636
  var widgetHTML = this.generateWidgetHTML();
21687
22637
  if (this.config.useShadowDOM) {
@@ -21699,10 +22649,22 @@ var TTPChatWidget = /*#__PURE__*/function () {
21699
22649
  var panel = this.shadowRoot.getElementById('text-chat-panel');
21700
22650
  if (panel) {
21701
22651
  if (this.config.behavior.startOpen || this.config.behavior.autoOpen) {
21702
- this.isOpen = true;
21703
- panel.classList.add('open');
21704
- // Hide prompt if widget starts open
21705
- this.hidePrompt();
22652
+ if (window.innerWidth <= 768) {
22653
+ var _this$shadowRoot7, _this$shadowRoot8;
22654
+ // Mobile: show mobile landing overlay
22655
+ var mobileFab = (_this$shadowRoot7 = this.shadowRoot) === null || _this$shadowRoot7 === void 0 ? void 0 : _this$shadowRoot7.querySelector('.ttp-mobile-fab');
22656
+ var mobileLanding = (_this$shadowRoot8 = this.shadowRoot) === null || _this$shadowRoot8 === void 0 ? void 0 : _this$shadowRoot8.getElementById('ttpMobileLanding');
22657
+ if (mobileFab && mobileLanding) {
22658
+ mobileFab.style.display = 'none';
22659
+ mobileLanding.classList.add('active');
22660
+ this.isMobileLandingOpen = true;
22661
+ }
22662
+ this.hidePrompt();
22663
+ } else {
22664
+ this.isOpen = true;
22665
+ panel.classList.add('open');
22666
+ this.hidePrompt();
22667
+ }
21706
22668
  } else {
21707
22669
  // Widget starts closed - show prompt
21708
22670
  this.showPrompt();
@@ -21850,7 +22812,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
21850
22812
  isPromptEnabled: isPromptEnabled,
21851
22813
  willGenerateHTML: !!promptBubbleHTML
21852
22814
  });
21853
- return "\n ".concat(styleTag, "\n \n ").concat(this.config.behavior.hidden ? '' : "\n <div id=\"text-chat-button-container\">\n ".concat(promptBubbleHTML, "\n ").concat(isPromptEnabled && promptConfig.showPulseRings !== false ? this.generatePulseRingsHTML(promptConfig) : '', "\n <button id=\"text-chat-button\" \n aria-label=\"").concat(this.config.accessibility.ariaLabel, "\"\n aria-description=\"").concat(this.config.accessibility.ariaDescription, "\">\n ").concat(iconHTML, "\n </button>\n </div>\n "), "\n \n <div id=\"text-chat-panel\">\n <div class=\"widget-shell\">\n <div class=\"panel-inner widget-container\" style=\"direction: ").concat(this.config.direction, ";\">\n <div class=\"widget-header\" style=\"background: ").concat(header.backgroundColor || '#7C3AED', " !important; color: ").concat(header.textColor || '#FFFFFF', " !important;\">\n <div>\n ").concat(header.showTitle ? "<div class=\"header-title\">".concat(header.title, "</div>") : '', "\n <div class=\"header-status\" style=\"color: ").concat(header.onlineIndicatorColor || 'rgba(255,255,255,0.7)', " !important;\">\n <span class=\"status-dot\" style=\"background: ").concat(header.onlineIndicatorDotColor || '#10b981', " !important;\"></span>\n <span>").concat(header.onlineIndicatorText !== undefined && header.onlineIndicatorText !== null ? header.onlineIndicatorText : t('online'), "</span>\n </div>\n </div>\n \n <div style=\"display: flex; gap: 6px; align-items: center; position: relative; z-index: 1;\">\n <!-- New Chat Button (hide on landing screen, show otherwise) -->\n <button class=\"header-icon new-chat-btn\" id=\"newChatBtn\" title=\"").concat(getTooltip('newChat'), "\" style=\"").concat(showLanding ? 'display: none;' : '', "\">\n <span style=\"font-size: 18px; font-weight: bold;\">+</span>\n </button>\n \n <!-- Back Button (only show in unified mode) -->\n ").concat(widgetMode === 'unified' ? "<button class=\"header-icon back-btn\" id=\"backBtn\" title=\"".concat(getTooltip('back'), "\" style=\"display: none;\">\n <span style=\"font-size: 16px;\">\u2039</span>\n </button>") : '', "\n \n <!-- Close Button -->\n ").concat(header.showCloseButton ? '<button class="header-icon close-btn" id="closeBtn" title="' + getTooltip('close') + '">' + '<span style="font-size: 18px; font-weight: bold;">×</span>' + '</button>' : '', "\n </div>\n </div>\n\n ").concat(showLanding && this.landingScreen ? this.landingScreen.generateHTML() : '', "\n\n ").concat(showVoice ? this.voiceInterface.generateHTML() : '', "\n ").concat(showText ? this.textInterface.generateHTML() : '', "\n ").concat(this.generateFooterHTML(), "\n </div>\n </div>\n </div>\n ");
22815
+ return "\n ".concat(styleTag, "\n \n ").concat(this.config.behavior.hidden ? '' : "\n <div id=\"text-chat-button-container\">\n ".concat(promptBubbleHTML, "\n ").concat(isPromptEnabled && promptConfig.showPulseRings !== false ? this.generatePulseRingsHTML(promptConfig) : '', "\n <button id=\"text-chat-button\" \n aria-label=\"").concat(this.config.accessibility.ariaLabel, "\"\n aria-description=\"").concat(this.config.accessibility.ariaDescription, "\">\n ").concat(iconHTML, "\n </button>\n <button class=\"ttp-mobile-fab\" aria-label=\"").concat(this.config.accessibility.ariaLabel, "\">\n <div class=\"ttp-mobile-fab__icon-wrap\">\n <div class=\"ttp-mobile-fab__waveform\">\n <span class=\"bar\"></span><span class=\"bar\"></span><span class=\"bar\"></span><span class=\"bar\"></span><span class=\"bar\"></span>\n </div>\n </div>\n <div class=\"ttp-mobile-fab__label-wrap\">\n <span class=\"ttp-mobile-fab__label\">").concat(header.mobileLabel || 'Chat', "</span>\n <span class=\"ttp-mobile-fab__status\">\n <span class=\"ttp-mobile-fab__dot\"></span>\n ").concat(header.onlineIndicatorText !== undefined && header.onlineIndicatorText !== null ? header.onlineIndicatorText : t('online'), "\n </span>\n </div>\n </button>\n </div>\n "), "\n\n ").concat(this.config.behavior.hidden ? '' : "\n <div id=\"ttpMobileLanding\" class=\"ttp-mobile-landing\">\n <div class=\"ttp-mobile-landing__header\">\n <div class=\"ttp-mobile-landing__avatar\">\n <div class=\"ttp-mobile-landing__waveform\">\n <span class=\"bar\"></span><span class=\"bar\"></span><span class=\"bar\"></span><span class=\"bar\"></span><span class=\"bar\"></span>\n </div>\n </div>\n <div class=\"ttp-mobile-landing__info\">\n <div class=\"ttp-mobile-landing__name\">".concat(header.title || 'Chat Assistant', "</div>\n <div class=\"ttp-mobile-landing__status\">\n <span class=\"ttp-mobile-landing__dot\"></span>\n ").concat(this.config.landing.statusText || t('online') + ' • ' + (this.config.landing.subtitle || 'Ready to help'), "\n </div>\n </div>\n <button class=\"ttp-mobile-landing__close\" aria-label=\"Close\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/>\n </svg>\n </button>\n </div>\n <div class=\"ttp-mobile-landing__error\" id=\"ttpMobileLandingError\" style=\"display:none;\">\n <svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"12\"/><line x1=\"12\" y1=\"16\" x2=\"12.01\" y2=\"16\"/>\n </svg>\n <span class=\"ttp-mobile-landing__error-text\"></span>\n </div>\n <div class=\"ttp-mobile-landing__actions\">\n ").concat(showVoice ? "\n <button class=\"ttp-mobile-landing__btn ttp-mobile-landing__btn--call\" id=\"ttpMobileCallBtn\">\n <svg 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.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72c.127.96.361 1.903.7 2.81a2 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-.45c.907.339 1.85.573 2.81.7A2 2 0 0 1 22 16.92z\"/>\n </svg>\n ".concat(this.config.landing.callButtonText || this.config.landing.voiceCardTitle || t('voiceCall'), "\n </button>\n ") : '', "\n ").concat(showText ? "\n <button class=\"ttp-mobile-landing__btn ttp-mobile-landing__btn--text\" id=\"ttpMobileChatBtn\">\n <svg 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.landing.chatButtonText || this.config.landing.textCardTitle || t('textChat'), "\n </button>\n ") : '', "\n </div>\n ").concat(this.config.footer.show !== false ? "\n <div class=\"ttp-mobile-landing__footer\">\n <div class=\"ttp-mobile-landing__powered\">\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"10\" height=\"10\">\n <polygon points=\"13 2 3 14 12 14 11 22 21 10 12 10 13 2\"/>\n </svg>\n Powered by <a href=\"https://talktopc.com\" target=\"_blank\" rel=\"noopener\">TalkToPC</a>\n </div>\n </div>\n " : '', "\n </div>\n "), "\n \n <div id=\"text-chat-panel\">\n <div class=\"widget-shell\">\n <div class=\"panel-inner widget-container\" style=\"direction: ").concat(this.config.direction, ";\">\n <div class=\"widget-header\" style=\"background: ").concat(header.backgroundColor || '#7C3AED', " !important; color: ").concat(header.textColor || '#FFFFFF', " !important;\">\n <div>\n ").concat(header.showTitle ? "<div class=\"header-title\">".concat(header.title, "</div>") : '', "\n <div class=\"header-status\" style=\"color: ").concat(header.onlineIndicatorColor || 'rgba(255,255,255,0.7)', " !important;\">\n <span class=\"status-dot\" style=\"background: ").concat(header.onlineIndicatorDotColor || '#10b981', " !important;\"></span>\n <span>").concat(header.onlineIndicatorText !== undefined && header.onlineIndicatorText !== null ? header.onlineIndicatorText : t('online'), "</span>\n </div>\n </div>\n \n <div style=\"display: flex; gap: 6px; align-items: center; position: relative; z-index: 1;\">\n <!-- New Chat Button (hide on landing screen, show otherwise) -->\n <button class=\"header-icon new-chat-btn\" id=\"newChatBtn\" title=\"").concat(getTooltip('newChat'), "\" style=\"").concat(showLanding ? 'display: none;' : '', "\">\n <span style=\"font-size: 18px; font-weight: bold;\">+</span>\n </button>\n \n <!-- Back Button (only show in unified mode) -->\n ").concat(widgetMode === 'unified' ? "<button class=\"header-icon back-btn\" id=\"backBtn\" title=\"".concat(getTooltip('back'), "\" style=\"display: none;\">\n <span style=\"font-size: 16px;\">\u2039</span>\n </button>") : '', "\n \n <!-- Close Button -->\n ").concat(header.showCloseButton ? '<button class="header-icon close-btn" id="closeBtn" title="' + getTooltip('close') + '">' + '<span style="font-size: 18px; font-weight: bold;">×</span>' + '</button>' : '', "\n </div>\n </div>\n\n ").concat(showLanding && this.landingScreen ? this.landingScreen.generateHTML() : '', "\n\n ").concat(showVoice ? this.voiceInterface.generateHTML() : '', "\n ").concat(showText ? this.textInterface.generateHTML() : '', "\n ").concat(this.generateFooterHTML(), "\n </div>\n </div>\n </div>\n ");
21854
22816
  }
21855
22817
 
21856
22818
  /**
@@ -21976,7 +22938,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
21976
22938
  // Note: Removed headerColor variable - landing screen colors now use explicit defaults
21977
22939
  // to prevent buttons from inheriting header backgroundColor
21978
22940
 
21979
- return "\n /* MOBILE FIRST - Default styles for all devices */\n #text-chat-widget {\n position: fixed !important;\n z-index: 10000;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n /* Mobile defaults */\n right: 10px;\n bottom: 10px;\n left: auto;\n top: auto;\n }\n \n /* Desktop positioning (only on larger screens) */\n @media (min-width: 769px) {\n #text-chat-widget {\n ".concat(positionStyles, "\n right: ").concat(this.config.position.horizontal === 'right' ? '20px' : 'auto', ";\n left: ").concat(this.config.position.horizontal === 'left' ? '20px' : 'auto', ";\n bottom: ").concat(this.config.position.vertical === 'bottom' ? '20px' : 'auto', ";\n top: ").concat(this.config.position.vertical === 'top' ? '20px' : 'auto', ";\n }\n }\n \n /* Mobile override (force mobile positioning) */\n @media (max-width: 768px) {\n #text-chat-widget {\n right: 10px !important;\n bottom: 10px !important;\n left: auto !important;\n top: auto !important;\n transform: none !important;\n }\n }\n \n @media (max-width: 480px) {\n #text-chat-widget {\n right: 8px !important;\n bottom: 8px !important;\n left: auto !important;\n top: auto !important;\n }\n }\n \n #text-chat-button {\n position: relative;\n width: ").concat(buttonSize, "px;\n height: ").concat(buttonSize, "px;\n margin: 0;\n flex-shrink: 0;\n border-radius: ").concat(btn.shape === 'circle' ? '50%' : btn.shape === 'square' ? '0' : '12px', ";\n background: ").concat(btn.backgroundColor || icon.backgroundColor || '#7C3AED', ";\n border: none;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all ").concat(anim.duration, "s ease;\n box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);\n touch-action: manipulation;\n -webkit-tap-highlight-color: transparent;\n -webkit-touch-callout: none;\n user-select: none;\n min-width: 44px;\n min-height: 44px;\n z-index: 1;\n }\n \n @media (max-width: 768px) {\n #text-chat-widget {\n right: 10px !important;\n bottom: 10px !important;\n left: auto !important;\n top: auto !important;\n transform: none !important;\n }\n \n #text-chat-button-container {\n width: 56px !important;\n height: 56px !important;\n }\n \n #text-chat-button {\n width: 56px !important;\n height: 56px !important;\n min-width: 56px !important;\n min-height: 56px !important;\n max-width: 56px !important;\n max-height: 56px !important;\n }\n \n #text-chat-panel {\n position: fixed !important;\n left: 10px !important;\n right: 10px !important;\n bottom: 92px !important; /* 56px button + 20px gap + 16px footer */\n top: 60px !important; /* Add top spacing */\n width: auto !important;\n max-width: none !important;\n height: auto !important; /* Change from max-height to auto */\n max-height: none !important; /* Remove max-height */\n transform: none !important;\n margin: 0 !important;\n }\n \n #text-chat-panel .widget-header {\n padding: 10px 14px;\n min-height: 56px;\n }\n \n #text-chat-panel .header-title {\n font-size: 15px;\n }\n \n #text-chat-panel .header-icon {\n width: 40px;\n height: 40px;\n min-width: 40px;\n min-height: 40px;\n }\n \n #text-chat-input {\n font-size: 16px !important; /* Prevents iOS zoom on focus */\n padding: 12px 16px !important;\n min-height: 48px !important;\n }\n \n #text-chat-send {\n min-width: 48px !important;\n min-height: 48px !important;\n width: 48px !important;\n height: 48px !important;\n }\n \n .landing-screen {\n padding: 16px;\n }\n \n .landing-logo {\n font-size: 40px;\n }\n \n .landing-title {\n font-size: 18px;\n margin-bottom: 16px;\n }\n \n .mode-selection {\n flex-direction: column;\n gap: 12px;\n align-items: center;\n }\n \n .mode-card {\n max-width: 100%;\n width: 100%;\n padding: 16px;\n }\n \n .mode-card-icon {\n width: 50px;\n height: 50px;\n font-size: 28px;\n }\n \n .mode-card-title {\n font-size: 14px;\n }\n }\n \n @media (max-width: 480px) {\n #text-chat-widget {\n right: 8px !important;\n bottom: 8px !important;\n left: auto !important;\n top: auto !important;\n }\n \n #text-chat-button-container {\n width: 54px !important;\n height: 54px !important;\n }\n \n #text-chat-button {\n width: 54px !important;\n height: 54px !important;\n min-width: 54px !important;\n min-height: 54px !important;\n }\n \n #text-chat-panel {\n left: 8px !important;\n right: 8px !important;\n bottom: 86px !important; /* 54px button + 20px gap + 12px footer */\n top: 50px !important; /* Add top spacing for very small screens */\n height: auto !important;\n max-height: none !important;\n }\n \n #text-chat-panel .widget-header {\n padding: 8px 12px;\n min-height: 52px;\n }\n \n #text-chat-panel .header-title {\n font-size: 14px;\n }\n \n .landing-logo {\n font-size: 36px;\n }\n \n .landing-title {\n font-size: 16px;\n }\n }\n \n ").concat(anim.enableHover ? "\n #text-chat-button:hover {\n ".concat(btn.hoverColor ? "background: ".concat(btn.hoverColor, ";") : '', "\n transform: scale(1.05);\n box-shadow: 0 8px 20px rgba(102, 126, 234, 0.5);\n }\n ") : '', "\n \n /* Prompt Animation Keyframes */\n @keyframes widget-shimmer {\n 0% { transform: translateX(-100%); }\n 100% { transform: translateX(200%); }\n }\n \n @keyframes widget-ripple {\n 0% { transform: scale(1); opacity: 0.6; }\n 100% { transform: scale(2.5); opacity: 0; }\n }\n \n @keyframes widget-float {\n 0%, 100% { transform: translateX(-50%) translateY(0); }\n 50% { transform: translateX(-50%) translateY(-8px); }\n }\n \n @keyframes widget-bounce {\n 0%, 100% { transform: translateX(-50%) translateY(0); }\n 50% { transform: translateX(-50%) translateY(-10px); }\n }\n \n @keyframes widget-pulse-ring {\n 0% { transform: scale(1); opacity: 0.4; }\n 100% { transform: scale(1.8); opacity: 0; }\n }\n \n /* Prompt Bubble Container */\n #text-chat-button-container {\n position: fixed;\n ").concat(this.config.position.vertical === 'bottom' ? "bottom: ".concat(((_this$config$position = this.config.position.offset) === null || _this$config$position === void 0 ? void 0 : _this$config$position.y) || 20, "px;") : "top: ".concat(((_this$config$position2 = this.config.position.offset) === null || _this$config$position2 === void 0 ? void 0 : _this$config$position2.y) || 20, "px;"), "\n ").concat(this.config.position.horizontal === 'right' ? "right: ".concat(((_this$config$position3 = this.config.position.offset) === null || _this$config$position3 === void 0 ? void 0 : _this$config$position3.x) || 20, "px;") : "left: ".concat(((_this$config$position4 = this.config.position.offset) === null || _this$config$position4 === void 0 ? void 0 : _this$config$position4.x) || 20, "px;"), "\n width: ").concat(buttonSize, "px;\n height: ").concat(buttonSize, "px;\n z-index: 10001;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n \n @media (max-width: 768px) {\n #text-chat-button-container {\n right: 10px !important;\n bottom: 10px !important;\n left: auto !important;\n top: auto !important;\n }\n }\n \n @media (max-width: 480px) {\n #text-chat-button-container {\n right: 8px !important;\n bottom: 8px !important;\n left: auto !important;\n top: auto !important;\n }\n }\n \n /* Prompt Bubble Styles */\n .prompt-bubble {\n position: absolute;\n z-index: 10002;\n pointer-events: none;\n white-space: nowrap;\n }\n \n .prompt-bubble.top {\n bottom: calc(100% + 18px);\n left: 50%;\n transform: translateX(-50%);\n }\n \n .prompt-bubble.left {\n right: calc(100% + 12px);\n top: 50%;\n transform: translateY(-50%);\n }\n \n .prompt-bubble.right {\n left: calc(100% + 12px);\n top: 50%;\n transform: translateY(-50%);\n }\n \n /* Ensure animations preserve horizontal centering for top position */\n .prompt-bubble.top.animation-bounce {\n animation: widget-bounce 1s ease-in-out infinite;\n transform: translateX(-50%); /* Keep centering */\n }\n \n .prompt-bubble.top.animation-float {\n animation: widget-float 2s ease-in-out infinite;\n transform: translateX(-50%); /* Keep centering */\n }\n \n .prompt-bubble.top.animation-pulse {\n animation: pulse 2s ease-in-out infinite;\n transform: translateX(-50%); /* Keep centering */\n }\n \n .prompt-bubble-content {\n position: relative;\n padding: 8px 16px;\n border-radius: 20px;\n font-weight: 500;\n font-size: 14px;\n box-shadow: 0 4px 15px rgba(124, 58, 237, 0.3);\n overflow: hidden;\n }\n \n .prompt-bubble-shimmer {\n position: absolute;\n inset: 0;\n background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.25), transparent);\n animation: widget-shimmer 2s infinite;\n }\n \n .prompt-bubble.animation-bounce {\n animation: widget-bounce 1s ease-in-out infinite;\n }\n \n .prompt-bubble.animation-pulse {\n animation: pulse 2s ease-in-out infinite;\n }\n \n .prompt-bubble.animation-float {\n animation: widget-float 2s ease-in-out infinite;\n }\n \n /* Ensure top-positioned bubbles maintain horizontal centering during animations */\n .prompt-bubble.top.animation-bounce,\n .prompt-bubble.top.animation-float,\n .prompt-bubble.top.animation-pulse {\n /* Animation keyframes already include translateX(-50%) for centering */\n }\n \n /* Prompt Bubble Arrow */\n .prompt-bubble-arrow {\n position: absolute;\n width: 0;\n height: 0;\n border: 8px solid transparent;\n }\n \n .prompt-bubble.top .prompt-bubble-arrow {\n top: 100%;\n left: 50%;\n transform: translateX(-50%);\n border-top-color: var(--prompt-bubble-bg-color, #7c3aed);\n border-bottom: none;\n margin-left: 0;\n }\n \n .prompt-bubble.left .prompt-bubble-arrow {\n left: 100%;\n top: 50%;\n transform: translateY(-50%);\n border-left-color: var(--prompt-bubble-bg-color, #7c3aed);\n border-right: none;\n }\n \n .prompt-bubble.right .prompt-bubble-arrow {\n right: 100%;\n top: 50%;\n transform: translateY(-50%);\n border-right-color: var(--prompt-bubble-bg-color, #7c3aed);\n border-left: none;\n }\n \n /* Pulse Rings */\n .prompt-pulse-rings {\n position: absolute;\n inset: 0;\n border-radius: 50%;\n pointer-events: none;\n }\n \n .prompt-pulse-ring {\n position: absolute;\n inset: 0;\n border-radius: 50%;\n animation: widget-pulse-ring 2s ease-out infinite;\n }\n \n .prompt-pulse-ring:nth-child(2) {\n animation-delay: 0.5s;\n }\n \n /* Mobile adjustments for prompt bubble */\n @media (max-width: 768px) {\n .prompt-bubble-content {\n font-size: 12px;\n padding: 6px 12px;\n }\n \n .prompt-bubble.top {\n bottom: calc(100% + 14px);\n }\n \n .prompt-bubble.left,\n .prompt-bubble.right {\n display: none; /* Hide side prompts on mobile */\n }\n }\n \n #text-chat-panel {\n display: none").concat(important, ";\n position: fixed;\n bottom: calc(").concat(buttonSize, "px + 20px + 20px); /* Button + gap + reduced footer offset */\n ").concat(this.config.position.horizontal === 'right' ? 'right: 20px;' : 'left: 20px;', "\n width: ").concat(panel.width, "px;\n max-width: calc(100vw - 40px);\n height: ").concat(panel.height, "px;\n max-height: calc(100vh - ").concat(buttonSize, "px - 40px - 20px); /* Account for footer height */\n background: transparent;\n border-radius: ").concat(panel.borderRadius, "px;\n border: none;\n flex-direction: column;\n overflow: hidden;\n ").concat(panel.backdropFilter ? "backdrop-filter: ".concat(panel.backdropFilter, ";") : '', "\n ").concat(anim.enableSlide ? "transition: all ".concat(anim.duration, "s ease;") : '', "\n box-sizing: border-box;\n }\n \n #text-chat-panel.open {\n display: flex").concat(important, ";\n ").concat(anim.enableSlide ? 'transform: translateY(0); opacity: 1;' : '', "\n }\n\n /* Shell for gradient border/background */\n .widget-shell { width: 100%; height: 100%; padding: 0; border-radius: ").concat(panel.borderRadius, "px; background: transparent; box-shadow: 0 20px 60px rgba(0,0,0,0.15); overflow: hidden; display: flex; flex-direction: column; box-sizing: border-box; }\n .panel-inner { width: 100%; height: 100%; background: #ffffff; border-radius: ").concat(panel.borderRadius, "px; border: ").concat(panel.border, "; overflow: hidden; display:flex; flex-direction: column; padding: 0; box-sizing: border-box; max-width: 100%; }\n\n /* New structure styles matching provided design */\n #text-chat-panel .widget-container {\n width: 100%; height: 100%; background: #FFFFFF; overflow: visible; display: flex; flex-direction: column; border-radius: ").concat(panel.borderRadius, "px;\n container-type: size;\n }\n \n /* Ensure content areas can scroll when height is constrained */\n #text-chat-panel .widget-container > .landing-screen,\n #text-chat-panel .widget-container > .voice-interface,\n #text-chat-panel .widget-container > .text-interface {\n flex: 1;\n overflow-y: auto;\n overflow-x: visible; /* Change from hidden to visible */\n }\n \n /* Header should not scroll */\n #text-chat-panel .widget-header {\n padding: 14px 16px;\n display: flex;\n justify-content: space-between;\n align-items: center;\n border-top-left-radius: ").concat(panel.borderRadius, "px;\n border-top-right-radius: ").concat(panel.borderRadius, "px;\n flex-shrink: 0;\n box-sizing: border-box;\n position: relative;\n overflow: hidden;\n background: ").concat(header.backgroundColor || '#7C3AED').concat(important, ";\n color: ").concat(header.textColor || '#FFFFFF').concat(important, ";\n }\n \n #text-chat-panel .widget-header::before {\n content: '';\n position: absolute;\n top: -50%;\n right: -20%;\n width: 180px;\n height: 180px;\n background: radial-gradient(circle, rgba(255,255,255,0.08) 0%, transparent 70%);\n pointer-events: none;\n }\n \n #text-chat-panel .widget-header > div:first-child {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 4px;\n position: relative;\n z-index: 1;\n }\n \n #text-chat-panel .header-title { font-size: 15px; font-weight: 600; margin: 0; }\n #text-chat-panel .header-status { display: flex; align-items: center; gap: 5px; font-size: 12px; margin: 0; }\n #text-chat-panel .status-dot { width: 6px; height: 6px; border-radius: 50%; animation: pulse 2s ease-in-out infinite; }\n /* Online indicator customization - inline styles take precedence */\n @keyframes pulse { 0%, 100% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.15); opacity: 0.8; } }\n /* Header icon buttons */\n .header-icon {\n background: rgba(255, 255, 255, 0.1);\n border: none;\n color: white;\n width: 34px;\n height: 34px;\n min-width: 34px;\n min-height: 34px;\n border-radius: 10px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: background 0.2s;\n flex-shrink: 0;\n font-size: 16px;\n padding: 0;\n box-sizing: border-box;\n position: relative;\n z-index: 1;\n }\n \n .header-icon:hover {\n background: rgba(255, 255, 255, 0.2);\n }\n \n .header-icon svg {\n pointer-events: none;\n stroke: white;\n fill: none;\n }\n \n .back-btn.visible {\n display: flex !important;\n }\n\n ").concat(showLanding && this.landingScreen ? this.landingScreen.generateCSS() : '', "\n\n ").concat(showVoice ? this.voiceInterface.generateCSS() : '', "\n ").concat(showText ? this.textInterface.generateCSS() : '', "\n \n /* Footer Branding */\n .widget-footer {\n box-sizing: border-box;\n }\n \n .footer-brand-link {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n }\n \n .footer-brand-link:hover {\n opacity: 0.9;\n }\n \n @media (max-width: 768px) {\n .widget-footer {\n height: 32px;\n }\n .widget-footer span {\n font-size: 10px;\n }\n .widget-footer span:last-child {\n font-size: 8px;\n }\n }\n \n #text-chat-send-hint {\n text-align: center;\n line-height: 1.4;\n }\n \n .agent-thinking {\n font-style: italic;\n color: #6B7280;\n }\n ");
22941
+ return "\n /* MOBILE FIRST - Default styles for all devices */\n #text-chat-widget {\n position: fixed !important;\n z-index: 10000;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n /* Mobile defaults */\n right: 10px;\n bottom: 10px;\n left: auto;\n top: auto;\n }\n \n /* Desktop positioning (only on larger screens) */\n @media (min-width: 769px) {\n #text-chat-widget {\n ".concat(positionStyles, "\n right: ").concat(this.config.position.horizontal === 'right' ? '20px' : 'auto', ";\n left: ").concat(this.config.position.horizontal === 'left' ? '20px' : 'auto', ";\n bottom: ").concat(this.config.position.vertical === 'bottom' ? '20px' : 'auto', ";\n top: ").concat(this.config.position.vertical === 'top' ? '20px' : 'auto', ";\n }\n }\n \n /* Mobile override (force mobile positioning) */\n @media (max-width: 768px) {\n #text-chat-widget {\n right: 10px !important;\n bottom: 10px !important;\n left: auto !important;\n top: auto !important;\n transform: none !important;\n }\n }\n \n @media (max-width: 480px) {\n #text-chat-widget {\n right: 8px !important;\n bottom: 8px !important;\n left: auto !important;\n top: auto !important;\n }\n }\n \n #text-chat-button {\n position: relative;\n width: ").concat(buttonSize, "px;\n height: ").concat(buttonSize, "px;\n margin: 0;\n flex-shrink: 0;\n border-radius: ").concat(btn.shape === 'circle' ? '50%' : btn.shape === 'square' ? '0' : '12px', ";\n background: ").concat(btn.backgroundColor || icon.backgroundColor || '#7C3AED', ";\n border: none;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all ").concat(anim.duration, "s ease;\n box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);\n touch-action: manipulation;\n -webkit-tap-highlight-color: transparent;\n -webkit-touch-callout: none;\n user-select: none;\n min-width: 44px;\n min-height: 44px;\n z-index: 1;\n }\n \n @media (max-width: 768px) {\n #text-chat-widget {\n right: 10px !important;\n bottom: 10px !important;\n left: auto !important;\n top: auto !important;\n transform: none !important;\n }\n \n #text-chat-button-container {\n width: 56px !important;\n height: 56px !important;\n }\n \n #text-chat-button {\n width: 56px !important;\n height: 56px !important;\n min-width: 56px !important;\n min-height: 56px !important;\n max-width: 56px !important;\n max-height: 56px !important;\n }\n \n #text-chat-panel {\n position: fixed !important;\n left: 10px !important;\n right: 10px !important;\n bottom: 92px !important; /* 56px button + 20px gap + 16px footer */\n top: 60px !important; /* Add top spacing */\n width: auto !important;\n max-width: none !important;\n height: auto !important; /* Change from max-height to auto */\n max-height: none !important; /* Remove max-height */\n transform: none !important;\n margin: 0 !important;\n }\n \n #text-chat-panel .widget-header {\n padding: 10px 14px;\n min-height: 56px;\n }\n \n #text-chat-panel .header-title {\n font-size: 15px;\n }\n \n #text-chat-panel .header-icon {\n width: 40px;\n height: 40px;\n min-width: 40px;\n min-height: 40px;\n }\n \n #text-chat-input {\n font-size: 16px !important; /* Prevents iOS zoom on focus */\n padding: 12px 16px !important;\n min-height: 48px !important;\n }\n \n #text-chat-send {\n min-width: 48px !important;\n min-height: 48px !important;\n width: 48px !important;\n height: 48px !important;\n }\n \n .landing-screen {\n padding: 16px;\n }\n \n .landing-logo {\n font-size: 40px;\n }\n \n .landing-title {\n font-size: 18px;\n margin-bottom: 16px;\n }\n \n .mode-selection {\n flex-direction: column;\n gap: 12px;\n align-items: center;\n }\n \n .mode-card {\n max-width: 100%;\n width: 100%;\n padding: 16px;\n }\n \n .mode-card-icon {\n width: 50px;\n height: 50px;\n font-size: 28px;\n }\n \n .mode-card-title {\n font-size: 14px;\n }\n }\n \n @media (max-width: 480px) {\n #text-chat-widget {\n right: 8px !important;\n bottom: 8px !important;\n left: auto !important;\n top: auto !important;\n }\n \n #text-chat-button-container {\n width: 54px !important;\n height: 54px !important;\n }\n \n #text-chat-button {\n width: 54px !important;\n height: 54px !important;\n min-width: 54px !important;\n min-height: 54px !important;\n }\n \n #text-chat-panel {\n left: 8px !important;\n right: 8px !important;\n bottom: 86px !important; /* 54px button + 20px gap + 12px footer */\n top: 50px !important; /* Add top spacing for very small screens */\n height: auto !important;\n max-height: none !important;\n }\n \n #text-chat-panel .widget-header {\n padding: 8px 12px;\n min-height: 52px;\n }\n \n #text-chat-panel .header-title {\n font-size: 14px;\n }\n \n .landing-logo {\n font-size: 36px;\n }\n \n .landing-title {\n font-size: 16px;\n }\n }\n \n ").concat(anim.enableHover ? "\n #text-chat-button:hover {\n ".concat(btn.hoverColor ? "background: ".concat(btn.hoverColor, ";") : '', "\n transform: scale(1.05);\n box-shadow: 0 8px 20px rgba(102, 126, 234, 0.5);\n }\n ") : '', "\n \n /* Prompt Animation Keyframes */\n @keyframes widget-shimmer {\n 0% { transform: translateX(-100%); }\n 100% { transform: translateX(200%); }\n }\n \n @keyframes widget-ripple {\n 0% { transform: scale(1); opacity: 0.6; }\n 100% { transform: scale(2.5); opacity: 0; }\n }\n \n @keyframes widget-float {\n 0%, 100% { transform: translateX(-50%) translateY(0); }\n 50% { transform: translateX(-50%) translateY(-8px); }\n }\n \n @keyframes widget-bounce {\n 0%, 100% { transform: translateX(-50%) translateY(0); }\n 50% { transform: translateX(-50%) translateY(-10px); }\n }\n \n @keyframes widget-pulse-ring {\n 0% { transform: scale(1); opacity: 0.4; }\n 100% { transform: scale(1.8); opacity: 0; }\n }\n \n /* Prompt Bubble Container */\n #text-chat-button-container {\n position: fixed;\n ").concat(this.config.position.vertical === 'bottom' ? "bottom: ".concat(((_this$config$position = this.config.position.offset) === null || _this$config$position === void 0 ? void 0 : _this$config$position.y) || 20, "px;") : "top: ".concat(((_this$config$position2 = this.config.position.offset) === null || _this$config$position2 === void 0 ? void 0 : _this$config$position2.y) || 20, "px;"), "\n ").concat(this.config.position.horizontal === 'right' ? "right: ".concat(((_this$config$position3 = this.config.position.offset) === null || _this$config$position3 === void 0 ? void 0 : _this$config$position3.x) || 20, "px;") : "left: ".concat(((_this$config$position4 = this.config.position.offset) === null || _this$config$position4 === void 0 ? void 0 : _this$config$position4.x) || 20, "px;"), "\n width: ").concat(buttonSize, "px;\n height: ").concat(buttonSize, "px;\n z-index: 10001;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n \n @media (max-width: 768px) {\n #text-chat-button-container {\n right: 10px !important;\n bottom: 10px !important;\n left: auto !important;\n top: auto !important;\n }\n }\n \n @media (max-width: 480px) {\n #text-chat-button-container {\n right: 8px !important;\n bottom: 8px !important;\n left: auto !important;\n top: auto !important;\n }\n }\n \n /* Prompt Bubble Styles */\n .prompt-bubble {\n position: absolute;\n z-index: 10002;\n pointer-events: none;\n white-space: nowrap;\n }\n \n .prompt-bubble.top {\n bottom: calc(100% + 18px);\n left: 50%;\n transform: translateX(-50%);\n }\n \n .prompt-bubble.left {\n right: calc(100% + 12px);\n top: 50%;\n transform: translateY(-50%);\n }\n \n .prompt-bubble.right {\n left: calc(100% + 12px);\n top: 50%;\n transform: translateY(-50%);\n }\n \n /* Ensure animations preserve horizontal centering for top position */\n .prompt-bubble.top.animation-bounce {\n animation: widget-bounce 1s ease-in-out infinite;\n transform: translateX(-50%); /* Keep centering */\n }\n \n .prompt-bubble.top.animation-float {\n animation: widget-float 2s ease-in-out infinite;\n transform: translateX(-50%); /* Keep centering */\n }\n \n .prompt-bubble.top.animation-pulse {\n animation: pulse 2s ease-in-out infinite;\n transform: translateX(-50%); /* Keep centering */\n }\n \n .prompt-bubble-content {\n position: relative;\n padding: 8px 16px;\n border-radius: 20px;\n font-weight: 500;\n font-size: 14px;\n box-shadow: 0 4px 15px rgba(124, 58, 237, 0.3);\n overflow: hidden;\n }\n \n .prompt-bubble-shimmer {\n position: absolute;\n inset: 0;\n background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.25), transparent);\n animation: widget-shimmer 2s infinite;\n }\n \n .prompt-bubble.animation-bounce {\n animation: widget-bounce 1s ease-in-out infinite;\n }\n \n .prompt-bubble.animation-pulse {\n animation: pulse 2s ease-in-out infinite;\n }\n \n .prompt-bubble.animation-float {\n animation: widget-float 2s ease-in-out infinite;\n }\n \n /* Ensure top-positioned bubbles maintain horizontal centering during animations */\n .prompt-bubble.top.animation-bounce,\n .prompt-bubble.top.animation-float,\n .prompt-bubble.top.animation-pulse {\n /* Animation keyframes already include translateX(-50%) for centering */\n }\n \n /* Prompt Bubble Arrow */\n .prompt-bubble-arrow {\n position: absolute;\n width: 0;\n height: 0;\n border: 8px solid transparent;\n }\n \n .prompt-bubble.top .prompt-bubble-arrow {\n top: 100%;\n left: 50%;\n transform: translateX(-50%);\n border-top-color: var(--prompt-bubble-bg-color, #7c3aed);\n border-bottom: none;\n margin-left: 0;\n }\n \n .prompt-bubble.left .prompt-bubble-arrow {\n left: 100%;\n top: 50%;\n transform: translateY(-50%);\n border-left-color: var(--prompt-bubble-bg-color, #7c3aed);\n border-right: none;\n }\n \n .prompt-bubble.right .prompt-bubble-arrow {\n right: 100%;\n top: 50%;\n transform: translateY(-50%);\n border-right-color: var(--prompt-bubble-bg-color, #7c3aed);\n border-left: none;\n }\n \n /* Pulse Rings */\n .prompt-pulse-rings {\n position: absolute;\n inset: 0;\n border-radius: 50%;\n pointer-events: none;\n }\n \n .prompt-pulse-ring {\n position: absolute;\n inset: 0;\n border-radius: 50%;\n animation: widget-pulse-ring 2s ease-out infinite;\n }\n \n .prompt-pulse-ring:nth-child(2) {\n animation-delay: 0.5s;\n }\n \n /* Mobile adjustments for prompt bubble */\n @media (max-width: 768px) {\n .prompt-bubble-content {\n font-size: 12px;\n padding: 6px 12px;\n }\n \n .prompt-bubble.top {\n bottom: calc(100% + 14px);\n }\n \n .prompt-bubble.left,\n .prompt-bubble.right {\n display: none; /* Hide side prompts on mobile */\n }\n }\n \n #text-chat-panel {\n display: none").concat(important, ";\n position: fixed;\n bottom: calc(").concat(buttonSize, "px + 20px + 20px); /* Button + gap + reduced footer offset */\n ").concat(this.config.position.horizontal === 'right' ? 'right: 20px;' : 'left: 20px;', "\n width: ").concat(panel.width, "px;\n max-width: calc(100vw - 40px);\n height: ").concat(panel.height, "px;\n max-height: calc(100vh - ").concat(buttonSize, "px - 40px - 20px); /* Account for footer height */\n background: transparent;\n border-radius: ").concat(panel.borderRadius, "px;\n border: none;\n flex-direction: column;\n overflow: hidden;\n ").concat(panel.backdropFilter ? "backdrop-filter: ".concat(panel.backdropFilter, ";") : '', "\n ").concat(anim.enableSlide ? "transition: all ".concat(anim.duration, "s ease;") : '', "\n box-sizing: border-box;\n }\n \n #text-chat-panel.open {\n display: flex").concat(important, ";\n ").concat(anim.enableSlide ? 'transform: translateY(0); opacity: 1;' : '', "\n }\n\n /* Shell for gradient border/background */\n .widget-shell { width: 100%; height: 100%; padding: 0; border-radius: ").concat(panel.borderRadius, "px; background: transparent; box-shadow: 0 20px 60px rgba(0,0,0,0.15); overflow: hidden; display: flex; flex-direction: column; box-sizing: border-box; }\n .panel-inner { width: 100%; height: 100%; background: #ffffff; border-radius: ").concat(panel.borderRadius, "px; border: ").concat(panel.border, "; overflow: hidden; display:flex; flex-direction: column; padding: 0; box-sizing: border-box; max-width: 100%; }\n\n /* New structure styles matching provided design */\n #text-chat-panel .widget-container {\n width: 100%; height: 100%; background: #FFFFFF; overflow: visible; display: flex; flex-direction: column; border-radius: ").concat(panel.borderRadius, "px;\n container-type: size;\n }\n \n /* Ensure content areas can scroll when height is constrained */\n #text-chat-panel .widget-container > .landing-screen,\n #text-chat-panel .widget-container > .voice-interface,\n #text-chat-panel .widget-container > .text-interface {\n flex: 1;\n overflow-y: auto;\n overflow-x: visible; /* Change from hidden to visible */\n }\n \n /* Header should not scroll */\n #text-chat-panel .widget-header {\n padding: 14px 16px;\n display: flex;\n justify-content: space-between;\n align-items: center;\n border-top-left-radius: ").concat(panel.borderRadius, "px;\n border-top-right-radius: ").concat(panel.borderRadius, "px;\n flex-shrink: 0;\n box-sizing: border-box;\n position: relative;\n overflow: hidden;\n background: ").concat(header.backgroundColor || '#7C3AED').concat(important, ";\n color: ").concat(header.textColor || '#FFFFFF').concat(important, ";\n }\n \n #text-chat-panel .widget-header::before {\n content: '';\n position: absolute;\n top: -50%;\n right: -20%;\n width: 180px;\n height: 180px;\n background: radial-gradient(circle, rgba(255,255,255,0.08) 0%, transparent 70%);\n pointer-events: none;\n }\n \n #text-chat-panel .widget-header > div:first-child {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 4px;\n position: relative;\n z-index: 1;\n }\n \n #text-chat-panel .header-title { font-size: 15px; font-weight: 600; margin: 0; }\n #text-chat-panel .header-status { display: flex; align-items: center; gap: 5px; font-size: 12px; margin: 0; }\n #text-chat-panel .status-dot { width: 6px; height: 6px; border-radius: 50%; animation: pulse 2s ease-in-out infinite; }\n /* Online indicator customization - inline styles take precedence */\n @keyframes pulse { 0%, 100% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.15); opacity: 0.8; } }\n /* Header icon buttons */\n .header-icon {\n background: rgba(255, 255, 255, 0.1);\n border: none;\n color: white;\n width: 34px;\n height: 34px;\n min-width: 34px;\n min-height: 34px;\n border-radius: 10px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: background 0.2s;\n flex-shrink: 0;\n font-size: 16px;\n padding: 0;\n box-sizing: border-box;\n position: relative;\n z-index: 1;\n }\n \n .header-icon:hover {\n background: rgba(255, 255, 255, 0.2);\n }\n \n .header-icon svg {\n pointer-events: none;\n stroke: white;\n fill: none;\n }\n \n .back-btn.visible {\n display: flex !important;\n }\n\n ").concat(showLanding && this.landingScreen ? this.landingScreen.generateCSS() : '', "\n\n ").concat(showVoice ? this.voiceInterface.generateCSS() : '', "\n ").concat(showText ? this.textInterface.generateCSS() : '', "\n \n /* Footer Branding */\n .widget-footer {\n box-sizing: border-box;\n }\n \n .footer-brand-link {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n }\n \n .footer-brand-link:hover {\n opacity: 0.9;\n }\n \n @media (max-width: 768px) {\n .widget-footer {\n height: 32px;\n }\n .widget-footer span {\n font-size: 10px;\n }\n .widget-footer span:last-child {\n font-size: 8px;\n }\n }\n \n #text-chat-send-hint {\n text-align: center;\n line-height: 1.4;\n }\n \n .agent-thinking {\n font-style: italic;\n color: #6B7280;\n }\n\n /* ========================================\n MOBILE PILL BUTTON & LANDING OVERLAY\n Hidden on desktop, shown on mobile only\n ======================================== */\n .ttp-mobile-fab {\n display: none;\n }\n\n .ttp-mobile-landing {\n display: none;\n }\n\n @keyframes ttpWaveAnimation {\n 0%, 100% { transform: scaleY(0.4); }\n 50% { transform: scaleY(1); }\n }\n\n @keyframes ttpPulseAnimation {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.5; }\n }\n\n @media (max-width: 768px) {\n /* Hide desktop circle button on mobile */\n #text-chat-button {\n display: none !important;\n }\n\n /* Re-flow the container for pill shape */\n #text-chat-button-container {\n width: auto !important;\n height: auto !important;\n min-width: 0 !important;\n min-height: 0 !important;\n max-width: none !important;\n max-height: none !important;\n }\n\n /* Show mobile pill button */\n .ttp-mobile-fab {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 16px 10px 12px;\n background: linear-gradient(135deg, #581c87, ").concat(header.backgroundColor || '#7C3AED', ");\n border: none;\n border-radius: 24px;\n cursor: pointer;\n box-shadow: 0 8px 24px rgba(88, 28, 135, 0.4);\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n z-index: 1;\n font-family: 'DM Sans', -apple-system, BlinkMacSystemFont, sans-serif;\n touch-action: manipulation;\n -webkit-tap-highlight-color: transparent;\n }\n\n .ttp-mobile-fab:hover {\n transform: translateY(-3px);\n box-shadow: 0 12px 32px rgba(88, 28, 135, 0.5);\n }\n\n .ttp-mobile-fab__icon-wrap {\n width: 36px;\n height: 36px;\n border-radius: 50%;\n background: rgba(255, 255, 255, 0.15);\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n }\n\n .ttp-mobile-fab__waveform {\n display: flex;\n align-items: center;\n gap: 2px;\n height: 16px;\n }\n\n .ttp-mobile-fab__waveform .bar {\n width: 2px;\n background: #fff;\n border-radius: 1px;\n animation: ttpWaveAnimation 0.8s ease-in-out infinite;\n }\n\n .ttp-mobile-fab__waveform .bar:nth-child(1) { height: 5px; animation-delay: 0s; }\n .ttp-mobile-fab__waveform .bar:nth-child(2) { height: 10px; animation-delay: 0.1s; }\n .ttp-mobile-fab__waveform .bar:nth-child(3) { height: 16px; animation-delay: 0.2s; }\n .ttp-mobile-fab__waveform .bar:nth-child(4) { height: 8px; animation-delay: 0.3s; }\n .ttp-mobile-fab__waveform .bar:nth-child(5) { height: 12px; animation-delay: 0.4s; }\n\n .ttp-mobile-fab__label-wrap {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 1px;\n }\n\n .ttp-mobile-fab__label {\n color: #fff;\n font-size: 14px;\n font-weight: 600;\n line-height: 1.2;\n }\n\n .ttp-mobile-fab__status {\n display: flex;\n align-items: center;\n gap: 4px;\n font-size: 11px;\n color: rgba(255, 255, 255, 0.7);\n line-height: 1.2;\n }\n\n .ttp-mobile-fab__dot {\n width: 6px;\n height: 6px;\n background: ").concat(header.onlineIndicatorDotColor || '#10b981', ";\n border-radius: 50%;\n box-shadow: 0 0 6px rgba(16, 185, 129, 0.6);\n }\n\n /* ---- Mobile Landing Overlay ---- */\n .ttp-mobile-landing.active {\n display: block;\n position: fixed;\n bottom: 16px;\n left: 12px;\n right: 12px;\n background: linear-gradient(135deg, #581c87 0%, ").concat(header.backgroundColor || '#7C3AED', " 100%);\n border-radius: 20px;\n overflow: hidden;\n box-shadow:\n 0 10px 40px rgba(88, 28, 135, 0.4),\n 0 0 0 1px rgba(255, 255, 255, 0.1) inset;\n z-index: 10002;\n font-family: 'DM Sans', -apple-system, BlinkMacSystemFont, sans-serif;\n }\n\n .ttp-mobile-landing::before {\n content: '';\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n height: 1px;\n background: linear-gradient(90deg,\n transparent 0%,\n rgba(255, 255, 255, 0.3) 50%,\n transparent 100%);\n }\n\n .ttp-mobile-landing__header {\n padding: 14px 16px;\n display: flex;\n align-items: center;\n gap: 12px;\n }\n\n .ttp-mobile-landing__avatar {\n width: 44px;\n height: 44px;\n border-radius: 12px;\n background: rgba(255, 255, 255, 0.15);\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n }\n\n .ttp-mobile-landing__waveform {\n display: flex;\n align-items: center;\n gap: 3px;\n height: 20px;\n }\n\n .ttp-mobile-landing__waveform .bar {\n width: 3px;\n background: rgba(255, 255, 255, 0.9);\n border-radius: 2px;\n animation: ttpWaveAnimation 0.8s ease-in-out infinite;\n }\n\n .ttp-mobile-landing__waveform .bar:nth-child(1) { height: 6px; animation-delay: 0s; }\n .ttp-mobile-landing__waveform .bar:nth-child(2) { height: 12px; animation-delay: 0.1s; }\n .ttp-mobile-landing__waveform .bar:nth-child(3) { height: 20px; animation-delay: 0.2s; }\n .ttp-mobile-landing__waveform .bar:nth-child(4) { height: 10px; animation-delay: 0.3s; }\n .ttp-mobile-landing__waveform .bar:nth-child(5) { height: 16px; animation-delay: 0.4s; }\n\n .ttp-mobile-landing__info {\n flex: 1;\n min-width: 0;\n }\n\n .ttp-mobile-landing__name {\n color: #fff;\n font-size: 15px;\n font-weight: 600;\n margin-bottom: 2px;\n }\n\n .ttp-mobile-landing__status {\n color: rgba(255, 255, 255, 0.7);\n font-size: 12px;\n display: flex;\n align-items: center;\n gap: 5px;\n }\n\n .ttp-mobile-landing__dot {\n width: 6px;\n height: 6px;\n background: ").concat(header.onlineIndicatorDotColor || '#10b981', ";\n border-radius: 50%;\n animation: ttpPulseAnimation 2s ease-in-out infinite;\n }\n\n .ttp-mobile-landing__close {\n width: 32px;\n height: 32px;\n border-radius: 10px;\n background: rgba(255, 255, 255, 0.1);\n border: none;\n color: rgba(255, 255, 255, 0.7);\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n transition: all 0.2s;\n flex-shrink: 0;\n }\n\n .ttp-mobile-landing__close:hover {\n background: rgba(255, 255, 255, 0.2);\n color: #fff;\n }\n\n .ttp-mobile-landing__error {\n margin: 0 14px 10px;\n padding: 10px 12px;\n background: #fef2f2;\n border: 1px solid #fecaca;\n border-radius: 10px;\n display: flex;\n align-items: flex-start;\n gap: 8px;\n color: #b91c1c;\n font-size: 13px;\n line-height: 1.4;\n }\n .ttp-mobile-landing__error svg {\n flex-shrink: 0;\n margin-top: 1px;\n stroke: #dc2626;\n }\n\n .ttp-mobile-landing__actions {\n padding: 0 14px 14px;\n display: flex;\n gap: 10px;\n }\n\n .ttp-mobile-landing__btn {\n flex: 1;\n padding: 14px;\n border-radius: 12px;\n border: none;\n cursor: pointer;\n font-family: inherit;\n font-size: 14px;\n font-weight: 600;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 10px;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n touch-action: manipulation;\n -webkit-tap-highlight-color: transparent;\n }\n\n .ttp-mobile-landing__btn svg {\n width: 20px;\n height: 20px;\n flex-shrink: 0;\n }\n\n .ttp-mobile-landing__btn--call {\n background: #fff;\n color: ").concat(header.backgroundColor || '#7C3AED', ";\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n }\n\n .ttp-mobile-landing__btn--call:hover {\n transform: translateY(-2px);\n box-shadow: 0 6px 20px rgba(0, 0, 0, 0.2);\n }\n\n .ttp-mobile-landing__btn--text {\n background: rgba(255, 255, 255, 0.15);\n color: #fff;\n border: 1px solid rgba(255, 255, 255, 0.1);\n }\n\n .ttp-mobile-landing__btn--text:hover {\n background: rgba(255, 255, 255, 0.2);\n border-color: rgba(255, 255, 255, 0.2);\n }\n\n .ttp-mobile-landing__footer {\n padding: 10px 16px;\n display: flex;\n justify-content: center;\n border-top: 1px solid rgba(255, 255, 255, 0.08);\n }\n\n .ttp-mobile-landing__powered {\n color: rgba(255, 255, 255, 0.4);\n font-size: 11px;\n display: flex;\n align-items: center;\n gap: 4px;\n }\n\n .ttp-mobile-landing__powered svg {\n width: 10px;\n height: 10px;\n color: #f59e0b;\n }\n\n .ttp-mobile-landing__powered a {\n color: rgba(255, 255, 255, 0.6);\n text-decoration: none;\n }\n\n .ttp-mobile-landing__powered a:hover {\n color: #fff;\n }\n }\n ");
21980
22942
  }
21981
22943
  }, {
21982
22944
  key: "setupWidgetEvents",
@@ -22037,8 +22999,18 @@ var TTPChatWidget = /*#__PURE__*/function () {
22037
22999
  if ((_this6$voiceInterface3 = _this6.voiceInterface) !== null && _this6$voiceInterface3 !== void 0 && _this6$voiceInterface3.isActive) {
22038
23000
  // If call is active, show message inside widget instead of modal
22039
23001
  _this6.showBackButtonWarning();
23002
+ } else if (window.innerWidth <= 768) {
23003
+ // On mobile, close panel and return to FAB
23004
+ var panel = _this6.shadowRoot.getElementById('text-chat-panel');
23005
+ if (panel) {
23006
+ panel.classList.remove('open');
23007
+ _this6.isOpen = false;
23008
+ }
23009
+ var fab = _this6.shadowRoot.querySelector('.ttp-mobile-fab');
23010
+ if (fab) fab.style.display = '';
23011
+ _this6.showLanding();
22040
23012
  } else {
22041
- // If not active, just show landing
23013
+ // Desktop: show landing inside panel
22042
23014
  _this6.showLanding();
22043
23015
  }
22044
23016
  };
@@ -22047,7 +23019,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
22047
23019
  this.landingScreen.setupEventHandlers({
22048
23020
  onSelectVoice: function () {
22049
23021
  var _onSelectVoice = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
22050
- var _this6$voiceInterface4, _this6$voiceInterface8, _this6$shadowRoot, _this6$voiceInterface5, _this6$shadowRoot2, _this6$voiceInterface6, panel, idleState, floatingButton, _this6$voiceInterface7, _this6$voiceInterface9, _this6$voiceInterface1, _this6$config$behavio, _idleState, isDomainError, statusTitle, _this6$voiceInterface0, titleText, domainErrorTitle, _widgetMode, _this6$voiceInterface11, _this6$voiceInterface12, _this6$config$behavio2, _this6$voiceInterface10, _this6$voiceInterface13, _this6$shadowRoot3, existingBar, _floatingButton, _widgetMode2, _t;
23022
+ var _this6$voiceInterface4, _this6$voiceInterface8, _this6$shadowRoot, _this6$voiceInterface5, _this6$shadowRoot2, _this6$shadowRoot3, _this6$voiceInterface6, panel, idleState, floatingButton, mobileFab, _this6$voiceInterface7, _this6$voiceInterface9, _this6$voiceInterface1, _this6$config$behavio, _idleState, isDomainError, statusTitle, _this6$voiceInterface0, titleText, domainErrorTitle, _widgetMode, _this6$voiceInterface11, _this6$voiceInterface12, _this6$config$behavio2, _this6$voiceInterface10, _this6$voiceInterface13, _this6$shadowRoot4, _this6$shadowRoot5, existingBar, _floatingButton, _mobileFab, _widgetMode2, _t;
22051
23023
  return _regenerator().w(function (_context) {
22052
23024
  while (1) switch (_context.p = _context.n) {
22053
23025
  case 0:
@@ -22097,6 +23069,10 @@ var TTPChatWidget = /*#__PURE__*/function () {
22097
23069
  if (floatingButton) {
22098
23070
  floatingButton.style.display = 'none';
22099
23071
  }
23072
+ mobileFab = (_this6$shadowRoot3 = _this6.shadowRoot) === null || _this6$shadowRoot3 === void 0 ? void 0 : _this6$shadowRoot3.querySelector('.ttp-mobile-fab');
23073
+ if (mobileFab) {
23074
+ mobileFab.style.display = 'none';
23075
+ }
22100
23076
  // Create minimized bar with connecting state
22101
23077
  if (!((_this6$voiceInterface6 = _this6.voiceInterface) !== null && _this6$voiceInterface6 !== void 0 && _this6$voiceInterface6.createMobileMinimizedBar)) {
22102
23078
  _context.n = 3;
@@ -22206,10 +23182,14 @@ var TTPChatWidget = /*#__PURE__*/function () {
22206
23182
  _this6.voiceInterface.removeMobileMinimizedBar();
22207
23183
  }
22208
23184
  // Restore floating button if it was hidden
22209
- _floatingButton = ((_this6$shadowRoot3 = _this6.shadowRoot) === null || _this6$shadowRoot3 === void 0 ? void 0 : _this6$shadowRoot3.getElementById('text-chat-button')) || document.getElementById('text-chat-button');
23185
+ _floatingButton = ((_this6$shadowRoot4 = _this6.shadowRoot) === null || _this6$shadowRoot4 === void 0 ? void 0 : _this6$shadowRoot4.getElementById('text-chat-button')) || document.getElementById('text-chat-button');
22210
23186
  if (_floatingButton) {
22211
23187
  _floatingButton.style.display = '';
22212
23188
  }
23189
+ _mobileFab = (_this6$shadowRoot5 = _this6.shadowRoot) === null || _this6$shadowRoot5 === void 0 ? void 0 : _this6$shadowRoot5.querySelector('.ttp-mobile-fab');
23190
+ if (_mobileFab) {
23191
+ _mobileFab.style.display = '';
23192
+ }
22213
23193
  }
22214
23194
 
22215
23195
  // Show user-friendly error message (unless it's a user cancellation)
@@ -22302,6 +23282,9 @@ var TTPChatWidget = /*#__PURE__*/function () {
22302
23282
  }
22303
23283
  }
22304
23284
 
23285
+ // Mobile floating button & landing overlay
23286
+ this.setupMobileWidgetEvents();
23287
+
22305
23288
  // Keyboard navigation
22306
23289
  if (this.config.accessibility.keyboardNavigation) {
22307
23290
  this.setupKeyboardNavigation();
@@ -22385,13 +23368,84 @@ var TTPChatWidget = /*#__PURE__*/function () {
22385
23368
  // Hide new chat button on voice interface (not applicable)
22386
23369
  if (newChatBtn) newChatBtn.style.display = 'none';
22387
23370
  }
23371
+ }, {
23372
+ key: "setupMobileWidgetEvents",
23373
+ value: function setupMobileWidgetEvents() {
23374
+ var _this7 = this;
23375
+ if (!this.shadowRoot) return;
23376
+ var mobileFab = this.shadowRoot.querySelector('.ttp-mobile-fab');
23377
+ var mobileLanding = this.shadowRoot.getElementById('ttpMobileLanding');
23378
+ if (!mobileFab || !mobileLanding) return;
23379
+ var closeMobileLanding = function closeMobileLanding() {
23380
+ mobileLanding.classList.remove('active');
23381
+ mobileFab.style.display = '';
23382
+ _this7.isMobileLandingOpen = false;
23383
+ };
23384
+ var openMobileLanding = function openMobileLanding() {
23385
+ if (_this7.config.promptAnimation.hideAfterClick) {
23386
+ _this7.hidePrompt();
23387
+ }
23388
+ // Clear any previous error banner
23389
+ var errorBanner = _this7.shadowRoot.getElementById('ttpMobileLandingError');
23390
+ if (errorBanner) errorBanner.style.display = 'none';
23391
+ mobileFab.style.display = 'none';
23392
+ mobileLanding.classList.add('active');
23393
+ _this7.isMobileLandingOpen = true;
23394
+ };
23395
+ mobileFab.onclick = openMobileLanding;
23396
+ var mobileClose = mobileLanding.querySelector('.ttp-mobile-landing__close');
23397
+ if (mobileClose) {
23398
+ mobileClose.onclick = closeMobileLanding;
23399
+ }
23400
+ var mobileCallBtn = this.shadowRoot.getElementById('ttpMobileCallBtn');
23401
+ if (mobileCallBtn) {
23402
+ mobileCallBtn.onclick = function () {
23403
+ closeMobileLanding();
23404
+ var desktopVoiceCard = _this7.shadowRoot.getElementById('mode-card-voice');
23405
+ if (desktopVoiceCard) {
23406
+ desktopVoiceCard.click();
23407
+ } else {
23408
+ _this7.showVoice();
23409
+ }
23410
+ };
23411
+ }
23412
+ var mobileChatBtn = this.shadowRoot.getElementById('ttpMobileChatBtn');
23413
+ if (mobileChatBtn) {
23414
+ mobileChatBtn.onclick = function () {
23415
+ closeMobileLanding();
23416
+ var panel = _this7.shadowRoot.getElementById('text-chat-panel');
23417
+ if (panel) {
23418
+ panel.classList.add('open');
23419
+ _this7.isOpen = true;
23420
+ }
23421
+ mobileFab.style.display = 'none';
23422
+ _this7.showText();
23423
+ };
23424
+ }
23425
+
23426
+ // Close mobile landing on outside click
23427
+ document.addEventListener('click', function (e) {
23428
+ if (!_this7.isMobileLandingOpen) return;
23429
+ var path = e.composedPath();
23430
+ if (!path.includes(mobileLanding) && !path.includes(mobileFab)) {
23431
+ closeMobileLanding();
23432
+ }
23433
+ });
23434
+
23435
+ // Close mobile landing on Escape
23436
+ document.addEventListener('keydown', function (e) {
23437
+ if (e.key === 'Escape' && _this7.isMobileLandingOpen) {
23438
+ closeMobileLanding();
23439
+ }
23440
+ });
23441
+ }
22388
23442
  }, {
22389
23443
  key: "setupKeyboardNavigation",
22390
23444
  value: function setupKeyboardNavigation() {
22391
- var _this7 = this;
23445
+ var _this8 = this;
22392
23446
  document.addEventListener('keydown', function (e) {
22393
- if (e.key === 'Escape' && _this7.isOpen) {
22394
- _this7.togglePanel();
23447
+ if (e.key === 'Escape' && _this8.isOpen) {
23448
+ _this8.togglePanel();
22395
23449
  }
22396
23450
  });
22397
23451
  }
@@ -22402,7 +23456,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
22402
23456
  }, {
22403
23457
  key: "setupPromptAnimation",
22404
23458
  value: function setupPromptAnimation() {
22405
- var _this8 = this;
23459
+ var _this9 = this;
22406
23460
  if (!this.shadowRoot) return;
22407
23461
  var promptConfig = this.config.promptAnimation || {};
22408
23462
  // Default to disabled if not specified (enabled === true means explicitly enabled)
@@ -22432,7 +23486,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
22432
23486
  // Auto-hide after configured seconds (only if widget is closed)
22433
23487
  if (!this.isOpen && promptConfig.hideAfterSeconds !== null && promptConfig.hideAfterSeconds > 0) {
22434
23488
  this.promptAutoHideTimer = setTimeout(function () {
22435
- _this8.hidePrompt();
23489
+ _this9.hidePrompt();
22436
23490
  }, promptConfig.hideAfterSeconds * 1000);
22437
23491
  }
22438
23492
 
@@ -22444,7 +23498,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
22444
23498
  // Wrap the callback to also hide pulse rings
22445
23499
  this.config.onConversationStart = function () {
22446
23500
  // Hide pulse rings when call starts
22447
- var pulseRings = _this8.shadowRoot.getElementById('prompt-pulse-rings');
23501
+ var pulseRings = _this9.shadowRoot.getElementById('prompt-pulse-rings');
22448
23502
  if (pulseRings) pulseRings.style.display = 'none';
22449
23503
 
22450
23504
  // Call original callback if it exists
@@ -22456,7 +23510,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
22456
23510
  // Also listen to recordingStarted event from SDK if available
22457
23511
  if (this.voiceInterface.sdk.voiceSDK) {
22458
23512
  this.voiceInterface.sdk.voiceSDK.on('recordingStarted', function () {
22459
- var pulseRings = _this8.shadowRoot.getElementById('prompt-pulse-rings');
23513
+ var pulseRings = _this9.shadowRoot.getElementById('prompt-pulse-rings');
22460
23514
  if (pulseRings) pulseRings.style.display = 'none';
22461
23515
  });
22462
23516
  }
@@ -22504,7 +23558,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
22504
23558
  }, {
22505
23559
  key: "showPrompt",
22506
23560
  value: function showPrompt() {
22507
- var _this9 = this;
23561
+ var _this0 = this;
22508
23562
  if (!this.shadowRoot) return;
22509
23563
  var promptConfig = this.config.promptAnimation || {};
22510
23564
  // Default to enabled if not specified
@@ -22524,8 +23578,8 @@ var TTPChatWidget = /*#__PURE__*/function () {
22524
23578
  // Function to actually show the elements
22525
23579
  var doShow = function doShow() {
22526
23580
  // Show prompt bubble (check both shadow root and document for compatibility)
22527
- var promptBubble = _this9.shadowRoot.getElementById('prompt-bubble');
22528
- if (!promptBubble && _this9.shadowRoot !== document) {
23581
+ var promptBubble = _this0.shadowRoot.getElementById('prompt-bubble');
23582
+ if (!promptBubble && _this0.shadowRoot !== document) {
22529
23583
  promptBubble = document.getElementById('prompt-bubble');
22530
23584
  }
22531
23585
  if (promptBubble) {
@@ -22539,8 +23593,8 @@ var TTPChatWidget = /*#__PURE__*/function () {
22539
23593
 
22540
23594
  // Show pulse rings if enabled
22541
23595
  if (promptConfig.showPulseRings !== false) {
22542
- var _pulseRings = _this9.shadowRoot.getElementById('prompt-pulse-rings');
22543
- if (!_pulseRings && _this9.shadowRoot !== document) {
23596
+ var _pulseRings = _this0.shadowRoot.getElementById('prompt-pulse-rings');
23597
+ if (!_pulseRings && _this0.shadowRoot !== document) {
22544
23598
  _pulseRings = document.getElementById('prompt-pulse-rings');
22545
23599
  }
22546
23600
  if (_pulseRings) {
@@ -22577,7 +23631,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
22577
23631
  }
22578
23632
  // Start new timer
22579
23633
  this.promptAutoHideTimer = setTimeout(function () {
22580
- _this9.hidePrompt();
23634
+ _this0.hidePrompt();
22581
23635
  }, promptConfig.hideAfterSeconds * 1000);
22582
23636
  }
22583
23637
  }
@@ -22593,7 +23647,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
22593
23647
  }, {
22594
23648
  key: "_doTogglePanel",
22595
23649
  value: function _doTogglePanel() {
22596
- var _this0 = this;
23650
+ var _this1 = this;
22597
23651
  if (!this.shadowRoot) return;
22598
23652
  this.isOpen = !this.isOpen;
22599
23653
  var panel = this.shadowRoot.getElementById('text-chat-panel');
@@ -22606,22 +23660,27 @@ var TTPChatWidget = /*#__PURE__*/function () {
22606
23660
  // Panel is opening - hide prompt
22607
23661
  this.hidePrompt();
22608
23662
  setTimeout(function () {
22609
- var input = _this0.shadowRoot.getElementById('messageInput');
23663
+ var input = _this1.shadowRoot.getElementById('messageInput');
22610
23664
  if (input) input.focus();
22611
23665
  }, 100);
22612
23666
  } else {
23667
+ // Panel is closing — restore mobile FAB if on mobile
23668
+ if (window.innerWidth <= 768) {
23669
+ var fab = this.shadowRoot.querySelector('.ttp-mobile-fab');
23670
+ if (fab) fab.style.display = '';
23671
+ }
22613
23672
  // Panel is closing - show prompt (if enabled)
22614
23673
  // Call showPrompt immediately, then also retry after a delay to ensure it's visible
22615
23674
  this.showPrompt();
22616
23675
  setTimeout(function () {
22617
- var _this0$config$promptA;
23676
+ var _this1$config$promptA;
22618
23677
  // Double-check and ensure prompt is visible
22619
- var promptBubble = _this0.shadowRoot.getElementById('prompt-bubble');
23678
+ var promptBubble = _this1.shadowRoot.getElementById('prompt-bubble');
22620
23679
  if (promptBubble && promptBubble.style.display === 'none') {
22621
23680
  promptBubble.style.display = 'block';
22622
23681
  }
22623
- var pulseRings = _this0.shadowRoot.getElementById('prompt-pulse-rings');
22624
- if (pulseRings && ((_this0$config$promptA = _this0.config.promptAnimation) === null || _this0$config$promptA === void 0 ? void 0 : _this0$config$promptA.showPulseRings) !== false) {
23682
+ var pulseRings = _this1.shadowRoot.getElementById('prompt-pulse-rings');
23683
+ if (pulseRings && ((_this1$config$promptA = _this1.config.promptAnimation) === null || _this1$config$promptA === void 0 ? void 0 : _this1$config$promptA.showPulseRings) !== false) {
22625
23684
  if (pulseRings.style.display === 'none') {
22626
23685
  pulseRings.style.display = 'block';
22627
23686
  }
@@ -22661,13 +23720,14 @@ var TTPChatWidget = /*#__PURE__*/function () {
22661
23720
  padding: '12px 24px',
22662
23721
  borderRadius: '8px',
22663
23722
  boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',
22664
- zIndex: '10000',
23723
+ zIndex: '10003',
22665
23724
  fontSize: '14px',
22666
23725
  fontWeight: '500',
22667
23726
  maxWidth: '90%',
22668
23727
  textAlign: 'center',
22669
23728
  animation: 'slideUp 0.3s ease-out',
22670
- fontFamily: 'inherit'
23729
+ fontFamily: 'inherit',
23730
+ pointerEvents: 'auto'
22671
23731
  });
22672
23732
 
22673
23733
  // Add CSS animation if not already added
@@ -22692,6 +23752,25 @@ var TTPChatWidget = /*#__PURE__*/function () {
22692
23752
  }, 300);
22693
23753
  }, 4000);
22694
23754
  }
23755
+ }, {
23756
+ key: "showMobileLandingError",
23757
+ value: function showMobileLandingError(message) {
23758
+ if (!this.shadowRoot) return;
23759
+ var mobileLanding = this.shadowRoot.getElementById('ttpMobileLanding');
23760
+ var mobileFab = this.shadowRoot.querySelector('.ttp-mobile-fab');
23761
+ var errorBanner = this.shadowRoot.getElementById('ttpMobileLandingError');
23762
+ if (!mobileLanding || !errorBanner) return;
23763
+
23764
+ // Set error text
23765
+ var errorText = errorBanner.querySelector('.ttp-mobile-landing__error-text');
23766
+ if (errorText) errorText.textContent = message;
23767
+ errorBanner.style.display = 'flex';
23768
+
23769
+ // Show the mobile landing overlay with the error
23770
+ mobileLanding.classList.add('active');
23771
+ if (mobileFab) mobileFab.style.display = 'none';
23772
+ this.isMobileLandingOpen = true;
23773
+ }
22695
23774
 
22696
23775
  /**
22697
23776
  * Show confirmation modal (reusing modal pattern)
@@ -23366,12 +24445,12 @@ var TTPChatWidget = /*#__PURE__*/function () {
23366
24445
  key: "_flushPendingClientTools",
23367
24446
  value: function _flushPendingClientTools() {
23368
24447
  var _this$voiceInterface1,
23369
- _this1 = this;
24448
+ _this10 = this;
23370
24449
  if (this._pendingClientTools && (_this$voiceInterface1 = this.voiceInterface) !== null && _this$voiceInterface1 !== void 0 && _this$voiceInterface1.sdk) {
23371
24450
  this._pendingClientTools.forEach(function (_ref2) {
23372
24451
  var name = _ref2.name,
23373
24452
  handler = _ref2.handler;
23374
- _this1.voiceInterface.sdk.registerToolHandler(name, handler);
24453
+ _this10.voiceInterface.sdk.registerToolHandler(name, handler);
23375
24454
  });
23376
24455
  this._pendingClientTools = null;
23377
24456
  console.log('TTPChatWidget: Flushed pending client tools');
@@ -24211,6 +25290,18 @@ var VoiceInterface = /*#__PURE__*/function () {
24211
25290
  return _ref2.apply(this, arguments);
24212
25291
  };
24213
25292
  }();
25293
+
25294
+ // Handle e-commerce actions received on a non-ecommerce widget
25295
+ this.sdk.onUnsupportedAction = function (actionType) {
25296
+ console.warn("\u26A0\uFE0F Received \"".concat(actionType, "\" but this is not an E-Commerce widget. Use TTPEcommerceWidget instead."));
25297
+ var msg = 'This widget does not support e-commerce features. Please use TTPEcommerceWidget to display products.';
25298
+ if (_this.config.onErrorToast) {
25299
+ _this.config.onErrorToast(msg, 'warning');
25300
+ }
25301
+ if (_this.isMobile && _this.config.onMobileError) {
25302
+ _this.config.onMobileError(msg);
25303
+ }
25304
+ };
24214
25305
  }
24215
25306
 
24216
25307
  /**
@@ -24463,7 +25554,7 @@ var VoiceInterface = /*#__PURE__*/function () {
24463
25554
  value: (function () {
24464
25555
  var _startVoiceCall = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4() {
24465
25556
  var _this2 = this;
24466
- var permissionState, deviceInfo;
25557
+ var permissionState, deviceInfo, idleState, activeState;
24467
25558
  return _regenerator().w(function (_context4) {
24468
25559
  while (1) switch (_context4.n) {
24469
25560
  case 0:
@@ -24506,14 +25597,19 @@ var VoiceInterface = /*#__PURE__*/function () {
24506
25597
  _context4.n = 4;
24507
25598
  break;
24508
25599
  }
24509
- console.log('📋 Permission is in prompt state, showing pre-prompt modal...');
25600
+ console.log('📋 Permission is in prompt state, showing pre-prompt modal with call view...');
25601
+
25602
+ // Switch UI to active call view so it's visible behind the modal
25603
+ idleState = this.shadowRoot.getElementById('voiceIdleState');
25604
+ activeState = this.shadowRoot.getElementById('voiceActiveState');
25605
+ if (idleState) idleState.style.display = 'none';
25606
+ if (activeState) activeState.style.display = 'flex';
24510
25607
  return _context4.a(2, new Promise(function (resolve, reject) {
24511
25608
  (0,_shared_MicPermissionModals_js__WEBPACK_IMPORTED_MODULE_2__.showPrePromptModal)(/*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee3() {
24512
25609
  var _t;
24513
25610
  return _regenerator().w(function (_context3) {
24514
25611
  while (1) switch (_context3.p = _context3.n) {
24515
25612
  case 0:
24516
- // User accepted pre-prompt - proceed with voice call
24517
25613
  console.log('✅ Pre-prompt accepted, proceeding with voice call...');
24518
25614
  _context3.p = 1;
24519
25615
  _context3.n = 2;
@@ -24531,10 +25627,11 @@ var VoiceInterface = /*#__PURE__*/function () {
24531
25627
  }
24532
25628
  }, _callee3, null, [[1, 3]]);
24533
25629
  })), function () {
24534
- // User cancelled pre-prompt - reset connecting state
24535
25630
  console.log('❌ Pre-prompt cancelled');
25631
+ // Revert UI back to idle state
25632
+ if (idleState) idleState.style.display = 'flex';
25633
+ if (activeState) activeState.style.display = 'none';
24536
25634
  _this2.resetConnectingState();
24537
- // Reject so caller knows call was cancelled
24538
25635
  reject(new Error('Call cancelled by user'));
24539
25636
  });
24540
25637
  }));
@@ -24560,7 +25657,7 @@ var VoiceInterface = /*#__PURE__*/function () {
24560
25657
  value: (function () {
24561
25658
  var _proceedWithVoiceCall = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee5() {
24562
25659
  var _this3 = this;
24563
- var isResumeCall, panel, header, toggleText, _voiceInterface, originalSection, compactSection, idleState, _panel, fallbackPanel, sampleRate, mediaStream, floatingButton, fallbackButton, existingBar, _floatingButton, _fallbackButton, _idleState, _panel2, _fallbackPanel, deviceInfo, _idleState2, activeState, voiceInterface, connected, serverRejected, originalOnError, originalOnDisconnected, attempts, _this$sdk$voiceSDK, error, _existingBar, _existingBar2, _idleState3, _panel3, _fallbackPanel2, _floatingButton2, _fallbackButton2, updateTimer, _existingBar3, _existingBar4, _idleState4, _panel4, _fallbackPanel3, _floatingButton3, _fallbackButton3, _updateTimer, _deviceInfo, _t2, _t3, _t4, _t5, _t6, _t7;
25660
+ var isResumeCall, panel, header, toggleText, _voiceInterface, originalSection, compactSection, idleState, _panel, fallbackPanel, sampleRate, mediaStream, floatingButton, fallbackButton, mobileFabGrant, existingBar, _floatingButton, _fallbackButton, mobileFabDeny, _idleState, _panel2, _fallbackPanel, deviceInfo, _idleState2, activeState, voiceInterface, connected, serverRejected, originalOnError, originalOnDisconnected, attempts, _this$sdk$voiceSDK, error, _existingBar, _existingBar2, _idleState3, _panel3, _fallbackPanel2, _floatingButton2, _fallbackButton2, updateTimer, _existingBar3, _existingBar4, _idleState4, _panel4, _fallbackPanel3, _floatingButton3, _fallbackButton3, _updateTimer, _deviceInfo, _t2, _t3, _t4, _t5, _t6, _t7;
24564
25661
  return _regenerator().w(function (_context5) {
24565
25662
  while (1) switch (_context5.p = _context5.n) {
24566
25663
  case 0:
@@ -24667,6 +25764,8 @@ var VoiceInterface = /*#__PURE__*/function () {
24667
25764
  console.log('✅ Floating button hidden (permission granted, fallback)');
24668
25765
  }
24669
25766
  }
25767
+ mobileFabGrant = this.shadowRoot.querySelector('.ttp-mobile-fab');
25768
+ if (mobileFabGrant) mobileFabGrant.style.display = 'none';
24670
25769
  _context5.n = 4;
24671
25770
  break;
24672
25771
  case 3:
@@ -24692,6 +25791,8 @@ var VoiceInterface = /*#__PURE__*/function () {
24692
25791
  _fallbackButton.style.display = '';
24693
25792
  }
24694
25793
  }
25794
+ mobileFabDeny = this.shadowRoot.querySelector('.ttp-mobile-fab');
25795
+ if (mobileFabDeny) mobileFabDeny.style.display = '';
24695
25796
  _idleState = this.shadowRoot.getElementById('voiceIdleState');
24696
25797
  if (_idleState) {
24697
25798
  _idleState.style.display = 'flex';
@@ -25262,7 +26363,7 @@ var VoiceInterface = /*#__PURE__*/function () {
25262
26363
  key: "endCallOnServerRejection",
25263
26364
  value: (function () {
25264
26365
  var _endCallOnServerRejection = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee6() {
25265
- var voiceInterfaceEl, landingScreen, _this$sdk$voiceSDK2, _this$sdk$voiceSDK3, audioRecorder, isRecording, _this$sdk$voiceSDK4, activeState, idleState, panel, fallbackPanel, floatingButton, fallbackButton, _t8, _t9, _t0, _t1, _t10;
26366
+ var voiceInterfaceEl, landingScreen, _this$sdk$voiceSDK2, _this$sdk$voiceSDK3, audioRecorder, isRecording, _this$sdk$voiceSDK4, activeState, idleState, panel, fallbackPanel, floatingButton, fallbackButton, mobileFabEnd, errorMsg, _t8, _t9, _t0, _t1, _t10;
25266
26367
  return _regenerator().w(function (_context6) {
25267
26368
  while (1) switch (_context6.p = _context6.n) {
25268
26369
  case 0:
@@ -25475,7 +26576,7 @@ var VoiceInterface = /*#__PURE__*/function () {
25475
26576
  console.log('🚫 showDomainError() completed, isShowingDomainError:', this.isShowingDomainError);
25476
26577
  }
25477
26578
 
25478
- // Mobile: restore panel visibility
26579
+ // Mobile: restore panel visibility and show error toast
25479
26580
  if (this.isMobile) {
25480
26581
  panel = this.shadowRoot.getElementById('text-chat-panel');
25481
26582
  if (panel) {
@@ -25497,10 +26598,20 @@ var VoiceInterface = /*#__PURE__*/function () {
25497
26598
  fallbackButton.style.display = '';
25498
26599
  }
25499
26600
  }
26601
+ mobileFabEnd = this.shadowRoot.querySelector('.ttp-mobile-fab');
26602
+ if (mobileFabEnd) mobileFabEnd.style.display = '';
25500
26603
 
25501
26604
  // Remove mobile minimized bar if it exists
25502
26605
  this.removeMobileMinimizedBar();
25503
26606
  this.stopWaveformAnimation();
26607
+
26608
+ // Show error in mobile landing overlay since the panel isn't visible
26609
+ errorMsg = this.t('domainNotValidated') || 'Domain not whitelisted. Please contact support.';
26610
+ if (this.config.onMobileError) {
26611
+ this.config.onMobileError(errorMsg);
26612
+ } else if (this.config.onErrorToast) {
26613
+ this.config.onErrorToast(errorMsg, 'error');
26614
+ }
25504
26615
  }
25505
26616
  case 23:
25506
26617
  return _context6.a(2);
@@ -25640,13 +26751,15 @@ var VoiceInterface = /*#__PURE__*/function () {
25640
26751
  this.removeMobileMinimizedBar();
25641
26752
  this.messages = [];
25642
26753
 
25643
- // Show widget panel again (restore it)
25644
- // Restore widget panel (use shadowRoot if in Shadow DOM)
26754
+ // Restore mobile FAB visibility (it was hidden when call started)
26755
+ var mobileFab = this.shadowRoot.querySelector('.ttp-mobile-fab');
26756
+ if (mobileFab) mobileFab.style.display = '';
26757
+
26758
+ // Restore widget panel (remove inline display:none)
25645
26759
  var panel = this.shadowRoot.getElementById('text-chat-panel');
25646
26760
  if (panel) {
25647
- panel.style.display = ''; // Remove inline display:none
26761
+ panel.style.display = '';
25648
26762
  } else {
25649
- // Fallback: try document if not in Shadow DOM
25650
26763
  var fallbackPanel = document.getElementById('text-chat-panel');
25651
26764
  if (fallbackPanel) {
25652
26765
  fallbackPanel.style.display = '';
@@ -27055,7 +28168,7 @@ var Mobile = /*#__PURE__*/function () {
27055
28168
  container.id = 'mobile-voice-call-bar-container';
27056
28169
  container.innerHTML = "\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 </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\">".concat(this.t('listening'), "</span>\n </div>\n \n <!-- Centered Waveform -->\n <div class=\"mobile-waveform-center\" id=\"mobileWaveform\">\n ").concat(Array(8).fill(0).map(function (_, i) {
27057
28170
  return "<div class=\"mobile-waveform-bar\" data-index=\"".concat(i, "\"></div>");
27058
- }).join(''), "\n </div>\n \n <!-- Controls -->\n <div class=\"mobile-controls\">\n <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=\"#ef4444\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"mobile-mic-off-icon\" style=\"display: none;\">\n <line x1=\"2\" x2=\"22\" y1=\"2\" y2=\"22\"/>\n <path d=\"M18.89 13.23A7.12 7.12 0 0 0 19 12v-2\"/>\n <path d=\"M5 10v2a7 7 0 0 0 12 5\"/>\n <path d=\"M15 9.34V5a3 3 0 0 0-5.68-1.33\"/>\n <path d=\"M9 9v3a3 3 0 0 0 5.12 2.12\"/>\n <line x1=\"12\" x2=\"12\" y1=\"19\" y2=\"22\"/>\n </svg>\n </button>\n \n <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=\"#ef4444\" 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 <line x1=\"22\" x2=\"16\" y1=\"9\" y2=\"15\"/>\n <line x1=\"16\" x2=\"22\" y1=\"9\" y2=\"15\"/>\n </svg>\n </button>\n \n <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 class=\"mobile-transcript-row\" id=\"mobileTranscriptRow\">\n <p class=\"mobile-transcript-text\" id=\"mobileTranscriptText\">").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 \n <!-- Expanded Transcript Overlay -->\n <div class=\"mobile-transcript-overlay\" id=\"mobileTranscriptOverlay\" style=\"display: none;\">\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\">").concat(this.t('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 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 />\n <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 ").concat(this.t('voiceActive') || 'Voice is still active', " \u2022 ").concat(this.t('typeOrSpeak') || 'Type or speak', "</p>\n </div>\n </div>\n </div>\n ");
28171
+ }).join(''), "\n </div>\n \n <!-- Controls -->\n <div class=\"mobile-controls\">\n <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=\"#ef4444\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"mobile-mic-off-icon\" style=\"display: none;\">\n <line x1=\"2\" x2=\"22\" y1=\"2\" y2=\"22\"/>\n <path d=\"M18.89 13.23A7.12 7.12 0 0 0 19 12v-2\"/>\n <path d=\"M5 10v2a7 7 0 0 0 12 5\"/>\n <path d=\"M15 9.34V5a3 3 0 0 0-5.68-1.33\"/>\n <path d=\"M9 9v3a3 3 0 0 0 5.12 2.12\"/>\n <line x1=\"12\" x2=\"12\" y1=\"19\" y2=\"22\"/>\n </svg>\n </button>\n \n <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=\"#ef4444\" 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 <line x1=\"22\" x2=\"16\" y1=\"9\" y2=\"15\"/>\n <line x1=\"16\" x2=\"22\" y1=\"9\" y2=\"15\"/>\n </svg>\n </button>\n \n <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 class=\"mobile-transcript-row\" id=\"mobileTranscriptRow\">\n <p class=\"mobile-transcript-text\" id=\"mobileTranscriptText\">").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\n <!-- Cart Row - shown when items are in cart -->\n <div class=\"mobile-cart-row\" id=\"mobileCartRow\" style=\"display: none;\">\n <div class=\"mobile-cart-row__content\">\n <span class=\"mobile-cart-row__icon\">\uD83D\uDED2</span>\n <span class=\"mobile-cart-row__text\" id=\"mobileCartText\">0 items \xB7 \u20AA0.00</span>\n </div>\n </div>\n </div>\n \n <!-- Expanded Transcript Overlay -->\n <div class=\"mobile-transcript-overlay\" id=\"mobileTranscriptOverlay\" style=\"display: none;\">\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\">").concat(this.t('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 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 />\n <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 ").concat(this.t('voiceActive') || 'Voice is still active', " \u2022 ").concat(this.t('typeOrSpeak') || 'Type or speak', "</p>\n </div>\n </div>\n </div>\n ");
27059
28172
 
27060
28173
  // Append to body
27061
28174
  document.body.appendChild(container);
@@ -27354,7 +28467,7 @@ var Styles = /*#__PURE__*/function () {
27354
28467
  }, {
27355
28468
  key: "_getSharedMobileCSS",
27356
28469
  value: function _getSharedMobileCSS(important) {
27357
- return "\n .mobile-duration-dot {\ndisplay: block !important;\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background-color: #ef4444;\n animation: mobilePulse 1.5s ease-in-out infinite;\n }\n\n @keyframes mobilePulse {\n 0%, 100% { opacity: 1; transform: scale(1); }\n 50% { opacity: 0.5; transform: scale(0.95); }\n }\n\n .mobile-voice-bar {\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding: 14px;\n background: linear-gradient(135deg, #7c3aed 0%, #a855f7 50%, #c084fc 100%);\n border-radius: 24px 24px 0 0;\n box-shadow: 0 -8px 32px rgba(124, 58, 237, 0.5), 0 -2px 8px rgba(0,0,0,0.3), inset 0 1px 0 rgba(255,255,255,0.2);\n width: 100%;\n max-width: 100%;\n pointer-events: auto;\n }\n\n .mobile-top-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n position: relative;\n }\n\n .mobile-status-indicator {\n display: flex;\n align-items: center;\n gap: 5px;\n min-width: 85px;\n }\n\n .mobile-status-dot {\ndisplay: block !important;\n width: 7px;\n height: 7px;\n border-radius: 50%;\n background-color: #22c55e;\n box-shadow: 0 0 8px #22c55e;\n transition: all 0.3s ease;\n }\n\n .mobile-status-text {\n font-size: 11px;\n font-weight: 700;\n color: rgba(255,255,255,0.95);\n text-transform: uppercase;\n letter-spacing: 0.3px;\n }\n\n .mobile-waveform-center {\n position: absolute;\n left: 50%;\n transform: translateX(-50%);\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 3px;\n height: 24px;\n }\n\n .mobile-waveform-bar {\ndisplay: block !important;\n width: 4px;\n height: 4px;\n background-color: #fff;\n border-radius: 2px;\n transition: height 0.15s ease;\n box-shadow: 0 0 8px rgba(255,255,255,0.5);\n }\n\n .mobile-controls {\n display: flex;\n align-items: center;\n gap: 6px;\n }\n\n .mobile-control-btn {\n display: flex".concat(important, ";\n align-items: center").concat(important, ";\n justify-content: center").concat(important, ";\n width: 32px").concat(important, ";\n height: 32px").concat(important, ";\n min-width: 32px").concat(important, ";\n min-height: 32px").concat(important, ";\n max-width: 32px").concat(important, ";\n max-height: 32px").concat(important, ";\n border-radius: 50%").concat(important, ";\n border: none").concat(important, ";\n cursor: pointer").concat(important, ";\n transition: all 0.2s ease;\n background-color: rgba(255,255,255,0.15)").concat(important, ";\n position: relative").concat(important, ";\n overflow: hidden").concat(important, ";\n padding: 0").concat(important, ";\n margin: 0").concat(important, ";\n box-sizing: border-box").concat(important, ";\n }\n\n .mobile-control-btn:hover {\n background-color: rgba(255,255,255,0.25)").concat(important, ";\n }\n\n .mobile-control-btn.muted {\n background-color: rgba(239, 68, 68, 0.3)").concat(important, ";\n }\n\n .mobile-control-btn svg {\n position: absolute").concat(important, ";\n top: 50%").concat(important, ";\n left: 50%").concat(important, ";\n transform: translate(-50%, -50%)").concat(important, ";\n display: block").concat(important, ";\n visibility: visible").concat(important, ";\n opacity: 1").concat(important, ";\n width: 15px").concat(important, ";\n height: 15px").concat(important, ";\n margin: 0").concat(important, ";\n padding: 0").concat(important, ";\n }\n\n .mobile-mic-off-icon,\n .mobile-speaker-off-icon {\n display: none !important;\n }\n\n .mobile-control-btn.muted .mobile-mic-icon,\n .mobile-control-btn.muted .mobile-speaker-icon {\n display: none !important;\n }\n\n .mobile-control-btn.muted .mobile-mic-off-icon,\n .mobile-control-btn.muted .mobile-speaker-off-icon {\n display: block !important;\n }\n\n .mobile-end-call-btn {\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n justify-content: center").concat(important, ";\n width: 36px").concat(important, ";\n height: 36px").concat(important, ";\n min-width: 36px").concat(important, ";\n min-height: 36px").concat(important, ";\n max-width: 36px").concat(important, ";\n max-height: 36px").concat(important, ";\n border-radius: 50%").concat(important, ";\n border: none").concat(important, ";\n cursor: pointer").concat(important, ";\n background-color: #ef4444").concat(important, ";\n box-shadow: 0 4px 14px rgba(239, 68, 68, 0.5);\n transition: all 0.2s ease;\n overflow: hidden").concat(important, ";\n padding: 0").concat(important, ";\n margin: 0").concat(important, ";\n box-sizing: border-box").concat(important, ";\n }\n\n .mobile-end-call-btn svg {\n display: block").concat(important, ";\n visibility: visible").concat(important, ";\n opacity: 1").concat(important, ";\n width: 15px").concat(important, ";\n height: 15px").concat(important, ";\n margin: 0").concat(important, ";\n padding: 0").concat(important, ";\n position: relative").concat(important, ";\n }\n\n /* ID-specific selectors for WordPress compatibility */\n #mobileMuteBtn.mobile-control-btn,\n #mobileSpeakerBtn.mobile-control-btn,\n #mobileEndCallBtn.mobile-end-call-btn {\n border-radius: 50% !important;\n overflow: hidden !important;\n }\n\n #mobileMuteBtn.mobile-control-btn,\n #mobileSpeakerBtn.mobile-control-btn {\n width: 32px !important;\n height: 32px !important;\n min-width: 32px !important;\n min-height: 32px !important;\n max-width: 32px !important;\n max-height: 32px !important;\n }\n\n #mobileEndCallBtn.mobile-end-call-btn {\n width: 36px !important;\n height: 36px !important;\n min-width: 36px !important;\n min-height: 36px !important;\n max-width: 36px !important;\n max-height: 36px !important;\n }\n\n #mobileMuteBtn.mobile-control-btn svg,\n #mobileSpeakerBtn.mobile-control-btn svg,\n #mobileEndCallBtn.mobile-end-call-btn svg {\n display: block !important;\n visibility: visible !important;\n opacity: 1 !important;\n width: 15px !important;\n height: 15px !important;\n margin: 0 !important;\n padding: 0 !important;\n }\n\n .mobile-end-call-btn:hover {\n transform: scale(1.05);\n }\n\n .mobile-transcript-row {\n width: 100%;\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 10px 12px;\n background: rgba(255,255,255,0.18);\n border-radius: 14px;\n border: none;\n cursor: pointer;\n text-align: left;\n backdrop-filter: blur(4px);\n transition: background 0.2s ease;\n }\n\n .mobile-transcript-row:hover {\n background: rgba(255,255,255,0.25);\n }\n\n .mobile-transcript-text {\n margin: 0;\n font-size: 13px;\n color: #fff;\n line-height: 1.4;\n font-weight: 500;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n }\n\n .mobile-transcript-footer {\n display: flex;\n align-items: center;\n justify-content: space-between;\n }\n\n .mobile-expand-hint {\n font-size: 10px;\n color: rgba(255,255,255,0.6);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n font-weight: 600;\n }\n\n .mobile-transcript-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: rgba(0,0,0,0.6);\n display: flex;\n align-items: flex-end;\n padding: 12px;\n z-index: 100000;\n pointer-events: auto;\n }\n\n .mobile-expanded-transcript {\n width: 100%;\n max-height: 75%;\n background: #fff;\n border-radius: 24px 24px 16px 16px;\n overflow: hidden;\n box-shadow: 0 -8px 40px rgba(0,0,0,0.3);\n display: flex;\n flex-direction: column;\n }\n\n .mobile-transcript-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 14px 16px;\n background: linear-gradient(135deg, #7c3aed 0%, #a855f7 100%);\n flex-shrink: 0;\n }\n\n .mobile-header-left {\n display: flex;\n flex-direction: column;\n gap: 2px;\n }\n\n .mobile-header-status {\n display: flex;\n align-items: center;\n gap: 6px;\n }\n\n .mobile-header-status-dot {\ndisplay: block !important;\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background-color: #22c55e;\n box-shadow: 0 0 6px #22c55e;\n }\n\n .mobile-header-status-text {\n font-size: 10px;\n font-weight: 700;\n color: rgba(255,255,255,0.9);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n }\n\n .mobile-header-duration {\n font-size: 10px;\n font-weight: 600;\n color: rgba(255,255,255,0.7);\n margin-left: 8px;\n }\n\n .mobile-transcript-label {\n font-size: 11px;\n font-weight: 800;\n color: rgba(255,255,255,0.7);\n letter-spacing: 1px;\n }\n\n .mobile-close-transcript-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n border-radius: 50%;\n border: none;\n background: rgba(255,255,255,0.2);\n color: #fff;\n cursor: pointer;\n }\n\n .mobile-close-transcript-btn:hover {\n background: rgba(255,255,255,0.3);\n }\n\n .mobile-messages-container {\n flex: 1;\n padding: 16px;\n overflow-y: auto;\n display: flex;\n flex-direction: column;\n gap: 12px;\n }\n\n .mobile-message-bubble {\n max-width: 85%;\n padding: 12px 16px;\n border-radius: 18px;\n font-size: 14px;\n line-height: 1.5;\n word-wrap: break-word;\n }\n\n .mobile-message-bubble.assistant {\n align-self: flex-start;\n background-color: #f3f4f6;\n color: #374151;\n border-bottom-left-radius: 4px;\n }\n\n .mobile-message-bubble.user {\n align-self: flex-end;\n background: linear-gradient(135deg, #7c3aed 0%, #a855f7 100%);\n color: #fff;\n border-bottom-right-radius: 4px;\n }\n\n .mobile-input-area {\n padding: 12px 16px 16px;\n border-top: 1px solid #e5e7eb;\n background: #f9fafb;\n flex-shrink: 0;\n }\n\n .mobile-input-wrapper {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 4px 4px 4px 16px;\n background: #fff;\n border-radius: 24px;\n border: 2px solid #e5e7eb;\n }\n\n .mobile-text-input {\n flex: 1;\n border: none;\n outline: none;\n font-size: 14px;\n color: #374151;\n background: transparent;\n padding: 10px 0;\n }\n\n .mobile-text-input::placeholder {\n color: #9ca3af;\n }\n\n .mobile-send-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 40px;\n height: 40px;\n border-radius: 50%;\n border: none;\n background: linear-gradient(135deg, #7c3aed 0%, #a855f7 100%);\n color: #fff;\n cursor: pointer;\n transition: all 0.2s ease;\n }\n\n .mobile-send-btn:hover:not(:disabled) {\n transform: scale(1.05);\n }\n\n .mobile-send-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .mobile-input-hint {\n margin: 8px 0 0;\n font-size: 11px;\n color: #9ca3af;\n text-align: center;\n transition: color 0.2s ease;\n }\n\n /* Mobile text input status states */\n .mobile-input-hint--sending {\n color: #666;\n }\n\n .mobile-input-hint--success {\n color: #28a745;\n }\n\n .mobile-input-hint--queued {\n color: #ffc107;\n }\n\n .mobile-input-hint--error {\n color: #dc3545;\n }\n\n /* Disabled mobile input styling */\n #mobileTextInput:disabled {\n background-color: #f5f5f5;\n cursor: not-allowed;\n }\n\n #mobileSendBtn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n ");
28470
+ return "\n .mobile-duration-dot {\ndisplay: block !important;\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background-color: #ef4444;\n animation: mobilePulse 1.5s ease-in-out infinite;\n }\n\n @keyframes mobilePulse {\n 0%, 100% { opacity: 1; transform: scale(1); }\n 50% { opacity: 0.5; transform: scale(0.95); }\n }\n\n .mobile-voice-bar {\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding: 14px;\n background: linear-gradient(135deg, #7c3aed 0%, #a855f7 50%, #c084fc 100%);\n border-radius: 24px 24px 0 0;\n box-shadow: 0 -8px 32px rgba(124, 58, 237, 0.5), 0 -2px 8px rgba(0,0,0,0.3), inset 0 1px 0 rgba(255,255,255,0.2);\n width: 100% !important;\n max-width: 100% !important;\n pointer-events: auto;\n overflow: hidden !important;\n margin: 0 !important;\n }\n\n .mobile-top-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n position: relative;\n }\n\n .mobile-status-indicator {\n display: flex;\n align-items: center;\n gap: 5px;\n min-width: 85px;\n }\n\n .mobile-status-dot {\ndisplay: block !important;\n width: 7px;\n height: 7px;\n border-radius: 50%;\n background-color: #22c55e;\n box-shadow: 0 0 8px #22c55e;\n transition: all 0.3s ease;\n }\n\n .mobile-status-text {\n font-size: 11px;\n font-weight: 700;\n color: rgba(255,255,255,0.95);\n text-transform: uppercase;\n letter-spacing: 0.3px;\n }\n\n .mobile-waveform-center {\n position: absolute;\n left: 50%;\n transform: translateX(-50%);\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 3px;\n height: 24px;\n }\n\n .mobile-waveform-bar {\ndisplay: block !important;\n width: 4px;\n height: 4px;\n background-color: #fff;\n border-radius: 2px;\n transition: height 0.15s ease;\n box-shadow: 0 0 8px rgba(255,255,255,0.5);\n }\n\n .mobile-controls {\n display: flex;\n align-items: center;\n gap: 6px;\n }\n\n .mobile-control-btn {\n display: flex".concat(important, ";\n align-items: center").concat(important, ";\n justify-content: center").concat(important, ";\n width: 32px").concat(important, ";\n height: 32px").concat(important, ";\n min-width: 32px").concat(important, ";\n min-height: 32px").concat(important, ";\n max-width: 32px").concat(important, ";\n max-height: 32px").concat(important, ";\n border-radius: 50%").concat(important, ";\n border: none").concat(important, ";\n cursor: pointer").concat(important, ";\n transition: all 0.2s ease;\n background-color: rgba(255,255,255,0.15)").concat(important, ";\n position: relative").concat(important, ";\n overflow: hidden").concat(important, ";\n padding: 0").concat(important, ";\n margin: 0").concat(important, ";\n box-sizing: border-box").concat(important, ";\n }\n\n .mobile-control-btn:hover {\n background-color: rgba(255,255,255,0.25)").concat(important, ";\n }\n\n .mobile-control-btn.muted {\n background-color: rgba(239, 68, 68, 0.3)").concat(important, ";\n }\n\n .mobile-control-btn svg {\n position: absolute").concat(important, ";\n top: 50%").concat(important, ";\n left: 50%").concat(important, ";\n transform: translate(-50%, -50%)").concat(important, ";\n display: block").concat(important, ";\n visibility: visible").concat(important, ";\n opacity: 1").concat(important, ";\n width: 15px").concat(important, ";\n height: 15px").concat(important, ";\n margin: 0").concat(important, ";\n padding: 0").concat(important, ";\n }\n\n .mobile-mic-off-icon,\n .mobile-speaker-off-icon {\n display: none !important;\n }\n\n .mobile-control-btn.muted .mobile-mic-icon,\n .mobile-control-btn.muted .mobile-speaker-icon {\n display: none !important;\n }\n\n .mobile-control-btn.muted .mobile-mic-off-icon,\n .mobile-control-btn.muted .mobile-speaker-off-icon {\n display: block !important;\n }\n\n .mobile-end-call-btn {\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n justify-content: center").concat(important, ";\n width: 36px").concat(important, ";\n height: 36px").concat(important, ";\n min-width: 36px").concat(important, ";\n min-height: 36px").concat(important, ";\n max-width: 36px").concat(important, ";\n max-height: 36px").concat(important, ";\n border-radius: 50%").concat(important, ";\n border: none").concat(important, ";\n cursor: pointer").concat(important, ";\n background-color: #ef4444").concat(important, ";\n box-shadow: 0 4px 14px rgba(239, 68, 68, 0.5);\n transition: all 0.2s ease;\n overflow: hidden").concat(important, ";\n padding: 0").concat(important, ";\n margin: 0").concat(important, ";\n box-sizing: border-box").concat(important, ";\n }\n\n .mobile-end-call-btn svg {\n display: block").concat(important, ";\n visibility: visible").concat(important, ";\n opacity: 1").concat(important, ";\n width: 15px").concat(important, ";\n height: 15px").concat(important, ";\n margin: 0").concat(important, ";\n padding: 0").concat(important, ";\n position: relative").concat(important, ";\n }\n\n /* ID-specific selectors for WordPress compatibility */\n #mobileMuteBtn.mobile-control-btn,\n #mobileSpeakerBtn.mobile-control-btn,\n #mobileEndCallBtn.mobile-end-call-btn {\n border-radius: 50% !important;\n overflow: hidden !important;\n }\n\n #mobileMuteBtn.mobile-control-btn,\n #mobileSpeakerBtn.mobile-control-btn {\n width: 32px !important;\n height: 32px !important;\n min-width: 32px !important;\n min-height: 32px !important;\n max-width: 32px !important;\n max-height: 32px !important;\n }\n\n #mobileEndCallBtn.mobile-end-call-btn {\n width: 36px !important;\n height: 36px !important;\n min-width: 36px !important;\n min-height: 36px !important;\n max-width: 36px !important;\n max-height: 36px !important;\n }\n\n #mobileMuteBtn.mobile-control-btn svg,\n #mobileSpeakerBtn.mobile-control-btn svg,\n #mobileEndCallBtn.mobile-end-call-btn svg {\n display: block !important;\n visibility: visible !important;\n opacity: 1 !important;\n width: 15px !important;\n height: 15px !important;\n margin: 0 !important;\n padding: 0 !important;\n }\n\n .mobile-end-call-btn:hover {\n transform: scale(1.05);\n }\n\n .mobile-transcript-row {\n width: 100%;\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 10px 12px;\n background: rgba(255,255,255,0.18);\n border-radius: 14px;\n border: none;\n cursor: pointer;\n text-align: left;\n backdrop-filter: blur(4px);\n transition: background 0.2s ease;\n }\n\n .mobile-transcript-row:hover {\n background: rgba(255,255,255,0.25);\n }\n\n .mobile-transcript-text {\n margin: 0;\n font-size: 13px;\n color: #fff;\n line-height: 1.4;\n font-weight: 500;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n }\n\n .mobile-transcript-footer {\n display: flex;\n align-items: center;\n justify-content: space-between;\n }\n\n .mobile-expand-hint {\n font-size: 10px;\n color: rgba(255,255,255,0.6);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n font-weight: 600;\n }\n\n .mobile-transcript-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: rgba(0,0,0,0.6);\n display: flex;\n align-items: flex-end;\n padding: 12px;\n z-index: 100000;\n pointer-events: auto;\n }\n\n .mobile-expanded-transcript {\n width: 100%;\n max-height: 75%;\n background: #fff;\n border-radius: 24px 24px 16px 16px;\n overflow: hidden;\n box-shadow: 0 -8px 40px rgba(0,0,0,0.3);\n display: flex;\n flex-direction: column;\n }\n\n .mobile-transcript-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 14px 16px;\n background: linear-gradient(135deg, #7c3aed 0%, #a855f7 100%);\n flex-shrink: 0;\n }\n\n .mobile-header-left {\n display: flex;\n flex-direction: column;\n gap: 2px;\n }\n\n .mobile-header-status {\n display: flex;\n align-items: center;\n gap: 6px;\n }\n\n .mobile-header-status-dot {\ndisplay: block !important;\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background-color: #22c55e;\n box-shadow: 0 0 6px #22c55e;\n }\n\n .mobile-header-status-text {\n font-size: 10px;\n font-weight: 700;\n color: rgba(255,255,255,0.9);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n }\n\n .mobile-header-duration {\n font-size: 10px;\n font-weight: 600;\n color: rgba(255,255,255,0.7);\n margin-left: 8px;\n }\n\n .mobile-transcript-label {\n font-size: 11px;\n font-weight: 800;\n color: rgba(255,255,255,0.7);\n letter-spacing: 1px;\n }\n\n .mobile-close-transcript-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n border-radius: 50%;\n border: none;\n background: rgba(255,255,255,0.2);\n color: #fff;\n cursor: pointer;\n }\n\n .mobile-close-transcript-btn:hover {\n background: rgba(255,255,255,0.3);\n }\n\n .mobile-messages-container {\n flex: 1;\n padding: 16px;\n overflow-y: auto;\n display: flex;\n flex-direction: column;\n gap: 12px;\n }\n\n .mobile-message-bubble {\n max-width: 85%;\n padding: 12px 16px;\n border-radius: 18px;\n font-size: 14px;\n line-height: 1.5;\n word-wrap: break-word;\n }\n\n .mobile-message-bubble.assistant {\n align-self: flex-start;\n background-color: #f3f4f6;\n color: #374151;\n border-bottom-left-radius: 4px;\n }\n\n .mobile-message-bubble.user {\n align-self: flex-end;\n background: linear-gradient(135deg, #7c3aed 0%, #a855f7 100%);\n color: #fff;\n border-bottom-right-radius: 4px;\n }\n\n .mobile-input-area {\n padding: 12px 16px 16px;\n border-top: 1px solid #e5e7eb;\n background: #f9fafb;\n flex-shrink: 0;\n }\n\n .mobile-input-wrapper {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 4px 4px 4px 16px;\n background: #fff;\n border-radius: 24px;\n border: 2px solid #e5e7eb;\n }\n\n .mobile-text-input {\n flex: 1;\n border: none;\n outline: none;\n font-size: 14px;\n color: #374151;\n background: transparent;\n padding: 10px 0;\n }\n\n .mobile-text-input::placeholder {\n color: #9ca3af;\n }\n\n .mobile-send-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 40px;\n height: 40px;\n border-radius: 50%;\n border: none;\n background: linear-gradient(135deg, #7c3aed 0%, #a855f7 100%);\n color: #fff;\n cursor: pointer;\n transition: all 0.2s ease;\n }\n\n .mobile-send-btn:hover:not(:disabled) {\n transform: scale(1.05);\n }\n\n .mobile-send-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .mobile-input-hint {\n margin: 8px 0 0;\n font-size: 11px;\n color: #9ca3af;\n text-align: center;\n transition: color 0.2s ease;\n }\n\n /* Mobile text input status states */\n .mobile-input-hint--sending {\n color: #666;\n }\n\n .mobile-input-hint--success {\n color: #28a745;\n }\n\n .mobile-input-hint--queued {\n color: #ffc107;\n }\n\n .mobile-input-hint--error {\n color: #dc3545;\n }\n\n /* Disabled mobile input styling */\n #mobileTextInput:disabled {\n background-color: #f5f5f5;\n cursor: not-allowed;\n }\n\n #mobileSendBtn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n ");
27358
28471
  }
27359
28472
 
27360
28473
  /**
@@ -27364,7 +28477,7 @@ var Styles = /*#__PURE__*/function () {
27364
28477
  key: "generateMobileCSS",
27365
28478
  value: function generateMobileCSS() {
27366
28479
  var important = this.config.useShadowDOM === false ? ' !important' : '';
27367
- return "\n /* Mobile Voice Call UI - Container positioning */\n #mobile-voice-call-bar-container {\n position: fixed;\n bottom: 0;\n left: 0;\n right: 0;\n z-index: 99999;\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;\n padding: 0 12px 12px;\n pointer-events: none;\n }\n\n .mobile-duration-badge {\n position: absolute;\n top: -28px;\n left: 50%;\n transform: translateX(-50%);\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 5px 14px;\n background: rgba(0,0,0,0.75);\n border-radius: 20px;\n font-size: 12px;\n font-weight: 700;\n color: #fff;\n backdrop-filter: blur(8px);\n z-index: 10;\n pointer-events: auto;\n }\n\n /* Shared mobile styles */\n ".concat(this._getSharedMobileCSS(important), "\n ");
28480
+ return "\n /* Mobile Voice Call UI - Container positioning */\n #mobile-voice-call-bar-container {\n position: fixed !important;\n bottom: 0 !important;\n left: 0 !important;\n right: 0 !important;\n z-index: 99999 !important;\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif !important;\n padding: 0 12px 12px !important;\n pointer-events: none;\n box-sizing: border-box !important;\n width: 100% !important;\n max-width: 100vw !important;\n margin: 0 !important;\n overflow: visible !important;\n }\n\n #mobile-voice-call-bar-container *,\n #mobile-voice-call-bar-container *::before,\n #mobile-voice-call-bar-container *::after {\n box-sizing: border-box !important;\n }\n\n .mobile-duration-badge {\n position: absolute;\n top: -28px;\n left: 50%;\n transform: translateX(-50%);\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 5px 14px;\n background: rgba(0,0,0,0.75);\n border-radius: 20px;\n font-size: 12px;\n font-weight: 700;\n color: #fff;\n backdrop-filter: blur(8px);\n z-index: 10;\n pointer-events: auto;\n }\n\n /* \u2500\u2500 Mobile Cart Row \u2500\u2500 */\n\n .mobile-cart-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 7px 16px;\n background: rgba(255, 255, 255, 0.08);\n border-top: 1px solid rgba(255, 255, 255, 0.1);\n border-radius: 0 0 16px 16px;\n animation: mobileCartRowIn 0.25s ease-out;\n pointer-events: auto;\n }\n\n @keyframes mobileCartRowIn {\n from { opacity: 0; max-height: 0; padding-top: 0; padding-bottom: 0; }\n to { opacity: 1; max-height: 50px; padding-top: 7px; padding-bottom: 7px; }\n }\n\n .mobile-cart-row__content {\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .mobile-cart-row__icon {\n font-size: 13px;\n }\n\n .mobile-cart-row__text {\n font-size: 12px;\n font-weight: 600;\n color: rgba(255, 255, 255, 0.9);\n letter-spacing: 0.2px;\n }\n\n /* Shared mobile styles */\n ".concat(this._getSharedMobileCSS(important), "\n ");
27368
28481
  }
27369
28482
  }]);
27370
28483
  }();
@@ -27692,6 +28805,14 @@ var TextHandlers = /*#__PURE__*/function () {
27692
28805
  input.value = '';
27693
28806
  if (sendBtn) sendBtn.disabled = true;
27694
28807
 
28808
+ // Stop any currently playing audio and clear the queue before sending
28809
+ try {
28810
+ var voiceSDK = this.voiceInterface.sdk.voiceSDK;
28811
+ if (voiceSDK !== null && voiceSDK !== void 0 && voiceSDK.stopAudioPlayback) {
28812
+ voiceSDK.stopAudioPlayback();
28813
+ }
28814
+ } catch (e) {/* best-effort */}
28815
+
27695
28816
  // Show sending status
27696
28817
  this.showTextInputStatus(isMobile ? 'mobile' : 'desktop', 'sending', 'Sending...');
27697
28818