costhawk 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/.claude/settings.local.json +32 -0
- package/STRATEGIC_PLAN_2025-12-01.md +934 -0
- package/costcanary/.claude/settings.local.json +9 -0
- package/costcanary/.env.production.template +38 -0
- package/costcanary/.eslintrc.json +22 -0
- package/costcanary/.nvmrc +1 -0
- package/costcanary/.prettierignore +11 -0
- package/costcanary/.prettierrc.json +12 -0
- package/costcanary/ADMIN_SETUP.md +68 -0
- package/costcanary/CLAUDE.md +228 -0
- package/costcanary/CLERK_SETUP.md +69 -0
- package/costcanary/DATABASE_SETUP.md +136 -0
- package/costcanary/DEMO_CHECKLIST.md +62 -0
- package/costcanary/DEPLOYMENT.md +31 -0
- package/costcanary/PRODUCTION_RECOVERY.md +109 -0
- package/costcanary/README.md +247 -0
- package/costcanary/STRIPE_SECURITY_AUDIT.md +123 -0
- package/costcanary/TESTING_ADMIN.md +92 -0
- package/costcanary/app/(auth)/sign-in/[[...sign-in]]/page.tsx +25 -0
- package/costcanary/app/(auth)/sign-up/[[...sign-up]]/page.tsx +25 -0
- package/costcanary/app/(dashboard)/dashboard/admin/page.tsx +260 -0
- package/costcanary/app/(dashboard)/dashboard/alerts/page.tsx +64 -0
- package/costcanary/app/(dashboard)/dashboard/api-keys/page.tsx +231 -0
- package/costcanary/app/(dashboard)/dashboard/billing/page.tsx +349 -0
- package/costcanary/app/(dashboard)/dashboard/layout.tsx +188 -0
- package/costcanary/app/(dashboard)/dashboard/page.tsx +13 -0
- package/costcanary/app/(dashboard)/dashboard/playground/page.tsx +605 -0
- package/costcanary/app/(dashboard)/dashboard/settings/page.tsx +86 -0
- package/costcanary/app/(dashboard)/dashboard/usage/page.tsx +354 -0
- package/costcanary/app/(dashboard)/dashboard/wrapped-keys/page.tsx +677 -0
- package/costcanary/app/(marketing)/page.tsx +90 -0
- package/costcanary/app/(marketing)/pricing/page.tsx +272 -0
- package/costcanary/app/admin/pricing-status/page.tsx +338 -0
- package/costcanary/app/api/admin/check-pricing/route.ts +127 -0
- package/costcanary/app/api/admin/debug/route.ts +44 -0
- package/costcanary/app/api/admin/fix-pricing/route.ts +216 -0
- package/costcanary/app/api/admin/pricing-jobs/[jobId]/route.ts +48 -0
- package/costcanary/app/api/admin/pricing-jobs/route.ts +45 -0
- package/costcanary/app/api/admin/trigger-pricing/route.ts +209 -0
- package/costcanary/app/api/admin/whoami/route.ts +44 -0
- package/costcanary/app/api/auth/clerk/[...nextjs]/route.ts +93 -0
- package/costcanary/app/api/debug/wrapped-key/route.ts +51 -0
- package/costcanary/app/api/debug-status/route.ts +9 -0
- package/costcanary/app/api/debug-version/route.ts +12 -0
- package/costcanary/app/api/health/route.ts +14 -0
- package/costcanary/app/api/health-simple/route.ts +18 -0
- package/costcanary/app/api/keys/route.ts +162 -0
- package/costcanary/app/api/keys/wrapped/[id]/revoke/route.ts +86 -0
- package/costcanary/app/api/keys/wrapped/[id]/rotate/route.ts +81 -0
- package/costcanary/app/api/keys/wrapped/route.ts +241 -0
- package/costcanary/app/api/optimizer/preview/route.ts +147 -0
- package/costcanary/app/api/optimizer/route.ts +118 -0
- package/costcanary/app/api/pricing/models/route.ts +102 -0
- package/costcanary/app/api/proxy/[...path]/route.ts +391 -0
- package/costcanary/app/api/proxy/anthropic/route.ts +539 -0
- package/costcanary/app/api/proxy/google/route.ts +395 -0
- package/costcanary/app/api/proxy/openai/route.ts +529 -0
- package/costcanary/app/api/simple-test/route.ts +7 -0
- package/costcanary/app/api/stripe/checkout/route.ts +201 -0
- package/costcanary/app/api/stripe/webhook/route.ts +392 -0
- package/costcanary/app/api/test-connection/route.ts +209 -0
- package/costcanary/app/api/test-proxy/route.ts +7 -0
- package/costcanary/app/api/test-simple/route.ts +20 -0
- package/costcanary/app/api/usage/current/route.ts +112 -0
- package/costcanary/app/api/usage/stats/route.ts +129 -0
- package/costcanary/app/api/usage/stream/route.ts +113 -0
- package/costcanary/app/api/usage/summary/route.ts +67 -0
- package/costcanary/app/api/usage/trend/route.ts +119 -0
- package/costcanary/app/api/ws/route.ts +23 -0
- package/costcanary/app/globals.css +280 -0
- package/costcanary/app/layout.tsx +87 -0
- package/costcanary/components/Header.tsx +85 -0
- package/costcanary/components/dashboard/AddApiKeyModal.tsx +264 -0
- package/costcanary/components/dashboard/dashboard-content.tsx +329 -0
- package/costcanary/components/landing/DashboardPreview.tsx +222 -0
- package/costcanary/components/landing/Features.tsx +238 -0
- package/costcanary/components/landing/Footer.tsx +83 -0
- package/costcanary/components/landing/Hero.tsx +193 -0
- package/costcanary/components/landing/Pricing.tsx +250 -0
- package/costcanary/components/landing/Testimonials.tsx +248 -0
- package/costcanary/components/theme-provider.tsx +8 -0
- package/costcanary/components/ui/alert.tsx +59 -0
- package/costcanary/components/ui/badge.tsx +36 -0
- package/costcanary/components/ui/button.tsx +56 -0
- package/costcanary/components/ui/card.tsx +79 -0
- package/costcanary/components/ui/dialog.tsx +122 -0
- package/costcanary/components/ui/input.tsx +22 -0
- package/costcanary/components/ui/label.tsx +26 -0
- package/costcanary/components/ui/progress.tsx +28 -0
- package/costcanary/components/ui/select.tsx +160 -0
- package/costcanary/components/ui/separator.tsx +31 -0
- package/costcanary/components/ui/switch.tsx +29 -0
- package/costcanary/components/ui/tabs.tsx +55 -0
- package/costcanary/components/ui/toast.tsx +127 -0
- package/costcanary/components/ui/toaster.tsx +35 -0
- package/costcanary/components/ui/use-toast.ts +189 -0
- package/costcanary/components.json +17 -0
- package/costcanary/debug-wrapped-keys.md +117 -0
- package/costcanary/fix-console.sh +30 -0
- package/costcanary/lib/admin-auth.ts +226 -0
- package/costcanary/lib/admin-security.ts +124 -0
- package/costcanary/lib/audit-events.ts +62 -0
- package/costcanary/lib/audit.ts +158 -0
- package/costcanary/lib/chart-colors.ts +152 -0
- package/costcanary/lib/cost-calculator.ts +212 -0
- package/costcanary/lib/db-utils.ts +325 -0
- package/costcanary/lib/db.ts +14 -0
- package/costcanary/lib/encryption.ts +120 -0
- package/costcanary/lib/kms.ts +358 -0
- package/costcanary/lib/model-alias.ts +180 -0
- package/costcanary/lib/pricing.ts +292 -0
- package/costcanary/lib/prisma.ts +52 -0
- package/costcanary/lib/railway-db.ts +157 -0
- package/costcanary/lib/sse-parser.ts +283 -0
- package/costcanary/lib/stripe-client.ts +81 -0
- package/costcanary/lib/stripe-server.ts +52 -0
- package/costcanary/lib/tokens.ts +396 -0
- package/costcanary/lib/usage-limits.ts +164 -0
- package/costcanary/lib/utils.ts +6 -0
- package/costcanary/lib/websocket.ts +153 -0
- package/costcanary/lib/wrapped-keys.ts +531 -0
- package/costcanary/market-research.md +443 -0
- package/costcanary/middleware.ts +48 -0
- package/costcanary/next.config.js +43 -0
- package/costcanary/nia-sources.md +151 -0
- package/costcanary/package-lock.json +12162 -0
- package/costcanary/package.json +92 -0
- package/costcanary/package.json.backup +89 -0
- package/costcanary/postcss.config.js +6 -0
- package/costcanary/pricing-worker/.env.example +8 -0
- package/costcanary/pricing-worker/README.md +81 -0
- package/costcanary/pricing-worker/package-lock.json +1109 -0
- package/costcanary/pricing-worker/package.json +26 -0
- package/costcanary/pricing-worker/railway.json +13 -0
- package/costcanary/pricing-worker/schema.prisma +326 -0
- package/costcanary/pricing-worker/src/index.ts +115 -0
- package/costcanary/pricing-worker/src/services/pricing-updater.ts +79 -0
- package/costcanary/pricing-worker/src/services/tavily-client.ts +474 -0
- package/costcanary/pricing-worker/test-tavily.ts +47 -0
- package/costcanary/pricing-worker/tsconfig.json +24 -0
- package/costcanary/prisma/migrations/001_add_stripe_fields.sql +26 -0
- package/costcanary/prisma/schema.prisma +326 -0
- package/costcanary/prisma/seed-pricing.ts +133 -0
- package/costcanary/public/costhawk-logo.png +0 -0
- package/costcanary/railway.json +30 -0
- package/costcanary/railway.toml +16 -0
- package/costcanary/research-nia.md +298 -0
- package/costcanary/research.md +411 -0
- package/costcanary/scripts/build-production.js +65 -0
- package/costcanary/scripts/check-current-pricing.ts +51 -0
- package/costcanary/scripts/check-pricing-data.ts +174 -0
- package/costcanary/scripts/create-stripe-prices.js +49 -0
- package/costcanary/scripts/fix-pricing-data.ts +135 -0
- package/costcanary/scripts/fix-pricing-db.ts +148 -0
- package/costcanary/scripts/postinstall.js +58 -0
- package/costcanary/scripts/railway-deploy.sh +52 -0
- package/costcanary/scripts/run-migration.js +61 -0
- package/costcanary/scripts/start-production.js +175 -0
- package/costcanary/scripts/test-wrapped-key.ts +85 -0
- package/costcanary/scripts/validate-deployment.js +176 -0
- package/costcanary/scripts/validate-production.js +119 -0
- package/costcanary/server.js.backup +38 -0
- package/costcanary/tailwind.config.ts +216 -0
- package/costcanary/test-pricing-status.sh +27 -0
- package/costcanary/tsconfig.json +42 -0
- package/docs/sessions/session-2025-12-01.md +570 -0
- package/executive-summary.md +302 -0
- package/index.js +1 -0
- package/nia-sources.md +163 -0
- package/package.json +16 -0
- package/research.md +750 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { Metadata } from "next"
|
|
2
|
+
import { Hero } from "@/components/landing/Hero"
|
|
3
|
+
import { DashboardPreview } from "@/components/landing/DashboardPreview"
|
|
4
|
+
import { Features } from "@/components/landing/Features"
|
|
5
|
+
import { Pricing } from "@/components/landing/Pricing"
|
|
6
|
+
import { Testimonials } from "@/components/landing/Testimonials"
|
|
7
|
+
import { Footer } from "@/components/landing/Footer"
|
|
8
|
+
|
|
9
|
+
export const metadata: Metadata = {
|
|
10
|
+
title: "CostHawk - Track Your LLM API Costs and Avoid Surprise Bills",
|
|
11
|
+
description:
|
|
12
|
+
"Tired of unpredictable LLM API bills? CostHawk gives you the tools to monitor your AI spending in real-time, so you can build innovative applications without worrying about surprise costs. Track OpenAI, Anthropic, and more.",
|
|
13
|
+
keywords: [
|
|
14
|
+
"LLM cost monitoring",
|
|
15
|
+
"AI API costs",
|
|
16
|
+
"OpenAI cost tracking",
|
|
17
|
+
"Anthropic billing",
|
|
18
|
+
"API cost management",
|
|
19
|
+
"developer tools for AI",
|
|
20
|
+
"avoid surprise bills",
|
|
21
|
+
"AI cost optimization",
|
|
22
|
+
],
|
|
23
|
+
authors: [{ name: "CostHawk" }],
|
|
24
|
+
creator: "CostHawk",
|
|
25
|
+
publisher: "CostHawk",
|
|
26
|
+
formatDetection: {
|
|
27
|
+
email: false,
|
|
28
|
+
address: false,
|
|
29
|
+
telephone: false,
|
|
30
|
+
},
|
|
31
|
+
metadataBase: new URL("https://costcanary.com"),
|
|
32
|
+
alternates: {
|
|
33
|
+
canonical: "/",
|
|
34
|
+
},
|
|
35
|
+
openGraph: {
|
|
36
|
+
title: "CostHawk - Track Your LLM API Costs and Avoid Surprise Bills",
|
|
37
|
+
description:
|
|
38
|
+
"Monitor and optimize your AI API costs across all providers. Real-time tracking, intelligent alerts, and powerful analytics to prevent surprise bills.",
|
|
39
|
+
url: "https://costcanary.com",
|
|
40
|
+
siteName: "CostHawk",
|
|
41
|
+
images: [
|
|
42
|
+
{
|
|
43
|
+
url: "/og-image.png",
|
|
44
|
+
width: 1200,
|
|
45
|
+
height: 630,
|
|
46
|
+
alt: "CostHawk - LLM API Cost Monitoring Platform",
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
locale: "en_US",
|
|
50
|
+
type: "website",
|
|
51
|
+
},
|
|
52
|
+
twitter: {
|
|
53
|
+
card: "summary_large_image",
|
|
54
|
+
title: "CostHawk - Track Your LLM API Costs and Avoid Surprise Bills",
|
|
55
|
+
description:
|
|
56
|
+
"Tired of unpredictable LLM API bills? Monitor your AI spending in real-time and avoid surprise costs. Start free today.",
|
|
57
|
+
images: ["/og-image.png"],
|
|
58
|
+
creator: "@costhawk",
|
|
59
|
+
},
|
|
60
|
+
robots: {
|
|
61
|
+
index: true,
|
|
62
|
+
follow: true,
|
|
63
|
+
googleBot: {
|
|
64
|
+
index: true,
|
|
65
|
+
follow: true,
|
|
66
|
+
"max-video-preview": -1,
|
|
67
|
+
"max-image-preview": "large",
|
|
68
|
+
"max-snippet": -1,
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
icons: {
|
|
72
|
+
icon: "/favicon.ico",
|
|
73
|
+
shortcut: "/favicon-16x16.png",
|
|
74
|
+
apple: "/apple-touch-icon.png",
|
|
75
|
+
},
|
|
76
|
+
manifest: "/site.webmanifest",
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export default function HomePage() {
|
|
80
|
+
return (
|
|
81
|
+
<main className="min-h-screen bg-slate-950">
|
|
82
|
+
<Hero />
|
|
83
|
+
<DashboardPreview />
|
|
84
|
+
<Features />
|
|
85
|
+
<Testimonials />
|
|
86
|
+
<Pricing />
|
|
87
|
+
<Footer />
|
|
88
|
+
</main>
|
|
89
|
+
)
|
|
90
|
+
}
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
import { Check, Zap, Shield, TrendingUp, Sparkles } from 'lucide-react';
|
|
5
|
+
import { Button } from '@/components/ui/button';
|
|
6
|
+
import { Switch } from '@/components/ui/switch';
|
|
7
|
+
import { motion } from 'framer-motion';
|
|
8
|
+
import { useAuth } from '@clerk/nextjs';
|
|
9
|
+
import { useRouter } from 'next/navigation';
|
|
10
|
+
import { PRICING_TIERS } from '@/lib/stripe-client';
|
|
11
|
+
|
|
12
|
+
export default function PricingPage() {
|
|
13
|
+
const [isAnnual, setIsAnnual] = useState(false);
|
|
14
|
+
const { isSignedIn } = useAuth();
|
|
15
|
+
const router = useRouter();
|
|
16
|
+
const [loading, setLoading] = useState<string | null>(null);
|
|
17
|
+
|
|
18
|
+
const handleSubscribe = async (tier: typeof PRICING_TIERS[0]) => {
|
|
19
|
+
if (tier.id === 'free') {
|
|
20
|
+
if (!isSignedIn) {
|
|
21
|
+
router.push('/sign-up');
|
|
22
|
+
} else {
|
|
23
|
+
router.push('/dashboard');
|
|
24
|
+
}
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (!isSignedIn) {
|
|
29
|
+
router.push(`/sign-up?redirect=/pricing&plan=${tier.id}`);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
setLoading(tier.id);
|
|
34
|
+
try {
|
|
35
|
+
const response = await fetch('/api/stripe/checkout', {
|
|
36
|
+
method: 'POST',
|
|
37
|
+
headers: {
|
|
38
|
+
'Content-Type': 'application/json',
|
|
39
|
+
},
|
|
40
|
+
body: JSON.stringify({
|
|
41
|
+
tierId: tier.id,
|
|
42
|
+
returnUrl: window.location.href,
|
|
43
|
+
}),
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const data = await response.json();
|
|
47
|
+
|
|
48
|
+
if (data.url) {
|
|
49
|
+
window.location.href = data.url;
|
|
50
|
+
} else {
|
|
51
|
+
console.error('No checkout URL returned');
|
|
52
|
+
}
|
|
53
|
+
} catch (error) {
|
|
54
|
+
console.error('Subscription error:', error);
|
|
55
|
+
} finally {
|
|
56
|
+
setLoading(null);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<div className="min-h-screen bg-black text-white">
|
|
62
|
+
{/* Hero Section */}
|
|
63
|
+
<section className="relative py-20 px-4">
|
|
64
|
+
<div className="absolute inset-0 bg-gradient-to-b from-purple-900/20 to-transparent" />
|
|
65
|
+
|
|
66
|
+
<div className="container mx-auto max-w-6xl relative z-10">
|
|
67
|
+
<motion.div
|
|
68
|
+
initial={{ opacity: 0, y: 20 }}
|
|
69
|
+
animate={{ opacity: 1, y: 0 }}
|
|
70
|
+
className="text-center mb-12"
|
|
71
|
+
>
|
|
72
|
+
<div className="inline-flex items-center gap-2 bg-purple-500/10 border border-purple-500/20 rounded-full px-4 py-2 mb-6">
|
|
73
|
+
<Sparkles className="w-4 h-4 text-purple-400" />
|
|
74
|
+
<span className="text-sm text-purple-300">Save 20% with annual billing</span>
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
<h1 className="text-5xl md:text-6xl font-bold mb-6 bg-gradient-to-r from-white to-purple-400 bg-clip-text text-transparent">
|
|
78
|
+
Simple, Transparent Pricing
|
|
79
|
+
</h1>
|
|
80
|
+
|
|
81
|
+
<p className="text-xl text-gray-300 max-w-2xl mx-auto mb-8">
|
|
82
|
+
Start free, upgrade when you need more. No hidden fees, no surprises.
|
|
83
|
+
</p>
|
|
84
|
+
|
|
85
|
+
{/* Billing Toggle */}
|
|
86
|
+
<div className="flex items-center justify-center gap-3 mb-12">
|
|
87
|
+
<span className={!isAnnual ? 'text-white' : 'text-gray-500'}>Monthly</span>
|
|
88
|
+
<Switch checked={isAnnual} onCheckedChange={setIsAnnual} />
|
|
89
|
+
<span className={isAnnual ? 'text-white' : 'text-gray-500'}>
|
|
90
|
+
Annual
|
|
91
|
+
<span className="ml-2 text-green-400 text-sm">Save 20%</span>
|
|
92
|
+
</span>
|
|
93
|
+
</div>
|
|
94
|
+
|
|
95
|
+
{/* Trust Badges */}
|
|
96
|
+
<div className="flex items-center justify-center gap-8 mb-12">
|
|
97
|
+
<div className="flex items-center gap-2">
|
|
98
|
+
<Shield className="w-5 h-5 text-green-400" />
|
|
99
|
+
<span className="text-sm text-gray-300">Bank-level encryption</span>
|
|
100
|
+
</div>
|
|
101
|
+
<div className="flex items-center gap-2">
|
|
102
|
+
<Zap className="w-5 h-5 text-yellow-400" />
|
|
103
|
+
<span className="text-sm text-gray-300">Instant setup</span>
|
|
104
|
+
</div>
|
|
105
|
+
<div className="flex items-center gap-2">
|
|
106
|
+
<TrendingUp className="w-5 h-5 text-blue-400" />
|
|
107
|
+
<span className="text-sm text-gray-300">Save 30%+ on API costs</span>
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
</motion.div>
|
|
111
|
+
|
|
112
|
+
{/* Pricing Cards */}
|
|
113
|
+
<div className="grid md:grid-cols-3 gap-8">
|
|
114
|
+
{PRICING_TIERS.map((tier, index) => {
|
|
115
|
+
const price = isAnnual && tier.price > 0
|
|
116
|
+
? Math.floor(tier.price * 0.8)
|
|
117
|
+
: tier.price;
|
|
118
|
+
|
|
119
|
+
const isPopular = tier.id === 'pro';
|
|
120
|
+
|
|
121
|
+
return (
|
|
122
|
+
<motion.div
|
|
123
|
+
key={tier.id}
|
|
124
|
+
initial={{ opacity: 0, y: 20 }}
|
|
125
|
+
animate={{ opacity: 1, y: 0 }}
|
|
126
|
+
transition={{ delay: index * 0.1 }}
|
|
127
|
+
className={`relative ${isPopular ? 'scale-105' : ''}`}
|
|
128
|
+
>
|
|
129
|
+
{isPopular && (
|
|
130
|
+
<div className="absolute -top-4 left-1/2 -translate-x-1/2 z-10">
|
|
131
|
+
<div className="bg-gradient-to-r from-purple-500 to-pink-500 text-white text-sm font-bold px-4 py-1 rounded-full">
|
|
132
|
+
MOST POPULAR
|
|
133
|
+
</div>
|
|
134
|
+
</div>
|
|
135
|
+
)}
|
|
136
|
+
|
|
137
|
+
<div className={`
|
|
138
|
+
h-full rounded-2xl p-8 backdrop-blur-sm
|
|
139
|
+
${isPopular
|
|
140
|
+
? 'bg-gradient-to-b from-purple-900/40 to-purple-900/20 border-2 border-purple-500'
|
|
141
|
+
: 'bg-white/5 border border-white/10'
|
|
142
|
+
}
|
|
143
|
+
`}>
|
|
144
|
+
<div className="mb-8">
|
|
145
|
+
<h3 className="text-2xl font-bold mb-2">{tier.name}</h3>
|
|
146
|
+
|
|
147
|
+
<div className="flex items-baseline gap-2 mb-4">
|
|
148
|
+
<span className="text-5xl font-bold">
|
|
149
|
+
${price}
|
|
150
|
+
</span>
|
|
151
|
+
{tier.price > 0 && (
|
|
152
|
+
<span className="text-gray-400">
|
|
153
|
+
/{isAnnual ? 'year' : 'month'}
|
|
154
|
+
</span>
|
|
155
|
+
)}
|
|
156
|
+
</div>
|
|
157
|
+
|
|
158
|
+
{tier.id === 'pro' && (
|
|
159
|
+
<div className="text-sm text-green-400 mb-2">
|
|
160
|
+
14-day free trial
|
|
161
|
+
</div>
|
|
162
|
+
)}
|
|
163
|
+
</div>
|
|
164
|
+
|
|
165
|
+
<ul className="space-y-4 mb-8">
|
|
166
|
+
{tier.features.map((feature, i) => (
|
|
167
|
+
<li key={i} className="flex items-start gap-3">
|
|
168
|
+
<Check className="w-5 h-5 text-green-400 flex-shrink-0 mt-0.5" />
|
|
169
|
+
<span className="text-sm text-gray-300">{feature}</span>
|
|
170
|
+
</li>
|
|
171
|
+
))}
|
|
172
|
+
</ul>
|
|
173
|
+
|
|
174
|
+
<Button
|
|
175
|
+
onClick={() => handleSubscribe(tier)}
|
|
176
|
+
disabled={loading !== null}
|
|
177
|
+
className={`
|
|
178
|
+
w-full py-6 text-lg font-semibold rounded-xl transition-all
|
|
179
|
+
${isPopular
|
|
180
|
+
? 'bg-gradient-to-r from-purple-500 to-pink-500 hover:from-purple-600 hover:to-pink-600'
|
|
181
|
+
: tier.id === 'free'
|
|
182
|
+
? 'bg-white/10 hover:bg-white/20'
|
|
183
|
+
: 'bg-white text-black hover:bg-gray-200'
|
|
184
|
+
}
|
|
185
|
+
`}
|
|
186
|
+
>
|
|
187
|
+
{loading === tier.id ? (
|
|
188
|
+
<span className="flex items-center gap-2">
|
|
189
|
+
<svg className="animate-spin h-5 w-5" viewBox="0 0 24 24">
|
|
190
|
+
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" fill="none" />
|
|
191
|
+
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" />
|
|
192
|
+
</svg>
|
|
193
|
+
Processing...
|
|
194
|
+
</span>
|
|
195
|
+
) : (
|
|
196
|
+
tier.id === 'free' ? 'Start Free' : 'Get Started'
|
|
197
|
+
)}
|
|
198
|
+
</Button>
|
|
199
|
+
</div>
|
|
200
|
+
</motion.div>
|
|
201
|
+
);
|
|
202
|
+
})}
|
|
203
|
+
</div>
|
|
204
|
+
|
|
205
|
+
{/* Social Proof */}
|
|
206
|
+
<motion.div
|
|
207
|
+
initial={{ opacity: 0 }}
|
|
208
|
+
animate={{ opacity: 1 }}
|
|
209
|
+
transition={{ delay: 0.5 }}
|
|
210
|
+
className="text-center mt-16"
|
|
211
|
+
>
|
|
212
|
+
<p className="text-gray-400 mb-4">Trusted by developers at</p>
|
|
213
|
+
<div className="flex items-center justify-center gap-8 opacity-50">
|
|
214
|
+
{['YC', 'ProductHunt', 'TechStars', 'AngelList', '500 Startups'].map((company) => (
|
|
215
|
+
<div key={company} className="text-xl font-bold">{company}</div>
|
|
216
|
+
))}
|
|
217
|
+
</div>
|
|
218
|
+
</motion.div>
|
|
219
|
+
|
|
220
|
+
{/* FAQ Section */}
|
|
221
|
+
<div className="mt-20">
|
|
222
|
+
<h2 className="text-3xl font-bold text-center mb-12">Frequently Asked Questions</h2>
|
|
223
|
+
|
|
224
|
+
<div className="grid md:grid-cols-2 gap-8 max-w-4xl mx-auto">
|
|
225
|
+
<div>
|
|
226
|
+
<h3 className="text-xl font-semibold mb-3">Can I change plans anytime?</h3>
|
|
227
|
+
<p className="text-gray-400">Yes! Upgrade or downgrade anytime. Changes take effect immediately, with prorated billing.</p>
|
|
228
|
+
</div>
|
|
229
|
+
|
|
230
|
+
<div>
|
|
231
|
+
<h3 className="text-xl font-semibold mb-3">What happens if I exceed my limit?</h3>
|
|
232
|
+
<p className="text-gray-400">We'll notify you at 80% usage. At 100%, tracking pauses until you upgrade or the next billing cycle.</p>
|
|
233
|
+
</div>
|
|
234
|
+
|
|
235
|
+
<div>
|
|
236
|
+
<h3 className="text-xl font-semibold mb-3">Do you offer refunds?</h3>
|
|
237
|
+
<p className="text-gray-400">Yes! 30-day money-back guarantee. If you're not saving money, we'll refund you.</p>
|
|
238
|
+
</div>
|
|
239
|
+
|
|
240
|
+
<div>
|
|
241
|
+
<h3 className="text-xl font-semibold mb-3">Is my data secure?</h3>
|
|
242
|
+
<p className="text-gray-400">Absolutely. AES-256 encryption, SOC2 compliant infrastructure, and we never store raw API keys.</p>
|
|
243
|
+
</div>
|
|
244
|
+
</div>
|
|
245
|
+
</div>
|
|
246
|
+
|
|
247
|
+
{/* ROI Calculator CTA */}
|
|
248
|
+
<motion.div
|
|
249
|
+
initial={{ opacity: 0, y: 20 }}
|
|
250
|
+
animate={{ opacity: 1, y: 0 }}
|
|
251
|
+
transition={{ delay: 0.7 }}
|
|
252
|
+
className="mt-20 text-center bg-gradient-to-r from-purple-900/40 to-pink-900/40 rounded-2xl p-12"
|
|
253
|
+
>
|
|
254
|
+
<h2 className="text-3xl font-bold mb-4">
|
|
255
|
+
Calculate Your Potential Savings
|
|
256
|
+
</h2>
|
|
257
|
+
<p className="text-xl text-gray-300 mb-8">
|
|
258
|
+
Most teams save 30-50% on their AI API costs within the first month
|
|
259
|
+
</p>
|
|
260
|
+
<Button
|
|
261
|
+
onClick={() => router.push('/#roi-calculator')}
|
|
262
|
+
size="lg"
|
|
263
|
+
className="bg-white text-black hover:bg-gray-200 px-8 py-6 text-lg rounded-xl"
|
|
264
|
+
>
|
|
265
|
+
Try ROI Calculator
|
|
266
|
+
</Button>
|
|
267
|
+
</motion.div>
|
|
268
|
+
</div>
|
|
269
|
+
</section>
|
|
270
|
+
</div>
|
|
271
|
+
);
|
|
272
|
+
}
|