---
name: a4-print-documents
description: >
  Constraints for producing print-quality A4 HTML documents intended for PDF output
  via the on-device browser-pdf-save tool. Use when creating or modifying any HTML document
  that will be saved as PDF, uses A4 page dimensions, or contains glassmorphism/backdrop-filter effects
  that must survive print. Trigger phrases: "PDF", "print to PDF", "download PDF",
  "one-pager", "A4", "brochure", "executive document", "market analysis", or any request
  to generate a document for print or download.
---

# A4 Print-Ready HTML Documents

Constraints for producing HTML documents that render correctly when saved as PDF via the on-device `browser-pdf-save` tool. Every rule exists because violating it caused a visible problem in production documents.

## Rendering path

Generate the HTML document, then save it as PDF with the on-device browser tools (no Playwright). The order is: capture any glassmorphism print-fallback images first (see below), then `browser-navigate` to the served HTML and `browser-pdf-save` to the output path. `browser-pdf-save` uses Chromium's print pipeline, so `@page` rules, page breaks, and `print-color-adjust` all apply and backgrounds are printed.

Serve the HTML over `http://127.0.0.1:<port>` and navigate to that URL — open it from a local server rather than relying on a `file://` path. `browser-navigate` keeps the page alive so a screenshot pass and the PDF pass act on the same rendered document.

## Glassmorphism does not survive print

`backdrop-filter: blur()`, CSS glassmorphism, and layered transparency effects render as flat, transparent boxes in browser print/PDF output. This is a browser limitation, not a CSS error.

**Required pattern:** Every section using glassmorphism needs a pre-rendered PNG fallback that is hidden on screen and shown only in print.

```css
.cover-print-img { display: none; }

@media print {
  .cover > *:not(.cover-print-img) { display: none !important; }
  .cover-print-img {
    display: block !important;
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
  }
}
```

```html
<div class="cover">
  <img class="cover-print-img" src="cover-print.png" alt="">
  <!-- live glassmorphism content follows -->
</div>
```

This pattern applies to every full-bleed glassmorphism section (cover pages, back pages).

### Capturing the print image

Each glassmorphism section needs its own screenshot — you cannot reuse a print image from a different document or a differently-sized section. Capture it with the on-device browser tools against the served HTML:

1. **Serve the HTML over HTTP** and `browser-navigate` to `http://127.0.0.1:<port>/...` — open it from a local server, not a `file://` path.
2. **Match the viewport to the element.** Read the element's bounding box with `browser-evaluate` (`document.querySelector('.cover').getBoundingClientRect()`), then `browser-resize` to `Math.ceil(width) + 1` by `Math.ceil(height) + 1` to eliminate scrollbars.
3. **Hide UI chrome** with `browser-evaluate` — hide download buttons and navigation overlays and set `document.body.style.overflow = 'hidden'` to suppress body-level scrollbars.
4. **Capture the specific element** with `browser-screenshot` passing the element's CSS selector (e.g. `.cover`, `.backpage`) — the selector clips to that element, not the full page.
5. **Save the PNG** alongside the HTML, named for the section it replaces (e.g. `cover-print.png`, `backpage-print.png`).
6. **Restore** hidden elements and overflow with `browser-evaluate` after capture.
7. **Verify** the saved PNG visually — confirm no scrollbars, no UI chrome, correct dimensions.

After capturing all print images, save the final PDF with `browser-pdf-save` to the output path.

## Page margins, page numbers, and running footers

Use `@page` margin boxes for page numbers and running footers — not `position: fixed` elements.

```css
@page {
  margin: 0.6in 0.7in 0.8in;
  @bottom-center {
    content: counter(page);
    font-family: 'Playfair Display', Georgia, serif;
    font-size: 9pt;
    color: #8A8A84;
  }
}

/* Cover page: zero margins, no page number */
@page :first {
  margin: 0;
  @bottom-center { content: none; }
}

/* Named page for full-bleed back page */
@page backpage-full {
  margin: 0;
  @bottom-center { content: none; }
}
```

Then in `@media print`, the cover needs NO `page:` property — `@page :first` handles it. Only the backpage needs the named page assignment:

```css
@media print {
  .cover {
    width: auto; margin: 0; box-shadow: none; /* reset screen-only properties */
    min-height: 0;
    height: 100vh;
    page-break-after: always;
    position: relative;
    overflow: hidden;
  }
  .backpage {
    width: auto; margin: 0; box-shadow: none; /* reset screen-only properties */
    min-height: 0;
    height: 100vh;
    page-break-before: always;
    position: relative;
    overflow: hidden;
    page: backpage-full;
  }
}
```

