@bigz-app/booking-widget 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/booking-widget.js +620 -148
  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/events/EventTypeSelection.d.ts +14 -1
  6. package/dist/components/events/EventTypeSelection.d.ts.map +1 -1
  7. package/dist/components/events/SpecialsView.d.ts +13 -0
  8. package/dist/components/events/SpecialsView.d.ts.map +1 -0
  9. package/dist/components/events/index.d.ts +1 -0
  10. package/dist/components/events/index.d.ts.map +1 -1
  11. package/dist/components/shared/Button.d.ts.map +1 -1
  12. package/dist/components/shared/DialogPortal.d.ts.map +1 -1
  13. package/dist/components/upsells/UpsellCard.d.ts +2 -0
  14. package/dist/components/upsells/UpsellCard.d.ts.map +1 -1
  15. package/dist/components/upsells/UpsellsStep.d.ts +2 -0
  16. package/dist/components/upsells/UpsellsStep.d.ts.map +1 -1
  17. package/dist/components/voucher/VoucherIntegration.d.ts +23 -0
  18. package/dist/components/voucher/VoucherIntegration.d.ts.map +1 -0
  19. package/dist/components/voucher/index.d.ts +2 -0
  20. package/dist/components/voucher/index.d.ts.map +1 -1
  21. package/dist/components/voucher/useVoucherConfig.d.ts +57 -0
  22. package/dist/components/voucher/useVoucherConfig.d.ts.map +1 -0
  23. package/dist/i18n/locales/de.d.ts.map +1 -1
  24. package/dist/i18n/locales/en.d.ts.map +1 -1
  25. package/dist/i18n/locales/es.d.ts.map +1 -1
  26. package/dist/i18n/locales/pt.d.ts.map +1 -1
  27. package/dist/i18n/locales/sv.d.ts.map +1 -1
  28. package/dist/index.cjs +620 -148
  29. package/dist/index.cjs.map +1 -1
  30. package/dist/index.esm.js +620 -148
  31. package/dist/index.esm.js.map +1 -1
  32. package/dist/styles/StyleProvider.d.ts.map +1 -1
  33. package/dist/styles/shared-styles.d.ts +1 -0
  34. package/dist/styles/shared-styles.d.ts.map +1 -1
  35. package/package.json +1 -1
