keystone-design-bootstrap 1.0.55 → 1.0.56
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/design_system/elements/index.js +8 -3
- package/dist/design_system/elements/index.js.map +1 -1
- package/dist/design_system/sections/index.js +203 -106
- package/dist/design_system/sections/index.js.map +1 -1
- package/dist/index.js +303 -247
- package/dist/index.js.map +1 -1
- package/dist/lib/hooks/index.js +72 -0
- package/dist/lib/hooks/index.js.map +1 -1
- package/dist/lib/server-api.js.map +1 -1
- package/dist/utils/phone-helpers.js +26 -0
- package/dist/utils/phone-helpers.js.map +1 -0
- package/package.json +5 -2
- package/src/design_system/components/ChatWidget.tsx +51 -34
- package/src/design_system/components/DynamicFormFields.tsx +1 -24
- package/src/design_system/elements/modal/modal.tsx +54 -35
- package/src/design_system/portal/LoginForm.tsx +339 -0
- package/src/design_system/portal/LoginModalController.tsx +63 -0
- package/src/design_system/portal/LogoutButton.tsx +23 -0
- package/src/design_system/portal/MessageComposer.tsx +84 -0
- package/src/design_system/portal/PortalPage.tsx +754 -0
- package/src/design_system/portal/RowThumbnail.tsx +76 -0
- package/src/design_system/portal/actions.ts +160 -0
- package/src/design_system/portal/index.ts +5 -0
- package/src/design_system/sections/index.tsx +1 -1
- package/src/design_system/sections/service-menu-section.tsx +7 -108
- package/src/lib/actions.ts +51 -115
- package/src/lib/consumer-session.ts +74 -0
- package/src/lib/hooks/index.ts +2 -0
- package/src/lib/hooks/use-image-cycle.ts +105 -0
- package/src/lib/server-api.ts +7 -6
- package/src/next/routes/chat.ts +30 -58
- package/src/next/routes/consumer-auth.ts +113 -0
- package/src/types/api/consumer.ts +39 -0
- package/src/types/api/offer.ts +1 -1
- package/src/types/api/package.ts +20 -0
- package/src/types/api/service.ts +6 -24
- package/src/types/index.ts +2 -0
- package/src/utils/phone-helpers.ts +27 -0
- package/dist/blog-post-DGjaJ3wf.d.ts +0 -50
- package/dist/contexts/index.d.ts +0 -13
- package/dist/design_system/elements/index.d.ts +0 -372
- package/dist/design_system/logo/keystone-logo.d.ts +0 -6
- package/dist/design_system/sections/index.d.ts +0 -237
- package/dist/form-CpsCONG5.d.ts +0 -151
- package/dist/index.d.ts +0 -76
- package/dist/lib/component-registry.d.ts +0 -13
- package/dist/lib/hooks/index.d.ts +0 -64
- package/dist/lib/server-api.d.ts +0 -43
- package/dist/themes/index.d.ts +0 -16
- package/dist/types/index.d.ts +0 -264
- package/dist/utils/cx.d.ts +0 -15
- package/dist/utils/gradient-placeholder.d.ts +0 -8
- package/dist/utils/is-react-component.d.ts +0 -21
- package/dist/utils/markdown-toc.d.ts +0 -14
- package/dist/utils/photo-helpers.d.ts +0 -37
- 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
|
|
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-
|
|
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/
|
|
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)
|
|
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
|
|
19211
|
+
import React61, { useState as useState32, useEffect as useEffect12, useCallback as useCallback10 } from "react";
|
|
19117
19212
|
import { createPortal } from "react-dom";
|
|
19118
|
-
|
|
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
|
-
|
|
19160
|
-
|
|
19161
|
-
return
|
|
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
|
|
19175
|
-
|
|
19176
|
-
|
|
19177
|
-
|
|
19178
|
-
|
|
19179
|
-
|
|
19180
|
-
|
|
19181
|
-
|
|
19182
|
-
|
|
19183
|
-
|
|
19184
|
-
|
|
19185
|
-
|
|
19186
|
-
})
|
|
19187
|
-
|
|
19188
|
-
const
|
|
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
|
-
|
|
19211
|
-
|
|
19212
|
-
|
|
19213
|
-
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
19349
|
+
/* @__PURE__ */ React61.createElement(
|
|
19254
19350
|
"img",
|
|
19255
19351
|
{
|
|
19256
|
-
src: (
|
|
19257
|
-
alt: (
|
|
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__ */
|
|
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__ */
|
|
19363
|
+
/* @__PURE__ */ React61.createElement(
|
|
19268
19364
|
"img",
|
|
19269
19365
|
{
|
|
19270
|
-
src: (
|
|
19271
|
-
alt: (
|
|
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__ */
|
|
19277
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
19328
|
-
o.value_terms ? /* @__PURE__ */
|
|
19329
|
-
o.description ? /* @__PURE__ */
|
|
19330
|
-
o.expires_at ? /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
19349
|
-
}))), p.pricing_info && /* @__PURE__ */
|
|
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__ */
|
|
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 =
|
|
19466
|
+
const packageList = React61.useMemo(
|
|
19371
19467
|
() => Array.isArray(packages) ? packages : [],
|
|
19372
19468
|
[packages]
|
|
19373
19469
|
);
|
|
19374
|
-
const serviceList =
|
|
19470
|
+
const serviceList = React61.useMemo(
|
|
19375
19471
|
() => Array.isArray(services) ? services : [],
|
|
19376
19472
|
[services]
|
|
19377
19473
|
);
|
|
19378
|
-
const serviceItemIdToService =
|
|
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 =
|
|
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] =
|
|
19414
|
-
const [portalTarget, setPortalTarget] =
|
|
19415
|
-
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
19503
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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
|
|
19627
|
+
return React62.createElement(Component2, props);
|
|
19532
19628
|
} catch (e) {
|
|
19533
|
-
return
|
|
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,
|