Add interactive demo page and update landing page

- Add /demo route with interactive quadratic voting demonstration
- Users can try voting without creating an account
- Sample proposals show both ranking and pass/fail voting stages
- Update landing page with comprehensive quadratic voting explainer
- Add "What is Quadratic Voting?" section with problem/solution
- Add vote cost calculator showing quadratic progression
- Add two-stage voting process explanation
- Add feature highlights (credits, decay, sybil resistance, auto-promotion)
- Add call-to-action sections linking to demo and signup

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Jeff Emmett 2026-02-05 04:57:52 +00:00
parent c6b7f5d899
commit f8bf201c7a
2 changed files with 770 additions and 105 deletions

446
src/app/demo/page.tsx Normal file
View File

@ -0,0 +1,446 @@
"use client";
import { useState } from "react";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Progress } from "@/components/ui/progress";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import Link from "next/link";
import {
ChevronUp,
ChevronDown,
Check,
X,
Minus,
ArrowRight,
RotateCcw,
Coins,
TrendingUp,
Clock,
Users,
} from "lucide-react";
interface DemoProposal {
id: number;
title: string;
description: string;
score: number;
userVote: number;
stage: "ranking" | "voting";
yesVotes: number;
noVotes: number;
}
const initialProposals: DemoProposal[] = [
{
id: 1,
title: "Add dark mode to the dashboard",
description: "Implement a dark theme option for better nighttime usage",
score: 87,
userVote: 0,
stage: "ranking",
yesVotes: 0,
noVotes: 0,
},
{
id: 2,
title: "Weekly community calls",
description: "Host weekly video calls to discuss proposals and progress",
score: 42,
userVote: 0,
stage: "ranking",
yesVotes: 0,
noVotes: 0,
},
{
id: 3,
title: "Create a mobile app",
description: "Build native iOS and Android apps for on-the-go voting",
score: 103,
userVote: 0,
stage: "voting",
yesVotes: 12,
noVotes: 5,
},
];
export default function DemoPage() {
const [credits, setCredits] = useState(100);
const [proposals, setProposals] = useState<DemoProposal[]>(initialProposals);
const [voteWeight, setVoteWeight] = useState(1);
const [activeProposal, setActiveProposal] = useState<number | null>(null);
const voteCost = voteWeight * voteWeight;
const maxWeight = Math.floor(Math.sqrt(credits));
function castVote(proposalId: number, direction: "up" | "down") {
const weight = direction === "up" ? voteWeight : -voteWeight;
const cost = voteCost;
if (cost > credits) return;
setProposals((prev) =>
prev.map((p) => {
if (p.id === proposalId) {
// Return old vote credits if changing vote
const oldCost = p.userVote !== 0 ? p.userVote * p.userVote : 0;
const newScore = p.score - p.userVote + weight;
// Check if promoted
const promoted = newScore >= 100 && p.stage === "ranking";
return {
...p,
score: newScore,
userVote: weight,
stage: promoted ? "voting" : p.stage,
yesVotes: promoted ? 8 : p.yesVotes,
noVotes: promoted ? 3 : p.noVotes,
};
}
return p;
})
);
// Deduct credits (simplified - doesn't return old vote credits in demo)
setCredits((prev) => prev - cost);
setActiveProposal(null);
}
function castFinalVote(proposalId: number, vote: "yes" | "no" | "abstain") {
setProposals((prev) =>
prev.map((p) => {
if (p.id === proposalId) {
return {
...p,
yesVotes: vote === "yes" ? p.yesVotes + 1 : p.yesVotes,
noVotes: vote === "no" ? p.noVotes + 1 : p.noVotes,
};
}
return p;
})
);
}
function resetDemo() {
setCredits(100);
setProposals(initialProposals);
setVoteWeight(1);
setActiveProposal(null);
}
const rankingProposals = proposals.filter((p) => p.stage === "ranking");
const votingProposals = proposals.filter((p) => p.stage === "voting");
return (
<div className="max-w-4xl mx-auto space-y-8">
<div className="text-center space-y-4">
<Badge variant="secondary" className="text-sm">
Interactive Demo
</Badge>
<h1 className="text-4xl font-bold">Try Quadratic Voting</h1>
<p className="text-xl text-muted-foreground max-w-2xl mx-auto">
Experience how rVote works without creating an account. Vote on these
sample proposals and see the quadratic cost in action.
</p>
</div>
{/* Credits display */}
<Card className="border-primary/50 bg-primary/5">
<CardContent className="py-4">
<div className="flex items-center justify-between">
<div className="flex items-center gap-4">
<div className="flex items-center gap-2">
<Coins className="h-5 w-5 text-primary" />
<span className="font-semibold text-lg">{credits} credits</span>
</div>
<span className="text-sm text-muted-foreground">
Max vote weight: {maxWeight}
</span>
</div>
<Button variant="outline" size="sm" onClick={resetDemo}>
<RotateCcw className="h-4 w-4 mr-2" />
Reset Demo
</Button>
</div>
</CardContent>
</Card>
{/* Quadratic cost explainer */}
<Card>
<CardHeader>
<CardTitle className="text-lg flex items-center gap-2">
<TrendingUp className="h-5 w-5" />
Quadratic Voting Cost
</CardTitle>
<CardDescription>
The more votes you put on one proposal, the more each additional
vote costs
</CardDescription>
</CardHeader>
<CardContent>
<div className="grid grid-cols-5 gap-2 text-center text-sm">
{[1, 2, 3, 4, 5].map((w) => (
<div
key={w}
className={`p-3 rounded-lg border ${
w <= maxWeight
? "bg-primary/10 border-primary/30"
: "bg-muted border-muted"
}`}
>
<div className="font-bold text-lg">{w}</div>
<div className="text-muted-foreground">vote{w > 1 ? "s" : ""}</div>
<div className="font-mono text-xs mt-1">{w * w} credits</div>
</div>
))}
</div>
<p className="text-sm text-muted-foreground mt-4 text-center">
This prevents wealthy voters from dominating. Spreading votes across
proposals is more efficient than concentrating them.
</p>
</CardContent>
</Card>
{/* Ranking stage */}
<section className="space-y-4">
<div className="flex items-center gap-2">
<Badge>Stage 1</Badge>
<h2 className="text-xl font-semibold">Ranking</h2>
<span className="text-muted-foreground text-sm">
Proposals need +100 to advance
</span>
</div>
{rankingProposals.map((proposal) => (
<Card key={proposal.id}>
<div className="flex">
<div className="flex flex-col items-center justify-center px-4 border-r bg-muted/30 min-w-[80px]">
<Button
variant={proposal.userVote > 0 ? "default" : "ghost"}
size="sm"
className="h-8 w-8 p-0"
onClick={() => {
if (proposal.userVote > 0) {
// Remove vote (simplified)
setProposals((prev) =>
prev.map((p) =>
p.id === proposal.id
? { ...p, score: p.score - p.userVote, userVote: 0 }
: p
)
);
setCredits((c) => c + proposal.userVote * proposal.userVote);
} else {
setActiveProposal(proposal.id);
}
}}
disabled={credits < 1 && proposal.userVote === 0}
>
<ChevronUp className="h-5 w-5" />
</Button>
<Badge variant="outline" className="font-mono text-lg my-1">
{proposal.score}
</Badge>
<Button
variant={proposal.userVote < 0 ? "destructive" : "ghost"}
size="sm"
className="h-8 w-8 p-0"
onClick={() => {
if (proposal.userVote < 0) {
setProposals((prev) =>
prev.map((p) =>
p.id === proposal.id
? { ...p, score: p.score - p.userVote, userVote: 0 }
: p
)
);
setCredits((c) => c + proposal.userVote * proposal.userVote);
} else {
setActiveProposal(-proposal.id); // Negative for downvote
}
}}
disabled={credits < 1 && proposal.userVote === 0}
>
<ChevronDown className="h-5 w-5" />
</Button>
</div>
<div className="flex-1 p-4">
<h3 className="font-semibold">{proposal.title}</h3>
<p className="text-sm text-muted-foreground">
{proposal.description}
</p>
<div className="mt-3">
<div className="flex items-center justify-between text-xs mb-1">
<span>Progress to voting</span>
<span>{proposal.score}/100</span>
</div>
<Progress
value={Math.min((proposal.score / 100) * 100, 100)}
className="h-2"
/>
</div>
{proposal.userVote !== 0 && (
<p className="text-xs text-muted-foreground mt-2">
Your vote: {proposal.userVote > 0 ? "+" : ""}
{proposal.userVote} ({Math.abs(proposal.userVote * proposal.userVote)} credits)
</p>
)}
</div>
</div>
{/* Vote weight selector */}
{(activeProposal === proposal.id ||
activeProposal === -proposal.id) && (
<div className="border-t p-4 bg-muted/30">
<div className="flex items-center gap-4">
<Label>Vote weight:</Label>
<Input
type="number"
min={1}
max={maxWeight}
value={voteWeight}
onChange={(e) =>
setVoteWeight(
Math.max(1, Math.min(maxWeight, parseInt(e.target.value) || 1))
)
}
className="w-20"
/>
<span className="text-sm text-muted-foreground">
Cost: {voteCost} credits
</span>
<div className="flex-1" />
<Button
variant="outline"
size="sm"
onClick={() => setActiveProposal(null)}
>
Cancel
</Button>
<Button
size="sm"
variant={activeProposal > 0 ? "default" : "destructive"}
onClick={() =>
castVote(
Math.abs(activeProposal),
activeProposal > 0 ? "up" : "down"
)
}
disabled={voteCost > credits}
>
{activeProposal > 0 ? "Upvote" : "Downvote"} ({voteCost} credits)
</Button>
</div>
</div>
)}
</Card>
))}
</section>
{/* Voting stage */}
<section className="space-y-4">
<div className="flex items-center gap-2">
<Badge variant="outline">Stage 2</Badge>
<h2 className="text-xl font-semibold">Pass/Fail Voting</h2>
<span className="text-muted-foreground text-sm">
One member = one vote
</span>
</div>
{votingProposals.map((proposal) => {
const total = proposal.yesVotes + proposal.noVotes;
const yesPercent = total > 0 ? (proposal.yesVotes / total) * 100 : 50;
return (
<Card key={proposal.id}>
<CardHeader className="pb-2">
<div className="flex items-center justify-between">
<CardTitle className="text-lg">{proposal.title}</CardTitle>
<div className="flex items-center gap-1 text-sm text-yellow-600">
<Clock className="h-4 w-4" />
<span>6 days left</span>
</div>
</div>
<CardDescription>{proposal.description}</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
{/* Vote bar */}
<div className="space-y-2">
<div className="h-4 rounded-full overflow-hidden bg-red-500/20 flex">
<div
className="h-full bg-green-500 transition-all"
style={{ width: `${yesPercent}%` }}
/>
<div
className="h-full bg-red-500 transition-all"
style={{ width: `${100 - yesPercent}%` }}
/>
</div>
<div className="flex justify-between text-sm">
<span className="text-green-600">
{proposal.yesVotes} Yes ({Math.round(yesPercent)}%)
</span>
<span className="text-red-600">
{proposal.noVotes} No ({Math.round(100 - yesPercent)}%)
</span>
</div>
</div>
{/* Vote buttons */}
<div className="grid grid-cols-3 gap-2">
<Button
variant="outline"
className="flex-col h-auto py-3 hover:bg-green-500/10 hover:border-green-500"
onClick={() => castFinalVote(proposal.id, "yes")}
>
<Check className="h-5 w-5 text-green-500" />
<span className="text-xs mt-1">Yes</span>
</Button>
<Button
variant="outline"
className="flex-col h-auto py-3 hover:bg-red-500/10 hover:border-red-500"
onClick={() => castFinalVote(proposal.id, "no")}
>
<X className="h-5 w-5 text-red-500" />
<span className="text-xs mt-1">No</span>
</Button>
<Button
variant="outline"
className="flex-col h-auto py-3"
onClick={() => castFinalVote(proposal.id, "abstain")}
>
<Minus className="h-5 w-5" />
<span className="text-xs mt-1">Abstain</span>
</Button>
</div>
</CardContent>
</Card>
);
})}
</section>
{/* CTA */}
<Card className="border-primary bg-primary/5">
<CardContent className="py-8 text-center space-y-4">
<h2 className="text-2xl font-bold">Ready to try it for real?</h2>
<p className="text-muted-foreground">
Create an account to start ranking and voting on community proposals.
You&apos;ll get 50 credits to start and earn 10 more each day.
</p>
<div className="flex justify-center gap-4">
<Button asChild size="lg">
<Link href="/auth/signup">
Create Account <ArrowRight className="ml-2 h-4 w-4" />
</Link>
</Button>
<Button variant="outline" asChild size="lg">
<Link href="/">Learn More</Link>
</Button>
</div>
</CardContent>
</Card>
</div>
);
}

