Fix payment flexibility quote length and orphan word

- Shorten quote 03 from 'Can I split it across a few months?' to 'Can I pay monthly?' for column symmetry
- Add nbsp between 'money' and 'arriving' to prevent orphan line break
This commit is contained in:
2026-03-04 13:58:42 +08:00
parent 6b71fa227b
commit ef37ca0c18
160 changed files with 6555 additions and 549 deletions

View File

@@ -0,0 +1,271 @@
/**
* Generate 30 MORE brand photography assets — Batch 2
* Run: npx tsx scripts/generate-brand-photos-2.ts
*/
import fs from "fs";
import path from "path";
import dotenv from "dotenv";
dotenv.config();
const API_KEY = process.env.GEMINI_API_KEY;
if (!API_KEY) { console.error("Missing GEMINI_API_KEY"); process.exit(1); }
const MODEL = "gemini-3-pro-image-preview";
const ENDPOINT = `https://generativelanguage.googleapis.com/v1beta/models/${MODEL}:generateContent?key=${API_KEY}`;
const OUTPUT_DIR = path.join(process.cwd(), "public/images/brand");
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
const STYLE = `Photorealistic documentary photography. Sony A7III, shallow depth of field, available light. Candid fly-on-the-wall. Nobody looks at camera. No stock aesthetic. No staged poses. No alcohol or wine ever. No visible text or watermarks. Young modern British-Muslim community. South Asian and Arab features.`;
interface Photo { filename: string; prompt: string; }
const PHOTOS: Photo[] = [
// ═══════════════════════════════════════════
// RAMADAN & IFTAR (5 photos)
// ═══════════════════════════════════════════
{
filename: "ramadan-01-iftar-table.jpg",
prompt: `16:9 landscape. A long communal iftar table in a mosque hall, moments before breaking fast. Dozens of place settings — each with a paper plate, three dates, a cup of water, and a samosa. The table stretches into the distance. A few early arrivals are seated, hands in dua (prayer), eyes closed. Late afternoon golden light through high windows. The anticipation of iftar. 35mm f/2.8. ${STYLE}`,
},
{
filename: "ramadan-02-breaking-fast.jpg",
prompt: `16:9 landscape. The exact moment of breaking fast at a community iftar. Close-up of a young British-Muslim man biting into a date, eyes half-closed with relief and gratitude. Beside him, another person is drinking water. Paper plates with samosas and fruit. The table is packed with people shoulder to shoulder. Warm overhead light. The beautiful ordinariness of this sacred moment. 85mm f/1.4. ${STYLE}`,
},
{
filename: "ramadan-03-taraweeh-crowd.jpg",
prompt: `16:9 landscape. Overhead angle looking down on rows and rows of men in sujood (prostration) during taraweeh prayers in a packed mosque. White, grey, and coloured thobes and prayer caps visible from above. Deep green carpet with geometric pattern. The symmetry is breathtaking. Shot from a balcony above. Warm mosque lighting. 24mm f/2.8. ${STYLE}`,
},
{
filename: "ramadan-04-suhoor-kitchen.jpg",
prompt: `16:9 landscape. 3am suhoor in a British-Muslim family kitchen. A mother in a dressing gown and loose hijab is making eggs at the stove while a teenage son sits at the kitchen table eating cereal, still half-asleep. A clock on the wall shows 3:15. The kitchen is lit by the warm glow of a single pendant light, darkness visible through the window. The quiet ritual of pre-dawn eating. 35mm f/2.0. ${STYLE}`,
},
{
filename: "ramadan-05-charity-collection.jpg",
prompt: `16:9 landscape. Outside a mosque after taraweeh prayer, a young British-Muslim volunteer in a charity bib holding a collection bucket. People are filing out of the mosque entrance, some dropping notes and coins in as they pass. The volunteer is saying thank you to an older man who just donated. Night time, the mosque entrance is warmly lit, the street is dark. 50mm f/1.8. ${STYLE}`,
},
// ═══════════════════════════════════════════
// OUTDOOR FUNDRAISING (5 photos)
// ═══════════════════════════════════════════
{
filename: "outdoor-01-sponsored-walk.jpg",
prompt: `16:9 landscape. A group of 8-10 young British-Muslim men and women on a charity sponsored walk through a park. They're in matching charity t-shirts, some in hijabs, walking together on a path with autumn trees. One person is holding a small charity banner. They're mid-conversation, laughing, energetic. Overcast British sky. Other park walkers in the background. The camaraderie of collective effort. 35mm f/2.8. ${STYLE}`,
},
{
filename: "outdoor-02-street-collection.jpg",
prompt: `16:9 landscape. A young British-Muslim woman in hijab and a charity tabard standing on a busy British high street with a collection bucket and clipboard. She's approaching a passer-by with a friendly smile. Behind her: a Boots pharmacy, a bus stop, and overcast sky. Red double-decker bus partially visible. The courage of street fundraising. Shallow depth of field on her. 85mm f/1.8. ${STYLE}`,
},
{
filename: "outdoor-03-funrun-finish.jpg",
prompt: `16:9 landscape. A young British-Muslim woman in hijab crossing the finish line of a charity fun run, arms spread wide in triumph. She's wearing a running vest with a race number pinned to it. Her face shows exhausted joy. Spectators on either side cheering. A finish line banner overhead. Other runners behind her. Morning light. The physical effort of giving. 85mm f/1.4 with motion blur on the background. ${STYLE}`,
},
{
filename: "outdoor-04-cake-sale.jpg",
prompt: `16:9 landscape. A charity bake sale table outside a community centre. Two young British-Muslim women, one in hijab, arranging homemade cakes, cupcakes, and brownies on a trestle table with a paper tablecloth. Price labels handwritten on cards. A small queue of people waiting. A child pointing at a chocolate cake. Overcast daylight. The grassroots simplicity of community fundraising. 35mm f/2.0. ${STYLE}`,
},
{
filename: "outdoor-05-carwash-fundraiser.jpg",
prompt: `16:9 landscape. Young British-Muslim volunteers doing a charity car wash in a mosque car park. Three teenagers in matching t-shirts, soaking wet and laughing, washing a silver car with sponges and buckets. Soapy water on the tarmac. A handwritten "Charity Car Wash £5" sign on cardboard. Another car waiting behind. Overcast British summer day. Pure joy and chaos. 35mm f/2.8. ${STYLE}`,
},
// ═══════════════════════════════════════════
// IMPACT & GRATITUDE (5 photos)
// ═══════════════════════════════════════════
{
filename: "impact-01-food-bank.jpg",
prompt: `16:9 landscape. Inside a community food bank in a mosque basement. Metal shelving units stacked with tinned food, rice bags, and pasta. A young British-Muslim volunteer is handing a filled carrier bag to an older woman. Both of them have quiet, dignified expressions — no pity, no performance. Fluorescent overhead light. A weighing scale on the counter. The serious work of feeding people. 35mm f/2.0. ${STYLE}`,
},
{
filename: "impact-02-thank-you-letter.jpg",
prompt: `3:4 portrait. Close-up of a handwritten thank-you card lying on a desk, partially visible. A young British-Muslim charity worker's hand is holding it, reading it. We see the card from a slight angle — a child's drawing and handwriting visible but not fully legible. The person's other hand rests on the desk near a mug of tea. Warm desk lamp light. The quiet reward of charity work. 50mm macro f/2.8. ${STYLE}`,
},
{
filename: "impact-03-cheque-presentation.jpg",
prompt: `16:9 landscape. A small informal cheque presentation in a charity office. A young British-Muslim fundraiser handing over a large novelty cheque to the charity director — both in smart-casual clothes, shaking hands with one hand and holding the cheque between them. Two other team members behind them, clapping. A plain office wall with a charity logo framed print. Not a staged PR shot — a genuine moment. Overhead fluorescent light. 35mm f/2.8. ${STYLE}`,
},
{
filename: "impact-04-building-project.jpg",
prompt: `16:9 landscape. A group of British-Muslim community members standing in front of a partially renovated community building, looking up at the progress. Hard hats on some of them, high-vis vests. Scaffolding on the building facade. One person is pointing upward at the work. A sense of pride and progress. Overcast sky. The tangible result of fundraising — bricks and mortar. 24mm f/4.0. ${STYLE}`,
},
{
filename: "impact-05-classroom-abroad.jpg",
prompt: `16:9 landscape. A video call on a laptop screen showing children in a classroom abroad, waving at the camera. The laptop is on a desk in a British charity office. A young British-Muslim woman in hijab is sitting in front of the laptop, hand raised waving back, with a huge genuine smile. The connection between giver and receiver, bridged by technology. Warm screen glow mixed with office fluorescent. 50mm f/2.0. ${STYLE}`,
},
// ═══════════════════════════════════════════
// YOUTH & NEXT GENERATION (5 photos)
// ═══════════════════════════════════════════
{
filename: "youth-01-workshop.jpg",
prompt: `16:9 landscape. A charity leadership workshop in a community centre. Young British-Muslim attendees, late teens and early 20s, sitting at desks arranged in a U-shape. A facilitator at a flipchart. One young woman in hijab is asking a question with her hand half-raised. Post-it notes on the wall behind. Notebooks and water bottles on desks. Fluorescent light. Engaged, not passive. 24mm f/2.8. ${STYLE}`,
},
{
filename: "youth-02-mentoring.jpg",
prompt: `16:9 landscape. A one-on-one mentoring session in a quiet corner of a community centre. An older British-Muslim man in his 40s, beard with flecks of grey, sitting across from a teenage boy. The man is leaning forward, hands clasped, listening intently. The teenager is speaking, gesturing. Between them on a small table: two cups of tea. A window with grey sky behind. The gift of time and attention. 85mm f/1.4. ${STYLE}`,
},
{
filename: "youth-03-childrens-class.jpg",
prompt: `16:9 landscape. A weekend Islamic school class in a community centre. Children aged 7-10 sitting on the floor in a semi-circle, some in traditional clothing. A young female teacher in hijab sitting at their level, holding up a picture book. The children are captivated. Colourful wall displays behind — children's artwork, Arabic alphabet posters. Bright fluorescent light. The investment in the next generation. 35mm f/2.0. ${STYLE}`,
},
{
filename: "youth-04-uni-stall.jpg",
prompt: `16:9 landscape. A charity fundraising stall at a British university freshers' fair. Two young British-Muslim students manning the stall — a man in a hoodie and a woman in hijab. Their table has leaflets, a banner, collection tins, and charity wristbands. A fresher student has stopped to look at a leaflet. Busy indoor hall with other stalls blurred behind. Bright harsh indoor lighting. The start of a volunteer journey. 35mm f/2.8. ${STYLE}`,
},
{
filename: "youth-05-award-ceremony.jpg",
prompt: `16:9 landscape. A young British-Muslim woman in hijab receiving a community volunteer award on a small stage. She's holding a framed certificate, looking down at it with a humble smile. A small audience of community members clapping in the foreground, some holding phones up. A simple stage setup — a table, a microphone, a charity banner. Community hall setting. The recognition of quiet dedication. 85mm f/1.8. ${STYLE}`,
},
// ═══════════════════════════════════════════
// MODERN CHARITY WORK (5 photos)
// ═══════════════════════════════════════════
{
filename: "modern-01-podcast-recording.jpg",
prompt: `16:9 landscape. Two young British-Muslim men recording a charity podcast in a makeshift studio — a small room with foam panels on the wall. Both wearing headphones, sitting at a desk with microphones on boom arms. One is mid-sentence, gesturing. Laptops open in front of them (screens not visible). A charity logo sticker on one of the laptops. Warm desk lamp mixed with overhead light. The new age of charity storytelling. 35mm f/2.0. ${STYLE}`,
},
{
filename: "modern-02-coworking-charity.jpg",
prompt: `16:9 landscape. Young British-Muslim charity workers in a modern coworking space. Three people at a long shared desk — one woman in hijab on a video call (laptop screen not visible), one man typing on a keyboard, another reviewing printed spreadsheets. Plants on the desk, coffee cups, a branded charity water bottle. Large windows with grey London sky. The professionalisation of small charities. 35mm f/2.8. ${STYLE}`,
},
{
filename: "modern-03-graphic-design.jpg",
prompt: `3:4 portrait. Over-the-shoulder shot of a young British-Muslim man at a desk, working on charity marketing materials on a large monitor (screen content blurred/not readable). His hand is on a mouse. Beside the monitor: a mood board pinned to a corkboard with colour swatches, logo printouts, and inspiration photos. A graphics tablet beside the keyboard. Evening, dark window reflecting the screen. Creative charity work. 50mm f/2.0. ${STYLE}`,
},
{
filename: "modern-04-whatsapp-group.jpg",
prompt: `3:4 portrait. Close-up of a young British-Muslim man's hands holding a phone. He's sitting on a London Underground tube train (visible through the window: dark tunnel, orange handrails). The phone screen shows a messaging app with multiple unread messages (screen content blurred, not readable). He's mid-scroll with his thumb. Rush hour — other commuters blurred. The always-on reality of charity coordination. 85mm f/1.4. ${STYLE}`,
},
{
filename: "modern-05-zoom-trustees.jpg",
prompt: `16:9 landscape. A laptop on a dining table at home showing a video call grid of 6 people — a charity trustees meeting (faces small and blurred, not identifiable). A young British-Muslim woman in hijab sits in front of the laptop, her notebook open beside it with handwritten meeting notes. A cup of tea, a pen. Evening — warm pendant light above, dark window behind. The unglamorous governance of charity. 50mm f/2.0. ${STYLE}`,
},
// ═══════════════════════════════════════════
// TEXTURE & DETAIL SHOTS (5 photos)
// ═══════════════════════════════════════════
{
filename: "detail-01-donation-jar.jpg",
prompt: `3:4 portrait. Close-up of a glass donation jar on a shop counter, half-full of coins and a few folded notes. The jar has a simple printed label saying "Charity" (no other text). Behind it, blurred: the warm interior of a small British-Asian grocery shop — shelves of spices, packets of lentils. The shopkeeper's hand is just visible dropping a coin in. Warm tungsten light. The everyday generosity of small business. 85mm macro f/2.0. ${STYLE}`,
},
{
filename: "detail-02-prayer-beads.jpg",
prompt: `3:4 portrait. Close-up of an elderly man's weathered hands holding wooden prayer beads (tasbih), resting on his knee. He's sitting in a mosque after prayer. His white thobe sleeve is visible. The green mosque carpet blurred beneath. Soft natural light from a nearby window. A lifetime of faith in two hands. 85mm macro f/1.4. ${STYLE}`,
},
{
filename: "detail-03-shoes-mosque.jpg",
prompt: `16:9 landscape. Dozens of pairs of shoes arranged on shelving racks outside a mosque prayer hall entrance. Trainers, dress shoes, sandals, children's shoes — all mixed together. The mosque doorway is visible with warm light inside. A pair of tiny children's wellies sits among the adult shoes. The diversity of a congregation told through their footwear. 35mm f/2.8. ${STYLE}`,
},
{
filename: "detail-04-fundraising-thermometer.jpg",
prompt: `3:4 portrait. A hand-painted fundraising thermometer chart on a large piece of card, mounted on a community centre wall. The "mercury" is painted red and filled to about 75% of the target. At the top: a target amount (numbers blurred). Around the base of the chart: small stars with donor names (not readable). Blu-tack marks on the wall around it. Fluorescent light. The lo-fi optimism of community fundraising. 50mm f/2.8. ${STYLE}`,
},
{
filename: "detail-05-langar-trays.jpg",
prompt: `16:9 landscape. Overhead shot of a long table being set with food for a community meal. Foil trays of biryani, large bowls of salad, stacks of naan bread, jugs of water, and paper plates. Hands from multiple people are visible placing items. The abundance and care of communal feeding. Shot from directly above. Bright fluorescent community hall light. 24mm f/4.0 from above. ${STYLE}`,
},
];
// ─── CONCURRENT GENERATION ENGINE ──────────────────────────
async function generateOne(spec: Photo): Promise<boolean> {
const outPath = path.join(OUTPUT_DIR, spec.filename);
const t0 = Date.now();
try {
const res = await fetch(ENDPOINT, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
contents: [{ parts: [{ text: `Generate a photorealistic photograph. ${spec.prompt}` }] }],
generationConfig: { responseModalities: ["IMAGE", "TEXT"] },
}),
});
if (!res.ok) {
const err = await res.text();
console.error(`${spec.filename} — API ${res.status}: ${err.slice(0, 150)}`);
return false;
}
const data: any = await res.json();
const parts = data.candidates?.[0]?.content?.parts;
const imgPart = parts?.find((p: any) => p.inlineData?.mimeType?.startsWith("image/"));
if (!imgPart) {
const textPart = parts?.find((p: any) => p.text);
console.error(`${spec.filename} — No image${textPart ? ": " + textPart.text.slice(0, 100) : ""}`);
return false;
}
const buf = Buffer.from(imgPart.inlineData.data, "base64");
fs.writeFileSync(outPath, buf);
const ms = Date.now() - t0;
console.log(`${spec.filename}${(buf.length / 1024).toFixed(0)}KB (${(ms / 1000).toFixed(1)}s)`);
return true;
} catch (e: any) {
console.error(`${spec.filename}${e.message}`);
return false;
}
}
async function main() {
const BATCH_SIZE = 10;
const batches: Photo[][] = [];
for (let i = 0; i < PHOTOS.length; i += BATCH_SIZE) {
batches.push(PHOTOS.slice(i, i + BATCH_SIZE));
}
console.log("═══════════════════════════════════════════════════════");
console.log(" PNPL Brand Photography — Batch 2 (30 more)");
console.log(` Model: ${MODEL}`);
console.log(` Strategy: ${batches.length} batches × ${BATCH_SIZE} concurrent`);
console.log(` Output: ${OUTPUT_DIR}`);
console.log("═══════════════════════════════════════════════════════");
const t0 = Date.now();
let success = 0;
let failed: Photo[] = [];
for (let i = 0; i < batches.length; i++) {
console.log(`\n⚡ Batch ${i + 1}/${batches.length} — firing ${batches[i].length} requests simultaneously...`);
const results = await Promise.allSettled(batches[i].map(p => generateOne(p)));
const batchSuccess = results.filter(r => r.status === "fulfilled" && r.value).length;
success += batchSuccess;
// Track failures
for (const spec of batches[i]) {
if (!fs.existsSync(path.join(OUTPUT_DIR, spec.filename))) {
failed.push(spec);
}
}
if (i < batches.length - 1) {
console.log(` ⏳ 2s cooldown...`);
await new Promise(r => setTimeout(r, 2000));
}
}
// Retry failures
if (failed.length > 0) {
console.log(`\n🔄 Retrying ${failed.length} failures...`);
await new Promise(r => setTimeout(r, 3000));
for (const spec of failed) {
const ok = await generateOne(spec);
if (ok) success++;
await new Promise(r => setTimeout(r, 1500));
}
}
const elapsed = ((Date.now() - t0) / 1000).toFixed(1);
console.log("\n═══════════════════════════════════════════════════════");
console.log(` Done: ${success}/${PHOTOS.length} photos in ${elapsed}s`);
console.log(` Total brand library: 60 photos`);
console.log(` Output: ${OUTPUT_DIR}`);
console.log("═══════════════════════════════════════════════════════");
}
main();

