@bigz-app/booking-widget 1.4.3 → 1.4.5

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.
Files changed (42) hide show
  1. package/dist/booking-widget.js +17305 -17065
  2. package/dist/booking-widget.js.map +1 -1
  3. package/dist/components/Sidebar.d.ts.map +1 -1
  4. package/dist/components/UniversalBookingWidget.d.ts +12 -4
  5. package/dist/components/UniversalBookingWidget.d.ts.map +1 -1
  6. package/dist/components/booking/BookingForm.d.ts +1 -3
  7. package/dist/components/booking/BookingForm.d.ts.map +1 -1
  8. package/dist/components/booking/GiftCardOnlyBooking.d.ts.map +1 -1
  9. package/dist/components/booking/HoldCountdown.d.ts +10 -0
  10. package/dist/components/booking/HoldCountdown.d.ts.map +1 -0
  11. package/dist/components/booking/MolliePaymentForm.d.ts.map +1 -1
  12. package/dist/components/booking/StripePaymentForm.d.ts +1 -2
  13. package/dist/components/booking/StripePaymentForm.d.ts.map +1 -1
  14. package/dist/components/events/EventInstanceSelection.d.ts +3 -1
  15. package/dist/components/events/EventInstanceSelection.d.ts.map +1 -1
  16. package/dist/components/events/EventTypeDetailsDialog.d.ts +0 -1
  17. package/dist/components/events/EventTypeDetailsDialog.d.ts.map +1 -1
  18. package/dist/components/events/EventTypeSelection.d.ts +1 -1
  19. package/dist/components/events/EventTypeSelection.d.ts.map +1 -1
  20. package/dist/components/events/NextEventsPreview.d.ts +3 -1
  21. package/dist/components/events/NextEventsPreview.d.ts.map +1 -1
  22. package/dist/components/events/WaitlistDialog.d.ts +10 -0
  23. package/dist/components/events/WaitlistDialog.d.ts.map +1 -0
  24. package/dist/components/shared/Button.d.ts.map +1 -1
  25. package/dist/components/shared/SectionHeader.d.ts.map +1 -1
  26. package/dist/components/voucher/VoucherPurchaseForm.d.ts +0 -1
  27. package/dist/components/voucher/VoucherPurchaseForm.d.ts.map +1 -1
  28. package/dist/components/voucher/useVoucherConfig.d.ts +0 -1
  29. package/dist/components/voucher/useVoucherConfig.d.ts.map +1 -1
  30. package/dist/i18n/locales/de.d.ts.map +1 -1
  31. package/dist/i18n/locales/en.d.ts.map +1 -1
  32. package/dist/index.cjs +620 -380
  33. package/dist/index.cjs.map +1 -1
  34. package/dist/index.esm.js +607 -367
  35. package/dist/index.esm.js.map +1 -1
  36. package/dist/styles/StyleProvider.d.ts.map +1 -1
  37. package/dist/styles/shared-styles.d.ts.map +1 -1
  38. package/dist/utils/analytics.d.ts +3 -1
  39. package/dist/utils/analytics.d.ts.map +1 -1
  40. package/dist/utils.d.ts.map +1 -1
  41. package/dist/validation/booking-schema.d.ts +4 -4
  42. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -6,188 +6,24 @@ var jsxRuntime = require('react/jsx-runtime');
6
6
  var ReactDOM = require('react-dom');
7
7
 
8
8
  function _interopNamespaceDefault(e) {
9
- var n = Object.create(null);
10
- if (e) {
11
- Object.keys(e).forEach(function (k) {
12
- if (k !== 'default') {
13
- var d = Object.getOwnPropertyDescriptor(e, k);
14
- Object.defineProperty(n, k, d.get ? d : {
15
- enumerable: true,
16
- get: function () { return e[k]; }
9
+ var n = Object.create(null);
10
+ if (e) {
11
+ Object.keys(e).forEach(function (k) {
12
+ if (k !== 'default') {
13
+ var d = Object.getOwnPropertyDescriptor(e, k);
14
+ Object.defineProperty(n, k, d.get ? d : {
15
+ enumerable: true,
16
+ get: function () { return e[k]; }
17
+ });
18
+ }
17
19
  });
18
- }
19
- });
20
- }
21
- n.default = e;
22
- return Object.freeze(n);
20
+ }
21
+ n.default = e;
22
+ return Object.freeze(n);
23
23
  }
24
24
 
25
25
  var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
26
26
 
27
- var V3_URL = 'https://js.stripe.com/v3';
28
- var V3_URL_REGEX = /^https:\/\/js\.stripe\.com\/v3\/?(\?.*)?$/;
29
- var EXISTING_SCRIPT_MESSAGE = 'loadStripe.setLoadParameters was called but an existing Stripe.js script already exists in the document; existing script parameters will be used';
30
- var findScript = function findScript() {
31
- var scripts = document.querySelectorAll("script[src^=\"".concat(V3_URL, "\"]"));
32
-
33
- for (var i = 0; i < scripts.length; i++) {
34
- var script = scripts[i];
35
-
36
- if (!V3_URL_REGEX.test(script.src)) {
37
- continue;
38
- }
39
-
40
- return script;
41
- }
42
-
43
- return null;
44
- };
45
-
46
- var injectScript = function injectScript(params) {
47
- var queryString = '';
48
- var script = document.createElement('script');
49
- script.src = "".concat(V3_URL).concat(queryString);
50
- var headOrBody = document.head || document.body;
51
-
52
- if (!headOrBody) {
53
- throw new Error('Expected document.body not to be null. Stripe.js requires a <body> element.');
54
- }
55
-
56
- headOrBody.appendChild(script);
57
- return script;
58
- };
59
-
60
- var registerWrapper = function registerWrapper(stripe, startTime) {
61
- if (!stripe || !stripe._registerWrapper) {
62
- return;
63
- }
64
-
65
- stripe._registerWrapper({
66
- name: 'stripe-js',
67
- version: "4.6.0",
68
- startTime: startTime
69
- });
70
- };
71
-
72
- var stripePromise = null;
73
- var onErrorListener = null;
74
- var onLoadListener = null;
75
-
76
- var onError = function onError(reject) {
77
- return function () {
78
- reject(new Error('Failed to load Stripe.js'));
79
- };
80
- };
81
-
82
- var onLoad = function onLoad(resolve, reject) {
83
- return function () {
84
- if (window.Stripe) {
85
- resolve(window.Stripe);
86
- } else {
87
- reject(new Error('Stripe.js not available'));
88
- }
89
- };
90
- };
91
-
92
- var loadScript = function loadScript(params) {
93
- // Ensure that we only attempt to load Stripe.js at most once
94
- if (stripePromise !== null) {
95
- return stripePromise;
96
- }
97
-
98
- stripePromise = new Promise(function (resolve, reject) {
99
- if (typeof window === 'undefined' || typeof document === 'undefined') {
100
- // Resolve to null when imported server side. This makes the module
101
- // safe to import in an isomorphic code base.
102
- resolve(null);
103
- return;
104
- }
105
-
106
- if (window.Stripe) {
107
- resolve(window.Stripe);
108
- return;
109
- }
110
-
111
- try {
112
- var script = findScript();
113
-
114
- if (script && params) ; else if (!script) {
115
- script = injectScript(params);
116
- } else if (script && onLoadListener !== null && onErrorListener !== null) {
117
- var _script$parentNode;
118
-
119
- // remove event listeners
120
- script.removeEventListener('load', onLoadListener);
121
- script.removeEventListener('error', onErrorListener); // if script exists, but we are reloading due to an error,
122
- // reload script to trigger 'load' event
123
-
124
- (_script$parentNode = script.parentNode) === null || _script$parentNode === void 0 ? void 0 : _script$parentNode.removeChild(script);
125
- script = injectScript(params);
126
- }
127
-
128
- onLoadListener = onLoad(resolve, reject);
129
- onErrorListener = onError(reject);
130
- script.addEventListener('load', onLoadListener);
131
- script.addEventListener('error', onErrorListener);
132
- } catch (error) {
133
- reject(error);
134
- return;
135
- }
136
- }); // Resets stripePromise on error
137
-
138
- return stripePromise["catch"](function (error) {
139
- stripePromise = null;
140
- return Promise.reject(error);
141
- });
142
- };
143
- var initStripe = function initStripe(maybeStripe, args, startTime) {
144
- if (maybeStripe === null) {
145
- return null;
146
- }
147
-
148
- var stripe = maybeStripe.apply(undefined, args);
149
- registerWrapper(stripe, startTime);
150
- return stripe;
151
- }; // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
152
-
153
- var stripePromise$1;
154
- var loadCalled = false;
155
-
156
- var getStripePromise = function getStripePromise() {
157
- if (stripePromise$1) {
158
- return stripePromise$1;
159
- }
160
-
161
- stripePromise$1 = loadScript(null)["catch"](function (error) {
162
- // clear cache on error
163
- stripePromise$1 = null;
164
- return Promise.reject(error);
165
- });
166
- return stripePromise$1;
167
- }; // Execute our own script injection after a tick to give users time to do their
168
- // own script injection.
169
-
170
-
171
- Promise.resolve().then(function () {
172
- return getStripePromise();
173
- })["catch"](function (error) {
174
- if (!loadCalled) {
175
- console.warn(error);
176
- }
177
- });
178
- var loadStripe = function loadStripe() {
179
- for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
180
- args[_key] = arguments[_key];
181
- }
182
-
183
- loadCalled = true;
184
- var startTime = Date.now(); // if previous attempts are unsuccessful, will re-load script
185
-
186
- return getStripePromise().then(function (maybeStripe) {
187
- return initStripe(maybeStripe, args, startTime);
188
- });
189
- };
190
-
191
27
  const de$1 = {
192
28
  // Common
193
29
  "common.back": "← Zurück",
@@ -267,6 +103,24 @@ const de$1 = {
267
103
  "instances.bestPrice": "bester Preis !!!",
268
104
  "instances.goodPrice": "günstiger Preis",
269
105
  "instances.oclock": "Uhr",
106
+ // Warteliste
107
+ "waitlist.joinCta": "Auf die Warteliste",
108
+ "waitlist.title": "Auf die Warteliste setzen",
109
+ "waitlist.subtitle": "{{name}} ist ausgebucht. Hinterlassen Sie Ihre Daten und wir melden uns, falls ein Platz frei wird.",
110
+ "waitlist.name": "Name",
111
+ "waitlist.email": "E-Mail",
112
+ "waitlist.phone": "Telefon (optional)",
113
+ "waitlist.requiredFields": "Bitte geben Sie Ihren Namen und Ihre E-Mail-Adresse ein.",
114
+ "waitlist.join": "Eintragen",
115
+ "waitlist.submitting": "Wird eingetragen...",
116
+ "waitlist.cancel": "Abbrechen",
117
+ "waitlist.submitError": "Etwas ist schiefgelaufen. Bitte versuchen Sie es erneut.",
118
+ "waitlist.successTitle": "Sie sind auf der Liste!",
119
+ "waitlist.successMessage": "Sie sind Nummer {{position}} auf der Warteliste. Wir melden uns, sobald ein Platz frei wird.",
120
+ "waitlist.done": "Fertig",
121
+ // Reservierungs-Countdown
122
+ "hold.reservedFor": "Platz reserviert für {{time}}",
123
+ "hold.expired": "Ihre Reservierung ist abgelaufen",
270
124
  // Next events preview
271
125
  "nextEvents.title": "Nächste verfügbare Termine",
272
126
  "nextEvents.subtitle": "Wähle einen Termin aus oder zeige alle verfügbaren Termine an",
@@ -557,6 +411,24 @@ const en = {
557
411
  "instances.bestPrice": "best price !!!",
558
412
  "instances.goodPrice": "good price",
559
413
  "instances.oclock": "",
414
+ // Waitlist
415
+ "waitlist.joinCta": "Join waitlist",
416
+ "waitlist.title": "Join the waitlist",
417
+ "waitlist.subtitle": "{{name}} is fully booked. Leave your details and we'll contact you if a spot opens up.",
418
+ "waitlist.name": "Name",
419
+ "waitlist.email": "Email",
420
+ "waitlist.phone": "Phone (optional)",
421
+ "waitlist.requiredFields": "Please enter your name and email.",
422
+ "waitlist.join": "Join waitlist",
423
+ "waitlist.submitting": "Joining...",
424
+ "waitlist.cancel": "Cancel",
425
+ "waitlist.submitError": "Something went wrong. Please try again.",
426
+ "waitlist.successTitle": "You're on the list!",
427
+ "waitlist.successMessage": "You are number {{position}} on the waitlist. We'll be in touch if a spot becomes available.",
428
+ "waitlist.done": "Done",
429
+ // Slot hold countdown
430
+ "hold.reservedFor": "Slot reserved for {{time}}",
431
+ "hold.expired": "Your reservation has expired",
560
432
  // Next events preview
561
433
  "nextEvents.title": "Next available dates",
562
434
  "nextEvents.subtitle": "Select a date or view all available dates",
@@ -2065,6 +1937,28 @@ const StyleProvider = ({ config, children, }) => {
2065
1937
  c = `0x${c.join("")}`;
2066
1938
  return [(c >> 16) & 255, (c >> 8) & 255, c & 255].join(",");
2067
1939
  };
1940
+ // Pick a readable text color (dark or light) for content sitting on top of
1941
+ // the given background color. Bright/neon highlights (teal, matrix green,
1942
+ // gold, etc.) need dark text — white text on them is unreadable.
1943
+ const getContrastingTextColor = (color, lightColor = "#ffffff", darkColor = "#0e1420") => {
1944
+ let rgb;
1945
+ if (color.startsWith("#")) {
1946
+ rgb = hexToRgb(color);
1947
+ }
1948
+ else if (color.startsWith("rgb")) {
1949
+ rgb = color.replace(/rgba?\(|\)/g, "").split(",").slice(0, 3).join(",");
1950
+ }
1951
+ else {
1952
+ return lightColor; // Can't parse (e.g. hsl/oklch/semantic) — keep existing default
1953
+ }
1954
+ const [r, g, b] = rgb.split(",").map((n) => parseInt(n, 10) / 255);
1955
+ if ([r, g, b].some((n) => Number.isNaN(n)))
1956
+ return lightColor;
1957
+ // Relative luminance (sRGB, WCAG)
1958
+ const toLinear = (n) => (n <= 0.03928 ? n / 12.92 : Math.pow((n + 0.055) / 1.055, 2.4));
1959
+ const luminance = 0.2126 * toLinear(r) + 0.7152 * toLinear(g) + 0.0722 * toLinear(b);
1960
+ return luminance > 0.45 ? darkColor : lightColor;
1961
+ };
2068
1962
  const colors = config.colors || {};
2069
1963
  const finalColors = {
2070
1964
  highlight: getCSSValue(colors.highlight, themeDefaults.highlight),
@@ -2084,7 +1978,7 @@ const StyleProvider = ({ config, children, }) => {
2084
1978
  "--bw-surface-color": finalColors.surface,
2085
1979
  "--bw-text-color": finalColors.text,
2086
1980
  "--bw-text-muted": addOpacity(finalColors.text, 0.7),
2087
- "--bw-button-text-color": themeDefaults.buttonTextColor || "#ffffff",
1981
+ "--bw-button-text-color": themeDefaults.buttonTextColor || getContrastingTextColor(finalColors.highlight),
2088
1982
  "--bw-border-color": finalColors.border,
2089
1983
  "--bw-success-color": finalColors.success,
2090
1984
  "--bw-warning-color": finalColors.warning,
@@ -2102,6 +1996,9 @@ const StyleProvider = ({ config, children, }) => {
2102
1996
  "--bw-highlight-muted": addOpacity(finalColors.highlight, 0.1),
2103
1997
  "--bw-highlight-subtle": addOpacity(finalColors.highlight, 0.05),
2104
1998
  "--bw-text-subtle": addOpacity(finalColors.text, 0.4),
1999
+ // Some themes (neon-brutalism) call for uppercased titles & buttons
2000
+ "--bw-text-transform": themeName === "dark-neon-brutalism" ? "uppercase" : "none",
2001
+ "--bw-letter-spacing": themeName === "dark-neon-brutalism" ? "0.04em" : "normal",
2105
2002
  colorScheme: (["navy-night", "green-deep", "green-matrix", "gold-luxury", "purple-electric", "dark-neon-brutalism", "amber-retro", "orange-raw"].includes(themeName) || themeName.startsWith("dark-")) ? "dark" : "light",
2106
2003
  };
2107
2004
  }, [
@@ -2237,6 +2134,7 @@ const inferConfigFromUrl = (baseConfig) => {
2237
2134
  const categoryId = urlParams.get("categoryId") || urlParams.get("category");
2238
2135
  const eventTypeId = urlParams.get("eventTypeId") || urlParams.get("type");
2239
2136
  const eventTypeIds = urlParams.get("eventTypeIds") || urlParams.get("types");
2137
+ const partnerContractId = urlParams.get("partnerContractId") || urlParams.get("contractId");
2240
2138
  const voucherValue = urlParams.get("voucherValue");
2241
2139
  const voucherCategory = urlParams.get("voucherCategory");
2242
2140
  const voucherEventType = urlParams.get("voucherEventType");
@@ -2249,11 +2147,14 @@ const inferConfigFromUrl = (baseConfig) => {
2249
2147
  : ["0", "false", "no", "off"].includes(voucherCardIntegration.toLowerCase())
2250
2148
  ? false
2251
2149
  : undefined;
2252
- const omitSelectionKeys = ({ categoryId: _c, eventTypeId: _e, eventTypeIds: _es, eventInstanceId: _i, voucherValue: _vv, voucherCategory: _vc, voucherEventType: _ve, voucherImageId: _vi, ...rest }) => rest;
2150
+ const omitSelectionKeys = ({ categoryId: _c, eventTypeId: _e, eventTypeIds: _es, eventInstanceId: _i, voucherValue: _vv, voucherCategory: _vc, voucherEventType: _ve, voucherImageId: _vi, partnerContractId: _pc, ...rest }) => rest;
2253
2151
  if (eventInstanceId && !Number.isNaN(Number(eventInstanceId))) {
2254
2152
  return {
2255
2153
  ...omitSelectionKeys(baseConfig),
2256
2154
  eventInstanceId: Number(eventInstanceId),
2155
+ ...(partnerContractId
2156
+ ? { partnerContractId }
2157
+ : {}),
2257
2158
  ...(parsedVoucherCardIntegration !== undefined ? { voucherIntegration: parsedVoucherCardIntegration } : {}),
2258
2159
  };
2259
2160
  }
@@ -2261,6 +2162,9 @@ const inferConfigFromUrl = (baseConfig) => {
2261
2162
  return {
2262
2163
  ...omitSelectionKeys(baseConfig),
2263
2164
  categoryId: Number(categoryId),
2165
+ ...(partnerContractId
2166
+ ? { partnerContractId }
2167
+ : {}),
2264
2168
  ...(parsedVoucherCardIntegration !== undefined ? { voucherIntegration: parsedVoucherCardIntegration } : {}),
2265
2169
  };
2266
2170
  }
@@ -2268,6 +2172,9 @@ const inferConfigFromUrl = (baseConfig) => {
2268
2172
  return {
2269
2173
  ...omitSelectionKeys(baseConfig),
2270
2174
  eventTypeId: Number(eventTypeId),
2175
+ ...(partnerContractId
2176
+ ? { partnerContractId }
2177
+ : {}),
2271
2178
  ...(parsedVoucherCardIntegration !== undefined ? { voucherIntegration: parsedVoucherCardIntegration } : {}),
2272
2179
  };
2273
2180
  }
@@ -2280,6 +2187,9 @@ const inferConfigFromUrl = (baseConfig) => {
2280
2187
  return {
2281
2188
  ...omitSelectionKeys(baseConfig),
2282
2189
  eventTypeIds: typeIds,
2190
+ ...(partnerContractId
2191
+ ? { partnerContractId }
2192
+ : {}),
2283
2193
  ...(parsedVoucherCardIntegration !== undefined ? { voucherIntegration: parsedVoucherCardIntegration } : {}),
2284
2194
  };
2285
2195
  }
@@ -2296,6 +2206,9 @@ const inferConfigFromUrl = (baseConfig) => {
2296
2206
  if (parsedVoucherValues.length || parsedVoucherCategories.length || parsedVoucherEventTypes.length) {
2297
2207
  return {
2298
2208
  ...baseConfig,
2209
+ ...(partnerContractId
2210
+ ? { partnerContractId }
2211
+ : {}),
2299
2212
  ...(parsedVoucherCardIntegration !== undefined ? { voucherIntegration: parsedVoucherCardIntegration } : {}),
2300
2213
  ...(parsedVoucherValues.length > 0
2301
2214
  ? { voucherValue: parsedVoucherValues.length === 1 ? parsedVoucherValues[0] : parsedVoucherValues }
@@ -2313,6 +2226,9 @@ const inferConfigFromUrl = (baseConfig) => {
2313
2226
  }
2314
2227
  return {
2315
2228
  ...baseConfig,
2229
+ ...(partnerContractId
2230
+ ? { partnerContractId }
2231
+ : {}),
2316
2232
  ...(parsedVoucherCardIntegration !== undefined ? { voucherIntegration: parsedVoucherCardIntegration } : {}),
2317
2233
  };
2318
2234
  };
@@ -6325,6 +6241,7 @@ function GiftCardOnlyBooking({ config, eventDetails, formData, discountCode, gif
6325
6241
  customerPhone: formData.customerPhone?.trim(),
6326
6242
  comment: formData.comment?.trim(),
6327
6243
  paymentMethod: "gift_card",
6244
+ ...(config.partnerContractId && { partnerContractId: config.partnerContractId }),
6328
6245
  ...(upsellSelections.length > 0 && { upsellSelections }),
6329
6246
  };
6330
6247
  const response = await fetch(getApiUrl(config.apiBaseUrl, "/booking/create-gift-card-booking"), {
@@ -6601,6 +6518,7 @@ function MolliePaymentForm({ config, eventDetails, formData, totalAmount, discou
6601
6518
  customerEmail: formData.customerEmail?.trim(),
6602
6519
  customerPhone: formData.customerPhone?.trim(),
6603
6520
  comment: formData.comment?.trim(),
6521
+ ...(config.partnerContractId && { partnerContractId: config.partnerContractId }),
6604
6522
  ...(cardToken && { cardToken }),
6605
6523
  ...(selectedMethod && !cardToken && { method: selectedMethod }),
6606
6524
  ...(upsellSelections && upsellSelections.length > 0 && { upsellSelections }),
@@ -6827,6 +6745,170 @@ function MolliePaymentForm({ config, eventDetails, formData, totalAmount, discou
6827
6745
  }, children: isLoading ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [spinner("var(--bw-surface-color)"), " ", buttonLabel] })) : (buttonLabel) }))] }));
6828
6746
  }
6829
6747
 
6748
+ var V3_URL = 'https://js.stripe.com/v3';
6749
+ var V3_URL_REGEX = /^https:\/\/js\.stripe\.com\/v3\/?(\?.*)?$/;
6750
+ var EXISTING_SCRIPT_MESSAGE = 'loadStripe.setLoadParameters was called but an existing Stripe.js script already exists in the document; existing script parameters will be used';
6751
+ var findScript = function findScript() {
6752
+ var scripts = document.querySelectorAll("script[src^=\"".concat(V3_URL, "\"]"));
6753
+
6754
+ for (var i = 0; i < scripts.length; i++) {
6755
+ var script = scripts[i];
6756
+
6757
+ if (!V3_URL_REGEX.test(script.src)) {
6758
+ continue;
6759
+ }
6760
+
6761
+ return script;
6762
+ }
6763
+
6764
+ return null;
6765
+ };
6766
+
6767
+ var injectScript = function injectScript(params) {
6768
+ var queryString = '';
6769
+ var script = document.createElement('script');
6770
+ script.src = "".concat(V3_URL).concat(queryString);
6771
+ var headOrBody = document.head || document.body;
6772
+
6773
+ if (!headOrBody) {
6774
+ throw new Error('Expected document.body not to be null. Stripe.js requires a <body> element.');
6775
+ }
6776
+
6777
+ headOrBody.appendChild(script);
6778
+ return script;
6779
+ };
6780
+
6781
+ var registerWrapper = function registerWrapper(stripe, startTime) {
6782
+ if (!stripe || !stripe._registerWrapper) {
6783
+ return;
6784
+ }
6785
+
6786
+ stripe._registerWrapper({
6787
+ name: 'stripe-js',
6788
+ version: "4.6.0",
6789
+ startTime: startTime
6790
+ });
6791
+ };
6792
+
6793
+ var stripePromise = null;
6794
+ var onErrorListener = null;
6795
+ var onLoadListener = null;
6796
+
6797
+ var onError = function onError(reject) {
6798
+ return function () {
6799
+ reject(new Error('Failed to load Stripe.js'));
6800
+ };
6801
+ };
6802
+
6803
+ var onLoad = function onLoad(resolve, reject) {
6804
+ return function () {
6805
+ if (window.Stripe) {
6806
+ resolve(window.Stripe);
6807
+ } else {
6808
+ reject(new Error('Stripe.js not available'));
6809
+ }
6810
+ };
6811
+ };
6812
+
6813
+ var loadScript = function loadScript(params) {
6814
+ // Ensure that we only attempt to load Stripe.js at most once
6815
+ if (stripePromise !== null) {
6816
+ return stripePromise;
6817
+ }
6818
+
6819
+ stripePromise = new Promise(function (resolve, reject) {
6820
+ if (typeof window === 'undefined' || typeof document === 'undefined') {
6821
+ // Resolve to null when imported server side. This makes the module
6822
+ // safe to import in an isomorphic code base.
6823
+ resolve(null);
6824
+ return;
6825
+ }
6826
+
6827
+ if (window.Stripe) {
6828
+ resolve(window.Stripe);
6829
+ return;
6830
+ }
6831
+
6832
+ try {
6833
+ var script = findScript();
6834
+
6835
+ if (script && params) ; else if (!script) {
6836
+ script = injectScript(params);
6837
+ } else if (script && onLoadListener !== null && onErrorListener !== null) {
6838
+ var _script$parentNode;
6839
+
6840
+ // remove event listeners
6841
+ script.removeEventListener('load', onLoadListener);
6842
+ script.removeEventListener('error', onErrorListener); // if script exists, but we are reloading due to an error,
6843
+ // reload script to trigger 'load' event
6844
+
6845
+ (_script$parentNode = script.parentNode) === null || _script$parentNode === void 0 ? void 0 : _script$parentNode.removeChild(script);
6846
+ script = injectScript(params);
6847
+ }
6848
+
6849
+ onLoadListener = onLoad(resolve, reject);
6850
+ onErrorListener = onError(reject);
6851
+ script.addEventListener('load', onLoadListener);
6852
+ script.addEventListener('error', onErrorListener);
6853
+ } catch (error) {
6854
+ reject(error);
6855
+ return;
6856
+ }
6857
+ }); // Resets stripePromise on error
6858
+
6859
+ return stripePromise["catch"](function (error) {
6860
+ stripePromise = null;
6861
+ return Promise.reject(error);
6862
+ });
6863
+ };
6864
+ var initStripe = function initStripe(maybeStripe, args, startTime) {
6865
+ if (maybeStripe === null) {
6866
+ return null;
6867
+ }
6868
+
6869
+ var stripe = maybeStripe.apply(undefined, args);
6870
+ registerWrapper(stripe, startTime);
6871
+ return stripe;
6872
+ }; // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
6873
+
6874
+ var stripePromise$1;
6875
+ var loadCalled = false;
6876
+
6877
+ var getStripePromise = function getStripePromise() {
6878
+ if (stripePromise$1) {
6879
+ return stripePromise$1;
6880
+ }
6881
+
6882
+ stripePromise$1 = loadScript(null)["catch"](function (error) {
6883
+ // clear cache on error
6884
+ stripePromise$1 = null;
6885
+ return Promise.reject(error);
6886
+ });
6887
+ return stripePromise$1;
6888
+ }; // Execute our own script injection after a tick to give users time to do their
6889
+ // own script injection.
6890
+
6891
+
6892
+ Promise.resolve().then(function () {
6893
+ return getStripePromise();
6894
+ })["catch"](function (error) {
6895
+ if (!loadCalled) {
6896
+ console.warn(error);
6897
+ }
6898
+ });
6899
+ var loadStripe = function loadStripe() {
6900
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
6901
+ args[_key] = arguments[_key];
6902
+ }
6903
+
6904
+ loadCalled = true;
6905
+ var startTime = Date.now(); // if previous attempts are unsuccessful, will re-load script
6906
+
6907
+ return getStripePromise().then(function (maybeStripe) {
6908
+ return initStripe(maybeStripe, args, startTime);
6909
+ });
6910
+ };
6911
+
6830
6912
  // Inner component that uses the Stripe hooks
6831
6913
  function PaymentFormInner({ eventDetails, formData, totalAmount, onSuccess, onError, }) {
6832
6914
  const t = useTranslations();
@@ -6855,13 +6937,25 @@ function PaymentFormInner({ eventDetails, formData, totalAmount, onSuccess, onEr
6855
6937
  },
6856
6938
  }, submitContent: jsxRuntime.jsx(jsxRuntime.Fragment, { children: submitLabel }), loadingContent: jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [spinner("var(--bw-surface-color)"), " ", t("button.processingPayment")] }) }));
6857
6939
  }
