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,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();