View File

@@ -0,0 +1,275 @@
/**
* Generate 30 MORE brand photography assets — Batch 3
* Filling gaps: everyday life, seasons, work, sport, intergenerational, atmosphere
* Run: npx tsx scripts/generate-brand-photos-3.ts
*/
import fs from "fs";
import path from "path";
import dotenv from "dotenv";
dotenv.config();
const API_KEY = process.env.GEMINI_API_KEY;
if (!API_KEY) { console.error("Missing GEMINI_API_KEY"); process.exit(1); }
const MODEL = "gemini-3-pro-image-preview";
const ENDPOINT = `https://generativelanguage.googleapis.com/v1beta/models/${MODEL}:generateContent?key=${API_KEY}`;
const OUTPUT_DIR = path.join(process.cwd(), "public/images/brand");
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
const STYLE = `Photorealistic documentary photography. Sony A7III, shallow depth of field, available light. Candid fly-on-the-wall. Nobody looks at camera. No stock aesthetic. No staged poses. No alcohol or wine ever. No visible watermarks. Young modern British-Muslim community. South Asian and Arab features.`;
interface Photo { filename: string; prompt: string; }
const PHOTOS: Photo[] = [
// ═══════════════════════════════════════════
// EVERYDAY BRITISH LIFE (6 photos)
// ═══════════════════════════════════════════
{
filename: "everyday-01-barbershop.jpg",
prompt: `16:9 landscape. Interior of a Turkish/Arab barbershop on a British high street. A young British-Muslim barber in a black apron is carefully trimming a young man's beard with clippers. The client is draped in a black cape, eyes down at his phone. Mirror reflection shows shelves of hair products, a small TV on the wall showing football. Warm halogen spotlights. The barbershop as a gathering place — another man waits on a leather sofa reading a newspaper. 35mm f/2.0. ${STYLE}`,
},
{
filename: "everyday-02-corner-shop.jpg",
prompt: `16:9 landscape. Interior of a British-Asian corner shop. An older Bangladeshi shopkeeper behind the counter, grey stubble, reading glasses pushed up on his forehead, handing change to a customer. Behind him: shelves of sweets, crisps, lottery scratchcard display, a charity collection tin by the till. A young child is reaching for something on a low shelf. The shop door is open showing a wet pavement outside. Fluorescent strip light. The heart of a neighbourhood. 35mm f/2.8. ${STYLE}`,
},
{
filename: "everyday-03-school-gate.jpg",
prompt: `16:9 landscape. School pick-up time outside a British primary school. A group of British-Muslim mothers in hijabs standing by the school gate, chatting while waiting. One is pushing a buggy, another holds a toddler on her hip. Children in school uniform are starting to stream out through the gate. Terraced houses across the road. Overcast afternoon. Yellow lollipop crossing sign visible. The daily ritual. 35mm f/2.8. ${STYLE}`,
},
{
filename: "everyday-04-nhs-nurse.jpg",
prompt: `3:4 portrait. A young British-Muslim woman in NHS nurse scrubs and a hijab, sitting on a bench outside a hospital building during a break. She's holding a takeaway coffee cup with both hands, staring into the middle distance with tired but peaceful eyes. Her hospital ID badge hangs from a lanyard. An ambulance is blurred in the background. Overcast grey light. The quiet exhaustion of someone who serves in every part of life. 85mm f/1.4. ${STYLE}`,
},
{
filename: "everyday-05-taxi-driver.jpg",
prompt: `16:9 landscape. A British-Muslim taxi driver, 40s, short beard, sitting in his parked black cab at night, filling in a logbook on the steering wheel. The dashboard is glowing green. Through the windshield: a rainy British high street with blurred neon shop signs and streetlights reflected in puddles. A prayer bead tasbih hangs from the rear-view mirror. The solitude of night-shift work. 35mm f/1.8. ${STYLE}`,
},
{
filename: "everyday-06-graduation.jpg",
prompt: `16:9 landscape. A young British-Muslim woman in graduation cap, gown, and hijab, walking across a university quad with her family. Her father in a suit is carrying her bouquet, her mother in a colourful hijab is wiping happy tears, a younger sibling is taking photos on a phone. The university's stone buildings behind them. Autumn light, scattered leaves. The pride of a whole family's sacrifice. 50mm f/2.0. ${STYLE}`,
},
// ═══════════════════════════════════════════
// SPORT & FITNESS (4 photos)
// ═══════════════════════════════════════════
{
filename: "sport-01-football.jpg",
prompt: `16:9 landscape. A five-a-side football match on an outdoor artificial pitch with floodlights. A team of young British-Muslim men in matching bibs mid-game — one player is about to shoot, another defending. In the background behind the cage fence: a group of friends watching, some in thobes having come straight from the mosque. Evening, floodlit, breath visible in cold air. The universal language of a kick-about. 85mm f/1.8. ${STYLE}`,
},
{
filename: "sport-02-sisters-gym.jpg",
prompt: `16:9 landscape. A sisters-only fitness class in a community centre hall. A young British-Muslim female instructor in a sports hijab and athletic wear leading a group of women in a circuits class. Some in hijabs, some without (private space). Gym mats on the wooden floor, a speaker playing music (small bluetooth speaker on the floor). Everyone mid-exercise, focused. Bright fluorescent hall light. The empowerment of movement. 35mm f/2.8. ${STYLE}`,
},
{
filename: "sport-03-cricket-park.jpg",
prompt: `16:9 landscape. A casual cricket game in a British public park on a summer afternoon. A young British-Muslim batsman in shalwar kameez hitting a tennis ball with a cricket bat. The bowler, in jeans and a t-shirt, mid-delivery. A few fielders spread across the grass, some in thobes. Families on picnic blankets in the background. Lush green grass, scattered clouds, warm summer light. Sunday afternoon perfection. 50mm f/2.8. ${STYLE}`,
},
{
filename: "sport-04-boxing-gym.jpg",
prompt: `16:9 landscape. A gritty boxing gym. A young British-Muslim man, early 20s, hitting a heavy bag, sweat flying. His trainer, an older man in a tracksuit, stands behind the bag steadying it, shouting encouragement. Worn red boxing gloves. Concrete walls, peeling posters of old fighters, ropes hanging. Harsh overhead strip light. The discipline and escape of the boxing gym. 85mm f/1.4. ${STYLE}`,
},
// ═══════════════════════════════════════════
// SPIRITUAL & SACRED (5 photos)
// ═══════════════════════════════════════════
{
filename: "sacred-01-wudu.jpg",
prompt: `16:9 landscape. A mosque wudu (ablution) area. A row of men at stainless steel washing stations, performing wudu before prayer. Water running over hands and forearms. Tiled walls, a drain channel on the floor. One man's face in profile, eyes closed, water dripping from his beard. The meditative ritual of preparation for prayer. Bright overhead light reflecting off wet tiles. 50mm f/2.0. ${STYLE}`,
},
{
filename: "sacred-02-quran-recitation.jpg",
prompt: `3:4 portrait. A young British-Muslim boy, about 10, sitting cross-legged on the floor of a mosque, a wooden Quran stand (rehal) in front of him, reciting from an open Quran. His finger follows the Arabic text. He's wearing a white topi. Soft natural light from a window falls across the page. Other children studying blurred in the background. The ancient discipline of learning. 85mm f/1.4. ${STYLE}`,
},
{
filename: "sacred-03-dua-hands.jpg",
prompt: `3:4 portrait. Close-up of two cupped hands raised in dua (supplication) — palms open, facing upward. The hands belong to a young man, visible from the wrists up only. Behind the hands, completely blurred: the warm interior of a mosque. Soft diffused light. The vulnerability and hope of asking. Minimal, powerful, iconic. 85mm macro f/1.4. ${STYLE}`,
},
{
filename: "sacred-04-night-mosque.jpg",
prompt: `16:9 landscape. Exterior of a British mosque at night. The building is modestly lit — warm light glowing through arched windows, a green neon crescent on the minaret. Wet pavement reflecting the lights. A couple of men walking toward the entrance for night prayer. Terraced houses dark on either side. A street lamp. The mosque as a beacon in the night. 24mm f/2.8 long exposure feel. ${STYLE}`,
},
{
filename: "sacred-05-calligraphy.jpg",
prompt: `3:4 portrait. Close-up of a young British-Muslim woman's hand practising Arabic calligraphy with a bamboo reed pen and black ink. She's writing bismillah on cream-coloured paper. Ink pot to the side. The lettering is beautiful, flowing. Her other hand steadies the paper. Shot from above at an angle. A desk lamp illuminating the work. The art of sacred writing. 50mm macro f/2.8. ${STYLE}`,
},
// ═══════════════════════════════════════════
// SEASONS & WEATHER (4 photos)
// ═══════════════════════════════════════════
{
filename: "season-01-winter-coats.jpg",
prompt: `16:9 landscape. A charity winter coat drive in a community centre. Racks of donated coats and jackets sorted by size. A young British-Muslim volunteer helping an older woman try on a warm parka. Children's coats hung low on a separate rail. A sign says "Free — Take What You Need" (handwritten). Other people browsing the racks. Fluorescent light, community hall feel. Practical compassion in January. 35mm f/2.0. ${STYLE}`,
},
{
filename: "season-02-rainy-commute.jpg",
prompt: `16:9 landscape. A rainy Monday morning on a British high street. A young British-Muslim woman in hijab and a trench coat walking briskly under an umbrella, splashing through puddles. She's carrying a laptop bag. Red double-decker bus passing behind her, headlights on. Other commuters with umbrellas. Wet reflections everywhere. The atmosphere of a British autumn morning. Shot through rain. 85mm f/1.8. ${STYLE}`,
},
{
filename: "season-03-park-summer.jpg",
prompt: `16:9 landscape. A British-Muslim family picnic in a London park on a warm summer day. A family of five on a large blanket — mother in a floral hijab unpacking tupperware containers, father pouring juice, three children running nearby on the grass. Samosas and fruit visible in the tupperware. Other park-goers in the background. Dappled sunlight through trees. The contentment of a simple day out. 35mm f/2.8. ${STYLE}`,
},
{
filename: "season-04-snow-mosque.jpg",
prompt: `16:9 landscape. A British mosque covered in fresh snow. The dome and minaret dusted white. A single set of footprints leads through the snow to the entrance. Early morning blue light before sunrise. The mosque's warm golden light glows through the windows. Terraced rooftops with snow. Chimney smoke rising. The serenity of a winter fajr. 24mm f/4.0. ${STYLE}`,
},
// ═══════════════════════════════════════════
// CHARITY LOGISTICS (3 photos)
// ═══════════════════════════════════════════
{
filename: "logistics-01-warehouse.jpg",
prompt: `16:9 landscape. Inside a charity aid warehouse. Metal shelving stacked high with cardboard boxes of donations. A young British-Muslim volunteer in a high-vis vest using a pallet truck to move a stack of boxes. Another volunteer with a clipboard checking inventory. Industrial strip lighting. Concrete floor. The scale of organised giving. 24mm f/2.8. ${STYLE}`,
},
{
filename: "logistics-02-loading-van.jpg",
prompt: `16:9 landscape. A white charity van with its back doors open in a community centre car park. Three young British-Muslim volunteers forming a chain, passing boxes from a doorway into the van. One inside the van stacking, one at the door passing, one carrying from inside. Early morning light, breath visible. A sense of purpose and teamwork. 35mm f/2.8. ${STYLE}`,
},
{
filename: "logistics-03-sorting-clothes.jpg",
prompt: `16:9 landscape. A clothing donation sorting session in a mosque basement. Long trestle tables covered in donated clothes being sorted into piles by size and condition. Five or six British-Muslim volunteers — mix of ages, women in hijabs and men — folding and sorting methodically. Black bin bags of unsorted donations on the floor. Overhead strip light. The labour of generosity. 35mm f/2.0. ${STYLE}`,
},
// ═══════════════════════════════════════════
// INTERGENERATIONAL (4 photos)
// ═══════════════════════════════════════════
{
filename: "intergen-01-grandma-cooking.jpg",
prompt: `16:9 landscape. A British-Pakistani grandmother in a traditional shalwar kameez and dupatta teaching her teenage granddaughter to make roti in a home kitchen. The grandmother's wrinkled hands are pressing and shaping the dough, the granddaughter watches closely, floury hands hovering. A tawa (flat pan) on the stove with a roti cooking. The kitchen is modest — patterned tiles, spice rack, Islamic calendar on the wall. Warm pendant light. Knowledge passed through hands. 50mm f/1.8. ${STYLE}`,
},
{
filename: "intergen-02-walking-together.jpg",
prompt: `16:9 landscape. An elderly British-Muslim man in a long coat and topi walking slowly along a suburban pavement, his young grandson (about 7) holding his hand. Shot from behind. The boy is looking up at his grandfather. Autumn leaves on the ground. Semi-detached houses with bay windows on either side. Weak afternoon sun. The tenderness of a small hand in a large one. 85mm f/1.8. ${STYLE}`,
},
{
filename: "intergen-03-tech-help.jpg",
prompt: `16:9 landscape. A teenage British-Muslim girl in hijab sitting beside her grandfather on a sofa, helping him with something on a tablet (screen not visible). She's pointing at the screen, explaining patiently. He's squinting through reading glasses, leaning in. A cup of chai on the coffee table, an Urdu newspaper folded beside it. The living room has a prayer mat rolled in the corner and family photos on a shelf. Warm lamp light. Patience across generations. 50mm f/2.0. ${STYLE}`,
},
{
filename: "intergen-04-mosque-elders.jpg",
prompt: `16:9 landscape. The social area of a mosque after Friday prayer. A circle of elderly British-Muslim men sitting on plastic chairs, drinking tea from paper cups, deep in conversation. One man is gesturing emphatically, another is laughing. In the background, young men in hoodies are leaving, heading out the door. Two worlds, one space. The mosque as living room. Fluorescent light, simple décor. 35mm f/2.0. ${STYLE}`,
},
// ═══════════════════════════════════════════
// ATMOSPHERE & CINEMATIC (4 photos)
// ═══════════════════════════════════════════
{
filename: "atmos-01-whitechapel-night.jpg",
prompt: `16:9 landscape. Whitechapel Road, East London at night. The neon signs of curry houses, fabric shops, and sweet shops glowing in Bengali and English. A few pedestrians — a man in a thobe, a woman with shopping bags, teenagers on bikes. Wet pavement reflecting the colours. The East London Mosque just visible in the distance. The electric energy of a British-Muslim high street after dark. 35mm f/1.8. ${STYLE}`,
},
{
filename: "atmos-02-dawn-call.jpg",
prompt: `16:9 landscape. Pre-dawn blue hour in a British city. Silhouette of a mosque minaret against a deep blue sky with the first pink glow on the horizon. Terraced rooftop chimneys in silhouette. A few lit windows in the houses below — people waking for fajr. A single bird on a wire. The stillness of a city between sleep and prayer. 50mm f/2.8 with deep color. ${STYLE}`,
},
{
filename: "atmos-03-market-day.jpg",
prompt: `16:9 landscape. A bustling outdoor market in a British-Muslim neighbourhood. Stalls selling fruit, vegetables, fabric, and household goods. A young British-Muslim woman in hijab examining mangoes at a fruit stall. The stallholder, an older man in a woollen hat, is weighing something on a hanging scale. Colourful awnings, handwritten price signs. Other shoppers with bags. Overcast daylight. The sensory richness of community commerce. 35mm f/2.8. ${STYLE}`,
},
{
filename: "atmos-04-library-study.jpg",
prompt: `16:9 landscape. A British public library. A young British-Muslim woman in hijab studying at a table piled with textbooks and notebooks. She's resting her chin on her hand, deep in thought, pen poised. Beside her, a non-Muslim student also studying — diverse but natural, not posed. Floor-to-ceiling bookshelves behind them. Warm reading lamp on the table mixed with overhead fluorescent. The democracy of a public library. 50mm f/2.0. ${STYLE}`,
},
];
// ─── CONCURRENT GENERATION ENGINE ──────────────────────────
async function generateOne(spec: Photo): Promise<boolean> {
const outPath = path.join(OUTPUT_DIR, spec.filename);
const t0 = Date.now();
try {
const res = await fetch(ENDPOINT, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
contents: [{ parts: [{ text: `Generate a photorealistic photograph. ${spec.prompt}` }] }],
generationConfig: { responseModalities: ["IMAGE", "TEXT"] },
}),
});
if (!res.ok) {
const err = await res.text();
console.error(`${spec.filename} — API ${res.status}: ${err.slice(0, 150)}`);
return false;
}
const data: any = await res.json();
const parts = data.candidates?.[0]?.content?.parts;
const imgPart = parts?.find((p: any) => p.inlineData?.mimeType?.startsWith("image/"));
if (!imgPart) {
const textPart = parts?.find((p: any) => p.text);
console.error(`${spec.filename} — No image${textPart ? ": " + textPart.text.slice(0, 100) : ""}`);
return false;
}
const buf = Buffer.from(imgPart.inlineData.data, "base64");
fs.writeFileSync(outPath, buf);
const ms = Date.now() - t0;
console.log(`${spec.filename}${(buf.length / 1024).toFixed(0)}KB (${(ms / 1000).toFixed(1)}s)`);
return true;
} catch (e: any) {
console.error(`${spec.filename}${e.message}`);
return false;
}
}
async function main() {
const BATCH_SIZE = 5; // smaller batches to avoid 429s
const batches: Photo[][] = [];
for (let i = 0; i < PHOTOS.length; i += BATCH_SIZE) {
batches.push(PHOTOS.slice(i, i + BATCH_SIZE));
}
console.log("═══════════════════════════════════════════════════════");
console.log(" PNPL Brand Photography — Batch 3 (30 more)");
console.log(` Model: ${MODEL}`);
console.log(` Strategy: ${batches.length} batches × ${BATCH_SIZE} concurrent`);
console.log(` Output: ${OUTPUT_DIR}`);
console.log("═══════════════════════════════════════════════════════");
const t0 = Date.now();
let success = 0;
let failed: Photo[] = [];
for (let i = 0; i < batches.length; i++) {
console.log(`\n⚡ Batch ${i + 1}/${batches.length} — firing ${batches[i].length} requests...`);
const results = await Promise.allSettled(batches[i].map(p => generateOne(p)));
const batchSuccess = results.filter(r => r.status === "fulfilled" && r.value).length;
success += batchSuccess;
for (const spec of batches[i]) {
if (!fs.existsSync(path.join(OUTPUT_DIR, spec.filename))) {
failed.push(spec);
}
}
if (i < batches.length - 1) {
console.log(` ⏳ 3s cooldown...`);
await new Promise(r => setTimeout(r, 3000));
}
}
// Retry failures with longer delays
if (failed.length > 0) {
console.log(`\n🔄 Retrying ${failed.length} failures (5s between each)...`);
await new Promise(r => setTimeout(r, 5000));
for (const spec of failed) {
const ok = await generateOne(spec);
if (ok) success++;
await new Promise(r => setTimeout(r, 5000));
}
}
const elapsed = ((Date.now() - t0) / 1000).toFixed(1);
console.log("\n═══════════════════════════════════════════════════════");
console.log(` Done: ${success}/${PHOTOS.length} photos in ${elapsed}s`);
console.log(` Total brand library: 90 photos`);
console.log(` Output: ${OUTPUT_DIR}`);
console.log("═══════════════════════════════════════════════════════");
}
main();

