keystone-design-bootstrap 1.0.55 → 1.0.57

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 (55) hide show
  1. package/dist/design_system/elements/index.js +8 -3
  2. package/dist/design_system/elements/index.js.map +1 -1
  3. package/dist/design_system/sections/index.js +203 -106
  4. package/dist/design_system/sections/index.js.map +1 -1
  5. package/dist/index.js +303 -247
  6. package/dist/index.js.map +1 -1
  7. package/dist/lib/hooks/index.js +72 -0
  8. package/dist/lib/hooks/index.js.map +1 -1
  9. package/dist/lib/server-api.js.map +1 -1
  10. package/dist/utils/phone-helpers.js +26 -0
  11. package/dist/utils/phone-helpers.js.map +1 -0
  12. package/package.json +5 -2
  13. package/src/design_system/components/ChatWidget.tsx +51 -34
  14. package/src/design_system/components/DynamicFormFields.tsx +1 -24
  15. package/src/design_system/elements/modal/modal.tsx +54 -35
  16. package/src/design_system/portal/LoginForm.tsx +358 -0
  17. package/src/design_system/portal/LoginModalController.tsx +63 -0
  18. package/src/design_system/portal/LogoutButton.tsx +22 -0
  19. package/src/design_system/portal/MessageComposer.tsx +92 -0
  20. package/src/design_system/portal/PortalPage.tsx +754 -0
  21. package/src/design_system/portal/RowThumbnail.tsx +76 -0
  22. package/src/design_system/portal/index.ts +5 -0
  23. package/src/design_system/sections/index.tsx +1 -1
  24. package/src/design_system/sections/service-menu-section.tsx +7 -108
  25. package/src/lib/actions.ts +51 -115
  26. package/src/lib/consumer-session.ts +74 -0
  27. package/src/lib/hooks/index.ts +2 -0
  28. package/src/lib/hooks/use-image-cycle.ts +105 -0
  29. package/src/lib/server-api.ts +7 -6
  30. package/src/next/routes/chat.ts +30 -58
  31. package/src/next/routes/consumer-auth.ts +180 -0
  32. package/src/types/api/consumer.ts +39 -0
  33. package/src/types/api/offer.ts +1 -1
  34. package/src/types/api/package.ts +20 -0
  35. package/src/types/api/service.ts +6 -24
  36. package/src/types/index.ts +2 -0
  37. package/src/utils/phone-helpers.ts +27 -0
  38. package/dist/blog-post-DGjaJ3wf.d.ts +0 -50
  39. package/dist/contexts/index.d.ts +0 -13
  40. package/dist/design_system/elements/index.d.ts +0 -372
  41. package/dist/design_system/logo/keystone-logo.d.ts +0 -6
  42. package/dist/design_system/sections/index.d.ts +0 -237
  43. package/dist/form-CpsCONG5.d.ts +0 -151
  44. package/dist/index.d.ts +0 -76
  45. package/dist/lib/component-registry.d.ts +0 -13
  46. package/dist/lib/hooks/index.d.ts +0 -64
  47. package/dist/lib/server-api.d.ts +0 -43
  48. package/dist/themes/index.d.ts +0 -16
  49. package/dist/types/index.d.ts +0 -264
  50. package/dist/utils/cx.d.ts +0 -15
  51. package/dist/utils/gradient-placeholder.d.ts +0 -8
  52. package/dist/utils/is-react-component.d.ts +0 -21
  53. package/dist/utils/markdown-toc.d.ts +0 -14
  54. package/dist/utils/photo-helpers.d.ts +0 -37
  55. package/dist/website-photos-Bm-CBK9g.d.ts +0 -47
@@ -38,7 +38,7 @@ var __objRest = (source, exclude) => {
38
38
  };
39
39
 
40
40
  // src/design_system/sections/index.tsx
41
- import React61 from "react";
41
+ import React62 from "react";
42
42
 
43
43
  // src/lib/component-registry.ts
44
44
  var registry = /* @__PURE__ */ new Map();
