@chatwidgetai/chat-widget 0.3.6 → 0.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai-chat-widget.umd.js +1562 -1380
- package/dist/ai-chat-widget.umd.js.map +1 -1
- package/dist/api/client.d.ts +12 -2
- package/dist/api/client.d.ts.map +1 -1
- package/dist/components/ChatWidget/hooks/useWidgetAppearance.d.ts +37 -0
- package/dist/components/ChatWidget/hooks/useWidgetAppearance.d.ts.map +1 -0
- package/dist/components/ChatWidget/icons.d.ts +2 -0
- package/dist/components/ChatWidget/icons.d.ts.map +1 -0
- package/dist/components/ChatWidget/parts/WidgetTriggers.d.ts +25 -0
- package/dist/components/ChatWidget/parts/WidgetTriggers.d.ts.map +1 -0
- package/dist/components/ChatWidget.d.ts.map +1 -1
- package/dist/components/ChatWindow.d.ts +4 -1
- package/dist/components/ChatWindow.d.ts.map +1 -1
- package/dist/components/WelcomeBubble.d.ts +12 -0
- package/dist/components/WelcomeBubble.d.ts.map +1 -0
- package/dist/components/icons.d.ts +21 -0
- package/dist/components/icons.d.ts.map +1 -0
- package/dist/components/triggers/ButtonTrigger.d.ts +11 -0
- package/dist/components/triggers/ButtonTrigger.d.ts.map +1 -0
- package/dist/components/triggers/InputBarTrigger.d.ts +18 -0
- package/dist/components/triggers/InputBarTrigger.d.ts.map +1 -0
- package/dist/components/triggers/PillTrigger.d.ts +12 -0
- package/dist/components/triggers/PillTrigger.d.ts.map +1 -0
- package/dist/components/triggers/index.d.ts +8 -0
- package/dist/components/triggers/index.d.ts.map +1 -0
- package/dist/hooks/useChat/action-handler.d.ts +17 -1
- package/dist/hooks/useChat/action-handler.d.ts.map +1 -1
- package/dist/hooks/useChat/index.d.ts +5 -0
- package/dist/hooks/useChat/index.d.ts.map +1 -1
- package/dist/hooks/useChat/message-hydration.d.ts.map +1 -1
- package/dist/hooks/useChat/stream-buffer.d.ts +18 -0
- package/dist/hooks/useChat/stream-buffer.d.ts.map +1 -0
- package/dist/hooks/useChat/stream-handlers.d.ts +3 -3
- package/dist/hooks/useChat/stream-handlers.d.ts.map +1 -1
- package/dist/hooks/useChat/stream-state.d.ts.map +1 -1
- package/dist/hooks/useChat/types.d.ts +2 -0
- package/dist/hooks/useChat/types.d.ts.map +1 -1
- package/dist/index.esm.js +1562 -1380
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1562 -1380
- package/dist/index.js.map +1 -1
- package/dist/types/index.d.ts +5 -92
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -210,58 +210,83 @@ class WidgetApiClient {
|
|
|
210
210
|
return typeof data === 'object' && data !== null && 'type' in data;
|
|
211
211
|
});
|
|
212
212
|
}
|
|
213
|
-
async *
|
|
213
|
+
async *dismissAgentMessageStream(conversationId, toolCallId, signal) {
|
|
214
214
|
const headers = {
|
|
215
215
|
'Content-Type': 'application/json',
|
|
216
216
|
};
|
|
217
217
|
if (this.config.currentRoute) {
|
|
218
218
|
headers['X-Current-Route'] = this.config.currentRoute;
|
|
219
219
|
}
|
|
220
|
-
const response = await fetch(`${this.config.apiUrl}/api/widget/${this.config.widgetId}/agent/
|
|
220
|
+
const response = await fetch(`${this.config.apiUrl}/api/widget/${this.config.widgetId}/agent/dismiss`, {
|
|
221
221
|
method: 'POST',
|
|
222
222
|
headers,
|
|
223
223
|
body: JSON.stringify({
|
|
224
224
|
conversationId: conversationId,
|
|
225
225
|
toolCallId,
|
|
226
|
-
|
|
227
|
-
timeZone: this.getTimeZone(),
|
|
226
|
+
reason: "user",
|
|
228
227
|
}),
|
|
229
228
|
signal,
|
|
230
229
|
});
|
|
230
|
+
if (response.status === 204) {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
231
233
|
if (!response.ok) {
|
|
232
|
-
throw await buildApiError(response, 'Failed to
|
|
234
|
+
throw await buildApiError(response, 'Failed to dismiss action');
|
|
233
235
|
}
|
|
234
236
|
yield* parseSSEStream(response, (data) => {
|
|
235
237
|
return typeof data === 'object' && data !== null && 'type' in data;
|
|
236
238
|
});
|
|
237
239
|
}
|
|
238
|
-
|
|
240
|
+
/**
|
|
241
|
+
* Continue agent after halting action completes
|
|
242
|
+
* Call this when frontend completes an action and wants to pass result back to agent
|
|
243
|
+
*/
|
|
244
|
+
async *continueAgentAction(conversationId, toolCallId, body, signal) {
|
|
239
245
|
const headers = {
|
|
240
246
|
'Content-Type': 'application/json',
|
|
241
247
|
};
|
|
242
248
|
if (this.config.currentRoute) {
|
|
243
249
|
headers['X-Current-Route'] = this.config.currentRoute;
|
|
244
250
|
}
|
|
245
|
-
const response = await fetch(`${this.config.apiUrl}/api/widget/${this.config.widgetId}/agent/
|
|
251
|
+
const response = await fetch(`${this.config.apiUrl}/api/widget/${this.config.widgetId}/agent/continue`, {
|
|
246
252
|
method: 'POST',
|
|
247
253
|
headers,
|
|
248
254
|
body: JSON.stringify({
|
|
249
|
-
conversationId
|
|
255
|
+
conversationId,
|
|
250
256
|
toolCallId,
|
|
251
|
-
|
|
257
|
+
body,
|
|
258
|
+
timeZone: this.getTimeZone(),
|
|
252
259
|
}),
|
|
253
260
|
signal,
|
|
254
261
|
});
|
|
255
|
-
if (response.status === 204) {
|
|
256
|
-
return;
|
|
257
|
-
}
|
|
258
262
|
if (!response.ok) {
|
|
259
|
-
throw await buildApiError(response, 'Failed to
|
|
263
|
+
throw await buildApiError(response, 'Failed to continue agent action');
|
|
260
264
|
}
|
|
261
265
|
yield* parseSSEStream(response, (data) => {
|
|
262
266
|
return typeof data === 'object' && data !== null && 'type' in data;
|
|
263
267
|
});
|
|
264
268
|
}
|
|
269
|
+
/**
|
|
270
|
+
* NEW: Call action endpoint (frontend-owned flow)
|
|
271
|
+
* Used for multi-step actions like booking appointments
|
|
272
|
+
*/
|
|
273
|
+
async callActionEndpoint(actionId, endpoint, input, token) {
|
|
274
|
+
const headers = {
|
|
275
|
+
'Content-Type': 'application/json',
|
|
276
|
+
};
|
|
277
|
+
if (token) {
|
|
278
|
+
headers['Authorization'] = `Bearer ${token}`;
|
|
279
|
+
}
|
|
280
|
+
const response = await fetch(`${this.config.apiUrl}/api/widget/${this.config.widgetId}/actions/${actionId}/${endpoint}`, {
|
|
281
|
+
method: 'POST',
|
|
282
|
+
headers,
|
|
283
|
+
body: JSON.stringify(input),
|
|
284
|
+
});
|
|
285
|
+
if (!response.ok) {
|
|
286
|
+
throw await buildApiError(response, `Action endpoint '${endpoint}' failed`);
|
|
287
|
+
}
|
|
288
|
+
return response.json();
|
|
289
|
+
}
|
|
265
290
|
/**
|
|
266
291
|
* Submit feedback for a message
|
|
267
292
|
*/
|
|
@@ -292,7 +317,8 @@ class WidgetApiClient {
|
|
|
292
317
|
}
|
|
293
318
|
/**
|
|
294
319
|
* Generate follow-up suggestions based on conversation context and available actions.
|
|
295
|
-
*
|
|
320
|
+
* @deprecated Follow-ups are now generated server-side in parallel and included in the done event.
|
|
321
|
+
* This method is kept for backwards compatibility but is no longer called by the widget.
|
|
296
322
|
*/
|
|
297
323
|
async generateFollowUps(messages, actionIds) {
|
|
298
324
|
try {
|
|
@@ -27160,7 +27186,7 @@ function TypingIndicator({ className = '' }) {
|
|
|
27160
27186
|
}
|
|
27161
27187
|
|
|
27162
27188
|
// Styles are provided by global messages.css - no component-specific CSS needed
|
|
27163
|
-
function ChevronDownIcon() {
|
|
27189
|
+
function ChevronDownIcon$1() {
|
|
27164
27190
|
return (jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: jsx("polyline", { points: "6 9 12 15 18 9" }) }));
|
|
27165
27191
|
}
|
|
27166
27192
|
function ScrollButton({ onClick, visible, className = '' }) {
|
|
@@ -27169,7 +27195,7 @@ function ScrollButton({ onClick, visible, className = '' }) {
|
|
|
27169
27195
|
visible && 'visible',
|
|
27170
27196
|
className,
|
|
27171
27197
|
].filter(Boolean).join(' ');
|
|
27172
|
-
return (jsx("button", { type: "button", className: classes, onClick: onClick, "aria-label": "Scroll to bottom", children: jsx(ChevronDownIcon, {}) }));
|
|
27198
|
+
return (jsx("button", { type: "button", className: classes, onClick: onClick, "aria-label": "Scroll to bottom", children: jsx(ChevronDownIcon$1, {}) }));
|
|
27173
27199
|
}
|
|
27174
27200
|
|
|
27175
27201
|
const formatToolName = (name) => {
|
|
@@ -27181,13 +27207,23 @@ const formatToolName = (name) => {
|
|
|
27181
27207
|
.join(' ');
|
|
27182
27208
|
};
|
|
27183
27209
|
function ToolIndicator({ badges, className = '' }) {
|
|
27184
|
-
|
|
27210
|
+
// Consolidate multiple badges into a single status indicator
|
|
27211
|
+
const hasLoading = badges.some(b => b.status === 'loading');
|
|
27212
|
+
const hasError = badges.some(b => b.status === 'error');
|
|
27213
|
+
const status = hasLoading ? 'loading' : hasError ? 'error' : 'completed';
|
|
27214
|
+
// Show only the most relevant badge name, or count if multiple
|
|
27215
|
+
const displayName = badges.length === 1
|
|
27216
|
+
? formatToolName(badges[0].name)
|
|
27217
|
+
: badges.length > 1
|
|
27218
|
+
? `${badges.length} actions`
|
|
27219
|
+
: 'Processing';
|
|
27220
|
+
return (jsx("div", { className: `ai-chat-tool-row ${className}`, children: jsx("div", { className: "ai-chat-tool-badges", children: jsx("div", { className: `ai-chat-tool-badge ${status}`, children: jsx("span", { className: "tool-name", children: displayName }) }) }) }));
|
|
27185
27221
|
}
|
|
27186
27222
|
|
|
27187
27223
|
// SVG Icon components
|
|
27188
27224
|
const ThumbsUpIcon = () => (jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: jsx("path", { d: "M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3" }) }));
|
|
27189
27225
|
const ThumbsDownIcon = () => (jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: jsx("path", { d: "M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17" }) }));
|
|
27190
|
-
const CheckIcon$
|
|
27226
|
+
const CheckIcon$1 = () => (jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: jsx("polyline", { points: "20 6 9 17 4 12" }) }));
|
|
27191
27227
|
const FeedbackButtons = ({ messageId, currentFeedback, onFeedback, }) => {
|
|
27192
27228
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
27193
27229
|
const [submitted, setSubmitted] = useState(false);
|
|
@@ -27208,7 +27244,7 @@ const FeedbackButtons = ({ messageId, currentFeedback, onFeedback, }) => {
|
|
|
27208
27244
|
setIsSubmitting(false);
|
|
27209
27245
|
}
|
|
27210
27246
|
};
|
|
27211
|
-
return (jsxs("div", { className: `ai-chat-feedback ${submitted ? 'submitted' : ''}`, children: [jsxs("div", { className: "ai-chat-feedback-buttons", children: [jsx("button", { className: `ai-chat-feedback-button ${currentFeedback === 'positive' ? 'active' : ''}`, onClick: () => handleFeedback('positive'), disabled: isDisabled, "aria-label": "Helpful", title: "This was helpful", children: jsx(ThumbsUpIcon, {}) }), jsx("button", { className: `ai-chat-feedback-button ${currentFeedback === 'negative' ? 'active' : ''}`, onClick: () => handleFeedback('negative'), disabled: isDisabled, "aria-label": "Not helpful", title: "This was not helpful", children: jsx(ThumbsDownIcon, {}) })] }), submitted && (jsxs("div", { className: "ai-chat-feedback-message", "aria-live": "polite", children: [jsx("span", { className: "ai-chat-feedback-checkmark", children: jsx(CheckIcon$
|
|
27247
|
+
return (jsxs("div", { className: `ai-chat-feedback ${submitted ? 'submitted' : ''}`, children: [jsxs("div", { className: "ai-chat-feedback-buttons", children: [jsx("button", { className: `ai-chat-feedback-button ${currentFeedback === 'positive' ? 'active' : ''}`, onClick: () => handleFeedback('positive'), disabled: isDisabled, "aria-label": "Helpful", title: "This was helpful", children: jsx(ThumbsUpIcon, {}) }), jsx("button", { className: `ai-chat-feedback-button ${currentFeedback === 'negative' ? 'active' : ''}`, onClick: () => handleFeedback('negative'), disabled: isDisabled, "aria-label": "Not helpful", title: "This was not helpful", children: jsx(ThumbsDownIcon, {}) })] }), submitted && (jsxs("div", { className: "ai-chat-feedback-message", "aria-live": "polite", children: [jsx("span", { className: "ai-chat-feedback-checkmark", children: jsx(CheckIcon$1, {}) }), jsx("span", { className: "ai-chat-feedback-text", children: "Thanks for feedback" })] }))] }));
|
|
27212
27248
|
};
|
|
27213
27249
|
|
|
27214
27250
|
const Message = ({ message, showTimestamp = true, enableFeedback = true, onFeedback, }) => {
|
|
@@ -27237,9 +27273,11 @@ const Message = ({ message, showTimestamp = true, enableFeedback = true, onFeedb
|
|
|
27237
27273
|
const hasContent = aiContent.trim().length > 0;
|
|
27238
27274
|
if (!hasContent)
|
|
27239
27275
|
return null;
|
|
27240
|
-
|
|
27276
|
+
// Only show timestamp and feedback when message is complete (not streaming)
|
|
27277
|
+
const isComplete = !message.isStreaming;
|
|
27278
|
+
return (jsxs("div", { className: `ai-chat-message assistant ${isError ? 'error' : ''} ${message.isStreaming ? 'streaming' : ''}`, children: [jsx("div", { className: "ai-chat-message-content", children: jsx(Markdown, { remarkPlugins: [remarkGfm], components: {
|
|
27241
27279
|
table: ({ children, ...props }) => (jsx("div", { className: "table-wrapper", children: jsx("div", { className: "table-scroll", children: jsx("table", { ...props, children: children }) }) })),
|
|
27242
|
-
}, children: aiContent }) }), showTimestamp && (jsxs("div", { className: "ai-chat-message-meta", children: [jsx("span", { className: "ai-chat-message-timestamp", children: formatTime(message.timestamp) }), enableFeedback && onFeedback && (jsx(FeedbackButtons, { messageId: message.id, currentFeedback: message.feedback, onFeedback: onFeedback }))] }))] }));
|
|
27280
|
+
}, children: aiContent }) }), showTimestamp && isComplete && (jsxs("div", { className: "ai-chat-message-meta", children: [jsx("span", { className: "ai-chat-message-timestamp", children: formatTime(message.timestamp) }), enableFeedback && onFeedback && (jsx(FeedbackButtons, { messageId: message.id, currentFeedback: message.feedback, onFeedback: onFeedback }))] }))] }));
|
|
27243
27281
|
}
|
|
27244
27282
|
// System message rendering
|
|
27245
27283
|
if (isSystem) {
|
|
@@ -27259,7 +27297,7 @@ function isActionComplete(state) {
|
|
|
27259
27297
|
return false;
|
|
27260
27298
|
const TERMINAL_STATUSES = [
|
|
27261
27299
|
'completed', 'booked', 'scheduled', 'cancelled', 'failed', 'error',
|
|
27262
|
-
'displaying', 'clicked', 'contacted', 'submitted', 'sent'
|
|
27300
|
+
'displaying', 'displayed', 'clicked', 'contacted', 'submitted', 'sent'
|
|
27263
27301
|
];
|
|
27264
27302
|
const status = state.status;
|
|
27265
27303
|
if (typeof status === 'string' && TERMINAL_STATUSES.includes(status)) {
|
|
@@ -27278,10 +27316,10 @@ function isActionLoading(message) {
|
|
|
27278
27316
|
return false;
|
|
27279
27317
|
if (message.isStreaming)
|
|
27280
27318
|
return true;
|
|
27281
|
-
const
|
|
27282
|
-
return !isActionComplete(
|
|
27319
|
+
const input = message.action.input;
|
|
27320
|
+
return !isActionComplete(input);
|
|
27283
27321
|
}
|
|
27284
|
-
const ToolMessageGroup = ({ messages, getActionRenderer, showToolIndicator = true, accentColor, variant, onActionDismiss, }) => {
|
|
27322
|
+
const ToolMessageGroup = ({ messages, getActionRenderer, showToolIndicator = true, accentColor, variant, onActionDismiss, onCallEndpoint, }) => {
|
|
27285
27323
|
const visibleMessages = messages.filter(message => !message.action?.hidden);
|
|
27286
27324
|
const actionMessages = visibleMessages.filter(message => message.action);
|
|
27287
27325
|
// Debug logging
|
|
@@ -27305,7 +27343,7 @@ const ToolMessageGroup = ({ messages, getActionRenderer, showToolIndicator = tru
|
|
|
27305
27343
|
implementation: impl,
|
|
27306
27344
|
hasRenderer: !!renderer,
|
|
27307
27345
|
rendererType: renderer ? typeof renderer : 'undefined',
|
|
27308
|
-
|
|
27346
|
+
input: msg.action?.input,
|
|
27309
27347
|
});
|
|
27310
27348
|
});
|
|
27311
27349
|
// If tool indicator is hidden AND there are no action cards to render, don't render anything
|
|
@@ -27332,7 +27370,7 @@ const ToolMessageGroup = ({ messages, getActionRenderer, showToolIndicator = tru
|
|
|
27332
27370
|
console.log('[ToolMessageGroup] No renderer for:', message.action.implementation);
|
|
27333
27371
|
return null;
|
|
27334
27372
|
}
|
|
27335
|
-
return (jsx("div", { className: "ai-chat-tool-action", children: renderer(message, accentColor, variant, onActionDismiss) }, `action-${message.id}`));
|
|
27373
|
+
return (jsx("div", { className: "ai-chat-tool-action", children: renderer(message, accentColor, variant, onActionDismiss, onCallEndpoint) }, `action-${message.id}`));
|
|
27336
27374
|
})] }));
|
|
27337
27375
|
};
|
|
27338
27376
|
|
|
@@ -27366,12 +27404,29 @@ const SuggestedQuestions = ({ questions, onQuestionClick, }) => {
|
|
|
27366
27404
|
};
|
|
27367
27405
|
|
|
27368
27406
|
const MAX_TEXT_LENGTH = 40;
|
|
27407
|
+
const STAGGER_DELAY_MS = 200; // Delay between each suggestion appearing
|
|
27369
27408
|
const FollowUpSuggestions = ({ suggestions, onQuestionClick, onActionClick, accentColor, }) => {
|
|
27370
|
-
|
|
27371
|
-
return null;
|
|
27372
|
-
}
|
|
27409
|
+
const [visibleIndices, setVisibleIndices] = useState(new Set());
|
|
27373
27410
|
// Filter out empty suggestions
|
|
27374
|
-
const validSuggestions = suggestions
|
|
27411
|
+
const validSuggestions = suggestions?.filter(s => s && s.text && s.text.trim()) || [];
|
|
27412
|
+
// Create a stable key for the current suggestions set
|
|
27413
|
+
const suggestionsKey = validSuggestions.map(s => s.id).join(',');
|
|
27414
|
+
// Stagger reveal each suggestion one at a time
|
|
27415
|
+
useEffect(() => {
|
|
27416
|
+
// Reset when suggestions change
|
|
27417
|
+
setVisibleIndices(new Set());
|
|
27418
|
+
if (validSuggestions.length === 0)
|
|
27419
|
+
return;
|
|
27420
|
+
const timers = [];
|
|
27421
|
+
// Reveal each suggestion one by one
|
|
27422
|
+
validSuggestions.slice(0, 5).forEach((_, index) => {
|
|
27423
|
+
const timer = setTimeout(() => {
|
|
27424
|
+
setVisibleIndices(prev => new Set([...prev, index]));
|
|
27425
|
+
}, (index + 1) * STAGGER_DELAY_MS);
|
|
27426
|
+
timers.push(timer);
|
|
27427
|
+
});
|
|
27428
|
+
return () => timers.forEach(t => clearTimeout(t));
|
|
27429
|
+
}, [suggestionsKey]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
27375
27430
|
if (validSuggestions.length === 0) {
|
|
27376
27431
|
return null;
|
|
27377
27432
|
}
|
|
@@ -27379,8 +27434,12 @@ const FollowUpSuggestions = ({ suggestions, onQuestionClick, onActionClick, acce
|
|
|
27379
27434
|
? { "--primary-color": accentColor }
|
|
27380
27435
|
: undefined;
|
|
27381
27436
|
const trimText = (text) => text.length > MAX_TEXT_LENGTH ? `${text.slice(0, MAX_TEXT_LENGTH)}...` : text;
|
|
27382
|
-
return (jsx("div", { className: "ai-chat-follow-up-suggestions", style: containerStyle, children: jsx("div", { className: "ai-chat-follow-up-list", children: validSuggestions.slice(0, 5).map((suggestion) => {
|
|
27437
|
+
return (jsx("div", { className: "ai-chat-follow-up-suggestions", style: containerStyle, children: jsx("div", { className: "ai-chat-follow-up-list", children: validSuggestions.slice(0, 5).map((suggestion, index) => {
|
|
27383
27438
|
const isActionSuggestion = suggestion.type === 'action';
|
|
27439
|
+
const isVisible = visibleIndices.has(index);
|
|
27440
|
+
// Only render if visible - this adds items one by one instead of showing all at once
|
|
27441
|
+
if (!isVisible)
|
|
27442
|
+
return null;
|
|
27384
27443
|
const className = `ai-chat-follow-up-item ${isActionSuggestion ? 'action-type' : 'question-type'}`;
|
|
27385
27444
|
const handleClick = () => {
|
|
27386
27445
|
if (isActionSuggestion && onActionClick) {
|
|
@@ -27394,7 +27453,7 @@ const FollowUpSuggestions = ({ suggestions, onQuestionClick, onActionClick, acce
|
|
|
27394
27453
|
};
|
|
27395
27454
|
|
|
27396
27455
|
const MessageList = (props) => {
|
|
27397
|
-
const { messages, isTyping = false, showTypingIndicator = true, showTimestamps = true, showToolCalls = false, enableFeedback = true, welcomeTitle, welcomeMessage, suggestedQuestions, accentColor, onSuggestedQuestionClick, onActionClick, onActionDismiss, onFeedback, onScrollStateChange, getActionRenderer, variant, } = props;
|
|
27456
|
+
const { messages, isTyping = false, showTypingIndicator = true, showTimestamps = true, showToolCalls = false, enableFeedback = true, welcomeTitle, welcomeMessage, suggestedQuestions, accentColor, onSuggestedQuestionClick, onActionClick, onActionDismiss, onFeedback, onScrollStateChange, getActionRenderer, onCallEndpoint, variant, } = props;
|
|
27398
27457
|
const containerRef = useRef(null);
|
|
27399
27458
|
const messagesEndRef = useRef(null);
|
|
27400
27459
|
const [showScrollButton, setShowScrollButton] = useState(false);
|
|
@@ -27405,24 +27464,31 @@ const MessageList = (props) => {
|
|
|
27405
27464
|
const lastToolMsg = [...visibleMessages].reverse().find(msg => msg.message.role === 'tool');
|
|
27406
27465
|
return lastToolMsg?.action && lastToolMsg.action.done !== true;
|
|
27407
27466
|
}, [visibleMessages]);
|
|
27408
|
-
const checkScrollPosition = useCallback(() => {
|
|
27409
|
-
const c = containerRef.current;
|
|
27410
|
-
if (!c)
|
|
27411
|
-
return;
|
|
27412
|
-
const pb = parseInt(getComputedStyle(c).paddingBottom || '0', 10);
|
|
27413
|
-
setShowScrollButton(c.scrollHeight - c.scrollTop - c.clientHeight - pb > 24);
|
|
27414
|
-
}, []);
|
|
27415
27467
|
const scrollToBottom = useCallback(() => {
|
|
27416
27468
|
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
|
27417
27469
|
}, []);
|
|
27418
|
-
|
|
27470
|
+
// Use IntersectionObserver to detect when the end marker is visible
|
|
27471
|
+
// This is more reliable than scroll position calculations
|
|
27419
27472
|
useEffect(() => {
|
|
27420
|
-
const
|
|
27421
|
-
|
|
27473
|
+
const endMarker = messagesEndRef.current;
|
|
27474
|
+
const container = containerRef.current;
|
|
27475
|
+
if (!endMarker || !container)
|
|
27422
27476
|
return;
|
|
27423
|
-
|
|
27424
|
-
|
|
27425
|
-
|
|
27477
|
+
const observer = new IntersectionObserver((entries) => {
|
|
27478
|
+
// If the end marker is intersecting (visible), hide the button
|
|
27479
|
+
// If it's not visible, show the button
|
|
27480
|
+
const isAtBottom = entries[0]?.isIntersecting ?? false;
|
|
27481
|
+
setShowScrollButton(!isAtBottom);
|
|
27482
|
+
}, {
|
|
27483
|
+
root: container,
|
|
27484
|
+
// rootMargin adds extra space - if end marker is within 100px of viewport, consider it "visible"
|
|
27485
|
+
rootMargin: '0px 0px 100px 0px',
|
|
27486
|
+
threshold: 0,
|
|
27487
|
+
});
|
|
27488
|
+
observer.observe(endMarker);
|
|
27489
|
+
return () => observer.disconnect();
|
|
27490
|
+
}, []);
|
|
27491
|
+
useEffect(() => { onScrollStateChange?.(showScrollButton, scrollToBottom); }, [showScrollButton, scrollToBottom, onScrollStateChange]);
|
|
27426
27492
|
useEffect(() => {
|
|
27427
27493
|
const c = containerRef.current;
|
|
27428
27494
|
if (!c)
|
|
@@ -27437,32 +27503,18 @@ const MessageList = (props) => {
|
|
|
27437
27503
|
if ((isNew || isTyping) && c.scrollHeight - c.scrollTop - c.clientHeight < 150) {
|
|
27438
27504
|
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
|
27439
27505
|
}
|
|
27440
|
-
|
|
27441
|
-
}, [visibleMessages, isTyping, checkScrollPosition]);
|
|
27506
|
+
}, [visibleMessages, isTyping]);
|
|
27442
27507
|
const groupedMessages = useMemo(() => {
|
|
27443
|
-
console.log('[DEBUG MessageList] ========================================');
|
|
27444
|
-
console.log('[DEBUG MessageList] Processing messages:', visibleMessages.length);
|
|
27445
|
-
visibleMessages.forEach((m, i) => {
|
|
27446
|
-
console.log(`[DEBUG MessageList] Message ${i}:`, {
|
|
27447
|
-
id: m.id,
|
|
27448
|
-
role: m.message.role,
|
|
27449
|
-
hasAction: !!m.action,
|
|
27450
|
-
actionImpl: m.action?.implementation,
|
|
27451
|
-
content: (m.message.content || '').substring(0, 50),
|
|
27452
|
-
});
|
|
27453
|
-
});
|
|
27454
27508
|
const result = [];
|
|
27455
27509
|
let toolGroup = [];
|
|
27456
27510
|
const flush = () => {
|
|
27457
27511
|
if (toolGroup.length) {
|
|
27458
|
-
console.log('[DEBUG MessageList] Flushing tool group with', toolGroup.length, 'messages');
|
|
27459
27512
|
result.push({ type: 'tool-group', messages: [...toolGroup] });
|
|
27460
27513
|
toolGroup = [];
|
|
27461
27514
|
}
|
|
27462
27515
|
};
|
|
27463
27516
|
for (const m of visibleMessages) {
|
|
27464
27517
|
if (m.message.role === 'tool') {
|
|
27465
|
-
console.log('[DEBUG MessageList] Adding to tool group:', m.id);
|
|
27466
27518
|
toolGroup.push(m);
|
|
27467
27519
|
}
|
|
27468
27520
|
else if (m.message.role === 'user') {
|
|
@@ -27484,27 +27536,26 @@ const MessageList = (props) => {
|
|
|
27484
27536
|
}
|
|
27485
27537
|
}
|
|
27486
27538
|
flush();
|
|
27487
|
-
console.log('[DEBUG MessageList] Final grouped result:', result.length, 'items');
|
|
27488
|
-
result.forEach((item, i) => {
|
|
27489
|
-
if (item.type === 'tool-group') {
|
|
27490
|
-
console.log(`[DEBUG MessageList] Group ${i}: tool-group with ${item.messages.length} messages`);
|
|
27491
|
-
}
|
|
27492
|
-
else {
|
|
27493
|
-
console.log(`[DEBUG MessageList] Group ${i}: message (${item.message.message.role})`);
|
|
27494
|
-
}
|
|
27495
|
-
});
|
|
27496
27539
|
return result;
|
|
27497
27540
|
}, [visibleMessages]);
|
|
27541
|
+
// Get the last assistant message's suggestions for rendering at the end
|
|
27542
|
+
const lastAssistantSuggestions = useMemo(() => {
|
|
27543
|
+
for (let i = groupedMessages.length - 1; i >= 0; i--) {
|
|
27544
|
+
const item = groupedMessages[i];
|
|
27545
|
+
if (item.type === 'message' && item.message.message.role === 'assistant') {
|
|
27546
|
+
return item.message.suggestions;
|
|
27547
|
+
}
|
|
27548
|
+
}
|
|
27549
|
+
return undefined;
|
|
27550
|
+
}, [groupedMessages]);
|
|
27498
27551
|
const hasSuggestions = visibleMessages.length === 0 && onSuggestedQuestionClick && suggestedQuestions?.length;
|
|
27499
27552
|
const showWelcome = welcomeTitle || welcomeMessage || hasSuggestions;
|
|
27500
27553
|
return (jsxs("div", { ref: containerRef, className: "ai-chat-messages", role: "log", "aria-live": "polite", children: [showWelcome && (jsxs("div", { className: "ai-chat-welcome", children: [welcomeTitle && jsx("div", { className: "ai-chat-welcome-title", children: welcomeTitle }), welcomeMessage && jsx("div", { className: "ai-chat-welcome-text", children: welcomeMessage }), hasSuggestions && jsx(SuggestedQuestions, { questions: suggestedQuestions, onQuestionClick: onSuggestedQuestionClick })] })), groupedMessages.map((item, i) => {
|
|
27501
27554
|
if (item.type === 'tool-group') {
|
|
27502
|
-
return (jsx(ToolMessageGroup, { messages: item.messages, getActionRenderer: getActionRenderer, showToolIndicator: showToolCalls, accentColor: accentColor, variant: variant, onActionDismiss: onActionDismiss }, `tg-${i}`));
|
|
27555
|
+
return (jsx(ToolMessageGroup, { messages: item.messages, getActionRenderer: getActionRenderer, showToolIndicator: showToolCalls, accentColor: accentColor, variant: variant, onActionDismiss: onActionDismiss, onCallEndpoint: onCallEndpoint }, `tg-${i}`));
|
|
27503
27556
|
}
|
|
27504
|
-
|
|
27505
|
-
|
|
27506
|
-
return (jsxs(React.Fragment, { children: [jsx(Message, { message: item.message, showTimestamp: showTimestamps, onFeedback: onFeedback, getActionRenderer: getActionRenderer, accentColor: accentColor }), hasFollowUp && onSuggestedQuestionClick && jsx(FollowUpSuggestions, { suggestions: item.message.suggestions, onQuestionClick: onSuggestedQuestionClick, onActionClick: onActionClick, accentColor: accentColor })] }, item.message.id));
|
|
27507
|
-
}), isTyping && showTypingIndicator && !hasActiveAction && visibleMessages.length > 0 && jsx(TypingIndicator, {}), jsx("div", { ref: messagesEndRef })] }));
|
|
27557
|
+
return (jsx(Message, { message: item.message, showTimestamp: showTimestamps, enableFeedback: enableFeedback, onFeedback: onFeedback, getActionRenderer: getActionRenderer, accentColor: accentColor }, item.message.id));
|
|
27558
|
+
}), !isTyping && lastAssistantSuggestions?.length && onSuggestedQuestionClick && (jsx(FollowUpSuggestions, { suggestions: lastAssistantSuggestions, onQuestionClick: onSuggestedQuestionClick, onActionClick: onActionClick, accentColor: accentColor })), isTyping && showTypingIndicator && !hasActiveAction && visibleMessages.length > 0 && jsx(TypingIndicator, {}), jsx("div", { ref: messagesEndRef })] }));
|
|
27508
27559
|
};
|
|
27509
27560
|
|
|
27510
27561
|
const ALLOWED_EXTENSIONS = ['.pdf', '.doc', '.docx', '.txt', '.md', '.csv'];
|
|
@@ -27518,7 +27569,7 @@ const formatFileSize = (bytes) => {
|
|
|
27518
27569
|
return (bytes / 1024).toFixed(1) + ' KB';
|
|
27519
27570
|
return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
|
|
27520
27571
|
};
|
|
27521
|
-
const MessageInput = ({ onSend, placeholder = 'Type your message...', disabled = false, enableFileUpload = false, separateFromChat = true, showDataPolicy = true, onDataPolicyClick, }) => {
|
|
27572
|
+
const MessageInput = ({ onSend, placeholder = 'Type your message...', disabled = false, enableFileUpload = false, separateFromChat = true, showDataPolicy = true, onDataPolicyClick, onInputFocus, }) => {
|
|
27522
27573
|
const [value, setValue] = useState('');
|
|
27523
27574
|
const [selectedFiles, setSelectedFiles] = useState([]);
|
|
27524
27575
|
const textareaRef = useRef(null);
|
|
@@ -27553,492 +27604,15 @@ const MessageInput = ({ onSend, placeholder = 'Type your message...', disabled =
|
|
|
27553
27604
|
}
|
|
27554
27605
|
};
|
|
27555
27606
|
const canSend = value.trim() || selectedFiles.length > 0;
|
|
27556
|
-
return (jsxs("div", { className: `ai-chat-input-container ${separateFromChat ? 'separate' : 'integrated'}`, children: [selectedFiles.length > 0 && (jsx("div", { className: "ai-chat-file-list", children: selectedFiles.map((file, index) => (jsxs("div", { className: "ai-chat-file-item", children: [jsx("span", { className: "ai-chat-file-extension", children: getFileExtension(file.name) }), jsxs("div", { className: "ai-chat-file-info", children: [jsx("span", { className: "ai-chat-file-name", children: file.name }), jsx("span", { className: "ai-chat-file-size", children: formatFileSize(file.size) })] }), jsx("button", { className: "ai-chat-file-remove", onClick: () => handleRemoveFile(index), "aria-label": "Remove file", children: jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", children: jsx("path", { d: "M18 6L6 18M6 6l12 12" }) }) })] }, index))) })), jsxs("div", { className: "ai-chat-input-wrapper", children: [enableFileUpload && (jsxs(Fragment, { children: [jsx("input", { ref: fileInputRef, type: "file", onChange: handleFileSelect, style: { display: 'none' }, multiple: true, accept: ALLOWED_EXTENSIONS.join(',') }), jsx("button", { className: "ai-chat-file-button", onClick: () => fileInputRef.current?.click(), disabled: disabled, "aria-label": "Attach file", children: jsx("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: jsx("path", { d: "M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48" }) }) })] })), jsx("textarea", { ref: textareaRef, className: "ai-chat-input", value: value, onChange: (e) => setValue(e.target.value), onKeyDown: handleKeyDown, placeholder: placeholder, disabled: disabled, rows: 2, wrap: "soft", "aria-label": "Message input" }), jsx("button", { className: `ai-chat-send-button ${canSend ? 'active' : ''}`, onClick: handleSend, disabled: disabled || !canSend, "aria-label": "Send message", children: jsxs("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", children: [jsx("path", { d: "M12 19V5" }), jsx("path", { d: "M5 12l7-7 7 7" })] }) })] }), showDataPolicy && (jsxs("div", { className: "ai-chat-data-policy", children: [jsx("span", { children: "AI-generated responses may be inaccurate." }), onDataPolicyClick && (jsxs(Fragment, { children: [' ', jsx("button", { type: "button", className: "ai-chat-data-policy-link", onClick: onDataPolicyClick, children: "Privacy Notice" })] }))] }))] }));
|
|
27557
|
-
};
|
|
27558
|
-
|
|
27559
|
-
const CloseButton = ({ onClick, className = "", ariaLabel = "Close", }) => {
|
|
27560
|
-
return (jsx("button", { type: "button", className: `ai-chat-action-close-btn ${className}`, onClick: onClick, "aria-label": ariaLabel, children: jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", stroke: "currentColor", strokeWidth: "2", children: jsx("path", { d: "M1 1L13 13M1 13L13 1" }) }) }));
|
|
27607
|
+
return (jsxs("div", { className: `ai-chat-input-container ${separateFromChat ? 'separate' : 'integrated'}`, children: [selectedFiles.length > 0 && (jsx("div", { className: "ai-chat-file-list", children: selectedFiles.map((file, index) => (jsxs("div", { className: "ai-chat-file-item", children: [jsx("span", { className: "ai-chat-file-extension", children: getFileExtension(file.name) }), jsxs("div", { className: "ai-chat-file-info", children: [jsx("span", { className: "ai-chat-file-name", children: file.name }), jsx("span", { className: "ai-chat-file-size", children: formatFileSize(file.size) })] }), jsx("button", { className: "ai-chat-file-remove", onClick: () => handleRemoveFile(index), "aria-label": "Remove file", children: jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", children: jsx("path", { d: "M18 6L6 18M6 6l12 12" }) }) })] }, index))) })), jsxs("div", { className: "ai-chat-input-wrapper", children: [enableFileUpload && (jsxs(Fragment, { children: [jsx("input", { ref: fileInputRef, type: "file", onChange: handleFileSelect, style: { display: 'none' }, multiple: true, accept: ALLOWED_EXTENSIONS.join(',') }), jsx("button", { className: "ai-chat-file-button", onClick: () => fileInputRef.current?.click(), disabled: disabled, "aria-label": "Attach file", children: jsx("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: jsx("path", { d: "M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48" }) }) })] })), jsx("textarea", { ref: textareaRef, className: "ai-chat-input", value: value, onChange: (e) => setValue(e.target.value), onKeyDown: handleKeyDown, onFocus: onInputFocus, placeholder: placeholder, disabled: disabled, rows: 2, wrap: "soft", "aria-label": "Message input" }), jsx("button", { className: `ai-chat-send-button ${canSend ? 'active' : ''}`, onClick: handleSend, disabled: disabled || !canSend, "aria-label": "Send message", children: jsxs("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", children: [jsx("path", { d: "M12 19V5" }), jsx("path", { d: "M5 12l7-7 7 7" })] }) })] }), showDataPolicy && (jsxs("div", { className: "ai-chat-data-policy", children: [jsx("span", { children: "AI-generated responses may be inaccurate." }), onDataPolicyClick && (jsxs(Fragment, { children: [' ', jsx("button", { type: "button", className: "ai-chat-data-policy-link", onClick: onDataPolicyClick, children: "Privacy Notice" })] }))] }))] }));
|
|
27561
27608
|
};
|
|
27562
27609
|
|
|
27563
|
-
function
|
|
27564
|
-
const grouped = new Map();
|
|
27565
|
-
for (const slot of slots) {
|
|
27566
|
-
if (!slot || typeof slot !== 'object' || !slot.startTime || typeof slot.startTime !== 'string') {
|
|
27567
|
-
continue;
|
|
27568
|
-
}
|
|
27569
|
-
const date = slot.startTime.slice(0, 10);
|
|
27570
|
-
if (!grouped.has(date)) {
|
|
27571
|
-
grouped.set(date, []);
|
|
27572
|
-
}
|
|
27573
|
-
grouped.get(date).push(slot);
|
|
27574
|
-
}
|
|
27575
|
-
return grouped;
|
|
27576
|
-
}
|
|
27577
|
-
function formatDate$1(dateStr) {
|
|
27578
|
-
try {
|
|
27579
|
-
const date = new Date(dateStr);
|
|
27580
|
-
return new Intl.DateTimeFormat("en-US", {
|
|
27581
|
-
weekday: "short",
|
|
27582
|
-
month: "short",
|
|
27583
|
-
day: "numeric",
|
|
27584
|
-
}).format(date);
|
|
27585
|
-
}
|
|
27586
|
-
catch {
|
|
27587
|
-
return dateStr;
|
|
27588
|
-
}
|
|
27589
|
-
}
|
|
27590
|
-
function CalendarIcon$1() {
|
|
27591
|
-
return (jsx("svg", { className: "ai-chat-action-icon", viewBox: "0 0 20 20", fill: "currentColor", children: jsx("path", { fillRule: "evenodd", d: "M6 2a1 1 0 00-1 1v1H4a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V6a2 2 0 00-2-2h-1V3a1 1 0 10-2 0v1H7V3a1 1 0 00-1-1zm0 5a1 1 0 000 2h8a1 1 0 100-2H6z", clipRule: "evenodd" }) }));
|
|
27592
|
-
}
|
|
27593
|
-
function CheckIcon$1() {
|
|
27594
|
-
return (jsx("svg", { className: "ai-chat-action-icon-success", viewBox: "0 0 20 20", fill: "currentColor", children: jsx("path", { fillRule: "evenodd", d: "M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z", clipRule: "evenodd" }) }));
|
|
27595
|
-
}
|
|
27596
|
-
function ExternalLinkIcon$2() {
|
|
27597
|
-
return (jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsx("path", { d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" }), jsx("polyline", { points: "15 3 21 3 21 9" }), jsx("line", { x1: "10", y1: "14", x2: "21", y2: "3" })] }));
|
|
27598
|
-
}
|
|
27599
|
-
function Skeleton$2({ width, height, borderRadius = '4px' }) {
|
|
27600
|
-
return (jsx("div", { className: "ai-chat-action-skeleton-item", style: { width, height, borderRadius } }));
|
|
27601
|
-
}
|
|
27602
|
-
function GoogleCalendarCard({ action, onComplete, onDismiss, accentColor, className = '' }) {
|
|
27603
|
-
const state = action.state;
|
|
27604
|
-
const rawSlots = state.availableSlots;
|
|
27605
|
-
const availableSlots = Array.isArray(rawSlots)
|
|
27606
|
-
? rawSlots.filter((slot) => slot !== null &&
|
|
27607
|
-
slot !== undefined &&
|
|
27608
|
-
typeof slot === "object" &&
|
|
27609
|
-
"startTime" in slot &&
|
|
27610
|
-
"endTime" in slot &&
|
|
27611
|
-
typeof slot.startTime === "string" &&
|
|
27612
|
-
typeof slot.endTime === "string")
|
|
27613
|
-
: [];
|
|
27614
|
-
const allowTopic = state.allowTopic !== false;
|
|
27615
|
-
const isBooked = state.status === "booked";
|
|
27616
|
-
const slotsByDate = groupSlotsByDate$1(availableSlots);
|
|
27617
|
-
const dates = Array.from(slotsByDate.keys()).sort();
|
|
27618
|
-
const [selectedDate, setSelectedDate] = useState(dates[0] ?? "");
|
|
27619
|
-
const [selectedSlot, setSelectedSlot] = useState(null);
|
|
27620
|
-
const [topic, setTopic] = useState("");
|
|
27621
|
-
const [error, setError] = useState(null);
|
|
27622
|
-
const slotsForSelectedDate = selectedDate ? slotsByDate.get(selectedDate) ?? [] : [];
|
|
27623
|
-
const accentStyle = accentColor ? { '--action-accent': accentColor } : {};
|
|
27624
|
-
const onConfirm = () => {
|
|
27625
|
-
if (!selectedSlot) {
|
|
27626
|
-
setError("Please select a time slot.");
|
|
27627
|
-
return;
|
|
27628
|
-
}
|
|
27629
|
-
if (allowTopic && !topic.trim()) {
|
|
27630
|
-
setError("Please enter a topic for the meeting.");
|
|
27631
|
-
return;
|
|
27632
|
-
}
|
|
27633
|
-
setError(null);
|
|
27634
|
-
onComplete?.(action.toolCallId, {
|
|
27635
|
-
...action.state,
|
|
27636
|
-
selectedSlot: {
|
|
27637
|
-
startTime: selectedSlot.startTime,
|
|
27638
|
-
endTime: selectedSlot.endTime,
|
|
27639
|
-
},
|
|
27640
|
-
topic: allowTopic ? topic.trim() : null,
|
|
27641
|
-
});
|
|
27642
|
-
};
|
|
27643
|
-
const handleDismiss = () => {
|
|
27644
|
-
onDismiss?.(action.toolCallId);
|
|
27645
|
-
};
|
|
27646
|
-
// Booked state
|
|
27647
|
-
if (isBooked) {
|
|
27648
|
-
const bookedSlot = state.selectedSlot;
|
|
27649
|
-
const bookedTopic = state.topic;
|
|
27650
|
-
const eventLink = state.bookedEventLink;
|
|
27651
|
-
return (jsxs("div", { className: `ai-chat-action-card ai-chat-action-booked ${className}`, style: accentStyle, children: [jsxs("div", { className: "ai-chat-action-header", children: [jsx("div", { className: "ai-chat-action-success-icon-wrapper", children: jsx(CheckIcon$1, {}) }), "Appointment Confirmed"] }), jsxs("div", { className: "ai-chat-action-body", children: [bookedTopic && (jsxs("div", { className: "ai-chat-action-detail-box", children: [jsx("span", { className: "ai-chat-action-label-small", children: "TOPIC" }), jsx("span", { className: "ai-chat-action-value-large", children: bookedTopic })] })), bookedSlot && bookedSlot.startTime && (jsxs("div", { className: "ai-chat-action-detail-box", children: [jsx("span", { className: "ai-chat-action-label-small", children: "TIME" }), jsx("span", { className: "ai-chat-action-value-large", children: bookedSlot.displayTime || new Date(bookedSlot.startTime).toLocaleString() })] })), eventLink && (jsxs("a", { href: eventLink, target: "_blank", rel: "noopener noreferrer", className: "ai-chat-action-link-button", children: ["View in Google Calendar", jsx(ExternalLinkIcon$2, {})] }))] })] }));
|
|
27652
|
-
}
|
|
27653
|
-
// Skeleton loading state - show when waiting for backend after user confirms
|
|
27654
|
-
const isWaitingForBackend = !action.done && state.selectedSlot && !isBooked;
|
|
27655
|
-
if (isWaitingForBackend) {
|
|
27656
|
-
return (jsx("div", { className: `ai-chat-action-card ai-chat-action-skeleton ${className}`, style: accentStyle, children: jsxs("div", { className: "ai-chat-action-skeleton-content", children: [jsxs("div", { className: "ai-chat-action-skeleton-header", children: [jsx(Skeleton$2, { width: "28px", height: "28px", borderRadius: "50%" }), jsx(Skeleton$2, { width: "180px", height: "20px", borderRadius: "4px" })] }), jsxs("div", { className: "ai-chat-action-skeleton-box", children: [jsx(Skeleton$2, { width: "60px", height: "12px", borderRadius: "4px" }), jsx(Skeleton$2, { width: "120px", height: "18px", borderRadius: "4px" })] }), jsxs("div", { className: "ai-chat-action-skeleton-box", children: [jsx(Skeleton$2, { width: "50px", height: "12px", borderRadius: "4px" }), jsx(Skeleton$2, { width: "200px", height: "18px", borderRadius: "4px" })] }), jsx(Skeleton$2, { width: "100%", height: "44px", borderRadius: "999px" })] }) }));
|
|
27657
|
-
}
|
|
27658
|
-
// Booking form
|
|
27659
|
-
return (jsxs("div", { className: `ai-chat-action-card ai-chat-action-card--closable ai-chat-google-calendar ${className}`, style: accentStyle, children: [jsxs("div", { className: "ai-chat-action-header", children: [jsx(CalendarIcon$1, {}), "Schedule an Appointment", onDismiss && (jsx(CloseButton, { onClick: handleDismiss, ariaLabel: "Cancel appointment booking" }))] }), jsxs("div", { className: "ai-chat-action-body", children: [allowTopic && (jsxs("div", { className: "ai-chat-action-field", children: [jsx("label", { htmlFor: `topic-${action.toolCallId}`, className: "ai-chat-action-label", children: "Meeting Topic" }), jsx("input", { id: `topic-${action.toolCallId}`, type: "text", className: "ai-chat-action-input", placeholder: "e.g., Product Demo", value: topic, onChange: (e) => setTopic(e.target.value) })] })), jsxs("div", { className: "ai-chat-action-field", children: [jsx("label", { className: "ai-chat-action-label", children: "Select Date" }), jsx("div", { className: "ai-chat-action-date-grid", children: dates.slice(0, 7).map((date) => (jsx("button", { type: "button", className: `ai-chat-action-date-btn ${selectedDate === date ? "active" : ""}`, onClick: () => {
|
|
27660
|
-
setSelectedDate(date);
|
|
27661
|
-
setSelectedSlot(null);
|
|
27662
|
-
}, children: formatDate$1(date) }, date))) })] }), selectedDate && slotsForSelectedDate.length > 0 && (jsxs("div", { className: "ai-chat-action-field", children: [jsx("label", { className: "ai-chat-action-label", children: "Select Time" }), jsx("div", { className: "ai-chat-action-time-grid", children: slotsForSelectedDate.map((slot) => (jsx("button", { type: "button", className: `ai-chat-action-time-btn ${selectedSlot?.startTime === slot.startTime ? "active" : ""}`, onClick: () => setSelectedSlot(slot), children: slot.displayTime || new Date(slot.startTime).toLocaleTimeString([], { hour: "numeric", minute: "2-digit" }) }, slot.startTime))) })] })), error && jsx("div", { className: "ai-chat-action-error", children: error }), jsx("button", { className: "ai-chat-action-button", type: "button", onClick: onConfirm, disabled: !selectedSlot, children: "Confirm Appointment" }), availableSlots.length === 0 && (jsx("div", { className: "ai-chat-action-hint", children: "No available time slots found." }))] })] }));
|
|
27663
|
-
}
|
|
27664
|
-
|
|
27665
|
-
function groupSlotsByDate(slots) {
|
|
27666
|
-
const grouped = new Map();
|
|
27667
|
-
for (const slot of slots) {
|
|
27668
|
-
if (!slot || typeof slot !== 'object' || !slot.startTime || typeof slot.startTime !== 'string') {
|
|
27669
|
-
continue;
|
|
27670
|
-
}
|
|
27671
|
-
const date = slot.startTime.slice(0, 10);
|
|
27672
|
-
if (!grouped.has(date)) {
|
|
27673
|
-
grouped.set(date, []);
|
|
27674
|
-
}
|
|
27675
|
-
grouped.get(date).push(slot);
|
|
27676
|
-
}
|
|
27677
|
-
return grouped;
|
|
27678
|
-
}
|
|
27679
|
-
function formatDate(dateStr) {
|
|
27680
|
-
try {
|
|
27681
|
-
const date = new Date(dateStr);
|
|
27682
|
-
return new Intl.DateTimeFormat("en-US", {
|
|
27683
|
-
weekday: "short",
|
|
27684
|
-
month: "short",
|
|
27685
|
-
day: "numeric",
|
|
27686
|
-
}).format(date);
|
|
27687
|
-
}
|
|
27688
|
-
catch {
|
|
27689
|
-
return dateStr;
|
|
27690
|
-
}
|
|
27691
|
-
}
|
|
27692
|
-
function CalendarIcon() {
|
|
27693
|
-
return (jsx("svg", { className: "ai-chat-action-icon", viewBox: "0 0 20 20", fill: "currentColor", children: jsx("path", { fillRule: "evenodd", d: "M6 2a1 1 0 00-1 1v1H4a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V6a2 2 0 00-2-2h-1V3a1 1 0 10-2 0v1H7V3a1 1 0 00-1-1zm0 5a1 1 0 000 2h8a1 1 0 100-2H6z", clipRule: "evenodd" }) }));
|
|
27694
|
-
}
|
|
27695
|
-
function MailIcon() {
|
|
27696
|
-
return (jsxs("svg", { className: "ai-chat-action-icon", viewBox: "0 0 20 20", fill: "currentColor", children: [jsx("path", { d: "M2.003 5.884L10 9.882l7.997-3.998A2 2 0 0016 4H4a2 2 0 00-1.997 1.884z" }), jsx("path", { d: "M18 8.118l-8 4-8-4V14a2 2 0 002 2h12a2 2 0 002-2V8.118z" })] }));
|
|
27697
|
-
}
|
|
27698
|
-
function CheckIcon() {
|
|
27699
|
-
return (jsx("svg", { className: "ai-chat-action-icon-success", viewBox: "0 0 20 20", fill: "currentColor", children: jsx("path", { fillRule: "evenodd", d: "M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z", clipRule: "evenodd" }) }));
|
|
27700
|
-
}
|
|
27701
|
-
function ErrorIcon() {
|
|
27702
|
-
return (jsx("svg", { className: "ai-chat-action-icon-error", viewBox: "0 0 20 20", fill: "currentColor", children: jsx("path", { fillRule: "evenodd", d: "M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z", clipRule: "evenodd" }) }));
|
|
27703
|
-
}
|
|
27704
|
-
function ExternalLinkIcon$1() {
|
|
27705
|
-
return (jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsx("path", { d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" }), jsx("polyline", { points: "15 3 21 3 21 9" }), jsx("line", { x1: "10", y1: "14", x2: "21", y2: "3" })] }));
|
|
27706
|
-
}
|
|
27707
|
-
function Skeleton$1({ width, height, borderRadius = '4px' }) {
|
|
27708
|
-
return (jsx("div", { className: "ai-chat-action-skeleton-item", style: { width, height, borderRadius } }));
|
|
27709
|
-
}
|
|
27710
|
-
function PinInputGroup$1({ values, onChange, disabled }) {
|
|
27711
|
-
const inputRefs = useRef([]);
|
|
27712
|
-
const handleChange = (index, value) => {
|
|
27713
|
-
// Only allow digits
|
|
27714
|
-
const digit = value.replace(/[^0-9]/g, '');
|
|
27715
|
-
const newValues = [...values];
|
|
27716
|
-
newValues[index] = digit.slice(-1);
|
|
27717
|
-
onChange(newValues);
|
|
27718
|
-
// Auto-focus next input
|
|
27719
|
-
if (digit && index < 5) {
|
|
27720
|
-
inputRefs.current[index + 1]?.focus();
|
|
27721
|
-
}
|
|
27722
|
-
};
|
|
27723
|
-
const handleKeyDown = (index, e) => {
|
|
27724
|
-
if (e.key === 'Backspace' && !values[index] && index > 0) {
|
|
27725
|
-
inputRefs.current[index - 1]?.focus();
|
|
27726
|
-
}
|
|
27727
|
-
};
|
|
27728
|
-
const handlePaste = (e) => {
|
|
27729
|
-
e.preventDefault();
|
|
27730
|
-
const pastedData = e.clipboardData.getData('text').replace(/[^0-9]/g, '').slice(0, 6);
|
|
27731
|
-
const newValues = pastedData.split('').concat(Array(6 - pastedData.length).fill(''));
|
|
27732
|
-
onChange(newValues);
|
|
27733
|
-
// Focus the next empty input or the last one
|
|
27734
|
-
const nextIndex = Math.min(pastedData.length, 5);
|
|
27735
|
-
inputRefs.current[nextIndex]?.focus();
|
|
27736
|
-
};
|
|
27737
|
-
return (jsx("div", { className: "ai-chat-pin-input-group", children: values.map((value, index) => (jsx("input", { ref: (el) => {
|
|
27738
|
-
inputRefs.current[index] = el;
|
|
27739
|
-
}, type: "text", inputMode: "numeric", maxLength: 1, className: "ai-chat-pin-input", value: value, onChange: (e) => handleChange(index, e.target.value), onKeyDown: (e) => handleKeyDown(index, e), onPaste: handlePaste, disabled: disabled, autoFocus: index === 0 }, index))) }));
|
|
27740
|
-
}
|
|
27741
|
-
function MicrosoftCalendarCard({ action, onComplete, onDismiss, accentColor, className = '' }) {
|
|
27742
|
-
const state = action.state;
|
|
27743
|
-
const phase = state.phase || "awaiting_email";
|
|
27744
|
-
const allowTopic = state.allowTopic !== false;
|
|
27745
|
-
const accentStyle = accentColor ? { '--action-accent': accentColor } : {};
|
|
27746
|
-
const handleDismiss = () => {
|
|
27747
|
-
onDismiss?.(action.toolCallId);
|
|
27748
|
-
};
|
|
27749
|
-
const showCloseButton = Boolean(onDismiss);
|
|
27750
|
-
// Debug: Log state changes
|
|
27751
|
-
const prevStateRef = useRef(null);
|
|
27752
|
-
useEffect(() => {
|
|
27753
|
-
if (JSON.stringify(prevStateRef.current) !== JSON.stringify(state)) {
|
|
27754
|
-
console.log('[MicrosoftCalendarCard] State updated:', {
|
|
27755
|
-
phase: state.phase,
|
|
27756
|
-
hasError: !!state.errorMessage,
|
|
27757
|
-
error: state.errorMessage,
|
|
27758
|
-
slotsCount: state.availableSlots?.length || 0
|
|
27759
|
-
});
|
|
27760
|
-
prevStateRef.current = state;
|
|
27761
|
-
}
|
|
27762
|
-
}, [state]);
|
|
27763
|
-
// Email phase state
|
|
27764
|
-
const [email, setEmail] = useState("");
|
|
27765
|
-
const [emailError, setEmailError] = useState(null);
|
|
27766
|
-
// OTP phase state
|
|
27767
|
-
const [otpValues, setOtpValues] = useState(Array(6).fill(''));
|
|
27768
|
-
const [otpError, setOtpError] = useState(null);
|
|
27769
|
-
// Loading states
|
|
27770
|
-
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
27771
|
-
// Reset loading state when phase changes (backend has responded)
|
|
27772
|
-
// Use useEffect for reliable re-rendering
|
|
27773
|
-
useEffect(() => {
|
|
27774
|
-
console.log('[MicrosoftCalendarCard] Phase changed to:', phase);
|
|
27775
|
-
setIsSubmitting(false);
|
|
27776
|
-
// Clear errors when transitioning to new phase
|
|
27777
|
-
if (phase === "awaiting_email") {
|
|
27778
|
-
setEmailError(null);
|
|
27779
|
-
setOtpError(null);
|
|
27780
|
-
setSelectionError(null);
|
|
27781
|
-
}
|
|
27782
|
-
else if (phase === "awaiting_otp") {
|
|
27783
|
-
setOtpError(state.errorMessage || null);
|
|
27784
|
-
setEmailError(null);
|
|
27785
|
-
setSelectionError(null);
|
|
27786
|
-
// Clear OTP input for fresh attempt
|
|
27787
|
-
setOtpValues(Array(6).fill(''));
|
|
27788
|
-
}
|
|
27789
|
-
else if (phase === "awaiting_options") {
|
|
27790
|
-
setEmailError(null);
|
|
27791
|
-
setOtpError(null);
|
|
27792
|
-
setSelectionError(null);
|
|
27793
|
-
}
|
|
27794
|
-
else if (phase === "awaiting_booking") {
|
|
27795
|
-
setSelectionError(state.errorMessage || null);
|
|
27796
|
-
setEmailError(null);
|
|
27797
|
-
setOtpError(null);
|
|
27798
|
-
}
|
|
27799
|
-
else if (phase === "booked" || phase === "cancelled" || phase === "error") {
|
|
27800
|
-
setEmailError(null);
|
|
27801
|
-
setOtpError(null);
|
|
27802
|
-
setSelectionError(null);
|
|
27803
|
-
setSelectedId(null); // Reset selection
|
|
27804
|
-
}
|
|
27805
|
-
}, [phase, state.errorMessage]);
|
|
27806
|
-
// Selection phase state
|
|
27807
|
-
const rawSlots = state.availableSlots;
|
|
27808
|
-
const availableSlots = Array.isArray(rawSlots)
|
|
27809
|
-
? rawSlots.filter((slot) => slot !== null &&
|
|
27810
|
-
slot !== undefined &&
|
|
27811
|
-
typeof slot === "object" &&
|
|
27812
|
-
"startTime" in slot &&
|
|
27813
|
-
"endTime" in slot &&
|
|
27814
|
-
typeof slot.startTime === "string" &&
|
|
27815
|
-
typeof slot.endTime === "string")
|
|
27816
|
-
: [];
|
|
27817
|
-
const slotsByDate = groupSlotsByDate(availableSlots);
|
|
27818
|
-
const dates = Array.from(slotsByDate.keys()).sort();
|
|
27819
|
-
const [selectedDate, setSelectedDate] = useState(dates[0] ?? "");
|
|
27820
|
-
const [selectedSlot, setSelectedSlot] = useState(null);
|
|
27821
|
-
const [topic, setTopic] = useState("");
|
|
27822
|
-
const [selectionError, setSelectionError] = useState(null);
|
|
27823
|
-
// Cancellation phase state
|
|
27824
|
-
const [selectedId, setSelectedId] = useState(null);
|
|
27825
|
-
const slotsForSelectedDate = selectedDate ? slotsByDate.get(selectedDate) ?? [] : [];
|
|
27826
|
-
// Phase 1: Email Input
|
|
27827
|
-
const handleEmailSubmit = () => {
|
|
27828
|
-
const trimmedEmail = email.trim();
|
|
27829
|
-
if (!trimmedEmail) {
|
|
27830
|
-
setEmailError("Please enter your email address");
|
|
27831
|
-
return;
|
|
27832
|
-
}
|
|
27833
|
-
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(trimmedEmail)) {
|
|
27834
|
-
setEmailError("Please enter a valid email address");
|
|
27835
|
-
return;
|
|
27836
|
-
}
|
|
27837
|
-
setEmailError(null);
|
|
27838
|
-
setIsSubmitting(true);
|
|
27839
|
-
console.log('[MicrosoftCalendarCard] Submitting email:', trimmedEmail);
|
|
27840
|
-
setTimeout(() => {
|
|
27841
|
-
onComplete?.(action.toolCallId, {
|
|
27842
|
-
...action.state,
|
|
27843
|
-
email: trimmedEmail,
|
|
27844
|
-
});
|
|
27845
|
-
}, 50);
|
|
27846
|
-
};
|
|
27847
|
-
// Phase 2: OTP Verification
|
|
27848
|
-
const handleOtpSubmit = () => {
|
|
27849
|
-
const otpCode = otpValues.join('');
|
|
27850
|
-
if (otpCode.length !== 6) {
|
|
27851
|
-
setOtpError("Please enter the 6-digit code");
|
|
27852
|
-
return;
|
|
27853
|
-
}
|
|
27854
|
-
setOtpError(null);
|
|
27855
|
-
setIsSubmitting(true);
|
|
27856
|
-
console.log('[MicrosoftCalendarCard] Submitting OTP code');
|
|
27857
|
-
setTimeout(() => {
|
|
27858
|
-
onComplete?.(action.toolCallId, {
|
|
27859
|
-
...action.state,
|
|
27860
|
-
otpCode,
|
|
27861
|
-
});
|
|
27862
|
-
}, 50);
|
|
27863
|
-
};
|
|
27864
|
-
// Phase 3: Appointment Selection
|
|
27865
|
-
const handleAppointmentConfirm = () => {
|
|
27866
|
-
if (!selectedSlot) {
|
|
27867
|
-
setSelectionError("Please select a time slot");
|
|
27868
|
-
return;
|
|
27869
|
-
}
|
|
27870
|
-
if (allowTopic && !topic.trim()) {
|
|
27871
|
-
setSelectionError("Please enter a meeting topic");
|
|
27872
|
-
return;
|
|
27873
|
-
}
|
|
27874
|
-
setSelectionError(null);
|
|
27875
|
-
setIsSubmitting(true);
|
|
27876
|
-
console.log('[MicrosoftCalendarCard] Confirming appointment:', {
|
|
27877
|
-
slot: selectedSlot,
|
|
27878
|
-
topic: topic.trim()
|
|
27879
|
-
});
|
|
27880
|
-
setTimeout(() => {
|
|
27881
|
-
onComplete?.(action.toolCallId, {
|
|
27882
|
-
...action.state,
|
|
27883
|
-
selectedSlot: {
|
|
27884
|
-
startTime: selectedSlot.startTime,
|
|
27885
|
-
endTime: selectedSlot.endTime,
|
|
27886
|
-
},
|
|
27887
|
-
topic: allowTopic ? topic.trim() : null,
|
|
27888
|
-
});
|
|
27889
|
-
}, 50);
|
|
27890
|
-
};
|
|
27891
|
-
// Handle "Use different email" button
|
|
27892
|
-
const handleUseDifferentEmail = () => {
|
|
27893
|
-
setEmail("");
|
|
27894
|
-
setEmailError(null);
|
|
27895
|
-
setOtpValues(Array(6).fill(''));
|
|
27896
|
-
setOtpError(null);
|
|
27897
|
-
onComplete?.(action.toolCallId, {
|
|
27898
|
-
phase: "awaiting_email",
|
|
27899
|
-
email: null,
|
|
27900
|
-
otpVerified: false,
|
|
27901
|
-
otpAttempts: 0,
|
|
27902
|
-
availableSlots: [],
|
|
27903
|
-
selectedSlot: null,
|
|
27904
|
-
topic: null,
|
|
27905
|
-
bookedEventId: null,
|
|
27906
|
-
bookedEventLink: null,
|
|
27907
|
-
allowTopic,
|
|
27908
|
-
errorMessage: null,
|
|
27909
|
-
});
|
|
27910
|
-
};
|
|
27911
|
-
// Phase 5: Booked Confirmation
|
|
27912
|
-
if (phase === "booked") {
|
|
27913
|
-
const bookedSlot = state.selectedSlot;
|
|
27914
|
-
const bookedTopic = state.topic;
|
|
27915
|
-
const eventLink = state.bookedEventLink;
|
|
27916
|
-
const bookedEmail = state.email;
|
|
27917
|
-
return (jsxs("div", { className: `ai-chat-action-card ai-chat-action-booked ${className}`, style: accentStyle, children: [jsxs("div", { className: "ai-chat-action-header", children: [jsx("div", { className: "ai-chat-action-success-icon-wrapper", children: jsx(CheckIcon, {}) }), "Appointment Confirmed"] }), jsxs("div", { className: "ai-chat-action-body", children: [bookedTopic && (jsxs("div", { className: "ai-chat-action-detail-box", children: [jsx("span", { className: "ai-chat-action-label-small", children: "TOPIC" }), jsx("span", { className: "ai-chat-action-value-large", children: bookedTopic })] })), bookedSlot && bookedSlot.startTime && (jsxs("div", { className: "ai-chat-action-detail-box", children: [jsx("span", { className: "ai-chat-action-label-small", children: "TIME" }), jsx("span", { className: "ai-chat-action-value-large", children: bookedSlot.displayTime || new Date(bookedSlot.startTime).toLocaleString() })] })), bookedEmail && (jsxs("div", { className: "ai-chat-action-hint", children: ["A calendar invitation has been sent to ", bookedEmail] })), eventLink && (jsxs("a", { href: eventLink, target: "_blank", rel: "noopener noreferrer", className: "ai-chat-action-link-button", children: ["View in Outlook Calendar", jsx(ExternalLinkIcon$1, {})] }))] })] }));
|
|
27918
|
-
}
|
|
27919
|
-
// Phase 6: Cancelled Confirmation
|
|
27920
|
-
if (phase === "cancelled") {
|
|
27921
|
-
const cancelledSubject = state.cancelledEventSubject;
|
|
27922
|
-
const userEmail = state.email;
|
|
27923
|
-
return (jsxs("div", { className: `ai-chat-action-card ai-chat-action-booked ${className}`, style: accentStyle, children: [jsxs("div", { className: "ai-chat-action-header", children: [jsx("div", { className: "ai-chat-action-success-icon-wrapper", children: jsx(CheckIcon, {}) }), "Appointment Cancelled"] }), jsxs("div", { className: "ai-chat-action-body", children: [cancelledSubject && (jsxs("div", { className: "ai-chat-action-detail-box", children: [jsx("span", { className: "ai-chat-action-label-small", children: "CANCELLED" }), jsx("span", { className: "ai-chat-action-value-large", children: cancelledSubject })] })), userEmail && (jsxs("div", { className: "ai-chat-action-hint", children: ["A cancellation notice has been sent to ", userEmail] }))] })] }));
|
|
27924
|
-
}
|
|
27925
|
-
// Error State
|
|
27926
|
-
if (phase === "error") {
|
|
27927
|
-
return (jsxs("div", { className: `ai-chat-action-card ai-chat-action-error ${className}`, style: accentStyle, children: [jsxs("div", { className: "ai-chat-action-header", children: [jsx(ErrorIcon, {}), "Error"] }), jsxs("div", { className: "ai-chat-action-body", children: [jsx("div", { className: "ai-chat-action-error-message", children: state.errorMessage || "An error occurred. Please try again." }), jsx("button", { className: "ai-chat-action-button-secondary", type: "button", onClick: () => {
|
|
27928
|
-
setEmail("");
|
|
27929
|
-
setEmailError(null);
|
|
27930
|
-
setOtpValues(Array(6).fill(''));
|
|
27931
|
-
setOtpError(null);
|
|
27932
|
-
onComplete?.(action.toolCallId, {
|
|
27933
|
-
phase: "awaiting_email",
|
|
27934
|
-
email: null,
|
|
27935
|
-
otpVerified: false,
|
|
27936
|
-
otpAttempts: 0,
|
|
27937
|
-
availableSlots: [],
|
|
27938
|
-
selectedSlot: null,
|
|
27939
|
-
topic: null,
|
|
27940
|
-
bookedEventId: null,
|
|
27941
|
-
bookedEventLink: null,
|
|
27942
|
-
allowTopic,
|
|
27943
|
-
errorMessage: null,
|
|
27944
|
-
});
|
|
27945
|
-
}, children: "Start Over" })] })] }));
|
|
27946
|
-
}
|
|
27947
|
-
// Loading State
|
|
27948
|
-
if (isSubmitting) {
|
|
27949
|
-
return (jsx("div", { className: `ai-chat-action-card ai-chat-action-skeleton ${className}`, style: accentStyle, children: jsxs("div", { className: "ai-chat-action-skeleton-content", children: [jsxs("div", { className: "ai-chat-action-skeleton-header", children: [jsx(Skeleton$1, { width: "28px", height: "28px", borderRadius: "50%" }), jsx(Skeleton$1, { width: "180px", height: "20px", borderRadius: "4px" })] }), jsxs("div", { className: "ai-chat-action-skeleton-box", children: [jsx(Skeleton$1, { width: "60px", height: "12px", borderRadius: "4px" }), jsx(Skeleton$1, { width: "120px", height: "18px", borderRadius: "4px" })] }), jsx(Skeleton$1, { width: "100%", height: "44px", borderRadius: "999px" })] }) }));
|
|
27950
|
-
}
|
|
27951
|
-
// Phase 1: Email Input
|
|
27952
|
-
if (phase === "awaiting_email") {
|
|
27953
|
-
const displayError = state.errorMessage || emailError;
|
|
27954
|
-
return (jsxs("div", { className: `ai-chat-action-card ai-chat-action-card--closable ${className}`, style: accentStyle, children: [jsxs("div", { className: "ai-chat-action-header", children: [jsx(CalendarIcon, {}), "Schedule an Appointment", showCloseButton && (jsx(CloseButton, { onClick: handleDismiss, ariaLabel: "Cancel appointment booking" }))] }), jsxs("div", { className: "ai-chat-action-body", children: [jsx("div", { className: "ai-chat-action-hint", children: "We'll send a verification code to your email" }), jsxs("div", { className: "ai-chat-action-field", children: [jsx("label", { htmlFor: `email-${action.toolCallId}`, className: "ai-chat-action-label", children: "Email Address" }), jsx("input", { id: `email-${action.toolCallId}`, type: "email", className: "ai-chat-action-input", placeholder: "your@email.com", value: email, onChange: (e) => {
|
|
27955
|
-
setEmail(e.target.value);
|
|
27956
|
-
setEmailError(null);
|
|
27957
|
-
}, onKeyPress: (e) => {
|
|
27958
|
-
if (e.key === 'Enter') {
|
|
27959
|
-
handleEmailSubmit();
|
|
27960
|
-
}
|
|
27961
|
-
}, autoFocus: true })] }), displayError && (jsx("div", { className: "ai-chat-action-error", children: displayError })), jsx("button", { className: "ai-chat-action-button", type: "button", onClick: handleEmailSubmit, children: "Continue" })] })] }));
|
|
27962
|
-
}
|
|
27963
|
-
// Phase 2: OTP Input
|
|
27964
|
-
if (phase === "awaiting_otp") {
|
|
27965
|
-
const displayError = state.errorMessage || otpError;
|
|
27966
|
-
const userEmail = state.email;
|
|
27967
|
-
return (jsxs("div", { className: `ai-chat-action-card ai-chat-action-card--closable ${className}`, style: accentStyle, children: [jsxs("div", { className: "ai-chat-action-header", children: [jsx(MailIcon, {}), "Verify Your Email", showCloseButton && (jsx(CloseButton, { onClick: handleDismiss, ariaLabel: "Cancel appointment booking" }))] }), jsxs("div", { className: "ai-chat-action-body", children: [jsxs("div", { className: "ai-chat-action-hint", children: ["We sent a 6-digit code to ", jsx("strong", { children: userEmail })] }), jsx(PinInputGroup$1, { values: otpValues, onChange: (newValues) => {
|
|
27968
|
-
setOtpValues(newValues);
|
|
27969
|
-
setOtpError(null);
|
|
27970
|
-
// Auto-submit when all 6 digits are entered
|
|
27971
|
-
if (newValues.every(v => v.length === 1)) {
|
|
27972
|
-
console.log('[MicrosoftCalendarCard] Auto-submitting OTP (all 6 digits entered)');
|
|
27973
|
-
setIsSubmitting(true);
|
|
27974
|
-
const code = newValues.join('');
|
|
27975
|
-
setTimeout(() => {
|
|
27976
|
-
onComplete?.(action.toolCallId, {
|
|
27977
|
-
...action.state,
|
|
27978
|
-
otpCode: code,
|
|
27979
|
-
});
|
|
27980
|
-
}, 100);
|
|
27981
|
-
}
|
|
27982
|
-
}, disabled: isSubmitting }), displayError && (jsx("div", { className: "ai-chat-action-error", children: displayError })), jsx("button", { className: "ai-chat-action-button", type: "button", onClick: handleOtpSubmit, disabled: otpValues.join('').length !== 6, children: "Verify Code" }), jsx("button", { className: "ai-chat-action-button-secondary", type: "button", onClick: handleUseDifferentEmail, children: "Use different email" })] })] }));
|
|
27983
|
-
}
|
|
27984
|
-
// Phase 3: Options Menu (with inline cancel buttons and confirmation)
|
|
27985
|
-
if (phase === "awaiting_options") {
|
|
27986
|
-
const userAppointments = state.userAppointments || [];
|
|
27987
|
-
const maxAppointments = state.maxAppointmentsPerUser || 3;
|
|
27988
|
-
const appointmentCount = userAppointments.length;
|
|
27989
|
-
const canBook = appointmentCount < maxAppointments;
|
|
27990
|
-
const hasAppointments = appointmentCount > 0;
|
|
27991
|
-
const displayError = state.errorMessage || selectionError;
|
|
27992
|
-
// If confirming cancellation, show confirmation dialog
|
|
27993
|
-
if (selectedId) {
|
|
27994
|
-
const appointmentToCancel = userAppointments.find(appt => appt.id === selectedId);
|
|
27995
|
-
if (!appointmentToCancel) {
|
|
27996
|
-
setSelectedId(null); // Reset if appointment not found
|
|
27997
|
-
}
|
|
27998
|
-
else {
|
|
27999
|
-
return (jsxs("div", { className: `ai-chat-action-card ai-chat-action-card--closable ${className}`, style: accentStyle, children: [jsxs("div", { className: "ai-chat-action-header", children: [jsx(CalendarIcon, {}), "Confirm Cancellation", showCloseButton && (jsx(CloseButton, { onClick: handleDismiss, ariaLabel: "Cancel appointment booking" }))] }), jsxs("div", { className: "ai-chat-action-body", children: [jsx("div", { className: "ai-chat-action-hint", children: "Are you sure you want to cancel this appointment?" }), jsx("div", { className: "ai-chat-action-appointment-item", style: { marginBottom: '16px' }, children: jsxs("div", { className: "ai-chat-action-appointment-info", children: [jsx("div", { className: "ai-chat-action-appointment-subject", children: appointmentToCancel.subject }), jsx("div", { className: "ai-chat-action-appointment-time", children: appointmentToCancel.displayTime })] }) }), displayError && (jsx("div", { className: "ai-chat-action-error", children: displayError })), jsxs("div", { className: "ai-chat-action-button-group", children: [jsx("button", { className: "ai-chat-action-button", type: "button", onClick: () => {
|
|
28000
|
-
setIsSubmitting(true);
|
|
28001
|
-
onComplete?.(action.toolCallId, {
|
|
28002
|
-
...action.state,
|
|
28003
|
-
selectedOption: "cancel",
|
|
28004
|
-
selectedAppointmentId: selectedId,
|
|
28005
|
-
});
|
|
28006
|
-
}, disabled: isSubmitting, children: isSubmitting ? 'Cancelling...' : 'Confirm Cancellation' }), jsx("button", { className: "ai-chat-action-button-secondary", type: "button", onClick: () => {
|
|
28007
|
-
setSelectedId(null);
|
|
28008
|
-
setSelectionError(null);
|
|
28009
|
-
}, disabled: isSubmitting, children: "Go Back" })] })] })] }));
|
|
28010
|
-
}
|
|
28011
|
-
}
|
|
28012
|
-
// Normal view with inline cancel buttons
|
|
28013
|
-
return (jsxs("div", { className: `ai-chat-action-card ai-chat-action-card--closable ${className}`, style: accentStyle, children: [jsxs("div", { className: "ai-chat-action-header", children: [jsx(CalendarIcon, {}), "Manage Your Appointments", showCloseButton && (jsx(CloseButton, { onClick: handleDismiss, ariaLabel: "Cancel appointment booking" }))] }), jsxs("div", { className: "ai-chat-action-body", children: [jsxs("div", { className: "ai-chat-action-hint", children: ["You have ", appointmentCount, " active appointment", appointmentCount !== 1 ? 's' : '', canBook && ` (${maxAppointments - appointmentCount} slot${maxAppointments - appointmentCount !== 1 ? 's' : ''} remaining)`] }), hasAppointments && (jsxs("div", { className: "ai-chat-action-appointment-list", children: [jsx("div", { className: "ai-chat-action-label", children: "Your Upcoming Appointments" }), userAppointments.map((appt) => (jsxs("div", { className: "ai-chat-action-appointment-item", children: [jsxs("div", { className: "ai-chat-action-appointment-info", children: [jsx("div", { className: "ai-chat-action-appointment-subject", children: appt.subject }), jsx("div", { className: "ai-chat-action-appointment-time", children: appt.displayTime })] }), jsx("button", { className: "ai-chat-action-button-secondary", type: "button", onClick: () => {
|
|
28014
|
-
setSelectedId(appt.id);
|
|
28015
|
-
setSelectionError(null);
|
|
28016
|
-
}, children: "Cancel" })] }, appt.id)))] })), displayError && (jsx("div", { className: "ai-chat-action-error", children: displayError })), canBook && (jsx("button", { className: "ai-chat-action-button", type: "button", onClick: () => {
|
|
28017
|
-
setIsSubmitting(true);
|
|
28018
|
-
onComplete?.(action.toolCallId, {
|
|
28019
|
-
...action.state,
|
|
28020
|
-
selectedOption: "book",
|
|
28021
|
-
});
|
|
28022
|
-
}, disabled: isSubmitting, children: isSubmitting ? 'Loading...' : 'Book New Appointment' })), !canBook && !hasAppointments && (jsx("div", { className: "ai-chat-action-hint", children: "No appointments found." }))] })] }));
|
|
28023
|
-
}
|
|
28024
|
-
// Phase 4: Appointment Selection (Booking)
|
|
28025
|
-
if (phase === "awaiting_booking") {
|
|
28026
|
-
const displayError = state.errorMessage || selectionError;
|
|
28027
|
-
return (jsxs("div", { className: `ai-chat-action-card ai-chat-action-card--closable ${className}`, style: accentStyle, children: [jsxs("div", { className: "ai-chat-action-header", children: [jsx(CalendarIcon, {}), "Select Appointment Time", showCloseButton && (jsx(CloseButton, { onClick: handleDismiss, ariaLabel: "Cancel appointment booking" }))] }), jsxs("div", { className: "ai-chat-action-body", children: [allowTopic && (jsxs("div", { className: "ai-chat-action-field", children: [jsx("label", { htmlFor: `topic-${action.toolCallId}`, className: "ai-chat-action-label", children: "Meeting Topic" }), jsx("input", { id: `topic-${action.toolCallId}`, type: "text", className: "ai-chat-action-input", placeholder: "e.g., Product Demo", value: topic, onChange: (e) => setTopic(e.target.value) })] })), jsxs("div", { className: "ai-chat-action-field", children: [jsx("label", { className: "ai-chat-action-label", children: "Select Date" }), jsx("div", { className: "ai-chat-action-date-grid", children: dates.slice(0, 7).map((date) => (jsx("button", { type: "button", className: `ai-chat-action-date-btn ${selectedDate === date ? "active" : ""}`, onClick: () => {
|
|
28028
|
-
setSelectedDate(date);
|
|
28029
|
-
setSelectedSlot(null);
|
|
28030
|
-
}, children: formatDate(date) }, date))) })] }), selectedDate && slotsForSelectedDate.length > 0 && (jsxs("div", { className: "ai-chat-action-field", children: [jsx("label", { className: "ai-chat-action-label", children: "Select Time" }), jsx("div", { className: "ai-chat-action-time-grid", children: slotsForSelectedDate.map((slot) => (jsx("button", { type: "button", className: `ai-chat-action-time-btn ${selectedSlot?.startTime === slot.startTime ? "active" : ""}`, onClick: () => setSelectedSlot(slot), children: slot.displayTime || new Date(slot.startTime).toLocaleTimeString([], { hour: "numeric", minute: "2-digit" }) }, slot.startTime))) })] })), displayError && (jsx("div", { className: "ai-chat-action-error", children: displayError })), jsx("button", { className: "ai-chat-action-button", type: "button", onClick: handleAppointmentConfirm, disabled: !selectedSlot, children: "Confirm Appointment" }), availableSlots.length === 0 && (jsx("div", { className: "ai-chat-action-hint", children: "No available time slots found." }))] })] }));
|
|
28031
|
-
}
|
|
28032
|
-
// Fallback
|
|
28033
|
-
return null;
|
|
28034
|
-
}
|
|
28035
|
-
|
|
28036
|
-
function truncate(text, maxLength) {
|
|
27610
|
+
function truncate$1(text, maxLength) {
|
|
28037
27611
|
if (text.length <= maxLength)
|
|
28038
27612
|
return text;
|
|
28039
27613
|
return text.slice(0, maxLength).trim() + '...';
|
|
28040
27614
|
}
|
|
28041
|
-
function ExternalLinkIcon() {
|
|
27615
|
+
function ExternalLinkIcon$2() {
|
|
28042
27616
|
return (jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsx("path", { d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" }), jsx("polyline", { points: "15 3 21 3 21 9" }), jsx("line", { x1: "10", y1: "14", x2: "21", y2: "3" })] }));
|
|
28043
27617
|
}
|
|
28044
27618
|
function SingleLinkPreview({ link, onLinkClick, accentColor }) {
|
|
@@ -28057,10 +27631,10 @@ function SingleLinkPreview({ link, onLinkClick, accentColor }) {
|
|
|
28057
27631
|
e.currentTarget.parentElement.style.display = 'none';
|
|
28058
27632
|
} }) })), jsxs("div", { className: "ai-chat-link-preview__content", children: [jsxs("div", { className: "ai-chat-link-preview__site", children: [link.favicon && (jsx("img", { src: link.favicon, alt: "", className: "ai-chat-link-preview__favicon", onError: (e) => {
|
|
28059
27633
|
e.currentTarget.style.display = 'none';
|
|
28060
|
-
} })), jsx("span", { className: "ai-chat-link-preview__domain", children: link.siteName || domain })] }), jsx("h4", { className: "ai-chat-link-preview__title", children: link.title }), link.description && (jsx("p", { className: "ai-chat-link-preview__description", children: truncate(link.description, 120) }))] }), jsx("div", { className: "ai-chat-link-preview__arrow", children: jsx(ExternalLinkIcon, {}) })] }));
|
|
27634
|
+
} })), jsx("span", { className: "ai-chat-link-preview__domain", children: link.siteName || domain })] }), jsx("h4", { className: "ai-chat-link-preview__title", children: link.title }), link.description && (jsx("p", { className: "ai-chat-link-preview__description", children: truncate$1(link.description, 120) }))] }), jsx("div", { className: "ai-chat-link-preview__arrow", children: jsx(ExternalLinkIcon$2, {}) })] }));
|
|
28061
27635
|
}
|
|
28062
27636
|
function LinkPreviewCard({ action, onComplete, accentColor }) {
|
|
28063
|
-
const rawState = action.
|
|
27637
|
+
const rawState = action.input;
|
|
28064
27638
|
const hasCompletedRef = useRef(false);
|
|
28065
27639
|
// Provide safe defaults if state is missing
|
|
28066
27640
|
const state = {
|
|
@@ -28090,11 +27664,12 @@ function LinkPreviewCard({ action, onComplete, accentColor }) {
|
|
|
28090
27664
|
if (state.links.length === 0) {
|
|
28091
27665
|
return null;
|
|
28092
27666
|
}
|
|
28093
|
-
|
|
28094
|
-
|
|
28095
|
-
|
|
28096
|
-
|
|
28097
|
-
|
|
27667
|
+
const gridClass = state.links.length === 1
|
|
27668
|
+
? 'ai-chat-link-preview-grid ai-chat-link-preview-grid--single'
|
|
27669
|
+
: state.links.length === 2
|
|
27670
|
+
? 'ai-chat-link-preview-grid ai-chat-link-preview-grid--double'
|
|
27671
|
+
: 'ai-chat-link-preview-grid ai-chat-link-preview-grid--triple';
|
|
27672
|
+
return (jsxs("div", { className: "ai-chat-link-preview-container", children: [state.context && (jsx("p", { className: "ai-chat-link-preview__context", style: { marginBottom: '8px', fontSize: '0.9em', color: 'var(--ai-chat-fg-muted)' }, children: state.context })), jsx("div", { className: gridClass, children: state.links.map((link, index) => (jsx(SingleLinkPreview, { link: link, onLinkClick: () => handleLinkClick(link.url), accentColor: accentColor }, index))) })] }));
|
|
28098
27673
|
}
|
|
28099
27674
|
|
|
28100
27675
|
function PlayIcon() {
|
|
@@ -28115,7 +27690,7 @@ function getProviderLabel(provider) {
|
|
|
28115
27690
|
}
|
|
28116
27691
|
}
|
|
28117
27692
|
function VideoPlayerCard({ action, onComplete, accentColor }) {
|
|
28118
|
-
const rawState = action.
|
|
27693
|
+
const rawState = action.input;
|
|
28119
27694
|
const hasCompletedRef = useRef(false);
|
|
28120
27695
|
const [isPlaying, setIsPlaying] = useState(false);
|
|
28121
27696
|
// Provide safe defaults if state is missing
|
|
@@ -28226,7 +27801,7 @@ function LocationItem({ location, settings, accentColor, onDirections, showMap =
|
|
|
28226
27801
|
return (jsxs("div", { className: `ai-chat-action-card ai-chat-location-card ${compact ? 'ai-chat-location-card--compact' : ''}`, style: style, children: [showMap && settings.showMap !== false && (jsx("div", { className: "ai-chat-location-card__map", style: { height: effectiveMapHeight }, children: jsx("iframe", { src: getMapEmbedUrl(), allowFullScreen: true, loading: "lazy", referrerPolicy: "no-referrer-when-downgrade", title: `Map of ${location.name}` }) })), jsxs("div", { className: "ai-chat-location-card__content", children: [jsxs("div", { className: "ai-chat-location-card__header", children: [jsx("h4", { className: "ai-chat-location-card__name", children: location.name }), location.type && (jsx("span", { className: "ai-chat-location-card__type", children: location.type })), openStatus !== null && (jsx("span", { className: `ai-chat-location-card__status ai-chat-location-card__status--${openStatus ? 'open' : 'closed'}`, children: openStatus ? 'Open' : 'Closed' }))] }), jsxs("p", { className: "ai-chat-location-card__address", children: [jsx(MapPinIcon, {}), location.address] }), location.description && (jsx("p", { className: "ai-chat-location-card__description", children: location.description })), settings.showHours !== false && location.hours && location.hours.length > 0 && (jsx(HoursDisplay, { hours: location.hours, compact: compact })), settings.showPhone !== false && location.phone && (jsxs("button", { type: "button", className: "ai-chat-location-card__phone", onClick: handleCall, children: [jsx(PhoneIcon, {}), location.phone] })), jsxs("div", { className: "ai-chat-location-card__actions", children: [settings.showDirectionsButton !== false && (jsxs("button", { type: "button", className: "ai-chat-location-card__button", style: accentColor ? { backgroundColor: accentColor } : undefined, onClick: onDirections, children: [jsx(NavigationIcon, {}), compact ? 'Directions' : 'Get Directions'] })), !compact && location.website && (jsxs("a", { href: location.website, target: "_blank", rel: "noopener noreferrer", className: "ai-chat-location-card__link", children: [jsx(GlobeIcon, {}), "Website"] }))] })] })] }));
|
|
28227
27802
|
}
|
|
28228
27803
|
function LocationCard({ action, onComplete, accentColor, maxColumns = 3 }) {
|
|
28229
|
-
const rawState = action.
|
|
27804
|
+
const rawState = action.input;
|
|
28230
27805
|
const hasCompletedRef = useRef(false);
|
|
28231
27806
|
const state = {
|
|
28232
27807
|
locations: rawState?.locations || [],
|
|
@@ -28308,7 +27883,7 @@ function ContactItem({ contact, settings, accentColor, onEmail, onPhone, compact
|
|
|
28308
27883
|
})()] }), jsxs("div", { className: "ai-chat-contact-card__info", children: [jsx("h4", { className: "ai-chat-contact-card__name", children: contact.name }), settings.showRole !== false && contact.role && (jsx("p", { className: "ai-chat-contact-card__role", children: contact.role })), jsxs("div", { className: "ai-chat-contact-card__details", children: [settings.showEmail !== false && contact.email && (jsx("a", { href: `mailto:${contact.email}`, className: "ai-chat-contact-card__detail", onClick: onEmail, children: contact.email })), settings.showPhone !== false && contact.phone && (jsx("a", { href: `tel:${contact.phone}`, className: "ai-chat-contact-card__detail", onClick: onPhone, children: contact.phone }))] }), settings.showResponsibilities !== false && contact.responsibilities && contact.responsibilities.length > 0 && !compact && (jsxs("div", { className: "ai-chat-contact-card__responsibilities", children: [contact.responsibilities.slice(0, 3).map((resp, idx) => (jsx("span", { className: "ai-chat-contact-card__responsibility-tag", children: resp }, idx))), contact.responsibilities.length > 3 && (jsxs("span", { className: "ai-chat-contact-card__responsibility-more", children: ["+", contact.responsibilities.length - 3, " more"] }))] }))] })] }));
|
|
28309
27884
|
}
|
|
28310
27885
|
function ContactCard({ action, onComplete, accentColor, maxColumns = 3 }) {
|
|
28311
|
-
const rawState = action.
|
|
27886
|
+
const rawState = action.input;
|
|
28312
27887
|
const hasCompletedRef = useRef(false);
|
|
28313
27888
|
const state = {
|
|
28314
27889
|
contacts: rawState?.contacts || [],
|
|
@@ -28343,11 +27918,37 @@ function ContactCard({ action, onComplete, accentColor, maxColumns = 3 }) {
|
|
|
28343
27918
|
}, children: contacts.map((contact) => (jsx(ContactItem, { contact: contact, settings: settings, accentColor: accentColor, onEmail: handleContact, onPhone: handleContact, compact: true, layout: settings.layout || 'vertical' }, contact.id))) })] }));
|
|
28344
27919
|
}
|
|
28345
27920
|
|
|
27921
|
+
const CloseButton = ({ onClick, className = "", ariaLabel = "Close", }) => {
|
|
27922
|
+
return (jsx("button", { type: "button", className: `ai-chat-action-close-btn ${className}`, onClick: onClick, "aria-label": ariaLabel, children: jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", stroke: "currentColor", strokeWidth: "2", children: jsx("path", { d: "M1 1L13 13M1 13L13 1" }) }) }));
|
|
27923
|
+
};
|
|
27924
|
+
|
|
27925
|
+
function FormIcon(props) {
|
|
27926
|
+
return (jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", ...props, children: [jsx("path", { d: "M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z" }), jsx("polyline", { points: "14,2 14,8 20,8" }), jsx("line", { x1: "16", y1: "13", x2: "8", y2: "13" }), jsx("line", { x1: "16", y1: "17", x2: "8", y2: "17" }), jsx("line", { x1: "10", y1: "9", x2: "8", y2: "9" })] }));
|
|
27927
|
+
}
|
|
27928
|
+
function CheckIcon(props) {
|
|
27929
|
+
return (jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", ...props, children: jsx("polyline", { points: "20,6 9,17 4,12" }) }));
|
|
27930
|
+
}
|
|
27931
|
+
function WarningIcon(props) {
|
|
27932
|
+
return (jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", ...props, children: [jsx("path", { d: "M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z" }), jsx("line", { x1: "12", y1: "9", x2: "12", y2: "13" }), jsx("line", { x1: "12", y1: "17", x2: "12.01", y2: "17" })] }));
|
|
27933
|
+
}
|
|
27934
|
+
function SkipIcon(props) {
|
|
27935
|
+
return (jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", ...props, children: [jsx("polygon", { points: "5,4 15,12 5,20 5,4" }), jsx("line", { x1: "19", y1: "5", x2: "19", y2: "19" })] }));
|
|
27936
|
+
}
|
|
27937
|
+
function BackArrowIcon(props) {
|
|
27938
|
+
return (jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", ...props, children: jsx("polyline", { points: "15,18 9,12 15,6" }) }));
|
|
27939
|
+
}
|
|
27940
|
+
|
|
28346
27941
|
function FormCard({ action, onComplete, onDismiss, accentColor }) {
|
|
28347
|
-
const state = action.
|
|
27942
|
+
const state = action.input;
|
|
28348
27943
|
const [currentStep, setCurrentStep] = useState(0);
|
|
28349
27944
|
const [answers, setAnswers] = useState({});
|
|
28350
27945
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
27946
|
+
const [localStatus, setLocalStatus] = useState(null);
|
|
27947
|
+
// If action is already done, show simple completion indicator
|
|
27948
|
+
// The agent's response will convey what happened
|
|
27949
|
+
if (action.done) {
|
|
27950
|
+
return (jsx("div", { className: "ai-chat-form-card ai-chat-form-card--completed", children: jsxs("div", { className: "ai-chat-form-card__header", children: [jsx("span", { className: "ai-chat-form-card__icon", children: jsx(CheckIcon, {}) }), jsx("span", { className: "ai-chat-form-card__title", children: "Form completed" })] }) }));
|
|
27951
|
+
}
|
|
28351
27952
|
const questions = state.questions || [];
|
|
28352
27953
|
const currentQuestion = questions[currentStep];
|
|
28353
27954
|
const totalQuestions = questions.length;
|
|
@@ -28368,6 +27969,7 @@ function FormCard({ action, onComplete, onDismiss, accentColor }) {
|
|
|
28368
27969
|
if (!onComplete)
|
|
28369
27970
|
return;
|
|
28370
27971
|
setIsSubmitting(true);
|
|
27972
|
+
setLocalStatus('submitted');
|
|
28371
27973
|
const formattedAnswers = Object.entries(answers).map(([questionId, value]) => ({
|
|
28372
27974
|
questionId,
|
|
28373
27975
|
value,
|
|
@@ -28381,6 +27983,7 @@ function FormCard({ action, onComplete, onDismiss, accentColor }) {
|
|
|
28381
27983
|
const handleSkip = () => {
|
|
28382
27984
|
if (!onComplete || !state.settings.allowSkip)
|
|
28383
27985
|
return;
|
|
27986
|
+
setLocalStatus('skipped');
|
|
28384
27987
|
onComplete(action.toolCallId, {
|
|
28385
27988
|
...state,
|
|
28386
27989
|
status: 'skipped',
|
|
@@ -28411,24 +28014,26 @@ function FormCard({ action, onComplete, onDismiss, accentColor }) {
|
|
|
28411
28014
|
const handleDismiss = () => {
|
|
28412
28015
|
onDismiss?.(action.toolCallId);
|
|
28413
28016
|
};
|
|
28017
|
+
// Use local status if available (for immediate UI feedback), otherwise use state.status
|
|
28018
|
+
const effectiveStatus = localStatus || state.status;
|
|
28414
28019
|
// Error state
|
|
28415
|
-
if (
|
|
28416
|
-
return (jsxs("div", { className: "ai-chat-form-card ai-chat-form-card--error", children: [jsxs("div", { className: "ai-chat-form-card__header", children: [jsx("span", { className: "ai-chat-form-card__icon", children:
|
|
28020
|
+
if (effectiveStatus === 'error') {
|
|
28021
|
+
return (jsxs("div", { className: "ai-chat-form-card ai-chat-form-card--error", children: [jsxs("div", { className: "ai-chat-form-card__header", children: [jsx("span", { className: "ai-chat-form-card__icon", children: jsx(WarningIcon, {}) }), jsx("span", { className: "ai-chat-form-card__title", children: "Form Error" })] }), jsx("p", { className: "ai-chat-form-card__error", children: state.error || 'Could not load form' })] }));
|
|
28417
28022
|
}
|
|
28418
28023
|
// Submitted state
|
|
28419
|
-
if (
|
|
28420
|
-
return (jsxs("div", { className: "ai-chat-form-card ai-chat-form-card--submitted", children: [jsxs("div", { className: "ai-chat-form-card__header", children: [jsx("span", { className: "ai-chat-form-card__icon", children:
|
|
28024
|
+
if (effectiveStatus === 'submitted') {
|
|
28025
|
+
return (jsxs("div", { className: "ai-chat-form-card ai-chat-form-card--submitted", children: [jsxs("div", { className: "ai-chat-form-card__header", children: [jsx("span", { className: "ai-chat-form-card__icon", children: jsx(CheckIcon, {}) }), jsx("span", { className: "ai-chat-form-card__title", children: state.title })] }), jsx("p", { className: "ai-chat-form-card__success", children: state.settings.successMessage || 'Thank you for your response!' })] }));
|
|
28421
28026
|
}
|
|
28422
28027
|
// Skipped state
|
|
28423
|
-
if (
|
|
28424
|
-
return (jsxs("div", { className: "ai-chat-form-card ai-chat-form-card--skipped", children: [jsxs("div", { className: "ai-chat-form-card__header", children: [jsx("span", { className: "ai-chat-form-card__icon", children:
|
|
28028
|
+
if (effectiveStatus === 'skipped') {
|
|
28029
|
+
return (jsxs("div", { className: "ai-chat-form-card ai-chat-form-card--skipped", children: [jsxs("div", { className: "ai-chat-form-card__header", children: [jsx("span", { className: "ai-chat-form-card__icon", children: jsx(SkipIcon, {}) }), jsx("span", { className: "ai-chat-form-card__title", children: state.title })] }), jsx("p", { className: "ai-chat-form-card__skipped-text", children: "Form skipped" })] }));
|
|
28425
28030
|
}
|
|
28426
28031
|
// No questions
|
|
28427
28032
|
if (totalQuestions === 0) {
|
|
28428
|
-
return (jsxs("div", { className: "ai-chat-form-card ai-chat-form-card--empty", children: [jsxs("div", { className: "ai-chat-form-card__header", children: [jsx("span", { className: "ai-chat-form-card__icon", children:
|
|
28033
|
+
return (jsxs("div", { className: "ai-chat-form-card ai-chat-form-card--empty", children: [jsxs("div", { className: "ai-chat-form-card__header", children: [jsx("span", { className: "ai-chat-form-card__icon", children: jsx(FormIcon, {}) }), jsx("span", { className: "ai-chat-form-card__title", children: state.title })] }), jsx("p", { className: "ai-chat-form-card__empty-text", children: "This form has no questions." })] }));
|
|
28429
28034
|
}
|
|
28430
|
-
const showCloseButton =
|
|
28431
|
-
return (jsxs("div", { className: `ai-chat-form-card${showCloseButton ? " ai-chat-form-card--closable" : ""}`, children: [jsxs("div", { className: "ai-chat-form-card__header", children: [jsx("span", { className: "ai-chat-form-card__icon", children:
|
|
28035
|
+
const showCloseButton = effectiveStatus === "displaying" && !action.done && Boolean(onDismiss);
|
|
28036
|
+
return (jsxs("div", { className: `ai-chat-form-card${showCloseButton ? " ai-chat-form-card--closable" : ""}`, children: [jsxs("div", { className: "ai-chat-form-card__header", children: [jsx("span", { className: "ai-chat-form-card__icon", children: jsx(FormIcon, {}) }), jsx("span", { className: "ai-chat-form-card__title", children: state.title }), showCloseButton && (jsx(CloseButton, { onClick: handleDismiss, ariaLabel: "Close form" }))] }), state.description && (jsx("p", { className: "ai-chat-form-card__description", children: state.description })), state.context && (jsx("p", { className: "ai-chat-form-card__context", children: state.context })), state.settings.showProgress && (jsxs("div", { className: "ai-chat-form-card__progress", children: [jsx("div", { className: "ai-chat-form-card__progress-bar", style: {
|
|
28432
28037
|
width: `${((currentStep + 1) / totalQuestions) * 100}%`,
|
|
28433
28038
|
backgroundColor: accentColor || 'var(--ai-chat-accent-color, #3b82f6)',
|
|
28434
28039
|
} }), jsxs("span", { className: "ai-chat-form-card__progress-text", children: [currentStep + 1, " of ", totalQuestions] })] })), jsxs("div", { className: "ai-chat-form-card__question", children: [jsxs("p", { className: "ai-chat-form-card__question-text", children: [currentQuestion.text, currentQuestion.required && jsx("span", { className: "ai-chat-form-card__required", children: "*" })] }), jsxs("div", { className: "ai-chat-form-card__answer", children: [currentQuestion.type === 'text' && (jsx("textarea", { className: "ai-chat-form-card__textarea", placeholder: currentQuestion.placeholder || 'Type your answer...', value: answers[currentQuestion.id] || '', onChange: (e) => handleAnswerChange(currentQuestion.id, e.target.value), rows: 3 })), currentQuestion.type === 'single_choice' && currentQuestion.options && (jsx("div", { className: "ai-chat-form-card__options", children: currentQuestion.options.map((option) => (jsxs("label", { className: "ai-chat-form-card__option", children: [jsx("input", { type: "radio", name: currentQuestion.id, value: option.value, checked: answers[currentQuestion.id] === option.value, onChange: () => handleAnswerChange(currentQuestion.id, option.value) }), jsx("span", { className: "ai-chat-form-card__option-text", children: option.text })] }, option.id))) })), currentQuestion.type === 'multiple_choice' && currentQuestion.options && (jsx("div", { className: "ai-chat-form-card__options", children: currentQuestion.options.map((option) => {
|
|
@@ -28454,9 +28059,37 @@ function FormCard({ action, onComplete, onDismiss, accentColor }) {
|
|
|
28454
28059
|
}, children: isSubmitting ? 'Submitting...' : (state.settings.submitButtonText || 'Submit') }))] })] }));
|
|
28455
28060
|
}
|
|
28456
28061
|
|
|
28062
|
+
function EmailPhase({ accentColor, onDismiss, showCloseButton, onSubmit, isLoading, error, }) {
|
|
28063
|
+
const [emailInput, setEmailInput] = useState('');
|
|
28064
|
+
const [localError, setLocalError] = useState(null);
|
|
28065
|
+
const handleSubmit = () => {
|
|
28066
|
+
const trimmedEmail = emailInput.trim();
|
|
28067
|
+
if (!trimmedEmail) {
|
|
28068
|
+
setLocalError('Please enter your email address');
|
|
28069
|
+
return;
|
|
28070
|
+
}
|
|
28071
|
+
if (!trimmedEmail.includes('@')) {
|
|
28072
|
+
setLocalError('Please enter a valid email address');
|
|
28073
|
+
return;
|
|
28074
|
+
}
|
|
28075
|
+
setLocalError(null);
|
|
28076
|
+
onSubmit(trimmedEmail);
|
|
28077
|
+
};
|
|
28078
|
+
const displayError = localError || error;
|
|
28079
|
+
return (jsxs("div", { className: `ai-chat-booking-card${showCloseButton ? ' ai-chat-booking-card--closable' : ''}`, children: [jsxs("div", { className: "ai-chat-booking-card__header", children: [jsx("span", { className: "ai-chat-booking-card__title", children: "Enter Your Email" }), showCloseButton && onDismiss && jsx(CloseButton, { onClick: onDismiss, ariaLabel: "Cancel booking" })] }), jsxs("div", { className: "ai-chat-booking-card__content", children: [jsx("p", { className: "ai-chat-booking-card__description", children: "Please enter your email address to start the booking process." }), displayError && (jsx("p", { className: "ai-chat-booking-card__error", children: displayError })), jsx("input", { type: "email", className: "ai-chat-booking-card__input", placeholder: "your@email.com", value: emailInput, onChange: (e) => {
|
|
28080
|
+
setEmailInput(e.target.value);
|
|
28081
|
+
setLocalError(null);
|
|
28082
|
+
}, onKeyDown: (e) => {
|
|
28083
|
+
if (e.key === 'Enter' && !isLoading) {
|
|
28084
|
+
handleSubmit();
|
|
28085
|
+
}
|
|
28086
|
+
}, disabled: isLoading }), jsx("button", { className: "ai-chat-booking-card__btn ai-chat-booking-card__btn--primary", onClick: handleSubmit, disabled: !emailInput.trim() || isLoading, children: isLoading ? 'Sending...' : 'Continue' })] })] }));
|
|
28087
|
+
}
|
|
28088
|
+
|
|
28457
28089
|
function Skeleton({ width, height, borderRadius = '4px' }) {
|
|
28458
28090
|
return (jsx("div", { className: "ai-chat-action-skeleton-item", style: { width, height, borderRadius } }));
|
|
28459
28091
|
}
|
|
28092
|
+
|
|
28460
28093
|
function PinInputGroup({ values, onChange, disabled }) {
|
|
28461
28094
|
const inputRefs = useRef([]);
|
|
28462
28095
|
const handleChange = (index, value) => {
|
|
@@ -28488,299 +28121,853 @@ function PinInputGroup({ values, onChange, disabled }) {
|
|
|
28488
28121
|
inputRefs.current[index] = el;
|
|
28489
28122
|
}, type: "text", inputMode: "numeric", maxLength: 1, className: "ai-chat-pin-input", value: value, onChange: (e) => handleChange(index, e.target.value), onKeyDown: (e) => handleKeyDown(index, e), onPaste: handlePaste, disabled: disabled, autoFocus: index === 0 }, index))) }));
|
|
28490
28123
|
}
|
|
28491
|
-
|
|
28492
|
-
|
|
28493
|
-
const [emailInput, setEmailInput] = useState('');
|
|
28124
|
+
|
|
28125
|
+
function OtpPhase({ accentColor, onDismiss, showCloseButton, email, onSubmit, onResend, isLoading, error, }) {
|
|
28494
28126
|
const [otpValues, setOtpValues] = useState(Array(6).fill(''));
|
|
28495
|
-
const [
|
|
28496
|
-
const [
|
|
28497
|
-
const
|
|
28498
|
-
|
|
28499
|
-
|
|
28500
|
-
|
|
28501
|
-
if (!onComplete)
|
|
28127
|
+
const [localError, setLocalError] = useState(null);
|
|
28128
|
+
const [isResending, setIsResending] = useState(false);
|
|
28129
|
+
const handleSubmit = () => {
|
|
28130
|
+
const code = otpValues.join('');
|
|
28131
|
+
if (code.length !== 6) {
|
|
28132
|
+
setLocalError('Please enter the 6-digit code');
|
|
28502
28133
|
return;
|
|
28503
|
-
setIsSubmitting(true);
|
|
28504
|
-
setTimeout(() => {
|
|
28505
|
-
onComplete(action.toolCallId, { ...state, ...newState, errorMessage: null });
|
|
28506
|
-
}, delay);
|
|
28507
|
-
};
|
|
28508
|
-
useEffect(() => {
|
|
28509
|
-
setIsSubmitting(false);
|
|
28510
|
-
if (phase === 'awaiting_email') {
|
|
28511
|
-
setEmailError(null);
|
|
28512
|
-
setOtpError(null);
|
|
28513
|
-
}
|
|
28514
|
-
else if (phase === 'awaiting_otp') {
|
|
28515
|
-
setOtpError(state.errorMessage || null);
|
|
28516
|
-
setEmailError(null);
|
|
28517
|
-
setOtpValues(Array(6).fill(''));
|
|
28518
|
-
if (state.email) {
|
|
28519
|
-
setEmailInput(state.email);
|
|
28520
|
-
}
|
|
28521
28134
|
}
|
|
28522
|
-
|
|
28523
|
-
|
|
28524
|
-
|
|
28135
|
+
setLocalError(null);
|
|
28136
|
+
onSubmit(code);
|
|
28137
|
+
};
|
|
28138
|
+
const handleResend = async () => {
|
|
28139
|
+
setIsResending(true);
|
|
28140
|
+
setLocalError(null);
|
|
28141
|
+
try {
|
|
28142
|
+
await onResend();
|
|
28525
28143
|
}
|
|
28526
|
-
|
|
28527
|
-
|
|
28528
|
-
const renderSkeleton = () => (jsx("div", { className: "ai-chat-booking-card", children: jsxs("div", { className: "ai-chat-action-skeleton-content", children: [jsxs("div", { className: "ai-chat-action-skeleton-header", children: [jsx(Skeleton, { width: "28px", height: "28px", borderRadius: "50%" }), jsx(Skeleton, { width: "180px", height: "20px", borderRadius: "4px" })] }), jsxs("div", { className: "ai-chat-action-skeleton-box", children: [jsx(Skeleton, { width: "60px", height: "12px", borderRadius: "4px" }), jsx(Skeleton, { width: "120px", height: "18px", borderRadius: "4px" })] }), jsx(Skeleton, { width: "100%", height: "44px", borderRadius: "999px" })] }) }));
|
|
28529
|
-
const handleEmailSubmit = () => {
|
|
28530
|
-
const trimmedEmail = emailInput.trim();
|
|
28531
|
-
if (!trimmedEmail) {
|
|
28532
|
-
setEmailError('Please enter your email address');
|
|
28533
|
-
return;
|
|
28144
|
+
finally {
|
|
28145
|
+
setIsResending(false);
|
|
28534
28146
|
}
|
|
28535
|
-
setEmailError(null);
|
|
28536
|
-
setEmailInput(trimmedEmail);
|
|
28537
|
-
handleSubmit({ email: trimmedEmail });
|
|
28538
28147
|
};
|
|
28539
|
-
const
|
|
28540
|
-
|
|
28541
|
-
|
|
28542
|
-
|
|
28148
|
+
const displayError = localError || error;
|
|
28149
|
+
return (jsxs("div", { className: `ai-chat-booking-card${showCloseButton ? ' ai-chat-booking-card--closable' : ''}`, children: [jsxs("div", { className: "ai-chat-booking-card__header", children: [jsx("span", { className: "ai-chat-booking-card__title", children: "Verify Your Email" }), showCloseButton && onDismiss && jsx(CloseButton, { onClick: onDismiss, ariaLabel: "Cancel booking" })] }), jsxs("div", { className: "ai-chat-booking-card__content", children: [jsxs("p", { className: "ai-chat-booking-card__description", children: ["Enter the verification code sent to ", jsx("strong", { children: email })] }), jsx(PinInputGroup, { values: otpValues, onChange: (newValues) => {
|
|
28150
|
+
setOtpValues(newValues);
|
|
28151
|
+
setLocalError(null);
|
|
28152
|
+
// Auto-submit when all 6 digits are entered
|
|
28153
|
+
if (newValues.every((value) => value.length === 1) && !isLoading) {
|
|
28154
|
+
onSubmit(newValues.join(''));
|
|
28155
|
+
}
|
|
28156
|
+
}, disabled: isLoading }), displayError && (jsx("p", { className: "ai-chat-booking-card__error", children: displayError })), jsx("button", { className: "ai-chat-booking-card__btn ai-chat-booking-card__btn--primary", onClick: handleSubmit, disabled: otpValues.join('').length !== 6 || isLoading, children: isLoading ? 'Verifying...' : 'Verify' }), jsx("button", { className: "ai-chat-booking-card__btn ai-chat-booking-card__btn--secondary", onClick: handleResend, disabled: isLoading || isResending, children: isResending ? 'Sending...' : 'Resend Code' })] })] }));
|
|
28157
|
+
}
|
|
28158
|
+
|
|
28159
|
+
function ContactSelectionPhase({ accentColor, onDismiss, showCloseButton, contacts, onSelect, }) {
|
|
28160
|
+
return (jsxs("div", { className: `ai-chat-booking-card${showCloseButton ? ' ai-chat-booking-card--closable' : ''}`, children: [jsxs("div", { className: "ai-chat-booking-card__header", children: [jsx("span", { className: "ai-chat-booking-card__title", children: "Select Contact" }), showCloseButton && onDismiss && jsx(CloseButton, { onClick: onDismiss, ariaLabel: "Cancel booking" })] }), jsxs("div", { className: "ai-chat-booking-card__content", children: [jsx("p", { className: "ai-chat-booking-card__description", children: "Choose who you would like to book an appointment with:" }), jsx("div", { className: "ai-chat-booking-card__list", children: contacts.map((contact) => (jsxs("button", { className: "ai-chat-booking-card__list-item", onClick: () => onSelect(contact.id), children: [jsxs("div", { className: "ai-chat-booking-card__list-item-content", children: [jsx("span", { className: "ai-chat-booking-card__list-item-name", children: contact.name }), contact.role && (jsx("span", { className: "ai-chat-booking-card__list-item-role", children: contact.role }))] }), jsx("span", { className: "ai-chat-booking-card__list-item-arrow", "aria-hidden": "true" })] }, contact.id))) })] })] }));
|
|
28161
|
+
}
|
|
28162
|
+
|
|
28163
|
+
// Calendar icon SVG
|
|
28164
|
+
const CalendarIcon = () => (jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsx("rect", { x: "3", y: "4", width: "18", height: "18", rx: "2", ry: "2" }), jsx("line", { x1: "16", y1: "2", x2: "16", y2: "6" }), jsx("line", { x1: "8", y1: "2", x2: "8", y2: "6" }), jsx("line", { x1: "3", y1: "10", x2: "21", y2: "10" })] }));
|
|
28165
|
+
// List icon SVG
|
|
28166
|
+
const ListIcon = () => (jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsx("line", { x1: "8", y1: "6", x2: "21", y2: "6" }), jsx("line", { x1: "8", y1: "12", x2: "21", y2: "12" }), jsx("line", { x1: "8", y1: "18", x2: "21", y2: "18" }), jsx("line", { x1: "3", y1: "6", x2: "3.01", y2: "6" }), jsx("line", { x1: "3", y1: "12", x2: "3.01", y2: "12" }), jsx("line", { x1: "3", y1: "18", x2: "3.01", y2: "18" })] }));
|
|
28167
|
+
function OptionsPhase({ onDismiss, showCloseButton, appointments, maxAppointments, onBookNew, onViewAppointments, isCancelling, }) {
|
|
28168
|
+
const activeAppointments = appointments.filter(a => a.status !== 'cancelled' && a.status !== 'declined');
|
|
28169
|
+
const canBookNew = activeAppointments.length < maxAppointments;
|
|
28170
|
+
const hasAppointments = appointments.length > 0;
|
|
28171
|
+
return (jsxs("div", { className: `ai-chat-booking-card${showCloseButton ? ' ai-chat-booking-card--closable' : ''}`, children: [jsxs("div", { className: "ai-chat-booking-card__header", children: [jsx("span", { className: "ai-chat-booking-card__title", children: "Booking Options" }), showCloseButton && onDismiss && jsx(CloseButton, { onClick: onDismiss, ariaLabel: "Cancel booking" })] }), jsxs("div", { className: "ai-chat-booking-card__content", children: [!canBookNew && (jsxs("p", { className: "ai-chat-booking-card__warning", children: ["You have reached the maximum number of appointments (", maxAppointments, "). Please cancel an existing appointment to book a new one."] })), jsxs("div", { className: "ai-chat-booking-card__options", children: [jsxs("button", { className: "ai-chat-booking-card__option-btn", onClick: onBookNew, disabled: !canBookNew || isCancelling, children: [jsx("span", { className: "ai-chat-booking-card__option-icon", children: jsx(CalendarIcon, {}) }), jsx("span", { className: "ai-chat-booking-card__option-text", children: "Book New Appointment" })] }), hasAppointments && (jsxs("button", { className: "ai-chat-booking-card__option-btn", onClick: onViewAppointments, disabled: isCancelling, children: [jsx("span", { className: "ai-chat-booking-card__option-icon", children: jsx(ListIcon, {}) }), jsxs("span", { className: "ai-chat-booking-card__option-text", children: ["View My Appointments (", activeAppointments.length, ")"] })] }))] })] })] }));
|
|
28172
|
+
}
|
|
28173
|
+
|
|
28174
|
+
function ViewAppointmentsPhase({ accentColor, onDismiss, showCloseButton, appointments, onBack, onCancelAppointment, isCancelling, }) {
|
|
28175
|
+
const [cancellingId, setCancellingId] = useState(null);
|
|
28176
|
+
const handleCancel = async (appointmentId) => {
|
|
28177
|
+
setCancellingId(appointmentId);
|
|
28178
|
+
await onCancelAppointment(appointmentId);
|
|
28179
|
+
setCancellingId(null);
|
|
28180
|
+
};
|
|
28181
|
+
const activeAppointments = appointments.filter(a => a.status !== 'cancelled');
|
|
28182
|
+
return (jsxs("div", { className: `ai-chat-booking-card${showCloseButton ? ' ai-chat-booking-card--closable' : ''}`, children: [jsxs("div", { className: "ai-chat-booking-card__header", children: [jsx("button", { className: "ai-chat-booking-card__back-btn", onClick: onBack, children: "Back" }), jsx("span", { className: "ai-chat-booking-card__title", children: "My Appointments" }), showCloseButton && onDismiss && jsx(CloseButton, { onClick: onDismiss, ariaLabel: "Cancel booking" })] }), jsx("div", { className: "ai-chat-booking-card__content", children: activeAppointments.length === 0 ? (jsx("p", { className: "ai-chat-booking-card__empty", children: "No appointments found." })) : (jsx("div", { className: "ai-chat-booking-card__appointments", children: activeAppointments.map((apt) => (jsxs("div", { className: "ai-chat-booking-card__appointment", children: [jsxs("div", { className: "ai-chat-booking-card__appointment-info", children: [jsx("span", { className: "ai-chat-booking-card__appointment-subject", children: apt.subject }), jsx("span", { className: "ai-chat-booking-card__appointment-contact", children: apt.contactName }), jsx("span", { className: "ai-chat-booking-card__appointment-time", children: apt.displayTime }), jsx("span", { className: `ai-chat-booking-card__appointment-status ai-chat-booking-card__appointment-status--${apt.status}`, children: apt.status === 'pending' ? 'Pending Approval' : apt.status })] }), jsxs("div", { className: "ai-chat-booking-card__appointment-actions", children: [apt.teamsLink && (jsx("a", { href: apt.teamsLink, target: "_blank", rel: "noopener noreferrer", className: "ai-chat-booking-card__appointment-link", children: "Join Meeting" })), apt.googleMeetLink && (jsx("a", { href: apt.googleMeetLink, target: "_blank", rel: "noopener noreferrer", className: "ai-chat-booking-card__appointment-link", children: "Join Meeting" })), jsx("button", { className: "ai-chat-booking-card__appointment-cancel", onClick: () => handleCancel(apt.id), disabled: isCancelling || cancellingId === apt.id, children: cancellingId === apt.id ? 'Cancelling...' : 'Cancel' })] })] }, apt.id))) })) })] }));
|
|
28183
|
+
}
|
|
28184
|
+
|
|
28185
|
+
function SlotSelectionPhase({ onDismiss, showCloseButton, slots, contactName, onSelect, onBack, isLoading, error, }) {
|
|
28186
|
+
// Group slots by date
|
|
28187
|
+
const slotsByDate = useMemo(() => {
|
|
28188
|
+
const grouped = {};
|
|
28189
|
+
for (const slot of slots) {
|
|
28190
|
+
const date = slot.displayDate;
|
|
28191
|
+
if (!grouped[date]) {
|
|
28192
|
+
grouped[date] = [];
|
|
28193
|
+
}
|
|
28194
|
+
grouped[date].push(slot);
|
|
28195
|
+
}
|
|
28196
|
+
return grouped;
|
|
28197
|
+
}, [slots]);
|
|
28198
|
+
const dates = Object.keys(slotsByDate);
|
|
28199
|
+
return (jsxs("div", { className: `ai-chat-booking-card${showCloseButton ? ' ai-chat-booking-card--closable' : ''}`, children: [jsxs("div", { className: "ai-chat-booking-card__header", children: [jsx("button", { className: "ai-chat-booking-card__back-btn", onClick: onBack, children: "Back" }), jsx("span", { className: "ai-chat-booking-card__title", children: "Select Time Slot" }), showCloseButton && onDismiss && jsx(CloseButton, { onClick: onDismiss, ariaLabel: "Cancel booking" })] }), jsxs("div", { className: "ai-chat-booking-card__content", children: [contactName && (jsxs("p", { className: "ai-chat-booking-card__description", children: ["Available times for ", jsx("strong", { children: contactName }), ":"] })), error && (jsx("p", { className: "ai-chat-booking-card__error", children: error })), slots.length === 0 ? (jsx("p", { className: "ai-chat-booking-card__empty", children: "No available time slots found. Please try again later." })) : (jsx("div", { className: "ai-chat-booking-card__slots-container", children: dates.map((date) => (jsxs("div", { className: "ai-chat-booking-card__date-group", children: [jsx("h4", { className: "ai-chat-booking-card__date-header", children: date }), jsx("div", { className: "ai-chat-booking-card__slots", children: slotsByDate[date].map((slot) => (jsx("button", { className: "ai-chat-booking-card__slot", onClick: () => onSelect({ startTime: slot.startTime, endTime: slot.endTime }), disabled: isLoading, children: slot.displayTime }, `${slot.startTime}-${slot.endTime}`))) })] }, date))) }))] })] }));
|
|
28200
|
+
}
|
|
28201
|
+
|
|
28202
|
+
function ConfirmationPhase({ onDismiss, showCloseButton, contactName, slotDisplay, subjectMode, predefinedSubjects, onConfirm, onBack, isLoading, error, }) {
|
|
28203
|
+
const [subject, setSubject] = useState('');
|
|
28204
|
+
const [selectedPredefined, setSelectedPredefined] = useState('');
|
|
28205
|
+
const [localError, setLocalError] = useState(null);
|
|
28206
|
+
const handleConfirm = () => {
|
|
28207
|
+
const finalSubject = subjectMode === 'predefined' ? selectedPredefined : subject.trim();
|
|
28208
|
+
if (!finalSubject) {
|
|
28209
|
+
setLocalError('Please enter a subject for your appointment');
|
|
28543
28210
|
return;
|
|
28544
28211
|
}
|
|
28545
|
-
|
|
28546
|
-
|
|
28547
|
-
handleSubmit({ otpCode: code, email: resolvedEmail });
|
|
28212
|
+
setLocalError(null);
|
|
28213
|
+
onConfirm(finalSubject);
|
|
28548
28214
|
};
|
|
28549
|
-
|
|
28550
|
-
|
|
28551
|
-
|
|
28552
|
-
|
|
28553
|
-
|
|
28554
|
-
|
|
28555
|
-
|
|
28556
|
-
|
|
28557
|
-
|
|
28558
|
-
|
|
28559
|
-
|
|
28560
|
-
|
|
28561
|
-
|
|
28562
|
-
return (jsxs("div", { className: "ai-chat-booking-card ai-chat-booking-card--error", children: [jsxs("div", { className: "ai-chat-booking-card__header", children: [jsx("span", { className: "ai-chat-booking-card__icon", children: "\u26A0\uFE0F" }), jsx("span", { className: "ai-chat-booking-card__title", children: "Booking Error" })] }), state.errorMessage && (jsx("p", { className: "ai-chat-booking-card__error", children: state.errorMessage }))] }));
|
|
28563
|
-
}
|
|
28564
|
-
if (isSubmitting || isWaitingForBackend) {
|
|
28565
|
-
return renderSkeleton();
|
|
28566
|
-
}
|
|
28567
|
-
// ========================================
|
|
28568
|
-
// Phase: Email Collection
|
|
28569
|
-
// ========================================
|
|
28570
|
-
if (phase === 'awaiting_email') {
|
|
28571
|
-
const displayError = emailError || state.errorMessage;
|
|
28572
|
-
return (jsxs("div", { className: "ai-chat-booking-card", children: [jsxs("div", { className: "ai-chat-booking-card__header", children: [jsx("span", { className: "ai-chat-booking-card__icon", children: "\uD83D\uDCE7" }), jsx("span", { className: "ai-chat-booking-card__title", children: "Enter Your Email" })] }), jsxs("div", { className: "ai-chat-booking-card__content", children: [jsx("p", { className: "ai-chat-booking-card__description", children: "Please enter your email address to start the booking process." }), displayError && (jsx("p", { className: "ai-chat-booking-card__error", children: displayError })), jsx("input", { type: "email", className: "ai-chat-booking-card__input", placeholder: "your@email.com", value: emailInput, onChange: (e) => {
|
|
28573
|
-
setEmailInput(e.target.value);
|
|
28574
|
-
setEmailError(null);
|
|
28575
|
-
}, onKeyDown: (e) => {
|
|
28576
|
-
if (e.key === 'Enter') {
|
|
28577
|
-
handleEmailSubmit();
|
|
28578
|
-
}
|
|
28579
|
-
} }), jsx("button", { className: "ai-chat-booking-card__btn ai-chat-booking-card__btn--primary", onClick: handleEmailSubmit, disabled: !emailInput.trim(), style: {
|
|
28580
|
-
backgroundColor: accentColor || undefined,
|
|
28581
|
-
borderColor: accentColor || undefined,
|
|
28582
|
-
}, children: "Continue" })] })] }));
|
|
28583
|
-
}
|
|
28584
|
-
// ========================================
|
|
28585
|
-
// Phase: OTP Verification
|
|
28586
|
-
// ========================================
|
|
28587
|
-
if (phase === 'awaiting_otp') {
|
|
28588
|
-
const displayError = otpError || state.errorMessage;
|
|
28589
|
-
return (jsxs("div", { className: "ai-chat-booking-card", children: [jsxs("div", { className: "ai-chat-booking-card__header", children: [jsx("span", { className: "ai-chat-booking-card__icon", children: "\uD83D\uDD10" }), jsx("span", { className: "ai-chat-booking-card__title", children: "Verify Your Email" })] }), jsxs("div", { className: "ai-chat-booking-card__content", children: [jsxs("p", { className: "ai-chat-booking-card__description", children: ["Enter the verification code sent to ", jsx("strong", { children: state.email })] }), jsx(PinInputGroup, { values: otpValues, onChange: (newValues) => {
|
|
28590
|
-
setOtpValues(newValues);
|
|
28591
|
-
setOtpError(null);
|
|
28592
|
-
if (newValues.every((value) => value.length === 1) && !isSubmitting) {
|
|
28593
|
-
const resolvedEmail = state.email || emailInput.trim() || null;
|
|
28594
|
-
handleSubmit({ otpCode: newValues.join(''), email: resolvedEmail }, 100);
|
|
28595
|
-
}
|
|
28596
|
-
}, disabled: isSubmitting }), displayError && (jsx("p", { className: "ai-chat-booking-card__error", children: displayError })), jsx("button", { className: "ai-chat-booking-card__btn ai-chat-booking-card__btn--primary", onClick: handleOtpSubmit, disabled: otpValues.join('').length !== 6, style: {
|
|
28597
|
-
backgroundColor: accentColor || undefined,
|
|
28598
|
-
borderColor: accentColor || undefined,
|
|
28599
|
-
}, children: "Verify" })] })] }));
|
|
28600
|
-
}
|
|
28601
|
-
// ========================================
|
|
28602
|
-
// Phase: Contact Selection
|
|
28603
|
-
// ========================================
|
|
28604
|
-
if (phase === 'awaiting_contact_selection') {
|
|
28605
|
-
return (jsxs("div", { className: "ai-chat-booking-card", children: [jsxs("div", { className: "ai-chat-booking-card__header", children: [jsx("span", { className: "ai-chat-booking-card__icon", children: "\uD83D\uDC65" }), jsx("span", { className: "ai-chat-booking-card__title", children: "Select a Contact" })] }), jsxs("div", { className: "ai-chat-booking-card__content", children: [state.errorMessage && (jsx("p", { className: "ai-chat-booking-card__error", children: state.errorMessage })), state.bookableContacts.length === 0 ? (jsx("p", { className: "ai-chat-booking-card__empty", children: "No contacts available for booking." })) : (jsx("div", { className: "ai-chat-booking-card__grid", children: state.bookableContacts.map((contact) => (jsxs("button", { className: `ai-chat-booking-card__contact ${state.selectedContactId === contact.id ? 'ai-chat-booking-card__contact--selected' : ''}`, onClick: () => handleSubmit({ selectedContactId: contact.id }), style: {
|
|
28606
|
-
borderColor: state.selectedContactId === contact.id
|
|
28607
|
-
? accentColor || undefined
|
|
28608
|
-
: undefined,
|
|
28609
|
-
backgroundColor: state.selectedContactId === contact.id
|
|
28610
|
-
? `${accentColor || '#3b82f6'}15`
|
|
28611
|
-
: undefined,
|
|
28612
|
-
}, children: [jsx("div", { className: "ai-chat-booking-card__contact-name", children: contact.name }), contact.role && (jsx("div", { className: "ai-chat-booking-card__contact-role", children: contact.role }))] }, contact.id))) }))] })] }));
|
|
28613
|
-
}
|
|
28614
|
-
// ========================================
|
|
28615
|
-
// Phase: Options Selection
|
|
28616
|
-
// ========================================
|
|
28617
|
-
if (phase === 'awaiting_options') {
|
|
28618
|
-
const selectedContact = state.bookableContacts.find((c) => c.id === state.selectedContactId);
|
|
28619
|
-
return (jsxs("div", { className: "ai-chat-booking-card", children: [jsxs("div", { className: "ai-chat-booking-card__header", children: [jsx("span", { className: "ai-chat-booking-card__icon", children: "\uD83D\uDCC5" }), jsxs("span", { className: "ai-chat-booking-card__title", children: ["Book with ", selectedContact?.name] })] }), jsxs("div", { className: "ai-chat-booking-card__content", children: [state.errorMessage && (jsx("p", { className: "ai-chat-booking-card__error", children: state.errorMessage })), jsxs("div", { className: "ai-chat-booking-card__options", children: [jsxs("button", { className: "ai-chat-booking-card__option-btn", onClick: () => handleSubmit({ selectedOption: 'book_new' }), children: [jsx("span", { className: "ai-chat-booking-card__option-icon", children: "\u2795" }), jsx("span", { className: "ai-chat-booking-card__option-text", children: "Book New Appointment" })] }), state.userAppointments.length > 0 && (jsxs(Fragment, { children: [jsxs("button", { className: "ai-chat-booking-card__option-btn", onClick: () => handleSubmit({ selectedOption: 'view_existing' }), children: [jsx("span", { className: "ai-chat-booking-card__option-icon", children: "\uD83D\uDCCB" }), jsx("span", { className: "ai-chat-booking-card__option-text", children: "View Appointments" })] }), jsxs("button", { className: "ai-chat-booking-card__option-btn", onClick: () => handleSubmit({ selectedOption: 'cancel_existing' }), children: [jsx("span", { className: "ai-chat-booking-card__option-icon", children: "\u2715" }), jsx("span", { className: "ai-chat-booking-card__option-text", children: "Cancel Appointment" })] })] }))] })] })] }));
|
|
28620
|
-
}
|
|
28621
|
-
// ========================================
|
|
28622
|
-
// Phase: View Existing Appointments
|
|
28623
|
-
// ========================================
|
|
28624
|
-
if (state.phase === 'awaiting_options' && state.selectedOption === 'view_existing') {
|
|
28625
|
-
return (jsxs("div", { className: "ai-chat-booking-card", children: [jsxs("div", { className: "ai-chat-booking-card__header", children: [jsx("span", { className: "ai-chat-booking-card__icon", children: "\uD83D\uDCCB" }), jsx("span", { className: "ai-chat-booking-card__title", children: "Your Appointments" })] }), jsxs("div", { className: "ai-chat-booking-card__content", children: [state.userAppointments.length === 0 ? (jsx("p", { className: "ai-chat-booking-card__empty", children: "You have no appointments yet." })) : (jsx("div", { className: "ai-chat-booking-card__appointments", children: state.userAppointments.map((apt) => (jsxs("div", { className: "ai-chat-booking-card__appointment", children: [jsxs("div", { className: "ai-chat-booking-card__appointment-header", children: [jsx("span", { className: "ai-chat-booking-card__appointment-subject", children: apt.subject }), jsx("span", { className: `ai-chat-booking-card__appointment-status ai-chat-booking-card__appointment-status--${apt.status}`, children: apt.status })] }), jsx("div", { className: "ai-chat-booking-card__appointment-time", children: apt.displayTime }), jsxs("div", { className: "ai-chat-booking-card__appointment-contact", children: ["with ", apt.contactName] }), apt.teamsLink && (jsx("a", { href: apt.teamsLink, target: "_blank", rel: "noopener noreferrer", className: "ai-chat-booking-card__link", style: { color: accentColor }, children: "Join Teams Meeting \u2192" }))] }, apt.id))) })), jsx("button", { className: "ai-chat-booking-card__btn ai-chat-booking-card__btn--secondary", onClick: () => handleSubmit({ selectedOption: undefined }), children: "Back" })] })] }));
|
|
28626
|
-
}
|
|
28627
|
-
// ========================================
|
|
28628
|
-
// Phase: Cancel Appointment
|
|
28629
|
-
// ========================================
|
|
28630
|
-
if (state.phase === 'awaiting_options' && state.selectedOption === 'cancel_existing') {
|
|
28631
|
-
const activeAppointments = state.userAppointments.filter((a) => a.status !== 'cancelled' && a.status !== 'declined');
|
|
28632
|
-
return (jsxs("div", { className: "ai-chat-booking-card", children: [jsxs("div", { className: "ai-chat-booking-card__header", children: [jsx("span", { className: "ai-chat-booking-card__icon", children: "\u2715" }), jsx("span", { className: "ai-chat-booking-card__title", children: "Cancel Appointment" })] }), jsxs("div", { className: "ai-chat-booking-card__content", children: [state.errorMessage && (jsx("p", { className: "ai-chat-booking-card__error", children: state.errorMessage })), activeAppointments.length === 0 ? (jsxs(Fragment, { children: [jsx("p", { className: "ai-chat-booking-card__empty", children: "No active appointments to cancel." }), jsx("button", { className: "ai-chat-booking-card__btn ai-chat-booking-card__btn--secondary", onClick: () => handleSubmit({ selectedOption: undefined }), children: "Back" })] })) : (jsx("div", { className: "ai-chat-booking-card__appointments", children: activeAppointments.map((apt) => (jsxs("div", { className: "ai-chat-booking-card__appointment", children: [jsx("div", { className: "ai-chat-booking-card__appointment-header", children: jsx("span", { className: "ai-chat-booking-card__appointment-subject", children: apt.subject }) }), jsx("div", { className: "ai-chat-booking-card__appointment-time", children: apt.displayTime }), jsx("button", { className: "ai-chat-booking-card__btn ai-chat-booking-card__btn--danger", onClick: () => handleSubmit({ selectedAppointmentId: apt.id, confirmCancel: true }), children: "Cancel This Appointment" })] }, apt.id))) }))] })] }));
|
|
28633
|
-
}
|
|
28634
|
-
// ========================================
|
|
28635
|
-
// Phase: Slot Selection
|
|
28636
|
-
// ========================================
|
|
28637
|
-
if (phase === 'awaiting_slot_selection') {
|
|
28638
|
-
const groupedSlots = state.availableSlots.reduce((acc, slot) => {
|
|
28639
|
-
if (!acc[slot.displayDate]) {
|
|
28640
|
-
acc[slot.displayDate] = [];
|
|
28641
|
-
}
|
|
28642
|
-
acc[slot.displayDate].push(slot);
|
|
28643
|
-
return acc;
|
|
28644
|
-
}, {});
|
|
28645
|
-
return (jsxs("div", { className: "ai-chat-booking-card", children: [jsxs("div", { className: "ai-chat-booking-card__header", children: [jsx("span", { className: "ai-chat-booking-card__icon", children: "\uD83D\uDD50" }), jsx("span", { className: "ai-chat-booking-card__title", children: "Select a Time Slot" })] }), jsxs("div", { className: "ai-chat-booking-card__content", children: [jsxs("p", { className: "ai-chat-booking-card__description", children: ["Available times in ", state.timeZone] }), state.errorMessage && (jsx("p", { className: "ai-chat-booking-card__error", children: state.errorMessage })), Object.entries(groupedSlots).map(([date, slots]) => (jsxs("div", { className: "ai-chat-booking-card__date-group", children: [jsx("div", { className: "ai-chat-booking-card__date-header", children: date }), jsx("div", { className: "ai-chat-booking-card__slots", children: slots.map((slot, idx) => {
|
|
28646
|
-
const isSelected = state.selectedSlot?.startTime === slot.startTime &&
|
|
28647
|
-
state.selectedSlot?.endTime === slot.endTime;
|
|
28648
|
-
return (jsx("button", { className: `ai-chat-booking-card__slot ${isSelected ? 'ai-chat-booking-card__slot--selected' : ''}`, onClick: () => handleSubmit({
|
|
28649
|
-
selectedSlot: { startTime: slot.startTime, endTime: slot.endTime },
|
|
28650
|
-
}), style: {
|
|
28651
|
-
borderColor: isSelected ? accentColor || undefined : undefined,
|
|
28652
|
-
backgroundColor: isSelected ? `${accentColor || '#3b82f6'}15` : undefined,
|
|
28653
|
-
}, children: slot.displayTime }, `${slot.startTime}-${idx}`));
|
|
28654
|
-
}) })] }, date))), state.availableSlots.length === 0 && (jsx("p", { className: "ai-chat-booking-card__empty", children: "No available time slots." }))] })] }));
|
|
28655
|
-
}
|
|
28656
|
-
// ========================================
|
|
28657
|
-
// Phase: Confirmation
|
|
28658
|
-
// ========================================
|
|
28659
|
-
if (phase === 'awaiting_confirmation') {
|
|
28660
|
-
const selectedContact = state.bookableContacts.find((c) => c.id === state.selectedContactId);
|
|
28661
|
-
const selectedSlot = state.availableSlots.find((s) => s.startTime === state.selectedSlot?.startTime && s.endTime === state.selectedSlot?.endTime);
|
|
28662
|
-
return (jsxs("div", { className: "ai-chat-booking-card", children: [jsxs("div", { className: "ai-chat-booking-card__header", children: [jsx("span", { className: "ai-chat-booking-card__icon", children: "\u2713" }), jsx("span", { className: "ai-chat-booking-card__title", children: "Confirm Booking" })] }), jsxs("div", { className: "ai-chat-booking-card__content", children: [state.errorMessage && (jsx("p", { className: "ai-chat-booking-card__error", children: state.errorMessage })), jsxs("div", { className: "ai-chat-booking-card__summary", children: [jsxs("div", { className: "ai-chat-booking-card__summary-row", children: [jsx("span", { className: "ai-chat-booking-card__summary-label", children: "Contact:" }), jsx("span", { className: "ai-chat-booking-card__summary-value", children: selectedContact?.name })] }), jsxs("div", { className: "ai-chat-booking-card__summary-row", children: [jsx("span", { className: "ai-chat-booking-card__summary-label", children: "Date:" }), jsx("span", { className: "ai-chat-booking-card__summary-value", children: selectedSlot?.displayDate })] }), jsxs("div", { className: "ai-chat-booking-card__summary-row", children: [jsx("span", { className: "ai-chat-booking-card__summary-label", children: "Time:" }), jsx("span", { className: "ai-chat-booking-card__summary-value", children: selectedSlot?.displayTime })] })] }), state.allowCustomSubject && (jsxs(Fragment, { children: [jsx("label", { className: "ai-chat-booking-card__label", children: "Subject (optional):" }), jsx("input", { type: "text", className: "ai-chat-booking-card__input", placeholder: "Meeting subject", value: subjectInput, onChange: (e) => setSubjectInput(e.target.value) })] })), jsx("button", { className: "ai-chat-booking-card__btn ai-chat-booking-card__btn--primary", onClick: () => handleSubmit({
|
|
28663
|
-
subject: state.allowCustomSubject ? subjectInput || undefined : undefined,
|
|
28664
|
-
confirmed: true,
|
|
28665
|
-
}), style: {
|
|
28666
|
-
backgroundColor: accentColor || undefined,
|
|
28667
|
-
borderColor: accentColor || undefined,
|
|
28668
|
-
}, children: "Confirm Booking" })] })] }));
|
|
28669
|
-
}
|
|
28670
|
-
// Fallback for unknown states
|
|
28671
|
-
return (jsxs("div", { className: "ai-chat-booking-card", children: [jsxs("div", { className: "ai-chat-booking-card__header", children: [jsx("span", { className: "ai-chat-booking-card__icon", children: "\uD83D\uDCC5" }), jsx("span", { className: "ai-chat-booking-card__title", children: "Booking" })] }), jsx("div", { className: "ai-chat-booking-card__content", children: jsx("p", { className: "ai-chat-booking-card__description", children: "Loading booking options..." }) })] }));
|
|
28215
|
+
const displayError = localError || error;
|
|
28216
|
+
const isValid = subjectMode === 'predefined' ? !!selectedPredefined : !!subject.trim();
|
|
28217
|
+
return (jsxs("div", { className: `ai-chat-booking-card${showCloseButton ? ' ai-chat-booking-card--closable' : ''}`, children: [jsxs("div", { className: "ai-chat-booking-card__header", children: [jsx("button", { className: "ai-chat-booking-card__back-btn", onClick: onBack, "aria-label": "Go back", children: jsx(BackArrowIcon, {}) }), jsx("span", { className: "ai-chat-booking-card__title", children: "Confirm Booking" }), showCloseButton && onDismiss && jsx(CloseButton, { onClick: onDismiss, ariaLabel: "Cancel booking" })] }), jsxs("div", { className: "ai-chat-booking-card__content", children: [jsxs("div", { className: "ai-chat-booking-card__summary", children: [contactName && (jsxs("div", { className: "ai-chat-booking-card__summary-row", children: [jsx("span", { className: "ai-chat-booking-card__summary-label", children: "With:" }), jsx("span", { className: "ai-chat-booking-card__summary-value", children: contactName })] })), jsxs("div", { className: "ai-chat-booking-card__summary-row", children: [jsx("span", { className: "ai-chat-booking-card__summary-label", children: "When:" }), jsx("span", { className: "ai-chat-booking-card__summary-value", children: slotDisplay })] })] }), displayError && (jsx("p", { className: "ai-chat-booking-card__error", children: displayError })), subjectMode === 'predefined' ? (jsxs("div", { className: "ai-chat-booking-card__field", children: [jsx("label", { className: "ai-chat-booking-card__label", children: "Select a topic:" }), jsx("div", { className: "ai-chat-booking-card__subject-options", children: predefinedSubjects.map((subj) => (jsx("button", { className: `ai-chat-booking-card__subject-option ${selectedPredefined === subj ? 'ai-chat-booking-card__subject-option--selected' : ''}`, onClick: () => {
|
|
28218
|
+
setSelectedPredefined(subj);
|
|
28219
|
+
setLocalError(null);
|
|
28220
|
+
}, disabled: isLoading, children: subj }, subj))) })] })) : (jsxs("div", { className: "ai-chat-booking-card__field", children: [jsx("label", { className: "ai-chat-booking-card__label", children: "What would you like to discuss?" }), jsx("input", { type: "text", className: "ai-chat-booking-card__input", placeholder: "Enter appointment subject...", value: subject, onChange: (e) => {
|
|
28221
|
+
setSubject(e.target.value);
|
|
28222
|
+
setLocalError(null);
|
|
28223
|
+
}, onKeyDown: (e) => {
|
|
28224
|
+
if (e.key === 'Enter' && isValid && !isLoading) {
|
|
28225
|
+
handleConfirm();
|
|
28226
|
+
}
|
|
28227
|
+
}, disabled: isLoading })] })), jsx("button", { className: "ai-chat-booking-card__btn ai-chat-booking-card__btn--primary", onClick: handleConfirm, disabled: !isValid || isLoading, children: isLoading ? 'Booking...' : 'Confirm Booking' })] })] }));
|
|
28672
28228
|
}
|
|
28673
28229
|
|
|
28674
|
-
|
|
28675
|
-
|
|
28676
|
-
|
|
28677
|
-
|
|
28678
|
-
|
|
28679
|
-
|
|
28230
|
+
function BookedPhase({ subject, contactName, meetingLink, meetingProvider, }) {
|
|
28231
|
+
return (jsxs("div", { className: "ai-chat-booking-card ai-chat-booking-card--success", children: [jsx("div", { className: "ai-chat-booking-card__header", children: jsx("span", { className: "ai-chat-booking-card__title", children: "Appointment Booked" }) }), jsxs("div", { className: "ai-chat-booking-card__content", children: [jsx("div", { className: "ai-chat-booking-card__success-icon", "aria-hidden": "true" }), jsx("p", { className: "ai-chat-booking-card__success-message", children: "Your appointment has been successfully scheduled." }), jsxs("div", { className: "ai-chat-booking-card__summary", children: [jsxs("div", { className: "ai-chat-booking-card__summary-row", children: [jsx("span", { className: "ai-chat-booking-card__summary-label", children: "Subject:" }), jsx("span", { className: "ai-chat-booking-card__summary-value", children: subject })] }), contactName && (jsxs("div", { className: "ai-chat-booking-card__summary-row", children: [jsx("span", { className: "ai-chat-booking-card__summary-label", children: "With:" }), jsx("span", { className: "ai-chat-booking-card__summary-value", children: contactName })] }))] }), meetingLink && (jsx("a", { href: meetingLink, target: "_blank", rel: "noopener noreferrer", className: "ai-chat-booking-card__btn ai-chat-booking-card__btn--primary", style: {
|
|
28232
|
+
textDecoration: 'none',
|
|
28233
|
+
display: 'inline-block',
|
|
28234
|
+
textAlign: 'center',
|
|
28235
|
+
}, children: meetingProvider === 'google' ? 'Join Google Meet' : 'Join Teams Meeting' }))] })] }));
|
|
28680
28236
|
}
|
|
28681
|
-
|
|
28682
|
-
|
|
28237
|
+
|
|
28238
|
+
function PendingApprovalPhase({ subject, contactName }) {
|
|
28239
|
+
return (jsxs("div", { className: "ai-chat-booking-card ai-chat-booking-card--pending", children: [jsxs("div", { className: "ai-chat-booking-card__header", children: [jsx("span", { className: "ai-chat-booking-card__status-icon ai-chat-booking-card__status-icon--pending" }), jsx("span", { className: "ai-chat-booking-card__title", children: "Awaiting Approval" })] }), jsxs("div", { className: "ai-chat-booking-card__content", children: [jsx("p", { className: "ai-chat-booking-card__pending-text", children: "Your appointment request has been sent and is awaiting approval." }), jsxs("div", { className: "ai-chat-booking-card__summary", children: [jsxs("div", { className: "ai-chat-booking-card__summary-row", children: [jsx("span", { className: "ai-chat-booking-card__summary-label", children: "Subject:" }), jsx("span", { className: "ai-chat-booking-card__summary-value", children: subject })] }), contactName && (jsxs("div", { className: "ai-chat-booking-card__summary-row", children: [jsx("span", { className: "ai-chat-booking-card__summary-label", children: "With:" }), jsx("span", { className: "ai-chat-booking-card__summary-value", children: contactName })] }))] })] })] }));
|
|
28683
28240
|
}
|
|
28684
|
-
|
|
28685
|
-
|
|
28686
|
-
|
|
28687
|
-
|
|
28241
|
+
|
|
28242
|
+
/**
|
|
28243
|
+
* Cancelled Phase Component (terminal state)
|
|
28244
|
+
*/
|
|
28245
|
+
function CancelledPhase() {
|
|
28246
|
+
return (jsxs("div", { className: "ai-chat-booking-card ai-chat-booking-card--cancelled", children: [jsxs("div", { className: "ai-chat-booking-card__header", children: [jsx("span", { className: "ai-chat-booking-card__status-icon ai-chat-booking-card__status-icon--cancelled" }), jsx("span", { className: "ai-chat-booking-card__title", children: "Appointment Cancelled" })] }), jsx("div", { className: "ai-chat-booking-card__content", children: jsx("p", { className: "ai-chat-booking-card__description", children: "This appointment has been cancelled." }) })] }));
|
|
28688
28247
|
}
|
|
28689
|
-
|
|
28690
|
-
|
|
28691
|
-
|
|
28692
|
-
pendingResolvers.delete(toolCallId);
|
|
28693
|
-
resolver(state);
|
|
28694
|
-
return;
|
|
28695
|
-
}
|
|
28696
|
-
const resumeCallback = resumeCallbacks.get(toolCallId);
|
|
28697
|
-
if (resumeCallback) {
|
|
28698
|
-
resumeCallback(state).catch((error) => {
|
|
28699
|
-
console.error("[Action] Failed to resume action:", error);
|
|
28700
|
-
});
|
|
28701
|
-
}
|
|
28248
|
+
|
|
28249
|
+
function ErrorPhase({ error, onRetry }) {
|
|
28250
|
+
return (jsxs("div", { className: "ai-chat-booking-card ai-chat-booking-card--error", children: [jsxs("div", { className: "ai-chat-booking-card__header", children: [jsx("span", { className: "ai-chat-booking-card__status-icon ai-chat-booking-card__status-icon--error" }), jsx("span", { className: "ai-chat-booking-card__title", children: "Booking Error" })] }), jsxs("div", { className: "ai-chat-booking-card__content", children: [jsx("p", { className: "ai-chat-booking-card__error", children: error }), onRetry && (jsx("button", { className: "ai-chat-booking-card__btn ai-chat-booking-card__btn--secondary", onClick: onRetry, children: "Try Again" }))] })] }));
|
|
28702
28251
|
}
|
|
28703
|
-
|
|
28704
|
-
|
|
28252
|
+
|
|
28253
|
+
function CompletedPhase() {
|
|
28254
|
+
return (jsx("div", { className: "ai-chat-booking-card ai-chat-booking-card--completed", children: jsxs("div", { className: "ai-chat-booking-card__completed-indicator", children: [jsx(CheckIcon, {}), jsx("span", { children: "Booking action completed" })] }) }));
|
|
28705
28255
|
}
|
|
28706
|
-
|
|
28707
|
-
|
|
28256
|
+
|
|
28257
|
+
function LoadingPhase() {
|
|
28258
|
+
return (jsx("div", { className: "ai-chat-booking-card", children: jsxs("div", { className: "ai-chat-action-skeleton-content", children: [jsxs("div", { className: "ai-chat-action-skeleton-header", children: [jsx(Skeleton, { width: "28px", height: "28px", borderRadius: "50%" }), jsx(Skeleton, { width: "180px", height: "20px", borderRadius: "4px" })] }), jsxs("div", { className: "ai-chat-action-skeleton-box", children: [jsx(Skeleton, { width: "60px", height: "12px", borderRadius: "4px" }), jsx(Skeleton, { width: "120px", height: "18px", borderRadius: "4px" })] }), jsx(Skeleton, { width: "100%", height: "44px", borderRadius: "999px" })] }) }));
|
|
28708
28259
|
}
|
|
28709
28260
|
|
|
28710
|
-
|
|
28711
|
-
|
|
28712
|
-
|
|
28261
|
+
/**
|
|
28262
|
+
* Core action lifecycle handlers for booking flow
|
|
28263
|
+
*/
|
|
28264
|
+
function useBookingAction({ onComplete, onDismiss, toolCallId, }) {
|
|
28265
|
+
const handleDismiss = useCallback(() => {
|
|
28266
|
+
onDismiss?.(toolCallId);
|
|
28267
|
+
}, [onDismiss, toolCallId]);
|
|
28268
|
+
const finishAction = useCallback((body) => {
|
|
28269
|
+
if (!onComplete)
|
|
28270
|
+
return;
|
|
28271
|
+
onComplete(toolCallId, body);
|
|
28272
|
+
}, [onComplete, toolCallId]);
|
|
28273
|
+
return {
|
|
28274
|
+
handleDismiss,
|
|
28275
|
+
finishAction,
|
|
28713
28276
|
};
|
|
28714
28277
|
}
|
|
28715
28278
|
|
|
28716
28279
|
/**
|
|
28717
|
-
*
|
|
28718
|
-
* Called by initializeActionHandlers to prevent tree-shaking.
|
|
28280
|
+
* Helper functions for accessing booking data
|
|
28719
28281
|
*/
|
|
28720
|
-
function
|
|
28721
|
-
|
|
28722
|
-
|
|
28723
|
-
|
|
28724
|
-
|
|
28725
|
-
|
|
28726
|
-
|
|
28727
|
-
|
|
28728
|
-
|
|
28729
|
-
|
|
28730
|
-
|
|
28731
|
-
|
|
28732
|
-
|
|
28733
|
-
|
|
28734
|
-
|
|
28282
|
+
function useBookingHelpers({ contacts, slots, bookingMode, }) {
|
|
28283
|
+
const getContactName = useCallback((contactId) => {
|
|
28284
|
+
if (!contactId)
|
|
28285
|
+
return bookingMode === 'general' ? 'Shared Calendar' : null;
|
|
28286
|
+
const contact = contacts.find(c => c.id === contactId);
|
|
28287
|
+
return contact?.name || null;
|
|
28288
|
+
}, [contacts, bookingMode]);
|
|
28289
|
+
const getSlotDisplay = useCallback((slot) => {
|
|
28290
|
+
if (!slot)
|
|
28291
|
+
return '';
|
|
28292
|
+
const slotData = slots.find(s => s.startTime === slot.startTime && s.endTime === slot.endTime);
|
|
28293
|
+
return slotData ? `${slotData.displayDate} ${slotData.displayTime}` : '';
|
|
28294
|
+
}, [slots]);
|
|
28295
|
+
return {
|
|
28296
|
+
getContactName,
|
|
28297
|
+
getSlotDisplay,
|
|
28298
|
+
};
|
|
28299
|
+
}
|
|
28300
|
+
|
|
28301
|
+
/**
|
|
28302
|
+
* OTP-related operations for email verification
|
|
28303
|
+
*/
|
|
28304
|
+
function useBookingOtp({ onCallEndpoint, state, setState, config, }) {
|
|
28305
|
+
const handleSendOtp = useCallback(async (email) => {
|
|
28306
|
+
if (!onCallEndpoint) {
|
|
28307
|
+
setState(prev => ({ ...prev, error: 'Endpoint not available' }));
|
|
28308
|
+
return;
|
|
28309
|
+
}
|
|
28310
|
+
setState(prev => ({ ...prev, isLoading: true, error: null, email }));
|
|
28311
|
+
try {
|
|
28312
|
+
await onCallEndpoint('send-otp', { email });
|
|
28313
|
+
setState(prev => ({ ...prev, isLoading: false, step: 'otp' }));
|
|
28314
|
+
}
|
|
28315
|
+
catch (err) {
|
|
28316
|
+
const message = err instanceof Error ? err.message : 'Failed to send verification code';
|
|
28317
|
+
setState(prev => ({ ...prev, isLoading: false, error: message }));
|
|
28318
|
+
}
|
|
28319
|
+
}, [onCallEndpoint, setState]);
|
|
28320
|
+
const handleVerifyOtp = useCallback(async (code) => {
|
|
28321
|
+
if (!onCallEndpoint) {
|
|
28322
|
+
setState(prev => ({ ...prev, error: 'Endpoint not available' }));
|
|
28323
|
+
return;
|
|
28324
|
+
}
|
|
28325
|
+
setState(prev => ({ ...prev, isLoading: true, error: null }));
|
|
28326
|
+
try {
|
|
28327
|
+
const result = await onCallEndpoint('verify-otp', {
|
|
28328
|
+
email: state.email,
|
|
28329
|
+
code,
|
|
28330
|
+
bookingMode: config.bookingMode,
|
|
28331
|
+
timeZone: config.timeZone,
|
|
28332
|
+
});
|
|
28333
|
+
const nextStep = config.bookingMode === 'general'
|
|
28334
|
+
? 'options'
|
|
28335
|
+
: result.contacts.length === 1
|
|
28336
|
+
? 'options'
|
|
28337
|
+
: 'contact_selection';
|
|
28338
|
+
setState(prev => ({
|
|
28339
|
+
...prev,
|
|
28340
|
+
isLoading: false,
|
|
28341
|
+
token: result.token,
|
|
28342
|
+
contacts: result.contacts,
|
|
28343
|
+
appointments: result.appointments,
|
|
28344
|
+
selectedContactId: result.contacts.length === 1 ? result.contacts[0].id : null,
|
|
28345
|
+
step: nextStep,
|
|
28346
|
+
}));
|
|
28347
|
+
}
|
|
28348
|
+
catch (err) {
|
|
28349
|
+
const message = err instanceof Error ? err.message : 'Invalid verification code';
|
|
28350
|
+
setState(prev => ({ ...prev, isLoading: false, error: message }));
|
|
28351
|
+
}
|
|
28352
|
+
}, [onCallEndpoint, state.email, config.bookingMode, config.timeZone, setState]);
|
|
28353
|
+
const handleResendOtp = useCallback(async () => {
|
|
28354
|
+
await handleSendOtp(state.email);
|
|
28355
|
+
}, [handleSendOtp, state.email]);
|
|
28356
|
+
return {
|
|
28357
|
+
handleSendOtp,
|
|
28358
|
+
handleVerifyOtp,
|
|
28359
|
+
handleResendOtp,
|
|
28360
|
+
};
|
|
28361
|
+
}
|
|
28362
|
+
|
|
28363
|
+
/**
|
|
28364
|
+
* Navigation and flow control handlers for booking steps
|
|
28365
|
+
*/
|
|
28366
|
+
function useBookingNavigation({ onCallEndpoint, state, setState, config, }) {
|
|
28367
|
+
const handleSelectContact = useCallback((contactId) => {
|
|
28368
|
+
setState(prev => ({
|
|
28369
|
+
...prev,
|
|
28370
|
+
selectedContactId: contactId,
|
|
28371
|
+
step: 'options',
|
|
28372
|
+
}));
|
|
28373
|
+
}, [setState]);
|
|
28374
|
+
const handleBookNew = useCallback(async () => {
|
|
28375
|
+
if (!onCallEndpoint || !state.token) {
|
|
28376
|
+
setState(prev => ({ ...prev, error: 'Not authenticated' }));
|
|
28377
|
+
return;
|
|
28378
|
+
}
|
|
28379
|
+
setState(prev => ({ ...prev, isLoading: true, error: null }));
|
|
28380
|
+
try {
|
|
28381
|
+
const result = await onCallEndpoint('get-slots', {
|
|
28382
|
+
contactId: state.selectedContactId,
|
|
28383
|
+
daysAhead: config.daysAhead,
|
|
28384
|
+
timeZone: config.timeZone,
|
|
28385
|
+
}, { token: state.token });
|
|
28386
|
+
setState(prev => ({
|
|
28387
|
+
...prev,
|
|
28388
|
+
isLoading: false,
|
|
28389
|
+
slots: result.slots,
|
|
28390
|
+
step: 'slot_selection',
|
|
28391
|
+
}));
|
|
28392
|
+
}
|
|
28393
|
+
catch (err) {
|
|
28394
|
+
const message = err instanceof Error ? err.message : 'Failed to load available slots';
|
|
28395
|
+
setState(prev => ({ ...prev, isLoading: false, error: message }));
|
|
28396
|
+
}
|
|
28397
|
+
}, [onCallEndpoint, state.token, state.selectedContactId, config.daysAhead, config.timeZone, setState]);
|
|
28398
|
+
const handleViewAppointments = useCallback(() => {
|
|
28399
|
+
setState(prev => ({ ...prev, step: 'view_appointments' }));
|
|
28400
|
+
}, [setState]);
|
|
28401
|
+
const handleBackToOptions = useCallback(() => {
|
|
28402
|
+
setState(prev => ({ ...prev, step: 'options', error: null }));
|
|
28403
|
+
}, [setState]);
|
|
28404
|
+
const handleSelectSlot = useCallback((slot) => {
|
|
28405
|
+
setState(prev => ({
|
|
28406
|
+
...prev,
|
|
28407
|
+
selectedSlot: slot,
|
|
28408
|
+
step: 'confirmation',
|
|
28409
|
+
}));
|
|
28410
|
+
}, [setState]);
|
|
28411
|
+
const handleBackToSlots = useCallback(() => {
|
|
28412
|
+
setState(prev => ({ ...prev, step: 'slot_selection', error: null }));
|
|
28413
|
+
}, [setState]);
|
|
28414
|
+
return {
|
|
28415
|
+
handleSelectContact,
|
|
28416
|
+
handleBookNew,
|
|
28417
|
+
handleViewAppointments,
|
|
28418
|
+
handleBackToOptions,
|
|
28419
|
+
handleSelectSlot,
|
|
28420
|
+
handleBackToSlots,
|
|
28421
|
+
};
|
|
28422
|
+
}
|
|
28423
|
+
|
|
28424
|
+
/**
|
|
28425
|
+
* Core booking operations (confirm and cancel)
|
|
28426
|
+
*/
|
|
28427
|
+
function useBookingOperations({ onCallEndpoint, state, setState, config, getContactName, finishAction, }) {
|
|
28428
|
+
const [isCancelling, setIsCancelling] = useState(false);
|
|
28429
|
+
const handleConfirmBooking = useCallback(async (subject) => {
|
|
28430
|
+
if (!onCallEndpoint || !state.token || !state.selectedSlot) {
|
|
28431
|
+
setState(prev => ({ ...prev, error: 'Missing required data' }));
|
|
28432
|
+
return;
|
|
28433
|
+
}
|
|
28434
|
+
setState(prev => ({ ...prev, isLoading: true, error: null, subject }));
|
|
28435
|
+
try {
|
|
28436
|
+
const result = await onCallEndpoint('book', {
|
|
28437
|
+
contactId: state.selectedContactId,
|
|
28438
|
+
slot: state.selectedSlot,
|
|
28439
|
+
subject,
|
|
28440
|
+
timeZone: config.timeZone,
|
|
28441
|
+
}, { token: state.token });
|
|
28442
|
+
const contactName = getContactName(state.selectedContactId);
|
|
28443
|
+
if (result.status === 'pending_approval') {
|
|
28444
|
+
setState(prev => ({
|
|
28445
|
+
...prev,
|
|
28446
|
+
isLoading: false,
|
|
28447
|
+
step: 'pending_approval',
|
|
28448
|
+
}));
|
|
28449
|
+
// Finish with pending status
|
|
28450
|
+
finishAction({
|
|
28451
|
+
status: 'pending_approval',
|
|
28452
|
+
subject,
|
|
28453
|
+
contactName,
|
|
28454
|
+
message: `Appointment request "${subject}" submitted and awaiting approval.`,
|
|
28455
|
+
});
|
|
28735
28456
|
}
|
|
28736
|
-
|
|
28737
|
-
|
|
28738
|
-
|
|
28739
|
-
|
|
28740
|
-
|
|
28741
|
-
|
|
28742
|
-
|
|
28743
|
-
|
|
28744
|
-
|
|
28457
|
+
else {
|
|
28458
|
+
setState(prev => ({
|
|
28459
|
+
...prev,
|
|
28460
|
+
isLoading: false,
|
|
28461
|
+
bookedMeetingLink: result.meetingLink || null,
|
|
28462
|
+
bookedMeetingProvider: result.meetingProvider || null,
|
|
28463
|
+
step: 'booked',
|
|
28464
|
+
}));
|
|
28465
|
+
// Finish with booked status
|
|
28466
|
+
finishAction({
|
|
28467
|
+
status: 'booked',
|
|
28468
|
+
subject,
|
|
28469
|
+
contactName,
|
|
28470
|
+
meetingLink: result.meetingLink,
|
|
28471
|
+
message: `Successfully booked appointment "${subject}"${contactName ? ` with ${contactName}` : ''}.`,
|
|
28472
|
+
});
|
|
28473
|
+
}
|
|
28474
|
+
}
|
|
28475
|
+
catch (err) {
|
|
28476
|
+
const message = err instanceof Error ? err.message : 'Failed to book appointment';
|
|
28477
|
+
setState(prev => ({ ...prev, isLoading: false, error: message }));
|
|
28478
|
+
}
|
|
28479
|
+
}, [onCallEndpoint, state.token, state.selectedSlot, state.selectedContactId, config.timeZone, getContactName, finishAction, setState]);
|
|
28480
|
+
const handleCancelAppointment = useCallback(async (appointmentId) => {
|
|
28481
|
+
if (!onCallEndpoint || !state.token) {
|
|
28482
|
+
setState(prev => ({ ...prev, error: 'Not authenticated' }));
|
|
28483
|
+
return;
|
|
28484
|
+
}
|
|
28485
|
+
setIsCancelling(true);
|
|
28486
|
+
try {
|
|
28487
|
+
await onCallEndpoint('cancel', { appointmentId }, { token: state.token });
|
|
28488
|
+
// Update local appointments list
|
|
28489
|
+
setState(prev => ({
|
|
28490
|
+
...prev,
|
|
28491
|
+
appointments: prev.appointments.map(apt => apt.id === appointmentId ? { ...apt, status: 'cancelled' } : apt),
|
|
28492
|
+
}));
|
|
28493
|
+
}
|
|
28494
|
+
catch (err) {
|
|
28495
|
+
const message = err instanceof Error ? err.message : 'Failed to cancel appointment';
|
|
28496
|
+
setState(prev => ({ ...prev, error: message }));
|
|
28497
|
+
}
|
|
28498
|
+
finally {
|
|
28499
|
+
setIsCancelling(false);
|
|
28500
|
+
}
|
|
28501
|
+
}, [onCallEndpoint, state.token, setState]);
|
|
28502
|
+
return {
|
|
28503
|
+
handleConfirmBooking,
|
|
28504
|
+
handleCancelAppointment,
|
|
28505
|
+
isCancelling,
|
|
28745
28506
|
};
|
|
28746
28507
|
}
|
|
28747
28508
|
|
|
28748
|
-
|
|
28749
|
-
|
|
28750
|
-
|
|
28509
|
+
// Default config values
|
|
28510
|
+
const DEFAULT_CONFIG = {
|
|
28511
|
+
bookingMode: 'per-contact',
|
|
28512
|
+
timeZone: 'UTC',
|
|
28513
|
+
maxAppointmentsPerUser: 3,
|
|
28514
|
+
daysAhead: 14,
|
|
28515
|
+
subjectMode: 'user_defined',
|
|
28516
|
+
predefinedSubjects: [],
|
|
28517
|
+
};
|
|
28518
|
+
// Initial UI state
|
|
28519
|
+
const createInitialState = () => ({
|
|
28520
|
+
step: 'email',
|
|
28521
|
+
email: '',
|
|
28522
|
+
selectedContactId: null,
|
|
28523
|
+
selectedSlot: null,
|
|
28524
|
+
subject: '',
|
|
28525
|
+
token: null,
|
|
28526
|
+
contacts: [],
|
|
28527
|
+
appointments: [],
|
|
28528
|
+
slots: [],
|
|
28529
|
+
bookedMeetingLink: null,
|
|
28530
|
+
bookedMeetingProvider: null,
|
|
28531
|
+
isLoading: false,
|
|
28532
|
+
error: null,
|
|
28533
|
+
});
|
|
28534
|
+
function BookContactAppointmentCard({ action, onComplete, onDismiss, onCallEndpoint, accentColor, }) {
|
|
28535
|
+
// Parse config from input (from getInitialClientState)
|
|
28536
|
+
const initialState = action.input;
|
|
28537
|
+
const config = {
|
|
28538
|
+
bookingMode: initialState.bookingMode || DEFAULT_CONFIG.bookingMode,
|
|
28539
|
+
timeZone: initialState.timeZone || DEFAULT_CONFIG.timeZone,
|
|
28540
|
+
maxAppointmentsPerUser: initialState.maxAppointmentsPerUser || DEFAULT_CONFIG.maxAppointmentsPerUser,
|
|
28541
|
+
daysAhead: initialState.daysAhead || DEFAULT_CONFIG.daysAhead,
|
|
28542
|
+
subjectMode: initialState.subjectMode || DEFAULT_CONFIG.subjectMode,
|
|
28543
|
+
predefinedSubjects: initialState.predefinedSubjects || DEFAULT_CONFIG.predefinedSubjects,
|
|
28544
|
+
};
|
|
28545
|
+
// Local UI state
|
|
28546
|
+
const [state, setState] = useState(createInitialState);
|
|
28547
|
+
// Check if action is already done (from server state)
|
|
28548
|
+
const isDone = action.done;
|
|
28549
|
+
// Determine if dismiss should be available
|
|
28550
|
+
const isInteractiveStep = ['email', 'otp', 'contact_selection', 'options', 'view_appointments', 'slot_selection', 'confirmation'].includes(state.step);
|
|
28551
|
+
const showCloseButton = !isDone && isInteractiveStep && Boolean(onDismiss);
|
|
28552
|
+
// =========================================================================
|
|
28553
|
+
// Custom Hooks
|
|
28554
|
+
// =========================================================================
|
|
28555
|
+
// Core action lifecycle
|
|
28556
|
+
const { handleDismiss, finishAction } = useBookingAction({
|
|
28557
|
+
onComplete,
|
|
28558
|
+
onDismiss,
|
|
28559
|
+
toolCallId: action.toolCallId,
|
|
28560
|
+
});
|
|
28561
|
+
// Helper functions
|
|
28562
|
+
const { getContactName, getSlotDisplay } = useBookingHelpers({
|
|
28563
|
+
contacts: state.contacts,
|
|
28564
|
+
slots: state.slots,
|
|
28565
|
+
bookingMode: config.bookingMode,
|
|
28566
|
+
});
|
|
28567
|
+
// OTP operations
|
|
28568
|
+
const { handleSendOtp, handleVerifyOtp, handleResendOtp, } = useBookingOtp({
|
|
28569
|
+
onCallEndpoint,
|
|
28570
|
+
state: { email: state.email },
|
|
28571
|
+
setState,
|
|
28572
|
+
config: {
|
|
28573
|
+
bookingMode: config.bookingMode,
|
|
28574
|
+
timeZone: config.timeZone,
|
|
28575
|
+
},
|
|
28576
|
+
});
|
|
28577
|
+
// Navigation handlers
|
|
28578
|
+
const { handleSelectContact, handleBookNew, handleViewAppointments, handleBackToOptions, handleSelectSlot, handleBackToSlots, } = useBookingNavigation({
|
|
28579
|
+
onCallEndpoint,
|
|
28580
|
+
state,
|
|
28581
|
+
setState,
|
|
28582
|
+
config: {
|
|
28583
|
+
daysAhead: config.daysAhead,
|
|
28584
|
+
timeZone: config.timeZone,
|
|
28585
|
+
},
|
|
28586
|
+
});
|
|
28587
|
+
// Booking operations
|
|
28588
|
+
const { handleConfirmBooking, handleCancelAppointment, isCancelling, } = useBookingOperations({
|
|
28589
|
+
onCallEndpoint,
|
|
28590
|
+
state,
|
|
28591
|
+
setState,
|
|
28592
|
+
config: {
|
|
28593
|
+
timeZone: config.timeZone,
|
|
28594
|
+
},
|
|
28595
|
+
getContactName,
|
|
28596
|
+
finishAction,
|
|
28597
|
+
});
|
|
28598
|
+
// =========================================================================
|
|
28599
|
+
// Render
|
|
28600
|
+
// =========================================================================
|
|
28601
|
+
// If action is already done, show a simple completion indicator
|
|
28602
|
+
// The agent's response will convey what happened - no need to reconstruct terminal state
|
|
28603
|
+
if (isDone) {
|
|
28604
|
+
return jsx(CompletedPhase, {});
|
|
28605
|
+
}
|
|
28606
|
+
// Show loading when making API calls
|
|
28607
|
+
if (state.isLoading) {
|
|
28608
|
+
return jsx(LoadingPhase, {});
|
|
28609
|
+
}
|
|
28610
|
+
// Render based on current step
|
|
28611
|
+
switch (state.step) {
|
|
28612
|
+
case 'email':
|
|
28613
|
+
return (jsx(EmailPhase, { accentColor: accentColor, onDismiss: handleDismiss, showCloseButton: showCloseButton, onSubmit: handleSendOtp, isLoading: state.isLoading, error: state.error }));
|
|
28614
|
+
case 'otp':
|
|
28615
|
+
return (jsx(OtpPhase, { accentColor: accentColor, onDismiss: handleDismiss, showCloseButton: showCloseButton, email: state.email, onSubmit: handleVerifyOtp, onResend: handleResendOtp, isLoading: state.isLoading, error: state.error }));
|
|
28616
|
+
case 'contact_selection':
|
|
28617
|
+
return (jsx(ContactSelectionPhase, { accentColor: accentColor, onDismiss: handleDismiss, showCloseButton: showCloseButton, contacts: state.contacts, onSelect: handleSelectContact }));
|
|
28618
|
+
case 'options':
|
|
28619
|
+
return (jsx(OptionsPhase, { accentColor: accentColor, onDismiss: handleDismiss, showCloseButton: showCloseButton, appointments: state.appointments.filter(a => a.status !== 'cancelled'), maxAppointments: config.maxAppointmentsPerUser, onBookNew: handleBookNew, onViewAppointments: handleViewAppointments, onCancelAppointment: handleCancelAppointment, isCancelling: isCancelling }));
|
|
28620
|
+
case 'view_appointments':
|
|
28621
|
+
return (jsx(ViewAppointmentsPhase, { accentColor: accentColor, onDismiss: handleDismiss, showCloseButton: showCloseButton, appointments: state.appointments, onBack: handleBackToOptions, onCancelAppointment: handleCancelAppointment, isCancelling: isCancelling }));
|
|
28622
|
+
case 'slot_selection':
|
|
28623
|
+
return (jsx(SlotSelectionPhase, { accentColor: accentColor, onDismiss: handleDismiss, showCloseButton: showCloseButton, slots: state.slots, contactName: getContactName(state.selectedContactId), onSelect: handleSelectSlot, onBack: handleBackToOptions, isLoading: state.isLoading, error: state.error }));
|
|
28624
|
+
case 'confirmation':
|
|
28625
|
+
return (jsx(ConfirmationPhase, { accentColor: accentColor, onDismiss: handleDismiss, showCloseButton: showCloseButton, contactName: getContactName(state.selectedContactId), slot: state.selectedSlot, slotDisplay: getSlotDisplay(state.selectedSlot), subjectMode: config.subjectMode, predefinedSubjects: config.predefinedSubjects, onConfirm: handleConfirmBooking, onBack: handleBackToSlots, isLoading: state.isLoading, error: state.error }));
|
|
28626
|
+
case 'booked':
|
|
28627
|
+
return (jsx(BookedPhase, { subject: state.subject, contactName: getContactName(state.selectedContactId), meetingLink: state.bookedMeetingLink, meetingProvider: state.bookedMeetingProvider }));
|
|
28628
|
+
case 'pending_approval':
|
|
28629
|
+
return (jsx(PendingApprovalPhase, { subject: state.subject, contactName: getContactName(state.selectedContactId) }));
|
|
28630
|
+
case 'cancelled':
|
|
28631
|
+
return jsx(CancelledPhase, {});
|
|
28632
|
+
case 'error':
|
|
28633
|
+
return jsx(ErrorPhase, { error: state.error || 'An error occurred' });
|
|
28634
|
+
default:
|
|
28635
|
+
return jsx(ErrorPhase, { error: "Unknown state" });
|
|
28636
|
+
}
|
|
28637
|
+
}
|
|
28638
|
+
|
|
28639
|
+
function ExternalLinkIcon$1() {
|
|
28640
|
+
return (jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsx("path", { d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" }), jsx("polyline", { points: "15 3 21 3 21 9" }), jsx("line", { x1: "10", y1: "14", x2: "21", y2: "3" })] }));
|
|
28641
|
+
}
|
|
28642
|
+
function ImageCard({ item, accentColor, showOverlay = true, onClick, onLinkClick }) {
|
|
28643
|
+
const [imageError, setImageError] = useState(false);
|
|
28644
|
+
const style = accentColor ? { '--action-accent': accentColor } : undefined;
|
|
28645
|
+
const hasMetadata = item.title || item.description || item.link;
|
|
28646
|
+
const handleLinkClick = (e) => {
|
|
28647
|
+
e.stopPropagation();
|
|
28648
|
+
if (item.link) {
|
|
28649
|
+
onLinkClick?.(item.link);
|
|
28650
|
+
window.open(item.link, '_blank', 'noopener,noreferrer');
|
|
28651
|
+
}
|
|
28652
|
+
};
|
|
28653
|
+
const handleImageClick = () => {
|
|
28654
|
+
if (onClick) {
|
|
28655
|
+
onClick();
|
|
28656
|
+
}
|
|
28751
28657
|
};
|
|
28658
|
+
return (jsxs("div", { className: "ai-chat-image-card", style: style, onClick: handleImageClick, children: [jsx("div", { className: "ai-chat-image-card__media", children: !imageError ? (jsx("img", { src: item.url, alt: item.alt || item.title || 'Image', className: "ai-chat-image-card__image", onError: () => setImageError(true) })) : (jsx("div", { className: "ai-chat-image-card__image-error", children: jsx("span", { children: "Failed to load image" }) })) }), hasMetadata && showOverlay && (jsxs("div", { className: "ai-chat-image-card__content", children: [item.title && (jsx("h4", { className: "ai-chat-image-card__title", children: item.title })), item.description && (jsx("p", { className: "ai-chat-image-card__description", children: item.description })), item.link && (jsxs("button", { className: "ai-chat-image-card__link-btn", onClick: handleLinkClick, children: [item.linkText || 'View More', jsx(ExternalLinkIcon$1, {})] }))] }))] }));
|
|
28659
|
+
}
|
|
28660
|
+
|
|
28661
|
+
function ImageGallery({ items, columns = 2, accentColor }) {
|
|
28662
|
+
const [imageErrors, setImageErrors] = useState(new Set());
|
|
28663
|
+
const imageItems = items;
|
|
28664
|
+
if (imageItems.length === 0)
|
|
28665
|
+
return null;
|
|
28666
|
+
// Determine if first item should span full width (for odd counts > 1)
|
|
28667
|
+
const itemCount = imageItems.length;
|
|
28668
|
+
const isOddCount = itemCount > 1 && itemCount % 2 === 1;
|
|
28669
|
+
const style = {
|
|
28670
|
+
...(accentColor ? { '--action-accent': accentColor } : {}),
|
|
28671
|
+
'--gallery-item-count': itemCount,
|
|
28672
|
+
};
|
|
28673
|
+
const handleImageError = (index) => {
|
|
28674
|
+
setImageErrors(prev => new Set(prev).add(index));
|
|
28675
|
+
};
|
|
28676
|
+
return (jsx("div", { className: "ai-chat-image-gallery", style: style, "data-item-count": itemCount, children: jsx("div", { className: `ai-chat-image-gallery__grid ${isOddCount ? 'odd-count' : ''}`, "data-count": itemCount, children: imageItems.map((item, index) => (jsx("div", { className: `ai-chat-image-gallery__item ${isOddCount && index === 0 ? 'span-full' : ''}`, children: jsxs("div", { className: "ai-chat-image-gallery__item-media", children: [!imageErrors.has(index) ? (jsx("img", { src: item.url, alt: item.alt || item.title || `Image ${index + 1}`, onError: () => handleImageError(index) })) : (jsx("div", { className: "ai-chat-image-gallery__error", children: jsx("span", { children: "Failed to load" }) })), item.title && (jsx("div", { className: "ai-chat-image-gallery__item-overlay", children: jsx("span", { className: "ai-chat-image-gallery__item-title", children: item.title }) }))] }) }, index))) }) }));
|
|
28677
|
+
}
|
|
28678
|
+
|
|
28679
|
+
function ExternalLinkIcon() {
|
|
28680
|
+
return (jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsx("path", { d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" }), jsx("polyline", { points: "15 3 21 3 21 9" }), jsx("line", { x1: "10", y1: "14", x2: "21", y2: "3" })] }));
|
|
28681
|
+
}
|
|
28682
|
+
function truncate(text, maxLength) {
|
|
28683
|
+
if (text.length <= maxLength)
|
|
28684
|
+
return text;
|
|
28685
|
+
return text.slice(0, maxLength).trim() + '...';
|
|
28686
|
+
}
|
|
28687
|
+
function ProjectCard({ item, accentColor, onLinkClick, variant }) {
|
|
28688
|
+
const [imageError, setImageError] = useState(false);
|
|
28689
|
+
const style = accentColor ? { '--action-accent': accentColor } : undefined;
|
|
28690
|
+
const imageUrl = item.url;
|
|
28691
|
+
// Determine variant based on content if not specified
|
|
28692
|
+
const hasTextContent = item.title || item.description;
|
|
28693
|
+
const effectiveVariant = variant || (hasTextContent ? 'full' : 'image-only');
|
|
28694
|
+
const handleCardClick = () => {
|
|
28695
|
+
if (item.link) {
|
|
28696
|
+
onLinkClick?.(item.link);
|
|
28697
|
+
window.open(item.link, '_blank', 'noopener,noreferrer');
|
|
28698
|
+
}
|
|
28699
|
+
};
|
|
28700
|
+
// Image-only variant - just the image with optional hover effect
|
|
28701
|
+
if (effectiveVariant === 'image-only') {
|
|
28702
|
+
return (jsx("div", { className: `ai-chat-project-card ai-chat-project-card--image-only ${item.link ? 'clickable' : ''}`, style: style, onClick: item.link ? handleCardClick : undefined, role: item.link ? 'link' : undefined, tabIndex: item.link ? 0 : undefined, onKeyDown: (e) => e.key === 'Enter' && item.link && handleCardClick(), children: jsx("div", { className: "ai-chat-project-card__media", children: imageUrl && !imageError ? (jsx("img", { src: imageUrl, alt: item.alt || 'Image', onError: () => setImageError(true) })) : (jsx("div", { className: "ai-chat-project-card__placeholder", children: imageError ? 'Failed to load' : 'No image' })) }) }));
|
|
28703
|
+
}
|
|
28704
|
+
// Overlay variant - title overlaid on image
|
|
28705
|
+
if (effectiveVariant === 'overlay') {
|
|
28706
|
+
return (jsx("div", { className: `ai-chat-project-card ai-chat-project-card--overlay ${item.link ? 'clickable' : ''}`, style: style, onClick: item.link ? handleCardClick : undefined, role: item.link ? 'link' : undefined, tabIndex: item.link ? 0 : undefined, onKeyDown: (e) => e.key === 'Enter' && item.link && handleCardClick(), children: jsxs("div", { className: "ai-chat-project-card__media", children: [imageUrl && !imageError ? (jsx("img", { src: imageUrl, alt: item.alt || item.title || 'Image', onError: () => setImageError(true) })) : (jsx("div", { className: "ai-chat-project-card__placeholder", children: imageError ? 'Failed to load' : 'No image' })), item.title && (jsx("div", { className: "ai-chat-project-card__overlay", children: jsx("h4", { className: "ai-chat-project-card__overlay-title", children: truncate(item.title, 50) }) }))] }) }));
|
|
28707
|
+
}
|
|
28708
|
+
// Full variant - image with separate content section below
|
|
28709
|
+
return (jsxs("div", { className: `ai-chat-project-card ai-chat-project-card--full ${item.link ? 'clickable' : ''}`, style: style, onClick: item.link ? handleCardClick : undefined, role: item.link ? 'link' : undefined, tabIndex: item.link ? 0 : undefined, onKeyDown: (e) => e.key === 'Enter' && item.link && handleCardClick(), children: [jsx("div", { className: "ai-chat-project-card__media", children: imageUrl && !imageError ? (jsx("img", { src: imageUrl, alt: item.alt || item.title || 'Project image', onError: () => setImageError(true) })) : (jsx("div", { className: "ai-chat-project-card__placeholder", children: imageError ? 'Failed to load' : 'No image' })) }), hasTextContent && (jsxs("div", { className: "ai-chat-project-card__content", children: [item.title && (jsx("h4", { className: "ai-chat-project-card__title", children: truncate(item.title, 60) })), item.description && (jsx("p", { className: "ai-chat-project-card__description", children: truncate(item.description, 120) })), item.link && (jsxs("div", { className: "ai-chat-project-card__link", children: [jsx("span", { children: item.linkText || 'View' }), jsx(ExternalLinkIcon, {})] }))] }))] }));
|
|
28752
28710
|
}
|
|
28753
28711
|
|
|
28754
28712
|
/**
|
|
28755
|
-
*
|
|
28756
|
-
*
|
|
28713
|
+
* URL validation utilities for images, cards, and links
|
|
28714
|
+
* Prevents 404 errors by validating URLs before display
|
|
28757
28715
|
*/
|
|
28758
|
-
|
|
28759
|
-
|
|
28760
|
-
|
|
28761
|
-
|
|
28762
|
-
|
|
28763
|
-
|
|
28764
|
-
|
|
28765
|
-
|
|
28766
|
-
|
|
28767
|
-
|
|
28716
|
+
/**
|
|
28717
|
+
* Validate a single URL by attempting to load it
|
|
28718
|
+
*/
|
|
28719
|
+
/**
|
|
28720
|
+
* Validate an image URL by attempting to load it as an Image
|
|
28721
|
+
*/
|
|
28722
|
+
async function validateImageUrl(url, timeout = 5000) {
|
|
28723
|
+
if (!url || typeof url !== 'string') {
|
|
28724
|
+
return { isValid: false, error: 'Invalid URL format' };
|
|
28725
|
+
}
|
|
28726
|
+
try {
|
|
28727
|
+
new URL(url);
|
|
28728
|
+
}
|
|
28729
|
+
catch (e) {
|
|
28730
|
+
return { isValid: false, error: 'Invalid URL structure' };
|
|
28731
|
+
}
|
|
28732
|
+
return new Promise((resolve) => {
|
|
28733
|
+
const img = new Image();
|
|
28734
|
+
const timeoutId = setTimeout(() => {
|
|
28735
|
+
img.src = '';
|
|
28736
|
+
resolve({ isValid: false, error: 'Image load timeout' });
|
|
28737
|
+
}, timeout);
|
|
28738
|
+
img.onload = () => {
|
|
28739
|
+
clearTimeout(timeoutId);
|
|
28740
|
+
resolve({ isValid: true });
|
|
28768
28741
|
};
|
|
28769
|
-
|
|
28770
|
-
|
|
28771
|
-
|
|
28772
|
-
|
|
28742
|
+
img.onerror = () => {
|
|
28743
|
+
clearTimeout(timeoutId);
|
|
28744
|
+
resolve({ isValid: false, error: 'Failed to load image' });
|
|
28745
|
+
};
|
|
28746
|
+
img.src = url;
|
|
28747
|
+
});
|
|
28748
|
+
}
|
|
28749
|
+
/**
|
|
28750
|
+
* Validate multiple image URLs concurrently
|
|
28751
|
+
*/
|
|
28752
|
+
async function validateImageUrls(urls, timeout = 5000, maxConcurrent = 10) {
|
|
28753
|
+
const results = new Map();
|
|
28754
|
+
// Process in batches to limit concurrent requests
|
|
28755
|
+
for (let i = 0; i < urls.length; i += maxConcurrent) {
|
|
28756
|
+
const batch = urls.slice(i, i + maxConcurrent);
|
|
28757
|
+
const batchResults = await Promise.all(batch.map(async (url) => {
|
|
28758
|
+
const result = await validateImageUrl(url, timeout);
|
|
28759
|
+
return { url, result };
|
|
28760
|
+
}));
|
|
28761
|
+
batchResults.forEach(({ url, result }) => {
|
|
28762
|
+
results.set(url, result);
|
|
28763
|
+
});
|
|
28764
|
+
}
|
|
28765
|
+
return results;
|
|
28766
|
+
}
|
|
28767
|
+
/**
|
|
28768
|
+
* Validate image items and separate valid from invalid
|
|
28769
|
+
*/
|
|
28770
|
+
async function validateImageItems(items, timeout = 5000) {
|
|
28771
|
+
const urls = items.map(item => item.url).filter(Boolean);
|
|
28772
|
+
if (urls.length === 0) {
|
|
28773
|
+
return { validItems: [], invalidItems: items };
|
|
28774
|
+
}
|
|
28775
|
+
const validationResults = await validateImageUrls(urls, timeout);
|
|
28776
|
+
const validItems = [];
|
|
28777
|
+
const invalidItems = [];
|
|
28778
|
+
items.forEach(item => {
|
|
28779
|
+
if (!item.url) {
|
|
28780
|
+
invalidItems.push({
|
|
28781
|
+
...item,
|
|
28782
|
+
isValid: false,
|
|
28783
|
+
validationError: 'Missing URL',
|
|
28784
|
+
});
|
|
28785
|
+
return;
|
|
28786
|
+
}
|
|
28787
|
+
const result = validationResults.get(item.url);
|
|
28788
|
+
if (result?.isValid) {
|
|
28789
|
+
validItems.push({
|
|
28790
|
+
...item,
|
|
28791
|
+
isValid: true,
|
|
28792
|
+
});
|
|
28793
|
+
}
|
|
28794
|
+
else {
|
|
28795
|
+
invalidItems.push({
|
|
28796
|
+
...item,
|
|
28797
|
+
isValid: false,
|
|
28798
|
+
validationError: result?.error || 'Unknown error',
|
|
28799
|
+
});
|
|
28800
|
+
}
|
|
28801
|
+
});
|
|
28802
|
+
return { validItems, invalidItems };
|
|
28803
|
+
}
|
|
28804
|
+
/**
|
|
28805
|
+
* Preload and validate images before rendering
|
|
28806
|
+
* Returns items with validation status
|
|
28807
|
+
*/
|
|
28808
|
+
async function preloadAndValidateImages(items, timeout = 5000) {
|
|
28809
|
+
const { validItems, invalidItems } = await validateImageItems(items, timeout);
|
|
28810
|
+
if (invalidItems.length > 0) {
|
|
28811
|
+
console.warn('[URLValidator] Invalid image URLs detected:', invalidItems.map(i => ({
|
|
28812
|
+
url: i.url,
|
|
28813
|
+
error: i.validationError,
|
|
28814
|
+
})));
|
|
28815
|
+
}
|
|
28816
|
+
return [...validItems, ...invalidItems];
|
|
28817
|
+
}
|
|
28818
|
+
|
|
28819
|
+
function determineLayout(items, requestedLayout) {
|
|
28820
|
+
// Single item always uses single layout
|
|
28821
|
+
if (items.length === 1) {
|
|
28822
|
+
return 'single';
|
|
28823
|
+
}
|
|
28824
|
+
// For multiple items, use gallery (which now supports overlay titles)
|
|
28825
|
+
// Cards and gallery are now visually the same - 2-column grid with overlay titles
|
|
28826
|
+
return 'gallery';
|
|
28827
|
+
}
|
|
28828
|
+
function determineColumns(itemCount, maxColumns = 2) {
|
|
28829
|
+
if (itemCount === 1)
|
|
28830
|
+
return 1;
|
|
28831
|
+
return Math.min(2, maxColumns);
|
|
28832
|
+
}
|
|
28833
|
+
function StructuredImageDisplay({ action, onComplete, accentColor, maxColumns = 3 }) {
|
|
28834
|
+
const rawState = action.input;
|
|
28835
|
+
const hasCompletedRef = useRef(false);
|
|
28836
|
+
const [validatedItems, setValidatedItems] = useState([]);
|
|
28837
|
+
const [isValidating, setIsValidating] = useState(true);
|
|
28838
|
+
const [validationErrors, setValidationErrors] = useState([]);
|
|
28839
|
+
// Provide safe defaults if state is missing
|
|
28840
|
+
const state = {
|
|
28841
|
+
items: rawState?.items || [],
|
|
28842
|
+
layout: rawState?.layout || 'single',
|
|
28843
|
+
context: rawState?.context,
|
|
28844
|
+
columns: rawState?.columns,
|
|
28845
|
+
status: rawState?.status || 'displaying',
|
|
28846
|
+
error: rawState?.error,
|
|
28847
|
+
};
|
|
28848
|
+
const isError = state.status === 'error';
|
|
28849
|
+
// Validate URLs before displaying
|
|
28850
|
+
useEffect(() => {
|
|
28851
|
+
if (state.items.length === 0) {
|
|
28852
|
+
setIsValidating(false);
|
|
28853
|
+
return;
|
|
28854
|
+
}
|
|
28855
|
+
let isMounted = true;
|
|
28856
|
+
const validateItems = async () => {
|
|
28857
|
+
try {
|
|
28858
|
+
const validated = await preloadAndValidateImages(state.items, 3000);
|
|
28859
|
+
if (!isMounted)
|
|
28860
|
+
return;
|
|
28861
|
+
const valid = validated.filter(item => item.isValid !== false);
|
|
28862
|
+
const invalid = validated.filter(item => item.isValid === false);
|
|
28863
|
+
setValidatedItems(valid);
|
|
28864
|
+
if (invalid.length > 0) {
|
|
28865
|
+
const errors = invalid.map(item => `${item.url}: ${item.validationError || 'Failed to load'}`);
|
|
28866
|
+
setValidationErrors(errors);
|
|
28867
|
+
console.warn('[StructuredImageDisplay] Filtered invalid URLs:', errors);
|
|
28868
|
+
}
|
|
28773
28869
|
}
|
|
28774
|
-
|
|
28775
|
-
|
|
28776
|
-
|
|
28777
|
-
|
|
28778
|
-
|
|
28779
|
-
|
|
28780
|
-
|
|
28781
|
-
|
|
28782
|
-
|
|
28870
|
+
catch (error) {
|
|
28871
|
+
console.error('[StructuredImageDisplay] Validation error:', error);
|
|
28872
|
+
// Fallback to unvalidated items on error
|
|
28873
|
+
if (isMounted) {
|
|
28874
|
+
setValidatedItems(state.items);
|
|
28875
|
+
}
|
|
28876
|
+
}
|
|
28877
|
+
finally {
|
|
28878
|
+
if (isMounted) {
|
|
28879
|
+
setIsValidating(false);
|
|
28880
|
+
}
|
|
28881
|
+
}
|
|
28882
|
+
};
|
|
28883
|
+
validateItems();
|
|
28884
|
+
return () => {
|
|
28885
|
+
isMounted = false;
|
|
28886
|
+
};
|
|
28887
|
+
}, [state.items]);
|
|
28888
|
+
// Auto-complete on mount so AI can continue generating text response
|
|
28889
|
+
useEffect(() => {
|
|
28890
|
+
if (!action.done && !hasCompletedRef.current && onComplete && state.items.length > 0) {
|
|
28891
|
+
hasCompletedRef.current = true;
|
|
28892
|
+
onComplete(action.toolCallId, { ...state, status: 'displaying' });
|
|
28893
|
+
}
|
|
28894
|
+
}, [action.done, action.toolCallId, onComplete, state]);
|
|
28895
|
+
const handleInteraction = () => {
|
|
28896
|
+
onComplete?.(action.toolCallId, { ...state, status: 'interacted' });
|
|
28897
|
+
};
|
|
28898
|
+
const handleLinkClick = (url) => {
|
|
28899
|
+
handleInteraction();
|
|
28900
|
+
};
|
|
28901
|
+
const style = accentColor ? { '--action-accent': accentColor } : undefined;
|
|
28902
|
+
// Error state
|
|
28903
|
+
if (isError) {
|
|
28904
|
+
return (jsx("div", { className: "ai-chat-structured-image ai-chat-structured-image--error", style: style, children: jsx("div", { className: "ai-chat-structured-image__error", children: state.error || 'Failed to load media' }) }));
|
|
28905
|
+
}
|
|
28906
|
+
// Loading state
|
|
28907
|
+
if (isValidating) {
|
|
28908
|
+
return (jsxs("div", { className: "ai-chat-structured-image", style: style, children: [state.context && (jsx("p", { className: "ai-chat-structured-image__context", children: state.context })), jsx("div", { className: "ai-chat-structured-image__loading", children: "Validating images..." })] }));
|
|
28909
|
+
}
|
|
28910
|
+
// No valid items after validation
|
|
28911
|
+
if (validatedItems.length === 0) {
|
|
28912
|
+
return (jsxs("div", { className: "ai-chat-structured-image", style: style, children: [state.context && (jsx("p", { className: "ai-chat-structured-image__context", children: state.context })), validationErrors.length > 0 && (jsx("div", { className: "ai-chat-structured-image__error", children: "All images failed to load. Please check the URLs." }))] }));
|
|
28913
|
+
}
|
|
28914
|
+
const layout = determineLayout(validatedItems, state.layout);
|
|
28915
|
+
const columns = state.columns || determineColumns(validatedItems.length, maxColumns);
|
|
28916
|
+
return (jsxs("div", { className: "ai-chat-structured-image", style: style, children: [state.context && (jsx("p", { className: "ai-chat-structured-image__context", children: state.context })), validationErrors.length > 0 && (jsxs("div", { className: "ai-chat-structured-image__warning", children: [validationErrors.length, " image(s) could not be loaded and were filtered."] })), layout === 'single' && validatedItems[0] && (jsx(ImageCard, { item: validatedItems[0], accentColor: accentColor, onLinkClick: handleLinkClick })), layout === 'gallery' && (jsx(ImageGallery, { items: validatedItems, columns: columns, accentColor: accentColor, maxColumns: maxColumns })), layout === 'cards' && (jsx("div", { className: "ai-chat-structured-image__cards", style: { '--card-columns': validatedItems.length === 1 ? 1 : 2 }, children: validatedItems.map((item, index) => (jsx(ProjectCard, { item: item, accentColor: accentColor, onLinkClick: handleLinkClick, variant: "overlay" }, index))) })), layout === 'carousel' && (jsx(ImageCarousel, { items: validatedItems, accentColor: accentColor, onLinkClick: handleLinkClick }))] }));
|
|
28917
|
+
}
|
|
28918
|
+
function ChevronLeftIcon() {
|
|
28919
|
+
return (jsx("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: jsx("polyline", { points: "15 18 9 12 15 6" }) }));
|
|
28920
|
+
}
|
|
28921
|
+
function ChevronRightIcon() {
|
|
28922
|
+
return (jsx("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: jsx("polyline", { points: "9 18 15 12 9 6" }) }));
|
|
28923
|
+
}
|
|
28924
|
+
function ImageCarousel({ items, accentColor, onLinkClick }) {
|
|
28925
|
+
const [currentIndex, setCurrentIndex] = React.useState(0);
|
|
28926
|
+
const containerRef = useRef(null);
|
|
28927
|
+
const goToPrevious = () => {
|
|
28928
|
+
setCurrentIndex(prev => prev === 0 ? items.length - 1 : prev - 1);
|
|
28783
28929
|
};
|
|
28930
|
+
const goToNext = () => {
|
|
28931
|
+
setCurrentIndex(prev => prev === items.length - 1 ? 0 : prev + 1);
|
|
28932
|
+
};
|
|
28933
|
+
const style = accentColor ? { '--action-accent': accentColor } : undefined;
|
|
28934
|
+
return (jsxs("div", { className: "ai-chat-image-carousel", style: style, ref: containerRef, children: [jsx("div", { className: "ai-chat-image-carousel__track", style: { transform: `translateX(-${currentIndex * 100}%)` }, children: items.map((item, index) => (jsx("div", { className: "ai-chat-image-carousel__slide", children: jsx(ProjectCard, { item: item, accentColor: accentColor, onLinkClick: onLinkClick }) }, index))) }), items.length > 1 && (jsxs(Fragment, { children: [jsx("button", { className: "ai-chat-image-carousel__nav prev", onClick: goToPrevious, "aria-label": "Previous", children: jsx(ChevronLeftIcon, {}) }), jsx("button", { className: "ai-chat-image-carousel__nav next", onClick: goToNext, "aria-label": "Next", children: jsx(ChevronRightIcon, {}) }), jsx("div", { className: "ai-chat-image-carousel__dots", children: items.map((_, index) => (jsx("button", { className: `ai-chat-image-carousel__dot ${index === currentIndex ? 'active' : ''}`, onClick: () => setCurrentIndex(index), "aria-label": `Go to slide ${index + 1}` }, index))) })] }))] }));
|
|
28935
|
+
}
|
|
28936
|
+
|
|
28937
|
+
const pendingResolvers = new Map();
|
|
28938
|
+
const resumeCallbacks = new Map();
|
|
28939
|
+
const frontendActionHandlers = {};
|
|
28940
|
+
const actionRenderers = {};
|
|
28941
|
+
function getFrontendActionHandler(implementation) {
|
|
28942
|
+
return frontendActionHandlers[implementation];
|
|
28943
|
+
}
|
|
28944
|
+
function getActionRenderer(implementation) {
|
|
28945
|
+
return actionRenderers[implementation];
|
|
28946
|
+
}
|
|
28947
|
+
function waitForActionState(toolCallId) {
|
|
28948
|
+
return new Promise((resolve) => {
|
|
28949
|
+
pendingResolvers.set(toolCallId, resolve);
|
|
28950
|
+
});
|
|
28951
|
+
}
|
|
28952
|
+
function resolveActionState(toolCallId, state) {
|
|
28953
|
+
const resolver = pendingResolvers.get(toolCallId);
|
|
28954
|
+
if (resolver) {
|
|
28955
|
+
pendingResolvers.delete(toolCallId);
|
|
28956
|
+
resolver(state);
|
|
28957
|
+
return;
|
|
28958
|
+
}
|
|
28959
|
+
const resumeCallback = resumeCallbacks.get(toolCallId);
|
|
28960
|
+
if (resumeCallback) {
|
|
28961
|
+
resumeCallback(state).catch((error) => {
|
|
28962
|
+
console.error("[Action] Failed to resume action:", error);
|
|
28963
|
+
});
|
|
28964
|
+
}
|
|
28965
|
+
}
|
|
28966
|
+
function registerActionResumeCallback(toolCallId, callback) {
|
|
28967
|
+
resumeCallbacks.set(toolCallId, callback);
|
|
28968
|
+
}
|
|
28969
|
+
function unregisterActionResumeCallback(toolCallId) {
|
|
28970
|
+
resumeCallbacks.delete(toolCallId);
|
|
28784
28971
|
}
|
|
28785
28972
|
|
|
28786
28973
|
/**
|
|
@@ -28788,9 +28975,15 @@ function registerMicrosoftCalendarAction() {
|
|
|
28788
28975
|
* Called by initializeActionHandlers to prevent tree-shaking.
|
|
28789
28976
|
*/
|
|
28790
28977
|
function registerLinkPreviewAction() {
|
|
28791
|
-
// Handler - auto-completes immediately
|
|
28978
|
+
// Handler - auto-completes immediately, returns body for agent
|
|
28792
28979
|
frontendActionHandlers["link-preview"] = async (_input, state, _context) => {
|
|
28793
|
-
|
|
28980
|
+
const links = state?.links || [];
|
|
28981
|
+
// Return body directly - sent to backend via continueAgentAction
|
|
28982
|
+
return {
|
|
28983
|
+
status: "displayed",
|
|
28984
|
+
linkCount: links.length,
|
|
28985
|
+
message: `Displayed ${links.length} link preview${links.length > 1 ? 's' : ''}.`,
|
|
28986
|
+
};
|
|
28794
28987
|
};
|
|
28795
28988
|
// Renderer - displays the link preview card
|
|
28796
28989
|
actionRenderers["link-preview"] = (message, accentColor) => {
|
|
@@ -28800,16 +28993,15 @@ function registerLinkPreviewAction() {
|
|
|
28800
28993
|
const handleComplete = (toolCallId, newState) => {
|
|
28801
28994
|
resolveActionState(toolCallId, newState);
|
|
28802
28995
|
};
|
|
28803
|
-
// Check if action
|
|
28804
|
-
const
|
|
28805
|
-
const status =
|
|
28806
|
-
const isDone = action.done || status === "displaying" || status === "clicked";
|
|
28996
|
+
// Check if action input indicates it's already complete (displaying or clicked)
|
|
28997
|
+
const input = action.input;
|
|
28998
|
+
const status = input?.status;
|
|
28999
|
+
const isDone = action.done || status === "displaying" || status === "displayed" || status === "clicked";
|
|
28807
29000
|
return (jsx(LinkPreviewCard, { action: {
|
|
28808
29001
|
implementation: action.implementation,
|
|
28809
29002
|
toolCallId: action.toolCallId,
|
|
28810
29003
|
actionId: action.actionId,
|
|
28811
29004
|
input: action.input,
|
|
28812
|
-
state: action.state,
|
|
28813
29005
|
done: isDone,
|
|
28814
29006
|
}, onComplete: handleComplete, accentColor: accentColor }));
|
|
28815
29007
|
};
|
|
@@ -28820,9 +29012,15 @@ function registerLinkPreviewAction() {
|
|
|
28820
29012
|
* Called by initializeActionHandlers to prevent tree-shaking.
|
|
28821
29013
|
*/
|
|
28822
29014
|
function registerVideoPlayerAction() {
|
|
28823
|
-
// Handler - auto-completes immediately
|
|
29015
|
+
// Handler - auto-completes immediately, returns body for agent
|
|
28824
29016
|
frontendActionHandlers["video-player"] = async (_input, state, _context) => {
|
|
28825
|
-
|
|
29017
|
+
const videos = state?.videos || [];
|
|
29018
|
+
// Return body directly - sent to backend via continueAgentAction
|
|
29019
|
+
return {
|
|
29020
|
+
status: "displayed",
|
|
29021
|
+
videoCount: videos.length,
|
|
29022
|
+
message: `Displayed ${videos.length} video${videos.length !== 1 ? 's' : ''}.`,
|
|
29023
|
+
};
|
|
28826
29024
|
};
|
|
28827
29025
|
// Renderer - displays the embedded video player card
|
|
28828
29026
|
actionRenderers["video-player"] = (message, accentColor) => {
|
|
@@ -28832,16 +29030,15 @@ function registerVideoPlayerAction() {
|
|
|
28832
29030
|
const handleComplete = (toolCallId, newState) => {
|
|
28833
29031
|
resolveActionState(toolCallId, newState);
|
|
28834
29032
|
};
|
|
28835
|
-
// Check if action
|
|
28836
|
-
const
|
|
28837
|
-
const status =
|
|
28838
|
-
const isDone = action.done || status === "displaying" || status === "played";
|
|
29033
|
+
// Check if action input indicates it's already complete (displaying or played)
|
|
29034
|
+
const input = action.input;
|
|
29035
|
+
const status = input?.status;
|
|
29036
|
+
const isDone = action.done || status === "displaying" || status === "displayed" || status === "played";
|
|
28839
29037
|
return (jsx(VideoPlayerCard, { action: {
|
|
28840
29038
|
implementation: action.implementation,
|
|
28841
29039
|
toolCallId: action.toolCallId,
|
|
28842
29040
|
actionId: action.actionId,
|
|
28843
29041
|
input: action.input,
|
|
28844
|
-
state: action.state,
|
|
28845
29042
|
done: isDone,
|
|
28846
29043
|
}, onComplete: handleComplete, accentColor: accentColor }));
|
|
28847
29044
|
};
|
|
@@ -28852,9 +29049,15 @@ function registerVideoPlayerAction() {
|
|
|
28852
29049
|
* Called by initializeActionHandlers to prevent tree-shaking.
|
|
28853
29050
|
*/
|
|
28854
29051
|
function registerLocationCardAction() {
|
|
28855
|
-
// Handler - auto-completes immediately
|
|
29052
|
+
// Handler - auto-completes immediately, returns body for agent
|
|
28856
29053
|
frontendActionHandlers["location-card"] = async (_input, state, _context) => {
|
|
28857
|
-
|
|
29054
|
+
const locations = state?.locations || [];
|
|
29055
|
+
// Return body directly - sent to backend via continueAgentAction
|
|
29056
|
+
return {
|
|
29057
|
+
status: "displayed",
|
|
29058
|
+
locationCount: locations.length,
|
|
29059
|
+
message: `Displayed ${locations.length} location${locations.length !== 1 ? 's' : ''}.`,
|
|
29060
|
+
};
|
|
28858
29061
|
};
|
|
28859
29062
|
// Renderer - displays the location card
|
|
28860
29063
|
actionRenderers["location-card"] = (message, accentColor, variant) => {
|
|
@@ -28864,25 +29067,30 @@ function registerLocationCardAction() {
|
|
|
28864
29067
|
const handleComplete = (toolCallId, newState) => {
|
|
28865
29068
|
resolveActionState(toolCallId, newState);
|
|
28866
29069
|
};
|
|
28867
|
-
// Check if action
|
|
28868
|
-
const
|
|
28869
|
-
const status =
|
|
28870
|
-
const isDone = action.done || status === "displaying" || status === "directions_opened";
|
|
29070
|
+
// Check if action input indicates it's already complete
|
|
29071
|
+
const input = action.input;
|
|
29072
|
+
const status = input?.status;
|
|
29073
|
+
const isDone = action.done || status === "displaying" || status === "displayed" || status === "directions_opened";
|
|
28871
29074
|
return (jsx(LocationCard, { action: {
|
|
28872
29075
|
implementation: action.implementation,
|
|
28873
29076
|
toolCallId: action.toolCallId,
|
|
28874
29077
|
actionId: action.actionId,
|
|
28875
29078
|
input: action.input,
|
|
28876
|
-
state: action.state,
|
|
28877
29079
|
done: isDone,
|
|
28878
29080
|
}, onComplete: handleComplete, accentColor: accentColor, maxColumns: variant === 'fullpage' ? 3 : 1 }));
|
|
28879
29081
|
};
|
|
28880
29082
|
}
|
|
28881
29083
|
|
|
28882
29084
|
function registerContactCardAction() {
|
|
28883
|
-
// Handler - auto-completes immediately
|
|
29085
|
+
// Handler - auto-completes immediately, returns body for agent
|
|
28884
29086
|
frontendActionHandlers['contact-card'] = async (_input, state, _context) => {
|
|
28885
|
-
|
|
29087
|
+
const contacts = state?.contacts || [];
|
|
29088
|
+
// Return body directly - sent to backend via continueAgentAction
|
|
29089
|
+
return {
|
|
29090
|
+
status: 'displayed',
|
|
29091
|
+
contactCount: contacts.length,
|
|
29092
|
+
message: `Displayed ${contacts.length} contact${contacts.length !== 1 ? 's' : ''}.`,
|
|
29093
|
+
};
|
|
28886
29094
|
};
|
|
28887
29095
|
// Renderer - displays the contact card
|
|
28888
29096
|
actionRenderers['contact-card'] = (message, accentColor, variant) => {
|
|
@@ -28892,16 +29100,15 @@ function registerContactCardAction() {
|
|
|
28892
29100
|
const handleComplete = (toolCallId, newState) => {
|
|
28893
29101
|
resolveActionState(toolCallId, newState);
|
|
28894
29102
|
};
|
|
28895
|
-
// Check if action
|
|
28896
|
-
const
|
|
28897
|
-
const status =
|
|
28898
|
-
const isDone = action.done || status === 'displaying' || status === 'contacted';
|
|
29103
|
+
// Check if action input indicates it's already complete
|
|
29104
|
+
const input = action.input;
|
|
29105
|
+
const status = input?.status;
|
|
29106
|
+
const isDone = action.done || status === 'displaying' || status === 'displayed' || status === 'contacted';
|
|
28899
29107
|
return (jsx(ContactCard, { action: {
|
|
28900
29108
|
implementation: action.implementation,
|
|
28901
29109
|
toolCallId: action.toolCallId,
|
|
28902
29110
|
actionId: action.actionId,
|
|
28903
29111
|
input: action.input,
|
|
28904
|
-
state: action.state,
|
|
28905
29112
|
done: isDone,
|
|
28906
29113
|
}, onComplete: handleComplete, accentColor: accentColor, maxColumns: variant === 'fullpage' ? 3 : 1 }));
|
|
28907
29114
|
};
|
|
@@ -28921,16 +29128,15 @@ function registerQueryContactDirectoryAction() {
|
|
|
28921
29128
|
const handleComplete = (toolCallId, newState) => {
|
|
28922
29129
|
resolveActionState(toolCallId, newState);
|
|
28923
29130
|
};
|
|
28924
|
-
// Check if action
|
|
28925
|
-
const
|
|
28926
|
-
const status =
|
|
29131
|
+
// Check if action input indicates it's already complete
|
|
29132
|
+
const input = action.input;
|
|
29133
|
+
const status = input?.status;
|
|
28927
29134
|
const isDone = action.done || status === 'displaying' || status === 'contacted';
|
|
28928
29135
|
return (jsx(ContactCard, { action: {
|
|
28929
29136
|
implementation: action.implementation,
|
|
28930
29137
|
toolCallId: action.toolCallId,
|
|
28931
29138
|
actionId: action.actionId,
|
|
28932
29139
|
input: action.input,
|
|
28933
|
-
state: action.state,
|
|
28934
29140
|
done: isDone,
|
|
28935
29141
|
}, onComplete: handleComplete, accentColor: accentColor, maxColumns: variant === 'fullpage' ? 3 : 1 }));
|
|
28936
29142
|
};
|
|
@@ -28942,7 +29148,9 @@ function registerDisplayFormAction() {
|
|
|
28942
29148
|
return waitForActionState(context.toolCallId);
|
|
28943
29149
|
};
|
|
28944
29150
|
// Renderer - displays the form card
|
|
28945
|
-
|
|
29151
|
+
// Rendering is based only on action.input and action.done
|
|
29152
|
+
// When done=true, FormCard shows a simple completion indicator
|
|
29153
|
+
actionRenderers['display-form'] = (message, accentColor, _variant, onActionDismiss) => {
|
|
28946
29154
|
const action = message.action;
|
|
28947
29155
|
if (!action)
|
|
28948
29156
|
return null;
|
|
@@ -28950,22 +29158,14 @@ function registerDisplayFormAction() {
|
|
|
28950
29158
|
resolveActionState(toolCallId, newState);
|
|
28951
29159
|
};
|
|
28952
29160
|
const handleDismiss = onActionDismiss
|
|
28953
|
-
? (toolCallId) =>
|
|
28954
|
-
resolveActionState(toolCallId, { __dismissed: true });
|
|
28955
|
-
onActionDismiss(toolCallId);
|
|
28956
|
-
}
|
|
29161
|
+
? (toolCallId) => onActionDismiss(toolCallId)
|
|
28957
29162
|
: undefined;
|
|
28958
|
-
// Check if action state indicates it's already complete
|
|
28959
|
-
const state = action.state;
|
|
28960
|
-
const status = state?.status;
|
|
28961
|
-
const isDone = action.done || status === 'completed' || status === 'submitted';
|
|
28962
29163
|
return (jsx(FormCard, { action: {
|
|
28963
29164
|
implementation: action.implementation,
|
|
28964
29165
|
toolCallId: action.toolCallId,
|
|
28965
29166
|
actionId: action.actionId,
|
|
28966
29167
|
input: action.input,
|
|
28967
|
-
|
|
28968
|
-
done: isDone,
|
|
29168
|
+
done: action.done ?? false,
|
|
28969
29169
|
}, onComplete: handleComplete, onDismiss: handleDismiss, accentColor: accentColor }));
|
|
28970
29170
|
};
|
|
28971
29171
|
}
|
|
@@ -28988,21 +29188,64 @@ function registerBookContactAppointmentAction() {
|
|
|
28988
29188
|
// Register the handler
|
|
28989
29189
|
registerBookContactAppointmentHandler();
|
|
28990
29190
|
// Register the renderer
|
|
28991
|
-
actionRenderers['book-contact-appointment'] = (message, accentColor) => {
|
|
29191
|
+
actionRenderers['book-contact-appointment'] = (message, accentColor, _variant, _onActionDismiss, onCallEndpoint) => {
|
|
28992
29192
|
const action = message.action;
|
|
28993
29193
|
if (!action)
|
|
28994
29194
|
return null;
|
|
28995
29195
|
const handleComplete = (toolCallId, newState) => {
|
|
28996
29196
|
resolveActionState(toolCallId, newState);
|
|
28997
29197
|
};
|
|
29198
|
+
// Create action-specific endpoint callback
|
|
29199
|
+
// Include toolCallId for done flag enforcement (server rejects calls for completed actions)
|
|
29200
|
+
const handleCallEndpoint = onCallEndpoint
|
|
29201
|
+
? async (endpoint, input, options) => {
|
|
29202
|
+
return onCallEndpoint(action.actionId, endpoint, input, { ...options, toolCallId: action.toolCallId });
|
|
29203
|
+
}
|
|
29204
|
+
: undefined;
|
|
29205
|
+
// Create dismiss handler
|
|
29206
|
+
const handleDismiss = _onActionDismiss
|
|
29207
|
+
? (toolCallId) => _onActionDismiss(toolCallId)
|
|
29208
|
+
: undefined;
|
|
28998
29209
|
return (jsx(BookContactAppointmentCard, { action: {
|
|
28999
29210
|
implementation: action.implementation,
|
|
29000
29211
|
toolCallId: action.toolCallId,
|
|
29001
29212
|
actionId: action.actionId,
|
|
29002
29213
|
input: action.input,
|
|
29003
|
-
state: action.state,
|
|
29004
29214
|
done: action.done ?? false,
|
|
29005
|
-
}, onComplete: handleComplete, accentColor: accentColor }));
|
|
29215
|
+
}, onComplete: handleComplete, onDismiss: handleDismiss, onCallEndpoint: handleCallEndpoint, accentColor: accentColor }));
|
|
29216
|
+
};
|
|
29217
|
+
}
|
|
29218
|
+
|
|
29219
|
+
function registerStructuredImageAction() {
|
|
29220
|
+
// Handler - auto-completes immediately, returns body for agent
|
|
29221
|
+
frontendActionHandlers['structured-image'] = async (_input, state, _context) => {
|
|
29222
|
+
const images = state?.images || [];
|
|
29223
|
+
// Return body directly - sent to backend via continueAgentAction
|
|
29224
|
+
return {
|
|
29225
|
+
status: 'displayed',
|
|
29226
|
+
imageCount: images.length,
|
|
29227
|
+
message: `Displayed ${images.length} image${images.length !== 1 ? 's' : ''}.`,
|
|
29228
|
+
};
|
|
29229
|
+
};
|
|
29230
|
+
// Renderer - displays the image content
|
|
29231
|
+
actionRenderers['structured-image'] = (message, accentColor, variant, onActionDismiss) => {
|
|
29232
|
+
const action = message.action;
|
|
29233
|
+
if (!action)
|
|
29234
|
+
return null;
|
|
29235
|
+
const handleComplete = (toolCallId, newState) => {
|
|
29236
|
+
resolveActionState(toolCallId, newState);
|
|
29237
|
+
};
|
|
29238
|
+
// Check if action input indicates it's already complete
|
|
29239
|
+
const input = action.input;
|
|
29240
|
+
const status = input?.status;
|
|
29241
|
+
const isDone = action.done || status === 'displaying' || status === 'displayed' || status === 'interacted';
|
|
29242
|
+
return (jsx(StructuredImageDisplay, { action: {
|
|
29243
|
+
implementation: action.implementation,
|
|
29244
|
+
toolCallId: action.toolCallId,
|
|
29245
|
+
actionId: action.actionId,
|
|
29246
|
+
input: action.input,
|
|
29247
|
+
done: isDone,
|
|
29248
|
+
}, onComplete: handleComplete, onDismiss: onActionDismiss ? () => onActionDismiss(action.toolCallId) : undefined, accentColor: accentColor, maxColumns: variant === 'fullpage' ? 3 : 2 }));
|
|
29006
29249
|
};
|
|
29007
29250
|
}
|
|
29008
29251
|
|
|
@@ -29018,8 +29261,6 @@ function initializeActionHandlers() {
|
|
|
29018
29261
|
return;
|
|
29019
29262
|
initialized = true;
|
|
29020
29263
|
// Explicitly call each registration function to prevent tree-shaking
|
|
29021
|
-
registerGoogleCalendarAction();
|
|
29022
|
-
registerMicrosoftCalendarAction();
|
|
29023
29264
|
registerLinkPreviewAction();
|
|
29024
29265
|
registerVideoPlayerAction();
|
|
29025
29266
|
registerLocationCardAction();
|
|
@@ -29027,6 +29268,7 @@ function initializeActionHandlers() {
|
|
|
29027
29268
|
registerContactCardAction();
|
|
29028
29269
|
registerDisplayFormAction();
|
|
29029
29270
|
registerBookContactAppointmentAction();
|
|
29271
|
+
registerStructuredImageAction();
|
|
29030
29272
|
}
|
|
29031
29273
|
|
|
29032
29274
|
/**
|
|
@@ -29289,9 +29531,49 @@ function hydrateToolNames(messages) {
|
|
|
29289
29531
|
};
|
|
29290
29532
|
});
|
|
29291
29533
|
}
|
|
29534
|
+
/**
|
|
29535
|
+
* Clean up messages that were interrupted during streaming.
|
|
29536
|
+
* - Marks streaming messages as complete
|
|
29537
|
+
* - Removes empty assistant messages
|
|
29538
|
+
* - Marks incomplete tool calls as done
|
|
29539
|
+
*/
|
|
29540
|
+
function cleanupInterruptedMessages(messages) {
|
|
29541
|
+
return messages
|
|
29542
|
+
.map((msg) => {
|
|
29543
|
+
// Mark streaming messages as complete
|
|
29544
|
+
if (msg.isStreaming) {
|
|
29545
|
+
msg = { ...msg, isStreaming: false };
|
|
29546
|
+
}
|
|
29547
|
+
// Mark incomplete tool calls as done (they were interrupted)
|
|
29548
|
+
if (msg.message.role === 'tool' && msg.action && !msg.action.done) {
|
|
29549
|
+
msg = {
|
|
29550
|
+
...msg,
|
|
29551
|
+
action: {
|
|
29552
|
+
...msg.action,
|
|
29553
|
+
done: true,
|
|
29554
|
+
// Mark as interrupted so UI can show appropriate state
|
|
29555
|
+
input: {
|
|
29556
|
+
...msg.action.input,
|
|
29557
|
+
_interrupted: true,
|
|
29558
|
+
},
|
|
29559
|
+
},
|
|
29560
|
+
};
|
|
29561
|
+
}
|
|
29562
|
+
return msg;
|
|
29563
|
+
})
|
|
29564
|
+
// Remove empty assistant messages (streaming was interrupted before content)
|
|
29565
|
+
.filter((msg) => {
|
|
29566
|
+
if (msg.message.role === 'assistant') {
|
|
29567
|
+
const content = msg.message.content;
|
|
29568
|
+
return content && typeof content === 'string' && content.trim().length > 0;
|
|
29569
|
+
}
|
|
29570
|
+
return true;
|
|
29571
|
+
});
|
|
29572
|
+
}
|
|
29292
29573
|
function hydrateMessages(messages) {
|
|
29293
29574
|
const visibleMessages = messages.filter((message) => !message.action?.hidden);
|
|
29294
|
-
|
|
29575
|
+
const cleanedMessages = cleanupInterruptedMessages(visibleMessages);
|
|
29576
|
+
return hydrateToolNames(cleanedMessages);
|
|
29295
29577
|
}
|
|
29296
29578
|
|
|
29297
29579
|
function deriveErrorInfo(error) {
|
|
@@ -29361,6 +29643,75 @@ function deriveErrorInfo(error) {
|
|
|
29361
29643
|
return { message: 'Something went wrong. Please try again.' };
|
|
29362
29644
|
}
|
|
29363
29645
|
|
|
29646
|
+
/**
|
|
29647
|
+
* Stream Buffer
|
|
29648
|
+
* Smooths out streaming text rendering by buffering words and draining them
|
|
29649
|
+
* with requestAnimationFrame for a fluid typing effect.
|
|
29650
|
+
*/
|
|
29651
|
+
function createStreamBuffer() {
|
|
29652
|
+
return {
|
|
29653
|
+
pendingWords: [],
|
|
29654
|
+
displayedContent: '',
|
|
29655
|
+
streamEnded: false,
|
|
29656
|
+
rafHandle: null,
|
|
29657
|
+
};
|
|
29658
|
+
}
|
|
29659
|
+
function appendToBuffer(buffer, content) {
|
|
29660
|
+
// Split by whitespace while preserving the whitespace characters
|
|
29661
|
+
const words = content.split(/(\s+)/);
|
|
29662
|
+
buffer.pendingWords.push(...words.filter(w => w.length > 0));
|
|
29663
|
+
}
|
|
29664
|
+
function startBufferDrain(buffer, onContentUpdate) {
|
|
29665
|
+
if (buffer.rafHandle !== null) {
|
|
29666
|
+
return; // Already draining
|
|
29667
|
+
}
|
|
29668
|
+
const drainLoop = () => {
|
|
29669
|
+
if (buffer.pendingWords.length === 0) {
|
|
29670
|
+
if (buffer.streamEnded) {
|
|
29671
|
+
buffer.rafHandle = null;
|
|
29672
|
+
return;
|
|
29673
|
+
}
|
|
29674
|
+
// No words to render, wait for more
|
|
29675
|
+
buffer.rafHandle = requestAnimationFrame(drainLoop);
|
|
29676
|
+
return;
|
|
29677
|
+
}
|
|
29678
|
+
// Adaptive: render more words if buffer is filling up
|
|
29679
|
+
const wordsToRender = buffer.pendingWords.length > 20 ? 3 : 1;
|
|
29680
|
+
const chunk = buffer.pendingWords.splice(0, wordsToRender).join('');
|
|
29681
|
+
buffer.displayedContent += chunk;
|
|
29682
|
+
onContentUpdate(buffer.displayedContent);
|
|
29683
|
+
buffer.rafHandle = requestAnimationFrame(drainLoop);
|
|
29684
|
+
};
|
|
29685
|
+
buffer.rafHandle = requestAnimationFrame(drainLoop);
|
|
29686
|
+
}
|
|
29687
|
+
function flushBuffer(buffer) {
|
|
29688
|
+
buffer.streamEnded = true;
|
|
29689
|
+
if (buffer.rafHandle !== null) {
|
|
29690
|
+
cancelAnimationFrame(buffer.rafHandle);
|
|
29691
|
+
buffer.rafHandle = null;
|
|
29692
|
+
}
|
|
29693
|
+
const remaining = buffer.pendingWords.join('');
|
|
29694
|
+
buffer.displayedContent += remaining;
|
|
29695
|
+
buffer.pendingWords = [];
|
|
29696
|
+
return buffer.displayedContent;
|
|
29697
|
+
}
|
|
29698
|
+
function cancelBuffer(buffer) {
|
|
29699
|
+
if (buffer.rafHandle !== null) {
|
|
29700
|
+
cancelAnimationFrame(buffer.rafHandle);
|
|
29701
|
+
buffer.rafHandle = null;
|
|
29702
|
+
}
|
|
29703
|
+
buffer.streamEnded = true;
|
|
29704
|
+
}
|
|
29705
|
+
function resetBuffer(buffer) {
|
|
29706
|
+
if (buffer.rafHandle !== null) {
|
|
29707
|
+
cancelAnimationFrame(buffer.rafHandle);
|
|
29708
|
+
buffer.rafHandle = null;
|
|
29709
|
+
}
|
|
29710
|
+
buffer.pendingWords = [];
|
|
29711
|
+
buffer.displayedContent = '';
|
|
29712
|
+
buffer.streamEnded = false;
|
|
29713
|
+
}
|
|
29714
|
+
|
|
29364
29715
|
function createStreamState() {
|
|
29365
29716
|
return {
|
|
29366
29717
|
currentContent: "",
|
|
@@ -29370,6 +29721,7 @@ function createStreamState() {
|
|
|
29370
29721
|
sources: [],
|
|
29371
29722
|
toolCallToActionId: {},
|
|
29372
29723
|
requestId: generateMessageId(),
|
|
29724
|
+
buffer: createStreamBuffer(),
|
|
29373
29725
|
};
|
|
29374
29726
|
}
|
|
29375
29727
|
function upsertMessage(setState, message, isTyping) {
|
|
@@ -29432,6 +29784,8 @@ function finalizeToolMessage(streamState, setState, toolCallId, toolName) {
|
|
|
29432
29784
|
},
|
|
29433
29785
|
isStreaming: false,
|
|
29434
29786
|
toolExecuting: existingName,
|
|
29787
|
+
// Mark action as done when tool message is finalized
|
|
29788
|
+
action: entry.action ? { ...entry.action, done: true } : undefined,
|
|
29435
29789
|
};
|
|
29436
29790
|
});
|
|
29437
29791
|
return { ...prev, messages, isTyping: false, isLoading: false };
|
|
@@ -29440,26 +29794,34 @@ function finalizeToolMessage(streamState, setState, toolCallId, toolName) {
|
|
|
29440
29794
|
}
|
|
29441
29795
|
|
|
29442
29796
|
function handleContentEvent(event, streamState, onMessageUpdate, setState) {
|
|
29797
|
+
// Track full content for finalization
|
|
29443
29798
|
streamState.currentContent += event.content;
|
|
29444
|
-
|
|
29445
|
-
|
|
29446
|
-
|
|
29447
|
-
|
|
29448
|
-
|
|
29449
|
-
|
|
29450
|
-
|
|
29451
|
-
|
|
29452
|
-
|
|
29453
|
-
|
|
29454
|
-
|
|
29455
|
-
|
|
29456
|
-
|
|
29799
|
+
// Add to buffer for smooth rendering
|
|
29800
|
+
appendToBuffer(streamState.buffer, event.content);
|
|
29801
|
+
// Start drain loop if not already running
|
|
29802
|
+
startBufferDrain(streamState.buffer, (displayedContent) => {
|
|
29803
|
+
const assistantMessage = {
|
|
29804
|
+
id: streamState.currentMessageId,
|
|
29805
|
+
message: { role: "assistant", content: displayedContent },
|
|
29806
|
+
timestamp: new Date().toISOString(),
|
|
29807
|
+
sources: streamState.sources,
|
|
29808
|
+
isStreaming: true,
|
|
29809
|
+
};
|
|
29810
|
+
streamState.newMessageIds.add(assistantMessage.id);
|
|
29811
|
+
onMessageUpdate(assistantMessage);
|
|
29812
|
+
const hasContent = displayedContent.trim().length > 0;
|
|
29813
|
+
const isToolExecuting = streamState.activeToolCallCount > 0;
|
|
29814
|
+
const isTyping = (!hasContent && assistantMessage.isStreaming) || isToolExecuting;
|
|
29815
|
+
upsertMessage(setState, assistantMessage, isTyping);
|
|
29816
|
+
});
|
|
29457
29817
|
}
|
|
29458
29818
|
function handleToolStartEvent(event, streamState, onMessageUpdate, setState) {
|
|
29459
|
-
|
|
29819
|
+
// Flush buffer before tool starts
|
|
29820
|
+
const flushedContent = flushBuffer(streamState.buffer);
|
|
29821
|
+
if (flushedContent.trim()) {
|
|
29460
29822
|
const finalAssistant = {
|
|
29461
29823
|
id: streamState.currentMessageId,
|
|
29462
|
-
message: { role: "assistant", content:
|
|
29824
|
+
message: { role: "assistant", content: flushedContent },
|
|
29463
29825
|
timestamp: new Date().toISOString(),
|
|
29464
29826
|
sources: streamState.sources,
|
|
29465
29827
|
isStreaming: false,
|
|
@@ -29470,6 +29832,8 @@ function handleToolStartEvent(event, streamState, onMessageUpdate, setState) {
|
|
|
29470
29832
|
streamState.currentContent = "";
|
|
29471
29833
|
streamState.currentMessageId = generateMessageId();
|
|
29472
29834
|
}
|
|
29835
|
+
// Reset buffer for post-tool content
|
|
29836
|
+
resetBuffer(streamState.buffer);
|
|
29473
29837
|
const toolMessageId = event.tool_call_id;
|
|
29474
29838
|
streamState.activeToolCallCount += 1;
|
|
29475
29839
|
streamState.newMessageIds.add(toolMessageId);
|
|
@@ -29487,44 +29851,22 @@ function handleToolEndEvent(event, streamState, _onMessageUpdate, setState) {
|
|
|
29487
29851
|
streamState.activeToolCallCount = Math.max(0, streamState.activeToolCallCount - 1);
|
|
29488
29852
|
setState(prev => {
|
|
29489
29853
|
const messages = prev.messages.map((msg) => {
|
|
29490
|
-
|
|
29491
|
-
if (!matchesToolCall) {
|
|
29854
|
+
if (msg.message.role !== "tool" || msg.message.tool_call_id !== event.tool_call_id) {
|
|
29492
29855
|
return msg;
|
|
29493
29856
|
}
|
|
29494
|
-
const
|
|
29495
|
-
|
|
29496
|
-
|
|
29497
|
-
|
|
29498
|
-
|
|
29499
|
-
|
|
29500
|
-
|
|
29501
|
-
actionId: event.action_id,
|
|
29502
|
-
input: (event.input || {}),
|
|
29503
|
-
state: (event.state || {}),
|
|
29504
|
-
done: event.done,
|
|
29505
|
-
};
|
|
29506
|
-
}
|
|
29507
|
-
else if (action) {
|
|
29508
|
-
action = {
|
|
29509
|
-
...action,
|
|
29510
|
-
input: event.input ? event.input : action.input,
|
|
29511
|
-
state: event.state ? event.state : action.state,
|
|
29512
|
-
done: event.done,
|
|
29513
|
-
};
|
|
29514
|
-
}
|
|
29515
|
-
const updatedMsg = {
|
|
29857
|
+
const toolName = msg.message.name || event.tool_name;
|
|
29858
|
+
// For actions: just mark as done, preserve existing input (display data is immutable)
|
|
29859
|
+
// For non-action tools: update normally
|
|
29860
|
+
const action = msg.action
|
|
29861
|
+
? { ...msg.action, done: event.done }
|
|
29862
|
+
: undefined;
|
|
29863
|
+
return {
|
|
29516
29864
|
...msg,
|
|
29517
|
-
message: {
|
|
29518
|
-
role: "tool",
|
|
29519
|
-
content: event.state ? JSON.stringify(event.state) : (typeof msg.message.content === "string" ? msg.message.content : ""),
|
|
29520
|
-
tool_call_id: event.tool_call_id,
|
|
29521
|
-
name: existingName,
|
|
29522
|
-
},
|
|
29865
|
+
message: { ...msg.message, name: toolName },
|
|
29523
29866
|
isStreaming: false,
|
|
29524
|
-
toolExecuting:
|
|
29867
|
+
toolExecuting: toolName,
|
|
29525
29868
|
action,
|
|
29526
29869
|
};
|
|
29527
|
-
return updatedMsg;
|
|
29528
29870
|
});
|
|
29529
29871
|
return { ...prev, messages, isTyping: true, isLoading: false };
|
|
29530
29872
|
});
|
|
@@ -29557,11 +29899,6 @@ function handleToolErrorEvent(event, streamState, _onMessageUpdate, setState) {
|
|
|
29557
29899
|
return { ...prev, messages, isTyping: true, isLoading: false };
|
|
29558
29900
|
});
|
|
29559
29901
|
}
|
|
29560
|
-
function handleDoneEvent(event, streamState, _onMessageUpdate, setState) {
|
|
29561
|
-
streamState.sources = event.sources;
|
|
29562
|
-
streamState.toolCallToActionId = event.tool_call_to_action_id;
|
|
29563
|
-
finalizeStreamMessages(setState, streamState.newMessageIds, event.sources, event.tool_call_to_action_id, event.suggestions);
|
|
29564
|
-
}
|
|
29565
29902
|
function handleHaltEvent(event, _streamState, onMessageUpdate, setState) {
|
|
29566
29903
|
const toolNames = event.tool_calls.map(call => call.name).join(", ");
|
|
29567
29904
|
const notice = toolNames
|
|
@@ -29576,7 +29913,8 @@ function handleHaltEvent(event, _streamState, onMessageUpdate, setState) {
|
|
|
29576
29913
|
};
|
|
29577
29914
|
onMessageUpdate(haltMessage);
|
|
29578
29915
|
upsertMessage(setState, haltMessage, false);
|
|
29579
|
-
|
|
29916
|
+
// Stop loading/typing state when halted
|
|
29917
|
+
setState(prev => ({ ...prev, isLoading: false, isTyping: false, error: "Awaiting external tool handling." }));
|
|
29580
29918
|
}
|
|
29581
29919
|
function handleErrorEvent(_event, _streamState, onMessageUpdate, setState) {
|
|
29582
29920
|
const errorMessage = {
|
|
@@ -29588,6 +29926,8 @@ function handleErrorEvent(_event, _streamState, onMessageUpdate, setState) {
|
|
|
29588
29926
|
};
|
|
29589
29927
|
onMessageUpdate(errorMessage);
|
|
29590
29928
|
upsertMessage(setState, errorMessage, false);
|
|
29929
|
+
// Stop loading/typing state on error
|
|
29930
|
+
setState(prev => ({ ...prev, isLoading: false, isTyping: false }));
|
|
29591
29931
|
}
|
|
29592
29932
|
const eventHandlers = {
|
|
29593
29933
|
content: handleContentEvent,
|
|
@@ -29615,10 +29955,37 @@ function handleStreamEvent(event, streamState, onMessageUpdate, setState) {
|
|
|
29615
29955
|
console.warn('[Chat] Unknown event type:', event.type);
|
|
29616
29956
|
}
|
|
29617
29957
|
}
|
|
29618
|
-
|
|
29619
|
-
|
|
29620
|
-
|
|
29958
|
+
function handleDoneEvent(event, streamState, onMessageUpdate, setState) {
|
|
29959
|
+
// Flush any remaining buffered content
|
|
29960
|
+
const flushedContent = flushBuffer(streamState.buffer);
|
|
29961
|
+
// Update the final message with complete content if there was content
|
|
29962
|
+
if (flushedContent.trim()) {
|
|
29963
|
+
const finalMessage = {
|
|
29964
|
+
id: streamState.currentMessageId,
|
|
29965
|
+
message: { role: "assistant", content: flushedContent },
|
|
29966
|
+
timestamp: new Date().toISOString(),
|
|
29967
|
+
sources: event.sources,
|
|
29968
|
+
isStreaming: false,
|
|
29969
|
+
};
|
|
29970
|
+
streamState.newMessageIds.add(finalMessage.id);
|
|
29971
|
+
onMessageUpdate(finalMessage);
|
|
29972
|
+
upsertMessage(setState, finalMessage, false);
|
|
29973
|
+
}
|
|
29974
|
+
streamState.sources = event.sources;
|
|
29975
|
+
streamState.toolCallToActionId = event.tool_call_to_action_id;
|
|
29976
|
+
finalizeStreamMessages(setState, streamState.newMessageIds, event.sources, event.tool_call_to_action_id, event.suggestions);
|
|
29621
29977
|
}
|
|
29978
|
+
|
|
29979
|
+
/**
|
|
29980
|
+
* Handle the action loop for halting actions.
|
|
29981
|
+
*
|
|
29982
|
+
* Flow:
|
|
29983
|
+
* 1. Display the action in UI
|
|
29984
|
+
* 2. Call the frontend handler (which waits for user interaction or returns immediately for display actions)
|
|
29985
|
+
* 3. Handler returns body when complete
|
|
29986
|
+
* 4. Send body to backend via continueAgentAction
|
|
29987
|
+
* 5. Process any subsequent events (may include new action_request for chained actions)
|
|
29988
|
+
*/
|
|
29622
29989
|
async function handleActionLoop(client, initialEvent, streamState, onMessageUpdate, setState, widgetId, conversationId, getMessages) {
|
|
29623
29990
|
let pendingEvent = initialEvent;
|
|
29624
29991
|
while (pendingEvent) {
|
|
@@ -29644,7 +30011,6 @@ async function handleActionLoop(client, initialEvent, streamState, onMessageUpda
|
|
|
29644
30011
|
toolCallId: pendingEvent.tool_call_id,
|
|
29645
30012
|
actionId: pendingEvent.action_id,
|
|
29646
30013
|
input: pendingEvent.input,
|
|
29647
|
-
state: pendingEvent.state,
|
|
29648
30014
|
done: pendingEvent.done ?? false,
|
|
29649
30015
|
},
|
|
29650
30016
|
};
|
|
@@ -29669,9 +30035,13 @@ async function handleActionLoop(client, initialEvent, streamState, onMessageUpda
|
|
|
29669
30035
|
upsertMessage(setState, errorMessage, false);
|
|
29670
30036
|
return;
|
|
29671
30037
|
}
|
|
29672
|
-
|
|
30038
|
+
// Handler returns body when complete (for display actions this is immediate,
|
|
30039
|
+
// for interactive actions this waits for user completion)
|
|
30040
|
+
// Note: For halting actions, input contains the transformed data from getInitialClientState
|
|
30041
|
+
let body;
|
|
29673
30042
|
try {
|
|
29674
|
-
|
|
30043
|
+
body = await handler(pendingEvent.input, pendingEvent.input, // Input now contains the display data for halting actions
|
|
30044
|
+
{
|
|
29675
30045
|
widgetId,
|
|
29676
30046
|
conversationId,
|
|
29677
30047
|
toolCallId: pendingEvent.tool_call_id,
|
|
@@ -29693,25 +30063,16 @@ async function handleActionLoop(client, initialEvent, streamState, onMessageUpda
|
|
|
29693
30063
|
return;
|
|
29694
30064
|
}
|
|
29695
30065
|
pendingEvent = null;
|
|
29696
|
-
|
|
29697
|
-
|
|
29698
|
-
const updatedToolMessage = {
|
|
29699
|
-
...toolMessage,
|
|
29700
|
-
action: toolMessage.action ? { ...toolMessage.action, state: nextState } : toolMessage.action,
|
|
29701
|
-
};
|
|
29702
|
-
upsertMessage(setState, updatedToolMessage, true);
|
|
29703
|
-
}
|
|
29704
|
-
if (dismissed) {
|
|
29705
|
-
return;
|
|
29706
|
-
}
|
|
30066
|
+
// Send body to backend and continue agent
|
|
30067
|
+
// No need to update message - input is immutable and already contains display data
|
|
29707
30068
|
let streamEnded = false;
|
|
29708
|
-
for await (const event of client.
|
|
30069
|
+
for await (const event of client.continueAgentAction(conversationId, resumeToolCallId, body)) {
|
|
29709
30070
|
if (event.type === "action_request") {
|
|
30071
|
+
// Chained action - continue the loop
|
|
29710
30072
|
pendingEvent = event;
|
|
29711
30073
|
break;
|
|
29712
30074
|
}
|
|
29713
30075
|
if (event.type === "done") {
|
|
29714
|
-
// Finalize tool message and stream messages
|
|
29715
30076
|
finalizeToolMessage(streamState, setState, resumeToolCallId, toolName);
|
|
29716
30077
|
streamState.sources = event.sources;
|
|
29717
30078
|
streamState.toolCallToActionId = event.tool_call_to_action_id;
|
|
@@ -29741,61 +30102,118 @@ async function handleActionLoop(client, initialEvent, streamState, onMessageUpda
|
|
|
29741
30102
|
}
|
|
29742
30103
|
}
|
|
29743
30104
|
}
|
|
29744
|
-
|
|
30105
|
+
/**
|
|
30106
|
+
* Setup resume callbacks for incomplete actions after page reload.
|
|
30107
|
+
*
|
|
30108
|
+
* When a page reloads with an incomplete action, this sets up callbacks
|
|
30109
|
+
* so when the user completes the action, we can continue the agent flow.
|
|
30110
|
+
*/
|
|
30111
|
+
function setupActionResumeCallbacks(messages, client, conversationId, widgetId, setState, onMessageUpdate, createStreamState, registerCallback) {
|
|
29745
30112
|
// Find all incomplete actions and register resume callbacks
|
|
29746
30113
|
for (const message of messages) {
|
|
29747
30114
|
if (message.action && !message.action.done && !message.action.hidden) {
|
|
29748
|
-
const
|
|
29749
|
-
const
|
|
29750
|
-
registerCallback(
|
|
29751
|
-
// When user
|
|
30115
|
+
const initialToolCallId = message.action.toolCallId;
|
|
30116
|
+
const initialToolName = message.message.name || message.toolExecuting || "tool";
|
|
30117
|
+
registerCallback(initialToolCallId, async (body) => {
|
|
30118
|
+
// When user completes the action after reload, continue the stream
|
|
29752
30119
|
try {
|
|
29753
|
-
// Update the action message with the new state and check completion
|
|
29754
|
-
if (isDismissedState(newState)) {
|
|
29755
|
-
setState(prev => ({ ...prev, isTyping: false }));
|
|
29756
|
-
return;
|
|
29757
|
-
}
|
|
29758
30120
|
setState(prev => ({
|
|
29759
30121
|
...prev,
|
|
29760
|
-
messages: prev.messages.map(m => {
|
|
29761
|
-
if (m.action?.toolCallId !== toolCallId) {
|
|
29762
|
-
return m;
|
|
29763
|
-
}
|
|
29764
|
-
if (!m.action) {
|
|
29765
|
-
return m;
|
|
29766
|
-
}
|
|
29767
|
-
return { ...m, action: { ...m.action, state: newState } };
|
|
29768
|
-
}),
|
|
29769
30122
|
isTyping: true,
|
|
29770
30123
|
}));
|
|
29771
30124
|
const streamState = createStreamState();
|
|
29772
|
-
|
|
29773
|
-
|
|
29774
|
-
|
|
29775
|
-
|
|
29776
|
-
|
|
29777
|
-
|
|
29778
|
-
|
|
29779
|
-
|
|
30125
|
+
let currentToolCallId = initialToolCallId;
|
|
30126
|
+
let currentToolName = initialToolName;
|
|
30127
|
+
let currentBody = body;
|
|
30128
|
+
// Loop to handle chained action_requests
|
|
30129
|
+
while (true) {
|
|
30130
|
+
let nextActionRequest = null;
|
|
30131
|
+
// Continue the agent with the body
|
|
30132
|
+
for await (const event of client.continueAgentAction(conversationId, currentToolCallId, currentBody)) {
|
|
30133
|
+
if (event.type === "action_request") {
|
|
30134
|
+
// Chained action - need to handle the new action request
|
|
30135
|
+
nextActionRequest = event;
|
|
30136
|
+
break;
|
|
30137
|
+
}
|
|
30138
|
+
if (event.type === "done") {
|
|
30139
|
+
finalizeToolMessage(streamState, setState, currentToolCallId, currentToolName);
|
|
30140
|
+
streamState.sources = event.sources;
|
|
30141
|
+
streamState.toolCallToActionId = event.tool_call_to_action_id;
|
|
30142
|
+
finalizeStreamMessages(setState, streamState.newMessageIds, event.sources, event.tool_call_to_action_id, event.suggestions);
|
|
30143
|
+
setState(prev => ({ ...prev, isTyping: false }));
|
|
30144
|
+
return;
|
|
30145
|
+
}
|
|
30146
|
+
if (event.type === "error") {
|
|
30147
|
+
const errorMessage = {
|
|
30148
|
+
id: generateMessageId(),
|
|
30149
|
+
message: {
|
|
30150
|
+
role: "assistant",
|
|
30151
|
+
content: "Sorry, an error occurred. Please try again later.",
|
|
30152
|
+
},
|
|
30153
|
+
timestamp: new Date().toISOString(),
|
|
30154
|
+
sources: [],
|
|
30155
|
+
isError: true,
|
|
30156
|
+
};
|
|
30157
|
+
upsertMessage(setState, errorMessage, false);
|
|
30158
|
+
setState(prev => ({ ...prev, isTyping: false }));
|
|
30159
|
+
return;
|
|
30160
|
+
}
|
|
30161
|
+
handleStreamEvent(event, streamState, onMessageUpdate, setState);
|
|
30162
|
+
}
|
|
30163
|
+
// If no action_request, stream ended normally
|
|
30164
|
+
if (!nextActionRequest) {
|
|
30165
|
+
setState(prev => ({ ...prev, isTyping: false }));
|
|
30166
|
+
return;
|
|
30167
|
+
}
|
|
30168
|
+
// Update UI with new action for the chained action
|
|
30169
|
+
const toolMessage = {
|
|
30170
|
+
id: nextActionRequest.tool_call_id,
|
|
30171
|
+
sources: [],
|
|
30172
|
+
message: {
|
|
30173
|
+
role: "tool",
|
|
30174
|
+
content: "",
|
|
30175
|
+
tool_call_id: nextActionRequest.tool_call_id,
|
|
30176
|
+
name: currentToolName,
|
|
30177
|
+
},
|
|
30178
|
+
timestamp: new Date().toISOString(),
|
|
30179
|
+
isStreaming: true,
|
|
30180
|
+
toolExecuting: currentToolName,
|
|
30181
|
+
action: {
|
|
30182
|
+
implementation: nextActionRequest.implementation,
|
|
30183
|
+
toolCallId: nextActionRequest.tool_call_id,
|
|
30184
|
+
actionId: nextActionRequest.action_id,
|
|
30185
|
+
input: nextActionRequest.input,
|
|
30186
|
+
done: nextActionRequest.done ?? false,
|
|
30187
|
+
},
|
|
30188
|
+
};
|
|
30189
|
+
upsertMessage(setState, toolMessage, true);
|
|
30190
|
+
// Wait for user to complete the chained action
|
|
30191
|
+
const handler = getFrontendActionHandler(nextActionRequest.implementation);
|
|
30192
|
+
if (!handler) {
|
|
30193
|
+
console.error("[Action Resume] No handler for implementation:", nextActionRequest.implementation);
|
|
30194
|
+
setState(prev => ({ ...prev, isTyping: false }));
|
|
30195
|
+
return;
|
|
30196
|
+
}
|
|
30197
|
+
let handlerResult;
|
|
30198
|
+
try {
|
|
30199
|
+
handlerResult = await handler(nextActionRequest.input, nextActionRequest.input, // Input contains display data for halting actions
|
|
30200
|
+
{
|
|
30201
|
+
widgetId,
|
|
30202
|
+
conversationId,
|
|
30203
|
+
toolCallId: nextActionRequest.tool_call_id,
|
|
30204
|
+
actionId: nextActionRequest.action_id,
|
|
30205
|
+
implementation: nextActionRequest.implementation,
|
|
30206
|
+
});
|
|
29780
30207
|
}
|
|
29781
|
-
|
|
29782
|
-
|
|
29783
|
-
id: generateMessageId(),
|
|
29784
|
-
message: {
|
|
29785
|
-
role: "assistant",
|
|
29786
|
-
content: "Sorry, an error occurred. Please try again later.",
|
|
29787
|
-
},
|
|
29788
|
-
timestamp: new Date().toISOString(),
|
|
29789
|
-
sources: [],
|
|
29790
|
-
isError: true,
|
|
29791
|
-
};
|
|
29792
|
-
upsertMessage(setState, errorMessage, false);
|
|
30208
|
+
catch (handlerError) {
|
|
30209
|
+
console.error("[Action Resume] Handler failed:", handlerError);
|
|
29793
30210
|
setState(prev => ({ ...prev, isTyping: false }));
|
|
29794
30211
|
return;
|
|
29795
30212
|
}
|
|
29796
|
-
|
|
30213
|
+
// Continue loop with new body
|
|
30214
|
+
currentToolCallId = nextActionRequest.tool_call_id;
|
|
30215
|
+
currentBody = handlerResult;
|
|
29797
30216
|
}
|
|
29798
|
-
setState(prev => ({ ...prev, isTyping: false }));
|
|
29799
30217
|
}
|
|
29800
30218
|
catch (error) {
|
|
29801
30219
|
console.error("[Action Resume] Failed to continue stream:", error);
|
|
@@ -29874,7 +30292,7 @@ function useChat(options) {
|
|
|
29874
30292
|
}));
|
|
29875
30293
|
// Setup resume callbacks for incomplete actions
|
|
29876
30294
|
if (conversationId) {
|
|
29877
|
-
setupActionResumeCallbacks(hydratedMessages, apiClient.current, conversationId, setState, onMessage ?? (() => { }), createStreamState, registerActionResumeCallback);
|
|
30295
|
+
setupActionResumeCallbacks(hydratedMessages, apiClient.current, conversationId, widgetId, setState, onMessage ?? (() => { }), createStreamState, registerActionResumeCallback);
|
|
29878
30296
|
}
|
|
29879
30297
|
}
|
|
29880
30298
|
catch (error) {
|
|
@@ -29908,6 +30326,21 @@ function useChat(options) {
|
|
|
29908
30326
|
saveConversation(widgetId, state.conversationId, state.messages);
|
|
29909
30327
|
}
|
|
29910
30328
|
}, [widgetId, state.messages, state.conversationId, state.config?.settings.persistConversation]);
|
|
30329
|
+
// Save conversation on page unload to preserve streaming state
|
|
30330
|
+
useEffect(() => {
|
|
30331
|
+
const persistConversation = state.config?.settings.persistConversation ?? true;
|
|
30332
|
+
if (!persistConversation || !isStorageAvailable())
|
|
30333
|
+
return;
|
|
30334
|
+
const handleBeforeUnload = () => {
|
|
30335
|
+
const currentState = stateRef.current;
|
|
30336
|
+
if (currentState.messages.length > 0 && currentState.conversationId) {
|
|
30337
|
+
// Force save current state before page closes
|
|
30338
|
+
saveConversation(widgetId, currentState.conversationId, currentState.messages);
|
|
30339
|
+
}
|
|
30340
|
+
};
|
|
30341
|
+
window.addEventListener('beforeunload', handleBeforeUnload);
|
|
30342
|
+
return () => window.removeEventListener('beforeunload', handleBeforeUnload);
|
|
30343
|
+
}, [widgetId, state.config?.settings.persistConversation]);
|
|
29911
30344
|
const sendMessage = useCallback(async (content, files) => {
|
|
29912
30345
|
const trimmedContent = content.trim();
|
|
29913
30346
|
const hasFiles = !!files && files.length > 0;
|
|
@@ -29991,12 +30424,14 @@ function useChat(options) {
|
|
|
29991
30424
|
stateRef.current.conversationId !== streamConversationId ||
|
|
29992
30425
|
currentRequestIdRef.current !== streamRequestId) {
|
|
29993
30426
|
console.log('[Widget] Stream aborted, conversation changed, or superseded by new request');
|
|
30427
|
+
cancelBuffer(streamState.buffer);
|
|
29994
30428
|
break;
|
|
29995
30429
|
}
|
|
29996
30430
|
if (event.type === "action_request") {
|
|
29997
30431
|
if (currentAbortController?.signal.aborted ||
|
|
29998
30432
|
stateRef.current.conversationId !== streamConversationId ||
|
|
29999
30433
|
currentRequestIdRef.current !== streamRequestId) {
|
|
30434
|
+
cancelBuffer(streamState.buffer);
|
|
30000
30435
|
break;
|
|
30001
30436
|
}
|
|
30002
30437
|
await handleActionLoop(apiClient.current, event, streamState, (message) => {
|
|
@@ -30022,26 +30457,8 @@ function useChat(options) {
|
|
|
30022
30457
|
if (lastStreamedMessage) {
|
|
30023
30458
|
onMessage?.(lastStreamedMessage);
|
|
30024
30459
|
}
|
|
30025
|
-
|
|
30026
|
-
|
|
30027
|
-
if (enableFollowUps) {
|
|
30028
|
-
apiClient.current.generateFollowUps(stateRef.current.messages, actionIds)
|
|
30029
|
-
.then(suggestions => {
|
|
30030
|
-
if (suggestions.length > 0) {
|
|
30031
|
-
setState(prev => {
|
|
30032
|
-
const messages = [...prev.messages];
|
|
30033
|
-
for (let i = messages.length - 1; i >= 0; i--) {
|
|
30034
|
-
if (messages[i].message.role === 'assistant' && !messages[i].isError) {
|
|
30035
|
-
messages[i] = { ...messages[i], suggestions };
|
|
30036
|
-
break;
|
|
30037
|
-
}
|
|
30038
|
-
}
|
|
30039
|
-
return { ...prev, messages };
|
|
30040
|
-
});
|
|
30041
|
-
}
|
|
30042
|
-
})
|
|
30043
|
-
.catch(err => console.warn('[Widget] Follow-up generation failed:', err));
|
|
30044
|
-
}
|
|
30460
|
+
// Follow-up suggestions are now generated server-side in parallel and included in the done event
|
|
30461
|
+
// They are automatically attached to the last assistant message by finalizeStreamMessages()
|
|
30045
30462
|
}
|
|
30046
30463
|
catch (error) {
|
|
30047
30464
|
console.error("[Widget] sendMessage error:", error);
|
|
@@ -30198,7 +30615,7 @@ function useChat(options) {
|
|
|
30198
30615
|
messages: hydratedMessages,
|
|
30199
30616
|
isLoading: false,
|
|
30200
30617
|
}));
|
|
30201
|
-
setupActionResumeCallbacks(hydratedMessages, apiClient.current, conversation.id, setState, onMessage ?? (() => { }), createStreamState, registerActionResumeCallback);
|
|
30618
|
+
setupActionResumeCallbacks(hydratedMessages, apiClient.current, conversation.id, widgetId, setState, onMessage ?? (() => { }), createStreamState, registerActionResumeCallback);
|
|
30202
30619
|
if (persistConversation && isStorageAvailable()) {
|
|
30203
30620
|
saveConversation(widgetId, conversation.id, hydratedMessages);
|
|
30204
30621
|
}
|
|
@@ -30285,6 +30702,16 @@ function useChat(options) {
|
|
|
30285
30702
|
return null;
|
|
30286
30703
|
}
|
|
30287
30704
|
}, [widgetId, state.config?.settings.persistConversation]);
|
|
30705
|
+
// Call an action endpoint directly (frontend-owned flow)
|
|
30706
|
+
// Auto-injects conversationId and toolCallId for done flag enforcement
|
|
30707
|
+
const callActionEndpoint = useCallback(async (actionId, endpoint, input, options) => {
|
|
30708
|
+
const enrichedInput = {
|
|
30709
|
+
...input,
|
|
30710
|
+
conversationId: state.conversationId,
|
|
30711
|
+
toolCallId: options?.toolCallId,
|
|
30712
|
+
};
|
|
30713
|
+
return apiClient.current.callActionEndpoint(actionId, endpoint, enrichedInput, options?.token);
|
|
30714
|
+
}, [state.conversationId]);
|
|
30288
30715
|
return {
|
|
30289
30716
|
messages: state.messages,
|
|
30290
30717
|
isLoading: state.isLoading,
|
|
@@ -30302,6 +30729,7 @@ function useChat(options) {
|
|
|
30302
30729
|
startNewConversation,
|
|
30303
30730
|
deleteConversation: deleteConversation$1,
|
|
30304
30731
|
createDemoConversation,
|
|
30732
|
+
callActionEndpoint,
|
|
30305
30733
|
};
|
|
30306
30734
|
}
|
|
30307
30735
|
|
|
@@ -30317,17 +30745,19 @@ const PlusIcon = () => (jsxs("svg", { width: "22", height: "22", viewBox: "0 0 2
|
|
|
30317
30745
|
const TrashIcon = () => (jsxs("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsx("path", { d: "M3 6h18" }), jsx("path", { d: "M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" }), jsx("path", { d: "M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2" })] }));
|
|
30318
30746
|
const CloseIcon = () => (jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }), jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })] }));
|
|
30319
30747
|
const BackIcon = () => (jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsx("path", { d: "M19 12H5" }), jsx("path", { d: "M12 19l-7-7 7-7" })] }));
|
|
30320
|
-
const ChatWindow = ({ messages, isLoading, isTyping, config, onSendMessage, onClose: _onClose, onFeedback, onActionClick, onActionDismiss,
|
|
30748
|
+
const ChatWindow = ({ messages, isLoading, isTyping, config, onSendMessage, onClose: _onClose, onFeedback, onActionClick, onActionDismiss, onCallEndpoint,
|
|
30321
30749
|
// Chat history props (only active when persistConversation is true)
|
|
30322
30750
|
conversations = [], onLoadConversations, onSwitchConversation, onStartNewConversation, onDeleteConversation, currentConversationId,
|
|
30323
30751
|
// Override props for live preview
|
|
30324
|
-
headerTitleOverride, welcomeTitleOverride, welcomeMessageOverride, placeholderOverride, suggestedQuestionsOverride,
|
|
30752
|
+
sizeOverride, headerTitleOverride, welcomeTitleOverride, welcomeMessageOverride, placeholderOverride, suggestedQuestionsOverride,
|
|
30753
|
+
// Demo mode
|
|
30754
|
+
onInputFocus, }) => {
|
|
30325
30755
|
const appearance = config?.appearance;
|
|
30326
30756
|
const settings = config?.settings;
|
|
30327
30757
|
// Check if chat history should be shown (requires both persistConversation AND showChatHistory)
|
|
30328
30758
|
const canShowHistory = (settings?.persistConversation ?? true) && (settings?.showChatHistory ?? true);
|
|
30329
30759
|
// Apply overrides for live preview (overrides take priority over saved config)
|
|
30330
|
-
const size = appearance?.size
|
|
30760
|
+
const size = sizeOverride ?? appearance?.size ?? 'medium';
|
|
30331
30761
|
const headerTitle = headerTitleOverride ?? appearance?.headerTitle ?? 'AI Assistant';
|
|
30332
30762
|
const welcomeTitle = welcomeTitleOverride ?? appearance?.welcomeTitle ?? '';
|
|
30333
30763
|
const welcomeMessage = welcomeMessageOverride ?? appearance?.welcomeMessage ?? '';
|
|
@@ -30404,7 +30834,7 @@ headerTitleOverride, welcomeTitleOverride, welcomeMessageOverride, placeholderOv
|
|
|
30404
30834
|
jsxs("div", { className: "ai-chat-history-panel", children: [conversations.length === 0 ? (jsx("div", { className: "ai-chat-history-empty", children: "No previous conversations" })) : (jsx("div", { className: `ai-chat-history-list ${isHistoryExiting ? 'exiting' : ''}`, children: conversations.map((conv) => (jsx("div", { className: `ai-chat-history-item ${conv.id === currentConversationId ? 'active' : ''}`, onClick: () => handleSelectConversation(conv.id), children: jsxs("div", { className: "ai-chat-history-item-content", children: [jsx("div", { className: "ai-chat-history-item-preview", children: conv.preview }), onDeleteConversation && (jsx("button", { className: "ai-chat-history-item-delete", onClick: (e) => {
|
|
30405
30835
|
e.stopPropagation();
|
|
30406
30836
|
onDeleteConversation(conv.id);
|
|
30407
|
-
}, "aria-label": "Delete conversation", children: jsx(TrashIcon, {}) }))] }) }, conv.id))) })), jsx(MessageInput, { onSend: (text) => handleSendFromOverview(text), placeholder: inputPlaceholder, disabled: isLoading, enableFileUpload: settings?.enableFileUpload, showDataPolicy: settings?.showDataPolicy ?? true, onDataPolicyClick: handleDataPolicyClick })] })) : (jsxs(Fragment, { children: [maxMessages && userMessageCount >= maxMessages - 2 && !isLimitReached && (jsxs("div", { className: "ai-chat-warning", role: "alert", children: [maxMessages - userMessageCount, " message", maxMessages - userMessageCount !== 1 ? 's' : '', " remaining"] })), isLimitReached && (jsx("div", { className: "ai-chat-error", role: "alert", children: "Message limit reached. Please start a new conversation." })), (() => {
|
|
30837
|
+
}, "aria-label": "Delete conversation", children: jsx(TrashIcon, {}) }))] }) }, conv.id))) })), jsx(MessageInput, { onSend: (text) => handleSendFromOverview(text), placeholder: inputPlaceholder, disabled: isLoading, enableFileUpload: settings?.enableFileUpload, showDataPolicy: settings?.showDataPolicy ?? true, onDataPolicyClick: handleDataPolicyClick, onInputFocus: onInputFocus })] })) : (jsxs(Fragment, { children: [maxMessages && userMessageCount >= maxMessages - 2 && !isLimitReached && (jsxs("div", { className: "ai-chat-warning", role: "alert", children: [maxMessages - userMessageCount, " message", maxMessages - userMessageCount !== 1 ? 's' : '', " remaining"] })), isLimitReached && (jsx("div", { className: "ai-chat-error", role: "alert", children: "Message limit reached. Please start a new conversation." })), (() => {
|
|
30408
30838
|
console.log('[DEBUG ChatWindow] Rendering MessageList with', messages.length, 'messages');
|
|
30409
30839
|
messages.forEach((m, i) => {
|
|
30410
30840
|
console.log(`[DEBUG ChatWindow] msg ${i}:`, { role: m.message.role, hasAction: !!m.action, impl: m.action?.implementation });
|
|
@@ -30414,7 +30844,15 @@ headerTitleOverride, welcomeTitleOverride, welcomeMessageOverride, placeholderOv
|
|
|
30414
30844
|
console.log('[DEBUG ChatWindow] Testing renderer for query-contact-directory:', !!getActionRenderer('query-contact-directory'));
|
|
30415
30845
|
}
|
|
30416
30846
|
return null;
|
|
30417
|
-
})(), jsx(MessageList, { messages: messages, isTyping: isTyping, showTypingIndicator: settings?.showTypingIndicator, showTimestamps: settings?.showTimestamps, showToolCalls: settings?.showToolCalls, enableFeedback: settings?.enableFeedback, welcomeTitle: welcomeTitle || 'Welcome Message', welcomeMessage: welcomeMessage, suggestedQuestions: suggestedQuestionsOverride ?? settings?.suggestedQuestions, accentColor: appearance?.primaryColor, onSuggestedQuestionClick: handleQuestionClick, onActionClick: onActionClick, onActionDismiss: onActionDismiss, onFeedback: onFeedback, onScrollStateChange: handleScrollStateChange, getActionRenderer: getActionRenderer }), settings?.showDataPolicy && (jsxs("div", { className: "ai-chat-page-disclaimer", children: [jsx("span", { children: "AI-generated responses may be inaccurate." }), jsx("button", { type: "button", className: "ai-chat-page-disclaimer-link", onClick: handleDataPolicyClick, children: "Privacy Notice" })] })), jsx(ScrollButton, { onClick: () => scrollToBottom?.(), visible: showScrollButton }), jsx(MessageInput, { onSend: onSendMessage, placeholder: isLimitReached ? 'Message limit reached' : (isTyping ? 'Waiting for response...' : inputPlaceholder), disabled: isLoading || isTyping || isLimitReached, enableFileUpload: settings?.enableFileUpload, showDataPolicy: settings?.showDataPolicy ?? true, onDataPolicyClick: handleDataPolicyClick })] }))] }));
|
|
30847
|
+
})(), jsx(MessageList, { messages: messages, isTyping: isTyping, showTypingIndicator: settings?.showTypingIndicator, showTimestamps: settings?.showTimestamps, showToolCalls: settings?.showToolCalls, enableFeedback: settings?.enableFeedback, welcomeTitle: welcomeTitle || 'Welcome Message', welcomeMessage: welcomeMessage, suggestedQuestions: suggestedQuestionsOverride ?? settings?.suggestedQuestions, accentColor: appearance?.primaryColor, onSuggestedQuestionClick: handleQuestionClick, onActionClick: onActionClick, onActionDismiss: onActionDismiss, onFeedback: onFeedback, onScrollStateChange: handleScrollStateChange, getActionRenderer: getActionRenderer, onCallEndpoint: onCallEndpoint }), settings?.showDataPolicy && (jsxs("div", { className: "ai-chat-page-disclaimer", children: [jsx("span", { children: "AI-generated responses may be inaccurate." }), jsx("button", { type: "button", className: "ai-chat-page-disclaimer-link", onClick: handleDataPolicyClick, children: "Privacy Notice" })] })), jsx(ScrollButton, { onClick: () => scrollToBottom?.(), visible: showScrollButton }), jsx(MessageInput, { onSend: onSendMessage, placeholder: isLimitReached ? 'Message limit reached' : (isTyping ? 'Waiting for response...' : inputPlaceholder), disabled: isLoading || isTyping || isLimitReached, enableFileUpload: settings?.enableFileUpload, showDataPolicy: settings?.showDataPolicy ?? true, onDataPolicyClick: handleDataPolicyClick, onInputFocus: onInputFocus })] }))] }));
|
|
30848
|
+
};
|
|
30849
|
+
|
|
30850
|
+
const MessageCircleIcon = () => (jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: jsx("path", { d: "M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z" }) }));
|
|
30851
|
+
const ChevronDownIcon = () => (jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: jsx("polyline", { points: "6 9 12 15 18 9" }) }));
|
|
30852
|
+
// Icon components mapping for dynamic lookup
|
|
30853
|
+
const iconComponents = {
|
|
30854
|
+
FiMessageCircle: MessageCircleIcon,
|
|
30855
|
+
FiChevronDown: ChevronDownIcon,
|
|
30418
30856
|
};
|
|
30419
30857
|
|
|
30420
30858
|
/**
|
|
@@ -30712,108 +31150,60 @@ function generateThemeStyles(appearance, theme) {
|
|
|
30712
31150
|
return styles;
|
|
30713
31151
|
}
|
|
30714
31152
|
|
|
30715
|
-
|
|
30716
|
-
|
|
30717
|
-
|
|
30718
|
-
|
|
30719
|
-
|
|
30720
|
-
|
|
30721
|
-
|
|
30722
|
-
* Uses multiple sampling points for accuracy
|
|
30723
|
-
*/
|
|
30724
|
-
function sampleBackgroundColor(element) {
|
|
30725
|
-
const rect = element.getBoundingClientRect();
|
|
30726
|
-
const centerX = rect.left + rect.width / 2;
|
|
30727
|
-
const centerY = rect.top + rect.height / 2;
|
|
30728
|
-
// Try to get the element behind our widget
|
|
30729
|
-
// Temporarily hide the element to sample what's behind
|
|
30730
|
-
const originalVisibility = element.style.visibility;
|
|
30731
|
-
element.style.visibility = 'hidden';
|
|
30732
|
-
// Sample the center point
|
|
30733
|
-
const elementBehind = document.elementFromPoint(centerX, centerY);
|
|
30734
|
-
// Restore visibility
|
|
30735
|
-
element.style.visibility = originalVisibility;
|
|
30736
|
-
if (!elementBehind) {
|
|
30737
|
-
return '#ffffff'; // Default to white
|
|
30738
|
-
}
|
|
30739
|
-
// Get computed background color
|
|
30740
|
-
const computedStyle = window.getComputedStyle(elementBehind);
|
|
30741
|
-
let bgColor = computedStyle.backgroundColor;
|
|
30742
|
-
// If transparent, walk up the DOM tree
|
|
30743
|
-
if (bgColor === 'rgba(0, 0, 0, 0)' || bgColor === 'transparent') {
|
|
30744
|
-
let parent = elementBehind.parentElement;
|
|
30745
|
-
while (parent) {
|
|
30746
|
-
const parentStyle = window.getComputedStyle(parent);
|
|
30747
|
-
bgColor = parentStyle.backgroundColor;
|
|
30748
|
-
if (bgColor !== 'rgba(0, 0, 0, 0)' && bgColor !== 'transparent') {
|
|
30749
|
-
break;
|
|
30750
|
-
}
|
|
30751
|
-
parent = parent.parentElement;
|
|
30752
|
-
}
|
|
30753
|
-
}
|
|
30754
|
-
// If still transparent, check body/html
|
|
30755
|
-
if (bgColor === 'rgba(0, 0, 0, 0)' || bgColor === 'transparent') {
|
|
30756
|
-
const bodyStyle = window.getComputedStyle(document.body);
|
|
30757
|
-
bgColor = bodyStyle.backgroundColor;
|
|
30758
|
-
if (bgColor === 'rgba(0, 0, 0, 0)' || bgColor === 'transparent') {
|
|
30759
|
-
return '#ffffff'; // Default to white
|
|
31153
|
+
function useWidgetAppearance({ containerRef, config, theme, primaryColor, position, size, headerTitle, welcomeTitle, welcomeMessage, placeholder, welcomeBubbleText, triggerType, triggerText, customStyles, zIndex, previewMode: _previewMode, }) {
|
|
31154
|
+
// Use theme prop directly - no dynamic detection
|
|
31155
|
+
const effectiveTheme = theme ?? "light";
|
|
31156
|
+
// Set data attribute for CSS styling
|
|
31157
|
+
useEffect(() => {
|
|
31158
|
+
if (containerRef.current) {
|
|
31159
|
+
containerRef.current.setAttribute('data-theme', effectiveTheme);
|
|
30760
31160
|
}
|
|
30761
|
-
}
|
|
30762
|
-
|
|
30763
|
-
|
|
30764
|
-
|
|
30765
|
-
|
|
30766
|
-
|
|
30767
|
-
|
|
30768
|
-
|
|
30769
|
-
const
|
|
30770
|
-
|
|
30771
|
-
|
|
30772
|
-
const
|
|
30773
|
-
const
|
|
30774
|
-
|
|
30775
|
-
|
|
30776
|
-
|
|
30777
|
-
|
|
30778
|
-
|
|
30779
|
-
|
|
30780
|
-
|
|
30781
|
-
|
|
30782
|
-
|
|
30783
|
-
|
|
30784
|
-
const
|
|
30785
|
-
return
|
|
30786
|
-
|
|
30787
|
-
|
|
30788
|
-
|
|
30789
|
-
|
|
30790
|
-
|
|
30791
|
-
|
|
30792
|
-
|
|
31161
|
+
}, [effectiveTheme, containerRef]);
|
|
31162
|
+
const appearanceConfig = config?.appearance;
|
|
31163
|
+
const effectivePosition = position || appearanceConfig?.position || "bottom-right";
|
|
31164
|
+
const accentColor = primaryColor !== undefined ? primaryColor : appearanceConfig?.primaryColor ?? "";
|
|
31165
|
+
const effectiveSize = size || appearanceConfig?.size || "small";
|
|
31166
|
+
const effectiveHeaderTitle = headerTitle ?? appearanceConfig?.headerTitle ?? "";
|
|
31167
|
+
const effectiveWelcomeTitle = welcomeTitle ?? appearanceConfig?.welcomeTitle ?? "";
|
|
31168
|
+
const effectiveWelcomeMessage = welcomeMessage ?? appearanceConfig?.welcomeMessage ?? "";
|
|
31169
|
+
const effectivePlaceholder = placeholder ?? appearanceConfig?.placeholder ?? "";
|
|
31170
|
+
const effectiveWelcomeBubbleText = welcomeBubbleText ?? appearanceConfig?.welcomeBubbleText ?? "";
|
|
31171
|
+
const effectiveTriggerType = triggerType ?? appearanceConfig?.triggerType ?? "button";
|
|
31172
|
+
const effectiveTriggerText = triggerText ?? appearanceConfig?.triggerText ?? "Chat";
|
|
31173
|
+
const simpleAppearance = {
|
|
31174
|
+
accentColor};
|
|
31175
|
+
const generatedStyles = generateThemeStyles(simpleAppearance, effectiveTheme);
|
|
31176
|
+
const legacyStyles = appearanceConfig ? applyAppearanceStyles(appearanceConfig) : {};
|
|
31177
|
+
const mergedStyles = {
|
|
31178
|
+
...legacyStyles,
|
|
31179
|
+
...generatedStyles,
|
|
31180
|
+
...customStyles,
|
|
31181
|
+
...(zIndex !== undefined ? { "--widget-z-index": String(zIndex) } : {}),
|
|
31182
|
+
};
|
|
31183
|
+
// Compute icon contrast color for inline button styling (prevents FOUC)
|
|
31184
|
+
const iconContrastColor = accentColor ? getContrastText(accentColor) : undefined;
|
|
31185
|
+
return {
|
|
31186
|
+
effectiveTheme,
|
|
31187
|
+
effectivePosition,
|
|
31188
|
+
accentColor,
|
|
31189
|
+
iconContrastColor,
|
|
31190
|
+
effectiveSize,
|
|
31191
|
+
effectiveHeaderTitle,
|
|
31192
|
+
effectiveWelcomeTitle,
|
|
31193
|
+
effectiveWelcomeMessage,
|
|
31194
|
+
effectivePlaceholder,
|
|
31195
|
+
effectiveWelcomeBubbleText,
|
|
31196
|
+
effectiveTriggerType,
|
|
31197
|
+
effectiveTriggerText,
|
|
31198
|
+
mergedStyles,
|
|
31199
|
+
};
|
|
30793
31200
|
}
|
|
30794
|
-
|
|
30795
|
-
|
|
30796
|
-
|
|
30797
|
-
|
|
30798
|
-
|
|
30799
|
-
|
|
30800
|
-
const theme = detectTheme(element);
|
|
30801
|
-
if (theme !== lastTheme) {
|
|
30802
|
-
lastTheme = theme;
|
|
30803
|
-
callback(theme);
|
|
30804
|
-
}
|
|
30805
|
-
});
|
|
30806
|
-
// Observe body for class/style changes (common for theme switching)
|
|
30807
|
-
observer.observe(document.body, {
|
|
30808
|
-
attributes: true,
|
|
30809
|
-
attributeFilter: ['class', 'style', 'data-theme', 'data-bs-theme'],
|
|
30810
|
-
});
|
|
30811
|
-
// Also observe html element
|
|
30812
|
-
observer.observe(document.documentElement, {
|
|
30813
|
-
attributes: true,
|
|
30814
|
-
attributeFilter: ['class', 'style', 'data-theme', 'data-bs-theme'],
|
|
30815
|
-
});
|
|
30816
|
-
return observer;
|
|
31201
|
+
|
|
31202
|
+
function WidgetTriggers({ triggerType, isOpen, onToggle, triggerText, placeholder, IconComponent, showWelcomeBubble, welcomeBubbleText, previewMode, onDismissBubble, isInputBarCollapsed, setIsInputBarCollapsed, inputBarValue, setInputBarValue, onSubmitInputBar, accentColor, iconColor, }) {
|
|
31203
|
+
// Inline button styles to prevent flash of unstyled content (FOUC)
|
|
31204
|
+
// Applied directly to button elements, bypassing CSS variable cascade timing
|
|
31205
|
+
const buttonStyle = accentColor ? { background: accentColor, color: iconColor } : undefined;
|
|
31206
|
+
return (jsxs(Fragment, { children: [triggerType === "button" && !isOpen && welcomeBubbleText && (previewMode || showWelcomeBubble) && (jsxs("div", { className: "ai-chat-welcome-bubble", onClick: onToggle, children: [jsx("span", { children: welcomeBubbleText }), jsx("button", { className: "ai-chat-welcome-bubble-close", onClick: onDismissBubble, "aria-label": "Dismiss", children: jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }), jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })] }) }), jsx("div", { className: "ai-chat-welcome-bubble-arrow" })] })), triggerType === "button" && (jsx("button", { className: `ai-chat-button ${isOpen ? "is-open" : ""}`, onClick: onToggle, "aria-label": isOpen ? "Minimize chat" : "Open chat", style: buttonStyle, children: jsx("div", { className: "ai-chat-button-svg", children: jsx(IconComponent, {}) }) })), triggerType === "pill-text" && (jsxs("button", { className: `ai-chat-trigger-pill ${isOpen ? "is-open" : ""}`, onClick: onToggle, "aria-label": isOpen ? "Close chat" : "Open chat", style: isOpen ? buttonStyle : undefined, children: [jsx("div", { className: "ai-chat-trigger-pill-icon", children: jsx(IconComponent, {}) }), !isOpen && jsx("span", { children: triggerText })] })), triggerType === "input-bar" && !isOpen && (jsx("div", { className: "ai-chat-trigger-input-container", children: isInputBarCollapsed ? (jsxs("div", { className: "ai-chat-trigger-input-row", children: [jsx("button", { type: "button", className: "ai-chat-trigger-collapse-toggle", onClick: () => setIsInputBarCollapsed(false), "aria-label": "Expand input bar", children: jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: jsx("polyline", { points: "15 18 9 12 15 6" }) }) }), jsx("button", { type: "button", className: "ai-chat-button", onClick: onToggle, "aria-label": "Open chat", style: buttonStyle, children: jsx("div", { className: "ai-chat-button-svg", children: jsx(IconComponent, {}) }) })] })) : (jsxs("div", { className: "ai-chat-trigger-input-row", children: [jsx("button", { type: "button", className: "ai-chat-trigger-collapse-toggle", onClick: () => setIsInputBarCollapsed(true), "aria-label": "Collapse input bar", children: jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: jsx("polyline", { points: "9 18 15 12 9 6" }) }) }), jsxs("form", { className: "ai-chat-trigger-input-wrapper", onSubmit: onSubmitInputBar, children: [jsx("input", { type: "text", className: "ai-chat-trigger-input", placeholder: placeholder || "Ask me anything...", value: inputBarValue, onChange: (event) => setInputBarValue(event.target.value), "aria-label": "Chat input" }), jsx("button", { type: "submit", className: "ai-chat-trigger-input-btn", disabled: !inputBarValue.trim(), "aria-label": "Send message", style: buttonStyle, children: jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsx("line", { x1: "12", y1: "19", x2: "12", y2: "5" }), jsx("polyline", { points: "5 12 12 5 19 12" })] }) })] }), jsx("button", { type: "button", className: "ai-chat-trigger-input-expand", onClick: onToggle, "aria-label": "Open chat", children: jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: jsx("polyline", { points: "18 15 12 9 6 15" }) }) })] })) }))] }));
|
|
30817
31207
|
}
|
|
30818
31208
|
|
|
30819
31209
|
function styleInject(css, ref) {
|
|
@@ -30843,34 +31233,19 @@ function styleInject(css, ref) {
|
|
|
30843
31233
|
}
|
|
30844
31234
|
}
|
|
30845
31235
|
|
|
30846
|
-
var css_248z$1 = ".ai-chat-message{animation:ai-chat-message-appear .2s var(--chat-ease-bounce);max-width:85%}.ai-chat-message-content{border-radius:var(--chat-radius-bubble,14px);font-size:var(--chat-text-md,15px);line-height:var(--chat-line-relaxed,1.6);padding:var(--chat-space-sm,8px) var(--chat-space-md,16px)}.ai-chat-message.user .ai-chat-message-content{background:var(--chat-user-bg,#f4f3f0);border-bottom-right-radius:var(--chat-radius-sm,4px);color:var(--chat-user-text,#000)}.ai-chat-message.assistant .ai-chat-message-content{background:var(--chat-assistant-bg,transparent);color:var(--chat-assistant-text,#000)}.ai-chat-message-timestamp{color:var(--chat-text-muted,#71717a);font-size:var(--chat-text-xs,12px);margin-top:var(--chat-space-xs,4px);padding:0 var(--chat-space-xs,4px)}.ai-chat-message.streaming .ai-chat-message-content:after{animation:ai-chat-cursor-blink .8s infinite;content:\"▋\";margin-left:2px;opacity:.7}@keyframes ai-chat-message-appear{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}@keyframes ai-chat-cursor-blink{0%,50%{opacity:1}51%,to{opacity:0}}.ai-chat-message.fullpage .ai-chat-message-content{font-size:var(--chat-text-lg,18px);padding:var(--chat-space-md,16px) var(--chat-space-lg,24px)}.ai-chat-typing{gap:var(--chat-space-xs,4px);padding:var(--chat-space-sm,8px) var(--chat-space-md,16px)}.ai-chat-typing-dot{background:var(--chat-text-muted,#71717a)}.ai-chat-tool-row{padding:0 16px}.ai-chat-tool-gear{color:var(--text-primary,#3e3e3e)}.ai-chat-tool-badge{border-radius:8px}.ai-chat-tool-badge.loading{background:#e5e7eb;border:1px solid #d1d5db;color:#1f2937}.ai-chat-tool-badge.error{background:rgba(239,68,68,.1);color:#ef4444}.ai-chat-tool-check{color:#22c55e;flex-shrink:0}.ai-chat-tool-error{color:#ef4444;flex-shrink:0}.ai-chat-tool-badge .tool-name{max-width:150px;overflow:hidden;text-overflow:ellipsis}.ai-chat-widget.dark .ai-chat-tool-gear,.ai-chat-widget[data-theme=dark] .ai-chat-tool-gear,.dark .ai-chat-tool-gear,[data-theme=dark] .ai-chat-tool-gear{color:#fff}.ai-chat-widget.dark .ai-chat-tool-badge,.ai-chat-widget[data-theme=dark] .ai-chat-tool-badge,.dark .ai-chat-tool-badge,[data-theme=dark] .ai-chat-tool-badge{background:hsla(0,0%,100%,.12);border:1px solid hsla(0,0%,100%,.2);color:hsla(0,0%,100%,.9)}.ai-chat-widget.dark .ai-chat-tool-badge.error,.ai-chat-widget[data-theme=dark] .ai-chat-tool-badge.error,.dark .ai-chat-tool-badge.error,[data-theme=dark] .ai-chat-tool-badge.error{background:rgba(239,68,68,.2);color:#f87171}.chakra-ui-dark .ai-chat-tool-gear,html.dark .ai-chat-tool-gear{color:#fff}.chakra-ui-dark .ai-chat-tool-badge,html.dark .ai-chat-tool-badge{background:hsla(0,0%,100%,.12);border:1px solid hsla(0,0%,100%,.2);color:hsla(0,0%,100%,.9)}.chakra-ui-dark .ai-chat-tool-badge.error,html.dark .ai-chat-tool-badge.error{background:rgba(239,68,68,.2);color:#f87171}.ai-chat-pin-input-group{align-items:center;display:flex;flex-wrap:nowrap;gap:8px;justify-content:center;margin:4px 0 8px}.ai-chat-pin-input{align-items:center;appearance:none;background:var(--bg-primary,#fff);border:1px solid var(--border-default,#e5e7eb);border-radius:var(--radius-md,8px);box-sizing:border-box;color:var(--text-primary,#3e3e3e);display:inline-flex;flex:0 0 42px;font-family:inherit;font-size:18px;font-weight:600;height:46px;justify-content:center;line-height:1;max-width:42px;min-width:42px;outline:none;padding:0;text-align:center;transition:border-color .2s ease,box-shadow .2s ease;width:42px}.ai-chat-pin-input:focus{border-color:var(--action-accent,var(--primary-color,#3b82f6));box-shadow:0 0 0 3px rgba(59,130,246,.1)}.ai-chat-pin-input:disabled{cursor:not-allowed;opacity:.6}.ai-chat-widget.dark .ai-chat-pin-input,.chakra-ui-dark .ai-chat-pin-input,.dark .ai-chat-pin-input,[data-theme=dark] .ai-chat-pin-input{background:rgba(0,0,0,.2);border-color:hsla(0,0%,100%,.1);color:#fff}.ai-chat-widget.dark .ai-chat-pin-input:focus,.chakra-ui-dark .ai-chat-pin-input:focus,.dark .ai-chat-pin-input:focus,[data-theme=dark] .ai-chat-pin-input:focus{border-color:var(--action-accent,var(--primary-color,#3b82f6));box-shadow:0 0 0 3px rgba(59,130,246,.2)}.ai-chat-action-button-secondary{background:var(--bg-primary,#fff);border:1px solid var(--border-default,#e5e7eb);border-radius:9999px;color:var(--text-secondary,#6b7280);cursor:pointer;font-size:var(--text-sm,13px);font-weight:var(--font-weight-medium,500);padding:10px 16px;transition:all .2s ease;width:100%}.ai-chat-action-button-secondary:hover:not(:disabled){background:rgba(59,130,246,.05);border-color:var(--action-accent,var(--primary-color,#3b82f6));color:var(--action-accent,var(--primary-color,#3b82f6))}.ai-chat-action-button-secondary:disabled{cursor:not-allowed;opacity:.5}.ai-chat-widget.dark .ai-chat-action-button-secondary,.chakra-ui-dark .ai-chat-action-button-secondary,.dark .ai-chat-action-button-secondary,[data-theme=dark] .ai-chat-action-button-secondary{background:rgba(0,0,0,.2);border-color:hsla(0,0%,100%,.1);color:#e5e7eb}.ai-chat-widget.dark .ai-chat-action-button-secondary:hover:not(:disabled),.chakra-ui-dark .ai-chat-action-button-secondary:hover:not(:disabled),.dark .ai-chat-action-button-secondary:hover:not(:disabled),[data-theme=dark] .ai-chat-action-button-secondary:hover:not(:disabled){background:rgba(59,130,246,.2);border-color:var(--action-accent,var(--primary-color,#3b82f6));color:#fff}.ai-chat-action-appointment-list,.ai-chat-action-button-group{display:flex;flex-direction:column;gap:8px}.ai-chat-action-appointment-item{align-items:center;background:var(--bg-primary,#fff);border:1px solid var(--border-subtle,rgba(0,0,0,.08));border-radius:var(--radius-md,8px);display:flex;gap:12px;justify-content:space-between;padding:10px 12px}.ai-chat-widget.dark .ai-chat-action-appointment-item,.chakra-ui-dark .ai-chat-action-appointment-item,.dark .ai-chat-action-appointment-item,[data-theme=dark] .ai-chat-action-appointment-item{background:rgba(0,0,0,.2);border-color:hsla(0,0%,100%,.08)}.ai-chat-action-appointment-info{display:flex;flex-direction:column;gap:2px;min-width:0}.ai-chat-action-appointment-subject{color:var(--text-primary,#3e3e3e);font-size:14px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ai-chat-widget.dark .ai-chat-action-appointment-subject,.chakra-ui-dark .ai-chat-action-appointment-subject,.dark .ai-chat-action-appointment-subject,[data-theme=dark] .ai-chat-action-appointment-subject{color:#fff}.ai-chat-action-appointment-time{color:var(--text-muted,#71717a);font-size:12px}.ai-chat-action-appointment-item .ai-chat-action-button-secondary{font-size:12px;padding:6px 12px;width:auto}.ai-chat-action-error-message{background:rgba(239,68,68,.1);border-radius:var(--radius-md,8px);color:#dc2626;font-size:var(--text-sm,13px);padding:12px}.ai-chat-widget.dark .ai-chat-action-error-message,.chakra-ui-dark .ai-chat-action-error-message,.dark .ai-chat-action-error-message,[data-theme=dark] .ai-chat-action-error-message{background:rgba(239,68,68,.2);color:#fca5a5}.ai-chat-widget,.chat-ui{--radius-sm:4px;--radius-md:8px;--radius-lg:12px;--radius-xl:16px;--radius-2xl:18px;--radius-pill:9999px;--radius-window-top:22px;--radius-window-bottom:44px;--radius-window-gutter:16px;--radius-chat-bubble:14px;--radius-preset-badge:13px;--radius-history-item:14px;--radius-action-badge:8px;--radius-input:62px;--space-xs:4px;--space-sm:8px;--space-md:16px;--space-lg:24px;--space-xl:32px;--text-xs:12px;--text-sm:14px;--text-md:15px;--text-lg:18px;--text-xl:22px;--text-2xl:28px;--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--line-height-tight:1.3;--line-height-normal:1.4;--line-height-relaxed:1.6;--bg-primary:#fff;--bg-secondary:#f4f4f4;--bg-tertiary:#e5e7eb;--bg-hover:#e5e7eb;--text-primary:#3e3e3e;--text-secondary:#000;--text-muted:#71717a;--text-placeholder:#a1a1aa;--border-default:#d3d3d3;--border-subtle:#e5e7eb;--border-muted:#f4f4f5;--user-bg:#f4f3f0;--user-text:#000;--user-bg-hover:#e8e7e4;--agent-bg:transparent;--agent-text:#000;--input-bg:#f4f4f4;--input-border:#d3d3d3;--input-text:#000;--btn-primary-bg:#151515;--btn-primary-text:#f4f4f4;--btn-secondary-bg:transparent;--btn-secondary-text:#71717a;--spring-bounce:cubic-bezier(0.34,1.56,0.64,1);--spring-smooth:cubic-bezier(0.4,0,0.2,1);--spring-snappy:cubic-bezier(0.2,0,0,1);--duration-fast:0.15s;--duration-normal:0.25s;--duration-slow:0.35s;--shadow-sm:0 1px 2px rgba(0,0,0,.05);--shadow-md:0 2px 8px rgba(0,0,0,.1);--shadow-lg:0 4px 16px rgba(0,0,0,.12);--shadow-window:0px 0px 15px 9px rgba(0,0,0,.1);--shadow-button:0px 0px 15px 9px rgba(0,0,0,.03)}.ai-chat-widget.dark,.chat-ui.dark{--bg-primary:#282625;--bg-secondary:#4a4846;--bg-tertiary:#484848;--bg-hover:#484848;--text-primary:#fff;--text-secondary:#fff;--text-muted:#a1a1aa;--text-placeholder:#71717a;--border-default:#5d5b5b;--border-subtle:#5d5b5b;--border-muted:#5d5b5b;--user-bg:#484848;--user-text:#fff;--user-bg-hover:#5a5a5a;--agent-bg:transparent;--agent-text:#fff;--input-bg:#4a4846;--input-border:#5d5b5b;--input-text:#fff;--btn-primary-bg:#fff;--btn-primary-text:#312f2d;--btn-secondary-bg:transparent;--btn-secondary-text:#a1a1aa;--shadow-window:0px 0px 15px 9px rgba(0,0,0,.2);--shadow-button:0px 0px 15px 9px rgba(0,0,0,.03);--shadow-input:0px 0px 10px rgba(0,0,0,.15)}.ai-chat-widget,.chat-ui{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif}.ai-chat-widget-container{font-size:var(--text-sm);line-height:1.5;position:fixed;z-index:var(--widget-z-index,2147483647)}.ai-chat-widget-container.bottom-right{bottom:20px;right:20px}.ai-chat-widget-container.bottom-left{bottom:20px;left:20px}.ai-chat-widget-container.top-right{right:20px;top:20px}.ai-chat-widget-container.top-left{left:20px;top:20px}.ai-chat-widget-container.container-mode{position:absolute}@keyframes ai-chat-window-open{0%{opacity:0;transform:scale(.9) translateY(20px)}to{opacity:1;transform:scale(1) translateY(0)}}@keyframes ai-chat-window-close{0%{opacity:1;transform:scale(1) translateY(0)}to{opacity:0;transform:scale(.9) translateY(20px)}}@keyframes ai-chat-message-slide-in{0%{opacity:0;transform:translateY(12px)}to{opacity:1;transform:translateY(0)}}@keyframes ai-chat-welcome-fade-in{0%{opacity:0;transform:translateY(16px)}to{opacity:1;transform:translateY(0)}}@keyframes ai-chat-typing-pulse{0%,60%,to{opacity:.4;transform:translateY(0) scale(1)}30%{opacity:1;transform:translateY(-4px) scale(1.1)}}@keyframes ai-chat-gear-spin{to{transform:rotate(1turn)}}@keyframes ai-chat-tool-active{0%,to{background:var(--bg-secondary);opacity:1}50%{background:var(--bg-tertiary);opacity:1}}@keyframes ai-chat-feedback-morph{0%{opacity:.5;transform:scale(.8)}to{opacity:1;transform:scale(1)}}@keyframes ai-chat-checkmark-pop{0%{opacity:0;transform:scale(0) rotate(-45deg)}50%{transform:scale(1.3) rotate(0deg)}to{opacity:1;transform:scale(1) rotate(0deg)}}@keyframes ai-chat-history-exit{to{opacity:0;transform:translateY(-18px)}}@keyframes ai-chat-tool-gradient{0%{background-position:200% 0}to{background-position:-200% 0}}.ai-chat-window{animation:ai-chat-window-open var(--duration-slow,.35s) var(--spring-bounce);background:var(--bg-primary,#fff);border:1px solid var(--border-default,#d3d3d3);border-radius:var(--radius-window-top,22px) var(--radius-window-top,22px) var(--radius-window-bottom,44px) var(--radius-window-bottom,44px);box-shadow:var(--shadow-window,0 0 15px 5px rgba(0,0,0,.08));display:flex;flex-direction:column;overflow:hidden;position:absolute;transform-origin:bottom right;z-index:2}.ai-chat-widget.dark .ai-chat-window{background:var(--bg-primary,#282625);border-color:var(--border-default,#5d5b5b);border-width:.7px}.ai-chat-window.closing{animation:ai-chat-window-close var(--duration-normal) var(--spring-smooth) forwards}.ai-chat-window.size-small{height:500px;width:380px}.ai-chat-window.size-medium,.ai-chat-window.size-small{max-height:calc(100vh - 100px);max-width:calc(100vw - 40px)}.ai-chat-window.size-medium{height:600px;width:420px}.ai-chat-window.size-large{height:700px;max-height:calc(100vh - 100px);max-width:calc(100vw - 40px);width:480px}.ai-chat-widget-container.bottom-right .ai-chat-window{bottom:calc(var(--button-size, 56px) + 8px);right:0}.ai-chat-widget-container.bottom-left .ai-chat-window{bottom:calc(var(--button-size, 56px) + 8px);left:0}.ai-chat-widget-container.top-right .ai-chat-window{right:0;top:calc(var(--button-size, 56px) + 8px)}.ai-chat-widget-container.top-left .ai-chat-window{left:0;top:calc(var(--button-size, 56px) + 8px)}.ai-chat-header{align-items:center;background:var(--bg-primary,#fff);border-bottom:1px solid var(--border-default,#d3d3d3);border-radius:var(--radius-window-top,22px) var(--radius-window-top,22px) 0 0;display:flex;justify-content:space-between;padding:12px var(--space-md,16px);position:relative;z-index:10}.ai-chat-widget.dark .ai-chat-header{background:var(--bg-primary,#282625);border-bottom-color:var(--border-default,#5d5b5b);border-bottom-width:.7px}.ai-chat-header.is-history{padding-left:var(--space-md)}.ai-chat-header.is-history .ai-chat-title{flex:1;min-width:0;overflow:hidden;padding-right:var(--space-lg);text-overflow:ellipsis;white-space:nowrap}.ai-chat-header-content{align-items:center;display:flex;flex:1;gap:var(--space-lg)}.ai-chat-header-actions{align-items:center;display:flex;gap:var(--space-sm)}.ai-chat-logo{border-radius:10px;height:36px;object-fit:cover;width:36px}.ai-chat-title{color:var(--text-primary,#3e3e3e);font-size:var(--text-xl,22px);font-weight:var(--font-weight-bold,700);letter-spacing:-.02em}.ai-chat-widget.dark .ai-chat-title{color:var(--text-primary,#fff)}.ai-chat-close-button,.ai-chat-header-button{align-items:center;background:transparent;border:none;border-radius:var(--radius-md);color:var(--text-muted);cursor:pointer;display:flex;height:32px;justify-content:center;padding:0;transition:color var(--duration-fast) ease;width:32px}.ai-chat-close-button:hover,.ai-chat-header-button:hover{color:var(--text-primary)}.ai-chat-close-button:active,.ai-chat-header-button:active{transform:scale(.95)}.ai-chat-close-button svg,.ai-chat-header-button svg{height:22px;width:22px}.ai-chat-button{align-items:center;background:var(--button-color,var(--btn-primary-bg));border:1px solid var(--border-default,#d3d3d3);border-radius:50%;box-shadow:var(--shadow-button,0 0 15px 9px rgba(0,0,0,.03));color:var(--button-icon-color,var(--btn-primary-text));cursor:pointer;display:flex;height:var(--button-size,56px);justify-content:center;overflow:hidden;position:relative;transition:transform var(--duration-fast) ease;width:var(--button-size,56px);z-index:1}.ai-chat-button:hover{transform:scale(1.05)}.ai-chat-button:active{transform:scale(.98)}.ai-chat-button-svg{height:50%;min-height:24px;min-width:24px;transition:transform var(--duration-fast) ease;width:50%}.ai-chat-button.is-open .ai-chat-button-svg{transform:rotate(0deg)}.ai-chat-button-icon{font-size:1.5em;line-height:1}.ai-chat-welcome-bubble{animation:ai-chat-bubble-fade-in .3s ease-out;background:var(--button-color,var(--btn-primary-bg,#07f));border:none;border-radius:16px;box-shadow:none;box-sizing:border-box;color:var(--button-icon-color,var(--btn-primary-text,#fff));cursor:pointer;font-size:13px;font-weight:500;line-height:1.5;max-width:min(420px,90vw);padding:12px 32px 12px 16px;position:absolute;text-align:left;white-space:normal;width:auto;z-index:0}.ai-chat-welcome-bubble-close{align-items:center;background:transparent;border:none;border-radius:50%;color:inherit;cursor:pointer;display:flex;height:20px;justify-content:center;opacity:.8;padding:0;position:absolute;right:8px;top:8px;transition:opacity .15s ease,background .15s ease;width:20px}.ai-chat-welcome-bubble-close:hover{background:hsla(0,0%,100%,.2);opacity:1}.ai-chat-welcome-bubble-close svg{height:12px;width:12px}.ai-chat-welcome-bubble-arrow{border-bottom:8px solid transparent;border-top:8px solid transparent;height:0;position:absolute;width:0}.ai-chat-widget-container.bottom-right .ai-chat-welcome-bubble{bottom:50%;right:calc(100% + 12px);text-align:left;transform:translateY(50%)}.ai-chat-widget-container.bottom-left .ai-chat-welcome-bubble{bottom:50%;left:calc(100% + 12px);text-align:left;transform:translateY(50%)}.ai-chat-widget-container.top-right .ai-chat-welcome-bubble{right:calc(100% + 12px);text-align:left;top:50%;transform:translateY(-50%)}.ai-chat-widget-container.top-left .ai-chat-welcome-bubble{left:calc(100% + 12px);text-align:left;top:50%;transform:translateY(-50%)}.ai-chat-widget-container.bottom-right .ai-chat-welcome-bubble-arrow{border-left:8px solid var(--button-color,var(--btn-primary-bg,#07f));right:-8px;top:50%;transform:translateY(-50%)}.ai-chat-widget-container.bottom-left .ai-chat-welcome-bubble-arrow{border-right:8px solid var(--button-color,var(--btn-primary-bg,#07f));left:-8px;top:50%;transform:translateY(-50%)}.ai-chat-widget-container.top-right .ai-chat-welcome-bubble-arrow{border-left:8px solid var(--button-color,var(--btn-primary-bg,#07f));right:-8px;top:50%;transform:translateY(-50%)}.ai-chat-widget-container.top-left .ai-chat-welcome-bubble-arrow{border-right:8px solid var(--button-color,var(--btn-primary-bg,#07f));left:-8px;top:50%;transform:translateY(-50%)}.ai-chat-welcome-bubble:hover{filter:brightness(1.1)}.ai-chat-widget-container.bottom-right .ai-chat-welcome-bubble{animation-name:ai-chat-bubble-fade-in-bottom-right}@keyframes ai-chat-bubble-fade-in-bottom-right{0%{opacity:0;transform:translateY(50%) translateX(8px)}to{opacity:1;transform:translateY(50%) translateX(0)}}.ai-chat-widget-container.bottom-left .ai-chat-welcome-bubble{animation-name:ai-chat-bubble-fade-in-bottom-left}@keyframes ai-chat-bubble-fade-in-bottom-left{0%{opacity:0;transform:translateY(50%) translateX(-8px)}to{opacity:1;transform:translateY(50%) translateX(0)}}.ai-chat-widget-container.top-right .ai-chat-welcome-bubble{animation-name:ai-chat-bubble-fade-in-top-right}@keyframes ai-chat-bubble-fade-in-top-right{0%{opacity:0;transform:translateY(-50%) translateX(8px)}to{opacity:1;transform:translateY(-50%) translateX(0)}}.ai-chat-widget-container.top-left .ai-chat-welcome-bubble{animation-name:ai-chat-bubble-fade-in-top-left}@keyframes ai-chat-bubble-fade-in-top-left{0%{opacity:0;transform:translateY(-50%) translateX(-8px)}to{opacity:1;transform:translateY(-50%) translateX(0)}}.ai-chat-trigger-pill{align-items:center;backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);background:hsla(0,0%,100%,.08);border:1px solid hsla(0,0%,100%,.15);border-radius:9999px;color:var(--text-primary,#fff);cursor:pointer;display:flex;font-size:13px;font-weight:500;gap:8px;height:40px;justify-content:center;padding:0 20px;position:relative;transition:all .3s ease;white-space:nowrap;z-index:1}.ai-chat-trigger-pill.is-open{background:var(--button-color,var(--btn-primary-bg));border-color:var(--border-default,#d3d3d3);box-shadow:var(--shadow-button,0 0 15px 9px rgba(0,0,0,.03));height:56px;padding:0;width:56px}.ai-chat-trigger-pill.is-open .ai-chat-trigger-pill-icon{height:24px;width:24px}.ai-chat-trigger-pill:hover:not(.is-open){background:hsla(0,0%,100%,.12);border-color:hsla(0,0%,100%,.25)}.ai-chat-trigger-pill.is-open:hover{transform:scale(1.05)}.ai-chat-trigger-pill:active{transform:scale(.98)}.ai-chat-trigger-pill-icon{flex-shrink:0;height:16px;transition:all .3s ease;width:16px}.ai-chat-widget.light .ai-chat-trigger-pill{background:rgba(0,0,0,.04);border-color:rgba(0,0,0,.12);color:var(--text-primary,#1a1a1a)}.ai-chat-widget.light .ai-chat-trigger-pill:hover:not(.is-open){background:rgba(0,0,0,.08);border-color:rgba(0,0,0,.2)}.ai-chat-trigger-input-container{align-items:flex-end;display:flex;flex-direction:column;gap:8px}.ai-chat-widget-container.bottom-left .ai-chat-trigger-input-container,.ai-chat-widget-container.top-left .ai-chat-trigger-input-container{align-items:flex-start}.ai-chat-trigger-input-expand{align-items:center;backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);background:hsla(0,0%,100%,.1);border:1px solid hsla(0,0%,100%,.15);border-radius:50%;color:hsla(0,0%,100%,.7);cursor:pointer;display:flex;height:32px;justify-content:center;padding:0;transition:all .2s ease;width:32px}.ai-chat-trigger-input-expand:hover{background:hsla(0,0%,100%,.15);color:hsla(0,0%,100%,.9);transform:translateY(-2px)}.ai-chat-trigger-input-expand:active{transform:translateY(0)}.ai-chat-trigger-input-expand svg{height:16px;width:16px}.ai-chat-widget.light .ai-chat-trigger-input-expand{background:rgba(0,0,0,.05);border:1px solid rgba(0,0,0,.1);color:rgba(0,0,0,.5)}.ai-chat-widget.light .ai-chat-trigger-input-expand:hover{background:rgba(0,0,0,.1);color:rgba(0,0,0,.7)}.ai-chat-trigger-input-wrapper{max-width:calc(100vw - 40px);position:relative;z-index:1}.ai-chat-widget-container.trigger-input-bar .ai-chat-trigger-input-wrapper{width:348px}.ai-chat-widget-container.trigger-input-bar.size-medium .ai-chat-trigger-input-wrapper{width:388px}.ai-chat-widget-container.trigger-input-bar.size-large .ai-chat-trigger-input-wrapper{width:448px}.ai-chat-trigger-input{backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);background:hsla(0,0%,100%,.08);border:1px solid hsla(0,0%,100%,.12);border-radius:var(--radius-input,62px);box-shadow:0 4px 24px rgba(0,0,0,.15);box-sizing:border-box;color:var(--input-text,#fff);font-size:var(--text-md,15px);height:52px;outline:none;padding:6px 52px 6px 16px;transition:all .2s ease;width:100%}.ai-chat-trigger-input::placeholder{color:var(--text-placeholder,hsla(0,0%,100%,.5))}.ai-chat-trigger-input:focus{background:hsla(0,0%,100%,.12);border-color:hsla(0,0%,100%,.25)}.ai-chat-trigger-input-btn{align-items:center;background:var(--primary-color,var(--button-color,#07f));border:none;border-radius:50%;box-shadow:0 2px 8px rgba(0,119,255,.3);color:#fff;cursor:pointer;display:flex;height:40px;justify-content:center;position:absolute;right:6px;top:50%;transform:translateY(-50%);transition:all .2s ease;width:40px}.ai-chat-trigger-input-btn:hover:not(:disabled){box-shadow:0 4px 12px rgba(0,119,255,.4);transform:translateY(-50%) scale(1.05)}.ai-chat-trigger-input-btn:active:not(:disabled){transform:translateY(-50%) scale(.95)}.ai-chat-trigger-input-btn:disabled{box-shadow:none;cursor:not-allowed;opacity:.4}.ai-chat-trigger-input-btn svg{height:18px;width:18px}.ai-chat-widget.light .ai-chat-trigger-input{backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);background:hsla(0,0%,100%,.7);border:1px solid rgba(0,0,0,.1);box-shadow:0 4px 24px rgba(0,0,0,.08);color:var(--input-text,#1a1a1a)}.ai-chat-widget.light .ai-chat-trigger-input::placeholder{color:var(--text-placeholder,rgba(0,0,0,.4))}.ai-chat-widget.light .ai-chat-trigger-input:focus{background:hsla(0,0%,100%,.85);border-color:rgba(0,0,0,.2)}.ai-chat-widget.light .ai-chat-trigger-input-btn{background:var(--primary-color,var(--button-color,#07f));box-shadow:0 2px 8px rgba(0,119,255,.25);color:#fff}.ai-chat-widget.light .ai-chat-trigger-input-btn:hover:not(:disabled){box-shadow:0 4px 12px rgba(0,119,255,.35)}.ai-chat-widget-container.trigger-input-bar{align-items:flex-end;display:flex;flex-direction:column;gap:12px}.ai-chat-widget-container.trigger-input-bar.bottom-left,.ai-chat-widget-container.trigger-input-bar.top-left{align-items:flex-start}.ai-chat-widget-container.trigger-input-bar .ai-chat-window{bottom:auto;left:auto;order:-1;position:relative;right:auto;top:auto;width:100%}.ai-chat-widget-container.trigger-input-bar .ai-chat-window.size-small{max-width:calc(100vw - 40px);width:380px}.ai-chat-widget-container.trigger-input-bar .ai-chat-window.size-medium{max-width:calc(100vw - 40px);width:420px}.ai-chat-widget-container.trigger-input-bar .ai-chat-window.size-large{max-width:calc(100vw - 40px);width:480px}.ai-chat-widget-container.trigger-input-bar .ai-chat-button,.ai-chat-widget-container.trigger-pill-text .ai-chat-button{display:none}.ai-chat-widget-container.trigger-pill-text.is-open{gap:8px}.ai-chat-input-container{background:var(--bg-primary,#fff);bottom:0;left:0;padding:8px 0 16px;position:absolute;right:0;z-index:10}.ai-chat-widget.dark .ai-chat-input-container{background:var(--bg-primary,#282625)}.ai-chat-input-container.separate{padding:0 var(--radius-window-gutter,16px) var(--radius-window-gutter,16px)}.ai-chat-input-wrapper{align-items:flex-end;background:var(--input-bg,#f4f4f4);border:1px solid var(--input-border,#d3d3d3);border-radius:var(--radius-input,62px);box-sizing:border-box;display:flex;gap:0;height:52px;overflow:hidden;padding:6px 6px 6px 16px;position:relative;transition:all var(--duration-fast,.15s) ease;z-index:5}.ai-chat-input-wrapper.multiline{border-radius:14px!important;min-height:64px;padding:10px 10px 10px 14px}.ai-chat-widget.dark .ai-chat-input-wrapper{background:var(--input-bg,#4a4846);border-color:var(--input-border,#5d5b5b);border-width:.7px;box-shadow:var(--shadow-input,0 0 10px rgba(0,0,0,.15))}.ai-chat-input-wrapper:focus-within{border-color:var(--text-muted,#a1a1aa)}.ai-chat-input{word-wrap:break-word!important;background:transparent!important;border:none!important;border-radius:0!important;box-shadow:none!important;box-sizing:border-box!important;color:var(--input-text,#000)!important;flex:1!important;font-family:inherit!important;font-size:var(--text-md,15px)!important;height:40px!important;line-height:20px!important;margin:0!important;max-height:40px!important;min-height:40px!important;min-width:0!important;outline:none!important;overflow-wrap:anywhere!important;overflow-x:hidden!important;overflow-y:auto!important;padding:10px var(--space-sm,8px)!important;resize:none!important;white-space:pre-wrap!important;width:0!important;word-break:break-word!important}.ai-chat-widget.dark .ai-chat-input{color:var(--input-text,#fff)}.ai-chat-input::placeholder{color:var(--text-placeholder,#a1a1aa)}.ai-chat-widget.dark .ai-chat-input::placeholder{color:var(--text-placeholder,#52525b)}.ai-chat-file-button{align-items:center;align-self:center;background:transparent;border:none;color:var(--text-placeholder);cursor:pointer;display:flex;flex-shrink:0;height:28px;justify-content:center;padding:0;transition:color var(--duration-fast) ease;width:28px}.ai-chat-file-button:hover{color:var(--text-secondary)}.ai-chat-file-button:disabled{cursor:not-allowed;opacity:.5}.ai-chat-send-button{align-items:center;align-self:center;background:var(--send-button-background,var(--primary-color,var(--button-color,var(--btn-primary-bg,#151515))));border:none;border-radius:50%;color:var(--button-icon-color,var(--btn-primary-text,#f4f4f4));cursor:pointer;display:flex;flex-shrink:0;height:40px;justify-content:center;min-height:40px;min-width:40px;padding:0;transition:all var(--duration-fast,.15s) ease;width:40px}.ai-chat-widget.dark .ai-chat-send-button{background:var(--send-button-background,var(--primary-color,var(--button-color,var(--btn-primary-bg,#fff))));color:var(--button-icon-color,var(--btn-primary-text,#312f2d))}.ai-chat-send-button.active{background:var(--send-button-background,var(--primary-color,var(--button-color,var(--btn-primary-bg,#151515))));color:var(--button-icon-color,var(--btn-primary-text,#f4f4f4))}.ai-chat-widget.dark .ai-chat-send-button.active{background:var(--send-button-background,var(--primary-color,var(--button-color,var(--btn-primary-bg,#fff))));color:var(--button-icon-color,var(--btn-primary-text,#312f2d))}.ai-chat-send-button:hover:not(:disabled){opacity:.8}.ai-chat-send-button:active:not(:disabled){transform:scale(.95)}.ai-chat-send-button:disabled{cursor:not-allowed;opacity:.3}.ai-chat-file-list{display:flex;flex-wrap:wrap;gap:var(--space-sm);padding:var(--space-sm) var(--space-sm)}.ai-chat-file-item{align-items:center;background:rgba(0,0,0,.05);border-radius:6px;display:flex;font-size:var(--text-xs);gap:var(--space-sm);padding:6px 10px}.ai-chat-file-extension{background:var(--btn-primary-bg);border-radius:3px;color:var(--btn-primary-text);display:inline-block;font-size:10px;font-weight:var(--font-weight-semibold);min-width:40px;padding:2px 6px;text-align:center;text-transform:uppercase}.ai-chat-file-info{display:flex;flex:1;flex-direction:column;gap:2px;min-width:0}.ai-chat-file-name{font-weight:var(--font-weight-medium);max-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ai-chat-file-size{color:var(--text-muted);font-size:10px;opacity:.7}.ai-chat-file-remove{align-items:center;background:none;border:none;color:inherit;cursor:pointer;display:flex;justify-content:center;opacity:.5;padding:var(--space-xs);transition:opacity var(--duration-fast) ease}.ai-chat-file-remove:hover{opacity:1}.ai-chat-data-policy{bottom:2px;color:var(--text-muted,#71717a);font-size:9px;left:0;line-height:1.4;opacity:.5;pointer-events:auto;position:absolute;right:0;text-align:center}.ai-chat-widget.dark .ai-chat-data-policy{color:var(--text-muted,#a1a1aa)}.ai-chat-data-policy-link{background:none;border:none;color:var(--text-muted,#71717a);cursor:pointer;font-family:inherit;font-size:inherit;margin:0;padding:0;text-decoration:underline;text-underline-offset:2px;transition:color .15s ease}.ai-chat-data-policy-link:hover{color:var(--text-secondary,#52525b)}.ai-chat-widget.dark .ai-chat-data-policy-link{color:var(--text-muted,#a1a1aa)}.ai-chat-widget.dark .ai-chat-data-policy-link:hover{color:var(--text-secondary,#d4d4d8)}.ai-chat-page-disclaimer{align-items:center;color:var(--text-muted,#71717a);display:flex;font-size:10px;gap:4px;justify-content:center;line-height:1.4;opacity:.7;padding:8px 16px;text-align:center}.ai-chat-widget.dark .ai-chat-page-disclaimer{color:var(--text-muted,#a1a1aa)}.ai-chat-page-disclaimer-link{background:none;border:none;color:var(--text-muted,#71717a);cursor:pointer;font-family:inherit;font-size:inherit;margin:0;padding:0;text-decoration:underline;text-underline-offset:2px;transition:color .15s ease}.ai-chat-page-disclaimer-link:hover{color:var(--text-secondary,#52525b)}.ai-chat-widget.dark .ai-chat-page-disclaimer-link{color:var(--text-muted,#a1a1aa)}.ai-chat-widget.dark .ai-chat-page-disclaimer-link:hover{color:var(--text-secondary,#d4d4d8)}.ai-chat-messages{-webkit-overflow-scrolling:touch;-ms-overflow-style:none;align-items:stretch;background:var(--bg-primary,#fff);display:flex;flex:1;flex-direction:column;gap:var(--space-md,16px);justify-content:flex-start;overflow-x:hidden;overflow-y:auto;padding:0 var(--space-md,16px) 100px;position:relative;scroll-behavior:smooth;scrollbar-width:none}.ai-chat-widget.dark .ai-chat-messages{background:var(--bg-primary,#18181b)}.ai-chat-messages::-webkit-scrollbar{display:none}.ai-chat-message{animation:ai-chat-message-slide-in .2s var(--spring-bounce);display:flex;flex-direction:column;max-width:90%}.ai-chat-message.user{align-items:flex-end;align-self:flex-end}.ai-chat-message.assistant{align-items:flex-start;align-self:flex-start;max-width:100%;width:100%}.ai-chat-message.tool{align-self:stretch;max-width:none;padding:0}.ai-chat-message-content{word-wrap:break-word;border-radius:18px;font-size:var(--text-md,15px);line-height:var(--line-height-relaxed,1.6);overflow-wrap:break-word;padding:8px 14px}.ai-chat-message.user .ai-chat-message-content{background:var(--user-bg,#f4f3f0);border-radius:18px;color:var(--user-text,#000)}.ai-chat-widget.dark .ai-chat-message.user .ai-chat-message-content{background:var(--user-bg,#484848);color:var(--user-text,#fff)}.ai-chat-message.assistant .ai-chat-message-content{background:var(--agent-bg,transparent);box-sizing:border-box;color:var(--agent-text,#000);padding:0;width:100%}.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content{color:var(--agent-text,#fff)}.ai-chat-message-timestamp{color:var(--text-muted,#71717a);font-size:var(--text-xs,12px);margin-top:var(--space-xs,4px);padding:0 var(--space-xs,4px)}.ai-chat-welcome{animation:ai-chat-welcome-fade-in .3s var(--spring-smooth);display:flex;flex-direction:column;gap:var(--space-md,16px);padding:var(--space-lg,24px) 0}.ai-chat-welcome-title{color:var(--text-primary,#3e3e3e);font-size:var(--text-2xl,28px);font-weight:var(--font-weight-bold,700);line-height:var(--line-height-tight,1.3)}.ai-chat-widget.dark .ai-chat-welcome-title{color:var(--text-primary,#fff)}.ai-chat-welcome-text{color:var(--text-secondary,#000);font-size:var(--text-md,15px);line-height:var(--line-height-relaxed,1.6);max-width:100%}.ai-chat-widget.dark .ai-chat-welcome-text{color:var(--text-secondary,#fff)}.ai-chat-typing{align-items:center;display:flex;gap:var(--space-xs,4px);padding:var(--space-sm,8px) var(--space-md,16px)}.ai-chat-typing-dot{animation:ai-chat-typing-bounce 1.4s ease-in-out infinite both;background:var(--text-muted,#71717a);border-radius:50%;height:8px;width:8px}.ai-chat-typing-dot:first-child{animation-delay:-.32s}.ai-chat-typing-dot:nth-child(2){animation-delay:-.16s}.ai-chat-typing-dot:nth-child(3){animation-delay:0s}@keyframes ai-chat-typing-bounce{0%,80%,to{opacity:.4;transform:scale(.6)}40%{opacity:1;transform:scale(1)}}.ai-chat-scroll-button{align-items:center;background:var(--bg-secondary,#f4f4f5);border:1px solid var(--border-subtle,rgba(0,0,0,.1));border-radius:50%;bottom:80px;box-shadow:0 2px 8px rgba(0,0,0,.1);color:var(--text-secondary,#71717a);cursor:pointer;display:flex;height:36px;justify-content:center;left:50%;opacity:0;pointer-events:none;position:absolute;transform:translateX(-50%);transition:background .15s ease,box-shadow .15s ease,opacity .15s ease,visibility .15s ease;visibility:hidden;width:36px;z-index:15}.ai-chat-scroll-button.visible{opacity:1;pointer-events:auto;visibility:visible}.ai-chat-scroll-button:hover{background:var(--bg-tertiary,#e4e4e7);box-shadow:0 4px 12px rgba(0,0,0,.15)}.ai-chat-scroll-button:active{background:var(--bg-tertiary,#d4d4d8)}.ai-chat-widget.dark .ai-chat-scroll-button{background:var(--bg-secondary,#3f3f46);border-color:var(--border-subtle,hsla(0,0%,100%,.1));box-shadow:0 2px 8px rgba(0,0,0,.3);color:var(--text-secondary,#a1a1aa)}.ai-chat-widget.dark .ai-chat-scroll-button:hover{background:var(--bg-tertiary,#52525b);box-shadow:0 4px 12px rgba(0,0,0,.4)}.ai-chat-error{background:var(--bg-secondary);border-radius:var(--radius-chat-bubble);color:var(--text-primary);font-size:var(--text-md);margin:0 auto;padding:10px var(--space-md)}.ai-chat-message.assistant .ai-chat-message-content p{margin:0 0 var(--space-sm) 0}.ai-chat-message.assistant .ai-chat-message-content p:last-child{margin-bottom:0}.ai-chat-message.assistant .ai-chat-message-content h1{font-size:1.5em}.ai-chat-message.assistant .ai-chat-message-content h1,.ai-chat-message.assistant .ai-chat-message-content h2{color:var(--text-primary,#3e3e3e);font-weight:var(--font-weight-bold,700);line-height:var(--line-height-tight,1.3);margin:var(--space-md,16px) 0 var(--space-sm,8px) 0}.ai-chat-message.assistant .ai-chat-message-content h2{font-size:1.3em}.ai-chat-message.assistant .ai-chat-message-content h3{color:var(--text-primary,#3e3e3e);font-size:1.15em;font-weight:var(--font-weight-semibold,600);line-height:var(--line-height-tight,1.3);margin:var(--space-sm,8px) 0 var(--space-xs,4px) 0}.ai-chat-message.assistant .ai-chat-message-content h4,.ai-chat-message.assistant .ai-chat-message-content h5,.ai-chat-message.assistant .ai-chat-message-content h6{color:var(--text-primary,#3e3e3e);font-size:1em;font-weight:var(--font-weight-semibold,600);line-height:var(--line-height-tight,1.3);margin:var(--space-sm,8px) 0 var(--space-xs,4px) 0}.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content h1,.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content h2,.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content h3,.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content h4,.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content h5,.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content h6{color:var(--text-primary,#fff)}.ai-chat-message.assistant .ai-chat-message-content>h1:first-child,.ai-chat-message.assistant .ai-chat-message-content>h2:first-child,.ai-chat-message.assistant .ai-chat-message-content>h3:first-child,.ai-chat-message.assistant .ai-chat-message-content>h4:first-child,.ai-chat-message.assistant .ai-chat-message-content>h5:first-child,.ai-chat-message.assistant .ai-chat-message-content>h6:first-child{margin-top:0}.ai-chat-message.assistant .ai-chat-message-content ul{list-style-type:disc;margin:var(--space-sm,8px) 0;padding-left:var(--space-lg,24px)}.ai-chat-message.assistant .ai-chat-message-content ul ul{list-style-type:circle;margin:var(--space-xs,4px) 0}.ai-chat-message.assistant .ai-chat-message-content ul ul ul{list-style-type:square}.ai-chat-message.assistant .ai-chat-message-content ol{list-style-type:decimal;margin:var(--space-sm,8px) 0;padding-left:var(--space-lg,24px)}.ai-chat-message.assistant .ai-chat-message-content ol ol{list-style-type:lower-alpha;margin:var(--space-xs,4px) 0}.ai-chat-message.assistant .ai-chat-message-content ol ol ol{list-style-type:lower-roman}.ai-chat-message.assistant .ai-chat-message-content li{margin-bottom:var(--space-xs,4px);padding-left:var(--space-xs,4px)}.ai-chat-message.assistant .ai-chat-message-content li:last-child{margin-bottom:0}.ai-chat-message.assistant .ai-chat-message-content li>ol,.ai-chat-message.assistant .ai-chat-message-content li>ul{margin-top:var(--space-xs,4px)}.ai-chat-message.assistant .ai-chat-message-content code{background:rgba(0,0,0,.05);border-radius:var(--radius-sm);font-family:SF Mono,Monaco,Cascadia Code,monospace;font-size:.9em;padding:2px 6px}.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content code{background:hsla(0,0%,100%,.1)}.ai-chat-message.assistant .ai-chat-message-content pre{background:rgba(0,0,0,.05);border-radius:var(--radius-md);margin:var(--space-sm) 0;overflow-x:auto;padding:var(--space-sm)}.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content pre{background:hsla(0,0%,100%,.05)}.ai-chat-message.assistant .ai-chat-message-content pre code{background:transparent;padding:0}.ai-chat-message.assistant .ai-chat-message-content a{color:var(--btn-primary-bg);text-decoration:underline}.ai-chat-message.assistant .ai-chat-message-content strong{font-weight:var(--font-weight-semibold)}.ai-chat-message.assistant .ai-chat-message-content blockquote{border-left:3px solid var(--border-default);color:var(--text-muted);margin:var(--space-sm) 0;padding-left:var(--space-md)}.ai-chat-message.assistant .ai-chat-message-content hr{border:none;border-top:1px solid var(--border-subtle,rgba(0,0,0,.1));margin:var(--space-lg,24px) 0}.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content hr{border-top-color:var(--border-subtle,hsla(0,0%,100%,.1))}.ai-chat-message.assistant .ai-chat-message-content .table-wrapper{border:1px solid var(--border-subtle,rgba(0,0,0,.1));border-radius:var(--radius-md,8px);box-sizing:border-box;display:block;margin:var(--space-sm) var(--space-sm);max-width:100%;overflow:hidden;width:auto}.ai-chat-message.assistant .ai-chat-message-content .table-scroll{max-width:100%;overflow-x:auto;overflow-y:hidden;width:100%}.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content .table-wrapper{border-color:var(--border-subtle,hsla(0,0%,100%,.1))}.ai-chat-message.assistant .ai-chat-message-content table{border-collapse:collapse;font-size:var(--text-sm);min-width:100%;width:max-content}.ai-chat-message.assistant .ai-chat-message-content td,.ai-chat-message.assistant .ai-chat-message-content th{border-bottom:1px solid var(--border-subtle,rgba(0,0,0,.1));border-right:1px solid var(--border-subtle,rgba(0,0,0,.1));padding:var(--space-sm);text-align:left}.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content td,.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content th{border-color:var(--border-subtle,hsla(0,0%,100%,.1))}.ai-chat-message.assistant .ai-chat-message-content td:last-child,.ai-chat-message.assistant .ai-chat-message-content th:last-child{border-right:none}.ai-chat-message.assistant .ai-chat-message-content tr:last-child td{border-bottom:none}.ai-chat-message.assistant .ai-chat-message-content th{background:rgba(0,0,0,.03);font-weight:var(--font-weight-semibold);white-space:nowrap}.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content th{background:hsla(0,0%,100%,.05)}.ai-chat-message.assistant .ai-chat-message-content tbody tr:nth-child(2n){background:rgba(0,0,0,.02)}.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content tbody tr:nth-child(2n){background:hsla(0,0%,100%,.03)}.ai-chat-suggested-questions{align-self:flex-end;margin:0;padding:16px 0 0;width:100%}.ai-chat-suggested-questions-list{align-items:center;display:flex;flex-wrap:wrap;gap:6px;justify-content:flex-end}.ai-chat-suggested-question{align-items:center;background:transparent;border:1px solid var(--border-default,#d4d4d8);border-radius:var(--radius-preset-badge,18px);color:var(--text-primary,#18181b);cursor:pointer;display:inline-flex;font-size:14px;font-weight:400;gap:6px;justify-content:center;line-height:1.3;max-width:100%;overflow:hidden;padding:8px 14px;text-overflow:ellipsis;transition:background .15s ease,border-color .15s ease,transform .1s ease;white-space:nowrap}.ai-chat-widget.dark .ai-chat-suggested-question{background:transparent;border-color:var(--border-subtle,#52525b);color:var(--text-primary,#fafafa)}.ai-chat-suggested-question-text{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ai-chat-suggested-question:hover{background:var(--bg-hover,#f4f4f5);border-color:var(--border-default,#d4d4d8)}.ai-chat-widget.dark .ai-chat-suggested-question:hover{background:var(--bg-hover,#3f3f46);border-color:var(--border-subtle,#52525b)}.ai-chat-suggested-question:active{transform:scale(.98)}.ai-chat-suggested-question.action-type{border:none}.ai-chat-suggested-question.action-type,.ai-chat-widget.dark .ai-chat-suggested-question.action-type{background:var(--primary-color,var(--button-color,#ef4444));color:var(--button-icon-color,#fff)}.ai-chat-suggested-question.action-type:hover{background:var(--primary-color,var(--button-color,#ef4444));opacity:.9}.ai-chat-suggested-question-icon{align-items:center;display:flex;flex-shrink:0;justify-content:center}.ai-chat-suggested-question:not(.action-type) .ai-chat-suggested-question-icon{display:none}.ai-chat-follow-up-suggestions{box-sizing:border-box;margin:0;padding:8px 16px 0;width:100%}.ai-chat-follow-up-list{align-items:flex-end;display:flex;flex-direction:column;gap:6px}.ai-chat-follow-up-item{align-items:center;border:none;border-radius:var(--radius-preset-badge,18px);cursor:pointer;display:inline-flex;font-size:14px;font-weight:400;gap:6px;justify-content:center;line-height:1.3;max-width:100%;overflow:hidden;padding:8px 14px;text-overflow:ellipsis;transition:opacity .15s ease,transform .1s ease;white-space:nowrap}.ai-chat-follow-up-item,.ai-chat-widget.dark .ai-chat-follow-up-item{background:var(--primary-color,var(--button-color,#07f));color:var(--button-icon-color,#fff)}.ai-chat-follow-up-item:hover{opacity:.9}.ai-chat-follow-up-item:active{transform:scale(.98)}.ai-chat-follow-up-item.question-type{background:transparent;border:1px solid var(--border-default,#d4d4d8);color:var(--text-primary,#18181b)}.ai-chat-widget.dark .ai-chat-follow-up-item.question-type,.dark .ai-chat-follow-up-item.question-type,[data-color-mode=dark] .ai-chat-follow-up-item.question-type,[data-theme=dark] .ai-chat-follow-up-item.question-type{background:transparent;border:1px solid var(--border-subtle,#52525b);color:var(--text-primary,#fafafa)}@media (prefers-color-scheme:dark){.ai-chat-follow-up-item.question-type{background:transparent;border:1px solid var(--border-subtle,#52525b);color:var(--text-primary,#fafafa)}}.ai-chat-follow-up-item.question-type:hover{background:var(--bg-hover,#f4f4f5);opacity:1}.ai-chat-widget.dark .ai-chat-follow-up-item.question-type:hover,.dark .ai-chat-follow-up-item.question-type:hover,[data-color-mode=dark] .ai-chat-follow-up-item.question-type:hover,[data-theme=dark] .ai-chat-follow-up-item.question-type:hover{background:var(--bg-hover,#3f3f46);opacity:1}@media (prefers-color-scheme:dark){.ai-chat-follow-up-item.question-type:hover{background:var(--bg-hover,#3f3f46);opacity:1}}.ai-chat-follow-up-item.action-type{background:var(--primary-color,var(--button-color,#07f));border:none;color:var(--button-icon-color,#fff)}.ai-chat-follow-up-icon{align-items:center;display:flex;flex-shrink:0;justify-content:center}.ai-chat-follow-up-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ai-chat-feedback-buttons{align-items:center;display:flex;gap:var(--space-xs)}.ai-chat-feedback{align-items:center;display:inline-flex;gap:0;height:20px}.ai-chat-feedback-button{align-items:center;background:transparent!important;border:none;border-radius:var(--radius-sm);color:var(--text-placeholder);cursor:pointer;display:flex;font-size:var(--text-sm);height:20px;justify-content:center;padding:var(--space-xs);transition:all var(--duration-fast) var(--spring-bounce)}.ai-chat-feedback-button:hover:not(:disabled){background:none!important;color:var(--text-secondary)}.ai-chat-feedback-button:active:not(:disabled){transform:scale(.9)}.ai-chat-feedback-button:disabled{cursor:not-allowed;opacity:.4}.ai-chat-feedback-button.active{background:none!important;color:var(--text-primary)}.ai-chat-feedback.submitted{align-items:center;animation:ai-chat-feedback-morph .3s var(--spring-bounce);gap:var(--space-xs)}.ai-chat-feedback-message{align-items:center;display:flex;gap:4px;margin-left:var(--space-xxs)}.ai-chat-feedback-checkmark{animation:ai-chat-checkmark-pop .3s var(--spring-bounce);color:#10b981;font-size:var(--text-md);font-weight:700}.ai-chat-feedback-text{color:#10b981;font-size:var(--text-xs);font-weight:var(--font-weight-medium)}.ai-chat-history-panel{background:var(--bg-primary,#fff);display:flex;flex:1;flex-direction:column;overflow:hidden}.ai-chat-widget.dark .ai-chat-history-panel{background:var(--bg-primary,#18181b)}.ai-chat-history-empty,.ai-chat-history-loading{align-items:center;color:var(--text-muted);display:flex;flex:1;font-size:var(--text-sm);justify-content:center;padding:var(--space-lg);text-align:center}.ai-chat-history-list{-ms-overflow-style:none;display:flex;flex:1;flex-direction:column;gap:var(--space-sm);overflow-y:auto;padding:var(--space-xs) var(--space-md) 120px;scrollbar-width:none}.ai-chat-history-list::-webkit-scrollbar{display:none}.ai-chat-history-list.exiting{animation:ai-chat-history-exit .22s var(--spring-smooth) forwards}.ai-chat-history-item{align-items:center;background:var(--user-bg,#f4f4f5);border-radius:var(--radius-history-item,15px);display:flex;flex:0 0 auto;flex-direction:row;height:var(--history-item-height,36px);margin:0;overflow:hidden;transition:background var(--duration-fast,.15s) ease;width:100%}.ai-chat-history-item-content{align-items:center;background:transparent;border:none;cursor:pointer;display:flex;flex:1;flex-direction:row;height:100%;min-width:0;padding:0 var(--space-xs,4px) 0 var(--space-md,16px);text-align:left}.ai-chat-widget.dark .ai-chat-history-item{background:var(--user-bg,#27272a)}.ai-chat-history-item:hover{background:var(--user-bg-hover,#e5e7eb)}.ai-chat-widget.dark .ai-chat-history-item:hover{background:var(--user-bg-hover,#3f3f46)}.ai-chat-history-item.active{background:var(--user-bg-hover,#e5e7eb)}.ai-chat-widget.dark .ai-chat-history-item.active{background:var(--user-bg-hover,#3f3f46)}.ai-chat-history-item-preview{color:var(--text-primary,#18181b);flex:1;font-size:var(--text-sm,14px);font-weight:var(--font-weight-medium,500);line-height:var(--line-height-normal,1.4);margin-bottom:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ai-chat-widget.dark .ai-chat-history-item-preview{color:var(--text-primary,#fafafa)}.ai-chat-history-item.active .ai-chat-history-item-preview{font-weight:var(--font-weight-medium)}.ai-chat-history-item-meta{display:none}.ai-chat-history-item-delete{align-items:center;background:transparent;border:none;border-radius:var(--radius-sm,6px);color:var(--text-muted,#71717a);cursor:pointer;display:flex;flex-shrink:0;height:24px;justify-content:center;margin-left:auto;margin-right:var(--space-xs,4px);opacity:0;transition:opacity var(--duration-fast,.15s) ease,background var(--duration-fast,.15s) ease,color var(--duration-fast,.15s) ease;width:24px}.ai-chat-history-item-delete svg{height:14px;width:14px}.ai-chat-history-item:hover .ai-chat-history-item-delete{opacity:1}.ai-chat-history-item-delete:hover{background:rgba(239,68,68,.1);color:#ef4444}.ai-chat-widget.dark .ai-chat-history-item-delete:hover{background:rgba(239,68,68,.2);color:#f87171}.ai-chat-tool-row{align-items:center;display:flex;gap:10px;margin:2px 0;padding:0}.ai-chat-tool-gear{color:#1f2937;flex-shrink:0;height:20px;width:20px}.ai-chat-tool-gear.spinning{animation:ai-chat-gear-spin 1.5s linear infinite}.ai-chat-tool-badges{align-items:center;display:flex;flex-wrap:wrap;gap:8px}.ai-chat-tool-badge{align-items:center;background:#e5e7eb;border:1px solid #d1d5db;border-radius:var(--radius-action-badge,8px);color:#1f2937;display:inline-flex;font-size:12px;font-weight:500;gap:4px;line-height:1.2;padding:5px 12px;transition:all .2s ease;white-space:nowrap}.ai-chat-tool-badge.loading{animation:ai-chat-tool-gradient 2s linear infinite;background:linear-gradient(90deg,var(--tool-loading-bg-1,#e0e0e0) 0,var(--tool-loading-bg-2,#f0f0f0) 25%,var(--tool-loading-bg-3,#fff) 50%,var(--tool-loading-bg-2,#f0f0f0) 75%,var(--tool-loading-bg-1,#e0e0e0) 100%);background-size:200% 100%;color:var(--tool-loading-text,#1a1a1a);position:relative}.ai-chat-widget:not(.dark) .ai-chat-tool-badge.loading{--tool-loading-bg-1:#2a2a2a;--tool-loading-bg-2:#3a3a3a;--tool-loading-bg-3:#4a4a4a;--tool-loading-text:#fff}.ai-chat-tool-badge.completed{background:#e5e7eb;border:1px solid #d1d5db;color:#1f2937}.ai-chat-widget.dark .ai-chat-tool-badge,.ai-chat-widget[data-theme=dark] .ai-chat-tool-badge,.chakra-ui-dark .ai-chat-tool-badge,.dark .ai-chat-tool-badge,[data-theme=dark] .ai-chat-tool-badge,html.dark .ai-chat-tool-badge{background:hsla(0,0%,100%,.12);border:1px solid hsla(0,0%,100%,.2);color:hsla(0,0%,100%,.9)}.ai-chat-widget.dark .ai-chat-tool-gear,.ai-chat-widget[data-theme=dark] .ai-chat-tool-gear,.chakra-ui-dark .ai-chat-tool-gear,.dark .ai-chat-tool-gear,[data-theme=dark] .ai-chat-tool-gear,html.dark .ai-chat-tool-gear{color:#fff}.ai-chat-tool-badge.error{background:var(--tool-error-bg,rgba(239,68,68,.15));color:var(--tool-error-text,#ef4444)}.ai-chat-tool-badge .ai-chat-tool-check{color:#fff;flex-shrink:0}.ai-chat-tool-badge .ai-chat-tool-error{color:#ef4444;flex-shrink:0}.tool-name{font-weight:500;line-height:1.2;white-space:nowrap}.ai-chat-tool-action{box-sizing:border-box;padding:0;width:100%}@keyframes ai-chat-skeleton-pulse{0%,to{opacity:.4}50%{opacity:.7}}.ai-chat-action-skeleton-item{animation:ai-chat-skeleton-pulse 1.5s ease-in-out infinite;background:var(--bg-secondary,#e5e7eb)}.ai-chat-widget.dark .ai-chat-action-skeleton-item,.chakra-ui-dark .ai-chat-action-skeleton-item,.dark .ai-chat-action-skeleton-item,[data-theme=dark] .ai-chat-action-skeleton-item{background:hsla(0,0%,100%,.1)}.ai-chat-action-skeleton-content{display:flex;flex-direction:column;gap:16px}.ai-chat-action-skeleton-header{align-items:center;display:flex;gap:10px}.ai-chat-action-skeleton-box{background:rgba(0,0,0,.08);border-radius:10px;display:flex;flex-direction:column;gap:8px;padding:14px}.ai-chat-widget.dark .ai-chat-action-skeleton-box,.chakra-ui-dark .ai-chat-action-skeleton-box,.dark .ai-chat-action-skeleton-box,[data-theme=dark] .ai-chat-action-skeleton-box{background:rgba(0,0,0,.25)}.ai-chat-action-card{--action-accent:var(--primary-color,#3b82f6);background:var(--bg-secondary,#f9fafb);border:1px solid var(--border-subtle,rgba(0,0,0,.06));border-radius:12px;box-sizing:border-box;margin-top:4px;padding:16px;transition:all .2s ease;width:100%}.ai-chat-widget.dark .ai-chat-action-card,.chakra-ui-dark .ai-chat-action-card,.dark .ai-chat-action-card,[data-theme=dark] .ai-chat-action-card{background:hsla(0,0%,100%,.05);border-color:hsla(0,0%,100%,.1)}.ai-chat-action-booked{background:var(--bg-secondary,#f9fafb);border:1px solid var(--border-subtle,rgba(0,0,0,.06))}.ai-chat-widget.dark .ai-chat-action-booked,.chakra-ui-dark .ai-chat-action-booked,.dark .ai-chat-action-booked,[data-theme=dark] .ai-chat-action-booked{background:var(--bg-secondary,#3a3a3a)}.ai-chat-action-header{align-items:center;color:var(--text-primary,#3e3e3e);display:flex;font-size:var(--text-md,15px);font-weight:var(--font-weight-semibold,600);gap:var(--space-sm,8px);margin-bottom:var(--space-md,16px)}.ai-chat-widget.dark .ai-chat-action-header,.chakra-ui-dark .ai-chat-action-header,.dark .ai-chat-action-header,[data-theme=dark] .ai-chat-action-header{color:var(--text-primary,#fff)}.ai-chat-action-icon{color:var(--action-accent,var(--primary-color,#3b82f6));flex-shrink:0;height:20px;width:20px}.ai-chat-action-success-icon-wrapper{align-items:center;background:var(--action-accent,var(--primary-color,#22c55e));border-radius:50%;color:#fff;display:flex;flex-shrink:0;height:24px;justify-content:center;width:24px}.ai-chat-action-icon-success{color:currentColor;height:14px;width:14px}.ai-chat-action-detail-box{background:var(--bg-primary,#fff);border:1px solid var(--border-subtle,rgba(0,0,0,.08));border-radius:var(--radius-md,8px);display:flex;flex-direction:column;gap:4px;padding:12px 16px}.ai-chat-widget.dark .ai-chat-action-detail-box,.chakra-ui-dark .ai-chat-action-detail-box,.dark .ai-chat-action-detail-box,[data-theme=dark] .ai-chat-action-detail-box{background:rgba(0,0,0,.2);border-color:hsla(0,0%,100%,.05)}.ai-chat-action-label-small{color:var(--text-muted,#71717a);font-size:11px;font-weight:600;letter-spacing:.5px;text-transform:uppercase}.ai-chat-action-value-large{color:var(--text-primary,#3e3e3e);font-size:15px;font-weight:500}.ai-chat-widget.dark .ai-chat-action-value-large,.chakra-ui-dark .ai-chat-action-value-large,.dark .ai-chat-action-value-large,[data-theme=dark] .ai-chat-action-value-large{color:#fff}.ai-chat-action-body{display:flex;flex-direction:column;gap:var(--space-md,16px)}.ai-chat-action-close-btn{align-items:center;background:transparent;border:none;border-radius:6px;color:var(--ai-chat-fg-muted,#6b7280);cursor:pointer;display:flex;height:28px;justify-content:center;padding:0;position:absolute;right:12px;top:12px;transition:all .15s ease;width:28px;z-index:10}.ai-chat-action-close-btn:hover{background:var(--ai-chat-bg-muted,#f3f4f6);color:var(--ai-chat-fg,#1f2937)}.ai-chat-action-close-btn:active{transform:scale(.95)}.ai-chat-action-close-btn:focus-visible{outline:2px solid var(--ai-chat-accent,#2563eb);outline-offset:2px}.ai-chat-action-card--closable,.ai-chat-form-card--closable{position:relative}.ai-chat-action-card--closable .ai-chat-action-header,.ai-chat-form-card--closable .ai-chat-form-card__header{padding-right:40px}.ai-chat-widget.dark .ai-chat-action-close-btn:hover,.chakra-ui-dark .ai-chat-action-close-btn:hover,.dark .ai-chat-action-close-btn:hover,[data-theme=dark] .ai-chat-action-close-btn:hover{background:hsla(0,0%,100%,.1);color:var(--ai-chat-fg,#f3f4f6)}.ai-chat-action-field{display:flex;flex-direction:column;gap:var(--space-xs,6px)}.ai-chat-action-label{color:var(--text-secondary,#6b7280);font-size:var(--text-sm,13px);font-weight:var(--font-weight-medium,500)}.ai-chat-widget.dark .ai-chat-action-label,.chakra-ui-dark .ai-chat-action-label,.dark .ai-chat-action-label,[data-theme=dark] .ai-chat-action-label{color:var(--text-secondary,#a1a1aa)}.ai-chat-action-input{background:var(--bg-primary,#fff);border:1px solid var(--border-default,#e5e7eb);border-radius:var(--radius-md,8px);color:var(--text-primary,#3e3e3e);font-size:var(--text-sm,13px);outline:none;padding:10px 12px;transition:border-color .2s ease,box-shadow .2s ease}.ai-chat-action-input:focus{border-color:var(--action-accent,var(--primary-color,#3b82f6));box-shadow:0 0 0 3px rgba(59,130,246,.1)}.ai-chat-action-input::placeholder{color:var(--text-muted,#9ca3af)}.ai-chat-widget.dark .ai-chat-action-input,.chakra-ui-dark .ai-chat-action-input,.dark .ai-chat-action-input,[data-theme=dark] .ai-chat-action-input{background:rgba(0,0,0,.2);border-color:hsla(0,0%,100%,.1);color:#fff}.ai-chat-widget.dark .ai-chat-action-input:focus,.chakra-ui-dark .ai-chat-action-input:focus,.dark .ai-chat-action-input:focus,[data-theme=dark] .ai-chat-action-input:focus{border-color:var(--action-accent,var(--primary-color,#3b82f6));box-shadow:0 0 0 3px rgba(59,130,246,.2)}.ai-chat-action-button{background:var(--action-accent,var(--primary-color,#3b82f6));border:none;border-radius:9999px;color:#fff;cursor:pointer;font-size:var(--text-sm,13px);font-weight:var(--font-weight-semibold,600);padding:12px 16px;transition:all .2s ease;width:100%}.ai-chat-action-button:hover:not(:disabled){opacity:.9;transform:translateY(-1px)}.ai-chat-action-button:disabled{cursor:not-allowed;opacity:.5}.ai-chat-action-link-button{align-items:center;background:var(--action-accent,var(--primary-color,#3b82f6));border:none;border-radius:9999px;box-sizing:border-box;color:#fff;display:flex;font-size:14px;font-weight:600;gap:6px;justify-content:center;margin-top:8px;padding:12px;text-decoration:none;transition:all .2s ease;width:100%}.ai-chat-action-link-button:hover{opacity:.9;transform:translateY(-1px)}.ai-chat-action-error{background:rgba(239,68,68,.1);border-radius:var(--radius-md,8px);color:#dc2626;font-size:var(--text-sm,13px);padding:10px 12px}.ai-chat-widget.dark .ai-chat-action-error,.chakra-ui-dark .ai-chat-action-error,.dark .ai-chat-action-error,[data-theme=dark] .ai-chat-action-error{background:rgba(239,68,68,.2);color:#fca5a5}.ai-chat-action-hint{color:var(--text-muted,#9ca3af);font-size:var(--text-sm,13px);padding:var(--space-sm,8px);text-align:center}.ai-chat-action-date-grid{display:grid;gap:var(--space-xs,6px);grid-template-columns:repeat(auto-fill,minmax(90px,1fr))}.ai-chat-action-date-btn{background:var(--bg-primary,#fff);border:1px solid var(--border-default,#e5e7eb);border-radius:var(--radius-md,8px);color:var(--text-primary,#3e3e3e);cursor:pointer;font-size:var(--text-xs,12px);font-weight:var(--font-weight-medium,500);padding:8px 10px;text-align:center;transition:all .15s ease}.ai-chat-action-date-btn:hover{background:rgba(59,130,246,.05);border-color:var(--action-accent,var(--primary-color,#3b82f6))}.ai-chat-action-date-btn.active{background:var(--action-accent,var(--primary-color,#3b82f6));border-color:var(--action-accent,var(--primary-color,#3b82f6));color:#fff}.ai-chat-widget.dark .ai-chat-action-date-btn,.chakra-ui-dark .ai-chat-action-date-btn,.dark .ai-chat-action-date-btn,[data-theme=dark] .ai-chat-action-date-btn{background:rgba(0,0,0,.2);border-color:hsla(0,0%,100%,.1);color:#fff}.ai-chat-widget.dark .ai-chat-action-date-btn:hover,.chakra-ui-dark .ai-chat-action-date-btn:hover,.dark .ai-chat-action-date-btn:hover,[data-theme=dark] .ai-chat-action-date-btn:hover{background:rgba(59,130,246,.15);border-color:var(--action-accent,var(--primary-color,#3b82f6))}.ai-chat-widget.dark .ai-chat-action-date-btn.active,.chakra-ui-dark .ai-chat-action-date-btn.active,.dark .ai-chat-action-date-btn.active,[data-theme=dark] .ai-chat-action-date-btn.active{background:var(--action-accent,var(--primary-color,#3b82f6));border-color:var(--action-accent,var(--primary-color,#3b82f6));color:#fff}.ai-chat-action-time-grid{display:grid;gap:var(--space-xs,6px);grid-template-columns:repeat(auto-fill,minmax(100px,1fr))}.ai-chat-action-time-btn{background:var(--bg-primary,#fff);border:1px solid var(--border-default,#e5e7eb);border-radius:var(--radius-md,8px);color:var(--text-primary,#3e3e3e);cursor:pointer;font-size:var(--text-xs,12px);font-weight:var(--font-weight-medium,500);padding:8px 10px;text-align:center;transition:all .15s ease}.ai-chat-action-time-btn:hover{background:rgba(59,130,246,.05);border-color:var(--action-accent,var(--primary-color,#3b82f6))}.ai-chat-action-time-btn.active{background:var(--action-accent,var(--primary-color,#3b82f6));border-color:var(--action-accent,var(--primary-color,#3b82f6));color:#fff}.ai-chat-widget.dark .ai-chat-action-time-btn,.chakra-ui-dark .ai-chat-action-time-btn,.dark .ai-chat-action-time-btn,[data-theme=dark] .ai-chat-action-time-btn{background:rgba(0,0,0,.2);border-color:hsla(0,0%,100%,.1);color:#fff}.ai-chat-widget.dark .ai-chat-action-time-btn:hover,.chakra-ui-dark .ai-chat-action-time-btn:hover,.dark .ai-chat-action-time-btn:hover,[data-theme=dark] .ai-chat-action-time-btn:hover{background:rgba(59,130,246,.15);border-color:var(--action-accent,var(--primary-color,#3b82f6))}.ai-chat-widget.dark .ai-chat-action-time-btn.active,.chakra-ui-dark .ai-chat-action-time-btn.active,.dark .ai-chat-action-time-btn.active,[data-theme=dark] .ai-chat-action-time-btn.active{background:var(--action-accent,var(--primary-color,#3b82f6));border-color:var(--action-accent,var(--primary-color,#3b82f6));color:#fff}.ai-chat-link-preview{--action-accent:var(--primary-color,#3b82f6);background:var(--bg-secondary,#f4f4f4);border:1px solid var(--border-subtle,rgba(0,0,0,.08));border-radius:var(--radius-lg,12px);cursor:pointer;display:flex;flex-direction:column;margin-top:4px;overflow:hidden;padding:0!important;position:relative;transition:border-color .2s,box-shadow .2s,transform .2s}.ai-chat-link-preview:hover{border-color:var(--action-accent);box-shadow:0 2px 8px rgba(0,0,0,.1);transform:translateY(-1px)}.ai-chat-link-preview:focus{border-color:var(--action-accent);box-shadow:0 0 0 2px rgba(59,130,246,.2);outline:none}.ai-chat-widget.dark .ai-chat-link-preview,.chakra-ui-dark .ai-chat-link-preview,.dark .ai-chat-link-preview,[data-theme=dark] .ai-chat-link-preview{background:var(--bg-secondary,#3a3a3a);border-color:hsla(0,0%,100%,.08)}.ai-chat-widget.dark .ai-chat-link-preview:hover,.chakra-ui-dark .ai-chat-link-preview:hover,.dark .ai-chat-link-preview:hover,[data-theme=dark] .ai-chat-link-preview:hover{border-color:var(--action-accent);box-shadow:0 2px 12px rgba(0,0,0,.3)}.ai-chat-link-preview__image{aspect-ratio:1.91/1;background:var(--bg-muted,#e5e7eb);overflow:hidden;width:100%}.ai-chat-widget.dark .ai-chat-link-preview__image,.chakra-ui-dark .ai-chat-link-preview__image,.dark .ai-chat-link-preview__image,[data-theme=dark] .ai-chat-link-preview__image{background:hsla(0,0%,100%,.05)}.ai-chat-link-preview__image img{height:100%;object-fit:cover;width:100%}.ai-chat-link-preview__content{flex:1;padding:8px 10px}.ai-chat-link-preview__site{align-items:center;display:flex;gap:6px;margin-bottom:6px}.ai-chat-link-preview__favicon{border-radius:2px;flex-shrink:0;height:16px;width:16px}.ai-chat-link-preview__domain{color:var(--text-muted,#71717a);font-size:12px;letter-spacing:.5px;overflow:hidden;text-overflow:ellipsis;text-transform:uppercase;white-space:nowrap}.ai-chat-link-preview__title{-webkit-line-clamp:2;line-clamp:2;-webkit-box-orient:vertical;color:var(--text-primary,#3e3e3e);display:-webkit-box;font-size:15px;font-weight:600;line-height:1.3;margin:0 0 4px;overflow:hidden}.ai-chat-widget.dark .ai-chat-link-preview__title,.chakra-ui-dark .ai-chat-link-preview__title,.dark .ai-chat-link-preview__title,[data-theme=dark] .ai-chat-link-preview__title{color:#fff}.ai-chat-link-preview__description{-webkit-line-clamp:2;line-clamp:2;-webkit-box-orient:vertical;color:var(--text-muted,#71717a);display:-webkit-box;font-size:13px;line-height:1.4;margin:0;overflow:hidden}.ai-chat-link-preview__context{border-top:1px solid var(--border-subtle,rgba(0,0,0,.08));color:var(--text-muted,#71717a);font-size:12px;font-style:italic;margin:8px 0 0;padding-top:8px}.ai-chat-widget.dark .ai-chat-link-preview__context,.chakra-ui-dark .ai-chat-link-preview__context,.dark .ai-chat-link-preview__context,[data-theme=dark] .ai-chat-link-preview__context{border-color:hsla(0,0%,100%,.08)}.ai-chat-link-preview__arrow{align-items:center;background:var(--bg-primary,#fff);border-radius:50%;box-shadow:0 1px 3px rgba(0,0,0,.1);color:var(--text-muted,#71717a);display:flex;height:28px;justify-content:center;opacity:0;position:absolute;right:12px;top:12px;transition:opacity .2s,background .2s;width:28px}.ai-chat-link-preview:hover .ai-chat-link-preview__arrow{opacity:1}.ai-chat-widget.dark .ai-chat-link-preview__arrow,.chakra-ui-dark .ai-chat-link-preview__arrow,.dark .ai-chat-link-preview__arrow,[data-theme=dark] .ai-chat-link-preview__arrow{background:hsla(0,0%,100%,.1);color:#fff}.ai-chat-link-preview--error{border-color:rgba(239,68,68,.3)}.ai-chat-link-preview--error:hover{border-color:rgba(239,68,68,.5)}.ai-chat-link-preview__error-text{color:#dc2626;font-size:12px;margin:4px 0 0}.ai-chat-widget.dark .ai-chat-link-preview__error-text,.chakra-ui-dark .ai-chat-link-preview__error-text,.dark .ai-chat-link-preview__error-text,[data-theme=dark] .ai-chat-link-preview__error-text{color:#fca5a5}.ai-chat-video-player{--action-accent:var(--primary-color,#3b82f6);background:var(--bg-secondary,#f4f4f4);border-radius:var(--radius-lg,12px);display:flex;flex-direction:column;gap:0;margin-top:4px;overflow:hidden;padding:0!important}.ai-chat-widget.dark .ai-chat-video-player,.chakra-ui-dark .ai-chat-video-player,.dark .ai-chat-video-player,[data-theme=dark] .ai-chat-video-player{background:var(--bg-secondary,#3a3a3a)}.ai-chat-video-player__header{align-items:center;color:var(--action-accent,var(--primary-color,#3b82f6));display:flex;gap:8px}.ai-chat-video-player__title{color:var(--text-primary,#3e3e3e);font-size:14px;font-weight:600}.ai-chat-widget.dark .ai-chat-video-player__title,.chakra-ui-dark .ai-chat-video-player__title,.dark .ai-chat-video-player__title,[data-theme=dark] .ai-chat-video-player__title{color:#fff}.ai-chat-video-player__container{aspect-ratio:16/9;background:#000;border-radius:8px;overflow:hidden;position:relative;width:100%}.ai-chat-video-player__thumbnail{cursor:pointer;height:100%;position:relative;width:100%}.ai-chat-video-player__thumbnail img{height:100%;object-fit:cover;width:100%}.ai-chat-video-player__placeholder{align-items:center;background:linear-gradient(135deg,#1a1a2e,#16213e);cursor:pointer;display:flex;flex-direction:column;gap:8px;height:100%;justify-content:center;position:relative;width:100%}.ai-chat-video-player__click-text{color:hsla(0,0%,100%,.7);font-size:13px}.ai-chat-video-player__play-btn{align-items:center;background:rgba(0,0,0,.7);border:none;border-radius:50%;color:#fff;cursor:pointer;display:flex;height:64px;justify-content:center;left:50%;position:absolute;top:50%;transform:translate(-50%,-50%);transition:background .2s,transform .2s;width:64px}.ai-chat-video-player__placeholder .ai-chat-video-player__play-btn{left:auto;position:relative;top:auto;transform:none}.ai-chat-video-player__play-btn:hover{background:rgba(0,0,0,.9);transform:translate(-50%,-50%) scale(1.05)}.ai-chat-video-player__placeholder .ai-chat-video-player__play-btn:hover{transform:scale(1.05)}.ai-chat-video-player__provider-badge{background:rgba(0,0,0,.8);border-radius:4px;bottom:8px;color:#fff;font-size:11px;font-weight:600;letter-spacing:.5px;padding:4px 8px;position:absolute;right:8px;text-transform:uppercase}.ai-chat-video-player__iframe,.ai-chat-video-player__video{border:none;height:100%;left:0;position:absolute;top:0;width:100%}.ai-chat-video-player__error{align-items:center;background:rgba(239,68,68,.1);color:#dc2626;display:flex;font-size:13px;height:100%;justify-content:center;padding:16px;text-align:center;width:100%}.ai-chat-widget.dark .ai-chat-video-player__error,.chakra-ui-dark .ai-chat-video-player__error,.dark .ai-chat-video-player__error,[data-theme=dark] .ai-chat-video-player__error{background:rgba(239,68,68,.2);color:#fca5a5}.ai-chat-video-player__context{color:var(--text-muted,#71717a);font-size:12px;font-style:italic;margin-top:4px;padding:8px 12px 12px}.ai-chat-location-card{background:var(--bg-secondary,#fff);border:1px solid var(--border-subtle,rgba(0,0,0,.08));border-radius:12px;overflow:hidden;padding:0}.ai-chat-widget.dark .ai-chat-location-card,.chakra-ui-dark .ai-chat-location-card,.dark .ai-chat-location-card,[data-theme=dark] .ai-chat-location-card{background:hsla(0,0%,100%,.05);border-color:hsla(0,0%,100%,.1)}.ai-chat-location-card--compact{border-radius:10px}.ai-chat-location-card--error{color:var(--text-muted,#71717a);padding:16px;text-align:center}.ai-chat-location-card__map{background:var(--bg-muted,#f4f4f5);position:relative;width:100%}.ai-chat-widget.dark .ai-chat-location-card__map,.chakra-ui-dark .ai-chat-location-card__map,.dark .ai-chat-location-card__map,[data-theme=dark] .ai-chat-location-card__map{background:rgba(0,0,0,.2)}.ai-chat-location-card__map iframe{border:none;display:block;height:100%;width:100%}.ai-chat-location-card__content{padding:12px}.ai-chat-location-card--compact .ai-chat-location-card__content{padding:10px}.ai-chat-location-card__header{align-items:center;display:flex;flex-wrap:wrap;gap:8px;margin-bottom:8px}.ai-chat-location-card__name{color:var(--text-primary,#18181b);flex:1;font-size:16px;font-weight:600;margin:0;min-width:0}.ai-chat-widget.dark .ai-chat-location-card__name,.chakra-ui-dark .ai-chat-location-card__name,.dark .ai-chat-location-card__name,[data-theme=dark] .ai-chat-location-card__name{color:#fff}.ai-chat-location-card--compact .ai-chat-location-card__name{font-size:14px}.ai-chat-location-card__type{background:var(--bg-muted,#f4f4f5);border-radius:10px;color:var(--text-muted,#71717a);font-size:11px;font-weight:500;letter-spacing:.5px;padding:2px 8px;text-transform:uppercase}.ai-chat-widget.dark .ai-chat-location-card__type,.chakra-ui-dark .ai-chat-location-card__type,.dark .ai-chat-location-card__type,[data-theme=dark] .ai-chat-location-card__type{background:hsla(0,0%,100%,.1);color:hsla(0,0%,100%,.7)}.ai-chat-location-card__status{border-radius:12px;font-size:12px;font-weight:500;padding:2px 8px}.ai-chat-location-card__status--open{background:#dcfce7;color:#16a34a}.ai-chat-location-card__status--closed{background:#fef2f2;color:#dc2626}.ai-chat-widget.dark .ai-chat-location-card__status--open,.chakra-ui-dark .ai-chat-location-card__status--open,.dark .ai-chat-location-card__status--open,[data-theme=dark] .ai-chat-location-card__status--open{background:rgba(34,197,94,.2);color:#4ade80}.ai-chat-widget.dark .ai-chat-location-card__status--closed,.chakra-ui-dark .ai-chat-location-card__status--closed,.dark .ai-chat-location-card__status--closed,[data-theme=dark] .ai-chat-location-card__status--closed{background:rgba(239,68,68,.2);color:#fca5a5}.ai-chat-location-card__address{align-items:flex-start;color:var(--text-muted,#71717a);display:flex;font-size:13px;gap:6px;line-height:1.4;margin:0 0 8px}.ai-chat-location-card__address svg{flex-shrink:0;margin-top:2px}.ai-chat-location-card__description{color:var(--text-muted,#71717a);font-size:13px;line-height:1.4;margin:0 0 8px}.ai-chat-location-card__hours{align-items:flex-start;color:var(--text-muted,#71717a);display:flex;font-size:13px;gap:6px;margin-bottom:8px}.ai-chat-location-card__hours svg{flex-shrink:0;margin-top:2px}.ai-chat-location-card__hours-list{flex:1}.ai-chat-location-card__hours-toggle{align-items:center;background:none;border:none;color:inherit;cursor:pointer;display:flex;font:inherit;gap:4px;padding:0}.ai-chat-location-card__hours-toggle:hover{text-decoration:underline}.ai-chat-location-card__hours-full{list-style:none;margin:8px 0 0;padding:0}.ai-chat-location-card__hours-full li{display:flex;font-size:12px;justify-content:space-between;padding:4px 0}.ai-chat-location-card__hours-today{color:var(--text-primary,#18181b);font-weight:600}.ai-chat-widget.dark .ai-chat-location-card__hours-today,.chakra-ui-dark .ai-chat-location-card__hours-today,.dark .ai-chat-location-card__hours-today,[data-theme=dark] .ai-chat-location-card__hours-today{color:#fff}.ai-chat-location-card__phone{align-items:center;background:none;border:none;color:var(--action-accent,#3b82f6);cursor:pointer;display:flex;font-size:13px;gap:6px;margin-bottom:12px;padding:0}.ai-chat-location-card__phone:hover{text-decoration:underline}.ai-chat-location-card__actions{display:flex;gap:8px;justify-content:flex-start;width:100%}.ai-chat-location-card__button{align-items:center;background:var(--action-accent,#3b82f6);border:none;border-radius:20px;color:#fff;cursor:pointer;display:flex;flex:1;font-size:13px;font-weight:500;gap:6px;justify-content:center;padding:10px 16px;transition:opacity .2s}.ai-chat-location-card__button:hover{opacity:.9}.ai-chat-location-card--compact .ai-chat-location-card__button{font-size:12px;padding:8px 12px}.ai-chat-location-card__link{align-items:center;background:var(--bg-secondary,#fff);border:1px solid var(--border-subtle,rgba(0,0,0,.08));border-radius:20px;color:var(--text-primary,#18181b);display:flex;font-size:13px;gap:6px;padding:10px 16px;text-decoration:none;transition:border-color .2s}.ai-chat-widget.dark .ai-chat-location-card__link,.chakra-ui-dark .ai-chat-location-card__link,.dark .ai-chat-location-card__link,[data-theme=dark] .ai-chat-location-card__link{background:hsla(0,0%,100%,.05);border-color:hsla(0,0%,100%,.1);color:#fff}.ai-chat-location-card__link:hover{border-color:var(--action-accent,#3b82f6)}.ai-chat-location-card-list{display:flex;flex-direction:column;gap:8px}.ai-chat-location-card-list__header{align-items:center;color:var(--text-muted,#71717a);display:flex;font-size:13px;font-weight:500;gap:6px;margin-bottom:4px;padding:0 4px}.ai-chat-location-card-list__stack{display:grid;gap:12px;grid-template-columns:1fr}.ai-chat-location-card-list__stack--cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.ai-chat-location-card-list__stack--cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}@media (max-width:1000px){.ai-chat-location-card-list__stack--cols-3{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (max-width:640px){.ai-chat-location-card-list__stack--cols-2,.ai-chat-location-card-list__stack--cols-3{grid-template-columns:1fr}}.ai-chat-location-card-list__grid{display:grid;gap:8px;grid-template-columns:repeat(2,1fr)}@media (max-width:400px){.ai-chat-location-card-list__grid{grid-template-columns:1fr}}.ai-chat-location-card-list__carousel{-webkit-overflow-scrolling:touch;-ms-overflow-style:none;display:flex;gap:8px;overflow-x:auto;padding-bottom:4px;scroll-snap-type:x mandatory;scrollbar-width:none}.ai-chat-location-card-list__carousel::-webkit-scrollbar{display:none}.ai-chat-location-card-list__carousel>.ai-chat-location-card{flex:0 0 280px;scroll-snap-align:start}.ai-chat-contact-card{background:#fff;border:1px solid rgba(0,0,0,.08);border-radius:16px;overflow:hidden;padding:0;position:relative}.ai-chat-widget.dark .ai-chat-contact-card,.ai-chat-widget[data-theme=dark] .ai-chat-contact-card,.chakra-ui-dark .ai-chat-contact-card,.dark .ai-chat-contact-card,[data-theme=dark] .ai-chat-contact-card,html.dark .ai-chat-contact-card{background:#4a4a4a;border-color:hsla(0,0%,100%,.08)}.ai-chat-contact-card-list{gap:12px;width:100%}.ai-chat-contact-card--compact{border-radius:12px}.ai-chat-contact-card--empty{align-items:center;background:var(--bg-secondary,#f4f4f5);display:flex;flex-direction:column;gap:8px;justify-content:center;padding:24px 16px;text-align:center}.ai-chat-widget.dark .ai-chat-contact-card--empty,.chakra-ui-dark .ai-chat-contact-card--empty,.dark .ai-chat-contact-card--empty,[data-theme=dark] .ai-chat-contact-card--empty{background:#3a3a3a}.ai-chat-contact-card__empty-icon{color:var(--text-muted,#71717a);opacity:.6}.ai-chat-contact-card__empty-text{color:var(--text-muted,#71717a);font-size:14px;margin:0}.ai-chat-contact-card--vertical{display:flex;flex-direction:column}.ai-chat-contact-card--vertical .ai-chat-contact-card__image-section{aspect-ratio:3/2;overflow:hidden;width:100%}.ai-chat-contact-card--vertical .ai-chat-contact-card__image{height:100%;object-fit:cover;width:100%}.ai-chat-contact-card--vertical .ai-chat-contact-card__image-placeholder{align-items:center;background:linear-gradient(135deg,#5a5a5a,#3a3a3a);color:hsla(0,0%,100%,.5);display:flex;height:100%;justify-content:center;width:100%}.ai-chat-contact-card--vertical .ai-chat-contact-card__image-placeholder svg{height:48px;width:48px}.ai-chat-contact-card--vertical .ai-chat-contact-card__info{padding:16px;text-align:center}.ai-chat-contact-card--horizontal{display:flex;flex-direction:row}.ai-chat-contact-card--horizontal .ai-chat-contact-card__image-section{height:160px;min-width:140px;overflow:hidden;width:140px}.ai-chat-contact-card--horizontal.ai-chat-contact-card--compact .ai-chat-contact-card__image-section{height:120px;min-width:100px;width:100px}.ai-chat-contact-card--horizontal .ai-chat-contact-card__image{height:100%;object-fit:cover;width:100%}.ai-chat-contact-card--horizontal .ai-chat-contact-card__image-placeholder{align-items:center;background:linear-gradient(135deg,#5a5a5a,#3a3a3a);color:hsla(0,0%,100%,.5);display:flex;height:100%;justify-content:center;width:100%}.ai-chat-contact-card--horizontal .ai-chat-contact-card__image-placeholder svg{height:36px;width:36px}.ai-chat-contact-card--horizontal .ai-chat-contact-card__info{display:flex;flex:1;flex-direction:column;justify-content:center;padding:16px}.ai-chat-contact-card__name{color:var(--action-accent,#ef4444);font-size:18px;font-weight:600;line-height:1.3;margin:0}.ai-chat-contact-card--compact .ai-chat-contact-card__name{font-size:15px}.ai-chat-contact-card__role{color:rgba(0,0,0,.7);font-size:14px;font-weight:400;margin:2px 0 0}.ai-chat-widget.dark .ai-chat-contact-card__role,.ai-chat-widget[data-theme=dark] .ai-chat-contact-card__role,.chakra-ui-dark .ai-chat-contact-card__role,.dark .ai-chat-contact-card__role,[data-theme=dark] .ai-chat-contact-card__role,html.dark .ai-chat-contact-card__role{color:hsla(0,0%,100%,.9)}.ai-chat-contact-card--compact .ai-chat-contact-card__role{font-size:13px}.ai-chat-contact-card__details{display:flex;flex-direction:column;gap:2px;margin-top:12px}.ai-chat-contact-card__detail{color:rgba(0,0,0,.6);display:block;font-size:14px;line-height:1.5;margin:0;text-decoration:none}.ai-chat-contact-card__detail:hover{color:#000;text-decoration:underline}.ai-chat-widget.dark .ai-chat-contact-card__detail,.ai-chat-widget[data-theme=dark] .ai-chat-contact-card__detail,.chakra-ui-dark .ai-chat-contact-card__detail,.dark .ai-chat-contact-card__detail,[data-theme=dark] .ai-chat-contact-card__detail,html.dark .ai-chat-contact-card__detail{color:hsla(0,0%,100%,.7)}.ai-chat-widget.dark .ai-chat-contact-card__detail:hover,.ai-chat-widget[data-theme=dark] .ai-chat-contact-card__detail:hover,.chakra-ui-dark .ai-chat-contact-card__detail:hover,.dark .ai-chat-contact-card__detail:hover,[data-theme=dark] .ai-chat-contact-card__detail:hover,html.dark .ai-chat-contact-card__detail:hover{color:#fff}.ai-chat-contact-card--compact .ai-chat-contact-card__detail{font-size:13px}.ai-chat-contact-card__responsibilities{display:flex;flex-wrap:wrap;gap:4px;margin-top:8px}.ai-chat-contact-card__responsibility-tag{background:rgba(0,0,0,.08);border-radius:10px;color:rgba(0,0,0,.8);font-size:11px;font-weight:500;padding:3px 10px}.ai-chat-widget.dark .ai-chat-contact-card__responsibility-tag,.ai-chat-widget[data-theme=dark] .ai-chat-contact-card__responsibility-tag,.chakra-ui-dark .ai-chat-contact-card__responsibility-tag,.dark .ai-chat-contact-card__responsibility-tag,[data-theme=dark] .ai-chat-contact-card__responsibility-tag,html.dark .ai-chat-contact-card__responsibility-tag{background:hsla(0,0%,100%,.15);color:hsla(0,0%,100%,.9)}.ai-chat-contact-card__responsibility-more{color:rgba(0,0,0,.5);font-size:11px;padding:3px 4px}.ai-chat-widget.dark .ai-chat-contact-card__responsibility-more,.ai-chat-widget[data-theme=dark] .ai-chat-contact-card__responsibility-more,.chakra-ui-dark .ai-chat-contact-card__responsibility-more,.dark .ai-chat-contact-card__responsibility-more,[data-theme=dark] .ai-chat-contact-card__responsibility-more,html.dark .ai-chat-contact-card__responsibility-more{color:hsla(0,0%,100%,.5)}.ai-chat-contact-card__actions{display:flex;gap:8px;padding:0 12px 12px}.ai-chat-contact-card--compact .ai-chat-contact-card__actions{gap:6px;padding:0 10px 10px}.ai-chat-contact-card__button{align-items:center;border:none;border-radius:9999px;cursor:pointer;display:flex;font-size:14px;font-weight:600;gap:8px;justify-content:center;padding:12px 20px;transition:all .15s ease;white-space:nowrap}.ai-chat-contact-card--compact .ai-chat-contact-card__button{font-size:13px;padding:10px 16px}.ai-chat-contact-card__button:hover{box-shadow:0 4px 12px rgba(0,0,0,.15);transform:translateY(-1px)}.ai-chat-contact-card__button:active{transform:translateY(0)}.ai-chat-contact-card__button--primary{background:var(--action-accent,#3b82f6);color:#fff;flex:1}.ai-chat-contact-card__button--primary:hover{background:color-mix(in srgb,var(--action-accent,#3b82f6) 90%,#000)}.ai-chat-contact-card__button--secondary{background:var(--bg-muted,#f4f4f5);border:1px solid var(--border-subtle,rgba(0,0,0,.08));color:var(--text-primary,#18181b);flex:1}.ai-chat-contact-card__button--secondary:hover{background:var(--bg-hover,#e4e4e7)}.ai-chat-widget.dark .ai-chat-contact-card__button--secondary,.chakra-ui-dark .ai-chat-contact-card__button--secondary,.dark .ai-chat-contact-card__button--secondary,[data-theme=dark] .ai-chat-contact-card__button--secondary{background:hsla(0,0%,100%,.1);border-color:hsla(0,0%,100%,.15);color:#fff}.ai-chat-widget.dark .ai-chat-contact-card__button--secondary:hover,.chakra-ui-dark .ai-chat-contact-card__button--secondary:hover,.dark .ai-chat-contact-card__button--secondary:hover,[data-theme=dark] .ai-chat-contact-card__button--secondary:hover{background:hsla(0,0%,100%,.15)}.ai-chat-contact-card-list{display:flex;flex-direction:column;gap:8px}.ai-chat-contact-card-list__header{align-items:center;color:var(--text-muted,#71717a);display:flex;font-size:13px;font-weight:500;gap:6px;margin-bottom:2px;margin-top:8px;padding:0 4px}.ai-chat-contact-card-list__stack{display:grid;gap:12px;grid-template-columns:repeat(3,minmax(0,1fr))}@media (max-width:900px){.ai-chat-contact-card-list__stack{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (max-width:600px){.ai-chat-contact-card-list__stack{grid-template-columns:1fr}}.ai-chat-contact-card-list__stack--widget{grid-template-columns:1fr}@container (min-width: 380px){.ai-chat-contact-card-list__stack--widget{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (max-width:520px){.ai-chat-contact-card-list__stack{grid-template-columns:1fr!important}.ai-chat-contact-card--horizontal{flex-direction:column}.ai-chat-contact-card--horizontal .ai-chat-contact-card__image-section{aspect-ratio:3/2;height:auto;min-width:100%;width:100%}}.ai-chat-contact-card__initials{align-items:center;display:flex;font-size:48px;font-weight:600;height:100%;justify-content:center;letter-spacing:.05em;text-transform:uppercase;width:100%}.ai-chat-contact-card--compact .ai-chat-contact-card__initials{font-size:32px}.ai-chat-contact-card--horizontal .ai-chat-contact-card__initials{font-size:28px}.ai-chat-contact-card--horizontal.ai-chat-contact-card--compact .ai-chat-contact-card__initials{font-size:22px}.ai-chat-form-card{--action-accent:var(--primary-color,#3b82f6);background:var(--bg-secondary,#f9fafb);border:1px solid var(--border-subtle,rgba(0,0,0,.06));border-radius:12px;box-sizing:border-box;margin:6px 0;overflow:hidden;padding:16px;width:100%}.ai-chat-widget.dark .ai-chat-form-card,.chakra-ui-dark .ai-chat-form-card,.dark .ai-chat-form-card,[data-theme=dark] .ai-chat-form-card{background:hsla(0,0%,100%,.05);border-color:hsla(0,0%,100%,.1)}.ai-chat-form-card--empty,.ai-chat-form-card--error,.ai-chat-form-card--skipped,.ai-chat-form-card--submitted{padding:12px 16px}.ai-chat-form-card__header{align-items:center;display:flex;gap:8px;margin-bottom:12px}.ai-chat-form-card__icon{font-size:18px}.ai-chat-form-card__title{color:var(--text-primary,#3e3e3e);font-size:15px;font-weight:600}.ai-chat-widget.dark .ai-chat-form-card__title,.chakra-ui-dark .ai-chat-form-card__title,.dark .ai-chat-form-card__title,[data-theme=dark] .ai-chat-form-card__title{color:#fff}.ai-chat-form-card__description{color:var(--text-muted,#71717a);font-size:13px;line-height:1.4;margin:0 0 12px}.ai-chat-form-card__context{color:var(--text-muted,#71717a);font-size:12px;font-style:italic;margin:0 0 12px}.ai-chat-form-card__error{color:#dc2626;font-size:13px;margin:0}.ai-chat-widget.dark .ai-chat-form-card__error,.chakra-ui-dark .ai-chat-form-card__error,.dark .ai-chat-form-card__error,[data-theme=dark] .ai-chat-form-card__error{color:#fca5a5}.ai-chat-form-card__success{color:#16a34a;font-size:13px;margin:0}.ai-chat-widget.dark .ai-chat-form-card__success,.chakra-ui-dark .ai-chat-form-card__success,.dark .ai-chat-form-card__success,[data-theme=dark] .ai-chat-form-card__success{color:#4ade80}.ai-chat-form-card__empty-text,.ai-chat-form-card__skipped-text{color:var(--text-muted,#71717a);font-size:13px;margin:0}.ai-chat-form-card__progress{align-items:center;display:flex;gap:12px;margin-bottom:16px}.ai-chat-form-card__progress-bar{background:var(--action-accent,var(--primary-color,#3b82f6));border-radius:2px;flex:1;height:4px;transition:width .3s ease}.ai-chat-form-card__progress-text{color:var(--text-muted,#71717a);font-size:12px;white-space:nowrap}.ai-chat-form-card__question{margin-bottom:16px}.ai-chat-form-card__question-text{color:var(--text-primary,#3e3e3e);font-size:14px;font-weight:500;line-height:1.4;margin:0 0 12px}.ai-chat-widget.dark .ai-chat-form-card__question-text,.chakra-ui-dark .ai-chat-form-card__question-text,.dark .ai-chat-form-card__question-text,[data-theme=dark] .ai-chat-form-card__question-text{color:#fff}.ai-chat-form-card__required{color:#dc2626;margin-left:2px}.ai-chat-form-card__answer{margin-top:8px}.ai-chat-form-card__textarea{background:var(--bg-primary,#fff);border:1px solid var(--border-default,#e5e7eb);border-radius:var(--radius-md,8px);box-sizing:border-box;color:var(--text-primary,#3e3e3e);font-family:inherit;font-size:14px;min-height:80px;outline:none;padding:10px 12px;resize:vertical;transition:border-color .2s ease,box-shadow .2s ease;width:100%}.ai-chat-form-card__textarea:focus{border-color:var(--action-accent,var(--primary-color,#3b82f6));box-shadow:0 0 0 3px rgba(59,130,246,.1)}.ai-chat-form-card__textarea::placeholder{color:var(--text-muted,#9ca3af)}.ai-chat-widget.dark .ai-chat-form-card__textarea,.chakra-ui-dark .ai-chat-form-card__textarea,.dark .ai-chat-form-card__textarea,[data-theme=dark] .ai-chat-form-card__textarea{background:rgba(0,0,0,.2);border-color:hsla(0,0%,100%,.1);color:#fff}.ai-chat-widget.dark .ai-chat-form-card__textarea:focus,.chakra-ui-dark .ai-chat-form-card__textarea:focus,.dark .ai-chat-form-card__textarea:focus,[data-theme=dark] .ai-chat-form-card__textarea:focus{border-color:var(--action-accent,var(--primary-color,#3b82f6));box-shadow:0 0 0 3px rgba(59,130,246,.2)}.ai-chat-form-card__options{display:flex;flex-direction:column;gap:8px}.ai-chat-form-card__option{align-items:center;background:var(--bg-primary,#fff);border:1px solid var(--border-default,#e5e7eb);border-radius:var(--radius-md,8px);cursor:pointer;display:flex;gap:10px;padding:10px 12px;transition:border-color .15s,background .15s}.ai-chat-form-card__option:hover{background:rgba(59,130,246,.05);border-color:var(--action-accent,var(--primary-color,#3b82f6))}.ai-chat-widget.dark .ai-chat-form-card__option,.chakra-ui-dark .ai-chat-form-card__option,.dark .ai-chat-form-card__option,[data-theme=dark] .ai-chat-form-card__option{background:rgba(0,0,0,.2);border-color:hsla(0,0%,100%,.1)}.ai-chat-widget.dark .ai-chat-form-card__option:hover,.chakra-ui-dark .ai-chat-form-card__option:hover,.dark .ai-chat-form-card__option:hover,[data-theme=dark] .ai-chat-form-card__option:hover{background:rgba(59,130,246,.15);border-color:var(--action-accent,var(--primary-color,#3b82f6))}.ai-chat-form-card__option input{accent-color:var(--action-accent,var(--primary-color,#3b82f6));margin:0}.ai-chat-form-card__option-text{color:var(--text-primary,#3e3e3e);font-size:14px}.ai-chat-widget.dark .ai-chat-form-card__option-text,.chakra-ui-dark .ai-chat-form-card__option-text,.dark .ai-chat-form-card__option-text,[data-theme=dark] .ai-chat-form-card__option-text{color:#fff}.ai-chat-form-card__rating{display:flex;flex-wrap:wrap;gap:8px}.ai-chat-form-card__rating-btn{align-items:center;background:var(--bg-primary,#fff);border:1px solid var(--border-default,#e5e7eb);border-radius:var(--radius-md,8px);color:var(--text-primary,#3e3e3e);cursor:pointer;display:flex;font-size:14px;font-weight:500;height:40px;justify-content:center;transition:all .15s ease;width:40px}.ai-chat-form-card__rating-btn--selected,.ai-chat-form-card__rating-btn:hover{border-color:var(--action-accent,var(--primary-color,#3b82f6))}.ai-chat-form-card__rating-btn--selected{background:var(--action-accent,var(--primary-color,#3b82f6));color:#fff}.ai-chat-widget.dark .ai-chat-form-card__rating-btn,.chakra-ui-dark .ai-chat-form-card__rating-btn,.dark .ai-chat-form-card__rating-btn,[data-theme=dark] .ai-chat-form-card__rating-btn{background:rgba(0,0,0,.2);border-color:hsla(0,0%,100%,.1);color:#fff}.ai-chat-widget.dark .ai-chat-form-card__rating-btn:hover,.chakra-ui-dark .ai-chat-form-card__rating-btn:hover,.dark .ai-chat-form-card__rating-btn:hover,[data-theme=dark] .ai-chat-form-card__rating-btn:hover{border-color:var(--action-accent,var(--primary-color,#3b82f6))}.ai-chat-widget.dark .ai-chat-form-card__rating-btn--selected,.chakra-ui-dark .ai-chat-form-card__rating-btn--selected,.dark .ai-chat-form-card__rating-btn--selected,[data-theme=dark] .ai-chat-form-card__rating-btn--selected{background:var(--action-accent,var(--primary-color,#3b82f6));border-color:var(--action-accent,var(--primary-color,#3b82f6));color:#fff}.ai-chat-form-card__actions{align-items:center;border-top:1px solid var(--border-subtle,rgba(0,0,0,.08));display:flex;gap:8px;margin-top:16px;padding-top:16px}.ai-chat-widget.dark .ai-chat-form-card__actions,.chakra-ui-dark .ai-chat-form-card__actions,.dark .ai-chat-form-card__actions,[data-theme=dark] .ai-chat-form-card__actions{border-color:hsla(0,0%,100%,.08)}.ai-chat-form-card__actions-spacer{flex:1}.ai-chat-form-card__btn{border:none;border-radius:9999px;cursor:pointer;font-family:inherit;font-size:13px;font-weight:500;padding:8px 16px;transition:all .2s ease}.ai-chat-form-card__btn:disabled{cursor:not-allowed;opacity:.5}.ai-chat-form-card__btn--primary{background:var(--action-accent,var(--primary-color,#3b82f6));color:#fff}.ai-chat-form-card__btn--primary:hover:not(:disabled){opacity:.9;transform:translateY(-1px)}.ai-chat-form-card__btn--secondary{background:var(--bg-primary,#fff);border:1px solid var(--border-default,#e5e7eb);color:var(--text-primary,#3e3e3e)}.ai-chat-form-card__btn--secondary:hover:not(:disabled){border-color:var(--action-accent,var(--primary-color,#3b82f6))}.ai-chat-widget.dark .ai-chat-form-card__btn--secondary,.chakra-ui-dark .ai-chat-form-card__btn--secondary,.dark .ai-chat-form-card__btn--secondary,[data-theme=dark] .ai-chat-form-card__btn--secondary{background:hsla(0,0%,100%,.05);border-color:hsla(0,0%,100%,.1);color:#fff}.ai-chat-form-card__btn--ghost{background:transparent;color:var(--text-muted,#71717a)}.ai-chat-form-card__btn--ghost:hover:not(:disabled){background:rgba(0,0,0,.05);color:var(--text-primary,#3e3e3e)}.ai-chat-widget.dark .ai-chat-form-card__btn--ghost:hover:not(:disabled),.chakra-ui-dark .ai-chat-form-card__btn--ghost:hover:not(:disabled),.dark .ai-chat-form-card__btn--ghost:hover:not(:disabled),[data-theme=dark] .ai-chat-form-card__btn--ghost:hover:not(:disabled){background:hsla(0,0%,100%,.05);color:#fff}.ai-chat-booking-card{--action-accent:var(--primary-color,#3b82f6);background:var(--bg-secondary,#f9fafb);border:1px solid var(--border-subtle,rgba(0,0,0,.06));border-radius:12px;box-sizing:border-box;margin:6px 0;padding:16px;width:100%}.ai-chat-widget.dark .ai-chat-booking-card,.chakra-ui-dark .ai-chat-booking-card,.dark .ai-chat-booking-card,[data-theme=dark] .ai-chat-booking-card{background:hsla(0,0%,100%,.05);border-color:hsla(0,0%,100%,.1)}.ai-chat-booking-card__header{align-items:center;display:flex;gap:8px;margin-bottom:12px}.ai-chat-booking-card__icon{flex-shrink:0;font-size:18px}.ai-chat-booking-card__title{color:var(--text-primary,#3e3e3e);font-size:15px;font-weight:600}.ai-chat-widget.dark .ai-chat-booking-card__title,.chakra-ui-dark .ai-chat-booking-card__title,.dark .ai-chat-booking-card__title,[data-theme=dark] .ai-chat-booking-card__title{color:#fff}.ai-chat-booking-card__content{display:flex;flex-direction:column;gap:12px}.ai-chat-booking-card__description{color:var(--text-muted,#71717a);font-size:13px;line-height:1.4;margin:0}.ai-chat-booking-card__empty{color:var(--text-muted,#71717a);font-size:13px;padding:16px;text-align:center}.ai-chat-booking-card__input{background:var(--bg-primary,#fff);border:1px solid var(--border-default,#e5e7eb);border-radius:8px;box-sizing:border-box;color:var(--text-primary,#3e3e3e);font-family:inherit;font-size:14px;outline:none;padding:10px 12px;transition:border-color .2s ease,box-shadow .2s ease;width:100%}.ai-chat-booking-card__input:focus{border-color:var(--action-accent,var(--primary-color,#3b82f6));box-shadow:0 0 0 3px rgba(59,130,246,.1)}.ai-chat-booking-card__input::placeholder{color:var(--text-muted,#9ca3af)}.ai-chat-widget.dark .ai-chat-booking-card__input,.chakra-ui-dark .ai-chat-booking-card__input,.dark .ai-chat-booking-card__input,[data-theme=dark] .ai-chat-booking-card__input{background:rgba(0,0,0,.2);border-color:hsla(0,0%,100%,.1);color:#fff}.ai-chat-widget.dark .ai-chat-booking-card__input:focus,.chakra-ui-dark .ai-chat-booking-card__input:focus,.dark .ai-chat-booking-card__input:focus,[data-theme=dark] .ai-chat-booking-card__input:focus{border-color:var(--action-accent,var(--primary-color,#3b82f6));box-shadow:0 0 0 3px rgba(59,130,246,.2)}.ai-chat-booking-card__label{color:var(--text-secondary,#6b7280);display:block;font-size:13px;font-weight:500;margin-bottom:4px}.ai-chat-widget.dark .ai-chat-booking-card__label,.chakra-ui-dark .ai-chat-booking-card__label,.dark .ai-chat-booking-card__label,[data-theme=dark] .ai-chat-booking-card__label{color:var(--text-secondary,#a1a1aa)}.ai-chat-booking-card__btn{border:none;border-radius:9999px;box-sizing:border-box;cursor:pointer;font-family:inherit;font-size:13px;font-weight:500;padding:10px 16px;transition:all .2s ease;width:100%}.ai-chat-booking-card__btn:disabled{cursor:not-allowed;opacity:.5}.ai-chat-booking-card__btn--primary{background:var(--action-accent,var(--primary-color,#3b82f6));color:#fff}.ai-chat-booking-card__btn--primary:hover:not(:disabled){opacity:.9;transform:translateY(-1px)}.ai-chat-booking-card__btn--secondary{background:var(--bg-primary,#fff);border:1px solid var(--border-default,#e5e7eb);color:var(--text-primary,#3e3e3e)}.ai-chat-booking-card__btn--secondary:hover:not(:disabled){border-color:var(--action-accent,var(--primary-color,#3b82f6))}.ai-chat-widget.dark .ai-chat-booking-card__btn--secondary,.chakra-ui-dark .ai-chat-booking-card__btn--secondary,.dark .ai-chat-booking-card__btn--secondary,[data-theme=dark] .ai-chat-booking-card__btn--secondary{background:hsla(0,0%,100%,.05);border-color:hsla(0,0%,100%,.1);color:#fff}.ai-chat-booking-card__btn--danger{background:rgba(220,38,38,.1);border:1px solid rgba(220,38,38,.2);color:#dc2626}.ai-chat-booking-card__btn--danger:hover:not(:disabled){background:rgba(220,38,38,.15)}.ai-chat-widget.dark .ai-chat-booking-card__btn--danger,.chakra-ui-dark .ai-chat-booking-card__btn--danger,.dark .ai-chat-booking-card__btn--danger,[data-theme=dark] .ai-chat-booking-card__btn--danger{background:rgba(239,68,68,.2);border-color:rgba(239,68,68,.3);color:#fca5a5}.ai-chat-booking-card__grid{display:grid;gap:10px;grid-template-columns:repeat(auto-fill,minmax(200px,1fr))}.ai-chat-booking-card__contact{background:var(--bg-primary,#fff);border:1px solid var(--border-default,#e5e7eb);border-radius:8px;cursor:pointer;display:flex;flex-direction:column;font-family:inherit;padding:12px;text-align:left;transition:all .2s ease}.ai-chat-booking-card__contact:hover{background:rgba(59,130,246,.05);border-color:var(--action-accent,var(--primary-color,#3b82f6))}.ai-chat-booking-card__contact--selected{border-width:2px}.ai-chat-widget.dark .ai-chat-booking-card__contact,.chakra-ui-dark .ai-chat-booking-card__contact,.dark .ai-chat-booking-card__contact,[data-theme=dark] .ai-chat-booking-card__contact{background:rgba(0,0,0,.2);border-color:hsla(0,0%,100%,.1)}.ai-chat-widget.dark .ai-chat-booking-card__contact--selected,.chakra-ui-dark .ai-chat-booking-card__contact--selected,.dark .ai-chat-booking-card__contact--selected,[data-theme=dark] .ai-chat-booking-card__contact--selected{background:rgba(59,130,246,.15)}.ai-chat-booking-card__contact-name{color:var(--text-primary,#3e3e3e);font-size:14px;font-weight:500}.ai-chat-widget.dark .ai-chat-booking-card__contact-name,.chakra-ui-dark .ai-chat-booking-card__contact-name,.dark .ai-chat-booking-card__contact-name,[data-theme=dark] .ai-chat-booking-card__contact-name{color:#fff}.ai-chat-booking-card__contact-role{color:var(--text-muted,#71717a);font-size:12px;margin-top:2px}.ai-chat-booking-card__options{display:flex;flex-direction:column;gap:8px}.ai-chat-booking-card__option-btn{align-items:center;background:var(--bg-primary,#fff);border:1px solid var(--border-default,#e5e7eb);border-radius:8px;box-sizing:border-box;cursor:pointer;display:flex;font-family:inherit;gap:12px;padding:12px;text-align:left;transition:all .2s ease;width:100%}.ai-chat-booking-card__option-btn:hover{background:rgba(59,130,246,.05);border-color:var(--action-accent,var(--primary-color,#3b82f6))}.ai-chat-widget.dark .ai-chat-booking-card__option-btn,.chakra-ui-dark .ai-chat-booking-card__option-btn,.dark .ai-chat-booking-card__option-btn,[data-theme=dark] .ai-chat-booking-card__option-btn{background:rgba(0,0,0,.2);border-color:hsla(0,0%,100%,.1)}.ai-chat-booking-card__option-icon{flex-shrink:0;font-size:18px}.ai-chat-booking-card__option-text{color:var(--text-primary,#3e3e3e);font-size:14px;font-weight:500}.ai-chat-widget.dark .ai-chat-booking-card__option-text,.chakra-ui-dark .ai-chat-booking-card__option-text,.dark .ai-chat-booking-card__option-text,[data-theme=dark] .ai-chat-booking-card__option-text{color:#fff}.ai-chat-booking-card__date-group{margin-bottom:12px}.ai-chat-booking-card__date-header{color:var(--text-muted,#71717a);font-size:12px;font-weight:600;letter-spacing:.5px;margin-bottom:8px;text-transform:uppercase}.ai-chat-booking-card__slots{display:grid;gap:8px;grid-template-columns:repeat(auto-fill,minmax(120px,1fr))}.ai-chat-booking-card__slot{background:var(--bg-primary,#fff);border:1px solid var(--border-default,#e5e7eb);border-radius:8px;color:var(--text-primary,#3e3e3e);cursor:pointer;font-family:inherit;font-size:13px;font-weight:500;padding:10px 12px;transition:all .2s ease}.ai-chat-booking-card__slot:hover{background:rgba(59,130,246,.05);border-color:var(--action-accent,var(--primary-color,#3b82f6))}.ai-chat-booking-card__slot--selected{border-width:2px}.ai-chat-widget.dark .ai-chat-booking-card__slot,.chakra-ui-dark .ai-chat-booking-card__slot,.dark .ai-chat-booking-card__slot,[data-theme=dark] .ai-chat-booking-card__slot{background:rgba(0,0,0,.2);border-color:hsla(0,0%,100%,.1);color:#fff}.ai-chat-widget.dark .ai-chat-booking-card__slot--selected,.chakra-ui-dark .ai-chat-booking-card__slot--selected,.dark .ai-chat-booking-card__slot--selected,[data-theme=dark] .ai-chat-booking-card__slot--selected{background:rgba(59,130,246,.15)}.ai-chat-booking-card__appointments{display:flex;flex-direction:column;gap:10px}.ai-chat-booking-card__appointment{background:var(--bg-primary,#fff);border:1px solid var(--border-default,#e5e7eb);border-radius:8px;padding:12px}.ai-chat-widget.dark .ai-chat-booking-card__appointment,.chakra-ui-dark .ai-chat-booking-card__appointment,.dark .ai-chat-booking-card__appointment,[data-theme=dark] .ai-chat-booking-card__appointment{background:rgba(0,0,0,.2);border-color:hsla(0,0%,100%,.1)}.ai-chat-booking-card__appointment-header{align-items:center;display:flex;gap:8px;justify-content:space-between;margin-bottom:6px}.ai-chat-booking-card__appointment-subject{color:var(--text-primary,#3e3e3e);font-size:14px;font-weight:500}.ai-chat-widget.dark .ai-chat-booking-card__appointment-subject,.chakra-ui-dark .ai-chat-booking-card__appointment-subject,.dark .ai-chat-booking-card__appointment-subject,[data-theme=dark] .ai-chat-booking-card__appointment-subject{color:#fff}.ai-chat-booking-card__appointment-status{border-radius:4px;font-size:11px;font-weight:600;padding:2px 6px;text-transform:uppercase}.ai-chat-booking-card__appointment-status--confirmed,.ai-chat-booking-card__appointment-status--pending{background:rgba(34,197,94,.1);color:#16a34a}.ai-chat-widget.dark .ai-chat-booking-card__appointment-status--confirmed,.ai-chat-widget.dark .ai-chat-booking-card__appointment-status--pending,.chakra-ui-dark .ai-chat-booking-card__appointment-status--confirmed,.chakra-ui-dark .ai-chat-booking-card__appointment-status--pending,.dark .ai-chat-booking-card__appointment-status--confirmed,.dark .ai-chat-booking-card__appointment-status--pending,[data-theme=dark] .ai-chat-booking-card__appointment-status--confirmed,[data-theme=dark] .ai-chat-booking-card__appointment-status--pending{background:rgba(34,197,94,.2);color:#4ade80}.ai-chat-booking-card__appointment-status--cancelled{background:hsla(220,9%,46%,.1);color:#6b7280}.ai-chat-booking-card__appointment-time{color:var(--text-secondary,#6b7280);font-size:13px;margin-bottom:4px}.ai-chat-widget.dark .ai-chat-booking-card__appointment-time,.chakra-ui-dark .ai-chat-booking-card__appointment-time,.dark .ai-chat-booking-card__appointment-time,[data-theme=dark] .ai-chat-booking-card__appointment-time{color:var(--text-secondary,#a1a1aa)}.ai-chat-booking-card__appointment-contact{color:var(--text-muted,#71717a);font-size:12px}.ai-chat-booking-card__summary{background:var(--bg-primary,#fff);border:1px solid var(--border-default,#e5e7eb);border-radius:8px;margin-bottom:12px;padding:12px}.ai-chat-widget.dark .ai-chat-booking-card__summary,.chakra-ui-dark .ai-chat-booking-card__summary,.dark .ai-chat-booking-card__summary,[data-theme=dark] .ai-chat-booking-card__summary{background:rgba(0,0,0,.2);border-color:hsla(0,0%,100%,.1)}.ai-chat-booking-card__summary-row{align-items:center;display:flex;justify-content:space-between;padding:6px 0}.ai-chat-booking-card__summary-row:not(:last-child){border-bottom:1px solid var(--border-subtle,rgba(0,0,0,.06))}.ai-chat-widget.dark .ai-chat-booking-card__summary-row:not(:last-child),.chakra-ui-dark .ai-chat-booking-card__summary-row:not(:last-child),.dark .ai-chat-booking-card__summary-row:not(:last-child),[data-theme=dark] .ai-chat-booking-card__summary-row:not(:last-child){border-bottom-color:hsla(0,0%,100%,.08)}.ai-chat-booking-card__summary-label{color:var(--text-muted,#71717a);font-size:12px;font-weight:600;letter-spacing:.5px;text-transform:uppercase}.ai-chat-booking-card__summary-value{color:var(--text-primary,#3e3e3e);font-size:14px;font-weight:500;text-align:right}.ai-chat-widget.dark .ai-chat-booking-card__summary-value,.chakra-ui-dark .ai-chat-booking-card__summary-value,.dark .ai-chat-booking-card__summary-value,[data-theme=dark] .ai-chat-booking-card__summary-value{color:#fff}.ai-chat-booking-card__link{display:inline-block;font-size:13px;font-weight:500;margin-top:8px;text-decoration:none;transition:opacity .2s ease}.ai-chat-booking-card__link:hover{opacity:.8;text-decoration:underline}.ai-chat-booking-card--success{background:rgba(34,197,94,.05);border-color:rgba(34,197,94,.2)}.ai-chat-widget.dark .ai-chat-booking-card--success,.chakra-ui-dark .ai-chat-booking-card--success,.dark .ai-chat-booking-card--success,[data-theme=dark] .ai-chat-booking-card--success{background:rgba(34,197,94,.1);border-color:rgba(34,197,94,.3)}.ai-chat-booking-card--pending{background:rgba(234,179,8,.05);border-color:rgba(234,179,8,.2)}.ai-chat-widget.dark .ai-chat-booking-card--pending,.chakra-ui-dark .ai-chat-booking-card--pending,.dark .ai-chat-booking-card--pending,[data-theme=dark] .ai-chat-booking-card--pending{background:rgba(234,179,8,.1);border-color:rgba(234,179,8,.3)}.ai-chat-booking-card--cancelled{background:hsla(220,9%,46%,.05);border-color:hsla(220,9%,46%,.2)}.ai-chat-widget.dark .ai-chat-booking-card--cancelled,.chakra-ui-dark .ai-chat-booking-card--cancelled,.dark .ai-chat-booking-card--cancelled,[data-theme=dark] .ai-chat-booking-card--cancelled{background:hsla(220,9%,46%,.1);border-color:hsla(220,9%,46%,.3)}.ai-chat-booking-card--error{background:rgba(239,68,68,.05);border-color:rgba(239,68,68,.2)}.ai-chat-widget.dark .ai-chat-booking-card--error,.chakra-ui-dark .ai-chat-booking-card--error,.dark .ai-chat-booking-card--error,[data-theme=dark] .ai-chat-booking-card--error{background:rgba(239,68,68,.1);border-color:rgba(239,68,68,.3)}.ai-chat-booking-card__pending-text,.ai-chat-booking-card__success-text{color:var(--text-secondary,#6b7280);font-size:13px;margin:0}.ai-chat-widget.dark .ai-chat-booking-card__pending-text,.ai-chat-widget.dark .ai-chat-booking-card__success-text,.chakra-ui-dark .ai-chat-booking-card__pending-text,.chakra-ui-dark .ai-chat-booking-card__success-text,.dark .ai-chat-booking-card__pending-text,.dark .ai-chat-booking-card__success-text,[data-theme=dark] .ai-chat-booking-card__pending-text,[data-theme=dark] .ai-chat-booking-card__success-text{color:var(--text-secondary,#a1a1aa)}.ai-chat-booking-card__error{color:#dc2626;font-size:13px;margin:0}.ai-chat-widget.dark .ai-chat-booking-card__error,.chakra-ui-dark .ai-chat-booking-card__error,.dark .ai-chat-booking-card__error,[data-theme=dark] .ai-chat-booking-card__error{color:#fca5a5}.chat-fullpage{--fp-max-width:800px;--fp-padding-x:16px;--fp-padding-top-mobile:64px;--fp-padding-top-desktop:16px;--fp-padding-bottom:200px;--fp-input-bottom:0}.chat-fullpage .ai-chat-messages{background:transparent;height:100%;margin:0 auto;max-width:var(--fp-max-width);padding:var(--fp-padding-top-desktop) var(--fp-padding-x) var(--fp-padding-bottom)}@media (max-width:768px){.chat-fullpage .ai-chat-messages{padding-top:var(--fp-padding-top-mobile)}}.chat-fullpage .ai-chat-message{animation:none}.chat-fullpage .ai-chat-message.user{max-width:85%}.chat-fullpage .ai-chat-message.user .ai-chat-message-content{background:var(--bg-muted,#f4f4f5);border-radius:24px;color:#000;padding:8px 16px}.chat-fullpage.dark .ai-chat-message.user .ai-chat-message-content{background:var(--bg-muted,#27272a);color:#fff}.chat-fullpage .ai-chat-message.assistant{width:100%}.chat-fullpage .ai-chat-message.assistant .ai-chat-message-content{color:#000;padding:8px 16px}.chat-fullpage.dark .ai-chat-message.assistant .ai-chat-message-content{color:#fff}.chat-fullpage .ai-chat-message.tool{margin:0;padding:0;width:100%}.chat-fullpage .ai-chat-welcome{align-items:center;display:flex;flex-direction:column;gap:24px;justify-content:center;min-height:60vh;padding:24px;text-align:center}.chat-fullpage .ai-chat-welcome-title{font-size:32px;font-weight:600}.chat-fullpage .ai-chat-welcome-text{color:var(--text-muted,#71717a);font-size:18px;max-width:400px}.chat-fullpage .ai-chat-input-container{background:transparent;bottom:0;left:0;padding:16px 16px calc(16px + env(safe-area-inset-bottom));position:fixed;right:0;z-index:20}.chat-fullpage .ai-chat-input-container:after{background:var(--bg-primary,#fff);bottom:0;content:\"\";height:calc(40% + 16px);left:0;pointer-events:none;position:absolute;right:0;z-index:-1}.chat-fullpage.dark .ai-chat-input-container:after{background:var(--bg-primary,#18181b)}@media (min-width:769px){.chat-fullpage .ai-chat-input-container{background:transparent;bottom:var(--fp-input-bottom);left:50%;max-width:var(--fp-max-width);padding:0;position:absolute;right:auto;transform:translateX(-50%);width:100%}}.chat-fullpage .ai-chat-input-wrapper{background:var(--bg-muted,#f4f4f5);border:1px solid var(--border-muted,#e4e4e7);border-radius:24px;box-shadow:0 1px 8px rgba(0,0,0,.06);margin:0 auto;max-width:var(--fp-max-width)}.chat-fullpage.dark .ai-chat-input-wrapper{background:var(--bg-muted,#27272a);border-color:var(--border-muted,#3f3f46);box-shadow:0 1px 12px rgba(0,0,0,.25)}.chat-fullpage .ai-chat-scroll-button{bottom:100px}@media (min-width:769px){.chat-fullpage .ai-chat-scroll-button{bottom:90px}}.chat-fullpage .ai-chat-suggested-questions{display:flex;flex-wrap:wrap;gap:8px;justify-content:center;max-width:600px}.chat-fullpage .ai-chat-suggested-question{border-radius:9999px;font-size:14px;padding:8px 16px}.chat-fullpage .ai-chat-follow-up-suggestions{margin-top:12px}.chat-fullpage .ai-chat-follow-up-item.question-type{background:transparent;border:1px solid var(--border-default,#d4d4d8);color:#000}.chat-fullpage .ai-chat-follow-up-item.question-type:hover{background:var(--bg-hover,#f4f4f5)}.chat-fullpage.dark .ai-chat-follow-up-item.question-type{background:transparent;border:1px solid var(--border-subtle,#52525b);color:#fff}.chat-fullpage.dark .ai-chat-follow-up-item.question-type:hover{background:var(--bg-hover,#3f3f46)}.chat-fullpage .ai-chat-typing{padding:8px 16px}@media (max-width:480px){body.ai-chat-widget-open{height:100%!important;overflow:hidden!important;position:fixed!important;touch-action:none!important;width:100%!important}.ai-chat-widget-container.is-open{height:100vh!important;height:100dvh!important;width:100vw!important;z-index:var(--widget-z-index,2147483647)!important}.ai-chat-widget-container.is-open,.ai-chat-widget-container.is-open .ai-chat-window{bottom:0!important;left:0!important;position:fixed!important;right:0!important;top:0!important}.ai-chat-widget-container.is-open .ai-chat-window{animation:none!important;border:none!important;border-radius:0!important;box-shadow:none!important;height:100%!important;max-height:100%!important;max-width:100%!important;outline:none!important;transform:none!important;width:100%!important}.ai-chat-widget-container.is-open .ai-chat-button{display:none!important;pointer-events:none!important;visibility:hidden!important}.ai-chat-widget-container.is-open .ai-chat-header{border-radius:0!important;flex-shrink:0;padding-left:max(16px,env(safe-area-inset-left));padding-right:max(16px,env(safe-area-inset-right));padding-top:max(12px,env(safe-area-inset-top));position:relative;z-index:100}.ai-chat-widget-container.is-open .ai-chat-messages{-webkit-overflow-scrolling:touch;flex:1;overflow-x:hidden;overflow-y:auto;overscroll-behavior:contain;padding-bottom:120px;padding-left:max(16px,env(safe-area-inset-left));padding-right:max(16px,env(safe-area-inset-right));touch-action:pan-y}.ai-chat-widget-container.is-open .ai-chat-input-container{background:var(--bg-primary,#fff);bottom:0!important;left:0!important;padding:8px max(12px,env(safe-area-inset-right)) max(16px,calc(env(safe-area-inset-bottom) + 8px)) max(12px,env(safe-area-inset-left));position:fixed!important;right:0!important;z-index:100}.ai-chat-widget.dark .ai-chat-widget-container.is-open .ai-chat-input-container{background:var(--bg-primary,#282625)}.ai-chat-widget-container.is-open .ai-chat-input-container:after{display:none}.ai-chat-widget-container.is-open .ai-chat-input-wrapper{margin:0;max-width:100%}.ai-chat-widget-container.is-open .ai-chat-scroll-button{bottom:calc(80px + env(safe-area-inset-bottom))}.ai-chat-widget-container.is-open .ai-chat-welcome{padding:16px 0}.ai-chat-widget-container.is-open .ai-chat-welcome-title{font-size:24px}.ai-chat-widget-container.is-open .ai-chat-suggested-questions{align-items:stretch;flex-direction:column}.ai-chat-widget-container.is-open .ai-chat-suggested-question{text-align:center;width:100%}}@media (min-width:481px) and (max-width:768px){.ai-chat-widget-container.is-open .ai-chat-window{border-radius:22px 22px 44px 44px;max-height:calc(100vh - 100px);max-width:calc(100vw - 32px)}}";
|
|
31236
|
+
var css_248z$1 = ".ai-chat-message{animation:ai-chat-message-appear .2s var(--chat-ease-bounce);max-width:85%}.ai-chat-message-content{border-radius:var(--chat-radius-bubble,14px);font-size:var(--chat-text-md,15px);line-height:var(--chat-line-relaxed,1.6);padding:var(--chat-space-sm,8px) var(--chat-space-md,16px)}.ai-chat-message.user .ai-chat-message-content{background:var(--chat-user-bg,#f4f3f0);border-bottom-right-radius:var(--chat-radius-sm,4px);color:var(--chat-user-text,#000)}.ai-chat-message.assistant .ai-chat-message-content{background:var(--chat-assistant-bg,transparent);color:var(--chat-assistant-text,#000)}.ai-chat-message-timestamp{color:var(--chat-text-muted,#71717a);font-size:var(--chat-text-xs,12px);margin-top:var(--chat-space-xs,4px);padding:0 var(--chat-space-xs,4px)}@keyframes ai-chat-message-appear{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}.ai-chat-message.fullpage .ai-chat-message-content{font-size:var(--chat-text-lg,18px);padding:var(--chat-space-md,16px) var(--chat-space-lg,24px)}.ai-chat-typing{gap:var(--chat-space-xs,4px);padding:var(--chat-space-sm,8px) var(--chat-space-md,16px)}.ai-chat-typing-dot{background:var(--chat-text-muted,#71717a)}.ai-chat-tool-row{padding:0 16px}.ai-chat-tool-gear{color:var(--text-primary,#3e3e3e)}.ai-chat-tool-badge{border-radius:8px}.ai-chat-tool-badge.loading{background:#e5e7eb;border:1px solid #d1d5db;color:#1f2937}.ai-chat-tool-badge.error{background:rgba(239,68,68,.1);color:#ef4444}.ai-chat-tool-check{color:#22c55e;flex-shrink:0}.ai-chat-tool-error{color:#ef4444;flex-shrink:0}.ai-chat-tool-badge .tool-name{max-width:150px;overflow:hidden;text-overflow:ellipsis}.ai-chat-widget.dark .ai-chat-tool-gear,.ai-chat-widget[data-theme=dark] .ai-chat-tool-gear,.dark .ai-chat-tool-gear,[data-theme=dark] .ai-chat-tool-gear{color:#fff}.ai-chat-widget.dark .ai-chat-tool-badge,.ai-chat-widget[data-theme=dark] .ai-chat-tool-badge,.dark .ai-chat-tool-badge,[data-theme=dark] .ai-chat-tool-badge{background:hsla(0,0%,100%,.12);border:1px solid hsla(0,0%,100%,.2);color:hsla(0,0%,100%,.9)}.ai-chat-widget.dark .ai-chat-tool-badge.error,.ai-chat-widget[data-theme=dark] .ai-chat-tool-badge.error,.dark .ai-chat-tool-badge.error,[data-theme=dark] .ai-chat-tool-badge.error{background:rgba(239,68,68,.2);color:#f87171}.chakra-ui-dark .ai-chat-tool-gear,html.dark .ai-chat-tool-gear{color:#fff}.chakra-ui-dark .ai-chat-tool-badge,html.dark .ai-chat-tool-badge{background:hsla(0,0%,100%,.12);border:1px solid hsla(0,0%,100%,.2);color:hsla(0,0%,100%,.9)}.chakra-ui-dark .ai-chat-tool-badge.error,html.dark .ai-chat-tool-badge.error{background:rgba(239,68,68,.2);color:#f87171}.ai-chat-widget,.chat-ui{--radius-sm:4px;--radius-md:8px;--radius-lg:12px;--radius-xl:16px;--radius-2xl:18px;--radius-pill:9999px;--radius-window-top:22px;--radius-window-bottom:44px;--radius-window-gutter:16px;--radius-chat-bubble:14px;--radius-preset-badge:13px;--radius-history-item:14px;--radius-action-badge:8px;--radius-input:62px;--space-xs:4px;--space-sm:8px;--space-md:16px;--space-lg:24px;--space-xl:32px;--text-xs:12px;--text-sm:14px;--text-md:15px;--text-lg:18px;--text-xl:22px;--text-2xl:28px;--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--line-height-tight:1.3;--line-height-normal:1.4;--line-height-relaxed:1.6;--breakpoint-mobile:480px;--breakpoint-tablet:768px;--breakpoint-desktop:769px;--bg-primary:#fff;--bg-secondary:#f4f4f4;--bg-tertiary:#e5e7eb;--bg-hover:#e5e7eb;--text-primary:#3e3e3e;--text-secondary:#000;--text-muted:#71717a;--text-placeholder:#a1a1aa;--border-default:#d3d3d3;--border-subtle:#e5e7eb;--border-muted:#f4f4f5;--user-bg:#f4f3f0;--user-text:#000;--user-bg-hover:#e8e7e4;--agent-bg:transparent;--agent-text:#000;--input-bg:#f4f4f4;--input-border:#d3d3d3;--input-text:#000;--btn-primary-bg:#151515;--btn-primary-text:#f4f4f4;--btn-secondary-bg:transparent;--btn-secondary-text:#71717a;--spring-bounce:cubic-bezier(0.34,1.56,0.64,1);--spring-smooth:cubic-bezier(0.4,0,0.2,1);--spring-snappy:cubic-bezier(0.2,0,0,1);--duration-fast:0.15s;--duration-normal:0.25s;--duration-slow:0.35s;--shadow-sm:0 1px 2px rgba(0,0,0,.05);--shadow-md:0 2px 8px rgba(0,0,0,.1);--shadow-lg:0 4px 16px rgba(0,0,0,.12);--shadow-window:0px 0px 15px 9px rgba(0,0,0,.1);--shadow-button:0px 0px 15px 9px rgba(0,0,0,.03)}.ai-chat-widget.dark,.chat-ui.dark{--bg-primary:#282625;--bg-secondary:#4a4846;--bg-tertiary:#484848;--bg-hover:#484848;--text-primary:#fff;--text-secondary:#fff;--text-muted:#a1a1aa;--text-placeholder:#71717a;--border-default:#5d5b5b;--border-subtle:#5d5b5b;--border-muted:#5d5b5b;--user-bg:#484848;--user-text:#fff;--user-bg-hover:#5a5a5a;--agent-bg:transparent;--agent-text:#fff;--input-bg:#4a4846;--input-border:#5d5b5b;--input-text:#fff;--btn-primary-bg:#fff;--btn-primary-text:#312f2d;--btn-secondary-bg:transparent;--btn-secondary-text:#a1a1aa;--shadow-window:0px 0px 15px 9px rgba(0,0,0,.2);--shadow-button:0px 0px 15px 9px rgba(0,0,0,.03);--shadow-input:0px 0px 10px rgba(0,0,0,.15)}.ai-chat-widget,.chat-ui{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif}.ai-chat-widget-container{font-size:var(--text-sm);line-height:1.5;position:fixed;z-index:var(--widget-z-index,2147483647)!important}.ai-chat-widget-container.bottom-right{bottom:20px;right:20px}.ai-chat-widget-container.bottom-left{bottom:20px;left:20px}.ai-chat-widget-container.top-right{right:20px;top:20px}.ai-chat-widget-container.top-left{left:20px;top:20px}.ai-chat-widget-container.container-mode{height:100%;inset:0;pointer-events:none;position:absolute;width:100%}.ai-chat-widget-container.container-mode .ai-chat-button,.ai-chat-widget-container.container-mode .ai-chat-trigger-button,.ai-chat-widget-container.container-mode .ai-chat-trigger-input-container,.ai-chat-widget-container.container-mode .ai-chat-window{pointer-events:auto}.ai-chat-widget-container.container-mode:not(.trigger-input-bar) .ai-chat-button{position:absolute}.ai-chat-widget-container.container-mode:not(.trigger-input-bar).bottom-right .ai-chat-button{bottom:20px;left:auto;right:20px;top:auto}.ai-chat-widget-container.container-mode:not(.trigger-input-bar).bottom-left .ai-chat-button{bottom:20px;left:20px;right:auto;top:auto}.ai-chat-widget-container.container-mode:not(.trigger-input-bar).top-right .ai-chat-button{bottom:auto;left:auto;right:20px;top:20px}.ai-chat-widget-container.container-mode:not(.trigger-input-bar).top-left .ai-chat-button{bottom:auto;left:20px;right:auto;top:20px}@keyframes ai-chat-window-open{0%{opacity:0;transform:scale(.9) translateY(20px)}to{opacity:1;transform:scale(1) translateY(0)}}@keyframes ai-chat-window-close{0%{opacity:1;transform:scale(1) translateY(0)}to{opacity:0;transform:scale(.9) translateY(20px)}}@keyframes ai-chat-message-slide-in{0%{opacity:0;transform:translateY(12px)}to{opacity:1;transform:translateY(0)}}@keyframes ai-chat-user-message-sent{0%{opacity:0;transform:translateX(20px) scale(.95)}60%{opacity:1;transform:translateX(-4px) scale(1.02)}to{opacity:1;transform:translateX(0) scale(1)}}@keyframes ai-chat-welcome-fade-in{0%{opacity:0;transform:translateY(16px)}to{opacity:1;transform:translateY(0)}}@keyframes ai-chat-typing-pulse{0%,60%,to{opacity:.4;transform:translateY(0) scale(1)}30%{opacity:1;transform:translateY(-4px) scale(1.1)}}@keyframes ai-chat-gear-spin{to{transform:rotate(1turn)}}@keyframes ai-chat-tool-active{0%,to{background:var(--bg-secondary);opacity:1}50%{background:var(--bg-tertiary);opacity:1}}@keyframes ai-chat-feedback-morph{0%{opacity:.5;transform:scale(.8)}to{opacity:1;transform:scale(1)}}@keyframes ai-chat-checkmark-pop{0%{opacity:0;transform:scale(0) rotate(-45deg)}50%{transform:scale(1.3) rotate(0deg)}to{opacity:1;transform:scale(1) rotate(0deg)}}@keyframes ai-chat-history-exit{to{opacity:0;transform:translateY(-18px)}}@keyframes ai-chat-tool-gradient{0%{background-position:200% 0}to{background-position:-200% 0}}.ai-chat-window{-webkit-font-smoothing:subpixel-antialiased;-moz-osx-font-smoothing:auto;animation:ai-chat-window-open var(--duration-slow,.35s) var(--spring-bounce);-webkit-backface-visibility:hidden;backface-visibility:hidden;background:var(--bg-primary,#fff);border:1px solid var(--border-default,#d3d3d3);border-radius:var(--radius-window-top,22px) var(--radius-window-top,22px) var(--radius-window-bottom,44px) var(--radius-window-bottom,44px);box-shadow:var(--shadow-window,0 0 15px 5px rgba(0,0,0,.08));display:flex;flex-direction:column;overflow:hidden;position:absolute;-webkit-transform:translateZ(0);transform:translateZ(0);transform-origin:bottom right;will-change:transform,opacity;z-index:2147483647!important}.ai-chat-widget.dark .ai-chat-window{background:var(--bg-primary,#282625);border-color:var(--border-default,#5d5b5b);border-width:.7px}.ai-chat-window.closing{animation:ai-chat-window-close var(--duration-normal) var(--spring-smooth) forwards}.ai-chat-window.size-small{height:500px;max-height:calc(100vh - 100px);max-width:calc(100vw - 40px);min-width:320px;width:380px}.ai-chat-window.size-medium{height:calc(100vh - 140px);max-height:680px;max-width:calc(100vw - 40px);min-width:380px;width:440px}.ai-chat-window.size-large{height:calc(100vh - 48px);max-height:calc(100vh - 48px);max-width:560px;min-width:440px;width:33vw}.ai-chat-widget-container.bottom-right .ai-chat-window{bottom:calc(var(--button-size, 56px) + 8px);right:0}.ai-chat-widget-container.bottom-left .ai-chat-window{bottom:calc(var(--button-size, 56px) + 8px);left:0}.ai-chat-widget-container.top-right .ai-chat-window{right:0;top:calc(var(--button-size, 56px) + 8px)}.ai-chat-widget-container.top-left .ai-chat-window{left:0;top:calc(var(--button-size, 56px) + 8px)}.ai-chat-widget-container.trigger-button.size-large.bottom-left .ai-chat-window,.ai-chat-widget-container.trigger-button.size-large.bottom-right .ai-chat-window{bottom:0}.ai-chat-widget-container.trigger-button.size-large.top-left .ai-chat-window,.ai-chat-widget-container.trigger-button.size-large.top-right .ai-chat-window{top:0}.ai-chat-widget-container.trigger-pill-text.size-large.bottom-left .ai-chat-window,.ai-chat-widget-container.trigger-pill-text.size-large.bottom-right .ai-chat-window{bottom:72px;height:calc(100vh - 120px);max-height:calc(100vh - 120px)}.ai-chat-widget-container.trigger-pill-text.size-large.top-left .ai-chat-window,.ai-chat-widget-container.trigger-pill-text.size-large.top-right .ai-chat-window{height:calc(100vh - 120px);max-height:calc(100vh - 120px);top:72px}.ai-chat-widget-container.trigger-input-bar.size-large.bottom-left .ai-chat-window,.ai-chat-widget-container.trigger-input-bar.size-large.bottom-right .ai-chat-window{bottom:72px;height:calc(100vh - 120px);max-height:calc(100vh - 120px)}.ai-chat-widget-container.trigger-input-bar.size-large.top-left .ai-chat-window,.ai-chat-widget-container.trigger-input-bar.size-large.top-right .ai-chat-window{height:calc(100vh - 120px);max-height:calc(100vh - 120px);top:72px}.ai-chat-widget-container.container-mode.bottom-right .ai-chat-window{bottom:20px;left:auto;right:20px;top:auto}.ai-chat-widget-container.container-mode.bottom-left .ai-chat-window{bottom:20px;left:20px;right:auto;top:auto}.ai-chat-widget-container.container-mode.top-right .ai-chat-window{bottom:auto;left:auto;right:20px;top:20px}.ai-chat-widget-container.container-mode.top-left .ai-chat-window{bottom:auto;left:20px;right:auto;top:20px}.ai-chat-widget-container.container-mode.size-small .ai-chat-window{height:min(500px,50%);max-height:calc(100% - 100px);max-width:calc(100% - 40px);min-height:300px;min-width:280px;width:min(380px,calc(100% - 40px))}.ai-chat-widget-container.container-mode.size-medium .ai-chat-window{height:min(680px,70%);max-height:calc(100% - 80px);max-width:calc(100% - 40px);min-height:400px;min-width:320px;width:min(440px,calc(100% - 40px))}.ai-chat-widget-container.container-mode.size-large .ai-chat-window{height:90%;max-height:calc(100% - 60px);max-width:calc(100% - 40px);min-height:500px;min-width:380px;width:min(560px,calc(100% - 40px))}.ai-chat-widget-container.trigger-button.container-mode.bottom-left .ai-chat-window,.ai-chat-widget-container.trigger-button.container-mode.bottom-right .ai-chat-window{bottom:88px}.ai-chat-widget-container.trigger-pill-text.container-mode.bottom-left .ai-chat-window,.ai-chat-widget-container.trigger-pill-text.container-mode.bottom-right .ai-chat-window{bottom:80px}.ai-chat-widget-container.trigger-button.container-mode.size-medium .ai-chat-window,.ai-chat-widget-container.trigger-button.container-mode.size-small .ai-chat-window,.ai-chat-widget-container.trigger-pill-text.container-mode.size-medium .ai-chat-window,.ai-chat-widget-container.trigger-pill-text.container-mode.size-small .ai-chat-window{max-height:calc(100% - 108px)}.ai-chat-widget-container.trigger-button.container-mode.size-large .ai-chat-window,.ai-chat-widget-container.trigger-pill-text.container-mode.size-large .ai-chat-window{height:calc(100% - 108px);max-height:calc(100% - 108px)}.ai-chat-widget-container.trigger-button.container-mode.top-left .ai-chat-window,.ai-chat-widget-container.trigger-button.container-mode.top-right .ai-chat-window{bottom:auto;top:88px}.ai-chat-widget-container.trigger-pill-text.container-mode.top-left .ai-chat-window,.ai-chat-widget-container.trigger-pill-text.container-mode.top-right .ai-chat-window{bottom:auto;top:80px}.ai-chat-widget-container.container-mode.mobile-mode.is-open{margin:0!important;padding:0!important}.ai-chat-widget-container.container-mode.mobile-mode.is-open,.ai-chat-widget-container.container-mode.mobile-mode.is-open .ai-chat-window{bottom:0!important;height:100%!important;left:0!important;position:absolute!important;right:0!important;top:0!important;width:100%!important}.ai-chat-widget-container.container-mode.mobile-mode.is-open .ai-chat-window{animation:none!important;border:none!important;border-radius:0!important;box-shadow:none!important;max-height:100%!important;max-width:100%!important;min-height:100%!important;min-width:100%!important}.ai-chat-widget-container.container-mode.mobile-mode.is-open .ai-chat-button,.ai-chat-widget-container.container-mode.mobile-mode.is-open .ai-chat-trigger-input-container,.ai-chat-widget-container.container-mode.mobile-mode.is-open .ai-chat-trigger-pill{display:none!important}.ai-chat-header{align-items:center;background:var(--bg-primary,#fff);border-bottom:1px solid var(--border-default,#d3d3d3);border-radius:var(--radius-window-top,22px) var(--radius-window-top,22px) 0 0;display:flex;justify-content:space-between;padding:12px var(--space-md,16px);position:relative;z-index:10}.ai-chat-widget.dark .ai-chat-header{background:var(--bg-primary,#282625);border-bottom-color:var(--border-default,#5d5b5b);border-bottom-width:.7px}.ai-chat-header.is-history{padding-left:var(--space-md)}.ai-chat-header.is-history .ai-chat-title{flex:1;min-width:0;overflow:hidden;padding-right:var(--space-lg);text-overflow:ellipsis;white-space:nowrap}.ai-chat-header-content{align-items:center;display:flex;flex:1;gap:var(--space-lg)}.ai-chat-header-actions{align-items:center;display:flex;gap:var(--space-sm)}.ai-chat-logo{border-radius:10px;height:36px;object-fit:cover;width:36px}.ai-chat-title{color:var(--text-primary,#3e3e3e);font-size:var(--text-xl,22px);font-weight:var(--font-weight-bold,700);letter-spacing:-.02em}.ai-chat-widget.dark .ai-chat-title{color:var(--text-primary,#fff)}.ai-chat-close-button,.ai-chat-header-button{align-items:center;background:transparent;border:none;border-radius:var(--radius-md);color:var(--text-muted);cursor:pointer;display:flex;height:32px;justify-content:center;padding:0;transition:color var(--duration-fast) ease;width:32px}.ai-chat-close-button:hover,.ai-chat-header-button:hover{color:var(--text-primary)}.ai-chat-close-button:active,.ai-chat-header-button:active{transform:scale(.95)}.ai-chat-close-button svg,.ai-chat-header-button svg{height:22px;width:22px}.ai-chat-button{-webkit-font-smoothing:subpixel-antialiased;-moz-osx-font-smoothing:auto;align-items:center;-webkit-backface-visibility:hidden;backface-visibility:hidden;background:var(--button-color,var(--btn-primary-bg));border:1px solid var(--border-default,#d3d3d3);border-radius:50%;box-shadow:var(--shadow-button,0 0 15px 9px rgba(0,0,0,.03));color:var(--button-icon-color,var(--btn-primary-text));cursor:pointer;display:flex;height:var(--button-size,56px);justify-content:center;overflow:hidden;position:relative;-webkit-transform:translateZ(0);transform:translateZ(0);transition:transform var(--duration-fast) ease;width:var(--button-size,56px);will-change:transform;z-index:2147483647!important}.ai-chat-button:hover{transform:scale(1.05)}.ai-chat-button:active{transform:scale(.98)}.ai-chat-button-svg{height:50%;min-height:24px;min-width:24px;transition:transform var(--duration-fast) ease;width:50%}.ai-chat-button.is-open .ai-chat-button-svg{transform:rotate(0deg)}.ai-chat-button-icon{font-size:1.5em;line-height:1}.ai-chat-welcome-bubble{animation:ai-chat-bubble-fade-in .3s ease-out;background:var(--button-color,var(--btn-primary-bg,#07f));border:none;border-radius:16px;box-shadow:none;box-sizing:border-box;color:var(--button-icon-color,var(--btn-primary-text,#fff));cursor:pointer;font-size:13px;font-weight:500;line-height:1.5;max-width:min(420px,90vw);padding:12px 32px 12px 16px;position:absolute;text-align:left;white-space:normal;width:auto;z-index:2147483647!important}.ai-chat-welcome-bubble-close{align-items:center;background:transparent;border:none;border-radius:50%;color:inherit;cursor:pointer;display:flex;height:20px;justify-content:center;opacity:.8;padding:0;position:absolute;right:8px;top:8px;transition:opacity .15s ease,background .15s ease;width:20px}.ai-chat-welcome-bubble-close:hover{background:hsla(0,0%,100%,.2);opacity:1}.ai-chat-welcome-bubble-close svg{height:12px;width:12px}.ai-chat-welcome-bubble-arrow{border-bottom:8px solid transparent;border-top:8px solid transparent;height:0;position:absolute;width:0}.ai-chat-widget-container.bottom-right .ai-chat-welcome-bubble{bottom:50%;right:calc(100% + 12px);text-align:left;transform:translateY(50%)}.ai-chat-widget-container.bottom-left .ai-chat-welcome-bubble{bottom:50%;left:calc(100% + 12px);text-align:left;transform:translateY(50%)}.ai-chat-widget-container.top-right .ai-chat-welcome-bubble{right:calc(100% + 12px);text-align:left;top:50%;transform:translateY(-50%)}.ai-chat-widget-container.top-left .ai-chat-welcome-bubble{left:calc(100% + 12px);text-align:left;top:50%;transform:translateY(-50%)}.ai-chat-widget-container.bottom-right .ai-chat-welcome-bubble-arrow{border-left:8px solid var(--button-color,var(--btn-primary-bg,#07f));right:-8px;top:50%;transform:translateY(-50%)}.ai-chat-widget-container.bottom-left .ai-chat-welcome-bubble-arrow{border-right:8px solid var(--button-color,var(--btn-primary-bg,#07f));left:-8px;top:50%;transform:translateY(-50%)}.ai-chat-widget-container.top-right .ai-chat-welcome-bubble-arrow{border-left:8px solid var(--button-color,var(--btn-primary-bg,#07f));right:-8px;top:50%;transform:translateY(-50%)}.ai-chat-widget-container.top-left .ai-chat-welcome-bubble-arrow{border-right:8px solid var(--button-color,var(--btn-primary-bg,#07f));left:-8px;top:50%;transform:translateY(-50%)}.ai-chat-welcome-bubble:hover{filter:brightness(1.1)}.ai-chat-widget-container.bottom-right .ai-chat-welcome-bubble{animation-name:ai-chat-bubble-fade-in-bottom-right}@keyframes ai-chat-bubble-fade-in-bottom-right{0%{opacity:0;transform:translateY(50%) translateX(8px)}to{opacity:1;transform:translateY(50%) translateX(0)}}.ai-chat-widget-container.bottom-left .ai-chat-welcome-bubble{animation-name:ai-chat-bubble-fade-in-bottom-left}@keyframes ai-chat-bubble-fade-in-bottom-left{0%{opacity:0;transform:translateY(50%) translateX(-8px)}to{opacity:1;transform:translateY(50%) translateX(0)}}.ai-chat-widget-container.top-right .ai-chat-welcome-bubble{animation-name:ai-chat-bubble-fade-in-top-right}@keyframes ai-chat-bubble-fade-in-top-right{0%{opacity:0;transform:translateY(-50%) translateX(8px)}to{opacity:1;transform:translateY(-50%) translateX(0)}}.ai-chat-widget-container.top-left .ai-chat-welcome-bubble{animation-name:ai-chat-bubble-fade-in-top-left}@keyframes ai-chat-bubble-fade-in-top-left{0%{opacity:0;transform:translateY(-50%) translateX(-8px)}to{opacity:1;transform:translateY(-50%) translateX(0)}}.ai-chat-trigger-pill{-webkit-font-smoothing:subpixel-antialiased;-moz-osx-font-smoothing:auto;align-items:center;backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);-webkit-backface-visibility:hidden;backface-visibility:hidden;background:var(--trigger-bg,rgba(0,0,0,.04));border:1px solid var(--trigger-border,rgba(0,0,0,.12));border-radius:9999px;box-shadow:0 4px 16px rgba(0,0,0,.12);color:var(--trigger-text,#1a1a1a);cursor:pointer;display:flex;font-size:14px;font-weight:500;gap:10px;height:48px;justify-content:center;padding:0 24px;position:relative;-webkit-transform:translateZ(0);transform:translateZ(0);transition:all .3s ease,color .4s ease,background .4s ease,border-color .4s ease;white-space:nowrap;will-change:transform;z-index:2147483647!important}.ai-chat-trigger-pill.is-open{background:var(--button-color,var(--btn-primary-bg));border-color:var(--border-default,#d3d3d3);box-shadow:var(--shadow-button,0 0 15px 9px rgba(0,0,0,.03));color:var(--button-icon-color,var(--btn-primary-text));height:56px;padding:0;width:56px}.ai-chat-trigger-pill.is-open .ai-chat-trigger-pill-icon{height:24px;width:24px}.ai-chat-trigger-pill:hover:not(.is-open){background:var(--trigger-bg-hover,rgba(0,0,0,.08));border-color:var(--trigger-border-hover,rgba(0,0,0,.2))}.ai-chat-trigger-pill.is-open:hover{transform:scale(1.05)}.ai-chat-trigger-pill:active{transform:scale(.98)}.ai-chat-trigger-pill-icon{flex-shrink:0;height:20px;transition:all .3s ease;width:20px}.ai-chat-widget-container.trigger-pill-text.container-mode .ai-chat-trigger-pill{position:absolute}.ai-chat-widget-container.trigger-pill-text.container-mode.bottom-right .ai-chat-trigger-pill{bottom:20px;left:auto;right:20px;top:auto}.ai-chat-widget-container.trigger-pill-text.container-mode.bottom-left .ai-chat-trigger-pill{bottom:20px;left:20px;right:auto;top:auto}.ai-chat-widget-container.trigger-pill-text.container-mode.top-right .ai-chat-trigger-pill{bottom:auto;left:auto;right:20px;top:20px}.ai-chat-widget-container.trigger-pill-text.container-mode.top-left .ai-chat-trigger-pill{bottom:auto;left:20px;right:auto;top:20px}.ai-chat-widget-container.trigger-pill-text.container-mode .ai-chat-trigger-pill{pointer-events:auto}.ai-chat-trigger-input-container{align-items:flex-end;display:flex;flex-direction:column;gap:8px;z-index:2147483647!important}.ai-chat-widget-container.bottom-left .ai-chat-trigger-input-container,.ai-chat-widget-container.top-left .ai-chat-trigger-input-container{align-items:flex-start}.ai-chat-trigger-input-row{align-items:center;display:flex;gap:8px;transition:gap .4s cubic-bezier(.4,0,.2,1),width .4s cubic-bezier(.4,0,.2,1)}.ai-chat-widget-container.trigger-input-bar.is-collapsed .ai-chat-trigger-input-row{gap:4px}.ai-chat-trigger-input-expand{-webkit-font-smoothing:subpixel-antialiased;-moz-osx-font-smoothing:auto;align-items:center;backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);-webkit-backface-visibility:hidden;backface-visibility:hidden;background:var(--trigger-bg,rgba(0,0,0,.04));border:1px solid var(--trigger-border,rgba(0,0,0,.12));border-radius:50%;box-shadow:0 2px 12px rgba(0,0,0,.12);color:var(--trigger-text-muted,rgba(0,0,0,.5));cursor:pointer;display:flex;flex-shrink:0;height:48px;justify-content:center;padding:0;-webkit-transform:translateZ(0);transform:translateZ(0);transition:all .2s ease,color .4s ease,background .4s ease,border-color .4s ease;width:48px;will-change:transform}.ai-chat-trigger-input-expand:hover{background:var(--trigger-bg-hover,rgba(0,0,0,.08));color:var(--trigger-text,#1a1a1a);transform:translateY(-2px)}.ai-chat-trigger-input-expand:active{transform:translateY(0)}.ai-chat-trigger-input-expand svg{stroke-width:2.5;height:20px;width:20px}.ai-chat-trigger-input-wrapper{flex:1;min-width:120px;position:relative;transform-origin:right center;transition:flex .4s cubic-bezier(.4,0,.2,1),width .4s cubic-bezier(.4,0,.2,1),opacity .3s cubic-bezier(.4,0,.2,1),transform .4s cubic-bezier(.4,0,.2,1);z-index:1}.ai-chat-widget-container.bottom-left .ai-chat-trigger-input-wrapper,.ai-chat-widget-container.top-left .ai-chat-trigger-input-wrapper{transform-origin:left center}.ai-chat-trigger-input{-webkit-font-smoothing:subpixel-antialiased;-moz-osx-font-smoothing:auto;backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);-webkit-backface-visibility:hidden;backface-visibility:hidden;background:var(--trigger-bg,rgba(0,0,0,.04));border:1px solid var(--trigger-border,rgba(0,0,0,.12));border-radius:var(--radius-input,62px);box-shadow:0 4px 24px rgba(0,0,0,.15);box-sizing:border-box;color:var(--trigger-text,#1a1a1a);font-size:var(--text-md,15px);height:48px;outline:none;padding:6px 52px 6px 16px;-webkit-transform:translateZ(0);transform:translateZ(0);transition:all .2s ease,color .4s ease,background .4s ease,border-color .4s ease;width:100%}.ai-chat-trigger-input::placeholder{color:var(--trigger-text-muted,rgba(0,0,0,.5))}.ai-chat-trigger-input:focus{background:var(--trigger-bg-hover,rgba(0,0,0,.08));border-color:var(--trigger-border-hover,rgba(0,0,0,.2))}.ai-chat-trigger-input-btn{-webkit-font-smoothing:subpixel-antialiased;-moz-osx-font-smoothing:auto;align-items:center;-webkit-backface-visibility:hidden;backface-visibility:hidden;background:var(--primary-color,var(--button-color,#07f));border:none;border-radius:50%;box-shadow:0 2px 8px rgba(0,119,255,.3);color:var(--send-button-icon-color,var(--button-icon-color,#fff));cursor:pointer;display:flex;height:38px;justify-content:center;position:absolute;right:4px;top:50%;transform:translateY(-50%);transition:all .2s ease;width:38px;will-change:transform}.ai-chat-trigger-input-btn:hover:not(:disabled){box-shadow:0 4px 12px rgba(0,119,255,.4);transform:translateY(-50%) scale(1.05)}.ai-chat-trigger-input-btn:active:not(:disabled){transform:translateY(-50%) scale(.95)}.ai-chat-trigger-input-btn:disabled{box-shadow:none;cursor:not-allowed;opacity:.4}.ai-chat-trigger-input-btn svg{height:18px;width:18px}.ai-chat-widget-container.trigger-input-bar .ai-chat-trigger-input-row{width:min(380px,calc(100vw - 40px))}.ai-chat-widget-container.trigger-input-bar.size-medium .ai-chat-trigger-input-row{width:min(440px,calc(100vw - 40px))}.ai-chat-widget-container.trigger-input-bar.size-large .ai-chat-trigger-input-row{width:min(560px,calc(100vw - 40px))}.ai-chat-widget-container.trigger-input-bar .ai-chat-trigger-input-wrapper{flex:1;min-width:160px}.ai-chat-widget-container.trigger-input-bar.container-mode .ai-chat-trigger-input-container{position:absolute;width:min(380px,calc(100% - 40px))}.ai-chat-widget-container.trigger-input-bar.container-mode.size-small .ai-chat-trigger-input-container{width:min(380px,calc(100% - 40px))}.ai-chat-widget-container.trigger-input-bar.container-mode.size-medium .ai-chat-trigger-input-container{width:min(440px,calc(100% - 40px))}.ai-chat-widget-container.trigger-input-bar.container-mode.size-large .ai-chat-trigger-input-container{width:min(560px,calc(100% - 40px))}.ai-chat-widget-container.trigger-input-bar.container-mode.bottom-right .ai-chat-trigger-input-container{bottom:20px;left:auto;right:20px;top:auto}.ai-chat-widget-container.trigger-input-bar.container-mode.bottom-left .ai-chat-trigger-input-container{bottom:20px;left:20px;right:auto;top:auto}.ai-chat-widget-container.trigger-input-bar.container-mode.top-right .ai-chat-trigger-input-container{bottom:auto;left:auto;right:20px;top:20px}.ai-chat-widget-container.trigger-input-bar.container-mode.top-left .ai-chat-trigger-input-container{bottom:auto;left:20px;right:auto;top:20px}.ai-chat-widget-container.trigger-input-bar.container-mode .ai-chat-trigger-input-row{width:100%}.ai-chat-widget-container.trigger-input-bar.container-mode .ai-chat-trigger-input-wrapper{flex:1;min-width:120px}.ai-chat-widget-container.trigger-input-bar.is-collapsed .ai-chat-trigger-input-wrapper{opacity:0;overflow:hidden;pointer-events:none;transform:scaleX(0);width:0}.ai-chat-widget-container.trigger-input-bar{align-items:flex-end;display:flex;flex-direction:column;gap:12px}.ai-chat-widget-container.trigger-input-bar.bottom-left,.ai-chat-widget-container.trigger-input-bar.top-left{align-items:flex-start}.ai-chat-widget-container.trigger-input-bar .ai-chat-window{bottom:auto;left:auto;order:-1;position:relative;right:auto;top:auto}.ai-chat-widget-container.trigger-input-bar .ai-chat-window.size-small{max-width:calc(100vw - 40px);min-width:320px;width:380px}.ai-chat-widget-container.trigger-input-bar .ai-chat-window.size-medium{max-width:calc(100vw - 40px);min-width:380px;width:440px}.ai-chat-widget-container.trigger-input-bar .ai-chat-window.size-large{max-width:calc(100vw - 40px);min-width:440px;width:560px}.ai-chat-widget-container.trigger-input-bar.container-mode .ai-chat-window{max-width:calc(100% - 40px);position:absolute}.ai-chat-widget-container.trigger-input-bar.container-mode.bottom-right .ai-chat-window{bottom:20px;left:auto;right:20px;top:auto}.ai-chat-widget-container.trigger-input-bar.container-mode.bottom-left .ai-chat-window{bottom:20px;left:20px;right:auto;top:auto}.ai-chat-widget-container.trigger-input-bar.container-mode.top-right .ai-chat-window{bottom:auto;left:auto;right:20px;top:20px}.ai-chat-widget-container.trigger-input-bar.container-mode.top-left .ai-chat-window{bottom:auto;left:20px;right:auto;top:20px}.ai-chat-widget-container.trigger-input-bar.container-mode.size-small .ai-chat-window{max-width:calc(100% - 40px);width:380px}.ai-chat-widget-container.trigger-input-bar.container-mode.size-medium .ai-chat-window{max-width:calc(100% - 40px);width:440px}.ai-chat-widget-container.trigger-input-bar.container-mode.size-large .ai-chat-window{max-width:calc(100% - 40px);width:560px}.ai-chat-widget-container.trigger-input-bar.mobile-mode .ai-chat-trigger-input-container{width:calc(100% - 40px)}.ai-chat-widget-container.trigger-input-bar.mobile-mode .ai-chat-trigger-input-row{width:100%}.ai-chat-widget-container.trigger-input-bar.mobile-mode .ai-chat-trigger-input-wrapper{flex:1;min-width:120px}.ai-chat-widget-container.trigger-input-bar.mobile-mode.is-collapsed .ai-chat-trigger-input-container{width:auto}.ai-chat-widget-container.trigger-input-bar.mobile-mode.is-collapsed .ai-chat-trigger-input-row{gap:8px;justify-content:flex-end}.ai-chat-trigger-collapse-toggle{align-items:center;background:transparent;border:none;border-radius:50%;color:var(--trigger-text-muted,rgba(0,0,0,.5));cursor:pointer;display:flex;flex-shrink:0;height:24px;justify-content:center;margin-right:-4px;order:-1;padding:0;transition:all .2s ease;width:24px}.ai-chat-trigger-collapse-toggle:hover{color:var(--trigger-text,#1a1a1a)}.ai-chat-trigger-collapse-toggle svg{stroke-width:2.5;height:20px;transition:transform .4s cubic-bezier(.4,0,.2,1);width:20px}.ai-chat-widget-container.trigger-input-bar.bottom-left .ai-chat-trigger-collapse-toggle svg,.ai-chat-widget-container.trigger-input-bar.top-left .ai-chat-trigger-collapse-toggle svg{transform:rotate(180deg)}.ai-chat-trigger-input-expand{margin-left:-4px;order:1;transition:transform .2s ease,opacity .3s ease}.ai-chat-widget-container.container-mode .ai-chat-trigger-input-expand{margin-left:0}.ai-chat-trigger-input-expand svg{transition:transform .4s cubic-bezier(.4,0,.2,1)}.ai-chat-widget-container.trigger-input-bar .ai-chat-trigger-input-expand svg{transform:rotate(0deg)}.ai-chat-widget-container.bottom-left .ai-chat-trigger-collapse-toggle,.ai-chat-widget-container.top-left .ai-chat-trigger-collapse-toggle{margin-left:-4px;margin-right:0;order:1}.ai-chat-widget-container.bottom-left .ai-chat-trigger-input-expand,.ai-chat-widget-container.top-left .ai-chat-trigger-input-expand{margin-left:0;margin-right:-4px;order:-1}.ai-chat-widget-container.trigger-input-bar .ai-chat-trigger-input-row .ai-chat-button{order:1}.ai-chat-widget-container.trigger-input-bar.bottom-left .ai-chat-trigger-input-row .ai-chat-button,.ai-chat-widget-container.trigger-input-bar.top-left .ai-chat-trigger-input-row .ai-chat-button{order:-1}.ai-chat-widget-container.trigger-input-bar.is-collapsed.bottom-right .ai-chat-button,.ai-chat-widget-container.trigger-input-bar.is-collapsed.top-right .ai-chat-button{margin-right:16px}.ai-chat-widget-container.trigger-input-bar.is-collapsed.bottom-left .ai-chat-button,.ai-chat-widget-container.trigger-input-bar.is-collapsed.top-left .ai-chat-button{margin-left:16px}.ai-chat-widget-container.trigger-input-bar.is-collapsed .ai-chat-trigger-input-row{flex-direction:row;justify-content:flex-end}.ai-chat-widget-container.trigger-input-bar.is-collapsed.bottom-left .ai-chat-trigger-input-row,.ai-chat-widget-container.trigger-input-bar.is-collapsed.top-left .ai-chat-trigger-input-row{justify-content:flex-start}.ai-chat-widget-container.trigger-input-bar .ai-chat-button{display:flex;opacity:1;transform:scale(1);transition:transform .4s cubic-bezier(.4,0,.2,1),opacity .3s cubic-bezier(.4,0,.2,1),width .4s cubic-bezier(.4,0,.2,1),height .4s cubic-bezier(.4,0,.2,1)}.ai-chat-widget-container.trigger-input-bar:not(.is-collapsed) .ai-chat-button{border:none;height:0;margin:0;min-width:0;opacity:0;overflow:hidden;padding:0;pointer-events:none;transform:scale(0);width:0}.ai-chat-widget-container.trigger-pill-text .ai-chat-button{display:none}.ai-chat-widget-container.trigger-pill-text.is-open{gap:8px}.ai-chat-widget.light .ai-chat-trigger-pill:not(.is-open){backdrop-filter:none;-webkit-backdrop-filter:none;background:#fff;border:1px solid rgba(0,0,0,.15);box-shadow:0 2px 8px rgba(0,0,0,.08);color:#18181b}.ai-chat-widget.light .ai-chat-trigger-pill:not(.is-open):hover{background:#fafafa;border-color:rgba(0,0,0,.2)}.ai-chat-widget.light .ai-chat-trigger-input-expand{backdrop-filter:none;-webkit-backdrop-filter:none;background:#fff;border:1px solid rgba(0,0,0,.15);box-shadow:0 2px 8px rgba(0,0,0,.08);color:#52525b}.ai-chat-widget.light .ai-chat-trigger-input-expand:hover{background:#fafafa;color:#18181b}.ai-chat-widget.light .ai-chat-trigger-input{backdrop-filter:none;-webkit-backdrop-filter:none;background:#fff;border:1px solid rgba(0,0,0,.15);box-shadow:0 2px 8px rgba(0,0,0,.08);color:#18181b}.ai-chat-widget.light .ai-chat-trigger-input::placeholder{color:#71717a}.ai-chat-widget.light .ai-chat-trigger-input:focus{background:#fff;border-color:rgba(0,0,0,.25)}.ai-chat-widget.light .ai-chat-trigger-collapse-toggle{color:#71717a}.ai-chat-widget.light .ai-chat-trigger-collapse-toggle:hover{color:#18181b}.ai-chat-widget.dark .ai-chat-trigger-pill:not(.is-open){backdrop-filter:none;-webkit-backdrop-filter:none;background:#27272a;border:1px solid hsla(0,0%,100%,.15);box-shadow:0 2px 8px rgba(0,0,0,.3);color:#fafafa}.ai-chat-widget.dark .ai-chat-trigger-pill:not(.is-open):hover{background:#3f3f46;border-color:hsla(0,0%,100%,.25)}.ai-chat-widget.dark .ai-chat-trigger-input-expand{backdrop-filter:none;-webkit-backdrop-filter:none;background:#27272a;border:1px solid hsla(0,0%,100%,.15);box-shadow:0 2px 8px rgba(0,0,0,.3);color:#a1a1aa}.ai-chat-widget.dark .ai-chat-trigger-input-expand:hover{background:#3f3f46;color:#fafafa}.ai-chat-widget.dark .ai-chat-trigger-input{backdrop-filter:none;-webkit-backdrop-filter:none;background:#27272a;border:1px solid hsla(0,0%,100%,.15);box-shadow:0 2px 8px rgba(0,0,0,.3);color:#fafafa}.ai-chat-widget.dark .ai-chat-trigger-input::placeholder{color:#71717a}.ai-chat-widget.dark .ai-chat-trigger-input:focus{background:#27272a;border-color:hsla(0,0%,100%,.25)}.ai-chat-widget.dark .ai-chat-trigger-collapse-toggle{color:#71717a}.ai-chat-widget.dark .ai-chat-trigger-collapse-toggle:hover{color:#fafafa}.ai-chat-input-container{background:var(--bg-primary,#fff);bottom:0;left:0;padding:8px 0 16px;position:absolute;right:0;z-index:10}.ai-chat-widget.dark .ai-chat-input-container{background:var(--bg-primary,#282625)}.ai-chat-input-container.separate{padding:0 var(--radius-window-gutter,16px) var(--radius-window-gutter,16px)}.ai-chat-input-wrapper{-webkit-font-smoothing:subpixel-antialiased;-moz-osx-font-smoothing:auto;align-items:flex-end;-webkit-backface-visibility:hidden;backface-visibility:hidden;background:var(--input-bg,#f4f4f4);border:1px solid var(--input-border,#d3d3d3);border-radius:var(--radius-input,62px);box-sizing:border-box;display:flex;gap:0;height:52px;overflow:hidden;padding:6px 6px 6px 16px;position:relative;-webkit-transform:translateZ(0);transform:translateZ(0);transition:all var(--duration-fast,.15s) ease;will-change:transform;z-index:5}.ai-chat-input-wrapper.multiline{border-radius:14px!important;min-height:64px;padding:10px 10px 10px 14px}.ai-chat-widget.dark .ai-chat-input-wrapper{background:var(--input-bg,#4a4846);border-color:var(--input-border,#5d5b5b);border-width:.7px;box-shadow:var(--shadow-input,0 0 10px rgba(0,0,0,.15))}.ai-chat-input-wrapper:focus-within{border-color:var(--text-muted,#a1a1aa)}.ai-chat-input{word-wrap:break-word!important;background:transparent!important;border:none!important;border-radius:0!important;box-shadow:none!important;box-sizing:border-box!important;color:var(--input-text,#000)!important;flex:1!important;font-family:inherit!important;font-size:16px!important;height:40px!important;line-height:20px!important;margin:0!important;max-height:40px!important;min-height:40px!important;min-width:0!important;outline:none!important;overflow-wrap:anywhere!important;overflow-x:hidden!important;overflow-y:auto!important;padding:10px var(--space-sm,8px)!important;resize:none!important;white-space:pre-wrap!important;width:0!important;word-break:break-word!important}.ai-chat-widget.dark .ai-chat-input{color:var(--input-text,#fff)}.ai-chat-input::placeholder{color:var(--text-placeholder,#a1a1aa)}.ai-chat-widget.dark .ai-chat-input::placeholder{color:var(--text-placeholder,#52525b)}.ai-chat-file-button{align-items:center;align-self:center;background:transparent;border:none;color:var(--text-placeholder);cursor:pointer;display:flex;flex-shrink:0;height:28px;justify-content:center;padding:0;transition:color var(--duration-fast) ease;width:28px}.ai-chat-file-button:hover{color:var(--text-secondary)}.ai-chat-file-button:disabled{cursor:not-allowed;opacity:.5}.ai-chat-send-button{-webkit-font-smoothing:subpixel-antialiased;-moz-osx-font-smoothing:auto;align-items:center;align-self:center;-webkit-backface-visibility:hidden;backface-visibility:hidden;background:var(--send-button-background,var(--primary-color,var(--button-color,var(--btn-primary-bg,#151515))));border:none;border-radius:50%;color:var(--send-button-icon-color,var(--button-icon-color,var(--btn-primary-text,#f4f4f4)));cursor:pointer;display:flex;flex-shrink:0;height:40px;justify-content:center;min-height:40px;min-width:40px;padding:0;-webkit-transform:translateZ(0);transform:translateZ(0);transition:all var(--duration-fast,.15s) ease;width:40px;will-change:transform,opacity}.ai-chat-widget.dark .ai-chat-send-button{background:var(--send-button-background,var(--primary-color,var(--button-color,var(--btn-primary-bg,#fff))));color:var(--send-button-icon-color,var(--button-icon-color,var(--btn-primary-text,#312f2d)))}.ai-chat-send-button.active{background:var(--send-button-background,var(--primary-color,var(--button-color,var(--btn-primary-bg,#151515))));color:var(--send-button-icon-color,var(--button-icon-color,var(--btn-primary-text,#f4f4f4)))}.ai-chat-widget.dark .ai-chat-send-button.active{background:var(--send-button-background,var(--primary-color,var(--button-color,var(--btn-primary-bg,#fff))));color:var(--send-button-icon-color,var(--button-icon-color,var(--btn-primary-text,#312f2d)))}.ai-chat-send-button:hover:not(:disabled){opacity:.8}.ai-chat-send-button:active:not(:disabled){transform:scale(.95)}.ai-chat-send-button:disabled{cursor:not-allowed;opacity:.3}.ai-chat-file-list{display:flex;flex-wrap:wrap;gap:var(--space-sm);padding:var(--space-sm) var(--space-sm)}.ai-chat-file-item{align-items:center;background:rgba(0,0,0,.05);border-radius:6px;display:flex;font-size:var(--text-xs);gap:var(--space-sm);padding:6px 10px}.ai-chat-file-extension{background:var(--btn-primary-bg);border-radius:3px;color:var(--btn-primary-text);display:inline-block;font-size:10px;font-weight:var(--font-weight-semibold);min-width:40px;padding:2px 6px;text-align:center;text-transform:uppercase}.ai-chat-file-info{display:flex;flex:1;flex-direction:column;gap:2px;min-width:0}.ai-chat-file-name{font-weight:var(--font-weight-medium);max-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ai-chat-file-size{color:var(--text-muted);font-size:10px;opacity:.7}.ai-chat-file-remove{align-items:center;background:none;border:none;color:inherit;cursor:pointer;display:flex;justify-content:center;opacity:.5;padding:var(--space-xs);transition:opacity var(--duration-fast) ease}.ai-chat-file-remove:hover{opacity:1}.ai-chat-data-policy{bottom:2px;color:var(--text-muted,#71717a);font-size:9px;left:0;line-height:1.4;opacity:.5;pointer-events:auto;position:absolute;right:0;text-align:center}.ai-chat-widget.dark .ai-chat-data-policy{color:var(--text-muted,#a1a1aa)}.ai-chat-data-policy-link{background:none;border:none;color:var(--text-muted,#71717a);cursor:pointer;font-family:inherit;font-size:inherit;margin:0;padding:0;text-decoration:underline;text-underline-offset:2px;transition:color .15s ease}.ai-chat-data-policy-link:hover{color:var(--text-secondary,#52525b)}.ai-chat-widget.dark .ai-chat-data-policy-link{color:var(--text-muted,#a1a1aa)}.ai-chat-widget.dark .ai-chat-data-policy-link:hover{color:var(--text-secondary,#d4d4d8)}.ai-chat-page-disclaimer{align-items:center;color:var(--text-muted,#71717a);display:flex;font-size:10px;gap:4px;justify-content:center;line-height:1.4;opacity:.7;padding:8px 16px;text-align:center}.ai-chat-widget.dark .ai-chat-page-disclaimer{color:var(--text-muted,#a1a1aa)}.ai-chat-page-disclaimer-link{background:none;border:none;color:var(--text-muted,#71717a);cursor:pointer;font-family:inherit;font-size:inherit;margin:0;padding:0;text-decoration:underline;text-underline-offset:2px;transition:color .15s ease}.ai-chat-page-disclaimer-link:hover{color:var(--text-secondary,#52525b)}.ai-chat-widget.dark .ai-chat-page-disclaimer-link{color:var(--text-muted,#a1a1aa)}.ai-chat-widget.dark .ai-chat-page-disclaimer-link:hover{color:var(--text-secondary,#d4d4d8)}.ai-chat-messages{-webkit-overflow-scrolling:touch;-ms-overflow-style:none;align-items:stretch;background:var(--bg-primary,#fff);display:flex;flex:1;flex-direction:column;gap:var(--space-md,16px);justify-content:flex-start;min-height:0;overflow-x:hidden;overflow-y:auto;padding:0 var(--space-md,16px) 100px;position:relative;scroll-behavior:smooth;scrollbar-width:none}.ai-chat-widget.dark .ai-chat-messages{background:var(--bg-primary,#18181b)}.ai-chat-messages::-webkit-scrollbar{display:none}.ai-chat-message{animation:ai-chat-message-slide-in .2s var(--spring-bounce);display:flex;flex-direction:column;max-width:90%}.ai-chat-message.user{align-items:flex-end;align-self:flex-end;animation:ai-chat-user-message-sent .35s var(--spring-bounce)}.ai-chat-message.assistant{align-items:flex-start;align-self:flex-start;max-width:100%;width:100%}.ai-chat-message.tool{align-self:stretch;max-width:none;padding:0}.ai-chat-message-content{word-wrap:break-word;border-radius:18px;font-size:var(--text-md,15px);line-height:var(--line-height-relaxed,1.6);max-width:100%;overflow-wrap:break-word;padding:8px 14px;word-break:break-word}.ai-chat-message.user .ai-chat-message-content{background:var(--user-bg,#f4f3f0);border-radius:18px;color:var(--user-text,#000)}.ai-chat-widget.dark .ai-chat-message.user .ai-chat-message-content{background:var(--user-bg,#484848);color:var(--user-text,#fff)}.ai-chat-message.assistant .ai-chat-message-content{background:var(--agent-bg,transparent);box-sizing:border-box;color:var(--agent-text,#000);padding:0;width:100%}.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content{color:var(--agent-text,#fff)}.ai-chat-message-meta{align-items:center;display:flex;gap:var(--space-sm,8px);margin-top:var(--space-xs,4px);padding:0 var(--space-xs,4px)}.ai-chat-message.user .ai-chat-message-meta{justify-content:flex-end}.ai-chat-message.assistant .ai-chat-message-meta{justify-content:flex-start}.ai-chat-message-timestamp{color:var(--text-muted,#71717a);font-size:var(--text-xs,12px)}.ai-chat-welcome{animation:ai-chat-welcome-fade-in .3s var(--spring-smooth);display:flex;flex-direction:column;gap:var(--space-md,16px);padding:var(--space-lg,24px) 0}.ai-chat-welcome-title{color:var(--text-primary,#3e3e3e);font-size:var(--text-2xl,28px);font-weight:var(--font-weight-bold,700);line-height:var(--line-height-tight,1.3)}.ai-chat-widget.dark .ai-chat-welcome-title{color:var(--text-primary,#fff)}.ai-chat-welcome-text{color:var(--text-secondary,#000);font-size:var(--text-md,15px);line-height:var(--line-height-relaxed,1.6);max-width:100%}.ai-chat-widget.dark .ai-chat-welcome-text{color:var(--text-secondary,#fff)}.ai-chat-typing{align-items:center;display:flex;gap:var(--space-xs,4px);padding:var(--space-sm,8px) var(--space-md,16px)}.ai-chat-typing-dot{animation:ai-chat-typing-bounce 1.4s ease-in-out infinite both;background:var(--text-muted,#71717a);border-radius:50%;height:8px;width:8px}.ai-chat-typing-dot:first-child{animation-delay:-.32s}.ai-chat-typing-dot:nth-child(2){animation-delay:-.16s}.ai-chat-typing-dot:nth-child(3){animation-delay:0s}@keyframes ai-chat-typing-bounce{0%,80%,to{opacity:.4;transform:scale(.6)}40%{opacity:1;transform:scale(1)}}.ai-chat-scroll-button{align-items:center;background:var(--bg-secondary,#f4f4f5);border:1px solid var(--border-subtle,rgba(0,0,0,.1));border-radius:50%;bottom:80px;box-shadow:0 2px 8px rgba(0,0,0,.1);color:var(--text-secondary,#71717a);cursor:pointer;display:flex;height:36px;justify-content:center;left:50%;opacity:0;pointer-events:none;position:absolute;transform:translateX(-50%);transition:background .15s ease,box-shadow .15s ease,opacity .15s ease,visibility .15s ease;visibility:hidden;width:36px;z-index:15}.ai-chat-scroll-button.visible{opacity:1;pointer-events:auto;visibility:visible}.ai-chat-scroll-button:hover{background:var(--bg-tertiary,#e4e4e7);box-shadow:0 4px 12px rgba(0,0,0,.15)}.ai-chat-scroll-button:active{background:var(--bg-tertiary,#d4d4d8)}.ai-chat-widget.dark .ai-chat-scroll-button{background:var(--bg-secondary,#3f3f46);border-color:var(--border-subtle,hsla(0,0%,100%,.1));box-shadow:0 2px 8px rgba(0,0,0,.3);color:var(--text-secondary,#a1a1aa)}.ai-chat-widget.dark .ai-chat-scroll-button:hover{background:var(--bg-tertiary,#52525b);box-shadow:0 4px 12px rgba(0,0,0,.4)}.ai-chat-error{background:var(--bg-secondary);border-radius:var(--radius-chat-bubble);color:var(--text-primary);font-size:var(--text-md);margin:0 auto;padding:10px var(--space-md)}.ai-chat-message.assistant .ai-chat-message-content p{margin:0 0 var(--space-sm) 0}.ai-chat-message.assistant .ai-chat-message-content p:last-child{margin-bottom:0}.ai-chat-message.assistant .ai-chat-message-content h1{font-size:1.5em}.ai-chat-message.assistant .ai-chat-message-content h1,.ai-chat-message.assistant .ai-chat-message-content h2{color:var(--text-primary,#3e3e3e);font-weight:var(--font-weight-bold,700);line-height:var(--line-height-tight,1.3);margin:var(--space-md,16px) 0 var(--space-sm,8px) 0}.ai-chat-message.assistant .ai-chat-message-content h2{font-size:1.3em}.ai-chat-message.assistant .ai-chat-message-content h3{color:var(--text-primary,#3e3e3e);font-size:1.15em;font-weight:var(--font-weight-semibold,600);line-height:var(--line-height-tight,1.3);margin:var(--space-sm,8px) 0 var(--space-xs,4px) 0}.ai-chat-message.assistant .ai-chat-message-content h4,.ai-chat-message.assistant .ai-chat-message-content h5,.ai-chat-message.assistant .ai-chat-message-content h6{color:var(--text-primary,#3e3e3e);font-size:1em;font-weight:var(--font-weight-semibold,600);line-height:var(--line-height-tight,1.3);margin:var(--space-sm,8px) 0 var(--space-xs,4px) 0}.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content h1,.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content h2,.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content h3,.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content h4,.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content h5,.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content h6{color:var(--text-primary,#fff)}.ai-chat-message.assistant .ai-chat-message-content>h1:first-child,.ai-chat-message.assistant .ai-chat-message-content>h2:first-child,.ai-chat-message.assistant .ai-chat-message-content>h3:first-child,.ai-chat-message.assistant .ai-chat-message-content>h4:first-child,.ai-chat-message.assistant .ai-chat-message-content>h5:first-child,.ai-chat-message.assistant .ai-chat-message-content>h6:first-child{margin-top:0}.ai-chat-message.assistant .ai-chat-message-content ul{list-style-type:disc;margin:var(--space-sm,8px) 0;padding-left:var(--space-lg,24px)}.ai-chat-message.assistant .ai-chat-message-content ul ul{list-style-type:circle;margin:var(--space-xs,4px) 0}.ai-chat-message.assistant .ai-chat-message-content ul ul ul{list-style-type:square}.ai-chat-message.assistant .ai-chat-message-content ol{list-style-type:decimal;margin:var(--space-sm,8px) 0;padding-left:var(--space-lg,24px)}.ai-chat-message.assistant .ai-chat-message-content ol ol{list-style-type:lower-alpha;margin:var(--space-xs,4px) 0}.ai-chat-message.assistant .ai-chat-message-content ol ol ol{list-style-type:lower-roman}.ai-chat-message.assistant .ai-chat-message-content li{margin-bottom:var(--space-xs,4px);padding-left:var(--space-xs,4px)}.ai-chat-message.assistant .ai-chat-message-content li:last-child{margin-bottom:0}.ai-chat-message.assistant .ai-chat-message-content li>ol,.ai-chat-message.assistant .ai-chat-message-content li>ul{margin-top:var(--space-xs,4px)}.ai-chat-message.assistant .ai-chat-message-content code{background:rgba(0,0,0,.05);border-radius:var(--radius-sm);font-family:SF Mono,Monaco,Cascadia Code,monospace;font-size:.9em;padding:2px 6px}.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content code{background:hsla(0,0%,100%,.1)}.ai-chat-message.assistant .ai-chat-message-content pre{background:rgba(0,0,0,.05);border-radius:var(--radius-md);margin:var(--space-sm) 0;overflow-x:auto;padding:var(--space-sm)}.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content pre{background:hsla(0,0%,100%,.05)}.ai-chat-message.assistant .ai-chat-message-content pre code{background:transparent;padding:0}.ai-chat-message.assistant .ai-chat-message-content a{color:var(--btn-primary-bg);text-decoration:underline}.ai-chat-message.assistant .ai-chat-message-content strong{font-weight:var(--font-weight-semibold)}.ai-chat-message.assistant .ai-chat-message-content blockquote{border-left:3px solid var(--border-default);color:var(--text-muted);margin:var(--space-sm) 0;padding-left:var(--space-md)}.ai-chat-message.assistant .ai-chat-message-content hr{border:none;border-top:1px solid var(--border-subtle,rgba(0,0,0,.1));margin:var(--space-lg,24px) 0}.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content hr{border-top-color:var(--border-subtle,hsla(0,0%,100%,.1))}.ai-chat-message.assistant .ai-chat-message-content .table-wrapper{border:1px solid var(--border-subtle,rgba(0,0,0,.1));border-radius:var(--radius-md,8px);box-sizing:border-box;display:block;margin:var(--space-sm) var(--space-sm);max-width:100%;overflow:hidden;width:auto}.ai-chat-message.assistant .ai-chat-message-content .table-scroll{max-width:100%;overflow-x:auto;overflow-y:hidden;width:100%}.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content .table-wrapper{border-color:var(--border-subtle,hsla(0,0%,100%,.1))}.ai-chat-message.assistant .ai-chat-message-content table{border-collapse:collapse;font-size:var(--text-sm);min-width:100%;width:max-content}.ai-chat-message.assistant .ai-chat-message-content td,.ai-chat-message.assistant .ai-chat-message-content th{border-bottom:1px solid var(--border-subtle,rgba(0,0,0,.1));border-right:1px solid var(--border-subtle,rgba(0,0,0,.1));padding:var(--space-sm);text-align:left}.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content td,.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content th{border-color:var(--border-subtle,hsla(0,0%,100%,.1))}.ai-chat-message.assistant .ai-chat-message-content td:last-child,.ai-chat-message.assistant .ai-chat-message-content th:last-child{border-right:none}.ai-chat-message.assistant .ai-chat-message-content tr:last-child td{border-bottom:none}.ai-chat-message.assistant .ai-chat-message-content th{background:rgba(0,0,0,.03);font-weight:var(--font-weight-semibold);white-space:nowrap}.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content th{background:hsla(0,0%,100%,.05)}.ai-chat-message.assistant .ai-chat-message-content tbody tr:nth-child(2n){background:rgba(0,0,0,.02)}.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content tbody tr:nth-child(2n){background:hsla(0,0%,100%,.03)}.ai-chat-message.assistant.streaming .ai-chat-message-content>ol:last-child>li:last-child,.ai-chat-message.assistant.streaming .ai-chat-message-content>p:last-child,.ai-chat-message.assistant.streaming .ai-chat-message-content>ul:last-child>li:last-child{animation:ai-chat-text-fade-in .3s ease-out}@keyframes ai-chat-text-fade-in{0%{opacity:0}to{opacity:1}}.ai-chat-suggested-questions{align-self:flex-end;margin:0;padding:16px 0 0;width:100%}.ai-chat-suggested-questions-list{align-items:center;display:flex;flex-wrap:wrap;gap:6px;justify-content:flex-end}.ai-chat-suggested-question{align-items:center;background:transparent;border:1px solid var(--border-default,#d4d4d8);border-radius:var(--radius-preset-badge,18px);color:var(--text-primary,#18181b);cursor:pointer;display:inline-flex;font-size:14px;font-weight:400;gap:6px;justify-content:center;line-height:1.3;max-width:100%;overflow:hidden;padding:8px 14px;text-overflow:ellipsis;transition:background .15s ease,border-color .15s ease,transform .1s ease;white-space:nowrap}.ai-chat-widget.dark .ai-chat-suggested-question{background:transparent;border-color:var(--border-subtle,#52525b);color:var(--text-primary,#fafafa)}.ai-chat-suggested-question-text{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ai-chat-suggested-question:hover{background:var(--bg-hover,#f4f4f5);border-color:var(--border-default,#d4d4d8)}.ai-chat-widget.dark .ai-chat-suggested-question:hover{background:var(--bg-hover,#3f3f46);border-color:var(--border-subtle,#52525b)}.ai-chat-suggested-question:active{transform:scale(.98)}.ai-chat-suggested-question.action-type{border:none}.ai-chat-suggested-question.action-type,.ai-chat-widget.dark .ai-chat-suggested-question.action-type{background:var(--primary-color,var(--button-color,#ef4444));color:var(--button-icon-color,#fff)}.ai-chat-suggested-question.action-type:hover{background:var(--primary-color,var(--button-color,#ef4444));opacity:.9}.ai-chat-suggested-question-icon{align-items:center;display:flex;flex-shrink:0;justify-content:center}.ai-chat-suggested-question:not(.action-type) .ai-chat-suggested-question-icon{display:none}.ai-chat-widget-container.container-mode.trigger-input-bar .ai-chat-suggested-questions{box-sizing:border-box;margin:0;padding:16px 0 0;width:100%}.ai-chat-widget-container.container-mode.trigger-input-bar .ai-chat-suggested-questions-list{box-sizing:border-box;display:flex;flex-wrap:wrap;gap:6px}.ai-chat-widget-container.container-mode.trigger-input-bar.bottom-right .ai-chat-suggested-questions-list,.ai-chat-widget-container.container-mode.trigger-input-bar.top-right .ai-chat-suggested-questions-list{justify-content:flex-end;margin-left:auto;margin-right:0;padding-right:0}.ai-chat-widget-container.container-mode.trigger-input-bar.bottom-left .ai-chat-suggested-questions-list,.ai-chat-widget-container.container-mode.trigger-input-bar.top-left .ai-chat-suggested-questions-list{justify-content:flex-start;margin-left:0;margin-right:auto;padding-left:0}.ai-chat-widget-container.container-mode.trigger-input-bar.size-small .ai-chat-suggested-questions-list{max-width:calc(100% - 132px);width:348px}.ai-chat-widget-container.container-mode.trigger-input-bar.size-medium .ai-chat-suggested-questions-list{max-width:calc(100% - 132px);width:408px}.ai-chat-widget-container.container-mode.trigger-input-bar.size-large .ai-chat-suggested-questions-list{max-width:calc(100% - 132px);width:528px}.ai-chat-follow-up-suggestions{box-sizing:border-box;margin:0;padding:8px 16px 0;width:100%}.ai-chat-follow-up-list{align-items:flex-end;display:flex;flex-direction:column;gap:6px}.ai-chat-follow-up-item{align-items:center;animation:followUpFadeIn .25s ease-out forwards;background:var(--primary-color,var(--button-color,#07f));border:none;border-radius:var(--radius-preset-badge,18px);color:var(--button-icon-color,#fff);cursor:pointer;display:inline-flex;font-size:14px;font-weight:400;gap:6px;justify-content:center;line-height:1.3;max-width:100%;overflow:hidden;padding:8px 14px;text-overflow:ellipsis;white-space:nowrap}@keyframes followUpFadeIn{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}.ai-chat-widget.dark .ai-chat-follow-up-item{background:var(--primary-color,var(--button-color,#07f));color:var(--button-icon-color,#fff)}.ai-chat-follow-up-item:hover{opacity:.9}.ai-chat-follow-up-item:active{transform:scale(.98)}.ai-chat-follow-up-item.question-type{background:transparent;border:1px solid var(--border-default,#d4d4d8);color:var(--text-primary,#18181b)}.ai-chat-widget.dark .ai-chat-follow-up-item.question-type,.dark .ai-chat-follow-up-item.question-type,[data-color-mode=dark] .ai-chat-follow-up-item.question-type,[data-theme=dark] .ai-chat-follow-up-item.question-type{background:transparent;border:1px solid var(--border-subtle,#52525b);color:var(--text-primary,#fafafa)}@media (prefers-color-scheme:dark){.ai-chat-follow-up-item.question-type{background:transparent;border:1px solid var(--border-subtle,#52525b);color:var(--text-primary,#fafafa)}}.ai-chat-follow-up-item.question-type:hover{background:var(--bg-hover,#f4f4f5);opacity:1}.ai-chat-widget.dark .ai-chat-follow-up-item.question-type:hover,.dark .ai-chat-follow-up-item.question-type:hover,[data-color-mode=dark] .ai-chat-follow-up-item.question-type:hover,[data-theme=dark] .ai-chat-follow-up-item.question-type:hover{background:var(--bg-hover,#3f3f46);opacity:1}@media (prefers-color-scheme:dark){.ai-chat-follow-up-item.question-type:hover{background:var(--bg-hover,#3f3f46);opacity:1}}.ai-chat-follow-up-item.action-type{background:var(--primary-color,var(--button-color,#07f));border:none;color:var(--button-icon-color,#fff)}.ai-chat-follow-up-icon{align-items:center;display:flex;flex-shrink:0;justify-content:center}.ai-chat-follow-up-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ai-chat-feedback-buttons{align-items:center;display:flex;gap:var(--space-xs)}.ai-chat-feedback{align-items:center;display:inline-flex;gap:0;height:20px}.ai-chat-feedback-button{align-items:center;background:transparent!important;border:none;border-radius:var(--radius-sm);color:var(--text-placeholder);cursor:pointer;display:flex;font-size:var(--text-sm);height:20px;justify-content:center;padding:var(--space-xs);transition:all var(--duration-fast) var(--spring-bounce)}.ai-chat-feedback-button:hover:not(:disabled){background:none!important;color:var(--text-secondary)}.ai-chat-feedback-button:active:not(:disabled){transform:scale(.9)}.ai-chat-feedback-button:disabled{cursor:not-allowed;opacity:.4}.ai-chat-feedback-button.active{background:none!important;color:var(--text-primary)}.ai-chat-feedback.submitted{align-items:center;animation:ai-chat-feedback-morph .3s var(--spring-bounce);gap:var(--space-xs)}.ai-chat-feedback-message{align-items:center;display:flex;gap:4px;margin-left:var(--space-xxs)}.ai-chat-feedback-checkmark{animation:ai-chat-checkmark-pop .3s var(--spring-bounce);color:#10b981;font-size:var(--text-md);font-weight:700}.ai-chat-feedback-text{color:#10b981;font-size:var(--text-xs);font-weight:var(--font-weight-medium)}.ai-chat-history-panel{background:var(--bg-primary,#fff);display:flex;flex:1;flex-direction:column;overflow:hidden}.ai-chat-widget.dark .ai-chat-history-panel{background:var(--bg-primary,#18181b)}.ai-chat-history-empty,.ai-chat-history-loading{align-items:center;color:var(--text-muted);display:flex;flex:1;font-size:var(--text-sm);justify-content:center;padding:var(--space-lg);text-align:center}.ai-chat-history-list{-ms-overflow-style:none;display:flex;flex:1;flex-direction:column;gap:var(--space-sm);overflow-y:auto;padding:var(--space-xs) var(--space-md) 120px;scrollbar-width:none}.ai-chat-history-list::-webkit-scrollbar{display:none}.ai-chat-history-list.exiting{animation:ai-chat-history-exit .22s var(--spring-smooth) forwards}.ai-chat-history-item{align-items:center;background:var(--user-bg,#f4f4f5);border-radius:var(--radius-history-item,15px);display:flex;flex:0 0 auto;flex-direction:row;height:var(--history-item-height,36px);margin:0;overflow:hidden;transition:background var(--duration-fast,.15s) ease;width:100%}.ai-chat-history-item-content{align-items:center;background:transparent;border:none;cursor:pointer;display:flex;flex:1;flex-direction:row;height:100%;min-width:0;padding:0 var(--space-xs,4px) 0 var(--space-md,16px);text-align:left}.ai-chat-widget.dark .ai-chat-history-item{background:var(--user-bg,#27272a)}.ai-chat-history-item:hover{background:var(--user-bg-hover,#e5e7eb)}.ai-chat-widget.dark .ai-chat-history-item:hover{background:var(--user-bg-hover,#3f3f46)}.ai-chat-history-item.active{background:var(--user-bg-hover,#e5e7eb)}.ai-chat-widget.dark .ai-chat-history-item.active{background:var(--user-bg-hover,#3f3f46)}.ai-chat-history-item-preview{color:var(--text-primary,#18181b);flex:1;font-size:var(--text-sm,14px);font-weight:var(--font-weight-medium,500);line-height:var(--line-height-normal,1.4);margin-bottom:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ai-chat-widget.dark .ai-chat-history-item-preview{color:var(--text-primary,#fafafa)}.ai-chat-history-item.active .ai-chat-history-item-preview{font-weight:var(--font-weight-medium)}.ai-chat-history-item-meta{display:none}.ai-chat-history-item-delete{align-items:center;background:transparent;border:none;border-radius:var(--radius-sm,6px);color:var(--text-muted,#71717a);cursor:pointer;display:flex;flex-shrink:0;height:24px;justify-content:center;margin-left:auto;margin-right:var(--space-xs,4px);opacity:0;transition:opacity var(--duration-fast,.15s) ease,background var(--duration-fast,.15s) ease,color var(--duration-fast,.15s) ease;width:24px}.ai-chat-history-item-delete svg{height:14px;width:14px}.ai-chat-history-item:hover .ai-chat-history-item-delete{opacity:1}.ai-chat-history-item-delete:hover{background:rgba(239,68,68,.1);color:#ef4444}.ai-chat-widget.dark .ai-chat-history-item-delete:hover{background:rgba(239,68,68,.2);color:#f87171}.ai-chat-tool-row{align-items:center;display:flex;gap:10px;margin:2px 0;padding:0}.ai-chat-tool-gear{color:#1f2937;flex-shrink:0;height:20px;width:20px}.ai-chat-tool-gear.spinning{animation:ai-chat-gear-spin 1.5s linear infinite}.ai-chat-tool-badges{align-items:center;display:flex;flex-wrap:wrap;gap:8px}.ai-chat-tool-badge{align-items:center;background:#e5e7eb;border:1px solid #d1d5db;border-radius:var(--radius-action-badge,8px);color:#1f2937;display:inline-flex;font-size:12px;font-weight:500;gap:4px;line-height:1.2;padding:5px 12px;transition:all .2s ease;white-space:nowrap}.ai-chat-tool-badge.loading{animation:ai-chat-tool-gradient 2s linear infinite;background:linear-gradient(90deg,var(--tool-loading-bg-1,#e0e0e0) 0,var(--tool-loading-bg-2,#f0f0f0) 25%,var(--tool-loading-bg-3,#fff) 50%,var(--tool-loading-bg-2,#f0f0f0) 75%,var(--tool-loading-bg-1,#e0e0e0) 100%);background-size:200% 100%;color:var(--tool-loading-text,#1a1a1a);position:relative}.ai-chat-widget:not(.dark) .ai-chat-tool-badge.loading{--tool-loading-bg-1:#2a2a2a;--tool-loading-bg-2:#3a3a3a;--tool-loading-bg-3:#4a4a4a;--tool-loading-text:#fff}.ai-chat-tool-badge.completed{background:#e5e7eb;border:1px solid #d1d5db;color:#1f2937}.ai-chat-widget.dark .ai-chat-tool-badge,.ai-chat-widget[data-theme=dark] .ai-chat-tool-badge,.chakra-ui-dark .ai-chat-tool-badge,.dark .ai-chat-tool-badge,[data-theme=dark] .ai-chat-tool-badge,html.dark .ai-chat-tool-badge{background:hsla(0,0%,100%,.12);border:1px solid hsla(0,0%,100%,.2);color:hsla(0,0%,100%,.9)}.ai-chat-widget.dark .ai-chat-tool-gear,.ai-chat-widget[data-theme=dark] .ai-chat-tool-gear,.chakra-ui-dark .ai-chat-tool-gear,.dark .ai-chat-tool-gear,[data-theme=dark] .ai-chat-tool-gear,html.dark .ai-chat-tool-gear{color:#fff}.ai-chat-tool-badge.error{background:var(--tool-error-bg,rgba(239,68,68,.15));color:var(--tool-error-text,#ef4444)}.ai-chat-tool-badge .ai-chat-tool-check{color:#fff;flex-shrink:0}.ai-chat-tool-badge .ai-chat-tool-error{color:#ef4444;flex-shrink:0}.tool-name{font-weight:500;line-height:1.2;white-space:nowrap}.ai-chat-tool-action{box-sizing:border-box;padding:0;width:100%}@keyframes ai-chat-skeleton-pulse{0%,to{opacity:.4}50%{opacity:.7}}.ai-chat-action-skeleton-item{animation:ai-chat-skeleton-pulse 1.5s ease-in-out infinite;background:var(--bg-secondary,#e5e7eb)}.ai-chat-widget.dark .ai-chat-action-skeleton-item,.chakra-ui-dark .ai-chat-action-skeleton-item,.dark .ai-chat-action-skeleton-item,[data-theme=dark] .ai-chat-action-skeleton-item{background:hsla(0,0%,100%,.1)}.ai-chat-action-skeleton-content{display:flex;flex-direction:column;gap:16px}.ai-chat-action-skeleton-header{align-items:center;display:flex;gap:10px}.ai-chat-action-skeleton-box{background:rgba(0,0,0,.08);border-radius:10px;display:flex;flex-direction:column;gap:8px;padding:14px}.ai-chat-widget.dark .ai-chat-action-skeleton-box,.chakra-ui-dark .ai-chat-action-skeleton-box,.dark .ai-chat-action-skeleton-box,[data-theme=dark] .ai-chat-action-skeleton-box{background:rgba(0,0,0,.25)}.ai-chat-action-card{--action-accent:var(--primary-color,#3b82f6);background:var(--bg-secondary,#f9fafb);border:1px solid var(--border-subtle,rgba(0,0,0,.06));border-radius:12px;box-sizing:border-box;margin-top:4px;padding:16px;transition:all .2s ease;width:100%}.ai-chat-widget.dark .ai-chat-action-card,.chakra-ui-dark .ai-chat-action-card,.dark .ai-chat-action-card,[data-theme=dark] .ai-chat-action-card{background:hsla(0,0%,100%,.05);border-color:hsla(0,0%,100%,.1)}.ai-chat-action-booked{background:var(--bg-secondary,#f9fafb);border:1px solid var(--border-subtle,rgba(0,0,0,.06))}.ai-chat-widget.dark .ai-chat-action-booked,.chakra-ui-dark .ai-chat-action-booked,.dark .ai-chat-action-booked,[data-theme=dark] .ai-chat-action-booked{background:var(--bg-secondary,#3a3a3a)}.ai-chat-action-header{align-items:center;color:var(--text-primary,#3e3e3e);display:flex;font-size:var(--text-md,15px);font-weight:var(--font-weight-semibold,600);gap:var(--space-sm,8px);margin-bottom:var(--space-md,16px)}.ai-chat-widget.dark .ai-chat-action-header,.chakra-ui-dark .ai-chat-action-header,.dark .ai-chat-action-header,[data-theme=dark] .ai-chat-action-header{color:var(--text-primary,#fff)}.ai-chat-action-icon{color:var(--action-accent,var(--primary-color,#3b82f6));flex-shrink:0;height:20px;width:20px}.ai-chat-action-success-icon-wrapper{align-items:center;background:var(--action-accent,var(--primary-color,#22c55e));border-radius:50%;color:#fff;display:flex;flex-shrink:0;height:24px;justify-content:center;width:24px}.ai-chat-action-icon-success{color:currentColor;height:14px;width:14px}.ai-chat-action-detail-box{background:var(--bg-primary,#fff);border:1px solid var(--border-subtle,rgba(0,0,0,.08));border-radius:var(--radius-md,8px);display:flex;flex-direction:column;gap:4px;padding:12px 16px}.ai-chat-widget.dark .ai-chat-action-detail-box,.chakra-ui-dark .ai-chat-action-detail-box,.dark .ai-chat-action-detail-box,[data-theme=dark] .ai-chat-action-detail-box{background:rgba(0,0,0,.2);border-color:hsla(0,0%,100%,.05)}.ai-chat-action-label-small{color:var(--text-muted,#71717a);font-size:11px;font-weight:600;letter-spacing:.5px;text-transform:uppercase}.ai-chat-action-value-large{color:var(--text-primary,#3e3e3e);font-size:15px;font-weight:500}.ai-chat-widget.dark .ai-chat-action-value-large,.chakra-ui-dark .ai-chat-action-value-large,.dark .ai-chat-action-value-large,[data-theme=dark] .ai-chat-action-value-large{color:#fff}.ai-chat-action-body{display:flex;flex-direction:column;gap:var(--space-md,16px)}.ai-chat-action-close-btn{align-items:center;background:transparent;border:none;border-radius:6px;color:var(--ai-chat-fg-muted,#6b7280);cursor:pointer;display:flex;height:28px;justify-content:center;padding:0;position:absolute;right:12px;top:12px;transition:all .15s ease;width:28px;z-index:10}.ai-chat-action-close-btn:hover{background:var(--ai-chat-bg-muted,#f3f4f6);color:var(--ai-chat-fg,#1f2937)}.ai-chat-action-close-btn:active{transform:scale(.95)}.ai-chat-action-close-btn:focus-visible{outline:2px solid var(--ai-chat-accent,#2563eb);outline-offset:2px}.ai-chat-action-card--closable,.ai-chat-form-card--closable{position:relative}.ai-chat-action-card--closable .ai-chat-action-header,.ai-chat-form-card--closable .ai-chat-form-card__header{padding-right:40px}.ai-chat-widget.dark .ai-chat-action-close-btn:hover,.chakra-ui-dark .ai-chat-action-close-btn:hover,.dark .ai-chat-action-close-btn:hover,[data-theme=dark] .ai-chat-action-close-btn:hover{background:hsla(0,0%,100%,.1);color:var(--ai-chat-fg,#f3f4f6)}.ai-chat-action-field{display:flex;flex-direction:column;gap:var(--space-xs,6px)}.ai-chat-action-label{color:var(--text-secondary,#6b7280);font-size:var(--text-sm,13px);font-weight:var(--font-weight-medium,500)}.ai-chat-widget.dark .ai-chat-action-label,.chakra-ui-dark .ai-chat-action-label,.dark .ai-chat-action-label,[data-theme=dark] .ai-chat-action-label{color:var(--text-secondary,#a1a1aa)}.ai-chat-action-input{background:var(--bg-primary,#fff);border:1px solid var(--border-default,#e5e7eb);border-radius:var(--radius-md,8px);color:var(--text-primary,#3e3e3e);font-size:var(--text-sm,13px);outline:none;padding:10px 12px;transition:border-color .2s ease,box-shadow .2s ease}.ai-chat-action-input:focus{border-color:var(--action-accent,var(--primary-color,#3b82f6));box-shadow:0 0 0 3px rgba(59,130,246,.1)}.ai-chat-action-input::placeholder{color:var(--text-muted,#9ca3af)}.ai-chat-widget.dark .ai-chat-action-input,.chakra-ui-dark .ai-chat-action-input,.dark .ai-chat-action-input,[data-theme=dark] .ai-chat-action-input{background:rgba(0,0,0,.2);border-color:hsla(0,0%,100%,.1);color:#fff}.ai-chat-widget.dark .ai-chat-action-input:focus,.chakra-ui-dark .ai-chat-action-input:focus,.dark .ai-chat-action-input:focus,[data-theme=dark] .ai-chat-action-input:focus{border-color:var(--action-accent,var(--primary-color,#3b82f6));box-shadow:0 0 0 3px rgba(59,130,246,.2)}.ai-chat-action-button{background:var(--action-accent,var(--primary-color,#3b82f6));border:none;border-radius:9999px;color:#fff;cursor:pointer;font-size:var(--text-sm,13px);font-weight:var(--font-weight-semibold,600);padding:12px 16px;transition:all .2s ease;width:100%}.ai-chat-action-button:hover:not(:disabled){opacity:.9;transform:translateY(-1px)}.ai-chat-action-button:disabled{cursor:not-allowed;opacity:.5}.ai-chat-action-link-button{align-items:center;background:var(--action-accent,var(--primary-color,#3b82f6));border:none;border-radius:9999px;box-sizing:border-box;color:#fff;display:flex;font-size:14px;font-weight:600;gap:6px;justify-content:center;margin-top:8px;padding:12px;text-decoration:none;transition:all .2s ease;width:100%}.ai-chat-action-link-button:hover{opacity:.9;transform:translateY(-1px)}.ai-chat-action-error{background:rgba(239,68,68,.1);border-radius:var(--radius-md,8px);color:#dc2626;font-size:var(--text-sm,13px);padding:10px 12px}.ai-chat-widget.dark .ai-chat-action-error,.chakra-ui-dark .ai-chat-action-error,.dark .ai-chat-action-error,[data-theme=dark] .ai-chat-action-error{background:rgba(239,68,68,.2);color:#fca5a5}.ai-chat-action-hint{color:var(--text-muted,#9ca3af);font-size:var(--text-sm,13px);padding:var(--space-sm,8px);text-align:center}@media (max-width:480px){.ai-chat-action-card{padding:12px}.ai-chat-action-header{font-size:var(--text-sm,14px);margin-bottom:var(--space-sm,12px)}.ai-chat-action-button{font-size:13px;padding:10px 14px}.ai-chat-action-link-button{font-size:13px;padding:10px}}.ai-chat-link-preview{--action-accent:var(--primary-color,#3b82f6);background:var(--bg-secondary,#f4f4f4);border:1px solid var(--border-subtle,rgba(0,0,0,.08));border-radius:var(--radius-lg,12px);cursor:pointer;display:flex;flex-direction:column;margin-top:4px;overflow:hidden;padding:0!important;position:relative;transition:border-color .2s,box-shadow .2s,transform .2s}.ai-chat-link-preview:hover{border-color:var(--action-accent);box-shadow:0 2px 8px rgba(0,0,0,.1);transform:translateY(-1px)}.ai-chat-link-preview:focus{border-color:var(--action-accent);box-shadow:0 0 0 2px rgba(59,130,246,.2);outline:none}.ai-chat-widget.dark .ai-chat-link-preview,.chakra-ui-dark .ai-chat-link-preview,.dark .ai-chat-link-preview,[data-theme=dark] .ai-chat-link-preview{background:var(--bg-secondary,#3a3a3a);border-color:hsla(0,0%,100%,.08)}.ai-chat-widget.dark .ai-chat-link-preview:hover,.chakra-ui-dark .ai-chat-link-preview:hover,.dark .ai-chat-link-preview:hover,[data-theme=dark] .ai-chat-link-preview:hover{border-color:var(--action-accent);box-shadow:0 2px 12px rgba(0,0,0,.3)}.ai-chat-link-preview__image{aspect-ratio:1.91/1;background:var(--bg-muted,#e5e7eb);overflow:hidden;width:100%}.ai-chat-widget.dark .ai-chat-link-preview__image,.chakra-ui-dark .ai-chat-link-preview__image,.dark .ai-chat-link-preview__image,[data-theme=dark] .ai-chat-link-preview__image{background:hsla(0,0%,100%,.05)}.ai-chat-link-preview__image img{height:100%;object-fit:cover;width:100%}.ai-chat-link-preview__content{flex:1;padding:8px 10px}.ai-chat-link-preview__site{align-items:center;display:flex;gap:6px;margin-bottom:6px}.ai-chat-link-preview__favicon{border-radius:2px;flex-shrink:0;height:16px;width:16px}.ai-chat-link-preview__domain{color:var(--text-muted,#71717a);font-size:12px;letter-spacing:.5px;overflow:hidden;text-overflow:ellipsis;text-transform:uppercase;white-space:nowrap}.ai-chat-link-preview__title{-webkit-line-clamp:2;line-clamp:2;-webkit-box-orient:vertical;color:var(--text-primary,#3e3e3e);display:-webkit-box;font-size:15px;font-weight:600;line-height:1.3;margin:0 0 4px;overflow:hidden}.ai-chat-widget.dark .ai-chat-link-preview__title,.chakra-ui-dark .ai-chat-link-preview__title,.dark .ai-chat-link-preview__title,[data-theme=dark] .ai-chat-link-preview__title{color:#fff}.ai-chat-link-preview__description{-webkit-line-clamp:2;line-clamp:2;-webkit-box-orient:vertical;color:var(--text-muted,#71717a);display:-webkit-box;font-size:13px;line-height:1.4;margin:0;overflow:hidden}.ai-chat-link-preview__context{border-top:1px solid var(--border-subtle,rgba(0,0,0,.08));color:var(--text-muted,#71717a);font-size:12px;font-style:italic;margin:8px 0 0;padding-top:8px}.ai-chat-widget.dark .ai-chat-link-preview__context,.chakra-ui-dark .ai-chat-link-preview__context,.dark .ai-chat-link-preview__context,[data-theme=dark] .ai-chat-link-preview__context{border-color:hsla(0,0%,100%,.08)}.ai-chat-link-preview__arrow{align-items:center;background:var(--bg-primary,#fff);border-radius:50%;box-shadow:0 1px 3px rgba(0,0,0,.1);color:var(--text-muted,#71717a);display:flex;height:28px;justify-content:center;opacity:0;position:absolute;right:12px;top:12px;transition:opacity .2s,background .2s;width:28px}.ai-chat-link-preview:hover .ai-chat-link-preview__arrow{opacity:1}.ai-chat-widget.dark .ai-chat-link-preview__arrow,.chakra-ui-dark .ai-chat-link-preview__arrow,.dark .ai-chat-link-preview__arrow,[data-theme=dark] .ai-chat-link-preview__arrow{background:hsla(0,0%,100%,.1);color:#fff}.ai-chat-link-preview--error{border-color:rgba(239,68,68,.3)}.ai-chat-link-preview--error:hover{border-color:rgba(239,68,68,.5)}.ai-chat-link-preview__error-text{color:#dc2626;font-size:12px;margin:4px 0 0}.ai-chat-widget.dark .ai-chat-link-preview__error-text,.chakra-ui-dark .ai-chat-link-preview__error-text,.dark .ai-chat-link-preview__error-text,[data-theme=dark] .ai-chat-link-preview__error-text{color:#fca5a5}.ai-chat-link-preview-grid{display:grid;gap:12px}.ai-chat-link-preview-grid--single{grid-template-columns:1fr}.ai-chat-link-preview-grid--double{grid-template-columns:repeat(2,1fr)}.ai-chat-link-preview-grid--triple{grid-template-columns:repeat(3,1fr)}@media (max-width:768px){.ai-chat-link-preview-grid--triple{grid-template-columns:repeat(2,1fr)}}@media (max-width:480px){.ai-chat-link-preview-grid--double,.ai-chat-link-preview-grid--triple{grid-template-columns:1fr}}.ai-chat-video-player{--action-accent:var(--primary-color,#3b82f6);background:var(--bg-secondary,#f4f4f4);border-radius:var(--radius-lg,12px);display:flex;flex-direction:column;gap:0;margin-top:4px;overflow:hidden;padding:0!important}.ai-chat-widget.dark .ai-chat-video-player,.chakra-ui-dark .ai-chat-video-player,.dark .ai-chat-video-player,[data-theme=dark] .ai-chat-video-player{background:var(--bg-secondary,#3a3a3a)}.ai-chat-video-player__header{align-items:center;color:var(--action-accent,var(--primary-color,#3b82f6));display:flex;gap:8px}.ai-chat-video-player__title{color:var(--text-primary,#3e3e3e);font-size:14px;font-weight:600}.ai-chat-widget.dark .ai-chat-video-player__title,.chakra-ui-dark .ai-chat-video-player__title,.dark .ai-chat-video-player__title,[data-theme=dark] .ai-chat-video-player__title{color:#fff}.ai-chat-video-player__container{aspect-ratio:16/9;background:#000;border-radius:8px;overflow:hidden;position:relative;width:100%}.ai-chat-video-player__thumbnail{cursor:pointer;height:100%;position:relative;width:100%}.ai-chat-video-player__thumbnail img{height:100%;object-fit:cover;width:100%}.ai-chat-video-player__placeholder{align-items:center;background:linear-gradient(135deg,#1a1a2e,#16213e);cursor:pointer;display:flex;flex-direction:column;gap:8px;height:100%;justify-content:center;position:relative;width:100%}.ai-chat-video-player__click-text{color:hsla(0,0%,100%,.7);font-size:13px}.ai-chat-video-player__play-btn{align-items:center;background:rgba(0,0,0,.7);border:none;border-radius:50%;color:#fff;cursor:pointer;display:flex;height:64px;justify-content:center;left:50%;position:absolute;top:50%;transform:translate(-50%,-50%);transition:background .2s,transform .2s;width:64px}.ai-chat-video-player__placeholder .ai-chat-video-player__play-btn{left:auto;position:relative;top:auto;transform:none}.ai-chat-video-player__play-btn:hover{background:rgba(0,0,0,.9);transform:translate(-50%,-50%) scale(1.05)}.ai-chat-video-player__placeholder .ai-chat-video-player__play-btn:hover{transform:scale(1.05)}.ai-chat-video-player__provider-badge{background:rgba(0,0,0,.8);border-radius:4px;bottom:8px;color:#fff;font-size:11px;font-weight:600;letter-spacing:.5px;padding:4px 8px;position:absolute;right:8px;text-transform:uppercase}.ai-chat-video-player__iframe,.ai-chat-video-player__video{border:none;height:100%;left:0;position:absolute;top:0;width:100%}.ai-chat-video-player__error{align-items:center;background:rgba(239,68,68,.1);color:#dc2626;display:flex;font-size:13px;height:100%;justify-content:center;padding:16px;text-align:center;width:100%}.ai-chat-widget.dark .ai-chat-video-player__error,.chakra-ui-dark .ai-chat-video-player__error,.dark .ai-chat-video-player__error,[data-theme=dark] .ai-chat-video-player__error{background:rgba(239,68,68,.2);color:#fca5a5}.ai-chat-video-player__context{color:var(--text-muted,#71717a);font-size:12px;font-style:italic;margin-top:4px;padding:8px 12px 12px}@media (max-width:480px){.ai-chat-video-player__play-btn{height:56px;width:56px}.ai-chat-video-player__provider-badge{font-size:10px;padding:3px 6px}}.ai-chat-location-card{background:var(--bg-secondary,#fff);border:1px solid var(--border-subtle,rgba(0,0,0,.08));border-radius:12px;overflow:hidden;padding:0}.ai-chat-widget.dark .ai-chat-location-card,.chakra-ui-dark .ai-chat-location-card,.dark .ai-chat-location-card,[data-theme=dark] .ai-chat-location-card{background:hsla(0,0%,100%,.05);border-color:hsla(0,0%,100%,.1)}.ai-chat-location-card--compact{border-radius:10px}.ai-chat-location-card--error{color:var(--text-muted,#71717a);padding:16px;text-align:center}.ai-chat-location-card__map{background:var(--bg-muted,#f4f4f5);position:relative;width:100%}.ai-chat-widget.dark .ai-chat-location-card__map,.chakra-ui-dark .ai-chat-location-card__map,.dark .ai-chat-location-card__map,[data-theme=dark] .ai-chat-location-card__map{background:rgba(0,0,0,.2)}.ai-chat-location-card__map iframe{border:none;display:block;height:100%;width:100%}.ai-chat-location-card__content{padding:12px}.ai-chat-location-card--compact .ai-chat-location-card__content{padding:10px}.ai-chat-location-card__header{align-items:center;display:flex;flex-wrap:wrap;gap:8px;margin-bottom:8px}.ai-chat-location-card__name{color:var(--text-primary,#18181b);flex:1;font-size:16px;font-weight:600;margin:0;min-width:0}.ai-chat-widget.dark .ai-chat-location-card__name,.chakra-ui-dark .ai-chat-location-card__name,.dark .ai-chat-location-card__name,[data-theme=dark] .ai-chat-location-card__name{color:#fff}.ai-chat-location-card--compact .ai-chat-location-card__name{font-size:14px}.ai-chat-location-card__type{background:var(--bg-muted,#f4f4f5);border-radius:10px;color:var(--text-muted,#71717a);font-size:11px;font-weight:500;letter-spacing:.5px;padding:2px 8px;text-transform:uppercase}.ai-chat-widget.dark .ai-chat-location-card__type,.chakra-ui-dark .ai-chat-location-card__type,.dark .ai-chat-location-card__type,[data-theme=dark] .ai-chat-location-card__type{background:hsla(0,0%,100%,.1);color:hsla(0,0%,100%,.7)}.ai-chat-location-card__status{border-radius:12px;font-size:12px;font-weight:500;padding:2px 8px}.ai-chat-location-card__status--open{background:#dcfce7;color:#16a34a}.ai-chat-location-card__status--closed{background:#fef2f2;color:#dc2626}.ai-chat-widget.dark .ai-chat-location-card__status--open,.chakra-ui-dark .ai-chat-location-card__status--open,.dark .ai-chat-location-card__status--open,[data-theme=dark] .ai-chat-location-card__status--open{background:rgba(34,197,94,.2);color:#4ade80}.ai-chat-widget.dark .ai-chat-location-card__status--closed,.chakra-ui-dark .ai-chat-location-card__status--closed,.dark .ai-chat-location-card__status--closed,[data-theme=dark] .ai-chat-location-card__status--closed{background:rgba(239,68,68,.2);color:#fca5a5}.ai-chat-location-card__address{align-items:flex-start;color:var(--text-muted,#71717a);display:flex;font-size:13px;gap:6px;line-height:1.4;margin:0 0 8px}.ai-chat-location-card__address svg{flex-shrink:0;margin-top:2px}.ai-chat-location-card__description{color:var(--text-muted,#71717a);font-size:13px;line-height:1.4;margin:0 0 8px}.ai-chat-location-card__hours{align-items:flex-start;color:var(--text-muted,#71717a);display:flex;font-size:13px;gap:6px;margin-bottom:8px}.ai-chat-location-card__hours svg{flex-shrink:0;margin-top:2px}.ai-chat-location-card__hours-list{flex:1}.ai-chat-location-card__hours-toggle{align-items:center;background:none;border:none;color:inherit;cursor:pointer;display:flex;font:inherit;gap:4px;padding:0}.ai-chat-location-card__hours-toggle:hover{text-decoration:underline}.ai-chat-location-card__hours-full{list-style:none;margin:8px 0 0;padding:0}.ai-chat-location-card__hours-full li{display:flex;font-size:12px;justify-content:space-between;padding:4px 0}.ai-chat-location-card__hours-today{color:var(--text-primary,#18181b);font-weight:600}.ai-chat-widget.dark .ai-chat-location-card__hours-today,.chakra-ui-dark .ai-chat-location-card__hours-today,.dark .ai-chat-location-card__hours-today,[data-theme=dark] .ai-chat-location-card__hours-today{color:#fff}.ai-chat-location-card__phone{align-items:center;background:none;border:none;color:var(--action-accent,#3b82f6);cursor:pointer;display:flex;font-size:13px;gap:6px;margin-bottom:12px;padding:0}.ai-chat-location-card__phone:hover{text-decoration:underline}.ai-chat-location-card__actions{display:flex;flex-wrap:wrap;gap:8px;justify-content:flex-start;width:100%}@media (max-width:380px){.ai-chat-location-card__actions{flex-direction:column}.ai-chat-location-card__button,.ai-chat-location-card__link{flex:none;justify-content:center;width:100%}}.ai-chat-location-card__button{align-items:center;background:var(--action-accent,#3b82f6);border:none;border-radius:20px;color:#fff;cursor:pointer;display:flex;flex:1;font-size:13px;font-weight:500;gap:6px;justify-content:center;padding:10px 16px;transition:opacity .2s}.ai-chat-location-card__button:hover{opacity:.9}.ai-chat-location-card--compact .ai-chat-location-card__button{font-size:12px;padding:8px 12px}.ai-chat-location-card__link{align-items:center;background:var(--bg-secondary,#fff);border:1px solid var(--border-subtle,rgba(0,0,0,.08));border-radius:20px;color:var(--text-primary,#18181b);display:flex;font-size:13px;gap:6px;padding:10px 16px;text-decoration:none;transition:border-color .2s}.ai-chat-widget.dark .ai-chat-location-card__link,.chakra-ui-dark .ai-chat-location-card__link,.dark .ai-chat-location-card__link,[data-theme=dark] .ai-chat-location-card__link{background:hsla(0,0%,100%,.05);border-color:hsla(0,0%,100%,.1);color:#fff}.ai-chat-location-card__link:hover{border-color:var(--action-accent,#3b82f6)}.ai-chat-location-card-list{display:flex;flex-direction:column;gap:8px}.ai-chat-location-card-list__header{align-items:center;color:var(--text-muted,#71717a);display:flex;font-size:13px;font-weight:500;gap:6px;margin-bottom:4px;padding:0 4px}.ai-chat-location-card-list__stack{display:grid;gap:12px;grid-template-columns:1fr}.ai-chat-location-card-list__stack--cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.ai-chat-location-card-list__stack--cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}@media (max-width:1000px){.ai-chat-location-card-list__stack--cols-3{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (max-width:640px){.ai-chat-location-card-list__stack--cols-2,.ai-chat-location-card-list__stack--cols-3{grid-template-columns:1fr}}.ai-chat-location-card-list__grid{display:grid;gap:8px;grid-template-columns:repeat(2,1fr)}@media (max-width:400px){.ai-chat-location-card-list__grid{grid-template-columns:1fr}}.ai-chat-location-card-list__carousel{-webkit-overflow-scrolling:touch;-ms-overflow-style:none;display:flex;gap:8px;overflow-x:auto;padding-bottom:4px;scroll-snap-type:x mandatory;scrollbar-width:none}.ai-chat-location-card-list__carousel::-webkit-scrollbar{display:none}.ai-chat-location-card-list__carousel>.ai-chat-location-card{flex:0 0 280px;scroll-snap-align:start}@media (max-width:480px){.ai-chat-location-card__content{padding:10px}.ai-chat-location-card__name{font-size:14px}.ai-chat-location-card__address,.ai-chat-location-card__description,.ai-chat-location-card__hours{font-size:12px}.ai-chat-location-card__button,.ai-chat-location-card__link{font-size:12px;padding:8px 12px}.ai-chat-location-card-list__carousel>.ai-chat-location-card{flex:0 0 240px}}.ai-chat-contact-card{background:#fff;border:1px solid rgba(0,0,0,.08);border-radius:16px;overflow:hidden;padding:0;position:relative}.ai-chat-widget.dark .ai-chat-contact-card,.ai-chat-widget[data-theme=dark] .ai-chat-contact-card,.chakra-ui-dark .ai-chat-contact-card,.dark .ai-chat-contact-card,[data-theme=dark] .ai-chat-contact-card,html.dark .ai-chat-contact-card{background:#4a4a4a;border-color:hsla(0,0%,100%,.08)}.ai-chat-contact-card-list{gap:12px;width:100%}.ai-chat-contact-card--compact{border-radius:12px}.ai-chat-contact-card--empty{align-items:center;background:var(--bg-secondary,#f4f4f5);display:flex;flex-direction:column;gap:8px;justify-content:center;padding:24px 16px;text-align:center}.ai-chat-widget.dark .ai-chat-contact-card--empty,.chakra-ui-dark .ai-chat-contact-card--empty,.dark .ai-chat-contact-card--empty,[data-theme=dark] .ai-chat-contact-card--empty{background:#3a3a3a}.ai-chat-contact-card__empty-icon{color:var(--text-muted,#71717a);opacity:.6}.ai-chat-contact-card__empty-text{color:var(--text-muted,#71717a);font-size:14px;margin:0}.ai-chat-contact-card--vertical{display:flex;flex-direction:column}.ai-chat-contact-card--vertical .ai-chat-contact-card__image-section{aspect-ratio:3/2;overflow:hidden;width:100%}.ai-chat-contact-card--vertical .ai-chat-contact-card__image{height:100%;object-fit:cover;width:100%}.ai-chat-contact-card--vertical .ai-chat-contact-card__image-placeholder{align-items:center;background:linear-gradient(135deg,#5a5a5a,#3a3a3a);color:hsla(0,0%,100%,.5);display:flex;height:100%;justify-content:center;width:100%}.ai-chat-contact-card--vertical .ai-chat-contact-card__image-placeholder svg{height:48px;width:48px}.ai-chat-contact-card--vertical .ai-chat-contact-card__info{padding:16px;text-align:center}.ai-chat-contact-card--horizontal{display:flex;flex-direction:row}.ai-chat-contact-card--horizontal .ai-chat-contact-card__image-section{height:160px;min-width:140px;overflow:hidden;width:140px}.ai-chat-contact-card--horizontal.ai-chat-contact-card--compact .ai-chat-contact-card__image-section{height:120px;min-width:100px;width:100px}.ai-chat-contact-card--horizontal .ai-chat-contact-card__image{height:100%;object-fit:cover;width:100%}.ai-chat-contact-card--horizontal .ai-chat-contact-card__image-placeholder{align-items:center;background:linear-gradient(135deg,#5a5a5a,#3a3a3a);color:hsla(0,0%,100%,.5);display:flex;height:100%;justify-content:center;width:100%}.ai-chat-contact-card--horizontal .ai-chat-contact-card__image-placeholder svg{height:36px;width:36px}.ai-chat-contact-card--horizontal .ai-chat-contact-card__info{display:flex;flex:1;flex-direction:column;justify-content:center;padding:16px}.ai-chat-contact-card__name{color:var(--action-accent,#ef4444);font-size:18px;font-weight:600;line-height:1.3;margin:0}.ai-chat-contact-card--compact .ai-chat-contact-card__name{font-size:15px}.ai-chat-contact-card__role{color:rgba(0,0,0,.7);font-size:14px;font-weight:400;margin:2px 0 0}.ai-chat-widget.dark .ai-chat-contact-card__role,.ai-chat-widget[data-theme=dark] .ai-chat-contact-card__role,.chakra-ui-dark .ai-chat-contact-card__role,.dark .ai-chat-contact-card__role,[data-theme=dark] .ai-chat-contact-card__role,html.dark .ai-chat-contact-card__role{color:hsla(0,0%,100%,.9)}.ai-chat-contact-card--compact .ai-chat-contact-card__role{font-size:13px}.ai-chat-contact-card__details{display:flex;flex-direction:column;gap:2px;margin-top:12px}.ai-chat-contact-card__detail{color:rgba(0,0,0,.6);display:block;font-size:14px;line-height:1.5;margin:0;text-decoration:none}.ai-chat-contact-card__detail:hover{color:#000;text-decoration:underline}.ai-chat-widget.dark .ai-chat-contact-card__detail,.ai-chat-widget[data-theme=dark] .ai-chat-contact-card__detail,.chakra-ui-dark .ai-chat-contact-card__detail,.dark .ai-chat-contact-card__detail,[data-theme=dark] .ai-chat-contact-card__detail,html.dark .ai-chat-contact-card__detail{color:hsla(0,0%,100%,.7)}.ai-chat-widget.dark .ai-chat-contact-card__detail:hover,.ai-chat-widget[data-theme=dark] .ai-chat-contact-card__detail:hover,.chakra-ui-dark .ai-chat-contact-card__detail:hover,.dark .ai-chat-contact-card__detail:hover,[data-theme=dark] .ai-chat-contact-card__detail:hover,html.dark .ai-chat-contact-card__detail:hover{color:#fff}.ai-chat-contact-card--compact .ai-chat-contact-card__detail{font-size:13px}.ai-chat-contact-card__responsibilities{display:flex;flex-wrap:wrap;gap:4px;margin-top:8px}.ai-chat-contact-card__responsibility-tag{background:rgba(0,0,0,.08);border-radius:10px;color:rgba(0,0,0,.8);font-size:11px;font-weight:500;padding:3px 10px}.ai-chat-widget.dark .ai-chat-contact-card__responsibility-tag,.ai-chat-widget[data-theme=dark] .ai-chat-contact-card__responsibility-tag,.chakra-ui-dark .ai-chat-contact-card__responsibility-tag,.dark .ai-chat-contact-card__responsibility-tag,[data-theme=dark] .ai-chat-contact-card__responsibility-tag,html.dark .ai-chat-contact-card__responsibility-tag{background:hsla(0,0%,100%,.15);color:hsla(0,0%,100%,.9)}.ai-chat-contact-card__responsibility-more{color:rgba(0,0,0,.5);font-size:11px;padding:3px 4px}.ai-chat-widget.dark .ai-chat-contact-card__responsibility-more,.ai-chat-widget[data-theme=dark] .ai-chat-contact-card__responsibility-more,.chakra-ui-dark .ai-chat-contact-card__responsibility-more,.dark .ai-chat-contact-card__responsibility-more,[data-theme=dark] .ai-chat-contact-card__responsibility-more,html.dark .ai-chat-contact-card__responsibility-more{color:hsla(0,0%,100%,.5)}.ai-chat-contact-card__actions{display:flex;flex-wrap:wrap;gap:8px;padding:0 12px 12px}@media (max-width:380px){.ai-chat-contact-card__actions{flex-direction:column}.ai-chat-contact-card__button{flex:none;width:100%}}.ai-chat-contact-card--compact .ai-chat-contact-card__actions{gap:6px;padding:0 10px 10px}.ai-chat-contact-card__button{align-items:center;border:none;border-radius:9999px;cursor:pointer;display:flex;font-size:14px;font-weight:600;gap:8px;justify-content:center;padding:12px 20px;transition:all .15s ease;white-space:nowrap}.ai-chat-contact-card--compact .ai-chat-contact-card__button{font-size:13px;padding:10px 16px}.ai-chat-contact-card__button:hover{box-shadow:0 4px 12px rgba(0,0,0,.15);transform:translateY(-1px)}.ai-chat-contact-card__button:active{transform:translateY(0)}.ai-chat-contact-card__button--primary{background:var(--action-accent,#3b82f6);color:#fff;flex:1}.ai-chat-contact-card__button--primary:hover{background:color-mix(in srgb,var(--action-accent,#3b82f6) 90%,#000)}.ai-chat-contact-card__button--secondary{background:var(--bg-muted,#f4f4f5);border:1px solid var(--border-subtle,rgba(0,0,0,.08));color:var(--text-primary,#18181b);flex:1}.ai-chat-contact-card__button--secondary:hover{background:var(--bg-hover,#e4e4e7)}.ai-chat-widget.dark .ai-chat-contact-card__button--secondary,.chakra-ui-dark .ai-chat-contact-card__button--secondary,.dark .ai-chat-contact-card__button--secondary,[data-theme=dark] .ai-chat-contact-card__button--secondary{background:hsla(0,0%,100%,.1);border-color:hsla(0,0%,100%,.15);color:#fff}.ai-chat-widget.dark .ai-chat-contact-card__button--secondary:hover,.chakra-ui-dark .ai-chat-contact-card__button--secondary:hover,.dark .ai-chat-contact-card__button--secondary:hover,[data-theme=dark] .ai-chat-contact-card__button--secondary:hover{background:hsla(0,0%,100%,.15)}.ai-chat-contact-card-list{display:flex;flex-direction:column;gap:8px}.ai-chat-contact-card-list__header{align-items:center;color:var(--text-muted,#71717a);display:flex;font-size:13px;font-weight:500;gap:6px;margin-bottom:2px;margin-top:8px;padding:0 4px}.ai-chat-contact-card-list__stack{display:grid;gap:12px;grid-template-columns:repeat(3,minmax(0,1fr))}@media (max-width:900px){.ai-chat-contact-card-list__stack{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (max-width:600px){.ai-chat-contact-card-list__stack{grid-template-columns:1fr}}.ai-chat-contact-card-list__stack--widget{grid-template-columns:1fr}@container (min-width: 380px){.ai-chat-contact-card-list__stack--widget{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (max-width:520px){.ai-chat-contact-card-list__stack{grid-template-columns:1fr!important}.ai-chat-contact-card--horizontal{flex-direction:column}.ai-chat-contact-card--horizontal .ai-chat-contact-card__image-section{aspect-ratio:3/2;height:auto;min-width:100%;width:100%}}.ai-chat-contact-card__initials{align-items:center;display:flex;font-size:48px;font-weight:600;height:100%;justify-content:center;letter-spacing:.05em;text-transform:uppercase;width:100%}.ai-chat-contact-card--compact .ai-chat-contact-card__initials{font-size:32px}.ai-chat-contact-card--horizontal .ai-chat-contact-card__initials{font-size:28px}.ai-chat-contact-card--horizontal.ai-chat-contact-card--compact .ai-chat-contact-card__initials{font-size:22px}@media (max-width:480px){.ai-chat-contact-card__name{font-size:16px}.ai-chat-contact-card__detail,.ai-chat-contact-card__role{font-size:13px}.ai-chat-contact-card__button{font-size:13px;padding:10px 16px}.ai-chat-contact-card--horizontal .ai-chat-contact-card__info,.ai-chat-contact-card--vertical .ai-chat-contact-card__info{padding:12px}}.ai-chat-form-card{--action-accent:var(--primary-color,#3b82f6);background:var(--bg-secondary,#f9fafb);border:1px solid var(--border-subtle,rgba(0,0,0,.06));border-radius:12px;box-sizing:border-box;margin:6px 0;overflow:hidden;padding:16px;width:100%}.ai-chat-widget.dark .ai-chat-form-card,.chakra-ui-dark .ai-chat-form-card,.dark .ai-chat-form-card,[data-theme=dark] .ai-chat-form-card{background:hsla(0,0%,100%,.05);border-color:hsla(0,0%,100%,.1)}.ai-chat-form-card--completed,.ai-chat-form-card--empty,.ai-chat-form-card--error,.ai-chat-form-card--skipped,.ai-chat-form-card--submitted{padding:12px 16px}.ai-chat-form-card--completed .ai-chat-form-card__header{margin-bottom:0}.ai-chat-form-card--completed .ai-chat-form-card__icon svg{color:#10b981}.ai-chat-form-card__header{align-items:center;display:flex;gap:8px;margin-bottom:12px}.ai-chat-form-card__icon{align-items:center;display:inline-flex;justify-content:center}.ai-chat-form-card__icon svg{color:var(--text-primary,#3e3e3e);height:18px;width:18px}.ai-chat-widget.dark .ai-chat-form-card__icon svg,.chakra-ui-dark .ai-chat-form-card__icon svg,.dark .ai-chat-form-card__icon svg,[data-theme=dark] .ai-chat-form-card__icon svg{color:#fff}.ai-chat-form-card__title{color:var(--text-primary,#3e3e3e);font-size:15px;font-weight:600}.ai-chat-widget.dark .ai-chat-form-card__title,.chakra-ui-dark .ai-chat-form-card__title,.dark .ai-chat-form-card__title,[data-theme=dark] .ai-chat-form-card__title{color:#fff}.ai-chat-form-card__description{color:var(--text-muted,#71717a);font-size:13px;line-height:1.4;margin:0 0 12px}.ai-chat-form-card__context{color:var(--text-muted,#71717a);font-size:12px;font-style:italic;margin:0 0 12px}.ai-chat-form-card--error .ai-chat-form-card__icon svg{color:#dc2626}.ai-chat-form-card--submitted .ai-chat-form-card__icon svg{color:#16a34a}.ai-chat-widget.dark .ai-chat-form-card--error .ai-chat-form-card__icon svg,.chakra-ui-dark .ai-chat-form-card--error .ai-chat-form-card__icon svg,.dark .ai-chat-form-card--error .ai-chat-form-card__icon svg,[data-theme=dark] .ai-chat-form-card--error .ai-chat-form-card__icon svg{color:#fca5a5}.ai-chat-widget.dark .ai-chat-form-card--submitted .ai-chat-form-card__icon svg,.chakra-ui-dark .ai-chat-form-card--submitted .ai-chat-form-card__icon svg,.dark .ai-chat-form-card--submitted .ai-chat-form-card__icon svg,[data-theme=dark] .ai-chat-form-card--submitted .ai-chat-form-card__icon svg{color:#4ade80}.ai-chat-form-card__error{color:#dc2626;font-size:13px;margin:0}.ai-chat-widget.dark .ai-chat-form-card__error,.chakra-ui-dark .ai-chat-form-card__error,.dark .ai-chat-form-card__error,[data-theme=dark] .ai-chat-form-card__error{color:#fca5a5}.ai-chat-form-card__success{color:#16a34a;font-size:13px;margin:0}.ai-chat-widget.dark .ai-chat-form-card__success,.chakra-ui-dark .ai-chat-form-card__success,.dark .ai-chat-form-card__success,[data-theme=dark] .ai-chat-form-card__success{color:#4ade80}.ai-chat-form-card__empty-text,.ai-chat-form-card__skipped-text{color:var(--text-muted,#71717a);font-size:13px;margin:0}.ai-chat-form-card__progress{align-items:center;display:flex;gap:12px;margin-bottom:16px}.ai-chat-form-card__progress-bar{background:var(--action-accent,var(--primary-color,#3b82f6));border-radius:2px;flex:1;height:4px;transition:width .3s ease}.ai-chat-form-card__progress-text{color:var(--text-muted,#71717a);font-size:12px;white-space:nowrap}.ai-chat-form-card__question{margin-bottom:16px}.ai-chat-form-card__question-text{color:var(--text-primary,#3e3e3e);font-size:14px;font-weight:500;line-height:1.4;margin:0 0 12px}.ai-chat-widget.dark .ai-chat-form-card__question-text,.chakra-ui-dark .ai-chat-form-card__question-text,.dark .ai-chat-form-card__question-text,[data-theme=dark] .ai-chat-form-card__question-text{color:#fff}.ai-chat-form-card__required{color:#dc2626;margin-left:2px}.ai-chat-form-card__answer{margin-top:8px}.ai-chat-form-card__textarea{background:var(--bg-primary,#fff);border:1px solid var(--border-default,#e5e7eb);border-radius:var(--radius-md,8px);box-sizing:border-box;color:var(--text-primary,#3e3e3e);font-family:inherit;font-size:14px;min-height:80px;outline:none;padding:10px 12px;resize:vertical;transition:border-color .2s ease,box-shadow .2s ease;width:100%}.ai-chat-form-card__textarea:focus{border-color:var(--action-accent,var(--primary-color,#3b82f6));box-shadow:0 0 0 3px rgba(59,130,246,.1)}.ai-chat-form-card__textarea::placeholder{color:var(--text-muted,#9ca3af)}.ai-chat-widget.dark .ai-chat-form-card__textarea,.chakra-ui-dark .ai-chat-form-card__textarea,.dark .ai-chat-form-card__textarea,[data-theme=dark] .ai-chat-form-card__textarea{background:rgba(0,0,0,.2);border-color:hsla(0,0%,100%,.1);color:#fff}.ai-chat-widget.dark .ai-chat-form-card__textarea:focus,.chakra-ui-dark .ai-chat-form-card__textarea:focus,.dark .ai-chat-form-card__textarea:focus,[data-theme=dark] .ai-chat-form-card__textarea:focus{border-color:var(--action-accent,var(--primary-color,#3b82f6));box-shadow:0 0 0 3px rgba(59,130,246,.2)}.ai-chat-form-card__options{display:flex;flex-direction:column;gap:8px}.ai-chat-form-card__option{align-items:center;background:var(--bg-primary,#fff);border:1px solid var(--border-default,#e5e7eb);border-radius:var(--radius-md,8px);cursor:pointer;display:flex;gap:10px;padding:10px 12px;transition:border-color .15s,background .15s}.ai-chat-form-card__option:hover{background:rgba(59,130,246,.05);border-color:var(--action-accent,var(--primary-color,#3b82f6))}.ai-chat-widget.dark .ai-chat-form-card__option,.chakra-ui-dark .ai-chat-form-card__option,.dark .ai-chat-form-card__option,[data-theme=dark] .ai-chat-form-card__option{background:rgba(0,0,0,.2);border-color:hsla(0,0%,100%,.1)}.ai-chat-widget.dark .ai-chat-form-card__option:hover,.chakra-ui-dark .ai-chat-form-card__option:hover,.dark .ai-chat-form-card__option:hover,[data-theme=dark] .ai-chat-form-card__option:hover{background:rgba(59,130,246,.15);border-color:var(--action-accent,var(--primary-color,#3b82f6))}.ai-chat-form-card__option input{accent-color:var(--action-accent,var(--primary-color,#3b82f6));margin:0}.ai-chat-form-card__option-text{color:var(--text-primary,#3e3e3e);font-size:14px}.ai-chat-widget.dark .ai-chat-form-card__option-text,.chakra-ui-dark .ai-chat-form-card__option-text,.dark .ai-chat-form-card__option-text,[data-theme=dark] .ai-chat-form-card__option-text{color:#fff}.ai-chat-form-card__rating{display:flex;flex-wrap:wrap;gap:8px;justify-content:flex-start}@media (max-width:380px){.ai-chat-form-card__rating{gap:6px}.ai-chat-form-card__rating-btn{font-size:13px;height:36px;width:36px}}.ai-chat-form-card__rating-btn{align-items:center;background:var(--bg-primary,#fff);border:1px solid var(--border-default,#e5e7eb);border-radius:var(--radius-md,8px);color:var(--text-primary,#3e3e3e);cursor:pointer;display:flex;font-size:14px;font-weight:500;height:40px;justify-content:center;transition:all .15s ease;width:40px}.ai-chat-form-card__rating-btn--selected,.ai-chat-form-card__rating-btn:hover{border-color:var(--action-accent,var(--primary-color,#3b82f6))}.ai-chat-form-card__rating-btn--selected{background:var(--action-accent,var(--primary-color,#3b82f6));color:#fff}.ai-chat-widget.dark .ai-chat-form-card__rating-btn,.chakra-ui-dark .ai-chat-form-card__rating-btn,.dark .ai-chat-form-card__rating-btn,[data-theme=dark] .ai-chat-form-card__rating-btn{background:rgba(0,0,0,.2);border-color:hsla(0,0%,100%,.1);color:#fff}.ai-chat-widget.dark .ai-chat-form-card__rating-btn:hover,.chakra-ui-dark .ai-chat-form-card__rating-btn:hover,.dark .ai-chat-form-card__rating-btn:hover,[data-theme=dark] .ai-chat-form-card__rating-btn:hover{border-color:var(--action-accent,var(--primary-color,#3b82f6))}.ai-chat-widget.dark .ai-chat-form-card__rating-btn--selected,.chakra-ui-dark .ai-chat-form-card__rating-btn--selected,.dark .ai-chat-form-card__rating-btn--selected,[data-theme=dark] .ai-chat-form-card__rating-btn--selected{background:var(--action-accent,var(--primary-color,#3b82f6));border-color:var(--action-accent,var(--primary-color,#3b82f6));color:#fff}.ai-chat-form-card__actions{align-items:center;border-top:1px solid var(--border-subtle,rgba(0,0,0,.08));display:flex;flex-wrap:wrap;gap:8px;margin-top:16px;padding-top:16px}@media (max-width:380px){.ai-chat-form-card__actions{align-items:stretch;flex-direction:column}.ai-chat-form-card__actions-spacer{display:none}.ai-chat-form-card__btn{width:100%}}.ai-chat-widget.dark .ai-chat-form-card__actions,.chakra-ui-dark .ai-chat-form-card__actions,.dark .ai-chat-form-card__actions,[data-theme=dark] .ai-chat-form-card__actions{border-color:hsla(0,0%,100%,.08)}.ai-chat-form-card__actions-spacer{flex:1}.ai-chat-form-card__btn{border:none;border-radius:9999px;cursor:pointer;font-family:inherit;font-size:13px;font-weight:500;padding:8px 16px;transition:all .2s ease}.ai-chat-form-card__btn:disabled{cursor:not-allowed;opacity:.5}.ai-chat-form-card__btn--primary{background:var(--action-accent,var(--primary-color,#3b82f6));color:#fff}.ai-chat-form-card__btn--primary:hover:not(:disabled){opacity:.9;transform:translateY(-1px)}.ai-chat-form-card__btn--secondary{background:var(--bg-primary,#fff);border:1px solid var(--border-default,#e5e7eb);color:var(--text-primary,#3e3e3e)}.ai-chat-form-card__btn--secondary:hover:not(:disabled){border-color:var(--action-accent,var(--primary-color,#3b82f6))}.ai-chat-widget.dark .ai-chat-form-card__btn--secondary,.chakra-ui-dark .ai-chat-form-card__btn--secondary,.dark .ai-chat-form-card__btn--secondary,[data-theme=dark] .ai-chat-form-card__btn--secondary{background:hsla(0,0%,100%,.05);border-color:hsla(0,0%,100%,.1);color:#fff}.ai-chat-form-card__btn--ghost{background:transparent;color:var(--text-muted,#71717a)}.ai-chat-form-card__btn--ghost:hover:not(:disabled){background:rgba(0,0,0,.05);color:var(--text-primary,#3e3e3e)}.ai-chat-widget.dark .ai-chat-form-card__btn--ghost:hover:not(:disabled),.chakra-ui-dark .ai-chat-form-card__btn--ghost:hover:not(:disabled),.dark .ai-chat-form-card__btn--ghost:hover:not(:disabled),[data-theme=dark] .ai-chat-form-card__btn--ghost:hover:not(:disabled){background:hsla(0,0%,100%,.05);color:#fff}@media (max-width:480px){.ai-chat-form-card{padding:12px}.ai-chat-form-card__title{font-size:14px}.ai-chat-form-card__context,.ai-chat-form-card__description{font-size:12px}.ai-chat-form-card__question-text{font-size:13px}.ai-chat-form-card__textarea{font-size:13px;min-height:70px}.ai-chat-form-card__option{padding:8px 10px}.ai-chat-form-card__option-text{font-size:13px}}.ai-chat-booking-card{--action-accent:var(--primary-color,#3b82f6);background:var(--bg-secondary,#f5f5f5);border-radius:12px;box-sizing:border-box;margin:6px 0;padding:16px;width:100%}.ai-chat-widget.dark .ai-chat-booking-card,.chakra-ui-dark .ai-chat-booking-card,.dark .ai-chat-booking-card,[data-theme=dark] .ai-chat-booking-card{background:hsla(0,0%,100%,.06)}.ai-chat-booking-card__header{align-items:center;display:flex;gap:8px;margin-bottom:12px}.ai-chat-booking-card__back-btn{align-items:center;background:rgba(0,0,0,.05);border:none;border-radius:6px;color:var(--text-secondary,#6b7280);cursor:pointer;display:flex;font-family:inherit;font-size:13px;font-weight:500;justify-content:center;padding:6px;transition:background .15s ease,color .15s ease}.ai-chat-booking-card__back-btn svg{height:16px;width:16px}.ai-chat-booking-card__back-btn:hover{background:rgba(0,0,0,.1);color:var(--text-primary,#3e3e3e)}.ai-chat-widget.dark .ai-chat-booking-card__back-btn,.chakra-ui-dark .ai-chat-booking-card__back-btn,.dark .ai-chat-booking-card__back-btn,[data-theme=dark] .ai-chat-booking-card__back-btn{background:hsla(0,0%,100%,.08);color:var(--text-secondary,#a1a1aa)}.ai-chat-widget.dark .ai-chat-booking-card__back-btn:hover,.chakra-ui-dark .ai-chat-booking-card__back-btn:hover,.dark .ai-chat-booking-card__back-btn:hover,[data-theme=dark] .ai-chat-booking-card__back-btn:hover{background:hsla(0,0%,100%,.15);color:#fff}.ai-chat-booking-card__title{color:var(--text-primary,#3e3e3e);font-size:15px;font-weight:600}.ai-chat-widget.dark .ai-chat-booking-card__title,.chakra-ui-dark .ai-chat-booking-card__title,.dark .ai-chat-booking-card__title,[data-theme=dark] .ai-chat-booking-card__title{color:#fff}.ai-chat-booking-card__status-icon{align-items:center;border-radius:50%;display:flex;flex-shrink:0;height:20px;justify-content:center;position:relative;width:20px}.ai-chat-booking-card__status-icon--pending{background:#6b7280}.ai-chat-booking-card__status-icon--pending:before{background:#fff;border-radius:1px;content:\"\";height:6px;position:absolute;top:5px;width:2px}.ai-chat-booking-card__status-icon--pending:after{background:#fff;border-radius:1px;content:\"\";height:2px;left:9px;position:absolute;top:9px;width:4px}.ai-chat-booking-card__status-icon--cancelled{background:#6b7280}.ai-chat-booking-card__status-icon--cancelled:after,.ai-chat-booking-card__status-icon--cancelled:before{background:#fff;border-radius:1px;content:\"\";height:2px;position:absolute;width:10px}.ai-chat-booking-card__status-icon--cancelled:before{transform:rotate(45deg)}.ai-chat-booking-card__status-icon--cancelled:after{transform:rotate(-45deg)}.ai-chat-booking-card__status-icon--error{background:#6b7280}.ai-chat-booking-card__status-icon--error:after{color:#fff;content:\"!\";font-size:12px;font-weight:700}.ai-chat-booking-card__content{display:flex;flex-direction:column;gap:12px}.ai-chat-booking-card__description{color:var(--text-muted,#71717a);font-size:13px;line-height:1.4;margin:0}.ai-chat-booking-card__empty{color:var(--text-muted,#71717a);font-size:13px;padding:16px;text-align:center}.ai-chat-booking-card__warning{background:rgba(0,0,0,.04);border-radius:8px;color:var(--text-secondary,#6b7280);font-size:13px;margin:0;padding:10px 12px}.ai-chat-widget.dark .ai-chat-booking-card__warning,.chakra-ui-dark .ai-chat-booking-card__warning,.dark .ai-chat-booking-card__warning,[data-theme=dark] .ai-chat-booking-card__warning{background:hsla(0,0%,100%,.06);color:var(--text-secondary,#a1a1aa)}.ai-chat-booking-card__input{background:rgba(0,0,0,.05);border:none;border-radius:8px;box-sizing:border-box;color:var(--text-primary,#3e3e3e);font-family:inherit;font-size:14px;outline:none;padding:10px 12px;transition:background .15s ease,box-shadow .15s ease;width:100%}.ai-chat-booking-card__input:focus{background:rgba(0,0,0,.08);box-shadow:0 0 0 2px var(--action-accent,var(--primary-color,#3b82f6))}.ai-chat-booking-card__input::placeholder{color:var(--text-muted,#9ca3af)}.ai-chat-widget.dark .ai-chat-booking-card__input,.chakra-ui-dark .ai-chat-booking-card__input,.dark .ai-chat-booking-card__input,[data-theme=dark] .ai-chat-booking-card__input{background:hsla(0,0%,100%,.08);color:#fff}.ai-chat-widget.dark .ai-chat-booking-card__input:focus,.chakra-ui-dark .ai-chat-booking-card__input:focus,.dark .ai-chat-booking-card__input:focus,[data-theme=dark] .ai-chat-booking-card__input:focus{background:hsla(0,0%,100%,.12)}.ai-chat-booking-card__field{display:flex;flex-direction:column;gap:8px}.ai-chat-booking-card__label{color:var(--text-secondary,#6b7280);display:block;font-size:13px;font-weight:500;margin-bottom:4px}.ai-chat-widget.dark .ai-chat-booking-card__label,.chakra-ui-dark .ai-chat-booking-card__label,.dark .ai-chat-booking-card__label,[data-theme=dark] .ai-chat-booking-card__label{color:var(--text-secondary,#a1a1aa)}.ai-chat-booking-card__btn{border:none;border-radius:9999px;box-sizing:border-box;cursor:pointer;font-family:inherit;font-size:13px;font-weight:500;padding:10px 16px;transition:all .15s ease;width:100%}.ai-chat-booking-card__btn:disabled{cursor:not-allowed;opacity:.5}.ai-chat-booking-card__btn--primary{background:var(--action-accent,var(--primary-color,#3b82f6));color:#fff}.ai-chat-booking-card__btn--primary:hover:not(:disabled){filter:brightness(1.1);transform:translateY(-1px)}.ai-chat-booking-card__btn--secondary{background:rgba(0,0,0,.06);color:var(--text-primary,#3e3e3e)}.ai-chat-booking-card__btn--secondary:hover:not(:disabled){background:rgba(0,0,0,.1)}.ai-chat-widget.dark .ai-chat-booking-card__btn--secondary,.chakra-ui-dark .ai-chat-booking-card__btn--secondary,.dark .ai-chat-booking-card__btn--secondary,[data-theme=dark] .ai-chat-booking-card__btn--secondary{background:hsla(0,0%,100%,.1);color:#fff}.ai-chat-widget.dark .ai-chat-booking-card__btn--secondary:hover:not(:disabled),.chakra-ui-dark .ai-chat-booking-card__btn--secondary:hover:not(:disabled),.dark .ai-chat-booking-card__btn--secondary:hover:not(:disabled),[data-theme=dark] .ai-chat-booking-card__btn--secondary:hover:not(:disabled){background:hsla(0,0%,100%,.15)}.ai-chat-booking-card__btn--danger{background:rgba(220,38,38,.1);color:#dc2626}.ai-chat-booking-card__btn--danger:hover:not(:disabled){background:rgba(220,38,38,.18)}.ai-chat-widget.dark .ai-chat-booking-card__btn--danger,.chakra-ui-dark .ai-chat-booking-card__btn--danger,.dark .ai-chat-booking-card__btn--danger,[data-theme=dark] .ai-chat-booking-card__btn--danger{background:rgba(239,68,68,.2);color:#fca5a5}.ai-chat-booking-card__btn--inline{font-size:12px;margin-top:8px;padding:6px 12px;width:auto}.ai-chat-booking-card__options{display:flex;flex-direction:column;gap:8px}.ai-chat-booking-card__option-btn{align-items:center;background:rgba(0,0,0,.04);border:none;border-radius:10px;box-sizing:border-box;cursor:pointer;display:flex;font-family:inherit;gap:12px;padding:14px 16px;text-align:left;transition:background .15s ease;width:100%}.ai-chat-booking-card__option-btn:hover:not(:disabled){background:rgba(0,0,0,.08)}.ai-chat-booking-card__option-btn:disabled{cursor:not-allowed;opacity:.5}.ai-chat-widget.dark .ai-chat-booking-card__option-btn,.chakra-ui-dark .ai-chat-booking-card__option-btn,.dark .ai-chat-booking-card__option-btn,[data-theme=dark] .ai-chat-booking-card__option-btn{background:hsla(0,0%,100%,.06)}.ai-chat-widget.dark .ai-chat-booking-card__option-btn:hover:not(:disabled),.chakra-ui-dark .ai-chat-booking-card__option-btn:hover:not(:disabled),.dark .ai-chat-booking-card__option-btn:hover:not(:disabled),[data-theme=dark] .ai-chat-booking-card__option-btn:hover:not(:disabled){background:hsla(0,0%,100%,.1)}.ai-chat-booking-card__option-icon{align-items:center;background:var(--action-accent,var(--primary-color,#3b82f6));border-radius:8px;color:#fff;display:flex;flex-shrink:0;height:32px;justify-content:center;width:32px}.ai-chat-booking-card__option-icon svg{height:16px;width:16px}.ai-chat-booking-card__option-text{color:var(--text-primary,#3e3e3e);font-size:14px;font-weight:500}.ai-chat-widget.dark .ai-chat-booking-card__option-text,.chakra-ui-dark .ai-chat-booking-card__option-text,.dark .ai-chat-booking-card__option-text,[data-theme=dark] .ai-chat-booking-card__option-text{color:#fff}.ai-chat-booking-card__list{display:flex;flex-direction:column;gap:6px}.ai-chat-booking-card__list-item{align-items:center;background:rgba(0,0,0,.04);border:none;border-radius:10px;box-sizing:border-box;cursor:pointer;display:flex;font-family:inherit;justify-content:space-between;padding:12px 14px;text-align:left;transition:background .15s ease;width:100%}.ai-chat-booking-card__list-item:hover{background:rgba(0,0,0,.08)}.ai-chat-widget.dark .ai-chat-booking-card__list-item,.chakra-ui-dark .ai-chat-booking-card__list-item,.dark .ai-chat-booking-card__list-item,[data-theme=dark] .ai-chat-booking-card__list-item{background:hsla(0,0%,100%,.06)}.ai-chat-widget.dark .ai-chat-booking-card__list-item:hover,.chakra-ui-dark .ai-chat-booking-card__list-item:hover,.dark .ai-chat-booking-card__list-item:hover,[data-theme=dark] .ai-chat-booking-card__list-item:hover{background:hsla(0,0%,100%,.1)}.ai-chat-booking-card__list-item-content{display:flex;flex-direction:column;gap:2px}.ai-chat-booking-card__list-item-name{color:var(--text-primary,#3e3e3e);font-size:14px;font-weight:500}.ai-chat-widget.dark .ai-chat-booking-card__list-item-name,.chakra-ui-dark .ai-chat-booking-card__list-item-name,.dark .ai-chat-booking-card__list-item-name,[data-theme=dark] .ai-chat-booking-card__list-item-name{color:#fff}.ai-chat-booking-card__list-item-role{color:var(--text-muted,#71717a);font-size:12px}.ai-chat-booking-card__list-item-arrow{flex-shrink:0;height:16px;position:relative;width:16px}.ai-chat-booking-card__list-item-arrow:before{border-width:medium;border-bottom:2px solid var(--text-muted,#9ca3af);border-left:0 solid var(--text-muted,#9ca3af);border-right:2px solid var(--text-muted,#9ca3af);border-top:0 solid var(--text-muted,#9ca3af);content:\"\";height:6px;left:50%;position:absolute;top:50%;transform:translate(-70%,-50%) rotate(-45deg);width:6px}.ai-chat-booking-card__slots-container{display:flex;flex-direction:column;gap:12px;max-height:280px;overflow-y:auto}.ai-chat-booking-card__date-group{display:flex;flex-direction:column;gap:8px}.ai-chat-booking-card__date-header{color:var(--text-muted,#71717a);font-size:12px;font-weight:600;letter-spacing:.5px;margin:0;padding-left:2px;text-transform:uppercase}.ai-chat-booking-card__slots{display:flex;flex-wrap:wrap;gap:6px}.ai-chat-booking-card__slot{background:rgba(0,0,0,.05);border:none;border-radius:20px;color:var(--text-primary,#3e3e3e);cursor:pointer;flex:0 0 auto;font-family:inherit;font-size:13px;font-weight:500;padding:8px 14px;transition:background .15s ease,transform .1s ease;white-space:nowrap}.ai-chat-booking-card__slot:hover:not(:disabled){background:var(--action-accent,var(--primary-color,#3b82f6));color:#fff}.ai-chat-booking-card__slot:active:not(:disabled){transform:scale(.97)}.ai-chat-booking-card__slot:disabled{cursor:not-allowed;opacity:.5}.ai-chat-widget.dark .ai-chat-booking-card__slot,.chakra-ui-dark .ai-chat-booking-card__slot,.dark .ai-chat-booking-card__slot,[data-theme=dark] .ai-chat-booking-card__slot{background:hsla(0,0%,100%,.1);color:#fff}.ai-chat-widget.dark .ai-chat-booking-card__slot:hover:not(:disabled),.chakra-ui-dark .ai-chat-booking-card__slot:hover:not(:disabled),.dark .ai-chat-booking-card__slot:hover:not(:disabled),[data-theme=dark] .ai-chat-booking-card__slot:hover:not(:disabled){background:var(--action-accent,var(--primary-color,#3b82f6));color:#fff}.ai-chat-booking-card__subject-options{display:flex;flex-direction:column;gap:6px}.ai-chat-booking-card__subject-option{background:rgba(0,0,0,.04);border:none;border-radius:10px;color:var(--text-primary,#3e3e3e);cursor:pointer;font-family:inherit;font-size:14px;font-weight:500;padding:12px 14px;text-align:left;transition:background .15s ease,filter .15s ease}.ai-chat-booking-card__subject-option:hover:not(.ai-chat-booking-card__subject-option--selected){background:rgba(0,0,0,.08)}.ai-chat-booking-card__subject-option--selected,.ai-chat-booking-card__subject-option--selected:hover{background:var(--action-accent,var(--primary-color,#3b82f6));color:#fff}.ai-chat-booking-card__subject-option--selected:hover{filter:brightness(1.1)}.ai-chat-widget.dark .ai-chat-booking-card__subject-option,.chakra-ui-dark .ai-chat-booking-card__subject-option,.dark .ai-chat-booking-card__subject-option,[data-theme=dark] .ai-chat-booking-card__subject-option{background:hsla(0,0%,100%,.06);color:#fff}.ai-chat-widget.dark .ai-chat-booking-card__subject-option:hover:not(.ai-chat-booking-card__subject-option--selected),.chakra-ui-dark .ai-chat-booking-card__subject-option:hover:not(.ai-chat-booking-card__subject-option--selected),.dark .ai-chat-booking-card__subject-option:hover:not(.ai-chat-booking-card__subject-option--selected),[data-theme=dark] .ai-chat-booking-card__subject-option:hover:not(.ai-chat-booking-card__subject-option--selected){background:hsla(0,0%,100%,.1)}.ai-chat-widget.dark .ai-chat-booking-card__subject-option--selected,.ai-chat-widget.dark .ai-chat-booking-card__subject-option--selected:hover,.chakra-ui-dark .ai-chat-booking-card__subject-option--selected,.chakra-ui-dark .ai-chat-booking-card__subject-option--selected:hover,.dark .ai-chat-booking-card__subject-option--selected,.dark .ai-chat-booking-card__subject-option--selected:hover,[data-theme=dark] .ai-chat-booking-card__subject-option--selected,[data-theme=dark] .ai-chat-booking-card__subject-option--selected:hover{background:var(--action-accent,var(--primary-color,#3b82f6));color:#fff}.ai-chat-booking-card__appointments{display:flex;flex-direction:column;gap:8px}.ai-chat-booking-card__appointment{background:rgba(0,0,0,.04);border-radius:10px;padding:12px 14px}.ai-chat-widget.dark .ai-chat-booking-card__appointment,.chakra-ui-dark .ai-chat-booking-card__appointment,.dark .ai-chat-booking-card__appointment,[data-theme=dark] .ai-chat-booking-card__appointment{background:hsla(0,0%,100%,.06)}.ai-chat-booking-card__appointment-header{align-items:center;display:flex;gap:8px;justify-content:space-between;margin-bottom:6px}.ai-chat-booking-card__appointment-subject{color:var(--text-primary,#3e3e3e);font-size:14px;font-weight:500}.ai-chat-widget.dark .ai-chat-booking-card__appointment-subject,.chakra-ui-dark .ai-chat-booking-card__appointment-subject,.dark .ai-chat-booking-card__appointment-subject,[data-theme=dark] .ai-chat-booking-card__appointment-subject{color:#fff}.ai-chat-booking-card__appointment-status{border-radius:10px;font-size:11px;font-weight:600;padding:3px 8px;text-transform:uppercase}.ai-chat-booking-card__appointment-status--confirmed{background:rgba(59,130,246,.15);color:#3b82f6}.ai-chat-booking-card__appointment-status--pending{background:hsla(220,9%,46%,.15);color:#6b7280}.ai-chat-widget.dark .ai-chat-booking-card__appointment-status--confirmed,.chakra-ui-dark .ai-chat-booking-card__appointment-status--confirmed,.dark .ai-chat-booking-card__appointment-status--confirmed,[data-theme=dark] .ai-chat-booking-card__appointment-status--confirmed{background:rgba(59,130,246,.25);color:#60a5fa}.ai-chat-widget.dark .ai-chat-booking-card__appointment-status--pending,.chakra-ui-dark .ai-chat-booking-card__appointment-status--pending,.dark .ai-chat-booking-card__appointment-status--pending,[data-theme=dark] .ai-chat-booking-card__appointment-status--pending{background:hsla(220,9%,46%,.25);color:#9ca3af}.ai-chat-booking-card__appointment-status--cancelled{background:hsla(220,9%,46%,.15);color:#6b7280}.ai-chat-booking-card__appointment-time{color:var(--text-secondary,#6b7280);font-size:13px;margin-bottom:4px}.ai-chat-widget.dark .ai-chat-booking-card__appointment-time,.chakra-ui-dark .ai-chat-booking-card__appointment-time,.dark .ai-chat-booking-card__appointment-time,[data-theme=dark] .ai-chat-booking-card__appointment-time{color:var(--text-secondary,#a1a1aa)}.ai-chat-booking-card__appointment-contact{color:var(--text-muted,#71717a);font-size:12px}.ai-chat-booking-card__appointment-info{display:flex;flex-direction:column;gap:4px;margin-bottom:10px}.ai-chat-booking-card__appointment-actions{display:flex;flex-wrap:wrap;gap:8px}@media (max-width:380px){.ai-chat-booking-card__appointment-actions{flex-direction:column}.ai-chat-booking-card__appointment-cancel,.ai-chat-booking-card__appointment-link{justify-content:center;text-align:center;width:100%}}.ai-chat-booking-card__appointment-link{background:var(--action-accent,var(--primary-color,#3b82f6));border-radius:6px;color:#fff;display:inline-block;font-size:12px;font-weight:500;padding:6px 12px;text-decoration:none;transition:filter .15s ease}.ai-chat-booking-card__appointment-link:hover{filter:brightness(1.1);text-decoration:none}.ai-chat-booking-card__appointment-cancel{background:rgba(220,38,38,.1);border:none;border-radius:6px;color:#dc2626;cursor:pointer;font-family:inherit;font-size:12px;font-weight:500;padding:6px 12px;transition:background .15s ease}.ai-chat-booking-card__appointment-cancel:hover:not(:disabled){background:rgba(220,38,38,.18)}.ai-chat-booking-card__appointment-cancel:disabled{cursor:not-allowed;opacity:.5}.ai-chat-widget.dark .ai-chat-booking-card__appointment-cancel,.chakra-ui-dark .ai-chat-booking-card__appointment-cancel,.dark .ai-chat-booking-card__appointment-cancel,[data-theme=dark] .ai-chat-booking-card__appointment-cancel{background:rgba(239,68,68,.2);color:#fca5a5}.ai-chat-booking-card__summary{background:rgba(0,0,0,.04);border-radius:10px;padding:12px 14px}.ai-chat-widget.dark .ai-chat-booking-card__summary,.chakra-ui-dark .ai-chat-booking-card__summary,.dark .ai-chat-booking-card__summary,[data-theme=dark] .ai-chat-booking-card__summary{background:hsla(0,0%,100%,.06)}.ai-chat-booking-card__summary-row{align-items:center;display:flex;gap:8px;justify-content:space-between;padding:6px 0}@media (max-width:380px){.ai-chat-booking-card__summary-row{align-items:flex-start;flex-direction:column;gap:2px}.ai-chat-booking-card__summary-value{text-align:left}}.ai-chat-booking-card__summary-row:not(:last-child){border-bottom:1px solid rgba(0,0,0,.06)}.ai-chat-widget.dark .ai-chat-booking-card__summary-row:not(:last-child),.chakra-ui-dark .ai-chat-booking-card__summary-row:not(:last-child),.dark .ai-chat-booking-card__summary-row:not(:last-child),[data-theme=dark] .ai-chat-booking-card__summary-row:not(:last-child){border-bottom-color:hsla(0,0%,100%,.08)}.ai-chat-booking-card__summary-label{color:var(--text-muted,#71717a);font-size:12px;font-weight:600;letter-spacing:.5px;text-transform:uppercase}.ai-chat-booking-card__summary-value{color:var(--text-primary,#3e3e3e);font-size:14px;font-weight:500;text-align:right}.ai-chat-widget.dark .ai-chat-booking-card__summary-value,.chakra-ui-dark .ai-chat-booking-card__summary-value,.dark .ai-chat-booking-card__summary-value,[data-theme=dark] .ai-chat-booking-card__summary-value{color:#fff}.ai-chat-booking-card__success-icon{align-items:center;background:var(--action-accent,var(--primary-color,#3b82f6));border-radius:50%;display:flex;height:40px;justify-content:center;margin:0 auto 12px;position:relative;width:40px}.ai-chat-booking-card__success-icon:after{border:solid #fff;border-width:0 3px 3px 0;content:\"\";height:16px;margin-bottom:4px;transform:rotate(45deg);width:10px}.ai-chat-booking-card__success-message{color:var(--text-secondary,#6b7280);font-size:14px;margin:0 0 12px;text-align:center}.ai-chat-widget.dark .ai-chat-booking-card__success-message,.chakra-ui-dark .ai-chat-booking-card__success-message,.dark .ai-chat-booking-card__success-message,[data-theme=dark] .ai-chat-booking-card__success-message{color:var(--text-secondary,#a1a1aa)}.ai-chat-booking-card__link{display:inline-block;font-size:13px;font-weight:500;margin-top:8px;text-decoration:none;transition:opacity .15s ease}.ai-chat-booking-card__link:hover{opacity:.8;text-decoration:underline}.ai-chat-booking-card--success{background:rgba(59,130,246,.08)}.ai-chat-widget.dark .ai-chat-booking-card--success,.chakra-ui-dark .ai-chat-booking-card--success,.dark .ai-chat-booking-card--success,[data-theme=dark] .ai-chat-booking-card--success{background:rgba(59,130,246,.12)}.ai-chat-booking-card--pending{background:hsla(220,9%,46%,.08)}.ai-chat-widget.dark .ai-chat-booking-card--pending,.chakra-ui-dark .ai-chat-booking-card--pending,.dark .ai-chat-booking-card--pending,[data-theme=dark] .ai-chat-booking-card--pending{background:hsla(220,9%,46%,.12)}.ai-chat-booking-card--cancelled{background:hsla(220,9%,46%,.08)}.ai-chat-widget.dark .ai-chat-booking-card--cancelled,.chakra-ui-dark .ai-chat-booking-card--cancelled,.dark .ai-chat-booking-card--cancelled,[data-theme=dark] .ai-chat-booking-card--cancelled{background:hsla(220,9%,46%,.12)}.ai-chat-booking-card--error{background:hsla(220,9%,46%,.08)}.ai-chat-widget.dark .ai-chat-booking-card--error,.chakra-ui-dark .ai-chat-booking-card--error,.dark .ai-chat-booking-card--error,[data-theme=dark] .ai-chat-booking-card--error{background:hsla(220,9%,46%,.12)}.ai-chat-booking-card__pending-text,.ai-chat-booking-card__success-text{color:var(--text-secondary,#6b7280);font-size:13px;margin:0}.ai-chat-widget.dark .ai-chat-booking-card__pending-text,.ai-chat-widget.dark .ai-chat-booking-card__success-text,.chakra-ui-dark .ai-chat-booking-card__pending-text,.chakra-ui-dark .ai-chat-booking-card__success-text,.dark .ai-chat-booking-card__pending-text,.dark .ai-chat-booking-card__success-text,[data-theme=dark] .ai-chat-booking-card__pending-text,[data-theme=dark] .ai-chat-booking-card__success-text{color:var(--text-secondary,#a1a1aa)}.ai-chat-booking-card__error{background:rgba(220,38,38,.08);border-radius:8px;color:#dc2626;font-size:13px;margin:0;padding:10px 12px}.ai-chat-widget.dark .ai-chat-booking-card__error,.chakra-ui-dark .ai-chat-booking-card__error,.dark .ai-chat-booking-card__error,[data-theme=dark] .ai-chat-booking-card__error{background:rgba(239,68,68,.15);color:#fca5a5}.ai-chat-booking-card--closable{position:relative}.ai-chat-booking-card--closable .ai-chat-booking-card__header{padding-right:40px}.ai-chat-pin-input-group{display:flex;flex-wrap:wrap;gap:8px;justify-content:center}@media (max-width:320px){.ai-chat-pin-input-group{gap:6px}}.ai-chat-pin-input{background:rgba(0,0,0,.05);border:none;border-radius:8px;box-sizing:border-box;color:var(--text-primary,#3e3e3e);font-family:inherit;font-size:20px;font-weight:600;height:48px;outline:none;padding:0;text-align:center;transition:background .15s ease,box-shadow .15s ease;width:42px}@media (max-width:320px){.ai-chat-pin-input{font-size:18px;height:42px;width:36px}}.ai-chat-pin-input:focus{background:rgba(0,0,0,.08);box-shadow:0 0 0 2px var(--action-accent,var(--primary-color,#3b82f6))}.ai-chat-pin-input::placeholder{color:var(--text-muted,#9ca3af)}.ai-chat-pin-input:disabled{cursor:not-allowed;opacity:.5}.ai-chat-widget.dark .ai-chat-pin-input,.chakra-ui-dark .ai-chat-pin-input,.dark .ai-chat-pin-input,[data-theme=dark] .ai-chat-pin-input{background:hsla(0,0%,100%,.08);color:#fff}.ai-chat-widget.dark .ai-chat-pin-input:focus,.chakra-ui-dark .ai-chat-pin-input:focus,.dark .ai-chat-pin-input:focus,[data-theme=dark] .ai-chat-pin-input:focus{background:hsla(0,0%,100%,.12)}@media (max-width:480px){.ai-chat-booking-card{padding:12px}.ai-chat-booking-card__title{font-size:14px}.ai-chat-booking-card__option-btn{padding:12px 14px}.ai-chat-booking-card__option-icon{height:28px;width:28px}.ai-chat-booking-card__option-icon svg{height:14px;width:14px}.ai-chat-booking-card__option-text{font-size:13px}.ai-chat-booking-card__slots-container{max-height:240px}.ai-chat-booking-card__slot{font-size:12px;padding:6px 12px}}.ai-chat-structured-image{--action-accent:var(--primary-color,#3b82f6);display:flex;flex-direction:column;gap:8px;width:100%}.ai-chat-structured-image__context{color:var(--text-primary,#3e3e3e);font-size:13px;line-height:1.4;margin:0 0 4px}.ai-chat-widget.dark .ai-chat-structured-image__context,.chakra-ui-dark .ai-chat-structured-image__context,.dark .ai-chat-structured-image__context,[data-theme=dark] .ai-chat-structured-image__context{color:var(--text-primary,#fff)}.ai-chat-structured-image__error{background:rgba(239,68,68,.1);border-radius:var(--radius-md,8px);color:#dc2626;font-size:13px;padding:12px 16px;text-align:center}.ai-chat-widget.dark .ai-chat-structured-image__error,.chakra-ui-dark .ai-chat-structured-image__error,.dark .ai-chat-structured-image__error,[data-theme=dark] .ai-chat-structured-image__error{background:rgba(239,68,68,.2);color:#fca5a5}.ai-chat-structured-image__cards{display:grid;gap:8px;grid-template-columns:repeat(2,1fr)}.ai-chat-structured-image__cards:has(.ai-chat-project-card:only-child){grid-template-columns:1fr}@supports not selector(:has(*)){.ai-chat-structured-image__cards{grid-template-columns:repeat(var(--card-columns,2),1fr)}}@media (max-width:480px){.ai-chat-structured-image__cards{grid-template-columns:1fr}}.ai-chat-media-display{--action-accent:var(--primary-color,#3b82f6);display:flex;flex-direction:column;gap:8px;margin-top:4px;padding:0!important}.ai-chat-media-display__context{color:var(--text-muted,#71717a);font-size:13px;margin:0 0 4px;padding:0 4px}.ai-chat-media-display__error{background:rgba(239,68,68,.1);border-radius:var(--radius-lg,12px);color:#dc2626;font-size:13px;padding:16px;text-align:center}.ai-chat-widget.dark .ai-chat-media-display__error,.chakra-ui-dark .ai-chat-media-display__error,.dark .ai-chat-media-display__error,[data-theme=dark] .ai-chat-media-display__error{background:rgba(239,68,68,.2);color:#fca5a5}.ai-chat-media-display__cards{display:grid;gap:12px;grid-template-columns:repeat(var(--card-columns,1),1fr)}.ai-chat-image-card{--action-accent:var(--primary-color,#3b82f6);background:transparent;display:flex;flex-direction:column}.ai-chat-image-card,.ai-chat-image-card__media{border-radius:var(--radius-lg,12px);overflow:hidden}.ai-chat-image-card__media{position:relative;width:100%}.ai-chat-image-card__image{border-radius:var(--radius-lg,12px);display:block;height:auto;max-height:300px;object-fit:cover;width:100%}.ai-chat-image-card__image-error{align-items:center;background:var(--bg-muted,#e5e7eb);border-radius:var(--radius-lg,12px);color:var(--text-muted,#71717a);display:flex;font-size:13px;height:150px;justify-content:center;width:100%}.ai-chat-image-card__content{padding:10px 0 0}.ai-chat-image-card__title{color:var(--text-primary,#1a1a1a);font-size:14px;font-weight:600;line-height:1.3;margin:0 0 4px}.ai-chat-widget.dark .ai-chat-image-card__title,.dark .ai-chat-image-card__title,[data-theme=dark] .ai-chat-image-card__title{color:#fff}.ai-chat-image-card__description{color:var(--text-muted,#6b7280);font-size:13px;line-height:1.4;margin:0 0 8px}.ai-chat-image-card__link-btn{align-items:center;background:transparent;border:none;color:var(--action-accent);cursor:pointer;display:inline-flex;font-size:13px;font-weight:500;gap:4px;padding:0;transition:opacity .2s}.ai-chat-image-card__link-btn:hover{opacity:.8}.ai-chat-media-card{--action-accent:var(--primary-color,#3b82f6);background:var(--bg-secondary,#f4f4f4);border:1px solid var(--border-subtle,rgba(0,0,0,.08));border-radius:var(--radius-lg,12px);display:flex;flex-direction:column;overflow:hidden}.ai-chat-widget.dark .ai-chat-media-card,.chakra-ui-dark .ai-chat-media-card,.dark .ai-chat-media-card,[data-theme=dark] .ai-chat-media-card{background:var(--bg-secondary,#3a3a3a);border-color:hsla(0,0%,100%,.08)}.ai-chat-media-card__media{background:#000;position:relative;width:100%}.ai-chat-media-card__media.video{aspect-ratio:16/9}.ai-chat-media-card__media.image{aspect-ratio:auto;max-height:400px}.ai-chat-media-card__image{display:block;height:100%;object-fit:cover;width:100%}.ai-chat-media-card__image-error{align-items:center;background:var(--bg-muted,#e5e7eb);color:var(--text-muted,#71717a);display:flex;font-size:13px;height:200px;justify-content:center;width:100%}.ai-chat-media-card__video-placeholder{cursor:pointer;height:100%;position:relative;width:100%}.ai-chat-media-card__video-placeholder img{height:100%;object-fit:cover;width:100%}.ai-chat-media-card__video-bg{background:linear-gradient(135deg,#1a1a2e,#16213e);height:100%;width:100%}.ai-chat-media-card__play-btn{align-items:center;background:rgba(0,0,0,.7);border:none;border-radius:50%;color:#fff;cursor:pointer;display:flex;height:56px;justify-content:center;left:50%;position:absolute;top:50%;transform:translate(-50%,-50%);transition:background .2s,transform .2s;width:56px}.ai-chat-media-card__play-btn:hover{background:rgba(0,0,0,.9);transform:translate(-50%,-50%) scale(1.05)}.ai-chat-media-card__provider-badge{background:rgba(0,0,0,.8);border-radius:4px;bottom:8px;color:#fff;font-size:11px;font-weight:600;letter-spacing:.5px;padding:4px 8px;position:absolute;right:8px;text-transform:uppercase}.ai-chat-media-card__iframe,.ai-chat-media-card__video{border:none;height:100%;left:0;position:absolute;top:0;width:100%}.ai-chat-media-card__content{padding:12px}.ai-chat-media-card__title{color:var(--text-primary,#3e3e3e);font-size:15px;font-weight:600;line-height:1.3;margin:0 0 4px}.ai-chat-widget.dark .ai-chat-media-card__title,.chakra-ui-dark .ai-chat-media-card__title,.dark .ai-chat-media-card__title,[data-theme=dark] .ai-chat-media-card__title{color:#fff}.ai-chat-media-card__description{color:var(--text-muted,#71717a);font-size:13px;line-height:1.4;margin:0 0 8px}.ai-chat-media-card__link-btn{align-items:center;background:var(--action-accent);border:none;border-radius:6px;color:#fff;cursor:pointer;display:inline-flex;font-size:13px;font-weight:500;gap:6px;padding:6px 12px;transition:opacity .2s}.ai-chat-media-card__link-btn:hover{opacity:.9}.ai-chat-image-gallery{--action-accent:var(--primary-color,#3b82f6)}.ai-chat-image-gallery__grid{display:grid;gap:8px;grid-template-columns:repeat(2,1fr)}@media (max-width:480px){.ai-chat-image-gallery__grid{grid-template-columns:1fr}.ai-chat-image-gallery__grid .ai-chat-image-gallery__item:first-child{grid-column:span 1}}.ai-chat-image-gallery__grid:has(.ai-chat-image-gallery__item:only-child){grid-template-columns:1fr}.ai-chat-image-gallery__grid:has(.ai-chat-image-gallery__item:nth-child(3):last-child) .ai-chat-image-gallery__item:first-child{grid-column:span 2}.ai-chat-image-gallery__grid:has(.ai-chat-image-gallery__item:nth-child(5):last-child) .ai-chat-image-gallery__item:first-child{grid-column:span 2}.ai-chat-image-gallery__grid:has(.ai-chat-image-gallery__item:nth-child(7):last-child) .ai-chat-image-gallery__item:first-child{grid-column:span 2}.ai-chat-image-gallery__item.span-full{grid-column:span 2}.ai-chat-image-gallery[data-item-count=\"1\"] .ai-chat-image-gallery__grid{grid-template-columns:1fr}.ai-chat-image-gallery__item{position:relative}.ai-chat-image-gallery__item-media{aspect-ratio:4/3;background:var(--bg-muted,#e5e7eb);border-radius:var(--radius-lg,12px);overflow:hidden;position:relative}.ai-chat-image-gallery__item-media img{height:100%;object-fit:cover;width:100%}.ai-chat-image-gallery__item-overlay{background:linear-gradient(0deg,rgba(0,0,0,.85) 0,rgba(0,0,0,.4) 60%,transparent);border-radius:0 0 var(--radius-lg,12px) var(--radius-lg,12px);bottom:0;left:0;padding:24px 12px 10px;position:absolute;right:0}.ai-chat-image-gallery__item-title{-webkit-line-clamp:2;line-clamp:2;-webkit-box-orient:vertical;color:#fff;display:-webkit-box;font-size:12px;font-weight:600;line-height:1.3;margin:0;overflow:hidden;text-shadow:0 1px 3px rgba(0,0,0,.5)}.ai-chat-image-gallery__error{align-items:center;color:var(--text-muted,#71717a);display:flex;font-size:12px;height:100%;justify-content:center;width:100%}.ai-chat-media-gallery{--action-accent:var(--primary-color,#3b82f6)}.ai-chat-media-gallery__grid{display:grid;gap:8px;grid-template-columns:repeat(2,1fr)}.ai-chat-image-gallery__lightbox,.ai-chat-media-gallery__lightbox{align-items:center;background:rgba(0,0,0,.95);bottom:0;display:flex;justify-content:center;left:0;padding:40px;position:fixed;right:0;top:0;z-index:10000}.ai-chat-image-gallery__lightbox-close,.ai-chat-media-gallery__lightbox-close{align-items:center;background:hsla(0,0%,100%,.1);border:none;border-radius:50%;color:#fff;cursor:pointer;display:flex;height:40px;justify-content:center;position:absolute;right:16px;top:16px;transition:background .2s;width:40px}.ai-chat-image-gallery__lightbox-close:hover,.ai-chat-media-gallery__lightbox-close:hover{background:hsla(0,0%,100%,.2)}.ai-chat-image-gallery__lightbox-nav,.ai-chat-media-gallery__lightbox-nav{align-items:center;background:hsla(0,0%,100%,.1);border:none;border-radius:50%;color:#fff;cursor:pointer;display:flex;height:48px;justify-content:center;position:absolute;top:50%;transform:translateY(-50%);transition:background .2s;width:48px}.ai-chat-image-gallery__lightbox-nav:hover,.ai-chat-media-gallery__lightbox-nav:hover{background:hsla(0,0%,100%,.2)}.ai-chat-image-gallery__lightbox-nav.prev,.ai-chat-media-gallery__lightbox-nav.prev{left:16px}.ai-chat-image-gallery__lightbox-nav.next,.ai-chat-media-gallery__lightbox-nav.next{right:16px}.ai-chat-image-gallery__lightbox-content,.ai-chat-media-gallery__lightbox-content{align-items:center;display:flex;flex-direction:column;max-height:80vh;max-width:90vw}.ai-chat-image-gallery__lightbox-content img,.ai-chat-media-gallery__lightbox-content img{border-radius:4px;max-height:70vh;max-width:100%;object-fit:contain}.ai-chat-image-gallery__lightbox-caption,.ai-chat-media-gallery__lightbox-caption{color:#fff;margin-top:16px;text-align:center}.ai-chat-image-gallery__lightbox-caption h4,.ai-chat-media-gallery__lightbox-caption h4{font-size:16px;font-weight:600;margin:0 0 4px}.ai-chat-image-gallery__lightbox-caption p,.ai-chat-media-gallery__lightbox-caption p{color:hsla(0,0%,100%,.7);font-size:14px;margin:0}.ai-chat-image-gallery__lightbox-counter,.ai-chat-media-gallery__lightbox-counter{background:rgba(0,0,0,.6);border-radius:20px;bottom:16px;color:#fff;font-size:13px;left:50%;padding:6px 12px;position:absolute;transform:translateX(-50%)}.ai-chat-project-card{--action-accent:var(--primary-color,#3b82f6);background:var(--bg-secondary,#f4f4f4);border:1px solid var(--border-subtle,rgba(0,0,0,.08));border-radius:var(--radius-lg,12px);display:flex;flex-direction:column;overflow:hidden;transition:border-color .2s,box-shadow .2s,transform .2s}.ai-chat-project-card.clickable{cursor:pointer}.ai-chat-project-card.clickable:hover{border-color:var(--action-accent);box-shadow:0 4px 12px rgba(0,0,0,.1);transform:translateY(-2px)}.ai-chat-widget.dark .ai-chat-project-card,.chakra-ui-dark .ai-chat-project-card,.dark .ai-chat-project-card,[data-theme=dark] .ai-chat-project-card{background:var(--bg-secondary,#2a2a2a);border-color:hsla(0,0%,100%,.1)}.ai-chat-widget.dark .ai-chat-project-card.clickable:hover,.chakra-ui-dark .ai-chat-project-card.clickable:hover,.dark .ai-chat-project-card.clickable:hover,[data-theme=dark] .ai-chat-project-card.clickable:hover{border-color:var(--action-accent);box-shadow:0 4px 16px rgba(0,0,0,.3)}.ai-chat-project-card--image-only{background:transparent;border:none}.ai-chat-project-card--image-only .ai-chat-project-card__media{aspect-ratio:16/9;border-radius:var(--radius-lg,12px)}.ai-chat-project-card--image-only.clickable:hover{border:none;box-shadow:0 4px 16px rgba(0,0,0,.2)}.ai-chat-project-card--overlay{background:transparent;border:none;border-radius:var(--radius-lg,12px);overflow:hidden}.ai-chat-project-card--overlay .ai-chat-project-card__media{aspect-ratio:4/3;border-radius:var(--radius-lg,12px)}.ai-chat-project-card__overlay{background:linear-gradient(0deg,rgba(0,0,0,.85) 0,rgba(0,0,0,.5) 50%,transparent);border-radius:0 0 var(--radius-lg,12px) var(--radius-lg,12px);bottom:0;left:0;padding:10px 12px;position:absolute;right:0}.ai-chat-project-card__overlay-title{-webkit-line-clamp:2;line-clamp:2;-webkit-box-orient:vertical;color:#fff;display:-webkit-box;font-size:12px;font-weight:600;line-height:1.3;margin:0;overflow:hidden;text-shadow:0 1px 3px rgba(0,0,0,.5)}.ai-chat-project-card--full .ai-chat-project-card__media{aspect-ratio:16/10;border-bottom:1px solid var(--border-subtle,rgba(0,0,0,.06))}.ai-chat-widget.dark .ai-chat-project-card--full .ai-chat-project-card__media,.chakra-ui-dark .ai-chat-project-card--full .ai-chat-project-card__media,.dark .ai-chat-project-card--full .ai-chat-project-card__media,[data-theme=dark] .ai-chat-project-card--full .ai-chat-project-card__media{border-bottom-color:hsla(0,0%,100%,.08)}.ai-chat-project-card__media{aspect-ratio:16/10;background:var(--bg-muted,#e5e7eb);overflow:hidden;position:relative;width:100%}.ai-chat-widget.dark .ai-chat-project-card__media,.chakra-ui-dark .ai-chat-project-card__media,.dark .ai-chat-project-card__media,[data-theme=dark] .ai-chat-project-card__media{background:hsla(0,0%,100%,.05)}.ai-chat-project-card__media img{height:100%;object-fit:cover;transition:transform .3s ease;width:100%}.ai-chat-project-card.clickable:hover .ai-chat-project-card__media img{transform:scale(1.03)}.ai-chat-project-card__iframe,.ai-chat-project-card__video{border:none;height:100%;left:0;position:absolute;top:0;width:100%}.ai-chat-project-card__placeholder{align-items:center;color:var(--text-muted,#71717a);display:flex;font-size:13px;height:100%;justify-content:center;width:100%}.ai-chat-project-card__play-btn{align-items:center;background:rgba(0,0,0,.7);border:none;border-radius:50%;color:#fff;cursor:pointer;display:flex;height:48px;justify-content:center;left:50%;position:absolute;top:50%;transform:translate(-50%,-50%);transition:background .2s,transform .2s;width:48px}.ai-chat-project-card__play-btn:hover{background:rgba(0,0,0,.9);transform:translate(-50%,-50%) scale(1.1)}.ai-chat-project-card__content{background:var(--bg-secondary,#f4f4f4);padding:12px 14px 14px}.ai-chat-widget.dark .ai-chat-project-card__content,.chakra-ui-dark .ai-chat-project-card__content,.dark .ai-chat-project-card__content,[data-theme=dark] .ai-chat-project-card__content{background:var(--bg-secondary,#2a2a2a)}.ai-chat-project-card__title{color:var(--text-primary,#1a1a1a);font-size:14px;font-weight:600;line-height:1.35;margin:0 0 6px}.ai-chat-widget.dark .ai-chat-project-card__title,.chakra-ui-dark .ai-chat-project-card__title,.dark .ai-chat-project-card__title,[data-theme=dark] .ai-chat-project-card__title{color:#fff}.ai-chat-project-card__description{-webkit-line-clamp:2;line-clamp:2;-webkit-box-orient:vertical;color:var(--text-muted,#6b7280);display:-webkit-box;font-size:13px;line-height:1.45;margin:0 0 10px;overflow:hidden}.ai-chat-project-card__link{align-items:center;color:var(--action-accent);display:inline-flex;font-size:13px;font-weight:500;gap:4px;transition:opacity .2s}.ai-chat-project-card__link:hover{opacity:.8}.ai-chat-media-carousel{--action-accent:var(--primary-color,#3b82f6);border-radius:var(--radius-lg,12px);overflow:hidden;position:relative}.ai-chat-media-carousel__track{display:flex;transition:transform .3s ease-out}.ai-chat-media-carousel__slide{flex:0 0 100%;min-width:0}.ai-chat-media-carousel__slide .ai-chat-project-card{border-radius:var(--radius-md,8px);margin:0 4px}.ai-chat-media-carousel__nav{align-items:center;background:hsla(0,0%,100%,.9);border:none;border-radius:50%;box-shadow:0 2px 8px rgba(0,0,0,.15);color:#333;cursor:pointer;display:flex;height:32px;justify-content:center;position:absolute;top:50%;transform:translateY(-50%);transition:background .2s;width:32px;z-index:2}.ai-chat-media-carousel__nav:hover{background:#fff}.ai-chat-media-carousel__nav.prev{left:8px}.ai-chat-media-carousel__nav.next{right:8px}.ai-chat-widget.dark .ai-chat-media-carousel__nav,.chakra-ui-dark .ai-chat-media-carousel__nav,.dark .ai-chat-media-carousel__nav,[data-theme=dark] .ai-chat-media-carousel__nav{background:rgba(0,0,0,.7);color:#fff}.ai-chat-widget.dark .ai-chat-media-carousel__nav:hover,.chakra-ui-dark .ai-chat-media-carousel__nav:hover,.dark .ai-chat-media-carousel__nav:hover,[data-theme=dark] .ai-chat-media-carousel__nav:hover{background:rgba(0,0,0,.9)}.ai-chat-media-carousel__dots{display:flex;gap:6px;justify-content:center;padding:12px 0 4px}.ai-chat-media-carousel__dot{background:var(--border-subtle,rgba(0,0,0,.2));border:none;border-radius:50%;cursor:pointer;height:8px;padding:0;transition:background .2s,transform .2s;width:8px}.ai-chat-media-carousel__dot:hover{transform:scale(1.2)}.ai-chat-media-carousel__dot.active{background:var(--action-accent)}.ai-chat-widget.dark .ai-chat-media-carousel__dot,.chakra-ui-dark .ai-chat-media-carousel__dot,.dark .ai-chat-media-carousel__dot,[data-theme=dark] .ai-chat-media-carousel__dot{background:hsla(0,0%,100%,.3)}.ai-chat-widget.dark .ai-chat-media-carousel__dot.active,.chakra-ui-dark .ai-chat-media-carousel__dot.active,.dark .ai-chat-media-carousel__dot.active,[data-theme=dark] .ai-chat-media-carousel__dot.active{background:var(--action-accent)}@media (max-width:480px){.ai-chat-media-carousel__nav{height:28px;width:28px}.ai-chat-media-carousel__nav.prev{left:4px}.ai-chat-media-carousel__nav.next{right:4px}.ai-chat-image-gallery__lightbox-nav,.ai-chat-media-gallery__lightbox-nav{height:40px;width:40px}.ai-chat-image-gallery__lightbox,.ai-chat-media-gallery__lightbox{padding:16px}.ai-chat-project-card__content{padding:10px 12px 12px}.ai-chat-project-card__title{font-size:13px}.ai-chat-project-card__description{font-size:12px}}.chat-fullpage{--fp-max-width:800px;--fp-padding-x:16px;--fp-padding-top-mobile:64px;--fp-padding-top-desktop:24px;--fp-padding-bottom:200px;--fp-input-bottom:0}.chat-fullpage .ai-chat-messages{background:transparent;flex:1 1 auto;height:auto;justify-content:flex-start;margin:0 auto;max-height:none;max-width:var(--fp-max-width);min-height:0!important;overflow-x:hidden!important;overflow-y:scroll!important;padding:var(--fp-padding-top-desktop) var(--fp-padding-x) var(--fp-padding-bottom);position:relative}@media (max-width:767px){.chat-fullpage .ai-chat-messages{padding-top:var(--fp-padding-top-mobile)!important}}@media (max-width:480px){.chat-fullpage .ai-chat-messages{padding-top:calc(var(--fp-padding-top-mobile) + env(safe-area-inset-top, 0px))}}.chat-fullpage .ai-chat-messages>.ai-chat-message:first-child,.chat-fullpage .ai-chat-messages>.ai-chat-welcome:first-child{margin-top:0}.chat-fullpage .ai-chat-message{animation:none}.chat-fullpage .ai-chat-message.user{max-width:85%}.chat-fullpage .ai-chat-message.user .ai-chat-message-content{background:var(--bg-muted,#f4f4f5);border-radius:24px;color:#000;padding:8px 16px}.chat-fullpage.dark .ai-chat-message.user .ai-chat-message-content{background:var(--bg-muted,#27272a);color:#fff}.chat-fullpage .ai-chat-message.assistant{width:100%}.chat-fullpage .ai-chat-message.assistant .ai-chat-message-content{color:#000;padding:8px 16px}.chat-fullpage.dark .ai-chat-message.assistant .ai-chat-message-content{color:#fff}.chat-fullpage .ai-chat-message.tool{margin:0;padding:0;width:100%}.chat-fullpage .ai-chat-welcome{align-items:center;display:flex;flex-direction:column;gap:24px;justify-content:center;min-height:60vh;padding:24px;text-align:center}.chat-fullpage .ai-chat-welcome-title{font-size:32px;font-weight:600}.chat-fullpage .ai-chat-welcome-text{color:var(--text-muted,#71717a);font-size:18px;max-width:400px}.chat-fullpage .ai-chat-input-container{background:transparent;bottom:0;left:0;padding:16px 16px calc(16px + env(safe-area-inset-bottom));position:fixed;right:0;z-index:20}.chat-fullpage .ai-chat-input-container:after{background:var(--bg-primary,#fff);bottom:0;content:\"\";height:calc(40% + 16px);left:0;pointer-events:none;position:absolute;right:0;z-index:-1}.chat-fullpage.dark .ai-chat-input-container:after{background:var(--bg-primary,#18181b)}@media (min-width:769px){.chat-fullpage .ai-chat-input-container{background:transparent;bottom:var(--fp-input-bottom);left:50%;max-width:var(--fp-max-width);padding:12px 0 0;position:absolute;right:auto;transform:translateX(-50%);width:100%}}.chat-fullpage .ai-chat-input-wrapper{background:var(--bg-muted,#f4f4f5);border:1px solid var(--border-muted,#e4e4e7);border-radius:24px;box-shadow:0 1px 8px rgba(0,0,0,.06);margin:0 auto;max-width:var(--fp-max-width)}.chat-fullpage.dark .ai-chat-input-wrapper{background:var(--bg-muted,#27272a);border-color:var(--border-muted,#3f3f46);box-shadow:0 1px 12px rgba(0,0,0,.25)}.chat-fullpage .ai-chat-scroll-button{bottom:100px}@media (min-width:769px){.chat-fullpage .ai-chat-scroll-button{bottom:110px}}.chat-fullpage .ai-chat-suggested-questions{display:flex;flex-wrap:wrap;gap:8px;justify-content:center;max-width:600px}.chat-fullpage .ai-chat-suggested-question{border-radius:9999px;font-size:14px;padding:8px 16px}.chat-fullpage .ai-chat-follow-up-suggestions{margin-top:12px}.chat-fullpage .ai-chat-follow-up-item.question-type{background:transparent;border:1px solid var(--border-default,#d4d4d8);color:#000}.chat-fullpage .ai-chat-follow-up-item.question-type:hover{background:var(--bg-hover,#f4f4f5)}.chat-fullpage.dark .ai-chat-follow-up-item.question-type{background:transparent;border:1px solid var(--border-subtle,#52525b);color:#fff}.chat-fullpage.dark .ai-chat-follow-up-item.question-type:hover{background:var(--bg-hover,#3f3f46)}.chat-fullpage .ai-chat-typing{padding:8px 16px}.chat-fullpage .ai-chat-message-meta{align-items:center;display:flex;gap:8px;margin-top:4px;padding:0 16px}.chat-fullpage .ai-chat-message-timestamp{color:var(--text-muted,#71717a);font-size:12px}.chat-fullpage .ai-chat-feedback{display:inline-flex}.chat-fullpage .ai-chat-feedback-button{display:flex}@media (max-width:480px){body.ai-chat-widget-open{height:100%!important;overflow:hidden!important;position:fixed!important;touch-action:none!important;width:100%!important}.ai-chat-widget-container:not(.is-open).bottom-right .ai-chat-button{bottom:12px!important;right:12px!important}.ai-chat-widget-container:not(.is-open).bottom-left .ai-chat-button{bottom:12px!important;left:12px!important}.ai-chat-widget-container:not(.is-open).top-right .ai-chat-button{right:12px!important;top:12px!important}.ai-chat-widget-container:not(.is-open).top-left .ai-chat-button{left:12px!important;top:12px!important}.ai-chat-widget-container.trigger-pill-text.container-mode:not(.is-open).bottom-right .ai-chat-trigger-pill{bottom:12px!important;right:12px!important}.ai-chat-widget-container.trigger-pill-text.container-mode:not(.is-open).bottom-left .ai-chat-trigger-pill{bottom:12px!important;left:12px!important}.ai-chat-widget-container.trigger-pill-text.container-mode:not(.is-open).top-right .ai-chat-trigger-pill{right:12px!important;top:12px!important}.ai-chat-widget-container.trigger-pill-text.container-mode:not(.is-open).top-left .ai-chat-trigger-pill{left:12px!important;top:12px!important}.ai-chat-widget-container.trigger-input-bar.container-mode:not(.is-open) .ai-chat-trigger-input-container{bottom:12px!important;left:12px!important;right:12px!important;width:calc(100% - 24px)!important}.ai-chat-widget-container.trigger-input-bar:not(.is-open) .ai-chat-trigger-input-wrapper{max-width:100%!important;width:100%!important}.ai-chat-widget-container.trigger-input-bar:not(.is-open) .ai-chat-trigger-input-row{display:flex;gap:8px;max-width:calc(100vw - 24px)!important;width:calc(100vw - 24px)!important}.ai-chat-widget-container.trigger-input-bar.is-collapsed:not(.is-open) .ai-chat-trigger-input-container{bottom:12px!important;right:12px!important;width:auto!important}.ai-chat-widget-container.trigger-input-bar.is-collapsed:not(.is-open) .ai-chat-button{margin-left:0!important;margin-right:0!important}.ai-chat-widget-container.is-open{height:100vh!important;height:100dvh!important;width:100vw!important;z-index:var(--widget-z-index,2147483647)!important}.ai-chat-widget-container.is-open,.ai-chat-widget-container.is-open .ai-chat-window{bottom:0!important;left:0!important;position:fixed!important;right:0!important;top:0!important}.ai-chat-widget-container.is-open .ai-chat-window{animation:none!important;border:none!important;border-radius:0!important;box-shadow:none!important;height:100%!important;max-height:100%!important;max-width:100%!important;outline:none!important;transform:none!important;width:100%!important}.ai-chat-widget-container.is-open .ai-chat-button{display:none!important;pointer-events:none!important;visibility:hidden!important}.ai-chat-widget-container.is-open .ai-chat-header{border-radius:0!important;flex-shrink:0;padding-left:max(16px,env(safe-area-inset-left));padding-right:max(16px,env(safe-area-inset-right));padding-top:max(12px,env(safe-area-inset-top));position:relative;z-index:100}.ai-chat-widget-container.is-open .ai-chat-messages{-webkit-overflow-scrolling:touch;flex:1;overflow-x:hidden;overflow-y:auto;overscroll-behavior:contain;padding-bottom:120px;padding-left:max(16px,env(safe-area-inset-left));padding-right:max(16px,env(safe-area-inset-right));touch-action:pan-y}.ai-chat-widget-container.is-open .ai-chat-input-container{background:var(--bg-primary,#fff);bottom:0!important;left:0!important;padding:8px max(12px,env(safe-area-inset-right)) max(16px,calc(env(safe-area-inset-bottom) + 8px)) max(12px,env(safe-area-inset-left));position:fixed!important;right:0!important;z-index:100}.ai-chat-widget.dark .ai-chat-widget-container.is-open .ai-chat-input-container{background:var(--bg-primary,#282625)}.ai-chat-widget-container.is-open .ai-chat-input-container:after{display:none}.ai-chat-widget-container.is-open .ai-chat-input-wrapper{margin:0;max-width:100%}.ai-chat-widget-container.is-open .ai-chat-scroll-button{bottom:calc(80px + env(safe-area-inset-bottom))}.ai-chat-widget-container.is-open .ai-chat-welcome{padding:16px 0}.ai-chat-widget-container.is-open .ai-chat-welcome-title{font-size:24px}.ai-chat-widget-container.is-open .ai-chat-suggested-questions{align-items:stretch;flex-direction:column}.ai-chat-widget-container.is-open .ai-chat-suggested-question{text-align:center;width:100%}.ai-chat-widget-container.is-open .ai-chat-message-meta{align-items:center;display:flex;gap:8px;margin-top:4px}.ai-chat-widget-container.is-open .ai-chat-message-timestamp{font-size:11px}.ai-chat-widget-container.is-open .ai-chat-feedback{display:inline-flex}.ai-chat-widget-container.is-open .ai-chat-feedback-button{display:flex;padding:4px}.ai-chat-widget-container.is-open .ai-chat-input{font-size:16px!important}}@media (min-width:481px) and (max-width:768px){.ai-chat-widget-container.is-open .ai-chat-window{border-radius:22px 22px 44px 44px;max-height:calc(100vh - 100px);max-width:calc(100vw - 32px)}}.ai-chat-widget-container.desktop-mode.container-mode.is-open{height:100%!important;inset:0!important;position:absolute!important;width:100%!important}.ai-chat-widget-container.desktop-mode.container-mode.is-open .ai-chat-window{border:1px solid var(--border-color,rgba(0,0,0,.08))!important;border-radius:var(--radius-window-top,22px) var(--radius-window-top,22px) var(--radius-window-bottom,44px) var(--radius-window-bottom,44px)!important;box-shadow:var(--window-shadow,0 20px 60px -10px rgba(0,0,0,.25))!important;position:absolute!important}.ai-chat-widget-container.desktop-mode.container-mode.size-small.is-open .ai-chat-window{height:min(500px,calc(100% - 120px))!important;max-height:500px!important;max-width:380px!important;min-height:300px!important;min-width:280px!important;width:min(380px,calc(100% - 40px))!important}.ai-chat-widget-container.desktop-mode.container-mode.size-medium.is-open .ai-chat-window{height:min(65%,calc(100% - 120px))!important;max-height:680px!important;max-width:440px!important;min-height:400px!important;min-width:320px!important;width:min(440px,calc(100% - 40px))!important}.ai-chat-widget-container.desktop-mode.container-mode.size-large.is-open .ai-chat-window{height:calc(100% - 100px)!important;max-height:calc(100% - 100px)!important;max-width:560px!important;min-height:500px!important;min-width:380px!important;width:min(560px,calc(100% - 40px))!important}.ai-chat-widget-container.desktop-mode.container-mode.is-open.bottom-right .ai-chat-window{bottom:88px!important;left:auto!important;right:20px!important;top:auto!important}.ai-chat-widget-container.desktop-mode.container-mode.is-open.bottom-left .ai-chat-window{bottom:88px!important;left:20px!important;right:auto!important;top:auto!important}.ai-chat-widget-container.desktop-mode.container-mode.is-open.top-right .ai-chat-window{bottom:auto!important;left:auto!important;right:20px!important;top:88px!important}.ai-chat-widget-container.desktop-mode.container-mode.is-open.top-left .ai-chat-window{bottom:auto!important;left:20px!important;right:auto!important;top:88px!important}.ai-chat-widget-container.desktop-mode.is-open .ai-chat-button{display:flex!important;opacity:1!important;pointer-events:auto!important;visibility:visible!important}.ai-chat-widget-container.desktop-mode.container-mode:not(.trigger-input-bar).bottom-right .ai-chat-button{bottom:20px!important;left:auto!important;right:20px!important;top:auto!important}.ai-chat-widget-container.desktop-mode.container-mode:not(.trigger-input-bar).bottom-left .ai-chat-button{bottom:20px!important;left:20px!important;right:auto!important;top:auto!important}.ai-chat-widget-container.desktop-mode.container-mode:not(.trigger-input-bar).top-right .ai-chat-button{bottom:auto!important;left:auto!important;right:20px!important;top:20px!important}.ai-chat-widget-container.desktop-mode.container-mode:not(.trigger-input-bar).top-left .ai-chat-button{bottom:auto!important;left:20px!important;right:auto!important;top:20px!important}.ai-chat-widget-container.desktop-mode.container-mode.trigger-input-bar.bottom-right .ai-chat-trigger-input-container{bottom:20px!important;left:auto!important;right:20px!important;top:auto!important}.ai-chat-widget-container.desktop-mode.container-mode.trigger-input-bar.bottom-left .ai-chat-trigger-input-container{bottom:20px!important;left:20px!important;right:auto!important;top:auto!important}.ai-chat-widget-container.desktop-mode.container-mode.trigger-input-bar.top-right .ai-chat-trigger-input-container{bottom:auto!important;left:auto!important;right:20px!important;top:20px!important}.ai-chat-widget-container.desktop-mode.container-mode.trigger-input-bar.top-left .ai-chat-trigger-input-container{bottom:auto!important;left:20px!important;right:auto!important;top:20px!important}.ai-chat-widget-container.desktop-mode.container-mode.trigger-input-bar.size-small .ai-chat-trigger-input-container{width:min(380px,calc(100% - 40px))!important}.ai-chat-widget-container.desktop-mode.container-mode.trigger-input-bar.size-medium .ai-chat-trigger-input-container{width:min(440px,calc(100% - 40px))!important}.ai-chat-widget-container.desktop-mode.container-mode.trigger-input-bar.size-large .ai-chat-trigger-input-container{width:min(560px,calc(100% - 40px))!important}.ai-chat-widget-container.desktop-mode.container-mode.trigger-input-bar.is-open.bottom-left .ai-chat-window,.ai-chat-widget-container.desktop-mode.container-mode.trigger-input-bar.is-open.bottom-right .ai-chat-window{bottom:20px!important}.ai-chat-widget-container.desktop-mode.container-mode.trigger-input-bar.is-open.top-left .ai-chat-window,.ai-chat-widget-container.desktop-mode.container-mode.trigger-input-bar.is-open.top-right .ai-chat-window{top:20px!important}.ai-chat-widget-container.desktop-mode.container-mode.trigger-input-bar.is-open.bottom-right .ai-chat-window,.ai-chat-widget-container.desktop-mode.container-mode.trigger-input-bar.is-open.top-right .ai-chat-window{left:auto!important;right:20px!important}.ai-chat-widget-container.desktop-mode.container-mode.trigger-input-bar.is-open.bottom-left .ai-chat-window,.ai-chat-widget-container.desktop-mode.container-mode.trigger-input-bar.is-open.top-left .ai-chat-window{left:20px!important;right:auto!important}";
|
|
30847
31237
|
styleInject(css_248z$1);
|
|
30848
31238
|
|
|
30849
|
-
var css_248z = ".ai-chat-data-policy-view{display:flex;flex:1;flex-direction:column;min-height:0;overflow:hidden}.ai-chat-data-policy-content{-webkit-overflow-scrolling:touch;flex:1;overflow-y:auto;padding:20px 16px 40px}.ai-chat-data-policy-intro{margin-bottom:24px}.ai-chat-data-policy-intro p{color:var(--text-primary,#18181b);font-size:13px;line-height:1.6;margin:0;text-align:left}.ai-chat-widget.dark .ai-chat-data-policy-intro p{color:var(--text-primary,#fafafa)}.ai-chat-data-policy-intro strong{color:var(--text-primary,#18181b)}.ai-chat-widget.dark .ai-chat-data-policy-intro strong{color:var(--text-primary,#fafafa)}.ai-chat-data-policy-section{margin-bottom:20px}.ai-chat-data-policy-section h3{color:var(--text-primary,#18181b);font-size:14px;font-weight:600;margin:0 0 12px}.ai-chat-widget.dark .ai-chat-data-policy-section h3{color:var(--text-primary,#fafafa)}.ai-chat-data-policy-section p{color:var(--text-secondary,#52525b);font-size:12px;line-height:1.6;margin:0 0 12px;text-align:justify;text-justify:inter-word}.ai-chat-widget.dark .ai-chat-data-policy-section p{color:var(--text-secondary,#a1a1aa)}.ai-chat-data-policy-section p strong{color:var(--text-primary,#18181b)}.ai-chat-widget.dark .ai-chat-data-policy-section p strong{color:var(--text-primary,#fafafa)}.ai-chat-data-policy-section p:last-child{margin-bottom:0}";
|
|
31239
|
+
var css_248z = ".ai-chat-data-policy-view{display:flex;flex:1;flex-direction:column;min-height:0;overflow:hidden}.ai-chat-data-policy-content{-webkit-overflow-scrolling:touch;flex:1;overflow-y:auto;padding:20px 16px 40px}.ai-chat-data-policy-intro{margin-bottom:24px}.ai-chat-data-policy-intro p{color:var(--text-primary,#18181b);font-size:13px;line-height:1.6;margin:0;text-align:left}.ai-chat-widget.dark .ai-chat-data-policy-intro p{color:var(--text-primary,#fafafa)}.ai-chat-data-policy-intro strong{color:var(--text-primary,#18181b)}.ai-chat-widget.dark .ai-chat-data-policy-intro strong{color:var(--text-primary,#fafafa)}.ai-chat-data-policy-section{margin-bottom:20px}.ai-chat-data-policy-section h3{color:var(--text-primary,#18181b);font-size:14px;font-weight:600;margin:0 0 12px}.ai-chat-widget.dark .ai-chat-data-policy-section h3{color:var(--text-primary,#fafafa)}.ai-chat-data-policy-section p{color:var(--text-secondary,#52525b);font-size:12px;line-height:1.6;margin:0 0 12px;text-align:justify;text-justify:inter-word}.ai-chat-widget.dark .ai-chat-data-policy-section p{color:var(--text-secondary,#a1a1aa)}.ai-chat-data-policy-section p strong{color:var(--text-primary,#18181b)}.ai-chat-widget.dark .ai-chat-data-policy-section p strong{color:var(--text-primary,#fafafa)}.ai-chat-data-policy-section p:last-child{margin-bottom:0}.ai-chat-widget-embedded .ai-chat-data-policy-view{-webkit-overflow-scrolling:touch;overflow-y:auto}.ai-chat-widget-embedded .ai-chat-data-policy-content{margin:0 auto;max-width:640px;overflow-y:visible;padding:32px 24px 60px}.ai-chat-widget-embedded .ai-chat-data-policy-intro p{font-size:14px;line-height:1.7}.ai-chat-widget-embedded .ai-chat-data-policy-section h3{font-size:15px;margin-bottom:14px}.ai-chat-widget-embedded .ai-chat-data-policy-section p{font-size:13px;line-height:1.7}.ai-chat-widget-embedded.dark .ai-chat-data-policy-intro p,.ai-chat-widget-embedded.dark .ai-chat-data-policy-intro strong,.ai-chat-widget-embedded.dark .ai-chat-data-policy-section h3{color:var(--text-primary,#fafafa)}.ai-chat-widget-embedded.dark .ai-chat-data-policy-section p{color:var(--text-secondary,#a1a1aa)}.ai-chat-widget-embedded.dark .ai-chat-data-policy-section p strong{color:var(--text-primary,#fafafa)}";
|
|
30850
31240
|
styleInject(css_248z);
|
|
30851
31241
|
|
|
30852
|
-
|
|
30853
|
-
const iconComponents = {
|
|
30854
|
-
FiMessageCircle: () => (jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: jsx("path", { d: "M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z" }) })),
|
|
30855
|
-
FiChevronDown: () => (jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: jsx("polyline", { points: "6 9 12 15 18 9" }) })),
|
|
30856
|
-
};
|
|
30857
|
-
const ChatWidget = ({ widgetId, apiUrl = window.location.origin, previewMode = false, previewConfig, demoMode = false, demoInput, demoOutput, onDemoComplete, position = 'bottom-right', primaryColor, size, headerTitle, welcomeTitle, welcomeMessage, placeholder, welcomeBubbleText, triggerType, triggerText, theme, suggestedQuestions, customStyles, currentRoute, defaultOpen = false, zIndex, containerMode = false, onOpen, onClose, onMessage, onError, mode = 'bubble', }) => {
|
|
31242
|
+
const ChatWidget = ({ widgetId, apiUrl = window.location.origin, previewMode = false, previewConfig, position = 'bottom-right', primaryColor, size, headerTitle, welcomeTitle, welcomeMessage, placeholder, welcomeBubbleText, triggerType, triggerText, theme, suggestedQuestions, customStyles, currentRoute, defaultOpen = false, zIndex, containerMode = false, mobileMode = false, desktopMode = false, onOpen, onClose, onMessage, onError, mode = 'bubble', }) => {
|
|
30858
31243
|
const [isOpen, setIsOpen] = useState(defaultOpen);
|
|
30859
31244
|
const [inputBarValue, setInputBarValue] = useState('');
|
|
30860
|
-
const [autoDetectedTheme, setAutoDetectedTheme] = useState('light');
|
|
30861
31245
|
const [showWelcomeBubble, setShowWelcomeBubble] = useState(false);
|
|
31246
|
+
const [isInputBarCollapsed, setIsInputBarCollapsed] = useState(false);
|
|
30862
31247
|
const widgetRef = useRef(null);
|
|
30863
31248
|
const containerRef = useRef(null);
|
|
30864
|
-
// Demo mode state - track whether demo has been activated and completed
|
|
30865
|
-
const [demoMessages, setDemoMessages] = useState([]);
|
|
30866
|
-
const [isDemoComplete, setIsDemoComplete] = useState(false);
|
|
30867
|
-
const [isDemoTyping, setIsDemoTyping] = useState(false);
|
|
30868
|
-
const [demoInputBarText, setDemoInputBarText] = useState(''); // For animating text in input bar
|
|
30869
|
-
const [isDemoAnimatingInput, setIsDemoAnimatingInput] = useState(false); // True while typing into input bar
|
|
30870
|
-
const [isDemoActive, setIsDemoActive] = useState(false); // True when demo animation is running
|
|
30871
|
-
const demoStartedRef = useRef(false);
|
|
30872
|
-
// Track if user has sent a real message (transitions from demo to real chat)
|
|
30873
|
-
const [userSentRealMessage, setUserSentRealMessage] = useState(false);
|
|
30874
31249
|
// Determine mode
|
|
30875
31250
|
const isEmbedded = mode === 'embedded';
|
|
30876
31251
|
// Default config for preview mode
|
|
@@ -30916,24 +31291,22 @@ const ChatWidget = ({ widgetId, apiUrl = window.location.origin, previewMode = f
|
|
|
30916
31291
|
...previewConfig?.behavior,
|
|
30917
31292
|
},
|
|
30918
31293
|
};
|
|
30919
|
-
// Always call useChat hook (React rules), but skip initialization in preview mode
|
|
30920
|
-
|
|
30921
|
-
// Note: We still pass the real widgetId so createDemoConversation works correctly
|
|
30922
|
-
const shouldSkipInit = previewMode || (demoMode && !userSentRealMessage);
|
|
31294
|
+
// Always call useChat hook (React rules), but skip initialization in preview mode
|
|
31295
|
+
const shouldSkipInit = previewMode;
|
|
30923
31296
|
const chatHook = useChat({
|
|
30924
31297
|
widgetId: previewMode ? '__preview__' : (widgetId || '__preview__'),
|
|
30925
31298
|
apiUrl,
|
|
30926
31299
|
currentRoute,
|
|
30927
31300
|
onMessage: shouldSkipInit ? undefined : onMessage,
|
|
30928
31301
|
onError: shouldSkipInit ? undefined : onError,
|
|
30929
|
-
skipInitialization: shouldSkipInit,
|
|
31302
|
+
skipInitialization: shouldSkipInit,
|
|
30930
31303
|
});
|
|
30931
31304
|
// Extract values from hook or use preview defaults
|
|
30932
|
-
const
|
|
30933
|
-
const
|
|
30934
|
-
const
|
|
31305
|
+
const messages = previewMode ? [] : chatHook.messages;
|
|
31306
|
+
const isLoading = previewMode ? false : chatHook.isLoading;
|
|
31307
|
+
const isTyping = previewMode ? false : chatHook.isTyping;
|
|
30935
31308
|
const config = previewMode ? mergedPreviewConfig : chatHook.config;
|
|
30936
|
-
const
|
|
31309
|
+
const sendMessage = previewMode ? (() => Promise.resolve()) : chatHook.sendMessage;
|
|
30937
31310
|
const submitFeedback = previewMode ? (() => Promise.resolve()) : chatHook.submitFeedback;
|
|
30938
31311
|
const dismissAction = previewMode ? (() => Promise.resolve()) : chatHook.dismissAction;
|
|
30939
31312
|
const conversations = previewMode ? [] : chatHook.conversations;
|
|
@@ -30941,54 +31314,26 @@ const ChatWidget = ({ widgetId, apiUrl = window.location.origin, previewMode = f
|
|
|
30941
31314
|
const switchConversation = previewMode ? (() => Promise.resolve()) : chatHook.switchConversation;
|
|
30942
31315
|
const startNewConversation = previewMode ? (() => { }) : chatHook.startNewConversation;
|
|
30943
31316
|
const deleteConversation = previewMode ? (() => { }) : chatHook.deleteConversation;
|
|
30944
|
-
const createDemoConversation = previewMode ? (() => Promise.resolve(null)) : chatHook.createDemoConversation;
|
|
30945
31317
|
const conversationId = previewMode ? '' : chatHook.conversationId;
|
|
30946
|
-
|
|
30947
|
-
|
|
30948
|
-
|
|
30949
|
-
|
|
30950
|
-
|
|
30951
|
-
|
|
30952
|
-
|
|
30953
|
-
|
|
30954
|
-
|
|
30955
|
-
|
|
30956
|
-
|
|
30957
|
-
|
|
30958
|
-
|
|
30959
|
-
|
|
30960
|
-
|
|
30961
|
-
|
|
30962
|
-
|
|
30963
|
-
|
|
30964
|
-
|
|
30965
|
-
return hookSendMessage(content);
|
|
30966
|
-
}, [demoMode, isDemoActive, isDemoComplete, userSentRealMessage, hookSendMessage]);
|
|
30967
|
-
// Auto-detect theme from background
|
|
30968
|
-
useEffect(() => {
|
|
30969
|
-
if (!containerRef.current)
|
|
30970
|
-
return;
|
|
30971
|
-
// Initial detection
|
|
30972
|
-
const detected = detectTheme(containerRef.current);
|
|
30973
|
-
setAutoDetectedTheme(detected);
|
|
30974
|
-
// Watch for theme changes on the page
|
|
30975
|
-
const observer = createThemeObserver(containerRef.current, (newTheme) => {
|
|
30976
|
-
setAutoDetectedTheme(newTheme);
|
|
30977
|
-
});
|
|
30978
|
-
// Also listen for system preference changes
|
|
30979
|
-
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
|
30980
|
-
const handleMediaChange = () => {
|
|
30981
|
-
if (containerRef.current) {
|
|
30982
|
-
const detected = detectTheme(containerRef.current);
|
|
30983
|
-
setAutoDetectedTheme(detected);
|
|
30984
|
-
}
|
|
30985
|
-
};
|
|
30986
|
-
mediaQuery.addEventListener('change', handleMediaChange);
|
|
30987
|
-
return () => {
|
|
30988
|
-
observer.disconnect();
|
|
30989
|
-
mediaQuery.removeEventListener('change', handleMediaChange);
|
|
30990
|
-
};
|
|
30991
|
-
}, [config]);
|
|
31318
|
+
const callActionEndpoint = previewMode ? (async () => { throw new Error('Not available in preview mode'); }) : chatHook.callActionEndpoint;
|
|
31319
|
+
const { effectiveTheme, effectivePosition, accentColor, iconContrastColor, effectiveSize, effectiveHeaderTitle, effectiveWelcomeTitle, effectiveWelcomeMessage, effectivePlaceholder, effectiveWelcomeBubbleText, effectiveTriggerType, effectiveTriggerText, mergedStyles, } = useWidgetAppearance({
|
|
31320
|
+
containerRef,
|
|
31321
|
+
config,
|
|
31322
|
+
theme,
|
|
31323
|
+
primaryColor,
|
|
31324
|
+
position,
|
|
31325
|
+
size,
|
|
31326
|
+
headerTitle,
|
|
31327
|
+
welcomeTitle,
|
|
31328
|
+
welcomeMessage,
|
|
31329
|
+
placeholder,
|
|
31330
|
+
welcomeBubbleText,
|
|
31331
|
+
triggerType,
|
|
31332
|
+
triggerText,
|
|
31333
|
+
customStyles,
|
|
31334
|
+
zIndex,
|
|
31335
|
+
previewMode,
|
|
31336
|
+
});
|
|
30992
31337
|
// Check if device is mobile
|
|
30993
31338
|
const isMobile = typeof window !== 'undefined' && window.innerWidth <= 480;
|
|
30994
31339
|
// Handle auto-open (only for bubble mode, disabled on mobile)
|
|
@@ -30996,9 +31341,6 @@ const ChatWidget = ({ widgetId, apiUrl = window.location.origin, previewMode = f
|
|
|
30996
31341
|
// Never auto-open on mobile devices
|
|
30997
31342
|
if (isMobile)
|
|
30998
31343
|
return undefined;
|
|
30999
|
-
// Don't auto-open if demo mode is active - let demo animation control opening
|
|
31000
|
-
if (demoMode && !isDemoComplete)
|
|
31001
|
-
return undefined;
|
|
31002
31344
|
if (!isEmbedded && config?.settings.autoOpen) {
|
|
31003
31345
|
const delay = config.settings.autoOpenDelay || 0;
|
|
31004
31346
|
const timer = setTimeout(() => {
|
|
@@ -31008,7 +31350,7 @@ const ChatWidget = ({ widgetId, apiUrl = window.location.origin, previewMode = f
|
|
|
31008
31350
|
return () => clearTimeout(timer);
|
|
31009
31351
|
}
|
|
31010
31352
|
return undefined;
|
|
31011
|
-
}, [config, onOpen, isEmbedded, isMobile
|
|
31353
|
+
}, [config, onOpen, isEmbedded, isMobile]);
|
|
31012
31354
|
// Handle close on Escape key (only for bubble mode)
|
|
31013
31355
|
useEffect(() => {
|
|
31014
31356
|
if (!isOpen || isEmbedded)
|
|
@@ -31085,172 +31427,12 @@ const ChatWidget = ({ widgetId, apiUrl = window.location.origin, previewMode = f
|
|
|
31085
31427
|
setShowWelcomeBubble(true);
|
|
31086
31428
|
}
|
|
31087
31429
|
}, [widgetId, welcomeBubbleText, config, isOpen, isEmbedded, previewMode]);
|
|
31088
|
-
|
|
31089
|
-
|
|
31090
|
-
|
|
31091
|
-
|
|
31092
|
-
|
|
31093
|
-
|
|
31094
|
-
const effectiveTriggerTypeForDemo = triggerType ?? config?.appearance?.triggerType ?? 'button';
|
|
31095
|
-
const isInputBarTrigger = effectiveTriggerTypeForDemo === 'input-bar';
|
|
31096
|
-
// Debug logging
|
|
31097
|
-
console.log('[Widget] Demo effect check:', {
|
|
31098
|
-
demoMode,
|
|
31099
|
-
isDemoComplete,
|
|
31100
|
-
demoStartedRef: demoStartedRef.current,
|
|
31101
|
-
hasDemoInput: !!demoInput,
|
|
31102
|
-
hasDemoOutput: !!demoOutput,
|
|
31103
|
-
triggerType: effectiveTriggerTypeForDemo,
|
|
31104
|
-
isInputBar: isInputBarTrigger,
|
|
31105
|
-
isOpen,
|
|
31106
|
-
});
|
|
31107
|
-
// Start demo when demoMode is enabled and we have input/output
|
|
31108
|
-
const shouldStartDemo = demoMode && !isDemoComplete && !demoStartedRef.current && demoInput && demoOutput;
|
|
31109
|
-
if (!shouldStartDemo) {
|
|
31110
|
-
console.log('[Widget] Demo not starting:', { shouldStartDemo, reason: !demoMode ? 'demoMode false' : !demoInput ? 'no demoInput' : !demoOutput ? 'no demoOutput' : isDemoComplete ? 'already complete' : demoStartedRef.current ? 'already started' : 'unknown' });
|
|
31111
|
-
return;
|
|
31112
|
-
}
|
|
31113
|
-
// Wait for config to load before starting demo (unless triggerType prop is explicitly provided)
|
|
31114
|
-
// This prevents the demo from starting with wrong trigger type
|
|
31115
|
-
if (!triggerType && !config) {
|
|
31116
|
-
console.log('[Widget] Demo waiting for config or triggerType prop');
|
|
31117
|
-
return;
|
|
31118
|
-
}
|
|
31119
|
-
// For input-bar: start immediately (widget closed, type into bar)
|
|
31120
|
-
// For others: wait until widget is open
|
|
31121
|
-
if (!isInputBarTrigger && !isOpen) {
|
|
31122
|
-
console.log('[Widget] Demo waiting for widget to open (non-input-bar trigger)');
|
|
31123
|
-
return;
|
|
31124
|
-
}
|
|
31125
|
-
console.log('[Widget] Starting demo animation...', {
|
|
31126
|
-
triggerType: effectiveTriggerTypeForDemo,
|
|
31127
|
-
isInputBar: isInputBarTrigger,
|
|
31128
|
-
demoInput,
|
|
31129
|
-
demoOutput: demoOutput?.substring(0, 50) + '...',
|
|
31130
|
-
});
|
|
31131
|
-
demoStartedRef.current = true;
|
|
31132
|
-
setIsDemoActive(true);
|
|
31133
|
-
// Use stable IDs to prevent React from remounting components
|
|
31134
|
-
const userMessageId = 'demo-user-message';
|
|
31135
|
-
const assistantMessageId = 'demo-assistant-message';
|
|
31136
|
-
// Helper to create a message with stable ID
|
|
31137
|
-
const createMessage = (id, role, content, isStreaming = false) => ({
|
|
31138
|
-
id,
|
|
31139
|
-
message: { role, content },
|
|
31140
|
-
timestamp: new Date().toISOString(),
|
|
31141
|
-
sources: [],
|
|
31142
|
-
isStreaming,
|
|
31143
|
-
});
|
|
31144
|
-
// Animation sequence - runs to completion even if widget is closed
|
|
31145
|
-
// This ensures conversation is always created and saved consistently
|
|
31146
|
-
const runDemoAnimation = async () => {
|
|
31147
|
-
// STEP 1: Create conversation IMMEDIATELY at the start
|
|
31148
|
-
// This ensures we have a conversation ID before any animation
|
|
31149
|
-
console.log('[Widget] Creating demo conversation at start...');
|
|
31150
|
-
let conversationId = null;
|
|
31151
|
-
try {
|
|
31152
|
-
conversationId = await createDemoConversation(demoInput, demoOutput);
|
|
31153
|
-
if (conversationId) {
|
|
31154
|
-
console.log('[Widget] Demo conversation created:', conversationId);
|
|
31155
|
-
}
|
|
31156
|
-
}
|
|
31157
|
-
catch (err) {
|
|
31158
|
-
console.warn('[Widget] Failed to create demo conversation:', err);
|
|
31159
|
-
}
|
|
31160
|
-
// STEP 2: For input-bar trigger, animate typing into input bar first
|
|
31161
|
-
if (isInputBarTrigger && !isOpen) {
|
|
31162
|
-
setIsDemoAnimatingInput(true);
|
|
31163
|
-
// Wait before starting to type - ensure input bar is fully visible
|
|
31164
|
-
await new Promise(resolve => setTimeout(resolve, 1500));
|
|
31165
|
-
// Type into input bar character by character
|
|
31166
|
-
const inputChars = demoInput.split('');
|
|
31167
|
-
for (let i = 0; i < inputChars.length; i++) {
|
|
31168
|
-
setDemoInputBarText(demoInput.slice(0, i + 1));
|
|
31169
|
-
// Slower typing speed for better visibility
|
|
31170
|
-
const delay = inputChars[i] === ' ' ? 100 : 60 + Math.random() * 40;
|
|
31171
|
-
await new Promise(resolve => setTimeout(resolve, delay));
|
|
31172
|
-
}
|
|
31173
|
-
// Longer pause after typing so user can read the question
|
|
31174
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
31175
|
-
setIsDemoAnimatingInput(false);
|
|
31176
|
-
setDemoInputBarText('');
|
|
31177
|
-
setIsOpen(true);
|
|
31178
|
-
onOpen?.();
|
|
31179
|
-
// Let widget open animation complete
|
|
31180
|
-
await new Promise(resolve => setTimeout(resolve, 500));
|
|
31181
|
-
}
|
|
31182
|
-
else {
|
|
31183
|
-
// For non-input-bar triggers, just wait for widget to settle
|
|
31184
|
-
await new Promise(resolve => setTimeout(resolve, 800));
|
|
31185
|
-
}
|
|
31186
|
-
// STEP 3: Add user message
|
|
31187
|
-
setDemoMessages([createMessage(userMessageId, 'user', demoInput)]);
|
|
31188
|
-
// Show typing indicator after a pause
|
|
31189
|
-
await new Promise(resolve => setTimeout(resolve, 500));
|
|
31190
|
-
setIsDemoTyping(true);
|
|
31191
|
-
// "Thinking" pause
|
|
31192
|
-
await new Promise(resolve => setTimeout(resolve, 1200));
|
|
31193
|
-
setIsDemoTyping(false);
|
|
31194
|
-
// STEP 4: Stream the response character by character
|
|
31195
|
-
const chars = demoOutput.split('');
|
|
31196
|
-
let currentText = '';
|
|
31197
|
-
// Initial message with empty content
|
|
31198
|
-
setDemoMessages([
|
|
31199
|
-
createMessage(userMessageId, 'user', demoInput),
|
|
31200
|
-
createMessage(assistantMessageId, 'assistant', '', true),
|
|
31201
|
-
]);
|
|
31202
|
-
await new Promise(resolve => setTimeout(resolve, 100));
|
|
31203
|
-
// Stream characters - continues even if widget is closed
|
|
31204
|
-
for (let i = 0; i < chars.length; i++) {
|
|
31205
|
-
currentText += chars[i];
|
|
31206
|
-
setDemoMessages([
|
|
31207
|
-
createMessage(userMessageId, 'user', demoInput),
|
|
31208
|
-
createMessage(assistantMessageId, 'assistant', currentText, i < chars.length - 1),
|
|
31209
|
-
]);
|
|
31210
|
-
const delay = chars[i] === ' ' ? 12 : 20;
|
|
31211
|
-
await new Promise(resolve => setTimeout(resolve, delay));
|
|
31212
|
-
}
|
|
31213
|
-
// STEP 5: Mark demo complete
|
|
31214
|
-
await new Promise(resolve => setTimeout(resolve, 500));
|
|
31215
|
-
setIsDemoComplete(true);
|
|
31216
|
-
setIsDemoActive(false);
|
|
31217
|
-
console.log('[Widget] Demo animation complete');
|
|
31218
|
-
onDemoComplete?.();
|
|
31219
|
-
};
|
|
31220
|
-
runDemoAnimation();
|
|
31221
|
-
}, [demoMode, isOpen, isDemoComplete, demoInput, demoOutput, onDemoComplete, triggerType, config?.appearance?.triggerType, onOpen, createDemoConversation]);
|
|
31222
|
-
// Determine theme - use prop override if provided, otherwise auto-detect
|
|
31223
|
-
const appearanceConfig = config?.appearance;
|
|
31224
|
-
const effectiveTheme = theme ?? autoDetectedTheme;
|
|
31225
|
-
// Determine position (prop override takes priority for live preview)
|
|
31226
|
-
const effectivePosition = position || config?.appearance.position || 'bottom-right';
|
|
31227
|
-
// Get accent color from prop or config (empty string means no accent color / vanilla mode)
|
|
31228
|
-
const accentColor = primaryColor ?? appearanceConfig?.primaryColor ?? '';
|
|
31229
|
-
// Apply prop overrides for live preview (props take priority over config)
|
|
31230
|
-
const effectiveSize = size || appearanceConfig?.size || 'small';
|
|
31231
|
-
const effectiveHeaderTitle = headerTitle ?? appearanceConfig?.headerTitle ?? '';
|
|
31232
|
-
const effectiveWelcomeTitle = welcomeTitle ?? appearanceConfig?.welcomeTitle ?? '';
|
|
31233
|
-
const effectiveWelcomeMessage = welcomeMessage ?? appearanceConfig?.welcomeMessage ?? '';
|
|
31234
|
-
const effectivePlaceholder = placeholder ?? appearanceConfig?.placeholder ?? '';
|
|
31235
|
-
const effectiveWelcomeBubbleText = welcomeBubbleText ?? appearanceConfig?.welcomeBubbleText ?? '';
|
|
31236
|
-
const effectiveTriggerType = triggerType ?? appearanceConfig?.triggerType ?? 'button';
|
|
31237
|
-
const effectiveTriggerText = triggerText ?? appearanceConfig?.triggerText ?? 'Chat';
|
|
31238
|
-
// Generate styles using simplified theme system
|
|
31239
|
-
const simpleAppearance = {
|
|
31240
|
-
accentColor};
|
|
31241
|
-
// Generate theme styles from accent color
|
|
31242
|
-
const generatedStyles = generateThemeStyles(simpleAppearance, effectiveTheme);
|
|
31243
|
-
// Also apply legacy styles for backward compatibility
|
|
31244
|
-
const legacyStyles = appearanceConfig
|
|
31245
|
-
? applyAppearanceStyles(appearanceConfig)
|
|
31246
|
-
: {};
|
|
31247
|
-
// Merge styles (generated takes priority for new simplified system, then custom overrides)
|
|
31248
|
-
const mergedStyles = {
|
|
31249
|
-
...legacyStyles,
|
|
31250
|
-
...generatedStyles,
|
|
31251
|
-
...customStyles,
|
|
31252
|
-
...(zIndex !== undefined ? { '--widget-z-index': String(zIndex) } : {}),
|
|
31253
|
-
};
|
|
31430
|
+
const handleStartNewConversation = useCallback(() => {
|
|
31431
|
+
startNewConversation();
|
|
31432
|
+
}, [startNewConversation]);
|
|
31433
|
+
const handleSendMessage = useCallback((content) => {
|
|
31434
|
+
sendMessage(content);
|
|
31435
|
+
}, [sendMessage]);
|
|
31254
31436
|
// Dismiss bubble and store based on frequency setting
|
|
31255
31437
|
const dismissBubble = () => {
|
|
31256
31438
|
setShowWelcomeBubble(false);
|
|
@@ -31286,9 +31468,6 @@ const ChatWidget = ({ widgetId, apiUrl = window.location.origin, previewMode = f
|
|
|
31286
31468
|
onOpen?.();
|
|
31287
31469
|
}
|
|
31288
31470
|
else {
|
|
31289
|
-
// Demo animation continues in background when widget is closed
|
|
31290
|
-
// The conversation was already created at the start, so closing is safe
|
|
31291
|
-
// When user reopens, they'll see the current state of the demo animation
|
|
31292
31471
|
onClose?.();
|
|
31293
31472
|
}
|
|
31294
31473
|
};
|
|
@@ -31325,10 +31504,13 @@ const ChatWidget = ({ widgetId, apiUrl = window.location.origin, previewMode = f
|
|
|
31325
31504
|
sendMessage(actionInstruction);
|
|
31326
31505
|
};
|
|
31327
31506
|
// Don't render until config is loaded to avoid flash of unstyled content
|
|
31328
|
-
//
|
|
31329
|
-
//
|
|
31507
|
+
// Exceptions that allow immediate rendering:
|
|
31508
|
+
// 1. triggerType prop provided - user wants specific trigger shown immediately
|
|
31509
|
+
// 2. primaryColor prop provided - we have initial styling, no flash will occur
|
|
31510
|
+
// This improves perceived loading speed - users see the correctly styled trigger while config loads
|
|
31330
31511
|
// In preview mode, config is always available
|
|
31331
|
-
const
|
|
31512
|
+
const hasInitialStyling = !!primaryColor;
|
|
31513
|
+
const canRenderWithoutConfig = !!triggerType || hasInitialStyling;
|
|
31332
31514
|
if (!config && !previewMode && !canRenderWithoutConfig) {
|
|
31333
31515
|
return null;
|
|
31334
31516
|
}
|
|
@@ -31336,21 +31518,21 @@ const ChatWidget = ({ widgetId, apiUrl = window.location.origin, previewMode = f
|
|
|
31336
31518
|
const IconComponent = isOpen ? iconComponents.FiChevronDown : iconComponents.FiMessageCircle;
|
|
31337
31519
|
// Embedded mode renders directly without wrapper positioning
|
|
31338
31520
|
if (isEmbedded) {
|
|
31339
|
-
return (jsx("div", { ref: containerRef, className: `ai-chat-widget ai-chat-widget-embedded ${effectiveTheme}`, style: { ...mergedStyles, width: '100%', height: '100%' }, children: jsx(ChatWindow, { messages: messages, isLoading: isLoading, isTyping: isTyping, config: config, onSendMessage:
|
|
31521
|
+
return (jsx("div", { ref: containerRef, className: `ai-chat-widget ai-chat-widget-embedded ${effectiveTheme}`, style: { ...mergedStyles, width: '100%', height: '100%' }, children: jsx(ChatWindow, { messages: messages, isLoading: isLoading, isTyping: isTyping, config: config, onSendMessage: handleSendMessage, onClose: () => { }, onFeedback: handleFeedback, onActionClick: handleActionClick, onActionDismiss: dismissAction, onCallEndpoint: callActionEndpoint, conversations: conversations, onLoadConversations: loadConversations, onSwitchConversation: switchConversation, onStartNewConversation: handleStartNewConversation, onDeleteConversation: deleteConversation, currentConversationId: conversationId, sizeOverride: effectiveSize, headerTitleOverride: effectiveHeaderTitle, welcomeTitleOverride: effectiveWelcomeTitle, welcomeMessageOverride: effectiveWelcomeMessage, placeholderOverride: effectivePlaceholder, suggestedQuestionsOverride: suggestedQuestions }) }));
|
|
31340
31522
|
}
|
|
31341
31523
|
// Determine trigger class for container
|
|
31342
31524
|
const triggerClass = effectiveTriggerType === 'pill-text'
|
|
31343
31525
|
? 'trigger-pill-text'
|
|
31344
31526
|
: effectiveTriggerType === 'input-bar'
|
|
31345
31527
|
? 'trigger-input-bar'
|
|
31346
|
-
: '';
|
|
31528
|
+
: 'trigger-button';
|
|
31347
31529
|
// Size class for CSS targeting (used by input-bar trigger for width matching)
|
|
31348
31530
|
const sizeClass = `size-${effectiveSize}`;
|
|
31349
|
-
|
|
31531
|
+
// Collapsed class for input bar
|
|
31532
|
+
const collapsedClass = isInputBarCollapsed ? 'is-collapsed' : '';
|
|
31533
|
+
return (jsx("div", { ref: containerRef, className: `ai-chat-widget ${effectiveTheme}`, style: mergedStyles, children: jsxs("div", { ref: widgetRef, className: `ai-chat-widget-container ${effectivePosition} ${isOpen ? 'is-open' : ''} ${containerMode ? 'container-mode' : ''} ${mobileMode ? 'mobile-mode' : ''} ${desktopMode ? 'desktop-mode' : ''} ${triggerClass} ${sizeClass} ${collapsedClass}`, children: [isOpen && (jsx(ChatWindow, { messages: messages, isLoading: isLoading, isTyping: isTyping, config: config, onSendMessage: handleSendMessage, onClose: handleToggle, onFeedback: handleFeedback, onActionClick: handleActionClick, onActionDismiss: dismissAction, onCallEndpoint: callActionEndpoint,
|
|
31350
31534
|
// Chat history props (only active when persistConversation is true)
|
|
31351
|
-
conversations: conversations, onLoadConversations: loadConversations, onSwitchConversation: switchConversation, onStartNewConversation:
|
|
31352
|
-
// Override props for live preview
|
|
31353
|
-
headerTitleOverride: effectiveHeaderTitle, welcomeTitleOverride: effectiveWelcomeTitle, welcomeMessageOverride: effectiveWelcomeMessage, placeholderOverride: effectivePlaceholder, suggestedQuestionsOverride: suggestedQuestions })), effectiveTriggerType === 'button' && !isOpen && effectiveWelcomeBubbleText && (previewMode || showWelcomeBubble) && (jsxs("div", { className: "ai-chat-welcome-bubble", onClick: handleToggle, children: [jsx("span", { children: effectiveWelcomeBubbleText }), jsx("button", { className: "ai-chat-welcome-bubble-close", onClick: handleDismissBubble, "aria-label": "Dismiss", children: jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }), jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })] }) }), jsx("div", { className: "ai-chat-welcome-bubble-arrow" })] })), effectiveTriggerType === 'button' && (jsx("button", { className: `ai-chat-button ${isOpen ? 'is-open' : ''}`, onClick: handleToggle, "aria-label": isOpen ? "Minimize chat" : "Open chat", children: jsx("div", { className: "ai-chat-button-svg", children: jsx(IconComponent, {}) }) })), effectiveTriggerType === 'pill-text' && (jsxs("button", { className: `ai-chat-trigger-pill ${isOpen ? 'is-open' : ''}`, onClick: handleToggle, "aria-label": isOpen ? "Close chat" : "Open chat", children: [jsx("div", { className: "ai-chat-trigger-pill-icon", children: jsx(IconComponent, {}) }), !isOpen && jsx("span", { children: effectiveTriggerText })] })), effectiveTriggerType === 'input-bar' && !isOpen && (jsxs("div", { className: "ai-chat-trigger-input-container", children: [jsx("button", { type: "button", className: "ai-chat-trigger-input-expand", onClick: handleToggle, "aria-label": "Open chat", children: jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: jsx("polyline", { points: "18 15 12 9 6 15" }) }) }), jsxs("form", { className: "ai-chat-trigger-input-wrapper", onSubmit: handleInputBarSubmit, children: [jsx("input", { type: "text", className: "ai-chat-trigger-input", placeholder: effectivePlaceholder || "Ask me anything...", value: isDemoAnimatingInput ? demoInputBarText : inputBarValue, onChange: (e) => !isDemoAnimatingInput && setInputBarValue(e.target.value), readOnly: isDemoAnimatingInput, "aria-label": "Chat input" }), jsx("button", { type: "submit", className: "ai-chat-trigger-input-btn", disabled: isDemoAnimatingInput ? !demoInputBarText.trim() : !inputBarValue.trim(), "aria-label": "Send message", children: jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsx("line", { x1: "22", y1: "2", x2: "11", y2: "13" }), jsx("polygon", { points: "22 2 15 22 11 13 2 9 22 2" })] }) })] })] }))] }) }));
|
|
31535
|
+
conversations: conversations, onLoadConversations: loadConversations, onSwitchConversation: switchConversation, onStartNewConversation: handleStartNewConversation, onDeleteConversation: deleteConversation, currentConversationId: conversationId, sizeOverride: effectiveSize, headerTitleOverride: effectiveHeaderTitle, welcomeTitleOverride: effectiveWelcomeTitle, welcomeMessageOverride: effectiveWelcomeMessage, placeholderOverride: effectivePlaceholder, suggestedQuestionsOverride: suggestedQuestions })), jsx(WidgetTriggers, { triggerType: effectiveTriggerType, isOpen: isOpen, onToggle: handleToggle, triggerText: effectiveTriggerText, placeholder: effectivePlaceholder, IconComponent: IconComponent, showWelcomeBubble: showWelcomeBubble, welcomeBubbleText: effectiveWelcomeBubbleText, previewMode: previewMode, onDismissBubble: handleDismissBubble, isInputBarCollapsed: isInputBarCollapsed, setIsInputBarCollapsed: setIsInputBarCollapsed, inputBarValue: inputBarValue, setInputBarValue: setInputBarValue, onSubmitInputBar: handleInputBarSubmit, accentColor: accentColor, iconColor: iconContrastColor })] }) }));
|
|
31354
31536
|
};
|
|
31355
31537
|
|
|
31356
31538
|
export { ApiError, ChatWidget, DataPolicyView, useChat };
|