@tangle-network/starter-foundry 0.1.0 → 0.2.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/dist/lib/compose.js +2 -0
- package/dist/lib/compose.js.map +1 -1
- package/dist/lib/index.d.ts +3 -1
- package/dist/lib/index.js +2 -1
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/industries.d.ts +33 -0
- package/dist/lib/industries.js +52 -0
- package/dist/lib/industries.js.map +1 -0
- package/dist/lib/keywords.d.ts +6 -0
- package/dist/lib/keywords.js +21 -0
- package/dist/lib/keywords.js.map +1 -1
- package/dist/lib/prompt-planner.js +5 -1
- package/dist/lib/prompt-planner.js.map +1 -1
- package/dist/lib/telemetry.d.ts +1 -0
- package/dist/lib/telemetry.js.map +1 -1
- package/package.json +1 -1
- package/registry/layers/capability/layout-landing/files/features-section.tsx +54 -77
- package/registry/layers/capability/layout-landing/files/hero-section.tsx +18 -23
- package/registry/layers/capability/layout-landing/files/landing-page.tsx +25 -63
- package/registry/layers/capability/layout-landing/files/personalize.json +146 -0
- package/registry/layers/capability/layout-landing/files/pricing-section.tsx +45 -94
- package/registry/layers/capability/layout-landing/manifest.json +4 -0
- package/registry/layers/capability/layout-landing/variants/dark-product/features-section.tsx +55 -59
- package/registry/layers/capability/layout-landing/variants/dark-product/hero-section.tsx +83 -55
- package/registry/layers/capability/layout-landing/variants/dark-product/landing-page.tsx +25 -63
- package/registry/layers/capability/layout-landing/variants/dark-product/personalize.json +53 -0
- package/registry/layers/capability/layout-landing/variants/dark-product/pricing-section.tsx +51 -91
- package/registry/layers/capability/layout-landing/variants/default/features-section.tsx +54 -77
- package/registry/layers/capability/layout-landing/variants/default/hero-section.tsx +18 -23
- package/registry/layers/capability/layout-landing/variants/default/landing-page.tsx +25 -63
- package/registry/layers/capability/layout-landing/variants/default/personalize.json +146 -0
- package/registry/layers/capability/layout-landing/variants/default/pricing-section.tsx +45 -94
- package/registry/layers/capability/layout-landing/variants/gradient-hero/features-section.tsx +53 -57
- package/registry/layers/capability/layout-landing/variants/gradient-hero/hero-section.tsx +86 -53
- package/registry/layers/capability/layout-landing/variants/gradient-hero/landing-page.tsx +25 -63
- package/registry/layers/capability/layout-landing/variants/gradient-hero/personalize.json +53 -0
- package/registry/layers/capability/layout-landing/variants/gradient-hero/pricing-section.tsx +49 -88
- package/registry/layers/capability/layout-landing/variants/minimal-clean/features-section.tsx +53 -51
- package/registry/layers/capability/layout-landing/variants/minimal-clean/hero-section.tsx +88 -47
- package/registry/layers/capability/layout-landing/variants/minimal-clean/landing-page.tsx +25 -63
- package/registry/layers/capability/layout-landing/variants/minimal-clean/personalize.json +53 -0
- package/registry/layers/capability/layout-landing/variants/minimal-clean/pricing-section.tsx +51 -87
- package/registry/layers/framework/nextjs-app-router/files/layout.tsx +1 -0
- package/registry/layers/framework/nextjs-app-router/files/personalize.css +1 -0
- package/registry/layers/framework/nextjs-app-router/manifest.json +4 -0
- package/registry/layers/framework/react-vite-ts/files/main.ts +1 -0
- package/registry/layers/framework/react-vite-ts/files/personalize.css +1 -0
- package/registry/layers/framework/react-vite-ts/manifest.json +4 -0
- package/registry/layers/industry/creative/files/personalize.css +11 -0
- package/registry/layers/industry/creative/manifest.json +39 -0
- package/registry/layers/industry/crypto/files/personalize.css +11 -0
- package/registry/layers/industry/crypto/manifest.json +41 -0
- package/registry/layers/industry/ecommerce/files/personalize.css +11 -0
- package/registry/layers/industry/ecommerce/manifest.json +39 -0
- package/registry/layers/industry/education/files/personalize.css +11 -0
- package/registry/layers/industry/education/manifest.json +39 -0
- package/registry/layers/industry/finance/files/personalize.css +11 -0
- package/registry/layers/industry/finance/manifest.json +39 -0
- package/registry/layers/industry/fitness/files/personalize.css +11 -0
- package/registry/layers/industry/fitness/manifest.json +43 -0
- package/registry/layers/industry/gaming/files/personalize.css +11 -0
- package/registry/layers/industry/gaming/manifest.json +39 -0
- package/registry/layers/industry/health/files/personalize.css +11 -0
- package/registry/layers/industry/health/manifest.json +39 -0
- package/registry/layers/industry/restaurant/files/personalize.css +11 -0
- package/registry/layers/industry/restaurant/manifest.json +41 -0
- package/registry/layers/industry/saas/files/personalize.css +11 -0
- package/registry/layers/industry/saas/manifest.json +39 -0
|
@@ -2,43 +2,10 @@ import { Button } from '@/components/ui/button'
|
|
|
2
2
|
import { HeroSection } from '@/components/landing/hero-section'
|
|
3
3
|
import { FeaturesSection } from '@/components/landing/features-section'
|
|
4
4
|
import { PricingSection } from '@/components/landing/pricing-section'
|
|
5
|
-
|
|
6
|
-
const footerLinks = {
|
|
7
|
-
Product: [
|
|
8
|
-
{ label: 'Features', href: '#features' },
|
|
9
|
-
{ label: 'Pricing', href: '#pricing' },
|
|
10
|
-
{ label: 'Changelog', href: '/changelog' },
|
|
11
|
-
{ label: 'Docs', href: '/docs' },
|
|
12
|
-
],
|
|
13
|
-
Company: [
|
|
14
|
-
{ label: 'About', href: '/about' },
|
|
15
|
-
{ label: 'Blog', href: '/blog' },
|
|
16
|
-
{ label: 'Careers', href: '/careers' },
|
|
17
|
-
{ label: 'Contact', href: '/contact' },
|
|
18
|
-
],
|
|
19
|
-
Resources: [
|
|
20
|
-
{ label: 'Documentation', href: '/docs' },
|
|
21
|
-
{ label: 'API Reference', href: '/docs/api' },
|
|
22
|
-
{ label: 'Status', href: '/status' },
|
|
23
|
-
{ label: 'Support', href: '/support' },
|
|
24
|
-
],
|
|
25
|
-
Legal: [
|
|
26
|
-
{ label: 'Privacy', href: '/privacy' },
|
|
27
|
-
{ label: 'Terms', href: '/terms' },
|
|
28
|
-
{ label: 'Security', href: '/security' },
|
|
29
|
-
],
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const trustLogos = [
|
|
33
|
-
'Acme Corp',
|
|
34
|
-
'TechFlow',
|
|
35
|
-
'Quantum',
|
|
36
|
-
'NovaSoft',
|
|
37
|
-
'BuildStack',
|
|
38
|
-
'DataPipe',
|
|
39
|
-
]
|
|
5
|
+
import personalize from '@/personalize.json'
|
|
40
6
|
|
|
41
7
|
export default function LandingPage() {
|
|
8
|
+
const { brand, trust, footer } = personalize
|
|
42
9
|
return (
|
|
43
10
|
<div className="min-h-screen scroll-smooth bg-background text-foreground">
|
|
44
11
|
<a
|
|
@@ -53,28 +20,28 @@ export default function LandingPage() {
|
|
|
53
20
|
<span className="flex items-center gap-2 text-lg font-semibold tracking-tight">
|
|
54
21
|
<img
|
|
55
22
|
src="/images/logo.svg"
|
|
56
|
-
alt=
|
|
23
|
+
alt={brand.name}
|
|
57
24
|
className="h-8 w-auto"
|
|
58
25
|
onError={(e) => { e.currentTarget.style.display = 'none' }}
|
|
59
26
|
/>
|
|
60
|
-
<span>{
|
|
27
|
+
<span>{brand.name}</span>
|
|
61
28
|
</span>
|
|
62
29
|
<nav className="hidden items-center gap-8 text-sm md:flex">
|
|
63
30
|
<a
|
|
64
31
|
href="#features"
|
|
65
|
-
className="font-medium text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2
|
|
32
|
+
className="font-medium text-muted-foreground transition-colors hover:text-foreground focus-visible:rounded focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2"
|
|
66
33
|
>
|
|
67
34
|
Features
|
|
68
35
|
</a>
|
|
69
36
|
<a
|
|
70
37
|
href="#pricing"
|
|
71
|
-
className="font-medium text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2
|
|
38
|
+
className="font-medium text-muted-foreground transition-colors hover:text-foreground focus-visible:rounded focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2"
|
|
72
39
|
>
|
|
73
40
|
Pricing
|
|
74
41
|
</a>
|
|
75
42
|
<a
|
|
76
43
|
href="/sign-in"
|
|
77
|
-
className="font-medium text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2
|
|
44
|
+
className="font-medium text-muted-foreground transition-colors hover:text-foreground focus-visible:rounded focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2"
|
|
78
45
|
>
|
|
79
46
|
Sign in
|
|
80
47
|
</a>
|
|
@@ -95,17 +62,16 @@ export default function LandingPage() {
|
|
|
95
62
|
<main id="main">
|
|
96
63
|
<HeroSection />
|
|
97
64
|
|
|
98
|
-
{/* Trust section */}
|
|
99
65
|
<section className="border-y border-border/50 bg-muted/20 py-12 dark:bg-muted/5">
|
|
100
66
|
<div className="mx-auto max-w-6xl px-6 lg:px-8">
|
|
101
67
|
<p className="mb-8 text-center text-xs font-medium uppercase tracking-widest text-muted-foreground">
|
|
102
68
|
Trusted by teams at
|
|
103
69
|
</p>
|
|
104
70
|
<div className="flex flex-wrap items-center justify-center gap-x-10 gap-y-4 lg:gap-x-16">
|
|
105
|
-
{
|
|
71
|
+
{trust.logoNames.map((name) => (
|
|
106
72
|
<span
|
|
107
73
|
key={name}
|
|
108
|
-
className="text-lg font-semibold tracking-tight text-muted-foreground/30
|
|
74
|
+
className="select-none text-lg font-semibold tracking-tight text-muted-foreground/30"
|
|
109
75
|
>
|
|
110
76
|
{name}
|
|
111
77
|
</span>
|
|
@@ -114,35 +80,33 @@ export default function LandingPage() {
|
|
|
114
80
|
</div>
|
|
115
81
|
</section>
|
|
116
82
|
|
|
117
|
-
{/* Gradient divider */}
|
|
118
83
|
<div aria-hidden="true" className="h-px bg-gradient-to-r from-transparent via-border to-transparent" />
|
|
119
84
|
|
|
120
|
-
<
|
|
121
|
-
<FeaturesSection />
|
|
122
|
-
</section>
|
|
85
|
+
<FeaturesSection />
|
|
123
86
|
|
|
124
|
-
{/* Gradient divider */}
|
|
125
87
|
<div aria-hidden="true" className="h-px bg-gradient-to-r from-transparent via-border to-transparent" />
|
|
126
88
|
|
|
127
|
-
<
|
|
128
|
-
<PricingSection />
|
|
129
|
-
</section>
|
|
89
|
+
<PricingSection />
|
|
130
90
|
</main>
|
|
131
91
|
|
|
132
|
-
<footer className="border-t
|
|
92
|
+
<footer className="border-t border-border/50 bg-muted/10">
|
|
133
93
|
<div className="mx-auto max-w-6xl px-6 py-16 lg:px-8">
|
|
134
|
-
<div className="grid gap-8
|
|
135
|
-
|
|
136
|
-
<
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
94
|
+
<div className="grid gap-8 lg:grid-cols-5 lg:gap-12">
|
|
95
|
+
<div className="lg:col-span-1">
|
|
96
|
+
<span className="text-lg font-semibold tracking-tight">{brand.name}</span>
|
|
97
|
+
<p className="mt-3 text-sm leading-relaxed text-muted-foreground">
|
|
98
|
+
{footer.tagline}
|
|
99
|
+
</p>
|
|
100
|
+
</div>
|
|
101
|
+
{Object.entries(footer.sections).map(([title, links]) => (
|
|
102
|
+
<div key={title}>
|
|
103
|
+
<h4 className="text-sm font-semibold text-foreground">{title}</h4>
|
|
140
104
|
<ul className="mt-4 space-y-3">
|
|
141
105
|
{links.map((link) => (
|
|
142
106
|
<li key={link.label}>
|
|
143
107
|
<a
|
|
144
108
|
href={link.href}
|
|
145
|
-
className="text-sm text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:
|
|
109
|
+
className="text-sm text-muted-foreground transition-colors hover:text-foreground focus-visible:rounded focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2"
|
|
146
110
|
>
|
|
147
111
|
{link.label}
|
|
148
112
|
</a>
|
|
@@ -152,10 +116,8 @@ export default function LandingPage() {
|
|
|
152
116
|
</div>
|
|
153
117
|
))}
|
|
154
118
|
</div>
|
|
155
|
-
<div className="mt-12 border-t pt-8">
|
|
156
|
-
<p className="text-sm text-muted-foreground">
|
|
157
|
-
© {new Date().getFullYear()} {'{{headline}}'}. All rights reserved.
|
|
158
|
-
</p>
|
|
119
|
+
<div className="mt-12 border-t border-border/50 pt-8">
|
|
120
|
+
<p className="text-sm text-muted-foreground">{footer.copyright}</p>
|
|
159
121
|
</div>
|
|
160
122
|
</div>
|
|
161
123
|
</footer>
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_meta": { "variant": "dark-product", "doc": "Dark product-focused variant" },
|
|
3
|
+
"brand": { "name": "Vault", "tagline": "Infrastructure that scales" },
|
|
4
|
+
"hero": {
|
|
5
|
+
"badge": "AVAILABLE NOW",
|
|
6
|
+
"headline": "Built for serious workloads.",
|
|
7
|
+
"subheadline": "The infrastructure layer for teams running production systems at scale. Battle-tested, observable, fast.",
|
|
8
|
+
"ctaPrimary": "Deploy in 60s",
|
|
9
|
+
"ctaSecondary": "View Architecture"
|
|
10
|
+
},
|
|
11
|
+
"trust": {
|
|
12
|
+
"label": "Powering 500+ production systems",
|
|
13
|
+
"logoNames": ["Anthropic", "OpenAI", "Cloudflare", "Vercel", "Railway", "Fly"]
|
|
14
|
+
},
|
|
15
|
+
"features": {
|
|
16
|
+
"eyebrow": "Capabilities",
|
|
17
|
+
"headline": "Engineered for production",
|
|
18
|
+
"subheadline": "What you need to run real systems, nothing else.",
|
|
19
|
+
"primary": [
|
|
20
|
+
{ "icon": "Server", "title": "Sub-millisecond latency", "description": "Edge-deployed globally. p99 response times under 50ms across all regions." },
|
|
21
|
+
{ "icon": "ShieldCheck", "title": "Zero-trust by default", "description": "End-to-end encryption, mTLS between services, audit logs for everything." },
|
|
22
|
+
{ "icon": "Activity", "title": "Real-time observability", "description": "Distributed tracing, structured logs, and metrics — all wired in by default." }
|
|
23
|
+
],
|
|
24
|
+
"secondary": [
|
|
25
|
+
{ "icon": "Cpu", "title": "Multi-region", "description": "Active-active across continents." },
|
|
26
|
+
{ "icon": "Database", "title": "Strong consistency", "description": "Linearizable reads when you need them." },
|
|
27
|
+
{ "icon": "GitBranch", "title": "Rollback safe", "description": "Atomic deploys with instant rollback." }
|
|
28
|
+
]
|
|
29
|
+
},
|
|
30
|
+
"pricing": {
|
|
31
|
+
"eyebrow": "Pricing",
|
|
32
|
+
"headline": "Pay for what you use",
|
|
33
|
+
"subheadline": "No per-seat fees. No surprise bills. Just compute.",
|
|
34
|
+
"toggleMonthly": "Pay as you go",
|
|
35
|
+
"toggleYearly": "Reserved",
|
|
36
|
+
"yearlyDiscount": "Save 40%",
|
|
37
|
+
"tiers": [
|
|
38
|
+
{ "name": "Hobby", "price": "$0", "period": "/mo", "description": "For experimentation", "features": ["1M requests/mo", "1 region", "Community support"], "cta": "Deploy for free", "highlighted": false, "note": "No card needed" },
|
|
39
|
+
{ "name": "Production", "price": "$50", "period": "/mo", "description": "For real applications", "features": ["50M requests/mo", "All regions", "99.99% SLA", "Email support", "Custom domains", "DDoS protection"], "cta": "Start trial", "highlighted": true },
|
|
40
|
+
{ "name": "Scale", "price": "Custom", "period": "", "description": "For high-volume systems", "features": ["Everything in Production", "Unlimited requests", "Dedicated infrastructure", "24/7 phone support", "Custom contracts", "On-prem options"], "cta": "Talk to sales", "highlighted": false }
|
|
41
|
+
]
|
|
42
|
+
},
|
|
43
|
+
"footer": {
|
|
44
|
+
"tagline": "The infrastructure your roadmap deserves.",
|
|
45
|
+
"sections": {
|
|
46
|
+
"Product": [{ "label": "Features", "href": "#features" }, { "label": "Pricing", "href": "#pricing" }, { "label": "Status", "href": "/status" }, { "label": "Roadmap", "href": "/roadmap" }],
|
|
47
|
+
"Company": [{ "label": "About", "href": "/about" }, { "label": "Blog", "href": "/blog" }, { "label": "Careers", "href": "/careers" }, { "label": "Customers", "href": "/customers" }],
|
|
48
|
+
"Resources": [{ "label": "Docs", "href": "/docs" }, { "label": "API Reference", "href": "/api" }, { "label": "Tutorials", "href": "/tutorials" }, { "label": "Examples", "href": "/examples" }],
|
|
49
|
+
"Legal": [{ "label": "Privacy", "href": "/privacy" }, { "label": "Terms", "href": "/terms" }, { "label": "Security", "href": "/security" }, { "label": "Compliance", "href": "/compliance" }]
|
|
50
|
+
},
|
|
51
|
+
"copyright": "© 2026 Vault Systems."
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -4,136 +4,100 @@ import * as React from 'react'
|
|
|
4
4
|
import { Button } from '@/components/ui/button'
|
|
5
5
|
import { Check } from 'lucide-react'
|
|
6
6
|
import { cn } from '@/lib/utils'
|
|
7
|
-
|
|
8
|
-
const tiers = [
|
|
9
|
-
{
|
|
10
|
-
name: 'Free',
|
|
11
|
-
price: { monthly: '$0', yearly: '$0' },
|
|
12
|
-
description: 'For side projects and experimentation.',
|
|
13
|
-
features: [
|
|
14
|
-
'Up to 3 projects',
|
|
15
|
-
'1 GB storage',
|
|
16
|
-
'Community support',
|
|
17
|
-
'Basic analytics',
|
|
18
|
-
],
|
|
19
|
-
cta: 'Get Started',
|
|
20
|
-
highlighted: false,
|
|
21
|
-
},
|
|
22
|
-
{
|
|
23
|
-
name: 'Pro',
|
|
24
|
-
price: { monthly: '$29', yearly: '$24' },
|
|
25
|
-
description: 'For growing teams that need more power.',
|
|
26
|
-
features: [
|
|
27
|
-
'Unlimited projects',
|
|
28
|
-
'100 GB storage',
|
|
29
|
-
'Priority support',
|
|
30
|
-
'Advanced analytics',
|
|
31
|
-
'Custom domains',
|
|
32
|
-
'Team roles',
|
|
33
|
-
],
|
|
34
|
-
cta: 'Start Free Trial',
|
|
35
|
-
highlighted: true,
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
name: 'Enterprise',
|
|
39
|
-
price: { monthly: 'Custom', yearly: 'Custom' },
|
|
40
|
-
description: 'For organizations with advanced needs.',
|
|
41
|
-
features: [
|
|
42
|
-
'Everything in Pro',
|
|
43
|
-
'Unlimited storage',
|
|
44
|
-
'Dedicated support engineer',
|
|
45
|
-
'SSO / SAML',
|
|
46
|
-
'SLA guarantees',
|
|
47
|
-
'Audit logs',
|
|
48
|
-
],
|
|
49
|
-
cta: 'Contact Sales',
|
|
50
|
-
highlighted: false,
|
|
51
|
-
},
|
|
52
|
-
]
|
|
7
|
+
import personalize from '@/personalize.json'
|
|
53
8
|
|
|
54
9
|
export function PricingSection() {
|
|
55
10
|
const [yearly, setYearly] = React.useState(false)
|
|
11
|
+
const { pricing } = personalize
|
|
56
12
|
|
|
57
13
|
return (
|
|
58
|
-
<section className="bg-
|
|
14
|
+
<section id="pricing" className="relative bg-background py-24 sm:py-32">
|
|
59
15
|
<div className="mx-auto max-w-6xl px-6 lg:px-8">
|
|
60
16
|
<div className="mx-auto max-w-2xl text-center">
|
|
61
|
-
<
|
|
62
|
-
|
|
17
|
+
<p className="text-sm font-semibold uppercase tracking-wider text-primary">
|
|
18
|
+
{pricing.eyebrow}
|
|
19
|
+
</p>
|
|
20
|
+
<h2 className="mt-3 text-4xl font-bold tracking-tight text-foreground sm:text-5xl">
|
|
21
|
+
{pricing.headline}
|
|
63
22
|
</h2>
|
|
64
|
-
<p className="mt-
|
|
65
|
-
|
|
23
|
+
<p className="mt-6 text-lg leading-relaxed text-muted-foreground">
|
|
24
|
+
{pricing.subheadline}
|
|
66
25
|
</p>
|
|
26
|
+
</div>
|
|
67
27
|
|
|
68
|
-
|
|
28
|
+
<div className="mt-12 flex justify-center">
|
|
29
|
+
<div className="inline-flex items-center rounded-full border border-border/50 bg-muted/50 p-1 shadow-sm">
|
|
69
30
|
<button
|
|
70
31
|
type="button"
|
|
71
32
|
onClick={() => setYearly(false)}
|
|
72
33
|
className={cn(
|
|
73
|
-
'rounded-full px-5 py-2 text-sm font-medium transition-
|
|
74
|
-
!yearly
|
|
75
|
-
? 'bg-emerald-500 text-zinc-950 shadow-sm'
|
|
76
|
-
: 'text-zinc-400 hover:text-zinc-200'
|
|
34
|
+
'rounded-full px-5 py-2 text-sm font-medium transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2',
|
|
35
|
+
!yearly ? 'bg-primary text-primary-foreground shadow-sm' : 'text-muted-foreground hover:text-foreground',
|
|
77
36
|
)}
|
|
78
37
|
>
|
|
79
|
-
|
|
38
|
+
{pricing.toggleMonthly}
|
|
80
39
|
</button>
|
|
81
40
|
<button
|
|
82
41
|
type="button"
|
|
83
42
|
onClick={() => setYearly(true)}
|
|
84
43
|
className={cn(
|
|
85
|
-
'inline-flex items-center gap-2 rounded-full px-5 py-2 text-sm font-medium transition-
|
|
86
|
-
yearly
|
|
87
|
-
? 'bg-emerald-500 text-zinc-950 shadow-sm'
|
|
88
|
-
: 'text-zinc-400 hover:text-zinc-200'
|
|
44
|
+
'inline-flex items-center gap-2 rounded-full px-5 py-2 text-sm font-medium transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2',
|
|
45
|
+
yearly ? 'bg-primary text-primary-foreground shadow-sm' : 'text-muted-foreground hover:text-foreground',
|
|
89
46
|
)}
|
|
90
47
|
>
|
|
91
|
-
|
|
92
|
-
{!yearly &&
|
|
48
|
+
{pricing.toggleYearly}
|
|
49
|
+
{!yearly && (
|
|
50
|
+
<span className="rounded-full bg-primary/10 px-2 py-0.5 text-xs font-semibold text-primary">
|
|
51
|
+
{pricing.yearlyDiscount}
|
|
52
|
+
</span>
|
|
53
|
+
)}
|
|
93
54
|
</button>
|
|
94
55
|
</div>
|
|
95
56
|
</div>
|
|
96
57
|
|
|
97
|
-
<div className="
|
|
98
|
-
{tiers.map((tier) => (
|
|
58
|
+
<div className="mt-16 grid gap-8 lg:grid-cols-3 lg:gap-6">
|
|
59
|
+
{pricing.tiers.map((tier) => (
|
|
99
60
|
<div
|
|
100
61
|
key={tier.name}
|
|
101
62
|
className={cn(
|
|
102
|
-
'
|
|
103
|
-
tier.highlighted
|
|
104
|
-
'
|
|
63
|
+
'relative rounded-2xl bg-card p-8 transition-all',
|
|
64
|
+
tier.highlighted
|
|
65
|
+
? 'border-2 border-primary bg-gradient-to-b from-primary/5 to-transparent shadow-2xl shadow-primary/10 ring-1 ring-primary/20 lg:scale-105'
|
|
66
|
+
: 'border border-border/50 shadow-sm hover:shadow-md',
|
|
105
67
|
)}
|
|
106
68
|
>
|
|
107
69
|
{tier.highlighted && (
|
|
108
|
-
<div className="absolute -top-4 left-1/2 -translate-x-1/2
|
|
109
|
-
|
|
70
|
+
<div className="absolute -top-4 left-1/2 -translate-x-1/2">
|
|
71
|
+
<span className="rounded-full bg-primary px-4 py-1 text-xs font-semibold uppercase tracking-wider text-primary-foreground shadow-md">
|
|
72
|
+
Most Popular
|
|
73
|
+
</span>
|
|
110
74
|
</div>
|
|
111
75
|
)}
|
|
112
76
|
|
|
113
77
|
<div className="space-y-8">
|
|
114
78
|
<div>
|
|
115
|
-
<h3 className="text-lg font-semibold text-
|
|
116
|
-
|
|
117
|
-
</h3>
|
|
118
|
-
<p className="mt-1 text-sm text-zinc-400">
|
|
119
|
-
{tier.description}
|
|
120
|
-
</p>
|
|
79
|
+
<h3 className="text-lg font-semibold text-foreground">{tier.name}</h3>
|
|
80
|
+
<p className="mt-2 text-sm text-muted-foreground">{tier.description}</p>
|
|
121
81
|
</div>
|
|
122
82
|
|
|
123
83
|
<div className="flex items-baseline gap-1">
|
|
124
|
-
<span className="text-5xl font-bold tracking-tight text-
|
|
125
|
-
{
|
|
84
|
+
<span className="text-5xl font-bold tracking-tight text-foreground">
|
|
85
|
+
{tier.price}
|
|
126
86
|
</span>
|
|
127
|
-
{tier.
|
|
128
|
-
<span className="text-sm text-
|
|
87
|
+
{tier.period && (
|
|
88
|
+
<span className="text-sm font-medium text-muted-foreground">
|
|
89
|
+
{tier.period}
|
|
90
|
+
</span>
|
|
129
91
|
)}
|
|
130
92
|
</div>
|
|
131
93
|
|
|
132
94
|
<ul className="space-y-4">
|
|
133
95
|
{tier.features.map((feature) => (
|
|
134
|
-
<li key={feature} className="flex items-start gap-
|
|
135
|
-
<
|
|
136
|
-
|
|
96
|
+
<li key={feature} className="flex items-start gap-3">
|
|
97
|
+
<span className="mt-0.5 flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-primary/10">
|
|
98
|
+
<Check className="h-3 w-3 text-primary" />
|
|
99
|
+
</span>
|
|
100
|
+
<span className="text-sm text-foreground">{feature}</span>
|
|
137
101
|
</li>
|
|
138
102
|
))}
|
|
139
103
|
</ul>
|
|
@@ -142,18 +106,14 @@ export function PricingSection() {
|
|
|
142
106
|
<Button
|
|
143
107
|
variant={tier.highlighted ? 'default' : 'outline'}
|
|
144
108
|
className={cn(
|
|
145
|
-
'h-12 w-full rounded-lg font-medium transition-
|
|
146
|
-
tier.highlighted
|
|
147
|
-
? 'bg-emerald-500 text-zinc-950 shadow-lg shadow-emerald-500/25 hover:bg-emerald-400'
|
|
148
|
-
: 'border-zinc-700 text-zinc-300 hover:bg-zinc-800 hover:text-zinc-50'
|
|
109
|
+
'h-12 w-full rounded-lg font-medium transition-transform hover:scale-[1.02] active:scale-[0.98] focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2',
|
|
110
|
+
tier.highlighted ? 'shadow-lg shadow-primary/25' : 'border-2',
|
|
149
111
|
)}
|
|
150
112
|
>
|
|
151
113
|
{tier.cta}
|
|
152
114
|
</Button>
|
|
153
|
-
{tier.
|
|
154
|
-
<p className="mt-3 text-center text-xs text-
|
|
155
|
-
No credit card required
|
|
156
|
-
</p>
|
|
115
|
+
{tier.note && (
|
|
116
|
+
<p className="mt-3 text-center text-xs text-muted-foreground">{tier.note}</p>
|
|
157
117
|
)}
|
|
158
118
|
</div>
|
|
159
119
|
</div>
|
|
@@ -1,98 +1,75 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as Icons from 'lucide-react'
|
|
2
|
+
import type { LucideIcon } from 'lucide-react'
|
|
3
|
+
import personalize from '@/personalize.json'
|
|
2
4
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
description:
|
|
8
|
-
'Optimized for speed with edge-first architecture. Sub-second response times out of the box with automatic CDN distribution.',
|
|
9
|
-
},
|
|
10
|
-
{
|
|
11
|
-
icon: Shield,
|
|
12
|
-
title: 'Secure by Default',
|
|
13
|
-
description:
|
|
14
|
-
'Enterprise-grade security with end-to-end encryption, SOC 2 compliance, and role-based access controls baked into every layer.',
|
|
15
|
-
},
|
|
16
|
-
{
|
|
17
|
-
icon: BarChart3,
|
|
18
|
-
title: 'Built-in Analytics',
|
|
19
|
-
description:
|
|
20
|
-
'Real-time dashboards and custom reports so you always know what is working. Track every metric that matters to your business.',
|
|
21
|
-
},
|
|
22
|
-
]
|
|
23
|
-
|
|
24
|
-
const secondaryFeatures = [
|
|
25
|
-
{ icon: Layers, label: 'Composable modules — pick only the pieces you need' },
|
|
26
|
-
{ icon: Globe, label: 'Deploy to 30+ regions with a single command' },
|
|
27
|
-
{ icon: Lock, label: 'SSO, SAML, and audit logs for compliance' },
|
|
28
|
-
]
|
|
5
|
+
function getIcon(name: string): LucideIcon {
|
|
6
|
+
const Icon = (Icons as unknown as Record<string, LucideIcon>)[name]
|
|
7
|
+
return Icon ?? Icons.Sparkles
|
|
8
|
+
}
|
|
29
9
|
|
|
30
10
|
export function FeaturesSection() {
|
|
11
|
+
const { features } = personalize
|
|
31
12
|
return (
|
|
32
|
-
<section className="relative bg-
|
|
33
|
-
<style>{`
|
|
34
|
-
@keyframes fadeInUp {
|
|
35
|
-
from {
|
|
36
|
-
opacity: 0;
|
|
37
|
-
transform: translateY(20px);
|
|
38
|
-
}
|
|
39
|
-
to {
|
|
40
|
-
opacity: 1;
|
|
41
|
-
transform: translateY(0);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
`}</style>
|
|
45
|
-
|
|
13
|
+
<section id="features" className="relative bg-background py-24 sm:py-32">
|
|
46
14
|
<div className="mx-auto max-w-6xl px-6 lg:px-8">
|
|
47
15
|
<div className="mx-auto max-w-2xl text-center">
|
|
48
|
-
<
|
|
49
|
-
|
|
16
|
+
<p className="text-sm font-semibold uppercase tracking-wider text-primary">
|
|
17
|
+
{features.eyebrow}
|
|
18
|
+
</p>
|
|
19
|
+
<h2 className="mt-3 text-4xl font-bold tracking-tight text-foreground sm:text-5xl">
|
|
20
|
+
{features.headline}
|
|
50
21
|
</h2>
|
|
51
|
-
<p className="mt-
|
|
52
|
-
|
|
22
|
+
<p className="mt-6 text-lg leading-relaxed text-muted-foreground">
|
|
23
|
+
{features.subheadline}
|
|
53
24
|
</p>
|
|
54
25
|
</div>
|
|
55
26
|
|
|
56
|
-
<div className="
|
|
57
|
-
{
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
className="relative rounded-xl border border-border/50 bg-card p-8 opacity-0 transition-all duration-200 hover:border-border hover:shadow-md [animation:fadeInUp_0.5s_ease_forwards]"
|
|
61
|
-
style={{ animationDelay: `${0.1 + i * 0.15}s` }}
|
|
62
|
-
>
|
|
63
|
-
{/* Gradient top border */}
|
|
27
|
+
<div className="mt-20 grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
|
|
28
|
+
{features.primary.map((feature, i) => {
|
|
29
|
+
const Icon = getIcon(feature.icon)
|
|
30
|
+
return (
|
|
64
31
|
<div
|
|
65
|
-
|
|
66
|
-
className="
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
<
|
|
32
|
+
key={feature.title}
|
|
33
|
+
className="group relative overflow-hidden rounded-xl border border-border/50 bg-card p-8 shadow-sm transition-all duration-200 hover:border-border hover:shadow-md"
|
|
34
|
+
style={{ animation: `fadeInUp 0.6s ease ${i * 0.15}s both` }}
|
|
35
|
+
>
|
|
36
|
+
<div className="absolute inset-x-0 top-0 h-px bg-gradient-to-r from-transparent via-primary/50 to-transparent" />
|
|
37
|
+
<div className="flex h-11 w-11 items-center justify-center rounded-lg bg-gradient-to-br from-primary/20 to-primary/5 text-primary">
|
|
38
|
+
<Icon className="h-5 w-5" />
|
|
39
|
+
</div>
|
|
40
|
+
<h3 className="mt-6 text-lg font-semibold text-foreground">{feature.title}</h3>
|
|
41
|
+
<p className="mt-3 text-sm leading-relaxed text-muted-foreground">
|
|
42
|
+
{feature.description}
|
|
43
|
+
</p>
|
|
70
44
|
</div>
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
</h3>
|
|
74
|
-
<p className="mt-2 text-sm leading-relaxed text-muted-foreground">
|
|
75
|
-
{feature.description}
|
|
76
|
-
</p>
|
|
77
|
-
</div>
|
|
78
|
-
))}
|
|
45
|
+
)
|
|
46
|
+
})}
|
|
79
47
|
</div>
|
|
80
48
|
|
|
81
|
-
<div className="
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
49
|
+
<div className="mt-16 grid gap-8 border-t border-border/50 pt-16 sm:grid-cols-3">
|
|
50
|
+
{features.secondary.map((feature) => {
|
|
51
|
+
const Icon = getIcon(feature.icon)
|
|
52
|
+
return (
|
|
53
|
+
<div key={feature.title} className="flex gap-4">
|
|
54
|
+
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-muted text-foreground">
|
|
55
|
+
<Icon className="h-4 w-4" />
|
|
56
|
+
</div>
|
|
57
|
+
<div>
|
|
58
|
+
<h4 className="text-sm font-semibold text-foreground">{feature.title}</h4>
|
|
59
|
+
<p className="mt-1 text-sm text-muted-foreground">{feature.description}</p>
|
|
87
60
|
</div>
|
|
88
|
-
<p className="text-sm leading-snug text-muted-foreground">
|
|
89
|
-
{feature.label}
|
|
90
|
-
</p>
|
|
91
61
|
</div>
|
|
92
|
-
)
|
|
93
|
-
|
|
62
|
+
)
|
|
63
|
+
})}
|
|
94
64
|
</div>
|
|
95
65
|
</div>
|
|
66
|
+
|
|
67
|
+
<style>{`
|
|
68
|
+
@keyframes fadeInUp {
|
|
69
|
+
from { opacity: 0; transform: translateY(20px); }
|
|
70
|
+
to { opacity: 1; transform: translateY(0); }
|
|
71
|
+
}
|
|
72
|
+
`}</style>
|
|
96
73
|
</section>
|
|
97
74
|
)
|
|
98
75
|
}
|