View File

@@ -0,0 +1,109 @@
/**
* Retry remaining 19 product photos — sequential with 8s delays
* Run: npx tsx scripts/generate-brand-photos-4-retry.ts
*/
import fs from "fs";
import path from "path";
import dotenv from "dotenv";
dotenv.config();
const API_KEY = process.env.GEMINI_API_KEY;
if (!API_KEY) { console.error("Missing GEMINI_API_KEY"); process.exit(1); }
const MODEL = "gemini-3-pro-image-preview";
const ENDPOINT = `https://generativelanguage.googleapis.com/v1beta/models/${MODEL}:generateContent?key=${API_KEY}`;
const OUTPUT_DIR = path.join(process.cwd(), "public/images/brand");
const STYLE = `Photorealistic documentary photography. Sony A7III, shallow depth of field, available light. Candid fly-on-the-wall. Nobody looks at camera. No stock aesthetic. No staged poses. No alcohol or wine ever. No visible watermarks. Young modern British-Muslim community. South Asian and Arab features.`;
interface Photo { filename: string; prompt: string; }
const ALL_PHOTOS: Photo[] = [
{ filename: "product-pledge-05-confirmation-smile.jpg", prompt: `3:4 portrait. A young British-Muslim man at a charity dinner, looking at his phone with a small satisfied smile. He's just completed something on his phone — putting it down on the table. His expression is quiet contentment, not performance. A QR tent card visible on the table near his plate. Other guests are chatting around him, unaware. The private satisfaction of having made a pledge. Warm ambient light. 85mm f/1.4. ${STYLE}` },
{ filename: "product-pledge-06-multiple-phones.jpg", prompt: `16:9 landscape. Wide shot of a charity dinner table where multiple guests are simultaneously looking at their phones. Five or six people at a round table, at least three of them have phones out, scanning or tapping. A QR tent card sits in the centre of the table. The speaker at the far end of the hall is mid-appeal (blurred). The viral moment when one person scans and everyone follows. Energy and momentum. 35mm f/2.0. ${STYLE}` },
{ filename: "product-pledge-07-outdoor-event.jpg", prompt: `16:9 landscape. An outdoor charity fundraising event — a marquee or gazebo in a park. A young British-Muslim woman is holding her phone up to a QR code on a printed poster attached to a display board. She's in casual clothes — denim jacket, colourful hijab. Behind her: families on the grass, a bouncy castle, charity stalls. Daytime, overcast. Pledging doesn't just happen at dinners — it happens anywhere. 50mm f/2.0. ${STYLE}` },
{ filename: "product-pledge-08-mosque-friday.jpg", prompt: `16:9 landscape. Just outside a mosque after Friday prayer. A printed A3 poster on a noticeboard near the exit shows a QR code with a headline (text blurred). A young British-Muslim man in a thobe has stopped to scan it with his phone on his way out. Other congregants are filing past him. Afternoon daylight through the entrance. The mosque foyer has a shoe rack, coats on hooks, and community notices. Pledging in the flow of daily religious life. 35mm f/2.0. ${STYLE}` },
{ filename: "product-dashboard-02-team-review.jpg", prompt: `16:9 landscape. A small charity office. Three young British-Muslim team members — two men and a woman in hijab — gathered around a single laptop on a desk. One is pointing at the screen, another is writing numbers on a notepad, the third has her hand on her chin looking pleased. The laptop screen (NOT visible to camera, shot from behind) shows data. Tea mugs and biscuits on the desk. The team debrief — reviewing pledge numbers after an event. 35mm f/2.0. ${STYLE}` },
{ filename: "product-dashboard-03-phone-notification.jpg", prompt: `3:4 portrait. A young British-Muslim woman's hand holding her phone, which shows a notification banner on the lock screen (notification content blurred but the banner shape is clear — a white rounded rectangle at the top of the screen with an app icon). She's on a London bus, the window shows a rainy street. Her other hand holds the pole. A pledge payment just came through — the system works even when you're not thinking about it. 85mm f/1.4. ${STYLE}` },
{ filename: "product-dashboard-04-whatsapp-reminder.jpg", prompt: `16:9 landscape. Split focus shot. In the foreground (sharp): a phone lying on a desk showing a WhatsApp message thread with a green chat bubble (content blurred). In the background (slightly soft): a young British-Muslim charity worker at the desk, reaching for the phone with a curious expression. A gentle follow-up reminder has arrived for a donor — the automated system nudging at the right time. Natural desk lamp light. 50mm f/2.8. ${STYLE}` },
{ filename: "product-dashboard-05-spreadsheet-vs-app.jpg", prompt: `16:9 landscape. A charity office desk showing the "before and after" of pledge tracking. On the left side of the desk: a messy stack of paper pledge forms, a calculator, scribbled Post-it notes, and a stressed-looking pen. On the right side: a clean laptop with a calm screen glow. A young British-Muslim woman's hands are pushing the paper pile to the side, turning toward the laptop. The transition from chaos to system. Overhead fluorescent. 35mm f/2.8. ${STYLE}` },
{ filename: "product-dashboard-06-treasurer-reconciling.jpg", prompt: `16:9 landscape. An older British-Muslim man (50s, glasses, trimmed beard) sitting at a home office desk with a laptop, a bank statement printout, and a cup of chai. He's running his finger down the printed statement, cross-referencing with the laptop screen (not visible). His expression is focused but not stressed — things are matching up. A desk lamp, family photos on the shelf behind. The charity treasurer's monthly reconciliation made simple. 50mm f/2.0. ${STYLE}` },
{ filename: "product-donor-01-reminder-commute.jpg", prompt: `16:9 landscape. A young British-Muslim man sitting on a London Underground tube train, looking at his phone with recognition — "oh right, I pledged at that dinner." He's in work clothes — shirt, no tie, messenger bag on lap. The tube carriage has other commuters reading, on phones. Through the dark window: tunnel reflections. A reminder notification has just arrived. The gentle nudge that converts a pledge to a payment. 50mm f/2.0. ${STYLE}` },
{ filename: "product-donor-02-paying-sofa.jpg", prompt: `16:9 landscape. A young British-Muslim woman in hijab sitting on a sofa at home, casually completing a payment on her phone. She's relaxed — legs tucked under her, a blanket, the TV on in the background (blurred). Her phone is in one hand, her other hand holds a cup of tea. She's tapping the screen with her thumb. Warm living room lamp light. Paying a pledge in her own time, on her own sofa, no pressure. The ease of delayed giving. 50mm f/2.0. ${STYLE}` },
{ filename: "product-donor-03-payday-payment.jpg", prompt: `3:4 portrait. Close-up of a young British-Muslim man's hands holding his phone over a kitchen counter. Next to the phone on the counter: his car keys, a wallet, and a pay slip (partially visible, blurred). He's completing a transaction on his phone (screen blurred, but a confirmation-style layout with a button is visible). Payday — the day he can finally honour his pledge from last month. Morning light. The integrity of following through. 85mm f/1.8. ${STYLE}` },
{ filename: "product-donor-04-installment-calendar.jpg", prompt: `16:9 landscape. A young British-Muslim woman at a desk with a physical wall calendar visible behind her. Some dates on the calendar have small coloured stickers on them — marking her pledge payment installments. She's on her phone (screen not visible), relaxed, chin in hand. The calendar shows this is a planned, manageable commitment spread over months, not a single painful payment. Soft afternoon light. Financial flexibility for generous hearts. 50mm f/2.0. ${STYLE}` },
{ filename: "product-donor-05-confirmation-peace.jpg", prompt: `3:4 portrait. A young British-Muslim man sitting alone on a park bench, phone in his lap, looking up at the sky with a peaceful half-smile. He's just completed his final pledge payment. The phone screen is dark/off. Trees in the background, late afternoon golden light. A private moment of completion — promise kept, obligation fulfilled. No fanfare, just quiet peace. 85mm f/1.4. ${STYLE}` },
{ filename: "product-donor-06-gift-aid-tick.jpg", prompt: `3:4 portrait. Extreme close-up of a phone screen showing a simple checkbox or toggle being tapped by a thumb. The checkbox area is in focus with a clean white interface and blue accent colour (specific text NOT readable). The background behind the phone is blurred — looks like a dinner table setting with warm light. The small action of ticking Gift Aid that adds 25% to a donation at no cost. The detail that charities need. 85mm macro f/1.4. ${STYLE}` },
{ filename: "product-celebrate-01-target-hit.jpg", prompt: `16:9 landscape. A small charity office. A young British-Muslim man jumps up from his desk chair, arms raised, fists clenched in celebration. His laptop is open in front of him. A young woman in hijab at the next desk is turning to look at him with a surprised laugh, mid-sip of tea. Papers on desks, charity posters on walls. Fluorescent light. The moment the pledge collection target is hit — the dashboard showed 100%. Genuine, unguarded joy. 35mm f/2.0. ${STYLE}` },
{ filename: "product-celebrate-02-showing-phone.jpg", prompt: `16:9 landscape. Two young British-Muslim charity volunteers at a post-event cleanup. One is holding his phone out to show the other something on the screen (phone screen facing away from camera, NOT visible). The one looking has wide eyes and is covering her mouth with her hand in disbelief. They're standing in a half-cleared community hall — stacked chairs, folded tablecloths. The total pledged amount exceeding expectations. 50mm f/1.8. ${STYLE}` },
{ filename: "product-celebrate-03-announcing-total.jpg", prompt: `16:9 landscape. A charity fundraising dinner. The MC/speaker at the front podium holds up a piece of paper and announces the total to the room. The audience is mid-applause — hands up, some standing, faces lit with pride. Round tables with dinner remnants. A projection screen behind the speaker shows a large number (blurred). Fairy lights. The climactic announcement powered by real-time pledge data. The room erupts. 24mm f/2.8. ${STYLE}` },
{ filename: "product-celebrate-04-thank-you-text.jpg", prompt: `3:4 portrait. A young British-Muslim woman in hijab sitting at a desk, typing on her phone with both thumbs, a warm smile on her face. She's sending thank-you messages to donors. On the desk beside her: a laptop with the lid half-closed, a notepad with a list of names (some ticked off), and an empty mug. Warm desk lamp light, evening. The personal follow-up that turns a one-time donor into a lifelong supporter. 85mm f/1.4. ${STYLE}` },
];
async function generateOne(spec: Photo): Promise<boolean> {
const outPath = path.join(OUTPUT_DIR, spec.filename);
if (fs.existsSync(outPath)) {
console.log(` ⏭️ ${spec.filename} — already exists, skipping`);
return true;
}
const t0 = Date.now();
try {
const res = await fetch(ENDPOINT, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
contents: [{ parts: [{ text: `Generate a photorealistic photograph. ${spec.prompt}` }] }],
generationConfig: { responseModalities: ["IMAGE", "TEXT"] },
}),
});
if (!res.ok) {
const err = await res.text();
console.error(`${spec.filename} — API ${res.status}: ${err.slice(0, 120)}`);
return false;
}
const data: any = await res.json();
const parts = data.candidates?.[0]?.content?.parts;
const imgPart = parts?.find((p: any) => p.inlineData?.mimeType?.startsWith("image/"));
if (!imgPart) {
console.error(`${spec.filename} — No image in response`);
return false;
}
const buf = Buffer.from(imgPart.inlineData.data, "base64");
fs.writeFileSync(outPath, buf);
console.log(`${spec.filename}${(buf.length / 1024).toFixed(0)}KB (${((Date.now() - t0) / 1000).toFixed(1)}s)`);
return true;
} catch (e: any) {
console.error(`${spec.filename}${e.message}`);
return false;
}
}
async function main() {
// Filter to only missing files
const missing = ALL_PHOTOS.filter(p => !fs.existsSync(path.join(OUTPUT_DIR, p.filename)));
console.log(`\n🔧 ${missing.length} photos still needed (${ALL_PHOTOS.length - missing.length} already exist)\n`);
if (missing.length === 0) {
console.log("All done!");
return;
}
// Sequential with 8s delays to respect rate limits
let success = 0;
const t0 = Date.now();
for (let i = 0; i < missing.length; i++) {
console.log(`[${i + 1}/${missing.length}]`);
const ok = await generateOne(missing[i]);
if (ok) success++;
if (i < missing.length - 1) {
await new Promise(r => setTimeout(r, 8000));
}
}
const elapsed = ((Date.now() - t0) / 1000).toFixed(1);
console.log(`\n═══ Done: ${success}/${missing.length} in ${elapsed}s ═══`);
}
main();

