# 版式库 (16:9, 13.33×7.5 in) — 卡片式视觉系统

> **要点**:版式 helper 全在 `scripts/pptx_helpers.py`,**不要把 helper 源码默写进 build_deck.py** —— 只 `import pptx_helpers as P` 然后调用。配色用 current spec(命名见 SKILL.md §阶段一)里的实际 hex,通过 `P.set_palette(spec_path=...)` 注入,默认商务红 + 自动派生明暗色阶。
>
> **观感升级要点(相对老版"左色条 + 圆点 bullet")**:内容尽量装进**圆角卡片**(`add_card`,自带柔和投影),业务概念配**图标底块**(`add_icon_tile`),数据页优先**KPI 数字卡**(`add_kpi`)而非小柱图,封面/章节用**渐变大色块**(`apply_brand` 已内置)。白底之上靠卡片浮起 + 浅色阶分层,才不是"扁平办公模板"。

## 通用起手(整 deck 单脚本 — 默认路径)

阶段二写一个 `build_deck.py`,一个进程内建完整份 deck、末尾 `save` 一次(**不逐页 run_python**)。每页一个小函数,主流程按逐页大纲依次调用:

```python
import sys
sys.path.insert(0, "<skill_dir>/scripts")   # <skill_dir> 用 system prompt 注入的绝对路径替换
import pptx_helpers as P
from pptx.enum.text import MSO_ANCHOR, PP_ALIGN
from pptx.enum.shapes import MSO_SHAPE

SPEC = "<task_dir>/<today>-<task_short_id>-<task_name>.spec.md"
OUT  = "<task_dir>/<topic>.pptx"
ICONS = "<task_dir>/assets/icons"   # fetch_icon.py 拉到这;种子库在 <skill_dir>/assets/icons

def page_1_cover(prs):
    s = P.add_slide(prs)
    P.apply_brand(s, "cover")
    # ... 见 L1 封面 ...

def page_2(prs):
    s = P.add_slide(prs)
    # ... 见对应 Lx 版式 ...

def main():
    prs = P.new_presentation("16:9")          # 默认 16:9;可传 "4:3" / "9:16" / "3:4"
    P.set_palette(spec_path=SPEC)             # 整 deck 设一次配色 + 派生色阶(同进程常驻)
    for build in (page_1_cover, page_2, ...): # 按逐页大纲顺序
        build(prs)
    prs.save(OUT)

main()
```

跑法:先 `write` 脚本到 `<task_dir>/build_deck.py`,再 `run_python(script_path=...)`。要改(quality_check 报错 / 用户要调)→ 改对应 `page_x` 函数重跑整脚本(可复现,不 edit 成品 .pptx)。

> **风格探针 / 增量补页**:要先看封面 + 1 页观感,把 `main()` 循环临时缩到前 2 个函数跑一遍;或对已存在 deck 追加单页时 `prs = P.load(OUT)` 再 `add_slide`。**常规整建不用 `load`**。

⚠️ 一律用 `P.xxx`(不要 `from pptx_helpers import *`)—— `set_palette` 靠改模块属性覆盖配色,`import *` 会把旧绑定拷进命名空间导致覆盖不生效。

---

## Helper API 速查 (都在 `P.` 命名空间下)

**画布 / 配色入口**
- `P.new_presentation(canvas="16:9")` → 建空 deck,设画布,回填 `P.SLIDE_W/H` 与安全区
- `P.load(path)` → 载入已有 deck,按文件实际尺寸回填画布常量
- `P.add_slide(prs)` → 追加空白 slide
- `P.set_palette(primary=, secondary=, accent=, cn_font=, en_font=, spec_path=)` → 覆盖主题色/字体并**重算派生色阶**;传 `spec_path` 自动取 spec 前 3 个 #hex;默认商务红

