feat: add email subscribe CTAs, Valley of the Commons section, update button text

- Add Ghost blog newsletter subscribe form (blog.crypto-commons.org) in hero and bottom CTA sections
- Update register buttons to "Register Now to Save Your Seat!"
- Add Valley of the Commons section with weekly themes and overlap info

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jeff Emmett 2026-02-12 11:29:49 +00:00
parent 42a406f46c
commit f82cd57aa9
1 changed files with 195 additions and 4 deletions

View File

@ -4,12 +4,42 @@ import type React from "react"
import { Button } from "@/components/ui/button" import { Button } from "@/components/ui/button"
import { Card, CardContent } from "@/components/ui/card" import { Card, CardContent } from "@/components/ui/card"
import { ArrowRight, Calendar, MapPin, Users, Heart, Sprout, Network, X } from "lucide-react" import { Input } from "@/components/ui/input"
import { ArrowRight, Calendar, MapPin, Users, Heart, Sprout, Network, X, Mail, Loader2, CheckCircle2 } from "lucide-react"
import Link from "next/link" import Link from "next/link"
import { useState } from "react" import { useState } from "react"
export default function HomePage() { export default function HomePage() {
const [selectedImage, setSelectedImage] = useState<string | null>(null) const [selectedImage, setSelectedImage] = useState<string | null>(null)
const [email, setEmail] = useState("")
const [subscribeStatus, setSubscribeStatus] = useState<"idle" | "loading" | "success" | "error">("idle")
const [subscribeError, setSubscribeError] = useState("")
const handleSubscribe = async (e: React.FormEvent) => {
e.preventDefault()
if (!email) return
setSubscribeStatus("loading")
setSubscribeError("")
try {
const res = await fetch("https://blog.crypto-commons.org/members/api/send-magic-link/", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email, emailType: "subscribe" }),
})
if (res.ok) {
setSubscribeStatus("success")
setEmail("")
} else {
throw new Error("Subscription failed")
}
} catch {
setSubscribeStatus("error")
setSubscribeError("Something went wrong. Please try again.")
}
}
const handleImageClick = (imageSrc: string) => { const handleImageClick = (imageSrc: string) => {
setSelectedImage(imageSrc) setSelectedImage(imageSrc)
@ -114,10 +144,10 @@ END:VCALENDAR`
building, and radical imagination in the Austrian Alps. building, and radical imagination in the Austrian Alps.
</p> </p>
<div className="flex flex-col sm:flex-row gap-4 justify-center items-center mb-12"> <div className="flex flex-col sm:flex-row gap-4 justify-center items-center mb-8">
<Button size="lg" className="gap-2 text-base" asChild> <Button size="lg" className="gap-2 text-base" asChild>
<Link href="/register"> <Link href="/register">
Register Now <ArrowRight className="w-4 h-4" /> Register Now to Save Your Seat! <ArrowRight className="w-4 h-4" />
</Link> </Link>
</Button> </Button>
<Button <Button
@ -142,6 +172,40 @@ END:VCALENDAR`
</Button> </Button>
</div> </div>
<div className="max-w-md mx-auto mb-12">
<p className="text-sm text-foreground/80 mb-3 font-medium">
Drop your email to stay in the loop on upcoming announcements!
</p>
{subscribeStatus === "success" ? (
<div className="flex items-center justify-center gap-2 text-sm text-green-600 dark:text-green-400 bg-green-50 dark:bg-green-950/30 rounded-md py-2.5 px-4 border border-green-200 dark:border-green-800">
<CheckCircle2 className="w-4 h-4" />
You're subscribed! Check your inbox to confirm.
</div>
) : (
<form onSubmit={handleSubscribe} className="flex gap-2">
<Input
type="email"
placeholder="your@email.com"
value={email}
onChange={(e) => { setEmail(e.target.value); setSubscribeStatus("idle") }}
required
className="bg-background/80 backdrop-blur-sm"
/>
<Button type="submit" size="default" className="gap-2 shrink-0" disabled={subscribeStatus === "loading"}>
{subscribeStatus === "loading" ? (
<Loader2 className="w-4 h-4 animate-spin" />
) : (
<Mail className="w-4 h-4" />
)}
Subscribe
</Button>
</form>
)}
{subscribeStatus === "error" && (
<p className="text-sm text-destructive mt-2">{subscribeError}</p>
)}
</div>
<div className="flex flex-wrap gap-6 justify-center text-sm text-muted-foreground"> <div className="flex flex-wrap gap-6 justify-center text-sm text-muted-foreground">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<MapPin className="w-4 h-4 text-primary" /> <MapPin className="w-4 h-4 text-primary" />
@ -406,19 +470,146 @@ END:VCALENDAR`
<div className="flex flex-col sm:flex-row gap-4 justify-center mb-8"> <div className="flex flex-col sm:flex-row gap-4 justify-center mb-8">
<Button size="lg" className="gap-2" asChild> <Button size="lg" className="gap-2" asChild>
<Link href="/register"> <Link href="/register">
Register and Get Tickets <ArrowRight className="w-4 h-4" /> Register Now to Save Your Seat! <ArrowRight className="w-4 h-4" />
</Link> </Link>
</Button> </Button>
<Button size="lg" variant="outline" asChild> <Button size="lg" variant="outline" asChild>
<Link href="/transparency">Financial Transparency</Link> <Link href="/transparency">Financial Transparency</Link>
</Button> </Button>
</div> </div>
<div className="max-w-md mx-auto mb-8">
<p className="text-sm text-muted-foreground mb-3 font-medium">
Drop your email to stay in the loop on upcoming announcements!
</p>
{subscribeStatus === "success" ? (
<div className="flex items-center justify-center gap-2 text-sm text-green-600 dark:text-green-400 bg-green-50 dark:bg-green-950/30 rounded-md py-2.5 px-4 border border-green-200 dark:border-green-800">
<CheckCircle2 className="w-4 h-4" />
You're subscribed! Check your inbox to confirm.
</div>
) : (
<form onSubmit={handleSubscribe} className="flex gap-2">
<Input
type="email"
placeholder="your@email.com"
value={email}
onChange={(e) => { setEmail(e.target.value); setSubscribeStatus("idle") }}
required
/>
<Button type="submit" size="default" className="gap-2 shrink-0" disabled={subscribeStatus === "loading"}>
{subscribeStatus === "loading" ? (
<Loader2 className="w-4 h-4 animate-spin" />
) : (
<Mail className="w-4 h-4" />
)}
Subscribe
</Button>
</form>
)}
{subscribeStatus === "error" && (
<p className="text-sm text-destructive mt-2">{subscribeError}</p>
)}
</div>
<p className="text-sm text-muted-foreground"> <p className="text-sm text-muted-foreground">
Interested in the Crypto Commons Association cooperative? Learn more at the event. Interested in the Crypto Commons Association cooperative? Learn more at the event.
</p> </p>
</div> </div>
</section> </section>
{/* Valley of the Commons Section */}
<section className="py-24 px-4 relative overflow-hidden">
<div
className="absolute inset-0 bg-cover bg-center"
style={{
backgroundImage: `url('/images/20220429-145359.jpg')`,
}}
/>
<div className="absolute inset-0 bg-gradient-to-br from-background/95 via-background/90 to-background/95" />
<div className="container mx-auto max-w-5xl relative z-10">
<div className="text-center mb-12">
<p className="text-sm font-mono text-primary mb-3 tracking-wider uppercase">New for 2026</p>
<h2 className="text-3xl md:text-4xl font-bold mb-4 text-balance">
Stay for the Valley of the Commons
</h2>
<p className="text-lg text-muted-foreground max-w-2xl mx-auto text-pretty">
When CCG wraps on August 22, the experiment is just getting started. Stick around for 1 to 4 weeks and
join a new extended event right where you are in the Austrian Alps.
</p>
</div>
<div className="grid md:grid-cols-2 gap-8 items-start mb-12">
<div>
<h3 className="text-xl font-bold mb-4">What is it?</h3>
<p className="text-muted-foreground leading-relaxed mb-4">
Valley of the Commons is a four-week experimental pop-up village running{" "}
<span className="font-semibold text-foreground">August 24 September 20, 2026</span> picking up
just two days after CCG ends. It's not a conference or a simulation: it's a functioning co-living
community taking concrete steps toward permanent settlement in the Höllental valley.
</p>
<p className="text-muted-foreground leading-relaxed">
Mornings are structured learning paths, afternoons bring workshops and working groups, evenings are
communal time, and weekends are open for exploring the Alps. You can stay for one week or all four.
</p>
</div>
<div>
<h3 className="text-xl font-bold mb-4">Weekly Themes</h3>
<div className="space-y-3">
<div className="flex gap-3 items-start">
<span className="text-primary font-mono font-bold text-sm mt-0.5 shrink-0">WK 1</span>
<div>
<p className="font-semibold text-sm">Return of the Commons</p>
<p className="text-sm text-muted-foreground">Commons theory and history, with guest speakers</p>
</div>
</div>
<div className="flex gap-3 items-start">
<span className="text-primary font-mono font-bold text-sm mt-0.5 shrink-0">WK 2</span>
<div>
<p className="font-semibold text-sm">Production & Value</p>
<p className="text-sm text-muted-foreground">Cosmo-local production, open value accounting systems</p>
</div>
</div>
<div className="flex gap-3 items-start">
<span className="text-primary font-mono font-bold text-sm mt-0.5 shrink-0">WK 3</span>
<div>
<p className="font-semibold text-sm">Cooperative Housing</p>
<p className="text-sm text-muted-foreground">Nomad-friendly communal housing, renovation, ecological integration</p>
</div>
</div>
<div className="flex gap-3 items-start">
<span className="text-primary font-mono font-bold text-sm mt-0.5 shrink-0">WK 4</span>
<div>
<p className="font-semibold text-sm">Governance & Funding</p>
<p className="text-sm text-muted-foreground">Horizontal governance, legal frameworks, funding mechanisms</p>
</div>
</div>
</div>
</div>
</div>
<Card className="border-primary/20 bg-background/80 backdrop-blur-sm">
<CardContent className="p-6 md:p-8">
<div className="flex flex-col md:flex-row gap-6 items-center justify-between">
<div className="text-center md:text-left">
<h3 className="font-bold text-lg mb-2">CCG + Valley: The Full Experience</h3>
<p className="text-sm text-muted-foreground max-w-lg">
Come for CCG's unconference week (Aug 16-22), then flow straight into Valley of the Commons
(Aug 24 Sep 20). Same mountains, same community, deeper roots. Perfect for digital workers,
post-corporate professionals, and anyone seeking grounded, cooperative ways of living.
</p>
</div>
<Button size="lg" variant="outline" className="gap-2 shrink-0" asChild>
<Link href="https://www.valleyofthecommons.com/" target="_blank" rel="noopener noreferrer">
Learn More <ArrowRight className="w-4 h-4" />
</Link>
</Button>
</div>
</CardContent>
</Card>
</div>
</section>
{/* Footer */} {/* Footer */}
<footer className="py-12 px-4 border-t border-border"> <footer className="py-12 px-4 border-t border-border">
<div className="container mx-auto max-w-6xl"> <div className="container mx-auto max-w-6xl">