# THIS YOU — SCENE Variant Architecture

## Dev implementation specification

**Purpose:** add support for personalized SCENE object variants without creating separate male/female page layouts or duplicating entire scenes.

The goal is to keep **one editorial scene and one layout**, while allowing individual object slots inside the scene to resolve to different content variants based on the reader profile.

This change affects both:

1. the THIS YOU application / renderer / CMS;
2. the issue template format used to create and import new magazine issues.

---

# 1. Product rule

A SCENE should remain a single editorial page.

Do **not** create separate `male_scene` and `female_scene` page types.

Instead use:

```text
SCENE
  = shared editorial content
  + shared layout
  + object slots
  + optional personalized variants per slot
```

Example:

```text
NYFW SCENE

Shared:
- title
- intro copy
- page layout
- coffee
- phone
- camera

Variant objects:
- male -> watch
- female -> lipstick

- male -> loafers
- female -> ballet flats
```

This preserves the editorial idea:

> Same scene. Same moment. Different you.

---

# 2. Important architectural decision

## Use generic `variants`, not hardcoded male/female columns

The data structure should support variants generically.

Recommended:

```json
{
  "variants": {
    "default": {},
    "male": {},
    "female": {}
  }
}
```

Do **not** make the core architecture depend permanently on fields such as:

```text
male_image
female_image
male_caption
female_caption
```

Reason: later THIS YOU may use other personalization dimensions, for example:

```text
minimal
bold
classic
sporty
luxury
```

The first implementation only needs to resolve:

```text
default
male
female
```

but the structure should not prevent additional keys later.

---

# 3. Existing SCENE behavior must continue to work

Current issue templates may contain objects similar to:

```json
{
  "slot": 4,
  "image": "watch.png",
  "caption": "A good watch"
}
```

This must remain valid.

Treat legacy objects as equivalent to:

```json
{
  "slot": 4,
  "variants": {
    "default": {
      "image": "watch.png",
      "caption": "A good watch"
    }
  }
}
```

**Do not require existing AUTUMN/WINTER issue files to be rewritten before the system works.**

Backward compatibility is required.

---

# 4. Proposed SCENE object schema

A scene contains a list of object slots.

Recommended structure:

```json
{
  "type": "scene",
  "slug": "nyfw-street",
  "title": "NEW YORK FASHION WEEK",
  "objects": [
    {
      "slot": 1,
      "variants": {
        "default": {
          "image": "scene/nyfw/coffee.png",
          "caption": "Coffee before the first show"
        }
      }
    },
    {
      "slot": 2,
      "variants": {
        "default": {
          "image": "scene/nyfw/phone.png",
          "caption": "Your whole schedule in one hand"
        }
      }
    },
    {
      "slot": 4,
      "variants": {
        "male": {
          "image": "scene/nyfw/watch.png",
          "caption": "Something precise at the wrist"
        },
        "female": {
          "image": "scene/nyfw/lipstick.png",
          "caption": "One color change before the next show"
        }
      }
    },
    {
      "slot": 5,
      "variants": {
        "default": {
          "image": "scene/nyfw/sunglasses.png",
          "caption": "The useful kind of anonymity"
        },
        "female": {
          "image": "scene/nyfw/sunglasses-cat-eye.png",
          "caption": "The useful kind of anonymity"
        }
      }
    }
  ]
}
```

---

# 5. Variant resolution logic

The renderer should receive or already know the reader profile.

For the first version use:

```text
reader.gender = male | female | null
```

For every scene object slot:

```text
1. determine requested variant from reader profile
2. if requested variant exists -> use it
3. otherwise use default variant
4. otherwise use legacy object data if present
5. otherwise do not render the slot
```

Pseudo-code:

```php
function resolveSceneObject($object, $reader) {
    $variantKey = null;

    if (!empty($reader['gender'])) {
        $variantKey = strtolower($reader['gender']);
    }

    if (
        $variantKey &&
        isset($object['variants'][$variantKey])
    ) {
        return $object['variants'][$variantKey];
    }

    if (isset($object['variants']['default'])) {
        return $object['variants']['default'];
    }

    // legacy format
    if (isset($object['image']) || isset($object['caption'])) {
        return [
            'image' => $object['image'] ?? null,
            'caption' => $object['caption'] ?? null,
            'scale' => $object['scale'] ?? null,
            'x_offset' => $object['x_offset'] ?? null,
            'y_offset' => $object['y_offset'] ?? null,
        ];
    }

    return null;
}
```

