Files
calvana/pledge-now-pay-later/src/app/page.tsx
Omair Saleh ac19afce4e world-class hero image + 85% image optimization + sharp
HERO IMAGE:
- Generated 3 concepts with gemini-3-pro-image-preview, picked #1
- Phone showing 'Payment Received' notification at a charity gala dinner
- Warm tungsten bokeh chandeliers against dark bg-gray-950
- Directly visualizes the headline: 'money in the bank'
- Candid documentary angle, not looking at camera, brand compliant

IMAGE OPTIMIZATION (85% total reduction):
- All 21 images resized: landscape max 1200px, portrait max 1000px
- Compressed JPEG quality 80, progressive encoding, EXIF stripped
- Total: 13.6MB -> 2.1MB (saved 11.5MB)
- Individual savings: 81-90% per image

NEXT.JS IMAGE PIPELINE:
- Added sharp (10x faster than squoosh for image processing)
- next.config.mjs: WebP format, proper device/image sizes, 1yr cache TTL
- Dockerfile: libc6-compat + NEXT_SHARP_PATH for Alpine sharp support
- First request: ~3s (processing), cached: <1s

WebP served sizes: hero 52KB, cards 32-40KB (vs original 500-800KB JPEGs)
2026-03-03 21:10:59 +08:00

