@mdxui/services 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,1322 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __export = (target, all) => {
3
+ for (var name in all)
4
+ __defProp(target, name, { get: all[name], enumerable: true });
5
+ };
6
+
7
+ // src/shared/scroll-reveal.tsx
8
+ import { useEffect, useRef, useState } from "react";
9
+ import { jsx } from "react/jsx-runtime";
10
+ function ScrollReveal({ children }) {
11
+ const ref = useRef(null);
12
+ const [visible, setVisible] = useState(false);
13
+ useEffect(() => {
14
+ const el = ref.current;
15
+ if (!el) return;
16
+ if (typeof IntersectionObserver === "undefined") {
17
+ setVisible(true);
18
+ return;
19
+ }
20
+ const observer = new IntersectionObserver(
21
+ ([entry]) => {
22
+ if (entry.isIntersecting) {
23
+ setVisible(true);
24
+ observer.disconnect();
25
+ }
26
+ },
27
+ { threshold: 0.1 }
28
+ );
29
+ observer.observe(el);
30
+ return () => observer.disconnect();
31
+ }, []);
32
+ return /* @__PURE__ */ jsx(
33
+ "div",
34
+ {
35
+ ref,
36
+ "data-scroll-reveal": true,
37
+ "data-visible": visible,
38
+ style: {
39
+ opacity: visible ? 1 : 0,
40
+ transition: "opacity var(--motion-fade-duration) var(--motion-fade-easing)"
41
+ },
42
+ children
43
+ }
44
+ );
45
+ }
46
+
47
+ // src/components/Masthead.tsx
48
+ import Link from "next/link";
49
+ import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
50
+ function Masthead({ brand, cta, eyebrow, narrow }) {
51
+ return /* @__PURE__ */ jsx2("header", { className: "border-b border-line", children: /* @__PURE__ */ jsxs(
52
+ "div",
53
+ {
54
+ className: "mx-auto flex items-center justify-between gap-4 px-6 py-5 sm:px-10 " + (narrow ? "max-w-[900px]" : "max-w-container"),
55
+ children: [
56
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
57
+ /* @__PURE__ */ jsx2(
58
+ Link,
59
+ {
60
+ href: brand.href,
61
+ className: "font-display text-[18px] tracking-tightish text-ink",
62
+ children: brand.primary
63
+ }
64
+ ),
65
+ brand.secondary && /* @__PURE__ */ jsxs(Fragment, { children: [
66
+ /* @__PURE__ */ jsx2("span", { className: "hidden h-4 w-px bg-line2 sm:inline-block" }),
67
+ /* @__PURE__ */ jsx2("span", { className: "hidden text-[11px] uppercase tracking-[0.18em] text-ink3 sm:inline", children: brand.secondary })
68
+ ] })
69
+ ] }),
70
+ cta ? /* @__PURE__ */ jsx2(
71
+ Link,
72
+ {
73
+ href: cta.href,
74
+ className: "rounded-cta bg-cta px-4 py-2 text-[12px] font-medium tracking-tightish text-cta-fg transition-colors hover:bg-cta-hover",
75
+ children: cta.label
76
+ }
77
+ ) : eyebrow ? /* @__PURE__ */ jsx2("span", { className: "text-[10.5px] uppercase tracking-[0.18em] text-ink3", children: eyebrow }) : null
78
+ ]
79
+ }
80
+ ) });
81
+ }
82
+
83
+ // src/components/ScrollHeader.tsx
84
+ import { useEffect as useEffect2, useState as useState2 } from "react";
85
+ import { jsx as jsx3 } from "react/jsx-runtime";
86
+ function ScrollHeader({
87
+ threshold = 480,
88
+ ...mastheadProps
89
+ }) {
90
+ const [visible, setVisible] = useState2(false);
91
+ useEffect2(() => {
92
+ let raf = 0;
93
+ const update = () => {
94
+ raf = 0;
95
+ setVisible(window.scrollY > threshold);
96
+ };
97
+ const onScroll = () => {
98
+ if (raf) return;
99
+ raf = requestAnimationFrame(update);
100
+ };
101
+ update();
102
+ window.addEventListener("scroll", onScroll, { passive: true });
103
+ return () => {
104
+ window.removeEventListener("scroll", onScroll);
105
+ if (raf) cancelAnimationFrame(raf);
106
+ };
107
+ }, [threshold]);
108
+ return /* @__PURE__ */ jsx3(
109
+ "div",
110
+ {
111
+ "aria-hidden": !visible,
112
+ className: "pointer-events-none fixed inset-x-0 top-0 z-40 transition-[transform,opacity] duration-[220ms] ease-out-quart " + (visible ? "translate-y-0 opacity-100" : "-translate-y-full opacity-0"),
113
+ children: /* @__PURE__ */ jsx3("div", { className: "pointer-events-auto bg-paper shadow-sheet", children: /* @__PURE__ */ jsx3(Masthead, { ...mastheadProps }) })
114
+ }
115
+ );
116
+ }
117
+
118
+ // src/components/Footer.tsx
119
+ import Link2 from "next/link";
120
+ import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
121
+ function Footer({ text, links }) {
122
+ return /* @__PURE__ */ jsx4("footer", { className: "border-t border-line", children: /* @__PURE__ */ jsxs2("div", { className: "mx-auto flex max-w-container flex-col items-center gap-4 px-6 py-10 text-center text-[12px] text-ink3 sm:flex-row sm:justify-between sm:px-10 sm:text-left", children: [
123
+ /* @__PURE__ */ jsx4("p", { children: text }),
124
+ links && links.length > 0 && /* @__PURE__ */ jsx4("ul", { className: "flex flex-wrap items-center justify-center gap-x-5 gap-y-2 sm:justify-end", children: links.map((l) => /* @__PURE__ */ jsx4("li", { children: /* @__PURE__ */ jsx4(
125
+ Link2,
126
+ {
127
+ href: l.href,
128
+ className: "text-ink3 no-underline transition-colors hover:text-ink",
129
+ children: l.label
130
+ }
131
+ ) }, l.href)) })
132
+ ] }) });
133
+ }
134
+
135
+ // src/components/HeroSplit.tsx
136
+ import Link3 from "next/link";
137
+ import { ChevronRight } from "lucide-react";
138
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
139
+ function HeroSplit({
140
+ eyebrow,
141
+ headline,
142
+ body,
143
+ primary,
144
+ secondary,
145
+ showGlyph = true,
146
+ /* INTENTIONALLY UNDEFAULTED — the smart default below uses the
147
+ * undefined state to distinguish "explicitly unset" from
148
+ * "explicitly false" (the opt-out escape hatch). */
149
+ glyphOnMobile,
150
+ illustration
151
+ }) {
152
+ const effectiveGlyphOnMobile = glyphOnMobile ?? Boolean(illustration);
153
+ return /* @__PURE__ */ jsx5("section", { className: "border-b border-line", children: /* @__PURE__ */ jsx5("div", { className: "mx-auto max-w-container px-6 py-16 sm:px-10 sm:py-28 lg:py-32", children: /* @__PURE__ */ jsxs3("div", { className: showGlyph ? "lg:grid lg:grid-cols-[minmax(0,1fr)_360px] lg:items-center lg:gap-16" : "", children: [
154
+ /* @__PURE__ */ jsxs3("div", { children: [
155
+ /* @__PURE__ */ jsx5("p", { className: "text-[11px] uppercase tracking-[0.18em] text-ink3", children: eyebrow }),
156
+ /* @__PURE__ */ jsx5("h1", { className: "mt-5 max-w-[18ch] font-display text-display-hero leading-[1.04] tracking-tighter2 text-ink", children: headline }),
157
+ /* @__PURE__ */ jsx5("p", { className: "mt-6 max-w-[58ch] text-[16px] leading-relaxed text-ink2 sm:mt-7 sm:text-[17px]", children: body }),
158
+ /* @__PURE__ */ jsxs3("div", { className: "mt-10 flex flex-col items-stretch gap-4 sm:flex-row sm:flex-wrap sm:items-center", children: [
159
+ /* @__PURE__ */ jsx5(PrimaryAction, { action: primary }),
160
+ secondary && /* @__PURE__ */ jsx5(SecondaryAction, { action: secondary })
161
+ ] })
162
+ ] }),
163
+ showGlyph && /* @__PURE__ */ jsx5(
164
+ "div",
165
+ {
166
+ className: effectiveGlyphOnMobile ? "mt-12 lg:mt-0 lg:block" : "hidden lg:block",
167
+ "aria-hidden": "true",
168
+ children: illustration ?? /* @__PURE__ */ jsx5(HeroGlyph, {})
169
+ }
170
+ )
171
+ ] }) }) });
172
+ }
173
+ function PrimaryAction({ action }) {
174
+ const className = "inline-flex w-full items-center justify-center gap-1.5 rounded-cta bg-cta px-5 py-3 text-[14px] font-medium tracking-tightish text-cta-fg transition-colors hover:bg-cta-hover sm:w-auto sm:justify-start";
175
+ if (action.external) {
176
+ return /* @__PURE__ */ jsxs3(
177
+ "a",
178
+ {
179
+ href: action.href,
180
+ target: "_blank",
181
+ rel: "noopener noreferrer",
182
+ className,
183
+ children: [
184
+ action.label,
185
+ /* @__PURE__ */ jsx5(ChevronRight, { className: "h-4 w-4", strokeWidth: 1.7 })
186
+ ]
187
+ }
188
+ );
189
+ }
190
+ return /* @__PURE__ */ jsxs3(Link3, { href: action.href, className, children: [
191
+ action.label,
192
+ /* @__PURE__ */ jsx5(ChevronRight, { className: "h-4 w-4", strokeWidth: 1.7 })
193
+ ] });
194
+ }
195
+ function SecondaryAction({ action }) {
196
+ const className = "inline-flex items-center justify-center gap-1.5 text-[13px] font-medium tracking-tightish text-ink2 transition-colors hover:text-accentDeep sm:justify-start";
197
+ if (action.external) {
198
+ return /* @__PURE__ */ jsxs3(
199
+ "a",
200
+ {
201
+ href: action.href,
202
+ target: "_blank",
203
+ rel: "noopener noreferrer",
204
+ className,
205
+ children: [
206
+ action.label,
207
+ /* @__PURE__ */ jsx5(ChevronRight, { className: "h-3.5 w-3.5", strokeWidth: 1.7 })
208
+ ]
209
+ }
210
+ );
211
+ }
212
+ return /* @__PURE__ */ jsxs3(Link3, { href: action.href, className, children: [
213
+ action.label,
214
+ /* @__PURE__ */ jsx5(ChevronRight, { className: "h-3.5 w-3.5", strokeWidth: 1.7 })
215
+ ] });
216
+ }
217
+ function HeroGlyph() {
218
+ return /* @__PURE__ */ jsxs3(
219
+ "svg",
220
+ {
221
+ viewBox: "0 0 360 280",
222
+ className: "h-auto w-full",
223
+ fill: "none",
224
+ role: "presentation",
225
+ children: [
226
+ /* @__PURE__ */ jsxs3(
227
+ "g",
228
+ {
229
+ stroke: "oklch(var(--ink3) / 0.55)",
230
+ strokeWidth: "1.5",
231
+ strokeLinecap: "round",
232
+ children: [
233
+ /* @__PURE__ */ jsx5("line", { x1: "40", y1: "40", x2: "180", y2: "40" }),
234
+ /* @__PURE__ */ jsx5("line", { x1: "60", y1: "78", x2: "220", y2: "78" }),
235
+ /* @__PURE__ */ jsx5("line", { x1: "40", y1: "172", x2: "200", y2: "172" }),
236
+ /* @__PURE__ */ jsx5("line", { x1: "80", y1: "210", x2: "260", y2: "210" }),
237
+ /* @__PURE__ */ jsx5("line", { x1: "60", y1: "248", x2: "180", y2: "248" })
238
+ ]
239
+ }
240
+ ),
241
+ /* @__PURE__ */ jsx5(
242
+ "line",
243
+ {
244
+ x1: "40",
245
+ y1: "135",
246
+ x2: "280",
247
+ y2: "135",
248
+ stroke: "oklch(var(--accent))",
249
+ strokeWidth: "2.75",
250
+ strokeLinecap: "round"
251
+ }
252
+ ),
253
+ /* @__PURE__ */ jsx5("circle", { cx: "280", cy: "135", r: "4", fill: "oklch(var(--accent-deep))" }),
254
+ /* @__PURE__ */ jsxs3(
255
+ "g",
256
+ {
257
+ stroke: "oklch(var(--accent-deep) / 0.85)",
258
+ strokeWidth: "1",
259
+ strokeLinecap: "round",
260
+ children: [
261
+ /* @__PURE__ */ jsx5("line", { x1: "312", y1: "100", x2: "312", y2: "170" }),
262
+ /* @__PURE__ */ jsx5("line", { x1: "306", y1: "100", x2: "318", y2: "100" }),
263
+ /* @__PURE__ */ jsx5("line", { x1: "306", y1: "170", x2: "318", y2: "170" }),
264
+ /* @__PURE__ */ jsx5("line", { x1: "308", y1: "135", x2: "316", y2: "135" })
265
+ ]
266
+ }
267
+ )
268
+ ]
269
+ }
270
+ );
271
+ }
272
+
273
+ // src/components/HeroStacked.tsx
274
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
275
+ function HeroStacked({
276
+ eyebrow,
277
+ headline,
278
+ body,
279
+ primary,
280
+ secondary,
281
+ illustration
282
+ }) {
283
+ return /* @__PURE__ */ jsx6("section", { className: "border-b border-line", children: /* @__PURE__ */ jsxs4("div", { className: "mx-auto max-w-container px-6 py-16 sm:px-10 sm:py-28 lg:py-32", children: [
284
+ /* @__PURE__ */ jsxs4("div", { className: "text-center", children: [
285
+ /* @__PURE__ */ jsx6("p", { className: "text-[11px] uppercase tracking-[0.18em] text-ink3", children: eyebrow }),
286
+ /* @__PURE__ */ jsx6("h1", { className: "mx-auto mt-5 max-w-[22ch] font-display text-display-hero leading-[1.04] tracking-tighter2 text-ink", children: headline }),
287
+ /* @__PURE__ */ jsx6("p", { className: "mx-auto mt-6 max-w-[60ch] text-[16px] leading-relaxed text-ink2 sm:mt-7 sm:text-[17px]", children: body }),
288
+ /* @__PURE__ */ jsxs4("div", { className: "mt-10 flex flex-col items-stretch justify-center gap-4 sm:flex-row sm:flex-wrap sm:items-center", children: [
289
+ /* @__PURE__ */ jsx6(PrimaryAction, { action: primary }),
290
+ secondary && /* @__PURE__ */ jsx6(SecondaryAction, { action: secondary })
291
+ ] })
292
+ ] }),
293
+ /* @__PURE__ */ jsx6("div", { className: "mx-auto mt-14 max-w-[1080px] sm:mt-16", "aria-hidden": "true", children: illustration })
294
+ ] }) });
295
+ }
296
+
297
+ // src/components/Hero.tsx
298
+ import { jsx as jsx7 } from "react/jsx-runtime";
299
+ function Hero(props) {
300
+ if (props.variant === "stacked-illustration-below" && props.illustration) {
301
+ return /* @__PURE__ */ jsx7(HeroStacked, { ...props });
302
+ }
303
+ return /* @__PURE__ */ jsx7(HeroSplit, { ...props });
304
+ }
305
+
306
+ // src/shared/section-eyebrow.tsx
307
+ import { jsx as jsx8 } from "react/jsx-runtime";
308
+ function SectionEyebrow({ children }) {
309
+ return /* @__PURE__ */ jsx8("p", { className: "text-[11px] uppercase tracking-[0.18em] text-ink3", children });
310
+ }
311
+
312
+ // src/components/Problem.tsx
313
+ import { jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
314
+ function Problem({ eyebrow, heading, items }) {
315
+ return /* @__PURE__ */ jsx9("section", { className: "border-b border-line", children: /* @__PURE__ */ jsxs5("div", { className: "mx-auto max-w-container px-6 py-16 sm:px-10 sm:py-24", children: [
316
+ /* @__PURE__ */ jsx9(SectionEyebrow, { children: eyebrow }),
317
+ /* @__PURE__ */ jsx9("h2", { className: "mt-3 max-w-[22ch] font-display text-display-section leading-[1.08] tracking-tightish text-ink", children: heading }),
318
+ /* @__PURE__ */ jsx9("div", { className: "mt-12 grid gap-8 md:grid-cols-3", children: items.map((item) => /* @__PURE__ */ jsx9(ProblemCard, { ...item }, item.label)) })
319
+ ] }) });
320
+ }
321
+ function ProblemCard({ label, cost, outcome, body }) {
322
+ return /* @__PURE__ */ jsxs5("article", { children: [
323
+ /* @__PURE__ */ jsx9("p", { className: "text-[10.5px] uppercase tracking-[0.16em] text-ink3", children: label }),
324
+ /* @__PURE__ */ jsx9("p", { className: "mt-3 font-display text-[24px] leading-tight tracking-tightish text-ink", children: cost }),
325
+ /* @__PURE__ */ jsx9("p", { className: "mt-4 text-body-sm font-medium leading-snug tracking-tightish text-accentDeep", children: outcome }),
326
+ /* @__PURE__ */ jsx9("p", { className: "mt-3 text-body-sm leading-relaxed text-ink2", children: body })
327
+ ] });
328
+ }
329
+
330
+ // src/components/WhatYouGet.tsx
331
+ import { jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
332
+ function WhatYouGet({ eyebrow, heading, sections }) {
333
+ if (sections.length === 0) return null;
334
+ return /* @__PURE__ */ jsx10("section", { className: "border-b border-line", children: /* @__PURE__ */ jsxs6("div", { className: "mx-auto max-w-container px-6 py-16 sm:px-10 sm:py-24", children: [
335
+ /* @__PURE__ */ jsx10(SectionEyebrow, { children: eyebrow }),
336
+ /* @__PURE__ */ jsx10("h2", { className: "mt-3 max-w-[22ch] font-display text-display-section leading-[1.08] tracking-tightish text-ink", children: heading }),
337
+ /* @__PURE__ */ jsx10("ul", { className: "mt-12 grid gap-x-8 gap-y-7 md:grid-cols-2 lg:grid-cols-3", children: sections.map((s) => /* @__PURE__ */ jsxs6("li", { children: [
338
+ /* @__PURE__ */ jsx10("p", { className: "font-display text-[14px] tracking-tightish text-ink3", children: s.n }),
339
+ /* @__PURE__ */ jsx10("p", { className: "mt-1.5 text-[15px] font-medium tracking-tightish text-ink", children: s.title }),
340
+ /* @__PURE__ */ jsx10("p", { className: "mt-1.5 text-[13.5px] leading-relaxed text-ink2", children: s.body })
341
+ ] }, s.n)) })
342
+ ] }) });
343
+ }
344
+
345
+ // src/components/HowItWorks.tsx
346
+ import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
347
+ function HowItWorks({
348
+ eyebrow,
349
+ heading,
350
+ steps,
351
+ stepIllustrations
352
+ }) {
353
+ if (steps.length === 0) return null;
354
+ const hasIllustrations = stepIllustrations !== void 0 && stepIllustrations.length === steps.length;
355
+ return /* @__PURE__ */ jsx11("section", { className: "border-b border-line", children: /* @__PURE__ */ jsxs7("div", { className: "mx-auto max-w-container px-6 py-16 sm:px-10 sm:py-24", children: [
356
+ /* @__PURE__ */ jsx11(SectionEyebrow, { children: eyebrow }),
357
+ /* @__PURE__ */ jsx11("h2", { className: "mt-3 max-w-[22ch] font-display text-display-section leading-[1.08] tracking-tightish text-ink", children: heading }),
358
+ /* @__PURE__ */ jsx11(
359
+ "ol",
360
+ {
361
+ itemScope: true,
362
+ itemType: "https://schema.org/HowTo",
363
+ className: "mt-12 grid gap-10 md:grid-cols-3",
364
+ children: steps.map((s, i) => /* @__PURE__ */ jsx11(
365
+ Step,
366
+ {
367
+ ...s,
368
+ illustration: hasIllustrations ? stepIllustrations[i] : void 0
369
+ },
370
+ s.n
371
+ ))
372
+ }
373
+ )
374
+ ] }) });
375
+ }
376
+ function Step({
377
+ n,
378
+ title,
379
+ body,
380
+ illustration: Illustration
381
+ }) {
382
+ return /* @__PURE__ */ jsxs7(
383
+ "li",
384
+ {
385
+ itemScope: true,
386
+ itemType: "https://schema.org/HowToStep",
387
+ className: Illustration ? "flex flex-col" : void 0,
388
+ children: [
389
+ Illustration && /* @__PURE__ */ jsx11("div", { className: "mb-6 w-full lg:max-w-[320px]", children: /* @__PURE__ */ jsx11(Illustration, {}) }),
390
+ /* @__PURE__ */ jsxs7("div", { className: Illustration ? "mt-auto" : void 0, children: [
391
+ /* @__PURE__ */ jsx11("p", { className: "font-display text-[36px] leading-none tracking-tighter2 text-ink3/60", children: n }),
392
+ /* @__PURE__ */ jsx11("p", { className: "mt-4 font-display text-[20px] leading-tight tracking-tightish text-ink", children: title }),
393
+ /* @__PURE__ */ jsx11("p", { className: "mt-3 text-body-sm leading-relaxed text-ink2", children: body })
394
+ ] })
395
+ ]
396
+ }
397
+ );
398
+ }
399
+
400
+ // src/components/Defensibility.tsx
401
+ import { jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
402
+ function Defensibility({
403
+ eyebrow,
404
+ heading,
405
+ columns,
406
+ caveat
407
+ }) {
408
+ return /* @__PURE__ */ jsx12("section", { className: "border-b border-line", children: /* @__PURE__ */ jsxs8("div", { className: "mx-auto max-w-container px-6 py-16 sm:px-10 sm:py-24", children: [
409
+ /* @__PURE__ */ jsx12(SectionEyebrow, { children: eyebrow }),
410
+ /* @__PURE__ */ jsx12("h2", { className: "mt-3 max-w-[22ch] font-display text-display-section leading-[1.08] tracking-tightish text-ink", children: heading }),
411
+ /* @__PURE__ */ jsx12("div", { className: "mt-10 grid gap-10 lg:grid-cols-2", children: columns.map((col) => /* @__PURE__ */ jsx12(Column, { ...col }, col.heading)) }),
412
+ caveat && /* @__PURE__ */ jsx12("p", { className: "mt-12 max-w-[68ch] text-body-xs leading-relaxed text-ink3", children: caveat })
413
+ ] }) });
414
+ }
415
+ function Column({ heading, bullets }) {
416
+ return /* @__PURE__ */ jsxs8("div", { children: [
417
+ /* @__PURE__ */ jsx12("h3", { className: "font-display text-[18px] leading-tight tracking-tightish text-ink", children: heading }),
418
+ /* @__PURE__ */ jsx12("ul", { className: "mt-4 space-y-3 text-body-sm leading-relaxed text-ink2", children: bullets.map((b, i) => /* @__PURE__ */ jsxs8("li", { className: "flex gap-2.5", children: [
419
+ /* @__PURE__ */ jsx12("span", { className: "mt-2 h-1 w-1 shrink-0 rounded-full bg-ink3" }),
420
+ /* @__PURE__ */ jsx12("span", { children: b })
421
+ ] }, i)) })
422
+ ] });
423
+ }
424
+
425
+ // src/components/Pricing.tsx
426
+ import Link4 from "next/link";
427
+ import { ChevronRight as ChevronRight2 } from "lucide-react";
428
+ import { Fragment as Fragment2, jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
429
+ var GRID_COLS = {
430
+ 1: "max-w-[420px]",
431
+ 2: "max-w-[820px] md:grid-cols-2",
432
+ 3: "max-w-container md:grid-cols-3"
433
+ };
434
+ function Pricing(props) {
435
+ if ("kind" in props && props.kind === "unit") {
436
+ return /* @__PURE__ */ jsx13(UnitPricing, { ...props });
437
+ }
438
+ return /* @__PURE__ */ jsx13(TieredPricing, { ...props });
439
+ }
440
+ function TieredPricing({
441
+ eyebrow,
442
+ heading,
443
+ tiers,
444
+ footnote
445
+ }) {
446
+ if (tiers.length === 0) return null;
447
+ const count = tiers.length === 1 || tiers.length === 2 ? tiers.length : 3;
448
+ return /* @__PURE__ */ jsx13("section", { className: "border-b border-line bg-surface", children: /* @__PURE__ */ jsxs9("div", { className: "mx-auto max-w-container px-6 py-16 sm:px-10 sm:py-24", children: [
449
+ /* @__PURE__ */ jsx13(SectionEyebrow, { children: eyebrow }),
450
+ /* @__PURE__ */ jsx13("h2", { className: "mt-3 max-w-[22ch] font-display text-display-section leading-[1.08] tracking-tightish text-ink", children: heading }),
451
+ /* @__PURE__ */ jsx13("div", { className: `mt-12 mx-auto grid gap-6 ${GRID_COLS[count]}`, children: tiers.map((t) => /* @__PURE__ */ jsx13(PriceCard, { ...t }, t.tier)) }),
452
+ footnote && /* @__PURE__ */ jsx13("p", { className: "mt-10 max-w-[64ch] text-[13px] text-ink3", children: footnote })
453
+ ] }) });
454
+ }
455
+ function formatPerUnit(perUnit, currency) {
456
+ const isWhole = Number.isInteger(perUnit);
457
+ return new Intl.NumberFormat("en-US", {
458
+ style: "currency",
459
+ currency: currency.toUpperCase(),
460
+ minimumFractionDigits: isWhole ? 0 : 2,
461
+ maximumFractionDigits: 2
462
+ }).format(perUnit);
463
+ }
464
+ function UnitPricing({
465
+ eyebrow,
466
+ heading,
467
+ footnote,
468
+ currency,
469
+ perUnit,
470
+ billingCadence
471
+ }) {
472
+ const formatted = formatPerUnit(perUnit, currency);
473
+ return /* @__PURE__ */ jsx13("section", { className: "border-b border-line bg-surface", children: /* @__PURE__ */ jsxs9("div", { className: "mx-auto max-w-container px-6 py-16 sm:px-10 sm:py-24", children: [
474
+ /* @__PURE__ */ jsx13(SectionEyebrow, { children: eyebrow }),
475
+ /* @__PURE__ */ jsx13("h2", { className: "mt-3 max-w-[22ch] font-display text-display-section leading-[1.08] tracking-tightish text-ink", children: heading }),
476
+ /* @__PURE__ */ jsx13("div", { className: "mt-12 mx-auto max-w-[420px]", children: /* @__PURE__ */ jsxs9(
477
+ "article",
478
+ {
479
+ itemScope: true,
480
+ itemType: "https://schema.org/Offer",
481
+ "data-pricing-kind": "unit",
482
+ className: "flex flex-col rounded-card border border-line bg-paper p-7",
483
+ children: [
484
+ /* @__PURE__ */ jsx13("p", { className: "text-[10.5px] uppercase tracking-[0.16em] text-ink3", children: "Per call" }),
485
+ /* @__PURE__ */ jsxs9("p", { className: "mt-3 flex items-baseline gap-2", children: [
486
+ /* @__PURE__ */ jsx13("span", { className: "font-display text-[40px] leading-none tracking-tighter2 text-ink", children: formatted }),
487
+ /* @__PURE__ */ jsx13("span", { className: "text-[12px] text-ink3", children: billingCadence })
488
+ ] })
489
+ ]
490
+ }
491
+ ) }),
492
+ footnote && /* @__PURE__ */ jsx13("p", { className: "mt-10 max-w-[64ch] text-[13px] text-ink3", children: footnote })
493
+ ] }) });
494
+ }
495
+ function splitPrice(price) {
496
+ const i = price.indexOf("/");
497
+ if (i === -1) return { core: price, suffix: null };
498
+ return { core: price.slice(0, i).trim(), suffix: price.slice(i) };
499
+ }
500
+ function resolvePriceSuffix(price, override) {
501
+ if (override !== void 0) return override;
502
+ const { suffix } = splitPrice(price);
503
+ return suffix ?? "/ report";
504
+ }
505
+ function PriceCard({
506
+ tierKey,
507
+ tier,
508
+ price,
509
+ priceSuffix,
510
+ audience,
511
+ features,
512
+ featured,
513
+ cta
514
+ }) {
515
+ const isMailto = cta.href.startsWith("mailto:");
516
+ const { core } = splitPrice(price);
517
+ const suffix = resolvePriceSuffix(price, priceSuffix);
518
+ return /* @__PURE__ */ jsxs9(
519
+ "article",
520
+ {
521
+ itemScope: true,
522
+ itemType: "https://schema.org/Offer",
523
+ "data-tier-key": tierKey,
524
+ className: "flex flex-col rounded-card border bg-paper p-7 " + (featured ? "border-accent shadow-lift" : "border-line"),
525
+ children: [
526
+ /* @__PURE__ */ jsx13("p", { className: "text-[10.5px] uppercase tracking-[0.16em] text-ink3", children: tier }),
527
+ /* @__PURE__ */ jsxs9("p", { className: "mt-3 flex items-baseline gap-2", children: [
528
+ /* @__PURE__ */ jsx13("span", { className: "font-display text-[40px] leading-none tracking-tighter2 text-ink", children: core }),
529
+ suffix && /* @__PURE__ */ jsx13("span", { className: "text-[12px] text-ink3", children: suffix })
530
+ ] }),
531
+ /* @__PURE__ */ jsx13("p", { className: "mt-2 text-[13px] text-ink2", children: audience }),
532
+ /* @__PURE__ */ jsx13("ul", { className: "mt-6 flex-1 space-y-2.5 text-[13.5px] text-ink2", children: features.map((f) => /* @__PURE__ */ jsxs9("li", { className: "flex gap-2.5", children: [
533
+ /* @__PURE__ */ jsx13(
534
+ "span",
535
+ {
536
+ className: "mt-2 h-1 w-1 shrink-0 rounded-full " + (featured ? "bg-accent" : "bg-ink3")
537
+ }
538
+ ),
539
+ /* @__PURE__ */ jsx13("span", { children: f })
540
+ ] }, f)) }),
541
+ /* @__PURE__ */ jsx13(
542
+ PriceCardCta,
543
+ {
544
+ label: cta.label,
545
+ href: cta.href,
546
+ featured,
547
+ external: isMailto
548
+ }
549
+ )
550
+ ]
551
+ }
552
+ );
553
+ }
554
+ function PriceCardCta({
555
+ label,
556
+ href,
557
+ featured,
558
+ external
559
+ }) {
560
+ const baseClass = "mt-7 inline-flex w-full items-center justify-center gap-1.5 rounded-cta px-5 py-2.5 text-[13.5px] font-medium tracking-tightish transition-colors";
561
+ const variantClass = featured ? "bg-cta text-cta-fg hover:bg-cta-hover" : "border border-line2 bg-paper text-ink hover:border-ink hover:text-ink";
562
+ const className = `${baseClass} ${variantClass}`;
563
+ const body = /* @__PURE__ */ jsxs9(Fragment2, { children: [
564
+ label,
565
+ /* @__PURE__ */ jsx13(ChevronRight2, { className: "h-3.5 w-3.5", strokeWidth: 1.7 })
566
+ ] });
567
+ if (external) {
568
+ return /* @__PURE__ */ jsx13("a", { className, href, "data-action": "buy", children: body });
569
+ }
570
+ return /* @__PURE__ */ jsx13(Link4, { className, href, "data-action": "buy", children: body });
571
+ }
572
+
573
+ // src/components/Faq.tsx
574
+ import { jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
575
+ function Faq({ eyebrow, heading, items }) {
576
+ if (items.length === 0) return null;
577
+ return /* @__PURE__ */ jsx14("section", { className: "border-b border-line", children: /* @__PURE__ */ jsxs10("div", { className: "mx-auto max-w-container px-6 py-16 sm:px-10 sm:py-24", children: [
578
+ /* @__PURE__ */ jsx14(SectionEyebrow, { children: eyebrow }),
579
+ /* @__PURE__ */ jsx14("h2", { className: "mt-3 max-w-[20ch] font-display text-display-section leading-[1.08] tracking-tightish text-ink", children: heading }),
580
+ /* @__PURE__ */ jsx14("dl", { className: "mt-12 grid gap-x-12 gap-y-10 md:grid-cols-2", children: items.map((f) => /* @__PURE__ */ jsxs10("div", { itemScope: true, itemType: "https://schema.org/Question", children: [
581
+ /* @__PURE__ */ jsx14("dt", { className: "font-display text-body leading-snug tracking-tightish text-ink", children: f.q }),
582
+ /* @__PURE__ */ jsx14("dd", { className: "mt-2 text-body-sm leading-relaxed text-ink2", children: f.a })
583
+ ] }, f.q)) })
584
+ ] }) });
585
+ }
586
+
587
+ // src/components/FinalCta.tsx
588
+ import Link5 from "next/link";
589
+ import { ChevronRight as ChevronRight3 } from "lucide-react";
590
+ import { Fragment as Fragment3, jsx as jsx15, jsxs as jsxs11 } from "react/jsx-runtime";
591
+ function FinalCta({
592
+ eyebrow,
593
+ headline,
594
+ primary,
595
+ secondary
596
+ }) {
597
+ return /* @__PURE__ */ jsx15("section", { children: /* @__PURE__ */ jsxs11("div", { className: "mx-auto max-w-container px-6 py-20 text-center sm:px-10 sm:py-32", children: [
598
+ /* @__PURE__ */ jsx15("p", { className: "text-[11px] uppercase tracking-[0.18em] text-ink3", children: eyebrow }),
599
+ /* @__PURE__ */ jsx15("h2", { className: "mx-auto mt-4 max-w-[22ch] font-display text-[36px] leading-[1.04] tracking-tighter2 text-ink sm:text-[52px]", children: headline }),
600
+ /* @__PURE__ */ jsxs11("div", { className: "mt-10 flex flex-col items-stretch gap-4 sm:flex-row sm:flex-wrap sm:items-center sm:justify-center", children: [
601
+ /* @__PURE__ */ jsx15(PrimaryButton, { action: primary }),
602
+ secondary && /* @__PURE__ */ jsx15(SecondaryLink, { action: secondary })
603
+ ] })
604
+ ] }) });
605
+ }
606
+ function PrimaryButton({ action }) {
607
+ const className = "inline-flex w-full items-center justify-center gap-1.5 rounded-cta bg-cta px-6 py-3 text-[14px] font-medium tracking-tightish text-cta-fg transition-colors hover:bg-cta-hover sm:w-auto";
608
+ const body = /* @__PURE__ */ jsxs11(Fragment3, { children: [
609
+ action.label,
610
+ /* @__PURE__ */ jsx15(ChevronRight3, { className: "h-4 w-4", strokeWidth: 1.7 })
611
+ ] });
612
+ if (action.external) {
613
+ return /* @__PURE__ */ jsx15(
614
+ "a",
615
+ {
616
+ href: action.href,
617
+ target: "_blank",
618
+ rel: "noopener noreferrer",
619
+ className,
620
+ children: body
621
+ }
622
+ );
623
+ }
624
+ return /* @__PURE__ */ jsx15(Link5, { href: action.href, className, children: body });
625
+ }
626
+ function SecondaryLink({ action }) {
627
+ const className = "inline-flex items-center justify-center gap-1.5 text-[13px] font-medium tracking-tightish text-ink3 transition-colors hover:text-ink";
628
+ if (action.external) {
629
+ return /* @__PURE__ */ jsx15(
630
+ "a",
631
+ {
632
+ href: action.href,
633
+ target: "_blank",
634
+ rel: "noopener noreferrer",
635
+ className,
636
+ children: action.label
637
+ }
638
+ );
639
+ }
640
+ return /* @__PURE__ */ jsx15(Link5, { href: action.href, className, children: action.label });
641
+ }
642
+
643
+ // src/views/ServicesLandingView.tsx
644
+ import { Fragment as Fragment4, jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
645
+ function ServicesLandingView({ content }) {
646
+ return /* @__PURE__ */ jsxs12(Fragment4, { children: [
647
+ /* @__PURE__ */ jsx16(Masthead, { ...content.masthead }),
648
+ /* @__PURE__ */ jsx16(
649
+ ScrollHeader,
650
+ {
651
+ ...content.masthead,
652
+ threshold: content.scrollHeaderThreshold
653
+ }
654
+ ),
655
+ /* @__PURE__ */ jsxs12("main", { children: [
656
+ /* @__PURE__ */ jsx16(ScrollReveal, { children: /* @__PURE__ */ jsx16(Hero, { ...content.hero, showGlyph: content.heroShowGlyph }) }),
657
+ content.problem && /* @__PURE__ */ jsx16(ScrollReveal, { children: /* @__PURE__ */ jsx16(Problem, { ...content.problem }) }),
658
+ content.whatYouGet && /* @__PURE__ */ jsx16(ScrollReveal, { children: /* @__PURE__ */ jsx16(WhatYouGet, { ...content.whatYouGet }) }),
659
+ content.howItWorks && /* @__PURE__ */ jsx16(ScrollReveal, { children: /* @__PURE__ */ jsx16(HowItWorks, { ...content.howItWorks }) }),
660
+ content.defensibility && /* @__PURE__ */ jsx16(ScrollReveal, { children: /* @__PURE__ */ jsx16(Defensibility, { ...content.defensibility }) }),
661
+ content.pricing && /* @__PURE__ */ jsx16(ScrollReveal, { children: /* @__PURE__ */ jsx16(Pricing, { ...content.pricing }) }),
662
+ content.faq && /* @__PURE__ */ jsx16(ScrollReveal, { children: /* @__PURE__ */ jsx16(Faq, { ...content.faq }) }),
663
+ /* @__PURE__ */ jsx16(ScrollReveal, { children: /* @__PURE__ */ jsx16(FinalCta, { ...content.finalCta }) })
664
+ ] }),
665
+ /* @__PURE__ */ jsx16(Footer, { ...content.footer })
666
+ ] });
667
+ }
668
+
669
+ // src/shared/svg-inline.tsx
670
+ import { jsx as jsx17 } from "react/jsx-runtime";
671
+ function SvgInline({ svg }) {
672
+ return /* @__PURE__ */ jsx17(
673
+ "div",
674
+ {
675
+ className: "h-auto w-full",
676
+ dangerouslySetInnerHTML: { __html: svg }
677
+ }
678
+ );
679
+ }
680
+
681
+ // src/parse-props.ts
682
+ var MdxuiParseError = class extends Error {
683
+ issues;
684
+ source;
685
+ constructor(zodError, source) {
686
+ const issues = zodError.issues.map((issue) => ({
687
+ path: issue.path.join(".") || "(root)",
688
+ message: issue.message
689
+ }));
690
+ const lines = issues.map((i) => ` ${i.path}: ${i.message}`);
691
+ const header = source ? `[@mdxui/services] parseProps failed (source: ${source})` : "[@mdxui/services] parseProps failed";
692
+ super(`${header}
693
+ ${lines.join("\n")}`);
694
+ this.name = "MdxuiParseError";
695
+ this.issues = issues;
696
+ this.source = source;
697
+ }
698
+ };
699
+ function parseProps(schema, data, opts) {
700
+ const result = schema.safeParse(data);
701
+ if (result.success) return result.data;
702
+ throw new MdxuiParseError(result.error, opts?.source);
703
+ }
704
+
705
+ // src/schemas/index.ts
706
+ var schemas_exports = {};
707
+ __export(schemas_exports, {
708
+ BreadcrumbSchema: () => BreadcrumbSchema,
709
+ CatalogHeroSchema: () => CatalogHeroSchema,
710
+ CatalogShapeFullSchema: () => CatalogShapeFullSchema,
711
+ CatalogShapeSchema: () => CatalogShapeSchema,
712
+ CheckoutPanelPropsSchema: () => CheckoutPanelPropsSchema,
713
+ CtaSchema: () => CtaSchema,
714
+ DefensibilityColumnSchema: () => DefensibilityColumnSchema,
715
+ DefensibilityPropsSchema: () => DefensibilityPropsSchema,
716
+ DeliveryShapeSchema: () => DeliveryShapeSchema,
717
+ DeliveryUxSchema: () => DeliveryUxSchema,
718
+ FaqItemSchema: () => FaqItemSchema,
719
+ FaqPropsExtendedSchema: () => FaqPropsExtendedSchema,
720
+ FieldRowPropsSchema: () => FieldRowPropsSchema,
721
+ FileInputSchema: () => FileInputSchema,
722
+ FinalCtaPropsSchema: () => FinalCtaPropsSchema,
723
+ HeroPropsExtendedSchema: () => HeroPropsExtendedSchema,
724
+ HeroVariantSchema: () => HeroVariantSchema,
725
+ HowItWorksPropsSchema: () => HowItWorksPropsSchema,
726
+ HowItWorksStepSchema: () => HowItWorksStepSchema,
727
+ ImageUploadFieldPropsSchema: () => ImageUploadFieldPropsSchema,
728
+ LandingPricingSchema: () => LandingPricingSchema,
729
+ LandingPublicCopySchema: () => LandingPublicCopySchema,
730
+ LogoUploadPropsSchema: () => LogoUploadPropsSchema,
731
+ MastheadPropsSchema: () => MastheadPropsSchema,
732
+ OrderFieldSchema: () => OrderFieldSchema,
733
+ OrderFlowSchema: () => OrderFlowSchema,
734
+ OrderGroupSchema: () => OrderGroupSchema,
735
+ OrderLegalSchema: () => OrderLegalSchema,
736
+ OrderShapeSchema: () => OrderShapeSchema,
737
+ OrderStepSchema: () => OrderStepSchema,
738
+ PortalActionKindSchema: () => PortalActionKindSchema,
739
+ PortalActionSchema: () => PortalActionSchema,
740
+ PortalColumnSchema: () => PortalColumnSchema,
741
+ PortalColumnTypeSchema: () => PortalColumnTypeSchema,
742
+ PortalFilterSchema: () => PortalFilterSchema,
743
+ PortalShapeSchema: () => PortalShapeSchema,
744
+ PreviewModeSchema: () => PreviewModeSchema,
745
+ PriceTierSchema: () => PriceTierSchema,
746
+ PricingPropsExtendedSchema: () => PricingPropsExtendedSchema,
747
+ PricingSchema: () => PricingSchema,
748
+ PricingSummarySchema: () => PricingSummarySchema,
749
+ ProblemItemSchema: () => ProblemItemSchema,
750
+ ProblemPropsSchema: () => ProblemPropsSchema,
751
+ RadioGroupInputSchema: () => RadioGroupInputSchema,
752
+ RadioGroupOptionSchema: () => RadioGroupOptionSchema,
753
+ RadioGroupPropsSchema: () => RadioGroupPropsSchema,
754
+ SelectFieldPropsSchema: () => SelectFieldPropsSchema,
755
+ SelectInputSchema: () => SelectInputSchema,
756
+ ServiceArchetypeIdSchema: () => ServiceArchetypeIdSchema,
757
+ ServiceBrandingSchema: () => ServiceBrandingSchema,
758
+ ServiceFormPropsSchema: () => ServiceFormPropsSchema,
759
+ ServiceSchemaSpecSchema: () => ServiceSchemaSpecSchema,
760
+ SiteActionSchema: () => SiteActionSchema,
761
+ SiteFooterLinkSchema: () => SiteFooterLinkSchema,
762
+ SiteFooterPropsSchema: () => SiteFooterPropsSchema,
763
+ StepHeadingPropsSchema: () => StepHeadingPropsSchema,
764
+ TextFieldPropsSchema: () => TextFieldPropsSchema,
765
+ TextInputSchema: () => TextInputSchema,
766
+ UpsellBannerPropsSchema: () => UpsellBannerPropsSchema,
767
+ UpsellCardPropsSchema: () => UpsellCardPropsSchema,
768
+ WhatYouGetItemSchema: () => WhatYouGetItemSchema,
769
+ WhatYouGetPropsSchema: () => WhatYouGetPropsSchema
770
+ });
771
+ import { z } from "zod";
772
+ import {
773
+ TextInputPropsSchema,
774
+ SelectInputPropsSchema,
775
+ RadioGroupInputPropsSchema,
776
+ FileInputPropsSchema
777
+ } from "mdxui";
778
+ var CatalogHeroSchema = z.object({
779
+ /** Main landing-page headline. Falls back to `service.name` when unset. */
780
+ headline: z.string().optional(),
781
+ /** Sub-headline. Falls back to `service.promise` when unset. */
782
+ subheadline: z.string().optional(),
783
+ /** Asset reference for a visual element resolved by the renderer.
784
+ * Today Carriage uses per-product HeroGlyph SVGs keyed by slug. */
785
+ visual: z.string().optional()
786
+ });
787
+ var PricingSummarySchema = z.enum([
788
+ "starting-at",
789
+ "per-call",
790
+ "tier-comparison",
791
+ "contact-us"
792
+ ]);
793
+ var CatalogShapeSchema = z.object({
794
+ hero: CatalogHeroSchema.optional(),
795
+ pricingSummary: PricingSummarySchema
796
+ });
797
+ var TextInputSchema = TextInputPropsSchema.extend({
798
+ source: z.string().optional()
799
+ });
800
+ var SelectInputSchema = SelectInputPropsSchema.extend({
801
+ source: z.string().optional()
802
+ });
803
+ var RadioGroupInputSchema = RadioGroupInputPropsSchema.extend({
804
+ source: z.string().optional()
805
+ });
806
+ var FileInputSchema = FileInputPropsSchema.extend({
807
+ source: z.string().optional()
808
+ });
809
+ var OrderFlowSchema = z.enum([
810
+ "instant",
811
+ "guided",
812
+ "consultation-first"
813
+ ]);
814
+ var OrderFieldSchema = z.object({
815
+ name: z.string(),
816
+ type: z.enum(["string", "number", "integer", "boolean"]),
817
+ required: z.boolean(),
818
+ title: z.string().optional(),
819
+ description: z.string().optional(),
820
+ enum: z.array(z.union([z.string(), z.number(), z.boolean()])).readonly().optional(),
821
+ pattern: z.string().optional(),
822
+ format: z.enum(["date", "date-time", "email", "uri"]).optional(),
823
+ minimum: z.number().optional(),
824
+ maximum: z.number().optional(),
825
+ widget: z.enum(["vin-decoder", "image-upload"]).optional(),
826
+ row: z.number().optional(),
827
+ placeholder: z.string().optional(),
828
+ autocomplete: z.string().optional(),
829
+ options: z.array(z.object({
830
+ value: z.union([z.string(), z.number(), z.boolean()]),
831
+ label: z.string().optional(),
832
+ sub: z.string().optional()
833
+ })).readonly().optional(),
834
+ tiers: z.array(z.string()).readonly().optional(),
835
+ /** Phase 1b multi-substrate (Carriage Iter / master 2026-05-26):
836
+ * forwarded from JSON Schema's `x-connect` vendor annotation. When
837
+ * set, the chassis form renders `<ConnectFlow>` instead of a
838
+ * TextField. upstream-proposal: §PR11-theme + §x-connect. */
839
+ xConnect: z.object({
840
+ providerId: z.string(),
841
+ resourceType: z.enum([
842
+ "channel",
843
+ "crm-record",
844
+ "sequence",
845
+ "phone-number",
846
+ "voice-line",
847
+ "webhook-event",
848
+ "database-table",
849
+ "folder",
850
+ "mailbox",
851
+ "account"
852
+ ])
853
+ }).optional(),
854
+ /** Phase 1b multi-substrate: forwarded from JSON Schema's
855
+ * `x-derived-from` vendor annotation. When set, the chassis form
856
+ * suppresses input UI and auto-populates from connection metadata.
857
+ * upstream-proposal: §PR11-theme + §x-derived-from. */
858
+ xDerivedFrom: z.object({
859
+ providerId: z.string(),
860
+ metadataKey: z.string()
861
+ }).optional()
862
+ });
863
+ var OrderGroupSchema = z.object({
864
+ id: z.string(),
865
+ title: z.string(),
866
+ description: z.string().optional(),
867
+ fields: z.array(OrderFieldSchema).readonly()
868
+ });
869
+ var OrderStepSchema = z.object({
870
+ id: z.string(),
871
+ title: z.string().optional(),
872
+ fields: z.array(OrderFieldSchema).readonly(),
873
+ groups: z.array(OrderGroupSchema).readonly()
874
+ });
875
+ var OrderLegalSchema = z.object({
876
+ termsRequired: z.boolean(),
877
+ privacyRequired: z.boolean()
878
+ });
879
+ var OrderShapeSchema = z.object({
880
+ flow: OrderFlowSchema,
881
+ steps: z.array(OrderStepSchema).readonly().optional(),
882
+ legal: OrderLegalSchema.optional()
883
+ });
884
+ var DeliveryUxSchema = z.enum([
885
+ "sync-result-card",
886
+ "document-download",
887
+ "async-batch-status"
888
+ ]);
889
+ var PreviewModeSchema = z.enum([
890
+ "first-page",
891
+ "first-row",
892
+ "inline-summary",
893
+ "none"
894
+ ]);
895
+ var DeliveryShapeSchema = z.object({
896
+ ux: DeliveryUxSchema,
897
+ previewMode: PreviewModeSchema,
898
+ /** Whether to show a step-by-step progress timeline while the cascade runs. */
899
+ progressTimeline: z.boolean(),
900
+ /** Optional SLA echo — ISO 8601 duration (e.g. "PT2M"). */
901
+ estimatedDelivery: z.string().optional(),
902
+ /** Optional outcome predicate lifted from outcomeContract. */
903
+ outcomePredicate: z.string().optional()
904
+ });
905
+ var PortalColumnTypeSchema = z.enum([
906
+ "identifier",
907
+ "timestamp",
908
+ "currency",
909
+ "duration",
910
+ "status",
911
+ "text",
912
+ "evaluator"
913
+ ]);
914
+ var PortalColumnSchema = z.object({
915
+ id: z.string(),
916
+ label: z.string(),
917
+ type: PortalColumnTypeSchema
918
+ });
919
+ var PortalActionKindSchema = z.enum([
920
+ "view-deliverable",
921
+ "reissue",
922
+ "refund",
923
+ "flag-for-review"
924
+ ]);
925
+ var PortalActionSchema = z.object({
926
+ id: z.string(),
927
+ label: z.string(),
928
+ kind: PortalActionKindSchema
929
+ });
930
+ var PortalFilterSchema = z.object({
931
+ id: z.string(),
932
+ field: z.string(),
933
+ label: z.string()
934
+ });
935
+ var PortalShapeSchema = z.object({
936
+ columns: z.array(PortalColumnSchema),
937
+ filters: z.array(PortalFilterSchema).optional(),
938
+ actions: z.array(PortalActionSchema).optional()
939
+ });
940
+ var SiteActionSchema = z.object({
941
+ label: z.string(),
942
+ href: z.string(),
943
+ external: z.boolean().optional()
944
+ });
945
+ var HeroVariantSchema = z.enum([
946
+ "split-illustration-right",
947
+ "stacked-illustration-below"
948
+ ]);
949
+ var HeroPropsExtendedSchema = z.object({
950
+ eyebrow: z.string(),
951
+ headline: z.string(),
952
+ body: z.string(),
953
+ primary: SiteActionSchema,
954
+ secondary: SiteActionSchema.optional(),
955
+ showGlyph: z.boolean().optional(),
956
+ /** When true, the glyph also renders below `lg` (stacked beneath
957
+ * the hero text on mobile). When false (default), the glyph is
958
+ * hidden below `lg`. Services opt in via `publicCopy.hero.glyphOnMobile`.
959
+ * upstream-proposal: docs/patterns/upstream-proposals.md §PR11-theme */
960
+ glyphOnMobile: z.boolean().optional(),
961
+ illustration: z.any().optional(),
962
+ /** Hero composition variant. Routes the `<Hero>` dispatcher to
963
+ * `HeroSplit` (default) or `HeroStacked`. Sourced from
964
+ * `ServiceDefinition.branding.heroVariant`.
965
+ * upstream-proposal: docs/patterns/upstream-proposals.md §PR12-hero-variant */
966
+ variant: HeroVariantSchema.optional()
967
+ });
968
+ var PriceTierSchema = z.object({
969
+ tierKey: z.string(),
970
+ tier: z.string(),
971
+ price: z.string(),
972
+ audience: z.string(),
973
+ features: z.array(z.string()),
974
+ featured: z.boolean().optional(),
975
+ cta: z.object({ label: z.string(), href: z.string() }),
976
+ sku: z.string(),
977
+ amountCents: z.number(),
978
+ currency: z.enum(["usd", "eur", "gbp"]),
979
+ stripeName: z.string(),
980
+ minimumAmountCents: z.number().optional(),
981
+ priceSuffix: z.string().optional()
982
+ });
983
+ var PricingPropsExtendedSchema = z.object({
984
+ eyebrow: z.string(),
985
+ heading: z.string(),
986
+ tiers: z.array(PriceTierSchema).readonly(),
987
+ footnote: z.string().optional()
988
+ });
989
+ var FaqItemSchema = z.object({
990
+ q: z.string(),
991
+ a: z.string()
992
+ });
993
+ var FaqPropsExtendedSchema = z.object({
994
+ eyebrow: z.string(),
995
+ heading: z.string(),
996
+ items: z.array(FaqItemSchema)
997
+ });
998
+ var HowItWorksStepSchema = z.object({
999
+ n: z.string(),
1000
+ title: z.string(),
1001
+ body: z.string()
1002
+ });
1003
+ var HowItWorksPropsSchema = z.object({
1004
+ eyebrow: z.string(),
1005
+ heading: z.string(),
1006
+ steps: z.array(HowItWorksStepSchema),
1007
+ /** Optional mini-illustration per step. When provided and length
1008
+ * matches `steps.length`, each step renders its illustration in
1009
+ * a row above the number. All-or-none — if length doesn't match,
1010
+ * the prop is silently ignored and the text-only layout renders
1011
+ * (per opt-in-only semantics in illustrations.md §2).
1012
+ *
1013
+ * Sourced from the per-service illustration manifest at
1014
+ * `src/services/<slug>/illustration.tsx`.
1015
+ *
1016
+ * Each element is a React `ComponentType` (zero-arg renderer).
1017
+ * Zod can't validate component identity at runtime; type-level
1018
+ * contract carries via `z.custom`.
1019
+ *
1020
+ * upstream-proposal: docs/patterns/upstream-proposals.md §PR12-step-illustrations
1021
+ * (master 2026-05-26 — illustration abstraction Phase 4). */
1022
+ stepIllustrations: z.array(z.custom()).optional()
1023
+ });
1024
+ var WhatYouGetItemSchema = z.object({
1025
+ n: z.string(),
1026
+ title: z.string(),
1027
+ body: z.string()
1028
+ });
1029
+ var WhatYouGetPropsSchema = z.object({
1030
+ eyebrow: z.string(),
1031
+ heading: z.string(),
1032
+ sections: z.array(WhatYouGetItemSchema)
1033
+ });
1034
+ var FinalCtaPropsSchema = z.object({
1035
+ eyebrow: z.string(),
1036
+ headline: z.string(),
1037
+ primary: SiteActionSchema,
1038
+ secondary: SiteActionSchema.optional()
1039
+ });
1040
+ var MastheadPropsSchema = z.object({
1041
+ brand: z.object({
1042
+ primary: z.string(),
1043
+ secondary: z.string().optional(),
1044
+ href: z.string()
1045
+ }),
1046
+ cta: z.object({ label: z.string(), href: z.string() }).optional(),
1047
+ eyebrow: z.string().optional(),
1048
+ narrow: z.boolean().optional()
1049
+ });
1050
+ var SiteFooterLinkSchema = z.object({
1051
+ label: z.string(),
1052
+ href: z.string()
1053
+ });
1054
+ var SiteFooterPropsSchema = z.object({
1055
+ text: z.string(),
1056
+ links: z.array(SiteFooterLinkSchema).optional()
1057
+ });
1058
+ var TextFieldPropsSchema = z.object({
1059
+ label: z.string(),
1060
+ value: z.string(),
1061
+ onChange: z.custom(),
1062
+ placeholder: z.string().optional(),
1063
+ /** Helper text shown below the field. `invalid=true` renders it in error colour. */
1064
+ hint: z.string().optional(),
1065
+ type: z.enum(["text", "date", "number", "email"]).optional(),
1066
+ /** Right-edge suffix label (e.g. "miles", "%"). */
1067
+ suffix: z.string().optional(),
1068
+ /** Renders the input in a monospace typeface (e.g. VIN, code). */
1069
+ mono: z.boolean().optional(),
1070
+ /** Renders the field in error state (red border + hint text). */
1071
+ invalid: z.boolean().optional(),
1072
+ inputRef: z.custom().optional(),
1073
+ /** HTML `autocomplete` attribute for browser autofill. */
1074
+ autoComplete: z.string().optional()
1075
+ });
1076
+ var SelectFieldPropsSchema = z.object({
1077
+ label: z.string(),
1078
+ value: z.string(),
1079
+ onChange: z.custom(),
1080
+ options: z.array(z.object({ value: z.string(), label: z.string() })),
1081
+ /** HTML `autocomplete` attribute for browser autofill. */
1082
+ autoComplete: z.string().optional()
1083
+ });
1084
+ var RadioGroupOptionSchema = z.object({
1085
+ value: z.string(),
1086
+ label: z.string(),
1087
+ /** Optional secondary descriptor line rendered inside the card. */
1088
+ sub: z.string().optional()
1089
+ });
1090
+ var RadioGroupPropsSchema = z.object({
1091
+ label: z.string(),
1092
+ name: z.string(),
1093
+ value: z.string(),
1094
+ onChange: z.custom(),
1095
+ options: z.array(RadioGroupOptionSchema)
1096
+ });
1097
+ var FieldRowPropsSchema = z.object({
1098
+ children: z.custom()
1099
+ });
1100
+ var StepHeadingPropsSchema = z.object({
1101
+ /** Numeric step indicator, e.g. "01", "02". Rendered in tabular mono. */
1102
+ index: z.string(),
1103
+ /** Short action-verb eyebrow label. e.g. "Connect", "Compose". */
1104
+ eyebrow: z.string(),
1105
+ /** Primary section heading. */
1106
+ title: z.string(),
1107
+ /** Supporting body copy below the heading. */
1108
+ body: z.string(),
1109
+ className: z.string().optional()
1110
+ });
1111
+ var LogoUploadPropsSchema = z.object({
1112
+ /** Resolved Vercel-Blob URL, or null when no logo is set. */
1113
+ value: z.string().nullable(),
1114
+ /** True while the upload network call is in-flight. */
1115
+ uploading: z.boolean(),
1116
+ /** Error message from the last failed upload attempt, or null. */
1117
+ error: z.string().nullable(),
1118
+ onChange: z.custom(),
1119
+ onClear: z.custom(),
1120
+ /** Widget label override (default: "Firm logo (optional)"). */
1121
+ label: z.string().optional(),
1122
+ /** Body copy override below the preview thumbnail. */
1123
+ bodyText: z.string().optional()
1124
+ });
1125
+ var ImageUploadFieldPropsSchema = z.object({
1126
+ /** Renderer label — sourced from `OrderField.title`. */
1127
+ label: z.string(),
1128
+ /** Helper text — sourced from `OrderField.description`. */
1129
+ description: z.string().optional(),
1130
+ /** Current value (resolved Vercel-Blob URL, or null). */
1131
+ value: z.string().nullable(),
1132
+ /** Called with the new URL after a successful upload, or null on clear. */
1133
+ onChange: z.custom()
1134
+ });
1135
+ var PricingSectionChromeSchema = z.object({
1136
+ eyebrow: z.string(),
1137
+ heading: z.string(),
1138
+ footnote: z.string().optional(),
1139
+ currency: z.enum(["usd", "eur", "gbp"])
1140
+ });
1141
+ var PricingTieredSchema = PricingSectionChromeSchema.extend({
1142
+ kind: z.literal("tiered"),
1143
+ tiers: z.array(PriceTierSchema).readonly()
1144
+ });
1145
+ var PricingUnitSchema = PricingSectionChromeSchema.extend({
1146
+ kind: z.literal("unit"),
1147
+ perUnit: z.number(),
1148
+ billingCadence: z.string()
1149
+ });
1150
+ var PricingSchema = z.union([PricingTieredSchema, PricingUnitSchema]);
1151
+ var ServiceFormPropsSchema = z.object({
1152
+ /** Service slug — used in the `/api/checkout` body + return-URL construction. */
1153
+ slug: z.string(),
1154
+ /** Derived order shape from `deriveOrder(svc)`. */
1155
+ order: OrderShapeSchema,
1156
+ /** Service pricing — from `ServiceDefinition.pricing`. Mirrors the `Pricing` union. */
1157
+ pricing: PricingSchema,
1158
+ /** Form-phase headline (h1 above the fields). Optional — omit in unit tests. */
1159
+ formHeadline: z.string().optional(),
1160
+ /** Form-phase intro paragraph. Optional. */
1161
+ formIntro: z.string().optional(),
1162
+ /** Product-name eyebrow (e.g. "Carriage Donation") rendered above the h1. */
1163
+ productName: z.string().optional(),
1164
+ /** Payment-phase h1. Defaults to "Generate your report" when unset. */
1165
+ paymentHeadline: z.string().optional()
1166
+ });
1167
+ var CheckoutPanelPropsSchema = z.object({
1168
+ /** Stripe PaymentIntent client secret from /api/checkout. */
1169
+ clientSecret: z.string(),
1170
+ /** Payment amount in cents — displayed as "Pay $X.XX & {ctaSuffix}". */
1171
+ amountCents: z.number(),
1172
+ /** Email pre-fill passed to Stripe as receipt email. */
1173
+ email: z.string().optional(),
1174
+ /** Where Stripe redirects after a successful payment (redirect flow). */
1175
+ returnUrl: z.string().optional(),
1176
+ /** Optional inline-confirmation callback (inline flow — bulk products). */
1177
+ onSuccess: z.custom().optional(),
1178
+ /** Custom CTA suffix on the Pay button. Default: "generate report". */
1179
+ ctaSuffix: z.string().optional(),
1180
+ /** "Edit details" callback — re-shows the form. */
1181
+ onCancel: z.custom()
1182
+ });
1183
+ var DefensibilityColumnSchema = z.object({
1184
+ heading: z.string(),
1185
+ /** Array of bullet content. Each bullet is a ReactNode — strings in
1186
+ * practice today; rich content possible in future JSON upgrades. */
1187
+ bullets: z.array(z.custom())
1188
+ });
1189
+ var DefensibilityPropsSchema = z.object({
1190
+ eyebrow: z.string(),
1191
+ heading: z.string(),
1192
+ /** Tuple of exactly two columns (left = "what it is", right = "what it isn't"). */
1193
+ columns: z.tuple([DefensibilityColumnSchema, DefensibilityColumnSchema]),
1194
+ /** Optional closing caveat paragraph. */
1195
+ caveat: z.custom().optional()
1196
+ });
1197
+ var UpsellBannerPropsSchema = z.object({
1198
+ /** Display-serif hook line. Often phrased as a question. */
1199
+ headline: z.string(),
1200
+ /** Optional one-line explanation below the headline. */
1201
+ body: z.string().optional(),
1202
+ /** Activation pill content — ReactNode for inline price transitions. */
1203
+ cta: z.custom(),
1204
+ onActivate: z.custom()
1205
+ });
1206
+ var UpsellCardPropsSchema = z.object({
1207
+ eyebrow: z.string(),
1208
+ /** Card body — ReactNode for future rich-text upgrades. */
1209
+ body: z.custom(),
1210
+ ctaLabel: z.string(),
1211
+ onActivate: z.custom()
1212
+ });
1213
+ var CtaSchema = z.object({
1214
+ label: z.string(),
1215
+ href: z.string()
1216
+ });
1217
+ var BreadcrumbSchema = z.object({
1218
+ name: z.string(),
1219
+ href: z.string()
1220
+ });
1221
+ var ServiceSchemaSpecSchema = z.object({
1222
+ name: z.string(),
1223
+ serviceType: z.string(),
1224
+ url: z.string(),
1225
+ areaServed: z.string()
1226
+ });
1227
+ var ProblemItemSchema = z.object({
1228
+ label: z.string(),
1229
+ cost: z.string(),
1230
+ outcome: z.string(),
1231
+ body: z.string()
1232
+ });
1233
+ var ProblemPropsSchema = z.object({
1234
+ eyebrow: z.string(),
1235
+ heading: z.string(),
1236
+ items: z.array(ProblemItemSchema)
1237
+ });
1238
+ var ServiceBrandingSchema = z.object({
1239
+ primary: z.string(),
1240
+ secondary: z.string().optional(),
1241
+ productName: z.string(),
1242
+ /** SERP title default for the landing page. */
1243
+ defaultTitle: z.string(),
1244
+ /** OG/SERP description default. */
1245
+ defaultDescription: z.string(),
1246
+ heroGlyph: z.string().optional(),
1247
+ showHeroGlyph: z.boolean().optional(),
1248
+ og: z.object({ headline: z.string() })
1249
+ });
1250
+ var LandingPublicCopySchema = z.object({
1251
+ pageTitle: z.string(),
1252
+ pageDescription: z.string(),
1253
+ headerCta: CtaSchema,
1254
+ hero: HeroPropsExtendedSchema,
1255
+ problem: ProblemPropsSchema,
1256
+ whatYouGet: WhatYouGetPropsSchema,
1257
+ howItWorks: HowItWorksPropsSchema,
1258
+ defensibility: DefensibilityPropsSchema,
1259
+ faq: FaqPropsExtendedSchema,
1260
+ finalCta: FinalCtaPropsSchema,
1261
+ footerText: z.string(),
1262
+ serviceSchema: ServiceSchemaSpecSchema,
1263
+ breadcrumbs: z.array(BreadcrumbSchema).optional()
1264
+ });
1265
+ var LandingPricingSchema = z.object({
1266
+ kind: z.enum(["tiered", "unit"]),
1267
+ eyebrow: z.string(),
1268
+ heading: z.string(),
1269
+ footnote: z.string().optional(),
1270
+ currency: z.enum(["usd", "eur", "gbp"]).optional(),
1271
+ tiers: z.array(PriceTierSchema).readonly().optional(),
1272
+ /** unit pricing fields — optional; only present when kind='unit' */
1273
+ perUnit: z.number().optional(),
1274
+ billingCadence: z.string().optional()
1275
+ });
1276
+ var ServiceArchetypeIdSchema = z.enum([
1277
+ "service-archetype-document-extraction",
1278
+ "service-archetype-data-enrichment",
1279
+ "service-archetype-transactional-action",
1280
+ "service-archetype-sourced-comparative-analysis",
1281
+ "service-archetype-form-preparation",
1282
+ "service-archetype-compliance-check",
1283
+ "service-archetype-market-intelligence",
1284
+ "service-archetype-communication-automation"
1285
+ ]);
1286
+ var CatalogShapeFullSchema = CatalogShapeSchema.extend({
1287
+ /** Full landing-page section copy. Present for all fully-absorbed services. */
1288
+ publicCopy: LandingPublicCopySchema.optional(),
1289
+ /** Product branding (wordmark + heroGlyph + og headline). */
1290
+ branding: ServiceBrandingSchema.optional(),
1291
+ /** Commercial pricing — section chrome + tiers. */
1292
+ pricing: LandingPricingSchema.optional(),
1293
+ /** Service archetype id — drives MIME/totalTime defaults at render time. */
1294
+ archetype: ServiceArchetypeIdSchema.optional()
1295
+ });
1296
+ export {
1297
+ Defensibility,
1298
+ Faq,
1299
+ FinalCta,
1300
+ Footer,
1301
+ Hero,
1302
+ HeroSplit,
1303
+ HeroStacked,
1304
+ HowItWorks,
1305
+ Masthead,
1306
+ MdxuiParseError,
1307
+ Pricing,
1308
+ PrimaryAction,
1309
+ Problem,
1310
+ Pricing as ReportPricing,
1311
+ ScrollHeader,
1312
+ ScrollReveal,
1313
+ SecondaryAction,
1314
+ SectionEyebrow,
1315
+ Hero as ServiceHero,
1316
+ ServicesLandingView,
1317
+ SvgInline,
1318
+ WhatYouGet,
1319
+ parseProps,
1320
+ schemas_exports as schemas
1321
+ };
1322
+ //# sourceMappingURL=index.js.map