View File

@@ -0,0 +1,273 @@
/**
* Generate 30 PRODUCT-SPECIFIC brand photos — Batch 4
* Every image maps to a moment in the PNPL user journey:
* Setup → Event → Pledge → Follow-up → Payment → Celebration
*
* Run: npx tsx scripts/generate-brand-photos-4.ts
*/
import fs from "fs";
import path from "path";
import dotenv from "dotenv";
dotenv.config();
const API_KEY = process.env.GEMINI_API_KEY;
if (!API_KEY) { console.error("Missing GEMINI_API_KEY"); process.exit(1); }
const MODEL = "gemini-3-pro-image-preview";
const ENDPOINT = `https://generativelanguage.googleapis.com/v1beta/models/${MODEL}:generateContent?key=${API_KEY}`;
const OUTPUT_DIR = path.join(process.cwd(), "public/images/brand");
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
const STYLE = `Photorealistic documentary photography. Sony A7III, shallow depth of field, available light. Candid fly-on-the-wall. Nobody looks at camera. No stock aesthetic. No staged poses. No alcohol or wine ever. No visible watermarks. Young modern British-Muslim community. South Asian and Arab features.`;
interface Photo { filename: string; prompt: string; }
const PHOTOS: Photo[] = [
// ═══════════════════════════════════════════
// PRE-EVENT SETUP (6 photos)
// The charity admin preparing pledge links
// ═══════════════════════════════════════════
{
filename: "product-setup-01-creating-link.jpg",
prompt: `16:9 landscape. A young British-Muslim woman in hijab sitting at a desk in a small charity office, working on a laptop. She's leaning forward slightly, one hand on the trackpad, focused and purposeful. On the desk beside the laptop: a printed event programme, a mug of tea, and a notepad with handwritten notes including a URL and amounts. The laptop screen casts a soft glow on her face (screen content NOT visible). Evening, dark window behind. Setting up a fundraising campaign before the big event. 50mm f/2.0. ${STYLE}`,
},
{
filename: "product-setup-02-printing-qr.jpg",
prompt: `16:9 landscape. Close-up of a small desktop printer outputting A5 cards, each with a large QR code printed on them. A young British-Muslim man's hands are picking up the freshly printed cards and stacking them neatly. Beside the printer: a paper cutter, a stack of already-cut tent cards, and a roll of sellotape. The office desk is cluttered with event prep materials — lanyards, name badges, a hi-vis vest. Fluorescent overhead light. The meticulous preparation before a fundraising event. 50mm f/2.8. ${STYLE}`,
},
{
filename: "product-setup-03-tent-cards.jpg",
prompt: `3:4 portrait. Overhead birds-eye shot of a desk covered with neat rows of folded tent cards, each showing a QR code on the front. A young person's hands are folding the last few cards along a scored line. A ruler and craft knife visible. The cards are white with minimal design — just a QR code and a small line of text below. The satisfying production line of event preparation. Clean, organised, purposeful. 50mm f/2.8 from directly above. ${STYLE}`,
},
{
filename: "product-setup-04-whatsapp-share.jpg",
prompt: `3:4 portrait. Close-up of a young British-Muslim man's hands holding a phone, composing a WhatsApp message. The phone screen shows a chat with a green send button (screen content blurred/not readable, but the WhatsApp green interface is recognisable). He's sitting on a sofa at home, wearing casual clothes. A cup of chai on the coffee table. Sharing a pledge link with the community before an event. Warm living room lamp light. 85mm f/1.4. ${STYLE}`,
},
{
filename: "product-setup-05-briefing-volunteers.jpg",
prompt: `16:9 landscape. A young British-Muslim charity organiser standing in front of 6-7 seated volunteers in a community hall, holding up a printed QR tent card to show them. He's in smart-casual — shirt, no tie. The volunteers are in matching charity t-shirts, listening attentively, some holding their own copies of the cards. A whiteboard behind the organiser shows a rough event floor plan. Pre-event briefing — "put one of these on every table." 35mm f/2.0. ${STYLE}`,
},
{
filename: "product-setup-06-table-setting.jpg",
prompt: `16:9 landscape. A community hall being set up for a fundraising dinner. In the foreground, a young British-Muslim woman in hijab is carefully placing a QR code tent card in the centre of a round table that already has a white tablecloth, water bottles, and a small bowl of dates. Behind her, other volunteers are setting other tables in a large hall. The tent card is small, white, elegant — sitting upright between the water jug and dates. The detail that will change everything. 50mm f/2.0. ${STYLE}`,
},
// ═══════════════════════════════════════════
// DURING EVENT — PLEDGING MOMENT (8 photos)
// Donors discovering and using the QR/link
// ═══════════════════════════════════════════
{
filename: "product-pledge-01-scanning-table.jpg",
prompt: `16:9 landscape. At a charity dinner table, a young British-Muslim man in a blazer is holding his phone camera up to a QR code tent card on the table. His phone is angled toward the card, we see the back of his phone and his hands. The tent card stands upright between plates of biryani, dates, and water. Other guests at the table are eating and chatting, not paying attention. Warm fairy-light ambiance. The private, effortless moment of scanning a pledge link. 85mm f/1.4. ${STYLE}`,
},
{
filename: "product-pledge-02-phone-form.jpg",
prompt: `3:4 portrait. Over-the-shoulder shot of a young British-Muslim woman at a charity dinner, looking at her phone which she holds in both hands. The phone screen shows a simple white form interface with a blue button (screen intentionally slightly blurred so text is not readable, but the layout of a clean mobile form is recognisable — white background, input fields, a coloured button at the bottom). Her thumbs hover over the screen. The decisive moment of entering a pledge amount. 85mm f/1.4. ${STYLE}`,
},
{
filename: "product-pledge-03-couple-discussing.jpg",
prompt: `16:9 landscape. A young British-Muslim couple at a charity dinner table, heads close together, discussing something on one of their phones. The husband is holding the phone between them (screen NOT visible), pointing at it. The wife is nodding thoughtfully. Between them on the table: a QR tent card, water glasses, half-eaten food. They're deciding together how much to pledge. Warm tungsten light. The intimacy of a shared financial decision for good. 85mm f/1.8. ${STYLE}`,
},
{
filename: "product-pledge-04-quick-tap.jpg",
prompt: `3:4 portrait. Extreme close-up of a thumb tapping a phone screen. The phone is held in one hand over a charity dinner table (blurred biryani and tent card in the background). The screen shows a confirmation-style layout — a coloured button being pressed (screen slightly blurred, colours recognisable but text not readable). The split-second of committing to a pledge. Shallow depth of field, everything except the thumb and screen is bokeh. 85mm macro f/1.4. ${STYLE}`,
},
{
filename: "product-pledge-05-confirmation-smile.jpg",
prompt: `3:4 portrait. A young British-Muslim man at a charity dinner, looking at his phone with a small satisfied smile. He's just completed something on his phone — putting it down on the table. His expression is quiet contentment, not performance. A QR tent card visible on the table near his plate. Other guests are chatting around him, unaware. The private satisfaction of having made a pledge. Warm ambient light. 85mm f/1.4. ${STYLE}`,
},
{
filename: "product-pledge-06-multiple-phones.jpg",
prompt: `16:9 landscape. Wide shot of a charity dinner table where multiple guests are simultaneously looking at their phones. Five or six people at a round table, at least three of them have phones out, scanning or tapping. A QR tent card sits in the centre of the table. The speaker at the far end of the hall is mid-appeal (blurred). The viral moment when one person scans and everyone follows. Energy and momentum. 35mm f/2.0. ${STYLE}`,
},
{
filename: "product-pledge-07-outdoor-event.jpg",
prompt: `16:9 landscape. An outdoor charity fundraising event — a marquee or gazebo in a park. A young British-Muslim woman is holding her phone up to a QR code on a printed poster attached to a display board. She's in casual clothes — denim jacket, colourful hijab. Behind her: families on the grass, a bouncy castle, charity stalls. Daytime, overcast. Pledging doesn't just happen at dinners — it happens anywhere. 50mm f/2.0. ${STYLE}`,
},
{
filename: "product-pledge-08-mosque-friday.jpg",
prompt: `16:9 landscape. Just outside a mosque after Friday prayer. A printed A3 poster on a noticeboard near the exit shows a QR code with a headline (text blurred). A young British-Muslim man in a thobe has stopped to scan it with his phone on his way out. Other congregants are filing past him. Afternoon daylight through the entrance. The mosque foyer has a shoe rack, coats on hooks, and community notices. Pledging in the flow of daily religious life. 35mm f/2.0. ${STYLE}`,
},
// ═══════════════════════════════════════════
// POST-EVENT — FOLLOW-UP & DASHBOARD (6 photos)
// The morning after, tracking pledges, sending reminders
// ═══════════════════════════════════════════
{
filename: "product-dashboard-01-morning-after.jpg",
prompt: `16:9 landscape. Morning light streaming through a kitchen window. A young British-Muslim man in a t-shirt sits at the kitchen table with a laptop open and a mug of coffee. His expression is quietly amazed — eyebrows slightly raised, leaning forward toward the screen. The laptop screen casts light on his face (content NOT visible). His phone on the table shows a notification (screen blurred). The morning after the fundraiser — checking how much was pledged overnight. 50mm f/2.0. ${STYLE}`,
},
{
filename: "product-dashboard-02-team-review.jpg",
prompt: `16:9 landscape. A small charity office. Three young British-Muslim team members — two men and a woman in hijab — gathered around a single laptop on a desk. One is pointing at the screen, another is writing numbers on a notepad, the third has her hand on her chin looking pleased. The laptop screen (NOT visible to camera, shot from behind) shows data. Tea mugs and biscuits on the desk. The team debrief — reviewing pledge numbers after an event. 35mm f/2.0. ${STYLE}`,
},
{
filename: "product-dashboard-03-phone-notification.jpg",
prompt: `3:4 portrait. A young British-Muslim woman's hand holding her phone, which shows a notification banner on the lock screen (notification content blurred but the banner shape is clear — a white rounded rectangle at the top of the screen with an app icon). She's on a London bus, the window shows a rainy street. Her other hand holds the pole. A pledge payment just came through — the system works even when you're not thinking about it. 85mm f/1.4. ${STYLE}`,
},
{
filename: "product-dashboard-04-whatsapp-reminder.jpg",
prompt: `16:9 landscape. Split focus shot. In the foreground (sharp): a phone lying on a desk showing a WhatsApp message thread with a green chat bubble (content blurred). In the background (slightly soft): a young British-Muslim charity worker at the desk, reaching for the phone with a curious expression. A gentle follow-up reminder has arrived for a donor — the automated system nudging at the right time. Natural desk lamp light. 50mm f/2.8. ${STYLE}`,
},
{
filename: "product-dashboard-05-spreadsheet-vs-app.jpg",
prompt: `16:9 landscape. A charity office desk showing the "before and after" of pledge tracking. On the left side of the desk: a messy stack of paper pledge forms, a calculator, scribbled Post-it notes, and a stressed-looking pen. On the right side: a clean laptop with a calm screen glow. A young British-Muslim woman's hands are pushing the paper pile to the side, turning toward the laptop. The transition from chaos to system. Overhead fluorescent. 35mm f/2.8. ${STYLE}`,
},
{
filename: "product-dashboard-06-treasurer-reconciling.jpg",
prompt: `16:9 landscape. An older British-Muslim man (50s, glasses, trimmed beard) sitting at a home office desk with a laptop, a bank statement printout, and a cup of chai. He's running his finger down the printed statement, cross-referencing with the laptop screen (not visible). His expression is focused but not stressed — things are matching up. A desk lamp, family photos on the shelf behind. The charity treasurer's monthly reconciliation made simple. 50mm f/2.0. ${STYLE}`,
},
// ═══════════════════════════════════════════
// DONOR JOURNEY — PAYMENT & COMPLETION (6 photos)
// The donor side: receiving reminders, paying, feeling good
// ═══════════════════════════════════════════
{
filename: "product-donor-01-reminder-commute.jpg",
prompt: `16:9 landscape. A young British-Muslim man sitting on a London Underground tube train, looking at his phone with recognition — "oh right, I pledged at that dinner." He's in work clothes — shirt, no tie, messenger bag on lap. The tube carriage has other commuters reading, on phones. Through the dark window: tunnel reflections. A reminder notification has just arrived. The gentle nudge that converts a pledge to a payment. 50mm f/2.0. ${STYLE}`,
},
{
filename: "product-donor-02-paying-sofa.jpg",
prompt: `16:9 landscape. A young British-Muslim woman in hijab sitting on a sofa at home, casually completing a payment on her phone. She's relaxed — legs tucked under her, a blanket, the TV on in the background (blurred). Her phone is in one hand, her other hand holds a cup of tea. She's tapping the screen with her thumb. Warm living room lamp light. Paying a pledge in her own time, on her own sofa, no pressure. The ease of delayed giving. 50mm f/2.0. ${STYLE}`,
},
{
filename: "product-donor-03-payday-payment.jpg",
prompt: `3:4 portrait. Close-up of a young British-Muslim man's hands holding his phone over a kitchen counter. Next to the phone on the counter: his car keys, a wallet, and a pay slip (partially visible, blurred). He's completing a transaction on his phone (screen blurred, but a confirmation-style layout with a button is visible). Payday — the day he can finally honour his pledge from last month. Morning light. The integrity of following through. 85mm f/1.8. ${STYLE}`,
},
{
filename: "product-donor-04-installment-calendar.jpg",
prompt: `16:9 landscape. A young British-Muslim woman at a desk with a physical wall calendar visible behind her. Some dates on the calendar have small coloured stickers on them — marking her pledge payment installments. She's on her phone (screen not visible), relaxed, chin in hand. The calendar shows this is a planned, manageable commitment spread over months, not a single painful payment. Soft afternoon light. Financial flexibility for generous hearts. 50mm f/2.0. ${STYLE}`,
},
{
filename: "product-donor-05-confirmation-peace.jpg",
prompt: `3:4 portrait. A young British-Muslim man sitting alone on a park bench, phone in his lap, looking up at the sky with a peaceful half-smile. He's just completed his final pledge payment. The phone screen is dark/off. Trees in the background, late afternoon golden light. A private moment of completion — promise kept, obligation fulfilled. No fanfare, just quiet peace. 85mm f/1.4. ${STYLE}`,
},
{
filename: "product-donor-06-gift-aid-tick.jpg",
prompt: `3:4 portrait. Extreme close-up of a phone screen showing a simple checkbox or toggle being tapped by a thumb. The checkbox area is in focus with a clean white interface and blue accent colour (specific text NOT readable). The background behind the phone is blurred — looks like a dinner table setting with warm light. The small action of ticking Gift Aid that adds 25% to a donation at no cost. The detail that charities need. 85mm macro f/1.4. ${STYLE}`,
},
// ═══════════════════════════════════════════
// CELEBRATION — TARGET HIT (4 photos)
// The moment when pledges convert and targets are reached
// ═══════════════════════════════════════════
{
filename: "product-celebrate-01-target-hit.jpg",
prompt: `16:9 landscape. A small charity office. A young British-Muslim man jumps up from his desk chair, arms raised, fists clenched in celebration. His laptop is open in front of him. A young woman in hijab at the next desk is turning to look at him with a surprised laugh, mid-sip of tea. Papers on desks, charity posters on walls. Fluorescent light. The moment the pledge collection target is hit — the dashboard showed 100%. Genuine, unguarded joy. 35mm f/2.0. ${STYLE}`,
},
{
filename: "product-celebrate-02-showing-phone.jpg",
prompt: `16:9 landscape. Two young British-Muslim charity volunteers at a post-event cleanup. One is holding his phone out to show the other something on the screen (phone screen facing away from camera, NOT visible). The one looking has wide eyes and is covering her mouth with her hand in disbelief. They're standing in a half-cleared community hall — stacked chairs, folded tablecloths. The total pledged amount exceeding expectations. 50mm f/1.8. ${STYLE}`,
},
{
filename: "product-celebrate-03-announcing-total.jpg",
prompt: `16:9 landscape. A charity fundraising dinner. The MC/speaker at the front podium holds up a piece of paper and announces the total to the room. The audience is mid-applause — hands up, some standing, faces lit with pride. Round tables with dinner remnants. A projection screen behind the speaker shows a large number (blurred). Fairy lights. The climactic announcement powered by real-time pledge data. The room erupts. 24mm f/2.8. ${STYLE}`,
},
{
filename: "product-celebrate-04-thank-you-text.jpg",
prompt: `3:4 portrait. A young British-Muslim woman in hijab sitting at a desk, typing on her phone with both thumbs, a warm smile on her face. She's sending thank-you messages to donors. On the desk beside her: a laptop with the lid half-closed, a notepad with a list of names (some ticked off), and an empty mug. Warm desk lamp light, evening. The personal follow-up that turns a one-time donor into a lifelong supporter. 85mm f/1.4. ${STYLE}`,
},
];
// ─── CONCURRENT GENERATION ENGINE ──────────────────────────
async function generateOne(spec: Photo): Promise<boolean> {
const outPath = path.join(OUTPUT_DIR, spec.filename);
const t0 = Date.now();
try {
const res = await fetch(ENDPOINT, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
contents: [{ parts: [{ text: `Generate a photorealistic photograph. ${spec.prompt}` }] }],
generationConfig: { responseModalities: ["IMAGE", "TEXT"] },
}),
});
if (!res.ok) {
const err = await res.text();
console.error(`${spec.filename} — API ${res.status}: ${err.slice(0, 150)}`);
return false;
}
const data: any = await res.json();
const parts = data.candidates?.[0]?.content?.parts;
const imgPart = parts?.find((p: any) => p.inlineData?.mimeType?.startsWith("image/"));
if (!imgPart) {
const textPart = parts?.find((p: any) => p.text);
console.error(`${spec.filename} — No image${textPart ? ": " + textPart.text.slice(0, 100) : ""}`);
return false;
}
const buf = Buffer.from(imgPart.inlineData.data, "base64");
fs.writeFileSync(outPath, buf);
const ms = Date.now() - t0;
console.log(`${spec.filename}${(buf.length / 1024).toFixed(0)}KB (${(ms / 1000).toFixed(1)}s)`);
return true;
} catch (e: any) {
console.error(`${spec.filename}${e.message}`);
return false;
}
}
async function main() {
const BATCH_SIZE = 5;
const batches: Photo[][] = [];
for (let i = 0; i < PHOTOS.length; i += BATCH_SIZE) {
batches.push(PHOTOS.slice(i, i + BATCH_SIZE));
}
console.log("═══════════════════════════════════════════════════════");
console.log(" PNPL Brand Photography — Batch 4 (Product Journey)");
console.log(` Model: ${MODEL}`);
console.log(` Strategy: ${batches.length} batches × ${BATCH_SIZE} concurrent`);
console.log(` Output: ${OUTPUT_DIR}`);
console.log("═══════════════════════════════════════════════════════");
const t0 = Date.now();
let success = 0;
let failed: Photo[] = [];
for (let i = 0; i < batches.length; i++) {
console.log(`\n⚡ Batch ${i + 1}/${batches.length} — firing ${batches[i].length} requests...`);
const results = await Promise.allSettled(batches[i].map(p => generateOne(p)));
const batchSuccess = results.filter(r => r.status === "fulfilled" && r.value).length;
success += batchSuccess;
for (const spec of batches[i]) {
if (!fs.existsSync(path.join(OUTPUT_DIR, spec.filename))) {
failed.push(spec);
}
}
if (i < batches.length - 1) {
console.log(` ⏳ 3s cooldown...`);
await new Promise(r => setTimeout(r, 3000));
}
}
if (failed.length > 0) {
console.log(`\n🔄 Retrying ${failed.length} failures (5s between each)...`);
await new Promise(r => setTimeout(r, 5000));
for (const spec of failed) {
const ok = await generateOne(spec);
if (ok) success++;
await new Promise(r => setTimeout(r, 5000));
}
}
const elapsed = ((Date.now() - t0) / 1000).toFixed(1);
console.log("\n═══════════════════════════════════════════════════════");
console.log(` Done: ${success}/${PHOTOS.length} photos in ${elapsed}s`);
console.log(` Total brand library: 120 photos`);
console.log(` Output: ${OUTPUT_DIR}`);
console.log("═══════════════════════════════════════════════════════");
}
main();

