@courseecho/ai-core-sdk 1.0.27 → 1.0.29

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.
@@ -2,7 +2,7 @@
2
2
  * AI Widget SDK - Core Communication Service
3
3
  * Handle HTTP requests to backend API
4
4
  */
5
- import { AiQueryRequest, AiQueryResponse, QuotaInfo } from './models';
5
+ import { AiQueryRequest, AiQueryResponse, QuotaInfo, ChatHistoryMessage } from './models';
6
6
  export declare class AiCommunicationService {
7
7
  private apiEndpoint;
8
8
  private apiKey?;
@@ -16,14 +16,58 @@ export declare class AiCommunicationService {
16
16
  private lastRequestAt;
17
17
  /** Minimum ms between requests (default 1 500 — human typing speed floor) */
18
18
  private minRequestIntervalMs;
19
+ /**
20
+ * Browser-generated UUID persisted in sessionStorage.
21
+ * Clears when the browser tab closes — so anonymous users get session-only history.
22
+ * Logged-in users are identified by `userId` instead.
23
+ */
24
+ private readonly anonymousId;
25
+ /**
26
+ * Customer's end-user ID. Set via `setUser()` when the user logs into the
27
+ * customer's application. When set, chat history is stored in the DB and
28
+ * persists across sessions.
29
+ */
30
+ private userId;
19
31
  constructor(apiEndpoint: string, apiKey?: string, minRequestIntervalMs?: number);
20
32
  /** Override the minimum request interval at runtime. */
21
33
  setMinRequestInterval(ms: number): void;
34
+ /** Returns the anonymous session UUID (sessionStorage — resets on browser close). */
35
+ getAnonymousId(): string;
36
+ /** Returns the current end-user ID, or null if not logged in. */
37
+ getUserId(): string | null;
38
+ /**
39
+ * Identify the logged-in user.
40
+ * - Sets the userId so every subsequent query is tagged with it in the DB.
41
+ * - Does NOT automatically sync anonymous history — call `syncSession()` for that.
42
+ */
43
+ setUser(userId: string): void;
44
+ /**
45
+ * Migrate anonymous session messages to the logged-in user.
46
+ * Call this immediately after the user logs in (before or after setUser).
47
+ * Updates all Conversation rows that share the anonymousId to carry the userId.
48
+ */
49
+ syncSession(userId: string): Promise<void>;
50
+ /**
51
+ * Fetch chat history from the DB.
52
+ *
53
+ * - Logged-in users (userId set) → queries by userId → full persistent history
54
+ * - Anonymous users (no userId) → queries by anonymousId → session history only
55
+ *
56
+ * Returns messages in chronological order (oldest first).
57
+ * Returns an empty array on error rather than throwing.
58
+ */
59
+ loadHistory(limit?: number): Promise<ChatHistoryMessage[]>;
22
60
  /**
23
61
  * Generate a random session identifier (UUID v4-like) that persists for the
24
62
  * lifetime of the communication-service instance (= one widget mount).
25
63
  */
26
64
  private static generateSessionId;
65
+ /**
66
+ * Retrieve (or create) a stable anonymous browser session ID from sessionStorage.
67
+ * Stored in sessionStorage so it resets when the browser tab closes — giving
68
+ * anonymous users session-scoped history only (not persistent like logged-in users).
69
+ */
70
+ private static getOrCreateAnonymousId;
27
71
  /**
28
72
  * Build a stable, non-PII browser fingerprint string.
29
73
  * This is NOT a tracking fingerprint — it only helps the backend distinguish
@@ -14,11 +14,44 @@ async function safeJson(res) {
14
14
  return null;
15
15
  }
16
16
  }
17
+ // ─── Page-text capture helper ─────────────────────────────────────────────────
18
+ /**
19
+ * Extracts visible rendered text from the current page, deduplicated and capped
20
+ * at `maxChars` characters. Covers SPA content (course cards, mentor profiles,
21
+ * employee tables, etc.) that server-side crawlers cannot see.
22
+ *
23
+ * Priority: <main> → [role="main"] → <article> → document.body
24
+ * Safe to call in SSR/Node environments — returns '' when `document` is absent.
25
+ */
26
+ function capturePageText(maxChars = 8000) {
27
+ try {
28
+ if (typeof document === 'undefined')
29
+ return '';
30
+ const candidates = [
31
+ document.querySelector('main'),
32
+ document.querySelector('[role="main"]'),
33
+ document.querySelector('article'),
34
+ document.body,
35
+ ];
36
+ const el = candidates.find((c) => c && (c.innerText?.trim().length ?? 0) > 50) ?? document.body;
37
+ const raw = (el?.innerText ?? '').replace(/\s{3,}/g, '\n\n').trim();
38
+ return raw.length > maxChars ? raw.slice(0, maxChars) : raw;
39
+ }
40
+ catch {
41
+ return '';
42
+ }
43
+ }
17
44
  export class AiCommunicationService {
18
45
  constructor(apiEndpoint, apiKey, minRequestIntervalMs = 1500) {
19
46
  this.lastQuotaInfo = null;
20
47
  /** Timestamp of the last outgoing AI query (ms) */
21
48
  this.lastRequestAt = 0;
49
+ /**
50
+ * Customer's end-user ID. Set via `setUser()` when the user logs into the
51
+ * customer's application. When set, chat history is stored in the DB and
52
+ * persists across sessions.
53
+ */
54
+ this.userId = null;
22
55
  if (!apiEndpoint) {
23
56
  throw new Error('apiEndpoint is required');
24
57
  }
@@ -27,11 +60,77 @@ export class AiCommunicationService {
27
60
  this.minRequestIntervalMs = minRequestIntervalMs;
28
61
  this.sessionId = AiCommunicationService.generateSessionId();
29
62
  this.clientFingerprint = AiCommunicationService.generateFingerprint();
63
+ this.anonymousId = AiCommunicationService.getOrCreateAnonymousId();
30
64
  }
31
65
  /** Override the minimum request interval at runtime. */
32
66
  setMinRequestInterval(ms) {
33
67
  this.minRequestIntervalMs = ms;
34
68
  }
69
+ // ── User identity + chat history ─────────────────────────────────────
70
+ /** Returns the anonymous session UUID (sessionStorage — resets on browser close). */
71
+ getAnonymousId() { return this.anonymousId; }
72
+ /** Returns the current end-user ID, or null if not logged in. */
73
+ getUserId() { return this.userId; }
74
+ /**
75
+ * Identify the logged-in user.
76
+ * - Sets the userId so every subsequent query is tagged with it in the DB.
77
+ * - Does NOT automatically sync anonymous history — call `syncSession()` for that.
78
+ */
79
+ setUser(userId) {
80
+ this.userId = userId || null;
81
+ }
82
+ /**
83
+ * Migrate anonymous session messages to the logged-in user.
84
+ * Call this immediately after the user logs in (before or after setUser).
85
+ * Updates all Conversation rows that share the anonymousId to carry the userId.
86
+ */
87
+ async syncSession(userId) {
88
+ try {
89
+ this.userId = userId;
90
+ const headers = { 'Content-Type': 'application/json' };
91
+ if (this.jwtToken)
92
+ headers['Authorization'] = `Bearer ${this.jwtToken}`;
93
+ else if (this.apiKey)
94
+ headers['X-API-Key'] = this.apiKey;
95
+ await fetch(`${this.apiEndpoint}/api/chat/sync`, {
96
+ method: 'POST',
97
+ headers,
98
+ body: JSON.stringify({ anonymousId: this.anonymousId, userId }),
99
+ });
100
+ }
101
+ catch { /* non-fatal — history sync is best-effort */ }
102
+ }
103
+ /**
104
+ * Fetch chat history from the DB.
105
+ *
106
+ * - Logged-in users (userId set) → queries by userId → full persistent history
107
+ * - Anonymous users (no userId) → queries by anonymousId → session history only
108
+ *
109
+ * Returns messages in chronological order (oldest first).
110
+ * Returns an empty array on error rather than throwing.
111
+ */
112
+ async loadHistory(limit = 30) {
113
+ try {
114
+ const params = new URLSearchParams({ limit: String(limit) });
115
+ if (this.userId)
116
+ params.set('userId', this.userId);
117
+ else
118
+ params.set('anonymousId', this.anonymousId);
119
+ const headers = {};
120
+ if (this.jwtToken)
121
+ headers['Authorization'] = `Bearer ${this.jwtToken}`;
122
+ else if (this.apiKey)
123
+ headers['X-API-Key'] = this.apiKey;
124
+ const res = await fetch(`${this.apiEndpoint}/api/chat/history?${params}`, { headers });
125
+ if (!res.ok)
126
+ return [];
127
+ const data = await res.json();
128
+ return (data.messages ?? []);
129
+ }
130
+ catch {
131
+ return [];
132
+ }
133
+ }
35
134
  // ── Session / fingerprint helpers ─────────────────────────────────────
36
135
  /**
37
136
  * Generate a random session identifier (UUID v4-like) that persists for the
@@ -47,6 +146,26 @@ export class AiCommunicationService {
47
146
  return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16);
48
147
  });
49
148
  }
149
+ /**
150
+ * Retrieve (or create) a stable anonymous browser session ID from sessionStorage.
151
+ * Stored in sessionStorage so it resets when the browser tab closes — giving
152
+ * anonymous users session-scoped history only (not persistent like logged-in users).
153
+ */
154
+ static getOrCreateAnonymousId() {
155
+ const KEY = 'ce_anon_id';
156
+ try {
157
+ let id = typeof sessionStorage !== 'undefined' ? sessionStorage.getItem(KEY) : null;
158
+ if (!id) {
159
+ id = AiCommunicationService.generateSessionId();
160
+ if (typeof sessionStorage !== 'undefined')
161
+ sessionStorage.setItem(KEY, id);
162
+ }
163
+ return id;
164
+ }
165
+ catch {
166
+ return AiCommunicationService.generateSessionId();
167
+ }
168
+ }
50
169
  /**
51
170
  * Build a stable, non-PII browser fingerprint string.
52
171
  * This is NOT a tracking fingerprint — it only helps the backend distinguish
@@ -123,11 +242,22 @@ export class AiCommunicationService {
123
242
  else if (this.apiKey) {
124
243
  headers['X-API-Key'] = this.apiKey;
125
244
  }
245
+ // Capture live rendered page text so the AI can answer from SPA content
246
+ // (mentor cards, course listings, employee tables, etc.) that server-side
247
+ // crawlers cannot see. We prefer targeted semantic regions for cleaner data.
248
+ const enrichedRequest = {
249
+ ...request,
250
+ pageText: request.pageText ?? capturePageText(8000),
251
+ pageUrl: request.pageUrl ?? (typeof window !== 'undefined' ? window.location.href : undefined),
252
+ pageTitle: request.pageTitle ?? (typeof window !== 'undefined' ? document.title : undefined),
253
+ anonymousId: this.anonymousId,
254
+ endUserId: this.userId ?? undefined,
255
+ };
126
256
  try {
127
257
  const response = await fetch(`${this.apiEndpoint}/api/aiorchestrator/query`, {
128
258
  method: 'POST',
129
259
  headers,
130
- body: JSON.stringify(request),
260
+ body: JSON.stringify(enrichedRequest),
131
261
  });
132
262
  if (!response.ok) {
133
263
  const errorData = await safeJson(response);
package/dist/models.d.ts CHANGED
@@ -145,6 +145,13 @@ export interface AiContextConfig {
145
145
  * Default: false
146
146
  */
147
147
  showUsageCounter?: boolean;
148
+ /**
149
+ * Customer's end-user ID — when set, chat history is loaded from the DB
150
+ * and persists across sessions. Leave unset for anonymous (session-only) mode.
151
+ *
152
+ * Example: userId: currentUser.id
153
+ */
154
+ userId?: string;
148
155
  /**
149
156
  * Client-side abuse prevention settings.
150
157
  * These defend against scripted quota-exhaustion attacks on behalf of your
@@ -159,14 +166,69 @@ export interface AiContextConfig {
159
166
  */
160
167
  minRequestIntervalMs?: number;
161
168
  };
169
+ /**
170
+ * Mobile responsiveness configuration.
171
+ * Controls how the widget behaves on mobile devices (< 768px).
172
+ */
173
+ mobileConfig?: {
174
+ /**
175
+ * Enable collapsible mobile view.
176
+ * On small screens, shows a small icon that expands when clicked.
177
+ * Default: true
178
+ */
179
+ enableCollapsible?: boolean;
180
+ /**
181
+ * Width breakpoint (in px) below which mobile view activates.
182
+ * Default: 768
183
+ */
184
+ breakpoint?: number;
185
+ /**
186
+ * Height of the collapsed preview badge (in px).
187
+ * Default: 60
188
+ */
189
+ collapsedHeight?: number;
190
+ /**
191
+ * Width of the collapsed preview badge (in px).
192
+ * Default: 60
193
+ */
194
+ collapsedWidth?: number;
195
+ /**
196
+ * Animation duration for expand/collapse (in ms).
197
+ * Default: 300
198
+ */
199
+ animationDuration?: number;
200
+ /**
201
+ * Show a pulsing indicator on the collapsed badge to attract attention.
202
+ * Default: true
203
+ */
204
+ showPulsingIndicator?: boolean;
205
+ };
162
206
  }
163
207
  export interface AiQueryRequest {
164
208
  userQuery: string;
165
209
  pageType: string;
166
210
  entityId?: string;
167
211
  contextData?: Record<string, any>;
212
+ pageUrl?: string;
213
+ pageTitle?: string;
214
+ /**
215
+ * Rendered page text captured from the user's browser (document.body.innerText).
216
+ * Provides live page content for SPAs where content is injected via API calls
217
+ * and is not available in the server-side crawl.
218
+ */
219
+ pageText?: string;
220
+ /** Browser-generated UUID from sessionStorage — identifies anonymous sessions. */
221
+ anonymousId?: string;
222
+ /** Customer's own end-user ID — set when the user is logged into the customer's app. */
223
+ endUserId?: string;
168
224
  [key: string]: any;
169
225
  }
226
+ /** A single message returned by GET /api/chat/history */
227
+ export interface ChatHistoryMessage {
228
+ role: 'user' | 'assistant';
229
+ content: string;
230
+ createdAt: string;
231
+ }
170
232
  export interface AiQueryResponse {
171
233
  sessionId: string;
172
234
  response: string;
package/dist/sdk.d.ts CHANGED
@@ -4,7 +4,7 @@
4
4
  */
5
5
  import { AiCommunicationService } from './communication-service';
6
6
  import { ChatStateManager, ContextStateManager, ChartManager, SuggestionManager } from './state-manager';
7
- import { AiContextConfig, AiMessage, AiSuggestion } from './models';
7
+ import { AiContextConfig, AiMessage, AiSuggestion, ChatHistoryMessage } from './models';
8
8
  import { PageContentScanner, AiSuggestionGenerator } from './auto-suggestion-generator';
9
9
  export declare class AiWidgetSDK {
10
10
  private communicationService;
@@ -43,8 +43,21 @@ export declare class AiWidgetSDK {
43
43
  /** Get the currently stored JWT token (if any). */
44
44
  getJwtToken(): string | undefined;
45
45
  /**
46
- * Send a query to the AI backend
46
+ * Identify the logged-in user.
47
+ * 1. Persists userId for future queries.
48
+ * 2. Syncs any anonymous session messages to DB under this userId.
47
49
  */
50
+ setUser(userId: string): Promise<void>;
51
+ /**
52
+ * Load chat history from the backend.
53
+ * For logged-in users fetches by userId; for anonymous users by anonymousId.
54
+ * @param limit Maximum number of messages to return (default 30).
55
+ */
56
+ loadHistory(limit?: number): Promise<ChatHistoryMessage[]>;
57
+ /**
58
+ * Get the current anonymous session ID (browser session-scoped UUID).
59
+ */
60
+ getAnonymousId(): string;
48
61
  sendQuery(userQuery: string): Promise<AiMessage>;
49
62
  /**
50
63
  * Update context
package/dist/sdk.js CHANGED
@@ -116,8 +116,28 @@ export class AiWidgetSDK {
116
116
  return this.communicationService.getJwtToken();
117
117
  }
118
118
  /**
119
- * Send a query to the AI backend
119
+ * Identify the logged-in user.
120
+ * 1. Persists userId for future queries.
121
+ * 2. Syncs any anonymous session messages to DB under this userId.
120
122
  */
123
+ async setUser(userId) {
124
+ this.communicationService.setUser(userId);
125
+ await this.communicationService.syncSession(userId);
126
+ }
127
+ /**
128
+ * Load chat history from the backend.
129
+ * For logged-in users fetches by userId; for anonymous users by anonymousId.
130
+ * @param limit Maximum number of messages to return (default 30).
131
+ */
132
+ async loadHistory(limit = 30) {
133
+ return this.communicationService.loadHistory(limit);
134
+ }
135
+ /**
136
+ * Get the current anonymous session ID (browser session-scoped UUID).
137
+ */
138
+ getAnonymousId() {
139
+ return this.communicationService.getAnonymousId();
140
+ }
121
141
  async sendQuery(userQuery) {
122
142
  try {
123
143
  this.chatStateManager.setLoading(true);
@@ -11,4 +11,4 @@
11
11
  * Each package is responsible for loading the Inter font into document <head>
12
12
  * separately (React: ensureFont(), jQuery: injectFontLink()).
13
13
  */
14
- export declare const WIDGET_CSS = "\n/* \u2500\u2500 Shadow-DOM host isolation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n:host {\n all: initial !important;\n display: block !important;\n}\n\n/* Scoped box-model reset \u2014 only touches widget elements, not all of shadow DOM */\n.aiwg-root *, .aiwg-root *::before, .aiwg-root *::after {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n}\n\n/* \u2500\u2500 Container \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.aiwg-root {\n --aiwg-primary: #6366f1;\n --aiwg-primary-dark: #8b5cf6;\n --aiwg-fg-color: #ffffff;\n --aiwg-bg-color: #ffffff;\n --aiwg-chat-bg: #f1f5f9;\n\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n font-size: 14px;\n line-height: 1.5;\n color: #1a1a2e;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n border-radius: 16px;\n background: var(--aiwg-bg-color);\n position: fixed !important;\n bottom: 24px;\n right: 24px;\n width: 380px !important;\n max-width: calc(100vw - 48px) !important;\n z-index: 99999;\n transition: height 0.25s ease;\n box-shadow: 0 24px 64px rgba(0,0,0,0.18);\n}\n\n/* \u2500\u2500 Header \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.aiwg-header {\n background: linear-gradient(135deg, var(--aiwg-primary) 0%, var(--aiwg-primary-dark) 100%);\n padding: 16px 20px;\n display: flex;\n align-items: center;\n gap: 12px;\n flex-shrink: 0;\n position: relative;\n overflow: hidden;\n}\n.aiwg-header::before {\n content: '';\n position: absolute;\n width: 140px; height: 140px;\n background: rgba(255,255,255,0.08);\n border-radius: 50%;\n top: -50px; right: -30px;\n}\n.aiwg-avatar {\n width: 40px; height: 40px;\n background: rgba(255,255,255,0.25);\n border-radius: 50%;\n display: flex; align-items: center; justify-content: center;\n font-size: 18px;\n flex-shrink: 0;\n border: 2px solid rgba(255,255,255,0.4);\n}\n.aiwg-header-info { flex: 1; }\n.aiwg-title { color: #fff; font-weight: 600; font-size: 15px; }\n.aiwg-subtitle {\n color: rgba(255,255,255,0.75);\n font-size: 12px;\n display: flex; align-items: center; gap: 5px;\n}\n.aiwg-online-dot {\n width: 7px; height: 7px;\n background: #4ade80;\n border-radius: 50%;\n display: inline-block;\n animation: aiwg-pulse 2s ease-in-out infinite;\n}\n@keyframes aiwg-pulse {\n 0%, 100% { opacity: 1; transform: scale(1); }\n 50% { opacity: 0.6; transform: scale(0.85); }\n}\n.aiwg-minimize-btn {\n width: 36px; height: 36px;\n background: rgba(255,255,255,0.18);\n border: 1.5px solid rgba(255,255,255,0.3);\n border-radius: 50%;\n color: #fff; cursor: pointer;\n display: flex; align-items: center; justify-content: center;\n font-size: 18px; line-height: 1;\n transition: background 0.15s, transform 0.15s;\n flex-shrink: 0;\n position: relative; z-index: 2;\n}\n.aiwg-minimize-btn * { cursor: pointer !important; pointer-events: none; }\n.aiwg-minimize-btn:hover { background: rgba(255,255,255,0.32); transform: scale(1.08); }\n\n/* \u2500\u2500 Messages area \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.aiwg-messages {\n flex: 1;\n overflow-y: auto;\n padding: 20px 16px 8px;\n scroll-behavior: smooth;\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n.aiwg-messages::-webkit-scrollbar { width: 4px; }\n.aiwg-messages::-webkit-scrollbar-thumb { background: #e2e8f0; border-radius: 99px; }\n\n/* \u2500\u2500 Welcome card \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.aiwg-welcome {\n text-align: center;\n padding: 24px 16px;\n display: flex; flex-direction: column; align-items: center; gap: 8px;\n}\n.aiwg-welcome-icon { font-size: 36px; margin-bottom: 4px; }\n.aiwg-welcome h4 { font-size: 15px; font-weight: 600; color: #1a1a2e; }\n.aiwg-welcome p { font-size: 13px; color: #64748b; }\n\n/* \u2500\u2500 Message bubbles \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.aiwg-msg {\n display: flex;\n gap: 8px;\n align-items: flex-end;\n animation: aiwg-fade-up 0.2s ease both;\n}\n@keyframes aiwg-fade-up {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n}\n.aiwg-msg--user { flex-direction: row-reverse; }\n.aiwg-msg-avatar {\n width: 28px; height: 28px; border-radius: 50%;\n display: flex; align-items: center; justify-content: center;\n font-size: 13px; flex-shrink: 0;\n margin-bottom: 2px;\n}\n.aiwg-msg--bot .aiwg-msg-avatar { background: linear-gradient(135deg, var(--aiwg-primary), var(--aiwg-primary-dark)); color: var(--aiwg-fg-color); }\n.aiwg-msg--user .aiwg-msg-avatar { background: #e2e8f0; color: #64748b; }\n.aiwg-msg-body { max-width: 78%; }\n.aiwg-msg-bubble {\n padding: 10px 14px;\n border-radius: 18px;\n font-size: 14px;\n line-height: 1.55;\n word-break: break-word;\n}\n.aiwg-msg--bot .aiwg-msg-bubble { background: var(--aiwg-chat-bg); color: #1a1a2e; border-bottom-left-radius: 4px; }\n.aiwg-msg--user .aiwg-msg-bubble { background: linear-gradient(135deg, var(--aiwg-primary), var(--aiwg-primary-dark)); color: var(--aiwg-fg-color); border-bottom-right-radius: 4px; }\n.aiwg-msg-time { font-size: 10px; color: #94a3b8; margin-top: 4px; text-align: right; padding: 0 4px; }\n.aiwg-msg--bot .aiwg-msg-time { text-align: left; }\n\n/* \u2500\u2500 Typing indicator \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.aiwg-typing { padding: 10px 14px; }\n.aiwg-typing-dots { display: flex; gap: 4px; align-items: center; }\n.aiwg-typing-dots span {\n width: 7px; height: 7px;\n background: #94a3b8; border-radius: 50%;\n animation: aiwg-bounce 1.2s ease-in-out infinite;\n}\n.aiwg-typing-dots span:nth-child(2) { animation-delay: 0.2s; }\n.aiwg-typing-dots span:nth-child(3) { animation-delay: 0.4s; }\n@keyframes aiwg-bounce {\n 0%, 80%, 100% { transform: translateY(0); }\n 40% { transform: translateY(-6px); }\n}\n\n/* \u2500\u2500 Quick-reply chips \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.aiwg-chip-row {\n padding: 6px 16px 2px;\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n flex-shrink: 0;\n}\n.aiwg-chip {\n background: #f1f5f9;\n border: 1px solid #e2e8f0;\n border-radius: 99px;\n padding: 4px 12px;\n font-size: 12px;\n color: #4f46e5;\n cursor: pointer;\n transition: all 0.15s;\n white-space: nowrap;\n font-weight: 500;\n}\n.aiwg-chip:hover { background: #ede9fe; border-color: #a5b4fc; }\n.aiwg-chip-shimmer {\n background: linear-gradient(90deg, #e2e8f0 25%, #f1f5f9 50%, #e2e8f0 75%);\n background-size: 200% 100%;\n border: 1px solid transparent;\n border-radius: 99px;\n padding: 4px 34px;\n animation: aiwg-shimmer 1.4s ease-in-out infinite;\n cursor: default;\n pointer-events: none;\n}\n.aiwg-chip-ai-badge {\n display: inline-block;\n font-size: 9px; font-weight: 700;\n background: linear-gradient(135deg, var(--aiwg-primary), var(--aiwg-primary-dark));\n color: #fff;\n padding: 1px 5px;\n border-radius: 99px;\n margin-left: 5px;\n vertical-align: middle;\n letter-spacing: 0.04em;\n}\n@keyframes aiwg-shimmer {\n 0% { background-position: 200% 0; }\n 100% { background-position: -200% 0; }\n}\n\n/* \u2500\u2500 Input area \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.aiwg-input-area {\n padding: 12px 16px 16px;\n flex-shrink: 0;\n background: #fff;\n border-top: 1px solid #f1f5f9;\n position: relative;\n}\n.aiwg-input-row { display: flex; gap: 8px; align-items: flex-end; }\n.aiwg-input {\n flex: 1;\n border: 1.5px solid #e2e8f0;\n border-radius: 12px;\n padding: 10px 14px;\n font-size: 14px;\n font-family: inherit;\n outline: none;\n resize: none;\n background: #f8fafc;\n color: #1a1a2e;\n transition: border-color 0.15s, box-shadow 0.15s, background 0.15s;\n line-height: 1.4;\n max-height: 120px;\n overflow-y: auto;\n scrollbar-width: none;\n -ms-overflow-style: none;\n}\n.aiwg-input::-webkit-scrollbar { display: none; }\n.aiwg-input::placeholder { color: #94a3b8; }\n.aiwg-input:focus {\n border-color: var(--aiwg-primary-dark);\n background: #fff;\n box-shadow: 0 0 0 3px rgba(139,92,246,0.1);\n}\n.aiwg-send-btn {\n width: 42px; height: 42px;\n background: linear-gradient(135deg, var(--aiwg-primary), var(--aiwg-primary-dark));\n border: none; border-radius: 12px;\n color: var(--aiwg-fg-color); cursor: pointer;\n display: flex; align-items: center; justify-content: center;\n flex-shrink: 0;\n transition: transform 0.15s, box-shadow 0.15s, opacity 0.15s;\n box-shadow: 0 4px 12px rgba(99,102,241,0.35);\n}\n.aiwg-send-btn svg { width: 18px; height: 18px; }\n.aiwg-send-btn:hover { transform: translateY(-1px); box-shadow: 0 6px 16px rgba(99,102,241,0.4); }\n.aiwg-send-btn:active { transform: translateY(0); }\n.aiwg-send-btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none; }\n\n/* \u2500\u2500 Autocomplete suggestions dropdown \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.aiwg-suggestions {\n position: absolute;\n bottom: calc(100% + 4px);\n left: 16px; right: 16px;\n background: #fff;\n border: 1px solid #e2e8f0;\n border-radius: 12px;\n box-shadow: 0 8px 24px rgba(0,0,0,0.12);\n overflow: hidden;\n z-index: 100;\n animation: aiwg-fade-up 0.15s ease both;\n}\n.aiwg-suggestions-header {\n padding: 8px 14px 4px;\n font-size: 11px; font-weight: 600;\n color: #94a3b8;\n text-transform: uppercase; letter-spacing: 0.06em;\n border-bottom: 1px solid #f1f5f9;\n}\n.aiwg-suggestion-item {\n display: flex; align-items: center; gap: 10px;\n padding: 10px 14px;\n cursor: pointer;\n transition: background 0.1s;\n border-bottom: 1px solid #f8fafc;\n}\n.aiwg-suggestion-item:last-child { border-bottom: none; }\n.aiwg-suggestion-item:hover,\n.aiwg-suggestion-item.aiwg-active { background: #f5f3ff; }\n.aiwg-suggestion-icon { font-size: 16px; flex-shrink: 0; }\n.aiwg-suggestion-main { flex: 1; min-width: 0; }\n.aiwg-suggestion-text { font-size: 13px; font-weight: 500; color: #1a1a2e; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n.aiwg-suggestion-text mark { background: none; color: #6366f1; font-weight: 600; }\n.aiwg-suggestion-desc { font-size: 11px; color: #94a3b8; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n.aiwg-suggestion-badge { font-size: 10px; font-weight: 600; padding: 2px 8px; border-radius: 99px; background: #ede9fe; color: #6366f1; flex-shrink: 0; }\n.aiwg-kbd-hint {\n padding: 6px 14px;\n font-size: 11px; color: #cbd5e1; background: #f8fafc;\n display: flex; justify-content: flex-end; gap: 10px;\n}\n.aiwg-kbd-hint kbd { font-family: inherit; background: #e2e8f0; border-radius: 4px; padding: 1px 5px; font-size: 10px; color: #64748b; }\n\n/* \u2500\u2500 Error toast \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.aiwg-error {\n margin: 0 16px 8px;\n padding: 8px 12px;\n background: #fef2f2;\n border: 1px solid #fecaca;\n border-radius: 8px;\n font-size: 12px;\n color: #dc2626;\n}\n\n/* \u2500\u2500 Minimized state \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.aiwg-root.aiwg-minimized .aiwg-messages,\n.aiwg-root.aiwg-minimized .aiwg-chip-row,\n.aiwg-root.aiwg-minimized .aiwg-error,\n.aiwg-root.aiwg-minimized .aiwg-input-area,\n.aiwg-root.aiwg-minimized .aiwg-powered { display: none !important; }\n.aiwg-root.aiwg-minimized .aiwg-minimize-btn { display: none !important; }\n.aiwg-root.aiwg-minimized { cursor: pointer; box-shadow: 0 8px 32px rgba(0,0,0,0.28); }\n.aiwg-root.aiwg-minimized .aiwg-header { cursor: pointer !important; }\n\n/* \u2500\u2500 Powered-by footer \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.aiwg-powered {\n text-align: center;\n padding: 6px 16px 10px;\n font-size: 11px; color: #94a3b8;\n flex-shrink: 0;\n border-top: 1px solid #f1f5f9;\n background: #fff;\n}\n.aiwg-powered a { color: #6366f1; text-decoration: none; font-weight: 500; }\n.aiwg-powered a:hover { text-decoration: underline; }\n\n/* \u2500\u2500 Markdown rendering \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.aiwg-msg-bubble strong { font-weight: 600; }\n.aiwg-msg-bubble em { font-style: italic; }\n.aiwg-msg-bubble code { background: rgba(0,0,0,0.08); padding: 1px 5px; border-radius: 4px; font-family: 'Fira Code','Consolas',monospace; font-size: 0.9em; }\n.aiwg-msg--user .aiwg-msg-bubble code { background: rgba(255,255,255,0.2); }\n.aiwg-msg-bubble ol, .aiwg-msg-bubble ul { padding-left: 18px; margin: 6px 0; display: flex; flex-direction: column; gap: 3px; }\n.aiwg-msg-bubble ol { list-style: decimal; }\n.aiwg-msg-bubble ul { list-style: disc; }\n.aiwg-msg-bubble li { line-height: 1.5; }\n.aiwg-msg-bubble h3, .aiwg-msg-bubble h4 { font-weight: 600; margin: 8px 0 4px; }\n.aiwg-msg-bubble p { margin: 2px 0; }\n\n/* \u2500\u2500 Dark theme \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.aiwg-root.aiwg-dark { background: #0f172a; color: #e2e8f0; }\n.aiwg-dark .aiwg-msg--bot .aiwg-msg-bubble { background: #1e293b; color: #e2e8f0; }\n.aiwg-dark .aiwg-input-area { background: #0f172a; border-top-color: #1e293b; }\n.aiwg-dark .aiwg-input { background: #1e293b; border-color: #334155; color: #e2e8f0; }\n.aiwg-dark .aiwg-input:focus { border-color: #8b5cf6; background: #1e293b; box-shadow: 0 0 0 3px rgba(139,92,246,0.2); }\n.aiwg-dark .aiwg-suggestions { background: #1e293b; border-color: #334155; }\n.aiwg-dark .aiwg-suggestions-header { color: #64748b; border-bottom-color: #334155; }\n.aiwg-dark .aiwg-suggestion-item { border-bottom-color: #0f172a; }\n.aiwg-dark .aiwg-suggestion-item:hover,\n.aiwg-dark .aiwg-suggestion-item.aiwg-active { background: #2d1b69; }\n.aiwg-dark .aiwg-suggestion-text { color: #e2e8f0; }\n.aiwg-dark .aiwg-chip { background: #1e293b; border-color: #334155; }\n.aiwg-dark .aiwg-chip:hover { background: #2d1b69; }\n.aiwg-dark .aiwg-chip-shimmer { background: linear-gradient(90deg, #1e293b 25%, #334155 50%, #1e293b 75%); background-size: 200% 100%; }\n.aiwg-dark .aiwg-kbd-hint { background: #1e293b; }\n.aiwg-dark .aiwg-welcome h4 { color: #e2e8f0; }\n.aiwg-dark .aiwg-powered { background: #0f172a; border-top-color: #1e293b; }\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n STREAMING \u2014 Typewriter / word-reveal effect\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n/* Blinking text cursor shown while AI response is streaming */\n.aiwg-stream-cursor {\n display: inline-block;\n width: 2px;\n height: 1em;\n background: #6366f1;\n margin-left: 2px;\n vertical-align: text-bottom;\n border-radius: 1px;\n animation: aiwg-cursor-blink 0.65s step-end infinite;\n}\n@keyframes aiwg-cursor-blink {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0; }\n}\n\n/* Streaming bubble gets a subtle shimmer border while words are appearing */\n.aiwg-msg-bubble--streaming {\n background: linear-gradient(135deg, #f8fafc, #f1f5f9) !important;\n border: 1.5px solid #e2e8f0 !important;\n position: relative;\n overflow: hidden;\n}\n.aiwg-msg-bubble--streaming::after {\n content: '';\n position: absolute;\n inset: 0;\n background: linear-gradient(90deg, transparent 0%, rgba(99,102,241,0.06) 50%, transparent 100%);\n background-size: 200% 100%;\n animation: aiwg-bubble-shimmer 1.4s ease infinite;\n pointer-events: none;\n}\n@keyframes aiwg-bubble-shimmer {\n 0% { background-position: 200% 0; }\n 100% { background-position: -200% 0; }\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n USAGE / QUOTA \u2014 Header badge + progress bar\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n/* 3-px progress bar immediately under the header */\n.aiwg-usage-bar {\n height: 3px;\n background: rgba(255,255,255,0.18);\n flex-shrink: 0;\n overflow: hidden;\n}\n.aiwg-usage-bar-fill {\n height: 100%;\n border-radius: 0 2px 2px 0;\n transition: width 0.5s cubic-bezier(0.4,0,0.2,1), background 0.5s ease;\n}\n.aiwg-usage-bar-fill--ok { background: linear-gradient(90deg, #4ade80, #22c55e); }\n.aiwg-usage-bar-fill--warn { background: linear-gradient(90deg, #fbbf24, #f59e0b); }\n.aiwg-usage-bar-fill--exhausted { background: linear-gradient(90deg, #f87171, #ef4444); }\n\n/* Compact X/Y badge in header */\n.aiwg-quota-badge {\n background: rgba(255,255,255,0.15);\n border: 1px solid rgba(255,255,255,0.25);\n border-radius: 99px;\n padding: 2px 8px;\n font-size: 10px;\n font-weight: 600;\n color: rgba(255,255,255,0.9);\n white-space: nowrap;\n flex-shrink: 0;\n}\n.aiwg-quota-badge--warn { background: rgba(251,191,36,0.2); border-color: rgba(251,191,36,0.4); color: #fde68a; }\n.aiwg-quota-badge--exhausted { background: rgba(239,68,68,0.2); border-color: rgba(239,68,68,0.4); color: #fca5a5; }\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n LIMIT EXPIRED \u2014 Softwall banner\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.aiwg-limit-banner {\n margin: 8px 12px 0;\n padding: 10px 14px;\n background: linear-gradient(135deg, #fefce8, #fef3c7);\n border: 1px solid #fde68a;\n border-radius: 10px;\n display: flex;\n align-items: flex-start;\n gap: 10px;\n flex-shrink: 0;\n animation: aiwg-fade-up 0.3s ease both;\n}\n.aiwg-limit-banner-icon { font-size: 18px; flex-shrink: 0; margin-top: 1px; line-height: 1; }\n.aiwg-limit-banner-body { flex: 1; min-width: 0; }\n.aiwg-limit-banner-title { font-size: 12px; font-weight: 700; color: #78350f; margin-bottom: 2px; }\n.aiwg-limit-banner-text { font-size: 11px; color: #92400e; line-height: 1.5; }\n.aiwg-limit-banner-actions { display: flex; gap: 6px; margin-top: 8px; flex-wrap: wrap; }\n\n.aiwg-limit-btn {\n padding: 4px 12px;\n border-radius: 99px;\n font-size: 11px;\n font-weight: 600;\n border: none;\n cursor: pointer;\n transition: transform 0.15s ease, box-shadow 0.15s ease, filter 0.15s;\n line-height: 1.5;\n}\n.aiwg-limit-btn--primary {\n background: linear-gradient(135deg, #6366f1, #8b5cf6);\n color: #fff;\n box-shadow: 0 2px 8px rgba(99,102,241,0.3);\n}\n.aiwg-limit-btn--secondary {\n background: rgba(99,102,241,0.08);\n color: #6366f1;\n border: 1px solid rgba(99,102,241,0.2);\n}\n.aiwg-limit-btn:hover { transform: translateY(-1px); filter: brightness(1.08); }\n.aiwg-limit-btn:active { transform: translateY(0); }\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n OFFLINE PANEL \u2014 Slide-up overlay: Form + Booking tabs\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.aiwg-offline-overlay {\n position: absolute;\n inset: 0;\n background: #fff;\n border-radius: 16px;\n display: flex;\n flex-direction: column;\n z-index: 20;\n animation: aiwg-slide-up 0.32s cubic-bezier(0.34, 1.4, 0.64, 1) both;\n overflow: hidden;\n}\n@keyframes aiwg-slide-up {\n from { transform: translateY(100%); opacity: 0; }\n to { transform: translateY(0); opacity: 1; }\n}\n.aiwg-offline-header {\n padding: 16px 16px 0;\n display: flex;\n align-items: center;\n justify-content: space-between;\n flex-shrink: 0;\n}\n.aiwg-offline-header-left h4 { font-size: 15px; font-weight: 700; color: #1a1a2e; margin-bottom: 2px; }\n.aiwg-offline-header-left p { font-size: 12px; color: #64748b; line-height: 1.4; }\n.aiwg-offline-close-btn {\n background: #f1f5f9;\n border: none;\n border-radius: 50%;\n width: 30px; height: 30px;\n display: flex; align-items: center; justify-content: center;\n cursor: pointer;\n font-size: 14px; color: #64748b;\n transition: background 0.15s;\n flex-shrink: 0;\n}\n.aiwg-offline-close-btn:hover { background: #e2e8f0; }\n\n/* Tab switcher */\n.aiwg-mode-tabs {\n display: flex;\n gap: 0;\n background: #f1f5f9;\n border-radius: 10px;\n padding: 3px;\n margin: 12px 16px 0;\n flex-shrink: 0;\n}\n.aiwg-mode-tab {\n flex: 1;\n padding: 7px 6px;\n border: none;\n background: transparent;\n border-radius: 8px;\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n color: #64748b;\n transition: all 0.15s;\n display: flex; align-items: center; justify-content: center; gap: 5px;\n}\n.aiwg-mode-tab.aiwg-active {\n background: #fff;\n color: #6366f1;\n font-weight: 700;\n box-shadow: 0 1px 4px rgba(0,0,0,0.08);\n}\n\n/* Scrollable body */\n.aiwg-offline-body {\n flex: 1;\n overflow-y: auto;\n padding: 14px 16px 16px;\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n/* \u2500\u2500 Offline Form \u2500\u2500 */\n.aiwg-offline-form { display: flex; flex-direction: column; gap: 10px; }\n.aiwg-offline-field { display: flex; flex-direction: column; gap: 4px; }\n.aiwg-offline-label { font-size: 12px; font-weight: 600; color: #475569; }\n.aiwg-offline-label .aiwg-required { color: #ef4444; }\n.aiwg-offline-control,\n.aiwg-offline-textarea {\n padding: 9px 12px;\n border: 1.5px solid #e2e8f0;\n border-radius: 8px;\n font-size: 13px;\n font-family: inherit;\n outline: none;\n background: #f8fafc;\n color: #1a1a2e;\n transition: border-color 0.15s, box-shadow 0.15s, background 0.15s;\n width: 100%;\n box-sizing: border-box;\n}\n.aiwg-offline-control:focus,\n.aiwg-offline-textarea:focus {\n border-color: #6366f1;\n box-shadow: 0 0 0 3px rgba(99,102,241,0.1);\n background: #fff;\n}\n.aiwg-offline-textarea { resize: none; min-height: 75px; }\n.aiwg-offline-submit {\n padding: 11px;\n background: linear-gradient(135deg, #6366f1, #8b5cf6);\n color: #fff;\n border: none;\n border-radius: 10px;\n font-size: 13px;\n font-weight: 700;\n cursor: pointer;\n transition: transform 0.15s, box-shadow 0.15s;\n box-shadow: 0 4px 12px rgba(99,102,241,0.3);\n letter-spacing: 0.01em;\n}\n.aiwg-offline-submit:hover:not(:disabled) { transform: translateY(-1px); box-shadow: 0 6px 16px rgba(99,102,241,0.4); }\n.aiwg-offline-submit:active:not(:disabled) { transform: translateY(0); }\n.aiwg-offline-submit:disabled { opacity: 0.55; cursor: not-allowed; transform: none; box-shadow: none; }\n\n/* Success state */\n.aiwg-offline-success {\n text-align: center;\n padding: 20px 12px;\n background: #f0fdf4;\n border: 1px solid #86efac;\n border-radius: 12px;\n animation: aiwg-fade-up 0.3s ease both;\n}\n.aiwg-offline-success-icon { font-size: 36px; margin-bottom: 8px; }\n.aiwg-offline-success h4 { font-size: 15px; font-weight: 700; color: #166534; margin-bottom: 4px; }\n.aiwg-offline-success p { font-size: 12px; color: #4ade80; line-height: 1.5; }\n\n/* \u2500\u2500 Booking card \u2500\u2500 */\n.aiwg-booking-card {\n background: linear-gradient(135deg, #f5f3ff, #ede9fe);\n border: 1px solid #ddd6fe;\n border-radius: 14px;\n padding: 20px 16px;\n text-align: center;\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 10px;\n}\n.aiwg-booking-icon { font-size: 40px; line-height: 1.1; }\n.aiwg-booking-title { font-size: 15px; font-weight: 700; color: #4c1d95; }\n.aiwg-booking-desc { font-size: 12px; color: #6d28d9; line-height: 1.5; }\n.aiwg-booking-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 10px 22px;\n background: linear-gradient(135deg, #6366f1, #8b5cf6);\n color: #fff;\n border: none;\n border-radius: 99px;\n font-size: 13px;\n font-weight: 700;\n cursor: pointer;\n transition: transform 0.15s, box-shadow 0.15s;\n box-shadow: 0 4px 14px rgba(99,102,241,0.35);\n text-decoration: none;\n letter-spacing: 0.01em;\n}\n.aiwg-booking-btn:hover { transform: translateY(-2px); box-shadow: 0 8px 20px rgba(99,102,241,0.45); }\n.aiwg-booking-btn:active { transform: translateY(0); }\n\n/* \u2500\u2500 Shared fade-up used by banner + success \u2500\u2500 */\n@keyframes aiwg-fade-up {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n DARK THEME \u2014 overrides for new elements\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.aiwg-dark .aiwg-stream-cursor { background: #a78bfa; }\n.aiwg-dark .aiwg-msg-bubble--streaming { background: linear-gradient(135deg, #1e293b, #0f172a) !important; border-color: #334155 !important; color: #e2e8f0; }\n.aiwg-dark .aiwg-limit-banner { background: linear-gradient(135deg, #292202, #261e01); border-color: #a16207; }\n.aiwg-dark .aiwg-limit-banner-title { color: #fde68a; }\n.aiwg-dark .aiwg-limit-banner-text { color: #fcd34d; }\n.aiwg-dark .aiwg-limit-btn--secondary { background: rgba(139,92,246,0.15); color: #a78bfa; border-color: rgba(139,92,246,0.3); }\n.aiwg-dark .aiwg-offline-overlay { background: #0f172a; }\n.aiwg-dark .aiwg-offline-header-left h4 { color: #e2e8f0; }\n.aiwg-dark .aiwg-offline-header-left p { color: #64748b; }\n.aiwg-dark .aiwg-offline-close-btn { background: #1e293b; color: #94a3b8; }\n.aiwg-dark .aiwg-offline-close-btn:hover { background: #334155; }\n.aiwg-dark .aiwg-mode-tabs { background: #1e293b; }\n.aiwg-dark .aiwg-mode-tab { color: #94a3b8; }\n.aiwg-dark .aiwg-mode-tab.aiwg-active { background: #0f172a; color: #a78bfa; }\n.aiwg-dark .aiwg-offline-label { color: #94a3b8; }\n.aiwg-dark .aiwg-offline-control,\n.aiwg-dark .aiwg-offline-textarea { background: #1e293b; border-color: #334155; color: #e2e8f0; }\n.aiwg-dark .aiwg-offline-control:focus,\n.aiwg-dark .aiwg-offline-textarea:focus { border-color: #8b5cf6; box-shadow: 0 0 0 3px rgba(139,92,246,0.2); background: #1e293b; }\n.aiwg-dark .aiwg-booking-card { background: linear-gradient(135deg, #2d1b69, #1a0f3c); border-color: #4c1d95; }\n.aiwg-dark .aiwg-booking-title { color: #c4b5fd; }\n.aiwg-dark .aiwg-booking-desc { color: #a78bfa; }\n.aiwg-dark .aiwg-quota-badge { color: rgba(255,255,255,0.85); background: rgba(255,255,255,0.1); border-color: rgba(255,255,255,0.2); }\n";
14
+ export declare const WIDGET_CSS = "\n/* \u2500\u2500 Shadow-DOM host isolation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n:host {\n all: initial !important;\n display: block !important;\n}\n\n/* Scoped box-model reset \u2014 only touches widget elements, not all of shadow DOM */\n.aiwg-root *, .aiwg-root *::before, .aiwg-root *::after {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n}\n\n/* \u2500\u2500 Container \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.aiwg-root {\n --aiwg-primary: #6366f1;\n --aiwg-primary-dark: #8b5cf6;\n --aiwg-fg-color: #ffffff;\n --aiwg-bg-color: #ffffff;\n --aiwg-chat-bg: #f1f5f9;\n\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n font-size: 14px;\n line-height: 1.5;\n color: #1a1a2e;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n border-radius: 16px;\n background: var(--aiwg-bg-color);\n position: fixed !important;\n bottom: 24px;\n right: 24px;\n width: 380px !important;\n max-width: calc(100vw - 48px) !important;\n z-index: 99999;\n transition: height 0.25s ease;\n box-shadow: 0 24px 64px rgba(0,0,0,0.18);\n}\n\n/* \u2500\u2500 Header \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.aiwg-header {\n background: linear-gradient(135deg, var(--aiwg-primary) 0%, var(--aiwg-primary-dark) 100%);\n padding: 16px 20px;\n display: flex;\n align-items: center;\n gap: 12px;\n flex-shrink: 0;\n position: relative;\n overflow: hidden;\n}\n.aiwg-header::before {\n content: '';\n position: absolute;\n width: 140px; height: 140px;\n background: rgba(255,255,255,0.08);\n border-radius: 50%;\n top: -50px; right: -30px;\n}\n.aiwg-avatar {\n width: 40px; height: 40px;\n background: rgba(255,255,255,0.25);\n border-radius: 50%;\n display: flex; align-items: center; justify-content: center;\n font-size: 18px;\n flex-shrink: 0;\n border: 2px solid rgba(255,255,255,0.4);\n}\n.aiwg-header-info { flex: 1; }\n.aiwg-title { color: #fff; font-weight: 600; font-size: 15px; }\n.aiwg-subtitle {\n color: rgba(255,255,255,0.75);\n font-size: 12px;\n display: flex; align-items: center; gap: 5px;\n}\n.aiwg-online-dot {\n width: 7px; height: 7px;\n background: #4ade80;\n border-radius: 50%;\n display: inline-block;\n animation: aiwg-pulse 2s ease-in-out infinite;\n}\n@keyframes aiwg-pulse {\n 0%, 100% { opacity: 1; transform: scale(1); }\n 50% { opacity: 0.6; transform: scale(0.85); }\n}\n.aiwg-minimize-btn {\n width: 36px; height: 36px;\n background: rgba(255,255,255,0.18);\n border: 1.5px solid rgba(255,255,255,0.3);\n border-radius: 50%;\n color: #fff; cursor: pointer;\n display: flex; align-items: center; justify-content: center;\n font-size: 18px; line-height: 1;\n transition: background 0.15s, transform 0.15s;\n flex-shrink: 0;\n position: relative; z-index: 2;\n}\n.aiwg-minimize-btn * { cursor: pointer !important; pointer-events: none; }\n.aiwg-minimize-btn:hover { background: rgba(255,255,255,0.32); transform: scale(1.08); }\n\n/* \u2500\u2500 Messages area \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.aiwg-messages {\n flex: 1;\n overflow-y: auto;\n padding: 20px 16px 8px;\n scroll-behavior: smooth;\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n.aiwg-messages::-webkit-scrollbar { width: 4px; }\n.aiwg-messages::-webkit-scrollbar-thumb { background: #e2e8f0; border-radius: 99px; }\n\n/* \u2500\u2500 Welcome card \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.aiwg-welcome {\n text-align: center;\n padding: 24px 16px;\n display: flex; flex-direction: column; align-items: center; gap: 8px;\n}\n.aiwg-welcome-icon { font-size: 36px; margin-bottom: 4px; }\n.aiwg-welcome h4 { font-size: 15px; font-weight: 600; color: #1a1a2e; }\n.aiwg-welcome p { font-size: 13px; color: #64748b; }\n\n/* \u2500\u2500 Message bubbles \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.aiwg-msg {\n display: flex;\n gap: 8px;\n align-items: flex-end;\n animation: aiwg-fade-up 0.2s ease both;\n}\n@keyframes aiwg-fade-up {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n}\n.aiwg-msg--user { flex-direction: row-reverse; }\n.aiwg-msg-avatar {\n width: 28px; height: 28px; border-radius: 50%;\n display: flex; align-items: center; justify-content: center;\n font-size: 13px; flex-shrink: 0;\n margin-bottom: 2px;\n}\n.aiwg-msg--bot .aiwg-msg-avatar { background: linear-gradient(135deg, var(--aiwg-primary), var(--aiwg-primary-dark)); color: var(--aiwg-fg-color); }\n.aiwg-msg--user .aiwg-msg-avatar { background: #e2e8f0; color: #64748b; }\n.aiwg-msg-body { max-width: 78%; }\n.aiwg-msg-bubble {\n padding: 10px 14px;\n border-radius: 18px;\n font-size: 14px;\n line-height: 1.55;\n word-break: break-word;\n}\n.aiwg-msg--bot .aiwg-msg-bubble { background: var(--aiwg-chat-bg); color: #1a1a2e; border-bottom-left-radius: 4px; }\n.aiwg-msg--user .aiwg-msg-bubble { background: linear-gradient(135deg, var(--aiwg-primary), var(--aiwg-primary-dark)); color: var(--aiwg-fg-color); border-bottom-right-radius: 4px; }\n.aiwg-msg-time { font-size: 10px; color: #94a3b8; margin-top: 4px; text-align: right; padding: 0 4px; }\n.aiwg-msg--bot .aiwg-msg-time { text-align: left; }\n\n/* \u2500\u2500 Typing indicator \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.aiwg-typing { padding: 10px 14px; }\n.aiwg-typing-dots { display: flex; gap: 4px; align-items: center; }\n.aiwg-typing-dots span {\n width: 7px; height: 7px;\n background: #94a3b8; border-radius: 50%;\n animation: aiwg-bounce 1.2s ease-in-out infinite;\n}\n.aiwg-typing-dots span:nth-child(2) { animation-delay: 0.2s; }\n.aiwg-typing-dots span:nth-child(3) { animation-delay: 0.4s; }\n@keyframes aiwg-bounce {\n 0%, 80%, 100% { transform: translateY(0); }\n 40% { transform: translateY(-6px); }\n}\n\n/* \u2500\u2500 Quick-reply chips \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.aiwg-chip-row {\n padding: 6px 16px 2px;\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n flex-shrink: 0;\n}\n.aiwg-chip {\n background: #f1f5f9;\n border: 1px solid #e2e8f0;\n border-radius: 99px;\n padding: 4px 12px;\n font-size: 12px;\n color: #4f46e5;\n cursor: pointer;\n transition: all 0.15s;\n white-space: nowrap;\n font-weight: 500;\n}\n.aiwg-chip:hover { background: #ede9fe; border-color: #a5b4fc; }\n.aiwg-chip-shimmer {\n background: linear-gradient(90deg, #e2e8f0 25%, #f1f5f9 50%, #e2e8f0 75%);\n background-size: 200% 100%;\n border: 1px solid transparent;\n border-radius: 99px;\n padding: 4px 34px;\n animation: aiwg-shimmer 1.4s ease-in-out infinite;\n cursor: default;\n pointer-events: none;\n}\n.aiwg-chip-ai-badge {\n display: inline-block;\n font-size: 9px; font-weight: 700;\n background: linear-gradient(135deg, var(--aiwg-primary), var(--aiwg-primary-dark));\n color: #fff;\n padding: 1px 5px;\n border-radius: 99px;\n margin-left: 5px;\n vertical-align: middle;\n letter-spacing: 0.04em;\n}\n@keyframes aiwg-shimmer {\n 0% { background-position: 200% 0; }\n 100% { background-position: -200% 0; }\n}\n\n/* \u2500\u2500 Input area \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.aiwg-input-area {\n padding: 12px 16px 16px;\n flex-shrink: 0;\n background: #fff;\n border-top: 1px solid #f1f5f9;\n position: relative;\n}\n.aiwg-input-row { display: flex; gap: 8px; align-items: flex-end; }\n.aiwg-input {\n flex: 1;\n border: 1.5px solid #e2e8f0;\n border-radius: 12px;\n padding: 10px 14px;\n font-size: 14px;\n font-family: inherit;\n outline: none;\n resize: none;\n background: #f8fafc;\n color: #1a1a2e;\n transition: border-color 0.15s, box-shadow 0.15s, background 0.15s;\n line-height: 1.4;\n max-height: 120px;\n overflow-y: auto;\n scrollbar-width: none;\n -ms-overflow-style: none;\n}\n.aiwg-input::-webkit-scrollbar { display: none; }\n.aiwg-input::placeholder { color: #94a3b8; }\n.aiwg-input:focus {\n border-color: var(--aiwg-primary-dark);\n background: #fff;\n box-shadow: 0 0 0 3px rgba(139,92,246,0.1);\n}\n.aiwg-send-btn {\n width: 42px; height: 42px;\n background: linear-gradient(135deg, var(--aiwg-primary), var(--aiwg-primary-dark));\n border: none; border-radius: 12px;\n color: var(--aiwg-fg-color); cursor: pointer;\n display: flex; align-items: center; justify-content: center;\n flex-shrink: 0;\n transition: transform 0.15s, box-shadow 0.15s, opacity 0.15s;\n box-shadow: 0 4px 12px rgba(99,102,241,0.35);\n}\n.aiwg-send-btn svg { width: 18px; height: 18px; }\n.aiwg-send-btn:hover { transform: translateY(-1px); box-shadow: 0 6px 16px rgba(99,102,241,0.4); }\n.aiwg-send-btn:active { transform: translateY(0); }\n.aiwg-send-btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none; }\n\n/* \u2500\u2500 Autocomplete suggestions dropdown \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.aiwg-suggestions {\n position: absolute;\n bottom: calc(100% + 4px);\n left: 16px; right: 16px;\n background: #fff;\n border: 1px solid #e2e8f0;\n border-radius: 12px;\n box-shadow: 0 8px 24px rgba(0,0,0,0.12);\n overflow: hidden;\n z-index: 100;\n animation: aiwg-fade-up 0.15s ease both;\n}\n.aiwg-suggestions-header {\n padding: 8px 14px 4px;\n font-size: 11px; font-weight: 600;\n color: #94a3b8;\n text-transform: uppercase; letter-spacing: 0.06em;\n border-bottom: 1px solid #f1f5f9;\n}\n.aiwg-suggestion-item {\n display: flex; align-items: center; gap: 10px;\n padding: 10px 14px;\n cursor: pointer;\n transition: background 0.1s;\n border-bottom: 1px solid #f8fafc;\n}\n.aiwg-suggestion-item:last-child { border-bottom: none; }\n.aiwg-suggestion-item:hover,\n.aiwg-suggestion-item.aiwg-active { background: #f5f3ff; }\n.aiwg-suggestion-icon { font-size: 16px; flex-shrink: 0; }\n.aiwg-suggestion-main { flex: 1; min-width: 0; }\n.aiwg-suggestion-text { font-size: 13px; font-weight: 500; color: #1a1a2e; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n.aiwg-suggestion-text mark { background: none; color: #6366f1; font-weight: 600; }\n.aiwg-suggestion-desc { font-size: 11px; color: #94a3b8; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n.aiwg-suggestion-badge { font-size: 10px; font-weight: 600; padding: 2px 8px; border-radius: 99px; background: #ede9fe; color: #6366f1; flex-shrink: 0; }\n.aiwg-kbd-hint {\n padding: 6px 14px;\n font-size: 11px; color: #cbd5e1; background: #f8fafc;\n display: flex; justify-content: flex-end; gap: 10px;\n}\n.aiwg-kbd-hint kbd { font-family: inherit; background: #e2e8f0; border-radius: 4px; padding: 1px 5px; font-size: 10px; color: #64748b; }\n\n/* \u2500\u2500 Error toast \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.aiwg-error {\n margin: 0 16px 8px;\n padding: 8px 12px;\n background: #fef2f2;\n border: 1px solid #fecaca;\n border-radius: 8px;\n font-size: 12px;\n color: #dc2626;\n}\n\n/* \u2500\u2500 Minimized state \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.aiwg-root.aiwg-minimized .aiwg-messages,\n.aiwg-root.aiwg-minimized .aiwg-chip-row,\n.aiwg-root.aiwg-minimized .aiwg-error,\n.aiwg-root.aiwg-minimized .aiwg-input-area,\n.aiwg-root.aiwg-minimized .aiwg-powered { display: none !important; }\n.aiwg-root.aiwg-minimized .aiwg-minimize-btn { display: none !important; }\n.aiwg-root.aiwg-minimized { cursor: pointer; box-shadow: 0 8px 32px rgba(0,0,0,0.28); }\n.aiwg-root.aiwg-minimized .aiwg-header { cursor: pointer !important; }\n\n/* \u2500\u2500 Powered-by footer \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.aiwg-powered {\n text-align: center;\n padding: 6px 16px 10px;\n font-size: 11px; color: #94a3b8;\n flex-shrink: 0;\n border-top: 1px solid #f1f5f9;\n background: #fff;\n}\n.aiwg-powered a { color: #6366f1; text-decoration: none; font-weight: 500; }\n.aiwg-powered a:hover { text-decoration: underline; }\n\n/* \u2500\u2500 Markdown rendering \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.aiwg-msg-bubble strong { font-weight: 600; }\n.aiwg-msg-bubble em { font-style: italic; }\n.aiwg-msg-bubble code { background: rgba(0,0,0,0.08); padding: 1px 5px; border-radius: 4px; font-family: 'Fira Code','Consolas',monospace; font-size: 0.9em; }\n.aiwg-msg--user .aiwg-msg-bubble code { background: rgba(255,255,255,0.2); }\n.aiwg-msg-bubble ol, .aiwg-msg-bubble ul { padding-left: 18px; margin: 6px 0; display: flex; flex-direction: column; gap: 3px; }\n.aiwg-msg-bubble ol { list-style: decimal; }\n.aiwg-msg-bubble ul { list-style: disc; }\n.aiwg-msg-bubble li { line-height: 1.5; }\n.aiwg-msg-bubble h3, .aiwg-msg-bubble h4 { font-weight: 600; margin: 8px 0 4px; }\n.aiwg-msg-bubble p { margin: 2px 0; }\n.aiwg-msg-bubble hr { border: none; border-top: 1px solid rgba(0,0,0,0.15); margin: 8px 0; }\n/* Links */\n.aiwg-msg-bubble a { color: #6366f1; text-decoration: underline; word-break: break-all; }\n.aiwg-msg--user .aiwg-msg-bubble a { color: #e0e7ff; }\n.aiwg-msg-bubble a:hover { opacity: 0.8; }\n/* Images */\n.aiwg-msg-bubble .aiwg-img { max-width: 100%; border-radius: 8px; margin: 6px 0; display: block; }\n/* Fenced code blocks */\n.aiwg-msg-bubble pre { background: #1e293b; color: #e2e8f0; border-radius: 8px; padding: 10px 12px; margin: 6px 0; overflow-x: auto; font-size: 0.85em; }\n.aiwg-msg-bubble pre code { background: none; padding: 0; font-size: inherit; color: inherit; }\n/* Tables */\n.aiwg-msg-bubble .aiwg-table { width: 100%; border-collapse: collapse; font-size: 0.85em; margin: 8px 0; }\n.aiwg-msg-bubble .aiwg-table th,\n.aiwg-msg-bubble .aiwg-table td { border: 1px solid rgba(0,0,0,0.15); padding: 5px 8px; text-align: left; }\n.aiwg-msg-bubble .aiwg-table th { background: rgba(99,102,241,0.12); font-weight: 600; }\n.aiwg-msg-bubble .aiwg-table tr:nth-child(even) { background: rgba(0,0,0,0.04); }\n\n/* \u2500\u2500 Dark theme \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.aiwg-root.aiwg-dark { background: #0f172a; color: #e2e8f0; }\n.aiwg-dark .aiwg-msg--bot .aiwg-msg-bubble { background: #1e293b; color: #e2e8f0; }\n.aiwg-dark .aiwg-input-area { background: #0f172a; border-top-color: #1e293b; }\n.aiwg-dark .aiwg-input { background: #1e293b; border-color: #334155; color: #e2e8f0; }\n.aiwg-dark .aiwg-input:focus { border-color: #8b5cf6; background: #1e293b; box-shadow: 0 0 0 3px rgba(139,92,246,0.2); }\n.aiwg-dark .aiwg-suggestions { background: #1e293b; border-color: #334155; }\n.aiwg-dark .aiwg-suggestions-header { color: #64748b; border-bottom-color: #334155; }\n.aiwg-dark .aiwg-suggestion-item { border-bottom-color: #0f172a; }\n.aiwg-dark .aiwg-suggestion-item:hover,\n.aiwg-dark .aiwg-suggestion-item.aiwg-active { background: #2d1b69; }\n.aiwg-dark .aiwg-suggestion-text { color: #e2e8f0; }\n.aiwg-dark .aiwg-chip { background: #1e293b; border-color: #334155; }\n.aiwg-dark .aiwg-chip:hover { background: #2d1b69; }\n.aiwg-dark .aiwg-chip-shimmer { background: linear-gradient(90deg, #1e293b 25%, #334155 50%, #1e293b 75%); background-size: 200% 100%; }\n.aiwg-dark .aiwg-kbd-hint { background: #1e293b; }\n.aiwg-dark .aiwg-welcome h4 { color: #e2e8f0; }\n.aiwg-dark .aiwg-powered { background: #0f172a; border-top-color: #1e293b; }\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n STREAMING \u2014 Typewriter / word-reveal effect\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n/* Blinking text cursor shown while AI response is streaming */\n.aiwg-stream-cursor {\n display: inline-block;\n width: 2px;\n height: 1em;\n background: #6366f1;\n margin-left: 2px;\n vertical-align: text-bottom;\n border-radius: 1px;\n animation: aiwg-cursor-blink 0.65s step-end infinite;\n}\n@keyframes aiwg-cursor-blink {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0; }\n}\n\n/* Streaming bubble gets a subtle shimmer border while words are appearing */\n.aiwg-msg-bubble--streaming {\n background: linear-gradient(135deg, #f8fafc, #f1f5f9) !important;\n border: 1.5px solid #e2e8f0 !important;\n position: relative;\n overflow: hidden;\n}\n.aiwg-msg-bubble--streaming::after {\n content: '';\n position: absolute;\n inset: 0;\n background: linear-gradient(90deg, transparent 0%, rgba(99,102,241,0.06) 50%, transparent 100%);\n background-size: 200% 100%;\n animation: aiwg-bubble-shimmer 1.4s ease infinite;\n pointer-events: none;\n}\n@keyframes aiwg-bubble-shimmer {\n 0% { background-position: 200% 0; }\n 100% { background-position: -200% 0; }\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n USAGE / QUOTA \u2014 Header badge + progress bar\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n/* 3-px progress bar immediately under the header */\n.aiwg-usage-bar {\n height: 3px;\n background: rgba(255,255,255,0.18);\n flex-shrink: 0;\n overflow: hidden;\n}\n.aiwg-usage-bar-fill {\n height: 100%;\n border-radius: 0 2px 2px 0;\n transition: width 0.5s cubic-bezier(0.4,0,0.2,1), background 0.5s ease;\n}\n.aiwg-usage-bar-fill--ok { background: linear-gradient(90deg, #4ade80, #22c55e); }\n.aiwg-usage-bar-fill--warn { background: linear-gradient(90deg, #fbbf24, #f59e0b); }\n.aiwg-usage-bar-fill--exhausted { background: linear-gradient(90deg, #f87171, #ef4444); }\n\n/* Compact X/Y badge in header */\n.aiwg-quota-badge {\n background: rgba(255,255,255,0.15);\n border: 1px solid rgba(255,255,255,0.25);\n border-radius: 99px;\n padding: 2px 8px;\n font-size: 10px;\n font-weight: 600;\n color: rgba(255,255,255,0.9);\n white-space: nowrap;\n flex-shrink: 0;\n}\n.aiwg-quota-badge--warn { background: rgba(251,191,36,0.2); border-color: rgba(251,191,36,0.4); color: #fde68a; }\n.aiwg-quota-badge--exhausted { background: rgba(239,68,68,0.2); border-color: rgba(239,68,68,0.4); color: #fca5a5; }\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n LIMIT EXPIRED \u2014 Softwall banner\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.aiwg-limit-banner {\n margin: 8px 12px 0;\n padding: 10px 14px;\n background: linear-gradient(135deg, #fefce8, #fef3c7);\n border: 1px solid #fde68a;\n border-radius: 10px;\n display: flex;\n align-items: flex-start;\n gap: 10px;\n flex-shrink: 0;\n animation: aiwg-fade-up 0.3s ease both;\n}\n.aiwg-limit-banner-icon { font-size: 18px; flex-shrink: 0; margin-top: 1px; line-height: 1; }\n.aiwg-limit-banner-body { flex: 1; min-width: 0; }\n.aiwg-limit-banner-title { font-size: 12px; font-weight: 700; color: #78350f; margin-bottom: 2px; }\n.aiwg-limit-banner-text { font-size: 11px; color: #92400e; line-height: 1.5; }\n.aiwg-limit-banner-actions { display: flex; gap: 6px; margin-top: 8px; flex-wrap: wrap; }\n\n.aiwg-limit-btn {\n padding: 4px 12px;\n border-radius: 99px;\n font-size: 11px;\n font-weight: 600;\n border: none;\n cursor: pointer;\n transition: transform 0.15s ease, box-shadow 0.15s ease, filter 0.15s;\n line-height: 1.5;\n}\n.aiwg-limit-btn--primary {\n background: linear-gradient(135deg, #6366f1, #8b5cf6);\n color: #fff;\n box-shadow: 0 2px 8px rgba(99,102,241,0.3);\n}\n.aiwg-limit-btn--secondary {\n background: rgba(99,102,241,0.08);\n color: #6366f1;\n border: 1px solid rgba(99,102,241,0.2);\n}\n.aiwg-limit-btn:hover { transform: translateY(-1px); filter: brightness(1.08); }\n.aiwg-limit-btn:active { transform: translateY(0); }\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n OFFLINE PANEL \u2014 Slide-up overlay: Form + Booking tabs\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.aiwg-offline-overlay {\n position: absolute;\n inset: 0;\n background: #fff;\n border-radius: 16px;\n display: flex;\n flex-direction: column;\n z-index: 20;\n animation: aiwg-slide-up 0.32s cubic-bezier(0.34, 1.4, 0.64, 1) both;\n overflow: hidden;\n}\n@keyframes aiwg-slide-up {\n from { transform: translateY(100%); opacity: 0; }\n to { transform: translateY(0); opacity: 1; }\n}\n.aiwg-offline-header {\n padding: 16px 16px 0;\n display: flex;\n align-items: center;\n justify-content: space-between;\n flex-shrink: 0;\n}\n.aiwg-offline-header-left h4 { font-size: 15px; font-weight: 700; color: #1a1a2e; margin-bottom: 2px; }\n.aiwg-offline-header-left p { font-size: 12px; color: #64748b; line-height: 1.4; }\n.aiwg-offline-close-btn {\n background: #f1f5f9;\n border: none;\n border-radius: 50%;\n width: 30px; height: 30px;\n display: flex; align-items: center; justify-content: center;\n cursor: pointer;\n font-size: 14px; color: #64748b;\n transition: background 0.15s;\n flex-shrink: 0;\n}\n.aiwg-offline-close-btn:hover { background: #e2e8f0; }\n\n/* Tab switcher */\n.aiwg-mode-tabs {\n display: flex;\n gap: 0;\n background: #f1f5f9;\n border-radius: 10px;\n padding: 3px;\n margin: 12px 16px 0;\n flex-shrink: 0;\n}\n.aiwg-mode-tab {\n flex: 1;\n padding: 7px 6px;\n border: none;\n background: transparent;\n border-radius: 8px;\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n color: #64748b;\n transition: all 0.15s;\n display: flex; align-items: center; justify-content: center; gap: 5px;\n}\n.aiwg-mode-tab.aiwg-active {\n background: #fff;\n color: #6366f1;\n font-weight: 700;\n box-shadow: 0 1px 4px rgba(0,0,0,0.08);\n}\n\n/* Scrollable body */\n.aiwg-offline-body {\n flex: 1;\n overflow-y: auto;\n padding: 14px 16px 16px;\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n/* \u2500\u2500 Offline Form \u2500\u2500 */\n.aiwg-offline-form { display: flex; flex-direction: column; gap: 10px; }\n.aiwg-offline-field { display: flex; flex-direction: column; gap: 4px; }\n.aiwg-offline-label { font-size: 12px; font-weight: 600; color: #475569; }\n.aiwg-offline-label .aiwg-required { color: #ef4444; }\n.aiwg-offline-control,\n.aiwg-offline-textarea {\n padding: 9px 12px;\n border: 1.5px solid #e2e8f0;\n border-radius: 8px;\n font-size: 13px;\n font-family: inherit;\n outline: none;\n background: #f8fafc;\n color: #1a1a2e;\n transition: border-color 0.15s, box-shadow 0.15s, background 0.15s;\n width: 100%;\n box-sizing: border-box;\n}\n.aiwg-offline-control:focus,\n.aiwg-offline-textarea:focus {\n border-color: #6366f1;\n box-shadow: 0 0 0 3px rgba(99,102,241,0.1);\n background: #fff;\n}\n.aiwg-offline-textarea { resize: none; min-height: 75px; }\n.aiwg-offline-submit {\n padding: 11px;\n background: linear-gradient(135deg, #6366f1, #8b5cf6);\n color: #fff;\n border: none;\n border-radius: 10px;\n font-size: 13px;\n font-weight: 700;\n cursor: pointer;\n transition: transform 0.15s, box-shadow 0.15s;\n box-shadow: 0 4px 12px rgba(99,102,241,0.3);\n letter-spacing: 0.01em;\n}\n.aiwg-offline-submit:hover:not(:disabled) { transform: translateY(-1px); box-shadow: 0 6px 16px rgba(99,102,241,0.4); }\n.aiwg-offline-submit:active:not(:disabled) { transform: translateY(0); }\n.aiwg-offline-submit:disabled { opacity: 0.55; cursor: not-allowed; transform: none; box-shadow: none; }\n\n/* Success state */\n.aiwg-offline-success {\n text-align: center;\n padding: 20px 12px;\n background: #f0fdf4;\n border: 1px solid #86efac;\n border-radius: 12px;\n animation: aiwg-fade-up 0.3s ease both;\n}\n.aiwg-offline-success-icon { font-size: 36px; margin-bottom: 8px; }\n.aiwg-offline-success h4 { font-size: 15px; font-weight: 700; color: #166534; margin-bottom: 4px; }\n.aiwg-offline-success p { font-size: 12px; color: #4ade80; line-height: 1.5; }\n\n/* \u2500\u2500 Booking card \u2500\u2500 */\n.aiwg-booking-card {\n background: linear-gradient(135deg, #f5f3ff, #ede9fe);\n border: 1px solid #ddd6fe;\n border-radius: 14px;\n padding: 20px 16px;\n text-align: center;\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 10px;\n}\n.aiwg-booking-icon { font-size: 40px; line-height: 1.1; }\n.aiwg-booking-title { font-size: 15px; font-weight: 700; color: #4c1d95; }\n.aiwg-booking-desc { font-size: 12px; color: #6d28d9; line-height: 1.5; }\n.aiwg-booking-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 10px 22px;\n background: linear-gradient(135deg, #6366f1, #8b5cf6);\n color: #fff;\n border: none;\n border-radius: 99px;\n font-size: 13px;\n font-weight: 700;\n cursor: pointer;\n transition: transform 0.15s, box-shadow 0.15s;\n box-shadow: 0 4px 14px rgba(99,102,241,0.35);\n text-decoration: none;\n letter-spacing: 0.01em;\n}\n.aiwg-booking-btn:hover { transform: translateY(-2px); box-shadow: 0 8px 20px rgba(99,102,241,0.45); }\n.aiwg-booking-btn:active { transform: translateY(0); }\n\n/* \u2500\u2500 Shared fade-up used by banner + success \u2500\u2500 */\n@keyframes aiwg-fade-up {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n DARK THEME \u2014 overrides for new elements\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.aiwg-dark .aiwg-stream-cursor { background: #a78bfa; }\n.aiwg-dark .aiwg-msg-bubble--streaming { background: linear-gradient(135deg, #1e293b, #0f172a) !important; border-color: #334155 !important; color: #e2e8f0; }\n.aiwg-dark .aiwg-limit-banner { background: linear-gradient(135deg, #292202, #261e01); border-color: #a16207; }\n.aiwg-dark .aiwg-limit-banner-title { color: #fde68a; }\n.aiwg-dark .aiwg-limit-banner-text { color: #fcd34d; }\n.aiwg-dark .aiwg-limit-btn--secondary { background: rgba(139,92,246,0.15); color: #a78bfa; border-color: rgba(139,92,246,0.3); }\n.aiwg-dark .aiwg-offline-overlay { background: #0f172a; }\n.aiwg-dark .aiwg-offline-header-left h4 { color: #e2e8f0; }\n.aiwg-dark .aiwg-offline-header-left p { color: #64748b; }\n.aiwg-dark .aiwg-offline-close-btn { background: #1e293b; color: #94a3b8; }\n.aiwg-dark .aiwg-offline-close-btn:hover { background: #334155; }\n.aiwg-dark .aiwg-mode-tabs { background: #1e293b; }\n.aiwg-dark .aiwg-mode-tab { color: #94a3b8; }\n.aiwg-dark .aiwg-mode-tab.aiwg-active { background: #0f172a; color: #a78bfa; }\n.aiwg-dark .aiwg-offline-label { color: #94a3b8; }\n.aiwg-dark .aiwg-offline-control,\n.aiwg-dark .aiwg-offline-textarea { background: #1e293b; border-color: #334155; color: #e2e8f0; }\n.aiwg-dark .aiwg-offline-control:focus,\n.aiwg-dark .aiwg-offline-textarea:focus { border-color: #8b5cf6; box-shadow: 0 0 0 3px rgba(139,92,246,0.2); background: #1e293b; }\n.aiwg-dark .aiwg-booking-card { background: linear-gradient(135deg, #2d1b69, #1a0f3c); border-color: #4c1d95; }\n.aiwg-dark .aiwg-booking-title { color: #c4b5fd; }\n.aiwg-dark .aiwg-booking-desc { color: #a78bfa; }\n.aiwg-dark .aiwg-quota-badge { color: rgba(255,255,255,0.85); background: rgba(255,255,255,0.1); border-color: rgba(255,255,255,0.2); }\n";
@@ -356,7 +356,7 @@ export const WIDGET_CSS = `
356
356
  .aiwg-powered a { color: #6366f1; text-decoration: none; font-weight: 500; }
357
357
  .aiwg-powered a:hover { text-decoration: underline; }
358
358
 
359
- /* ── Markdown rendering ───────────────────────────────────────────────────── */
359
+ /* ── Markdown rendering ────────────────────────────────────────────────── */
360
360
  .aiwg-msg-bubble strong { font-weight: 600; }
361
361
  .aiwg-msg-bubble em { font-style: italic; }
362
362
  .aiwg-msg-bubble code { background: rgba(0,0,0,0.08); padding: 1px 5px; border-radius: 4px; font-family: 'Fira Code','Consolas',monospace; font-size: 0.9em; }
@@ -367,6 +367,22 @@ export const WIDGET_CSS = `
367
367
  .aiwg-msg-bubble li { line-height: 1.5; }
368
368
  .aiwg-msg-bubble h3, .aiwg-msg-bubble h4 { font-weight: 600; margin: 8px 0 4px; }
369
369
  .aiwg-msg-bubble p { margin: 2px 0; }
370
+ .aiwg-msg-bubble hr { border: none; border-top: 1px solid rgba(0,0,0,0.15); margin: 8px 0; }
371
+ /* Links */
372
+ .aiwg-msg-bubble a { color: #6366f1; text-decoration: underline; word-break: break-all; }
373
+ .aiwg-msg--user .aiwg-msg-bubble a { color: #e0e7ff; }
374
+ .aiwg-msg-bubble a:hover { opacity: 0.8; }
375
+ /* Images */
376
+ .aiwg-msg-bubble .aiwg-img { max-width: 100%; border-radius: 8px; margin: 6px 0; display: block; }
377
+ /* Fenced code blocks */
378
+ .aiwg-msg-bubble pre { background: #1e293b; color: #e2e8f0; border-radius: 8px; padding: 10px 12px; margin: 6px 0; overflow-x: auto; font-size: 0.85em; }
379
+ .aiwg-msg-bubble pre code { background: none; padding: 0; font-size: inherit; color: inherit; }
380
+ /* Tables */
381
+ .aiwg-msg-bubble .aiwg-table { width: 100%; border-collapse: collapse; font-size: 0.85em; margin: 8px 0; }
382
+ .aiwg-msg-bubble .aiwg-table th,
383
+ .aiwg-msg-bubble .aiwg-table td { border: 1px solid rgba(0,0,0,0.15); padding: 5px 8px; text-align: left; }
384
+ .aiwg-msg-bubble .aiwg-table th { background: rgba(99,102,241,0.12); font-weight: 600; }
385
+ .aiwg-msg-bubble .aiwg-table tr:nth-child(even) { background: rgba(0,0,0,0.04); }
370
386
 
371
387
  /* ── Dark theme ───────────────────────────────────────────────────────────── */
372
388
  .aiwg-root.aiwg-dark { background: #0f172a; color: #e2e8f0; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@courseecho/ai-core-sdk",
3
- "version": "1.0.27",
3
+ "version": "1.0.29",
4
4
  "description": "Framework-agnostic core AI chat SDK. Shared logic for all framework wrappers.",
5
5
  "license": "MIT",
6
6
  "author": "CourseEcho",