# Reusing the suite-ad system

These instructions are for a future authorized update in a working copy. The accepted handoff uses checkpoint 0.2.1 artwork unchanged. Keep the immutable ZIP as the baseline; do not rebuild just to refresh timestamps.

## Sources and generated files

| Edit here | Purpose |
|---|---|
| [sources/ads/suites.json](../sources/ads/suites.json) | Per-suite facts, total price, URL, CTA and promotion settings |
| [sources/ads/concepts.json](../sources/ads/concepts.json) | Nine stable concept IDs, headlines, photo selection, per-format crop focus, templates and literal caption text |
| [assets/listings/manifest.json](../assets/listings/manifest.json) | Original-photo provenance, suite mapping, dimensions and SHA-256 |
| [tools/build-ads.py](../tools/build-ads.py) | Layout rules, typography, square/portrait geometry, crop/resize and export pipeline |
| [exports/ads/build-report.json](../exports/ads/build-report.json) | Generated crop boxes, photo placements, text runs and delivered-file hashes |

Generated captions and the build report are outputs. Make source changes first, then rebuild; manually changing only a generated caption or report leaves the source inconsistent.

## Change a price or listing detail

1. Obtain the new authorized fact. Change only the affected suite entry in `sources/ads/suites.json`. Price-related fields include `monthly_total`, `price_label` and `price_caption`; size and type have both artwork and caption fields. The listing destination is `url`.
2. Update all three affected concepts' literal `caption` fields in `sources/ads/concepts.json`: `primary_text`, `primary_text_short`, `headline` and CTA wording as needed. Captions are not all derived from price tokens. The exported destination is copied from the suite's `url`.
3. Keep `INCLUDES CAM` next to the total and keep Suite 104's regular-price qualifier while the special is active. Recheck the short-text/headline budgets, shared-area labels and both formats.
4. Update the verification baseline to the newly authorized brief. `FACTS` and promotion checks in `tools/verify_suite_ads.py`, the hard-coded Suite 104 regular-price caption check in `tools/build-ads.py`, and relevant test expectations currently protect the accepted prices. A JSON-only price edit will intentionally fail those guards. Change their expectations explicitly; do not disable checks or edit output hashes to force a pass.
5. Rebuild, verify and obtain a new review before publishing changed artwork. The acceptance of 0.2.1 does not transfer to altered files.

## Replace a photograph honestly

Keep the genuine original file unmodified in the appropriate `assets/listings/suite-104/`, `suite-201f/`, `suite-201e/` or `shared-201/` directory. Update its manifest entry with the actual original filename, source URL/repository path and revision where available, retrieval date, suite/shared classification, measured width/height and SHA-256. Never copy another photo's provenance or mark a new image visually inspected before inspecting it.

In the concept's `photos` entry, set `file` and the separate `focus.square` and `focus.portrait` crop centers (0–1). A shared photo must keep `shared: true` and an explicit shared-area `label`. Private-suite photographs must belong to the advertised suite. The current Daylight and focus concept uses `photo_panel: "contain"` to retain the complete source frame.

The existing builder applies rectangular crop plus proportional LANCZOS resize, then places the result as `photo-N.png`. It records source crop boxes, destination rectangles, original hashes and dimensions in `build-report.json`. A different source ratio may require adjusting the documented panel dimensions in the layout code to preserve proportions; do not stretch a replacement to fill a box. Keep the office as hero when a smaller shared-amenity photo is present.

Do not repaint, regenerate, retouch, widen, stage, remove objects, outpaint or upscale through a generative model. Do not merge rooms into a fictional continuous room. The `00-base.png` contains only documented photos on a plain ground; optional contrast and design remain separate. Natural interior color is preserved.

## Edit or remove the Suite 104 promotion

Only `suites["104"].promotion` contains a promotion. `201F` and `201E` remain `null`. Keep all unspecified terms unset. The accepted special has no supplied promotional rate, discount amount, duration, deadline, eligibility condition, deposit condition or lease-length requirement. Absence from the website does not invalidate the owner-authorized brief.

For a future authorized wording change, update the promotion's `headline`, `cta` and `cta_alt`, then update all three Suite 104 long and short captions and their applicable headlines/CTA wording. Concept artwork uses `{promo}`, `{promo_cta}` and `{promo_cta_alt}` tokens.

