@bigz-app/booking-widget 1.2.1 → 1.3.1

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 (35) hide show
  1. package/dist/booking-widget.js +707 -180
  2. package/dist/booking-widget.js.map +1 -1
  3. package/dist/components/UniversalBookingWidget.d.ts +52 -2
  4. package/dist/components/UniversalBookingWidget.d.ts.map +1 -1
  5. package/dist/components/booking/BookingForm.d.ts +3 -0
  6. package/dist/components/booking/BookingForm.d.ts.map +1 -1
  7. package/dist/components/booking/BookingSuccessModal.d.ts.map +1 -1
  8. package/dist/components/events/EventTypeSelection.d.ts +14 -1
  9. package/dist/components/events/EventTypeSelection.d.ts.map +1 -1
  10. package/dist/components/events/SpecialsView.d.ts +13 -0
  11. package/dist/components/events/SpecialsView.d.ts.map +1 -0
  12. package/dist/components/events/index.d.ts +1 -0
  13. package/dist/components/events/index.d.ts.map +1 -1
  14. package/dist/components/shared/Button.d.ts.map +1 -1
  15. package/dist/components/shared/DialogPortal.d.ts.map +1 -1
  16. package/dist/components/upsells/UpsellCard.d.ts +2 -0
  17. package/dist/components/upsells/UpsellCard.d.ts.map +1 -1
  18. package/dist/components/upsells/UpsellsStep.d.ts +2 -0
  19. package/dist/components/upsells/UpsellsStep.d.ts.map +1 -1
  20. package/dist/i18n/i18n-context.d.ts.map +1 -1
  21. package/dist/i18n/locales/de.d.ts.map +1 -1
  22. package/dist/i18n/locales/en.d.ts.map +1 -1
  23. package/dist/i18n/locales/es.d.ts.map +1 -1
  24. package/dist/i18n/locales/pt.d.ts.map +1 -1
  25. package/dist/i18n/locales/sv.d.ts.map +1 -1
  26. package/dist/index.cjs +707 -180
  27. package/dist/index.cjs.map +1 -1
  28. package/dist/index.esm.js +708 -181
  29. package/dist/index.esm.js.map +1 -1
  30. package/dist/styles/StyleProvider.d.ts.map +1 -1
  31. package/dist/styles/shared-styles.d.ts +1 -0
  32. package/dist/styles/shared-styles.d.ts.map +1 -1
  33. package/dist/validation/booking-schema.d.ts +54 -13
  34. package/dist/validation/booking-schema.d.ts.map +1 -1
  35. package/package.json +1 -1
@@ -291,6 +291,7 @@
291
291
  "events.soldOut": "Ausgebucht",
292
292
  "events.availableFrom": "Freie Plätze ab {{date}}",
293
293
  "events.noAvailableDates": "Keine Termine frei",
294
+ "events.previewSectionTitle": "Specials & nächste Termine",
294
295
  // Event instances
295
296
  "instances.title": "Terminauswahl",
296
297
  "instances.noAvailable": "Keine verfügbaren Termine",
@@ -307,6 +308,15 @@
307
308
  "nextEvents.noUpcomingMessage": "Aktuell sind keine Termine verfügbar. Bitte schaue später noch einmal vorbei oder kontaktiere uns direkt.",
308
309
  "nextEvents.showAll": "Alle Events anzeigen",
309
310
  "nextEvents.priceOnRequest": "Preis auf Anfrage",
311
+ // Specials view
312
+ "specials.title": "Sonderangebote",
313
+ "specials.subtitle": "Unsere aktuellen Angebote auf einen Blick",
314
+ "specials.noSpecials": "Keine Sonderangebote",
315
+ "specials.noSpecialsMessage": "Derzeit sind keine Sonderangebote verfügbar. Bitte schaue später noch einmal vorbei.",
316
+ "specials.save": "Spare {{amount}}",
317
+ "specials.savePercent": "{{percent}}% Rabatt",
318
+ "specials.spotsLeft": "Noch {{count}} Plätze frei",
319
+ "specials.bookNow": "Jetzt buchen",
310
320
  // Booking form
311
321
  "booking.title": "Buchung - {{name}}",
312
322
  "booking.notPossible": "Buchung nicht möglich",
@@ -328,6 +338,8 @@
328
338
  "booking.participantName": "Name *",
329
339
  "booking.participantNamePlaceholder": "Teilnehmername",
330
340
  "booking.participantAge": "Alter",
341
+ "booking.participantLevel": "Level",
342
+ "booking.participantLevelPlaceholder": "Level wählen...",
331
343
  "booking.addParticipant": "{{number}}. Teilnehmer hinzufügen",
332
344
  "booking.maxParticipants": "Maximale Anzahl an Teilnehmern erreicht. Es sind nur noch {{count}} Plätze verfügbar.",
333
345
  "booking.maxSpotsReached": "Maximal {{count}} Plätze verfügbar.",
@@ -476,7 +488,11 @@
476
488
  "validation.emailInvalid": "Ungültiges E-Mail-Format",
477
489
  "validation.emailDomainInvalid": "Ungültige E-Mail-Domain",
478
490
  "validation.participantRequired": "Mindestens ein Teilnehmer erforderlich",
491
+ "validation.ageRequired": "Alter ist erforderlich",
492
+ "validation.levelRequired": "Bitte ein Level auswählen",
479
493
  "validation.acceptTerms": "Bitte akzeptiere die Allgemeinen Geschäftsbedingungen",
494
+ "level.beginner": "Anfänger",
495
+ "level.advanced": "Fortgeschritten",
480
496
  // Sidebar
481
497
  "sidebar.close": "Schließen",
482
498
  // Promo
@@ -551,6 +567,7 @@
551
567
  "events.soldOut": "Sold out",
552
568
  "events.availableFrom": "Available from {{date}}",
553
569
  "events.noAvailableDates": "No dates available",
570
+ "events.previewSectionTitle": "Specials & upcoming dates",
554
571
  // Event instances
555
572
  "instances.title": "Select a date",
556
573
  "instances.noAvailable": "No available dates",
@@ -567,6 +584,15 @@
567
584
  "nextEvents.noUpcomingMessage": "There are currently no dates available. Please check back later or contact us directly.",
568
585
  "nextEvents.showAll": "Show all events",
569
586
  "nextEvents.priceOnRequest": "Price on request",
587
+ // Specials view
588
+ "specials.title": "Special Offers",
589
+ "specials.subtitle": "Our current deals at a glance",
590
+ "specials.noSpecials": "No special offers",
591
+ "specials.noSpecialsMessage": "There are currently no special offers available. Please check back later.",
592
+ "specials.save": "Save {{amount}}",
593
+ "specials.savePercent": "{{percent}}% off",
594
+ "specials.spotsLeft": "{{count}} spots left",
595
+ "specials.bookNow": "Book now",
570
596
  // Booking form
571
597
  "booking.title": "Booking - {{name}}",
572
598
  "booking.notPossible": "Booking not possible",
@@ -588,6 +614,8 @@
588
614
  "booking.participantName": "Name *",
589
615
  "booking.participantNamePlaceholder": "Participant name",
590
616
  "booking.participantAge": "Age",
617
+ "booking.participantLevel": "Level",
618
+ "booking.participantLevelPlaceholder": "Select level...",
591
619
  "booking.addParticipant": "Add participant {{number}}",
592
620
  "booking.maxParticipants": "Maximum number of participants reached. Only {{count}} spots are available.",
593
621
  "booking.maxSpotsReached": "Maximum {{count}} spots available.",
@@ -736,7 +764,11 @@
736
764
  "validation.emailInvalid": "Invalid email format",
737
765
  "validation.emailDomainInvalid": "Invalid email domain",
738
766
  "validation.participantRequired": "At least one participant is required",
767
+ "validation.ageRequired": "Age is required",
768
+ "validation.levelRequired": "Please select a level",
739
769
  "validation.acceptTerms": "Please accept the terms and conditions",
770
+ "level.beginner": "Beginner",
771
+ "level.advanced": "Advanced",
740
772
  // Sidebar
741
773
  "sidebar.close": "Close",
742
774
  // Promo
@@ -811,6 +843,7 @@
811
843
  "events.soldOut": "Agotado",
812
844
  "events.availableFrom": "Disponible desde {{date}}",
813
845
  "events.noAvailableDates": "Sin fechas disponibles",
846
+ "events.previewSectionTitle": "Especiales & próximas fechas",
814
847
  // Event instances
815
848
  "instances.title": "Seleccionar fecha",
816
849
  "instances.noAvailable": "Sin fechas disponibles",
@@ -827,6 +860,15 @@
827
860
  "nextEvents.noUpcomingMessage": "Actualmente no hay fechas disponibles. Por favor, vuelve más tarde o contáctanos directamente.",
828
861
  "nextEvents.showAll": "Mostrar todos los eventos",
829
862
  "nextEvents.priceOnRequest": "Precio bajo consulta",
863
+ // Specials view
864
+ "specials.title": "Ofertas especiales",
865
+ "specials.subtitle": "Nuestras ofertas actuales de un vistazo",
866
+ "specials.noSpecials": "Sin ofertas especiales",
867
+ "specials.noSpecialsMessage": "Actualmente no hay ofertas especiales disponibles. Vuelve más tarde.",
868
+ "specials.save": "Ahorra {{amount}}",
869
+ "specials.savePercent": "{{percent}}% de descuento",
870
+ "specials.spotsLeft": "{{count}} plazas disponibles",
871
+ "specials.bookNow": "Reservar ahora",
830
872
  // Booking form
831
873
  "booking.title": "Reserva - {{name}}",
832
874
  "booking.notPossible": "Reserva no posible",
@@ -848,6 +890,8 @@
848
890
  "booking.participantName": "Nombre *",
849
891
  "booking.participantNamePlaceholder": "Nombre del participante",
850
892
  "booking.participantAge": "Edad",
893
+ "booking.participantLevel": "Nivel",
894
+ "booking.participantLevelPlaceholder": "Seleccionar nivel...",
851
895
  "booking.addParticipant": "Añadir participante {{number}}",
852
896
  "booking.maxParticipants": "Número máximo de participantes alcanzado. Solo quedan {{count}} plazas disponibles.",
853
897
  "booking.maxSpotsReached": "Máximo {{count}} plazas disponibles.",
@@ -996,7 +1040,11 @@
996
1040
  "validation.emailInvalid": "Formato de correo electrónico inválido",
997
1041
  "validation.emailDomainInvalid": "Dominio de correo electrónico inválido",
998
1042
  "validation.participantRequired": "Se requiere al menos un participante",
1043
+ "validation.ageRequired": "La edad es obligatoria",
1044
+ "validation.levelRequired": "Selecciona un nivel",
999
1045
  "validation.acceptTerms": "Por favor, acepta los términos y condiciones",
1046
+ "level.beginner": "Principiante",
1047
+ "level.advanced": "Avanzado",
1000
1048
  // Sidebar
1001
1049
  "sidebar.close": "Cerrar",
1002
1050
  // Promo
@@ -1071,6 +1119,7 @@
1071
1119
  "events.soldOut": "Esgotado",
1072
1120
  "events.availableFrom": "Disponível a partir de {{date}}",
1073
1121
  "events.noAvailableDates": "Sem datas disponíveis",
1122
+ "events.previewSectionTitle": "Especiais & próximas datas",
1074
1123
  // Event instances
1075
1124
  "instances.title": "Selecionar data",
1076
1125
  "instances.noAvailable": "Sem datas disponíveis",
@@ -1087,6 +1136,15 @@
1087
1136
  "nextEvents.noUpcomingMessage": "Atualmente não há datas disponíveis. Por favor, volte mais tarde ou contacte-nos diretamente.",
1088
1137
  "nextEvents.showAll": "Mostrar todos os eventos",
1089
1138
  "nextEvents.priceOnRequest": "Preço sob consulta",