View File

@ -2,12 +2,24 @@ import { auth } from "@/lib/auth";
import { prisma } from "@/lib/prisma";
import { ProposalList } from "@/components/ProposalList";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { calculateAvailableCredits } from "@/lib/credits";
import { getEffectiveWeight } from "@/lib/voting";
import Link from "next/link";
import { ArrowRight, TrendingUp, Vote, Zap } from "lucide-react";
import {
ArrowRight,
TrendingUp,
Vote,
Zap,
Users,
Scale,
Clock,
Coins,
CheckCircle,
Shield,
Play,
} from "lucide-react";
export default async function HomePage() {
const session = await auth();
@ -16,7 +28,7 @@ export default async function HomePage() {
const proposals = await prisma.proposal.findMany({
where: { status: "RANKING" },
orderBy: { score: "desc" },
take: 10,
take: 5,
include: {
author: {
select: { id: true, name: true, email: true },
@ -54,156 +66,363 @@ export default async function HomePage() {
}
// Get counts for stats
const [rankingCount, votingCount, passedCount] = await Promise.all([
const [rankingCount, votingCount, passedCount, userCount] = await Promise.all([
prisma.proposal.count({ where: { status: "RANKING" } }),
prisma.proposal.count({ where: { status: "VOTING" } }),
prisma.proposal.count({ where: { status: "PASSED" } }),
prisma.user.count(),
]);
return (
<div className="space-y-8">
<div className="space-y-16">
{/* Hero section */}
<section className="text-center py-12 space-y-4">
<h1 className="text-4xl font-bold tracking-tight">
Community-Driven Governance
<section className="text-center py-16 space-y-6">
<Badge variant="secondary" className="text-sm px-4 py-1">
Quadratic Voting for Communities
</Badge>
<h1 className="text-5xl md:text-6xl font-bold tracking-tight max-w-4xl mx-auto leading-tight">
Democratic Governance,{" "}
<span className="text-primary">Reimagined</span>
</h1>
<p className="text-xl text-muted-foreground max-w-2xl mx-auto">
Rank proposals using quadratic voting. Top proposals advance to pass/fail voting.
Your voice matters more when you concentrate your votes.
rVote uses quadratic voting to give every voice weight while preventing
any single actor from dominating. Proposals are ranked by the community,
and the best ideas rise to the top.
</p>
{!session?.user && (
<div className="flex justify-center gap-4 pt-4">
<Button asChild size="lg">
<Link href="/auth/signup">Get Started</Link>
<div className="flex flex-col sm:flex-row justify-center gap-4 pt-4">
<Button asChild size="lg" className="text-lg px-8">
<Link href="/demo">
<Play className="mr-2 h-5 w-5" />
Try the Demo
</Link>
</Button>
{!session?.user ? (
<Button asChild variant="outline" size="lg" className="text-lg px-8">
<Link href="/auth/signup">Create Account</Link>
</Button>
<Button asChild variant="outline" size="lg">
) : (
<Button asChild variant="outline" size="lg" className="text-lg px-8">
<Link href="/proposals">Browse Proposals</Link>
</Button>
</div>
)}
{session?.user && (
<div className="flex justify-center gap-4 pt-4">
<Button asChild size="lg">
<Link href="/proposals/new">Create Proposal</Link>
</Button>
<Button asChild variant="outline" size="lg">
<Link href="/voting">View Active Votes</Link>
</Button>
</div>
)}
)}
</div>
</section>
{/* Stats */}
<section className="grid grid-cols-1 md:grid-cols-3 gap-4">
<Card>
<CardHeader className="flex flex-row items-center justify-between pb-2">
<CardTitle className="text-sm font-medium">Ranking</CardTitle>
<TrendingUp className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">{rankingCount}</div>
<p className="text-xs text-muted-foreground">proposals being ranked</p>
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between pb-2">
<CardTitle className="text-sm font-medium">Voting</CardTitle>
<Vote className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">{votingCount}</div>
<p className="text-xs text-muted-foreground">proposals in pass/fail voting</p>
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between pb-2">
<CardTitle className="text-sm font-medium">Passed</CardTitle>
<Zap className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">{passedCount}</div>
<p className="text-xs text-muted-foreground">proposals approved</p>
</CardContent>
</Card>
</section>
{/* How it works */}
{/* What is Quadratic Voting */}
<section className="py-8">
<h2 className="text-2xl font-bold mb-6">How It Works</h2>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<Card>
<div className="text-center mb-12">
<h2 className="text-3xl font-bold mb-4">What is Quadratic Voting?</h2>
<p className="text-lg text-muted-foreground max-w-2xl mx-auto">
A voting system where the cost of each additional vote increases
quadratically, making it expensive to dominate but cheap to participate.
</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 max-w-4xl mx-auto">
<Card className="border-2">
<CardHeader>
<Badge className="w-fit mb-2">Stage 1</Badge>
<CardTitle>Ranking</CardTitle>
<CardTitle className="flex items-center gap-2">
<Scale className="h-5 w-5 text-primary" />
The Problem with Traditional Voting
</CardTitle>
</CardHeader>
<CardContent className="text-muted-foreground">
<CardContent className="space-y-3 text-muted-foreground">
<p>
Proposals start in the ranking stage. Use your credits to upvote or
downvote. <strong>Quadratic voting</strong>: 1 vote costs 1 credit,
2 votes cost 4 credits, 3 votes cost 9 credits.
In traditional systems, those with more resources (time, money,
influence) can easily dominate outcomes.
</p>
<ul className="list-disc list-inside space-y-1">
<li>One vote per person ignores intensity of preference</li>
<li>Unlimited voting lets whales control results</li>
<li>Small voices get drowned out</li>
</ul>
</CardContent>
</Card>
<Card className="border-2 border-primary/50 bg-primary/5">
<CardHeader>
<CardTitle className="flex items-center gap-2">
<CheckCircle className="h-5 w-5 text-primary" />
The Quadratic Solution
</CardTitle>
</CardHeader>
<CardContent className="space-y-3 text-muted-foreground">
<p>
Quadratic voting balances participation and conviction by making
additional votes progressively more expensive.
</p>
<ul className="list-disc list-inside space-y-1">
<li>1 vote = 1 credit, 2 votes = 4 credits, 3 = 9</li>
<li>Express strong opinions, but at a cost</li>
<li>More voices, more balanced outcomes</li>
</ul>
</CardContent>
</Card>
</div>
{/* Visual cost table */}
<div className="mt-12 max-w-2xl mx-auto">
<h3 className="text-lg font-semibold text-center mb-4">
Vote Cost Calculator
</h3>
<div className="grid grid-cols-5 gap-2 text-center">
{[1, 2, 3, 4, 5].map((votes) => (
<div
key={votes}
className="p-4 rounded-lg border-2 bg-card hover:border-primary/50 transition-colors"
>
<div className="text-2xl font-bold text-primary">{votes}</div>
<div className="text-sm text-muted-foreground">
vote{votes > 1 ? "s" : ""}
</div>
<div className="text-lg font-mono mt-2">{votes * votes}</div>
<div className="text-xs text-muted-foreground">credits</div>
</div>
))}
</div>
<p className="text-center text-sm text-muted-foreground mt-4">
It&apos;s more efficient to spread votes across proposals you support
than to concentrate them on one.
</p>
</div>
</section>
{/* How it works - 2 stages */}
<section className="py-8 bg-muted/30 -mx-4 px-4 rounded-xl">
<div className="text-center mb-12">
<h2 className="text-3xl font-bold mb-4">Two-Stage Voting Process</h2>
<p className="text-lg text-muted-foreground max-w-2xl mx-auto">
Proposals go through ranking before reaching a final vote, ensuring
only well-supported ideas get full consideration.
</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 max-w-5xl mx-auto">
<Card>
<CardHeader>
<Badge className="w-fit mb-2" variant="secondary">
Threshold
</Badge>
<CardTitle>Score +100</CardTitle>
<div className="flex items-center gap-3">
<div className="h-10 w-10 rounded-full bg-primary/10 flex items-center justify-center">
<span className="font-bold text-primary">1</span>
</div>
<div>
<Badge className="mb-1">Stage 1</Badge>
<CardTitle>Ranking</CardTitle>
</div>
</div>
</CardHeader>
<CardContent className="text-muted-foreground">
<ul className="space-y-2">
<li className="flex items-start gap-2">
<TrendingUp className="h-4 w-4 mt-1 shrink-0" />
<span>Proposals start here</span>
</li>
<li className="flex items-start gap-2">
<Coins className="h-4 w-4 mt-1 shrink-0" />
<span>Upvote/downvote with quadratic cost</span>
</li>
<li className="flex items-start gap-2">
<Clock className="h-4 w-4 mt-1 shrink-0" />
<span>Votes decay over 30-60 days</span>
</li>
</ul>
</CardContent>
</Card>
<Card className="border-primary/50">
<CardHeader>
<div className="flex items-center gap-3">
<div className="h-10 w-10 rounded-full bg-primary flex items-center justify-center">
<ArrowRight className="h-5 w-5 text-primary-foreground" />
</div>
<div>
<Badge variant="secondary" className="mb-1">
Threshold
</Badge>
<CardTitle>Score +100</CardTitle>
</div>
</div>
</CardHeader>
<CardContent className="text-muted-foreground">
<p>
When a proposal reaches a score of <strong>+100</strong>, it
automatically advances to the pass/fail voting stage. Old votes
decay over time, keeping rankings fresh.
automatically advances to the final voting stage.
</p>
<p className="mt-2 text-sm">
This ensures only proposals with genuine community support move
forward.
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<Badge className="w-fit mb-2" variant="outline">
Stage 2
</Badge>
<CardTitle>Pass/Fail</CardTitle>
<div className="flex items-center gap-3">
<div className="h-10 w-10 rounded-full bg-primary/10 flex items-center justify-center">
<span className="font-bold text-primary">2</span>
</div>
<div>
<Badge variant="outline" className="mb-1">
Stage 2
</Badge>
<CardTitle>Pass/Fail</CardTitle>
</div>
</div>
</CardHeader>
<CardContent className="text-muted-foreground">
<p>
In the final stage, members vote <strong>Yes, No, or Abstain</strong>.
Voting is open for 7 days. Simple majority wins. One member = one vote.
<ul className="space-y-2">
<li className="flex items-start gap-2">
<Vote className="h-4 w-4 mt-1 shrink-0" />
<span>Yes / No / Abstain voting</span>
</li>
<li className="flex items-start gap-2">
<Users className="h-4 w-4 mt-1 shrink-0" />
<span>One member = one vote</span>
</li>
<li className="flex items-start gap-2">
<Clock className="h-4 w-4 mt-1 shrink-0" />
<span>7-day voting period</span>
</li>
</ul>
</CardContent>
</Card>
</div>
</section>
{/* Features */}
<section className="py-8">
<div className="text-center mb-12">
<h2 className="text-3xl font-bold mb-4">Built for Fair Governance</h2>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
<Card>
<CardContent className="pt-6 text-center">
<div className="h-12 w-12 rounded-full bg-green-500/10 flex items-center justify-center mx-auto mb-3">
<Coins className="h-6 w-6 text-green-500" />
</div>
<h3 className="font-semibold mb-1">Earn Credits Daily</h3>
<p className="text-sm text-muted-foreground">
Get 10 credits every day. Start with 50. Max 500.
</p>
</CardContent>
</Card>
<Card>
<CardContent className="pt-6 text-center">
<div className="h-12 w-12 rounded-full bg-blue-500/10 flex items-center justify-center mx-auto mb-3">
<Clock className="h-6 w-6 text-blue-500" />
</div>
<h3 className="font-semibold mb-1">Vote Decay</h3>
<p className="text-sm text-muted-foreground">
Old votes fade away, keeping rankings fresh and dynamic.
</p>
</CardContent>
</Card>
<Card>
<CardContent className="pt-6 text-center">
<div className="h-12 w-12 rounded-full bg-purple-500/10 flex items-center justify-center mx-auto mb-3">
<Shield className="h-6 w-6 text-purple-500" />
</div>
<h3 className="font-semibold mb-1">Sybil Resistant</h3>
<p className="text-sm text-muted-foreground">
Quadratic costs make fake account attacks expensive.
</p>
</CardContent>
</Card>
<Card>
<CardContent className="pt-6 text-center">
<div className="h-12 w-12 rounded-full bg-orange-500/10 flex items-center justify-center mx-auto mb-3">
<Zap className="h-6 w-6 text-orange-500" />
</div>
<h3 className="font-semibold mb-1">Auto Promotion</h3>
<p className="text-sm text-muted-foreground">
Top proposals automatically advance to voting.
</p>
</CardContent>
</Card>
</div>
</section>
{/* Top proposals */}
<section className="py-8">
<div className="flex items-center justify-between mb-6">
<h2 className="text-2xl font-bold">Top Proposals</h2>
<Button asChild variant="ghost">
<Link href="/proposals">
View All <ArrowRight className="ml-2 h-4 w-4" />
</Link>
</Button>
</div>
{proposals.length > 0 ? (
{/* Stats */}
{(rankingCount > 0 || userCount > 1) && (
<section className="py-8">
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
<Card>
<CardContent className="pt-6 text-center">
<div className="text-3xl font-bold">{userCount}</div>
<p className="text-sm text-muted-foreground">Members</p>
</CardContent>
</Card>
<Card>
<CardContent className="pt-6 text-center">
<div className="text-3xl font-bold">{rankingCount}</div>
<p className="text-sm text-muted-foreground">Being Ranked</p>
</CardContent>
</Card>
<Card>
<CardContent className="pt-6 text-center">
<div className="text-3xl font-bold">{votingCount}</div>
<p className="text-sm text-muted-foreground">In Voting</p>
</CardContent>
</Card>
<Card>
<CardContent className="pt-6 text-center">
<div className="text-3xl font-bold">{passedCount}</div>
<p className="text-sm text-muted-foreground">Passed</p>
</CardContent>
</Card>
</div>
</section>
)}
{/* Active proposals */}
{proposals.length > 0 && (
<section className="py-8">
<div className="flex items-center justify-between mb-6">
<h2 className="text-2xl font-bold">Active Proposals</h2>
<Button asChild variant="ghost">
<Link href="/proposals">
View All <ArrowRight className="ml-2 h-4 w-4" />
</Link>
</Button>
</div>
<ProposalList
proposals={proposals}
userVotes={userVotes}
availableCredits={availableCredits}
/>
) : (
<Card>
<CardContent className="py-12 text-center text-muted-foreground">
<p>No proposals yet. Be the first to create one!</p>
{session?.user && (
<Button asChild className="mt-4">
<Link href="/proposals/new">Create Proposal</Link>
</section>
)}
{/* CTA */}
<section className="py-12">
<Card className="border-2 border-primary/50 bg-gradient-to-br from-primary/5 to-primary/10">
<CardContent className="py-12 text-center space-y-6">
<h2 className="text-3xl font-bold">Ready to give it a try?</h2>
<p className="text-lg text-muted-foreground max-w-xl mx-auto">
Experience quadratic voting firsthand. Try the interactive demo or
create an account to start participating in community governance.
</p>
<div className="flex flex-col sm:flex-row justify-center gap-4">
<Button asChild size="lg" className="text-lg px-8">
<Link href="/demo">
<Play className="mr-2 h-5 w-5" />
Interactive Demo
</Link>
</Button>
{!session?.user && (
<Button asChild variant="outline" size="lg" className="text-lg px-8">
<Link href="/auth/signup">
Create Free Account
<ArrowRight className="ml-2 h-5 w-5" />
</Link>
</Button>
)}
</CardContent>
</Card>
)}
</div>
</CardContent>
</Card>
</section>
</div>
);