# Story media map — Wrong Number / Yearnkeep
**Source of truth:** `content/pilot/story.json`, character photo slots, `content/sms/templates.json`.
**Website today:** marketing cold-open + legal/permissions pages. Full graph still plays in the iOS app.

## Graph counts

| Type | Count |
| --- | ---: |
| `audio` | 16 |
| `choice` | 9 |
| `end` | 6 |
| `paywall` | 2 |
| `sms` | 63 |
| `visual_artifact` | 6 |
| `visual_photo` | 12 |
| `visual_profile` | 2 |

## Photo slots (character bible → files)

| Character | Slot | Unlock node | Premium | File |
| --- | --- | --- | --- | --- |
| julian | `profile_primary` | `sms_cold_open` | false | `uploads/profile_primary.webp` |
| julian | `cafe_exterior` | `audio_almost_meet` | false | `uploads/cafe_exterior.webp` |
| julian | `cafe_table` | `audio_almost_meet` | false | `uploads/cafe_table.webp` |
| julian | `work_sketch` | `sms_morning` | false | `uploads/work_sketch.webp` |
| julian | `studio_model` | `sms_morning` | false | `uploads/studio_model.webp` |
| julian | `rain_coat` | `audio_almost_meet` | false | `uploads/rain_coat.webp` |
| julian | `voice_note_cover` | `sms_cold_open` | false | `uploads/voice_note_cover.webp` |
| julian | `rooftop_dusk` | `paywall_gate` | true | `uploads/rooftop_dusk.webp` |
| julian | `night_window` | `paywall_gate` | true | `uploads/night_window.webp` |
| julian | `hallway_soft` | `paywall_gate` | true | `uploads/hallway_soft.webp` |
| julian | `halcyon_reading_room` | `visual_photo_halcyon_room` | true | `uploads/halcyon_reading_room.webp` |
| julian | `train_carriage` | `visual_artifact_train_tickets` | true | `uploads/train_carriage.webp` |
| claire | `claire_silhouette_cafe` | `sms_cold_open` | false | `uploads/claire_silhouette_cafe.webp` |
| claire | `claire_hands_phone` | `sms_cold_open` | false | `uploads/claire_hands_phone.webp` |
| claire | `claire_profile_soft` | `sms_cold_open` | false | `uploads/claire_profile_soft.webp` |

## Visual story nodes

| Node | Type | Premium | Title/caption |
| --- | --- | --- | --- |
| `visual_profile_julian` | visual_profile | false | A name she can search |
| `visual_photo_coffee` | visual_photo | false | Two cups, one almost-touch |
| `visual_photo_sidewalk` | visual_photo | false | Under the awning |
| `visual_artifact_tease` | visual_artifact | false | What waits past tonight |
| `visual_artifact_truth` | visual_artifact | true | The name on his phone |
| `visual_photo_rooftop_ticket` | visual_photo | true | Guest access · unlocked |
| `visual_photo_rooftop` | visual_photo | true | String lights · skyline |
| `visual_photo_night_window` | visual_photo | true | Lamp light · late |
| `visual_photo_almost` | visual_photo | true | After the inch |
| `visual_photo_ep2_cliff` | visual_photo | true | Halcyon House · daylight |
| `visual_profile_julian_after_work` | visual_profile | false | A name she can verify first |
| `visual_photo_evening_cafe` | visual_photo | false | Six o'clock, not twenty minutes |
| `visual_artifact_tease_messages` | visual_artifact | false | What another message might hold |
| `visual_after_coffee_walk` | visual_photo | false | One block, chosen |
| `visual_after_coffee_save` | visual_photo | false | Her exit, intact |
| `visual_after_coffee_walls` | visual_photo | false | A courteous distance |
| `visual_artifact_claire_grant` | visual_artifact | true | Claire's ten o'clock |
| `visual_artifact_episode2_timeline` | visual_artifact | true | The unedited timeline |
| `visual_photo_halcyon_room` | visual_photo | true | The room that stayed |
| `visual_artifact_train_tickets` | visual_artifact | true | Two tickets · one chosen day |

## Carrier SMS templates (optional · separate consent)

### `verification_code_v1` (verification)

- Body: Wrong Number: {{code}} is your verification code for optional fictional story texts. Expires in 10 minutes. Reply STOP to opt out.

### `julian_saved_place_meet_v1` (story)

- Body: Wrong Number fiction — Julian: I saved your place. Come back when you choose. Reply STOP to opt out; HELP for help. Msg & data rates may apply.
- Trigger: story `pilot-wrong-number` · node `free_end_hold` · delay 180 min

### `julian_saved_place_messages_v1` (story)

- Body: Wrong Number fiction — Julian: Messages are enough. Your place is saved whenever you want the next page. Reply STOP to opt out; HELP for help. Msg & data rates may apply.
- Trigger: story `pilot-wrong-number` · node `free_end_hold_messages` · delay 180 min

### `help_v1` (help)

- Body: Wrong Number: automated fictional story texts, up to 4 per chapter. Help: {{support_email}}. Reply STOP to opt out. Msg & data rates may apply.

## In-app SMS

63 SMS nodes in the graph (not carrier). Start node: `sms_cold_open`.

## Voice notes

19 prerecorded voice notes (in-app).

## Email

Email is **ops only** (waitlist, support, legal). Characters never email the player.

## Permissions

Canonical user-facing grant page: `/permissions.html`.
