nextworks 0.2.0-alpha.16 → 0.2.0-alpha.18
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/README.md +0 -4
- package/dist/cli_manifests/blocks_manifest.json +10 -2
- package/dist/kits/blocks/.nextworks/docs/BLOCKS_QUICKSTART.md +3 -0
- package/dist/kits/blocks/.nextworks/docs/BLOCKS_README.md +11 -1
- package/dist/kits/blocks/components/sections/CommandShowcase.tsx +517 -0
- package/dist/kits/blocks/components/sections/FeaturedProjectShowcase.tsx +687 -0
- package/dist/kits/blocks/components/sections/HeroWithVideo.tsx +495 -0
- package/dist/kits/blocks/components/sections/ProjectDeepDive.tsx +805 -0
- package/dist/kits/blocks/components/sections/SelectedWorkRail.tsx +485 -0
- package/dist/kits/blocks/package-deps.json +3 -3
- package/dist/kits/blocks/tsconfig.json +0 -2
- package/dist/kits/blocks/tsconfig.tsbuildinfo +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,687 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import Image from "next/image";
|
|
3
|
+
import Link from "next/link";
|
|
4
|
+
import { Button } from "@/components/ui/button";
|
|
5
|
+
import { cn } from "@/lib/utils";
|
|
6
|
+
|
|
7
|
+
type ButtonVariant =
|
|
8
|
+
| "default"
|
|
9
|
+
| "destructive"
|
|
10
|
+
| "outline"
|
|
11
|
+
| "secondary"
|
|
12
|
+
| "ghost"
|
|
13
|
+
| "link";
|
|
14
|
+
|
|
15
|
+
type ButtonSize = "default" | "sm" | "lg" | "icon";
|
|
16
|
+
|
|
17
|
+
export interface FeaturedProjectShowcaseCta {
|
|
18
|
+
label?: string;
|
|
19
|
+
href?: string;
|
|
20
|
+
ariaLabel?: string;
|
|
21
|
+
variant?: ButtonVariant;
|
|
22
|
+
size?: ButtonSize;
|
|
23
|
+
className?: string;
|
|
24
|
+
unstyled?: boolean;
|
|
25
|
+
style?: React.CSSProperties;
|
|
26
|
+
target?: string;
|
|
27
|
+
rel?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface FeaturedProjectShowcaseTag {
|
|
31
|
+
label: string;
|
|
32
|
+
className?: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface FeaturedProjectShowcaseFeature {
|
|
36
|
+
title?: string;
|
|
37
|
+
description: React.ReactNode;
|
|
38
|
+
className?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface FeaturedProjectShowcaseMetaItem {
|
|
42
|
+
label: string;
|
|
43
|
+
value: React.ReactNode;
|
|
44
|
+
className?: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface FeaturedProjectShowcaseBreakdownItem {
|
|
48
|
+
label: string;
|
|
49
|
+
description: React.ReactNode;
|
|
50
|
+
className?: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface FeaturedProjectShowcaseMediaConfig {
|
|
54
|
+
type?: "image" | "video" | "terminal" | "custom";
|
|
55
|
+
src?: string;
|
|
56
|
+
alt?: string;
|
|
57
|
+
title?: string;
|
|
58
|
+
ariaLabel?: string;
|
|
59
|
+
poster?: string;
|
|
60
|
+
autoPlay?: boolean;
|
|
61
|
+
muted?: boolean;
|
|
62
|
+
loop?: boolean;
|
|
63
|
+
playsInline?: boolean;
|
|
64
|
+
controls?: boolean;
|
|
65
|
+
preload?: "none" | "metadata" | "auto";
|
|
66
|
+
commands?: string[];
|
|
67
|
+
output?: string[];
|
|
68
|
+
caption?: React.ReactNode;
|
|
69
|
+
className?: string;
|
|
70
|
+
content?: React.ReactNode;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface FeaturedProjectShowcaseClassNames {
|
|
74
|
+
section?: string;
|
|
75
|
+
backgroundGlow?: string;
|
|
76
|
+
container?: string;
|
|
77
|
+
contentGrid?: string;
|
|
78
|
+
content?: string;
|
|
79
|
+
eyebrow?: string;
|
|
80
|
+
title?: string;
|
|
81
|
+
description?: string;
|
|
82
|
+
tags?: string;
|
|
83
|
+
tag?: string;
|
|
84
|
+
features?: string;
|
|
85
|
+
feature?: string;
|
|
86
|
+
featureMarker?: string;
|
|
87
|
+
featureTitle?: string;
|
|
88
|
+
featureDescription?: string;
|
|
89
|
+
breakdown?: string;
|
|
90
|
+
breakdownItem?: string;
|
|
91
|
+
breakdownLabel?: string;
|
|
92
|
+
breakdownDescription?: string;
|
|
93
|
+
meta?: string;
|
|
94
|
+
metaItem?: string;
|
|
95
|
+
metaLabel?: string;
|
|
96
|
+
metaValue?: string;
|
|
97
|
+
buttons?: string;
|
|
98
|
+
primaryCta?: string;
|
|
99
|
+
secondaryCta?: string;
|
|
100
|
+
mediaOuter?: string;
|
|
101
|
+
mediaFrame?: string;
|
|
102
|
+
mediaChrome?: string;
|
|
103
|
+
mediaContent?: string;
|
|
104
|
+
mediaCaption?: string;
|
|
105
|
+
terminal?: string;
|
|
106
|
+
terminalLine?: string;
|
|
107
|
+
terminalOutput?: string;
|
|
108
|
+
image?: string;
|
|
109
|
+
video?: string;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export interface FeaturedProjectShowcaseProps {
|
|
113
|
+
id?: string;
|
|
114
|
+
className?: string;
|
|
115
|
+
eyebrow?: React.ReactNode;
|
|
116
|
+
title?: React.ReactNode;
|
|
117
|
+
description?: React.ReactNode;
|
|
118
|
+
tags?: Array<string | FeaturedProjectShowcaseTag>;
|
|
119
|
+
features?: Array<string | FeaturedProjectShowcaseFeature>;
|
|
120
|
+
breakdown?: FeaturedProjectShowcaseBreakdownItem[];
|
|
121
|
+
meta?: FeaturedProjectShowcaseMetaItem[];
|
|
122
|
+
primaryCta?: FeaturedProjectShowcaseCta;
|
|
123
|
+
secondaryCta?: FeaturedProjectShowcaseCta;
|
|
124
|
+
media?: React.ReactNode | FeaturedProjectShowcaseMediaConfig | false;
|
|
125
|
+
mediaId?: string;
|
|
126
|
+
detailsId?: string;
|
|
127
|
+
ariaLabel?: string;
|
|
128
|
+
titleId?: string;
|
|
129
|
+
enableMotion?: boolean;
|
|
130
|
+
classNames?: FeaturedProjectShowcaseClassNames;
|
|
131
|
+
section?: { className?: string };
|
|
132
|
+
container?: { className?: string };
|
|
133
|
+
content?: { className?: string };
|
|
134
|
+
mediaSlot?: { className?: string };
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const defaultTags = [
|
|
138
|
+
"Product",
|
|
139
|
+
"Frontend",
|
|
140
|
+
"Launch",
|
|
141
|
+
"Case study",
|
|
142
|
+
"Design system",
|
|
143
|
+
"Workflow",
|
|
144
|
+
];
|
|
145
|
+
|
|
146
|
+
const defaultFeatures: FeaturedProjectShowcaseFeature[] = [
|
|
147
|
+
{ description: "Clear project positioning with room for technical details." },
|
|
148
|
+
{ description: "Flexible media area for screenshots, video, terminal output, or custom content." },
|
|
149
|
+
{ description: "Optional tags, metadata, and calls to action." },
|
|
150
|
+
{ description: "Works for products, tools, portfolios, and case studies." },
|
|
151
|
+
];
|
|
152
|
+
|
|
153
|
+
const defaultBreakdown: FeaturedProjectShowcaseBreakdownItem[] = [
|
|
154
|
+
{
|
|
155
|
+
label: "What it is",
|
|
156
|
+
description: "A focused project highlight with enough context to understand the work.",
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
label: "What it shows",
|
|
160
|
+
description: "Key features, implementation notes, supporting details, and proof points.",
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
label: "How to use it",
|
|
164
|
+
description: "Wrap it with project-specific copy, links, media, and metadata.",
|
|
165
|
+
},
|
|
166
|
+
];
|
|
167
|
+
|
|
168
|
+
const defaultMeta: FeaturedProjectShowcaseMetaItem[] = [
|
|
169
|
+
{ label: "Type", value: "Featured project" },
|
|
170
|
+
{ label: "Focus", value: "Product story" },
|
|
171
|
+
{ label: "Status", value: "Demo content" },
|
|
172
|
+
];
|
|
173
|
+
|
|
174
|
+
const defaultMedia: FeaturedProjectShowcaseMediaConfig = {
|
|
175
|
+
type: "terminal",
|
|
176
|
+
title: "Project preview",
|
|
177
|
+
ariaLabel: "Terminal-style preview showing example project notes",
|
|
178
|
+
commands: [
|
|
179
|
+
"open project-brief.md",
|
|
180
|
+
"review design-system-notes.md",
|
|
181
|
+
"ship polished-preview",
|
|
182
|
+
],
|
|
183
|
+
output: [
|
|
184
|
+
"✔ summary, tags, and project metadata ready",
|
|
185
|
+
"✔ feature bullets and implementation notes added",
|
|
186
|
+
"✔ media area available for screenshots or video",
|
|
187
|
+
],
|
|
188
|
+
caption: "Use the media area for a screenshot, video, terminal preview, or custom visual.",
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
function isMediaConfig(
|
|
192
|
+
media: FeaturedProjectShowcaseProps["media"],
|
|
193
|
+
): media is FeaturedProjectShowcaseMediaConfig {
|
|
194
|
+
return Boolean(
|
|
195
|
+
media &&
|
|
196
|
+
typeof media === "object" &&
|
|
197
|
+
!React.isValidElement(media) &&
|
|
198
|
+
("type" in media || "src" in media || "content" in media),
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function normalizeTag(tag: string | FeaturedProjectShowcaseTag) {
|
|
203
|
+
return typeof tag === "string" ? { label: tag } : tag;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function normalizeFeature(feature: string | FeaturedProjectShowcaseFeature) {
|
|
207
|
+
return typeof feature === "string" ? { description: feature } : feature;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function renderCta(cta: FeaturedProjectShowcaseCta | undefined) {
|
|
211
|
+
if (!cta?.label) {
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return (
|
|
216
|
+
<Button
|
|
217
|
+
asChild
|
|
218
|
+
variant={cta.variant}
|
|
219
|
+
size={cta.size}
|
|
220
|
+
className={cta.className}
|
|
221
|
+
unstyled={cta.unstyled}
|
|
222
|
+
style={cta.style}
|
|
223
|
+
>
|
|
224
|
+
<Link
|
|
225
|
+
href={cta.href || "#"}
|
|
226
|
+
aria-label={cta.ariaLabel ?? cta.label}
|
|
227
|
+
target={cta.target}
|
|
228
|
+
rel={cta.rel ?? (cta.target === "_blank" ? "noreferrer" : undefined)}
|
|
229
|
+
>
|
|
230
|
+
{cta.label}
|
|
231
|
+
</Link>
|
|
232
|
+
</Button>
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function renderMediaContent(
|
|
237
|
+
media: FeaturedProjectShowcaseMediaConfig,
|
|
238
|
+
classNames?: FeaturedProjectShowcaseClassNames,
|
|
239
|
+
) {
|
|
240
|
+
if (media.content) {
|
|
241
|
+
return media.content;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (media.type === "image" && media.src) {
|
|
245
|
+
return (
|
|
246
|
+
<div
|
|
247
|
+
className={cn(
|
|
248
|
+
"relative min-h-[24rem] overflow-hidden",
|
|
249
|
+
media.className,
|
|
250
|
+
classNames?.image,
|
|
251
|
+
)}
|
|
252
|
+
>
|
|
253
|
+
<Image
|
|
254
|
+
src={media.src}
|
|
255
|
+
alt={media.alt ?? media.title ?? "Featured project preview"}
|
|
256
|
+
fill
|
|
257
|
+
sizes="(max-width: 1024px) 100vw, 50vw"
|
|
258
|
+
className="object-cover"
|
|
259
|
+
unoptimized
|
|
260
|
+
/>
|
|
261
|
+
</div>
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (media.type === "video" && media.src) {
|
|
266
|
+
return (
|
|
267
|
+
<video
|
|
268
|
+
src={media.src}
|
|
269
|
+
poster={media.poster}
|
|
270
|
+
title={media.title ?? media.ariaLabel ?? "Featured project video"}
|
|
271
|
+
aria-label={media.ariaLabel ?? media.title ?? "Featured project video"}
|
|
272
|
+
className={cn("size-full object-cover", media.className, classNames?.video)}
|
|
273
|
+
autoPlay={media.autoPlay}
|
|
274
|
+
muted={media.muted ?? media.autoPlay ?? false}
|
|
275
|
+
loop={media.loop}
|
|
276
|
+
playsInline={media.playsInline ?? true}
|
|
277
|
+
controls={media.controls ?? true}
|
|
278
|
+
preload={media.preload ?? "metadata"}
|
|
279
|
+
/>
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return (
|
|
284
|
+
<div
|
|
285
|
+
className={cn(
|
|
286
|
+
"flex min-h-[24rem] flex-col justify-between bg-muted p-5 font-mono text-xs text-muted-foreground sm:p-6",
|
|
287
|
+
|
|
288
|
+
media.className,
|
|
289
|
+
classNames?.terminal,
|
|
290
|
+
)}
|
|
291
|
+
role="img"
|
|
292
|
+
aria-label={media.ariaLabel ?? media.title ?? "Terminal preview"}
|
|
293
|
+
>
|
|
294
|
+
<div className="space-y-3">
|
|
295
|
+
{(media.commands ?? defaultMedia.commands ?? []).map((command) => (
|
|
296
|
+
<p key={command} className={cn("break-all", classNames?.terminalLine)}>
|
|
297
|
+
<span className="mr-2 text-muted-foreground/60" aria-hidden="true">
|
|
298
|
+
|
|
299
|
+
$
|
|
300
|
+
</span>
|
|
301
|
+
<span>{command}</span>
|
|
302
|
+
</p>
|
|
303
|
+
))}
|
|
304
|
+
</div>
|
|
305
|
+
|
|
306
|
+
<div className="mt-8 space-y-2 border-t border-border pt-5">
|
|
307
|
+
|
|
308
|
+
{(media.output ?? defaultMedia.output ?? []).map((line) => (
|
|
309
|
+
<p key={line} className={cn("text-muted-foreground", classNames?.terminalOutput)}>
|
|
310
|
+
|
|
311
|
+
{line}
|
|
312
|
+
</p>
|
|
313
|
+
))}
|
|
314
|
+
</div>
|
|
315
|
+
</div>
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function renderMedia(
|
|
320
|
+
media: FeaturedProjectShowcaseProps["media"],
|
|
321
|
+
classNames?: FeaturedProjectShowcaseClassNames,
|
|
322
|
+
) {
|
|
323
|
+
if (media === false) {
|
|
324
|
+
return null;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (media !== undefined && !isMediaConfig(media)) {
|
|
328
|
+
return media;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const resolvedMedia = isMediaConfig(media) ? media : defaultMedia;
|
|
332
|
+
|
|
333
|
+
return (
|
|
334
|
+
<>
|
|
335
|
+
{renderMediaContent(resolvedMedia, classNames)}
|
|
336
|
+
{resolvedMedia.caption ? (
|
|
337
|
+
<p className={cn("px-5 py-4 text-sm text-muted-foreground", classNames?.mediaCaption)}>
|
|
338
|
+
|
|
339
|
+
{resolvedMedia.caption}
|
|
340
|
+
</p>
|
|
341
|
+
) : null}
|
|
342
|
+
</>
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
export function FeaturedProjectShowcase({
|
|
347
|
+
id,
|
|
348
|
+
className,
|
|
349
|
+
eyebrow = "Featured project",
|
|
350
|
+
title = "Featured project",
|
|
351
|
+
description =
|
|
352
|
+
"Highlight one project with a short summary, key features, supporting details, and an optional visual preview.",
|
|
353
|
+
tags = defaultTags,
|
|
354
|
+
features = defaultFeatures,
|
|
355
|
+
breakdown = defaultBreakdown,
|
|
356
|
+
meta = defaultMeta,
|
|
357
|
+
primaryCta,
|
|
358
|
+
secondaryCta,
|
|
359
|
+
media,
|
|
360
|
+
mediaId,
|
|
361
|
+
detailsId,
|
|
362
|
+
ariaLabel = "Featured project showcase section",
|
|
363
|
+
titleId,
|
|
364
|
+
enableMotion = true,
|
|
365
|
+
classNames,
|
|
366
|
+
section,
|
|
367
|
+
container,
|
|
368
|
+
content,
|
|
369
|
+
mediaSlot,
|
|
370
|
+
}: FeaturedProjectShowcaseProps) {
|
|
371
|
+
const motionClasses = enableMotion
|
|
372
|
+
? "transition-all duration-200 hover:-translate-y-0.5"
|
|
373
|
+
: "transition-none hover:!translate-y-0";
|
|
374
|
+
|
|
375
|
+
const resolvedMediaId = mediaId ?? (id ? `${id}-media` : "featured-project-media");
|
|
376
|
+
const resolvedDetailsId = detailsId ?? (id ? `${id}-details` : "featured-project-details");
|
|
377
|
+
|
|
378
|
+
const resolvedPrimaryCta: FeaturedProjectShowcaseCta = {
|
|
379
|
+
label: "View project",
|
|
380
|
+
href: `#${resolvedMediaId}`,
|
|
381
|
+
variant: "default",
|
|
382
|
+
size: "lg",
|
|
383
|
+
unstyled: true,
|
|
384
|
+
...(primaryCta ?? {}),
|
|
385
|
+
className: cn(
|
|
386
|
+
"h-11 rounded-full bg-primary px-6 text-sm font-medium text-primary-foreground shadow-lg hover:bg-primary/90",
|
|
387
|
+
|
|
388
|
+
motionClasses,
|
|
389
|
+
classNames?.primaryCta,
|
|
390
|
+
primaryCta?.className,
|
|
391
|
+
),
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
const resolvedSecondaryCta: FeaturedProjectShowcaseCta = {
|
|
395
|
+
label: "Read notes",
|
|
396
|
+
href: `#${resolvedDetailsId}`,
|
|
397
|
+
variant: "outline",
|
|
398
|
+
size: "lg",
|
|
399
|
+
unstyled: true,
|
|
400
|
+
...(secondaryCta ?? {}),
|
|
401
|
+
className: cn(
|
|
402
|
+
"h-11 rounded-full border border-border bg-background/80 px-6 text-sm font-medium text-foreground hover:bg-muted",
|
|
403
|
+
|
|
404
|
+
motionClasses,
|
|
405
|
+
classNames?.secondaryCta,
|
|
406
|
+
secondaryCta?.className,
|
|
407
|
+
),
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
const mediaContent = renderMedia(media, classNames);
|
|
411
|
+
|
|
412
|
+
return (
|
|
413
|
+
<section
|
|
414
|
+
id={id}
|
|
415
|
+
className={cn(
|
|
416
|
+
"relative overflow-hidden bg-background px-4 py-20 text-foreground sm:px-6 lg:px-8 lg:py-24",
|
|
417
|
+
|
|
418
|
+
section?.className,
|
|
419
|
+
classNames?.section,
|
|
420
|
+
className,
|
|
421
|
+
)}
|
|
422
|
+
aria-label={ariaLabel}
|
|
423
|
+
aria-labelledby={titleId}
|
|
424
|
+
>
|
|
425
|
+
<div
|
|
426
|
+
className={cn(
|
|
427
|
+
"pointer-events-none absolute right-[-18rem] top-24 h-[34rem] w-[34rem] rounded-full bg-[radial-gradient(circle_at_center,rgba(0,0,0,0.08),rgba(0,0,0,0.03)_36%,transparent_70%)] blur-3xl dark:bg-[radial-gradient(circle_at_center,rgba(255,255,255,0.12),rgba(255,255,255,0.04)_36%,transparent_70%)]",
|
|
428
|
+
|
|
429
|
+
classNames?.backgroundGlow,
|
|
430
|
+
)}
|
|
431
|
+
aria-hidden="true"
|
|
432
|
+
/>
|
|
433
|
+
|
|
434
|
+
<div
|
|
435
|
+
className={cn(
|
|
436
|
+
"relative z-10 mx-auto max-w-7xl",
|
|
437
|
+
container?.className,
|
|
438
|
+
classNames?.container,
|
|
439
|
+
)}
|
|
440
|
+
>
|
|
441
|
+
<div
|
|
442
|
+
className={cn(
|
|
443
|
+
"grid gap-8 lg:grid-cols-[minmax(0,0.92fr)_minmax(26rem,1.08fr)] lg:items-stretch",
|
|
444
|
+
classNames?.contentGrid,
|
|
445
|
+
)}
|
|
446
|
+
>
|
|
447
|
+
<div
|
|
448
|
+
className={cn(
|
|
449
|
+
"flex flex-col rounded-[2rem] border border-border bg-card/80 p-6 shadow-[0_24px_90px_rgba(0,0,0,0.12)] backdrop-blur dark:shadow-[0_24px_90px_rgba(0,0,0,0.55)] sm:p-8 lg:p-10",
|
|
450
|
+
|
|
451
|
+
content?.className,
|
|
452
|
+
classNames?.content,
|
|
453
|
+
)}
|
|
454
|
+
>
|
|
455
|
+
{eyebrow ? (
|
|
456
|
+
<p
|
|
457
|
+
className={cn(
|
|
458
|
+
"text-xs font-medium uppercase tracking-[0.28em] text-muted-foreground",
|
|
459
|
+
|
|
460
|
+
classNames?.eyebrow,
|
|
461
|
+
)}
|
|
462
|
+
>
|
|
463
|
+
{eyebrow}
|
|
464
|
+
</p>
|
|
465
|
+
) : null}
|
|
466
|
+
|
|
467
|
+
<h2
|
|
468
|
+
id={titleId}
|
|
469
|
+
className={cn(
|
|
470
|
+
"mt-5 text-balance text-4xl font-semibold tracking-[-0.055em] text-foreground sm:text-5xl lg:text-6xl",
|
|
471
|
+
|
|
472
|
+
classNames?.title,
|
|
473
|
+
)}
|
|
474
|
+
>
|
|
475
|
+
{title}
|
|
476
|
+
</h2>
|
|
477
|
+
|
|
478
|
+
{description ? (
|
|
479
|
+
<p
|
|
480
|
+
className={cn(
|
|
481
|
+
"mt-5 max-w-2xl text-pretty text-base leading-7 text-muted-foreground sm:text-lg",
|
|
482
|
+
|
|
483
|
+
classNames?.description,
|
|
484
|
+
)}
|
|
485
|
+
>
|
|
486
|
+
{description}
|
|
487
|
+
</p>
|
|
488
|
+
) : null}
|
|
489
|
+
|
|
490
|
+
{tags.length > 0 ? (
|
|
491
|
+
<div className={cn("mt-7 flex flex-wrap gap-2", classNames?.tags)}>
|
|
492
|
+
{tags.map((tag) => {
|
|
493
|
+
const normalizedTag = normalizeTag(tag);
|
|
494
|
+
|
|
495
|
+
return (
|
|
496
|
+
<span
|
|
497
|
+
key={normalizedTag.label}
|
|
498
|
+
className={cn(
|
|
499
|
+
"rounded-full border border-border bg-muted/60 px-3 py-1 text-xs font-medium text-muted-foreground",
|
|
500
|
+
|
|
501
|
+
classNames?.tag,
|
|
502
|
+
normalizedTag.className,
|
|
503
|
+
)}
|
|
504
|
+
>
|
|
505
|
+
{normalizedTag.label}
|
|
506
|
+
</span>
|
|
507
|
+
);
|
|
508
|
+
})}
|
|
509
|
+
</div>
|
|
510
|
+
) : null}
|
|
511
|
+
|
|
512
|
+
{features.length > 0 ? (
|
|
513
|
+
<ul className={cn("mt-8 space-y-3", classNames?.features)}>
|
|
514
|
+
{features.map((feature, index) => {
|
|
515
|
+
const normalizedFeature = normalizeFeature(feature);
|
|
516
|
+
|
|
517
|
+
return (
|
|
518
|
+
<li
|
|
519
|
+
key={`${normalizedFeature.title ?? "feature"}-${index}`}
|
|
520
|
+
className={cn(
|
|
521
|
+
"flex gap-3 text-sm leading-6 text-muted-foreground",
|
|
522
|
+
|
|
523
|
+
classNames?.feature,
|
|
524
|
+
normalizedFeature.className,
|
|
525
|
+
)}
|
|
526
|
+
>
|
|
527
|
+
<span
|
|
528
|
+
className={cn(
|
|
529
|
+
"mt-2 size-1.5 shrink-0 rounded-full bg-foreground/50",
|
|
530
|
+
|
|
531
|
+
classNames?.featureMarker,
|
|
532
|
+
)}
|
|
533
|
+
aria-hidden="true"
|
|
534
|
+
/>
|
|
535
|
+
<span>
|
|
536
|
+
{normalizedFeature.title ? (
|
|
537
|
+
<span
|
|
538
|
+
className={cn(
|
|
539
|
+
"mr-1 font-medium text-foreground",
|
|
540
|
+
|
|
541
|
+
classNames?.featureTitle,
|
|
542
|
+
)}
|
|
543
|
+
>
|
|
544
|
+
{normalizedFeature.title}:
|
|
545
|
+
</span>
|
|
546
|
+
) : null}
|
|
547
|
+
<span className={classNames?.featureDescription}>
|
|
548
|
+
{normalizedFeature.description}
|
|
549
|
+
</span>
|
|
550
|
+
</span>
|
|
551
|
+
</li>
|
|
552
|
+
);
|
|
553
|
+
})}
|
|
554
|
+
</ul>
|
|
555
|
+
) : null}
|
|
556
|
+
|
|
557
|
+
{breakdown.length > 0 ? (
|
|
558
|
+
<div
|
|
559
|
+
id={resolvedDetailsId}
|
|
560
|
+
className={cn(
|
|
561
|
+
"mt-8 grid gap-3 border-t border-border pt-6 sm:grid-cols-3",
|
|
562
|
+
|
|
563
|
+
classNames?.breakdown,
|
|
564
|
+
)}
|
|
565
|
+
>
|
|
566
|
+
{breakdown.map((item) => (
|
|
567
|
+
<div
|
|
568
|
+
key={item.label}
|
|
569
|
+
className={cn(
|
|
570
|
+
"rounded-2xl border border-border bg-muted/40 p-4",
|
|
571
|
+
|
|
572
|
+
classNames?.breakdownItem,
|
|
573
|
+
item.className,
|
|
574
|
+
)}
|
|
575
|
+
>
|
|
576
|
+
<p
|
|
577
|
+
className={cn(
|
|
578
|
+
"text-sm font-medium text-foreground",
|
|
579
|
+
|
|
580
|
+
classNames?.breakdownLabel,
|
|
581
|
+
)}
|
|
582
|
+
>
|
|
583
|
+
{item.label}
|
|
584
|
+
</p>
|
|
585
|
+
<p
|
|
586
|
+
className={cn(
|
|
587
|
+
"mt-2 text-sm leading-6 text-muted-foreground",
|
|
588
|
+
|
|
589
|
+
classNames?.breakdownDescription,
|
|
590
|
+
)}
|
|
591
|
+
>
|
|
592
|
+
{item.description}
|
|
593
|
+
</p>
|
|
594
|
+
</div>
|
|
595
|
+
))}
|
|
596
|
+
</div>
|
|
597
|
+
) : null}
|
|
598
|
+
|
|
599
|
+
<div
|
|
600
|
+
className={cn(
|
|
601
|
+
"mt-8 flex flex-col gap-3 sm:flex-row",
|
|
602
|
+
classNames?.buttons,
|
|
603
|
+
)}
|
|
604
|
+
>
|
|
605
|
+
{renderCta(resolvedPrimaryCta)}
|
|
606
|
+
{renderCta(resolvedSecondaryCta)}
|
|
607
|
+
</div>
|
|
608
|
+
|
|
609
|
+
{meta.length > 0 ? (
|
|
610
|
+
<dl
|
|
611
|
+
className={cn(
|
|
612
|
+
"mt-auto grid gap-4 border-t border-border pt-6 sm:grid-cols-3 lg:mt-10",
|
|
613
|
+
|
|
614
|
+
classNames?.meta,
|
|
615
|
+
)}
|
|
616
|
+
>
|
|
617
|
+
{meta.map((item) => (
|
|
618
|
+
<div
|
|
619
|
+
key={item.label}
|
|
620
|
+
className={cn("min-w-0", classNames?.metaItem, item.className)}
|
|
621
|
+
>
|
|
622
|
+
<dt
|
|
623
|
+
className={cn(
|
|
624
|
+
"text-xs uppercase tracking-[0.2em] text-muted-foreground",
|
|
625
|
+
|
|
626
|
+
classNames?.metaLabel,
|
|
627
|
+
)}
|
|
628
|
+
>
|
|
629
|
+
{item.label}
|
|
630
|
+
</dt>
|
|
631
|
+
<dd
|
|
632
|
+
className={cn(
|
|
633
|
+
"mt-2 truncate text-sm font-medium text-foreground/80",
|
|
634
|
+
|
|
635
|
+
classNames?.metaValue,
|
|
636
|
+
)}
|
|
637
|
+
>
|
|
638
|
+
{item.value}
|
|
639
|
+
</dd>
|
|
640
|
+
</div>
|
|
641
|
+
))}
|
|
642
|
+
</dl>
|
|
643
|
+
) : null}
|
|
644
|
+
</div>
|
|
645
|
+
|
|
646
|
+
{mediaContent ? (
|
|
647
|
+
<div
|
|
648
|
+
id={resolvedMediaId}
|
|
649
|
+
className={cn(
|
|
650
|
+
"relative overflow-hidden rounded-[2rem] border border-border bg-card/80 p-2 shadow-[0_24px_90px_rgba(0,0,0,0.12)] backdrop-blur dark:shadow-[0_24px_90px_rgba(0,0,0,0.62)]",
|
|
651
|
+
|
|
652
|
+
enableMotion && "transition-transform duration-300 hover:-translate-y-1",
|
|
653
|
+
!enableMotion && "transition-none hover:!translate-y-0",
|
|
654
|
+
mediaSlot?.className,
|
|
655
|
+
classNames?.mediaOuter,
|
|
656
|
+
)}
|
|
657
|
+
>
|
|
658
|
+
<div
|
|
659
|
+
className={cn(
|
|
660
|
+
"flex h-10 items-center gap-2 border-b border-border px-4",
|
|
661
|
+
|
|
662
|
+
classNames?.mediaChrome,
|
|
663
|
+
)}
|
|
664
|
+
aria-hidden="true"
|
|
665
|
+
>
|
|
666
|
+
<span className="size-2.5 rounded-full bg-foreground/24" />
|
|
667
|
+
<span className="size-2.5 rounded-full bg-foreground/16" />
|
|
668
|
+
<span className="size-2.5 rounded-full bg-foreground/10" />
|
|
669
|
+
<span className="ml-3 h-2 w-28 rounded-full bg-foreground/8" />
|
|
670
|
+
|
|
671
|
+
</div>
|
|
672
|
+
<div
|
|
673
|
+
className={cn(
|
|
674
|
+
"overflow-hidden rounded-[1.45rem] bg-muted",
|
|
675
|
+
|
|
676
|
+
classNames?.mediaFrame,
|
|
677
|
+
)}
|
|
678
|
+
>
|
|
679
|
+
<div className={classNames?.mediaContent}>{mediaContent}</div>
|
|
680
|
+
</div>
|
|
681
|
+
</div>
|
|
682
|
+
) : null}
|
|
683
|
+
</div>
|
|
684
|
+
</div>
|
|
685
|
+
</section>
|
|
686
|
+
);
|
|
687
|
+
}
|