View File

@@ -0,0 +1,274 @@
/**
* Generate 30 brand photography assets — MAXIMUM CONCURRENCY
* All requests fire simultaneously in batches of 10
*
* Run: npx tsx scripts/generate-brand-photos.ts
*/
import fs from "fs";
import path from "path";
import dotenv from "dotenv";
dotenv.config();
const API_KEY = process.env.GEMINI_API_KEY;
if (!API_KEY) { console.error("Missing GEMINI_API_KEY"); process.exit(1); }
const MODEL = "gemini-3-pro-image-preview";
const ENDPOINT = `https://generativelanguage.googleapis.com/v1beta/models/${MODEL}:generateContent?key=${API_KEY}`;
const OUTPUT_DIR = path.join(process.cwd(), "public/images/brand");
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
const STYLE = `Photorealistic documentary photography. Sony A7III, shallow depth of field, available light. Candid fly-on-the-wall. Nobody looks at camera. No stock aesthetic. No staged poses. No alcohol or wine. No visible text or watermarks. Young modern British-Muslim community.`;
interface Photo { filename: string; prompt: string; }
const PHOTOS: Photo[] = [
// ═══════════════════════════════════════════
// FUNDRAISING EVENTS (8 photos)
// ═══════════════════════════════════════════
{
filename: "event-01-speaker-passion.jpg",
prompt: `16:9 landscape. Young British-Muslim woman, mid-20s, wearing a navy hijab and smart blazer, standing at a podium giving a passionate charity appeal speech. One hand gesturing. Behind her a projected screen showing a fundraising target. Packed community hall audience at round tables with white tablecloths, water jugs, biryani trays. Overhead strip lights mixed with fairy lights. She is mid-sentence, totally absorbed. 85mm f/1.4. ${STYLE}`,
},
{
filename: "event-02-hands-raised.jpg",
prompt: `16:9 landscape. Wide shot of a charity fundraising dinner in a community hall. Multiple hands raised across the room as people make pledges during a live appeal. Round tables with white tablecloths, dates in bowls, water bottles, foil food trays. Mix of men in suits and thobes, women in hijabs. A speaker at the front is barely visible. Fairy lights along the walls. The energy is electric — momentum building. 24mm f/2.8. ${STYLE}`,
},
{
filename: "event-03-table-conversation.jpg",
prompt: `16:9 landscape. Close-up of a round table at a British-Muslim charity dinner. Two young men, one in a grey crewneck and one in a blazer, leaning toward each other in animated conversation. Between them on the white tablecloth: a bowl of dates, water glasses, paper plates with leftover biryani. A woman in a patterned hijab to their left is smiling at something on her phone (screen not visible). Warm tungsten overhead light. Intimate, social energy. 50mm f/1.8. ${STYLE}`,
},
{
filename: "event-04-registration-desk.jpg",
prompt: `16:9 landscape. A young British-Muslim woman in hijab and a young man in a charity t-shirt sitting behind a registration desk at the entrance of a community hall. Printed name badges spread out on the table, a laptop (screen not visible), a stack of event programmes, a charity collection bucket. A guest is signing in. Behind them: a pull-up banner for the charity event. Overhead fluorescent light. The organised chaos of event check-in. 35mm f/2.0. ${STYLE}`,
},
{
filename: "event-05-qr-scanning.jpg",
prompt: `16:9 landscape. Over-the-shoulder shot of a young British-Muslim man at a charity dinner table holding his phone near a small printed QR code tent card on the table. We see the back of his phone and his hands, but NOT the screen. The QR card sits among dates, water bottles and paper plates. Other guests at the table are chatting, blurred in warm background. Candlelight from tea lights. Tight crop on the action. 85mm f/1.4. ${STYLE}`,
},
{
filename: "event-06-volunteer-serving.jpg",
prompt: `16:9 landscape. A teenage British-Muslim girl in a matching blue charity volunteer t-shirt carrying a large tray of samosas between round dinner tables. She's focused, weaving through chairs. Guests at the tables are mid-conversation, some looking at the food. Community hall setting — strip lights, fire exit sign, magnolia walls. The unglamorous heroism of a 16-year-old volunteer. 50mm f/1.8. ${STYLE}`,
},
{
filename: "event-07-stage-wide.jpg",
prompt: `16:9 landscape. Ultra-wide establishing shot of a large charity fundraising dinner in a mosque function hall. 200+ guests at round tables stretching into the distance. At the far end, a stage with a speaker at a podium, a large projection screen showing donation amounts, and charity banners. Ornate Islamic geometric ceiling with brass chandeliers. The scale is impressive. Shot from the very back of the hall, framed by dark silhouettes of standing figures. 16mm f/2.8. ${STYLE}`,
},
{
filename: "event-08-end-of-night.jpg",
prompt: `16:9 landscape. The end of a charity fundraising dinner. A community hall with most tables now empty — some chairs pushed back, crumpled napkins, empty water bottles. Two young volunteers, a man and woman in charity t-shirts, are stacking chairs in the background. In the foreground, a single table still has a few guests lingering over tea in paper cups. The fairy lights are still on. Quiet, tired, satisfied energy — the event was a success. 35mm f/2.0. ${STYLE}`,
},
// ═══════════════════════════════════════════
// BEHIND THE SCENES / OPERATIONS (6 photos)
// ═══════════════════════════════════════════
{
filename: "ops-01-whiteboard-planning.jpg",
prompt: `16:9 landscape. A small cramped charity office. A young British-Muslim man in a hoodie standing at a whiteboard covered in handwritten fundraising campaign plans — target amounts, dates, venue names. He's pointing at something on the board while a young woman in hijab sits at the desk behind him, looking at the board thoughtfully. Tea mugs on the desk. Stacked brown envelopes. A charity poster on the wall. Fluorescent overhead light, evening (dark window). 35mm f/2.0. ${STYLE}`,
},
{
filename: "ops-02-packing-envelopes.jpg",
prompt: `16:9 landscape. Overhead birds-eye shot of a table covered in organised rows of brown envelopes, printed letters, and charity leaflets being stuffed and sealed. Two pairs of young hands visible working — one person sealing envelopes, another placing letters inside. A roll of stamps, a pen, and a mug of chai visible at the edge of the table. The satisfying mundanity of charity admin. 50mm f/2.8 from above. ${STYLE}`,
},
{
filename: "ops-03-laptop-late-night.jpg",
prompt: `16:9 landscape. Side profile of a young British-Muslim man, late 20s, short beard, wearing a plain t-shirt, hunched over a laptop at a kitchen table late at night. The laptop screen casts a pale blue glow on his face (screen content NOT visible). A mug of cold tea beside him, phone face-down on the table. Through the kitchen window: orange street light. The solitude of volunteer work done after hours. 85mm f/1.8. ${STYLE}`,
},
{
filename: "ops-04-printing-materials.jpg",
prompt: `16:9 landscape. Close-up of freshly printed A5 flyers coming out of a small desktop printer in a community centre office. A young hand is picking up the top sheet. The flyers have QR codes on them (no readable text). In the background, slightly blurred: stacks of printed materials, a paper cutter, a box of lanyards. The printer's green light glowing. Fluorescent overhead. The details of event preparation. 50mm macro f/2.8. ${STYLE}`,
},
{
filename: "ops-05-meeting-circle.jpg",
prompt: `16:9 landscape. Five young British-Muslim volunteers sitting in mismatched chairs in a loose circle in a community centre room, having a planning meeting. Mix of men and women, casual clothes, one woman in hijab taking notes on a clipboard. Someone is speaking with hand gestures. Paper cups of tea, some on the floor beside chairs. A noticeboard behind them with pinned flyers. Informal, energetic, collaborative. Overhead fluorescent. 24mm f/2.8. ${STYLE}`,
},
{
filename: "ops-06-counting-money.jpg",
prompt: `16:9 landscape. Close-up of a charity collection bucket being emptied onto a desk. Coins and notes spilling out. A young person's hands are sorting the money into piles. A calculator and a notepad with handwritten tallies beside them. A second collection bucket waits, still full. The desk is scratched wood. Overhead strip light. The honest reality of charity collection counting. 50mm f/2.0. ${STYLE}`,
},
// ═══════════════════════════════════════════
// PEOPLE / PORTRAITS (6 photos)
// ═══════════════════════════════════════════
{
filename: "people-01-imam-young.jpg",
prompt: `3:4 portrait. A young British-Muslim imam, early 30s, neatly trimmed beard, wearing a white thobe and a grey cardigan over it. He is standing in the doorway of a mosque, leaning against the door frame with his arms loosely crossed, looking out toward the street with a thoughtful expression. Afternoon light catching his profile. The mosque doorway has simple geometric tile work. He looks approachable, modern, grounded. 85mm f/1.4. ${STYLE}`,
},
{
filename: "people-02-student-volunteer.jpg",
prompt: `3:4 portrait. A young British-Muslim woman, early 20s, university student, wearing a colourful printed hijab, a denim jacket, and carrying a canvas tote bag with charity pins on it. She's walking through a university campus corridor, looking to the side at something, mid-stride. Natural daylight from large windows. Other students blurred in background. Energetic, purposeful, young. 50mm f/1.8. ${STYLE}`,
},
{
filename: "people-03-elder-donor.jpg",
prompt: `3:4 portrait. A dignified older British-Muslim man, 60s, silver beard, wearing a traditional white topi and a dark suit, sitting at a charity dinner table. He is looking straight ahead with a slight nod, as if acknowledging a speaker. His hands are folded on the table in front of him. Water glass and dates beside him. Warm tungsten light. The quiet authority of a community elder who has supported this charity for decades. 85mm f/1.4. ${STYLE}`,
},
{
filename: "people-04-teen-volunteers.jpg",
prompt: `16:9 landscape. Three British-Muslim teenagers, two girls in hijab and a boy, all wearing matching charity event t-shirts, standing together outside a community hall entrance. They're on a break — one is drinking from a water bottle, another is laughing at something, the third is adjusting their lanyard. Evening light, the community hall door propped open behind them with warm light spilling out. The joy of being young and useful. 50mm f/1.8. ${STYLE}`,
},
{
filename: "people-05-fundraiser-car.jpg",
prompt: `16:9 landscape. A young British-Muslim man in the driver's seat of a parked car, looking at printed directions or a map on his phone (phone not visible, shot from passenger side). He's wearing a North Face jacket, has a takeaway coffee in the cup holder. Early morning grey light through the windshield. Charity collection buckets and banner rolls visible on the back seat. The early morning drive to set up an event in another city. 35mm f/2.0. ${STYLE}`,
},
{
filename: "people-06-hijabi-professional.jpg",
prompt: `3:4 portrait. A young British-Muslim woman, late 20s, wearing a tailored black hijab and a sharp navy blazer, walking up the steps of a modern office building. She's carrying a leather portfolio and looking ahead with composed confidence. Morning light. Glass doors and steel railings. Other professionals blurred in background. She could be going to a board meeting or a charity trustee meeting. Polished, modern, capable. 85mm f/1.4. ${STYLE}`,
},
// ═══════════════════════════════════════════
// COMMUNITY & DAILY LIFE (6 photos)
// ═══════════════════════════════════════════
{
filename: "life-01-mosque-exterior.jpg",
prompt: `16:9 landscape. Exterior of a British mosque on an ordinary weekday. Red brick Victorian building converted into a mosque, with a simple green dome and crescent added to the roof. A few men leaving after prayer, one carrying prayer beads. A bicycle chained to the railing. Terraced houses on either side. Overcast grey sky. A very normal, very British scene. 35mm f/4.0 with deep depth of field. ${STYLE}`,
},
{
filename: "life-02-food-prep.jpg",
prompt: `16:9 landscape. A community centre kitchen. Three women in hijabs and aprons working together to prepare food for a charity event. One is stirring a massive pot on an industrial stove, another is arranging samosas on a tray, the third is covering foil containers with cling film. Steam rising. Stainless steel surfaces. Fluorescent kitchen light. The fellowship of communal cooking. 35mm f/2.0. ${STYLE}`,
},
{
filename: "life-03-friday-prayer.jpg",
prompt: `16:9 landscape. Wide shot from behind: rows of men standing in prayer (salah) in a mosque prayer hall. White, grey and dark thobes and clothing. Shoes lined up neatly at the back. Soft daylight coming through geometric windows. Deep green carpet. A profound stillness. Shot from the very back of the room, low angle. 24mm f/2.8. ${STYLE}`,
},
{
filename: "life-04-family-dinner.jpg",
prompt: `16:9 landscape. A multigenerational British-Muslim family around a dining table at home for a Friday evening meal. Grandparents, parents, and young children. Dishes of biryani, salad, naan bread spread across the table. The grandfather is serving rice onto a child's plate. Warm overhead pendant light. Family photos on the wall behind. The warmth of a weekly gathering. 35mm f/2.0. ${STYLE}`,
},
{
filename: "life-05-eid-morning.jpg",
prompt: `16:9 landscape. Eid morning outside a mosque. Families streaming out after Eid prayer — men in smart thobes and suits, women in colourful hijabs and abayas, children running around excited. People hugging and greeting each other on the pavement. Bunting or a simple "Eid Mubarak" banner above the mosque entrance. Morning light. The joy and colour of Eid in a British city. 35mm f/2.8. ${STYLE}`,
},
{
filename: "life-06-charity-shop.jpg",
prompt: `16:9 landscape. Interior of a small Islamic charity shop on a British high street. A young British-Muslim woman volunteer behind the counter, sorting through donated clothes on a rack. Shelves with second-hand books and homewares. A charity poster in the window. A customer browsing in the background. Natural daylight from the shop front window. The quiet everyday engine of charity. 35mm f/2.0. ${STYLE}`,
},
// ═══════════════════════════════════════════
// DIGITAL / MODERN MOMENTS (4 photos)
// ═══════════════════════════════════════════
{
filename: "digital-01-group-selfie.jpg",
prompt: `16:9 landscape. A group of young British-Muslim volunteers, 5-6 people, huddled together for a group selfie after a successful charity event. They're in matching charity t-shirts, some still wearing lanyards. Big genuine smiles, one person doing a peace sign. The community hall behind them is half-cleared — stacked chairs, tablecloths being folded. Shot from slightly behind the group so we see them from the side/back, NOT the phone screen. Evening. The camaraderie of shared work. 35mm f/2.0. ${STYLE}`,
},
{
filename: "digital-02-social-media.jpg",
prompt: `16:9 landscape. A young British-Muslim woman in hijab sitting cross-legged on a prayer mat in a quiet corner, filming a short video on her phone (we see her from the side, phone pointed at herself but screen NOT visible). She's in the community hall after an event — empty tables in the background. She's recording a thank-you message for social media. Relaxed, natural, not overly produced. Warm available light. 85mm f/1.8. ${STYLE}`,
},
{
filename: "digital-03-notification-smile.jpg",
prompt: `3:4 portrait. A young British-Muslim man, early 20s, sitting on a bench at a London bus stop, looking at his phone with a small private smile. He's in casual clothes — hoodie, jeans, trainers. A bus shelter ad is blurred behind him. Overcast London light. The red of a London bus approaching in the far background. A fleeting good-news moment in an ordinary day. 85mm f/1.4. ${STYLE}`,
},
{
filename: "digital-04-dashboard-team.jpg",
prompt: `16:9 landscape. Two young British-Muslim charity workers — a man and a woman in hijab — leaning over a desk looking at a laptop screen together (screen NOT visible to camera, shot from behind the laptop). The man is pointing at the screen, the woman has her hand on her chin, considering. Tea mugs on the desk. Evening, dark window behind. Charity paperwork stacked to one side. The moment of reviewing results together. 50mm f/2.0. ${STYLE}`,
},
];
// ─── CONCURRENT GENERATION ENGINE ──────────────────────────
async function generateOne(spec: Photo): Promise<boolean> {
const outPath = path.join(OUTPUT_DIR, spec.filename);
const t0 = Date.now();
try {
const res = await fetch(ENDPOINT, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
contents: [{ parts: [{ text: `Generate a photorealistic photograph. ${spec.prompt}` }] }],
generationConfig: { responseModalities: ["IMAGE", "TEXT"] },
}),
});
if (!res.ok) {
const err = await res.text();
console.error(`${spec.filename} — API ${res.status}: ${err.slice(0, 150)}`);
return false;
}
const data: any = await res.json();
const parts = data.candidates?.[0]?.content?.parts;
const imgPart = parts?.find((p: any) => p.inlineData?.mimeType?.startsWith("image/"));
if (!imgPart) {
const textPart = parts?.find((p: any) => p.text);
console.error(`${spec.filename} — No image${textPart ? ": " + textPart.text.slice(0, 100) : ""}`);
return false;
}
const buf = Buffer.from(imgPart.inlineData.data, "base64");
fs.writeFileSync(outPath, buf);
const ms = Date.now() - t0;
console.log(`${spec.filename}${(buf.length / 1024).toFixed(0)}KB (${(ms / 1000).toFixed(1)}s)`);
return true;
} catch (e: any) {
console.error(`${spec.filename}${e.message}`);
return false;
}
}
async function runBatch(batch: Photo[], batchNum: number, totalBatches: number): Promise<number> {
console.log(`\n⚡ Batch ${batchNum}/${totalBatches} — firing ${batch.length} requests simultaneously...`);
const results = await Promise.allSettled(batch.map(p => generateOne(p)));
return results.filter(r => r.status === "fulfilled" && r.value).length;
}
async function main() {
const BATCH_SIZE = 10; // 10 concurrent requests per batch
const batches: Photo[][] = [];
for (let i = 0; i < PHOTOS.length; i += BATCH_SIZE) {
batches.push(PHOTOS.slice(i, i + BATCH_SIZE));
}
console.log("═══════════════════════════════════════════════════════");
console.log(" PNPL Brand Photography — 30 Marketing Assets");
console.log(` Model: ${MODEL}`);
console.log(` Strategy: ${batches.length} batches × ${BATCH_SIZE} concurrent`);
console.log(` Output: ${OUTPUT_DIR}`);
console.log("═══════════════════════════════════════════════════════");
const t0 = Date.now();
let success = 0;
let failed: Photo[] = [];
for (let i = 0; i < batches.length; i++) {
const count = await runBatch(batches[i], i + 1, batches.length);
success += count;
// Track failures for retry
const batchResults = await Promise.allSettled(
batches[i].map(async (p) => {
const exists = fs.existsSync(path.join(OUTPUT_DIR, p.filename));
if (!exists) failed.push(p);
})
);
// Small pause between batches to avoid rate limit walls
if (i < batches.length - 1) {
console.log(` ⏳ 2s cooldown...`);
await new Promise(r => setTimeout(r, 2000));
}
}
// ─── RETRY FAILURES ────────────────────────────────
if (failed.length > 0) {
console.log(`\n🔄 Retrying ${failed.length} failures...`);
await new Promise(r => setTimeout(r, 3000));
for (const spec of failed) {
const ok = await generateOne(spec);
if (ok) success++;
await new Promise(r => setTimeout(r, 1500));
}
}
const elapsed = ((Date.now() - t0) / 1000).toFixed(1);
console.log("\n═══════════════════════════════════════════════════════");
console.log(` Done: ${success}/${PHOTOS.length} photos in ${elapsed}s`);
console.log(` Output: ${OUTPUT_DIR}`);
console.log("═══════════════════════════════════════════════════════");
}
main();