1139
+ // Specials view
1140
+ "specials.title": "Ofertas especiais",
1141
+ "specials.subtitle": "As nossas ofertas atuais num relance",
1142
+ "specials.noSpecials": "Sem ofertas especiais",
1143
+ "specials.noSpecialsMessage": "Atualmente não há ofertas especiais disponíveis. Por favor, volte mais tarde.",
1144
+ "specials.save": "Poupe {{amount}}",
1145
+ "specials.savePercent": "{{percent}}% de desconto",
1146
+ "specials.spotsLeft": "{{count}} lugares disponíveis",
1147
+ "specials.bookNow": "Reservar agora",
1090
1148
  // Booking form
1091
1149
  "booking.title": "Reserva - {{name}}",
1092
1150
  "booking.notPossible": "Reserva não possível",
@@ -1108,6 +1166,8 @@
1108
1166
  "booking.participantName": "Nome *",
1109
1167
  "booking.participantNamePlaceholder": "Nome do participante",
1110
1168
  "booking.participantAge": "Idade",
1169
+ "booking.participantLevel": "Nível",
1170
+ "booking.participantLevelPlaceholder": "Selecionar nível...",
1111
1171
  "booking.addParticipant": "Adicionar participante {{number}}",
1112
1172
  "booking.maxParticipants": "Número máximo de participantes atingido. Apenas {{count}} lugares disponíveis.",
1113
1173
  "booking.maxSpotsReached": "Máximo {{count}} lugares disponíveis.",
@@ -1256,7 +1316,11 @@
1256
1316
  "validation.emailInvalid": "Formato de email inválido",
1257
1317
  "validation.emailDomainInvalid": "Domínio de email inválido",
1258
1318
  "validation.participantRequired": "É necessário pelo menos um participante",
1319
+ "validation.ageRequired": "A idade é obrigatória",
1320
+ "validation.levelRequired": "Por favor selecione um nível",
1259
1321
  "validation.acceptTerms": "Por favor, aceite os termos e condições",
1322
+ "level.beginner": "Iniciante",
1323
+ "level.advanced": "Avançado",
1260
1324
  // Sidebar
1261
1325
  "sidebar.close": "Fechar",
1262
1326
  // Promo
@@ -1331,6 +1395,7 @@
1331
1395
  "events.soldOut": "Fullbokat",
1332
1396
  "events.availableFrom": "Lediga platser från {{date}}",
1333
1397
  "events.noAvailableDates": "Inga datum lediga",
1398
+ "events.previewSectionTitle": "Specials & kommande datum",
1334
1399
  // Event instances
1335
1400
  "instances.title": "Välj datum",
1336
1401
  "instances.noAvailable": "Inga tillgängliga datum",
@@ -1347,6 +1412,15 @@
1347
1412
  "nextEvents.noUpcomingMessage": "Det finns för närvarande inga datum tillgängliga. Kom tillbaka senare eller kontakta oss direkt.",
1348
1413
  "nextEvents.showAll": "Visa alla evenemang",
1349
1414
  "nextEvents.priceOnRequest": "Pris på förfrågan",
1415
+ // Specials view
1416
+ "specials.title": "Specialerbjudanden",
1417
+ "specials.subtitle": "Våra aktuella erbjudanden i korthet",
1418
+ "specials.noSpecials": "Inga specialerbjudanden",
1419
+ "specials.noSpecialsMessage": "Det finns för närvarande inga specialerbjudanden tillgängliga. Kom tillbaka senare.",
1420
+ "specials.save": "Spara {{amount}}",
1421
+ "specials.savePercent": "{{percent}}% rabatt",
1422
+ "specials.spotsLeft": "{{count}} platser kvar",
1423
+ "specials.bookNow": "Boka nu",
1350
1424
  // Booking form
1351
1425
  "booking.title": "Bokning - {{name}}",
1352
1426
  "booking.notPossible": "Bokning inte möjlig",
@@ -1368,6 +1442,8 @@
1368
1442
  "booking.participantName": "Namn *",
1369
1443
  "booking.participantNamePlaceholder": "Deltagarens namn",
1370
1444
  "booking.participantAge": "Ålder",
1445
+ "booking.participantLevel": "Nivå",
1446
+ "booking.participantLevelPlaceholder": "Välj nivå...",
1371
1447
  "booking.addParticipant": "Lägg till deltagare {{number}}",
1372
1448
  "booking.maxParticipants": "Maximalt antal deltagare uppnått. Bara {{count}} platser är tillgängliga.",
1373
1449
  "booking.maxSpotsReached": "Maximalt {{count}} platser tillgängliga.",
@@ -1516,7 +1592,11 @@
1516
1592
  "validation.emailInvalid": "Ogiltigt e-postformat",
1517
1593
  "validation.emailDomainInvalid": "Ogiltig e-postdomän",
1518
1594
  "validation.participantRequired": "Minst en deltagare krävs",
1595
+ "validation.ageRequired": "Ålder krävs",
1596
+ "validation.levelRequired": "Välj en nivå",
1519
1597
  "validation.acceptTerms": "Acceptera villkoren",
1598
+ "level.beginner": "Nybörjare",
1599
+ "level.advanced": "Avancerad",
1520
1600
  // Sidebar
1521
1601
  "sidebar.close": "Stäng",
1522
1602
  // Promo
@@ -1609,18 +1689,9 @@
1609
1689
  }
1610
1690
  const I18nContext = R$2(null);