**颜色常量**:`P.PRIMARY` `P.SECONDARY` `P.ACCENT` `P.INK` `P.GREY` `P.GREY_LIGHT` `P.HAIRLINE` `P.BG` `P.WHITE`
**派生色阶**(从主/辅/强调自动算):`P.PRIMARY_WASH`(整页/大区域浅底) `P.PRIMARY_SOFT`(卡片/标签浅底) `P.PRIMARY_DARK`(渐变深端) `P.ACCENT_SOFT`(高亮浅底) `P.SURFACE`(卡片白面)
**字体常量**:`P.CN_FONT`(微软雅黑) `P.EN_FONT`(Arial)
**画布常量**:`P.SLIDE_W` `P.SLIDE_H` `P.SAFE_LEFT/TOP/RIGHT/BOTTOM` `P.SAFE_W` `P.SAFE_H`
**色阶工具**:`P.tint(color, pct)` 提亮 / `P.shade(color, pct)` 压暗(自定义中间色用)

**🔥 组合版式件**(一个函数摆一整块 —— 优先用这些,别手摆参差网格/拿卡片硬凑时间线)
- `P.add_card_grid(slide, items, top, height, cols=None, icon_dir=None, accent=None)` → **均衡概念网格**;items=每项 `{icon,title,body}`;自动均衡行列(2×2/2×3,不参差),单行图标顶置、多行图标左置;`icon_dir` 给图标目录(图标名去 `tabler_` 前缀)
- `P.add_timeline(slide, nodes, y=3.2)` → **横向时间轴**;nodes=`{year,title,body}`;发展历程/路线图用,别塞卡片网格
- `P.add_cycle(slide, steps, cy=4.5, radius=1.55, center_label=)` → **流程闭环**(节点沿环+中心词);循环类用。⚠️文字多时改用横向流程(L12)更稳
- `P.add_toc(slide, items, top=2.2)` → **目录**(序号+标题+右副标+发丝线,贯通整宽);items=`(title, caption)`
- `P.add_kpi(slide, l, t, w, h, value, label, baseline=, delta=, delta_dir=)` → **KPI 数字卡**;`baseline`=对比基准、`delta`=趋势(升绿降红);**数字别孤立**
- `P.add_takeaway(slide, "<一句话结论>", top=None)` → **结论框**(浅主色底+左条);内容页论断标题下标配
- `P.add_source(slide, "<来源>")` → 数据来源(右下角弱化);含数据的页必标
- `P.add_picture_bg(slide, png)` → 整页铺渲染好的高清背景图(混合方案:背景图+原生可编辑文字)

**容器 / 质感**(卡片式核心)
- `P.add_card(slide, l, t, w, h, fill=SURFACE, radius=0.12, shadow=False, border=None, accent=None)` → 圆角卡片。**默认平卡**(白底描发丝边);**投影是克制**:平铺对等卡一律平,`shadow=True` 只给真悬浮/被挑出的卡,每页 ≤2-3 个;**一容器一手段**(投影/描边/底色/accent 四选一不叠)。见 design_principles §视觉深度
- `P.add_round_rect(slide, l, t, w, h, fill, radius=0.10)` → 无投影圆角矩形
- `P.add_gradient_rect(slide, l, t, w, h, c1, c2, angle=90, rounded=False)` → 渐变块(封面/章节大色块;原生可编辑非图片)
- `P.set_shadow(shape, ...)` / `P.set_line(shape, color, weight)` → 手动投影 / 描边
- `P.add_bg(slide, color=BG)` → 整页背景(`apply_brand` 已内置)
- 语义色:`P.GOOD`(增长绿)/ `P.BAD`(下降红)—— KPI 趋势用,不计三色制

**组件**
- `P.add_icon_tile(slide, x, y, size=0.9, png_path=None, fill=PRIMARY_SOFT)` → 图标圆角底块 + 居中图标
- `P.add_icon(slide, png_path, x, y, size=0.6)` → 裸图标 PNG(方形源等比)
- `P.add_pill(slide, x, y, w, h, text, fill=PRIMARY, fg=WHITE, size=12)` → 胶囊标签 / chip
- `P.add_eyebrow(slide, x, y, text, color=PRIMARY, size=13)` → 标题上方小标签 / kicker
- `P.add_badge(slide, x, y, num, diameter=0.7)` → 编号徽章(圆+数字)
- `P.add_chevron(slide, x, y, w=0.55, h=0.5, color=GREY_LIGHT)` → 流程箭头
- `P.add_dot(slide, x, y, size=0.18, color=ACCENT)` → 圆点(bullet 前缀)
- `P.add_accent_line(slide, x, y, length=1.0, thickness=0.05)` → 强调短线
- `P.add_divider(slide, x, y, length, vertical=False)` → 细分隔线