@@ -3714,6 +3714,9 @@ function Modal({
3714
3714
  title,
3715
3715
  titleId = "modal-title",
3716
3716
  children,
3717
+ footer,
3718
+ hideHeader = false,
3719
+ ariaLabel,
3717
3720
  overlayClassName,
3718
3721
  panelClassName,
3719
3722
  maxWidth = "md"
@@ -3745,7 +3748,8 @@ function Modal({
3745
3748
  className: overlayClassName != null ? overlayClassName : "fixed inset-0 z-[200] flex items-center justify-center p-4 bg-black/50",
3746
3749
  role: "dialog",
3747
3750
  "aria-modal": "true",
3748
- "aria-labelledby": title ? titleId : void 0,
3751
+ "aria-label": ariaLabel,
3752
+ "aria-labelledby": !ariaLabel && title && !hideHeader ? titleId : void 0,
3749
3753
  onClick: (e) => e.target === overlayRef.current && onClose()
3750
3754
  },
3751
3755
  /* @__PURE__ */ React13.createElement(
@@ -3754,7 +3758,7 @@ function Modal({
3754
3758
  className: panelClassName != null ? panelClassName : `bg-primary border border-secondary rounded-lg shadow-xl w-full overflow-hidden ${maxWidthClass} max-h-[90vh] flex flex-col`,
3755
3759
  onClick: (e) => e.stopPropagation()
3756
3760
  },
3757
- /* @__PURE__ */ React13.createElement("div", { className: "flex items-start justify-between gap-4 p-4 md:p-6 border-b border-secondary flex-shrink-0" }, title ? /* @__PURE__ */ React13.createElement(
3761
+ !hideHeader && /* @__PURE__ */ React13.createElement("div", { className: "flex items-start justify-between gap-4 p-4 md:p-6 border-b border-secondary flex-shrink-0" }, title ? /* @__PURE__ */ React13.createElement(
3758
3762
  "h2",
3759
3763
  {
3760
3764
  id: titleId,
@@ -3790,7 +3794,8 @@ function Modal({
3790
3794
  )
3791
3795
  )
3792
3796
  )),
3793
- /* @__PURE__ */ React13.createElement("div", { className: "overflow-y-auto flex-1 p-4 md:p-6" }, children)
3797
+ /* @__PURE__ */ React13.createElement("div", { className: "overflow-y-auto flex-1 p-4 md:p-6" }, children),
3798
+ footer && /* @__PURE__ */ React13.createElement("div", { className: "flex-shrink-0 border-t border-secondary" }, footer)
3794
3799
  )
3795
3800
  );
3796
3801
  }
@@ -5951,8 +5956,7 @@ var countriesOptions = countries.map((country) => ({
5951
5956
  }));
5952
5957
  var countries_default = countries;
5953
5958
 
5954
- // src/design_system/components/DynamicFormFields.tsx
5955
- var INPUT_TYPES = ["text", "email", "tel"];
5959
+ // src/utils/phone-helpers.ts
5956
5960
  function getNationalMask(country) {
5957
5961
  if (!(country == null ? void 0 : country.phoneMask)) return "";
5958
5962
  const code = country.phoneCode.startsWith("+") ? country.phoneCode : `+${country.phoneCode}`;
@@ -5973,6 +5977,9 @@ function formatDigitsToMask(digits, mask) {
5973
5977
  }
5974
5978
  return out;
5975
5979
  }
5980
+
5981
+ // src/design_system/components/DynamicFormFields.tsx
5982
+ var INPUT_TYPES = ["text", "email", "tel"];
5976
5983
  function allFieldsFlat(fields) {
5977
5984
  const out = [];
5978
5985
  for (const item of fields) {
@@ -6168,14 +6175,20 @@ function DynamicFormFields({ form, jobSlug, privacyPolicyUrl, termsOfServiceUrl
6168
6175
  function trackMetaLead(eventId) {
6169
6176
  if (typeof window === "undefined" || !eventId) return;
6170
6177
  const fbq = window.fbq;
6171
- if (fbq) fbq("track", "Lead", {}, { eventID: eventId });
6178
+ if (!fbq) {
6179
+ console.debug("[MetaPixel] Lead skipped \u2014 fbq not loaded", { eventId });
6180
+ return;
6181
+ }
6182
+ console.debug("[MetaPixel] Lead", { eventId });
6183
+ fbq("track", "Lead", {}, { eventID: eventId });
6172
6184
  }
6173
6185
 
6174
6186
  // src/next/contexts/form-definitions.tsx
6175
6187
  import React19, { createContext as createContext9, useContext as useContext10 } from "react";
6176
6188
  var FormDefinitionsContext = createContext9({
6177
6189
  leadFormDefinition: null,
6178
- jobApplicationFormDefinition: null
6190
+ jobApplicationFormDefinition: null,
6191
+ marketingListSignupFormDefinition: null
6179
6192
  });
6180
6193
  function useFormDefinitions() {
6181
6194
  return useContext10(FormDefinitionsContext);
@@ -8510,8 +8523,9 @@ function HeaderNavigation2({
8510
8523
  React36.useEffect(() => {
8511
8524
  const handleScroll = () => {
8512
8525
  setIsScrolled(window.scrollY > 10);
8526
+ setActiveDropdown(null);
8513
8527
  };
8514
- window.addEventListener("scroll", handleScroll);
8528
+ window.addEventListener("scroll", handleScroll, { passive: true });
8515
8529
  return () => window.removeEventListener("scroll", handleScroll);
8516
8530
  }, []);
8517
8531
  React36.useEffect(() => {
@@ -8543,6 +8557,8 @@ function HeaderNavigation2({
8543
8557
  setDropdownTop(rect.bottom);
8544
8558
  }
8545
8559
  setActiveDropdown(item.label);
8560
+ } else {
8561
+ setActiveDropdown(null);
8546
8562
  }
8547
8563
  }, [cancelCloseTimeout]);
8548
8564
  const handleMouseLeave = useCallback5(() => {
@@ -17620,6 +17636,8 @@ function HeaderNavigation3({
17620
17636
  cancelCloseTimeout();
17621
17637
  if (item.children && item.children.length > 0) {
17622
17638
  setActiveDropdown(item.label);
17639
+ } else {
17640
+ setActiveDropdown(null);
17623
17641
  }
17624
17642
  }, [cancelCloseTimeout]);
17625
17643
  const handleMouseLeave = useCallback8(() => {
@@ -18410,6 +18428,8 @@ function HeaderNavigation4({
18410
18428
  cancelCloseTimeout();
18411
18429
  if (item.children && item.children.length > 0) {
18412
18430
  setActiveDropdown(item.label);
18431
+ } else {
18432
+ setActiveDropdown(null);
18413
18433
  }
18414
18434
  }, [cancelCloseTimeout]);
18415
18435
  const handleMouseLeave = useCallback9(() => {
@@ -19112,12 +19132,90 @@ var FeatureTabHorizontal = ({ title, subtitle, footer, isCurrent }) => /* @__PUR
19112
19132
  footer
19113
19133
  );
19114
19134
 
19135
+ // src/design_system/sections/email-signup-section.tsx
19136
+ import React60, { useRef as useRef18, useState as useState30 } from "react";
19137
+ var EmailSignupSection = ({
19138
+ title = "Stay in the loop",
19139
+ subtitle = "Subscribe to our newsletter for updates, tips, and exclusive offers.",
19140
+ buttonText = "Subscribe",
19141
+ successMessage = "You're subscribed! Thank you.",
19142
+ formDefinition
19143
+ }) => {
19144
+ const { marketingListSignupFormDefinition } = useFormDefinitions();
19145
+ const resolvedFormDefinition = formDefinition != null ? formDefinition : marketingListSignupFormDefinition;
19146
+ const [isSubmitting, setIsSubmitting] = useState30(false);
19147
+ const [submitStatus, setSubmitStatus] = useState30("idle");
19148
+ const [statusMessage, setStatusMessage] = useState30("");
19149
+ const formRef = useRef18(null);
19150
+ const handleSubmit = async (e) => {
19151
+ var _a;
19152
+ e.preventDefault();
19153
+ setIsSubmitting(true);
19154
+ setSubmitStatus("idle");
19155
+ setStatusMessage("");
19156
+ const formData = new FormData(e.currentTarget);
19157
+ const data = { formType: "marketing_list_signup" };
19158
+ formData.forEach((value, key) => {
19159
+ if (key.endsWith("_prefix")) return;
19160
+ if (typeof value === "string") data[key] = value;
19161
+ });
19162
+ try {
19163
+ const response = await fetch("/api/form/", {
19164
+ method: "POST",
19165
+ headers: { "Content-Type": "application/json" },
19166
+ body: JSON.stringify(data)
19167
+ });
19168
+ const result = await response.json();
19169
+ if (result.success) {
19170
+ setSubmitStatus("success");
19171
+ setStatusMessage(result.message || successMessage);
19172
+ (_a = formRef.current) == null ? void 0 : _a.reset();
19173
+ setTimeout(() => setSubmitStatus("idle"), 6e3);
19174
+ } else {
19175
+ setSubmitStatus("error");
19176
+ setStatusMessage(result.error || "Something went wrong. Please try again.");
19177
+ }
19178
+ } catch (e2) {
19179
+ setSubmitStatus("error");
19180
+ setStatusMessage("Network error. Please try again later.");
19181
+ }
19182
+ setIsSubmitting(false);
19183
+ };
19184
+ if (!resolvedFormDefinition) return null;
19185
+ return /* @__PURE__ */ React60.createElement("section", { className: "bg-secondary py-16 md:py-20" }, /* @__PURE__ */ React60.createElement("div", { className: "mx-auto max-w-container px-4 md:px-8" }, /* @__PURE__ */ React60.createElement("div", { className: "mx-auto max-w-xl text-center" }, /* @__PURE__ */ React60.createElement("h2", { className: "font-display text-display-sm font-semibold text-primary md:text-display-md" }, title), subtitle && /* @__PURE__ */ React60.createElement("p", { className: "mt-4 font-body text-lg text-tertiary" }, subtitle), /* @__PURE__ */ React60.createElement(
19186
+ Form2,
19187
+ {
19188
+ ref: formRef,
19189
+ onSubmit: handleSubmit,
19190
+ className: "mt-8 flex flex-col gap-6 text-left"
19191
+ },
19192
+ /* @__PURE__ */ React60.createElement(DynamicFormFields, { form: resolvedFormDefinition }),
19193
+ submitStatus === "success" && /* @__PURE__ */ React60.createElement("div", { className: "rounded-lg bg-success-50 p-4 text-success-700" }, statusMessage || successMessage),
19194
+ submitStatus === "error" && /* @__PURE__ */ React60.createElement("div", { className: "rounded-lg bg-error-50 p-4 text-error-700" }, statusMessage),
19195
+ /* @__PURE__ */ React60.createElement(
19196
+ Button2,
19197
+ {
19198
+ type: "submit",
19199
+ color: "primary",
19200
+ size: "xl",
19201
+ isDisabled: isSubmitting,
19202
+ isLoading: isSubmitting,
19203
+ className: "w-full"
19204
+ },
19205
+ isSubmitting ? "Subscribing..." : buttonText
19206
+ )
19207
+ ))));
19208
+ };
19209
+
19115
19210
  // src/design_system/sections/service-menu-section.tsx
19116
- import React60, { useState as useState30, useEffect as useEffect11, useCallback as useCallback10, useMemo as useMemo8 } from "react";
19211
+ import React61, { useState as useState32, useEffect as useEffect12, useCallback as useCallback10 } from "react";
19117
19212
  import { createPortal } from "react-dom";
19118
- var SERVICE_MENU_MODAL_ROOT_ID = "service-menu-modal-root";
19213
+
19214
+ // src/lib/hooks/use-image-cycle.ts
19215
+ import { useState as useState31, useEffect as useEffect11, useMemo as useMemo8 } from "react";
19119
19216
  var CYCLE_INTERVAL_MIN_MS = 6e3;
19120
19217
  var CYCLE_INTERVAL_MAX_MS = 8e3;
19218
+ var CROSSFADE_DURATION_MS = 600;
19121
19219
  function seedToUnit(seed) {
19122
19220
  let h = 2166136261 >>> 0;
19123
19221
  for (let i = 0; i < seed.length; i++) {
@@ -19126,18 +19224,6 @@ function seedToUnit(seed) {
19126
19224
  }
19127
19225
  return (h >>> 0) / 4294967296;
19128
19226
  }
19129
- function getActivePublicOffers(offers) {
19130
- if (!Array.isArray(offers) || offers.length === 0) return [];
19131
- return offers.filter((o) => o.active !== false && o.expired !== true);
19132
- }
19133
- function photoAttachmentDisplayUrl(pa) {
19134
- var _a, _b;
19135
- return ((_a = pa.photo) == null ? void 0 : _a.large_url) || ((_b = pa.photo) == null ? void 0 : _b.medium_url);
19136
- }
19137
- function photoAttachmentAlt(pa) {
19138
- var _a, _b;
19139
- return ((_a = pa.photo) == null ? void 0 : _a.alt_text) || ((_b = pa.photo) == null ? void 0 : _b.title) || "";
19140
- }
19141
19227
  function shuffleWithSeed(array, seed) {
19142
19228
  if (array.length <= 1) return array;
19143
19229
  const arr = [...array];
@@ -19156,39 +19242,25 @@ function shuffleWithSeed(array, seed) {
19156
19242
  }
19157
19243
  return arr;
19158
19244
  }
19159
- var CROSSFADE_DURATION_MS = 600;
19160
- function useCycledPhotoList(photoAttachments, seed) {
19161
- return useMemo8(
19162
- () => {
19163
- const arr = Array.isArray(photoAttachments) && photoAttachments.length > 0 ? photoAttachments : [];
19164
- if (arr.length === 0) return [];
19165
- const shuffled = shuffleWithSeed(arr, seed);
19166
- return shuffled.map((pa) => {
19167
- var _a;
19168
- return { url: (_a = photoAttachmentDisplayUrl(pa)) != null ? _a : "", alt: photoAttachmentAlt(pa) };
19169
- }).filter((x) => x.url);
19170
- },
19171
- [photoAttachments, seed]
19172
- );
19245
+ function photoUrlFromAttachment(pa) {
19246
+ var _a, _b, _c;
19247
+ return ((_a = pa.photo) == null ? void 0 : _a.large_url) || ((_b = pa.photo) == null ? void 0 : _b.medium_url) || ((_c = pa.photo) == null ? void 0 : _c.thumbnail_url);
19173
19248
  }
19174
- function GridCardWithImage({
19175
- photoAttachments,
19176
- fallbackId,
19177
- fallbackAlt,
19178
- title,
19179
- subtitle,
19180
- children,
19181
- onClick,
19182
- websitePhotos,
19183
- companyInformation,
19184
- cycleSeed,
19185
- hasSpecial
19186
- }) {
19187
- var _a, _b, _c, _d, _e, _f, _g;
19188
- const seed = cycleSeed != null ? cycleSeed : String(fallbackId);
19189
- const list = useCycledPhotoList(photoAttachments, seed);
19190
- const [currentIndex, setCurrentIndex] = useState30(0);
19191
- const [transitioning, setTransitioning] = useState30(false);
19249
+ function photoAltFromAttachment(pa) {
19250
+ var _a, _b;
19251
+ return ((_a = pa.photo) == null ? void 0 : _a.alt_text) || ((_b = pa.photo) == null ? void 0 : _b.title) || "";
19252
+ }
19253
+ function useImageCycle(photoAttachments, seed) {
19254
+ const list = useMemo8(() => {
19255
+ const arr = Array.isArray(photoAttachments) && photoAttachments.length > 0 ? photoAttachments : [];
19256
+ if (arr.length === 0) return [];
19257
+ return shuffleWithSeed(arr, seed).map((pa) => {
19258
+ var _a;
19259
+ return { url: (_a = photoUrlFromAttachment(pa)) != null ? _a : "", alt: photoAltFromAttachment(pa) };
19260
+ }).filter((x) => x.url);
19261
+ }, [photoAttachments, seed]);
19262
+ const [currentIndex, setCurrentIndex] = useState31(0);
19263
+ const [transitioning, setTransitioning] = useState31(false);
19192
19264
  const intervalMs = useMemo8(
19193
19265
  () => CYCLE_INTERVAL_MIN_MS + Math.floor(seedToUnit(seed) * (CYCLE_INTERVAL_MAX_MS - CYCLE_INTERVAL_MIN_MS + 1)),
19194
19266
  [seed]
@@ -19207,24 +19279,48 @@ function GridCardWithImage({
19207
19279
  return () => clearTimeout(t);
19208
19280
  }, [transitioning, list.length]);
19209
19281
  const nextIndex = list.length > 1 ? (currentIndex + 1) % list.length : 0;
19210
- const currentItem = list[currentIndex];
19211
- const displayAlt = (currentItem == null ? void 0 : currentItem.alt) || fallbackAlt;
19212
- const singleUrl = (_a = list[0]) == null ? void 0 : _a.url;
19213
- return /* @__PURE__ */ React60.createElement(
19282
+ return { list, currentIndex, nextIndex, transitioning };
19283
+ }
19284
+
19285
+ // src/design_system/sections/service-menu-section.tsx
19286
+ var SERVICE_MENU_MODAL_ROOT_ID = "service-menu-modal-root";
19287
+ function getActivePublicOffers(offers) {
19288
+ if (!Array.isArray(offers) || offers.length === 0) return [];
19289
+ return offers.filter((o) => o.active !== false && o.expired !== true);
19290
+ }
19291
+ function GridCardWithImage({
19292
+ photoAttachments,
19293
+ fallbackId,
19294
+ fallbackAlt,
19295
+ title,
19296
+ subtitle,
19297
+ children,
19298
+ onClick,
19299
+ websitePhotos,
19300
+ companyInformation,
19301
+ cycleSeed,
19302
+ hasSpecial
19303
+ }) {
19304
+ var _a, _b, _c, _d, _e, _f, _g, _h;
19305
+ const seed = cycleSeed != null ? cycleSeed : String(fallbackId);
19306
+ const { list, currentIndex, nextIndex, transitioning } = useImageCycle(photoAttachments, seed);
19307
+ const displayAlt = ((_a = list[currentIndex]) == null ? void 0 : _a.alt) || fallbackAlt;
19308
+ const singleUrl = (_b = list[0]) == null ? void 0 : _b.url;
19309
+ return /* @__PURE__ */ React61.createElement(
19214
19310
  "button",
19215
19311
  {
19216
19312
  type: "button",
19217
19313
  onClick,
19218
19314
  className: "flex flex-col h-full w-full text-left group block rounded-lg outline-none focus-visible:ring-2 focus-visible:ring-brand-accent focus-visible:ring-offset-2"
19219
19315
  },
19220
- /* @__PURE__ */ React60.createElement("div", { className: "w-full h-36 overflow-hidden rounded-lg mb-3 relative" }, hasSpecial ? /* @__PURE__ */ React60.createElement(
19316
+ /* @__PURE__ */ React61.createElement("div", { className: "w-full h-36 overflow-hidden rounded-lg mb-3 relative" }, hasSpecial ? /* @__PURE__ */ React61.createElement(
19221
19317
  "span",
19222
19318
  {
19223
19319
  className: "absolute top-2 right-2 z-10 rounded-full border border-brand-accent/40 bg-secondary/95 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-brand-accent shadow-sm backdrop-blur-sm",
19224
19320
  "aria-label": "Has special offer"
19225
19321
  },
19226
19322
  "Special"
19227
- ) : null, list.length === 0 ? /* @__PURE__ */ React60.createElement(
19323
+ ) : null, list.length === 0 ? /* @__PURE__ */ React61.createElement(
19228
19324
  PhotoWithFallback2,
19229
19325
  {
19230
19326
  item: void 0,
@@ -19236,7 +19332,7 @@ function GridCardWithImage({
19236
19332
  }
19237
19333
  ) : list.length === 1 && singleUrl ? (
19238
19334
  // eslint-disable-next-line @next/next/no-img-element -- dynamic API URLs; next/image not configured for external host
19239
- /* @__PURE__ */ React60.createElement(
19335
+ /* @__PURE__ */ React61.createElement(
19240
19336
  "img",
19241
19337
  {
19242
19338
  src: singleUrl,
@@ -19244,37 +19340,37 @@ function GridCardWithImage({
19244
19340
  className: "w-full h-full object-cover transition-transform duration-300 group-hover:scale-105"
19245
19341
  }
19246
19342
  )
19247
- ) : /* @__PURE__ */ React60.createElement(React60.Fragment, null, /* @__PURE__ */ React60.createElement(
19343
+ ) : /* @__PURE__ */ React61.createElement(React61.Fragment, null, /* @__PURE__ */ React61.createElement(
19248
19344
  "div",
19249
19345
  {
19250
19346
  className: `absolute inset-0 ${transitioning ? "transition-opacity duration-[600ms] ease-in-out" : "transition-none"}`,
19251
19347
  style: { opacity: transitioning ? 0 : 1 }
19252
19348
  },
19253
- /* @__PURE__ */ React60.createElement(
19349
+ /* @__PURE__ */ React61.createElement(
19254
19350
  "img",
19255
19351
  {
19256
- src: (_b = list[currentIndex]) == null ? void 0 : _b.url,
19257
- alt: (_d = (_c = list[currentIndex]) == null ? void 0 : _c.alt) != null ? _d : displayAlt,
19352
+ src: (_c = list[currentIndex]) == null ? void 0 : _c.url,
19353
+ alt: (_e = (_d = list[currentIndex]) == null ? void 0 : _d.alt) != null ? _e : displayAlt,
19258
19354
  className: "w-full h-full object-cover transition-transform duration-300 group-hover:scale-105"
19259
19355
  }
19260
19356
  )
19261
- ), /* @__PURE__ */ React60.createElement(
19357
+ ), /* @__PURE__ */ React61.createElement(
19262
19358
  "div",
19263
19359
  {
19264
19360
  className: `absolute inset-0 ${transitioning ? "transition-opacity duration-[600ms] ease-in-out" : "transition-none"}`,
19265
19361
  style: { opacity: transitioning ? 1 : 0 }
19266
19362
  },
19267
- /* @__PURE__ */ React60.createElement(
19363
+ /* @__PURE__ */ React61.createElement(
19268
19364
  "img",
19269
19365
  {
19270
- src: (_e = list[nextIndex]) == null ? void 0 : _e.url,
19271
- alt: (_g = (_f = list[nextIndex]) == null ? void 0 : _f.alt) != null ? _g : displayAlt,
19366
+ src: (_f = list[nextIndex]) == null ? void 0 : _f.url,
19367
+ alt: (_h = (_g = list[nextIndex]) == null ? void 0 : _g.alt) != null ? _h : displayAlt,
19272
19368
  className: "w-full h-full object-cover transition-transform duration-300 group-hover:scale-105"
19273
19369
  }
19274
19370
  )
19275
19371
  ))),
19276
- subtitle && /* @__PURE__ */ React60.createElement("p", { className: "text-xs font-medium text-fg-secondary uppercase tracking-wide line-clamp-1" }, subtitle),
19277
- /* @__PURE__ */ React60.createElement("h4", { className: "font-display text-base font-normal text-fg-primary mt-1 group-hover:underline line-clamp-2" }, title),
19372
+ subtitle && /* @__PURE__ */ React61.createElement("p", { className: "text-xs font-medium text-fg-secondary uppercase tracking-wide line-clamp-1" }, subtitle),
19373
+ /* @__PURE__ */ React61.createElement("h4", { className: "font-display text-base font-normal text-fg-primary mt-1 group-hover:underline line-clamp-2" }, title),
19278
19374
  children
19279
19375
  );
19280
19376
  }
@@ -19284,9 +19380,9 @@ function CarouselRow({
19284
19380
  renderItem
19285
19381
  }) {
19286
19382
  if (!(items == null ? void 0 : items.length)) return null;
19287
- return /* @__PURE__ */ React60.createElement("div", { className: "mb-12 last:mb-0" }, /* @__PURE__ */ React60.createElement(Carousel.Root, { opts: { align: "start", loop: true } }, /* @__PURE__ */ React60.createElement("div", { className: "flex items-center justify-between gap-4 mb-2" }, /* @__PURE__ */ React60.createElement("h3", { className: "font-display text-2xl font-normal text-fg-primary md:text-3xl" }, rowTitle), /* @__PURE__ */ React60.createElement("div", { className: "flex gap-2 flex-shrink-0" }, /* @__PURE__ */ React60.createElement(Carousel.PrevTrigger, { className: "rounded-full p-1.5 border border-secondary hover:bg-primary_hover transition-colors disabled:opacity-30 disabled:cursor-not-allowed" }, /* @__PURE__ */ React60.createElement("svg", { className: "w-5 h-5 text-fg-primary", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": true }, /* @__PURE__ */ React60.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M15 19l-7-7 7-7" }))), /* @__PURE__ */ React60.createElement(Carousel.NextTrigger, { className: "rounded-full p-1.5 border border-secondary hover:bg-primary_hover transition-colors disabled:opacity-30 disabled:cursor-not-allowed" }, /* @__PURE__ */ React60.createElement("svg", { className: "w-5 h-5 text-fg-primary", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": true }, /* @__PURE__ */ React60.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 5l7 7-7 7" }))))), /* @__PURE__ */ React60.createElement(Carousel.Content, { className: "-ml-3" }, items.map((item, index) => {
19383
+ return /* @__PURE__ */ React61.createElement("div", { className: "mb-12 last:mb-0" }, /* @__PURE__ */ React61.createElement(Carousel.Root, { opts: { align: "start", loop: true } }, /* @__PURE__ */ React61.createElement("div", { className: "flex items-center justify-between gap-4 mb-2" }, /* @__PURE__ */ React61.createElement("h3", { className: "font-display text-2xl font-normal text-fg-primary md:text-3xl" }, rowTitle), /* @__PURE__ */ React61.createElement("div", { className: "flex gap-2 flex-shrink-0" }, /* @__PURE__ */ React61.createElement(Carousel.PrevTrigger, { className: "rounded-full p-1.5 border border-secondary hover:bg-primary_hover transition-colors disabled:opacity-30 disabled:cursor-not-allowed" }, /* @__PURE__ */ React61.createElement("svg", { className: "w-5 h-5 text-fg-primary", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": true }, /* @__PURE__ */ React61.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M15 19l-7-7 7-7" }))), /* @__PURE__ */ React61.createElement(Carousel.NextTrigger, { className: "rounded-full p-1.5 border border-secondary hover:bg-primary_hover transition-colors disabled:opacity-30 disabled:cursor-not-allowed" }, /* @__PURE__ */ React61.createElement("svg", { className: "w-5 h-5 text-fg-primary", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": true }, /* @__PURE__ */ React61.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 5l7 7-7 7" }))))), /* @__PURE__ */ React61.createElement(Carousel.Content, { className: "-ml-3" }, items.map((item, index) => {
19288
19384
  var _a;
19289
- return /* @__PURE__ */ React60.createElement(Carousel.Item, { key: (_a = item.id) != null ? _a : index, className: "pl-3 basis-[70%] sm:basis-1/2 md:basis-1/3 lg:basis-1/4" }, renderItem(item, index));
19385
+ return /* @__PURE__ */ React61.createElement(Carousel.Item, { key: (_a = item.id) != null ? _a : index, className: "pl-3 basis-[70%] sm:basis-1/2 md:basis-1/3 lg:basis-1/4" }, renderItem(item, index));
19290
19386
  }))));
19291
19387
  }
19292
19388
  function GridRow({
@@ -19295,9 +19391,9 @@ function GridRow({
19295
19391
  renderItem
19296
19392
  }) {
19297
19393
  if (!(items == null ? void 0 : items.length)) return null;
19298
- return /* @__PURE__ */ React60.createElement("div", { className: "mb-12 last:mb-0" }, /* @__PURE__ */ React60.createElement("h3", { className: "font-display text-2xl font-normal text-fg-primary mb-3 md:text-3xl" }, rowTitle), /* @__PURE__ */ React60.createElement("div", { className: "grid grid-cols-2 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4" }, items.map((item, index) => {
19394
+ return /* @__PURE__ */ React61.createElement("div", { className: "mb-12 last:mb-0" }, /* @__PURE__ */ React61.createElement("h3", { className: "font-display text-2xl font-normal text-fg-primary mb-3 md:text-3xl" }, rowTitle), /* @__PURE__ */ React61.createElement("div", { className: "grid grid-cols-2 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4" }, items.map((item, index) => {
19299
19395
  var _a;
19300
- return /* @__PURE__ */ React60.createElement("div", { key: (_a = item.id) != null ? _a : index, className: "min-w-0 flex" }, renderItem(item, index));
19396
+ return /* @__PURE__ */ React61.createElement("div", { key: (_a = item.id) != null ? _a : index, className: "min-w-0 flex" }, renderItem(item, index));
19301
19397
  })));
19302
19398
  }
19303
19399
  function MenuBlock({
@@ -19307,27 +19403,27 @@ function MenuBlock({
19307
19403
  layout: layout2
19308
19404
  }) {
19309
19405
  if (!(items == null ? void 0 : items.length)) return null;
19310
- return layout2 === "carousel" ? /* @__PURE__ */ React60.createElement(CarouselRow, { rowTitle, items, renderItem }) : /* @__PURE__ */ React60.createElement(GridRow, { rowTitle, items, renderItem });
19406
+ return layout2 === "carousel" ? /* @__PURE__ */ React61.createElement(CarouselRow, { rowTitle, items, renderItem }) : /* @__PURE__ */ React61.createElement(GridRow, { rowTitle, items, renderItem });
19311
19407
  }
19312
19408
  function formatPriceCents(cents) {
19313
19409
  if (cents == null) return null;
19314
19410
  return new Intl.NumberFormat("en-US", { style: "currency", currency: "USD", minimumFractionDigits: 0, maximumFractionDigits: 0 }).format(cents / 100);
19315
19411
  }
19316
19412
  function ModalSection({ title, children }) {
19317
- return /* @__PURE__ */ React60.createElement("div", null, /* @__PURE__ */ React60.createElement("h3", { className: "font-display text-sm font-semibold text-fg-primary uppercase tracking-wide mb-2" }, title), children);
19413
+ return /* @__PURE__ */ React61.createElement("div", null, /* @__PURE__ */ React61.createElement("h3", { className: "font-display text-sm font-semibold text-fg-primary uppercase tracking-wide mb-2" }, title), children);
19318
19414
  }
19319
19415
  function ActiveOffersCallout({ offers }) {
19320
19416
  if (!offers.length) return null;
19321
- return /* @__PURE__ */ React60.createElement("div", { className: "rounded-xl border border-secondary bg-secondary/25 p-4 md:p-5 space-y-3 ring-1 ring-brand-accent/25" }, /* @__PURE__ */ React60.createElement("p", { className: "text-xs font-semibold uppercase tracking-wide text-brand-accent" }, "Current specials"), /* @__PURE__ */ React60.createElement("ul", { className: "space-y-3 list-none m-0 p-0" }, offers.map((o) => /* @__PURE__ */ React60.createElement(
19417
+ return /* @__PURE__ */ React61.createElement("div", { className: "rounded-xl border border-secondary bg-secondary/25 p-4 md:p-5 space-y-3 ring-1 ring-brand-accent/25" }, /* @__PURE__ */ React61.createElement("p", { className: "text-xs font-semibold uppercase tracking-wide text-brand-accent" }, "Current specials"), /* @__PURE__ */ React61.createElement("ul", { className: "space-y-3 list-none m-0 p-0" }, offers.map((o) => /* @__PURE__ */ React61.createElement(
19322
19418
  "li",
19323
19419
  {
19324
19420
  key: o.id,
19325
19421
  className: "rounded-lg border border-secondary border-l-[3px] border-l-brand-accent bg-primary_hover/30 p-4"
19326
19422
  },
19327
- /* @__PURE__ */ React60.createElement("p", { className: "font-display text-base font-medium text-fg-primary" }, o.name),
19328
- o.value_terms ? /* @__PURE__ */ React60.createElement("p", { className: "font-body text-sm text-brand-accent mt-1 font-medium" }, o.value_terms) : null,
19329
- o.description ? /* @__PURE__ */ React60.createElement("p", { className: "font-body text-sm text-fg-secondary mt-2 leading-relaxed" }, o.description) : null,
19330
- o.expires_at ? /* @__PURE__ */ React60.createElement("p", { className: "text-xs text-tertiary mt-2" }, "Ends ", new Date(o.expires_at).toLocaleDateString(void 0, { dateStyle: "medium" })) : null
19423
+ /* @__PURE__ */ React61.createElement("p", { className: "font-display text-base font-medium text-fg-primary" }, o.name),
19424
+ o.value_terms ? /* @__PURE__ */ React61.createElement("p", { className: "font-body text-sm text-brand-accent mt-1 font-medium" }, o.value_terms) : null,
19425
+ o.description ? /* @__PURE__ */ React61.createElement("p", { className: "font-body text-sm text-fg-secondary mt-2 leading-relaxed" }, o.description) : null,
19426
+ o.expires_at ? /* @__PURE__ */ React61.createElement("p", { className: "text-xs text-tertiary mt-2" }, "Ends ", new Date(o.expires_at).toLocaleDateString(void 0, { dateStyle: "medium" })) : null
19331
19427
  ))));
19332
19428
  }
19333
19429
  function DetailModalContent({
@@ -19340,20 +19436,20 @@ function DetailModalContent({
19340
19436
  const categoryLine = ((_a = p.category_names) == null ? void 0 : _a.length) ? p.category_names.join(" | ") : null;
19341
19437
  const descriptionMarkdown2 = p.description_markdown || p.summary || p.first_service_description_markdown;
19342
19438
  const packageSpecials = getActivePublicOffers(p.offers);
19343
- return /* @__PURE__ */ React60.createElement("div", { className: "space-y-6" }, categoryLine && /* @__PURE__ */ React60.createElement("p", { className: "text-sm font-medium text-fg-secondary uppercase tracking-wide" }, categoryLine), /* @__PURE__ */ React60.createElement(ActiveOffersCallout, { offers: packageSpecials }), descriptionMarkdown2 && /* @__PURE__ */ React60.createElement(ModalSection, { title: "Package details" }, /* @__PURE__ */ React60.createElement("div", { className: "prose prose-sm font-body text-fg-primary max-w-none" }, /* @__PURE__ */ React60.createElement(MarkdownRenderer2, { content: descriptionMarkdown2 }))), p.package_items && p.package_items.length > 0 && /* @__PURE__ */ React60.createElement(ModalSection, { title: "What's included" }, /* @__PURE__ */ React60.createElement("ul", { className: "space-y-4" }, p.package_items.map((pi, i) => {
19439
+ return /* @__PURE__ */ React61.createElement("div", { className: "space-y-6" }, categoryLine && /* @__PURE__ */ React61.createElement("p", { className: "text-sm font-medium text-fg-secondary uppercase tracking-wide" }, categoryLine), /* @__PURE__ */ React61.createElement(ActiveOffersCallout, { offers: packageSpecials }), descriptionMarkdown2 && /* @__PURE__ */ React61.createElement(ModalSection, { title: "Package details" }, /* @__PURE__ */ React61.createElement("div", { className: "prose prose-sm font-body text-fg-primary max-w-none" }, /* @__PURE__ */ React61.createElement(MarkdownRenderer2, { content: descriptionMarkdown2 }))), p.package_items && p.package_items.length > 0 && /* @__PURE__ */ React61.createElement(ModalSection, { title: "What's included" }, /* @__PURE__ */ React61.createElement("ul", { className: "space-y-4" }, p.package_items.map((pi, i) => {
19344
19440
  var _a2, _b, _c, _d, _e, _f, _g, _h, _i;
19345
19441
  const fullItem = ((_a2 = pi.service_item) == null ? void 0 : _a2.id) != null ? serviceItems.find((si2) => si2.id === pi.service_item.id) : null;
19346
19442
  const name = (_d = (_c = fullItem == null ? void 0 : fullItem.name) != null ? _c : (_b = pi.service_item) == null ? void 0 : _b.name) != null ? _d : "Item";
19347
19443
  const desc = (_g = (_e = fullItem == null ? void 0 : fullItem.summary) != null ? _e : fullItem == null ? void 0 : fullItem.description_markdown) != null ? _g : (_f = pi.service_item) == null ? void 0 : _f.summary;
19348
- return /* @__PURE__ */ React60.createElement("li", { key: (_i = (_h = pi.service_item) == null ? void 0 : _h.id) != null ? _i : i, className: "border border-secondary rounded-lg p-4 bg-secondary/20" }, /* @__PURE__ */ React60.createElement("p", { className: "font-display font-medium text-fg-primary" }, pi.quantity > 1 && `${pi.quantity}\xD7 `, name), desc && /* @__PURE__ */ React60.createElement("div", { className: "mt-2 prose prose-sm font-body text-fg-secondary max-w-none" }, typeof desc === "string" && !desc.includes("\n") && !desc.match(/[#*\[\]]/) ? /* @__PURE__ */ React60.createElement("p", null, desc) : /* @__PURE__ */ React60.createElement(MarkdownRenderer2, { content: desc })));
19349
- }))), p.pricing_info && /* @__PURE__ */ React60.createElement(ModalSection, { title: "Pricing" }, /* @__PURE__ */ React60.createElement("div", { className: "rounded-lg border border-secondary bg-secondary/40 p-4" }, /* @__PURE__ */ React60.createElement("div", { className: "prose prose-sm font-body text-fg-primary max-w-none" }, /* @__PURE__ */ React60.createElement(MarkdownRenderer2, { content: p.pricing_info })))));
19444
+ return /* @__PURE__ */ React61.createElement("li", { key: (_i = (_h = pi.service_item) == null ? void 0 : _h.id) != null ? _i : i, className: "border border-secondary rounded-lg p-4 bg-secondary/20" }, /* @__PURE__ */ React61.createElement("p", { className: "font-display font-medium text-fg-primary" }, pi.quantity > 1 && `${pi.quantity}\xD7 `, name), desc && /* @__PURE__ */ React61.createElement("div", { className: "mt-2 prose prose-sm font-body text-fg-secondary max-w-none" }, typeof desc === "string" && !desc.includes("\n") && !desc.match(/[#*\[\]]/) ? /* @__PURE__ */ React61.createElement("p", null, desc) : /* @__PURE__ */ React61.createElement(MarkdownRenderer2, { content: desc })));
19445
+ }))), p.pricing_info && /* @__PURE__ */ React61.createElement(ModalSection, { title: "Pricing" }, /* @__PURE__ */ React61.createElement("div", { className: "rounded-lg border border-secondary bg-secondary/40 p-4" }, /* @__PURE__ */ React61.createElement("div", { className: "prose prose-sm font-body text-fg-primary max-w-none" }, /* @__PURE__ */ React61.createElement(MarkdownRenderer2, { content: p.pricing_info })))));
19350
19446
  }
19351
19447
  const si = detail.item;
19352
19448
  const priceStr = formatPriceCents(si.price_cents);
19353
19449
  const summary = si.summary || si.service_summary;
19354
19450
  const descriptionMarkdown = si.description_markdown || si.service_description_markdown;
19355
19451
  const itemSpecials = getActivePublicOffers(si.offers);
19356
- return /* @__PURE__ */ React60.createElement("div", { className: "space-y-6" }, si.service_name && /* @__PURE__ */ React60.createElement("p", { className: "text-sm font-medium text-fg-secondary uppercase tracking-wide" }, si.service_name), /* @__PURE__ */ React60.createElement(ActiveOffersCallout, { offers: itemSpecials }), priceStr && /* @__PURE__ */ React60.createElement(ModalSection, { title: "Price" }, /* @__PURE__ */ React60.createElement("p", { className: "font-display text-lg font-normal text-fg-primary" }, priceStr), si.duration_minutes != null && si.duration_minutes > 0 && /* @__PURE__ */ React60.createElement("p", { className: "font-body text-sm text-fg-secondary mt-1" }, "Duration: ", si.duration_minutes, " min")), summary && /* @__PURE__ */ React60.createElement(ModalSection, { title: "Overview" }, /* @__PURE__ */ React60.createElement("p", { className: "font-body text-fg-primary" }, summary)), descriptionMarkdown && /* @__PURE__ */ React60.createElement(ModalSection, { title: "Full description" }, /* @__PURE__ */ React60.createElement("div", { className: "prose prose-sm font-body text-fg-primary max-w-none" }, /* @__PURE__ */ React60.createElement(MarkdownRenderer2, { content: descriptionMarkdown }))), si.pricing_info && /* @__PURE__ */ React60.createElement(ModalSection, { title: "Pricing details" }, /* @__PURE__ */ React60.createElement("div", { className: "rounded-lg border border-secondary bg-secondary/40 p-4" }, /* @__PURE__ */ React60.createElement("div", { className: "prose prose-sm font-body text-fg-primary max-w-none" }, /* @__PURE__ */ React60.createElement(MarkdownRenderer2, { content: si.pricing_info })))));
19452
+ return /* @__PURE__ */ React61.createElement("div", { className: "space-y-6" }, si.service_name && /* @__PURE__ */ React61.createElement("p", { className: "text-sm font-medium text-fg-secondary uppercase tracking-wide" }, si.service_name), /* @__PURE__ */ React61.createElement(ActiveOffersCallout, { offers: itemSpecials }), priceStr && /* @__PURE__ */ React61.createElement(ModalSection, { title: "Price" }, /* @__PURE__ */ React61.createElement("p", { className: "font-display text-lg font-normal text-fg-primary" }, priceStr), si.duration_minutes != null && si.duration_minutes > 0 && /* @__PURE__ */ React61.createElement("p", { className: "font-body text-sm text-fg-secondary mt-1" }, "Duration: ", si.duration_minutes, " min")), summary && /* @__PURE__ */ React61.createElement(ModalSection, { title: "Overview" }, /* @__PURE__ */ React61.createElement("p", { className: "font-body text-fg-primary" }, summary)), descriptionMarkdown && /* @__PURE__ */ React61.createElement(ModalSection, { title: "Full description" }, /* @__PURE__ */ React61.createElement("div", { className: "prose prose-sm font-body text-fg-primary max-w-none" }, /* @__PURE__ */ React61.createElement(MarkdownRenderer2, { content: descriptionMarkdown }))), si.pricing_info && /* @__PURE__ */ React61.createElement(ModalSection, { title: "Pricing details" }, /* @__PURE__ */ React61.createElement("div", { className: "rounded-lg border border-secondary bg-secondary/40 p-4" }, /* @__PURE__ */ React61.createElement("div", { className: "prose prose-sm font-body text-fg-primary max-w-none" }, /* @__PURE__ */ React61.createElement(MarkdownRenderer2, { content: si.pricing_info })))));
19357
19453
  }
19358
19454
  function ServiceMenuSection({
19359
19455
  title = "Service Menu",
@@ -19367,20 +19463,20 @@ function ServiceMenuSection({
19367
19463
  servicesRowTitle = "Treatments",
19368
19464
  variant = "section"
19369
19465
  }) {
19370
- const packageList = React60.useMemo(
19466
+ const packageList = React61.useMemo(
19371
19467
  () => Array.isArray(packages) ? packages : [],
19372
19468
  [packages]
19373
19469
  );
19374
- const serviceList = React60.useMemo(
19470
+ const serviceList = React61.useMemo(
19375
19471
  () => Array.isArray(services) ? services : [],
19376
19472
  [services]
19377
19473
  );
19378
- const serviceItemIdToService = React60.useMemo(() => {
19474
+ const serviceItemIdToService = React61.useMemo(() => {
19379
19475
  const m = /* @__PURE__ */ new Map();
19380
19476
  serviceList.forEach((s) => (s.service_items || []).forEach((si) => m.set(si.id, s)));
19381
19477
  return m;
19382
19478
  }, [serviceList]);
19383
- const packagesForMenu = React60.useMemo(
19479
+ const packagesForMenu = React61.useMemo(
19384
19480
  () => packageList.map((pkg) => {
19385
19481
  var _a, _b, _c;
19386
19482
  const category_names = [
@@ -19410,9 +19506,9 @@ function ServiceMenuSection({
19410
19506
  const isPage = variant === "page";
19411
19507
  const packagesLayout = isPage ? "grid" : packagesForMenu.length >= CAROUSEL_MIN ? "carousel" : "grid";
19412
19508
  const serviceItemsLayout = isPage ? "grid" : serviceItemsForMenu.length >= CAROUSEL_MIN ? "carousel" : "grid";
19413
- const [detailItem, setDetailItem] = useState30(null);
19414
- const [portalTarget, setPortalTarget] = useState30(null);
19415
- useEffect11(() => {
19509
+ const [detailItem, setDetailItem] = useState32(null);
19510
+ const [portalTarget, setPortalTarget] = useState32(null);
19511
+ useEffect12(() => {
19416
19512
  let el = document.getElementById(SERVICE_MENU_MODAL_ROOT_ID);
19417
19513
  if (!el) {
19418
19514
  el = document.createElement("div");
@@ -19433,7 +19529,7 @@ function ServiceMenuSection({
19433
19529
  const plainDesc = cardDesc ? cardDesc.replace(/[#*`\[\]]/g, "").replace(/\n+/g, " ").trim() : "";
19434
19530
  const descText = plainDesc.length > 120 ? plainDesc.slice(0, 120) + "\u2026" : plainDesc;
19435
19531
  const hasSpecial = getActivePublicOffers(pkg.offers).length > 0;
19436
- return /* @__PURE__ */ React60.createElement(
19532
+ return /* @__PURE__ */ React61.createElement(
19437
19533
  GridCardWithImage,
19438
19534
  {
19439
19535
  photoAttachments: pkg.photo_attachments,
@@ -19447,7 +19543,7 @@ function ServiceMenuSection({
19447
19543
  cycleSeed: `pkg-${pkg.id}-${index}`,
19448
19544
  hasSpecial
19449
19545
  },
19450
- descText && /* @__PURE__ */ React60.createElement("p", { className: "font-body text-sm text-tertiary mt-1 line-clamp-2" }, descText)
19546
+ descText && /* @__PURE__ */ React61.createElement("p", { className: "font-body text-sm text-tertiary mt-1 line-clamp-2" }, descText)
19451
19547
  );
19452
19548
  },
19453
19549
  [websitePhotos, companyInformation]
@@ -19455,7 +19551,7 @@ function ServiceMenuSection({
19455
19551
  const renderServiceItemCard = useCallback10(
19456
19552
  (si, index) => {
19457
19553
  var _a, _b, _c, _d;
19458
- return /* @__PURE__ */ React60.createElement(
19554
+ return /* @__PURE__ */ React61.createElement(
19459
19555
  GridCardWithImage,
19460
19556
  {
19461
19557
  photoAttachments: si.photo_attachments,
@@ -19469,13 +19565,13 @@ function ServiceMenuSection({
19469
19565
  cycleSeed: `service-item-${si.id}-${index}`,
19470
19566
  hasSpecial: getActivePublicOffers(si.offers).length > 0
19471
19567
  },
19472
- formatPriceCents(si.price_cents) && /* @__PURE__ */ React60.createElement("p", { className: "font-body text-sm font-medium text-fg-primary mt-1" }, formatPriceCents(si.price_cents)),
19568
+ formatPriceCents(si.price_cents) && /* @__PURE__ */ React61.createElement("p", { className: "font-body text-sm font-medium text-fg-primary mt-1" }, formatPriceCents(si.price_cents)),
19473
19569
  (() => {
19474
19570
  const desc = si.summary || si.description_markdown || si.service_summary || si.service_description_markdown;
19475
19571
  if (!desc) return null;
19476
19572
  const plain = desc.replace(/[#*`\[\]]/g, "").replace(/\n+/g, " ").trim();
19477
19573
  const text = plain.length > 120 ? plain.slice(0, 120) + "\u2026" : plain;
19478
- return /* @__PURE__ */ React60.createElement("p", { className: "font-body text-sm text-tertiary mt-1 line-clamp-2" }, text);
19574
+ return /* @__PURE__ */ React61.createElement("p", { className: "font-body text-sm text-tertiary mt-1 line-clamp-2" }, text);
19479
19575
  })()
19480
19576
  );
19481
19577
  },
@@ -19483,7 +19579,7 @@ function ServiceMenuSection({
19483
19579
  );
19484
19580
  if (!hasAny) return null;
19485
19581
  const modalTitle = detailItem ? detailItem.item.name : "";
19486
- return /* @__PURE__ */ React60.createElement("section", { className: variant === "page" ? "py-12 md:py-20" : "py-12 md:py-16" }, /* @__PURE__ */ React60.createElement("div", { className: "mx-auto max-w-container px-4 md:px-8" }, variant === "section" && /* @__PURE__ */ React60.createElement("div", { className: "mx-auto max-w-3xl text-center mb-12 md:mb-16" }, title && /* @__PURE__ */ React60.createElement("h2", { className: "font-display text-4xl font-normal text-fg-primary md:text-5xl" }, title), subtitle && /* @__PURE__ */ React60.createElement("p", { className: "mt-4 font-display text-lg leading-relaxed text-tertiary md:text-xl max-w-3xl mx-auto" }, subtitle)), /* @__PURE__ */ React60.createElement(
19582
+ return /* @__PURE__ */ React61.createElement("section", { className: variant === "page" ? "py-12 md:py-20" : "py-12 md:py-16" }, /* @__PURE__ */ React61.createElement("div", { className: "mx-auto max-w-container px-4 md:px-8" }, variant === "section" && /* @__PURE__ */ React61.createElement("div", { className: "mx-auto max-w-3xl text-center mb-12 md:mb-16" }, title && /* @__PURE__ */ React61.createElement("h2", { className: "font-display text-4xl font-normal text-fg-primary md:text-5xl" }, title), subtitle && /* @__PURE__ */ React61.createElement("p", { className: "mt-4 font-display text-lg leading-relaxed text-tertiary md:text-xl max-w-3xl mx-auto" }, subtitle)), /* @__PURE__ */ React61.createElement(
19487
19583
  MenuBlock,
19488
19584
  {
19489
19585
  rowTitle: "Packages",
@@ -19491,7 +19587,7 @@ function ServiceMenuSection({
19491
19587
  layout: packagesLayout,
19492
19588
  renderItem: renderPackageCard
19493
19589
  }
19494
- ), /* @__PURE__ */ React60.createElement(
19590
+ ), /* @__PURE__ */ React61.createElement(
19495
19591
  MenuBlock,
19496
19592
  {
19497
19593
  rowTitle: servicesRowTitle,
@@ -19499,8 +19595,8 @@ function ServiceMenuSection({
19499
19595
  layout: serviceItemsLayout,
19500
19596
  renderItem: renderServiceItemCard
19501
19597
  }
19502
- ), variant === "section" && viewAllHref && viewAllText && /* @__PURE__ */ React60.createElement("div", { className: "mt-12 text-center" }, /* @__PURE__ */ React60.createElement(Button2, { href: viewAllHref, color: "primary", size: "md" }, viewAllText))), portalTarget && detailItem && createPortal(
19503
- /* @__PURE__ */ React60.createElement(
19598
+ ), variant === "section" && viewAllHref && viewAllText && /* @__PURE__ */ React61.createElement("div", { className: "mt-12 text-center" }, /* @__PURE__ */ React61.createElement(Button2, { href: viewAllHref, color: "primary", size: "md" }, viewAllText))), portalTarget && detailItem && createPortal(
19599
+ /* @__PURE__ */ React61.createElement(
19504
19600
  Modal,
19505
19601
  {
19506
19602
  isOpen: true,
@@ -19508,7 +19604,7 @@ function ServiceMenuSection({
19508
19604
  title: modalTitle,
19509
19605
  maxWidth: "2xl"
19510
19606
  },
19511
- /* @__PURE__ */ React60.createElement(
19607
+ /* @__PURE__ */ React61.createElement(
19512
19608
  DetailModalContent,
19513
19609
  {
19514
19610
  detail: detailItem,
@@ -19528,9 +19624,9 @@ function createThemedExport2(componentName, BaseComponent) {
19528
19624
  const { theme } = useTheme();
19529
19625
  try {
19530
19626
  const Component2 = getThemedComponent(componentName, theme);
19531
- return React61.createElement(Component2, props);
19627
+ return React62.createElement(Component2, props);
19532
19628
  } catch (e) {
19533
- return React61.createElement(BaseComponent, props);
19629
+ return React62.createElement(BaseComponent, props);
19534
19630
  }
19535
19631
  };
19536
19632
  }
@@ -19593,6 +19689,7 @@ export {
19593
19689
  BlogSection5 as BlogSection,
19594
19690
  ContactHome2 as ContactHome,
19595
19691
  ContactSection5 as ContactSection,
19692
+ EmailSignupSection,
19596
19693
  FAQGrid3 as FAQGrid,
19597
19694
  FAQHero3 as FAQHero,
19598
19695
  FAQHome4 as FAQHome,