View File

@@ -0,0 +1,289 @@
/**
* Generate landing page photography — Round 2
* Young, modern, British-Muslim charity photography
* Model: Gemini 3 Pro Image Preview
*
* Run: npx tsx scripts/generate-photos.ts
*/
import fs from "fs";
import path from "path";
import dotenv from "dotenv";
dotenv.config();
const API_KEY = process.env.GEMINI_API_KEY;
if (!API_KEY) {
console.error("Missing GEMINI_API_KEY in .env");
process.exit(1);
}
const MODEL = "gemini-3-pro-image-preview";
const ENDPOINT = `https://generativelanguage.googleapis.com/v1beta/models/${MODEL}:generateContent?key=${API_KEY}`;
const OUTPUT_DIR = path.join(process.cwd(), "public/images/landing");
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
interface PhotoSpec {
filename: string;
prompt: string;
}
/*
* ═══════════════════════════════════════════════════════════════
* PHOTOGRAPHY DIRECTION
* ═══════════════════════════════════════════════════════════════
*
* Audience: Young British Muslims (20s-30s) who run/volunteer
* for Islamic charities in the UK. Design-literate. Can smell
* stock photography instantly.
*
* Visual language: Amaliah magazine, Ramadan Tent Project,
* Penny Appeal's younger campaigns. Understated, cinematic,
* lived-in. NOT UN poster diversity. NOT Western gala.
*
* Rules:
* - NO alcohol/wine glasses. Ever. Water jugs, chai, dates.
* - Real British-Muslim spaces: community halls with strip
* lighting, cramped charity offices, the Overground, home.
* - Quiet expressions > exaggerated reactions
* - Max 2 images showing someone holding a phone
* - Cultural texture through environment, not costume
* - Camera is a fly on the wall. Nobody performs.
* - One coherent visual story: before → during → after
* ═══════════════════════════════════════════════════════════════
*/
const STYLE = `Photorealistic documentary photography. Shot on Sony A7III, shallow depth of field, available light only. Fly-on-the-wall candid — nobody is looking at the camera. No stock photo aesthetic. No staged poses. No bright studio lighting. No visible text or watermarks. No wine or alcohol.`;
const PHOTOS: PhotoSpec[] = [
// ─── HERO (3:4 portrait) ───────────────────────────────────
// Psychology: "This is MY world. These are MY people."
// The first image the visitor sees. Must create instant recognition.
{
filename: "hero-gala-moment.jpg",
prompt: `Generate a photorealistic photograph. Portrait orientation, 3:4 aspect ratio.
A young British-Muslim man, late 20s, neat trimmed beard, wearing a crisp white thobe with a dark blazer over it. He is standing at the front of a packed community hall during a charity fundraising appeal, speaking passionately with one hand slightly raised. He is mid-sentence, totally in the moment — not posing.
The camera is positioned BEHIND the seated audience, shooting through the crowd toward him. Blurred silhouettes of heads and shoulders in the dark foreground. The audience is seated at round tables with white tablecloths. On the tables: water jugs, paper plates, foil trays of biryani. NO wine, NO alcohol, NO candles.
The hall has basic overhead strip lighting mixed with some string fairy lights someone has hung along the wall. A rolled-up vinyl charity banner is leaning against the side wall. The ceiling has standard suspended ceiling tiles. This is a real UK community hall, not a hotel.
The audience is mixed — men in thobes, suits, casual clothes; women in hijabs and modest dresses. They are LISTENING, leaning forward, engaged. Natural warm tone from the tungsten strip lights.
85mm lens, f/1.4, the speaker is sharp, the foreground audience is beautiful creamy bokeh. ${STYLE}`,
},
// ─── PERSONA: FUNDRAISER (16:9) ───────────────────────────
// Psychology: "I am this person." Identity mirror.
// The young charity hustler — modern, grounded, purposeful.
{
filename: "persona-fundraiser-street.jpg",
prompt: `Generate a photorealistic photograph. Landscape orientation, 16:9 aspect ratio.
A young British-Muslim man, early 20s, short fade haircut, wearing a black North Face puffer jacket, dark joggers, and clean white Nike Air Force 1 trainers. He is walking along Whitechapel Road in East London on an overcast morning. One hand holds a paper coffee cup, the other is in his jacket pocket. He is looking ahead with quiet focus — not at the camera, not at a phone. Just walking with purpose.
The background shows the real Whitechapel streetscape: a halal butcher shop with Arabic and English signage, a red London bus in the far background, Victorian brick buildings, the grey overcast London sky. Pigeons on the pavement. A discarded Metro newspaper on a bench.
The colour palette is cool and muted — grey sky, dark jacket, white trainers popping. Morning light is flat and even, no harsh shadows.
50mm lens, f/2.0, shallow depth of field with the man sharp and the shopfronts softly blurred behind him. Street-level camera angle. ${STYLE}`,
},
// ─── PERSONA: TREASURER (16:9) ─────────────────────────────
// Psychology: "I am this person." The unglamorous reality.
// The volunteer treasurer drowning in spreadsheets.
{
filename: "persona-treasurer-community.jpg",
prompt: `Generate a photorealistic photograph. Landscape orientation, 16:9 aspect ratio.
A young British-Muslim woman, early 30s, wearing a plain black hijab and a simple dark jumper. She is sitting at a cramped desk in a tiny community centre office, rubbing her temples with one hand while staring down at a printed spreadsheet covered in highlighter marks. The expression is focused exhaustion — the face of someone reconciling pledge amounts at 10pm on a Tuesday.
The desk is genuinely messy: stacked brown envelopes, a clear plastic charity collection bucket with coins in it, a calculator, two empty mugs, a Tesco meal deal wrapper, scattered receipts, a battered ring binder. Behind her: a small window showing it is DARK outside (evening). A corkboard on the wall with pinned flyers — one says "Ramadan Food Drive" in English. Fluorescent tube light overhead giving that slightly blue-green office tint.
NO laptop screen visible. NO phone. Just paper, pen, and the weight of volunteer admin.
35mm lens, f/2.0, focused on her face, the desk clutter falling into gentle blur. ${STYLE}`,
},
// ─── STEP 1: CREATE LINK (16:9) ───────────────────────────
// Psychology: "This is easy to start." Low-stakes familiarity.
// The quiet preparation before an event.
{
filename: "step-create-link.jpg",
prompt: `Generate a photorealistic photograph. Landscape orientation, 16:9 aspect ratio.
Close-up of a pair of young brown hands taping a small printed A5 poster to a community hall wall with strips of masking tape. The poster has a simple QR code on it — no readable text, just the QR pattern. A roll of masking tape sits on a folding table below.
The background is out of focus but tells a story: stacked grey plastic chairs, folding tables being set up, a digital prayer timetable display on the wall showing prayer times, a fire exit sign. Someone in the far background is laying out white tablecloths on round tables.
The lighting is overhead fluorescent tubes — that familiar harsh community hall light. The wall is painted magnolia (that specific UK institutional cream colour).
The shot is tight on the hands and poster — we don't see a face. This is about the ACT of preparation, not the person. Intimate, quiet, mundane.
50mm macro, f/2.8, hands and poster sharp, background a soft blur of event setup. ${STYLE}`,
},
// ─── STEP 2: DONOR PLEDGES (16:9) ─────────────────────────
// Psychology: "This is the moment it works." Energy, momentum.
// The live appeal — the room is charged.
{
filename: "step-donor-pledges.jpg",
prompt: `Generate a photorealistic photograph. Landscape orientation, 16:9 aspect ratio.
Wide shot of a British-Muslim charity fundraising dinner mid-appeal. A large community hall filled with guests at round tables with white tablecloths. On the tables: water jugs, dates in small bowls, foil biryani trays, stacked paper plates, water bottles. Absolutely NO wine, NO alcohol, NO candles.
In the foreground, a middle-aged man in a suit is raising his hand — he is making a pledge. His table neighbours are looking at him, one person smiling. At the next table, a young woman in a patterned hijab is looking down at her phone, about to tap something. The phone screen is NOT visible to the camera.
At the far end of the hall, a speaker stands at a small podium with a projected screen behind showing a fundraising thermometer graphic. The room is ALIVE — people are animated, hands going up, conversations happening.
The lighting is standard community hall: overhead fluorescent strips mixed with some warm decorative fairy lights strung along the ceiling edges. A UK fire exit sign visible. This is a real community hall, not a hotel ballroom.
24mm wide lens, f/2.8, capturing the full scope and energy of the room. The man pledging in the foreground is sharpest, the rest falls into a busy, energetic blur. ${STYLE}`,
},
// ─── STEP 3: FOLLOW-UP NOTIFICATION (16:9) ────────────────
// Psychology: "It happens automatically, in daily life."
// The morning after. Effortless. Normal.
{
filename: "step-followup-notification.jpg",
prompt: `Generate a photorealistic photograph. Landscape orientation, 16:9 aspect ratio.
A young British-Muslim woman, mid-20s, wearing a beige trench coat and a dark hijab, sitting on the London Overground train during a morning commute. She is looking out the window with a small, private, contented half-smile — like she just remembered something good. She is NOT looking at a phone. Her AirPods are in. A tote bag is on the seat beside her.
Through the train window: blurred terraced houses and a grey London sky streaming past. The train interior is the distinctive orange-and-blue TfL Overground seating. A few other commuters visible in the blurred background, one reading a newspaper.
Morning flat light coming through the window, casting soft even illumination on her face. Cool colour palette — greys, beiges, the pop of orange seats.
85mm lens, f/1.8, shot from across the aisle. Her face is in sharp focus, everything else is soft. The feeling is: quiet, private, a good notification arrived and she doesn't need to do anything about it right now. ${STYLE}`,
},
// ─── STEP 4: MONEY ARRIVES (16:9) ─────────────────────────
// Psychology: "The payoff. It worked." Relief, not performance.
// Quiet satisfaction, not stock-photo celebration.
{
filename: "step-money-arrives.jpg",
prompt: `Generate a photorealistic photograph. Landscape orientation, 16:9 aspect ratio.
Two young British-Muslim people in a cramped community centre office. A young man in a plain crewneck and a young woman in a hijab and cardigan, both late 20s. They are sitting side by side at a desk. The man is leaning back slightly in his chair with a quiet exhale of relief — eyes closed for a second, the smallest smile, like a weight just lifted. The woman next to him is still looking at a printed sheet of paper on the desk, running her finger down a column of numbers.
This is NOT a screaming celebration. It is the private, quiet moment of "we actually did it." Understated. Real.
On the desk: the printed pledge summary, two mugs of tea (one is the classic brown PG Tips colour), a calculator, a pen. On the wall behind: a printed fundraising target poster with a hand-drawn thermometer that is coloured in to near the top. A small window shows street light outside — it is evening.
35mm lens, f/2.0, both faces in focus, shallow enough that the background details are legible but soft. Warm overhead fluorescent light. ${STYLE}`,
},
// ─── COMPLIANCE: MOSQUE HALL (16:9) ────────────────────────
// Psychology: "Scale. Trust. This is serious."
// The establishing shot. Gravitas.
{
filename: "compliance-mosque-hall.jpg",
prompt: `Generate a photorealistic photograph. Landscape orientation, 16:9 aspect ratio.
Grand wide interior shot of a large British mosque community hall during a major fundraising dinner. Over 150 guests seated at round tables with crisp white tablecloths. The architecture is distinctly British-mosque: ornate Islamic geometric plasterwork on the ceiling, large brass Moroccan-style pendant chandeliers casting warm golden light, arched windows with geometric stained glass.
On the tables: water jugs, dates, samosa platters, water bottles. NO wine, NO alcohol. The guests are a mix: older men in traditional thobes and topis, younger men in suits and smart-casual, women in hijabs of every colour and style. Families, not just adults.
In the middle ground, two young teenage volunteers in matching charity t-shirts are carrying trays of food between tables. The camera is positioned at the back of the hall shooting forward, capturing the full depth and grandeur of the space. Blurred silhouettes of standing figures in the immediate foreground frame the shot.
The mood is warm, communal, important. This is the biggest night of the year for this community. 24mm lens, f/2.8, available light from the chandeliers creating pools of warm gold. ${STYLE}`,
},
// ─── PAYMENT FLEX: HOME (1:1 square) ──────────────────────
// Psychology: "Donors pay on their terms. No pressure."
// Domestic, intimate, unhurried.
{
filename: "payment-flex-kitchen.jpg",
prompt: `Generate a photorealistic photograph. Square format, 1:1 aspect ratio.
A young British-Muslim couple, late 20s, on a sofa in their living room in the evening. She is wearing a loose headscarf and an oversized knit cardigan, reading a paperback book with her legs tucked under her. He is next to her, casually looking at his phone (phone screen NOT visible to camera — the phone is angled away). He has a short beard and is wearing a plain t-shirt.
Between them on the sofa is a small cushion. On the coffee table in front: two small glasses of Moroccan-style mint tea on a brass tray, a small bowl of dates, the TV remote. A floor lamp with a warm bulb casts a golden pool of light over them. The TV is off. The walls have a simple framed Arabic calligraphy print.
The mood is completely unhurried. Evening. Quiet. They are comfortable and there is no urgency. This is the opposite of a hard sell — it is "whenever you are ready."
50mm lens, f/1.8, focused on the couple, the coffee table details softly blurred in the foreground. Warm, intimate, still. ${STYLE}`,
},
];
async function generateImage(spec: PhotoSpec): Promise<void> {
const outPath = path.join(OUTPUT_DIR, spec.filename);
console.log(`\n🎨 Generating: ${spec.filename}`);
try {
const response = await fetch(ENDPOINT, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
contents: [{ parts: [{ text: spec.prompt }] }],
generationConfig: {
responseModalities: ["IMAGE", "TEXT"],
},
}),
});
if (!response.ok) {
const errText = await response.text();
console.error(` ❌ API error ${response.status}: ${errText.slice(0, 300)}`);
return;
}
const data: any = await response.json();
const candidates = data.candidates;
if (!candidates?.length) {
console.error(` ❌ No candidates returned`);
console.error(` Response: ${JSON.stringify(data).slice(0, 400)}`);
return;
}
const parts = candidates[0].content?.parts;
if (!parts) {
console.error(` ❌ No parts in candidate`);
console.error(` Finish reason: ${candidates[0].finishReason}`);
return;
}
const imagePart = parts.find((p: any) => p.inlineData?.mimeType?.startsWith("image/"));
if (!imagePart) {
// Check if there's a text explanation (usually safety filter)
const textPart = parts.find((p: any) => p.text);
if (textPart) {
console.error(` ❌ Text response instead of image: ${textPart.text.slice(0, 200)}`);
} else {
console.error(` ❌ No image part found`);
}
return;
}
const buffer = Buffer.from(imagePart.inlineData.data, "base64");
fs.writeFileSync(outPath, buffer);
console.log(`${spec.filename}${(buffer.length / 1024).toFixed(0)}KB`);
} catch (err: any) {
console.error(` ❌ Error: ${err.message}`);
}
}
async function main() {
console.log("═══════════════════════════════════════════════");
console.log(" PNPL Photography — Round 2");
console.log(" Young British-Muslim charity photography");
console.log(` Model: ${MODEL}`);
console.log(` Images: ${PHOTOS.length}`);
console.log("═══════════════════════════════════════════════");
for (const spec of PHOTOS) {
await generateImage(spec);
// Rate limit — be gentle
await new Promise((r) => setTimeout(r, 3000));
}
console.log("\n═══════════════════════════════════════════════");
console.log(" Done. Hard refresh your browser (Ctrl+Shift+R)");
console.log("═══════════════════════════════════════════════");
}
main();