nextworks 0.2.0-alpha.2 → 0.2.0-alpha.20

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.
Files changed (145) hide show
  1. package/README.md +280 -228
  2. package/dist/cli_manifests/blocks_manifest.json +193 -194
  3. package/dist/commands/blocks.d.ts +3 -0
  4. package/dist/commands/blocks.d.ts.map +1 -1
  5. package/dist/commands/blocks.js +128 -19
  6. package/dist/commands/blocks.js.map +1 -1
  7. package/dist/commands/doctor.d.ts +136 -0
  8. package/dist/commands/doctor.d.ts.map +1 -0
  9. package/dist/commands/doctor.js +696 -0
  10. package/dist/commands/doctor.js.map +1 -0
  11. package/dist/commands/remove-blocks.d.ts +4 -1
  12. package/dist/commands/remove-blocks.d.ts.map +1 -1
  13. package/dist/commands/remove-blocks.js +24 -6
  14. package/dist/commands/remove-blocks.js.map +1 -1
  15. package/dist/index.js +125 -4
  16. package/dist/index.js.map +1 -1
  17. package/dist/kits/blocks/.nextworks/docs/BLOCKS_QUICKSTART.md +104 -194
  18. package/dist/kits/blocks/.nextworks/docs/BLOCKS_README.md +115 -75
  19. package/dist/kits/blocks/.nextworks/docs/THEME_GUIDE.md +269 -251
  20. package/dist/kits/blocks/app/templates/digitalagency/PresetThemeVars.tsx +80 -80
  21. package/dist/kits/blocks/app/templates/digitalagency/README.md +42 -36
  22. package/dist/kits/blocks/app/templates/digitalagency/components/About.tsx +71 -99
  23. package/dist/kits/blocks/app/templates/digitalagency/components/CTA.tsx +40 -74
  24. package/dist/kits/blocks/app/templates/digitalagency/components/Contact.tsx +123 -227
  25. package/dist/kits/blocks/app/templates/digitalagency/components/Footer.tsx +89 -89
  26. package/dist/kits/blocks/app/templates/digitalagency/components/Hero.tsx +83 -90
  27. package/dist/kits/blocks/app/templates/digitalagency/components/Navbar.tsx +121 -168
  28. package/dist/kits/blocks/app/templates/digitalagency/components/NetworkPattern.tsx +288 -297
  29. package/dist/kits/blocks/app/templates/digitalagency/components/Portfolio.tsx +157 -157
  30. package/dist/kits/blocks/app/templates/digitalagency/components/Pricing.tsx +114 -114
  31. package/dist/kits/blocks/app/templates/digitalagency/components/Process.tsx +59 -59
  32. package/dist/kits/blocks/app/templates/digitalagency/components/Services.tsx +55 -55
  33. package/dist/kits/blocks/app/templates/digitalagency/components/Team.tsx +28 -28
  34. package/dist/kits/blocks/app/templates/digitalagency/components/Testimonials.tsx +65 -65
  35. package/dist/kits/blocks/app/templates/digitalagency/page.tsx +38 -38
  36. package/dist/kits/blocks/app/templates/gallery/PresetThemeVars.tsx +130 -85
  37. package/dist/kits/blocks/app/templates/gallery/page.tsx +357 -303
  38. package/dist/kits/blocks/app/templates/productlaunch/PresetThemeVars.tsx +75 -74
  39. package/dist/kits/blocks/app/templates/productlaunch/README.md +62 -55
  40. package/dist/kits/blocks/app/templates/productlaunch/components/About.tsx +84 -178
  41. package/dist/kits/blocks/app/templates/productlaunch/components/CTA.tsx +50 -93
  42. package/dist/kits/blocks/app/templates/productlaunch/components/Contact.tsx +231 -231
  43. package/dist/kits/blocks/app/templates/productlaunch/components/FAQ.tsx +86 -93
  44. package/dist/kits/blocks/app/templates/productlaunch/components/Features.tsx +83 -84
  45. package/dist/kits/blocks/app/templates/productlaunch/components/Footer.tsx +132 -132
  46. package/dist/kits/blocks/app/templates/productlaunch/components/Hero.tsx +88 -89
  47. package/dist/kits/blocks/app/templates/productlaunch/components/Navbar.tsx +116 -162
  48. package/dist/kits/blocks/app/templates/productlaunch/components/Pricing.tsx +106 -106
  49. package/dist/kits/blocks/app/templates/productlaunch/components/ProcessTimeline.tsx +110 -110
  50. package/dist/kits/blocks/app/templates/productlaunch/components/ServicesGrid.tsx +68 -68
  51. package/dist/kits/blocks/app/templates/productlaunch/components/Team.tsx +104 -104
  52. package/dist/kits/blocks/app/templates/productlaunch/components/Testimonials.tsx +90 -89
  53. package/dist/kits/blocks/app/templates/productlaunch/components/TrustBadges.tsx +76 -76
  54. package/dist/kits/blocks/app/templates/productlaunch/page.tsx +43 -43
  55. package/dist/kits/blocks/app/templates/saasdashboard/PresetThemeVars.tsx +80 -80
  56. package/dist/kits/blocks/app/templates/saasdashboard/README.md +44 -38
  57. package/dist/kits/blocks/app/templates/saasdashboard/components/Contact.tsx +129 -176
  58. package/dist/kits/blocks/app/templates/saasdashboard/components/Dashboard.tsx +293 -293
  59. package/dist/kits/blocks/app/templates/saasdashboard/components/FAQ.tsx +55 -55
  60. package/dist/kits/blocks/app/templates/saasdashboard/components/Features.tsx +90 -91
  61. package/dist/kits/blocks/app/templates/saasdashboard/components/Footer.tsx +77 -77
  62. package/dist/kits/blocks/app/templates/saasdashboard/components/Hero.tsx +104 -105
  63. package/dist/kits/blocks/app/templates/saasdashboard/components/Hero_mask.tsx +126 -127
  64. package/dist/kits/blocks/app/templates/saasdashboard/components/Navbar.tsx +117 -159
  65. package/dist/kits/blocks/app/templates/saasdashboard/components/Pricing.tsx +90 -90
  66. package/dist/kits/blocks/app/templates/saasdashboard/components/SmoothScroll.tsx +96 -97
  67. package/dist/kits/blocks/app/templates/saasdashboard/components/Testimonials.tsx +72 -72
  68. package/dist/kits/blocks/app/templates/saasdashboard/components/TrustBadges.tsx +53 -53
  69. package/dist/kits/blocks/app/templates/saasdashboard/page.tsx +39 -39
  70. package/dist/kits/blocks/components/enhanced-theme-provider.tsx +195 -183
  71. package/dist/kits/blocks/components/providers/BlocksAppProviders.tsx +27 -27
  72. package/dist/kits/blocks/components/sections/About.tsx +291 -291
  73. package/dist/kits/blocks/components/sections/CTA.tsx +257 -258
  74. package/dist/kits/blocks/components/sections/CommandShowcase.tsx +517 -0
  75. package/dist/kits/blocks/components/sections/Contact.tsx +267 -267
  76. package/dist/kits/blocks/components/sections/FAQ.tsx +214 -225
  77. package/dist/kits/blocks/components/sections/FeaturedProjectShowcase.tsx +687 -0
  78. package/dist/kits/blocks/components/sections/Features.tsx +268 -270
  79. package/dist/kits/blocks/components/sections/Footer.tsx +302 -302
  80. package/dist/kits/blocks/components/sections/HeroMotion.tsx +308 -308
  81. package/dist/kits/blocks/components/sections/HeroOverlay.tsx +358 -358
  82. package/dist/kits/blocks/components/sections/HeroProductDemo.tsx +246 -0
  83. package/dist/kits/blocks/components/sections/HeroSplit.tsx +352 -352
  84. package/dist/kits/blocks/components/sections/HeroWithVideo.tsx +495 -0
  85. package/dist/kits/blocks/components/sections/Navbar.tsx +352 -353
  86. package/dist/kits/blocks/components/sections/PortfolioSimple.tsx +549 -550
  87. package/dist/kits/blocks/components/sections/Pricing.tsx +264 -264
  88. package/dist/kits/blocks/components/sections/ProcessTimeline.tsx +325 -325
  89. package/dist/kits/blocks/components/sections/ProjectDeepDive.tsx +805 -0
  90. package/dist/kits/blocks/components/sections/SelectedWorkRail.tsx +485 -0
  91. package/dist/kits/blocks/components/sections/ServicesGrid.tsx +210 -210
  92. package/dist/kits/blocks/components/sections/Team.tsx +309 -309
  93. package/dist/kits/blocks/components/sections/Testimonials.tsx +158 -158
  94. package/dist/kits/blocks/components/sections/TrustBadges.tsx +162 -162
  95. package/dist/kits/blocks/components/sections/product-demo/ApprovalInboxPanel.tsx +128 -0
  96. package/dist/kits/blocks/components/sections/product-demo/DemoStage.tsx +519 -0
  97. package/dist/kits/blocks/components/sections/product-demo/DemoWindow.tsx +140 -0
  98. package/dist/kits/blocks/components/sections/product-demo/KnowledgePanel.tsx +130 -0
  99. package/dist/kits/blocks/components/sections/product-demo/RunConsolePanel.tsx +430 -0
  100. package/dist/kits/blocks/components/sections/product-demo/TaskListPanel.tsx +302 -0
  101. package/dist/kits/blocks/components/sections/product-demo/WorkflowStudioPanel.tsx +744 -0
  102. package/dist/kits/blocks/components/sections/product-demo/types.ts +262 -0
  103. package/dist/kits/blocks/components/theme-provider.tsx +1 -34
  104. package/dist/kits/blocks/components/ui/alert-dialog.tsx +134 -134
  105. package/dist/kits/blocks/components/ui/brand-node.tsx +121 -121
  106. package/dist/kits/blocks/components/ui/button.tsx +122 -122
  107. package/dist/kits/blocks/components/ui/card.tsx +95 -95
  108. package/dist/kits/blocks/components/ui/checkbox.tsx +30 -30
  109. package/dist/kits/blocks/components/ui/cta-button.tsx +125 -125
  110. package/dist/kits/blocks/components/ui/dropdown-menu.tsx +201 -201
  111. package/dist/kits/blocks/components/ui/feature-card.tsx +91 -91
  112. package/dist/kits/blocks/components/ui/input.tsx +27 -27
  113. package/dist/kits/blocks/components/ui/label.tsx +29 -29
  114. package/dist/kits/blocks/components/ui/pricing-card.tsx +120 -120
  115. package/dist/kits/blocks/components/ui/select.tsx +25 -25
  116. package/dist/kits/blocks/components/ui/skeleton.tsx +13 -13
  117. package/dist/kits/blocks/components/ui/table.tsx +98 -98
  118. package/dist/kits/blocks/components/ui/testimonial-card.tsx +108 -108
  119. package/dist/kits/blocks/components/ui/textarea.tsx +26 -26
  120. package/dist/kits/blocks/components/ui/theme-selector.tsx +243 -247
  121. package/dist/kits/blocks/components/ui/theme-toggle.tsx +74 -74
  122. package/dist/kits/blocks/components/ui/toaster.tsx +7 -7
  123. package/dist/kits/blocks/lib/themes.ts +400 -400
  124. package/dist/kits/blocks/lib/utils.ts +6 -9
  125. package/dist/kits/blocks/package-deps.json +40 -28
  126. package/dist/kits/blocks/tsconfig.json +11 -0
  127. package/dist/kits/blocks/tsconfig.tsbuildinfo +1 -0
  128. package/dist/utils/file-operations.d.ts +3 -1
  129. package/dist/utils/file-operations.d.ts.map +1 -1
  130. package/dist/utils/file-operations.js +84 -12
  131. package/dist/utils/file-operations.js.map +1 -1
  132. package/dist/utils/installation-tracker.d.ts +2 -2
  133. package/dist/utils/installation-tracker.d.ts.map +1 -1
  134. package/dist/utils/installation-tracker.js +11 -11
  135. package/dist/utils/installation-tracker.js.map +1 -1
  136. package/dist/utils/package-manager.d.ts +6 -0
  137. package/dist/utils/package-manager.d.ts.map +1 -0
  138. package/dist/utils/package-manager.js +58 -0
  139. package/dist/utils/package-manager.js.map +1 -0
  140. package/dist/utils/yarn-pnp.d.ts +6 -0
  141. package/dist/utils/yarn-pnp.d.ts.map +1 -0
  142. package/dist/utils/yarn-pnp.js +39 -0
  143. package/dist/utils/yarn-pnp.js.map +1 -0
  144. package/package.json +5 -2
  145. package/dist/kits/blocks/notes/THEME_GUIDE.md +0 -29
