@taloswork/ose-shared 0.1.11 → 0.1.13

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 (159) hide show
  1. package/package.json +3 -3
  2. package/skills/document/SKILL.md +135 -219
  3. package/skills/document/art-directions/base.css +27 -2
  4. package/skills/document/art-directions/cinematic-depth.css +3 -1
  5. package/skills/document/art-directions/collage-paper.css +5 -1
  6. package/skills/document/art-directions/editorial-cut.css +5 -1
  7. package/skills/document/art-directions/geometric-pop.css +6 -0
  8. package/skills/document/art-directions/gradient-mesh.css +5 -1
  9. package/skills/document/art-directions/illustrated-doodle.css +5 -0
  10. package/skills/document/art-directions/luxury-ornament.css +4 -1
  11. package/skills/document/art-directions/maximalist-blocks.css +6 -0
  12. package/skills/document/art-directions/technical-grid.css +4 -0
  13. package/skills/document/assets/fonts/fonts.json +2 -2
  14. package/skills/document/assets/pdf-fonts/NotoSansSC-PDF-Bold.ttf +0 -0
  15. package/skills/document/assets/pdf-fonts/NotoSansSC-PDF-Regular.ttf +0 -0
  16. package/skills/document/assets/pdf-fonts/OFL.txt +93 -0
  17. package/skills/document/assets/pdf-fonts/README.md +35 -0
  18. package/skills/document/assets/tessdata/LICENSE-Apache-2.0.txt +202 -0
  19. package/skills/document/assets/tessdata/README.md +21 -0
  20. package/skills/document/assets/tessdata/chi_sim.traineddata.gz +0 -0
  21. package/skills/document/assets/tessdata/eng.traineddata.gz +0 -0
  22. package/skills/document/bin/deck-build.cjs +12563 -83
  23. package/skills/document/bin/deck-chart.cjs +771 -0
  24. package/skills/document/bin/deck-plan.cjs +12414 -0
  25. package/skills/document/bin/deck-qa.cjs +1075 -266
  26. package/skills/document/bin/deck-scenario.cjs +1363 -0
  27. package/skills/document/bin/deck-styles.cjs +1604 -0
  28. package/skills/document/bin/doc-convert.cjs +308 -292
  29. package/skills/document/bin/doc-fill.cjs +73264 -192
  30. package/skills/document/bin/doc-gen.cjs +2491 -312
  31. package/skills/document/bin/doc-run.cjs +162 -24
  32. package/skills/document/bin/doc-scan.cjs +13 -0
  33. package/skills/document/bin/doc-text.cjs +13 -0
  34. package/skills/document/bin/doc-validate.cjs +838 -68
  35. package/skills/document/bin/doc-xml.cjs +9 -5
  36. package/skills/document/bin/docx-annotate.cjs +95 -19
  37. package/skills/document/bin/docx-defaults.cjs +9907 -0
  38. package/skills/document/bin/fontkit.cjs +1 -0
  39. package/skills/document/bin/html2pptx.cjs +36622 -31394
  40. package/skills/document/bin/ocr-worker.cjs +1 -0
  41. package/skills/document/bin/ocr.cjs +131 -40
  42. package/skills/document/bin/pdf-form.cjs +682 -177
  43. package/skills/document/bin/pdf-images.cjs +13 -0
  44. package/skills/document/bin/pdf-ops.cjs +642 -199
  45. package/skills/document/bin/pdf-render.cjs +19 -3
  46. package/skills/document/bin/pdf-tables.cjs +13 -0
  47. package/skills/document/bin/pptx-clone.cjs +3 -3
  48. package/skills/document/bin/pptx-notes.cjs +10038 -0
  49. package/skills/document/bin/pptx-slide.cjs +3 -3
  50. package/skills/document/bin/pptx-tokenize.cjs +19 -4
  51. package/skills/document/bin/template-skin.cjs +10910 -0
  52. package/skills/document/bin/xlsx-query.cjs +192 -61
  53. package/skills/document/bin-reference.md +61 -33
  54. package/skills/document/catalog.json +33 -32
  55. package/skills/document/content-pipeline.md +179 -257
  56. package/skills/document/deck-design.md +378 -638
  57. package/skills/document/generated-visuals.md +99 -0
  58. package/skills/document/layouts/MAINTAINERS.md +33 -0
  59. package/skills/document/layouts/README.md +49 -36
  60. package/skills/document/layouts/big-stat.tpl +1 -1
  61. package/skills/document/layouts/capacity.json +1 -0
  62. package/skills/document/layouts/cards-3.tpl +1 -1
  63. package/skills/document/layouts/chart-full.tpl +1 -1
  64. package/skills/document/layouts/chart-kpi.tpl +1 -1
  65. package/skills/document/layouts/comparison.tpl +1 -1
  66. package/skills/document/layouts/data-table.tpl +1 -1
  67. package/skills/document/layouts/funnel.tpl +1 -1
  68. package/skills/document/layouts/gantt.tpl +1 -1
  69. package/skills/document/layouts/hub-flow.tpl +4 -0
  70. package/skills/document/layouts/icon-row.tpl +1 -1
  71. package/skills/document/layouts/image-grid.tpl +1 -1
  72. package/skills/document/layouts/matrix-2x2.tpl +1 -1
  73. package/skills/document/layouts/mock-qa.tpl +4 -0
  74. package/skills/document/layouts/profiles.json +2 -2
  75. package/skills/document/layouts/pyramid.tpl +1 -1
  76. package/skills/document/layouts/radar.tpl +1 -1
  77. package/skills/document/layouts/ring-progress.tpl +1 -1
  78. package/skills/document/layouts/slots.json +1 -1
  79. package/skills/document/layouts/steps-vertical.tpl +1 -1
  80. package/skills/document/layouts/steps.tpl +1 -1
  81. package/skills/document/layouts/three-kpi.tpl +1 -1
  82. package/skills/document/layouts/timeline.tpl +1 -1
  83. package/skills/document/layouts/toc-panel.tpl +2 -2
  84. package/skills/document/layouts/two-kpi.tpl +2 -2
  85. package/skills/document/layouts/versus.tpl +1 -1
  86. package/skills/document/motion/README.md +3 -2
  87. package/skills/document/office.md +3 -3
  88. package/skills/document/packs/README.md +1 -1
  89. package/skills/document/packs/board-brief/backdrop.html +2 -0
  90. package/skills/document/packs/board-brief/pack.css +2 -1
  91. package/skills/document/packs/mckinsey-photo/backdrop.html +2 -0
  92. package/skills/document/packs/mckinsey-photo/pack.css +2 -1
  93. package/skills/document/packs/press-room/backdrop.html +2 -0
  94. package/skills/document/packs/press-room/pack.css +2 -1
  95. package/skills/document/packs/prismatic-3d/backdrop.html +2 -0
  96. package/skills/document/packs/prismatic-3d/pack.css +4 -1
  97. package/skills/document/pdf.md +60 -43
  98. package/skills/document/rewrite.md +257 -309
  99. package/skills/document/scenarios.json +1307 -0
  100. package/skills/document/scenarios.schema.json +104 -0
  101. package/skills/document/styles/README.md +15 -25
  102. package/skills/document/styles/_backdrops/rimlight.html +3 -2
  103. package/skills/document/styles/_contract-defaults.css +31 -2
  104. package/skills/document/styles/_samples/_groups/biz-premium.webp +0 -0
  105. package/skills/document/styles/_samples/mckinsey.webp +0 -0
  106. package/skills/document/styles/anthropic/skin.css +6 -3
  107. package/skills/document/styles/apple/skin.css +4 -3
  108. package/skills/document/styles/bistro-warm/backdrop.html +2 -0
  109. package/skills/document/styles/bistro-warm/skin.css +4 -1
  110. package/skills/document/styles/black-gold/skin.css +6 -5
  111. package/skills/document/styles/bold-clash/backdrop.html +2 -0
  112. package/skills/document/styles/bold-clash/skin.css +2 -1
  113. package/skills/document/styles/branding/skin.css +4 -4
  114. package/skills/document/styles/campus-teal/backdrop.html +2 -0
  115. package/skills/document/styles/campus-teal/skin.css +2 -1
  116. package/skills/document/styles/cinereel/skin.css +7 -3
  117. package/skills/document/styles/clinic-care/backdrop.html +2 -0
  118. package/skills/document/styles/clinic-care/skin.css +2 -1
  119. package/skills/document/styles/cool/skin.css +6 -5
  120. package/skills/document/styles/dark-glass/backdrop.html +2 -0
  121. package/skills/document/styles/dark-glass/skin.css +2 -1
  122. package/skills/document/styles/gov-brief/backdrop.html +2 -0
  123. package/skills/document/styles/gov-brief/skin.css +2 -1
  124. package/skills/document/styles/grid-tech/backdrop.html +2 -0
  125. package/skills/document/styles/grid-tech/skin.css +2 -1
  126. package/skills/document/styles/hand-drawn/skin.css +3 -3
  127. package/skills/document/styles/ivory-gold/skin.css +7 -6
  128. package/skills/document/styles/legal-slate/backdrop.html +2 -0
  129. package/skills/document/styles/legal-slate/skin.css +2 -1
  130. package/skills/document/styles/magazine-ink/skin.css +4 -3
  131. package/skills/document/styles/mckinsey/backdrop.html +7 -5
  132. package/skills/document/styles/mckinsey/skin.css +2 -1
  133. package/skills/document/styles/memphis/backdrop.html +2 -0
  134. package/skills/document/styles/memphis/skin.css +2 -1
  135. package/skills/document/styles/mono/skin.css +6 -2
  136. package/skills/document/styles/neon-cyber/backdrop.html +2 -0
  137. package/skills/document/styles/neon-cyber/skin.css +2 -1
  138. package/skills/document/styles/olive-gold/skin.css +7 -6
  139. package/skills/document/styles/organic-earth/backdrop.html +2 -0
  140. package/skills/document/styles/organic-earth/skin.css +2 -1
  141. package/skills/document/styles/pastel-saas/backdrop.html +2 -0
  142. package/skills/document/styles/pastel-saas/skin.css +2 -1
  143. package/skills/document/styles/portfolio-noir/backdrop.html +2 -0
  144. package/skills/document/styles/portfolio-noir/skin.css +2 -1
  145. package/skills/document/styles/retro-press/backdrop.html +2 -0
  146. package/skills/document/styles/retro-press/skin.css +2 -1
  147. package/skills/document/styles/sport-arena/backdrop.html +2 -0
  148. package/skills/document/styles/sport-arena/skin.css +4 -1
  149. package/skills/document/styles/swiss-grid/skin.css +10 -5
  150. package/skills/document/styles/wedding-blush/backdrop.html +2 -0
  151. package/skills/document/styles/wedding-blush/skin.css +4 -1
  152. package/skills/document/theme-system.md +85 -77
  153. package/skills/document/themes/README.md +7 -5
  154. package/skills/document/vision.md +8 -5
  155. package/skills/document/xlsx.md +15 -25
  156. package/skills/grill-me/SKILL.md +6 -0
  157. package/skills/grill-me/agents/openai.yaml +5 -0
  158. package/skills/grilling/SKILL.md +28 -0
  159. package/skills/grilling/agents/openai.yaml +3 -0
