@pylonsync/create-pylon 0.3.268 → 0.3.270
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/bin/create-pylon.js +11 -9
- package/package.json +1 -1
- package/templates/b2b/app/layout.tsx +1 -1
- package/templates/b2b/app/page.tsx +2 -2
- package/templates/b2b/tsconfig.json +1 -1
- package/templates/barebones/app/page.tsx +1 -1
- package/templates/barebones/tsconfig.json +1 -1
- package/templates/chat/app/page.tsx +1 -1
- package/templates/chat/tsconfig.json +1 -1
- package/templates/consumer/app/page.tsx +1 -1
- package/templates/consumer/tsconfig.json +1 -1
- package/templates/default/.env.example +19 -0
- package/templates/default/README.md +85 -0
- package/templates/default/app/auth-form.tsx +218 -0
- package/templates/default/app/auth-shell.tsx +76 -0
- package/templates/default/app/company/[slug]/page.tsx +28 -0
- package/templates/default/app/compare/[slug]/page.tsx +27 -0
- package/templates/default/app/dashboard/billing/page.tsx +49 -0
- package/templates/default/app/dashboard/dashboard-client.tsx +832 -0
- package/templates/default/app/dashboard/members/page.tsx +37 -0
- package/templates/default/app/dashboard/page.tsx +64 -0
- package/templates/default/app/dashboard/projects/page.tsx +37 -0
- package/templates/default/app/dashboard/settings/page.tsx +45 -0
- package/templates/{ssr → default}/app/globals.css +14 -0
- package/templates/default/app/layout.tsx +466 -0
- package/templates/default/app/login/page.tsx +27 -0
- package/templates/default/app/onboarding/onboarding-client.tsx +261 -0
- package/templates/default/app/onboarding/page.tsx +29 -0
- package/templates/default/app/page.tsx +653 -0
- package/templates/default/app/products/[slug]/page.tsx +134 -0
- package/templates/default/app/resources/[slug]/page.tsx +28 -0
- package/templates/default/app/signup/page.tsx +24 -0
- package/templates/default/app/sitemap.ts +40 -0
- package/templates/default/app/solutions/[slug]/page.tsx +28 -0
- package/templates/default/app.ts +194 -0
- package/templates/default/components/dashboard-shell.tsx +150 -0
- package/templates/default/components/marketing.tsx +370 -0
- package/templates/default/functions/_pylonStripeFindActiveSubForReference.ts +3 -0
- package/templates/default/functions/_pylonStripeFindByCustomerId.ts +3 -0
- package/templates/default/functions/_pylonStripeGetCustomerHolder.ts +3 -0
- package/templates/default/functions/_pylonStripeListSubsForReference.ts +3 -0
- package/templates/default/functions/_pylonStripeOrgMembership.ts +3 -0
- package/templates/default/functions/_pylonStripeSetCustomerId.ts +3 -0
- package/templates/default/functions/_pylonStripeUpsertSubscription.ts +3 -0
- package/templates/default/functions/cancelSubscription.ts +3 -0
- package/templates/default/functions/createBillingPortalSession.ts +3 -0
- package/templates/default/functions/createCheckoutSession.ts +3 -0
- package/templates/default/functions/restoreSubscription.ts +3 -0
- package/templates/default/functions/stripeWebhook.ts +3 -0
- package/templates/default/lib/billing.ts +46 -0
- package/templates/default/lib/products.ts +122 -0
- package/templates/default/lib/site.ts +261 -0
- package/templates/{ssr → default}/package.json +2 -0
- package/templates/{ssr → default}/tsconfig.json +2 -2
- package/templates/todo/app/page.tsx +1 -1
- package/templates/todo/tsconfig.json +1 -1
- package/templates/ssr/README.md +0 -56
- package/templates/ssr/app/auth-form.tsx +0 -142
- package/templates/ssr/app/dashboard/dashboard-client.tsx +0 -116
- package/templates/ssr/app/dashboard/page.tsx +0 -70
- package/templates/ssr/app/layout.tsx +0 -71
- package/templates/ssr/app/login/page.tsx +0 -47
- package/templates/ssr/app/page.tsx +0 -114
- package/templates/ssr/app/signup/page.tsx +0 -44
- package/templates/ssr/app/sitemap.ts +0 -27
- package/templates/ssr/app.ts +0 -94
- package/templates/ssr/functions/_keep.ts +0 -13
- /package/templates/{ssr → default}/AGENTS.md +0 -0
- /package/templates/{ssr → default}/app/error.tsx +0 -0
- /package/templates/{ssr → default}/app/not-found.tsx +0 -0
- /package/templates/{ssr → default}/app/robots.ts +0 -0
- /package/templates/{ssr → default}/components/ui/button.tsx +0 -0
- /package/templates/{ssr → default}/components/ui/card.tsx +0 -0
- /package/templates/{ssr → default}/components.json +0 -0
- /package/templates/{ssr → default}/gitignore +0 -0
- /package/templates/{ssr → default}/lib/utils.ts +0 -0
|
@@ -0,0 +1,653 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Link, type Metadata, type PageProps } from "@pylonsync/react";
|
|
3
|
+
import {
|
|
4
|
+
WRAP,
|
|
5
|
+
Badge,
|
|
6
|
+
Divider,
|
|
7
|
+
Eyebrow,
|
|
8
|
+
SectionHead,
|
|
9
|
+
FeatureGrid,
|
|
10
|
+
PrimaryButton,
|
|
11
|
+
GhostLink,
|
|
12
|
+
Shot,
|
|
13
|
+
Portrait,
|
|
14
|
+
Terminal,
|
|
15
|
+
} from "@/components/marketing";
|
|
16
|
+
import { PRODUCTS, productBySlug } from "@/lib/products";
|
|
17
|
+
|
|
18
|
+
// SEO metadata. Exported `metadata` is rendered into <head> on the server, so
|
|
19
|
+
// this marketing page is fully indexable — view source and the copy is in the
|
|
20
|
+
// HTML. Swap "Acme" for your product throughout.
|
|
21
|
+
export const metadata: Metadata = {
|
|
22
|
+
title: "Acme — the workspace where work gets done",
|
|
23
|
+
description:
|
|
24
|
+
"Acme brings your projects, your people, and your updates into one fast, real-time workspace. Plan together, ship together, keep everyone in the loop.",
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// The three products the homepage features inline. Each links to its own
|
|
28
|
+
// /products/[slug] page for the full story. (Defined once in lib/products.ts.)
|
|
29
|
+
const projects = productBySlug("projects")!;
|
|
30
|
+
const tasks = productBySlug("tasks")!;
|
|
31
|
+
const docs = productBySlug("docs")!;
|
|
32
|
+
const automations = productBySlug("automations")!;
|
|
33
|
+
|
|
34
|
+
// `app/page.tsx` → `/`. A server-rendered marketing landing page. It reads
|
|
35
|
+
// `auth` (resolved from the session cookie during the render) so the call to
|
|
36
|
+
// action is right on the first byte — "Get started" for visitors, "Open
|
|
37
|
+
// dashboard" once you're signed in. No client fetch, no flash.
|
|
38
|
+
export default function LandingPage({ auth }: PageProps) {
|
|
39
|
+
const signedIn = Boolean(auth.user_id);
|
|
40
|
+
const primaryHref = signedIn ? "/dashboard" : "/signup";
|
|
41
|
+
const primaryLabel = signedIn ? "Open dashboard" : "Get started";
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<div className="bg-white text-zinc-900">
|
|
45
|
+
{/* ============================ HERO ============================ */}
|
|
46
|
+
<section className={`${WRAP} pt-20 pb-16 sm:pt-28`}>
|
|
47
|
+
<Badge>Acme for teams is here →</Badge>
|
|
48
|
+
<h1 className="mt-6 max-w-2xl text-balance text-[2.75rem] font-semibold leading-[1.05] tracking-[-0.02em] sm:text-[3.5rem]">
|
|
49
|
+
The workspace where work gets done.
|
|
50
|
+
</h1>
|
|
51
|
+
<p className="mt-6 max-w-xl text-[17px] leading-relaxed text-zinc-500">
|
|
52
|
+
Acme brings your projects, your people, and your updates into one
|
|
53
|
+
fast, real-time workspace. Plan together, ship together, and keep
|
|
54
|
+
everyone in the loop without the busywork.
|
|
55
|
+
</p>
|
|
56
|
+
<div className="mt-8 flex flex-wrap items-center gap-4">
|
|
57
|
+
<PrimaryButton href={primaryHref}>{primaryLabel}</PrimaryButton>
|
|
58
|
+
<GhostLink href="/#product">Take the tour →</GhostLink>
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
<div className="mt-16">
|
|
62
|
+
<Shot url="acme.app/dashboard" label="Dashboard preview" />
|
|
63
|
+
</div>
|
|
64
|
+
</section>
|
|
65
|
+
|
|
66
|
+
{/* ========================= LOGO CLOUD ========================= */}
|
|
67
|
+
<section className={`${WRAP} pb-16`}>
|
|
68
|
+
<p className="font-mono text-[11px] uppercase tracking-[0.14em] text-zinc-400">
|
|
69
|
+
Powering fast-moving teams
|
|
70
|
+
</p>
|
|
71
|
+
<div className="mt-6 flex flex-wrap items-center gap-x-10 gap-y-4">
|
|
72
|
+
{["Northwind", "Globex", "Initech", "Umbrella", "Soylent", "Hooli"].map(
|
|
73
|
+
(name) => (
|
|
74
|
+
<div
|
|
75
|
+
key={name}
|
|
76
|
+
className="flex items-center gap-2 text-zinc-400"
|
|
77
|
+
title={name}
|
|
78
|
+
>
|
|
79
|
+
<span className="size-5 rounded bg-zinc-200" />
|
|
80
|
+
<span className="text-[13px] font-semibold uppercase tracking-wide">
|
|
81
|
+
{name}
|
|
82
|
+
</span>
|
|
83
|
+
</div>
|
|
84
|
+
),
|
|
85
|
+
)}
|
|
86
|
+
</div>
|
|
87
|
+
</section>
|
|
88
|
+
|
|
89
|
+
{/* ===================== OUTCOMES (2-col) ====================== */}
|
|
90
|
+
<Divider />
|
|
91
|
+
<section className={`${WRAP} py-20`}>
|
|
92
|
+
<div className="grid gap-12 lg:grid-cols-[0.9fr_1.1fr]">
|
|
93
|
+
<div>
|
|
94
|
+
<Eyebrow>Why Acme</Eyebrow>
|
|
95
|
+
<h2 className="mt-4 text-balance text-3xl font-semibold leading-[1.1] tracking-[-0.02em] sm:text-[2.5rem]">
|
|
96
|
+
Everything your team needs to stay in motion.
|
|
97
|
+
</h2>
|
|
98
|
+
<p className="mt-5 max-w-md text-[15px] leading-relaxed text-zinc-500">
|
|
99
|
+
Most teams lose work somewhere between the kickoff and the ship
|
|
100
|
+
date. Acme keeps the whole path in one place, so every idea has a
|
|
101
|
+
clear route from planned, to in progress, to done.
|
|
102
|
+
</p>
|
|
103
|
+
</div>
|
|
104
|
+
<FeatureGrid columns={2} items={OUTCOMES} />
|
|
105
|
+
</div>
|
|
106
|
+
</section>
|
|
107
|
+
|
|
108
|
+
{/* ======================= TESTIMONIAL ========================= */}
|
|
109
|
+
<Divider />
|
|
110
|
+
<section className={`${WRAP} py-20`}>
|
|
111
|
+
<div className="grid items-center gap-12 lg:grid-cols-[0.8fr_1.2fr]">
|
|
112
|
+
<Portrait name="Maya Chen" />
|
|
113
|
+
<figure>
|
|
114
|
+
<div className="font-serif text-4xl leading-none text-brand">
|
|
115
|
+
“
|
|
116
|
+
</div>
|
|
117
|
+
<blockquote className="mt-4 text-balance text-2xl font-medium leading-[1.35] tracking-[-0.01em] sm:text-[1.75rem]">
|
|
118
|
+
Our whole team finally works in one place. Acme made it easy to see
|
|
119
|
+
what is happening, decide what is next, and keep everyone moving in
|
|
120
|
+
the same direction.
|
|
121
|
+
</blockquote>
|
|
122
|
+
<figcaption className="mt-8 border-t border-zinc-200/70 pt-6">
|
|
123
|
+
<div className="text-sm font-semibold">Maya Chen</div>
|
|
124
|
+
<div className="text-sm text-zinc-500">
|
|
125
|
+
Head of Product, Northwind
|
|
126
|
+
</div>
|
|
127
|
+
</figcaption>
|
|
128
|
+
</figure>
|
|
129
|
+
</div>
|
|
130
|
+
</section>
|
|
131
|
+
|
|
132
|
+
{/* =================== FEATURE: PROJECTS ======================= */}
|
|
133
|
+
<Divider />
|
|
134
|
+
<ProductSection id="product" product={projects} primaryHref={primaryHref} />
|
|
135
|
+
|
|
136
|
+
{/* ===================== ENTRY POINTS ========================= */}
|
|
137
|
+
<Divider />
|
|
138
|
+
<section className={`${WRAP} py-20`}>
|
|
139
|
+
<SectionHead
|
|
140
|
+
eyebrow="Anywhere"
|
|
141
|
+
title="Meet your team where they already are."
|
|
142
|
+
body="Web, desktop, and a typed API all share the same workspace underneath, so nothing lives in two places."
|
|
143
|
+
/>
|
|
144
|
+
<div className="mt-12 grid gap-5 sm:grid-cols-2">
|
|
145
|
+
{ENTRY_POINTS.map((c, i) => (
|
|
146
|
+
<div
|
|
147
|
+
key={c.title}
|
|
148
|
+
className="rounded-2xl border border-zinc-200 bg-paper p-7"
|
|
149
|
+
>
|
|
150
|
+
<div className="flex items-start justify-between">
|
|
151
|
+
<span className="flex size-9 items-center justify-center rounded-lg bg-brand-soft text-brand">
|
|
152
|
+
{c.icon}
|
|
153
|
+
</span>
|
|
154
|
+
<span className="font-mono text-[11px] text-zinc-300">
|
|
155
|
+
0{i + 1}
|
|
156
|
+
</span>
|
|
157
|
+
</div>
|
|
158
|
+
<h3 className="mt-5 text-base font-semibold">{c.title}</h3>
|
|
159
|
+
<p className="mt-2 text-[14px] leading-relaxed text-zinc-500">
|
|
160
|
+
{c.body}
|
|
161
|
+
</p>
|
|
162
|
+
</div>
|
|
163
|
+
))}
|
|
164
|
+
</div>
|
|
165
|
+
</section>
|
|
166
|
+
|
|
167
|
+
{/* ====================== FEATURE: TASKS ===================== */}
|
|
168
|
+
<Divider />
|
|
169
|
+
<ProductSection product={tasks} primaryHref={primaryHref} />
|
|
170
|
+
|
|
171
|
+
{/* ====================== FEATURE: DOCS ====================== */}
|
|
172
|
+
<Divider />
|
|
173
|
+
<ProductSection product={docs} primaryHref={primaryHref} />
|
|
174
|
+
|
|
175
|
+
{/* ============== ENGAGEMENT (prose + numbered) ============== */}
|
|
176
|
+
<Divider />
|
|
177
|
+
<section className={`${WRAP} py-20`}>
|
|
178
|
+
<Eyebrow>Momentum</Eyebrow>
|
|
179
|
+
<h2 className="mt-4 max-w-2xl text-balance text-3xl font-semibold leading-[1.1] tracking-[-0.02em] sm:text-[2.5rem]">
|
|
180
|
+
Most tools go quiet after the kickoff.
|
|
181
|
+
</h2>
|
|
182
|
+
<div className="mt-12 grid gap-12 lg:grid-cols-2">
|
|
183
|
+
<div className="space-y-5 text-[15px] leading-relaxed text-zinc-500">
|
|
184
|
+
<p>
|
|
185
|
+
Someone shares an idea, it lands in a list, and that is the last
|
|
186
|
+
anyone hears of it. Acme treats every idea as the start of a loop,
|
|
187
|
+
not the end of one.
|
|
188
|
+
</p>
|
|
189
|
+
<p>
|
|
190
|
+
When the status moves to planned, the people who care hear about
|
|
191
|
+
it. When it ships, they hear about it first. And every week a
|
|
192
|
+
digest pulls them back with the work worth weighing in on.
|
|
193
|
+
</p>
|
|
194
|
+
<p>
|
|
195
|
+
None of it is something you configure. Turn Acme on, and the loop
|
|
196
|
+
starts running the same day.
|
|
197
|
+
</p>
|
|
198
|
+
</div>
|
|
199
|
+
<ol className="space-y-7">
|
|
200
|
+
{ENGAGEMENT.map((e, i) => (
|
|
201
|
+
<li key={e.title} className="flex gap-4">
|
|
202
|
+
<span className="mt-0.5 font-mono text-[11px] text-zinc-300">
|
|
203
|
+
0{i + 1}
|
|
204
|
+
</span>
|
|
205
|
+
<div>
|
|
206
|
+
<span className="text-[15px] font-medium text-brand">
|
|
207
|
+
{e.title}.
|
|
208
|
+
</span>{" "}
|
|
209
|
+
<span className="text-[15px] leading-relaxed text-zinc-500">
|
|
210
|
+
{e.body}
|
|
211
|
+
</span>
|
|
212
|
+
</div>
|
|
213
|
+
</li>
|
|
214
|
+
))}
|
|
215
|
+
</ol>
|
|
216
|
+
</div>
|
|
217
|
+
</section>
|
|
218
|
+
|
|
219
|
+
{/* ======================= AUTOMATIONS ======================= */}
|
|
220
|
+
<Divider />
|
|
221
|
+
<section className={`${WRAP} py-20`}>
|
|
222
|
+
<SectionHead
|
|
223
|
+
eyebrow={automations.eyebrow}
|
|
224
|
+
arrow
|
|
225
|
+
title={automations.headline}
|
|
226
|
+
body={automations.summary}
|
|
227
|
+
/>
|
|
228
|
+
<FeatureGrid className="mt-14" items={automations.features} />
|
|
229
|
+
<div className="mt-8">
|
|
230
|
+
<GhostLink href="/products/automations">
|
|
231
|
+
Explore Automations →
|
|
232
|
+
</GhostLink>
|
|
233
|
+
</div>
|
|
234
|
+
<div className="mt-12">
|
|
235
|
+
<Terminal />
|
|
236
|
+
</div>
|
|
237
|
+
</section>
|
|
238
|
+
|
|
239
|
+
{/* =================== TESTIMONIAL CARDS ===================== */}
|
|
240
|
+
<Divider />
|
|
241
|
+
<section id="customers" className={`${WRAP} py-20`}>
|
|
242
|
+
<SectionHead
|
|
243
|
+
eyebrow="Customers"
|
|
244
|
+
title="Teams that ship with Acme."
|
|
245
|
+
body="A few words from the people who run their work in Acme every day."
|
|
246
|
+
/>
|
|
247
|
+
<div className="mt-12 grid gap-5 sm:grid-cols-3">
|
|
248
|
+
{QUOTES.map((q) => (
|
|
249
|
+
<figure
|
|
250
|
+
key={q.name + q.role}
|
|
251
|
+
className="flex flex-col rounded-2xl border border-zinc-200 bg-paper p-6"
|
|
252
|
+
>
|
|
253
|
+
<blockquote className="text-[14px] leading-relaxed text-zinc-600">
|
|
254
|
+
“{q.quote}”
|
|
255
|
+
</blockquote>
|
|
256
|
+
<figcaption className="mt-6 flex items-center gap-3">
|
|
257
|
+
<span className="flex size-8 items-center justify-center rounded-full bg-zinc-200 text-[11px] font-semibold text-zinc-500">
|
|
258
|
+
{initials(q.name)}
|
|
259
|
+
</span>
|
|
260
|
+
<div className="leading-tight">
|
|
261
|
+
<div className="text-[13px] font-semibold">{q.name}</div>
|
|
262
|
+
<div className="text-[12px] text-zinc-500">{q.role}</div>
|
|
263
|
+
</div>
|
|
264
|
+
</figcaption>
|
|
265
|
+
</figure>
|
|
266
|
+
))}
|
|
267
|
+
</div>
|
|
268
|
+
</section>
|
|
269
|
+
|
|
270
|
+
{/* ===================== GETTING STARTED ==================== */}
|
|
271
|
+
<Divider />
|
|
272
|
+
<section className={`${WRAP} py-20`}>
|
|
273
|
+
<Eyebrow>Get started</Eyebrow>
|
|
274
|
+
<h2 className="mt-4 max-w-xl text-balance text-3xl font-semibold leading-[1.1] tracking-[-0.02em] sm:text-[2.5rem]">
|
|
275
|
+
Up and running in 60 seconds.
|
|
276
|
+
</h2>
|
|
277
|
+
<p className="mt-5 max-w-md text-[15px] leading-relaxed text-zinc-500">
|
|
278
|
+
No credit card, no sales call, no setup wizard. An email and a
|
|
279
|
+
workspace name are all it takes to start.
|
|
280
|
+
</p>
|
|
281
|
+
<div className="mt-8">
|
|
282
|
+
<PrimaryButton href={primaryHref}>{primaryLabel}</PrimaryButton>
|
|
283
|
+
</div>
|
|
284
|
+
<ol className="mt-14 max-w-xl space-y-8">
|
|
285
|
+
{STEPS.map((s, i) => (
|
|
286
|
+
<li key={s.title} className="flex gap-5">
|
|
287
|
+
<span className="flex size-7 shrink-0 items-center justify-center rounded-full bg-brand-soft font-mono text-[11px] text-brand">
|
|
288
|
+
0{i + 1}
|
|
289
|
+
</span>
|
|
290
|
+
<div>
|
|
291
|
+
<h3 className="text-[15px] font-semibold">{s.title}</h3>
|
|
292
|
+
<p className="mt-1.5 text-[14px] leading-relaxed text-zinc-500">
|
|
293
|
+
{s.body}
|
|
294
|
+
</p>
|
|
295
|
+
</div>
|
|
296
|
+
</li>
|
|
297
|
+
))}
|
|
298
|
+
</ol>
|
|
299
|
+
</section>
|
|
300
|
+
|
|
301
|
+
{/* ========================= PRICING ======================== */}
|
|
302
|
+
<Divider />
|
|
303
|
+
<section id="pricing" className={`${WRAP} py-20`}>
|
|
304
|
+
<Eyebrow>Pricing</Eyebrow>
|
|
305
|
+
<h2 className="mt-4 max-w-xl text-balance text-3xl font-semibold leading-[1.1] tracking-[-0.02em] sm:text-[2.5rem]">
|
|
306
|
+
Simple pricing. Every plan.
|
|
307
|
+
</h2>
|
|
308
|
+
<p className="mt-5 max-w-md text-[15px] leading-relaxed text-zinc-500">
|
|
309
|
+
Start free and upgrade when your team grows. No per-seat surprises, no
|
|
310
|
+
annual lock-in.
|
|
311
|
+
</p>
|
|
312
|
+
<div className="mt-12 grid gap-5 lg:grid-cols-3">
|
|
313
|
+
{PLANS.map((p) => (
|
|
314
|
+
<div
|
|
315
|
+
key={p.name}
|
|
316
|
+
className={`flex flex-col rounded-2xl border p-7 ${
|
|
317
|
+
p.featured
|
|
318
|
+
? "border-zinc-900 bg-white shadow-[0_24px_60px_-30px_rgba(0,0,0,0.3)]"
|
|
319
|
+
: "border-zinc-200 bg-paper"
|
|
320
|
+
}`}
|
|
321
|
+
>
|
|
322
|
+
<div className="flex items-center justify-between">
|
|
323
|
+
<h3 className="text-base font-semibold">{p.name}</h3>
|
|
324
|
+
{p.featured && (
|
|
325
|
+
<span className="font-mono text-[10px] uppercase tracking-[0.12em] text-brand">
|
|
326
|
+
Most popular
|
|
327
|
+
</span>
|
|
328
|
+
)}
|
|
329
|
+
</div>
|
|
330
|
+
<p className="mt-1 text-[13px] text-zinc-500">{p.tagline}</p>
|
|
331
|
+
<div className="mt-5 flex items-baseline gap-1">
|
|
332
|
+
<span className="text-4xl font-semibold tracking-tight">
|
|
333
|
+
{p.price}
|
|
334
|
+
</span>
|
|
335
|
+
<span className="text-[13px] text-zinc-500">{p.unit}</span>
|
|
336
|
+
</div>
|
|
337
|
+
<ul className="mt-6 flex-1 space-y-3 text-[14px] text-zinc-600">
|
|
338
|
+
{p.features.map((f) => (
|
|
339
|
+
<li key={f} className="flex gap-2.5">
|
|
340
|
+
<span className="mt-[3px] text-brand">✓</span>
|
|
341
|
+
{f}
|
|
342
|
+
</li>
|
|
343
|
+
))}
|
|
344
|
+
</ul>
|
|
345
|
+
<div className="mt-7">
|
|
346
|
+
{p.featured ? (
|
|
347
|
+
<PrimaryButton
|
|
348
|
+
href={primaryHref}
|
|
349
|
+
className="w-full justify-center"
|
|
350
|
+
>
|
|
351
|
+
{p.cta}
|
|
352
|
+
</PrimaryButton>
|
|
353
|
+
) : (
|
|
354
|
+
<Link
|
|
355
|
+
href={primaryHref}
|
|
356
|
+
className="inline-flex w-full items-center justify-center rounded-full border border-zinc-300 px-5 py-2.5 text-sm font-medium text-zinc-900 transition-colors hover:border-zinc-400 hover:bg-zinc-50"
|
|
357
|
+
>
|
|
358
|
+
{p.cta}
|
|
359
|
+
</Link>
|
|
360
|
+
)}
|
|
361
|
+
</div>
|
|
362
|
+
</div>
|
|
363
|
+
))}
|
|
364
|
+
</div>
|
|
365
|
+
</section>
|
|
366
|
+
|
|
367
|
+
{/* ====================== THE TEAM (2-col) ================== */}
|
|
368
|
+
<Divider />
|
|
369
|
+
<section className={`${WRAP} py-20`}>
|
|
370
|
+
<div className="grid gap-12 lg:grid-cols-[0.9fr_1.1fr]">
|
|
371
|
+
<div>
|
|
372
|
+
<Eyebrow>The team</Eyebrow>
|
|
373
|
+
<h2 className="mt-4 text-balance text-3xl font-semibold leading-[1.1] tracking-[-0.02em] sm:text-[2.5rem]">
|
|
374
|
+
Built by people who use it every day.
|
|
375
|
+
</h2>
|
|
376
|
+
<p className="mt-5 max-w-md text-[15px] leading-relaxed text-zinc-500">
|
|
377
|
+
Acme is built by a small team that got tired of clunky, overpriced
|
|
378
|
+
tools. Every feature earns its place by being something worth using
|
|
379
|
+
every day.
|
|
380
|
+
</p>
|
|
381
|
+
</div>
|
|
382
|
+
<FeatureGrid columns={2} items={TEAM} />
|
|
383
|
+
</div>
|
|
384
|
+
</section>
|
|
385
|
+
|
|
386
|
+
{/* ======================= FINAL CTA ======================== */}
|
|
387
|
+
<Divider />
|
|
388
|
+
<section className={`${WRAP} py-24`}>
|
|
389
|
+
<Eyebrow>Start building</Eyebrow>
|
|
390
|
+
<h2 className="mt-4 max-w-xl text-balance text-[2.5rem] font-semibold leading-[1.05] tracking-[-0.02em] sm:text-[3rem]">
|
|
391
|
+
Stop losing momentum to busywork.
|
|
392
|
+
</h2>
|
|
393
|
+
<p className="mt-6 max-w-lg text-[16px] leading-relaxed text-zinc-500">
|
|
394
|
+
When your team can see the work, decide what is next, and ship in one
|
|
395
|
+
place, momentum takes care of itself. This is what{" "}
|
|
396
|
+
<span className="rounded bg-brand-soft px-1.5 py-0.5 font-medium text-brand">
|
|
397
|
+
working in flow
|
|
398
|
+
</span>{" "}
|
|
399
|
+
looks like.
|
|
400
|
+
</p>
|
|
401
|
+
<div className="mt-8">
|
|
402
|
+
<PrimaryButton href={primaryHref}>
|
|
403
|
+
Start building with Acme
|
|
404
|
+
</PrimaryButton>
|
|
405
|
+
</div>
|
|
406
|
+
<p className="mt-4 text-[12px] text-zinc-400">
|
|
407
|
+
Free to start · No credit card · Cancel anytime
|
|
408
|
+
</p>
|
|
409
|
+
</section>
|
|
410
|
+
|
|
411
|
+
{/* ========================== FAQ =========================== */}
|
|
412
|
+
<Divider />
|
|
413
|
+
<section id="faq" className={`${WRAP} py-20`}>
|
|
414
|
+
<Eyebrow>Questions</Eyebrow>
|
|
415
|
+
<h2 className="mt-4 text-balance text-3xl font-semibold leading-[1.1] tracking-[-0.02em] sm:text-[2.5rem]">
|
|
416
|
+
Frequently asked.
|
|
417
|
+
</h2>
|
|
418
|
+
<div className="mt-10 divide-y divide-zinc-200/70 border-y border-zinc-200/70">
|
|
419
|
+
{FAQ.map((f) => (
|
|
420
|
+
<details key={f.q} className="group py-5">
|
|
421
|
+
<summary className="flex cursor-pointer items-center justify-between text-[15px] font-medium text-zinc-900 marker:hidden [&::-webkit-details-marker]:hidden">
|
|
422
|
+
{f.q}
|
|
423
|
+
<span className="text-brand transition-transform group-open:rotate-45">
|
|
424
|
+
+
|
|
425
|
+
</span>
|
|
426
|
+
</summary>
|
|
427
|
+
<p className="mt-3 max-w-2xl text-[14px] leading-relaxed text-zinc-500">
|
|
428
|
+
{f.a}
|
|
429
|
+
</p>
|
|
430
|
+
</details>
|
|
431
|
+
))}
|
|
432
|
+
</div>
|
|
433
|
+
</section>
|
|
434
|
+
</div>
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// A homepage feature section for one product: eyebrow + headline + grid +
|
|
439
|
+
// "Explore →" link to its /products/[slug] page + a product mockup.
|
|
440
|
+
function ProductSection({
|
|
441
|
+
product,
|
|
442
|
+
primaryHref,
|
|
443
|
+
id,
|
|
444
|
+
}: {
|
|
445
|
+
product: (typeof PRODUCTS)[number];
|
|
446
|
+
primaryHref: string;
|
|
447
|
+
id?: string;
|
|
448
|
+
}) {
|
|
449
|
+
return (
|
|
450
|
+
<section id={id} className={`${WRAP} py-20`}>
|
|
451
|
+
<SectionHead
|
|
452
|
+
eyebrow={product.eyebrow}
|
|
453
|
+
arrow
|
|
454
|
+
title={product.headline}
|
|
455
|
+
body={product.summary}
|
|
456
|
+
/>
|
|
457
|
+
<FeatureGrid className="mt-14" items={product.features.slice(0, 6)} />
|
|
458
|
+
<div className="mt-8">
|
|
459
|
+
<GhostLink href={`/products/${product.slug}`}>
|
|
460
|
+
Explore {product.title} →
|
|
461
|
+
</GhostLink>
|
|
462
|
+
</div>
|
|
463
|
+
<div className="mt-12">
|
|
464
|
+
<Shot url={product.mockupUrl} label={product.mockupLabel} />
|
|
465
|
+
</div>
|
|
466
|
+
</section>
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// Initials for the testimonial avatars, so the cards look finished without a
|
|
471
|
+
// real photo. Drop in an <img> when you have one.
|
|
472
|
+
function initials(name: string) {
|
|
473
|
+
return name
|
|
474
|
+
.split(/\s+/)
|
|
475
|
+
.map((w) => w[0])
|
|
476
|
+
.join("")
|
|
477
|
+
.slice(0, 2)
|
|
478
|
+
.toUpperCase();
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/* ---------------------------------------------------------------- */
|
|
482
|
+
/* Homepage-specific content — fictional demo copy; swap for yours. */
|
|
483
|
+
/* ---------------------------------------------------------------- */
|
|
484
|
+
|
|
485
|
+
const OUTCOMES = [
|
|
486
|
+
{
|
|
487
|
+
title: "Work in one place",
|
|
488
|
+
body: "Plans, tasks, and docs live together, so nothing gets lost between tools.",
|
|
489
|
+
},
|
|
490
|
+
{
|
|
491
|
+
title: "Clear priorities",
|
|
492
|
+
body: "Everyone can see what is planned, in progress, and done — and why.",
|
|
493
|
+
},
|
|
494
|
+
{
|
|
495
|
+
title: "Real-time by default",
|
|
496
|
+
body: "Every change syncs instantly, so the workspace is the same on every screen.",
|
|
497
|
+
},
|
|
498
|
+
{
|
|
499
|
+
title: "Less busywork",
|
|
500
|
+
body: "Automations handle the routine, so your team focuses on the work that matters.",
|
|
501
|
+
},
|
|
502
|
+
];
|
|
503
|
+
|
|
504
|
+
const ENTRY_POINTS = [
|
|
505
|
+
{
|
|
506
|
+
icon: "◇",
|
|
507
|
+
title: "Cloud workspace",
|
|
508
|
+
body: "Your whole team works in the browser. Nothing to install, always up to date, and secure by default.",
|
|
509
|
+
},
|
|
510
|
+
{
|
|
511
|
+
icon: "◆",
|
|
512
|
+
title: "Open API",
|
|
513
|
+
body: "A typed API and webhooks for everything in Acme, so you can wire it into the rest of your stack.",
|
|
514
|
+
},
|
|
515
|
+
];
|
|
516
|
+
|
|
517
|
+
const ENGAGEMENT = [
|
|
518
|
+
{
|
|
519
|
+
title: "The loop",
|
|
520
|
+
body: "Submission, acknowledgment, progress, launch. Four moments every person gets to feel heard.",
|
|
521
|
+
},
|
|
522
|
+
{
|
|
523
|
+
title: "Instant alerts",
|
|
524
|
+
body: "The moment someone votes or comments on their idea, social proof brings them back.",
|
|
525
|
+
},
|
|
526
|
+
{
|
|
527
|
+
title: "Launch alerts",
|
|
528
|
+
body: "The note that tells a person the thing they asked for just shipped. Nothing builds loyalty faster.",
|
|
529
|
+
},
|
|
530
|
+
{
|
|
531
|
+
title: "Weekly digest",
|
|
532
|
+
body: "The top ideas across your boards, delivered every week, with a one-click way to weigh in.",
|
|
533
|
+
},
|
|
534
|
+
];
|
|
535
|
+
|
|
536
|
+
const QUOTES = [
|
|
537
|
+
{
|
|
538
|
+
quote:
|
|
539
|
+
"We finally have one source of truth for the work. The whole team can see what is happening without a status meeting.",
|
|
540
|
+
name: "Daniel Reyes",
|
|
541
|
+
role: "Founder, Globex",
|
|
542
|
+
},
|
|
543
|
+
{
|
|
544
|
+
quote:
|
|
545
|
+
"Acme noticeably improved how we plan. Instead of piecing together five tools, we have one hub for everything.",
|
|
546
|
+
name: "Priya Sharma",
|
|
547
|
+
role: "Founder, OpenLane",
|
|
548
|
+
},
|
|
549
|
+
{
|
|
550
|
+
quote:
|
|
551
|
+
"Ever since we added Acme, people actually feel heard. It has helped us build a loyal community of users.",
|
|
552
|
+
name: "Marcus Bell",
|
|
553
|
+
role: "Cofounder, Initech",
|
|
554
|
+
},
|
|
555
|
+
];
|
|
556
|
+
|
|
557
|
+
const STEPS = [
|
|
558
|
+
{
|
|
559
|
+
title: "Create a workspace",
|
|
560
|
+
body: "Sign up with just an email. Pick a name. That is the entire form.",
|
|
561
|
+
},
|
|
562
|
+
{
|
|
563
|
+
title: "Invite your team",
|
|
564
|
+
body: "Add teammates, share the board URL, or link it from your site. Work starts flowing in.",
|
|
565
|
+
},
|
|
566
|
+
{
|
|
567
|
+
title: "Watch the loop start",
|
|
568
|
+
body: "People add ideas, votes pile up, you ship, and everyone hears about it and comes back with more.",
|
|
569
|
+
},
|
|
570
|
+
];
|
|
571
|
+
|
|
572
|
+
const PLANS = [
|
|
573
|
+
{
|
|
574
|
+
name: "Free",
|
|
575
|
+
tagline: "For getting started.",
|
|
576
|
+
price: "$0",
|
|
577
|
+
unit: "forever",
|
|
578
|
+
cta: "Get started",
|
|
579
|
+
featured: false,
|
|
580
|
+
features: [
|
|
581
|
+
"Up to 3 projects",
|
|
582
|
+
"Unlimited members",
|
|
583
|
+
"Real-time board",
|
|
584
|
+
"Community support",
|
|
585
|
+
],
|
|
586
|
+
},
|
|
587
|
+
{
|
|
588
|
+
name: "Team",
|
|
589
|
+
tagline: "For small teams.",
|
|
590
|
+
price: "$29",
|
|
591
|
+
unit: "/ month",
|
|
592
|
+
cta: "Start free trial",
|
|
593
|
+
featured: true,
|
|
594
|
+
features: [
|
|
595
|
+
"Unlimited projects",
|
|
596
|
+
"Roadmap and updates",
|
|
597
|
+
"Private boards",
|
|
598
|
+
"Priority support",
|
|
599
|
+
],
|
|
600
|
+
},
|
|
601
|
+
{
|
|
602
|
+
name: "Business",
|
|
603
|
+
tagline: "For growing teams.",
|
|
604
|
+
price: "$59",
|
|
605
|
+
unit: "/ month",
|
|
606
|
+
cta: "Start free trial",
|
|
607
|
+
featured: false,
|
|
608
|
+
features: [
|
|
609
|
+
"Everything in Team",
|
|
610
|
+
"SSO and audit log",
|
|
611
|
+
"Custom domain",
|
|
612
|
+
"Onboarding help",
|
|
613
|
+
],
|
|
614
|
+
},
|
|
615
|
+
];
|
|
616
|
+
|
|
617
|
+
const TEAM = [
|
|
618
|
+
{
|
|
619
|
+
title: "A small team",
|
|
620
|
+
body: "No committees. Every feature ships because someone decided it was worth building.",
|
|
621
|
+
},
|
|
622
|
+
{
|
|
623
|
+
title: "Design first",
|
|
624
|
+
body: "Beautiful software makes people want to use it. Every screen is built with that in mind.",
|
|
625
|
+
},
|
|
626
|
+
{
|
|
627
|
+
title: "Ships every week",
|
|
628
|
+
body: "Updates go out weekly, often based on work posted directly to our own board.",
|
|
629
|
+
},
|
|
630
|
+
{
|
|
631
|
+
title: "Customer funded",
|
|
632
|
+
body: "We optimize for your renewal, not an exit. If Acme works for you, it works for us.",
|
|
633
|
+
},
|
|
634
|
+
];
|
|
635
|
+
|
|
636
|
+
const FAQ = [
|
|
637
|
+
{
|
|
638
|
+
q: "Is Acme a fit for my team?",
|
|
639
|
+
a: "Acme works for any team that plans and ships work together — product, design, engineering, or ops.",
|
|
640
|
+
},
|
|
641
|
+
{
|
|
642
|
+
q: "What about migrating from another tool?",
|
|
643
|
+
a: "Import your existing projects and pick up where you left off.",
|
|
644
|
+
},
|
|
645
|
+
{
|
|
646
|
+
q: "Is there an API or a way to script this?",
|
|
647
|
+
a: "Yes — every action in Acme is available over a typed API and an MCP server.",
|
|
648
|
+
},
|
|
649
|
+
{
|
|
650
|
+
q: "Do you offer SSO?",
|
|
651
|
+
a: "SSO and audit logging are included on the Business plan.",
|
|
652
|
+
},
|
|
653
|
+
];
|