Redesign demo page with Reddit-style voting UX and improve ELI5 section

Demo page changes:
- Reddit-style vote arrows on left (orange upvote, blue downvote)
- More realistic governance proposals (treasury, moderation, research, security, town halls)
- Prominent color-coded vote states and confirmation flow
- Cleaner card layout with progress bars

Homepage ELI5 section:
- Three distinct colored cards (orange/blue/purple) for each mechanism
- Icon badges for visual hierarchy
- Clearer explanations with emphasized takeaways

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Jeff Emmett 2026-02-05 19:11:11 +00:00
parent 9fdf122145
commit eebd6a4349
2 changed files with 342 additions and 263 deletions

View File

@ -34,9 +34,9 @@ interface DemoProposal {
const initialProposals: DemoProposal[] = [ const initialProposals: DemoProposal[] = [
{ {
id: 1, id: 1,
title: "Add dark mode to the dashboard", title: "Allocate 15% of treasury to ecosystem grants program",
description: "Implement a dark theme option for better nighttime usage", description: "Fund community developers building tools and integrations for the ecosystem over the next 6 months",
score: 45, score: 72,
userVote: 0, userVote: 0,
pendingVote: 0, pendingVote: 0,
stage: "ranking", stage: "ranking",
@ -45,9 +45,9 @@ const initialProposals: DemoProposal[] = [
}, },
{ {
id: 2, id: 2,
title: "Weekly community calls", title: "Establish a community moderation council",
description: "Host weekly video calls to discuss proposals and progress", description: "Elect 7 members to handle disputes, enforce guidelines, and maintain community standards",
score: 43, score: 58,
userVote: 0, userVote: 0,
pendingVote: 0, pendingVote: 0,
stage: "ranking", stage: "ranking",
@ -56,9 +56,31 @@ const initialProposals: DemoProposal[] = [
}, },
{ {
id: 3, id: 3,
title: "Create a mobile app", title: "Partner with University research lab for governance study",
description: "Build native iOS and Android apps for on-the-go voting", description: "Collaborate with academic researchers to analyze and improve our decision-making processes",
score: 44, score: 41,
userVote: 0,
pendingVote: 0,
stage: "ranking",
yesVotes: 0,
noVotes: 0,
},
{
id: 4,
title: "Create bounty program for security audits",
description: "Reward external security researchers who identify vulnerabilities in our smart contracts",
score: 35,
userVote: 0,
pendingVote: 0,
stage: "ranking",
yesVotes: 0,
noVotes: 0,
},
{
id: 5,
title: "Host quarterly virtual town halls",
description: "Regular video conferences for community updates, Q&A sessions, and open discussion",
score: 23,
userVote: 0, userVote: 0,
pendingVote: 0, pendingVote: 0,
stage: "ranking", stage: "ranking",
@ -173,7 +195,7 @@ export default function DemoPage() {
const rankingProposals = proposals const rankingProposals = proposals
.filter((p) => p.stage === "ranking") .filter((p) => p.stage === "ranking")
.sort((a, b) => b.score - a.score); // Sort by score descending .sort((a, b) => b.score - a.score);
const votingProposals = proposals.filter((p) => p.stage === "voting"); const votingProposals = proposals.filter((p) => p.stage === "voting");
return ( return (
@ -184,25 +206,26 @@ export default function DemoPage() {
</Badge> </Badge>
<h1 className="text-4xl font-bold">Try Quadratic Proposal Ranking</h1> <h1 className="text-4xl font-bold">Try Quadratic Proposal Ranking</h1>
<p className="text-xl text-muted-foreground max-w-2xl mx-auto"> <p className="text-xl text-muted-foreground max-w-2xl mx-auto">
Experience how rVote works without creating an account. Click the arrows Experience how rVote works without creating an account. Click the vote
to add votes and see the quadratic cost increase in real-time. arrows to rank proposalswatch how quadratic costs scale in real-time.
</p> </p>
</div> </div>
{/* Credits display */} {/* Credits display */}
<Card className="border-primary/50 bg-primary/5"> <Card className="border-2 border-orange-500/30 bg-gradient-to-r from-orange-500/10 to-amber-500/10">
<CardContent className="py-4"> <CardContent className="py-4">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Coins className="h-5 w-5 text-primary" /> <Coins className="h-6 w-6 text-orange-500" />
<span className="font-semibold text-lg">{credits} credits</span> <span className="font-bold text-2xl text-orange-600">{credits}</span>
<span className="text-muted-foreground">credits</span>
</div> </div>
<span className="text-sm text-muted-foreground"> <Badge variant="outline" className="border-orange-500/30 text-orange-600">
Max vote weight: {maxWeight} Max vote: ±{maxWeight}
</span> </Badge>
</div> </div>
<Button variant="outline" size="sm" onClick={resetDemo}> <Button variant="outline" size="sm" onClick={resetDemo} className="border-orange-500/30 hover:bg-orange-500/10">
<RotateCcw className="h-4 w-4 mr-2" /> <RotateCcw className="h-4 w-4 mr-2" />
Reset Demo Reset Demo
</Button> </Button>
@ -211,14 +234,14 @@ export default function DemoPage() {
</Card> </Card>
{/* Quadratic cost explainer */} {/* Quadratic cost explainer */}
<Card> <Card className="border-muted">
<CardHeader> <CardHeader className="pb-2">
<CardTitle className="text-lg flex items-center gap-2"> <CardTitle className="text-lg flex items-center gap-2">
<TrendingUp className="h-5 w-5" /> <TrendingUp className="h-5 w-5 text-orange-500" />
Quadratic Ranking Cost Quadratic Voting Cost
</CardTitle> </CardTitle>
<CardDescription> <CardDescription>
Click the arrows to add votes. Each additional vote costs more! Each additional vote costs exponentially more credits
</CardDescription> </CardDescription>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
@ -226,174 +249,203 @@ export default function DemoPage() {
{[1, 2, 3, 4, 5].map((w) => ( {[1, 2, 3, 4, 5].map((w) => (
<div <div
key={w} key={w}
className={`p-3 rounded-lg border ${ className={`p-3 rounded-lg border-2 transition-all ${
w <= maxWeight w <= maxWeight
? "bg-primary/10 border-primary/30" ? "bg-orange-500/10 border-orange-500/40 text-orange-700"
: "bg-muted border-muted" : "bg-muted/50 border-muted text-muted-foreground"
}`} }`}
> >
<div className="font-bold text-lg">{w}</div> <div className="font-bold text-xl">{w > 0 ? "+" : ""}{w}</div>
<div className="text-muted-foreground">vote{w > 1 ? "s" : ""}</div> <div className="text-xs opacity-70">vote{w > 1 ? "s" : ""}</div>
<div className="font-mono text-xs mt-1">{w * w} credits</div> <div className="font-mono text-sm mt-1 font-semibold">{w * w}¢</div>
</div> </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> </CardContent>
</Card> </Card>
{/* Ranking stage */} {/* Ranking stage */}
<section className="space-y-4"> <section className="space-y-3">
<div className="flex items-center gap-2"> <div className="flex items-center gap-3">
<Badge>Stage 1</Badge> <Badge className="bg-orange-500 hover:bg-orange-600">Stage 1</Badge>
<h2 className="text-xl font-semibold">Ranking</h2> <h2 className="text-xl font-semibold">Quadratic Ranking</h2>
<span className="text-muted-foreground text-sm"> <span className="text-muted-foreground text-sm">
Proposals need +100 to advance Score +100 to advance
</span> </span>
</div> </div>
{rankingProposals.map((proposal, index) => { <div className="space-y-2">
{rankingProposals.map((proposal) => {
const hasPending = proposal.pendingVote !== 0; const hasPending = proposal.pendingVote !== 0;
const hasVoted = proposal.userVote !== 0; const hasVoted = proposal.userVote !== 0;
const pendingCost = proposal.pendingVote * proposal.pendingVote; const pendingCost = proposal.pendingVote * proposal.pendingVote;
const previewScore = proposal.score + proposal.pendingVote; const previewScore = proposal.score + proposal.pendingVote;
const rank = index + 1; const progressPercent = Math.min((proposal.score / 100) * 100, 100);
const previewPercent = Math.min((previewScore / 100) * 100, 100);
return ( return (
<Card key={proposal.id} className="transition-all duration-300"> <Card
key={proposal.id}
className={`transition-all duration-200 overflow-hidden ${
hasPending
? proposal.pendingVote > 0
? "ring-2 ring-orange-500/50 bg-orange-500/5"
: "ring-2 ring-blue-500/50 bg-blue-500/5"
: hasVoted
? proposal.userVote > 0
? "bg-orange-500/5"
: "bg-blue-500/5"
: ""
}`}
>
<div className="flex"> <div className="flex">
{/* Rank indicator */} {/* Reddit-style vote column */}
<div className="flex items-center justify-center px-3 bg-muted/50 border-r font-bold text-2xl text-muted-foreground min-w-[50px]"> <div className="flex flex-col items-center justify-center py-4 px-3 bg-muted/40 min-w-[70px] gap-1">
#{rank} {/* Upvote button */}
</div> <button
<div className="flex flex-col items-center justify-center px-4 border-r bg-muted/30 min-w-[100px]">
{/* Up arrow */}
<Button
variant={proposal.userVote > 0 ? "default" : proposal.pendingVote > 0 ? "outline" : "ghost"}
size="sm"
className={`h-8 w-8 p-0 ${proposal.pendingVote > 0 ? "border-primary bg-primary/10" : ""}`}
onClick={() => { onClick={() => {
if (proposal.userVote > 0) { if (hasVoted && proposal.userVote > 0) {
removeVote(proposal.id); removeVote(proposal.id);
} else if (!hasPending || proposal.pendingVote > 0) { } else if (!hasVoted || proposal.pendingVote >= 0) {
incrementVote(proposal.id); incrementVote(proposal.id);
} }
}} }}
disabled={hasVoted && proposal.userVote < 0} disabled={hasVoted && proposal.userVote < 0}
className={`p-1 rounded transition-all hover:scale-110 ${
(hasVoted && proposal.userVote > 0) || proposal.pendingVote > 0
? "text-orange-500"
: "text-muted-foreground hover:text-orange-500"
} ${hasVoted && proposal.userVote < 0 ? "opacity-30 cursor-not-allowed" : "cursor-pointer"}`}
> >
<ChevronUp className="h-5 w-5" /> <ChevronUp className="h-8 w-8" strokeWidth={3} />
</Button> </button>
{/* Score display */} {/* Score display */}
<Badge <div className={`font-bold text-xl tabular-nums min-w-[3ch] text-center ${
variant={hasPending ? "default" : "outline"}
className={`font-mono text-lg my-1 min-w-[4rem] justify-center transition-all ${
hasPending hasPending
? proposal.pendingVote > 0 ? proposal.pendingVote > 0
? "bg-primary text-primary-foreground" ? "text-orange-500"
: "bg-destructive text-destructive-foreground" : "text-blue-500"
: "" : hasVoted
}`} ? proposal.userVote > 0
> ? "text-orange-500"
{hasPending ? ( : "text-blue-500"
<span className="flex items-center gap-1 text-sm"> : "text-foreground"
<span className="opacity-70">{proposal.score}</span> }`}>
<span></span> {hasPending ? previewScore : proposal.score}
<span>{previewScore}</span> </div>
</span>
) : (
proposal.score
)}
</Badge>
{/* Down arrow */} {/* Downvote button */}
<Button <button
variant={proposal.userVote < 0 ? "destructive" : proposal.pendingVote < 0 ? "outline" : "ghost"}
size="sm"
className={`h-8 w-8 p-0 ${proposal.pendingVote < 0 ? "border-destructive bg-destructive/10" : ""}`}
onClick={() => { onClick={() => {
if (proposal.userVote < 0) { if (hasVoted && proposal.userVote < 0) {
removeVote(proposal.id); removeVote(proposal.id);
} else if (!hasPending || proposal.pendingVote < 0) { } else if (!hasVoted || proposal.pendingVote <= 0) {
decrementVote(proposal.id); decrementVote(proposal.id);
} }
}} }}
disabled={hasVoted && proposal.userVote > 0} disabled={hasVoted && proposal.userVote > 0}
className={`p-1 rounded transition-all hover:scale-110 ${
(hasVoted && proposal.userVote < 0) || proposal.pendingVote < 0
? "text-blue-500"
: "text-muted-foreground hover:text-blue-500"
} ${hasVoted && proposal.userVote > 0 ? "opacity-30 cursor-not-allowed" : "cursor-pointer"}`}
> >
<ChevronDown className="h-5 w-5" /> <ChevronDown className="h-8 w-8" strokeWidth={3} />
</Button> </button>
{/* Pending vote info */}
{hasPending && (
<div className="flex flex-col items-center gap-0.5 mt-2">
<span className="text-xs font-medium">
{proposal.pendingVote > 0 ? "+" : ""}{proposal.pendingVote} vote{Math.abs(proposal.pendingVote) !== 1 ? "s" : ""}
</span>
<span className="text-xs text-muted-foreground">
{pendingCost} credit{pendingCost !== 1 ? "s" : ""}
</span>
<div className="flex gap-1 mt-1">
<Button
variant="ghost"
size="sm"
className="h-6 w-6 p-0"
onClick={() => cancelPending(proposal.id)}
title="Cancel"
>
<X className="h-3 w-3" />
</Button>
<Button
variant={proposal.pendingVote > 0 ? "default" : "destructive"}
size="sm"
className="h-6 px-2 text-xs"
onClick={() => castVote(proposal.id)}
title="Confirm vote"
>
<Check className="h-3 w-3 mr-1" />
Cast
</Button>
</div>
</div>
)}
{/* Existing vote display */}
{hasVoted && !hasPending && (
<span className="text-xs text-muted-foreground mt-2">
Your vote: {proposal.userVote > 0 ? "+" : ""}{proposal.userVote}
</span>
)}
</div> </div>
<div className="flex-1 p-4"> {/* Proposal content */}
<h3 className="font-semibold">{proposal.title}</h3> <div className="flex-1 p-4 min-w-0">
<p className="text-sm text-muted-foreground"> <h3 className="font-semibold text-base leading-tight">{proposal.title}</h3>
<p className="text-sm text-muted-foreground mt-1 line-clamp-2">
{proposal.description} {proposal.description}
</p> </p>
{/* Progress bar */}
<div className="mt-3"> <div className="mt-3">
<div className="flex items-center justify-between text-xs mb-1"> <div className="flex items-center justify-between text-xs text-muted-foreground mb-1">
<span>Progress to voting</span> <span>Progress to voting stage</span>
<span>{hasPending ? previewScore : proposal.score}/100</span> <span className={hasPending ? (proposal.pendingVote > 0 ? "text-orange-500" : "text-blue-500") : ""}>
{hasPending ? previewScore : proposal.score}/100
</span>
</div> </div>
<Progress <div className="h-2 rounded-full bg-muted overflow-hidden">
value={Math.min(((hasPending ? previewScore : proposal.score) / 100) * 100, 100)} <div
className="h-2" className={`h-full rounded-full transition-all duration-300 ${
hasPending
? proposal.pendingVote > 0
? "bg-orange-500"
: "bg-blue-500"
: "bg-primary"
}`}
style={{ width: `${hasPending ? previewPercent : progressPercent}%` }}
/> />
</div> </div>
</div> </div>
{/* Vote status / pending confirmation */}
{(hasPending || hasVoted) && (
<div className="mt-3 flex items-center gap-3">
{hasPending ? (
<>
<Badge
variant="outline"
className={proposal.pendingVote > 0
? "border-orange-500/50 text-orange-600 bg-orange-500/10"
: "border-blue-500/50 text-blue-600 bg-blue-500/10"
}
>
{proposal.pendingVote > 0 ? "+" : ""}{proposal.pendingVote} vote = {pendingCost} credits
</Badge>
<Button
size="sm"
variant="ghost"
className="h-7 px-2 text-muted-foreground hover:text-foreground"
onClick={() => cancelPending(proposal.id)}
>
<X className="h-4 w-4 mr-1" />
Cancel
</Button>
<Button
size="sm"
className={`h-7 ${
proposal.pendingVote > 0
? "bg-orange-500 hover:bg-orange-600"
: "bg-blue-500 hover:bg-blue-600"
}`}
onClick={() => castVote(proposal.id)}
>
<Check className="h-4 w-4 mr-1" />
Confirm
</Button>
</>
) : hasVoted && (
<Badge
variant="secondary"
className={proposal.userVote > 0
? "bg-orange-500/20 text-orange-600 border-orange-500/30"
: "bg-blue-500/20 text-blue-600 border-blue-500/30"
}
>
You voted: {proposal.userVote > 0 ? "+" : ""}{proposal.userVote}
</Badge>
)}
</div>
)}
</div>
</div> </div>
</Card> </Card>
); );
})} })}
</div>
</section> </section>
{/* Voting stage - only show if proposals have been promoted */} {/* Voting stage */}
{votingProposals.length > 0 && ( {votingProposals.length > 0 && (
<section className="space-y-4"> <section className="space-y-3">
<div className="flex items-center gap-2"> <div className="flex items-center gap-3">
<Badge variant="outline">Stage 2</Badge> <Badge variant="outline" className="border-green-500/50 text-green-600">Stage 2</Badge>
<h2 className="text-xl font-semibold">Pass/Fail Voting</h2> <h2 className="text-xl font-semibold">Pass/Fail Voting</h2>
<span className="text-muted-foreground text-sm"> <span className="text-muted-foreground text-sm">
One member = one vote One member = one vote
@ -404,11 +456,11 @@ export default function DemoPage() {
const total = proposal.yesVotes + proposal.noVotes; const total = proposal.yesVotes + proposal.noVotes;
const yesPercent = total > 0 ? (proposal.yesVotes / total) * 100 : 50; const yesPercent = total > 0 ? (proposal.yesVotes / total) * 100 : 50;
return ( return (
<Card key={proposal.id}> <Card key={proposal.id} className="border-green-500/30 bg-green-500/5">
<CardHeader className="pb-2"> <CardHeader className="pb-2">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<CardTitle className="text-lg">{proposal.title}</CardTitle> <CardTitle className="text-lg">{proposal.title}</CardTitle>
<div className="flex items-center gap-1 text-sm text-yellow-600"> <div className="flex items-center gap-1 text-sm text-amber-600">
<Clock className="h-4 w-4" /> <Clock className="h-4 w-4" />
<span>6 days left</span> <span>6 days left</span>
</div> </div>
@ -418,7 +470,7 @@ export default function DemoPage() {
<CardContent className="space-y-4"> <CardContent className="space-y-4">
{/* Vote bar */} {/* Vote bar */}
<div className="space-y-2"> <div className="space-y-2">
<div className="h-4 rounded-full overflow-hidden bg-red-500/20 flex"> <div className="h-4 rounded-full overflow-hidden bg-muted flex">
<div <div
className="h-full bg-green-500 transition-all" className="h-full bg-green-500 transition-all"
style={{ width: `${yesPercent}%` }} style={{ width: `${yesPercent}%` }}
@ -428,7 +480,7 @@ export default function DemoPage() {
style={{ width: `${100 - yesPercent}%` }} style={{ width: `${100 - yesPercent}%` }}
/> />
</div> </div>
<div className="flex justify-between text-sm"> <div className="flex justify-between text-sm font-medium">
<span className="text-green-600"> <span className="text-green-600">
{proposal.yesVotes} Yes ({Math.round(yesPercent)}%) {proposal.yesVotes} Yes ({Math.round(yesPercent)}%)
</span> </span>
@ -442,7 +494,7 @@ export default function DemoPage() {
<div className="grid grid-cols-3 gap-2"> <div className="grid grid-cols-3 gap-2">
<Button <Button
variant="outline" variant="outline"
className="flex-col h-auto py-3 hover:bg-green-500/10 hover:border-green-500" className="flex-col h-auto py-3 border-green-500/30 hover:bg-green-500/10 hover:border-green-500"
onClick={() => castFinalVote(proposal.id, "yes")} onClick={() => castFinalVote(proposal.id, "yes")}
> >
<Check className="h-5 w-5 text-green-500" /> <Check className="h-5 w-5 text-green-500" />
@ -450,7 +502,7 @@ export default function DemoPage() {
</Button> </Button>
<Button <Button
variant="outline" variant="outline"
className="flex-col h-auto py-3 hover:bg-red-500/10 hover:border-red-500" className="flex-col h-auto py-3 border-red-500/30 hover:bg-red-500/10 hover:border-red-500"
onClick={() => castFinalVote(proposal.id, "no")} onClick={() => castFinalVote(proposal.id, "no")}
> >
<X className="h-5 w-5 text-red-500" /> <X className="h-5 w-5 text-red-500" />
@ -473,7 +525,7 @@ export default function DemoPage() {
)} )}
{/* CTA */} {/* CTA */}
<Card className="border-primary bg-primary/5"> <Card className="border-2 border-primary/30 bg-gradient-to-r from-primary/10 to-accent/10">
<CardContent className="py-8 text-center space-y-4"> <CardContent className="py-8 text-center space-y-4">
<h2 className="text-2xl font-bold">Ready to try it for real?</h2> <h2 className="text-2xl font-bold">Ready to try it for real?</h2>
<p className="text-muted-foreground"> <p className="text-muted-foreground">
@ -481,7 +533,7 @@ export default function DemoPage() {
You&apos;ll get 50 credits to start and earn 10 more each day. You&apos;ll get 50 credits to start and earn 10 more each day.
</p> </p>
<div className="flex justify-center gap-4"> <div className="flex justify-center gap-4">
<Button asChild size="lg"> <Button asChild size="lg" className="bg-orange-500 hover:bg-orange-600">
<Link href="/auth/signup"> <Link href="/auth/signup">
Create Account <ArrowRight className="ml-2 h-4 w-4" /> Create Account <ArrowRight className="ml-2 h-4 w-4" />
</Link> </Link>

View File

@ -22,6 +22,7 @@ import {
ListOrdered, ListOrdered,
Target, Target,
Layers, Layers,
ChevronUp,
} from "lucide-react"; } from "lucide-react";
export default async function HomePage() { export default async function HomePage() {
@ -117,42 +118,68 @@ export default async function HomePage() {
{/* ELI5 Section */} {/* ELI5 Section */}
<section className="py-8"> <section className="py-8">
<Card className="border-2 border-accent/30 bg-gradient-to-r from-accent/5 via-primary/5 to-accent/5"> <div className="text-center mb-6">
<CardHeader className="text-center pb-2"> <Badge variant="secondary" className="mb-3 bg-muted text-muted-foreground">
<Badge variant="secondary" className="w-fit mx-auto mb-2 bg-accent/10 text-accent-foreground border-accent/20">
ELI5 ELI5
</Badge> </Badge>
<CardTitle className="text-2xl">rVote in 30 Seconds</CardTitle> <h2 className="text-2xl font-bold">rVote in 30 Seconds</h2>
</CardHeader> <p className="text-lg text-muted-foreground mt-2 max-w-2xl mx-auto">
<CardContent className="text-center max-w-3xl mx-auto"> A <strong className="text-orange-500">quadratic</strong>{" "}
<p className="text-lg text-muted-foreground leading-relaxed"> <strong className="text-blue-500">Reddit-style ranking system</strong>{" "}
<strong className="text-foreground">rVote</strong> is a{" "} with <strong className="text-purple-500">time-delayed vote decay</strong>{" "}
<strong className="text-primary">quadratic Reddit-style ranking system</strong>{" "}
with <strong className="text-accent">time-delayed vote decay</strong>{" "}
for proposal prioritization. for proposal prioritization.
</p> </p>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mt-6 text-sm"> </div>
<div className="p-3 rounded-lg bg-primary/10">
<strong className="text-primary">Quadratic</strong> <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<p className="text-muted-foreground mt-1"> {/* Quadratic */}
Voting more costs exponentially more credits (11, 24, 39), preventing any single voice from dominating. <Card className="border-2 border-orange-500/40 bg-gradient-to-br from-orange-500/10 to-orange-500/5 overflow-hidden">
<CardContent className="pt-5 pb-4">
<div className="flex items-center gap-2 mb-3">
<div className="h-8 w-8 rounded-full bg-orange-500 flex items-center justify-center">
<span className="text-white font-bold text-sm">x²</span>
</div>
<h3 className="font-bold text-orange-600 text-lg">Quadratic</h3>
</div>
<p className="text-sm text-muted-foreground leading-relaxed">
Voting more costs exponentially more credits. 1 vote = 1 credit, 2 votes = 4, 3 votes = 9.
<strong className="text-foreground block mt-2">No single voice can dominate.</strong>
</p> </p>
</div>
<div className="p-3 rounded-lg bg-secondary/10">
<strong className="text-secondary">Reddit-style</strong>
<p className="text-muted-foreground mt-1">
Upvote or downvote proposals. The best ideas rise to the top through collective ranking.
</p>
</div>
<div className="p-3 rounded-lg bg-accent/10">
<strong className="text-accent-foreground">Vote Decay</strong>
<p className="text-muted-foreground mt-1">
Votes fade after 30-60 days, ensuring rankings reflect current community priorities, not ancient history.
</p>
</div>
</div>
</CardContent> </CardContent>
</Card> </Card>
{/* Reddit-style */}
<Card className="border-2 border-blue-500/40 bg-gradient-to-br from-blue-500/10 to-blue-500/5 overflow-hidden">
<CardContent className="pt-5 pb-4">
<div className="flex items-center gap-2 mb-3">
<div className="h-8 w-8 rounded-full bg-blue-500 flex items-center justify-center">
<ChevronUp className="h-5 w-5 text-white" />
</div>
<h3 className="font-bold text-blue-600 text-lg">Reddit-style</h3>
</div>
<p className="text-sm text-muted-foreground leading-relaxed">
Upvote or downvote proposals. Scores aggregate from all community votes.
<strong className="text-foreground block mt-2">Best ideas rise to the top.</strong>
</p>
</CardContent>
</Card>
{/* Vote Decay */}
<Card className="border-2 border-purple-500/40 bg-gradient-to-br from-purple-500/10 to-purple-500/5 overflow-hidden">
<CardContent className="pt-5 pb-4">
<div className="flex items-center gap-2 mb-3">
<div className="h-8 w-8 rounded-full bg-purple-500 flex items-center justify-center">
<Clock className="h-4 w-4 text-white" />
</div>
<h3 className="font-bold text-purple-600 text-lg">Vote Decay</h3>
</div>
<p className="text-sm text-muted-foreground leading-relaxed">
Votes fade after 30-60 days. Old support expires, requiring renewed interest.
<strong className="text-foreground block mt-2">Rankings stay fresh and relevant.</strong>
</p>
</CardContent>
</Card>
</div>
</section> </section>
{/* What is Quadratic Proposal Ranking */} {/* What is Quadratic Proposal Ranking */}