Important:

**Do not duplicate this logic in multiple templates.**

Create one central resolver/helper/service and use it for preview, reader view and PDF generation.

---

# 6. Layout behavior

The page layout must NOT depend on which gender variant is selected.

Each slot keeps the same bounding box.

Example:

```text
slot 1 -> same x/y/width/height
slot 2 -> same x/y/width/height
slot 3 -> same x/y/width/height
...
```

Only the object inside the slot changes.

Recommended CSS behavior:

```css
.scene-object-image {
    width: 100%;
    height: 100%;
    object-fit: contain;
    object-position: center;
}
```

Transparent PNG / WebP assets are preferred.

Do not use `cover` for scene objects.

The purpose is to preserve the full object and avoid cropping.

---

# 7. Per-variant visual overrides

Different objects may have very different proportions.

Example:

```text
lipstick -> tall / narrow
watch -> compact / wide
shoe -> horizontal
bag -> almost square
```

Therefore each variant should optionally support small visual corrections.

Recommended fields:

```json
{
  "image": "lipstick.png",
  "caption": "One color change before the next show",
  "scale": 0.88,
  "x_offset": 4,
  "y_offset": -8
}
```

Defaults:

```text
scale = 1
x_offset = 0
y_offset = 0
```

Suggested transform:

```css
transform:
    translate(var(--x-offset), var(--y-offset))
    scale(var(--scale));
```

Offsets should use one consistent unit throughout the app.

Recommended for CMS/data:

```text
x_offset / y_offset = pixels in the base layout coordinate system
```

If the current renderer already uses another coordinate system, keep that system instead of introducing a second one.

---

# 8. Caption handling

Captions can also be variant-specific.

For example:

```json
"variants": {
  "male": {
    "image": "watch.png",
    "caption": "Something precise at the wrist"
  },
  "female": {
    "image": "lipstick.png",
    "caption": "One color change before the next show"
  }
}
```

If the image differs but the caption should remain shared, allow fallback from the selected variant to the default caption.

Recommended property-level fallback:

```text
selected variant image -> selected variant image
selected variant caption missing -> default caption
selected variant scale missing -> default scale or 1
```

Pseudo-code:

```php
$selected = $variants[$variantKey] ?? [];
$default  = $variants['default'] ?? [];

$result = [
    'image' => $selected['image'] ?? $default['image'] ?? null,
    'caption' => $selected['caption'] ?? $default['caption'] ?? null,
    'scale' => $selected['scale'] ?? $default['scale'] ?? 1,
    'x_offset' => $selected['x_offset'] ?? $default['x_offset'] ?? 0,
    'y_offset' => $selected['y_offset'] ?? $default['y_offset'] ?? 0,
];
```

This makes templates much less repetitive.

---

# 9. Example: shared object + female override

This should be valid:

```json
{
  "slot": 3,
  "variants": {
    "default": {
      "image": "sunglasses.png",
      "caption": "A little distance from the crowd"
    },
    "female": {
      "image": "cat-eye-sunglasses.png"
    }
  }
}
```

Female resolves to:

```text
image   = cat-eye-sunglasses.png
caption = A little distance from the crowd
```

Male resolves entirely to `default`.

---

# 10. Example: male/female only, no default

Also valid:

```json
{
  "slot": 4,
  "variants": {
    "male": {
      "image": "watch.png",
      "caption": "The small detail that finishes it"
    },
    "female": {
      "image": "lipstick.png",
      "caption": "The small detail that changes it"
    }
  }
}
```

If reader gender is unknown and no `default` exists:

```text
skip the slot
```

Do not show a broken image.

The validator should warn about this situation.

---

# 11. Reader profile

The renderer needs one normalized variant value.

Recommended normalized output:

```text
male
female
null
```

Do not let SCENE templates inspect raw form answers directly.

Create a helper such as:

```php
getReaderSceneVariant($reader)
```

For V1 it can simply return normalized gender.

