# Poster Scene Rules — Creative Pipeline (Poster-Specific Constraints)

> When poster / 海报 / 传单 / flyer / 宣传页 keywords are detected, load these rules on top of `creative.md`.
> These rules take priority over `creative.md` generic rules; in case of conflict, this file prevails.
>
> **Positioning**: This is a scene-layer patch on `creative.md` (the generic Creative pipeline) — only covers poster-specific constraints, does not repeat generic rules.

---

## 1. Page Count & Dimensions

### 1.1 Default Single Page
- When the user does not explicitly request multiple pages, **default to a single-page poster**
- Single-page poster `total_pages: 1`, never split into multiple pages on your own

### 1.2 Sizing Strategy

| Text Volume | Recommended Size | Aspect Ratio | Notes |
|--------|---------|--------|------|
| ≤ 50 chars | 720 × 960 | 3:4 | Title poster / social media cover / card |
| 50–200 chars | 720 × 960 | 3:4 | Standard promotional poster |
| > 200 chars | 720 × min-height 960 | Adaptive | Long poster (H5 style), content stretches height |
| User-specified landscape | Adjust as needed | As needed | Width and height can be swapped |

### 1.3 Canvas Variables
```json
{
  "canvas": { "width": 720, "height": 960 }
}
```
Default dimensions can be overridden via the `canvas` field in the Blueprint. `design_engine.py` will automatically inject `--canvas-w` and `--canvas-h` CSS variables.

---

## 2. Information Density & Page Fill

### 2.1 Core Iron Rule: Content Must Fill the Page

> **The biggest visual disaster for a poster is not content overflow, but content only occupying half the page with a blank bottom half.**

| Text Volume | Content Area / Page Ratio | Notes |
|--------|----------------|------|
| ≤ 50 chars | 70–80% | Enlarged cards, large font sizes, generous decorative whitespace is intentional |
| 50–200 chars | 75–85% | Content modules distributed evenly, must not be crammed in the top half |
| > 200 chars | 80–90% | Content-dominant, whitespace only at margins |

### 2.2 Anti-Top-Heavy

- All components' `grid_area` **must cover the full 1→13 rows**, never stop at row 10 or 11
- The last component's `grid_area` row endpoint must be `13`
- **No large blank space at top/header** — the first content component's `grid_area` should start at row 1, not row 3 or 4
- **No large blank space on left/right sides** — components should utilize full column width (most should span 1→13 columns)
- If content is insufficient to fill 12 rows, use these strategies (by priority):
  1. **Increase font size**: Hero from scale 5 → 6, body font +2pt
  2. **Increase component spacing**: grid gap from 16px → 24px
  3. **Insert decorative components**: `Hairline_Divider`, `Page_Ghost_Number`
  4. **Expand stat block / hero grid row count**

### 2.3 Grid Area Allocation Iron Rule

```
✅ Correct: Components cover all rows 1→13
Page: [Hero 1→4] [Stats 4→7] [Glass 7→10] [Meta 10→13]

❌ Wrong: Components only reach row 10, rows 11-13 empty  
Page: [Hero 1→3] [Stats 3→5] [Glass 5→8] [Meta 8→10] [??? 10→13 void]
```

### 2.4 ★★★ Content-Proportional Row Allocation (Anti-Overlap Iron Rule)

> **When two or more text components share a page, rows must be allocated proportionally to content volume — never split evenly!**

**Problem reproduction:**
```
Attractions: 450 chars (3 attractions × 150 chars description)
Food:        250 chars (2 foods × 125 chars description)
Available rows: 8 rows (5→13)

❌ Even split: Attractions 5→9, Food 9→13 → 4 rows each
   → Attractions content far exceeds 4-row capacity, overflows into Food area, text overlaps!

✅ Proportional split: 
   Attractions: 8 × 450/700 ≈ 5 rows → 5→10
   Food:        8 × 250/700 ≈ 3 rows → 10→13
```

**Steps:**
1. Write all `markdown_content` first
2. Count the **character count** of each text component (including titles/paragraphs/lists)
3. Allocate rows proportionally: `rows_i = total_rows × (chars_i / total_chars)`
4. Round off, each component **minimum 3 rows**
5. Verify: all component grid_area endpoints connect end-to-end, covering full 1→13

