# Demo companions — the portrait prompt pack

The eight companions on `/browse` and `/p/<slug>` are **fictional**, and so are their portraits: each was made by an image model from the prompt printed below. One prompt = one image = one person. Nothing here is a photograph of anybody.

**Doctrine (`apps/paiduay/docs/MVP-BRIEF.md`).** A real seller's plate is HER OWN photo, uploaded with her consent, never generated and never retouched. Generated faces are allowed in exactly one place — these eight invented people — and only because every plate that draws one prints the provenance beside it:

| | |
|---|---|
| Thai | ตัวละครสมมติ · ภาพสร้างด้วย AI |
| English | Fictional character · AI-made picture |

That is `v3.photoAi` in `apps/paiduay/content/copy.ts`, drawn by `components/v3/plate.tsx` as a `figcaption` in an AA scrim band at the foot of the plate, on `/browse` and on `/p/<slug>`. It is gated on `CardPhoto.generated === true` (`lib/card-model.ts`), which is set only on these eight records in `content/providers.ts` — so a real companion's photo can never print it, and an AI picture can never be shown without it.

Made 2026-09-18 with **Higgsfield → `gpt_image_2_5`** (OpenAI GPT Image 2.5, variant `flare`), `aspect_ratio: "4:5"`, `quality: "high"`, `resolution: "2k"` → 1792×2240 PNG, 3 credits each. Any model that takes a long text prompt and a 4:5 frame will do; the prompts are written to be portable, with no model-specific syntax.

## The style block

Every prompt ends with the same block, and it is the half that matters — it is what keeps eight different people looking like one photographer shot them on one afternoon:

> Natural daylight only, shot on a 50mm lens at f/2.8 with a softly blurred background. Medium shot from the chest up with clear headroom above her head, vertical 4:5 framing, subject slightly off-centre. Warm, friendly, relaxed expression, a small closed-mouth smile, eyes looking just past the camera lens. Ordinary modest everyday clothes, no makeup styling, hair as it would be on a normal day. Colour and skin texture like an unretouched phone photo: visible pores, no beauty smoothing, no studio lighting, no glamour, no fashion-editorial posing, fully clothed, shoulders covered. No legible text anywhere, no signboards or lettering in frame, no watermark, no logo. No other faces visible: anyone else in the background is distant and turned away.

Vary "her/she" for the men, and swap the expression clause for the one the person's voice implies (Gun is quiet, Beam is mid-laugh, Nok is generous). Everything else stays word for word.

And the half before it, per person: **age and sex exactly as the bio states them** (these are not eight women in their twenties — they are 22 to 38, five women and three men), a build, a hair description, a skin tone, an outfit **carrying her plate's hue** (`avatar.hue` in `content/providers.ts` — a bag, a wall, a shirt), one thing in her hands from her own listing, and a real Bangkok place taken from her `areas` and `activities`. That is what makes eight strangers rather than one face eight times.

## Reject rules

Read every generated image before it goes near the repo. Regenerate — up to two retries — if it is:

1. **sexualised or glamour-styled** in any degree: swimwear, cleavage, a bedroom, a bar, night, "beautiful woman" lighting, a model's pose;
2. carrying **visible text**: signage, a price card, a watermark, a logo, gibberish lettering on a box or a shirt;
3. **recognisable** as a real person, or wearing an identifiable brand;
4. **off the stated age or sex** — the bio is the spec, not the picture;
5. off-spec on the style block: studio light, retouched skin, a second face in focus, a crop with no headroom, a landscape frame.

Then read it again at full resolution over the busy corners (a shopfront, a shelf, a market stall): 900 px previews hide small lettering.

## The file spec

Each portrait ships as three committed files under `apps/paiduay/public/people/`, the same shape the two demo CARD photos use in `public/cards/` one size up:

| File | Size | Made with |
|---|---|---|
| `<slug>.jpg` | 1280×1600 | sharp, `jpeg({ quality: 82, chromaSubsampling: "4:4:4", mozjpeg: true })` |
| `<slug>.webp` | 1280×1600 | sharp, `webp({ quality: 82 })` |
| `<slug>-640.webp` | 640×800 | sharp, `webp({ quality: 80 })` |

sharp writes no metadata unless asked, so EXIF and GPS never survive the resize (`.withMetadata()` is deliberately absent). `components/card/plate.ts` turns `/people/<slug>.jpg` into the `<picture>` candidates — WebP first, the JPEG as the fallback, `sizes="(min-width: 720px) 44vw, 100vw"`; the deck's eight plates load lazily. The record in `content/providers.ts` carries `src`, a bilingual `alt`, a `focal` point (the face: the deck crops the 4:5 plate to a wide band, and the focal is what keeps her in it) and `generated: true`.

Regenerating one person: run her prompt, take the 4:5 output, then

```
sharp(src).resize(1280, 1600, { fit: "cover", position: "centre" })   // + the 640×800 twin
```

and drop the three files in place under her slug. Nothing else changes: the record already points at them.

## The eight prompts

Each is the person-half; append the style block above to every one.

### ploy — พลอย, 24, hue 340 (pink), cafes and photos around Ari

