@topmindspace/tms-skills 0.1.13 → 0.1.15

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 (44) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/README.en.md +3 -3
  3. package/README.md +3 -3
  4. package/package.json +1 -1
  5. package/top-ppt-html/README.en.md +2 -2
  6. package/top-ppt-html/README.md +1 -1
  7. package/top-ppt-html/SKILL.md +3 -3
  8. package/top-ppt-html/assets/examples/2026-09-09-architecture-graphite-dark.html +59 -20
  9. package/top-ppt-html/assets/examples/2026-09-09-presentation-business-blue.html +64 -26
  10. package/top-ppt-html/assets/examples/2026-09-09-research-mckinsey.html +64 -42
  11. package/top-ppt-html/assets/examples/2026-09-26-topmind-tms-skills-showcase.html +85 -46
  12. package/top-ppt-html/assets/examples/2026-09-26-topmind-tms-skills-showcase.model.json +1 -1
  13. package/top-ppt-html/assets/pptx-export.js +40 -17
  14. package/top-ppt-html/assets/showcase/showcase-charts.png +0 -0
  15. package/top-ppt-html/assets/showcase/showcase-cover.png +0 -0
  16. package/top-ppt-html/assets/showcase/showcase-gates.png +0 -0
  17. package/top-ppt-html/assets/showcase/showcase-modes.png +0 -0
  18. package/top-ppt-html/assets/showcase/showcase-positioning.png +0 -0
  19. package/top-ppt-html/assets/showcase/showcase-split.png +0 -0
  20. package/top-ppt-html/assets/showcase/showcase-toolbar.png +0 -0
  21. package/top-ppt-html/assets/style-gallery.html +3 -3
  22. package/top-ppt-html/assets/templates/architecture.html +59 -20
  23. package/top-ppt-html/assets/templates/engine.css +17 -1
  24. package/top-ppt-html/assets/templates/presentation.html +59 -20
  25. package/top-ppt-html/assets/templates/research.html +60 -27
  26. package/top-ppt-html/assets/templates/ui.js +1 -1
  27. package/top-ppt-html/package-lock.json +2 -2
  28. package/top-ppt-html/package.json +1 -1
  29. package/top-ppt-html/references/components-atoms.md +14 -1
  30. package/top-ppt-html/references/content-rules.md +1 -1
  31. package/top-ppt-html/references/design-system.md +1 -1
  32. package/top-ppt-html/references/icons.md +19 -0
  33. package/top-ppt-html/references/layout-grammar.md +17 -0
  34. package/top-ppt-html/references/layouts-combo.md +2 -4
  35. package/top-ppt-html/references/modes.md +1 -1
  36. package/top-ppt-html/references/pptx-export.md +1 -1
  37. package/top-ppt-html/scripts/build_pptx.js +50 -22
  38. package/top-ppt-html/scripts/layout-constants.json +18 -6
  39. package/top-ppt-html/scripts/quality_gate.py +2 -2
  40. package/top-ppt-html/scripts/render_from_model.py +2 -4
  41. package/top-ppt-html/scripts/scaffold_report.py +4 -10
  42. package/top-ppt-html/scripts/test_feedback_gates.py +2 -2
  43. package/top-ppt-html/scripts/validate_pptx.py +27 -11
  44. package/top-ppt-html/scripts/validate_report.py +27 -5
package/CHANGELOG.md CHANGED
@@ -1,3 +1,37 @@
1
+ ## [0.1.15] - 2026-09-26
2
+
3
+ ### Release
4
+ - Republish of the **0.1.14 craft overhaul** (MD3 结论条 · 一屏/大纲自适应 · 克制图标). npm registry left `0.1.14` in a staged/conflict state (`E409 previously staged`); content identical to the `v0.1.14` GitHub Release / docs Pages source.
5
+
6
+ ### 红线未动
7
+ 反截断、图表多样性地板、Mode A craft、runtime SHA 同版本门禁、中文「演示文稿」术语、整仓 npm 发版、motion=none。
8
+
9
+ ## [0.1.14] - 2026-09-26
10
+
11
+ ### Craft overhaul · 三支柱:结论条 · 一屏高度/大纲自适应 · 克制图标
12
+
13
+ #### A. MD3 结论条(原 so-what)
14
+ - `.sowhat` 升为 MD3 tonal 衬条(`accent-soft` 底 + 左 4px accent 轨 + `--fw-title` / `fs-h3`);**默认不显示「So what / SO WHAT」标签**(可选 `.sowhat--labeled`)。
15
+ - 与上方主内容呼吸间距 `clamp(28px,3.6vh,48px)`;`:has(>.sowhat)` 时 wrap 纵向 flex,结论条 `flex:none` 防挤压。
16
+ - CSS 迁入公共 `engine.css`(三模式共享);research 模板去掉重复规则。
17
+ - PPTX `soWhatBar()` 双通道只画衬底+左轨+正文;`chartBottom` 相对 `soWhatY` 让位 0.12→0.20in。
18
+ - 门禁:`EXHIBIT_PAGE_MISSING_SOWHAT` 改检测结论条形状;文案「结论条」。
19
+
20
+ #### B. 一页一屏 · 大纲自适应
21
+ - 高度契约:`section.band` 一屏;主内容不得溢出或压进结论条/注释带;放不下走 `overflowRule`(重构→拆页→换形态→有限缩字),禁静默截断。
22
+ - 大纲:**>8 条须 `.agenda--2col`(两列/两排)**;>16 拆篇。阈值入 `contentQuality.agenda`;`validate_report` 只认 `<ol class="…agenda--2col">`(修 CSS 选择器误伤假通过)。
23
+ - Showcase 12 条大纲改双列;`layout-grammar` / `components-atoms` / SKILL 同步。
24
+
25
+ #### C. 图标原则(行业共识 · 非装饰)
26
+ - `icons.md` 增补 wayfinding / 同家族 / 有限密度 / 四正当位置 / 禁区(Agenda·表·图内);Mode A 内容页必做。
27
+ - Showcase / 黄金样张落地 `card__ico` / `ul--ico`;`validate_report` 对「≥2 卡且无 `.card__ico`」发 WARN。
28
+
29
+ #### 发布
30
+ - 样张、模板、references、README、PUBLISHING、截图同步;版本对齐 **0.1.14**。
31
+
32
+ ### 红线未动
33
+ 反截断、图表多样性地板、Mode A craft、runtime SHA 同版本门禁、中文「演示文稿」术语、整仓 npm 发版、motion=none。
34
+
1
35
  ## [0.1.13] - 2026-09-26
2
36
 
3
37
  ### Changed
