@caqophony/ui 0.3.1 → 0.4.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.
@@ -0,0 +1,1422 @@
1
+ 'use client';
2
+ import {
3
+ Button,
4
+ Container,
5
+ NavbarMenu
6
+ } from "./chunk-PKP3ZIRB.js";
7
+ import {
8
+ ScrollHintXMark
9
+ } from "./chunk-BAW3RIMF.js";
10
+ import {
11
+ cn
12
+ } from "./chunk-DP5WQAPO.js";
13
+
14
+ // src/marketing/marketing-header.tsx
15
+ import * as React from "react";
16
+ import { createPortal } from "react-dom";
17
+ import { jsx, jsxs } from "react/jsx-runtime";
18
+ var ON_DARK_GROUND = "text-background dark:text-foreground";
19
+ var DARK_MENU_BUTTON = cn(
20
+ ON_DARK_GROUND,
21
+ "hover:bg-background/10 hover:text-background dark:hover:bg-foreground/10 dark:hover:text-foreground"
22
+ );
23
+ var FOLDED_AWAY = "md:invisible md:absolute md:w-0 md:overflow-hidden";
24
+ var px = (value) => parseFloat(value) || 0;
25
+ function useFold(refs, actionsHost, hasLinks) {
26
+ const [folded, setFolded] = React.useState(false);
27
+ React.useLayoutEffect(() => {
28
+ const { bar, brand, brandText, nav, links, actionsRow, actionsHome } = refs;
29
+ if (!bar.current || !brand.current || !brandText.current) return;
30
+ let actionsWidth = 0;
31
+ const measure = () => {
32
+ const row = bar.current;
33
+ const mark = brand.current;
34
+ const text = brandText.current;
35
+ if (!row || !mark || !text) return;
36
+ const shown = nav.current ?? actionsRow.current;
37
+ if (!shown || getComputedStyle(shown).display === "none") return;
38
+ const home = actionsHome.current;
39
+ if (home && (!actionsHost || home.contains(actionsHost)))
40
+ actionsWidth = home.getBoundingClientRect().width;
41
+ const barStyle = getComputedStyle(row);
42
+ const room = row.clientWidth - px(barStyle.paddingLeft) - px(barStyle.paddingRight);
43
+ const brandWidth = mark.getBoundingClientRect().width - text.getBoundingClientRect().width + text.scrollWidth;
44
+ const linksWidth = nav.current ? px(getComputedStyle(nav.current).marginLeft) + (links.current?.getBoundingClientRect().width ?? 0) : 0;
45
+ const actionsSpace = actionsRow.current ? nav.current ? px(getComputedStyle(actionsRow.current).marginLeft) : px(getComputedStyle(actionsRow.current).paddingLeft) : 0;
46
+ setFolded(
47
+ Math.ceil(brandWidth + linksWidth + actionsSpace + actionsWidth) > room
48
+ );
49
+ };
50
+ measure();
51
+ const observer = new ResizeObserver(measure);
52
+ for (const el of [
53
+ bar.current,
54
+ brandText.current,
55
+ links.current,
56
+ actionsHome.current
57
+ ])
58
+ if (el) observer.observe(el);
59
+ return () => observer.disconnect();
60
+ }, [refs, actionsHost, hasLinks]);
61
+ return folded;
62
+ }
63
+ function MarketingHeader({
64
+ brandName,
65
+ brandInitials,
66
+ navItems = [],
67
+ actions,
68
+ className,
69
+ variant = "light"
70
+ }) {
71
+ const isDark = variant === "dark";
72
+ const hasLinks = navItems.length > 0;
73
+ const bar = React.useRef(null);
74
+ const brand = React.useRef(null);
75
+ const brandText = React.useRef(null);
76
+ const nav = React.useRef(null);
77
+ const links = React.useRef(null);
78
+ const actionsRow = React.useRef(null);
79
+ const actionsHome = React.useRef(null);
80
+ const refs = React.useMemo(
81
+ () => ({ bar, brand, brandText, nav, links, actionsRow, actionsHome }),
82
+ []
83
+ );
84
+ const [actionsHost, setActionsHost] = React.useState(
85
+ null
86
+ );
87
+ const hasActions = Boolean(actions);
88
+ React.useLayoutEffect(() => {
89
+ if (!hasActions) return;
90
+ const host = document.createElement("div");
91
+ host.className = "contents";
92
+ actionsHome.current?.appendChild(host);
93
+ setActionsHost(host);
94
+ return () => {
95
+ host.remove();
96
+ setActionsHost(null);
97
+ };
98
+ }, [hasActions]);
99
+ const drawerActions = React.useCallback(
100
+ (slot) => {
101
+ if (actionsHost) (slot ?? actionsHome.current)?.appendChild(actionsHost);
102
+ },
103
+ [actionsHost]
104
+ );
105
+ const folded = useFold(refs, actionsHost, hasLinks);
106
+ return /* @__PURE__ */ jsxs(
107
+ "header",
108
+ {
109
+ ref: bar,
110
+ "data-testid": "marketing-header",
111
+ "data-folded": folded ? "" : void 0,
112
+ className: cn(
113
+ "sticky top-0 z-50 flex h-16 items-center px-6 lg:px-12",
114
+ isDark ? "justify-between border-b border-primary-foreground/[0.06] bg-foreground/85 backdrop-blur-md dark:bg-background/85" : "border-b border-border bg-card dark:border-border dark:bg-card",
115
+ className
116
+ ),
117
+ children: [
118
+ /* @__PURE__ */ jsxs(
119
+ "a",
120
+ {
121
+ ref: brand,
122
+ href: "/",
123
+ className: cn(
124
+ "flex min-w-0 items-center gap-2.5 text-base font-bold no-underline",
125
+ isDark ? ON_DARK_GROUND : "text-foreground dark:text-foreground"
126
+ ),
127
+ children: [
128
+ /* @__PURE__ */ jsx("div", { className: "bg-primary-solid flex size-8 shrink-0 items-center justify-center rounded-lg text-xs font-extrabold text-primary-foreground", children: brandInitials }),
129
+ /* @__PURE__ */ jsx("span", { ref: brandText, className: "truncate", children: brandName })
130
+ ]
131
+ }
132
+ ),
133
+ hasLinks && /* @__PURE__ */ jsx(
134
+ "nav",
135
+ {
136
+ ref: nav,
137
+ className: cn(
138
+ "hidden min-w-0 flex-1 overflow-x-clip md:flex",
139
+ isDark ? "ml-6" : "ml-6 lg:ml-12",
140
+ folded && FOLDED_AWAY
141
+ ),
142
+ children: /* @__PURE__ */ jsx(
143
+ "div",
144
+ {
145
+ ref: links,
146
+ className: cn(
147
+ "flex w-max items-center gap-4 lg:gap-8",
148
+ isDark && "mx-auto"
149
+ ),
150
+ children: navItems.map((item) => /* @__PURE__ */ jsx(
151
+ "a",
152
+ {
153
+ href: item.href,
154
+ className: cn(
155
+ "whitespace-nowrap text-sm font-medium no-underline transition-colors",
156
+ isDark ? "flex min-h-[44px] items-center text-background/70 hover:text-background dark:text-foreground/70 dark:hover:text-foreground" : item.active ? "text-primary-text font-semibold" : "text-muted-foreground hover:text-foreground dark:text-muted-foreground dark:hover:text-foreground"
157
+ ),
158
+ children: item.label
159
+ },
160
+ item.label
161
+ ))
162
+ }
163
+ )
164
+ }
165
+ ),
166
+ actions && /* @__PURE__ */ jsx(
167
+ "div",
168
+ {
169
+ ref: actionsRow,
170
+ className: cn(
171
+ "hidden shrink-0 md:flex",
172
+ hasLinks ? "ml-6" : "ml-auto pl-6",
173
+ folded && FOLDED_AWAY
174
+ ),
175
+ children: /* @__PURE__ */ jsx("div", { ref: actionsHome, className: "flex w-max items-center gap-3", children: actionsHost ? createPortal(actions, actionsHost) : actions })
176
+ }
177
+ ),
178
+ (hasLinks || actions) && /* @__PURE__ */ jsx(
179
+ NavbarMenu,
180
+ {
181
+ folded,
182
+ triggerClassName: isDark ? DARK_MENU_BUTTON : void 0,
183
+ links: hasLinks ? navItems.map((item) => /* @__PURE__ */ jsx(
184
+ "a",
185
+ {
186
+ href: item.href,
187
+ className: cn(
188
+ "text-sm font-medium no-underline",
189
+ item.active ? "text-primary-text" : "text-foreground"
190
+ ),
191
+ children: item.label
192
+ },
193
+ item.label
194
+ )) : void 0,
195
+ actions: actions ? /* @__PURE__ */ jsx(
196
+ "div",
197
+ {
198
+ ref: drawerActions,
199
+ className: "flex flex-col gap-2 [&>*>*]:w-full"
200
+ }
201
+ ) : void 0
202
+ }
203
+ )
204
+ ]
205
+ }
206
+ );
207
+ }
208
+
209
+ // src/marketing/marketing-hero.tsx
210
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
211
+ function MarketingHero({
212
+ headline,
213
+ subtitle,
214
+ actions,
215
+ visual,
216
+ layout = "centered",
217
+ className
218
+ }) {
219
+ if (layout === "split") {
220
+ return /* @__PURE__ */ jsx2(
221
+ "section",
222
+ {
223
+ "data-testid": "marketing-hero",
224
+ className: cn("bg-primary-solid px-6 py-20 lg:px-12", className),
225
+ children: /* @__PURE__ */ jsxs2("div", { className: "mx-auto grid max-w-[1200px] grid-cols-1 items-center gap-16 md:grid-cols-2", children: [
226
+ /* @__PURE__ */ jsxs2("div", { className: "flex flex-col gap-5", children: [
227
+ /* @__PURE__ */ jsx2("h1", { className: "text-4xl font-black leading-tight text-primary-foreground md:text-5xl", children: headline }),
228
+ /* @__PURE__ */ jsx2("p", { className: "text-lg font-normal leading-relaxed text-primary-foreground", children: subtitle }),
229
+ actions && /* @__PURE__ */ jsx2("div", { className: "flex flex-wrap gap-4", children: actions })
230
+ ] }),
231
+ visual && /* @__PURE__ */ jsx2("div", { className: "flex items-end justify-end", children: visual })
232
+ ] })
233
+ }
234
+ );
235
+ }
236
+ return /* @__PURE__ */ jsxs2(
237
+ "section",
238
+ {
239
+ "data-testid": "marketing-hero",
240
+ className: cn(
241
+ "bg-primary-solid px-6 pb-20 pt-24 text-center lg:px-12",
242
+ className
243
+ ),
244
+ children: [
245
+ /* @__PURE__ */ jsx2("h1", { className: "mx-auto mb-6 max-w-[800px] text-4xl font-black leading-tight text-primary-foreground md:text-6xl", children: headline }),
246
+ /* @__PURE__ */ jsx2("p", { className: "mx-auto mb-10 max-w-[640px] text-xl font-normal leading-relaxed text-primary-foreground", children: subtitle }),
247
+ actions && /* @__PURE__ */ jsx2("div", { className: "flex flex-wrap justify-center gap-4", children: actions })
248
+ ]
249
+ }
250
+ );
251
+ }
252
+
253
+ // src/marketing/feature-card.tsx
254
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
255
+ function FeatureCard({
256
+ icon,
257
+ title,
258
+ description,
259
+ className
260
+ }) {
261
+ return /* @__PURE__ */ jsxs3(
262
+ "div",
263
+ {
264
+ "data-testid": "feature-card",
265
+ className: cn(
266
+ "rounded-xl border border-border bg-card p-6 shadow-sm dark:border-border dark:bg-card",
267
+ className
268
+ ),
269
+ children: [
270
+ /* @__PURE__ */ jsx3("div", { className: "mb-4 flex h-12 w-12 items-center justify-center rounded-lg bg-primary/10 dark:bg-primary/10", children: /* @__PURE__ */ jsx3("div", { className: "text-primary-text dark:text-primary-text h-6 w-6", children: icon }) }),
271
+ /* @__PURE__ */ jsx3("h3", { className: "mb-2 text-base font-bold text-foreground dark:text-foreground", children: title }),
272
+ /* @__PURE__ */ jsx3("p", { className: "text-sm leading-relaxed text-muted-foreground dark:text-muted-foreground", children: description })
273
+ ]
274
+ }
275
+ );
276
+ }
277
+
278
+ // src/marketing/pricing-tier.tsx
279
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
280
+ function PricingTier({
281
+ name,
282
+ price,
283
+ period,
284
+ features,
285
+ highlighted = false,
286
+ action,
287
+ className
288
+ }) {
289
+ return /* @__PURE__ */ jsxs4(
290
+ "div",
291
+ {
292
+ "data-testid": "pricing-tier",
293
+ className: cn(
294
+ "relative rounded-xl border bg-card p-8 text-center dark:bg-card",
295
+ highlighted ? "border-primary shadow-lg ring-2 ring-primary/10 dark:border-primary dark:ring-primary/10" : "border-border dark:border-border",
296
+ className
297
+ ),
298
+ children: [
299
+ /* @__PURE__ */ jsx4("div", { className: "mb-2 text-lg font-bold text-foreground dark:text-foreground", children: name }),
300
+ /* @__PURE__ */ jsx4("div", { className: "text-primary-text dark:text-primary-text text-4xl font-black", children: price }),
301
+ /* @__PURE__ */ jsx4("div", { className: "mb-6 text-sm text-muted-foreground dark:text-muted-foreground", children: period }),
302
+ /* @__PURE__ */ jsx4("ul", { className: "mb-6 space-y-1.5 text-left", children: features.map((feature) => /* @__PURE__ */ jsxs4(
303
+ "li",
304
+ {
305
+ className: "relative pl-6 text-sm text-foreground dark:text-foreground",
306
+ children: [
307
+ /* @__PURE__ */ jsx4("span", { className: "bg-intent-success dark:bg-intent-success absolute left-0 top-[7px] h-2 w-2 rounded-full" }),
308
+ feature
309
+ ]
310
+ },
311
+ feature
312
+ )) }),
313
+ action && /* @__PURE__ */ jsx4("div", { className: "w-full", children: action })
314
+ ]
315
+ }
316
+ );
317
+ }
318
+
319
+ // src/marketing/faq-item.tsx
320
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
321
+ function MarketingFAQItem({
322
+ question,
323
+ answer,
324
+ dark = false,
325
+ className
326
+ }) {
327
+ return /* @__PURE__ */ jsxs5(
328
+ "div",
329
+ {
330
+ "data-testid": "marketing-faq-item",
331
+ className: cn(
332
+ "border-b py-6",
333
+ dark ? "border-muted-foreground/20 dark:border-muted-foreground/20" : "border-border dark:border-border",
334
+ className
335
+ ),
336
+ children: [
337
+ /* @__PURE__ */ jsx5(
338
+ "h3",
339
+ {
340
+ className: cn(
341
+ "mb-3 text-base font-semibold",
342
+ dark ? "text-primary-foreground dark:text-foreground" : "text-foreground dark:text-foreground"
343
+ ),
344
+ children: question
345
+ }
346
+ ),
347
+ /* @__PURE__ */ jsx5(
348
+ "p",
349
+ {
350
+ className: cn(
351
+ "text-sm leading-[1.7]",
352
+ dark ? "text-primary-foreground/70 dark:text-muted-foreground" : "text-muted-foreground dark:text-muted-foreground"
353
+ ),
354
+ children: answer
355
+ }
356
+ )
357
+ ]
358
+ }
359
+ );
360
+ }
361
+
362
+ // src/marketing/cta-banner.tsx
363
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
364
+ function CTABanner({
365
+ headline,
366
+ description,
367
+ actions,
368
+ className
369
+ }) {
370
+ return /* @__PURE__ */ jsxs6(
371
+ "section",
372
+ {
373
+ "data-testid": "cta-banner",
374
+ className: cn(
375
+ "bg-primary-solid px-6 py-20 text-center lg:px-12",
376
+ className
377
+ ),
378
+ children: [
379
+ /* @__PURE__ */ jsx6("h2", { className: "mb-4 text-4xl font-extrabold text-primary-foreground", children: headline }),
380
+ /* @__PURE__ */ jsx6("p", { className: "mb-8 text-lg text-primary-foreground", children: description }),
381
+ actions && /* @__PURE__ */ jsx6("div", { className: "flex flex-wrap justify-center gap-4", children: actions })
382
+ ]
383
+ }
384
+ );
385
+ }
386
+
387
+ // src/marketing/marketing-cta.tsx
388
+ import { jsx as jsx7 } from "react/jsx-runtime";
389
+ var TREATMENT = {
390
+ light: {
391
+ // the edge is `primary-text`, which reads on the page in either scheme: a
392
+ // dark primary's fill alone sinks into a dark page
393
+ primary: "border-primary-text bg-primary-solid text-primary-foreground hover:opacity-hover",
394
+ secondary: "border-primary-text text-primary-text hover:bg-muted"
395
+ },
396
+ brand: {
397
+ primary: "bg-brand-fill text-brand-fill-foreground hover:opacity-hover",
398
+ secondary: "border-brand-fill text-brand-fill hover:bg-brand-fill hover:text-brand-fill-foreground"
399
+ },
400
+ dark: {
401
+ primary: "bg-night-accent text-night-accent-foreground hover:opacity-hover",
402
+ secondary: "border-night-muted-foreground text-night-foreground hover:bg-night-muted"
403
+ },
404
+ image: {
405
+ primary: "bg-night-accent text-night-accent-foreground shadow-lg hover:opacity-hover",
406
+ secondary: "border-night-muted-foreground bg-night-0 text-night-foreground shadow-lg hover:bg-night-muted"
407
+ }
408
+ };
409
+ var FOCUS = {
410
+ light: "focus-visible:outline-primary-text",
411
+ brand: "focus-visible:outline-brand-fill",
412
+ dark: "focus-visible:outline-night-foreground",
413
+ image: "focus-visible:outline-night-foreground"
414
+ };
415
+ function MarketingCta({
416
+ href,
417
+ ground,
418
+ emphasis,
419
+ className,
420
+ children,
421
+ ...anchor
422
+ }) {
423
+ return /* @__PURE__ */ jsx7(
424
+ "a",
425
+ {
426
+ ...anchor,
427
+ href,
428
+ "data-cta-ground": ground,
429
+ "data-cta-emphasis": emphasis,
430
+ "data-border-current": emphasis === "secondary" ? "" : void 0,
431
+ className: cn(
432
+ "inline-flex min-h-12 items-center justify-center rounded-lg border-2 border-transparent px-6 py-2.5 text-center text-base font-semibold",
433
+ "motion-safe:duration-fast motion-safe:transition-colors",
434
+ "focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2",
435
+ TREATMENT[ground][emphasis],
436
+ FOCUS[ground],
437
+ className
438
+ ),
439
+ children
440
+ }
441
+ );
442
+ }
443
+
444
+ // src/marketing/how-it-works-step.tsx
445
+ import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
446
+ function HowItWorksStep({
447
+ stepNumber,
448
+ title,
449
+ description,
450
+ className
451
+ }) {
452
+ return /* @__PURE__ */ jsxs7(
453
+ "div",
454
+ {
455
+ "data-testid": "how-it-works-step",
456
+ className: cn("text-center", className),
457
+ children: [
458
+ /* @__PURE__ */ jsx8("div", { className: "bg-primary-solid mx-auto mb-4 flex h-12 w-12 items-center justify-center rounded-full text-xl font-bold text-primary-foreground", children: stepNumber }),
459
+ /* @__PURE__ */ jsx8("h3", { className: "mb-2 text-base font-bold text-foreground dark:text-foreground", children: title }),
460
+ /* @__PURE__ */ jsx8("p", { className: "text-sm leading-relaxed text-muted-foreground dark:text-muted-foreground", children: description })
461
+ ]
462
+ }
463
+ );
464
+ }
465
+
466
+ // src/marketing/use-case-card.tsx
467
+ import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
468
+ function UseCaseCard({
469
+ persona,
470
+ scenario,
471
+ outcome,
472
+ className
473
+ }) {
474
+ return /* @__PURE__ */ jsxs8(
475
+ "div",
476
+ {
477
+ "data-testid": "use-case-card",
478
+ className: cn(
479
+ "rounded-xl border border-border bg-card p-8 dark:border-border dark:bg-card",
480
+ className
481
+ ),
482
+ children: [
483
+ /* @__PURE__ */ jsx9("h3", { className: "mb-3 text-lg font-bold text-foreground dark:text-foreground", children: persona }),
484
+ /* @__PURE__ */ jsx9("p", { className: "mb-4 text-sm leading-relaxed text-muted-foreground dark:text-muted-foreground", children: scenario }),
485
+ /* @__PURE__ */ jsx9("p", { className: "text-intent-success-foreground text-sm font-medium leading-normal", children: outcome })
486
+ ]
487
+ }
488
+ );
489
+ }
490
+
491
+ // src/marketing/blog-grid.tsx
492
+ import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
493
+ function PostCard({
494
+ post,
495
+ featured = false
496
+ }) {
497
+ const Wrapper = post.href ? "a" : "div";
498
+ const wrapperProps = post.href ? { href: post.href } : {};
499
+ return /* @__PURE__ */ jsxs9(
500
+ Wrapper,
501
+ {
502
+ ...wrapperProps,
503
+ className: cn(
504
+ "group flex flex-col overflow-hidden rounded-xl border border-border bg-card shadow-sm transition-shadow hover:shadow-md",
505
+ featured && "sm:flex-row"
506
+ ),
507
+ children: [
508
+ post.thumbnail && /* @__PURE__ */ jsx10(
509
+ "div",
510
+ {
511
+ className: cn(
512
+ "overflow-hidden bg-muted",
513
+ featured ? "aspect-video sm:w-1/2" : "aspect-video w-full"
514
+ ),
515
+ children: /* @__PURE__ */ jsx10(
516
+ "img",
517
+ {
518
+ src: post.thumbnail,
519
+ alt: post.title,
520
+ className: "h-full w-full object-cover transition-transform group-hover:scale-105"
521
+ }
522
+ )
523
+ }
524
+ ),
525
+ /* @__PURE__ */ jsxs9(
526
+ "div",
527
+ {
528
+ className: cn("flex flex-1 flex-col gap-2 p-4", featured && "sm:p-6"),
529
+ children: [
530
+ post.category && /* @__PURE__ */ jsx10("span", { className: "bg-primary-solid w-fit rounded-full px-2.5 py-0.5 text-xs font-medium text-primary-foreground", children: post.category }),
531
+ /* @__PURE__ */ jsx10(
532
+ "h3",
533
+ {
534
+ className: cn(
535
+ "line-clamp-2 font-semibold text-foreground",
536
+ featured ? "text-lg" : "text-sm"
537
+ ),
538
+ children: post.title
539
+ }
540
+ ),
541
+ post.excerpt && /* @__PURE__ */ jsx10("p", { className: "line-clamp-2 text-xs text-muted-foreground", children: post.excerpt }),
542
+ /* @__PURE__ */ jsxs9("div", { className: "mt-auto flex items-center gap-2 pt-2", children: [
543
+ post.authorAvatar && /* @__PURE__ */ jsx10(
544
+ "img",
545
+ {
546
+ src: post.authorAvatar,
547
+ alt: post.author ?? "",
548
+ className: "h-6 w-6 rounded-full object-cover"
549
+ }
550
+ ),
551
+ post.author && /* @__PURE__ */ jsx10("span", { className: "text-xs font-medium text-foreground", children: post.author }),
552
+ /* @__PURE__ */ jsx10("span", { className: "text-xs text-muted-foreground", children: post.date })
553
+ ] })
554
+ ]
555
+ }
556
+ )
557
+ ]
558
+ }
559
+ );
560
+ }
561
+ function PostRow({ post }) {
562
+ const Wrapper = post.href ? "a" : "div";
563
+ const wrapperProps = post.href ? { href: post.href } : {};
564
+ return /* @__PURE__ */ jsxs9(
565
+ Wrapper,
566
+ {
567
+ ...wrapperProps,
568
+ className: "group flex gap-4 border-b border-border py-4 last:border-b-0",
569
+ children: [
570
+ post.thumbnail && /* @__PURE__ */ jsx10("div", { className: "h-20 w-28 flex-shrink-0 overflow-hidden rounded-lg bg-muted", children: /* @__PURE__ */ jsx10(
571
+ "img",
572
+ {
573
+ src: post.thumbnail,
574
+ alt: post.title,
575
+ className: "h-full w-full object-cover"
576
+ }
577
+ ) }),
578
+ /* @__PURE__ */ jsxs9("div", { className: "flex flex-1 flex-col gap-1", children: [
579
+ post.category && /* @__PURE__ */ jsx10("span", { className: "bg-primary-solid w-fit rounded-full px-2 py-0.5 text-xs font-medium text-primary-foreground", children: post.category }),
580
+ /* @__PURE__ */ jsx10("h3", { className: "line-clamp-1 text-sm font-semibold text-foreground group-hover:underline", children: post.title }),
581
+ post.excerpt && /* @__PURE__ */ jsx10("p", { className: "line-clamp-1 text-xs text-muted-foreground", children: post.excerpt }),
582
+ /* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-2", children: [
583
+ post.author && /* @__PURE__ */ jsx10("span", { className: "text-xs text-muted-foreground", children: post.author }),
584
+ /* @__PURE__ */ jsx10("span", { className: "text-xs text-muted-foreground", children: post.date })
585
+ ] })
586
+ ] })
587
+ ]
588
+ }
589
+ );
590
+ }
591
+ function BlogGrid({
592
+ variant = "threeColumn",
593
+ posts,
594
+ heading,
595
+ description,
596
+ className,
597
+ ...props
598
+ }) {
599
+ return /* @__PURE__ */ jsxs9(
600
+ "section",
601
+ {
602
+ className: cn("w-full px-4 py-16 sm:px-6 lg:px-8", className),
603
+ ...props,
604
+ children: [
605
+ (heading || description) && /* @__PURE__ */ jsxs9("div", { className: "mx-auto mb-10 max-w-2xl text-center", children: [
606
+ heading && /* @__PURE__ */ jsx10("h2", { className: "text-2xl font-bold text-foreground sm:text-3xl", children: heading }),
607
+ description && /* @__PURE__ */ jsx10("p", { className: "mt-3 text-sm text-muted-foreground", children: description })
608
+ ] }),
609
+ variant === "threeColumn" && /* @__PURE__ */ jsx10("div", { className: "mx-auto grid max-w-5xl grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3", children: posts.map((p) => /* @__PURE__ */ jsx10(PostCard, { post: p }, p.id)) }),
610
+ variant === "featured" && posts.length > 0 && /* @__PURE__ */ jsxs9("div", { className: "mx-auto max-w-5xl space-y-6", children: [
611
+ /* @__PURE__ */ jsx10(PostCard, { post: posts[0], featured: true }),
612
+ posts.length > 1 && /* @__PURE__ */ jsx10("div", { className: "grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3", children: posts.slice(1).map((p) => /* @__PURE__ */ jsx10(PostCard, { post: p }, p.id)) })
613
+ ] }),
614
+ variant === "list" && /* @__PURE__ */ jsx10("div", { className: "mx-auto max-w-3xl", children: posts.map((p) => /* @__PURE__ */ jsx10(PostRow, { post: p }, p.id)) })
615
+ ]
616
+ }
617
+ );
618
+ }
619
+
620
+ // src/marketing/comparison-table.tsx
621
+ import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
622
+ function ComparisonTable({
623
+ variant = "simple",
624
+ plans,
625
+ features,
626
+ heading,
627
+ className,
628
+ ...props
629
+ }) {
630
+ const featuredPlan = variant === "featured" ? plans.find((p) => p.featured) : null;
631
+ return /* @__PURE__ */ jsxs10("div", { className: cn("w-full", className), ...props, children: [
632
+ heading && /* @__PURE__ */ jsx11("h2", { className: "mb-8 text-center text-2xl font-bold text-foreground sm:text-3xl", children: heading }),
633
+ /* @__PURE__ */ jsxs10("div", { className: "flex w-full overflow-x-auto", children: [
634
+ /* @__PURE__ */ jsxs10("table", { className: "w-full border-collapse", children: [
635
+ /* @__PURE__ */ jsx11("thead", { children: /* @__PURE__ */ jsxs10("tr", { children: [
636
+ /* @__PURE__ */ jsx11("td", { className: "min-w-[180px] border-b border-border p-4" }),
637
+ plans.map((plan) => /* @__PURE__ */ jsxs10(
638
+ "th",
639
+ {
640
+ className: cn(
641
+ "min-w-[140px] border-b border-l border-border p-4 text-center",
642
+ featuredPlan?.id === plan.id && "bg-primary/5"
643
+ ),
644
+ children: [
645
+ featuredPlan?.id === plan.id && plan.badge && /* @__PURE__ */ jsx11("span", { className: "bg-primary-solid mb-2 inline-block rounded-full px-2.5 py-0.5 text-xs font-medium text-primary-foreground", children: plan.badge }),
646
+ /* @__PURE__ */ jsx11("div", { className: "text-sm font-semibold text-foreground", children: plan.name }),
647
+ plan.description && /* @__PURE__ */ jsx11("div", { className: "mt-0.5 text-xs text-muted-foreground", children: plan.description })
648
+ ]
649
+ },
650
+ plan.id
651
+ ))
652
+ ] }) }),
653
+ /* @__PURE__ */ jsx11("tbody", { children: features.map((feature, i) => /* @__PURE__ */ jsxs10(
654
+ "tr",
655
+ {
656
+ className: i % 2 === 1 ? "bg-muted/50" : "",
657
+ children: [
658
+ /* @__PURE__ */ jsx11("td", { className: "border-b border-border p-3 text-sm text-foreground", children: feature.label }),
659
+ plans.map((plan) => {
660
+ const val = feature.values[plan.id];
661
+ return /* @__PURE__ */ jsx11(
662
+ "td",
663
+ {
664
+ className: cn(
665
+ "border-b border-l border-border p-3 text-center text-sm",
666
+ featuredPlan?.id === plan.id && "bg-primary/5"
667
+ ),
668
+ children: val === true ? /* @__PURE__ */ jsx11("span", { className: "text-intent-success-foreground", children: "\u2713" }) : val === false ? /* @__PURE__ */ jsx11("span", { className: "text-muted-foreground", children: "\u2014" }) : /* @__PURE__ */ jsx11("span", { className: "text-foreground", children: val })
669
+ },
670
+ plan.id
671
+ );
672
+ })
673
+ ]
674
+ },
675
+ feature.key
676
+ )) })
677
+ ] }),
678
+ /* @__PURE__ */ jsx11(ScrollHintXMark, {})
679
+ ] })
680
+ ] });
681
+ }
682
+
683
+ // src/marketing/logo-cloud.tsx
684
+ import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
685
+ function LogoCloud({
686
+ variant = "simple",
687
+ logos,
688
+ heading,
689
+ className,
690
+ ...props
691
+ }) {
692
+ const logoElement = (logo, idx) => /* @__PURE__ */ jsx12(
693
+ "div",
694
+ {
695
+ className: "flex items-center justify-center px-4 py-2 text-muted-foreground",
696
+ children: logo.icon ? logo.icon : logo.src ? /* @__PURE__ */ jsx12(
697
+ "img",
698
+ {
699
+ src: logo.src,
700
+ alt: logo.name,
701
+ className: "h-8 max-w-[120px] object-contain opacity-60 grayscale transition-opacity hover:opacity-100"
702
+ }
703
+ ) : /* @__PURE__ */ jsx12("span", { className: "text-sm font-semibold", children: logo.name })
704
+ },
705
+ idx
706
+ );
707
+ if (variant === "scrolling") {
708
+ return /* @__PURE__ */ jsxs11(
709
+ "section",
710
+ {
711
+ className: cn("w-full overflow-hidden px-4 py-10 sm:px-6", className),
712
+ ...props,
713
+ children: [
714
+ heading && /* @__PURE__ */ jsx12("p", { className: "mb-6 text-center text-sm text-muted-foreground", children: heading }),
715
+ /* @__PURE__ */ jsxs11("div", { className: "relative flex gap-12 overflow-hidden", children: [
716
+ /* @__PURE__ */ jsx12("div", { className: "animate-scroll flex shrink-0 gap-12", children: logos.map((l, i) => logoElement(l, i)) }),
717
+ /* @__PURE__ */ jsx12("div", { className: "animate-scroll flex shrink-0 gap-12", "aria-hidden": true, children: logos.map((l, i) => logoElement(l, i + logos.length)) })
718
+ ] }),
719
+ /* @__PURE__ */ jsx12("style", { children: `
720
+ @keyframes scroll { from { transform: translateX(0); } to { transform: translateX(-50%); } }
721
+ .animate-scroll { animation: scroll 20s linear infinite; }
722
+ ` })
723
+ ]
724
+ }
725
+ );
726
+ }
727
+ if (variant === "branded") {
728
+ return /* @__PURE__ */ jsxs11(
729
+ "section",
730
+ {
731
+ className: cn(
732
+ "w-full border-y border-border px-4 py-10 sm:px-6",
733
+ className
734
+ ),
735
+ ...props,
736
+ children: [
737
+ heading && /* @__PURE__ */ jsx12("p", { className: "mb-6 text-center text-sm font-medium text-foreground", children: heading }),
738
+ /* @__PURE__ */ jsx12("div", { className: "mx-auto flex max-w-4xl flex-wrap items-center justify-center gap-x-10 gap-y-6", children: logos.map((l, i) => logoElement(l, i)) })
739
+ ]
740
+ }
741
+ );
742
+ }
743
+ return /* @__PURE__ */ jsxs11("section", { className: cn("w-full px-4 py-10 sm:px-6", className), ...props, children: [
744
+ heading && /* @__PURE__ */ jsx12("p", { className: "mb-6 text-center text-sm text-muted-foreground", children: heading }),
745
+ /* @__PURE__ */ jsx12("div", { className: "mx-auto flex max-w-4xl flex-wrap items-center justify-center gap-x-10 gap-y-6", children: logos.map((l, i) => logoElement(l, i)) })
746
+ ] });
747
+ }
748
+
749
+ // src/marketing/team-section.tsx
750
+ import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
751
+ function MemberAvatar({
752
+ member,
753
+ size
754
+ }) {
755
+ const sizeClass = size === "sm" ? "h-12 w-12" : size === "md" ? "h-16 w-16" : "h-24 w-24";
756
+ return member.avatar ? /* @__PURE__ */ jsx13(
757
+ "img",
758
+ {
759
+ src: member.avatar,
760
+ alt: member.name,
761
+ className: cn("rounded-full object-cover", sizeClass)
762
+ }
763
+ ) : /* @__PURE__ */ jsx13(
764
+ "div",
765
+ {
766
+ className: cn(
767
+ "flex items-center justify-center rounded-full bg-muted text-lg font-semibold text-muted-foreground",
768
+ sizeClass
769
+ ),
770
+ children: member.name.split(" ").map((n) => n[0]).join("").slice(0, 2)
771
+ }
772
+ );
773
+ }
774
+ function TeamSection({
775
+ variant = "grid",
776
+ members,
777
+ heading,
778
+ description,
779
+ columns = 3,
780
+ className,
781
+ ...props
782
+ }) {
783
+ const colsClass = columns === 2 ? "sm:grid-cols-2" : columns === 4 ? "sm:grid-cols-2 lg:grid-cols-4" : "sm:grid-cols-2 lg:grid-cols-3";
784
+ return /* @__PURE__ */ jsxs12(
785
+ "section",
786
+ {
787
+ className: cn("w-full px-4 py-16 sm:px-6 lg:px-8", className),
788
+ ...props,
789
+ children: [
790
+ (heading || description) && /* @__PURE__ */ jsxs12("div", { className: "mx-auto mb-12 max-w-2xl text-center", children: [
791
+ heading && /* @__PURE__ */ jsx13("h2", { className: "text-2xl font-bold text-foreground sm:text-3xl", children: heading }),
792
+ description && /* @__PURE__ */ jsx13("p", { className: "mt-3 text-sm text-muted-foreground sm:text-base", children: description })
793
+ ] }),
794
+ variant === "grid" && /* @__PURE__ */ jsx13(
795
+ "div",
796
+ {
797
+ className: cn("mx-auto grid max-w-5xl grid-cols-1 gap-10", colsClass),
798
+ children: members.map((m) => /* @__PURE__ */ jsxs12("div", { className: "flex flex-col items-center text-center", children: [
799
+ /* @__PURE__ */ jsx13(MemberAvatar, { member: m, size: "lg" }),
800
+ /* @__PURE__ */ jsx13("h3", { className: "mt-4 text-sm font-semibold text-foreground", children: m.name }),
801
+ /* @__PURE__ */ jsx13("p", { className: "text-xs text-muted-foreground", children: m.role }),
802
+ m.bio && /* @__PURE__ */ jsx13("p", { className: "mt-2 line-clamp-3 text-xs text-muted-foreground", children: m.bio })
803
+ ] }, m.id))
804
+ }
805
+ ),
806
+ variant === "list" && /* @__PURE__ */ jsx13("div", { className: "mx-auto max-w-3xl divide-y divide-border", children: members.map((m) => /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-4 py-4", children: [
807
+ /* @__PURE__ */ jsx13(MemberAvatar, { member: m, size: "md" }),
808
+ /* @__PURE__ */ jsxs12("div", { className: "flex-1", children: [
809
+ /* @__PURE__ */ jsx13("h3", { className: "text-sm font-semibold text-foreground", children: m.name }),
810
+ /* @__PURE__ */ jsx13("p", { className: "text-xs text-muted-foreground", children: m.role }),
811
+ m.bio && /* @__PURE__ */ jsx13("p", { className: "mt-1 line-clamp-2 text-xs text-muted-foreground", children: m.bio })
812
+ ] }),
813
+ m.socialLinks && m.socialLinks.length > 0 && /* @__PURE__ */ jsx13("div", { className: "flex gap-2", children: m.socialLinks.map((s) => /* @__PURE__ */ jsx13(
814
+ "a",
815
+ {
816
+ href: s.href,
817
+ "aria-label": s.label,
818
+ className: "text-muted-foreground hover:text-foreground",
819
+ children: s.icon ?? s.label
820
+ },
821
+ s.label
822
+ )) })
823
+ ] }, m.id)) }),
824
+ variant === "cards" && /* @__PURE__ */ jsx13(
825
+ "div",
826
+ {
827
+ className: cn("mx-auto grid max-w-5xl grid-cols-1 gap-6", colsClass),
828
+ children: members.map((m) => /* @__PURE__ */ jsxs12(
829
+ "div",
830
+ {
831
+ className: "flex flex-col items-center rounded-xl border border-border bg-card p-6 text-center shadow-sm",
832
+ children: [
833
+ /* @__PURE__ */ jsx13(MemberAvatar, { member: m, size: "lg" }),
834
+ /* @__PURE__ */ jsx13("h3", { className: "mt-4 text-sm font-semibold text-foreground", children: m.name }),
835
+ /* @__PURE__ */ jsx13("p", { className: "text-xs text-muted-foreground", children: m.role }),
836
+ m.bio && /* @__PURE__ */ jsx13("p", { className: "mt-2 line-clamp-3 text-xs text-muted-foreground", children: m.bio }),
837
+ m.socialLinks && m.socialLinks.length > 0 && /* @__PURE__ */ jsx13("div", { className: "mt-3 flex gap-3", children: m.socialLinks.map((s) => /* @__PURE__ */ jsx13(
838
+ "a",
839
+ {
840
+ href: s.href,
841
+ "aria-label": s.label,
842
+ className: "text-muted-foreground hover:text-foreground",
843
+ children: s.icon ?? s.label
844
+ },
845
+ s.label
846
+ )) })
847
+ ]
848
+ },
849
+ m.id
850
+ ))
851
+ }
852
+ )
853
+ ]
854
+ }
855
+ );
856
+ }
857
+
858
+ // src/marketing/newsletter-section.tsx
859
+ import * as React2 from "react";
860
+ import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
861
+ function NewsletterSection({
862
+ variant = "centered",
863
+ heading,
864
+ description,
865
+ ctaLabel,
866
+ placeholder,
867
+ onSubscribe,
868
+ className,
869
+ ...props
870
+ }) {
871
+ const [email, setEmail] = React2.useState("");
872
+ if (onSubscribe && !ctaLabel)
873
+ throw new Error(
874
+ "NewsletterSection: `onSubscribe` needs `ctaLabel`; with no label the form is not drawn"
875
+ );
876
+ const handleSubmit = (e) => {
877
+ e.preventDefault();
878
+ if (email.trim()) onSubscribe?.(email.trim());
879
+ };
880
+ const inputField = /* @__PURE__ */ jsx14(
881
+ "input",
882
+ {
883
+ type: "email",
884
+ value: email,
885
+ onChange: (e) => setEmail(e.target.value),
886
+ placeholder,
887
+ "aria-label": placeholder ?? ctaLabel,
888
+ className: "h-10 min-w-40 flex-1 rounded-md border border-border bg-card px-3 text-sm text-foreground outline-none placeholder:text-muted-foreground focus:ring-2 focus:ring-ring"
889
+ }
890
+ );
891
+ const submitBtn = /* @__PURE__ */ jsx14(Button, { type: "submit", children: ctaLabel });
892
+ if (variant === "inline") {
893
+ return /* @__PURE__ */ jsx14("section", { className: cn("w-full px-4 py-8 sm:px-6", className), ...props, children: /* @__PURE__ */ jsxs13("div", { className: "mx-auto flex max-w-xl flex-wrap items-center gap-3", children: [
894
+ heading && /* @__PURE__ */ jsx14("span", { className: "text-sm font-medium text-foreground", children: heading }),
895
+ ctaLabel && /* @__PURE__ */ jsxs13(
896
+ "form",
897
+ {
898
+ onSubmit: handleSubmit,
899
+ className: "flex min-w-0 flex-1 flex-wrap gap-3",
900
+ children: [
901
+ inputField,
902
+ submitBtn
903
+ ]
904
+ }
905
+ )
906
+ ] }) });
907
+ }
908
+ if (variant === "split") {
909
+ return /* @__PURE__ */ jsx14(
910
+ "section",
911
+ {
912
+ className: cn("w-full px-4 py-12 sm:px-6 lg:px-8", className),
913
+ ...props,
914
+ children: /* @__PURE__ */ jsxs13("div", { className: "mx-auto flex max-w-4xl flex-col items-start gap-6 sm:flex-row sm:items-center sm:justify-between", children: [
915
+ /* @__PURE__ */ jsxs13("div", { className: "max-w-md", children: [
916
+ heading && /* @__PURE__ */ jsx14("h2", { className: "text-xl font-bold text-foreground sm:text-2xl", children: heading }),
917
+ description && /* @__PURE__ */ jsx14("p", { className: "mt-2 text-sm text-muted-foreground", children: description })
918
+ ] }),
919
+ ctaLabel && /* @__PURE__ */ jsxs13(
920
+ "form",
921
+ {
922
+ onSubmit: handleSubmit,
923
+ className: "flex w-full flex-wrap gap-3 sm:w-auto",
924
+ children: [
925
+ inputField,
926
+ submitBtn
927
+ ]
928
+ }
929
+ )
930
+ ] })
931
+ }
932
+ );
933
+ }
934
+ return /* @__PURE__ */ jsx14(
935
+ "section",
936
+ {
937
+ className: cn("w-full px-4 py-16 sm:px-6 lg:px-8", className),
938
+ ...props,
939
+ children: /* @__PURE__ */ jsxs13("div", { className: "mx-auto max-w-md text-center", children: [
940
+ heading && /* @__PURE__ */ jsx14("h2", { className: "text-2xl font-bold text-foreground sm:text-3xl", children: heading }),
941
+ description && /* @__PURE__ */ jsx14("p", { className: "mt-3 text-sm text-muted-foreground", children: description }),
942
+ ctaLabel && /* @__PURE__ */ jsxs13("form", { onSubmit: handleSubmit, className: "mt-6 flex flex-wrap gap-3", children: [
943
+ inputField,
944
+ submitBtn
945
+ ] })
946
+ ] })
947
+ }
948
+ );
949
+ }
950
+
951
+ // src/marketing/contact-section.tsx
952
+ import * as React3 from "react";
953
+ import { jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
954
+ function ContactForm({
955
+ submitLabel,
956
+ onSubmitForm
957
+ }) {
958
+ const id = React3.useId();
959
+ const [form, setForm] = React3.useState({
960
+ name: "",
961
+ email: "",
962
+ message: ""
963
+ });
964
+ const handleSubmit = (e) => {
965
+ e.preventDefault();
966
+ onSubmitForm?.(form);
967
+ };
968
+ const inputClass = "h-10 w-full rounded-md border border-border bg-card px-3 text-sm text-foreground outline-none placeholder:text-muted-foreground focus:ring-2 focus:ring-ring";
969
+ return /* @__PURE__ */ jsxs14("form", { onSubmit: handleSubmit, className: "flex flex-col gap-4", children: [
970
+ /* @__PURE__ */ jsxs14("div", { children: [
971
+ /* @__PURE__ */ jsx15(
972
+ "label",
973
+ {
974
+ htmlFor: `${id}-name`,
975
+ className: "mb-1.5 block text-sm font-medium text-foreground",
976
+ children: "Name"
977
+ }
978
+ ),
979
+ /* @__PURE__ */ jsx15(
980
+ "input",
981
+ {
982
+ id: `${id}-name`,
983
+ type: "text",
984
+ value: form.name,
985
+ onChange: (e) => setForm((f) => ({ ...f, name: e.target.value })),
986
+ className: inputClass
987
+ }
988
+ )
989
+ ] }),
990
+ /* @__PURE__ */ jsxs14("div", { children: [
991
+ /* @__PURE__ */ jsx15(
992
+ "label",
993
+ {
994
+ htmlFor: `${id}-email`,
995
+ className: "mb-1.5 block text-sm font-medium text-foreground",
996
+ children: "Email"
997
+ }
998
+ ),
999
+ /* @__PURE__ */ jsx15(
1000
+ "input",
1001
+ {
1002
+ id: `${id}-email`,
1003
+ type: "email",
1004
+ value: form.email,
1005
+ onChange: (e) => setForm((f) => ({ ...f, email: e.target.value })),
1006
+ className: inputClass
1007
+ }
1008
+ )
1009
+ ] }),
1010
+ /* @__PURE__ */ jsxs14("div", { children: [
1011
+ /* @__PURE__ */ jsx15(
1012
+ "label",
1013
+ {
1014
+ htmlFor: `${id}-message`,
1015
+ className: "mb-1.5 block text-sm font-medium text-foreground",
1016
+ children: "Message"
1017
+ }
1018
+ ),
1019
+ /* @__PURE__ */ jsx15(
1020
+ "textarea",
1021
+ {
1022
+ id: `${id}-message`,
1023
+ value: form.message,
1024
+ onChange: (e) => setForm((f) => ({ ...f, message: e.target.value })),
1025
+ rows: 4,
1026
+ className: "w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground outline-none placeholder:text-muted-foreground focus:ring-2 focus:ring-ring"
1027
+ }
1028
+ )
1029
+ ] }),
1030
+ /* @__PURE__ */ jsx15(Button, { type: "submit", children: submitLabel })
1031
+ ] });
1032
+ }
1033
+ function ContactSection({
1034
+ variant = "centered",
1035
+ heading,
1036
+ description,
1037
+ submitLabel,
1038
+ onSubmitForm,
1039
+ mapSlot,
1040
+ className,
1041
+ ...props
1042
+ }) {
1043
+ if (onSubmitForm && !submitLabel)
1044
+ throw new Error(
1045
+ "ContactSection: `onSubmitForm` needs `submitLabel`; with no label the form is not drawn"
1046
+ );
1047
+ const headerBlock = (heading || description) && /* @__PURE__ */ jsxs14("div", { children: [
1048
+ heading && /* @__PURE__ */ jsx15("h2", { className: "text-2xl font-bold text-foreground sm:text-3xl", children: heading }),
1049
+ description && /* @__PURE__ */ jsx15("p", { className: "mt-3 text-sm text-muted-foreground", children: description })
1050
+ ] });
1051
+ const formCard = submitLabel && /* @__PURE__ */ jsx15("div", { className: "rounded-xl border border-border bg-card p-6 shadow-sm", children: /* @__PURE__ */ jsx15(ContactForm, { submitLabel, onSubmitForm }) });
1052
+ if (variant === "splitForm") {
1053
+ return /* @__PURE__ */ jsx15(
1054
+ "section",
1055
+ {
1056
+ className: cn("w-full px-4 py-16 sm:px-6 lg:px-8", className),
1057
+ ...props,
1058
+ children: /* @__PURE__ */ jsxs14("div", { className: "mx-auto grid max-w-5xl gap-12 lg:grid-cols-2", children: [
1059
+ /* @__PURE__ */ jsx15("div", { className: "flex flex-col justify-center", children: headerBlock }),
1060
+ formCard
1061
+ ] })
1062
+ }
1063
+ );
1064
+ }
1065
+ if (variant === "withMap") {
1066
+ return /* @__PURE__ */ jsx15(
1067
+ "section",
1068
+ {
1069
+ className: cn("w-full px-4 py-16 sm:px-6 lg:px-8", className),
1070
+ ...props,
1071
+ children: /* @__PURE__ */ jsxs14("div", { className: "mx-auto max-w-5xl", children: [
1072
+ headerBlock,
1073
+ /* @__PURE__ */ jsxs14("div", { className: "mt-8 grid gap-8 lg:grid-cols-2", children: [
1074
+ formCard,
1075
+ mapSlot && /* @__PURE__ */ jsx15("div", { className: "overflow-hidden rounded-xl border border-border bg-muted", children: mapSlot })
1076
+ ] })
1077
+ ] })
1078
+ }
1079
+ );
1080
+ }
1081
+ return /* @__PURE__ */ jsx15(
1082
+ "section",
1083
+ {
1084
+ className: cn("w-full px-4 py-16 sm:px-6 lg:px-8", className),
1085
+ ...props,
1086
+ children: /* @__PURE__ */ jsxs14("div", { className: "mx-auto max-w-lg text-center", children: [
1087
+ headerBlock,
1088
+ /* @__PURE__ */ jsx15("div", { className: "mt-8 text-left", children: formCard })
1089
+ ] })
1090
+ }
1091
+ );
1092
+ }
1093
+
1094
+ // src/marketing/footer.tsx
1095
+ import * as React4 from "react";
1096
+ import { jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
1097
+ function MarketingFooter({
1098
+ variant = "simple",
1099
+ brandName,
1100
+ logo,
1101
+ linkGroups = [],
1102
+ links = [],
1103
+ socialLinks = [],
1104
+ legalText,
1105
+ newsletterHeading,
1106
+ newsletterDescription,
1107
+ onSubscribe,
1108
+ className,
1109
+ ...props
1110
+ }) {
1111
+ const [email, setEmail] = React4.useState("");
1112
+ const newsletterTitle = React4.useId();
1113
+ if (onSubscribe && !newsletterHeading)
1114
+ throw new Error(
1115
+ "MarketingFooter: `onSubscribe` needs `newsletterHeading`; with no heading the newsletter is not drawn"
1116
+ );
1117
+ const logoBox = logo && /* @__PURE__ */ jsx16("span", { className: "text-primary-text block size-6 shrink-0 [&>*]:size-full", children: logo });
1118
+ const socialRow = socialLinks.length > 0 && /* @__PURE__ */ jsx16("div", { className: "flex gap-4", children: socialLinks.map((s) => /* @__PURE__ */ jsx16(
1119
+ "a",
1120
+ {
1121
+ href: s.href,
1122
+ "aria-label": s.label,
1123
+ className: "text-muted-foreground transition-colors hover:text-foreground",
1124
+ children: s.icon
1125
+ },
1126
+ s.label
1127
+ )) });
1128
+ if (variant === "simple") {
1129
+ return /* @__PURE__ */ jsx16(
1130
+ "footer",
1131
+ {
1132
+ className: cn("w-full border-t border-border bg-card py-8", className),
1133
+ ...props,
1134
+ children: /* @__PURE__ */ jsxs15(Container, { className: "flex flex-col items-center gap-4 sm:flex-row sm:justify-between", children: [
1135
+ /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-2", children: [
1136
+ logoBox,
1137
+ /* @__PURE__ */ jsx16("span", { className: "text-sm font-semibold text-foreground", children: brandName })
1138
+ ] }),
1139
+ links.length > 0 && /* @__PURE__ */ jsx16("nav", { className: "flex flex-wrap gap-4", children: links.map((l) => /* @__PURE__ */ jsx16(
1140
+ "a",
1141
+ {
1142
+ href: l.href,
1143
+ className: "text-sm text-muted-foreground hover:text-foreground",
1144
+ children: l.label
1145
+ },
1146
+ l.label
1147
+ )) }),
1148
+ /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-4", children: [
1149
+ socialRow,
1150
+ legalText && /* @__PURE__ */ jsx16("span", { className: "text-xs text-muted-foreground", children: legalText })
1151
+ ] })
1152
+ ] })
1153
+ }
1154
+ );
1155
+ }
1156
+ if (variant === "withNewsletter") {
1157
+ return /* @__PURE__ */ jsx16(
1158
+ "footer",
1159
+ {
1160
+ className: cn("w-full border-t border-border bg-card py-12", className),
1161
+ ...props,
1162
+ children: /* @__PURE__ */ jsxs15(Container, { children: [
1163
+ /* @__PURE__ */ jsxs15("div", { className: "grid gap-8 lg:grid-cols-[1fr_320px]", children: [
1164
+ /* @__PURE__ */ jsx16("div", { className: "grid grid-cols-2 gap-8 sm:grid-cols-3", children: linkGroups.map((group) => /* @__PURE__ */ jsxs15("div", { children: [
1165
+ /* @__PURE__ */ jsx16("h3", { className: "mb-3 text-sm font-semibold text-foreground", children: group.title }),
1166
+ /* @__PURE__ */ jsx16("ul", { className: "space-y-2", children: group.links.map((l) => /* @__PURE__ */ jsx16("li", { children: /* @__PURE__ */ jsx16(
1167
+ "a",
1168
+ {
1169
+ href: l.href,
1170
+ className: "text-sm text-muted-foreground hover:text-foreground",
1171
+ children: l.label
1172
+ }
1173
+ ) }, l.label)) })
1174
+ ] }, group.title)) }),
1175
+ newsletterHeading && /* @__PURE__ */ jsxs15("div", { children: [
1176
+ /* @__PURE__ */ jsx16(
1177
+ "h3",
1178
+ {
1179
+ id: newsletterTitle,
1180
+ className: "mb-2 text-sm font-semibold text-foreground",
1181
+ children: newsletterHeading
1182
+ }
1183
+ ),
1184
+ newsletterDescription && /* @__PURE__ */ jsx16("p", { className: "mb-3 text-xs text-muted-foreground", children: newsletterDescription }),
1185
+ /* @__PURE__ */ jsxs15(
1186
+ "form",
1187
+ {
1188
+ onSubmit: (e) => {
1189
+ e.preventDefault();
1190
+ if (email.trim()) onSubscribe?.(email.trim());
1191
+ },
1192
+ className: "flex gap-2",
1193
+ children: [
1194
+ /* @__PURE__ */ jsx16(
1195
+ "input",
1196
+ {
1197
+ type: "email",
1198
+ value: email,
1199
+ onChange: (e) => setEmail(e.target.value),
1200
+ "aria-labelledby": newsletterTitle,
1201
+ className: "h-9 min-w-0 flex-1 rounded-md border border-border bg-card px-3 text-sm text-foreground outline-none focus:ring-2 focus:ring-ring"
1202
+ }
1203
+ ),
1204
+ /* @__PURE__ */ jsx16(
1205
+ "button",
1206
+ {
1207
+ type: "submit",
1208
+ className: "bg-primary-solid h-9 rounded-md px-4 text-sm font-medium text-primary-foreground hover:opacity-90",
1209
+ children: "Subscribe"
1210
+ }
1211
+ )
1212
+ ]
1213
+ }
1214
+ )
1215
+ ] })
1216
+ ] }),
1217
+ /* @__PURE__ */ jsxs15("div", { className: "mt-8 flex flex-col items-center justify-between gap-4 border-t border-border pt-6 sm:flex-row", children: [
1218
+ /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-2", children: [
1219
+ logoBox,
1220
+ legalText && /* @__PURE__ */ jsx16("span", { className: "text-sm text-muted-foreground", children: legalText })
1221
+ ] }),
1222
+ socialRow
1223
+ ] })
1224
+ ] })
1225
+ }
1226
+ );
1227
+ }
1228
+ return /* @__PURE__ */ jsx16(
1229
+ "footer",
1230
+ {
1231
+ className: cn("w-full border-t border-border bg-card py-12", className),
1232
+ ...props,
1233
+ children: /* @__PURE__ */ jsxs15(Container, { children: [
1234
+ /* @__PURE__ */ jsxs15("div", { className: "grid gap-8 sm:grid-cols-2 lg:grid-cols-4", children: [
1235
+ /* @__PURE__ */ jsxs15("div", { children: [
1236
+ /* @__PURE__ */ jsxs15("div", { className: "mb-4 flex items-center gap-2", children: [
1237
+ logoBox,
1238
+ /* @__PURE__ */ jsx16("span", { className: "text-sm font-semibold text-foreground", children: brandName })
1239
+ ] }),
1240
+ socialRow
1241
+ ] }),
1242
+ linkGroups.map((group) => /* @__PURE__ */ jsxs15("div", { children: [
1243
+ /* @__PURE__ */ jsx16("h3", { className: "mb-3 text-sm font-semibold text-foreground", children: group.title }),
1244
+ /* @__PURE__ */ jsx16("ul", { className: "space-y-2", children: group.links.map((l) => /* @__PURE__ */ jsx16("li", { children: /* @__PURE__ */ jsx16(
1245
+ "a",
1246
+ {
1247
+ href: l.href,
1248
+ className: "text-sm text-muted-foreground hover:text-foreground",
1249
+ children: l.label
1250
+ }
1251
+ ) }, l.label)) })
1252
+ ] }, group.title))
1253
+ ] }),
1254
+ legalText && /* @__PURE__ */ jsx16("div", { className: "mt-8 border-t border-border pt-6 text-center text-xs text-muted-foreground", children: legalText })
1255
+ ] })
1256
+ }
1257
+ );
1258
+ }
1259
+
1260
+ // src/marketing/testimonial-section.tsx
1261
+ import * as React5 from "react";
1262
+ import { jsx as jsx17, jsxs as jsxs16 } from "react/jsx-runtime";
1263
+ function TestimonialCard({
1264
+ testimonial,
1265
+ className
1266
+ }) {
1267
+ return /* @__PURE__ */ jsxs16(
1268
+ "div",
1269
+ {
1270
+ className: cn(
1271
+ "flex flex-col gap-4 rounded-xl border border-border bg-card p-6 shadow-sm",
1272
+ className
1273
+ ),
1274
+ children: [
1275
+ /* @__PURE__ */ jsxs16("p", { className: "flex-1 text-sm italic text-foreground", children: [
1276
+ "\u201C",
1277
+ testimonial.quote,
1278
+ "\u201D"
1279
+ ] }),
1280
+ /* @__PURE__ */ jsxs16("div", { className: "flex items-center gap-3", children: [
1281
+ testimonial.avatar ? /* @__PURE__ */ jsx17(
1282
+ "img",
1283
+ {
1284
+ src: testimonial.avatar,
1285
+ alt: testimonial.name,
1286
+ className: "h-10 w-10 rounded-full object-cover"
1287
+ }
1288
+ ) : /* @__PURE__ */ jsx17("div", { className: "flex h-10 w-10 items-center justify-center rounded-full bg-muted text-sm font-medium text-muted-foreground", children: testimonial.name.charAt(0) }),
1289
+ /* @__PURE__ */ jsxs16("div", { children: [
1290
+ /* @__PURE__ */ jsx17("p", { className: "text-sm font-semibold text-foreground", children: testimonial.name }),
1291
+ (testimonial.role || testimonial.company) && /* @__PURE__ */ jsx17("p", { className: "text-xs text-muted-foreground", children: [testimonial.role, testimonial.company].filter(Boolean).join(" \xB7 ") })
1292
+ ] })
1293
+ ] })
1294
+ ]
1295
+ }
1296
+ );
1297
+ }
1298
+ function GridLayout({
1299
+ testimonials,
1300
+ columns = 3
1301
+ }) {
1302
+ const gridCols = columns === 2 ? "sm:grid-cols-2" : "sm:grid-cols-2 lg:grid-cols-3";
1303
+ return /* @__PURE__ */ jsx17("div", { className: cn("grid grid-cols-1 gap-6", gridCols), children: testimonials.map((t) => /* @__PURE__ */ jsx17(TestimonialCard, { testimonial: t }, t.id)) });
1304
+ }
1305
+ function CarouselLayout({
1306
+ testimonials,
1307
+ autoPlayInterval = 5e3
1308
+ }) {
1309
+ const [activeIndex, setActiveIndex] = React5.useState(0);
1310
+ React5.useEffect(() => {
1311
+ if (autoPlayInterval <= 0 || testimonials.length <= 1) return;
1312
+ const timer = setInterval(() => {
1313
+ setActiveIndex((i) => (i + 1) % testimonials.length);
1314
+ }, autoPlayInterval);
1315
+ return () => clearInterval(timer);
1316
+ }, [autoPlayInterval, testimonials.length]);
1317
+ const current = testimonials[activeIndex];
1318
+ if (!current) return null;
1319
+ return /* @__PURE__ */ jsxs16("div", { className: "flex flex-col items-center gap-6", children: [
1320
+ /* @__PURE__ */ jsx17("div", { className: "w-full max-w-2xl", children: /* @__PURE__ */ jsx17(TestimonialCard, { testimonial: current, className: "text-center" }) }),
1321
+ testimonials.length > 1 && /* @__PURE__ */ jsx17("div", { className: "flex gap-2", children: testimonials.map((t, i) => /* @__PURE__ */ jsx17(
1322
+ "button",
1323
+ {
1324
+ type: "button",
1325
+ onClick: () => setActiveIndex(i),
1326
+ "aria-label": t.name,
1327
+ className: cn(
1328
+ "h-2 rounded-full transition-all",
1329
+ i === activeIndex ? "w-6 bg-primary" : "w-2 bg-muted-foreground/30"
1330
+ )
1331
+ },
1332
+ i
1333
+ )) })
1334
+ ] });
1335
+ }
1336
+ function SingleQuoteLayout({ testimonial }) {
1337
+ return /* @__PURE__ */ jsxs16("div", { className: "mx-auto flex max-w-3xl flex-col items-center gap-6 text-center", children: [
1338
+ testimonial.logo && /* @__PURE__ */ jsx17(
1339
+ "img",
1340
+ {
1341
+ src: testimonial.logo,
1342
+ alt: testimonial.company ?? "",
1343
+ className: "h-8 opacity-60"
1344
+ }
1345
+ ),
1346
+ /* @__PURE__ */ jsxs16("blockquote", { className: "text-xl font-medium italic text-foreground sm:text-2xl", children: [
1347
+ "\u201C",
1348
+ testimonial.quote,
1349
+ "\u201D"
1350
+ ] }),
1351
+ /* @__PURE__ */ jsxs16("div", { className: "flex items-center gap-3", children: [
1352
+ testimonial.avatar ? /* @__PURE__ */ jsx17(
1353
+ "img",
1354
+ {
1355
+ src: testimonial.avatar,
1356
+ alt: testimonial.name,
1357
+ className: "h-12 w-12 rounded-full object-cover"
1358
+ }
1359
+ ) : /* @__PURE__ */ jsx17("div", { className: "flex h-12 w-12 items-center justify-center rounded-full bg-muted text-sm font-medium text-muted-foreground", children: testimonial.name.charAt(0) }),
1360
+ /* @__PURE__ */ jsxs16("div", { className: "text-left", children: [
1361
+ /* @__PURE__ */ jsx17("p", { className: "text-sm font-semibold text-foreground", children: testimonial.name }),
1362
+ (testimonial.role || testimonial.company) && /* @__PURE__ */ jsx17("p", { className: "text-xs text-muted-foreground", children: [testimonial.role, testimonial.company].filter(Boolean).join(" \xB7 ") })
1363
+ ] })
1364
+ ] })
1365
+ ] });
1366
+ }
1367
+ function WallOfLoveLayout({ testimonials }) {
1368
+ return /* @__PURE__ */ jsx17("div", { className: "columns-1 gap-6 sm:columns-2 lg:columns-3", children: testimonials.map((t) => /* @__PURE__ */ jsx17("div", { className: "mb-6 break-inside-avoid", children: /* @__PURE__ */ jsx17(TestimonialCard, { testimonial: t }) }, t.id)) });
1369
+ }
1370
+ function TestimonialSection({
1371
+ variant = "grid",
1372
+ testimonials,
1373
+ heading,
1374
+ subheading,
1375
+ columns = 3,
1376
+ autoPlayInterval = 5e3,
1377
+ className,
1378
+ ...props
1379
+ }) {
1380
+ return /* @__PURE__ */ jsxs16(
1381
+ "section",
1382
+ {
1383
+ className: cn("w-full px-4 py-12 sm:px-6 lg:px-8", className),
1384
+ ...props,
1385
+ children: [
1386
+ (heading || subheading) && /* @__PURE__ */ jsxs16("div", { className: "mx-auto mb-10 max-w-2xl text-center", children: [
1387
+ heading && /* @__PURE__ */ jsx17("h2", { className: "text-2xl font-bold text-foreground sm:text-3xl", children: heading }),
1388
+ subheading && /* @__PURE__ */ jsx17("p", { className: "mt-3 text-sm text-muted-foreground sm:text-base", children: subheading })
1389
+ ] }),
1390
+ variant === "grid" && /* @__PURE__ */ jsx17(GridLayout, { testimonials, columns }),
1391
+ variant === "carousel" && /* @__PURE__ */ jsx17(
1392
+ CarouselLayout,
1393
+ {
1394
+ testimonials,
1395
+ autoPlayInterval
1396
+ }
1397
+ ),
1398
+ variant === "singleQuote" && testimonials[0] && /* @__PURE__ */ jsx17(SingleQuoteLayout, { testimonial: testimonials[0] }),
1399
+ variant === "wallOfLove" && /* @__PURE__ */ jsx17(WallOfLoveLayout, { testimonials })
1400
+ ]
1401
+ }
1402
+ );
1403
+ }
1404
+ export {
1405
+ BlogGrid,
1406
+ CTABanner,
1407
+ ComparisonTable,
1408
+ ContactSection,
1409
+ FeatureCard,
1410
+ LogoCloud,
1411
+ MarketingCta,
1412
+ MarketingFAQItem,
1413
+ MarketingFooter,
1414
+ MarketingHeader,
1415
+ MarketingHero,
1416
+ HowItWorksStep as MarketingHowItWorksStep,
1417
+ PricingTier as MarketingPricingTier,
1418
+ NewsletterSection,
1419
+ TeamSection,
1420
+ TestimonialSection,
1421
+ UseCaseCard
1422
+ };