# Shoe Customizer — Architecture

## 1. Rendering: runtime multiply-tint

Each colourable **zone** of a shoe is a white photographic PNG slice, transparent outside the
zone, registered to the same canvas as the base (2560×1440 for the AF1). The white leather in
the slice doubles as a **luminance map**: multiplying a flat colour over it darkens toward the
colour while preserving the photographed shadows and highlights — i.e. realistic dyed leather
from a single asset per zone.

Compositing recipe (see `poc/index.html` → `tinted()` and `render()`):

```
draw base                                   // always
for each zone with a colour selected:
    offscreen = drawImage(zone slice)       // 1. white leather (shading)
    offscreen.op = 'multiply'
    offscreen.fillRect(colour)              // 2. apply colour over the whole tile
    offscreen.op = 'destination-in'
    offscreen.drawImage(zone slice)         // 3. clip fill back to the zone's alpha shape
    draw offscreen onto main canvas
```

A zone with **no** colour selected is simply not drawn, so the white base shows through
("White" = default). Colours are pure data — adding a swatch never touches assets.

**Asset cost per shoe:** 1 base + 1 mask per zone. The AF1 has 9 zones
(swoosh, toe cap, vamp, heel panel, mudguard, collar, tongue tab, laces, sole) → 10 PNGs.

## 2. Data model (multi-shoe from day one)

Custom tables (proposed; could be CPT+meta but relational is cleaner for the order/spec side):

```
shoe            id, slug, name, base_image, canvas_w, canvas_h, base_price, active
zone            id, shoe_id, code, label, z_index, print_area(nullable json)   -- one row per colourable region
zone_layer      id, zone_id, image                                             -- a zone may composite >1 slice (e.g. laces = upper+lower)
option_group    id, shoe_id, label, sort, required, type(select|radio|swatch|upload|text|size)
option          id, group_id, label, price_delta, target_zone_id(nullable),
                colour_hex(nullable), asset(nullable), sort
visibility_rule id, option_group_id, depends_group_id, equals_option_id        -- conditional reveal
swatch_palette  id, shoe_id, name, hex                                         -- reusable colour set
design          id, shoe_id, selections_json, mockup_image, price, created     -- a saved customer configuration
```

Notes
- **Colourable zone vs. option**: a colour option points at a `target_zone_id` + `colour_hex`.
  The render walks selected options, groups by zone, and tints. This is exactly the POC's
  `selection[zoneId] = {hex}` map, normalised.
- **Logo/upload options** carry no zone; they place the uploaded image into a zone's
  `print_area` rectangle as an extra top layer.
- **Sizes** are order-line data, not render data (see §4).

## 3. Frontend customizer

