@openpresentation/opf 0.22.1 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/dist/browser.js +16 -16
  2. package/dist/{chunk-MAJHCGOW.js → chunk-3FWUA763.js} +4 -4
  3. package/dist/{chunk-SPCONMIY.js → chunk-4I3XPRAO.js} +1 -1
  4. package/dist/{chunk-7XNAMCGT.js → chunk-B2XALDGS.js} +6 -6
  5. package/dist/{chunk-LKMR3OLD.js → chunk-BOWBPSFF.js} +26 -9
  6. package/dist/{chunk-YUIEWE5B.js → chunk-CNOLYQ4C.js} +2 -2
  7. package/dist/{chunk-2SENTA2R.js → chunk-F55S5HDP.js} +1 -1
  8. package/dist/{chunk-23MOKE4B.js → chunk-L67MPTPZ.js} +43 -9
  9. package/dist/{chunk-VKNFZEMG.js → chunk-N3ZPLGK5.js} +137 -16
  10. package/dist/{chunk-KUNXFN7B.js → chunk-NIA2WEXN.js} +1 -1
  11. package/dist/{chunk-MG34DCDB.js → chunk-PLV7P236.js} +2 -2
  12. package/dist/{chunk-3T7MNRBY.js → chunk-TILQBXPZ.js} +1 -1
  13. package/dist/{chunk-BULGAKBY.js → chunk-TMOQ532D.js} +2 -2
  14. package/dist/{chunk-MLMTXXM4.js → chunk-VHNG75IH.js} +5 -5
  15. package/dist/{chunk-YSJRBRHB.js → chunk-VIOSRKMA.js} +1 -1
  16. package/dist/{chunk-SBWIZSPB.js → chunk-XKUEEDDN.js} +5 -5
  17. package/dist/{chunk-X3PQMMAY.js → chunk-YMBA3V2O.js} +1 -1
  18. package/dist/{chunk-3UAVON3O.js → chunk-ZIRSAFRC.js} +99 -0
  19. package/dist/{composition-CkHn6JrM.d.ts → composition-qX-igJe5.d.ts} +147 -15
  20. package/dist/composition.d.ts +1 -1
  21. package/dist/composition.js +1 -1
  22. package/dist/conversion-CY34E5WI.js +19 -0
  23. package/dist/{conversion-CSD65VLi.d.ts → conversion-t-YYHWL-.d.ts} +17 -5
  24. package/dist/convert.d.ts +2 -2
  25. package/dist/convert.js +7 -7
  26. package/dist/data.d.ts +1 -1
  27. package/dist/data.js +2 -2
  28. package/dist/{deck-report-vnWmXBjq.d.ts → deck-report-CJ6gd_6-.d.ts} +2 -2
  29. package/dist/deck.d.ts +5 -5
  30. package/dist/deck.js +10 -10
  31. package/dist/diff.js +5 -5
  32. package/dist/docs.js +9 -9
  33. package/dist/examples.d.ts +1 -1
  34. package/dist/examples.js +213 -0
  35. package/dist/format.js +2 -2
  36. package/dist/index.d.ts +11 -11
  37. package/dist/index.js +20 -20
  38. package/dist/markdown.d.ts +3 -3
  39. package/dist/markdown.js +7 -7
  40. package/dist/node-engine.d.ts +7 -7
  41. package/dist/node-engine.js +20 -20
  42. package/dist/{pagination-C4jzAtFy.d.ts → pagination-LiWxhbsz.d.ts} +3 -1
  43. package/dist/pagination.d.ts +2 -2
  44. package/dist/pagination.js +4 -4
  45. package/dist/patch.js +4 -4
  46. package/dist/{presentation-fN6tORPZ.d.ts → presentation-Dn-fm0iY.d.ts} +35 -0
  47. package/dist/repo-readme.js +1 -1
  48. package/dist/schemas.d.ts +2 -0
  49. package/dist/schemas.js +1 -1
  50. package/dist/spec/schemas/opf.schema.json +40 -0
  51. package/dist/types.d.ts +1 -1
  52. package/dist/{validation-types-B21fZfb2.d.ts → validation-types-CzPKoKWo.d.ts} +1 -1
  53. package/dist/{validator-Cflv4F9e.d.ts → validator-BuAV_HUo.d.ts} +2 -2
  54. package/dist/validator.d.ts +4 -4
  55. package/dist/validator.js +3 -3
  56. package/dist/yaml.d.ts +3 -3
  57. package/dist/yaml.js +7 -7
  58. package/package.json +5 -5
  59. package/dist/conversion-JDFXRXTK.js +0 -19
package/dist/docs.js CHANGED
@@ -4,7 +4,7 @@ var docsData = Object.freeze([
4
4
  "slug": "agent-skills",
5
5
  "file": "docs/agent-skills.md",
6
6
  "title": "AI agent skills for OPF",
7
- "markdown": "# AI agent skills for OPF\n\nThe repository ships six reusable skills in `skills/`. Each folder has a `SKILL.md` entrypoint and optional references, assets, or scripts. `agents/openai.yaml` supplies Codex display metadata; the instructions themselves are Markdown and do not require a hosted service.\n\n| Skill | Use it for |\n| --- | --- |\n| [opf-author](../skills/opf-author/SKILL.md) | Turn briefs and source material into valid OPF content; includes a complete starter deck |\n| [opf-layout](../skills/opf-layout/SKILL.md) | Dynamic composition, nested groups, promoted regions, overflow repair, and pagination |\n| [opf-presets](../skills/opf-presets/SKILL.md) | Catalog discovery, design inheritance, gallery reuse, colors, themes, and fonts |\n| [opf-edit](../skills/opf-edit/SKILL.md) | Precise JSON Patch edits, undo, canvas/schema integration, and copy/import |\n| [opf-export](../skills/opf-export/SKILL.md) | Browser previews, SVG/PNG/PDF/PPTX, assets, fonts, and export verification |\n| [opf-inspect](../skills/opf-inspect/SKILL.md) | Exact schema fields, catalog IDs, validation errors, and reference warnings |\n\nLoad only the skills relevant to the request. They distinguish the portable format from current renderer/editor capabilities, and distinguish imported document instructions from the user's request. They do not authorize publishing, sending decks, or changing unrelated project configuration.\n\n## Use from a checkout\n\nAn agent can read the entrypoint directly, for example:\n\n> Use `skills/opf-author/SKILL.md` to create a decision brief in OPF, then validate it using `skills/opf-inspect/SKILL.md`.\n\nThe root `AGENTS.md` points repository agents to these entrypoints. Skills read the current project's schema and package exports instead of hardcoding a historical field count or assuming a public package has unreleased APIs.\n\n## Install in an agent environment\n\nPublished CLI 0.20.0 bundles all six complete skill folders. Use Node 24 for the current packages and this checkout. From your project directory:\n\n```sh\nnpx @openpresentation/cli@0.20.0 skills install\n```\n\nThe default installs copies into `.agents/skills` in the current project, suitable for agents including Codex. It does not change AGENTS.md or any agent configuration. No symlink privileges, paid service, API key or AI provider is required. npm downloads the CLI on first use; the installed CLI then installs its bundled skills without network access. The explicit 0.20.0 pin makes this installation repeatable and matches the [current package train](compatibility-matrix.md). For CLI source development, use `node packages/cli/dist/index.js skills install` after building.\n\n| Target | Project directory | Personal directory with `--global` |\n| --- | --- | --- |\n| Default / `--agent universal` | `.agents/skills` | `~/.agents/skills` |\n| `--agent codex` | `.agents/skills` | `~/.codex/skills` |\n| `--agent claude-code` | `.claude/skills` | `~/.claude/skills` |\n| `--agent cursor` | `.cursor/skills` | `~/.cursor/skills` |\n\nFor example, `npx @openpresentation/cli@0.20.0 skills install --agent codex --global` installs personal Codex skills. For another compatible agent use `--directory <its-skills-directory>`; this option cannot be combined with `--agent` or `--global`. Restart or reload your agent if its skill discovery requires it. A compatible client can invoke the installed skills with names such as `$opf-author` or `$opf-inspect`.\n\nInspect or update the same destination:\n\n```sh\nnpx @openpresentation/cli@0.20.0 skills status\nnpx @openpresentation/cli@0.20.0 skills update\n```\n\nSupply the same target options used for installation. `status` is read-only and compares against the invoked CLI's bundled version; it does not query npm for newer releases. Repeated installation is idempotent. Updates check every installed file before changing any skill. Modified, added, deleted or unmanaged files cause the command to stop and list the conflicting folders; keep your customizations, move those folders outside the active skills directory, then retry. There is no force-overwrite option. A successful update returns backup paths outside the active skills directory for recovering the previous managed versions. Keep those backups until you have reviewed the update. Do not run concurrent writers: the installer lock coordinates other installer runs, but cannot lock an external editor.\n\nNo skills are installed by the repository build. Manual installation remains supported: copy whole folders from `skills/`, including references and scripts, to your agent's skill directory. Each folder is self-contained. The managed installer treats existing manual copies as unmanaged and preserves them.\n\nThe inspection helper requires Node 24 and `@openpresentation/opf` in the current project. In this checkout, build with `pnpm build` first. For an installed skill used outside the checkout, either run from an npm project that has the package or set `OPF_ROOT` to the built OPF checkout. It does not install dependencies, fetch catalogs, or modify input files.\n\n## Local CLI\n\nThe [installable CLI](../packages/cli/README.md) complements these skills with `opf create`, `opf validate`, `opf edit`, and schema/catalog lookup. It runs on the core it depends on; the inspection skill helper instead resolves the host project's core package. Check versions when moving between them. From code, core's `@openpresentation/opf` offers `convert`, `open` and `save` in Node (see the opf-export skill).\n\n## Examples of requests\n\n- \u201CUse $opf-author to turn these notes into a five-slide decision brief. Keep every factual claim sourced.\u201D\n- \u201CUse $opf-layout to fix this overflow without losing any text or notes.\u201D\n- \u201CUse $opf-presets to apply our brand colors while preserving slide-specific overrides.\u201D\n- \u201CUse $opf-edit to replace one table and retain all other document fields.\u201D\n- \u201CUse $opf-export to export the same reviewed slides to SVG and PPTX.\u201D (PPTX text is editable one line at a time; the skill says so.)\n- \u201CUse $opf-inspect to explain which background forms the installed schema accepts.\u201D\n\n## Maintenance\n\n`pnpm test:skills` checks skill links, schema-valid examples, and the inspection helper's actual behavior, including a copied standalone skill and a package installed in a consumer project. Run the skill-creator frontmatter validator when editing skill metadata. Behavioral tests are not evidence that every renderer option is visually complete.\n\nWhen schema/package APIs change, update only the affected skill/reference and its executable examples. Keep option lists in the canonical schema and catalogs. The format package and skill folders are separate distribution surfaces: CLI 0.20.0 includes the six skills; the core `@openpresentation/opf` package does not install agent configuration. Repository skill prose can be newer than the immutable snapshot bundled in an already-published CLI. These source documentation corrections do not change the CLI 0.20.0 tarball; `skills status` and `skills update` compare against the invoked CLI's bundled snapshot.\n"
7
+ "markdown": "# AI agent skills for OPF\n\nThe repository ships six reusable skills in `skills/`. Each folder has a `SKILL.md` entrypoint and optional references, assets, or scripts. `agents/openai.yaml` supplies Codex display metadata; the instructions themselves are Markdown and do not require a hosted service.\n\n| Skill | Use it for |\n| --- | --- |\n| [opf-author](../skills/opf-author/SKILL.md) | Turn briefs and source material into valid OPF content; includes a complete starter deck |\n| [opf-layout](../skills/opf-layout/SKILL.md) | Dynamic composition, nested groups, promoted regions, overflow repair, and pagination |\n| [opf-presets](../skills/opf-presets/SKILL.md) | Catalog discovery, design inheritance, gallery reuse, colors, themes, and fonts |\n| [opf-edit](../skills/opf-edit/SKILL.md) | Precise JSON Patch edits, undo, canvas/schema integration, and copy/import |\n| [opf-export](../skills/opf-export/SKILL.md) | Browser previews, SVG/PNG/PDF/PPTX, assets, fonts, and export verification |\n| [opf-inspect](../skills/opf-inspect/SKILL.md) | Exact schema fields, catalog IDs, validation errors, and reference warnings |\n\nLoad only the skills relevant to the request. They distinguish the portable format from current renderer/editor capabilities, and distinguish imported document instructions from the user's request. They do not authorize publishing, sending decks, or changing unrelated project configuration.\n\n## Use from a checkout\n\nAn agent can read the entrypoint directly, for example:\n\n> Use `skills/opf-author/SKILL.md` to create a decision brief in OPF, then validate it using `skills/opf-inspect/SKILL.md`.\n\nThe root `AGENTS.md` points repository agents to these entrypoints. Skills read the current project's schema and package exports instead of hardcoding a historical field count or assuming a public package has unreleased APIs.\n\n## Install in an agent environment\n\nPublished CLI 0.20.0 bundles all six complete skill folders. Use Node 24 for the current packages and this checkout. From your project directory:\n\n```sh\nnpx @openpresentation/cli@0.20.0 skills install\n```\n\nThe default installs copies into `.agents/skills` in the current project, suitable for agents including Codex. It does not change AGENTS.md or any agent configuration. No symlink privileges, paid service, API key or AI provider is required. npm downloads the CLI on first use; the installed CLI then installs its bundled skills without network access. The explicit 0.20.0 pin makes this installation repeatable and matches the [current package train](compatibility-matrix.md). For CLI source development, use `node packages/cli/dist/index.js skills install` after building.\n\n| Target | Project directory | Personal directory with `--global` |\n| --- | --- | --- |\n| Default / `--agent universal` | `.agents/skills` | `~/.agents/skills` |\n| `--agent codex` | `.agents/skills` | `~/.codex/skills` |\n| `--agent claude-code` | `.claude/skills` | `~/.claude/skills` |\n| `--agent cursor` | `.cursor/skills` | `~/.cursor/skills` |\n\nFor example, `npx @openpresentation/cli@0.20.0 skills install --agent codex --global` installs personal Codex skills. For another compatible agent use `--directory <its-skills-directory>`; this option cannot be combined with `--agent` or `--global`. Restart or reload your agent if its skill discovery requires it. A compatible client can invoke the installed skills with names such as `$opf-author` or `$opf-inspect`.\n\nInspect or update the same destination:\n\n```sh\nnpx @openpresentation/cli@0.20.0 skills status\nnpx @openpresentation/cli@0.20.0 skills update\n```\n\nSupply the same target options used for installation. `status` is read-only and compares against the invoked CLI's bundled version; it does not query npm for newer releases. Repeated installation is idempotent. Updates check every installed file before changing any skill. Modified, added, deleted or unmanaged files cause the command to stop and list the conflicting folders; keep your customizations, move those folders outside the active skills directory, then retry. There is no force-overwrite option. A successful update returns backup paths outside the active skills directory for recovering the previous managed versions. Keep those backups until you have reviewed the update. Do not run concurrent writers: the installer lock coordinates other installer runs, but cannot lock an external editor.\n\nNo skills are installed by the repository build. Manual installation remains supported: copy whole folders from `skills/`, including references and scripts, to your agent's skill directory. Each folder is self-contained. The managed installer treats existing manual copies as unmanaged and preserves them.\n\nThe inspection helper requires Node 24 and `@openpresentation/opf` in the current project. In this checkout, build with `pnpm build` first. For an installed skill used outside the checkout, either run from an npm project that has the package or set `OPF_ROOT` to the built OPF checkout. It does not install dependencies, fetch catalogs, or modify input files.\n\n## Local CLI\n\nThe [installable CLI](../packages/cli/README.md) complements these skills with `opf create`, `opf validate`, `opf edit`, and schema/catalog lookup. It runs on the core it depends on; the inspection skill helper instead resolves the host project's core package. Check versions when moving between them. From code, core's `@openpresentation/opf` offers `convert`, `open` and `save` in Node (see the opf-export skill).\n\n## Examples of requests\n\n- \u201CUse $opf-author to turn these notes into a five-slide decision brief. Keep every factual claim sourced.\u201D\n- \u201CUse $opf-layout to fix this overflow without losing any text or notes.\u201D\n- \u201CUse $opf-presets to apply our brand colors while preserving slide-specific overrides.\u201D\n- \u201CUse $opf-edit to replace one table and retain all other document fields.\u201D\n- \u201CUse $opf-export to export the same reviewed slides to SVG and PPTX.\u201D (PPTX text is editable one line at a time unless you opt in to `--body-text native`, from opf-pptx and CLI 0.22.0; the skill says so.)\n- \u201CUse $opf-inspect to explain which background forms the installed schema accepts.\u201D\n\n## Maintenance\n\n`pnpm test:skills` checks skill links, schema-valid examples, and the inspection helper's actual behavior, including a copied standalone skill and a package installed in a consumer project. Run the skill-creator frontmatter validator when editing skill metadata. Behavioral tests are not evidence that every renderer option is visually complete.\n\nWhen schema/package APIs change, update only the affected skill/reference and its executable examples. Keep option lists in the canonical schema and catalogs. The format package and skill folders are separate distribution surfaces: CLI 0.20.0 includes the six skills; the core `@openpresentation/opf` package does not install agent configuration. Repository skill prose can be newer than the immutable snapshot bundled in an already-published CLI. These source documentation corrections do not change the CLI 0.20.0 tarball; `skills status` and `skills update` compare against the invoked CLI's bundled snapshot.\n"
8
8
  },
9
9
  {
10
10
  "slug": "catalog-schema-reference",
@@ -28,13 +28,13 @@ var docsData = Object.freeze([
28
28
  "slug": "cli",
29
29
  "file": "docs/cli.md",
30
30
  "title": "The `opf` CLI",
31
- "markdown": '# The `opf` CLI\n\nThe CLI (`@openpresentation/cli`, binary `opf`, Node 22 or later; verified on Node 24) creates, checks, edits, converts and\ninspects decks (see [its README](../packages/cli/README.md)). `opf validate` is the one checker ([validate](validate.md)).\n`opf convert` is the one command that changes formats: a deck form, PDF, PPTX, PNG and SVG. Every command runs core\'s\nfunctions, the ones applications call from [`@openpresentation/opf`](node.md); the commands add the flags, the JSON report and\nthe exit codes.\n\nEverything is deterministic and local: no network, no model, no telemetry, no system fonts. The same document, options and\ninstalled package versions give the same bytes on every operating system.\n\n## Commands\n\n| Command | What it does |\n| --- | --- |\n| `opf create [output] [--title <text> \\| --example <slug>]` | A one-slide starter deck, or one of core\'s bundled examples (`opf catalog examples` lists them). |\n| `opf validate <file\\|dir\\|->...` | The one checker: format, references, policy, accessibility, layout, content. Several files, folders and stdin in one run; `--format json\\|text\\|github`. |\n| `opf convert <input> <output>` | Any change of format: `.json`, `.opf.yaml`, `.opf.md` (and a plain `.md` input), `.pptx`, `.pdf`, `.png`, `.svg`, `.zip`, or stdin and stdout. |\n| `opf edit <file> [output] --patch <patch>` | A JSON Patch, applied whole or not at all, the result checked. |\n| `opf format <file>... [output]` | Canonical key order and layout, in the form the file has. |\n| `opf diff <a> <b>` | What changed between two decks (`--format text\\|json\\|patch`). |\n| `opf merge <base> <ours> <theirs> [output]` | A three-way merge; conflicts are listed. |\n| `opf stats <file>` | Neutral facts about a deck (`--format json\\|text`). |\n| `opf paginate <input> [output]` | Overflowing slides split into continuation slides, measured with the renderer\'s fonts when it is installed. |\n| `opf embed <input> [output]` | The catalog records a deck references, copied into it. |\n| `opf fill <template> <data> [output]` | A template\'s variables filled: one deck per record (the output is a name pattern), or `--combine`. The second argument is always the data (`-` for stdin). |\n| `opf ingest <data> [output] --as table\\|chart` | CSV, TSV or JSON rows as a table or chart slide, alone or added `--into` a deck. |\n| `opf doctor [deck]` | Which formats this install can write, and the one command that installs what is missing. |\n| `opf schemas`, `opf schema [name] [pointer]` | The OPF schemas. |\n| `opf catalogs`, `opf catalog <kind> [id]`, `opf catalog examples [slug]` | The pptx.gallery catalog (`@openpresentation/gallery`) and the bundled example decks. |\n| `opf skills <install\\|update\\|status>` | The six bundled agent skills. |\n\n`opf <command> --help` (or `-h`, or `opf help <command>`) prints a command\'s flags and examples.\n\nRemoved in 0.18 (each is a usage error, exit 2, that names its replacement): `opf render`, `opf export`, `opf import`,\n`opf from-md`, `opf to-md`, `opf from-yaml` and `opf to-yaml` (all `opf convert`), `opf create --from` (`opf convert`), and the\nflags `--output`, `--out`, `--out-dir` and `--name` (a positional output), `--input-format` (`--from`), `--json` (reports are\nJSON), `--dry-run` (stdout), `--data` (fill\'s second argument) and the deck values of `--format` (`--to`).\n\n## One output convention\n\nEvery command that writes a deck takes the same arguments:\n\n| Form | Meaning |\n| --- | --- |\n| `opf embed deck.opf.json` | No output: the deck goes to stdout, the report to stderr. `-` says the same. |\n| `opf embed deck.opf.json out.opf.json` | An output after the inputs: written atomically (a temporary sibling, then a rename); an existing file needs `--force`. |\n| `opf embed deck.opf.json -i` | `-i` (`--in-place`) rewrites the input, refusing if it changed since it was read. Not for stdin, and not with an output. |\n| `opf embed deck.opf.json --check` | Writes nothing and exits 1 when the command would change something: `format --check` (not canonical), `embed --check` (not self-contained) and `paginate --check` (a slide overflows). |\n\nThe form written is the output\'s extension (`.json`, `.opf.yaml`/`.yml`, `.opf.md`), else `--to json|yaml|md`, else the form\nthat was read. An existing output is refused before anything is printed or written.\n\n`opf fill` writes one deck per record through a file name pattern: `{n}` is the record number (padded to the width of the largest, the rule of `opf convert`), `{column}` a slug of that\ncolumn\'s value (`opf fill t.opf.md clients.csv decks/qbr-{client}.opf.md`); `--combine` writes one deck with every record\'s slides.\n\n## What each flag means\n\n| Flag | Meaning |\n| --- | --- |\n| `--format json\\|text\\|github\\|patch` | The report format only: `json` (the default), `text`, `github` (validate\'s workflow annotations) or `patch` (diff\'s JSON Patch). |\n| `--from json\\|yaml\\|md` | The form of a deck input: stdin, and names that say none. Without it, a name ending `.json`, `.opf.yaml`, `.yaml`, `.yml` or `.opf.md` says the form, and text that starts with `{` or `[` is JSON; anything else is a usage error that asks for `--from yaml\\|md`. `opf convert` also takes `--from pptx`. |\n| `--to json\\|yaml\\|md` | The form of a deck output, for stdout and names that say none. `opf convert` also takes `pdf`, `pptx`, `png`, `svg` and `zip`. A `--to` that disagrees with the output\'s extension is refused. |\n| `--data-format csv\\|tsv\\|json` | The form of the data of `opf fill` and `opf ingest` (default: the extension, else JSON when it starts with `{` or `[`, else CSV). |\n| `--fail-on error\\|warning\\|info` | Findings at or above this severity fail the command (exit 1) and nothing is written (default `error`). |\n| `--force` | Replace an existing output. |\n\n## Install\n\n**Verified package set (2026-10-16):** CLI 0.20.0 with core 0.20.3, renderer\n0.20.2, PPTX/editor 0.20.3 and Gallery 3.0.0. Use the complete pinned\n[quickstart installation](quickstart.md#versions) and its required font/converter\npeers. [Artifact verification](evidence/rr-85-final-published-set-2026-10-16/README.md).\n\nDeck forms (JSON, YAML, Markdown) need nothing beyond the CLI. PDF, PNG and SVG need `@openpresentation/opf-render`; PPTX output\nand `.pptx` input also need `@openpresentation/opf-pptx`. Both are **optional peer dependencies** of the CLI and of core (decision\nRR-27, below), loaded the first time a command needs them, from beside the CLI first and from the working directory second.\n\n`opf doctor` says what this install can write and prints the one command that installs the rest:\n\n```sh\nopf doctor --format text # per format: ready, or what is missing; then one install command\nopf doctor deck.opf.md # JSON; with a deck, a PDF needs sharp only when the deck has pictures\n```\n\nThe command matches the package manager that runs the CLI (`npm_config_user_agent`: npm, pnpm, yarn or bun) and the way the CLI\nis installed: global (`npm install -g ...`), a project (`npm install ...`), or one npx run (`npx -p @openpresentation/cli -p ... opf <command>`).\nA conversion that misses a peer exits 2 with `code: "peer-not-installed"` (or `peer-too-old`), and its `install` field and message\ncarry that one command for the format asked for. What each output needs beside `@openpresentation/opf-render`:\n\n| Output | Also install |\n| --- | --- |\n| every drawn format (the office font pack) | `@expo-google-fonts/roboto@0.4.3 @expo-google-fonts/roboto-mono@0.4.2 @expo-google-fonts/caladea@0.4.2 @expo-google-fonts/arimo@0.4.3 @expo-google-fonts/tinos@0.4.2 @expo-google-fonts/cousine@0.4.3 @expo-google-fonts/gelasio@0.4.1 @expo-google-fonts/noto-sans@0.4.2` |\n| `png` | `@resvg/resvg-js@^2.6.2 sharp@^0.35.5` |\n| `pdf` | nothing for text in the default vector mode; `pdf-lib@^1.17.1` for `--raster`; `sharp@^0.35.5` when the deck has pictures |\n| `svg` | the fonts only |\n| `pptx`, a `.pptx` input | `@openpresentation/opf-pptx` |\n\n## From code\n\nThe engine the commands run is core\'s `@openpresentation/opf` in Node, which an application imports instead of spawning `opf`.\nIn Node, `validate`, `stats`, `paginate`, `embed` and `edit` register the gallery (`@openpresentation/gallery`) when a call names none, as the commands\ndo, so `opf validate deck` and `validate(deck)` agree:\n\n```ts\nimport * as opf from "@openpresentation/opf";\n\nawait opf.convert("deck.opf.md", "deck.pdf"); // opf convert deck.opf.md deck.pdf\nconst deck = await opf.open("deck.pptx"); // opf convert deck.pptx -\nconst { files } = await opf.convert(deck, { format: "png", scale: 2 }); // bytes, nothing written\nconst { presentation } = opf.edit(deck, patch); // opf edit deck --patch patch.json\nconst { decks } = opf.fill(template, csvText); // opf fill template data.csv out-{n}.opf.json\n```\n\nThe options are the flags in camel case (`--raster` is `raster: true`, `--fonts` is `fonts` (a folder or a list of folders),\n`--asset-dir` is `assetDir`, `--include-hidden` is `includeHidden`). The commands refuse an existing output without `--force`;\n`convert` replaces it unless `overwrite: false`. See [OPF files in Node](node.md).\n\n## `opf convert`\n\n```sh\nopf convert deck.opf.md deck.pdf\nopf convert deck.opf.md slides/deck.png --slides 1,3-5 --scale 2 # slides/deck-1.png, -3, -4, -5\nopf convert deck.opf.md "slides/slide-{n}.png" # slides/slide-1.png, slide-2.png, ... (always numbered)\nopf convert deck.opf.md slides.zip --to svg\nopf convert deck.pptx deck.opf.md --signals signals.json\nopf convert outline.md deck.opf.yaml # a plain .md input is OPF Markdown\nopf convert deck.opf.json deck.opf.yaml --schema-comment\nopf convert deck.opf.md - --to pdf > deck.pdf\n```\n\n| Output | Written as |\n| --- | --- |\n| `.pdf`, `.pptx` | one file |\n| `.png`, `.svg` | one file per slide beside the output, named after it: `slides/deck.png` writes `slides/deck-1.png`, `-2`, ... (the deck\'s slide numbers). One selected slide, or a one-slide deck, is written to the output itself. The extension is the format: `deck.png` is always PNG. With `{n}` in the output (`"slides/slide-{n}.png"`) the files are always numbered, even for one slide or one selected slide, and `{n}` is the deck\'s slide number (2 and 5 with `--slides 2,5`). `{n}` is the only placeholder: any other `{key}`, and `{n}` in `.pdf`, `.pptx`, `.zip` or a deck form, is a usage error (exit 2, `invalid-option`). |\n\nNumbers are padded with zeros to the width of the largest number written: `1`...`9` for slides 1 to 9, `01`...`99` up to 99, `001` from 100. With `--slides` the width comes from the largest slide selected (`--slides 2,5` gives `deck-2.png`, `deck-5.png`). The same rule pads `{n}` in `opf fill` (the largest record number) and the zip entries below.\n| `.zip` | one archive of the slides, its entries named as the plain `.png` and `.svg` files above (`slides.zip` holds `slides-1.png`, ...; always numbered, and no `{n}` in the archive\'s own name): PNG, or SVG with `--to svg`. Entries are stored with a fixed timestamp, so the archive is byte-identical everywhere. |\n| `.json`, `.opf.yaml`, `.yml`, `.opf.md` | the deck in that form |\n| `-` | stdout, with `--to`: one file (`pdf`, `pptx`, `zip`, a deck form, or one slide as `png` or `svg`) |\n\nA flag applies only when its format is involved; anywhere else it is a usage error (exit 2, `option-not-applicable`):\n\n| Involved | Flags |\n| --- | --- |\n| a Markdown input | `--split auto\\|rules\\|headings` (default `auto`: slides at `---` lines, and an outline with none at its `#` headings), `--title <text>` (the deck name unless the front matter sets one) |\n| a YAML input | `--aliases` (expand anchors, aliases and merge keys, at most 100) |\n| a `.pptx` input | `--signals <file>` (also write the raw per-shape layout and style signals, `fromPptx` with `signals: true`) |\n| a YAML output | `--schema-comment` (the `# yaml-language-server: $schema=...` line) |\n| a Markdown output | `--drop-unsupported` (leave out what the dialect has no syntax for and list it as loss, instead of embedding it as YAML in a fence) |\n| pdf, png, svg | `--slides 1,3-5` (one-based; `2-` to the end), `--include-hidden` |\n| pdf, png, svg, pptx | `--paginate`, `--date YYYY-MM-DD`, `--fonts <dir>` (repeatable), `--asset-dir <dir>` |\n| png, raster pdf | `--scale 0.1-8` (1 draws the 1280 x 720 reference slide at 1280 x 720 pixels) |\n| pdf | `--raster` (each page a picture instead of selectable vector text) |\n| svg | `--text fonts\\|system\\|paths` (embed the faces the slide uses, none, or draw glyph outlines) |\n| pptx | `--charts auto\\|native\\|picture`, `--images compatible\\|preserve`, `--provenance full\\|references-only\\|none` |\n\nA `.pptx` input is imported, checked and then written or drawn; `findings` holds both steps (`import/` rules from the importer).\nPer-slide images and the PDF skip slides marked `hidden: true` unless `--include-hidden`; slides named with `--slides` are always\nwritten, and the report lists the slides left out in `skippedHidden`. The PPTX keeps a hidden slide as a hidden slide.\n\n## The other commands\n\n- **`opf create`** writes a starter deck (`--title`) or a bundled example (`--example <slug>`, from `opf catalog examples`).\n- **`opf validate`** checks files, folders (their `*.opf.json`, `*.opf.yaml`, `*.opf.yml` and `*.opf.md`, sorted, skipping\n `node_modules` and dot folders) and `-`. One input prints its report; several print `{ command, ok, files: [reports], counts }`.\n The exit status is the worst over every file. `--format github` prints `::error`, `::warning` and `::notice` annotations with the\n file, line and column. See [validate](validate.md).\n- **`opf paginate`** measures the page breaks with the fonts `opf convert --paginate` draws with when `@openpresentation/opf-render`\n is installed (`layout: "measured"`); without it they are estimated (`layout: "estimated"`) and `hint` names the install command.\n- **`opf fill`** reads CSV, TSV or JSON data; a blank cell keeps the variable\'s declared value; an unfilled required variable fails\n unless `--partial`; `--examples` fills from the declared examples. The second argument is always the data, whatever its name\n (`-` reads it from stdin), and the third is the output; a template alone fills from its own values, to stdout.\n- **`opf ingest`** gives the same slide id and bytes for the same inputs; `--dataset <id>` stores the rows in the deck\'s datasets.\n\n## Reports and errors\n\nEvery command that reports prints one envelope:\n\n```json\n{ "command": "convert", "ok": true, "input": { "file": "/abs/deck.opf.md", "sha256": "\u2026", "bytes": 1234 },\n "outputs": [{ "file": "/abs/deck.pdf", "sha256": "\u2026", "bytes": 56789, "mediaType": "application/pdf" }],\n "findings": [], "counts": { "error": 0, "warning": 0, "info": 0 }, "format": "pdf", "\u2026": "\u2026" }\n```\n\n`input` is `null` when nothing was read and a list when several were (diff, merge, fill). The report is on stdout, or on stderr when\nstdout carries the document. Findings have the [`opf validate`](validate.md) shape (`ruleId`, `severity`, `category`, JSON Pointer\n`path`, `message`, `help`, and `location` when read from text); engine findings carry the prefix `render/`, `pptx/`, `pdf/`,\n`fonts/`, `import/` or `cli/`. The lookup commands (`schemas`, `schema`, `catalogs`, `catalog`, `--version`) print the data asked for.\n\nErrors are `{ command, ok: false, code, error, ... }` on stderr. The message states the fix: a missing argument names it and\nprints the usage, an unknown command, option or value suggests the nearest one ("Did you mean --check?"), and a removed command\nor flag names its replacement. Codes include `usage`, `unknown-command`, `unknown-option`, `removed-command`, `removed-option`,\n`missing-argument`, `extra-argument`, `missing-value`, `invalid-value`, `option-not-applicable`, `unknown-input-format`,\n`input-not-found`, `invalid-json`, `invalid-yaml`, `invalid-markdown`, `invalid-document`, `findings-at-fail-on`,\n`output-exists`, `merge-conflict`, `fill-failed`, `peer-not-installed` and `peer-too-old`.\n\nExit `0`: success (warnings allowed). Exit `1`: an invalid document, a finding at or above `--fail-on`, an existing output, a\nmerge conflict, or a `--check` that found a change. Exit `2`: usage, a read or I/O problem, text that is not valid JSON, YAML or\nMarkdown, or a missing or too-old peer. (`opf validate` reports text that does not parse as a finding, exit 1.)\n\n## Fonts\n\nThe renderer\'s bundled open font pack (the office pack: Carlito, Intos for Aptos, the open families font schemes select and the\nopen replacements the font policy routes proprietary families to) with the visual substitution policy, plus the files from\n`--fonts`. **System fonts are never loaded**, and nothing is downloaded. Font substitutions are listed under\n`fonts.substitutions` in the report. The PPTX keeps the font names the document chose.\n\nScripts beyond Latin, Greek and Cyrillic need the optional Noto script packages of the renderer (`@expo-google-fonts/noto-sans-jp`\nand so on); install the ones named in the `fonts/script-font-not-installed` diagnostic next to the CLI. A standalone SVG\n(`--text fonts`, the default) embeds the faces its text uses; `--text system` omits all font bytes; `--text paths` draws the text\nas glyph outlines (the real text kept invisible beside it for selection and search), so the SVG needs no font.\n\n## Images and assets\n\nRelative image paths and `file:` paths resolve against the input\'s folder (or `--asset-dir`). Only `.png`, `.jpg`, `.jpeg`,\n`.gif`, `.webp` and `.svg` files whose content matches are read, and only inside that folder (symlinks are resolved first). URLs\nare never fetched. For SVG and PNG an unreadable image draws the renderer\'s placeholder with an `unresolved-asset` or\n`cli/asset-blocked` warning; for PPTX an unreadable or blocked local path stops the export (`pptx/asset-unresolved`), and an\nunresolved remote URL becomes an unavailable-image placeholder with a `pptx/unresolved-asset` warning. Use `--fail-on warning` to\nreject that output before any file is written.\n\n## Markdown and YAML\n\nJSON stays the canonical form of a deck. YAML (`deck.opf.yaml`) and Markdown (`deck.opf.md`) are authoring forms of the same data,\nand every command that reads or writes a deck reads and writes all three:\n\n```sh\nopf convert deck.md deck.opf.json # Markdown in the OPF dialect (or an outline) to a deck\nopf convert deck.opf.json deck.opf.md # a deck as Markdown that reads back unchanged\nopf convert deck.opf.yaml deck.opf.json --aliases # strict YAML (--aliases expands anchors)\nopf validate deck.opf.yaml deck.opf.md # every command reads .yaml, .yml and .opf.md\nopf edit deck.opf.md --patch changes.json -i # the file stays Markdown\nopf format deck.opf.md --check # canonical Markdown\nopf validate - --from yaml < deck.txt # stdin and other names: --from\n```\n\nSee [Markdown and outlines](markdown.md) and [OPF as YAML](yaml.md). A syntax error exits 2 with the line and column (`opf\nvalidate` reports it as a `yaml/<rule>` or `markdown/<rule>` finding and exits 1); a command that rewrites a YAML file does not\nkeep its comments and says so on stderr, and one that has to put content into an `opf-slide`/`opf-block` fence says so too.\n\n## Runtime choices\n\n- **Peers, not bundled.** opf-render and opf-pptx are optional peer dependencies loaded lazily. opf-pptx pulls the native `sharp`\n engine, opf-render `resvg`, `fontkit` and the font packs (about 135 MB installed); bundling them would make the small CLI\n impossible to install offline or on a locked-down agent host, and validating or editing a deck would pay for it.\n- **JSON reports.** `--format text` gives human-readable output where a command has it; `--format github` gives CI annotations.\n- **No clock.** `--date` is explicit so a rerun tomorrow gives the same bytes.\n- **Fonts are the bundled pack plus `--fonts`.** Never system fonts (owner font policy).\n- **Stored zip entries.** Deflate output differs between zlib builds, which would make archive hashes host-dependent.\n\n## Catalog names\n\n`opf catalogs` lists canonical camelCase names such as `colorSchemes` and `fontSchemes`, and `examples`. `opf catalog` also\naccepts the website-style `color-schemes`, `font-schemes` and `chart-types` spellings.\n'
31
+ "markdown": '# The `opf` CLI\n\nThe CLI (`@openpresentation/cli`, binary `opf`, Node 22 or later; verified on Node 24) creates, checks, edits, converts and\ninspects decks (see [its README](../packages/cli/README.md)). `opf validate` is the one checker ([validate](validate.md)).\n`opf convert` is the one command that changes formats: a deck form, PDF, PPTX, PNG and SVG. Every command runs core\'s\nfunctions, the ones applications call from [`@openpresentation/opf`](node.md); the commands add the flags, the JSON report and\nthe exit codes.\n\nEverything is deterministic and local: no network, no model, no telemetry, no system fonts. The same document, options and\ninstalled package versions give the same bytes on every operating system.\n\n## Commands\n\n| Command | What it does |\n| --- | --- |\n| `opf create [output] [--title <text> \\| --example <slug>]` | A one-slide starter deck, or one of core\'s bundled examples (`opf catalog examples` lists them). |\n| `opf validate <file\\|dir\\|->...` | The one checker: format, references, policy, accessibility, layout, content. Several files, folders and stdin in one run; `--format json\\|text\\|github`. |\n| `opf convert <input> <output>` | Any change of format: `.json`, `.opf.yaml`, `.opf.md` (and a plain `.md` input), `.pptx`, `.pdf`, `.png`, `.svg`, `.zip`, or stdin and stdout. |\n| `opf edit <file> [output] --patch <patch>` | A JSON Patch, applied whole or not at all, the result checked. |\n| `opf format <file>... [output]` | Canonical key order and layout, in the form the file has. |\n| `opf diff <a> <b>` | What changed between two decks (`--format text\\|json\\|patch`). |\n| `opf merge <base> <ours> <theirs> [output]` | A three-way merge; conflicts are listed. |\n| `opf stats <file>` | Neutral facts about a deck (`--format json\\|text`). |\n| `opf paginate <input> [output]` | Overflowing slides split into continuation slides, measured with the renderer\'s fonts when it is installed. |\n| `opf embed <input> [output]` | The catalog records a deck references, copied into it. |\n| `opf fill <template> <data> [output]` | A template\'s variables filled: one deck per record (the output is a name pattern), or `--combine`. The second argument is always the data (`-` for stdin). |\n| `opf ingest <data> [output] --as table\\|chart` | CSV, TSV or JSON rows as a table or chart slide, alone or added `--into` a deck. |\n| `opf doctor [deck]` | Which formats this install can write, and the one command that installs what is missing. |\n| `opf schemas`, `opf schema [name] [pointer]` | The OPF schemas. |\n| `opf catalogs`, `opf catalog <kind> [id]`, `opf catalog examples [slug]` | The pptx.gallery catalog (`@openpresentation/gallery`) and the bundled example decks. |\n| `opf skills <install\\|update\\|status>` | The six bundled agent skills. |\n\n`opf <command> --help` (or `-h`, or `opf help <command>`) prints a command\'s flags and examples.\n\nRemoved in 0.18 (each is a usage error, exit 2, that names its replacement): `opf render`, `opf export`, `opf import`,\n`opf from-md`, `opf to-md`, `opf from-yaml` and `opf to-yaml` (all `opf convert`), `opf create --from` (`opf convert`), and the\nflags `--output`, `--out`, `--out-dir` and `--name` (a positional output), `--input-format` (`--from`), `--json` (reports are\nJSON), `--dry-run` (stdout), `--data` (fill\'s second argument) and the deck values of `--format` (`--to`).\n\n## One output convention\n\nEvery command that writes a deck takes the same arguments:\n\n| Form | Meaning |\n| --- | --- |\n| `opf embed deck.opf.json` | No output: the deck goes to stdout, the report to stderr. `-` says the same. |\n| `opf embed deck.opf.json out.opf.json` | An output after the inputs: written atomically (a temporary sibling, then a rename); an existing file needs `--force`. |\n| `opf embed deck.opf.json -i` | `-i` (`--in-place`) rewrites the input, refusing if it changed since it was read. Not for stdin, and not with an output. |\n| `opf embed deck.opf.json --check` | Writes nothing and exits 1 when the command would change something: `format --check` (not canonical), `embed --check` (not self-contained) and `paginate --check` (a slide overflows). |\n\nThe form written is the output\'s extension (`.json`, `.opf.yaml`/`.yml`, `.opf.md`), else `--to json|yaml|md`, else the form\nthat was read. An existing output is refused before anything is printed or written.\n\n`opf fill` writes one deck per record through a file name pattern: `{n}` is the record number (padded to the width of the largest, the rule of `opf convert`), `{column}` a slug of that\ncolumn\'s value (`opf fill t.opf.md clients.csv decks/qbr-{client}.opf.md`); `--combine` writes one deck with every record\'s slides.\n\n## What each flag means\n\n| Flag | Meaning |\n| --- | --- |\n| `--format json\\|text\\|github\\|patch` | The report format only: `json` (the default), `text`, `github` (validate\'s workflow annotations) or `patch` (diff\'s JSON Patch). |\n| `--from json\\|yaml\\|md` | The form of a deck input: stdin, and names that say none. Without it, a name ending `.json`, `.opf.yaml`, `.yaml`, `.yml` or `.opf.md` says the form, and text that starts with `{` or `[` is JSON; anything else is a usage error that asks for `--from yaml\\|md`. `opf convert` also takes `--from pptx`. |\n| `--to json\\|yaml\\|md` | The form of a deck output, for stdout and names that say none. `opf convert` also takes `pdf`, `pptx`, `png`, `svg` and `zip`. A `--to` that disagrees with the output\'s extension is refused. |\n| `--data-format csv\\|tsv\\|json` | The form of the data of `opf fill` and `opf ingest` (default: the extension, else JSON when it starts with `{` or `[`, else CSV). |\n| `--fail-on error\\|warning\\|info` | Findings at or above this severity fail the command (exit 1) and nothing is written (default `error`). |\n| `--force` | Replace an existing output. |\n\n## Install\n\n**Verified package set (2026-10-16):** CLI 0.20.0 with core 0.20.3, renderer\n0.20.2, PPTX/editor 0.20.3 and Gallery 3.0.0. Use the complete pinned\n[quickstart installation](quickstart.md#versions) and its required font/converter\npeers. [Artifact verification](evidence/rr-85-final-published-set-2026-10-16/README.md).\n\nDeck forms (JSON, YAML, Markdown) need nothing beyond the CLI. PDF, PNG and SVG need `@openpresentation/opf-render`; PPTX output\nand `.pptx` input also need `@openpresentation/opf-pptx`. Both are **optional peer dependencies** of the CLI and of core (decision\nRR-27, below), loaded the first time a command needs them, from beside the CLI first and from the working directory second.\n\n`opf doctor` says what this install can write and prints the one command that installs the rest:\n\n```sh\nopf doctor --format text # per format: ready, or what is missing; then one install command\nopf doctor deck.opf.md # JSON; with a deck, a PDF needs sharp only when the deck has pictures\n```\n\nThe command matches the package manager that runs the CLI (`npm_config_user_agent`: npm, pnpm, yarn or bun) and the way the CLI\nis installed: global (`npm install -g ...`), a project (`npm install ...`), or one npx run (`npx -p @openpresentation/cli -p ... opf <command>`).\nA conversion that misses a peer exits 2 with `code: "peer-not-installed"` (or `peer-too-old`), and its `install` field and message\ncarry that one command for the format asked for. What each output needs beside `@openpresentation/opf-render`:\n\n| Output | Also install |\n| --- | --- |\n| every drawn format (the office font pack) | `@expo-google-fonts/roboto@0.4.3 @expo-google-fonts/roboto-mono@0.4.2 @expo-google-fonts/caladea@0.4.2 @expo-google-fonts/arimo@0.4.3 @expo-google-fonts/tinos@0.4.2 @expo-google-fonts/cousine@0.4.3 @expo-google-fonts/gelasio@0.4.1 @expo-google-fonts/noto-sans@0.4.2` |\n| `png` | `@resvg/resvg-js@^2.6.2 sharp@^0.35.5` |\n| `pdf` | nothing for text in the default vector mode; `pdf-lib@^1.17.1` for `--raster`; `sharp@^0.35.5` when the deck has pictures |\n| `svg` | the fonts only |\n| `pptx`, a `.pptx` input | `@openpresentation/opf-pptx` |\n\n## From code\n\nThe engine the commands run is core\'s `@openpresentation/opf` in Node, which an application imports instead of spawning `opf`.\nIn Node, `validate`, `stats`, `paginate`, `embed` and `edit` register the gallery (`@openpresentation/gallery`) when a call names none, as the commands\ndo, so `opf validate deck` and `validate(deck)` agree:\n\n```ts\nimport * as opf from "@openpresentation/opf";\n\nawait opf.convert("deck.opf.md", "deck.pdf"); // opf convert deck.opf.md deck.pdf\nconst deck = await opf.open("deck.pptx"); // opf convert deck.pptx -\nconst { files } = await opf.convert(deck, { format: "png", scale: 2 }); // bytes, nothing written\nconst { presentation } = opf.edit(deck, patch); // opf edit deck --patch patch.json\nconst { decks } = opf.fill(template, csvText); // opf fill template data.csv out-{n}.opf.json\n```\n\nThe options are the flags in camel case (`--raster` is `raster: true`, `--fonts` is `fonts` (a folder or a list of folders),\n`--asset-dir` is `assetDir`, `--include-hidden` is `includeHidden`). The commands refuse an existing output without `--force`;\n`convert` replaces it unless `overwrite: false`. See [OPF files in Node](node.md).\n\n## `opf convert`\n\n```sh\nopf convert deck.opf.md deck.pdf\nopf convert deck.opf.md slides/deck.png --slides 1,3-5 --scale 2 # slides/deck-1.png, -3, -4, -5\nopf convert deck.opf.md "slides/slide-{n}.png" # slides/slide-1.png, slide-2.png, ... (always numbered)\nopf convert deck.opf.md slides.zip --to svg\nopf convert deck.pptx deck.opf.md --signals signals.json\nopf convert outline.md deck.opf.yaml # a plain .md input is OPF Markdown\nopf convert deck.opf.json deck.opf.yaml --schema-comment\nopf convert deck.opf.md - --to pdf > deck.pdf\n```\n\n| Output | Written as |\n| --- | --- |\n| `.pdf`, `.pptx` | one file |\n| `.png`, `.svg` | one file per slide beside the output, named after it: `slides/deck.png` writes `slides/deck-1.png`, `-2`, ... (the deck\'s slide numbers). One selected slide, or a one-slide deck, is written to the output itself. The extension is the format: `deck.png` is always PNG. With `{n}` in the output (`"slides/slide-{n}.png"`) the files are always numbered, even for one slide or one selected slide, and `{n}` is the deck\'s slide number (2 and 5 with `--slides 2,5`). `{n}` is the only placeholder: any other `{key}`, and `{n}` in `.pdf`, `.pptx`, `.zip` or a deck form, is a usage error (exit 2, `invalid-option`). |\n\nNumbers are padded with zeros to the width of the largest number written: `1`...`9` for slides 1 to 9, `01`...`99` up to 99, `001` from 100. With `--slides` the width comes from the largest slide selected (`--slides 2,5` gives `deck-2.png`, `deck-5.png`). The same rule pads `{n}` in `opf fill` (the largest record number) and the zip entries below.\n| `.zip` | one archive of the slides, its entries named as the plain `.png` and `.svg` files above (`slides.zip` holds `slides-1.png`, ...; always numbered, and no `{n}` in the archive\'s own name): PNG, or SVG with `--to svg`. Entries are stored with a fixed timestamp, so the archive is byte-identical everywhere. |\n| `.json`, `.opf.yaml`, `.yml`, `.opf.md` | the deck in that form |\n| `-` | stdout, with `--to`: one file (`pdf`, `pptx`, `zip`, a deck form, or one slide as `png` or `svg`) |\n\nA flag applies only when its format is involved; anywhere else it is a usage error (exit 2, `option-not-applicable`):\n\n| Involved | Flags |\n| --- | --- |\n| a Markdown input | `--split auto\\|rules\\|headings` (default `auto`: slides at `---` lines, and an outline with none at its `#` headings), `--title <text>` (the deck name unless the front matter sets one) |\n| a YAML input | `--aliases` (expand anchors, aliases and merge keys, at most 100) |\n| a `.pptx` input | `--signals <file>` (also write the raw per-shape layout and style signals, `fromPptx` with `signals: true`) |\n| a YAML output | `--schema-comment` (the `# yaml-language-server: $schema=...` line) |\n| a Markdown output | `--drop-unsupported` (leave out what the dialect has no syntax for and list it as loss, instead of embedding it as YAML in a fence) |\n| pdf, png, svg | `--slides 1,3-5` (one-based; `2-` to the end), `--include-hidden` |\n| pdf, png, svg, pptx | `--paginate`, `--date YYYY-MM-DD`, `--fonts <dir>` (repeatable), `--asset-dir <dir>` |\n| png, raster pdf | `--scale 0.1-8` (1 draws the 1280 x 720 reference slide at 1280 x 720 pixels) |\n| pdf | `--raster` (each page a picture instead of selectable vector text) |\n| svg | `--text fonts\\|system\\|paths` (embed the faces the slide uses, none, or draw glyph outlines) |\n| pptx | `--charts auto\\|native\\|picture`, `--images compatible\\|preserve`, `--provenance full\\|references-only\\|none`, `--body-text measured\\|native`, `--slide-numbers static\\|live` |\n\nA `.pptx` input is imported, checked and then written or drawn; `findings` holds both steps (`import/` rules from the importer).\nPer-slide images and the PDF skip slides marked `hidden: true` unless `--include-hidden`; slides named with `--slides` are always\nwritten, and the report lists the slides left out in `skippedHidden`. The PPTX keeps a hidden slide as a hidden slide.\n\n### PPTX text (RR-88, RR-91)\n\nBy default (`--body-text measured`) a wrapped paragraph becomes one text box per line, laid out exactly as the preview draws it, so editing a line in PowerPoint or Keynote does not reflow the rest. `--body-text native` opts in to whole paragraphs: a title that wraps is one Title placeholder, and a text, bullets or numbered-list region is one body that the application re-wraps when edited. What native mode cannot carry (groups, cards, nested or described lists, restarted numbering, titles with a hard break) stays measured and is reported as `native-body-fallback`. `--slide-numbers` decides a footer such as `{{slide.number}} / {{deck.slideCount}}`: `static` (the default) writes the number as fixed text, so Keynote shows the words around it, and `live` keeps the slide-number field, which Keynote draws as the digit alone because it drops the words next to a live number. A footer that is only `{{slide.number}}` stays a live field in both modes (the total is always fixed text). Both flags need an opf-pptx that reads them (0.22.0 or later); an older one is refused (`peer-too-old`) rather than ignoring them, and they are exit 2 for any other output.\n\n**Which to choose.** Pick `measured` (the default) when the deck is final and the PPTX is a delivery format: every line sits where the preview put it, and the deck is edited in OPF. Pick `native` when people will edit the text in PowerPoint or Keynote: a longer bullet wraps, a title keeps its placeholder (so outline view and slide titles work), and numbered lists renumber. Native text is re-wrapped by the application\'s own fonts and engine, so line breaks can differ from the preview; native wrapping is not yet qualified in every application, and anything it cannot carry (see above) stays measured with a `native-body-fallback` finding, so read the findings. `--slide-numbers static` is the default because Keynote replaces any paragraph that holds a live slide-number field with the number alone, so `{{slide.number}} / {{deck.slideCount}}` showed only the digit; the cost is that PowerPoint does not renumber a static number when you reorder slides there (choose `live` for that, and accept the digit-only footer in Keynote).\n\n## The other commands\n\n- **`opf create`** writes a starter deck (`--title`) or a bundled example (`--example <slug>`, from `opf catalog examples`).\n- **`opf validate`** checks files, folders (their `*.opf.json`, `*.opf.yaml`, `*.opf.yml` and `*.opf.md`, sorted, skipping\n `node_modules` and dot folders) and `-`. One input prints its report; several print `{ command, ok, files: [reports], counts }`.\n The exit status is the worst over every file. `--format github` prints `::error`, `::warning` and `::notice` annotations with the\n file, line and column. See [validate](validate.md).\n- **`opf paginate`** measures the page breaks with the fonts `opf convert --paginate` draws with when `@openpresentation/opf-render`\n is installed (`layout: "measured"`); without it they are estimated (`layout: "estimated"`) and `hint` names the install command.\n- **`opf fill`** reads CSV, TSV or JSON data; a blank cell keeps the variable\'s declared value; an unfilled required variable fails\n unless `--partial`; `--examples` fills from the declared examples. The second argument is always the data, whatever its name\n (`-` reads it from stdin), and the third is the output; a template alone fills from its own values, to stdout.\n- **`opf ingest`** gives the same slide id and bytes for the same inputs; `--dataset <id>` stores the rows in the deck\'s datasets.\n\n## Reports and errors\n\nEvery command that reports prints one envelope:\n\n```json\n{ "command": "convert", "ok": true, "input": { "file": "/abs/deck.opf.md", "sha256": "\u2026", "bytes": 1234 },\n "outputs": [{ "file": "/abs/deck.pdf", "sha256": "\u2026", "bytes": 56789, "mediaType": "application/pdf" }],\n "findings": [], "counts": { "error": 0, "warning": 0, "info": 0 }, "format": "pdf", "\u2026": "\u2026" }\n```\n\n`input` is `null` when nothing was read and a list when several were (diff, merge, fill). The report is on stdout, or on stderr when\nstdout carries the document. Findings have the [`opf validate`](validate.md) shape (`ruleId`, `severity`, `category`, JSON Pointer\n`path`, `message`, `help`, and `location` when read from text); engine findings carry the prefix `render/`, `pptx/`, `pdf/`,\n`fonts/`, `import/` or `cli/`. The lookup commands (`schemas`, `schema`, `catalogs`, `catalog`, `--version`) print the data asked for.\n\nErrors are `{ command, ok: false, code, error, ... }` on stderr. The message states the fix: a missing argument names it and\nprints the usage, an unknown command, option or value suggests the nearest one ("Did you mean --check?"), and a removed command\nor flag names its replacement. Codes include `usage`, `unknown-command`, `unknown-option`, `removed-command`, `removed-option`,\n`missing-argument`, `extra-argument`, `missing-value`, `invalid-value`, `option-not-applicable`, `unknown-input-format`,\n`input-not-found`, `invalid-json`, `invalid-yaml`, `invalid-markdown`, `invalid-document`, `findings-at-fail-on`,\n`output-exists`, `merge-conflict`, `fill-failed`, `peer-not-installed` and `peer-too-old`.\n\nExit `0`: success (warnings allowed). Exit `1`: an invalid document, a finding at or above `--fail-on`, an existing output, a\nmerge conflict, or a `--check` that found a change. Exit `2`: usage, a read or I/O problem, text that is not valid JSON, YAML or\nMarkdown, or a missing or too-old peer. (`opf validate` reports text that does not parse as a finding, exit 1.)\n\n## Fonts\n\nThe renderer\'s bundled open font pack (the office pack: Carlito, Intos for Aptos, the open families font schemes select and the\nopen replacements the font policy routes proprietary families to) with the visual substitution policy, plus the files from\n`--fonts`. **System fonts are never loaded**, and nothing is downloaded. Font substitutions are listed under\n`fonts.substitutions` in the report. The PPTX keeps the font names the document chose.\n\nScripts beyond Latin, Greek and Cyrillic need the optional Noto script packages of the renderer (`@expo-google-fonts/noto-sans-jp`\nand so on); install the ones named in the `fonts/script-font-not-installed` diagnostic next to the CLI. A standalone SVG\n(`--text fonts`, the default) embeds the faces its text uses; `--text system` omits all font bytes; `--text paths` draws the text\nas glyph outlines (the real text kept invisible beside it for selection and search), so the SVG needs no font.\n\n## Images and assets\n\nRelative image paths and `file:` paths resolve against the input\'s folder (or `--asset-dir`). Only `.png`, `.jpg`, `.jpeg`,\n`.gif`, `.webp` and `.svg` files whose content matches are read, and only inside that folder (symlinks are resolved first). URLs\nare never fetched. For SVG and PNG an unreadable image draws the renderer\'s placeholder with an `unresolved-asset` or\n`cli/asset-blocked` warning; for PPTX an unreadable or blocked local path stops the export (`pptx/asset-unresolved`), and an\nunresolved remote URL becomes an unavailable-image placeholder with a `pptx/unresolved-asset` warning. Use `--fail-on warning` to\nreject that output before any file is written.\n\n## Markdown and YAML\n\nJSON stays the canonical form of a deck. YAML (`deck.opf.yaml`) and Markdown (`deck.opf.md`) are authoring forms of the same data,\nand every command that reads or writes a deck reads and writes all three:\n\n```sh\nopf convert deck.md deck.opf.json # Markdown in the OPF dialect (or an outline) to a deck\nopf convert deck.opf.json deck.opf.md # a deck as Markdown that reads back unchanged\nopf convert deck.opf.yaml deck.opf.json --aliases # strict YAML (--aliases expands anchors)\nopf validate deck.opf.yaml deck.opf.md # every command reads .yaml, .yml and .opf.md\nopf edit deck.opf.md --patch changes.json -i # the file stays Markdown\nopf format deck.opf.md --check # canonical Markdown\nopf validate - --from yaml < deck.txt # stdin and other names: --from\n```\n\nSee [Markdown and outlines](markdown.md) and [OPF as YAML](yaml.md). A syntax error exits 2 with the line and column (`opf\nvalidate` reports it as a `yaml/<rule>` or `markdown/<rule>` finding and exits 1); a command that rewrites a YAML file does not\nkeep its comments and says so on stderr, and one that has to put content into an `opf-slide`/`opf-block` fence says so too.\n\n## Runtime choices\n\n- **Peers, not bundled.** opf-render and opf-pptx are optional peer dependencies loaded lazily. opf-pptx pulls the native `sharp`\n engine, opf-render `resvg`, `fontkit` and the font packs (about 135 MB installed); bundling them would make the small CLI\n impossible to install offline or on a locked-down agent host, and validating or editing a deck would pay for it.\n- **JSON reports.** `--format text` gives human-readable output where a command has it; `--format github` gives CI annotations.\n- **No clock.** `--date` is explicit so a rerun tomorrow gives the same bytes.\n- **Fonts are the bundled pack plus `--fonts`.** Never system fonts (owner font policy).\n- **Stored zip entries.** Deflate output differs between zlib builds, which would make archive hashes host-dependent.\n\n## Catalog names\n\n`opf catalogs` lists canonical camelCase names such as `colorSchemes` and `fontSchemes`, and `examples`. `opf catalog` also\naccepts the website-style `color-schemes`, `font-schemes` and `chart-types` spellings.\n'
32
32
  },
33
33
  {
34
34
  "slug": "compatibility-matrix",
35
35
  "file": "docs/compatibility-matrix.md",
36
36
  "title": "Compatibility matrix",
37
- "markdown": "# Compatibility matrix\n\nPublished registry evidence for the coordinated Node 24 toolchain. This matrix\nis the honest supported subset for [the developer quickstart](quickstart.md).\nIt is not universal Office parity and does not describe archived prototypes as\nshipped.\n\n**Verified artifacts (2026-10-17):** core 0.20.5, renderer/PPTX/editor 0.20.4,\nCLI 0.20.1 and Gallery 3.0.0.\n[Artifact verification](https://github.com/OpenPresentation/opf/blob/main/docs/evidence-rr-87-final-published-set-2026-10-17.md) binds\nthe selected archives to their release commits and trusted publication checks.\nEarlier matrices and native observations retain their historical identities.\nComplete native compatibility remains a separate gate.\n\n## Published 0.20 contract\n\nLayout `areas` uses nested arrays of cell names. Repeated names span rectangular\nregions and `.` denotes an empty cell. Gallery owns every default record;\nGallery 3 is required for catalog compatibility generation 3, while per-kind\nschema versions remain v2. Core 0.19 and later consume the Gallery package and\ndo not export `@openpresentation/opf/catalog` or ship a catalog snapshot.\n\nThe 0.20 engines share measured quote allocation, the 32 reference pixel\nstarting size for lists without an authored size, neutral structural borders\nand default right alignment for numeric table columns. Core and CLI forward\nregistered catalogs during PPTX import. PPTX preserves stretch images with\nzero crop through native save/re-import. These published contracts do not\nimply universal Office or full-catalog visual parity.\n\n## Current published package contracts\n\n| Package | Version | Dependency contract |\n| --- | --- | --- |\n| Core | 0.20.5 | Gallery ^3.0.0; optional renderer/PPTX ^0.20.0 |\n| CLI | 0.20.1 | Core ^0.20.5, Gallery ^3.0.0; optional renderer/PPTX ^0.20.0 |\n| Renderer | 0.20.4 | Core ^0.20.5; optional pinned font/converter peers |\n| PPTX | 0.20.4 | Core ^0.20.5, Gallery ^3.0.0; optional renderer ^0.20.4 |\n| Editor | 0.20.4 | Core ^0.20.5; optional renderer ^0.20.4 |\n| Gallery | 3.0.0 | Independent owner of every default catalog record; 28 intent layouts |\n\n## Runtime\n\n| Requirement | Status |\n| --- | --- |\n| Node.js | `engines.node` is `>=22` on every package below; **Node 24** is the coordinated toolchain these records are verified on (the 0.12.2 set and later are tested on Node 22, 24 and 26) |\n| Package managers | npm for published installs; this repo uses pnpm 10.33.2 for core development |\n| Account / model / hosted API | Not required |\n| Operating systems | macOS, Linux, Windows for Node APIs; browser entrypoints are separate |\n\n## Historical coordinated 0.18 published packages\n\n| Package | Version | Depends on |\n| --- | --- | --- |\n| `@openpresentation/opf` | 0.18.4 | optional peers `@openpresentation/opf-render@^0.18.0` and `@openpresentation/opf-pptx@^0.18.0`, which the Node build loads for `convert`, `open` and `save` |\n| `@openpresentation/cli` | 0.18.1 | `@openpresentation/opf@^0.18.3` (a regular dependency, no bundled copy); optional peers `@openpresentation/opf-render@^0.18.0` and `@openpresentation/opf-pptx@^0.18.0` |\n| `@openpresentation/opf-render` | 0.18.2 | `@openpresentation/opf@^0.18.4`; optional peers (RR-63): the converters `sharp`, `@resvg/resvg-js` and `pdf-lib` and the `@expo-google-fonts/*` font packages, which a host installs for the formats and fonts it uses |\n| `@openpresentation/opf-editor` | 0.18.2 | `@openpresentation/opf@^0.18.4`; optional peer `@openpresentation/opf-render@^0.18.2` |\n| `@openpresentation/opf-pptx` | 0.18.2 | `@openpresentation/opf@^0.18.4`; optional peer `@openpresentation/opf-render@^0.18.2` |\n\nThe 0.18 set is the consistent-API release ([design](programs/release-readiness/0.18-consistent-api.md)), breaking:\n- **One import (RR-70).** `import * as opf from \"@openpresentation/opf\"` works in every runtime through conditional\n exports, and `/node` is removed. `defaultCatalog` lives at `@openpresentation/opf/catalog` only.\n- **Names (RR-73) and options (RR-74).**\n - `ingest` replaces `importData` (and `opf ingest` replaces `opf import-data`).\n - `toSvg`, `toPng`, `toPdf` and `toHtml` work on decks.\n - The editor exports with `convert`.\n - `convert` takes `text`, `raster`, `charts`, `images`, `fonts` and `name`.\n- **Logos (RR-71).** Logos come from `organizations[].logo` (`full`, `stacked`, `icon`, `wordmark`, `onLight`/`onDark`),\n placed with `var:organization.logo.*`. `LogoSet` and zone `logo: true` are removed.\n- **PowerPoint (RR-72).** Shared furniture is written once on the PPTX slide master or layout.\n- **CLI (RR-75).** `opf convert` absorbs `render`, `export` and `import`. There is one output convention, plus\n `opf doctor` and a multi-file `validate`.\n\nSee the 0.18.0 and 0.18.1 entries of the [changelog](../CHANGELOG.md) and the 0.18.0 entry of the [CLI changelog](../packages/cli/CHANGELOG.md).\n\nCore starts at **0.18.1**. `opf-v0.18.0` was tagged, but its publish run failed before `npm publish`: a core test\nspawned the CLI, which the publish workflow had not built. So 0.18.1 is the first published 0.18 core.\n\nThe 0.17 set (core, editor and PPTX 0.17.0, renderer 0.17.1, CLI 0.17.1) was the lockstep minor after 0.16, also breaking:\n- **Furniture variables (FA-31):** header and footer text uses the `{{slide.number}}`, `{{slide.section}}` and\n `{{deck.slideCount}}` variables, and the `HeaderFooterItem` flags are removed.\n- **`@openpresentation/opf/node` (RR-62):** `convert`, `open` and `save` work from files and bytes. `parse` and\n `stringify` replace `readDeck` and `writeDeck`. `opf convert` is new, and `@openpresentation/cli/api` is removed.\n- **Fixes and additions:**\n - deterministic data import;\n - `validate` and `paginate` that plan script faces (RR-59);\n - the Arabic and Latin preview fixes;\n - `renderSvg(deck, { textAsPaths: true })` (RR-64 phase 1);\n - the Windows CLI path fixes (RR-66).\n\nSee the 0.17.0 entries of the [changelog](../CHANGELOG.md) and the 0.17.1 entry of the\n[CLI changelog](../packages/cli/CHANGELOG.md).\n\nRenderer 0.17.1 is a patch on the 0.17 line: SVG never embeds a face whose OS/2 `fsType` forbids it (RR-76), embedded faces are cut to the glyphs a slide draws (RR-65, mean example slide 2.4 MB to 59 KB), and `textAsPaths` shapes with HarfBuzz and keeps an invisible text layer (RR-64 phases 2 and 3).\n\nThe CLI starts at **0.17.1**. `cli-v0.17.0` was tagged, but its publish run failed before `npm publish`, because the\nrelease commit carried a stale generated gallery tracker. So 0.17.1 is the first published 0.17 CLI.\n\nThe 0.16 set (core, CLI, renderer and editor 0.16.0, PPTX 0.16.1) was the lockstep minor after 0.15 (owner decision 2026-10-08: no 0.15.x patch train). It adds nested\nplaceholder groups in layout records (FA-26), `Table.alt` (FA-27), the `opf/language-tag` warning (FA-29), Markdown\ndecks (`.opf.md`) in every command with core `readDeck`/`writeDeck` (RR-60), SVGs that embed only the faces they draw\n(RR-61), the CLI's programmatic `@openpresentation/cli/api` (RR-62), a lighter renderer install with optional\nconverters and fonts and the `/png`, `/pdf` and `/html` subpaths (RR-63), and the developer-contract fixes (RR-59).\n0.15 documents are 0.16 documents. See the 0.16.0 entries of the [changelog](../CHANGELOG.md) and the\n[CLI changelog](../packages/cli/CHANGELOG.md).\n\nThe 0.15 set (core 0.15.1, CLI 0.15.0, renderer, PPTX and editor 0.15.0) was the breaking spec line (format audit wave C): catalogs group the records a document embeds by the catalog they\ncame from (`catalogs.default`, `catalogs.custom`, named groups, `name:id` references), core ships no built-in records\n(the default catalog is the opt-in `@openpresentation/opf/catalog`, which a host registers with `{ catalogs }`), a\nfull-bleed photo is `design.background` and every content picture is an image block, and `language` is a BCP-47 tag.\nSee the 0.15.0 and 0.15.1 entries of the [changelog](../CHANGELOG.md).\nCore 0.15 started at **0.15.1**: `opf-v0.15.0` was tagged, but its publish run failed before `npm publish` (the CLI tests\nthrough the published renderer and PPTX ran in the core publish workflow before those 0.15 siblings existed), so 0.15.1\nwas the first published 0.15 core and the floor of every 0.15 sibling.\n\nOPF 0.15 is a breaking spec line (format audit wave C): catalogs group the records a document embeds by the catalog they\ncame from (`catalogs.default`, `catalogs.custom`, named groups, `name:id` references), core ships no built-in records\n(the default catalog is the opt-in `@openpresentation/opf/catalog`, which a host registers with `{ catalogs }`), a\nfull-bleed photo is `design.background` and every content picture is an image block, and `language` is a BCP-47 tag.\nThe 0.15 set reads and writes only 0.15 documents; see the 0.15.0 and 0.15.1 entries of the [changelog](../CHANGELOG.md).\nCore starts at **0.15.1**: `opf-v0.15.0` was tagged, but its publish run failed before `npm publish` (the CLI tests\nthrough the published renderer and PPTX ran in the core publish workflow before those 0.15 siblings existed), so 0.15.1\nis the first published 0.15 core and the floor of every 0.15 sibling.\n\nFrom CLI 0.16.0 the CLI depends on core (`@openpresentation/opf`, a regular dependency, not a bundled copy). CLI 0.16.0\nexposed `@openpresentation/cli/api`, and 0.17 removes it in favour of core's `@openpresentation/opf/node`. The renderer\nand PPTX stay optional peers. CLI 0.15.0 and earlier bundled their core.\n\nInstall the complete pinned set. A caret range starting at 0.10.1 does not\ninclude 0.11.x; old consumers can install a second core and do not establish\nColorRef preview/export support. The selected renderer 0.20.2 and PPTX/editor 0.20.3 require core ^0.20.2;\nCLI 0.20.0 accepts core ^0.20.1. Pin core 0.20.3 alongside them and\nverify one installed Core so preview and export use the same composition.\nCore 0.20.2 and later center metrics when alignment is omitted; explicit alignment remains\nauthoritative.\n\nShared header/footer geometry (`furniture-flow-v2`) is published. PPTX exports\neditable slide shapes tagged `OPF_FURNITURE_V1` with provenance for controlled\nreimport. Published PPTX through 0.11.8 draws every part that way, not as native\nOffice Header/Footer objects. PPTX 0.11.9 and later (RR-11) write the footer's\nfirst text, date and slide number as native `ftr`, `dt` and `sldNum` placeholders\n(with master/layout placeholders, `p:hf` flags and a notes-master flag) at the same\ngeometry and reads them back with or without provenance; the rest stays tagged\nshapes. Native PowerPoint acceptance remains [issue 87](https://github.com/OpenPresentation/opf/issues/87).\nPPTX 0.18 (RR-72) writes a tagged part that is the same on two or more slides once,\non the slide master or a shared layout (one per background tone for `onLight`/`onDark`\nlogos; a hidden footer uses a layout that hides the master's), and reads it back from\nthere ([rr-72-master-furniture.md](programs/release-readiness/rr-72-master-furniture.md)).\n\nThe [Windows native-picture checkpoint](evidence/windows-native-picture-20260921/README.md)\nand accepted [native B/C bundle](evidence/windows-native-edits-20260921/README.md)\nrecord finite picture/furniture edits, current-content provenance reimport and\nsafe fallback, production notes packaging and two controlled reordered-file\nrefusals. Core105 publishes that evidence; PPTX47 adds the tested harness, with\nno new package version. UI image replacement changes geometry, longer header\ntext clips, and duplicated tagged headers overlap. Refused workers retain their\nfailed cleanup state separately from later empty-workspace observations.\nThis is not general native layout/reflow fidelity.\n\nAccepted core106's [tab and font checkpoint](evidence/windows-native-tabs-fonts-20260921/README.md)\nrecords plain native tab target error **0.022655487060546875pt** and tab/literal\ndifference **0.022678375244140625pt**, both above the unchanged **0.02pt** gate.\nIts bounded four-face Carlito edit/save/reopen control passes exact text/style\npersistence, zero observed bounds drift, matching rasters and owned font cleanup.\nMixed-size table fidelity, physical glyph-font identity, fallback/synthesis and actual embedding remain\nopen; embedding was disabled for this control. The Windows supervisor retains sole\nOffice control. This documentation task reads evidence and makes no Office calls.\n\nThe later [read-only font inventory](evidence/windows-native-font-inventory-20260921/README.md)\nretains the four Carlito text styles but reports both Carlito and unexpected\nAptos in `Presentation.Fonts`. The native font allowlist fails, and no embedding\nwas attempted. The original parent report incorrectly fails cleanup because of\na Windows PowerShell 5.1 JSON-array parsing defect; raw stages and registration\nrows establish one owned close and four removals in a separately labeled offline\naudit. The raw failure remains intact. Collection names and flags do not identify\nthe physical font used for each glyph.\n\nThe [read-only mixed-table observation](evidence/windows-native-mixed-table-20260921/README.md)\nretains all 245 characters, one literal tab and five authored runs, with outer\ngeometry within 0.02pt and confirmed owned close/font cleanup. Native soft-line\nboundaries are 92/194 versus the estimated preview's 78/172, and native default\ntab spacing is 72pt. These finite content/style results do not pass table\nedit/save/reopen, browser/native raster agreement or physical glyph identity.\nThe accepted [nine-pair offline tab analysis](evidence/windows-native-tab-analysis-20260921/REPORT.md)\nfinds a 0.05pt-compatible pattern in the observed character starts, with finer saved\ntab coordinates. These inputs do not distinguish relative versus absolute placement\nor establish an internal engine cause. The 0.02pt native tab gate remains failed;\nthe separate 0.1px renderer gate is unchanged. Accepted core108 `9b277e1` and\ncore109 `b2711549` publish bounded evidence only. Windows-owned [core110](https://github.com/OpenPresentation/opf/pull/110)\nis merged as `4f7a4bd494f1a873319eff897423d301d1cfc9d6`, from reviewed fc3c36e\nwith four required PR checks passing. [Renderer30](https://github.com/OpenPresentation/opf-render/pull/30) is now merged\nas `c8d7d5ca1f67a7b39f70c7c4bd14577a865b175b`, with exact-head CI 35661051100\npassing. The supervisor reports postmerge 35661504472 also passed. Its companion\nsource preserves rich-tab advances/spans; the [accepted Windows wrap-up](handoff-windows-native-2026-09-21-wrap-up.md)\nrecords bounded source-linked rendering/browser checks and the original missing-test\nCI failure. These checks do not update the frozen registry consumer. Core111\n`3c5048522714365a41d9b5b9ba81620affae718b` publishes the font-inventory evidence\nabove with both PR workflows green; its postmerge workflows were started at the\nfinal notice, not recorded as passed. Package/lock/release/site pins, schema,\ngoldens and tolerances are unchanged. Native allowlist and physical-glyph/embedding\nacceptance remain open; no new package train or broad native pass is inferred.\n\n## Independently released packages\n\n| Package | Version | Depends on |\n| --- | --- | --- |\n| `@openpresentation/gallery` | 3.0.0 ([independent artifact verification](evidence/rr-85-final-published-set-2026-10-16/README.md); prior 2.0.0 and 1.0.0 remain historical) | nothing (its `GalleryCatalog` type has the shape of `@openpresentation/opf`'s `Catalog`) |\n\nThe gallery (RR-78) is released from its own repository, OpenPresentation/gallery, on its own version line (`vX.Y.Z`\ntags, `gallery-publish.yml` there), outside the lockstep set above and\nrecorded under `independentPackages` in `release-plan.json`: new catalog records are a minor release, a removed id or\na changed drawing a major one ([release process](release-process.md#the-gallery-lane-rr-78)). From OPF 0.19 core and the\nCLI consume the Gallery package (Gallery 2 in OPF 0.19; Gallery 3 in OPF 0.20), and core has no `/catalog` subpath; up to 0.18 core ships the same records as\n`@openpresentation/opf/catalog`.\n\n## Supported in this set\n\n| Capability | How | Notes |\n| --- | --- | --- |\n| JSON authoring | `*.opf.json` plus CLI `opf create` | Local files only |\n| Bundled examples catalog | `@openpresentation/opf/examples` | **127** decks; the quickstart JSON is a docs fixture, not a 128th catalog entry |\n| Validate | `validate` / `opf validate` | One read-only checker: format, references, policy, accessibility, layout and content findings ([guide](validate.md)); no network catalog fetch |\n| Color references | `ColorRef`, `variables`, `resolveColorRef` | Core schema/resolution, renderer preview and PPTX resolved colors are shipped. Supported theme-slot colors write native `schemeClr` references; arbitrary native Office recoloring and full visual fidelity remain separately scoped. |\n| Embed catalog records | `embed` / `opf embed` | Embeds every record a document references, in the group it resolves in, so it renders with no catalog registered; remote media and data remain explicit host concerns. |\n| Offline fonts | `loadFonts` (`/fonts-node`) | Bundled Roboto pack (`pack: 'base'`) or the office pack; hashed files; the handle is passed as `{ fonts }` to every deck-level call |\n| Composition | `composeSlide` | Includes shared headers/footers |\n| Pagination | `paginate` / `opf paginate` | Returns mappings; preserves source |\n| Edit + undo | `@openpresentation/opf-editor` `createEditorSession` | JSON Patch undo/redo |\n| JSON Patch CLI | `opf edit` | No persistent CLI undo history |\n| SVG preview | `toSvg(deck)` (every slide) / `toSvg(deck, n)` (slide n, counted from 1) (opf-render 0.18; `renderSvg` / `renderSlideSvg` before) | Local; the same `{ fonts }` as layout; `text: \"paths\"` draws the text as glyph outlines |\n| Convert, export and import files from code | `convert`, `open` and `save` of `@openpresentation/opf` in Node (core 0.18.0 and later; core 0.17 had them at `@openpresentation/opf/node`; CLI 0.16.0 had `exportDeck` / `importDeck` in `@openpresentation/cli/api`) | PDF, PNG, SVG and PPTX out, PPTX in, deck forms both ways, with the bundled fonts, no network and no clock; calls opf-render and opf-pptx as optional peers and throws `OPFExportError` / `OPFImportError` with the install command when one is missing. `opf convert` runs the same engine (CLI 0.18; `opf render`, `opf export` and `opf import` before it) ([OPF files in Node](node.md)) |\n| Read and write a deck in JSON, YAML or Markdown | `parse` / `stringify` (core 0.17.0 and later; `readDeck` / `writeDeck` in 0.16) | One entry point for the three text formats; `parse` throws `OPFValidationError` with located findings |\n| PNG | `toPng` (opf-render 0.18; `svgToPng` before) | Node raster of a deck or of SVG slides |\n| PDF | `toPdf` (opf-render 0.18; `svgToPdf` before) | opf-render 0.12.0 and later (RR-12, opf-render#90): **vector with selectable text by default** (embedded TrueType subsets of the supplied/bundled fonts, ToUnicode, links, metadata, tagged structure); `raster: true` keeps the image-per-slide output (`mode: \"raster\"` before 0.18). Renderers up to 0.11.9: raster-backed, not selectable text. Not a PDF/UA or PDF/A claim; see the renderer's `docs/evidence/rr-12-vector-pdf.md` for reader limits |\n| Editable PPTX export | `toPptx` | OPF \u2192 PPTX serialization. Text is one editable box per wrapped line, so edited text does not reflow in PowerPoint or Keynote, and a wrapped title is not the Title placeholder; an opt-in native body mode is in progress. Shared furniture is written on the master or layout; supported footer placeholders and tagged parts retain controlled re-import provenance. General native Office behavior remains separately scoped |\n| Agent skills | `opf skills install` | Offline after the CLI is installed |\n| Browser canvas | `@openpresentation/opf-editor/canvas` | Host must supply font bytes |\n\n## Public sites\n\nThe current coordinated adoption is recorded in the [final adoption note](evidence/rr-85-final-adoption-2026-10-16/README.md). The following source, CI and canonical production results are the historical September 21 checkpoint, recorded in the\n[font-readiness checkpoint](evidence/font-readiness-acceptance-20260921/README.md),\n[prior Inspector actions checkpoint](evidence/inspector-current-actions-20260921/README.md),\n[earlier publication checkpoint](evidence/inspector-share-acceptance-20260921/README.md),\n[source-preservation checkpoint](evidence/author-source-acceptance-20260921/README.md),\n[completion checkpoint](evidence/completion-acceptance-20260921/README.md),\n[earlier acceptance ledger](evidence/issue88-final-20260921/README.md) and\n[handoff](handoff-2026-09-21.md). [Issue88](https://github.com/OpenPresentation/opf/issues/88)\nremains open. Package adoption, deployed features and complete workflow\nacceptance are separate claims.\n\n| Surface | Deployed scope and acceptance | Source commit |\n| --- | --- | --- |\n| [openpresentation.org](https://www.openpresentation.org) | Current published guides, agent skills, JSON/preview workflow and downloads. Exact canonical deployment passes 321 checks across 11 pages and 18 raw resources, plus two browser flows for agent installation/navigation and JSON/SVG/PPTX downloads. Reviewed screenshots and output hashes match the accepted build. | `a85bcc77d899ce9ba1df659548be564142c16120` |\n| [pptx.dev](https://www.pptx.dev) `/inspector` and `/author` | App54 merged/live on exact READY production. Premerge Linux/Windows pass 704 units, 13 standalone controls and 39/39 browsers. Full canonical acceptance **fails (34/39 passed)** at five no-POST assertions; the bounded audit does not establish an introduced upload regression. Postmerge Linux 39/39 passes, Windows 38/39 fails initial font readiness. Preset Undo all and broader source writers remain unresolved. | `8f54228a9e38a1dcc0bf8188bcdd519795b3799a` |\n| [pptx.gallery](https://www.pptx.gallery) `/docs`, `/editor` and gallery pages | Published ColorRef/bundle guidance, Playground and Editor actions, and the canonical docs-to-editor flow are verified. | `f17e9ae5869669d5fbac3720f285652d0c37551c` |\n\nThe site uses documentation source `120a770`, whose tree matches accepted core\nPR98 commit `b1ff81db6f8714b0db1a98bde482ed8a64d0ccc9`. Core PR93/97/98/99/100/102/103 passed\npre-merge and post-merge CI. Accepted core102 is\n`578bcc6e0894129b00059258bd4ad1994a414baa`; its reviewed and accepted trees match,\nand all four required pre/post-merge runs passed on their first attempts. The\n[core102 receipts](evidence/inspector-share-acceptance-20260921/README.md#accepted-core102)\npin this documentation checkpoint without changing the site's older accepted\ndocumentation snapshot. The site's complete guides and raw resources match\nthe reviewed source; binary evidence remains linked and downloadable without\nbeing decoded into the AI-facing guide.\nCore104 `3d301f1` preserves exact postmerge OPF success and coordinated cancellation;\nit is not a complete green postmerge gate. Accepted descendant core105\n`84e914710520a7b0e777fce30e5758ee64a64924` preserves all 60 core104 evidence blobs\nand passes both exact-head workflows on their first attempts. [Compact receipts](evidence/inspector-current-actions-20260921/README.md#core-source-and-ci)\nkeep descendant acceptance separate from the canceled predecessor run.\nAccepted core106 `3847f712ccb2379952bcc8ab7c9fdbaedfd0a4ce` also passes both\npre/postmerge workflows on their first attempts. Its [compact receipt](evidence/inspector-current-actions-20260921/core106/acceptance-receipt.json)\nbinds the bounded native evidence above without completing general compatibility.\nAccepted core107 `5bc0d3f89414b382b2ce48452c7e56e5d66aaf74` has reviewed tree\n`ffdc678beadf0808bc717d67e7fc0a9ec4790127`. Both original postmerge push workflows\n**35652360502 / 35652360551** passed on attempt 1 under Node24.20.0.\n[Compact receipts](evidence/font-readiness-acceptance-20260921/README.md#core107-acceptance)\nretain earlier automatic premerge cancellations separately from the later automatic\nsuccessful pair; no rerun or accepted checkpoint relabels them.\n\nApp47 corrects the pre-app47\ncompletion adapter's rejected layout choices while preserving unchanged source\ntokens and undo history. Accepted commit `0f35352a1445f56ad4bb7c9f4c5609e01f2dd9ae`\nhas reviewed tree `203bdab509d05911f04f234d996f9c91f2b5e4f2`, green Linux/Windows\npre/post-merge CI and its exact READY canonical deployment. The historical App47 **23/24** production run passes all five new completion cases but still fails the existing\nLF Author third-popup assertion. This does not establish complete public-surface\nacceptance; the [historical App47 report](evidence/completion-acceptance-20260921/canonical/REPORT.md)\nand [earlier failed app45/app46 results](evidence/issue88-final-20260921/README.md)\nretain their evidence and unresolved causes.\n\nThe [source audit](evidence/completion-acceptance-20260921/source-preservation-audit/REPORT.md)\nidentified Author canvas/Copy/export and Inspector JSON-download normalization.\nMerged App53 at\n`e40c287b64fcbcfb85fb4a8a50641aea8e3e54a8` has the identical reviewed b33dc18\ntree and preserves those bounded raw-source\npaths and corrects order-only reimport history. A public Suggest-action guard\naddresses the observed stale Quick Input context competing with focused-editor\nCtrl+Space. Current local checks pass **627 unit tests and 29/29 browser cases\nin 88.78 seconds**, zero retries. First-attempt Linux/Windows application CI\npassed 627 unit tests and 29 browser cases per platform; artifact CI also passed.\nThe [exact READY canonical run](evidence/author-source-acceptance-20260921/canonical/REPORT.md) passes **29/29**, zero retries, with matching deployment receipts before and after. Postmerge application CI fails Linux **28/29** while Windows passes **29/29**; both pass 627 unit tests and separate artifact CI passes. The [Linux failure](evidence/author-source-acceptance-20260921/app53/postmerge-ci/README.md) stops before security assertions because five default-deck canvases remain after the shared-load toast. No rerun or canonical pass replaces that failed gate. The retained draft preview was READY but not browser-accepted.\n\nThe earlier **24/27** import-undo regression, **26/27** local popup failure and\nold-head Windows **26/27** shared-load failure remain historical evidence.\nThe fresh successful Windows job retained its sanitized timing artifact, but\nonly Author timings survived; the Inspector pagehide snapshot is missing. This\ndoes not explain or fix the old readiness delay. Phase4\ncaptured no post-fix stale-context overlap, so causal stress is inconclusive.\nThe existing suggestion-details pane remains clipped; visibility is not legibility.\nThe [postmerge trace diagnosis](evidence/author-source-acceptance-20260921/inspector-share-diagnosis/REPORT.md)\nproves wrong-document automatic share-hash publication during import.\nApp54 first corrected automatic\npublication at `60c91f6`: authoritative source/format is checked before and after\nencoding, load/navigation guards remain, and obsolete `import=hash:` is removed.\nURL transfer preserves semantics, not raw spelling. Local 659-unit/31-browser\nacceptance does not replace original first-attempt CI **35638158483**, which\npassed Linux **31/31** but failed Windows **30/31** at the unchanged 45-second\nAuthor readiness deadline. Both publication cases passed. The [frozen diagnosis](evidence/inspector-share-acceptance-20260921/app54/windows-timeout/REPORT.md)\nretains bounded slow-delivery observations with unknown cause. Late assertions\nare not an in-budget pass. Browser History tests permit prior accepted content\nuntil first new publication; held-promise controls establish the narrower race guard.\n\nThe product correction was added at `57e5e59fddbc94346f142dc12d86a916228bf2ae`, tree\n`d9c3aab543c6a886a85c6ec07dd55e5ab22590cd`. It guards current snapshots for\nexplicit Copy/JSON/Share/PPTX/PDF/Author/Deckchat actions. Pending public canvas\ndrafts commit before capture, pointer-blur rejection survives session recovery,\nand newer source/load/navigation/unmount/action invalidates late results. Raw\nCopy and accepted same-format JSON bytes are preserved; handoffs remain semantic.\nAlready-started clipboard/download effects cannot be retroactively canceled.\nThe integrated portable standalone startup verifies packaged runtime assets,\nfonts and traced schema inputs without changing dependencies or published pins.\n\nLocal runtime checks pass **692 unit tests**, **7 standalone controls**, focused\n**8/8** and full **39/39** browser cases, zero retries, with visual review. The\nimmutable 93-file app bundle\nretains stale-draft negative evidence and the initial candidate **7/8** result.\nThe latter did not establish accepted pasted source before releasing mocked PDF 401;\nfinal visible-code preconditions change no product bytes or budgets. PDF/Deckchat\nare locally mocked. CRLF ingress normalized 279 to 274 LF bytes; subsequent exact\nactions preserve the accepted buffer, not that ingress boundary.\n\n**The original 57e packaging gate failed.** First-attempt CI **35645493900** failed Linux and\nWindows typecheck on archived `.spec.ts` evidence copies after each platform\npassed 692 units and 7 standalone controls. The exact-head preview failed with\n`module_not_found`; precise Vercel compiler logs were unavailable. [The first-attempt receipt](evidence/inspector-current-actions-20260921/app54/first57-ci/REPORT.md)\nrecords skipped build/browser steps and no uploaded artifacts. The passing runtime build\npredates those copies. A byte-identical `.ts.txt` archive correction produced captured App54 head\n`6dfc2584698c1306505929a2bc3e427996d66563`, tree\n`f3279495f7685945b7541c90d24f7239407639a0`. Its final-tree local typecheck passes\nwith all 305 product/test inputs unchanged. [The corrected receipt](evidence/inspector-current-actions-20260921/app54/corrected6df/commit-receipt.json)\nbinds its 106-file evidence bundle. [Exact-head CI 35646213757](evidence/inspector-current-actions-20260921/app54/corrected6df/ci/REPORT.md)\npasses 692 units, seven standalone controls, typechecks and build on both platforms;\nLinux passes **39/39** browsers. Windows executes **zero browser tests** because\nstandalone startup fails with `EPERM` while statting its packaged React dependency\nlink. No timing or test-result artifacts were uploaded. The exact-head preview\nis READY, which is metadata only; at that 6df checkpoint App54 was unmerged and undeployed to production.\nThe [current ledger](evidence/inspector-current-actions-20260921/README.md)\nretains the separate 60c readiness, 57e packaging and 6df startup failures. Production then remained App53 `e40c287`, with its original\nfailed Linux postmerge gate preserved separately from canonical **29/29**.\nThe startup-link correction at 4338e57\nthen reached all browser cases: Linux **39/39**, Windows **38/39** in first-attempt\nCI **35649707689**, with 692 units and 13 standalone controls passing per platform.\nThe sole Windows failure was initial canvas-title visibility at five seconds,\nbefore any edit/recovery operation; correct incoming source remained at loading\nfonts. Late font acquisition and eleven incomplete responses at teardown do not\nestablish a permanent stall or a dominant cause. The [failed gate](evidence/font-readiness-acceptance-20260921/README.md#preserved-failed-windows-gate)\nis preserved.\n\nApp54 font-preparation revision `a4eb88ab7aa585c9efb91de4c190c1f1c0c7d0eb`, tree\n`031d893855a540fd2a4d2ec2162605817b8dde16`, overlaps font acquisition with converter\nwarmup while readiness still waits for both. The offline converter barrier and all\n**33 faces / 9,317,044 bytes**, manifest, substitution/measurement policy and\n`document.fonts.ready` gate remain unchanged. Fresh local Node24.21.0 checks pass\n**704 units, 13 standalone controls and 39/39 browsers**, zero retries, including\nunchanged offline export/reimport. Immutable app evidence\nretains reviewed images, exact source/output bindings and the original failed run.\n\n**The original a4eb gate failed:** first-attempt application **35654753237** passes\nLinux **39/39** but fails Windows **38/39**; both pass 704 units, 13 standalone\ncontrols, typecheck and build. The Open in Author URL assertion exceeds its existing\nfive-second deadline at `inspector-actions.spec.ts:176`; later source checks are\nnot reached. Original4338 font readiness passes in this run. No navigation cause\nor data-loss finding is established. The [frozen final audit](evidence/font-readiness-acceptance-20260921/app54/final-a4eb-ci/release-audit.json)\nretains exact source/artifact bindings and the original failed trace; that failure remains preserved.\nThe Python artifact workflow is not applicable under its full-PR path filters,\nnot a fresh pass. Exact-head preview is READY metadata only; an\nunauthenticated request redirects to sign-in, with no preview-browser acceptance.\nAt that a4eb capture App54 was unmerged and production remained App53\n`e40c287`. The separate suggestion-details candidate remains unreleased and supplies\nno acceptance here. No local/browser pass broadens native compatibility.\nThe [bounded navigation diagnosis](evidence/font-readiness-acceptance-20260921/README.md#author-navigation-diagnosis-and-prospective-policy)\nrecords Loading Author and a delayed successful script response: 1490 bytes inferred\nfrom ETag, 766 compressed bytes recorded, actual body absent. Later DOM does not\naccept unreached assertions or identify a cause. App54 accepted merge\n`8f54228a9e38a1dcc0bf8188bcdd519795b3799a` retains reviewed 79ba tree\n`3639d3c14daec94d13711fa2b10f2b927df45eca`, preregisters `waitForURL(load)` before\nthe real action, matching `page.goto` within the unchanged 45-second test and default\nfive-second content budgets. It retains all oracles but deliberately removes the\nincidental five-second navigation deadline. Fresh local **39/39**, zero retries,\npasses in 112.780048s with reviewed source/images and prepared-tree build/typecheck.\nThe immutable app bundle\nretains original failures. Units/standalone controls were not repeated locally;\nfresh first-attempt application **35659187971 passes 704 units, 13 standalone\ncontrols and 39/39 browsers on both Linux and Windows**. Exact-head preview was\nREADY/protected, not browser accepted. The identical reviewed tree is merged/live\nat 8f on READY deployment `dpl_H1FtXx1QSuGxn3MwzJwWJpGtRg8b`, but full canonical\nacceptance **fails (34/39 passed)** at five no-POST assertions observing Clerk environment\nPOSTs. The [safe audit](evidence/font-readiness-acceptance-20260921/app54/canonical8f/write-audit/REPORT.md.txt)\nrecords ten such requests, nine with HTTP200/zero-length bodies and one incomplete.\nNo fixture-content needle was detected in captured fields; uncaptured data remains\nunknown. Three final action page-error assertions were not reached; the two share\ncases passed theirs. The prior auth/config comparison is 28/29 identical, with only\npackage scripts changed. Production is kept without a rollback, test change or\nrerun; the strict gate remains failed. Raw authentication-bearing diagnostics\nremain private. Postmerge CI 35660464578\nfinishes failed: Linux 39/39 passes while Windows 38/39 fails, with 704 units/13 controls/typecheck/build\npassing on each. Windows fails initial gallery-rail title visibility after 5,000ms\nwith correct source, clean schema and Loading slide fonts; later editing/export/\nreimport checks were not reached. The [final audit](evidence/font-readiness-acceptance-20260921/app54/merged8f/postmerge-ci/REPORT.md.txt)\npreserves this separate failed gate without cause inference or a retry. No canonical\npass or general native/font acceptance is claimed. That September 21 checkpoint was paused; the user resumed work on September 29. See the [current source checkpoint](handoff-runtime-2026-09-29.md) for ongoing repairs and release holds.\n\nSeparate local negative controls confirmed that preset Undo all discarded New run\nand imported replacement documents. The guarded correction is now preserved in\ndraft app #58: independent\nreview and local Node 24 checks passed (716 units, 13 standalone controls and\n49 browsers without retries). Original Linux/Windows CI could not start because\nof the account payment/spending-limit restriction; no application CI or production\nacceptance is claimed. Unbusy asynchronous account replacements still need a\nsynchronous invalidation guard and held-response control.\nOther local source writers still require their separate preservation checks. These unresolved local findings and raw imported-file/account/agent/metadata boundaries remain outside\nApp53 and App54. See the [App53 ledger](evidence/author-source-acceptance-20260921/README.md)\nand its immutable application evidence links. Issue88 remains OPEN. Native/font\ncompatibility, required repair and release gates remain separate; geometry is\ndeferred as the coordinated set below. The unimplemented worker candidate remains in the [handoff](handoff-2026-09-21.md).\n\nThe five coordinated geometry drafts (core94, renderer27, editor25, PPTX42,\nsite40) remain unmerged. In particular, site40 is not independently shipped.\n\n## Explicitly not shipped\n\n| Topic | Tracker | Do not describe as done |\n| --- | --- | --- |\n| Linux vs Chromium native-width residual at the 0.1px gate | [opf-render#24](https://github.com/OpenPresentation/opf-render/issues/24) | Rounding that fixes Linux but breaks macOS is rejected |\n| General native PowerPoint fidelity beyond the supported footer placeholders | [opf#87](https://github.com/OpenPresentation/opf/issues/87) | Finite B/C and bounded Carlito edit controls above are accepted evidence, as is one finite mixed-size table edit/save/reopen ([evidence](evidence/windows-native-mixed-edit-20260922/README.md)). Tab tolerance, general mixed-size table layout and preview/native wrapping, physical glyph identity/fallback/synthesis, embedding and general layout/reflow fidelity remain open; supported native footer placeholders, self-import and tagged furniture do not certify arbitrary Office behavior |\n| Public-surface acceptance checklist | [opf#88](https://github.com/OpenPresentation/opf/issues/88) | Shipping features does not establish every acceptance item; use the checklist and deployment receipts |\n| HarfBuzz / prepared-glyph shaping | Archive branches `codex/archive-shaping-20260915` | Prototypes are preserved, not in npm |\n| Selectable vector PDF | [pdf plan](plans/pdf-export.md) | Shipped in opf-render 0.12.0 (opf-render#90; the browser download entry `@openpresentation/opf-render/export-browser` ships there too); PDF/UA, PDF/A and viewer coverage beyond pdf.js, PDFium and poppler remain open |\n| General SVG diagrams / Mermaid | [diagrams plan](plans/diagrams-svg.md) | Embedded SVG \u2260 native editable primitives |\n| Full visual editor / IME / bidi / repair loop | [developer adoption](plans/developer-adoption-20260915.md) | Schema support \u2260 WYSIWYG coverage |\n\nCLI 0.9.2 and earlier do not render or export PPTX; CLI 0.10.0 adds `opf render`, `opf export` and `opf import`\nthrough the optional peers opf-render and opf-pptx ([CLI reference](cli.md)). The Node `toPng` / `toPdf`\n(`svgToPng` / `svgToPdf` before 0.18) APIs stay Node-only; renderer 0.12.0 adds the separate `@openpresentation/opf-render/export-browser` entry for browsers.\nCore's file API (`open`, `save`, `convert`) is Node-only too: it reads files, font files and the engines' native packages. From\ncore 0.18 the root resolves to a browser-safe build in a browser or a worker, with the same names, where those three reject\nwith `node-only`; every other core entry is browser-safe. In a browser, use the engines' browser entries.\n\n## Predecessor notes\n\n| Older set | Relationship |\n| --- | --- |\n| core 0.17.0, CLI 0.17.1, renderer 0.17.1, PPTX 0.17.0, editor 0.17.0 | Previous coordinated set (FA-31 furniture variables, `@openpresentation/opf/node`, `parse`/`stringify`, `opf convert`). The 0.18 set is breaking: one root import replaces `/node`, the `toSvg`/`ingest` names and `convert` options, organization logos, and the consolidated CLI. |\n| core 0.16.0, CLI 0.16.0, renderer 0.16.0, PPTX 0.16.1, editor 0.16.0 | Previous coordinated set (Markdown decks, used-face SVG embedding, optional renderer converters, nested placeholder groups, `@openpresentation/cli/api`). The 0.17 set is breaking: header and footer flags become `{{slide.number}}`-style variables (FA-31), `readDeck`/`writeDeck` become `parse`/`stringify`, and `@openpresentation/cli/api` is replaced by `@openpresentation/opf/node`. |\n| core 0.15.1, CLI 0.15.0, renderer 0.15.0, PPTX 0.15.0, editor 0.15.0 | Previous coordinated set (OPF 0.15, format audit wave C; CLI 0.15.0 bundles core 0.15.1). The 0.16 set is additive for documents; the renderer's converters and font packages became optional peers (RR-63), so a 0.16 host installs the ones it uses, and the CLI depends on core instead of bundling it (RR-62). |\n| core 0.14.0, CLI 0.11.0, renderer 0.14.0, PPTX 0.14.0, editor 0.14.2 | Previous coordinated set (format audit waves A and B, RR-55 and RR-56; CLI 0.11.0 bundles core 0.13.0). The 0.15 set is breaking: a 0.14 document's catalog shape (`catalogs.<kind>.records`), catalog-id languages and `design.slideImage` are invalid in 0.15, and the 0.15 engines need the host to register the default catalog. |\n| core 0.13.0, CLI 0.11.0, renderer 0.13.1, PPTX 0.13.2, editor 0.12.1 | Previous coordinated set (chart and table data, RR-54). |\n| core 0.12.2, CLI 0.10.1, renderer 0.12.2, PPTX 0.12.4, editor 0.11.3 | Previous coordinated set (the first set whose `engines.node` is the open-ended `>=22`). The 0.13 set adds chart and table data (RR-54, [contract](chart-table-data.md)): strict chart numbers (a value is a finite number or a strict decimal string; `\"12%\"`, `\"$5\"` and `\"Q1\"` are a gap with a `chart-value-not-numeric` warning, never a guessed number), number formats (`DataColumn` `{ name, format }`), top-level `datasets` with `{ \"dataset\", \"fields\" }` references and `chart.mapping`, in core 0.13.0, previewed by renderer 0.13.0, exported and re-imported by PPTX 0.13.0 (`OPF_DATASETS_V1` and `OPF_DATA_V1` provenance), edited in the editor 0.12.x data grid and imported by CLI 0.11.0 (`opf import-data --dataset`). PPTX 0.13.0 also vendors pptxgenjs-plus 4.3.4 (was PptxGenJS 4.0.1) and PPTX 0.13.1 bundles for browsers again. Renderer 0.13.1 and PPTX 0.13.2 pin Sharp 0.35.5 (GHSA-wq5f-xc86-pv6w; output unchanged); editor 0.12.1 replaces 0.12.0, which was never published. Renderer, PPTX and editor raise their core floor to `^0.13.0` and the renderer peer to `^0.13.1` together. |\n| core 0.12.1, CLI 0.10.0, renderer 0.12.0, PPTX 0.12.2, editor 0.11.2 | Previous coordinated set. PPTX 0.12.3 corrects the table range of a chart's embedded workbook (apostrophe-quoted sheet references and bubble-series references), which made Keynote drop category charts on import (opf-pptx#162, opf-pptx#163; every other part of the package is byte-identical). Core, renderer and editor are unchanged and keep the core floor `^0.12.0` and the renderer peer `^0.12.0`. |\n| core 0.12.0, CLI 0.10.0, renderer 0.12.0, PPTX 0.12.2, editor 0.11.2 | Previous coordinated set. Core 0.12.1 deprecates the six plural audience ids (`executives`, `investors`, `customers`, `sales-team`, `marketing-team`, `regulators`) in favour of the singular ids: additive catalog data, validation warns and never errors, and no geometry moves, so renderer, PPTX and editor keep the core floor `^0.12.0`. |\n| core 0.12.0, CLI 0.10.0, renderer 0.12.0, PPTX 0.12.1, editor 0.11.1 | Previous coordinated set (PowerPoint lists only the deck's fonts). PPTX 0.12.2 also names the deck's font in the theme's `Viet` (Vietnamese) and `Uigh` (Uyghur) per-language script entries, which PowerPoint applies to `vi-VN` and `ug` runs and which kept Office's Times New Roman and Arial for those two languages (RR-17; every other language is byte-identical). Editor 0.11.2 adds the `slide-sizes` and `purposes` switch dimensions, `SLIDE_SIZE_PRESETS` and the Slide size and Purpose selects in the Design panel (RR-41, additive API). Both keep the core floor `^0.12.0` and the renderer peer `^0.12.0`. |\n| core 0.12.0, CLI 0.10.0, renderer 0.12.0, PPTX 0.12.0, editor 0.11.0 | Previous coordinated set (the 0.12.0 train). PPTX 0.12.1 writes the deck's fonts so that PowerPoint's font list shows only the fonts the deck uses (FF-05: schema-order `presentation.xml`, an own notes theme, no east-asian or complex-script font on runs, and a theme east-asian slot that is never empty); editor 0.11.1 stops the restore prompt showing a literal `null`. Both keep the core floor `^0.12.0` and the renderer peer `^0.12.0`. |\n| core 0.11.4, CLI 0.9.2, renderer 0.11.9, PPTX 0.11.9, editor 0.10.6 | Previous coordinated set (native header/footer placeholders, SVG pictures, `fromPptx` import signals; the published 0.11.4 train measured in the font-fidelity program). Core 0.12.0 moves geometry (composed font sizes on PowerPoint's 0.01 pt grid, hanging wrap whitespace, promoted regions in reading order, right-to-left decks composed mirrored), so renderer 0.12.0, PPTX 0.12.0 and editor 0.11.0 raise their core floor to `^0.12.0` and the renderer peer of PPTX and editor to `^0.12.0` together; the set adds templates and variables, numbered lists, footnotes, citations and captions and chart options (additive schema), vector PDF with selectable text and the browser export entry, the `<opf-deck>` player, the editor's slide management, autosave, data grid, find and replace, image crop, Review panel and fill UI, and the shared JSON Patch module. CLI 0.10.0 bundles core 0.12.0 and adds `opf audit`, `from-md`, `to-md`, `diff`, `merge`, `format`, `render`, `export` and `import`. |\n| core 0.11.4, CLI 0.9.2, renderer 0.11.9, PPTX 0.11.8, editor 0.10.6 | Previous coordinated set (PPTX 0.11.8 re-imports wrapped rich text as one authored payload; CLI 0.9.2 bundles core 0.11.4). PPTX 0.11.9 writes a deck footer's first text, date and slide number as native PowerPoint Header & Footer placeholders (every export also carries the footer placeholders on its master, layout and notes master, so Insert > Header & Footer works), exports an SVG image as a native SVG picture over a PNG fallback (rasterized in Node by the optional opf-render peer or `options.svgRasterizer`) and adds the opt-in `fromPptx(bytes, {signals: true})` import signals; core floor `^0.11.4` unchanged. |\n| core 0.11.4, CLI 0.9.1, renderer 0.11.9, PPTX 0.11.7, editor 0.10.6 | Previous coordinated set (the design fields compose and export natively). PPTX 0.11.8 re-imports rich text that wraps over several native lines as one authored payload (the export records the line count; decks exported by 0.11.7 import as before) and keeps the core floor `^0.11.4`; CLI 0.9.2 bundles core 0.11.4 (CLI 0.9.1 bundled core 0.11.3) and still requires Node 24. |\n| core 0.11.3, CLI 0.9.1, renderer 0.11.8, PPTX 0.11.6, editor 0.10.5 | Previous coordinated set (the native chartex export by default). Core 0.11.4 composes the design fields (logos on covers and section slides, `contentDirection`, `chartPrimary`, picture bullets, header and footer logos, the accent font), aligns a cover's tag and subtitle with its title, and sizes picture bullets and furniture images as PowerPoint does, so renderer, PPTX and editor raise their core floor to `^0.11.4` together; renderer 0.11.9 draws those fields and applies the tag contrast rule (FF-61: the tag draws in the text colour when the scheme primary is under 4.5:1); PPTX 0.11.7 exports them natively, writes every chart's text at the preview's size (FF-62: 12 pt, not 9 pt), writes slide sections as PowerPoint's section list and restores the authored form of a fresh export on import (a root payload returns as `slides.N.text`, `.items`, `.chart` ... rather than one typed block); editor 0.10.6 is a floor bump. CLI 0.9.1 still bundles core 0.11.3. |\n| core 0.11.3, CLI 0.9.1, renderer 0.11.8, PPTX 0.11.5, editor 0.10.5 | Previous coordinated set (the slide tag draws in the scheme primary colour and PPTX writes it as `a:schemeClr accent1`; the playground loads its base faces through `extraLazyFonts`). PPTX 0.11.6 exports the treemap, histogram, pareto, box-and-whisker, waterfall and funnel charts as native chartex parts by default (`toPptx({chartex: 'auto'})`, confirmed in desktop PowerPoint; `world` stays a clustered column with `chart-data-adapted` because PowerPoint's map needs online geodata; pass `chartex: 'fallback'` for the previous output) and gives chartex text the deck's label colour and font. |\n| core 0.11.3, CLI 0.9.1, renderer 0.11.6, PPTX 0.11.4, editor 0.10.4 | Previous coordinated set (the 100-layout catalog and its geometry, category-axis label rotation, quote and slide image re-import). Renderer 0.11.7 adds the `extraLazyFonts` registry option and `splitStartupFaces` (a browser host can start with Roboto Regular alone and load its other base faces on demand); renderer 0.11.8 draws the slide tag in the scheme primary colour; PPTX 0.11.5 writes the tag run as `a:schemeClr accent1` where the deck theme holds the primary (the colour is unchanged); editor 0.10.5 loads its playground base faces through `extraLazyFonts`. |\n| core 0.11.2, CLI 0.9.0, renderer 0.11.5, PPTX 0.11.3, editor 0.10.3 | Previous coordinated set (native classic and chartex chart previews, opt-in chartex export, face-level lazy fonts and the font gate's render options). Core 0.11.3 adds the pinned pptx.gallery default catalog and the 70 legacy gallery layout ids (layouts 30 to 100; 25 carry a `composition` or `contentBox` contract, which moves geometry, so renderer, PPTX and editor raise their core floor to `^0.11.3` together); renderer 0.11.6 rotates and skips dense category-axis labels; PPTX 0.11.4 re-imports quote and slide image payloads and writes theme `a:ea`/`a:cs` only where a script font is selected; editor 0.10.4 is a floor bump; CLI 0.9.1 bundles core 0.11.3. |\n| core 0.11.2, CLI 0.9.0, renderer 0.11.1, PPTX 0.11.0, editor 0.10.0 | Previous coordinated set (lockstep floors, Intos and the open families, selected-name export). Renderer 0.11.2 adds script-face loading (`scripts: 'auto'`); PPTX 0.11.1 adds `design.watermark` export; editor 0.10.2 loads the fonts a document needs before every render (FF-41). Renderer 0.11.3 previews every kept classic chart type natively; PPTX 0.11.2 exports the native construct for each kept classic chart type (with `chart-data-adapted` diagnostics where data is adapted) and writes theme colour references for table and text colours. Renderer 0.11.4 previews the seven chartex chart types natively (the world map as a non-geographic tile grid), keeps the Latin Noto Sans replacement for script schemes under `scripts: 'auto'`, shapes Noto Sans Mongolian, and bundles Raleway and Playfair Display (94 lazy faces); PPTX 0.11.3 adds the opt-in `toPptx({chartex: 'native'})` export of the chartex chart types (the default output is unchanged) and always imports chartex charts. |\n| core 0.11.0, CLI 0.9.0, renderer 0.9.0, PPTX 0.9.1, editor 0.8.0 | Previous coordinated Node 24 set (ColorRef, shared furniture). Renderer and PPTX had different core floors from 0.10.x. |\n| core 0.10.0, renderer/PPTX/CLI 0.8.0, editor 0.7.0 | Previous coordinated Node 24 baseline. Lint and furniture landed across 0.10.0/0.8.0 then layout-placeholder fixes in 0.10.1/0.8.1/0.7.1. |\n| Node 20 | Not valid for these packages |\n| Node 22 / 26 | Declared by the current set (`engines.node` `>=22`) and tested, but Node 24 is the toolchain the evidence runs on. Releases up to core 0.12.1, CLI 0.10.0, renderer 0.12.0, PPTX 0.12.3 and editor 0.11.2 declared `24.x`, so npm on Node 22 or 26 silently picked an older release |\n\nDo not install sibling `../opf-render` dist folders when following the\nquickstart. Packed and registry consumers must resolve `@openpresentation/*`\nfrom npm.\n"
37
+ "markdown": "# Compatibility matrix\n\nPublished registry evidence for the coordinated Node 24 toolchain. This matrix\nis the honest supported subset for [the developer quickstart](quickstart.md).\nIt is not universal Office parity and does not describe archived prototypes as\nshipped.\n\n**Verified artifacts (2026-10-17):** core 0.20.5, renderer/PPTX/editor 0.20.4,\nCLI 0.20.1 and Gallery 3.0.0.\n[Artifact verification](https://github.com/OpenPresentation/opf/blob/main/docs/evidence-rr-87-final-published-set-2026-10-17.md) binds\nthe selected archives to their release commits and trusted publication checks.\nEarlier matrices and native observations retain their historical identities.\nComplete native compatibility remains a separate gate.\n\n## Published 0.20 contract\n\nLayout `areas` uses nested arrays of cell names. Repeated names span rectangular\nregions and `.` denotes an empty cell. Gallery owns every default record;\nGallery 3 is required for catalog compatibility generation 3, while per-kind\nschema versions remain v2. Core 0.19 and later consume the Gallery package and\ndo not export `@openpresentation/opf/catalog` or ship a catalog snapshot.\n\nThe 0.20 engines share measured quote allocation, the 32 reference pixel\nstarting size for lists without an authored size, neutral structural borders\nand default right alignment for numeric table columns. Core and CLI forward\nregistered catalogs during PPTX import. PPTX preserves stretch images with\nzero crop through native save/re-import. These published contracts do not\nimply universal Office or full-catalog visual parity.\n\n## Current published package contracts\n\n| Package | Version | Dependency contract |\n| --- | --- | --- |\n| Core | 0.20.5 | Gallery ^3.0.0; optional renderer/PPTX ^0.20.0 |\n| CLI | 0.20.1 | Core ^0.20.5, Gallery ^3.0.0; optional renderer/PPTX ^0.20.0 |\n| Renderer | 0.20.4 | Core ^0.20.5; optional pinned font/converter peers |\n| PPTX | 0.20.4 | Core ^0.20.5, Gallery ^3.0.0; optional renderer ^0.20.4 |\n| Editor | 0.20.4 | Core ^0.20.5; optional renderer ^0.20.4 |\n| Gallery | 3.0.0 | Independent owner of every default catalog record; 28 intent layouts |\n\n## Runtime\n\n| Requirement | Status |\n| --- | --- |\n| Node.js | `engines.node` is `>=22` on every package below; **Node 24** is the coordinated toolchain these records are verified on (the 0.12.2 set and later are tested on Node 22, 24 and 26) |\n| Package managers | npm for published installs; this repo uses pnpm 10.33.2 for core development |\n| Account / model / hosted API | Not required |\n| Operating systems | macOS, Linux, Windows for Node APIs; browser entrypoints are separate |\n\n## Historical coordinated 0.18 published packages\n\n| Package | Version | Depends on |\n| --- | --- | --- |\n| `@openpresentation/opf` | 0.18.4 | optional peers `@openpresentation/opf-render@^0.18.0` and `@openpresentation/opf-pptx@^0.18.0`, which the Node build loads for `convert`, `open` and `save` |\n| `@openpresentation/cli` | 0.18.1 | `@openpresentation/opf@^0.18.3` (a regular dependency, no bundled copy); optional peers `@openpresentation/opf-render@^0.18.0` and `@openpresentation/opf-pptx@^0.18.0` |\n| `@openpresentation/opf-render` | 0.18.2 | `@openpresentation/opf@^0.18.4`; optional peers (RR-63): the converters `sharp`, `@resvg/resvg-js` and `pdf-lib` and the `@expo-google-fonts/*` font packages, which a host installs for the formats and fonts it uses |\n| `@openpresentation/opf-editor` | 0.18.2 | `@openpresentation/opf@^0.18.4`; optional peer `@openpresentation/opf-render@^0.18.2` |\n| `@openpresentation/opf-pptx` | 0.18.2 | `@openpresentation/opf@^0.18.4`; optional peer `@openpresentation/opf-render@^0.18.2` |\n\nThe 0.18 set is the consistent-API release ([design](programs/release-readiness/0.18-consistent-api.md)), breaking:\n- **One import (RR-70).** `import * as opf from \"@openpresentation/opf\"` works in every runtime through conditional\n exports, and `/node` is removed. `defaultCatalog` lives at `@openpresentation/opf/catalog` only.\n- **Names (RR-73) and options (RR-74).**\n - `ingest` replaces `importData` (and `opf ingest` replaces `opf import-data`).\n - `toSvg`, `toPng`, `toPdf` and `toHtml` work on decks.\n - The editor exports with `convert`.\n - `convert` takes `text`, `raster`, `charts`, `images`, `fonts` and `name`.\n- **Logos (RR-71).** Logos come from `organizations[].logo` (`full`, `stacked`, `icon`, `wordmark`, `onLight`/`onDark`),\n placed with `var:organization.logo.*`. `LogoSet` and zone `logo: true` are removed.\n- **PowerPoint (RR-72).** Shared furniture is written once on the PPTX slide master or layout.\n- **CLI (RR-75).** `opf convert` absorbs `render`, `export` and `import`. There is one output convention, plus\n `opf doctor` and a multi-file `validate`.\n\nSee the 0.18.0 and 0.18.1 entries of the [changelog](../CHANGELOG.md) and the 0.18.0 entry of the [CLI changelog](../packages/cli/CHANGELOG.md).\n\nCore starts at **0.18.1**. `opf-v0.18.0` was tagged, but its publish run failed before `npm publish`: a core test\nspawned the CLI, which the publish workflow had not built. So 0.18.1 is the first published 0.18 core.\n\nThe 0.17 set (core, editor and PPTX 0.17.0, renderer 0.17.1, CLI 0.17.1) was the lockstep minor after 0.16, also breaking:\n- **Furniture variables (FA-31):** header and footer text uses the `{{slide.number}}`, `{{slide.section}}` and\n `{{deck.slideCount}}` variables, and the `HeaderFooterItem` flags are removed.\n- **`@openpresentation/opf/node` (RR-62):** `convert`, `open` and `save` work from files and bytes. `parse` and\n `stringify` replace `readDeck` and `writeDeck`. `opf convert` is new, and `@openpresentation/cli/api` is removed.\n- **Fixes and additions:**\n - deterministic data import;\n - `validate` and `paginate` that plan script faces (RR-59);\n - the Arabic and Latin preview fixes;\n - `renderSvg(deck, { textAsPaths: true })` (RR-64 phase 1);\n - the Windows CLI path fixes (RR-66).\n\nSee the 0.17.0 entries of the [changelog](../CHANGELOG.md) and the 0.17.1 entry of the\n[CLI changelog](../packages/cli/CHANGELOG.md).\n\nRenderer 0.17.1 is a patch on the 0.17 line: SVG never embeds a face whose OS/2 `fsType` forbids it (RR-76), embedded faces are cut to the glyphs a slide draws (RR-65, mean example slide 2.4 MB to 59 KB), and `textAsPaths` shapes with HarfBuzz and keeps an invisible text layer (RR-64 phases 2 and 3).\n\nThe CLI starts at **0.17.1**. `cli-v0.17.0` was tagged, but its publish run failed before `npm publish`, because the\nrelease commit carried a stale generated gallery tracker. So 0.17.1 is the first published 0.17 CLI.\n\nThe 0.16 set (core, CLI, renderer and editor 0.16.0, PPTX 0.16.1) was the lockstep minor after 0.15 (owner decision 2026-10-08: no 0.15.x patch train). It adds nested\nplaceholder groups in layout records (FA-26), `Table.alt` (FA-27), the `opf/language-tag` warning (FA-29), Markdown\ndecks (`.opf.md`) in every command with core `readDeck`/`writeDeck` (RR-60), SVGs that embed only the faces they draw\n(RR-61), the CLI's programmatic `@openpresentation/cli/api` (RR-62), a lighter renderer install with optional\nconverters and fonts and the `/png`, `/pdf` and `/html` subpaths (RR-63), and the developer-contract fixes (RR-59).\n0.15 documents are 0.16 documents. See the 0.16.0 entries of the [changelog](../CHANGELOG.md) and the\n[CLI changelog](../packages/cli/CHANGELOG.md).\n\nThe 0.15 set (core 0.15.1, CLI 0.15.0, renderer, PPTX and editor 0.15.0) was the breaking spec line (format audit wave C): catalogs group the records a document embeds by the catalog they\ncame from (`catalogs.default`, `catalogs.custom`, named groups, `name:id` references), core ships no built-in records\n(the default catalog is the opt-in `@openpresentation/opf/catalog`, which a host registers with `{ catalogs }`), a\nfull-bleed photo is `design.background` and every content picture is an image block, and `language` is a BCP-47 tag.\nSee the 0.15.0 and 0.15.1 entries of the [changelog](../CHANGELOG.md).\nCore 0.15 started at **0.15.1**: `opf-v0.15.0` was tagged, but its publish run failed before `npm publish` (the CLI tests\nthrough the published renderer and PPTX ran in the core publish workflow before those 0.15 siblings existed), so 0.15.1\nwas the first published 0.15 core and the floor of every 0.15 sibling.\n\nOPF 0.15 is a breaking spec line (format audit wave C): catalogs group the records a document embeds by the catalog they\ncame from (`catalogs.default`, `catalogs.custom`, named groups, `name:id` references), core ships no built-in records\n(the default catalog is the opt-in `@openpresentation/opf/catalog`, which a host registers with `{ catalogs }`), a\nfull-bleed photo is `design.background` and every content picture is an image block, and `language` is a BCP-47 tag.\nThe 0.15 set reads and writes only 0.15 documents; see the 0.15.0 and 0.15.1 entries of the [changelog](../CHANGELOG.md).\nCore starts at **0.15.1**: `opf-v0.15.0` was tagged, but its publish run failed before `npm publish` (the CLI tests\nthrough the published renderer and PPTX ran in the core publish workflow before those 0.15 siblings existed), so 0.15.1\nis the first published 0.15 core and the floor of every 0.15 sibling.\n\nFrom CLI 0.16.0 the CLI depends on core (`@openpresentation/opf`, a regular dependency, not a bundled copy). CLI 0.16.0\nexposed `@openpresentation/cli/api`, and 0.17 removes it in favour of core's `@openpresentation/opf/node`. The renderer\nand PPTX stay optional peers. CLI 0.15.0 and earlier bundled their core.\n\nInstall the complete pinned set. A caret range starting at 0.10.1 does not\ninclude 0.11.x; old consumers can install a second core and do not establish\nColorRef preview/export support. The selected renderer 0.20.2 and PPTX/editor 0.20.3 require core ^0.20.2;\nCLI 0.20.0 accepts core ^0.20.1. Pin core 0.20.3 alongside them and\nverify one installed Core so preview and export use the same composition.\nCore 0.20.2 and later center metrics when alignment is omitted; explicit alignment remains\nauthoritative.\n\nShared header/footer geometry (`furniture-flow-v2`) is published. PPTX exports\neditable slide shapes tagged `OPF_FURNITURE_V1` with provenance for controlled\nreimport. Published PPTX through 0.11.8 draws every part that way, not as native\nOffice Header/Footer objects. PPTX 0.11.9 and later (RR-11) write the footer's\nfirst text, date and slide number as native `ftr`, `dt` and `sldNum` placeholders\n(with master/layout placeholders, `p:hf` flags and a notes-master flag) at the same\ngeometry and reads them back with or without provenance; the rest stays tagged\nshapes. Native PowerPoint acceptance remains [issue 87](https://github.com/OpenPresentation/opf/issues/87).\nPPTX 0.18 (RR-72) writes a tagged part that is the same on two or more slides once,\non the slide master or a shared layout (one per background tone for `onLight`/`onDark`\nlogos; a hidden footer uses a layout that hides the master's), and reads it back from\nthere ([rr-72-master-furniture.md](programs/release-readiness/rr-72-master-furniture.md)).\n\nThe [Windows native-picture checkpoint](evidence/windows-native-picture-20260921/README.md)\nand accepted [native B/C bundle](evidence/windows-native-edits-20260921/README.md)\nrecord finite picture/furniture edits, current-content provenance reimport and\nsafe fallback, production notes packaging and two controlled reordered-file\nrefusals. Core105 publishes that evidence; PPTX47 adds the tested harness, with\nno new package version. UI image replacement changes geometry, longer header\ntext clips, and duplicated tagged headers overlap. Refused workers retain their\nfailed cleanup state separately from later empty-workspace observations.\nThis is not general native layout/reflow fidelity.\n\nAccepted core106's [tab and font checkpoint](evidence/windows-native-tabs-fonts-20260921/README.md)\nrecords plain native tab target error **0.022655487060546875pt** and tab/literal\ndifference **0.022678375244140625pt**, both above the unchanged **0.02pt** gate.\nIts bounded four-face Carlito edit/save/reopen control passes exact text/style\npersistence, zero observed bounds drift, matching rasters and owned font cleanup.\nMixed-size table fidelity, physical glyph-font identity, fallback/synthesis and actual embedding remain\nopen; embedding was disabled for this control. The Windows supervisor retains sole\nOffice control. This documentation task reads evidence and makes no Office calls.\n\nThe later [read-only font inventory](evidence/windows-native-font-inventory-20260921/README.md)\nretains the four Carlito text styles but reports both Carlito and unexpected\nAptos in `Presentation.Fonts`. The native font allowlist fails, and no embedding\nwas attempted. The original parent report incorrectly fails cleanup because of\na Windows PowerShell 5.1 JSON-array parsing defect; raw stages and registration\nrows establish one owned close and four removals in a separately labeled offline\naudit. The raw failure remains intact. Collection names and flags do not identify\nthe physical font used for each glyph.\n\nThe [read-only mixed-table observation](evidence/windows-native-mixed-table-20260921/README.md)\nretains all 245 characters, one literal tab and five authored runs, with outer\ngeometry within 0.02pt and confirmed owned close/font cleanup. Native soft-line\nboundaries are 92/194 versus the estimated preview's 78/172, and native default\ntab spacing is 72pt. These finite content/style results do not pass table\nedit/save/reopen, browser/native raster agreement or physical glyph identity.\nThe accepted [nine-pair offline tab analysis](evidence/windows-native-tab-analysis-20260921/REPORT.md)\nfinds a 0.05pt-compatible pattern in the observed character starts, with finer saved\ntab coordinates. These inputs do not distinguish relative versus absolute placement\nor establish an internal engine cause. The 0.02pt native tab gate remains failed;\nthe separate 0.1px renderer gate is unchanged. Accepted core108 `9b277e1` and\ncore109 `b2711549` publish bounded evidence only. Windows-owned [core110](https://github.com/OpenPresentation/opf/pull/110)\nis merged as `4f7a4bd494f1a873319eff897423d301d1cfc9d6`, from reviewed fc3c36e\nwith four required PR checks passing. [Renderer30](https://github.com/OpenPresentation/opf-render/pull/30) is now merged\nas `c8d7d5ca1f67a7b39f70c7c4bd14577a865b175b`, with exact-head CI 35661051100\npassing. The supervisor reports postmerge 35661504472 also passed. Its companion\nsource preserves rich-tab advances/spans; the [accepted Windows wrap-up](handoff-windows-native-2026-09-21-wrap-up.md)\nrecords bounded source-linked rendering/browser checks and the original missing-test\nCI failure. These checks do not update the frozen registry consumer. Core111\n`3c5048522714365a41d9b5b9ba81620affae718b` publishes the font-inventory evidence\nabove with both PR workflows green; its postmerge workflows were started at the\nfinal notice, not recorded as passed. Package/lock/release/site pins, schema,\ngoldens and tolerances are unchanged. Native allowlist and physical-glyph/embedding\nacceptance remain open; no new package train or broad native pass is inferred.\n\n## Independently released packages\n\n| Package | Version | Depends on |\n| --- | --- | --- |\n| `@openpresentation/gallery` | 3.0.0 ([independent artifact verification](evidence/rr-85-final-published-set-2026-10-16/README.md); prior 2.0.0 and 1.0.0 remain historical) | nothing (its `GalleryCatalog` type has the shape of `@openpresentation/opf`'s `Catalog`) |\n\nThe gallery (RR-78) is released from its own repository, OpenPresentation/gallery, on its own version line (`vX.Y.Z`\ntags, `gallery-publish.yml` there), outside the lockstep set above and\nrecorded under `independentPackages` in `release-plan.json`: new catalog records are a minor release, a removed id or\na changed drawing a major one ([release process](release-process.md#the-gallery-lane-rr-78)). From OPF 0.19 core and the\nCLI consume the Gallery package (Gallery 2 in OPF 0.19; Gallery 3 in OPF 0.20), and core has no `/catalog` subpath; up to 0.18 core ships the same records as\n`@openpresentation/opf/catalog`.\n\n## Supported in this set\n\n| Capability | How | Notes |\n| --- | --- | --- |\n| JSON authoring | `*.opf.json` plus CLI `opf create` | Local files only |\n| Bundled examples catalog | `@openpresentation/opf/examples` | **127** decks; the quickstart JSON is a docs fixture, not a 128th catalog entry |\n| Validate | `validate` / `opf validate` | One read-only checker: format, references, policy, accessibility, layout and content findings ([guide](validate.md)); no network catalog fetch |\n| Color references | `ColorRef`, `variables`, `resolveColorRef` | Core schema/resolution, renderer preview and PPTX resolved colors are shipped. Supported theme-slot colors write native `schemeClr` references; arbitrary native Office recoloring and full visual fidelity remain separately scoped. |\n| Embed catalog records | `embed` / `opf embed` | Embeds every record a document references, in the group it resolves in, so it renders with no catalog registered; remote media and data remain explicit host concerns. |\n| Offline fonts | `loadFonts` (`/fonts-node`) | Bundled Roboto pack (`pack: 'base'`) or the office pack; hashed files; the handle is passed as `{ fonts }` to every deck-level call |\n| Composition | `composeSlide` | Includes shared headers/footers |\n| Pagination | `paginate` / `opf paginate` | Returns mappings; preserves source |\n| Edit + undo | `@openpresentation/opf-editor` `createEditorSession` | JSON Patch undo/redo |\n| JSON Patch CLI | `opf edit` | No persistent CLI undo history |\n| SVG preview | `toSvg(deck)` (every slide) / `toSvg(deck, n)` (slide n, counted from 1) (opf-render 0.18; `renderSvg` / `renderSlideSvg` before) | Local; the same `{ fonts }` as layout; `text: \"paths\"` draws the text as glyph outlines |\n| Convert, export and import files from code | `convert`, `open` and `save` of `@openpresentation/opf` in Node (core 0.18.0 and later; core 0.17 had them at `@openpresentation/opf/node`; CLI 0.16.0 had `exportDeck` / `importDeck` in `@openpresentation/cli/api`) | PDF, PNG, SVG and PPTX out, PPTX in, deck forms both ways, with the bundled fonts, no network and no clock; calls opf-render and opf-pptx as optional peers and throws `OPFExportError` / `OPFImportError` with the install command when one is missing. `opf convert` runs the same engine (CLI 0.18; `opf render`, `opf export` and `opf import` before it) ([OPF files in Node](node.md)) |\n| Read and write a deck in JSON, YAML or Markdown | `parse` / `stringify` (core 0.17.0 and later; `readDeck` / `writeDeck` in 0.16) | One entry point for the three text formats; `parse` throws `OPFValidationError` with located findings |\n| PNG | `toPng` (opf-render 0.18; `svgToPng` before) | Node raster of a deck or of SVG slides |\n| PDF | `toPdf` (opf-render 0.18; `svgToPdf` before) | opf-render 0.12.0 and later (RR-12, opf-render#90): **vector with selectable text by default** (embedded TrueType subsets of the supplied/bundled fonts, ToUnicode, links, metadata, tagged structure); `raster: true` keeps the image-per-slide output (`mode: \"raster\"` before 0.18). Renderers up to 0.11.9: raster-backed, not selectable text. Not a PDF/UA or PDF/A claim; see the renderer's `docs/evidence/rr-12-vector-pdf.md` for reader limits |\n| Editable PPTX export | `toPptx` | OPF \u2192 PPTX serialization. Text is one editable box per wrapped line, so edited text does not reflow in PowerPoint or Keynote, and a wrapped title is not the Title placeholder; the opt-in `bodyText: \"native\"` mode (CLI `--body-text native`, from opf-pptx and CLI 0.22.0) writes whole paragraphs, a wrapped title as the Title placeholder. Shared furniture is written on the master or layout; supported footer placeholders and tagged parts retain controlled re-import provenance. General native Office behavior remains separately scoped |\n| Agent skills | `opf skills install` | Offline after the CLI is installed |\n| Browser canvas | `@openpresentation/opf-editor/canvas` | Host must supply font bytes |\n\n## Public sites\n\nThe current coordinated adoption is recorded in the [final adoption note](evidence/rr-85-final-adoption-2026-10-16/README.md). The following source, CI and canonical production results are the historical September 21 checkpoint, recorded in the\n[font-readiness checkpoint](evidence/font-readiness-acceptance-20260921/README.md),\n[prior Inspector actions checkpoint](evidence/inspector-current-actions-20260921/README.md),\n[earlier publication checkpoint](evidence/inspector-share-acceptance-20260921/README.md),\n[source-preservation checkpoint](evidence/author-source-acceptance-20260921/README.md),\n[completion checkpoint](evidence/completion-acceptance-20260921/README.md),\n[earlier acceptance ledger](evidence/issue88-final-20260921/README.md) and\n[handoff](handoff-2026-09-21.md). [Issue88](https://github.com/OpenPresentation/opf/issues/88)\nremains open. Package adoption, deployed features and complete workflow\nacceptance are separate claims.\n\n| Surface | Deployed scope and acceptance | Source commit |\n| --- | --- | --- |\n| [openpresentation.org](https://www.openpresentation.org) | Current published guides, agent skills, JSON/preview workflow and downloads. Exact canonical deployment passes 321 checks across 11 pages and 18 raw resources, plus two browser flows for agent installation/navigation and JSON/SVG/PPTX downloads. Reviewed screenshots and output hashes match the accepted build. | `a85bcc77d899ce9ba1df659548be564142c16120` |\n| [pptx.dev](https://www.pptx.dev) `/inspector` and `/author` | App54 merged/live on exact READY production. Premerge Linux/Windows pass 704 units, 13 standalone controls and 39/39 browsers. Full canonical acceptance **fails (34/39 passed)** at five no-POST assertions; the bounded audit does not establish an introduced upload regression. Postmerge Linux 39/39 passes, Windows 38/39 fails initial font readiness. Preset Undo all and broader source writers remain unresolved. | `8f54228a9e38a1dcc0bf8188bcdd519795b3799a` |\n| [pptx.gallery](https://www.pptx.gallery) `/docs`, `/editor` and gallery pages | Published ColorRef/bundle guidance, Playground and Editor actions, and the canonical docs-to-editor flow are verified. | `f17e9ae5869669d5fbac3720f285652d0c37551c` |\n\nThe site uses documentation source `120a770`, whose tree matches accepted core\nPR98 commit `b1ff81db6f8714b0db1a98bde482ed8a64d0ccc9`. Core PR93/97/98/99/100/102/103 passed\npre-merge and post-merge CI. Accepted core102 is\n`578bcc6e0894129b00059258bd4ad1994a414baa`; its reviewed and accepted trees match,\nand all four required pre/post-merge runs passed on their first attempts. The\n[core102 receipts](evidence/inspector-share-acceptance-20260921/README.md#accepted-core102)\npin this documentation checkpoint without changing the site's older accepted\ndocumentation snapshot. The site's complete guides and raw resources match\nthe reviewed source; binary evidence remains linked and downloadable without\nbeing decoded into the AI-facing guide.\nCore104 `3d301f1` preserves exact postmerge OPF success and coordinated cancellation;\nit is not a complete green postmerge gate. Accepted descendant core105\n`84e914710520a7b0e777fce30e5758ee64a64924` preserves all 60 core104 evidence blobs\nand passes both exact-head workflows on their first attempts. [Compact receipts](evidence/inspector-current-actions-20260921/README.md#core-source-and-ci)\nkeep descendant acceptance separate from the canceled predecessor run.\nAccepted core106 `3847f712ccb2379952bcc8ab7c9fdbaedfd0a4ce` also passes both\npre/postmerge workflows on their first attempts. Its [compact receipt](evidence/inspector-current-actions-20260921/core106/acceptance-receipt.json)\nbinds the bounded native evidence above without completing general compatibility.\nAccepted core107 `5bc0d3f89414b382b2ce48452c7e56e5d66aaf74` has reviewed tree\n`ffdc678beadf0808bc717d67e7fc0a9ec4790127`. Both original postmerge push workflows\n**35652360502 / 35652360551** passed on attempt 1 under Node24.20.0.\n[Compact receipts](evidence/font-readiness-acceptance-20260921/README.md#core107-acceptance)\nretain earlier automatic premerge cancellations separately from the later automatic\nsuccessful pair; no rerun or accepted checkpoint relabels them.\n\nApp47 corrects the pre-app47\ncompletion adapter's rejected layout choices while preserving unchanged source\ntokens and undo history. Accepted commit `0f35352a1445f56ad4bb7c9f4c5609e01f2dd9ae`\nhas reviewed tree `203bdab509d05911f04f234d996f9c91f2b5e4f2`, green Linux/Windows\npre/post-merge CI and its exact READY canonical deployment. The historical App47 **23/24** production run passes all five new completion cases but still fails the existing\nLF Author third-popup assertion. This does not establish complete public-surface\nacceptance; the [historical App47 report](evidence/completion-acceptance-20260921/canonical/REPORT.md)\nand [earlier failed app45/app46 results](evidence/issue88-final-20260921/README.md)\nretain their evidence and unresolved causes.\n\nThe [source audit](evidence/completion-acceptance-20260921/source-preservation-audit/REPORT.md)\nidentified Author canvas/Copy/export and Inspector JSON-download normalization.\nMerged App53 at\n`e40c287b64fcbcfb85fb4a8a50641aea8e3e54a8` has the identical reviewed b33dc18\ntree and preserves those bounded raw-source\npaths and corrects order-only reimport history. A public Suggest-action guard\naddresses the observed stale Quick Input context competing with focused-editor\nCtrl+Space. Current local checks pass **627 unit tests and 29/29 browser cases\nin 88.78 seconds**, zero retries. First-attempt Linux/Windows application CI\npassed 627 unit tests and 29 browser cases per platform; artifact CI also passed.\nThe [exact READY canonical run](evidence/author-source-acceptance-20260921/canonical/REPORT.md) passes **29/29**, zero retries, with matching deployment receipts before and after. Postmerge application CI fails Linux **28/29** while Windows passes **29/29**; both pass 627 unit tests and separate artifact CI passes. The [Linux failure](evidence/author-source-acceptance-20260921/app53/postmerge-ci/README.md) stops before security assertions because five default-deck canvases remain after the shared-load toast. No rerun or canonical pass replaces that failed gate. The retained draft preview was READY but not browser-accepted.\n\nThe earlier **24/27** import-undo regression, **26/27** local popup failure and\nold-head Windows **26/27** shared-load failure remain historical evidence.\nThe fresh successful Windows job retained its sanitized timing artifact, but\nonly Author timings survived; the Inspector pagehide snapshot is missing. This\ndoes not explain or fix the old readiness delay. Phase4\ncaptured no post-fix stale-context overlap, so causal stress is inconclusive.\nThe existing suggestion-details pane remains clipped; visibility is not legibility.\nThe [postmerge trace diagnosis](evidence/author-source-acceptance-20260921/inspector-share-diagnosis/REPORT.md)\nproves wrong-document automatic share-hash publication during import.\nApp54 first corrected automatic\npublication at `60c91f6`: authoritative source/format is checked before and after\nencoding, load/navigation guards remain, and obsolete `import=hash:` is removed.\nURL transfer preserves semantics, not raw spelling. Local 659-unit/31-browser\nacceptance does not replace original first-attempt CI **35638158483**, which\npassed Linux **31/31** but failed Windows **30/31** at the unchanged 45-second\nAuthor readiness deadline. Both publication cases passed. The [frozen diagnosis](evidence/inspector-share-acceptance-20260921/app54/windows-timeout/REPORT.md)\nretains bounded slow-delivery observations with unknown cause. Late assertions\nare not an in-budget pass. Browser History tests permit prior accepted content\nuntil first new publication; held-promise controls establish the narrower race guard.\n\nThe product correction was added at `57e5e59fddbc94346f142dc12d86a916228bf2ae`, tree\n`d9c3aab543c6a886a85c6ec07dd55e5ab22590cd`. It guards current snapshots for\nexplicit Copy/JSON/Share/PPTX/PDF/Author/Deckchat actions. Pending public canvas\ndrafts commit before capture, pointer-blur rejection survives session recovery,\nand newer source/load/navigation/unmount/action invalidates late results. Raw\nCopy and accepted same-format JSON bytes are preserved; handoffs remain semantic.\nAlready-started clipboard/download effects cannot be retroactively canceled.\nThe integrated portable standalone startup verifies packaged runtime assets,\nfonts and traced schema inputs without changing dependencies or published pins.\n\nLocal runtime checks pass **692 unit tests**, **7 standalone controls**, focused\n**8/8** and full **39/39** browser cases, zero retries, with visual review. The\nimmutable 93-file app bundle\nretains stale-draft negative evidence and the initial candidate **7/8** result.\nThe latter did not establish accepted pasted source before releasing mocked PDF 401;\nfinal visible-code preconditions change no product bytes or budgets. PDF/Deckchat\nare locally mocked. CRLF ingress normalized 279 to 274 LF bytes; subsequent exact\nactions preserve the accepted buffer, not that ingress boundary.\n\n**The original 57e packaging gate failed.** First-attempt CI **35645493900** failed Linux and\nWindows typecheck on archived `.spec.ts` evidence copies after each platform\npassed 692 units and 7 standalone controls. The exact-head preview failed with\n`module_not_found`; precise Vercel compiler logs were unavailable. [The first-attempt receipt](evidence/inspector-current-actions-20260921/app54/first57-ci/REPORT.md)\nrecords skipped build/browser steps and no uploaded artifacts. The passing runtime build\npredates those copies. A byte-identical `.ts.txt` archive correction produced captured App54 head\n`6dfc2584698c1306505929a2bc3e427996d66563`, tree\n`f3279495f7685945b7541c90d24f7239407639a0`. Its final-tree local typecheck passes\nwith all 305 product/test inputs unchanged. [The corrected receipt](evidence/inspector-current-actions-20260921/app54/corrected6df/commit-receipt.json)\nbinds its 106-file evidence bundle. [Exact-head CI 35646213757](evidence/inspector-current-actions-20260921/app54/corrected6df/ci/REPORT.md)\npasses 692 units, seven standalone controls, typechecks and build on both platforms;\nLinux passes **39/39** browsers. Windows executes **zero browser tests** because\nstandalone startup fails with `EPERM` while statting its packaged React dependency\nlink. No timing or test-result artifacts were uploaded. The exact-head preview\nis READY, which is metadata only; at that 6df checkpoint App54 was unmerged and undeployed to production.\nThe [current ledger](evidence/inspector-current-actions-20260921/README.md)\nretains the separate 60c readiness, 57e packaging and 6df startup failures. Production then remained App53 `e40c287`, with its original\nfailed Linux postmerge gate preserved separately from canonical **29/29**.\nThe startup-link correction at 4338e57\nthen reached all browser cases: Linux **39/39**, Windows **38/39** in first-attempt\nCI **35649707689**, with 692 units and 13 standalone controls passing per platform.\nThe sole Windows failure was initial canvas-title visibility at five seconds,\nbefore any edit/recovery operation; correct incoming source remained at loading\nfonts. Late font acquisition and eleven incomplete responses at teardown do not\nestablish a permanent stall or a dominant cause. The [failed gate](evidence/font-readiness-acceptance-20260921/README.md#preserved-failed-windows-gate)\nis preserved.\n\nApp54 font-preparation revision `a4eb88ab7aa585c9efb91de4c190c1f1c0c7d0eb`, tree\n`031d893855a540fd2a4d2ec2162605817b8dde16`, overlaps font acquisition with converter\nwarmup while readiness still waits for both. The offline converter barrier and all\n**33 faces / 9,317,044 bytes**, manifest, substitution/measurement policy and\n`document.fonts.ready` gate remain unchanged. Fresh local Node24.21.0 checks pass\n**704 units, 13 standalone controls and 39/39 browsers**, zero retries, including\nunchanged offline export/reimport. Immutable app evidence\nretains reviewed images, exact source/output bindings and the original failed run.\n\n**The original a4eb gate failed:** first-attempt application **35654753237** passes\nLinux **39/39** but fails Windows **38/39**; both pass 704 units, 13 standalone\ncontrols, typecheck and build. The Open in Author URL assertion exceeds its existing\nfive-second deadline at `inspector-actions.spec.ts:176`; later source checks are\nnot reached. Original4338 font readiness passes in this run. No navigation cause\nor data-loss finding is established. The [frozen final audit](evidence/font-readiness-acceptance-20260921/app54/final-a4eb-ci/release-audit.json)\nretains exact source/artifact bindings and the original failed trace; that failure remains preserved.\nThe Python artifact workflow is not applicable under its full-PR path filters,\nnot a fresh pass. Exact-head preview is READY metadata only; an\nunauthenticated request redirects to sign-in, with no preview-browser acceptance.\nAt that a4eb capture App54 was unmerged and production remained App53\n`e40c287`. The separate suggestion-details candidate remains unreleased and supplies\nno acceptance here. No local/browser pass broadens native compatibility.\nThe [bounded navigation diagnosis](evidence/font-readiness-acceptance-20260921/README.md#author-navigation-diagnosis-and-prospective-policy)\nrecords Loading Author and a delayed successful script response: 1490 bytes inferred\nfrom ETag, 766 compressed bytes recorded, actual body absent. Later DOM does not\naccept unreached assertions or identify a cause. App54 accepted merge\n`8f54228a9e38a1dcc0bf8188bcdd519795b3799a` retains reviewed 79ba tree\n`3639d3c14daec94d13711fa2b10f2b927df45eca`, preregisters `waitForURL(load)` before\nthe real action, matching `page.goto` within the unchanged 45-second test and default\nfive-second content budgets. It retains all oracles but deliberately removes the\nincidental five-second navigation deadline. Fresh local **39/39**, zero retries,\npasses in 112.780048s with reviewed source/images and prepared-tree build/typecheck.\nThe immutable app bundle\nretains original failures. Units/standalone controls were not repeated locally;\nfresh first-attempt application **35659187971 passes 704 units, 13 standalone\ncontrols and 39/39 browsers on both Linux and Windows**. Exact-head preview was\nREADY/protected, not browser accepted. The identical reviewed tree is merged/live\nat 8f on READY deployment `dpl_H1FtXx1QSuGxn3MwzJwWJpGtRg8b`, but full canonical\nacceptance **fails (34/39 passed)** at five no-POST assertions observing Clerk environment\nPOSTs. The [safe audit](evidence/font-readiness-acceptance-20260921/app54/canonical8f/write-audit/REPORT.md.txt)\nrecords ten such requests, nine with HTTP200/zero-length bodies and one incomplete.\nNo fixture-content needle was detected in captured fields; uncaptured data remains\nunknown. Three final action page-error assertions were not reached; the two share\ncases passed theirs. The prior auth/config comparison is 28/29 identical, with only\npackage scripts changed. Production is kept without a rollback, test change or\nrerun; the strict gate remains failed. Raw authentication-bearing diagnostics\nremain private. Postmerge CI 35660464578\nfinishes failed: Linux 39/39 passes while Windows 38/39 fails, with 704 units/13 controls/typecheck/build\npassing on each. Windows fails initial gallery-rail title visibility after 5,000ms\nwith correct source, clean schema and Loading slide fonts; later editing/export/\nreimport checks were not reached. The [final audit](evidence/font-readiness-acceptance-20260921/app54/merged8f/postmerge-ci/REPORT.md.txt)\npreserves this separate failed gate without cause inference or a retry. No canonical\npass or general native/font acceptance is claimed. That September 21 checkpoint was paused; the user resumed work on September 29. See the [current source checkpoint](handoff-runtime-2026-09-29.md) for ongoing repairs and release holds.\n\nSeparate local negative controls confirmed that preset Undo all discarded New run\nand imported replacement documents. The guarded correction is now preserved in\ndraft app #58: independent\nreview and local Node 24 checks passed (716 units, 13 standalone controls and\n49 browsers without retries). Original Linux/Windows CI could not start because\nof the account payment/spending-limit restriction; no application CI or production\nacceptance is claimed. Unbusy asynchronous account replacements still need a\nsynchronous invalidation guard and held-response control.\nOther local source writers still require their separate preservation checks. These unresolved local findings and raw imported-file/account/agent/metadata boundaries remain outside\nApp53 and App54. See the [App53 ledger](evidence/author-source-acceptance-20260921/README.md)\nand its immutable application evidence links. Issue88 remains OPEN. Native/font\ncompatibility, required repair and release gates remain separate; geometry is\ndeferred as the coordinated set below. The unimplemented worker candidate remains in the [handoff](handoff-2026-09-21.md).\n\nThe five coordinated geometry drafts (core94, renderer27, editor25, PPTX42,\nsite40) remain unmerged. In particular, site40 is not independently shipped.\n\n## Explicitly not shipped\n\n| Topic | Tracker | Do not describe as done |\n| --- | --- | --- |\n| Linux vs Chromium native-width residual at the 0.1px gate | [opf-render#24](https://github.com/OpenPresentation/opf-render/issues/24) | Rounding that fixes Linux but breaks macOS is rejected |\n| General native PowerPoint fidelity beyond the supported footer placeholders | [opf#87](https://github.com/OpenPresentation/opf/issues/87) | Finite B/C and bounded Carlito edit controls above are accepted evidence, as is one finite mixed-size table edit/save/reopen ([evidence](evidence/windows-native-mixed-edit-20260922/README.md)). Tab tolerance, general mixed-size table layout and preview/native wrapping, physical glyph identity/fallback/synthesis, embedding and general layout/reflow fidelity remain open; supported native footer placeholders, self-import and tagged furniture do not certify arbitrary Office behavior |\n| Public-surface acceptance checklist | [opf#88](https://github.com/OpenPresentation/opf/issues/88) | Shipping features does not establish every acceptance item; use the checklist and deployment receipts |\n| HarfBuzz / prepared-glyph shaping | Archive branches `codex/archive-shaping-20260915` | Prototypes are preserved, not in npm |\n| Selectable vector PDF | [pdf plan](plans/pdf-export.md) | Shipped in opf-render 0.12.0 (opf-render#90; the browser download entry `@openpresentation/opf-render/export-browser` ships there too); PDF/UA, PDF/A and viewer coverage beyond pdf.js, PDFium and poppler remain open |\n| General SVG diagrams / Mermaid | [diagrams plan](plans/diagrams-svg.md) | Embedded SVG \u2260 native editable primitives |\n| Full visual editor / IME / bidi / repair loop | [developer adoption](plans/developer-adoption-20260915.md) | Schema support \u2260 WYSIWYG coverage |\n\nCLI 0.9.2 and earlier do not render or export PPTX; CLI 0.10.0 adds `opf render`, `opf export` and `opf import`\nthrough the optional peers opf-render and opf-pptx ([CLI reference](cli.md)). The Node `toPng` / `toPdf`\n(`svgToPng` / `svgToPdf` before 0.18) APIs stay Node-only; renderer 0.12.0 adds the separate `@openpresentation/opf-render/export-browser` entry for browsers.\nCore's file API (`open`, `save`, `convert`) is Node-only too: it reads files, font files and the engines' native packages. From\ncore 0.18 the root resolves to a browser-safe build in a browser or a worker, with the same names, where those three reject\nwith `node-only`; every other core entry is browser-safe. In a browser, use the engines' browser entries.\n\n## Predecessor notes\n\n| Older set | Relationship |\n| --- | --- |\n| core 0.17.0, CLI 0.17.1, renderer 0.17.1, PPTX 0.17.0, editor 0.17.0 | Previous coordinated set (FA-31 furniture variables, `@openpresentation/opf/node`, `parse`/`stringify`, `opf convert`). The 0.18 set is breaking: one root import replaces `/node`, the `toSvg`/`ingest` names and `convert` options, organization logos, and the consolidated CLI. |\n| core 0.16.0, CLI 0.16.0, renderer 0.16.0, PPTX 0.16.1, editor 0.16.0 | Previous coordinated set (Markdown decks, used-face SVG embedding, optional renderer converters, nested placeholder groups, `@openpresentation/cli/api`). The 0.17 set is breaking: header and footer flags become `{{slide.number}}`-style variables (FA-31), `readDeck`/`writeDeck` become `parse`/`stringify`, and `@openpresentation/cli/api` is replaced by `@openpresentation/opf/node`. |\n| core 0.15.1, CLI 0.15.0, renderer 0.15.0, PPTX 0.15.0, editor 0.15.0 | Previous coordinated set (OPF 0.15, format audit wave C; CLI 0.15.0 bundles core 0.15.1). The 0.16 set is additive for documents; the renderer's converters and font packages became optional peers (RR-63), so a 0.16 host installs the ones it uses, and the CLI depends on core instead of bundling it (RR-62). |\n| core 0.14.0, CLI 0.11.0, renderer 0.14.0, PPTX 0.14.0, editor 0.14.2 | Previous coordinated set (format audit waves A and B, RR-55 and RR-56; CLI 0.11.0 bundles core 0.13.0). The 0.15 set is breaking: a 0.14 document's catalog shape (`catalogs.<kind>.records`), catalog-id languages and `design.slideImage` are invalid in 0.15, and the 0.15 engines need the host to register the default catalog. |\n| core 0.13.0, CLI 0.11.0, renderer 0.13.1, PPTX 0.13.2, editor 0.12.1 | Previous coordinated set (chart and table data, RR-54). |\n| core 0.12.2, CLI 0.10.1, renderer 0.12.2, PPTX 0.12.4, editor 0.11.3 | Previous coordinated set (the first set whose `engines.node` is the open-ended `>=22`). The 0.13 set adds chart and table data (RR-54, [contract](chart-table-data.md)): strict chart numbers (a value is a finite number or a strict decimal string; `\"12%\"`, `\"$5\"` and `\"Q1\"` are a gap with a `chart-value-not-numeric` warning, never a guessed number), number formats (`DataColumn` `{ name, format }`), top-level `datasets` with `{ \"dataset\", \"fields\" }` references and `chart.mapping`, in core 0.13.0, previewed by renderer 0.13.0, exported and re-imported by PPTX 0.13.0 (`OPF_DATASETS_V1` and `OPF_DATA_V1` provenance), edited in the editor 0.12.x data grid and imported by CLI 0.11.0 (`opf import-data --dataset`). PPTX 0.13.0 also vendors pptxgenjs-plus 4.3.4 (was PptxGenJS 4.0.1) and PPTX 0.13.1 bundles for browsers again. Renderer 0.13.1 and PPTX 0.13.2 pin Sharp 0.35.5 (GHSA-wq5f-xc86-pv6w; output unchanged); editor 0.12.1 replaces 0.12.0, which was never published. Renderer, PPTX and editor raise their core floor to `^0.13.0` and the renderer peer to `^0.13.1` together. |\n| core 0.12.1, CLI 0.10.0, renderer 0.12.0, PPTX 0.12.2, editor 0.11.2 | Previous coordinated set. PPTX 0.12.3 corrects the table range of a chart's embedded workbook (apostrophe-quoted sheet references and bubble-series references), which made Keynote drop category charts on import (opf-pptx#162, opf-pptx#163; every other part of the package is byte-identical). Core, renderer and editor are unchanged and keep the core floor `^0.12.0` and the renderer peer `^0.12.0`. |\n| core 0.12.0, CLI 0.10.0, renderer 0.12.0, PPTX 0.12.2, editor 0.11.2 | Previous coordinated set. Core 0.12.1 deprecates the six plural audience ids (`executives`, `investors`, `customers`, `sales-team`, `marketing-team`, `regulators`) in favour of the singular ids: additive catalog data, validation warns and never errors, and no geometry moves, so renderer, PPTX and editor keep the core floor `^0.12.0`. |\n| core 0.12.0, CLI 0.10.0, renderer 0.12.0, PPTX 0.12.1, editor 0.11.1 | Previous coordinated set (PowerPoint lists only the deck's fonts). PPTX 0.12.2 also names the deck's font in the theme's `Viet` (Vietnamese) and `Uigh` (Uyghur) per-language script entries, which PowerPoint applies to `vi-VN` and `ug` runs and which kept Office's Times New Roman and Arial for those two languages (RR-17; every other language is byte-identical). Editor 0.11.2 adds the `slide-sizes` and `purposes` switch dimensions, `SLIDE_SIZE_PRESETS` and the Slide size and Purpose selects in the Design panel (RR-41, additive API). Both keep the core floor `^0.12.0` and the renderer peer `^0.12.0`. |\n| core 0.12.0, CLI 0.10.0, renderer 0.12.0, PPTX 0.12.0, editor 0.11.0 | Previous coordinated set (the 0.12.0 train). PPTX 0.12.1 writes the deck's fonts so that PowerPoint's font list shows only the fonts the deck uses (FF-05: schema-order `presentation.xml`, an own notes theme, no east-asian or complex-script font on runs, and a theme east-asian slot that is never empty); editor 0.11.1 stops the restore prompt showing a literal `null`. Both keep the core floor `^0.12.0` and the renderer peer `^0.12.0`. |\n| core 0.11.4, CLI 0.9.2, renderer 0.11.9, PPTX 0.11.9, editor 0.10.6 | Previous coordinated set (native header/footer placeholders, SVG pictures, `fromPptx` import signals; the published 0.11.4 train measured in the font-fidelity program). Core 0.12.0 moves geometry (composed font sizes on PowerPoint's 0.01 pt grid, hanging wrap whitespace, promoted regions in reading order, right-to-left decks composed mirrored), so renderer 0.12.0, PPTX 0.12.0 and editor 0.11.0 raise their core floor to `^0.12.0` and the renderer peer of PPTX and editor to `^0.12.0` together; the set adds templates and variables, numbered lists, footnotes, citations and captions and chart options (additive schema), vector PDF with selectable text and the browser export entry, the `<opf-deck>` player, the editor's slide management, autosave, data grid, find and replace, image crop, Review panel and fill UI, and the shared JSON Patch module. CLI 0.10.0 bundles core 0.12.0 and adds `opf audit`, `from-md`, `to-md`, `diff`, `merge`, `format`, `render`, `export` and `import`. |\n| core 0.11.4, CLI 0.9.2, renderer 0.11.9, PPTX 0.11.8, editor 0.10.6 | Previous coordinated set (PPTX 0.11.8 re-imports wrapped rich text as one authored payload; CLI 0.9.2 bundles core 0.11.4). PPTX 0.11.9 writes a deck footer's first text, date and slide number as native PowerPoint Header & Footer placeholders (every export also carries the footer placeholders on its master, layout and notes master, so Insert > Header & Footer works), exports an SVG image as a native SVG picture over a PNG fallback (rasterized in Node by the optional opf-render peer or `options.svgRasterizer`) and adds the opt-in `fromPptx(bytes, {signals: true})` import signals; core floor `^0.11.4` unchanged. |\n| core 0.11.4, CLI 0.9.1, renderer 0.11.9, PPTX 0.11.7, editor 0.10.6 | Previous coordinated set (the design fields compose and export natively). PPTX 0.11.8 re-imports rich text that wraps over several native lines as one authored payload (the export records the line count; decks exported by 0.11.7 import as before) and keeps the core floor `^0.11.4`; CLI 0.9.2 bundles core 0.11.4 (CLI 0.9.1 bundled core 0.11.3) and still requires Node 24. |\n| core 0.11.3, CLI 0.9.1, renderer 0.11.8, PPTX 0.11.6, editor 0.10.5 | Previous coordinated set (the native chartex export by default). Core 0.11.4 composes the design fields (logos on covers and section slides, `contentDirection`, `chartPrimary`, picture bullets, header and footer logos, the accent font), aligns a cover's tag and subtitle with its title, and sizes picture bullets and furniture images as PowerPoint does, so renderer, PPTX and editor raise their core floor to `^0.11.4` together; renderer 0.11.9 draws those fields and applies the tag contrast rule (FF-61: the tag draws in the text colour when the scheme primary is under 4.5:1); PPTX 0.11.7 exports them natively, writes every chart's text at the preview's size (FF-62: 12 pt, not 9 pt), writes slide sections as PowerPoint's section list and restores the authored form of a fresh export on import (a root payload returns as `slides.N.text`, `.items`, `.chart` ... rather than one typed block); editor 0.10.6 is a floor bump. CLI 0.9.1 still bundles core 0.11.3. |\n| core 0.11.3, CLI 0.9.1, renderer 0.11.8, PPTX 0.11.5, editor 0.10.5 | Previous coordinated set (the slide tag draws in the scheme primary colour and PPTX writes it as `a:schemeClr accent1`; the playground loads its base faces through `extraLazyFonts`). PPTX 0.11.6 exports the treemap, histogram, pareto, box-and-whisker, waterfall and funnel charts as native chartex parts by default (`toPptx({chartex: 'auto'})`, confirmed in desktop PowerPoint; `world` stays a clustered column with `chart-data-adapted` because PowerPoint's map needs online geodata; pass `chartex: 'fallback'` for the previous output) and gives chartex text the deck's label colour and font. |\n| core 0.11.3, CLI 0.9.1, renderer 0.11.6, PPTX 0.11.4, editor 0.10.4 | Previous coordinated set (the 100-layout catalog and its geometry, category-axis label rotation, quote and slide image re-import). Renderer 0.11.7 adds the `extraLazyFonts` registry option and `splitStartupFaces` (a browser host can start with Roboto Regular alone and load its other base faces on demand); renderer 0.11.8 draws the slide tag in the scheme primary colour; PPTX 0.11.5 writes the tag run as `a:schemeClr accent1` where the deck theme holds the primary (the colour is unchanged); editor 0.10.5 loads its playground base faces through `extraLazyFonts`. |\n| core 0.11.2, CLI 0.9.0, renderer 0.11.5, PPTX 0.11.3, editor 0.10.3 | Previous coordinated set (native classic and chartex chart previews, opt-in chartex export, face-level lazy fonts and the font gate's render options). Core 0.11.3 adds the pinned pptx.gallery default catalog and the 70 legacy gallery layout ids (layouts 30 to 100; 25 carry a `composition` or `contentBox` contract, which moves geometry, so renderer, PPTX and editor raise their core floor to `^0.11.3` together); renderer 0.11.6 rotates and skips dense category-axis labels; PPTX 0.11.4 re-imports quote and slide image payloads and writes theme `a:ea`/`a:cs` only where a script font is selected; editor 0.10.4 is a floor bump; CLI 0.9.1 bundles core 0.11.3. |\n| core 0.11.2, CLI 0.9.0, renderer 0.11.1, PPTX 0.11.0, editor 0.10.0 | Previous coordinated set (lockstep floors, Intos and the open families, selected-name export). Renderer 0.11.2 adds script-face loading (`scripts: 'auto'`); PPTX 0.11.1 adds `design.watermark` export; editor 0.10.2 loads the fonts a document needs before every render (FF-41). Renderer 0.11.3 previews every kept classic chart type natively; PPTX 0.11.2 exports the native construct for each kept classic chart type (with `chart-data-adapted` diagnostics where data is adapted) and writes theme colour references for table and text colours. Renderer 0.11.4 previews the seven chartex chart types natively (the world map as a non-geographic tile grid), keeps the Latin Noto Sans replacement for script schemes under `scripts: 'auto'`, shapes Noto Sans Mongolian, and bundles Raleway and Playfair Display (94 lazy faces); PPTX 0.11.3 adds the opt-in `toPptx({chartex: 'native'})` export of the chartex chart types (the default output is unchanged) and always imports chartex charts. |\n| core 0.11.0, CLI 0.9.0, renderer 0.9.0, PPTX 0.9.1, editor 0.8.0 | Previous coordinated Node 24 set (ColorRef, shared furniture). Renderer and PPTX had different core floors from 0.10.x. |\n| core 0.10.0, renderer/PPTX/CLI 0.8.0, editor 0.7.0 | Previous coordinated Node 24 baseline. Lint and furniture landed across 0.10.0/0.8.0 then layout-placeholder fixes in 0.10.1/0.8.1/0.7.1. |\n| Node 20 | Not valid for these packages |\n| Node 22 / 26 | Declared by the current set (`engines.node` `>=22`) and tested, but Node 24 is the toolchain the evidence runs on. Releases up to core 0.12.1, CLI 0.10.0, renderer 0.12.0, PPTX 0.12.3 and editor 0.11.2 declared `24.x`, so npm on Node 22 or 26 silently picked an older release |\n\nDo not install sibling `../opf-render` dist folders when following the\nquickstart. Packed and registry consumers must resolve `@openpresentation/*`\nfrom npm.\n"
38
38
  },
39
39
  {
40
40
  "slug": "content-item-design-overrides",
@@ -70,7 +70,7 @@ var docsData = Object.freeze([
70
70
  "slug": "design-resolution",
71
71
  "file": "docs/design-resolution.md",
72
72
  "title": "Design Resolution",
73
- "markdown": '# Design Resolution\n\nHow an engine decides the effective design for any given slide. The schema spreads these rules across field descriptions; this page states them once, as an algorithm.\n\n## Precedence\n\nFor every design field independently, the most specific source wins:\n\n```\n wins +--------------------------------------------------------+\n ^ | 1. slide design slides[i].design.* |\n | +--------------------------------------------------------+\n | | 2. deck design design.* on the presentation root |\n | +--------------------------------------------------------+\n | | 3. resolved theme colorScheme, fontScheme, |\n | | background, dimensions from the |\n | | theme record |\n | +--------------------------------------------------------+\n loses | 4. engine defaults e.g. spec/reference/ |\n v | engine-defaults.json |\n +--------------------------------------------------------+\n```\n\n1. **Slide design** \u2014 `slides[].design.*`\n2. **Deck design** \u2014 `design.*` on the presentation root\n3. **Resolved theme** \u2014 defaults carried by the theme record (`colorScheme`, `fontScheme`, `background`, `dimensions`)\n4. **Engine defaults** \u2014 [`spec/reference/engine-defaults.json`](../spec/reference/engine-defaults.json), compiled into core as `ENGINE_DEFAULT_THEME`, `ENGINE_DEFAULT_COLOR_SCHEME` and `ENGINE_DEFAULT_FONT_SCHEME`\n\nResolution is **per field**, not per object. A slide that sets only `design.contentAlignment` inherits everything else from the deck design; a deck that sets only `design.colorScheme` keeps the theme\'s font scheme and background.\n\nTwo field-level rules complete the picture:\n\n- **Base-plus-overrides within one object.** Wherever a reference object carries an `id` (`ColorScheme`, `FontScheme`, and the inline `Audience`, `Purpose` and `Tone`), the `id` resolves a catalog record as the base and sibling fields override the resolved record per key. `design.theme` is a reference string only. The string shorthand (`"colorScheme": "cool-horizon"`) is equivalent to setting only `id`.\n\n ```\n "colorScheme": { "id": "cool-horizon", "accent1": "#0F4C81" }\n\n catalog record "cool-horizon" sibling fields on the object\n accent1: "#2874A6" <-- replaced -- accent1: "#0F4C81"\n accent2: "#1B4F72" <-- kept\n light1: "#FFFFFF" <-- kept\n |\n v\n effective scheme: accent1 from the override, everything else\n from the record\n ```\n- **Explicit suppression.** `watermark`, `header`, and `footer` accept `false` to switch off an inherited value \u2014 distinct from omitting the field, which inherits.\n\nEvery reference inside this chain resolves the one way every content reference does: `catalogs.custom`, then the records embedded under `catalogs.default`, then the catalog the host registered for its source (`name:id` in `catalogs.<name>`, then the host catalog for its source); see [`how-opf-works.md`](./how-opf-works.md) and [the catalogs page](default-catalog.md).\n\n## Worked example 1: color scheme through every level\n\n```json\n{\n "design": {\n "theme": "classic",\n "colorScheme": "forest-green"\n },\n "slides": [\n { "title": "Inherits the deck" },\n {\n "title": "Slide override",\n "design": {\n "colorScheme": { "id": "cool-horizon", "accent1": "#0F4C81" }\n }\n }\n ]\n}\n```\n\n- Slide 1: the `classic` theme record supplies its own default color scheme, but the deck design sets `colorScheme` explicitly, so `forest-green` wins (level 2 beats level 3). Fonts, background, and dimensions still come from `classic`.\n- Slide 2: slide design beats deck design (level 1 beats level 2). The `cool-horizon` record resolves as the base, then `accent1` is replaced by `#0F4C81`. All other `cool-horizon` slots survive.\n\nThere is no ambiguity between "override" and "reference": every scheme value *is* a reference, and any sibling fields on the same object are overrides applied after the reference resolves.\n\n## Worked example 2: backgrounds and suppression\n\n```json\n{\n "design": {\n "theme": "dark",\n "background": "light1",\n "footer": {\n "left": { "text": "Acme Corp" },\n "right": { "text": "{{slide.number}}" }\n }\n },\n "slides": [\n { "title": "Light slide in a dark theme" },\n {\n "title": "Section divider",\n "design": {\n "background": {\n "type": "gradient",\n "gradient": {\n "angle": 90,\n "stops": [\n { "color": "#0B1B2B", "position": 0 },\n { "color": "#123A5F", "position": 1 }\n ]\n }\n },\n "footer": false\n }\n }\n ]\n}\n```\n\n- Slide 1: the deck-level `background: "light1"` overrides the `dark` theme\'s default background. `light1` is a theme slot \u2014 it resolves through the effective color scheme, which itself resolved through the chain above.\n- Slide 2: the gradient replaces the deck background for this slide only, and `footer: false` suppresses the inherited footer rather than inheriting or replacing it.\n\n## Worked example 3: font scheme models\n\n```json\n{\n "design": {\n "fontScheme": {\n "id": "aptos",\n "code": "JetBrains Mono"\n }\n }\n}\n```\n\nThe `aptos` record supplies the OOXML pair (`major`/`minor`). The `code` role is an OPF-specific addition with no OOXML slot, so it layers on top without disturbing the pair. When serializing to PowerPoint, engines write `major`/`minor` to `majorFont`/`minorFont` and map abstract roles (`heading`, `body`) onto those slots. `accent` has no slot; the `code` family is written directly on code runs. The same slot-versus-role split applies to color schemes: OOXML slots (`accent1`\u2013`accent6`, `dark1/2`, `light1/2`) round-trip directly, abstract roles (`primary`, `text`, `surface`, \u2026) are mapped onto slots by the engine.\n\n### Code font\n\nThe `code` role resolves per key like every other override:\n\n1. `code` on the effective `design.fontScheme` object;\n2. `code` on the resolved font-scheme record (the `consolas` and `courier-new` records carry `"Consolas"` and `"Courier New"`);\n3. otherwise **Roboto Mono**, the documented fallback that `@openpresentation/opf-render` bundles.\n\nThe heading and body families are never reused as the code fallback, so choosing `aptos` still gives Roboto Mono code unless the deck sets `code`. `resolveFontFamilies()` in `@openpresentation/opf/composition` applies these rules for all engines.\n\n### Engine default font scheme\n\nThe last-resort font scheme applies only when neither the slide, the deck nor the resolved theme names one, or when the reference resolves nowhere. It is an engine default in code, not a catalog record: `ENGINE_DEFAULT_FONT_SCHEME` (`{ major: "Aptos Display", minor: "Aptos", languageFamily: "latin" }`, the drawing fields of the gallery\'s `aptos` scheme), recorded in [`engine-defaults.json`](../spec/reference/engine-defaults.json) and exported from the package root and `@openpresentation/opf/composition`. A document with no `design` uses the engine default theme (`ENGINE_DEFAULT_THEME`, which names no font scheme), so it gets Aptos in every engine without any catalog registered.\n\nEvery engine shares this one last resort, so a theme without `fontScheme` is measured, paginated, previewed and exported in the same fonts:\n\n| Engine | Last resort | Where |\n| --- | --- | --- |\n| Core pagination and validate | `ENGINE_DEFAULT_FONT_SCHEME` | `packages/javascript/src/design-records.ts` |\n| opf-render preview | core\'s `resolveSlideContext` | `src/svg.js` |\n| opf-editor composition and slide transfer | core\'s `resolveSlideContext` | `src/font-defaults.js` |\n| opf-pptx export | core\'s `resolveSlideContext` | `src/index.js` |\n\nAptos is not openly licensed, so no OPF package bundles it. Previews take the same path for the last resort as for any `aptos` deck:\n\n- **Estimated layout** (no `fonts`): the SVG names `Aptos` and `Aptos Display`, and the default raster engine draws them with its bundled sans-serif fallback (Roboto).\n- **Measured layout** with the opf-render office font pack (`loadFonts({pack: \'office\'})`): `Aptos` and `Aptos Display` resolve to Intos and Intos Display, metric-compatible replacements from the OPF font policy (0.000% mean width difference against Aptos 2.01), and the substitution report lists both. With only the base pack and `substitutionPolicy: "visual"` they fall back to the visual alternates Roboto and Carlito. The PPTX always names Aptos. See [font-fidelity.md](font-fidelity.md#font-policy-ff-31).\n- **Measured layout with only the base pack under the metric policy**: `font-unavailable` for Aptos, as for a document with no `design`. Supply licensed Aptos faces, allow visual substitution, or set a `fallbackFamily`.\n\n`packages/javascript/test/font-scheme-defaults.test.mjs` checks the shared default in core pagination, and each sibling repository has a parity test.\n\n### References that resolve nowhere\n\nA layout, theme, colour scheme or font scheme reference that neither the document embeds nor a registered catalog defines falls back, and no engine throws for it (unless the host asks for a strict export, `strictReferences`, which throws `OPFUnresolvedReferenceError`):\n\n- **Layout.** A slide with no `layout` is composed with **no layout record**, and that is not a finding: title, subtitle, tag and content are arranged automatically, a slide with no body payload is a cover (the title block vertically centred) and the design hints apply. A layout reference that resolves nowhere composes the same way. An engine never substitutes a different layout.\n- **Theme.** A theme reference that resolves nowhere uses the engine default theme (`ENGINE_DEFAULT_THEME`: the background and dimensions of the gallery\'s `minimal`).\n- **Colour scheme.** A colour-scheme reference that resolves nowhere uses `ENGINE_DEFAULT_COLOR_SCHEME` (the twelve slots of `cool-horizon`). The theme\'s own colour scheme counts as the reference when the deck and slide name none, and resolves in the theme\'s catalog group first. An object without an `id` is laid over the engine default, so a partial inline scheme keeps the slots it does not name.\n- **Font scheme.** A font-scheme reference that resolves nowhere uses `ENGINE_DEFAULT_FONT_SCHEME`. Sibling fields on an object reference still override it per key, so `{ "id": "no-such-scheme", "major": "Inter", "minor": "Inter" }` uses Inter, and a `code` role still applies. An object without `id` is an inline scheme on the same base and reports nothing.\n\nEach is one `unresolved-reference` diagnostic: `{ code, kind, reference, path, group, source?, fallback, message }`, with `path` where the reference is written (`slides.N.layout`, `slides.N.design.theme`, `design.colorScheme`, `design.fontScheme.id`, or `catalogs.<group>.themes.<id>.colorScheme` for a reference inside an embedded theme), `fallback` `automatic` for a layout and `engine-default` otherwise, and a message that names the reference and the catalog source it was looked for in. `validate` reports the same references as `opf/unresolved-reference` warnings.\n\n`resolveSlideContext(presentation, index, { catalogs, fonts })` (package root) applies these rules with the slide, deck and theme precedence above. It returns the `ComposeSlideOptions` for one slide (canvas, layout, families as `fontFamilies`, measurement, `slideNumber`, `slideCount`), the slide to compose and draw (`slide`, with `{{slide.number}}`, `{{slide.section}}` and `{{deck.slideCount}}` substituted for those numbers), the diagnostics, and `darkBackground` (the preview\'s decision: the one colour the background names, resolved through the colour scheme and the deck\'s `variables` so `var:brand`, a scheme slot or `primary`/`secondary`/`accent` work, has a WCAG relative luminance below 0.179; a gradient counts as light, a picture reads `light1`, a pattern its `backgroundColor`; no opacity is applied), which `composeSlide` uses to pick logo variants. `resolveDesignRecords` and `resolveFontScheme` expose the same resolution for hosts that need only the records. Core pagination continues with the fallbacks and passes each diagnostic to `onDiagnostic`.\n\n| Engine | Diagnostic channel | Reported |\n| --- | --- | --- |\n| Core pagination | `paginate(..., { onDiagnostic })` | once per path per call |\n| opf-render preview | `toSvg` `onDiagnostic` | once per path per rendered slide |\n| opf-editor | `session.composeSlide` / `paginateSlide` `onDiagnostic` option | once per call |\n| opf-pptx export | `toPptx(..., { onDiagnostic })` | once per path per export |\n\nEvery engine passes the catalogs its host registered (the `catalogs` option) to core, so a record a host registers resolves the same in preview, editor and export.\n\n## Color references in content\n\nContent color fields (`TextRun.color`, styled table cell `style.fill` / `style.color`, table cell border `color`) accept references as well as literal hex, and those references resolve through the same chain above:\n\n```\n "color": "accent2" slot name -> effective color scheme slot\n "color": "text" role name -> role-to-slot mapping, then the slot\n "color": "var:risk" variable -> top-level variables map\n "color": "#B42318" literal -> used as-is (frozen at authoring time)\n```\n\n- **Slot names** (`accent1`\u2013`accent6`, `dark1`, `dark2`, `light1`, `light2`, `hyperlink`, `followedHyperlink`) read the named slot from the *effective* color scheme \u2014 the one produced by the slide \u2192 deck \u2192 theme \u2192 engine-default precedence at the top of this page. A slide-level `design.colorScheme` override therefore recolors that slide\'s named runs too.\n- **Role names** (`primary`, `secondary`, `accent`, `background`, `surface`, `text`, `textSecondary`) resolve through one shared definition, `resolveColorRoles()`, which the opf-render preview, the PPTX export and `validate` all call, so the same deck draws the same colors in each. A role defined on the effective scheme wins; otherwise it defaults from a slot:\n\n | Role | Default |\n | --- | --- |\n | `primary`, `secondary`, `accent` | `accent1`, `accent2`, `accent3` |\n | `background` | the slide\'s own resolved background when it is one color (solid, theme slot or pattern background color); else the scheme\'s `background` role, else `light1`. A gradient or picture background uses the scheme default. |\n | `surface` | `light2`, or `dark2` on a dark slide |\n | `text` | `dark1`, or `light1` on a dark slide. A `text` override applies on a light slide only, so a dark slide always keeps readable light text. |\n | `textSecondary` | `dark2`, or `light2` on a dark slide |\n\n A slide is dark when its background\'s WCAG relative luminance is under 0.179, the point where white and black text contrast equally.\n- **Links.** A link run (`link`) with no `color` of its own is drawn underlined in the scheme\'s `hyperlink` slot (the OOXML `hlink` color; Office blue `#0563C1` when the scheme sets none), in the preview and in the PPTX export (`a:schemeClr hlink` where the deck theme holds that color). Where that color has under 4.5:1 contrast against the slide background (the default Office blue on a dark slide), the slide\'s `text` color is used instead, as the slide tag does for a low-contrast primary color. A link run with its own `color` keeps it, and a `hyperlink` ColorRef always names the slot. `followedHyperlink` is written to the PPTX theme; a static preview cannot know which links were visited, so it draws every link in `hyperlink`.\n- **Variable references** (`var:<id>`) resolve against the document\'s top-level `variables` map, independent of the scheme. Variables are deck-scoped named colors \u2014 use them for values that have meaning (`var:risk`) or repeat across slides. An unknown id is a validation warning, never an error, and engines fall back to their default text color.\n\nThe styled table cell and border color fields enforce the reference forms at the schema level (a typo like `"acent2"` is a schema error there \u2014 neither hex, a known name, nor a `var:` reference). Run colors stay open strings so imported decks keep validating: an unrecognized run color is a validation warning, and renderers fall back to the theme text color \u2014 the same warn-don\'t-error posture unknown catalog ids get. Unknown `var:` ids are warnings everywhere.\n\n### Background colors are ColorRefs\n\nThe colors of a solid background (`SolidBackground.color`), of each gradient stop and of a pattern (`foregroundColor`, `backgroundColor`) take the same forms as the content fields above: a literal hex, a slot or role name, or a `var:<id>` reference. The schema keeps these fields as plain strings, so a reference validates; engines resolve it through `resolveColorRef()` against the effective color scheme and the deck `variables`, and a reference that resolves to nothing is drawn as the engine default (white for a background), as for any other unresolvable color. The default text color of the slide follows the resolved background. A PPTX export writes `a:schemeClr` where the deck theme holds the named slot or role exactly, and the resolved literal otherwise (FF-24 conventions).\n\n> **Decision, 2026-09-30 (agent decision, vetoable).** The spec coverage audit found that `SolidBackground.color` accepted `var:` and slot names (the field is a string) but both engines painted white, while [`content-item-design-overrides.md`](./content-item-design-overrides.md) already states that every color field must accept the ColorRef forms and never hex alone. Rather than tighten the schema (which would break documents that validate today), the engines resolve ColorRefs in backgrounds. The owner can veto this by restricting the three background color fields to `HexColor` in the schema and the engines to hex only.\n\n`@openpresentation/opf/composition` exports `resolveColorRef()` with the shared slot, role, variable, and hex rules above so renderers and exporters do not drift. Pass the effective color scheme, optional resolved role colors, the deck `variables` map, and a theme-text `fallback` for unrecognized references.\n\n## Script fonts and language\n\nOOXML gives each theme font (major and minor) three script slots: `latin`, East Asian (`ea`) and complex script (`cs`). The presentation `language` and the effective font scheme resolve to all three:\n\n```\n latin design font scheme heading/body (the chain above)\n eastAsian 1. design.fontScheme.eastAsian explicit slot\n complexScript 2. the scheme\'s own major/minor when languageFamily is ea / cs (or eastAsian /\n complexScript, the same values) and its\n languages list (language ids) is empty or\n names the language\n 3. the language\'s font scheme when the language\'s script uses the slot\n 4. the latin family otherwise\n```\n\n- A language record\'s `script` (ISO 15924) picks its slot. East Asian scripts (`Jpan`, `Hans`, `Hant`, `Kore`, ...) use `eastAsian`. Complex scripts (`Arab`, `Hebr`, `Deva`, `Thai`, ...) use `complexScript`. Latin, Cyrillic, Greek and other scripts use `latin`. `direction` defaults from the script (Arabic and Hebrew are right-to-left).\n- The language\'s `fontScheme` applies to PowerPoint output and `googleFontScheme` to Google Slides output. For Latin-script languages, the design font scheme always supplies the latin slot.\n- A language sets `lang`, the text direction and the script slots, and never the Latin scheme: only `design.fontScheme` (slide, then deck, then theme, then the shared default `aptos`) sets the latin fonts, and a language record\'s `fontScheme` is a default for its own script slot, not a deck font. The PPTX theme\'s `a:ea` and `a:cs` are written only for a slot a script font is selected for (the scheme\'s explicit slot or the language\'s script font) and stay empty otherwise, as in Office\'s own themes. See [Language contract](./programs/font-fidelity-everywhere/script-font-model.md#language-contract-ff-50-model-c) and [Theme slots](./programs/font-fidelity-everywhere/script-font-model.md#theme-slots-ff-49).\n- A Latin deck therefore repeats its heading/body family in `ea`/`cs`. A Japanese deck with `design.fontScheme: { "major": "Carlito", "minor": "Carlito" }` keeps the Latin family in `latin` and uses Meiryo (PowerPoint) or Noto Sans JP (Google Slides) in `ea`. `design.fontScheme.eastAsian` / `.complexScript` (`{ "major": ..., "minor": ... }`) name a script font explicitly, for example for CJK text inside a Latin deck.\n\n`@openpresentation/opf/composition` exports `resolveScriptFonts(document, { app, slideIndex })` (`app` is `"powerpoint"`, the default, or `"google-slides"`), and the package root exports `normalizeLanguageFamily(value)` (which reads a font scheme\'s `languageFamily`, `eastAsian` as `ea` and `complexScript` as `cs`). `resolveScriptFonts` returns the heading and body slots, the OOXML `lang` (a curated `ooxmlLang` culture tag such as `ja-JP` or `ms-MY`, or an authored region tag), the canonical `bcp47` tag, `script`, `direction`/`rtl`, and the per-script supplemental theme font. Renderers and exporters should use it rather than re-deriving slots. The model, the OOXML mapping and the open questions are in [`programs/font-fidelity-everywhere/script-font-model.md`](./programs/font-fidelity-everywhere/script-font-model.md). opf-render and opf-pptx implement it (FF-07, FF-19, FF-49).\n\n## Brand assets and layout hints\n\nThe 2026-09-30 spec coverage audit found that the deck logo, `organization.logo`, `speaker.photo`, `design.contentDirection`, `design.chartPrimary`, `design.listBullet` and `fontScheme.accent` validated, edited and round-tripped but changed nothing in any engine. This section states what they do now. Every rule below is implemented once, in `composeSlide()` and `resolveLogo()` of `@openpresentation/opf`, and consumed by the renderer and the exporter; the decisions marked **(vetoable)** are agent decisions the owner can overturn.\n\n### Layout templates and design\n\nAn OPF 0.19 layout record (`opf-layout/v2`) describes grid `areas`, relative `columns` and `rows`, and one `regions` entry for each body area. Regions declare accepted content kinds, role, flow, capacity and optional bleed. `layoutContent(record)` and `layoutStructure(record)` summarize those regions for hosts; `composeSlide()` reports their binding and boxes in `geometry.regions`. A slide may use `layout: "auto"` to bypass a record explicitly. [Layout templates](layout-templates.md) documents the binding and flow rules.\n\nThe effective `design` value of each key comes from the slide, then the deck, then the resolved theme, then the layout record, then the engine default. This includes `titleAlignment`, `contentAlignment`, `contentBox`, `contentDirection`, `chartPrimary`, `imageFit`, `listBullet` and `mirror`. `resolveDesignHints()` reports the supplying level, and `SlideComposition.design` holds the merged result. A layout\'s grid determines placement; `design.mirror` reverses its tracks, while the slide\'s own `composition` arranges the first primary flowing region. A top-level image block may still carry its own `placement` band. A layout sits on any slide background; it does not opt into a picture.\n\nThe `tag` (eyebrow) follows `titleAlignment` on every slide, and is left-aligned when none resolves, like the title, so a layout that centres its content does not centre the tag over a left-aligned title. On a cover the `subtitle` joins them. Only the slide\'s own `design.contentAlignment` keeps the tag (and a cover\'s subtitle) on content alignment, preserving the 2026-10-06 authoring decision.\n\n### Logos live on the organization (RR-71, OPF 0.18)\n\nA logo belongs to an organization. `Organization.logo` is one path or Asset for every shape, or up to four shapes:\n\n```json\n"organization": {\n "id": "acme", "name": "Acme",\n "logo": {\n "full": { "onLight": "./assets/acme-logo.svg", "onDark": "./assets/acme-logo-white.svg" },\n "icon": "./assets/acme-mark.svg"\n }\n}\n```\n\n`resolveLogo(presentation, slide, { shape, onDark, slideIndex, reference })` (`@openpresentation/opf/composition`) returns `{ source, path, shape, variant, reference, designPath? }` or `null`:\n\n```\n organization 1. options.reference var:organization.beta.logo.icon\n 2. slides[i].design.logo, then design.logo:\n false draws no logo; a reference picks the organization\n (and the shape, when it names one)\n 3. the primary organization role "primary", else the first\n shape the reference\'s shape, else options.shape (default full)\n missing shape -> full -> first of wordmark, stacked, icon\n variant onDark on a dark background, else onLight; a missing one uses\n the other; a plain asset is "default"\n path organization.logo, organization.1.logo.icon,\n organization.logo.full.onDark, ...\n```\n\nAn override that names an organization without a logo draws nothing: it never falls back to another organization. Hosts pass their own background luminance test as `composeSlide(..., { darkBackground })` (`resolveSlideContext` computes it); core never inspects colors.\n\nThe same logos are placed anywhere through slide-scoped built-in references (`var:organization.logo`, `var:organization.logo.icon`, `var:organization.beta.logo.wordmark`), which resolve for each slide\'s background: see [templates and variables](templates-and-variables.md#organization-logos).\n\n### Where the logo is drawn (vetoable)\n\n1. **Cover and section slides.** A slide with no body payload on a title-only template, after empty template regions collapse, or without a layout (the same rule that centers covers) draws the primary organization\'s `full` logo (or the `design.logo` override) at the top-left of the free area, inside the slide padding and below any header furniture. `composeSlide` returns it as `geometry.logo` (`{ box, shape, path, source, variant, reference, anchor: \'left\' }`): `x = area.left + padding`, `y` at the image-safe heading top, `height = 56` reference pixels at a 720-pixel short edge, `width = min(4 * height, free width)`. Headings start one gap below the box and the cover-centering rule centers the tag/title/subtitle group in the remaining span; the logo itself does not move. Consumers fit the image inside the box preserving its aspect ratio, anchored left and vertically centered (SVG `preserveAspectRatio="xMinYMid meet"`; PPTX computes the fitted size from the raster dimensions and places it at `box.x`). Nothing is drawn when no logo resolves or `design.logo` is `false`. **Content slides never get an automatic logo** (vetoable: it would move every content area).\n2. **Headers and footers.** A zone places a logo like any picture: `"image": "var:organization.logo.icon"`. `layoutFurniture` resolves the reference for the slide\'s background; the image part carries the asset in `image`, the organization path in `sourcePath` and the reference in `reference`. Without a logo the engine reports `unresolved-content` at `<zone>.image`. The zone\'s parts sit in one row (see [dynamic composition](dynamic-composition.md)).\n3. **Picture bullets.** See `listBullet` below: the `icon` shape.\n\n> **Decision, 2026-09-30 (agent decision, vetoable).** 84 of the 126 bundled example decks carry a logo, so 81 cover slides gain a logo and their heading group moves down. The placement (top-left, 56 px, full logo) and the content-slide exclusion are the reference-engine defaults; a layout-driven logo slot is a separate design.\n\n### `speaker.photo` is a built-in image variable (vetoable)\n\nNo reference engine draws a speaker photo by itself: the schema has no speaker slot on any slide and no slide-to-speaker link, and a speaker block on covers would be a separate design. The author places it explicitly with the built-in image variable (`"image": "var:speaker.photo"`, see [templates and variables](templates-and-variables.md#built-in-variables)); the field also round-trips through PPTX provenance.\n\n### `contentDirection`\n\nThe effective value follows the normal design precedence: slide, deck, theme, layout, engine default. On automatic slides `vertical` sets the root arrangement to `column` and `horizontal` to `row`, unless the slide explicitly sets `composition.mode`. Promoted regions keep their geometry and nested content groups keep their own composition.\n\nOn a v2 template, `contentDirection` changes every flowing region into a row or column without changing the template\'s areas. An atomic `flow: "none"` region remains atomic. Without the direction hint each region uses its declared flow. Layout records do not have `composition.mode`, `columns` or `weights`; their areas, tracks and regions define the grid.\n\n### `titleAlignment` and `contentAlignment` in right-to-left decks\n\n> **Decision, 2026-10-01 (RR-05, supervisor decision, vetoable).** Alignment is logical for right-to-left text. In a deck whose language is written right to left, `left` means the start edge and `right` the end edge of each paragraph: an Arabic or Hebrew paragraph with the default (or authored) `left` is drawn against the right edge, with its bullets, indents and table cells to match, while an English paragraph in the same deck keeps the left edge. `center` is unchanged and a left-to-right deck is unchanged. The effective value (slide design, then deck design, then `left`) is still what `item.alignment` reports; `placement.lines[i].alignment` and the exported `algn` carry the physical edge. A right-to-left deck that wants a paragraph at its end edge sets `right`. See [Layout direction](programs/font-fidelity-everywhere/script-font-model.md#layout-direction-rr-05).\n\n### `chartPrimary`\n\nThe resolved `design.chartPrimary` applies on automatic slides only. It changes the root arrangement when the slide has no promoted regions, no `composition.mode` of its own, and its root nodes contain at least one chart leaf and at least one node that is not a chart. The **first chart node is primary** and the other root nodes form one synthetic sub-grid:\n\n- `left` / `right`: the root is a two-track row with weights `[3, 2]` (chart first for `left`, last for `right`); the rest arrange in `auto` mode inside their track.\n- `top` / `bottom`: a two-track column with weights `[3, 2]` (chart first for `top`).\n- `none`, or any other value: no change, the existing automatic grid with equal weight.\n\nAutomatic composition builds the primary chart track and a direct content group for the other root nodes, preserving the 3:2 chart weight and source content order. A v2 template remains authoritative and does not apply this automatic hint. OPF 0.19 removes the v1 record-construction helper `chartPrimaryLayout`; authors use `chart-beside` or a custom v2 template for an authored grid.\n\nThe synthetic container has no OPF path, so it records no `groups`, `flows` or explanation entry; the root decision has `reason: \'chart-primary\'` and `selectedColumns` 2 (row) or 1 (column). Explicit root `columns` and `weights` are ignored while the automatic hint applies. A chart inside a nested group, a chart-only root, or a single root `chart` payload leaves the arrangement unchanged. A slide using a v2 template follows that template instead.\n\n### `listBullet` (vetoable)\n\n`character` (the default) draws the current glyph marker. `image` draws the organization\'s icon logo (`resolveLogo(..., { shape: \'icon\', onDark })`: the primary organization\'s, or the organization and shape `design.logo` names) as a picture bullet: every `items`/`bullets` item in `composeSlide` and every `listEntries[]` entry of its fit carry `bulletImage: { source, path }`. Marker geometry is unchanged, and each entry carries `bulletBox`, where the image draws: a square of side `marker.fontSize * PICTURE_BULLET_SCALE` (0.65, exported) whose bottom sits on the marker baseline (`marker.y`) with its left edge at `marker.x`. The scale is what desktop PowerPoint draws for an `a:buBlip` at `a:buSzPct 100000` (what the exporter writes), measured as a square 10, 15, 16, 20 and 31 px wide at font sizes of 16, 24, 25, 32 and 48 px (0.625 to 0.646; the heights run a pixel more from anti-aliasing) with its bottom on the text baseline, in Arial, Aptos, Georgia and Courier New alike, so it does not depend on the typeface; the text start and hanging indent are identical in both. The exporter sets no size, because PowerPoint sizes the bullet itself; consumers that draw it themselves (the preview) use `bulletBox`. Vetoable: the constant is a measurement, not a rule of the format. When `image` is set and no logo resolves, the glyph stays and the slide reports one `unresolved-content` diagnostic at `slides.N.design.listBullet` or `design.listBullet`, only when the slide has a list. The renderer draws an `<image>` per marker; the exporter writes native picture bullets (`a:buBlip`).\n\n### `fontScheme.accent`\n\n`resolveFontFamilies()` returns `accent` only when the effective scheme defines an `accent` role (a family string or a `Font` object). The slide `tag` (eyebrow) and the quote body use `fonts.accent ?? <current family>` (body for the tag, heading for the quote body); nothing else changes. The renderer loads and embeds it, the exporter writes it on those runs (`a:latin`) while the theme fonts stay major/minor, and import keeps restoring `design.fontScheme` from provenance. None of the bundled examples sets an accent font.\n\n## What is *not* part of this chain\n\nBeyond the color references above, content payloads carry no design controls in v1 \u2014 `position`, `fontSize` overrides at payload level, and the like were deliberately kept out while the content model stabilizes (see [`content-item-design-overrides.md`](./content-item-design-overrides.md); styled table cells and rich-text runs carry the only per-content styling, and their color fields take the reference forms above). The design system, plus layout hints (`titleAlignment`, `contentBox`, `chartPrimary`, ...) and dynamic composition, is the styling surface of an OPF document.\n'
73
+ "markdown": '# Design Resolution\n\nHow an engine decides the effective design for any given slide. The schema spreads these rules across field descriptions; this page states them once, as an algorithm.\n\n## Precedence\n\nFor every design field independently, the most specific source wins:\n\n```\n wins +--------------------------------------------------------+\n ^ | 1. slide design slides[i].design.* |\n | +--------------------------------------------------------+\n | | 2. deck design design.* on the presentation root |\n | +--------------------------------------------------------+\n | | 3. resolved theme colorScheme, fontScheme, |\n | | background, dimensions from the |\n | | theme record |\n | +--------------------------------------------------------+\n loses | 4. engine defaults e.g. spec/reference/ |\n v | engine-defaults.json |\n +--------------------------------------------------------+\n```\n\n1. **Slide design** \u2014 `slides[].design.*`\n2. **Deck design** \u2014 `design.*` on the presentation root\n3. **Resolved theme** \u2014 defaults carried by the theme record (`colorScheme`, `fontScheme`, `background`, `dimensions`)\n4. **Engine defaults** \u2014 [`spec/reference/engine-defaults.json`](../spec/reference/engine-defaults.json), compiled into core as `ENGINE_DEFAULT_THEME`, `ENGINE_DEFAULT_COLOR_SCHEME` and `ENGINE_DEFAULT_FONT_SCHEME`\n\nResolution is **per field**, not per object. A slide that sets only `design.contentAlignment` inherits everything else from the deck design; a deck that sets only `design.colorScheme` keeps the theme\'s font scheme and background.\n\nTwo field-level rules complete the picture:\n\n- **Base-plus-overrides within one object.** Wherever a reference object carries an `id` (`ColorScheme`, `FontScheme`, and the inline `Audience`, `Purpose` and `Tone`), the `id` resolves a catalog record as the base and sibling fields override the resolved record per key. `design.theme` is a reference string only. The string shorthand (`"colorScheme": "cool-horizon"`) is equivalent to setting only `id`.\n\n ```\n "colorScheme": { "id": "cool-horizon", "accent1": "#0F4C81" }\n\n catalog record "cool-horizon" sibling fields on the object\n accent1: "#2874A6" <-- replaced -- accent1: "#0F4C81"\n accent2: "#1B4F72" <-- kept\n light1: "#FFFFFF" <-- kept\n |\n v\n effective scheme: accent1 from the override, everything else\n from the record\n ```\n- **Explicit suppression.** `watermark`, `header`, and `footer` accept `false` to switch off an inherited value \u2014 distinct from omitting the field, which inherits.\n\nEvery reference inside this chain resolves the one way every content reference does: `catalogs.custom`, then the records embedded under `catalogs.default`, then the catalog the host registered for its source (`name:id` in `catalogs.<name>`, then the host catalog for its source); see [`how-opf-works.md`](./how-opf-works.md) and [the catalogs page](default-catalog.md).\n\n## Worked example 1: color scheme through every level\n\n```json\n{\n "design": {\n "theme": "classic",\n "colorScheme": "forest-green"\n },\n "slides": [\n { "title": "Inherits the deck" },\n {\n "title": "Slide override",\n "design": {\n "colorScheme": { "id": "cool-horizon", "accent1": "#0F4C81" }\n }\n }\n ]\n}\n```\n\n- Slide 1: the `classic` theme record supplies its own default color scheme, but the deck design sets `colorScheme` explicitly, so `forest-green` wins (level 2 beats level 3). Fonts, background, and dimensions still come from `classic`.\n- Slide 2: slide design beats deck design (level 1 beats level 2). The `cool-horizon` record resolves as the base, then `accent1` is replaced by `#0F4C81`. All other `cool-horizon` slots survive.\n\nThere is no ambiguity between "override" and "reference": every scheme value *is* a reference, and any sibling fields on the same object are overrides applied after the reference resolves.\n\n## Worked example 2: backgrounds and suppression\n\n```json\n{\n "design": {\n "theme": "dark",\n "background": "light1",\n "footer": {\n "left": { "text": "Acme Corp" },\n "right": { "text": "{{slide.number}}" }\n }\n },\n "slides": [\n { "title": "Light slide in a dark theme" },\n {\n "title": "Section divider",\n "design": {\n "background": {\n "type": "gradient",\n "gradient": {\n "angle": 90,\n "stops": [\n { "color": "#0B1B2B", "position": 0 },\n { "color": "#123A5F", "position": 1 }\n ]\n }\n },\n "footer": false\n }\n }\n ]\n}\n```\n\n- Slide 1: the deck-level `background: "light1"` overrides the `dark` theme\'s default background. `light1` is a theme slot \u2014 it resolves through the effective color scheme, which itself resolved through the chain above.\n- Slide 2: the gradient replaces the deck background for this slide only, and `footer: false` suppresses the inherited footer rather than inheriting or replacing it.\n\n## Worked example 3: font scheme models\n\n```json\n{\n "design": {\n "fontScheme": {\n "id": "aptos",\n "code": "JetBrains Mono"\n }\n }\n}\n```\n\nThe `aptos` record supplies the OOXML pair (`major`/`minor`). The `code` role is an OPF-specific addition with no OOXML slot, so it layers on top without disturbing the pair. When serializing to PowerPoint, engines write `major`/`minor` to `majorFont`/`minorFont` and map abstract roles (`heading`, `body`) onto those slots. `accent` has no slot; the `code` family is written directly on code runs. The same slot-versus-role split applies to color schemes: OOXML slots (`accent1`\u2013`accent6`, `dark1/2`, `light1/2`) round-trip directly, abstract roles (`primary`, `text`, `surface`, \u2026) are mapped onto slots by the engine.\n\n### Code font\n\nThe `code` role resolves per key like every other override:\n\n1. `code` on the effective `design.fontScheme` object;\n2. `code` on the resolved font-scheme record (the `consolas` and `courier-new` records carry `"Consolas"` and `"Courier New"`);\n3. otherwise **Roboto Mono**, the documented fallback that `@openpresentation/opf-render` bundles.\n\nThe heading and body families are never reused as the code fallback, so choosing `aptos` still gives Roboto Mono code unless the deck sets `code`. `resolveFontFamilies()` in `@openpresentation/opf/composition` applies these rules for all engines.\n\n### Engine default font scheme\n\nThe last-resort font scheme applies only when neither the slide, the deck nor the resolved theme names one, or when the reference resolves nowhere. It is an engine default in code, not a catalog record: `ENGINE_DEFAULT_FONT_SCHEME` (`{ major: "Aptos Display", minor: "Aptos", languageFamily: "latin" }`, the drawing fields of the gallery\'s `aptos` scheme), recorded in [`engine-defaults.json`](../spec/reference/engine-defaults.json) and exported from the package root and `@openpresentation/opf/composition`. A document with no `design` uses the engine default theme (`ENGINE_DEFAULT_THEME`, which names no font scheme), so it gets Aptos in every engine without any catalog registered.\n\nEvery engine shares this one last resort, so a theme without `fontScheme` is measured, paginated, previewed and exported in the same fonts:\n\n| Engine | Last resort | Where |\n| --- | --- | --- |\n| Core pagination and validate | `ENGINE_DEFAULT_FONT_SCHEME` | `packages/javascript/src/design-records.ts` |\n| opf-render preview | core\'s `resolveSlideContext` | `src/svg.js` |\n| opf-editor composition and slide transfer | core\'s `resolveSlideContext` | `src/font-defaults.js` |\n| opf-pptx export | core\'s `resolveSlideContext` | `src/index.js` |\n\nAptos is not openly licensed, so no OPF package bundles it. Previews take the same path for the last resort as for any `aptos` deck:\n\n- **Estimated layout** (no `fonts`): the SVG names `Aptos` and `Aptos Display`, and the default raster engine draws them with its bundled sans-serif fallback (Roboto).\n- **Measured layout** with the opf-render office font pack (`loadFonts({pack: \'office\'})`): `Aptos` and `Aptos Display` resolve to Intos and Intos Display, metric-compatible replacements from the OPF font policy (0.000% mean width difference against Aptos 2.01), and the substitution report lists both. With only the base pack and `substitutionPolicy: "visual"` they fall back to the visual alternates Roboto and Carlito. The PPTX always names Aptos. See [font-fidelity.md](font-fidelity.md#font-policy-ff-31).\n- **Measured layout with only the base pack under the metric policy**: `font-unavailable` for Aptos, as for a document with no `design`. Supply licensed Aptos faces, allow visual substitution, or set a `fallbackFamily`.\n\n`packages/javascript/test/font-scheme-defaults.test.mjs` checks the shared default in core pagination, and each sibling repository has a parity test.\n\n### References that resolve nowhere\n\nA layout, theme, colour scheme or font scheme reference that neither the document embeds nor a registered catalog defines falls back, and no engine throws for it (unless the host asks for a strict export, `strictReferences`, which throws `OPFUnresolvedReferenceError`):\n\n- **Layout.** A slide with no `layout` is composed with **no layout record**, and that is not a finding: title, subtitle, tag and content are arranged automatically, a slide with no body payload is a cover (the title block vertically centred) and the design hints apply. A layout reference that resolves nowhere composes the same way. An engine never substitutes a different layout.\n- **Theme.** A theme reference that resolves nowhere uses the engine default theme (`ENGINE_DEFAULT_THEME`: the background and dimensions of the gallery\'s `minimal`).\n- **Colour scheme.** A colour-scheme reference that resolves nowhere uses `ENGINE_DEFAULT_COLOR_SCHEME` (the twelve slots of `cool-horizon`). The theme\'s own colour scheme counts as the reference when the deck and slide name none, and resolves in the theme\'s catalog group first. An object without an `id` is laid over the engine default, so a partial inline scheme keeps the slots it does not name.\n- **Font scheme.** A font-scheme reference that resolves nowhere uses `ENGINE_DEFAULT_FONT_SCHEME`. Sibling fields on an object reference still override it per key, so `{ "id": "no-such-scheme", "major": "Inter", "minor": "Inter" }` uses Inter, and a `code` role still applies. An object without `id` is an inline scheme on the same base and reports nothing.\n\nEach is one `unresolved-reference` diagnostic: `{ code, kind, reference, path, group, source?, fallback, message }`, with `path` where the reference is written (`slides.N.layout`, `slides.N.design.theme`, `design.colorScheme`, `design.fontScheme.id`, or `catalogs.<group>.themes.<id>.colorScheme` for a reference inside an embedded theme), `fallback` `automatic` for a layout and `engine-default` otherwise, and a message that names the reference and the catalog source it was looked for in. `validate` reports the same references as `opf/unresolved-reference` warnings.\n\n`resolveSlideContext(presentation, index, { catalogs, fonts })` (package root) applies these rules with the slide, deck and theme precedence above. It returns the `ComposeSlideOptions` for one slide (canvas, layout, families as `fontFamilies`, measurement, `slideNumber`, `slideCount`), the slide to compose and draw (`slide`, with `{{slide.number}}`, `{{slide.section}}` and `{{deck.slideCount}}` substituted for those numbers), the diagnostics, and `darkBackground` (the preview\'s decision: the one colour the background names, resolved through the colour scheme and the deck\'s `variables` so `var:brand`, a scheme slot or `primary`/`secondary`/`accent` work, has a WCAG relative luminance below 0.179; a gradient counts as light, a picture reads `light1`, a pattern its `backgroundColor`; no opacity is applied), which `composeSlide` uses to pick logo variants. `resolveDesignRecords` and `resolveFontScheme` expose the same resolution for hosts that need only the records. Core pagination continues with the fallbacks and passes each diagnostic to `onDiagnostic`.\n\n| Engine | Diagnostic channel | Reported |\n| --- | --- | --- |\n| Core pagination | `paginate(..., { onDiagnostic })` | once per path per call |\n| opf-render preview | `toSvg` `onDiagnostic` | once per path per rendered slide |\n| opf-editor | `session.composeSlide` / `paginateSlide` `onDiagnostic` option | once per call |\n| opf-pptx export | `toPptx(..., { onDiagnostic })` | once per path per export |\n\nEvery engine passes the catalogs its host registered (the `catalogs` option) to core, so a record a host registers resolves the same in preview, editor and export.\n\n## Color references in content\n\nContent color fields (`TextRun.color`, styled table cell `style.fill` / `style.color`, table cell border `color`) accept references as well as literal hex, and those references resolve through the same chain above:\n\n```\n "color": "accent2" slot name -> effective color scheme slot\n "color": "text" role name -> role-to-slot mapping, then the slot\n "color": "var:risk" variable -> top-level variables map\n "color": "#B42318" literal -> used as-is (frozen at authoring time)\n```\n\n- **Slot names** (`accent1`\u2013`accent6`, `dark1`, `dark2`, `light1`, `light2`, `hyperlink`, `followedHyperlink`) read the named slot from the *effective* color scheme \u2014 the one produced by the slide \u2192 deck \u2192 theme \u2192 engine-default precedence at the top of this page. A slide-level `design.colorScheme` override therefore recolors that slide\'s named runs too.\n- **Role names** (`primary`, `secondary`, `accent`, `background`, `surface`, `text`, `textSecondary`) resolve through one shared definition, `resolveColorRoles()`, which the opf-render preview, the PPTX export and `validate` all call, so the same deck draws the same colors in each. A role defined on the effective scheme wins; otherwise it defaults from a slot:\n\n | Role | Default (through the slide\'s variant; see [Color variants](#color-variants)) |\n | --- | --- |\n | `primary`, `secondary`, `accent` | the first three accents of the variant: `accent1`, `accent2`, `accent3` unless `variant.accents` reorders them |\n | `background` | the slide\'s own resolved background when it is one color (solid or pattern background color); else the variant\'s `bg1` (`light1` by default, the scheme\'s `background` role overriding it there). A gradient or picture background uses the variant, or its `tone`, or `light1`. |\n | `surface` | `bg2` of the variant: `light2` on a light slide, `dark2` on a dark one |\n | `text` | `tx1` of the variant: `dark1`, or `light1` on a dark slide. A `text` override applies on a light slide only, so a dark slide always keeps readable light text. |\n | `textSecondary` | `tx2` of the variant: `dark2`, or `light2` on a dark slide |\n\n A slide is dark when its background\'s WCAG relative luminance is under 0.179, the point where white and black text contrast equally.\n- **Links.** A link run (`link`) with no `color` of its own is drawn underlined in the scheme\'s `hyperlink` slot (the OOXML `hlink` color; Office blue `#0563C1` when the scheme sets none), in the preview and in the PPTX export (`a:schemeClr hlink` where the deck theme holds that color). Where that color has under 4.5:1 contrast against the slide background (the default Office blue on a dark slide), the slide\'s `text` color is used instead, as the slide tag does for a low-contrast primary color. A link run with its own `color` keeps it, and a `hyperlink` ColorRef always names the slot. `followedHyperlink` is written to the PPTX theme; a static preview cannot know which links were visited, so it draws every link in `hyperlink`.\n- **Variable references** (`var:<id>`) resolve against the document\'s top-level `variables` map, independent of the scheme. Variables are deck-scoped named colors \u2014 use them for values that have meaning (`var:risk`) or repeat across slides. An unknown id is a validation warning, never an error, and engines fall back to their default text color.\n\nThe styled table cell and border color fields enforce the reference forms at the schema level (a typo like `"acent2"` is a schema error there \u2014 neither hex, a known name, nor a `var:` reference). Run colors stay open strings so imported decks keep validating: an unrecognized run color is a validation warning, and renderers fall back to the theme text color \u2014 the same warn-don\'t-error posture unknown catalog ids get. Unknown `var:` ids are warnings everywhere.\n\n### Color variants\n\nA deck has one color scheme of twelve slots. A **variant** is a mapping over those same slots, as PowerPoint\'s `clrMap` is, never a second scheme: which slot is the background, and so which are the text and the alternate surface, plus an optional order for the accents. Everything OPF draws refers to the roles, so a slide that changes variant recolors text, lines, cards, chart furniture and code together.\n\n`variant` is a string, the same way `colorScheme` is: the slot that is the background.\n\n```json\n{\n "design": { "variant": "dark1" },\n "slides": [{ "title": "Light one", "design": { "variant": "light1" } }]\n}\n```\n\n`"dark1"` is `{ "background": "dark1" }`. The object form is only needed to reorder the accents: `{ "background": "light1", "accents": ["accent2", "accent1", "accent3", "accent4", "accent5", "accent6"] }`. Both forms are allowed on `design` and on `slides[n].design`, and what `resolveColorRoles` and `resolveSlideContext` return is always the normalized object (`normalizeColorVariant`).\n\n| `variant` (or `variant.background`) | bg1 | tx1 | bg2 | tx2 |\n| --- | --- | --- | --- | --- |\n| `light1` (default) | `light1` | `dark1` | `light2` | `dark2` |\n| `dark1` | `dark1` | `light1` | `dark2` | `light2` |\n| `light2` | `light2` | `dark1` | `light2` | `dark2` |\n| `dark2` | `dark2` | `light1` | `dark2` | `light2` |\n\n(`light2` and `dark2` keep the background slot as the surface too, as a slide with that theme background has always been drawn, so the engine default theme, whose background is `dark2`, is unchanged.) `variant.accents` (object form) is a permutation of `accent1` to `accent6`; entry `i` is the slot drawn as accent `i + 1`, which reorders `primary`, `secondary`, `accent` and the chart series colors.\n\nWhich variant a slide has, from the most specific to the least (slide design, deck design, then the theme record), at the first level that says anything:\n\n1. a `variant`;\n2. a theme-slot background (`"dark1"`, `{ "type": "theme", "slot": "dark1" }`), which is the variant of that slot, so `design.background` stays a shorthand;\n3. a picture or gradient background with a `tone` (`"dark"` or `"light"`), which is the `dark1` or `light1` variant. Without a tone only a variant of a less specific level can apply; with none the slide counts as light, as before;\n4. any other background (a solid or pattern color) decides by luminance (dark below 0.179), as before.\n\nA slide\'s variant replaces the deck\'s whole, accent order included. `resolveSlideContext(...).resolved.roles` is the resolved record, and `resolveColorRoles(scheme, { variant, tone, background })` is the function: besides the colors it returns `variant`, `map` (bg1, tx1, bg2, tx2 and the accents; `clrMapAttributes(map)` gives the PowerPoint `clrMap` attributes for the master and for a slide\'s `clrMapOvr`), `accents` (the six colors in order) and `neutrals`.\n\n#### Scheme-derived neutrals\n\nLines and quiet colors are not fixed grey. `roles.neutrals` derives each from the map, as PowerPoint\'s chart styles do: the text color (tx1) at a share over the background (bg1), chosen as a DrawingML luminance transform of the slot so a PPTX can write `<a:schemeClr val="tx1"><a:lumMod val="15000"/><a:lumOff val="85000"/></a:schemeClr>` and the preview draws the same color. Each neutral is `{ slot, hex, lumMod?, lumOff?, alpha? }` (the transforms in thousandths of a percent; `hex` is the transform applied, `applyLuminance(slotHex, lumMod, lumOff)`).\n\n| Neutral | Derivation |\n| --- | --- |\n| `border` (also `roles.border`) | tx1 at 20% over bg1: card, rule and table borders |\n| `gridline` | tx1 at 15%: chart gridlines |\n| `axisLine` | tx1 at 25%: chart axis lines |\n| `chartText` | tx1 at 65%: chart axis labels and legend text |\n| `outline` | bg1: pie and treemap outlines, the video play icon, anything drawn on a mark |\n| `trendNeutral` | tx2: the neutral metric trend |\n| `codePanel` | tx1 on a light map, bg2 on a dark one: the code block is a dark card on either |\n| `codeText` | bg1 or tx1, whichever reads better on the panel |\n| `codeComment` | the code text at 65% over the panel (comments and line numbers) |\n\n`codeSyntaxPaletteForRoles(roles)` builds the syntax palette from `codePanel`, `codeText`, `codeComment` and the variant\'s first three accents, and `codeHighlightColors(scheme, palette, codePanel.hex)` takes the panel the highlight band sits on. `readableSchemeColor(fill, roles)` returns bg1 or tx1 (as a scheme color), whichever reads on a fill the scheme does not name, for table header text and text on marks. The validator\'s contrast rules read the same roles, so a dark variant is checked against its own text and surface.\n\n### Background colors are ColorRefs\n\nThe colors of a solid background (`SolidBackground.color`), of each gradient stop and of a pattern (`foregroundColor`, `backgroundColor`) take the same forms as the content fields above: a literal hex, a slot or role name, or a `var:<id>` reference. The schema keeps these fields as plain strings, so a reference validates; engines resolve it through `resolveColorRef()` against the effective color scheme and the deck `variables`, and a reference that resolves to nothing is drawn as the engine default (white for a background), as for any other unresolvable color. The default text color of the slide follows the resolved background. A PPTX export writes `a:schemeClr` where the deck theme holds the named slot or role exactly, and the resolved literal otherwise (FF-24 conventions).\n\n> **Decision, 2026-09-30 (agent decision, vetoable).** The spec coverage audit found that `SolidBackground.color` accepted `var:` and slot names (the field is a string) but both engines painted white, while [`content-item-design-overrides.md`](./content-item-design-overrides.md) already states that every color field must accept the ColorRef forms and never hex alone. Rather than tighten the schema (which would break documents that validate today), the engines resolve ColorRefs in backgrounds. The owner can veto this by restricting the three background color fields to `HexColor` in the schema and the engines to hex only.\n\n`@openpresentation/opf/composition` exports `resolveColorRef()` with the shared slot, role, variable, and hex rules above so renderers and exporters do not drift. Pass the effective color scheme, optional resolved role colors, the deck `variables` map, and a theme-text `fallback` for unrecognized references.\n\n## Script fonts and language\n\nOOXML gives each theme font (major and minor) three script slots: `latin`, East Asian (`ea`) and complex script (`cs`). The presentation `language` and the effective font scheme resolve to all three:\n\n```\n latin design font scheme heading/body (the chain above)\n eastAsian 1. design.fontScheme.eastAsian explicit slot\n complexScript 2. the scheme\'s own major/minor when languageFamily is ea / cs (or eastAsian /\n complexScript, the same values) and its\n languages list (language ids) is empty or\n names the language\n 3. the language\'s font scheme when the language\'s script uses the slot\n 4. the latin family otherwise\n```\n\n- A language record\'s `script` (ISO 15924) picks its slot. East Asian scripts (`Jpan`, `Hans`, `Hant`, `Kore`, ...) use `eastAsian`. Complex scripts (`Arab`, `Hebr`, `Deva`, `Thai`, ...) use `complexScript`. Latin, Cyrillic, Greek and other scripts use `latin`. `direction` defaults from the script (Arabic and Hebrew are right-to-left).\n- The language\'s `fontScheme` applies to PowerPoint output and `googleFontScheme` to Google Slides output. For Latin-script languages, the design font scheme always supplies the latin slot.\n- A language sets `lang`, the text direction and the script slots, and never the Latin scheme: only `design.fontScheme` (slide, then deck, then theme, then the shared default `aptos`) sets the latin fonts, and a language record\'s `fontScheme` is a default for its own script slot, not a deck font. The PPTX theme\'s `a:ea` and `a:cs` are written only for a slot a script font is selected for (the scheme\'s explicit slot or the language\'s script font) and stay empty otherwise, as in Office\'s own themes. See [Language contract](./programs/font-fidelity-everywhere/script-font-model.md#language-contract-ff-50-model-c) and [Theme slots](./programs/font-fidelity-everywhere/script-font-model.md#theme-slots-ff-49).\n- A Latin deck therefore repeats its heading/body family in `ea`/`cs`. A Japanese deck with `design.fontScheme: { "major": "Carlito", "minor": "Carlito" }` keeps the Latin family in `latin` and uses Meiryo (PowerPoint) or Noto Sans JP (Google Slides) in `ea`. `design.fontScheme.eastAsian` / `.complexScript` (`{ "major": ..., "minor": ... }`) name a script font explicitly, for example for CJK text inside a Latin deck.\n\n`@openpresentation/opf/composition` exports `resolveScriptFonts(document, { app, slideIndex })` (`app` is `"powerpoint"`, the default, or `"google-slides"`), and the package root exports `normalizeLanguageFamily(value)` (which reads a font scheme\'s `languageFamily`, `eastAsian` as `ea` and `complexScript` as `cs`). `resolveScriptFonts` returns the heading and body slots, the OOXML `lang` (a curated `ooxmlLang` culture tag such as `ja-JP` or `ms-MY`, or an authored region tag), the canonical `bcp47` tag, `script`, `direction`/`rtl`, and the per-script supplemental theme font. Renderers and exporters should use it rather than re-deriving slots. The model, the OOXML mapping and the open questions are in [`programs/font-fidelity-everywhere/script-font-model.md`](./programs/font-fidelity-everywhere/script-font-model.md). opf-render and opf-pptx implement it (FF-07, FF-19, FF-49).\n\n## Brand assets and layout hints\n\nThe 2026-09-30 spec coverage audit found that the deck logo, `organization.logo`, `speaker.photo`, `design.contentDirection`, `design.chartPrimary`, `design.listBullet` and `fontScheme.accent` validated, edited and round-tripped but changed nothing in any engine. This section states what they do now. Every rule below is implemented once, in `composeSlide()` and `resolveLogo()` of `@openpresentation/opf`, and consumed by the renderer and the exporter; the decisions marked **(vetoable)** are agent decisions the owner can overturn.\n\n### Layout templates and design\n\nAn OPF 0.19 layout record (`opf-layout/v2`) describes grid `areas`, relative `columns` and `rows`, and one `regions` entry for each body area. Regions declare accepted content kinds, role, flow, capacity and optional bleed. `layoutContent(record)` and `layoutStructure(record)` summarize those regions for hosts; `composeSlide()` reports their binding and boxes in `geometry.regions`. A slide may use `layout: "auto"` to bypass a record explicitly. [Layout templates](layout-templates.md) documents the binding and flow rules.\n\nThe effective `design` value of each key comes from the slide, then the deck, then the resolved theme, then the layout record, then the engine default. This includes `titleAlignment`, `contentAlignment`, `contentBox`, `contentDirection`, `chartPrimary`, `imageFit`, `listBullet` and `mirror`. `resolveDesignHints()` reports the supplying level, and `SlideComposition.design` holds the merged result. A layout\'s grid determines placement; `design.mirror` reverses its tracks, while the slide\'s own `composition` arranges the first primary flowing region. A top-level image block may still carry its own `placement` band. A layout sits on any slide background; it does not opt into a picture.\n\nThe `tag` (eyebrow) follows `titleAlignment` on every slide, and is left-aligned when none resolves, like the title, so a layout that centres its content does not centre the tag over a left-aligned title. On a cover the `subtitle` joins them. Only the slide\'s own `design.contentAlignment` keeps the tag (and a cover\'s subtitle) on content alignment, preserving the 2026-10-06 authoring decision.\n\n### Logos live on the organization (RR-71, OPF 0.18)\n\nA logo belongs to an organization. `Organization.logo` is one path or Asset for every shape, or up to four shapes:\n\n```json\n"organization": {\n "id": "acme", "name": "Acme",\n "logo": {\n "full": { "onLight": "./assets/acme-logo.svg", "onDark": "./assets/acme-logo-white.svg" },\n "icon": "./assets/acme-mark.svg"\n }\n}\n```\n\n`resolveLogo(presentation, slide, { shape, onDark, slideIndex, reference })` (`@openpresentation/opf/composition`) returns `{ source, path, shape, variant, reference, designPath? }` or `null`:\n\n```\n organization 1. options.reference var:organization.beta.logo.icon\n 2. slides[i].design.logo, then design.logo:\n false draws no logo; a reference picks the organization\n (and the shape, when it names one)\n 3. the primary organization role "primary", else the first\n shape the reference\'s shape, else options.shape (default full)\n missing shape -> full -> first of wordmark, stacked, icon\n variant onDark on a dark background, else onLight; a missing one uses\n the other; a plain asset is "default"\n path organization.logo, organization.1.logo.icon,\n organization.logo.full.onDark, ...\n```\n\nAn override that names an organization without a logo draws nothing: it never falls back to another organization. Hosts pass their own background luminance test as `composeSlide(..., { darkBackground })` (`resolveSlideContext` computes it); core never inspects colors.\n\nThe same logos are placed anywhere through slide-scoped built-in references (`var:organization.logo`, `var:organization.logo.icon`, `var:organization.beta.logo.wordmark`), which resolve for each slide\'s background: see [templates and variables](templates-and-variables.md#organization-logos).\n\n### Where the logo is drawn (vetoable)\n\n1. **Cover and section slides.** A slide with no body payload on a title-only template, after empty template regions collapse, or without a layout (the same rule that centers covers) draws the primary organization\'s `full` logo (or the `design.logo` override) at the top-left of the free area, inside the slide padding and below any header furniture. `composeSlide` returns it as `geometry.logo` (`{ box, shape, path, source, variant, reference, anchor: \'left\' }`): `x = area.left + padding`, `y` at the image-safe heading top, `height = 56` reference pixels at a 720-pixel short edge, `width = min(4 * height, free width)`. Headings start one gap below the box and the cover-centering rule centers the tag/title/subtitle group in the remaining span; the logo itself does not move. Consumers fit the image inside the box preserving its aspect ratio, anchored left and vertically centered (SVG `preserveAspectRatio="xMinYMid meet"`; PPTX computes the fitted size from the raster dimensions and places it at `box.x`). Nothing is drawn when no logo resolves or `design.logo` is `false`. **Content slides never get an automatic logo** (vetoable: it would move every content area).\n2. **Headers and footers.** A zone places a logo like any picture: `"image": "var:organization.logo.icon"`. `layoutFurniture` resolves the reference for the slide\'s background; the image part carries the asset in `image`, the organization path in `sourcePath` and the reference in `reference`. Without a logo the engine reports `unresolved-content` at `<zone>.image`. The zone\'s parts sit in one row (see [dynamic composition](dynamic-composition.md)).\n3. **Picture bullets.** See `listBullet` below: the `icon` shape.\n\n> **Decision, 2026-09-30 (agent decision, vetoable).** 84 of the 126 bundled example decks carry a logo, so 81 cover slides gain a logo and their heading group moves down. The placement (top-left, 56 px, full logo) and the content-slide exclusion are the reference-engine defaults; a layout-driven logo slot is a separate design.\n\n### `speaker.photo` is a built-in image variable (vetoable)\n\nNo reference engine draws a speaker photo by itself: the schema has no speaker slot on any slide and no slide-to-speaker link, and a speaker block on covers would be a separate design. The author places it explicitly with the built-in image variable (`"image": "var:speaker.photo"`, see [templates and variables](templates-and-variables.md#built-in-variables)); the field also round-trips through PPTX provenance.\n\n### `contentDirection`\n\nThe effective value follows the normal design precedence: slide, deck, theme, layout, engine default. On automatic slides `vertical` sets the root arrangement to `column` and `horizontal` to `row`, unless the slide explicitly sets `composition.mode`. Promoted regions keep their geometry and nested content groups keep their own composition.\n\nOn a v2 template, `contentDirection` changes every flowing region into a row or column without changing the template\'s areas. An atomic `flow: "none"` region remains atomic. Without the direction hint each region uses its declared flow. Layout records do not have `composition.mode`, `columns` or `weights`; their areas, tracks and regions define the grid.\n\n### `titleAlignment` and `contentAlignment` in right-to-left decks\n\n> **Decision, 2026-10-01 (RR-05, supervisor decision, vetoable).** Alignment is logical for right-to-left text. In a deck whose language is written right to left, `left` means the start edge and `right` the end edge of each paragraph: an Arabic or Hebrew paragraph with the default (or authored) `left` is drawn against the right edge, with its bullets, indents and table cells to match, while an English paragraph in the same deck keeps the left edge. `center` is unchanged and a left-to-right deck is unchanged. The effective value (slide design, then deck design, then `left`) is still what `item.alignment` reports; `placement.lines[i].alignment` and the exported `algn` carry the physical edge. A right-to-left deck that wants a paragraph at its end edge sets `right`. See [Layout direction](programs/font-fidelity-everywhere/script-font-model.md#layout-direction-rr-05).\n\n### `chartPrimary`\n\nThe resolved `design.chartPrimary` applies on automatic slides only. It changes the root arrangement when the slide has no promoted regions, no `composition.mode` of its own, and its root nodes contain at least one chart leaf and at least one node that is not a chart. The **first chart node is primary** and the other root nodes form one synthetic sub-grid:\n\n- `left` / `right`: the root is a two-track row with weights `[3, 2]` (chart first for `left`, last for `right`); the rest arrange in `auto` mode inside their track.\n- `top` / `bottom`: a two-track column with weights `[3, 2]` (chart first for `top`).\n- `none`, or any other value: no change, the existing automatic grid with equal weight.\n\nAutomatic composition builds the primary chart track and a direct content group for the other root nodes, preserving the 3:2 chart weight and source content order. A v2 template remains authoritative and does not apply this automatic hint. OPF 0.19 removes the v1 record-construction helper `chartPrimaryLayout`; authors use `chart-beside` or a custom v2 template for an authored grid.\n\nThe synthetic container has no OPF path, so it records no `groups`, `flows` or explanation entry; the root decision has `reason: \'chart-primary\'` and `selectedColumns` 2 (row) or 1 (column). Explicit root `columns` and `weights` are ignored while the automatic hint applies. A chart inside a nested group, a chart-only root, or a single root `chart` payload leaves the arrangement unchanged. A slide using a v2 template follows that template instead.\n\n### `listBullet` (vetoable)\n\n`character` (the default) draws the current glyph marker. `image` draws the organization\'s icon logo (`resolveLogo(..., { shape: \'icon\', onDark })`: the primary organization\'s, or the organization and shape `design.logo` names) as a picture bullet: every `items`/`bullets` item in `composeSlide` and every `listEntries[]` entry of its fit carry `bulletImage: { source, path }`. Marker geometry is unchanged, and each entry carries `bulletBox`, where the image draws: a square of side `marker.fontSize * PICTURE_BULLET_SCALE` (0.65, exported) whose bottom sits on the marker baseline (`marker.y`) with its left edge at `marker.x`. The scale is what desktop PowerPoint draws for an `a:buBlip` at `a:buSzPct 100000` (what the exporter writes), measured as a square 10, 15, 16, 20 and 31 px wide at font sizes of 16, 24, 25, 32 and 48 px (0.625 to 0.646; the heights run a pixel more from anti-aliasing) with its bottom on the text baseline, in Arial, Aptos, Georgia and Courier New alike, so it does not depend on the typeface; the text start and hanging indent are identical in both. The exporter sets no size, because PowerPoint sizes the bullet itself; consumers that draw it themselves (the preview) use `bulletBox`. Vetoable: the constant is a measurement, not a rule of the format. When `image` is set and no logo resolves, the glyph stays and the slide reports one `unresolved-content` diagnostic at `slides.N.design.listBullet` or `design.listBullet`, only when the slide has a list. The renderer draws an `<image>` per marker; the exporter writes native picture bullets (`a:buBlip`).\n\n### `fontScheme.accent`\n\n`resolveFontFamilies()` returns `accent` only when the effective scheme defines an `accent` role (a family string or a `Font` object). The slide `tag` (eyebrow) and the quote body use `fonts.accent ?? <current family>` (body for the tag, heading for the quote body); nothing else changes. The renderer loads and embeds it, the exporter writes it on those runs (`a:latin`) while the theme fonts stay major/minor, and import keeps restoring `design.fontScheme` from provenance. None of the bundled examples sets an accent font.\n\n## What is *not* part of this chain\n\nBeyond the color references above, content payloads carry no design controls in v1 \u2014 `position`, `fontSize` overrides at payload level, and the like were deliberately kept out while the content model stabilizes (see [`content-item-design-overrides.md`](./content-item-design-overrides.md); styled table cells and rich-text runs carry the only per-content styling, and their color fields take the reference forms above). The design system, plus layout hints (`titleAlignment`, `contentBox`, `chartPrimary`, ...) and dynamic composition, is the styling surface of an OPF document.\n'
74
74
  },
75
75
  {
76
76
  "slug": "dynamic-composition",
@@ -100,7 +100,7 @@ var docsData = Object.freeze([
100
100
  "slug": "examples",
101
101
  "file": "docs/examples.md",
102
102
  "title": "OPF Examples Guide",
103
- "markdown": "# OPF Examples Guide\n\nThe `examples/` directory has two shipped layers, plus a docs fixture kept outside the catalog:\n\n- `examples/technical/` contains compact fixtures that isolate one or two schema behaviors.\n- `examples/gallery/` contains scenario-oriented decks that show OPF working across industries, functions, education, government, international, presentation-type, and design/media use cases.\n- The representative deck for [the published-package quickstart](quickstart.md) lives at [`docs/quickstart/developer-quickstart.opf.json`](quickstart/developer-quickstart.opf.json), outside the catalog, so `@openpresentation/opf/examples` stays at the published example count (currently 128 decks); the renderer golden corpus tracks that catalog on its own release cadence.\n- The runnable first deck lives at [`docs/first-deck/`](first-deck/README.md), also outside the catalog (a deck under `examples/` would change the examples corpus that the renderer golden covers): six slides (bullets, a metric, a chart, a styled table, a local image) and a README that goes from install to validate, PNG, PPTX and opening the file in PowerPoint or Keynote, with the known limits.\n- `examples/markdown/` holds decks written in the [Markdown dialect](markdown.md) (`.opf.md`, not `.opf.json`, so they are outside the catalog count): `quarterly-review.opf.md` is a Markdown deck that every command reads (`opf validate examples/markdown/quarterly-review.opf.md`); it uses every block kind and is canonical. `outline.md` is a plain outline, not a deck (a plain `.md` file is never taken for one), read with `opf convert outline.md deck.opf.json` (a plain `.md` input is OPF Markdown, and an outline with no `---` is cut at its headings). The tests convert, validate, compose and round trip them.\n- The examples root is kept as an organizing directory rather than a home for standalone OPF files.\n\n## Technical Fixtures\n\nUse `examples/technical/` when you want a small file that exercises a specific schema surface:\n\n- content payloads, rich text, blocks, charts, tables, media, metrics, quotes, and timelines\n- promoted region keys and span combinations\n- asset string/object forms and asset-backed chart data\n- design backgrounds, organization logo shapes, headers, footers, watermarks, and slide-level overrides\n- metadata array forms, language metadata, custom narrative records, and catalog overrides\n\n## Gallery Folders\n\n| Folder | What It Demonstrates |\n| --- | --- |\n| `industries/` | Vertical market decks with operating plans, investment briefs, readiness reviews, and launch coordination. |\n| `business-functions/` | Department-specific decks for sales, marketing, product, engineering, finance, HR, legal, security, support, procurement, and strategy. |\n| `education/` | K-12, higher education, research, advising, workforce, advancement, and student services scenarios. |\n| `government/` | Public health, transit, emergency management, utilities, regulators, courts, parks, workforce, tax, and civic engagement decks. |\n| `presentation-types/` | Reusable deck archetypes such as pitches, board updates, QBRs, conference talks, workshops, postmortems, launches, policy briefings, training, and research reports. |\n| `international/` | Region- or language-specific decks, including examples of language object metadata and right-to-left direction. |\n| `design-and-media/` | Decks that emphasize design controls, image/video assets, data storytelling, and self-running orientation patterns. |\n\n## Patterns To Look For\n\n- Technical fixtures that isolate validator and renderer behavior.\n- Sparse gallery documents that use shorthand catalog references and a small slide list.\n- Medium documents with schema ids, metadata, organization and speaker records, design overrides, assets, and richer slide payloads.\n- Dense documents with their own records in `catalogs.custom`, promoted region keys, `blocks`, media assets, code payloads, header/footer configuration, organization logo shapes, watermarks, and extensions.\n- Mixed content payloads across text, bullets, lists, image, video, chart, table, code, metric, quote, and timeline slides.\n- Catalog references across narratives, layouts, themes, color schemes, font schemes, audiences, purposes and tones, every record embedded under `catalogs.default` or `catalogs.custom`, so each deck validates and renders with no catalog registered; chart types, languages and social platforms as engine vocabularies.\n\n## Validation\n\nRun the example validator after changing any `*.opf.json` file:\n\n```sh\nnode scripts/validate-examples.mjs\n```\n\nThe script walks every OPF document under `examples/` and reports schema or semantic validation issues with file paths.\n"
103
+ "markdown": "# OPF Examples Guide\n\nThe `examples/` directory has two shipped layers, plus a docs fixture kept outside the catalog:\n\n- `examples/technical/` contains compact fixtures that isolate one or two schema behaviors.\n- `examples/gallery/` contains scenario-oriented decks that show OPF working across industries, functions, education, government, international, presentation-type, and design/media use cases.\n- The representative deck for [the published-package quickstart](quickstart.md) lives at [`docs/quickstart/developer-quickstart.opf.json`](quickstart/developer-quickstart.opf.json), outside the catalog, so `@openpresentation/opf/examples` stays at the published example count (currently 129 decks); the renderer golden corpus tracks that catalog on its own release cadence.\n- The runnable first deck lives at [`docs/first-deck/`](first-deck/README.md), also outside the catalog (a deck under `examples/` would change the examples corpus that the renderer golden covers): six slides (bullets, a metric, a chart, a styled table, a local image) and a README that goes from install to validate, PNG, PPTX and opening the file in PowerPoint or Keynote, with the known limits.\n- `examples/markdown/` holds decks written in the [Markdown dialect](markdown.md) (`.opf.md`, not `.opf.json`, so they are outside the catalog count): `quarterly-review.opf.md` is a Markdown deck that every command reads (`opf validate examples/markdown/quarterly-review.opf.md`); it uses every block kind and is canonical. `outline.md` is a plain outline, not a deck (a plain `.md` file is never taken for one), read with `opf convert outline.md deck.opf.json` (a plain `.md` input is OPF Markdown, and an outline with no `---` is cut at its headings). The tests convert, validate, compose and round trip them.\n- The examples root is kept as an organizing directory rather than a home for standalone OPF files.\n\n## Technical Fixtures\n\nUse `examples/technical/` when you want a small file that exercises a specific schema surface:\n\n- content payloads, rich text, blocks, charts, tables, media, metrics, quotes, and timelines\n- promoted region keys and span combinations\n- asset string/object forms and asset-backed chart data\n- design backgrounds, organization logo shapes, headers, footers, watermarks, and slide-level overrides\n- metadata array forms, language metadata, custom narrative records, and catalog overrides\n\n## Gallery Folders\n\n| Folder | What It Demonstrates |\n| --- | --- |\n| `industries/` | Vertical market decks with operating plans, investment briefs, readiness reviews, and launch coordination. |\n| `business-functions/` | Department-specific decks for sales, marketing, product, engineering, finance, HR, legal, security, support, procurement, and strategy. |\n| `education/` | K-12, higher education, research, advising, workforce, advancement, and student services scenarios. |\n| `government/` | Public health, transit, emergency management, utilities, regulators, courts, parks, workforce, tax, and civic engagement decks. |\n| `presentation-types/` | Reusable deck archetypes such as pitches, board updates, QBRs, conference talks, workshops, postmortems, launches, policy briefings, training, and research reports. |\n| `international/` | Region- or language-specific decks, including examples of language object metadata and right-to-left direction. |\n| `design-and-media/` | Decks that emphasize design controls, image/video assets, data storytelling, and self-running orientation patterns. |\n\n## Patterns To Look For\n\n- Technical fixtures that isolate validator and renderer behavior.\n- Sparse gallery documents that use shorthand catalog references and a small slide list.\n- Medium documents with schema ids, metadata, organization and speaker records, design overrides, assets, and richer slide payloads.\n- Dense documents with their own records in `catalogs.custom`, promoted region keys, `blocks`, media assets, code payloads, header/footer configuration, organization logo shapes, watermarks, and extensions.\n- Mixed content payloads across text, bullets, lists, image, video, chart, table, code, metric, quote, and timeline slides.\n- Catalog references across narratives, layouts, themes, color schemes, font schemes, audiences, purposes and tones, every record embedded under `catalogs.default` or `catalogs.custom`, so each deck validates and renders with no catalog registered; chart types, languages and social platforms as engine vocabularies.\n\n## Validation\n\nRun the example validator after changing any `*.opf.json` file:\n\n```sh\nnode scripts/validate-examples.mjs\n```\n\nThe script walks every OPF document under `examples/` and reports schema or semantic validation issues with file paths.\n"
104
104
  },
105
105
  {
106
106
  "slug": "finding-schema-reference",
@@ -124,7 +124,7 @@ var docsData = Object.freeze([
124
124
  "slug": "format-card",
125
125
  "file": "docs/format-card.md",
126
126
  "title": "OPF format card",
127
- "markdown": '# OPF format card\n\nA self-contained authoring reference for agents and humans writing `*.opf.json` documents, sized for pasting into a model\'s context. The canonical contract is the JSON Schema (`https://openpresentation.org/schema/opf/v1`, in [`spec/schemas/opf.schema.json`](../spec/schemas/opf.schema.json)); this card compresses it. Validate with `validate` from `@openpresentation/opf` or `opf validate <file>` ([validate](validate.md)).\n\n## Document shape\n\nA presentation is one JSON object; only `slides` is required.\n\n```json\n{ "name": "Minimal Deck", "slides": [{ "title": "Minimal Deck" }] }\n```\n\nOptional top-level fields, grouped:\n\n- Identity: `name`, `description`, `filename`, `author`, `organization`, `speaker`, `tags`.\n- Intent (used by AI generation): `audience`, `purpose`, `tone`, `language`, `narrative`, `takeaway`, `duration`.\n- Appearance: `design` (`theme`, `colorScheme`, `fontScheme`, `dimensions`, `background`, `logo` override, `watermark`, `header`, `footer`, alignment hints), `variables`; logos themselves live on `organization`.\n- Resources: `assets` (registry referenced as `asset:<id>`), `catalogs` (the records the document embeds, grouped by catalog: `default`, `custom` and named groups with a `source`).\n- Machine state: `extensions` (preserved, never rendered).\n\n## Slides and content\n\nA slide carries `title` / `subtitle` / `tag` / `notes` / `section` / `beat` / `layout` / `id` / `extensions` plus content in one of three shapes \u2014 pick the loosest that says what you mean:\n\n1. **Root payload** \u2014 content-kind fields directly on the slide. The kind is inferred from the field: `text`, `items` (list), `bullets`, `image`, `video`, `chart`, `table`, `code`, `metric`, `quote`, `timeline`. Several kinds at the root are shorthand for `blocks`.\n2. **`blocks`** \u2014 an ordered array of payloads when placement should stay engine-inferred. A block is a leaf payload or a **group**: `{ "type": "group", "blocks": [...], "composition": { ... } }`. Groups nest (hard cap 32; stay \u2264 3 in practice) and cannot mix `blocks` with leaf fields.\n3. **Promoted regions** \u2014 a 3\xD73 grid when position matters. Rows `top|middle|bottom`, columns `left|center|right`, spans with `+`, intersections with `:` \u2014 `"left"`, `"center+right"`, `"top:left"`, `"middle+bottom:center+right"`. Keys must not overlap; regions cannot mix with a root payload.\n\n`composition` (on a slide or group) arranges children: `{ "mode": "auto|grid|row|column", "columns": 1-12, "weights": [..], "gap": 0-0.1, "padding": 0-0.2, "minFontSize": 8-32, "overflow": "warn|error" }`. Weights are relative track sizes; gap/padding are fractions of the canvas short edge. Engines own placement \u2014 there is no x/y.\n\nPayload notes: chart is `{ "type": "<chart-type id>", "data": { "columns": [...], "rows": [...] } }` or `{ "data": { "dataset": "<id>" } }`; table rows may hold scalars, `TextRun[]`, or styled cells `{ "value", "style": { "fill", "color", "align", "borders", ... }, "colSpan", "rowSpan" }`; `metric`/`quote`/`code` accept string shorthand; list nesting uses `level` on items, not nested payloads.\n\n## Rich text and color references\n\n`text`, list items, bullets, and table cells accept `TextRun[]`: strings or `{ "text", "bold", "italic", "underline", "strikethrough", "color", "fontSize", "fontFamily", "link", "superscript", "subscript" }`.\n\nEvery content color field (`TextRun.color`, table cell `style.fill` / `style.color`, cell border `color`) accepts three forms:\n\n- Literal hex: `"#0F172A"`, `"#B42318CC"`.\n- A color-scheme name, resolved through the effective scheme: slots `accent1`\u2013`accent6`, `dark1`, `dark2`, `light1`, `light2`, `hyperlink`, `followedHyperlink`, or roles `primary`, `secondary`, `accent`, `background`, `surface`, `text`, `textSecondary`.\n- A variable reference `var:<id>` into the top-level map: `"variables": { "risk": "#B42318" }` (or `{ "type": "color", "value": "#B42318", "description": "..." }`).\n\nPrefer names and variables over hex \u2014 they survive re-theming. Unknown `var:` ids warn, never error. The styled cell and border color fields enforce the three forms at the schema level; run colors tolerate any string (unrecognized values warn and renderers fall back to the theme), so imported decks keep validating.\n\n## Ids and extensions\n\n`id` is optional on slides and on any content payload (including groups), unique document-wide. Use ids when something outside the document must address content across edits \u2014 patch edits, comments, review state. `extensions` objects at document, slide, and payload scope carry machine state; engines ignore and preserve them.\n\n## Catalog references\n\nReusable records live in catalogs; a reference is a bare id or `name:id`: `narrative`, `tone`, `purpose`, `audience`, `design.theme`, `design.colorScheme`, `design.fontScheme`, `Slide.layout`. `catalogs` holds the records a document embeds, grouped by catalog: `default` (with its `source`), `custom` (the document\'s own) and named groups (`"acme": { "source": "pkg:@acme/opf-catalog", ... }`, referenced as `acme:hero`). Resolution: `custom` \u2192 embedded `default` \u2192 the catalog the host registered for its source; nothing is fetched. Object form overrides a record per key: `{ "id": "cool-horizon", "accent1": "#0F4C81" }`. A reference that resolves nowhere warns (`opf/unresolved-reference`), never errors. `opf embed <in> <out>` embeds every record a document uses so it renders with no catalog registered. `language` (a BCP-47 tag), `Chart.type` and the socials keys are engine vocabularies, not references.\n\n## Design in three lines\n\n`design.theme` bundles scheme + fonts + background + dimensions; `design.colorScheme` / `fontScheme` override it; `slides[].design` overrides per slide; resolution is per field, most specific wins. Backgrounds accept slot names (`"light1"`) or hex. `false` suppresses inherited `watermark` / `header` / `footer`. A header or footer zone (`left` / `center` / `right`) takes `image`, `text`, `socials`, `date`, laid out side by side in that order; generated values are variables in `text`: `"{{slide.number}} / {{deck.slideCount}}"`, `"{{slide.section}}"`, `"{{organization.name}}"` (the slide-scoped `{{slide.number}}`, `{{slide.section}}`, `{{deck.slideCount}}` also work in body text). Logos live on `organization.logo` (one path, or `full` / `stacked` / `icon` / `wordmark`, each a path or `{ onLight, onDark }`); place one with `"image": "var:organization.logo.icon"`; covers draw the full logo, and `design.logo` is only an override reference or `false`.\n\n## Prefer / never\n\nPrefer: string shorthands; bare ids for default-catalog records; inference over explicit `type`; regions only when position matters; groups only when `blocks` ordering is not enough; names/`var:` over hex in color fields; `items` for lists and `bullets` only for plain talking points (never both in one block or region; see [content payloads](content-payloads.md#items-versus-bullets)).\n\nNever: loose chart or table fields directly on a slide; region keys mixed with a root payload; overlapping region keys; `blocks` mixed with leaf fields on one payload; duplicate ids; x/y or pixel geometry (it does not exist in OPF).\n\n## Two canonical slides\n\n```json\n{\n "id": "headline",\n "title": "Adoption Doubled",\n "left": { "id": "kpi", "metric": { "value": "2.1x", "label": "Adoption", "trend": "up" } },\n "center+right": { "chart": { "type": "line", "data": { "columns": ["Month", "Teams"], "rows": [["Jan", 12], ["Feb", 18]] } } }\n}\n```\n\n```json\n{\n "id": "risks",\n "title": "What Could Go Wrong",\n "composition": { "mode": "column" },\n "blocks": [\n { "items": [["Two regions at ", { "text": "85% utilization", "color": "var:risk", "bold": true }]] },\n { "quote": { "text": "Exceptions became visible before they became escalations.", "attribution": "VP Operations" } }\n ],\n "extensions": { "authoring": { "locked": true } }\n}\n```\n'
127
+ "markdown": '# OPF format card\n\nA self-contained authoring reference for agents and humans writing `*.opf.json` documents, sized for pasting into a model\'s context. The canonical contract is the JSON Schema (`https://openpresentation.org/schema/opf/v1`, in [`spec/schemas/opf.schema.json`](../spec/schemas/opf.schema.json)); this card compresses it. Validate with `validate` from `@openpresentation/opf` or `opf validate <file>` ([validate](validate.md)).\n\n## Document shape\n\nA presentation is one JSON object; only `slides` is required.\n\n```json\n{ "name": "Minimal Deck", "slides": [{ "title": "Minimal Deck" }] }\n```\n\nOptional top-level fields, grouped:\n\n- Identity: `name`, `description`, `filename`, `author`, `organization`, `speaker`, `tags`.\n- Intent (used by AI generation): `audience`, `purpose`, `tone`, `language`, `narrative`, `takeaway`, `duration`.\n- Appearance: `design` (`theme`, `colorScheme`, `fontScheme`, `dimensions`, `background`, `variant` (from core 0.23.0: `"dark1"` or `{ background, accents }`, a map over the one color scheme), `logo` override, `watermark`, `header`, `footer`, alignment hints), `variables`; logos themselves live on `organization`.\n- Resources: `assets` (registry referenced as `asset:<id>`), `catalogs` (the records the document embeds, grouped by catalog: `default`, `custom` and named groups with a `source`).\n- Machine state: `extensions` (preserved, never rendered).\n\n## Slides and content\n\nA slide carries `title` / `subtitle` / `tag` / `notes` / `section` / `beat` / `layout` / `id` / `extensions` plus content in one of three shapes \u2014 pick the loosest that says what you mean:\n\n1. **Root payload** \u2014 content-kind fields directly on the slide. The kind is inferred from the field: `text`, `items` (list), `bullets`, `image`, `video`, `chart`, `table`, `code`, `metric`, `quote`, `timeline`. Several kinds at the root are shorthand for `blocks`.\n2. **`blocks`** \u2014 an ordered array of payloads when placement should stay engine-inferred. A block is a leaf payload or a **group**: `{ "type": "group", "blocks": [...], "composition": { ... } }`. Groups nest (hard cap 32; stay \u2264 3 in practice) and cannot mix `blocks` with leaf fields.\n3. **Promoted regions** \u2014 a 3\xD73 grid when position matters. Rows `top|middle|bottom`, columns `left|center|right`, spans with `+`, intersections with `:` \u2014 `"left"`, `"center+right"`, `"top:left"`, `"middle+bottom:center+right"`. Keys must not overlap; regions cannot mix with a root payload.\n\n`composition` (on a slide or group) arranges children: `{ "mode": "auto|grid|row|column", "columns": 1-12, "weights": [..], "gap": 0-0.1, "padding": 0-0.2, "minFontSize": 8-32, "overflow": "warn|error" }`. Weights are relative track sizes; gap/padding are fractions of the canvas short edge. Engines own placement \u2014 there is no x/y.\n\nPayload notes: chart is `{ "type": "<chart-type id>", "data": { "columns": [...], "rows": [...] } }` or `{ "data": { "dataset": "<id>" } }`; table rows may hold scalars, `TextRun[]`, or styled cells `{ "value", "style": { "fill", "color", "align", "borders", ... }, "colSpan", "rowSpan" }`; `metric`/`quote`/`code` accept string shorthand; list nesting uses `level` on items, not nested payloads.\n\n## Rich text and color references\n\n`text`, list items, bullets, and table cells accept `TextRun[]`: strings or `{ "text", "bold", "italic", "underline", "strikethrough", "color", "fontSize", "fontFamily", "link", "superscript", "subscript" }`.\n\nEvery content color field (`TextRun.color`, table cell `style.fill` / `style.color`, cell border `color`) accepts three forms:\n\n- Literal hex: `"#0F172A"`, `"#B42318CC"`.\n- A color-scheme name, resolved through the effective scheme: slots `accent1`\u2013`accent6`, `dark1`, `dark2`, `light1`, `light2`, `hyperlink`, `followedHyperlink`, or roles `primary`, `secondary`, `accent`, `background`, `surface`, `text`, `textSecondary`.\n- A variable reference `var:<id>` into the top-level map: `"variables": { "risk": "#B42318" }` (or `{ "type": "color", "value": "#B42318", "description": "..." }`).\n\nPrefer names and variables over hex \u2014 they survive re-theming. Unknown `var:` ids warn, never error. The styled cell and border color fields enforce the three forms at the schema level; run colors tolerate any string (unrecognized values warn and renderers fall back to the theme), so imported decks keep validating.\n\n## Ids and extensions\n\n`id` is optional on slides and on any content payload (including groups), unique document-wide. Use ids when something outside the document must address content across edits \u2014 patch edits, comments, review state. `extensions` objects at document, slide, and payload scope carry machine state; engines ignore and preserve them.\n\n## Catalog references\n\nReusable records live in catalogs; a reference is a bare id or `name:id`: `narrative`, `tone`, `purpose`, `audience`, `design.theme`, `design.colorScheme`, `design.fontScheme`, `Slide.layout`. `catalogs` holds the records a document embeds, grouped by catalog: `default` (with its `source`), `custom` (the document\'s own) and named groups (`"acme": { "source": "pkg:@acme/opf-catalog", ... }`, referenced as `acme:hero`). Resolution: `custom` \u2192 embedded `default` \u2192 the catalog the host registered for its source; nothing is fetched. Object form overrides a record per key: `{ "id": "cool-horizon", "accent1": "#0F4C81" }`. A reference that resolves nowhere warns (`opf/unresolved-reference`), never errors. `opf embed <in> <out>` embeds every record a document uses so it renders with no catalog registered. `language` (a BCP-47 tag), `Chart.type` and the socials keys are engine vocabularies, not references.\n\n## Design in three lines\n\n`design.theme` bundles scheme + fonts + background + dimensions; `design.colorScheme` / `fontScheme` override it; `slides[].design` overrides per slide; resolution is per field, most specific wins. Backgrounds accept slot names (`"light1"`) or hex. `false` suppresses inherited `watermark` / `header` / `footer`. A header or footer zone (`left` / `center` / `right`) takes `image`, `text`, `socials`, `date`, laid out side by side in that order; generated values are variables in `text`: `"{{slide.number}} / {{deck.slideCount}}"`, `"{{slide.section}}"`, `"{{organization.name}}"` (the slide-scoped `{{slide.number}}`, `{{slide.section}}`, `{{deck.slideCount}}` also work in body text). Logos live on `organization.logo` (one path, or `full` / `stacked` / `icon` / `wordmark`, each a path or `{ onLight, onDark }`); place one with `"image": "var:organization.logo.icon"`; covers draw the full logo, and `design.logo` is only an override reference or `false`.\n\n## Prefer / never\n\nPrefer: string shorthands; bare ids for default-catalog records; inference over explicit `type`; regions only when position matters; groups only when `blocks` ordering is not enough; names/`var:` over hex in color fields; `items` for lists and `bullets` only for plain talking points (never both in one block or region; see [content payloads](content-payloads.md#items-versus-bullets)).\n\nNever: loose chart or table fields directly on a slide; region keys mixed with a root payload; overlapping region keys; `blocks` mixed with leaf fields on one payload; duplicate ids; x/y or pixel geometry (it does not exist in OPF).\n\n## Two canonical slides\n\n```json\n{\n "id": "headline",\n "title": "Adoption Doubled",\n "left": { "id": "kpi", "metric": { "value": "2.1x", "label": "Adoption", "trend": "up" } },\n "center+right": { "chart": { "type": "line", "data": { "columns": ["Month", "Teams"], "rows": [["Jan", 12], ["Feb", 18]] } } }\n}\n```\n\n```json\n{\n "id": "risks",\n "title": "What Could Go Wrong",\n "composition": { "mode": "column" },\n "blocks": [\n { "items": [["Two regions at ", { "text": "85% utilization", "color": "var:risk", "bold": true }]] },\n { "quote": { "text": "Exceptions became visible before they became escalations.", "attribution": "VP Operations" } }\n ],\n "extensions": { "authoring": { "locked": true } }\n}\n```\n'
128
128
  },
129
129
  {
130
130
  "slug": "handoff-2026-09-08",
@@ -202,7 +202,7 @@ var docsData = Object.freeze([
202
202
  "slug": "image-treatments",
203
203
  "file": "docs/image-treatments.md",
204
204
  "title": "Images: backgrounds, image blocks and treatments",
205
- "markdown": '# Images: backgrounds, image blocks and treatments\n\nOPF 0.15 has two places for a picture, with one fit vocabulary:\n\n- A **background** is the slide\'s canvas fill. A picture background fills the whole slide behind everything and never moves content.\n- An **image block** holds every content picture. It carries fit, focus, the treatment vocabulary below, and an optional placement that bleeds it to one slide edge.\n\nopf-render draws both as SVG and opf-pptx exports them as native PowerPoint DrawingML, from the same core geometry. This page also maps each of the 15 [pptx.gallery image treatments](https://www.pptx.gallery/image-treatments) to OPF, with its support status and the reason. The reference deck [`fixtures/image-treatments.opf.json`](fixtures/image-treatments.opf.json) has one slide per gallery treatment. Core\'s `test/image-blocks.test.mjs` checks it against core 0.14\'s frozen picture geometry, and `pnpm test:image-treatments` checks it across core, SVG and PPTX.\n\n## Backgrounds\n\n```json\n{\n "design": {\n "background": {\n "type": "image",\n "src": "./images/harbour.jpg",\n "alt": "Container ships at dawn",\n "fit": "cover",\n "focus": { "x": 0.5, "y": 0.7 },\n "overlay": { "color": "dark1", "opacity": 0.4 }\n }\n }\n}\n```\n\nThe effective background is the slide\'s `design.background`, then the deck\'s, then the theme\'s. A picture background has these properties:\n\n| Property | Meaning |\n|---|---|\n| `src` | The picture: `asset:<id>`, an HTTPS URL, a data URI or a relative path. |\n| `alt` | Alternative text. With `alt` the picture is meaningful, and PPTX export draws it as a full-slide picture at the back that carries the text. Without `alt` it is decorative and exports as the native slide background. A referenced asset\'s alt text does not count. In the preview a picture with `alt` is an image with that name, and one with `alt: ""` is hidden from assistive technology. |\n| `fit` | `cover` (the default) fills the slide and crops around `focus`. `contain` shows the whole picture over the colour scheme\'s default background. `stretch` scales it to the slide. `tile` repeats it at its own size from the top-left corner. |\n| `focus` | `{ x, y }` in 0 to 1: the point of the picture that a `cover` crop keeps in view. The default is the center. |\n| `opacity` | Picture opacity. The overlay keeps its own. |\n| `recolor` | `"grayscale"`, or `{ dark, light }` for duotone, as on an image block. Recolor and opacity apply to the picture\'s pixels; the overlay is drawn above them. |\n| `overlay` | A scrim over the whole picture, or a band along one edge (`edge`, `size`; see [Overlay](#overlay)). |\n\nThe string shorthand `"asset:hero"` (or any source starting with `asset:`, `https://`, `data:`, `./` or `../`) is a cover picture background. Theme slots (`light1`) and hex colours keep their meaning, and any other string is invalid.\n\n`composeSlide` reports the picture as `SlideComposition.backgroundImage`: the canvas `box`, `fit`, `focus`, `opacity`, `recolor`, the overlay box, and the fit placement when core can read the picture\'s proportions. The headings and the body compose exactly as on a slide without the background. A slide with only a title over a picture background is a cover, and its title centers like any cover\'s.\n\n## Image blocks\n\n```json\n{ "image": { "src": "asset:hero", "alt": "Harbour" }, "fit": "cover", "focus": { "x": 0.3, "y": 0.5 }, "shape": "rounded", "border": { "color": "accent5", "width": 1 } }\n```\n\n`Slide.image` is source shorthand for one image block, with the effective `design.imageFit`: it takes no fit, focus, treatment or placement keys of its own (the schema rejects them on the slide). To treat or place a picture, write an image block in `blocks`. Every property below is optional, valid only on an image payload, and additive.\n\n| Property | Meaning | Native PPTX | SVG preview |\n|---|---|---|---|\n| `fit` | `cover` fills the frame and crops around `focus`. `contain` shows the whole picture. `stretch` scales it to the frame. Default: the effective `design.imageFit` (the slide\'s, the deck\'s, then the layout\'s), else `cover`. | `a:srcRect`: positive insets crop, negative insets pad | the shared placement (`fitImage`) |\n| `focus` | `{ x, y }`: the point a `cover` crop keeps in view. | `a:srcRect` | the same placement |\n| `aspectRatio` | The largest centered frame with this width/height ratio (`circle` uses 1). | frame geometry | frame geometry |\n| `shape` | `rectangle`, `rounded`, `circle` or `hexagon`. | `a:prstGeom` `rect` / `roundRect` / `ellipse` / `hexagon` with core\'s guide values | `clipPath` with core\'s outline of the same preset formula |\n| `cornerRadius` | Radius of a `rounded` frame as a share of the shorter side. Default 0.16667. | `roundRect` `adj` | circular arcs with the same radius |\n| `border` | `{ color, width }`. The line is centered on the outline, and its width is in reference pixels. | `a:ln` with `solidFill`, `algn="ctr"` and a miter join | `stroke` on the same outline, miter join |\n| `opacity` | Opacity of the picture\'s pixels only. | `a:alphaModFix` | `opacity` on the image only |\n| `recolor` | `"grayscale"`, or `{ dark, light }` for duotone. Luminance uses Rec. 601 weights on sRGB. | `a:grayscl` / `a:duotone` | `feColorMatrix` in sRGB |\n| `overlay` | A scrim in the frame\'s shape, or an edge band on a rectangle frame. | one `p:sp` directly above the picture | `path` with `fill-opacity` |\n| `placement` | `{ edge, size, inset }`: bleed the block to one slide edge (see below). | frame geometry | frame geometry |\n\nColors accept the usual `ColorRef` forms: hex, scheme slot or role, or `var:<id>`. Eight-digit hex alpha is kept as native `a:alpha` and SVG opacity. Alt text comes from the image\'s asset.\n\n`composeSlide` reports every image item\'s picture as `ComposedItem.image`: the `region` (the cell, or the placement band), the frame `box`, `fit`, `focus`, the `shape` with its DrawingML preset and SVG outline, `border`, `opacity`, `recolor`, `overlay` and `placement`. Both engines draw exactly that. The paint order is the same in both: the picture with its recolor and opacity, clipped to the shape, then the line, then the overlay.\n\n### Placement\n\n`placement: { edge: "left" | "right" | "top" | "bottom", size: 0.1\u20130.9 (default 0.5), inset: false }` gives the image a band along that edge of the slide, edge to edge, and the headings and other content compose in the rest of the slide at the normal padding. `size` is the band\'s share of the slide width (left, right) or height (top, bottom). `inset: true` puts the frame inside the slide padding of the band, like a card. Header and footer bands keep their full-width placement.\n\n- Only a top-level block (`slides.N.blocks.I`) can be placed, and each slide edge takes at most one placed image. A placed block inside a group or a promoted region, or a second block on the same edge, is an `opf/image-placement-invalid` error.\n- Bands are taken in block order. Each spans the free area left by the earlier ones.\n- Placement belongs to a top-level image block. Current v2 layout records do not have image-placeholder placement; write a block in `blocks` to place a picture rather than using the root `Slide.image` shorthand.\n- A slide whose only body is placed images is composed like a cover: its headings center in the free area beside the image.\n- In a right-to-left deck `left` is the start side, drawn at the right. Edge overlays mirror the same way.\n\nPlacement is the only mechanism that moves content aside. A background never does.\n\n## Overlay\n\n`{ color, opacity, edge?, size? }` is shared by backgrounds and image blocks. Without `edge` it covers the whole frame in the frame\'s shape. With `edge` (`top`, `bottom`, `left` or `right`) it covers a band of `size` (default 0.3) of the frame along that edge, for example a caption strip. An edge overlay on a non-rectangle image frame reports `unsupported-image-treatment` at `...overlay.edge`, and neither engine draws it. That diagnostic never fails strict layout. Text over a picture background is certified readable (`opf/text-on-image`) only through a full-frame overlay strong enough for every possible pixel; an edge band does not count.\n\nAn unchanged export imports back as the same OPF: the background and the image blocks with their treatments, plus a data URI of each embedded picture. The `OPF_IMAGE_V1` and `OPF_IMAGE_OVERLAY_V1` shape tags record the native geometry of an image block. A picture that was moved, re-cropped or recolored stays an ordinary image and reports its provenance diagnostic. An edited overlay drops only the overlay.\n\n## pptx.gallery treatments\n\n| Gallery treatment | OPF | Status | Reason or gap |\n|---|---|---|---|\n| full-bleed | background `{type: image, src, overlay: {color: dark1, opacity: 0.2}}` | supported | |\n| text-overlay | background with `overlay: {color: dark1, opacity: 0.55}` | supported | Heading color still follows the theme background. Use a dark theme or background for light text over photos. |\n| side-by-side | image block, `placement: {edge: left, size: 0.46}` | supported | |\n| caption-overlay | image block, `overlay: {color: dark1, opacity: 0.75, edge: bottom, size: 0.25}` | supported | Square corners. An edge band cannot follow a rounded mask as one native shape. The gallery\'s card sits inside the slide padding, which a background cannot do, so the picture is content below the headings. Without the inset it is a background with the same edge-band overlay. |\n| masked-shape | image block, `shape: hexagon, aspectRatio: 1.1547, placement: {edge: right, inset: true}` | supported | Masks are limited to the four presets. Arbitrary polygon and custom-geometry masks are not in the vocabulary. |\n| circular-crop | image block, `shape: circle, placement: {edge: left, size: 0.4, inset: true}` | supported | The gallery\'s small shadow is not drawn (see shadows). |\n| rounded-card | image block, `shape: rounded, cornerRadius: 0.05, border: {color: accent5, width: 1}` | supported, without shadow | The optional card shadow is unsupported (see shadows). |\n| duotone | background with `recolor: {dark: accent1, light: light1}` | supported | Native raster parity of PowerPoint\'s luminance weights is unverified (see fidelity). |\n| background-blur | background with `overlay: {color: light1, opacity: 0.4}`, plus `contentBox: true` | **unsupported: blur** | Blur is not expressible. The mapping keeps the light scrim and the sharp card, without the blur. |\n| image-strip | image block, `placement: {edge: bottom, size: 0.3}` | partial | A placed block is one picture. A strip of several images is content: image blocks in a row composition. |\n| collage-grid | 4 image blocks, `composition: {mode: grid, columns: 2}`, `imageFit: cover` | supported via content blocks | A collage is content composition, not a single-image treatment. |\n| device-frame | image block, `aspectRatio: 0.5, shape: rounded, cornerRadius: 0.12, border: {color: dark1, width: 12}, placement: {edge: right, inset: true}` | partial | The bezel is a thick line on a rounded portrait frame. Device artwork (notch, buttons, device-specific screens) is not expressible. |\n| cutout-subject | image block, `fit: contain, placement: {edge: right}`, with a transparent PNG | partial | **Background removal is unsupported.** PowerPoint\'s Remove Background produces an edited bitmap, not a declarative effect. Supply a pre-cut transparent PNG, and both engines keep its alpha. The grounding shadow is unsupported (see shadows). |\n| watermark | image block, `fit: contain, opacity: 0.1, recolor: grayscale` | supported | `design.watermark` remains a separate, preview-only feature. |\n| cinematic-crop | image block, `aspectRatio: 2.39`, with `design.background: dark1` | supported | |\n\nThe frames of full-bleed, text-overlay, background-blur, duotone, side-by-side, masked-shape, circular-crop, image-strip, device-frame, cutout-subject and collage-grid are those of core 0.14. Caption-overlay, rounded-card, watermark and cinematic-crop were 0.14 slide images behind the headings that need an inset, a mask, a line or an aspect ratio, which a background does not have. In 0.15 they are image blocks below the headings, and their treatment geometry within the frame is unchanged. The gallery snippets themselves live in pptx.gallery (FF-30).\n\n## Unsupported effects\n\nThese effects are left out of the vocabulary on purpose. A value that both engines cannot draw identically would break the preview/export contract.\n\n- **Blur** (`a:blur` inside a blip, or the Office 2010 `a14:imgEffect` artistic blur): PowerPoint\'s blur kernel and radius scale are not specified. There is also no native evidence that PowerPoint renders a blip `a:blur` on pictures. An SVG `feGaussianBlur` cannot be shown to match. background-blur is therefore unsupported.\n- **Shadows and soft edges** (`a:outerShdw`, `a:softEdge` in `a:effectLst`): these are native, but PowerPoint\'s blur kernel for them is unspecified. An SVG drop shadow would only approximate it.\n- **Background removal**: this is a bitmap edit, not an effect. Use a transparent source.\n- **Device artwork and arbitrary masks**: these would need custom geometry and composed artwork. The treatment vocabulary is limited to preset masks and a line.\n- **Tint, brightness and contrast** (`a:lum`, `a:clrChange`, `a:tint`): no gallery treatment needs them. They are left out until they have the same luminance-formula verification as grayscale and duotone.\n- **Frame treatments on backgrounds**: a background has fit, focus, opacity, recolor and an overlay (with edge bands). A masked, bordered, inset or aspect-ratio picture is an image block.\n\n## Fidelity boundary\n\nThese checks prove the shared contract: geometry, preset guides and outlines, line width and color, recolor endpoints, alpha, overlay and round trip (`pnpm test:image-treatments`, plus the tests in each engine). They do not prove PowerPoint raster parity. Office runs are root-only, and the following are still unverified in native PowerPoint:\n\n- negative `a:srcRect` insets for `contain`;\n- the luminance weights PowerPoint uses for `a:grayscl` and `a:duotone`. The preview uses Rec. 601 on sRGB, as LibreOffice does.\n- line joins on the hexagon outline.\n'
205
+ "markdown": '# Images: backgrounds, image blocks and treatments\n\nOPF 0.15 has two places for a picture, with one fit vocabulary:\n\n- A **background** is the slide\'s canvas fill. A picture background fills the whole slide behind everything and never moves content.\n- An **image block** holds every content picture. It carries fit, focus, the treatment vocabulary below, and an optional placement that bleeds it to one slide edge.\n\nopf-render draws both as SVG and opf-pptx exports them as native PowerPoint DrawingML, from the same core geometry. This page also maps each of the 15 [pptx.gallery image treatments](https://www.pptx.gallery/image-treatments) to OPF, with its support status and the reason. The reference deck [`fixtures/image-treatments.opf.json`](fixtures/image-treatments.opf.json) has one slide per gallery treatment. Core\'s `test/image-blocks.test.mjs` checks it against core 0.14\'s frozen picture geometry, and `pnpm test:image-treatments` checks it across core, SVG and PPTX.\n\n## Backgrounds\n\n```json\n{\n "design": {\n "background": {\n "type": "image",\n "src": "./images/harbour.jpg",\n "alt": "Container ships at dawn",\n "fit": "cover",\n "focus": { "x": 0.5, "y": 0.7 },\n "overlay": { "color": "dark1", "opacity": 0.4 }\n }\n }\n}\n```\n\nThe effective background is the slide\'s `design.background`, then the deck\'s, then the theme\'s. A picture background has these properties:\n\n| Property | Meaning |\n|---|---|\n| `src` | The picture: `asset:<id>`, an HTTPS URL, a data URI or a relative path. |\n| `alt` | Alternative text. With `alt` the picture is meaningful, and PPTX export draws it as a full-slide picture at the back that carries the text. Without `alt` it is decorative and exports as the native slide background. A referenced asset\'s alt text does not count. In the preview a picture with `alt` is an image with that name, and one with `alt: ""` is hidden from assistive technology. |\n| `fit` | `cover` (the default) fills the slide and crops around `focus`. `contain` shows the whole picture over the colour scheme\'s default background. `stretch` scales it to the slide. `tile` repeats it at its own size from the top-left corner. |\n| `focus` | `{ x, y }` in 0 to 1: the point of the picture that a `cover` crop keeps in view. The default is the center. |\n| `opacity` | Picture opacity. The overlay keeps its own. |\n| `recolor` | `"grayscale"`, or `{ dark, light }` for duotone, as on an image block. Recolor and opacity apply to the picture\'s pixels; the overlay is drawn above them. |\n| `overlay` | A scrim over the whole picture, or a band along one edge (`edge`, `size`; see [Overlay](#overlay)). |\n| `tone` | `dark` or `light` (from core 0.23.0): the variant the slide takes (`dark1` or `light1`) so text and lines read on the picture. Absent: the deck\'s variant, else light. |\n\nA picture or gradient background can carry `tone: "dark"` or `"light"` (from core 0.23.0). The tone picks the color map (`dark1` or `light1`, see `design.variant` in [design resolution](design-resolution.md#color-variants)) so the slide\'s text, borders and chart furniture are chosen to read on the picture. Without a tone the picture takes the deck\'s variant, else counts as light, as before.\n\nThe string shorthand `"asset:hero"` (or any source starting with `asset:`, `https://`, `data:`, `./` or `../`) is a cover picture background. Theme slots (`light1`) and hex colours keep their meaning, and any other string is invalid.\n\n`composeSlide` reports the picture as `SlideComposition.backgroundImage`: the canvas `box`, `fit`, `focus`, `opacity`, `recolor`, the overlay box, and the fit placement when core can read the picture\'s proportions. The headings and the body compose exactly as on a slide without the background. A slide with only a title over a picture background is a cover, and its title centers like any cover\'s.\n\n## Image blocks\n\n```json\n{ "image": { "src": "asset:hero", "alt": "Harbour" }, "fit": "cover", "focus": { "x": 0.3, "y": 0.5 }, "shape": "rounded", "border": { "color": "accent5", "width": 1 } }\n```\n\n`Slide.image` is source shorthand for one image block, with the effective `design.imageFit`: it takes no fit, focus, treatment or placement keys of its own (the schema rejects them on the slide). To treat or place a picture, write an image block in `blocks`. Every property below is optional, valid only on an image payload, and additive.\n\n| Property | Meaning | Native PPTX | SVG preview |\n|---|---|---|---|\n| `fit` | `cover` fills the frame and crops around `focus`. `contain` shows the whole picture. `stretch` scales it to the frame. Default: the effective `design.imageFit` (the slide\'s, the deck\'s, then the layout\'s), else `cover`. | `a:srcRect`: positive insets crop, negative insets pad | the shared placement (`fitImage`) |\n| `focus` | `{ x, y }`: the point a `cover` crop keeps in view. | `a:srcRect` | the same placement |\n| `aspectRatio` | The largest centered frame with this width/height ratio (`circle` uses 1). | frame geometry | frame geometry |\n| `shape` | `rectangle`, `rounded`, `circle` or `hexagon`. | `a:prstGeom` `rect` / `roundRect` / `ellipse` / `hexagon` with core\'s guide values | `clipPath` with core\'s outline of the same preset formula |\n| `cornerRadius` | Radius of a `rounded` frame as a share of the shorter side. Default 0.16667. | `roundRect` `adj` | circular arcs with the same radius |\n| `border` | `{ color, width }`. The line is centered on the outline, and its width is in reference pixels. | `a:ln` with `solidFill`, `algn="ctr"` and a miter join | `stroke` on the same outline, miter join |\n| `opacity` | Opacity of the picture\'s pixels only. | `a:alphaModFix` | `opacity` on the image only |\n| `recolor` | `"grayscale"`, or `{ dark, light }` for duotone. Luminance uses Rec. 601 weights on sRGB. | `a:grayscl` / `a:duotone` | `feColorMatrix` in sRGB |\n| `overlay` | A scrim in the frame\'s shape, or an edge band on a rectangle frame. | one `p:sp` directly above the picture | `path` with `fill-opacity` |\n| `placement` | `{ edge, size, inset }`: bleed the block to one slide edge (see below). | frame geometry | frame geometry |\n\nColors accept the usual `ColorRef` forms: hex, scheme slot or role, or `var:<id>`. Eight-digit hex alpha is kept as native `a:alpha` and SVG opacity. Alt text comes from the image\'s asset.\n\n`composeSlide` reports every image item\'s picture as `ComposedItem.image`: the `region` (the cell, or the placement band), the frame `box`, `fit`, `focus`, the `shape` with its DrawingML preset and SVG outline, `border`, `opacity`, `recolor`, `overlay` and `placement`. Both engines draw exactly that. The paint order is the same in both: the picture with its recolor and opacity, clipped to the shape, then the line, then the overlay.\n\n### Placement\n\n`placement: { edge: "left" | "right" | "top" | "bottom", size: 0.1\u20130.9 (default 0.5), inset: false }` gives the image a band along that edge of the slide, edge to edge, and the headings and other content compose in the rest of the slide at the normal padding. `size` is the band\'s share of the slide width (left, right) or height (top, bottom). `inset: true` puts the frame inside the slide padding of the band, like a card. Header and footer bands keep their full-width placement.\n\n- Only a top-level block (`slides.N.blocks.I`) can be placed, and each slide edge takes at most one placed image. A placed block inside a group or a promoted region, or a second block on the same edge, is an `opf/image-placement-invalid` error.\n- Bands are taken in block order. Each spans the free area left by the earlier ones.\n- Placement belongs to a top-level image block. Current v2 layout records do not have image-placeholder placement; write a block in `blocks` to place a picture rather than using the root `Slide.image` shorthand.\n- A slide whose only body is placed images is composed like a cover: its headings center in the free area beside the image.\n- In a right-to-left deck `left` is the start side, drawn at the right. Edge overlays mirror the same way.\n\nPlacement is the only mechanism that moves content aside. A background never does.\n\n## Overlay\n\n`{ color, opacity, edge?, size? }` is shared by backgrounds and image blocks. Without `edge` it covers the whole frame in the frame\'s shape. With `edge` (`top`, `bottom`, `left` or `right`) it covers a band of `size` (default 0.3) of the frame along that edge, for example a caption strip. An edge overlay on a non-rectangle image frame reports `unsupported-image-treatment` at `...overlay.edge`, and neither engine draws it. That diagnostic never fails strict layout. Text over a picture background is certified readable (`opf/text-on-image`) only through a full-frame overlay strong enough for every possible pixel; an edge band does not count.\n\nAn unchanged export imports back as the same OPF: the background and the image blocks with their treatments, plus a data URI of each embedded picture. The `OPF_IMAGE_V1` and `OPF_IMAGE_OVERLAY_V1` shape tags record the native geometry of an image block. A picture that was moved, re-cropped or recolored stays an ordinary image and reports its provenance diagnostic. An edited overlay drops only the overlay.\n\n## pptx.gallery treatments\n\n| Gallery treatment | OPF | Status | Reason or gap |\n|---|---|---|---|\n| full-bleed | background `{type: image, src, overlay: {color: dark1, opacity: 0.2}}` | supported | |\n| text-overlay | background with `overlay: {color: dark1, opacity: 0.55}` | supported | Heading color still follows the theme background. Use a dark theme or background for light text over photos. |\n| side-by-side | image block, `placement: {edge: left, size: 0.46}` | supported | |\n| caption-overlay | image block, `overlay: {color: dark1, opacity: 0.75, edge: bottom, size: 0.25}` | supported | Square corners. An edge band cannot follow a rounded mask as one native shape. The gallery\'s card sits inside the slide padding, which a background cannot do, so the picture is content below the headings. Without the inset it is a background with the same edge-band overlay. |\n| masked-shape | image block, `shape: hexagon, aspectRatio: 1.1547, placement: {edge: right, inset: true}` | supported | Masks are limited to the four presets. Arbitrary polygon and custom-geometry masks are not in the vocabulary. |\n| circular-crop | image block, `shape: circle, placement: {edge: left, size: 0.4, inset: true}` | supported | The gallery\'s small shadow is not drawn (see shadows). |\n| rounded-card | image block, `shape: rounded, cornerRadius: 0.05, border: {color: accent5, width: 1}` | supported, without shadow | The optional card shadow is unsupported (see shadows). |\n| duotone | background with `recolor: {dark: accent1, light: light1}` | supported | Native raster parity of PowerPoint\'s luminance weights is unverified (see fidelity). |\n| background-blur | background with `overlay: {color: light1, opacity: 0.4}`, plus `contentBox: true` | **unsupported: blur** | Blur is not expressible. The mapping keeps the light scrim and the sharp card, without the blur. |\n| image-strip | image block, `placement: {edge: bottom, size: 0.3}` | partial | A placed block is one picture. A strip of several images is content: image blocks in a row composition. |\n| collage-grid | 4 image blocks, `composition: {mode: grid, columns: 2}`, `imageFit: cover` | supported via content blocks | A collage is content composition, not a single-image treatment. |\n| device-frame | image block, `aspectRatio: 0.5, shape: rounded, cornerRadius: 0.12, border: {color: dark1, width: 12}, placement: {edge: right, inset: true}` | partial | The bezel is a thick line on a rounded portrait frame. Device artwork (notch, buttons, device-specific screens) is not expressible. |\n| cutout-subject | image block, `fit: contain, placement: {edge: right}`, with a transparent PNG | partial | **Background removal is unsupported.** PowerPoint\'s Remove Background produces an edited bitmap, not a declarative effect. Supply a pre-cut transparent PNG, and both engines keep its alpha. The grounding shadow is unsupported (see shadows). |\n| watermark | image block, `fit: contain, opacity: 0.1, recolor: grayscale` | supported | `design.watermark` remains a separate, preview-only feature. |\n| cinematic-crop | image block, `aspectRatio: 2.39`, with `design.background: dark1` | supported | |\n\nThe frames of full-bleed, text-overlay, background-blur, duotone, side-by-side, masked-shape, circular-crop, image-strip, device-frame, cutout-subject and collage-grid are those of core 0.14. Caption-overlay, rounded-card, watermark and cinematic-crop were 0.14 slide images behind the headings that need an inset, a mask, a line or an aspect ratio, which a background does not have. In 0.15 they are image blocks below the headings, and their treatment geometry within the frame is unchanged. The gallery snippets themselves live in pptx.gallery (FF-30).\n\n## Unsupported effects\n\nThese effects are left out of the vocabulary on purpose. A value that both engines cannot draw identically would break the preview/export contract.\n\n- **Blur** (`a:blur` inside a blip, or the Office 2010 `a14:imgEffect` artistic blur): PowerPoint\'s blur kernel and radius scale are not specified. There is also no native evidence that PowerPoint renders a blip `a:blur` on pictures. An SVG `feGaussianBlur` cannot be shown to match. background-blur is therefore unsupported.\n- **Shadows and soft edges** (`a:outerShdw`, `a:softEdge` in `a:effectLst`): these are native, but PowerPoint\'s blur kernel for them is unspecified. An SVG drop shadow would only approximate it.\n- **Background removal**: this is a bitmap edit, not an effect. Use a transparent source.\n- **Device artwork and arbitrary masks**: these would need custom geometry and composed artwork. The treatment vocabulary is limited to preset masks and a line.\n- **Tint, brightness and contrast** (`a:lum`, `a:clrChange`, `a:tint`): no gallery treatment needs them. They are left out until they have the same luminance-formula verification as grayscale and duotone.\n- **Frame treatments on backgrounds**: a background has fit, focus, opacity, recolor and an overlay (with edge bands). A masked, bordered, inset or aspect-ratio picture is an image block.\n\n## Fidelity boundary\n\nThese checks prove the shared contract: geometry, preset guides and outlines, line width and color, recolor endpoints, alpha, overlay and round trip (`pnpm test:image-treatments`, plus the tests in each engine). They do not prove PowerPoint raster parity. Office runs are root-only, and the following are still unverified in native PowerPoint:\n\n- negative `a:srcRect` insets for `contain`;\n- the luminance weights PowerPoint uses for `a:grayscl` and `a:duotone`. The preview uses Rec. 601 on sRGB, as LibreOffice does.\n- line joins on the hexagon outline.\n'
206
206
  },
207
207
  {
208
208
  "slug": "layout-templates",
@@ -286,7 +286,7 @@ var docsData = Object.freeze([
286
286
  "slug": "quickstart",
287
287
  "file": "docs/quickstart.md",
288
288
  "title": "Developer quickstart",
289
- "markdown": "# Developer quickstart\n\nA new developer can install the **published** OPF packages into a fresh Node 24\nproject and author, validate, compose, paginate, edit with undo, preview and export\na representative deck. No account, model call, or sibling repository checkout\nis required.\n\nThis is a documented supported subset, not universal Office or all-feature\nparity. See the [compatibility matrix](compatibility-matrix.md) for what is\nshipped versus deferred.\n\n## Versions\n\n**Verified artifacts (2026-10-17):** core **0.20.5**, CLI **0.20.1**,\nrenderer **0.20.4**, PPTX **0.20.4**, editor **0.20.4** and Gallery **3.0.0**.\n[Artifact identities and verification](https://github.com/OpenPresentation/opf/blob/main/docs/evidence-rr-87-final-published-set-2026-10-17.md) records\nthe releases selected in `release-plan.json`; artifact verification is separate\nfrom the consumer and native checks below. All declare\n`engines.node: >=22`; Node 24 is the toolchain this quickstart is verified on.\n\n```sh\nnode -v # 24.x (verified); 22 and later are declared\nnpm install @openpresentation/opf@0.20.5 \\\n @openpresentation/opf-render@0.20.4 \\\n @openpresentation/opf-editor@0.20.4 \\\n @openpresentation/opf-pptx@0.20.4 \\\n @openpresentation/cli@0.20.1 \\\n @openpresentation/gallery@3.0.0\n# opf-render 0.20 keeps its converters and font packages as optional peers: install the ones you use\n# (here the base font pack, PNG and PDF; add @expo-google-fonts/* office or script packs as needed)\nnpm install @expo-google-fonts/roboto@0.4.3 @expo-google-fonts/roboto-mono@0.4.2 \\\n @resvg/resvg-js@^2.6.2 pdf-lib@^1.17.1 sharp@^0.35.5\n```\n\nA missing converter or font package is a typed error (`converter-missing`, `font-resource-unavailable`) whose message\nnames the `npm install` command, so a host that only previews SVG in a browser installs none of them.\n\nCopy [`docs/quickstart/developer-quickstart.opf.json`](quickstart/developer-quickstart.opf.json)\ninto that project as `deck.opf.json`. That file is a docs fixture, not one of\nthe 127 decks in `@openpresentation/opf/examples`. Verify the install came from the registry\n(`package-lock.json` `resolved` URLs start with `https://registry.npmjs.org/`)\nand that you did not add `file:` dependencies on this repository.\n\nThe [format card](format-card.md) describes ColorRef, named variables and the\ncurrent source contract. `opf embed` embeds the catalog records a deck uses for\nportable offline authoring; it does not download remote assets. Keep the\nColorRef docs fixture outside the 127-deck example/golden corpus in this update.\n\n## The short path\n\nWith core 0.18.0 and later, three calls of `@openpresentation/opf` cover files in a Node application (core 0.17 had them at `@openpresentation/opf/node`):\n\n```js\nimport * as opf from '@openpresentation/opf';\n\nawait opf.convert('deck.opf.md', 'deck.pdf'); // also .pptx, .png (one per slide), .svg, .zip and the deck forms\nconst deck = await opf.open('deck.opf.md'); // .opf.md, .opf.yaml, .opf.json, or a .pptx to import\ndeck.slides.push({ title: 'Q4' });\nawait opf.save(deck, 'deck.opf.md');\n```\n\nThe CLI does the same from a shell: `opf convert deck.opf.md deck.pdf`. The sections below take the pieces one at a time.\n\n## Author and validate\n\n```sh\nnpx --no-install opf --version\nnpx --no-install opf validate deck.opf.json\nnpx --no-install opf validate deck.opf.json --format text\n```\n\nThe CLI depends on core, which holds the schema, catalogs and the checker. Validation never renders; `opf convert` produces PDF, PPTX, PNG and SVG and reads `.pptx` files through the optional peers `@openpresentation/opf-render` and\n`@openpresentation/opf-pptx` (see [the CLI reference](cli.md); `opf doctor` says what is installed).\n`opf --version` reports the CLI and the installed core. Successful validation is not\nvisual verification.\n\nLibrary equivalents:\n\n```js\nimport { readFile } from 'node:fs/promises';\nimport { validate } from '@openpresentation/opf';\n\nconst source = await readFile('deck.opf.json', 'utf8');\nconst document = JSON.parse(source);\nconsole.log(validate(document)); // a parsed document: findings of every category\nconsole.log(validate(source)); // JSON text: the same findings with line and column\n```\n\n## Offline fonts, composition, pagination\n\n```js\nimport { paginate, resolveSlideContext } from '@openpresentation/opf';\nimport { composeSlide } from '@openpresentation/opf/composition';\nimport { loadFonts } from '@openpresentation/opf-render/fonts-node';\n\nconst fonts = await loadFonts({ pack: 'base' });\nconst { slide, options } = resolveSlideContext(document, 0, { fonts });\nconst geometry = composeSlide(slide, options);\nconst { presentation, pages } = paginate(document, { fonts });\n```\n\n`loadFonts({ pack: 'base' })` loads the bundled Roboto faces for\n`design.fontScheme: 'roboto'`. It returns the fonts handle: every deck-level\nverb (`paginate`, `toSvg`, `toPng`, `toPdf`, `toPptx`) takes it as\n`{ fonts }` and reads what it needs from it (the `textMeasurement`, the faces\nto embed, the font files). `resolveSlideContext(document, index, { fonts, catalogs })` resolves\none slide's layout, canvas, theme and font families (slide design, then deck\ndesign, then theme, then the engine default font scheme) into the options `composeSlide`\ntakes, so you never look up a font scheme yourself; a reference that resolves\nnowhere comes back as an `unresolved-reference` diagnostic. It also returns\n`slide`: the slide with `{{slide.number}}`, `{{slide.section}}` and\n`{{deck.slideCount}}` substituted for `options.slideNumber` and\n`options.slideCount`; compose and draw that slide. `paginate`\ndoes the same for every slide. The helper does not install system fonts or\nchange the authored scheme. Reuse the same `fonts` for SVG preview and PPTX\nexport.\n\nShared headers and footers use `furniture-flow-v2`. Body content stays between\n`geometry.furniture.headerBottom` and `geometry.furniture.footerTop`. A zone\nwrites generated values as variables in its `text`, such as\n`\"{{slide.number}} / {{deck.slideCount}}\"` or `\"{{organization.name}}\"`.\n\nPagination returns a new presentation plus source mappings. It preserves\nauthored text, whitespace and reading order; it does not drop overflowed\ncontent.\n\n```sh\nnpx --no-install opf paginate deck.opf.json paginated.opf.json\n```\n\n## Edit with undo\n\n```js\nimport { createEditorSession } from '@openpresentation/opf-editor';\n\nconst editor = createEditorSession(document, { rejectInvalid: true });\nconst original = editor.presentation.slides[0].title;\neditor.set('slides.0.title', 'Edited title');\neditor.undo();\n// original title, including whitespace, is restored\n```\n\nThe CLI can apply JSON Patch edits (`opf edit`) but has no persistent undo\nhistory. Use the editor session or version control for undo.\n\n## Preview and export\n\n`@openpresentation/opf` in Node (core 0.18.0 and later) reads and writes files with the bundled fonts and loads the render and\nPPTX engines you installed:\n\n```js\nimport * as opf from '@openpresentation/opf';\n\nawait opf.convert('deck.opf.json', 'deck.pdf');\nawait opf.convert('deck.opf.json', 'slides/deck.png', { scale: 2 }); // slides/deck-1.png, -2, ... (or \"slides/slide-{n}.png\")\nawait opf.convert('deck.opf.json', 'deck.pptx');\nconst back = await opf.open('deck.pptx'); // PowerPoint to OPF\nconst { files } = await opf.convert(back, { format: 'svg' }); // bytes, nothing written\n```\n\n`convert` checks the deck (format and references), draws with the renderer's bundled open fonts and no system fonts,\nreads images next to the input file, never reads a clock (pass `date: 'YYYY-MM-DD'` for a date field) and writes each file\natomically. A missing engine throws `OPFExportError` with code `peer-not-installed` and the install command. The options and\nerrors are in [OPF files in Node](node.md).\n\n### Advanced: the engines directly\n\n`convert` calls these. Use them when you need the pieces: your own fonts handle, one SVG per slide in a browser,\nor options the function does not expose.\n\n```js\nimport { toSvg, toPng, toPdf } from '@openpresentation/opf-render';\nimport { toPptx } from '@openpresentation/opf-pptx';\n\nconst svgs = toSvg(presentation, { fonts }); // one SVG per slide\nconst png = await toPng(svgs[0], { fonts });\nconst pdf = await toPdf(svgs, { fonts });\nconst pptx = await toPptx(presentation, { fonts });\n```\n\n`toSvg` is the local preview of every slide (`toSvg(presentation, 3, { fonts })` draws slide 3 as one string). PNG rasterizes that SVG.\nPDF (opf-render 0.12.0 and later) converts the same SVG to **vector paths with\nselectable, searchable text** in embedded font subsets, with no second layout pass;\npass `{ raster: true }` for the image-per-slide output that renderers up to 0.11.9\nalways wrote. Pass the same fonts handle as for PNG; vector PDF never uses system\nfonts.\n`toPptx` is the supported PowerPoint export from OPF. **Its text is editable\nline by line, not paragraph by paragraph:** it writes one text box for each\nwrapped line, so text you add or lengthen in PowerPoint or Keynote does not\nreflow into the next line, and a title that wraps is not the Title placeholder.\nFor heavy text changes, edit the OPF source and export again. An opt-in native\nbody mode is in progress and is not published yet. Shared\nheaders and footers are written on the slide master or layout. The retained\n[Windows sample](evidence/rr-20-windows-current-2026-10-14/README.md) covers\n17 selected native cases and four actual UI operations; 41 of 58 cases were\nnot executed natively. This quickstart verifies published Node APIs and\nexports. It does not certify every Office operation or global native fidelity.\n\nBrowser preview uses the same SVG core plus\n`@openpresentation/opf-render/fonts-browser` and\n`@openpresentation/opf-editor/canvas`. Load the same font bytes the Node helper\nresolved. Do not fetch fonts from the network at render time.\n\n## Prove it\n\nFrom this repository, after a normal `pnpm install`:\n\n```sh\nnode scripts/test-developer-quickstart.mjs\n```\n\nThat script creates an empty temp project, installs the published versions from\nthe npm registry, copies this example, and asserts validate, offline\nfonts, furniture composition, pagination, undo, SVG, PNG, PDF and PPTX.\nIt fails if any package is a `file:` or workspace link.\n\n## What this does not cover\n\n- Renderer native-width residuals:\n [opf-render#24](https://github.com/OpenPresentation/opf-render/issues/24)\n- Native PowerPoint open/edit/save/reopen and real Office Header/Footer (`p:hf`):\n [opf#87](https://github.com/OpenPresentation/opf/issues/87)\n- Remaining GitHub [issue 88](https://github.com/OpenPresentation/opf/issues/88)\n checklist (the Inspector overlay/json-options, gallery Playground+Editor\n links, and Header & footer playground example are already live on\n production; the issue stays open)\n- Archived font-shaping prototypes (not in the published runtime)\n- PDF/UA or PDF/A conformance, general SVG diagrams, and Mermaid\n"
289
+ "markdown": "# Developer quickstart\n\nA new developer can install the **published** OPF packages into a fresh Node 24\nproject and author, validate, compose, paginate, edit with undo, preview and export\na representative deck. No account, model call, or sibling repository checkout\nis required.\n\nThis is a documented supported subset, not universal Office or all-feature\nparity. See the [compatibility matrix](compatibility-matrix.md) for what is\nshipped versus deferred.\n\n## Versions\n\n**Verified artifacts (2026-10-17):** core **0.20.5**, CLI **0.20.1**,\nrenderer **0.20.4**, PPTX **0.20.4**, editor **0.20.4** and Gallery **3.0.0**.\n[Artifact identities and verification](https://github.com/OpenPresentation/opf/blob/main/docs/evidence-rr-87-final-published-set-2026-10-17.md) records\nthe releases selected in `release-plan.json`; artifact verification is separate\nfrom the consumer and native checks below. All declare\n`engines.node: >=22`; Node 24 is the toolchain this quickstart is verified on.\n\n```sh\nnode -v # 24.x (verified); 22 and later are declared\nnpm install @openpresentation/opf@0.20.5 \\\n @openpresentation/opf-render@0.20.4 \\\n @openpresentation/opf-editor@0.20.4 \\\n @openpresentation/opf-pptx@0.20.4 \\\n @openpresentation/cli@0.20.1 \\\n @openpresentation/gallery@3.0.0\n# opf-render 0.20 keeps its converters and font packages as optional peers: install the ones you use\n# (here the base font pack, PNG and PDF; add @expo-google-fonts/* office or script packs as needed)\nnpm install @expo-google-fonts/roboto@0.4.3 @expo-google-fonts/roboto-mono@0.4.2 \\\n @resvg/resvg-js@^2.6.2 pdf-lib@^1.17.1 sharp@^0.35.5\n```\n\nA missing converter or font package is a typed error (`converter-missing`, `font-resource-unavailable`) whose message\nnames the `npm install` command, so a host that only previews SVG in a browser installs none of them.\n\nCopy [`docs/quickstart/developer-quickstart.opf.json`](quickstart/developer-quickstart.opf.json)\ninto that project as `deck.opf.json`. That file is a docs fixture, not one of\nthe 127 decks in `@openpresentation/opf/examples`. Verify the install came from the registry\n(`package-lock.json` `resolved` URLs start with `https://registry.npmjs.org/`)\nand that you did not add `file:` dependencies on this repository.\n\nThe [format card](format-card.md) describes ColorRef, named variables and the\ncurrent source contract. `opf embed` embeds the catalog records a deck uses for\nportable offline authoring; it does not download remote assets. Keep the\nColorRef docs fixture outside the 127-deck example/golden corpus in this update.\n\n## The short path\n\nWith core 0.18.0 and later, three calls of `@openpresentation/opf` cover files in a Node application (core 0.17 had them at `@openpresentation/opf/node`):\n\n```js\nimport * as opf from '@openpresentation/opf';\n\nawait opf.convert('deck.opf.md', 'deck.pdf'); // also .pptx, .png (one per slide), .svg, .zip and the deck forms\nconst deck = await opf.open('deck.opf.md'); // .opf.md, .opf.yaml, .opf.json, or a .pptx to import\ndeck.slides.push({ title: 'Q4' });\nawait opf.save(deck, 'deck.opf.md');\n```\n\nThe CLI does the same from a shell: `opf convert deck.opf.md deck.pdf`. The sections below take the pieces one at a time.\n\n## Author and validate\n\n```sh\nnpx --no-install opf --version\nnpx --no-install opf validate deck.opf.json\nnpx --no-install opf validate deck.opf.json --format text\n```\n\nThe CLI depends on core, which holds the schema, catalogs and the checker. Validation never renders; `opf convert` produces PDF, PPTX, PNG and SVG and reads `.pptx` files through the optional peers `@openpresentation/opf-render` and\n`@openpresentation/opf-pptx` (see [the CLI reference](cli.md); `opf doctor` says what is installed).\n`opf --version` reports the CLI and the installed core. Successful validation is not\nvisual verification.\n\nLibrary equivalents:\n\n```js\nimport { readFile } from 'node:fs/promises';\nimport { validate } from '@openpresentation/opf';\n\nconst source = await readFile('deck.opf.json', 'utf8');\nconst document = JSON.parse(source);\nconsole.log(validate(document)); // a parsed document: findings of every category\nconsole.log(validate(source)); // JSON text: the same findings with line and column\n```\n\n## Offline fonts, composition, pagination\n\n```js\nimport { paginate, resolveSlideContext } from '@openpresentation/opf';\nimport { composeSlide } from '@openpresentation/opf/composition';\nimport { loadFonts } from '@openpresentation/opf-render/fonts-node';\n\nconst fonts = await loadFonts({ pack: 'base' });\nconst { slide, options } = resolveSlideContext(document, 0, { fonts });\nconst geometry = composeSlide(slide, options);\nconst { presentation, pages } = paginate(document, { fonts });\n```\n\n`loadFonts({ pack: 'base' })` loads the bundled Roboto faces for\n`design.fontScheme: 'roboto'`. It returns the fonts handle: every deck-level\nverb (`paginate`, `toSvg`, `toPng`, `toPdf`, `toPptx`) takes it as\n`{ fonts }` and reads what it needs from it (the `textMeasurement`, the faces\nto embed, the font files). `resolveSlideContext(document, index, { fonts, catalogs })` resolves\none slide's layout, canvas, theme and font families (slide design, then deck\ndesign, then theme, then the engine default font scheme) into the options `composeSlide`\ntakes, so you never look up a font scheme yourself; a reference that resolves\nnowhere comes back as an `unresolved-reference` diagnostic. It also returns\n`slide`: the slide with `{{slide.number}}`, `{{slide.section}}` and\n`{{deck.slideCount}}` substituted for `options.slideNumber` and\n`options.slideCount`; compose and draw that slide. `paginate`\ndoes the same for every slide. The helper does not install system fonts or\nchange the authored scheme. Reuse the same `fonts` for SVG preview and PPTX\nexport.\n\nShared headers and footers use `furniture-flow-v2`. Body content stays between\n`geometry.furniture.headerBottom` and `geometry.furniture.footerTop`. A zone\nwrites generated values as variables in its `text`, such as\n`\"{{slide.number}} / {{deck.slideCount}}\"` or `\"{{organization.name}}\"`.\n\nPagination returns a new presentation plus source mappings. It preserves\nauthored text, whitespace and reading order; it does not drop overflowed\ncontent.\n\n```sh\nnpx --no-install opf paginate deck.opf.json paginated.opf.json\n```\n\n## Edit with undo\n\n```js\nimport { createEditorSession } from '@openpresentation/opf-editor';\n\nconst editor = createEditorSession(document, { rejectInvalid: true });\nconst original = editor.presentation.slides[0].title;\neditor.set('slides.0.title', 'Edited title');\neditor.undo();\n// original title, including whitespace, is restored\n```\n\nThe CLI can apply JSON Patch edits (`opf edit`) but has no persistent undo\nhistory. Use the editor session or version control for undo.\n\n## Preview and export\n\n`@openpresentation/opf` in Node (core 0.18.0 and later) reads and writes files with the bundled fonts and loads the render and\nPPTX engines you installed:\n\n```js\nimport * as opf from '@openpresentation/opf';\n\nawait opf.convert('deck.opf.json', 'deck.pdf');\nawait opf.convert('deck.opf.json', 'slides/deck.png', { scale: 2 }); // slides/deck-1.png, -2, ... (or \"slides/slide-{n}.png\")\nawait opf.convert('deck.opf.json', 'deck.pptx');\nconst back = await opf.open('deck.pptx'); // PowerPoint to OPF\nconst { files } = await opf.convert(back, { format: 'svg' }); // bytes, nothing written\n```\n\n`convert` checks the deck (format and references), draws with the renderer's bundled open fonts and no system fonts,\nreads images next to the input file, never reads a clock (pass `date: 'YYYY-MM-DD'` for a date field) and writes each file\natomically. A missing engine throws `OPFExportError` with code `peer-not-installed` and the install command. The options and\nerrors are in [OPF files in Node](node.md).\n\n### Advanced: the engines directly\n\n`convert` calls these. Use them when you need the pieces: your own fonts handle, one SVG per slide in a browser,\nor options the function does not expose.\n\n```js\nimport { toSvg, toPng, toPdf } from '@openpresentation/opf-render';\nimport { toPptx } from '@openpresentation/opf-pptx';\n\nconst svgs = toSvg(presentation, { fonts }); // one SVG per slide\nconst png = await toPng(svgs[0], { fonts });\nconst pdf = await toPdf(svgs, { fonts });\nconst pptx = await toPptx(presentation, { fonts });\n```\n\n`toSvg` is the local preview of every slide (`toSvg(presentation, 3, { fonts })` draws slide 3 as one string). PNG rasterizes that SVG.\nPDF (opf-render 0.12.0 and later) converts the same SVG to **vector paths with\nselectable, searchable text** in embedded font subsets, with no second layout pass;\npass `{ raster: true }` for the image-per-slide output that renderers up to 0.11.9\nalways wrote. Pass the same fonts handle as for PNG; vector PDF never uses system\nfonts.\n`toPptx` is the supported PowerPoint export from OPF. **Its text is editable\nline by line, not paragraph by paragraph:** it writes one text box for each\nwrapped line, so text you add or lengthen in PowerPoint or Keynote does not\nreflow into the next line, and a title that wraps is not the Title placeholder.\nFor heavy text changes, edit the OPF source and export again. From opf-pptx and\nCLI 0.22.0, `opf convert deck.opf.json deck.pptx --body-text native` (`bodyText:\n'native'` in `toPptx`) is an opt-in mode that writes whole paragraphs and binds a\nwrapped title to the Title placeholder; see [when to choose it](cli.md#pptx-text-rr-88-rr-91). Shared\nheaders and footers are written on the slide master or layout. The retained\n[Windows sample](evidence/rr-20-windows-current-2026-10-14/README.md) covers\n17 selected native cases and four actual UI operations; 41 of 58 cases were\nnot executed natively. This quickstart verifies published Node APIs and\nexports. It does not certify every Office operation or global native fidelity.\n\nBrowser preview uses the same SVG core plus\n`@openpresentation/opf-render/fonts-browser` and\n`@openpresentation/opf-editor/canvas`. Load the same font bytes the Node helper\nresolved. Do not fetch fonts from the network at render time.\n\n## Prove it\n\nFrom this repository, after a normal `pnpm install`:\n\n```sh\nnode scripts/test-developer-quickstart.mjs\n```\n\nThat script creates an empty temp project, installs the published versions from\nthe npm registry, copies this example, and asserts validate, offline\nfonts, furniture composition, pagination, undo, SVG, PNG, PDF and PPTX.\nIt fails if any package is a `file:` or workspace link.\n\n## What this does not cover\n\n- Renderer native-width residuals:\n [opf-render#24](https://github.com/OpenPresentation/opf-render/issues/24)\n- Native PowerPoint open/edit/save/reopen and real Office Header/Footer (`p:hf`):\n [opf#87](https://github.com/OpenPresentation/opf/issues/87)\n- Remaining GitHub [issue 88](https://github.com/OpenPresentation/opf/issues/88)\n checklist (the Inspector overlay/json-options, gallery Playground+Editor\n links, and Header & footer playground example are already live on\n production; the issue stays open)\n- Archived font-shaping prototypes (not in the published runtime)\n- PDF/UA or PDF/A conformance, general SVG diagrams, and Mermaid\n"
290
290
  },
291
291
  {
292
292
  "slug": "release-process",
@@ -304,7 +304,7 @@ var docsData = Object.freeze([
304
304
  "slug": "schema-reference",
305
305
  "file": "docs/schema-reference.md",
306
306
  "title": "OPF Presentation Schema Reference",
307
- "markdown": "# OPF Presentation Schema Reference\n\nThis reference documents the author-facing shape of a complete `*.opf.json` presentation document. It summarizes the canonical schema in `spec/schemas/opf.schema.json`; the schema remains the source of truth for validators.\n\n## Document Contract\n\n- Schema id: `https://openpresentation.org/schema/opf/v1`\n- Required top-level fields: `slides`\n- Additional top-level fields: not allowed\n\n## Top-Level Fields\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `$schema` | no | `const:\"https://openpresentation.org/schema/opf/v1\"` | Optional OPF schema version. When omitted, validators and engines should assume the latest supported OPF schema. |\n| `name` | no | `string` | Display name of the presentation for GUI/TUI lists, library/search indexing, OS-level metadata, and default export filenames. This is deck identity, not slide content. Use slides[].title and slides[].subtitle for text... |\n| `description` | no | `string` | Free-form prose describing what this presentation is about. Used by agents and humans as a deck-level summary; complements purpose (the goal) and narrative (the structured storyline). Round-trips to OOXML 'docProps/co... |\n| `filename` | no | `string` | Optional base filename for exports (without extension). The opf CLI (convert) and the editor strip a trailing .pptx, .pdf, .png, or .svg (case-insensitive) and append the target format's extension. When omitted, they... |\n| `organization` | no | `oneOf:ref:Organization / array<ref:Organization>` | Organization associated with the presentation, usually the presenting company. Array form supports hosts, partners, clients, and sponsors. The primary organization (Organization.role 'primary', else the first item) su... |\n| `speaker` | no | `oneOf:ref:Speaker / array<ref:Speaker>` | Person presenting the deck. Array form supports panels and multi-speaker decks. The first speaker is the primary speaker: the built-in variables 'speaker.<field>' read it, 'speakers' lists every name, and 'speaker.<id... |\n| `author` | no | `oneOf:string / array<string>` | Optional credit for the person who authored or contributed to the deck, distinct from speaker. Array form supports multiple contributors. Round-trips to OOXML 'docProps/core.xml' as '<dc:creator>' (semicolon-joined wh... |\n| `audience` | no | `oneOf:string / ref:Audience / array<oneOf:string / ref:Audience>` | Intended audiences for the presentation. Accepts any of: - A single string: an audiences reference ('executive', or 'acme:board' for a record of the catalogs.acme group), or free-form text ('Series B investors'). A st... |\n| `purpose` | no | `oneOf:string / ref:Purpose` | Primary goal of the presentation. Accepts either: - A string: a purposes reference ('decide', or 'acme:fundraise' for a record of the catalogs.acme group), or a free-form goal ('Raise a Series B round of $30M'). A str... |\n| `language` | no | `oneOf:ref:LanguageTag / ref:Language` | Language of the presentation content, an engine vocabulary: a BCP-47 language tag, validated directly with no catalog lookup. Accepts either: - A BCP-47 tag ('en-US', 'en-GB', 'ja', 'zh-Hans', 'ar-SA'). - An inline La... |\n| `tone` | no | `oneOf:ref:CatalogReference / ref:Tone` | Desired tone for the presentation. Accepts either: - A tones reference: a bare id ('formal') or 'name:id' for a record of a named catalogs group. - An inline Tone object for custom tone metadata or overrides on a refe... |\n| `takeaway` | no | `oneOf:string / array<string>` | Audience-facing takeaway the presentation should leave behind. Array form supports multiple takeaways. Deck-level intent used by AI to seed and pressure-test slide content. |\n| `duration` | no | `integer` | Target presentation duration, as an integer number of minutes. The opf-render presenter view counts the elapsed time against it, and core validate warns when it lies outside the resolved narrative's 'duration' range.... |\n| `tags` | no | `array<string>` | Free-form labels used for categorization, search, and filtering. Lowercase kebab-case is recommended for consistency across a deck library. |\n| `design` | no | `ref:Design` | Optional design system covering theme, color scheme, font scheme, dimensions, background, logo, watermark, header, and footer applied to the deck. When omitted, engines use their default design configuration. |\n| `variables` | no | `ref:Variables` | Optional named variables: deck colors referenced as 'var:<id>' (the original use), and typed content variables (text, number, date, image, url, list) referenced inline as '{{<id>}}' or whole as 'var:<id>'. Variables a... |\n| `template` | no | `boolean` | Marks this document as a template: an incomplete OPF file. A template declares variables (top-level 'variables') and references them from content, and may leave required variables unfilled; validation then reports the... |\n| `narrative` | no | `ref:CatalogReference` | The deck's narrative plan, by reference: a bare narratives id ('classic-story') or 'name:id' for a record of a named catalogs group ('acme:founder-pitch'). The deck holds only this pointer; the plan (the arc, its beat... |\n| `slides` | yes | `array<ref:Slide>` | Ordered array of slides that make up the presentation. |\n| `references` | no | `array<ref:Reference>` | Sources that text runs cite with 'cite'. Ids are unique. A cited reference is listed in the footnote area of every slide that cites it, with a marker number assigned per deck in order of first use; a reference no run... |\n| `datasets` | no | `ref:Datasets` | Optional shared data tables, keyed by id. A chart ('chart.data': { \"dataset\": \"<id>\" }) or a table ('table': { \"dataset\": \"<id>\" }) references one instead of holding its own copy; engines inline the reference before c... |\n| `assets` | no | `ref:Assets` | Optional reusable asset registry for images, data files, videos, documents, fonts, and other resources referenced elsewhere in the deck via 'asset:<id>' strings. |\n| `catalogs` | no | `ref:Catalogs` | The catalog records this document embeds, grouped by the catalog they came from: 'default' (the catalog bare ids come from), 'custom' (records the document defines itself) and any number of named catalogs ('acme', ref... |\n| `extensions` | no | `object` | Custom data passthrough for agent workflows; ignored by the engine but preserved across read/write round-trips. |\n\n## Object And Type Reference\n\n### Assets\n\n- Type: `object`\n- Required fields: none\n- Purpose: Reusable asset registry for resources used by slides, charts, metadata, and design. Keys are stable asset ids referenced elsewhere as 'asset:<id>'. Each asset can be a source string or an object with src plus optional metadata.\n\n_No named properties._\n\n\n### Asset\n\n- Type: `oneOf:string / object`\n- Required fields: none\n- Purpose: Reusable or inline resource. A string is shorthand for { \"src\": value }. Source strings accept 'asset:<id>' references, HTTPS URLs, data URIs, relative paths resolved against the OPF file location, or local filesystem paths. Use object form when metadata such as alt text, title or mediaType matters.\n\n_No named properties._\n\n\n### Audience\n\n- Type: `anyOf:schema / schema`\n- Required fields: none\n- Purpose: Inline audience metadata for the presentation. Use 'id' to reference an audiences record (a bare id or 'name:id') and override selected fields, or use 'name' for a custom inline audience.\n- Conditional requirement: `id` or `name`\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `id` | no | `ref:CatalogReference` | Optional audiences reference (a bare id or 'name:id') resolved before applying the inline overrides. |\n| `name` | no | `string` | Human-readable audience name shown in pickers. |\n| `summary` | no | `string` | One-sentence positioning of the audience. |\n| `description` | no | `string` | Longer prose describing the audience and how to address them. |\n| `seniority` | no | `enum:ic \\| manager \\| director \\| vp \\| c-suite \\| mixed` | Typical seniority level of the audience. |\n| `technicalFluency` | no | `enum:low \\| medium \\| high \\| mixed` | Typical technical fluency of the audience. |\n| `decisionPower` | no | `enum:informational \\| advisory \\| decision-maker` | Whether the audience is expected to be informed, advise, or decide. |\n| `attentionBudgetMinutes` | no | `number` | Realistic upper bound on focused attention for a single presentation, in minutes. |\n| `recommendedNarratives` | no | `array<string>` | Soft cross-link: narrative ids that work well for this audience, for picker UIs. Never resolved, embedded or validated as references. |\n| `recommendedTones` | no | `array<string>` | Soft cross-link: tone ids that work well for this audience, for picker UIs. Never resolved, embedded or validated as references. |\n| `tags` | no | `array<string>` | Free-form labels for filtering and search. |\n\n\n### Purpose\n\n- Type: `anyOf:schema / schema`\n- Required fields: none\n- Purpose: Inline purpose metadata for the presentation. Use 'id' to reference a purposes record (a bare id or 'name:id') and override selected fields, or use 'name' for a custom inline purpose.\n- Conditional requirement: `id` or `name`\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `id` | no | `ref:CatalogReference` | Optional purposes reference (a bare id or 'name:id') resolved before applying the inline overrides. |\n| `name` | no | `string` | Human-readable purpose name shown in pickers. |\n| `summary` | no | `string` | One-sentence positioning of the purpose. |\n| `description` | no | `string` | Longer prose describing when to use this purpose and how it should shape a deck. |\n| `outcome` | no | `string` | Desired audience outcome after the presentation. |\n| `successCriteria` | no | `array<string>` | Observable signals that the deck accomplished this purpose. |\n| `recommendedNarratives` | no | `array<string>` | Soft cross-link: narrative ids that work well for this purpose, for picker UIs. Never resolved, embedded or validated as references. |\n| `recommendedTones` | no | `array<string>` | Soft cross-link: tone ids that work well for this purpose, for picker UIs. Never resolved, embedded or validated as references. |\n| `tags` | no | `array<string>` | Free-form labels for filtering and search. |\n\n\n### Language\n\n- Type: `object`\n- Required fields: `bcp47`\n- Purpose: Inline language metadata for the presentation: a BCP-47 'bcp47' tag plus overrides of what engines know about it. Languages are an engine vocabulary, not catalog records, so there is no 'id'.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `name` | no | `string` | Human-readable language name. |\n| `bcp47` | yes | `ref:LanguageTag` | BCP-47 language tag used for locale-aware rendering, proofing, and accessibility metadata. Use 'en-GB' for UK English; 'en-UK' is not a valid BCP-47 region form. |\n| `ooxmlLang` | no | `string` | Culture tag for OOXML text-run language attributes (a:rPr/@lang, a:endParaRPr/@lang), in the language-[Script-]REGION form Office recognizes (e.g. 'ja-JP', 'ar-SA', 'ms-MY', 'nb-NO', 'fil-PH', 'zh-CN'). Without it, en... |\n| `code` | no | `string` | ISO 639-3 or 639-2 language code carried for engines that prefer ISO codes. |\n| `direction` | no | `enum:ltr \\| rtl` | Base text direction for the language. When omitted, engines derive it from the script: Arabic (Arab), Hebrew (Hebr), Syriac (Syrc), Thaana (Thaa), N'Ko (Nkoo), Adlam (Adlm), Samaritan (Samr), Mandaic (Mand) and Hanifi... |\n| `script` | no | `string` | ISO 15924 script code of the language's writing system. The script selects the OOXML font slot the language's text uses: East Asian scripts (Hans, Hant, Hani, Jpan, Kore, Hang, Hira, Kana, Bopo, Yiii) use the eastAsia... |\n| `fontScheme` | no | `ref:CatalogReference` | Font-scheme reference (a bare id or 'name:id') for this language's script slot when targeting PowerPoint output. Its major/minor families fill the language's script slot (eastAsian or complexScript, chosen by 'script'... |\n| `googleFontScheme` | no | `ref:CatalogReference` | Font-scheme reference (a bare id or 'name:id') used in place of 'fontScheme' when resolving script fonts for Google Slides output. |\n| `summary` | no | `string` | One-sentence note about coverage or font defaults. |\n| `description` | no | `string` | Longer prose describing the language record and any font-pairing rationale. |\n| `tags` | no | `array<string>` | Free-form labels for filtering and search. |\n\n\n### Tone\n\n- Type: `anyOf:schema / schema`\n- Required fields: none\n- Purpose: Inline tone metadata for the presentation. Use 'id' to reference a tones record (a bare id or 'name:id') and override selected fields, or use 'name' for a custom inline tone.\n- Conditional requirement: `id` or `name`\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `id` | no | `ref:CatalogReference` | Optional tones reference (a bare id or 'name:id') resolved before applying the inline overrides. |\n| `name` | no | `string` | Human-readable tone name shown in pickers. |\n| `summary` | no | `string` | One-sentence positioning of the tone. |\n| `description` | no | `string` | Longer prose describing the tone and the kinds of decks it suits. |\n| `voiceCues` | no | `array<string>` | Short directives that shape AI generation toward this tone. |\n| `avoid` | no | `array<string>` | Anti-patterns that AI generation should not produce when this tone is active. |\n| `samplePhrases` | no | `array<string>` | Short example phrases that exemplify this tone. |\n| `recommendedNarratives` | no | `array<string>` | Soft cross-link: narrative ids this tone pairs well with, for picker UIs. Never resolved, embedded or validated as references. |\n| `tags` | no | `array<string>` | Free-form labels for filtering and search. |\n\n\n### Organization\n\n- Type: `object`\n- Required fields: `id`, `name`\n- Purpose: An organization associated with the presentation, typically the presenting company, but also hosts, partners, clients, or sponsors. The primary organization (role 'primary', else the first one) supplies the cover and section logo (unless design.logo overrides it) and the 'socials' header/footer field (its socials). Every field is also a built-in variable ('{{organization.name}}', 'organization.<id>.<field>'), and every logo a slide-scoped image ('var:organization.logo.icon', 'var:organization...\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `id` | yes | `string` | Stable identifier for the organization, used to reference it from Speaker.organizationId and to address it in built-in variables as 'organization.<id>.<field>'. Must be unique among organizations (a duplicate is a val... |\n| `name` | yes | `string` | Display name. The built-in variable 'organization.name' (primary organization) or 'organization.<id>.name' carries it into any string, such as a header or footer text of '{{organization.name}}'. |\n| `legalName` | no | `string` | Optional legal entity name when it differs from the display name. Never drawn automatically; available as the built-in variable 'organization.legalName'. |\n| `logo` | no | `oneOf:ref:Asset / ref:OrganizationLogo` | The organization's logo. A path, URL or Asset is one logo for every shape; an object gives up to four shapes (full, stacked, icon, wordmark), each a path or Asset, or { onLight, onDark } for the two backgrounds. A pat... |\n| `domain` | no | `string` | Bare internet domain for the organization. Never drawn automatically and not used to look up assets; available as the built-in variable 'organization.domain'. |\n| `email` | no | `string` | General contact email for the organization. Never drawn automatically; available as the built-in variable 'organization.email'. |\n| `phone` | no | `string` | Main contact phone number for the organization. E.164 format is recommended. Never drawn automatically; available as the built-in variable 'organization.phone'. |\n| `tagline` | no | `string` | Short tagline. Never drawn automatically (cover slides do not show it); available as the built-in variable 'organization.tagline', for example in a footer 'text' of '{{organization.tagline}}'. |\n| `role` | no | `enum:primary \\| partner \\| client \\| sponsor \\| host` | Role of the organization relative to the presentation. Only 'primary' has behavior: it selects the primary organization (cover and section logo, the 'socials' header/footer field, and the 'organization.<field>' built-... |\n| `socials` | no | `ref:Socials` | Optional social media handles or URLs for the organization. The primary organization's socials render in header/footer zones that set socials: true; otherwise they are authoring metadata. |\n\n\n### Speaker\n\n- Type: `object`\n- Required fields: `id`, `name`\n- Purpose: A person presenting the deck. A speaker is drawn only through built-in variables ('{{speaker.name}}' inside any string, a header or footer text included, and 'var:speaker.photo' as a whole image field). No layout, cover or bio slide places a speaker on its own. See docs/templates-and-variables.md.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `id` | yes | `string` | Stable identifier for the speaker. Must be unique among speakers (a duplicate is a validation error). Addresses this speaker in built-in variables as 'speaker.<id>.<field>', such as '{{speaker.alice.title}}'. |\n| `name` | yes | `string` | Display name. Built-in variable 'speaker.name' (first speaker) or 'speaker.<id>.name'; every name is listed by 'speakers'. |\n| `title` | no | `string` | Role or title. Built-in variable 'speaker.title' (or 'speaker.<id>.title'), for example in a footer text of '{{speaker.name}}, {{speaker.title}}'. |\n| `photo` | no | `ref:Asset` | Source for the speaker's headshot image. Accepts an HTTPS URL, data URI, relative path (resolved against the OPF file location), local path, or 'asset:<id>' reference. Common formats are JPG or PNG; SVG is not appropr... |\n| `email` | no | `string` | Contact email. Never drawn automatically; available as the built-in variable 'speaker.email'. |\n| `phone` | no | `string` | Contact phone number for the speaker. E.164 format is recommended. Never drawn automatically; available as the built-in variable 'speaker.phone'. |\n| `bio` | no | `string` | Short biographical paragraph. Never drawn automatically; available as the built-in variable 'speaker.bio', for a bio or 'about the speaker' slide you write. |\n| `organizationId` | no | `string` | Reference to an Organization.id in organization; it must name an existing organization (a validation error otherwise). Attribution metadata for hosts: no engine draws it or changes which organization is primary becaus... |\n| `socials` | no | `ref:Socials` | Optional social media handles or URLs for the speaker. Authoring metadata only: nothing draws speaker socials and there is no built-in variable for them (the 'socials' header/footer field draws the primary organizatio... |\n\n\n### Socials\n\n- Type: `object`\n- Required fields: none\n- Purpose: Social media handles or URLs, keyed by platform. The keys are an engine vocabulary validated directly (no catalog lookup): engines link a handle with the platform's URL pattern and handle prefix (spec/reference/engine-vocabularies.json). Each value is a string, either a full URL or a platform handle (e.g., '@acme'). Brand colours and icons are catalog display metadata for authoring UIs; engines render the profile URL, not icons or brand colours. Used both for organization and speaker records.\n\n_No named properties._\n\n\n### Design\n\n- Type: `object`\n- Required fields: none\n- Purpose: Visual design system applied to the presentation; individual slides may override fields via Slide.design.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `theme` | no | `ref:CatalogReference` | Theme for the deck: a themes reference, a bare id ('minimal') or 'name:id' ('acme:brand'). The theme supplies the default colour scheme, font scheme, background and dimensions; design.colorScheme, design.fontScheme, d... |\n| `colorScheme` | no | `oneOf:string / ref:ColorScheme` | Color scheme for the presentation. Accepts two forms: - A colorSchemes reference: a bare id ('cool-horizon') or 'name:id' ('acme:ocean'). - Object form: a ColorScheme with an optional 'id' reference as the base plus s... |\n| `fontScheme` | no | `oneOf:string / ref:FontScheme` | Font scheme for heading, body, accent, and code text. Accepts two forms: - A fontSchemes reference: a bare id ('aptos') or 'name:id' ('acme:inter'). - Object form: a FontScheme with an optional 'id' reference as the b... |\n| `dimensions` | no | `oneOf:ref:DimensionPreset / ref:Dimensions` | Slide dimensions and aspect ratio. String shorthand such as 'widescreen' is equivalent to { preset: 'widescreen' }. |\n| `background` | no | `oneOf:ref:BackgroundShortcut / ref:Background` | Default slide background applied across the deck unless overridden on a slide (slides.N.design.background); the resolved theme's background is the fallback. A background is the only canvas fill and never moves content... |\n| `logo` | no | `oneOf:string / const:false` | Which logo covers, section dividers and picture bullets draw. Logos live on the organization (Organization.logo); without this field covers and sections draw the primary organization's full logo and picture bullets it... |\n| `watermark` | no | `oneOf:const:false / ref:Asset / ref:Watermark` | Optional decorative watermark applied across slides: an image or a text stamp, in the fixed frame and at the opacity described on Watermark. Use false to suppress an inherited watermark in slide-level design; a string... |\n| `header` | no | `oneOf:const:false / ref:HeaderFooter` | Repeated header furniture rendered outside the main slide content. Use false to suppress an inherited header. |\n| `footer` | no | `oneOf:const:false / ref:HeaderFooter` | Repeated footer furniture rendered outside the main slide content. Use false to suppress an inherited footer. |\n| `titleAlignment` | no | `enum:left \\| center \\| right` | Default horizontal alignment for the title heading area (the title and the tag) in resolved layouts. Effective value: the slide's design, then the deck's design, then the theme's design.titleAlignment, then the layout... |\n| `contentAlignment` | no | `enum:left \\| center \\| right` | Default horizontal alignment for body/content regions in resolved layouts. Effective value: the slide's design, then the deck's design, then the theme's design.contentAlignment, then the layout record's design.content... |\n| `contentBox` | no | `boolean` | Whether body/content regions are rendered inside a visible card or surface. Effective value: the slide's design, then the deck's design, then the theme's design.contentBox, then the layout record's design.contentBox,... |\n| `contentDirection` | no | `enum:horizontal \\| vertical` | Axis along which parallel body content is arranged: 'vertical' is a column, 'horizontal' a row. Effective value: the slide's design, then the deck's design, then the theme's design.contentDirection, then the layout re... |\n| `chartPrimary` | no | `enum:none \\| top \\| bottom \\| left \\| right` | Where the primary chart sits relative to supporting content. Effective value: slide design, then deck design, then the layout record's design.chartPrimary. It applies to automatic slides only; a template layout says t... |\n| `imageFit` | no | `enum:cover \\| contain \\| stretch` | Default fit of image blocks (and Slide.image) that set no fit of their own: 'cover' fills the frame and crops what overflows around the block's focus, 'contain' shows the whole picture centered in the frame, 'stretch'... |\n| `listBullet` | no | `enum:character \\| image` | Marker style for items and bullets lists. Effective value: the slide's design, then the deck's design, then the theme's design.listBullet, then the layout record's design.listBullet, then 'character'. 'character' (the... |\n| `mirror` | no | `boolean` | Draw the slide's template layout mirrored (OPF 0.19): each row's cells and the column sizes reversed, so a 'beside' layout puts its picture, chart or table at the end. Logical: a right-to-left deck mirrors on top. Bin... |\n\n\n### ColorScheme\n\n- Type: `object`\n- Required fields: none\n- Purpose: Color palette used by the design system. Every slot and role is a hex color ('#RGB', '#RRGGBB' or '#RRGGBBAA'); a color-scheme name or a 'var:<id>' reference is not valid here, because those resolve through the scheme. The slot fields (accent1-accent6, dark1, dark2, light1, light2, hyperlink, followedHyperlink) mirror color-scheme.schema.json (https://openpresentation.org/schema/opf-color-scheme/v1) so library records and inline OPF overrides are interchangeable on those fields. Two parallel...\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `id` | no | `ref:CatalogReference` | Colour-scheme reference (a bare id or 'name:id') resolved as the base; slot and role overrides on the surrounding ColorScheme object take precedence over the resolved scheme. |\n| `accent1` | no | `ref:HexColor` | Accent 1 color (hex). Mirrors the OOXML accent1 slot. |\n| `accent2` | no | `ref:HexColor` | Accent 2 color (hex). Mirrors the OOXML accent2 slot. |\n| `accent3` | no | `ref:HexColor` | Accent 3 color (hex). Mirrors the OOXML accent3 slot. |\n| `accent4` | no | `ref:HexColor` | Accent 4 color (hex). Mirrors the OOXML accent4 slot. |\n| `accent5` | no | `ref:HexColor` | Accent 5 color (hex). Mirrors the OOXML accent5 slot. |\n| `accent6` | no | `ref:HexColor` | Accent 6 color (hex). Mirrors the OOXML accent6 slot. |\n| `dark1` | no | `ref:HexColor` | Dark 1 color (hex). Typically the deepest neutral; OOXML dark1. |\n| `dark2` | no | `ref:HexColor` | Dark 2 color (hex). Secondary dark; OOXML dark2. |\n| `light1` | no | `ref:HexColor` | Light 1 color (hex). Typically the slide canvas; OOXML lt1. |\n| `light2` | no | `ref:HexColor` | Light 2 color (hex). Secondary light surface; OOXML lt2. |\n| `hyperlink` | no | `ref:HexColor` | Hyperlink color (hex). OOXML hlink. Link runs with no color of their own are drawn underlined in it, in the preview and in PowerPoint, unless it has under 4.5:1 contrast against the slide background, then in the slide... |\n| `followedHyperlink` | no | `ref:HexColor` | Followed-hyperlink color (hex). OOXML folHlink. |\n| `primary` | no | `ref:HexColor` | Abstract role: primary brand color (hex). The engine maps this onto an OOXML accent slot when serializing. |\n| `secondary` | no | `ref:HexColor` | Abstract role: secondary brand color (hex). |\n| `accent` | no | `ref:HexColor` | Abstract role: accent color used for highlights and emphasis (hex). |\n| `background` | no | `ref:HexColor` | Abstract role: default slide background color (hex), used when the design names no single-color background (and for gradient and picture backgrounds). Overrides light1 as that default. |\n| `surface` | no | `ref:HexColor` | Abstract role: color for elevated surfaces such as cards and panels (hex). Overrides the default of light2 (dark2 on a dark slide). |\n| `text` | no | `ref:HexColor` | Abstract role: primary body text color (hex). Overrides dark1 on a light slide only; a dark slide always uses light1. |\n| `textSecondary` | no | `ref:HexColor` | Abstract role: secondary or muted text color used for captions and supporting copy (hex). Overrides the default of dark2 (light2 on a dark slide). |\n\n\n### FontScheme\n\n- Type: `object`\n- Required fields: none\n- Purpose: Typography selections used by the design system. The pair fields (major, minor) and refinement fields (type, app, languageFamily) mirror font-scheme.schema.json (https://openpresentation.org/schema/opf-font-scheme/v1) so library records and inline OPF overrides are interchangeable on those fields. Two parallel models are supported and may be mixed: - OOXML pairs (major, minor) - heading and body family names that round-trip directly to PowerPoint majorFont/minorFont entries. - Abstract roles...\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `id` | no | `ref:CatalogReference` | Font-scheme reference (a bare id or 'name:id') resolved as the base; field overrides on the surrounding FontScheme object take precedence over the resolved scheme. |\n| `major` | no | `string` | Heading (major) font family mirrors the OOXML majorFont entry. Pairs with 'minor'. |\n| `minor` | no | `string` | Body (minor) font family mirrors the OOXML minorFont entry. Pairs with 'major'. |\n| `eastAsian` | no | `object` | East Asian script fonts. Maps to the OOXML a:ea element of majorFont (major) and minorFont (minor), and to run-level a:ea. When set, they fill the eastAsian slot for every language; when omitted, the slot comes from t... |\n| `complexScript` | no | `object` | Complex-script fonts (for example Arabic, Hebrew, Indic and Thai). Maps to the OOXML a:cs element of majorFont (major) and minorFont (minor), and to run-level a:cs. When set, they fill the complexScript slot for every... |\n| `type` | no | `enum:sans-serif \\| serif \\| monospace` | High-level typographic class of the scheme. |\n| `app` | no | `enum:powerpoint \\| google-slides` | Target application this font pairing is intended for. Metadata for pickers and catalog filters: no engine changes its output by it. |\n| `languageFamily` | no | `enum:latin \\| ea \\| cs \\| eastAsian \\| complexScript` | Font-language family this scheme is intended for: 'latin' for Latin-script content, 'ea' (or 'eastAsian', the same value) for East Asian scripts, 'cs' (or 'complexScript', the same value) for Complex Scripts. The long... |\n| `heading` | no | `string` | Abstract role: font family name used for slide titles and headings. Maps onto the OOXML major slot when serializing. |\n| `body` | no | `string` | Abstract role: font family name used for body copy. Maps onto the OOXML minor slot when serializing. |\n| `accent` | no | `string` | Abstract role: font family name used for accent text. When set, the slide tag (eyebrow) and the quote body use this family instead of the body and heading families; nothing else changes. resolveFontFamilies() returns... |\n| `code` | no | `string` | Abstract role: monospaced font family name used for code blocks and for inline code runs (TextRun.code). No direct OOXML slot. Resolution: this override, then the resolved font-scheme record's 'code' (for example Cons... |\n\n\n### SlideDesign\n\n- Type: `allOf:ref:Design + schema`\n- Required fields: none\n- Purpose: A slide's design: every Design field except dimensions. A PPTX has one slide size, so the size is set once, on the deck's design.dimensions (or its theme), and a slide's design cannot set it. A slide-level theme whose resolved dimensions differ from the deck's is a validate warning (opf/slide-theme-dimensions); exporting such a deck to PPTX fails with mixed-slide-dimensions.\n\n_No named properties._\n\n\n### DimensionPreset\n\n- Type: `enum:16:9 | 4:3 | 16:10 | 1:1 | 4:5 | 9:16 | letter | a4 | widescreen | standard`\n- Required fields: none\n- Purpose: Named preset; sets aspect ratio and physical size. 16:9 ('widescreen'): 13.333 x 7.5 in, PowerPoint's Widescreen. 4:3 ('standard'): 10 x 7.5 in. 16:10: 10 x 6.25 in. letter (11 x 8.5 in) and a4 (11.69 x 8.27 in) are paper sizes, not PowerPoint's Letter Paper or A4 Paper. 1:1, 4:5 and 9:16 are 7.5 in on the short edge.\n\n_No named properties._\n\n\n### Dimensions\n\n- Type: `object`\n- Required fields: none\n- Purpose: Slide dimensions: a preset, or custom inches that override it. PowerPoint's Letter Paper (10 x 7.5 in), A4 Paper (10.8333 x 7.5 in) and On-screen Show (16:9) (10 x 5.625 in) are not OPF presets; use widthInches and heightInches. docs/slide-sizes.md compares every preset.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `preset` | no | `ref:DimensionPreset` | |\n| `widthInches` | no | `number` | Custom slide width in inches; overrides the preset width when provided. |\n| `heightInches` | no | `number` | Custom slide height in inches; overrides the preset height when provided. |\n\n\n### ThemeBackgroundSlot\n\n- Type: `enum:light1 | light2 | dark1 | dark2`\n- Required fields: none\n- Purpose: PowerPoint theme-controlled slide background slot from the active color scheme. These are slots, not assumptions about actual colors: light1 is usually white and dark1 is usually black by convention, but the color scheme controls the real values.\n\n_No named properties._\n\n\n### HexColor\n\n- Type: `string`\n- Required fields: none\n- Purpose: Hex color shorthand accepted by selected string fields.\n\n_No named properties._\n\n\n### ColorRef\n\n- Type: `anyOf:ref:HexColor / enum:accent1 | accent2 | accent3 | accent4 | accent5 | accent6 | dark1 | dark2 | light1 | light2 | hyperlink | followedHyperlink | primary | secondary | accent | background | surface | surfaceAlt | text | textSecondary / string`\n- Required fields: none\n- Purpose: A color value or reference, enforced on styled table cell fill and text colors, cell border colors, solid background colors, gradient stop colors and pattern foreground and background colors. Three forms: - Literal hex: '#RGB', '#RRGGBB', or '#RRGGBBAA'. - Color-scheme name, resolved through the effective color scheme after design resolution: an OOXML slot ('accent1'-'accent6', 'dark1', 'dark2', 'light1', 'light2', 'hyperlink', 'followedHyperlink') or an abstract role ('primary', 'secondary',...\n\n_No named properties._\n\n\n### Variables\n\n- Type: `object`\n- Required fields: none\n- Purpose: Named variables, keyed by stable kebab-case id. A variable is a typed, named value the deck declares once and uses in many places: a color ('var:<id>' in color fields, the original use), or content that fills a template (text, number, date, image, url, list). Content is referenced inline as '{{<id>}}' inside any string, or whole as 'var:<id>' in a field of the matching type; '\\{{' writes a literal '{{'. A hex string is shorthand for a color variable. A variable with no 'value' is unfilled: ex...\n\n_No named properties._\n\n\n### Variable\n\n- Type: `oneOf:ref:HexColor / ref:ColorVariable / ref:TextVariable / ref:NumberVariable / ref:DateVariable / ref:ImageVariable / ref:UrlVariable / ref:ListVariable`\n- Required fields: none\n- Purpose: A single named variable: a hex string (shorthand for a color variable) or an object whose 'type' is color, text, number, date, image, url or list. 'value' is the current value and is optional: a variable with no value is unfilled, which a template allows and a normal deck does not. 'example' only illustrates the slot (fill forms, template previews) and never reaches output.\n\n_No named properties._\n\n\n### ColorVariable\n\n- Type: `object`\n- Required fields: `type`\n- Purpose: A named color. Content color fields reference it as 'var:<id>'. Colors resolve at render time through the ordinary color-reference path, so a color variable keeps working as before.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `type` | yes | `const:\"color\"` | Variable kind. One of color, text, number, date, image, url or list. |\n| `value` | no | `ref:HexColor` | Hex color this variable resolves to. |\n| `required` | no | `boolean` | Whether the variable must be filled. Defaults to true: a variable with no 'value' is unfilled, and an unfilled required variable is an error in a normal deck and expected in a template. Set false for an optional slot:... |\n| `label` | no | `string` | Optional short human label for forms and fill panels. |\n| `description` | no | `string` | Optional prose describing what the variable is for, surfaced by pickers, fill forms and agents. |\n| `example` | no | `ref:HexColor` | Illustrative color shown in fill forms and used when a template is previewed with examples. Never written to output. |\n\n\n### TextVariable\n\n- Type: `object`\n- Required fields: `type`\n- Purpose: Text content. Plain string or rich TextRun[]. Insert it inline as '{{<id>}}' inside any string (rich text is flattened to plain text there), or reference it whole as 'var:<id>' in a field that accepts string or TextRun[] (the rich runs are kept).\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `type` | yes | `const:\"text\"` | Variable kind. One of color, text, number, date, image, url or list. |\n| `value` | no | `oneOf:string / array<ref:TextRun>` | Text this variable resolves to: a plain string, or TextRun[] for rich text. |\n| `required` | no | `boolean` | Whether the variable must be filled. Defaults to true: a variable with no 'value' is unfilled, and an unfilled required variable is an error in a normal deck and expected in a template. Set false for an optional slot:... |\n| `label` | no | `string` | Optional short human label for forms and fill panels. |\n| `description` | no | `string` | Optional prose describing what the variable is for, surfaced by pickers, fill forms and agents. |\n| `example` | no | `oneOf:string / array<ref:TextRun>` | Illustrative text shown in fill forms and used when a template is previewed with examples. Never written to output. |\n\n\n### NumberVariable\n\n- Type: `object`\n- Required fields: `type`\n- Purpose: A number. '{{<id>}}' inserts it as text using 'format'; 'var:<id>' as a whole field supplies the number itself (chart values, font sizes).\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `type` | yes | `const:\"number\"` | Variable kind. One of color, text, number, date, image, url or list. |\n| `value` | no | `number` | Number this variable resolves to. |\n| `required` | no | `boolean` | Whether the variable must be filled. Defaults to true: a variable with no 'value' is unfilled, and an unfilled required variable is an error in a normal deck and expected in a template. Set false for an optional slot:... |\n| `label` | no | `string` | Optional short human label for forms and fill panels. |\n| `description` | no | `string` | Optional prose describing what the variable is for, surfaced by pickers, fill forms and agents. |\n| `example` | no | `number` | Illustrative number shown in fill forms and used when a template is previewed with examples. Never written to output. |\n| `format` | no | `ref:NumberFormat` | Display pattern used by '{{<id>}}' (the shared NumberFormat syntax). A literal prefix, a numeric part of '#', '0', ',' and '.', and a literal suffix. '0' pads digits, '#' is optional, ',' groups thousands, digits afte... |\n\n\n### NumberFormat\n\n- Type: `string`\n- Required fields: none\n- Purpose: Number display pattern, shared by NumberVariable.format, data column formats (DataColumn.format) and table cell formats (StyledTableCell.format). An optional literal prefix, a numeric part of '#', '0', ',' and '.', and an optional literal suffix. '0' pads digits, '#' is optional, ',' groups thousands, digits after '.' fix the decimals ('0' required, '#' optional), and a '%' in the prefix or suffix multiplies the value by 100. English separators only. A format applies only to number values; st...\n\n_No named properties._\n\n\n### DateVariable\n\n- Type: `object`\n- Required fields: `type`\n- Purpose: A calendar date as an ISO YYYY-MM-DD string. No time zone and no clock are involved. '{{<id>}}' inserts it formatted with 'format'.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `type` | yes | `const:\"date\"` | Variable kind. One of color, text, number, date, image, url or list. |\n| `value` | no | `string` | ISO calendar date (YYYY-MM-DD) this variable resolves to. |\n| `required` | no | `boolean` | Whether the variable must be filled. Defaults to true: a variable with no 'value' is unfilled, and an unfilled required variable is an error in a normal deck and expected in a template. Set false for an optional slot:... |\n| `label` | no | `string` | Optional short human label for forms and fill panels. |\n| `description` | no | `string` | Optional prose describing what the variable is for, surfaced by pickers, fill forms and agents. |\n| `example` | no | `string` | Illustrative ISO date shown in fill forms and used when a template is previewed with examples. Never written to output. |\n| `format` | no | `string` | Date display pattern, the same LDML-style tokens as header/footer dateFormat: yyyy, yy, MMMM, MMM, MM, M, dd, d, EEEE, EEE and quoted literals. English names. Default 'MMMM d, yyyy'. |\n\n\n### ImageVariable\n\n- Type: `object`\n- Required fields: `type`\n- Purpose: An image source: any Asset (an 'asset:<id>' reference, HTTPS URL, data URI, relative or local path, or an object with src, alt and metadata). Reference it whole as 'var:<id>' in an image, asset or src field; '{{<id>}}' inserts the source string.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `type` | yes | `const:\"image\"` | Variable kind. One of color, text, number, date, image, url or list. |\n| `value` | no | `ref:Asset` | Image source this variable resolves to. |\n| `required` | no | `boolean` | Whether the variable must be filled. Defaults to true: a variable with no 'value' is unfilled, and an unfilled required variable is an error in a normal deck and expected in a template. Set false for an optional slot:... |\n| `label` | no | `string` | Optional short human label for forms and fill panels. |\n| `description` | no | `string` | Optional prose describing what the variable is for, surfaced by pickers, fill forms and agents. |\n| `example` | no | `ref:Asset` | Illustrative image source shown in fill forms and used when a template is previewed with examples. Never written to output. |\n\n\n### UrlVariable\n\n- Type: `object`\n- Required fields: `type`\n- Purpose: A link target (http, https, mailto or tel). Use it as '{{<id>}}' inside a link string or reference it whole as 'var:<id>' in a link field.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `type` | yes | `const:\"url\"` | Variable kind. One of color, text, number, date, image, url or list. |\n| `value` | no | `string` | Link target this variable resolves to. |\n| `required` | no | `boolean` | Whether the variable must be filled. Defaults to true: a variable with no 'value' is unfilled, and an unfilled required variable is an error in a normal deck and expected in a template. Set false for an optional slot:... |\n| `label` | no | `string` | Optional short human label for forms and fill panels. |\n| `description` | no | `string` | Optional prose describing what the variable is for, surfaced by pickers, fill forms and agents. |\n| `example` | no | `string` | Illustrative link shown in fill forms and used when a template is previewed with examples. Never written to output. |\n\n\n### ListVariable\n\n- Type: `object`\n- Required fields: `type`\n- Purpose: A list of strings, for bullets and list items. A whole-string array element 'var:<id>' splices every entry into the array in place; a whole field 'var:<id>' becomes the array; '{{<id>}}' joins the entries with ', ' (or the separator after a pipe: '{{<id>|; }}').\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `type` | yes | `const:\"list\"` | Variable kind. One of color, text, number, date, image, url or list. |\n| `value` | no | `array<string>` | Entries this variable resolves to. |\n| `required` | no | `boolean` | Whether the variable must be filled. Defaults to true: a variable with no 'value' is unfilled, and an unfilled required variable is an error in a normal deck and expected in a template. Set false for an optional slot:... |\n| `label` | no | `string` | Optional short human label for forms and fill panels. |\n| `description` | no | `string` | Optional prose describing what the variable is for, surfaced by pickers, fill forms and agents. |\n| `example` | no | `array<string>` | Illustrative entries shown in fill forms and used when a template is previewed with examples. Never written to output. |\n\n\n### BackgroundShortcut\n\n- Type: `oneOf:ref:ThemeBackgroundSlot / ref:HexColor / ref:ImageSource`\n- Required fields: none\n- Purpose: String shorthand for a background. Theme slots ('light1', 'light2', 'dark1', 'dark2') are equivalent to { type: 'theme', slot: value }; hex colors are equivalent to { type: 'solid', color: value }; an image source (starting with 'asset:', 'https://', 'data:', './' or '../') is equivalent to { type: 'image', src: value }, a cover image. Any other string is invalid.\n\n_No named properties._\n\n\n### ImageSource\n\n- Type: `string`\n- Required fields: none\n- Purpose: An image source in a string shorthand: an 'asset:<id>' reference, an HTTPS URL, a data URI, or a relative path starting with './' or '../' (resolved against the OPF file location).\n\n_No named properties._\n\n\n### Background\n\n- Type: `oneOf:ref:ThemeBackground / ref:SolidBackground / ref:GradientBackground / ref:ImageBackground / ref:PatternBackground`\n- Required fields: none\n- Purpose: Background fill applied to slides. Theme backgrounds preserve PowerPoint's color-scheme background choice; other variants represent fixed background fills.\n\n_No named properties._\n\n\n### ThemeBackground\n\n- Type: `object`\n- Required fields: `type`, `slot`\n- Purpose: Theme-controlled PowerPoint slide background. The slot is resolved through the active color scheme and remains theme-aware.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `type` | yes | `const:\"theme\"` | Theme-controlled background fill. |\n| `slot` | yes | `ref:ThemeBackgroundSlot` | |\n\n\n### SolidBackground\n\n- Type: `object`\n- Required fields: `type`, `color`\n- Purpose: Fixed solid slide background fill.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `type` | yes | `const:\"solid\"` | Fixed solid background fill. |\n| `color` | yes | `ref:ColorRef` | Fixed solid fill color: a hex string, a color-scheme slot or role name, or a var:<id> variable reference (a ColorRef, resolved against the effective color scheme and the deck variables). Use { type: 'theme', slot: ...... |\n| `opacity` | no | `number` | Background opacity from 0 (fully transparent) to 1 (fully opaque). |\n\n\n### GradientBackground\n\n- Type: `object`\n- Required fields: `type`, `gradient`\n- Purpose: Fixed gradient slide background fill.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `type` | yes | `const:\"gradient\"` | Fixed gradient background fill. |\n| `gradient` | yes | `object` | Gradient fill definition. |\n| `opacity` | no | `number` | Background opacity from 0 (fully transparent) to 1 (fully opaque). |\n\n\n### ImageBackground\n\n- Type: `object`\n- Required fields: `type`, `src`\n- Purpose: Picture background: the image fills the whole canvas behind everything and never moves content (to push content aside, use an image block with placement). The string shorthand 'asset:x' (or any image source) is { type: 'image', src: 'asset:x' }, a cover image. Paint order: the colour scheme's default slide background, the picture, then the overlay, then furniture and content.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `type` | yes | `const:\"image\"` | Picture background. |\n| `src` | yes | `string` | Source for the background image: an 'asset:<id>' reference, HTTPS URL, data URI, or a relative path resolved against the OPF file location. |\n| `alt` | no | `string` | Alternative text. A background with alt is meaningful: PowerPoint export draws it as a full-slide picture at the back that carries the text, so screen readers see it. Without alt it is decorative and exports as the na... |\n| `fit` | no | `enum:cover \\| contain \\| stretch \\| tile` | How the picture fills the canvas: 'cover' fills it and crops what overflows around focus; 'contain' shows the whole picture centered over the colour scheme's default background; 'stretch' scales it to the canvas exact... |\n| `focus` | no | `ref:ImageFocus` | Point of the picture to keep in view when 'cover' crops it. Ignored by the other fits. |\n| `opacity` | no | `number` | Picture opacity from 0 (fully transparent) to 1 (fully opaque); the overlay keeps its own opacity. Default 1. |\n| `recolor` | no | `ref:ImageRecolor` | Grayscale or duotone treatment of the picture's pixels, as on an image block: recolor and opacity apply to the pixels, then the overlay is drawn above. |\n| `overlay` | no | `ref:Overlay` | Scrim over the whole picture or a band along one edge (edge, size), beneath furniture and content, for example to keep a title readable over a busy photo. |\n\n\n### ImageFocus\n\n- Type: `object`\n- Required fields: `x`, `y`\n- Purpose: A point of a picture as fractions of its width and height, from the top-left corner. A cover fit centers this point in the frame as far as the picture still covers the frame, so it always stays in view: { x: 0.5, y: 0.5 } (the default) is a center crop, { x: 0, y: 0 } keeps the top-left corner.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `x` | yes | `number` | Horizontal position, 0 (left edge) to 1 (right edge). |\n| `y` | yes | `number` | Vertical position, 0 (top edge) to 1 (bottom edge). |\n\n\n### Overlay\n\n- Type: `object`\n- Required fields: `color`, `opacity`\n- Purpose: Solid scrim over a picture: the whole frame in the frame's shape, or a band along one edge. Shared by image backgrounds and image blocks. Drawn directly above its picture and beneath headings and content; PowerPoint export writes one native shape above the picture.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `color` | yes | `ref:ColorRef` | Overlay fill color: a hex color (eight-digit hex keeps its alpha), a color-scheme slot or role name, or a 'var:<id>' variable reference. |\n| `opacity` | yes | `number` | Overlay fill opacity. |\n| `edge` | no | `enum:top \\| bottom \\| left \\| right` | Cover only a band along this edge of the frame, for example a caption strip. Backgrounds and rectangle image frames only; on another shape the overlay is not drawn and composition reports unsupported-image-treatment.... |\n| `size` | no | `number` | Band share of the frame for an edge overlay. Default 0.3. Ignored without edge. |\n\n\n### ImagePlacement\n\n- Type: `object`\n- Required fields: `edge`\n- Purpose: Bleeds an image block to one edge of the slide: the image takes a band along that edge, edge to edge, and the headings and other content compose in the rest of the slide at the normal padding. Only top-level blocks (slides.N.blocks.I) can be placed, at most one per edge. Template layout regions do not supply image placement; set it on the image block itself. Bands are taken in block order, each spanning the free area left by earlier ones. Placement is the only mechanism that moves content asi...\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `edge` | yes | `enum:left \\| right \\| top \\| bottom` | The slide edge the image bleeds to. |\n| `size` | no | `number` | Share of the slide width (left, right) or height (top, bottom) given to the band. Default 0.5. |\n| `inset` | no | `boolean` | Draw the frame inside the slide padding on every side of the band, like a card, instead of edge to edge. Default false. |\n\n\n### ImageBorder\n\n- Type: `object`\n- Required fields: `color`, `width`\n- Purpose: Solid line along the image frame's shape, centered on the outline with a miter join, like a native picture line. A thick dark border on a rounded portrait frame gives a device bezel.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `color` | yes | `ref:ColorRef` | Line color: a hex color, a color-scheme slot or role name, or a 'var:<id>' variable reference. |\n| `width` | yes | `number` | Line width in reference pixels at a 720-pixel short edge. 0 removes the line. |\n\n\n### ImageRecolor\n\n- Type: `oneOf:const:\"grayscale\" / object`\n- Required fields: none\n- Purpose: Color treatment for the image pixels. Luminance uses Rec. 601 weights (0.299, 0.587, 0.114) on sRGB values.\n\n_No named properties._\n\n\n### PatternBackground\n\n- Type: `object`\n- Required fields: `type`, `pattern`\n- Purpose: Fixed pattern slide background fill.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `type` | yes | `const:\"pattern\"` | Fixed pattern background fill. |\n| `pattern` | yes | `object` | Pattern fill definition. |\n| `opacity` | no | `number` | Background opacity from 0 (fully transparent) to 1 (fully opaque). |\n\n\n### OrganizationLogo\n\n- Type: `object`\n- Required fields: none\n- Purpose: An organization's logo in up to four shapes. Each shape is a path or Asset, or { onLight, onDark } for the two backgrounds. A missing shape falls back to full, and full to the first defined of wordmark, stacked and icon; within a shape a missing onLight or onDark uses the other. Shapes are placed with slide-scoped references such as 'var:organization.logo.icon'.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `full` | no | `ref:LogoSource` | The full logo (lockup): mark and name side by side. Drawn on cover and section slides, and placed by 'var:organization.logo'. |\n| `stacked` | no | `ref:LogoSource` | The stacked lockup: mark above the name, for portrait or square slots. Placed by 'var:organization.logo.stacked'. |\n| `icon` | no | `ref:LogoSource` | The mark or symbol without the name, for tight spaces such as header and footer zones and picture bullets. Placed by 'var:organization.logo.icon'. |\n| `wordmark` | no | `ref:LogoSource` | The name set in the brand's lettering, without the mark. Placed by 'var:organization.logo.wordmark'. |\n\n\n### LogoSource\n\n- Type: `oneOf:ref:Asset / object`\n- Required fields: none\n- Purpose: One logo shape: a path or Asset for every background, or { onLight, onDark }. onLight is drawn on light backgrounds (usually dark artwork) and onDark on dark backgrounds (usually light artwork), by each slide's background; a missing one uses the other.\n\n_No named properties._\n\n\n### Watermark\n\n- Type: `oneOf:schema / schema`\n- Required fields: `opacity`\n- Purpose: Decorative watermark: an image ('src') or a text stamp ('text'), exactly one of the two, with its opacity. An image is drawn once per slide, contained and centered in a fixed frame (the middle 40% of the slide width and height, from 30% to 70% on each axis). A text watermark is one line of text in the heading font and the theme text color at the given opacity, centered on the slide and rotated 30 degrees counterclockwise (rising to the right), sized to span at most 70% of the slide width and...\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `src` | no | `string` | Source for the watermark image: an 'asset:<id>' reference, HTTPS URL, data URI, relative path or local path, as for Asset.src. Exactly one of src and text is set. |\n| `text` | no | `string` | Text for a text watermark, such as DRAFT or CONFIDENTIAL; line breaks are drawn as spaces. Exactly one of src and text is set. |\n| `opacity` | yes | `number` | Watermark opacity from 0 (fully transparent) to 1 (fully opaque). |\n\n\n### HeaderFooter\n\n- Type: `object`\n- Required fields: none\n- Purpose: Repeated header or footer content split into left, center, and right zones. Header/footer content is slide furniture, separate from the main slide content payloads.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `left` | no | `ref:HeaderFooterItem` | Left-aligned header/footer content. |\n| `center` | no | `ref:HeaderFooterItem` | Centered header/footer content. |\n| `right` | no | `ref:HeaderFooterItem` | Right-aligned header/footer content. |\n\n\n### HeaderFooterItem\n\n- Type: `object`\n- Required fields: none\n- Purpose: One header/footer zone. Every configured field renders, side by side in one row in the order image, text, socials, date (mirrored in a right-to-left deck), aligned to the zone's edge (left, center or right) and vertically centered on each other; a line break inside text or socials ('\\n') stacks lines within that part. When the parts are wider than the zone their text wraps, and a row that cannot fit even so is reported as overflow. Generated values such as the slide number, the slide count, t...\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `text` | no | `string` | Text rendered in this zone. Variables resolve inside it like any string: deck-wide ones ('{{organization.name}}', '{{speaker.name}}, {{speaker.title}}', '{{customer}}') before composition, and the slide-scoped built-i... |\n| `image` | no | `ref:Asset` | Image rendered first in this zone's row, such as a logo, partner mark, certification badge, or icon: a path, URL or Asset, or an organization logo reference ('var:organization.logo.icon', 'var:organization.<id>.logo.w... |\n| `date` | no | `oneOf:boolean / string` | true renders the current date: the renderer or exporter must be given an explicit ISO date by its host (core never reads a clock). PPTX export writes a native date field only for a supported dateFormat whose complete... |\n| `dateFormat` | no | `string` | Date pattern for date. Tokens: yyyy (2026), yy (26), MMMM (April), MMM (Apr), MM (04), M (4), dd (09), d (9), EEEE (Thursday), EEE (Thu). Text in single quotes and other non-letter characters are literal. Month and we... |\n| `socials` | no | `boolean` | Whether to render the primary organization's social profiles from organization.socials, one line per platform in key order. A handle is formatted through the platform's socialPlatforms record (companyUrlPattern, else... |\n\n\n### Slide\n\n- Type: `object`\n- Required fields: none\n- Purpose: A single slide. Content can be authored as a full-slide root payload, or inside promoted named region keys such as 'left', 'center+right', and 'top:left'.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `id` | no | `string` | Optional stable identifier for the slide within the document. Use when another system needs to reference a slide across edits, comments, generation state, exports, or narrative tooling. Slide order is defined by the s... |\n| `type` | no | `enum:text \\| list \\| image \\| chart \\| table \\| video \\| code \\| metric \\| quote \\| timeline` | Optional full-slide content kind. When omitted, engines infer the kind from root payload fields. |\n| `beat` | no | `oneOf:string / array<string>` | Optional reference to one or more beats of the deck's narrative (each value is the id of a beat in the narrative record that the root 'narrative' resolves to). A single string declares the slide's primary beat; an arr... |\n| `layout` | no | `ref:CatalogReference` | Optional layout: a layouts reference, a bare id ('pillars') or 'name:id' ('acme:hero'), or 'auto'. When omitted or 'auto', the slide is composed with no layout record: engines arrange title, subtitle, tag and content... |\n| `title` | no | `oneOf:string / array<ref:TextRun>` | Slide-level title content. A v2 layout renders this in its title heading area, or an implicit heading row when that area is absent. Use a string for plain text or TextRun[] for inline rich text (a colored word, bold,... |\n| `subtitle` | no | `oneOf:string / array<ref:TextRun>` | Slide-level subtitle or supporting line. A v2 layout renders this in its subtitle area when present, otherwise in the title heading group. Use a string for plain text or TextRun[] for inline rich text (a colored word,... |\n| `tag` | no | `oneOf:string / array<ref:TextRun>` | Small slide-level label or badge. A v2 layout renders this before the title in its title heading group; tag is not a separate layout area. Use a string for plain text or TextRun[] for inline rich text (a colored word,... |\n| `text` | no | `oneOf:string / array<ref:TextRun>` | Full-slide text payload. Use a string for plain text or TextRun[] for inline rich text. TextRun items may be plain strings or formatted run objects. |\n| `items` | no | `array<ref:ListItem>` | Full-slide structured list payload. Presence of this field infers type 'list'. Entries are strings, TextRun[] or objects with `text`, optional `description` (supporting detail under the entry), `level` and `start`. Us... |\n| `bullets` | no | `array<ref:BulletItem>` | Full-slide prose bullet payload: short talking points, each standing alone. Presence of this field infers type 'text', so it may sit beside `text` in one payload. Entries are strings, TextRun[] or objects with `text`,... |\n| `numbering` | no | `ref:NumberingSpec` | Number the full-slide `items` or `bullets` instead of bulleting them. A style name (arabic, roman-upper, roman-lower, alpha-upper, alpha-lower) or a Numbering object applies to every list level; an array gives one ent... |\n| `image` | no | `ref:Asset` | Shorthand for one image block: the picture composes as body content, like { type: 'image', image: value }, with the effective design.imageFit. A v2 layout binds it to a compatible body region; a region's bleed setting... |\n| `video` | no | `ref:Asset` | Full-slide video source. Presence of this field infers type 'video'. Engines do not play video: the preview draws a placeholder (a play badge with the asset's title or source as its caption), and PPTX export draws the... |\n| `chart` | no | `ref:Chart` | Full-slide chart payload. Presence of this field infers type 'chart'. |\n| `table` | no | `ref:Table` | Full-slide table payload. Presence of this field infers type 'table'. |\n| `code` | no | `oneOf:string / ref:Code` | Full-slide code payload. A string is shorthand for { \"source\": value }; object form carries optional syntax language and filename metadata. |\n| `metric` | no | `oneOf:string / number / ref:Metric` | Full-slide metric payload. A string or number is shorthand for { \"value\": value }; object form carries optional label, description, unit, delta, trend, and sentiment metadata. Numeric values remain numbers; renderers... |\n| `quote` | no | `oneOf:string / ref:Quote` | Full-slide quote payload. A string is shorthand for { \"text\": value }; object form carries optional attribution and source metadata. Presence of this field infers type 'quote'. |\n| `timeline` | no | `ref:Timeline` | Full-slide timeline payload. An array is shorthand for { \"events\": value }; object form carries optional name and description metadata. Presence of this field infers type 'timeline'. |\n| `caption` | no | `ref:Caption` | Caption for the slide's root image, chart, table or video payload. Valid only when the slide root holds exactly one of those payloads. |\n| `blocks` | no | `array<ref:ContentPayload>` | Layout-agnostic content blocks rendered together as a composed payload when exact placement is unspecified. At slide root, multiple content payload kinds with no explicit type, blocks, or regions are accepted as short... |\n| `design` | no | `ref:SlideDesign` | Slide-level design applied on top of the deck-wide design. |\n| `left` | no | `ref:ContentPayload` | |\n| `center` | no | `ref:ContentPayload` | |\n| `right` | no | `ref:ContentPayload` | |\n| `left+center` | no | `ref:ContentPayload` | |\n| `center+right` | no | `ref:ContentPayload` | |\n| `left+center+right` | no | `ref:ContentPayload` | |\n| `top` | no | `ref:ContentPayload` | |\n| `middle` | no | `ref:ContentPayload` | |\n| `bottom` | no | `ref:ContentPayload` | |\n| `top+middle` | no | `ref:ContentPayload` | |\n| `middle+bottom` | no | `ref:ContentPayload` | |\n| `top+middle+bottom` | no | `ref:ContentPayload` | |\n| `top:left` | no | `ref:ContentPayload` | |\n| `top:center` | no | `ref:ContentPayload` | |\n| `top:right` | no | `ref:ContentPayload` | |\n| `top:left+center` | no | `ref:ContentPayload` | |\n| `top:center+right` | no | `ref:ContentPayload` | |\n| `top:left+center+right` | no | `ref:ContentPayload` | |\n| `middle:left` | no | `ref:ContentPayload` | |\n| `middle:center` | no | `ref:ContentPayload` | |\n| `middle:right` | no | `ref:ContentPayload` | |\n| `middle:left+center` | no | `ref:ContentPayload` | |\n| `middle:center+right` | no | `ref:ContentPayload` | |\n| `middle:left+center+right` | no | `ref:ContentPayload` | |\n| `bottom:left` | no | `ref:ContentPayload` | |\n| `bottom:center` | no | `ref:ContentPayload` | |\n| `bottom:right` | no | `ref:ContentPayload` | |\n| `bottom:left+center` | no | `ref:ContentPayload` | |\n| `bottom:center+right` | no | `ref:ContentPayload` | |\n| `bottom:left+center+right` | no | `ref:ContentPayload` | |\n| `top+middle:left` | no | `ref:ContentPayload` | |\n| `top+middle:center` | no | `ref:ContentPayload` | |\n| `top+middle:right` | no | `ref:ContentPayload` | |\n| `top+middle:left+center` | no | `ref:ContentPayload` | |\n| `top+middle:center+right` | no | `ref:ContentPayload` | |\n| `top+middle:left+center+right` | no | `ref:ContentPayload` | |\n| `middle+bottom:left` | no | `ref:ContentPayload` | |\n| `middle+bottom:center` | no | `ref:ContentPayload` | |\n| `middle+bottom:right` | no | `ref:ContentPayload` | |\n| `middle+bottom:left+center` | no | `ref:ContentPayload` | |\n| `middle+bottom:center+right` | no | `ref:ContentPayload` | |\n| `middle+bottom:left+center+right` | no | `ref:ContentPayload` | |\n| `top+middle+bottom:left` | no | `ref:ContentPayload` | |\n| `top+middle+bottom:center` | no | `ref:ContentPayload` | |\n| `top+middle+bottom:right` | no | `ref:ContentPayload` | |\n| `top+middle+bottom:left+center` | no | `ref:ContentPayload` | |\n| `top+middle+bottom:center+right` | no | `ref:ContentPayload` | |\n| `top+middle+bottom:left+center+right` | no | `ref:ContentPayload` | |\n| `notes` | no | `string` | Speaker notes shown in presenter view. |\n| `section` | no | `string` | PowerPoint-style slide section label. Consecutive slides with the same value belong to the same section in presenter view, outlines, and PowerPoint section-aware exports. Also the slide-scoped built-in variable '{{sli... |\n| `hidden` | no | `boolean` | Whether the slide is hidden from the presented sequence. The player skips it, the PPTX export writes it as a hidden slide, and per-slide image and PDF output skips it unless the caller asks to include hidden slides (o... |\n| `composition` | no | `ref:Composition` | |\n| `extensions` | no | `object` | Custom data passthrough for agent workflows at slide scope; ignored by the engine but preserved across read/write round-trips. Use for review state, generation provenance, or authoring conventions such as { \"authoring... |\n\n\n### ContentPayload\n\n- Type: `allOf:schema + schema + schema + schema + schema + schema + schema + schema + schema + schema + schema + schema + schema`\n- Required fields: none\n- Purpose: A content leaf or recursively composed group. A group contains blocks and optional composition; it cannot mix blocks with leaf payload fields. Groups may nest up to 32 levels.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `id` | no | `string` | Optional stable identifier for this payload, unique among slide and payload ids in the document. Use when another system needs to address the payload across edits patch-style agent edits, comments, review state, or ge... |\n| `extensions` | no | `object` | Custom data passthrough for agent workflows at payload scope; ignored by the engine but preserved across read/write round-trips. |\n| `region` | no | `string` | Pin a root block (slides.N.blocks.I) to a region of the slide's template layout by name (OPF 0.19). The block goes to that region when the region exists, accepts the block's kind and has room; otherwise the pin is ign... |\n| `type` | no | `enum:text \\| list \\| image \\| chart \\| table \\| video \\| code \\| metric \\| quote \\| timeline \\| group` | Optional content kind. When omitted, engines infer the kind from the fields present. |\n| `text` | no | `oneOf:string / array<ref:TextRun>` | Text payload. Use a string for plain text or TextRun[] for inline rich text. TextRun items may be plain strings or formatted run objects. |\n| `items` | no | `array<ref:ListItem>` | Structured list payload (type 'list'). Each item is either a plain string, a TextRun[] rich text sequence, or a ListItem object with optional `description`. List nesting uses item.level rather than nested content payl... |\n| `bullets` | no | `array<ref:BulletItem>` | Prose bullet payload: short talking points, each standing alone. Presence of this field infers type 'text', so it may sit beside `text` in one payload. Entries have no `description`; use `items` for entries with suppo... |\n| `numbering` | no | `ref:NumberingSpec` | Number the payload's `items` or `bullets` instead of bulleting them. A style name (arabic, roman-upper, roman-lower, alpha-upper, alpha-lower) or a Numbering object applies to every list level; an array gives one entr... |\n| `columns` | no | `oneOf:const:\"auto\" / integer` | Lay the payload's `items` or `bullets` out in columns (OPF 0.19). 'auto' tries 1, 2 then 3 equal columns at the starting text size and uses the first count at which the list fits, as long as a column stays at least a... |\n| `image` | no | `ref:Asset` | Source for an image item. |\n| `fit` | no | `enum:cover \\| contain \\| stretch` | Image payloads only. How the picture fills its frame: 'cover' fills it and crops what overflows around focus; 'contain' shows the whole picture centered; 'stretch' scales it to the frame exactly. Default: the effectiv... |\n| `focus` | no | `ref:ImageFocus` | Image payloads only. Point of the picture to keep in view when 'cover' crops it. Ignored by the other fits. |\n| `aspectRatio` | no | `number` | Image payloads only. Width-to-height ratio of the frame: the frame becomes the largest centered box with this ratio inside the block's region, for example 2.39 for a cinematic letterbox or 0.5 for a phone-shaped frame... |\n| `shape` | no | `enum:rectangle \\| rounded \\| circle \\| hexagon` | Image payloads only. Mask applied to the frame, exported as the picture's native preset geometry (rect, roundRect, ellipse, hexagon). 'circle' makes the frame square. Default 'rectangle'. |\n| `cornerRadius` | no | `number` | Image payloads only. Corner radius for shape 'rounded' as a fraction of the frame's shorter side. Default 0.16667, PowerPoint's roundRect default. |\n| `border` | no | `ref:ImageBorder` | Image payloads only. Line along the frame's shape. |\n| `opacity` | no | `number` | Image payloads only. Picture opacity from 0 to 1, exported as a native alphaModFix. The border and overlay keep their own opacity. Default 1. |\n| `recolor` | no | `ref:ImageRecolor` | Image payloads only. Grayscale or duotone treatment of the picture's pixels. |\n| `overlay` | no | `ref:Overlay` | Image payloads only. Scrim over the picture in the frame's shape, or a band along one edge of a rectangle frame. |\n| `placement` | no | `ref:ImagePlacement` | Image payloads only, and only on a top-level block (slides.N.blocks.I), at most one per edge. Bleeds the image to one slide edge; the headings and the other blocks compose in the rest of the slide. |\n| `video` | no | `ref:Asset` | Source for a video item. Engines do not play video: the preview draws a placeholder (a play badge with the asset's title or source as its caption), and PPTX export draws the same placeholder as native shapes linked to... |\n| `chart` | no | `ref:Chart` | Chart payload. Presence of this field infers type 'chart'. |\n| `table` | no | `ref:Table` | Table payload. Presence of this field infers type 'table'. |\n| `code` | no | `oneOf:string / ref:Code` | Code payload. A string is shorthand for { \"source\": value }; object form carries optional syntax language and filename metadata. |\n| `metric` | no | `oneOf:string / number / ref:Metric` | Metric payload. A string or number is shorthand for { \"value\": value }; object form carries optional label, description, unit, delta, trend, and sentiment metadata. Numeric values remain numbers; renderers format them... |\n| `quote` | no | `oneOf:string / ref:Quote` | Quote payload. A string is shorthand for { \"text\": value }; object form carries optional attribution, role, photo and source metadata. |\n| `timeline` | no | `ref:Timeline` | Timeline payload ordered by narrative or chronology. |\n| `caption` | no | `ref:Caption` | Caption for an image, chart, table or video payload, composed inside the block's region (below the media by default). Invalid on other payload kinds and on groups. |\n| `blocks` | no | `array<ref:ContentPayload>` | Ordered children of a group. Each child is a leaf or another group. |\n| `composition` | no | `ref:Composition` | Arrangement within this group. Only minFontSize and overflow inherit from the parent; strict overflow cannot be weakened. |\n\n\n### Quote\n\n- Type: `object`\n- Required fields: `text`\n- Purpose: Quote content with optional attribution metadata. Use 'text' for the quoted text, 'attribution' for the credited person or organization, 'role' for that person's title and organization, 'photo' for their headshot and 'source' for a citation or URL. A string value in a quote field is shorthand for { \"text\": value }.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `text` | yes | `oneOf:string / array<ref:TextRun>` | Quoted text: a string, or TextRun[] for inline rich text (a colored phrase, bold, a link, or a citation or footnote marker). The engine draws the surrounding quotation marks. |\n| `attribution` | no | `string` | Person or organization credited for the quote. Drawn after the quote text, in the muted text color. |\n| `role` | no | `string` | Title and organization of the attributed person. Drawn on its own line under the attribution; without an attribution it stands alone. It does not replace the attribution, so the person's name belongs in 'attribution'. |\n| `photo` | no | `ref:Asset` | Headshot of the attributed person, drawn as a circle beside the attribution and role lines (on the logical start side: left in a left-to-right deck, right in a right-to-left deck). Give it alt text (validate's opf/mis... |\n| `source` | no | `string` | Optional quote source, citation, or URL. Follows the attribution and role after ' - '. |\n\n\n### Code\n\n- Type: `object`\n- Required fields: `source`\n- Purpose: Code content with optional rendering metadata. Use 'source' for the code text, 'language' for syntax highlighting, 'filename' when the rendered block should show a file label, and 'highlight' to emphasize lines. A string value in a code field is shorthand for { \"source\": value }.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `source` | yes | `string` | Source code text to display. |\n| `language` | no | `string` | Language identifier used for syntax highlighting. |\n| `filename` | no | `string` | Optional file label shown with the code block. |\n| `highlight` | no | `array<oneOf:integer / array<integer>>` | Source lines to emphasize, 1-based: each entry is a line number or an inclusive [start, end] range, so [3, [5, 7]] marks lines 3 and 5 to 7. Lines are counted by line break in 'source', so a line that wraps stays one... |\n\n\n### Metric\n\n- Type: `object`\n- Required fields: `value`\n- Purpose: Metric content with optional display metadata. Use 'value' for the primary value, 'label' for the metric name, 'description' for supporting context, 'unit' for a suffix/currency marker, 'delta' for change, 'trend' for direction, and 'sentiment' for whether the change is good news. A string or number value in a metric field is shorthand for { \"value\": value }; numeric values remain numbers and are formatted by renderers.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `value` | yes | `oneOf:string / number` | Primary metric value. |\n| `label` | no | `string` | Metric label. |\n| `description` | no | `string` | Optional supporting context for the metric. |\n| `unit` | no | `string` | Metric unit, suffix, or currency marker. |\n| `delta` | no | `oneOf:string / number` | Metric change value. |\n| `trend` | no | `enum:up \\| down \\| flat` | Metric trend direction. The preview and the PowerPoint export draw an arrow beside the trend word that always points the way the trend does (up, down or flat) and colour the arrow, the trend word and the delta text by... |\n| `sentiment` | no | `enum:positive \\| negative \\| neutral` | Whether the change is good news. Positive draws the trend arrow and the trend and delta text in green, negative in red and neutral in the neutral text colour, each kept at 4.5:1 contrast or more against the slide back... |\n\n\n### Timeline\n\n- Type: `oneOf:array<ref:TimelineEvent> / object`\n- Required fields: none\n- Purpose: Timeline content. An array is shorthand for { \"events\": value }; object form carries optional name and description metadata.\n\n_No named properties._\n\n\n### TimelineEvent\n\n- Type: `object`\n- Required fields: `what`\n- Purpose: A single event inside a timeline content payload.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `when` | no | `string` | Event time, date, or sequence label. Use ISO-like values when possible, but human labels are allowed for quarters, eras, and relative milestones. |\n| `what` | yes | `string` | Short event label. |\n| `description` | no | `string` | Optional event detail. |\n| `status` | no | `enum:done \\| current \\| planned` | Progress of the event, drawn from the deck's own colors by both the SVG renderer and the PPTX exporter. Absent means no status and the event is drawn as a plain filled marker with normal text, which is also how 'done'... |\n\n\n### ListItem\n\n- Type: `oneOf:string / array<ref:TextRun> / object`\n- Required fields: none\n- Purpose: A flat item inside a list. Strings cover the common case, TextRun[] supports inline rich text without an object wrapper, and object form adds description and nesting depth without creating nested slide content payloads.\n\n_No named properties._\n\n\n### BulletItem\n\n- Type: `oneOf:string / array<ref:TextRun> / object`\n- Required fields: none\n- Purpose: A flat bullet item. Strings cover the common case, TextRun[] supports inline rich text without an object wrapper, and object form adds nesting depth without list-item descriptions.\n\n_No named properties._\n\n\n### NumberingStyle\n\n- Type: `enum:arabic | roman-upper | roman-lower | alpha-upper | alpha-lower`\n- Required fields: none\n- Purpose: A list number style: 1, 2, 3; I, II, III; i, ii, iii; A, B, C; a, b, c. Alphabetic numbering past 26 repeats the letter as PowerPoint does (aa, bb, cc). Roman numerals stop at 3999; larger values are drawn in arabic with a numbering-adapted diagnostic.\n\n_No named properties._\n\n\n### Numbering\n\n- Type: `object`\n- Required fields: none\n- Purpose: Numbering of one list level.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `style` | no | `ref:NumberingStyle` | Number style. Default arabic. |\n| `start` | no | `integer` | First number counted at this level. Default 1. Native PowerPoint accepts 1 to 32767. |\n| `suffix` | no | `enum:period \\| paren \\| paren-both` | Text after the number: period (1.), paren (1)) or paren-both ((1)). Default period. |\n\n\n### NumberingSpec\n\n- Type: `oneOf:ref:NumberingStyle / ref:Numbering / array<oneOf:ref:NumberingStyle / ref:Numbering>`\n- Required fields: none\n- Purpose: The value of a numbering field: a style name or Numbering object for every level, or an array with one entry per level (at most 9, the native depth).\n\n_No named properties._\n\n\n### TextRun\n\n- Type: `oneOf:string / object`\n- Required fields: none\n- Purpose: A contiguous run of text. Strings cover unformatted spans; object form adds character formatting.\n\n_No named properties._\n\n\n### Caption\n\n- Type: `oneOf:string / array<ref:TextRun> / object`\n- Required fields: none\n- Purpose: A caption for an image, chart, table or video payload. A string or TextRun[] is the caption text placed below the media; object form adds the position and alignment.\n\n_No named properties._\n\n\n### Reference\n\n- Type: `object`\n- Required fields: `id`, `text`\n- Purpose: A source that runs cite with 'cite'. Cited references are listed in the footnote area of the slides that cite them, numbered per deck in order of first use; referencesSlide() builds an ordinary list slide of them.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `id` | yes | `string` | Identifier runs cite. Unique within the references list. |\n| `text` | yes | `oneOf:string / array<ref:TextRun>` | The reference as it is listed: a string or TextRun[] for inline rich text. |\n| `url` | no | `string` | Optional link for the reference; a references slide links its entry to it. |\n\n\n### Chart\n\n- Type: `object`\n- Required fields: `type`, `data`\n- Purpose: Chart content. The chart object keeps chart-specific fields together so slides and regions do not expose loose chart fields.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `type` | yes | `enum:100pct-stacked-area \\| 100pct-stacked-bar \\| 100pct-stacked-column \\| area \\| bar \\| box-and-whisker \\| column \\| combo \\| doughnut \\| filled-radar \\| funnel \\| histogram \\| line \\| line-with-markers \\| pareto \\| pie \\| radar \\| radar-with-markers \\| scatter \\| stacked-area \\| stacked-bar \\| stacked-column \\| stacked-line \\| stacked-line-with-markers \\| treemap \\| waterfall \\| world` | Chart type, an engine vocabulary validated directly (no catalog lookup): the chart types Aspose.Slides officially supports, plus 'combo' (clustered columns with line series, see 'line' and 'secondaryAxis'). Each engin... |\n| `data` | yes | `oneOf:ref:ChartData / ref:DatasetRef` | Chart data. Inline data uses a tabular columns/rows shape; renderers convert rows to chart series internally. A DatasetRef ({ \"dataset\": \"<id>\" }) plots a top-level dataset instead. Data from a spreadsheet or file is... |\n| `mapping` | no | `ref:ChartMapping` | Optional series mapping by column name: which column is the category, which is the X column of a scatter chart, and which columns are plotted, in order. Absent keeps the positional rule (first column the category, the... |\n| `line` | no | `array<string>` | Combo charts only (type 'combo'): the plotted series, by column name, that are drawn as lines with markers; every other plotted series is drawn as clustered columns. Absent: the last plotted series is the line. A comb... |\n| `secondaryAxis` | no | `array<string>` | Combo charts only: line series, by column name, plotted against a secondary value axis at the right of the plot. That axis has its own scale, and its tick labels use the number format of the first secondary series' co... |\n| `axisTitles` | no | `ref:ChartAxisTitles` | Optional axis titles. Absent keeps today's untitled axes. Supported on the chart types that have a category/value (or X/Y) axis pair (column, bar, line, area, scatter, and the histogram, pareto, waterfall and box-and-... |\n| `legend` | no | `enum:none \\| top \\| bottom \\| left \\| right` | Optional legend position. 'none' hides the legend. Absent keeps today's behaviour exactly (a legend at the right of multi-series charts and of pie and doughnut charts, none for single-series charts). A named position... |\n| `dataLabels` | no | `oneOf:boolean / ref:ChartDataLabels` | Optional data labels. true shows value labels at each type's default position, false (or absent) shows none, which is today's behaviour. Use the object form for the label content, position and separator. |\n| `alt` | no | `string` | Text alternative for the chart: what the data shows (the point and the key numbers), not 'a chart' or 'bar chart'. The preview exposes it as the chart's accessible name (role img with aria-label) and the PowerPoint ex... |\n| `colors` | no | `array<ref:ColorRef>` | Optional series colors, by position: entry i replaces palette color i, the color of series i (the i-th plotted value column), of slice or category i on a pie, doughnut, treemap or funnel chart, and of the i-th color a... |\n| `highlight` | no | `ref:ChartHighlight` | Optional emphasis: the series and/or categories that carry the message. The engines draw the highlighted marks in the deck's primary (accent) color and every other mark in a muted neutral derived from the theme (a lig... |\n\n\n### ChartAxisTitles\n\n- Type: `object`\n- Required fields: none\n- Purpose: Titles for the axes of a chart. 'category' is the axis that carries the row labels (the horizontal axis of a column or line chart, the vertical axis of a bar chart, the X axis of a scatter chart); 'value' is the other axis; 'secondary' is the secondary value axis of a combo chart.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `category` | no | `string` | Title of the category (X) axis. |\n| `value` | no | `string` | Title of the value (Y) axis. On a combo chart, the primary (left) value axis. |\n| `secondary` | no | `string` | Combo charts only: title of the secondary value axis at the right of the plot, drawn rotated like the primary value axis title. Dropped with a 'chart-option-adapted' diagnostic on any other chart type and on a combo c... |\n\n\n### ChartHighlight\n\n- Type: `object`\n- Required fields: none\n- Purpose: Which series and categories a chart emphasizes. Names are matched exactly: series by data column name (after any 'fields' selection; a series is a plotted value column), categories by row label (the first column, or mapping.category), as text. A category label shared by several rows highlights each of them.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `series` | no | `array<string>` | Series (value columns) to highlight: all marks of each named series. A name that is not a column is a 'chart-highlight-unknown-name' error; a column that is not plotted as a series (the category or X column, or one ma... |\n| `categories` | no | `array<string>` | Categories (row labels) to highlight: the marks of each named category (a column or bar of every series, a line's points, a pie or doughnut slice). A label no row has is a 'chart-highlight-unknown-name' error. |\n\n\n### ChartDataLabels\n\n- Type: `object`\n- Required fields: none\n- Purpose: Data label settings. A label shows the selected content parts in the fixed order category, value, percent, joined by the separator.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `content` | no | `array<enum:value \\| percent \\| category>` | Which parts a label shows. Defaults to ['value']. 'percent' is the share of the total and exists only on pie and doughnut charts; 'category' shows the category name (the X value on a scatter chart). A part a chart typ... |\n| `position` | no | `enum:auto \\| center \\| inside-end \\| inside-base \\| outside-end \\| above \\| below \\| left \\| right` | Where a label sits relative to its mark. 'auto' (the default) is the type's default: outside-end for clustered columns and bars, pie slices and the histogram, pareto and waterfall constructs; center for stacked column... |\n| `separator` | no | `string` | Text between the parts of a label that shows more than one. Defaults to ', '. |\n\n\n### Table\n\n- Type: `object`\n- Required fields: none\n- Purpose: Table content, inline or dataset-backed. An inline table has 'rows' (required) and optional 'columns'. A dataset-backed table has 'dataset' (required) and optional 'fields', and no 'rows' or 'columns': it takes its headers, rows and column formats from the dataset (per-cell styles need an inline table).\n- Conditional requirement: `dataset`, or otherwise `rows`\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `columns` | no | `array<oneOf:string / array<ref:TextRun> / ref:StyledTableCell / ref:DataColumn / null>` | Optional column labels. Labels may be strings, rich runs, styled cell objects or DataColumn objects ({ \"name\", \"format\" }). A header's 'format' (DataColumn or StyledTableCell) is the column's number format; a body cel... |\n| `rows` | no | `array<array<ref:TableCell>>` | Two-dimensional table row data; each row aligns by index with columns when columns are supplied. |\n| `dataset` | no | `ref:DatasetId` | Id of a top-level dataset that supplies this table's headers, rows and column formats. Excludes 'rows' and 'columns'. An unknown id is a 'dataset-unknown' error. |\n| `fields` | no | `ref:DatasetFields` | Dataset tables only: the dataset columns to show, by name and in order. Absent shows every column. An unknown name is a 'dataset-field-unknown' error. |\n| `alt` | no | `string` | Text alternative for the table: what it shows (the point and the key numbers), not 'a table'. The preview exposes it as the table's accessible name (role group with aria-label; the cells stay readable) and the PowerPo... |\n\n\n### ChartData\n\n- Type: `object`\n- Required fields: `columns`, `rows`\n- Purpose: Inline tabular data driving a chart. The first column usually supplies category/x-axis labels; subsequent columns are plotted measures unless 'chart.mapping' or a chart type maps them differently. Value cells are numbers; a string is read only in strict decimal syntax ('12', '-3.5', '1e6'), and anything else ('12%', '$5', '(5)', '1,234') is a gap and a 'chart-value-not-numeric' warning.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `columns` | yes | `array<oneOf:string / ref:DataColumn>` | Ordered column labels for the chart data table. A label is a string or a DataColumn ({ \"name\", \"format\" }) whose number format the data labels, value axis and exported workbook use. |\n| `rows` | yes | `array<array<ref:ChartDataCell>>` | Tabular chart rows. Each row aligns by index with columns. |\n| `source` | no | `ref:DataSourceRef` | Optional provenance: where this inline data came from. Engines never read, fetch or refresh it; they keep it through editing, export and re-import. |\n\n\n### DataColumn\n\n- Type: `object`\n- Required fields: `name`\n- Purpose: A named data column with an optional number format. Anywhere a chart or dataset column is a string it may be a DataColumn; the string form is { \"name\": value } with no format. A table column header may also be a DataColumn.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `name` | yes | `string` | Column label, used as the series name, the table header and the name 'fields' and 'chart.mapping' address. |\n| `format` | no | `ref:NumberFormat` | Number format of the column's number values (data labels, value axis, table cells, exported workbook). Absent: the General form. |\n\n\n### DataSourceRef\n\n- Type: `object`\n- Required fields: `src`\n- Purpose: Provenance of inline data or a dataset: the file, sheet and range it came from and when. Engines never read, fetch or refresh it; they keep it through editing, export and re-import. Re-import the data to update it.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `src` | yes | `string` | Where the data came from: an 'asset:<id>' reference, a URL, or a relative or local path. |\n| `sheet` | no | `string` | Sheet or table name inside a spreadsheet source. |\n| `range` | no | `string` | A1-style range inside a spreadsheet source. |\n| `fields` | no | `array<string>` | The source fields the data was taken from, in order. |\n| `retrieved` | no | `anyOf:schema / schema` | When the data was taken from the source: an ISO 8601 date or date-time. |\n| `description` | no | `string` | Free-form note about the source. |\n\n\n### DatasetId\n\n- Type: `string`\n- Required fields: none\n- Purpose: Id of an entry in the top-level datasets map (the assets id pattern).\n\n_No named properties._\n\n\n### DatasetFields\n\n- Type: `array<string>`\n- Required fields: none\n- Purpose: Dataset column names to use, in order, each at most once. Selects and orders the dataset's columns; an unknown name is a 'dataset-field-unknown' error.\n\n_No named properties._\n\n\n### Datasets\n\n- Type: `object`\n- Required fields: none\n- Purpose: Shared data tables keyed by id (the assets id pattern). Charts reference one with 'chart.data': { \"dataset\": \"<id>\" } and tables with 'table': { \"dataset\": \"<id>\" }.\n\n_No named properties._\n\n\n### Dataset\n\n- Type: `object`\n- Required fields: `columns`, `rows`\n- Purpose: One shared data table: named columns (strings or DataColumn objects with a number format) and rows of scalar cells. Column names are unique ('data-column-duplicate' error). Rich text and styling belong in an inline table.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `title` | no | `string` | Human name of the dataset, shown by editors. |\n| `description` | no | `string` | What the dataset holds. |\n| `columns` | yes | `array<oneOf:string / ref:DataColumn>` | Ordered, uniquely named columns. |\n| `rows` | yes | `array<array<ref:ChartDataCell>>` | Rows of scalar cells (string, number, boolean, null); each row aligns by index with columns. |\n| `source` | no | `ref:DataSourceRef` | Optional provenance. Engines never read, fetch or refresh it. |\n\n\n### DatasetRef\n\n- Type: `object`\n- Required fields: `dataset`\n- Purpose: Chart data taken from a top-level dataset. Engines inline it before plotting; 'chart.mapping' names columns after the 'fields' selection.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `dataset` | yes | `ref:DatasetId` | Id of the dataset. An unknown id is a 'dataset-unknown' error. |\n| `fields` | no | `ref:DatasetFields` | The dataset columns to use, by name and in order. Absent uses every column. |\n\n\n### ChartMapping\n\n- Type: `object`\n- Required fields: none\n- Purpose: Series mapping by column name (after any 'fields' selection). Mapping only selects and orders columns; pie, doughnut and other single-series constructs still plot one series. An unknown name is a 'chart-mapping-unknown-column' error; a series that repeats the category or X column, and an X column on a chart type without an X axis, are dropped with a 'chart-mapping-adapted' warning.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `category` | no | `string` | The label (category) column. Default: the first column. |\n| `x` | no | `string` | The X column of an XY (scatter) chart. Default: the second column (the first, when the category is the second column), and only when there are three or more columns: with two, the second column is the one series, plot... |\n| `series` | no | `array<string>` | The plotted columns, in order. Default: every column that is not the category or the X column. |\n\n\n### ChartDataCell\n\n- Type: `oneOf:string / number / boolean / null`\n- Required fields: none\n- Purpose: A cell in inline chart data.\n\n_No named properties._\n\n\n### TableCell\n\n- Type: `oneOf:ref:TableCellValue / ref:StyledTableCell`\n- Required fields: none\n- Purpose: A scalar, rich-run array, or styled/spanning cell object. Existing scalar and rich forms remain valid.\n\n_No named properties._\n\n\n### TableCellValue\n\n- Type: `oneOf:string / number / boolean / null / array<ref:TextRun>`\n- Required fields: none\n- Purpose: A scalar table value or canonical rich text runs, without cell decoration or geometry.\n\n_No named properties._\n\n\n### StyledTableCell\n\n- Type: `object`\n- Required fields: `value`\n- Purpose: A cell with explicit visual style or merged geometry. Its position remains its array column index; use null placeholders for every covered grid position.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `value` | yes | `ref:TableCellValue` | Editable cell content; styling and spans do not change its scalar type or rich runs. |\n| `style` | no | `ref:TableCellStyle` | |\n| `colSpan` | no | `integer` | Number of grid columns covered, starting at this cell. Covered positions must contain null. Default 1. |\n| `rowSpan` | no | `integer` | Number of grid rows covered, starting at this cell. Covered positions must contain null. Header cells cannot span into body rows. Default 1. |\n| `format` | no | `ref:NumberFormat` | Number format. On a body cell it formats that cell's number value (and wins over the column's format); on a header cell (in 'columns') it is the column's format, the same as a DataColumn header. Strings, booleans, nul... |\n\n\n### TableCellStyle\n\n- Type: `object`\n- Required fields: none\n- Purpose: Cell appearance. Sizes use reference pixels at a 720-pixel canvas short edge and scale with the slide.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `fill` | no | `ref:ColorRef` | Cell background: a hex color, a color-scheme slot or role name, or a 'var:<id>' variable reference. Eight-digit hex colors include alpha; #00000000 is transparent. |\n| `color` | no | `ref:ColorRef` | Default text color, overridden by individual rich run colors. Accepts a hex color, a color-scheme slot or role name, or a 'var:<id>' variable reference. |\n| `bold` | no | `boolean` | Bold cell text. Runs that set their own bold keep it; a number cell stays bold and still uses the cell's or column's number format. |\n| `align` | no | `enum:left \\| center \\| right` | Horizontal text alignment inside the cell. |\n| `verticalAlign` | no | `enum:top \\| middle \\| bottom` | Vertical alignment inside the padded cell box. |\n| `padding` | no | `ref:TableCellPadding` | |\n| `borders` | no | `object` | Independent cell edges. Omitted edges retain the table theme border; width 0 removes an edge. |\n\n\n### TableCellPadding\n\n- Type: `object`\n- Required fields: none\n- Purpose: Text insets in reference pixels. Defaults: top 8, right 10, bottom 4, left 10.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `top` | no | `number` | |\n| `right` | no | `number` | |\n| `bottom` | no | `number` | |\n| `left` | no | `number` | |\n\n\n### TableCellBorder\n\n- Type: `object`\n- Required fields: `color`, `width`\n- Purpose: One explicit cell border.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `color` | yes | `ref:ColorRef` | Border color: a hex color, a color-scheme slot or role name, or a 'var:<id>' variable reference. Eight-digit hex colors include alpha; #00000000 is transparent. |\n| `width` | yes | `number` | Border width in reference pixels; 0 removes this edge. |\n| `dash` | no | `enum:solid \\| dash \\| dot` | Default solid. |\n\n\n### Catalogs\n\n- Type: `object`\n- Required fields: none\n- Purpose: The catalog records a document embeds, grouped by the catalog they came from. Each property is a group; inside a group, records are keyed by kind and then by id. - 'default': the catalog bare ids come from. It needs a 'source' when it holds records. Omitted: bare ids fall back to the host's default catalog. false: no catalog fallback, so every bare id must be embedded (under 'custom' or 'default'). - 'custom': the records the document defines itself. It has no source. - any other name ('acme'...\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `default` | no | `oneOf:allOf:ref:CatalogGroup + schema / const:false` | The catalog bare ids come from: its source and the records of it this document embeds. false turns off the catalog fallback for bare ids. |\n| `custom` | no | `allOf:ref:CatalogGroup + schema` | Records the document defines itself, referenced by bare id. Checked first for every bare id. Forking a catalog record copies it here under a new id, so the change of ownership is visible. |\n\n\n### CatalogGroup\n\n- Type: `object`\n- Required fields: none\n- Purpose: One catalog group: the catalog's source and the records of it the document embeds, by kind and then by id.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `source` | no | `ref:CatalogSource` | |\n| `layouts` | no | `ref:CatalogRecords` | Layout records keyed by id, each with the fields of https://openpresentation.org/schema/opf-layout/v2. Referenced from Slide.layout. |\n| `themes` | no | `ref:CatalogRecords` | Theme records keyed by id, each with the fields of https://openpresentation.org/schema/opf-theme/v1. Referenced from design.theme. |\n| `colorSchemes` | no | `ref:CatalogRecords` | Colour-scheme records keyed by id, each with the fields of https://openpresentation.org/schema/opf-color-scheme/v1. Referenced from design.colorScheme and a theme's colorScheme. |\n| `fontSchemes` | no | `ref:CatalogRecords` | Font-scheme records keyed by id, each with the fields of https://openpresentation.org/schema/opf-font-scheme/v1. Referenced from design.fontScheme, a theme's fontScheme and a language's font schemes. |\n| `narratives` | no | `ref:CatalogRecords` | Narrative records keyed by id, each with the fields of https://openpresentation.org/schema/opf-narrative/v1. Referenced from narrative. |\n| `audiences` | no | `ref:CatalogRecords` | Audience records keyed by id, each with the fields of https://openpresentation.org/schema/opf-audience/v1. Referenced from audience. |\n| `purposes` | no | `ref:CatalogRecords` | Purpose records keyed by id, each with the fields of https://openpresentation.org/schema/opf-purpose/v1. Referenced from purpose. |\n| `tones` | no | `ref:CatalogRecords` | Tone records keyed by id, each with the fields of https://openpresentation.org/schema/opf-tone/v1. Referenced from tone. |\n\n\n### CatalogSource\n\n- Type: `string`\n- Required fields: none\n- Purpose: Identity of a catalog: an HTTPS URL ('https://www.pptx.gallery') or a package reference ('pkg:@acme/opf-catalog'). It names the catalog; engines never fetch or install it. A host registers the catalog's records under this exact string, and copying slides between documents matches groups by it.\n\n_No named properties._\n\n\n### CatalogRecords\n\n- Type: `object`\n- Required fields: none\n- Purpose: Embedded records of one kind, keyed by id. The key is the record's id; an embedded record carries no '$schema', 'id', origin or digest. Catalog display metadata ('x-*' members) is stripped when a record is embedded.\n\n_No named properties._\n\n\n### CatalogReference\n\n- Type: `string`\n- Required fields: none\n- Purpose: A content reference: a bare id ('two-column') or 'name:id' ('acme:hero'), where the prefix names a group of 'catalogs'. URLs and 'pkg:' strings are not references; a named catalog group replaces them. See Catalogs for the resolution order.\n\n_No named properties._\n\n\n### LanguageTag\n\n- Type: `string`\n- Required fields: none\n- Purpose: A BCP-47 language tag ('en-US', 'ja', 'zh-Hans', 'sr-Latn-ME'), an engine vocabulary. Use 'en-GB' for UK English; 'en-UK' is not a valid region.\n\n_No named properties._\n\n\n### Composition\n\n- Type: `object`\n- Required fields: none\n- Purpose: Automatic slide and content-group arrangement. Nested content groups arrange their children independently, inheriting only minFontSize and overflow. Explicit promoted regions retain their positions. A v2 layout defines its grid through areas, tracks and regions; its composition supports gap, padding, minFontSize and overflow rather than mode, columns or weights.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `mode` | no | `enum:auto \\| grid \\| row \\| column` | auto chooses a grid from available space and content; grid uses columns; row and column use one horizontal or vertical track. |\n| `columns` | no | `integer` | Column count for grid. In auto mode this caps the number of columns. |\n| `gap` | no | `number` | Space between cells as a fraction of the container short edge (canvas at slide root). Default 0.03333333333333333. |\n| `padding` | no | `number` | Inset as a fraction of the container short edge. Default 0.08 on a slide, 0 inside a group. |\n| `weights` | no | `array<number>` | Relative track sizes: columns for row/grid/auto, rows for column. Omitted tracks have weight 1; extra weights are ignored. |\n| `minFontSize` | no | `number` | Minimum readable text size in reference pixels at a 720-pixel canvas short edge. Default 16. Overflow is diagnosed when text cannot fit at this size. |\n| `overflow` | no | `enum:warn \\| error` | warn returns diagnostics for content that does not fit; error rejects layout. Content is never silently removed. Default warn. |\n"
307
+ "markdown": "# OPF Presentation Schema Reference\n\nThis reference documents the author-facing shape of a complete `*.opf.json` presentation document. It summarizes the canonical schema in `spec/schemas/opf.schema.json`; the schema remains the source of truth for validators.\n\n## Document Contract\n\n- Schema id: `https://openpresentation.org/schema/opf/v1`\n- Required top-level fields: `slides`\n- Additional top-level fields: not allowed\n\n## Top-Level Fields\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `$schema` | no | `const:\"https://openpresentation.org/schema/opf/v1\"` | Optional OPF schema version. When omitted, validators and engines should assume the latest supported OPF schema. |\n| `name` | no | `string` | Display name of the presentation for GUI/TUI lists, library/search indexing, OS-level metadata, and default export filenames. This is deck identity, not slide content. Use slides[].title and slides[].subtitle for text... |\n| `description` | no | `string` | Free-form prose describing what this presentation is about. Used by agents and humans as a deck-level summary; complements purpose (the goal) and narrative (the structured storyline). Round-trips to OOXML 'docProps/co... |\n| `filename` | no | `string` | Optional base filename for exports (without extension). The opf CLI (convert) and the editor strip a trailing .pptx, .pdf, .png, or .svg (case-insensitive) and append the target format's extension. When omitted, they... |\n| `organization` | no | `oneOf:ref:Organization / array<ref:Organization>` | Organization associated with the presentation, usually the presenting company. Array form supports hosts, partners, clients, and sponsors. The primary organization (Organization.role 'primary', else the first item) su... |\n| `speaker` | no | `oneOf:ref:Speaker / array<ref:Speaker>` | Person presenting the deck. Array form supports panels and multi-speaker decks. The first speaker is the primary speaker: the built-in variables 'speaker.<field>' read it, 'speakers' lists every name, and 'speaker.<id... |\n| `author` | no | `oneOf:string / array<string>` | Optional credit for the person who authored or contributed to the deck, distinct from speaker. Array form supports multiple contributors. Round-trips to OOXML 'docProps/core.xml' as '<dc:creator>' (semicolon-joined wh... |\n| `audience` | no | `oneOf:string / ref:Audience / array<oneOf:string / ref:Audience>` | Intended audiences for the presentation. Accepts any of: - A single string: an audiences reference ('executive', or 'acme:board' for a record of the catalogs.acme group), or free-form text ('Series B investors'). A st... |\n| `purpose` | no | `oneOf:string / ref:Purpose` | Primary goal of the presentation. Accepts either: - A string: a purposes reference ('decide', or 'acme:fundraise' for a record of the catalogs.acme group), or a free-form goal ('Raise a Series B round of $30M'). A str... |\n| `language` | no | `oneOf:ref:LanguageTag / ref:Language` | Language of the presentation content, an engine vocabulary: a BCP-47 language tag, validated directly with no catalog lookup. Accepts either: - A BCP-47 tag ('en-US', 'en-GB', 'ja', 'zh-Hans', 'ar-SA'). - An inline La... |\n| `tone` | no | `oneOf:ref:CatalogReference / ref:Tone` | Desired tone for the presentation. Accepts either: - A tones reference: a bare id ('formal') or 'name:id' for a record of a named catalogs group. - An inline Tone object for custom tone metadata or overrides on a refe... |\n| `takeaway` | no | `oneOf:string / array<string>` | Audience-facing takeaway the presentation should leave behind. Array form supports multiple takeaways. Deck-level intent used by AI to seed and pressure-test slide content. |\n| `duration` | no | `integer` | Target presentation duration, as an integer number of minutes. The opf-render presenter view counts the elapsed time against it, and core validate warns when it lies outside the resolved narrative's 'duration' range.... |\n| `tags` | no | `array<string>` | Free-form labels used for categorization, search, and filtering. Lowercase kebab-case is recommended for consistency across a deck library. |\n| `design` | no | `ref:Design` | Optional design system covering theme, color scheme, font scheme, dimensions, background, logo, watermark, header, and footer applied to the deck. When omitted, engines use their default design configuration. |\n| `variables` | no | `ref:Variables` | Optional named variables: deck colors referenced as 'var:<id>' (the original use), and typed content variables (text, number, date, image, url, list) referenced inline as '{{<id>}}' or whole as 'var:<id>'. Variables a... |\n| `template` | no | `boolean` | Marks this document as a template: an incomplete OPF file. A template declares variables (top-level 'variables') and references them from content, and may leave required variables unfilled; validation then reports the... |\n| `narrative` | no | `ref:CatalogReference` | The deck's narrative plan, by reference: a bare narratives id ('classic-story') or 'name:id' for a record of a named catalogs group ('acme:founder-pitch'). The deck holds only this pointer; the plan (the arc, its beat... |\n| `slides` | yes | `array<ref:Slide>` | Ordered array of slides that make up the presentation. |\n| `references` | no | `array<ref:Reference>` | Sources that text runs cite with 'cite'. Ids are unique. A cited reference is listed in the footnote area of every slide that cites it, with a marker number assigned per deck in order of first use; a reference no run... |\n| `datasets` | no | `ref:Datasets` | Optional shared data tables, keyed by id. A chart ('chart.data': { \"dataset\": \"<id>\" }) or a table ('table': { \"dataset\": \"<id>\" }) references one instead of holding its own copy; engines inline the reference before c... |\n| `assets` | no | `ref:Assets` | Optional reusable asset registry for images, data files, videos, documents, fonts, and other resources referenced elsewhere in the deck via 'asset:<id>' strings. |\n| `catalogs` | no | `ref:Catalogs` | The catalog records this document embeds, grouped by the catalog they came from: 'default' (the catalog bare ids come from), 'custom' (records the document defines itself) and any number of named catalogs ('acme', ref... |\n| `extensions` | no | `object` | Custom data passthrough for agent workflows; ignored by the engine but preserved across read/write round-trips. |\n\n## Object And Type Reference\n\n### Assets\n\n- Type: `object`\n- Required fields: none\n- Purpose: Reusable asset registry for resources used by slides, charts, metadata, and design. Keys are stable asset ids referenced elsewhere as 'asset:<id>'. Each asset can be a source string or an object with src plus optional metadata.\n\n_No named properties._\n\n\n### Asset\n\n- Type: `oneOf:string / object`\n- Required fields: none\n- Purpose: Reusable or inline resource. A string is shorthand for { \"src\": value }. Source strings accept 'asset:<id>' references, HTTPS URLs, data URIs, relative paths resolved against the OPF file location, or local filesystem paths. Use object form when metadata such as alt text, title or mediaType matters.\n\n_No named properties._\n\n\n### Audience\n\n- Type: `anyOf:schema / schema`\n- Required fields: none\n- Purpose: Inline audience metadata for the presentation. Use 'id' to reference an audiences record (a bare id or 'name:id') and override selected fields, or use 'name' for a custom inline audience.\n- Conditional requirement: `id` or `name`\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `id` | no | `ref:CatalogReference` | Optional audiences reference (a bare id or 'name:id') resolved before applying the inline overrides. |\n| `name` | no | `string` | Human-readable audience name shown in pickers. |\n| `summary` | no | `string` | One-sentence positioning of the audience. |\n| `description` | no | `string` | Longer prose describing the audience and how to address them. |\n| `seniority` | no | `enum:ic \\| manager \\| director \\| vp \\| c-suite \\| mixed` | Typical seniority level of the audience. |\n| `technicalFluency` | no | `enum:low \\| medium \\| high \\| mixed` | Typical technical fluency of the audience. |\n| `decisionPower` | no | `enum:informational \\| advisory \\| decision-maker` | Whether the audience is expected to be informed, advise, or decide. |\n| `attentionBudgetMinutes` | no | `number` | Realistic upper bound on focused attention for a single presentation, in minutes. |\n| `recommendedNarratives` | no | `array<string>` | Soft cross-link: narrative ids that work well for this audience, for picker UIs. Never resolved, embedded or validated as references. |\n| `recommendedTones` | no | `array<string>` | Soft cross-link: tone ids that work well for this audience, for picker UIs. Never resolved, embedded or validated as references. |\n| `tags` | no | `array<string>` | Free-form labels for filtering and search. |\n\n\n### Purpose\n\n- Type: `anyOf:schema / schema`\n- Required fields: none\n- Purpose: Inline purpose metadata for the presentation. Use 'id' to reference a purposes record (a bare id or 'name:id') and override selected fields, or use 'name' for a custom inline purpose.\n- Conditional requirement: `id` or `name`\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `id` | no | `ref:CatalogReference` | Optional purposes reference (a bare id or 'name:id') resolved before applying the inline overrides. |\n| `name` | no | `string` | Human-readable purpose name shown in pickers. |\n| `summary` | no | `string` | One-sentence positioning of the purpose. |\n| `description` | no | `string` | Longer prose describing when to use this purpose and how it should shape a deck. |\n| `outcome` | no | `string` | Desired audience outcome after the presentation. |\n| `successCriteria` | no | `array<string>` | Observable signals that the deck accomplished this purpose. |\n| `recommendedNarratives` | no | `array<string>` | Soft cross-link: narrative ids that work well for this purpose, for picker UIs. Never resolved, embedded or validated as references. |\n| `recommendedTones` | no | `array<string>` | Soft cross-link: tone ids that work well for this purpose, for picker UIs. Never resolved, embedded or validated as references. |\n| `tags` | no | `array<string>` | Free-form labels for filtering and search. |\n\n\n### Language\n\n- Type: `object`\n- Required fields: `bcp47`\n- Purpose: Inline language metadata for the presentation: a BCP-47 'bcp47' tag plus overrides of what engines know about it. Languages are an engine vocabulary, not catalog records, so there is no 'id'.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `name` | no | `string` | Human-readable language name. |\n| `bcp47` | yes | `ref:LanguageTag` | BCP-47 language tag used for locale-aware rendering, proofing, and accessibility metadata. Use 'en-GB' for UK English; 'en-UK' is not a valid BCP-47 region form. |\n| `ooxmlLang` | no | `string` | Culture tag for OOXML text-run language attributes (a:rPr/@lang, a:endParaRPr/@lang), in the language-[Script-]REGION form Office recognizes (e.g. 'ja-JP', 'ar-SA', 'ms-MY', 'nb-NO', 'fil-PH', 'zh-CN'). Without it, en... |\n| `code` | no | `string` | ISO 639-3 or 639-2 language code carried for engines that prefer ISO codes. |\n| `direction` | no | `enum:ltr \\| rtl` | Base text direction for the language. When omitted, engines derive it from the script: Arabic (Arab), Hebrew (Hebr), Syriac (Syrc), Thaana (Thaa), N'Ko (Nkoo), Adlam (Adlm), Samaritan (Samr), Mandaic (Mand) and Hanifi... |\n| `script` | no | `string` | ISO 15924 script code of the language's writing system. The script selects the OOXML font slot the language's text uses: East Asian scripts (Hans, Hant, Hani, Jpan, Kore, Hang, Hira, Kana, Bopo, Yiii) use the eastAsia... |\n| `fontScheme` | no | `ref:CatalogReference` | Font-scheme reference (a bare id or 'name:id') for this language's script slot when targeting PowerPoint output. Its major/minor families fill the language's script slot (eastAsian or complexScript, chosen by 'script'... |\n| `googleFontScheme` | no | `ref:CatalogReference` | Font-scheme reference (a bare id or 'name:id') used in place of 'fontScheme' when resolving script fonts for Google Slides output. |\n| `summary` | no | `string` | One-sentence note about coverage or font defaults. |\n| `description` | no | `string` | Longer prose describing the language record and any font-pairing rationale. |\n| `tags` | no | `array<string>` | Free-form labels for filtering and search. |\n\n\n### Tone\n\n- Type: `anyOf:schema / schema`\n- Required fields: none\n- Purpose: Inline tone metadata for the presentation. Use 'id' to reference a tones record (a bare id or 'name:id') and override selected fields, or use 'name' for a custom inline tone.\n- Conditional requirement: `id` or `name`\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `id` | no | `ref:CatalogReference` | Optional tones reference (a bare id or 'name:id') resolved before applying the inline overrides. |\n| `name` | no | `string` | Human-readable tone name shown in pickers. |\n| `summary` | no | `string` | One-sentence positioning of the tone. |\n| `description` | no | `string` | Longer prose describing the tone and the kinds of decks it suits. |\n| `voiceCues` | no | `array<string>` | Short directives that shape AI generation toward this tone. |\n| `avoid` | no | `array<string>` | Anti-patterns that AI generation should not produce when this tone is active. |\n| `samplePhrases` | no | `array<string>` | Short example phrases that exemplify this tone. |\n| `recommendedNarratives` | no | `array<string>` | Soft cross-link: narrative ids this tone pairs well with, for picker UIs. Never resolved, embedded or validated as references. |\n| `tags` | no | `array<string>` | Free-form labels for filtering and search. |\n\n\n### Organization\n\n- Type: `object`\n- Required fields: `id`, `name`\n- Purpose: An organization associated with the presentation, typically the presenting company, but also hosts, partners, clients, or sponsors. The primary organization (role 'primary', else the first one) supplies the cover and section logo (unless design.logo overrides it) and the 'socials' header/footer field (its socials). Every field is also a built-in variable ('{{organization.name}}', 'organization.<id>.<field>'), and every logo a slide-scoped image ('var:organization.logo.icon', 'var:organization...\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `id` | yes | `string` | Stable identifier for the organization, used to reference it from Speaker.organizationId and to address it in built-in variables as 'organization.<id>.<field>'. Must be unique among organizations (a duplicate is a val... |\n| `name` | yes | `string` | Display name. The built-in variable 'organization.name' (primary organization) or 'organization.<id>.name' carries it into any string, such as a header or footer text of '{{organization.name}}'. |\n| `legalName` | no | `string` | Optional legal entity name when it differs from the display name. Never drawn automatically; available as the built-in variable 'organization.legalName'. |\n| `logo` | no | `oneOf:ref:Asset / ref:OrganizationLogo` | The organization's logo. A path, URL or Asset is one logo for every shape; an object gives up to four shapes (full, stacked, icon, wordmark), each a path or Asset, or { onLight, onDark } for the two backgrounds. A pat... |\n| `domain` | no | `string` | Bare internet domain for the organization. Never drawn automatically and not used to look up assets; available as the built-in variable 'organization.domain'. |\n| `email` | no | `string` | General contact email for the organization. Never drawn automatically; available as the built-in variable 'organization.email'. |\n| `phone` | no | `string` | Main contact phone number for the organization. E.164 format is recommended. Never drawn automatically; available as the built-in variable 'organization.phone'. |\n| `tagline` | no | `string` | Short tagline. Never drawn automatically (cover slides do not show it); available as the built-in variable 'organization.tagline', for example in a footer 'text' of '{{organization.tagline}}'. |\n| `role` | no | `enum:primary \\| partner \\| client \\| sponsor \\| host` | Role of the organization relative to the presentation. Only 'primary' has behavior: it selects the primary organization (cover and section logo, the 'socials' header/footer field, and the 'organization.<field>' built-... |\n| `socials` | no | `ref:Socials` | Optional social media handles or URLs for the organization. The primary organization's socials render in header/footer zones that set socials: true; otherwise they are authoring metadata. |\n\n\n### Speaker\n\n- Type: `object`\n- Required fields: `id`, `name`\n- Purpose: A person presenting the deck. A speaker is drawn only through built-in variables ('{{speaker.name}}' inside any string, a header or footer text included, and 'var:speaker.photo' as a whole image field). No layout, cover or bio slide places a speaker on its own. See docs/templates-and-variables.md.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `id` | yes | `string` | Stable identifier for the speaker. Must be unique among speakers (a duplicate is a validation error). Addresses this speaker in built-in variables as 'speaker.<id>.<field>', such as '{{speaker.alice.title}}'. |\n| `name` | yes | `string` | Display name. Built-in variable 'speaker.name' (first speaker) or 'speaker.<id>.name'; every name is listed by 'speakers'. |\n| `title` | no | `string` | Role or title. Built-in variable 'speaker.title' (or 'speaker.<id>.title'), for example in a footer text of '{{speaker.name}}, {{speaker.title}}'. |\n| `photo` | no | `ref:Asset` | Source for the speaker's headshot image. Accepts an HTTPS URL, data URI, relative path (resolved against the OPF file location), local path, or 'asset:<id>' reference. Common formats are JPG or PNG; SVG is not appropr... |\n| `email` | no | `string` | Contact email. Never drawn automatically; available as the built-in variable 'speaker.email'. |\n| `phone` | no | `string` | Contact phone number for the speaker. E.164 format is recommended. Never drawn automatically; available as the built-in variable 'speaker.phone'. |\n| `bio` | no | `string` | Short biographical paragraph. Never drawn automatically; available as the built-in variable 'speaker.bio', for a bio or 'about the speaker' slide you write. |\n| `organizationId` | no | `string` | Reference to an Organization.id in organization; it must name an existing organization (a validation error otherwise). Attribution metadata for hosts: no engine draws it or changes which organization is primary becaus... |\n| `socials` | no | `ref:Socials` | Optional social media handles or URLs for the speaker. Authoring metadata only: nothing draws speaker socials and there is no built-in variable for them (the 'socials' header/footer field draws the primary organizatio... |\n\n\n### Socials\n\n- Type: `object`\n- Required fields: none\n- Purpose: Social media handles or URLs, keyed by platform. The keys are an engine vocabulary validated directly (no catalog lookup): engines link a handle with the platform's URL pattern and handle prefix (spec/reference/engine-vocabularies.json). Each value is a string, either a full URL or a platform handle (e.g., '@acme'). Brand colours and icons are catalog display metadata for authoring UIs; engines render the profile URL, not icons or brand colours. Used both for organization and speaker records.\n\n_No named properties._\n\n\n### Design\n\n- Type: `object`\n- Required fields: none\n- Purpose: Visual design system applied to the presentation; individual slides may override fields via Slide.design.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `theme` | no | `ref:CatalogReference` | Theme for the deck: a themes reference, a bare id ('minimal') or 'name:id' ('acme:brand'). The theme supplies the default colour scheme, font scheme, background and dimensions; design.colorScheme, design.fontScheme, d... |\n| `colorScheme` | no | `oneOf:string / ref:ColorScheme` | Color scheme for the presentation. Accepts two forms: - A colorSchemes reference: a bare id ('cool-horizon') or 'name:id' ('acme:ocean'). - Object form: a ColorScheme with an optional 'id' reference as the base plus s... |\n| `fontScheme` | no | `oneOf:string / ref:FontScheme` | Font scheme for heading, body, accent, and code text. Accepts two forms: - A fontSchemes reference: a bare id ('aptos') or 'name:id' ('acme:inter'). - Object form: a FontScheme with an optional 'id' reference as the b... |\n| `dimensions` | no | `oneOf:ref:DimensionPreset / ref:Dimensions` | Slide dimensions and aspect ratio. String shorthand such as 'widescreen' is equivalent to { preset: 'widescreen' }. |\n| `background` | no | `oneOf:ref:BackgroundShortcut / ref:Background` | Default slide background applied across the deck unless overridden on a slide (slides.N.design.background); the resolved theme's background is the fallback. A background is the only canvas fill and never moves content... |\n| `variant` | no | `oneOf:ref:ThemeBackgroundSlot / ref:ColorVariant` | Optional color variant, as a background slot ('light1', 'light2', 'dark1', 'dark2'; the same as { background: slot }) or the object form, which is needed only for an accent order: which slot of the color scheme is the... |\n| `logo` | no | `oneOf:string / const:false` | Which logo covers, section dividers and picture bullets draw. Logos live on the organization (Organization.logo); without this field covers and sections draw the primary organization's full logo and picture bullets it... |\n| `watermark` | no | `oneOf:const:false / ref:Asset / ref:Watermark` | Optional decorative watermark applied across slides: an image or a text stamp, in the fixed frame and at the opacity described on Watermark. Use false to suppress an inherited watermark in slide-level design; a string... |\n| `header` | no | `oneOf:const:false / ref:HeaderFooter` | Repeated header furniture rendered outside the main slide content. Use false to suppress an inherited header. |\n| `footer` | no | `oneOf:const:false / ref:HeaderFooter` | Repeated footer furniture rendered outside the main slide content. Use false to suppress an inherited footer. |\n| `titleAlignment` | no | `enum:left \\| center \\| right` | Default horizontal alignment for the title heading area (the title and the tag) in resolved layouts. Effective value: the slide's design, then the deck's design, then the theme's design.titleAlignment, then the layout... |\n| `contentAlignment` | no | `enum:left \\| center \\| right` | Default horizontal alignment for body/content regions in resolved layouts. Effective value: the slide's design, then the deck's design, then the theme's design.contentAlignment, then the layout record's design.content... |\n| `contentBox` | no | `boolean` | Whether body/content regions are rendered inside a visible card or surface. Effective value: the slide's design, then the deck's design, then the theme's design.contentBox, then the layout record's design.contentBox,... |\n| `contentDirection` | no | `enum:horizontal \\| vertical` | Axis along which parallel body content is arranged: 'vertical' is a column, 'horizontal' a row. Effective value: the slide's design, then the deck's design, then the theme's design.contentDirection, then the layout re... |\n| `chartPrimary` | no | `enum:none \\| top \\| bottom \\| left \\| right` | Where the primary chart sits relative to supporting content. Effective value: slide design, then deck design, then the layout record's design.chartPrimary. It applies to automatic slides only; a template layout says t... |\n| `imageFit` | no | `enum:cover \\| contain \\| stretch` | Default fit of image blocks (and Slide.image) that set no fit of their own: 'cover' fills the frame and crops what overflows around the block's focus, 'contain' shows the whole picture centered in the frame, 'stretch'... |\n| `listBullet` | no | `enum:character \\| image` | Marker style for items and bullets lists. Effective value: the slide's design, then the deck's design, then the theme's design.listBullet, then the layout record's design.listBullet, then 'character'. 'character' (the... |\n| `mirror` | no | `boolean` | Draw the slide's template layout mirrored (OPF 0.19): each row's cells and the column sizes reversed, so a 'beside' layout puts its picture, chart or table at the end. Logical: a right-to-left deck mirrors on top. Bin... |\n\n\n### ColorScheme\n\n- Type: `object`\n- Required fields: none\n- Purpose: Color palette used by the design system. Every slot and role is a hex color ('#RGB', '#RRGGBB' or '#RRGGBBAA'); a color-scheme name or a 'var:<id>' reference is not valid here, because those resolve through the scheme. The slot fields (accent1-accent6, dark1, dark2, light1, light2, hyperlink, followedHyperlink) mirror color-scheme.schema.json (https://openpresentation.org/schema/opf-color-scheme/v1) so library records and inline OPF overrides are interchangeable on those fields. Two parallel...\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `id` | no | `ref:CatalogReference` | Colour-scheme reference (a bare id or 'name:id') resolved as the base; slot and role overrides on the surrounding ColorScheme object take precedence over the resolved scheme. |\n| `accent1` | no | `ref:HexColor` | Accent 1 color (hex). Mirrors the OOXML accent1 slot. |\n| `accent2` | no | `ref:HexColor` | Accent 2 color (hex). Mirrors the OOXML accent2 slot. |\n| `accent3` | no | `ref:HexColor` | Accent 3 color (hex). Mirrors the OOXML accent3 slot. |\n| `accent4` | no | `ref:HexColor` | Accent 4 color (hex). Mirrors the OOXML accent4 slot. |\n| `accent5` | no | `ref:HexColor` | Accent 5 color (hex). Mirrors the OOXML accent5 slot. |\n| `accent6` | no | `ref:HexColor` | Accent 6 color (hex). Mirrors the OOXML accent6 slot. |\n| `dark1` | no | `ref:HexColor` | Dark 1 color (hex). Typically the deepest neutral; OOXML dark1. |\n| `dark2` | no | `ref:HexColor` | Dark 2 color (hex). Secondary dark; OOXML dark2. |\n| `light1` | no | `ref:HexColor` | Light 1 color (hex). Typically the slide canvas; OOXML lt1. |\n| `light2` | no | `ref:HexColor` | Light 2 color (hex). Secondary light surface; OOXML lt2. |\n| `hyperlink` | no | `ref:HexColor` | Hyperlink color (hex). OOXML hlink. Link runs with no color of their own are drawn underlined in it, in the preview and in PowerPoint, unless it has under 4.5:1 contrast against the slide background, then in the slide... |\n| `followedHyperlink` | no | `ref:HexColor` | Followed-hyperlink color (hex). OOXML folHlink. |\n| `primary` | no | `ref:HexColor` | Abstract role: primary brand color (hex). The engine maps this onto an OOXML accent slot when serializing. |\n| `secondary` | no | `ref:HexColor` | Abstract role: secondary brand color (hex). |\n| `accent` | no | `ref:HexColor` | Abstract role: accent color used for highlights and emphasis (hex). |\n| `background` | no | `ref:HexColor` | Abstract role: default slide background color (hex), used when the design names no single-color background (and for gradient and picture backgrounds). Overrides light1 as that default. |\n| `surface` | no | `ref:HexColor` | Abstract role: color for elevated surfaces such as cards and panels (hex). Overrides the default of light2 (dark2 on a dark slide). |\n| `text` | no | `ref:HexColor` | Abstract role: primary body text color (hex). Overrides dark1 on a light slide only; a dark slide always uses light1. |\n| `textSecondary` | no | `ref:HexColor` | Abstract role: secondary or muted text color used for captions and supporting copy (hex). Overrides the default of dark2 (light2 on a dark slide). |\n\n\n### FontScheme\n\n- Type: `object`\n- Required fields: none\n- Purpose: Typography selections used by the design system. The pair fields (major, minor) and refinement fields (type, app, languageFamily) mirror font-scheme.schema.json (https://openpresentation.org/schema/opf-font-scheme/v1) so library records and inline OPF overrides are interchangeable on those fields. Two parallel models are supported and may be mixed: - OOXML pairs (major, minor) - heading and body family names that round-trip directly to PowerPoint majorFont/minorFont entries. - Abstract roles...\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `id` | no | `ref:CatalogReference` | Font-scheme reference (a bare id or 'name:id') resolved as the base; field overrides on the surrounding FontScheme object take precedence over the resolved scheme. |\n| `major` | no | `string` | Heading (major) font family mirrors the OOXML majorFont entry. Pairs with 'minor'. |\n| `minor` | no | `string` | Body (minor) font family mirrors the OOXML minorFont entry. Pairs with 'major'. |\n| `eastAsian` | no | `object` | East Asian script fonts. Maps to the OOXML a:ea element of majorFont (major) and minorFont (minor), and to run-level a:ea. When set, they fill the eastAsian slot for every language; when omitted, the slot comes from t... |\n| `complexScript` | no | `object` | Complex-script fonts (for example Arabic, Hebrew, Indic and Thai). Maps to the OOXML a:cs element of majorFont (major) and minorFont (minor), and to run-level a:cs. When set, they fill the complexScript slot for every... |\n| `type` | no | `enum:sans-serif \\| serif \\| monospace` | High-level typographic class of the scheme. |\n| `app` | no | `enum:powerpoint \\| google-slides` | Target application this font pairing is intended for. Metadata for pickers and catalog filters: no engine changes its output by it. |\n| `languageFamily` | no | `enum:latin \\| ea \\| cs \\| eastAsian \\| complexScript` | Font-language family this scheme is intended for: 'latin' for Latin-script content, 'ea' (or 'eastAsian', the same value) for East Asian scripts, 'cs' (or 'complexScript', the same value) for Complex Scripts. The long... |\n| `heading` | no | `string` | Abstract role: font family name used for slide titles and headings. Maps onto the OOXML major slot when serializing. |\n| `body` | no | `string` | Abstract role: font family name used for body copy. Maps onto the OOXML minor slot when serializing. |\n| `accent` | no | `string` | Abstract role: font family name used for accent text. When set, the slide tag (eyebrow) and the quote body use this family instead of the body and heading families; nothing else changes. resolveFontFamilies() returns... |\n| `code` | no | `string` | Abstract role: monospaced font family name used for code blocks and for inline code runs (TextRun.code). No direct OOXML slot. Resolution: this override, then the resolved font-scheme record's 'code' (for example Cons... |\n\n\n### SlideDesign\n\n- Type: `allOf:ref:Design + schema`\n- Required fields: none\n- Purpose: A slide's design: every Design field except dimensions. A PPTX has one slide size, so the size is set once, on the deck's design.dimensions (or its theme), and a slide's design cannot set it. A slide-level theme whose resolved dimensions differ from the deck's is a validate warning (opf/slide-theme-dimensions); exporting such a deck to PPTX fails with mixed-slide-dimensions.\n\n_No named properties._\n\n\n### DimensionPreset\n\n- Type: `enum:16:9 | 4:3 | 16:10 | 1:1 | 4:5 | 9:16 | letter | a4 | widescreen | standard`\n- Required fields: none\n- Purpose: Named preset; sets aspect ratio and physical size. 16:9 ('widescreen'): 13.333 x 7.5 in, PowerPoint's Widescreen. 4:3 ('standard'): 10 x 7.5 in. 16:10: 10 x 6.25 in. letter (11 x 8.5 in) and a4 (11.69 x 8.27 in) are paper sizes, not PowerPoint's Letter Paper or A4 Paper. 1:1, 4:5 and 9:16 are 7.5 in on the short edge.\n\n_No named properties._\n\n\n### Dimensions\n\n- Type: `object`\n- Required fields: none\n- Purpose: Slide dimensions: a preset, or custom inches that override it. PowerPoint's Letter Paper (10 x 7.5 in), A4 Paper (10.8333 x 7.5 in) and On-screen Show (16:9) (10 x 5.625 in) are not OPF presets; use widthInches and heightInches. docs/slide-sizes.md compares every preset.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `preset` | no | `ref:DimensionPreset` | |\n| `widthInches` | no | `number` | Custom slide width in inches; overrides the preset width when provided. |\n| `heightInches` | no | `number` | Custom slide height in inches; overrides the preset height when provided. |\n\n\n### ThemeBackgroundSlot\n\n- Type: `enum:light1 | light2 | dark1 | dark2`\n- Required fields: none\n- Purpose: PowerPoint theme-controlled slide background slot from the active color scheme. These are slots, not assumptions about actual colors: light1 is usually white and dark1 is usually black by convention, but the color scheme controls the real values.\n\n_No named properties._\n\n\n### HexColor\n\n- Type: `string`\n- Required fields: none\n- Purpose: Hex color shorthand accepted by selected string fields.\n\n_No named properties._\n\n\n### ColorRef\n\n- Type: `anyOf:ref:HexColor / enum:accent1 | accent2 | accent3 | accent4 | accent5 | accent6 | dark1 | dark2 | light1 | light2 | hyperlink | followedHyperlink | primary | secondary | accent | background | surface | surfaceAlt | text | textSecondary / string`\n- Required fields: none\n- Purpose: A color value or reference, enforced on styled table cell fill and text colors, cell border colors, solid background colors, gradient stop colors and pattern foreground and background colors. Three forms: - Literal hex: '#RGB', '#RRGGBB', or '#RRGGBBAA'. - Color-scheme name, resolved through the effective color scheme after design resolution: an OOXML slot ('accent1'-'accent6', 'dark1', 'dark2', 'light1', 'light2', 'hyperlink', 'followedHyperlink') or an abstract role ('primary', 'secondary',...\n\n_No named properties._\n\n\n### Variables\n\n- Type: `object`\n- Required fields: none\n- Purpose: Named variables, keyed by stable kebab-case id. A variable is a typed, named value the deck declares once and uses in many places: a color ('var:<id>' in color fields, the original use), or content that fills a template (text, number, date, image, url, list). Content is referenced inline as '{{<id>}}' inside any string, or whole as 'var:<id>' in a field of the matching type; '\\{{' writes a literal '{{'. A hex string is shorthand for a color variable. A variable with no 'value' is unfilled: ex...\n\n_No named properties._\n\n\n### Variable\n\n- Type: `oneOf:ref:HexColor / ref:ColorVariable / ref:TextVariable / ref:NumberVariable / ref:DateVariable / ref:ImageVariable / ref:UrlVariable / ref:ListVariable`\n- Required fields: none\n- Purpose: A single named variable: a hex string (shorthand for a color variable) or an object whose 'type' is color, text, number, date, image, url or list. 'value' is the current value and is optional: a variable with no value is unfilled, which a template allows and a normal deck does not. 'example' only illustrates the slot (fill forms, template previews) and never reaches output.\n\n_No named properties._\n\n\n### ColorVariable\n\n- Type: `object`\n- Required fields: `type`\n- Purpose: A named color. Content color fields reference it as 'var:<id>'. Colors resolve at render time through the ordinary color-reference path, so a color variable keeps working as before.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `type` | yes | `const:\"color\"` | Variable kind. One of color, text, number, date, image, url or list. |\n| `value` | no | `ref:HexColor` | Hex color this variable resolves to. |\n| `required` | no | `boolean` | Whether the variable must be filled. Defaults to true: a variable with no 'value' is unfilled, and an unfilled required variable is an error in a normal deck and expected in a template. Set false for an optional slot:... |\n| `label` | no | `string` | Optional short human label for forms and fill panels. |\n| `description` | no | `string` | Optional prose describing what the variable is for, surfaced by pickers, fill forms and agents. |\n| `example` | no | `ref:HexColor` | Illustrative color shown in fill forms and used when a template is previewed with examples. Never written to output. |\n\n\n### TextVariable\n\n- Type: `object`\n- Required fields: `type`\n- Purpose: Text content. Plain string or rich TextRun[]. Insert it inline as '{{<id>}}' inside any string (rich text is flattened to plain text there), or reference it whole as 'var:<id>' in a field that accepts string or TextRun[] (the rich runs are kept).\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `type` | yes | `const:\"text\"` | Variable kind. One of color, text, number, date, image, url or list. |\n| `value` | no | `oneOf:string / array<ref:TextRun>` | Text this variable resolves to: a plain string, or TextRun[] for rich text. |\n| `required` | no | `boolean` | Whether the variable must be filled. Defaults to true: a variable with no 'value' is unfilled, and an unfilled required variable is an error in a normal deck and expected in a template. Set false for an optional slot:... |\n| `label` | no | `string` | Optional short human label for forms and fill panels. |\n| `description` | no | `string` | Optional prose describing what the variable is for, surfaced by pickers, fill forms and agents. |\n| `example` | no | `oneOf:string / array<ref:TextRun>` | Illustrative text shown in fill forms and used when a template is previewed with examples. Never written to output. |\n\n\n### NumberVariable\n\n- Type: `object`\n- Required fields: `type`\n- Purpose: A number. '{{<id>}}' inserts it as text using 'format'; 'var:<id>' as a whole field supplies the number itself (chart values, font sizes).\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `type` | yes | `const:\"number\"` | Variable kind. One of color, text, number, date, image, url or list. |\n| `value` | no | `number` | Number this variable resolves to. |\n| `required` | no | `boolean` | Whether the variable must be filled. Defaults to true: a variable with no 'value' is unfilled, and an unfilled required variable is an error in a normal deck and expected in a template. Set false for an optional slot:... |\n| `label` | no | `string` | Optional short human label for forms and fill panels. |\n| `description` | no | `string` | Optional prose describing what the variable is for, surfaced by pickers, fill forms and agents. |\n| `example` | no | `number` | Illustrative number shown in fill forms and used when a template is previewed with examples. Never written to output. |\n| `format` | no | `ref:NumberFormat` | Display pattern used by '{{<id>}}' (the shared NumberFormat syntax). A literal prefix, a numeric part of '#', '0', ',' and '.', and a literal suffix. '0' pads digits, '#' is optional, ',' groups thousands, digits afte... |\n\n\n### NumberFormat\n\n- Type: `string`\n- Required fields: none\n- Purpose: Number display pattern, shared by NumberVariable.format, data column formats (DataColumn.format) and table cell formats (StyledTableCell.format). An optional literal prefix, a numeric part of '#', '0', ',' and '.', and an optional literal suffix. '0' pads digits, '#' is optional, ',' groups thousands, digits after '.' fix the decimals ('0' required, '#' optional), and a '%' in the prefix or suffix multiplies the value by 100. English separators only. A format applies only to number values; st...\n\n_No named properties._\n\n\n### DateVariable\n\n- Type: `object`\n- Required fields: `type`\n- Purpose: A calendar date as an ISO YYYY-MM-DD string. No time zone and no clock are involved. '{{<id>}}' inserts it formatted with 'format'.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `type` | yes | `const:\"date\"` | Variable kind. One of color, text, number, date, image, url or list. |\n| `value` | no | `string` | ISO calendar date (YYYY-MM-DD) this variable resolves to. |\n| `required` | no | `boolean` | Whether the variable must be filled. Defaults to true: a variable with no 'value' is unfilled, and an unfilled required variable is an error in a normal deck and expected in a template. Set false for an optional slot:... |\n| `label` | no | `string` | Optional short human label for forms and fill panels. |\n| `description` | no | `string` | Optional prose describing what the variable is for, surfaced by pickers, fill forms and agents. |\n| `example` | no | `string` | Illustrative ISO date shown in fill forms and used when a template is previewed with examples. Never written to output. |\n| `format` | no | `string` | Date display pattern, the same LDML-style tokens as header/footer dateFormat: yyyy, yy, MMMM, MMM, MM, M, dd, d, EEEE, EEE and quoted literals. English names. Default 'MMMM d, yyyy'. |\n\n\n### ImageVariable\n\n- Type: `object`\n- Required fields: `type`\n- Purpose: An image source: any Asset (an 'asset:<id>' reference, HTTPS URL, data URI, relative or local path, or an object with src, alt and metadata). Reference it whole as 'var:<id>' in an image, asset or src field; '{{<id>}}' inserts the source string.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `type` | yes | `const:\"image\"` | Variable kind. One of color, text, number, date, image, url or list. |\n| `value` | no | `ref:Asset` | Image source this variable resolves to. |\n| `required` | no | `boolean` | Whether the variable must be filled. Defaults to true: a variable with no 'value' is unfilled, and an unfilled required variable is an error in a normal deck and expected in a template. Set false for an optional slot:... |\n| `label` | no | `string` | Optional short human label for forms and fill panels. |\n| `description` | no | `string` | Optional prose describing what the variable is for, surfaced by pickers, fill forms and agents. |\n| `example` | no | `ref:Asset` | Illustrative image source shown in fill forms and used when a template is previewed with examples. Never written to output. |\n\n\n### UrlVariable\n\n- Type: `object`\n- Required fields: `type`\n- Purpose: A link target (http, https, mailto or tel). Use it as '{{<id>}}' inside a link string or reference it whole as 'var:<id>' in a link field.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `type` | yes | `const:\"url\"` | Variable kind. One of color, text, number, date, image, url or list. |\n| `value` | no | `string` | Link target this variable resolves to. |\n| `required` | no | `boolean` | Whether the variable must be filled. Defaults to true: a variable with no 'value' is unfilled, and an unfilled required variable is an error in a normal deck and expected in a template. Set false for an optional slot:... |\n| `label` | no | `string` | Optional short human label for forms and fill panels. |\n| `description` | no | `string` | Optional prose describing what the variable is for, surfaced by pickers, fill forms and agents. |\n| `example` | no | `string` | Illustrative link shown in fill forms and used when a template is previewed with examples. Never written to output. |\n\n\n### ListVariable\n\n- Type: `object`\n- Required fields: `type`\n- Purpose: A list of strings, for bullets and list items. A whole-string array element 'var:<id>' splices every entry into the array in place; a whole field 'var:<id>' becomes the array; '{{<id>}}' joins the entries with ', ' (or the separator after a pipe: '{{<id>|; }}').\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `type` | yes | `const:\"list\"` | Variable kind. One of color, text, number, date, image, url or list. |\n| `value` | no | `array<string>` | Entries this variable resolves to. |\n| `required` | no | `boolean` | Whether the variable must be filled. Defaults to true: a variable with no 'value' is unfilled, and an unfilled required variable is an error in a normal deck and expected in a template. Set false for an optional slot:... |\n| `label` | no | `string` | Optional short human label for forms and fill panels. |\n| `description` | no | `string` | Optional prose describing what the variable is for, surfaced by pickers, fill forms and agents. |\n| `example` | no | `array<string>` | Illustrative entries shown in fill forms and used when a template is previewed with examples. Never written to output. |\n\n\n### BackgroundShortcut\n\n- Type: `oneOf:ref:ThemeBackgroundSlot / ref:HexColor / ref:ImageSource`\n- Required fields: none\n- Purpose: String shorthand for a background. Theme slots ('light1', 'light2', 'dark1', 'dark2') are equivalent to { type: 'theme', slot: value }; hex colors are equivalent to { type: 'solid', color: value }; an image source (starting with 'asset:', 'https://', 'data:', './' or '../') is equivalent to { type: 'image', src: value }, a cover image. Any other string is invalid.\n\n_No named properties._\n\n\n### ImageSource\n\n- Type: `string`\n- Required fields: none\n- Purpose: An image source in a string shorthand: an 'asset:<id>' reference, an HTTPS URL, a data URI, or a relative path starting with './' or '../' (resolved against the OPF file location).\n\n_No named properties._\n\n\n### ColorVariant\n\n- Type: `object`\n- Required fields: `background`\n- Purpose: A variant of the deck's single color scheme, as a mapping over its twelve slots (PowerPoint's clrMap): 'background' is the slot the slide background and the role bg1 take, and with it the other roles: light1 maps bg1 = light1, tx1 = dark1, bg2 = light2, tx2 = dark2; dark1 maps bg1 = dark1, tx1 = light1, bg2 = dark2, tx2 = light2; light2 and dark2 make that slot both the background and the alternate surface (light2: bg1 = bg2 = light2, tx1 = dark1, tx2 = dark2; dark2: bg1 = bg2 = dark2, tx1 =...\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `background` | yes | `ref:ThemeBackgroundSlot` | The scheme slot that is the background (bg1) of the variant. |\n| `accents` | no | `array<enum:accent1 \\| accent2 \\| accent3 \\| accent4 \\| accent5 \\| accent6>` | Optional accent order: a permutation of accent1 to accent6. Entry i is the scheme slot drawn as accent i + 1, so primary, secondary and accent (the first three) and the chart series colors follow it. Default: the sche... |\n\n\n### Background\n\n- Type: `oneOf:ref:ThemeBackground / ref:SolidBackground / ref:GradientBackground / ref:ImageBackground / ref:PatternBackground`\n- Required fields: none\n- Purpose: Background fill applied to slides. Theme backgrounds preserve PowerPoint's color-scheme background choice; other variants represent fixed background fills.\n\n_No named properties._\n\n\n### ThemeBackground\n\n- Type: `object`\n- Required fields: `type`, `slot`\n- Purpose: Theme-controlled PowerPoint slide background. The slot is resolved through the active color scheme and remains theme-aware.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `type` | yes | `const:\"theme\"` | Theme-controlled background fill. |\n| `slot` | yes | `ref:ThemeBackgroundSlot` | |\n\n\n### SolidBackground\n\n- Type: `object`\n- Required fields: `type`, `color`\n- Purpose: Fixed solid slide background fill.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `type` | yes | `const:\"solid\"` | Fixed solid background fill. |\n| `color` | yes | `ref:ColorRef` | Fixed solid fill color: a hex string, a color-scheme slot or role name, or a var:<id> variable reference (a ColorRef, resolved against the effective color scheme and the deck variables). Use { type: 'theme', slot: ...... |\n| `opacity` | no | `number` | Background opacity from 0 (fully transparent) to 1 (fully opaque). |\n\n\n### GradientBackground\n\n- Type: `object`\n- Required fields: `type`, `gradient`\n- Purpose: Fixed gradient slide background fill.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `tone` | no | `enum:dark \\| light` | Whether the background is dark or light, which picks the color map (dark1 or light1) so text, lines and neutrals read on it. Without a tone a picture or gradient takes the deck's variant, else counts as light. |\n| `type` | yes | `const:\"gradient\"` | Fixed gradient background fill. |\n| `gradient` | yes | `object` | Gradient fill definition. |\n| `opacity` | no | `number` | Background opacity from 0 (fully transparent) to 1 (fully opaque). |\n\n\n### ImageBackground\n\n- Type: `object`\n- Required fields: `type`, `src`\n- Purpose: Picture background: the image fills the whole canvas behind everything and never moves content (to push content aside, use an image block with placement). The string shorthand 'asset:x' (or any image source) is { type: 'image', src: 'asset:x' }, a cover image. Paint order: the colour scheme's default slide background, the picture, then the overlay, then furniture and content.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `tone` | no | `enum:dark \\| light` | Whether the background is dark or light, which picks the color map (dark1 or light1) so text, lines and neutrals read on it. Without a tone a picture or gradient takes the deck's variant, else counts as light. |\n| `type` | yes | `const:\"image\"` | Picture background. |\n| `src` | yes | `string` | Source for the background image: an 'asset:<id>' reference, HTTPS URL, data URI, or a relative path resolved against the OPF file location. |\n| `alt` | no | `string` | Alternative text. A background with alt is meaningful: PowerPoint export draws it as a full-slide picture at the back that carries the text, so screen readers see it. Without alt it is decorative and exports as the na... |\n| `fit` | no | `enum:cover \\| contain \\| stretch \\| tile` | How the picture fills the canvas: 'cover' fills it and crops what overflows around focus; 'contain' shows the whole picture centered over the colour scheme's default background; 'stretch' scales it to the canvas exact... |\n| `focus` | no | `ref:ImageFocus` | Point of the picture to keep in view when 'cover' crops it. Ignored by the other fits. |\n| `opacity` | no | `number` | Picture opacity from 0 (fully transparent) to 1 (fully opaque); the overlay keeps its own opacity. Default 1. |\n| `recolor` | no | `ref:ImageRecolor` | Grayscale or duotone treatment of the picture's pixels, as on an image block: recolor and opacity apply to the pixels, then the overlay is drawn above. |\n| `overlay` | no | `ref:Overlay` | Scrim over the whole picture or a band along one edge (edge, size), beneath furniture and content, for example to keep a title readable over a busy photo. |\n\n\n### ImageFocus\n\n- Type: `object`\n- Required fields: `x`, `y`\n- Purpose: A point of a picture as fractions of its width and height, from the top-left corner. A cover fit centers this point in the frame as far as the picture still covers the frame, so it always stays in view: { x: 0.5, y: 0.5 } (the default) is a center crop, { x: 0, y: 0 } keeps the top-left corner.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `x` | yes | `number` | Horizontal position, 0 (left edge) to 1 (right edge). |\n| `y` | yes | `number` | Vertical position, 0 (top edge) to 1 (bottom edge). |\n\n\n### Overlay\n\n- Type: `object`\n- Required fields: `color`, `opacity`\n- Purpose: Solid scrim over a picture: the whole frame in the frame's shape, or a band along one edge. Shared by image backgrounds and image blocks. Drawn directly above its picture and beneath headings and content; PowerPoint export writes one native shape above the picture.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `color` | yes | `ref:ColorRef` | Overlay fill color: a hex color (eight-digit hex keeps its alpha), a color-scheme slot or role name, or a 'var:<id>' variable reference. |\n| `opacity` | yes | `number` | Overlay fill opacity. |\n| `edge` | no | `enum:top \\| bottom \\| left \\| right` | Cover only a band along this edge of the frame, for example a caption strip. Backgrounds and rectangle image frames only; on another shape the overlay is not drawn and composition reports unsupported-image-treatment.... |\n| `size` | no | `number` | Band share of the frame for an edge overlay. Default 0.3. Ignored without edge. |\n\n\n### ImagePlacement\n\n- Type: `object`\n- Required fields: `edge`\n- Purpose: Bleeds an image block to one edge of the slide: the image takes a band along that edge, edge to edge, and the headings and other content compose in the rest of the slide at the normal padding. Only top-level blocks (slides.N.blocks.I) can be placed, at most one per edge. Template layout regions do not supply image placement; set it on the image block itself. Bands are taken in block order, each spanning the free area left by earlier ones. Placement is the only mechanism that moves content asi...\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `edge` | yes | `enum:left \\| right \\| top \\| bottom` | The slide edge the image bleeds to. |\n| `size` | no | `number` | Share of the slide width (left, right) or height (top, bottom) given to the band. Default 0.5. |\n| `inset` | no | `boolean` | Draw the frame inside the slide padding on every side of the band, like a card, instead of edge to edge. Default false. |\n\n\n### ImageBorder\n\n- Type: `object`\n- Required fields: `color`, `width`\n- Purpose: Solid line along the image frame's shape, centered on the outline with a miter join, like a native picture line. A thick dark border on a rounded portrait frame gives a device bezel.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `color` | yes | `ref:ColorRef` | Line color: a hex color, a color-scheme slot or role name, or a 'var:<id>' variable reference. |\n| `width` | yes | `number` | Line width in reference pixels at a 720-pixel short edge. 0 removes the line. |\n\n\n### ImageRecolor\n\n- Type: `oneOf:const:\"grayscale\" / object`\n- Required fields: none\n- Purpose: Color treatment for the image pixels. Luminance uses Rec. 601 weights (0.299, 0.587, 0.114) on sRGB values.\n\n_No named properties._\n\n\n### PatternBackground\n\n- Type: `object`\n- Required fields: `type`, `pattern`\n- Purpose: Fixed pattern slide background fill.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `type` | yes | `const:\"pattern\"` | Fixed pattern background fill. |\n| `pattern` | yes | `object` | Pattern fill definition. |\n| `opacity` | no | `number` | Background opacity from 0 (fully transparent) to 1 (fully opaque). |\n\n\n### OrganizationLogo\n\n- Type: `object`\n- Required fields: none\n- Purpose: An organization's logo in up to four shapes. Each shape is a path or Asset, or { onLight, onDark } for the two backgrounds. A missing shape falls back to full, and full to the first defined of wordmark, stacked and icon; within a shape a missing onLight or onDark uses the other. Shapes are placed with slide-scoped references such as 'var:organization.logo.icon'.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `full` | no | `ref:LogoSource` | The full logo (lockup): mark and name side by side. Drawn on cover and section slides, and placed by 'var:organization.logo'. |\n| `stacked` | no | `ref:LogoSource` | The stacked lockup: mark above the name, for portrait or square slots. Placed by 'var:organization.logo.stacked'. |\n| `icon` | no | `ref:LogoSource` | The mark or symbol without the name, for tight spaces such as header and footer zones and picture bullets. Placed by 'var:organization.logo.icon'. |\n| `wordmark` | no | `ref:LogoSource` | The name set in the brand's lettering, without the mark. Placed by 'var:organization.logo.wordmark'. |\n\n\n### LogoSource\n\n- Type: `oneOf:ref:Asset / object`\n- Required fields: none\n- Purpose: One logo shape: a path or Asset for every background, or { onLight, onDark }. onLight is drawn on light backgrounds (usually dark artwork) and onDark on dark backgrounds (usually light artwork), by each slide's background; a missing one uses the other.\n\n_No named properties._\n\n\n### Watermark\n\n- Type: `oneOf:schema / schema`\n- Required fields: `opacity`\n- Purpose: Decorative watermark: an image ('src') or a text stamp ('text'), exactly one of the two, with its opacity. An image is drawn once per slide, contained and centered in a fixed frame (the middle 40% of the slide width and height, from 30% to 70% on each axis). A text watermark is one line of text in the heading font and the theme text color at the given opacity, centered on the slide and rotated 30 degrees counterclockwise (rising to the right), sized to span at most 70% of the slide width and...\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `src` | no | `string` | Source for the watermark image: an 'asset:<id>' reference, HTTPS URL, data URI, relative path or local path, as for Asset.src. Exactly one of src and text is set. |\n| `text` | no | `string` | Text for a text watermark, such as DRAFT or CONFIDENTIAL; line breaks are drawn as spaces. Exactly one of src and text is set. |\n| `opacity` | yes | `number` | Watermark opacity from 0 (fully transparent) to 1 (fully opaque). |\n\n\n### HeaderFooter\n\n- Type: `object`\n- Required fields: none\n- Purpose: Repeated header or footer content split into left, center, and right zones. Header/footer content is slide furniture, separate from the main slide content payloads.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `left` | no | `ref:HeaderFooterItem` | Left-aligned header/footer content. |\n| `center` | no | `ref:HeaderFooterItem` | Centered header/footer content. |\n| `right` | no | `ref:HeaderFooterItem` | Right-aligned header/footer content. |\n\n\n### HeaderFooterItem\n\n- Type: `object`\n- Required fields: none\n- Purpose: One header/footer zone. Every configured field renders, side by side in one row in the order image, text, socials, date (mirrored in a right-to-left deck), aligned to the zone's edge (left, center or right) and vertically centered on each other; a line break inside text or socials ('\\n') stacks lines within that part. When the parts are wider than the zone their text wraps, and a row that cannot fit even so is reported as overflow. Generated values such as the slide number, the slide count, t...\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `text` | no | `string` | Text rendered in this zone. Variables resolve inside it like any string: deck-wide ones ('{{organization.name}}', '{{speaker.name}}, {{speaker.title}}', '{{customer}}') before composition, and the slide-scoped built-i... |\n| `image` | no | `ref:Asset` | Image rendered first in this zone's row, such as a logo, partner mark, certification badge, or icon: a path, URL or Asset, or an organization logo reference ('var:organization.logo.icon', 'var:organization.<id>.logo.w... |\n| `date` | no | `oneOf:boolean / string` | true renders the current date: the renderer or exporter must be given an explicit ISO date by its host (core never reads a clock). PPTX export writes a native date field only for a supported dateFormat whose complete... |\n| `dateFormat` | no | `string` | Date pattern for date. Tokens: yyyy (2026), yy (26), MMMM (April), MMM (Apr), MM (04), M (4), dd (09), d (9), EEEE (Thursday), EEE (Thu). Text in single quotes and other non-letter characters are literal. Month and we... |\n| `socials` | no | `boolean` | Whether to render the primary organization's social profiles from organization.socials, one line per platform in key order. A handle is formatted through the platform's socialPlatforms record (companyUrlPattern, else... |\n\n\n### Slide\n\n- Type: `object`\n- Required fields: none\n- Purpose: A single slide. Content can be authored as a full-slide root payload, or inside promoted named region keys such as 'left', 'center+right', and 'top:left'.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `id` | no | `string` | Optional stable identifier for the slide within the document. Use when another system needs to reference a slide across edits, comments, generation state, exports, or narrative tooling. Slide order is defined by the s... |\n| `type` | no | `enum:text \\| list \\| image \\| chart \\| table \\| video \\| code \\| metric \\| quote \\| timeline` | Optional full-slide content kind. When omitted, engines infer the kind from root payload fields. |\n| `beat` | no | `oneOf:string / array<string>` | Optional reference to one or more beats of the deck's narrative (each value is the id of a beat in the narrative record that the root 'narrative' resolves to). A single string declares the slide's primary beat; an arr... |\n| `layout` | no | `ref:CatalogReference` | Optional layout: a layouts reference, a bare id ('pillars') or 'name:id' ('acme:hero'), or 'auto'. When omitted or 'auto', the slide is composed with no layout record: engines arrange title, subtitle, tag and content... |\n| `title` | no | `oneOf:string / array<ref:TextRun>` | Slide-level title content. A v2 layout renders this in its title heading area, or an implicit heading row when that area is absent. Use a string for plain text or TextRun[] for inline rich text (a colored word, bold,... |\n| `subtitle` | no | `oneOf:string / array<ref:TextRun>` | Slide-level subtitle or supporting line. A v2 layout renders this in its subtitle area when present, otherwise in the title heading group. Use a string for plain text or TextRun[] for inline rich text (a colored word,... |\n| `tag` | no | `oneOf:string / array<ref:TextRun>` | Small slide-level label or badge. A v2 layout renders this before the title in its title heading group; tag is not a separate layout area. Use a string for plain text or TextRun[] for inline rich text (a colored word,... |\n| `text` | no | `oneOf:string / array<ref:TextRun>` | Full-slide text payload. Use a string for plain text or TextRun[] for inline rich text. TextRun items may be plain strings or formatted run objects. |\n| `items` | no | `array<ref:ListItem>` | Full-slide structured list payload. Presence of this field infers type 'list'. Entries are strings, TextRun[] or objects with `text`, optional `description` (supporting detail under the entry), `level` and `start`. Us... |\n| `bullets` | no | `array<ref:BulletItem>` | Full-slide prose bullet payload: short talking points, each standing alone. Presence of this field infers type 'text', so it may sit beside `text` in one payload. Entries are strings, TextRun[] or objects with `text`,... |\n| `numbering` | no | `ref:NumberingSpec` | Number the full-slide `items` or `bullets` instead of bulleting them. A style name (arabic, roman-upper, roman-lower, alpha-upper, alpha-lower) or a Numbering object applies to every list level; an array gives one ent... |\n| `image` | no | `ref:Asset` | Shorthand for one image block: the picture composes as body content, like { type: 'image', image: value }, with the effective design.imageFit. A v2 layout binds it to a compatible body region; a region's bleed setting... |\n| `video` | no | `ref:Asset` | Full-slide video source. Presence of this field infers type 'video'. Engines do not play video: the preview draws a placeholder (a play badge with the asset's title or source as its caption), and PPTX export draws the... |\n| `chart` | no | `ref:Chart` | Full-slide chart payload. Presence of this field infers type 'chart'. |\n| `table` | no | `ref:Table` | Full-slide table payload. Presence of this field infers type 'table'. |\n| `code` | no | `oneOf:string / ref:Code` | Full-slide code payload. A string is shorthand for { \"source\": value }; object form carries optional syntax language and filename metadata. |\n| `metric` | no | `oneOf:string / number / ref:Metric` | Full-slide metric payload. A string or number is shorthand for { \"value\": value }; object form carries optional label, description, unit, delta, trend, and sentiment metadata. Numeric values remain numbers; renderers... |\n| `quote` | no | `oneOf:string / ref:Quote` | Full-slide quote payload. A string is shorthand for { \"text\": value }; object form carries optional attribution and source metadata. Presence of this field infers type 'quote'. |\n| `timeline` | no | `ref:Timeline` | Full-slide timeline payload. An array is shorthand for { \"events\": value }; object form carries optional name and description metadata. Presence of this field infers type 'timeline'. |\n| `caption` | no | `ref:Caption` | Caption for the slide's root image, chart, table or video payload. Valid only when the slide root holds exactly one of those payloads. |\n| `blocks` | no | `array<ref:ContentPayload>` | Layout-agnostic content blocks rendered together as a composed payload when exact placement is unspecified. At slide root, multiple content payload kinds with no explicit type, blocks, or regions are accepted as short... |\n| `design` | no | `ref:SlideDesign` | Slide-level design applied on top of the deck-wide design. |\n| `left` | no | `ref:ContentPayload` | |\n| `center` | no | `ref:ContentPayload` | |\n| `right` | no | `ref:ContentPayload` | |\n| `left+center` | no | `ref:ContentPayload` | |\n| `center+right` | no | `ref:ContentPayload` | |\n| `left+center+right` | no | `ref:ContentPayload` | |\n| `top` | no | `ref:ContentPayload` | |\n| `middle` | no | `ref:ContentPayload` | |\n| `bottom` | no | `ref:ContentPayload` | |\n| `top+middle` | no | `ref:ContentPayload` | |\n| `middle+bottom` | no | `ref:ContentPayload` | |\n| `top+middle+bottom` | no | `ref:ContentPayload` | |\n| `top:left` | no | `ref:ContentPayload` | |\n| `top:center` | no | `ref:ContentPayload` | |\n| `top:right` | no | `ref:ContentPayload` | |\n| `top:left+center` | no | `ref:ContentPayload` | |\n| `top:center+right` | no | `ref:ContentPayload` | |\n| `top:left+center+right` | no | `ref:ContentPayload` | |\n| `middle:left` | no | `ref:ContentPayload` | |\n| `middle:center` | no | `ref:ContentPayload` | |\n| `middle:right` | no | `ref:ContentPayload` | |\n| `middle:left+center` | no | `ref:ContentPayload` | |\n| `middle:center+right` | no | `ref:ContentPayload` | |\n| `middle:left+center+right` | no | `ref:ContentPayload` | |\n| `bottom:left` | no | `ref:ContentPayload` | |\n| `bottom:center` | no | `ref:ContentPayload` | |\n| `bottom:right` | no | `ref:ContentPayload` | |\n| `bottom:left+center` | no | `ref:ContentPayload` | |\n| `bottom:center+right` | no | `ref:ContentPayload` | |\n| `bottom:left+center+right` | no | `ref:ContentPayload` | |\n| `top+middle:left` | no | `ref:ContentPayload` | |\n| `top+middle:center` | no | `ref:ContentPayload` | |\n| `top+middle:right` | no | `ref:ContentPayload` | |\n| `top+middle:left+center` | no | `ref:ContentPayload` | |\n| `top+middle:center+right` | no | `ref:ContentPayload` | |\n| `top+middle:left+center+right` | no | `ref:ContentPayload` | |\n| `middle+bottom:left` | no | `ref:ContentPayload` | |\n| `middle+bottom:center` | no | `ref:ContentPayload` | |\n| `middle+bottom:right` | no | `ref:ContentPayload` | |\n| `middle+bottom:left+center` | no | `ref:ContentPayload` | |\n| `middle+bottom:center+right` | no | `ref:ContentPayload` | |\n| `middle+bottom:left+center+right` | no | `ref:ContentPayload` | |\n| `top+middle+bottom:left` | no | `ref:ContentPayload` | |\n| `top+middle+bottom:center` | no | `ref:ContentPayload` | |\n| `top+middle+bottom:right` | no | `ref:ContentPayload` | |\n| `top+middle+bottom:left+center` | no | `ref:ContentPayload` | |\n| `top+middle+bottom:center+right` | no | `ref:ContentPayload` | |\n| `top+middle+bottom:left+center+right` | no | `ref:ContentPayload` | |\n| `notes` | no | `string` | Speaker notes shown in presenter view. |\n| `section` | no | `string` | PowerPoint-style slide section label. Consecutive slides with the same value belong to the same section in presenter view, outlines, and PowerPoint section-aware exports. Also the slide-scoped built-in variable '{{sli... |\n| `hidden` | no | `boolean` | Whether the slide is hidden from the presented sequence. The player skips it, the PPTX export writes it as a hidden slide, and per-slide image and PDF output skips it unless the caller asks to include hidden slides (o... |\n| `composition` | no | `ref:Composition` | |\n| `extensions` | no | `object` | Custom data passthrough for agent workflows at slide scope; ignored by the engine but preserved across read/write round-trips. Use for review state, generation provenance, or authoring conventions such as { \"authoring... |\n\n\n### ContentPayload\n\n- Type: `allOf:schema + schema + schema + schema + schema + schema + schema + schema + schema + schema + schema + schema + schema`\n- Required fields: none\n- Purpose: A content leaf or recursively composed group. A group contains blocks and optional composition; it cannot mix blocks with leaf payload fields. Groups may nest up to 32 levels.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `id` | no | `string` | Optional stable identifier for this payload, unique among slide and payload ids in the document. Use when another system needs to address the payload across edits patch-style agent edits, comments, review state, or ge... |\n| `extensions` | no | `object` | Custom data passthrough for agent workflows at payload scope; ignored by the engine but preserved across read/write round-trips. |\n| `region` | no | `string` | Pin a root block (slides.N.blocks.I) to a region of the slide's template layout by name (OPF 0.19). The block goes to that region when the region exists, accepts the block's kind and has room; otherwise the pin is ign... |\n| `type` | no | `enum:text \\| list \\| image \\| chart \\| table \\| video \\| code \\| metric \\| quote \\| timeline \\| group` | Optional content kind. When omitted, engines infer the kind from the fields present. |\n| `text` | no | `oneOf:string / array<ref:TextRun>` | Text payload. Use a string for plain text or TextRun[] for inline rich text. TextRun items may be plain strings or formatted run objects. |\n| `items` | no | `array<ref:ListItem>` | Structured list payload (type 'list'). Each item is either a plain string, a TextRun[] rich text sequence, or a ListItem object with optional `description`. List nesting uses item.level rather than nested content payl... |\n| `bullets` | no | `array<ref:BulletItem>` | Prose bullet payload: short talking points, each standing alone. Presence of this field infers type 'text', so it may sit beside `text` in one payload. Entries have no `description`; use `items` for entries with suppo... |\n| `numbering` | no | `ref:NumberingSpec` | Number the payload's `items` or `bullets` instead of bulleting them. A style name (arabic, roman-upper, roman-lower, alpha-upper, alpha-lower) or a Numbering object applies to every list level; an array gives one entr... |\n| `columns` | no | `oneOf:const:\"auto\" / integer` | Lay the payload's `items` or `bullets` out in columns (OPF 0.19). 'auto' tries 1, 2 then 3 equal columns at the starting text size and uses the first count at which the list fits, as long as a column stays at least a... |\n| `image` | no | `ref:Asset` | Source for an image item. |\n| `fit` | no | `enum:cover \\| contain \\| stretch` | Image payloads only. How the picture fills its frame: 'cover' fills it and crops what overflows around focus; 'contain' shows the whole picture centered; 'stretch' scales it to the frame exactly. Default: the effectiv... |\n| `focus` | no | `ref:ImageFocus` | Image payloads only. Point of the picture to keep in view when 'cover' crops it. Ignored by the other fits. |\n| `aspectRatio` | no | `number` | Image payloads only. Width-to-height ratio of the frame: the frame becomes the largest centered box with this ratio inside the block's region, for example 2.39 for a cinematic letterbox or 0.5 for a phone-shaped frame... |\n| `shape` | no | `enum:rectangle \\| rounded \\| circle \\| hexagon` | Image payloads only. Mask applied to the frame, exported as the picture's native preset geometry (rect, roundRect, ellipse, hexagon). 'circle' makes the frame square. Default 'rectangle'. |\n| `cornerRadius` | no | `number` | Image payloads only. Corner radius for shape 'rounded' as a fraction of the frame's shorter side. Default 0.16667, PowerPoint's roundRect default. |\n| `border` | no | `ref:ImageBorder` | Image payloads only. Line along the frame's shape. |\n| `opacity` | no | `number` | Image payloads only. Picture opacity from 0 to 1, exported as a native alphaModFix. The border and overlay keep their own opacity. Default 1. |\n| `recolor` | no | `ref:ImageRecolor` | Image payloads only. Grayscale or duotone treatment of the picture's pixels. |\n| `overlay` | no | `ref:Overlay` | Image payloads only. Scrim over the picture in the frame's shape, or a band along one edge of a rectangle frame. |\n| `placement` | no | `ref:ImagePlacement` | Image payloads only, and only on a top-level block (slides.N.blocks.I), at most one per edge. Bleeds the image to one slide edge; the headings and the other blocks compose in the rest of the slide. |\n| `video` | no | `ref:Asset` | Source for a video item. Engines do not play video: the preview draws a placeholder (a play badge with the asset's title or source as its caption), and PPTX export draws the same placeholder as native shapes linked to... |\n| `chart` | no | `ref:Chart` | Chart payload. Presence of this field infers type 'chart'. |\n| `table` | no | `ref:Table` | Table payload. Presence of this field infers type 'table'. |\n| `code` | no | `oneOf:string / ref:Code` | Code payload. A string is shorthand for { \"source\": value }; object form carries optional syntax language and filename metadata. |\n| `metric` | no | `oneOf:string / number / ref:Metric` | Metric payload. A string or number is shorthand for { \"value\": value }; object form carries optional label, description, unit, delta, trend, and sentiment metadata. Numeric values remain numbers; renderers format them... |\n| `quote` | no | `oneOf:string / ref:Quote` | Quote payload. A string is shorthand for { \"text\": value }; object form carries optional attribution, role, photo and source metadata. |\n| `timeline` | no | `ref:Timeline` | Timeline payload ordered by narrative or chronology. |\n| `caption` | no | `ref:Caption` | Caption for an image, chart, table or video payload, composed inside the block's region (below the media by default). Invalid on other payload kinds and on groups. |\n| `blocks` | no | `array<ref:ContentPayload>` | Ordered children of a group. Each child is a leaf or another group. |\n| `composition` | no | `ref:Composition` | Arrangement within this group. Only minFontSize and overflow inherit from the parent; strict overflow cannot be weakened. |\n\n\n### Quote\n\n- Type: `object`\n- Required fields: `text`\n- Purpose: Quote content with optional attribution metadata. Use 'text' for the quoted text, 'attribution' for the credited person or organization, 'role' for that person's title and organization, 'photo' for their headshot and 'source' for a citation or URL. A string value in a quote field is shorthand for { \"text\": value }.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `text` | yes | `oneOf:string / array<ref:TextRun>` | Quoted text: a string, or TextRun[] for inline rich text (a colored phrase, bold, a link, or a citation or footnote marker). The engine draws the surrounding quotation marks. |\n| `attribution` | no | `string` | Person or organization credited for the quote. Drawn after the quote text, in the muted text color. |\n| `role` | no | `string` | Title and organization of the attributed person. Drawn on its own line under the attribution; without an attribution it stands alone. It does not replace the attribution, so the person's name belongs in 'attribution'. |\n| `photo` | no | `ref:Asset` | Headshot of the attributed person, drawn as a circle beside the attribution and role lines (on the logical start side: left in a left-to-right deck, right in a right-to-left deck). Give it alt text (validate's opf/mis... |\n| `source` | no | `string` | Optional quote source, citation, or URL. Follows the attribution and role after ' - '. |\n\n\n### Code\n\n- Type: `object`\n- Required fields: `source`\n- Purpose: Code content with optional rendering metadata. Use 'source' for the code text, 'language' for syntax highlighting, 'filename' when the rendered block should show a file label, and 'highlight' to emphasize lines. A string value in a code field is shorthand for { \"source\": value }.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `source` | yes | `string` | Source code text to display. |\n| `language` | no | `string` | Language identifier used for syntax highlighting. |\n| `filename` | no | `string` | Optional file label shown with the code block. |\n| `highlight` | no | `array<oneOf:integer / array<integer>>` | Source lines to emphasize, 1-based: each entry is a line number or an inclusive [start, end] range, so [3, [5, 7]] marks lines 3 and 5 to 7. Lines are counted by line break in 'source', so a line that wraps stays one... |\n\n\n### Metric\n\n- Type: `object`\n- Required fields: `value`\n- Purpose: Metric content with optional display metadata. Use 'value' for the primary value, 'label' for the metric name, 'description' for supporting context, 'unit' for a suffix/currency marker, 'delta' for change, 'trend' for direction, and 'sentiment' for whether the change is good news. A string or number value in a metric field is shorthand for { \"value\": value }; numeric values remain numbers and are formatted by renderers.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `value` | yes | `oneOf:string / number` | Primary metric value. |\n| `label` | no | `string` | Metric label. |\n| `description` | no | `string` | Optional supporting context for the metric. |\n| `unit` | no | `string` | Metric unit, suffix, or currency marker. |\n| `delta` | no | `oneOf:string / number` | Metric change value. |\n| `trend` | no | `enum:up \\| down \\| flat` | Metric trend direction. The preview and the PowerPoint export draw an arrow beside the trend word that always points the way the trend does (up, down or flat) and colour the arrow, the trend word and the delta text by... |\n| `sentiment` | no | `enum:positive \\| negative \\| neutral` | Whether the change is good news. Positive draws the trend arrow and the trend and delta text in green, negative in red and neutral in the neutral text colour, each kept at 4.5:1 contrast or more against the slide back... |\n\n\n### Timeline\n\n- Type: `oneOf:array<ref:TimelineEvent> / object`\n- Required fields: none\n- Purpose: Timeline content. An array is shorthand for { \"events\": value }; object form carries optional name and description metadata.\n\n_No named properties._\n\n\n### TimelineEvent\n\n- Type: `object`\n- Required fields: `what`\n- Purpose: A single event inside a timeline content payload.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `when` | no | `string` | Event time, date, or sequence label. Use ISO-like values when possible, but human labels are allowed for quarters, eras, and relative milestones. |\n| `what` | yes | `string` | Short event label. |\n| `description` | no | `string` | Optional event detail. |\n| `status` | no | `enum:done \\| current \\| planned` | Progress of the event, drawn from the deck's own colors by both the SVG renderer and the PPTX exporter. Absent means no status and the event is drawn as a plain filled marker with normal text, which is also how 'done'... |\n\n\n### ListItem\n\n- Type: `oneOf:string / array<ref:TextRun> / object`\n- Required fields: none\n- Purpose: A flat item inside a list. Strings cover the common case, TextRun[] supports inline rich text without an object wrapper, and object form adds description and nesting depth without creating nested slide content payloads.\n\n_No named properties._\n\n\n### BulletItem\n\n- Type: `oneOf:string / array<ref:TextRun> / object`\n- Required fields: none\n- Purpose: A flat bullet item. Strings cover the common case, TextRun[] supports inline rich text without an object wrapper, and object form adds nesting depth without list-item descriptions.\n\n_No named properties._\n\n\n### NumberingStyle\n\n- Type: `enum:arabic | roman-upper | roman-lower | alpha-upper | alpha-lower`\n- Required fields: none\n- Purpose: A list number style: 1, 2, 3; I, II, III; i, ii, iii; A, B, C; a, b, c. Alphabetic numbering past 26 repeats the letter as PowerPoint does (aa, bb, cc). Roman numerals stop at 3999; larger values are drawn in arabic with a numbering-adapted diagnostic.\n\n_No named properties._\n\n\n### Numbering\n\n- Type: `object`\n- Required fields: none\n- Purpose: Numbering of one list level.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `style` | no | `ref:NumberingStyle` | Number style. Default arabic. |\n| `start` | no | `integer` | First number counted at this level. Default 1. Native PowerPoint accepts 1 to 32767. |\n| `suffix` | no | `enum:period \\| paren \\| paren-both` | Text after the number: period (1.), paren (1)) or paren-both ((1)). Default period. |\n\n\n### NumberingSpec\n\n- Type: `oneOf:ref:NumberingStyle / ref:Numbering / array<oneOf:ref:NumberingStyle / ref:Numbering>`\n- Required fields: none\n- Purpose: The value of a numbering field: a style name or Numbering object for every level, or an array with one entry per level (at most 9, the native depth).\n\n_No named properties._\n\n\n### TextRun\n\n- Type: `oneOf:string / object`\n- Required fields: none\n- Purpose: A contiguous run of text. Strings cover unformatted spans; object form adds character formatting.\n\n_No named properties._\n\n\n### Caption\n\n- Type: `oneOf:string / array<ref:TextRun> / object`\n- Required fields: none\n- Purpose: A caption for an image, chart, table or video payload. A string or TextRun[] is the caption text placed below the media; object form adds the position and alignment.\n\n_No named properties._\n\n\n### Reference\n\n- Type: `object`\n- Required fields: `id`, `text`\n- Purpose: A source that runs cite with 'cite'. Cited references are listed in the footnote area of the slides that cite them, numbered per deck in order of first use; referencesSlide() builds an ordinary list slide of them.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `id` | yes | `string` | Identifier runs cite. Unique within the references list. |\n| `text` | yes | `oneOf:string / array<ref:TextRun>` | The reference as it is listed: a string or TextRun[] for inline rich text. |\n| `url` | no | `string` | Optional link for the reference; a references slide links its entry to it. |\n\n\n### Chart\n\n- Type: `object`\n- Required fields: `type`, `data`\n- Purpose: Chart content. The chart object keeps chart-specific fields together so slides and regions do not expose loose chart fields.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `type` | yes | `enum:100pct-stacked-area \\| 100pct-stacked-bar \\| 100pct-stacked-column \\| area \\| bar \\| box-and-whisker \\| column \\| combo \\| doughnut \\| filled-radar \\| funnel \\| histogram \\| line \\| line-with-markers \\| pareto \\| pie \\| radar \\| radar-with-markers \\| scatter \\| stacked-area \\| stacked-bar \\| stacked-column \\| stacked-line \\| stacked-line-with-markers \\| treemap \\| waterfall \\| world` | Chart type, an engine vocabulary validated directly (no catalog lookup): the chart types Aspose.Slides officially supports, plus 'combo' (clustered columns with line series, see 'line' and 'secondaryAxis'). Each engin... |\n| `data` | yes | `oneOf:ref:ChartData / ref:DatasetRef` | Chart data. Inline data uses a tabular columns/rows shape; renderers convert rows to chart series internally. A DatasetRef ({ \"dataset\": \"<id>\" }) plots a top-level dataset instead. Data from a spreadsheet or file is... |\n| `mapping` | no | `ref:ChartMapping` | Optional series mapping by column name: which column is the category, which is the X column of a scatter chart, and which columns are plotted, in order. Absent keeps the positional rule (first column the category, the... |\n| `line` | no | `array<string>` | Combo charts only (type 'combo'): the plotted series, by column name, that are drawn as lines with markers; every other plotted series is drawn as clustered columns. Absent: the last plotted series is the line. A comb... |\n| `secondaryAxis` | no | `array<string>` | Combo charts only: line series, by column name, plotted against a secondary value axis at the right of the plot. That axis has its own scale, and its tick labels use the number format of the first secondary series' co... |\n| `axisTitles` | no | `ref:ChartAxisTitles` | Optional axis titles. Absent keeps today's untitled axes. Supported on the chart types that have a category/value (or X/Y) axis pair (column, bar, line, area, scatter, and the histogram, pareto, waterfall and box-and-... |\n| `legend` | no | `enum:none \\| top \\| bottom \\| left \\| right` | Optional legend position. 'none' hides the legend. Absent keeps today's behaviour exactly (a legend at the right of multi-series charts and of pie and doughnut charts, none for single-series charts). A named position... |\n| `dataLabels` | no | `oneOf:boolean / ref:ChartDataLabels` | Optional data labels. true shows value labels at each type's default position, false (or absent) shows none, which is today's behaviour. Use the object form for the label content, position and separator. |\n| `alt` | no | `string` | Text alternative for the chart: what the data shows (the point and the key numbers), not 'a chart' or 'bar chart'. The preview exposes it as the chart's accessible name (role img with aria-label) and the PowerPoint ex... |\n| `colors` | no | `array<ref:ColorRef>` | Optional series colors, by position: entry i replaces palette color i, the color of series i (the i-th plotted value column), of slice or category i on a pie, doughnut, treemap or funnel chart, and of the i-th color a... |\n| `highlight` | no | `ref:ChartHighlight` | Optional emphasis: the series and/or categories that carry the message. The engines draw the highlighted marks in the deck's primary (accent) color and every other mark in a muted neutral derived from the theme (a lig... |\n\n\n### ChartAxisTitles\n\n- Type: `object`\n- Required fields: none\n- Purpose: Titles for the axes of a chart. 'category' is the axis that carries the row labels (the horizontal axis of a column or line chart, the vertical axis of a bar chart, the X axis of a scatter chart); 'value' is the other axis; 'secondary' is the secondary value axis of a combo chart.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `category` | no | `string` | Title of the category (X) axis. |\n| `value` | no | `string` | Title of the value (Y) axis. On a combo chart, the primary (left) value axis. |\n| `secondary` | no | `string` | Combo charts only: title of the secondary value axis at the right of the plot, drawn rotated like the primary value axis title. Dropped with a 'chart-option-adapted' diagnostic on any other chart type and on a combo c... |\n\n\n### ChartHighlight\n\n- Type: `object`\n- Required fields: none\n- Purpose: Which series and categories a chart emphasizes. Names are matched exactly: series by data column name (after any 'fields' selection; a series is a plotted value column), categories by row label (the first column, or mapping.category), as text. A category label shared by several rows highlights each of them.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `series` | no | `array<string>` | Series (value columns) to highlight: all marks of each named series. A name that is not a column is a 'chart-highlight-unknown-name' error; a column that is not plotted as a series (the category or X column, or one ma... |\n| `categories` | no | `array<string>` | Categories (row labels) to highlight: the marks of each named category (a column or bar of every series, a line's points, a pie or doughnut slice). A label no row has is a 'chart-highlight-unknown-name' error. |\n\n\n### ChartDataLabels\n\n- Type: `object`\n- Required fields: none\n- Purpose: Data label settings. A label shows the selected content parts in the fixed order category, value, percent, joined by the separator.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `content` | no | `array<enum:value \\| percent \\| category>` | Which parts a label shows. Defaults to ['value']. 'percent' is the share of the total and exists only on pie and doughnut charts; 'category' shows the category name (the X value on a scatter chart). A part a chart typ... |\n| `position` | no | `enum:auto \\| center \\| inside-end \\| inside-base \\| outside-end \\| above \\| below \\| left \\| right` | Where a label sits relative to its mark. 'auto' (the default) is the type's default: outside-end for clustered columns and bars, pie slices and the histogram, pareto and waterfall constructs; center for stacked column... |\n| `separator` | no | `string` | Text between the parts of a label that shows more than one. Defaults to ', '. |\n\n\n### Table\n\n- Type: `object`\n- Required fields: none\n- Purpose: Table content, inline or dataset-backed. An inline table has 'rows' (required) and optional 'columns'. A dataset-backed table has 'dataset' (required) and optional 'fields', and no 'rows' or 'columns': it takes its headers, rows and column formats from the dataset (per-cell styles need an inline table).\n- Conditional requirement: `dataset`, or otherwise `rows`\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `columns` | no | `array<oneOf:string / array<ref:TextRun> / ref:StyledTableCell / ref:DataColumn / null>` | Optional column labels. Labels may be strings, rich runs, styled cell objects or DataColumn objects ({ \"name\", \"format\" }). A header's 'format' (DataColumn or StyledTableCell) is the column's number format; a body cel... |\n| `rows` | no | `array<array<ref:TableCell>>` | Two-dimensional table row data; each row aligns by index with columns when columns are supplied. |\n| `dataset` | no | `ref:DatasetId` | Id of a top-level dataset that supplies this table's headers, rows and column formats. Excludes 'rows' and 'columns'. An unknown id is a 'dataset-unknown' error. |\n| `fields` | no | `ref:DatasetFields` | Dataset tables only: the dataset columns to show, by name and in order. Absent shows every column. An unknown name is a 'dataset-field-unknown' error. |\n| `alt` | no | `string` | Text alternative for the table: what it shows (the point and the key numbers), not 'a table'. The preview exposes it as the table's accessible name (role group with aria-label; the cells stay readable) and the PowerPo... |\n\n\n### ChartData\n\n- Type: `object`\n- Required fields: `columns`, `rows`\n- Purpose: Inline tabular data driving a chart. The first column usually supplies category/x-axis labels; subsequent columns are plotted measures unless 'chart.mapping' or a chart type maps them differently. Value cells are numbers; a string is read only in strict decimal syntax ('12', '-3.5', '1e6'), and anything else ('12%', '$5', '(5)', '1,234') is a gap and a 'chart-value-not-numeric' warning.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `columns` | yes | `array<oneOf:string / ref:DataColumn>` | Ordered column labels for the chart data table. A label is a string or a DataColumn ({ \"name\", \"format\" }) whose number format the data labels, value axis and exported workbook use. |\n| `rows` | yes | `array<array<ref:ChartDataCell>>` | Tabular chart rows. Each row aligns by index with columns. |\n| `source` | no | `ref:DataSourceRef` | Optional provenance: where this inline data came from. Engines never read, fetch or refresh it; they keep it through editing, export and re-import. |\n\n\n### DataColumn\n\n- Type: `object`\n- Required fields: `name`\n- Purpose: A named data column with an optional number format. Anywhere a chart or dataset column is a string it may be a DataColumn; the string form is { \"name\": value } with no format. A table column header may also be a DataColumn.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `name` | yes | `string` | Column label, used as the series name, the table header and the name 'fields' and 'chart.mapping' address. |\n| `format` | no | `ref:NumberFormat` | Number format of the column's number values (data labels, value axis, table cells, exported workbook). Absent: the General form. |\n\n\n### DataSourceRef\n\n- Type: `object`\n- Required fields: `src`\n- Purpose: Provenance of inline data or a dataset: the file, sheet and range it came from and when. Engines never read, fetch or refresh it; they keep it through editing, export and re-import. Re-import the data to update it.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `src` | yes | `string` | Where the data came from: an 'asset:<id>' reference, a URL, or a relative or local path. |\n| `sheet` | no | `string` | Sheet or table name inside a spreadsheet source. |\n| `range` | no | `string` | A1-style range inside a spreadsheet source. |\n| `fields` | no | `array<string>` | The source fields the data was taken from, in order. |\n| `retrieved` | no | `anyOf:schema / schema` | When the data was taken from the source: an ISO 8601 date or date-time. |\n| `description` | no | `string` | Free-form note about the source. |\n\n\n### DatasetId\n\n- Type: `string`\n- Required fields: none\n- Purpose: Id of an entry in the top-level datasets map (the assets id pattern).\n\n_No named properties._\n\n\n### DatasetFields\n\n- Type: `array<string>`\n- Required fields: none\n- Purpose: Dataset column names to use, in order, each at most once. Selects and orders the dataset's columns; an unknown name is a 'dataset-field-unknown' error.\n\n_No named properties._\n\n\n### Datasets\n\n- Type: `object`\n- Required fields: none\n- Purpose: Shared data tables keyed by id (the assets id pattern). Charts reference one with 'chart.data': { \"dataset\": \"<id>\" } and tables with 'table': { \"dataset\": \"<id>\" }.\n\n_No named properties._\n\n\n### Dataset\n\n- Type: `object`\n- Required fields: `columns`, `rows`\n- Purpose: One shared data table: named columns (strings or DataColumn objects with a number format) and rows of scalar cells. Column names are unique ('data-column-duplicate' error). Rich text and styling belong in an inline table.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `title` | no | `string` | Human name of the dataset, shown by editors. |\n| `description` | no | `string` | What the dataset holds. |\n| `columns` | yes | `array<oneOf:string / ref:DataColumn>` | Ordered, uniquely named columns. |\n| `rows` | yes | `array<array<ref:ChartDataCell>>` | Rows of scalar cells (string, number, boolean, null); each row aligns by index with columns. |\n| `source` | no | `ref:DataSourceRef` | Optional provenance. Engines never read, fetch or refresh it. |\n\n\n### DatasetRef\n\n- Type: `object`\n- Required fields: `dataset`\n- Purpose: Chart data taken from a top-level dataset. Engines inline it before plotting; 'chart.mapping' names columns after the 'fields' selection.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `dataset` | yes | `ref:DatasetId` | Id of the dataset. An unknown id is a 'dataset-unknown' error. |\n| `fields` | no | `ref:DatasetFields` | The dataset columns to use, by name and in order. Absent uses every column. |\n\n\n### ChartMapping\n\n- Type: `object`\n- Required fields: none\n- Purpose: Series mapping by column name (after any 'fields' selection). Mapping only selects and orders columns; pie, doughnut and other single-series constructs still plot one series. An unknown name is a 'chart-mapping-unknown-column' error; a series that repeats the category or X column, and an X column on a chart type without an X axis, are dropped with a 'chart-mapping-adapted' warning.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `category` | no | `string` | The label (category) column. Default: the first column. |\n| `x` | no | `string` | The X column of an XY (scatter) chart. Default: the second column (the first, when the category is the second column), and only when there are three or more columns: with two, the second column is the one series, plot... |\n| `series` | no | `array<string>` | The plotted columns, in order. Default: every column that is not the category or the X column. |\n\n\n### ChartDataCell\n\n- Type: `oneOf:string / number / boolean / null`\n- Required fields: none\n- Purpose: A cell in inline chart data.\n\n_No named properties._\n\n\n### TableCell\n\n- Type: `oneOf:ref:TableCellValue / ref:StyledTableCell`\n- Required fields: none\n- Purpose: A scalar, rich-run array, or styled/spanning cell object. Existing scalar and rich forms remain valid.\n\n_No named properties._\n\n\n### TableCellValue\n\n- Type: `oneOf:string / number / boolean / null / array<ref:TextRun>`\n- Required fields: none\n- Purpose: A scalar table value or canonical rich text runs, without cell decoration or geometry.\n\n_No named properties._\n\n\n### StyledTableCell\n\n- Type: `object`\n- Required fields: `value`\n- Purpose: A cell with explicit visual style or merged geometry. Its position remains its array column index; use null placeholders for every covered grid position.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `value` | yes | `ref:TableCellValue` | Editable cell content; styling and spans do not change its scalar type or rich runs. |\n| `style` | no | `ref:TableCellStyle` | |\n| `colSpan` | no | `integer` | Number of grid columns covered, starting at this cell. Covered positions must contain null. Default 1. |\n| `rowSpan` | no | `integer` | Number of grid rows covered, starting at this cell. Covered positions must contain null. Header cells cannot span into body rows. Default 1. |\n| `format` | no | `ref:NumberFormat` | Number format. On a body cell it formats that cell's number value (and wins over the column's format); on a header cell (in 'columns') it is the column's format, the same as a DataColumn header. Strings, booleans, nul... |\n\n\n### TableCellStyle\n\n- Type: `object`\n- Required fields: none\n- Purpose: Cell appearance. Sizes use reference pixels at a 720-pixel canvas short edge and scale with the slide.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `fill` | no | `ref:ColorRef` | Cell background: a hex color, a color-scheme slot or role name, or a 'var:<id>' variable reference. Eight-digit hex colors include alpha; #00000000 is transparent. |\n| `color` | no | `ref:ColorRef` | Default text color, overridden by individual rich run colors. Accepts a hex color, a color-scheme slot or role name, or a 'var:<id>' variable reference. |\n| `bold` | no | `boolean` | Bold cell text. Runs that set their own bold keep it; a number cell stays bold and still uses the cell's or column's number format. |\n| `align` | no | `enum:left \\| center \\| right` | Horizontal text alignment inside the cell. |\n| `verticalAlign` | no | `enum:top \\| middle \\| bottom` | Vertical alignment inside the padded cell box. |\n| `padding` | no | `ref:TableCellPadding` | |\n| `borders` | no | `object` | Independent cell edges. Omitted edges retain the table theme border; width 0 removes an edge. |\n\n\n### TableCellPadding\n\n- Type: `object`\n- Required fields: none\n- Purpose: Text insets in reference pixels. Defaults: top 8, right 10, bottom 4, left 10.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `top` | no | `number` | |\n| `right` | no | `number` | |\n| `bottom` | no | `number` | |\n| `left` | no | `number` | |\n\n\n### TableCellBorder\n\n- Type: `object`\n- Required fields: `color`, `width`\n- Purpose: One explicit cell border.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `color` | yes | `ref:ColorRef` | Border color: a hex color, a color-scheme slot or role name, or a 'var:<id>' variable reference. Eight-digit hex colors include alpha; #00000000 is transparent. |\n| `width` | yes | `number` | Border width in reference pixels; 0 removes this edge. |\n| `dash` | no | `enum:solid \\| dash \\| dot` | Default solid. |\n\n\n### Catalogs\n\n- Type: `object`\n- Required fields: none\n- Purpose: The catalog records a document embeds, grouped by the catalog they came from. Each property is a group; inside a group, records are keyed by kind and then by id. - 'default': the catalog bare ids come from. It needs a 'source' when it holds records. Omitted: bare ids fall back to the host's default catalog. false: no catalog fallback, so every bare id must be embedded (under 'custom' or 'default'). - 'custom': the records the document defines itself. It has no source. - any other name ('acme'...\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `default` | no | `oneOf:allOf:ref:CatalogGroup + schema / const:false` | The catalog bare ids come from: its source and the records of it this document embeds. false turns off the catalog fallback for bare ids. |\n| `custom` | no | `allOf:ref:CatalogGroup + schema` | Records the document defines itself, referenced by bare id. Checked first for every bare id. Forking a catalog record copies it here under a new id, so the change of ownership is visible. |\n\n\n### CatalogGroup\n\n- Type: `object`\n- Required fields: none\n- Purpose: One catalog group: the catalog's source and the records of it the document embeds, by kind and then by id.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `source` | no | `ref:CatalogSource` | |\n| `layouts` | no | `ref:CatalogRecords` | Layout records keyed by id, each with the fields of https://openpresentation.org/schema/opf-layout/v2. Referenced from Slide.layout. |\n| `themes` | no | `ref:CatalogRecords` | Theme records keyed by id, each with the fields of https://openpresentation.org/schema/opf-theme/v1. Referenced from design.theme. |\n| `colorSchemes` | no | `ref:CatalogRecords` | Colour-scheme records keyed by id, each with the fields of https://openpresentation.org/schema/opf-color-scheme/v1. Referenced from design.colorScheme and a theme's colorScheme. |\n| `fontSchemes` | no | `ref:CatalogRecords` | Font-scheme records keyed by id, each with the fields of https://openpresentation.org/schema/opf-font-scheme/v1. Referenced from design.fontScheme, a theme's fontScheme and a language's font schemes. |\n| `narratives` | no | `ref:CatalogRecords` | Narrative records keyed by id, each with the fields of https://openpresentation.org/schema/opf-narrative/v1. Referenced from narrative. |\n| `audiences` | no | `ref:CatalogRecords` | Audience records keyed by id, each with the fields of https://openpresentation.org/schema/opf-audience/v1. Referenced from audience. |\n| `purposes` | no | `ref:CatalogRecords` | Purpose records keyed by id, each with the fields of https://openpresentation.org/schema/opf-purpose/v1. Referenced from purpose. |\n| `tones` | no | `ref:CatalogRecords` | Tone records keyed by id, each with the fields of https://openpresentation.org/schema/opf-tone/v1. Referenced from tone. |\n\n\n### CatalogSource\n\n- Type: `string`\n- Required fields: none\n- Purpose: Identity of a catalog: an HTTPS URL ('https://www.pptx.gallery') or a package reference ('pkg:@acme/opf-catalog'). It names the catalog; engines never fetch or install it. A host registers the catalog's records under this exact string, and copying slides between documents matches groups by it.\n\n_No named properties._\n\n\n### CatalogRecords\n\n- Type: `object`\n- Required fields: none\n- Purpose: Embedded records of one kind, keyed by id. The key is the record's id; an embedded record carries no '$schema', 'id', origin or digest. Catalog display metadata ('x-*' members) is stripped when a record is embedded.\n\n_No named properties._\n\n\n### CatalogReference\n\n- Type: `string`\n- Required fields: none\n- Purpose: A content reference: a bare id ('two-column') or 'name:id' ('acme:hero'), where the prefix names a group of 'catalogs'. URLs and 'pkg:' strings are not references; a named catalog group replaces them. See Catalogs for the resolution order.\n\n_No named properties._\n\n\n### LanguageTag\n\n- Type: `string`\n- Required fields: none\n- Purpose: A BCP-47 language tag ('en-US', 'ja', 'zh-Hans', 'sr-Latn-ME'), an engine vocabulary. Use 'en-GB' for UK English; 'en-UK' is not a valid region.\n\n_No named properties._\n\n\n### Composition\n\n- Type: `object`\n- Required fields: none\n- Purpose: Automatic slide and content-group arrangement. Nested content groups arrange their children independently, inheriting only minFontSize and overflow. Explicit promoted regions retain their positions. A v2 layout defines its grid through areas, tracks and regions; its composition supports gap, padding, minFontSize and overflow rather than mode, columns or weights.\n\n| Field | Required | Type | Notes |\n| --- | --- | --- | --- |\n| `mode` | no | `enum:auto \\| grid \\| row \\| column` | auto chooses a grid from available space and content; grid uses columns; row and column use one horizontal or vertical track. |\n| `columns` | no | `integer` | Column count for grid. In auto mode this caps the number of columns. |\n| `gap` | no | `number` | Space between cells as a fraction of the container short edge (canvas at slide root). Default 0.03333333333333333. |\n| `padding` | no | `number` | Inset as a fraction of the container short edge. Default 0.08 on a slide, 0 inside a group. |\n| `weights` | no | `array<number>` | Relative track sizes: columns for row/grid/auto, rows for column. Omitted tracks have weight 1; extra weights are ignored. |\n| `minFontSize` | no | `number` | Minimum readable text size in reference pixels at a 720-pixel canvas short edge. Default 16. Overflow is diagnosed when text cannot fit at this size. |\n| `overflow` | no | `enum:warn \\| error` | warn returns diagnostics for content that does not fit; error rejects layout. Content is never silently removed. Default warn. |\n"
308
308
  },
309
309
  {
310
310
  "slug": "security-2026-09-09",