create-frootkit 1.2.11 → 1.2.13
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/package.json +1 -1
- package/templates/auth/src/app/dashboard/layout.tsx +8 -11
- package/templates/auth/src/app/dashboard/page.tsx +31 -27
- package/templates/auth/src/app/layout.tsx +5 -5
- package/templates/auth/src/app/login/page.tsx +11 -4
- package/templates/auth/src/app/page.tsx +278 -120
- package/templates/auth/src/app/signup/page.tsx +12 -6
- package/templates/auth/src/components/AuthForm.tsx +27 -34
- package/templates/auth/src/components/DashboardNav.tsx +41 -61
- package/templates/auth/src/components/MetricCard.tsx +12 -22
- package/templates/auth/src/components/Navbar.tsx +18 -21
- package/templates/auth/src/styles.css +157 -27
- package/templates/blog/src/app/blog/[slug]/page.tsx +85 -46
- package/templates/blog/src/app/layout.tsx +5 -5
- package/templates/blog/src/app/page.tsx +39 -26
- package/templates/blog/src/components/Footer.tsx +11 -9
- package/templates/blog/src/components/Navbar.tsx +21 -26
- package/templates/blog/src/components/Newsletter.tsx +11 -33
- package/templates/blog/src/components/PostCard.tsx +43 -76
- package/templates/blog/src/styles.css +113 -25
- package/templates/ecommerce/README.md +1 -0
- package/templates/ecommerce/src/app/cart/page.tsx +12 -4
- package/templates/ecommerce/src/app/layout.tsx +5 -5
- package/templates/ecommerce/src/app/page.tsx +92 -37
- package/templates/ecommerce/src/app/products/[id]/page.tsx +39 -31
- package/templates/ecommerce/src/components/CartDrawer.tsx +113 -45
- package/templates/ecommerce/src/components/Footer.tsx +11 -9
- package/templates/ecommerce/src/components/Navbar.tsx +32 -34
- package/templates/ecommerce/src/components/ProductCard.tsx +27 -38
- package/templates/ecommerce/src/styles.css +155 -36
- package/templates/starter/README.md +1 -0
- package/templates/starter/src/app/layout.tsx +5 -5
- package/templates/starter/src/app/page.tsx +114 -107
- package/templates/starter/src/components/FeatureCard.tsx +9 -15
- package/templates/starter/src/components/Footer.tsx +11 -9
- package/templates/starter/src/components/Header.tsx +15 -38
- package/templates/starter/src/styles.css +102 -27
|
@@ -1,156 +1,314 @@
|
|
|
1
1
|
import { Navbar } from '../components/Navbar.js';
|
|
2
2
|
|
|
3
3
|
export const metadata = {
|
|
4
|
-
title: 'FrootAuth —
|
|
5
|
-
description: 'Pure Web Crypto PBKDF2
|
|
4
|
+
title: 'FrootAuth — Authentication & Revenue Infrastructure',
|
|
5
|
+
description: 'Pure Web Crypto PBKDF2 authentication, subscription billing, and real-time dashboard telemetry.',
|
|
6
6
|
};
|
|
7
7
|
|
|
8
8
|
export default function SaaSMarketingPage() {
|
|
9
|
+
const features = [
|
|
10
|
+
{
|
|
11
|
+
title: 'Subscriptions',
|
|
12
|
+
desc: 'Flat, tiered or per-seat plans with trials, coupons and prorated upgrades.',
|
|
13
|
+
icon: 'M4 12a8 8 0 0 1 14-5M20 12a8 8 0 0 1-14 5M18 3v4h-4M6 21v-4h4',
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
title: 'Usage-based billing',
|
|
17
|
+
desc: 'Meter API calls or compute cycles and bill exactly what customers used.',
|
|
18
|
+
icon: 'M4 20V10M10 20V4M16 20v-7M22 20H2',
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
title: 'Smart retries',
|
|
22
|
+
desc: 'Failed cards retry at the best time, and customers get a link to update them.',
|
|
23
|
+
icon: 'M3 12a9 9 0 1 0 3-6.7M3 4v5h5',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
title: 'Invoicing',
|
|
27
|
+
desc: 'Branded PDF invoices with payment links, reminders and automatic numbering.',
|
|
28
|
+
icon: 'M6 3h9l4 4v14H6zM14 3v5h5M9 13h6M9 17h6',
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
title: 'Tax and compliance',
|
|
32
|
+
desc: 'Calculate sales tax and VAT at checkout and file reports in one click.',
|
|
33
|
+
icon: 'M5 19 19 5M7 8h.01M17 16h.01',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
title: 'Reconciliation',
|
|
37
|
+
desc: 'Match every payout to the payments inside it and export to your ledger.',
|
|
38
|
+
icon: 'M4 7h16M4 12h16M4 17h10',
|
|
39
|
+
},
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
const plans = [
|
|
43
|
+
{
|
|
44
|
+
name: 'Starter',
|
|
45
|
+
price: 0,
|
|
46
|
+
desc: 'For side projects and first customers.',
|
|
47
|
+
features: ['2.9% + 30¢ per payment', 'Up to $10k monthly volume', 'Email support'],
|
|
48
|
+
popular: false,
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: 'Growth',
|
|
52
|
+
price: 49,
|
|
53
|
+
desc: 'For teams with recurring revenue.',
|
|
54
|
+
features: ['2.4% + 30¢ per payment', 'Smart retries and dunning', 'Usage-based billing', 'Tax calculation'],
|
|
55
|
+
popular: true,
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
name: 'Scale',
|
|
59
|
+
price: 199,
|
|
60
|
+
desc: 'For finance teams at volume.',
|
|
61
|
+
features: ['Custom volume pricing', 'Revenue recognition', 'SSO and audit logs', 'Dedicated manager'],
|
|
62
|
+
popular: false,
|
|
63
|
+
},
|
|
64
|
+
];
|
|
65
|
+
|
|
9
66
|
return (
|
|
10
|
-
<div class="
|
|
67
|
+
<div class="bg-white font-sans text-[#425466]">
|
|
11
68
|
<Navbar />
|
|
12
69
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
<div class="
|
|
17
|
-
<
|
|
18
|
-
|
|
19
|
-
|
|
70
|
+
{/* HERO SECTION */}
|
|
71
|
+
<section class="hero relative pb-44 pt-32 text-white md:pb-56">
|
|
72
|
+
<div class="mx-auto max-w-6xl px-6 grid items-center gap-14 lg:grid-cols-[1fr_1.05fr]">
|
|
73
|
+
<div class="rise">
|
|
74
|
+
<span class="bd bg-white/15 text-white">New: usage-based billing is live</span>
|
|
75
|
+
<h1 class="mt-5 text-[42px] font-light leading-[1.08] tracking-tight md:text-[56px] md:leading-[64px]">
|
|
76
|
+
Billing that keeps up with your revenue
|
|
77
|
+
</h1>
|
|
78
|
+
<p class="mt-6 max-w-md text-[18px] font-light leading-7 text-white/90">
|
|
79
|
+
Charge for subscriptions, usage, and one-off invoices from one unified system. Failed payments retry on their own, and every dollar reconciles to your bank.
|
|
80
|
+
</p>
|
|
81
|
+
<div class="mt-8 flex flex-wrap gap-3">
|
|
82
|
+
<a href="/signup" class="btn btn-w">Start free</a>
|
|
83
|
+
<a href="/dashboard" class="btn btn-gl">View Dashboard</a>
|
|
20
84
|
</div>
|
|
85
|
+
<p class="mt-4 text-[13px] text-white/75">
|
|
86
|
+
No card needed. Free up to $10k in monthly volume. Demo: <code class="font-mono bg-white/10 px-1.5 py-0.5 rounded">admin@frootkit.dev</code>
|
|
87
|
+
</p>
|
|
88
|
+
</div>
|
|
21
89
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
90
|
+
{/* Interactive Hero Dashboard Mock */}
|
|
91
|
+
<div class="rise relative [animation-delay:.15s]">
|
|
92
|
+
<div class="overflow-hidden rounded-lg bg-white text-[#425466] shadow-[0_13px_27px_-5px_rgba(50,50,93,0.25),0_8px_16px_-8px_rgba(0,0,0,0.3)]">
|
|
93
|
+
<div class="flex items-center gap-1.5 border-b border-[#e6ebf1] bg-[#f6f9fc] px-4 py-2.5">
|
|
94
|
+
<i class="h-2.5 w-2.5 rounded-full bg-[#e6ebf1]" />
|
|
95
|
+
<i class="h-2.5 w-2.5 rounded-full bg-[#e6ebf1]" />
|
|
96
|
+
<i class="h-2.5 w-2.5 rounded-full bg-[#e6ebf1]" />
|
|
97
|
+
<span class="ml-3 font-mono text-[12px] text-[#425466]">dashboard.frootauth.com</span>
|
|
98
|
+
</div>
|
|
99
|
+
<div class="p-5">
|
|
100
|
+
<div class="flex items-end justify-between">
|
|
101
|
+
<div>
|
|
102
|
+
<p class="text-[13px] text-[#425466]">Net volume · last 30 days</p>
|
|
103
|
+
<p class="font-mono text-[30px] font-light text-[#0a2540]">$482,190.40</p>
|
|
104
|
+
</div>
|
|
105
|
+
<span class="bd bg-[#24b47e]/15 text-[#0e6245]">+18.2%</span>
|
|
106
|
+
</div>
|
|
25
107
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
108
|
+
<svg viewBox="0 0 300 90" class="mt-3 h-24 w-full">
|
|
109
|
+
<defs>
|
|
110
|
+
<linearGradient id="g" x1="0" y1="0" x2="0" y2="1">
|
|
111
|
+
<stop offset="0" stop-color="#635bff" stop-opacity=".25" />
|
|
112
|
+
<stop offset="1" stop-color="#635bff" stop-opacity="0" />
|
|
113
|
+
</linearGradient>
|
|
114
|
+
</defs>
|
|
115
|
+
<path d="M0 70 C30 66 40 50 70 54 S120 30 150 36 S210 18 240 22 S285 8 300 6 V90 H0Z" fill="url(#g)" />
|
|
116
|
+
<path d="M0 70 C30 66 40 50 70 54 S120 30 150 36 S210 18 240 22 S285 8 300 6" fill="none" stroke="#635bff" stroke-width="2.5" stroke-linecap="round" />
|
|
117
|
+
</svg>
|
|
29
118
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
119
|
+
<div class="mt-3 divide-y divide-[#e6ebf1] text-[14px]">
|
|
120
|
+
<div class="flex items-center justify-between py-2">
|
|
121
|
+
<span class="font-medium text-[#0a2540]">Northwind Traders</span>
|
|
122
|
+
<span class="flex items-center gap-3">
|
|
123
|
+
<span class="font-mono">$1,240.00</span>
|
|
124
|
+
<span class="bd bg-[#24b47e]/15 text-[#0e6245]">Paid</span>
|
|
125
|
+
</span>
|
|
126
|
+
</div>
|
|
127
|
+
<div class="flex items-center justify-between py-2">
|
|
128
|
+
<span class="font-medium text-[#0a2540]">Kestrel Labs</span>
|
|
129
|
+
<span class="flex items-center gap-3">
|
|
130
|
+
<span class="font-mono">$89.50</span>
|
|
131
|
+
<span class="bd bg-[#cd3d64]/10 text-[#cd3d64]">Retrying</span>
|
|
132
|
+
</span>
|
|
133
|
+
</div>
|
|
134
|
+
<div class="flex items-center justify-between py-2">
|
|
135
|
+
<span class="font-medium text-[#0a2540]">Orbit Studio</span>
|
|
136
|
+
<span class="flex items-center gap-3">
|
|
137
|
+
<span class="font-mono">$2,150.00</span>
|
|
138
|
+
<span class="bd bg-[#24b47e]/15 text-[#0e6245]">Paid</span>
|
|
139
|
+
</span>
|
|
140
|
+
</div>
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
44
143
|
</div>
|
|
45
144
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
<
|
|
49
|
-
<
|
|
50
|
-
<span class="text-zinc-600">/</span>
|
|
51
|
-
<code class="text-zinc-200 font-mono text-[11px] bg-white/[0.04] px-1.5 py-0.5 rounded">password123</code>
|
|
145
|
+
{/* Floating Recovery Notification */}
|
|
146
|
+
<div class="absolute -bottom-10 -left-4 hidden w-60 rounded-lg bg-white p-4 text-[13px] shadow-[0_13px_27px_-5px_rgba(50,50,93,0.25),0_8px_16px_-8px_rgba(0,0,0,0.3)] sm:block md:-left-10 text-[#425466]">
|
|
147
|
+
<p class="font-medium text-[#0a2540]">Payment recovered</p>
|
|
148
|
+
<p>Kestrel Labs paid on retry 2 of 4. Recovered $89.50.</p>
|
|
52
149
|
</div>
|
|
53
150
|
</div>
|
|
151
|
+
</div>
|
|
152
|
+
</section>
|
|
54
153
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
154
|
+
{/* STATS SECTION */}
|
|
155
|
+
<section class="bg-[#f6f9fc] py-14">
|
|
156
|
+
<div class="mx-auto max-w-6xl px-6 grid gap-8 text-center sm:grid-cols-3">
|
|
157
|
+
<div>
|
|
158
|
+
<p class="font-mono text-[40px] font-light text-[#0a2540]">99.99%</p>
|
|
159
|
+
<p class="text-[14px]">API uptime over the last 12 months</p>
|
|
160
|
+
</div>
|
|
161
|
+
<div>
|
|
162
|
+
<p class="font-mono text-[40px] font-light text-[#0a2540]">+11%</p>
|
|
163
|
+
<p class="text-[14px]">Average revenue recovered from failed payments</p>
|
|
164
|
+
</div>
|
|
165
|
+
<div>
|
|
166
|
+
<p class="font-mono text-[40px] font-light text-[#0a2540]">0.42ms</p>
|
|
167
|
+
<p class="text-[14px]">P99 server-side dispatch latency</p>
|
|
168
|
+
</div>
|
|
169
|
+
</div>
|
|
170
|
+
</section>
|
|
60
171
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
172
|
+
{/* FEATURES SECTION */}
|
|
173
|
+
<section id="features" class="scroll-mt-16 py-24">
|
|
174
|
+
<div class="mx-auto max-w-6xl px-6">
|
|
175
|
+
<h2 class="text-[32px] font-light leading-[1.15] tracking-tight text-[#0a2540] md:text-[40px] max-w-2xl">
|
|
176
|
+
One system for every way you get paid
|
|
177
|
+
</h2>
|
|
178
|
+
<p class="mt-4 max-w-xl text-[17px] leading-7">
|
|
179
|
+
Start with simple subscriptions and add usage, invoicing, and tax as you grow, without changing your integration.
|
|
180
|
+
</p>
|
|
181
|
+
<div class="mt-12 grid gap-5 md:grid-cols-3">
|
|
182
|
+
{features.map((f) => (
|
|
183
|
+
<div
|
|
184
|
+
key={f.title}
|
|
185
|
+
class="rounded-lg border border-[#e6ebf1] bg-white p-6 shadow-[0_2px_5px_-1px_rgba(50,50,93,0.25),0_1px_3px_-1px_rgba(0,0,0,0.3)] transition hover:-translate-y-0.5 hover:shadow-[0_13px_27px_-5px_rgba(50,50,93,0.25),0_8px_16px_-8px_rgba(0,0,0,0.3)]"
|
|
186
|
+
>
|
|
187
|
+
<span class="grid h-10 w-10 place-items-center rounded-md bg-[#635bff]/10 text-[#635bff]">
|
|
188
|
+
<svg class="h-5 w-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
|
189
|
+
<path d={f.icon} />
|
|
190
|
+
</svg>
|
|
68
191
|
</span>
|
|
192
|
+
<h3 class="mt-4 text-[17px] font-medium text-[#0a2540]">{f.title}</h3>
|
|
193
|
+
<p class="mt-1 text-[14px] leading-relaxed">{f.desc}</p>
|
|
69
194
|
</div>
|
|
195
|
+
))}
|
|
196
|
+
</div>
|
|
197
|
+
</div>
|
|
198
|
+
</section>
|
|
70
199
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
</div>
|
|
92
|
-
</div>
|
|
93
|
-
|
|
94
|
-
<div class="mt-5 pt-4 border-t border-white/[0.06] flex justify-between items-center text-xs">
|
|
95
|
-
<span class="text-zinc-500">Auth Route</span>
|
|
96
|
-
<code class="text-indigo-400 font-mono text-[11px] bg-indigo-500/5 px-2 py-0.5 rounded border border-indigo-500/15">
|
|
97
|
-
/api/auth?action=login
|
|
98
|
-
</code>
|
|
99
|
-
</div>
|
|
200
|
+
{/* DEVELOPERS SECTION */}
|
|
201
|
+
<section id="developers" class="scroll-mt-16 bg-[#0a2540] py-24 text-white">
|
|
202
|
+
<div class="mx-auto max-w-6xl px-6 grid items-center gap-12 lg:grid-cols-2">
|
|
203
|
+
<div>
|
|
204
|
+
<h2 class="text-[32px] font-light leading-[1.15] tracking-tight md:text-[40px]">
|
|
205
|
+
Built for developers, readable by finance
|
|
206
|
+
</h2>
|
|
207
|
+
<p class="mt-4 max-w-xl text-[17px] leading-7 text-white/75">
|
|
208
|
+
Create a customer, attach a plan, and bill them in a few lines. Every object has a test mode, so you can ship on day one.
|
|
209
|
+
</p>
|
|
210
|
+
<ul class="mt-6 space-y-2 text-[15px] text-white/90">
|
|
211
|
+
<li>• Idempotent requests and signed webhooks</li>
|
|
212
|
+
<li>• Pure Web Crypto PBKDF2 authentication</li>
|
|
213
|
+
<li>• A full sandbox with simulated test clocks</li>
|
|
214
|
+
</ul>
|
|
215
|
+
<a href="/dashboard" class="btn btn-w mt-8">Explore Dashboard</a>
|
|
216
|
+
</div>
|
|
217
|
+
<div class="overflow-hidden rounded-lg bg-[#06182b] shadow-[0_13px_27px_-5px_rgba(50,50,93,0.25),0_8px_16px_-8px_rgba(0,0,0,0.3)]">
|
|
218
|
+
<div class="flex gap-1 border-b border-white/10 px-3 pt-2">
|
|
219
|
+
<span class="rounded-t-md px-3 py-1.5 text-[13px] font-medium bg-[#0a2540] text-white">TypeScript</span>
|
|
100
220
|
</div>
|
|
221
|
+
<pre class="overflow-x-auto p-5 font-mono text-[13px] leading-6 text-white/90">
|
|
222
|
+
<code>{`// Zero external C++ dependencies
|
|
223
|
+
import { hashPassword, verifyPassword } from '@/lib/auth.js';
|
|
224
|
+
|
|
225
|
+
// PBKDF2-SHA256 with 100,000 iterations
|
|
226
|
+
const salt = crypto.getRandomValues(new Uint8Array(16));
|
|
227
|
+
const hash = await hashPassword('password123', salt);
|
|
228
|
+
|
|
229
|
+
// Verified in pure Web Crypto
|
|
230
|
+
const isValid = await verifyPassword('password123', salt, hash);`}</code>
|
|
231
|
+
</pre>
|
|
101
232
|
</div>
|
|
102
233
|
</div>
|
|
234
|
+
</section>
|
|
103
235
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
<p class="text-xs text-zinc-400 leading-relaxed">
|
|
114
|
-
Native PBKDF2 key derivation using the Web Cryptography API. Zero C++ native rebuilds across any architecture.
|
|
236
|
+
{/* PRICING SECTION */}
|
|
237
|
+
<section id="pricing" class="scroll-mt-16 py-24">
|
|
238
|
+
<div class="mx-auto max-w-6xl px-6">
|
|
239
|
+
<div class="text-center">
|
|
240
|
+
<h2 class="text-[32px] font-light leading-[1.15] tracking-tight text-[#0a2540] md:text-[40px]">
|
|
241
|
+
Pricing that scales with you
|
|
242
|
+
</h2>
|
|
243
|
+
<p class="mx-auto mt-4 max-w-md text-[17px]">
|
|
244
|
+
Pay a percentage of volume you process. No setup fees, no monthly minimums.
|
|
115
245
|
</p>
|
|
116
246
|
</div>
|
|
117
247
|
|
|
118
|
-
<div class="
|
|
119
|
-
|
|
120
|
-
<
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
248
|
+
<div class="mt-12 grid items-stretch gap-6 md:grid-cols-3">
|
|
249
|
+
{plans.map((p) => (
|
|
250
|
+
<div
|
|
251
|
+
key={p.name}
|
|
252
|
+
class={`flex flex-col rounded-lg bg-white p-7 ${
|
|
253
|
+
p.popular
|
|
254
|
+
? 'border-2 border-[#635bff] shadow-[0_13px_27px_-5px_rgba(50,50,93,0.25),0_8px_16px_-8px_rgba(0,0,0,0.3)]'
|
|
255
|
+
: 'border border-[#e6ebf1] shadow-[0_2px_5px_-1px_rgba(50,50,93,0.25),0_1px_3px_-1px_rgba(0,0,0,0.3)]'
|
|
256
|
+
}`}
|
|
257
|
+
>
|
|
258
|
+
<div class="flex items-center justify-between">
|
|
259
|
+
<h3 class="text-[18px] font-medium text-[#0a2540]">{p.name}</h3>
|
|
260
|
+
{p.popular && <span class="bd bg-[#635bff]/10 text-[#635bff]">Most popular</span>}
|
|
261
|
+
</div>
|
|
262
|
+
<p class="mt-1 text-[14px]">{p.desc}</p>
|
|
263
|
+
<p class="mt-5 text-[#0a2540]">
|
|
264
|
+
<span class="text-[44px] font-light">${p.price}</span>
|
|
265
|
+
<span class="text-[14px] text-[#425466]"> /month</span>
|
|
266
|
+
</p>
|
|
267
|
+
<ul class="my-6 flex-1 space-y-2.5 text-[14px]">
|
|
268
|
+
{p.features.map((feat) => (
|
|
269
|
+
<li key={feat} class="flex gap-2">
|
|
270
|
+
<svg class="mt-0.5 h-4 w-4 shrink-0 text-[#24b47e]" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3">
|
|
271
|
+
<path d="m5 12 5 5 9-10" stroke-linecap="round" stroke-linejoin="round" />
|
|
272
|
+
</svg>
|
|
273
|
+
{feat}
|
|
274
|
+
</li>
|
|
275
|
+
))}
|
|
276
|
+
</ul>
|
|
277
|
+
<a href="/signup" class={`btn ${p.popular ? 'btn-p' : 'btn-o'} w-full`}>
|
|
278
|
+
{p.price ? `Choose ${p.name}` : 'Start free'}
|
|
279
|
+
</a>
|
|
280
|
+
</div>
|
|
281
|
+
))}
|
|
129
282
|
</div>
|
|
283
|
+
</div>
|
|
284
|
+
</section>
|
|
130
285
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
</div>
|
|
139
|
-
<h3 class="text-sm font-semibold text-zinc-100 mb-1">SQLite Session Cache</h3>
|
|
140
|
-
<p class="text-xs text-zinc-400 leading-relaxed">
|
|
141
|
-
Local persistent storage with sub-millisecond lookup latency, automatic expiry purging, and single-file portability.
|
|
286
|
+
{/* FOOTER */}
|
|
287
|
+
<footer class="border-t border-[#e6ebf1] bg-[#f6f9fc] py-14 text-[14px]">
|
|
288
|
+
<div class="mx-auto max-w-6xl px-6 flex flex-wrap justify-between gap-6">
|
|
289
|
+
<div>
|
|
290
|
+
<p class="text-[20px] font-semibold tracking-tight text-[#635bff]">frootauth</p>
|
|
291
|
+
<p class="mt-2 max-w-xs text-xs text-[#425466]">
|
|
292
|
+
Stripe-inspired authentication and billing infrastructure powered by Preact & FrootKit.
|
|
142
293
|
</p>
|
|
143
294
|
</div>
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
<
|
|
295
|
+
<div class="flex gap-12">
|
|
296
|
+
<div>
|
|
297
|
+
<p class="mb-3 font-medium text-[#0a2540]">Product</p>
|
|
298
|
+
<ul class="space-y-2 text-xs">
|
|
299
|
+
<li><a href="#features" class="hover:text-[#635bff]">Billing</a></li>
|
|
300
|
+
<li><a href="#features" class="hover:text-[#635bff]">Invoicing</a></li>
|
|
301
|
+
<li><a href="#pricing" class="hover:text-[#635bff]">Pricing</a></li>
|
|
302
|
+
</ul>
|
|
303
|
+
</div>
|
|
304
|
+
<div>
|
|
305
|
+
<p class="mb-3 font-medium text-[#0a2540]">Platform</p>
|
|
306
|
+
<ul class="space-y-2 text-xs">
|
|
307
|
+
<li><a href="/dashboard" class="hover:text-[#635bff]">Dashboard</a></li>
|
|
308
|
+
<li><a href="/login" class="hover:text-[#635bff]">Sign In</a></li>
|
|
309
|
+
<li><a href="/signup" class="hover:text-[#635bff]">Register</a></li>
|
|
310
|
+
</ul>
|
|
311
|
+
</div>
|
|
154
312
|
</div>
|
|
155
313
|
</div>
|
|
156
314
|
</footer>
|
|
@@ -1,21 +1,27 @@
|
|
|
1
|
-
import { Navbar } from '../../components/Navbar.js';
|
|
2
1
|
import { AuthForm } from '../../components/AuthForm.js';
|
|
3
2
|
|
|
4
3
|
export const metadata = {
|
|
5
|
-
title: '
|
|
4
|
+
title: 'Sign Up — FrootAuth',
|
|
6
5
|
};
|
|
7
6
|
|
|
8
7
|
export default function SignupPage() {
|
|
9
8
|
return (
|
|
10
|
-
<div class="min-h-screen bg-[#
|
|
11
|
-
<
|
|
9
|
+
<div class="min-h-screen bg-[#f6f9fc] text-[#425466] flex flex-col justify-between">
|
|
10
|
+
<header class="border-b border-[#e6ebf1] bg-white py-4 px-6 flex items-center justify-between">
|
|
11
|
+
<a href="/" class="text-[20px] font-semibold tracking-tight text-[#635bff]">
|
|
12
|
+
frootauth
|
|
13
|
+
</a>
|
|
14
|
+
<a href="/" class="text-xs text-[#425466] hover:text-[#0a2540]">
|
|
15
|
+
← Back to home
|
|
16
|
+
</a>
|
|
17
|
+
</header>
|
|
12
18
|
|
|
13
19
|
<main class="max-w-md mx-auto px-6 py-16 flex-1 w-full flex flex-col justify-center">
|
|
14
20
|
<AuthForm mode="signup" />
|
|
15
21
|
</main>
|
|
16
22
|
|
|
17
|
-
<footer class="py-6 text-center text-xs text-
|
|
18
|
-
FrootAuth
|
|
23
|
+
<footer class="py-6 text-center text-xs text-[#425466]">
|
|
24
|
+
FrootAuth · Web Crypto PBKDF2 Authentication
|
|
19
25
|
</footer>
|
|
20
26
|
</div>
|
|
21
27
|
);
|
|
@@ -6,31 +6,28 @@ export function AuthForm({ mode }: AuthFormProps) {
|
|
|
6
6
|
const isLogin = mode === 'login';
|
|
7
7
|
|
|
8
8
|
return (
|
|
9
|
-
<div class="rounded-
|
|
10
|
-
{/* Top ambient highlight */}
|
|
11
|
-
<div class="absolute inset-x-0 top-0 h-px bg-gradient-to-r from-transparent via-indigo-500/40 to-transparent" />
|
|
12
|
-
|
|
9
|
+
<div class="rounded-lg border border-[#e6ebf1] bg-white p-7 sm:p-8 shadow-[0_13px_27px_-5px_rgba(50,50,93,0.25),0_8px_16px_-8px_rgba(0,0,0,0.3)]">
|
|
13
10
|
<div class="mb-6">
|
|
14
|
-
<h1 class="text-
|
|
15
|
-
{isLogin ? 'Sign in to
|
|
11
|
+
<h1 class="text-2xl font-light text-[#0a2540] tracking-tight mb-1">
|
|
12
|
+
{isLogin ? 'Sign in to FrootAuth' : 'Create your account'}
|
|
16
13
|
</h1>
|
|
17
|
-
<p class="text-
|
|
14
|
+
<p class="text-sm text-[#425466]">
|
|
18
15
|
{isLogin ? 'Need an account? ' : 'Already registered? '}
|
|
19
|
-
<a href={isLogin ? '/signup' : '/login'} class="text-
|
|
16
|
+
<a href={isLogin ? '/signup' : '/login'} class="text-[#635bff] font-medium hover:underline">
|
|
20
17
|
{isLogin ? 'Sign up' : 'Sign in'}
|
|
21
18
|
</a>
|
|
22
19
|
</p>
|
|
23
20
|
</div>
|
|
24
21
|
|
|
25
22
|
{isLogin && (
|
|
26
|
-
<div class="mb-5 p-3 rounded-
|
|
27
|
-
<div class="flex items-center gap-1.5 text-
|
|
28
|
-
<span class="w-1.5 h-1.5 rounded-full bg-
|
|
29
|
-
<span class="font-medium text-
|
|
23
|
+
<div class="mb-5 p-3 rounded-md bg-[#f6f9fc] border border-[#e6ebf1] text-xs">
|
|
24
|
+
<div class="flex items-center gap-1.5 text-[#425466] text-[12px] mb-1">
|
|
25
|
+
<span class="w-1.5 h-1.5 rounded-full bg-[#24b47e]" />
|
|
26
|
+
<span class="font-medium text-[#0a2540]">Demo Credentials:</span>
|
|
30
27
|
</div>
|
|
31
|
-
<div class="font-mono text-
|
|
28
|
+
<div class="font-mono text-[#0a2540] text-[12px] bg-white px-2.5 py-1 rounded border border-[#e6ebf1] flex items-center justify-between">
|
|
32
29
|
<span>admin@frootkit.dev</span>
|
|
33
|
-
<span class="text-
|
|
30
|
+
<span class="text-[#425466]">/</span>
|
|
34
31
|
<span>password123</span>
|
|
35
32
|
</div>
|
|
36
33
|
</div>
|
|
@@ -39,46 +36,39 @@ export function AuthForm({ mode }: AuthFormProps) {
|
|
|
39
36
|
<form action={`/api/auth?action=${mode}`} method="POST" class="space-y-4">
|
|
40
37
|
{!isLogin && (
|
|
41
38
|
<div>
|
|
42
|
-
<label class="block text-xs font-medium text-
|
|
39
|
+
<label class="block text-xs font-medium text-[#0a2540] mb-1.5" for="name">
|
|
43
40
|
Full Name
|
|
44
41
|
</label>
|
|
45
42
|
<input
|
|
46
43
|
id="name"
|
|
47
44
|
type="text"
|
|
48
45
|
name="name"
|
|
49
|
-
placeholder="
|
|
46
|
+
placeholder="Jane Doe"
|
|
50
47
|
required
|
|
51
|
-
class="
|
|
48
|
+
class="inp"
|
|
52
49
|
/>
|
|
53
50
|
</div>
|
|
54
51
|
)}
|
|
55
52
|
|
|
56
53
|
<div>
|
|
57
|
-
<label class="block text-xs font-medium text-
|
|
54
|
+
<label class="block text-xs font-medium text-[#0a2540] mb-1.5" for="email">
|
|
58
55
|
Email Address
|
|
59
56
|
</label>
|
|
60
57
|
<input
|
|
61
58
|
id="email"
|
|
62
59
|
type="email"
|
|
63
60
|
name="email"
|
|
64
|
-
placeholder="
|
|
61
|
+
placeholder="you@company.com"
|
|
65
62
|
defaultValue={isLogin ? 'admin@frootkit.dev' : ''}
|
|
66
63
|
required
|
|
67
|
-
class="
|
|
64
|
+
class="inp"
|
|
68
65
|
/>
|
|
69
66
|
</div>
|
|
70
67
|
|
|
71
68
|
<div>
|
|
72
|
-
<
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
</label>
|
|
76
|
-
{isLogin && (
|
|
77
|
-
<span class="text-[11px] text-zinc-500">
|
|
78
|
-
min 8 characters
|
|
79
|
-
</span>
|
|
80
|
-
)}
|
|
81
|
-
</div>
|
|
69
|
+
<label class="block text-xs font-medium text-[#0a2540] mb-1.5" for="password">
|
|
70
|
+
Password
|
|
71
|
+
</label>
|
|
82
72
|
<input
|
|
83
73
|
id="password"
|
|
84
74
|
type="password"
|
|
@@ -86,18 +76,21 @@ export function AuthForm({ mode }: AuthFormProps) {
|
|
|
86
76
|
placeholder="••••••••••••"
|
|
87
77
|
defaultValue={isLogin ? 'password123' : ''}
|
|
88
78
|
required
|
|
89
|
-
class="
|
|
79
|
+
class="inp"
|
|
90
80
|
/>
|
|
91
81
|
</div>
|
|
92
82
|
|
|
93
83
|
<button
|
|
94
84
|
type="submit"
|
|
95
|
-
class="w-full py-2.5
|
|
85
|
+
class="btn btn-p w-full py-2.5 text-sm font-medium mt-2"
|
|
96
86
|
>
|
|
97
|
-
|
|
98
|
-
<span class="text-zinc-500 group-hover:translate-x-0.5 transition-transform">→</span>
|
|
87
|
+
{isLogin ? 'Sign In' : 'Create Account'}
|
|
99
88
|
</button>
|
|
100
89
|
</form>
|
|
90
|
+
|
|
91
|
+
<div class="mt-6 pt-4 border-t border-[#e6ebf1] text-center text-xs text-[#425466]">
|
|
92
|
+
Protected by Web Crypto PBKDF2 with 100,000 iterations.
|
|
93
|
+
</div>
|
|
101
94
|
</div>
|
|
102
95
|
);
|
|
103
96
|
}
|