6858
- function StripePaymentForm({ config, eventDetails, formData, totalAmount, discountCode, giftCards, onSuccess, onError, systemConfig, stripePromise, stripeAppearance, upsellSelections = [], }) {
6940
+ function StripePaymentForm({ config, eventDetails, formData, totalAmount, discountCode, giftCards, onSuccess, onError, systemConfig, stripeAppearance, upsellSelections = [], }) {
6859
6941
  const t = useTranslations();
6860
6942
  const { locale } = useLocale();
6861
6943
  const [clientSecret, setClientSecret] = React.useState(null);
6862
6944
  const [paymentIntentId, setPaymentIntentId] = React.useState(null);
6863
6945
  const [isCreatingPaymentIntent, setIsCreatingPaymentIntent] = React.useState(false);
6864
6946
  const [paymentError, setPaymentError] = React.useState(null);
6947
+ const stripePromise = React.useMemo(() => {
6948
+ if (!systemConfig?.stripePublishableKey) {
6949
+ return null;
6950
+ }
6951
+ const stripeOptions = {
6952
+ locale: locale,
6953
+ };
6954
+ if (systemConfig.connectedAccountId) {
6955
+ stripeOptions.stripeAccount = systemConfig.connectedAccountId;
6956
+ }
6957
+ return loadStripe(systemConfig.stripePublishableKey, stripeOptions);
6958
+ }, [systemConfig?.stripePublishableKey, systemConfig?.connectedAccountId, locale]);
6865
6959
  const storageKey = typeof window !== "undefined"
6866
6960
  ? `bw_pi_${config?.organizationId}_${config?.eventInstanceId || eventDetails?.id}`
6867
6961
  : "";
@@ -6940,6 +7034,7 @@ function StripePaymentForm({ config, eventDetails, formData, totalAmount, discou
6940
7034
  customerEmail: formData.customerEmail?.trim(),
6941
7035
  customerPhone: formData.customerPhone?.trim(),
6942
7036
  comment: formData.comment?.trim(),
7037
+ ...(config.partnerContractId && { partnerContractId: config.partnerContractId }),
6943
7038
  ...(paymentIntentId && { paymentIntentId }),
6944
7039
  ...(upsellSelections && upsellSelections.length > 0 && { upsellSelections }),
6945
7040
  };
@@ -7009,7 +7104,7 @@ function StripePaymentForm({ config, eventDetails, formData, totalAmount, discou
7009
7104
  if (isFullyCoveredByGiftCards && totalAmount <= 0) {
7010
7105
  return (jsxRuntime.jsx(GiftCardOnlyBooking, { config: config, eventDetails: eventDetails, formData: formData, discountCode: discountCode, giftCards: giftCards, onSuccess: onSuccess, onError: onError, upsellSelections: upsellSelections }));
7011
7106
  }
7012
- if (isCreatingPaymentIntent || !clientSecret) {
7107
+ if (isCreatingPaymentIntent || !clientSecret || !stripePromise) {
7013
7108
  return (jsxRuntime.jsxs("div", { style: {
7014
7109
  display: "flex",
7015
7110
  alignItems: "center",
@@ -7047,6 +7142,49 @@ function StripePaymentForm({ config, eventDetails, formData, totalAmount, discou
7047
7142
  }, onError: onError }) }));
7048
7143
  }
7049
7144
 
7145
+ const HOLD_DURATION_SECONDS = 15 * 60;
7146
+ /**
7147
+ * Shows the remaining time on the 15-minute slot hold that the server places on
7148
+ * the pending order while the customer completes checkout. The timer is started
7149
+ * client-side when the payment step opens — matching the moment the pending
7150
+ * order (and its `holdExpiresAt`) is created server-side.
7151
+ */
7152
+ function HoldCountdown({ onExpire }) {
7153
+ const t = useTranslations();
7154
+ const [remaining, setRemaining] = React.useState(HOLD_DURATION_SECONDS);
7155
+ React.useEffect(() => {
7156
+ const startedAt = Date.now();
7157
+ const interval = setInterval(() => {
7158
+ const elapsed = Math.floor((Date.now() - startedAt) / 1000);
7159
+ const left = Math.max(0, HOLD_DURATION_SECONDS - elapsed);
7160
+ setRemaining(left);
7161
+ if (left <= 0) {
7162
+ clearInterval(interval);
7163
+ onExpire?.();
7164
+ }
7165
+ }, 1000);
7166
+ return () => clearInterval(interval);
7167
+ }, [onExpire]);
7168
+ const minutes = Math.floor(remaining / 60);
7169
+ const seconds = remaining % 60;
7170
+ const formatted = `${minutes}:${String(seconds).padStart(2, "0")}`;
7171
+ const isLow = remaining <= 60;
7172
+ return (jsxRuntime.jsxs("div", { style: {
7173
+ display: "flex",
7174
+ alignItems: "center",
7175
+ gap: "8px",
7176
+ padding: "10px 14px",
7177
+ borderRadius: "var(--bw-border-radius)",
7178
+ border: `1px solid ${isLow ? "var(--bw-error-color)" : "var(--bw-border-color)"}`,
7179
+ backgroundColor: "var(--bw-surface-color)",
7180
+ color: isLow ? "var(--bw-error-color)" : "var(--bw-text-color)",
7181
+ fontFamily: "var(--bw-font-family)",
7182
+ fontSize: "14px",
7183
+ }, children: [jsxRuntime.jsx(IconClock, {}), jsxRuntime.jsx("span", { children: remaining > 0
7184
+ ? t("hold.reservedFor", { time: formatted })
7185
+ : t("hold.expired") })] }));
7186
+ }
7187
+
7050
7188
  const FLAG_EMOJIS = {
7051
7189
  de: "🇩🇪",
7052
7190
  en: "🇬🇧",
@@ -7261,6 +7399,8 @@ function Sidebar({ isOpen, onClose, title, children, width = "450px", footer, })
7261
7399
  fontSize: "16px",
7262
7400
  color: "var(--bw-text-color)",
7263
7401
  fontFamily: "var(--bw-font-family)",
7402
+ textTransform: "var(--bw-text-transform, none)",
7403
+ letterSpacing: "var(--bw-letter-spacing, normal)",
7264
7404
  flex: 1,
7265
7405
  paddingRight: "12px",
7266
7406
  minWidth: 0,
@@ -11438,6 +11578,8 @@ const buttonBase = {
11438
11578
  transition: "all 0.2s ease",
11439
11579
  whiteSpace: "nowrap",
11440
11580
  border: "none",
11581
+ textTransform: "var(--bw-text-transform, none)",
11582
+ letterSpacing: "var(--bw-letter-spacing, normal)",
11441
11583
  };
11442
11584
  // CSS class name for button hover effects
11443
11585
  const buttonClassName = "bw-button-hover";
@@ -11454,6 +11596,11 @@ const buttonStyles = {
11454
11596
  color: "var(--bw-text-muted)",
11455
11597
  border: "1px solid var(--bw-border-color)",
11456
11598
  },
11599
+ // Size variants
11600
+ small: {
11601
+ padding: "8px 12px",
11602
+ fontSize: "13px",
11603
+ },
11457
11604
  // Full width modifier
11458
11605
  fullWidth: {
11459
11606
  width: "100%",
@@ -11526,6 +11673,12 @@ const sectionStyles = {
11526
11673
  // TEXT
11527
11674
  // ============================================
11528
11675
  const textStyles = {
11676
+ body: {
11677
+ fontSize: "14px",
11678
+ color: "var(--bw-text-color)",
11679
+ fontFamily: "var(--bw-font-family)",
11680
+ lineHeight: 1.5,
11681
+ },
11529
11682
  muted: {
11530
11683
  fontSize: "14px",
11531
11684
  color: "var(--bw-text-muted)",
@@ -11595,7 +11748,7 @@ const sectionHeaderStyles$1 = sectionStyles.header;
11595
11748
  const labelStyles$1 = formStyles.label;
11596
11749
  const inputStyles$1 = formStyles.input;
11597
11750
  const errorTextStyles$1 = formStyles.error;
11598
- function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError, isOpen, onClose, systemConfig, selectedUpsells = [], upsells = [], persistedState = null, onPersistedStateChange, }) {
11751
+ function BookingForm({ config, eventDetails, onSuccess, onError, isOpen, onClose, systemConfig, selectedUpsells = [], upsells = [], persistedState = null, onPersistedStateChange, }) {
11599
11752
  const t$1 = useTranslations();
11600
11753
  const { locale } = useLocale();
11601
11754
  const timezone = useTimezone();
@@ -11795,7 +11948,7 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
11795
11948
  newTotal: appliedDiscountCode.newTotal,
11796
11949
  }
11797
11950
  : null;
11798
- const hasPaymentProvider = Boolean(stripePromise || systemConfig?.paymentProvider === "mollie");
11951
+ const hasPaymentProvider = systemConfig?.paymentProvider === "stripe" || systemConfig?.paymentProvider === "mollie";
11799
11952
  const handleVoucherValidated = React.useCallback((voucher, _error) => {
11800
11953
  if (voucher) {
11801
11954
  setAppliedVouchers((prev) => [...prev, voucher]);
@@ -12061,7 +12214,7 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
12061
12214
  ...inputStyles$1,
12062
12215
  resize: "vertical",
12063
12216
  minHeight: "80px",
12064
- } })] })] })] })), checkoutStep === "payment" && (jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "24px" }, children: [jsxRuntime.jsxs("div", { style: cardStyles$1, children: [jsxRuntime.jsx("h2", { style: { ...sectionHeaderStyles$1, marginBottom: "16px" }, children: t$1("summary.title") }), jsxRuntime.jsxs("div", { style: { marginTop: "10px", display: "flex", flexDirection: "column", gap: "12px" }, children: [jsxRuntime.jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center" }, children: [jsxRuntime.jsx("span", { style: { color: "var(--bw-text-muted)", fontFamily: "var(--bw-font-family)" }, children: t$1("booking.price") }), jsxRuntime.jsxs("div", { style: {
12217
+ } })] })] })] })), checkoutStep === "payment" && (jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "24px" }, children: [jsxRuntime.jsx(HoldCountdown, {}), jsxRuntime.jsxs("div", { style: cardStyles$1, children: [jsxRuntime.jsx("h2", { style: { ...sectionHeaderStyles$1, marginBottom: "16px" }, children: t$1("summary.title") }), jsxRuntime.jsxs("div", { style: { marginTop: "10px", display: "flex", flexDirection: "column", gap: "12px" }, children: [jsxRuntime.jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center" }, children: [jsxRuntime.jsx("span", { style: { color: "var(--bw-text-muted)", fontFamily: "var(--bw-font-family)" }, children: t$1("booking.price") }), jsxRuntime.jsxs("div", { style: {
12065
12218
  color: "var(--bw-text-color)",
12066
12219
  fontWeight: 500,
12067
12220
  fontFamily: "var(--bw-font-family)",
@@ -12165,7 +12318,7 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
12165
12318
  fontFamily: "var(--bw-font-family)",
12166
12319
  marginTop: "8px",
12167
12320
  textAlign: "right",
12168
- }, children: t$1("summary.remainingOnSite", { amount: formatCurrency(totalAmount - depositAmount) }) }))] })] })] }), depositsOnlyBlocked && (jsxRuntime.jsx("div", { style: cardStyles$1, children: jsxRuntime.jsx("p", { style: { ...errorTextStyles$1, margin: 0 }, children: t$1("booking.depositNotConfigured") }) })), !hasPaymentProvider && !depositsOnlyBlocked && (jsxRuntime.jsx("div", { style: cardStyles$1, children: jsxRuntime.jsx("p", { style: { ...errorTextStyles$1, margin: 0 }, children: t$1("booking.paymentUnavailable") }) })), hasPaymentProvider && !depositsOnlyBlocked && (systemConfig?.paymentProvider === "mollie" ? (jsxRuntime.jsxs("div", { style: cardStyles$1, children: [jsxRuntime.jsx("h2", { style: { ...sectionHeaderStyles$1 }, children: t$1("summary.payment") }), jsxRuntime.jsx(MolliePaymentForm, { config: config, eventDetails: eventDetails, formData: paymentFormData, totalAmount: paymentAmount, discountCode: discountCodeProp, giftCards: appliedGiftCards, onSuccess: onSuccess, onError: onError, upsellSelections: aggregatedUpsellSelections(), mollieProfileId: systemConfig?.mollieProfileId, mollieTestmode: systemConfig?.mollieTestmode })] })) : (jsxRuntime.jsxs("div", { style: cardStyles$1, children: [jsxRuntime.jsx("h2", { style: { ...sectionHeaderStyles$1 }, children: t$1("summary.payment") }), jsxRuntime.jsx(StripePaymentForm, { config: config, eventDetails: eventDetails, formData: paymentFormData, totalAmount: paymentAmount, discountCode: discountCodeProp, giftCards: appliedGiftCards, onSuccess: onSuccess, onError: onError, systemConfig: systemConfig ?? null, stripePromise: stripePromise, stripeAppearance: stripeAppearance, upsellSelections: aggregatedUpsellSelections() })] })))] }))] }) }));
12321
+ }, children: t$1("summary.remainingOnSite", { amount: formatCurrency(totalAmount - depositAmount) }) }))] })] })] }), depositsOnlyBlocked && (jsxRuntime.jsx("div", { style: cardStyles$1, children: jsxRuntime.jsx("p", { style: { ...errorTextStyles$1, margin: 0 }, children: t$1("booking.depositNotConfigured") }) })), !hasPaymentProvider && !depositsOnlyBlocked && (jsxRuntime.jsx("div", { style: cardStyles$1, children: jsxRuntime.jsx("p", { style: { ...errorTextStyles$1, margin: 0 }, children: t$1("booking.paymentUnavailable") }) })), hasPaymentProvider && !depositsOnlyBlocked && (systemConfig?.paymentProvider === "mollie" ? (jsxRuntime.jsxs("div", { style: cardStyles$1, children: [jsxRuntime.jsx("h2", { style: { ...sectionHeaderStyles$1 }, children: t$1("summary.payment") }), jsxRuntime.jsx(MolliePaymentForm, { config: config, eventDetails: eventDetails, formData: paymentFormData, totalAmount: paymentAmount, discountCode: discountCodeProp, giftCards: appliedGiftCards, onSuccess: onSuccess, onError: onError, upsellSelections: aggregatedUpsellSelections(), mollieProfileId: systemConfig?.mollieProfileId, mollieTestmode: systemConfig?.mollieTestmode })] })) : (jsxRuntime.jsxs("div", { style: cardStyles$1, children: [jsxRuntime.jsx("h2", { style: { ...sectionHeaderStyles$1 }, children: t$1("summary.payment") }), jsxRuntime.jsx(StripePaymentForm, { config: config, eventDetails: eventDetails, formData: paymentFormData, totalAmount: paymentAmount, discountCode: discountCodeProp, giftCards: appliedGiftCards, onSuccess: onSuccess, onError: onError, systemConfig: systemConfig ?? null, stripeAppearance: stripeAppearance, upsellSelections: aggregatedUpsellSelections() })] })))] }))] }) }));
12169
12322
  }