**文本 / 标题 / 品牌 / 备注**
- `P.add_textbox(slide, l, t, w, h, text, size, bold=False, color=INK, align=, anchor=, font=None, shrink=True, name=)` → 文本框;`font=None` 自动 latin=Arial + 东亚=微软雅黑(**中文真落雅黑靠这个**),传 `font` 则两槽都用它(纯英文大字/数字)
- `P.page_title(slide, text, page_num=None, total=None, footer=, eyebrow=None)` → 内页标题+强调线(+可选 eyebrow / 页脚页码)
- `P.apply_brand(slide, kind)` → 品牌锚点,`kind` ∈ `"cover"/"inner"/"section"/"end"`;**每页第一行必调**(已含整页背景)
- `P.add_notes(slide, text)` → 演讲者备注(正式产物每页给 2-4 句口述要点)
- `P.assert_inside(l, t, w, h, name="")` → 手动越界校验(放置 helper 已内置)

---

## 🔥 组合件示例 (优先用 —— 一个函数一整块)

### 内容页范式:论断标题 + Takeaway + 均衡网格

> 内容页的"黄金结构"(咨询级):**论断式标题**(写结论)→ **Takeaway 一句话**(浅底框)→ 内容。把它做成本地小函数 `content_header`。

```python
from pptx.enum.text import MSO_ANCHOR
def content_header(s, title, takeaway, eyebrow=None):
    ty = P.SAFE_TOP
    if eyebrow:
        P.add_eyebrow(s, P.SAFE_LEFT, ty, eyebrow); ty += 0.4
    P.add_textbox(s, P.SAFE_LEFT, ty, P.SAFE_W, 0.7, title, 28, bold=True,
                  color=P.PRIMARY, name="title")            # 论断标题
    if takeaway:
        P.add_takeaway(s, takeaway, top=ty + 0.82)           # 结论框

s = P.add_slide(prs); P.apply_brand(s, "inner")
content_header(s, "大模型靠规模涌现出通用智能",
               "参数突破千亿临界点后,模型从'专用工具'跃升为'通用大脑'",
               eyebrow="DEFINITION")
items = [                                                     # 每项 icon 名 + 标题 + 精炼正文(≤18 字)
    {"icon": "brain", "title": "超大参数", "body": "千亿参数突破临界点,涌现推理力"},
    {"icon": "cpu",   "title": "对话生成", "body": "多轮对话、写代码、摘要改写"},
    {"icon": "cloud-network", "title": "多模态", "body": "文本+图像+音频+视频统一理解"},
    {"icon": "target", "title": "任务规划", "body": "高级推理与链式拆解"},
    {"icon": "bolt",  "title": "持续成长", "body": "RLHF、RAG、微调持续打磨"},
]
P.add_card_grid(s, items, top=2.35, height=4.5, icon_dir=ICONS)   # 平卡,自动均衡
```

### 时间轴(发展历程 / 路线图)

```python
content_header(s, "六年从 GPT-1 到推理模型,能力指数跃迁",
               "每一代都在重定义能力边界", eyebrow="TIMELINE")
P.add_timeline(s, [
    {"year": "2018", "title": "GPT-1", "body": "预训练范式确立"},
    {"year": "2020", "title": "GPT-3", "body": "1750 亿参数,few-shot 涌现"},
    {"year": "2022", "title": "ChatGPT", "body": "对话式 AI 引爆全民应用"},
    {"year": "2023", "title": "GPT-4", "body": "多模态 + 强推理"},
], y=3.9)
P.add_source(s, "OpenAI / 各厂商公开发布")
```

### KPI 数字卡(数据语境化:对比基准 + 升降)

```python
data = [("158%", "实验吞吐同比", "行业均值 90%", "+68pt", "up"),
        ("27天", "配方迭代周期", "去年 45 天", "-40%", "up"),
        ("92.3%", "中试一次通过率", "行业 81%", "+11pt", "up")]
n, gap = len(data), 0.3; cw = (P.SAFE_W - gap*(n-1))/n
for i,(v,lab,base,delta,d) in enumerate(data):
    P.add_kpi(s, P.SAFE_LEFT+i*(cw+gap), 2.6, cw, 2.7, v, lab,
              baseline=base, delta=delta, delta_dir=d)
```

