diff --git a/docs/README.md b/docs/README.md
index 33d23e7d4..56b023b30 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -13,7 +13,8 @@ back here.
| You are… | Read |
|---|---|
-| **New to GraphCompose** — what is it, how do I render my first PDF | [Getting started](getting-started.md) → [Hello world in root README](../README.md#hello-world) |
+| **New to GraphCompose** — what is it, how do I render my first PDF | [Your first document](first-document.md) → [Getting started](getting-started.md) |
+| **Author rendering an invoice or proposal** | [Built-in business templates](templates/business-templates.md) |
| **Author rendering a CV** with your own data | [Templates v2 (layered) — quickstart](templates/v2-layered/quickstart.md) |
| **Designer / author** wanting a custom visual style for CVs | [Templates v2 (layered) — authoring presets](templates/v2-layered/authoring-presets.md) |
| **Author using legacy v1.6 templates** (CV / cover-letter / invoice / proposal still using `*Spec` + builders) | [Templates v1-classic — landing](templates/v1-classic/README.md) |
@@ -26,10 +27,14 @@ back here.
## 📁 By category
### Getting started
+- **[first-document.md](first-document.md)** — the five-minute path from an empty project to a rendered PDF.
- **[getting-started.md](getting-started.md)** — DSL vs templates, first-render walk-through, decision tree.
+- **[capabilities.md](capabilities.md)** — one-glance map of every feature with its stability tier and guide link.
+- **[diagrams.md](diagrams.md)** — visual decision diagrams (authoring path, layout, output, lifecycle).
- **[troubleshooting.md](troubleshooting.md)** — symptom-first fixes for common gotchas: stray `?` glyphs, silent DOCX drops, optional-dependency `NoClassDefFoundError`, running the bundled examples.
### Templates
+- **[templates/business-templates.md](templates/business-templates.md)** — built-in invoice & proposal templates: the compose-first contract, end to end.
- **[templates/v2-layered/](templates/v2-layered/)** — 🆕 canonical going-forward pattern (CV is the reference implementation): `data` / `theme` / `components` / `widgets` / `presets`.
- **[templates/v1-classic/](templates/v1-classic/)** — the spec/builder/presets surface used by v1.6 CV, cover-letter, invoice, proposal templates. Still ships, still supported.
diff --git a/docs/capabilities.md b/docs/capabilities.md
new file mode 100644
index 000000000..0aa109414
--- /dev/null
+++ b/docs/capabilities.md
@@ -0,0 +1,89 @@
+# Capabilities
+
+A one-glance map of what GraphCompose can do, the main API for each,
+and its stability tier. "Stability" rows use the tiers defined in the
+[API stability policy](api-stability.md); "Guide" links to the page that
+shows it in context.
+
+This is a feature catalogue, not the contract — the
+[API stability policy](api-stability.md) is authoritative for what each
+tier promises, and [canonical ⇄ legacy parity](architecture/canonical-legacy-parity.md)
+tracks what is `Partial` or `Planned`.
+
+---
+
+## Authoring
+
+| Capability | Main API | Stability | Guide |
+|---|---|---|---|
+| Open a document session | `GraphCompose.document(...)` → `DocumentSession` | Stable | [Your first document](first-document.md) |
+| Describe content in reading order | `pageFlow(...)`, `module(...)`, `addSection(...)` | Stable | [Getting started](getting-started.md) |
+| Maintained document templates | `InvoiceTemplateV2`, `ProposalTemplateV2`, `cv.v2.*`, `coverletter.v2.*` | Stable | [Templates](templates/which-template-system.md) |
+| Reusable building blocks (helpers) | helper methods / widgets over the DSL | Stable | [Diagrams](diagrams.md#choose-your-authoring-path) |
+| Custom node / backend | `NodeDefinition`, render-handler SPI, `FixedLayoutBackend` | Extension SPI (`@Beta`) | [Extending](recipes/extending.md) |
+
+## Content
+
+| Capability | Main API | Stability | Guide |
+|---|---|---|---|
+| Paragraphs & rich inline text | `addParagraph(...)`, `addRich(...)`, `RichText` | Stable | [Recipes — rich text](recipes.md) |
+| Lists (flat & nested) | `addList(...)`, `ListBuilder` | Stable | [Recipes](recipes.md) |
+| Tables (spans, zebra, totals, repeat header) | `addTable(...)`, `DocumentTableCell` | Stable | [Advanced tables](recipes/tables.md) |
+| Raster images | `addImage(...)`, fit modes | Stable | [Shapes & images](recipes/shapes.md) |
+| Vector shapes, dividers, lines | `addShape(...)`, `addLine(...)`, `addEllipse(...)` | Stable | [Shapes](recipes/shapes.md) |
+| Charts (bar / line / pie) | `chart(ChartSpec...)`, `ChartData` | Stable | [Charts](recipes/charts.md) |
+| Barcodes & QR | `addBarcode(...)` | Stable | [Recipes](recipes.md) |
+
+## Layout
+
+| Capability | Main API | Stability | Guide |
+|---|---|---|---|
+| Columns that still flow | `addRow(row -> row.weights(...))` | Stable | [Layered page design](recipes/layered-page-design.md) |
+| Page-wide fills / bands | `pageBackground(...)`, `pageBackgrounds(...)` | Stable | [Page backgrounds](recipes/page-backgrounds.md) |
+| Overlap & alignment | `addLayerStack(...)` | Stable | [Layered page design](recipes/layered-page-design.md) |
+| Shape-as-container | `addContainer(...)`, `addCircle(...)`, `addEllipse(...)` | Stable | [Shape as container](recipes/shape-as-container.md) |
+| Fixed (x, y) placement | `addCanvas(w, h, canvas -> canvas.position(...))` | Stable | [Absolute placement](recipes/absolute-placement.md) |
+| Bleed to page edge | `bleedToEdge(...)` | Stable | [Page backgrounds](recipes/page-backgrounds.md) |
+| Transforms (rotate / scale) | `DocumentTransform` | Stable | [Transforms](recipes/transforms.md) |
+
+## Output & testing
+
+| Capability | Main API | Stability | Guide |
+|---|---|---|---|
+| Write a PDF file | `buildPdf()`, `buildPdf(Path)` | Stable | [Getting started](getting-started.md) |
+| Stream to a caller-owned stream | `writePdf(OutputStream)` | Stable | [Streaming](recipes/streaming.md) |
+| In-memory bytes | `toPdfBytes()` | Stable | [Getting started](getting-started.md) |
+| Editable Word (semantic) | `export(new DocxSemanticBackend())` | Stable (semantic, not PDF parity) | [Troubleshooting](troubleshooting.md) |
+| PDF chrome (metadata / watermark / header / footer / protection) | `metadata(...)`, `watermark(...)`, `header(...)`, `footer(...)`, `protect(...)` | Stable | [Getting started](getting-started.md) |
+| Layout snapshot regression | `LayoutSnapshotAssertions.assertMatches(...)` | Stable | [Layout snapshot testing](operations/layout-snapshot-testing.md) |
+| Visual (pixel) regression | `PdfVisualRegression` | Stable | [Layout snapshot testing](operations/layout-snapshot-testing.md) |
+| Render-only debug overlays | `guideLines(...)`, `debug(...)` | Stable | [Getting started](getting-started.md#debug-guide-lines) |
+
+## Navigation
+
+| Capability | Main API | Stability | Guide |
+|---|---|---|---|
+| External links | `addLink(...)`, `inlineLink(...)` | Stable | [Getting started](getting-started.md) |
+| Internal jumps | `anchor("x")` + `linkTo("x")` | Stable | [Getting started](getting-started.md) |
+| PDF outline bookmarks | `bookmark(new DocumentBookmarkOptions(...))` | Stable | [Getting started](getting-started.md) |
+
+---
+
+## New in 1.9.0
+
+These ship from 1.9.0 onward — confirm your dependency version before relying on them:
+
+| Capability | Main API |
+|---|---|
+| Printed page references | `addPageReference("anchor")` |
+| Generated Table of Contents | `addTableOfContents(toc -> toc.entry(...))` |
+| Page preview images | `toImage(pageIndex, dpi)`, `toImages(dpi)` |
+
+---
+
+## See also
+
+- [API stability policy](api-stability.md) — what each tier promises.
+- [Decision diagrams](diagrams.md) — visual "which API do I use?".
+- [Recipes](recipes.md) — the full cookbook.
+- [Which template system should I use?](templates/which-template-system.md) — template-surface decision.
diff --git a/docs/diagrams.md b/docs/diagrams.md
new file mode 100644
index 000000000..927339dd2
--- /dev/null
+++ b/docs/diagrams.md
@@ -0,0 +1,89 @@
+# Decision Diagrams
+
+Visual versions of the "which API do I reach for?" decisions. Each
+diagram renders on GitHub (Mermaid). The prose walkthroughs live in
+[Getting started](getting-started.md) and
+[Your first document](first-document.md).
+
+---
+
+## Choose your authoring path
+
+Start from intent. Most documents are either a maintained template or a
+custom page flow; helpers, layout primitives, and extensions come later.
+
+```mermaid
+flowchart TD
+ A[I want to generate a document] --> B{Known family?
CV / invoice / proposal / cover letter}
+ B -- Yes --> T[Use a maintained template]
+ B -- No --> C{Mostly reads top to bottom?}
+ C -- Yes --> F["GraphCompose.document(...) + pageFlow(...)"]
+ C -- Needs specific placement --> L["A layout primitive
(row / canvas / shape container)"]
+ F --> D{Same shape repeats across your app?}
+ D -- Yes --> H[Extract a helper / widget over the DSL]
+ D -- No --> Done1[Author the page flow]
+ H --> Done1
+ L --> Done2[See the layout diagram below]
+ T --> Done3[Supply the data spec, then render]
+```
+
+---
+
+## Where does content go on the page?
+
+Flow is the default. Reach for a stronger primitive only when the
+content has a specific placement relationship.
+
+```mermaid
+flowchart TD
+ A[Place content on the page] --> B{Top-to-bottom reading order?}
+ B -- Yes --> Flow[pageFlow / sections / modules]
+ B -- No --> C{Side by side, still part of the flow?}
+ C -- Yes --> Row["addRow(row -> row.weights(...))"]
+ C -- No --> D{A fill behind every page?}
+ D -- Yes --> BG["pageBackground(s)(...)"]
+ D -- No --> E{Overlap or a framed block?}
+ E -- Yes --> Layer[Layer stack / shape container]
+ E -- No --> Canvas["addCanvas(w, h, ...) — exact x/y"]
+```
+
+---
+
+## Where does the output go?
+
+Choose the output method by destination. Create one `DocumentSession`
+per render request.
+
+```mermaid
+flowchart TD
+ A[The document is built] --> B{Where does it go?}
+ B -- A file --> F["buildPdf() / buildPdf(path)"]
+ B -- HTTP / cloud stream --> S["writePdf(OutputStream)"]
+ B -- A byte array --> Y["toPdfBytes()"]
+ B -- A preview image --> I["toImage(...) / toImages(...)"]
+ B -- Editable Word --> D["export(new DocxSemanticBackend())"]
+```
+
+---
+
+## Document lifecycle
+
+What happens between your code and the PDF.
+
+```mermaid
+flowchart LR
+ A["GraphCompose.document(...)"] --> B[DocumentSession]
+ B --> C["pageFlow(...) → semantic nodes"]
+ C --> D[Layout + pagination]
+ D --> E[PDF output]
+ D -. inspect .-> S["layoutSnapshot() (testing)"]
+```
+
+---
+
+## See also
+
+- [Getting started](getting-started.md) — the prose decision tree and first render.
+- [Your first document](first-document.md) — a five-minute walk-through.
+- [Which template system should I use?](templates/which-template-system.md) — the template-surface decision.
+- [Capabilities](capabilities.md) — what GraphCompose can do, with stability tiers.
diff --git a/docs/first-document.md b/docs/first-document.md
new file mode 100644
index 000000000..56666d72e
--- /dev/null
+++ b/docs/first-document.md
@@ -0,0 +1,121 @@
+# Your First Document
+
+A five-minute path from an empty project to a real PDF. GraphCompose is
+session-first: you open a `DocumentSession`, describe content in reading order
+with a page flow, and render. No coordinates, no manual page breaks.
+
+> **Prerequisites:** Java 17+ and the `io.github.demchaav:graph-compose`
+> dependency — see the [README install snippet](../README.md#installation).
+
+## The smallest document
+
+Open a session for a file path, add one page flow, render. The engine handles
+placement and pagination.
+
+```java
+import com.demcha.compose.GraphCompose;
+import com.demcha.compose.document.api.DocumentPageSize;
+import com.demcha.compose.document.api.DocumentSession;
+
+import java.nio.file.Path;
+
+try (DocumentSession document = GraphCompose.document(Path.of("hello.pdf"))
+ .pageSize(DocumentPageSize.A4)
+ .margin(24, 24, 24, 24)
+ .create()) {
+
+ document.pageFlow(page -> page
+ .module("Summary", module -> module.paragraph("Hello GraphCompose")));
+
+ document.buildPdf();
+}
+```
+
+`GraphCompose.document(path)` configures the output; `create()` returns the
+`DocumentSession`. Use try-with-resources so the session is always released, even
+if rendering fails. Inside the session, `pageFlow(...)` is the document body:
+modules, sections, paragraphs, lists, tables, and rows are added top to bottom.
+
+## A real custom document
+
+The same Flow model scales to a multi-section document. There are still no
+coordinates and no manual page breaks — just structure in reading order.
+
+```java
+import com.demcha.compose.GraphCompose;
+import com.demcha.compose.document.api.DocumentPageSize;
+import com.demcha.compose.document.api.DocumentSession;
+
+import java.nio.file.Path;
+
+try (DocumentSession document = GraphCompose.document(Path.of("profile.pdf"))
+ .pageSize(DocumentPageSize.A4)
+ .margin(24, 24, 24, 24)
+ .create()) {
+
+ document.pageFlow()
+ .name("CandidateProfile")
+ .spacing(12)
+ .module("Professional Summary", module -> module.paragraph(
+ "Backend engineer focused on clean Java APIs, stable document "
+ + "output, and reusable template architecture."))
+ .module("Technical Skills", module -> module.bullets(
+ "Java 21 and Spring Boot",
+ "PDF document generation with GraphCompose",
+ "Layout snapshot testing and render regression checks"))
+ .module("Projects", module -> module.rows(
+ "GraphCompose - declarative document layout engine.",
+ "CVRewriter - profile-aware CV tailoring platform."))
+ .build();
+
+ document.buildPdf();
+}
+```
+
+The callback form (`pageFlow(page -> ...)`) builds and attaches the root for you.
+The builder form (`pageFlow().…build()`) gives you the fluent chain but you must
+call `.build()` yourself.
+
+## Already a known document? Use a template
+
+If your document is a known family — invoice, proposal, CV, cover letter — do not
+hand-build it. A maintained template maps a typed data object into the same
+session, then you render as usual:
+
+```java
+import com.demcha.compose.document.templates.builtins.InvoiceTemplateV2;
+import com.demcha.compose.document.theme.BusinessTheme;
+
+InvoiceTemplateV2 template = new InvoiceTemplateV2(BusinessTheme.modern());
+
+try (DocumentSession document = GraphCompose.document(Path.of("invoice.pdf")).create()) {
+ template.compose(document, invoice); // invoice = your InvoiceDocumentSpec
+ document.buildPdf();
+}
+```
+
+Templates and hand-written Flow compose into the *same* `DocumentSession`, so you
+can mix them. To choose a template surface, see
+[Which template system should I use?](templates/which-template-system.md).
+
+## Rendering on a server
+
+When the caller already owns the output stream — an HTTP response, a cloud
+upload — create the session *without* a default path and stream the PDF with
+`writePdf(OutputStream)` instead of `buildPdf()`. GraphCompose writes the
+stream but does not close it. For the full server snippet, see
+[Getting started — Streaming output](getting-started.md#streaming-output).
+
+Create one `DocumentSession` per render request; it is mutable and not
+thread-safe. Use `toPdfBytes()` only when the caller truly needs a byte array.
+
+## Where to go next
+
+- [Getting Started](getting-started.md) — themes, hero blocks, layer stacks,
+ shape-as-container, and built-in templates.
+- [Recipes](recipes.md) — themes, shapes, transforms, tables, and layout
+ snapshots.
+- [Which template system should I use?](templates/which-template-system.md) —
+ the decision tree for CV / invoice / proposal surfaces.
+- [Production Rendering](operations/production-rendering.md) — server-side
+ lifecycle, streaming, and load guidance.
diff --git a/docs/getting-started.md b/docs/getting-started.md
index 22de720cd..ded4d78ec 100644
--- a/docs/getting-started.md
+++ b/docs/getting-started.md
@@ -7,6 +7,8 @@ with `writePdf(...)`, `buildPdf()`, or `toPdfBytes()`.
> **Prerequisites:** Java 17+ and the `io.github.demchaav:graph-compose` dependency — see the [README install snippet](../README.md#installation).
+> **New to GraphCompose?** Start with [Your First Document](first-document.md) — a five-minute, copy-paste path from an empty project to a rendered PDF, then come back here for themes, layer stacks, and built-in templates.
+
## Templates vs DSL — pick the right starting point
GraphCompose has two layers a caller can target. Use this decision
diff --git a/docs/templates/business-templates.md b/docs/templates/business-templates.md
new file mode 100644
index 000000000..29c843ace
--- /dev/null
+++ b/docs/templates/business-templates.md
@@ -0,0 +1,179 @@
+# Built-In Business Templates — Invoice & Proposal
+
+GraphCompose ships maintained templates for two common business
+documents: **invoices** and **proposals**. You supply a typed data
+spec, pick a `BusinessTheme`, and the template renders a consistent,
+branded document. You never position anything by hand.
+
+> For **CVs and cover letters**, use the layered `cv.v2` / `coverletter.v2`
+> model instead — see the [Templates v2 (layered) quickstart](v2-layered/quickstart.md).
+> Not sure which surface to target? See
+> [Which template system should I use?](which-template-system.md).
+
+## The compose-first contract
+
+Every built-in template follows the same five steps. The template owns
+the document structure; your application owns the data and the output
+destination.
+
+1. Build a data spec — `InvoiceDocumentSpec` or `ProposalDocumentSpec`.
+2. Choose a `BusinessTheme` (commonly `BusinessTheme.modern()`).
+3. Create the template with the theme.
+4. Open a `DocumentSession`.
+5. `template.compose(document, spec)`, then render.
+
+The template composes into an **open** `DocumentSession` — it never
+decides file vs stream vs bytes. The caller does.
+
+## Invoice
+
+```java
+import com.demcha.compose.GraphCompose;
+import com.demcha.compose.document.api.DocumentPageSize;
+import com.demcha.compose.document.api.DocumentSession;
+import com.demcha.compose.document.templates.builtins.InvoiceTemplateV2;
+import com.demcha.compose.document.templates.data.invoice.InvoiceDocumentSpec;
+import com.demcha.compose.document.theme.BusinessTheme;
+
+import java.nio.file.Path;
+
+InvoiceDocumentSpec invoice = InvoiceDocumentSpec.builder()
+ .title("Invoice")
+ .invoiceNumber("GC-2026-041")
+ .issueDate("25 Jun 2026")
+ .dueDate("25 Jul 2026")
+ .reference("GraphCompose implementation")
+ .status("Due")
+ .fromParty(party -> party
+ .name("GraphCompose Studio")
+ .addressLines("10 Example Street", "London")
+ .email("billing@example.com")
+ .phone("+44 20 0000 0000")
+ .taxId("VAT GB000000000"))
+ .billToParty(party -> party
+ .name("Client Ltd")
+ .addressLines("22 Client Road", "Manchester")
+ .email("accounts@client.example"))
+ .lineItem("Document engine integration", "Implementation and support",
+ "1", "4,800.00", "4,800.00")
+ .summaryRow("Subtotal", "4,800.00")
+ .summaryRow("VAT", "960.00")
+ .totalRow("Total", "5,760.00")
+ .paymentTerm("Payment due within 30 days.")
+ .footerNote("Thank you for your business.")
+ .build();
+
+BusinessTheme theme = BusinessTheme.modern();
+InvoiceTemplateV2 template = new InvoiceTemplateV2(theme);
+
+try (DocumentSession document = GraphCompose.document(Path.of("invoice.pdf"))
+ .pageSize(DocumentPageSize.A4)
+ .pageBackground(theme.pageBackground())
+ .margin(28, 28, 28, 28)
+ .create()) {
+ template.compose(document, invoice);
+ document.buildPdf();
+}
+```
+
+The template renders a masthead with the invoice metadata, two-column
+seller / buyer blocks, a zebra-striped line-item table with summary and
+total rows, and a notes / payment-terms footer.
+
+## Proposal
+
+Same shape, different spec. Use a proposal when the artifact is sales or
+project scope rather than billing. The timeline takes a three-argument
+`timelineItem(phase, duration, details)`.
+
+```java
+import com.demcha.compose.GraphCompose;
+import com.demcha.compose.document.api.DocumentPageSize;
+import com.demcha.compose.document.api.DocumentSession;
+import com.demcha.compose.document.templates.builtins.ProposalTemplateV2;
+import com.demcha.compose.document.templates.data.proposal.ProposalDocumentSpec;
+import com.demcha.compose.document.theme.BusinessTheme;
+
+import java.nio.file.Path;
+
+ProposalDocumentSpec proposal = ProposalDocumentSpec.builder()
+ .title("Proposal")
+ .proposalNumber("PR-2026-014")
+ .preparedDate("25 Jun 2026")
+ .validUntil("25 Jul 2026")
+ .projectTitle("Document Automation Platform")
+ .executiveSummary("A proposal for building reliable PDF generation into the product workflow.")
+ .sender(party -> party
+ .name("GraphCompose Studio")
+ .addressLines("10 Example Street", "London")
+ .email("hello@example.com")
+ .website("graphcompose.example"))
+ .recipient(party -> party
+ .name("Client Ltd")
+ .addressLines("22 Client Road", "Manchester")
+ .email("product@client.example"))
+ .section("Scope", "Backend integration, template setup, and regression checks.")
+ .timelineItem("Week 1", "1 week", "Data model and rendering endpoint.")
+ .pricingRow("Implementation", "Fixed scope", "4,800.00")
+ .emphasizedPricingRow("Total", "Excluding tax", "4,800.00")
+ .acceptanceTerm("Proposal valid for 30 days.")
+ .footerNote("Prepared with GraphCompose.")
+ .build();
+
+BusinessTheme theme = BusinessTheme.modern();
+ProposalTemplateV2 template = new ProposalTemplateV2(theme);
+
+try (DocumentSession document = GraphCompose.document(Path.of("proposal.pdf"))
+ .pageSize(DocumentPageSize.A4)
+ .pageBackground(theme.pageBackground())
+ .margin(28, 28, 28, 28)
+ .create()) {
+ template.compose(document, proposal);
+ document.buildPdf();
+}
+```
+
+## Rendering on a server
+
+In production the spec usually comes from application data and the
+document is streamed to the caller's stream. The template composes the
+same way before any output method; create one session per request.
+
+```java
+import com.demcha.compose.GraphCompose;
+import com.demcha.compose.document.api.DocumentSession;
+import com.demcha.compose.document.templates.builtins.InvoiceTemplateV2;
+import com.demcha.compose.document.templates.data.invoice.InvoiceDocumentSpec;
+import com.demcha.compose.document.theme.BusinessTheme;
+
+import java.io.OutputStream;
+
+void streamInvoice(InvoiceDocumentSpec invoice, OutputStream out) throws Exception {
+ InvoiceTemplateV2 template = new InvoiceTemplateV2(BusinessTheme.modern());
+
+ try (DocumentSession document = GraphCompose.document().create()) {
+ template.compose(document, invoice);
+ document.writePdf(out);
+ }
+}
+```
+
+## Customizing
+
+If the built-in structure is close but not exact, prefer these moves in
+order:
+
+1. Check whether the spec already has the field you need.
+2. Change the `BusinessTheme` (or its tokens) for branding.
+3. Wrap the template call with session-level PDF chrome — a footer,
+ metadata, or protection — see [Getting started](../getting-started.md).
+4. Only fork or write a new template when the document *structure* itself
+ differs. A custom template implements the same `DocumentTemplate`
+ contract and composes into the same session.
+
+## See also
+
+- [Which template system should I use?](which-template-system.md) — the full decision tree.
+- [Templates v2 (layered) quickstart](v2-layered/quickstart.md) — CVs and cover letters.
+- [Recipes — themes](../recipes/themes.md) — customizing `BusinessTheme`.
+- [Streaming](../recipes/streaming.md) — server output patterns.