- Rendered **inline** on the Woo product page (no iframe). Small widget — vanilla JS canvas
  is enough (matches the Portal's no-build ethos); React optional if the team prefers.
- Reads the shoe's config as JSON (one request), preloads base + all zone masks, then all
  tinting/compositing is **client-side and instant** — no per-change server round trip
  (Kickflip round-trips; we won't need to).
- Drives: stepped/validated option form → live canvas → running price → validation.
- **Step 0 "Pick a shoe"** (future): a gallery of `shoe` rows; selecting one loads that
  shoe's config into the same widget. The engine is already shoe-agnostic — only the config
  and asset folder change.

## 4. Cart / order integration (stays in WooCommerce)

- `woocommerce_add_cart_item_data` — attach `{ shoe, selections_json, mockup_dataurl, price_delta }`.
- `woocommerce_get_item_data` / order-item meta — render the chosen colours + mockup thumbnail
  in cart, checkout, order-received, and admin/emails.
- Price = `shoe.base_price + Σ option.price_delta` (e.g. coloured laces +$10, add name +$5),
  computed client-side for display and **re-validated server-side** on add-to-cart.
- **Size run**: the "Sizes Required" repeater produces N line entries, each `{ size, name?,
  name_price }`. Stored as structured order-item meta so fulfilment gets a clean size/name table.

### Design JSON (the contract the Evolution plugin will later consume)

```json
{
  "shoe": "af1",
  "basePrice": 200.00,
  "selections": [
    { "zone": "swoosh", "colour": "Red",  "hex": "#c62828" },
    { "zone": "vamp",   "colour": "Navy", "hex": "#1f2d4d" }
  ],
  "logo": { "asset": "uploads/logo123.png", "zone": "heel" },
  "sizes": [ { "size": "US 9",  "name": "J. Smith" }, { "size": "US 10" } ],
  "priceDelta": 15.00
}
```

Self-describing and stable — the future Evolution "pull Woo orders" plugin reads this straight
into a sales order + production spec without needing our render internals.

## 5. Production output

The part SaaS does worst and we care about most:
- **Mockup PNG** (the composed canvas) saved per design — shown to customer and to production.
- **Spec / picking sheet** per order line: shoe, per-zone colour callouts, logo file at full
  resolution + placement zone, and the size/name run table.
- Optional later: push the whole thing into **Evolution** as an order via the separate plugin.

## 6. Roadmap

| Phase | Deliverable | Status |
|---|---|---|
| 0 | Runtime-tint compositor POC — **5 shoes** (AF1, Samba, Dunk, Advantage, Court) + "Pick a shoe" | **DONE** (`poc/`) |
| 1 | Data model + no-code admin builder + REST config + seed importer (colour path) | **DONE** (`plugin/`) |
| 1b | Priced option groups + logo upload (live drag/scale) + size-run matrix (qty/size) — one release, v0.4.0 | **SCOPED** ([PHASE_1B_SCOPE.md](PHASE_1B_SCOPE.md)) |
| 2 | Frontend engine + live canvas via `[shoe_customizer]` shortcode (consumes REST config) | **DONE** (`plugin/`) |
| 3 | Cart / checkout / order integration (design JSON + mockup on the Woo line item) | **DONE** (`plugin/`) |
| 3b | Size-run matrix + logo upload on the line item (depend on Phase 1b options) | |
| 4 | Production spec sheet / mockup export | |
| 5 | "Pick a shoe" step 0 + migrate remaining shoe models (data entry, not code) | |

### Phase 1 plugin (`plugin/shoe-customizer/`)

Deployable WordPress/WooCommerce plugin. Follows WP conventions (`$wpdb`, `dbDelta`, nonces,
`wp.media`) — **not** Evolution's `DB::` layer (different app).

```
plugin/shoe-customizer/
├── shoe-customizer.php        ← header, constants, boot (activation → schema; loads REST + admin)
├── uninstall.php              ← drops tables + option (keeps copied assets)
├── includes/
│   ├── class-sc-install.php   ← dbDelta schema (8 tables, prefix wp_sc_*)
│   ├── class-sc-repo.php      ← data access: shoes/zones/layers/swatches CRUD + config() builder
│   ├── class-sc-rest.php      ← GET /wp-json/shoe-customizer/v1/shoes|shoe/<slug>  (Phase-2 contract)
│   ├── class-sc-admin.php     ← menu + nonce-guarded save/delete/seed handlers
│   ├── class-sc-seed.php      ← copies seed/assets → uploads, builds all 5 shoes (idempotent per slug)
│   ├── class-sc-frontend.php  ← [shoe_customizer] shortcode + asset enqueue + SCData localize (Phase 2/3)
│   ├── class-sc-cart.php      ← Phase 3: sc_add_to_cart AJAX (server-side re-validate + price) + cart/order/admin hooks
│   └── views/{list,edit}.php  ← shoe table + no-code editor (meta, zone repeater, palette)
├── assets/{admin.js,admin.css}← wp.media pickers, zone/swatch repeaters, live chips
└── seed/
    ├── shoes.php              ← PHP mirror of poc/shoes.js (5 shoes + palette)
    └── assets/<slug>/*.png    ← bundled real slices (53 PNGs)
```

The REST `shoe/<slug>` payload is the exact config the Phase-2 engine loads (base + zones with
ordered layer URLs + palette), mirroring `poc/shoes.js` — so the proven compositor drives it
unchanged. **Not yet WP-runtime-tested** (no WordPress in the dev env); all PHP is `php -l`
clean. Install on the site, activate, then **Shoe Customizer → Import seed shoes**.

## 7. Open items
- Swatch fidelity: confirm multiply-tint looks right on **dark** colours over white leather
  (may want a subtle levels tweak per palette). Validate on a few real colourways.
- Whether logo upload needs live placement/scaling UI in v1 or just "upload + we position".
- Final zone grouping for the customer UI (per-panel vs. grouped "shoe body / swoosh / laces")
  — the data model supports either; it's a merchandising choice.
