@v-office/website-chat 1.1.3 → 1.3.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 +3 -2
- package/dist/{catalog-y3xQ2jXW.js → catalog-Buxzskkg.js} +21 -15
- package/dist/catalog.d.ts +82 -2
- package/dist/catalog.js +2 -2
- package/dist/{icons-DIqOmKoe.js → icons-B0_KM_uQ.js} +18 -2
- package/dist/index.d.ts +94 -3
- package/dist/index.js +625 -94
- package/dist/panel-B_wg4Wl0.js +2594 -0
- package/dist/{protocol-Bmb_uxVQ.d.ts → protocol-Dg1HZNWG.d.ts} +12 -1
- package/instructions/CHANGELOG.md +2 -0
- package/instructions/MIGRATION.md +2 -0
- package/instructions/versions/1.2.0/CHANGELOG.md +45 -0
- package/instructions/versions/1.2.0/MIGRATION.md +15 -0
- package/instructions/versions/1.3.0/CHANGELOG.md +82 -0
- package/instructions/versions/1.3.0/MIGRATION.md +22 -0
- package/package.json +2 -2
- package/dist/panel-BN_DMA4R.js +0 -1247
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 {
|
|
2
|
+
import { h as chatAvailable, l as Spark, p as loadConversation, t as Arrow } from "./icons-B0_KM_uQ.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,16 +12,43 @@ 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
|
+
aiBadge: "KI",
|
|
16
|
+
noticeShort: "Preise aus dem Buchungssystem",
|
|
17
|
+
noticeMore: "Mehr zum KI-Hinweis",
|
|
15
18
|
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
19
|
suggestions: [
|
|
17
|
-
"
|
|
20
|
+
"Sommerwoche für 2 Erwachsene und 2 Kinder",
|
|
18
21
|
"Eine Woche mit Hund, gern mit eingezäuntem Garten",
|
|
19
22
|
"Ein Wochenende zu zweit mit Sauna",
|
|
20
|
-
"Was ist
|
|
23
|
+
"Was ist am nächsten Wochenende noch frei?"
|
|
21
24
|
],
|
|
22
|
-
placeholder: "
|
|
25
|
+
placeholder: "Ihre Reise beschreiben …",
|
|
23
26
|
send: "Senden",
|
|
24
27
|
thinking: "Die Antwort wird geschrieben",
|
|
28
|
+
status: {
|
|
29
|
+
search: "Suche passende Unterkünfte …",
|
|
30
|
+
rental: "Lese die Angaben zur Unterkunft …",
|
|
31
|
+
availability: "Prüfe freie Tage …",
|
|
32
|
+
quote: "Prüfe den Preis im Buchungssystem …",
|
|
33
|
+
reviews: "Lese die Bewertungen …"
|
|
34
|
+
},
|
|
35
|
+
retry: "Erneut senden",
|
|
36
|
+
privacyLink: "Datenschutzerklärung",
|
|
37
|
+
yourPick: "Ihre Auswahl",
|
|
38
|
+
yourTrip: "Ihre Reise",
|
|
39
|
+
assumed: "angenommen",
|
|
40
|
+
guests: {
|
|
41
|
+
one: "{n} Gast",
|
|
42
|
+
other: "{n} Gäste"
|
|
43
|
+
},
|
|
44
|
+
adultsShort: { other: "{n} Erw." },
|
|
45
|
+
weekend: "Wochenende",
|
|
46
|
+
tripStarters: {
|
|
47
|
+
party: "Wir sind ",
|
|
48
|
+
period: "Andere Reisezeit: ",
|
|
49
|
+
nights: "Andere Dauer: ",
|
|
50
|
+
wishes: "Andere Wünsche: "
|
|
51
|
+
},
|
|
25
52
|
yourMessage: "Ihre Nachricht",
|
|
26
53
|
consent: "Der Chat wird von einer KI beantwortet. Ihre Nachrichten werden dazu an unseren KI-Dienstleister übermittelt.",
|
|
27
54
|
consentAccept: "Chat starten",
|
|
@@ -31,7 +58,7 @@ const DEFAULT_COPY = {
|
|
|
31
58
|
contactLink: "Kontakt aufnehmen",
|
|
32
59
|
bookStay: "Zur Buchung",
|
|
33
60
|
viewRental: "Zur Unterkunft",
|
|
34
|
-
book: "
|
|
61
|
+
book: "Zur Buchung",
|
|
35
62
|
view: "Ansehen",
|
|
36
63
|
matchBadge: "Passt genau",
|
|
37
64
|
nights: {
|
|
@@ -39,10 +66,31 @@ const DEFAULT_COPY = {
|
|
|
39
66
|
other: "{n} Nächte"
|
|
40
67
|
},
|
|
41
68
|
total: "Gesamtpreis",
|
|
69
|
+
totalForNights: {
|
|
70
|
+
one: "Gesamtpreis für {n} Nacht",
|
|
71
|
+
other: "Gesamtpreis für {n} Nächte"
|
|
72
|
+
},
|
|
73
|
+
priceDetails: "Preisdetails",
|
|
74
|
+
dealFallback: "Angebot",
|
|
75
|
+
youSave: "Sie sparen {amount}",
|
|
76
|
+
freeCancellation: "Kostenlos stornierbar bis {date}",
|
|
77
|
+
nonRefundable: "Nicht erstattbar",
|
|
78
|
+
arrival: "Anreise",
|
|
79
|
+
departure: "Abreise",
|
|
80
|
+
continueToBooking: "Weiter zur Buchung",
|
|
81
|
+
nothingBookedYet: "Noch nichts gebucht. Auf der Buchungsseite:",
|
|
82
|
+
handoverSteps: [
|
|
83
|
+
"Kontaktdaten eingeben",
|
|
84
|
+
"Zahlungsart wählen",
|
|
85
|
+
"Bestätigung per E-Mail"
|
|
86
|
+
],
|
|
42
87
|
notBookable: "Für diesen Zeitraum nicht buchbar",
|
|
43
88
|
photo: "Foto",
|
|
89
|
+
openPhotos: "Fotos von {name} ansehen",
|
|
90
|
+
closePhotos: "Fotos schließen",
|
|
44
91
|
previous: "Zurück",
|
|
45
92
|
next: "Weiter",
|
|
93
|
+
rentalsFound: "Gefundene Unterkünfte",
|
|
46
94
|
reviews: {
|
|
47
95
|
one: "{n} Bewertung",
|
|
48
96
|
other: "{n} Bewertungen"
|
|
@@ -53,6 +101,19 @@ const DEFAULT_COPY = {
|
|
|
53
101
|
booked: "belegt",
|
|
54
102
|
closed: "nicht buchbar",
|
|
55
103
|
yourStay: "Ihr Zeitraum",
|
|
104
|
+
arrivalPossible: "Anreise möglich",
|
|
105
|
+
cheaper: "günstiger",
|
|
106
|
+
dearer: "teurer",
|
|
107
|
+
showPrice: "Preis anzeigen",
|
|
108
|
+
arrivalOn: "Anreise {date}",
|
|
109
|
+
possibleFrom: "Ab {date} möglich",
|
|
110
|
+
until: "bis {date}",
|
|
111
|
+
otherArrival: "Anderen Anreisetag wählen",
|
|
112
|
+
otherArrivalStarter: "Anreise am ",
|
|
113
|
+
freePeriods: "Freie Termine",
|
|
114
|
+
freePeriodsIn: "Freie Termine im {month}",
|
|
115
|
+
datesDiffer: "Termine je Unterkunft verschieden",
|
|
116
|
+
otherDate: "anderer Termin",
|
|
56
117
|
compareRooms: "Räume",
|
|
57
118
|
compareHighlights: "Highlights",
|
|
58
119
|
compareRating: "Bewertung",
|
|
@@ -75,6 +136,10 @@ const DEFAULT_COPY = {
|
|
|
75
136
|
children: {
|
|
76
137
|
one: "{n} Kind",
|
|
77
138
|
other: "{n} Kinder"
|
|
139
|
+
},
|
|
140
|
+
pets: {
|
|
141
|
+
one: "{n} Haustier",
|
|
142
|
+
other: "{n} Haustiere"
|
|
78
143
|
}
|
|
79
144
|
},
|
|
80
145
|
en: {
|
|
@@ -84,16 +149,43 @@ const DEFAULT_COPY = {
|
|
|
84
149
|
title: "Travel assistant",
|
|
85
150
|
newConversation: "New conversation",
|
|
86
151
|
notice: "You are chatting with an AI. Prices and availability in the cards come straight from our booking system.",
|
|
152
|
+
aiBadge: "AI",
|
|
153
|
+
noticeShort: "Prices from the booking system",
|
|
154
|
+
noticeMore: "About this AI chat",
|
|
87
155
|
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.",
|
|
88
156
|
suggestions: [
|
|
89
|
-
"A
|
|
157
|
+
"A summer week for 2 adults and 2 kids",
|
|
90
158
|
"A week with our dog, ideally with a fenced garden",
|
|
91
159
|
"A weekend for two with a sauna",
|
|
92
|
-
"
|
|
160
|
+
"Anything free next weekend?"
|
|
93
161
|
],
|
|
94
|
-
placeholder: "
|
|
162
|
+
placeholder: "Describe your trip …",
|
|
95
163
|
send: "Send",
|
|
96
164
|
thinking: "Writing the answer",
|
|
165
|
+
status: {
|
|
166
|
+
search: "Searching rentals …",
|
|
167
|
+
rental: "Reading about the rental …",
|
|
168
|
+
availability: "Checking free days …",
|
|
169
|
+
quote: "Checking the price with the booking system …",
|
|
170
|
+
reviews: "Reading reviews …"
|
|
171
|
+
},
|
|
172
|
+
retry: "Send again",
|
|
173
|
+
privacyLink: "Privacy policy",
|
|
174
|
+
yourPick: "Your choice",
|
|
175
|
+
yourTrip: "Your trip",
|
|
176
|
+
assumed: "assumed",
|
|
177
|
+
guests: {
|
|
178
|
+
one: "{n} guest",
|
|
179
|
+
other: "{n} guests"
|
|
180
|
+
},
|
|
181
|
+
adultsShort: { other: "{n} ad." },
|
|
182
|
+
weekend: "Weekend",
|
|
183
|
+
tripStarters: {
|
|
184
|
+
party: "We are ",
|
|
185
|
+
period: "Other dates: ",
|
|
186
|
+
nights: "Other length: ",
|
|
187
|
+
wishes: "Other wishes: "
|
|
188
|
+
},
|
|
97
189
|
yourMessage: "Your message",
|
|
98
190
|
consent: "The chat is answered by an AI. Your messages are sent to our AI provider for that.",
|
|
99
191
|
consentAccept: "Start chat",
|
|
@@ -111,10 +203,31 @@ const DEFAULT_COPY = {
|
|
|
111
203
|
other: "{n} nights"
|
|
112
204
|
},
|
|
113
205
|
total: "Total",
|
|
206
|
+
totalForNights: {
|
|
207
|
+
one: "Total for {n} night",
|
|
208
|
+
other: "Total for {n} nights"
|
|
209
|
+
},
|
|
210
|
+
priceDetails: "Price details",
|
|
211
|
+
dealFallback: "Offer",
|
|
212
|
+
youSave: "You save {amount}",
|
|
213
|
+
freeCancellation: "Free cancellation until {date}",
|
|
214
|
+
nonRefundable: "Non-refundable",
|
|
215
|
+
arrival: "Arrival",
|
|
216
|
+
departure: "Departure",
|
|
217
|
+
continueToBooking: "Continue to booking",
|
|
218
|
+
nothingBookedYet: "Nothing is booked yet. On the booking page:",
|
|
219
|
+
handoverSteps: [
|
|
220
|
+
"Enter your details",
|
|
221
|
+
"Choose how to pay",
|
|
222
|
+
"Confirmation by email"
|
|
223
|
+
],
|
|
114
224
|
notBookable: "Not bookable for these dates",
|
|
115
225
|
photo: "Photo",
|
|
226
|
+
openPhotos: "View photos of {name}",
|
|
227
|
+
closePhotos: "Close photos",
|
|
116
228
|
previous: "Previous",
|
|
117
229
|
next: "Next",
|
|
230
|
+
rentalsFound: "Rentals found",
|
|
118
231
|
reviews: {
|
|
119
232
|
one: "{n} review",
|
|
120
233
|
other: "{n} reviews"
|
|
@@ -125,6 +238,19 @@ const DEFAULT_COPY = {
|
|
|
125
238
|
booked: "taken",
|
|
126
239
|
closed: "not bookable",
|
|
127
240
|
yourStay: "Your stay",
|
|
241
|
+
arrivalPossible: "Arrival possible",
|
|
242
|
+
cheaper: "cheaper",
|
|
243
|
+
dearer: "dearer",
|
|
244
|
+
showPrice: "Show price",
|
|
245
|
+
arrivalOn: "Arrival {date}",
|
|
246
|
+
possibleFrom: "Possible from {date}",
|
|
247
|
+
until: "until {date}",
|
|
248
|
+
otherArrival: "Choose another arrival",
|
|
249
|
+
otherArrivalStarter: "Arrival on ",
|
|
250
|
+
freePeriods: "Free dates",
|
|
251
|
+
freePeriodsIn: "Free dates in {month}",
|
|
252
|
+
datesDiffer: "Dates differ per rental",
|
|
253
|
+
otherDate: "other dates",
|
|
128
254
|
compareRooms: "Rooms",
|
|
129
255
|
compareHighlights: "Highlights",
|
|
130
256
|
compareRating: "Rating",
|
|
@@ -147,6 +273,10 @@ const DEFAULT_COPY = {
|
|
|
147
273
|
children: {
|
|
148
274
|
one: "{n} child",
|
|
149
275
|
other: "{n} children"
|
|
276
|
+
},
|
|
277
|
+
pets: {
|
|
278
|
+
one: "{n} pet",
|
|
279
|
+
other: "{n} pets"
|
|
150
280
|
}
|
|
151
281
|
}
|
|
152
282
|
};
|
|
@@ -246,6 +376,8 @@ ${SITE_COLORS.map((t) => ` ${t.chat}: var(${t.site}, ${t.own});`).join("\n")}
|
|
|
246
376
|
${SITE_RADIUS.chat}: var(${SITE_RADIUS.site}, ${SITE_RADIUS.own});
|
|
247
377
|
--chat-secondary: color-mix(in srgb, var(--chat-primary) 12%, var(--chat-surface));
|
|
248
378
|
--chat-on-secondary: var(--chat-text);
|
|
379
|
+
/* The tinted bubbles' edge: on a dark surface the tint alone barely stands off the message area. */
|
|
380
|
+
--chat-tint-edge: color-mix(in srgb, var(--chat-primary) 30%, transparent);
|
|
249
381
|
/* The small marks (the chat's name and sparkle, a card's place, icons, the ring): the primary,
|
|
250
382
|
unless a site sets an accent of its own (\`colors.accent\`). */
|
|
251
383
|
--chat-accent: var(--chat-primary);
|
|
@@ -255,15 +387,28 @@ ${SITE_COLORS.map((t) => ` ${t.chat}: var(${t.site}, ${t.own});`).join("\n")}
|
|
|
255
387
|
--chat-round: calc(var(--chat-base-radius) * 100);
|
|
256
388
|
--chat-shadow: 0 18px 48px rgb(15 23 42 / 0.18);
|
|
257
389
|
--chat-card-shadow: 0 1px 3px rgb(15 23 42 / 0.08), 0 8px 24px rgb(15 23 42 / 0.06);
|
|
258
|
-
/*
|
|
259
|
-
|
|
390
|
+
/* The stars' colour: dark amber on a light surface, light amber on a dark one, AA on both. */
|
|
391
|
+
--chat-rating: #8a5a00;
|
|
392
|
+
/* Not set here, so a site's value reaches the widget, on wide screens (from 1024px): \`--chat-top\`
|
|
393
|
+
keeps a sticky header uncovered, \`--chat-width\` widens the panel. A phone's sheet covers the
|
|
394
|
+
header: the panel is modal, so the header could not be tapped and would only cost height. */
|
|
395
|
+
}
|
|
396
|
+
@supports (color: oklch(from red calc(l + 0.1) c h)) {
|
|
397
|
+
:host { --chat-rating: oklch(from var(--chat-surface) calc(0.52 + (1 - l) * 0.35) 0.13 75);
|
|
398
|
+
/* Lines inside a raised dark card: the site's border lifted as the card is (cards below). */
|
|
399
|
+
--chat-card-border: oklch(from var(--chat-border) calc(l + max(0, 0.5 - l) * 0.6) c h); }
|
|
260
400
|
}
|
|
261
401
|
* { box-sizing: border-box; }
|
|
262
402
|
.chat { color: var(--chat-text); font-size: 15px; line-height: 1.45; }
|
|
403
|
+
/* One focus ring for every control, in the site's primary; a click or a tap shows none. Without
|
|
404
|
+
specificity (:where), so a control that needs its ring elsewhere sets it with a plain rule. */
|
|
405
|
+
:where(a, button, [tabindex]:not([tabindex="-1"])):focus-visible { outline: 2px solid var(--chat-primary);
|
|
406
|
+
outline-offset: 2px; }
|
|
263
407
|
|
|
264
408
|
/* The entry pill: frosted glass over any page, the chat's mark, a ring running round it until the
|
|
265
409
|
visitor has asked something. Phones tap it; wide screens type into it. */
|
|
266
|
-
.entry { position: fixed; right: 20px; bottom: 20px
|
|
410
|
+
.entry { position: fixed; right: 20px; bottom: calc(20px + env(safe-area-inset-bottom, 0px));
|
|
411
|
+
z-index: 2147483000; }
|
|
267
412
|
[data-position="left"] .entry { right: auto; left: 20px; }
|
|
268
413
|
.pill { position: relative; z-index: 1; display: inline-flex; align-items: center; gap: 12px;
|
|
269
414
|
padding: 8px 20px 8px 8px; border-radius: var(--chat-round);
|
|
@@ -297,72 +442,132 @@ ${SITE_COLORS.map((t) => ` ${t.chat}: var(${t.site}, ${t.own});`).join("\n")}
|
|
|
297
442
|
}
|
|
298
443
|
|
|
299
444
|
/* The panel: over the dimmed page, a card at the screen's edge; on a phone a sheet from the bottom. */
|
|
300
|
-
.scrim { position: fixed;
|
|
445
|
+
.scrim { position: fixed; inset: 0; z-index: 2147482999;
|
|
301
446
|
background: rgb(15 23 42 / 0.45); backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px); }
|
|
447
|
+
/* Clipped, not hidden, where supported: a clipping box is no scroll container, so no script, focus or
|
|
448
|
+
find-in-page can scroll the panel and shift its header out of view. */
|
|
302
449
|
.panel { position: fixed; z-index: 2147483000; left: 0; right: 0; bottom: 0;
|
|
303
|
-
top:
|
|
450
|
+
top: 12px; display: flex; flex-direction: column;
|
|
304
451
|
background: var(--chat-surface); border-radius: var(--chat-radius) var(--chat-radius) 0 0;
|
|
305
|
-
box-shadow: var(--chat-shadow); overflow: hidden; }
|
|
452
|
+
box-shadow: var(--chat-shadow); overflow: hidden; overflow: clip; }
|
|
306
453
|
.scrim[hidden], .panel[hidden] { display: none; }
|
|
454
|
+
/* Focused by script only (a phone reopening a conversation), never a stop for Tab. */
|
|
455
|
+
.panel:focus { outline: none; }
|
|
307
456
|
@media (min-width: 1024px) {
|
|
457
|
+
.scrim { top: var(--chat-top, 0px); }
|
|
308
458
|
.panel { left: auto; right: 20px; bottom: 20px; top: calc(var(--chat-top, 0px) + 20px);
|
|
309
459
|
width: min(var(--chat-width, 700px), calc(100vw - 40px)); border-radius: var(--chat-radius); }
|
|
310
460
|
[data-position="left"] .panel { right: auto; left: 20px; }
|
|
311
461
|
}
|
|
312
|
-
.header { display: flex; align-items: center; gap: 8px; padding:
|
|
313
|
-
|
|
314
|
-
|
|
462
|
+
.header { position: relative; display: flex; align-items: center; gap: 8px; padding: 12px 24px 4px; }
|
|
463
|
+
@media (min-width: 1024px) { .header { padding-top: 20px; } }
|
|
464
|
+
/* Once the conversation runs: one line under the title instead of the notice, the list gets the room. */
|
|
465
|
+
.header[data-slim] { padding: 10px 16px 10px 24px; border-bottom: 1px solid var(--chat-border); }
|
|
466
|
+
/* A small phone keeps the line on one line: less inset, and an ellipsis before it would wrap. */
|
|
467
|
+
@media (max-width: 359px) { .header[data-slim] { padding-inline: 16px 12px; } }
|
|
468
|
+
.title-block { margin-right: auto; display: flex; flex-direction: column; gap: 2px; min-width: 0; }
|
|
469
|
+
.title { margin: 0; display: flex; align-items: center; gap: 8px; font-size: 13px; font-weight: 600;
|
|
470
|
+
letter-spacing: 0.16em; text-transform: uppercase; color: var(--chat-accent); }
|
|
471
|
+
/* The persistent AI mark (AI Act transparency), in words, not colour. */
|
|
472
|
+
.badge { padding: 0 6px; border: 1px solid var(--chat-tint-edge); border-radius: min(6px, var(--chat-radius));
|
|
473
|
+
background: var(--chat-secondary); color: var(--chat-on-secondary); font-size: 12px; letter-spacing: 0.04em; }
|
|
474
|
+
.notice-short { margin: 0; display: flex; align-items: center; gap: 4px; min-width: 0; font-size: 12px;
|
|
475
|
+
color: var(--chat-muted); }
|
|
476
|
+
.notice-short > span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
477
|
+
/* A small mark with a finger's target: the pseudo-element takes the tap around it. */
|
|
478
|
+
.info { position: relative; display: grid; place-items: center; width: 20px; height: 20px; padding: 0; border: 0;
|
|
479
|
+
border-radius: 999px; background: transparent; color: inherit; cursor: pointer; }
|
|
480
|
+
.info::after { content: ""; position: absolute; inset: -12px; }
|
|
481
|
+
.info svg { width: 14px; height: 14px; }
|
|
482
|
+
.notice-pop { position: absolute; z-index: 2; top: calc(100% + 6px); left: 16px; right: 16px; margin: 0;
|
|
483
|
+
padding: 12px 16px; border: 1px solid var(--chat-border); border-radius: calc(var(--chat-radius) * 0.75);
|
|
484
|
+
background: var(--chat-surface); box-shadow: var(--chat-shadow); font-size: 13px; color: var(--chat-text); }
|
|
315
485
|
.icon-button { flex: none; display: grid; place-items: center; width: 40px; height: 40px; padding: 0;
|
|
316
486
|
border: 1px solid var(--chat-border); border-radius: var(--chat-round); background: var(--chat-surface);
|
|
317
487
|
color: var(--chat-text); cursor: pointer; }
|
|
318
|
-
.icon-button:hover { border-color: var(--chat-primary); }
|
|
319
488
|
/* Closing is the panel's one strong control, filled like the site's buttons. */
|
|
320
489
|
.close { background: var(--chat-primary); border-color: var(--chat-primary); color: var(--chat-on-primary); }
|
|
321
|
-
.
|
|
322
|
-
|
|
323
|
-
|
|
490
|
+
.notice { margin: 0; padding: 0 24px 14px; font-size: 13px; color: var(--chat-muted); }
|
|
491
|
+
/* Positioned: the absolute speaker labels (.sr-only) scroll with the list instead of giving the panel a
|
|
492
|
+
scroll range, and a turn's offsetTop is measured from the list. */
|
|
493
|
+
.messages { position: relative; flex: 1; overflow-y: auto; overscroll-behavior: contain; padding: 24px 24px 40px;
|
|
324
494
|
display: flex; flex-direction: column; gap: 16px; background: var(--chat-surface-soft);
|
|
325
495
|
container-type: inline-size; }
|
|
326
496
|
.turn { display: flex; flex-direction: column; }
|
|
327
497
|
.message { max-width: 88%; white-space: pre-wrap; overflow-wrap: anywhere; }
|
|
328
498
|
.message[data-role="visitor"] { align-self: flex-end; margin-left: 48px; padding: 12px 16px;
|
|
329
|
-
border-radius: calc(var(--chat-radius) * 0.75);
|
|
499
|
+
border: 1px solid var(--chat-tint-edge); border-radius: calc(var(--chat-radius) * 0.75);
|
|
500
|
+
background: var(--chat-secondary);
|
|
330
501
|
color: var(--chat-on-secondary); }
|
|
331
502
|
.message[data-role="assistant"] { align-self: flex-start; max-width: 100%; font-size: 16px; line-height: 1.6; }
|
|
332
503
|
.message strong { font-weight: 600; }
|
|
333
504
|
.ui { align-self: stretch; display: flex; flex-direction: column; gap: 12px; }
|
|
334
|
-
.thinking { display: flex; gap: 5px; padding: 4px 2px; }
|
|
505
|
+
.thinking { display: flex; align-items: center; gap: 5px; padding: 4px 2px; }
|
|
506
|
+
.thinking-label { margin-left: 6px; color: var(--chat-muted); font-size: 14px; }
|
|
335
507
|
.thinking i { width: 7px; height: 7px; border-radius: 999px; background: var(--chat-accent); opacity: 0.5; }
|
|
336
|
-
.dock { position: relative; padding:
|
|
508
|
+
.dock { position: relative; padding: 8px 12px calc(14px + env(safe-area-inset-bottom, 0px));
|
|
509
|
+
background: var(--chat-surface-soft); container: dock / inline-size; }
|
|
337
510
|
.dock::before { content: ""; position: absolute; left: 0; right: 0; top: -28px; height: 28px;
|
|
338
511
|
pointer-events: none; background: linear-gradient(to top, var(--chat-surface-soft), transparent); }
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
512
|
+
/* Suggestions wrap, on a phone too: in a row that scrolls, one cut chip hides the others. */
|
|
513
|
+
.suggestions { display: flex; flex-wrap: wrap; gap: 8px; margin: 2px 8px 10px; }
|
|
514
|
+
/* Suggestions and quick replies: filled pills; a suggestion has an icon for its topic in the accent
|
|
515
|
+
colour (a quick reply is the model's words, an icon guessed from them can be wrong). */
|
|
343
516
|
.suggestion, .chip { flex: none; max-width: 100%; display: inline-flex; align-items: center; gap: 8px;
|
|
344
|
-
border: 1px solid
|
|
517
|
+
border: 1px solid var(--chat-tint-edge); border-radius: var(--chat-round); background: var(--chat-secondary);
|
|
345
518
|
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;
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
.chip {
|
|
350
|
-
.
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
.suggestion { white-space: normal; }
|
|
354
|
-
}
|
|
519
|
+
font-weight: 600; line-height: 1.35; text-align: left; cursor: pointer; }
|
|
520
|
+
/* A quick reply is a tinted pill, never outlined: outlined is a link out of the chat (ContactLink). The
|
|
521
|
+
one the visitor took goes quiet. */
|
|
522
|
+
.chip { padding-left: 16px; }
|
|
523
|
+
.chip[data-chosen] { border-color: var(--chat-border); background: transparent; color: var(--chat-muted);
|
|
524
|
+
cursor: default; }
|
|
525
|
+
.suggestion svg { flex: none; color: var(--chat-accent); }
|
|
355
526
|
.composer { display: flex; align-items: flex-end; gap: 8px; padding: 6px 6px 6px 18px;
|
|
356
527
|
border: 1px solid var(--chat-border); border-radius: var(--chat-round); background: var(--chat-surface);
|
|
357
528
|
box-shadow: 0 4px 16px rgb(15 23 42 / 0.08); }
|
|
358
|
-
|
|
529
|
+
/* The field's ring, not the send button's too (that one has its own), and only for a keyboard: a text
|
|
530
|
+
field matches :focus-visible after a tap as well. */
|
|
531
|
+
.composer[data-keyboard]:has(textarea:focus-visible) { outline: 2px solid var(--chat-primary); outline-offset: 2px; }
|
|
359
532
|
/* 16px keeps iOS from zooming into the field. */
|
|
360
533
|
.composer textarea { flex: 1; min-width: 0; min-height: 36px; max-height: 128px; resize: none; border: 0;
|
|
361
534
|
outline: none; background: transparent; padding: 6px 0; font: inherit; font-size: 16px;
|
|
362
535
|
line-height: 1.5; color: inherit; field-sizing: content; }
|
|
536
|
+
/* The field's only visible label: the muted text colour, not the browser's grey (below AA in dark). */
|
|
537
|
+
.composer textarea::placeholder { color: var(--chat-muted); opacity: 1; }
|
|
363
538
|
.send { flex: none; display: grid; place-items: center; width: 36px; height: 36px; padding: 0; border: 0;
|
|
364
539
|
border-radius: var(--chat-round); background: var(--chat-primary); color: var(--chat-on-primary); cursor: pointer; }
|
|
365
540
|
.send:disabled { opacity: 0.3; cursor: not-allowed; }
|
|
541
|
+
/* The bar over the input: the stay in reach with its total and button, or what the chat searched with.
|
|
542
|
+
The composer's shape, one thumb above it. */
|
|
543
|
+
.dock-bar { display: flex; align-items: center; gap: 8px; min-height: 50px; margin-bottom: 8px; padding: 3px 3px 3px 16px;
|
|
544
|
+
border: 1px solid var(--chat-border); border-radius: var(--chat-round); background: var(--chat-surface);
|
|
545
|
+
box-shadow: 0 4px 16px rgb(15 23 42 / 0.08); }
|
|
546
|
+
.dock-bar .thumb { display: none; width: 40px; height: 40px; margin-left: -10px; border-radius: 999px; }
|
|
547
|
+
/* A wide panel has room for the photo; a phone needs the width for the party. */
|
|
548
|
+
@container dock (min-width: 456px) { .dock-bar .thumb { display: block; } }
|
|
549
|
+
.dock-text { flex: 1; min-width: 0; display: flex; flex-direction: column; align-items: flex-start; padding: 0;
|
|
550
|
+
border: 0; background: none; color: inherit; font: inherit; line-height: 1.3; text-align: left; cursor: pointer; }
|
|
551
|
+
.dock-name, .dock-sub { max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
552
|
+
.dock-name { font-size: 14px; font-weight: 600; }
|
|
553
|
+
.dock-sub { font-size: 12px; color: var(--chat-muted); }
|
|
554
|
+
.dock-sub .price { color: var(--chat-text); }
|
|
555
|
+
/* Narrower than a card's button: the bar's width goes to the party. */
|
|
556
|
+
.dock-bar .button.compact { margin: 0; min-height: 42px; padding: 0 11px; font-size: 13px; }
|
|
557
|
+
.dock-bar[data-kind="trip"] { padding: 3px 0 3px 6px; }
|
|
558
|
+
.trip-chips { flex: 1; min-width: 0; display: flex; gap: 4px; padding-right: 16px; overflow-x: auto;
|
|
559
|
+
scrollbar-width: none; mask-image: linear-gradient(to left, transparent, #000 16px); }
|
|
560
|
+
.trip-chips::-webkit-scrollbar { display: none; }
|
|
561
|
+
.trip-chip { flex: none; display: inline-flex; align-items: center; gap: 5px; min-height: 36px; padding: 0 10px;
|
|
562
|
+
border: 1px solid var(--chat-border); border-radius: var(--chat-round); background: transparent;
|
|
563
|
+
color: var(--chat-text); font: inherit; font-size: 13px; font-weight: 600; white-space: nowrap; cursor: pointer; }
|
|
564
|
+
/* Inside the row that scrolls, so it clips: the ring sits inside the chip. */
|
|
565
|
+
.trip-chip:focus-visible { outline-offset: -2px; }
|
|
566
|
+
/* A party the search assumed: dashed, and said in words. */
|
|
567
|
+
.trip-chip[data-assumed] { border-style: dashed; border-color: var(--chat-muted); }
|
|
568
|
+
.trip-chip em { font-style: normal; font-weight: 500; color: var(--chat-muted); }
|
|
569
|
+
/* The bar's stay, brought into view in the list: marked for a moment. */
|
|
570
|
+
[data-pulse] { outline: 2px solid var(--chat-primary); outline-offset: 2px; }
|
|
366
571
|
.consent { flex: 1; overflow-y: auto; padding: 24px; display: flex; flex-direction: column; gap: 14px;
|
|
367
572
|
background: var(--chat-surface-soft); }
|
|
368
573
|
.consent p { margin: 0; }
|
|
@@ -374,8 +579,11 @@ ${SITE_COLORS.map((t) => ` ${t.chat}: var(${t.site}, ${t.own});`).join("\n")}
|
|
|
374
579
|
white-space: pre-wrap; }
|
|
375
580
|
/* The bubble's own text colour, softened: the muted grey suits the surface, not a set secondary. */
|
|
376
581
|
.pending figcaption { margin-bottom: 2px; color: inherit; opacity: 0.75; }
|
|
377
|
-
|
|
582
|
+
/* The chat cannot answer: its reply to the visitor's message, the ways on as buttons. */
|
|
583
|
+
.fallback { display: flex; flex-direction: column; gap: 12px; }
|
|
378
584
|
.fallback p { margin: 0; }
|
|
585
|
+
.fallback-links { display: flex; flex-wrap: wrap; gap: 8px; }
|
|
586
|
+
.fallback-links > .button { margin: 0; }
|
|
379
587
|
|
|
380
588
|
/* What the replies show. */
|
|
381
589
|
.card { border-radius: var(--chat-radius); overflow: hidden; background: var(--chat-surface);
|
|
@@ -383,8 +591,26 @@ ${SITE_COLORS.map((t) => ` ${t.chat}: var(${t.site}, ${t.own});`).join("\n")}
|
|
|
383
591
|
.gallery-wrap, .carousel-wrap { position: relative; }
|
|
384
592
|
.gallery, .carousel { display: flex; overflow-x: auto; scroll-snap-type: x mandatory; scrollbar-width: none; }
|
|
385
593
|
.gallery::-webkit-scrollbar, .carousel::-webkit-scrollbar { display: none; }
|
|
386
|
-
.gallery img { flex: 0 0 100%; scroll-snap-align: start; display: block; width: 100%;
|
|
387
|
-
height: auto; object-fit: cover; }
|
|
594
|
+
.gallery img, .photo-open { flex: 0 0 100%; scroll-snap-align: start; display: block; width: 100%;
|
|
595
|
+
aspect-ratio: 4 / 3; height: auto; object-fit: cover; }
|
|
596
|
+
/* Flatter in a row, to bring a card's price above the input on a phone: 5:3, and 2:1 on a card with
|
|
597
|
+
free periods; the single card keeps 4:3. */
|
|
598
|
+
.carousel :is(.gallery > img, .photo-open), .carousel .no-photo { aspect-ratio: 5 / 3; }
|
|
599
|
+
.carousel .rental-card:has(.periods) :is(.gallery > img, .photo-open, .no-photo) { aspect-ratio: 2 / 1; }
|
|
600
|
+
/* A card's photo opens the photos large. */
|
|
601
|
+
.photo-open { padding: 0; border: 0; background: none; cursor: zoom-in; }
|
|
602
|
+
.photo-open img { aspect-ratio: auto; height: 100%; }
|
|
603
|
+
.photo-open:focus-visible { outline-offset: -3px; }
|
|
604
|
+
.photo-count { position: absolute; top: 10px; right: 10px; padding: 3px 10px; border-radius: var(--chat-round);
|
|
605
|
+
background: rgb(15 23 42 / 0.55); color: #fff; font-size: 12px; font-weight: 600;
|
|
606
|
+
font-variant-numeric: tabular-nums; pointer-events: none; }
|
|
607
|
+
/* Place and score on the photo's foot, white on a dark gradient whatever the photo. */
|
|
608
|
+
.photo-meta { position: absolute; left: 0; right: 0; bottom: 0; display: flex; align-items: flex-end;
|
|
609
|
+
justify-content: space-between; gap: 8px; padding: 36px 16px 12px; pointer-events: none;
|
|
610
|
+
background: linear-gradient(transparent, rgb(15 23 42 / 0.62)); color: #fff; font-size: 14px; font-weight: 600; }
|
|
611
|
+
.photo-rating { flex: none; }
|
|
612
|
+
.photo-rating .star { color: #ffd166; }
|
|
613
|
+
.photo-rating small { font-size: 12px; font-weight: 500; opacity: 0.9; }
|
|
388
614
|
/* Dots say which photo shows; arrows come up under a pointer and on keyboard focus (a finger swipes:
|
|
389
615
|
on a touch screen they take no tap, so a tap on a photo's edge does not turn it unseen). */
|
|
390
616
|
.dots { position: absolute; left: 50%; bottom: 10px; translate: -50% 0; display: flex; gap: 4px; }
|
|
@@ -397,7 +623,7 @@ ${SITE_COLORS.map((t) => ` ${t.chat}: var(${t.site}, ${t.own});`).join("\n")}
|
|
|
397
623
|
box-shadow: 0 2px 8px rgb(0 0 0 / 0.18); cursor: pointer; opacity: 0; }
|
|
398
624
|
.gallery-arrow[data-side="prev"] { left: 10px; }
|
|
399
625
|
.gallery-arrow[data-side="next"] { right: 10px; }
|
|
400
|
-
.gallery-
|
|
626
|
+
.gallery-arrow:focus-visible { opacity: 1; }
|
|
401
627
|
@media (hover: none) { .gallery-arrow:not(:focus-visible) { pointer-events: none; } }
|
|
402
628
|
/* The row scrolls, so it clips: room below for the cards' shadow, taken back from the gap under it. */
|
|
403
629
|
.carousel { gap: 12px; padding: 2px 2px 16px; margin-bottom: -10px; }
|
|
@@ -407,105 +633,382 @@ ${SITE_COLORS.map((t) => ` ${t.chat}: var(${t.site}, ${t.own});`).join("\n")}
|
|
|
407
633
|
@container (min-width: 520px) {
|
|
408
634
|
.carousel > .card { flex-basis: calc((100% - 24px) / 2.15); }
|
|
409
635
|
.carousel:has(> .card:nth-child(2):last-child) > .card { flex-basis: calc((100% - 12px) / 2); }
|
|
410
|
-
.carousel-wrap:has(> .carousel > .card:nth-child(2):last-child) > .snap-nav
|
|
411
|
-
|
|
412
|
-
|
|
636
|
+
.carousel-wrap:has(> .carousel > .card:nth-child(2):last-child) > .snap-nav > :not(.snap-caption) {
|
|
637
|
+
display: none; }
|
|
638
|
+
/* The count follows the first card in view, and two show at once: it would skip numbers. */
|
|
639
|
+
.carousel-wrap > .snap-nav > .snap-count { display: none; }
|
|
413
640
|
}
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
.snap-
|
|
419
|
-
.
|
|
420
|
-
.
|
|
421
|
-
|
|
641
|
+
/* The row's controls, grouped at its end: "‹ 2 / 8 ›" under a pointer, dots under a finger; a caption
|
|
642
|
+
at its start. */
|
|
643
|
+
.snap-nav { display: flex; align-items: center; justify-content: flex-end; gap: 8px; margin-top: 6px; }
|
|
644
|
+
.snap-caption { flex: 1; min-width: 0; color: var(--chat-muted); font-size: 13px; }
|
|
645
|
+
.snap-dots { display: none; gap: 5px; }
|
|
646
|
+
.snap-dots i { width: 7px; height: 7px; border-radius: 999px; background: var(--chat-border); }
|
|
647
|
+
.snap-dots i[data-current] { width: 18px; background: var(--chat-primary); }
|
|
648
|
+
@media (pointer: coarse) {
|
|
649
|
+
.snap-dots { display: flex; }
|
|
650
|
+
.snap-nav > button.snap-arrow, .snap-count { display: none; }
|
|
651
|
+
}
|
|
652
|
+
/* More cards beyond an edge fade out there. Cards snap to the row's start, so in the middle of the row
|
|
653
|
+
nothing of the previous card shows: only the end fades, never the card being read. At the row's end
|
|
654
|
+
the previous card peeks in, and its edge fades. */
|
|
655
|
+
.carousel:is([data-fade="end"], [data-fade="both"]) { mask-image: linear-gradient(to left, transparent, #000 44px); }
|
|
656
|
+
.carousel[data-fade="start"] { mask-image: linear-gradient(to right, transparent, #000 44px); }
|
|
657
|
+
.snap-nav button, .calendar-head button { flex: none; display: grid; place-items: center; width: 36px;
|
|
658
|
+
height: 36px; padding: 0; border: 1px solid var(--chat-border); background: var(--chat-surface);
|
|
659
|
+
color: var(--chat-text); border-radius: var(--chat-round); cursor: pointer; }
|
|
660
|
+
.snap-nav button:disabled, .calendar-head button:disabled { opacity: 0.5; cursor: default; }
|
|
661
|
+
.rating { color: var(--chat-rating); font-weight: 700; }
|
|
662
|
+
.breakdown .line, .price-details .line { display: flex; justify-content: space-between; gap: 12px;
|
|
663
|
+
font-variant-numeric: tabular-nums; }
|
|
664
|
+
.breakdown .total, .price-details .total { border-top: 1px solid var(--chat-border); padding-top: 6px; margin-top: 2px;
|
|
422
665
|
font-weight: 700; }
|
|
423
|
-
.
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
666
|
+
.quick-replies { display: flex; flex-wrap: wrap; gap: 6px; }
|
|
667
|
+
/* A chip on its own in a reply (a refused stay's "Anderen Anreisetag wählen"): as wide as its words. */
|
|
668
|
+
.ui > .chip { align-self: flex-start; }
|
|
669
|
+
/* Rows that choose or ask: other free periods (a radio mark, the total right-aligned), a refused stay's
|
|
670
|
+
allowed lengths ("Preis anzeigen"). Full width, a finger's height. */
|
|
671
|
+
.rows-head { display: flex; align-items: center; gap: 10px; padding: 12px 16px;
|
|
672
|
+
border-bottom: 1px solid var(--chat-border); }
|
|
673
|
+
.rows-head > div { display: flex; flex-direction: column; min-width: 0; line-height: 1.3; }
|
|
674
|
+
.rows-head strong { font-weight: 600; }
|
|
675
|
+
.rows-head .thumb { width: 40px; height: 40px; }
|
|
676
|
+
.row { display: flex; align-items: center; gap: 12px; width: 100%; min-height: 60px; padding: 8px 16px;
|
|
677
|
+
border: 0; border-top: 1px solid var(--chat-border); background: transparent; color: inherit; font: inherit;
|
|
678
|
+
text-align: left; cursor: pointer; }
|
|
679
|
+
.rows > .row:first-child { border-top: 0; }
|
|
680
|
+
.row:disabled { cursor: default; }
|
|
681
|
+
/* Inside the card, which clips: the ring sits inside the row. */
|
|
682
|
+
.row:focus-visible { outline-offset: -2px; }
|
|
683
|
+
.row[aria-pressed="true"] { background: var(--chat-secondary); box-shadow: inset 3px 0 0 var(--chat-primary); }
|
|
684
|
+
.row-when { flex: 1; min-width: 0; display: flex; flex-direction: column; line-height: 1.3; }
|
|
685
|
+
.row-when strong { font-weight: 600; }
|
|
686
|
+
.row-when span { color: var(--chat-muted); font-size: 13px; }
|
|
687
|
+
.row > .price { flex: none; white-space: nowrap; }
|
|
688
|
+
/* The nights and a discount's badge: side by side, or the badge under the nights where the row is narrow. */
|
|
689
|
+
.row-when > span:has(.deal) { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 6px; }
|
|
690
|
+
.row-when .deal { color: var(--chat-on-secondary); }
|
|
691
|
+
.row-go { display: inline-flex; align-items: center; gap: 4px; color: var(--chat-primary); font-size: 14px;
|
|
692
|
+
font-weight: 600; white-space: nowrap; }
|
|
693
|
+
.radio { flex: none; display: grid; place-items: center; width: 22px; height: 22px; border-radius: 50%;
|
|
694
|
+
border: 1.5px solid var(--chat-muted); }
|
|
695
|
+
.row[aria-pressed="true"] .radio { border-color: var(--chat-primary); background: var(--chat-primary);
|
|
696
|
+
color: var(--chat-on-primary); }
|
|
697
|
+
.refused .card-body { padding-bottom: 12px; }
|
|
698
|
+
.refused .rows { border-top: 1px solid var(--chat-border); }
|
|
699
|
+
/* A card's free periods from a flexible search: pills that switch its stay in place. */
|
|
700
|
+
.periods { margin: 0 18px; padding: 10px 0 12px; border-top: 1px solid var(--chat-border); }
|
|
701
|
+
.periods-caption { margin: 0 0 6px; font-size: 13px; color: var(--chat-muted); }
|
|
702
|
+
/* "24. Apr. – 1. Mai" needs about 115px: two to a carousel card's row, one on a small phone. Days of one
|
|
703
|
+
month ("14.–21.") fit three or four to a row. */
|
|
704
|
+
.period-pills { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 124px), 1fr)); gap: 6px; }
|
|
705
|
+
.period-pills[data-one-month] { grid-template-columns: repeat(auto-fit, minmax(60px, 1fr)); }
|
|
706
|
+
.period-pill { min-height: 38px; padding: 0 8px; border: 1px solid var(--chat-border);
|
|
707
|
+
border-radius: var(--chat-round); background: transparent; color: var(--chat-text); font: inherit;
|
|
708
|
+
font-size: 13px; font-weight: 600; white-space: nowrap; cursor: pointer; }
|
|
709
|
+
.period-pill:disabled { cursor: default; }
|
|
710
|
+
.period-pill[aria-pressed="true"] { border-color: var(--chat-primary); background: var(--chat-primary);
|
|
711
|
+
color: var(--chat-on-primary); }
|
|
712
|
+
.periods + .card-foot { border-top: 0; padding-top: 0; }
|
|
713
|
+
/* Compare: a stay on other dates than the first column's says so, in words. */
|
|
714
|
+
.other-date { display: inline-block; margin-left: 6px; padding: 0 6px; border: 1px solid var(--chat-border);
|
|
715
|
+
border-radius: var(--chat-round); color: var(--chat-muted); font-size: 12px; }
|
|
427
716
|
.calendar { border: 1px solid var(--chat-border); border-radius: var(--chat-radius); padding: 12px;
|
|
428
717
|
background: var(--chat-surface); }
|
|
429
718
|
.calendar-head { display: flex; align-items: center; justify-content: space-between; gap: 8px;
|
|
430
719
|
font-weight: 700; margin-bottom: 8px; text-align: center; }
|
|
431
720
|
.calendar-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 3px; text-align: center;
|
|
432
|
-
font-size:
|
|
721
|
+
font-size: 13px; }
|
|
433
722
|
.weekday { color: var(--chat-muted); font-weight: 600; }
|
|
434
723
|
.day { padding: 4px 0; border-radius: min(6px, var(--chat-radius)); }
|
|
435
724
|
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%);
|
|
436
725
|
white-space: nowrap; }
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
.day
|
|
440
|
-
|
|
441
|
-
|
|
726
|
+
/* Read without colour: free days plain, taken ones hatched with a muted numeral, arrival days a dot and a
|
|
727
|
+
bold numeral, a free day's price level one to three ticks; the stay filled. */
|
|
728
|
+
.day { position: relative; display: flex; flex-direction: column; align-items: center; justify-content: center;
|
|
729
|
+
gap: 3px; min-height: 40px; padding: 0; border: 0; background: transparent; color: inherit; font: inherit;
|
|
730
|
+
font-variant-numeric: tabular-nums; }
|
|
731
|
+
button.day { cursor: pointer; }
|
|
732
|
+
.day[data-status="booked"], .legend i[data-status="booked"] { color: var(--chat-muted);
|
|
733
|
+
background: repeating-linear-gradient(135deg, transparent 0 4px, var(--chat-border) 4px 5.5px);
|
|
734
|
+
box-shadow: inset 0 0 0 1px var(--chat-border); }
|
|
442
735
|
.day[data-status="closed"] { color: var(--chat-muted); opacity: 0.6; }
|
|
443
|
-
.day[data-
|
|
444
|
-
|
|
445
|
-
.legend {
|
|
446
|
-
|
|
447
|
-
|
|
736
|
+
.day[data-arrival] { font-weight: 700; }
|
|
737
|
+
/* At the numeral, however wide the cell. */
|
|
738
|
+
.day[data-arrival]::before, .legend i[data-arrival] { content: ""; position: absolute; top: 4px;
|
|
739
|
+
right: calc(50% - 16px);
|
|
740
|
+
width: 5px; height: 5px; border-radius: 50%; background: var(--chat-primary); }
|
|
741
|
+
.ticks { display: flex; gap: 2px; height: 3px; }
|
|
742
|
+
.ticks i { width: 6px; height: 3px; border-radius: 2px; background: var(--chat-muted); }
|
|
743
|
+
.day[data-stay], .legend i[data-stay], .day[aria-pressed="true"] { background: var(--chat-primary);
|
|
744
|
+
color: var(--chat-on-primary); }
|
|
745
|
+
.day[data-stay]::before, .day[data-stay] .ticks i, .day[aria-pressed="true"]::before,
|
|
746
|
+
.day[aria-pressed="true"] .ticks i { background: var(--chat-on-primary); }
|
|
747
|
+
/* A tapped arrival is the one filled day: the marked stay steps back to a tint meanwhile. */
|
|
748
|
+
.calendar-grid:has(.day[aria-pressed="true"]) .day[data-stay]:not([aria-pressed="true"]) {
|
|
749
|
+
background: var(--chat-secondary); color: var(--chat-on-secondary); }
|
|
750
|
+
.calendar-grid:has(.day[aria-pressed="true"]) .day[data-stay]:not([aria-pressed="true"])::before {
|
|
751
|
+
background: var(--chat-primary); }
|
|
752
|
+
.calendar-grid:has(.day[aria-pressed="true"]) .day[data-stay]:not([aria-pressed="true"]) .ticks i {
|
|
753
|
+
background: var(--chat-muted); }
|
|
754
|
+
/* A finger's height for a day that takes a tap. */
|
|
755
|
+
@media (pointer: coarse) { button.day { min-height: 44px; } }
|
|
756
|
+
.cal-foot { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 10px;
|
|
757
|
+
padding-top: 10px; border-top: 1px solid var(--chat-border); }
|
|
758
|
+
.cal-foot strong { font-weight: 600; white-space: nowrap; }
|
|
759
|
+
.cal-foot .button { gap: 6px; }
|
|
760
|
+
/* Forced colours drop tints and backgrounds: borders and the system's text colour carry the marks. */
|
|
761
|
+
@media (forced-colors: active) {
|
|
762
|
+
.day[data-status="booked"], .legend i[data-status="booked"] { background: none; border: 1px dashed CanvasText;
|
|
763
|
+
text-decoration: line-through; }
|
|
764
|
+
.day[data-arrival]::before, .ticks i, .legend i[data-arrival] { background: CanvasText; }
|
|
765
|
+
.day[data-stay], .day[aria-pressed="true"] { border: 2px solid Highlight; }
|
|
766
|
+
/* The stay and the tapped day share that border: the tapped one gets a second mark. */
|
|
767
|
+
.day[aria-pressed="true"] { outline: 2px solid Highlight; outline-offset: -5px; }
|
|
768
|
+
}
|
|
769
|
+
.legend { display: flex; flex-wrap: wrap; gap: 6px 12px; margin-top: 8px; font-size: 13px; }
|
|
770
|
+
.legend > span { display: inline-flex; align-items: center; gap: 4px; }
|
|
771
|
+
.legend > span > i { display: inline-block; width: 12px; height: 12px; border-radius: 3px;
|
|
772
|
+
border: 1px solid var(--chat-border); }
|
|
773
|
+
.legend > span > i[data-arrival] { position: static; width: 6px; height: 6px; border: 0; }
|
|
774
|
+
.legend-levels .ticks { align-self: center; }
|
|
448
775
|
.compare { display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; gap: 8px; }
|
|
449
776
|
.compare-col { border-radius: var(--chat-radius); padding: 10px; background: var(--chat-surface);
|
|
450
|
-
box-shadow: var(--chat-card-shadow); display: flex; flex-direction: column; gap: 6px; font-size:
|
|
451
|
-
min-width: 0; }
|
|
452
|
-
/* Three columns leave a button about 90px: "Zur Buchung" fits on one line only without side padding. */
|
|
453
|
-
.compare-col .button { padding: 8px 2px; font-size: 13px; }
|
|
777
|
+
box-shadow: var(--chat-card-shadow); display: flex; flex-direction: column; gap: 6px; font-size: 14px;
|
|
778
|
+
min-width: 0; hyphens: auto; }
|
|
454
779
|
.compare-col dl { margin: 0; display: flex; flex-direction: column; gap: 2px; flex: 1; }
|
|
455
|
-
.compare-col dt { color: var(--chat-muted); font-size:
|
|
456
|
-
.compare-col dd { margin: 0; overflow-wrap:
|
|
780
|
+
.compare-col dt { color: var(--chat-muted); font-size: 12px; margin-top: 4px; }
|
|
781
|
+
.compare-col dd { margin: 0; overflow-wrap: break-word; }
|
|
782
|
+
.compare-col .price { font-size: 16px; }
|
|
783
|
+
/* A phone has room for two columns, not three of 100px with a word on each line: the third peeks in
|
|
784
|
+
and the row scrolls, with room below for the columns' shadow, as the carousel. */
|
|
785
|
+
@container (max-width: 519px) {
|
|
786
|
+
/* A phone shows one card at a time: each as tall as its own content, so the first card's price and
|
|
787
|
+
button are not pushed down to the tallest card's foot. */
|
|
788
|
+
.carousel { align-items: flex-start; }
|
|
789
|
+
.compare { grid-auto-columns: minmax(150px, 1fr); overflow-x: auto; scroll-snap-type: x mandatory;
|
|
790
|
+
scrollbar-width: none; padding: 2px 2px 16px; margin-bottom: -10px; }
|
|
791
|
+
.compare::-webkit-scrollbar { display: none; }
|
|
792
|
+
.compare-col { scroll-snap-align: start; }
|
|
793
|
+
/* The rows line up across the columns: one long name off screen would push every visible row down. */
|
|
794
|
+
.compare-col > strong { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
|
|
795
|
+
overflow: hidden; }
|
|
796
|
+
}
|
|
797
|
+
/* Side by side, the rows line up across the columns (a long name pushes every row of its column down
|
|
798
|
+
otherwise): the name, the list's eight cells and the button share the comparison's ten rows. */
|
|
799
|
+
@supports (grid-template-rows: subgrid) {
|
|
800
|
+
.compare { grid-template-rows: repeat(10, auto); row-gap: 0; }
|
|
801
|
+
.compare-col { display: grid; grid-row: span 10; grid-template-rows: subgrid; }
|
|
802
|
+
.compare-col dl { display: grid; grid-row: span 8; grid-template-rows: subgrid; }
|
|
803
|
+
.compare-col .button { grid-row: -2; }
|
|
804
|
+
}
|
|
457
805
|
.card-body { padding: 16px 18px 12px; display: flex; flex-direction: column; gap: 6px; }
|
|
458
806
|
.card-title { margin: 0; font-size: 18px; font-weight: 500; line-height: 1.3; }
|
|
807
|
+
/* One long name (v9 names often run to a dozen words) would make every card of a row taller: in a
|
|
808
|
+
carousel the name stops at three lines, as in a narrow Compare; the single card and the quote show it
|
|
809
|
+
whole. */
|
|
810
|
+
.carousel .card-title { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
|
|
811
|
+
overflow: hidden; }
|
|
459
812
|
/* A rental card: place and score, name, facts and wishes; the stay's price and button below a line. */
|
|
460
813
|
.rental-card, .stay-card { display: flex; flex-direction: column; }
|
|
461
814
|
.rental-card .card-body { flex: 1; }
|
|
462
815
|
.card-meta { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-height: 24px; }
|
|
463
816
|
.place { color: var(--chat-accent); font-size: 14px; }
|
|
464
|
-
.
|
|
465
|
-
|
|
817
|
+
.card-meta .photo-rating { margin-left: auto; font-size: 14px; font-weight: 600; }
|
|
818
|
+
.card-meta .photo-rating .star { color: var(--chat-rating); }
|
|
466
819
|
.specs { margin: 0; color: var(--chat-muted); font-size: 14px; }
|
|
467
820
|
.card-foot { display: flex; align-items: center; justify-content: space-between; gap: 12px;
|
|
468
821
|
margin: 0 18px; padding: 12px 0 16px; border-top: 1px solid var(--chat-border); }
|
|
469
822
|
.foot-price { display: flex; flex-direction: column; min-width: 0; }
|
|
470
823
|
.foot-price .price { font-size: 18px; line-height: 1.3; }
|
|
471
|
-
.foot-meta { color: var(--chat-muted); font-size:
|
|
824
|
+
.foot-meta { color: var(--chat-muted); font-size: 14px; }
|
|
825
|
+
.foot-meta .period { white-space: nowrap; }
|
|
472
826
|
.button.compact { margin: 0 0 0 auto; padding: 9px 20px; font-size: 14px; white-space: nowrap; }
|
|
473
827
|
.muted { color: var(--chat-muted); font-size: 13px; }
|
|
474
|
-
.price { font-weight: 700; }
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
828
|
+
.price { font-weight: 700; font-variant-numeric: tabular-nums; }
|
|
829
|
+
/* "Gesamtpreis für 7 Nächte": what a total covers, over it. */
|
|
830
|
+
.price-label { color: var(--chat-muted); font-size: 13px; }
|
|
831
|
+
.price-line { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 8px; }
|
|
832
|
+
/* Relative: the link's screen-reader name (an absolute .sr-only) stays inside a scrolled row, which
|
|
833
|
+
clips it, instead of sticking out of the row and widening the panel. */
|
|
834
|
+
.button { position: relative; display: inline-flex; align-items: center; justify-content: center; margin-top: 6px;
|
|
835
|
+
text-align: center; text-decoration: none; padding: 10px 16px; border-radius: var(--chat-round);
|
|
836
|
+
background: var(--chat-primary); color: var(--chat-on-primary); border: 0; cursor: pointer; font: inherit;
|
|
837
|
+
font-weight: 600; }
|
|
478
838
|
.link { color: var(--chat-primary); }
|
|
479
|
-
/* A
|
|
839
|
+
/* A button on its own in a reply: as wide as its label needs, not the panel's 650px. */
|
|
840
|
+
.ui > .button, .ui > .link { align-self: flex-start; min-width: 14rem; }
|
|
841
|
+
/* The way to the contact page in a reply (ContactLink) and in the fallback: a button, outlined. */
|
|
842
|
+
.ui > .link, .fallback-links > .link { display: inline-flex; align-items: center; justify-content: center; padding: 10px 16px;
|
|
843
|
+
border: 1px solid var(--chat-primary); border-radius: var(--chat-round); text-decoration: none;
|
|
844
|
+
font-weight: 600; }
|
|
845
|
+
/* A link out of the chat while a reply still streams (ReplyLink). A button keeps its full strength: it
|
|
846
|
+
waits a moment, it is not unavailable. */
|
|
480
847
|
[aria-disabled="true"] { opacity: 0.5; cursor: progress; }
|
|
848
|
+
.button[aria-disabled="true"] { opacity: 1; }
|
|
849
|
+
|
|
850
|
+
/* Cards rise above the message area: on a dark surface a step lighter, with a 1 px edge where the shadow
|
|
851
|
+
cannot show; a light surface stays as it is (the lift only applies below the middle lightness). */
|
|
852
|
+
@supports (color: oklch(from red calc(l + 0.1) c h)) {
|
|
853
|
+
.card, .compare-col, .calendar { background: oklch(from var(--chat-surface) calc(l + max(0, 0.5 - l) * 0.45) c h); }
|
|
854
|
+
.card, .compare-col, .calendar { --chat-border: var(--chat-card-border); }
|
|
855
|
+
.card, .compare-col { box-shadow: var(--chat-card-shadow),
|
|
856
|
+
0 0 0 1px oklch(from var(--chat-surface) calc(l + max(0, 0.5 - l) * 0.75) c h); }
|
|
857
|
+
}
|
|
858
|
+
/* A carousel card, about 290px wide on a phone and on a wide panel alike: the total's label across the
|
|
859
|
+
foot, the total and the button side by side under it, so the label does not wrap beside the button
|
|
860
|
+
and push the price under the input. */
|
|
861
|
+
.carousel .card-foot { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center;
|
|
862
|
+
gap: 0 8px; }
|
|
863
|
+
.carousel .card-foot > .foot-price { display: contents; }
|
|
864
|
+
.carousel .card-foot .price-label { grid-column: 1 / -1; }
|
|
865
|
+
.carousel .card-foot > .button { grid-column: 2; grid-row: 2 / span 3; }
|
|
866
|
+
@container (min-width: 300px) {
|
|
867
|
+
/* With a deal badge the total and the badge take the line, and the button sits beside dates and party. */
|
|
868
|
+
.carousel .card-foot:has(.deal) .price-line { grid-column: 1 / -1; }
|
|
869
|
+
.carousel .card-foot:has(.deal) > .button { grid-row: 3 / span 2; }
|
|
870
|
+
/* An unbookable stay: the button beside its one line. */
|
|
871
|
+
.carousel .card-foot:not(:has(.price-label)) > .button { grid-row: 1; }
|
|
872
|
+
}
|
|
873
|
+
/* A small phone's card has no room beside the total: the button goes under it, full width. */
|
|
874
|
+
@container (max-width: 299px) {
|
|
875
|
+
.carousel .card-foot { grid-template-columns: minmax(0, 1fr); }
|
|
876
|
+
.carousel .card-foot > .button { grid-column: 1; grid-row: auto; margin-top: 8px; }
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
/* A card's photos large, in a sheet over the panel's content (sheet.tsx). */
|
|
880
|
+
.sheet-host { position: absolute; inset: 0; z-index: 3; pointer-events: none; }
|
|
881
|
+
.sheet-host:empty { display: none; }
|
|
882
|
+
.sheet-scrim { position: absolute; inset: 0; display: flex; flex-direction: column; justify-content: flex-end;
|
|
883
|
+
pointer-events: auto; background: rgb(15 23 42 / 0.55); }
|
|
884
|
+
.sheet { display: flex; flex-direction: column; height: calc(100% - 40px);
|
|
885
|
+
border-radius: var(--chat-radius) var(--chat-radius) 0 0; background: var(--chat-surface);
|
|
886
|
+
box-shadow: 0 -8px 32px rgb(15 23 42 / 0.25); overflow: hidden; }
|
|
887
|
+
.sheet:focus { outline: none; }
|
|
888
|
+
.sheet-head { display: flex; align-items: center; gap: 10px; padding: 10px 16px 10px 20px; }
|
|
889
|
+
.sheet-title { margin-right: auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
890
|
+
font-weight: 600; }
|
|
891
|
+
.sheet-count { color: var(--chat-muted); font-size: 13px; font-variant-numeric: tabular-nums; }
|
|
892
|
+
.sheet-photo { position: relative; flex: 1; min-height: 0; background: var(--chat-surface-soft); }
|
|
893
|
+
.sheet-photos { display: flex; height: 100%; overflow-x: auto; overscroll-behavior: contain;
|
|
894
|
+
scroll-snap-type: x mandatory; scrollbar-width: none; }
|
|
895
|
+
.sheet-photos::-webkit-scrollbar { display: none; }
|
|
896
|
+
/* Uncropped, at the photo's own ratio. */
|
|
897
|
+
.sheet-photos img { flex: 0 0 100%; width: 100%; height: 100%; object-fit: contain; scroll-snap-align: start; }
|
|
898
|
+
.sheet .sheet-photo > .gallery-arrow { opacity: 1; pointer-events: auto; }
|
|
899
|
+
.sheet .sheet-photo > .gallery-arrow:disabled { opacity: 0.4; }
|
|
900
|
+
.sheet-caption { margin: 0; padding: 10px 20px 0; font-size: 14px; }
|
|
901
|
+
.sheet-foot { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 10px;
|
|
902
|
+
padding: 12px 20px calc(18px + env(safe-area-inset-bottom, 0px)); border-top: 1px solid var(--chat-border); }
|
|
481
903
|
|
|
482
904
|
/* A card without photos: in a carousel a quiet block holds the photo's place, so the row lines up;
|
|
483
905
|
on its own the card simply starts with its text. */
|
|
484
|
-
.no-photo { display: none; aspect-ratio:
|
|
906
|
+
.no-photo { display: none; aspect-ratio: 4 / 3; place-items: center; background: var(--chat-surface-soft);
|
|
485
907
|
color: var(--chat-muted); }
|
|
486
908
|
.carousel .no-photo { display: grid; }
|
|
909
|
+
/* A card on its own in a wide panel: a flatter photo, so the card's name and price fit the list with it. */
|
|
910
|
+
@container (min-width: 520px) {
|
|
911
|
+
.ui > .rental-card :is(.gallery > img, .photo-open) { aspect-ratio: 2 / 1; max-height: 280px; }
|
|
912
|
+
/* In a carousel too: the cards' prices and the reply under them come up above the dock sooner. */
|
|
913
|
+
.carousel :is(.gallery > img, .photo-open), .carousel .no-photo { aspect-ratio: 2 / 1; }
|
|
914
|
+
}
|
|
487
915
|
/* Wishes a found stay matches, ticked under the name. */
|
|
488
916
|
.wishes { list-style: none; margin: 4px 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 6px; }
|
|
489
917
|
.wishes li { display: inline-flex; align-items: center; gap: 6px; padding: 4px 12px 4px 10px;
|
|
490
918
|
border: 1px solid var(--chat-border); border-radius: var(--chat-round); font-size: 13px; font-weight: 600; }
|
|
491
919
|
.wishes svg { color: var(--chat-accent); }
|
|
492
920
|
|
|
921
|
+
/* A quote: its dates and party, the labelled total beside the button, the discount and the
|
|
922
|
+
cancellation terms, then the price lines folded away under "Preisdetails". */
|
|
923
|
+
.stay-card .card-body { gap: 12px; }
|
|
924
|
+
.stay-card .card-body:last-child { padding-bottom: 16px; }
|
|
925
|
+
.quote-head { display: flex; align-items: center; gap: 12px; }
|
|
926
|
+
.quote-head > div { display: flex; flex-direction: column; min-width: 0; }
|
|
927
|
+
.thumb { flex: none; width: 52px; height: 52px; border-radius: calc(var(--chat-radius) * 0.75);
|
|
928
|
+
object-fit: cover; }
|
|
929
|
+
.quote-when { margin: 0; color: var(--chat-muted); font-size: 14px; }
|
|
930
|
+
.quote-when .period { white-space: nowrap; }
|
|
931
|
+
/* Wraps on a small phone: the button goes under a total that would not fit beside it. */
|
|
932
|
+
.price-row { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 12px;
|
|
933
|
+
padding-top: 12px; border-top: 1px solid var(--chat-border); }
|
|
934
|
+
.price-row .price { font-size: 24px; line-height: 1.2; white-space: nowrap; }
|
|
935
|
+
.quote-facts { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; }
|
|
936
|
+
.deal, .cancel-line { margin: 0; display: flex; align-items: center; gap: 8px; font-size: 14px; }
|
|
937
|
+
.deal svg, .cancel-line svg, .ho-lines svg { flex: none; color: var(--chat-accent); }
|
|
938
|
+
.deal strong, .cancel-line strong { font-weight: 600; }
|
|
939
|
+
/* The date wraps as one: "7." at a line's end and "Jan. 2027" on the next read as two. */
|
|
940
|
+
.cancel-line strong { white-space: nowrap; }
|
|
941
|
+
/* The discount in the primary's tint: its words say it, not the colour. */
|
|
942
|
+
.deal { padding: 4px 12px 4px 10px; border: 1px solid var(--chat-tint-edge); border-radius: var(--chat-round);
|
|
943
|
+
background: var(--chat-secondary); color: var(--chat-on-secondary); }
|
|
944
|
+
/* A badge stays one line: two would make a blob beside a card's button. */
|
|
945
|
+
.deal.compact { display: inline-flex; padding: 1px 8px; font-size: 12px; font-weight: 600; white-space: nowrap; }
|
|
946
|
+
/* The quote's line: a pill on one line, a rounded box on two. */
|
|
947
|
+
.deal:not(.compact) { border-radius: 16px; }
|
|
948
|
+
.deal .saving { white-space: nowrap; }
|
|
949
|
+
.price-details { padding-top: 10px; border-top: 1px solid var(--chat-border); font-size: 14px; }
|
|
950
|
+
.price-details summary { display: flex; align-items: center; justify-content: space-between; min-height: 32px;
|
|
951
|
+
list-style: none; color: var(--chat-primary); font-weight: 600; cursor: pointer; }
|
|
952
|
+
.price-details summary::-webkit-details-marker { display: none; }
|
|
953
|
+
.price-details summary svg { transform: rotate(90deg); }
|
|
954
|
+
.price-details[open] summary svg { transform: rotate(-90deg); }
|
|
955
|
+
.price-details .line { margin-top: 6px; }
|
|
956
|
+
/* The hand-over: what was chosen, the total as the largest figure, the conversation's one full-width
|
|
957
|
+
button, and what the booking page asks next. */
|
|
958
|
+
.ho-dates { display: flex; align-items: center; gap: 10px; padding: 12px 14px;
|
|
959
|
+
border-radius: calc(var(--chat-radius) * 0.75); background: var(--chat-surface-soft); }
|
|
960
|
+
.ho-dates > div { display: flex; flex-direction: column; }
|
|
961
|
+
.ho-dates > div:last-child { text-align: right; }
|
|
962
|
+
.ho-dates strong { font-weight: 600; white-space: nowrap; }
|
|
963
|
+
.ho-nights { flex: 1; display: flex; align-items: center; gap: 6px; color: var(--chat-muted); font-size: 13px;
|
|
964
|
+
white-space: nowrap; }
|
|
965
|
+
.ho-nights::before, .ho-nights::after { content: ""; flex: 1; height: 1px; background: currentColor; opacity: 0.5; }
|
|
966
|
+
/* A phone under 360px has no room for both dates and the nights in a row: the dates side by side, the
|
|
967
|
+
nights under them. */
|
|
968
|
+
@container (max-width: 311px) {
|
|
969
|
+
.ho-dates { display: grid; grid-template-columns: 1fr 1fr; gap: 4px 10px; }
|
|
970
|
+
.ho-nights { grid-column: 1 / -1; grid-row: 2; }
|
|
971
|
+
}
|
|
972
|
+
.ho-lines { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px;
|
|
973
|
+
font-size: 14px; }
|
|
974
|
+
.ho-lines li { display: flex; align-items: center; gap: 6px; }
|
|
975
|
+
.ho-total { display: flex; flex-direction: column; padding-top: 12px; border-top: 1px solid var(--chat-border); }
|
|
976
|
+
.ho-total .price { font-size: 30px; line-height: 1.15; }
|
|
977
|
+
.button.block { display: flex; width: 100%; min-height: 50px; margin: 0; gap: 8px; font-size: 16px; }
|
|
978
|
+
.ho-note { margin: 0; color: var(--chat-muted); font-size: 13px; }
|
|
979
|
+
.ho-steps { list-style: none; counter-reset: step; margin: -4px 0 0; padding: 0; display: flex;
|
|
980
|
+
flex-direction: column; gap: 5px; font-size: 13px; }
|
|
981
|
+
.ho-steps li { counter-increment: step; display: flex; align-items: center; gap: 8px; }
|
|
982
|
+
.ho-steps li::before { content: counter(step); flex: none; display: grid; place-items: center; width: 20px;
|
|
983
|
+
height: 20px; border: 1px solid var(--chat-border); border-radius: 50%; color: var(--chat-muted);
|
|
984
|
+
font-size: 12px; font-weight: 700; }
|
|
985
|
+
|
|
493
986
|
/* The price calendar: a column per arrival, the bar's height its total. */
|
|
494
|
-
.price-columns { list-style: none; margin: 10px 0 0; padding: 0; display: grid;
|
|
495
|
-
grid-auto-columns: minmax(68px, 1fr); gap: 6px; overflow-x: auto;
|
|
496
|
-
|
|
987
|
+
.price-columns { position: relative; list-style: none; margin: 10px 0 0; padding: 0; display: grid;
|
|
988
|
+
grid-auto-flow: column; grid-auto-columns: minmax(68px, 1fr); gap: 6px; overflow-x: auto;
|
|
989
|
+
scrollbar-width: none; }
|
|
990
|
+
.price-columns::-webkit-scrollbar { display: none; }
|
|
991
|
+
/* More columns beyond an edge fade out there. */
|
|
992
|
+
.price-columns[data-fade="end"] { mask-image: linear-gradient(to left, transparent, #000 32px); }
|
|
993
|
+
.price-columns[data-fade="start"] { mask-image: linear-gradient(to right, transparent, #000 32px); }
|
|
994
|
+
.price-columns[data-fade="both"] { mask-image: linear-gradient(to right, transparent, #000 32px,
|
|
995
|
+
#000 calc(100% - 32px), transparent); }
|
|
996
|
+
/* The arrival's day at the column's foot, level across the row when "nicht buchbar" takes two lines. */
|
|
997
|
+
.price-column { height: 100%; width: 100%; display: flex; flex-direction: column; align-items: center; gap: 4px;
|
|
497
998
|
padding: 6px 2px; border: 1px solid transparent; border-radius: 10px; background: transparent;
|
|
498
999
|
color: inherit; font: inherit; cursor: pointer; }
|
|
499
1000
|
.price-column:disabled { cursor: default; }
|
|
500
|
-
|
|
1001
|
+
/* Inside the row that scrolls, so it clips: the ring sits inside the column. */
|
|
1002
|
+
.price-column:focus-visible { outline-offset: -2px; }
|
|
501
1003
|
.price-column[data-asked] { border-color: var(--chat-border); background: var(--chat-surface-soft); }
|
|
502
1004
|
.bar-track { display: flex; align-items: flex-end; justify-content: center; width: 100%; height: 72px; }
|
|
503
1005
|
.bar { width: 60%; border-radius: min(6px, var(--chat-radius)) min(6px, var(--chat-radius)) 0 0;
|
|
504
1006
|
background: color-mix(in srgb, var(--chat-primary) 30%, var(--chat-surface)); }
|
|
505
1007
|
.price-column[data-cheapest] .bar, .legend i[data-cheapest] { background: var(--chat-primary); }
|
|
506
1008
|
.legend i[data-asked] { background: var(--chat-surface-soft); border: 1px solid var(--chat-border); }
|
|
507
|
-
.price-
|
|
508
|
-
.price-
|
|
1009
|
+
.price-column > .muted { margin-top: auto; }
|
|
1010
|
+
.price-text { font-size: 14px; font-weight: 700; white-space: nowrap; }
|
|
1011
|
+
.price-column:not([data-free]) .price-text { color: var(--chat-muted); font-weight: 500; white-space: normal; }
|
|
509
1012
|
|
|
510
1013
|
/* Reviews: the PMS's score and grade, its categories, a few texts. */
|
|
511
1014
|
.score { display: flex; align-items: center; gap: 12px; margin-top: 4px; }
|
|
@@ -531,7 +1034,7 @@ ${SITE_COLORS.map((t) => ` ${t.chat}: var(${t.site}, ${t.own});`).join("\n")}
|
|
|
531
1034
|
.map-gate { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px;
|
|
532
1035
|
min-height: 150px; padding: 16px; text-align: center; background: var(--chat-surface-soft); }
|
|
533
1036
|
.map-gate p { margin: 0; }
|
|
534
|
-
.map-attribution { margin: 0; padding: 2px 8px; font-size:
|
|
1037
|
+
.map-attribution { margin: 0; padding: 2px 8px; font-size: 12px; color: var(--chat-muted); text-align: right; }
|
|
535
1038
|
.map-attribution a { color: inherit; }
|
|
536
1039
|
.map-pin { display: block; width: 18px; height: 18px; border-radius: 999px; background: var(--chat-primary);
|
|
537
1040
|
border: 3px solid var(--chat-surface); box-shadow: 0 2px 6px rgb(0 0 0 / 0.35); }
|
|
@@ -542,6 +1045,29 @@ ${SITE_COLORS.map((t) => ` ${t.chat}: var(${t.site}, ${t.own});`).join("\n")}
|
|
|
542
1045
|
.maplibregl-canvas:focus-visible { outline: 2px solid var(--chat-primary); outline-offset: -2px; }
|
|
543
1046
|
.maplibregl-marker { position: absolute; top: 0; left: 0; will-change: transform; }
|
|
544
1047
|
|
|
1048
|
+
/* Hover only where there is a pointer: on a touch screen a tapped chip would keep its hover look. */
|
|
1049
|
+
@media (hover: hover) {
|
|
1050
|
+
.icon-button:hover { border-color: var(--chat-primary); }
|
|
1051
|
+
.close:hover { opacity: 0.9; }
|
|
1052
|
+
.suggestion:hover, .chip:not(:disabled):hover { border-color: var(--chat-primary);
|
|
1053
|
+
background: var(--chat-surface); color: var(--chat-text); }
|
|
1054
|
+
.gallery-wrap:hover .gallery-arrow:not(:disabled) { opacity: 1; }
|
|
1055
|
+
.price-column[data-free]:not(:disabled):hover { border-color: var(--chat-primary); }
|
|
1056
|
+
button.day:hover { box-shadow: inset 0 0 0 1px var(--chat-primary); }
|
|
1057
|
+
.row:not(:disabled):hover { background: var(--chat-surface-soft); }
|
|
1058
|
+
.row[aria-pressed="true"]:hover { background: var(--chat-secondary); }
|
|
1059
|
+
.period-pill:not(:disabled):hover { border-color: var(--chat-primary); }
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
/* Fingers: every control at least 44px each way. */
|
|
1063
|
+
@media (pointer: coarse) {
|
|
1064
|
+
.icon-button, .send, .snap-nav button, .calendar-head button, .gallery-arrow { width: 44px; height: 44px; }
|
|
1065
|
+
.composer { padding: 4px 4px 4px 18px; }
|
|
1066
|
+
.composer textarea { min-height: 44px; padding-block: 10px; }
|
|
1067
|
+
.chip, .suggestion, .button, .ui > .link, .fallback-links > .link, .period-pill { min-height: 44px; }
|
|
1068
|
+
.price-details summary { min-height: 44px; }
|
|
1069
|
+
}
|
|
1070
|
+
|
|
545
1071
|
[data-theme="soft"] { --chat-radius: calc(var(--chat-base-radius) * 2.4); --chat-shadow: 0 12px 32px rgb(15 23 42 / 0.12);
|
|
546
1072
|
--chat-card-shadow: 0 4px 18px rgb(15 23 42 / 0.07); }
|
|
547
1073
|
[data-theme="minimal"] { --chat-radius: calc(var(--chat-base-radius) * 0.6); --chat-shadow: none; --chat-card-shadow: none; }
|
|
@@ -554,16 +1080,20 @@ ${SITE_COLORS.map((t) => ` ${t.chat}: var(${t.site}, ${t.own});`).join("\n")}
|
|
|
554
1080
|
@keyframes chat-in { from { opacity: 0; transform: translateY(16px); } }
|
|
555
1081
|
@keyframes chat-fade { from { opacity: 0; } }
|
|
556
1082
|
@keyframes chat-pulse { 50% { opacity: 1; transform: translateY(-2px); } }
|
|
1083
|
+
@keyframes chat-ring { from { outline-color: transparent; outline-offset: 8px; } }
|
|
1084
|
+
@media (prefers-reduced-motion: no-preference) and (hover: hover) {
|
|
1085
|
+
.pill:hover { transform: translateY(-2px); }
|
|
1086
|
+
}
|
|
557
1087
|
@media (prefers-reduced-motion: no-preference) {
|
|
558
1088
|
.glow::before { animation: chat-spin 3.2s linear infinite; }
|
|
559
1089
|
.entry { animation: chat-in 400ms cubic-bezier(0.25, 1, 0.5, 1); }
|
|
560
1090
|
.pill { transition: transform 200ms ease-out; }
|
|
561
|
-
.pill:hover { transform: translateY(-2px); }
|
|
562
1091
|
.panel { animation: chat-in 400ms cubic-bezier(0.25, 1, 0.5, 1); }
|
|
563
1092
|
.scrim { animation: chat-fade 300ms ease-out; }
|
|
564
1093
|
.thinking i { animation: chat-pulse 1.1s ease-in-out infinite; }
|
|
565
1094
|
.thinking i:nth-child(2) { animation-delay: 160ms; }
|
|
566
1095
|
.thinking i:nth-child(3) { animation-delay: 320ms; }
|
|
1096
|
+
[data-pulse] { animation: chat-ring 500ms ease-out 2 alternate; }
|
|
567
1097
|
}
|
|
568
1098
|
/* Without motion the ring stands still, in the primary colour all round. */
|
|
569
1099
|
@media (prefers-reduced-motion: reduce) {
|
|
@@ -572,7 +1102,7 @@ ${SITE_COLORS.map((t) => ` ${t.chat}: var(${t.site}, ${t.own});`).join("\n")}
|
|
|
572
1102
|
`;
|
|
573
1103
|
//#endregion
|
|
574
1104
|
//#region src/WebsiteChat.tsx
|
|
575
|
-
const Panel = lazy(() => import("./panel-
|
|
1105
|
+
const Panel = lazy(() => import("./panel-B_wg4Wl0.js"));
|
|
576
1106
|
/** See {@link WebsiteChatComponent}. */
|
|
577
1107
|
const WebsiteChat = function WebsiteChat(props) {
|
|
578
1108
|
const { endpoint, locale, theme = "classic", position = "right" } = props;
|
|
@@ -644,11 +1174,12 @@ const WebsiteChat = function WebsiteChat(props) {
|
|
|
644
1174
|
fallback: null,
|
|
645
1175
|
children: /* @__PURE__ */ jsx(Panel, {
|
|
646
1176
|
endpoint,
|
|
647
|
-
clientVersion: "1.
|
|
1177
|
+
clientVersion: "1.3.0",
|
|
648
1178
|
locale,
|
|
649
1179
|
copy,
|
|
650
1180
|
searchPath: props.searchPath,
|
|
651
1181
|
contactPath: props.contactPath,
|
|
1182
|
+
...props.privacyPath && { privacyPath: props.privacyPath },
|
|
652
1183
|
open,
|
|
653
1184
|
pending,
|
|
654
1185
|
onSent: sent,
|