@hachther/akilio-widget 0.5.0

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/README.md ADDED
@@ -0,0 +1,301 @@
1
+ # Akilio Widget
2
+
3
+ Production React and standalone JavaScript widget for the Akilio backend.
4
+
5
+ ## Included modes
6
+
7
+ - `floating`: compact chat popover anchored above the floating launcher.
8
+ - `side-panel`: persistent desktop side panel with a mobile drawer fallback.
9
+ - `inline`: renders inside the supplied container.
10
+ - `hybrid`: backward-compatible alias for side panel on desktop and drawer on mobile.
11
+ - `drawer`: full-height overlay drawer opened from the floating launcher.
12
+
13
+ ## Build
14
+
15
+ ```bash
16
+ npm install
17
+ npm run typecheck
18
+ npm run build
19
+ ```
20
+
21
+ Production files:
22
+
23
+ ```text
24
+ dist/index.min.js
25
+ dist/index.umd.min.cjs
26
+ dist/index.d.ts
27
+ dist/style.css
28
+ dist/akilio-widget.min.js
29
+ ```
30
+
31
+ ## React
32
+
33
+ ```tsx
34
+ import { AkilioWidget } from '@hachther/akilio-widget';
35
+ import '@hachther/akilio-widget/style.css';
36
+
37
+ <AkilioWidget
38
+ apiBaseUrl='https://api.akilio-ai.com'
39
+ projectKey='ask_pk_xxx'
40
+ mode='floating'
41
+ colorMode='system'
42
+ auth={{
43
+ getAccessToken: async () => sessionStorage.getItem('akilio_access_token'),
44
+ }}
45
+ requireConsent
46
+ privacyNotice='Questions are processed to provide an AI-generated answer.'
47
+ privacyNoticeUrl='https://example.com/privacy'
48
+ />;
49
+ ```
50
+
51
+ ### Custom launcher and controlled state
52
+
53
+ Hide the built-in launcher and control the floating popup from any host element,
54
+ such as a header navigation button:
55
+
56
+ ```tsx
57
+ const [chatOpen, setChatOpen] = useState(false);
58
+
59
+ <button onClick={() => setChatOpen(true)}>Ask Akilio</button>
60
+ <AkilioWidget
61
+ apiBaseUrl='https://api.akilio-ai.com'
62
+ projectKey='ask_pk_xxx'
63
+ mode='floating'
64
+ launcher='hidden'
65
+ open={chatOpen}
66
+ onOpenChange={setChatOpen}
67
+ />
68
+ ```
69
+
70
+ Or supply a custom React launcher:
71
+
72
+ ```tsx
73
+ <AkilioWidget
74
+ apiBaseUrl='https://api.akilio-ai.com'
75
+ projectKey='ask_pk_xxx'
76
+ mode='floating'
77
+ launcher={({ open, toggleChat }) => (
78
+ <button className='header-assistant' onClick={toggleChat}>
79
+ {open ? 'Close assistant' : 'Ask Akilio'}
80
+ </button>
81
+ )}
82
+ />
83
+ ```
84
+
85
+ Authentication supports:
86
+
87
+ ```tsx
88
+ auth={{ accessToken: 'short-lived-jwt' }}
89
+ auth={{ getAccessToken: refreshAndReturnToken }}
90
+ auth={{ apiKey: 'widget-key', apiKeyHeader: 'X-Akilio-Key' }}
91
+ auth={{ credentials: 'include' }}
92
+ ```
93
+
94
+ The token resolver is evaluated for every request, so a host application can rotate short-lived backend tokens without remounting the widget.
95
+
96
+ ## Standalone script
97
+
98
+ ```html
99
+ <script
100
+ data-akilio-widget
101
+ src="/assets/akilio-widget.min.js"
102
+ data-api-base-url="https://api.akilio-ai.com"
103
+ data-project-key="ask_pk_xxx"
104
+ data-mode="floating"
105
+ data-color-mode="system"
106
+ data-access-token="short-lived-widget-token"
107
+ data-require-consent="true"
108
+ data-privacy-notice="Questions are processed to provide an AI-generated answer."
109
+ data-privacy-notice-url="https://example.com/privacy"
110
+ ></script>
111
+ ```
112
+
113
+ For an inline widget:
114
+
115
+ ```html
116
+ <div id="support-assistant" style="height: 680px"></div>
117
+ <script src="/assets/akilio-widget.min.js" data-auto-init="false"></script>
118
+ <script>
119
+ window.Akilio.mount({
120
+ target: '#support-assistant',
121
+ apiBaseUrl: 'https://api.akilio-ai.com',
122
+ projectKey: 'ask_pk_xxx',
123
+ mode: 'inline',
124
+ colorMode: 'system',
125
+ accessToken: 'short-lived-widget-token',
126
+ });
127
+ </script>
128
+ ```
129
+
130
+ The standalone API can hide the launcher and control the chat imperatively:
131
+
132
+ ```js
133
+ const widget = window.Akilio.mount({
134
+ target: '#akilio-root',
135
+ apiBaseUrl: 'https://api.akilio-ai.com',
136
+ projectKey: 'ask_pk_xxx',
137
+ mode: 'floating',
138
+ launcher: 'hidden',
139
+ });
140
+
141
+ document.querySelector('#header-chat').addEventListener('click', widget.toggle);
142
+ // widget.open(); widget.close(); widget.destroy();
143
+ ```
144
+
145
+ Script-tag configuration also accepts `data-launcher="hidden"`.
146
+
147
+ Supported authentication attributes are `data-access-token`, `data-widget-token`, `data-api-key`, and `data-api-key-header`.
148
+
149
+ ## Backend contract
150
+
151
+ ```text
152
+ GET /api/v1/projects/{project_key}/config/
153
+ POST /api/v1/chat/
154
+ GET /api/v1/conversations/{conversation_id}/?project_key=...
155
+ POST /api/v1/messages/{message_id}/feedback/
156
+ ```
157
+
158
+ Every request uses the configured authentication headers. Chat requests also include consent metadata when available.
159
+
160
+ ## Rendering performance
161
+
162
+ The composer maintains its own local input state. Typing therefore does not update the widget root or re-render the message history. Message bubbles and Markdown answers are memoized, and automatic scrolling depends on message count rather than text input changes.
163
+
164
+ Local history retains the latest 60 messages. Server history is restored from the persisted conversation ID when the local message cache is empty.
165
+
166
+ ## Manual light/dark switch
167
+
168
+ The widget displays an accessible light/dark toggle in its header by default. The visitor's choice is persisted per project in local storage. When no manual choice exists, `colorMode="system"` follows the operating-system preference.
169
+
170
+ ```tsx
171
+ <AkilioWidget
172
+ apiBaseUrl='https://api.example.com'
173
+ projectKey='project-key'
174
+ colorMode='system'
175
+ allowColorModeToggle
176
+ />
177
+ ```
178
+
179
+ For the standalone build, use `data-allow-color-mode-toggle="true"`. Set the React prop or data attribute to `false` to hide the control.
180
+
181
+ ## Widget authentication flow
182
+
183
+ By default, the widget now implements the Akilio Public API bootstrap flow automatically:
184
+
185
+ 1. Load public configuration from `GET /api/v1/projects/{public_key}/config/`.
186
+ 2. Create or reuse a stable browser subject.
187
+ 3. Request a short-lived token from `POST /api/v1/projects/{public_key}/widget-token/`.
188
+ 4. Send `Authorization: Bearer <token>` to chat, conversation, and feedback endpoints.
189
+ 5. Refresh the token before `expires_in` elapses, and retry once after an HTTP 401.
190
+
191
+ No token configuration is required for a normal public widget:
192
+
193
+ ```tsx
194
+ <AkilioWidget
195
+ apiBaseUrl='https://api.akilio-ai.com'
196
+ projectKey='your-public-project-key'
197
+ />
198
+ ```
199
+
200
+ Use a known subject when the host application has one:
201
+
202
+ ```tsx
203
+ <AkilioWidget
204
+ apiBaseUrl='https://api.akilio-ai.com'
205
+ projectKey='your-public-project-key'
206
+ externalUserId='customer-123'
207
+ />
208
+ ```
209
+
210
+ Host-managed tokens remain supported and take precedence over automatic issuance:
211
+
212
+ ```tsx
213
+ <AkilioWidget
214
+ apiBaseUrl='https://api.akilio-ai.com'
215
+ projectKey='your-public-project-key'
216
+ auth={{ getAccessToken: refreshAndReturnToken }}
217
+ />
218
+ ```
219
+
220
+ Advanced token controls:
221
+
222
+ ```tsx
223
+ <AkilioWidget
224
+ apiBaseUrl='https://api.akilio-ai.com'
225
+ projectKey='your-public-project-key'
226
+ auth={{
227
+ autoIssueWidgetToken: true,
228
+ tokenRefreshLeewaySeconds: 30,
229
+ onWidgetToken: ({ expiresAt }) =>
230
+ console.debug('Akilio token expires at', expiresAt),
231
+ }}
232
+ />
233
+ ```
234
+
235
+ ## Next.js (App Router)
236
+
237
+ Render the widget from a Client Component because it uses browser functionality after hydration:
238
+
239
+ ```tsx
240
+ 'use client';
241
+
242
+ import { AkilioWidget } from '@hachther/akilio-widget';
243
+ import '@hachther/akilio-widget/style.css';
244
+
245
+ export function AkilioAssistant() {
246
+ return (
247
+ <AkilioWidget
248
+ apiBaseUrl={process.env.NEXT_PUBLIC_AKILIO_API_URL!}
249
+ projectKey={process.env.NEXT_PUBLIC_AKILIO_PROJECT_KEY!}
250
+ />
251
+ );
252
+ }
253
+ ```
254
+
255
+ The package itself is now safe to import in an SSR module. The component must still be rendered beneath a `use client` boundary. A dynamic import with `ssr: false` is optional, not required.
256
+
257
+ ## Ecommerce responses
258
+
259
+ When the chat API returns a `commerce` capability, the widget automatically displays its products below the assistant answer. No additional configuration is required.
260
+
261
+ ```tsx
262
+ <AkilioWidget
263
+ apiBaseUrl="https://api.example.com"
264
+ projectKey="public-project-key"
265
+ onProductClick={(product, variant) => {
266
+ console.log('Product selected', product, variant);
267
+ }}
268
+ />
269
+ ```
270
+
271
+ Product links open in a new tab. The widget also sends an authenticated commerce event to `/api/v1/commerce/events/` using the response capability (`search` or `recommendation`).
272
+
273
+ ## Assistant mode
274
+
275
+ Use `assistantMode` to pass the optional `assistant_mode` routing hint to the chat endpoint:
276
+
277
+ ```tsx
278
+ <AkilioWidget
279
+ apiBaseUrl="https://api.example.com"
280
+ projectKey="public-project-key"
281
+ assistantMode="ecommerce"
282
+ />
283
+ ```
284
+
285
+ Supported values are `auto`, `general`, `knowledge`, `ecommerce`, and `hybrid`. The project configuration remains authoritative; this option only narrows behavior permitted by the backend.
286
+
287
+ For the standalone build:
288
+
289
+ ```html
290
+ <script
291
+ src="/akilio-widget.min.js"
292
+ data-akilio-widget
293
+ data-api-base-url="https://api.example.com"
294
+ data-project-key="public-project-key"
295
+ data-assistant-mode="ecommerce"
296
+ ></script>
297
+ ```
298
+
299
+ ## Follow-up suggestions
300
+
301
+ When `/api/v1/chat/` returns a `suggestions` array, the widget displays accessible suggestion chips under that assistant response. Selecting a chip sends it as the next user message. Suggestions are retained in local history and restored from server conversation message metadata or structured content when supplied by the API.
@@ -0,0 +1,4 @@
1
+ import 'driver.js/dist/driver.css';
2
+ import './styles.css';
3
+ import type { AkilioWidgetProps } from './types';
4
+ export declare function AkilioWidget({ apiBaseUrl, projectKey, locale, mode, colorMode, allowColorModeToggle, defaultOpen, open: controlledOpen, launcher, persistOpenState, enableTour, enableLocalHistory, enableServerHistorySync, anonymousId, externalUserId, channel, audience, productArea, assistantMode, pageContext, auth, accessToken, privacyNotice, privacyNoticeUrl, requireConsent, consentNoticeVersion, initialConsent, onConsentChange, copy: copyOverrides, theme, className, onOpenChange, onMessage, onProductClick, onError, }: AkilioWidgetProps): import("react").JSX.Element;
@@ -0,0 +1 @@
1
+ .driver-popover{all:unset;font-family:var(--driver-popover-font-family,"Helvetica Neue", Inter, ui-sans-serif, "Apple Color Emoji", Helvetica, Arial, sans-serif);box-sizing:border-box;color:#2d2d2d;z-index:1000000000;background-color:#fff;border-radius:5px;min-width:250px;max-width:300px;margin:0;padding:15px;position:fixed;top:0;right:0;box-shadow:0 1px 10px #0006}.driver-popover-title{zoom:1;margin:0;font-size:19px;font-weight:700;line-height:1.5;display:block;position:relative}.driver-popover-close-btn{all:unset;cursor:pointer;color:#d2d2d2;z-index:1;text-align:center;width:32px;height:28px;font-size:18px;font-weight:500;transition:color .2s;position:absolute;top:0;right:0}.driver-popover-close-btn:hover,.driver-popover-close-btn:focus{color:#2d2d2d}.driver-popover-title[style*=block]+.driver-popover-description{margin-top:5px}.driver-popover-description{zoom:1;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.5}.driver-popover-footer{text-align:right;zoom:1;justify-content:space-between;align-items:center;margin-top:15px;display:flex}.driver-popover-progress-text{color:#727272;zoom:1;font-size:13px;font-weight:400}.driver-popover-footer-btn{all:unset;box-sizing:border-box;color:#2d2d2d;cursor:pointer;zoom:1;background-color:#fff;border:1px solid #ccc;border-radius:3px;outline:0;padding:3px 7px;font-size:12px;line-height:1.3;text-decoration:none;display:inline-block}.driver-popover-footer .driver-popover-btn-disabled{opacity:.5;pointer-events:none}.driver-popover-footer-btn:hover,.driver-popover-footer-btn:focus{background-color:#f7f7f7}.driver-popover-navigation-btns{flex-grow:1;justify-content:flex-end;display:flex}.driver-popover-navigation-btns button+button{margin-left:4px}.driver-popover-arrow{content:"";border:5px solid #fff;position:absolute}.driver-popover-arrow-side-over{display:none}.driver-popover-arrow-side-left{border-top-color:#0000;border-bottom-color:#0000;border-right-color:#0000;left:100%}.driver-popover-arrow-side-right{border-top-color:#0000;border-bottom-color:#0000;border-left-color:#0000;right:100%}.driver-popover-arrow-side-top{border-bottom-color:#0000;border-left-color:#0000;border-right-color:#0000;top:100%}.driver-popover-arrow-side-bottom{border-top-color:#0000;border-left-color:#0000;border-right-color:#0000;bottom:100%}.driver-popover-arrow-side-center,.driver-popover-arrow-none{display:none}.driver-active .driver-overlay{pointer-events:none}.driver-active.driver-no-scroll{overflow:hidden}.driver-active *{pointer-events:none}.driver-active .driver-active-element,.driver-active .driver-active-element *,.driver-popover,.driver-popover *{pointer-events:auto}@keyframes animate-fade-in{0%{opacity:0}to{opacity:1}}.driver-fade .driver-overlay{animation:animate-fade-in var(--driver-animation-duration,.4s) ease-in-out}.driver-fade .driver-popover{animation:animate-fade-in var(--driver-animation-duration,.4s)}.driver-active-element-parent-no-scroll{overflow:hidden!important}.driver-no-interaction,.driver-no-interaction *{pointer-events:none!important}:root{--akilio-primary: #0891b2;--akilio-primary-foreground: #ffffff;--akilio-accent: #2563eb;--akilio-radius: 22px;--akilio-panel-width: 440px;--akilio-z-index: 2147483000}@media(min-width:1280px){html.akilio-panel-open body{padding-right:var(--akilio-panel-width);transition:padding-right .26s ease}html.akilio-panel-open #akilio-launcher{transform:translate(calc(-1 * var(--akilio-panel-width)))}}.akilio-root,.akilio-root *{box-sizing:border-box}.akilio-root{color:#18181b;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}.akilio-launcher{position:fixed;right:22px;bottom:22px;z-index:calc(var(--akilio-z-index) - 10);display:inline-flex;align-items:center;gap:10px;border:1px solid rgba(255,255,255,.22);border-radius:999px;background:linear-gradient(135deg,#09090b,#27272a);color:#fff;padding:11px 16px 11px 12px;cursor:pointer;box-shadow:0 24px 60px #18181b40,0 0 0 1px #09090b14;transition:transform .26s ease,box-shadow .18s ease,background .18s ease}.akilio-launcher:hover{transform:translateY(-2px);box-shadow:0 28px 70px #0891b240,0 0 0 1px #09090b14}.akilio-launcher span{display:grid;width:30px;height:30px;place-items:center;overflow:hidden;border-radius:999px;background:#fff}.akilio-launcher-logo{display:block;width:22px;height:22px;object-fit:contain}.akilio-launcher strong{font-size:14px;font-weight:750;letter-spacing:-.01em}.akilio-overlay{position:fixed;inset:0;z-index:calc(var(--akilio-z-index) - 2);border:0;background:#09090b75;-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px);cursor:default}.akilio-panel{position:fixed;top:0;right:0;z-index:var(--akilio-z-index);display:flex;width:min(100vw,var(--akilio-panel-width));height:100dvh;transform:translate(103%);flex-direction:column;overflow:hidden;border-left:1px solid rgba(212,212,216,.9);background:#f8fafc;box-shadow:-30px 0 90px #0f172a3d;transition:transform .26s cubic-bezier(.2,.8,.2,1)}.akilio-panel--open{transform:translate(0)}.akilio-root[data-akilio-mode=floating] .akilio-panel{top:auto;right:22px;bottom:88px;width:min(400px,calc(100vw - 32px));height:min(650px,calc(100dvh - 116px));min-height:420px;transform:translateY(18px) scale(.98);transform-origin:right bottom;border:1px solid rgba(212,212,216,.9);border-radius:var(--akilio-radius);box-shadow:0 28px 80px #0f172a3d;opacity:0;visibility:hidden;pointer-events:none;transition:transform .22s cubic-bezier(.2,.8,.2,1),opacity .18s ease,visibility .18s ease}.akilio-root[data-akilio-mode=floating] .akilio-panel--open{transform:translateY(0) scale(1);opacity:1;visibility:visible;pointer-events:auto}.akilio-panel-glow{position:absolute;inset:0 0 auto;height:185px;pointer-events:none;background:radial-gradient(circle at 0% 0%,color-mix(in srgb,var(--akilio-primary) 35%,transparent),transparent 36%),radial-gradient(circle at 100% 0%,rgba(37,99,235,.22),transparent 34%)}.akilio-panel-header{position:relative;display:flex;align-items:flex-start;gap:12px;border-bottom:1px solid rgba(212,212,216,.82);background:#ffffffc7;padding:16px;-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px)}.akilio-logo,.akilio-empty-icon{display:grid;flex-shrink:0;place-items:center}.akilio-logo{width:94px;height:34px}.akilio-logo img{display:block;width:100%;height:100%;object-fit:contain;object-position:left center}.akilio-empty-icon{width:64px;height:64px;border:1px solid rgba(124,58,237,.12);border-radius:calc(var(--akilio-radius) - 4px);background:#ffffffe0;box-shadow:0 18px 42px #6d28d929}.akilio-empty-icon img{display:block;width:42px;height:42px;object-fit:contain}.akilio-header-content{min-width:0;flex:1}.akilio-header-topline{display:flex;min-width:0;align-items:center;justify-content:space-between;gap:10px}.akilio-header-title{display:flex;min-width:0;align-items:center;gap:8px}.akilio-header-title h2{overflow:hidden;margin:0;color:#09090b;font-size:14px;font-weight:800;letter-spacing:-.015em;text-overflow:ellipsis;white-space:nowrap}.akilio-online{display:inline-flex;align-items:center;gap:5px;border-radius:999px;background:#10b9811f;color:#047857;padding:3px 8px;font-size:10px;font-weight:800;white-space:nowrap}.akilio-online i{width:6px;height:6px;border-radius:999px;background:#10b981}.akilio-header-description{display:-webkit-box;margin:6px 0 0;overflow:hidden;color:#71717a;font-size:12px;line-height:1.55;-webkit-box-orient:vertical;-webkit-line-clamp:3}.akilio-header-actions{display:flex;flex-shrink:0;align-items:center;gap:4px}.akilio-header-actions button{display:grid;width:36px;height:36px;place-items:center;border:0;border-radius:999px;background:transparent;color:#71717a;cursor:pointer;transition:background .16s ease,color .16s ease}.akilio-header-actions button:hover{background:#f4f4f5;color:#09090b}.akilio-header-actions svg{width:18px;height:18px}.akilio-thread{position:relative;display:flex;flex:1;flex-direction:column;gap:14px;overflow-y:auto;padding:18px 16px 22px}.akilio-message-row{display:flex;width:100%}.akilio-message-row--user{justify-content:flex-end}.akilio-message-row--assistant{justify-content:flex-start}.akilio-bubble{max-width:88%;overflow:hidden;border:1px solid rgba(212,212,216,.86);border-radius:26px 26px 26px 8px;background:#fffffff0;color:#27272a;padding:13px 15px;box-shadow:0 10px 28px #0f172a12}.akilio-bubble--user{border-color:#ffffff26;border-bottom-right-radius:8px;border-bottom-left-radius:26px;background:linear-gradient(135deg,#09090b,#27272a);color:#fff}.akilio-bubble--error{border-color:#f8717159;background:#fef2f2;color:#991b1b}.akilio-bubble-meta{display:flex;align-items:center;gap:7px;margin-bottom:8px;color:var(--akilio-primary);font-size:10px;font-weight:850;letter-spacing:.15em;text-transform:uppercase}.akilio-mini-icon{width:14px;height:14px}.akilio-confidence{margin-left:auto;border-radius:999px;background:color-mix(in srgb,var(--akilio-primary) 11%,transparent);padding:2px 7px;color:var(--akilio-primary);letter-spacing:0;text-transform:none}.akilio-answer{color:inherit;font-size:14px;line-height:1.7}.akilio-answer p{margin:.5rem 0}.akilio-answer p:first-child{margin-top:0}.akilio-answer p:last-child{margin-bottom:0}.akilio-answer ul,.akilio-answer ol{margin:.625rem 0 .625rem 1.25rem;padding-left:.75rem}.akilio-answer ul{list-style:disc}.akilio-answer ol{list-style:decimal}.akilio-answer li{margin:.25rem 0}.akilio-answer a{color:var(--akilio-primary);font-weight:700;text-decoration:underline;text-underline-offset:3px}.akilio-answer code{border-radius:7px;background:#0f172a14;padding:.12rem .35rem;font-size:.86em}.akilio-answer pre{margin:.75rem 0;overflow-x:auto;border:1px solid rgba(148,163,184,.2);border-radius:16px;background:#020617;padding:13px}.akilio-answer pre code{background:transparent;color:#e2e8f0;padding:0}.akilio-answer .hljs-keyword,.akilio-answer .hljs-selector-tag,.akilio-answer .hljs-built_in{color:#f0abfc}.akilio-answer .hljs-string,.akilio-answer .hljs-attr,.akilio-answer .hljs-symbol{color:#86efac}.akilio-answer .hljs-number,.akilio-answer .hljs-literal{color:#fcd34d}.akilio-answer .hljs-comment{color:#64748b;font-style:italic}.akilio-answer .hljs-title,.akilio-answer .hljs-function{color:#7dd3fc}.akilio-answer .hljs-variable{color:#fdba74}.akilio-answer--user{white-space:pre-wrap}.akilio-answer--user code{background:#ffffff29}.akilio-sources{margin-top:11px;overflow:hidden;border:1px solid rgba(212,212,216,.85);border-radius:16px;background:#f4f4f5c7}.akilio-sources summary{cursor:pointer;padding:9px 11px;color:#52525b;font-size:12px;font-weight:800}.akilio-sources-list{display:grid;gap:8px;border-top:1px solid rgba(212,212,216,.85);padding:8px}.akilio-source-card{display:grid;gap:3px;border:1px solid rgba(212,212,216,.82);border-radius:13px;background:#fff;padding:8px;font-size:12px}.akilio-source-card strong{color:#18181b}.akilio-source-card span{color:#71717a}.akilio-source-card a{overflow:hidden;color:var(--akilio-primary);text-overflow:ellipsis;white-space:nowrap}.akilio-feedback{display:flex;align-items:center;gap:7px;margin-top:11px;color:#71717a;font-size:11px}.akilio-feedback span{margin-right:auto}.akilio-feedback button{display:grid;width:28px;height:28px;place-items:center;border:1px solid rgba(212,212,216,.9);border-radius:999px;background:#fff;color:#71717a;cursor:pointer}.akilio-feedback button:hover,.akilio-feedback .akilio-feedback-active{border-color:color-mix(in srgb,var(--akilio-primary) 35%,#d4d4d8);color:var(--akilio-primary)}.akilio-feedback svg{width:14px;height:14px}.akilio-dots{display:flex;gap:6px;padding:2px 0}.akilio-dots span{width:8px;height:8px;border-radius:999px;background:var(--akilio-primary);animation:akilio-bounce .9s infinite ease-in-out}.akilio-dots span:nth-child(2){animation-delay:.1s}.akilio-dots span:nth-child(3){animation-delay:.2s}@keyframes akilio-bounce{0%,80%,to{transform:translateY(0);opacity:.45}40%{transform:translateY(-4px);opacity:1}}.akilio-empty{margin:auto;display:flex;max-width:340px;flex-direction:column;align-items:center;padding:34px 0;text-align:center}.akilio-empty-icon{width:58px;height:58px;margin-bottom:16px}.akilio-empty h3{margin:0;color:#09090b;font-size:17px;font-weight:850;letter-spacing:-.02em}.akilio-empty p{margin:8px 0 0;color:#71717a;font-size:14px;line-height:1.65}.akilio-suggestions{display:grid;width:100%;gap:9px;margin-top:18px}.akilio-suggestions button{border:1px solid rgba(212,212,216,.9);border-radius:16px;background:#fff;color:#3f3f46;cursor:pointer;padding:10px 12px;text-align:left;font-size:12px;font-weight:700;box-shadow:0 5px 14px #0f172a0a;transition:transform .15s ease,border-color .15s ease,box-shadow .15s ease}.akilio-suggestions button:hover{transform:translateY(-1px);border-color:color-mix(in srgb,var(--akilio-primary) 35%,#d4d4d8);box-shadow:0 12px 22px #0f172a14}.akilio-composer{position:relative;border-top:1px solid rgba(212,212,216,.85);background:#ffffffdb;padding:12px;-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px)}.akilio-composer-box{border:1px solid rgba(212,212,216,.95);border-radius:24px;background:#f8fafc;padding:8px;box-shadow:inset 0 1px 2px #0f172a0a;transition:border-color .15s ease,background .15s ease,box-shadow .15s ease}.akilio-composer-box:focus-within{border-color:color-mix(in srgb,var(--akilio-primary) 55%,#d4d4d8);background:#fff;box-shadow:0 0 0 4px color-mix(in srgb,var(--akilio-primary) 13%,transparent),inset 0 1px 2px #0f172a08}.akilio-composer textarea{width:100%;min-height:48px;max-height:148px;resize:none;border:0;outline:0;background:transparent;color:#09090b;padding:8px 10px;font:inherit;font-size:14px;line-height:1.55}.akilio-composer textarea::placeholder{color:#a1a1aa}.akilio-composer-footer{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:0 2px 2px 7px}.akilio-composer-footer span{color:#71717a;font-size:11px}.akilio-composer-footer button{display:inline-flex;align-items:center;gap:8px;border:0;border-radius:999px;background:#09090b;color:#fff;cursor:pointer;padding:10px 15px;font-size:12px;font-weight:850;box-shadow:0 10px 24px #0f172a24;transition:transform .15s ease,opacity .15s ease}.akilio-composer-footer button:hover:not(:disabled){transform:translateY(-1px)}.akilio-composer-footer button:disabled{cursor:not-allowed;opacity:.45}.akilio-composer-footer svg{width:15px;height:15px}.akilio-tour{max-width:360px;border-radius:18px!important}@media(max-width:640px){.akilio-launcher{right:16px;bottom:16px;padding:10px 12px}.akilio-launcher strong{display:none}.akilio-panel{width:100vw}.akilio-bubble{max-width:94%}.akilio-composer-footer span{display:none}}.akilio-root[data-akilio-mode=inline]{width:100%;height:100%;min-height:520px}.akilio-panel--inline{position:relative;inset:auto;width:100%;height:100%;min-height:520px;transform:none;border:1px solid rgba(212,212,216,.9);border-radius:var(--akilio-radius);box-shadow:0 18px 50px #0f172a1f}.akilio-privacy{display:grid;gap:8px;border-top:1px solid rgba(212,212,216,.82);background:#ffffffe6;padding:10px 14px;font-size:11px;line-height:1.45}.akilio-privacy strong{color:#18181b}.akilio-privacy p{margin:2px 0 0;color:#71717a}.akilio-privacy a{color:var(--akilio-primary);font-weight:700}.akilio-privacy label{display:flex;align-items:flex-start;gap:8px;color:#3f3f46;cursor:pointer}.akilio-privacy input{margin-top:2px;accent-color:var(--akilio-primary)}.akilio-composer textarea:disabled{cursor:not-allowed;opacity:.65}.akilio-root button:focus-visible,.akilio-root a:focus-visible,.akilio-root input:focus-visible{outline:3px solid color-mix(in srgb,var(--akilio-primary) 45%,transparent);outline-offset:2px}.akilio-root .akilio-composer textarea:focus,.akilio-root .akilio-composer textarea:focus-visible{border:0;outline:none;box-shadow:none}@media(prefers-reduced-motion:reduce){.akilio-root *,.akilio-root *:before,.akilio-root *:after{scroll-behavior:auto!important;animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}.akilio-root[data-akilio-color-mode=dark]{color:#e4e4e7}.akilio-root[data-akilio-color-mode=dark] .akilio-panel,.akilio-root[data-akilio-color-mode=dark] .akilio-panel--inline{background:var(--akilio-surface, #09090b);border-color:#27272a}.akilio-root[data-akilio-color-mode=dark] .akilio-panel-header,.akilio-root[data-akilio-color-mode=dark] .akilio-privacy{background:#18181beb;border-color:#3f3f46}.akilio-root[data-akilio-color-mode=dark] .akilio-header-title h2,.akilio-root[data-akilio-color-mode=dark] .akilio-empty h3,.akilio-root[data-akilio-color-mode=dark] .akilio-privacy strong{color:var(--akilio-text, #fafafa)}.akilio-root[data-akilio-color-mode=dark] .akilio-header-description,.akilio-root[data-akilio-color-mode=dark] .akilio-empty p,.akilio-root[data-akilio-color-mode=dark] .akilio-privacy p,.akilio-root[data-akilio-color-mode=dark] .akilio-privacy label{color:var(--akilio-muted, #a1a1aa)}.akilio-root[data-akilio-color-mode=dark] .akilio-bubble{background:#18181b;border-color:#3f3f46;color:#e4e4e7;box-shadow:none}.akilio-root[data-akilio-color-mode=dark] .akilio-bubble--user{background:linear-gradient(135deg,var(--akilio-primary),var(--akilio-accent));color:var(--akilio-primary-foreground)}.akilio-root[data-akilio-color-mode=dark] .akilio-sources{background:#09090b;border-color:#3f3f46}.akilio-root[data-akilio-color-mode=dark] .akilio-sources-list{border-color:#3f3f46}.akilio-root[data-akilio-color-mode=dark] .akilio-source-card,.akilio-root[data-akilio-color-mode=dark] .akilio-feedback button,.akilio-root[data-akilio-color-mode=dark] .akilio-composer-box{background:#18181b;border-color:#3f3f46;color:#e4e4e7}.akilio-root[data-akilio-color-mode=dark] .akilio-source-card strong{color:#fafafa}.akilio-root[data-akilio-color-mode=dark] .akilio-source-card span,.akilio-root[data-akilio-color-mode=dark] .akilio-feedback{color:#a1a1aa}@media(prefers-color-scheme:dark){.akilio-root[data-akilio-color-mode=system] .akilio-panel,.akilio-root[data-akilio-color-mode=system] .akilio-panel--inline{background:#09090b;border-color:#27272a}.akilio-root[data-akilio-color-mode=system] .akilio-panel-header,.akilio-root[data-akilio-color-mode=system] .akilio-privacy{background:#18181beb;border-color:#3f3f46}.akilio-root[data-akilio-color-mode=system] .akilio-header-title h2,.akilio-root[data-akilio-color-mode=system] .akilio-empty h3,.akilio-root[data-akilio-color-mode=system] .akilio-privacy strong{color:#fafafa}.akilio-root[data-akilio-color-mode=system] .akilio-header-description,.akilio-root[data-akilio-color-mode=system] .akilio-empty p,.akilio-root[data-akilio-color-mode=system] .akilio-privacy p,.akilio-root[data-akilio-color-mode=system] .akilio-privacy label{color:#a1a1aa}.akilio-root[data-akilio-color-mode=system] .akilio-bubble{background:#18181b;border-color:#3f3f46;color:#e4e4e7;box-shadow:none}.akilio-root[data-akilio-color-mode=system] .akilio-bubble--user{background:linear-gradient(135deg,var(--akilio-primary),var(--akilio-accent));color:var(--akilio-primary-foreground)}.akilio-root[data-akilio-color-mode=system] .akilio-source-card,.akilio-root[data-akilio-color-mode=system] .akilio-feedback button,.akilio-root[data-akilio-color-mode=system] .akilio-composer-box{background:#18181b;border-color:#3f3f46;color:#e4e4e7}}@media(max-width:640px){.akilio-launcher{right:14px;bottom:max(14px,env(safe-area-inset-bottom));padding:10px}.akilio-launcher strong{display:none}.akilio-panel{width:100vw;height:100dvh;border-left:0}.akilio-root[data-akilio-mode=floating] .akilio-panel{right:14px;bottom:calc(76px + env(safe-area-inset-bottom));width:calc(100vw - 28px);height:min(640px,calc(100dvh - 104px - env(safe-area-inset-bottom)));min-height:min(420px,calc(100dvh - 104px));border:1px solid rgba(212,212,216,.9);border-radius:18px}.akilio-panel-header{padding-top:max(14px,env(safe-area-inset-top))}.akilio-thread{padding-inline:12px}.akilio-bubble{max-width:94%}.akilio-composer{padding-bottom:max(10px,env(safe-area-inset-bottom))}.akilio-root[data-akilio-mode=inline]{min-height:480px}.akilio-panel--inline{min-height:480px;border-radius:16px}}.akilio-root[data-akilio-color-mode=dark] .akilio-composer{background:#18181beb;border-color:#3f3f46}.akilio-root[data-akilio-color-mode=dark] .akilio-composer textarea{color:var(--akilio-text, #fafafa);caret-color:var(--akilio-text, #fafafa);-webkit-text-fill-color:var(--akilio-text, #fafafa)}.akilio-root[data-akilio-color-mode=dark] .akilio-composer textarea::placeholder{color:var(--akilio-muted, #a1a1aa);-webkit-text-fill-color:var(--akilio-muted, #a1a1aa);opacity:1}.akilio-root[data-akilio-color-mode=dark] .akilio-composer-footer span{color:var(--akilio-muted, #a1a1aa)}.akilio-root[data-akilio-color-mode=dark] .akilio-composer-box:focus-within{background:#18181b}@media(max-width:420px){.akilio-logo{width:78px;height:30px}}.akilio-privacy-consent-copy{margin-top:6px!important}.akilio-privacy-actions{display:flex;justify-content:flex-end;gap:8px}.akilio-privacy-actions button{border:1px solid rgba(161,161,170,.65);border-radius:999px;padding:6px 12px;font:inherit;font-weight:700;cursor:pointer}.akilio-privacy-decline{background:transparent;color:#52525b}.akilio-privacy-accept{border-color:var(--akilio-primary)!important;background:var(--akilio-primary);color:#fff}.akilio-privacy-review{border:0;background:transparent;color:var(--akilio-primary);font:inherit;font-weight:700;text-decoration:underline;cursor:pointer;padding:0}.akilio-products{margin-top:14px;min-width:0}.akilio-products-heading{display:flex;align-items:center;justify-content:space-between;margin-bottom:9px;font-size:12px;color:#64748b}.akilio-products-heading span{display:inline-flex;align-items:center;justify-content:center;min-width:22px;height:22px;padding:0 7px;border-radius:999px;background:#eef2ff;color:#4338ca;font-weight:700}.akilio-product-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px}.akilio-product-card{min-width:0;overflow:hidden;border:1px solid #e2e8f0;border-radius:16px;background:#fff;box-shadow:0 6px 20px #0f172a0f}.akilio-product-media{position:relative;aspect-ratio:4 / 3;overflow:hidden;background:#f1f5f9}.akilio-product-media img{width:100%;height:100%;display:block;object-fit:cover;transition:transform .18s ease}.akilio-product-card:hover .akilio-product-media img{transform:scale(1.025)}.akilio-product-placeholder{width:100%;height:100%;display:grid;place-items:center;color:var(--akilio-primary);font-size:30px;font-weight:800;background:linear-gradient(145deg,#0891b21f,#2563eb14)}.akilio-stock{position:absolute;top:8px;right:8px;max-width:calc(100% - 16px);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;padding:4px 7px;border-radius:999px;background:#7f1d1de6;color:#fff;font-size:9px;font-weight:700}.akilio-stock--available{background:#065f46e6}.akilio-product-body{padding:11px}.akilio-product-vendor,.akilio-product-variant{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#64748b;font-size:10px}.akilio-product-card h4{margin:4px 0 8px;min-height:34px;overflow:hidden;color:#0f172a;font-size:13px;line-height:1.3;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}.akilio-product-price{min-height:37px;display:flex;flex-wrap:wrap;align-items:baseline;gap:5px;margin-bottom:9px}.akilio-product-price strong{color:#0f172a;font-size:14px}.akilio-product-price del{color:#94a3b8;font-size:10px}.akilio-product-action{width:100%;min-height:34px;display:inline-flex;align-items:center;justify-content:center;padding:7px 10px;border:0;border-radius:10px;background:var(--akilio-primary);color:var(--akilio-primary-foreground);font:inherit;font-size:11px;font-weight:700;text-decoration:none;cursor:pointer}.akilio-product-action:hover{filter:brightness(.96)}.akilio-product-action:focus-visible{outline:3px solid color-mix(in srgb,var(--akilio-primary) 28%,transparent);outline-offset:2px}.akilio-product-action:disabled{opacity:.55;cursor:not-allowed}.akilio-root[data-akilio-color-mode=dark] .akilio-products-heading{color:#94a3b8}.akilio-root[data-akilio-color-mode=dark] .akilio-products-heading span{background:#312e81;color:#e0e7ff}.akilio-root[data-akilio-color-mode=dark] .akilio-product-card{background:#111827;border-color:#334155;box-shadow:none}.akilio-root[data-akilio-color-mode=dark] .akilio-product-media{background:#1e293b}.akilio-root[data-akilio-color-mode=dark] .akilio-product-card h4,.akilio-root[data-akilio-color-mode=dark] .akilio-product-price strong{color:#f8fafc}.akilio-root[data-akilio-color-mode=dark] .akilio-product-vendor,.akilio-root[data-akilio-color-mode=dark] .akilio-product-variant{color:#94a3b8}@media(max-width:390px){.akilio-product-grid{grid-template-columns:1fr}.akilio-product-media{aspect-ratio:16 / 9}}.akilio-response-suggestions{display:flex;flex-wrap:wrap;gap:8px;margin-top:14px}.akilio-response-suggestions button{appearance:none;border:1px solid color-mix(in srgb,var(--akilio-primary) 26%,#d4d4d8);border-radius:999px;background:color-mix(in srgb,var(--akilio-primary) 7%,#ffffff);color:color-mix(in srgb,var(--akilio-primary) 72%,#18181b);cursor:pointer;padding:8px 11px;text-align:left;font:inherit;font-size:12px;font-weight:700;line-height:1.35;transition:transform .15s ease,border-color .15s ease,background .15s ease}.akilio-response-suggestions button:hover{transform:translateY(-1px);border-color:color-mix(in srgb,var(--akilio-primary) 52%,#d4d4d8);background:color-mix(in srgb,var(--akilio-primary) 12%,#ffffff)}.akilio-response-suggestions button:focus-visible{outline:3px solid color-mix(in srgb,var(--akilio-primary) 24%,transparent);outline-offset:2px}.akilio-root[data-akilio-color-mode=dark] .akilio-response-suggestions button{border-color:color-mix(in srgb,var(--akilio-primary) 34%,#3f3f46);background:color-mix(in srgb,var(--akilio-primary) 14%,#18181b);color:#f4f4f5}