To remove the special after authorization, set the Suite 104 promotion to `null`, remove its concepts' `promo_band` fields, and replace every promotion token or special-led block with approved non-promotional copy. Update the suite CTA and all three captions too. Setting the promotion to `null` alone fails because those tokens no longer resolve. Preserve the total/CAM meaning. This is a future creative change requiring review, especially for the special-led concept.

Update the verifier's `PROMO`, Suite 104 semantic requirements and relevant regression/logic expectations to that newly authorized brief. Do not weaken unrelated missing-file, photo-integrity or office-promotion checks. This handoff makes no promotion changes.

## Export an overlay or rebuild an ad

Each accepted format already has a standalone transparent design overlay at:

`exports/ads/layers/<concept-id>-<square|portrait>/03-design-overlay.png`

Use that file directly when you need the overlay alone. Do not cut text out of the flattened ad. The matching directory also contains `photo-N.png`, `00-base.png`, an optional `02-contrast.png`, and live-text/outlined SVGs. A missing optional contrast layer is intentional when `has_contrast_layer` is false in the report.

The full generator writes the standalone overlay and flattened exports together; there is no overlay-only CLI switch for suite ads. After authorized source changes, on the supported build machine, run from the project root:

```sh
/usr/bin/python3 tools/build-ads.py --preflight
/usr/bin/python3 tools/build-ads.py
/usr/bin/python3 tools/build-ad-review.py
/usr/bin/python3 tools/verify_suite_ads.py --root . --report docs/test-suite-ad-results.json
/usr/bin/python3 tools/test_verify_suite_ads.py
```

These are future-use commands; the generators were not rerun for this handoff. `build-ads.py` rebuilds all eighteen variants and regenerates captions, SVGs and the build report. `build-ad-review.py` writes a draft review page; renew its acceptance/status documentation after reviewing any changed work.

Flattening order is `00-base.png` converted to RGBA → optional `02-contrast.png` → `03-design-overlay.png`, using Pillow `alpha_composite`, then RGB PNG; the JPEG alternative uses quality 92 and subsampling 0. Use the matching format's layers at native size without resizing them. The delivered-file verifier recomposes in memory and compares exact PNG pixels without writing new artwork.

For future changes, retain the existing logic tests (`tools/test_logic.py`) and the separately scoped legacy social/overlay tests (`tools/test_assets.py`). The latter are not suite-ad export coverage. Some legacy tests render into temporary directories and require the full build environment.

## Build and font dependencies

The generators require **macOS and the system Avenir Next font**, specifically Bold, Demi Bold and Heavy, plus Python with Pillow, numpy and fontTools. The current font path and collection indices are coded in the generators; `--preflight` checks the actual faces. The generators have not been made portable. No font binaries are included in the package.

- PNG/JPEG exports need no installed font for viewing or uploading.
- Live-text SVGs use the viewer's available fonts; fallback fonts may change spacing and wrapping.
- Outlined SVGs convert text to paths and avoid that text-font dependency. Edit JSON or live text for future changes; outlines are a delivery copy.
- Both SVG types that reference `photo-N.png` must remain with those sibling image files. Copy the whole matching layer directory when moving an SVG.
- `tools/verify_suite_ads.py` needs Python 3.9+ and Pillow, not Avenir, fontTools, numpy, a generator or a fresh render. It does not demand matching antialiasing across unrelated SVG renderers.

## Captions, destinations and distinct formats

Use the concept ID to find matching long/short text, headline, suggested button, CTA and URL in [captions.md](../exports/ads/captions.md) or [captions.json](../exports/ads/captions.json). The [start guide](../START-HERE.md) links all nine concepts and both image formats. The suggested button is not a configured campaign destination.

Square is 1080×1080; portrait is 1080×1350. They are separate compositions, not interchangeable resizes. Keep each format's `focus` and layout geometry, filenames, overlays and source folders distinct. Do not stretch or crop the finished square to manufacture a portrait. Check both genuine exports in the actual placement preview before publication.

Placeholder listing/video templates in `overlays/listing/` and `overlays/video/` are a separate reusable system. Use [overlay-kit.html](../overlay-kit.html) and its placeholder guard for those; they are not finished suite ads. Retained cover/profile alternatives are reached from [index.html](../index.html).
