add AI-generated landing page photography (Gemini 3 Pro)

20 images generated via gemini-3-pro-image-preview (Nano Banana Pro):
- Documentary street photography style, British-diverse subjects
- 12 square (1:1), 8 landscape (16:9) matching placeholder aspect ratios
- Replaced all ImagePlaceholder components with LandingImage + next/image
- Images in public/images/landing/, served statically

Pages updated: /, /for/charities, /for/fundraisers, /for/volunteers, /for/organisations
New component: LandingImage (next/image with fill + object-cover)
This commit is contained in:
2026-03-03 19:27:36 +08:00
parent 581f1e5f14
commit f4ad6df45a
27 changed files with 182 additions and 29 deletions

135
gen_images.py Normal file
View File

@@ -0,0 +1,135 @@
#!/usr/bin/env python3
"""Generate PNPL landing page images using Gemini 3 Pro Image (Nano Banana Pro)."""
import time, base64
from pathlib import Path
from google import genai
from google.genai import types
API_KEY = "AIzaSyCHnesXLjPw-UgeZaQotut66bgjFdvy12E"
MODEL = "gemini-3-pro-image-preview"
OUT_DIR = Path(r"C:\Users\uldvs\Downloads\PNPL Photography")
OUT_DIR.mkdir(parents=True, exist_ok=True)
client = genai.Client(api_key=API_KEY)
IMAGES = [
# ── Main page ──
("01-main-dashboard-hero", "16:9",
"Editorial product photography of a MacBook Pro on a minimal white desk showing a charity analytics dashboard with blue bar charts and a pipeline table. Clean desk with only a coffee cup and a single pen. Soft diffused daylight from the left. Shot on Sony A7III, 50mm f/2.8, shallow depth of field on the screen edges. Muted tones, magazine editorial style. Landscape orientation 16:9 aspect ratio."),
("02-main-charity-manager-card", "16:9",
"Candid documentary photograph of a British-Bangladeshi woman in her 40s wearing a dark navy hijab and tailored blazer, standing at the edge of a charity gala dinner in a London hotel ballroom. She holds a tablet at her side, looking across the room. Round tables with white cloths and warm tungsten pendant lights behind her, out of focus. Shot on Leica Q2, 28mm, f/1.7, available light only, natural film grain. Skin tones warm, background amber bokeh. No eye contact with camera. Landscape orientation 16:9 aspect ratio."),
("03-main-fundraiser-card", "16:9",
"Street photography of a young British-Somali man in his mid-20s wearing a grey crewneck sweatshirt, walking on a London residential street and looking down at his phone screen. Overcast daylight, Victorian terraced houses soft in the background. He is mid-stride, natural posture, not posing. Shot on Fujifilm X100V, 23mm, f/2, ISO 400, slight grain. Desaturated cool tones, editorial documentary feel. Landscape orientation 16:9 aspect ratio."),
("04-main-volunteer-card", "16:9",
"Candid indoor photograph of a young British-Pakistani woman in her early 20s wearing a plain volunteer lanyard on a black cord, leaning across a round banquet table pointing at a small printed card on the table. Warm overhead chandelier lighting in a mosque community hall, patterned carpet floor. Other seated guests softly blurred. Shot on Canon R6, 35mm f/1.4, shallow depth of field, warm amber tones. Natural, mid-conversation moment. Landscape orientation 16:9 aspect ratio."),
("05-main-org-card", "16:9",
"Documentary photograph of four people seated around a plain rectangular table in a small community centre meeting room. Mixed British ethnicities, business casual clothing. One person gestures at printed papers on the table, others listening. Overhead fluorescent strip lights mixed with grey window daylight. Institutional beige walls, a whiteboard partially visible behind. Shot on Nikon Z6, 28mm f/2.8, natural exposure, slightly flat lighting. No one looking at camera, mid-discussion. Landscape orientation 16:9 aspect ratio."),
("06-main-pledge-form", "1:1",
"Close-up over-the-shoulder photograph of a person holding an iPhone in their right hand at a dinner table. Screen shows a clean white mobile form with toggle switches and a green button, content not readable. A glass of water and folded linen napkin on the dark tablecloth beside the hand. Warm tungsten ambient light. Face not visible, only shoulder, arm, and hand in frame. Shot on 85mm f/1.8, tight crop, shallow depth of field on the screen. Natural skin tones, no flash. Square 1:1 aspect ratio."),
("07-main-schedule-step", "1:1",
"Overhead 45-degree photograph of a woman's hands holding a smartphone at a kitchen table. Soft morning window light from the left casting a gentle shadow. A ceramic mug of tea and a small plate with toast crumbs slightly out of focus behind the phone. The phone screen is bright white with blue accent elements, not readable. Domestic setting, wooden table surface. Shot on 50mm f/2.0, natural light only, warm muted tones. Quiet, everyday moment. Square 1:1 aspect ratio."),
# ── Charities page ──
("08-charities-hero", "1:1",
"Documentary portrait of a British-Bangladeshi man in his 50s wearing a white embroidered kufi cap, silver-framed reading glasses, and a pressed light blue shirt. He is seated at a side table during a mosque fundraising event, looking at an open laptop screen. Behind him, blurred rows of seated attendees in a large hall with green and gold decorations. Warm interior lighting, patterned Islamic geometric carpet visible on the floor. Shot on 56mm f/1.2, available light, rich warm tones, film grain. Candid, absorbed in his work. Square 1:1 aspect ratio."),
("09-charities-amount-selection", "1:1",
"Flat lay photograph of a hand holding an iPhone face-up on a dark navy tablecloth. The thumb hovers just above the bright screen. Visible beside the phone: silver cutlery, the edge of a white dinner plate, a printed menu card, and a glass of still water. Shot from directly above, 35mm, f/4, tungsten warm lighting from overhead chandeliers. The screen shows large numbers and a blue button, not readable. Charity dinner table setting, elegant but modest. Square 1:1 aspect ratio."),
("10-charities-whatsapp", "16:9",
"Candid photograph of a woman's hand holding a phone on her lap while sitting on a grey fabric sofa at home. The phone screen shows green WhatsApp message bubbles, text not readable. She wears a loose cardigan sleeve and a simple watch. A cushion and part of a wooden bookshelf with books visible softly in the background. Afternoon window daylight, warm golden hour tones. Shot on 50mm f/1.8, eye-level, shallow depth of field. Domestic, relaxed, real. Landscape orientation 16:9 aspect ratio."),
# ── Fundraisers page ──
("11-fundraisers-hero", "1:1",
"Street photography of a young British-Arab woman in her late 20s wearing a patterned burgundy hijab and a denim jacket, standing on a London high street and holding her phone screen towards a friend. The friend is partially visible and out of focus on the right edge of frame. Overcast flat daylight, shop fronts with awnings blurred behind. She is mid-laugh, natural expression. Shot on 85mm f/1.4, compressed background bokeh, desaturated cool tones with warm skin. Unstaged genuine interaction. Square 1:1 aspect ratio."),
("12-fundraisers-redirect", "1:1",
"Close-up of two hands holding a phone in a cafe. The screen shows a green-themed webpage with a large rounded button, generic and not readable. A marble cafe table surface with a flat white coffee in a ceramic cup and a torn croissant on a small plate beside the phone. Natural soft window light from the right. Shot on 50mm f/2.0, shallow depth of field focused on the phone screen. Warm cafe tones, European coffee shop atmosphere. No face visible. Square 1:1 aspect ratio."),
("13-fundraisers-dashboard", "16:9",
"Editorial product photography of a laptop screen showing a minimal analytics dashboard with a horizontal funnel chart in shades of blue, grey number cards across the top, and a simple data table below. The laptop sits on a clean white desk. Beside it, a small potted succulent and a closed notebook. Soft even studio lighting, no harsh shadows. Shot on 50mm f/4, sharp focus on screen, edges of laptop slightly soft. Clean, SaaS product marketing style. Landscape orientation 16:9 aspect ratio."),
# ── Volunteers page ──
("14-volunteers-hero", "1:1",
"Candid photograph of a young British-Pakistani man in his early 20s standing behind a round banquet table at a charity dinner. He wears a plain navy polo shirt and a clear plastic lanyard with a printed name badge. In the sharp foreground, a small clear acrylic A-frame stand with a QR code printed on white card sits on the white tablecloth. The volunteer is smiling at someone off-camera to the left. Background shows other round tables and warm pendant lighting, all soft and blurred. Shot on 35mm f/1.4 from table height, warm amber tones, available light. Energetic, genuine. Square 1:1 aspect ratio."),
("15-volunteers-phone-stats", "1:1",
"Dynamic low-angle photograph of a volunteer's torso and hand holding a phone at waist height. A black lanyard with a badge hangs from their neck, visible at the top of frame. The phone screen is bright showing a large number and a progress bar in blue, not readable. Behind and above, blurred figures walking through an event hall with overhead spotlights creating flare. Shot on 24mm f/2.0, slightly tilted angle, motion blur on background figures, available light. Raw, energetic, in-the-moment. Square 1:1 aspect ratio."),
("16-volunteers-leaderboard", "16:9",
"Editorial product photography of a phone screen showing a ranked list with numbered rows, small circular avatar placeholders, names, and bar charts beside each row. The phone lies flat on a dark surface. A lanyard and a paper name badge sit beside it. Soft directional studio light from the top left. Shot on macro lens, f/4, sharp focus on the screen, surroundings slightly dark. Clean, focused, app showcase. Landscape orientation 16:9 aspect ratio."),
# ── Organisations page ──
("17-orgs-hero-boardroom", "1:1",
"Documentary photograph of three people in a modest charity office meeting room around a rectangular table. A Black British woman in her 40s in a navy blazer is pointing at a laptop screen. A South Asian man in his 30s in a grey suit leans in to look. A white British man in his 50s in a rolled-sleeve shirt sits across, arms folded, listening. Papers and printed spreadsheets on the table. Grey carpet, a window with vertical blinds letting in flat daylight. Overhead fluorescent lighting. Shot on 28mm f/2.8, documentary wide angle, no one looking at camera, mid-discussion. Slightly cool tones, institutional setting. Square 1:1 aspect ratio."),
("18-orgs-pipeline", "1:1",
"Editorial product photography of a laptop screen showing a Kanban-style board with three columns of cards in white, each card showing a name, a currency amount, and a coloured status dot. The laptop is a MacBook on a plain wooden desk. A pair of reading glasses and a printed agenda document sit beside it. Soft even natural light from a window. Shot on 50mm f/3.5, focused on the screen, desk edges slightly out of focus. Neutral professional tones. Square 1:1 aspect ratio."),
("19-orgs-instalment-schedule", "16:9",
"Editorial product photography of a laptop screen showing a timeline or calendar view with monthly markers and progress bars in blue and grey. The laptop sits on a white conference table. Two chairs partially visible on either side, slightly blurred. A glass of water and a pen on the table. Clean fluorescent overhead lighting, even exposure. Shot on 35mm f/4, sharp focus on screen. Corporate clean, minimal, informational. Landscape orientation 16:9 aspect ratio."),
("20-orgs-laptop-desk", "1:1",
"Documentary photograph of a laptop open on a cluttered but real charity office desk. The screen shows a table with rows of data, not readable. A hand rests on the trackpad, wearing a simple watch. On the desk: a white ceramic mug with a tea bag tag hanging over the edge, a stack of manila folders, a small framed family photo, a desk lamp switched on. Mixed warm desk lamp light and cool daylight from a window on the right. Shot on 35mm f/2.0, focused on the laptop and hand, background slightly soft. Warm, lived-in, working moment. Square 1:1 aspect ratio."),
]
def generate_image(filename, aspect, prompt):
out_path = OUT_DIR / f"{filename}.jpg"
if out_path.exists():
print(f" SKIP (exists): {filename}")
return True
print(f" Generating: {filename} ({aspect})...")
try:
response = client.models.generate_content(
model=MODEL,
contents=prompt,
config=types.GenerateContentConfig(
response_modalities=["IMAGE", "TEXT"],
),
)
for part in response.candidates[0].content.parts:
if part.inline_data:
img_bytes = part.inline_data.data
with open(out_path, "wb") as f:
f.write(img_bytes)
size_kb = len(img_bytes) / 1024
print(f" DONE: {filename} ({size_kb:.0f} KB)")
return True
print(f" FAIL: {filename} - no image in response")
return False
except Exception as e:
print(f" ERROR: {filename} - {e}")
return False
if __name__ == "__main__":
print(f"Model: {MODEL} (Nano Banana Pro)")
print(f"Output: {OUT_DIR}")
print(f"Images: {len(IMAGES)}")
print()
ok = 0
fail = 0
for i, (fn, asp, pr) in enumerate(IMAGES, 1):
print(f"[{i}/{len(IMAGES)}]")
if generate_image(fn, asp, pr):
ok += 1
else:
fail += 1
if i < len(IMAGES):
time.sleep(5)
print(f"\nDone: {ok} generated, {fail} failed")
print(f"Files in: {OUT_DIR}")