### breathing 大字页(打破卡片单调 —— 每隔 2-3 页插一个)

```python
s = P.add_slide(prs); P.apply_brand(s, "inner")
P.add_eyebrow(s, P.SAFE_LEFT, 1.5, "THE INFLECTION POINT")
P.add_textbox(s, P.SAFE_LEFT, 2.15, 9.0, 2.5, "2 个月", 150, bold=True,
              color=P.PRIMARY, font=P.EN_FONT, shrink=False, name="big_stat")
P.add_textbox(s, P.SAFE_LEFT, 4.7, 11, 0.7, "ChatGPT 月活突破 1 亿", 30,
              bold=True, color=P.INK, name="big_label")
P.add_textbox(s, P.SAFE_LEFT, 5.6, 11, 0.6,
              "史上最快 —— 此前纪录是 TikTok 的 9 个月", 18, color=P.GREY,
              name="big_ctx")   # 数据语境化:大数字必带对比
```

### 目录(贯通整宽)

```python
P.page_title(s, "目录", eyebrow="AGENDA")
P.add_toc(s, [("什么是大模型", "规模、能力与边界"),
              ("发展历程", "六年能力跃迁"),
              ("AI 智能体", "从对话到自主行动")], top=2.25)
```

### 混合背景封面(杂志级,opt-in)

```python
# 先 run_python: python render_bg.py --out <task_dir>/figures/cover_bg.png --kind cover --primary C00000
s = P.add_slide(prs)
P.add_picture_bg(s, "<task_dir>/figures/cover_bg.png")       # 背景图(不可编辑)
P.add_eyebrow(s, 0.95, 1.95, "TECHNOLOGY INSIGHT · 2026", color=P.ACCENT)
P.add_textbox(s, 0.95, 2.45, 8.0, 1.7, "主标题\n副标题行", 44, bold=True,
              color=P.WHITE, name="cover_title")             # 白字叠背景(可编辑)
```

> 下面 L1-L13 是更细的手摆版式参考;**业务概念/数据/历程/循环优先用上面的组合件**,手摆只在组合件不覆盖时用。

> ⚠️ **给每个元素起语义 `name`**(`"bullet_1"`/`"kpi_val"`/`"eyebrow"`/`"pill"` 等)。quality_check 靠 name 判定"哪些是标签(小字号豁免)、哪些是真 bullet(计 ≤5)、谁压了谁",名字乱起会误报。helper 默认名已合理,自己加文本时照着命名。

> `MSO_SHAPE` / `PP_ALIGN` / `MSO_ANCHOR` 页面里要直接用就自行 import(`pptx_helpers` 内部已 import 但不重导出)。

---

## L1 · 封面 (Cover) —— 渐变大色块 + 左侧标题区

```python
s = P.add_slide(prs)
P.apply_brand(s, "cover")  # 右侧 40% 主色→深主色渐变块 + 左上强调短线 + 底细线
# 左侧标题区(避开右侧渐变块,文字区约 7.4 寸宽)
P.add_eyebrow(s, 0.9, 2.0, "2026 年度技术汇报")          # kicker 小标签
P.add_textbox(s, 0.9, 2.5, 7.2, 1.6, "项目名称 / 演示主题",
              42, bold=True, color=P.INK, name="cover_title")
P.add_textbox(s, 0.9, 4.4, 7.0, 0.6, "一句话副标题或定位",
              20, color=P.GREY, name="cover_sub")
P.add_textbox(s, 0.9, 6.4, 7.0, 0.4, "汇报人 · 部门 · 2026-06-08",
              14, color=P.GREY_LIGHT, name="cover_meta")
P.add_notes(s, "开场白:点出主题与本次汇报要解决的核心问题。")
```

> 有合适主图时(见 SKILL.md §配图),可把右侧渐变块换成**真实图片**:`s.shapes.add_picture(hero, Inches(P.SLIDE_W*0.6), Inches(0), height=Inches(7.5))`,再在图上叠半透明主色块保证文字区干净。

---

## L2 · 目录 (Agenda) —— 编号徽章 + 文字

