@v-office/website-chat 1.0.0 → 1.1.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/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { r as MAX_MESSAGE_CHARS, t as CHAT_LOCALES } from "./protocol-BuNqZAMt.js";
2
- import { c as chatAvailable, i as Spark, o as loadConversation, t as Arrow } from "./icons-B5PAY3aU.js";
2
+ import { d as chatAvailable, l as loadConversation, o as Spark, t as Arrow } from "./icons-B4hI9ipR.js";
3
3
  import { Suspense, lazy, useCallback, useEffect, useMemo, useRef, useState } from "react";
4
4
  import { createPortal } from "react-dom";
5
5
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
@@ -12,7 +12,7 @@ const DEFAULT_COPY = {
12
12
  title: "Reiseassistent",
13
13
  newConversation: "Neues Gespräch",
14
14
  notice: "Sie chatten mit einer KI. Preise und Verfügbarkeit in den Karten kommen direkt aus unserem Buchungssystem.",
15
- greeting: "Hallo! Wann möchten Sie reisen, und mit wie vielen Personen?",
15
+ greeting: "Hallo! Erzählen Sie mir einfach, wie Ihr Urlaub aussehen soll: wann, mit wem und was Ihnen wichtig ist. Ich suche passende Unterkünfte, prüfe freie Termine und Preise und zeige Ihnen, was passt.",
16
16
  suggestions: [
17
17
  "Ein Ferienhaus für 4 Personen in den Sommerferien",
18
18
  "Eine Woche mit Hund, gern mit eingezäuntem Garten",
@@ -31,6 +31,13 @@ const DEFAULT_COPY = {
31
31
  contactLink: "Kontakt aufnehmen",
32
32
  bookStay: "Zur Buchung",
33
33
  viewRental: "Zur Unterkunft",
34
+ book: "Buchen",
35
+ view: "Ansehen",
36
+ matchBadge: "Passt genau",
37
+ nights: {
38
+ one: "{n} Nacht",
39
+ other: "{n} Nächte"
40
+ },
34
41
  total: "Gesamtpreis",
35
42
  notBookable: "Für diesen Zeitraum nicht buchbar",
36
43
  photo: "Foto",
@@ -77,7 +84,7 @@ const DEFAULT_COPY = {
77
84
  title: "Travel assistant",
78
85
  newConversation: "New conversation",
79
86
  notice: "You are chatting with an AI. Prices and availability in the cards come straight from our booking system.",
80
- greeting: "Hello! When would you like to travel, and how many of you?",
87
+ greeting: "Hello! Just tell me what your holiday should look like: when, who with and what matters to you. I find rentals that fit, check free dates and prices and show you what suits.",
81
88
  suggestions: [
82
89
  "A holiday home for 4 in the summer holidays",
83
90
  "A week with our dog, ideally with a fenced garden",
@@ -96,6 +103,13 @@ const DEFAULT_COPY = {
96
103
  contactLink: "Contact us",
97
104
  bookStay: "Book this stay",
98
105
  viewRental: "View the rental",
106
+ book: "Book",
107
+ view: "View",
108
+ matchBadge: "Good match",
109
+ nights: {
110
+ one: "{n} night",
111
+ other: "{n} nights"
112
+ },
99
113
  total: "Total",
100
114
  notBookable: "Not bookable for these dates",
101
115
  photo: "Photo",
@@ -138,17 +152,88 @@ const DEFAULT_COPY = {
138
152
  };
139
153
  //#endregion
140
154
  //#region src/theme.ts
141
- /** The curated looks over one layout: `classic` (soft shadows, medium corners), `soft` (round corners,
142
- * lighter shadows), `minimal` (borders instead of shadows, small corners). */
155
+ /** The curated looks over one layout, each relative to the site's corner radius: `classic` (soft
156
+ * shadows), `soft` (rounder corners, lighter shadows), `minimal` (borders instead of shadows, tighter
157
+ * corners). */
143
158
  const CHAT_THEMES = [
144
159
  "classic",
145
160
  "soft",
146
161
  "minimal"
147
162
  ];
148
- /** See {@link ColorVariables}. */
163
+ /** The site tokens the chat follows: the shadcn set every AI Studio template defines (and
164
+ * website-components reads), the chat variable each sets, and the chat's own value for a site
165
+ * without it. CSS custom properties reach into the shadow root; `all: initial` leaves them alone.
166
+ * Not `--secondary`: shadcn sets it next to `--muted`, so the visitor's messages would vanish on the
167
+ * message area; the chat's tint is the site's primary, lightened (`--chat-secondary`). */
168
+ const SITE_COLORS = [
169
+ {
170
+ site: "--primary",
171
+ chat: "--chat-primary",
172
+ own: "#1f4f8b"
173
+ },
174
+ {
175
+ site: "--primary-foreground",
176
+ chat: "--chat-on-primary",
177
+ own: "#fff"
178
+ },
179
+ {
180
+ site: "--background",
181
+ chat: "--chat-surface",
182
+ own: "#fff"
183
+ },
184
+ {
185
+ site: "--muted",
186
+ chat: "--chat-surface-soft",
187
+ own: "#f5f7fa"
188
+ },
189
+ {
190
+ site: "--foreground",
191
+ chat: "--chat-text",
192
+ own: "#1b1f24"
193
+ },
194
+ {
195
+ site: "--muted-foreground",
196
+ chat: "--chat-muted",
197
+ own: "#5b6470"
198
+ },
199
+ {
200
+ site: "--border",
201
+ chat: "--chat-border",
202
+ own: "#d9dee5"
203
+ }
204
+ ];
205
+ /** shadcn's base radius; the themes scale it (classic 1.6×, so 16px for shadcn's 10px). */
206
+ const SITE_RADIUS = {
207
+ site: "--radius",
208
+ chat: "--chat-base-radius",
209
+ own: "10px"
210
+ };
211
+ /** See {@link UnusableSiteTokens}. */
212
+ const unusableSiteTokens = (host) => {
213
+ if (typeof CSS === "undefined" || typeof CSS.supports !== "function") return {};
214
+ const style = getComputedStyle(host);
215
+ const unusable = (token, property) => {
216
+ const value = style.getPropertyValue(token.site).trim();
217
+ return value !== "" && !CSS.supports(property, value);
218
+ };
219
+ return Object.fromEntries([...SITE_COLORS.filter((token) => unusable(token, "color")).map((t) => [t.chat, t.own]), ...unusable(SITE_RADIUS, "width") ? [[SITE_RADIUS.chat, SITE_RADIUS.own]] : []]);
220
+ };
221
+ /** See {@link ColorVariables}. An override brings its own text colour, as before the chat took the
222
+ * site's tokens: the site's foregrounds suit the site's colours (a dark theme's light primary has
223
+ * dark text), not the brand colour a site sets here. */
149
224
  const colorVariables = (colors) => ({
150
- ...colors?.primary && { "--chat-primary": colors.primary },
151
- ...colors?.secondary && { "--chat-secondary": colors.secondary }
225
+ ...colors?.primary && {
226
+ "--chat-primary": colors.primary,
227
+ "--chat-on-primary": "#fff"
228
+ },
229
+ ...colors?.secondary && {
230
+ "--chat-secondary": colors.secondary,
231
+ "--chat-on-secondary": "#1b1f24"
232
+ },
233
+ ...colors?.accent && {
234
+ "--chat-accent": colors.accent,
235
+ "--chat-on-accent": "#fff"
236
+ }
152
237
  });
153
238
  /** The widget's stylesheet, inserted into its shadow root. */
154
239
  const CHAT_CSS = `
@@ -157,15 +242,17 @@ const CHAT_CSS = `
157
242
  \`:host\` rules, and only important ones reverse that. */
158
243
  all: initial !important;
159
244
  font-family: inherit !important;
160
- --chat-primary: #1f4f8b;
161
- --chat-on-primary: #fff;
162
- --chat-secondary: #eaf0f7;
163
- --chat-surface: #fff;
164
- --chat-surface-soft: #f5f7fa;
165
- --chat-text: #1b1f24;
166
- --chat-muted: #5b6470;
167
- --chat-border: #d9dee5;
168
- --chat-radius: 16px;
245
+ ${SITE_COLORS.map((t) => ` ${t.chat}: var(${t.site}, ${t.own});`).join("\n")}
246
+ ${SITE_RADIUS.chat}: var(${SITE_RADIUS.site}, ${SITE_RADIUS.own});
247
+ --chat-secondary: color-mix(in srgb, var(--chat-primary) 12%, var(--chat-surface));
248
+ --chat-on-secondary: var(--chat-text);
249
+ /* The small marks (the chat's name and sparkle, a card's place, icons, the ring): the primary,
250
+ unless a site sets an accent of its own (\`colors.accent\`). */
251
+ --chat-accent: var(--chat-primary);
252
+ --chat-on-accent: var(--chat-on-primary);
253
+ --chat-radius: calc(var(--chat-base-radius) * 1.6);
254
+ /* Buttons, chips and the pill are pills, unless the site's corners are square (radius 0). */
255
+ --chat-round: calc(var(--chat-base-radius) * 100);
169
256
  --chat-shadow: 0 18px 48px rgb(15 23 42 / 0.18);
170
257
  --chat-card-shadow: 0 1px 3px rgb(15 23 42 / 0.08), 0 8px 24px rgb(15 23 42 / 0.06);
171
258
  /* Not set here, so a site's value reaches the widget: \`--chat-top\` keeps a sticky header uncovered,
@@ -179,15 +266,16 @@ const CHAT_CSS = `
179
266
  .entry { position: fixed; right: 20px; bottom: 20px; z-index: 2147483000; }
180
267
  [data-position="left"] .entry { right: auto; left: 20px; }
181
268
  .pill { position: relative; z-index: 1; display: inline-flex; align-items: center; gap: 12px;
182
- padding: 8px 20px 8px 8px; border-radius: 999px; border: 1px solid rgb(255 255 255 / 0.7);
183
- background: rgb(255 255 255 / 0.86); backdrop-filter: blur(16px) saturate(1.4);
269
+ padding: 8px 20px 8px 8px; border-radius: var(--chat-round);
270
+ border: 1px solid color-mix(in srgb, var(--chat-surface) 70%, transparent);
271
+ background: color-mix(in srgb, var(--chat-surface) 86%, transparent); backdrop-filter: blur(16px) saturate(1.4);
184
272
  -webkit-backdrop-filter: blur(16px) saturate(1.4); box-shadow: var(--chat-shadow);
185
273
  color: var(--chat-text); font: inherit; text-align: left; cursor: pointer; }
186
274
  .spark { flex: none; display: grid; place-items: center; width: 40px; height: 40px; border-radius: 999px;
187
- background: var(--chat-primary); color: var(--chat-on-primary); }
275
+ background: var(--chat-accent); color: var(--chat-on-accent); }
188
276
  .pill-text { display: flex; flex-direction: column; min-width: 0; flex: 1; line-height: 1.25; }
189
- .pill-title { font-size: 12px; font-weight: 600; color: var(--chat-primary); }
190
- .pill-prompt { font-size: 14px; font-weight: 600; }
277
+ .pill-title { font-size: 12px; font-weight: 600; color: var(--chat-accent); }
278
+ .pill-prompt { font-size: 15px; font-weight: 500; }
191
279
  .launcher-field { display: none; }
192
280
  .launcher-field input { border: 0; outline: none; background: transparent; padding: 0; margin: 0;
193
281
  font: inherit; font-size: 14px; font-weight: 600; color: var(--chat-text); width: 100%; }
@@ -195,12 +283,14 @@ const CHAT_CSS = `
195
283
  .launcher-field:focus-within input::placeholder { color: var(--chat-muted); }
196
284
  /* The field has no outline of its own: the pill shows keyboard focus, typed text or not. */
197
285
  .launcher-field:focus-within { outline: 2px solid var(--chat-primary); outline-offset: 2px; }
198
- .glow { position: absolute; inset: -3px; border-radius: 999px; overflow: hidden; padding: 3px;
286
+ /* Until the first message the pill glows in the accent colour as well. */
287
+ .entry[data-glow] .pill { box-shadow: var(--chat-shadow), 0 0 28px color-mix(in srgb, var(--chat-accent) 38%, transparent); }
288
+ .glow { position: absolute; inset: -3px; border-radius: var(--chat-round); overflow: hidden; padding: 3px;
199
289
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
200
290
  -webkit-mask-composite: xor; mask: linear-gradient(#000 0 0) content-box exclude,
201
291
  linear-gradient(#000 0 0); }
202
292
  .glow::before { content: ""; position: absolute; inset: -150%;
203
- background: conic-gradient(from 0deg, transparent 0 62%, var(--chat-primary) 82%, transparent 100%); }
293
+ background: conic-gradient(from 0deg, transparent 0 62%, var(--chat-accent) 82%, transparent 100%); }
204
294
  @media (min-width: 1024px) {
205
295
  .launcher[data-narrow] { display: none; }
206
296
  .launcher-field { display: flex; width: 26rem; padding: 8px; cursor: text; }
@@ -216,46 +306,54 @@ const CHAT_CSS = `
216
306
  .scrim[hidden], .panel[hidden] { display: none; }
217
307
  @media (min-width: 1024px) {
218
308
  .panel { left: auto; right: 20px; bottom: 20px; top: calc(var(--chat-top, 0px) + 20px);
219
- width: min(var(--chat-width, 600px), calc(100vw - 40px)); border-radius: var(--chat-radius); }
309
+ width: min(var(--chat-width, 700px), calc(100vw - 40px)); border-radius: var(--chat-radius); }
220
310
  [data-position="left"] .panel { right: auto; left: 20px; }
221
311
  }
222
- .header { display: flex; align-items: center; gap: 8px; padding: 16px 20px 6px; }
223
- .title { margin: 0 auto 0 0; font-size: 12px; font-weight: 700; letter-spacing: 0.08em;
224
- text-transform: uppercase; color: var(--chat-primary); }
225
- .icon-button { flex: none; display: grid; place-items: center; width: 36px; height: 36px; padding: 0;
226
- border: 1px solid var(--chat-border); border-radius: 999px; background: var(--chat-surface);
312
+ .header { display: flex; align-items: center; gap: 8px; padding: 20px 24px 4px; }
313
+ .title { margin: 0 auto 0 0; font-size: 13px; font-weight: 600; letter-spacing: 0.16em;
314
+ text-transform: uppercase; color: var(--chat-accent); }
315
+ .icon-button { flex: none; display: grid; place-items: center; width: 40px; height: 40px; padding: 0;
316
+ border: 1px solid var(--chat-border); border-radius: var(--chat-round); background: var(--chat-surface);
227
317
  color: var(--chat-text); cursor: pointer; }
228
318
  .icon-button:hover { border-color: var(--chat-primary); }
229
- .notice { margin: 0; padding: 0 20px 12px; font-size: 12px; color: var(--chat-muted);
230
- border-bottom: 1px solid var(--chat-border); }
231
- .messages { flex: 1; overflow-y: auto; overscroll-behavior: contain; padding: 20px 20px 36px;
319
+ /* Closing is the panel's one strong control, filled like the site's buttons. */
320
+ .close { background: var(--chat-primary); border-color: var(--chat-primary); color: var(--chat-on-primary); }
321
+ .close:hover { opacity: 0.9; }
322
+ .notice { margin: 0; padding: 0 24px 14px; font-size: 12px; color: var(--chat-muted); }
323
+ .messages { flex: 1; overflow-y: auto; overscroll-behavior: contain; padding: 24px 24px 40px;
232
324
  display: flex; flex-direction: column; gap: 16px; background: var(--chat-surface-soft);
233
325
  container-type: inline-size; }
234
326
  .turn { display: flex; flex-direction: column; }
235
327
  .message { max-width: 88%; white-space: pre-wrap; overflow-wrap: anywhere; }
236
- .message[data-role="visitor"] { align-self: flex-end; margin-left: 48px; padding: 10px 14px;
237
- border-radius: var(--chat-radius); background: var(--chat-secondary); }
238
- .message[data-role="assistant"] { align-self: flex-start; max-width: 100%; }
328
+ .message[data-role="visitor"] { align-self: flex-end; margin-left: 48px; padding: 12px 16px;
329
+ border-radius: calc(var(--chat-radius) * 0.75); background: var(--chat-secondary);
330
+ color: var(--chat-on-secondary); }
331
+ .message[data-role="assistant"] { align-self: flex-start; max-width: 100%; font-size: 16px; line-height: 1.6; }
332
+ .message strong { font-weight: 600; }
239
333
  .ui { align-self: stretch; display: flex; flex-direction: column; gap: 12px; }
240
334
  .thinking { display: flex; gap: 5px; padding: 4px 2px; }
241
- .thinking i { width: 7px; height: 7px; border-radius: 999px; background: var(--chat-primary); opacity: 0.5; }
335
+ .thinking i { width: 7px; height: 7px; border-radius: 999px; background: var(--chat-accent); opacity: 0.5; }
242
336
  .dock { position: relative; padding: 10px 20px 16px; background: var(--chat-surface); }
243
337
  .dock::before { content: ""; position: absolute; left: 0; right: 0; top: -28px; height: 28px;
244
338
  pointer-events: none; background: linear-gradient(to top, var(--chat-surface-soft), transparent); }
245
339
  .suggestions { display: flex; gap: 8px; overflow-x: auto; scrollbar-width: none; margin: 0 -20px 10px;
246
340
  padding: 0 20px; }
247
341
  .suggestions::-webkit-scrollbar { display: none; }
248
- .suggestion { flex: none; max-width: 100%; border: 1px solid transparent; border-radius: 20px;
249
- background: var(--chat-secondary); color: var(--chat-text); padding: 8px 14px; font: inherit;
250
- font-size: 13px; font-weight: 500; line-height: 1.35; text-align: left; white-space: nowrap;
251
- cursor: pointer; }
252
- .suggestion:hover { border-color: var(--chat-primary); background: var(--chat-surface); }
342
+ /* Suggestions and quick replies: filled pills, an icon for the topic in the accent colour. */
343
+ .suggestion, .chip { flex: none; max-width: 100%; display: inline-flex; align-items: center; gap: 8px;
344
+ border: 1px solid transparent; border-radius: var(--chat-round); background: var(--chat-secondary);
345
+ color: var(--chat-on-secondary); padding: 8px 16px 8px 12px; font: inherit; font-size: 14px;
346
+ font-weight: 600; line-height: 1.35; text-align: left; white-space: nowrap; cursor: pointer; }
347
+ .suggestion svg, .chip svg { flex: none; color: var(--chat-accent); }
348
+ /* Suggestions scroll in one row; a quick reply wraps inside the message's width. */
349
+ .chip { white-space: normal; }
350
+ .suggestion:hover { border-color: var(--chat-primary); background: var(--chat-surface); color: var(--chat-text); }
253
351
  @media (min-width: 1024px) {
254
352
  .suggestions { flex-wrap: wrap; overflow: visible; margin: 0 0 10px; padding: 0; }
255
353
  .suggestion { white-space: normal; }
256
354
  }
257
355
  .composer { display: flex; align-items: flex-end; gap: 8px; padding: 6px 6px 6px 18px;
258
- border: 1px solid var(--chat-border); border-radius: 24px; background: var(--chat-surface);
356
+ border: 1px solid var(--chat-border); border-radius: var(--chat-round); background: var(--chat-surface);
259
357
  box-shadow: 0 4px 16px rgb(15 23 42 / 0.08); }
260
358
  .composer:focus-within { border-color: var(--chat-text); }
261
359
  /* 16px keeps iOS from zooming into the field. */
@@ -263,14 +361,19 @@ const CHAT_CSS = `
263
361
  outline: none; background: transparent; padding: 6px 0; font: inherit; font-size: 16px;
264
362
  line-height: 1.5; color: inherit; field-sizing: content; }
265
363
  .send { flex: none; display: grid; place-items: center; width: 36px; height: 36px; padding: 0; border: 0;
266
- border-radius: 999px; background: var(--chat-primary); color: var(--chat-on-primary); cursor: pointer; }
364
+ border-radius: var(--chat-round); background: var(--chat-primary); color: var(--chat-on-primary); cursor: pointer; }
267
365
  .send:disabled { opacity: 0.3; cursor: not-allowed; }
268
- .consent { flex: 1; overflow-y: auto; padding: 24px 20px; display: flex; flex-direction: column; gap: 14px;
366
+ .consent { flex: 1; overflow-y: auto; padding: 24px; display: flex; flex-direction: column; gap: 14px;
269
367
  background: var(--chat-surface-soft); }
270
368
  .consent p { margin: 0; }
369
+ .consent-card { display: flex; flex-direction: column; align-items: flex-start; gap: 14px; padding: 18px 20px;
370
+ border: 1px solid var(--chat-border); border-radius: var(--chat-radius); background: var(--chat-surface);
371
+ font-size: 14px; color: var(--chat-muted); }
271
372
  .pending { margin: 0; align-self: flex-end; max-width: 88%; padding: 10px 14px;
272
- border-radius: var(--chat-radius); background: var(--chat-secondary); white-space: pre-wrap; }
273
- .pending figcaption { margin-bottom: 2px; }
373
+ border-radius: var(--chat-radius); background: var(--chat-secondary); color: var(--chat-on-secondary);
374
+ white-space: pre-wrap; }
375
+ /* The bubble's own text colour, softened: the muted grey suits the surface, not a set secondary. */
376
+ .pending figcaption { margin-bottom: 2px; color: inherit; opacity: 0.75; }
274
377
  .fallback { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; margin-bottom: 10px; }
275
378
  .fallback p { margin: 0; }
276
379
 
@@ -282,9 +385,20 @@ const CHAT_CSS = `
282
385
  .gallery::-webkit-scrollbar, .carousel::-webkit-scrollbar { display: none; }
283
386
  .gallery img { flex: 0 0 100%; scroll-snap-align: start; display: block; width: 100%; aspect-ratio: 5 / 3;
284
387
  height: auto; object-fit: cover; }
285
- .gallery-wrap .snap-nav { position: absolute; left: 8px; right: 8px; bottom: 8px; }
286
- .gallery-wrap .snap-nav .muted { background: rgb(0 0 0 / 0.5); color: #fff; padding: 1px 7px;
287
- border-radius: 999px; font-size: 11px; }
388
+ /* Dots say which photo shows; arrows come up under a pointer and on keyboard focus (a finger swipes:
389
+ on a touch screen they take no tap, so a tap on a photo's edge does not turn it unseen). */
390
+ .dots { position: absolute; left: 50%; bottom: 10px; translate: -50% 0; display: flex; gap: 4px; }
391
+ .dots i { width: 6px; height: 6px; border-radius: 999px; background: rgb(255 255 255 / 0.6);
392
+ box-shadow: 0 0 2px rgb(0 0 0 / 0.3); }
393
+ .dots i[data-current] { width: 14px; background: #fff; }
394
+ .gallery-arrow { position: absolute; top: 50%; translate: 0 -50%; display: grid; place-items: center;
395
+ width: 32px; height: 32px; padding: 0; border: 0; border-radius: var(--chat-round);
396
+ background: color-mix(in srgb, var(--chat-surface) 92%, transparent); color: var(--chat-text);
397
+ box-shadow: 0 2px 8px rgb(0 0 0 / 0.18); cursor: pointer; opacity: 0; }
398
+ .gallery-arrow[data-side="prev"] { left: 10px; }
399
+ .gallery-arrow[data-side="next"] { right: 10px; }
400
+ .gallery-wrap:hover .gallery-arrow:not(:disabled), .gallery-arrow:focus-visible { opacity: 1; }
401
+ @media (hover: none) { .gallery-arrow:not(:focus-visible) { pointer-events: none; } }
288
402
  .carousel { gap: 12px; padding: 2px 2px 6px; }
289
403
  .carousel > .card { flex: 0 0 86%; scroll-snap-align: start; }
290
404
  /* Room for two: results side by side, all of them, instead of a row to swipe. */
@@ -294,7 +408,7 @@ const CHAT_CSS = `
294
408
  }
295
409
  .snap-nav { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-top: 6px; }
296
410
  .snap-nav button, .calendar-head button { border: 1px solid var(--chat-border); background: var(--chat-surface);
297
- color: var(--chat-text); border-radius: 999px; width: 28px; height: 28px; cursor: pointer; font: inherit;
411
+ color: var(--chat-text); border-radius: var(--chat-round); width: 28px; height: 28px; cursor: pointer; font: inherit;
298
412
  line-height: 1; }
299
413
  .snap-nav button:disabled, .calendar-head button:disabled { opacity: 0.35; cursor: default; }
300
414
  .rating { color: #b7791f; font-weight: 700; }
@@ -302,11 +416,9 @@ const CHAT_CSS = `
302
416
  .breakdown .total { border-top: 1px solid var(--chat-border); padding-top: 6px; margin-top: 2px;
303
417
  font-weight: 700; }
304
418
  .alternatives, .quick-replies { display: flex; flex-wrap: wrap; gap: 6px; }
305
- .chip-link { border: 1px solid var(--chat-primary); color: var(--chat-primary); border-radius: 999px;
419
+ .chip-link { border: 1px solid var(--chat-primary); color: var(--chat-primary); border-radius: var(--chat-round);
306
420
  padding: 5px 10px; text-decoration: none; font-size: 13px; }
307
- .chip { border: 1px solid transparent; background: var(--chat-secondary); color: var(--chat-text);
308
- border-radius: 999px; padding: 7px 13px; font: inherit; font-size: 13px; cursor: pointer; }
309
- .chip:hover { border-color: var(--chat-primary); background: var(--chat-surface); }
421
+ .chip:hover { border-color: var(--chat-primary); background: var(--chat-surface); color: var(--chat-text); }
310
422
  .calendar { border: 1px solid var(--chat-border); border-radius: var(--chat-radius); padding: 12px;
311
423
  background: var(--chat-surface); }
312
424
  .calendar-head { display: flex; align-items: center; justify-content: space-between; gap: 8px;
@@ -314,13 +426,15 @@ const CHAT_CSS = `
314
426
  .calendar-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 3px; text-align: center;
315
427
  font-size: 12px; }
316
428
  .weekday { color: var(--chat-muted); font-weight: 600; }
317
- .day { padding: 4px 0; border-radius: 6px; }
429
+ .day { padding: 4px 0; border-radius: min(6px, var(--chat-radius)); }
318
430
  .sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%);
319
431
  white-space: nowrap; }
320
- .day[data-status="free"], .legend i[data-status="free"] { background: #e3f1e7; color: #1f5133; }
321
- .day[data-status="booked"], .legend i[data-status="booked"] { background: #f1f2f4; color: #9aa1a9;
432
+ .day[data-status="free"], .legend i[data-status="free"] {
433
+ background: color-mix(in srgb, var(--chat-primary) 14%, var(--chat-surface)); color: var(--chat-text); }
434
+ .day[data-status="booked"], .legend i[data-status="booked"] { background: var(--chat-surface-soft);
435
+ color: var(--chat-muted);
322
436
  text-decoration: line-through; }
323
- .day[data-status="closed"] { color: #b8bec6; }
437
+ .day[data-status="closed"] { color: var(--chat-muted); opacity: 0.6; }
324
438
  .day[data-stay], .legend i[data-stay] { background: var(--chat-primary); color: var(--chat-on-primary);
325
439
  text-decoration: none; }
326
440
  .legend { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 8px; font-size: 12px; }
@@ -335,12 +449,26 @@ const CHAT_CSS = `
335
449
  .compare-col dl { margin: 0; display: flex; flex-direction: column; gap: 2px; flex: 1; }
336
450
  .compare-col dt { color: var(--chat-muted); font-size: 11px; margin-top: 4px; }
337
451
  .compare-col dd { margin: 0; overflow-wrap: anywhere; }
338
- .card-body { padding: 12px 14px 14px; display: flex; flex-direction: column; gap: 4px; }
339
- .card-title { margin: 0; font-size: 15px; font-weight: 700; }
452
+ .card-body { padding: 16px 18px 12px; display: flex; flex-direction: column; gap: 6px; }
453
+ .card-title { margin: 0; font-size: 18px; font-weight: 500; line-height: 1.3; }
454
+ /* A rental card: place and score, name, facts and wishes; the stay's price and button below a line. */
455
+ .rental-card, .stay-card { display: flex; flex-direction: column; }
456
+ .rental-card .card-body { flex: 1; }
457
+ .card-meta { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-height: 24px; }
458
+ .place { color: var(--chat-accent); font-size: 14px; }
459
+ .score-chip { margin-left: auto; padding: 2px 8px; border-radius: calc(var(--chat-radius) * 0.4);
460
+ background: var(--chat-secondary); color: var(--chat-on-secondary); font-size: 12px; font-weight: 700; }
461
+ .specs { margin: 0; color: var(--chat-muted); font-size: 14px; }
462
+ .card-foot { display: flex; align-items: center; justify-content: space-between; gap: 12px;
463
+ margin: 0 18px; padding: 12px 0 16px; border-top: 1px solid var(--chat-border); }
464
+ .foot-price { display: flex; flex-direction: column; min-width: 0; }
465
+ .foot-price .price { font-size: 18px; line-height: 1.3; }
466
+ .foot-meta { color: var(--chat-muted); font-size: 12px; }
467
+ .button.compact { margin: 0 0 0 auto; padding: 9px 20px; font-size: 14px; white-space: nowrap; }
340
468
  .muted { color: var(--chat-muted); font-size: 13px; }
341
469
  .price { font-weight: 700; }
342
470
  .button { display: inline-block; margin-top: 6px; text-align: center; text-decoration: none;
343
- padding: 10px 16px; border-radius: 999px; background: var(--chat-primary); color: var(--chat-on-primary);
471
+ padding: 10px 16px; border-radius: var(--chat-round); background: var(--chat-primary); color: var(--chat-on-primary);
344
472
  border: 0; cursor: pointer; font: inherit; font-weight: 600; }
345
473
  .link { color: var(--chat-primary); }
346
474
  /* A link out of the chat while a reply still streams (ReplyLink). */
@@ -348,12 +476,13 @@ const CHAT_CSS = `
348
476
 
349
477
  /* Wishes a found stay matches: a badge on the photo, the wishes ticked under the name. */
350
478
  .card-media { position: relative; }
351
- .badge { position: absolute; left: 10px; top: 10px; z-index: 1; padding: 4px 10px; border-radius: 999px;
352
- background: var(--chat-surface); color: var(--chat-primary); font-size: 12px; font-weight: 600;
479
+ .badge { position: absolute; left: 12px; top: 12px; z-index: 1; padding: 5px 12px; border-radius: var(--chat-round);
480
+ background: var(--chat-surface); color: var(--chat-accent); font-size: 12px; font-weight: 600;
353
481
  box-shadow: var(--chat-card-shadow); }
354
- .wishes { list-style: none; margin: 2px 0; padding: 0; display: flex; flex-wrap: wrap; gap: 2px 10px;
355
- font-size: 13px; }
356
- .wishes li::before { content: "✓ "; color: var(--chat-primary); font-weight: 700; }
482
+ .wishes { list-style: none; margin: 4px 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 6px; }
483
+ .wishes li { display: inline-flex; align-items: center; gap: 6px; padding: 4px 12px 4px 10px;
484
+ border: 1px solid var(--chat-border); border-radius: var(--chat-round); font-size: 13px; font-weight: 600; }
485
+ .wishes svg { color: var(--chat-accent); }
357
486
 
358
487
  /* The price calendar: a column per arrival, the bar's height its total. */
359
488
  .price-columns { list-style: none; margin: 10px 0 0; padding: 0; display: grid; grid-auto-flow: column;
@@ -365,7 +494,8 @@ const CHAT_CSS = `
365
494
  .price-column[data-free]:not(:disabled):hover { border-color: var(--chat-primary); }
366
495
  .price-column[data-asked] { border-color: var(--chat-border); background: var(--chat-surface-soft); }
367
496
  .bar-track { display: flex; align-items: flex-end; justify-content: center; width: 100%; height: 72px; }
368
- .bar { width: 60%; border-radius: 6px 6px 2px 2px; background: var(--chat-secondary); }
497
+ .bar { width: 60%; border-radius: min(6px, var(--chat-radius)) min(6px, var(--chat-radius)) 0 0;
498
+ background: color-mix(in srgb, var(--chat-primary) 30%, var(--chat-surface)); }
369
499
  .price-column[data-cheapest] .bar, .legend i[data-cheapest] { background: var(--chat-primary); }
370
500
  .legend i[data-asked] { background: var(--chat-surface-soft); border: 1px solid var(--chat-border); }
371
501
  .price-text { font-size: 12px; font-weight: 700; white-space: nowrap; }
@@ -398,7 +528,7 @@ const CHAT_CSS = `
398
528
  .map-attribution { margin: 0; padding: 2px 8px; font-size: 10px; color: var(--chat-muted); text-align: right; }
399
529
  .map-attribution a { color: inherit; }
400
530
  .map-pin { display: block; width: 18px; height: 18px; border-radius: 999px; background: var(--chat-primary);
401
- border: 3px solid #fff; box-shadow: 0 2px 6px rgb(0 0 0 / 0.35); }
531
+ border: 3px solid var(--chat-surface); box-shadow: 0 2px 6px rgb(0 0 0 / 0.35); }
402
532
  .maplibregl-map { position: relative; overflow: hidden; }
403
533
  .maplibregl-canvas-container, .maplibregl-canvas { position: absolute; left: 0; top: 0; }
404
534
  /* The canvas takes keyboard focus: a ring for the keyboard, none for a click or a tap. */
@@ -406,13 +536,13 @@ const CHAT_CSS = `
406
536
  .maplibregl-canvas:focus-visible { outline: 2px solid var(--chat-primary); outline-offset: -2px; }
407
537
  .maplibregl-marker { position: absolute; top: 0; left: 0; will-change: transform; }
408
538
 
409
- [data-theme="soft"] { --chat-radius: 24px; --chat-shadow: 0 12px 32px rgb(15 23 42 / 0.12);
539
+ [data-theme="soft"] { --chat-radius: calc(var(--chat-base-radius) * 2.4); --chat-shadow: 0 12px 32px rgb(15 23 42 / 0.12);
410
540
  --chat-card-shadow: 0 4px 18px rgb(15 23 42 / 0.07); }
411
- [data-theme="minimal"] { --chat-radius: 6px; --chat-shadow: none; --chat-card-shadow: none; }
541
+ [data-theme="minimal"] { --chat-radius: calc(var(--chat-base-radius) * 0.6); --chat-shadow: none; --chat-card-shadow: none; }
412
542
  [data-theme="minimal"] .panel, [data-theme="minimal"] .pill, [data-theme="minimal"] .card,
413
543
  [data-theme="minimal"] .compare-col { border: 1px solid var(--chat-border); }
414
- [data-theme="minimal"] .pill, [data-theme="minimal"] .composer, [data-theme="minimal"] .suggestion,
415
- [data-theme="minimal"] .chip { border-radius: var(--chat-radius); }
544
+ /* No pills in minimal: every control has its tight corners. */
545
+ [data-theme="minimal"] { --chat-round: var(--chat-radius); }
416
546
 
417
547
  @keyframes chat-spin { to { transform: rotate(1turn); } }
418
548
  @keyframes chat-in { from { opacity: 0; transform: translateY(16px); } }
@@ -436,7 +566,7 @@ const CHAT_CSS = `
436
566
  `;
437
567
  //#endregion
438
568
  //#region src/WebsiteChat.tsx
439
- const Panel = lazy(() => import("./panel-D8pKi4RZ.js"));
569
+ const Panel = lazy(() => import("./panel-hLRBoh8t.js"));
440
570
  /** See {@link WebsiteChatComponent}. */
441
571
  const WebsiteChat = function WebsiteChat(props) {
442
572
  const { endpoint, locale, theme = "classic", position = "right" } = props;
@@ -445,6 +575,7 @@ const WebsiteChat = function WebsiteChat(props) {
445
575
  const field = useRef(null);
446
576
  const wasOpen = useRef(false);
447
577
  const [root, setRoot] = useState(null);
578
+ const [replaced, setReplaced] = useState({});
448
579
  const [available, setAvailable] = useState(false);
449
580
  const [mounted, setMounted] = useState(false);
450
581
  const [open, setOpen] = useState(false);
@@ -483,6 +614,7 @@ const WebsiteChat = function WebsiteChat(props) {
483
614
  useEffect(() => {
484
615
  const element = host.current;
485
616
  if (!available || !element) return;
617
+ setReplaced(unusableSiteTokens(element));
486
618
  setRoot(element.shadowRoot ?? element.attachShadow({ mode: "open" }));
487
619
  }, [available]);
488
620
  const submitEntry = (event) => {
@@ -494,7 +626,10 @@ const WebsiteChat = function WebsiteChat(props) {
494
626
  };
495
627
  return /* @__PURE__ */ jsx("div", {
496
628
  ref: host,
497
- style: colorVariables(props.colors),
629
+ style: {
630
+ ...replaced,
631
+ ...colorVariables(props.colors)
632
+ },
498
633
  children: root && available && createPortal(/* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("style", { children: CHAT_CSS }), /* @__PURE__ */ jsxs("div", {
499
634
  className: "chat",
500
635
  "data-theme": theme,
@@ -503,7 +638,7 @@ const WebsiteChat = function WebsiteChat(props) {
503
638
  fallback: null,
504
639
  children: /* @__PURE__ */ jsx(Panel, {
505
640
  endpoint,
506
- clientVersion: "1.0.0",
641
+ clientVersion: "1.1.0",
507
642
  locale,
508
643
  copy,
509
644
  searchPath: props.searchPath,