1611
1691
  function I18nProvider({ configLocale, children }) {
1612
- // Priority: configLocale (site owner) > persisted user choice > browser language > "de"
1613
- // If configLocale is set, the site owner has locked the language - don't restore user choice.
1614
- const [overrideLocale, setOverrideLocale] = d$1(() => {
1615
- if (configLocale)
1616
- return null;
1617
- return readPersistedLocale();
1618
- });
1619
- y$1(() => {
1620
- if (configLocale) {
1621
- setOverrideLocale(null);
1622
- }
1623
- }, [configLocale]);
1692
+ // Priority: persisted user choice > configLocale (org default) > browser language > "de"
1693
+ // This keeps org locale as default, but remembers explicit user overrides across reloads.
1694
+ const [overrideLocale, setOverrideLocale] = d$1(() => readPersistedLocale());
1624
1695
  const locale = overrideLocale ?? resolveLocale(configLocale);
1625
1696
  const handleSetLocale = q$2((next) => {
1626
1697
  persistLocale(next);
@@ -1752,129 +1823,153 @@
1752
1823
  // If semantic resolution fails, use fallback or return the original value
1753
1824
  return fallbackValue || colorValue;
1754
1825
  };
1755
- // Predefined themes (modern, accessibility-tested)
1826
+ // Legacy theme name redirects (old name → new name)
1827
+ const legacyThemeRedirects = {
1828
+ "light-fresh": "teal-minimal",
1829
+ "light-elegant": "blue-business",
1830
+ "light-vibrant": "orange-raw",
1831
+ "light-professional": "blue-business",
1832
+ "dark-night": "navy-night",
1833
+ "dark-modern": "navy-night",
1834
+ "dark-forest": "green-deep",
1835
+ };
1836
+ // Predefined themes
1756
1837
  const themes = {
1757
1838
  // --- Light Themes ---
1758
- "light-fresh": {
1759
- highlight: "#00b1aa", // accent-strong
1760
- background: "#f8fdfe", // neutral-strong (background)
1761
- surface: "#ffffff", // card (pure white)
1762
- text: "#0e7490", // Turquoise 800
1763
- border: "#bae6fd", // Blue 200
1764
- success: "#38bdf8", // Blue 400
1765
- warning: "#fbbf24", // Amber 400
1766
- error: "#f43f5e", // Rose 500
1767
- borderRadius: "18px", // Very rounded corners
1839
+ "teal-minimal": {
1840
+ highlight: "#00b1aa",
1841
+ background: "#f8fdfe",
1842
+ surface: "#ffffff",
1843
+ text: "#0e7490",
1844
+ border: "#bae6fd",
1845
+ success: "#38bdf8",
1846
+ warning: "#fbbf24",
1847
+ error: "#f43f5e",
1848
+ borderRadius: "18px",
1768
1849
  fontFamily: "'Inter', system-ui, sans-serif",
1769
1850
  },
1770
- "light-elegant": {
1771
- highlight: "#8b5cf6", // Violet 500
1772
- background: "#f5f3ff", // Violet 50
1773
- surface: "#ffffff", // White
1774
- text: "#4c1d95", // Violet 900
1775
- border: "#ede9fe", // Violet 100
1776
- success: "#16a34a", // Green 600
1777
- warning: "#ca8a04", // Yellow 600
1778
- error: "#dc2626", // Red 600
1779
- borderRadius: "12px",
1780
- fontFamily: "'Playfair Display', serif",
1851
+ "blue-business": {
1852
+ highlight: "#2563eb",
1853
+ background: "#f8fafc",
1854
+ surface: "#ffffff",
1855
+ text: "#0f172a",
1856
+ border: "#cbd5e1",
1857
+ success: "#059669",
1858
+ warning: "#d97706",
1859
+ error: "#b91c1c",
1860
+ borderRadius: "6px",
1861
+ fontFamily: "'Plus Jakarta Sans', system-ui, sans-serif",
1781
1862
  },
1782
- "light-vibrant": {
1783
- highlight: "#ed702d", // blue-500 - bright blue accent
1784
- background: "#1f2630", // slate-900 - dark background
1785
- surface: "#1f2630", // slate-800 - dark cards
1786
- text: "#f1f5f9", // slate-100 - light text
1787
- border: "#ed702d", // slate-700 - subtle borders
1788
- success: "#22c55e", // green-500
1789
- warning: "#eab308", // yellow-500
1790
- error: "#ef4444", // red-500
1863
+ "orange-raw": {
1864
+ highlight: "#ed702d",
1865
+ background: "#1f2630",
1866
+ surface: "#1f2630",
1867
+ text: "#f1f5f9",
1868
+ border: "#ed702d",
1869
+ success: "#22c55e",
1870
+ warning: "#eab308",
1871
+ error: "#ef4444",
1791
1872
  borderRadius: "0px",
1792
1873
  fontFamily: "Inter, system-ui, sans-serif",
1793
1874
  },
1794
- "light-professional": {
1795
- highlight: "#2563eb", // Blue 600
1796
- background: "#f8fafc", // Slate 50
1797
- surface: "#ffffff", // White
1798
- text: "#1e293b", // Slate 800
1799
- border: "#e2e8f0", // Slate 200
1800
- success: "#059669", // Emerald 600
1801
- warning: "#d97706", // Amber 600
1802
- error: "#b91c1c", // Red 700
1803
- borderRadius: "4px",
1804
- fontFamily: "system-ui, -apple-system, sans-serif",
1875
+ // --- Dark Themes ---
1876
+ "navy-night": {
1877
+ highlight: "#60a5fa",
1878
+ background: "#0b1120",
1879
+ surface: "#111827",
1880
+ text: "#e2e8f0",
1881
+ border: "#1e3a5f",
1882
+ success: "#34d399",
1883
+ warning: "#fbbf24",
1884
+ error: "#f87171",
1885
+ borderRadius: "10px",
1886
+ fontFamily: "'Outfit', system-ui, sans-serif",
1805
1887
  },
1806
- "dark-night": {
1807
- highlight: "#3b82f6", // blue-500 - bright blue accent
1808
- background: "#0f172a", // slate-900 - dark background
1809
- surface: "#1e293b", // slate-800 - dark cards
1810
- text: "#f1f5f9", // slate-100 - light text
1811
- border: "#334155", // slate-700 - subtle borders
1812
- success: "#22c55e", // green-500
1813
- warning: "#eab308", // yellow-500
1814
- error: "#ef4444", // red-500
1815
- borderRadius: "8px",
1816
- fontFamily: "Inter, system-ui, sans-serif",
1817
- },
1818
- "dark-modern": {
1819
- highlight: "#3b82f6", // blue-500 - bright blue accent
1820
- background: "#0f172a", // slate-900 - dark background
1821
- surface: "#1e293b", // slate-800 - dark cards
1822
- text: "#f1f5f9", // slate-100 - light text
1823
- border: "#334155", // slate-700 - subtle borders
1824
- success: "#22c55e", // green-500
1825
- warning: "#eab308", // yellow-500
1826
- error: "#ef4444", // red-500
1827
- borderRadius: "8px",
1828
- fontFamily: "Inter, system-ui, sans-serif",
1829
- },
1830
- "dark-forest": {
1831
- highlight: "#34d399", // Emerald 400
1832
- background: "#05140d",
1833
- surface: "#062215",
1834
- text: "#d1fae5", // Emerald 100
1835
- border: "#043322",
1836
- success: "#4ade80", // Green 400
1837
- warning: "#facc15", // Yellow 400
1838
- error: "#f87171", // Red 400
1888
+ "green-deep": {
1889
+ highlight: "#34d399",
1890
+ background: "#030d07",
1891
+ surface: "#051a0e",
1892
+ text: "#d1fae5",
1893
+ border: "#064e20",
1894
+ success: "#4ade80",
1895
+ warning: "#facc15",
1896
+ error: "#f87171",
1839
1897
  borderRadius: "12px",
1840
- fontFamily: "system-ui, -apple-system, sans-serif",
1898
+ fontFamily: "'Instrument Sans', system-ui, sans-serif",
1841
1899
  },
1842
- "dark-matrix": {
1843
- highlight: "#33ff33",
1900
+ "green-matrix": {
1901
+ highlight: "#39ff14",
1844
1902
  background: "#000000",
1845
- surface: "#0a0a0a",
1846
- text: "#00ff00",
1847
- border: "#1a1a1a",
1848
- success: "#33ff33",
1903
+ surface: "#060f06",
1904
+ text: "#00ff41",
1905
+ border: "#0d2b0d",
1906
+ success: "#39ff14",
1849
1907
  warning: "#ffff00",
1850
1908
  error: "#ff3333",
1851
1909
  borderRadius: "0px",
1852
- fontFamily: "'Courier New', monospace",
1910
+ fontFamily: "'Share Tech Mono', monospace",
1853
1911
  },
1854
- "dark-luxury": {
1855
- highlight: "#fde047", // Yellow 300
1856
- background: "#1c1917", // Stone 900
1857
- surface: "#292524", // Stone 800
1858
- text: "#fafaf9", // Stone 50
1859
- border: "#44403c", // Stone 700
1860
- success: "#a3e635", // Lime 400
1861
- warning: "#f59e0b", // Amber 500
1862
- error: "#fca5a5", // Red 300
1912
+ "gold-luxury": {
1913
+ highlight: "#fde047",
1914
+ background: "#1c1917",
1915
+ surface: "#292524",
1916
+ text: "#fafaf9",
1917
+ border: "#44403c",
1918
+ success: "#a3e635",
1919
+ warning: "#f59e0b",
1920
+ error: "#fca5a5",
1863
1921
  borderRadius: "24px",
1864
- fontFamily: "'Cinzel', serif",
1922
+ fontFamily: "'Bodoni Moda', serif",
1865
1923
  },
1866
- "dark-energetic": {
1867
- highlight: "#d946ef", // Fuchsia 500
1868
- background: "#2e1046",
1869
- surface: "#401561",
1870
- text: "#f3e8ff", // Purple 50
1871
- border: "#581c87", // Purple 900
1872
- success: "#4ade80", // Green 400
1873
- warning: "#facc15", // Yellow 400
1874
- error: "#f87171", // Red 400
1924
+ "purple-electric": {
1925
+ highlight: "#d946ef",
1926
+ background: "#110820",
1927
+ surface: "#1a0d30",
1928
+ text: "#f3e8ff",
1929
+ border: "#3b0764",
1930
+ success: "#4ade80",
1931
+ warning: "#facc15",
1932
+ error: "#f87171",
1875
1933
  borderRadius: "14px",
1876
1934
  fontFamily: "'Geologica', sans-serif",
1877
1935
  },
1936
+ "dark-neon-brutalism": {
1937
+ highlight: "#00e5cc",
1938
+ background: "#0e1420",
1939
+ surface: "#0e1420",
1940
+ text: "#e8eef5",
1941
+ border: "#00e5cc",
1942
+ success: "#00e5cc",
1943
+ warning: "#ffe45e",
1944
+ error: "#ff4d6d",
1945
+ borderRadius: "4px",
1946
+ fontFamily: "'JetBrains Mono', monospace",
1947
+ buttonTextColor: "#0e1420",
1948
+ },
1949
+ "rose-editorial": {
1950
+ highlight: "#be3455",
1951
+ background: "#fdf8f5",
1952
+ surface: "#ffffff",
1953
+ text: "#1a0a0f",
1954
+ border: "#f2d5db",
1955
+ success: "#2d6a4f",
1956
+ warning: "#b5451b",
1957
+ error: "#9b1d20",
1958
+ borderRadius: "0px",
1959
+ fontFamily: "'Cormorant', serif",
1960
+ },
1961
+ "amber-retro": {
1962
+ highlight: "#f59e0b",
1963
+ background: "#1a1008",
1964
+ surface: "#241a0a",
1965
+ text: "#fef3c7",
1966
+ border: "#78350f",
1967
+ success: "#84cc16",
1968
+ warning: "#f59e0b",
1969
+ error: "#ef4444",
1970
+ borderRadius: "6px",
1971
+ fontFamily: "'Syne', sans-serif",
1972
+ },
1878
1973
  };
1879
1974
  const StyleProvider = ({ config, children, }) => {
1880
1975
  // Track hydration state to prevent mismatches
@@ -1884,8 +1979,10 @@
1884
1979
  }, []);
1885
1980
  // PERFORMANCE OPTIMIZATION: Memoize style calculations
1886
1981
  const themedStyles = T$2(() => {
1887
- const themeName = config.theme || "light-fresh";
1888
- const themeDefaults = themes[themeName] || themes["light-fresh"];
1982
+ const rawThemeName = config.theme || "teal-minimal";
1983
+ // Redirect legacy theme names to new names
1984
+ const themeName = legacyThemeRedirects[rawThemeName] || rawThemeName;
1985
+ const themeDefaults = themes[themeName] || themes["teal-minimal"];
1889
1986
  const getCSSValue = (value, fallback) => {
1890
1987
  if (!value)
1891
1988
  return fallback;
@@ -1955,6 +2052,7 @@
1955
2052
  "--bw-surface-color": finalColors.surface,
1956
2053
  "--bw-text-color": finalColors.text,
1957
2054
  "--bw-text-muted": addOpacity(finalColors.text, 0.7),
2055
+ "--bw-button-text-color": themeDefaults.buttonTextColor || "#ffffff",
1958
2056
  "--bw-border-color": finalColors.border,
1959
2057
  "--bw-success-color": finalColors.success,
1960
2058
  "--bw-warning-color": finalColors.warning,
@@ -1972,7 +2070,7 @@
1972
2070
  "--bw-highlight-muted": addOpacity(finalColors.highlight, 0.1),
1973
2071
  "--bw-highlight-subtle": addOpacity(finalColors.highlight, 0.05),
1974
2072
  "--bw-text-subtle": addOpacity(finalColors.text, 0.4),
1975
- colorScheme: themeName.startsWith("dark-") || themeName === "dark-night" ? "dark" : "light",
2073
+ 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",
1976
2074
  };
1977
2075
  }, [
1978
2076
  config.theme,
@@ -4526,6 +4624,7 @@
4526
4624
  "--bw-font-family": computedStyles.getPropertyValue("--bw-font-family").trim() || "system-ui, sans-serif",
4527
4625
  "--bw-shadow-md": computedStyles.getPropertyValue("--bw-shadow-md").trim() ||
4528
4626
  "0 4px 6px -1px rgba(0, 0, 0, 0.1)",
4627
+ "--bw-button-text-color": computedStyles.getPropertyValue("--bw-button-text-color").trim() || "#ffffff",
4529
4628
  };
4530
4629
  setFallbackStyles(fallbacks);
4531
4630
  }
@@ -11243,16 +11342,37 @@
11243
11342
  ZodUnion.create;
11244
11343
  ZodIntersection.create;
11245
11344
  ZodTuple.create;
11246
- ZodEnum.create;
11345
+ const enumType = ZodEnum.create;
11247
11346
  ZodPromise.create;
11248
11347
  ZodOptional.create;
11249
11348
  ZodNullable.create;
11349
+ const preprocessType = ZodEffects.createWithPreprocess;
11250
11350
 
11251
- const participantSchema = (t) => objectType({
11252
- name: stringType().trim().min(1, t("validation.nameRequired")),
11351
+ const DEFAULT_PARTICIPANT_FIELDS_CONFIG = {
11352
+ name: { enabled: true, required: true },
11353
+ age: { enabled: true, required: false },
11354
+ level: { enabled: false, required: false },
11355
+ };
11356
+ const participantSchema = (t, fieldsConfig) => objectType({
11357
+ name: stringType().trim().optional(),
11253
11358
  age: numberType().min(0).max(120).optional(),
11359
+ level: preprocessType((value) => (value === "" ? undefined : value), enumType(["beginner", "advanced"]).optional()),
11360
+ })
11361
+ .superRefine((value, ctx) => {
11362
+ if (fieldsConfig.name.required && (!value.name || value.name.trim().length < 1)) {
11363
+ ctx.addIssue({ code: ZodIssueCode.custom, message: t("validation.nameRequired"), path: ["name"] });
11364
+ }
11365
+ if (!fieldsConfig.name.enabled && value.name && value.name.trim().length > 0) {
11366
+ ctx.addIssue({ code: ZodIssueCode.custom, message: t("validation.nameRequired"), path: ["name"] });
11367
+ }
11368
+ if (fieldsConfig.age.required && typeof value.age !== "number") {
11369
+ ctx.addIssue({ code: ZodIssueCode.custom, message: t("validation.ageRequired"), path: ["age"] });
11370
+ }
11371
+ if (fieldsConfig.level.required && !value.level) {
11372
+ ctx.addIssue({ code: ZodIssueCode.custom, message: t("validation.levelRequired"), path: ["level"] });
11373
+ }
11254
11374
  });
11255
- function createBookingFormSchema(t) {
11375
+ function createBookingFormSchema(t, fieldsConfig = DEFAULT_PARTICIPANT_FIELDS_CONFIG) {
11256
11376
  const tr = t ?? ((key) => key);
11257
11377
  return objectType({
11258
11378
  customerName: stringType().trim().min(2, tr("validation.nameMinLength")),
@@ -11262,7 +11382,7 @@
11262
11382
  .email(tr("validation.emailInvalid"))
11263
11383
  .regex(/\.[a-zA-Z]{2,}$/, tr("validation.emailDomainInvalid")),
11264
11384
  customerPhone: stringType().trim().optional(),
11265
- participants: arrayType(participantSchema(tr)).min(1, tr("validation.participantRequired")),
11385
+ participants: arrayType(participantSchema(tr, fieldsConfig)).min(1, tr("validation.participantRequired")),
11266
11386
  discountCode: stringType().trim().optional(),
11267
11387
  comment: stringType().trim().optional(),
11268
11388
  acceptTerms: booleanType().refine((val) => val === true, {
@@ -11303,11 +11423,13 @@
11303
11423
  whiteSpace: "nowrap",
11304
11424
  border: "none",
11305
11425
  };
11426
+ // CSS class name for button hover effects
11427
+ const buttonClassName = "bw-button-hover";
11306
11428
  const buttonStyles = {
11307
11429
  primary: {
11308
11430
  ...buttonBase,
11309
11431
  backgroundColor: "var(--bw-highlight-color)",
11310
- color: "#ffffff",
11432
+ color: "var(--bw-button-text-color, #ffffff)",
11311
11433
  border: "none",
11312
11434
  },
11313
11435
  secondary: {
@@ -11403,7 +11525,8 @@
11403
11525
  gap: "8px",
11404
11526
  marginTop: "10px",
11405
11527
  paddingTop: "10px",
11406
- borderTop: "1px dashed var(--bw-border-color)",
11528
+ paddingBottom: "25px",
11529
+ borderBottom: "1px dashed var(--bw-border-color)",
11407
11530
  },
11408
11531
  label: {
11409
11532
  display: "inline-flex",
@@ -11461,6 +11584,8 @@
11461
11584
  const { locale } = useLocale();
11462
11585
  const timezone = useTimezone();
11463
11586
  const roundEnabled = systemConfig?.roundPricesEnabled !== false;
11587
+ const participantFieldsConfig = eventDetails.participantFieldsConfig ?? DEFAULT_PARTICIPANT_FIELDS_CONFIG;
11588
+ const participantLevelOptions = eventDetails.participantLevelOptions ?? ["beginner", "advanced"];
11464
11589
  const roundDiscountUp = (minorUnits) => Math.ceil(minorUnits / 100) * 100;
11465
11590
  const calcPercentDiscountAmount = (baseAmount, basisPoints, round) => {
11466
11591
  const raw = Math.round((baseAmount * basisPoints) / 10000);
@@ -11473,18 +11598,19 @@
11473
11598
  // Per-participant upsell selections: participantIndex -> array of upsell package IDs
11474
11599
  const [participantUpsells, setParticipantUpsells] = d$1({});
11475
11600
  const form = useForm({
11476
- resolver: t(createBookingFormSchema(t$1)),
11601
+ resolver: t(createBookingFormSchema(t$1, participantFieldsConfig)),
11477
11602
  defaultValues: {
11478
11603
  customerName: "",
11479
11604
  customerEmail: "",
11480
11605
  customerPhone: "",
11481
- participants: [{ name: "" }],
11606
+ participants: [{ name: "", level: undefined }],
11482
11607
  discountCode: "",
11483
11608
  comment: "",
11484
11609
  acceptTerms: false,
11485
11610
  },
11486
11611
  });
11487
11612
  const watchedParticipants = form.watch("participants");
11613
+ const participantCount = watchedParticipants.length;
11488
11614
  const watchedCustomerName = form.watch("customerName");
11489
11615
  const watchedCustomerEmail = form.watch("customerEmail");
11490
11616
  const watchedComment = form.watch("comment");
@@ -11526,14 +11652,13 @@
11526
11652
  const calculateBaseTotal = q$2(() => {
11527
11653
  if (!eventDetails)
11528
11654
  return 0;
11529
- return eventDetails.price * watchedParticipants.filter((p) => p.name.trim()).length;
11530
- }, [eventDetails, watchedParticipants]);
11655
+ return eventDetails.price * participantCount;
11656
+ }, [eventDetails, participantCount]);
11531
11657
  // Calculate upsells total based on per-participant selections
11532
11658
  const calculateUpsellsTotal = q$2(() => {
11533
11659
  let total = 0;
11534
- watchedParticipants.forEach((participant, index) => {
11535
- // Only count upsells for participants with names
11536
- if (participant.name.trim()) {
11660
+ watchedParticipants.forEach((_, index) => {
11661
+ if (participantCount > 0) {
11537
11662
  const participantUpsellIds = participantUpsells[index] || [];
11538
11663
  participantUpsellIds.forEach(upsellId => {
11539
11664
  const upsell = upsells.find(u => u.id === upsellId);
@@ -11544,7 +11669,7 @@
11544
11669
  }
11545
11670
  });
11546
11671
  return total;
11547
- }, [participantUpsells, upsells, watchedParticipants]);
11672
+ }, [participantUpsells, upsells, watchedParticipants, participantCount]);
11548
11673
  const calculateTotalDiscount = q$2(() => {
11549
11674
  return appliedVouchers.reduce((total, voucher) => {
11550
11675
  if (voucher.type === "discount") {
@@ -11565,8 +11690,7 @@
11565
11690
  const calculateDeposit = () => {
11566
11691
  if (!eventDetails || !eventDetails.deposit)
11567
11692
  return 0;
11568
- const participantCount = watchedParticipants.filter((p) => p.name.trim()).length;
11569
- return eventDetails.deposit * participantCount;
11693
+ return eventDetails.deposit * watchedParticipants.length;
11570
11694
  };
11571
11695
  const baseTotal = calculateBaseTotal();
11572
11696
  const upsellsTotal = calculateUpsellsTotal();
@@ -11583,8 +11707,8 @@
11583
11707
  // Includes participantIndices to track which participants selected each upsell
11584
11708
  const aggregatedUpsellSelections = q$2(() => {
11585
11709
  const upsellParticipantMap = {};
11586
- watchedParticipants.forEach((participant, index) => {
11587
- if (participant.name.trim()) {
11710
+ watchedParticipants.forEach((_, index) => {
11711
+ if (participantCount > 0) {
11588
11712
  const participantUpsellIds = participantUpsells[index] || [];
11589
11713
  participantUpsellIds.forEach(upsellId => {
11590
11714
  if (!upsellParticipantMap[upsellId]) {
@@ -11618,15 +11742,17 @@
11618
11742
  setAppliedVouchers((prev) => prev.filter((v) => v.code !== code));
11619
11743
  }, []);
11620
11744
  const isReadyForPayment = () => {
11621
- const participantsWithNames = watchedParticipants.filter((p) => p.name.trim()).length;
11745
+ const participantsWithNames = watchedParticipants.filter((p) => p.name?.trim()).length;
11622
11746
  const totalParticipantRows = watchedParticipants.length;
11623
- const allParticipantsHaveNames = participantsWithNames === totalParticipantRows;
11747
+ const allParticipantsHaveNames = participantFieldsConfig.name.required
11748
+ ? participantsWithNames === totalParticipantRows
11749
+ : true;
11624
11750
  const participantsWithinLimit = participantsWithNames <= (eventDetails?.availableSpots || 0);
11625
11751
  const hasValidCustomerName = watchedCustomerName && watchedCustomerName.trim().length >= 2;
11626
11752
  const hasValidCustomerEmail = watchedCustomerEmail && watchedCustomerEmail.trim().length > 0 && !customerEmailError;
11627
11753
  return allParticipantsHaveNames &&
11628
11754
  participantsWithinLimit &&
11629
- participantsWithNames > 0 &&
11755
+ (participantFieldsConfig.name.required ? participantsWithNames > 0 : totalParticipantRows > 0) &&
11630
11756
  hasValidCustomerName &&
11631
11757
  hasValidCustomerEmail &&
11632
11758
  watchedAcceptTerms;
@@ -11634,7 +11760,7 @@
11634
11760
  y$1(() => {
11635
11761
  if (appliedVouchers.length > 0) {
11636
11762
  const newBaseTotal = eventDetails?.price
11637
- ? eventDetails.price * watchedParticipants.filter((p) => p.name.trim()).length
11763
+ ? eventDetails.price * watchedParticipants.length
11638
11764
  : 0;
11639
11765
  const currentUpsellsTotal = calculateUpsellsTotal();
11640
11766
  const orderTotal = newBaseTotal + currentUpsellsTotal;
@@ -11679,7 +11805,7 @@
11679
11805
  const currentParticipants = form.getValues("participants");
11680
11806
  const availableSpots = eventDetails?.availableSpots || 0;
11681
11807
  if (currentParticipants.length < availableSpots) {
11682
- form.setValue("participants", [...currentParticipants, { name: "" }]);
11808
+ form.setValue("participants", [...currentParticipants, { name: "", level: undefined }]);
11683
11809
  }
11684
11810
  else {
11685
11811
  alert(t$1("booking.maxParticipants", { count: availableSpots }));
@@ -11798,7 +11924,7 @@
11798
11924
  justifyContent: "space-between",
11799
11925
  alignItems: "center",
11800
11926
  marginBottom: "16px",
11801
- }, children: u$2("h2", { style: { ...sectionHeaderStyles$1, marginBottom: 0 }, children: t$1("booking.participants") }) }), u$2("div", { style: { display: "flex", flexDirection: "column", gap: "16px" }, children: [watchedParticipants.map((_, index) => (u$2("div", { style: { display: "flex", flexDirection: "column", gap: "8px" }, children: [u$2("div", { style: { display: "flex", gap: "12px", alignItems: "center" }, children: [u$2("div", { style: { flex: 1 }, children: [u$2("label", { htmlFor: `participant-name-${index}`, style: labelStyles$1, children: t$1("booking.participantName") }), u$2("input", { id: `participant-name-${index}`, ...form.register(`participants.${index}.name`), type: "text", style: inputStyles$1, placeholder: t$1("booking.participantNamePlaceholder") }), form.formState.errors.participants?.[index]?.name && (u$2("p", { style: errorTextStyles$1, children: form.formState.errors.participants[index]?.name?.message }))] }), u$2("div", { style: { width: "80px" }, children: [u$2("label", { htmlFor: `participant-age-${index}`, style: labelStyles$1, children: t$1("booking.participantAge") }), u$2("input", { id: `participant-age-${index}`, ...form.register(`participants.${index}.age`, {
11927
+ }, children: u$2("h2", { style: { ...sectionHeaderStyles$1, marginBottom: 0 }, children: t$1("booking.participants") }) }), u$2("div", { style: { display: "flex", flexDirection: "column", gap: "16px" }, children: [watchedParticipants.map((_, index) => (u$2("div", { style: { display: "flex", flexDirection: "column", gap: "8px" }, children: [u$2("div", { style: { display: "flex", gap: "12px", alignItems: "center" }, children: [participantFieldsConfig.name.enabled && (u$2("div", { style: { flex: 1 }, children: [u$2("label", { htmlFor: `participant-name-${index}`, style: labelStyles$1, children: t$1("booking.participantName") }), u$2("input", { id: `participant-name-${index}`, ...form.register(`participants.${index}.name`), type: "text", style: inputStyles$1, placeholder: t$1("booking.participantNamePlaceholder") }), form.formState.errors.participants?.[index]?.name && (u$2("p", { style: errorTextStyles$1, children: form.formState.errors.participants[index]?.name?.message }))] })), participantFieldsConfig.age.enabled && (u$2("div", { style: { width: "80px" }, children: [u$2("label", { htmlFor: `participant-age-${index}`, style: labelStyles$1, children: t$1("booking.participantAge") }), u$2("input", { id: `participant-age-${index}`, ...form.register(`participants.${index}.age`, {
11802
11928
  setValueAs: (value) => {
11803
11929
  if (value === "" || value === null || value === undefined) {
11804
11930
  return undefined;
@@ -11806,7 +11932,7 @@
11806
11932
  const num = Number(value);
11807
11933
  return Number.isNaN(num) ? undefined : num;
11808
11934
  },
11809
- }), type: "number", min: "0", max: "120", style: inputStyles$1, placeholder: "25" })] }), watchedParticipants.length > 1 && (u$2("div", { children: [u$2("label", { style: { ...labelStyles$1, visibility: "hidden" }, children: "\u00A0" }), u$2("button", { type: "button", onClick: () => removeParticipant(index), style: {
11935
+ }), type: "number", min: "0", max: "120", style: inputStyles$1, placeholder: "25" })] })), watchedParticipants.length > 1 && (u$2("div", { children: [u$2("label", { style: { ...labelStyles$1, visibility: "hidden" }, children: "\u00A0" }), u$2("button", { type: "button", onClick: () => removeParticipant(index), style: {
11810
11936
  color: "var(--bw-error-color)",
11811
11937
  backgroundColor: "var(--bw-surface-color)",
11812
11938
  border: "1px solid var(--bw-border-color)",
@@ -11822,7 +11948,7 @@
11822
11948
  fontWeight: 700,
11823
11949
  fontFamily: "var(--bw-font-family)",
11824
11950
  padding: 0,
11825
- }, children: "\u00D7" })] }))] }), upsells.length > 0 && (u$2("div", { style: participantUpsellStyles.container, children: upsells.map((upsell) => {
11951
+ }, children: "\u00D7" })] }))] }), participantFieldsConfig.level.enabled && (u$2("div", { style: { minWidth: "140px" }, children: [u$2("label", { htmlFor: `participant-level-${index}`, style: labelStyles$1, children: t$1("booking.participantLevel") }), u$2("select", { id: `participant-level-${index}`, ...form.register(`participants.${index}.level`), style: inputStyles$1, children: [u$2("option", { value: "", children: t$1("booking.participantLevelPlaceholder") }), participantLevelOptions.map((level) => (u$2("option", { value: level, children: t$1(`level.${level}`) }, level)))] }), form.formState.errors.participants?.[index]?.level && (u$2("p", { style: errorTextStyles$1, children: form.formState.errors.participants[index]?.level?.message }))] })), upsells.length > 0 && (u$2("div", { style: participantUpsellStyles.container, children: upsells.map((upsell) => {
11826
11952
  const isSelected = (participantUpsells[index] || []).includes(upsell.id);
11827
11953
  return (u$2("label", { htmlFor: `upsell-${index}-${upsell.id}`, style: isSelected ? participantUpsellStyles.labelSelected : participantUpsellStyles.label, children: [u$2("input", { id: `upsell-${index}-${upsell.id}`, type: "checkbox", style: participantUpsellStyles.checkbox, checked: isSelected, onChange: () => toggleParticipantUpsell(index, upsell.id) }), u$2("span", { style: { fontWeight: 500 }, children: upsell.name }), u$2("span", { style: { fontSize: "12px", opacity: 0.8 }, children: ["(+", formatCurrency(upsell.price), ")"] })] }, upsell.id));
11828
11954
  }) }))] }, index))), watchedParticipants.length < eventDetails.availableSpots ? (u$2("div", { style: {
@@ -11851,9 +11977,9 @@
11851
11977
  color: "var(--bw-text-color)",
11852
11978
  fontWeight: 500,
11853
11979
  fontFamily: "var(--bw-font-family)",
11854
- }, children: [u$2("span", { style: { fontWeight: 200 }, children: [watchedParticipants.length > 1 ? watchedParticipants.filter((p) => p.name.trim()).length : 1, " x "] }), " ", formatCurrency(eventDetails.price)] })] }), upsellsTotal > 0 && (u$2("div", { style: { marginTop: "8px", paddingTop: "8px", borderTop: "1px dashed var(--bw-border-color)" }, children: [u$2("span", { style: { color: "var(--bw-text-muted)", fontFamily: "var(--bw-font-family)", fontSize: "13px", display: "block", marginBottom: "4px" }, children: [t$1("common.extras"), ":"] }), upsells.map((upsell) => {
11980
+ }, children: [u$2("span", { style: { fontWeight: 200 }, children: [watchedParticipants.length > 1 ? watchedParticipants.length : 1, " x "] }), " ", formatCurrency(eventDetails.price)] })] }), upsellsTotal > 0 && (u$2("div", { style: { marginTop: "8px", paddingTop: "8px", borderTop: "1px dashed var(--bw-border-color)" }, children: [u$2("span", { style: { color: "var(--bw-text-muted)", fontFamily: "var(--bw-font-family)", fontSize: "13px", display: "block", marginBottom: "4px" }, children: [t$1("common.extras"), ":"] }), upsells.map((upsell) => {
11855
11981
  // Count how many participants have this upsell selected
11856
- const countWithUpsell = watchedParticipants.filter((p, idx) => p.name.trim() && (participantUpsells[idx] || []).includes(upsell.id)).length;
11982
+ const countWithUpsell = watchedParticipants.filter((_, idx) => (participantUpsells[idx] || []).includes(upsell.id)).length;
11857
11983
  if (countWithUpsell === 0)
11858
11984
  return null;
11859
11985
  const upsellLineTotal = upsell.price * countWithUpsell;
@@ -11954,15 +12080,17 @@
11954
12080
  }, children: t$1("summary.remainingOnSite", { amount: formatCurrency(totalAmount - depositAmount) }) }))] })] })] }), u$2("div", { ref: paymentSectionRef, children: (stripePromise || systemConfig?.paymentProvider === "mollie") &&
11955
12081
  (() => {
11956
12082
  if (!isReadyForPayment()) {
11957
- const participantsWithNames = watchedParticipants.filter((p) => p.name.trim()).length;
12083
+ const participantsWithNames = watchedParticipants.filter((p) => p.name?.trim()).length;
11958
12084
  const totalParticipantRows = watchedParticipants.length;
11959
12085
  const participantsWithoutNames = totalParticipantRows - participantsWithNames;
11960
12086
  const missing = [];
11961
- if (participantsWithNames === 0) {
11962
- missing.push(t$1("payment.needParticipant"));
11963
- }
11964
- else if (participantsWithoutNames > 0) {
11965
- missing.push(t$1("payment.needAllNames", { count: totalParticipantRows }));
12087
+ if (participantFieldsConfig.name.required) {
12088
+ if (participantsWithNames === 0) {
12089
+ missing.push(t$1("payment.needParticipant"));
12090
+ }
12091
+ else if (participantsWithoutNames > 0) {
12092
+ missing.push(t$1("payment.needAllNames", { count: totalParticipantRows }));
12093
+ }
11966
12094
  }
11967
12095
  if (participantsWithNames > (eventDetails?.availableSpots || 0)) {
11968
12096
  missing.push(t$1("payment.reduceParticipants", { count: eventDetails?.availableSpots || 0 }));
@@ -12137,7 +12265,7 @@
12137
12265
  try {
12138
12266
  const response = await fetch(getApiUrl(config.apiBaseUrl, "/booking/get-booking-by-payment"), {
12139
12267
  method: "POST",
12140
- headers: createApiHeaders(config),
12268
+ headers: createApiHeaders(config, locale),
12141
12269
  body: JSON.stringify(createRequestBody(config, {
12142
12270
  paymentIntentId: targetPaymentIntentId,
12143
12271
  })),
@@ -12371,7 +12499,7 @@
12371
12499
  flexDirection: "column",
12372
12500
  gap: "var(--bw-spacing-small)",
12373
12501
  }, children: formData.participants
12374
- .filter((p) => p.name.trim())
12502
+ .filter((p) => p.name?.trim() || p.age || p.level)
12375
12503
  .map((participant, index) => (u$2("div", { className: "print-participant", style: {
12376
12504
  display: "flex",
12377
12505
  justifyContent: "space-between",
@@ -12382,11 +12510,15 @@
12382
12510
  }, children: u$2("div", { className: "print-participant-info", children: [u$2("div", { className: "print-participant-name", style: {
12383
12511
  color: "var(--bw-text-color)",
12384
12512
  fontFamily: "var(--bw-font-family)",
12385
- }, children: participant.name }), participant.age && (u$2("div", { className: "print-participant-age", style: {
12513
+ }, children: participant.name || `#${index + 1}` }), participant.age && (u$2("div", { className: "print-participant-age", style: {
12386
12514
  color: "var(--bw-text-muted)",
12387
12515
  fontSize: "var(--bw-font-size-small)",
12388
12516
  fontFamily: "var(--bw-font-family)",
12389
- }, children: t("success.age", { age: participant.age }) }))] }) }, index))) }) })] })), u$2("div", { className: "print-booking-card", style: {
12517
+ }, children: t("success.age", { age: participant.age }) })), participant.level && (u$2("div", { style: {
12518
+ color: "var(--bw-text-muted)",
12519
+ fontSize: "var(--bw-font-size-small)",
12520
+ fontFamily: "var(--bw-font-family)",
12521
+ }, children: [t("booking.participantLevel"), ": ", t(`level.${participant.level}`)] }))] }) }, index))) }) })] })), u$2("div", { className: "print-booking-card", style: {
12390
12522
  backgroundColor: "var(--bw-surface-color)",
12391
12523
  border: `1px solid var(--bw-border-color)`,
12392
12524
  borderRadius: "var(--bw-border-radius)",
@@ -13794,8 +13926,48 @@
13794
13926
  const rest = formatted.slice(0, -1).join(", ");
13795
13927
  return `${t("duration.optionally")} ${rest} ${t("duration.or")} ${last} ${unitPlural}`;
13796
13928
  }
13797
- function EventTypeSelection({ eventTypes, onEventTypeSelect, isLoading = false, skeletonCount = 4, showVoucherAttachment = false, onVoucherClick, }) {
13929
+ function InfoBadge({ text }) {
13930
+ const [open, setOpen] = d$1(false);
13931
+ const ref = A$2(null);
13932
+ return (u$2("span", { ref: ref, onClick: (e) => { e.stopPropagation(); setOpen((v) => !v); }, style: {
13933
+ position: "relative",
13934
+ display: "inline-flex",
13935
+ alignItems: "center",
13936
+ justifyContent: "center",
13937
+ width: "16px",
13938
+ height: "16px",
13939
+ borderRadius: "50%",
13940
+ border: "1px solid var(--bw-highlight-color)",
13941
+ color: "var(--bw-highlight-color)",
13942
+ fontSize: "9px",
13943
+ fontWeight: 700,
13944
+ cursor: "pointer",
13945
+ flexShrink: 0,
13946
+ userSelect: "none",
13947
+ }, children: ["i", open && (u$2("span", { style: {
13948
+ position: "absolute",
13949
+ bottom: "calc(100% + 6px)",
13950
+ right: 0,
13951
+ backgroundColor: "var(--bw-surface-color)",
13952
+ border: "1px solid var(--bw-border-color)",
13953
+ borderRadius: "var(--bw-border-radius-small)",
13954
+ boxShadow: "var(--bw-shadow-md)",
13955
+ padding: "6px 10px",
13956
+ fontSize: "14px",
13957
+ color: "var(--bw-text-color)",
13958
+ fontWeight: 400,
13959
+ whiteSpace: "normal",
13960
+ width: "160px",
13961
+ lineHeight: 1.4,
13962
+ zIndex: 100,
13963
+ textAlign: "left",
13964
+ pointerEvents: "none",
13965
+ }, children: text }))] }));
13966
+ }
13967
+ function EventTypeSelection({ eventTypes, onEventTypeSelect, onInstancePreview, isLoading = false, skeletonCount = 4, showVoucherAttachment = false, onVoucherClick, }) {
13798
13968
  const t = useTranslations();
13969
+ const { locale } = useLocale();
13970
+ const timezone = useTimezone();
13799
13971
  // State for details dialog
13800
13972
  const [detailsDialogOpen, setDetailsDialogOpen] = d$1(false);
13801
13973
  const [selectedEventTypeForDetails, setSelectedEventTypeForDetails] = d$1(null);
@@ -13905,7 +14077,7 @@
13905
14077
  display: "flex",
13906
14078
  flexDirection: "column",
13907
14079
  justifyContent: "space-between",
13908
- height: "400px",
14080
+ height: "490px",
13909
14081
  }, children: [u$2("div", { children: [u$2("h2", { className: "event-type-title", style: {
13910
14082
  fontSize: "clamp(1.1rem, 2.5vw, 24px)",
13911
14083
  fontWeight: 700,
@@ -13986,7 +14158,43 @@
13986
14158
  color: "var(--bw-text-color)",
13987
14159
  fontFamily: "var(--bw-font-family)",
13988
14160
  textAlign: "right",
13989
- }, children: u$2("span", { children: [t("common.from"), " ", formatCurrency(eventType.minPrice)] }) })] }), u$2("div", { style: {
14161
+ }, children: u$2("span", { children: [t("common.from"), " ", formatCurrency(eventType.minPrice)] }) })] }), (() => {
14162
+ const preview = eventType.cardPreview ?? [];
14163
+ return (u$2("div", { style: {
14164
+ marginTop: "12px",
14165
+ borderTop: "1px solid var(--bw-border-color)",
14166
+ paddingTop: "8px",
14167
+ marginBottom: "16px",
14168
+ }, children: [u$2("div", { style: {
14169
+ fontSize: "11px",
14170
+ fontWeight: 700,
14171
+ color: "var(--bw-text-muted)",
14172
+ textTransform: "uppercase",
14173
+ letterSpacing: "0.05em",
14174
+ marginBottom: "4px",
14175
+ }, children: t("events.previewSectionTitle") }), u$2("div", { style: { height: "102px" }, children: Array.from({ length: 3 }).map((_, i) => {
14176
+ const item = preview[i];
14177
+ if (!item)
14178
+ return u$2("div", { style: { height: "34px" } }, i);
14179
+ const hasDiscount = item.basePrice > 0 && item.price < item.basePrice;
14180
+ return (u$2("div", { onClick: (e) => { e.stopPropagation(); onInstancePreview?.(item.id, eventType.id); }, onMouseEnter: (e) => { if (onInstancePreview)
14181
+ e.currentTarget.style.backgroundColor = "var(--bw-border-color)"; }, onMouseLeave: (e) => { e.currentTarget.style.backgroundColor = "transparent"; }, style: {
14182
+ height: "34px",
14183
+ display: "grid",
14184
+ gridTemplateColumns: "auto 1fr auto 20px",
14185
+ alignItems: "center",
14186
+ gap: "8px",
14187
+ width: "100%",
14188
+ fontSize: "13px",
14189
+ color: "var(--bw-text-muted)",
14190
+ cursor: onInstancePreview ? "pointer" : "default",
14191
+ borderRadius: "4px",
14192
+ padding: "0 2px",
14193
+ transition: "background 0.15s",
14194
+ boxSizing: "border-box",
14195
+ }, children: [u$2("span", { style: { whiteSpace: "nowrap", display: "flex", alignItems: "center", gap: "3px" }, children: [item.isSpecial && (u$2("span", { style: { color: "var(--bw-highlight-color)", fontSize: "11px", lineHeight: 1 }, children: "\u2605" })), formatWeekday(item.startTime, timezone, locale), " ", formatDate(item.startTime, timezone, locale)] }), u$2("span", { style: { overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", opacity: 0.75 }, children: item.name }), u$2("span", { style: { display: "flex", alignItems: "center", gap: "4px", whiteSpace: "nowrap" }, children: [hasDiscount && (u$2("span", { style: { textDecoration: "line-through", opacity: 0.55, fontSize: "11px" }, children: formatCurrency(item.basePrice) })), u$2("span", { style: { fontWeight: 700, color: item.isSpecial ? "var(--bw-highlight-color)" : "var(--bw-text-color)" }, children: formatCurrency(item.price) })] }), item.specialDescription ? (u$2(InfoBadge, { text: item.specialDescription })) : (u$2("span", {}))] }, item.id));
14196
+ }) })] }));
14197
+ })(), u$2("div", { style: {
13990
14198
  display: "flex",
13991
14199
  justifyContent: "flex-end",
13992
14200
  alignItems: "center",
@@ -14001,7 +14209,6 @@
14001
14209
  backgroundColor: "var(--bw-surface-color)",
14002
14210
  padding: "12px",
14003
14211
  borderRadius: "var(--bw-border-radius)",
14004
- fontSize: "clamp(0.8rem, 2vw, 16px)",
14005
14212
  fontWeight: 600,
14006
14213
  fontFamily: "var(--bw-font-family)",
14007
14214
  display: "flex",
@@ -14014,7 +14221,7 @@
14014
14221
  }, children: t("button.moreDetails") })), isAvailable && (u$2("div", { style: {
14015
14222
  backgroundColor: "var(--bw-highlight-color)",
14016
14223
  color: "var(--bw-surface-color)",
14017
- padding: "12px 24px",
14224
+ padding: "12px 14px",
14018
14225
  borderRadius: "var(--bw-border-radius)",
14019
14226
  fontSize: "clamp(1rem, 2vw, 16px)",
14020
14227
  fontWeight: 600,
@@ -14672,6 +14879,172 @@
14672
14879
  }, children: "\u27F3" }), t("common.loading")] })) : (showAllButtonText) }) }))] }));
14673
14880
  }
14674
14881
 
14882
+ function SpecialsView({ specials, onEventSelect, isLoading = false, showSavingsAmount = true, showSavingsPercent = false, emptyStateText, isLoadingEventDetails = false, }) {
14883
+ const t = useTranslations();
14884
+ const { locale } = useLocale();
14885
+ const timezone = useTimezone();
14886
+ const [selectedId, setSelectedId] = d$1(null);
14887
+ const handleSelect = (id) => {
14888
+ setSelectedId(id);
14889
+ onEventSelect(id);
14890
+ };
14891
+ if (isLoading) {
14892
+ return u$2(NextEventsSkeleton, { count: 3 });
14893
+ }
14894
+ if (specials.length === 0) {
14895
+ return (u$2("div", { style: { maxWidth: "500px", margin: "0 auto", padding: "16px" }, children: u$2("div", { style: {
14896
+ display: "flex",
14897
+ flexDirection: "column",
14898
+ alignItems: "center",
14899
+ justifyContent: "center",
14900
+ textAlign: "center",
14901
+ backgroundColor: "var(--bw-surface-color)",
14902
+ border: "1px solid var(--bw-border-color)",
14903
+ borderRadius: "var(--bw-border-radius)",
14904
+ padding: "24px",
14905
+ fontFamily: "var(--bw-font-family)",
14906
+ minHeight: "300px",
14907
+ }, children: [u$2("div", { style: {
14908
+ display: "flex",
14909
+ alignItems: "center",
14910
+ justifyContent: "center",
14911
+ borderRadius: "50%",
14912
+ width: "64px",
14913
+ height: "64px",
14914
+ backgroundColor: "var(--bw-highlight-color)",
14915
+ marginBottom: "16px",
14916
+ fontSize: "32px",
14917
+ color: "#ffffff",
14918
+ opacity: 0.8,
14919
+ }, children: "\uD83C\uDFF7\uFE0F" }), u$2("h3", { style: {
14920
+ fontWeight: 600,
14921
+ margin: "0 0 8px 0",
14922
+ fontSize: "20px",
14923
+ color: "var(--bw-text-color)",
14924
+ fontFamily: "var(--bw-font-family)",
14925
+ }, children: t("specials.noSpecials") }), u$2("p", { style: {
14926
+ margin: 0,
14927
+ color: "var(--bw-text-muted)",
14928
+ fontSize: "16px",
14929
+ lineHeight: 1.6,
14930
+ fontFamily: "var(--bw-font-family)",
14931
+ maxWidth: "400px",
14932
+ }, children: emptyStateText ?? t("specials.noSpecialsMessage") })] }) }));
14933
+ }
14934
+ return (u$2("div", { style: {
14935
+ maxWidth: "500px",
14936
+ margin: "0 auto",
14937
+ padding: "16px",
14938
+ fontFamily: "var(--bw-font-family)",
14939
+ }, children: [u$2("div", { style: { textAlign: "center", marginBottom: "24px" }, children: [u$2("h2", { style: {
14940
+ fontWeight: 600,
14941
+ margin: "0 0 8px 0",
14942
+ fontSize: "18px",
14943
+ color: "var(--bw-text-color)",
14944
+ fontFamily: "var(--bw-font-family)",
14945
+ }, children: t("specials.title") }), u$2("p", { style: {
14946
+ margin: 0,
14947
+ fontSize: "16px",
14948
+ color: "var(--bw-text-muted)",
14949
+ fontFamily: "var(--bw-font-family)",
14950
+ }, children: t("specials.subtitle") })] }), u$2("div", { style: { display: "flex", flexDirection: "column", gap: "12px" }, children: specials.map((special) => {
14951
+ const isFullyBooked = special.availableSpots === 0;
14952
+ const isDisabled = isFullyBooked || !special.bookingOpen;
14953
+ const hasDiscount = special.basePrice > 0 && special.price < special.basePrice;
14954
+ return (u$2("div", { style: {
14955
+ position: "relative",
14956
+ backgroundColor: "var(--bw-surface-color)",
14957
+ borderRadius: "var(--bw-border-radius)",
14958
+ border: "1px solid var(--bw-highlight-color)",
14959
+ overflow: "hidden",
14960
+ opacity: isDisabled ? 0.5 : 1,
14961
+ cursor: isDisabled ? "not-allowed" : "pointer",
14962
+ transition: "all 0.2s ease",
14963
+ }, onClick: () => {
14964
+ if (!isDisabled)
14965
+ handleSelect(special.id);
14966
+ }, children: [selectedId === special.id && isLoadingEventDetails && (u$2("div", { style: {
14967
+ position: "absolute",
14968
+ inset: 0,
14969
+ display: "flex",
14970
+ alignItems: "center",
14971
+ justifyContent: "center",
14972
+ backgroundColor: "rgba(15, 23, 42, 0.8)",
14973
+ borderRadius: "var(--bw-border-radius)",
14974
+ zIndex: 10,
14975
+ }, children: u$2("div", { style: { fontSize: "32px", color: "var(--bw-highlight-color)", animation: "spin 1s linear infinite" }, children: "\u27F3" }) })), u$2("div", { style: { display: "flex", gap: "12px", padding: "12px" }, children: [special.images.length > 0 && (u$2("div", { style: {
14976
+ flexShrink: 0,
14977
+ width: "72px",
14978
+ height: "72px",
14979
+ borderRadius: "var(--bw-border-radius-small)",
14980
+ overflow: "hidden",
14981
+ }, children: u$2("img", { src: special.images[0], alt: special.eventTypeName, style: { width: "100%", height: "100%", objectFit: "cover" } }) })), u$2("div", { style: { flex: 1, minWidth: 0 }, children: [u$2("div", { style: { marginBottom: "4px" }, children: u$2("span", { style: {
14982
+ fontSize: "11px",
14983
+ fontWeight: 600,
14984
+ color: "var(--bw-text-muted)",
14985
+ textTransform: "uppercase",
14986
+ letterSpacing: "0.05em",
14987
+ }, children: special.categoryName }) }), u$2("h4", { style: {
14988
+ margin: "0 0 2px 0",
14989
+ fontSize: "15px",
14990
+ fontWeight: 600,
14991
+ color: "var(--bw-text-color)",
14992
+ lineHeight: 1.3,
14993
+ overflow: "hidden",
14994
+ textOverflow: "ellipsis",
14995
+ whiteSpace: "nowrap",
14996
+ }, children: special.eventTypeName }), u$2("div", { style: {
14997
+ fontSize: "13px",
14998
+ fontWeight: 500,
14999
+ color: "var(--bw-highlight-color)",
15000
+ marginBottom: "2px",
15001
+ overflow: "hidden",
15002
+ textOverflow: "ellipsis",
15003
+ whiteSpace: "nowrap",
15004
+ }, children: ["\u2605 ", special.name] }), special.specialDescription && (u$2("div", { style: {
15005
+ fontSize: "12px",
15006
+ color: "var(--bw-text-muted)",
15007
+ marginBottom: "6px",
15008
+ lineHeight: 1.4,
15009
+ }, children: special.specialDescription })), u$2("div", { style: { fontSize: "13px", color: "var(--bw-text-muted)", marginBottom: "8px" }, children: [formatWeekday(special.startTime, timezone, locale), ",", " ", formatDate(special.startTime, timezone, locale)] }), u$2("div", { style: { display: "flex", alignItems: "center", gap: "8px", flexWrap: "wrap" }, children: [hasDiscount && (u$2("span", { style: {
15010
+ fontSize: "13px",
15011
+ color: "var(--bw-text-muted)",
15012
+ textDecoration: "line-through",
15013
+ }, children: formatCurrency(special.basePrice) })), u$2("span", { style: {
15014
+ fontSize: "16px",
15015
+ fontWeight: 700,
15016
+ color: "var(--bw-highlight-color)",
15017
+ }, children: formatCurrency(special.price) }), hasDiscount && showSavingsAmount && (u$2("span", { style: {
15018
+ fontSize: "12px",
15019
+ fontWeight: 600,
15020
+ color: "#ffffff",
15021
+ backgroundColor: "var(--bw-success-color, #22c55e)",
15022
+ borderRadius: "4px",
15023
+ padding: "2px 6px",
15024
+ }, children: t("specials.save").replace("{{amount}}", formatCurrency(special.savings)) })), hasDiscount && showSavingsPercent && (u$2("span", { style: {
15025
+ fontSize: "12px",
15026
+ fontWeight: 600,
15027
+ color: "#ffffff",
15028
+ backgroundColor: "var(--bw-success-color, #22c55e)",
15029
+ borderRadius: "4px",
15030
+ padding: "2px 6px",
15031
+ }, children: t("specials.savePercent").replace("{{percent}}", String(special.savingsPercent)) }))] })] })] }), u$2("div", { style: {
15032
+ borderTop: "1px solid var(--bw-border-color)",
15033
+ padding: "8px 12px",
15034
+ display: "flex",
15035
+ justifyContent: "space-between",
15036
+ alignItems: "center",
15037
+ backgroundColor: "var(--bw-background-color)",
15038
+ }, children: [u$2("span", { style: { fontSize: "12px", color: "var(--bw-text-muted)" }, children: isFullyBooked
15039
+ ? t("common.fullyBooked")
15040
+ : t("specials.spotsLeft").replace("{{count}}", String(special.availableSpots)) }), u$2("span", { style: {
15041
+ fontSize: "13px",
15042
+ fontWeight: 600,
15043
+ color: "var(--bw-highlight-color)",
15044
+ }, children: [t("specials.bookNow"), " \u2192"] })] })] }, special.id));
15045
+ }) })] }));
15046
+ }
15047
+
14675
15048
  const getThemeConfig = (theme = "generic") => {
14676
15049
  switch (theme) {
14677
15050
  case "christmas":
@@ -14931,8 +15304,8 @@
14931
15304
  };
14932
15305
  const checkboxContainerStyles = {
14933
15306
  position: "absolute",
14934
- top: "12px",
14935
- right: "12px",
15307
+ bottom: "12px",
15308
+ left: "12px",
14936
15309
  zIndex: 1,
14937
15310
  };
14938
15311
  const checkboxInnerStyles = {
@@ -15027,6 +15400,7 @@
15027
15400
  alignItems: "flex-end",
15028
15401
  marginTop: "8px",
15029
15402
  paddingTop: "8px",
15403
+ paddingBottom: "0px",
15030
15404
  borderTop: "1px solid var(--bw-border-color)",
15031
15405
  };
15032
15406
  const pricePerPersonStyles = {
@@ -15118,7 +15492,7 @@
15118
15492
  };
15119
15493
  const selectedTotal = calculateTotal();
15120
15494
  const selectedCount = selectedUpsells.length;
15121
- const footerContent = (u$2(k$3, { children: [u$2("button", { type: "button", onClick: onBack, style: mergeStyles(buttonStyles.secondary, buttonStyles.fullWidth), children: t("common.back") }), u$2("button", { type: "button", onClick: onContinue, style: mergeStyles(buttonStyles.primary, buttonStyles.fullWidth), children: selectedCount === 0 ? t("button.continueWithout") : t("button.continue") })] }));
15495
+ const footerContent = (u$2(k$3, { children: [u$2("button", { type: "button", onClick: onBack, style: mergeStyles(buttonStyles.secondary, buttonStyles.fullWidth), className: buttonClassName, children: t("common.back") }), u$2("button", { type: "button", onClick: onContinue, style: mergeStyles(buttonStyles.primary, buttonStyles.fullWidth), className: buttonClassName, children: selectedCount === 0 ? t("button.continueWithout") : t("button.continue") })] }));
15122
15496
  return (u$2(Sidebar, { isOpen: isOpen, onClose: onClose, title: t("upsells.title"), footer: footerContent, children: u$2("div", { style: { display: "flex", flexDirection: "column", height: "100%", padding: "16px 16px" }, children: [isLoading && (u$2("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", gap: "12px", padding: "40px 20px", ...textStyles.muted }, children: [spinner(), u$2("span", { children: t("upsells.loading") })] })), !isLoading && upsells.length === 0 && (u$2("div", { style: { textAlign: "center", padding: "40px 20px", ...textStyles.muted }, children: u$2("p", { children: t("upsells.noExtras") }) })), !isLoading && upsells.length > 0 && (u$2("div", { style: { display: "flex", flexDirection: "column", gap: "12px", flex: 1, overflowY: "auto", paddingBottom: "16px" }, children: upsells.map((upsell) => (u$2(UpsellCard, { upsell: upsell, isSelected: isSelected(upsell.id), participantCount: participantCount, onSelect: () => selectUpsell(upsell.id) }, upsell.id))) })), selectedCount > 0 && (u$2("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginTop: "16px", paddingBottom: "16px", paddingTop: "16px", borderTop: "1px solid var(--bw-border-color)", fontSize: "14px" }, children: [u$2("span", { style: textStyles.muted, children: selectedCount === 1 ? t("upsells.selected", { count: selectedCount }) : t("upsells.selectedPlural", { count: selectedCount }) }), u$2("span", { style: { fontWeight: 600, color: "var(--bw-highlight-color)", fontFamily: "var(--bw-font-family)" }, children: ["+", formatCurrency(selectedTotal)] })] }))] }) }));
15123
15497
  }
15124
15498
 
@@ -15166,6 +15540,8 @@
15166
15540
  : (config.voucherIntegration ?? (hasEventSelection && !isDirectInstanceMode));
15167
15541
  // Selection flow state
15168
15542
  const [currentStep, setCurrentStep] = d$1("eventTypes");
15543
+ // Tracks where to return when closing the booking form
15544
+ const bookingReturnStep = A$2("eventInstances");
15169
15545
  const [eventTypes, setEventTypes] = d$1([]);
15170
15546
  const [selectedEventType, setSelectedEventType] = d$1(null);
15171
15547
  const [eventInstances, setEventInstances] = d$1([]);
@@ -15178,6 +15554,9 @@
15178
15554
  // State for upcoming events (next-events view mode)
15179
15555
  const [upcomingEvents, setUpcomingEvents] = d$1([]);
15180
15556
  const [showingPreview, setShowingPreview] = d$1(true);
15557
+ // State for specials view mode
15558
+ const [specials, setSpecials] = d$1([]);
15559
+ const [isLoadingSpecials, setIsLoadingSpecials] = d$1(false);
15181
15560
  // New: sidebar open state for single event type mode
15182
15561
  const [sidebarOpen, setSidebarOpen] = d$1(false);
15183
15562
  // Booking flow state
@@ -15328,6 +15707,11 @@
15328
15707
  await loadUpcomingEvents();
15329
15708
  return;
15330
15709
  }
15710
+ // Specials view mode: load special offers
15711
+ if (viewMode === "specials") {
15712
+ await loadSpecials();
15713
+ return;
15714
+ }
15331
15715
  // Single event type mode: load event type and instances, but don't open sidebar yet
15332
15716
  if (isSingleEventTypeMode) {
15333
15717
  await loadEventTypes();
@@ -15551,6 +15935,45 @@
15551
15935
  setError(data.error || t("error.loadUpcomingEvents"));
15552
15936
  }
15553
15937
  };
15938
+ const loadSpecials = async () => {
15939
+ setIsLoadingSpecials(true);
15940
+ const specialsSettings = config.specialsSettings ?? {};
15941
+ const requestBody = {
15942
+ organizationId: config.organizationId,
15943
+ limit: specialsSettings.count ?? 20,
15944
+ };
15945
+ if (config.categoryId) {
15946
+ requestBody.categoryId = config.categoryId;
15947
+ }
15948
+ else if (config.eventTypeIds) {
15949
+ requestBody.eventTypeIds = config.eventTypeIds;
15950
+ }
15951
+ else if (config.eventTypeId) {
15952
+ requestBody.eventTypeId = config.eventTypeId;
15953
+ }
15954
+ try {
15955
+ const response = await fetch(getApiUrl(config.apiBaseUrl, "/booking/specials"), {
15956
+ method: "POST",
15957
+ headers: createApiHeaders(config, locale),
15958
+ body: JSON.stringify(requestBody),
15959
+ });
15960
+ const data = await response.json();
15961
+ if (response.ok) {
15962
+ const wl = extractWidgetLanguagePayload(data);
15963
+ if (wl) {
15964
+ onWidgetLanguage?.(wl);
15965
+ onTimezone?.(wl.timezone);
15966
+ }
15967
+ setSpecials(data.specials || []);
15968
+ }
15969
+ else {
15970
+ setError(data.error || t("error.loadUpcomingEvents"));
15971
+ }
15972
+ }
15973
+ finally {
15974
+ setIsLoadingSpecials(false);
15975
+ }
15976
+ };
15554
15977
  const loadEventInstances = async (eventTypeId) => {
15555
15978
  const response = await fetch(getApiUrl(config.apiBaseUrl, "/booking/event-instances"), {
15556
15979
  method: "POST",
@@ -15735,6 +16158,7 @@
15735
16158
  // Event instance selection handlers
15736
16159
  const handleEventInstanceSelect = async (eventInstance) => {
15737
16160
  setSelectedEventInstance(eventInstance);
16161
+ bookingReturnStep.current = "eventInstances";
15738
16162
  // Set default participant count for upsell calculations
15739
16163
  const defaultParticipantCount = 1;
15740
16164
  setTempParticipantCount(defaultParticipantCount);
@@ -15747,7 +16171,14 @@
15747
16171
  if (availableUpsells.length > 0) {
15748
16172
  // Show upsells step
15749
16173
  setUpsells(availableUpsells);
15750
- setSelectedUpsells([]);
16174
+ // Pre-select default-checked upsells
16175
+ const defaultSelections = availableUpsells
16176
+ .filter((upsell) => upsell.defaultChecked && upsell.available)
16177
+ .map((upsell) => ({
16178
+ upsellPackageId: upsell.id,
16179
+ quantity: defaultParticipantCount,
16180
+ }));
16181
+ setSelectedUpsells(defaultSelections);
15751
16182
  setCurrentStep("upsells");
15752
16183
  setIsLoadingUpsells(false);
15753
16184
  return; // Don't proceed to booking yet
@@ -15778,7 +16209,7 @@
15778
16209
  setEventInstances([]);
15779
16210
  };
15780
16211
  const handleBackToEventInstances = () => {
15781
- setCurrentStep("eventInstances");
16212
+ setCurrentStep(bookingReturnStep.current);
15782
16213
  setSelectedEventInstance(null);
15783
16214
  setEventDetails(null);
15784
16215
  };
@@ -15812,8 +16243,7 @@
15812
16243
  }
15813
16244
  };
15814
16245
  const handleUpsellsBack = () => {
15815
- // Go back to event instance selection
15816
- setCurrentStep("eventInstances");
16246
+ setCurrentStep(bookingReturnStep.current);
15817
16247
  setSelectedUpsells([]);
15818
16248
  setUpsells([]);
15819
16249
  };
@@ -15848,10 +16278,36 @@
15848
16278
  setError(errorMessage);
15849
16279
  config.onError?.(errorMessage);
15850
16280
  };
15851
- const handleUpcomingEventSelect = async (eventInstanceId) => {
16281
+ const handleUpcomingEventSelect = async (eventInstanceId, eventTypeId) => {
16282
+ // Resolve the event type — may come from card preview (eventTypeId provided) or
16283
+ // from the next-events list where selectedEventType is already set
16284
+ const resolvedEventType = eventTypeId != null
16285
+ ? (eventTypes.find((et) => et.id === eventTypeId) ?? selectedEventType)
16286
+ : selectedEventType;
16287
+ if (resolvedEventType && resolvedEventType !== selectedEventType) {
16288
+ setSelectedEventType(resolvedEventType);
16289
+ }
16290
+ // Check if this is coming from a card preview (eventTypeId was provided)
16291
+ // In that case, we need to load event instances so back navigation works properly
16292
+ const isFromCardPreview = eventTypeId != null;
16293
+ if (isFromCardPreview && resolvedEventType) {
16294
+ // Load event instances in background so back navigation shows instances
16295
+ setShouldRenderInstanceSelection(true);
16296
+ void loadEventInstances(resolvedEventType.id);
16297
+ // Record that we should return to instance selection (not event types)
16298
+ bookingReturnStep.current = "eventInstances";
16299
+ }
16300
+ else {
16301
+ // Record where to return when the booking form is closed
16302
+ bookingReturnStep.current = currentStep === "eventInstances" ? "eventInstances" : "eventTypes";
16303
+ }
16304
+ // First try to find the event in upcomingEvents (for next-events view mode)
15852
16305
  const upcomingEvent = upcomingEvents.find((event) => event.id === eventInstanceId);
15853
- if (upcomingEvent) {
15854
- const eventInstance = {
16306
+ // If not found in upcomingEvents, try to find in card preview items
16307
+ const cardPreviewItem = !upcomingEvent && resolvedEventType?.cardPreview?.find((item) => item.id === eventInstanceId);
16308
+ // Build the event instance from either source
16309
+ const eventInstance = upcomingEvent
16310
+ ? {
15855
16311
  id: upcomingEvent.id,
15856
16312
  name: upcomingEvent.name,
15857
16313
  startTime: upcomingEvent.startTime,
@@ -15865,13 +16321,63 @@
15865
16321
  bookingOpen: upcomingEvent.bookingOpen,
15866
16322
  ...(upcomingEvent.deposit !== undefined && { deposit: upcomingEvent.deposit }),
15867
16323
  ...(upcomingEvent.notes !== undefined && { notes: upcomingEvent.notes }),
15868
- };
16324
+ }
16325
+ : cardPreviewItem
16326
+ ? {
16327
+ id: cardPreviewItem.id,
16328
+ name: cardPreviewItem.name,
16329
+ startTime: cardPreviewItem.startTime,
16330
+ endTime: cardPreviewItem.startTime,
16331
+ price: cardPreviewItem.price,
16332
+ maxParticipants: cardPreviewItem.availableSpots + 1,
16333
+ participantCount: 1,
16334
+ availableSpots: cardPreviewItem.availableSpots,
16335
+ durationDays: 1,
16336
+ durationPerDay: 1,
16337
+ bookingOpen: true,
16338
+ }
16339
+ : null;
16340
+ if (eventInstance) {
15869
16341
  setSelectedEventInstance(eventInstance);
15870
16342
  }
16343
+ setError(null);
16344
+ // Check for upsells before going to booking (same as handleEventInstanceSelect)
16345
+ const eventTypeForUpsells = resolvedEventType;
16346
+ if (eventTypeForUpsells) {
16347
+ const defaultParticipantCount = 1;
16348
+ setTempParticipantCount(defaultParticipantCount);
16349
+ setIsLoadingUpsells(true);
16350
+ setShouldRenderUpsells(true);
16351
+ try {
16352
+ const availableUpsells = await loadUpsells(eventTypeForUpsells.id, eventInstanceId, defaultParticipantCount);
16353
+ if (availableUpsells.length > 0) {
16354
+ setUpsells(availableUpsells);
16355
+ // Pre-select default-checked upsells
16356
+ const defaultSelections = availableUpsells
16357
+ .filter((upsell) => upsell.defaultChecked && upsell.available)
16358
+ .map((upsell) => ({
16359
+ upsellPackageId: upsell.id,
16360
+ quantity: defaultParticipantCount,
16361
+ }));
16362
+ setSelectedUpsells(defaultSelections);
16363
+ setCurrentStep("upsells");
16364
+ setIsLoadingUpsells(false);
16365
+ // Load event details in background for when user continues past upsells
16366
+ void loadEventDetails(eventInstanceId);
16367
+ return;
16368
+ }
16369
+ }
16370
+ catch (err) {
16371
+ console.error("Error loading upsells:", err);
16372
+ }
16373
+ finally {
16374
+ setIsLoadingUpsells(false);
16375
+ }
16376
+ }
16377
+ // No upsells — go directly to booking
15871
16378
  setCurrentStep("booking");
15872
16379
  setShouldRenderBookingForm(true);
15873
16380
  setIsLoadingEventDetails(true);
15874
- setError(null);
15875
16381
  try {
15876
16382
  await loadEventDetails(eventInstanceId);
15877
16383
  }
@@ -16028,6 +16534,31 @@
16028
16534
  window.history.replaceState({}, "", url.toString());
16029
16535
  }, config: config, onError: setError, paymentIntentId: successPaymentId })] }), showPromoDialog && config.promo && (u$2(PromoDialog, { config: config.promo, onClose: handlePromoDialogClose, onCtaClick: handlePromoCtaClick }))] }));
16030
16536
  }
16537
+ if (viewMode === "specials" && showingPreview) {
16538
+ return (u$2(StyleProvider, { config: config, children: [u$2("div", { ref: setWidgetContainerRef, children: [u$2(SpecialsView, { specials: specials, onEventSelect: handleUpcomingEventSelect, isLoading: isLoadingSpecials, showSavingsAmount: config.specialsSettings?.showSavingsAmount ?? true, showSavingsPercent: config.specialsSettings?.showSavingsPercent ?? false, emptyStateText: config.specialsSettings?.emptyStateText }), shouldRenderBookingForm && eventDetails && (u$2(BookingForm, { config: config, eventDetails: eventDetails, stripePromise: stripePromise, onSuccess: handleBookingSuccess, onError: handleBookingError, onBackToEventInstances: () => {
16539
+ setCurrentStep("eventTypes");
16540
+ setShowingPreview(true);
16541
+ setEventDetails(null);
16542
+ }, onBackToEventTypes: () => {
16543
+ setCurrentStep("eventTypes");
16544
+ setShowingPreview(true);
16545
+ setEventDetails(null);
16546
+ }, selectedEventType: selectedEventType, selectedEventInstance: selectedEventInstance, isOpen: currentStep === "booking" && !!eventDetails, onClose: () => {
16547
+ setCurrentStep("eventTypes");
16548
+ setShowingPreview(true);
16549
+ setEventDetails(null);
16550
+ }, systemConfig: systemConfig, selectedUpsells: selectedUpsells, upsells: upsells })), u$2(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
16551
+ setIsSuccess(false);
16552
+ setCurrentStep("eventTypes");
16553
+ setShowingPreview(true);
16554
+ setSuccessPaymentId(null);
16555
+ setShouldRenderInstanceSelection(false);
16556
+ setShouldRenderUpsells(false);
16557
+ setShouldRenderBookingForm(false);
16558
+ setSelectedUpsells([]);
16559
+ setUpsells([]);
16560
+ }, config: config, onError: setError, paymentIntentId: successPaymentId })] }), showPromoDialog && config.promo && (u$2(PromoDialog, { config: config.promo, onClose: handlePromoDialogClose, onCtaClick: handlePromoCtaClick }))] }));
16561
+ }
16031
16562
  if (viewMode === "next-events" && !showingPreview && currentStep === "eventInstances") {
16032
16563
  return (u$2(StyleProvider, { config: config, children: [u$2("div", { ref: setWidgetContainerRef, children: [shouldRenderInstanceSelection && (u$2(EventInstanceSelection, { eventInstances: eventInstances, selectedEventType: selectedEventType, onEventInstanceSelect: handleEventInstanceSelect, onBackToEventTypes: () => {
16033
16564
  setShowingPreview(true);
@@ -16098,7 +16629,7 @@
16098
16629
  }, config: config, onError: setError, paymentIntentId: successPaymentId })] }), showPromoDialog && config.promo && (u$2(PromoDialog, { config: config.promo, onClose: handlePromoDialogClose, onCtaClick: handlePromoCtaClick }))] }));
16099
16630
  }
16100
16631
  // Cards mode (default) - show event type selection with optional voucher card
16101
- const cardsView = (u$2(k$3, { children: [hasEventSelection && (u$2(EventTypeSelection, { eventTypes: eventTypes, onEventTypeSelect: handleEventTypeSelect, isLoading: isLoading, skeletonCount: getSkeletonCount(), showVoucherAttachment: Boolean(voucherConfig?.enabled && voucherCardIntegrationEnabled && !isStandaloneVoucherMode), onVoucherClick: handleVoucherAttachmentClick })), isStandaloneVoucherMode && (u$2(VoucherIntegration, { config: config, voucherConfig: voucherConfig, eventTypes: voucherEventTypes, systemConfig: systemConfig, isFormOpen: false, isLoadingConfig: isLoadingVoucherConfig, preselectedEventTypeId: null, voucherPurchaseResult: null, isSuccess: false, showStandaloneCard: Boolean(voucherConfig?.enabled && voucherCardIntegrationEnabled), onCardClick: handleVoucherCardClick, onFormClose: handleVoucherFormClose, onSuccess: handleVoucherSuccess, onError: handleVoucherError, onSuccessModalClose: () => { } })), isStandaloneVoucherMode && isLoading && !voucherConfig && (u$2("div", { style: { padding: "24px", textAlign: "center" }, children: u$2("div", { style: {
16632
+ const cardsView = (u$2(k$3, { children: [hasEventSelection && (u$2(EventTypeSelection, { eventTypes: eventTypes, onEventTypeSelect: handleEventTypeSelect, onInstancePreview: (instanceId, eventTypeId) => void handleUpcomingEventSelect(instanceId, eventTypeId), isLoading: isLoading, skeletonCount: getSkeletonCount(), showVoucherAttachment: Boolean(voucherConfig?.enabled && voucherCardIntegrationEnabled && !isStandaloneVoucherMode), onVoucherClick: handleVoucherAttachmentClick })), isStandaloneVoucherMode && (u$2(VoucherIntegration, { config: config, voucherConfig: voucherConfig, eventTypes: voucherEventTypes, systemConfig: systemConfig, isFormOpen: false, isLoadingConfig: isLoadingVoucherConfig, preselectedEventTypeId: null, voucherPurchaseResult: null, isSuccess: false, showStandaloneCard: Boolean(voucherConfig?.enabled && voucherCardIntegrationEnabled), onCardClick: handleVoucherCardClick, onFormClose: handleVoucherFormClose, onSuccess: handleVoucherSuccess, onError: handleVoucherError, onSuccessModalClose: () => { } })), isStandaloneVoucherMode && isLoading && !voucherConfig && (u$2("div", { style: { padding: "24px", textAlign: "center" }, children: u$2("div", { style: {
16102
16633
  display: "inline-block",
16103
16634
  width: "32px",
16104
16635
  height: "32px",
@@ -16166,11 +16697,7 @@
16166
16697
  function UniversalBookingWidget(props) {
16167
16698
  const [languagePolicy, setLanguagePolicy] = d$1(null);
16168
16699
  const [orgTimezone, setOrgTimezone] = d$1("Europe/Berlin");
16169
- const serverLockedLocale = languagePolicy && !languagePolicy.multiLanguageEnabled
16170
- ? languagePolicy.organizationLocale
16171
- : undefined;
16172
- const i18nConfigLocale = serverLockedLocale !== undefined ? serverLockedLocale : props.config.locale;
16173
- const providerProps = i18nConfigLocale ? { configLocale: i18nConfigLocale } : {};
16700
+ const providerProps = props.config.locale ? { configLocale: props.config.locale } : {};
16174
16701
  const showLanguagePicker = props.config.showLanguagePicker !== false &&
16175
16702
  (languagePolicy === null || languagePolicy.multiLanguageEnabled);
16176
16703
  return (u$2(I18nProvider, { ...providerProps, children: u$2(ShowLanguagePickerProvider, { value: showLanguagePicker, children: u$2(TimezoneProvider, { value: orgTimezone, children: u$2(UniversalBookingWidgetInner, { ...props, onWidgetLanguage: setLanguagePolicy, onTimezone: setOrgTimezone }) }) }) }));
@@ -16208,7 +16735,7 @@
16208
16735
  }
16209
16736
  }
16210
16737
 
16211
- 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%}@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}}";
16738
+ 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}}";
16212
16739
  styleInject(css_248z);
16213
16740
 
16214
16741
  // Export init function for vanilla JS usage with Preact