```python
from pptx.enum.text import MSO_ANCHOR
s = P.add_slide(prs)
P.apply_brand(s, "inner")
P.page_title(s, "目录")

items = ["背景与现状", "核心问题", "解决方案", "实施计划", "预期成果"]
for i, item in enumerate(items):
    y = 1.9 + i * 0.95
    P.add_badge(s, P.SAFE_LEFT, y, i + 1, diameter=0.65)
    P.add_textbox(s, P.SAFE_LEFT + 1.0, y, P.SAFE_W - 1.0, 0.65, item, 22,
                  color=P.INK, anchor=MSO_ANCHOR.MIDDLE, name=f"agenda_{i}")
```

---

## L3 · 章节分隔 (Section Divider) —— 渐变整页 + 大字编号(白字)

```python
from pptx.enum.text import MSO_ANCHOR
s = P.add_slide(prs)
P.apply_brand(s, "section")  # 主色→深主色整页渐变 + 强调装饰条
# 大编号(白色;font=EN_FONT 让数字走 Arial)
P.add_textbox(s, 1.1, 2.0, 4, 2.5, "01", 150, bold=True, color=P.WHITE,
              font=P.EN_FONT, name="sec_num")
# 章节名(白色)
P.add_textbox(s, 5.3, 2.8, 7, 1.0, "背景与现状", 44, bold=True,
              color=P.WHITE, anchor=MSO_ANCHOR.MIDDLE, name="sec_title")
# 引言(强调浅色,渐变深底上可读)
P.add_textbox(s, 5.3, 4.0, 7, 0.6, "本章讨论行业现状与机会窗口", 18,
              color=P.ACCENT_SOFT, name="sec_lead")
```

> 渐变深底上文字一律用 **白 / `ACCENT_SOFT`** 等浅色,不要用 `INK` 深灰(看不清)。

---

## L4 · 要点 (Bullets) —— 圆点 + 文字;≥3 条建议升级成卡片(见 L11)

```python
from pptx.enum.text import MSO_ANCHOR
s = P.add_slide(prs)
P.apply_brand(s, "inner")
P.page_title(s, "核心结论")

bullets = [
    "结论一:用一句话讲清楚",
    "结论二:具体数据支撑,如增长 27%",
    "结论三:对未来的判断,简洁有力",
    "结论四:可选第四条,不要超过 5 条",
]
for i, b in enumerate(bullets):
    y = 2.0 + i * 0.95
    P.add_dot(s, P.SAFE_LEFT + 0.05, y + 0.22, size=0.18)
    P.add_textbox(s, P.SAFE_LEFT + 0.45, y, P.SAFE_W - 0.45, 0.6, b, 22,
                  color=P.INK, anchor=MSO_ANCHOR.MIDDLE, name=f"bullet_{i}")
```

> 纯圆点 bullet 偏单薄。**业务概念类要点(能力/模块/策略)优先用 L11 卡片网格 + 图标底块**,视觉重量足。

---

## L5 · 双栏对比 (Two-Column) —— 两张卡片,左中右灰

```python
from pptx.enum.text import PP_ALIGN, MSO_ANCHOR
s = P.add_slide(prs)
P.apply_brand(s, "inner")
P.page_title(s, "现状 vs 改进后")

cw = (P.SAFE_W - 0.5) / 2    # 两卡 + 中间 0.5 间隙
ly, lh = 2.0, 4.5
# 左卡:现状(中性灰底,弱化)
P.add_card(s, P.SAFE_LEFT, ly, cw, lh, fill=P.BG, border=True, shadow=False)
P.add_pill(s, P.SAFE_LEFT + 0.35, ly + 0.35, 1.1, 0.36, "现状", fill=P.GREY)
left_pts = ["问题 A:描述", "问题 B:描述", "问题 C:描述"]
for i, p in enumerate(left_pts):
    yy = ly + 1.1 + i * 0.7
    P.add_dot(s, P.SAFE_LEFT + 0.4, yy + 0.16, color=P.GREY)
    P.add_textbox(s, P.SAFE_LEFT + 0.8, yy, cw - 1.1, 0.55, p, 17,
                  color=P.INK, anchor=MSO_ANCHOR.MIDDLE, name=f"l_pt_{i}")
# 右卡:改进后(主色强调条 + 浅底,突出)
rx = P.SAFE_LEFT + cw + 0.5
P.add_card(s, rx, ly, cw, lh, fill=P.SURFACE, accent=P.PRIMARY)
P.add_pill(s, rx + 0.5, ly + 0.35, 1.3, 0.36, "改进后", fill=P.PRIMARY)
right_pts = ["改善 A:描述", "改善 B:描述", "改善 C:描述"]
for i, p in enumerate(right_pts):
    yy = ly + 1.1 + i * 0.7
    P.add_dot(s, rx + 0.55, yy + 0.16, color=P.ACCENT)
    P.add_textbox(s, rx + 0.95, yy, cw - 1.3, 0.55, p, 17, color=P.INK,
                  anchor=MSO_ANCHOR.MIDDLE, name=f"r_pt_{i}")
```