> A candid documentary photograph of a Thai woman aged 24, slim build, long straight black hair with a middle part, medium-light warm skin tone, small silver stud earrings, wearing a loose plain cream cotton t-shirt with a deep pink canvas tote bag over one shoulder, holding a phone loosely in one hand. She is standing inside a small plant-filled independent coffee shop in the Ari neighbourhood of Bangkok: wooden counter, hanging pothos and monstera leaves, a pink-painted wall panel behind her, late-morning daylight coming through a window from the left.

### tae — เต้, 27, man, hue 200 (blue), running and the gym around Sathorn

> A candid documentary photograph of a Thai man aged 27, lean athletic runner's build, short cropped black hair, tanned medium skin tone, light stubble, wearing a plain steel-blue technical running t-shirt with a sports watch on his wrist and a small white towel over one shoulder, slightly out of breath after a run. He is standing on a paved path in a large Bangkok city park just after sunrise: big banyan trees, a lake and mown grass behind him, low warm side light.

*(expression clause: "a small open smile"; skin clause: "visible pores and a little sweat")*

### nan — แนน, 31, hue 20 (terracotta), a plus-one at weddings

> A candid documentary photograph of a Thai woman aged 31, medium build, shoulder-length dark brown hair tucked behind one ear, light-medium skin tone, small gold stud earrings, wearing a terracotta-orange short-sleeved blouse buttoned high with a small fabric clutch held in both hands. She is standing on the open-air garden terrace of a Bangkok hotel that is being set up for an afternoon wedding reception: rows of white folding chairs, potted palms and a ribbon-tied arch softly blurred behind her, bright hazy afternoon light.

*(expression clause: "warm, friendly, composed"; clothes clause: "ordinary modest smart clothes, restrained makeup")*

### beam — บีม, 22, man, hue 260 (violet), board games around Siam and Ladprao

> A candid documentary photograph of a Thai man aged 22, slightly stocky student build, thick black hair a little messy, medium skin tone, round metal-framed glasses, wearing a plain violet cotton t-shirt, sitting at a wooden table and leaning forward on his elbows. He is inside a board-game cafe in Bangkok: tall shelves stacked with plain unmarked board-game boxes behind him, a partly set-up board game with wooden pieces in the blurred foreground, warm afternoon daylight from a window on the right.

*(expression clause: "cheerful, an open smile mid-conversation"; add "no lettering on the boxes")*

### plai — ปลาย, 29, hue 40 (mustard), old-town temples and museums

> A candid documentary photograph of a Thai woman aged 29, petite build, black hair gathered in a low bun, light-medium skin tone, no jewellery, wearing a mustard-gold short-sleeved button-up shirt buttoned high with the shoulders fully covered and a plain canvas satchel strap across her chest. She is standing in the courtyard of an old Buddhist temple in the Rattanakosin old town of Bangkok: a whitewashed wall and ochre roof tiles behind her, strong midday sun with dappled shade from a tree overhead.

*(expression clause: "thoughtful"; clothes clause: "ordinary modest everyday clothes suitable for a temple")*

### mew — มิว, 26, hue 300 (orchid), Japanese-speaking company around Phrom Phong

> A candid documentary photograph of a Thai woman aged 26, tall slim build, shoulder-length black hair with a soft fringe, fair skin tone, a thin silver necklace, wearing a soft lilac-orchid short-sleeved blouse and carrying a woven shopping basket on one arm. She is standing in a weekend fresh-food market near Phrom Phong in Bangkok: stalls of fruit, leafy vegetables and cut flowers blurred behind her under a canopy, bright overcast daylight.

*(expression clause: "bright, a small open smile"; add "no price signs")*

### gun — กัน, 33, man, hue 160 (teal), Yaowarat and hospital company, in Chinese

> A candid documentary photograph of a Thai man aged 33, broad build, short black hair combed flat, medium-tan skin tone, wearing a plain teal-green polo shirt and holding a clear plastic bag of mandarin oranges in one hand. He is standing in a narrow Chinatown side street in the Yaowarat district of Bangkok in the late afternoon: red and gold fabric awnings and plain hanging paper lanterns blurred behind him, no signboards in frame, warm low side light.

*(expression clause: "quiet, reserved, kind, a faint closed-mouth half-smile"; add "no Chinese or Thai lettering")*

### nok — ป้านก, 38, hue 80 (lime), the dawn markets

> A candid documentary photograph of a Thai woman who is exactly 38 years old and looks 38, not older: smooth skin with only faint smile lines, no grey hair, sturdy build, black hair pulled back in a plastic clip, medium skin tone, laughing eyes, wearing a lime-green patterned short-sleeved cotton blouse with a large woven plastic market basket over one arm. She is standing in a Bangkok flower market at dawn: stacked baskets of orange marigolds and white jasmine garlands behind her, cool early morning light mixed with a faint fluorescent glow from the stalls.

*(expression clause: "warm, generous, an open laughing smile"; clothes clause: "ordinary modest everyday market clothes, no makeup"; add "no price signs")*

**The one retry, and why.** Nok's first pass asked only for "aged 38" plus "a few greys" and "smile lines", and came back reading mid-to-late forties — a fine photograph of the wrong person, since her bio says 38 and twelve years behind a curry pot, not a generation more. The prompt above is the second pass: the age is stated twice, once as a number and once as an instruction ("and looks 38, not older"), and the ageing cues are named and removed. That is the general fix when a model drifts on age — say the number, then say the direction.

The other seven passed on the first generation.
