create-varity-app 2.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +85 -0
  3. package/dist/create.js +141 -0
  4. package/dist/index.js +45 -0
  5. package/dist/utils.js +29 -0
  6. package/package.json +61 -0
  7. package/template/.env.example +17 -0
  8. package/template/KNOWN_ISSUES.md +69 -0
  9. package/template/LICENSE +21 -0
  10. package/template/README.md +241 -0
  11. package/template/gitignore +42 -0
  12. package/template/next-env.d.ts +6 -0
  13. package/template/next.config.js +21 -0
  14. package/template/package.json +39 -0
  15. package/template/postcss.config.js +6 -0
  16. package/template/public/logo.svg +4 -0
  17. package/template/public/robots.txt +4 -0
  18. package/template/public/sitemap.xml +4 -0
  19. package/template/src/app/dashboard/layout.tsx +298 -0
  20. package/template/src/app/dashboard/page.tsx +209 -0
  21. package/template/src/app/dashboard/projects/page.tsx +638 -0
  22. package/template/src/app/dashboard/settings/page.tsx +749 -0
  23. package/template/src/app/dashboard/tasks/page.tsx +301 -0
  24. package/template/src/app/dashboard/team/page.tsx +295 -0
  25. package/template/src/app/globals.css +177 -0
  26. package/template/src/app/icon.svg +4 -0
  27. package/template/src/app/layout.tsx +33 -0
  28. package/template/src/app/login/page.tsx +98 -0
  29. package/template/src/app/not-found.tsx +20 -0
  30. package/template/src/app/page.tsx +23 -0
  31. package/template/src/components/dashboard/DashboardStats.tsx +137 -0
  32. package/template/src/components/dashboard/RecentActivity.tsx +63 -0
  33. package/template/src/components/landing/CTA.tsx +42 -0
  34. package/template/src/components/landing/Features.tsx +116 -0
  35. package/template/src/components/landing/Hero.tsx +146 -0
  36. package/template/src/components/landing/HowItWorks.tsx +80 -0
  37. package/template/src/components/landing/Pricing.tsx +124 -0
  38. package/template/src/components/landing/Testimonials.tsx +78 -0
  39. package/template/src/components/providers.tsx +11 -0
  40. package/template/src/components/shared/Footer.tsx +71 -0
  41. package/template/src/components/shared/Navbar.tsx +87 -0
  42. package/template/src/lib/constants.ts +35 -0
  43. package/template/src/lib/database.ts +7 -0
  44. package/template/src/lib/hooks.ts +331 -0
  45. package/template/src/lib/utils.ts +68 -0
  46. package/template/src/lib/varity.ts +1 -0
  47. package/template/src/services/dashboardService.ts +589 -0
  48. package/template/src/types/index.ts +52 -0
  49. package/template/tailwind.config.js +27 -0
  50. package/template/tsconfig.json +23 -0
  51. package/template/varity.config.json +14 -0