266 lines
13 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import Link from "next/link"
import Image from "next/image"
import { Nav, Footer, BottomCta, LandingImage } from "./for/_components"
/* ── Hero stats ── */
const HERO_STATS = [
{ stat: "3050%", label: "of pledges never collected" },
{ stat: "60s", label: "to complete a pledge" },
{ stat: "£0", label: "cost to charities" },
{ stat: "2 min", label: "signup to first link" },
]
/* ── Persona cards ── */
const PERSONAS = [
{
slug: "charities",
title: "Charity Manager",
oneLiner: "You raise pledges at events. We make sure the money actually arrives.",
tags: ["Dashboard", "WhatsApp reminders", "Gift Aid", "Zakat", "HMRC export"],
image: "/images/landing/08-charities-hero.jpg",
},
{
slug: "fundraisers",
title: "Personal Fundraiser",
oneLiner: "You share a LaunchGood or JustGiving link. We track who actually donates.",
tags: ["LaunchGood", "Enthuse", "JustGiving", "Social sharing", "Conversion tracking"],
image: "/images/landing/03-main-fundraiser-card.jpg",
},
{
slug: "volunteers",
title: "Volunteer",
oneLiner: "You help collect pledges at events. We show you exactly how much you raised.",
tags: ["Personal link", "Live stats", "Leaderboard", "WhatsApp share"],
image: "/images/landing/04-main-volunteer-card.jpg",
},
{
slug: "organisations",
title: "Organisation",
oneLiner: "You coordinate pledges across multiple charities or departments. We track every commitment.",
tags: ["Multi-party", "Fund allocation", "Pipeline view", "Instalments"],
image: "/images/landing/05-main-org-card.jpg",
},
]
export default function HomePage() {
return (
<div className="min-h-screen bg-white">
<Nav />
{/* ━━ HERO (dark) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */}
<section className="bg-gray-950 overflow-hidden">
<div className="max-w-5xl mx-auto px-6 pt-20 pb-16 md:pt-28 md:pb-20">
<div className="grid md:grid-cols-12 gap-10 md:gap-14 items-start">
{/* ── Text column ── */}
<div className="md:col-span-7 pt-2 md:pt-6 stagger-children">
{/* Eyebrow — border-l-2 accent (signature pattern 1) */}
<div className="border-l-2 border-promise-blue pl-3 mb-8">
<p className="text-[11px] font-semibold tracking-[0.15em] uppercase text-gray-500">
Pledge collection for UK charities
</p>
</div>
{/* Headline — Display scale */}
<h1 className="text-[2.75rem] leading-[0.95] sm:text-6xl md:text-[4.25rem] lg:text-7xl font-black text-white tracking-tighter">
Turn &ldquo;I&apos;ll donate&rdquo; into money in&nbsp;the&nbsp;bank.
</h1>
{/* Sub */}
<p className="text-base md:text-lg text-gray-400 max-w-md mt-7 leading-relaxed">
People pledge at events, over dinner, on WhatsApp. We make sure the money actually arrives.
</p>
{/* CTAs */}
<div className="flex flex-col sm:flex-row gap-3 mt-9">
<Link
href="/signup"
className="inline-flex items-center justify-center bg-white px-7 py-3.5 text-sm font-bold text-gray-900 hover:bg-gray-100 transition-colors"
>
Start free takes 2 minutes
</Link>
<Link
href="/login?demo=1"
className="inline-flex items-center justify-center border border-gray-700 px-7 py-3.5 text-sm font-bold text-gray-400 hover:text-white hover:border-gray-500 transition-colors"
>
See live demo
</Link>
</div>
{/* Trust line — vertical separators */}
<div className="flex items-center gap-4 mt-7 text-[11px] text-gray-600">
<span>No card required</span>
<span className="w-px h-3 bg-gray-800" />
<span>HMRC compliant</span>
<span className="w-px h-3 bg-gray-800" />
<span>Free forever</span>
</div>
</div>
{/* ── Image column ── */}
<div className="md:col-span-5" style={{ opacity: 0, animation: "fadeUp 0.5s ease-out 0.25s forwards" }}>
<div className="aspect-[3/4] md:aspect-[4/5] w-full relative overflow-hidden">
<Image
src="/images/landing/00-hero.jpg"
alt="Payment received notification on phone at a charity gala dinner"
fill
className="object-cover"
sizes="(max-width: 768px) 100vw, 40vw"
quality={90}
priority
/>
</div>
</div>
</div>
</div>
{/* Stat strip — gap-px pattern (signature pattern 2) */}
<div className="border-t border-gray-800/50">
<div className="max-w-5xl mx-auto">
<div className="grid grid-cols-2 md:grid-cols-4 gap-px bg-gray-800/50">
{HERO_STATS.map((s) => (
<div key={s.stat} className="bg-gray-950 py-6 px-6">
<p className="text-xl md:text-2xl font-black text-white tracking-tight">{s.stat}</p>
<p className="text-[11px] text-gray-500 mt-1">{s.label}</p>
</div>
))}
</div>
</div>
</div>
</section>
{/* ━━ PRODUCT IMAGE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */}
<section className="pt-16 pb-8 md:pt-20 md:pb-10 px-6">
<div className="max-w-5xl mx-auto">
<LandingImage src="/images/landing/01-main-dashboard-hero.jpg" alt="Live pledge pipeline dashboard showing donation trends and collection status" aspect="video" />
</div>
</section>
{/* ━━ 4 PERSONAS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */}
<section className="py-24 px-6">
<div className="max-w-5xl mx-auto space-y-14">
<div className="max-w-2xl">
<h2 className="text-4xl font-black text-gray-900 tracking-tight">Built for how you actually work</h2>
<p className="text-lg text-gray-500 mt-3">Four roles. One platform. Every pledge tracked.</p>
</div>
<div className="grid md:grid-cols-2 gap-6">
{PERSONAS.map((p) => (
<Link
key={p.slug}
href={`/for/${p.slug}`}
className="group block bg-white border border-gray-200 hover:border-gray-900 transition-colors"
>
<LandingImage src={p.image} alt={p.title} aspect="video" />
<div className="p-6 space-y-3">
<h3 className="text-lg font-black text-gray-900 group-hover:text-promise-blue transition-colors">{p.title}</h3>
<p className="text-sm text-gray-500 leading-relaxed">{p.oneLiner}</p>
<div className="flex flex-wrap gap-2">
{p.tags.map((t) => (
<span key={t} className="text-[11px] font-medium text-gray-400 bg-gray-50 px-2 py-0.5">{t}</span>
))}
</div>
<p className="text-sm font-semibold text-promise-blue opacity-0 group-hover:opacity-100 transition-opacity pt-1">
Learn more
</p>
</div>
</Link>
))}
</div>
</div>
</section>
{/* ━━ HOW IT WORKS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */}
<section className="py-24 bg-gray-50 px-6">
<div className="max-w-5xl mx-auto">
<div className="max-w-2xl mb-16">
<h2 className="text-4xl font-black text-gray-900 tracking-tight">Four steps. Zero pledges lost.</h2>
</div>
<div className="grid md:grid-cols-4 gap-10">
{[
{ n: "01", title: "Create a pledge link", desc: "One link per campaign, table, volunteer, or WhatsApp group. Share anywhere." },
{ n: "02", title: "Donor pledges", desc: "Amount, Gift Aid, Zakat, schedule — 60-second mobile flow. No app download." },
{ n: "03", title: "WhatsApp follows up", desc: "Automated reminders with bank details. They reply PAID when done." },
{ n: "04", title: "Money arrives", desc: "Live dashboard. Who pledged, who paid, who needs a nudge." },
].map((s) => (
<div key={s.n} className="space-y-4">
<p className="text-4xl font-black text-gray-200">{s.n}</p>
<h3 className="text-base font-bold text-gray-900">{s.title}</h3>
<p className="text-sm text-gray-500 leading-relaxed">{s.desc}</p>
</div>
))}
</div>
</div>
</section>
{/* ━━ COMPLIANCE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */}
<section className="py-24 px-6">
<div className="max-w-5xl mx-auto grid md:grid-cols-2 gap-12 items-center">
<LandingImage src="/images/landing/06-main-pledge-form.jpg" alt="Person holding phone showing pledge form with Gift Aid and consent checkboxes" aspect="square" />
<div className="space-y-8">
<div>
<h2 className="text-3xl font-black text-gray-900 tracking-tight">Compliance is not optional</h2>
<p className="text-gray-500 mt-3">Every pledge collects bulletproof consent. Ready for HMRC, ICO, and your trustees.</p>
</div>
<div className="space-y-5">
{[
{ label: "Gift Aid", sub: "HMRC model declaration, home address, timestamped. One-click CSV for claiming." },
{ label: "Zakat", sub: "Per-campaign toggle. Donors tick one checkbox. Tracked separately in reports." },
{ label: "Email consent", sub: "GDPR compliant. Separate opt-in, never pre-ticked. Full audit trail." },
{ label: "WhatsApp consent", sub: "PECR compliant. Separate opt-in. Reply STOP to opt out. No sends without permission." },
].map((c) => (
<div key={c.label} className="border-l-2 border-gray-900 pl-4">
<p className="text-sm font-bold text-gray-900">{c.label}</p>
<p className="text-xs text-gray-500 mt-0.5">{c.sub}</p>
</div>
))}
</div>
</div>
</div>
</section>
{/* ━━ PAYMENT FLEXIBILITY ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */}
<section className="py-24 bg-gray-50 px-6">
<div className="max-w-5xl mx-auto grid md:grid-cols-2 gap-12 items-center">
<div className="space-y-6 order-2 md:order-1">
<h2 className="text-3xl font-black text-gray-900 tracking-tight">Donors pay when they&apos;re ready</h2>
<div className="space-y-4">
{[
{ title: "Pay now", desc: "Bank transfer or redirect to their fundraising page." },
{ title: "Pick a date", desc: "\"I'll pay on payday\" — WhatsApp reminder sent automatically." },
{ title: "Monthly instalments", desc: "Split into 212 payments. Each one tracked and reminded separately." },
].map((o) => (
<div key={o.title} className="border-l-2 border-gray-900 pl-4">
<p className="text-sm font-bold text-gray-900">{o.title}</p>
<p className="text-xs text-gray-500 mt-0.5">{o.desc}</p>
</div>
))}
</div>
</div>
<div className="order-1 md:order-2">
<LandingImage src="/images/landing/07-main-schedule-step.jpg" alt="Hands holding phone at kitchen table showing payment schedule options" aspect="square" />
</div>
</div>
</section>
{/* ━━ PLATFORMS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */}
<section className="py-20 px-6 border-t">
<div className="max-w-4xl mx-auto">
<div className="text-center mb-10">
<h2 className="text-2xl font-black text-gray-900">Works with your existing platform</h2>
</div>
<div className="flex flex-wrap justify-center gap-4">
{["Bank Transfer (UK)", "LaunchGood", "Enthuse", "JustGiving", "GoFundMe", "Any URL"].map((p) => (
<div key={p} className="border border-gray-200 px-5 py-3 text-sm font-medium text-gray-700">{p}</div>
))}
</div>
</div>
</section>
<BottomCta />
<Footer />
</div>
)
}