**Key details:**
- `@page :first` zeros the margins for the cover — no named page needed on the cover element.
- Named pages are only needed for non-first full-bleed pages (backpage).
- `counter(page)` gives automatic page numbering with no JavaScript.
- No `position: fixed` hacks are needed for repeating headers/footers.
- If the screen CSS uses `width: 210mm; margin: 20px auto; box-shadow: ...` on the cover/backpage (for the card-on-grey-background look), the print CSS MUST explicitly reset these: `width: auto; margin: 0; box-shadow: none;`. Screen-only layout properties leak into print if not reset. Do NOT use `width: 100vw` or `margin: 0 !important` — just `width: auto; margin: 0`.
- The print image uses `position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;`.

## A4 layout — continuous flow, not fixed-height boxes

Forcing each section into a fixed `height: 297mm` div creates large whitespace gaps when content doesn't fill a full page.

**Correct approach:**

- **Cover page:** `min-height: 100vh` on screen; `height: 100vh; page-break-after: always;` in print.
- **Content:** Flows naturally. The `@page` margins handle spacing. No fixed-height containers.
- Use `page-break-inside: avoid` on logical units (cards, tables, callouts, stat groups, timeline items, competitor cards, pullquotes).
- Use `page-break-before: always` on major section dividers (part breaks).
- Use `page-break-after: avoid` on headings and section numbers so they don't strand at the bottom of a page.
- Set `orphans: 3; widows: 3;` on paragraphs to prevent single-line stragglers.

## Dark backgrounds are stripped in print

Browsers strip background colours by default when printing.

**Required:** Add both properties to every dark element in `@media print`:

```css
@media print {
  .dark-element {
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }
}
```

## Stripping glassmorphism from content areas

In multi-page documents, strip `backdrop-filter` from content sections (where it won't render correctly anyway) while preserving it on cover/backpage elements that are replaced with print images:

```css
@media print {
  /* Strip glass from content */
  .content *, .content *::before, .content *::after {
    backdrop-filter: none !important;
    -webkit-backdrop-filter: none !important;
    border-radius: 0 !important;
    box-shadow: none !important;
  }
}
```

## Document structure pattern

Multi-page documents follow this structure:

1. **Cover** — full-bleed, glassmorphism, with print image fallback. Uses `@page :first { margin: 0; }` — NO named page on the element. `page-break-after: always`.
2. **Table of contents** (optional) — `page-break-after: always` to start content on a fresh page.
3. **Content sections** — flowing, with part dividers using `page-break-before: always`.
4. **Back page** — full-bleed CTA/contact page. **Not** an inline footer div — a separate top-level element outside `.body-wrap` with its own named page, print image fallback, and `page-break-before: always`.

**The back page is mandatory for multi-page documents.** An inline CTA footer (a `div` inside flowing content) ends up floating mid-page with whitespace below it. The back page pattern guarantees a clean final page.

```css
@page backpage-full {
  margin: 0;
  @bottom-center { content: none; }
}

@media print {
  .backpage {
    page: backpage-full;
    min-height: 0; height: 100vh;
    page-break-before: always;
    position: relative; overflow: hidden;
  }
  .backpage > *:not(.backpage-print-img) { display: none !important; }
  .backpage-print-img {
    display: block !important;
    position: absolute;
    inset: 0;
    width: 100%; height: 100%;
    object-fit: cover;
  }
}
```

```html
<!-- Outside .body-wrap, after it closes -->
<div class="backpage">
  <img class="backpage-print-img" src="backpage-print.png" alt="" style="display:none;">
  <!-- live backpage content (bg, text, contact info) -->
</div>
```

The backpage print image is captured the same way as the cover — `browser-screenshot` with the element selector, UI chrome hidden via `browser-evaluate`.

## Single-page documents

For single A4 documents (one-pagers), use fixed dimensions:

```css
.page {
  width: 210mm;
  min-height: 297mm;
  margin: 0 auto;
  overflow: hidden;
}

@page { size: A4; margin: 0; }

@media print {
  .page {
    width: 210mm;
    height: 297mm;
    page-break-after: always;
  }
}
```

## Document background for multi-page screen viewing

Multi-page documents use a neutral body background (e.g. `body { background: var(--background); }`) on screen. In `@media print`, reset to `body { background: white; }`.