@@ -0,0 +1,116 @@
1
+ 'use client';
2
+
3
+ import {
4
+ LayoutDashboard,
5
+ Users,
6
+ BarChart3,
7
+ ListChecks,
8
+ Shield,
9
+ Zap,
10
+ } from 'lucide-react';
11
+
12
+ export function Features() {
13
+ const features = [
14
+ {
15
+ icon: 'layoutDashboard',
16
+ iconColor: 'text-blue-600',
17
+ title: 'Project Dashboard',
18
+ description:
19
+ 'See all your projects at a glance with real-time status, progress, and deadlines in one unified view.',
20
+ iconBg: 'bg-blue-100',
21
+ hoverBorder: 'hover:border-blue-200',
22
+ },
23
+ {
24
+ icon: 'listChecks',
25
+ iconColor: 'text-amber-600',
26
+ title: 'Task Management',
27
+ description:
28
+ 'Create, assign, and track tasks with priorities, statuses, and due dates. Keep every deliverable on track.',
29
+ iconBg: 'bg-amber-100',
30
+ hoverBorder: 'hover:border-amber-200',
31
+ },
32
+ {
33
+ icon: 'users',
34
+ iconColor: 'text-green-600',
35
+ title: 'Team Collaboration',
36
+ description:
37
+ 'Invite team members, assign roles, and coordinate work. Everyone stays aligned without endless meetings.',
38
+ iconBg: 'bg-green-100',
39
+ hoverBorder: 'hover:border-green-200',
40
+ },
41
+ {
42
+ icon: 'barChart',
43
+ iconColor: 'text-purple-600',
44
+ title: 'Analytics & Insights',
45
+ description:
46
+ 'Track completion rates, team velocity, and project health with KPIs that update as your team works.',
47
+ iconBg: 'bg-purple-100',
48
+ hoverBorder: 'hover:border-purple-200',
49
+ },
50
+ {
51
+ icon: 'shield',
52
+ iconColor: 'text-rose-600',
53
+ title: 'Secure by Default',
54
+ description:
55
+ 'Email and social login built in. Your data is protected with industry-standard authentication and encryption.',
56
+ iconBg: 'bg-rose-100',
57
+ hoverBorder: 'hover:border-rose-200',
58
+ },
59
+ {
60
+ icon: 'zap',
61
+ iconColor: 'text-cyan-600',
62
+ title: 'Instant Setup',
63
+ description:
64
+ 'No complex configuration. Sign up, create a project, and invite your team in under a minute.',
65
+ iconBg: 'bg-cyan-100',
66
+ hoverBorder: 'hover:border-cyan-200',
67
+ },
68
+ ];
69
+
70
+ const getIcon = (name: string, color: string) => {
71
+ const className = `h-6 w-6 ${color}`;
72
+ switch (name) {
73
+ case 'layoutDashboard': return <LayoutDashboard className={className} />;
74
+ case 'listChecks': return <ListChecks className={className} />;
75
+ case 'users': return <Users className={className} />;
76
+ case 'barChart': return <BarChart3 className={className} />;
77
+ case 'shield': return <Shield className={className} />;
78
+ case 'zap': return <Zap className={className} />;
79
+ default: return null;
80
+ }
81
+ };
82
+
83
+ return (
84
+ <section id="features" className="py-24 bg-gray-50">
85
+ <div className="mx-auto max-w-7xl px-4">
86
+ <div className="mx-auto max-w-2xl text-center">
87
+ <p className="text-sm font-semibold uppercase tracking-wider text-primary-600">
88
+ Features
89
+ </p>
90
+ <h2 className="mt-2 heading-section text-gray-900">
91
+ Everything your team needs
92
+ </h2>
93
+ <p className="mt-4 text-lg text-gray-600">
94
+ Simple, powerful tools to keep projects moving and teams productive.
95
+ </p>
96
+ </div>
97
+ <div className="mt-16 grid gap-8 sm:grid-cols-2 lg:grid-cols-3">
98
+ {features.map((feature) => (
99
+ <div
100
+ key={feature.title}
101
+ className={`group rounded-xl border border-gray-200 bg-white p-8 shadow-sm transition-all duration-300 ${feature.hoverBorder} hover:shadow-lg hover:-translate-y-1`}
102
+ >
103
+ <div className={`flex h-12 w-12 items-center justify-center rounded-lg ${feature.iconBg} transition-transform duration-200 group-hover:scale-110`}>
104
+ {getIcon(feature.icon, feature.iconColor)}
105
+ </div>
106
+ <h3 className="mt-5 text-lg font-semibold text-gray-900">
107
+ {feature.title}
108
+ </h3>
109
+ <p className="mt-2 leading-relaxed text-gray-600">{feature.description}</p>
110
+ </div>
111
+ ))}
112
+ </div>
113
+ </div>
114
+ </section>
115
+ );
116
+ }
@@ -0,0 +1,146 @@
1
+ 'use client';
2
+
3
+ import Link from 'next/link';
4
+ import { ArrowRight, BarChart3, Shield, Zap, CheckCircle2 } from 'lucide-react';
5
+ import { APP_NAME } from '@/lib/constants';
6
+
7
+ function DashboardMockup() {
8
+ return (
9
+ <div className="animate-fade-in-up-delay-3 mx-auto mt-16 max-w-4xl px-4">
10
+ <div className="rounded-xl border border-gray-200 bg-white shadow-2xl shadow-gray-200/50 ring-1 ring-gray-900/5 overflow-hidden">
11
+ {/* Browser chrome */}
12
+ <div className="flex items-center gap-2 border-b border-gray-100 bg-gray-50 px-4 py-2.5">
13
+ <div className="flex gap-1.5">
14
+ <div className="h-2.5 w-2.5 rounded-full bg-red-400" />
15
+ <div className="h-2.5 w-2.5 rounded-full bg-amber-400" />
16
+ <div className="h-2.5 w-2.5 rounded-full bg-green-400" />
17
+ </div>
18
+ <div className="ml-3 flex-1 rounded-md bg-gray-100 px-3 py-1 text-xs text-gray-400">
19
+ app.yourcompany.com/dashboard
20
+ </div>
21
+ </div>
22
+ {/* Mock dashboard content */}
23
+ <div className="p-6">
24
+ <div className="grid grid-cols-4 gap-3">
25
+ {[
26
+ { label: 'Projects', value: '12', color: 'bg-blue-500' },
27
+ { label: 'Tasks', value: '48', color: 'bg-amber-500' },
28
+ { label: 'Completed', value: '31', color: 'bg-green-500' },
29
+ { label: 'Team', value: '8', color: 'bg-purple-500' },
30
+ ].map((stat) => (
31
+ <div key={stat.label} className="rounded-lg border border-gray-100 p-3">
32
+ <div className={`mb-2 h-1.5 w-6 rounded-full ${stat.color} opacity-60`} />
33
+ <div className="text-lg font-bold text-gray-900">{stat.value}</div>
34
+ <div className="text-xs text-gray-500">{stat.label}</div>
35
+ </div>
36
+ ))}
37
+ </div>
38
+ <div className="mt-4 space-y-2">
39
+ {[
40
+ { name: 'Website Redesign', status: 'In Progress', pct: 65 },
41
+ { name: 'Mobile App v2', status: 'Planning', pct: 20 },
42
+ { name: 'API Integration', status: 'Review', pct: 85 },
43
+ ].map((project) => (
44
+ <div key={project.name} className="flex items-center gap-3 rounded-lg border border-gray-50 bg-gray-50/50 px-3 py-2">
45
+ <div className="flex-1">
46
+ <div className="text-sm font-medium text-gray-800">{project.name}</div>
47
+ <div className="text-xs text-gray-400">{project.status}</div>
48
+ </div>
49
+ <div className="h-1.5 w-24 rounded-full bg-gray-100">
50
+ <div
51
+ className="h-1.5 rounded-full bg-primary-500"
52
+ style={{ width: `${project.pct}%` }}
53
+ />
54
+ </div>
55
+ </div>
56
+ ))}
57
+ </div>
58
+ </div>
59
+ </div>
60
+ </div>
61
+ );
62
+ }
63
+
64
+ export function Hero() {
65
+ const highlights = [
66
+ { icon: 'zap', label: 'Fast setup' },
67
+ { icon: 'shield', label: 'Secure by default' },
68
+ { icon: 'barChart', label: 'Built-in analytics' },
69
+ ];
70
+
71
+ const getIcon = (name: string) => {
72
+ const className = "h-4 w-4 text-primary-500";
73
+ switch (name) {
74
+ case 'zap': return <Zap className={className} />;
75
+ case 'shield': return <Shield className={className} />;
76
+ case 'barChart': return <BarChart3 className={className} />;
77
+ default: return null;
78
+ }
79
+ };
80
+
81
+ return (
82
+ <section className="relative overflow-hidden bg-gradient-to-b from-primary-50 via-white to-white py-24 sm:py-32">
83
+ <div className="absolute inset-0 bg-[radial-gradient(ellipse_at_top,_var(--tw-gradient-stops))] from-primary-100/40 via-transparent to-transparent" />
84
+ <div className="relative mx-auto max-w-7xl px-4 text-center">
85
+ <div className="animate-fade-in-up mx-auto inline-flex items-center gap-2 rounded-full border border-primary-200 bg-primary-50 px-4 py-1.5 text-sm font-medium text-primary-700">
86
+ <span className="h-1.5 w-1.5 rounded-full bg-primary-500" />
87
+ Free to get started
88
+ </div>
89
+ <h1 className="animate-fade-in-up-delay-1 mt-6 heading-hero tracking-tight text-gray-900">
90
+ Manage Projects,{' '}
91
+ <span className="bg-gradient-to-r from-primary-600 to-primary-500 bg-clip-text text-transparent">
92
+ Ship Faster
93
+ </span>
94
+ </h1>
95
+ <p className="animate-fade-in-up-delay-2 mx-auto mt-6 max-w-2xl text-lg leading-relaxed text-gray-600">
96
+ {APP_NAME} gives your team a clear view of every project, task, and deadline.
97
+ Stop juggling spreadsheets and start delivering on time.
98
+ </p>
99
+ <div className="animate-fade-in-up-delay-2 mt-10 flex flex-col items-center justify-center gap-4 sm:flex-row">
100
+ <Link
101
+ href="/login"
102
+ className="group inline-flex items-center gap-2 rounded-lg bg-primary-600 px-6 py-3 text-base font-medium text-white shadow-lg shadow-primary-200 hover:bg-primary-700 hover:shadow-xl hover:shadow-primary-200/50 transition-all"
103
+ >
104
+ Start Free
105
+ <ArrowRight className="h-4 w-4 transition-transform group-hover:translate-x-0.5" />
106
+ </Link>
107
+ <a
108
+ href="#how-it-works"
109
+ className="inline-flex items-center gap-2 rounded-lg border border-gray-300 bg-white px-6 py-3 text-base font-medium text-gray-700 hover:bg-gray-50 hover:border-gray-400 transition-all"
110
+ >
111
+ See How It Works
112
+ </a>
113
+ </div>
114
+
115
+ {/* Social proof */}
116
+ <div className="animate-fade-in-up-delay-3 mt-12 flex flex-col items-center gap-3">
117
+ <div className="flex -space-x-2">
118
+ {['bg-blue-500', 'bg-green-500', 'bg-amber-500', 'bg-purple-500', 'bg-pink-500'].map((color, i) => (
119
+ <div
120
+ key={i}
121
+ className={`flex h-8 w-8 items-center justify-center rounded-full ${color} text-xs font-bold text-white ring-2 ring-white`}
122
+ >
123
+ {String.fromCharCode(65 + i)}
124
+ </div>
125
+ ))}
126
+ </div>
127
+ <div className="flex items-center gap-1.5 text-sm text-gray-500">
128
+ <CheckCircle2 className="h-4 w-4 text-green-500" />
129
+ <span>Trusted by teams who ship on time</span>
130
+ </div>
131
+ </div>
132
+
133
+ <div className="animate-fade-in-up-delay-4 mt-12 flex items-center justify-center gap-8 sm:gap-12">
134
+ {highlights.map((item) => (
135
+ <div key={item.label} className="flex items-center gap-2 text-sm text-gray-500">
136
+ {getIcon(item.icon)}
137
+ <span>{item.label}</span>
138
+ </div>
139
+ ))}
140
+ </div>
141
+
142
+ <DashboardMockup />
143
+ </div>
144
+ </section>
145
+ );
146
+ }
@@ -0,0 +1,80 @@
1
+ 'use client';
2
+
3
+ import { FolderPlus, ListTodo, TrendingUp } from 'lucide-react';
4
+
5
+ export function HowItWorks() {
6
+ const steps = [
7
+ {
8
+ number: '01',
9
+ icon: 'folderPlus',
10
+ title: 'Create a Project',
11
+ description:
12
+ 'Set up a project in seconds. Add a name, description, and due date. Invite your team to collaborate.',
13
+ color: 'bg-blue-600',
14
+ shadow: 'shadow-blue-200',
15
+ },
16
+ {
17
+ number: '02',
18
+ icon: 'listTodo',
19
+ title: 'Add Tasks & Assign',
20
+ description:
21
+ 'Break work into tasks with priorities and assignees. Everyone knows what to do and when it is due.',
22
+ color: 'bg-amber-600',
23
+ shadow: 'shadow-amber-200',
24
+ },
25
+ {
26
+ number: '03',
27
+ icon: 'trendingUp',
28
+ title: 'Track & Deliver',
29
+ description:
30
+ 'Monitor progress on your dashboard. Update task statuses with a click and ship on schedule.',
31
+ color: 'bg-green-600',
32
+ shadow: 'shadow-green-200',
33
+ },
34
+ ];
35
+
36
+ const getIcon = (name: string) => {
37
+ const className = "h-7 w-7 text-white";
38
+ switch (name) {
39
+ case 'folderPlus': return <FolderPlus className={className} />;
40
+ case 'listTodo': return <ListTodo className={className} />;
41
+ case 'trendingUp': return <TrendingUp className={className} />;
42
+ default: return null;
43
+ }
44
+ };
45
+
46
+ return (
47
+ <section id="how-it-works" className="py-24 bg-gray-50">
48
+ <div className="mx-auto max-w-7xl px-4">
49
+ <div className="mx-auto max-w-2xl text-center">
50
+ <p className="text-sm font-semibold uppercase tracking-wider text-primary-600">
51
+ How It Works
52
+ </p>
53
+ <h2 className="mt-2 heading-section text-gray-900">
54
+ Up and running in three steps
55
+ </h2>
56
+ <p className="mt-4 text-lg text-gray-600">
57
+ No complicated setup. No steep learning curve. Just results.
58
+ </p>
59
+ </div>
60
+ <div className="relative mt-16 grid gap-12 md:grid-cols-3">
61
+ {/* Connecting line between steps (desktop only) */}
62
+ <div className="absolute top-8 left-[calc(16.67%+32px)] right-[calc(16.67%+32px)] hidden h-0.5 bg-gradient-to-r from-blue-200 via-amber-200 to-green-200 md:block" />
63
+
64
+ {steps.map((step) => (
65
+ <div key={step.number} className="relative text-center">
66
+ <div className={`relative z-10 mx-auto flex h-16 w-16 items-center justify-center rounded-2xl ${step.color} shadow-lg ${step.shadow}`}>
67
+ {getIcon(step.icon)}
68
+ </div>
69
+ <span className="mt-4 block text-xs font-bold uppercase tracking-widest text-primary-500">
70
+ Step {step.number}
71
+ </span>
72
+ <h3 className="mt-2 text-xl font-semibold text-gray-900">{step.title}</h3>
73
+ <p className="mt-3 leading-relaxed text-gray-600">{step.description}</p>
74
+ </div>
75
+ ))}
76
+ </div>
77
+ </div>
78
+ </section>
79
+ );
80
+ }
@@ -0,0 +1,124 @@
1
+ import { Check } from 'lucide-react';
2
+
3
+ const plans = [
4
+ {
5
+ name: 'Free',
6
+ price: 0,
7
+ priceLabel: '$0',
8
+ period: 'forever',
9
+ description: 'Perfect for trying out the platform',
10
+ features: [
11
+ '3 projects',
12
+ '10 tasks per project',
13
+ 'Basic analytics',
14
+ 'Email support',
15
+ ],
16
+ cta: 'Get Started Free',
17
+ popular: false,
18
+ },
19
+ {
20
+ name: 'Pro',
21
+ price: 1900,
22
+ priceLabel: '$19',
23
+ period: 'per month',
24
+ description: 'Best for growing teams',
25
+ features: [
26
+ 'Unlimited projects',
27
+ 'Unlimited tasks',
28
+ 'Advanced analytics',
29
+ 'Priority support',
30
+ 'Team collaboration',
31
+ 'Custom integrations',
32
+ ],
33
+ cta: 'Start Pro Trial',
34
+ popular: true,
35
+ },
36
+ {
37
+ name: 'Enterprise',
38
+ price: 9900,
39
+ priceLabel: '$99',
40
+ period: 'per month',
41
+ description: 'For large organizations',
42
+ features: [
43
+ 'Everything in Pro',
44
+ 'SSO authentication',
45
+ 'Audit logs',
46
+ 'Dedicated account manager',
47
+ 'SLA guarantee',
48
+ 'Custom contracts',
49
+ ],
50
+ cta: 'Contact Sales',
51
+ popular: false,
52
+ },
53
+ ];
54
+
55
+ export function Pricing() {
56
+ return (
57
+ <section id="pricing" className="py-24 bg-white">
58
+ <div className="mx-auto max-w-7xl px-4">
59
+ <div className="mx-auto max-w-2xl text-center">
60
+ <p className="text-sm font-semibold uppercase tracking-wider text-primary-600">
61
+ Pricing
62
+ </p>
63
+ <h2 className="mt-2 heading-section text-gray-900">
64
+ Simple, transparent pricing
65
+ </h2>
66
+ <p className="mt-4 text-lg text-gray-600">
67
+ Choose the plan that works for your team. All plans include a 14-day free trial.
68
+ </p>
69
+ </div>
70
+
71
+ <div className="mt-16 grid gap-8 lg:grid-cols-3">
72
+ {plans.map((plan) => (
73
+ <div
74
+ key={plan.name}
75
+ className={`relative rounded-2xl border ${
76
+ plan.popular
77
+ ? 'border-primary-500 shadow-xl ring-2 ring-primary-500/20'
78
+ : 'border-gray-200 shadow-md hover:shadow-xl transition-shadow duration-300'
79
+ } bg-white p-8`}
80
+ >
81
+ {plan.popular && (
82
+ <span className="absolute -top-3 left-1/2 -translate-x-1/2 rounded-full bg-primary-600 px-4 py-1 text-sm font-medium text-white">
83
+ Most Popular
84
+ </span>
85
+ )}
86
+
87
+ <h3 className="text-xl font-semibold text-gray-900">{plan.name}</h3>
88
+ <p className="mt-2 text-sm text-gray-600">{plan.description}</p>
89
+
90
+ <div className="mt-6">
91
+ <span className="text-4xl font-bold text-gray-900">{plan.priceLabel}</span>
92
+ <span className="text-gray-600">/{plan.period}</span>
93
+ </div>
94
+
95
+ <ul className="mt-8 space-y-4">
96
+ {plan.features.map((feature) => (
97
+ <li key={feature} className="flex items-start gap-3">
98
+ <Check className="h-5 w-5 text-primary-600 shrink-0" />
99
+ <span className="text-gray-600">{feature}</span>
100
+ </li>
101
+ ))}
102
+ </ul>
103
+
104
+ <div className="mt-8">
105
+ <a
106
+ href="/login"
107
+ className={`block w-full rounded-lg py-3 text-center font-medium transition-colors ${
108
+ plan.popular
109
+ ? 'bg-primary-600 text-white hover:bg-primary-700'
110
+ : plan.price === 0
111
+ ? 'bg-gray-100 text-gray-900 hover:bg-gray-200'
112
+ : 'bg-gray-900 text-white hover:bg-gray-800'
113
+ }`}
114
+ >
115
+ {plan.cta}
116
+ </a>
117
+ </div>
118
+ </div>
119
+ ))}
120
+ </div>
121
+ </div>
122
+ </section>
123
+ );
124
+ }
@@ -0,0 +1,78 @@
1
+ import { Star } from 'lucide-react';
2
+
3
+ const testimonials = [
4
+ {
5
+ quote:
6
+ 'We switched from a messy spreadsheet to this and our team is finally aligned. Projects get shipped on time now.',
7
+ name: 'Sarah Chen',
8
+ role: 'Engineering Manager',
9
+ company: 'Acme Corp',
10
+ color: 'bg-blue-500',
11
+ },
12
+ {
13
+ quote:
14
+ 'The setup took less than a minute. No config files, no environment variables. It just works.',
15
+ name: 'Marcus Rodriguez',
16
+ role: 'CTO',
17
+ company: 'StartupHQ',
18
+ color: 'bg-green-500',
19
+ },
20
+ {
21
+ quote:
22
+ 'Our team went from constant status meetings to a dashboard everyone actually checks. Huge time saver.',
23
+ name: 'Emily Park',
24
+ role: 'Product Lead',
25
+ company: 'BuildFast',
26
+ color: 'bg-purple-500',
27
+ },
28
+ ];
29
+
30
+ export function Testimonials() {
31
+ return (
32
+ <section id="testimonials" className="py-24 bg-white">
33
+ <div className="mx-auto max-w-7xl px-4">
34
+ <div className="mx-auto max-w-2xl text-center">
35
+ <p className="text-sm font-semibold uppercase tracking-wider text-primary-600">
36
+ Testimonials
37
+ </p>
38
+ <h2 className="mt-2 heading-section text-gray-900">
39
+ Teams love using it
40
+ </h2>
41
+ <p className="mt-4 text-lg text-gray-600">
42
+ See why teams switch to a simpler way of managing projects.
43
+ </p>
44
+ </div>
45
+ <div className="mt-16 grid gap-8 md:grid-cols-3">
46
+ {testimonials.map((testimonial) => (
47
+ <div
48
+ key={testimonial.name}
49
+ className="rounded-xl border border-gray-200 bg-white p-8 shadow-sm transition-all duration-300 hover:shadow-lg hover:-translate-y-1"
50
+ >
51
+ <div className="flex gap-0.5">
52
+ {[...Array(5)].map((_, i) => (
53
+ <Star key={i} className="h-4 w-4 fill-amber-400 text-amber-400" />
54
+ ))}
55
+ </div>
56
+ <p className="mt-4 leading-relaxed text-gray-600">
57
+ &ldquo;{testimonial.quote}&rdquo;
58
+ </p>
59
+ <div className="mt-6 flex items-center gap-3">
60
+ <div
61
+ className={`flex h-10 w-10 items-center justify-center rounded-full ${testimonial.color} text-sm font-bold text-white`}
62
+ >
63
+ {testimonial.name.split(' ').map((n) => n[0]).join('')}
64
+ </div>
65
+ <div>
66
+ <p className="text-sm font-semibold text-gray-900">{testimonial.name}</p>
67
+ <p className="text-xs text-gray-500">
68
+ {testimonial.role}, {testimonial.company}
69
+ </p>
70
+ </div>
71
+ </div>
72
+ </div>
73
+ ))}
74
+ </div>
75
+ </div>
76
+ </section>
77
+ );
78
+ }
@@ -0,0 +1,11 @@
1
+ 'use client';
2
+
3
+ import { ReactNode } from 'react';
4
+ import { ToastProvider } from '@varity-labs/ui-kit';
5
+
6
+ export function Providers({ children }: { children: ReactNode }) {
7
+ // Only ToastProvider at the global level.
8
+ // PrivyStack is added in dashboard/layout.tsx and login/page.tsx —
9
+ // the landing page loads instantly without any auth dependency.
10
+ return <ToastProvider>{children}</ToastProvider>;
11
+ }
@@ -0,0 +1,71 @@
1
+ import Link from 'next/link';
2
+ import { CheckCircle } from 'lucide-react';
3
+ import { APP_NAME } from '@/lib/constants';
4
+
5
+ export function Footer() {
6
+ const year = new Date().getFullYear();
7
+
8
+ return (
9
+ <footer className="border-t border-gray-200 bg-white py-12">
10
+ <div className="mx-auto max-w-7xl px-4">
11
+ <div className="flex flex-col items-center justify-between gap-6 sm:flex-row">
12
+ <div className="flex items-center gap-2">
13
+ <CheckCircle className="h-5 w-5 text-primary-600" />
14
+ <span className="font-semibold text-gray-900">{APP_NAME}</span>
15
+ </div>
16
+ <div className="flex items-center gap-6 text-sm text-gray-500">
17
+ <a href="#features" className="hover:text-gray-700 transition-colors">
18
+ Features
19
+ </a>
20
+ <a href="#how-it-works" className="hover:text-gray-700 transition-colors">
21
+ How It Works
22
+ </a>
23
+ <a href="#pricing" className="hover:text-gray-700 transition-colors">
24
+ Pricing
25
+ </a>
26
+ <Link href="/login" className="hover:text-gray-700 transition-colors">
27
+ Sign In
28
+ </Link>
29
+ </div>
30
+ </div>
31
+ <div className="mt-8 border-t border-gray-100 pt-6 text-center text-sm text-gray-400">
32
+ &copy; {year} {APP_NAME}. Built with{' '}
33
+ <a
34
+ href="https://varity.so"
35
+ target="_blank"
36
+ rel="noopener noreferrer"
37
+ className="inline-flex items-center gap-1 text-primary-500 hover:text-primary-600 transition-colors"
38
+ >
39
+ <svg
40
+ width="14"
41
+ height="14"
42
+ viewBox="0 0 64 64"
43
+ xmlns="http://www.w3.org/2000/svg"
44
+ className="inline-block"
45
+ >
46
+ <defs>
47
+ <linearGradient id="varity-lf-f1" x1="0%" y1="0%" x2="100%" y2="100%">
48
+ <stop offset="0%" stopColor="#5EEAD4"/><stop offset="100%" stopColor="#0D9488"/>
49
+ </linearGradient>
50
+ <linearGradient id="varity-lf-f2" x1="100%" y1="0%" x2="0%" y2="100%">
51
+ <stop offset="0%" stopColor="#60A5FA"/><stop offset="100%" stopColor="#1D4ED8"/>
52
+ </linearGradient>
53
+ <linearGradient id="varity-lf-f4" x1="0%" y1="100%" x2="100%" y2="0%">
54
+ <stop offset="0%" stopColor="#14B8A6"/><stop offset="100%" stopColor="#2DD4BF"/>
55
+ </linearGradient>
56
+ </defs>
57
+ <path d="M32 6 L48 22 L32 32 L16 22 Z" fill="url(#varity-lf-f4)"/>
58
+ <path d="M16 22 L32 32 L32 58 L8 36 Z" fill="url(#varity-lf-f1)"/>
59
+ <path d="M48 22 L56 36 L32 58 L32 32 Z" fill="url(#varity-lf-f2)"/>
60
+ <path d="M8 36 L32 58 L20 58 Z" fill="url(#varity-lf-f1)" opacity="0.7"/>
61
+ <path d="M56 36 L44 58 L32 58 Z" fill="url(#varity-lf-f2)" opacity="0.7"/>
62
+ <path d="M32 12 L40 22 L32 28 L24 22 Z" fill="white" opacity="0.25"/>
63
+ </svg>
64
+ Varity
65
+ </a>
66
+ .
67
+ </div>
68
+ </div>
69
+ </footer>
70
+ );
71
+ }