@courseecho/ai-core-sdk 1.0.25 → 1.0.27
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/communication-service.d.ts +41 -2
- package/dist/communication-service.js +114 -2
- package/dist/models.d.ts +111 -0
- package/dist/sdk.d.ts +2 -0
- package/dist/sdk.js +4 -0
- package/dist/widget-css.d.ts +1 -1
- package/dist/widget-css.js +327 -0
- package/package.json +1 -1
|
@@ -2,12 +2,35 @@
|
|
|
2
2
|
* AI Widget SDK - Core Communication Service
|
|
3
3
|
* Handle HTTP requests to backend API
|
|
4
4
|
*/
|
|
5
|
-
import { AiQueryRequest, AiQueryResponse } from './models';
|
|
5
|
+
import { AiQueryRequest, AiQueryResponse, QuotaInfo } from './models';
|
|
6
6
|
export declare class AiCommunicationService {
|
|
7
7
|
private apiEndpoint;
|
|
8
8
|
private apiKey?;
|
|
9
9
|
private jwtToken?;
|
|
10
|
-
|
|
10
|
+
private lastQuotaInfo;
|
|
11
|
+
/** Unique identifier for this widget session — sent as X-Session-Id header */
|
|
12
|
+
private readonly sessionId;
|
|
13
|
+
/** Lightweight browser fingerprint — sent as X-Client-Fingerprint header */
|
|
14
|
+
private readonly clientFingerprint;
|
|
15
|
+
/** Timestamp of the last outgoing AI query (ms) */
|
|
16
|
+
private lastRequestAt;
|
|
17
|
+
/** Minimum ms between requests (default 1 500 — human typing speed floor) */
|
|
18
|
+
private minRequestIntervalMs;
|
|
19
|
+
constructor(apiEndpoint: string, apiKey?: string, minRequestIntervalMs?: number);
|
|
20
|
+
/** Override the minimum request interval at runtime. */
|
|
21
|
+
setMinRequestInterval(ms: number): void;
|
|
22
|
+
/**
|
|
23
|
+
* Generate a random session identifier (UUID v4-like) that persists for the
|
|
24
|
+
* lifetime of the communication-service instance (= one widget mount).
|
|
25
|
+
*/
|
|
26
|
+
private static generateSessionId;
|
|
27
|
+
/**
|
|
28
|
+
* Build a stable, non-PII browser fingerprint string.
|
|
29
|
+
* This is NOT a tracking fingerprint — it only helps the backend distinguish
|
|
30
|
+
* a scripted headless browser from a real user session.
|
|
31
|
+
* Encoded as Base64 to avoid special-character issues in headers.
|
|
32
|
+
*/
|
|
33
|
+
private static generateFingerprint;
|
|
11
34
|
/**
|
|
12
35
|
* Set JWT token for authentication
|
|
13
36
|
*/
|
|
@@ -24,6 +47,22 @@ export declare class AiCommunicationService {
|
|
|
24
47
|
* Send query to backend API
|
|
25
48
|
*/
|
|
26
49
|
sendQuery(request: AiQueryRequest): Promise<AiQueryResponse>;
|
|
50
|
+
/**
|
|
51
|
+
* Returns the last quota info extracted from a 429 response, or null.
|
|
52
|
+
*/
|
|
53
|
+
getLastQuotaInfo(): QuotaInfo | null;
|
|
54
|
+
/**
|
|
55
|
+
* Stream an AI response word-by-word as an async generator.
|
|
56
|
+
*
|
|
57
|
+
* Internally fetches the complete response from the backend and then
|
|
58
|
+
* reveals it one word at a time with a configurable delay, giving a
|
|
59
|
+
* natural typewriter feel without requiring SSE support from the server.
|
|
60
|
+
*
|
|
61
|
+
* @param request - The same AiQueryRequest used with sendQuery()
|
|
62
|
+
* @param wordDelayMs - Delay between each word in ms (default: 30)
|
|
63
|
+
* @yields string - One word (or punctuation token) at a time
|
|
64
|
+
*/
|
|
65
|
+
sendQueryStream(request: AiQueryRequest, wordDelayMs?: number): AsyncGenerator<string, AiQueryResponse, undefined>;
|
|
27
66
|
/**
|
|
28
67
|
* Exchange an API key for a short-lived JWT via POST /api/auth/token.
|
|
29
68
|
* Returns the JWT string on success; throws ApiError on failure.
|
|
@@ -15,12 +15,66 @@ async function safeJson(res) {
|
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
export class AiCommunicationService {
|
|
18
|
-
constructor(apiEndpoint, apiKey) {
|
|
18
|
+
constructor(apiEndpoint, apiKey, minRequestIntervalMs = 1500) {
|
|
19
|
+
this.lastQuotaInfo = null;
|
|
20
|
+
/** Timestamp of the last outgoing AI query (ms) */
|
|
21
|
+
this.lastRequestAt = 0;
|
|
19
22
|
if (!apiEndpoint) {
|
|
20
23
|
throw new Error('apiEndpoint is required');
|
|
21
24
|
}
|
|
22
|
-
this.apiEndpoint = apiEndpoint.replace(/\/$/, '');
|
|
25
|
+
this.apiEndpoint = apiEndpoint.replace(/\/$/, '');
|
|
23
26
|
this.apiKey = apiKey;
|
|
27
|
+
this.minRequestIntervalMs = minRequestIntervalMs;
|
|
28
|
+
this.sessionId = AiCommunicationService.generateSessionId();
|
|
29
|
+
this.clientFingerprint = AiCommunicationService.generateFingerprint();
|
|
30
|
+
}
|
|
31
|
+
/** Override the minimum request interval at runtime. */
|
|
32
|
+
setMinRequestInterval(ms) {
|
|
33
|
+
this.minRequestIntervalMs = ms;
|
|
34
|
+
}
|
|
35
|
+
// ── Session / fingerprint helpers ─────────────────────────────────────
|
|
36
|
+
/**
|
|
37
|
+
* Generate a random session identifier (UUID v4-like) that persists for the
|
|
38
|
+
* lifetime of the communication-service instance (= one widget mount).
|
|
39
|
+
*/
|
|
40
|
+
static generateSessionId() {
|
|
41
|
+
if (typeof crypto !== 'undefined' && crypto.randomUUID) {
|
|
42
|
+
return crypto.randomUUID();
|
|
43
|
+
}
|
|
44
|
+
// Fallback for environments where crypto.randomUUID is missing
|
|
45
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
|
46
|
+
const r = (Math.random() * 16) | 0;
|
|
47
|
+
return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16);
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Build a stable, non-PII browser fingerprint string.
|
|
52
|
+
* This is NOT a tracking fingerprint — it only helps the backend distinguish
|
|
53
|
+
* a scripted headless browser from a real user session.
|
|
54
|
+
* Encoded as Base64 to avoid special-character issues in headers.
|
|
55
|
+
*/
|
|
56
|
+
static generateFingerprint() {
|
|
57
|
+
try {
|
|
58
|
+
const parts = [];
|
|
59
|
+
if (typeof navigator !== 'undefined') {
|
|
60
|
+
parts.push(navigator.userAgent || '');
|
|
61
|
+
parts.push(navigator.language || '');
|
|
62
|
+
parts.push(String(navigator.hardwareConcurrency ?? ''));
|
|
63
|
+
}
|
|
64
|
+
if (typeof screen !== 'undefined') {
|
|
65
|
+
parts.push(`${screen.width}x${screen.height}`);
|
|
66
|
+
parts.push(String(screen.colorDepth ?? ''));
|
|
67
|
+
}
|
|
68
|
+
if (typeof window !== 'undefined') {
|
|
69
|
+
parts.push(Intl.DateTimeFormat().resolvedOptions().timeZone || '');
|
|
70
|
+
}
|
|
71
|
+
const raw = parts.join('|');
|
|
72
|
+
// btoa is available in browsers; fall back to plain string in Node/test envs
|
|
73
|
+
return typeof btoa !== 'undefined' ? btoa(raw).slice(0, 64) : raw.slice(0, 64);
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return 'unknown';
|
|
77
|
+
}
|
|
24
78
|
}
|
|
25
79
|
/**
|
|
26
80
|
* Set JWT token for authentication
|
|
@@ -47,8 +101,21 @@ export class AiCommunicationService {
|
|
|
47
101
|
if (!this.jwtToken && !this.apiKey) {
|
|
48
102
|
throw new Error('Authentication required: Set JWT token or API key. Call setJwtToken() first.');
|
|
49
103
|
}
|
|
104
|
+
// ── Client-side rate guard ──────────────────────────────────────────
|
|
105
|
+
// Reject requests that arrive faster than a human could realistically type
|
|
106
|
+
// (after reading the previous AI response). This immediately drops any
|
|
107
|
+
// scripted flood at the SDK layer before it ever reaches the network.
|
|
108
|
+
if (this.minRequestIntervalMs > 0) {
|
|
109
|
+
const elapsed = Date.now() - this.lastRequestAt;
|
|
110
|
+
if (this.lastRequestAt > 0 && elapsed < this.minRequestIntervalMs) {
|
|
111
|
+
throw new ApiError(`Please wait ${((this.minRequestIntervalMs - elapsed) / 1000).toFixed(1)} seconds before sending another message.`, 429);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
this.lastRequestAt = Date.now();
|
|
50
115
|
const headers = {
|
|
51
116
|
'Content-Type': 'application/json',
|
|
117
|
+
'X-Session-Id': this.sessionId,
|
|
118
|
+
'X-Client-Fingerprint': this.clientFingerprint,
|
|
52
119
|
};
|
|
53
120
|
if (this.jwtToken) {
|
|
54
121
|
headers['Authorization'] = `Bearer ${this.jwtToken}`;
|
|
@@ -64,6 +131,17 @@ export class AiCommunicationService {
|
|
|
64
131
|
});
|
|
65
132
|
if (!response.ok) {
|
|
66
133
|
const errorData = await safeJson(response);
|
|
134
|
+
// Parse quota headers from 429 responses
|
|
135
|
+
if (response.status === 429) {
|
|
136
|
+
const used = parseInt(response.headers.get('X-Daily-Used') || response.headers.get('x-daily-used') || '0', 10);
|
|
137
|
+
const limit = parseInt(response.headers.get('X-Daily-Limit') || response.headers.get('x-daily-limit') || '0', 10);
|
|
138
|
+
const resetAt = response.headers.get('X-Rate-Limit-Reset') || response.headers.get('x-rate-limit-reset');
|
|
139
|
+
this.lastQuotaInfo = {
|
|
140
|
+
used: isNaN(used) ? (errorData?.queriesUsedToday ?? 0) : used,
|
|
141
|
+
limit: isNaN(limit) ? (errorData?.dailyLimit ?? 0) : limit,
|
|
142
|
+
resetAt: resetAt ? new Date(resetAt) : undefined,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
67
145
|
throw new ApiError(errorData?.message || `HTTP ${response.status}`, response.status, errorData);
|
|
68
146
|
}
|
|
69
147
|
const data = (await safeJson(response));
|
|
@@ -80,6 +158,40 @@ export class AiCommunicationService {
|
|
|
80
158
|
throw new ApiError(`Failed to send query: ${String(error)}`, 500);
|
|
81
159
|
}
|
|
82
160
|
}
|
|
161
|
+
/**
|
|
162
|
+
* Returns the last quota info extracted from a 429 response, or null.
|
|
163
|
+
*/
|
|
164
|
+
getLastQuotaInfo() {
|
|
165
|
+
return this.lastQuotaInfo;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Stream an AI response word-by-word as an async generator.
|
|
169
|
+
*
|
|
170
|
+
* Internally fetches the complete response from the backend and then
|
|
171
|
+
* reveals it one word at a time with a configurable delay, giving a
|
|
172
|
+
* natural typewriter feel without requiring SSE support from the server.
|
|
173
|
+
*
|
|
174
|
+
* @param request - The same AiQueryRequest used with sendQuery()
|
|
175
|
+
* @param wordDelayMs - Delay between each word in ms (default: 30)
|
|
176
|
+
* @yields string - One word (or punctuation token) at a time
|
|
177
|
+
*/
|
|
178
|
+
async *sendQueryStream(request, wordDelayMs = 30) {
|
|
179
|
+
// Fetch the full response first
|
|
180
|
+
const data = await this.sendQuery(request);
|
|
181
|
+
// Stream the response text word-by-word
|
|
182
|
+
const words = data.response.split(/(\s+)/);
|
|
183
|
+
for (let i = 0; i < words.length; i++) {
|
|
184
|
+
yield words[i];
|
|
185
|
+
// Tiny pause every word; slightly longer after sentence-ending punctuation.
|
|
186
|
+
const delay = /[.!?]$/.test(words[i].trimEnd()) && wordDelayMs > 0
|
|
187
|
+
? wordDelayMs * 4
|
|
188
|
+
: wordDelayMs;
|
|
189
|
+
if (delay > 0) {
|
|
190
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return data;
|
|
194
|
+
}
|
|
83
195
|
/**
|
|
84
196
|
* Exchange an API key for a short-lived JWT via POST /api/auth/token.
|
|
85
197
|
* Returns the JWT string on success; throws ApiError on failure.
|
package/dist/models.d.ts
CHANGED
|
@@ -48,6 +48,78 @@ export interface AiContext {
|
|
|
48
48
|
userId?: string;
|
|
49
49
|
customData?: Record<string, any>;
|
|
50
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Controls the word-by-word typewriter streaming effect for AI responses.
|
|
53
|
+
*/
|
|
54
|
+
export interface StreamingConfig {
|
|
55
|
+
/** Enable typewriter reveal effect. Default: true */
|
|
56
|
+
enabled?: boolean;
|
|
57
|
+
/** Delay in ms between each word. Default: 30 */
|
|
58
|
+
wordDelayMs?: number;
|
|
59
|
+
/** Show blinking cursor while streaming. Default: true */
|
|
60
|
+
showCursor?: boolean;
|
|
61
|
+
}
|
|
62
|
+
/** A single field in the offline contact form */
|
|
63
|
+
export interface OfflineFormField {
|
|
64
|
+
name: string;
|
|
65
|
+
label: string;
|
|
66
|
+
type: 'text' | 'email' | 'tel' | 'textarea' | 'select';
|
|
67
|
+
placeholder?: string;
|
|
68
|
+
required?: boolean;
|
|
69
|
+
/** Only for type='select' */
|
|
70
|
+
options?: string[];
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Configuration for what users see when their daily AI query quota is exhausted.
|
|
74
|
+
* Set this on AiContextConfig.limitExpiredConfig.
|
|
75
|
+
*/
|
|
76
|
+
export interface LimitExpiredConfig {
|
|
77
|
+
/**
|
|
78
|
+
* 'soft' (default) — show a banner warning; user can still type but gets no AI suggestions.
|
|
79
|
+
* 'hard' — hide input completely; show only offline form / booking.
|
|
80
|
+
*/
|
|
81
|
+
mode?: 'soft' | 'hard';
|
|
82
|
+
/** Title shown in the limit-exceeded banner. Default: 'Daily limit reached' */
|
|
83
|
+
bannerTitle?: string;
|
|
84
|
+
/** Body text in the banner. Default: 'You've used all your AI queries for today.' */
|
|
85
|
+
bannerMessage?: string;
|
|
86
|
+
/** Label for the "Go Offline" / "Leave a message" toggle button. Default: 'Leave a message' */
|
|
87
|
+
offlineButtonLabel?: string;
|
|
88
|
+
/** URL to an upgrade / pricing page. If set, shows an upgrade CTA button. */
|
|
89
|
+
upgradeUrl?: string;
|
|
90
|
+
/** Label for the upgrade button. Default: 'Upgrade' */
|
|
91
|
+
upgradeLabel?: string;
|
|
92
|
+
/** Offline enquiry form configuration */
|
|
93
|
+
offlineForm?: {
|
|
94
|
+
title?: string;
|
|
95
|
+
subtitle?: string;
|
|
96
|
+
fields?: OfflineFormField[];
|
|
97
|
+
submitLabel?: string;
|
|
98
|
+
successTitle?: string;
|
|
99
|
+
successMessage?: string;
|
|
100
|
+
/** POST endpoint that receives { name, email, message, extra } as JSON */
|
|
101
|
+
submitEndpoint?: string;
|
|
102
|
+
/** Custom async submit handler — overrides submitEndpoint */
|
|
103
|
+
onSubmit?: (data: Record<string, string>) => Promise<void>;
|
|
104
|
+
};
|
|
105
|
+
/** Booking / calendar configuration */
|
|
106
|
+
booking?: {
|
|
107
|
+
title?: string;
|
|
108
|
+
description?: string;
|
|
109
|
+
buttonLabel?: string;
|
|
110
|
+
/** Calendly, Cal.com, or any bookings URL */
|
|
111
|
+
calendarUrl?: string;
|
|
112
|
+
/** true = open in a new browser tab, false = embed inline (default: true) */
|
|
113
|
+
openInNewTab?: boolean;
|
|
114
|
+
/** Optional custom click handler */
|
|
115
|
+
onBookingClick?: () => void;
|
|
116
|
+
};
|
|
117
|
+
/**
|
|
118
|
+
* Which tab to show first when the offline panel opens.
|
|
119
|
+
* 'form' | 'booking'. Default: 'form' if offlineForm defined, else 'booking'.
|
|
120
|
+
*/
|
|
121
|
+
defaultOfflineTab?: 'form' | 'booking';
|
|
122
|
+
}
|
|
51
123
|
export interface AiContextConfig {
|
|
52
124
|
context: AiContext;
|
|
53
125
|
apiEndpoint?: string;
|
|
@@ -58,6 +130,35 @@ export interface AiContextConfig {
|
|
|
58
130
|
enableCharts?: boolean;
|
|
59
131
|
enableExport?: boolean;
|
|
60
132
|
suggestionConfig?: SuggestionConfig;
|
|
133
|
+
/**
|
|
134
|
+
* Typewriter streaming effect for AI responses.
|
|
135
|
+
* Defaults to enabled with 30 ms word delay.
|
|
136
|
+
*/
|
|
137
|
+
streamingConfig?: StreamingConfig;
|
|
138
|
+
/**
|
|
139
|
+
* Configuration for the limit-expired softwall / offline panel.
|
|
140
|
+
* If not provided a sensible default banner is shown.
|
|
141
|
+
*/
|
|
142
|
+
limitExpiredConfig?: LimitExpiredConfig;
|
|
143
|
+
/**
|
|
144
|
+
* Show a live X/Y query usage badge in the widget header.
|
|
145
|
+
* Default: false
|
|
146
|
+
*/
|
|
147
|
+
showUsageCounter?: boolean;
|
|
148
|
+
/**
|
|
149
|
+
* Client-side abuse prevention settings.
|
|
150
|
+
* These defend against scripted quota-exhaustion attacks on behalf of your
|
|
151
|
+
* paying customer — e.g. a competitor visiting the site and rapid-firing
|
|
152
|
+
* requests to drain the daily limit.
|
|
153
|
+
*/
|
|
154
|
+
abuseProtection?: {
|
|
155
|
+
/**
|
|
156
|
+
* Minimum milliseconds that must elapse between consecutive AI query
|
|
157
|
+
* submissions. Prevents sub-human-speed scripted requests.
|
|
158
|
+
* Default: 1500 ms. Set 0 to disable.
|
|
159
|
+
*/
|
|
160
|
+
minRequestIntervalMs?: number;
|
|
161
|
+
};
|
|
61
162
|
}
|
|
62
163
|
export interface AiQueryRequest {
|
|
63
164
|
userQuery: string;
|
|
@@ -73,6 +174,16 @@ export interface AiQueryResponse {
|
|
|
73
174
|
tokensUsed: number;
|
|
74
175
|
cost: number;
|
|
75
176
|
timestamp: Date;
|
|
177
|
+
/** Number of AI queries consumed today (may be sent by backend) */
|
|
178
|
+
queriesUsedToday?: number;
|
|
179
|
+
/** Daily query limit for this API key */
|
|
180
|
+
dailyLimit?: number;
|
|
181
|
+
}
|
|
182
|
+
/** Quota info extracted from 429 response headers or body */
|
|
183
|
+
export interface QuotaInfo {
|
|
184
|
+
used: number;
|
|
185
|
+
limit: number;
|
|
186
|
+
resetAt?: Date;
|
|
76
187
|
}
|
|
77
188
|
export interface AiErrorResponse {
|
|
78
189
|
error: string;
|
package/dist/sdk.d.ts
CHANGED
package/dist/sdk.js
CHANGED
|
@@ -111,6 +111,10 @@ export class AiWidgetSDK {
|
|
|
111
111
|
setJwtToken(token) {
|
|
112
112
|
this.communicationService.setJwtToken(token);
|
|
113
113
|
}
|
|
114
|
+
/** Get the currently stored JWT token (if any). */
|
|
115
|
+
getJwtToken() {
|
|
116
|
+
return this.communicationService.getJwtToken();
|
|
117
|
+
}
|
|
114
118
|
/**
|
|
115
119
|
* Send a query to the AI backend
|
|
116
120
|
*/
|
package/dist/widget-css.d.ts
CHANGED
|
@@ -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";
|
|
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";
|
package/dist/widget-css.js
CHANGED
|
@@ -386,4 +386,331 @@ export const WIDGET_CSS = `
|
|
|
386
386
|
.aiwg-dark .aiwg-kbd-hint { background: #1e293b; }
|
|
387
387
|
.aiwg-dark .aiwg-welcome h4 { color: #e2e8f0; }
|
|
388
388
|
.aiwg-dark .aiwg-powered { background: #0f172a; border-top-color: #1e293b; }
|
|
389
|
+
|
|
390
|
+
/* ═══════════════════════════════════════════════════════════════════════════
|
|
391
|
+
STREAMING — Typewriter / word-reveal effect
|
|
392
|
+
═══════════════════════════════════════════════════════════════════════════ */
|
|
393
|
+
|
|
394
|
+
/* Blinking text cursor shown while AI response is streaming */
|
|
395
|
+
.aiwg-stream-cursor {
|
|
396
|
+
display: inline-block;
|
|
397
|
+
width: 2px;
|
|
398
|
+
height: 1em;
|
|
399
|
+
background: #6366f1;
|
|
400
|
+
margin-left: 2px;
|
|
401
|
+
vertical-align: text-bottom;
|
|
402
|
+
border-radius: 1px;
|
|
403
|
+
animation: aiwg-cursor-blink 0.65s step-end infinite;
|
|
404
|
+
}
|
|
405
|
+
@keyframes aiwg-cursor-blink {
|
|
406
|
+
0%, 100% { opacity: 1; }
|
|
407
|
+
50% { opacity: 0; }
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/* Streaming bubble gets a subtle shimmer border while words are appearing */
|
|
411
|
+
.aiwg-msg-bubble--streaming {
|
|
412
|
+
background: linear-gradient(135deg, #f8fafc, #f1f5f9) !important;
|
|
413
|
+
border: 1.5px solid #e2e8f0 !important;
|
|
414
|
+
position: relative;
|
|
415
|
+
overflow: hidden;
|
|
416
|
+
}
|
|
417
|
+
.aiwg-msg-bubble--streaming::after {
|
|
418
|
+
content: '';
|
|
419
|
+
position: absolute;
|
|
420
|
+
inset: 0;
|
|
421
|
+
background: linear-gradient(90deg, transparent 0%, rgba(99,102,241,0.06) 50%, transparent 100%);
|
|
422
|
+
background-size: 200% 100%;
|
|
423
|
+
animation: aiwg-bubble-shimmer 1.4s ease infinite;
|
|
424
|
+
pointer-events: none;
|
|
425
|
+
}
|
|
426
|
+
@keyframes aiwg-bubble-shimmer {
|
|
427
|
+
0% { background-position: 200% 0; }
|
|
428
|
+
100% { background-position: -200% 0; }
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/* ═══════════════════════════════════════════════════════════════════════════
|
|
432
|
+
USAGE / QUOTA — Header badge + progress bar
|
|
433
|
+
═══════════════════════════════════════════════════════════════════════════ */
|
|
434
|
+
|
|
435
|
+
/* 3-px progress bar immediately under the header */
|
|
436
|
+
.aiwg-usage-bar {
|
|
437
|
+
height: 3px;
|
|
438
|
+
background: rgba(255,255,255,0.18);
|
|
439
|
+
flex-shrink: 0;
|
|
440
|
+
overflow: hidden;
|
|
441
|
+
}
|
|
442
|
+
.aiwg-usage-bar-fill {
|
|
443
|
+
height: 100%;
|
|
444
|
+
border-radius: 0 2px 2px 0;
|
|
445
|
+
transition: width 0.5s cubic-bezier(0.4,0,0.2,1), background 0.5s ease;
|
|
446
|
+
}
|
|
447
|
+
.aiwg-usage-bar-fill--ok { background: linear-gradient(90deg, #4ade80, #22c55e); }
|
|
448
|
+
.aiwg-usage-bar-fill--warn { background: linear-gradient(90deg, #fbbf24, #f59e0b); }
|
|
449
|
+
.aiwg-usage-bar-fill--exhausted { background: linear-gradient(90deg, #f87171, #ef4444); }
|
|
450
|
+
|
|
451
|
+
/* Compact X/Y badge in header */
|
|
452
|
+
.aiwg-quota-badge {
|
|
453
|
+
background: rgba(255,255,255,0.15);
|
|
454
|
+
border: 1px solid rgba(255,255,255,0.25);
|
|
455
|
+
border-radius: 99px;
|
|
456
|
+
padding: 2px 8px;
|
|
457
|
+
font-size: 10px;
|
|
458
|
+
font-weight: 600;
|
|
459
|
+
color: rgba(255,255,255,0.9);
|
|
460
|
+
white-space: nowrap;
|
|
461
|
+
flex-shrink: 0;
|
|
462
|
+
}
|
|
463
|
+
.aiwg-quota-badge--warn { background: rgba(251,191,36,0.2); border-color: rgba(251,191,36,0.4); color: #fde68a; }
|
|
464
|
+
.aiwg-quota-badge--exhausted { background: rgba(239,68,68,0.2); border-color: rgba(239,68,68,0.4); color: #fca5a5; }
|
|
465
|
+
|
|
466
|
+
/* ═══════════════════════════════════════════════════════════════════════════
|
|
467
|
+
LIMIT EXPIRED — Softwall banner
|
|
468
|
+
═══════════════════════════════════════════════════════════════════════════ */
|
|
469
|
+
|
|
470
|
+
.aiwg-limit-banner {
|
|
471
|
+
margin: 8px 12px 0;
|
|
472
|
+
padding: 10px 14px;
|
|
473
|
+
background: linear-gradient(135deg, #fefce8, #fef3c7);
|
|
474
|
+
border: 1px solid #fde68a;
|
|
475
|
+
border-radius: 10px;
|
|
476
|
+
display: flex;
|
|
477
|
+
align-items: flex-start;
|
|
478
|
+
gap: 10px;
|
|
479
|
+
flex-shrink: 0;
|
|
480
|
+
animation: aiwg-fade-up 0.3s ease both;
|
|
481
|
+
}
|
|
482
|
+
.aiwg-limit-banner-icon { font-size: 18px; flex-shrink: 0; margin-top: 1px; line-height: 1; }
|
|
483
|
+
.aiwg-limit-banner-body { flex: 1; min-width: 0; }
|
|
484
|
+
.aiwg-limit-banner-title { font-size: 12px; font-weight: 700; color: #78350f; margin-bottom: 2px; }
|
|
485
|
+
.aiwg-limit-banner-text { font-size: 11px; color: #92400e; line-height: 1.5; }
|
|
486
|
+
.aiwg-limit-banner-actions { display: flex; gap: 6px; margin-top: 8px; flex-wrap: wrap; }
|
|
487
|
+
|
|
488
|
+
.aiwg-limit-btn {
|
|
489
|
+
padding: 4px 12px;
|
|
490
|
+
border-radius: 99px;
|
|
491
|
+
font-size: 11px;
|
|
492
|
+
font-weight: 600;
|
|
493
|
+
border: none;
|
|
494
|
+
cursor: pointer;
|
|
495
|
+
transition: transform 0.15s ease, box-shadow 0.15s ease, filter 0.15s;
|
|
496
|
+
line-height: 1.5;
|
|
497
|
+
}
|
|
498
|
+
.aiwg-limit-btn--primary {
|
|
499
|
+
background: linear-gradient(135deg, #6366f1, #8b5cf6);
|
|
500
|
+
color: #fff;
|
|
501
|
+
box-shadow: 0 2px 8px rgba(99,102,241,0.3);
|
|
502
|
+
}
|
|
503
|
+
.aiwg-limit-btn--secondary {
|
|
504
|
+
background: rgba(99,102,241,0.08);
|
|
505
|
+
color: #6366f1;
|
|
506
|
+
border: 1px solid rgba(99,102,241,0.2);
|
|
507
|
+
}
|
|
508
|
+
.aiwg-limit-btn:hover { transform: translateY(-1px); filter: brightness(1.08); }
|
|
509
|
+
.aiwg-limit-btn:active { transform: translateY(0); }
|
|
510
|
+
|
|
511
|
+
/* ═══════════════════════════════════════════════════════════════════════════
|
|
512
|
+
OFFLINE PANEL — Slide-up overlay: Form + Booking tabs
|
|
513
|
+
═══════════════════════════════════════════════════════════════════════════ */
|
|
514
|
+
|
|
515
|
+
.aiwg-offline-overlay {
|
|
516
|
+
position: absolute;
|
|
517
|
+
inset: 0;
|
|
518
|
+
background: #fff;
|
|
519
|
+
border-radius: 16px;
|
|
520
|
+
display: flex;
|
|
521
|
+
flex-direction: column;
|
|
522
|
+
z-index: 20;
|
|
523
|
+
animation: aiwg-slide-up 0.32s cubic-bezier(0.34, 1.4, 0.64, 1) both;
|
|
524
|
+
overflow: hidden;
|
|
525
|
+
}
|
|
526
|
+
@keyframes aiwg-slide-up {
|
|
527
|
+
from { transform: translateY(100%); opacity: 0; }
|
|
528
|
+
to { transform: translateY(0); opacity: 1; }
|
|
529
|
+
}
|
|
530
|
+
.aiwg-offline-header {
|
|
531
|
+
padding: 16px 16px 0;
|
|
532
|
+
display: flex;
|
|
533
|
+
align-items: center;
|
|
534
|
+
justify-content: space-between;
|
|
535
|
+
flex-shrink: 0;
|
|
536
|
+
}
|
|
537
|
+
.aiwg-offline-header-left h4 { font-size: 15px; font-weight: 700; color: #1a1a2e; margin-bottom: 2px; }
|
|
538
|
+
.aiwg-offline-header-left p { font-size: 12px; color: #64748b; line-height: 1.4; }
|
|
539
|
+
.aiwg-offline-close-btn {
|
|
540
|
+
background: #f1f5f9;
|
|
541
|
+
border: none;
|
|
542
|
+
border-radius: 50%;
|
|
543
|
+
width: 30px; height: 30px;
|
|
544
|
+
display: flex; align-items: center; justify-content: center;
|
|
545
|
+
cursor: pointer;
|
|
546
|
+
font-size: 14px; color: #64748b;
|
|
547
|
+
transition: background 0.15s;
|
|
548
|
+
flex-shrink: 0;
|
|
549
|
+
}
|
|
550
|
+
.aiwg-offline-close-btn:hover { background: #e2e8f0; }
|
|
551
|
+
|
|
552
|
+
/* Tab switcher */
|
|
553
|
+
.aiwg-mode-tabs {
|
|
554
|
+
display: flex;
|
|
555
|
+
gap: 0;
|
|
556
|
+
background: #f1f5f9;
|
|
557
|
+
border-radius: 10px;
|
|
558
|
+
padding: 3px;
|
|
559
|
+
margin: 12px 16px 0;
|
|
560
|
+
flex-shrink: 0;
|
|
561
|
+
}
|
|
562
|
+
.aiwg-mode-tab {
|
|
563
|
+
flex: 1;
|
|
564
|
+
padding: 7px 6px;
|
|
565
|
+
border: none;
|
|
566
|
+
background: transparent;
|
|
567
|
+
border-radius: 8px;
|
|
568
|
+
font-size: 12px;
|
|
569
|
+
font-weight: 500;
|
|
570
|
+
cursor: pointer;
|
|
571
|
+
color: #64748b;
|
|
572
|
+
transition: all 0.15s;
|
|
573
|
+
display: flex; align-items: center; justify-content: center; gap: 5px;
|
|
574
|
+
}
|
|
575
|
+
.aiwg-mode-tab.aiwg-active {
|
|
576
|
+
background: #fff;
|
|
577
|
+
color: #6366f1;
|
|
578
|
+
font-weight: 700;
|
|
579
|
+
box-shadow: 0 1px 4px rgba(0,0,0,0.08);
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
/* Scrollable body */
|
|
583
|
+
.aiwg-offline-body {
|
|
584
|
+
flex: 1;
|
|
585
|
+
overflow-y: auto;
|
|
586
|
+
padding: 14px 16px 16px;
|
|
587
|
+
display: flex;
|
|
588
|
+
flex-direction: column;
|
|
589
|
+
gap: 12px;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
/* ── Offline Form ── */
|
|
593
|
+
.aiwg-offline-form { display: flex; flex-direction: column; gap: 10px; }
|
|
594
|
+
.aiwg-offline-field { display: flex; flex-direction: column; gap: 4px; }
|
|
595
|
+
.aiwg-offline-label { font-size: 12px; font-weight: 600; color: #475569; }
|
|
596
|
+
.aiwg-offline-label .aiwg-required { color: #ef4444; }
|
|
597
|
+
.aiwg-offline-control,
|
|
598
|
+
.aiwg-offline-textarea {
|
|
599
|
+
padding: 9px 12px;
|
|
600
|
+
border: 1.5px solid #e2e8f0;
|
|
601
|
+
border-radius: 8px;
|
|
602
|
+
font-size: 13px;
|
|
603
|
+
font-family: inherit;
|
|
604
|
+
outline: none;
|
|
605
|
+
background: #f8fafc;
|
|
606
|
+
color: #1a1a2e;
|
|
607
|
+
transition: border-color 0.15s, box-shadow 0.15s, background 0.15s;
|
|
608
|
+
width: 100%;
|
|
609
|
+
box-sizing: border-box;
|
|
610
|
+
}
|
|
611
|
+
.aiwg-offline-control:focus,
|
|
612
|
+
.aiwg-offline-textarea:focus {
|
|
613
|
+
border-color: #6366f1;
|
|
614
|
+
box-shadow: 0 0 0 3px rgba(99,102,241,0.1);
|
|
615
|
+
background: #fff;
|
|
616
|
+
}
|
|
617
|
+
.aiwg-offline-textarea { resize: none; min-height: 75px; }
|
|
618
|
+
.aiwg-offline-submit {
|
|
619
|
+
padding: 11px;
|
|
620
|
+
background: linear-gradient(135deg, #6366f1, #8b5cf6);
|
|
621
|
+
color: #fff;
|
|
622
|
+
border: none;
|
|
623
|
+
border-radius: 10px;
|
|
624
|
+
font-size: 13px;
|
|
625
|
+
font-weight: 700;
|
|
626
|
+
cursor: pointer;
|
|
627
|
+
transition: transform 0.15s, box-shadow 0.15s;
|
|
628
|
+
box-shadow: 0 4px 12px rgba(99,102,241,0.3);
|
|
629
|
+
letter-spacing: 0.01em;
|
|
630
|
+
}
|
|
631
|
+
.aiwg-offline-submit:hover:not(:disabled) { transform: translateY(-1px); box-shadow: 0 6px 16px rgba(99,102,241,0.4); }
|
|
632
|
+
.aiwg-offline-submit:active:not(:disabled) { transform: translateY(0); }
|
|
633
|
+
.aiwg-offline-submit:disabled { opacity: 0.55; cursor: not-allowed; transform: none; box-shadow: none; }
|
|
634
|
+
|
|
635
|
+
/* Success state */
|
|
636
|
+
.aiwg-offline-success {
|
|
637
|
+
text-align: center;
|
|
638
|
+
padding: 20px 12px;
|
|
639
|
+
background: #f0fdf4;
|
|
640
|
+
border: 1px solid #86efac;
|
|
641
|
+
border-radius: 12px;
|
|
642
|
+
animation: aiwg-fade-up 0.3s ease both;
|
|
643
|
+
}
|
|
644
|
+
.aiwg-offline-success-icon { font-size: 36px; margin-bottom: 8px; }
|
|
645
|
+
.aiwg-offline-success h4 { font-size: 15px; font-weight: 700; color: #166534; margin-bottom: 4px; }
|
|
646
|
+
.aiwg-offline-success p { font-size: 12px; color: #4ade80; line-height: 1.5; }
|
|
647
|
+
|
|
648
|
+
/* ── Booking card ── */
|
|
649
|
+
.aiwg-booking-card {
|
|
650
|
+
background: linear-gradient(135deg, #f5f3ff, #ede9fe);
|
|
651
|
+
border: 1px solid #ddd6fe;
|
|
652
|
+
border-radius: 14px;
|
|
653
|
+
padding: 20px 16px;
|
|
654
|
+
text-align: center;
|
|
655
|
+
display: flex;
|
|
656
|
+
flex-direction: column;
|
|
657
|
+
align-items: center;
|
|
658
|
+
gap: 10px;
|
|
659
|
+
}
|
|
660
|
+
.aiwg-booking-icon { font-size: 40px; line-height: 1.1; }
|
|
661
|
+
.aiwg-booking-title { font-size: 15px; font-weight: 700; color: #4c1d95; }
|
|
662
|
+
.aiwg-booking-desc { font-size: 12px; color: #6d28d9; line-height: 1.5; }
|
|
663
|
+
.aiwg-booking-btn {
|
|
664
|
+
display: inline-flex;
|
|
665
|
+
align-items: center;
|
|
666
|
+
gap: 6px;
|
|
667
|
+
padding: 10px 22px;
|
|
668
|
+
background: linear-gradient(135deg, #6366f1, #8b5cf6);
|
|
669
|
+
color: #fff;
|
|
670
|
+
border: none;
|
|
671
|
+
border-radius: 99px;
|
|
672
|
+
font-size: 13px;
|
|
673
|
+
font-weight: 700;
|
|
674
|
+
cursor: pointer;
|
|
675
|
+
transition: transform 0.15s, box-shadow 0.15s;
|
|
676
|
+
box-shadow: 0 4px 14px rgba(99,102,241,0.35);
|
|
677
|
+
text-decoration: none;
|
|
678
|
+
letter-spacing: 0.01em;
|
|
679
|
+
}
|
|
680
|
+
.aiwg-booking-btn:hover { transform: translateY(-2px); box-shadow: 0 8px 20px rgba(99,102,241,0.45); }
|
|
681
|
+
.aiwg-booking-btn:active { transform: translateY(0); }
|
|
682
|
+
|
|
683
|
+
/* ── Shared fade-up used by banner + success ── */
|
|
684
|
+
@keyframes aiwg-fade-up {
|
|
685
|
+
from { opacity: 0; transform: translateY(8px); }
|
|
686
|
+
to { opacity: 1; transform: translateY(0); }
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
/* ═══════════════════════════════════════════════════════════════════════════
|
|
690
|
+
DARK THEME — overrides for new elements
|
|
691
|
+
═══════════════════════════════════════════════════════════════════════════ */
|
|
692
|
+
|
|
693
|
+
.aiwg-dark .aiwg-stream-cursor { background: #a78bfa; }
|
|
694
|
+
.aiwg-dark .aiwg-msg-bubble--streaming { background: linear-gradient(135deg, #1e293b, #0f172a) !important; border-color: #334155 !important; color: #e2e8f0; }
|
|
695
|
+
.aiwg-dark .aiwg-limit-banner { background: linear-gradient(135deg, #292202, #261e01); border-color: #a16207; }
|
|
696
|
+
.aiwg-dark .aiwg-limit-banner-title { color: #fde68a; }
|
|
697
|
+
.aiwg-dark .aiwg-limit-banner-text { color: #fcd34d; }
|
|
698
|
+
.aiwg-dark .aiwg-limit-btn--secondary { background: rgba(139,92,246,0.15); color: #a78bfa; border-color: rgba(139,92,246,0.3); }
|
|
699
|
+
.aiwg-dark .aiwg-offline-overlay { background: #0f172a; }
|
|
700
|
+
.aiwg-dark .aiwg-offline-header-left h4 { color: #e2e8f0; }
|
|
701
|
+
.aiwg-dark .aiwg-offline-header-left p { color: #64748b; }
|
|
702
|
+
.aiwg-dark .aiwg-offline-close-btn { background: #1e293b; color: #94a3b8; }
|
|
703
|
+
.aiwg-dark .aiwg-offline-close-btn:hover { background: #334155; }
|
|
704
|
+
.aiwg-dark .aiwg-mode-tabs { background: #1e293b; }
|
|
705
|
+
.aiwg-dark .aiwg-mode-tab { color: #94a3b8; }
|
|
706
|
+
.aiwg-dark .aiwg-mode-tab.aiwg-active { background: #0f172a; color: #a78bfa; }
|
|
707
|
+
.aiwg-dark .aiwg-offline-label { color: #94a3b8; }
|
|
708
|
+
.aiwg-dark .aiwg-offline-control,
|
|
709
|
+
.aiwg-dark .aiwg-offline-textarea { background: #1e293b; border-color: #334155; color: #e2e8f0; }
|
|
710
|
+
.aiwg-dark .aiwg-offline-control:focus,
|
|
711
|
+
.aiwg-dark .aiwg-offline-textarea:focus { border-color: #8b5cf6; box-shadow: 0 0 0 3px rgba(139,92,246,0.2); background: #1e293b; }
|
|
712
|
+
.aiwg-dark .aiwg-booking-card { background: linear-gradient(135deg, #2d1b69, #1a0f3c); border-color: #4c1d95; }
|
|
713
|
+
.aiwg-dark .aiwg-booking-title { color: #c4b5fd; }
|
|
714
|
+
.aiwg-dark .aiwg-booking-desc { color: #a78bfa; }
|
|
715
|
+
.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); }
|
|
389
716
|
`;
|