package/README.en.md CHANGED
@@ -76,9 +76,9 @@ See [top-ppt-html/README.md](./top-ppt-html/README.md) and [SKILL.md](./top-ppt-
76
76
 
77
77
  | Skill | Version | What it does |
78
78
  |-------|---------|--------------|
79
- | [`top-ppt-html`](./top-ppt-html/) | **0.1.13** | Formal business decks: paginated HTML + editable 16:9 PPTX; dual delivery · MD3-inspired density; 3 modes × 9 styles; strict 0/0 |
79
+ | [`top-ppt-html`](./top-ppt-html/) | **0.1.15** | Formal business decks: paginated HTML + editable 16:9 PPTX; dual delivery · MD3-inspired density; 3 modes × 9 styles; strict 0/0 |
80
80
 
81
- Installer [`@topmindspace/tms-skills`](https://www.npmjs.com/package/@topmindspace/tms-skills) is also **0.1.13** (whole-repo same-tag releases).
81
+ Installer [`@topmindspace/tms-skills`](https://www.npmjs.com/package/@topmindspace/tms-skills) is also **0.1.15** (whole-repo same-tag releases).
82
82
 
83
83
  ## Install
84
84
 
@@ -88,7 +88,7 @@ Installer [`@topmindspace/tms-skills`](https://www.npmjs.com/package/@topmindspa
88
88
  npx @topmindspace/tms-skills list
89
89
  npx @topmindspace/tms-skills install top-ppt-html
90
90
  npx @topmindspace/tms-skills install top-ppt-html --to ./.claude/skills
91
- npx @topmindspace/tms-skills@0.1.13 install top-ppt-html # pin
91
+ npx @topmindspace/tms-skills@0.1.15 install top-ppt-html # pin
92
92
  ```
93
93
 
94
94
  ```bash
package/README.md CHANGED
@@ -74,9 +74,9 @@
74
74
 
75
75
  | 技能 | 版本 | 做什么 |
76
76
  |------|------|--------|
77
- | [`top-ppt-html`](./top-ppt-html/) | **0.1.13** | 正式商务演示:HTML 可翻页 + 16:9 可编辑 PPTX;双交付 · MD3 密度克制;三模式 × 九风格;strict 0/0 |
77
+ | [`top-ppt-html`](./top-ppt-html/) | **0.1.15** | 正式商务演示:HTML 可翻页 + 16:9 可编辑 PPTX;双交付 · MD3 密度克制;三模式 × 九风格;strict 0/0 |
78
78
 
79
- 安装器 [`@topmindspace/tms-skills`](https://www.npmjs.com/package/@topmindspace/tms-skills) 同为 **0.1.13**(整仓同 tag 发版)。
79
+ 安装器 [`@topmindspace/tms-skills`](https://www.npmjs.com/package/@topmindspace/tms-skills) 同为 **0.1.15**(整仓同 tag 发版)。
80
80
 
81
81
  ## 安装
82
82
 
@@ -86,7 +86,7 @@
86
86
  npx @topmindspace/tms-skills list
87
87
  npx @topmindspace/tms-skills install top-ppt-html
88
88
  npx @topmindspace/tms-skills install top-ppt-html --to ./.claude/skills
89
- npx @topmindspace/tms-skills@0.1.13 install top-ppt-html # 钉版本
89
+ npx @topmindspace/tms-skills@0.1.15 install top-ppt-html # 钉版本
90
90
  ```
91
91
 
92
92
  ```bash
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@topmindspace/tms-skills",
3
- "version": "0.1.13",
3
+ "version": "0.1.15",
4
4
  "description": "TopMindspace 智能体技能 monorepo — 通过 npm / GitHub npx 安装技能(top-ppt-html:HTML+PPT 双交付,正式商务演示文稿)。",
5
5
  "license": "MIT",
6
6
  "author": "TopMindspace",
@@ -18,7 +18,7 @@ Plenty of PPT skills exist. This one is for **formal business presenting**: clea
18
18
  </p>
19
19
 
20
20
  - Skill id: `top-ppt-html`; brand: **TopPPT HTML**
21
- - Version: **v0.1.13** (same tag as `@topmindspace/tms-skills@0.1.13`)
21
+ - Version: **v0.1.15** (same tag as `@topmindspace/tms-skills@0.1.15`)
22
22
  - **Agent entry**: `SKILL.md` → `references/playbook.md` (L1) → L2 on demand
23
23
  - **Human maintainers**: this README (install / commands / layout); do not treat it as the generation spec
24
24
 
@@ -73,7 +73,7 @@ Also: arrow-key paging; **Esc** closes modals. After style/theme change, re-run
73
73
 
74
74
  ```bash
75
75
  npx @topmindspace/tms-skills install top-ppt-html
76
- npx @topmindspace/tms-skills@0.1.13 install top-ppt-html
76
+ npx @topmindspace/tms-skills@0.1.15 install top-ppt-html
77
77
  npx github:topmindspace/tms-skills install top-ppt-html
78
78
  ```
79
79
 
@@ -18,7 +18,7 @@
18
18
  </p>
19
19
 
20
20
  - 技能标识:`top-ppt-html`;品牌名:**TopPPT HTML**
21
- - 版本:**v0.1.13**(与 `@topmindspace/tms-skills@0.1.13` 同 tag)
21
+ - 版本:**v0.1.15**(与 `@topmindspace/tms-skills@0.1.15` 同 tag)
22
22
  - **智能体入口**:`SKILL.md` → `references/playbook.md`(L1)→ L2 按需
23
23
  - **人类维护者**:本 README(安装 / 命令 / 目录);勿把本文件当生成规范
24
24
 
@@ -4,7 +4,7 @@ description: "Use when 用户要做报告、演示、汇报、PPT、slides、dec
4
4
  license: MIT
5
5
  compatibility: "Python 3 stdlib for HTML generation; Node >=18 + pptxgenjs for PPTX; optional playwright for browser regression / theme captures."
6
6
  metadata:
7
- version: "0.1.13"
7
+ version: "0.1.15"
8
8
  author: TopMindspace
9
9
  ---
10
10
  # TopPPT HTML
@@ -102,7 +102,7 @@ metadata:
102
102
  1. **单文件零外链**——无 CDN/外部字体/外部图片,图形一律内联 SVG;`<img src>` 仅 `data:` 或相对路径且必带 `alt`(单图 ≤1.5MB、全篇 ≤8MB);无素材图不留空不省略,用配图占位(`components.md` §11c-3)。
103
103
  2. **模式先定后写**——从对应模式模板起步;`data-mode` = `REPORT_MODEL.mode`,页面无模式切换;MD3 映射;PPTX 字号走三模式独立比例尺。
104
104
  3. **亮暗双主题一致**——CSS 变量整块换肤 + header 切换 + 文件级记忆;`REPORT_MODEL.theme` 与页面一致,PPTX 同主题导出;强调带只用 accent 家族,**末页禁 `band--deep`**。
105
- 4. **每页一屏 + 高度稳定**——`section.band` ≥ 一屏高且垂直居中;六七成屏用 `.band--top`,长结构页 `.band--flow`。放不下按「重构承载 → 拆页/分章 → 换布局形态 → 有限缩字号」(`containers.overflowRule`);**禁止**静默截断或为疏朗删 so-what/证据;拆页保持信息完整。
105
+ 4. **每页一屏 + 高度稳定**——`section.band` ≥ 一屏;主内容不得溢出画布或压进结论条/注释带。放不下按「重构承载 → 拆页/分章 → 换布局形态 → 有限缩字号」(`containers.overflowRule`);**禁止**静默截断或为疏朗删结论条/证据。大纲 **>8 条**用 `.agenda--2col`(两列/两排)。结论条 = MD3 衬条(无「So what」标签);要点卡/关键列表按 `icons.md` 克制取用图标。
106
106
  5. **PPT 式翻页 + 骨架固定**——`section.band` 即一页;方向键翻页、页码可点;Agenda 第二页(arch 内容页 ≤4 可省);页头只留 eyebrow + 标题 + 可选导语,页脚全文一个;页头页脚之外不加装饰。
107
107
  6. **标题与字号**——主标题粗体;research=结论句(≥12 字);**presentation=主张/行动句**(禁话题标签);字号随模式,`clamp()`,图表不缩水。
108
108
  7. **组合版式 + 细节保全**——默认一页 = 主件 + 从件 + 注释层(矩阵 playbook §四);先判定决策必需信息再定形态,**禁砍口径列/时间列/维度**;密度 L/M/H 禁连续 3 页同档;同一版式不连用超 2 页(扩展签名可 3 页)。
@@ -132,7 +132,7 @@ metadata:
132
132
 
133
133
  ## 版本口径
134
134
 
135
- 包 semver(现 0.1.13)≠ schema 线(layout-constants / model-schema,现 `0.1`);patch 不抬 schema。
135
+ 包 semver(现 0.1.15)≠ schema 线(layout-constants / model-schema,现 `0.1`);patch 不抬 schema。
136
136
 
137
137
  ## 环境依赖
138
138
 
@@ -263,7 +263,8 @@ section.band{scroll-snap-align:start}
263
263
  svh 兼容移动端浏览器工具栏;旧浏览器落到 100vh。 */
264
264
  .band{padding-block:var(--band-pad);min-height:var(--band-min);
265
265
  display:flex;flex-direction:column;justify-content:center}
266
- .band>.wrap{width:100%}
266
+ .band>.wrap{width:100%;min-height:0}
267
+ .band>.wrap>.grid,.band>.wrap>.stack,.band>.wrap>.fig,.band>.wrap>.cols-2,.band>.wrap>.cols-3,.band>.wrap>.exhibit{min-height:0}
267
268
  .band--top{justify-content:flex-start} /* 内容约六七成屏:贴顶 + 底部自然留白 */
268
269
  .band--fit{justify-content:center} /* 内容接近一屏:垂直居中(默认行为,保留以兼容旧报告) */
269
270
  .band--flow{min-height:0;justify-content:flex-start} /* 长内容页(附录/全量参考)显式退出固定页高 */
@@ -504,6 +505,21 @@ tbody tr:hover{background:var(--surface-1)}
504
505
  td strong{color:var(--text);font-weight:600}
505
506
  td.k{color:var(--text);font-weight:500;white-space:nowrap}
506
507
  td.num{font-variant-numeric:tabular-nums}
508
+ /* ── 结论条(MD3 tonal strip · 本页重点收口;默认不显示「So what」标签) ──
509
+ 与上方主内容呼吸间距 ≥ --sp-6;flex:none 防止被主内容挤压;一屏页不得压进注释带。 */
510
+ .band>.wrap:has(>.sowhat),.band>.wrap:has(>.note){display:flex;flex-direction:column}
511
+ .sowhat{display:flex;align-items:flex-start;gap:var(--sp-3);flex:none;
512
+ margin-top:clamp(28px,3.6vh,48px); /* ≥ --sp-6 量级,修「贴底」 */
513
+ padding:var(--sp-4) var(--sp-5);background:var(--accent-soft);border:1px solid var(--accent-soft-2);
514
+ border-left:4px solid var(--accent);border-radius:var(--r-md);max-width:100%}
515
+ .sowhat__k{display:none}
516
+ .sowhat--labeled .sowhat__k{display:inline;font-size:var(--fs-xs);font-weight:700;letter-spacing:.1em;
517
+ text-transform:uppercase;color:var(--accent-text);white-space:nowrap;flex:none;padding-top:.15em}
518
+ .sowhat__v,.sowhat>:not(.sowhat__k){font-size:var(--fs-h3);color:var(--text);font-weight:var(--fw-title);
519
+ line-height:1.55;flex:1;min-width:0}
520
+ .sowhat__v p,.sowhat__stack{margin:0}
521
+ .sowhat__stack{display:flex;flex-direction:column;gap:var(--sp-2)}
522
+
507
523
  .note{display:flex;gap:var(--sp-4);padding:clamp(20px,2vw,28px);border-radius:var(--r-lg);
508
524
  background:var(--accent-soft);border-left:3px solid var(--accent)}
509
525
  .note__ico{width:26px;height:26px;flex:none;border-radius:50%;background:var(--accent);
@@ -1542,7 +1558,7 @@ if(e.isIntersecting){e.target.classList.add('in');o.unobserve(e.target)}})},
1542
1558
  var PROMPT=[
1543
1559
  '请为这份 HTML 报告生成可编辑 PPTX 并交付:',
1544
1560
  '1) 阅读 references/pptx-export.md 与 references/modes.md(三模式独立规则);',
1545
- '2) 核对/补全 window.REPORT_MODEL 为与正文严格一致的内容(含 mode/style/theme/title/agenda/sections/closing;theme 为 light 或 dark、与页面 data-theme 一致——PPTX 按此导出亮色版或深色版;29 种页型字段与 30 类 chart.type 见 scripts/model-schema.json;research 关键图表页用 type:"exhibit" 并填 exhibitNo/soWhat/footnote);',
1561
+ '2) 核对/补全 window.REPORT_MODEL 为与正文严格一致的内容(含 mode/style/theme/title/agenda/sections/closing;theme 为 light 或 dark、与页面 data-theme 一致——PPTX 按此导出亮色版或深色版;29 种页型字段与 30 类 chart.type 见 scripts/model-schema.json;research 关键图表页用 type:"exhibit" 并填 exhibitNo/soWhat(结论条)/footnote);',
1546
1562
  '3) 运行 python scripts/validate_report.py <报告.html> 直到全 PASS(含模型一致性检查);',
1547
1563
  '4) 智能体精导并质检(0/0 才交付):python scripts/extract_model.py <报告.html> → NODE_PATH=<pptxgenjs 所在 node_modules> node scripts/build_pptx.js <报告.pptx> --model=<报告.model.json>(如需覆盖模型主题可加 --theme=light|dark) → python scripts/validate_pptx.py <报告.pptx> --strict --model=<报告.model.json>。',
1548
1564
  '报告文件:<请填写报告.html 的路径>'
@@ -1976,7 +1992,7 @@ window.REPORT_MODEL = {
1976
1992
 
1977
1993
  /* __TOPPPT_RUNTIME_START__ */
1978
1994
  /* ══ PPTX 导出运行时(assets/pptx-export.js 的内联副本 · 由 scripts/sync_runtime.py 注入,禁止手改) ══ */
1979
- /* __TOPPPT_RUNTIME_SHA__:77ca318e7a510e01 */
1995
+ /* __TOPPPT_RUNTIME_SHA__:f4dc2ab366ea79c4 */
1980
1996
  /* ═══════════════════════════════════════════════════════════════════════════
1981
1997
  * TopPPT HTML· 浏览器端 PPTX 预览运行时
1982
1998
  * 零依赖、随报告单文件分发;OOXML 序列化器(仅供预览,页面不导出文件)。
@@ -2057,7 +2073,7 @@ var PRESETS_DARK = {
2057
2073
  var STYLE_DATA_COLORS = {"business-blue":["1a73e8","3526bb","21a199","b77148","99983d"],"apple-mono":["1d1d1f","0071e3","0f7b6c","c2410c","7c3aed"],"mckinsey":["003a70","2826bb","21a18e","b76848","99903d"],"brand-red":["d0021b","bb6326","d634ae","40a182","438aa8"],"warm-sand":["96681f","839324","cf3b3e","4d88b2","4a47a4"],"deep-teal":["0f6b5c","2683bb","22aa3e","b74870","a85c43"],"graphite-dark":["0369a1","2637bb","1f9977","b75948","a18a40"],"indigo-violet":["5b5bd6","7432ae","4197c8","a08f4b","7ca04b"],"spectrum":["4563ef","5b26bb","269ebb","af8746","83993d"]};
2058
2074
  var STYLE_DATA_COLORS_DARK = {"business-blue":["8ab4f8","7966e0","88e7e5","d4ad91","cbce83"],"apple-mono":["f5f5f7","2997ff","2dd4bf","ff9f0a","bf5af2"],"mckinsey":["7fb2e5","6f6bdb","8ce3d8","d1a694","cbc586"],"brand-red":["ff5a66","e09e66","e788cb","91d4c2","83b4ce"],"warm-sand":["d9b36a","c2d571","df9391","97b5ce","8e89c7"],"deep-teal":["4fc3b0","7aaecd","97d8a4","c99cac","c29c8f"],"graphite-dark":["4da3ff","6c66e0","88e7dc","d4a691","cec983"],"indigo-violet":["a5a4f0","a967e0","89c3e7","d3c792","aece83"],"spectrum":["8ba0f8","9066e0","88d6e7","d4ba91","bdce83"]};
2059
2075
  var GRID = {"columns":12,"gutter":0.16,"colW":0.8644,"x":[0.6,1.6244,2.6488,3.6732,4.6976,5.722,6.7464,7.7708,8.7952,9.8196,10.844,11.8684,12.733],"y":[2.35,3.27,4.19,5.11,6.03,6.9],"safe":{"top":0.4,"bottom":0.4,"left":0.6,"right":0.6}};
2060
- var TYPO = {"levels":[{"id":"C0","name":"封面/章节幕标题","role":"coverTitle","usage":"封面主标题、章节幕标题"},{"id":"T1","name":"页码/章节徽章","role":"caption","usage":"页码徽章、章节编号"},{"id":"T2","name":"页面主标题/结论标题","role":"h1","usage":"每页顶部结论句(research 行动标题)"},{"id":"T3","name":"副标题/语境说明","role":"lead","usage":"主标题下方导语"},{"id":"T4","name":"模块标题/图表标题","role":"h2","usage":"卡片标题、图表标题、面板标题"},{"id":"T5","name":"证据编号/轻量标签","role":"micro","usage":"Exhibit 编号、状态标签"},{"id":"T6","name":"证据块标题/小节标题","role":"h2","usage":"分栏小标题、证据块标题"},{"id":"T7","name":"正文解释段落","role":"body","usage":"证据解释、管理解读正文、表格正文"},{"id":"T8","name":"结论条文字","role":"body","usage":"so-what 结论条、核心结论框"},{"id":"T9","name":"SO WHAT 标签","role":"caption","usage":"so-what 标签行"},{"id":"T10","name":"SO WHAT 正文/业务含义","role":"body","usage":"so-what 正文、行动含义、表格行动项"},{"id":"T11","name":"图表轴/图例/刻度/微标签","role":"micro","usage":"坐标轴、图例、刻度、单位(禁用于表格正文与完整短句)"},{"id":"T12","name":"图表数据标签","role":"caption","usage":"折线点值、柱形标签、百分比标注"},{"id":"T13","name":"关键 KPI 大数字","role":"h1","usage":"KPI 大数字、hero 数值"},{"id":"T14","name":"注释/口径/来源/页脚","role":"micro","usage":"footnote、来源行、页脚、小页码"}],"tableSemanticRule":{"body":["T7","T10"],"microAllowed":["T11"],"microForbiddenFor":["表格正文","行动项","风险项","解释句","建议句","长项目符号","完整短句"]}};
2076
+ var TYPO = {"levels":[{"id":"C0","name":"封面/章节幕标题","role":"coverTitle","usage":"封面主标题、章节幕标题"},{"id":"T1","name":"页码/章节徽章","role":"caption","usage":"页码徽章、章节编号"},{"id":"T2","name":"页面主标题/结论标题","role":"h1","usage":"每页顶部结论句(research 行动标题)"},{"id":"T3","name":"副标题/语境说明","role":"lead","usage":"主标题下方导语"},{"id":"T4","name":"模块标题/图表标题","role":"h2","usage":"卡片标题、图表标题、面板标题"},{"id":"T5","name":"证据编号/轻量标签","role":"micro","usage":"Exhibit 编号、状态标签"},{"id":"T6","name":"证据块标题/小节标题","role":"h2","usage":"分栏小标题、证据块标题"},{"id":"T7","name":"正文解释段落","role":"body","usage":"证据解释、管理解读正文、表格正文"},{"id":"T8","name":"结论条文字","role":"body","usage":"结论条、核心结论框"},{"id":"T9","name":"结论条标签(可选·默认隐藏)","role":"caption","usage":"结论条可选标签(默认不显示)"},{"id":"T10","name":"结论条正文/业务含义","role":"body","usage":"结论条正文、行动含义、表格行动项"},{"id":"T11","name":"图表轴/图例/刻度/微标签","role":"micro","usage":"坐标轴、图例、刻度、单位(禁用于表格正文与完整短句)"},{"id":"T12","name":"图表数据标签","role":"caption","usage":"折线点值、柱形标签、百分比标注"},{"id":"T13","name":"关键 KPI 大数字","role":"h1","usage":"KPI 大数字、hero 数值"},{"id":"T14","name":"注释/口径/来源/页脚","role":"micro","usage":"footnote、来源行、页脚、小页码"}],"tableSemanticRule":{"body":["T7","T10"],"microAllowed":["T11"],"microForbiddenFor":["表格正文","行动项","风险项","解释句","建议句","长项目符号","完整短句"]}};
2061
2077
  var CONT = {"pad":{"card":0.18,"panel":0.2,"cell":0.08,"band":0.16,"soWhat":0.16,"kpi":0.18,"chart":0.1,"head":0.05,"footer":0.05,"node":0.06},"minPad":0.06,"textMetrics":{"lineFactor":1.45,"emAsciiRatio":0.52},"overflowRule":"溢出处置优先级(禁静默截断):① 重构承载(列表/卡片/表/图/组合版式)→ ② 拆页/分章(议程→主张→证据卡→明细;01a/01b;信息须完整)→ ③ 换布局形态(V1–V4/多列/卡栅格)→ ④ 有限 fontShrink(阶梯内、模式 floor 以上、最多 4 档)→ ⑤ 禁止:静默截断、砍 so-what/证据/口径、为审美稀疏删决策必需信息。单页字数预算是预警不是删字许可证。","fontShrink":{"ladder":[15,14,13.5,13,12.5,12,11.5,11,10.5,10,9.5,9,8.5],"maxShrinkSteps":4,"floorPt":{"presentation":10,"research":9,"architecture":10},"preferContentFirst":true,"warnBelow":9.5},"continuousTextRule":"语义连续的一句话 / so-what 主句 / 结论句 / 表格单元格正文不得拆成多个独立文本框(避免异常空格、断句、基线漂移);局部加粗高亮必须用同一文本框内富文本。"};
2062
2078
  var REG = {"bar":{"html":"svg-bars","pptx":"native","nativeType":"bar","dataTable":"notes","min":{"vbHeightMin":160}},"hbar":{"html":"svg-hbars","pptx":"native","nativeType":"bar","dataTable":"notes","min":{"vbHeightMin":100}},"stack":{"html":"svg-stack","pptx":"native","nativeType":"bar","dataTable":"notes","min":{"vbHeightMin":160},"nativeOpt":{"barGrouping":"stacked"}},"stackline":{"html":"svg-stackline","pptx":"native","nativeType":"bar","dataTable":"notes","min":{"vbHeightMin":40},"nativeOpt":{"barGrouping":"percentStacked"}},"line":{"html":"svg-line","pptx":"native","nativeType":"line","dataTable":"notes","min":{"vbHeightMin":160}},"dualline":{"html":"svg-dualline","pptx":"native","nativeType":"line","dataTable":"notes","min":{"vbHeightMin":160}},"area":{"html":"svg-area","pptx":"native","nativeType":"area","dataTable":"notes","min":{"vbHeightMin":160}},"donut":{"html":"svg-donut","pptx":"native","nativeType":"doughnut","dataTable":"notes","min":{"pxWidthMin":140}},"multidonut":{"html":"svg-mdonut","pptx":"native","nativeType":"doughnut","dataTable":"notes","min":{"pxWidthMin":140}},"pie":{"html":"svg-pie","pptx":"native","nativeType":"pie","dataTable":"notes","min":{"pxWidthMin":140}},"radar":{"html":"svg-radar","pptx":"native","nativeType":"radar","dataTable":"notes","min":{"pxWidthMin":220}},"scatter":{"html":"svg-scatter","pptx":"native","nativeType":"scatter","dataTable":"notes","min":{"vbHeightMin":180}},"bubble":{"html":"svg-bubble","pptx":"native","nativeType":"bubble","dataTable":"notes","min":{"vbHeightMin":180}},"waterfall":{"html":"svg-wf","pptx":"native","nativeType":"bar","dataTable":"notes","min":{"vbHeightMin":170},"nativeTrick":"stacked+hiddenBase+connector"},"gauge":{"html":"svg-gauge","pptx":"native","nativeType":"doughnut","dataTable":"notes","min":{"pxWidthMin":180},"nativeTrick":"doughnut+firstSliceAng+hiddenRemainder"},"pareto":{"html":"svg-pareto","pptx":"native","nativeType":"bar","dataTable":"notes","min":{"vbHeightMin":180},"nativeTrick":"multiType+bar+line"},"gantt":{"html":"svg-gantt","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":130},"path":{"forbidPresetShapes":true}},"vsbar":{"html":"svg-vsbar","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":100},"path":{"forbidPresetShapes":true}},"progress":{"html":"svg-progress","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":100},"path":{"forbidPresetShapes":true}},"sparkline":{"html":"svg-spark","pptx":"shape","dataTable":"off","min":{"vbHeightMin":20},"path":{"forbidPresetShapes":true}},"funnel":{"html":"svg-funnel","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":160},"path":{"minSamplePoints":8,"forbidPresetShapes":true}},"treemap":{"html":"svg-treemap","pptx":"shape","dataTable":"appendix","min":{"vbHeightMin":170},"path":{"forbidPresetShapes":true}},"marimekko":{"html":"svg-marimekko","pptx":"shape","dataTable":"appendix","min":{"vbHeightMin":160},"path":{"forbidPresetShapes":true}},"boxplot":{"html":"svg-boxplot","pptx":"shape","dataTable":"appendix","min":{"vbHeightMin":180},"path":{"forbidPresetShapes":true}},"network":{"html":"svg-network","pptx":"shape","dataTable":"appendix","min":{"vbHeightMin":200},"path":{"forbidPresetShapes":true}},"sankey":{"html":"svg-sankey","pptx":"shape","dataTable":"appendix","min":{"vbHeightMin":200},"path":{"minSamplePoints":16,"trackBothEdges":true,"forbidPresetShapes":true}},"streamgraph":{"html":"svg-stream","pptx":"shape","dataTable":"appendix","min":{"vbHeightMin":160},"path":{"minSamplePoints":24,"trackBothEdges":true,"forbidPresetShapes":true}},"slope":{"html":"svg-slope","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":180},"path":{"forbidPresetShapes":true}},"dumbbell":{"html":"svg-dumbbell","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":160},"path":{"forbidPresetShapes":true}},"lollipop":{"html":"svg-lollipop","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":140},"path":{"forbidPresetShapes":true}},"dotplot":{"html":"svg-dotplot","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":120},"path":{"forbidPresetShapes":true}},"bulletchart":{"html":"svg-bullet","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":140},"path":{"forbidPresetShapes":true}},"waffle":{"html":"svg-waffle","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":160},"path":{"forbidPresetShapes":true}},"radialbar":{"html":"svg-radialbar","pptx":"shape","dataTable":"notes","min":{"pxWidthMin":200},"path":{"minSamplePoints":16,"forbidPresetShapes":true}},"rose":{"html":"svg-rose","pptx":"shape","dataTable":"notes","min":{"pxWidthMin":180},"path":{"minSamplePoints":16,"forbidPresetShapes":true}},"candlestick":{"html":"svg-candle","pptx":"shape","dataTable":"appendix","min":{"vbHeightMin":180},"path":{"forbidPresetShapes":true}}};
2063
2079
  var DEEP = {"triggers":["高保真","1:1","一比一","精确还原","按图还原","正式交付","不能偏移","像素级","严格对照","逐页验收"],"complexCharts":["sankey","streamgraph","treemap","marimekko","boxplot","network","rose","radialbar","candlestick","funnel"],"checks":{"anchors":true,"containerOverflow":true,"renderCompare":true,"manifest":true},"renderCompare":{"engine":"soffice","fallback":"powerpoint-com","skipIfMissing":true,"dpi":96}};
@@ -2308,7 +2324,7 @@ function fitRowH(avail, n, pref, floor) {
2308
2324
  function chartBottom(hasSoWhat, hasFootnote) {
2309
2325
  /* D8: tightest of soWhat/footnote/contentBottomWithNote — no short-circuit */
2310
2326
  var bot = CONTENT_BOTTOM;
2311
- if (hasSoWhat) bot = Math.min(bot, PT.exhibit.soWhatY - 0.12);
2327
+ if (hasSoWhat) bot = Math.min(bot, PT.exhibit.soWhatY - 0.20);
2312
2328
  if (hasFootnote) bot = Math.min(bot, PT.exhibit.footnoteY - 0.12);
2313
2329
  if (hasSoWhat || hasFootnote) bot = Math.min(bot, CONTENT_BOTTOM_NOTE);
2314
2330
  return bot;
@@ -2392,24 +2408,31 @@ function donutWedges(labels, values) {
2392
2408
  });
2393
2409
  return segs;
2394
2410
  }
2395
- function donutChart(sh, ec, S, dcols) {
2411
+ function donutChart(sh, ec, S, dcols, bottomY) {
2396
2412
  var d = PT.donut, labels = ec.labels || [], values = ec.values || [];
2397
2413
  var segs = donutWedges(labels, values);
2398
- var bx = d.centerX - d.r, by = d.centerY - d.r, bw = d.r * 2;
2414
+ var r = d.r;
2415
+ if (bottomY != null && d.centerY + r > bottomY) {
2416
+ r = Math.max(0.9, bottomY - d.centerY - 0.04);
2417
+ }
2418
+ var holeR = d.holeR * (r / d.r);
2419
+ var bx = d.centerX - r, by = d.centerY - r, bw = r * 2;
2399
2420
  segs.forEach(function (sg) {
2400
2421
  sh.push(shape('pie', bx, by, bw, bw, dcols[sg[2] % dcols.length], { av: pieAv(sg[0], sg[1]) }));
2401
2422
  });
2402
- var hb = d.holeR * 2;
2403
- sh.push(shape('ellipse', d.centerX - d.holeR, d.centerY - d.holeR, hb, hb, S.bg));
2423
+ var hb = holeR * 2;
2424
+ sh.push(shape('ellipse', d.centerX - holeR, d.centerY - holeR, hb, hb, S.bg));
2404
2425
  var total = 0; values.forEach(function (v) { total += Math.max(0, v); });
2405
2426
  var totalStr = (Math.round(total * 10) / 10) + (ec.unit || '');
2406
- sh.push(txSp(d.centerX - d.holeR, d.centerY - 0.42, hb, 0.5,
2427
+ sh.push(txSp(d.centerX - holeR, d.centerY - 0.42, hb, 0.5,
2407
2428
  [[{ t: totalStr, sz: 20, b: 1, col: S.ink, font: S.fontDisplay }]], { align: 'ctr', anchor: 'ctr' }));
2408
- sh.push(txSp(d.centerX - d.holeR, d.centerY + 0.08, hb, 0.35,
2429
+ sh.push(txSp(d.centerX - holeR, d.centerY + 0.08, hb, 0.35,
2409
2430
  [[{ t: ec.centerLabel || '合计', sz: 9.5, col: S.faint, font: S.font }]], { align: 'ctr', anchor: 'ctr' }));
2410
2431
  var n = Math.max(1, labels.length);
2411
- var rowH = Math.min(d.legendRowH, 3.4 / n);
2432
+ var maxLegH = (bottomY != null) ? Math.max(1.2, bottomY - (d.centerY - 1.7)) : 3.4;
2433
+ var rowH = Math.min(d.legendRowH, maxLegH / n, 3.4 / n);
2412
2434
  var ly0 = d.centerY - (n * rowH) / 2 + 0.1;
2435
+ if (bottomY != null) ly0 = Math.min(ly0, bottomY - n * rowH - 0.04);
2413
2436
  var sum2 = total || 1;
2414
2437
  labels.forEach(function (lb, i) {
2415
2438
  var v = values[i] || 0;
@@ -2466,14 +2489,13 @@ function table(x, y, w, colW, rows, S, opts) {
2466
2489
  function soWhatBar(sh, text, S) {
2467
2490
  var R = (typeof regionOf === 'function') ? regionOf('exhibit', 'annotation')
2468
2491
  : { x: MX, y: PT.exhibit.soWhatY, w: CW, h: 0.62 };
2469
- /* 与 B 通道同算法:长结论条有限缩字号,优先保容器不溢出 */
2470
- var bodyW = Math.max(2.5, R.w - 0.44 - 1.35);
2471
- var fzB = fitFont([String(text || '')], bodyW, R.h - 0.14, { max: 12.5, gapFactor: 0.2, maxShrinkSteps: 3 });
2492
+ /* MD3 结论条:衬底 + 左 accent 轨 + 正文(不绘制「SO WHAT」标签字样) */
2493
+ var bodyW = Math.max(2.5, R.w - 0.44);
2494
+ var fzB = fitFont([String(text || '')], bodyW, R.h - 0.14, { max: 14, gapFactor: 0.2, maxShrinkSteps: 3 });
2472
2495
  sh.push(shape('rect', R.x, R.y, R.w, R.h, S.soft));
2473
2496
  sh.push(shape('rect', R.x, R.y, 0.06, R.h, S.accent));
2474
2497
  sh.push(txSp(R.x + 0.22, R.y + 0.06, R.w - 0.44, R.h - 0.12,
2475
- [[{ t: 'SO WHAT ', sz: 11, b: 1, col: S.accent, font: S.font, spc: 150 },
2476
- { t: text, sz: fzB, col: S.ink, font: S.font }]], { anchor: 'ctr' }));
2498
+ [[{ t: String(text || ''), sz: fzB, b: 1, col: S.ink, font: S.font }]], { anchor: 'ctr' }));
2477
2499
  }
2478
2500
  function footnoteLine(sh, text, S) {
2479
2501
  var R = (typeof regionOf === 'function') ? regionOf('exhibit', 'footnote')
@@ -3333,7 +3355,7 @@ function slidesOf(model, S) {
3333
3355
  if (img.caption) sh.push(txSp(MX, capYImg + 0.04, CW, capH2, [[{ t: img.caption, sz: 10.5, col: S.faint, font: S.font }]]));
3334
3356
  } else if (type === 'donut') {
3335
3357
  var dcn = sec.chart || {};
3336
- if (dcn.labels && dcn.values) donutChart(sh, dcn, S, dcn.colors || donutColors(model.style, S, model.theme));
3358
+ if (dcn.labels && dcn.values) donutChart(sh, dcn, S, dcn.colors || donutColors(model.style, S, model.theme), bodyBottom);
3337
3359
  if (sec.note) sh.push(txSp(MX, PT.note.y, CW, PT.note.h, [[{ t: sec.note, sz: 11, col: S.faint, font: S.font }]]));
3338
3360
  } else if (type === 'heatmap') {
3339
3361
  var H = PT.heatmap;
@@ -3486,10 +3508,27 @@ function slidesOf(model, S) {
3486
3508
  var bRR = regOf('bar', isHBar ? 'hbar' : 'primary', { bottom: botY }) ||
3487
3509
  { x: isHBar ? MX : MX + PT.bar.chartX, y: isHBar ? PT.bar.hbarY0 : PT.bar.chartY,
3488
3510
  w: isHBar ? CW : CW - PT.bar.chartW, h: 3 };
3511
+ var ptsB = (sec.points || []).filter(Boolean);
3512
+ var bx = bRR.x, bw = bRR.w;
3513
+ if (ptsB.length && !isHBar) {
3514
+ var sideW = Math.min(3.6, Math.max(2.6, bRR.w * 0.30));
3515
+ bw = Math.max(3.2, bRR.w - sideW - 0.28);
3516
+ var sx = bRR.x + bw + 0.28;
3517
+ var rowHb = Math.min(0.72, (bRR.h - 0.1) / Math.max(1, ptsB.length));
3518
+ ptsB.slice(0, 6).forEach(function (p, i) {
3519
+ var k = Array.isArray(p) ? String(p[0] || '') : String((p && p.t) || '');
3520
+ var v = Array.isArray(p) ? String(p[1] || '') : String((p && p.d) || '');
3521
+ var y = bRR.y + i * rowHb;
3522
+ sh.push(shape('rect', sx, y + 0.12, 0.08, 0.08, S.accent));
3523
+ sh.push(txSp(sx + 0.2, y, sideW - 0.25, rowHb,
3524
+ [[{ t: k + (v ? ' ' : ''), sz: 12, b: 1, col: S.ink, font: S.font },
3525
+ { t: v, sz: 11, col: S.body, font: S.font }]], { anchor: 'ctr' }));
3526
+ });
3527
+ }
3489
3528
  if (isHBar) {
3490
- chartBlockShapes(sh, ch, S, bRR.x, bRR.y, bRR.w, bRR.h, dcols2);
3529
+ chartBlockShapes(sh, ch, S, bx, bRR.y, bw, bRR.h, dcols2);
3491
3530
  } else {
3492
- chartBlockShapes(sh, ch, S, bRR.x, bRR.y, bRR.w, Math.max(1.5, bRR.h), dcols2);
3531
+ chartBlockShapes(sh, ch, S, bx, bRR.y, bw, Math.max(1.5, bRR.h), dcols2);
3493
3532
  }
3494
3533
  if (sec.note) sh.push(txSp(MX, PT.note.y, CW, PT.note.h, [[{ t: sec.note, sz: 11, col: S.faint, font: S.font }]]));
3495
3534
  } else if (type === 'twocol' || type === 'threecol') {
@@ -261,7 +261,8 @@ section.band{scroll-snap-align:start}
261
261
  svh 兼容移动端浏览器工具栏;旧浏览器落到 100vh。 */
262
262
  .band{padding-block:var(--band-pad);min-height:var(--band-min);
263
263
  display:flex;flex-direction:column;justify-content:center}
264
- .band>.wrap{width:100%}
264
+ .band>.wrap{width:100%;min-height:0}
265
+ .band>.wrap>.grid,.band>.wrap>.stack,.band>.wrap>.fig,.band>.wrap>.cols-2,.band>.wrap>.cols-3,.band>.wrap>.exhibit{min-height:0}
265
266
  .band--top{justify-content:flex-start} /* 内容约六七成屏:贴顶 + 底部自然留白 */
266
267
  .band--fit{justify-content:center} /* 内容接近一屏:垂直居中(默认行为,保留以兼容旧报告) */
267
268
  .band--flow{min-height:0;justify-content:flex-start} /* 长内容页(附录/全量参考)显式退出固定页高 */
@@ -502,6 +503,21 @@ tbody tr:hover{background:var(--surface-1)}
502
503
  td strong{color:var(--text);font-weight:600}
503
504
  td.k{color:var(--text);font-weight:500;white-space:nowrap}
504
505
  td.num{font-variant-numeric:tabular-nums}
506
+ /* ── 结论条(MD3 tonal strip · 本页重点收口;默认不显示「So what」标签) ──
507
+ 与上方主内容呼吸间距 ≥ --sp-6;flex:none 防止被主内容挤压;一屏页不得压进注释带。 */
508
+ .band>.wrap:has(>.sowhat),.band>.wrap:has(>.note){display:flex;flex-direction:column}
509
+ .sowhat{display:flex;align-items:flex-start;gap:var(--sp-3);flex:none;
510
+ margin-top:clamp(28px,3.6vh,48px); /* ≥ --sp-6 量级,修「贴底」 */
511
+ padding:var(--sp-4) var(--sp-5);background:var(--accent-soft);border:1px solid var(--accent-soft-2);
512
+ border-left:4px solid var(--accent);border-radius:var(--r-md);max-width:100%}
513
+ .sowhat__k{display:none}
514
+ .sowhat--labeled .sowhat__k{display:inline;font-size:var(--fs-xs);font-weight:700;letter-spacing:.1em;
515
+ text-transform:uppercase;color:var(--accent-text);white-space:nowrap;flex:none;padding-top:.15em}
516
+ .sowhat__v,.sowhat>:not(.sowhat__k){font-size:var(--fs-h3);color:var(--text);font-weight:var(--fw-title);
517
+ line-height:1.55;flex:1;min-width:0}
518
+ .sowhat__v p,.sowhat__stack{margin:0}
519
+ .sowhat__stack{display:flex;flex-direction:column;gap:var(--sp-2)}
520
+
505
521
  .note{display:flex;gap:var(--sp-4);padding:clamp(20px,2vw,28px);border-radius:var(--r-lg);
506
522
  background:var(--accent-soft);border-left:3px solid var(--accent)}
507
523
  .note__ico{width:26px;height:26px;flex:none;border-radius:50%;background:var(--accent);
@@ -1133,7 +1149,7 @@ background:var(--surface-2);border-radius:6px;padding:2px 7px;color:var(--text)}
1133
1149
  </div>
1134
1150
  <div class="grid g-half rv">
1135
1151
  <div class="card">
1136
- <h3 class="t-h3">堆场景路径 · 现状</h3>
1152
+ <div class="card__hd"><div class="card__ico"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M10.3 3.9L1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0z"/><path d="M12 9v4M12 17h.01"/></svg></div><h3 class="t-h3">堆场景路径 · 现状</h3></div>
1137
1153
  <ul class="ul">
1138
1154
  <li><strong>接入慢。</strong>每场景重复对接工具与权限。</li>
1139
1155
  <li><strong>治理弱。</strong>事后审计,回退靠人工。</li>
@@ -1141,7 +1157,7 @@ background:var(--surface-2);border-radius:6px;padding:2px 7px;color:var(--text)}
1141
1157
  </ul>
1142
1158
  </div>
1143
1159
  <div class="card" style="background:var(--accent-soft);border-color:transparent">
1144
- <h3 class="t-h3" style="color:var(--accent)">平台化路径 · 目标</h3>
1160
+ <div class="card__hd"><div class="card__ico"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6L9 17l-5-5"/></svg></div><h3 class="t-h3" style="color:var(--accent)">平台化路径 · 目标</h3></div>
1145
1161
  <ul class="ul">
1146
1162
  <li><strong>接入快。</strong>统一编排,接入成本降一个量级。</li>
1147
1163
  <li><strong>治理内嵌。</strong>权限、审计、回退进平台层。</li>
@@ -1150,7 +1166,6 @@ background:var(--surface-2);border-radius:6px;padding:2px 7px;color:var(--text)}
1150
1166
  </div>
1151
1167
  </div>
1152
1168
  <div class="sowhat rv">
1153
- <span class="sowhat__k">结论</span>
1154
1169
  <span class="sowhat__v">平台化不是可选项,而是规模化穿越分水岭的唯一共同路径。</span>
1155
1170
  </div>
1156
1171
  </div>
@@ -1382,17 +1397,17 @@ background:var(--surface-2);border-radius:6px;padding:2px 7px;color:var(--text)}
1382
1397
  </div>
1383
1398
  <div class="grid g-3 rv">
1384
1399
  <div class="card">
1385
- <h3 class="t-h3" style="margin-bottom:var(--sp-2)">选场景</h3>
1400
+ <div class="card__hd"><div class="card__ico"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9"/><path d="M14.5 9.5l-2 5-5 2 2-5 5-2z"/></svg></div><h3 class="t-h3" style="margin-bottom:var(--sp-2)">选场景</h3></div>
1386
1401
  <p class="t-body">
1387
1402
  客服 / 研发 / 数据三类高价值场景。</p>
1388
1403
  </div>
1389
1404
  <div class="card">
1390
- <h3 class="t-h3" style="margin-bottom:var(--sp-2)">建平台</h3>
1405
+ <div class="card__hd"><div class="card__ico"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2l9 5-9 5-9-5 9-5z"/><path d="M3 12l9 5 9-5"/><path d="M3 17l9 5 9-5"/></svg></div><h3 class="t-h3" style="margin-bottom:var(--sp-2)">建平台</h3></div>
1391
1406
  <p class="t-body">
1392
1407
  三层解耦 + 统一编排 + 护栏。</p>
1393
1408
  </div>
1394
1409
  <div class="card">
1395
- <h3 class="t-h3" style="margin-bottom:var(--sp-2)">立治理</h3>
1410
+ <div class="card__hd"><div class="card__ico"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3l8 3v6c0 5-3.5 8.5-8 10-4.5-1.5-8-5-8-10V6l8-3z"/></svg></div><h3 class="t-h3" style="margin-bottom:var(--sp-2)">立治理</h3></div>
1396
1411
  <p class="t-body">
1397
1412
  权限、审计、回退全闭环。</p>
1398
1413
  </div>
@@ -1789,7 +1804,7 @@ if(e.isIntersecting){e.target.classList.add('in');o.unobserve(e.target)}})},
1789
1804
  var PROMPT=[
1790
1805
  '请为这份 HTML 报告生成可编辑 PPTX 并交付:',
1791
1806
  '1) 阅读 references/pptx-export.md 与 references/modes.md(三模式独立规则);',
1792
- '2) 核对/补全 window.REPORT_MODEL 为与正文严格一致的内容(含 mode/style/theme/title/agenda/sections/closing;theme 为 light 或 dark、与页面 data-theme 一致——PPTX 按此导出亮色版或深色版;29 种页型字段与 30 类 chart.type 见 scripts/model-schema.json;research 关键图表页用 type:"exhibit" 并填 exhibitNo/soWhat/footnote);',
1807
+ '2) 核对/补全 window.REPORT_MODEL 为与正文严格一致的内容(含 mode/style/theme/title/agenda/sections/closing;theme 为 light 或 dark、与页面 data-theme 一致——PPTX 按此导出亮色版或深色版;29 种页型字段与 30 类 chart.type 见 scripts/model-schema.json;research 关键图表页用 type:"exhibit" 并填 exhibitNo/soWhat(结论条)/footnote);',
1793
1808
  '3) 运行 python scripts/validate_report.py <报告.html> 直到全 PASS(含模型一致性检查);',
1794
1809
  '4) 智能体精导并质检(0/0 才交付):python scripts/extract_model.py <报告.html> → NODE_PATH=<pptxgenjs 所在 node_modules> node scripts/build_pptx.js <报告.pptx> --model=<报告.model.json>(如需覆盖模型主题可加 --theme=light|dark) → python scripts/validate_pptx.py <报告.pptx> --strict --model=<报告.model.json>。',
1795
1810
  '报告文件:<请填写报告.html 的路径>'
@@ -2375,7 +2390,7 @@ window.REPORT_MODEL = {
2375
2390
 
2376
2391
  /* __TOPPPT_RUNTIME_START__ */
2377
2392
  /* ══ PPTX 导出运行时(assets/pptx-export.js 的内联副本 · 由 scripts/sync_runtime.py 注入,禁止手改) ══ */
2378
- /* __TOPPPT_RUNTIME_SHA__:77ca318e7a510e01 */
2393
+ /* __TOPPPT_RUNTIME_SHA__:f4dc2ab366ea79c4 */
2379
2394
  /* ═══════════════════════════════════════════════════════════════════════════
2380
2395
  * TopPPT HTML· 浏览器端 PPTX 预览运行时
2381
2396
  * 零依赖、随报告单文件分发;OOXML 序列化器(仅供预览,页面不导出文件)。
@@ -2456,7 +2471,7 @@ var PRESETS_DARK = {
2456
2471
  var STYLE_DATA_COLORS = {"business-blue":["1a73e8","3526bb","21a199","b77148","99983d"],"apple-mono":["1d1d1f","0071e3","0f7b6c","c2410c","7c3aed"],"mckinsey":["003a70","2826bb","21a18e","b76848","99903d"],"brand-red":["d0021b","bb6326","d634ae","40a182","438aa8"],"warm-sand":["96681f","839324","cf3b3e","4d88b2","4a47a4"],"deep-teal":["0f6b5c","2683bb","22aa3e","b74870","a85c43"],"graphite-dark":["0369a1","2637bb","1f9977","b75948","a18a40"],"indigo-violet":["5b5bd6","7432ae","4197c8","a08f4b","7ca04b"],"spectrum":["4563ef","5b26bb","269ebb","af8746","83993d"]};
2457
2472
  var STYLE_DATA_COLORS_DARK = {"business-blue":["8ab4f8","7966e0","88e7e5","d4ad91","cbce83"],"apple-mono":["f5f5f7","2997ff","2dd4bf","ff9f0a","bf5af2"],"mckinsey":["7fb2e5","6f6bdb","8ce3d8","d1a694","cbc586"],"brand-red":["ff5a66","e09e66","e788cb","91d4c2","83b4ce"],"warm-sand":["d9b36a","c2d571","df9391","97b5ce","8e89c7"],"deep-teal":["4fc3b0","7aaecd","97d8a4","c99cac","c29c8f"],"graphite-dark":["4da3ff","6c66e0","88e7dc","d4a691","cec983"],"indigo-violet":["a5a4f0","a967e0","89c3e7","d3c792","aece83"],"spectrum":["8ba0f8","9066e0","88d6e7","d4ba91","bdce83"]};
2458
2473
  var GRID = {"columns":12,"gutter":0.16,"colW":0.8644,"x":[0.6,1.6244,2.6488,3.6732,4.6976,5.722,6.7464,7.7708,8.7952,9.8196,10.844,11.8684,12.733],"y":[2.35,3.27,4.19,5.11,6.03,6.9],"safe":{"top":0.4,"bottom":0.4,"left":0.6,"right":0.6}};
2459
- var TYPO = {"levels":[{"id":"C0","name":"封面/章节幕标题","role":"coverTitle","usage":"封面主标题、章节幕标题"},{"id":"T1","name":"页码/章节徽章","role":"caption","usage":"页码徽章、章节编号"},{"id":"T2","name":"页面主标题/结论标题","role":"h1","usage":"每页顶部结论句(research 行动标题)"},{"id":"T3","name":"副标题/语境说明","role":"lead","usage":"主标题下方导语"},{"id":"T4","name":"模块标题/图表标题","role":"h2","usage":"卡片标题、图表标题、面板标题"},{"id":"T5","name":"证据编号/轻量标签","role":"micro","usage":"Exhibit 编号、状态标签"},{"id":"T6","name":"证据块标题/小节标题","role":"h2","usage":"分栏小标题、证据块标题"},{"id":"T7","name":"正文解释段落","role":"body","usage":"证据解释、管理解读正文、表格正文"},{"id":"T8","name":"结论条文字","role":"body","usage":"so-what 结论条、核心结论框"},{"id":"T9","name":"SO WHAT 标签","role":"caption","usage":"so-what 标签行"},{"id":"T10","name":"SO WHAT 正文/业务含义","role":"body","usage":"so-what 正文、行动含义、表格行动项"},{"id":"T11","name":"图表轴/图例/刻度/微标签","role":"micro","usage":"坐标轴、图例、刻度、单位(禁用于表格正文与完整短句)"},{"id":"T12","name":"图表数据标签","role":"caption","usage":"折线点值、柱形标签、百分比标注"},{"id":"T13","name":"关键 KPI 大数字","role":"h1","usage":"KPI 大数字、hero 数值"},{"id":"T14","name":"注释/口径/来源/页脚","role":"micro","usage":"footnote、来源行、页脚、小页码"}],"tableSemanticRule":{"body":["T7","T10"],"microAllowed":["T11"],"microForbiddenFor":["表格正文","行动项","风险项","解释句","建议句","长项目符号","完整短句"]}};
2474
+ var TYPO = {"levels":[{"id":"C0","name":"封面/章节幕标题","role":"coverTitle","usage":"封面主标题、章节幕标题"},{"id":"T1","name":"页码/章节徽章","role":"caption","usage":"页码徽章、章节编号"},{"id":"T2","name":"页面主标题/结论标题","role":"h1","usage":"每页顶部结论句(research 行动标题)"},{"id":"T3","name":"副标题/语境说明","role":"lead","usage":"主标题下方导语"},{"id":"T4","name":"模块标题/图表标题","role":"h2","usage":"卡片标题、图表标题、面板标题"},{"id":"T5","name":"证据编号/轻量标签","role":"micro","usage":"Exhibit 编号、状态标签"},{"id":"T6","name":"证据块标题/小节标题","role":"h2","usage":"分栏小标题、证据块标题"},{"id":"T7","name":"正文解释段落","role":"body","usage":"证据解释、管理解读正文、表格正文"},{"id":"T8","name":"结论条文字","role":"body","usage":"结论条、核心结论框"},{"id":"T9","name":"结论条标签(可选·默认隐藏)","role":"caption","usage":"结论条可选标签(默认不显示)"},{"id":"T10","name":"结论条正文/业务含义","role":"body","usage":"结论条正文、行动含义、表格行动项"},{"id":"T11","name":"图表轴/图例/刻度/微标签","role":"micro","usage":"坐标轴、图例、刻度、单位(禁用于表格正文与完整短句)"},{"id":"T12","name":"图表数据标签","role":"caption","usage":"折线点值、柱形标签、百分比标注"},{"id":"T13","name":"关键 KPI 大数字","role":"h1","usage":"KPI 大数字、hero 数值"},{"id":"T14","name":"注释/口径/来源/页脚","role":"micro","usage":"footnote、来源行、页脚、小页码"}],"tableSemanticRule":{"body":["T7","T10"],"microAllowed":["T11"],"microForbiddenFor":["表格正文","行动项","风险项","解释句","建议句","长项目符号","完整短句"]}};
2460
2475
  var CONT = {"pad":{"card":0.18,"panel":0.2,"cell":0.08,"band":0.16,"soWhat":0.16,"kpi":0.18,"chart":0.1,"head":0.05,"footer":0.05,"node":0.06},"minPad":0.06,"textMetrics":{"lineFactor":1.45,"emAsciiRatio":0.52},"overflowRule":"溢出处置优先级(禁静默截断):① 重构承载(列表/卡片/表/图/组合版式)→ ② 拆页/分章(议程→主张→证据卡→明细;01a/01b;信息须完整)→ ③ 换布局形态(V1–V4/多列/卡栅格)→ ④ 有限 fontShrink(阶梯内、模式 floor 以上、最多 4 档)→ ⑤ 禁止:静默截断、砍 so-what/证据/口径、为审美稀疏删决策必需信息。单页字数预算是预警不是删字许可证。","fontShrink":{"ladder":[15,14,13.5,13,12.5,12,11.5,11,10.5,10,9.5,9,8.5],"maxShrinkSteps":4,"floorPt":{"presentation":10,"research":9,"architecture":10},"preferContentFirst":true,"warnBelow":9.5},"continuousTextRule":"语义连续的一句话 / so-what 主句 / 结论句 / 表格单元格正文不得拆成多个独立文本框(避免异常空格、断句、基线漂移);局部加粗高亮必须用同一文本框内富文本。"};
2461
2476
  var REG = {"bar":{"html":"svg-bars","pptx":"native","nativeType":"bar","dataTable":"notes","min":{"vbHeightMin":160}},"hbar":{"html":"svg-hbars","pptx":"native","nativeType":"bar","dataTable":"notes","min":{"vbHeightMin":100}},"stack":{"html":"svg-stack","pptx":"native","nativeType":"bar","dataTable":"notes","min":{"vbHeightMin":160},"nativeOpt":{"barGrouping":"stacked"}},"stackline":{"html":"svg-stackline","pptx":"native","nativeType":"bar","dataTable":"notes","min":{"vbHeightMin":40},"nativeOpt":{"barGrouping":"percentStacked"}},"line":{"html":"svg-line","pptx":"native","nativeType":"line","dataTable":"notes","min":{"vbHeightMin":160}},"dualline":{"html":"svg-dualline","pptx":"native","nativeType":"line","dataTable":"notes","min":{"vbHeightMin":160}},"area":{"html":"svg-area","pptx":"native","nativeType":"area","dataTable":"notes","min":{"vbHeightMin":160}},"donut":{"html":"svg-donut","pptx":"native","nativeType":"doughnut","dataTable":"notes","min":{"pxWidthMin":140}},"multidonut":{"html":"svg-mdonut","pptx":"native","nativeType":"doughnut","dataTable":"notes","min":{"pxWidthMin":140}},"pie":{"html":"svg-pie","pptx":"native","nativeType":"pie","dataTable":"notes","min":{"pxWidthMin":140}},"radar":{"html":"svg-radar","pptx":"native","nativeType":"radar","dataTable":"notes","min":{"pxWidthMin":220}},"scatter":{"html":"svg-scatter","pptx":"native","nativeType":"scatter","dataTable":"notes","min":{"vbHeightMin":180}},"bubble":{"html":"svg-bubble","pptx":"native","nativeType":"bubble","dataTable":"notes","min":{"vbHeightMin":180}},"waterfall":{"html":"svg-wf","pptx":"native","nativeType":"bar","dataTable":"notes","min":{"vbHeightMin":170},"nativeTrick":"stacked+hiddenBase+connector"},"gauge":{"html":"svg-gauge","pptx":"native","nativeType":"doughnut","dataTable":"notes","min":{"pxWidthMin":180},"nativeTrick":"doughnut+firstSliceAng+hiddenRemainder"},"pareto":{"html":"svg-pareto","pptx":"native","nativeType":"bar","dataTable":"notes","min":{"vbHeightMin":180},"nativeTrick":"multiType+bar+line"},"gantt":{"html":"svg-gantt","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":130},"path":{"forbidPresetShapes":true}},"vsbar":{"html":"svg-vsbar","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":100},"path":{"forbidPresetShapes":true}},"progress":{"html":"svg-progress","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":100},"path":{"forbidPresetShapes":true}},"sparkline":{"html":"svg-spark","pptx":"shape","dataTable":"off","min":{"vbHeightMin":20},"path":{"forbidPresetShapes":true}},"funnel":{"html":"svg-funnel","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":160},"path":{"minSamplePoints":8,"forbidPresetShapes":true}},"treemap":{"html":"svg-treemap","pptx":"shape","dataTable":"appendix","min":{"vbHeightMin":170},"path":{"forbidPresetShapes":true}},"marimekko":{"html":"svg-marimekko","pptx":"shape","dataTable":"appendix","min":{"vbHeightMin":160},"path":{"forbidPresetShapes":true}},"boxplot":{"html":"svg-boxplot","pptx":"shape","dataTable":"appendix","min":{"vbHeightMin":180},"path":{"forbidPresetShapes":true}},"network":{"html":"svg-network","pptx":"shape","dataTable":"appendix","min":{"vbHeightMin":200},"path":{"forbidPresetShapes":true}},"sankey":{"html":"svg-sankey","pptx":"shape","dataTable":"appendix","min":{"vbHeightMin":200},"path":{"minSamplePoints":16,"trackBothEdges":true,"forbidPresetShapes":true}},"streamgraph":{"html":"svg-stream","pptx":"shape","dataTable":"appendix","min":{"vbHeightMin":160},"path":{"minSamplePoints":24,"trackBothEdges":true,"forbidPresetShapes":true}},"slope":{"html":"svg-slope","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":180},"path":{"forbidPresetShapes":true}},"dumbbell":{"html":"svg-dumbbell","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":160},"path":{"forbidPresetShapes":true}},"lollipop":{"html":"svg-lollipop","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":140},"path":{"forbidPresetShapes":true}},"dotplot":{"html":"svg-dotplot","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":120},"path":{"forbidPresetShapes":true}},"bulletchart":{"html":"svg-bullet","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":140},"path":{"forbidPresetShapes":true}},"waffle":{"html":"svg-waffle","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":160},"path":{"forbidPresetShapes":true}},"radialbar":{"html":"svg-radialbar","pptx":"shape","dataTable":"notes","min":{"pxWidthMin":200},"path":{"minSamplePoints":16,"forbidPresetShapes":true}},"rose":{"html":"svg-rose","pptx":"shape","dataTable":"notes","min":{"pxWidthMin":180},"path":{"minSamplePoints":16,"forbidPresetShapes":true}},"candlestick":{"html":"svg-candle","pptx":"shape","dataTable":"appendix","min":{"vbHeightMin":180},"path":{"forbidPresetShapes":true}}};
2462
2477
  var DEEP = {"triggers":["高保真","1:1","一比一","精确还原","按图还原","正式交付","不能偏移","像素级","严格对照","逐页验收"],"complexCharts":["sankey","streamgraph","treemap","marimekko","boxplot","network","rose","radialbar","candlestick","funnel"],"checks":{"anchors":true,"containerOverflow":true,"renderCompare":true,"manifest":true},"renderCompare":{"engine":"soffice","fallback":"powerpoint-com","skipIfMissing":true,"dpi":96}};
@@ -2707,7 +2722,7 @@ function fitRowH(avail, n, pref, floor) {
2707
2722
  function chartBottom(hasSoWhat, hasFootnote) {
2708
2723
  /* D8: tightest of soWhat/footnote/contentBottomWithNote — no short-circuit */
2709
2724
  var bot = CONTENT_BOTTOM;
2710
- if (hasSoWhat) bot = Math.min(bot, PT.exhibit.soWhatY - 0.12);
2725
+ if (hasSoWhat) bot = Math.min(bot, PT.exhibit.soWhatY - 0.20);
2711
2726
  if (hasFootnote) bot = Math.min(bot, PT.exhibit.footnoteY - 0.12);
2712
2727
  if (hasSoWhat || hasFootnote) bot = Math.min(bot, CONTENT_BOTTOM_NOTE);
2713
2728
  return bot;
@@ -2791,24 +2806,31 @@ function donutWedges(labels, values) {
2791
2806
  });
2792
2807
  return segs;
2793
2808
  }
2794
- function donutChart(sh, ec, S, dcols) {
2809
+ function donutChart(sh, ec, S, dcols, bottomY) {
2795
2810
  var d = PT.donut, labels = ec.labels || [], values = ec.values || [];
2796
2811
  var segs = donutWedges(labels, values);
2797
- var bx = d.centerX - d.r, by = d.centerY - d.r, bw = d.r * 2;
2812
+ var r = d.r;
2813
+ if (bottomY != null && d.centerY + r > bottomY) {
2814
+ r = Math.max(0.9, bottomY - d.centerY - 0.04);
2815
+ }
2816
+ var holeR = d.holeR * (r / d.r);
2817
+ var bx = d.centerX - r, by = d.centerY - r, bw = r * 2;
2798
2818
  segs.forEach(function (sg) {
2799
2819
  sh.push(shape('pie', bx, by, bw, bw, dcols[sg[2] % dcols.length], { av: pieAv(sg[0], sg[1]) }));
2800
2820
  });
2801
- var hb = d.holeR * 2;
2802
- sh.push(shape('ellipse', d.centerX - d.holeR, d.centerY - d.holeR, hb, hb, S.bg));
2821
+ var hb = holeR * 2;
2822
+ sh.push(shape('ellipse', d.centerX - holeR, d.centerY - holeR, hb, hb, S.bg));
2803
2823
  var total = 0; values.forEach(function (v) { total += Math.max(0, v); });
2804
2824
  var totalStr = (Math.round(total * 10) / 10) + (ec.unit || '');
2805
- sh.push(txSp(d.centerX - d.holeR, d.centerY - 0.42, hb, 0.5,
2825
+ sh.push(txSp(d.centerX - holeR, d.centerY - 0.42, hb, 0.5,
2806
2826
  [[{ t: totalStr, sz: 20, b: 1, col: S.ink, font: S.fontDisplay }]], { align: 'ctr', anchor: 'ctr' }));
2807
- sh.push(txSp(d.centerX - d.holeR, d.centerY + 0.08, hb, 0.35,
2827
+ sh.push(txSp(d.centerX - holeR, d.centerY + 0.08, hb, 0.35,
2808
2828
  [[{ t: ec.centerLabel || '合计', sz: 9.5, col: S.faint, font: S.font }]], { align: 'ctr', anchor: 'ctr' }));
2809
2829
  var n = Math.max(1, labels.length);
2810
- var rowH = Math.min(d.legendRowH, 3.4 / n);
2830
+ var maxLegH = (bottomY != null) ? Math.max(1.2, bottomY - (d.centerY - 1.7)) : 3.4;
2831
+ var rowH = Math.min(d.legendRowH, maxLegH / n, 3.4 / n);
2811
2832
  var ly0 = d.centerY - (n * rowH) / 2 + 0.1;
2833
+ if (bottomY != null) ly0 = Math.min(ly0, bottomY - n * rowH - 0.04);
2812
2834
  var sum2 = total || 1;
2813
2835
  labels.forEach(function (lb, i) {
2814
2836
  var v = values[i] || 0;
@@ -2865,14 +2887,13 @@ function table(x, y, w, colW, rows, S, opts) {
2865
2887
  function soWhatBar(sh, text, S) {
2866
2888
  var R = (typeof regionOf === 'function') ? regionOf('exhibit', 'annotation')
2867
2889
  : { x: MX, y: PT.exhibit.soWhatY, w: CW, h: 0.62 };
2868
- /* 与 B 通道同算法:长结论条有限缩字号,优先保容器不溢出 */
2869
- var bodyW = Math.max(2.5, R.w - 0.44 - 1.35);
2870
- var fzB = fitFont([String(text || '')], bodyW, R.h - 0.14, { max: 12.5, gapFactor: 0.2, maxShrinkSteps: 3 });
2890
+ /* MD3 结论条:衬底 + 左 accent 轨 + 正文(不绘制「SO WHAT」标签字样) */
2891
+ var bodyW = Math.max(2.5, R.w - 0.44);
2892
+ var fzB = fitFont([String(text || '')], bodyW, R.h - 0.14, { max: 14, gapFactor: 0.2, maxShrinkSteps: 3 });
2871
2893
  sh.push(shape('rect', R.x, R.y, R.w, R.h, S.soft));
2872
2894
  sh.push(shape('rect', R.x, R.y, 0.06, R.h, S.accent));
2873
2895
  sh.push(txSp(R.x + 0.22, R.y + 0.06, R.w - 0.44, R.h - 0.12,
2874
- [[{ t: 'SO WHAT ', sz: 11, b: 1, col: S.accent, font: S.font, spc: 150 },
2875
- { t: text, sz: fzB, col: S.ink, font: S.font }]], { anchor: 'ctr' }));
2896
+ [[{ t: String(text || ''), sz: fzB, b: 1, col: S.ink, font: S.font }]], { anchor: 'ctr' }));
2876
2897
  }
2877
2898
  function footnoteLine(sh, text, S) {
2878
2899
  var R = (typeof regionOf === 'function') ? regionOf('exhibit', 'footnote')
@@ -3732,7 +3753,7 @@ function slidesOf(model, S) {
3732
3753
  if (img.caption) sh.push(txSp(MX, capYImg + 0.04, CW, capH2, [[{ t: img.caption, sz: 10.5, col: S.faint, font: S.font }]]));
3733
3754
  } else if (type === 'donut') {
3734
3755
  var dcn = sec.chart || {};
3735
- if (dcn.labels && dcn.values) donutChart(sh, dcn, S, dcn.colors || donutColors(model.style, S, model.theme));
3756
+ if (dcn.labels && dcn.values) donutChart(sh, dcn, S, dcn.colors || donutColors(model.style, S, model.theme), bodyBottom);
3736
3757
  if (sec.note) sh.push(txSp(MX, PT.note.y, CW, PT.note.h, [[{ t: sec.note, sz: 11, col: S.faint, font: S.font }]]));
3737
3758
  } else if (type === 'heatmap') {
3738
3759
  var H = PT.heatmap;
@@ -3885,10 +3906,27 @@ function slidesOf(model, S) {
3885
3906
  var bRR = regOf('bar', isHBar ? 'hbar' : 'primary', { bottom: botY }) ||
3886
3907
  { x: isHBar ? MX : MX + PT.bar.chartX, y: isHBar ? PT.bar.hbarY0 : PT.bar.chartY,
3887
3908
  w: isHBar ? CW : CW - PT.bar.chartW, h: 3 };
3909
+ var ptsB = (sec.points || []).filter(Boolean);
3910
+ var bx = bRR.x, bw = bRR.w;
3911
+ if (ptsB.length && !isHBar) {
3912
+ var sideW = Math.min(3.6, Math.max(2.6, bRR.w * 0.30));
3913
+ bw = Math.max(3.2, bRR.w - sideW - 0.28);
3914
+ var sx = bRR.x + bw + 0.28;
3915
+ var rowHb = Math.min(0.72, (bRR.h - 0.1) / Math.max(1, ptsB.length));
3916
+ ptsB.slice(0, 6).forEach(function (p, i) {
3917
+ var k = Array.isArray(p) ? String(p[0] || '') : String((p && p.t) || '');
3918
+ var v = Array.isArray(p) ? String(p[1] || '') : String((p && p.d) || '');
3919
+ var y = bRR.y + i * rowHb;
3920
+ sh.push(shape('rect', sx, y + 0.12, 0.08, 0.08, S.accent));
3921
+ sh.push(txSp(sx + 0.2, y, sideW - 0.25, rowHb,
3922
+ [[{ t: k + (v ? ' ' : ''), sz: 12, b: 1, col: S.ink, font: S.font },
3923
+ { t: v, sz: 11, col: S.body, font: S.font }]], { anchor: 'ctr' }));
3924
+ });
3925
+ }
3888
3926
  if (isHBar) {
3889
- chartBlockShapes(sh, ch, S, bRR.x, bRR.y, bRR.w, bRR.h, dcols2);
3927
+ chartBlockShapes(sh, ch, S, bx, bRR.y, bw, bRR.h, dcols2);
3890
3928
  } else {
3891
- chartBlockShapes(sh, ch, S, bRR.x, bRR.y, bRR.w, Math.max(1.5, bRR.h), dcols2);
3929
+ chartBlockShapes(sh, ch, S, bx, bRR.y, bw, Math.max(1.5, bRR.h), dcols2);
3892
3930
  }
3893
3931
  if (sec.note) sh.push(txSp(MX, PT.note.y, CW, PT.note.h, [[{ t: sec.note, sz: 11, col: S.faint, font: S.font }]]));
3894
3932
  } else if (type === 'twocol' || type === 'threecol') {