@@ -0,0 +1,485 @@
1
+ import React from "react";
2
+ import Image from "next/image";
3
+ import Link from "next/link";
4
+ import { cn } from "@/lib/utils";
5
+
6
+ export type SelectedWorkRailLayout = "grid" | "rail";
7
+
8
+ export interface SelectedWorkRailImage {
9
+ src: string;
10
+ alt?: string;
11
+ className?: string;
12
+ }
13
+
14
+ export interface SelectedWorkRailVideo {
15
+ src: string;
16
+ poster?: string;
17
+ title?: string;
18
+ ariaLabel?: string;
19
+ className?: string;
20
+ autoPlay?: boolean;
21
+ muted?: boolean;
22
+ loop?: boolean;
23
+ playsInline?: boolean;
24
+ controls?: boolean;
25
+ preload?: "none" | "metadata" | "auto";
26
+ }
27
+
28
+ export interface SelectedWorkRailTag {
29
+ label: string;
30
+ className?: string;
31
+ }
32
+
33
+ export interface SelectedWorkRailItem {
34
+ id?: string;
35
+ title: React.ReactNode;
36
+ label?: React.ReactNode;
37
+ description?: React.ReactNode;
38
+ href?: string;
39
+ ariaLabel?: string;
40
+ image?: string | SelectedWorkRailImage;
41
+ video?: string | SelectedWorkRailVideo;
42
+ poster?: string;
43
+ tags?: Array<string | SelectedWorkRailTag>;
44
+ visual?: React.ReactNode;
45
+ className?: string;
46
+ }
47
+
48
+ export interface SelectedWorkRailClassNames {
49
+ section?: string;
50
+ backgroundGlow?: string;
51
+ container?: string;
52
+ header?: string;
53
+ eyebrow?: string;
54
+ title?: string;
55
+ description?: string;
56
+ rail?: string;
57
+ item?: string;
58
+ featuredItem?: string;
59
+ itemInner?: string;
60
+ itemLink?: string;
61
+ media?: string;
62
+ image?: string;
63
+ video?: string;
64
+ placeholder?: string;
65
+ content?: string;
66
+ label?: string;
67
+ itemTitle?: string;
68
+ itemDescription?: string;
69
+ tags?: string;
70
+ tag?: string;
71
+ }
72
+
73
+ export interface SelectedWorkRailProps {
74
+ id?: string;
75
+ className?: string;
76
+ eyebrow?: React.ReactNode;
77
+ title?: React.ReactNode;
78
+ description?: React.ReactNode;
79
+ items?: SelectedWorkRailItem[];
80
+ layout?: SelectedWorkRailLayout;
81
+ featuredIndex?: number;
82
+ ariaLabel?: string;
83
+ titleId?: string;
84
+ enableMotion?: boolean;
85
+ classNames?: SelectedWorkRailClassNames;
86
+ section?: { className?: string };
87
+ container?: { className?: string };
88
+ header?: { className?: string };
89
+ railSlot?: { className?: string };
90
+ }
91
+
92
+ const defaultItems: SelectedWorkRailItem[] = [
93
+ {
94
+ title: "Product launch",
95
+ label: "Launch page",
96
+ description: "A focused launch story with space for benefits, proof, and a clear next step.",
97
+ tags: ["Hero", "Proof", "CTA"],
98
+ },
99
+ {
100
+ title: "Dashboard preview",
101
+ label: "Product UI",
102
+ description: "A visual product tile for showing interface direction or feature depth.",
103
+ tags: ["Dashboard", "Preview"],
104
+ },
105
+ {
106
+ title: "Marketing site",
107
+ label: "Website",
108
+ description: "A services or company page with room for work, process, and credibility.",
109
+ tags: ["Services", "Portfolio"],
110
+ },
111
+ {
112
+ title: "Workflow demo",
113
+ label: "Product story",
114
+ description: "A short walkthrough-style item for explaining a process or automation flow.",
115
+ tags: ["Workflow", "Demo"],
116
+ },
117
+ {
118
+ title: "Template gallery",
119
+ label: "Showcase",
120
+ description: "A compact gallery item for grouping related examples or visual variants.",
121
+ tags: ["Gallery", "Sections"],
122
+ },
123
+ {
124
+ title: "Developer tool",
125
+ label: "Project",
126
+ description: "A technical project tile for showing setup flow, docs, or implementation notes.",
127
+ tags: ["Tooling", "Docs"],
128
+ },
129
+ ];
130
+
131
+ function normalizeMediaImage(
132
+ image: SelectedWorkRailItem["image"],
133
+ ): SelectedWorkRailImage | undefined {
134
+ if (!image) {
135
+ return undefined;
136
+ }
137
+
138
+ return typeof image === "string" ? { src: image } : image;
139
+ }
140
+
141
+ function normalizeMediaVideo(
142
+ video: SelectedWorkRailItem["video"],
143
+ poster?: string,
144
+ ): SelectedWorkRailVideo | undefined {
145
+ if (!video) {
146
+ return undefined;
147
+ }
148
+
149
+ return typeof video === "string" ? { src: video, poster } : { ...video, poster: video.poster ?? poster };
150
+ }
151
+
152
+ function normalizeTag(tag: string | SelectedWorkRailTag): SelectedWorkRailTag {
153
+ return typeof tag === "string" ? { label: tag } : tag;
154
+ }
155
+
156
+ function renderVisual(
157
+ item: SelectedWorkRailItem,
158
+ classNames?: SelectedWorkRailClassNames,
159
+ ) {
160
+ if (item.visual) {
161
+ return item.visual;
162
+ }
163
+
164
+ const video = normalizeMediaVideo(item.video, item.poster);
165
+
166
+ if (video?.src) {
167
+ return (
168
+ <video
169
+ src={video.src}
170
+ poster={video.poster}
171
+ title={video.title ?? video.ariaLabel ?? "Selected work preview video"}
172
+ aria-label={video.ariaLabel ?? video.title ?? "Selected work preview video"}
173
+ className={cn("size-full object-cover", classNames?.video, video.className)}
174
+ autoPlay={video.autoPlay ?? true}
175
+ muted={video.muted ?? true}
176
+ loop={video.loop ?? true}
177
+ playsInline={video.playsInline ?? true}
178
+ controls={video.controls ?? false}
179
+ preload={video.preload ?? "metadata"}
180
+ />
181
+ );
182
+ }
183
+
184
+ const image = normalizeMediaImage(item.image);
185
+
186
+ if (image?.src) {
187
+ return (
188
+ <Image
189
+ src={image.src}
190
+ alt={image.alt ?? (typeof item.title === "string" ? item.title : "Selected work preview")}
191
+ fill
192
+ sizes="(max-width: 768px) 100vw, (max-width: 1280px) 50vw, 33vw"
193
+ className={cn("object-cover", classNames?.image, image.className)}
194
+ unoptimized
195
+ />
196
+ );
197
+ }
198
+
199
+ return (
200
+ <div
201
+ className={cn(
202
+ "flex size-full flex-col justify-between bg-[radial-gradient(circle_at_top,rgba(0,0,0,0.08),rgba(0,0,0,0.03)_32%,rgba(0,0,0,0)_72%)] p-5 sm:p-6 dark:bg-[radial-gradient(circle_at_top,rgba(255,255,255,0.12),rgba(255,255,255,0.04)_32%,rgba(0,0,0,0.96)_72%)]",
203
+
204
+ classNames?.placeholder,
205
+ )}
206
+ aria-hidden="true"
207
+ >
208
+ <div className="h-10 w-28 rounded-full border border-border bg-muted/60" />
209
+
210
+ <div className="space-y-2">
211
+ <div className="h-3 w-24 rounded-full bg-foreground/10" />
212
+ <div className="h-3 w-40 rounded-full bg-foreground/7" />
213
+ <div className="h-20 rounded-[1.25rem] border border-border bg-muted/60" />
214
+
215
+ </div>
216
+ </div>
217
+ );
218
+ }
219
+
220
+ function renderItemBody(
221
+ item: SelectedWorkRailItem,
222
+ isFeatured: boolean,
223
+ classNames?: SelectedWorkRailClassNames,
224
+ ) {
225
+ return (
226
+ <>
227
+ <div
228
+ className={cn(
229
+ "relative aspect-[16/11] overflow-hidden rounded-[1.4rem] border border-border bg-muted",
230
+
231
+ isFeatured && "aspect-[16/9]",
232
+ classNames?.media,
233
+ )}
234
+ >
235
+ {renderVisual(item, classNames)}
236
+ </div>
237
+
238
+ <div className={cn("mt-5 flex flex-col gap-3", classNames?.content)}>
239
+ {item.label ? (
240
+ <p
241
+ className={cn(
242
+ "text-[0.7rem] font-medium uppercase tracking-[0.24em] text-muted-foreground",
243
+
244
+ classNames?.label,
245
+ )}
246
+ >
247
+ {item.label}
248
+ </p>
249
+ ) : null}
250
+
251
+ <div>
252
+ <h3
253
+ className={cn(
254
+ "text-xl font-medium tracking-[-0.035em] text-foreground sm:text-2xl",
255
+
256
+ classNames?.itemTitle,
257
+ )}
258
+ >
259
+ {item.title}
260
+ </h3>
261
+
262
+ {item.description ? (
263
+ <p
264
+ className={cn(
265
+ "mt-2 max-w-2xl text-sm leading-6 text-muted-foreground sm:text-[0.95rem]",
266
+
267
+ classNames?.itemDescription,
268
+ )}
269
+ >
270
+ {item.description}
271
+ </p>
272
+ ) : null}
273
+ </div>
274
+
275
+ {item.tags?.length ? (
276
+ <div className={cn("flex flex-wrap gap-2", classNames?.tags)}>
277
+ {item.tags.map((tag) => {
278
+ const normalizedTag = normalizeTag(tag);
279
+
280
+ return (
281
+ <span
282
+ key={normalizedTag.label}
283
+ className={cn(
284
+ "rounded-full border border-border bg-muted/60 px-3 py-1 text-xs text-muted-foreground",
285
+
286
+ classNames?.tag,
287
+ normalizedTag.className,
288
+ )}
289
+ >
290
+ {normalizedTag.label}
291
+ </span>
292
+ );
293
+ })}
294
+ </div>
295
+ ) : null}
296
+ </div>
297
+ </>
298
+ );
299
+ }
300
+
301
+ export function SelectedWorkRail({
302
+ id,
303
+ className,
304
+ eyebrow = "Selected work",
305
+ title = "A visual rail for selected work and project highlights.",
306
+ description =
307
+ "Use this section to show a few polished examples with minimal copy and stronger visuals.",
308
+ items = defaultItems,
309
+ layout = "grid",
310
+ featuredIndex = 0,
311
+ ariaLabel = "Selected work showcase section",
312
+ titleId,
313
+ enableMotion = true,
314
+ classNames,
315
+ section,
316
+ container,
317
+ header,
318
+ railSlot,
319
+ }: SelectedWorkRailProps) {
320
+ const motionClasses = enableMotion
321
+ ? "transition-all duration-300 hover:-translate-y-1"
322
+ : "transition-none hover:!translate-y-0";
323
+
324
+ return (
325
+ <section
326
+ id={id}
327
+ className={cn(
328
+ "relative overflow-hidden bg-background px-4 py-20 text-foreground sm:px-6 lg:px-8 lg:py-24",
329
+
330
+ section?.className,
331
+ classNames?.section,
332
+ className,
333
+ )}
334
+ aria-label={ariaLabel}
335
+ aria-labelledby={titleId}
336
+ >
337
+ <div
338
+ className={cn(
339
+ "pointer-events-none absolute left-1/2 top-28 h-[36rem] w-[52rem] -translate-x-1/2 rounded-full bg-[radial-gradient(circle_at_center,rgba(0,0,0,0.08),rgba(0,0,0,0.03)_38%,transparent_74%)] blur-3xl dark:bg-[radial-gradient(circle_at_center,rgba(255,255,255,0.11),rgba(255,255,255,0.04)_38%,transparent_74%)]",
340
+
341
+ classNames?.backgroundGlow,
342
+ )}
343
+ aria-hidden="true"
344
+ />
345
+
346
+ <div
347
+ className={cn(
348
+ "relative z-10 mx-auto max-w-7xl",
349
+ container?.className,
350
+ classNames?.container,
351
+ )}
352
+ >
353
+ <div
354
+ className={cn(
355
+ "mx-auto max-w-3xl text-center",
356
+ header?.className,
357
+ classNames?.header,
358
+ )}
359
+ >
360
+ {eyebrow ? (
361
+ <p
362
+ className={cn(
363
+ "text-xs font-medium uppercase tracking-[0.28em] text-muted-foreground",
364
+
365
+ classNames?.eyebrow,
366
+ )}
367
+ >
368
+ {eyebrow}
369
+ </p>
370
+ ) : null}
371
+
372
+ <h2
373
+ id={titleId}
374
+ className={cn(
375
+ "mt-5 text-balance text-4xl font-semibold tracking-[-0.055em] text-foreground sm:text-5xl lg:text-6xl",
376
+
377
+ classNames?.title,
378
+ )}
379
+ >
380
+ {title}
381
+ </h2>
382
+
383
+ {description ? (
384
+ <p
385
+ className={cn(
386
+ "mx-auto mt-5 max-w-2xl text-pretty text-base leading-7 text-muted-foreground sm:text-lg",
387
+
388
+ classNames?.description,
389
+ )}
390
+ >
391
+ {description}
392
+ </p>
393
+ ) : null}
394
+ </div>
395
+
396
+ {layout === "rail" ? (
397
+ <div
398
+ className={cn(
399
+ "mt-12 flex snap-x snap-mandatory gap-5 overflow-x-auto pb-2",
400
+ railSlot?.className,
401
+ classNames?.rail,
402
+ )}
403
+ >
404
+ {items.map((item, index) => {
405
+ const isFeatured = index === featuredIndex;
406
+ const itemBody = renderItemBody(item, isFeatured, classNames);
407
+
408
+ return item.href ? (
409
+ <Link
410
+ key={item.id ?? `${String(item.title)}-${index}`}
411
+ href={item.href}
412
+ aria-label={item.ariaLabel ?? (typeof item.title === "string" ? item.title : "Selected work item")}
413
+ className={cn(
414
+ "group min-w-[18rem] max-w-[26rem] flex-1 snap-start rounded-[2rem] border border-border bg-card/80 p-4 shadow-[0_20px_80px_rgba(0,0,0,0.12)] backdrop-blur dark:shadow-[0_20px_80px_rgba(0,0,0,0.46)] sm:min-w-[22rem] sm:p-5",
415
+
416
+ isFeatured && "min-w-[22rem] max-w-[32rem]",
417
+ motionClasses,
418
+ classNames?.item,
419
+ classNames?.itemLink,
420
+ isFeatured && classNames?.featuredItem,
421
+ item.className,
422
+ )}
423
+ >
424
+ <div className={cn("flex h-full flex-col", classNames?.itemInner)}>{itemBody}</div>
425
+ </Link>
426
+ ) : (
427
+ <div
428
+ key={item.id ?? `${String(item.title)}-${index}`}
429
+ className={cn(
430
+ "min-w-[18rem] max-w-[26rem] flex-1 snap-start rounded-[2rem] border border-border bg-card/80 p-4 shadow-[0_20px_80px_rgba(0,0,0,0.12)] backdrop-blur dark:shadow-[0_20px_80px_rgba(0,0,0,0.46)] sm:min-w-[22rem] sm:p-5",
431
+
432
+ isFeatured && "min-w-[22rem] max-w-[32rem]",
433
+ motionClasses,
434
+ classNames?.item,
435
+ isFeatured && classNames?.featuredItem,
436
+ item.className,
437
+ )}
438
+ >
439
+ <div className={cn("flex h-full flex-col", classNames?.itemInner)}>{itemBody}</div>
440
+ </div>
441
+ );
442
+ })}
443
+ </div>
444
+ ) : (
445
+ <div
446
+ className={cn(
447
+ "mt-12 grid gap-5 md:grid-cols-2 xl:grid-cols-3",
448
+ railSlot?.className,
449
+ classNames?.rail,
450
+ )}
451
+ >
452
+ {items.map((item, index) => {
453
+ const isFeatured = index === featuredIndex;
454
+ const itemBody = renderItemBody(item, isFeatured, classNames);
455
+ const sharedClassName = cn(
456
+ "rounded-[2rem] border border-border bg-card/80 p-4 shadow-[0_20px_80px_rgba(0,0,0,0.12)] backdrop-blur dark:shadow-[0_20px_80px_rgba(0,0,0,0.46)] sm:p-5",
457
+
458
+ isFeatured && "md:col-span-2 xl:col-span-2",
459
+ motionClasses,
460
+ classNames?.item,
461
+ isFeatured && classNames?.featuredItem,
462
+ item.className,
463
+ );
464
+
465
+ return item.href ? (
466
+ <Link
467
+ key={item.id ?? `${String(item.title)}-${index}`}
468
+ href={item.href}
469
+ aria-label={item.ariaLabel ?? (typeof item.title === "string" ? item.title : "Selected work item")}
470
+ className={cn(sharedClassName, classNames?.itemLink)}
471
+ >
472
+ <div className={cn("flex h-full flex-col", classNames?.itemInner)}>{itemBody}</div>
473
+ </Link>
474
+ ) : (
475
+ <div key={item.id ?? `${String(item.title)}-${index}`} className={sharedClassName}>
476
+ <div className={cn("flex h-full flex-col", classNames?.itemInner)}>{itemBody}</div>
477
+ </div>
478
+ );
479
+ })}
480
+ </div>
481
+ )}
482
+ </div>
483
+ </section>
484
+ );
485
+ }