Binary file not shown.

After

Width:  |  Height:  |  Size: 561 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 749 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 812 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 620 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 738 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 664 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 672 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 776 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 716 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 669 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 625 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 459 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 660 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 743 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 694 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 500 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 659 KiB

View File

@@ -1,4 +1,5 @@
import Link from "next/link" import Link from "next/link"
import Image from "next/image"
/* ── Nav ── */ /* ── Nav ── */
export function Nav() { export function Nav() {
@@ -79,7 +80,24 @@ export function BottomCta({ headline, sub }: { headline?: string; sub?: string }
) )
} }
/* ── Image Placeholder ── */ /* ── Landing Image ── */
export function LandingImage({ src, alt, aspect = "video" }: { src: string; alt: string; aspect?: "video" | "square" | "wide" }) {
const aspectClass = aspect === "square" ? "aspect-square" : aspect === "wide" ? "aspect-[2/1]" : "aspect-video"
return (
<div className={`${aspectClass} w-full relative overflow-hidden bg-gray-100`}>
<Image
src={src}
alt={alt}
fill
className="object-cover"
sizes="(max-width: 768px) 100vw, 50vw"
quality={85}
/>
</div>
)
}
/* ── Image Placeholder (kept as fallback) ── */
export function ImagePlaceholder({ aspect = "video", label }: { aspect?: "video" | "square" | "wide"; label?: string }) { export function ImagePlaceholder({ aspect = "video", label }: { aspect?: "video" | "square" | "wide"; label?: string }) {
const aspectClass = aspect === "square" ? "aspect-square" : aspect === "wide" ? "aspect-[2/1]" : "aspect-video" const aspectClass = aspect === "square" ? "aspect-square" : aspect === "wide" ? "aspect-[2/1]" : "aspect-video"
return ( return (

View File

@@ -1,5 +1,5 @@
import Link from "next/link" import Link from "next/link"
import { Nav, Footer, BottomCta, ImagePlaceholder } from "../_components" import { Nav, Footer, BottomCta, LandingImage } from "../_components"
export default function ForCharitiesPage() { export default function ForCharitiesPage() {
return ( return (
@@ -26,7 +26,7 @@ export default function ForCharitiesPage() {
</Link> </Link>
</div> </div>
</div> </div>
<ImagePlaceholder aspect="square" label="Charity manager reviewing dashboard on laptop at event" /> <LandingImage src="/images/landing/08-charities-hero.jpg" alt="Charity manager reviewing dashboard on laptop at mosque fundraising event" aspect="square" />
</div> </div>
</section> </section>
@@ -49,7 +49,7 @@ export default function ForCharitiesPage() {
</div> </div>
</section> </section>
{/* ── How it works — with image ── */} {/* ── How it works — with images ── */}
<section className="py-24 px-6"> <section className="py-24 px-6">
<div className="max-w-5xl mx-auto grid md:grid-cols-2 gap-16 items-start"> <div className="max-w-5xl mx-auto grid md:grid-cols-2 gap-16 items-start">
<div> <div>
@@ -73,8 +73,8 @@ export default function ForCharitiesPage() {
</div> </div>
</div> </div>
<div className="space-y-6"> <div className="space-y-6">
<ImagePlaceholder aspect="square" label="Mobile pledge form — amount selection" /> <LandingImage src="/images/landing/09-charities-amount-selection.jpg" alt="Hand holding phone at charity dinner table showing pledge amount selection" aspect="square" />
<ImagePlaceholder aspect="video" label="WhatsApp reminder conversation" /> <LandingImage src="/images/landing/10-charities-whatsapp.jpg" alt="Woman on sofa viewing WhatsApp pledge reminder conversation" aspect="video" />
</div> </div>
</div> </div>
</section> </section>

View File

@@ -1,5 +1,5 @@
import Link from "next/link" import Link from "next/link"
import { Nav, Footer, BottomCta, ImagePlaceholder } from "../_components" import { Nav, Footer, BottomCta, LandingImage } from "../_components"
export default function ForFundraisersPage() { export default function ForFundraisersPage() {
return ( return (
@@ -26,7 +26,7 @@ export default function ForFundraisersPage() {
</Link> </Link>
</div> </div>
</div> </div>
<ImagePlaceholder aspect="square" label="Fundraiser sharing pledge link on phone" /> <LandingImage src="/images/landing/11-fundraisers-hero.jpg" alt="Fundraiser sharing pledge link on phone with friend on London high street" aspect="square" />
</div> </div>
</section> </section>
@@ -73,8 +73,8 @@ export default function ForFundraisersPage() {
</div> </div>
</div> </div>
<div className="space-y-6"> <div className="space-y-6">
<ImagePlaceholder aspect="square" label="External redirect — branded LaunchGood handoff" /> <LandingImage src="/images/landing/12-fundraisers-redirect.jpg" alt="Hands holding phone in café showing fundraising page redirect" aspect="square" />
<ImagePlaceholder aspect="video" label="Pledge tracking dashboard conversion funnel" /> <LandingImage src="/images/landing/13-fundraisers-dashboard.jpg" alt="Laptop showing pledge tracking dashboard with conversion funnel" aspect="video" />
</div> </div>
</div> </div>
</section> </section>

View File

@@ -1,5 +1,5 @@
import Link from "next/link" import Link from "next/link"
import { Nav, Footer, BottomCta, ImagePlaceholder } from "../_components" import { Nav, Footer, BottomCta, LandingImage } from "../_components"
export default function ForOrganisationsPage() { export default function ForOrganisationsPage() {
return ( return (
@@ -26,7 +26,7 @@ export default function ForOrganisationsPage() {
</Link> </Link>
</div> </div>
</div> </div>
<ImagePlaceholder aspect="square" label="Multi-org pledge pipeline — boardroom view" /> <LandingImage src="/images/landing/17-orgs-hero-boardroom.jpg" alt="Three people in charity office meeting room discussing pledge pipeline" aspect="square" />
</div> </div>
</section> </section>
@@ -74,8 +74,8 @@ export default function ForOrganisationsPage() {
</div> </div>
</div> </div>
<div className="space-y-6"> <div className="space-y-6">
<ImagePlaceholder aspect="square" label="Pipeline view — pledges by organisation" /> <LandingImage src="/images/landing/18-orgs-pipeline.jpg" alt="Laptop showing Kanban-style pledge pipeline view by organisation" aspect="square" />
<ImagePlaceholder aspect="video" label="Instalment schedule — multi-month commitment" /> <LandingImage src="/images/landing/19-orgs-instalment-schedule.jpg" alt="Laptop showing instalment schedule with monthly progress bars" aspect="video" />
</div> </div>
</div> </div>
</section> </section>
@@ -83,7 +83,7 @@ export default function ForOrganisationsPage() {
{/* ── Why this works ── */} {/* ── Why this works ── */}
<section className="py-24 bg-gray-50 px-6"> <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="max-w-5xl mx-auto grid md:grid-cols-2 gap-12 items-center">
<ImagePlaceholder aspect="square" label="Organisation dashboard — commitment tracking" /> <LandingImage src="/images/landing/20-orgs-laptop-desk.jpg" alt="Laptop on charity office desk showing commitment tracking dashboard" aspect="square" />
<div> <div>
<h2 className="text-3xl font-black text-gray-900 tracking-tight mb-8">Spreadsheets don&apos;t send reminders</h2> <h2 className="text-3xl font-black text-gray-900 tracking-tight mb-8">Spreadsheets don&apos;t send reminders</h2>
<div className="space-y-5"> <div className="space-y-5">

View File

@@ -1,5 +1,5 @@
import Link from "next/link" import Link from "next/link"
import { Nav, Footer, ImagePlaceholder } from "../_components" import { Nav, Footer, LandingImage } from "../_components"
export default function ForVolunteersPage() { export default function ForVolunteersPage() {
return ( return (
@@ -26,7 +26,7 @@ export default function ForVolunteersPage() {
</Link> </Link>
</div> </div>
</div> </div>
<ImagePlaceholder aspect="square" label="Volunteer at event table with QR code stand" /> <LandingImage src="/images/landing/14-volunteers-hero.jpg" alt="Volunteer at charity dinner table with QR code stand" aspect="square" />
</div> </div>
</section> </section>
@@ -72,8 +72,8 @@ export default function ForVolunteersPage() {
</div> </div>
</div> </div>
<div className="space-y-6"> <div className="space-y-6">
<ImagePlaceholder aspect="square" label="Volunteer stats — live pledge counter on mobile" /> <LandingImage src="/images/landing/15-volunteers-phone-stats.jpg" alt="Volunteer holding phone showing live pledge counter at event" aspect="square" />
<ImagePlaceholder aspect="video" label="Leaderboard — top collectors" /> <LandingImage src="/images/landing/16-volunteers-leaderboard.jpg" alt="Phone showing volunteer leaderboard with rankings" aspect="video" />
</div> </div>
</div> </div>
</section> </section>

View File

@@ -1,5 +1,5 @@
import Link from "next/link" import Link from "next/link"
import { Nav, Footer, BottomCta, ImagePlaceholder } from "./for/_components" import { Nav, Footer, BottomCta, LandingImage } from "./for/_components"
const PERSONAS = [ const PERSONAS = [
{ {
@@ -7,28 +7,28 @@ const PERSONAS = [
title: "Charity Manager", title: "Charity Manager",
oneLiner: "You raise pledges at events. We make sure the money actually arrives.", oneLiner: "You raise pledges at events. We make sure the money actually arrives.",
tags: ["Dashboard", "WhatsApp reminders", "Gift Aid", "Zakat", "HMRC export"], tags: ["Dashboard", "WhatsApp reminders", "Gift Aid", "Zakat", "HMRC export"],
image: "charity-manager-gala-dinner", image: "/images/landing/02-main-charity-manager-card.jpg",
}, },
{ {
slug: "fundraisers", slug: "fundraisers",
title: "Personal Fundraiser", title: "Personal Fundraiser",
oneLiner: "You share a LaunchGood or JustGiving link. We track who actually donates.", oneLiner: "You share a LaunchGood or JustGiving link. We track who actually donates.",
tags: ["LaunchGood", "Enthuse", "JustGiving", "Social sharing", "Conversion tracking"], tags: ["LaunchGood", "Enthuse", "JustGiving", "Social sharing", "Conversion tracking"],
image: "fundraiser-sharing-phone", image: "/images/landing/03-main-fundraiser-card.jpg",
}, },
{ {
slug: "volunteers", slug: "volunteers",
title: "Volunteer", title: "Volunteer",
oneLiner: "You help collect pledges at events. We show you exactly how much you raised.", oneLiner: "You help collect pledges at events. We show you exactly how much you raised.",
tags: ["Personal link", "Live stats", "Leaderboard", "WhatsApp share"], tags: ["Personal link", "Live stats", "Leaderboard", "WhatsApp share"],
image: "volunteer-event-table", image: "/images/landing/04-main-volunteer-card.jpg",
}, },
{ {
slug: "organisations", slug: "organisations",
title: "Organisation", title: "Organisation",
oneLiner: "You coordinate pledges across multiple charities or departments. We track every commitment.", oneLiner: "You coordinate pledges across multiple charities or departments. We track every commitment.",
tags: ["Multi-party", "Fund allocation", "Pipeline view", "Instalments"], tags: ["Multi-party", "Fund allocation", "Pipeline view", "Instalments"],
image: "org-boardroom-meeting", image: "/images/landing/05-main-org-card.jpg",
}, },
] ]
@@ -62,10 +62,10 @@ export default function HomePage() {
</div> </div>
</section> </section>
{/* ── Hero image placeholder ── */} {/* ── Hero image ── */}
<section className="px-6 pb-20"> <section className="px-6 pb-20">
<div className="max-w-5xl mx-auto"> <div className="max-w-5xl mx-auto">
<ImagePlaceholder aspect="video" label="Dashboard screenshot — live pledge pipeline" /> <LandingImage src="/images/landing/01-main-dashboard-hero.jpg" alt="Charity analytics dashboard showing live pledge pipeline" aspect="video" />
</div> </div>
</section> </section>
@@ -97,7 +97,7 @@ export default function HomePage() {
href={`/for/${p.slug}`} href={`/for/${p.slug}`}
className="group block bg-white border border-gray-200 hover:border-gray-900 transition-colors" className="group block bg-white border border-gray-200 hover:border-gray-900 transition-colors"
> >
<ImagePlaceholder aspect="video" label={p.image} /> <LandingImage src={p.image} alt={p.title} aspect="video" />
<div className="p-6 space-y-3"> <div className="p-6 space-y-3">
<h3 className="text-lg font-black text-gray-900 group-hover:text-trust-blue transition-colors">{p.title}</h3> <h3 className="text-lg font-black text-gray-900 group-hover:text-trust-blue transition-colors">{p.title}</h3>
<p className="text-sm text-gray-500 leading-relaxed">{p.oneLiner}</p> <p className="text-sm text-gray-500 leading-relaxed">{p.oneLiner}</p>
@@ -142,7 +142,7 @@ export default function HomePage() {
{/* ── Split: Image + compliance ── */} {/* ── Split: Image + compliance ── */}
<section className="py-24 px-6"> <section className="py-24 px-6">
<div className="max-w-5xl mx-auto grid md:grid-cols-2 gap-12 items-center"> <div className="max-w-5xl mx-auto grid md:grid-cols-2 gap-12 items-center">
<ImagePlaceholder aspect="square" label="Pledge form Gift Aid, Zakat, consent checkboxes" /> <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 className="space-y-8">
<div> <div>
<h2 className="text-3xl font-black text-gray-900 tracking-tight">Compliance is not optional</h2> <h2 className="text-3xl font-black text-gray-900 tracking-tight">Compliance is not optional</h2>
@@ -184,7 +184,7 @@ export default function HomePage() {
</div> </div>
</div> </div>
<div className="order-1 md:order-2"> <div className="order-1 md:order-2">
<ImagePlaceholder aspect="square" label="Schedule step — now, date, monthly options" /> <LandingImage src="/images/landing/07-main-schedule-step.jpg" alt="Hands holding phone at kitchen table showing payment schedule options" aspect="square" />
</div> </div>
</div> </div>
</section> </section>