package/dist/index.esm.js CHANGED
@@ -233,6 +233,7 @@ const de$1 = {
233
233
  "events.soldOut": "Ausgebucht",
234
234
  "events.availableFrom": "Freie Plätze ab {{date}}",
235
235
  "events.noAvailableDates": "Keine Termine frei",
236
+ "events.previewSectionTitle": "Specials & nächste Termine",
236
237
  // Event instances
237
238
  "instances.title": "Terminauswahl",
238
239
  "instances.noAvailable": "Keine verfügbaren Termine",
@@ -249,6 +250,15 @@ const de$1 = {
249
250
  "nextEvents.noUpcomingMessage": "Aktuell sind keine Termine verfügbar. Bitte schaue später noch einmal vorbei oder kontaktiere uns direkt.",
250
251
  "nextEvents.showAll": "Alle Events anzeigen",
251
252
  "nextEvents.priceOnRequest": "Preis auf Anfrage",
253
+ // Specials view
254
+ "specials.title": "Sonderangebote",
255
+ "specials.subtitle": "Unsere aktuellen Angebote auf einen Blick",
256
+ "specials.noSpecials": "Keine Sonderangebote",
257
+ "specials.noSpecialsMessage": "Derzeit sind keine Sonderangebote verfügbar. Bitte schaue später noch einmal vorbei.",
258
+ "specials.save": "Spare {{amount}}",
259
+ "specials.savePercent": "{{percent}}% Rabatt",
260
+ "specials.spotsLeft": "Noch {{count}} Plätze frei",
261
+ "specials.bookNow": "Jetzt buchen",
252
262
  // Booking form
253
263
  "booking.title": "Buchung - {{name}}",
254
264
  "booking.notPossible": "Buchung nicht möglich",
@@ -493,6 +503,7 @@ const en = {
493
503
  "events.soldOut": "Sold out",
494
504
  "events.availableFrom": "Available from {{date}}",
495
505
  "events.noAvailableDates": "No dates available",
506
+ "events.previewSectionTitle": "Specials & upcoming dates",
496
507
  // Event instances
497
508
  "instances.title": "Select a date",
498
509
  "instances.noAvailable": "No available dates",
@@ -509,6 +520,15 @@ const en = {
509
520
  "nextEvents.noUpcomingMessage": "There are currently no dates available. Please check back later or contact us directly.",
510
521
  "nextEvents.showAll": "Show all events",
511
522
  "nextEvents.priceOnRequest": "Price on request",
523
+ // Specials view
524
+ "specials.title": "Special Offers",
525
+ "specials.subtitle": "Our current deals at a glance",
526
+ "specials.noSpecials": "No special offers",
527
+ "specials.noSpecialsMessage": "There are currently no special offers available. Please check back later.",
528
+ "specials.save": "Save {{amount}}",
529
+ "specials.savePercent": "{{percent}}% off",
530
+ "specials.spotsLeft": "{{count}} spots left",
531
+ "specials.bookNow": "Book now",
512
532
  // Booking form
513
533
  "booking.title": "Booking - {{name}}",
514
534
  "booking.notPossible": "Booking not possible",
@@ -753,6 +773,7 @@ const es = {
753
773
  "events.soldOut": "Agotado",
754
774
  "events.availableFrom": "Disponible desde {{date}}",
755
775
  "events.noAvailableDates": "Sin fechas disponibles",
776
+ "events.previewSectionTitle": "Especiales & próximas fechas",
756
777
  // Event instances
757
778
  "instances.title": "Seleccionar fecha",
758
779
  "instances.noAvailable": "Sin fechas disponibles",
@@ -769,6 +790,15 @@ const es = {
769
790
  "nextEvents.noUpcomingMessage": "Actualmente no hay fechas disponibles. Por favor, vuelve más tarde o contáctanos directamente.",
770
791
  "nextEvents.showAll": "Mostrar todos los eventos",
771
792
  "nextEvents.priceOnRequest": "Precio bajo consulta",
793
+ // Specials view
794
+ "specials.title": "Ofertas especiales",
795
+ "specials.subtitle": "Nuestras ofertas actuales de un vistazo",
796
+ "specials.noSpecials": "Sin ofertas especiales",
797
+ "specials.noSpecialsMessage": "Actualmente no hay ofertas especiales disponibles. Vuelve más tarde.",
798
+ "specials.save": "Ahorra {{amount}}",
799
+ "specials.savePercent": "{{percent}}% de descuento",
800
+ "specials.spotsLeft": "{{count}} plazas disponibles",
801
+ "specials.bookNow": "Reservar ahora",
772
802
  // Booking form
773
803
  "booking.title": "Reserva - {{name}}",
774
804
  "booking.notPossible": "Reserva no posible",
@@ -1013,6 +1043,7 @@ const pt = {
1013
1043
  "events.soldOut": "Esgotado",
1014
1044
  "events.availableFrom": "Disponível a partir de {{date}}",
1015
1045
  "events.noAvailableDates": "Sem datas disponíveis",
1046
+ "events.previewSectionTitle": "Especiais & próximas datas",
1016
1047
  // Event instances
1017
1048
  "instances.title": "Selecionar data",
1018
1049
  "instances.noAvailable": "Sem datas disponíveis",
@@ -1029,6 +1060,15 @@ const pt = {
1029
1060
  "nextEvents.noUpcomingMessage": "Atualmente não há datas disponíveis. Por favor, volte mais tarde ou contacte-nos diretamente.",
1030
1061
  "nextEvents.showAll": "Mostrar todos os eventos",
1031
1062
  "nextEvents.priceOnRequest": "Preço sob consulta",
1063
+ // Specials view
1064
+ "specials.title": "Ofertas especiais",
1065
+ "specials.subtitle": "As nossas ofertas atuais num relance",
1066
+ "specials.noSpecials": "Sem ofertas especiais",
1067
+ "specials.noSpecialsMessage": "Atualmente não há ofertas especiais disponíveis. Por favor, volte mais tarde.",
1068
+ "specials.save": "Poupe {{amount}}",
1069
+ "specials.savePercent": "{{percent}}% de desconto",
1070
+ "specials.spotsLeft": "{{count}} lugares disponíveis",
1071
+ "specials.bookNow": "Reservar agora",
1032
1072
  // Booking form
1033
1073
  "booking.title": "Reserva - {{name}}",
1034
1074
  "booking.notPossible": "Reserva não possível",
@@ -1273,6 +1313,7 @@ const sv = {
1273
1313
  "events.soldOut": "Fullbokat",
1274
1314
  "events.availableFrom": "Lediga platser från {{date}}",
1275
1315
  "events.noAvailableDates": "Inga datum lediga",
1316
+ "events.previewSectionTitle": "Specials & kommande datum",
1276
1317
  // Event instances
1277
1318
  "instances.title": "Välj datum",
1278
1319
  "instances.noAvailable": "Inga tillgängliga datum",
@@ -1289,6 +1330,15 @@ const sv = {
1289
1330
  "nextEvents.noUpcomingMessage": "Det finns för närvarande inga datum tillgängliga. Kom tillbaka senare eller kontakta oss direkt.",
1290
1331
  "nextEvents.showAll": "Visa alla evenemang",
1291
1332
  "nextEvents.priceOnRequest": "Pris på förfrågan",
1333
+ // Specials view
1334
+ "specials.title": "Specialerbjudanden",
1335
+ "specials.subtitle": "Våra aktuella erbjudanden i korthet",
1336
+ "specials.noSpecials": "Inga specialerbjudanden",
1337
+ "specials.noSpecialsMessage": "Det finns för närvarande inga specialerbjudanden tillgängliga. Kom tillbaka senare.",
1338
+ "specials.save": "Spara {{amount}}",
1339
+ "specials.savePercent": "{{percent}}% rabatt",
1340
+ "specials.spotsLeft": "{{count}} platser kvar",
1341
+ "specials.bookNow": "Boka nu",
1292
1342
  // Booking form
1293
1343
  "booking.title": "Bokning - {{name}}",
1294
1344
  "booking.notPossible": "Bokning inte möjlig",
@@ -1694,129 +1744,143 @@ const resolveSemanticColor = (colorValue, fallbackValue) => {
1694
1744
  // If semantic resolution fails, use fallback or return the original value
1695
1745
  return fallbackValue || colorValue;
1696
1746
  };
1697
- // Predefined themes (modern, accessibility-tested)
1747
+ // Predefined themes
1698
1748
  const themes = {
1699
1749
  // --- Light Themes ---
1700
- "light-fresh": {
1701
- highlight: "#00b1aa", // accent-strong
1702
- background: "#f8fdfe", // neutral-strong (background)
1703
- surface: "#ffffff", // card (pure white)
1704
- text: "#0e7490", // Turquoise 800
1705
- border: "#bae6fd", // Blue 200
1706
- success: "#38bdf8", // Blue 400
1707
- warning: "#fbbf24", // Amber 400
1708
- error: "#f43f5e", // Rose 500
1709
- borderRadius: "18px", // Very rounded corners
1750
+ "teal-minimal": {
1751
+ highlight: "#00b1aa",
1752
+ background: "#f8fdfe",
1753
+ surface: "#ffffff",
1754
+ text: "#0e7490",
1755
+ border: "#bae6fd",
1756
+ success: "#38bdf8",
1757
+ warning: "#fbbf24",
1758
+ error: "#f43f5e",
1759
+ borderRadius: "18px",
1710
1760
  fontFamily: "'Inter', system-ui, sans-serif",
1711
1761
  },
1712
- "light-elegant": {
1713
- highlight: "#8b5cf6", // Violet 500
1714
- background: "#f5f3ff", // Violet 50
1715
- surface: "#ffffff", // White
1716
- text: "#4c1d95", // Violet 900
1717
- border: "#ede9fe", // Violet 100
1718
- success: "#16a34a", // Green 600
1719
- warning: "#ca8a04", // Yellow 600
1720
- error: "#dc2626", // Red 600
1721
- borderRadius: "12px",
1722
- fontFamily: "'Playfair Display', serif",
1762
+ "blue-business": {
1763
+ highlight: "#2563eb",
1764
+ background: "#f8fafc",
1765
+ surface: "#ffffff",
1766
+ text: "#0f172a",
1767
+ border: "#cbd5e1",
1768
+ success: "#059669",
1769
+ warning: "#d97706",
1770
+ error: "#b91c1c",
1771
+ borderRadius: "6px",
1772
+ fontFamily: "'Plus Jakarta Sans', system-ui, sans-serif",
1723
1773
  },
1724
- "light-vibrant": {
1725
- highlight: "#ed702d", // blue-500 - bright blue accent
1726
- background: "#1f2630", // slate-900 - dark background
1727
- surface: "#1f2630", // slate-800 - dark cards
1728
- text: "#f1f5f9", // slate-100 - light text
1729
- border: "#ed702d", // slate-700 - subtle borders
1730
- success: "#22c55e", // green-500
1731
- warning: "#eab308", // yellow-500
1732
- error: "#ef4444", // red-500
1774
+ "orange-raw": {
1775
+ highlight: "#ed702d",
1776
+ background: "#1f2630",
1777
+ surface: "#1f2630",
1778
+ text: "#f1f5f9",
1779
+ border: "#ed702d",
1780
+ success: "#22c55e",
1781
+ warning: "#eab308",
1782
+ error: "#ef4444",
1733
1783
  borderRadius: "0px",
1734
1784
  fontFamily: "Inter, system-ui, sans-serif",
1735
1785
  },
1736
- "light-professional": {
1737
- highlight: "#2563eb", // Blue 600
1738
- background: "#f8fafc", // Slate 50
1739
- surface: "#ffffff", // White
1740
- text: "#1e293b", // Slate 800
1741
- border: "#e2e8f0", // Slate 200
1742
- success: "#059669", // Emerald 600
1743
- warning: "#d97706", // Amber 600
1744
- error: "#b91c1c", // Red 700
1745
- borderRadius: "4px",
1746
- fontFamily: "system-ui, -apple-system, sans-serif",
1747
- },
1748
- "dark-night": {
1749
- highlight: "#3b82f6", // blue-500 - bright blue accent
1750
- background: "#0f172a", // slate-900 - dark background
1751
- surface: "#1e293b", // slate-800 - dark cards
1752
- text: "#f1f5f9", // slate-100 - light text
1753
- border: "#334155", // slate-700 - subtle borders
1754
- success: "#22c55e", // green-500
1755
- warning: "#eab308", // yellow-500
1756
- error: "#ef4444", // red-500
1757
- borderRadius: "8px",
1758
- fontFamily: "Inter, system-ui, sans-serif",
1759
- },
1760
- "dark-modern": {
1761
- highlight: "#3b82f6", // blue-500 - bright blue accent
1762
- background: "#0f172a", // slate-900 - dark background
1763
- surface: "#1e293b", // slate-800 - dark cards
1764
- text: "#f1f5f9", // slate-100 - light text
1765
- border: "#334155", // slate-700 - subtle borders
1766
- success: "#22c55e", // green-500
1767
- warning: "#eab308", // yellow-500
1768
- error: "#ef4444", // red-500
1769
- borderRadius: "8px",
1770
- fontFamily: "Inter, system-ui, sans-serif",
1786
+ // --- Dark Themes ---
1787
+ "navy-night": {
1788
+ highlight: "#60a5fa",
1789
+ background: "#0b1120",
1790
+ surface: "#111827",
1791
+ text: "#e2e8f0",
1792
+ border: "#1e3a5f",
1793
+ success: "#34d399",
1794
+ warning: "#fbbf24",
1795
+ error: "#f87171",
1796
+ borderRadius: "10px",
1797
+ fontFamily: "'Outfit', system-ui, sans-serif",
1771
1798
  },
1772
- "dark-forest": {
1773
- highlight: "#34d399", // Emerald 400
1774
- background: "#05140d",
1775
- surface: "#062215",
1776
- text: "#d1fae5", // Emerald 100
1777
- border: "#043322",
1778
- success: "#4ade80", // Green 400
1779
- warning: "#facc15", // Yellow 400
1780
- error: "#f87171", // Red 400
1799
+ "green-deep": {
1800
+ highlight: "#34d399",
1801
+ background: "#030d07",
1802
+ surface: "#051a0e",
1803
+ text: "#d1fae5",
1804
+ border: "#064e20",
1805
+ success: "#4ade80",
1806
+ warning: "#facc15",
1807
+ error: "#f87171",
1781
1808
  borderRadius: "12px",
1782
- fontFamily: "system-ui, -apple-system, sans-serif",
1809
+ fontFamily: "'Instrument Sans', system-ui, sans-serif",
1783
1810
  },
1784
- "dark-matrix": {
1785
- highlight: "#33ff33",
1811
+ "green-matrix": {
1812
+ highlight: "#39ff14",
1786
1813
  background: "#000000",
1787
- surface: "#0a0a0a",
1788
- text: "#00ff00",
1789
- border: "#1a1a1a",
1790
- success: "#33ff33",
1814
+ surface: "#060f06",
1815
+ text: "#00ff41",
1816
+ border: "#0d2b0d",
1817
+ success: "#39ff14",
1791
1818
  warning: "#ffff00",
1792
1819
  error: "#ff3333",
1793
1820
  borderRadius: "0px",
1794
- fontFamily: "'Courier New', monospace",
1821
+ fontFamily: "'Share Tech Mono', monospace",
1795
1822
  },
1796
- "dark-luxury": {
1797
- highlight: "#fde047", // Yellow 300
1798
- background: "#1c1917", // Stone 900
1799
- surface: "#292524", // Stone 800
1800
- text: "#fafaf9", // Stone 50
1801
- border: "#44403c", // Stone 700
1802
- success: "#a3e635", // Lime 400
1803
- warning: "#f59e0b", // Amber 500
1804
- error: "#fca5a5", // Red 300
1823
+ "gold-luxury": {
1824
+ highlight: "#fde047",
1825
+ background: "#1c1917",
1826
+ surface: "#292524",
1827
+ text: "#fafaf9",
1828
+ border: "#44403c",
1829
+ success: "#a3e635",
1830
+ warning: "#f59e0b",
1831
+ error: "#fca5a5",
1805
1832
  borderRadius: "24px",
1806
- fontFamily: "'Cinzel', serif",
1833
+ fontFamily: "'Bodoni Moda', serif",
1807
1834
  },
1808
- "dark-energetic": {
1809
- highlight: "#d946ef", // Fuchsia 500
1810
- background: "#2e1046",
1811
- surface: "#401561",
1812
- text: "#f3e8ff", // Purple 50
1813
- border: "#581c87", // Purple 900
1814
- success: "#4ade80", // Green 400
1815
- warning: "#facc15", // Yellow 400
1816
- error: "#f87171", // Red 400
1835
+ "purple-electric": {
1836
+ highlight: "#d946ef",
1837
+ background: "#110820",
1838
+ surface: "#1a0d30",
1839
+ text: "#f3e8ff",
1840
+ border: "#3b0764",
1841
+ success: "#4ade80",
1842
+ warning: "#facc15",
1843
+ error: "#f87171",
1817
1844
  borderRadius: "14px",
1818
1845
  fontFamily: "'Geologica', sans-serif",
1819
1846
  },
1847
+ "dark-neon-brutalism": {
1848
+ highlight: "#00e5cc",
1849
+ background: "#0e1420",
1850
+ surface: "#0e1420",
1851
+ text: "#e8eef5",
1852
+ border: "#00e5cc",
1853
+ success: "#00e5cc",
1854
+ warning: "#ffe45e",
1855
+ error: "#ff4d6d",
1856
+ borderRadius: "4px",
1857
+ fontFamily: "'JetBrains Mono', monospace",
1858
+ buttonTextColor: "#0e1420",
1859
+ },
1860
+ "rose-editorial": {
1861
+ highlight: "#be3455",
1862
+ background: "#fdf8f5",
1863
+ surface: "#ffffff",
1864
+ text: "#1a0a0f",
1865
+ border: "#f2d5db",
1866
+ success: "#2d6a4f",
1867
+ warning: "#b5451b",
1868
+ error: "#9b1d20",
1869
+ borderRadius: "0px",
1870
+ fontFamily: "'Cormorant', serif",
1871
+ },
1872
+ "amber-retro": {
1873
+ highlight: "#f59e0b",
1874
+ background: "#1a1008",
1875
+ surface: "#241a0a",
1876
+ text: "#fef3c7",
1877
+ border: "#78350f",
1878
+ success: "#84cc16",
1879
+ warning: "#f59e0b",
1880
+ error: "#ef4444",
1881
+ borderRadius: "6px",
1882
+ fontFamily: "'Syne', sans-serif",
1883
+ },
1820
1884
  };
1821
1885
  const StyleProvider = ({ config, children, }) => {
1822
1886
  // Track hydration state to prevent mismatches
@@ -1826,8 +1890,8 @@ const StyleProvider = ({ config, children, }) => {
1826
1890
  }, []);
1827
1891
  // PERFORMANCE OPTIMIZATION: Memoize style calculations
1828
1892
  const themedStyles = useMemo(() => {
1829
- const themeName = config.theme || "light-fresh";
1830
- const themeDefaults = themes[themeName] || themes["light-fresh"];
1893
+ const themeName = config.theme || "teal-minimal";
1894
+ const themeDefaults = themes[themeName] || themes["teal-minimal"];
1831
1895
  const getCSSValue = (value, fallback) => {
1832
1896
  if (!value)
1833
1897
  return fallback;
@@ -1897,6 +1961,7 @@ const StyleProvider = ({ config, children, }) => {
1897
1961
  "--bw-surface-color": finalColors.surface,
1898
1962
  "--bw-text-color": finalColors.text,
1899
1963
  "--bw-text-muted": addOpacity(finalColors.text, 0.7),
1964
+ "--bw-button-text-color": themeDefaults.buttonTextColor || "#ffffff",
1900
1965
  "--bw-border-color": finalColors.border,
1901
1966
  "--bw-success-color": finalColors.success,
1902
1967
  "--bw-warning-color": finalColors.warning,
@@ -1914,7 +1979,7 @@ const StyleProvider = ({ config, children, }) => {
1914
1979
  "--bw-highlight-muted": addOpacity(finalColors.highlight, 0.1),
1915
1980
  "--bw-highlight-subtle": addOpacity(finalColors.highlight, 0.05),
1916
1981
  "--bw-text-subtle": addOpacity(finalColors.text, 0.4),
1917
- colorScheme: themeName.startsWith("dark-") || themeName === "dark-night" ? "dark" : "light",
1982
+ 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",
1918
1983
  };
1919
1984
  }, [
1920
1985
  config.theme,
@@ -4468,6 +4533,7 @@ function DialogWrapper({ isOpen, onClose, children, maxWidth = "700px", classNam
4468
4533
  "--bw-font-family": computedStyles.getPropertyValue("--bw-font-family").trim() || "system-ui, sans-serif",
4469
4534
  "--bw-shadow-md": computedStyles.getPropertyValue("--bw-shadow-md").trim() ||
4470
4535
  "0 4px 6px -1px rgba(0, 0, 0, 0.1)",
4536
+ "--bw-button-text-color": computedStyles.getPropertyValue("--bw-button-text-color").trim() || "#ffffff",
4471
4537
  };
4472
4538
  setFallbackStyles(fallbacks);
4473
4539
  }
@@ -11177,11 +11243,13 @@ const buttonBase = {
11177
11243
  whiteSpace: "nowrap",
11178
11244
  border: "none",
11179
11245
  };
11246
+ // CSS class name for button hover effects
11247
+ const buttonClassName = "bw-button-hover";
11180
11248
  const buttonStyles = {
11181
11249
  primary: {
11182
11250
  ...buttonBase,
11183
11251
  backgroundColor: "var(--bw-highlight-color)",
11184
- color: "#ffffff",
11252
+ color: "var(--bw-button-text-color, #ffffff)",
11185
11253
  border: "none",
11186
11254
  },
11187
11255
  secondary: {
@@ -12709,8 +12777,8 @@ function VoucherPurchaseCard({ config, minEventPrice, fallbackImages = [], onCli
12709
12777
  const displayPrice = !config.allowMonetaryVouchers && minEventPrice && minEventPrice > 0
12710
12778
  ? minEventPrice
12711
12779
  : minEventPrice && minEventPrice > 0
12712
- ? Math.min(config.monetaryPresets[0] || 2500, minEventPrice)
12713
- : config.monetaryPresets[0] || 2500;
12780
+ ? Math.min(config.monetaryPresets[0] || 2000, minEventPrice)
12781
+ : config.monetaryPresets[0] || 2000;
12714
12782
  const slideshowImages = useMemo(() => Array.from(new Set(fallbackImages.filter(Boolean))).slice(0, 5), [fallbackImages]);
12715
12783
  const hasSlideshow = !config.image && slideshowImages.length > 0;
12716
12784
  useEffect(() => {
@@ -13364,7 +13432,7 @@ function VoucherPurchaseForm({ config, voucherConfig, eventTypes, isOpen, onClos
13364
13432
  color: "var(--bw-text-muted)",
13365
13433
  fontFamily: "var(--bw-font-family)",
13366
13434
  }, children: [t("voucher.for"), " ", recipientName] }))] })] }), jsx("div", { style: {
13367
- fontSize: "24px",
13435
+ fontSize: "clamp(18px, 4vw, 22px)",
13368
13436
  fontWeight: 700,
13369
13437
  color: "var(--bw-highlight-color)",
13370
13438
  fontFamily: "var(--bw-font-family)",
@@ -13635,6 +13703,20 @@ function VoucherAttachment({ onClick }) {
13635
13703
  }, children: t("voucher.buyAsGift") })] }));
13636
13704
  }
13637
13705
 
13706
+ function VoucherIntegration({ config, voucherConfig, eventTypes, systemConfig, isFormOpen, isLoadingConfig, preselectedEventTypeId, voucherPurchaseResult, isSuccess, showStandaloneCard, onCardClick, onFormClose, onSuccess, onError, onSuccessModalClose, }) {
13707
+ if (!voucherConfig?.enabled) {
13708
+ return null;
13709
+ }
13710
+ return (jsxs(Fragment, { children: [showStandaloneCard && (jsx("div", { style: { padding: "0" }, children: jsx("div", { style: {
13711
+ display: "grid",
13712
+ gridTemplateColumns: "minmax(350px, 500px)",
13713
+ gap: "24px",
13714
+ justifyContent: "center",
13715
+ }, children: jsx(VoucherPurchaseCard, { config: voucherConfig, minEventPrice: eventTypes.length > 0
13716
+ ? Math.min(...eventTypes.map((et) => et.maxPrice))
13717
+ : undefined, fallbackImages: eventTypes.flatMap((eventType) => eventType.images || []), onClick: onCardClick, standalone: true }) }) })), jsx(VoucherPurchaseForm, { config: config, voucherConfig: voucherConfig, eventTypes: eventTypes, isOpen: isFormOpen, onClose: onFormClose, onSuccess: onSuccess, onError: onError, systemConfig: systemConfig, preselectedEventTypeId: preselectedEventTypeId, isLoadingEventTypes: isLoadingConfig }), isSuccess && voucherPurchaseResult && (jsx(VoucherSuccessModal, { isOpen: true, onClose: onSuccessModalClose, result: voucherPurchaseResult }))] }));
13718
+ }
13719
+
13638
13720
  // Helper function to preprocess markdown for underline support
13639
13721
  const preprocessMarkdown = (markdown) => {
13640
13722
  // Convert double underscores to HTML underline tags for React Markdown
@@ -13654,8 +13736,48 @@ function formatDurationInfo(info, t) {
13654
13736
  const rest = formatted.slice(0, -1).join(", ");
13655
13737
  return `${t("duration.optionally")} ${rest} ${t("duration.or")} ${last} ${unitPlural}`;
13656
13738
  }
13657
- function EventTypeSelection({ eventTypes, onEventTypeSelect, isLoading = false, skeletonCount = 4, showVoucherAttachment = false, onVoucherClick, }) {
13739
+ function InfoBadge({ text }) {
13740
+ const [open, setOpen] = useState(false);
13741
+ const ref = useRef(null);
13742
+ return (jsxs("span", { ref: ref, onClick: (e) => { e.stopPropagation(); setOpen((v) => !v); }, style: {
13743
+ position: "relative",
13744
+ display: "inline-flex",
13745
+ alignItems: "center",
13746
+ justifyContent: "center",
13747
+ width: "16px",
13748
+ height: "16px",
13749
+ borderRadius: "50%",
13750
+ border: "1px solid var(--bw-highlight-color)",
13751
+ color: "var(--bw-highlight-color)",
13752
+ fontSize: "9px",
13753
+ fontWeight: 700,
13754
+ cursor: "pointer",
13755
+ flexShrink: 0,
13756
+ userSelect: "none",
13757
+ }, children: ["i", open && (jsx("span", { style: {
13758
+ position: "absolute",
13759
+ bottom: "calc(100% + 6px)",
13760
+ right: 0,
13761
+ backgroundColor: "var(--bw-surface-color)",
13762
+ border: "1px solid var(--bw-border-color)",
13763
+ borderRadius: "var(--bw-border-radius-small)",
13764
+ boxShadow: "var(--bw-shadow-md)",
13765
+ padding: "6px 10px",
13766
+ fontSize: "14px",
13767
+ color: "var(--bw-text-color)",
13768
+ fontWeight: 400,
13769
+ whiteSpace: "normal",
13770
+ width: "160px",
13771
+ lineHeight: 1.4,
13772
+ zIndex: 100,
13773
+ textAlign: "left",
13774
+ pointerEvents: "none",
13775
+ }, children: text }))] }));
13776
+ }
13777
+ function EventTypeSelection({ eventTypes, onEventTypeSelect, onInstancePreview, isLoading = false, skeletonCount = 4, showVoucherAttachment = false, onVoucherClick, }) {
13658
13778
  const t = useTranslations();
13779
+ const { locale } = useLocale();
13780
+ const timezone = useTimezone();
13659
13781
  // State for details dialog
13660
13782
  const [detailsDialogOpen, setDetailsDialogOpen] = useState(false);
13661
13783
  const [selectedEventTypeForDetails, setSelectedEventTypeForDetails] = useState(null);
@@ -13765,7 +13887,7 @@ function EventTypeSelection({ eventTypes, onEventTypeSelect, isLoading = false,
13765
13887
  display: "flex",
13766
13888
  flexDirection: "column",
13767
13889
  justifyContent: "space-between",
13768
- height: "400px",
13890
+ height: "490px",
13769
13891
  }, children: [jsxs("div", { children: [jsx("h2", { className: "event-type-title", style: {
13770
13892
  fontSize: "clamp(1.1rem, 2.5vw, 24px)",
13771
13893
  fontWeight: 700,
@@ -13846,7 +13968,43 @@ function EventTypeSelection({ eventTypes, onEventTypeSelect, isLoading = false,
13846
13968
  color: "var(--bw-text-color)",
13847
13969
  fontFamily: "var(--bw-font-family)",
13848
13970
  textAlign: "right",
13849
- }, children: jsxs("span", { children: [t("common.from"), " ", formatCurrency(eventType.minPrice)] }) })] }), jsxs("div", { style: {
13971
+ }, children: jsxs("span", { children: [t("common.from"), " ", formatCurrency(eventType.minPrice)] }) })] }), (() => {
13972
+ const preview = eventType.cardPreview ?? [];
13973
+ return (jsxs("div", { style: {
13974
+ marginTop: "12px",
13975
+ borderTop: "1px solid var(--bw-border-color)",
13976
+ paddingTop: "8px",
13977
+ marginBottom: "16px",
13978
+ }, children: [jsx("div", { style: {
13979
+ fontSize: "11px",
13980
+ fontWeight: 700,
13981
+ color: "var(--bw-text-muted)",
13982
+ textTransform: "uppercase",
13983
+ letterSpacing: "0.05em",
13984
+ marginBottom: "4px",
13985
+ }, children: t("events.previewSectionTitle") }), jsx("div", { style: { height: "102px" }, children: Array.from({ length: 3 }).map((_, i) => {
13986
+ const item = preview[i];
13987
+ if (!item)
13988
+ return jsx("div", { style: { height: "34px" } }, i);
13989
+ const hasDiscount = item.basePrice > 0 && item.price < item.basePrice;
13990
+ return (jsxs("div", { onClick: (e) => { e.stopPropagation(); onInstancePreview?.(item.id, eventType.id); }, onMouseEnter: (e) => { if (onInstancePreview)
13991
+ e.currentTarget.style.backgroundColor = "var(--bw-border-color)"; }, onMouseLeave: (e) => { e.currentTarget.style.backgroundColor = "transparent"; }, style: {
13992
+ height: "34px",
13993
+ display: "grid",
13994
+ gridTemplateColumns: "auto 1fr auto 20px",
13995
+ alignItems: "center",
13996
+ gap: "8px",
13997
+ width: "100%",
13998
+ fontSize: "13px",
13999
+ color: "var(--bw-text-muted)",
14000
+ cursor: onInstancePreview ? "pointer" : "default",
14001
+ borderRadius: "4px",
14002
+ padding: "0 2px",
14003
+ transition: "background 0.15s",
14004
+ boxSizing: "border-box",
14005
+ }, children: [jsxs("span", { style: { whiteSpace: "nowrap", display: "flex", alignItems: "center", gap: "3px" }, children: [item.isSpecial && (jsx("span", { style: { color: "var(--bw-highlight-color)", fontSize: "11px", lineHeight: 1 }, children: "\u2605" })), formatWeekday(item.startTime, timezone, locale), " ", formatDate(item.startTime, timezone, locale)] }), jsx("span", { style: { overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", opacity: 0.75 }, children: item.name }), jsxs("span", { style: { display: "flex", alignItems: "center", gap: "4px", whiteSpace: "nowrap" }, children: [hasDiscount && (jsx("span", { style: { textDecoration: "line-through", opacity: 0.55, fontSize: "11px" }, children: formatCurrency(item.basePrice) })), jsx("span", { style: { fontWeight: 700, color: item.isSpecial ? "var(--bw-highlight-color)" : "var(--bw-text-color)" }, children: formatCurrency(item.price) })] }), item.specialDescription ? (jsx(InfoBadge, { text: item.specialDescription })) : (jsx("span", {}))] }, item.id));
14006
+ }) })] }));
14007
+ })(), jsxs("div", { style: {
13850
14008
  display: "flex",
13851
14009
  justifyContent: "flex-end",
13852
14010
  alignItems: "center",
@@ -13861,7 +14019,6 @@ function EventTypeSelection({ eventTypes, onEventTypeSelect, isLoading = false,
13861
14019
  backgroundColor: "var(--bw-surface-color)",
13862
14020
  padding: "12px",
13863
14021
  borderRadius: "var(--bw-border-radius)",
13864
- fontSize: "clamp(0.8rem, 2vw, 16px)",
13865
14022
  fontWeight: 600,
13866
14023
  fontFamily: "var(--bw-font-family)",
13867
14024
  display: "flex",
@@ -13874,7 +14031,7 @@ function EventTypeSelection({ eventTypes, onEventTypeSelect, isLoading = false,
13874
14031
  }, children: t("button.moreDetails") })), isAvailable && (jsxs("div", { style: {
13875
14032
  backgroundColor: "var(--bw-highlight-color)",
13876
14033
  color: "var(--bw-surface-color)",
13877
- padding: "12px 24px",
14034
+ padding: "12px 14px",
13878
14035
  borderRadius: "var(--bw-border-radius)",
13879
14036
  fontSize: "clamp(1rem, 2vw, 16px)",
13880
14037
  fontWeight: 600,
@@ -14532,6 +14689,172 @@ function NextEventsPreview({ events, onEventSelect, onShowAll, showAllButtonText
14532
14689
  }, children: "\u27F3" }), t("common.loading")] })) : (showAllButtonText) }) }))] }));
14533
14690
  }
14534
14691
 
14692
+ function SpecialsView({ specials, onEventSelect, isLoading = false, showSavingsAmount = true, showSavingsPercent = false, emptyStateText, isLoadingEventDetails = false, }) {
14693
+ const t = useTranslations();
14694
+ const { locale } = useLocale();
14695
+ const timezone = useTimezone();
14696
+ const [selectedId, setSelectedId] = useState(null);
14697
+ const handleSelect = (id) => {
14698
+ setSelectedId(id);
14699
+ onEventSelect(id);
14700
+ };
14701
+ if (isLoading) {
14702
+ return jsx(NextEventsSkeleton, { count: 3 });
14703
+ }
14704
+ if (specials.length === 0) {
14705
+ return (jsx("div", { style: { maxWidth: "500px", margin: "0 auto", padding: "16px" }, children: jsxs("div", { style: {
14706
+ display: "flex",
14707
+ flexDirection: "column",
14708
+ alignItems: "center",
14709
+ justifyContent: "center",
14710
+ textAlign: "center",
14711
+ backgroundColor: "var(--bw-surface-color)",
14712
+ border: "1px solid var(--bw-border-color)",
14713
+ borderRadius: "var(--bw-border-radius)",
14714
+ padding: "24px",
14715
+ fontFamily: "var(--bw-font-family)",
14716
+ minHeight: "300px",
14717
+ }, children: [jsx("div", { style: {
14718
+ display: "flex",
14719
+ alignItems: "center",
14720
+ justifyContent: "center",
14721
+ borderRadius: "50%",
14722
+ width: "64px",
14723
+ height: "64px",
14724
+ backgroundColor: "var(--bw-highlight-color)",
14725
+ marginBottom: "16px",
14726
+ fontSize: "32px",
14727
+ color: "#ffffff",
14728
+ opacity: 0.8,
14729
+ }, children: "\uD83C\uDFF7\uFE0F" }), jsx("h3", { style: {
14730
+ fontWeight: 600,
14731
+ margin: "0 0 8px 0",
14732
+ fontSize: "20px",
14733
+ color: "var(--bw-text-color)",
14734
+ fontFamily: "var(--bw-font-family)",
14735
+ }, children: t("specials.noSpecials") }), jsx("p", { style: {
14736
+ margin: 0,
14737
+ color: "var(--bw-text-muted)",
14738
+ fontSize: "16px",
14739
+ lineHeight: 1.6,
14740
+ fontFamily: "var(--bw-font-family)",
14741
+ maxWidth: "400px",
14742
+ }, children: emptyStateText ?? t("specials.noSpecialsMessage") })] }) }));
14743
+ }
14744
+ return (jsxs("div", { style: {
14745
+ maxWidth: "500px",
14746
+ margin: "0 auto",
14747
+ padding: "16px",
14748
+ fontFamily: "var(--bw-font-family)",
14749
+ }, children: [jsxs("div", { style: { textAlign: "center", marginBottom: "24px" }, children: [jsx("h2", { style: {
14750
+ fontWeight: 600,
14751
+ margin: "0 0 8px 0",
14752
+ fontSize: "18px",
14753
+ color: "var(--bw-text-color)",
14754
+ fontFamily: "var(--bw-font-family)",
14755
+ }, children: t("specials.title") }), jsx("p", { style: {
14756
+ margin: 0,
14757
+ fontSize: "16px",
14758
+ color: "var(--bw-text-muted)",
14759
+ fontFamily: "var(--bw-font-family)",
14760
+ }, children: t("specials.subtitle") })] }), jsx("div", { style: { display: "flex", flexDirection: "column", gap: "12px" }, children: specials.map((special) => {
14761
+ const isFullyBooked = special.availableSpots === 0;
14762
+ const isDisabled = isFullyBooked || !special.bookingOpen;
14763
+ const hasDiscount = special.basePrice > 0 && special.price < special.basePrice;
14764
+ return (jsxs("div", { style: {
14765
+ position: "relative",
14766
+ backgroundColor: "var(--bw-surface-color)",
14767
+ borderRadius: "var(--bw-border-radius)",
14768
+ border: "1px solid var(--bw-highlight-color)",
14769
+ overflow: "hidden",
14770
+ opacity: isDisabled ? 0.5 : 1,
14771
+ cursor: isDisabled ? "not-allowed" : "pointer",
14772
+ transition: "all 0.2s ease",
14773
+ }, onClick: () => {
14774
+ if (!isDisabled)
14775
+ handleSelect(special.id);
14776
+ }, children: [selectedId === special.id && isLoadingEventDetails && (jsx("div", { style: {
14777
+ position: "absolute",
14778
+ inset: 0,
14779
+ display: "flex",
14780
+ alignItems: "center",
14781
+ justifyContent: "center",
14782
+ backgroundColor: "rgba(15, 23, 42, 0.8)",
14783
+ borderRadius: "var(--bw-border-radius)",
14784
+ zIndex: 10,
14785
+ }, children: jsx("div", { style: { fontSize: "32px", color: "var(--bw-highlight-color)", animation: "spin 1s linear infinite" }, children: "\u27F3" }) })), jsxs("div", { style: { display: "flex", gap: "12px", padding: "12px" }, children: [special.images.length > 0 && (jsx("div", { style: {
14786
+ flexShrink: 0,
14787
+ width: "72px",
14788
+ height: "72px",
14789
+ borderRadius: "var(--bw-border-radius-small)",
14790
+ overflow: "hidden",
14791
+ }, children: jsx("img", { src: special.images[0], alt: special.eventTypeName, style: { width: "100%", height: "100%", objectFit: "cover" } }) })), jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [jsx("div", { style: { marginBottom: "4px" }, children: jsx("span", { style: {
14792
+ fontSize: "11px",
14793
+ fontWeight: 600,
14794
+ color: "var(--bw-text-muted)",
14795
+ textTransform: "uppercase",
14796
+ letterSpacing: "0.05em",
14797
+ }, children: special.categoryName }) }), jsx("h4", { style: {
14798
+ margin: "0 0 2px 0",
14799
+ fontSize: "15px",
14800
+ fontWeight: 600,
14801
+ color: "var(--bw-text-color)",
14802
+ lineHeight: 1.3,
14803
+ overflow: "hidden",
14804
+ textOverflow: "ellipsis",
14805
+ whiteSpace: "nowrap",
14806
+ }, children: special.eventTypeName }), jsxs("div", { style: {
14807
+ fontSize: "13px",
14808
+ fontWeight: 500,
14809
+ color: "var(--bw-highlight-color)",
14810
+ marginBottom: "2px",
14811
+ overflow: "hidden",
14812
+ textOverflow: "ellipsis",
14813
+ whiteSpace: "nowrap",
14814
+ }, children: ["\u2605 ", special.name] }), special.specialDescription && (jsx("div", { style: {
14815
+ fontSize: "12px",
14816
+ color: "var(--bw-text-muted)",
14817
+ marginBottom: "6px",
14818
+ lineHeight: 1.4,
14819
+ }, children: special.specialDescription })), jsxs("div", { style: { fontSize: "13px", color: "var(--bw-text-muted)", marginBottom: "8px" }, children: [formatWeekday(special.startTime, timezone, locale), ",", " ", formatDate(special.startTime, timezone, locale)] }), jsxs("div", { style: { display: "flex", alignItems: "center", gap: "8px", flexWrap: "wrap" }, children: [hasDiscount && (jsx("span", { style: {
14820
+ fontSize: "13px",
14821
+ color: "var(--bw-text-muted)",
14822
+ textDecoration: "line-through",
14823
+ }, children: formatCurrency(special.basePrice) })), jsx("span", { style: {
14824
+ fontSize: "16px",
14825
+ fontWeight: 700,
14826
+ color: "var(--bw-highlight-color)",
14827
+ }, children: formatCurrency(special.price) }), hasDiscount && showSavingsAmount && (jsx("span", { style: {
14828
+ fontSize: "12px",
14829
+ fontWeight: 600,
14830
+ color: "#ffffff",
14831
+ backgroundColor: "var(--bw-success-color, #22c55e)",
14832
+ borderRadius: "4px",
14833
+ padding: "2px 6px",
14834
+ }, children: t("specials.save").replace("{{amount}}", formatCurrency(special.savings)) })), hasDiscount && showSavingsPercent && (jsx("span", { style: {
14835
+ fontSize: "12px",
14836
+ fontWeight: 600,
14837
+ color: "#ffffff",
14838
+ backgroundColor: "var(--bw-success-color, #22c55e)",
14839
+ borderRadius: "4px",
14840
+ padding: "2px 6px",
14841
+ }, children: t("specials.savePercent").replace("{{percent}}", String(special.savingsPercent)) }))] })] })] }), jsxs("div", { style: {
14842
+ borderTop: "1px solid var(--bw-border-color)",
14843
+ padding: "8px 12px",
14844
+ display: "flex",
14845
+ justifyContent: "space-between",
14846
+ alignItems: "center",
14847
+ backgroundColor: "var(--bw-background-color)",
14848
+ }, children: [jsx("span", { style: { fontSize: "12px", color: "var(--bw-text-muted)" }, children: isFullyBooked
14849
+ ? t("common.fullyBooked")
14850
+ : t("specials.spotsLeft").replace("{{count}}", String(special.availableSpots)) }), jsxs("span", { style: {
14851
+ fontSize: "13px",
14852
+ fontWeight: 600,
14853
+ color: "var(--bw-highlight-color)",
14854
+ }, children: [t("specials.bookNow"), " \u2192"] })] })] }, special.id));
14855
+ }) })] }));
14856
+ }
14857
+
14535
14858
  const getThemeConfig = (theme = "generic") => {
14536
14859
  switch (theme) {
14537
14860
  case "christmas":
@@ -14791,8 +15114,8 @@ const cardDisabledStyles = {
14791
15114
  };
14792
15115
  const checkboxContainerStyles = {
14793
15116
  position: "absolute",
14794
- top: "12px",
14795
- right: "12px",
15117
+ bottom: "12px",
15118
+ left: "12px",
14796
15119
  zIndex: 1,
14797
15120
  };
14798
15121
  const checkboxInnerStyles = {
@@ -14887,6 +15210,7 @@ const priceContainerStyles = {
14887
15210
  alignItems: "flex-end",
14888
15211
  marginTop: "8px",
14889
15212
  paddingTop: "8px",
15213
+ paddingBottom: "0px",
14890
15214
  borderTop: "1px solid var(--bw-border-color)",
14891
15215
  };
14892
15216
  const pricePerPersonStyles = {
@@ -14978,7 +15302,7 @@ function UpsellsStep({ upsells, selectedUpsells, participantCount, isLoading, is
14978
15302
  };
14979
15303
  const selectedTotal = calculateTotal();
14980
15304
  const selectedCount = selectedUpsells.length;
14981
- const footerContent = (jsxs(Fragment, { children: [jsx("button", { type: "button", onClick: onBack, style: mergeStyles(buttonStyles.secondary, buttonStyles.fullWidth), children: t("common.back") }), jsx("button", { type: "button", onClick: onContinue, style: mergeStyles(buttonStyles.primary, buttonStyles.fullWidth), children: selectedCount === 0 ? t("button.continueWithout") : t("button.continue") })] }));
15305
+ const footerContent = (jsxs(Fragment, { children: [jsx("button", { type: "button", onClick: onBack, style: mergeStyles(buttonStyles.secondary, buttonStyles.fullWidth), className: buttonClassName, children: t("common.back") }), jsx("button", { type: "button", onClick: onContinue, style: mergeStyles(buttonStyles.primary, buttonStyles.fullWidth), className: buttonClassName, children: selectedCount === 0 ? t("button.continueWithout") : t("button.continue") })] }));
14982
15306
  return (jsx(Sidebar, { isOpen: isOpen, onClose: onClose, title: t("upsells.title"), footer: footerContent, children: jsxs("div", { style: { display: "flex", flexDirection: "column", height: "100%", padding: "16px 16px" }, children: [isLoading && (jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", gap: "12px", padding: "40px 20px", ...textStyles.muted }, children: [spinner(), jsx("span", { children: t("upsells.loading") })] })), !isLoading && upsells.length === 0 && (jsx("div", { style: { textAlign: "center", padding: "40px 20px", ...textStyles.muted }, children: jsx("p", { children: t("upsells.noExtras") }) })), !isLoading && upsells.length > 0 && (jsx("div", { style: { display: "flex", flexDirection: "column", gap: "12px", flex: 1, overflowY: "auto", paddingBottom: "16px" }, children: upsells.map((upsell) => (jsx(UpsellCard, { upsell: upsell, isSelected: isSelected(upsell.id), participantCount: participantCount, onSelect: () => selectUpsell(upsell.id) }, upsell.id))) })), selectedCount > 0 && (jsxs("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: [jsx("span", { style: textStyles.muted, children: selectedCount === 1 ? t("upsells.selected", { count: selectedCount }) : t("upsells.selectedPlural", { count: selectedCount }) }), jsxs("span", { style: { fontWeight: 600, color: "var(--bw-highlight-color)", fontFamily: "var(--bw-font-family)" }, children: ["+", formatCurrency(selectedTotal)] })] }))] }) }));
14983
15307
  }
14984
15308
 
@@ -15017,7 +15341,6 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
15017
15341
  // Detect single event type mode and direct instance mode
15018
15342
  const isSingleEventTypeMode = !!config.eventTypeId && !config.categoryId && !config.eventTypeIds;
15019
15343
  const isDirectInstanceMode = !!config.eventInstanceId;
15020
- const isMultiEventListingContext = Boolean(config.categoryId || config.eventTypeIds?.length);
15021
15344
  // Voucher card integration logic:
15022
15345
  // - For standalone voucher mode: always show voucher card (it's the main content)
15023
15346
  // - For event listings (single or multi): show by default unless explicitly disabled
@@ -15027,6 +15350,8 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
15027
15350
  : (config.voucherIntegration ?? (hasEventSelection && !isDirectInstanceMode));
15028
15351
  // Selection flow state
15029
15352
  const [currentStep, setCurrentStep] = useState("eventTypes");
15353
+ // Tracks where to return when closing the booking form
15354
+ const bookingReturnStep = useRef("eventInstances");
15030
15355
  const [eventTypes, setEventTypes] = useState([]);
15031
15356
  const [selectedEventType, setSelectedEventType] = useState(null);
15032
15357
  const [eventInstances, setEventInstances] = useState([]);
@@ -15039,6 +15364,9 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
15039
15364
  // State for upcoming events (next-events view mode)
15040
15365
  const [upcomingEvents, setUpcomingEvents] = useState([]);
15041
15366
  const [showingPreview, setShowingPreview] = useState(true);
15367
+ // State for specials view mode
15368
+ const [specials, setSpecials] = useState([]);
15369
+ const [isLoadingSpecials, setIsLoadingSpecials] = useState(false);
15042
15370
  // New: sidebar open state for single event type mode
15043
15371
  const [sidebarOpen, setSidebarOpen] = useState(false);
15044
15372
  // Booking flow state
@@ -15074,8 +15402,9 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
15074
15402
  // Load voucher config if:
15075
15403
  // - Explicit voucher params are set
15076
15404
  // - OR voucherIntegration is explicitly set (even to true/false)
15077
- // - OR it's a multi-event listing context (may want to show voucher card)
15078
- if (!isVoucherModeRequested && !isMultiEventListingContext)
15405
+ // - OR it's an event listing context (single or multi) that may want to show voucher card
15406
+ const shouldLoadVoucherConfig = isVoucherModeRequested || hasEventSelection;
15407
+ if (!shouldLoadVoucherConfig)
15079
15408
  return;
15080
15409
  setIsLoadingVoucherConfig(true);
15081
15410
  try {
@@ -15128,10 +15457,10 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
15128
15457
  const mergedConfig = {
15129
15458
  enabled: true,
15130
15459
  monetaryPresets: allowMonetaryVouchers
15131
- ? (presetFromValue ?? serverConfig.monetaryPresets ?? [2500, 5000, 10000, 15000, 20000])
15460
+ ? (presetFromValue ?? serverConfig.monetaryPresets ?? [2000, 5000, 10000, 15000, 20000, 25000, 30000, 40000, 50000])
15132
15461
  : [],
15133
- minAmount: serverConfig.minAmount || 1000,
15134
- maxAmount: serverConfig.maxAmount || 100000,
15462
+ minAmount: serverConfig.minAmount || 500,
15463
+ maxAmount: serverConfig.maxAmount || 1000000,
15135
15464
  allowMonetaryVouchers,
15136
15465
  allowEventVouchers,
15137
15466
  title: serverConfig.title,
@@ -15166,8 +15495,8 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
15166
15495
  const initializeWidget = async () => {
15167
15496
  try {
15168
15497
  setIsLoading(true);
15169
- // Load voucher config in parallel if voucher mode is requested or it's a multi-event context
15170
- if (isVoucherModeRequested || isMultiEventListingContext) {
15498
+ // Load voucher config in parallel if voucher mode is requested or there's event selection
15499
+ if (isVoucherModeRequested || hasEventSelection) {
15171
15500
  void loadVoucherConfig();
15172
15501
  }
15173
15502
  // Direct instance selection (old behavior)
@@ -15188,6 +15517,11 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
15188
15517
  await loadUpcomingEvents();
15189
15518
  return;
15190
15519
  }
15520
+ // Specials view mode: load special offers
15521
+ if (viewMode === "specials") {
15522
+ await loadSpecials();
15523
+ return;
15524
+ }
15191
15525
  // Single event type mode: load event type and instances, but don't open sidebar yet
15192
15526
  if (isSingleEventTypeMode) {
15193
15527
  await loadEventTypes();
@@ -15213,7 +15547,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
15213
15547
  }
15214
15548
  };
15215
15549
  void initializeWidget();
15216
- }, [config, isVoucherModeRequested, hasEventSelection, isStandaloneVoucherMode, isMultiEventListingContext]);
15550
+ }, [config, isVoucherModeRequested, hasEventSelection, isStandaloneVoucherMode]);
15217
15551
  // Re-fetch translated content when locale changes (skip initial mount)
15218
15552
  const prevLocaleRef = useRef(locale);
15219
15553
  useEffect(() => {
@@ -15411,6 +15745,45 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
15411
15745
  setError(data.error || t("error.loadUpcomingEvents"));
15412
15746
  }
15413
15747
  };
15748
+ const loadSpecials = async () => {
15749
+ setIsLoadingSpecials(true);
15750
+ const specialsSettings = config.specialsSettings ?? {};
15751
+ const requestBody = {
15752
+ organizationId: config.organizationId,
15753
+ limit: specialsSettings.count ?? 20,
15754
+ };
15755
+ if (config.categoryId) {
15756
+ requestBody.categoryId = config.categoryId;
15757
+ }
15758
+ else if (config.eventTypeIds) {
15759
+ requestBody.eventTypeIds = config.eventTypeIds;
15760
+ }
15761
+ else if (config.eventTypeId) {
15762
+ requestBody.eventTypeId = config.eventTypeId;
15763
+ }
15764
+ try {
15765
+ const response = await fetch(getApiUrl(config.apiBaseUrl, "/booking/specials"), {
15766
+ method: "POST",
15767
+ headers: createApiHeaders(config, locale),
15768
+ body: JSON.stringify(requestBody),
15769
+ });
15770
+ const data = await response.json();
15771
+ if (response.ok) {
15772
+ const wl = extractWidgetLanguagePayload(data);
15773
+ if (wl) {
15774
+ onWidgetLanguage?.(wl);
15775
+ onTimezone?.(wl.timezone);
15776
+ }
15777
+ setSpecials(data.specials || []);
15778
+ }
15779
+ else {
15780
+ setError(data.error || t("error.loadUpcomingEvents"));
15781
+ }
15782
+ }
15783
+ finally {
15784
+ setIsLoadingSpecials(false);
15785
+ }
15786
+ };
15414
15787
  const loadEventInstances = async (eventTypeId) => {
15415
15788
  const response = await fetch(getApiUrl(config.apiBaseUrl, "/booking/event-instances"), {
15416
15789
  method: "POST",
@@ -15595,6 +15968,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
15595
15968
  // Event instance selection handlers
15596
15969
  const handleEventInstanceSelect = async (eventInstance) => {
15597
15970
  setSelectedEventInstance(eventInstance);
15971
+ bookingReturnStep.current = "eventInstances";
15598
15972
  // Set default participant count for upsell calculations
15599
15973
  const defaultParticipantCount = 1;
15600
15974
  setTempParticipantCount(defaultParticipantCount);
@@ -15607,7 +15981,14 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
15607
15981
  if (availableUpsells.length > 0) {
15608
15982
  // Show upsells step
15609
15983
  setUpsells(availableUpsells);
15610
- setSelectedUpsells([]);
15984
+ // Pre-select default-checked upsells
15985
+ const defaultSelections = availableUpsells
15986
+ .filter((upsell) => upsell.defaultChecked && upsell.available)
15987
+ .map((upsell) => ({
15988
+ upsellPackageId: upsell.id,
15989
+ quantity: defaultParticipantCount,
15990
+ }));
15991
+ setSelectedUpsells(defaultSelections);
15611
15992
  setCurrentStep("upsells");
15612
15993
  setIsLoadingUpsells(false);
15613
15994
  return; // Don't proceed to booking yet
@@ -15638,7 +16019,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
15638
16019
  setEventInstances([]);
15639
16020
  };
15640
16021
  const handleBackToEventInstances = () => {
15641
- setCurrentStep("eventInstances");
16022
+ setCurrentStep(bookingReturnStep.current);
15642
16023
  setSelectedEventInstance(null);
15643
16024
  setEventDetails(null);
15644
16025
  };
@@ -15672,8 +16053,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
15672
16053
  }
15673
16054
  };
15674
16055
  const handleUpsellsBack = () => {
15675
- // Go back to event instance selection
15676
- setCurrentStep("eventInstances");
16056
+ setCurrentStep(bookingReturnStep.current);
15677
16057
  setSelectedUpsells([]);
15678
16058
  setUpsells([]);
15679
16059
  };
@@ -15708,10 +16088,36 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
15708
16088
  setError(errorMessage);
15709
16089
  config.onError?.(errorMessage);
15710
16090
  };
15711
- const handleUpcomingEventSelect = async (eventInstanceId) => {
16091
+ const handleUpcomingEventSelect = async (eventInstanceId, eventTypeId) => {
16092
+ // Resolve the event type — may come from card preview (eventTypeId provided) or
16093
+ // from the next-events list where selectedEventType is already set
16094
+ const resolvedEventType = eventTypeId != null
16095
+ ? (eventTypes.find((et) => et.id === eventTypeId) ?? selectedEventType)
16096
+ : selectedEventType;
16097
+ if (resolvedEventType && resolvedEventType !== selectedEventType) {
16098
+ setSelectedEventType(resolvedEventType);
16099
+ }
16100
+ // Check if this is coming from a card preview (eventTypeId was provided)
16101
+ // In that case, we need to load event instances so back navigation works properly
16102
+ const isFromCardPreview = eventTypeId != null;
16103
+ if (isFromCardPreview && resolvedEventType) {
16104
+ // Load event instances in background so back navigation shows instances
16105
+ setShouldRenderInstanceSelection(true);
16106
+ void loadEventInstances(resolvedEventType.id);
16107
+ // Record that we should return to instance selection (not event types)
16108
+ bookingReturnStep.current = "eventInstances";
16109
+ }
16110
+ else {
16111
+ // Record where to return when the booking form is closed
16112
+ bookingReturnStep.current = currentStep === "eventInstances" ? "eventInstances" : "eventTypes";
16113
+ }
16114
+ // First try to find the event in upcomingEvents (for next-events view mode)
15712
16115
  const upcomingEvent = upcomingEvents.find((event) => event.id === eventInstanceId);
15713
- if (upcomingEvent) {
15714
- const eventInstance = {
16116
+ // If not found in upcomingEvents, try to find in card preview items
16117
+ const cardPreviewItem = !upcomingEvent && resolvedEventType?.cardPreview?.find((item) => item.id === eventInstanceId);
16118
+ // Build the event instance from either source
16119
+ const eventInstance = upcomingEvent
16120
+ ? {
15715
16121
  id: upcomingEvent.id,
15716
16122
  name: upcomingEvent.name,
15717
16123
  startTime: upcomingEvent.startTime,
@@ -15725,13 +16131,63 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
15725
16131
  bookingOpen: upcomingEvent.bookingOpen,
15726
16132
  ...(upcomingEvent.deposit !== undefined && { deposit: upcomingEvent.deposit }),
15727
16133
  ...(upcomingEvent.notes !== undefined && { notes: upcomingEvent.notes }),
15728
- };
16134
+ }
16135
+ : cardPreviewItem
16136
+ ? {
16137
+ id: cardPreviewItem.id,
16138
+ name: cardPreviewItem.name,
16139
+ startTime: cardPreviewItem.startTime,
16140
+ endTime: cardPreviewItem.startTime,
16141
+ price: cardPreviewItem.price,
16142
+ maxParticipants: cardPreviewItem.availableSpots + 1,
16143
+ participantCount: 1,
16144
+ availableSpots: cardPreviewItem.availableSpots,
16145
+ durationDays: 1,
16146
+ durationPerDay: 1,
16147
+ bookingOpen: true,
16148
+ }
16149
+ : null;
16150
+ if (eventInstance) {
15729
16151
  setSelectedEventInstance(eventInstance);
15730
16152
  }
16153
+ setError(null);
16154
+ // Check for upsells before going to booking (same as handleEventInstanceSelect)
16155
+ const eventTypeForUpsells = resolvedEventType;
16156
+ if (eventTypeForUpsells) {
16157
+ const defaultParticipantCount = 1;
16158
+ setTempParticipantCount(defaultParticipantCount);
16159
+ setIsLoadingUpsells(true);
16160
+ setShouldRenderUpsells(true);
16161
+ try {
16162
+ const availableUpsells = await loadUpsells(eventTypeForUpsells.id, eventInstanceId, defaultParticipantCount);
16163
+ if (availableUpsells.length > 0) {
16164
+ setUpsells(availableUpsells);
16165
+ // Pre-select default-checked upsells
16166
+ const defaultSelections = availableUpsells
16167
+ .filter((upsell) => upsell.defaultChecked && upsell.available)
16168
+ .map((upsell) => ({
16169
+ upsellPackageId: upsell.id,
16170
+ quantity: defaultParticipantCount,
16171
+ }));
16172
+ setSelectedUpsells(defaultSelections);
16173
+ setCurrentStep("upsells");
16174
+ setIsLoadingUpsells(false);
16175
+ // Load event details in background for when user continues past upsells
16176
+ void loadEventDetails(eventInstanceId);
16177
+ return;
16178
+ }
16179
+ }
16180
+ catch (err) {
16181
+ console.error("Error loading upsells:", err);
16182
+ }
16183
+ finally {
16184
+ setIsLoadingUpsells(false);
16185
+ }
16186
+ }
16187
+ // No upsells — go directly to booking
15731
16188
  setCurrentStep("booking");
15732
16189
  setShouldRenderBookingForm(true);
15733
16190
  setIsLoadingEventDetails(true);
15734
- setError(null);
15735
16191
  try {
15736
16192
  await loadEventDetails(eventInstanceId);
15737
16193
  }
@@ -15888,6 +16344,31 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
15888
16344
  window.history.replaceState({}, "", url.toString());
15889
16345
  }, config: config, onError: setError, paymentIntentId: successPaymentId })] }), showPromoDialog && config.promo && (jsx(PromoDialog, { config: config.promo, onClose: handlePromoDialogClose, onCtaClick: handlePromoCtaClick }))] }));
15890
16346
  }
16347
+ if (viewMode === "specials" && showingPreview) {
16348
+ return (jsxs(StyleProvider, { config: config, children: [jsxs("div", { ref: setWidgetContainerRef, children: [jsx(SpecialsView, { specials: specials, onEventSelect: handleUpcomingEventSelect, isLoading: isLoadingSpecials, showSavingsAmount: config.specialsSettings?.showSavingsAmount ?? true, showSavingsPercent: config.specialsSettings?.showSavingsPercent ?? false, emptyStateText: config.specialsSettings?.emptyStateText }), shouldRenderBookingForm && eventDetails && (jsx(BookingForm, { config: config, eventDetails: eventDetails, stripePromise: stripePromise, onSuccess: handleBookingSuccess, onError: handleBookingError, onBackToEventInstances: () => {
16349
+ setCurrentStep("eventTypes");
16350
+ setShowingPreview(true);
16351
+ setEventDetails(null);
16352
+ }, onBackToEventTypes: () => {
16353
+ setCurrentStep("eventTypes");
16354
+ setShowingPreview(true);
16355
+ setEventDetails(null);
16356
+ }, selectedEventType: selectedEventType, selectedEventInstance: selectedEventInstance, isOpen: currentStep === "booking" && !!eventDetails, onClose: () => {
16357
+ setCurrentStep("eventTypes");
16358
+ setShowingPreview(true);
16359
+ setEventDetails(null);
16360
+ }, systemConfig: systemConfig, selectedUpsells: selectedUpsells, upsells: upsells })), jsx(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
16361
+ setIsSuccess(false);
16362
+ setCurrentStep("eventTypes");
16363
+ setShowingPreview(true);
16364
+ setSuccessPaymentId(null);
16365
+ setShouldRenderInstanceSelection(false);
16366
+ setShouldRenderUpsells(false);
16367
+ setShouldRenderBookingForm(false);
16368
+ setSelectedUpsells([]);
16369
+ setUpsells([]);
16370
+ }, config: config, onError: setError, paymentIntentId: successPaymentId })] }), showPromoDialog && config.promo && (jsx(PromoDialog, { config: config.promo, onClose: handlePromoDialogClose, onCtaClick: handlePromoCtaClick }))] }));
16371
+ }
15891
16372
  if (viewMode === "next-events" && !showingPreview && currentStep === "eventInstances") {
15892
16373
  return (jsxs(StyleProvider, { config: config, children: [jsxs("div", { ref: setWidgetContainerRef, children: [shouldRenderInstanceSelection && (jsx(EventInstanceSelection, { eventInstances: eventInstances, selectedEventType: selectedEventType, onEventInstanceSelect: handleEventInstanceSelect, onBackToEventTypes: () => {
15893
16374
  setShowingPreview(true);
@@ -15958,14 +16439,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
15958
16439
  }, config: config, onError: setError, paymentIntentId: successPaymentId })] }), showPromoDialog && config.promo && (jsx(PromoDialog, { config: config.promo, onClose: handlePromoDialogClose, onCtaClick: handlePromoCtaClick }))] }));
15959
16440
  }
15960
16441
  // Cards mode (default) - show event type selection with optional voucher card
15961
- const cardsView = (jsxs(Fragment, { children: [hasEventSelection && (jsx(EventTypeSelection, { eventTypes: eventTypes, onEventTypeSelect: handleEventTypeSelect, isLoading: isLoading, skeletonCount: getSkeletonCount(), showVoucherAttachment: Boolean(voucherConfig?.enabled && voucherCardIntegrationEnabled && !isStandaloneVoucherMode), onVoucherClick: handleVoucherAttachmentClick })), voucherConfig?.enabled && voucherCardIntegrationEnabled && isStandaloneVoucherMode && (jsx("div", { style: { padding: hasEventSelection ? "0 0 24px 0" : "0" }, children: jsx("div", { style: {
15962
- display: "grid",
15963
- gridTemplateColumns: "minmax(350px, 500px)",
15964
- gap: "24px",
15965
- justifyContent: "center",
15966
- }, children: jsx(VoucherPurchaseCard, { config: voucherConfig, minEventPrice: voucherEventTypes.length > 0
15967
- ? Math.min(...voucherEventTypes.map((et) => et.maxPrice))
15968
- : undefined, fallbackImages: voucherEventTypes.flatMap((eventType) => eventType.images || []), onClick: handleVoucherCardClick, standalone: true }) }) })), isStandaloneVoucherMode && isLoading && !voucherConfig && (jsx("div", { style: { padding: "24px", textAlign: "center" }, children: jsx("div", { style: {
16442
+ const cardsView = (jsxs(Fragment, { children: [hasEventSelection && (jsx(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 && (jsx(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 && (jsx("div", { style: { padding: "24px", textAlign: "center" }, children: jsx("div", { style: {
15969
16443
  display: "inline-block",
15970
16444
  width: "32px",
15971
16445
  height: "32px",
@@ -16018,19 +16492,17 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
16018
16492
  url.searchParams.delete("mollie_payment_id");
16019
16493
  url.searchParams.delete("mollie_status");
16020
16494
  window.history.replaceState({}, "", url.toString());
16021
- }, config: config, onError: setError, paymentIntentId: successPaymentId }), isSuccess && voucherPurchaseResult && (jsx(VoucherSuccessModal, { isOpen: true, onClose: () => {
16495
+ }, config: config, onError: setError, paymentIntentId: successPaymentId }), jsx(VoucherIntegration, { config: config, voucherConfig: voucherConfig, eventTypes: voucherEventTypes, systemConfig: systemConfig, isFormOpen: isVoucherFormOpen, isLoadingConfig: isLoadingVoucherConfig, preselectedEventTypeId: preselectedVoucherEventTypeId, voucherPurchaseResult: voucherPurchaseResult, isSuccess: isSuccess, showStandaloneCard: false, onCardClick: handleVoucherCardClick, onFormClose: handleVoucherFormClose, onSuccess: handleVoucherSuccess, onError: handleVoucherError, onSuccessModalClose: () => {
16022
16496
  setIsSuccess(false);
16023
16497
  setVoucherPurchaseResult(null);
16024
16498
  const url = new URL(window.location.href);
16025
- // Clean up Stripe params
16026
16499
  url.searchParams.delete("payment_intent");
16027
16500
  url.searchParams.delete("payment_intent_client_secret");
16028
16501
  url.searchParams.delete("redirect_status");
16029
- // Clean up Mollie params
16030
16502
  url.searchParams.delete("mollie_payment_id");
16031
16503
  url.searchParams.delete("mollie_status");
16032
16504
  window.history.replaceState({}, "", url.toString());
16033
- }, result: voucherPurchaseResult })), voucherConfig?.enabled && (jsx(VoucherPurchaseForm, { config: config, voucherConfig: voucherConfig, eventTypes: voucherEventTypes, isOpen: isVoucherFormOpen, onClose: handleVoucherFormClose, onSuccess: handleVoucherSuccess, onError: handleVoucherError, systemConfig: systemConfig, preselectedEventTypeId: preselectedVoucherEventTypeId, isLoadingEventTypes: isLoadingVoucherConfig }))] }), showPromoDialog && config.promo && (jsx(PromoDialog, { config: config.promo, onClose: handlePromoDialogClose, onCtaClick: handlePromoCtaClick }))] }));
16505
+ } })] }), showPromoDialog && config.promo && (jsx(PromoDialog, { config: config.promo, onClose: handlePromoDialogClose, onCtaClick: handlePromoCtaClick }))] }));
16034
16506
  }
16035
16507
  function UniversalBookingWidget(props) {
16036
16508
  const [languagePolicy, setLanguagePolicy] = useState(null);
@@ -16072,7 +16544,7 @@ function styleInject(css, ref) {
16072
16544
  }
16073
16545
  }
16074
16546
 
16075
- 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}}";
16547
+ 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}}";
16076
16548
  styleInject(css_248z);
16077
16549
 
16078
16550
  // Export init function for vanilla JS usage