create-lacspace-app 1.17.1 → 2.1.1
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/BENCHMARKS.md +3 -2
- package/README.md +16 -14
- package/dist/index.js +607 -348
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -17,15 +17,15 @@ var C = {
|
|
|
17
17
|
};
|
|
18
18
|
var c = (k, s) => `${C[k]}${s}${C.reset}`;
|
|
19
19
|
var TEMPLATES = [
|
|
20
|
-
{ key: "personal", label: "Personal portfolio", description: "A sleek personal / developer portfolio with projects and contact.", accent: ["#6366f1", "#a855f7"], siteName: "
|
|
21
|
-
{ key: "business", label: "Business site", description: "A professional company / agency site with services and a CTA.", accent: ["#2563eb", "#06b6d4"], siteName: "
|
|
22
|
-
{ key: "ecommerce", label: "E-commerce storefront", description: "A modern product storefront home with a featured grid.", accent: ["#0d9488", "#84cc16"], siteName: "
|
|
23
|
-
{ key: "saas", label: "SaaS landing", description: "A high-converting SaaS landing page with features and pricing.", accent: ["#7c3aed", "#ec4899"], siteName: "
|
|
24
|
-
{ key: "blog", label: "Blog / magazine", description: "A clean editorial blog home with a featured post and a grid.", accent: ["#f97316", "#ef4444"], siteName: "
|
|
25
|
-
{ key: "docs", label: "Documentation", description: "A docs landing with quick-start and feature cards.", accent: ["#0ea5e9", "#6366f1"], siteName: "
|
|
26
|
-
{ key: "dashboard", label: "Admin dashboard", description: "An app dashboard shell with stat cards and a table.", accent: ["#10b981", "#14b8a6"], siteName: "
|
|
27
|
-
{ key: "restaurant", label: "Restaurant / cafe", description: "A warm restaurant home with menu highlights and reservations.", accent: ["#e11d48", "#f59e0b"], siteName: "
|
|
28
|
-
{ key: "marketplace", label: "Marketplace / commerce", description: "A real storefront wired to the Lacspace commerce packages \u2014 cart, checkout, tax, shipping, orders, invoices and Nepal payments.", accent: ["#0d9488", "#6366f1"], siteName: "
|
|
20
|
+
{ key: "personal", label: "Personal portfolio", description: "A sleek personal / developer portfolio with projects and contact.", accent: ["#6366f1", "#a855f7"], siteName: "LSFolio", siteDescription: "Developer, designer & maker. Selected work and writing." },
|
|
21
|
+
{ key: "business", label: "Business site", description: "A professional company / agency site with services and a CTA.", accent: ["#2563eb", "#06b6d4"], siteName: "LSStudio", siteDescription: "We design and build digital products that grow businesses." },
|
|
22
|
+
{ key: "ecommerce", label: "E-commerce storefront", description: "A modern product storefront home with a featured grid.", accent: ["#0d9488", "#84cc16"], siteName: "LSStore", siteDescription: "Beautiful things, thoughtfully made. Free shipping worldwide." },
|
|
23
|
+
{ key: "saas", label: "SaaS landing", description: "A high-converting SaaS landing page with features and pricing.", accent: ["#7c3aed", "#ec4899"], siteName: "LSCloud", siteDescription: "The all-in-one platform your team will love. Ship faster." },
|
|
24
|
+
{ key: "blog", label: "Blog / magazine", description: "A clean editorial blog home with a featured post and a grid.", accent: ["#f97316", "#ef4444"], siteName: "LSBlogs", siteDescription: "Essays, notes and stories on building things that matter." },
|
|
25
|
+
{ key: "docs", label: "Documentation", description: "A docs landing with quick-start and feature cards.", accent: ["#0ea5e9", "#6366f1"], siteName: "LSDocs", siteDescription: "Everything you need to build with LSDocs \u2014 guides, API and examples." },
|
|
26
|
+
{ key: "dashboard", label: "Admin dashboard", description: "An app dashboard shell with stat cards and a table.", accent: ["#10b981", "#14b8a6"], siteName: "LSAdmin", siteDescription: "Your control center \u2014 metrics, activity and management in one place." },
|
|
27
|
+
{ key: "restaurant", label: "Restaurant / cafe", description: "A warm restaurant home with menu highlights and reservations.", accent: ["#e11d48", "#f59e0b"], siteName: "LSResto", siteDescription: "Seasonal plates, natural wine and a warm room. Book a table." },
|
|
28
|
+
{ key: "marketplace", label: "Marketplace / commerce", description: "A real storefront wired to the Lacspace commerce packages \u2014 cart, checkout, tax, shipping, orders, invoices and Nepal payments.", accent: ["#0d9488", "#6366f1"], siteName: "LSBazaar", siteDescription: "A modern storefront \u2014 cart to checkout, wired end to end." }
|
|
29
29
|
];
|
|
30
30
|
var pkgJson = (ctx) => JSON.stringify({
|
|
31
31
|
name: ctx.name,
|
|
@@ -142,30 +142,47 @@ var globalsCss = (ctx) => `@import "tailwindcss";
|
|
|
142
142
|
--color-surface: var(--surface);
|
|
143
143
|
--color-panel: var(--panel);
|
|
144
144
|
--color-hairline: var(--hairline);
|
|
145
|
+
--color-accent: var(--accent-to);
|
|
146
|
+
--font-sans: var(--font-inter), ui-sans-serif, system-ui, sans-serif;
|
|
147
|
+
--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
|
|
145
148
|
}
|
|
146
149
|
|
|
147
150
|
:root {
|
|
148
151
|
--accent-from: ${ctx.template.accent[0]};
|
|
149
152
|
--accent-to: ${ctx.template.accent[1]};
|
|
150
|
-
/*
|
|
151
|
-
--
|
|
153
|
+
/* on-accent text \u2014 accent gradients are saturated, so black reads cleanest */
|
|
154
|
+
--on-accent: #0a0a0f;
|
|
155
|
+
/* Light theme \u2014 warm off-white ground, near-black ink */
|
|
156
|
+
--bg: #fbfbfd;
|
|
152
157
|
--fg: #0a0a0f;
|
|
153
|
-
--muted: #
|
|
154
|
-
--faint: #
|
|
155
|
-
--surface: rgb(0 0 0 / 0.
|
|
156
|
-
--panel: rgb(0 0 0 / 0.
|
|
157
|
-
--hairline: rgb(0 0 0 / 0.
|
|
158
|
+
--muted: #52525b;
|
|
159
|
+
--faint: #8a8a94;
|
|
160
|
+
--surface: rgb(0 0 0 / 0.025);
|
|
161
|
+
--panel: rgb(0 0 0 / 0.05);
|
|
162
|
+
--hairline: rgb(0 0 0 / 0.08);
|
|
163
|
+
/* soft, layered shadows (never harsh) */
|
|
164
|
+
--shadow-sm: 0 1px 2px rgb(15 15 25 / 0.05), 0 1px 1px rgb(15 15 25 / 0.03);
|
|
165
|
+
--shadow: 0 4px 12px -2px rgb(15 15 25 / 0.08), 0 2px 6px -2px rgb(15 15 25 / 0.05);
|
|
166
|
+
--shadow-lg: 0 24px 48px -12px rgb(15 15 25 / 0.14), 0 8px 20px -8px rgb(15 15 25 / 0.08);
|
|
167
|
+
--glow: 0 0 0 1px rgb(0 0 0 / 0.04);
|
|
168
|
+
--radius: 1rem;
|
|
169
|
+
--radius-lg: 1.5rem;
|
|
158
170
|
}
|
|
159
171
|
|
|
160
172
|
.dark {
|
|
161
|
-
/* Dark theme (default) */
|
|
162
|
-
--
|
|
173
|
+
/* Dark theme (default) \u2014 deep near-black with a hint of indigo */
|
|
174
|
+
--on-accent: #0a0a0f;
|
|
175
|
+
--bg: #08080c;
|
|
163
176
|
--fg: #f5f5f7;
|
|
164
|
-
--muted: rgb(
|
|
165
|
-
--faint: rgb(
|
|
166
|
-
--surface: rgb(255 255 255 / 0.
|
|
167
|
-
--panel: rgb(255 255 255 / 0.
|
|
168
|
-
--hairline: rgb(255 255 255 / 0.
|
|
177
|
+
--muted: rgb(235 235 245 / 0.66);
|
|
178
|
+
--faint: rgb(235 235 245 / 0.42);
|
|
179
|
+
--surface: rgb(255 255 255 / 0.035);
|
|
180
|
+
--panel: rgb(255 255 255 / 0.07);
|
|
181
|
+
--hairline: rgb(255 255 255 / 0.09);
|
|
182
|
+
--shadow-sm: 0 1px 2px rgb(0 0 0 / 0.4);
|
|
183
|
+
--shadow: 0 8px 24px -6px rgb(0 0 0 / 0.5);
|
|
184
|
+
--shadow-lg: 0 32px 64px -16px rgb(0 0 0 / 0.65), 0 12px 28px -12px rgb(0 0 0 / 0.5);
|
|
185
|
+
--glow: 0 0 0 1px rgb(255 255 255 / 0.06);
|
|
169
186
|
}
|
|
170
187
|
|
|
171
188
|
* { border-color: var(--hairline); }
|
|
@@ -175,49 +192,129 @@ body {
|
|
|
175
192
|
color: var(--fg);
|
|
176
193
|
-webkit-font-smoothing: antialiased;
|
|
177
194
|
text-rendering: optimizeLegibility;
|
|
195
|
+
font-feature-settings: "cv02", "cv03", "cv04", "cv11";
|
|
178
196
|
transition: background-color 0.3s ease, color 0.3s ease;
|
|
179
197
|
}
|
|
180
198
|
|
|
199
|
+
/* Confident, tight display type \u2014 a fluid clamp() scale + negative tracking. */
|
|
200
|
+
h1, h2, h3, .display { letter-spacing: -0.02em; text-wrap: balance; }
|
|
201
|
+
h1, .display { letter-spacing: -0.035em; line-height: 1.02; }
|
|
202
|
+
.text-display { font-size: clamp(2.6rem, 6.5vw, 4.75rem); line-height: 1; letter-spacing: -0.04em; font-weight: 800; text-wrap: balance; }
|
|
203
|
+
.text-hero { font-size: clamp(2.2rem, 5.5vw, 3.75rem); line-height: 1.03; letter-spacing: -0.035em; font-weight: 800; text-wrap: balance; }
|
|
204
|
+
.lead { font-size: clamp(1.05rem, 1.6vw, 1.25rem); line-height: 1.6; color: var(--muted); text-wrap: pretty; }
|
|
205
|
+
|
|
181
206
|
::selection { background: var(--accent-to); color: #fff; }
|
|
182
207
|
::-webkit-scrollbar { width: 10px; height: 10px; }
|
|
183
|
-
::-webkit-scrollbar-thumb { background: var(--panel); border-radius: 8px; }
|
|
184
|
-
:
|
|
208
|
+
::-webkit-scrollbar-thumb { background: var(--panel); border-radius: 8px; border: 2px solid transparent; background-clip: padding-box; }
|
|
209
|
+
::-webkit-scrollbar-thumb:hover { background: var(--hairline); }
|
|
210
|
+
:focus-visible { outline: 2px solid var(--accent-to); outline-offset: 2px; border-radius: 6px; }
|
|
185
211
|
|
|
186
212
|
.gradient-text {
|
|
187
|
-
background: linear-gradient(
|
|
213
|
+
background: linear-gradient(115deg, var(--accent-from), var(--accent-to));
|
|
188
214
|
-webkit-background-clip: text; background-clip: text; color: transparent;
|
|
189
215
|
}
|
|
190
|
-
.gradient-bg { background: linear-gradient(
|
|
216
|
+
.gradient-bg { background: linear-gradient(115deg, var(--accent-from), var(--accent-to)); }
|
|
217
|
+
.on-accent { color: var(--on-accent); }
|
|
218
|
+
|
|
219
|
+
/* soft accent halo used behind hero type & badges */
|
|
220
|
+
.accent-glow { box-shadow: 0 8px 32px -8px color-mix(in oklab, var(--accent-to) 55%, transparent); }
|
|
191
221
|
|
|
192
222
|
/* opaque theme background (for sticky chrome & chips) */
|
|
193
223
|
.bg-app { background: var(--bg); }
|
|
194
224
|
|
|
225
|
+
/* frosted glass \u2014 translucent surface + blur + hairline */
|
|
226
|
+
.glass {
|
|
227
|
+
background: color-mix(in oklab, var(--bg) 72%, transparent);
|
|
228
|
+
backdrop-filter: blur(14px) saturate(1.4);
|
|
229
|
+
-webkit-backdrop-filter: blur(14px) saturate(1.4);
|
|
230
|
+
border: 1px solid var(--hairline);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/* the workhorse surface card \u2014 rounded, hairline, soft shadow, hover lift + glow */
|
|
234
|
+
.card {
|
|
235
|
+
border-radius: var(--radius-lg);
|
|
236
|
+
border: 1px solid var(--hairline);
|
|
237
|
+
background: var(--surface);
|
|
238
|
+
box-shadow: var(--shadow-sm);
|
|
239
|
+
transition: transform 0.4s var(--ease-out-expo), box-shadow 0.4s var(--ease-out-expo), border-color 0.4s var(--ease-out-expo);
|
|
240
|
+
}
|
|
241
|
+
.card:hover {
|
|
242
|
+
transform: translateY(-4px);
|
|
243
|
+
box-shadow: var(--shadow-lg);
|
|
244
|
+
border-color: color-mix(in oklab, var(--accent-to) 40%, var(--hairline));
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/* subtle gradient hairline border (draws focus without shouting) */
|
|
248
|
+
.gradient-border { position: relative; }
|
|
249
|
+
.gradient-border::after {
|
|
250
|
+
content: ""; position: absolute; inset: 0; border-radius: inherit; padding: 1px;
|
|
251
|
+
background: linear-gradient(140deg, color-mix(in oklab, var(--accent-from) 60%, transparent), transparent 45%);
|
|
252
|
+
-webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
|
|
253
|
+
mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
|
|
254
|
+
-webkit-mask-composite: xor; mask-composite: exclude; pointer-events: none;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/* subtle background patterns for section grounds */
|
|
258
|
+
.grid-bg {
|
|
259
|
+
background-image: linear-gradient(var(--hairline) 1px, transparent 1px), linear-gradient(90deg, var(--hairline) 1px, transparent 1px);
|
|
260
|
+
background-size: 56px 56px;
|
|
261
|
+
-webkit-mask-image: radial-gradient(ellipse 70% 60% at 50% 0%, #000 30%, transparent 75%);
|
|
262
|
+
mask-image: radial-gradient(ellipse 70% 60% at 50% 0%, #000 30%, transparent 75%);
|
|
263
|
+
}
|
|
264
|
+
.dot-bg {
|
|
265
|
+
background-image: radial-gradient(var(--hairline) 1px, transparent 1px);
|
|
266
|
+
background-size: 22px 22px;
|
|
267
|
+
-webkit-mask-image: radial-gradient(ellipse 60% 50% at 50% 40%, #000 20%, transparent 70%);
|
|
268
|
+
mask-image: radial-gradient(ellipse 60% 50% at 50% 40%, #000 20%, transparent 70%);
|
|
269
|
+
}
|
|
270
|
+
|
|
195
271
|
/* indeterminate progress bar (used by the "under development" pages) */
|
|
196
272
|
@keyframes loadbar { 0% { transform: translateX(-120%); } 100% { transform: translateX(340%); } }
|
|
197
273
|
|
|
198
274
|
/* ambient motion for illustrations & the aurora background */
|
|
199
|
-
@keyframes floaty { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-
|
|
275
|
+
@keyframes floaty { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-18px); } }
|
|
276
|
+
@keyframes drift { 0%, 100% { transform: translate3d(0, 0, 0) scale(1); } 33% { transform: translate3d(4%, -3%, 0) scale(1.06); } 66% { transform: translate3d(-3%, 3%, 0) scale(0.96); } }
|
|
200
277
|
@keyframes spin-slow { to { transform: rotate(360deg); } }
|
|
201
278
|
@keyframes gradient-pan { 0%, 100% { background-position: 0% 50%; } 50% { background-position: 100% 50%; } }
|
|
202
279
|
@keyframes marquee-x { from { transform: translateX(0); } to { transform: translateX(-50%); } }
|
|
280
|
+
@keyframes shimmer-sweep { 0% { transform: translateX(-120%) skewX(-16deg); } 60%, 100% { transform: translateX(320%) skewX(-16deg); } }
|
|
203
281
|
.animate-floaty { animation: floaty 6s ease-in-out infinite; }
|
|
204
282
|
.animate-floaty-slow { animation: floaty 9s ease-in-out infinite; }
|
|
205
|
-
.animate-
|
|
283
|
+
.animate-drift { animation: drift 22s ease-in-out infinite; }
|
|
284
|
+
.animate-drift-slow { animation: drift 30s ease-in-out infinite; }
|
|
285
|
+
.animate-spin-slow { animation: spin-slow 30s linear infinite; }
|
|
206
286
|
.gradient-text-animated {
|
|
207
|
-
background: linear-gradient(
|
|
287
|
+
background: linear-gradient(115deg, var(--accent-from), var(--accent-to), var(--accent-from));
|
|
208
288
|
background-size: 200% 200%;
|
|
209
289
|
-webkit-background-clip: text; background-clip: text; color: transparent;
|
|
210
290
|
animation: gradient-pan 6s ease infinite;
|
|
211
291
|
}
|
|
212
|
-
.marquee-track { display: flex; width: max-content; animation: marquee-x
|
|
292
|
+
.marquee-track { display: flex; width: max-content; animation: marquee-x 40s linear infinite; }
|
|
293
|
+
.marquee-track:hover { animation-play-state: paused; }
|
|
213
294
|
@keyframes draw { to { stroke-dashoffset: 0; } }
|
|
214
|
-
.animate-draw { stroke-dasharray: 2000; stroke-dashoffset: 2000; animation: draw 1.8s ease forwards; }
|
|
295
|
+
.animate-draw { stroke-dasharray: 2000; stroke-dashoffset: 2000; animation: draw 1.8s var(--ease-out-expo) forwards; }
|
|
215
296
|
.marquee-mask { -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent); mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent); }
|
|
216
297
|
|
|
217
|
-
/*
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
298
|
+
/* a slow gradient light-sweep for primary CTAs */
|
|
299
|
+
.shimmer { position: relative; overflow: hidden; }
|
|
300
|
+
.shimmer::after {
|
|
301
|
+
content: ""; position: absolute; top: 0; bottom: 0; left: 0; width: 40%;
|
|
302
|
+
background: linear-gradient(90deg, transparent, rgb(255 255 255 / 0.45), transparent);
|
|
303
|
+
animation: shimmer-sweep 4.5s var(--ease-out-expo) infinite; pointer-events: none;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/* soft, staggered entrance for content \u2014 nicer easing */
|
|
307
|
+
@keyframes rise { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: none; } }
|
|
308
|
+
main > section { animation: rise 0.7s var(--ease-out-expo) both; }
|
|
309
|
+
main > section:nth-of-type(2) { animation-delay: 0.06s; }
|
|
310
|
+
main > section:nth-of-type(3) { animation-delay: 0.12s; }
|
|
311
|
+
main > section:nth-of-type(4) { animation-delay: 0.16s; }
|
|
312
|
+
|
|
313
|
+
/* respect reduced motion \u2014 kill animation AND transition-driven transforms */
|
|
314
|
+
@media (prefers-reduced-motion: reduce) {
|
|
315
|
+
*, ::before, ::after { animation: none !important; transition-duration: 0.01ms !important; scroll-behavior: auto !important; }
|
|
316
|
+
.shimmer::after { display: none; }
|
|
317
|
+
}
|
|
221
318
|
${ctx.template.key === "blog" || ctx.template.key === "docs" ? BLOG_PROSE_CSS : ""}`;
|
|
222
319
|
var BLOG_PROSE_CSS = `
|
|
223
320
|
/* article typography for Markdown posts */
|
|
@@ -268,13 +365,13 @@ import { site } from "@/lib/site";
|
|
|
268
365
|
import { CommandMenu } from "@/components/command-menu";
|
|
269
366
|
${imports}import "./globals.css";
|
|
270
367
|
|
|
271
|
-
const inter = Inter({ subsets: ["latin"], display: "swap" });
|
|
368
|
+
const inter = Inter({ subsets: ["latin"], display: "swap", variable: "--font-inter" });
|
|
272
369
|
|
|
273
370
|
export const metadata: Metadata = site.meta({ title: ${JSON.stringify(ctx.template.siteName)} });
|
|
274
371
|
|
|
275
372
|
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
276
373
|
return (
|
|
277
|
-
<html lang="en" className={inter.className} suppressHydrationWarning>
|
|
374
|
+
<html lang="en" className={\`\${inter.variable} \${inter.className}\`} suppressHydrationWarning>
|
|
278
375
|
<body className="antialiased">
|
|
279
376
|
{/* \u2728 Dark / light / system theming with a built-in no-flash script \u2014 @lacspace/theme */}
|
|
280
377
|
<ThemeProvider defaultTheme="dark">
|
|
@@ -324,10 +421,11 @@ var notFound = () => `import Link from "next/link";
|
|
|
324
421
|
|
|
325
422
|
export default function NotFound() {
|
|
326
423
|
return (
|
|
327
|
-
<main className="flex min-h-screen flex-col items-center justify-center gap-6 px-6 text-center">
|
|
328
|
-
<div className="
|
|
329
|
-
<
|
|
330
|
-
<
|
|
424
|
+
<main className="relative flex min-h-screen flex-col items-center justify-center gap-6 overflow-hidden px-6 text-center">
|
|
425
|
+
<div aria-hidden className="pointer-events-none absolute h-96 w-96 rounded-full gradient-bg opacity-20 blur-3xl animate-drift-slow" />
|
|
426
|
+
<div className="relative text-[9rem] font-black leading-none gradient-text-animated">404</div>
|
|
427
|
+
<p className="relative text-lg text-muted">This page wandered off. Let's get you home.</p>
|
|
428
|
+
<Link href="/" className="shimmer relative rounded-full gradient-bg px-7 py-3.5 font-semibold on-accent transition hover:-translate-y-0.5">Back home</Link>
|
|
331
429
|
</main>
|
|
332
430
|
);
|
|
333
431
|
}
|
|
@@ -446,11 +544,23 @@ function homePage(ctx) {
|
|
|
446
544
|
const n = ctx.template.siteName;
|
|
447
545
|
if (ctx.template.key === "dashboard") return dashboardHome(ctx);
|
|
448
546
|
if (ctx.template.key === "marketplace") return marketplaceHome(ctx);
|
|
449
|
-
const shell = (inner) =>
|
|
547
|
+
const shell = (inner) => {
|
|
548
|
+
const ctaCopy = {
|
|
549
|
+
personal: { t: "Have something in mind?", s: "I'm taking on a couple of projects this quarter \u2014 let's talk.", l: "Start a conversation", h: "/contact" },
|
|
550
|
+
business: { t: "Let's build something that ships", s: "Tell us the outcome you're after and we'll map the path.", l: "Start a project", h: "/contact" },
|
|
551
|
+
ecommerce: { t: "Find your next favourite thing", s: "Free shipping over $50 and 30-day easy returns, always.", l: "Shop the collection", h: "/shop" },
|
|
552
|
+
saas: { t: "Ready to ship faster?", s: "Start free in minutes \u2014 no credit card, no lock-in.", l: "Get started free", h: "/pricing" },
|
|
553
|
+
blog: { t: "Never miss a piece", s: "Get new essays in your inbox \u2014 no spam, unsubscribe anytime.", l: "Join the newsletter", h: "/newsletter" },
|
|
554
|
+
docs: { t: "Start building today", s: "Go from install to your first request in five minutes.", l: "Read the quickstart", h: "/docs" },
|
|
555
|
+
restaurant: { t: "Join us for dinner", s: "We fill up fast on weekends \u2014 book your table ahead.", l: "Reserve a table", h: "/reservations" }
|
|
556
|
+
};
|
|
557
|
+
const cc = ctaCopy[ctx.template.key] ?? ctaCopy.business;
|
|
558
|
+
return `import Link from "next/link";
|
|
559
|
+
import { site } from "@/lib/site";
|
|
450
560
|
import { LiveStats } from "@/components/live-stats";
|
|
451
561
|
import { Aurora } from "@/components/aurora";
|
|
452
562
|
import { HeroArt } from "@/components/hero-art";
|
|
453
|
-
import { StatBand, Steps, Testimonial } from "@/components/ui";
|
|
563
|
+
import { StatBand, Steps, Testimonial, CTABand } from "@/components/ui";
|
|
454
564
|
|
|
455
565
|
// \u2728 Self-canonical home page \u2014 one line, full SEO (title, canonical, OG, Twitter).
|
|
456
566
|
export const metadata = site.meta({ title: ${JSON.stringify(n)}, path: "/" });
|
|
@@ -458,202 +568,229 @@ export const metadata = site.meta({ title: ${JSON.stringify(n)}, path: "/" });
|
|
|
458
568
|
export default function Home() {
|
|
459
569
|
return (
|
|
460
570
|
<main className="relative min-h-screen overflow-hidden">
|
|
461
|
-
{/* \u2728
|
|
571
|
+
{/* \u2728 Soft, theme-aware gradient-mesh backdrop */}
|
|
462
572
|
<Aurora />
|
|
463
573
|
${inner}
|
|
464
|
-
${showcaseSection(ctx)}
|
|
465
574
|
${marqueeSection(ctx)}
|
|
575
|
+
${showcaseSection(ctx)}
|
|
466
576
|
${extraHomeSections(ctx)}
|
|
577
|
+
<CTABand title=${JSON.stringify(cc.t)} subtitle=${JSON.stringify(cc.s)} ctaLabel=${JSON.stringify(cc.l)} ctaHref=${JSON.stringify(cc.h)} />
|
|
467
578
|
${faqSection(ctx)}
|
|
468
579
|
${builtWithSection(ctx)}
|
|
469
580
|
</main>
|
|
470
581
|
);
|
|
471
582
|
}
|
|
472
583
|
`;
|
|
584
|
+
};
|
|
473
585
|
if (ctx.template.key === "personal") {
|
|
474
|
-
return shell(`<section className="mx-auto max-w-3xl px-6 py-
|
|
475
|
-
<
|
|
476
|
-
<h1 className="text-
|
|
477
|
-
<p className="mx-auto mt-6 max-w-xl
|
|
478
|
-
<div className="mt-10 flex justify-center gap-
|
|
479
|
-
<a href="#work" className="
|
|
480
|
-
<
|
|
586
|
+
return shell(`<section className="relative mx-auto max-w-3xl px-6 py-32 text-center sm:py-40">
|
|
587
|
+
<span className="glass inline-flex items-center gap-2 rounded-full px-4 py-1.5 text-xs font-semibold text-muted"><span className="relative flex h-2 w-2"><span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-emerald-400 opacity-75" /><span className="relative inline-flex h-2 w-2 rounded-full bg-emerald-400" /></span> Available for select projects</span>
|
|
588
|
+
<h1 className="text-display mt-7">Hi, I'm <span className="gradient-text">${n}</span></h1>
|
|
589
|
+
<p className="lead mx-auto mt-6 max-w-xl">${ctx.template.siteDescription}</p>
|
|
590
|
+
<div className="mt-10 flex flex-wrap justify-center gap-3">
|
|
591
|
+
<a href="#work" className="shimmer rounded-full gradient-bg px-7 py-3.5 font-semibold on-accent transition hover:-translate-y-0.5">View my work</a>
|
|
592
|
+
<Link href="/contact" className="rounded-full border border-hairline px-7 py-3.5 font-semibold transition hover:bg-surface">Get in touch</Link>
|
|
481
593
|
</div>
|
|
482
594
|
</section>
|
|
483
|
-
<section id="work" className="mx-auto max-w-6xl px-6 py-
|
|
484
|
-
<
|
|
595
|
+
<section id="work" className="mx-auto max-w-6xl px-6 py-20">
|
|
596
|
+
<div className="mb-12 flex items-end justify-between">
|
|
597
|
+
<div><p className="text-sm font-semibold uppercase tracking-widest gradient-text">Selected work</p><h2 className="mt-3 text-3xl font-bold sm:text-4xl">Things I've shipped</h2></div>
|
|
598
|
+
<Link href="/work" className="hidden text-sm text-muted underline-offset-4 transition hover:text-fg hover:underline sm:block">All projects \u2192</Link>
|
|
599
|
+
</div>
|
|
485
600
|
<div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
|
|
486
|
-
{[
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
601
|
+
{[
|
|
602
|
+
{ t: "Aurora Design System", d: "A themeable component library and tokens powering 40+ product screens.", tag: "Design + Code", e: "\u{1F3A8}" },
|
|
603
|
+
{ t: "Northwind Analytics", d: "A real-time dashboard that cut reporting time from hours to seconds.", tag: "Product", e: "\u{1F4CA}" },
|
|
604
|
+
{ t: "Lumen Mobile", d: "A 4.9\u2605 React Native app rebuilt from the ground up for speed.", tag: "Mobile", e: "\u{1F4F1}" },
|
|
605
|
+
{ t: "Harbor Commerce", d: "A headless storefront with a checkout that converts 38% better.", tag: "E-commerce", e: "\u{1F6CD}\uFE0F" },
|
|
606
|
+
{ t: "Cadence Marketing", d: "A brand and site refresh that doubled qualified inbound leads.", tag: "Brand", e: "\u2728" },
|
|
607
|
+
{ t: "Meadow Docs", d: "A fast, searchable docs platform teams actually enjoy reading.", tag: "Web", e: "\u{1F4DA}" },
|
|
608
|
+
].map((p) => (
|
|
609
|
+
<Link key={p.t} href="/work" className="card group flex flex-col p-6">
|
|
610
|
+
<div className="mb-5 flex aspect-[16/10] items-center justify-center overflow-hidden rounded-xl gradient-bg text-5xl on-accent">{p.e}</div>
|
|
611
|
+
<span className="text-xs font-semibold uppercase tracking-widest gradient-text">{p.tag}</span>
|
|
612
|
+
<h3 className="mt-1.5 font-semibold tracking-tight">{p.t}</h3>
|
|
613
|
+
<p className="mt-1.5 text-sm leading-relaxed text-muted">{p.d}</p>
|
|
614
|
+
</Link>
|
|
492
615
|
))}
|
|
493
616
|
</div>
|
|
494
617
|
</section>`);
|
|
495
618
|
}
|
|
496
619
|
if (ctx.template.key === "business") {
|
|
497
|
-
return shell(`<section className="mx-auto max-w-4xl px-6 py-
|
|
498
|
-
<
|
|
499
|
-
<
|
|
500
|
-
<
|
|
620
|
+
return shell(`<section className="relative mx-auto max-w-4xl px-6 py-32 text-center sm:py-40">
|
|
621
|
+
<span className="glass inline-flex items-center gap-2 rounded-full px-4 py-1.5 text-xs font-semibold text-muted"><span aria-hidden className="h-1.5 w-1.5 rounded-full gradient-bg" /> A senior product studio</span>
|
|
622
|
+
<h1 className="text-display mt-7">We build products <span className="gradient-text">that grow businesses</span></h1>
|
|
623
|
+
<p className="lead mx-auto mt-6 max-w-2xl">${ctx.template.siteDescription}</p>
|
|
624
|
+
<div className="mt-10 flex flex-wrap justify-center gap-3">
|
|
625
|
+
<Link href="/contact" className="shimmer rounded-full gradient-bg px-7 py-3.5 font-semibold on-accent transition hover:-translate-y-0.5">Start a project</Link>
|
|
626
|
+
<Link href="/work" className="rounded-full border border-hairline px-7 py-3.5 font-semibold transition hover:bg-surface">See our work</Link>
|
|
627
|
+
</div>
|
|
501
628
|
</section>
|
|
502
|
-
<section id="services" className="mx-auto max-w-6xl px-6 py-
|
|
503
|
-
<
|
|
629
|
+
<section id="services" className="mx-auto max-w-6xl px-6 py-20">
|
|
630
|
+
<div className="mb-12 text-center"><p className="text-sm font-semibold uppercase tracking-widest gradient-text">What we do</p><h2 className="mt-3 text-3xl font-bold sm:text-4xl">One team, from strategy to launch</h2></div>
|
|
504
631
|
<div className="grid gap-6 md:grid-cols-3">
|
|
505
632
|
{[
|
|
506
|
-
{ t: "Strategy", d: "Positioning, research and a roadmap that ships." },
|
|
507
|
-
{ t: "Design", d: "Brand and product design that
|
|
508
|
-
{ t: "Engineering", d: "Fast, reliable software built to last." },
|
|
633
|
+
{ t: "Strategy", d: "Positioning, research and a roadmap that ships \u2014 grounded in evidence, not opinions.", e: "\u{1F9ED}" },
|
|
634
|
+
{ t: "Design", d: "Brand and product design people remember, with a system that scales past launch day.", e: "\u{1F3A8}" },
|
|
635
|
+
{ t: "Engineering", d: "Fast, reliable software built to last \u2014 typed, tested and observable from day one.", e: "\u2699\uFE0F" },
|
|
509
636
|
].map((s) => (
|
|
510
|
-
<div key={s.t} className="
|
|
511
|
-
<
|
|
512
|
-
<
|
|
637
|
+
<div key={s.t} className="card p-8">
|
|
638
|
+
<div className="mb-5 inline-flex h-12 w-12 items-center justify-center rounded-2xl gradient-bg text-2xl accent-glow">{s.e}</div>
|
|
639
|
+
<h3 className="text-xl font-bold tracking-tight">{s.t}</h3>
|
|
640
|
+
<p className="mt-2 leading-relaxed text-muted">{s.d}</p>
|
|
513
641
|
</div>
|
|
514
642
|
))}
|
|
515
643
|
</div>
|
|
516
|
-
</section>
|
|
517
|
-
<section id="contact" className="mx-auto max-w-3xl px-6 py-24 text-center">
|
|
518
|
-
<h2 className="text-3xl font-bold">Let's work together</h2>
|
|
519
|
-
<p className="mt-3 text-muted">Tell us about your project and we'll get back within a day.</p>
|
|
520
|
-
<a href="mailto:hello@example.com" className="mt-8 inline-block rounded-full border border-hairline px-8 py-3 font-semibold hover:bg-surface">hello@example.com</a>
|
|
521
644
|
</section>`);
|
|
522
645
|
}
|
|
523
646
|
if (ctx.template.key === "ecommerce") {
|
|
524
|
-
return shell(`<section className="mx-auto max-w-5xl px-6 py-
|
|
525
|
-
<
|
|
526
|
-
<
|
|
527
|
-
<
|
|
647
|
+
return shell(`<section className="relative mx-auto max-w-5xl px-6 py-32 text-center sm:py-40">
|
|
648
|
+
<span className="glass inline-flex items-center gap-2 rounded-full px-4 py-1.5 text-xs font-semibold text-muted">\u{1F69A} Free worldwide shipping over $50</span>
|
|
649
|
+
<h1 className="text-display mt-7"><span className="gradient-text">${n}</span></h1>
|
|
650
|
+
<p className="lead mx-auto mt-6 max-w-xl">${ctx.template.siteDescription}</p>
|
|
651
|
+
<div className="mt-10 flex flex-wrap justify-center gap-3">
|
|
652
|
+
<Link href="/shop" className="shimmer rounded-full gradient-bg px-8 py-3.5 font-semibold on-accent transition hover:-translate-y-0.5">Shop the collection</Link>
|
|
653
|
+
<Link href="/collections" className="rounded-full border border-hairline px-8 py-3.5 font-semibold transition hover:bg-surface">Browse collections</Link>
|
|
654
|
+
</div>
|
|
528
655
|
</section>
|
|
529
|
-
<section id="shop" className="mx-auto max-w-6xl px-6 py-
|
|
530
|
-
<
|
|
656
|
+
<section id="shop" className="mx-auto max-w-6xl px-6 py-20">
|
|
657
|
+
<div className="mb-12 flex items-end justify-between">
|
|
658
|
+
<div><p className="text-sm font-semibold uppercase tracking-widest gradient-text">Featured</p><h2 className="mt-3 text-3xl font-bold sm:text-4xl">This week's edit</h2></div>
|
|
659
|
+
<Link href="/shop" className="hidden text-sm text-muted underline-offset-4 transition hover:text-fg hover:underline sm:block">Shop all \u2192</Link>
|
|
660
|
+
</div>
|
|
531
661
|
<div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-4">
|
|
532
662
|
{[
|
|
533
|
-
{ n: "Aurora Lamp", p: "$89" }, { n: "Terra Mug", p: "$24" },
|
|
534
|
-
{ n: "Linen Throw", p: "$65" }, { n: "Oak Stand", p: "$120" },
|
|
663
|
+
{ n: "Aurora Table Lamp", p: "$89", e: "\u{1F4A1}" }, { n: "Terra Stoneware Mug", p: "$24", e: "\u2615" },
|
|
664
|
+
{ n: "Linen Waffle Throw", p: "$65", e: "\u{1F9FA}" }, { n: "Oak Monitor Stand", p: "$120", e: "\u{1FAB5}" },
|
|
535
665
|
].map((prod) => (
|
|
536
|
-
<div key={prod.n} className="group
|
|
537
|
-
<
|
|
538
|
-
<div className="flex items-
|
|
539
|
-
<h3 className="font-semibold">{prod.n}</h3>
|
|
540
|
-
<span className="text-muted">{prod.p}</span>
|
|
666
|
+
<div key={prod.n} className="card group flex flex-col p-4">
|
|
667
|
+
<Link href="/shop" className="mb-4 flex aspect-square items-center justify-center overflow-hidden rounded-xl gradient-bg text-6xl on-accent transition group-hover:scale-[1.03]">{prod.e}</Link>
|
|
668
|
+
<div className="flex items-start justify-between gap-2">
|
|
669
|
+
<h3 className="font-semibold tracking-tight">{prod.n}</h3>
|
|
670
|
+
<span className="shrink-0 font-semibold text-muted">{prod.p}</span>
|
|
541
671
|
</div>
|
|
542
|
-
<
|
|
672
|
+
<Link href="/shop" className="mt-4 block w-full rounded-full gradient-bg py-2.5 text-center text-sm font-semibold on-accent transition hover:-translate-y-0.5">Add to cart</Link>
|
|
543
673
|
</div>
|
|
544
674
|
))}
|
|
545
675
|
</div>
|
|
546
676
|
</section>`);
|
|
547
677
|
}
|
|
548
678
|
if (ctx.template.key === "blog") {
|
|
549
|
-
return shell(`<section className="mx-auto max-w-3xl px-6 py-
|
|
550
|
-
<p className="
|
|
551
|
-
<h1 className="text-
|
|
552
|
-
<p className="mt-6
|
|
679
|
+
return shell(`<section className="mx-auto max-w-3xl px-6 py-28 sm:py-32">
|
|
680
|
+
<p className="text-sm font-semibold uppercase tracking-widest gradient-text">The Journal</p>
|
|
681
|
+
<h1 className="text-display mt-4"><span className="gradient-text">${n}</span></h1>
|
|
682
|
+
<p className="lead mt-6 max-w-2xl">${ctx.template.siteDescription}</p>
|
|
683
|
+
<Link href="/blog" className="mt-8 inline-flex items-center gap-2 text-sm font-semibold text-fg underline-offset-4 hover:underline">Read the archive \u2192</Link>
|
|
553
684
|
</section>
|
|
554
685
|
<section id="latest" className="mx-auto max-w-5xl px-6 pb-8">
|
|
555
|
-
<
|
|
556
|
-
<div className="
|
|
557
|
-
<
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
686
|
+
<Link href="/blog" className="card group grid gap-6 overflow-hidden p-6 sm:grid-cols-[1.2fr_1fr] sm:p-8">
|
|
687
|
+
<div className="flex aspect-[16/10] items-center justify-center overflow-hidden rounded-2xl gradient-bg text-6xl on-accent transition group-hover:scale-[1.02]">\u{1F4EE}</div>
|
|
688
|
+
<div className="flex flex-col justify-center">
|
|
689
|
+
<span className="text-xs font-semibold uppercase tracking-widest gradient-text">Featured</span>
|
|
690
|
+
<h2 className="mt-3 text-2xl font-bold tracking-tight sm:text-3xl">The one thing every product needs before launch</h2>
|
|
691
|
+
<p className="mt-3 leading-relaxed text-muted">Most teams polish the wrong things. Here's the single unglamorous check that separates a launch that lands from one that limps.</p>
|
|
692
|
+
<span className="mt-5 text-sm text-faint">8 min read \xB7 Product</span>
|
|
693
|
+
</div>
|
|
694
|
+
</Link>
|
|
561
695
|
</section>
|
|
562
|
-
<section className="mx-auto max-w-5xl px-6 py-
|
|
696
|
+
<section className="mx-auto max-w-5xl px-6 py-16">
|
|
563
697
|
<div className="grid gap-8 sm:grid-cols-2">
|
|
564
|
-
{[
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
698
|
+
{[
|
|
699
|
+
{ t: "Writing that survives the scroll", d: "Editing tricks that keep readers past the first paragraph.", tag: "Craft", e: "\u270D\uFE0F" },
|
|
700
|
+
{ t: "The cost of a fast 'yes'", d: "Why the cheapest decisions are often the most expensive.", tag: "Product", e: "\u2696\uFE0F" },
|
|
701
|
+
{ t: "Building in the open, one year in", d: "What we learned shipping every week where anyone could watch.", tag: "Culture", e: "\u{1F331}" },
|
|
702
|
+
{ t: "Design systems for teams of one", d: "You don't need a committee to move fast and stay consistent.", tag: "Design", e: "\u{1F3A8}" },
|
|
703
|
+
].map((p) => (
|
|
704
|
+
<Link key={p.t} href="/blog" className="group">
|
|
705
|
+
<div className="mb-4 flex aspect-[16/9] items-center justify-center overflow-hidden rounded-2xl gradient-bg text-5xl on-accent transition group-hover:scale-[1.02]">{p.e}</div>
|
|
706
|
+
<span className="text-xs font-semibold uppercase tracking-widest gradient-text">{p.tag}</span>
|
|
707
|
+
<h3 className="mt-1.5 text-xl font-semibold tracking-tight transition group-hover:text-[color:var(--accent-to)]">{p.t}</h3>
|
|
708
|
+
<p className="mt-1.5 text-sm leading-relaxed text-muted">{p.d}</p>
|
|
709
|
+
</Link>
|
|
571
710
|
))}
|
|
572
711
|
</div>
|
|
573
712
|
</section>`);
|
|
574
713
|
}
|
|
575
714
|
if (ctx.template.key === "docs") {
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
<
|
|
579
|
-
<
|
|
580
|
-
|
|
715
|
+
const pkg = "@" + n.toLowerCase() + "/sdk";
|
|
716
|
+
return shell(`<section className="relative mx-auto max-w-4xl px-6 py-32 text-center sm:py-40">
|
|
717
|
+
<span className="glass inline-flex items-center gap-2 rounded-full px-4 py-1.5 text-xs font-semibold text-muted">\u{1F4D8} Docs \xB7 v1.0</span>
|
|
718
|
+
<h1 className="text-display mt-7">Build with <span className="gradient-text">${n}</span></h1>
|
|
719
|
+
<p className="lead mx-auto mt-6 max-w-2xl">Guides, API references and copy-paste examples \u2014 everything you need, in one fast, searchable place.</p>
|
|
720
|
+
<div className="mt-9 flex flex-wrap items-center justify-center gap-3">
|
|
721
|
+
<Link href="/docs" className="shimmer rounded-full gradient-bg px-7 py-3.5 font-semibold on-accent transition hover:-translate-y-0.5">Read the docs</Link>
|
|
722
|
+
<div className="glass inline-flex items-center gap-3 rounded-full px-5 py-3 font-mono text-sm text-muted"><span className="gradient-text font-bold">$</span> npm i ${pkg}</div>
|
|
581
723
|
</div>
|
|
582
724
|
</section>
|
|
583
|
-
<section id="guides" className="mx-auto max-w-6xl px-6 py-
|
|
725
|
+
<section id="guides" className="mx-auto max-w-6xl px-6 py-16">
|
|
584
726
|
<div className="grid gap-6 md:grid-cols-3">
|
|
585
727
|
{[
|
|
586
|
-
{ t: "Quick start", d: "Go from zero to your first request in five minutes." },
|
|
587
|
-
{ t: "Guides", d: "Task-focused walkthroughs for the
|
|
588
|
-
{ t: "API reference", d: "Every endpoint, typed, with copy-paste examples." },
|
|
728
|
+
{ t: "Quick start", d: "Go from zero to your first successful request in under five minutes.", e: "\u{1F680}", href: "/docs" },
|
|
729
|
+
{ t: "Guides", d: "Task-focused walkthroughs for the paths you'll actually take.", e: "\u{1F9ED}", href: "/guides" },
|
|
730
|
+
{ t: "API reference", d: "Every endpoint, fully typed, with copy-paste examples in each language.", e: "\u{1F50C}", href: "/api-reference" },
|
|
589
731
|
].map((c) => (
|
|
590
|
-
<
|
|
591
|
-
<
|
|
592
|
-
<
|
|
593
|
-
|
|
732
|
+
<Link key={c.t} href={c.href} className="card group p-8">
|
|
733
|
+
<div className="mb-5 inline-flex h-12 w-12 items-center justify-center rounded-2xl gradient-bg text-2xl accent-glow">{c.e}</div>
|
|
734
|
+
<h3 className="text-xl font-bold tracking-tight">{c.t} <span className="inline-block transition group-hover:translate-x-1">\u2192</span></h3>
|
|
735
|
+
<p className="mt-2 leading-relaxed text-muted">{c.d}</p>
|
|
736
|
+
</Link>
|
|
594
737
|
))}
|
|
595
738
|
</div>
|
|
596
739
|
</section>`);
|
|
597
740
|
}
|
|
598
741
|
if (ctx.template.key === "restaurant") {
|
|
599
|
-
return shell(`<section className="mx-auto max-w-4xl px-6 py-
|
|
600
|
-
<p className="
|
|
601
|
-
<h1 className="text-
|
|
602
|
-
<p className="mx-auto mt-6 max-w-xl
|
|
603
|
-
<
|
|
742
|
+
return shell(`<section className="relative mx-auto max-w-4xl px-6 py-32 text-center sm:py-40">
|
|
743
|
+
<p className="text-sm font-semibold uppercase tracking-[0.35em] text-faint">Est. 2026 \xB7 Natural wine bar & kitchen</p>
|
|
744
|
+
<h1 className="text-display mt-5"><span className="gradient-text">${n}</span></h1>
|
|
745
|
+
<p className="lead mx-auto mt-6 max-w-xl">${ctx.template.siteDescription}</p>
|
|
746
|
+
<div className="mt-10 flex flex-wrap justify-center gap-3">
|
|
747
|
+
<Link href="/reservations" className="shimmer rounded-full gradient-bg px-8 py-3.5 font-semibold on-accent transition hover:-translate-y-0.5">Reserve a table</Link>
|
|
748
|
+
<Link href="/menu" className="rounded-full border border-hairline px-8 py-3.5 font-semibold transition hover:bg-surface">View the menu</Link>
|
|
749
|
+
</div>
|
|
604
750
|
</section>
|
|
605
|
-
<section id="menu" className="mx-auto max-w-4xl px-6 py-
|
|
606
|
-
<
|
|
607
|
-
<div className="grid gap-6 sm:grid-cols-2">
|
|
751
|
+
<section id="menu" className="mx-auto max-w-4xl px-6 py-20">
|
|
752
|
+
<div className="mb-12 text-center"><p className="text-sm font-semibold uppercase tracking-widest gradient-text">From the pass</p><h2 className="mt-3 text-3xl font-bold sm:text-4xl">Tonight's plates</h2></div>
|
|
753
|
+
<div className="grid gap-x-12 gap-y-6 sm:grid-cols-2">
|
|
608
754
|
{[
|
|
609
|
-
{ n: "Charred leeks", p: "$14" }, { n: "Handmade tagliatelle", p: "$22" },
|
|
610
|
-
{ n: "Wood-fired trout", p: "$28" }, { n: "
|
|
755
|
+
{ n: "Charred leeks, hazelnut & aged pecorino", p: "$14" }, { n: "Handmade tagliatelle, brown butter & sage", p: "$22" },
|
|
756
|
+
{ n: "Wood-fired trout, fennel & burnt lemon", p: "$28" }, { n: "Dry-aged sirloin, bone marrow butter", p: "$34" },
|
|
757
|
+
{ n: "Roasted heritage carrots, dukkah & yoghurt", p: "$13" }, { n: "Olive oil & almond cake, cr\xE8me fra\xEEche", p: "$11" },
|
|
611
758
|
].map((d) => (
|
|
612
|
-
<div key={d.n} className="flex items-baseline
|
|
613
|
-
<
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
</div>
|
|
617
|
-
<span className="shrink-0 gradient-text font-bold">{d.p}</span>
|
|
759
|
+
<div key={d.n} className="flex items-baseline gap-3 border-b border-dashed border-hairline pb-4">
|
|
760
|
+
<h3 className="font-semibold tracking-tight">{d.n}</h3>
|
|
761
|
+
<span className="flex-1" />
|
|
762
|
+
<span className="shrink-0 gradient-text font-bold tabular-nums">{d.p}</span>
|
|
618
763
|
</div>
|
|
619
764
|
))}
|
|
620
765
|
</div>
|
|
621
|
-
|
|
622
|
-
<section id="reserve" className="mx-auto max-w-2xl px-6 py-24 text-center">
|
|
623
|
-
<h2 className="text-3xl font-bold">Join us</h2>
|
|
624
|
-
<p className="mt-3 text-muted">Open Wed\u2013Sun, 5pm till late. Walk-ins welcome; bookings recommended.</p>
|
|
625
|
-
<a href="tel:+10000000000" className="mt-8 inline-block rounded-full border border-hairline px-8 py-3 font-semibold hover:bg-surface">Call to book</a>
|
|
766
|
+
<p className="mt-10 text-center text-sm text-muted">Menu changes with the seasons \xB7 ask us about tonight's natural wine pairings.</p>
|
|
626
767
|
</section>`);
|
|
627
768
|
}
|
|
628
|
-
return shell(`<section className="mx-auto max-w-4xl px-6 py-
|
|
629
|
-
<
|
|
630
|
-
<h1 className="text-
|
|
631
|
-
<p className="mx-auto mt-6 max-w-2xl
|
|
632
|
-
<div className="mt-10 flex justify-center gap-
|
|
633
|
-
<
|
|
634
|
-
<
|
|
769
|
+
return shell(`<section className="relative mx-auto max-w-4xl px-6 py-32 text-center sm:py-40">
|
|
770
|
+
<span className="glass inline-flex items-center gap-2 rounded-full px-4 py-1.5 text-xs font-semibold text-muted"><span aria-hidden className="h-1.5 w-1.5 rounded-full gradient-bg" /> New \xB7 Realtime dashboards are live</span>
|
|
771
|
+
<h1 className="text-display mt-7">Ship faster with <span className="gradient-text">${n}</span></h1>
|
|
772
|
+
<p className="lead mx-auto mt-6 max-w-2xl">${ctx.template.siteDescription}</p>
|
|
773
|
+
<div className="mt-10 flex flex-wrap justify-center gap-3">
|
|
774
|
+
<Link href="/pricing" className="shimmer rounded-full gradient-bg px-8 py-3.5 font-semibold on-accent transition hover:-translate-y-0.5">Start free</Link>
|
|
775
|
+
<Link href="/features" className="rounded-full border border-hairline px-8 py-3.5 font-semibold transition hover:bg-surface">See features</Link>
|
|
635
776
|
</div>
|
|
777
|
+
<p className="mt-5 text-sm text-faint">No credit card \xB7 Free forever plan \xB7 SOC 2-ready</p>
|
|
636
778
|
</section>
|
|
637
|
-
<section id="features" className="mx-auto max-w-6xl px-6 py-
|
|
779
|
+
<section id="features" className="mx-auto max-w-6xl px-6 py-20">
|
|
780
|
+
<div className="mb-12 text-center"><p className="text-sm font-semibold uppercase tracking-widest gradient-text">Why teams switch</p><h2 className="mt-3 text-3xl font-bold sm:text-4xl">Everything in one fast place</h2></div>
|
|
638
781
|
<div className="grid gap-6 md:grid-cols-3">
|
|
639
782
|
{[
|
|
640
|
-
{ t: "
|
|
641
|
-
{ t: "Secure", d: "Hardened headers and
|
|
642
|
-
{ t: "
|
|
783
|
+
{ t: "Realtime analytics", d: "Live dashboards on the edge \u2014 global by default, no waiting for pipelines.", e: "\u26A1" },
|
|
784
|
+
{ t: "Secure by default", d: "Hardened headers, SSO and audit logs out of the box. SOC 2-ready.", e: "\u{1F512}" },
|
|
785
|
+
{ t: "Scales with you", d: "From your first user to your millionth, with usage-based billing built in.", e: "\u{1F4C8}" },
|
|
643
786
|
].map((f) => (
|
|
644
|
-
<div key={f.t} className="
|
|
645
|
-
<
|
|
646
|
-
<
|
|
787
|
+
<div key={f.t} className="card p-8">
|
|
788
|
+
<div className="mb-5 inline-flex h-12 w-12 items-center justify-center rounded-2xl gradient-bg text-2xl accent-glow">{f.e}</div>
|
|
789
|
+
<h3 className="text-xl font-bold tracking-tight">{f.t}</h3>
|
|
790
|
+
<p className="mt-2 leading-relaxed text-muted">{f.d}</p>
|
|
647
791
|
</div>
|
|
648
792
|
))}
|
|
649
793
|
</div>
|
|
650
|
-
</section>
|
|
651
|
-
<section id="pricing" className="mx-auto max-w-md px-6 py-24 text-center">
|
|
652
|
-
<div className="rounded-3xl border border-hairline bg-surface p-10">
|
|
653
|
-
<h2 className="text-3xl font-bold">Pro</h2>
|
|
654
|
-
<p className="mt-2 text-5xl font-black gradient-text">$29<span className="text-lg text-faint">/mo</span></p>
|
|
655
|
-
<a href="#" className="mt-8 inline-block w-full gradient-bg rounded-full px-8 py-3 font-semibold text-black">Get started</a>
|
|
656
|
-
</div>
|
|
657
794
|
</section>`);
|
|
658
795
|
}
|
|
659
796
|
var glyph = (ctx) => (ctx.template.siteName.trim()[0] ?? "A").toUpperCase();
|
|
@@ -879,7 +1016,7 @@ var careersPage = (ctx) => {
|
|
|
879
1016
|
].map((j) => (
|
|
880
1017
|
<div key={j.role} className="flex flex-wrap items-center justify-between gap-3 rounded-2xl border border-hairline bg-surface p-5">
|
|
881
1018
|
<div><h3 className="font-semibold">{j.role}</h3><p className="text-sm text-muted">{j.team}</p></div>
|
|
882
|
-
<div className="flex items-center gap-3"><Badge>{j.type}</Badge><
|
|
1019
|
+
<div className="flex items-center gap-3"><Badge>{j.type}</Badge><Link href="/contact" className="rounded-full gradient-bg px-4 py-2 text-sm font-semibold on-accent">Apply</Link></div>
|
|
883
1020
|
</div>
|
|
884
1021
|
))}
|
|
885
1022
|
</div>
|
|
@@ -976,7 +1113,7 @@ var reservationsPage = (ctx) => {
|
|
|
976
1113
|
<div className="mx-auto max-w-2xl rounded-3xl border border-hairline bg-surface p-8 text-center">
|
|
977
1114
|
<h2 className="text-2xl font-bold">Book by phone or online</h2>
|
|
978
1115
|
<p className="mt-3 text-muted">Call us on <span className="font-semibold text-fg">+1 (555) 012-3456</span> or reserve online in seconds.</p>
|
|
979
|
-
<
|
|
1116
|
+
<Link href="/contact" className="mt-6 inline-block rounded-full gradient-bg px-8 py-3 font-semibold on-accent">Reserve a table</Link>
|
|
980
1117
|
</div>
|
|
981
1118
|
</Section>`,
|
|
982
1119
|
cta: { title: "Planning something special?", subtitle: "Ask us about private dining and set menus.", label: "Enquire now", href: "/contact" }
|
|
@@ -1154,7 +1291,7 @@ export function ContactForm() {
|
|
|
1154
1291
|
{err("_form") && <p className={errCls}>{err("_form")}</p>}
|
|
1155
1292
|
<button
|
|
1156
1293
|
disabled={pending}
|
|
1157
|
-
className="gradient-bg rounded-full px-8 py-3 font-semibold
|
|
1294
|
+
className="gradient-bg rounded-full px-8 py-3 font-semibold on-accent disabled:opacity-60"
|
|
1158
1295
|
>
|
|
1159
1296
|
{pending ? "Sending\u2026" : "Send message"}
|
|
1160
1297
|
</button>
|
|
@@ -1284,7 +1421,7 @@ export function DocsSidebar({ nav }: { nav: { group: string; items: DocMeta[] }[
|
|
|
1284
1421
|
<li key={d.slug}>
|
|
1285
1422
|
<Link
|
|
1286
1423
|
href={href}
|
|
1287
|
-
className={\`block rounded-lg px-3 py-1.5 transition \${active ? "gradient-bg font-semibold
|
|
1424
|
+
className={\`block rounded-lg px-3 py-1.5 transition \${active ? "gradient-bg font-semibold on-accent" : "text-muted hover:bg-surface hover:text-fg"}\`}
|
|
1288
1425
|
>
|
|
1289
1426
|
{d.title}
|
|
1290
1427
|
</Link>
|
|
@@ -1903,12 +2040,14 @@ import { useIsMounted } from "@lacspace/hooks";` : "";
|
|
|
1903
2040
|
<Link href="/cart" aria-label="Cart" className="relative inline-flex h-9 w-9 items-center justify-center rounded-full border border-hairline text-muted transition hover:text-fg">
|
|
1904
2041
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden><circle cx="9" cy="21" r="1" /><circle cx="20" cy="21" r="1" /><path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6" /></svg>
|
|
1905
2042
|
{mounted() && count > 0 ? (
|
|
1906
|
-
<span className="absolute -right-1 -top-1 inline-flex h-4 min-w-4 items-center justify-center rounded-full gradient-bg px-1 text-[10px] font-bold
|
|
2043
|
+
<span className="absolute -right-1 -top-1 inline-flex h-4 min-w-4 items-center justify-center rounded-full gradient-bg px-1 text-[10px] font-bold on-accent">{count}</span>
|
|
1907
2044
|
) : null}
|
|
1908
2045
|
</Link>` : "";
|
|
1909
2046
|
return `"use client";
|
|
1910
2047
|
|
|
2048
|
+
import { useEffect, useState } from "react";
|
|
1911
2049
|
import Link from "next/link";
|
|
2050
|
+
import { usePathname } from "next/navigation";
|
|
1912
2051
|
import { useUI } from "@/lib/store";
|
|
1913
2052
|
import { ThemeToggle } from "./theme-toggle";${cartImport}
|
|
1914
2053
|
|
|
@@ -1917,16 +2056,28 @@ const LINKS = [${linkLiteral(pagesFor(ctx).filter((p) => p.nav))}];
|
|
|
1917
2056
|
export function SiteHeader() {
|
|
1918
2057
|
const open = useUI((s) => s.navOpen);
|
|
1919
2058
|
const toggle = useUI((s) => s.toggleNav);
|
|
1920
|
-
const setOpen = useUI((s) => s.setNavOpen)
|
|
2059
|
+
const setOpen = useUI((s) => s.setNavOpen);
|
|
2060
|
+
const pathname = usePathname();${cartHook}
|
|
2061
|
+
// Sticky glass header that tightens and gains a hairline once you scroll.
|
|
2062
|
+
const [scrolled, setScrolled] = useState(false);
|
|
2063
|
+
useEffect(() => {
|
|
2064
|
+
const onScroll = () => setScrolled(window.scrollY > 8);
|
|
2065
|
+
onScroll();
|
|
2066
|
+
window.addEventListener("scroll", onScroll, { passive: true });
|
|
2067
|
+
return () => window.removeEventListener("scroll", onScroll);
|
|
2068
|
+
}, []);
|
|
1921
2069
|
return (
|
|
1922
|
-
<header className="sticky top-0 z-40 border-b border-
|
|
1923
|
-
<nav className="mx-auto flex max-w-6xl items-center justify-between px-6 py-
|
|
1924
|
-
<Link href="/" className="text-lg font-black gradient-text">${ctx.template.siteName}</Link>
|
|
1925
|
-
<div className="hidden items-center gap-
|
|
1926
|
-
{LINKS.map((l) =>
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
2070
|
+
<header className={"sticky top-0 z-40 transition-all duration-300 " + (scrolled ? "glass shadow-sm" : "border-b border-transparent bg-transparent")}>
|
|
2071
|
+
<nav className={"mx-auto flex max-w-6xl items-center justify-between px-6 transition-all duration-300 " + (scrolled ? "py-3" : "py-5")}>
|
|
2072
|
+
<Link href="/" className="text-lg font-black tracking-tight gradient-text">${ctx.template.siteName}</Link>
|
|
2073
|
+
<div className="hidden items-center gap-1 md:flex">
|
|
2074
|
+
{LINKS.map((l) => {
|
|
2075
|
+
const active = pathname === l.href;
|
|
2076
|
+
return (
|
|
2077
|
+
<Link key={l.href} href={l.href} className={"rounded-full px-3.5 py-1.5 text-sm font-medium transition " + (active ? "bg-surface text-fg" : "text-muted hover:bg-surface hover:text-fg")}>{l.label}</Link>
|
|
2078
|
+
);
|
|
2079
|
+
})}
|
|
2080
|
+
<div className="ml-2 flex items-center gap-2"><ThemeToggle />${cartBtn}</div>
|
|
1930
2081
|
</div>
|
|
1931
2082
|
<div className="flex items-center gap-2 md:hidden">
|
|
1932
2083
|
<ThemeToggle />${cartBtn}
|
|
@@ -1936,10 +2087,10 @@ export function SiteHeader() {
|
|
|
1936
2087
|
</div>
|
|
1937
2088
|
</nav>
|
|
1938
2089
|
{open ? (
|
|
1939
|
-
<div className="border-t border-hairline md:hidden">
|
|
2090
|
+
<div className="glass border-t border-hairline md:hidden">
|
|
1940
2091
|
<div className="mx-auto flex max-w-6xl flex-col gap-1 px-6 py-3">
|
|
1941
2092
|
{LINKS.map((l) => (
|
|
1942
|
-
<Link key={l.href} href={l.href} onClick={() => setOpen(false)} className="rounded-
|
|
2093
|
+
<Link key={l.href} href={l.href} onClick={() => setOpen(false)} className="rounded-xl px-3 py-2 text-muted transition hover:bg-surface hover:text-fg">{l.label}</Link>
|
|
1943
2094
|
))}
|
|
1944
2095
|
</div>
|
|
1945
2096
|
</div>
|
|
@@ -1954,22 +2105,32 @@ var siteFooter = (ctx) => {
|
|
|
1954
2105
|
const groups = ["Product", "Company", "Resources"].map((title) => ({ title, items: pages.filter((p) => p.group === title) })).filter((g) => g.items.length > 0);
|
|
1955
2106
|
const groupsLiteral = groups.map((g) => `{ title: ${JSON.stringify(g.title)}, links: [${linkLiteral(g.items)}] }`).join(", ");
|
|
1956
2107
|
return `import Link from "next/link";
|
|
2108
|
+
import { Newsletter } from "@/components/ui";
|
|
1957
2109
|
|
|
1958
2110
|
const GROUPS = [${groupsLiteral}];
|
|
1959
2111
|
|
|
1960
2112
|
export function SiteFooter() {
|
|
1961
2113
|
return (
|
|
1962
|
-
<footer className="border-t border-hairline">
|
|
1963
|
-
<div className="mx-auto max-w-6xl px-6
|
|
1964
|
-
|
|
2114
|
+
<footer className="relative mt-24 border-t border-hairline">
|
|
2115
|
+
<div className="mx-auto max-w-6xl px-6">
|
|
2116
|
+
{/* newsletter island \u2014 floats over the fold between page and footer */}
|
|
2117
|
+
<div className="-mt-16 mb-16">
|
|
2118
|
+
<Newsletter title="Stay in the loop" subtitle="Occasional updates from ${ctx.template.siteName}. No spam \u2014 unsubscribe anytime." />
|
|
2119
|
+
</div>
|
|
2120
|
+
<div className="grid gap-10 pb-4 md:grid-cols-[1.4fr_1fr_1fr_1fr]">
|
|
1965
2121
|
<div>
|
|
1966
|
-
<div className="text-lg font-black gradient-text">${ctx.template.siteName}</div>
|
|
1967
|
-
<p className="mt-3 max-w-xs text-sm text-muted">${ctx.template.siteDescription}</p>
|
|
2122
|
+
<div className="text-lg font-black tracking-tight gradient-text">${ctx.template.siteName}</div>
|
|
2123
|
+
<p className="mt-3 max-w-xs text-sm leading-relaxed text-muted">${ctx.template.siteDescription}</p>
|
|
2124
|
+
<div className="mt-5 flex gap-2">
|
|
2125
|
+
{["\u{1D54F}", "in", "gh"].map((s) => (
|
|
2126
|
+
<span key={s} className="inline-flex h-9 w-9 items-center justify-center rounded-full border border-hairline text-xs font-semibold text-muted transition hover:border-[color:var(--accent-to)] hover:text-fg">{s}</span>
|
|
2127
|
+
))}
|
|
2128
|
+
</div>
|
|
1968
2129
|
</div>
|
|
1969
2130
|
{GROUPS.map((g) => (
|
|
1970
2131
|
<div key={g.title}>
|
|
1971
|
-
<div className="text-
|
|
1972
|
-
<ul className="mt-
|
|
2132
|
+
<div className="text-xs font-semibold uppercase tracking-widest text-faint">{g.title}</div>
|
|
2133
|
+
<ul className="mt-4 space-y-2.5 text-sm text-muted">
|
|
1973
2134
|
{g.links.map((l) => (
|
|
1974
2135
|
<li key={l.href}><Link href={l.href} className="transition hover:text-fg">{l.label}</Link></li>
|
|
1975
2136
|
))}
|
|
@@ -1977,7 +2138,7 @@ export function SiteFooter() {
|
|
|
1977
2138
|
</div>
|
|
1978
2139
|
))}
|
|
1979
2140
|
</div>
|
|
1980
|
-
<div className="mt-12 flex flex-col gap-3 border-t border-hairline
|
|
2141
|
+
<div className="mt-12 flex flex-col gap-3 border-t border-hairline py-8 text-sm text-faint sm:flex-row sm:items-center sm:justify-between">
|
|
1981
2142
|
<span>\xA9 {new Date().getFullYear()} ${ctx.template.siteName}. All rights reserved.</span>
|
|
1982
2143
|
<span>Built with <a href="https://lacspace.com/packages" className="text-muted transition hover:text-fg">the Lacspace React Kit</a>.</span>
|
|
1983
2144
|
</div>
|
|
@@ -2001,10 +2162,10 @@ export function AnnouncementBar() {
|
|
|
2001
2162
|
if (!mounted() || dismissed) return null;
|
|
2002
2163
|
|
|
2003
2164
|
return (
|
|
2004
|
-
<div className="relative gradient-bg px-10 py-2 text-center text-sm font-medium
|
|
2165
|
+
<div className="relative gradient-bg px-10 py-2 text-center text-sm font-medium on-accent">
|
|
2005
2166
|
\u2728 Built with the Lacspace React Kit \u2014{" "}
|
|
2006
2167
|
<a href="https://lacspace.com/packages" className="underline underline-offset-2">explore the packages</a>
|
|
2007
|
-
<button type="button" aria-label="Dismiss" onClick={dismiss} className="absolute right-3 top-1/2 -translate-y-1/2 text-lg leading-none
|
|
2168
|
+
<button type="button" aria-label="Dismiss" onClick={dismiss} className="absolute right-3 top-1/2 -translate-y-1/2 text-lg leading-none on-accent/60 hover:on-accent">\xD7</button>
|
|
2008
2169
|
</div>
|
|
2009
2170
|
);
|
|
2010
2171
|
}
|
|
@@ -2025,7 +2186,7 @@ export function UnderDevelopment({ title = "This page", path }: { title?: string
|
|
|
2025
2186
|
<div className="h-full w-1/3 gradient-bg" style={{ animation: "loadbar 1.8s ease-in-out infinite" }} />
|
|
2026
2187
|
</div>
|
|
2027
2188
|
<div className="mt-10 flex flex-wrap items-center justify-center gap-3">
|
|
2028
|
-
<Link href="/" className="gradient-bg rounded-full px-6 py-3 font-semibold
|
|
2189
|
+
<Link href="/" className="gradient-bg rounded-full px-6 py-3 font-semibold on-accent">Back home</Link>
|
|
2029
2190
|
<Link href="/contact" className="rounded-full border border-hairline px-6 py-3 font-semibold transition hover:bg-surface">Get in touch</Link>
|
|
2030
2191
|
</div>
|
|
2031
2192
|
{path ? (
|
|
@@ -2187,29 +2348,48 @@ var builtWithSection = (ctx) => {
|
|
|
2187
2348
|
};
|
|
2188
2349
|
var dashboardShell = (ctx) => {
|
|
2189
2350
|
const nav = [{ path: "/", label: "Overview" }, ...pagesFor(ctx).filter((p) => p.nav)];
|
|
2190
|
-
return `
|
|
2351
|
+
return `"use client";
|
|
2352
|
+
import Link from "next/link";
|
|
2353
|
+
import { usePathname } from "next/navigation";
|
|
2191
2354
|
import type { ReactNode } from "react";
|
|
2192
2355
|
import { ThemeToggle } from "./theme-toggle";
|
|
2193
2356
|
|
|
2194
2357
|
const NAV = [${linkLiteral(nav)}];
|
|
2195
2358
|
|
|
2196
2359
|
export function DashboardShell({ title, subtitle, children }: { title: string; subtitle?: string; children: ReactNode }) {
|
|
2360
|
+
const pathname = usePathname();
|
|
2197
2361
|
return (
|
|
2198
2362
|
<div className="flex min-h-screen">
|
|
2199
|
-
<aside className="hidden w-
|
|
2200
|
-
<div className="mb-8 flex items-center justify-between">
|
|
2201
|
-
<Link href="/" className="text-lg font-black gradient-text">${ctx.template.siteName}</Link>
|
|
2363
|
+
<aside className="sticky top-0 hidden h-screen w-60 shrink-0 flex-col border-r border-hairline bg-surface/40 p-5 md:flex">
|
|
2364
|
+
<div className="mb-8 flex items-center justify-between px-2">
|
|
2365
|
+
<Link href="/" className="text-lg font-black tracking-tight gradient-text">${ctx.template.siteName}</Link>
|
|
2202
2366
|
<ThemeToggle />
|
|
2203
2367
|
</div>
|
|
2204
|
-
<nav className="space-y-1 text-sm
|
|
2205
|
-
{NAV.map((l) =>
|
|
2206
|
-
|
|
2207
|
-
|
|
2368
|
+
<nav className="flex-1 space-y-1 text-sm">
|
|
2369
|
+
{NAV.map((l) => {
|
|
2370
|
+
const active = pathname === l.href;
|
|
2371
|
+
return (
|
|
2372
|
+
<Link key={l.href} href={l.href} className={"flex items-center gap-2 rounded-xl px-3 py-2 font-medium transition " + (active ? "gradient-bg on-accent shadow-sm" : "text-muted hover:bg-surface hover:text-fg")}>
|
|
2373
|
+
<span aria-hidden className={"h-1.5 w-1.5 rounded-full " + (active ? "bg-[color:var(--on-accent)]" : "bg-current opacity-40")} />
|
|
2374
|
+
{l.label}
|
|
2375
|
+
</Link>
|
|
2376
|
+
);
|
|
2377
|
+
})}
|
|
2208
2378
|
</nav>
|
|
2379
|
+
<div className="mt-4 rounded-2xl border border-hairline p-4 text-xs text-muted">
|
|
2380
|
+
<p className="font-semibold text-fg">Need a hand?</p>
|
|
2381
|
+
<p className="mt-1">Read the docs or reach the team.</p>
|
|
2382
|
+
<Link href="/help" className="mt-3 inline-block rounded-full gradient-bg px-3 py-1.5 font-semibold on-accent">Get help</Link>
|
|
2383
|
+
</div>
|
|
2209
2384
|
</aside>
|
|
2210
|
-
<main className="flex-1 p-6 md:p-10">
|
|
2211
|
-
<
|
|
2212
|
-
|
|
2385
|
+
<main className="min-w-0 flex-1 p-6 md:p-10">
|
|
2386
|
+
<div className="flex flex-wrap items-end justify-between gap-4">
|
|
2387
|
+
<div>
|
|
2388
|
+
<h1 className="text-2xl font-bold tracking-tight sm:text-3xl">{title}</h1>
|
|
2389
|
+
{subtitle ? <p className="mt-1.5 text-muted">{subtitle}</p> : null}
|
|
2390
|
+
</div>
|
|
2391
|
+
<div className="md:hidden"><ThemeToggle /></div>
|
|
2392
|
+
</div>
|
|
2213
2393
|
<div className="mt-8">{children}</div>
|
|
2214
2394
|
</main>
|
|
2215
2395
|
</div>
|
|
@@ -2234,19 +2414,25 @@ export default function Home() {
|
|
|
2234
2414
|
</div>
|
|
2235
2415
|
<div className="mt-6 grid gap-6 lg:grid-cols-3">
|
|
2236
2416
|
<div className="rounded-2xl border border-hairline bg-surface p-6 lg:col-span-2">
|
|
2237
|
-
<div className="mb-
|
|
2238
|
-
<h2 className="font-semibold">Revenue</h2>
|
|
2239
|
-
<span className="text-
|
|
2417
|
+
<div className="mb-5 flex items-center justify-between">
|
|
2418
|
+
<div><h2 className="font-semibold tracking-tight">Revenue</h2><p className="text-sm text-muted">Last 12 months</p></div>
|
|
2419
|
+
<span className="rounded-full bg-emerald-500/10 px-2.5 py-1 text-xs font-semibold text-emerald-400">\u25B2 12.4%</span>
|
|
2240
2420
|
</div>
|
|
2241
2421
|
<AreaChart data={[12, 18, 15, 22, 20, 28, 26, 34, 30, 38, 42, 48]} />
|
|
2242
2422
|
</div>
|
|
2243
2423
|
<div className="rounded-2xl border border-hairline bg-surface p-6">
|
|
2244
|
-
<div className="mb-
|
|
2245
|
-
<div className="space-y-
|
|
2246
|
-
{[
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2424
|
+
<div className="mb-5 font-semibold tracking-tight">Recent activity</div>
|
|
2425
|
+
<div className="space-y-4">
|
|
2426
|
+
{[
|
|
2427
|
+
{ who: "Ava Chen", what: "upgraded to Pro", when: "2m ago" },
|
|
2428
|
+
{ who: "Order #4821", what: "was paid", when: "18m ago" },
|
|
2429
|
+
{ who: "Liam Patel", what: "started a trial", when: "1h ago" },
|
|
2430
|
+
{ who: "Refund #204", what: "was issued", when: "3h ago" },
|
|
2431
|
+
].map((e) => (
|
|
2432
|
+
<div key={e.who} className="flex items-center gap-3 text-sm">
|
|
2433
|
+
<span className="inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-full gradient-bg text-xs font-bold on-accent">{e.who.slice(0, 1)}</span>
|
|
2434
|
+
<span className="min-w-0 flex-1 truncate text-muted"><span className="font-medium text-fg">{e.who}</span> {e.what}</span>
|
|
2435
|
+
<span className="shrink-0 text-faint">{e.when}</span>
|
|
2250
2436
|
</div>
|
|
2251
2437
|
))}
|
|
2252
2438
|
</div>
|
|
@@ -2293,7 +2479,7 @@ var pricingPage = (ctx) => pageFile({
|
|
|
2293
2479
|
{ name: "Scale", price: "Custom", period: "", tagline: "For organizations", features: ["SSO & SAML", "Dedicated support", "SLA & audit logs", "Guided onboarding"], cta: "Contact sales", highlight: false },
|
|
2294
2480
|
].map((tier) => (
|
|
2295
2481
|
<div key={tier.name} className={"flex flex-col rounded-3xl border p-8 " + (tier.highlight ? "border-transparent bg-surface ring-2 ring-[color:var(--accent-to)]" : "border-hairline bg-surface")}>
|
|
2296
|
-
{tier.highlight ? <span className="mb-4 inline-block w-fit rounded-full gradient-bg px-3 py-1 text-xs font-bold
|
|
2482
|
+
{tier.highlight ? <span className="mb-4 inline-block w-fit rounded-full gradient-bg px-3 py-1 text-xs font-bold on-accent">Most popular</span> : null}
|
|
2297
2483
|
<h2 className="text-lg font-semibold">{tier.name}</h2>
|
|
2298
2484
|
<p className="mt-1 text-sm text-muted">{tier.tagline}</p>
|
|
2299
2485
|
<div className="mt-6 flex items-baseline gap-1">
|
|
@@ -2305,7 +2491,7 @@ var pricingPage = (ctx) => pageFile({
|
|
|
2305
2491
|
<li key={f} className="flex items-center gap-2 text-muted"><span className="text-[color:var(--accent-to)]">\u2713</span> {f}</li>
|
|
2306
2492
|
))}
|
|
2307
2493
|
</ul>
|
|
2308
|
-
<Link href="/contact" className={"mt-8 rounded-full px-6 py-3 text-center font-semibold transition " + (tier.highlight ? "gradient-bg
|
|
2494
|
+
<Link href="/contact" className={"mt-8 rounded-full px-6 py-3 text-center font-semibold transition " + (tier.highlight ? "gradient-bg on-accent" : "border border-hairline hover:bg-app")}>{tier.cta}</Link>
|
|
2309
2495
|
</div>
|
|
2310
2496
|
))}
|
|
2311
2497
|
</div>
|
|
@@ -2602,7 +2788,7 @@ export function ProductGrid() {
|
|
|
2602
2788
|
<div className="mb-4 flex aspect-square items-center justify-center rounded-xl gradient-bg text-5xl">{p.emoji}</div>
|
|
2603
2789
|
<h3 className="font-semibold">{p.name}</h3>
|
|
2604
2790
|
<div className="mt-1 text-muted">{"$" + p.price.toFixed(2)}</div>
|
|
2605
|
-
<button type="button" onClick={() => add({ id: p.id, name: p.name, price: p.price })} className="mt-4 rounded-full gradient-bg px-4 py-2 text-sm font-semibold
|
|
2791
|
+
<button type="button" onClick={() => add({ id: p.id, name: p.name, price: p.price })} className="mt-4 rounded-full gradient-bg px-4 py-2 text-sm font-semibold on-accent transition hover:opacity-90">Add to bag</button>
|
|
2606
2792
|
</div>
|
|
2607
2793
|
))}
|
|
2608
2794
|
</div>
|
|
@@ -2627,7 +2813,7 @@ export function CartView() {
|
|
|
2627
2813
|
return (
|
|
2628
2814
|
<div className="mx-auto max-w-md py-16 text-center">
|
|
2629
2815
|
<p className="text-lg text-muted">Your bag is empty.</p>
|
|
2630
|
-
<Link href="/shop" className="mt-6 inline-block rounded-full gradient-bg px-6 py-3 font-semibold
|
|
2816
|
+
<Link href="/shop" className="mt-6 inline-block rounded-full gradient-bg px-6 py-3 font-semibold on-accent">Browse the shop</Link>
|
|
2631
2817
|
</div>
|
|
2632
2818
|
);
|
|
2633
2819
|
}
|
|
@@ -2651,7 +2837,7 @@ export function CartView() {
|
|
|
2651
2837
|
<button type="button" onClick={clear} className="text-sm text-muted transition hover:text-fg">Clear bag</button>
|
|
2652
2838
|
<span className="text-lg font-bold">Total {"$" + total.toFixed(2)}</span>
|
|
2653
2839
|
</div>
|
|
2654
|
-
<button type="button" className="mt-8 w-full rounded-full gradient-bg px-6 py-3 font-semibold
|
|
2840
|
+
<button type="button" className="mt-8 w-full rounded-full gradient-bg px-6 py-3 font-semibold on-accent">Checkout</button>
|
|
2655
2841
|
</div>
|
|
2656
2842
|
);
|
|
2657
2843
|
}
|
|
@@ -2878,7 +3064,7 @@ export function CartButton() {
|
|
|
2878
3064
|
<Link href="/cart" aria-label="Cart" className="relative inline-flex h-9 w-9 items-center justify-center rounded-full border border-hairline text-muted transition hover:text-fg">
|
|
2879
3065
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden><circle cx="9" cy="21" r="1" /><circle cx="20" cy="21" r="1" /><path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6" /></svg>
|
|
2880
3066
|
{mounted() && count > 0 ? (
|
|
2881
|
-
<span className="absolute -right-1 -top-1 inline-flex h-4 min-w-4 items-center justify-center rounded-full gradient-bg px-1 text-[10px] font-bold
|
|
3067
|
+
<span className="absolute -right-1 -top-1 inline-flex h-4 min-w-4 items-center justify-center rounded-full gradient-bg px-1 text-[10px] font-bold on-accent">{count}</span>
|
|
2882
3068
|
) : null}
|
|
2883
3069
|
</Link>
|
|
2884
3070
|
);
|
|
@@ -2914,7 +3100,7 @@ export function AddToCart({ product, className = "" }: { product: AddToCartProdu
|
|
|
2914
3100
|
setAdded(true);
|
|
2915
3101
|
window.setTimeout(() => setAdded(false), 1200);
|
|
2916
3102
|
}}
|
|
2917
|
-
className={"rounded-full gradient-bg px-4 py-2 text-sm font-semibold
|
|
3103
|
+
className={"rounded-full gradient-bg px-4 py-2.5 text-sm font-semibold on-accent transition hover:-translate-y-0.5 " + className}
|
|
2918
3104
|
>
|
|
2919
3105
|
{added ? "Added \\u2713" : "Add to cart"}
|
|
2920
3106
|
</button>
|
|
@@ -2923,7 +3109,9 @@ export function AddToCart({ product, className = "" }: { product: AddToCartProdu
|
|
|
2923
3109
|
`;
|
|
2924
3110
|
var marketHeader = (ctx) => `"use client";
|
|
2925
3111
|
|
|
3112
|
+
import { useEffect, useState } from "react";
|
|
2926
3113
|
import Link from "next/link";
|
|
3114
|
+
import { usePathname } from "next/navigation";
|
|
2927
3115
|
import { useUI } from "@/lib/store";
|
|
2928
3116
|
import { ThemeToggle } from "./theme-toggle";
|
|
2929
3117
|
import { CartButton } from "./cart-button";
|
|
@@ -2934,16 +3122,26 @@ export function SiteHeader() {
|
|
|
2934
3122
|
const open = useUI((s) => s.navOpen);
|
|
2935
3123
|
const toggle = useUI((s) => s.toggleNav);
|
|
2936
3124
|
const setOpen = useUI((s) => s.setNavOpen);
|
|
3125
|
+
const pathname = usePathname();
|
|
3126
|
+
const [scrolled, setScrolled] = useState(false);
|
|
3127
|
+
useEffect(() => {
|
|
3128
|
+
const onScroll = () => setScrolled(window.scrollY > 8);
|
|
3129
|
+
onScroll();
|
|
3130
|
+
window.addEventListener("scroll", onScroll, { passive: true });
|
|
3131
|
+
return () => window.removeEventListener("scroll", onScroll);
|
|
3132
|
+
}, []);
|
|
2937
3133
|
return (
|
|
2938
|
-
<header className="sticky top-0 z-40 border-b border-
|
|
2939
|
-
<nav className="mx-auto flex max-w-6xl items-center justify-between px-6 py-
|
|
2940
|
-
<Link href="/" className="text-lg font-black gradient-text">${ctx.template.siteName}</Link>
|
|
2941
|
-
<div className="hidden items-center gap-
|
|
2942
|
-
{LINKS.map((l) =>
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
3134
|
+
<header className={"sticky top-0 z-40 transition-all duration-300 " + (scrolled ? "glass shadow-sm" : "border-b border-transparent bg-transparent")}>
|
|
3135
|
+
<nav className={"mx-auto flex max-w-6xl items-center justify-between px-6 transition-all duration-300 " + (scrolled ? "py-3" : "py-5")}>
|
|
3136
|
+
<Link href="/" className="text-lg font-black tracking-tight gradient-text">${ctx.template.siteName}</Link>
|
|
3137
|
+
<div className="hidden items-center gap-1 md:flex">
|
|
3138
|
+
{LINKS.map((l) => {
|
|
3139
|
+
const active = pathname === l.href;
|
|
3140
|
+
return (
|
|
3141
|
+
<Link key={l.href} href={l.href} className={"rounded-full px-3.5 py-1.5 text-sm font-medium transition " + (active ? "bg-surface text-fg" : "text-muted hover:bg-surface hover:text-fg")}>{l.label}</Link>
|
|
3142
|
+
);
|
|
3143
|
+
})}
|
|
3144
|
+
<div className="ml-2 flex items-center gap-2"><ThemeToggle /><CartButton /></div>
|
|
2947
3145
|
</div>
|
|
2948
3146
|
<div className="flex items-center gap-2 md:hidden">
|
|
2949
3147
|
<ThemeToggle />
|
|
@@ -2954,10 +3152,10 @@ export function SiteHeader() {
|
|
|
2954
3152
|
</div>
|
|
2955
3153
|
</nav>
|
|
2956
3154
|
{open ? (
|
|
2957
|
-
<div className="border-t border-hairline md:hidden">
|
|
3155
|
+
<div className="glass border-t border-hairline md:hidden">
|
|
2958
3156
|
<div className="mx-auto flex max-w-6xl flex-col gap-1 px-6 py-3">
|
|
2959
3157
|
{LINKS.map((l) => (
|
|
2960
|
-
<Link key={l.href} href={l.href} onClick={() => setOpen(false)} className="rounded-
|
|
3158
|
+
<Link key={l.href} href={l.href} onClick={() => setOpen(false)} className="rounded-xl px-3 py-2 text-muted transition hover:bg-surface hover:text-fg">{l.label}</Link>
|
|
2961
3159
|
))}
|
|
2962
3160
|
</div>
|
|
2963
3161
|
</div>
|
|
@@ -2984,30 +3182,30 @@ export default function Home() {
|
|
|
2984
3182
|
return (
|
|
2985
3183
|
<main className="relative min-h-screen overflow-hidden">
|
|
2986
3184
|
<Aurora />
|
|
2987
|
-
<section className="mx-auto grid max-w-6xl items-center gap-12 px-6 py-
|
|
3185
|
+
<section className="mx-auto grid max-w-6xl items-center gap-12 px-6 py-28 md:grid-cols-2">
|
|
2988
3186
|
<div>
|
|
2989
|
-
<
|
|
2990
|
-
<h1 className="text-
|
|
2991
|
-
<p className="mt-6 max-w-md
|
|
2992
|
-
<div className="mt-
|
|
2993
|
-
<Link href="/shop" className="
|
|
2994
|
-
<Link href="/cart" className="rounded-full border border-hairline px-8 py-3 font-semibold hover:bg-surface">View cart</Link>
|
|
3187
|
+
<span className="glass inline-flex items-center gap-2 rounded-full px-4 py-1.5 text-xs font-semibold text-muted">\u{1F6D2} Cart to checkout, wired end to end</span>
|
|
3188
|
+
<h1 className="text-display mt-6">Shop <span className="gradient-text">${n}</span></h1>
|
|
3189
|
+
<p className="lead mt-6 max-w-md">${ctx.template.siteDescription}</p>
|
|
3190
|
+
<div className="mt-9 flex flex-wrap gap-3">
|
|
3191
|
+
<Link href="/shop" className="shimmer rounded-full gradient-bg px-8 py-3.5 font-semibold on-accent transition hover:-translate-y-0.5">Shop the collection</Link>
|
|
3192
|
+
<Link href="/cart" className="rounded-full border border-hairline px-8 py-3.5 font-semibold transition hover:bg-surface">View cart</Link>
|
|
2995
3193
|
</div>
|
|
2996
|
-
<p className="mt-6 text-sm text-muted">Free standard shipping over {formatMoney(500000)}.</p>
|
|
3194
|
+
<p className="mt-6 text-sm text-muted">Free standard shipping over {formatMoney(500000)} \xB7 eSewa & Khalti at checkout.</p>
|
|
2997
3195
|
</div>
|
|
2998
3196
|
<HeroArt />
|
|
2999
3197
|
</section>
|
|
3000
3198
|
<section className="mx-auto max-w-6xl px-6 py-16">
|
|
3001
|
-
<div className="mb-
|
|
3002
|
-
<h2 className="text-3xl font-bold">
|
|
3003
|
-
<Link href="/shop" className="text-sm text-muted underline
|
|
3199
|
+
<div className="mb-12 flex items-end justify-between">
|
|
3200
|
+
<div><p className="text-sm font-semibold uppercase tracking-widest gradient-text">Featured</p><h2 className="mt-3 text-3xl font-bold sm:text-4xl">Fresh in the store</h2></div>
|
|
3201
|
+
<Link href="/shop" className="hidden text-sm text-muted underline-offset-4 transition hover:text-fg hover:underline sm:block">See all \u2192</Link>
|
|
3004
3202
|
</div>
|
|
3005
3203
|
<div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
|
|
3006
3204
|
{featured.map((p) => (
|
|
3007
|
-
<div key={p.slug} className="group flex flex-col
|
|
3008
|
-
<Link href={"/product/" + p.slug} className="mb-
|
|
3009
|
-
<Link href={"/product/" + p.slug} className="font-semibold group-hover:
|
|
3010
|
-
<div className="mt-1 text-muted">{formatMoney(p.price)}</div>
|
|
3205
|
+
<div key={p.slug} className="card group flex flex-col p-5">
|
|
3206
|
+
<Link href={"/product/" + p.slug} className="mb-5 flex aspect-square items-center justify-center overflow-hidden rounded-xl gradient-bg text-7xl on-accent transition group-hover:scale-[1.03]">{p.emoji}</Link>
|
|
3207
|
+
<Link href={"/product/" + p.slug} className="font-semibold tracking-tight transition group-hover:text-[color:var(--accent-to)]">{p.name}</Link>
|
|
3208
|
+
<div className="mt-1 font-semibold text-muted">{formatMoney(p.price)}</div>
|
|
3011
3209
|
<div className="mt-4">
|
|
3012
3210
|
<AddToCart product={{ id: p.slug, name: p.name, unitPrice: p.price, weight: p.weight, emoji: p.emoji }} className="w-full" />
|
|
3013
3211
|
</div>
|
|
@@ -3119,7 +3317,7 @@ export default function Page() {
|
|
|
3119
3317
|
<main className="mx-auto min-h-screen max-w-3xl px-6 py-24 text-center">
|
|
3120
3318
|
<h1 className="text-4xl font-bold">Your <span className="gradient-text">cart</span></h1>
|
|
3121
3319
|
<p className="mt-4 text-lg text-muted">It's empty for now.</p>
|
|
3122
|
-
<Link href="/shop" className="mt-8 inline-block rounded-full gradient-bg px-6 py-3 font-semibold
|
|
3320
|
+
<Link href="/shop" className="mt-8 inline-block rounded-full gradient-bg px-6 py-3 font-semibold on-accent">Browse the shop</Link>
|
|
3123
3321
|
</main>
|
|
3124
3322
|
);
|
|
3125
3323
|
}
|
|
@@ -3178,7 +3376,7 @@ export default function Page() {
|
|
|
3178
3376
|
{q.freeShippingRemaining > 0 ? (
|
|
3179
3377
|
<p className="mt-4 text-xs text-muted">Add {formatMoney(q.freeShippingRemaining)} more for free shipping.</p>
|
|
3180
3378
|
) : null}
|
|
3181
|
-
<Link href="/checkout" className="mt-6 block w-full rounded-full gradient-bg px-6 py-3 text-center font-semibold
|
|
3379
|
+
<Link href="/checkout" className="mt-6 block w-full rounded-full gradient-bg px-6 py-3 text-center font-semibold on-accent">Checkout</Link>
|
|
3182
3380
|
<button type="button" onClick={clear} className="mt-3 block w-full text-center text-sm text-muted transition hover:text-fg">Clear cart</button>
|
|
3183
3381
|
</aside>
|
|
3184
3382
|
</div>
|
|
@@ -3218,7 +3416,7 @@ export default function Page() {
|
|
|
3218
3416
|
<main className="mx-auto min-h-screen max-w-3xl px-6 py-24 text-center">
|
|
3219
3417
|
<h1 className="text-4xl font-bold">Checkout</h1>
|
|
3220
3418
|
<p className="mt-4 text-lg text-muted">Your cart is empty.</p>
|
|
3221
|
-
<Link href="/shop" className="mt-8 inline-block rounded-full gradient-bg px-6 py-3 font-semibold
|
|
3419
|
+
<Link href="/shop" className="mt-8 inline-block rounded-full gradient-bg px-6 py-3 font-semibold on-accent">Browse the shop</Link>
|
|
3222
3420
|
</main>
|
|
3223
3421
|
);
|
|
3224
3422
|
}
|
|
@@ -3341,7 +3539,7 @@ export default function Page() {
|
|
|
3341
3539
|
<div className="flex justify-between text-muted"><dt>Shipping</dt><dd className="tabular-nums">{q.shipping === 0 ? "Free" : formatMoney(q.shipping)}</dd></div>
|
|
3342
3540
|
<div className="flex justify-between border-t border-hairline pt-2 text-base font-bold"><dt>Total</dt><dd className="tabular-nums">{formatMoney(q.total)}</dd></div>
|
|
3343
3541
|
</dl>
|
|
3344
|
-
<button type="submit" disabled={busy} className="mt-6 block w-full rounded-full gradient-bg px-6 py-3 text-center font-semibold
|
|
3542
|
+
<button type="submit" disabled={busy} className="mt-6 block w-full rounded-full gradient-bg px-6 py-3 text-center font-semibold on-accent transition hover:opacity-90 disabled:opacity-60">
|
|
3345
3543
|
{busy ? "Placing order\\u2026" : "Pay " + formatMoney(q.total)}
|
|
3346
3544
|
</button>
|
|
3347
3545
|
</aside>
|
|
@@ -3363,7 +3561,7 @@ export default async function Page({ searchParams }: { searchParams: Promise<{ o
|
|
|
3363
3561
|
const mock = sp.mock === "1";
|
|
3364
3562
|
return (
|
|
3365
3563
|
<main className="mx-auto flex min-h-screen max-w-2xl flex-col items-center justify-center px-6 py-24 text-center">
|
|
3366
|
-
<div className="flex h-20 w-20 items-center justify-center rounded-full gradient-bg text-4xl
|
|
3564
|
+
<div className="flex h-20 w-20 items-center justify-center rounded-full gradient-bg text-4xl on-accent">\u2713</div>
|
|
3367
3565
|
<h1 className="mt-8 text-4xl font-bold">Thank you!</h1>
|
|
3368
3566
|
<p className="mt-4 text-lg text-muted">Your order with ${ctx.template.siteName} is confirmed.</p>
|
|
3369
3567
|
{order ? (
|
|
@@ -3371,7 +3569,7 @@ export default async function Page({ searchParams }: { searchParams: Promise<{ o
|
|
|
3371
3569
|
) : null}
|
|
3372
3570
|
{method ? <p className="mt-3 text-sm text-muted">Payment method: {method}</p> : null}
|
|
3373
3571
|
{mock ? <p className="mt-3 max-w-md text-sm text-muted">This Khalti payment ran in mock mode. Set KHALTI_SECRET in your environment to hit the real gateway.</p> : null}
|
|
3374
|
-
<Link href="/shop" className="mt-10 inline-block rounded-full gradient-bg px-6 py-3 font-semibold
|
|
3572
|
+
<Link href="/shop" className="mt-10 inline-block rounded-full gradient-bg px-6 py-3 font-semibold on-accent">Continue shopping</Link>
|
|
3375
3573
|
</main>
|
|
3376
3574
|
);
|
|
3377
3575
|
}
|
|
@@ -3543,7 +3741,7 @@ export function SettingsPanel() {
|
|
|
3543
3741
|
<p className="mt-1 text-sm text-muted">Theme via @lacspace/theme.</p>
|
|
3544
3742
|
<div className="mt-4 inline-flex rounded-full border border-hairline p-1">
|
|
3545
3743
|
{["light", "dark", "system"].map((t) => (
|
|
3546
|
-
<button key={t} type="button" onClick={() => setTheme(t)} className={"rounded-full px-4 py-1.5 text-sm capitalize transition " + (theme === t ? "gradient-bg font-semibold
|
|
3744
|
+
<button key={t} type="button" onClick={() => setTheme(t)} className={"rounded-full px-4 py-1.5 text-sm capitalize transition " + (theme === t ? "gradient-bg font-semibold on-accent" : "text-muted hover:text-fg")}>{t}</button>
|
|
3547
3745
|
))}
|
|
3548
3746
|
</div>
|
|
3549
3747
|
</section>
|
|
@@ -3556,11 +3754,11 @@ var uiKitFiles = () => ({
|
|
|
3556
3754
|
|
|
3557
3755
|
export function Section({ eyebrow, title, lead, children, className = "" }: { eyebrow?: string; title?: string; lead?: string; children?: ReactNode; className?: string }) {
|
|
3558
3756
|
return (
|
|
3559
|
-
<section className={"mx-auto max-w-6xl px-6 py-
|
|
3560
|
-
{eyebrow ? <p className="text-sm font-semibold uppercase tracking-widest text
|
|
3757
|
+
<section className={"mx-auto max-w-6xl px-6 py-20 sm:py-24 " + className}>
|
|
3758
|
+
{eyebrow ? <p className="text-sm font-semibold uppercase tracking-widest gradient-text">{eyebrow}</p> : null}
|
|
3561
3759
|
{title ? <h2 className="mt-3 text-3xl font-bold sm:text-4xl">{title}</h2> : null}
|
|
3562
3760
|
{lead ? <p className="mt-4 max-w-2xl text-lg text-muted">{lead}</p> : null}
|
|
3563
|
-
{children ? <div className="mt-
|
|
3761
|
+
{children ? <div className="mt-12">{children}</div> : null}
|
|
3564
3762
|
</section>
|
|
3565
3763
|
);
|
|
3566
3764
|
}
|
|
@@ -3568,15 +3766,16 @@ export function Section({ eyebrow, title, lead, children, className = "" }: { ey
|
|
|
3568
3766
|
"components/ui/pill.tsx": `import type { ReactNode } from "react";
|
|
3569
3767
|
|
|
3570
3768
|
export function Pill({ children, className = "" }: { children: ReactNode; className?: string }) {
|
|
3571
|
-
return <span className={"inline-flex items-center gap-1 rounded-full
|
|
3769
|
+
return <span className={"glass inline-flex items-center gap-1.5 rounded-full px-3.5 py-1.5 text-xs font-semibold uppercase tracking-widest text-muted " + className}><span aria-hidden className="h-1.5 w-1.5 rounded-full gradient-bg" />{children}</span>;
|
|
3572
3770
|
}
|
|
3573
3771
|
`,
|
|
3574
3772
|
"components/ui/stat-card.tsx": `export function StatCard({ label, value, delta }: { label: string; value: string; delta?: string }) {
|
|
3773
|
+
const down = delta?.trim().startsWith("-");
|
|
3575
3774
|
return (
|
|
3576
|
-
<div className="
|
|
3577
|
-
<div className="text-3xl font-bold tabular-nums">{value}</div>
|
|
3775
|
+
<div className="card p-6">
|
|
3776
|
+
<div className="text-3xl font-bold tabular-nums tracking-tight">{value}</div>
|
|
3578
3777
|
<div className="mt-1 text-sm text-muted">{label}</div>
|
|
3579
|
-
{delta ? <div className="mt-2 text-xs font-
|
|
3778
|
+
{delta ? <div className={"mt-3 inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-xs font-semibold " + (down ? "bg-red-500/10 text-red-400" : "bg-emerald-500/10 text-emerald-400")}>{down ? "\u25BC" : "\u25B2"} {delta}</div> : null}
|
|
3580
3779
|
</div>
|
|
3581
3780
|
);
|
|
3582
3781
|
}
|
|
@@ -3585,20 +3784,24 @@ export function Pill({ children, className = "" }: { children: ReactNode; classN
|
|
|
3585
3784
|
|
|
3586
3785
|
export function FeatureCard({ icon, title, desc }: { icon?: ReactNode; title: string; desc: string }) {
|
|
3587
3786
|
return (
|
|
3588
|
-
<div className="
|
|
3589
|
-
{icon ? <div className="mb-
|
|
3590
|
-
<h3 className="font-semibold">{title}</h3>
|
|
3591
|
-
<p className="mt-1 text-sm leading-relaxed text-muted">{desc}</p>
|
|
3787
|
+
<div className="card group p-7">
|
|
3788
|
+
{icon ? <div className="mb-5 inline-flex h-12 w-12 items-center justify-center rounded-2xl gradient-bg text-2xl accent-glow transition group-hover:scale-105">{icon}</div> : null}
|
|
3789
|
+
<h3 className="font-semibold tracking-tight">{title}</h3>
|
|
3790
|
+
<p className="mt-1.5 text-sm leading-relaxed text-muted">{desc}</p>
|
|
3592
3791
|
</div>
|
|
3593
3792
|
);
|
|
3594
3793
|
}
|
|
3595
3794
|
`,
|
|
3596
3795
|
"components/ui/testimonial.tsx": `export function Testimonial({ quote, author, role }: { quote: string; author: string; role?: string }) {
|
|
3796
|
+
const initials = author.split(" ").map((x) => x[0]).join("").slice(0, 2).toUpperCase();
|
|
3597
3797
|
return (
|
|
3598
|
-
<figure className="
|
|
3599
|
-
<div className="text-
|
|
3600
|
-
<blockquote className="mt-
|
|
3601
|
-
<figcaption className="mt-
|
|
3798
|
+
<figure className="card flex h-full flex-col p-7">
|
|
3799
|
+
<div aria-hidden className="text-3xl leading-none gradient-text">“</div>
|
|
3800
|
+
<blockquote className="mt-1 flex-1 text-[15px] font-medium leading-relaxed text-fg/90">{quote}</blockquote>
|
|
3801
|
+
<figcaption className="mt-6 flex items-center gap-3 border-t border-hairline pt-5">
|
|
3802
|
+
<span className="inline-flex h-10 w-10 items-center justify-center rounded-full gradient-bg text-sm font-bold on-accent">{initials}</span>
|
|
3803
|
+
<span className="text-sm"><span className="block font-semibold text-fg">{author}</span>{role ? <span className="block text-muted">{role}</span> : null}</span>
|
|
3804
|
+
</figcaption>
|
|
3602
3805
|
</figure>
|
|
3603
3806
|
);
|
|
3604
3807
|
}
|
|
@@ -3607,10 +3810,10 @@ export function FeatureCard({ icon, title, desc }: { icon?: ReactNode; title: st
|
|
|
3607
3810
|
return (
|
|
3608
3811
|
<ol className="grid gap-6 sm:grid-cols-3">
|
|
3609
3812
|
{items.map((s, i) => (
|
|
3610
|
-
<li key={s.title} className="
|
|
3611
|
-
<div className="mb-
|
|
3612
|
-
<h3 className="font-semibold">{s.title}</h3>
|
|
3613
|
-
<p className="mt-1 text-sm text-muted">{s.desc}</p>
|
|
3813
|
+
<li key={s.title} className="card p-7">
|
|
3814
|
+
<div className="mb-4 inline-flex h-9 w-9 items-center justify-center rounded-xl gradient-bg text-sm font-bold on-accent">{String(i + 1).padStart(2, "0")}</div>
|
|
3815
|
+
<h3 className="font-semibold tracking-tight">{s.title}</h3>
|
|
3816
|
+
<p className="mt-1.5 text-sm leading-relaxed text-muted">{s.desc}</p>
|
|
3614
3817
|
</li>
|
|
3615
3818
|
))}
|
|
3616
3819
|
</ol>
|
|
@@ -3621,13 +3824,14 @@ export function FeatureCard({ icon, title, desc }: { icon?: ReactNode; title: st
|
|
|
3621
3824
|
|
|
3622
3825
|
export function CTABand({ title, subtitle, ctaLabel = "Get started", ctaHref = "/contact" }: { title: string; subtitle?: string; ctaLabel?: string; ctaHref?: string }) {
|
|
3623
3826
|
return (
|
|
3624
|
-
<section className="mx-auto max-w-6xl px-6 py-
|
|
3625
|
-
<div className="relative overflow-hidden rounded-
|
|
3626
|
-
<div aria-hidden className="pointer-events-none absolute -right-16 -top-16 h-
|
|
3627
|
-
<div aria-hidden className="pointer-events-none absolute -bottom-
|
|
3628
|
-
<
|
|
3629
|
-
|
|
3630
|
-
<
|
|
3827
|
+
<section className="mx-auto max-w-6xl px-6 py-24">
|
|
3828
|
+
<div className="relative overflow-hidden rounded-[2rem] border border-hairline bg-surface p-12 text-center shadow-lg sm:p-20">
|
|
3829
|
+
<div aria-hidden className="pointer-events-none absolute -right-16 -top-16 h-64 w-64 rounded-full gradient-bg opacity-30 blur-3xl animate-drift" />
|
|
3830
|
+
<div aria-hidden className="pointer-events-none absolute -bottom-20 -left-16 h-64 w-64 rounded-full gradient-bg opacity-25 blur-3xl animate-drift-slow" />
|
|
3831
|
+
<div aria-hidden className="dot-bg pointer-events-none absolute inset-0 opacity-40" />
|
|
3832
|
+
<h2 className="relative text-3xl font-bold tracking-tight sm:text-5xl">{title}</h2>
|
|
3833
|
+
{subtitle ? <p className="relative mx-auto mt-4 max-w-xl text-lg text-muted">{subtitle}</p> : null}
|
|
3834
|
+
<Link href={ctaHref} className="shimmer relative mt-9 inline-block rounded-full gradient-bg px-8 py-3.5 font-semibold on-accent shadow-lg transition hover:-translate-y-0.5">{ctaLabel}</Link>
|
|
3631
3835
|
</div>
|
|
3632
3836
|
</section>
|
|
3633
3837
|
);
|
|
@@ -3689,20 +3893,21 @@ export function Newsletter({ title = "Stay in the loop", subtitle = "Occasional
|
|
|
3689
3893
|
const err = state && !state.ok ? (state.errors.email ?? state.errors._form) : undefined;
|
|
3690
3894
|
|
|
3691
3895
|
return (
|
|
3692
|
-
<div className="mx-auto max-w-
|
|
3693
|
-
<
|
|
3694
|
-
<
|
|
3896
|
+
<div className="gradient-border relative mx-auto max-w-3xl overflow-hidden rounded-[1.75rem] border border-transparent bg-surface p-8 text-center shadow-lg sm:p-12">
|
|
3897
|
+
<div aria-hidden className="pointer-events-none absolute -right-16 -top-16 h-48 w-48 rounded-full gradient-bg opacity-20 blur-3xl" />
|
|
3898
|
+
<h2 className="relative text-2xl font-bold tracking-tight sm:text-3xl">{title}</h2>
|
|
3899
|
+
<p className="relative mx-auto mt-2 max-w-md text-sm text-muted">{subtitle}</p>
|
|
3695
3900
|
{state?.ok ? (
|
|
3696
|
-
<p className="mt-6 rounded-xl border border-hairline bg-app p-4 text-sm">You’re subscribed \u2014 thanks! \u2705</p>
|
|
3901
|
+
<p className="relative mx-auto mt-6 max-w-md rounded-xl border border-hairline bg-app p-4 text-sm">You’re subscribed \u2014 thanks! \u2705</p>
|
|
3697
3902
|
) : (
|
|
3698
|
-
<form action={action} className="mt-
|
|
3699
|
-
<input name="email" type="email" placeholder="you@example.com" aria-label="Email" className="w-full flex-1 rounded-full border border-hairline bg-app px-5 py-3 outline-none focus:border-[color:var(--accent-to)]" />
|
|
3903
|
+
<form action={action} className="relative mx-auto mt-7 flex max-w-md flex-col gap-3 sm:flex-row">
|
|
3904
|
+
<input name="email" type="email" placeholder="you@example.com" aria-label="Email" className="w-full flex-1 rounded-full border border-hairline bg-app px-5 py-3 outline-none transition focus:border-[color:var(--accent-to)]" />
|
|
3700
3905
|
<input {...honeypotProps("website")} />
|
|
3701
3906
|
<input type="hidden" name="_ts" defaultValue={timestampValue()} />
|
|
3702
|
-
<button disabled={pending} className="rounded-full gradient-bg px-6 py-3 font-semibold
|
|
3907
|
+
<button disabled={pending} className="rounded-full gradient-bg px-6 py-3 font-semibold on-accent transition hover:-translate-y-0.5 disabled:opacity-60">{pending ? "Joining\u2026" : "Subscribe"}</button>
|
|
3703
3908
|
</form>
|
|
3704
3909
|
)}
|
|
3705
|
-
{err ? <p className="mt-3 text-sm text-red-400">{err}</p> : null}
|
|
3910
|
+
{err ? <p className="relative mt-3 text-sm text-red-400">{err}</p> : null}
|
|
3706
3911
|
</div>
|
|
3707
3912
|
);
|
|
3708
3913
|
}
|
|
@@ -3711,7 +3916,7 @@ export function Newsletter({ title = "Stay in the loop", subtitle = "Occasional
|
|
|
3711
3916
|
|
|
3712
3917
|
const tones = {
|
|
3713
3918
|
default: "border-hairline bg-surface text-muted",
|
|
3714
|
-
accent: "border-transparent gradient-bg
|
|
3919
|
+
accent: "border-transparent gradient-bg on-accent",
|
|
3715
3920
|
success: "border-emerald-500/30 bg-emerald-500/10 text-emerald-400",
|
|
3716
3921
|
warning: "border-amber-500/30 bg-amber-500/10 text-amber-400",
|
|
3717
3922
|
danger: "border-red-500/30 bg-red-500/10 text-red-400",
|
|
@@ -3785,7 +3990,7 @@ export function Tabs({ tabs }: { tabs: { label: string; content: ReactNode }[] }
|
|
|
3785
3990
|
<div>
|
|
3786
3991
|
<div className="inline-flex flex-wrap gap-1 rounded-full border border-hairline bg-surface p-1">
|
|
3787
3992
|
{tabs.map((t, idx) => (
|
|
3788
|
-
<button key={t.label} onClick={() => setI(idx)} className={"rounded-full px-4 py-2 text-sm font-medium transition " + (i === idx ? "gradient-bg
|
|
3993
|
+
<button key={t.label} onClick={() => setI(idx)} className={"rounded-full px-4 py-2 text-sm font-medium transition " + (i === idx ? "gradient-bg on-accent" : "text-muted hover:text-fg")}>{t.label}</button>
|
|
3789
3994
|
))}
|
|
3790
3995
|
</div>
|
|
3791
3996
|
<div className="mt-6">{tabs[i]?.content}</div>
|
|
@@ -3814,16 +4019,16 @@ interface Tier { name: string; price: string; period?: string; features: string[
|
|
|
3814
4019
|
|
|
3815
4020
|
export function PricingTable({ tiers }: { tiers: Tier[] }) {
|
|
3816
4021
|
return (
|
|
3817
|
-
<div className="grid gap-6 md:grid-cols-3">
|
|
4022
|
+
<div className="grid items-center gap-6 md:grid-cols-3">
|
|
3818
4023
|
{tiers.map((t) => (
|
|
3819
|
-
<div key={t.name} className={"relative flex flex-col rounded-
|
|
3820
|
-
{t.featured ? <span className="absolute -top-3 left-8 rounded-full gradient-bg px-3 py-1 text-xs font-bold
|
|
3821
|
-
<h3 className="font-semibold">{t.name}</h3>
|
|
3822
|
-
<div className="mt-
|
|
3823
|
-
<ul className="mt-
|
|
3824
|
-
{t.features.map((f) => <li key={f} className="flex gap-2"><span className="gradient-text">\u2713</span>{f}</li>)}
|
|
4024
|
+
<div key={t.name} className={"relative flex flex-col rounded-[1.5rem] border p-8 transition " + (t.featured ? "gradient-border border-transparent bg-surface shadow-lg md:scale-[1.04]" : "border-hairline bg-surface hover:border-[color:var(--accent-to)]")}>
|
|
4025
|
+
{t.featured ? <span className="absolute -top-3 left-8 rounded-full gradient-bg px-3 py-1 text-xs font-bold on-accent shadow-sm">Most popular</span> : null}
|
|
4026
|
+
<h3 className="font-semibold tracking-tight">{t.name}</h3>
|
|
4027
|
+
<div className="mt-4 flex items-end gap-1"><span className="text-5xl font-bold tracking-tight">{t.price}</span>{t.period ? <span className="mb-1.5 text-sm text-muted">/{t.period}</span> : null}</div>
|
|
4028
|
+
<ul className="mt-7 flex-1 space-y-3 text-sm text-muted">
|
|
4029
|
+
{t.features.map((f) => <li key={f} className="flex gap-2.5"><span className="mt-0.5 gradient-text font-bold">\u2713</span>{f}</li>)}
|
|
3825
4030
|
</ul>
|
|
3826
|
-
<Link href={t.ctaHref ?? "/contact"} className={"mt-8 rounded-full px-6 py-3 text-center font-semibold transition " + (t.featured ? "gradient-bg
|
|
4031
|
+
<Link href={t.ctaHref ?? "/contact"} className={"mt-8 rounded-full px-6 py-3 text-center font-semibold transition " + (t.featured ? "shimmer gradient-bg on-accent hover:-translate-y-0.5" : "border border-hairline hover:bg-app")}>{t.ctaLabel ?? "Choose " + t.name}</Link>
|
|
3827
4032
|
</div>
|
|
3828
4033
|
))}
|
|
3829
4034
|
</div>
|
|
@@ -3833,9 +4038,9 @@ export function PricingTable({ tiers }: { tiers: Tier[] }) {
|
|
|
3833
4038
|
"components/ui/logo-cloud.tsx": `export function LogoCloud({ names, label }: { names: string[]; label?: string }) {
|
|
3834
4039
|
return (
|
|
3835
4040
|
<div className="text-center">
|
|
3836
|
-
{label ? <p className="text-
|
|
3837
|
-
<div className="mt-
|
|
3838
|
-
{names.map((n) => <span key={n} className="text-lg font-bold text-muted opacity-
|
|
4041
|
+
{label ? <p className="text-xs font-semibold uppercase tracking-widest text-faint">{label}</p> : null}
|
|
4042
|
+
<div className="mt-7 flex flex-wrap items-center justify-center gap-x-12 gap-y-5">
|
|
4043
|
+
{names.map((n) => <span key={n} className="text-lg font-bold tracking-tight text-muted opacity-60 grayscale transition hover:opacity-100 hover:grayscale-0">{n}</span>)}
|
|
3839
4044
|
</div>
|
|
3840
4045
|
</div>
|
|
3841
4046
|
);
|
|
@@ -3843,11 +4048,11 @@ export function PricingTable({ tiers }: { tiers: Tier[] }) {
|
|
|
3843
4048
|
`,
|
|
3844
4049
|
"components/ui/stat-band.tsx": `export function StatBand({ stats }: { stats: { value: string; label: string }[] }) {
|
|
3845
4050
|
return (
|
|
3846
|
-
<div className="grid gap-
|
|
4051
|
+
<div className="grid gap-px overflow-hidden rounded-[1.5rem] border border-hairline bg-hairline sm:grid-cols-2 lg:grid-cols-4">
|
|
3847
4052
|
{stats.map((s) => (
|
|
3848
|
-
<div key={s.label} className="text-center">
|
|
3849
|
-
<div className="gradient-text text-4xl font-bold tabular-nums">{s.value}</div>
|
|
3850
|
-
<div className="mt-
|
|
4053
|
+
<div key={s.label} className="bg-surface px-6 py-10 text-center transition hover:bg-panel">
|
|
4054
|
+
<div className="gradient-text text-4xl font-bold tabular-nums tracking-tight sm:text-5xl">{s.value}</div>
|
|
4055
|
+
<div className="mt-2 text-sm text-muted">{s.label}</div>
|
|
3851
4056
|
</div>
|
|
3852
4057
|
))}
|
|
3853
4058
|
</div>
|
|
@@ -3858,11 +4063,11 @@ export function PricingTable({ tiers }: { tiers: Tier[] }) {
|
|
|
3858
4063
|
return (
|
|
3859
4064
|
<div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
|
|
3860
4065
|
{members.map((m) => (
|
|
3861
|
-
<div key={m.name} className="
|
|
3862
|
-
<div className="mx-auto flex h-16 w-16 items-center justify-center rounded-
|
|
3863
|
-
<h3 className="mt-4 font-semibold">{m.name}</h3>
|
|
3864
|
-
<p className="gradient-text text-sm">{m.role}</p>
|
|
3865
|
-
{m.bio ? <p className="mt-2 text-sm text-muted">{m.bio}</p> : null}
|
|
4066
|
+
<div key={m.name} className="card p-7 text-center">
|
|
4067
|
+
<div className="mx-auto flex h-16 w-16 items-center justify-center rounded-2xl gradient-bg text-xl font-bold on-accent accent-glow">{m.name.split(" ").map((x) => x[0]).join("").slice(0, 2)}</div>
|
|
4068
|
+
<h3 className="mt-4 font-semibold tracking-tight">{m.name}</h3>
|
|
4069
|
+
<p className="gradient-text text-sm font-medium">{m.role}</p>
|
|
4070
|
+
{m.bio ? <p className="mt-2 text-sm leading-relaxed text-muted">{m.bio}</p> : null}
|
|
3866
4071
|
</div>
|
|
3867
4072
|
))}
|
|
3868
4073
|
</div>
|
|
@@ -3938,7 +4143,7 @@ export function Breadcrumbs({ items }: { items: { label: string; href?: string }
|
|
|
3938
4143
|
`,
|
|
3939
4144
|
"components/ui/avatar.tsx": `export function Avatar({ name, size = 40 }: { name: string; size?: number }) {
|
|
3940
4145
|
const initials = name.split(" ").map((x) => x[0]).join("").slice(0, 2).toUpperCase();
|
|
3941
|
-
return <span className="inline-flex items-center justify-center rounded-full gradient-bg font-bold
|
|
4146
|
+
return <span className="inline-flex items-center justify-center rounded-full gradient-bg font-bold on-accent" style={{ width: size, height: size, fontSize: size * 0.4 }}>{initials}</span>;
|
|
3942
4147
|
}
|
|
3943
4148
|
|
|
3944
4149
|
export function AvatarGroup({ names }: { names: string[] }) {
|
|
@@ -4159,10 +4364,15 @@ function creativeData(ctx) {
|
|
|
4159
4364
|
}
|
|
4160
4365
|
var aurora = () => `export function Aurora() {
|
|
4161
4366
|
return (
|
|
4162
|
-
<div aria-hidden className="pointer-events-none absolute inset-x-0 top-0 -z-10 h-[
|
|
4163
|
-
|
|
4164
|
-
<div className="absolute -
|
|
4165
|
-
<div className="absolute
|
|
4367
|
+
<div aria-hidden className="pointer-events-none absolute inset-x-0 top-0 -z-10 h-[820px] overflow-hidden">
|
|
4368
|
+
{/* soft gradient-mesh \u2014 blurred orbs keyed to the accent, drifting slowly */}
|
|
4369
|
+
<div className="absolute -left-32 -top-24 h-[32rem] w-[32rem] rounded-full opacity-25 blur-[90px] animate-drift" style={{ background: "radial-gradient(circle at 50% 50%, var(--accent-from), transparent 68%)" }} />
|
|
4370
|
+
<div className="absolute -right-24 top-24 h-[30rem] w-[30rem] rounded-full opacity-20 blur-[90px] animate-drift-slow" style={{ background: "radial-gradient(circle at 50% 50%, var(--accent-to), transparent 68%)" }} />
|
|
4371
|
+
<div className="absolute left-1/3 top-64 h-96 w-96 rounded-full opacity-[0.14] blur-[80px] animate-floaty" style={{ background: "radial-gradient(circle at 50% 50%, var(--accent-to), transparent 70%)" }} />
|
|
4372
|
+
{/* faint dotted texture, fading out toward the fold */}
|
|
4373
|
+
<div className="dot-bg absolute inset-0 opacity-60" />
|
|
4374
|
+
{/* seam that melts the aurora into the page */}
|
|
4375
|
+
<div className="absolute inset-x-0 bottom-0 h-40 bg-gradient-to-b from-transparent to-[var(--bg)]" />
|
|
4166
4376
|
</div>
|
|
4167
4377
|
);
|
|
4168
4378
|
}
|
|
@@ -4173,22 +4383,25 @@ var heroArt = (ctx) => {
|
|
|
4173
4383
|
return `export function HeroArt() {
|
|
4174
4384
|
return (
|
|
4175
4385
|
<div className="relative mx-auto aspect-square w-full max-w-sm select-none">
|
|
4386
|
+
{/* ambient accent glow behind the whole piece */}
|
|
4387
|
+
<div aria-hidden className="absolute inset-6 rounded-full opacity-30 blur-3xl gradient-bg animate-drift-slow" />
|
|
4176
4388
|
{/* orbiting rings */}
|
|
4177
4389
|
<div className="absolute inset-0 rounded-full border border-hairline animate-spin-slow" />
|
|
4178
|
-
<div className="absolute inset-10 rounded-full border border-hairline animate-spin-slow" style={{ animationDirection: "reverse" }} />
|
|
4179
|
-
|
|
4390
|
+
<div className="absolute inset-10 rounded-full border border-dashed border-hairline animate-spin-slow" style={{ animationDirection: "reverse" }} />
|
|
4391
|
+
{/* orbit node */}
|
|
4392
|
+
<div className="absolute left-1/2 top-0 h-2.5 w-2.5 -translate-x-1/2 rounded-full gradient-bg animate-spin-slow" style={{ transformOrigin: "50% 190px" }} />
|
|
4180
4393
|
{/* center badge with the platform initials */}
|
|
4181
|
-
<div className="absolute left-1/2 top-1/2 flex h-
|
|
4394
|
+
<div className="absolute left-1/2 top-1/2 flex h-32 w-32 -translate-x-1/2 -translate-y-1/2 items-center justify-center rounded-[1.75rem] gradient-bg text-4xl font-black on-accent shadow-lg accent-glow animate-floaty" style={{ animationDuration: "7s" }}>
|
|
4182
4395
|
${JSON.stringify(initials)}
|
|
4183
4396
|
</div>
|
|
4184
|
-
{/* floating
|
|
4185
|
-
<div className="absolute left-0 top-10 flex items-center gap-2 rounded-
|
|
4186
|
-
<span>${d.emoji}</span> ${JSON.stringify(d.chips[0])}
|
|
4397
|
+
{/* floating glass chips */}
|
|
4398
|
+
<div className="glass absolute left-0 top-10 flex items-center gap-2 rounded-2xl px-3.5 py-2 text-xs font-semibold shadow-lg animate-floaty">
|
|
4399
|
+
<span className="text-sm">${d.emoji}</span> ${JSON.stringify(d.chips[0])}
|
|
4187
4400
|
</div>
|
|
4188
|
-
<div className="absolute right-0 top-28
|
|
4189
|
-
${JSON.stringify(d.chips[1])}
|
|
4401
|
+
<div className="glass absolute right-0 top-28 flex items-center gap-1.5 rounded-2xl px-3.5 py-2 text-xs font-semibold shadow-lg animate-floaty-slow">
|
|
4402
|
+
<span className="h-1.5 w-1.5 rounded-full gradient-bg" /> ${JSON.stringify(d.chips[1])}
|
|
4190
4403
|
</div>
|
|
4191
|
-
<div className="absolute bottom-8 left-6 rounded-
|
|
4404
|
+
<div className="glass absolute bottom-8 left-6 rounded-2xl px-3.5 py-2 text-xs font-semibold shadow-lg animate-floaty" style={{ animationDelay: "1.2s" }}>
|
|
4192
4405
|
${JSON.stringify(d.chips[2])}
|
|
4193
4406
|
</div>
|
|
4194
4407
|
</div>
|
|
@@ -4270,30 +4483,29 @@ var extraHomeSections = (ctx) => {
|
|
|
4270
4483
|
const r = richContent(ctx);
|
|
4271
4484
|
const tst = r.testimonials.map((t) => `<Testimonial quote={${JSON.stringify(t.quote)}} author={${JSON.stringify(t.author)}} role={${JSON.stringify(t.role ?? "")}} />`).join("\n ");
|
|
4272
4485
|
return `<section className="mx-auto max-w-6xl px-6 py-16">
|
|
4273
|
-
<
|
|
4274
|
-
<
|
|
4486
|
+
<div className="mb-10 text-center"><p className="text-sm font-semibold uppercase tracking-widest gradient-text">By the numbers</p><h2 className="mt-3 text-3xl font-bold sm:text-4xl">Built to perform</h2></div>
|
|
4487
|
+
<StatBand stats={${JSON.stringify(r.stats)}} />
|
|
4275
4488
|
</section>
|
|
4276
4489
|
<section className="mx-auto max-w-6xl px-6 py-16">
|
|
4277
|
-
<
|
|
4278
|
-
<
|
|
4279
|
-
<div className="mt-10"><Steps items={${JSON.stringify(r.process)}} /></div>
|
|
4490
|
+
<div className="mb-12 text-center"><p className="text-sm font-semibold uppercase tracking-widest gradient-text">How it works</p><h2 className="mt-3 text-3xl font-bold sm:text-4xl">A simple, proven path</h2><p className="mx-auto mt-3 max-w-xl text-muted">From first hello to launch day \u2014 no surprises.</p></div>
|
|
4491
|
+
<Steps items={${JSON.stringify(r.process)}} />
|
|
4280
4492
|
</section>
|
|
4281
4493
|
<section className="mx-auto max-w-6xl px-6 py-16">
|
|
4282
|
-
<
|
|
4283
|
-
<div className="
|
|
4494
|
+
<div className="mb-12 text-center"><p className="text-sm font-semibold uppercase tracking-widest gradient-text">Loved by teams</p><h2 className="mt-3 text-3xl font-bold sm:text-4xl">Don't just take our word for it</h2></div>
|
|
4495
|
+
<div className="grid items-stretch gap-6 md:grid-cols-3">
|
|
4284
4496
|
${tst}
|
|
4285
4497
|
</div>
|
|
4286
4498
|
</section>`;
|
|
4287
4499
|
};
|
|
4288
4500
|
var showcaseSection = (ctx) => {
|
|
4289
4501
|
const d = creativeData(ctx);
|
|
4290
|
-
const bullets = d.bullets.map((b) => `<li className="flex items-center gap-
|
|
4291
|
-
return `<section className="mx-auto grid max-w-6xl items-center gap-12 px-6 py-
|
|
4502
|
+
const bullets = d.bullets.map((b) => `<li className="flex items-center gap-3 text-muted"><span className="inline-flex h-5 w-5 shrink-0 items-center justify-center rounded-full gradient-bg text-[11px] on-accent">\u2713</span> ${b}</li>`).join("\n ");
|
|
4503
|
+
return `<section className="mx-auto grid max-w-6xl items-center gap-12 px-6 py-24 md:grid-cols-2">
|
|
4292
4504
|
<div>
|
|
4293
|
-
<p className="text-sm font-semibold uppercase tracking-widest text
|
|
4505
|
+
<p className="text-sm font-semibold uppercase tracking-widest gradient-text">${ctx.template.siteName}</p>
|
|
4294
4506
|
<h2 className="mt-3 text-3xl font-bold sm:text-4xl">${d.showcaseTitle}</h2>
|
|
4295
4507
|
<p className="mt-4 max-w-md text-lg text-muted">${d.showcaseLead}</p>
|
|
4296
|
-
<ul className="mt-
|
|
4508
|
+
<ul className="mt-7 space-y-3.5 text-sm font-medium">
|
|
4297
4509
|
${bullets}
|
|
4298
4510
|
</ul>
|
|
4299
4511
|
</div>
|
|
@@ -4302,10 +4514,10 @@ var showcaseSection = (ctx) => {
|
|
|
4302
4514
|
};
|
|
4303
4515
|
var marqueeSection = (ctx) => {
|
|
4304
4516
|
const d = creativeData(ctx);
|
|
4305
|
-
const chip = (t) => `<span className="rounded-full border border-hairline bg-surface px-5 py-2 text-sm font-
|
|
4517
|
+
const chip = (t) => `<span className="whitespace-nowrap rounded-full border border-hairline bg-surface px-5 py-2 text-sm font-semibold text-muted">${t}</span>`;
|
|
4306
4518
|
const track = d.marquee.map(chip).join("\n ");
|
|
4307
|
-
return `<section className="
|
|
4308
|
-
<p className="mb-
|
|
4519
|
+
return `<section className="py-14">
|
|
4520
|
+
<p className="mb-8 text-center text-xs font-semibold uppercase tracking-widest text-faint">${d.marqueeLabel}</p>
|
|
4309
4521
|
<div className="marquee-mask overflow-hidden">
|
|
4310
4522
|
<div className="marquee-track gap-4">
|
|
4311
4523
|
${track}
|
|
@@ -4316,16 +4528,16 @@ var marqueeSection = (ctx) => {
|
|
|
4316
4528
|
};
|
|
4317
4529
|
var faqSection = (ctx) => {
|
|
4318
4530
|
const d = creativeData(ctx);
|
|
4319
|
-
const items = d.faq.map((f) => `<details className="group rounded-2xl border border-hairline bg-surface p-5">
|
|
4320
|
-
<summary className="flex cursor-pointer list-none items-center justify-between gap-4 font-
|
|
4531
|
+
const items = d.faq.map((f) => `<details className="group rounded-2xl border border-hairline bg-surface p-5 transition hover:border-[color:var(--accent-to)]">
|
|
4532
|
+
<summary className="flex cursor-pointer list-none items-center justify-between gap-4 font-semibold">
|
|
4321
4533
|
${f.q}
|
|
4322
|
-
<span className="text-muted transition group-open:rotate-45">+</span>
|
|
4534
|
+
<span className="text-lg text-muted transition group-open:rotate-45">+</span>
|
|
4323
4535
|
</summary>
|
|
4324
4536
|
<p className="mt-3 text-sm leading-relaxed text-muted">${f.a}</p>
|
|
4325
4537
|
</details>`).join("\n ");
|
|
4326
|
-
return `<section className="mx-auto max-w-3xl px-6 py-
|
|
4327
|
-
<
|
|
4328
|
-
<div className="
|
|
4538
|
+
return `<section className="mx-auto max-w-3xl px-6 py-24">
|
|
4539
|
+
<div className="mb-10 text-center"><p className="text-sm font-semibold uppercase tracking-widest gradient-text">FAQ</p><h2 className="mt-3 text-3xl font-bold sm:text-4xl">Frequently asked</h2></div>
|
|
4540
|
+
<div className="space-y-3">
|
|
4329
4541
|
${items}
|
|
4330
4542
|
</div>
|
|
4331
4543
|
</section>`;
|
|
@@ -4460,13 +4672,13 @@ import { Pill } from "@/components/ui";
|
|
|
4460
4672
|
|
|
4461
4673
|
export function HeroSection() {
|
|
4462
4674
|
return (
|
|
4463
|
-
<section className="mx-auto max-w-4xl px-6 py-
|
|
4675
|
+
<section className="mx-auto max-w-4xl px-6 py-32 text-center">
|
|
4464
4676
|
<Pill>New</Pill>
|
|
4465
|
-
<h1 className="
|
|
4466
|
-
<p className="mx-auto mt-
|
|
4467
|
-
<div className="mt-
|
|
4468
|
-
<Link href="/contact" className="rounded-full gradient-bg px-7 py-3 font-semibold
|
|
4469
|
-
<Link href="/about" className="rounded-full border border-hairline px-7 py-3 font-semibold transition hover:bg-surface">Learn more</Link>
|
|
4677
|
+
<h1 className="text-display mt-6">Build something <span className="gradient-text">people love</span></h1>
|
|
4678
|
+
<p className="lead mx-auto mt-6 max-w-xl">A confident headline and one sentence that sells the outcome \u2014 edit me.</p>
|
|
4679
|
+
<div className="mt-9 flex flex-wrap justify-center gap-3">
|
|
4680
|
+
<Link href="/contact" className="shimmer rounded-full gradient-bg px-7 py-3.5 font-semibold on-accent transition hover:-translate-y-0.5">Get started</Link>
|
|
4681
|
+
<Link href="/about" className="rounded-full border border-hairline px-7 py-3.5 font-semibold transition hover:bg-surface">Learn more</Link>
|
|
4470
4682
|
</div>
|
|
4471
4683
|
</section>
|
|
4472
4684
|
);
|
|
@@ -4628,6 +4840,53 @@ export function BentoSection() {
|
|
|
4628
4840
|
</Section>
|
|
4629
4841
|
);
|
|
4630
4842
|
}
|
|
4843
|
+
`,
|
|
4844
|
+
steps: `import { Section, Steps } from "@/components/ui";
|
|
4845
|
+
|
|
4846
|
+
export function StepsSection() {
|
|
4847
|
+
return (
|
|
4848
|
+
<Section eyebrow="How it works" title="Up and running in three steps">
|
|
4849
|
+
<Steps items={[
|
|
4850
|
+
{ title: "Create your account", desc: "Sign up free in seconds \u2014 no credit card required." },
|
|
4851
|
+
{ title: "Connect your data", desc: "Import in one click or use the API. Your call." },
|
|
4852
|
+
{ title: "Ship something great", desc: "Go live and watch it work, from day one." },
|
|
4853
|
+
]} />
|
|
4854
|
+
</Section>
|
|
4855
|
+
);
|
|
4856
|
+
}
|
|
4857
|
+
`,
|
|
4858
|
+
"feature-split": `import { Section, FeatureSplit } from "@/components/ui";
|
|
4859
|
+
|
|
4860
|
+
export function FeatureSplitSection() {
|
|
4861
|
+
return (
|
|
4862
|
+
<Section eyebrow="Why teams switch" title="Built for the way you actually work">
|
|
4863
|
+
<FeatureSplit
|
|
4864
|
+
eyebrow="Focus"
|
|
4865
|
+
title="Less busywork, more momentum"
|
|
4866
|
+
desc="Everything in one place, so your team spends time on the work that matters \u2014 not on wiring tools together."
|
|
4867
|
+
bullets={["Set up in minutes, not weeks", "Real-time, always in sync", "Scales from one to a thousand"]}
|
|
4868
|
+
media={<div className="grid h-52 place-items-center text-7xl">\u{1F680}</div>}
|
|
4869
|
+
/>
|
|
4870
|
+
</Section>
|
|
4871
|
+
);
|
|
4872
|
+
}
|
|
4873
|
+
`,
|
|
4874
|
+
banner: `import Link from "next/link";
|
|
4875
|
+
|
|
4876
|
+
export function BannerSection() {
|
|
4877
|
+
return (
|
|
4878
|
+
<section className="mx-auto max-w-6xl px-6 py-12">
|
|
4879
|
+
<div className="relative flex flex-col items-center gap-6 overflow-hidden rounded-[1.75rem] gradient-bg px-8 py-10 text-center on-accent shadow-lg sm:flex-row sm:justify-between sm:text-left">
|
|
4880
|
+
<div aria-hidden className="dot-bg pointer-events-none absolute inset-0 opacity-20" />
|
|
4881
|
+
<div className="relative">
|
|
4882
|
+
<h2 className="text-2xl font-bold tracking-tight sm:text-3xl">Ready to see it in action?</h2>
|
|
4883
|
+
<p className="mt-1.5 text-sm opacity-80">Start free today \u2014 it takes less than a minute.</p>
|
|
4884
|
+
</div>
|
|
4885
|
+
<Link href="/contact" className="relative shrink-0 rounded-full bg-[var(--bg)] px-7 py-3 font-semibold text-fg transition hover:-translate-y-0.5">Get started</Link>
|
|
4886
|
+
</div>
|
|
4887
|
+
</section>
|
|
4888
|
+
);
|
|
4889
|
+
}
|
|
4631
4890
|
`
|
|
4632
4891
|
};
|
|
4633
4892
|
function ensureUiKit(root) {
|