@@ -1,316 +1,198 @@
1
1
  # Deck design — html2pptx (hybrid) is THE deck engine
2
2
 
3
- **Every deck is built ONE way:** author slide HTML and render with `html2pptx` in
4
- its default **HYBRID** mode — `bin/html2pptx.cjs deck.html out.pptx --style <id>`
5
- (no `--editable`/`--rasterize`). Chromium gives you the full power of HTML/CSS
6
- (flexbox/grid, layering, per-element SVG) and converts to NATIVE editable pptx
7
- text boxes/shapes, with the decorative visual layer baked into one pixel-perfect
8
- backdrop — designed look **AND** editable words + editable `data-chart`. This is
9
- the path for every deck: pitch / keynote / report / design-y / plain alike.
10
-
11
- `doc-gen` makes no decks, and `doc-run` PptxGenJS is **NOT a deck-building path** —
12
- do not use it to build a deck. (It remains for NON-deck jobs only: structured
13
- data tables, batch/deterministic generation, shape-level one-offs.)
14
-
15
- html2pptx needs a browser — the app's Chromium service or the bundled Playwright.
16
- If neither is available, **say so and stop; never silently downgrade** to a lesser
17
- deck (no doc-gen fallback).
18
-
19
- **Compose from structure-only layouts + one skin, don't author from scratch.**
20
- `layouts/README.md` catalogs the structure-only slide layouts (cover / toc / section /
21
- big-stat / cards / comparison / chart-kpi / gantt / radar / stats-chart / …), each
22
- tagged role · structure · density · has-image. `styles/README.md` catalogs the skins
23
- (the paint). Those two READMEs are the source of truth for what exists — don't rely on
24
- counts quoted elsewhere. Pick layouts by **P1/P2/P3** (structure → density → **≤2 dedup**), paste
25
- each `layouts/<id>.tpl` into `deck.html`, fill real content (leave color to the skin),
26
- and render with `--style <skin>` — see the **⭐ PREFERRED workflow** below. A layout with a
27
- `{{#rows}}` block (gantt · radar · ring-progress · map-distribution · team-row ·
28
- brand-values · color-palette · steps-vertical · toc-panel) is filled through `deck-build`
29
- instead of by hand — it repeats the block per row and DERIVES the figure geometry from
30
- your numbers (see `layouts/README.md` → "How to fill"). Authoring
31
- every slide from zero is the #1 "AI-generated" tell; compose from the catalog first.
32
-
33
- ## html2pptx — HTML/CSS is the layout engine
34
-
35
- ```
36
- node <skill>/bin/html2pptx.cjs deck.html out.pptx --style <id> [--theme-v2] # DEFAULT = hybrid: paint layouts/ with the catalog entry
37
- node <skill>/bin/html2pptx.cjs deck.html --preview-dir <dir> --style <id> # authoring preview: PNG per slide, no pptx
38
- node <skill>/bin/html2pptx.cjs deck.html out.pptx --style <id> --editable # plain native (effects flatten)
39
- node <skill>/bin/html2pptx.cjs deck.html out.pptx --style <id> --rasterize # pixel-perfect, NOT editable
40
- node <skill>/bin/html2pptx.cjs snapshot.layout.json out.pptx # headless-safe input (always plain native)
41
- ```
42
-
43
- ### ⭐ PREFERRED workflow — structure/skin split (`--style`)
44
-
45
- **Build a deck this way by default.** Compose from **structure-only layouts** and
46
- paint with **one skin** at render — every layout × every skin, no duplicated
47
- templates, lint-guarded. Steps:
48
-
49
- 1. **Pick ONE skin** for the whole deck from **`styles/README.md`** (the full list
50
- with 适用场景 + light/dark) — e.g. `cool`(默认酷炫深色) · `black-gold`(黑金) ·
51
- `mckinsey`(白底咨询) · `apple`(纯黑极简) · `anthropic`(奶油赤陶·衬线) ·
52
- `hand-drawn`(手绘·马克笔) · `mono`(极简黑白); more in the catalog. Never mix skins.
53
- 2. **Plan pages, pick layouts** from `layouts/README.md` by role + **P1/P2/P3**
54
- (structure → density → **≤2 dedup**). Paste each `layouts/<id>.tpl` into `deck.html`
55
- (they already carry `data-layout`), fill real content — leave the color to the skin
56
- (never hardcode a brand hex; `lintHardcodedColors` will flag it).
57
- 3. **Render:** `html2pptx deck.html out.pptx --style <name>`. It injects
58
- `styles/<name>/skin.css`, fills each `.slide-backdrop` slot from the skin's
59
- `backdrop.html`, and substitutes the chart accent (`__ACCENT__`) — so any layout
60
- renders in that skin, text/charts stay native, decoration bakes.
61
-
62
- **Coverage** (see `layouts/README.md` for the full table + P1/P2 tags):
63
- - cover(4): cover · cover-centered · cover-statement · cover-image
64
- - toc(2): toc · toc-agenda · section · ending(2): ending · ending-contact
65
- - numbers/data: big-stat · two/three-kpi · chart-kpi · chart-full · **data-table** · **gantt** · **map-distribution** · **map-quad**
66
- - cards/list: cards-3 · cards-grid · list · bento · icon-row
67
- - compare/argue: comparison · problem-solution · versus · matrix-2x2
68
- - process/hierarchy: steps · steps-vertical · timeline · pyramid · concept-hub · **org-tree**
69
- - image: split-two-col · image-left · image-grid · image-full · quote
70
-
71
- **Extend:** add a skin = one `styles/<name>/skin.css` (define the full class contract,
72
- `deck-layouts` guard checks it); add a layout = one `layouts/<id>.tpl` (structure-only,
73
- tagged in README). Both get the other for free. `--style` and `--theme` combine (skin
74
- wins).
75
-
76
- ### When the catalog doesn't fit — hand-author THAT ONE slide (the bespoke escape hatch)
77
-
78
- The catalog is the default for the **vast majority** of slides — breadth, consistency,
79
- and no "generated-from-scratch" tell. But the catalog is a **palette, not a cage**: a
80
- genuinely **bespoke structure it can't express** — a specific system/architecture
81
- diagram, a custom flow/topology/pipeline, a signature hero moment — is a legitimate
82
- reason to **hand-author that single slide** directly in `deck.html`. That is how you get
83
- template-grade polish without abandoning the model: **catalog for scale, bespoke for the
84
- one slide that earns it.** (The zero-color rule binds only `layouts/*.tpl`, the reusable
85
- library — your `deck.html` is free HTML and MAY use color; see below.)
86
-
87
- Reach for it ONLY when **no `layouts/` structure matches the content's inherent shape**
88
- and forcing a generic layout would flatten or misrepresent it — not because "custom
89
- looks nicer." Most slides do NOT earn a bespoke; a deck that hand-authors every slide is
90
- back to the #1 AI tell.
91
-
92
- A hand-authored bespoke slide MUST still:
93
- - be one `<section class="slide" …1280×720>`; text stays native; diagram parts are
94
- inline `<svg>` (they rasterize per-element, the rest stays editable);
95
- - **paint from the skin, never random hex** — use the skin's classes (`.card`,
96
- `.accent-bar`, `.eyebrow`, `.deck-title`…) and tokens `var(--accent|--title|--body|
97
- --muted|--hair)` **plus the ramp `var(--c1..--c4)` for per-item color-coding**. The
98
- ramp is exactly how you get "left legend color-matches the right boxes." Hardcoding a
99
- brand hex reads as unfinished AND won't re-skin (`lintHardcodedColors` flags it);
100
- - obey the Design principles (one anchor, size hierarchy, ≥0.5" margins, **no crude
101
- full-height accent bar**);
102
- - look **of the same deck** — same skin palette/type as the catalog slides around it, so
103
- it never reads as pasted in.
104
-
105
- *Example:* a 4-layer architecture (left annotated L1–L4 list ↔ right vertical connected
106
- stages, each layer color-matched via `--c1..--c4`, dashed `<svg>` connectors, coder-style
107
- header). No catalog layout is that — hand-author it in the skin's tokens. **If the pattern
108
- recurs across decks, promote it to a real `layouts/<id>.tpl`** (+ any ramp selectors it
109
- needs) so it becomes reusable: bespoke first, then graduate the winners into the catalog.
110
-
111
- **Three delivery modes — the fidelity ⇄ editability dial:**
112
- - **hybrid** (DEFAULT): each slide's decorative VISUAL layer (gradients, glass,
113
- glow, mesh, cards, shadows, art) is rasterized into ONE full-bleed backdrop
114
- image; **TEXT and native CHARTS (`data-chart`) stay native/editable on top.**
115
- **Pixel-perfect look AND editable words + editable chart data** — the sweet spot
116
- for a cool deck you still want to tweak. (Gradient-clipped display text is baked
117
- into the backdrop; solid text stays native. Note: gradients/shadows LOOK right
118
- but live in the image — use `--editable` if you need them as editable shapes,
119
- at the cost of effects the mapper can't express flattening.) Auto-falls back to
120
- plain native if the backdrop capture is unavailable.
121
- - **`--editable`** (alias `--mode native`): text/boxes/`<img>`/`<hr>` → native
122
- pptx; CSS gradients flatten to a solid, blur/glow are dropped. Fully editable.
123
- Best when the design lives in layout + type, not effects, or when file size
124
- matters (no backdrop images).
125
- - **`--rasterize`**: the whole slide (text included) becomes an image —
126
- pixel-perfect but ZERO editability. Only when even the text must be exact.
127
-
128
- Contract (violations fail loudly, nothing degrades silently):
129
-
130
- - One `<section class="slide">` per slide, laid out on a **1280×720 px** canvas
131
- (= 13.33×7.5 in). Color comes from the skin (`--style`), whose tokens every skin
132
- defines: `var(--accent|--title|--body|--muted|--hair|--slide-bg|--title-color|
133
- --body-color|--font-heading|--font-body)` plus the ramp `var(--c1..--c4)` — never
134
- hardcode brand colors, and never use a name outside this set (`--primary`,
135
- `--text`, `--card-bg`, `--border` are NOT defined by skins: they only exist under
136
- the legacy `--theme` path, so on a `--style` deck they silently stick on their
137
- fallback). html2pptx **warns** (`N hardcoded brand color(s) bypass theme vars`)
138
- when a slide writes a hex instead of a `var(--…)` — that deck won't re-skin, so
139
- fix it before shipping. (SVG diagram semantic colors are exempt.)
140
- - **Tailwind utility classes are available** (vendored offline — no CDN needed):
141
- author like a modern web deck — `grid grid-cols-2 gap-16`, `flex items-center
142
- justify-center`, `p-16`, `text-6xl font-bold`, `rounded-3xl`, `bg-blue-600`,
143
- `space-y-6` all compute and map to native pptx. Prefer theme `var(--...)` for
144
- brand colors; use Tailwind for layout/spacing/scale. A structural type scale is
145
- also available: `<h1 size="1".."7">` → 12/14/16/24/32/48/72px (stay on-scale
146
- instead of ad-hoc font sizes).
147
- - **Images must be `data:` URIs** — the render sandbox has no network.
148
- - **Icons = INLINE `<svg>` ONLY.** Never `<img src="…icon…">` for an icon — an
149
- external/CDN icon URL fails in the no-network sandbox and Chromium bakes a
150
- **broken-image glyph** (缺图红叉) into the hybrid backdrop. Paste the SVG markup
151
- inline (lucide/heroicons path data), or a `data:image/svg+xml` URI at worst.
152
- - **No crude full-height accent bar.** A raw bright `var(--accent)` rectangle down
153
- the left edge reads as an unfinished template — if a family wants a spine it is
154
- baked into its `.tpl`; do not bolt a colored bar onto a bespoke slide.
155
- - Mapped to native pptx: text (per style run, editable/searchable), boxes
156
- (background/border/radius), `<img>`, `<hr>`. **Inline `<svg>`/`<canvas>` are
157
- rasterized per-element** — only that box becomes a PNG, everything else on the
158
- slide stays native editable text. So a slide with an SVG icon/diagram + text
159
- keeps its text editable; you do NOT need `--rasterize` for SVG anymore. Use
160
- SVG freely for icons, logos, small diagrams, decorative shapes.
161
- - **Full-bleed photo covers stay editable**: an `<img>` with `object-fit:cover`
162
- /`contain` maps to a NATIVE pptx crop/letterbox (aspect preserved — no need to
163
- pre-crop to 16:9), and a semi-transparent `rgba(...)` fill maps to native fill
164
- transparency, so a dark scrim over a photo no longer flattens to an opaque
165
- block. Overlay a title + scrim on a photo and the text stays native/editable.
166
- - Still not mapped as native: CSS gradients (flatten to a solid backgroundColor
167
- in the converter) and `<video>`. When a WHOLE slide relies on effects the
168
- mapper can't express natively, `--rasterize` turns every slide into a
169
- pixel-perfect **image** (zero text editability — say so when delivering).
170
- - HTML input renders in the app's Chromium when available, else a **bundled
171
- headless Chromium** (Playwright) — so it works off-app too (CI/cloud/doc-run),
172
- not only in-app. `.layout.json` snapshot input converts anywhere with no
173
- browser. (CJK renders natively where the host has CJK fonts; a font-less Linux
174
- CI may need a CJK font installed.)
175
- - **Speaker notes**: add a hidden `<div class="talos-notes" style="display:none">
176
- …notes…</div>` (or `data-notes="…"` on the `<section>`) per slide → it becomes
177
- the slide's pptx notesSlide (never rendered on the slide). Pass `--notes-doc
178
- <out.md>` to also emit a standalone speaker-notes document.
179
- - Fonts are measured by Chromium but re-laid-out by PowerPoint — keep ~10%
180
- slack in text boxes; don't butt text against box edges.
181
-
182
- After generating: the build reports `valid` and `qa` itself; sign off with
183
- `deck-qa --final` like any deck ("Verify before delivering").
184
-
185
- ## HTML slide authoring — the design-first route
186
-
187
- This is the **toolbox for the bespoke escape-hatch slides** identified above (the
188
- one architecture diagram / hero the catalog can't express) — NOT a license to
189
- hand-author every slide (that's the #1 AI tell; compose the rest from the catalog).
190
- When a slide DOES earn it, you get the full CSS toolbox (flexbox/grid, layering,
191
- `position:absolute`, per-element SVG) and the output is still **native, editable**
192
- pptx — text boxes stay text, only SVG boxes rasterize. Author against the skin/theme
193
- CSS variables (incl. the ramp `var(--c1..--c4)`) so the same HTML re-skins for free.
194
-
195
- Every slide is `<section class="slide" style="position:relative;width:1280px;height:720px">`.
196
- Position with `absolute` inside it. Compose from these recipes (vary them across
197
- the deck — don't repeat one layout):
198
-
199
- - **Full-bleed cover** — `background:var(--primary)`; oversized title (64–96px)
200
- bottom-left; one thin accent bar; presenter/date small at the very bottom.
201
- - **Two-column split** — 50/50 flex row: text column left (title + 3–4 short
202
- lines), a visual right (an `<img>` data-URI, an SVG diagram, or a big stat).
203
- - **Half-bleed image** — image fills the left or right ~55% (`object-fit:cover`),
204
- content overlaid on the other half with a solid or scrim panel behind the text.
205
- - **Big-stat hero** — one number at 140–200px in `var(--accent)`, a short label
206
- under it, one supporting sentence. Nothing competes.
207
- - **Icon rows / 2×2 grid** — each cell: an SVG icon in a colored circle, a bold
208
- header, one line of body. Equal cells, consistent gaps.
209
- - **Quote / statement** — one sentence at 40–56px, generous padding, attribution
210
- small below. Let the type be the visual.
211
- - **Section divider** — `background:var(--primary)`; an oversized ghosted chapter
212
- number; the section title beside it.
213
-
214
- Honor the **Design principles** below (dominant color, one visual anchor per
215
- slide, size hierarchy, ≥0.5" margins, NO accent line under a title). Icons: inline
216
- `<svg>` (stroke `currentColor`/theme vars) — they rasterize per-element, so the
217
- rest of the slide stays editable.
218
-
219
- **Charts — pick by where the deck will be OPENED (this is a real tradeoff, not a
220
- default):**
221
- - **Inline `<svg>` bar/line you hand-draw** = DEFAULT for a delivered deck. It
222
- rasterizes into the backdrop, so it renders **everywhere** — Keynote, macOS
223
- Preview / Quick Look, Google Slides, WPS, PowerPoint. The cost: the chart is a
224
- picture, not editable data.
225
- - **Native `data-chart`** `<div class="talos-chart" style="position:absolute;
3
+ Every deck is slide HTML rendered by `html2pptx` in its default **hybrid** mode: Chromium
4
+ lays out the HTML (flexbox/grid, layering, per-element SVG), the decorative layer bakes
5
+ into one pixel-perfect backdrop per slide, and text plus native `data-chart`s stay
6
+ editable pptx objects on top. Pitch, keynote, report or plain — same path.
7
+
8
+ - `doc-gen` makes no decks, and `doc-run` PptxGenJS is **not** a deck path (it remains for
9
+ non-deck jobs: data tables, batch generation, shape-level one-offs).
10
+ - html2pptx needs a browser (the app's Chromium service or the bundled Playwright). With
11
+ neither, **say so and stop** — never downgrade to a lesser deck.
12
+
13
+ Where things live: layout choice and fill contract → `layouts/README.md`; Theme V2 vs
14
+ classic-v1 → `theme-system.md`; flags, outputs and exit codes → `bin-reference.md`;
15
+ content and outline → `content-pipeline.md`.
16
+
17
+ ## Workflow
18
+
19
+ 1. **Plan, then fill.** The approved `plan.json` IS the outline (content-pipeline.md
20
+ Phase 3 — written once, `--outline` renders what the user approved) and runs
21
+ ```
22
+ node <skill>/bin/deck-plan.cjs plan.json spec.json # → per slide: pick · alternatives · rejected[{layout, why}]
23
+ node <skill>/bin/deck-build.cjs spec.json deck.html
24
+ ```
25
+ `deck-plan` maps each slide's content onto every candidate layout's slots, checks
26
+ every string against the layout's capacity **measured under your style**
27
+ (`layouts/capacity.json`), applies P1/P2/P3 and drafts the spec (every slot filled,
28
+ charts as `chartRender:"svg"`). A slide with `pick: null` fits nothing as written —
29
+ change the content its `rejected[].why` names ("holds exactly 3 items, content has
30
+ 4", "card2_body is ~58 wide but holds ≤42 — cut ~16"), or pin `layout` + `slots`, and
31
+ re-plan; do NOT render to find out. Edit `spec.json` freely afterwards — `deck-build`
32
+ re-checks capacity on every build, and its warnings (unfilled slot → demo copy) are
33
+ defects. Put the confirmed `scenario` in the plan (it decides motion, below). A
34
+ `\n` in a value is a line break (a typed `<br>` ships as text); a slide's `source`
35
+ ("IDC,2026") is its one short source line, in the footer.
36
+ 2. **Bespoke slides** (no layout fits, or approved generated-visual pages): author one
37
+ `<section class="slide">` in its own file and give the plan slide `file` — deck-build
38
+ puts it at its position (its `notes` too); every other slide stays catalog. **Never patch the built `deck.html`
39
+ with a script or regex**: capacity checks cannot see those edits. Change content
40
+ or slots and re-plan, or replace
41
+ that ONE slide with a bespoke `file`. Speaker notes are each slide's `notes`.
42
+ 3. **Preview, build, sign off** — "Verify before delivering" below.
43
+
44
+ Hand-pasting a `layouts/<id>.tpl` skips the capacity check; a layout with a `{{#rows}}`
45
+ block (gantt · radar · ring-progress · map-distribution · team-row · brand-values ·
46
+ color-palette · steps-vertical · toc-panel) can only be filled through `deck-build`,
47
+ which repeats the block and derives the figure geometry from your numbers.
48
+
49
+ ## Delivery modes — the fidelity ⇄ editability dial
50
+
51
+ - **hybrid** (default — no flag): visuals (gradients, glass, glow, mesh, cards, shadows,
52
+ art, gradient-clipped display text) bake into the backdrop; solid text and
53
+ `data-chart`s stay native. Gradients/shadows *look* right but live in the image. If the
54
+ backdrop capture is unavailable it falls back to plain native and reports a
55
+ `layout (fallback)` engine.
56
+ - **`--editable`** (alias `--mode native`): everything native; CSS gradients flatten to
57
+ a solid, blur/glow drop. Fully editable, smallest file. For designs that live in
58
+ layout + type. One exception: a Theme V2 art layer / generated-skin signature in
59
+ `.slide-backdrop` is one full-slide picture under the native content (its opacity,
60
+ blur and masks survive); the content on top stays native.
61
+ - **`--rasterize`**: every slide one image — pixel-perfect, ZERO editability; only when
62
+ even the text must be exact, and say so when delivering.
63
+
64
+ ## The slide contract
65
+
66
+ Violations fail loudly; nothing degrades silently.
67
+
68
+ - One `<section class="slide">` per slide on a **1280×720 px** canvas (13.33×7.5 in).
69
+ - **Colour only from the skin's tokens**: `var(--accent|--title|--body|--muted|--hair|
70
+ --slide-bg|--title-color|--body-color|--font-heading|--font-body)` and the ramp
71
+ `var(--c1..--c4)`. Any other name (`--primary`, `--text`, `--card-bg`, `--border`,
72
+ `--accent2`) exists only under the legacy `--theme` path, so on a `--style` deck it
73
+ silently sticks on its fallback. A raw hex triggers html2pptx's `N hardcoded brand
74
+ color(s) bypass theme vars` warning — that deck won't re-skin; fix it (SVG diagram
75
+ semantic colours are exempt). To recolour one element, override a token on its
76
+ `<section>`: `style="--accent:#b8292e"`.
77
+ - **Tailwind utilities** are vendored offline (`grid grid-cols-2 gap-16`, `p-16`,
78
+ `rounded-3xl`…) and map to native pptx — use them for layout/spacing, tokens for
79
+ colour. Type scale: `size="1".."7"` → 12/14/16/24/32/48/72px.
80
+ - **Images**: `src` accepts an absolute path, a path relative to deck.html, or HTTPS;
81
+ html2pptx inlines assets before rendering. Mark required content with
82
+ `<img data-required-image src="…" alt="…">`: unreadable or undecodable images fail
83
+ export and previews. Unmarked images retain best-effort loading. For useful `gen`
84
+ pages, follow generated-visuals.md.
85
+ - **Icons = inline `<svg>` only** (lucide/heroicons path data, or a
86
+ `data:image/svg+xml` URI at worst). An icon `<img>` that fails to load bakes a
87
+ broken-image glyph into the backdrop.
88
+ - **No crude full-height accent bar** down an edge, and **no thin accent line under a
89
+ title** — the two loudest "template" tells. A family that wants a spine bakes it into
90
+ its `.tpl`.
91
+ - Native mapping: text (per style run), boxes (background/border/radius), `<img>`
92
+ (`object-fit:cover|contain` → native crop/letterbox, no pre-cropping), `<hr>`,
93
+ `rgba()` fills (native transparency — a scrim over a photo stays a scrim).
94
+ Inline `<svg>`/`<canvas>` rasterize **per element**; the rest of the slide stays
95
+ editable, so SVG icons/diagrams never need `--rasterize`. Not mapped: CSS gradients
96
+ in `--editable`, `<video>`.
97
+ - **Speaker notes**: `<div class="talos-notes" style="display:none">…</div>` (or
98
+ `data-notes="…"` on the `<section>`) → the slide's notes page; `--notes-doc
99
+ <out.md>` also writes a standalone notes document.
100
+ - PowerPoint re-lays out what Chromium measured: keep ~10% slack in multi-line text
101
+ boxes (single-line boxes are widened automatically — see CJK notes).
102
+ - Runs in the app's Chromium or a bundled headless Chromium, so it works off-app; a
103
+ `.layout.json` snapshot converts with no browser. A font-less Linux host may need a
104
+ CJK font installed.
105
+
106
+ ## Bespoke slides — the escape hatch
107
+
108
+ The catalog is a palette, not a cage: a structure no layout expresses (a specific
109
+ architecture diagram, a custom flow/topology, a signature hero moment) earns a
110
+ hand-authored slide. Approved generated-visual pages also use this route; other pages
111
+ stay catalog. A bespoke slide must:
112
+
113
+ - be one `<section class="slide" style="position:relative;width:1280px;height:720px">`,
114
+ positioned with `absolute`, text native, diagram parts inline `<svg>`;
115
+ - paint from the skin: its classes (`.card`, `.eyebrow`, `.deck-title`, `.accent-bar`…)
116
+ and tokens, with the ramp `--c1..--c4` for per-item colour-coding (that is how a
117
+ legend colour-matches its boxes). The zero-colour rule binds only the reusable
118
+ `layouts/*.tpl`; deck.html may use colour, but through tokens;
119
+ - write sizes and gutters on the skin's scale — `calc(48px * var(--type-scale,1))`,
120
+ `calc(96px * var(--space-scale,1))` — or it ignores the skin's density;
121
+ - pass the Design principles below and look of the same deck.
122
+
123
+ Composition recipes (vary them): **full-bleed statement** (the skin's background or
124
+ `.slide-inverse`, oversized title bottom-left, presenter/date small at the bottom) ·
125
+ **two-column split** (text left, visual right) · **half-bleed image** (~55% image with
126
+ `object-fit:cover`, text on a scrim/panel on the other half) · **big stat** (one number
127
+ at 140–200px in `var(--accent)`, a label, one sentence) · **icon rows / 2×2** (SVG icon,
128
+ bold header, one line each; equal cells) · **quote** (one sentence at 40–56px) ·
129
+ **section divider** (oversized ghost chapter number + title). Example: a 4-layer
130
+ architecture — annotated L1–L4 list left, connected stages right colour-matched via
131
+ `--c1..--c4`, dashed `<svg>` connectors. A pattern that recurs across decks graduates
132
+ into a real `layouts/<id>.tpl`.
133
+
134
+ ## Charts — pick by where the deck will be OPENED
135
+
136
+ - **Inline `<svg>` drawn FROM DATA** — the default. It bakes into the backdrop, so it
137
+ renders everywhere (Keynote, macOS Preview / Quick Look, Google Slides, WPS,
138
+ PowerPoint); its labels stay native text, but the data is not editable.
139
+ **Never hand-write the chart's SVG** (scales, stacking and axes are where hand-drawn charts go wrong): in a catalog
140
+ chart layout give the spec `"chartRender":"svg"` (or one chart `"render":"svg"`) and
141
+ `deck-build` draws it for that layout's measured chart box; on a bespoke slide run
142
+ `bin/deck-chart.cjs chart.json --width W --height H` and paste the printed `<svg>`
143
+ into a W×H box. Types `bar` · `barH` · `line` · `area` · `pie` · `doughnut` (+
144
+ `stacked`, `unit`, `highlight` (category index → accent), `showValues`,
145
+ `centerLabel`, `zeroBaseline` (line axis from 0)); ≤4 series (a 5th+ slice folds into 其他); painted only with skin
146
+ variables, so it re-skins. `radar`: 3–8 `labels`, 1–3 series of 0..`max` scores (the
147
+ `radar` layout draws it from its rows); `scatter`: `series[].points` `[x,y]`/`{x,y,label}`
148
+ + `xLabel`/`yLabel`.
149
+ - **Native `data-chart`** — `<div class="talos-chart" style="position:absolute;
226
150
  left/top/width/height" data-chart='{"type":"bar|line|pie|doughnut|area","labels":
227
- […],"series":[{"name":"…","values":[…]}],"colors":["#2563eb",…]}'></div>` becomes
228
- a real editable `c:chart` (double-click to edit the data sheet) — **but it only
229
- renders in PowerPoint / WPS / LibreOffice; it shows BLANK in Keynote, macOS
230
- Preview, Quick Look and Google Slides.** Use it only when you KNOW the deck stays
231
- in PowerPoint and the client needs to edit the numbers.
232
- - Never Chart.js/`<canvas>` (rasterizes with no control, like Manus).
233
-
234
- When unsure, ship the SVG — a chart nobody can see in Preview is worse than one
235
- nobody can edit.
236
-
237
- ## Premium craft — the 8-layer finish (raise "fine" to "agency-grade")
238
-
239
- The Design principles below are the *floor* (don't look AI-generated). This is the
240
- *ceiling*: the layered finish that makes a slide read like a paid studio made it.
241
- Build **every hero slide** (cover / section / big-stat / ending) by stacking these
242
- bottom-to-top; content slides use layers 1–2 + 6–8. Spend the boldness in ONE
243
- place (layer 5) and keep everything else quiet.
244
-
245
- 1. **Tonal base, not flat fill.** Background is a low-angle `linear-gradient`
246
- (`160deg`) between two near-neighbors of the base color, not a solid — it gives
247
- the slide an implicit light direction. (Dark example: `#0a0a0a→#141008`.)
248
- 2. **Grain.** One full-bleed `feTurbulence` fractal-noise `<svg>` at
249
- `opacity:.03; mix-blend-mode:multiply`. This is the single biggest "cheap→
250
- premium" lever — film/paper grain kills the plastic look of a clean digital
251
- gradient. Give each slide's filter a **unique `id`** (`gcv`/`gsc`/…) so multiple
252
- slides in one file don't collide.
253
- 3. **Light (hero pages only).** ONE soft light per hero page, chosen from a fixed
254
- set — `spotlight` (top radial), `bloom` (offset soft disc), `duotone` (two
255
- corner washes), `aurora` (bottom multi-stop glow), `frosted` (corner falloff),
256
- `rimlight` (edge gradient + thin bright rim). All are **pure background-gradient
257
- divs — no text, no `background-clip`** (so they're ghost-safe, see layer 8).
258
- Dark themes take full strength; light themes use low-alpha versions or they blow
259
- out the white. Vary WHICH light per page so a deck isn't monotone.
260
- 4. **Depth: vignette + edge highlight.** A corner/edge `radial-gradient` that darkens
261
- toward the frame pulls the eye inward; a top `inset` light hairline on cards
262
- fakes a lit glass/metal edge. Subtle — you should feel thickness, not see the trick.
263
- 5. **One signature motif.** Each style gets exactly ONE recurring decorative mark —
264
- concentric rings + star-dots (black-gold), a sweeping aurora arc (cool), an
265
- oversized ghost chapter number (editorial). This is where the deck earns its
266
- identity; everything else stays restrained. Never put content inside the motif.
267
- 6. **Chrome (the print-binding).** Treat the slide as a page in a bound report:
268
- **running header** (report title left · chapter right) + a 1px **hairline**
269
- under it + a **footer** (report / chapter) + a **page number**. This four-piece
270
- frame is what makes ten slides feel like ONE document — the strongest
271
- "consulting-grade" signal.
272
- 7. **Refined type: three-tier contrast.** An **eyebrow** — tracked (`letter-
273
- spacing:.28em`) small-caps label with a short lead rule (`— MARKET RESEARCH
274
- REPORT`); a **display** title (cover 82–96px); a **micro-label** at 12–14px. Big
275
- size jumps + deliberate tracking is the line between "designed" and "typed".
276
- 8. **Ghost-safe export (hybrid rules).** Two hard rules for html2pptx hybrid:
277
- - **Solid text only.** NEVER `-webkit-background-clip:text` gradient text — the
278
- backdrop-hide pass zeroes `color`/`-webkit-text-fill-color` but NOT
279
- `background`, so a gradient-clipped glyph renders TWICE (a ghost behind the
280
- native text). Use a solid `color:`.
281
- - **Charts follow the cross-viewer rule above** (SVG by default).
282
-
283
- The shipped `layouts/*.tpl` are this recipe applied as structure-only slides —
284
- paste the nearest one and paint it with a skin (`--style`) rather than rebuilding
285
- the stack by hand; each already carries the 8 layers correctly wired.
286
-
287
- **Every catalog entry ships a motion preset; the scenario decides whether
288
- `deck-build` applies it.** (`motion/<id>.json`, named by the entry's `motion` field.)
289
- Put the confirmed scenario in the spec as `"scenario"`: a **presented** deck (pitch /
290
- 路演 / 发布会 / keynote / 大会 / 讲座 / 销售) gets the preset — `data-anim*` /
291
- `data-transition*` injected per slide by the layout's role; a **read** deck (内审 /
292
- 评审 / 报告 / 汇报 / 讲义 / 培训 / 述职) gets none; no or an unknown scenario keeps the
293
- preset. `"motion": true|false` overrides the scenario. The build's JSON says which
294
- applied and why (`motion: {on, reason}`). Hand-written `data-anim` /
295
- `data-transition` always wins — `deck-build` only adds an attribute an element does
296
- not already have, and a deck authored straight into `deck.html` animates only
297
- where you write `data-anim`.
298
-
299
- ## Motion — entrance animation + slide transitions
300
-
301
- ### Entrance animation (`data-anim`) — opt-in, hero pages only
302
-
303
- html2pptx emits **native PowerPoint entrance animations** (a real `<p:timing>`
304
- build sequence — not a video, not a GIF). Mark an element with `data-anim` and it
305
- becomes one build step; the deck then plays like a keynote instead of appearing
306
- all at once.
307
-
308
- **The attribute itself is opt-in.** No `data-anim` on the page → no `<p:timing>`.
309
- Write your own when the scenario is a **stage** — 发布会 / 路演 / pitch / 主题演讲;
310
- for 内审 / 报告 / 讲义 decks that people read rather than watch, add none beyond
311
- what the preset already injects.
312
-
313
- #### The attribute contract
151
+ […],"series":[{"name":"…","values":[…]}]}'></div>` becomes an editable `c:chart`,
152
+ **but it only renders in PowerPoint / WPS / LibreOffice and shows BLANK in Keynote,
153
+ macOS Preview, Quick Look and Google Slides.** Only when you KNOW the deck stays in
154
+ PowerPoint and the client must edit the numbers.
155
+ - Never Chart.js/`<canvas>`. One insight per chart, highlight the key datapoint, no junk.
156
+ When unsure, ship the SVG — a chart nobody can see is worse than one nobody can edit.
157
+
158
+ ## Premium craft — the 8-layer finish
159
+
160
+ The shipped layouts already wire these layers; this is what to reproduce on a bespoke
161
+ hero slide (cover / section / big-stat / ending: all eight; content slides: 1–2 and 6–8).
162
+ Spend the boldness in ONE place (layer 5).
163
+
164
+ 1. **Tonal base** — a low-angle (`160deg`) gradient between two near-neighbours of the
165
+ base colour, not a flat fill.
166
+ 2. **Grain** — one full-bleed `feTurbulence` noise `<svg>` at `opacity:.03;
167
+ mix-blend-mode:multiply`; give each slide's filter a unique `id`.
168
+ 3. **Light (hero only)** — ONE of `spotlight` · `bloom` · `duotone` · `aurora` ·
169
+ `frosted` · `rimlight`, as plain background-gradient divs (no text, no
170
+ `background-clip`); full strength on dark skins, low alpha on light ones; vary it
171
+ per page.
172
+ 4. **Depth** — an edge vignette and a top inset hairline on cards; felt, not seen.
173
+ 5. **One signature motif** per style (rings + star-dots, an aurora arc, a ghost chapter
174
+ number); never put content in it.
175
+ 6. **Chrome** — running header (title · chapter) + 1px hairline + footer + page number:
176
+ what makes ten slides read as one document.
177
+ 7. **Three-tier type** — tracked (`letter-spacing:.28em`) eyebrow with a lead rule, a
178
+ display title, 12–14px micro-labels.
179
+ 8. **Ghost-safe export** — solid text only: NEVER `-webkit-background-clip:text`
180
+ (the backdrop pass hides `color` but not `background`, so the glyph renders twice).
181
+ Charts follow the rule above.
182
+
183
+ ## Motion — presets, entrance animation, transitions
184
+
185
+ **Every catalog entry ships a motion preset (`motion/<id>.json`); the scenario decides
186
+ whether `deck-build` applies it.** Put the confirmed scenario in the spec as
187
+ `"scenario"`: a **presented** deck (pitch / 路演 / 发布会 / keynote / 大会 / 讲座 / 销售)
188
+ gets the preset — `data-anim*` / `data-transition*` injected per slide by role; a
189
+ **read** deck (内审 / 评审 / 报告 / 汇报 / 讲义 / 培训 / 述职) gets none; no or an unknown
190
+ scenario keeps the preset. `"motion": true|false` overrides; the build reports
191
+ `motion: {on, reason}`. Hand-written attributes always win (deck-build only adds what an
192
+ element lacks), and a deck written straight into `deck.html` animates only where you
193
+ write `data-anim`. Add your own only on a stage deck, never on a read deck.
194
+
195
+ html2pptx emits native PowerPoint builds (`<p:timing>`). Attribute contract:
314
196
 
315
197
  | attribute | value | default |
316
198
  |---|---|---|
@@ -320,85 +202,36 @@ what the preset already injects.
320
202
  | `data-anim-delay` | ms before this step | `0` first step, `200` after |
321
203
  | `data-anim-dur` | ms the effect runs | `500` |
322
204
 
323
- `fade-up` and `float` are the same gentle 5%-rise fade — the house default.
324
- `fly-*` travels 30% of the slide and is loud; spend it once, on a cover. `appear`
325
- is a hard cut with no fade (use for a punchline that should snap in).
326
-
327
- Put the attribute on a **container** and the text inside inherits it — the walker
328
- resolves each text run to its nearest `[data-anim]` ancestor, so one attribute on
329
- a `.card` animates the whole card as a unit. That is almost always what you want:
330
- animate BLOCKS, not individual words.
331
-
332
- #### Playback model
333
-
334
- The first step fires when the presenter advances to the slide's build; every later
335
- step chains **after-previous**, so its `data-anim-delay` reads as the stagger
336
- between items. One click plays the whole page — you never click per element.
337
-
338
- #### Hard constraints (silent failure if ignored)
339
-
340
- - **Only native shapes animate.** Anything baked into the raster backdrop is one
341
- flat image and cannot be a step. That includes every `--talos-bake:1` title —
342
- **`cinereel` and `ivory-gold` bake ALL their title classes**, so on those two
343
- skins a `data-anim` on `.cover-title`/`.section-title` is silently dropped.
344
- Animate the eyebrow, body, cards and image instead, or pick another skin.
345
- **Same rule for a motion preset's `entrance.<role>[].sel`** — `deck-build` writes
346
- `data-anim*` onto whatever the selector matches by class, so a preset step aimed
347
- at a title class on a bake-everything skin drops just as silently; a family's
348
- `motion/<id>.json` has to know its own skin's `--talos-bake` list.
349
- - **`background-clip:text` is baked too** (layer 8 above) — same silent drop.
350
- - **PDF flattens everything.** `doc-convert` / LibreOffice / Gotenberg ignore
351
- `<p:timing>`; the animation exists only in the .pptx opened in PowerPoint,
352
- Keynote or Impress. Never promise animation on a PDF deliverable.
353
- - **Charts and images animate**, as whole objects — a `data-chart` cannot animate
354
- series-by-series.
355
-
356
- #### Recipes (paste onto the layout you already picked)
357
-
358
- **Cover** — one statement, one beat:
205
+ `fade-up` = `float` (gentle 5% rise — the house default); `fly-*` travels 30% of the
206
+ slide and is loud (once, on a cover); `appear` is a hard cut for a punchline. Put the
207
+ attribute on a **container** — text inside inherits its nearest `[data-anim]`
208
+ ancestor, so one attribute on a `.card` animates the card as a unit. The first step
209
+ fires when the presenter advances; later steps chain after-previous, so
210
+ `data-anim-delay` is the stagger — one click plays the page.
359
211
 
360
212
  ```html
361
213
  <h1 class="cover-title" data-anim="fade-up" data-anim-seq="1" data-anim-dur="700">…</h1>
362
214
  <div class="cover-sub" data-anim="fade-up" data-anim-seq="2" data-anim-delay="120">…</div>
363
- ```
364
-
365
- **Section divider** — the number lands, then the title:
366
-
367
- ```html
368
- <div class="section-ghost" data-anim="fade" data-anim-seq="1">03</div>
369
- <h2 class="section-title" data-anim="fly-up" data-anim-seq="2" data-anim-delay="150">…</h2>
370
- ```
371
-
372
- **Card grid / TOC** — stagger the blocks, never the words. One attribute per card,
373
- `seq` in reading order, a short even delay:
374
-
375
- ```html
376
- <div class="card" data-anim="fade-up" data-anim-seq="1">…</div>
377
- <div class="card" data-anim="fade-up" data-anim-seq="2" data-anim-delay="140">…</div>
378
215
  <div class="card" data-anim="fade-up" data-anim-seq="3" data-anim-delay="140">…</div>
379
- ```
380
-
381
- **Big stat** — the label sets up, the number pays off:
382
-
383
- ```html
384
- <div class="stat-lead" data-anim="fade" data-anim-seq="1">…</div>
385
216
  <div class="stat-num" data-anim="appear" data-anim-seq="2" data-anim-delay="260">…</div>
386
217
  ```
387
218
 
388
- #### Discipline
219
+ Silent-failure constraints:
389
220
 
390
- - **≤4 steps per slide.** A build with eight steps is a slide with too much on it.
391
- - **Hero pages only** — cover, section, big-stat, ending, and at most one content
392
- page per chapter. A deck where *every* page builds is exhausting to present.
393
- - **Never animate the chrome** (header/footer/page number/hairline) — the frame is
394
- supposed to be constant; animating it makes the deck feel unstable.
395
- - **Keep durations short** (500–700ms). Anything slower reads as lag on a projector.
221
+ - **Only native shapes animate.** Baked text is part of the backdrop image: every
222
+ class a skin marks `--talos-bake:1` drops its `data-anim` silently. Many skins bake
223
+ their titles (`cinereel` and `ivory-gold` bake all title classes; check the skin's
224
+ `skin.css` for `--talos-bake:1`) — animate the eyebrow, body, cards and images
225
+ instead. A preset's `entrance.<role>[].sel` obeys the same rule, so a family's
226
+ `motion/<id>.json` must know its skin's bake list. `background-clip:text` is baked too.
227
+ - **PDF flattens everything** (doc-convert / LibreOffice / Gotenberg ignore timing and
228
+ transitions): never promise animation on a PDF.
229
+ - Charts and images animate as whole objects — never series-by-series.
396
230
 
397
- ### Slide transitions (`data-transition`)
231
+ Discipline: ≤4 steps per slide; hero pages only (cover, section, big-stat, ending, at
232
+ most one content page per chapter); never animate the chrome; 500–700ms.
398
233
 
399
- Where `data-anim` builds content WITHIN a slide, `data-transition` sets how the deck
400
- moves BETWEEN slides. Put it on the `<section class="slide">` — it describes the move
401
- **into** that slide.
234
+ **Transitions** go on the `<section class="slide">` and describe the move INTO it:
402
235
 
403
236
  | attribute | value | default |
404
237
  |---|---|---|
@@ -406,272 +239,179 @@ moves BETWEEN slides. Put it on the `<section class="slide">` — it describes t
406
239
  | `data-transition-dir` | `l` · `r` · `u` · `d` (wipe/push/cover) · `in` · `out` (zoom/split) | per-effect default |
407
240
  | `data-transition-dur` | ms | the `med` speed bucket |
408
241
 
409
- ```html
410
- <section class="slide" data-transition="fade" data-transition-dur="600">…</section>
411
- <section class="slide" data-transition="push" data-transition-dir="u">…</section>
412
- ```
413
-
414
- **`morph` is the one that makes a deck look produced** — PowerPoint tweens shapes that
415
- persist across two slides instead of cutting. Two caveats before you reach for it:
416
- it needs **PowerPoint 2016+** (older consumers get the fade fallback we emit), and in
417
- hybrid mode most of each slide is a single baked backdrop image, so morph has little
418
- to tween — it reads as a soft cross-dissolve unless the two slides share **native**
419
- shapes (text/charts) in similar positions. Use it between slides that genuinely
420
- continue one another (a title that moves, a stat that grows); elsewhere `fade` is more
421
- honest and costs nothing.
422
-
423
- **Pick one transition for the deck and stay with it** — a different effect per slide is
424
- the single loudest "amateur deck" signal. `fade` for almost everything; `push` or
425
- `cover` when chapters should feel like turning a page; `cut` (or nothing) for dense
426
- analytical decks where motion is noise; `morph` for the two or three slides that
427
- actually continue each other.
428
-
429
- Same two constraints as animation: **PDF export flattens transitions** (they exist only
430
- in the .pptx opened in PowerPoint/Keynote/Impress), and an unknown name degrades to
431
- `fade` rather than failing.
432
-
433
- ## Type & space scale — how a skin sets its own rhythm
434
-
435
- Every `font-size` and page gutter in `layouts/*.tpl` is written as a scalable value:
436
-
437
- ```html
438
- font-size:calc(23px * var(--type-scale,1)) left:calc(96px * var(--space-scale,1))
439
- ```
440
-
441
- A skin sets `--space-scale` / `--type-scale` in its `:root` and the whole deck retimes —
442
- **without touching a single layout**, and keeping each layout's internal hierarchy intact
443
- (everything scales together). Both default to `1`, so a skin that ignores them renders
444
- exactly as before. The shipped range runs from `mckinsey` at `0.86` (board-pack density)
445
- to `apple` at `1.22` (keynote-stage air).
242
+ Pick ONE transition for the deck — a different effect per slide is the loudest amateur
243
+ signal: `fade` for almost everything, `push`/`cover` for page-turn chapters, `cut` (or
244
+ none) for dense analytical decks. `morph` needs PowerPoint 2016+ (older apps get the
245
+ emitted fade fallback) and only tweens **native** shapes in similar positions on both
246
+ slides — in hybrid most of a slide is one baked image, so use it only between slides
247
+ that genuinely continue each other. An unknown name degrades to `fade`.
446
248
 
447
- Two rules:
249
+ ## Type & space scale
448
250
 
449
- - **Hand-authoring a bespoke slide?** Write your sizes the same way
450
- (`calc(48px * var(--type-scale,1))`) or that one slide will ignore the skin's rhythm
451
- and sit at a different density than the rest of the deck.
452
- - **Changed a scale? Re-run `deck-qa`.** Scaling reflows text; push it too far and content
453
- runs off-canvas or collides. That is a mechanical check — never eyeball it.
251
+ Every `font-size` and gutter in `layouts/*.tpl` is `calc(Npx * var(--type-scale,1))` /
252
+ `calc(Npx * var(--space-scale,1))`; a skin sets the two scales in `:root` and the whole
253
+ deck retimes with its hierarchy intact (from `mckinsey` 0.86 to `apple` 1.22; both
254
+ default 1). A bespoke slide writes its sizes the same way. **Changed a scale? Re-export
255
+ (`html2pptx deck.html out.pptx`) and read its `qa`** — scaling reflows text and can push
256
+ it off-canvas; never eyeball it. (`deck-qa` takes the .pptx only.)
454
257
 
455
258
  ## Fonts — bundled faces reach the render, not the native text layer
456
259
 
457
- `assets/fonts/fonts.json` is the single source of truth for the skill's font set.
458
- `html2pptx` base64-injects every face from it at render; `scripts/deck/deck-skin-samples.mjs`
459
- and `scripts/deck/deck-fidelity.mjs` serve the same files from disk. **Add a face there and
460
- drop the `.woff2` beside it — never inline a base64 `@font-face` into a `skin.css`.**
461
-
462
- The part that trips people up: an injected `@font-face` styles the **browser** render —
463
- the raster backdrop, any `--talos-bake:1` text, `--rasterize` output, and the skin
464
- preview cards. It does **not** reach the **native pptx text layer**. There a run carries
465
- only a typeface *name*, and PowerPoint / Keynote / LibreOffice substitute whatever they
466
- have installed. So a skin can name a face, we can bundle it, and the audience still sees
467
- a substitute.
468
-
469
- Three ways a non-system face actually survives into the delivered deck:
470
-
471
- 1. **Bake it** (`--talos-bake:1` on the class) — what `cinereel` and `ivory-gold` do for
472
- their Anton titles. Guaranteed correct; the text becomes part of the backdrop image,
473
- so it is no longer editable, searchable, `deck-qa`-checkable, or animatable.
474
- 2. **Embed it** — `bin/pptx-embed-fonts.cjs` writes an OOXML font embed. Needs a TTF
475
- (we bundle woff2), and is only sane for small Latin faces, never CJK.
476
- 3. **Ask for a system face** — Helvetica Neue / Georgia / Arial and the CJK families are
477
- everywhere; that is why most skins lead with them.
478
-
479
- A face that is bundled but neither baked nor embedded is *still worth bundling* (the
480
- backdrop and the picker previews come out right), just don't assume the native text
481
- followed. `hand-drawn`'s Gochi Hand heading is exactly that case today.
482
-
483
- ## Design principles (the constraint EVERY deck honors)
484
-
485
- This is the "design ideas" bar for a freeform deck. The catalog layouts
486
- (`layouts/*.tpl`) already bake most of it in (dominant accent, contrast-picked
487
- text, motif, no under-title lines); when you hand-build slides YOU are responsible for it.
488
- Not a menu — treat it as a checklist the deck must pass.
489
-
490
- **Color — commit, don't sprinkle.**
491
- - Pick ONE dominant color from the theme and let it own the deck (roughly 60–70%
492
- of the colored area). Accent is a spark, not an equal partner; a palette where
493
- three colors fight for attention reads busy and amateur.
494
- - Alternate value: sandwich full-bleed dark slides (covers, section breaks,
495
- a punchy stat) between light content slides. The rhythm is the design.
496
- - Do NOT default to generic corporate blue because it's "safe" — let the topic
497
- inform the hue. Use the theme; the theme already made this choice deliberately.
498
-
499
- **Layout — every slide earns a visual anchor.**
500
- - No slide is a wall of text. Each one gets a structural element: a colored band,
501
- a big number, an icon row, a 2×2, a chart, or a supporting image. A title +
502
- bullets on white is the classic AI tell — restructure it (this is exactly what
503
- the build's `qa` flags as `text-only-slide`).
504
- - Reach for the strong composition patterns: two-column split, icon+label rows,
505
- a 2×2 grid, a half-bleed image with text on the other half, one hero figure.
506
- - Vary the layout slide-to-slide. Ten identical bullet slides is the single
507
- biggest "generated by a machine" signal — pick different archetypes.
508
- - Generous margins (≥0.5"), real breathing room. If a slide feels full at 60%,
509
- stop adding — whitespace is structure, not waste.
510
-
511
- **Typography — scale and pairing.**
512
- - Exactly one focal element per slide. A clear size ladder: title 24–28pt bold,
513
- body 14–16pt, captions 10–11pt. Never more than 3 sizes on one slide.
514
- - Contrast heading vs. body deliberately (weight or family), don't run everything
515
- in one flat size. Use the theme's font pairing.
516
-
517
- **Alignment & consistency.**
518
- - Everything snaps to the grid. Mixed left/center in one zone reads amateur —
519
- pick one per zone, and never center running body text (left-align it).
520
- - Same corner radius (0.06–0.1"), same 1pt border, same accent placement across
521
- every card of a family. Filled boxes ≥0.15" internal padding; siblings ≥0.25"
522
- apart.
523
- - **6×6 discipline**: ≤6 bullets, ≤6 words each. Past it, redesign as cards/graphic
524
- — don't shrink the font.
525
-
526
- **Contrast — readability is non-negotiable.**
527
- - Text on fills must clear ~4.5:1 (large/bold ~3:1). Dark fills → light text
528
- (`F1FAEE`/`FFFFFF`); light fills → the `text` color. When unsure compute
529
- luminance. The build's `qa` flags the worst offenders as `low-contrast-text`.
530
-
531
- **The AI tells to avoid** (these scream "template"):
532
- - A thin accent LINE under the title. Never. It's the #1 giveaway; use space,
533
- weight, or color for hierarchy instead.
534
- - Repeating the same layout every slide; centering body copy; defaulting to blue;
535
- equal-weight color soup; a slide that is only text.
536
-
537
- ## Picking layouts & snippets — deterministic selection
538
-
539
- Choose layouts like a **matcher**, not by vibe. Two decisions:
540
-
541
- **Style — decide once, lock the whole deck.** From the prompt keywords pick ONE
542
- **skin** (`--style <skin>`, see `styles/README.md`) and use it on every page. Never
543
- mix skins mid-deck.
544
-
545
- **Layout — match on description, then dedup.** Every layout carries a one-line
546
- description of `role · structure · density · has-image` (see `layouts/README.md`).
547
- For each planned page match by priority:
548
- - **P1 Layout structure** (most important): has image? image left/right? text-only?
549
- → pick a template whose description matches (`image-left`, `text-only`, …).
550
- - **P2 Content density**: 1–2 points → `minimal`/`hero`/`centered`; 3–4 →
551
- `two-column`/`balanced`; 5+ → `grid`/`dense`/`cards`.
552
- - **P3 Diversity (ENFORCED)**: never the same content template twice **in a row**;
553
- **≤2 times total** across the deck. This dedup rule is the real source of
554
- "not machine-made" — honor it over a marginally better P1/P2 fit.
555
-
556
- Per page type: **cover** — one that fits the theme; **toc** — by chapter count (3–4
557
- `centered` / 5–6 `grid` / 7+ `dense`); **transition** — reuse ONE across all section
558
- breaks; **ending** — by purpose (thanks / Q&A / contact).
559
-
560
- Plan it explicitly before authoring — list each page as
561
- `type · title · density · image? · chosen-template` so the ≤2 dedup constraint is
562
- visible while you assign.
563
-
564
- **Functional & decorative snippets — self-serve, NOT planned.** These are not matched
565
- per page; add them as the deck needs. Catalogs in `assets/snippets/`:
566
- - **`functional.md`** — `nav-bar` / `breadcrumb` / `footer-pageno` / `cta-row`. Put a
567
- footer (`org · page N/total`) on nearly every content page and a nav-bar on
568
- section/content pages — the cheapest "produced" signal. `cta-row` on the ending.
569
- - **`motifs.md`** — `seal` / `gold-flourish` / `neon-dot` / `corner-frame` /
570
- `thin-geo` / `grid-dots`. Sprinkle **≤2 per slide** as a style signature; match the
571
- motif family to the style. Never put content in a motif.
572
- - **`decorations.md`** — full-slide decorations (`neural-net` / `data-pipeline` /
573
- `code-editor` / `cube-3d` / `radar` / `glow-cover` / `chinese-seal`) for
574
- diagram/hero pages.
575
-
576
- All snippets are hybrid-native: chips, rules, frames, gradients, SVG **bake into the
577
- backdrop**; labels, page numbers, chart data stay **native/editable**. Route every
578
- color through the skin tokens (`var(--accent, …)`) so one skin reskins them — the same
579
- `footer-pageno` fragment becomes tech-cyan or 国风-朱红 by swapping `--accent`.
580
-
581
- **Pairing a snippet with the theme** — two independent dimensions:
582
- - **Color = automatic.** Snippets read only the skin tokens listed in the slide
583
- contract above (`--accent --title --body --muted --hair --slide-bg --title-color
584
- --body-color --font-heading --font-body --c1..--c4`). Pick the skin and every
585
- snippet recolors for free; the value after the comma is just the fallback. Any
586
- OTHER var name (e.g. `--accent2`, `--primary`) is not defined → it silently sticks
587
- on its fallback, so **only use those names**. Need one snippet in an off-skin
588
- color? Override on its `<section>`: `style="--accent:#b8292e;--muted:#9a8b76"`.
589
- - **Form = your choice.** The theme supplies the palette, NOT the shape. A `neon-dot`
590
- on a 侘寂 light theme reads wrong even recolored — pick the motif/functional family
591
- that matches the style's aesthetic (seal→chinese, gold-flourish→black-gold,
592
- neon/corner-frame→tech).
593
- - **Exception — semantic colors don't follow the theme.** Multi-color ramps that
594
- encode meaning (`neural-net` layer colors, `cube-3d` Layer 1/2/3, `radar` severity)
595
- are intentionally hardcoded; the theme reskins their title/bg/accent but the ramp
596
- stays so the hierarchy reads.
597
-
598
- ## Verify before delivering (MANDATORY automated visual-QA loop)
599
-
600
- **Assume there are problems — your job is to find them.** Treat QA as a bug hunt,
601
- not a confirmation step. Use the cheapest faithful surface for each stage: HTML
602
- screenshots while authoring, the build's own checks on every build, and one
603
- Office-rendered sign-off at the end. Do not launch LibreOffice after every edit.
604
-
605
- **Machine checks come with the build.** Every `html2pptx … out.pptx` run prints,
606
- next to the build result, `valid` (does the file open — `false` exits 1: never
607
- deliver) and `qa: { ok, findings:[{slide,code,message}] }` — deck-qa's lint:
608
- **off-canvas**, **tiny-font** (<10pt), **shape-overlap**, **text-only-slide**,
609
- **low-contrast-text**, **placeholder-text** (替换为…/占位/在这里写/Lorem left in a
610
- slide) and **font-mismatch** (CJK in a Latin-only font → 豆腐; language defaults to
611
- the system locale, override with `TALOS_DECK_LANG`). **Fix every finding before
612
- anything else** — they are objective and free, so they must never reach the
613
- vision pass or delivery. No separate `doc-validate` / `deck-qa` call is needed
614
- after a build.
615
-
616
- **The loop** (≤2 delivery rounds, then report residuals — never silently ship):
617
-
618
- 1. **Author against the HTML preview.** `html2pptx.cjs deck.html --preview-dir
619
- <dir> --style <id> …` renders every slide the first time; after a fix,
620
- `--preview-pages 3,7-8` renders only the changed ones. Read these images and
621
- iterate until the composition is clean.
622
- 2. **Build and fix the build's findings** (`html2pptx … deck.pptx`, see above).
623
- 3. **Sign off once: `deck-qa.cjs deck.pptx --final`.** One call runs strict XSD
624
- validation (a failure exits 1), the lint, an Office render (LibreOffice via
625
- `doc-convert`, cached), and the render check: `render-overflow` (text Office
626
- re-wrapped onto more lines than its box holds, or past its side) and
627
- `render-collision` (text that grew out of its box onto another). It returns
628
- `review` — the slides with such a finding or a native chart (Office draws
629
- charts; the preview never showed them) — and `previews`, a ≈72dpi image of
630
- each. **Read only `previews`.** Every other slide already passed your eyes in
631
- the HTML preview, and in the default hybrid mode its visuals are baked from
632
- that same render; Office only re-lays the native text, which was just
633
- measured. Re-wrapping at a different word, spacing CJK and Latin apart or a
634
- substituted Latin face is normal, not a defect.
260
+ `assets/fonts/fonts.json` is the single source of the font set: html2pptx base64-injects
261
+ every face from it (sample/fidelity scripts serve the same files). Add a face there with
262
+ its `.woff2` — never inline a base64 `@font-face` into a `skin.css`. An injected face
263
+ styles only the **browser** render (backdrop, baked text, `--rasterize`, previews); a
264
+ native pptx run carries just a typeface *name*, and PowerPoint / Keynote / LibreOffice
265
+ substitute what they have. A non-system face survives into the delivered deck only if:
266
+
267
+ 1. **baked** (`--talos-bake:1` on the class) — exact, but that text is no longer
268
+ editable, searchable, `deck-qa`-checkable or animatable;
269
+ 2. **embedded** — `bin/pptx-embed-fonts.cjs` (needs a TTF; small Latin faces only, never
270
+ CJK); or
271
+ 3. **a system face** (Helvetica Neue / Georgia / Arial, the CJK families) — why most
272
+ skins lead with them.
273
+
274
+ ## Design principles (the bar EVERY deck passes)
275
+
276
+ The catalog layouts bake most of this in; on a bespoke slide it is yours.
277
+
278
+ - **Colour — commit.** One dominant colour (~60–70% of the coloured area), accent as a
279
+ spark; let the skin's palette choose the hue, never a "safe" corporate blue. The
280
+ background comes from the skin — don't paint your own. Alternate value: put dark
281
+ slides (cover, section breaks, a punchy stat — via a dark layout or `.slide-inverse`
282
+ where the skin defines it) between light content slides.
283
+ - **Every slide earns a visual anchor** — a colour band, big number, icon row, 2×2,
284
+ chart or image, plus ≥2 visual elements in all (anchor, motif, chrome). Title +
285
+ bullets on a plain ground is the classic AI tell (`qa` flags `text-only-slide`). Vary the layout slide to slide; ≥0.5" margins; if a slide feels
286
+ full at 60%, stop adding.
287
+ - **Type.** One focal element per slide; ≤3 sizes per slide; ≤2 font families; contrast
288
+ by weight or family. Catalog layouts already set the ladder — don't override their
289
+ sizes. Bespoke: title `size 6–7` (48–72px), section headings `size 5–6`, body 14–24px,
290
+ labels ≥13px; `qa` flags anything under 10pt as `tiny-font`. Never shrink type to fit
291
+ — split or cut instead (≤6 items, each ≤2 lines; past that, cards or a graphic).
292
+ - **Alignment.** Snap to a grid; one alignment per zone; never centre running body
293
+ text. Siblings share radius, border and accent placement (use the skin's classes);
294
+ filled boxes keep ≥0.15" inner padding, siblings ≥0.25" apart.
295
+ - **Contrast.** Text on fills clears ~4.5:1 (large/bold ~3:1): `var(--title)` /
296
+ `var(--body)` on the skin's own surfaces; `qa` flags `low-contrast-text`.
297
+
298
+ ## Snippets — self-serve, not planned
299
+
300
+ `assets/snippets/`: **`functional.md`** (`nav-bar` · `breadcrumb` · `footer-pageno` ·
301
+ `cta-row` — a footer on nearly every content page is the cheapest "produced" signal;
302
+ `cta-row` on the ending) · **`motifs.md`** (`seal` · `gold-flourish` · `neon-dot` ·
303
+ `corner-frame` · `thin-geo` · `grid-dots`; ≤2 per slide, never content inside) ·
304
+ **`decorations.md`** (full-slide `neural-net` · `data-pipeline` · `code-editor` ·
305
+ `cube-3d` · `radar` · `glow-cover` · `chinese-seal` for diagram/hero pages). They read
306
+ only the contract tokens, so colour follows the skin automatically; FORM is your choice —
307
+ match the family's aesthetic (seal→chinese, gold-flourish→black-gold,
308
+ neon/corner-frame→tech). Exception: meaning-bearing ramps (`neural-net` layers,
309
+ `cube-3d` levels, `radar` severity) stay hardcoded on purpose.
310
+
311
+ ## Custom skins
312
+
313
+ When the user picks 其他/自定义 or 使用我的模板文件 in the style picker, clarify the route —
314
+ **prefer B**:
315
+
316
+ - **B — derive a bespoke look (PREFERRED).** From an **existing deck** (the picker's
317
+ 使用我的模板文件): learn its STYLE — what every page shows (logo, bars, footer,
318
+ page number, palette, fonts) — with `template-skin <deck.pptx> <name> --label "<a short name the user would recognise, e.g. 太平洋证券研报>"`, not its
319
+ photos/tables/text. The template's look **is** the style: it overrides an earlier
320
+ 深色/浅色 ask — do not ask how to blend them. Confirm the two `samples` + `uncertain`
321
+ list as an image card (relay any `warnings`); a fix is a re-run: `--exclude <kept key>`
322
+ — a brand logo, a disclaimer (read `kept[].hint`), a photo taken as logo —
323
+ `--include <uncertain key>`, `--content-page N`. Never rebuild the look as a
324
+ hand-written skin and never set `TALOS_DOC_STYLES_DIR` yourself (2026-10-09 a deck
325
+ dropped its learned template for a patched mckinsey copy). Then
326
+ author with `style: "<name>"`: titles go in the template's title bar, content in its
327
+ free area (a bespoke slide starts at top ≥210px); `deck-plan --layouts --style <name>`
328
+ lists what fits. Refilling its
329
+ ORIGINAL PAGES is a separate route, if asked (rewrite.md "Reuse an uploaded
330
+ deck"). From a **reference image / brand color**: sample the palette into a new skin.
331
+ - **A — map to the nearest catalog family** (they only described a vibe): pick the
332
+ closest, render its sample, confirm.
333
+ - **C — tweak an existing skin** ("刚才那个族但换绿色"): clone it, swap accent/palette or
334
+ flip light↔dark.
335
+
336
+ Render a cover+content sample of the result and confirm (same image-card step as a
337
+ normal pick) before authoring; save the preference to memory.
338
+
339
+ A new skin is a user style: one `skin.css` defining the full class contract
340
+ (`layouts/README.md`), saved at `$TALOS_DOC_STYLES_DIR/styles/<name>/skin.css` (the
341
+ user's own styles, kept across sessions and shared by every expert) and rendered with
342
+ `--style <name>`. Pick a name no catalog entry uses — catalog ids resolve first. The
343
+ saved ones are `ls "$TALOS_DOC_STYLES_DIR/styles"`; offer them for "我之前那个风格". If
344
+ `$TALOS_DOC_STYLES_DIR` is unset (CI / bare headless run), build it in the working folder
345
+ and pass `--style ./styles/<name>`. Never write inside the skill directory: it is an
346
+ integrity-checked install, reinstalled — and your file lost — once it changes.
347
+ Start from the catalog skin of the same light/dark polarity
348
+ (`styles/<id>/skin.css`) and replace its tokens with the sampled palette — every layout
349
+ then works in it. To reuse an existing deck's palette, `pptx-theme` extracts it as JSON:
350
+ copy its colours into those tokens. `html2pptx --theme` does not do this: on a
351
+ `--style` deck the skin's tokens override the theme's.
352
+
353
+ ## Verify before delivering (MANDATORY)
354
+
355
+ **Assume there are problems — your job is to find them.** Use the cheapest faithful
356
+ surface per stage: HTML screenshots while authoring, the build's own checks on every
357
+ build, one Office-rendered sign-off at the end.
358
+ Do not launch LibreOffice after every edit.
359
+
360
+ 1. **Author against the HTML preview.** `html2pptx.cjs deck.html --preview-dir <dir>
361
+ --style <id> …` renders every slide the first time; after a fix,
362
+ `--preview-pages 3,7-8` renders only the changed ones. Iterate until clean.
363
+ **Look at each preview once:** after the first render read `sheet.png` (every slide
364
+ as a 640×360 tile, one image), then open a single `slide-NNN.png` only where the
365
+ sheet shows something to check, then only the pages you re-rendered. Never re-read an
366
+ image you already looked at — `deck-qa --final` (step 3) is the final check.
367
+ 2. **Build and fix the build's findings.** Every `html2pptx … out.pptx` prints `valid`
368
+ (`false` exits 1 — never deliver) and `qa: {ok, findings}` — deck-qa's lint (codes in
369
+ bin-reference.md). Fix every finding first; they are objective and free.
370
+ No separate `doc-validate` / `deck-qa` call is needed after a build.
371
+ 3. **Sign off once: `deck-qa.cjs deck.pptx --final`** — strict XSD validation (failure
372
+ exits 1), the lint, a cached Office render and its `render-overflow` /
373
+ `render-collision` check. It returns `review` (slides with a finding or a native
374
+ chart, which only Office draws) and `previews` (≈72dpi images of those).
375
+ **Read only `previews`.** For hybrid PPTX QA, other slides already passed the HTML
376
+ preview; their visuals come from that render. PDF delivery needs the final check below.
635
377
  - **Read every slide instead** (`pdf-render.cjs <deck>.office.pdf all 1`) when
636
- `html2pptx` reported a `layout (fallback)` engine (no baked visuals, so the
637
- preview no longer matches) or you changed the PPTX after building it
638
- (`doc-xml`, `pptx-slide`, a script).
639
- - `office.faithful: false` (no LibreOffice, only a Chromium reconstruction) or
640
- `office.error`: there is no Office layout to check. The HTML preview stands;
641
- say so when delivering.
642
- 4. **Vision critic — the part only a model can do.** It judges the HTML preview
643
- images (step 1) and the `previews` of step 3 — never a second full pass over
644
- slides already judged. LOOK at every image you critique (not "glance").
645
- **Either you critique a slide or a sub-agent does, not both:** with sub-agent
646
- dispatch, hand slides to fresh-eyes critics in parallel and read their verdicts
647
- instead of opening the images yourself. Give each critic the slide's findings
648
- so it does NOT re-report them — it judges only what a machine can't: balance
649
- and whitespace, hierarchy and focal point, a real visual anchor (not a text
650
- wall), palette/motif consistency, image quality, crowding and uneven gaps,
651
- misaligned sibling columns, decorative lines sized for one-line text when the
652
- title wrapped, and leftovers the lint can't phrase-match (`{{tokens}}`,
653
- "undefined", stray "xxx"). Each critic returns **{slide, verdict: pass|fail,
654
- defects:[…]}**.
655
- 5. **Regenerate only the failing slides.** Re-author that `<section>` (one section
656
- per file lets a slide rebuild in isolation) and rebuild, or use `pptx-slide`
657
- (dup/remove/reorder) / `pptx-clone` for template-driven slides. Then preview
658
- the changed slides, rebuild and run `--final` again — one fix often creates
659
- another. Stop when a pass finds nothing new **or** after 2 rounds; if defects
660
- remain, **deliver with an explicit note listing them**.
661
-
662
- **Reading images is the most token-expensive step of the deck flow** (≈ w×h∕750
663
- tokens each; a 1280×720 slide ≈ 1.2k). Preview and `--final` images are ≈72dpi,
664
- ¼ of the default render; `pdf-render … 2` only when fine print must be legible.
665
-
666
- **Do not declare done before at least one complete build→sign-off→critic cycle.**
378
+ html2pptx reported a `layout (fallback)` engine, or you changed the PPTX after
379
+ building it (`doc-xml`, `pptx-slide`, a script).
380
+ - `office.unavailable`: PPTX has no Office layout check; disclose this. A running
381
+ LibreOffice that fails exits 1; rerun `--final`. `--require-office` fails both.
382
+ 4. **Vision critic — you, not a sub-agent.** Judge the HTML previews (step 1) and the
383
+ `previews` of step 3; PDF delivery also requires the check below. LOOK at every image you critique.
384
+ Never dispatch the visual review to sub-agents: they may not see images at all, and
385
+ one deck lost 10 minutes to critics reading placeholders. Judge what a machine can't: balance and whitespace, hierarchy and
386
+ focal point, a real anchor, palette/motif consistency, image quality, crowding and
387
+ uneven gaps, misaligned sibling columns, decorative lines sized for one-line text when
388
+ the title wrapped, leftovers the lint can't phrase-match (`{{tokens}}`, "undefined",
389
+ "xxx"). Each critic returns `{slide, verdict: pass|fail, defects:[…]}`.
390
+ 5. **Regenerate only the failing slides** (re-author that `<section>`, or
391
+ `pptx-slide` / `pptx-clone` for template-driven slides), preview them, rebuild, run
392
+ `--final` again. Stop when a pass finds nothing new **or** after 2 rounds; deliver
393
+ with an explicit note of what remains.
394
+
395
+ Images are the most token-expensive step (≈ w×h∕750 tokens; a 1280×720 slide ≈ 1.2k):
396
+ previews and `--final` images are ≈72dpi; `pdf-render … 2` only when fine print must be
397
+ legible. **Do not declare done before one complete build → sign-off → critic cycle.**
398
+
399
+ **PDF delivery:** convert the exact final PPTX with `doc-convert.cjs deck.pptx out.pdf`,
400
+ or reuse its final QA Office PDF if the PPTX is unchanged. Inspect every delivered PDF
401
+ page once. If Office is unavailable or fails, do not deliver a substitute PDF.
402
+ Use matching fonts in the Office environment; font substitution and different viewers
403
+ can change wrapping. Fix visible differences in the source or font environment and
404
+ re-export. HTML previews guide authoring; they are not the final PDF.
667
405
 
668
406
  ## CJK notes
669
407
 
670
- - Do NOT set `fontFace` to a Latin-only font for CJK text — omit it (platform
671
- default renders CJK) or use the theme's font.
672
- - CJK line estimate ≈ width(inches) × 96 / fontSize(px) characters; wrap long 中文
673
- manually with `\n` only when you must control the break.
674
- - **Single-line boxes are auto-widened** on conversion (+10% CJK / +5% Latin)
675
- because PowerPoint re-lays-out text wider than Chromium measured — no need to
676
- hand-pad single-line labels/titles. Multi-line boxes are untouched (still keep
677
- ~10% slack there so wrapped text doesn't butt the edge).
408
+ - html2pptx drops the typeface from CJK runs on purpose (any pinned family can render
409
+ blank somewhere), so a CJK title cannot be forced into a serif face natively — bake it
410
+ if the face matters. The runs are tagged zh-CN / zh-TW / ja-JP / ko-KR and a Chinese deck's
411
+ theme names Microsoft YaHei (Traditional: JhengHei) as its East Asian font, so bold Chinese
412
+ gets a real bold. Traditional is read from the text; `<html lang="zh-Hant">` (or zh-TW/HK) decides a deck whose text shows neither script.
413
+ - Text boxes are widened automatically on conversion because PowerPoint lays text out
414
+ wider than Chromium: single-line boxes by ~22% (CJK) / ~16% (Latin), multi-line by
415
+ ~10% / ~6%. Don't hand-pad labels; do leave slack in multi-line boxes.
416
+ - Slot capacity is measured (`layouts/capacity.json`) and enforced by deck-plan /
417
+ deck-build — trust their numbers over a character estimate.