Later this function can become more sophisticated without changing issue templates.

Example future possibility:

```php
getReaderSceneVariants($reader)
```

could return:

```json
[
  "female",
  "minimal",
  "sporty"
]
```

No need to build that future multi-rule engine now.

Just avoid an architecture that makes it impossible.

---

# 12. Issue template changes

Upgrade the issue template format to a new version.

Recommended:

```text
THIS_YOU_ISSUE_TEMPLATE_v3
```

or increment the current internal schema version if a formal version already exists.

Add a top-level schema/version marker if one does not already exist:

```json
{
  "schema_version": 3,
  "issue": {
    "slug": "autumn-2026"
  }
}
```

The importer should detect:

```text
schema v2 / legacy scene objects -> supported
schema v3 -> variants supported
```

Do not create separate import paths if avoidable.

Normalize legacy data into the new internal representation during import/runtime.

---

# 13. Recommended asset folder structure

Keep scene variant assets together by scene.

Example:

```text
assets/
  scenes/
    nyfw/
      shared/
        coffee.png
        phone.png
        camera.png
      male/
        watch.png
        loafers.png
      female/
        lipstick.png
        ballet-flats.png
```

This is mainly for human maintainability.

The renderer should rely on the JSON path, not infer gender from folder names.

Alternative valid structure:

```text
assets/scenes/nyfw/coffee.png
assets/scenes/nyfw/watch-male.png
assets/scenes/nyfw/lipstick-female.png
```

Pick one convention and document it in the issue template manual.

Preferred: separate `shared`, `male`, `female` folders because it is clearer while producing issues manually.

---

# 14. CMS / admin changes

The SCENE editor should not require two completely separate scene forms.

For every slot show:

```text
SLOT 4

Default
[ image ]
[ caption ]
[ scale ] [ x ] [ y ]

Male
[ image ]
[ caption ]
[ scale ] [ x ] [ y ]

Female
[ image ]
[ caption ]
[ scale ] [ x ] [ y ]
```

However, to keep the UI clean, male/female sections can be collapsed by default.

Recommended UX:

```text
Default object

+ Add male variant
+ Add female variant
```

After adding one:

```text
Female variant
[ image ]
[ optional caption override ]
[ scale / x / y ]
[ remove variant ]
```

This prevents the admin from feeling three times larger for every scene.

---

# 15. Preview switcher

The admin preview should allow developers/editors to inspect all variants without changing a real reader profile.

Add a preview selector:

```text
SCENE PREVIEW

[ Default ] [ Male ] [ Female ]
```

This should override the reader variant **only inside preview mode**.

It is important for editorial QA because otherwise female-specific assets can easily remain unseen.

---

# 16. PDF generation

The same variant resolver must be used by PDF output.

Expected flow:

```text
reader profile
  -> reader scene variant
  -> scene resolver
  -> final resolved scene object list
  -> HTML/layout
  -> PDF
```

Do not generate a generic scene first and attempt to swap images inside the PDF afterward.

Personalization must happen before the page is rendered.

---

# 17. Import validation

Update issue validation rules.

For each SCENE object:

### Error

Fail validation when:

```text
slot is missing
variants exists but is not an object/map
image path points to a missing asset
scale is invalid/non-numeric
x/y offsets are invalid
```

### Warning

Warn when:

```text
male/female variants exist but no default exists
selected variant contains no image and default contains no image
same slot number appears twice
variant key is unknown
```

Unknown variant keys should probably be allowed with a warning rather than rejected, so future template extensions are possible.

---

# 18. Legacy normalization

Recommended implementation: normalize scene object structures before rendering.

Example:

```php
function normalizeSceneObject(array $object): array
{
    if (!isset($object['variants'])) {
        return [
            'slot' => $object['slot'] ?? null,
            'variants' => [
                'default' => [
                    'image' => $object['image'] ?? null,
                    'caption' => $object['caption'] ?? null,
                    'scale' => $object['scale'] ?? 1,
                    'x_offset' => $object['x_offset'] ?? 0,
                    'y_offset' => $object['y_offset'] ?? 0,
                ]
            ]
        ];
    }

    return $object;
}
```

Then the rest of the application only deals with the new normalized format.