12170
12323
 
12171
12324
  /**
@@ -12846,19 +12999,21 @@ function EventTypeDetailsDialog({ isOpen, onClose, eventType, onEventTypeSelect,
12846
12999
  textAlign: "right",
12847
13000
  }, children: jsxRuntime.jsxs("span", { children: [t("common.from"), " ", formatCurrency(eventType.minPrice)] }) })] }), isAvailable && (jsxRuntime.jsxs("button", { onClick: handleBookingClick, style: {
12848
13001
  backgroundColor: "var(--bw-highlight-color)",
12849
- color: "#ffffff",
13002
+ color: "var(--bw-button-text-color, #ffffff)",
12850
13003
  padding: "14px 28px",
12851
13004
  border: "none",
12852
13005
  borderRadius: "var(--bw-border-radius)",
12853
13006
  fontSize: "16px",
12854
13007
  fontWeight: 600,
12855
13008
  fontFamily: "var(--bw-font-family)",
13009
+ textTransform: "var(--bw-text-transform, none)",
13010
+ letterSpacing: "var(--bw-letter-spacing, normal)",
12856
13011
  display: "flex",
12857
13012
  alignItems: "center",
12858
13013
  gap: "8px",
12859
13014
  cursor: "pointer",
12860
13015
  transition: "all 0.2s ease",
12861
- }, children: [jsxRuntime.jsx(IconWave, { size: 20, color: "white" }), t("button.bookNow")] }))] }), !isAvailable && (jsxRuntime.jsx("div", { style: {
13016
+ }, children: [jsxRuntime.jsx(IconWave, { size: 20, color: "var(--bw-button-text-color, #ffffff)" }), t("button.bookNow")] }))] }), !isAvailable && (jsxRuntime.jsx("div", { style: {
12862
13017
  position: "absolute",
12863
13018
  inset: 0,
12864
13019
  backgroundColor: "rgba(0, 0, 0, 0.3)",
@@ -13272,6 +13427,8 @@ function VoucherPurchaseForm({ config, voucherConfig, eventTypes, isOpen, onClos
13272
13427
  const [stripeAppearance, setStripeAppearance] = React.useState(null);
13273
13428
  // Initialize Stripe
13274
13429
  React.useEffect(() => {
13430
+ if (!isOpen)
13431
+ return;
13275
13432
  if (paymentProvider !== "stripe")
13276
13433
  return;
13277
13434
  if (systemConfig?.stripePublishableKey && !stripePromise) {
@@ -13283,7 +13440,7 @@ function VoucherPurchaseForm({ config, voucherConfig, eventTypes, isOpen, onClos
13283
13440
  }
13284
13441
  setStripePromise(loadStripe(systemConfig.stripePublishableKey, stripeOptions));
13285
13442
  }
13286
- }, [paymentProvider, systemConfig, locale, stripePromise]);
13443
+ }, [isOpen, paymentProvider, systemConfig, locale, stripePromise]);
13287
13444
  // Initialize Stripe appearance
13288
13445
  React.useEffect(() => {
13289
13446
  const container = document.querySelector(".booking-widget-container");
@@ -14144,15 +14301,15 @@ function EventTypeSelection({ eventTypes, onEventTypeSelect, onInstancePreview,
14144
14301
  backgroundColor: isAvailable
14145
14302
  ? "var(--bw-success-color)"
14146
14303
  : "var(--bw-error-color)",
14147
- }, children: isAvailable ? t("events.spotsAvailable") : t("events.soldOut") }) }), jsxRuntime.jsx("div", { style: { position: "absolute", top: "16px", left: "16px", zIndex: 10 }, children: jsxRuntime.jsx("div", { style: {
14148
- fontSize: "13px",
14149
- color: "var(--bw-surface-color)",
14150
- fontWeight: 600,
14151
- backgroundColor: "var(--bw-highlight-color)",
14152
- padding: "2px 8px",
14153
- borderRadius: "var(--bw-border-radius)",
14154
- fontFamily: "var(--bw-font-family)",
14155
- }, children: eventType.category.name }) }), eventType.images && eventType.images.length > 0 && (jsxRuntime.jsx("div", { className: "event-type-img", style: { position: "relative", width: "100%", height: "300px" }, children: jsxRuntime.jsx(ImageCarousel, { images: eventType.images, eventName: eventType.name }) })), jsxRuntime.jsxs("div", { className: "event-type-content", style: {
14304
+ }, children: isAvailable ? t("events.spotsAvailable") : t("events.soldOut") }) }), eventType.images && eventType.images.length > 0 && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { style: { position: "absolute", top: "16px", left: "16px", zIndex: 10 }, children: jsxRuntime.jsx("div", { style: {
14305
+ fontSize: "13px",
14306
+ color: "var(--bw-surface-color)",
14307
+ fontWeight: 600,
14308
+ backgroundColor: "var(--bw-highlight-color)",
14309
+ padding: "2px 8px",
14310
+ borderRadius: "var(--bw-border-radius)",
14311
+ fontFamily: "var(--bw-font-family)",
14312
+ }, children: eventType.category.name }) }), jsxRuntime.jsx("div", { className: "event-type-img", style: { position: "relative", width: "100%", height: "300px" }, children: jsxRuntime.jsx(ImageCarousel, { images: eventType.images, eventName: eventType.name }) })] })), jsxRuntime.jsxs("div", { className: "event-type-content", style: {
14156
14313
  padding: "12px 18px",
14157
14314
  display: "flex",
14158
14315
  flexDirection: "column",
@@ -14247,8 +14404,17 @@ function EventTypeSelection({ eventTypes, onEventTypeSelect, onInstancePreview,
14247
14404
  if (!item)
14248
14405
  return jsxRuntime.jsx("div", { style: { height: "34px" } }, i);
14249
14406
  const hasDiscount = item.basePrice > 0 && item.price < item.basePrice;
14250
- return (jsxRuntime.jsxs("div", { onClick: (e) => { e.stopPropagation(); onInstancePreview?.(item.id, eventType.id); }, onMouseEnter: (e) => { if (onInstancePreview)
14251
- e.currentTarget.style.backgroundColor = "var(--bw-border-color)"; }, onMouseLeave: (e) => { e.currentTarget.style.backgroundColor = "transparent"; }, style: {
14407
+ const isRowBookable = item.bookingOpen && item.availableSpots > 0;
14408
+ return (jsxRuntime.jsxs("div", { onClick: (e) => {
14409
+ e.stopPropagation();
14410
+ if (isRowBookable) {
14411
+ onInstancePreview?.(item.id, eventType.id);
14412
+ }
14413
+ }, onMouseEnter: (e) => {
14414
+ if (onInstancePreview && isRowBookable) {
14415
+ e.currentTarget.style.backgroundColor = "var(--bw-highlight-subtle)";
14416
+ }
14417
+ }, onMouseLeave: (e) => { e.currentTarget.style.backgroundColor = "transparent"; }, style: {
14252
14418
  height: "34px",
14253
14419
  display: "grid",
14254
14420
  gridTemplateColumns: "auto 1fr auto 20px",
@@ -14257,12 +14423,13 @@ function EventTypeSelection({ eventTypes, onEventTypeSelect, onInstancePreview,
14257
14423
  width: "100%",
14258
14424
  fontSize: "13px",
14259
14425
  color: "var(--bw-text-muted)",
14260
- cursor: onInstancePreview ? "pointer" : "default",
14426
+ cursor: onInstancePreview && isRowBookable ? "pointer" : "default",
14261
14427
  borderRadius: "4px",
14262
14428
  padding: "0 2px",
14263
14429
  transition: "background 0.15s",
14264
14430
  boxSizing: "border-box",
14265
- }, children: [jsxRuntime.jsxs("span", { style: { whiteSpace: "nowrap", display: "flex", alignItems: "center", gap: "3px" }, children: [item.isSpecial && (jsxRuntime.jsx("span", { style: { color: "var(--bw-highlight-color)", fontSize: "11px", lineHeight: 1 }, children: "\u2605" })), formatWeekday(item.startTime, timezone, locale), " ", formatDate(item.startTime, timezone, locale)] }), jsxRuntime.jsx("span", { style: { overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", opacity: 0.75 }, children: item.name }), jsxRuntime.jsxs("span", { style: { display: "flex", alignItems: "center", gap: "4px", whiteSpace: "nowrap" }, children: [hasDiscount && (jsxRuntime.jsx("span", { style: { textDecoration: "line-through", opacity: 0.55, fontSize: "11px" }, children: formatCurrency(item.basePrice) })), jsxRuntime.jsx("span", { style: { fontWeight: 700, color: item.isSpecial ? "var(--bw-highlight-color)" : "var(--bw-text-color)" }, children: formatCurrency(item.price) })] }), item.specialDescription ? (jsxRuntime.jsx(InfoBadge, { text: item.specialDescription })) : (jsxRuntime.jsx("span", {}))] }, item.id));
14431
+ opacity: isRowBookable ? 1 : 0.55,
14432
+ }, children: [jsxRuntime.jsxs("span", { style: { whiteSpace: "nowrap", display: "flex", alignItems: "center", gap: "3px" }, children: [item.isSpecial && (jsxRuntime.jsx("span", { style: { color: "var(--bw-highlight-color)", fontSize: "11px", lineHeight: 1 }, children: "\u2605" })), formatWeekday(item.startTime, timezone, locale), " ", formatDate(item.startTime, timezone, locale)] }), jsxRuntime.jsx("span", { style: { overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", opacity: 0.75 }, children: item.name }), jsxRuntime.jsxs("span", { style: { display: "flex", alignItems: "center", gap: "4px", whiteSpace: "nowrap" }, children: [hasDiscount && (jsxRuntime.jsx("span", { style: { textDecoration: "line-through", opacity: 0.55, fontSize: "11px" }, children: formatCurrency(item.basePrice) })), jsxRuntime.jsx("span", { style: { fontWeight: 700, color: item.isSpecial ? "var(--bw-highlight-color)" : "var(--bw-text-color)" }, children: formatCurrency(item.price) })] }), item.isSpecial && item.specialDescription ? (jsxRuntime.jsx(InfoBadge, { text: item.specialDescription })) : (jsxRuntime.jsx("span", {}))] }, item.id));
14266
14433
  }) }), hasMoreDates && (jsxRuntime.jsx("button", { type: "button", onClick: (e) => {
14267
14434
  e.stopPropagation();
14268
14435
  onEventTypeSelect(eventType);
@@ -14314,12 +14481,14 @@ function EventTypeSelection({ eventTypes, onEventTypeSelect, onInstancePreview,
14314
14481
  transition: "all 0.2s ease",
14315
14482
  }, children: t("button.moreDetails") })), isAvailable && (jsxRuntime.jsxs("div", { style: {
14316
14483
  backgroundColor: "var(--bw-highlight-color)",
14317
- color: "var(--bw-surface-color)",
14484
+ color: "var(--bw-button-text-color, #ffffff)",
14318
14485
  padding: "12px 14px",
14319
14486
  borderRadius: "var(--bw-border-radius)",
14320
14487
  fontSize: "clamp(1rem, 2vw, 16px)",
14321
14488
  fontWeight: 600,
14322
14489
  fontFamily: "var(--bw-font-family)",
14490
+ textTransform: "var(--bw-text-transform, none)",
14491
+ letterSpacing: "var(--bw-letter-spacing, normal)",
14323
14492
  display: "flex",
14324
14493
  alignItems: "center",
14325
14494
  justifyContent: "center",
@@ -14328,7 +14497,7 @@ function EventTypeSelection({ eventTypes, onEventTypeSelect, onInstancePreview,
14328
14497
  border: "none",
14329
14498
  cursor: "pointer",
14330
14499
  transition: "all 0.2s ease",
14331
- }, children: [jsxRuntime.jsx(IconWave, { size: 15, color: "var(--bw-surface-color)" }), " ", t("button.bookNow")] }))] })] })] }), showVoucherAttachment && onVoucherClick && (jsxRuntime.jsx(VoucherAttachment, { onClick: () => onVoucherClick(eventType.id) })), !isAvailable && (jsxRuntime.jsx("div", { style: {
14500
+ }, children: [jsxRuntime.jsx(IconWave, { size: 15, color: "var(--bw-button-text-color, #ffffff)" }), " ", t("button.bookNow")] }))] })] })] }), showVoucherAttachment && onVoucherClick && (jsxRuntime.jsx(VoucherAttachment, { onClick: () => onVoucherClick(eventType.id) })), !isAvailable && (jsxRuntime.jsx("div", { style: {
14332
14501
  position: "absolute",
14333
14502
  inset: 0,
14334
14503
  backgroundColor: "rgba(0, 0, 0, 0.3)",
@@ -14349,6 +14518,74 @@ function EventTypeSelection({ eventTypes, onEventTypeSelect, onInstancePreview,
14349
14518
  }) }) })), jsxRuntime.jsx(PaymentLogosStrip, {}), jsxRuntime.jsx(EventTypeDetailsDialog, { isOpen: detailsDialogOpen, onClose: handleCloseDetails, eventType: selectedEventTypeForDetails, onEventTypeSelect: onEventTypeSelect })] }));
14350
14519
  }
14351
14520
 
14521
+ function WaitlistDialog({ apiBaseUrl, organizationId, eventInstanceId, eventName, onClose, }) {
14522
+ const t = useTranslations();
14523
+ const [name, setName] = React.useState("");
14524
+ const [email, setEmail] = React.useState("");
14525
+ const [phone, setPhone] = React.useState("");
14526
+ const [isSubmitting, setIsSubmitting] = React.useState(false);
14527
+ const [error, setError] = React.useState(null);
14528
+ const [position, setPosition] = React.useState(null);
14529
+ const handleSubmit = async () => {
14530
+ setError(null);
14531
+ if (!name.trim() || !email.trim()) {
14532
+ setError(t("waitlist.requiredFields"));
14533
+ return;
14534
+ }
14535
+ setIsSubmitting(true);
14536
+ try {
14537
+ const response = await fetch(getApiUrl(apiBaseUrl, "/booking/waitlist"), {
14538
+ method: "POST",
14539
+ headers: { "Content-Type": "application/json" },
14540
+ body: JSON.stringify({
14541
+ organizationId,
14542
+ eventInstanceId,
14543
+ name: name.trim(),
14544
+ email: email.trim(),
14545
+ phone: phone.trim() || undefined,
14546
+ }),
14547
+ });
14548
+ if (!response.ok) {
14549
+ throw new Error("request_failed");
14550
+ }
14551
+ const data = (await response.json());
14552
+ setPosition(data.position ?? null);
14553
+ }
14554
+ catch {
14555
+ setError(t("waitlist.submitError"));
14556
+ }
14557
+ finally {
14558
+ setIsSubmitting(false);
14559
+ }
14560
+ };
14561
+ return (jsxRuntime.jsx("div", { style: {
14562
+ position: "fixed",
14563
+ inset: 0,
14564
+ zIndex: 1000,
14565
+ display: "flex",
14566
+ alignItems: "center",
14567
+ justifyContent: "center",
14568
+ backgroundColor: "rgba(15, 23, 42, 0.6)",
14569
+ padding: "16px",
14570
+ fontFamily: "var(--bw-font-family)",
14571
+ }, onClick: onClose, children: jsxRuntime.jsx("div", { style: {
14572
+ width: "100%",
14573
+ maxWidth: "420px",
14574
+ backgroundColor: "var(--bw-surface-color)",
14575
+ border: "1px solid var(--bw-border-color)",
14576
+ borderRadius: "var(--bw-border-radius)",
14577
+ padding: "24px",
14578
+ }, onClick: (e) => e.stopPropagation(), children: position !== null ? (jsxRuntime.jsxs("div", { style: { textAlign: "center" }, children: [jsxRuntime.jsx("h3", { style: {
14579
+ margin: "0 0 8px",
14580
+ color: "var(--bw-highlight-color)",
14581
+ fontSize: "18px",
14582
+ }, children: t("waitlist.successTitle") }), jsxRuntime.jsx("p", { style: mergeStyles(textStyles.body, { marginBottom: "20px" }), children: t("waitlist.successMessage", { position }) }), jsxRuntime.jsx("button", { type: "button", style: mergeStyles(buttonStyles.primary, buttonStyles.fullWidth), onClick: onClose, children: t("waitlist.done") })] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("h3", { style: {
14583
+ margin: "0 0 4px",
14584
+ color: "var(--bw-highlight-color)",
14585
+ fontSize: "18px",
14586
+ }, children: t("waitlist.title") }), jsxRuntime.jsx("p", { style: mergeStyles(textStyles.muted, { marginBottom: "16px" }), children: t("waitlist.subtitle", { name: eventName }) }), jsxRuntime.jsxs("div", { style: { marginBottom: "12px" }, children: [jsxRuntime.jsx("label", { style: formStyles.label, htmlFor: "wl-name", children: t("waitlist.name") }), jsxRuntime.jsx("input", { id: "wl-name", style: formStyles.input, value: name, onChange: (e) => setName(e.target.value) })] }), jsxRuntime.jsxs("div", { style: { marginBottom: "12px" }, children: [jsxRuntime.jsx("label", { style: formStyles.label, htmlFor: "wl-email", children: t("waitlist.email") }), jsxRuntime.jsx("input", { id: "wl-email", type: "email", style: formStyles.input, value: email, onChange: (e) => setEmail(e.target.value) })] }), jsxRuntime.jsxs("div", { style: { marginBottom: "12px" }, children: [jsxRuntime.jsx("label", { style: formStyles.label, htmlFor: "wl-phone", children: t("waitlist.phone") }), jsxRuntime.jsx("input", { id: "wl-phone", type: "tel", style: formStyles.input, value: phone, onChange: (e) => setPhone(e.target.value) })] }), error && jsxRuntime.jsx("p", { style: formStyles.error, children: error }), jsxRuntime.jsxs("div", { style: { display: "flex", gap: "8px", marginTop: "16px" }, children: [jsxRuntime.jsx("button", { type: "button", style: mergeStyles(buttonStyles.secondary, buttonStyles.fullWidth), onClick: onClose, disabled: isSubmitting, children: t("waitlist.cancel") }), jsxRuntime.jsx("button", { type: "button", style: mergeStyles(buttonStyles.primary, buttonStyles.fullWidth), onClick: handleSubmit, disabled: isSubmitting, children: isSubmitting ? t("waitlist.submitting") : t("waitlist.join") })] })] })) }) }));
14587
+ }
14588
+
14352
14589
  const getAllocationBadgeInfo = (availableSpots, maxParticipants, t) => {
14353
14590
  const availabilityRatio = availableSpots / maxParticipants;
14354
14591
  if (availabilityRatio >= 0.6)
@@ -14473,12 +14710,13 @@ const PriceDisplay = ({ price, yearPrices, t }) => {
14473
14710
  boxShadow: displayInfo ? "0 2px 4px rgba(0, 0, 0, 0.2)" : "none",
14474
14711
  }, children: formatCurrency(price) }));
14475
14712
  };
14476
- function EventInstanceSelection({ eventInstances, selectedEventType, onEventInstanceSelect, onBackToEventTypes, isOpen, onClose, isLoadingEventInstances = false, isLoadingEventDetails = false, hasUpsellsStep = false, }) {
14713
+ function EventInstanceSelection({ eventInstances, selectedEventType, onEventInstanceSelect, onBackToEventTypes, isOpen, onClose, isLoadingEventInstances = false, isLoadingEventDetails = false, hasUpsellsStep = false, apiBaseUrl, organizationId, }) {
14477
14714
  const t = useTranslations();
14478
14715
  const { locale } = useLocale();
14479
14716
  const timezone = useTimezone();
14480
14717
  const [selectedEventInstanceId, setSelectedEventInstanceId] = React.useState(null);
14481
14718
  const [openGroups, setOpenGroups] = React.useState(new Set());
14719
+ const [waitlistEvent, setWaitlistEvent] = React.useState(null);
14482
14720
  const getMonthPriceDisplayInfo = (minPrice) => {
14483
14721
  return getPriceDisplayInfo(minPrice, yearPrices, t);
14484
14722
  };
@@ -14639,119 +14877,130 @@ function EventInstanceSelection({ eventInstances, selectedEventType, onEventInst
14639
14877
  fontFamily: "var(--bw-font-family)",
14640
14878
  }, children: t("instances.noAvailable") }), jsxRuntime.jsx("p", { style: { color: "var(--bw-text-muted)", fontFamily: "var(--bw-font-family)" }, children: t("instances.noAvailableMessage") })] }) }) }));
14641
14879
  }
14642
- return (jsxRuntime.jsx(Sidebar, { isOpen: isOpen, onClose: handleClose, title: `${selectedEventType?.name}`, footer: footerNav, children: jsxRuntime.jsxs("div", { style: { padding: "20px 10px" }, children: [jsxRuntime.jsx(FlowProgress, { currentStep: "date", hasUpsellsStep: hasUpsellsStep }), jsxRuntime.jsx("div", { style: { display: "flex", flexDirection: "column", gap: "16px" }, children: monthYearGroups.map(({ key, label, events, minPrice, year }, idx) => {
14643
- const monthPriceDisplayInfo = getMonthPriceDisplayInfo(minPrice);
14644
- return (jsxRuntime.jsxs(React.Fragment, { children: [idx > 0 && monthYearGroups[idx - 1].year !== year && (jsxRuntime.jsx("div", { style: { height: "1px", backgroundColor: "var(--bw-border-color)", margin: "4px 0" } })), jsxRuntime.jsx(Accordion, { title: label, priceInfo: jsxRuntime.jsx("div", { style: {
14645
- fontSize: "16px",
14646
- fontWeight: 500,
14647
- marginLeft: "auto",
14648
- padding: "4px 8px",
14649
- borderRadius: "var(--bw-border-radius-small)",
14650
- backgroundColor: monthPriceDisplayInfo
14651
- ? monthPriceDisplayInfo.backgroundColor
14652
- : "#14532d",
14653
- color: monthPriceDisplayInfo
14654
- ? monthPriceDisplayInfo.textColor
14655
- : "#4ade80",
14656
- boxShadow: monthPriceDisplayInfo
14657
- ? "0 2px 4px rgba(0, 0, 0, 0.2)"
14658
- : undefined,
14659
- }, children: `${t("common.from")} ${formatCurrency(minPrice)}` }), isOpen: openGroups.has(key), onToggle: () => toggleGroup(key), children: jsxRuntime.jsx("div", { style: { display: "flex", flexDirection: "column", gap: "12px", paddingTop: "12px" }, children: events.map((event) => {
14660
- const availableSpots = event.maxParticipants - event.participantCount;
14661
- const isFullyBooked = availableSpots === 0;
14662
- const startDate = new Date(event.startTime);
14663
- const isPastEvent = today.toISOString() >= startDate.toISOString();
14664
- const isDisabled = isFullyBooked || isPastEvent || !event.bookingOpen;
14665
- return (jsxRuntime.jsxs("div", { style: {
14666
- position: "relative",
14667
- border: "1px solid var(--bw-border-color)",
14668
- backgroundColor: "var(--bw-surface-color)",
14669
- borderRadius: "var(--bw-border-radius)",
14670
- padding: "16px 10px",
14671
- transition: "all 0.2s ease",
14672
- fontFamily: "var(--bw-font-family)",
14673
- opacity: isDisabled ? 0.3 : 1,
14674
- filter: isDisabled ? "grayscale(40%)" : "none",
14675
- cursor: isDisabled ? "not-allowed" : "pointer",
14676
- }, onClick: () => {
14677
- if (!isDisabled) {
14678
- handleEventInstanceSelect(event);
14679
- }
14680
- }, children: [selectedEventInstanceId === event.id && isLoadingEventDetails && (jsxRuntime.jsx("div", { style: {
14681
- position: "absolute",
14682
- inset: 0,
14683
- backgroundColor: "rgba(15, 23, 42, 0.8)",
14684
- borderRadius: "var(--bw-border-radius)",
14685
- display: "flex",
14686
- alignItems: "center",
14687
- justifyContent: "center",
14688
- }, children: jsxRuntime.jsx("div", { style: {
14689
- width: "32px",
14690
- height: "32px",
14691
- color: "var(--bw-highlight-color)",
14692
- opacity: 0.8,
14693
- fontSize: "32px",
14694
- }, children: spinner() }) })), jsxRuntime.jsx(SpecialPriceBadge, { price: event.price, yearPrices: yearPrices, t: t }), jsxRuntime.jsx(AllocationBadge, { availableSpots: availableSpots, maxParticipants: event.maxParticipants, t: t }), jsxRuntime.jsxs("div", { style: {
14695
- display: "flex",
14696
- justifyContent: "space-between",
14697
- width: "100%",
14698
- alignItems: "flex-start",
14699
- gap: "12px",
14700
- marginBottom: "4px",
14701
- }, children: [jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "flex-start", gap: "12px" }, children: [jsxRuntime.jsx("div", { style: {
14702
- fontSize: "16px",
14703
- transition: "all 0.2s ease",
14704
- borderRadius: "var(--bw-border-radius-small)",
14705
- borderTop: "4px solid var(--bw-border-color)",
14706
- border: "1px solid var(--bw-border-color)",
14707
- width: "40px",
14708
- height: "40px",
14709
- display: "flex",
14710
- alignItems: "center",
14711
- justifyContent: "center",
14712
- fontWeight: 700,
14713
- color: "var(--bw-text-color)",
14714
- backgroundColor: "var(--bw-background-color)",
14715
- }, children: startDate.getDate() }), jsxRuntime.jsxs("div", { style: {
14716
- fontSize: "16px",
14717
- color: "var(--bw-text-color)",
14718
- display: "flex",
14719
- flexDirection: "column",
14720
- alignItems: "flex-start",
14721
- justifyContent: "flex-start",
14722
- lineHeight: 1.25,
14723
- }, children: [jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("span", { style: { fontWeight: 600, marginBottom: "2px", textTransform: "capitalize" }, children: formatWeekday(event.startTime, timezone, locale) }), formatWeekday(event.startTime, timezone, locale) !==
14724
- formatWeekday(event.endTime, timezone, locale) && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("span", { style: { color: "var(--bw-text-muted)", fontSize: "14px" }, children: " - " }), jsxRuntime.jsx("span", { style: { fontWeight: 600, marginBottom: "2px", textTransform: "capitalize" }, children: formatWeekday(event.endTime, timezone, locale) })] }))] }), jsxRuntime.jsx("div", { children: formatWeekday(event.startTime, timezone, locale) ===
14725
- formatWeekday(event.endTime, timezone, locale) ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("span", { style: { color: "var(--bw-text-muted)", fontSize: "14px" }, children: formatTime(event.startTime, timezone, locale) }), jsxRuntime.jsx("span", { style: { color: "var(--bw-text-muted)", fontSize: "14px" }, children: " - " }), jsxRuntime.jsx("span", { style: { color: "var(--bw-text-muted)", fontSize: "14px" }, children: formatTime(event.endTime, timezone, locale) })] })) : (jsxRuntime.jsxs("span", { style: { color: "var(--bw-text-muted)", fontSize: "14px" }, children: [formatTime(event.startTime, timezone, locale), " ", t("instances.oclock")] })) })] }), jsxRuntime.jsxs("span", { style: {
14726
- fontSize: "12px",
14727
- fontWeight: 400,
14728
- color: "var(--bw-text-muted)",
14729
- marginLeft: "6px",
14730
- backgroundColor: "rgba(0, 0, 0, 0.05)",
14731
- whiteSpace: "nowrap",
14732
- padding: "2px 6px",
14733
- borderRadius: "var(--bw-border-radius-small)",
14734
- }, children: [event.durationDays, " ", event.durationDays > 1 ? t("common.days") : t("common.day")] })] }), jsxRuntime.jsx("div", { style: {
14735
- textAlign: "right",
14736
- display: "flex",
14737
- flexDirection: "column",
14738
- alignItems: "flex-end",
14739
- }, children: jsxRuntime.jsx(PriceDisplay, { price: event.price, yearPrices: yearPrices, t: t }) })] }), event.name !== selectedEventType?.name && (jsxRuntime.jsx("h4", { style: {
14740
- fontSize: "16px",
14741
- fontWeight: 600,
14742
- color: "var(--bw-text-color)",
14743
- lineHeight: 1.25,
14744
- margin: "0 0 2px 0",
14745
- display: "flex",
14746
- alignItems: "center",
14747
- gap: "8px",
14748
- maxWidth: "230px",
14749
- }, children: event.name }))] }, event.id));
14750
- }) }) })] }, key));
14751
- }) })] }) }));
14880
+ return (jsxRuntime.jsxs(Sidebar, { isOpen: isOpen, onClose: handleClose, title: `${selectedEventType?.name}`, footer: footerNav, children: [jsxRuntime.jsxs("div", { style: { padding: "20px 10px" }, children: [jsxRuntime.jsx(FlowProgress, { currentStep: "date", hasUpsellsStep: hasUpsellsStep }), jsxRuntime.jsx("div", { style: { display: "flex", flexDirection: "column", gap: "16px" }, children: monthYearGroups.map(({ key, label, events, minPrice, year }, idx) => {
14881
+ const monthPriceDisplayInfo = getMonthPriceDisplayInfo(minPrice);
14882
+ return (jsxRuntime.jsxs(React.Fragment, { children: [idx > 0 && monthYearGroups[idx - 1].year !== year && (jsxRuntime.jsx("div", { style: { height: "1px", backgroundColor: "var(--bw-border-color)", margin: "4px 0" } })), jsxRuntime.jsx(Accordion, { title: label, priceInfo: jsxRuntime.jsx("div", { style: {
14883
+ fontSize: "16px",
14884
+ fontWeight: 500,
14885
+ marginLeft: "auto",
14886
+ padding: "4px 8px",
14887
+ borderRadius: "var(--bw-border-radius-small)",
14888
+ backgroundColor: monthPriceDisplayInfo
14889
+ ? monthPriceDisplayInfo.backgroundColor
14890
+ : "#14532d",
14891
+ color: monthPriceDisplayInfo
14892
+ ? monthPriceDisplayInfo.textColor
14893
+ : "#4ade80",
14894
+ boxShadow: monthPriceDisplayInfo
14895
+ ? "0 2px 4px rgba(0, 0, 0, 0.2)"
14896
+ : undefined,
14897
+ }, children: `${t("common.from")} ${formatCurrency(minPrice)}` }), isOpen: openGroups.has(key), onToggle: () => toggleGroup(key), children: jsxRuntime.jsx("div", { style: { display: "flex", flexDirection: "column", gap: "12px", paddingTop: "12px" }, children: events.map((event) => {
14898
+ const availableSpots = event.maxParticipants - event.participantCount;
14899
+ const isFullyBooked = availableSpots === 0;
14900
+ const startDate = new Date(event.startTime);
14901
+ const isPastEvent = today.toISOString() >= startDate.toISOString();
14902
+ const isDisabled = isFullyBooked || isPastEvent || !event.bookingOpen;
14903
+ const canWaitlist = isFullyBooked &&
14904
+ !isPastEvent &&
14905
+ !!apiBaseUrl &&
14906
+ !!organizationId;
14907
+ return (jsxRuntime.jsxs("div", { style: {
14908
+ position: "relative",
14909
+ border: "1px solid var(--bw-border-color)",
14910
+ backgroundColor: "var(--bw-surface-color)",
14911
+ borderRadius: "var(--bw-border-radius)",
14912
+ padding: "16px 10px",
14913
+ transition: "all 0.2s ease",
14914
+ fontFamily: "var(--bw-font-family)",
14915
+ opacity: isDisabled && !canWaitlist ? 0.3 : 1,
14916
+ filter: isDisabled && !canWaitlist ? "grayscale(40%)" : "none",
14917
+ cursor: isDisabled
14918
+ ? canWaitlist
14919
+ ? "default"
14920
+ : "not-allowed"
14921
+ : "pointer",
14922
+ }, onClick: () => {
14923
+ if (!isDisabled) {
14924
+ handleEventInstanceSelect(event);
14925
+ }
14926
+ }, children: [selectedEventInstanceId === event.id && isLoadingEventDetails && (jsxRuntime.jsx("div", { style: {
14927
+ position: "absolute",
14928
+ inset: 0,
14929
+ backgroundColor: "rgba(15, 23, 42, 0.8)",
14930
+ borderRadius: "var(--bw-border-radius)",
14931
+ display: "flex",
14932
+ alignItems: "center",
14933
+ justifyContent: "center",
14934
+ }, children: jsxRuntime.jsx("div", { style: {
14935
+ width: "32px",
14936
+ height: "32px",
14937
+ color: "var(--bw-highlight-color)",
14938
+ opacity: 0.8,
14939
+ fontSize: "32px",
14940
+ }, children: spinner() }) })), jsxRuntime.jsx(SpecialPriceBadge, { price: event.price, yearPrices: yearPrices, t: t }), jsxRuntime.jsx(AllocationBadge, { availableSpots: availableSpots, maxParticipants: event.maxParticipants, t: t }), jsxRuntime.jsxs("div", { style: {
14941
+ display: "flex",
14942
+ justifyContent: "space-between",
14943
+ width: "100%",
14944
+ alignItems: "flex-start",
14945
+ gap: "12px",
14946
+ marginBottom: "4px",
14947
+ }, children: [jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "flex-start", gap: "12px" }, children: [jsxRuntime.jsx("div", { style: {
14948
+ fontSize: "16px",
14949
+ transition: "all 0.2s ease",
14950
+ borderRadius: "var(--bw-border-radius-small)",
14951
+ borderTop: "4px solid var(--bw-border-color)",
14952
+ border: "1px solid var(--bw-border-color)",
14953
+ width: "40px",
14954
+ height: "40px",
14955
+ display: "flex",
14956
+ alignItems: "center",
14957
+ justifyContent: "center",
14958
+ fontWeight: 700,
14959
+ color: "var(--bw-text-color)",
14960
+ backgroundColor: "var(--bw-background-color)",
14961
+ }, children: startDate.getDate() }), jsxRuntime.jsxs("div", { style: {
14962
+ fontSize: "16px",
14963
+ color: "var(--bw-text-color)",
14964
+ display: "flex",
14965
+ flexDirection: "column",
14966
+ alignItems: "flex-start",
14967
+ justifyContent: "flex-start",
14968
+ lineHeight: 1.25,
14969
+ }, children: [jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("span", { style: { fontWeight: 600, marginBottom: "2px", textTransform: "capitalize" }, children: formatWeekday(event.startTime, timezone, locale) }), formatWeekday(event.startTime, timezone, locale) !==
14970
+ formatWeekday(event.endTime, timezone, locale) && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("span", { style: { color: "var(--bw-text-muted)", fontSize: "14px" }, children: " - " }), jsxRuntime.jsx("span", { style: { fontWeight: 600, marginBottom: "2px", textTransform: "capitalize" }, children: formatWeekday(event.endTime, timezone, locale) })] }))] }), jsxRuntime.jsx("div", { children: formatWeekday(event.startTime, timezone, locale) ===
14971
+ formatWeekday(event.endTime, timezone, locale) ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("span", { style: { color: "var(--bw-text-muted)", fontSize: "14px" }, children: formatTime(event.startTime, timezone, locale) }), jsxRuntime.jsx("span", { style: { color: "var(--bw-text-muted)", fontSize: "14px" }, children: " - " }), jsxRuntime.jsx("span", { style: { color: "var(--bw-text-muted)", fontSize: "14px" }, children: formatTime(event.endTime, timezone, locale) })] })) : (jsxRuntime.jsxs("span", { style: { color: "var(--bw-text-muted)", fontSize: "14px" }, children: [formatTime(event.startTime, timezone, locale), " ", t("instances.oclock")] })) })] }), jsxRuntime.jsxs("span", { style: {
14972
+ fontSize: "12px",
14973
+ fontWeight: 400,
14974
+ color: "var(--bw-text-muted)",
14975
+ marginLeft: "6px",
14976
+ backgroundColor: "rgba(0, 0, 0, 0.05)",
14977
+ whiteSpace: "nowrap",
14978
+ padding: "2px 6px",
14979
+ borderRadius: "var(--bw-border-radius-small)",
14980
+ }, children: [event.durationDays, " ", event.durationDays > 1 ? t("common.days") : t("common.day")] })] }), jsxRuntime.jsx("div", { style: {
14981
+ textAlign: "right",
14982
+ display: "flex",
14983
+ flexDirection: "column",
14984
+ alignItems: "flex-end",
14985
+ }, children: jsxRuntime.jsx(PriceDisplay, { price: event.price, yearPrices: yearPrices, t: t }) })] }), event.name !== selectedEventType?.name && (jsxRuntime.jsx("h4", { style: {
14986
+ fontSize: "16px",
14987
+ fontWeight: 600,
14988
+ color: "var(--bw-text-color)",
14989
+ lineHeight: 1.25,
14990
+ margin: "0 0 2px 0",
14991
+ display: "flex",
14992
+ alignItems: "center",
14993
+ gap: "8px",
14994
+ maxWidth: "230px",
14995
+ }, children: event.name })), canWaitlist && (jsxRuntime.jsx("button", { type: "button", style: mergeStyles(buttonStyles.secondary, buttonStyles.fullWidth, buttonStyles.small, { marginTop: "8px" }), onClick: (e) => {
14996
+ e.stopPropagation();
14997
+ setWaitlistEvent(event);
14998
+ }, children: t("waitlist.joinCta") }))] }, event.id));
14999
+ }) }) })] }, key));
15000
+ }) })] }), waitlistEvent && apiBaseUrl && organizationId && (jsxRuntime.jsx(WaitlistDialog, { apiBaseUrl: apiBaseUrl, organizationId: organizationId, eventInstanceId: waitlistEvent.id, eventName: waitlistEvent.name, onClose: () => setWaitlistEvent(null) }))] }));
14752
15001
  }
14753
15002
 
14754
- function NextEventsPreview({ events, onEventSelect, onShowAll, showAllButtonText, showAllButton, isLoadingEventDetails = false, isLoadingShowAll = false, isLoading = false, }) {
15003
+ function NextEventsPreview({ events, onEventSelect, onShowAll, showAllButtonText, showAllButton, isLoadingEventDetails = false, isLoadingShowAll = false, isLoading = false, count = 3, }) {
14755
15004
  const t = useTranslations();
14756
15005
  const { locale } = useLocale();
14757
15006
  const timezone = useTimezone();
@@ -14803,7 +15052,7 @@ function NextEventsPreview({ events, onEventSelect, onShowAll, showAllButtonText
14803
15052
  };
14804
15053
  // Show loading skeleton
14805
15054
  if (isLoading) {
14806
- return jsxRuntime.jsx(NextEventsSkeleton, { count: 3 });
15055
+ return jsxRuntime.jsx(NextEventsSkeleton, { count: count });
14807
15056
  }
14808
15057
  if (events.length === 0) {
14809
15058
  return (jsxRuntime.jsx("div", { style: { maxWidth: "500px", margin: "0 auto", padding: "16px" }, children: jsxRuntime.jsxs("div", { style: {
@@ -15642,6 +15891,7 @@ let buffer = [];
15642
15891
  let flushTimer = null;
15643
15892
  let currentApiBaseUrl = "";
15644
15893
  let currentOrganizationId = "";
15894
+ let currentPartnerContractId = null;
15645
15895
  const FLUSH_INTERVAL_MS = 3000;
15646
15896
  function flush() {
15647
15897
  if (buffer.length === 0)
@@ -15683,9 +15933,10 @@ function scheduleFlush() {
15683
15933
  /**
15684
15934
  * Initialise the analytics module. Must be called once before `trackEvent`.
15685
15935
  */