**Applicable scenarios:**
- Multiple `Glass_Canvas` on the same page (most common)
- `Glass_Canvas` + `Process_List` on the same page
- Any two or more components containing text paragraphs

**Not applicable:**
- `Hero_Typography` (very large font, 1-3 lines occupying 2-3 grid rows is reasonable)
- `Stat_Block` (number + label, fixed height, typically 2 rows)
- `Floating_Meta` (short labels, typically 2-3 rows)

---

## 3. Font Size System (Poster-Specific)

### 3.1 Minimum Font Size (Hard Floor)

| Element | Min Font Size | Recommended | Notes |
|------|---------|---------|------|
| **Page main title** | **50px** | 56–72px | Poster title must have visual impact |
| **Body text** | **24px** | 24–28px | Posters are not reports — body font must be large |
| **Subtitle / card title** | **28px** | 32–40px | Secondary headings |
| **Floating Meta** | **16px** | 16–20px | Metadata text |
| **Stat Number** | **48px** | 56–72px | Data sculptures must be eye-catching |
| **Stat Label** | **14px** | 14–16px | Data labels |

> Compared to `fill-engine.md`'s generic red line (body ≥ 14pt), the poster body floor is **24px** — posters are a distance-reading medium, font sizes must be larger.

### 3.2 Emphasis Hierarchy

- Use **font size + font weight** to create hierarchy, **not color differentiation**
- Emphasis text (keyword/number highlights) font size must be smaller than titles
- Hero title recommended `weight: "black"` (900), subtitle `weight: "thin"` (100), creating extreme contrast

---

## 4. Color Rules (Poster-Specific)

### 4.1 Color Palette System

**Primary palette: Material Design 3 with low-medium saturation.** Default to medium-saturation colors; for light themes, use gradient backgrounds with white/light text on top.

| Area | Proportion | Role |
|------|------|------|
| 60% Ground | Background/margins/whitespace | Main color (low saturation) |
| 30% Structure | Cards/dividers/secondary areas | Derived from main by adjusting lightness |
| 10% Emphasis | Titles/key numbers/single accent | Adjusted purity/brightness of main color |

**Color derivation rules:**
- Title/subtitle text color: Adjust main color's purity and brightness (not a separate random color)
- Auxiliary colors: **Maximum 2**, derived from primary by adjusting lightness/saturation
- Keep consistent color palette throughout — do NOT change main color between sections
- Default recommendation: **Light theme with medium saturation**, or gradient background + white fonts

### 4.2 Poster Additional Constraints