This is better than carrying legacy conditionals throughout the renderer.

---

# 19. Database approach

If SCENE page content is currently stored as JSON, the preferred approach is to keep variants inside the existing JSON document.

Do **not** create new relational tables solely for male/female scene objects unless the existing CMS architecture already models each scene object as its own database entity.

Preferred when content is JSON-based:

```text
scene_page.content_json
  -> objects
     -> slot
     -> variants
```

If objects already live in a relational table, use a child variant table rather than adding many gender-specific columns.

Example:

```text
scene_objects
- id
- scene_id
- slot

scene_object_variants
- id
- scene_object_id
- variant_key
- image
- caption
- scale
- x_offset
- y_offset
```

Unique constraint:

```text
(scene_object_id, variant_key)
```

Again: do not use columns such as `male_image`, `female_image` if the current model can reasonably support generic variants.

---

# 20. API/internal resolved format

After personalization, the renderer should ideally receive a simple resolved object.

Example:

```json
{
  "slot": 4,
  "image": "scene/nyfw/lipstick.png",
  "caption": "One color change before the next show",
  "scale": 0.88,
  "x_offset": 4,
  "y_offset": -8,
  "resolved_variant": "female"
}
```

The page template should not need to know about all available variants.

It should only render the selected result.

This separation is important:

```text
personalization logic != visual layout logic
```

---

# 21. Existing SCENE layout compatibility

Do not redesign the existing SCENE grid as part of this task unless necessary.

The existing slots, typography, captions, spacing and object positioning should remain intact.

This feature should primarily change:

```text
which asset is placed into a slot
```

not:

```text
where the slot exists
```

This keeps the implementation small and reduces regression risk.

---

# 22. Empty slot behavior

A slot with no resolved object must disappear cleanly.

Do not render:

```text
broken image icon
empty caption
placeholder border
```

If the grid requires a fixed number of items for composition, retain the empty grid cell but render it visually empty.

Which option is correct depends on the current scene CSS.

Prefer preserving layout geometry so switching gender does not cause the whole page to reflow.

---

# 23. Recommended rule for THIS YOU editorial production

When building new issues:

### Use `default` for genuinely shared objects

Examples:

```text
coffee
phone
camera
book
headphones
festival wristband
metro card
wine glass
museum ticket
```

### Add gender variants only when editorially useful

Examples:

```text
lipstick / grooming object
jewelry
bags
shoes
fashion accessories
certain fragrance objects
```

Do not force every object into male/female versions.

A scene can contain:

```text
6 shared + 2 personalized
```

or:

```text
4 shared + 4 personalized
```

or:

```text
8 shared
```

This should be editorially flexible.

---

# 24. Template authoring example

Full example:

```json
{
  "type": "scene",
  "slug": "nyfw-street-style",
  "title": "THE STREET BEFORE THE SHOW",
  "subtitle": "New York Fashion Week",
  "objects": [
    {
      "slot": 1,
      "variants": {
        "default": {
          "image": "assets/scenes/nyfw/shared/coffee.png",
          "caption": "Coffee before the first show"
        }
      }
    },
    {
      "slot": 2,
      "variants": {
        "default": {
          "image": "assets/scenes/nyfw/shared/phone.png",
          "caption": "Everything moves through the phone"
        }
      }
    },
    {
      "slot": 3,
      "variants": {
        "default": {
          "image": "assets/scenes/nyfw/shared/sunglasses.png",
          "caption": "A little distance from the crowd"
        }
      }
    },
    {
      "slot": 4,
      "variants": {
        "male": {
          "image": "assets/scenes/nyfw/male/watch.png",
          "caption": "Something precise at the wrist",
          "scale": 0.92
        },
        "female": {
          "image": "assets/scenes/nyfw/female/lipstick.png",
          "caption": "One color change before the next show",
          "scale": 0.84,
          "y_offset": -4
        }
      }
    },
    {
      "slot": 5,
      "variants": {
        "male": {
          "image": "assets/scenes/nyfw/male/loafers.png",
          "caption": "Shoes that can handle the whole day"
        },
        "female": {
          "image": "assets/scenes/nyfw/female/ballet-flats.png",
          "caption": "Shoes that can handle the whole day"
        }
      }
    },
    {
      "slot": 6,
      "variants": {
        "default": {
          "image": "assets/scenes/nyfw/shared/camera.png",
          "caption": "Everyone is documenting everyone"
        }
      }
    }
  ]
}
```

