ship-create 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -0
- package/create.mjs +301 -0
- package/package.json +25 -0
- package/templates/.cursorrules +51 -0
- package/templates/.windsurfrules +51 -0
- package/templates/AGENTS.md +51 -0
- package/templates/CLAUDE.md +51 -0
- package/templates/docs/HUMAN_FLOW.md +205 -0
- package/templates/docs/PROJECT.md +154 -0
- package/templates/docs/PROMPTS.md +246 -0
- package/templates/docs/product-types/CRM_TEMPLATE.md +78 -0
- package/templates/docs/product-types/DASHBOARD_TEMPLATE.md +78 -0
- package/templates/docs/product-types/DIRECTORY_TEMPLATE.md +75 -0
- package/templates/docs/product-types/INTERNAL_TOOL_TEMPLATE.md +77 -0
- package/templates/docs/product-types/LEADGEN_TEMPLATE.md +78 -0
- package/templates/docs/product-types/MARKETPLACE_TEMPLATE.md +81 -0
- package/templates/docs/product-types/MEMBERSHIP_TEMPLATE.md +80 -0
- package/templates/docs/product-types/SAAS_TEMPLATE.md +79 -0
- package/templates/starter-kit/README.md +64 -0
- package/templates/starter-kit/app/backoffice/content/page.tsx +93 -0
- package/templates/starter-kit/app/backoffice/layout.tsx +105 -0
- package/templates/starter-kit/app/backoffice/page.tsx +165 -0
- package/templates/starter-kit/app/backoffice/settings/page.tsx +145 -0
- package/templates/starter-kit/app/backoffice/users/page.tsx +134 -0
- package/templates/starter-kit/app/globals.css +141 -0
- package/templates/starter-kit/app/layout.tsx +43 -0
- package/templates/starter-kit/app/member/(app)/billing/page.tsx +137 -0
- package/templates/starter-kit/app/member/(app)/content/page.tsx +111 -0
- package/templates/starter-kit/app/member/(app)/dashboard/page.tsx +129 -0
- package/templates/starter-kit/app/member/(app)/layout.tsx +130 -0
- package/templates/starter-kit/app/member/(app)/settings/page.tsx +96 -0
- package/templates/starter-kit/app/member/login/page.tsx +106 -0
- package/templates/starter-kit/app/member/signup/page.tsx +120 -0
- package/templates/starter-kit/app/page.tsx +82 -0
- package/templates/starter-kit/app/sale/_components/cta-footer.tsx +66 -0
- package/templates/starter-kit/app/sale/_components/faq.tsx +107 -0
- package/templates/starter-kit/app/sale/_components/features.tsx +95 -0
- package/templates/starter-kit/app/sale/_components/hero.tsx +106 -0
- package/templates/starter-kit/app/sale/_components/pricing.tsx +133 -0
- package/templates/starter-kit/app/sale/_components/problem.tsx +59 -0
- package/templates/starter-kit/app/sale/_components/testimonials.tsx +100 -0
- package/templates/starter-kit/app/sale/page.tsx +21 -0
- package/templates/starter-kit/components/ui/avatar.tsx +50 -0
- package/templates/starter-kit/components/ui/badge.tsx +36 -0
- package/templates/starter-kit/components/ui/button.tsx +56 -0
- package/templates/starter-kit/components/ui/card.tsx +78 -0
- package/templates/starter-kit/components/ui/input.tsx +24 -0
- package/templates/starter-kit/components/ui/table.tsx +88 -0
- package/templates/starter-kit/components/ui/tabs.tsx +55 -0
- package/templates/starter-kit/lib/mock-data.ts +118 -0
- package/templates/starter-kit/lib/utils.ts +6 -0
- package/templates/starter-kit/next.config.mjs +6 -0
- package/templates/starter-kit/package.json +36 -0
- package/templates/starter-kit/postcss.config.mjs +9 -0
- package/templates/starter-kit/tailwind.config.ts +83 -0
- package/templates/starter-kit/tsconfig.json +41 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { Plus } from "lucide-react";
|
|
5
|
+
|
|
6
|
+
import { cn } from "@/lib/utils";
|
|
7
|
+
|
|
8
|
+
const faqs = [
|
|
9
|
+
{
|
|
10
|
+
question: "Can I try it before I pay?",
|
|
11
|
+
answer:
|
|
12
|
+
"Yes — every plan includes a free 14-day trial with full access to features. No credit card required to start.",
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
question: "Can I change plans later?",
|
|
16
|
+
answer:
|
|
17
|
+
"Absolutely. You can upgrade, downgrade, or cancel at any time from your account settings — changes apply immediately.",
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
question: "Is my data secure?",
|
|
21
|
+
answer:
|
|
22
|
+
"All data is encrypted in transit and at rest. Business plans also include SSO, audit logs, and role-based permissions.",
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
question: "Do you offer discounts for annual billing?",
|
|
26
|
+
answer:
|
|
27
|
+
"Yes, switching to annual billing saves roughly two months compared to paying monthly. You can switch from your billing page.",
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
question: "What kind of support do you provide?",
|
|
31
|
+
answer:
|
|
32
|
+
"Starter includes email support, Pro includes priority support, and Business includes a dedicated success manager.",
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
question: "Can I cancel anytime?",
|
|
36
|
+
answer:
|
|
37
|
+
"Yes, there are no long-term contracts. You can cancel from your account at any time and you won't be charged again.",
|
|
38
|
+
},
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
export function Faq() {
|
|
42
|
+
const [openIndex, setOpenIndex] = useState<number | null>(0);
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<section className="container mx-auto px-4 py-28">
|
|
46
|
+
<div className="mx-auto mb-16 flex max-w-2xl flex-col items-center gap-3 text-center">
|
|
47
|
+
<p className="label-mono text-primary">FAQ</p>
|
|
48
|
+
<h2 className="font-display text-4xl font-medium tracking-tight sm:text-5xl">
|
|
49
|
+
Frequently asked{" "}
|
|
50
|
+
<span className="italic text-primary">questions</span>
|
|
51
|
+
</h2>
|
|
52
|
+
<p className="mt-2 text-muted-foreground">
|
|
53
|
+
Everything you need to know before you get started.
|
|
54
|
+
</p>
|
|
55
|
+
</div>
|
|
56
|
+
<div className="mx-auto max-w-2xl divide-y divide-border rounded-2xl border border-border">
|
|
57
|
+
{faqs.map((faq, index) => {
|
|
58
|
+
const isOpen = openIndex === index;
|
|
59
|
+
return (
|
|
60
|
+
<div key={faq.question}>
|
|
61
|
+
<button
|
|
62
|
+
type="button"
|
|
63
|
+
onClick={() => setOpenIndex(isOpen ? null : index)}
|
|
64
|
+
className="flex w-full items-center gap-4 px-6 py-5 text-left transition-colors hover:bg-muted/40"
|
|
65
|
+
aria-expanded={isOpen}
|
|
66
|
+
>
|
|
67
|
+
<span className="label-mono shrink-0 text-muted-foreground/50">
|
|
68
|
+
{String(index + 1).padStart(2, "0")}
|
|
69
|
+
</span>
|
|
70
|
+
<span
|
|
71
|
+
className={cn(
|
|
72
|
+
"flex-1 text-sm font-medium transition-colors",
|
|
73
|
+
isOpen && "text-primary"
|
|
74
|
+
)}
|
|
75
|
+
>
|
|
76
|
+
{faq.question}
|
|
77
|
+
</span>
|
|
78
|
+
<span
|
|
79
|
+
className={cn(
|
|
80
|
+
"flex h-7 w-7 shrink-0 items-center justify-center rounded-full border border-border text-muted-foreground transition-all duration-300",
|
|
81
|
+
isOpen && "rotate-45 border-primary/50 text-primary"
|
|
82
|
+
)}
|
|
83
|
+
>
|
|
84
|
+
<Plus className="h-3.5 w-3.5" />
|
|
85
|
+
</span>
|
|
86
|
+
</button>
|
|
87
|
+
<div
|
|
88
|
+
className={cn(
|
|
89
|
+
"grid overflow-hidden transition-all duration-300 ease-out",
|
|
90
|
+
isOpen
|
|
91
|
+
? "grid-rows-[1fr] opacity-100"
|
|
92
|
+
: "grid-rows-[0fr] opacity-0"
|
|
93
|
+
)}
|
|
94
|
+
>
|
|
95
|
+
<div className="overflow-hidden">
|
|
96
|
+
<p className="px-6 pb-5 pl-16 text-sm leading-relaxed text-muted-foreground">
|
|
97
|
+
{faq.answer}
|
|
98
|
+
</p>
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
);
|
|
103
|
+
})}
|
|
104
|
+
</div>
|
|
105
|
+
</section>
|
|
106
|
+
);
|
|
107
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BarChart3,
|
|
3
|
+
Bell,
|
|
4
|
+
Layers,
|
|
5
|
+
Lock,
|
|
6
|
+
Workflow,
|
|
7
|
+
Zap,
|
|
8
|
+
} from "lucide-react";
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
Card,
|
|
12
|
+
CardContent,
|
|
13
|
+
CardDescription,
|
|
14
|
+
CardHeader,
|
|
15
|
+
CardTitle,
|
|
16
|
+
} from "@/components/ui/card";
|
|
17
|
+
|
|
18
|
+
const features = [
|
|
19
|
+
{
|
|
20
|
+
icon: Workflow,
|
|
21
|
+
title: "Unified workflows",
|
|
22
|
+
description:
|
|
23
|
+
"Build pipelines that connect every step of your process, from intake to delivery, without switching tools.",
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
icon: BarChart3,
|
|
27
|
+
title: "Real-time analytics",
|
|
28
|
+
description:
|
|
29
|
+
"See what's working and what's not with dashboards that update the moment your data changes.",
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
icon: Bell,
|
|
33
|
+
title: "Smart notifications",
|
|
34
|
+
description:
|
|
35
|
+
"Only get pinged when it matters. Configurable alerts keep noise down and signal up.",
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
icon: Layers,
|
|
39
|
+
title: "Flexible templates",
|
|
40
|
+
description:
|
|
41
|
+
"Start from a proven template or build your own — every workspace adapts to how your team actually works.",
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
icon: Lock,
|
|
45
|
+
title: "Enterprise-grade security",
|
|
46
|
+
description:
|
|
47
|
+
"Role-based access, audit logs, and encryption at rest keep your data safe by default.",
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
icon: Zap,
|
|
51
|
+
title: "Built for speed",
|
|
52
|
+
description:
|
|
53
|
+
"A fast, responsive interface so your team spends time working, not waiting on spinners.",
|
|
54
|
+
},
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
export function Features() {
|
|
58
|
+
return (
|
|
59
|
+
<section className="bg-grid relative py-28">
|
|
60
|
+
<div className="container relative mx-auto px-4">
|
|
61
|
+
<div className="mx-auto mb-16 flex max-w-2xl flex-col items-center gap-3 text-center">
|
|
62
|
+
<p className="label-mono text-primary">Capabilities</p>
|
|
63
|
+
<h2 className="font-display text-4xl font-medium tracking-tight sm:text-5xl">
|
|
64
|
+
Everything your team{" "}
|
|
65
|
+
<span className="italic text-primary">needs</span>
|
|
66
|
+
</h2>
|
|
67
|
+
<p className="mt-2 text-muted-foreground">
|
|
68
|
+
One platform, every workflow — no more duct-taping five tools
|
|
69
|
+
together.
|
|
70
|
+
</p>
|
|
71
|
+
</div>
|
|
72
|
+
<div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
|
|
73
|
+
{features.map((feature) => {
|
|
74
|
+
const Icon = feature.icon;
|
|
75
|
+
return (
|
|
76
|
+
<Card
|
|
77
|
+
key={feature.title}
|
|
78
|
+
className="bg-card/90 backdrop-blur transition-transform duration-300 hover:-translate-y-1"
|
|
79
|
+
>
|
|
80
|
+
<CardHeader>
|
|
81
|
+
<div className="mb-3 flex h-11 w-11 items-center justify-center rounded-full bg-primary/15 text-primary">
|
|
82
|
+
<Icon className="h-5 w-5" />
|
|
83
|
+
</div>
|
|
84
|
+
<CardTitle>{feature.title}</CardTitle>
|
|
85
|
+
<CardDescription>{feature.description}</CardDescription>
|
|
86
|
+
</CardHeader>
|
|
87
|
+
<CardContent />
|
|
88
|
+
</Card>
|
|
89
|
+
);
|
|
90
|
+
})}
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
|
93
|
+
</section>
|
|
94
|
+
);
|
|
95
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { ArrowRight } from "lucide-react";
|
|
2
|
+
|
|
3
|
+
import { Button } from "@/components/ui/button";
|
|
4
|
+
|
|
5
|
+
export function Hero() {
|
|
6
|
+
return (
|
|
7
|
+
<section className="relative overflow-hidden pb-24 pt-28 md:pt-36">
|
|
8
|
+
{/* Decorative grid panel, off to the right, faded */}
|
|
9
|
+
<div className="bg-grid pointer-events-none absolute inset-y-0 right-0 hidden w-1/2 [mask-image:linear-gradient(to_left,black,transparent)] lg:block" />
|
|
10
|
+
|
|
11
|
+
{/* Soft ember glow blob behind the headline */}
|
|
12
|
+
<div className="pointer-events-none absolute -left-32 top-10 h-[28rem] w-[28rem] rounded-full bg-primary/20 blur-[110px]" />
|
|
13
|
+
|
|
14
|
+
{/* Giant faint serif numeral as a decorative mark */}
|
|
15
|
+
<span
|
|
16
|
+
aria-hidden
|
|
17
|
+
className="pointer-events-none absolute -right-6 top-4 select-none font-display text-[16rem] font-medium leading-none text-foreground/[0.04] sm:text-[22rem]"
|
|
18
|
+
>
|
|
19
|
+
01
|
|
20
|
+
</span>
|
|
21
|
+
|
|
22
|
+
<div className="container relative mx-auto px-4">
|
|
23
|
+
<div className="grid items-end gap-12 lg:grid-cols-[1.15fr_0.85fr]">
|
|
24
|
+
<div className="flex flex-col items-start gap-7 text-left">
|
|
25
|
+
<span
|
|
26
|
+
className="label-mono animate-fade-up inline-flex items-center gap-2 text-primary"
|
|
27
|
+
style={{ animationDelay: "0s" }}
|
|
28
|
+
>
|
|
29
|
+
<span className="h-1.5 w-1.5 rounded-full bg-primary" />
|
|
30
|
+
Now in public beta
|
|
31
|
+
</span>
|
|
32
|
+
|
|
33
|
+
<h1
|
|
34
|
+
className="animate-fade-up max-w-2xl font-display text-5xl font-medium leading-[1.05] tracking-tight sm:text-6xl md:text-7xl"
|
|
35
|
+
style={{ animationDelay: "0.1s" }}
|
|
36
|
+
>
|
|
37
|
+
Manage your entire workflow{" "}
|
|
38
|
+
<span className="relative inline-block italic text-primary">
|
|
39
|
+
in one place
|
|
40
|
+
<svg
|
|
41
|
+
aria-hidden
|
|
42
|
+
viewBox="0 0 300 18"
|
|
43
|
+
className="absolute -bottom-2 left-0 h-3 w-full text-primary/70"
|
|
44
|
+
preserveAspectRatio="none"
|
|
45
|
+
>
|
|
46
|
+
<path
|
|
47
|
+
d="M2 13C58 4 130 2 178 7C220 11 262 13 298 6"
|
|
48
|
+
fill="none"
|
|
49
|
+
stroke="currentColor"
|
|
50
|
+
strokeWidth="4"
|
|
51
|
+
strokeLinecap="round"
|
|
52
|
+
/>
|
|
53
|
+
</svg>
|
|
54
|
+
</span>
|
|
55
|
+
</h1>
|
|
56
|
+
|
|
57
|
+
<p
|
|
58
|
+
className="animate-fade-up max-w-md text-lg leading-relaxed text-muted-foreground"
|
|
59
|
+
style={{ animationDelay: "0.2s" }}
|
|
60
|
+
>
|
|
61
|
+
Replace your spreadsheets, sticky notes, and ten disconnected
|
|
62
|
+
tools with a single platform built for teams who want to move
|
|
63
|
+
faster.
|
|
64
|
+
</p>
|
|
65
|
+
|
|
66
|
+
<div
|
|
67
|
+
className="animate-fade-up flex flex-col gap-3 sm:flex-row"
|
|
68
|
+
style={{ animationDelay: "0.3s" }}
|
|
69
|
+
>
|
|
70
|
+
<Button size="lg" className="glow-primary gap-2">
|
|
71
|
+
Get Started
|
|
72
|
+
<ArrowRight className="h-4 w-4" />
|
|
73
|
+
</Button>
|
|
74
|
+
<Button size="lg" variant="outline">
|
|
75
|
+
See Pricing
|
|
76
|
+
</Button>
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
<p
|
|
80
|
+
className="label-mono animate-fade-up text-muted-foreground"
|
|
81
|
+
style={{ animationDelay: "0.4s" }}
|
|
82
|
+
>
|
|
83
|
+
No credit card required · Free 14-day trial
|
|
84
|
+
</p>
|
|
85
|
+
</div>
|
|
86
|
+
|
|
87
|
+
<div
|
|
88
|
+
className="animate-fade-up relative"
|
|
89
|
+
style={{ animationDelay: "0.25s" }}
|
|
90
|
+
>
|
|
91
|
+
<div className="bg-grid relative aspect-[4/5] w-full overflow-hidden rounded-2xl border border-border bg-card shadow-soft">
|
|
92
|
+
<div className="absolute inset-0 bg-gradient-to-br from-primary/15 via-transparent to-transparent" />
|
|
93
|
+
<div className="absolute bottom-6 left-6 right-6 rounded-xl border border-border bg-background/80 p-4 backdrop-blur">
|
|
94
|
+
<p className="label-mono text-primary">Live workspace</p>
|
|
95
|
+
<p className="mt-1 font-display text-lg">
|
|
96
|
+
Everything in sync, automatically.
|
|
97
|
+
</p>
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
<div className="absolute -bottom-5 -left-5 hidden h-20 w-20 rounded-full border border-primary/40 bg-primary/20 blur-sm sm:block" />
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
103
|
+
</div>
|
|
104
|
+
</section>
|
|
105
|
+
);
|
|
106
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { Check } from "lucide-react";
|
|
2
|
+
|
|
3
|
+
import { Badge } from "@/components/ui/badge";
|
|
4
|
+
import { Button } from "@/components/ui/button";
|
|
5
|
+
import {
|
|
6
|
+
Card,
|
|
7
|
+
CardContent,
|
|
8
|
+
CardDescription,
|
|
9
|
+
CardFooter,
|
|
10
|
+
CardHeader,
|
|
11
|
+
CardTitle,
|
|
12
|
+
} from "@/components/ui/card";
|
|
13
|
+
import { cn } from "@/lib/utils";
|
|
14
|
+
|
|
15
|
+
const tiers = [
|
|
16
|
+
{
|
|
17
|
+
name: "Starter",
|
|
18
|
+
price: "$19",
|
|
19
|
+
period: "/mo",
|
|
20
|
+
description: "For individuals and small teams getting started.",
|
|
21
|
+
features: [
|
|
22
|
+
"Up to 3 workspaces",
|
|
23
|
+
"Basic analytics",
|
|
24
|
+
"Email support",
|
|
25
|
+
"1 GB storage",
|
|
26
|
+
],
|
|
27
|
+
cta: "Start Free Trial",
|
|
28
|
+
highlighted: false,
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: "Pro",
|
|
32
|
+
price: "$49",
|
|
33
|
+
period: "/mo",
|
|
34
|
+
description: "For growing teams that need more power and automation.",
|
|
35
|
+
features: [
|
|
36
|
+
"Unlimited workspaces",
|
|
37
|
+
"Advanced analytics",
|
|
38
|
+
"Priority support",
|
|
39
|
+
"50 GB storage",
|
|
40
|
+
"Custom integrations",
|
|
41
|
+
],
|
|
42
|
+
cta: "Start Free Trial",
|
|
43
|
+
highlighted: true,
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
name: "Business",
|
|
47
|
+
price: "$129",
|
|
48
|
+
period: "/mo",
|
|
49
|
+
description: "For organizations that need scale, security, and control.",
|
|
50
|
+
features: [
|
|
51
|
+
"Everything in Pro",
|
|
52
|
+
"SSO & advanced permissions",
|
|
53
|
+
"Dedicated success manager",
|
|
54
|
+
"Unlimited storage",
|
|
55
|
+
"SLA & audit logs",
|
|
56
|
+
],
|
|
57
|
+
cta: "Contact Sales",
|
|
58
|
+
highlighted: false,
|
|
59
|
+
},
|
|
60
|
+
];
|
|
61
|
+
|
|
62
|
+
export function Pricing() {
|
|
63
|
+
return (
|
|
64
|
+
<section className="container mx-auto px-4 py-28">
|
|
65
|
+
<div className="mx-auto mb-16 flex max-w-2xl flex-col items-center gap-3 text-center">
|
|
66
|
+
<p className="label-mono text-primary">Pricing</p>
|
|
67
|
+
<h2 className="font-display text-4xl font-medium tracking-tight sm:text-5xl">
|
|
68
|
+
Simple, <span className="italic text-primary">transparent</span>{" "}
|
|
69
|
+
pricing
|
|
70
|
+
</h2>
|
|
71
|
+
<p className="mt-2 text-muted-foreground">
|
|
72
|
+
Choose the plan that fits your team. Upgrade or downgrade anytime.
|
|
73
|
+
</p>
|
|
74
|
+
</div>
|
|
75
|
+
<div className="grid items-center gap-8 lg:grid-cols-3">
|
|
76
|
+
{tiers.map((tier) => (
|
|
77
|
+
<Card
|
|
78
|
+
key={tier.name}
|
|
79
|
+
className={cn(
|
|
80
|
+
"relative flex flex-col transition-transform duration-300",
|
|
81
|
+
tier.highlighted &&
|
|
82
|
+
"glow-primary z-10 border-primary/60 lg:scale-110"
|
|
83
|
+
)}
|
|
84
|
+
>
|
|
85
|
+
{tier.highlighted && (
|
|
86
|
+
<Badge className="absolute -top-3 left-1/2 -translate-x-1/2">
|
|
87
|
+
Most Popular
|
|
88
|
+
</Badge>
|
|
89
|
+
)}
|
|
90
|
+
<CardHeader>
|
|
91
|
+
<p className="label-mono text-muted-foreground">{tier.name}</p>
|
|
92
|
+
<CardTitle
|
|
93
|
+
className={cn(
|
|
94
|
+
"text-2xl",
|
|
95
|
+
tier.highlighted && "text-primary"
|
|
96
|
+
)}
|
|
97
|
+
>
|
|
98
|
+
{tier.description}
|
|
99
|
+
</CardTitle>
|
|
100
|
+
<div className="mt-4 flex items-baseline gap-1">
|
|
101
|
+
<span className="font-display text-5xl font-medium tracking-tight">
|
|
102
|
+
{tier.price}
|
|
103
|
+
</span>
|
|
104
|
+
<span className="text-sm text-muted-foreground">
|
|
105
|
+
{tier.period}
|
|
106
|
+
</span>
|
|
107
|
+
</div>
|
|
108
|
+
</CardHeader>
|
|
109
|
+
<CardContent className="flex-1">
|
|
110
|
+
<ul className="space-y-3">
|
|
111
|
+
{tier.features.map((feature) => (
|
|
112
|
+
<li key={feature} className="flex items-start gap-2 text-sm">
|
|
113
|
+
<Check className="mt-0.5 h-4 w-4 shrink-0 text-success" />
|
|
114
|
+
<span>{feature}</span>
|
|
115
|
+
</li>
|
|
116
|
+
))}
|
|
117
|
+
</ul>
|
|
118
|
+
</CardContent>
|
|
119
|
+
<CardFooter>
|
|
120
|
+
<Button
|
|
121
|
+
className="w-full"
|
|
122
|
+
size={tier.highlighted ? "lg" : "default"}
|
|
123
|
+
variant={tier.highlighted ? "default" : "outline"}
|
|
124
|
+
>
|
|
125
|
+
{tier.cta}
|
|
126
|
+
</Button>
|
|
127
|
+
</CardFooter>
|
|
128
|
+
</Card>
|
|
129
|
+
))}
|
|
130
|
+
</div>
|
|
131
|
+
</section>
|
|
132
|
+
);
|
|
133
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { AlertTriangle, Clock, FolderX } from "lucide-react";
|
|
2
|
+
|
|
3
|
+
const problems = [
|
|
4
|
+
{
|
|
5
|
+
icon: Clock,
|
|
6
|
+
title: "Hours lost to busywork",
|
|
7
|
+
description:
|
|
8
|
+
"Your team spends more time updating status docs than actually doing the work that moves the business forward.",
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
icon: FolderX,
|
|
12
|
+
title: "Context scattered everywhere",
|
|
13
|
+
description:
|
|
14
|
+
"Decisions live in chat threads, files live in five different drives, and nobody can find the latest version of anything.",
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
icon: AlertTriangle,
|
|
18
|
+
title: "No visibility until it's too late",
|
|
19
|
+
description:
|
|
20
|
+
"Problems surface in a status meeting instead of the moment they happen, so small issues turn into missed deadlines.",
|
|
21
|
+
},
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
export function Problem() {
|
|
25
|
+
return (
|
|
26
|
+
<section className="container mx-auto px-4 py-28">
|
|
27
|
+
<div className="mb-16 grid gap-6 lg:grid-cols-[0.6fr_1fr] lg:items-end">
|
|
28
|
+
<p className="label-mono text-primary">The problem</p>
|
|
29
|
+
<h2 className="max-w-xl font-display text-4xl font-medium leading-tight tracking-tight sm:text-5xl">
|
|
30
|
+
Sound <span className="italic text-primary">familiar?</span>
|
|
31
|
+
</h2>
|
|
32
|
+
</div>
|
|
33
|
+
<div className="grid gap-px overflow-hidden rounded-2xl border border-border bg-border sm:grid-cols-3">
|
|
34
|
+
{problems.map((problem, index) => {
|
|
35
|
+
const Icon = problem.icon;
|
|
36
|
+
return (
|
|
37
|
+
<div
|
|
38
|
+
key={problem.title}
|
|
39
|
+
className="flex flex-col gap-5 bg-background p-8"
|
|
40
|
+
>
|
|
41
|
+
<span className="label-mono text-muted-foreground/60">
|
|
42
|
+
{String(index + 1).padStart(2, "0")}
|
|
43
|
+
</span>
|
|
44
|
+
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-destructive/10 text-destructive">
|
|
45
|
+
<Icon className="h-5 w-5" />
|
|
46
|
+
</div>
|
|
47
|
+
<h3 className="font-display text-xl font-medium leading-snug">
|
|
48
|
+
{problem.title}
|
|
49
|
+
</h3>
|
|
50
|
+
<p className="text-sm leading-relaxed text-muted-foreground">
|
|
51
|
+
{problem.description}
|
|
52
|
+
</p>
|
|
53
|
+
</div>
|
|
54
|
+
);
|
|
55
|
+
})}
|
|
56
|
+
</div>
|
|
57
|
+
</section>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
|
|
2
|
+
import { Card, CardContent } from "@/components/ui/card";
|
|
3
|
+
import { cn } from "@/lib/utils";
|
|
4
|
+
|
|
5
|
+
const testimonials = [
|
|
6
|
+
{
|
|
7
|
+
name: "Jordan Mills",
|
|
8
|
+
role: "Operations Lead, Northwind Co.",
|
|
9
|
+
initials: "JM",
|
|
10
|
+
quote:
|
|
11
|
+
"We replaced four separate tools with this in under a month. Our team finally has one source of truth.",
|
|
12
|
+
featured: true,
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
name: "Priya Anand",
|
|
16
|
+
role: "Founder, Lattice Studio",
|
|
17
|
+
initials: "PA",
|
|
18
|
+
quote:
|
|
19
|
+
"The automation alone saved us about 10 hours a week. It paid for itself in the first quarter.",
|
|
20
|
+
featured: false,
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: "Marcus Webb",
|
|
24
|
+
role: "Head of Product, Brightline",
|
|
25
|
+
initials: "MW",
|
|
26
|
+
quote:
|
|
27
|
+
"Setup took an afternoon, not a quarter. Our team adopted it without any real onboarding pain.",
|
|
28
|
+
featured: false,
|
|
29
|
+
},
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
export function Testimonials() {
|
|
33
|
+
const [featured, ...rest] = testimonials;
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<section className="bg-grid relative py-28">
|
|
37
|
+
<div className="container relative mx-auto px-4">
|
|
38
|
+
<div className="mb-16 grid gap-6 lg:grid-cols-[0.6fr_1fr] lg:items-end">
|
|
39
|
+
<p className="label-mono text-primary">Social proof</p>
|
|
40
|
+
<h2 className="max-w-xl font-display text-4xl font-medium leading-tight tracking-tight sm:text-5xl">
|
|
41
|
+
Loved by teams{" "}
|
|
42
|
+
<span className="italic text-primary">everywhere</span>
|
|
43
|
+
</h2>
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
<div className="grid gap-6 lg:grid-cols-5">
|
|
47
|
+
<Card className="bg-card/90 backdrop-blur lg:col-span-3 lg:row-span-2">
|
|
48
|
+
<CardContent className="flex h-full flex-col justify-between gap-8 p-8">
|
|
49
|
+
<p className="font-display text-2xl font-medium leading-snug sm:text-3xl">
|
|
50
|
+
“{featured.quote}”
|
|
51
|
+
</p>
|
|
52
|
+
<div className="flex items-center gap-3">
|
|
53
|
+
<Avatar className="h-12 w-12">
|
|
54
|
+
<AvatarFallback className="bg-primary/15 text-primary">
|
|
55
|
+
{featured.initials}
|
|
56
|
+
</AvatarFallback>
|
|
57
|
+
</Avatar>
|
|
58
|
+
<div>
|
|
59
|
+
<p className="text-sm font-semibold">{featured.name}</p>
|
|
60
|
+
<p className="label-mono text-muted-foreground">
|
|
61
|
+
{featured.role}
|
|
62
|
+
</p>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
</CardContent>
|
|
66
|
+
</Card>
|
|
67
|
+
|
|
68
|
+
{rest.map((testimonial, index) => (
|
|
69
|
+
<Card
|
|
70
|
+
key={testimonial.name}
|
|
71
|
+
className={cn(
|
|
72
|
+
"bg-card/90 backdrop-blur lg:col-span-2",
|
|
73
|
+
index === 0 ? "lg:translate-y-6" : "lg:-translate-y-2"
|
|
74
|
+
)}
|
|
75
|
+
>
|
|
76
|
+
<CardContent className="flex flex-col gap-5 p-6">
|
|
77
|
+
<p className="text-sm leading-relaxed text-muted-foreground">
|
|
78
|
+
“{testimonial.quote}”
|
|
79
|
+
</p>
|
|
80
|
+
<div className="flex items-center gap-3">
|
|
81
|
+
<Avatar>
|
|
82
|
+
<AvatarFallback>{testimonial.initials}</AvatarFallback>
|
|
83
|
+
</Avatar>
|
|
84
|
+
<div>
|
|
85
|
+
<p className="text-sm font-semibold">
|
|
86
|
+
{testimonial.name}
|
|
87
|
+
</p>
|
|
88
|
+
<p className="text-xs text-muted-foreground">
|
|
89
|
+
{testimonial.role}
|
|
90
|
+
</p>
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
|
93
|
+
</CardContent>
|
|
94
|
+
</Card>
|
|
95
|
+
))}
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
</section>
|
|
99
|
+
);
|
|
100
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { CtaFooter } from "./_components/cta-footer";
|
|
2
|
+
import { Faq } from "./_components/faq";
|
|
3
|
+
import { Features } from "./_components/features";
|
|
4
|
+
import { Hero } from "./_components/hero";
|
|
5
|
+
import { Pricing } from "./_components/pricing";
|
|
6
|
+
import { Problem } from "./_components/problem";
|
|
7
|
+
import { Testimonials } from "./_components/testimonials";
|
|
8
|
+
|
|
9
|
+
export default function SalePage() {
|
|
10
|
+
return (
|
|
11
|
+
<main className="flex flex-col">
|
|
12
|
+
<Hero />
|
|
13
|
+
<Problem />
|
|
14
|
+
<Features />
|
|
15
|
+
<Pricing />
|
|
16
|
+
<Testimonials />
|
|
17
|
+
<Faq />
|
|
18
|
+
<CtaFooter />
|
|
19
|
+
</main>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import * as AvatarPrimitive from "@radix-ui/react-avatar";
|
|
5
|
+
|
|
6
|
+
import { cn } from "@/lib/utils";
|
|
7
|
+
|
|
8
|
+
const Avatar = React.forwardRef<
|
|
9
|
+
React.ElementRef<typeof AvatarPrimitive.Root>,
|
|
10
|
+
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
|
|
11
|
+
>(({ className, ...props }, ref) => (
|
|
12
|
+
<AvatarPrimitive.Root
|
|
13
|
+
ref={ref}
|
|
14
|
+
className={cn(
|
|
15
|
+
"relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
|
|
16
|
+
className
|
|
17
|
+
)}
|
|
18
|
+
{...props}
|
|
19
|
+
/>
|
|
20
|
+
));
|
|
21
|
+
Avatar.displayName = AvatarPrimitive.Root.displayName;
|
|
22
|
+
|
|
23
|
+
const AvatarImage = React.forwardRef<
|
|
24
|
+
React.ElementRef<typeof AvatarPrimitive.Image>,
|
|
25
|
+
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
|
|
26
|
+
>(({ className, ...props }, ref) => (
|
|
27
|
+
<AvatarPrimitive.Image
|
|
28
|
+
ref={ref}
|
|
29
|
+
className={cn("aspect-square h-full w-full", className)}
|
|
30
|
+
{...props}
|
|
31
|
+
/>
|
|
32
|
+
));
|
|
33
|
+
AvatarImage.displayName = AvatarPrimitive.Image.displayName;
|
|
34
|
+
|
|
35
|
+
const AvatarFallback = React.forwardRef<
|
|
36
|
+
React.ElementRef<typeof AvatarPrimitive.Fallback>,
|
|
37
|
+
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
|
|
38
|
+
>(({ className, ...props }, ref) => (
|
|
39
|
+
<AvatarPrimitive.Fallback
|
|
40
|
+
ref={ref}
|
|
41
|
+
className={cn(
|
|
42
|
+
"flex h-full w-full items-center justify-center rounded-full bg-muted text-sm font-medium text-muted-foreground",
|
|
43
|
+
className
|
|
44
|
+
)}
|
|
45
|
+
{...props}
|
|
46
|
+
/>
|
|
47
|
+
));
|
|
48
|
+
AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;
|
|
49
|
+
|
|
50
|
+
export { Avatar, AvatarImage, AvatarFallback };
|