---

## L6 · 图表为主 (Chart-focus) —— 标题 + 一句结论 + 大图嵌卡片

```python
from pptx.util import Inches
from pptx.enum.text import PP_ALIGN
# chart.png 已用 matplotlib 生成(见 design_principles.md §7)
s = P.add_slide(prs)
P.apply_brand(s, "inner")
P.page_title(s, "季度营收持续增长")
P.add_textbox(s, P.SAFE_LEFT, P.SAFE_TOP + 1.1, P.SAFE_W, 0.5,
              "Q4 同比增长 158%,创历史新高", 18, color=P.GREY, name="lead")
# 图表衬一张白卡片(浮起,比裸图精致)
P.add_card(s, 2.0, 2.4, 9.3, 4.3, fill=P.SURFACE)
s.shapes.add_picture("<task_dir>/slides/chart.png", Inches(2.4),
                     Inches(2.7), width=Inches(8.5))
P.add_textbox(s, P.SAFE_LEFT, 6.95, P.SAFE_W, 0.4, "数据来源:公司年报 2025",
              11, color=P.GREY_LIGHT, align=PP_ALIGN.RIGHT, shrink=False,
              name="source")
```

---

## L7 · 图片为主 (Image-focus) —— 图占 58%,文字独立区

```python
from pptx.util import Inches
from pptx.enum.shapes import MSO_SHAPE
s = P.add_slide(prs)
P.add_bg(s, P.WHITE)
# 左侧图(只给 height 等比铺满,避免变形)
s.shapes.add_picture("<task_dir>/slides/hero.jpg", Inches(0), Inches(0),
                     height=Inches(7.5))
# 右侧浅底文字区
P.add_rect(s, 7.7, 0, 5.63, 7.5, P.PRIMARY_WASH, "text_panel")
P.add_eyebrow(s, 8.1, 1.4, "PRODUCT")
P.add_textbox(s, 8.1, 1.9, 4.9, 1.0, "走进未来", 36, bold=True, color=P.INK,
              name="img_title")
P.add_accent_line(s, 8.1, 3.0, length=0.6)
P.add_textbox(s, 8.1, 3.4, 4.9, 1.6, "用一两句话点出主旨,不要把演讲稿搬上来。",
              18, color=P.GREY, name="img_caption")
P.add_shape(s, MSO_SHAPE.RIGHT_ARROW, 8.1, 6.4, 0.7, 0.35, P.ACCENT, "img_cta")
```

---

## L8 · 金句 / 大字 (Quote) —— 留白主导

```python
from pptx.enum.text import MSO_ANCHOR
s = P.add_slide(prs)
P.apply_brand(s, "inner")
P.add_textbox(s, 0.8, 0.6, 1.5, 1.5, '"', 200, bold=True, color=P.ACCENT,
              font=P.EN_FONT, shrink=False, name="quote_mark")
P.add_textbox(s, 1.5, 2.7, 10.5, 2.0, "把复杂留给我们,把简单留给用户。", 36,
              bold=True, color=P.INK, anchor=MSO_ANCHOR.MIDDLE, name="quote_text")
P.add_accent_line(s, 1.5, 5.0, length=0.5)
P.add_textbox(s, 1.5, 5.2, 10.5, 0.5, "—— 公司价值观 2025", 16, color=P.GREY,
              name="quote_attr")
```

---

## L9 · 结尾 / Q&A —— 浅底 + 大字,**强制必有**

> **不是可选** —— 任何 deck 都必须以这页收尾。