---

# 25. Template manual update

The issue template documentation should gain a new section:

```text
SCENE PERSONALIZATION
```

It should explain:

1. what `default` means;
2. how to add `male` and `female` variants;
3. that every slot does not need variants;
4. asset folder conventions;
5. `scale`, `x_offset`, `y_offset`;
6. fallback behavior;
7. previewing both variants;
8. validation rules.

Include at least one ready-to-copy JSON example.

---

# 26. Implementation order

Recommended order:

## Phase 1 — data + resolver

- add normalized variants structure;
- add legacy normalization;
- implement central scene-object resolver;
- connect reader gender normalization.

## Phase 2 — renderer

- use resolved object assets/captions;
- support scale/x/y overrides;
- handle empty slot safely.

## Phase 3 — issue importer

- accept `variants` structure;
- validate asset paths;
- keep legacy issue support;
- increment schema/template version.

## Phase 4 — admin

- add variant controls per slot;
- add Default/Male/Female preview switcher;
- expose scale/offset controls.

## Phase 5 — issue template

- update master template ZIP;
- update manual MD;
- add example scene containing shared + male/female objects.

## Phase 6 — QA

Test at minimum:

```text
male reader
female reader
reader with no gender
legacy scene
new scene with only defaults
new scene with mixed defaults + variants
new scene where female exists but male falls back to default
new scene where no default exists
PDF output for male + female
admin preview for all three modes
```

---

# 27. Acceptance criteria

The feature is complete when all of the following are true.

### Rendering

- [ ] One SCENE page can render different object assets for male and female readers.
- [ ] Page layout remains identical between variants.
- [ ] Shared/default objects render for both.
- [ ] Missing gender-specific variants fall back correctly.
- [ ] Missing objects never produce broken-image UI.
- [ ] Captions support the same fallback system.
- [ ] Scale/x/y corrections work per variant.

### Compatibility

- [ ] Existing scene JSON still renders without modification.
- [ ] Existing AUTUMN/WINTER issue imports continue to work.
- [ ] PDF generation uses the same variant resolution as the web preview.

### Issue templates

- [ ] New issue templates accept `variants.default`, `variants.male`, `variants.female`.
- [ ] Template schema/version is incremented.
- [ ] Validator understands the new structure.
- [ ] Missing image assets are reported before commit/import.

### Admin

- [ ] Editor can add/remove male and female variants per slot.
- [ ] Editor does not need to duplicate the whole scene.
- [ ] Preview can force Default/Male/Female modes.

### Architecture

- [ ] Gender-selection logic exists in one central resolver.
- [ ] Layout templates receive already-resolved scene objects.
- [ ] No duplicated male/female page templates are introduced.
- [ ] No schema design based on permanent `male_*` / `female_*` database columns unless unavoidable due to existing architecture.

---

# 28. Non-goals for this patch

Do not expand the task into a full recommendation/personalization engine.

Not required now:

```text
style-based object selection
brand-based object selection
budget-based scene objects
AI-generated scene variants at runtime
multiple simultaneous personalization dimensions
per-reader automatic object generation
```

The architecture may allow these later, but V1 should stay simple:

```text
reader gender
  -> variant key
  -> slot variant
  -> fallback to default
```

---

# 29. Final architectural summary

Implement SCENE personalization as a **content variant layer**, not as duplicate pages.

Correct model:

```text
ONE SCENE
ONE LAYOUT
ONE SET OF SLOT POSITIONS

slot 1 -> default
slot 2 -> default
slot 3 -> default / male / female
slot 4 -> male / female
slot 5 -> default
...
```

Renderer flow:

```text
Issue JSON / CMS
      ↓
normalize legacy/new scene structure
      ↓
reader profile -> variant key
      ↓
resolve each scene object
      ↓
resolved scene object list
      ↓
existing SCENE layout
      ↓
web preview / PDF
```

This gives THIS YOU personalized scene details without doubling editorial work or maintaining separate male/female magazine layouts.
