@dreamtree-org/landing-kit 0.0.0-stage → 0.2.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,1117 @@
1
+ 'use client';
2
+
3
+ // src/LandingPage.jsx
4
+ import { Fragment as Fragment4, cloneElement, isValidElement as isValidElement2 } from "react";
5
+
6
+ // src/AuthShell.jsx
7
+ import { useId } from "react";
8
+ import { jsx, jsxs } from "react/jsx-runtime";
9
+ var DEFAULT_LEGAL = [
10
+ { label: "Privacy Policy", href: "/privacy" },
11
+ { label: "Terms", href: "/terms" },
12
+ { label: "Data Policy", href: "/data-policy" }
13
+ ];
14
+ function BrandMark({ brand, size }) {
15
+ if (brand.logoSrc) {
16
+ return /* @__PURE__ */ jsx("img", { className: "dt-auth-mark-img", src: brand.logoSrc, alt: "", width: size, height: size });
17
+ }
18
+ if (brand.mark) return /* @__PURE__ */ jsx("span", { className: "dt-auth-mark-node", children: brand.mark });
19
+ return /* @__PURE__ */ jsx("span", { className: "dt-auth-monogram", style: { width: size, height: size }, "aria-hidden": "true", children: brand.name.charAt(0) });
20
+ }
21
+ function brandStyle(brand) {
22
+ return {
23
+ "--dt-brand": brand.primary,
24
+ "--dt-brand-accent": brand.accent || brand.primary,
25
+ ...brand.panel ? { "--dt-brand-panel": brand.panel } : {}
26
+ };
27
+ }
28
+ function AuthShell({
29
+ brand,
30
+ title,
31
+ subtitle,
32
+ children,
33
+ footer,
34
+ variant = "tenant",
35
+ legalLinks = DEFAULT_LEGAL,
36
+ linkComponent: LinkComponent,
37
+ className = ""
38
+ }) {
39
+ var _a;
40
+ const headingId = useId();
41
+ const isPlatform = variant === "platform";
42
+ const headline = isPlatform ? brand.platformHeadline || "Platform administration" : brand.headline;
43
+ const pitch = isPlatform ? brand.platformPitch || `Super administrator console for ${brand.name}. Every action here is audited.` : brand.pitch;
44
+ const renderLink = (l) => LinkComponent ? /* @__PURE__ */ jsx(LinkComponent, { to: l.href, href: l.href, className: "dt-auth-legal-link", children: l.label }, l.href) : /* @__PURE__ */ jsx("a", { href: l.href, className: "dt-auth-legal-link", children: l.label }, l.href);
45
+ return /* @__PURE__ */ jsxs(
46
+ "div",
47
+ {
48
+ className: `dt-auth ${isPlatform ? "dt-auth--platform" : ""} ${className}`,
49
+ style: brandStyle(brand),
50
+ "data-product": brand.id,
51
+ children: [
52
+ /* @__PURE__ */ jsxs("aside", { className: "dt-auth-panel", children: [
53
+ /* @__PURE__ */ jsxs("a", { href: "/", className: "dt-auth-brand", children: [
54
+ /* @__PURE__ */ jsx(BrandMark, { brand, size: 28 }),
55
+ /* @__PURE__ */ jsx("span", { className: "dt-auth-brand-name", children: brand.name }),
56
+ isPlatform ? /* @__PURE__ */ jsx("span", { className: "dt-auth-badge", children: "Platform" }) : null
57
+ ] }),
58
+ /* @__PURE__ */ jsxs("div", { className: "dt-auth-pitch", children: [
59
+ /* @__PURE__ */ jsx("h2", { children: headline }),
60
+ pitch ? /* @__PURE__ */ jsx("p", { children: pitch }) : null,
61
+ !isPlatform && ((_a = brand.points) == null ? void 0 : _a.length) ? /* @__PURE__ */ jsx("ul", { children: brand.points.map((p) => /* @__PURE__ */ jsx("li", { children: p }, p)) }) : null
62
+ ] }),
63
+ /* @__PURE__ */ jsx("p", { className: "dt-auth-panel-foot", children: brand.category || "by Dreamtree-Org" })
64
+ ] }),
65
+ /* @__PURE__ */ jsx("main", { className: "dt-auth-main", children: /* @__PURE__ */ jsxs("div", { className: "dt-auth-column", children: [
66
+ /* @__PURE__ */ jsxs("a", { href: "/", className: "dt-auth-brand dt-auth-brand--mobile", children: [
67
+ /* @__PURE__ */ jsx(BrandMark, { brand, size: 24 }),
68
+ /* @__PURE__ */ jsx("span", { className: "dt-auth-brand-name", children: brand.name }),
69
+ isPlatform ? /* @__PURE__ */ jsx("span", { className: "dt-auth-badge", children: "Platform" }) : null
70
+ ] }),
71
+ /* @__PURE__ */ jsxs("section", { className: "dt-auth-card", "aria-labelledby": headingId, children: [
72
+ /* @__PURE__ */ jsxs("header", { className: "dt-auth-head", children: [
73
+ /* @__PURE__ */ jsx("h1", { id: headingId, children: title }),
74
+ subtitle ? /* @__PURE__ */ jsx("p", { children: subtitle }) : null
75
+ ] }),
76
+ children
77
+ ] }),
78
+ footer ? /* @__PURE__ */ jsx("div", { className: "dt-auth-footer", children: footer }) : null,
79
+ (legalLinks == null ? void 0 : legalLinks.length) ? /* @__PURE__ */ jsx("nav", { className: "dt-auth-legal", "aria-label": "Legal", children: legalLinks.map((l, i) => /* @__PURE__ */ jsxs("span", { className: "dt-auth-legal-item", children: [
80
+ i > 0 ? /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\xB7" }) : null,
81
+ renderLink(l)
82
+ ] }, l.href)) }) : null
83
+ ] }) })
84
+ ]
85
+ }
86
+ );
87
+ }
88
+
89
+ // src/context.jsx
90
+ import { createContext, useContext } from "react";
91
+ import { jsx as jsx2 } from "react/jsx-runtime";
92
+ var LandingContext = createContext({ brand: null, linkComponent: null, onBookDemo: null, home: true });
93
+ var LandingProvider = LandingContext.Provider;
94
+ var useLanding = () => useContext(LandingContext);
95
+ function resolveHref(href, home) {
96
+ if (!href) return href;
97
+ return !home && href.startsWith("#") ? `/${href}` : href;
98
+ }
99
+ function SmartLink({ href, children, className, reload, ...rest }) {
100
+ const { linkComponent: Link, home } = useLanding();
101
+ const to = resolveHref(href, home);
102
+ const internal = to && to.startsWith("/") && !to.startsWith("//") && !to.includes("#");
103
+ if (Link && internal && !reload) {
104
+ return /* @__PURE__ */ jsx2(Link, { to, href: to, className, ...rest, children });
105
+ }
106
+ return /* @__PURE__ */ jsx2("a", { href: to, className, ...rest, children });
107
+ }
108
+
109
+ // src/Header.jsx
110
+ import { useState } from "react";
111
+
112
+ // src/icons.jsx
113
+ import { createElement, isValidElement } from "react";
114
+ import { Fragment, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
115
+ var base = {
116
+ viewBox: "0 0 24 24",
117
+ fill: "none",
118
+ stroke: "currentColor",
119
+ strokeWidth: 2,
120
+ strokeLinecap: "round",
121
+ strokeLinejoin: "round",
122
+ "aria-hidden": "true",
123
+ focusable: "false"
124
+ };
125
+ var make = (paths) => function Icon({ size = 16, className = "dt-lp-icon" }) {
126
+ return /* @__PURE__ */ jsx3("svg", { ...base, width: size, height: size, className, children: paths });
127
+ };
128
+ var ArrowRight = make(/* @__PURE__ */ jsx3("path", { d: "M5 12h14M13 6l6 6-6 6" }));
129
+ var ChevronRight = make(/* @__PURE__ */ jsx3("path", { d: "m9 6 6 6-6 6" }));
130
+ var Check = make(/* @__PURE__ */ jsx3("path", { d: "M20 6 9 17l-5-5" }));
131
+ var Help = make(
132
+ /* @__PURE__ */ jsxs2(Fragment, { children: [
133
+ /* @__PURE__ */ jsx3("circle", { cx: "12", cy: "12", r: "10" }),
134
+ /* @__PURE__ */ jsx3("path", { d: "M9.1 9a3 3 0 0 1 5.8 1c0 2-3 3-3 3M12 17h.01" })
135
+ ] })
136
+ );
137
+ var Alert = make(
138
+ /* @__PURE__ */ jsxs2(Fragment, { children: [
139
+ /* @__PURE__ */ jsx3("path", { d: "M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0Z" }),
140
+ /* @__PURE__ */ jsx3("path", { d: "M12 9v4M12 17h.01" })
141
+ ] })
142
+ );
143
+ var MenuIcon = make(/* @__PURE__ */ jsx3("path", { d: "M4 6h16M4 12h16M4 18h16" }));
144
+ var Close = make(/* @__PURE__ */ jsx3("path", { d: "M18 6 6 18M6 6l12 12" }));
145
+ var Calendar = make(
146
+ /* @__PURE__ */ jsxs2(Fragment, { children: [
147
+ /* @__PURE__ */ jsx3("rect", { x: "3", y: "4", width: "18", height: "18", rx: "2" }),
148
+ /* @__PURE__ */ jsx3("path", { d: "M16 2v4M8 2v4M3 10h18m-12 6 2 2 4-4" })
149
+ ] })
150
+ );
151
+ function renderIcon(icon, size = 20, className = "dt-lp-icon") {
152
+ if (!icon) return null;
153
+ if (isValidElement(icon)) return icon;
154
+ return createElement(icon, { size, "aria-hidden": true, className, strokeWidth: 1.8 });
155
+ }
156
+
157
+ // src/Brand.jsx
158
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
159
+ function BrandMark2({ brand, size = 36 }) {
160
+ if (brand.logoSrc) {
161
+ return /* @__PURE__ */ jsx4("img", { className: "dt-lp-mark-img", src: brand.logoSrc, alt: "", width: size, height: size });
162
+ }
163
+ if (brand.mark) return /* @__PURE__ */ jsx4("span", { className: "dt-lp-mark-node", "aria-hidden": "true", children: brand.mark });
164
+ return /* @__PURE__ */ jsx4("span", { className: "dt-lp-monogram", style: { width: size, height: size }, "aria-hidden": "true", children: brand.name.charAt(0) });
165
+ }
166
+ function Wordmark({ name }) {
167
+ const m = /^(.+?)(Pro)$/.exec(name);
168
+ if (!m) return /* @__PURE__ */ jsx4("span", { className: "dt-lp-wordmark", children: name });
169
+ return /* @__PURE__ */ jsxs3("span", { className: "dt-lp-wordmark", children: [
170
+ m[1],
171
+ /* @__PURE__ */ jsx4("span", { className: "dt-lp-wordmark-hi", children: m[2] })
172
+ ] });
173
+ }
174
+ function BrandLockup({ brand: own, byline = "by Dreamtree-Org", href = "/" }) {
175
+ const ctx = useLanding();
176
+ const brand = own || ctx.brand;
177
+ if (!brand) return null;
178
+ return /* @__PURE__ */ jsxs3("a", { href, className: "dt-lp-brand", "aria-label": `${brand.name}${byline ? ` ${byline}` : ""}, home`, children: [
179
+ /* @__PURE__ */ jsx4(BrandMark2, { brand }),
180
+ /* @__PURE__ */ jsxs3("span", { className: "dt-lp-brand-text", "aria-hidden": "true", children: [
181
+ /* @__PURE__ */ jsx4(Wordmark, { name: brand.name }),
182
+ byline ? /* @__PURE__ */ jsx4("span", { className: "dt-lp-byline", children: byline }) : null
183
+ ] })
184
+ ] });
185
+ }
186
+
187
+ // src/BookDemoButton.jsx
188
+ import { jsx as jsx5 } from "react/jsx-runtime";
189
+ function BookDemoButton({ size = "md", variant = "primary", className = "", children = "Book a demo", onClick }) {
190
+ const { onBookDemo } = useLanding();
191
+ const handler = onClick || (onBookDemo ? (e) => onBookDemo(e.currentTarget) : void 0);
192
+ return /* @__PURE__ */ jsx5(
193
+ "button",
194
+ {
195
+ type: "button",
196
+ className: `dt-lp-btn dt-lp-btn--${variant} dt-lp-btn--${size} ${className}`.trim(),
197
+ "data-book-demo": "",
198
+ "aria-haspopup": "dialog",
199
+ onClick: handler,
200
+ children
201
+ }
202
+ );
203
+ }
204
+
205
+ // src/Section.jsx
206
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
207
+ function Wrap({ className = "", narrow, children }) {
208
+ return /* @__PURE__ */ jsx6("div", { className: `dt-lp-wrap${narrow ? " dt-lp-wrap--narrow" : ""} ${className}`.trim(), children });
209
+ }
210
+ function Eyebrow({ children, className = "" }) {
211
+ if (!children) return null;
212
+ return /* @__PURE__ */ jsx6("p", { className: `dt-lp-eyebrow ${className}`.trim(), children });
213
+ }
214
+ function SectionIntro({ id, eyebrow, title, body, align = "start", children }) {
215
+ return /* @__PURE__ */ jsxs4("div", { className: `dt-lp-intro dt-lp-intro--${align}`, children: [
216
+ /* @__PURE__ */ jsx6(Eyebrow, { children: eyebrow }),
217
+ /* @__PURE__ */ jsx6("h2", { id, className: "dt-lp-h2", children: title }),
218
+ body ? /* @__PURE__ */ jsx6("p", { className: "dt-lp-lead", children: body }) : null,
219
+ children
220
+ ] });
221
+ }
222
+ function Section({ id, labelledBy, tone = "plain", className = "", children, narrow }) {
223
+ return /* @__PURE__ */ jsx6("section", { id, "aria-labelledby": labelledBy, className: `dt-lp-section dt-lp-tone--${tone} ${className}`.trim(), children: /* @__PURE__ */ jsx6(Wrap, { narrow, children }) });
224
+ }
225
+ function TextLink({ href, children, className = "", ...rest }) {
226
+ return /* @__PURE__ */ jsxs4(SmartLink, { href, className: `dt-lp-textlink ${className}`.trim(), ...rest, children: [
227
+ children,
228
+ " ",
229
+ /* @__PURE__ */ jsx6(ArrowRight, {})
230
+ ] });
231
+ }
232
+ function Cta({ cta, className = "" }) {
233
+ if (!cta) return null;
234
+ if (Array.isArray(cta)) {
235
+ if (!cta.length) return null;
236
+ return /* @__PURE__ */ jsx6("div", { className: `dt-lp-ctas ${className}`.trim(), children: cta.map((c2, i) => /* @__PURE__ */ jsx6(Cta, { cta: c2 }, i)) });
237
+ }
238
+ const c = typeof cta === "string" ? { label: cta, demo: true } : cta;
239
+ if (c.href && !c.demo) return /* @__PURE__ */ jsx6(TextLink, { href: c.href, className, children: c.label });
240
+ return /* @__PURE__ */ jsxs4(BookDemoButton, { size: "lg", className, children: [
241
+ c.label,
242
+ " ",
243
+ /* @__PURE__ */ jsx6(ArrowRight, {})
244
+ ] });
245
+ }
246
+
247
+ // src/Header.jsx
248
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
249
+ function Header({ nav = [], login = { label: "Log in", href: "/login" }, cta = "Book a demo", brand }) {
250
+ const { home } = useLanding();
251
+ const [open, setOpen] = useState(false);
252
+ const items = login ? [...nav, login] : nav;
253
+ return /* @__PURE__ */ jsxs5("header", { className: "dt-lp-header", children: [
254
+ /* @__PURE__ */ jsxs5(Wrap, { className: "dt-lp-header-bar", children: [
255
+ /* @__PURE__ */ jsx7(BrandLockup, { brand }),
256
+ nav.length ? /* @__PURE__ */ jsx7("nav", { "aria-label": "Primary", className: "dt-lp-nav", children: nav.map((n) => /* @__PURE__ */ jsx7(SmartLink, { href: n.href, className: "dt-lp-nav-link", children: n.label }, n.href)) }) : null,
257
+ /* @__PURE__ */ jsxs5("div", { className: "dt-lp-header-actions", children: [
258
+ login ? /* @__PURE__ */ jsx7(SmartLink, { href: login.href, className: "dt-lp-nav-link dt-lp-login", reload: true, children: login.label }) : null,
259
+ cta ? /* @__PURE__ */ jsx7(BookDemoButton, { size: "sm", children: cta }) : null,
260
+ items.length ? /* @__PURE__ */ jsxs5(
261
+ "button",
262
+ {
263
+ type: "button",
264
+ className: "dt-lp-menu-btn",
265
+ "aria-label": open ? "Close menu" : "Open menu",
266
+ "aria-expanded": open ? "true" : "false",
267
+ "aria-controls": "mobile-nav",
268
+ "data-menu-toggle": "",
269
+ onClick: () => setOpen((o) => !o),
270
+ children: [
271
+ /* @__PURE__ */ jsx7("span", { className: "dt-lp-menu-open", children: /* @__PURE__ */ jsx7(MenuIcon, { size: 20 }) }),
272
+ /* @__PURE__ */ jsx7("span", { className: "dt-lp-menu-close", children: /* @__PURE__ */ jsx7(Close, { size: 20 }) })
273
+ ]
274
+ }
275
+ ) : null
276
+ ] })
277
+ ] }),
278
+ items.length ? /* @__PURE__ */ jsx7("nav", { id: "mobile-nav", "aria-label": "Mobile", className: "dt-lp-mobile-nav", hidden: !open, onClick: (e) => {
279
+ var _a, _b;
280
+ return ((_b = (_a = e.target).closest) == null ? void 0 : _b.call(_a, "a")) && setOpen(false);
281
+ }, children: /* @__PURE__ */ jsx7(Wrap, { children: /* @__PURE__ */ jsx7("ul", { children: items.map((n) => /* @__PURE__ */ jsx7("li", { children: /* @__PURE__ */ jsx7(SmartLink, { href: n.href, reload: n === login, children: n.label }) }, n.href)) }) }) }) : null
282
+ ] });
283
+ }
284
+
285
+ // src/DreamtreeFooter.jsx
286
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
287
+ function DreamtreeMark({ size = 28 }) {
288
+ return /* @__PURE__ */ jsxs6("span", { className: "dt-lp-org", role: "img", "aria-label": "Dreamtree-Org", children: [
289
+ /* @__PURE__ */ jsx8("svg", { viewBox: "0 0 48.35 48.35", width: size, height: size, "aria-hidden": "true", children: /* @__PURE__ */ jsxs6("g", { fill: "none", strokeWidth: "4", strokeLinecap: "round", strokeLinejoin: "round", stroke: "#0f766e", children: [
290
+ /* @__PURE__ */ jsx8("path", { d: "M23.01,44.02c-9.02.03-16.36-7.27-16.39-16.29-.02-7.98,5.72-14.81,13.59-16.16,13.31-2.57,16.81-3.78,21.48-9.57,2.33,4.67,4.67,9.76,4.67,18.67,0,12.84-11.16,23.34-23.34,23.34Z" }),
291
+ /* @__PURE__ */ jsx8("path", { d: "M2,46.35c0-7,4.32-12.51,11.86-14.01,5.65-1.12,11.49-4.67,13.82-7" })
292
+ ] }) }),
293
+ /* @__PURE__ */ jsx8("span", { "aria-hidden": "true", className: "dt-lp-org-name", children: "Dreamtree-Org" })
294
+ ] });
295
+ }
296
+ function DreamtreeFooter({
297
+ blurb,
298
+ columns = [],
299
+ org = "Dreamtree-Org",
300
+ tagline,
301
+ legal = [],
302
+ year = (/* @__PURE__ */ new Date()).getFullYear(),
303
+ brand: own
304
+ }) {
305
+ const ctx = useLanding();
306
+ const brand = own || ctx.brand;
307
+ return /* @__PURE__ */ jsxs6("footer", { className: "dt-lp-footer", children: [
308
+ /* @__PURE__ */ jsxs6(Wrap, { className: `dt-lp-footer-grid dt-lp-footer-grid--${Math.min(columns.length, 3)}`, children: [
309
+ /* @__PURE__ */ jsxs6("div", { className: "dt-lp-footer-about", children: [
310
+ /* @__PURE__ */ jsx8(DreamtreeMark, {}),
311
+ blurb ? /* @__PURE__ */ jsx8("p", { children: blurb }) : null
312
+ ] }),
313
+ columns.map((col) => /* @__PURE__ */ jsxs6("nav", { "aria-label": col.label || col.title, className: "dt-lp-footer-col", children: [
314
+ /* @__PURE__ */ jsx8("p", { className: "dt-lp-footer-title", children: col.title }),
315
+ /* @__PURE__ */ jsx8("ul", { className: col.links.length > 5 && col.compact !== false ? "dt-lp-footer-links" : "dt-lp-footer-links dt-lp-footer-links--one", children: col.links.map((l) => /* @__PURE__ */ jsx8("li", { children: /* @__PURE__ */ jsx8(SmartLink, { href: l.href, reload: l.reload, children: l.label }) }, l.href)) })
316
+ ] }, col.title))
317
+ ] }),
318
+ /* @__PURE__ */ jsx8("div", { className: "dt-lp-footer-bar", children: /* @__PURE__ */ jsxs6(Wrap, { className: "dt-lp-footer-bar-inner", children: [
319
+ /* @__PURE__ */ jsxs6("p", { children: [
320
+ "\xA9 ",
321
+ year,
322
+ " ",
323
+ org,
324
+ ". All rights reserved."
325
+ ] }),
326
+ legal.length ? /* @__PURE__ */ jsx8("nav", { "aria-label": "Legal", className: "dt-lp-footer-legal", children: legal.map((l) => /* @__PURE__ */ jsx8(SmartLink, { href: l.href, children: l.label }, l.href)) }) : null,
327
+ brand && tagline ? /* @__PURE__ */ jsxs6("p", { children: [
328
+ brand.name,
329
+ ": ",
330
+ tagline
331
+ ] }) : null
332
+ ] }) })
333
+ ] });
334
+ }
335
+
336
+ // src/MarketingLayout.jsx
337
+ import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
338
+ function MarketingLayout({
339
+ brand,
340
+ nav = [],
341
+ login,
342
+ cta,
343
+ footer,
344
+ home = true,
345
+ linkComponent = null,
346
+ onBookDemo = null,
347
+ className = "",
348
+ children
349
+ }) {
350
+ if (!brand || !brand.name) {
351
+ throw new Error(`landing-kit: "brand" is missing. Pass getBrand('<product>') from @dreamtree-org/auth-kit, or your own { id, name, primary }.`);
352
+ }
353
+ return /* @__PURE__ */ jsx9(LandingProvider, { value: { brand, linkComponent, onBookDemo, home }, children: /* @__PURE__ */ jsxs7("div", { className: `dt-landing ${className}`.trim(), style: brandStyle(brand), "data-product": brand.id, children: [
354
+ /* @__PURE__ */ jsx9("a", { href: "#main", className: "dt-lp-skip", children: "Skip to content" }),
355
+ /* @__PURE__ */ jsx9(Header, { nav, ...login !== void 0 ? { login } : {}, ...cta !== void 0 ? { cta } : {} }),
356
+ /* @__PURE__ */ jsx9("main", { id: "main", children }),
357
+ footer === false ? null : /* @__PURE__ */ jsx9(DreamtreeFooter, { ...footer || {} })
358
+ ] }) });
359
+ }
360
+
361
+ // src/Hero.jsx
362
+ import { Fragment as Fragment2, jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
363
+ function Hero({
364
+ eyebrow,
365
+ title,
366
+ subtitle,
367
+ lead,
368
+ chips,
369
+ chipsLabel = "Who it is for",
370
+ cta = "Book a demo",
371
+ secondary,
372
+ overlap = false,
373
+ id = "hero",
374
+ backdrop
375
+ }) {
376
+ const [first, hi] = Array.isArray(title) ? title : [title, null];
377
+ return /* @__PURE__ */ jsxs8("section", { id, "aria-labelledby": "hero-title", className: `dt-lp-hero${overlap ? " dt-lp-hero--overlap" : ""}`, children: [
378
+ /* @__PURE__ */ jsx10("div", { className: "dt-lp-hero-glow", "aria-hidden": "true" }),
379
+ backdrop ? /* @__PURE__ */ jsx10("div", { className: "dt-lp-hero-backdrop", "aria-hidden": "true", children: backdrop }) : null,
380
+ /* @__PURE__ */ jsxs8(Wrap, { className: "dt-lp-hero-inner", children: [
381
+ /* @__PURE__ */ jsx10(Eyebrow, { children: eyebrow }),
382
+ /* @__PURE__ */ jsxs8("h1", { id: "hero-title", className: "dt-lp-h1", children: [
383
+ first,
384
+ hi ? /* @__PURE__ */ jsxs8(Fragment2, { children: [
385
+ " ",
386
+ /* @__PURE__ */ jsx10("span", { className: "dt-lp-h1-hi", children: hi })
387
+ ] }) : null
388
+ ] }),
389
+ subtitle ? /* @__PURE__ */ jsx10("p", { className: "dt-lp-hero-subtitle", children: subtitle }) : null,
390
+ lead ? /* @__PURE__ */ jsx10("p", { className: "dt-lp-hero-lead", children: lead }) : null,
391
+ /* @__PURE__ */ jsxs8("div", { className: "dt-lp-actions", children: [
392
+ cta ? /* @__PURE__ */ jsx10(BookDemoButton, { size: "lg", children: cta }) : null,
393
+ secondary ? /* @__PURE__ */ jsx10(TextLink, { href: secondary.href, children: secondary.label }) : null
394
+ ] }),
395
+ (chips == null ? void 0 : chips.length) ? /* @__PURE__ */ jsx10("ul", { className: "dt-lp-chips", "aria-label": chipsLabel, children: chips.map((c) => /* @__PURE__ */ jsx10("li", { children: c }, c)) }) : null
396
+ ] })
397
+ ] });
398
+ }
399
+
400
+ // src/FeatureSection.jsx
401
+ import { Fragment as Fragment3, jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
402
+ var asItem = (x) => typeof x === "string" ? { title: x } : x;
403
+ function Items({ items, kind }) {
404
+ return /* @__PURE__ */ jsx11("ul", { className: `dt-lp-grid dt-lp-grid--${kind}`, children: items.map(asItem).map((f) => /* @__PURE__ */ jsxs9("li", { className: `dt-lp-card dt-lp-card--${kind}`, children: [
405
+ f.icon ? /* @__PURE__ */ jsx11("span", { className: "dt-lp-icon-tile", children: renderIcon(f.icon, 20) }) : null,
406
+ /* @__PURE__ */ jsxs9("div", { className: "dt-lp-card-text", children: [
407
+ f.label ? /* @__PURE__ */ jsx11("p", { className: "dt-lp-kicker", children: f.label }) : null,
408
+ /* @__PURE__ */ jsx11("h3", { className: "dt-lp-h3", children: f.href ? /* @__PURE__ */ jsx11(SmartLink, { href: f.href, children: f.title }) : f.title }),
409
+ f.body ? /* @__PURE__ */ jsx11("p", { children: f.body }) : null
410
+ ] })
411
+ ] }, f.title)) });
412
+ }
413
+ function Checklist({ items, icon: Icon = Check, tone = "good", labelledBy }) {
414
+ return /* @__PURE__ */ jsx11("ul", { className: `dt-lp-checklist dt-lp-checklist--${tone}`, "aria-labelledby": labelledBy, children: items.map((x) => /* @__PURE__ */ jsxs9("li", { children: [
415
+ /* @__PURE__ */ jsx11(Icon, { size: 18 }),
416
+ /* @__PURE__ */ jsx11("span", { children: x })
417
+ ] }, x)) });
418
+ }
419
+ function InlineItems({ items }) {
420
+ if (!(items == null ? void 0 : items.length)) return null;
421
+ return /* @__PURE__ */ jsx11("ul", { className: "dt-lp-inline-items", children: items.map(asItem).map((f) => /* @__PURE__ */ jsxs9("li", { children: [
422
+ f.icon ? renderIcon(f.icon, 22) : null,
423
+ /* @__PURE__ */ jsxs9("span", { children: [
424
+ /* @__PURE__ */ jsx11("span", { className: "dt-lp-inline-title", children: f.title }),
425
+ f.body ? /* @__PURE__ */ jsx11("span", { className: "dt-lp-inline-body", children: f.body }) : null
426
+ ] })
427
+ ] }, f.title)) });
428
+ }
429
+ function Media({ children }) {
430
+ return /* @__PURE__ */ jsx11("div", { className: "dt-lp-media", children });
431
+ }
432
+ function Pair({ className, reverse, text, side }) {
433
+ return /* @__PURE__ */ jsxs9("div", { className: `${className}${reverse ? " dt-lp-pair--reverse" : ""}`, children: [
434
+ reverse ? side : text,
435
+ reverse ? text : side
436
+ ] });
437
+ }
438
+ function Chips({ items, label, className = "" }) {
439
+ if (!(items == null ? void 0 : items.length)) return null;
440
+ return /* @__PURE__ */ jsx11("ul", { className: `dt-lp-chips dt-lp-chips--start ${className}`.trim(), "aria-label": label, children: items.map((c) => /* @__PURE__ */ jsx11("li", { children: c }, c)) });
441
+ }
442
+ function FeatureSection(props) {
443
+ const { id, variant = "cards", tone, eyebrow, title, body, items = [], cta, footnote, media, reverse, bullets } = props;
444
+ const hid = `${id || variant}-title`;
445
+ const defaultTone = { highlight: "dark", split: "tinted", compare: "muted" }[variant] || "plain";
446
+ const shell = (children, extra = {}) => /* @__PURE__ */ jsx11(Section, { id, labelledBy: hid, tone: tone || defaultTone, className: `dt-lp-feature dt-lp-feature--${variant}`, ...extra, children });
447
+ switch (variant) {
448
+ case "split":
449
+ return shell(
450
+ /* @__PURE__ */ jsx11(
451
+ Pair,
452
+ {
453
+ className: "dt-lp-split",
454
+ reverse,
455
+ text: /* @__PURE__ */ jsxs9(SectionIntro, { id: hid, eyebrow, title, body, children: [
456
+ media ? /* @__PURE__ */ jsx11(InlineItems, { items }) : null,
457
+ (bullets == null ? void 0 : bullets.length) ? /* @__PURE__ */ jsx11(Checklist, { items: bullets }) : null,
458
+ /* @__PURE__ */ jsx11(Cta, { cta, className: "dt-lp-mt" })
459
+ ] }, "text"),
460
+ side: media ? /* @__PURE__ */ jsx11(Media, { children: media }, "side") : /* @__PURE__ */ jsx11(Items, { items, kind: "split" }, "side")
461
+ }
462
+ )
463
+ );
464
+ case "list":
465
+ return shell(
466
+ /* @__PURE__ */ jsxs9(Fragment3, { children: [
467
+ /* @__PURE__ */ jsx11(SectionIntro, { id: hid, eyebrow, title, body, align: "center" }),
468
+ /* @__PURE__ */ jsx11(Items, { items, kind: "list" }),
469
+ /* @__PURE__ */ jsx11(Cta, { cta, className: "dt-lp-mt" })
470
+ ] })
471
+ );
472
+ case "intro": {
473
+ const { subtitle, detail, note, glance = [], glanceTitle = "At a glance", link } = props;
474
+ return shell(
475
+ /* @__PURE__ */ jsxs9("div", { className: "dt-lp-intro-grid", children: [
476
+ /* @__PURE__ */ jsxs9("div", { children: [
477
+ /* @__PURE__ */ jsx11(SectionIntro, { id: hid, eyebrow, title, body }),
478
+ subtitle ? /* @__PURE__ */ jsx11("h3", { className: "dt-lp-h3 dt-lp-h3--lg", children: subtitle }) : null,
479
+ detail ? /* @__PURE__ */ jsx11("p", { className: "dt-lp-body", children: detail }) : null,
480
+ note ? /* @__PURE__ */ jsx11("p", { className: "dt-lp-note", children: note }) : null,
481
+ /* @__PURE__ */ jsx11(Cta, { cta, className: "dt-lp-mt" })
482
+ ] }),
483
+ glance.length ? /* @__PURE__ */ jsxs9("aside", { className: "dt-lp-glance", "aria-labelledby": `${hid}-glance`, children: [
484
+ /* @__PURE__ */ jsx11("p", { id: `${hid}-glance`, className: "dt-lp-eyebrow", children: glanceTitle }),
485
+ /* @__PURE__ */ jsx11(Checklist, { items: glance }),
486
+ props.glanceNote ? /* @__PURE__ */ jsx11("p", { className: "dt-lp-note", children: props.glanceNote }) : null,
487
+ link ? /* @__PURE__ */ jsx11(TextLink, { href: link.href, className: "dt-lp-mt", children: link.label }) : null
488
+ ] }) : null
489
+ ] })
490
+ );
491
+ }
492
+ case "highlight": {
493
+ const { note, noteLabel = "Important:" } = props;
494
+ const text = /* @__PURE__ */ jsxs9("div", { className: "dt-lp-intro", children: [
495
+ /* @__PURE__ */ jsx11(Eyebrow, { children: eyebrow }),
496
+ /* @__PURE__ */ jsx11("h2", { id: hid, className: "dt-lp-h2", children: title }),
497
+ body ? /* @__PURE__ */ jsx11("p", { className: "dt-lp-lead", children: body }) : null,
498
+ note ? /* @__PURE__ */ jsxs9("p", { className: "dt-lp-callout", children: [
499
+ /* @__PURE__ */ jsx11(Alert, { size: 20 }),
500
+ /* @__PURE__ */ jsxs9("span", { children: [
501
+ /* @__PURE__ */ jsx11("strong", { children: noteLabel }),
502
+ " ",
503
+ note
504
+ ] })
505
+ ] }) : null,
506
+ media ? /* @__PURE__ */ jsx11(InlineItems, { items }) : null,
507
+ /* @__PURE__ */ jsx11(Cta, { cta, className: "dt-lp-mt" })
508
+ ] }, "text");
509
+ const side = media ? /* @__PURE__ */ jsx11(Media, { children: media }, "side") : /* @__PURE__ */ jsx11("ul", { className: "dt-lp-grid dt-lp-grid--chips", children: items.map(asItem).map((f) => /* @__PURE__ */ jsxs9("li", { className: "dt-lp-chip-card", children: [
510
+ renderIcon(f.icon, 22),
511
+ /* @__PURE__ */ jsx11("span", { children: f.title })
512
+ ] }, f.title)) }, "side");
513
+ return shell(/* @__PURE__ */ jsx11(Pair, { className: "dt-lp-split dt-lp-split--wide", reverse, text, side }));
514
+ }
515
+ case "band": {
516
+ const { examples, examplesLabel = "Examples" } = props;
517
+ const text = /* @__PURE__ */ jsxs9("div", { children: [
518
+ /* @__PURE__ */ jsx11(SectionIntro, { id: hid, eyebrow, title, body }),
519
+ /* @__PURE__ */ jsx11(Chips, { items: examples, label: examplesLabel, className: "dt-lp-mt" }),
520
+ media ? /* @__PURE__ */ jsx11(InlineItems, { items }) : null,
521
+ /* @__PURE__ */ jsx11(Cta, { cta, className: "dt-lp-mt" })
522
+ ] }, "text");
523
+ const side = media ? /* @__PURE__ */ jsx11(Media, { children: media }, "side") : /* @__PURE__ */ jsx11("ul", { className: "dt-lp-band-list", children: items.map(asItem).map((f) => /* @__PURE__ */ jsxs9("li", { children: [
524
+ renderIcon(f.icon, 22),
525
+ /* @__PURE__ */ jsx11("span", { children: f.title })
526
+ ] }, f.title)) }, "side");
527
+ return shell(/* @__PURE__ */ jsx11(Pair, { className: "dt-lp-band", reverse, text, side }));
528
+ }
529
+ case "compare": {
530
+ const { columns = [] } = props;
531
+ return shell(
532
+ /* @__PURE__ */ jsxs9(Fragment3, { children: [
533
+ /* @__PURE__ */ jsx11(SectionIntro, { id: hid, eyebrow, title, body }),
534
+ /* @__PURE__ */ jsx11("div", { className: "dt-lp-compare", children: columns.map((col, i) => {
535
+ const cid = `${id || "compare"}-${i + 1}`;
536
+ const tone2 = col.tone || (i === 0 ? "good" : "check");
537
+ return /* @__PURE__ */ jsxs9("div", { className: "dt-lp-card dt-lp-card--compare", children: [
538
+ /* @__PURE__ */ jsx11("h3", { id: cid, className: "dt-lp-h3 dt-lp-h3--lg", children: col.title }),
539
+ /* @__PURE__ */ jsx11(Checklist, { items: col.items, tone: tone2, icon: tone2 === "good" ? Check : Help, labelledBy: cid })
540
+ ] }, cid);
541
+ }) })
542
+ ] })
543
+ );
544
+ }
545
+ case "links":
546
+ return shell(
547
+ /* @__PURE__ */ jsxs9(Fragment3, { children: [
548
+ /* @__PURE__ */ jsx11(SectionIntro, { id: hid, eyebrow, title, body }),
549
+ /* @__PURE__ */ jsx11("ul", { className: "dt-lp-grid dt-lp-grid--links", children: items.map((s) => /* @__PURE__ */ jsx11("li", { children: /* @__PURE__ */ jsxs9(SmartLink, { href: s.href, className: "dt-lp-linkcard", children: [
550
+ /* @__PURE__ */ jsxs9("span", { className: "dt-lp-linkcard-title", children: [
551
+ s.title,
552
+ " ",
553
+ /* @__PURE__ */ jsx11(ArrowRight, {})
554
+ ] }),
555
+ s.body ? /* @__PURE__ */ jsx11("span", { className: "dt-lp-linkcard-body", children: s.body }) : null
556
+ ] }) }, s.href)) })
557
+ ] })
558
+ );
559
+ case "detail": {
560
+ return shell(
561
+ /* @__PURE__ */ jsxs9("div", { className: "dt-lp-detail", children: [
562
+ /* @__PURE__ */ jsx11("h2", { id: hid, className: "dt-lp-h2", children: title }),
563
+ /* @__PURE__ */ jsxs9("div", { children: [
564
+ body ? /* @__PURE__ */ jsx11("p", { className: "dt-lp-body", children: body }) : null,
565
+ (bullets == null ? void 0 : bullets.length) ? /* @__PURE__ */ jsx11(Checklist, { items: bullets }) : null
566
+ ] })
567
+ ] })
568
+ );
569
+ }
570
+ default:
571
+ return shell(
572
+ /* @__PURE__ */ jsxs9(Fragment3, { children: [
573
+ /* @__PURE__ */ jsx11(SectionIntro, { id: hid, eyebrow, title, body }),
574
+ /* @__PURE__ */ jsx11(Items, { items, kind: "cards" }),
575
+ footnote ? /* @__PURE__ */ jsx11("p", { className: "dt-lp-footnote", children: footnote }) : null,
576
+ /* @__PURE__ */ jsx11(Cta, { cta, className: "dt-lp-mt" })
577
+ ] })
578
+ );
579
+ }
580
+ }
581
+
582
+ // src/Audiences.jsx
583
+ import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
584
+ function Audiences({ id = "audiences", eyebrow, title, body, items = [], linkLabel = "See how it works", tone = "muted" }) {
585
+ const hid = `${id}-title`;
586
+ return /* @__PURE__ */ jsxs10(Section, { id, labelledBy: hid, tone, className: "dt-lp-audiences", children: [
587
+ /* @__PURE__ */ jsx12(SectionIntro, { id: hid, eyebrow, title, body, align: "center" }),
588
+ /* @__PURE__ */ jsx12("div", { className: `dt-lp-grid dt-lp-grid--audiences dt-lp-grid--n${Math.min(items.length, 4)}`, children: items.map((a, i) => {
589
+ var _a;
590
+ const aid = a.id || `${id}-${i + 1}`;
591
+ return /* @__PURE__ */ jsxs10("article", { id: aid, "aria-labelledby": `${aid}-title`, className: "dt-lp-card dt-lp-card--audience", children: [
592
+ a.icon ? /* @__PURE__ */ jsx12("span", { className: "dt-lp-audience-icon", children: renderIcon(a.icon, 30) }) : null,
593
+ /* @__PURE__ */ jsxs10("div", { className: a.media ? "dt-lp-audience-head" : void 0, children: [
594
+ /* @__PURE__ */ jsxs10("div", { children: [
595
+ a.keyword ? /* @__PURE__ */ jsx12("p", { className: "dt-lp-kicker", children: a.keyword }) : null,
596
+ /* @__PURE__ */ jsx12("h3", { id: `${aid}-title`, className: "dt-lp-h3 dt-lp-h3--lg", children: a.title }),
597
+ /* @__PURE__ */ jsx12("p", { className: a.media || a.features ? void 0 : "dt-lp-grow", children: a.body })
598
+ ] }),
599
+ a.media ? /* @__PURE__ */ jsx12("div", { className: "dt-lp-audience-media", "aria-hidden": "true", children: a.media }) : null
600
+ ] }),
601
+ ((_a = a.features) == null ? void 0 : _a.length) ? /* @__PURE__ */ jsx12("ul", { className: "dt-lp-audience-features dt-lp-grow", children: a.features.map((f) => {
602
+ const it = typeof f === "string" ? { title: f } : f;
603
+ return /* @__PURE__ */ jsxs10("li", { children: [
604
+ it.icon ? renderIcon(it.icon, 22) : null,
605
+ /* @__PURE__ */ jsx12("span", { children: it.title })
606
+ ] }, it.title);
607
+ }) }) : null,
608
+ a.href ? /* @__PURE__ */ jsx12(TextLink, { href: a.href, "aria-label": `${a.keyword || a.title}: ${(a.linkLabel || linkLabel).toLowerCase()}`, children: a.linkLabel || linkLabel }) : null
609
+ ] }, aid);
610
+ }) })
611
+ ] });
612
+ }
613
+
614
+ // src/HowItWorks.jsx
615
+ import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
616
+ function HowItWorks({ id = "how-it-works", eyebrow, title, body, steps, items, tone = "plain", cta }) {
617
+ const list = steps || items || [];
618
+ const hid = `${id}-title`;
619
+ return /* @__PURE__ */ jsxs11(Section, { id, labelledBy: hid, tone, className: "dt-lp-steps", children: [
620
+ /* @__PURE__ */ jsx13(SectionIntro, { id: hid, eyebrow, title, body }),
621
+ /* @__PURE__ */ jsx13("ol", { className: "dt-lp-grid dt-lp-grid--steps", children: list.map((st, i) => /* @__PURE__ */ jsxs11("li", { className: "dt-lp-card", children: [
622
+ /* @__PURE__ */ jsx13("span", { className: "dt-lp-step-num", "aria-hidden": "true", children: i + 1 }),
623
+ /* @__PURE__ */ jsx13("h3", { className: "dt-lp-h3", children: st.name || st.title }),
624
+ /* @__PURE__ */ jsx13("p", { children: st.text || st.body })
625
+ ] }, st.name || st.title)) }),
626
+ /* @__PURE__ */ jsx13(Cta, { cta, className: "dt-lp-mt" })
627
+ ] });
628
+ }
629
+
630
+ // src/Faq.jsx
631
+ import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
632
+ function Faq({ id = "faq", eyebrow, title = "Frequently asked questions", intro, items = [], openFirst = true, tone = "muted" }) {
633
+ const hid = `${id}-title`;
634
+ return /* @__PURE__ */ jsxs12(Section, { id, labelledBy: hid, tone, narrow: true, className: "dt-lp-faq", children: [
635
+ /* @__PURE__ */ jsx14(SectionIntro, { id: hid, eyebrow, title, body: intro, align: "center" }),
636
+ /* @__PURE__ */ jsx14("div", { className: "dt-lp-faq-list", children: items.map((f, i) => /* @__PURE__ */ jsxs12("details", { className: "dt-lp-faq-item", open: openFirst && i === 0, children: [
637
+ /* @__PURE__ */ jsxs12("summary", { children: [
638
+ /* @__PURE__ */ jsx14("h3", { className: "dt-lp-h3", children: f.q }),
639
+ /* @__PURE__ */ jsx14(ChevronRight, { size: 18, className: "dt-lp-icon dt-lp-faq-chevron" })
640
+ ] }),
641
+ /* @__PURE__ */ jsx14("p", { children: f.a })
642
+ ] }, f.q)) })
643
+ ] });
644
+ }
645
+
646
+ // src/DemoCta.jsx
647
+ import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
648
+ function DemoCta({ id = "demo", eyebrow, title, body, label = "Book a demo", chips, chipsLabel = "Business types", note, link }) {
649
+ return /* @__PURE__ */ jsx15("section", { id, "aria-labelledby": `${id}-title`, className: "dt-lp-section dt-lp-cta", children: /* @__PURE__ */ jsx15(Wrap, { children: /* @__PURE__ */ jsxs13("div", { className: "dt-lp-cta-panel", children: [
650
+ eyebrow ? /* @__PURE__ */ jsx15("p", { className: "dt-lp-eyebrow dt-lp-eyebrow--inverse", children: eyebrow }) : null,
651
+ /* @__PURE__ */ jsx15("h2", { id: `${id}-title`, className: "dt-lp-h2 dt-lp-h2--cta", children: title }),
652
+ body ? /* @__PURE__ */ jsx15("p", { className: "dt-lp-cta-body", children: body }) : null,
653
+ /* @__PURE__ */ jsx15(BookDemoButton, { size: "lg", variant: "inverse", children: label }),
654
+ (chips == null ? void 0 : chips.length) ? /* @__PURE__ */ jsx15("ul", { className: "dt-lp-chips dt-lp-chips--inverse", "aria-label": chipsLabel, children: chips.map((k) => /* @__PURE__ */ jsx15("li", { children: k }, k)) }) : null,
655
+ note ? /* @__PURE__ */ jsxs13("div", { className: "dt-lp-cta-note", children: [
656
+ note.title ? /* @__PURE__ */ jsx15("h3", { className: "dt-lp-h3", children: note.title }) : null,
657
+ note.body ? /* @__PURE__ */ jsx15("p", { children: note.body }) : null
658
+ ] }) : null,
659
+ link ? /* @__PURE__ */ jsx15("p", { className: "dt-lp-cta-link", children: /* @__PURE__ */ jsx15(SmartLink, { href: link.href, reload: link.reload, children: link.label }) }) : null
660
+ ] }) }) });
661
+ }
662
+
663
+ // src/LandingPage.jsx
664
+ import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
665
+ var BLOCKS = { audiences: Audiences, steps: HowItWorks, faq: Faq, cta: DemoCta };
666
+ var TAIL = ["audiences", "steps", "faq", "cta"];
667
+ function LandingPage({ brand, content, preview = null, slots = {}, linkComponent, onBookDemo, home = true, className }) {
668
+ if (!content || !content.hero) {
669
+ throw new Error('landing-kit: "content.hero" is missing. LandingPage needs at least { hero: { title } }; see README "Content".');
670
+ }
671
+ const { nav = [], login, hero, sections = [], footer, header = {} } = content;
672
+ const used = new Set(sections.filter((s) => s && s.use).map((s) => s.use));
673
+ const previewInline = used.has("preview");
674
+ const render = (s, i) => {
675
+ var _a;
676
+ if (!s) return null;
677
+ if (isValidElement2(s)) return cloneElement(s, { key: (_a = s.key) != null ? _a : `el-${i}` });
678
+ if (s.use === "preview") return preview ? /* @__PURE__ */ jsx16("div", { children: preview }, "preview") : null;
679
+ if (s.use) {
680
+ if (slots && Object.prototype.hasOwnProperty.call(slots, s.use)) {
681
+ const node = slots[s.use];
682
+ return node ? /* @__PURE__ */ jsx16(Fragment4, { children: node }, `slot-${s.use}`) : null;
683
+ }
684
+ const Block = BLOCKS[s.use];
685
+ if (!Block) {
686
+ const known = ["preview", ...TAIL, ...Object.keys(slots || {})].join(", ");
687
+ throw new Error(`landing-kit: unknown section use "${s.use}" (known: ${known}). Pass custom sections as <LandingPage slots={{ ${s.use}: <YourSection /> }} />.`);
688
+ }
689
+ return content[s.use] ? /* @__PURE__ */ jsx16(Block, { ...content[s.use] }, s.use) : null;
690
+ }
691
+ return /* @__PURE__ */ jsx16(FeatureSection, { ...s }, s.id || `section-${i}`);
692
+ };
693
+ return /* @__PURE__ */ jsxs14(
694
+ MarketingLayout,
695
+ {
696
+ brand,
697
+ nav,
698
+ login,
699
+ cta: header.cta,
700
+ footer,
701
+ home,
702
+ linkComponent,
703
+ onBookDemo,
704
+ className,
705
+ children: [
706
+ /* @__PURE__ */ jsx16(Hero, { ...hero, overlap: !!preview && !previewInline }),
707
+ preview && !previewInline ? preview : null,
708
+ sections.map(render),
709
+ TAIL.filter((k) => !used.has(k) && content[k]).map((k) => {
710
+ const Block = BLOCKS[k];
711
+ return /* @__PURE__ */ jsx16(Block, { ...content[k] }, k);
712
+ })
713
+ ]
714
+ }
715
+ );
716
+ }
717
+
718
+ // src/PreviewFrame.jsx
719
+ import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
720
+ function PreviewFrame({
721
+ id = "product",
722
+ heading,
723
+ caption,
724
+ meta,
725
+ badge = "Demo data",
726
+ brand: own,
727
+ children
728
+ }) {
729
+ const ctx = useLanding();
730
+ const brand = own || ctx.brand;
731
+ return /* @__PURE__ */ jsxs15("section", { id, "aria-labelledby": `${id}-title`, className: "dt-lp-preview", children: [
732
+ /* @__PURE__ */ jsx17("h2", { id: `${id}-title`, className: "dt-lp-sr-only", children: heading || `${brand ? brand.name : "Product"} overview` }),
733
+ /* @__PURE__ */ jsx17(Wrap, { children: /* @__PURE__ */ jsxs15("figure", { className: "dt-lp-preview-frame", children: [
734
+ /* @__PURE__ */ jsxs15("div", { className: "dt-lp-preview-bar", children: [
735
+ /* @__PURE__ */ jsxs15("div", { className: "dt-lp-preview-title", children: [
736
+ brand ? /* @__PURE__ */ jsx17(Wordmark, { name: brand.name }) : null,
737
+ meta ? /* @__PURE__ */ jsx17("span", { className: "dt-lp-preview-meta", children: meta }) : null
738
+ ] }),
739
+ badge ? /* @__PURE__ */ jsx17("span", { className: "dt-lp-preview-badge", children: badge }) : null
740
+ ] }),
741
+ /* @__PURE__ */ jsx17("div", { className: "dt-lp-preview-body", children }),
742
+ caption ? /* @__PURE__ */ jsx17("figcaption", { className: "dt-lp-sr-only", children: caption }) : null
743
+ ] }) })
744
+ ] });
745
+ }
746
+
747
+ // src/DemoDialog.jsx
748
+ import { useEffect, useRef, useState as useState2 } from "react";
749
+ import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
750
+ var DEFAULT_DEMO_FIELDS = [
751
+ { name: "name", label: "Your name", required: true, maxLength: 120, autoComplete: "name" },
752
+ { name: "phone", label: "Mobile", type: "tel", required: true, maxLength: 20, autoComplete: "tel", inputMode: "tel" },
753
+ { name: "email", label: "Email", type: "email", maxLength: 190, autoComplete: "email" },
754
+ { name: "company", label: "Business name", maxLength: 160, autoComplete: "organization" }
755
+ ];
756
+ var initial = (fields) => Object.fromEntries(fields.map((f) => [f.name, ""]));
757
+ function Field({ f, value, onChange, error }) {
758
+ const id = `dt-demo-${f.name}`;
759
+ const errId = `${id}-error`;
760
+ const common = {
761
+ id,
762
+ name: f.name,
763
+ value,
764
+ onChange,
765
+ required: !!f.required,
766
+ className: "dt-lp-input",
767
+ ...error ? { "aria-invalid": "true", "aria-describedby": errId } : {}
768
+ };
769
+ let control;
770
+ if (f.type === "select") {
771
+ control = /* @__PURE__ */ jsxs16("select", { ...common, children: [
772
+ /* @__PURE__ */ jsx18("option", { value: "", children: f.placeholder || "Choose one" }),
773
+ (f.options || []).map((o) => /* @__PURE__ */ jsx18("option", { value: o.value, children: o.label }, o.value))
774
+ ] });
775
+ } else if (f.type === "textarea") {
776
+ control = /* @__PURE__ */ jsx18("textarea", { ...common, rows: f.rows || 3, maxLength: f.maxLength });
777
+ } else {
778
+ control = /* @__PURE__ */ jsx18(
779
+ "input",
780
+ {
781
+ ...common,
782
+ type: f.type || "text",
783
+ maxLength: f.maxLength,
784
+ min: f.min,
785
+ inputMode: f.inputMode,
786
+ autoComplete: f.autoComplete,
787
+ placeholder: f.placeholder
788
+ }
789
+ );
790
+ }
791
+ return /* @__PURE__ */ jsxs16("label", { htmlFor: id, className: `dt-lp-field${f.wide || f.type === "textarea" ? " dt-lp-field--wide" : ""}`, children: [
792
+ /* @__PURE__ */ jsxs16("span", { children: [
793
+ f.label,
794
+ f.required ? " *" : ""
795
+ ] }),
796
+ control,
797
+ error ? /* @__PURE__ */ jsx18("span", { id: errId, className: "dt-lp-field-error", children: error }) : null
798
+ ] });
799
+ }
800
+ function DemoDialog({
801
+ brand,
802
+ open = true,
803
+ onClose = () => {
804
+ },
805
+ onSubmit,
806
+ fields = DEFAULT_DEMO_FIELDS,
807
+ title,
808
+ intro = "Tell us a little about your business and we will walk you through the workflows that matter to your team.",
809
+ fiduciary = "Dreamtree-Org",
810
+ consentText,
811
+ privacyHref = "/privacy",
812
+ policyVersion,
813
+ submitLabel = "Request my demo",
814
+ successMessage,
815
+ validate
816
+ }) {
817
+ const ref = useRef(null);
818
+ const [form, setForm] = useState2(() => initial(fields));
819
+ const [website, setWebsite] = useState2("");
820
+ const [consent, setConsent] = useState2(false);
821
+ const [busy, setBusy] = useState2(false);
822
+ const [err, setErr] = useState2("");
823
+ const [fieldErrors, setFieldErrors] = useState2({});
824
+ const [sent, setSent] = useState2(false);
825
+ const name = brand ? brand.name : "a";
826
+ useEffect(() => {
827
+ const dlg = ref.current;
828
+ if (open && dlg && !dlg.open && typeof dlg.showModal === "function") dlg.showModal();
829
+ }, [open]);
830
+ if (!open) return null;
831
+ if (typeof onSubmit !== "function") {
832
+ throw new Error('landing-kit DemoDialog: "onSubmit" is missing. Pass async (data) => { /* POST the demo request */ }.');
833
+ }
834
+ const set = (k) => (e) => {
835
+ setForm((f) => ({ ...f, [k]: e.target.value }));
836
+ if (fieldErrors[k]) setFieldErrors(({ [k]: _gone, ...rest }) => rest);
837
+ };
838
+ const ready = consent && (typeof validate === "function" || fields.every((f) => !f.required || String(form[f.name] || "").trim()));
839
+ const submit = async (e) => {
840
+ e.preventDefault();
841
+ if (!ready || busy) return;
842
+ setErr("");
843
+ if (typeof validate === "function") {
844
+ const found = validate(form) || {};
845
+ setFieldErrors(found);
846
+ if (Object.keys(found).length) return;
847
+ }
848
+ setBusy(true);
849
+ try {
850
+ await onSubmit({ ...form, website, consent: true, source_path: typeof window === "undefined" ? "/" : window.location.pathname });
851
+ setSent(true);
852
+ } catch (ex) {
853
+ if (ex && ex.fields && typeof ex.fields === "object") setFieldErrors(ex.fields);
854
+ setErr(ex && ex.message || "Could not send your request. Please try again.");
855
+ } finally {
856
+ setBusy(false);
857
+ }
858
+ };
859
+ const first = String(form.name || "").trim().split(/\s+/)[0] || "there";
860
+ const thanks = typeof successMessage === "function" ? successMessage(form) : successMessage || `Thanks, ${first}. We have your request. Someone from the ${name} team will call you within one working day to fix a time.`;
861
+ return /* @__PURE__ */ jsxs16(
862
+ "dialog",
863
+ {
864
+ ref,
865
+ className: "dt-lp-dialog",
866
+ style: brand ? brandStyle(brand) : void 0,
867
+ "aria-labelledby": "dt-demo-title",
868
+ onClose,
869
+ onCancel: (e) => {
870
+ e.preventDefault();
871
+ onClose();
872
+ },
873
+ children: [
874
+ /* @__PURE__ */ jsxs16("div", { className: "dt-lp-dialog-head", children: [
875
+ /* @__PURE__ */ jsx18("h2", { id: "dt-demo-title", children: sent ? "Thank you" : title || `Book a ${name} demo` }),
876
+ /* @__PURE__ */ jsx18("button", { type: "button", className: "dt-lp-dialog-x", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsx18(Close, { size: 18 }) })
877
+ ] }),
878
+ /* @__PURE__ */ jsx18("div", { className: "dt-lp-dialog-body", children: sent ? /* @__PURE__ */ jsxs16("div", { className: "dt-lp-stack", children: [
879
+ /* @__PURE__ */ jsx18("p", { role: "status", children: thanks }),
880
+ /* @__PURE__ */ jsx18("button", { type: "button", className: "dt-lp-btn dt-lp-btn--outline dt-lp-btn--md", onClick: onClose, children: "Close" })
881
+ ] }) : /* @__PURE__ */ jsxs16("form", { className: "dt-lp-stack", onSubmit: submit, noValidate: true, children: [
882
+ intro ? /* @__PURE__ */ jsx18("p", { className: "dt-lp-muted", children: intro }) : null,
883
+ err ? /* @__PURE__ */ jsx18("p", { role: "alert", className: "dt-lp-error", children: err }) : null,
884
+ /* @__PURE__ */ jsx18("div", { className: "dt-lp-fields", children: fields.map((f) => /* @__PURE__ */ jsx18(Field, { f, value: form[f.name], onChange: set(f.name), error: fieldErrors[f.name] }, f.name)) }),
885
+ /* @__PURE__ */ jsx18("div", { "aria-hidden": "true", className: "dt-lp-honeypot", children: /* @__PURE__ */ jsxs16("label", { children: [
886
+ "Website",
887
+ /* @__PURE__ */ jsx18("input", { type: "text", name: "website", tabIndex: -1, autoComplete: "off", value: website, onChange: (e) => setWebsite(e.target.value) })
888
+ ] }) }),
889
+ /* @__PURE__ */ jsxs16("label", { className: "dt-lp-consent", children: [
890
+ /* @__PURE__ */ jsx18("input", { type: "checkbox", checked: consent, onChange: (e) => setConsent(e.target.checked) }),
891
+ /* @__PURE__ */ jsx18("span", { children: consentText || `I agree that ${fiduciary} may call, message or email me about this demo.` })
892
+ ] }),
893
+ privacyHref ? /* @__PURE__ */ jsxs16("p", { className: "dt-lp-fineprint", children: [
894
+ "Used only to arrange your demo. See the ",
895
+ /* @__PURE__ */ jsx18("a", { href: privacyHref, children: "privacy policy" }),
896
+ policyVersion ? ` (v${policyVersion})` : "",
897
+ "."
898
+ ] }) : null,
899
+ /* @__PURE__ */ jsxs16("button", { type: "submit", className: "dt-lp-btn dt-lp-btn--primary dt-lp-btn--md", disabled: !ready || busy, "aria-busy": busy ? "true" : void 0, children: [
900
+ /* @__PURE__ */ jsx18(Calendar, { size: 16 }),
901
+ busy ? "Sending\u2026" : submitLabel
902
+ ] })
903
+ ] }) })
904
+ ]
905
+ }
906
+ );
907
+ }
908
+
909
+ // src/PageHero.jsx
910
+ import { jsx as jsx19, jsxs as jsxs17 } from "react/jsx-runtime";
911
+ function Breadcrumbs({ items = [] }) {
912
+ if (!items.length) return null;
913
+ return /* @__PURE__ */ jsx19("nav", { "aria-label": "Breadcrumb", className: "dt-lp-crumbs", children: /* @__PURE__ */ jsx19("ol", { children: items.map((c, i) => {
914
+ const last = i === items.length - 1;
915
+ return /* @__PURE__ */ jsxs17("li", { children: [
916
+ i > 0 ? /* @__PURE__ */ jsx19(ChevronRight, { size: 14 }) : null,
917
+ last || !c.href ? /* @__PURE__ */ jsx19("span", { "aria-current": last ? "page" : void 0, children: c.label }) : /* @__PURE__ */ jsx19(SmartLink, { href: c.href, children: c.label })
918
+ ] }, `${c.label}-${i}`);
919
+ }) }) });
920
+ }
921
+ function PageHero({ breadcrumbs, eyebrow, title, lead, cta = "Book a demo", secondary }) {
922
+ return /* @__PURE__ */ jsx19("section", { "aria-labelledby": "hero-title", className: "dt-lp-hero dt-lp-hero--page", children: /* @__PURE__ */ jsxs17(Wrap, { className: "dt-lp-hero-inner dt-lp-hero-inner--start", children: [
923
+ /* @__PURE__ */ jsx19(Breadcrumbs, { items: breadcrumbs }),
924
+ /* @__PURE__ */ jsx19(Eyebrow, { className: "dt-lp-mt-lg", children: eyebrow }),
925
+ /* @__PURE__ */ jsx19("h1", { id: "hero-title", className: "dt-lp-h1 dt-lp-h1--page", children: title }),
926
+ lead ? /* @__PURE__ */ jsx19("p", { id: "answer", className: "dt-lp-hero-lead", children: lead }) : null,
927
+ /* @__PURE__ */ jsxs17("div", { className: "dt-lp-actions dt-lp-actions--start", children: [
928
+ cta ? /* @__PURE__ */ jsx19(BookDemoButton, { size: "lg", children: cta }) : null,
929
+ secondary ? /* @__PURE__ */ jsx19(TextLink, { href: secondary.href, children: secondary.label }) : null
930
+ ] })
931
+ ] }) });
932
+ }
933
+
934
+ // src/island.jsx
935
+ import { createRoot } from "react-dom/client";
936
+ import { jsx as jsx20 } from "react/jsx-runtime";
937
+ function createDemoIsland(props) {
938
+ let root;
939
+ let returnTo = null;
940
+ const close = () => {
941
+ root.render(null);
942
+ const target = returnTo;
943
+ if (target) setTimeout(() => target.focus(), 0);
944
+ };
945
+ return {
946
+ openDemo(trigger) {
947
+ returnTo = trigger || null;
948
+ if (!root) {
949
+ const host = document.createElement("div");
950
+ host.id = "demo-dialog-root";
951
+ document.body.appendChild(host);
952
+ root = createRoot(host);
953
+ }
954
+ const p = typeof props === "function" ? props() : props;
955
+ root.render(/* @__PURE__ */ jsx20(DemoDialog, { ...p, onClose: close }, Date.now()));
956
+ },
957
+ close
958
+ };
959
+ }
960
+
961
+ // src/brands.js
962
+ var brands = {
963
+ billpro: {
964
+ id: "billpro",
965
+ name: "BillPro",
966
+ primary: "#15803d",
967
+ // current #19a249, darkened one step for AA contrast on white
968
+ accent: "#4ade80",
969
+ logoSrc: "/brand/billpro-mark.svg",
970
+ headline: "Billing, inventory and GST \u2014 done right, every day.",
971
+ pitch: "GST invoices, stock, purchases and accounts for growing Indian businesses.",
972
+ points: ["GST-ready invoices and returns", "Live inventory across branches", "Payments, ledgers and reports"],
973
+ category: "Billing & Inventory \xB7 by Dreamtree-Org"
974
+ },
975
+ taskpro: {
976
+ id: "taskpro",
977
+ name: "TaskPro",
978
+ primary: "#065f46",
979
+ accent: "#34d399",
980
+ logoSrc: "/taskpro-mark-48.webp",
981
+ headline: "Projects and workflows, shaped the way your team works.",
982
+ pitch: "Configurable project types, statuses and automations \u2014 one place for every task.",
983
+ points: ["Your own workflows and statuses", "Boards, timelines and my-work", "Automations and audit trail"],
984
+ category: "Project & Workflow Management \xB7 by Dreamtree-Org"
985
+ },
986
+ venturepro: {
987
+ id: "venturepro",
988
+ name: "VenturePro",
989
+ primary: "#1e3a6e",
990
+ accent: "#e5ad42",
991
+ // accent-400, as shipped
992
+ panel: "#182e57",
993
+ // primary-700, as shipped
994
+ logoSrc: "/brand/venturepro-mark.svg",
995
+ headline: "Every venture, block and flat \u2014 in one place.",
996
+ pitch: "Create ventures, lay out blocks and units, track sales, rentals and collections across your whole portfolio.",
997
+ points: [],
998
+ category: "Builder Property & Apartment Management System"
999
+ },
1000
+ campaignpro: {
1001
+ id: "campaignpro",
1002
+ name: "CampaignPro",
1003
+ primary: "#6d28d9",
1004
+ // PROPOSED — was the same red as firepro
1005
+ accent: "#a78bfa",
1006
+ headline: "Run every campaign from one pipeline.",
1007
+ pitch: "Plan, send and track campaigns across channels with your whole team.",
1008
+ points: ["Multi-channel campaigns", "Pipeline and follow-ups", "Team roles and approvals"],
1009
+ category: "Campaign Management \xB7 by Dreamtree-Org"
1010
+ },
1011
+ firepro: {
1012
+ id: "firepro",
1013
+ name: "FirePro",
1014
+ primary: "#b42318",
1015
+ // keeps the red — it is the fire-safety product
1016
+ accent: "#fb923c",
1017
+ headline: "Fire safety compliance, inspections and service \u2014 on schedule.",
1018
+ pitch: "Assets, inspections, service jobs and certificates for fire-safety teams.",
1019
+ points: ["Scheduled inspections", "Asset and site registers", "Certificates and reports"],
1020
+ category: "Fire Safety Operations \xB7 by Dreamtree-Org"
1021
+ },
1022
+ procurementpro: {
1023
+ id: "procurementpro",
1024
+ name: "ProcurementPro",
1025
+ primary: "#1d4ed8",
1026
+ // current #2563eb, one step darker for panel contrast
1027
+ accent: "#60a5fa",
1028
+ headline: "From request to purchase order, without the chase.",
1029
+ pitch: "RFQs, quotations, approvals and purchase orders in one audited flow.",
1030
+ points: ["RFQs and quotation comparison", "Approval workflows", "Purchase orders and vendors"],
1031
+ category: "Procurement \xB7 by Dreamtree-Org"
1032
+ },
1033
+ fitweavepro: {
1034
+ id: "fitweavepro",
1035
+ name: "FitWeavePro",
1036
+ primary: "#be185d",
1037
+ // PROPOSED — was Tailwind blue
1038
+ accent: "#f9a8d4",
1039
+ headline: "Made-to-measure, made simple.",
1040
+ pitch: "Measurements, patterns and orders for tailoring shops.",
1041
+ points: ["Customer measurements", "Pattern and cutting plans", "Orders and deliveries"],
1042
+ category: "Tailoring Shop Management \xB7 by Dreamtree-Org"
1043
+ },
1044
+ aiagentbuilder: {
1045
+ id: "aiagentbuilder",
1046
+ name: "AI Agent Builder",
1047
+ primary: "#0f766e",
1048
+ // PROPOSED — was blue, same as procurementpro
1049
+ accent: "#5eead4",
1050
+ headline: "Build, approve and ship AI agents your team can trust.",
1051
+ pitch: "Multi-provider agents with approvals, knowledge bases, voice and an MCP server.",
1052
+ points: ["Human-in-the-loop approvals", "Knowledge bases (RAG)", "Voice calls and campaigns"],
1053
+ category: "AI Agent Platform \xB7 by Dreamtree-Org"
1054
+ },
1055
+ runloompro: {
1056
+ id: "runloompro",
1057
+ name: "RunLoom",
1058
+ primary: "#4f46e5",
1059
+ accent: "#a5b4fc",
1060
+ headline: "Missions, run reliably.",
1061
+ pitch: "Orchestrate long-running work across workers with full telemetry.",
1062
+ points: ["Durable missions", "Node and Python workers", "Telemetry built in"],
1063
+ category: "by Dreamtree-Org"
1064
+ },
1065
+ appforge: {
1066
+ id: "appforge",
1067
+ name: "AppForge",
1068
+ primary: "#1f3864",
1069
+ accent: "#2563eb",
1070
+ headline: "Design, build and publish business apps.",
1071
+ pitch: "Schema-driven apps with policy, review and one-click release.",
1072
+ points: ["Visual builder", "Policies and RBAC", "Review and publish"],
1073
+ category: "by Dreamtree-Org"
1074
+ }
1075
+ };
1076
+ function getBrand(id) {
1077
+ const b = brands[id];
1078
+ if (!b) throw new Error(`landing-kit: unknown brand "${id}" (known: ${Object.keys(brands).join(", ")})`);
1079
+ return b;
1080
+ }
1081
+ export {
1082
+ Audiences,
1083
+ AuthShell,
1084
+ BookDemoButton,
1085
+ BrandLockup,
1086
+ BrandMark2 as BrandMark,
1087
+ Breadcrumbs,
1088
+ Cta,
1089
+ DEFAULT_DEMO_FIELDS,
1090
+ DemoCta,
1091
+ DemoDialog,
1092
+ DreamtreeFooter,
1093
+ DreamtreeMark,
1094
+ Eyebrow,
1095
+ Faq,
1096
+ FeatureSection,
1097
+ Header,
1098
+ Hero,
1099
+ HowItWorks,
1100
+ LandingPage,
1101
+ LandingProvider,
1102
+ MarketingLayout,
1103
+ PageHero,
1104
+ PreviewFrame,
1105
+ Section,
1106
+ SectionIntro,
1107
+ SmartLink,
1108
+ TextLink,
1109
+ Wordmark,
1110
+ Wrap,
1111
+ brandStyle,
1112
+ brands,
1113
+ createDemoIsland,
1114
+ getBrand,
1115
+ resolveHref,
1116
+ useLanding
1117
+ };