```python
from pptx.enum.text import PP_ALIGN
s = P.add_slide(prs)
P.apply_brand(s, "end")  # PRIMARY_WASH 浅底 + 顶/底强调短线
P.add_textbox(s, 0, 2.5, P.SLIDE_W, 1.6, "Thank You", 80, bold=True,
              color=P.PRIMARY, align=PP_ALIGN.CENTER, font=P.EN_FONT,
              name="thanks")
P.add_textbox(s, 0, 4.3, P.SLIDE_W, 0.6, "欢迎提问与讨论", 22, color=P.INK,
              align=PP_ALIGN.CENTER, name="qa")
P.add_textbox(s, 0, 6.2, P.SLIDE_W, 0.5, "联系方式 / 邮箱 / 公众号", 14,
              color=P.GREY_LIGHT, align=PP_ALIGN.CENTER, name="contact")
```

---

## L10 · KPI 数字卡 (Metrics) —— 2-4 张并排,数据页主力

> 数据页**优先用这个**,不要为 2-4 个数字硬画柱状图。大数字 + 标签 + 同比小注,信息密度与质感俱佳。

```python
s = P.add_slide(prs)
P.apply_brand(s, "inner")
P.page_title(s, "平台运行关键指标", eyebrow="运行数据 / 2025")

data = [("158%", "实验吞吐同比", "↑ 较去年"),
        ("27天", "配方迭代周期", "↓ 缩短 40%"),
        ("92.3%", "中试一次通过率", "↑ +11pt"),
        ("4.2万", "累计实验记录", "条")]
n = len(data)
gap = 0.3
cw = (P.SAFE_W - gap * (n - 1)) / n
for i, (v, lab, sub) in enumerate(data):
    P.add_kpi(s, P.SAFE_LEFT + i * (cw + gap), 2.6, cw, 2.7, v, lab, sub=sub)
```

> 想突出某张卡:传 `value_color=P.ACCENT` 或给那张卡 `add_card(..., accent=P.ACCENT)` 后 `add_kpi(..., card=False)` 叠上。

---

## L11 · 卡片网格 (Card Grid) —— 图标底块 + 标题 + 说明,业务概念主力

> 能力 / 模块 / 策略 / 价值点这类**业务概念**用它,替代单薄的圆点 bullet。2-4 列均可;图标走 `add_icon_tile`(图标先按 SKILL.md §阶段二第 2 步批量 `fetch_icon.py` 拉到 `<task_dir>/assets/icons`)。

```python
import os
s = P.add_slide(prs)
P.apply_brand(s, "inner")
P.page_title(s, "三大核心能力")

items = [("target", "数据底座", "统一实验/表征/工艺数据湖,一处录入处处可用"),
         ("cpu",    "智能配方", "贝叶斯优化叠加机理约束,迭代更快更稳"),
         ("chart-bar", "中试放大", "小试到中试参数迁移模型,放大不失真")]
n = len(items)
gap = 0.35
cw = (P.SAFE_W - gap * (n - 1)) / n
for i, (icon, h, body) in enumerate(items):
    x = P.SAFE_LEFT + i * (cw + gap)
    P.add_card(s, x, 2.3, cw, 3.6, accent=P.PRIMARY)
    png = os.path.join(ICONS, f"tabler_{icon}_C00000_128.png")  # 主色染色后的图标
    P.add_icon_tile(s, x + 0.4, 2.7, 0.95, png_path=png)
    P.add_textbox(s, x + 0.4, 3.85, cw - 0.8, 0.5, h, 20, bold=True,
                  color=P.INK, name=f"card_h_{i}")
    P.add_textbox(s, x + 0.4, 4.45, cw - 0.8, 1.1, body, 15, color=P.GREY,
                  name=f"card_b_{i}")
```

---

## L12 · 流程 / 步骤 (Process) —— 卡片 + chevron 箭头串联