15686
- function initAnalytics(apiBaseUrl, organizationId) {
15936
+ function initAnalytics(apiBaseUrl, organizationId, options) {
15687
15937
  currentApiBaseUrl = apiBaseUrl;
15688
15938
  currentOrganizationId = organizationId;
15939
+ currentPartnerContractId = options?.partnerContractId?.trim() || null;
15689
15940
  if (typeof window !== "undefined") {
15690
15941
  window.addEventListener("pagehide", flush);
15691
15942
  window.addEventListener("visibilitychange", () => {
@@ -15702,9 +15953,12 @@ function trackEvent(event, properties = {}) {
15702
15953
  return;
15703
15954
  if (!currentOrganizationId)
15704
15955
  return;
15956
+ const mergedProperties = currentPartnerContractId
15957
+ ? { ...properties, partnerContractId: currentPartnerContractId }
15958
+ : properties;
15705
15959
  buffer.push({
15706
15960
  event,
15707
- properties,
15961
+ properties: mergedProperties,
15708
15962
  domain: window.location.hostname,
15709
15963
  url: window.location.href,
15710
15964
  referrer: document.referrer,
@@ -15780,7 +16034,6 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
15780
16034
  const [sidebarOpen, setSidebarOpen] = React.useState(false);
15781
16035
  // Booking flow state
15782
16036
  const [eventDetails, setEventDetails] = React.useState(null);
15783
- const [stripePromise, setStripePromise] = React.useState(null);
15784
16037
  // SEPARATED LOADING STATES
15785
16038
  const [isLoading, setIsLoading] = React.useState(true);
15786
16039
  const [isLoadingEventInstances, setIsLoadingEventInstances] = React.useState(false);
@@ -15915,7 +16168,9 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
15915
16168
  React.useEffect(() => {
15916
16169
  if (!analyticsInitRef.current && config.organizationId) {
15917
16170
  analyticsInitRef.current = true;
15918
- initAnalytics(config.apiBaseUrl, config.organizationId);
16171
+ initAnalytics(config.apiBaseUrl, config.organizationId, {
16172
+ partnerContractId: config.partnerContractId,
16173
+ });
15919
16174
  trackEvent("widget_loaded", {
15920
16175
  viewMode,
15921
16176
  eventTypeId: config.eventTypeId,
@@ -15924,7 +16179,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
15924
16179
  isStandaloneVoucherMode,
15925
16180
  });
15926
16181
  }
15927
- }, [config.organizationId, config.apiBaseUrl]);
16182
+ }, [config.organizationId, config.apiBaseUrl, config.partnerContractId, viewMode, config.eventTypeId, config.categoryId, config.eventInstanceId, isStandaloneVoucherMode]);
15928
16183
  // Fire widget pageview once when Google Ads config is received from API
15929
16184
  const pageviewFiredRef = React.useRef(false);
15930
16185
  React.useEffect(() => {
@@ -15938,6 +16193,23 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
15938
16193
  const initializeWidget = async () => {
15939
16194
  try {
15940
16195
  setIsLoading(true);
16196
+ if (config.partnerContractId) {
16197
+ const validationResponse = await fetch(getApiUrl(config.apiBaseUrl, `/booking/validate-partner?organizationId=${encodeURIComponent(config.organizationId)}&contractId=${config.partnerContractId}`), {
16198
+ method: "GET",
16199
+ headers: createApiHeaders(config, locale),
16200
+ });
16201
+ if (!validationResponse.ok) {
16202
+ setError("Invalid partner configuration");
16203
+ config.onError?.("Invalid partner configuration");
16204
+ return;
16205
+ }
16206
+ const validationData = (await validationResponse.json());
16207
+ if (!validationData.valid) {
16208
+ setError("Invalid partner configuration");
16209
+ config.onError?.("Invalid partner configuration");
16210
+ return;
16211
+ }
16212
+ }
15941
16213
  // Load voucher config in parallel if voucher mode is requested or there's event selection
15942
16214
  if (isVoucherModeRequested || hasEventSelection) {
15943
16215
  void loadVoucherConfig();
@@ -16073,11 +16345,18 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
16073
16345
  setVoucherPurchaseResult(voucherData.voucherResult);
16074
16346
  setIsSuccess(true);
16075
16347
  setSuccessPaymentId(null);
16348
+ return;
16076
16349
  }
16077
16350
  }
16078
16351
  catch {
16079
- // Ignore lookup errors; webhook eventual consistency may delay data.
16080
- }
16352
+ // Fall back to booking success flow if voucher lookup fails.
16353
+ }
16354
+ // Not a voucher purchase: treat as a booking return and open the
16355
+ // booking confirmation, mirroring the Stripe redirect handler.
16356
+ trackEvent("booking_completed", { paymentIntentId: mollieReturn.molliePaymentId, source: "mollie_redirect" });
16357
+ setVoucherPurchaseResult(null);
16358
+ setSuccessPaymentId(mollieReturn.molliePaymentId);
16359
+ setIsSuccess(true);
16081
16360
  };
16082
16361
  if (!isLoading) {
16083
16362
  void handleMollieVoucherReturn();
@@ -16266,27 +16545,6 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
16266
16545
  widgetPaymentMode: data.widgetPaymentMode ?? "all",
16267
16546
  });
16268
16547
  }
16269
- if (data.stripePublishableKey) {
16270
- const stripeOptions = {
16271
- locale: locale,
16272
- };
16273
- if (data.connectedAccountId) {
16274
- stripeOptions.stripeAccount = data.connectedAccountId;
16275
- }
16276
- if (typeof window !== "undefined" && window.document) {
16277
- try {
16278
- void (window.parent === window || window.parent.location.href);
16279
- if (!stripeOptions.betas) {
16280
- stripeOptions.betas = [];
16281
- }
16282
- }
16283
- catch {
16284
- console.warn("[WIDGET] Detected restricted environment, adjusting Stripe options");
16285
- stripeOptions.betas = [];
16286
- }
16287
- }
16288
- setStripePromise(loadStripe(data.stripePublishableKey, stripeOptions));
16289
- }
16290
16548
  if (!skipInstanceAutoSelectRef.current && data.eventInstances.length === 1) {
16291
16549
  setSelectedEventInstance(data.eventInstances[0]);
16292
16550
  setCurrentStep("booking");
@@ -16326,29 +16584,6 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
16326
16584
  roundPricesEnabled: data.roundPricesEnabled ?? true,
16327
16585
  widgetPaymentMode: data.widgetPaymentMode ?? "all",
16328
16586
  });
16329
- if (!stripePromise && data.stripePublishableKey) {
16330
- const stripeOptions = {
16331
- locale: locale,
16332
- };
16333
- if (data.connectedAccountId) {
16334
- stripeOptions.stripeAccount = data.connectedAccountId;
16335
- }
16336
- if (typeof window !== "undefined" && window.document) {
16337
- try {
16338
- // Check if we have parent access (not in a cross-origin iframe)
16339
- void (window.parent === window || window.parent.location.href);
16340
- stripeOptions.apiVersion = "2025-02-24.acacia";
16341
- if (!stripeOptions.betas) {
16342
- stripeOptions.betas = [];
16343
- }
16344
- }
16345
- catch {
16346
- console.warn("[WIDGET] Detected restricted environment, adjusting Stripe options");
16347
- stripeOptions.betas = [];
16348
- }
16349
- }
16350
- setStripePromise(loadStripe(data.stripePublishableKey, stripeOptions));
16351
- }
16352
16587
  }
16353
16588
  else {
16354
16589
  const errorMessage = data.error || t("error.loadEventDetails");
@@ -16617,12 +16852,12 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
16617
16852
  startTime: cardPreviewItem.startTime,
16618
16853
  endTime: cardPreviewItem.startTime,
16619
16854
  price: cardPreviewItem.price,
16620
- maxParticipants: cardPreviewItem.availableSpots + 1,
16621
- participantCount: 1,
16855
+ maxParticipants: Math.max(cardPreviewItem.availableSpots, 0),
16856
+ participantCount: 0,
16622
16857
  availableSpots: cardPreviewItem.availableSpots,
16623
16858
  durationDays: 1,
16624
16859
  durationPerDay: 1,
16625
- bookingOpen: true,
16860
+ bookingOpen: cardPreviewItem.bookingOpen && cardPreviewItem.availableSpots > 0,
16626
16861
  }
16627
16862
  : null;
16628
16863
  if (eventInstance) {
@@ -16810,7 +17045,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
16810
17045
  }
16811
17046
  // Main view based on view mode
16812
17047
  if (viewMode === "next-events" && showingPreview) {
16813
- return (jsxRuntime.jsxs(StyleProvider, { config: config, children: [jsxRuntime.jsxs("div", { ref: setWidgetContainerRef, children: [jsxRuntime.jsx(NextEventsPreview, { events: upcomingEvents, onEventSelect: handleUpcomingEventSelect, onShowAll: handleShowAllEvents, showAllButtonText: nextEventsSettings.showAllButtonText, showAllButton: nextEventsSettings.showAllButton, isLoadingEventDetails: isLoadingEventDetails, isLoadingShowAll: isLoadingShowAll, isLoading: isLoading }), shouldRenderBookingForm && eventDetails && (jsxRuntime.jsx(BookingForm, { config: config, eventDetails: eventDetails, stripePromise: stripePromise, onSuccess: handleBookingSuccess, onError: handleBookingError, isOpen: currentStep === "booking" && !!eventDetails, onClose: handleBackFromBooking, systemConfig: systemConfig, selectedUpsells: selectedUpsells, upsells: upsells, persistedState: bookingPersistedState, onPersistedStateChange: setBookingPersistedState })), jsxRuntime.jsx(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
17048
+ return (jsxRuntime.jsxs(StyleProvider, { config: config, children: [jsxRuntime.jsxs("div", { ref: setWidgetContainerRef, children: [jsxRuntime.jsx(NextEventsPreview, { events: upcomingEvents, onEventSelect: handleUpcomingEventSelect, onShowAll: handleShowAllEvents, showAllButtonText: nextEventsSettings.showAllButtonText, showAllButton: nextEventsSettings.showAllButton, isLoadingEventDetails: isLoadingEventDetails, isLoadingShowAll: isLoadingShowAll, isLoading: isLoading, count: nextEventsSettings.count }), shouldRenderBookingForm && eventDetails && (jsxRuntime.jsx(BookingForm, { config: config, eventDetails: eventDetails, onSuccess: handleBookingSuccess, onError: handleBookingError, isOpen: currentStep === "booking" && !!eventDetails, onClose: handleBackFromBooking, systemConfig: systemConfig, selectedUpsells: selectedUpsells, upsells: upsells, persistedState: bookingPersistedState, onPersistedStateChange: setBookingPersistedState })), jsxRuntime.jsx(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
16814
17049
  setIsSuccess(false);
16815
17050
  setCurrentStep("eventTypes");
16816
17051
  setShowingPreview(true);
@@ -16831,7 +17066,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
16831
17066
  }, config: config, googleAdsConfig: googleAdsConfig, onError: setError, paymentIntentId: successPaymentId })] }), showPromoDialog && config.promo && (jsxRuntime.jsx(PromoDialog, { config: config.promo, onClose: handlePromoDialogClose, onCtaClick: handlePromoCtaClick }))] }));
16832
17067
  }
16833
17068
  if (viewMode === "specials" && showingPreview) {
16834
- return (jsxRuntime.jsxs(StyleProvider, { config: config, children: [jsxRuntime.jsxs("div", { ref: setWidgetContainerRef, children: [jsxRuntime.jsx(SpecialsView, { specials: specials, onEventSelect: handleUpcomingEventSelect, isLoading: isLoadingSpecials, showSavingsAmount: config.specialsSettings?.showSavingsAmount ?? true, showSavingsPercent: config.specialsSettings?.showSavingsPercent ?? false, emptyStateText: config.specialsSettings?.emptyStateText }), shouldRenderBookingForm && eventDetails && (jsxRuntime.jsx(BookingForm, { config: config, eventDetails: eventDetails, stripePromise: stripePromise, onSuccess: handleBookingSuccess, onError: handleBookingError, isOpen: currentStep === "booking" && !!eventDetails, onClose: handleBackFromBooking, systemConfig: systemConfig, selectedUpsells: selectedUpsells, upsells: upsells, persistedState: bookingPersistedState, onPersistedStateChange: setBookingPersistedState })), jsxRuntime.jsx(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
17069
+ return (jsxRuntime.jsxs(StyleProvider, { config: config, children: [jsxRuntime.jsxs("div", { ref: setWidgetContainerRef, children: [jsxRuntime.jsx(SpecialsView, { specials: specials, onEventSelect: handleUpcomingEventSelect, isLoading: isLoadingSpecials, showSavingsAmount: config.specialsSettings?.showSavingsAmount ?? true, showSavingsPercent: config.specialsSettings?.showSavingsPercent ?? false, emptyStateText: config.specialsSettings?.emptyStateText }), shouldRenderBookingForm && eventDetails && (jsxRuntime.jsx(BookingForm, { config: config, eventDetails: eventDetails, onSuccess: handleBookingSuccess, onError: handleBookingError, isOpen: currentStep === "booking" && !!eventDetails, onClose: handleBackFromBooking, systemConfig: systemConfig, selectedUpsells: selectedUpsells, upsells: upsells, persistedState: bookingPersistedState, onPersistedStateChange: setBookingPersistedState })), jsxRuntime.jsx(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
16835
17070
  setIsSuccess(false);
16836
17071
  setCurrentStep("eventTypes");
16837
17072
  setShowingPreview(true);
@@ -16845,13 +17080,13 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
16845
17080
  }, config: config, googleAdsConfig: googleAdsConfig, onError: setError, paymentIntentId: successPaymentId })] }), showPromoDialog && config.promo && (jsxRuntime.jsx(PromoDialog, { config: config.promo, onClose: handlePromoDialogClose, onCtaClick: handlePromoCtaClick }))] }));
16846
17081
  }
16847
17082
  if (viewMode === "next-events" && !showingPreview && currentStep === "eventInstances") {
16848
- return (jsxRuntime.jsxs(StyleProvider, { config: config, children: [jsxRuntime.jsxs("div", { ref: setWidgetContainerRef, children: [shouldRenderInstanceSelection && (jsxRuntime.jsx(EventInstanceSelection, { eventInstances: eventInstances, selectedEventType: selectedEventType, onEventInstanceSelect: handleEventInstanceSelect, onBackToEventTypes: () => {
17083
+ return (jsxRuntime.jsxs(StyleProvider, { config: config, children: [jsxRuntime.jsxs("div", { ref: setWidgetContainerRef, children: [jsxRuntime.jsx(NextEventsPreview, { events: upcomingEvents, onEventSelect: handleUpcomingEventSelect, onShowAll: handleShowAllEvents, showAllButtonText: nextEventsSettings.showAllButtonText, showAllButton: nextEventsSettings.showAllButton, isLoadingEventDetails: isLoadingEventDetails, isLoadingShowAll: isLoadingShowAll, isLoading: isLoading, count: nextEventsSettings.count }), shouldRenderInstanceSelection && (jsxRuntime.jsx(EventInstanceSelection, { eventInstances: eventInstances, selectedEventType: selectedEventType, onEventInstanceSelect: handleEventInstanceSelect, onBackToEventTypes: () => {
16849
17084
  setShowingPreview(true);
16850
17085
  setCurrentStep("eventTypes");
16851
17086
  }, isOpen: currentStep === "eventInstances", onClose: () => {
16852
17087
  setShowingPreview(true);
16853
17088
  setCurrentStep("eventTypes");
16854
- }, isLoadingEventInstances: isLoadingEventInstances, isLoadingEventDetails: isLoadingEventDetails, hasUpsellsStep: hasUpsellsFlowStep })), jsxRuntime.jsx(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
17089
+ }, isLoadingEventInstances: isLoadingEventInstances, isLoadingEventDetails: isLoadingEventDetails, hasUpsellsStep: hasUpsellsFlowStep, apiBaseUrl: config.apiBaseUrl, organizationId: config.organizationId })), jsxRuntime.jsx(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
16855
17090
  setIsSuccess(false);
16856
17091
  setCurrentStep("eventTypes");
16857
17092
  setShowingPreview(true);
@@ -16868,8 +17103,11 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
16868
17103
  }, config: config, googleAdsConfig: googleAdsConfig, onError: setError, paymentIntentId: successPaymentId })] }), showPromoDialog && config.promo && (jsxRuntime.jsx(PromoDialog, { config: config.promo, onClose: handlePromoDialogClose, onCtaClick: handlePromoCtaClick }))] }));
16869
17104
  }
16870
17105
  if (viewMode === "button" && (isSingleEventTypeMode || isDirectInstanceMode)) {
17106
+ // Busy only while genuinely loading. Note: we intentionally do NOT key off
17107
+ // `shouldRenderInstanceSelection && !sidebarOpen` — that flag stays true after the
17108
+ // sidebar is closed (only `sidebarOpen` resets), which left the spinner stuck on.
16871
17109
  const isButtonBusy = (isDirectInstanceMode && isLoadingEventDetails) ||
16872
- (!isDirectInstanceMode && (isLoadingEventInstances || (shouldRenderInstanceSelection && !sidebarOpen)));
17110
+ (!isDirectInstanceMode && isLoadingEventInstances);
16873
17111
  return (jsxRuntime.jsxs(StyleProvider, { config: config, children: [jsxRuntime.jsxs("div", { ref: setWidgetContainerRef, style: {
16874
17112
  display: "flex",
16875
17113
  justifyContent: "center",
@@ -16880,13 +17118,15 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
16880
17118
  alignItems: "center",
16881
17119
  gap: "10px",
16882
17120
  backgroundColor: "var(--bw-highlight-color)",
16883
- color: "white",
17121
+ color: "var(--bw-button-text-color, #ffffff)",
16884
17122
  padding: "16px 32px",
16885
17123
  border: "none",
16886
17124
  borderRadius: "var(--bw-border-radius)",
16887
17125
  fontSize: "18px",
16888
17126
  fontWeight: 600,
16889
17127
  fontFamily: "var(--bw-font-family)",
17128
+ textTransform: "var(--bw-text-transform, none)",
17129
+ letterSpacing: "var(--bw-letter-spacing, normal)",
16890
17130
  boxShadow: "var(--bw-shadow-md)",
16891
17131
  cursor: isButtonBusy ? "default" : "pointer",
16892
17132
  opacity: isButtonBusy ? 0.85 : 1,
@@ -16901,8 +17141,8 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
16901
17141
  setSidebarOpen(true);
16902
17142
  setShouldRenderInstanceSelection(true);
16903
17143
  }
16904
- }, children: [isButtonBusy && jsxRuntime.jsx(Spinner, { size: 20, borderColor: "white" }), config.buttonText ||
16905
- (isDirectInstanceMode ? t("button.bookNow") : t("button.viewDates"))] }), shouldRenderInstanceSelection && (jsxRuntime.jsx(EventInstanceSelection, { eventInstances: eventInstances, selectedEventType: selectedEventType, onEventInstanceSelect: handleEventInstanceSelect, onBackToEventTypes: () => setSidebarOpen(false), isOpen: sidebarOpen && currentStep === "eventInstances", onClose: () => setSidebarOpen(false), isLoadingEventInstances: isLoadingEventInstances, isLoadingEventDetails: isLoadingEventDetails, hasUpsellsStep: hasUpsellsFlowStep })), shouldRenderUpsells && (jsxRuntime.jsx(UpsellsStep, { upsells: upsells, selectedUpsells: selectedUpsells, participantCount: tempParticipantCount, isLoading: isLoadingUpsells, isOpen: currentStep === "upsells", onClose: () => setCurrentStep("eventInstances"), onSelect: handleUpsellsSelect, onContinue: handleUpsellsContinue, onBack: handleUpsellsBack })), shouldRenderBookingForm && eventDetails && (jsxRuntime.jsx(BookingForm, { config: config, eventDetails: eventDetails, stripePromise: stripePromise, onSuccess: handleBookingSuccess, onError: handleBookingError, isOpen: currentStep === "booking" && !!eventDetails, onClose: handleBackFromBooking, systemConfig: systemConfig, selectedUpsells: selectedUpsells, upsells: upsells, persistedState: bookingPersistedState, onPersistedStateChange: setBookingPersistedState })), jsxRuntime.jsx(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
17144
+ }, children: [isButtonBusy && jsxRuntime.jsx(Spinner, { size: 20, borderColor: "var(--bw-button-text-color, #ffffff)" }), config.buttonText ||
17145
+ (isDirectInstanceMode ? t("button.bookNow") : t("button.viewDates"))] }), shouldRenderInstanceSelection && (jsxRuntime.jsx(EventInstanceSelection, { eventInstances: eventInstances, selectedEventType: selectedEventType, onEventInstanceSelect: handleEventInstanceSelect, onBackToEventTypes: () => setSidebarOpen(false), isOpen: sidebarOpen && currentStep === "eventInstances", onClose: () => setSidebarOpen(false), isLoadingEventInstances: isLoadingEventInstances, isLoadingEventDetails: isLoadingEventDetails, hasUpsellsStep: hasUpsellsFlowStep, apiBaseUrl: config.apiBaseUrl, organizationId: config.organizationId })), shouldRenderUpsells && (jsxRuntime.jsx(UpsellsStep, { upsells: upsells, selectedUpsells: selectedUpsells, participantCount: tempParticipantCount, isLoading: isLoadingUpsells, isOpen: currentStep === "upsells", onClose: () => setCurrentStep("eventInstances"), onSelect: handleUpsellsSelect, onContinue: handleUpsellsContinue, onBack: handleUpsellsBack })), shouldRenderBookingForm && eventDetails && (jsxRuntime.jsx(BookingForm, { config: config, eventDetails: eventDetails, onSuccess: handleBookingSuccess, onError: handleBookingError, isOpen: currentStep === "booking" && !!eventDetails, onClose: handleBackFromBooking, systemConfig: systemConfig, selectedUpsells: selectedUpsells, upsells: upsells, persistedState: bookingPersistedState, onPersistedStateChange: setBookingPersistedState })), jsxRuntime.jsx(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
16906
17146
  setIsSuccess(false);
16907
17147
  setCurrentStep("eventTypes");
16908
17148
  setSidebarOpen(false);
@@ -16963,7 +17203,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
16963
17203
  };
16964
17204
  };
16965
17205
  const backHandlers = getBackHandlers();
16966
- return (jsxRuntime.jsxs(StyleProvider, { config: config, children: [jsxRuntime.jsxs("div", { ref: setWidgetContainerRef, children: [cardsView, shouldRenderInstanceSelection && (jsxRuntime.jsx(EventInstanceSelection, { eventInstances: eventInstances, selectedEventType: selectedEventType, onEventInstanceSelect: handleEventInstanceSelect, onBackToEventTypes: handleBackToEventTypes, isOpen: currentStep === "eventInstances", onClose: handleBackToEventTypes, isLoadingEventInstances: isLoadingEventInstances, isLoadingEventDetails: isLoadingEventDetails, hasUpsellsStep: hasUpsellsFlowStep })), shouldRenderUpsells && (jsxRuntime.jsx(UpsellsStep, { upsells: upsells, selectedUpsells: selectedUpsells, participantCount: tempParticipantCount, isLoading: isLoadingUpsells, isOpen: currentStep === "upsells", onClose: () => setCurrentStep("eventInstances"), onSelect: handleUpsellsSelect, onContinue: handleUpsellsContinue, onBack: handleUpsellsBack })), shouldRenderBookingForm && eventDetails && (jsxRuntime.jsx(BookingForm, { config: config, eventDetails: eventDetails, stripePromise: stripePromise, onSuccess: handleBookingSuccess, onError: handleBookingError, isOpen: currentStep === "booking" && !!eventDetails, onClose: backHandlers.onClose, systemConfig: systemConfig, selectedUpsells: selectedUpsells, upsells: upsells, persistedState: bookingPersistedState, onPersistedStateChange: setBookingPersistedState })), jsxRuntime.jsx(BookingSuccessModal, { isOpen: isSuccess && !voucherPurchaseResult, onClose: () => {
17206
+ return (jsxRuntime.jsxs(StyleProvider, { config: config, children: [jsxRuntime.jsxs("div", { ref: setWidgetContainerRef, children: [cardsView, shouldRenderInstanceSelection && (jsxRuntime.jsx(EventInstanceSelection, { eventInstances: eventInstances, selectedEventType: selectedEventType, onEventInstanceSelect: handleEventInstanceSelect, onBackToEventTypes: handleBackToEventTypes, isOpen: currentStep === "eventInstances", onClose: handleBackToEventTypes, isLoadingEventInstances: isLoadingEventInstances, isLoadingEventDetails: isLoadingEventDetails, hasUpsellsStep: hasUpsellsFlowStep, apiBaseUrl: config.apiBaseUrl, organizationId: config.organizationId })), shouldRenderUpsells && (jsxRuntime.jsx(UpsellsStep, { upsells: upsells, selectedUpsells: selectedUpsells, participantCount: tempParticipantCount, isLoading: isLoadingUpsells, isOpen: currentStep === "upsells", onClose: () => setCurrentStep("eventInstances"), onSelect: handleUpsellsSelect, onContinue: handleUpsellsContinue, onBack: handleUpsellsBack })), shouldRenderBookingForm && eventDetails && (jsxRuntime.jsx(BookingForm, { config: config, eventDetails: eventDetails, onSuccess: handleBookingSuccess, onError: handleBookingError, isOpen: currentStep === "booking" && !!eventDetails, onClose: backHandlers.onClose, systemConfig: systemConfig, selectedUpsells: selectedUpsells, upsells: upsells, persistedState: bookingPersistedState, onPersistedStateChange: setBookingPersistedState })), jsxRuntime.jsx(BookingSuccessModal, { isOpen: isSuccess && !voucherPurchaseResult, onClose: () => {
16967
17207
  setIsSuccess(false);
16968
17208
  setCurrentStep("eventTypes");
16969
17209
  setSuccessPaymentId(null);
@@ -17027,7 +17267,7 @@ function styleInject(css, ref) {
17027
17267
  }
17028
17268
  }
17029
17269
 
17030
- var css_248z = ".booking-widget-container{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;box-sizing:border-box;color:var(--bw-text-color,#1e293b);direction:ltr;display:block;font-family:var(--bw-font-family,system-ui,-apple-system,sans-serif);font-size:var(--bw-font-size,14px);isolation:isolate;line-height:1.5;position:relative;text-align:left}.booking-widget-container *,.booking-widget-container :after,.booking-widget-container :before{box-sizing:border-box;margin:0;padding:0}.booking-widget-container input,.booking-widget-container select,.booking-widget-container textarea{font-family:inherit;font-size:inherit;line-height:inherit}.booking-widget-container button{background:none;border:none;cursor:pointer;font-family:inherit;font-size:inherit}.booking-widget-container a{color:inherit;text-decoration:none}.booking-widget-container img{display:block;height:auto;max-width:100%;vertical-align:middle}.booking-widget-container ol,.booking-widget-container ul{list-style:none}.booking-widget-container h1,.booking-widget-container h2,.booking-widget-container h3,.booking-widget-container h4,.booking-widget-container h5,.booking-widget-container h6{font-size:inherit;font-weight:inherit}#booking-widget-portal{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;color:var(--bw-text-color,#1e293b);direction:ltr;font-family:var(--bw-font-family,system-ui,-apple-system,sans-serif);font-size:var(--bw-font-size,14px);isolation:isolate;line-height:1.5;text-align:left}#booking-widget-portal *,#booking-widget-portal :after,#booking-widget-portal :before{box-sizing:border-box}#booking-widget-portal-root{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;color:var(--bw-text-color,#1e293b);font-family:var(--bw-font-family,system-ui,-apple-system,sans-serif);font-size:var(--bw-font-size,14px);line-height:1.5}:root{--bw-highlight-color:#00b1aa;--bw-highlight-color-rgb:0,177,170;--bw-background-color:#f8fdfe;--bw-surface-color:#fff;--bw-text-color:#0e7490;--bw-text-muted:rgba(14,116,144,.7);--bw-border-color:#bae6fd;--bw-success-color:#38bdf8;--bw-warning-color:#fbbf24;--bw-error-color:#f43f5e;--bw-border-radius:18px;--bw-border-radius-small:calc(var(--bw-border-radius)*0.8);--bw-spacing:16px;--bw-spacing-large:24px;--bw-font-family:\"Inter\",system-ui,sans-serif;--bw-font-size:14px;--bw-font-size-large:18px;--bw-font-size-small:12px;--bw-shadow-md:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -1px rgba(0,0,0,.06);--bw-shadow-lg:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -2px rgba(0,0,0,.05);--bw-highlight-muted:rgba(0,177,170,.1);--bw-highlight-subtle:rgba(0,177,170,.05);--bw-text-subtle:rgba(14,116,144,.4)}@keyframes spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}@keyframes shimmer{0%{transform:translateX(-100%)}to{transform:translateX(100%)}}@keyframes fade-in{0%{opacity:0}to{opacity:1}}@keyframes fade-out{0%{opacity:1}to{opacity:0}}@keyframes slide-in-right{0%{opacity:0;transform:translateX(100%)}to{opacity:1;transform:translateX(0)}}@keyframes slide-out-right{0%{opacity:1;transform:translateX(0)}to{opacity:0;transform:translateX(100%)}}@keyframes slide-in-up{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}@keyframes scale-in{0%{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}@keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}.animate-spin{animation:spin 1s linear infinite}.animate-shimmer{animation:shimmer 2s infinite}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.animate-fade-in{animation:fade-in .2s ease-out}.animate-slide-in-up{animation:slide-in-up .3s ease-out}.animate-scale-in{animation:scale-in .2s ease-out}.skeleton-shimmer{overflow:hidden;position:relative}.skeleton-shimmer:after{animation:shimmer 1.5s infinite;background:linear-gradient(90deg,transparent,hsla(0,0%,100%,.3),transparent);content:\"\";height:100%;left:0;position:absolute;top:0;width:100%}.bw-btn{transition:all .2s ease!important}.bw-btn:hover:not(:disabled):not([disabled]){box-shadow:0 4px 12px rgba(0,0,0,.15);transform:translateY(-1px)}.bw-btn:active:not(:disabled):not([disabled]){box-shadow:0 2px 4px rgba(0,0,0,.1)}.bw-btn-primary:hover:not(:disabled):not([disabled]){background-color:var(--bw-highlight-color);filter:brightness(1.1)}.bw-btn-secondary:hover:not(:disabled):not([disabled]){background-color:var(--bw-surface-color);border-color:var(--bw-highlight-color);color:var(--bw-highlight-color)}.bw-btn-ghost:hover:not(:disabled):not([disabled]){background-color:var(--bw-highlight-muted)}.bw-btn-outline:hover:not(:disabled):not([disabled]){background-color:var(--bw-highlight-color);color:var(--bw-button-text-color,#fff)}.bw-btn:disabled,.bw-btn[disabled]{cursor:not-allowed!important;opacity:.5!important}button[class*=bw-btn],button[style*=transition]{transition:all .2s ease!important}button[data-variant=primary]:hover:not(:disabled),button[style*=\"--bw-highlight-color\"]:hover:not(:disabled){box-shadow:0 4px 12px rgba(0,0,0,.15);filter:brightness(1.1);transform:translateY(-1px)}button[data-variant=secondary]:hover:not(:disabled){border-color:var(--bw-highlight-color)!important;color:var(--bw-highlight-color)!important}button[data-variant=ghost]:hover:not(:disabled){background-color:var(--bw-highlight-muted)!important}button[data-variant=outline]:hover:not(:disabled){background-color:var(--bw-highlight-color)!important;color:var(--bw-button-text-color,#fff)!important}.bw-button-hover:hover:not(:disabled){box-shadow:0 4px 12px rgba(0,0,0,.15);transform:translateY(-1px)}.bw-button-hover:active:not(:disabled){box-shadow:0 2px 4px rgba(0,0,0,.1);transform:translateY(0)}@media (max-width:768px){.sidebar-mobile{border-radius:0!important;max-width:100%!important;width:100%!important}}@media (max-width:600px){.event-type-list{gap:12px!important;padding:8px!important}.event-type-card{flex:1 1 100%!important;max-width:100%!important;padding:0!important}.event-type-img{height:160px!important}.event-type-title{font-size:1.1rem!important}.event-type-desc{font-size:.8rem!important;max-height:100px!important;min-height:100px!important}.event-type-content{padding:16px 24px!important}}.event-type-markdown{overflow:visible!important}.event-type-markdown p{color:var(--bw-text-muted);font-family:var(--bw-font-family);line-height:1.6;margin:0 0 8px}.event-type-markdown p:last-child{margin-bottom:0}.event-type-markdown h2{font-size:18px!important;font-weight:700!important;margin:12px 0 6px!important}.event-type-markdown h2,.event-type-markdown h3{color:var(--bw-text-color)!important;line-height:1.3!important}.event-type-markdown h3{font-size:16px!important;font-weight:600!important;margin:10px 0 4px!important}.event-type-markdown strong{color:var(--bw-text-color);font-weight:600}.event-type-markdown em{font-style:italic}.event-type-markdown u{text-decoration:underline}.event-type-markdown ul{list-style:none!important;margin:6px 0!important;padding:0 0 0 24px!important;position:relative!important}.event-type-markdown ul li{color:var(--bw-text-muted)!important;font-family:var(--bw-font-family)!important;margin-bottom:2px!important;padding-left:0!important;position:relative!important}.event-type-markdown ul li:before{color:var(--bw-text-color)!important;content:\"•\"!important;font-weight:700!important;left:-16px!important;position:absolute!important;top:0!important}.event-type-markdown ol{counter-reset:list-counter!important;list-style:none!important;margin:6px 0!important;padding:0 0 0 24px!important;position:relative!important}.event-type-markdown ol li{color:var(--bw-text-muted)!important;counter-increment:list-counter!important;font-family:var(--bw-font-family)!important;margin-bottom:2px!important;padding-left:0!important;position:relative!important}.event-type-markdown ol li:before{color:var(--bw-text-color)!important;content:counter(list-counter) \".\"!important;font-weight:700!important;left:-20px!important;position:absolute!important;top:0!important}.event-type-markdown blockquote{border-left:2px solid var(--bw-border-color);color:var(--bw-text-muted);font-style:italic;margin:4px 0;padding-left:12px}.event-type-markdown a{color:var(--bw-highlight-color);text-decoration:underline}.markdown-content h1,.markdown-content h2,.markdown-content h3,.markdown-content h4,.markdown-content h5,.markdown-content h6{color:var(--bw-text-color);font-weight:600;margin-bottom:.5em}.markdown-content h1{font-size:1.5em}.markdown-content h2{font-size:1.25em}.markdown-content h3{font-size:1.1em}.markdown-content p{line-height:1.6;margin-bottom:1em}.markdown-content ol,.markdown-content ul{margin-bottom:1em;padding-left:1.5em}.markdown-content ul{list-style-type:disc}.markdown-content ol{list-style-type:decimal}.markdown-content li{margin-bottom:.25em}.markdown-content a{color:var(--bw-highlight-color);text-decoration:underline}.markdown-content a:hover{opacity:.8}.markdown-content strong{font-weight:600}.markdown-content em{font-style:italic}.markdown-content code{background:var(--bw-highlight-subtle);border-radius:4px;font-family:monospace;font-size:.9em;padding:.125em .25em}.markdown-content blockquote{border-left:3px solid var(--bw-highlight-color);color:var(--bw-text-muted);margin:1em 0;padding-left:1em}.print-only{display:none}.print-hidden{display:block}@media print{.print-only{display:block}.print-hidden{display:none!important}.print-booking-header{border-bottom:2px solid #000;display:block;margin-bottom:24px;padding-bottom:16px;text-align:center}.print-booking-header h1{font-size:24px;margin:0 0 8px}.print-booking-header .subtitle{color:#666;font-size:14px}.print-booking-card{border:1px solid #ccc;border-radius:8px;margin-bottom:16px;padding:16px;page-break-inside:avoid}.print-section-title{border-bottom:1px solid #ddd;display:block;font-size:16px;font-weight:600;margin-bottom:12px;padding-bottom:8px}.print-detail-grid{display:grid;gap:12px;grid-template-columns:1fr 1fr}.print-detail-item{margin-bottom:8px}.print-detail-label{color:#666;font-size:12px;margin-bottom:4px}.print-detail-value{font-size:14px;font-weight:600}.print-status-badge{border-radius:9999px;display:inline-block;font-size:12px;font-weight:600;padding:4px 12px}.print-status-paid{background-color:#dcfce7;color:#166534;display:inline-block}.print-participant{align-items:center;background-color:#f9fafb;border-radius:4px;display:flex;justify-content:space-between;margin-bottom:8px;padding:8px}.print-participant-name{font-weight:600}.print-participant-age{color:#666;font-size:12px}.print-payment-summary{display:block}.print-payment-row{border-bottom:1px solid #eee;display:flex;justify-content:space-between;padding:4px 0}.print-payment-row:last-child{border-bottom:none;font-weight:600}.print-footer{border-top:1px solid #ddd;color:#666;display:block;font-size:12px;margin-top:24px;padding-top:16px;text-align:center}.print-footer p{margin:4px 0}}";
17270
+ var css_248z = ".booking-widget-container{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;box-sizing:border-box;color:var(--bw-text-color,#1e293b);direction:ltr;display:block;font-family:var(--bw-font-family,system-ui,-apple-system,sans-serif);font-size:var(--bw-font-size,14px);isolation:isolate;line-height:1.5;position:relative;text-align:left}.booking-widget-container *,.booking-widget-container :after,.booking-widget-container :before{box-sizing:border-box;margin:0;padding:0}.booking-widget-container input,.booking-widget-container select,.booking-widget-container textarea{font-family:inherit;font-size:inherit;line-height:inherit}.booking-widget-container button{background:none;border:none;cursor:pointer;font-family:inherit;font-size:inherit}.booking-widget-container a{color:inherit;text-decoration:none}.booking-widget-container img{display:block;height:auto;max-width:100%;vertical-align:middle}.booking-widget-container ol,.booking-widget-container ul{list-style:none}.booking-widget-container h1,.booking-widget-container h2,.booking-widget-container h3,.booking-widget-container h4,.booking-widget-container h5,.booking-widget-container h6{font-size:inherit;font-weight:inherit}#booking-widget-portal{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;color:var(--bw-text-color,#1e293b);direction:ltr;font-family:var(--bw-font-family,system-ui,-apple-system,sans-serif);font-size:var(--bw-font-size,14px);isolation:isolate;line-height:1.5;text-align:left}#booking-widget-portal *,#booking-widget-portal :after,#booking-widget-portal :before{box-sizing:border-box}#booking-widget-portal-root{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;color:var(--bw-text-color,#1e293b);font-family:var(--bw-font-family,system-ui,-apple-system,sans-serif);font-size:var(--bw-font-size,14px);line-height:1.5}:root{--bw-highlight-color:#00b1aa;--bw-highlight-color-rgb:0,177,170;--bw-background-color:#f8fdfe;--bw-surface-color:#fff;--bw-text-color:#0e7490;--bw-text-muted:rgba(14,116,144,.7);--bw-border-color:#bae6fd;--bw-success-color:#38bdf8;--bw-warning-color:#fbbf24;--bw-error-color:#f43f5e;--bw-border-radius:18px;--bw-border-radius-small:calc(var(--bw-border-radius)*0.8);--bw-spacing:16px;--bw-spacing-large:24px;--bw-font-family:\"Inter\",system-ui,sans-serif;--bw-font-size:14px;--bw-font-size-large:18px;--bw-font-size-small:12px;--bw-shadow-md:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -1px rgba(0,0,0,.06);--bw-shadow-lg:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -2px rgba(0,0,0,.05);--bw-highlight-muted:rgba(0,177,170,.1);--bw-highlight-subtle:rgba(0,177,170,.05);--bw-text-subtle:rgba(14,116,144,.4)}@keyframes spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}@keyframes shimmer{0%{transform:translateX(-100%)}to{transform:translateX(100%)}}@keyframes fade-in{0%{opacity:0}to{opacity:1}}@keyframes fade-out{0%{opacity:1}to{opacity:0}}@keyframes slide-in-right{0%{opacity:0;transform:translateX(100%)}to{opacity:1;transform:translateX(0)}}@keyframes slide-out-right{0%{opacity:1;transform:translateX(0)}to{opacity:0;transform:translateX(100%)}}@keyframes slide-in-up{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}@keyframes scale-in{0%{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}@keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}.animate-spin{animation:spin 1s linear infinite}.animate-shimmer{animation:shimmer 2s infinite}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.animate-fade-in{animation:fade-in .2s ease-out}.animate-slide-in-up{animation:slide-in-up .3s ease-out}.animate-scale-in{animation:scale-in .2s ease-out}.skeleton-shimmer{overflow:hidden;position:relative}.skeleton-shimmer:after{animation:shimmer 1.5s infinite;background:linear-gradient(90deg,transparent,hsla(0,0%,100%,.3),transparent);content:\"\";height:100%;left:0;position:absolute;top:0;width:100%}.bw-btn{letter-spacing:var(--bw-letter-spacing,normal);text-transform:var(--bw-text-transform,none);transition:all .2s ease!important}.bw-btn:hover:not(:disabled):not([disabled]){box-shadow:0 4px 12px rgba(0,0,0,.15);transform:translateY(-1px)}.bw-btn:active:not(:disabled):not([disabled]){box-shadow:0 2px 4px rgba(0,0,0,.1)}.bw-btn-primary:hover:not(:disabled):not([disabled]){background-color:var(--bw-highlight-color);filter:brightness(1.1)}.bw-btn-secondary:hover:not(:disabled):not([disabled]){background-color:var(--bw-surface-color);border-color:var(--bw-highlight-color);color:var(--bw-highlight-color)}.bw-btn-ghost:hover:not(:disabled):not([disabled]){background-color:var(--bw-highlight-muted)}.bw-btn-outline:hover:not(:disabled):not([disabled]){background-color:var(--bw-highlight-color);color:var(--bw-button-text-color,#fff)}.bw-btn:disabled,.bw-btn[disabled]{cursor:not-allowed!important;opacity:.5!important}button[class*=bw-btn],button[style*=transition]{transition:all .2s ease!important}button[data-variant=primary]:hover:not(:disabled),button[style*=\"--bw-highlight-color\"]:hover:not(:disabled){box-shadow:0 4px 12px rgba(0,0,0,.15);filter:brightness(1.1);transform:translateY(-1px)}button[data-variant=secondary]:hover:not(:disabled){border-color:var(--bw-highlight-color)!important;color:var(--bw-highlight-color)!important}button[data-variant=ghost]:hover:not(:disabled){background-color:var(--bw-highlight-muted)!important}button[data-variant=outline]:hover:not(:disabled){background-color:var(--bw-highlight-color)!important;color:var(--bw-button-text-color,#fff)!important}.bw-button-hover:hover:not(:disabled){box-shadow:0 4px 12px rgba(0,0,0,.15);transform:translateY(-1px)}.bw-button-hover:active:not(:disabled){box-shadow:0 2px 4px rgba(0,0,0,.1);transform:translateY(0)}@media (max-width:768px){.sidebar-mobile{border-radius:0!important;max-width:100%!important;width:100%!important}}@media (max-width:600px){.event-type-list{gap:12px!important;padding:8px!important}.event-type-card{flex:1 1 100%!important;max-width:100%!important;padding:0!important}.event-type-img{height:160px!important}.event-type-title{font-size:1.1rem!important}.event-type-desc{font-size:.8rem!important;max-height:100px!important;min-height:100px!important}.event-type-content{padding:16px 24px!important}}.event-type-markdown{overflow:visible!important}.event-type-markdown p{color:var(--bw-text-muted);font-family:var(--bw-font-family);line-height:1.6;margin:0 0 8px}.event-type-markdown p:last-child{margin-bottom:0}.event-type-markdown h2{font-size:18px!important;font-weight:700!important;margin:12px 0 6px!important}.event-type-markdown h2,.event-type-markdown h3{color:var(--bw-text-color)!important;line-height:1.3!important}.event-type-markdown h3{font-size:16px!important;font-weight:600!important;margin:10px 0 4px!important}.event-type-markdown strong{color:var(--bw-text-color);font-weight:600}.event-type-markdown em{font-style:italic}.event-type-markdown u{text-decoration:underline}.event-type-markdown ul{list-style:none!important;margin:6px 0!important;padding:0 0 0 24px!important;position:relative!important}.event-type-markdown ul li{color:var(--bw-text-muted)!important;font-family:var(--bw-font-family)!important;margin-bottom:2px!important;padding-left:0!important;position:relative!important}.event-type-markdown ul li:before{color:var(--bw-text-color)!important;content:\"•\"!important;font-weight:700!important;left:-16px!important;position:absolute!important;top:0!important}.event-type-markdown ol{counter-reset:list-counter!important;list-style:none!important;margin:6px 0!important;padding:0 0 0 24px!important;position:relative!important}.event-type-markdown ol li{color:var(--bw-text-muted)!important;counter-increment:list-counter!important;font-family:var(--bw-font-family)!important;margin-bottom:2px!important;padding-left:0!important;position:relative!important}.event-type-markdown ol li:before{color:var(--bw-text-color)!important;content:counter(list-counter) \".\"!important;font-weight:700!important;left:-20px!important;position:absolute!important;top:0!important}.event-type-markdown blockquote{border-left:2px solid var(--bw-border-color);color:var(--bw-text-muted);font-style:italic;margin:4px 0;padding-left:12px}.event-type-markdown a{color:var(--bw-highlight-color);text-decoration:underline}.markdown-content h1,.markdown-content h2,.markdown-content h3,.markdown-content h4,.markdown-content h5,.markdown-content h6{color:var(--bw-text-color);font-weight:600;margin-bottom:.5em}.markdown-content h1{font-size:1.5em}.markdown-content h2{font-size:1.25em}.markdown-content h3{font-size:1.1em}.markdown-content p{line-height:1.6;margin-bottom:1em}.markdown-content ol,.markdown-content ul{margin-bottom:1em;padding-left:1.5em}.markdown-content ul{list-style-type:disc}.markdown-content ol{list-style-type:decimal}.markdown-content li{margin-bottom:.25em}.markdown-content a{color:var(--bw-highlight-color);text-decoration:underline}.markdown-content a:hover{opacity:.8}.markdown-content strong{font-weight:600}.markdown-content em{font-style:italic}.markdown-content code{background:var(--bw-highlight-subtle);border-radius:4px;font-family:monospace;font-size:.9em;padding:.125em .25em}.markdown-content blockquote{border-left:3px solid var(--bw-highlight-color);color:var(--bw-text-muted);margin:1em 0;padding-left:1em}.print-only{display:none}.print-hidden{display:block}@media print{.print-only{display:block}.print-hidden{display:none!important}.print-booking-header{border-bottom:2px solid #000;display:block;margin-bottom:24px;padding-bottom:16px;text-align:center}.print-booking-header h1{font-size:24px;margin:0 0 8px}.print-booking-header .subtitle{color:#666;font-size:14px}.print-booking-card{border:1px solid #ccc;border-radius:8px;margin-bottom:16px;padding:16px;page-break-inside:avoid}.print-section-title{border-bottom:1px solid #ddd;display:block;font-size:16px;font-weight:600;margin-bottom:12px;padding-bottom:8px}.print-detail-grid{display:grid;gap:12px;grid-template-columns:1fr 1fr}.print-detail-item{margin-bottom:8px}.print-detail-label{color:#666;font-size:12px;margin-bottom:4px}.print-detail-value{font-size:14px;font-weight:600}.print-status-badge{border-radius:9999px;display:inline-block;font-size:12px;font-weight:600;padding:4px 12px}.print-status-paid{background-color:#dcfce7;color:#166534;display:inline-block}.print-participant{align-items:center;background-color:#f9fafb;border-radius:4px;display:flex;justify-content:space-between;margin-bottom:8px;padding:8px}.print-participant-name{font-weight:600}.print-participant-age{color:#666;font-size:12px}.print-payment-summary{display:block}.print-payment-row{border-bottom:1px solid #eee;display:flex;justify-content:space-between;padding:4px 0}.print-payment-row:last-child{border-bottom:none;font-weight:600}.print-footer{border-top:1px solid #ddd;color:#666;display:block;font-size:12px;margin-top:24px;padding-top:16px;text-align:center}.print-footer p{margin:4px 0}}";
17031
17271
  styleInject(css_248z);
17032
17272
 
17033
17273
  // Export init function for vanilla JS usage