center stat strip text
@@ -120,7 +120,7 @@ export default function HomePage() {
|
|||||||
<div className="max-w-7xl mx-auto">
|
<div className="max-w-7xl mx-auto">
|
||||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-px bg-gray-800/50">
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-px bg-gray-800/50">
|
||||||
{HERO_STATS.map((s) => (
|
{HERO_STATS.map((s) => (
|
||||||
<div key={s.stat} className="bg-gray-950 py-6 px-6">
|
<div key={s.stat} className="bg-gray-950 py-6 px-6 text-center">
|
||||||
<p className="text-xl md:text-2xl font-black text-white tracking-tight">{s.stat}</p>
|
<p className="text-xl md:text-2xl font-black text-white tracking-tight">{s.stat}</p>
|
||||||
<p className="text-[11px] text-gray-500 mt-1">{s.label}</p>
|
<p className="text-[11px] text-gray-500 mt-1">{s.label}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
BIN
screenshots/cr-brand/cr-fidya-new.jpg
Normal file
|
After Width: | Height: | Size: 340 KiB |
BIN
screenshots/cr-brand/cr-final.jpg
Normal file
|
After Width: | Height: | Size: 506 KiB |
BIN
screenshots/cr-brand/cr-hifz-meals.jpg
Normal file
|
After Width: | Height: | Size: 5.3 MiB |
BIN
screenshots/cr-brand/cr-hunger6-zakat.jpg
Normal file
|
After Width: | Height: | Size: 380 KiB |
BIN
screenshots/cr-brand/cr-tech-meals.jpg
Normal file
|
After Width: | Height: | Size: 2.8 MiB |
BIN
screenshots/cr-brand/cr-wrong-1.jpg
Normal file
|
After Width: | Height: | Size: 2.7 MiB |
BIN
screenshots/cr-brand/cr-wrong1-hero.png
Normal file
|
After Width: | Height: | Size: 2.4 MiB |
BIN
screenshots/fidya-kaffarah/fidya-hero.jpg
Normal file
|
After Width: | Height: | Size: 709 KiB |
208
scripts/generate-fidya-images.py
Normal file
@@ -0,0 +1,208 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Generate on-brand photography for the Charity Right Fidya/Kaffarah page
|
||||||
|
using Gemini Nano Banana Pro (Gemini 3 Pro Image Preview).
|
||||||
|
|
||||||
|
Brand style reference (from Ramadan homepage):
|
||||||
|
- Documentary/photojournalistic style
|
||||||
|
- Warm earth tones (sandy, ochre, warm brown palette)
|
||||||
|
- Natural golden hour lighting
|
||||||
|
- Shallow depth of field (subject sharp, background softly blurred)
|
||||||
|
- Dignified portraits of people in need (children, elderly, families)
|
||||||
|
- High quality, professional look
|
||||||
|
- Wide aspect ratio (~16:9)
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
import base64
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
# Load from .env
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
API_KEY = os.getenv("GEMINI_API_KEY")
|
||||||
|
MODEL = os.getenv("GEMINI_IMAGE_MODEL", "nano-banana-pro-preview")
|
||||||
|
|
||||||
|
if not API_KEY:
|
||||||
|
print("ERROR: GEMINI_API_KEY not set in .env")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
print(f"Using model: {MODEL}")
|
||||||
|
print(f"API Key: {API_KEY[:10]}...")
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
OUTPUT_DIR = Path("screenshots/fidya-kaffarah")
|
||||||
|
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
# Image prompts designed to match the CR brand photography style for fidya/kaffarah context
|
||||||
|
PROMPTS = [
|
||||||
|
{
|
||||||
|
"name": "fidya-hero",
|
||||||
|
"prompt": (
|
||||||
|
"Documentary photography, wide shot. An elderly South Asian grandmother "
|
||||||
|
"sitting peacefully in a modest home, warm golden hour light streaming through "
|
||||||
|
"a window, she has gentle wrinkles and wise eyes, wearing a simple white cotton "
|
||||||
|
"dupatta over her head. A plate of simple food (rice and lentils) sits beside her. "
|
||||||
|
"Warm earth tones, shallow depth of field, photojournalistic style, "
|
||||||
|
"shot on 85mm lens, natural lighting. Dignified and compassionate mood. "
|
||||||
|
"16:9 aspect ratio, high resolution professional photograph."
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "fidya-feeding",
|
||||||
|
"prompt": (
|
||||||
|
"Documentary photography. A warm scene of food being served to people in need "
|
||||||
|
"at a community feeding program in rural Bangladesh. Focus on hands gently "
|
||||||
|
"placing a plate of rice and curry in front of a grateful elderly person. "
|
||||||
|
"Golden warm lighting, earth tones, shallow depth of field with other people "
|
||||||
|
"softly blurred in background. Photojournalistic style, authentic, dignified. "
|
||||||
|
"Shot on 50mm lens, natural light. 16:9 aspect ratio, professional photograph."
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "kaffarah-community",
|
||||||
|
"prompt": (
|
||||||
|
"Documentary photography. A community meal distribution scene in a rural village. "
|
||||||
|
"Several children and elderly people receiving meals from volunteers. "
|
||||||
|
"The scene is outdoors with dusty earth ground and simple structures in background. "
|
||||||
|
"Warm golden hour sunlight, rich earth tones (ochre, sand, brown). "
|
||||||
|
"Focus on a child holding a bowl of food, looking up with hopeful eyes. "
|
||||||
|
"Shallow depth of field, photojournalistic authenticity. "
|
||||||
|
"Shot on 35mm lens. 16:9 aspect ratio, professional photograph."
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "fidya-quran-elder",
|
||||||
|
"prompt": (
|
||||||
|
"Documentary photography, intimate portrait. An elderly Muslim man with a white beard "
|
||||||
|
"and simple prayer cap, sitting cross-legged on the floor in a humble room, "
|
||||||
|
"holding prayer beads (tasbih). Soft warm natural light from a side window "
|
||||||
|
"illuminating his weathered, peaceful face. Warm earth tone palette, "
|
||||||
|
"shallow depth of field, background is a simple mud-colored wall. "
|
||||||
|
"Dignified, contemplative, spiritual mood. Shot on 85mm lens. "
|
||||||
|
"16:9 aspect ratio, high resolution professional photograph."
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "fidya-child-meal",
|
||||||
|
"prompt": (
|
||||||
|
"Documentary photography. A young child in South Asia or East Africa, "
|
||||||
|
"around 6-8 years old, sitting at a simple wooden table eating a plate of food "
|
||||||
|
"(rice and vegetables). The child has a subtle, content smile. "
|
||||||
|
"Warm golden natural light, earth tones (sandy, ochre), background is softly "
|
||||||
|
"blurred showing a simple classroom or community center. "
|
||||||
|
"Shallow depth of field, photojournalistic style. Dignified, hopeful. "
|
||||||
|
"Shot on 50mm lens. 16:9 aspect ratio, professional photograph."
|
||||||
|
)
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def generate_image(prompt_data):
|
||||||
|
"""Generate an image using Gemini Nano Banana Pro."""
|
||||||
|
name = prompt_data["name"]
|
||||||
|
prompt = prompt_data["prompt"]
|
||||||
|
|
||||||
|
url = f"https://generativelanguage.googleapis.com/v1beta/models/{MODEL}:generateContent?key={API_KEY}"
|
||||||
|
|
||||||
|
payload = {
|
||||||
|
"contents": [
|
||||||
|
{
|
||||||
|
"parts": [
|
||||||
|
{"text": prompt}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"generationConfig": {
|
||||||
|
"responseModalities": ["TEXT", "IMAGE"],
|
||||||
|
"temperature": 1.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
headers = {"Content-Type": "application/json"}
|
||||||
|
|
||||||
|
print(f"\n[*] Generating: {name}")
|
||||||
|
print(f" Prompt: {prompt[:80]}...")
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.post(url, json=payload, headers=headers, timeout=120)
|
||||||
|
|
||||||
|
if response.status_code != 200:
|
||||||
|
print(f" [ERR] Error {response.status_code}: {response.text[:300]}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
data = response.json()
|
||||||
|
|
||||||
|
# Extract image from response
|
||||||
|
candidates = data.get("candidates", [])
|
||||||
|
if not candidates:
|
||||||
|
print(f" [ERR] No candidates in response")
|
||||||
|
print(f" Response: {json.dumps(data, indent=2)[:500]}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
parts = candidates[0].get("content", {}).get("parts", [])
|
||||||
|
|
||||||
|
for part in parts:
|
||||||
|
if "inlineData" in part:
|
||||||
|
image_data = part["inlineData"]
|
||||||
|
mime_type = image_data.get("mimeType", "image/png")
|
||||||
|
b64_data = image_data["data"]
|
||||||
|
|
||||||
|
# Determine extension
|
||||||
|
ext = "png" if "png" in mime_type else "jpg"
|
||||||
|
output_path = OUTPUT_DIR / f"{name}.{ext}"
|
||||||
|
|
||||||
|
# Decode and save
|
||||||
|
img_bytes = base64.b64decode(b64_data)
|
||||||
|
output_path.write_bytes(img_bytes)
|
||||||
|
|
||||||
|
size_kb = len(img_bytes) / 1024
|
||||||
|
print(f" [OK] Saved: {output_path} ({size_kb:.0f} KB)")
|
||||||
|
return str(output_path)
|
||||||
|
|
||||||
|
# If we get here, no image was found
|
||||||
|
print(f" [ERR] No image in response parts")
|
||||||
|
for part in parts:
|
||||||
|
if "text" in part:
|
||||||
|
print(f" Text response: {part['text'][:200]}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f" [ERR] Exception: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
print("=" * 60)
|
||||||
|
print("Charity Right — Fidya/Kaffarah Photography Generator")
|
||||||
|
print(f"Model: {MODEL}")
|
||||||
|
print("=" * 60)
|
||||||
|
|
||||||
|
results = []
|
||||||
|
for i, prompt_data in enumerate(PROMPTS):
|
||||||
|
result = generate_image(prompt_data)
|
||||||
|
results.append((prompt_data["name"], result))
|
||||||
|
|
||||||
|
# Small delay between requests to avoid rate limiting
|
||||||
|
if i < len(PROMPTS) - 1:
|
||||||
|
print(" ... Waiting 3s before next generation...")
|
||||||
|
time.sleep(3)
|
||||||
|
|
||||||
|
print("\n" + "=" * 60)
|
||||||
|
print("RESULTS SUMMARY")
|
||||||
|
print("=" * 60)
|
||||||
|
for name, path in results:
|
||||||
|
status = f"[OK] {path}" if path else "[FAIL] Failed"
|
||||||
|
print(f" {name}: {status}")
|
||||||
|
|
||||||
|
successful = sum(1 for _, p in results if p)
|
||||||
|
print(f"\n {successful}/{len(results)} images generated successfully")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||