```python
s = P.add_slide(prs)
P.apply_brand(s, "inner")
P.page_title(s, "实施四步走", eyebrow="路线图")

steps = [("01", "调研", "梳理现状与痛点"),
         ("02", "建模", "搭数据底座与模型"),
         ("03", "试点", "单产线小批验证"),
         ("04", "推广", "全厂复制与运维")]
n = len(steps)
arrow_w = 0.5
cw = (P.SAFE_W - arrow_w * (n - 1) - 0.2 * (n - 1)) / n
y, h = 2.8, 2.6
for i, (num, title, body) in enumerate(steps):
    x = P.SAFE_LEFT + i * (cw + arrow_w + 0.2)
    P.add_card(s, x, y, cw, h, fill=P.SURFACE)
    P.add_textbox(s, x + 0.3, y + 0.3, cw - 0.6, 0.7, num, 34, bold=True,
                  color=P.PRIMARY, font=P.EN_FONT, name=f"step_num_{i}")
    P.add_textbox(s, x + 0.3, y + 1.1, cw - 0.6, 0.5, title, 19, bold=True,
                  color=P.INK, name=f"step_t_{i}")
    P.add_textbox(s, x + 0.3, y + 1.65, cw - 0.6, 0.8, body, 14, color=P.GREY,
                  name=f"step_b_{i}")
    if i < n - 1:
        P.add_chevron(s, x + cw + 0.1, y + h / 2 - 0.25, arrow_w, 0.5)
```

---

## L13 · 大数字 + 论据 (Stat Highlight) —— 单个震撼数字撑半屏

> 一个核心数字要砸出冲击力时用。左侧超大数字,右侧三两条支撑论据卡。

```python
from pptx.enum.text import MSO_ANCHOR
s = P.add_slide(prs)
P.apply_brand(s, "inner")
P.page_title(s, "一年走完三年的路", eyebrow="成效")
# 左:超大数字(主色)
P.add_textbox(s, P.SAFE_LEFT, 2.4, 5.2, 2.4, "3.6×", 140, bold=True,
              color=P.PRIMARY, font=P.EN_FONT, anchor=MSO_ANCHOR.MIDDLE,
              name="big_stat")
P.add_textbox(s, P.SAFE_LEFT, 4.9, 5.2, 0.5, "研发效率提升", 20, color=P.INK,
              name="big_stat_label")
# 右:支撑论据(浅底小卡堆叠)
facts = ["实验自动排程,人力释放 60%", "失败配方提前预警,返工 ↓45%", "知识沉淀复用,新人上手周期减半"]
for i, f in enumerate(facts):
    yy = 2.5 + i * 1.25
    P.add_card(s, 6.6, yy, 6.0, 1.05, fill=P.PRIMARY_WASH, shadow=False)
    P.add_dot(s, 6.95, yy + 0.45, color=P.PRIMARY)
    P.add_textbox(s, 7.35, yy, 5.0, 1.05, f, 16, color=P.INK,
                  anchor=MSO_ANCHOR.MIDDLE, name=f"fact_{i}")
```

---

## 选版式速查

```
封面                    → L1 (Cover)
目录                    → L2 (Agenda)
转场 / 换章             → L3 (Section Divider)
要点 ≤ 5 条(纯文字)    → L4 (Bullets)
对比类 (前/后, A/B)     → L5 (Two-Column)
有数据图表             → L6 (Chart-focus)
有大图 / 视觉优先       → L7 (Image-focus)
观点强调 / 名言          → L8 (Quote)
末页                    → L9 (Q&A)  [强制]
2-4 个关键数字          → L10 (KPI 数字卡)   ← 优先于硬画柱图
业务概念(能力/模块)    → L11 (卡片网格 + 图标)   ← 优先于圆点 bullet
流程 / 步骤             → L12 (Process)
单个震撼数字            → L13 (Stat Highlight)
```

## 三个常犯的越界场景

1. **bullet 字数超额** —— 22pt 在 11.5 寸宽下每行约 50 个中文字,超 1 行就溢出 0.6 in 框。根本解法是**字数压缩**(见 design_principles.md §字数预算),不要靠 `auto_size` 收字号兜底。
2. **卡片内容超出卡片** —— 卡片内文字按 `卡宽 - 2×0.4` 内边距算框宽;标题/正文字数超了会顶出卡片下边缘。卡片高度留够(KPI 卡 ≥2.5,概念卡 ≥3.4)。
3. **图片不等比拉伸** —— `add_picture(width=, height=)` 同时给会变形;**只给 width 或 height 一项**。
4. **渐变深底上用深色字** —— L3 章节页 / cover 渐变块上的文字必须 `WHITE` / `ACCENT_SOFT`,用 `INK` 看不清。
```
