# Example card designs

Working reference designs, one per renderer. They exist to be uploaded, broken and copied —
start from the one matching your renderer rather than from a blank file.

Every file here has been run through the real upload pipeline
(`app/services/card_html.sanitise` and `app/services/media.save`): nothing is stripped,
nothing is rejected, every reference resolves. The rules they follow are in
[CARD-AUTHORING.md](../CARD-AUTHORING.md).

## The renderers

| Renderer | Status | Upload path |
|---|---|---|
| `html` | **Implemented.** The primary path. | Event → Card design → upload |
| `image` | Renders correctly; **nothing can create one yet** — see below | none |
| `component` | Declared in the enum, not implemented | rejected, naming the supported renderers |
| `layered` | Declared in the enum, not implemented | rejected, naming the supported renderers |

## `html/` — the primary path

```
html/card.html      the document      4.0 KB
html/border.svg     CSS background    1.2 KB
html/flourish.svg   <img> reference   0.8 KB
html/monogram.svg   <img> reference   1.0 KB
                                    ─────────
                                      6.9 KB   (limit 600 KB, admin warns above 500 KB)
```

Upload `card.html` in the card file field and **all three SVGs together** in the assets
field, in one step. Splitting the upload fails: the document's references are rewritten
against files that are already stored, so the companions have to arrive with it.

What it demonstrates, deliberately:

- **Both kinds of reference get rewritten.** `flourish.svg` and `monogram.svg` arrive as
  `src` attributes; `border.svg` arrives as `url(border.svg)` inside the `<style>` block.
  Both are resolved to content-addressed `/media/...` URLs on upload. Drop any one of the
  three from the upload and it is refused by name — worth trying once.
- **A reduced-motion branch that resolves to the finished state**, not a paused starting
  one. Both animations are cancelled and their end state forced.
- **Container-relative sizing** — `clamp()` and `%` throughout, no `vw`/`vh`, so it holds
  from a 320px phone to the desktop column.
- **Fixed text.** Names, date and venue are typed in. There is no slot for a guest name,
  because the invitation prints that in the header directly above the card.
- **Bangla without doing anything.** `শুভ বিবাহ` inherits the page's self-hosted subset
  through the shadow boundary; the file references no font of its own.

## `image/` — the flat-artwork path

`image/card-artwork.svg` is the whole card as one file, 1000×1400 (5:7 portrait).

Two things to know before reaching for this:

**There is currently no way to upload one.** The upload endpoint hardcodes the `html`
renderer, so an `image` design cannot be created through the admin today. The renderer
itself is complete — the invitation page, the payload and the view model all handle it — so
this file is a valid target waiting on a create path, not a design you can ship this week.

**Text in an `<img>` is isolated.** An SVG loaded as an image gets no page CSS and no page
fonts, so the Bangla line falls back to whatever the device happens to have. For anything
real, either convert the text to paths on export or ship a **WebP** instead. The 600 KB
budget makes WebP the right answer for photographic artwork anyway.

Alt text is required per locale when this path is wired up, because the artwork *is* the
invitation's words. For this file:

- `alt_en` — `Wedding invitation for Nazifa Rahman and Abdullah Karim, Thursday 24 September 2026, Grand Ballroom, Hotel Sonargaon, Dhaka`
- `alt_bn` — `নাজিফা রহমান ও আবদুল্লাহ করিমের বিবাহের আমন্ত্রণ, ২৪ সেপ্টেম্বর ২০২৬, গ্র্যান্ড বলরুম, হোটেল সোনারগাঁও, ঢাকা`

## `component` and `layered`

Nothing to provide. Both values exist in the `card_renderer` enum so a future catalogue tier
is an additive change; saving a design with either is refused with a message naming the two
renderers that work. Do not author against them.