- **No pure white (#FFFFFF) background** — use at least `#f5f4f2` or warmer off-white
- **No transparent background**
- **Page base color (`<body>` / `<html>` background) must match poster content background** — `printBackground: true` renders body background. If body is white but poster content is gray, white borders/gaps appear in the PDF. Ensure `html, body { background: var(--c-bg); }` matches the poster canvas exactly
- **Do not use a single image to fill the background** — use grid textures, gradients, geometric shapes, and other generative backgrounds
- Long posters (>200 chars) must not use full-image backgrounds
- **Gradients** or **large blurred circles/symbols** can be used sparingly as background accents
- Maximum 2 auxiliary colors, derived from primary by adjusting lightness/saturation
- Background accents: grid textures, organic shapes, large blurred circles/symbols — NOT a single image
- **Overall bright and vibrant color combinations** — the poster should feel visually striking, not muted/dull

### 4.3 palette_mode Mapping

| Poster Style | Recommended palette_mode | color_harmony |
|---------|-------------------|---------------|
| Business/Formal | `minimal` | `auto` |
| Tech/AI | `dark` | `complementary` |
| Lifestyle/Food/Artistic | `pastel` | `analogous` |
| Luxury/Ceremony | `jewel` | `split_complementary` |
| Other | `minimal` (default) | `auto` |

---

## 4.5 ★★★ Anti-Modularization Iron Rule (Anti-Card / Anti-Dashboard)

> **A poster is a complete composition, not a dashboard, not an APP interface, not a report.**

### Root Cause

LLMs naturally tend to "classify information → put each category in a box → stack them vertically", because that is report/document organization logic. But poster visual logic is the exact opposite — **information is unified, hierarchy is established through typographic rhythm (font size, weight, spacing, whitespace), not through borders and background colors**.

### Comparison

| Report/UI Thinking ❌ | Poster Thinking ✅ |
|---|---|
| Each info block wrapped in `border + border-radius + background` as a card | Information placed directly on the canvas, no borders no background |
| Clear visual boundaries between modules | Modules naturally separated by **whitespace and thin lines** |
| Looks like a mobile APP interface | Looks like a design piece |
| Hierarchy via "different colored boxes" | Hierarchy via **font size gradient + weight contrast + color lightness** |
| Stacked Glass_Canvas components = card wall | Pure typography + occasional Hairline_Divider |

### Mandatory Rules

1. **Single-page posters must not have more than 3 containers with `background` + `border`.** If information needs grouping, use whitespace (margin/padding) and thin lines (1px, opacity < 10%) instead of bordered cards.
2. **No 2×2 / 2×3 grid card layouts** (unless it's a pure data scenario with `data_dashboard` archetype). Information flows in a single column; multiple items in the same row use `flex + gap` horizontal layout without borders.
3. **Information hierarchy must be established through typography properties**, not through "different colored/backgrounded boxes":
   - Primary information: large font (≥48px) + heavy weight (900)
   - Secondary information: medium font (18-24px) + medium weight (700)
   - Tertiary information: small font (12-14px) + light weight (300-400) + reduced opacity
4. **Glass_Canvas may be used at most once in a single-page poster.** If multiple text sections are needed, use direct HTML typography instead of wrapping each paragraph in a Glass_Canvas.
5. **Bottom action information (price/address/QR code) should not be wrapped in a separate color block.** Use larger font + heavier weight to emphasize, keeping it unified with the overall composition.

### Correct Example (Direct HTML Flow)

```html
<!-- ❌ Wrong: card wall -->
<div class="card" style="background:rgba(255,255,255,0.5); border-radius:12px; border:1px solid #ddd;">
  <h3>📅 展期</h3>
  <p>7.15 — 8.30</p>
</div>
<div class="card" style="background:rgba(255,255,255,0.5); border-radius:12px; border:1px solid #ddd;">
  <h3>📍 地点</h3>
  <p>城市艺术中心</p>
</div>

<!-- ✅ Correct: pure typography, no borders -->
<div class="info-row" style="display:flex; gap:48px;">
  <div>
    <div style="font-size:10px; letter-spacing:3px; opacity:0.45;">展期</div>
    <div style="font-size:20px; font-weight:700;">7.15 — 8.30</div>
  </div>
  <div>
    <div style="font-size:10px; letter-spacing:3px; opacity:0.45;">地点</div>
    <div style="font-size:20px; font-weight:700;">城市艺术中心</div>
  </div>
</div>
```

### Blueprint Route Additional Constraints

When using Blueprint JSON (not Direct HTML):
- Single-page posters may have at most 1 `Glass_Canvas`, used only for text that truly needs reading
- Prefer combining `Stat_Block` (data), `Floating_Meta` (metadata), `Hero_Typography` (titles) instead of multiple Glass_Canvas
- Information data (dates, locations, headcounts) should use `Floating_Meta` or `Stat_Block`, not Glass_Canvas

---

## 5. Layout Strategy

### 5.1 Composition Priority
1. **Vertical composition** (vertical flow) — most common, information flows top to bottom
2. **Left-right composition** (split vertical) — image and text split, `archetype: "split_vertical"`
3. **Centered composition** (centered) — suitable for title cards with ≤ 50 chars

### 5.2 Card Rules
- Cards **must never overlap**
- Card content should be well-distributed, **no large internal whitespace**
- Text within cards **vertically centered**
- Glass Canvas `align-items` must be `stretch` (built into the engine)

### 5.3 Breathing Margins

| Scenario | safe-zone inset | Notes |
|------|----------------|------|
| Cover / title poster (≤50 chars) | `12% 14%` | Generous whitespace is intentional |
| Standard poster (50-200 chars) | `8% 10%` | Balance whitespace and content |
| Long poster (>200 chars) | `6% 8%` | Maximize content area |

> Max content width = 80% of page width (consistent with original prompt)

---

## 5.5 Visual Impact Rules

### 5.5.1 Emphasis & Contrast
- Create visual contrast with **oversized and small elements** — hero numbers/titles vs tiny metadata
- Highlight core points with large fonts or numbers for strong visual contrast
- **Emphasized text must remain smaller than headings/titles** — never let a highlight be bigger than the heading
- Texts that need emphasis can be highlighted with color, weight, or circled with hand-drawn lines
- **Do not insert multiple small pictures as embellishments** — this won’t enhance visual appeal

### 5.5.2 Alignment & Spacing
- Allow blocks to resize based on content, align appropriately, optimize space utilization
- If excess whitespace exists, **enlarge fonts or modules** to balance the layout
- Cards cannot overlap; content should fill the card area without excessive empty space
- Use **flexbox** layouts to prevent footer from moving up (with top and bottom margin settings)
- For visual variety: encourage diverse and creative layouts beyond standard grids, while maintaining alignment and hierarchy

---

## 6. Design Style Library

Based on content theme, the model should autonomously select one of the following styles. Explain the selection rationale in the Blueprint's `design_rationale`.

| Style | Characteristics | Applicable Scenarios |
|------|------|---------|
| **Modern Minimal** | Clean colors, organic shapes, flowing curves, rounded cards, clear hierarchy | Business, tech, education |
| **Neo-Brutalism** | Flat elements, illustrations, patterns, large text blocks, special font designs, thick borders | Creative, events, youth |
| **Artistic Gradient** | Diffused light, gradient glow, semi-transparent elements, blur effects, glass texture | Art, music, branding |
| **Collage** | Contrasting color design, material collage, large text, irregular layout | Trends, fashion, exhibitions |
| **Playful UI** | Bright colors, interesting shapes, energetic | Children, games, social |

### Special Forms for Text Volume ≤ 50 Characters

When content is minimal, prioritize the following compact forms:

| Form | Description | archetype |
|------|------|-----------|
| **Centered Card** | Calendar-like effect, key content centered as note/card | `cover_hero` |
| **Bookmark Page** | Narrow tall ratio (e.g. 360×960), vertical reading | `cover_hero` |
| **Minimal Text** | Title + whitespace only, no additional information | `cover_hero` |
| **Sticky Note** | Content displayed as floating sticky notes above background | `cover_hero` |
| **Polaroid Card** | Photo-style card with caption below | `cover_hero` |

### Special Forms for Text Volume ≤ 100 Characters

| Form | Description |
|------|------|
| **Floating Card System** | Content as cards floating above organic shape backgrounds |
| **Notebook Style** | Lined-paper aesthetic with handwritten-feel content |
| **Fortune Stick** | Vertical strip with centered calligraphy text |

> **Key constraint**: If the user only provides a title with no other requirements, **do not expand content on your own** — just place the title.

---

## 6.5 Icons and Illustrations

- Use **Material Design Icons** (Google Fonts method):
  ```html
  <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
  <!-- Usage: -->
  <i class="material-icons">icon_name</i>
  ```
- Icon color: Use the **theme color** (not random colors)
- Icon size and position: Aligned with surrounding elements, never stretched
- If positioned beside text: **center-aligned with the first line of text**
- **Emoji can be used as icons** — 🌸 🍴 🏙️ etc. (but remember ReportLab can’t render emoji — only use in HTML/Playwright route)
- For logos/emblems: Use text "Your Logo" or icons, **never** search for logo images

---

## 7. Text Readability

### 7.1 Iron Rules
- **Line height ≥ 130%** (`line-height: 1.3` or above)
- **No text shadow/glow effects**
- **When text overlays images**, a semi-transparent mask layer is required
- **Do not use images with lots of text/charts/numbers as text background**
- **No `text-align: justify`** (CJK characters get stretched letter-spacing, terrible result) — always use `text-align: left`

### 7.2 Contrast
- Text on light background: `color: #242220` or darker
- Text on dark background: `color: #f5f4f2` or lighter
- Text on medium background (L 0.30–0.70): **forbidden** — do not place text on mid-tone backgrounds

---

## 8. Image Usage Rules

### 8.0 Image Recommendations

When creating posters, actively use images to enrich visual effects. Good images can significantly enhance the poster's visual impact.

- Priority: **installed image generation skill** > web image search
- Image style must match the poster theme
- Download images locally first, then embed into the poster
- Local images must be converted to base64 data URI in HTML (Playwright cannot load local absolute paths)

### 8.1 Core Principles
- Don't force images when none are suitable, but images improve results when available
- Each image must be **unique** in the design, no reuse
- Prefer clear, high-resolution, watermark-free, text-free images
- Images should have rounded corners, sized consistently with the overall design
- You can try adding **irregularly shaped masks** (CSS `clip-path`) to images for visual interest

### 8.2 Prohibited Behaviors
- ❌ Placing images directly in corners
- ❌ Images obscuring text or overlapping with other modules
- ❌ Multiple images scattered randomly as decoration
- ❌ Searching for images when logo/badge is needed — use text "Your Logo" or icons instead

---

## 9. Chart Rules (Poster Scene)

- Large numerical datasets → consider creating visual charts
- Chart style should match the poster theme
- Use **Bento Grid** layout for multiple charts
- Chart containers **must have height constraints** to prevent infinite growth

---

## 10. Prohibited Items (Poster-Specific)

### 10.0 HTML Rendering Iron Rules (Applicable to All HTML → PDF Routes)

| Rule | Description |
|------|------|
| **No `overflow: hidden` on content components** | Truncates text. Only allowed on page-level `.canvas` and decorative background layers |
| **Use `min-height` instead of `height` for content containers** | `height:100%` locks height, content gets clipped when too much; `min-height:100%` allows natural expansion |
| **Exceptions where `overflow: hidden` is allowed** | `.canvas` (page boundary), `.poster` (auto-injected by `html2poster.js`), `.floating-meta` (short label ellipsis), cover Layer 1 (decorative clipping), SVG/img (fill container) |
| **Absolutely no `backdrop-filter`** | **Playwright PDF rendering silently discards entire element content!** Use fixed rgba() background color for cards instead |
| **Absolutely no `text-align: justify`** | CJK character spacing gets abnormally stretched, always use `text-align: left` |
| **`overflow: hidden` on `.page` containers** | **MANDATORY for multi-page documents.** Decorative elements (glow, gradient circles, oversized backgrounds) with `width > 100%` or negative offsets cause `scrollWidth > clientWidth`, triggering Playwright to shrink the entire page → content drifts left. `.page { overflow: hidden }` clips decorative overflow without affecting visible content |
| **Horizontal flex rows must have `flex-wrap`** | ≥3 inline items (flow bars, step lists, tag rows) without `flex-wrap: wrap` will overflow the page right edge when content is long. See `typesetting/overflow.md` §3.5 for full rules |

| Prohibited | Reason |
|--------|------|
| ❌ Timeline graphics | Complex connecting lines easily misalign in PDF rendering |
| ❌ Complex SVG-drawn structure/flow diagrams | Unless user explicitly requests |
| ❌ Code-drawn maps or flags | Poor quality |
| ❌ Base64 images (when exceeding 10MB) | File too large. Small image base64 is acceptable (Playwright cannot load local paths) |
| ❌ Content truncation | Must adjust container height to ensure all content fully displayed |
| ❌ Pure white background (#FFFFFF) | Lacks design quality |
| ❌ Transparent background | PDF output cannot be transparent |

---

## 11. Poster Font Recommendations

### 11.1 CJK Font Recommendations

**For Chinese posters (serious/formal scenes):**

| Purpose | Recommended Font | Google Fonts / CDN Name | Style |
|------|---------|-------------------|------|
| CJK title (serious) | DingTalk JinBuTi | `DingTalk JinBuTi` (letter-spacing: -5%) | Bold, impactful |
| CJK title (alt) | Douyin Sans / Alimama FangYuanTi VF Bold | Via CDN | Modern Chinese |
| CJK title (serif) | Swei B2 Serif CJKtc Bold | Via CDN | Elegant serif |
| CJK body | HarmonyOS Sans SC | `HarmonyOS Sans SC` | Clear, readable |
| CJK body (fallback) | Noto Sans SC Regular | `Noto Sans SC:wght@400` | Google Fonts guaranteed |
| CJK artistic | Noto Serif SC Bold | `Noto Serif SC:wght@700` | Elegant, artistic |
| Handwritten | ZhanKuKuaiLeTi2016XiuDingBan-2 | Via CDN | Casual, playful |
| Pixel/dot-matrix | DottedSongtiSquareRegular | Via CDN | Retro, xiangsuti |

**For English posters:**

| Purpose | Recommended Font | Google Fonts Name | Style |
|------|---------|-------------------|------|
| English/number title | Futura | System / `Futura` | Classic geometric |
| English body | PingFang HK | System | Clean, modern |
| English title (Google) | Inter Black | `Inter:wght@900` | Modern geometric |
| English serif | Playfair Display | `Playfair+Display:wght@900` | Classic editorial |
| Number emphasis | Inter Black | `Inter:wght@900` | Data sculpture |

### 11.2 Font Usage Constraints
- Entire poster **maximum 3 fonts**
- Title and body may use different fonts, but must be visually harmonious
- **Never reduce font size or line height to squeeze in more content**
- Font content in cards should be **vertically centered** within the card
- You may use different style fonts for entertaining or artistic scenes

---

## 12. Coordination with design_engine.py

### 12.0 ★★★ Stable Layout Selection Strategy (Anti-Overflow Core Rule)

> **Content-heavy posters must bypass the Blueprint 12×12 Grid and use a pure flow-based HTML approach.**

**Why:** The Blueprint 12×12 Grid allocates space with fixed row heights and cannot predict actual text rendering height, causing:
- Text overflowing Glass Canvas containers
- CJK character spacing stretched by `text-align: justify`
- Inaccurate row allocation for multiple components, text overlap

**Route selection:**

| Scenario | Recommended Route | Notes |
|------|---------|------|
| Title poster (≤ 50 chars) | Blueprint JSON | Few components, little content, Grid sufficient |
| Standard poster (50-150 chars) | Blueprint JSON | Grid mostly sufficient, mind row allocation |
| **Info-dense poster (>150 chars or multiple sections)** | **★ Direct HTML Flow** | **Strongly recommended — completely avoids overflow** |
| **Multi-page info poster** | **★ Direct HTML Flow** | **Strongly recommended** |
| Poster with images | Direct HTML Flow | Image embedding is more stable |

### 12.1 Direct HTML Flow Approach (Recommended Default)

**Core idea: No Grid, no fixed height, content flows naturally, never overflows.**

Write HTML directly, convert to PDF via `html2poster.js`. The poster is a **single continuous `<div class="poster">` container** — NOT split into separate `<div class="page">` blocks.

#### ★★★ Single-Canvas vs Multi-Page (Iron Rule)

| Approach | When to Use | HTML Structure |
|----------|-------------|----------------|
| **Single Canvas** (default) | All content forms one unified poster | One `<div class="poster">`, no `page-break` |
| **Multi-Page** (exception) | User explicitly requests separate pages (e.g., "make a 4-page booklet") | Multiple `<div class="page">` with `page-break-after: always` |

> **Default = Single Canvas.** A poster is ONE design composition, not a paginated report. Multi-page is only for booklets/multi-page documents where the user explicitly asks for page separation.

#### ★★★ Dynamic Height (Anti-Blank-Bottom Iron Rule)

**NEVER hardcode `@page { size: W H }` or `.poster { min-height: Xpx }` with a fixed height.** This creates blank space at the bottom when content is shorter than the hardcoded value.

**Correct approach — use `html2poster.js`:**

`html2poster.js` automatically handles all of this — you just need to write the HTML correctly and call one command:

```bash
node "$PDF_SKILL_DIR/scripts/html2poster.js" poster.html --output poster.pdf --width 720px
```

It will automatically:
1. Add `overflow: hidden` to `.poster` container (clips decorative overflow)
2. Inject `@page { margin: 0 }` (zero margins)
3. Sync `html/body` background with `.poster` background
4. Measure `.poster` scrollHeight (actual content height)
5. Generate single-page vector PDF with exact dimensions

**⚠️ Do NOT use `html2pdf-next.js` for posters.** It is designed for multi-page documents and will inject 20mm margins / A4 pagination.

**⚠️ Do NOT write hand-written Playwright scripts for posters.** `html2poster.js` handles everything.

```css
/* ✅ CORRECT CSS for poster HTML (html2poster.js handles the rest): */
html, body { margin: 0; padding: 0; background: var(--c-bg); }
.poster { width: 720px; position: relative; background: var(--c-bg); }
/* Note: overflow:hidden on .poster is auto-injected by html2poster.js, 
   but including it in CSS is fine too */
```

**CSS iron rules:**
```css
html, body { margin: 0; padding: 0; background: var(--c-bg); }

/* Single poster canvas — NO fixed height */
.poster {
  width: 720px;
  position: relative;
  overflow: hidden;
  background: var(--c-bg);
  /* Height is determined by content, measured at render time */
}

/* Card/content block */
.card {
  background: rgba(255,255,255,0.7);
  border-radius: 12px;
  padding: 24px 28px;
  margin-bottom: 20px;
  /* No height, no max-height, no overflow:hidden */
}

/* CJK text iron rules */
.card, .card p, .card li {
  text-align: left;          /* Absolutely no justify! CJK stretches letter-spacing */
  line-height: 1.6;
  word-break: break-all;     /* CJK natural line break */
}

/* Image */
.hero-image {
  width: 100%;
  border-radius: 8px;
  margin-bottom: 20px;
  display: block;
}
```

**HTML structure template (Single Canvas):**
```html
<div class="poster">
  <!-- Hero section -->
  <div class="hero"> ... </div>

  <!-- City 1 -->
  <div class="city-section">
    <h2>南京</h2>
    <div class="items-row"> ... attractions ... </div>
    <div class="food-row"> ... food ... </div>
  </div>

  <!-- City separator (thin line, NOT page-break) -->
  <div class="city-sep"></div>

  <!-- City 2 -->
  <div class="city-section"> ... </div>

  <!-- Footer -->
  <div class="poster-footer"> ... </div>
</div>
```

**Why it's stable:**
- No fixed height — content naturally defines poster size
- No `overflow: hidden`, text always fully displayed
- `text-align: left` avoids CJK letter-spacing stretch
- Single canvas = one unified composition, not chopped pages
- PDF height measured at render time = zero blank space

**Convert to PDF and PNG:**
```bash
# PDF (vector, single-page, zero margins, auto-height):
node "$PDF_SKILL_DIR/scripts/html2poster.js" poster.html --output poster.pdf --width 720px

# PNG preview (screenshot):
# Use Playwright screenshot with measured height
```

> **⚠️ Do NOT write hand-written Playwright `page.pdf()` scripts.** Use `html2poster.js` which handles overflow:hidden, margin:0, background sync, and height measurement automatically.

### 12.2 Blueprint Grid Approach (Only for Simple Posters)

| Behavior | Status | Notes |
|------|------|------|
| Dynamic inset | ✅ Fixed | More content → smaller inset (`8% 10%`), less content → default (`10% 12%`) |
| Glass Canvas overflow | ✅ Fixed | `min-height:100%` replaces `height:100%`, removed `overflow:hidden` |
| Glass Canvas / Process List stretch | ✅ Fixed | Auto `align-items: stretch` |

### 12.3 Poster Marker in Blueprint

Add `scene: "poster"` marker in `art_direction` so `design_engine.py` can identify poster scenes and apply specific logic in the future:

```json
{
  "art_direction": {
    "scene": "poster",
    "palette_mode": "minimal",
    "color_harmony": "auto",
    "background_svg": "flow",
    "design_rationale": "..."
  }
}
```

---

---

## 14. PDF Conversion Iron Rules

### 14.1 Background Color Consistency
```css
/* Must ensure html/body background = poster canvas background */
html, body {
  background: var(--c-bg);  /* Same color as .canvas background */
}
```
- Playwright `page.pdf({ printBackground: true })` renders body background color
- If body is white but poster is gray, white borders appear in the PDF
- `design_engine.py` already auto-injects `background: var(--c-bg)`, but if bypassing the engine and writing HTML directly, **you must ensure manually**

**Multi-page posters / brochures with mixed page backgrounds:**
- When pages alternate between dark and light backgrounds, set `body { background }` to the **darkest page color** (see SKILL.md "Background Color Consistency" for full rationale)
- This eliminates sub-pixel white edges on dark pages without affecting light pages

### 14.2 Content Centering (Anti-Drift)

**Poster content must be centered in the PDF, no left or right drift allowed.**

Common drift causes and fixes:
| Cause | Fix | 
|------|------|
| `@page { margin }` not 0 | Must be `@page { size: <w> <h>; margin: 0; }` |
| `.safe-zone` `inset` left-right asymmetric | Ensure `inset: Y% X%` uses same X% for left and right |
| Component `grid_area` only uses partial columns | Most components should span `1 / 1 / X / 13` (full width) |
| Content container has `max-width` but no `margin: 0 auto` | Add `margin: 0 auto` to center |
| Playwright PDF default margin | Pass `margin: { top: 0, right: 0, bottom: 0, left: 0 }` |

### 14.3 Anti-Blank Edges (Dynamic Height Iron Rule)

**Poster edges, top, and bottom should not have large meaningless whitespace.**

**★★★ CRITICAL: Never hardcode poster height.** The poster is a single continuous canvas — its height is defined by content, not by a fixed CSS value.

| Pattern | Status | Result |
|---------|--------|--------|
| `@page { size: 720px 3600px }` | ❌ FORBIDDEN | Creates 853px+ blank space at bottom if content is shorter |
| `.poster { min-height: 3600px }` | ❌ FORBIDDEN | Same problem — blank bottom |
| `.poster { width: 720px }` (no height) | ✅ CORRECT | Content defines height naturally |
| `node html2poster.js poster.html --width 720px` | ✅ CORRECT | Auto-measures height, zero blank space |

**The 720×960 dimension is for multi-page documents with `page-break-after: always` only — NOT for single-canvas posters.**

- Content must make full use of page area, no more than 20% unused space within safe-zone
- If excess whitespace exists, enlarge font sizes or modules to balance the layout
- Checklist:
  - [ ] Poster height defined by content (no hardcoded height)?
  - [ ] PDF generated via `html2poster.js` (not html2pdf-next.js)?
  - [ ] First component starts from the top?
  - [ ] Last component reaches the bottom with proper padding?
  - [ ] Left-right margins symmetric?
  - [ ] No blank space at bottom of PDF?

---

## 15. Preflight Checklist (Poster-Specific)

Before outputting JSON Blueprint, verify the following items:

```
□ ★ Single-canvas check: poster is ONE continuous <div>, not split into separate pages? (unless user explicitly requests multi-page)
□ ★ Dynamic height check: no hardcoded @page size height or .poster min-height? PDF generated via html2poster.js?
□ ★ Anti-modularization check: ≤ 3 containers with background+border in single-page poster? No 2×2 card grid? Hierarchy via font size/weight not border colors?
□ Emphasis elements (price/date/address) highlighted via font size+weight, not wrapped in separate color blocks?
□ Overall looks like a design piece, not an APP interface or dashboard?
□ total_pages = 1 (single canvas)? (unless user explicitly requests multi-page booklet)
□ No hardcoded @page height or .poster min-height? Content defines height?
□ Left-right margins symmetric? (no left/right drift)
□ html/body background = poster canvas background? (no color mismatch)
□ No bottom blank space in PDF? (height measured dynamically)
□ Page main title ≥ 50px? Body text ≥ 24px?
□ Text volume ≤ 150 chars? (single Glass Canvas)
□ palette_mode not #FFFFFF pure white?
□ No single image filling the background?
□ No overlap between cards?
□ No Timeline / complex SVG structure diagrams?
□ All content fully displayed, not truncated?
□ Emphasis text font size < title font size?
□ Entire design ≤ 3 fonts?
□ cover_hero page ≤ 4 components?
□ Hero_Typography has <br> line breaks?
□ @page { margin: 0 } set? (prevents PDF drift)
```
