Bläddra i källkod

Initial release: magazine-web-ppt skill v1.0

A Claude Code skill for generating single-file HTML horizontal-swipe
presentations in "e-magazine × e-ink" style.

- SKILL.md: 6-step workflow with requirement discovery
- assets/template.html: runnable seed (CSS + WebGL + paging JS)
- references/layouts.md: 10 page layouts
- references/themes.md: 5 color presets (no custom hex allowed)
- references/components.md: font/grid/icon/callout/stat/pipeline catalog
- references/checklist.md: P0-P3 graded QA checklist

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
郭浩 2 månader sedan
incheckning
604f5f335b
8 ändrade filer med 2371 tillägg och 0 borttagningar
  1. 21 0
      LICENSE
  2. 103 0
      README.md
  3. 264 0
      SKILL.md
  4. 603 0
      assets/template.html
  5. 265 0
      references/checklist.md
  6. 363 0
      references/components.md
  7. 630 0
      references/layouts.md
  8. 122 0
      references/themes.md

+ 21 - 0
LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2026 op7418 (歸藏)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OF OR OTHER DEALINGS IN
+THE SOFTWARE.

+ 103 - 0
README.md

@@ -0,0 +1,103 @@
+# Magazine Web PPT · 电子杂志风网页 PPT Skill
+
+一个 [Claude Code / Claude Agent Skills](https://agentskills.io/) 技能,用于生成**单文件 HTML 横向翻页 PPT**,视觉基调是"**电子杂志 × 电子墨水**"——像 *Monocle* 贴上了代码的样子。
+
+> 由 [歸藏](https://x.com/op7418) 在"一人公司:被 AI 折叠的组织"、"一种新的工作方式"等线下分享中沉淀而成,踩过的每一个坑都写进了 `checklist.md`。
+
+## 效果
+
+- 🖋 **衬线大标题 + 非衬线正文 + 等宽元数据**的三级字体分工
+- 🌊 **WebGL 流体/色散背景**,hero 页可见,正文页克制
+- 📐 **横向左右翻页**:键盘 ← → / 滚轮 / 触屏滑动 / 底部圆点 / ESC 索引
+- 🎨 **5 套主题色预设**:墨水经典 / 靛蓝瓷 / 森林墨 / 牛皮纸 / 沙丘
+- 🧩 **10 种页面布局**:开场封面、章节幕封、数据大字报、左文右图、图片网格、Pipeline、悬念问题、大引用、Before/After 对比、图文混排
+- 📄 **单文件 HTML**:不需要构建、不需要服务器,浏览器直接打开
+
+## 适合 / 不适合
+
+**✅ 合适**:线下分享 / 行业内部讲话 / 私享会 / AI 产品发布 / demo day / 带强烈个人风格的演讲
+
+**❌ 不合适**:大段表格数据 / 培训课件(信息密度不够)/ 需要多人协作编辑(静态 HTML)
+
+## 安装
+
+把整个 skill 目录放进 Claude Code 的 skills 目录:
+
+```bash
+git clone https://github.com/op7418/guizang-ppt-skill.git ~/.claude/skills/magazine-web-ppt
+```
+
+Claude Code 会在对话里自动发现并调用这个 skill。触发关键词:
+
+- "帮我做一份杂志风 PPT"
+- "生成一个 horizontal swipe deck"
+- "editorial magazine style presentation"
+- "electronic ink 风格演讲 slides"
+
+## 使用流程
+
+Skill 本身是结构化的 6 步工作流,Claude 会逐步引导:
+
+1. **需求澄清** — 6 问清单:受众、时长、素材、图片、主题色、硬约束
+2. **拷贝模板** — `assets/template.html` → 项目目录,改 `<title>`,换主题色
+3. **填充内容** — 从 10 种 layout 骨架里挑、粘、改文案(先做类名预检 + 主题节奏规划)
+4. **自检** — 对照 `references/checklist.md`,P0 级问题必须全过
+5. **预览** — 浏览器直接打开
+6. **迭代** — inline style 改字号/高度/间距
+
+详细说明见 [`SKILL.md`](./SKILL.md)。
+
+## 目录结构
+
+```
+magazine-web-ppt/
+├── SKILL.md              ← Skill 主文件:工作流、原则、常见错误
+├── README.md             ← 本文件
+├── assets/
+│   └── template.html     ← 完整可运行的种子 HTML(CSS + WebGL + 翻页 JS 全配好)
+└── references/
+    ├── components.md     ← 组件手册(字体、色、网格、图标、callout、stat、pipeline)
+    ├── layouts.md        ← 10 种页面布局骨架(可直接粘贴)
+    ├── themes.md         ← 5 套主题色预设(只能选不能自定义)
+    └── checklist.md      ← 质量检查清单(P0 / P1 / P2 / P3 分级)
+```
+
+## 主题色预设
+
+从 `references/themes.md` 里选一套——**不允许自定义 hex 值**,保护美学比给自由更重要。
+
+| 主题 | 适合场景 |
+|------|---------|
+| 🖋 墨水经典 | 通用默认、商业发布、不知道选啥 |
+| 🌊 靛蓝瓷 | 科技 / 研究 / AI / 技术发布会 |
+| 🌿 森林墨 | 自然 / 可持续 / 文化 / 非虚构 |
+| 🍂 牛皮纸 | 怀旧 / 人文 / 文学 / 独立杂志 |
+| 🌙 沙丘 | 艺术 / 设计 / 创意 / 画廊 |
+
+切换主题只需替换 `template.html` 开头 `:root{}` 里的 6 行变量,其他 CSS 全走 `var(--...)`。
+
+## 核心设计原则
+
+1. **克制优于炫技** — WebGL 背景只在 hero 页透出
+2. **结构优于装饰** — 信息靠字号 + 字体对比 + 网格留白,不用阴影和浮动卡片
+3. **图片是第一公民** — 只裁底部,顶部和左右完整
+4. **节奏靠 hero 页** — hero / non-hero 交替,才不累眼睛
+5. **术语统一** — Skills 就是 Skills,不中英混译
+
+## 视觉参考
+
+- [*Monocle*](https://monocle.com) 杂志的版式
+- YC Garry Tan "Thin Harness, Fat Skills"
+- 歸藏线下分享 PPT 系列
+
+## 贡献
+
+Bug、排版问题、新布局需求——欢迎开 Issue 或 PR。改动请优先:
+
+- 在 `template.html` 里补类,不要让 layouts.md 使用未定义的类
+- 把踩过的坑写到 `checklist.md` 对应的 P0 / P1 / P2 / P3 级别
+- 新主题色进 `themes.md` 并给出适合的场景
+
+## License
+
+MIT © 2026 [op7418](https://github.com/op7418)

+ 264 - 0
SKILL.md

@@ -0,0 +1,264 @@
+---
+name: magazine-web-ppt
+description: 生成"电子杂志 × 电子墨水"风格的横向翻页网页 PPT(单 HTML 文件),含 WebGL 流体背景、衬线标题 + 非衬线正文、章节幕封、数据大字报、图片网格等模板。当用户需要制作分享 / 演讲 / 发布会风格的网页 PPT,或提到"杂志风 PPT"、"horizontal swipe deck"、"editorial magazine"、"e-ink presentation"时使用。
+---
+
+# Magazine Web Ppt
+
+## 这个 Skill 做什么
+
+生成一份**单文件 HTML**的横向翻页 PPT,视觉基调是:
+
+- **电子杂志 + 电子墨水**混血风格
+- **WebGL 流体 / 等高线 / 色散背景**(hero 页可见)
+- **衬线标题(Noto Serif SC + Playfair Display)+ 非衬线正文(Noto Sans SC + Inter)+ 等宽元数据(IBM Plex Mono)**
+- **Lucide 线性图标**(不用 emoji)
+- **横向左右翻页**(键盘 ← →、滚轮、触屏滑动、底部圆点、ESC 索引)
+- **主题平滑插值**:翻到 hero 页时颜色和 shader 柔顺过渡
+
+这个 skill 的美学不是"商务 PPT",也不是"消费互联网 UI"——它像 *Monocle* 杂志贴上了代码后的样子。
+
+## 何时使用
+
+**合适的场景**:
+- 线下分享 / 行业内部讲话 / 私享会
+- AI 新产品发布 / demo day
+- 带有强烈个人风格的演讲
+- 需要"一次做完,不用翻页工具"的网页版 slides
+
+**不合适的场景**:
+- 大段表格数据、图表叠加(用常规 PPT)
+- 培训课件(信息密度不够)
+- 需要多人协作编辑(这是静态 HTML)
+
+## 工作流
+
+### Step 1 · 需求澄清(**动手前必做**)
+
+**如果用户已经给了完整的大纲 + 图片**,可以跳过直接进 Step 2。
+
+**如果用户只给了主题或一个模糊想法**,用这 6 个问题逐个对齐后再动手。不要基于猜测就开始写 slide——一旦结构定错,后期翻修代价很高:
+
+#### 6 问澄清清单
+
+| # | 问题 | 为什么要问 |
+|---|------|-----------|
+| 1 | **受众是谁?分享场景?**(行业内部 / 商业发布 / demo day / 私享会) | 决定语言风格和深度 |
+| 2 | **分享时长?** | 15 分钟 ≈ 10 页,30 分钟 ≈ 20 页,45 分钟 ≈ 25-30 页 |
+| 3 | **有没有原始素材?**(文档 / 数据 / 旧 PPT / 文章链接) | 有素材就基于素材,没有就帮他搭 |
+| 4 | **有没有图片?放在哪?** | 详见下方"图片约定" |
+| 5 | **想要哪套主题色?** | 见 `references/themes.md`,5 套预设挑一 |
+| 6 | **有没有硬约束?**(必须包含 XX 数据 / 不能出现 YY) | 避免返工 |
+
+#### 大纲协助(如果用户没有大纲)
+
+用"叙事弧"模板搭骨架,再填内容:
+
+```
+钩子(Hook)       → 1 页   : 抛一个反差 / 问题 / 硬数据让人停下来
+定调(Context)    → 1-2 页 : 说明背景 / 你是谁 / 为什么讲这个
+主体(Core)       → 3-5 页 : 核心内容,用 Layout 4/5/6/9/10 穿插
+转折(Shift)      → 1 页   : 打破预期 / 提出新观点
+收束(Takeaway)   → 1-2 页 : 金句 / 悬念问题 / 行动建议
+```
+
+叙事弧 + 页数规划 + 主题节奏表(见 `layouts.md`),**三张表对齐后**再进 Step 2。
+
+大纲建议保存为 `项目记录.md` 或 `大纲-v1.md`,便于后续迭代。
+
+#### 图片约定(告知用户)
+
+在动手前向用户说清:
+
+- **文件夹位置**:`项目/XXX/ppt/images/` 下(和 `index.html` 同级)
+- **命名规范**:`{页号}-{语义}.{ext}`,例如 `01-cover.jpg` / `03-figma.jpg` / `05-dashboard.png`
+  - 页号补零便于排序
+  - 语义用英文,短、具体、和内容对应
+- **规格建议**:
+  - 单张 ≥ 1600px 宽(避免大屏模糊)
+  - JPG 用于照片/截图,PNG 用于透明 UI/图表
+  - 总大小控制在 10MB 内(影响翻页流畅度)
+- **如何替换**:保持**同名覆盖**最稳(HTML 里不用改路径);如果文件名变了,记得全局搜 `images/旧名` 改成新名
+- **没图怎么办**:和用户对齐,可以先用占位色块生成结构,等图片后期补;但要告知 layout 4/5/10 等图文混排页没图就没法验证视觉效果
+
+### Step 2 · 拷贝模板
+
+从 `assets/template.html` 拷贝一份到目标位置(通常是 `项目/XXX/ppt/index.html`),同时在同级建一个 `images/` 文件夹准备接图片。
+
+```bash
+mkdir -p "项目/XXX/ppt/images"
+cp "<SKILL_ROOT>/assets/template.html" "项目/XXX/ppt/index.html"
+```
+
+`template.html` 是一个**完整可运行**的文件——CSS、WebGL shader、翻页 JS、字体/图标 CDN 全已预设好,只有 `<main id="deck">` 里面是 3 个示例 slide(封面、章节幕封、空白填充页)。
+
+#### 2.1 · 必改占位符(**容易漏**)
+
+拷贝后立刻改掉以下占位符,否则浏览器 Tab 会显示"[必填] 替换为 PPT 标题"这种尴尬文字:
+
+| 位置 | 原始 | 需改为 |
+|------|------|--------|
+| `<title>` | `[必填] 替换为 PPT 标题 · Deck Title` | 实际 deck 标题(如 `一种新的工作方式 · Luke Wroblewski`) |
+
+每次拷贝完 template.html 第一件事:grep 一下"[必填]" 确认全部替换完。
+
+#### 2.2 · 选定主题色(5 套预设 · 不允许自定义)
+
+本 skill **只允许从 5 套精心调配的预设里选一套**,不接受用户自定义 hex 值——颜色搭配错了画面瞬间变丑,保护美学比给自由更重要。
+
+| # | 主题 | 适合 |
+|---|------|------|
+| 1 | 🖋 墨水经典 | 通用 / 商业发布 / 不知道选啥的默认 |
+| 2 | 🌊 靛蓝瓷 | 科技 / 研究 / 数据 / 技术发布会 |
+| 3 | 🌿 森林墨 | 自然 / 可持续 / 文化 / 非虚构 |
+| 4 | 🍂 牛皮纸 | 怀旧 / 人文 / 文学 / 独立杂志 |
+| 5 | 🌙 沙丘 | 艺术 / 设计 / 创意 / 画廊 |
+
+**操作**:
+1. 基于内容主题推荐一套,或直接问用户选哪一套
+2. 打开 `references/themes.md`,找到对应主题的 `:root` 块
+3. **整体替换** `assets/template.html`(已拷贝版本)开头 `:root{` 块里标有"主题色"注释的那几行(`--ink` / `--ink-rgb` / `--paper` / `--paper-rgb` / `--paper-tint` / `--ink-tint`)
+4. 其他 CSS 都走 `var(--...)`,无需任何其他改动
+
+**硬规则**:
+- 一份 deck 只用一套主题,不要中途换色
+- 不要接受用户给的任意 hex 值——委婉拒绝并展示 5 套让选
+- 不要混搭(例如 ink 取墨水经典、paper 取沙丘)——会彻底违和
+
+### Step 3 · 填充内容
+
+#### 3.0 · 预检:类名必须在 template.html 里有定义(**最重要**)
+
+**这是所有生成问题的源头**。layouts.md 的骨架使用了很多类名(`h-hero` / `h-xl` / `stat-card` / `pipeline` / `grid-2-7-5` 等),如果 `assets/template.html` 的 `<style>` 里没有对应定义,浏览器会 fallback 到默认样式——大标题变成非衬线、数据卡片挤成一团、pipeline 糊成一行、图片堆到页面底部。
+
+**在写任何 slide 代码之前:**
+
+1. **先 Read `assets/template.html`**(至少读到 `<style>` 块末尾)
+2. **对照 layouts.md 的 Pre-flight 列表**,确认你要用的每个类都在 `<style>` 里存在
+3. 如果某个类缺失:**在 template.html 的 `<style>` 里补上**,不要在每个 slide 里 inline 重写
+4. **template.html 是唯一的类名来源**——不要发明新类名,如需自定义用 `style="..."` inline
+
+常见容易遗漏的类(必须预先确认存在):
+`h-hero` / `h-xl` / `h-sub` / `h-md` / `lead` / `kicker` / `meta-row` / `stat-card` / `stat-label` / `stat-nb` / `stat-unit` / `stat-note` / `pipeline-section` / `pipeline-label` / `pipeline` / `step` / `step-nb` / `step-title` / `step-desc` / `grid-2-7-5` / `grid-2-6-6` / `grid-2-8-4` / `grid-3-3` / `grid-6` / `grid-3` / `grid-4` / `frame` / `frame-img` / `img-cap` / `callout` / `callout-src` / `chrome` / `foot`
+
+#### 3.0.5 · 规划主题节奏(**和类预检同等重要**)
+
+**在挑布局之前**,必须先列出每一页的主题 class(`hero dark` / `hero light` / `light` / `dark`)并写到文档或草稿里对齐。详细规则看 `references/layouts.md` 开头的"主题节奏规划"一节。
+
+**强制规则**:
+
+- 每页 section 必须带 `light` / `dark` / `hero light` / `hero dark` 之一,不要只写 `hero`
+- 连续 3 页以上同主题 = 视觉疲劳,不允许
+- 8 页以上必须有 ≥1 个 `hero dark` + ≥1 个 `hero light`
+- 整个 deck 不能只有 `light` 正文页,必须有 `dark` 正文页制造呼吸
+- 每 3-4 页插入 1 个 hero 页(封面/幕封/问题/大引用)
+
+**生成后自检**:`grep 'class="slide' index.html` 列出所有主题,人工确认节奏合理再交付。
+
+#### 3.1 · 挑布局
+
+**不要从零写 slide**。打开 `references/layouts.md`,里面有 10 种现成布局骨架,每种都是完整可粘贴的 `<section>` 代码块:
+
+| Layout | 用途 |
+|---|---|
+| 1. 开场封面 | 第 1 页 |
+| 2. 章节幕封 | 每幕开场 |
+| 3. 数据大字报 | 抛硬数据 |
+| 4. 左文右图(Quote + Image) | 身份反差 / 故事 |
+| 5. 图片网格 | 多图对比 / 截图实证 |
+| 6. 两列流水线(Pipeline) | 工作流程 |
+| 7. 悬念收束 / 问题页 | 幕末 / 收尾 |
+| 8. 大引用页(Big Quote) | 衬线金句 / takeaway |
+| 9. 并列对比(Before / After) | 旧模式 vs 新模式 |
+| 10. 图文混排(Lead Image + Side Text) | 信息密集的图文页 |
+
+选对应 layout,粘过去,改文案和图片路径即可。**务必先完成 3.0 预检**。
+
+#### 3.2 · 图片比例规范
+
+永远用**标准比例**,不要用原图奇葩比例(如 `2592/1798`):
+
+| 场景 | 推荐比例 |
+|------|---------|
+| 左文右图 主图 | 16:10 或 4:3 + `max-height:56vh` |
+| 图片网格(多图对比) | **固定 `height:26vh`**,不用 aspect-ratio |
+| 左小图 + 右文字 | 1:1 或 3:2 |
+| 全屏主视觉 | 16:9 + `max-height:64vh` |
+| 图文混排小插图 | 3:2 或 3:4 |
+
+**图片绝不使用 `align-self:end`**——会滑到 cell 底被浏览器工具栏遮挡。用 grid 容器 + `align-items:start`(template 已预设)让图片贴顶即可;左列若想贴底,用 flex column + `justify-content:space-between`。
+
+组件细节(字体、颜色、网格、图标、callout、stat-card 等)在 `references/components.md`。
+
+### Step 4 · 对照检查清单自检
+
+生成完一定要打开 `references/checklist.md`,逐项对照。里面总结了**真实迭代过程中踩过的所有坑**,P0 级别的问题(emoji、图片撑破、标题换行、字体分工)必须全部通过。
+
+特别要注意的几条:
+
+1. **大标题必须是衬线字体**——如果显示成非衬线,99% 是 Step 3.0 预检没做,`h-hero` 类在 template.html 里缺失
+2. **图片网格里只用 `height:Nvh`,不用 `aspect-ratio`**(会撑破)
+3. **图片不能堆到页面底部**——不要用 `align-self:end`,用 grid + `align-items:start`(见 Step 3.2)
+4. **图片只能用标准比例**(16:10 / 4:3 / 3:2 / 1:1 / 16:9),不要复制原图的奇葩比例
+5. **中文大标题 ≤ 5 字且 `nowrap`**(避免 1 字 1 行)
+6. **用 Lucide,不用 emoji**
+7. **标题用衬线,正文用非衬线,元数据用等宽**
+
+### Step 5 · 本地预览
+
+直接在浏览器打开 `index.html` 就行。macOS 下:
+
+```bash
+open "项目/XXX/ppt/index.html"
+```
+
+不需要本地服务器。图片走相对路径 `images/xxx.png`。
+
+### Step 6 · 迭代
+
+根据用户反馈修改——模板的 CSS 已经高度参数化,90% 的调整都是改 inline style(字号 `font-size:Xvw` / 高度 `height:Yvh` / 间距 `gap:Zvh`)。
+
+---
+
+## 资源文件导览
+
+```
+magazine-web-ppt/
+├── SKILL.md              ← 你正在读
+├── assets/
+│   └── template.html     ← 完整的可运行模板(种子文件)
+└── references/
+    ├── components.md     ← 组件手册(字体、色、网格、图标、callout、stat、pipeline...)
+    ├── layouts.md        ← 10 种页面布局骨架(可直接粘贴)
+    ├── themes.md         ← 5 套主题色预设(只能选不能自定义)
+    └── checklist.md      ← 质量检查清单(P0/P1/P2/P3 分级)
+```
+
+**加载顺序建议**:
+1. 先读完 `SKILL.md`(这个文件)了解整体
+2. Step 1 需求澄清完成后,读 `themes.md` 帮用户选定一套主题色
+3. **动手前 Read `assets/template.html` 的 `<style>` 块**——这是类名的唯一来源,缺类会导致整页样式崩
+4. 读 `layouts.md` 挑布局(顶部有 Pre-flight 类名清单和主题节奏规划)
+5. 细节调整时读 `components.md` 查组件
+6. 生成后读 `checklist.md` 自检(顶部 P0-0 规则强制预检)
+
+## 核心设计原则(哲学)
+
+> 这些原则是"一人公司"分享 PPT 的 5 轮迭代总结出来的。违反其中任何一条,视觉感都会垮。
+
+1. **克制优于炫技** — WebGL 背景只在 hero 页透出,普通页几乎看不见
+2. **结构优于装饰** — 不用阴影、不用浮动卡片、不用 padding box,一切信息靠**大字号 + 字体对比 + 网格留白**
+3. **内容层级由字号和字体共同定义** — 最大衬线 = 主标题,中衬线 = 副标,大非衬线 = lead,小非衬线 = body,等宽 = 元数据
+4. **图片是第一公民** — 图片只裁底部,保证顶部和左右完整;网格用 `height:Nvh` 固定,不要用 `aspect-ratio` 撑
+5. **节奏靠 hero 页** — hero 和 non-hero 交替,才不累眼睛
+6. **术语统一** — Skills 就是 Skills,不要中英混合翻译
+
+## 参考作品
+
+本 skill 的视觉基调参考了:
+
+- 歸藏 "一人公司:被 AI 折叠的组织" 分享(2026-04-22,27 页)
+- *Monocle* 杂志的版式
+- YC 总裁 Garry Tan "Thin Harness, Fat Skills" 那篇博客的 demo
+
+可以把它们当做风格锚点。

+ 603 - 0
assets/template.html

@@ -0,0 +1,603 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>[必填] 替换为 PPT 标题 · Deck Title</title>
+<link rel="preconnect" href="https://fonts.googleapis.com">
+<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
+<link href="https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,500;0,600;0,700;0,800;0,900;1,400;1,700&family=Source+Serif+4:ital,opsz,wght@0,8..60,300;0,8..60,400;0,8..60,500;0,8..60,600;1,8..60,400&family=IBM+Plex+Mono:wght@300;400;500;600&family=Noto+Serif+SC:wght@300;400;500;600;700;900&family=Noto+Sans+SC:wght@300;400;500;700;900&display=swap" rel="stylesheet">
+<style>
+  :root{
+    /* ============ 主题色(默认:🖋 墨水经典) ============
+       切换主题:从 references/themes.md 复制对应的 :root 块
+       整体替换这几行(--ink / --ink-rgb / --paper / --paper-rgb)
+       其他地方散落的 rgba() 都走 var(--ink-rgb) / var(--paper-rgb),无需逐处改 */
+    --ink:#0a0a0b;
+    --ink-rgb:10,10,11;
+    --paper:#f1efea;
+    --paper-rgb:241,239,234;
+    --paper-tint:#e8e5de;
+    --ink-tint:#18181a;
+
+    /* ============ 字体(跨主题固定) ============ */
+    --mono:"IBM Plex Mono",ui-monospace,monospace;
+    --serif-en:"Playfair Display","Source Serif 4",Georgia,serif;
+    --serif-body-en:"Source Serif 4",Georgia,serif;
+    --serif-zh:"Noto Serif SC",source-han-serif-sc,serif;
+    --sans-zh:"Noto Sans SC",source-han-sans-sc,sans-serif;
+  }
+  *{box-sizing:border-box;margin:0;padding:0}
+  html,body{width:100%;height:100%;overflow:hidden;background:var(--ink);color:var(--paper);font-family:var(--sans-zh);-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}
+
+  /* ============ WebGL 双背景 ============ */
+  canvas.bg{position:fixed;inset:0;width:100vw;height:100vh;z-index:0;display:block;transition:opacity 1.2s ease}
+  canvas#bg-light{opacity:0}
+  canvas#bg-dark{opacity:1}
+  body.light-bg canvas#bg-light{opacity:1}
+  body.light-bg canvas#bg-dark{opacity:0}
+
+  /* ============ Deck 容器 + 翻页 ============ */
+  /* width: NSLIDES * 100vw,会在 JS 里动态矫正 */
+  #deck{position:fixed;inset:0;width:10000vw;height:100vh;display:flex;flex-wrap:nowrap;transition:transform .9s cubic-bezier(.77,0,.175,1);z-index:10;will-change:transform}
+  .slide{width:100vw;height:100vh;flex:0 0 100vw;position:relative;padding:6vh 6vw 10vh 6vw;display:flex;flex-direction:column;overflow:hidden}
+  .slide.light{color:var(--ink)}
+  .slide.dark{color:var(--paper)}
+
+  /* 默认页:遮罩较厚,保证文字可读 */
+  .slide::before{content:"";position:absolute;inset:0;z-index:-1;pointer-events:none;transition:background .7s ease}
+  .slide.light::before{background:rgba(var(--paper-rgb),.78);backdrop-filter:blur(3px)}
+  .slide.dark::before{background:rgba(var(--ink-rgb),.78);backdrop-filter:blur(3px)}
+  /* Hero 页:遮罩大幅降低,让 WebGL 背景明显透出 */
+  .slide.hero.light::before{background:rgba(var(--paper-rgb),.16);backdrop-filter:none}
+  .slide.hero.dark::before{background:rgba(var(--ink-rgb),.12);backdrop-filter:none}
+  /* Hero 页顶底微弱渐隐,保证 chrome/foot 区域可读 */
+  .slide.hero::after{content:"";position:absolute;inset:0;z-index:-1;pointer-events:none}
+  .slide.hero.light::after{background:linear-gradient(180deg,rgba(var(--paper-rgb),.28) 0%,rgba(var(--paper-rgb),0) 14%,rgba(var(--paper-rgb),0) 86%,rgba(var(--paper-rgb),.28) 100%)}
+  .slide.hero.dark::after{background:linear-gradient(180deg,rgba(var(--ink-rgb),.32) 0%,rgba(var(--ink-rgb),0) 14%,rgba(var(--ink-rgb),0) 86%,rgba(var(--ink-rgb),.32) 100%)}
+
+  /* ============ Magazine chrome:顶部 meta + 底部 foot ============ */
+  .chrome{display:flex;justify-content:space-between;align-items:flex-start;font-family:var(--mono);font-size:12px;letter-spacing:.18em;text-transform:uppercase;opacity:.7}
+  .chrome .left,.chrome .right{display:flex;gap:2.4em;align-items:center}
+  .chrome .sep{width:40px;height:1px;background:currentColor;opacity:.4}
+  .foot{margin-top:auto;display:flex;justify-content:space-between;align-items:flex-end;font-family:var(--mono);font-size:12px;letter-spacing:.14em;text-transform:uppercase;opacity:.55}
+  .foot .title{font-family:var(--serif-zh);font-weight:400;letter-spacing:.05em;text-transform:none;opacity:.75;font-size:13px}
+
+  .tag{display:inline-block;font-family:var(--mono);font-size:11px;letter-spacing:.24em;text-transform:uppercase;padding:6px 14px;border:1px solid currentColor;opacity:.85}
+  .rule{width:100%;height:1px;background:currentColor;opacity:.25;margin:3vh 0}
+  .rule.v{width:1px;height:100%;margin:0}
+
+  /* ============ 字体规则 ============
+     · 衬线(Noto Serif SC / Playfair):大标题、重点金句、数字
+     · 非衬线(Noto Sans SC):正文描述、body、补充说明
+     · 等宽(IBM Plex Mono):kicker、meta 小标签、foot 右侧
+  */
+  .kicker{font-family:var(--mono);font-size:12px;letter-spacing:.3em;text-transform:uppercase;opacity:.6;margin-bottom:2.6vh}
+  .display{font-family:var(--serif-en);font-weight:700;font-size:11vw;line-height:.92;letter-spacing:-.025em}
+  .display-zh{font-family:var(--serif-zh);font-weight:700;font-size:7.8vw;line-height:1.04;letter-spacing:-.005em}
+  .h1-zh{font-family:var(--serif-zh);font-weight:700;font-size:4.6vw;line-height:1.12;letter-spacing:-.005em}
+  .h2-zh{font-family:var(--serif-zh);font-weight:600;font-size:3.2vw;line-height:1.2;letter-spacing:0}
+  .h3-zh{font-family:var(--serif-zh);font-weight:500;font-size:1.9vw;line-height:1.35}
+  .body-zh{font-family:var(--sans-zh);font-weight:400;font-size:max(15px,1.22vw);line-height:1.75;opacity:.82;letter-spacing:.01em}
+  .body-serif{font-family:var(--serif-zh);font-weight:400;font-size:max(15px,1.3vw);line-height:1.65;opacity:.88}
+  .lead{font-family:var(--serif-zh);font-weight:400;font-size:1.9vw;line-height:1.4;opacity:.85}
+  .meta{font-family:var(--mono);font-size:max(11px,.88vw);letter-spacing:.16em;text-transform:uppercase;opacity:.6}
+  .big-num{font-family:var(--serif-en);font-weight:800;font-size:10vw;line-height:.85;letter-spacing:-.03em;font-feature-settings:"tnum"}
+  .mid-num{font-family:var(--serif-en);font-weight:700;font-size:5.5vw;line-height:.88;letter-spacing:-.02em;font-feature-settings:"tnum"}
+  .ghost{font-family:var(--serif-en);font-weight:900;font-size:34vw;line-height:.8;opacity:.06;letter-spacing:-.04em;position:absolute;font-feature-settings:"tnum"}
+  em{font-style:italic;font-family:var(--serif-en)}
+  .en{font-family:var(--serif-en);font-style:italic;font-weight:500}
+
+  /* ============ 布局工具 ============ */
+  .col{display:flex;flex-direction:column;gap:2.4vh}
+  .row{display:flex;align-items:center;gap:3vw}
+  .grid-6{display:grid;grid-template-columns:repeat(3,1fr);grid-template-rows:repeat(2,1fr);gap:4vw 6vw;flex:1;align-content:center;padding:2vh 0}
+  .grid-9{display:grid;grid-template-columns:repeat(3,1fr);grid-template-rows:repeat(3,1fr);gap:3vh 4vw;flex:1;align-content:center}
+  .grid-4{display:grid;grid-template-columns:repeat(2,1fr);grid-template-rows:repeat(2,1fr);gap:4vh 6vw;flex:1;align-content:center}
+  .grid-3{display:grid;grid-template-columns:repeat(3,1fr);gap:4vw;flex:1;align-content:center}
+  .split{display:grid;grid-template-columns:1fr 1fr;gap:4vw;flex:1;align-items:center}
+  .split-55{display:grid;grid-template-columns:55fr 45fr;gap:5vw;flex:1;align-items:stretch}
+  .fill{flex:1}
+  .center{align-items:center;justify-content:center;text-align:center}
+  .bottom-left{position:absolute;left:6vw;bottom:9vh;max-width:50vw}
+  .bottom-right{position:absolute;right:6vw;bottom:9vh;max-width:50vw;text-align:right}
+  .top-right{position:absolute;right:6vw;top:6vh;text-align:right}
+
+  /* ============ Stat(数字矩阵) ============ */
+  .stat{display:flex;flex-direction:column;gap:1vh;align-items:flex-start}
+  .stat .n{font-family:var(--serif-en);font-weight:800;font-size:8vw;line-height:.88;letter-spacing:-.03em;font-feature-settings:"tnum"}
+  .stat .l{font-family:var(--sans-zh);font-size:max(13px,1.05vw);opacity:.7;margin-top:1vh;font-weight:400;line-height:1.5}
+  .stat .m{font-family:var(--mono);font-size:10px;letter-spacing:.22em;text-transform:uppercase;opacity:.5;margin-bottom:.2vh}
+
+  /* ============ Callout(引用框) ============ */
+  .callout{padding:3vh 2.4vw;border-left:3px solid currentColor;position:relative;font-family:var(--serif-zh);font-size:max(15px,1.2vw);line-height:1.55;opacity:.92}
+  .slide.light .callout{background:rgba(var(--ink-rgb),.05)}
+  .slide.dark .callout{background:rgba(var(--paper-rgb),.06)}
+  .callout .cite{display:block;margin-top:1.6vh;font-family:var(--mono);font-size:11px;letter-spacing:.2em;text-transform:uppercase;opacity:.6}
+  .callout .q-big{font-family:var(--serif-zh);font-weight:600;font-size:max(17px,1.6vw);line-height:1.42}
+
+  /* ============ Platform(平台卡) ============ */
+  .plat{display:flex;flex-direction:column;justify-content:flex-end;padding:2vh 0;border-top:1px solid currentColor;border-color:rgba(127,127,127,.35)}
+  .plat .name{font-family:var(--serif-zh);font-weight:700;font-size:1.8vw;margin-bottom:.6vh}
+  .plat .nb{font-family:var(--serif-en);font-weight:700;font-size:3.2vw;letter-spacing:-.02em;line-height:1;font-feature-settings:"tnum"}
+  .plat .sub{font-family:var(--mono);font-size:10px;letter-spacing:.18em;text-transform:uppercase;opacity:.55;margin-top:.6vh}
+  .plat .fill{font-family:var(--sans-zh);font-weight:300;font-size:2.4vw;opacity:.28;letter-spacing:-.01em;line-height:1}
+
+  /* ============ Rowline(表格行) ============ */
+  .rowline{display:grid;grid-template-columns:1fr 2fr 1fr;gap:2vw;padding:2.2vh 0;border-top:1px solid currentColor;align-items:center;border-color:rgba(127,127,127,.25)}
+  .rowline:last-child{border-bottom:1px solid currentColor;border-color:rgba(127,127,127,.25)}
+  .rowline .k{font-family:var(--serif-zh);font-weight:700;font-size:1.7vw}
+  .rowline .v{font-family:var(--sans-zh);font-weight:400;font-size:max(14px,1.2vw);opacity:.85;line-height:1.55}
+  .rowline .m{font-family:var(--mono);font-size:11px;letter-spacing:.2em;text-transform:uppercase;opacity:.6;justify-self:end}
+
+  /* ============ Pillar(支柱卡片) ============ */
+  .pillar{display:flex;flex-direction:column;gap:1.8vh}
+  .pillar .ic{font-family:var(--serif-en);font-style:italic;font-size:2.6vw;opacity:.45;font-weight:400}
+  .pillar .ic svg{width:2.8vw;height:2.8vw;stroke-width:1.2;opacity:.7}
+  .pillar .t{font-family:var(--serif-zh);font-weight:700;font-size:2.4vw;line-height:1.1}
+  .pillar .d{font-family:var(--sans-zh);font-weight:400;font-size:max(14px,1.1vw);opacity:.76;line-height:1.6}
+
+  /* ============ Signature / Highlight ============ */
+  .sign{font-family:var(--serif-en);font-style:italic;font-weight:500;font-size:2vw;opacity:.7}
+  .hi{position:relative;display:inline}
+  .slide.dark .hi::after{content:"";position:absolute;left:-.1em;right:-.1em;bottom:-.05em;height:.28em;background:rgba(var(--paper-rgb),.15);z-index:-1}
+  .slide.light .hi::after{content:"";position:absolute;left:-.1em;right:-.1em;bottom:-.05em;height:.28em;background:rgba(var(--ink-rgb),.08);z-index:-1}
+
+  /* ============ Icons(Lucide via CDN) ============ */
+  .ico{width:1em;height:1em;display:inline-block;vertical-align:-.12em;stroke:currentColor;fill:none;stroke-width:1.4;stroke-linecap:round;stroke-linejoin:round;flex-shrink:0}
+  .ico-lg,.ico-md,.ico-sm{fill:none;stroke:currentColor;stroke-linecap:round;stroke-linejoin:round}
+  .ico-lg{width:2.6vw;height:2.6vw;stroke-width:1.2;display:inline-block}
+  .ico-md{width:1.8vw;height:1.8vw;stroke-width:1.3;display:inline-block;vertical-align:-.4em}
+  .ico-sm{width:1.1vw;height:1.1vw;stroke-width:1.4;display:inline-block;vertical-align:-.15em;opacity:.7}
+
+  /* ============ 图片占位(虚线框,提示设计师位置) ============ */
+  .img-slot{border:1.5px dashed rgba(127,127,127,.4);display:flex;align-items:center;justify-content:center;flex-direction:column;gap:1vh;padding:2vh 2vw;font-family:var(--mono);font-size:10px;letter-spacing:.28em;text-transform:uppercase;opacity:.55;position:relative;aspect-ratio:16/9;width:100%;max-height:56vh;margin-inline:auto;box-sizing:border-box}
+  .img-slot::before{content:"";position:absolute;inset:8px;border:1px solid currentColor;opacity:.2}
+  .img-slot .plus{font-size:2vw;font-weight:300;opacity:.5;letter-spacing:0}
+  .img-slot .label{position:relative;z-index:2;text-align:center}
+  .img-slot.r-4x3{aspect-ratio:4/3}
+  .img-slot.r-3x2{aspect-ratio:3/2}
+  .img-slot.r-1x1{aspect-ratio:1/1}
+
+  /* ============ 图片实填框(关键:固定高度 + 只裁底部) ============
+     重要约束:高度用内联 height:Nvh 精确控制,不要用 aspect-ratio(会撑破布局)
+     object-position:top center 保证严禁裁剪顶部和左右,只裁剪底部
+  */
+  .frame-img{overflow:hidden;position:relative;background:rgba(0,0,0,.04);box-sizing:border-box;width:100%;border-radius:4px}
+  .slide.dark .frame-img{background:rgba(255,255,255,.04);border-color:rgba(255,255,255,.12)}
+  .frame-img > img{width:100%;height:100%;object-fit:cover;object-position:top center;display:block}
+  .frame-cap{display:flex;justify-content:space-between;align-items:baseline;gap:1vw;margin-top:.8vh;font-family:var(--mono);font-size:10px;letter-spacing:.22em;text-transform:uppercase;opacity:.72}
+  .frame-cap .pf{font-family:var(--serif-zh);font-weight:600;font-size:max(13px,1vw);letter-spacing:.04em;text-transform:none;opacity:.94}
+  .frame-cap .nb{font-family:var(--serif-en);font-style:italic;font-size:max(15px,1.2vw);letter-spacing:.02em;text-transform:none;opacity:.88}
+  .frame-cap .idx{font-family:var(--mono);opacity:.5}
+  figure.tile{display:flex;flex-direction:column;margin:0;min-width:0}
+  figure.tile > .frame-img{flex:0 0 auto}
+
+  /* ============ 导航 ============ */
+  #nav{position:fixed;left:50%;bottom:2.6vh;transform:translateX(-50%);z-index:30;display:flex;gap:10px;padding:8px 14px;border-radius:999px;background:rgba(0,0,0,.18);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px)}
+  #nav .dot{width:8px;height:8px;border-radius:50%;background:rgba(255,255,255,.3);cursor:pointer;transition:all .3s ease;border:0;padding:0}
+  #nav .dot:hover{background:rgba(255,255,255,.5);transform:scale(1.15)}
+  #nav .dot.active{background:rgba(255,255,255,.95);width:22px;border-radius:999px}
+  body.light-bg #nav{background:rgba(255,255,255,.25)}
+  body.light-bg #nav .dot{background:rgba(var(--ink-rgb),.25)}
+  body.light-bg #nav .dot.active{background:rgba(var(--ink-rgb),.9)}
+  #hint{position:fixed;bottom:3vh;right:3vw;z-index:30;font-family:var(--mono);font-size:10px;letter-spacing:.2em;text-transform:uppercase;opacity:.4;mix-blend-mode:difference;color:#aaa}
+
+  /* ============================================================
+     ============ LAYOUTS API · 面向 agent 的类(v2)============
+     所有 layouts.md 中的骨架都基于下面这套命名。
+     如果你在 layouts.md 里看到某个类,它必须在下面有定义。
+     ============================================================ */
+
+  /* ---------- .frame:每页主内容容器 ---------- */
+  .frame{flex:1;display:flex;flex-direction:column;min-height:0}
+  /* 当 .frame 同时加了 grid 类时,grid 的 display:grid 覆盖 flex */
+  .frame.grid-2-7-5,
+  .frame.grid-2-6-6,
+  .frame.grid-2-8-4,
+  .frame.grid-3-3,
+  .frame.grid-6{display:grid}
+
+  /* ---------- 标题层级(API 名称,衬线为主) ---------- */
+  .h-hero{
+    font-family:var(--serif-zh);
+    font-weight:900;
+    font-size:10vw;
+    line-height:.96;
+    letter-spacing:-.02em;
+  }
+  .h-xl{
+    font-family:var(--serif-zh);
+    font-weight:700;
+    font-size:6.2vw;
+    line-height:1.08;
+    letter-spacing:-.01em;
+  }
+  .h-sub{
+    font-family:var(--serif-zh);
+    font-weight:500;
+    font-size:3.1vw;
+    line-height:1.25;
+    letter-spacing:0;
+    opacity:.7;
+  }
+  .h-md{
+    font-family:var(--serif-zh);
+    font-weight:600;
+    font-size:2.3vw;
+    line-height:1.3;
+  }
+  /* 英文标题专用(Playfair 衬线) */
+  .h-hero-en,.h-xl-en{font-family:var(--serif-en);letter-spacing:-.025em}
+
+  /* ---------- lead 引语 ---------- */
+  .lead{
+    font-family:var(--serif-zh);
+    font-weight:400;
+    font-size:1.75vw;
+    line-height:1.5;
+    opacity:.86;
+  }
+
+  /* ---------- meta-row 底部元数据 ---------- */
+  .meta-row{
+    display:flex;
+    gap:1.2em;
+    align-items:baseline;
+    flex-wrap:wrap;
+    font-family:var(--mono);
+    font-size:max(12px,.92vw);
+    letter-spacing:.16em;
+    text-transform:uppercase;
+    opacity:.6;
+  }
+
+  /* ---------- stat-card(数据大字报用) ---------- */
+  .stat-card{
+    display:flex;
+    flex-direction:column;
+    gap:.8vh;
+    align-items:flex-start;
+    padding-top:1.6vh;
+    border-top:1px solid currentColor;
+    border-color:rgba(127,127,127,.3);
+  }
+  .stat-card .stat-label{
+    font-family:var(--mono);
+    font-size:max(10px,.78vw);
+    letter-spacing:.24em;
+    text-transform:uppercase;
+    opacity:.55;
+  }
+  .stat-card .stat-nb{
+    font-family:var(--serif-en);
+    font-weight:800;
+    font-size:5.8vw;
+    line-height:.9;
+    letter-spacing:-.03em;
+    font-feature-settings:"tnum";
+    margin-top:.4vh;
+  }
+  .stat-card .stat-nb .stat-unit{
+    font-family:var(--serif-zh);
+    font-weight:500;
+    font-size:.38em;
+    letter-spacing:0;
+    opacity:.72;
+    margin-left:.14em;
+  }
+  .stat-card .stat-note{
+    font-family:var(--sans-zh);
+    font-weight:400;
+    font-size:max(13px,1.05vw);
+    line-height:1.5;
+    opacity:.72;
+    margin-top:.6vh;
+  }
+  /* 当 stat-card 用于 grid-4(2x2),数字可以更大 */
+  .grid-4 .stat-card .stat-nb{font-size:7.5vw}
+  /* 当只有 3 个,字也可以稍大 */
+  .grid-3 .stat-card .stat-nb{font-size:6.8vw}
+
+  /* ---------- pipeline(流水线) ---------- */
+  .pipeline-section{
+    margin-top:4.4vh;
+    padding-top:2.8vh;
+    border-top:1px dashed rgba(127,127,127,.32);
+  }
+  .pipeline-section:first-of-type{
+    border-top:0;
+    padding-top:0;
+    margin-top:3vh;
+  }
+  .pipeline-label{
+    font-family:var(--mono);
+    font-size:max(11px,.85vw);
+    letter-spacing:.24em;
+    text-transform:uppercase;
+    opacity:.62;
+    margin-bottom:2.2vh;
+  }
+  .pipeline{
+    display:grid;
+    grid-template-columns:repeat(5,1fr);
+    gap:1.2vw;
+  }
+  .pipeline[data-cols="3"]{grid-template-columns:repeat(3,1fr)}
+  .pipeline[data-cols="4"]{grid-template-columns:repeat(4,1fr)}
+  .pipeline[data-cols="6"]{grid-template-columns:repeat(6,1fr)}
+  .step{
+    display:flex;
+    flex-direction:column;
+    gap:.8vh;
+    padding-top:1.4vh;
+    border-top:1px solid currentColor;
+    border-color:rgba(127,127,127,.35);
+  }
+  .step-nb{
+    font-family:var(--serif-en);
+    font-style:italic;
+    font-weight:500;
+    font-size:1.15vw;
+    opacity:.45;
+  }
+  .step-title{
+    font-family:var(--sans-zh);
+    font-weight:700;
+    font-size:1.55vw;
+    letter-spacing:.01em;
+    line-height:1.2;
+  }
+  .step-desc{
+    font-family:var(--sans-zh);
+    font-weight:400;
+    font-size:max(12px,.95vw);
+    line-height:1.45;
+    opacity:.72;
+  }
+
+  /* ---------- 网格(layouts.md 所用) ---------- */
+  /* 这些类独立挂到任何容器上都能生效,不依赖 .frame 复合选择器 */
+  .grid-2-7-5{display:grid;grid-template-columns:7fr 5fr;gap:3vw 4vh;align-items:start}
+  .grid-2-6-6{display:grid;grid-template-columns:1fr 1fr;gap:3vw 4vh;align-items:start}
+  .grid-2-8-4{display:grid;grid-template-columns:8fr 4fr;gap:3vw 4vh;align-items:start}
+  .grid-3-3{
+    display:grid;
+    grid-template-columns:repeat(3,1fr);
+    grid-auto-rows:minmax(0,1fr);
+    gap:2.4vh 2vw;
+  }
+  /* grid-6 已在旧样式里定义为 3x2,这里仅补 align */
+
+  /* ---------- 图片 frame-img(layouts.md 主命名) ---------- */
+  /* 在旧样式里已定义,这里补 img-cap 命名别名与增强 */
+  figure.frame-img{margin:0;display:flex;flex-direction:column;min-width:0}
+  .img-cap{
+    display:block;
+    margin-top:.8vh;
+    font-family:var(--mono);
+    font-size:max(10px,.8vw);
+    letter-spacing:.22em;
+    text-transform:uppercase;
+    opacity:.6;
+  }
+  /* callout src 命名别名 */
+  .callout-src{
+    display:block;
+    margin-top:1.6vh;
+    font-family:var(--mono);
+    font-size:11px;
+    letter-spacing:.2em;
+    text-transform:uppercase;
+    opacity:.6;
+  }
+
+  /* ---------- chrome & foot 补位(layouts.md 简单写法) ---------- */
+  .chrome{font-family:var(--mono);font-size:max(11px,.78vw);letter-spacing:.2em;text-transform:uppercase;opacity:.62}
+  .foot{font-family:var(--mono);font-size:max(11px,.78vw);letter-spacing:.18em;text-transform:uppercase;opacity:.5}
+
+  /* ---------- 响应式降级 ---------- */
+  @media (max-width:900px){
+    .display{font-size:16vw}
+    .display-zh{font-size:12vw}
+    .h1-zh{font-size:7vw}
+    .h-hero{font-size:14vw}
+    .h-xl{font-size:9vw}
+    .pipeline{grid-template-columns:repeat(2,1fr)}
+    .grid-2-7-5,.grid-2-6-6,.grid-2-8-4{grid-template-columns:1fr}
+  }
+</style>
+</head>
+<body>
+
+<canvas id="bg-dark" class="bg"></canvas>
+<canvas id="bg-light" class="bg"></canvas>
+<div id="hint">← → 翻页 · ESC 索引</div>
+
+<div id="deck">
+
+<!-- ============================================================
+     SLIDES 插入区 · 在此处填充所有 <section class="slide ..."> 页面
+     每页模板参考 references/page-patterns.md
+     页面组件参考 references/components.md
+     ============================================================ -->
+
+<!-- SLIDES_HERE -->
+
+</div>
+
+<div id="nav"></div>
+
+<script>
+/* =============== WebGL 双背景 ===============
+   深色页:Holographic Dispersion(全息色散 · 钛金暗流)—— 彩虹微扰、鼠标径向涟漪
+   浅色页:Spiral Vortex(旋转涡流 · 银色珍珠)—— domain-warp 流动、无中心
+   修改风格请参考 references/webgl-backgrounds.md
+*/
+const VS = `attribute vec2 position;void main(){gl_Position=vec4(position,0.0,1.0);}`;
+
+const FS_DARK = `precision highp float;
+uniform vec2 u_resolution;uniform float u_time;uniform vec2 u_mouse;
+vec3 palette(float t,vec3 a,vec3 b,vec3 c,vec3 d){return a+b*cos(6.28318*(c*t+d));}
+void main(){
+  vec2 uv=gl_FragCoord.xy/u_resolution.xy;
+  vec2 p=uv*2.0-1.0;p.x*=u_resolution.x/u_resolution.y;
+  vec2 m=u_mouse*2.0-1.0;m.x*=u_resolution.x/u_resolution.y;
+  float md=length(p-m);
+  float mr=sin(md*15.0-u_time*4.0)*exp(-md*3.0);p+=mr*0.08;
+  vec2 p0=p;
+  for(float i=1.0;i<4.0;i++){
+    p.x+=0.1/i*sin(i*3.0*p.y+u_time*0.4)+0.05;
+    p.y+=0.1/i*cos(i*2.0*p.x+u_time*0.3)-0.05;
+  }
+  float r=length(p);float ang=atan(p.y,p.x);
+  vec3 a=vec3(0.12,0.12,0.13);
+  vec3 b=vec3(0.03,0.04,0.05);
+  vec3 c=vec3(1.0,1.0,1.0);
+  vec3 d=vec3(0.1,0.2,0.4);
+  vec3 col=palette(r*1.5+p0.x*0.5+u_time*0.1,a,b,c,d);
+  float disp=sin(r*25.0-u_time*1.5+ang*2.0)*0.5+0.5;
+  col+=vec3(disp*0.015,disp*0.01,disp*0.02);
+  float hi=pow(sin(p.x*4.0+p.y*3.0+u_time)*0.5+0.5,8.0);
+  col+=hi*0.08;
+  vec3 base=vec3(0.05,0.05,0.06);
+  col=mix(base,col,0.85);
+  gl_FragColor=vec4(col,1.0);
+}`;
+
+const FS_LIGHT = `precision highp float;
+uniform vec2 u_resolution;uniform float u_time;uniform vec2 u_mouse;
+float hash(vec2 p){return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5453);}
+float noise(vec2 p){
+  vec2 i=floor(p),f=fract(p);
+  float a=hash(i),b=hash(i+vec2(1,0));
+  float c=hash(i+vec2(0,1)),d=hash(i+vec2(1,1));
+  vec2 u=f*f*(3.0-2.0*f);
+  return mix(a,b,u.x)+(c-a)*u.y*(1.0-u.x)+(d-b)*u.x*u.y;
+}
+float fbm(vec2 p){
+  float v=0.0,a=0.5;
+  mat2 m=mat2(0.80,0.60,-0.60,0.80);
+  for(int i=0;i<5;i++){v+=a*noise(p);p=m*p*2.02;a*=0.5;}
+  return v;
+}
+void main(){
+  vec2 uv=gl_FragCoord.xy/u_resolution.xy;
+  vec2 p=uv;p.x*=u_resolution.x/u_resolution.y;
+  vec2 m=u_mouse;m.x*=u_resolution.x/u_resolution.y;
+  vec2 md=p-m;float dl=length(md);
+  p+=normalize(md+vec2(0.0001))*exp(-dl*5.0)*0.03;
+  vec2 q=vec2(fbm(p*1.8+u_time*0.07),fbm(p*1.8+vec2(5.2,1.3)+u_time*0.06));
+  vec2 r=vec2(fbm(p*2.0+q*1.3+vec2(1.7,9.2)+u_time*0.05),
+              fbm(p*2.0+q*1.3+vec2(8.3,2.8)+u_time*0.04));
+  float f=fbm(p*2.2+r*1.5);
+  vec3 silverDark=vec3(0.86,0.85,0.84);
+  vec3 paper=vec3(0.955,0.945,0.925);
+  vec3 col=mix(silverDark,paper,f);
+  float ph=r.x*2.2+u_time*0.35;
+  col+=vec3(0.78,0.62,0.92)*sin(ph)*0.055;
+  col+=vec3(0.55,0.72,0.95)*sin(ph*0.8+2.0)*0.05;
+  float hl=smoothstep(0.48,0.92,f);
+  col+=hl*0.06;
+  gl_FragColor=vec4(col,1.0);
+}`;
+
+const mouse={x:0.5,y:0.5};
+addEventListener('mousemove',e=>{mouse.x=e.clientX/innerWidth;mouse.y=e.clientY/innerHeight});
+
+function bootGL(canvasId, fsSrc){
+  const canvas=document.getElementById(canvasId);
+  const gl=canvas.getContext('webgl',{alpha:false,antialias:true});
+  if(!gl) return ()=>false;
+  const mk=(t,s)=>{const sh=gl.createShader(t);gl.shaderSource(sh,s);gl.compileShader(sh);return sh};
+  const prog=gl.createProgram();
+  gl.attachShader(prog,mk(gl.VERTEX_SHADER,VS));
+  gl.attachShader(prog,mk(gl.FRAGMENT_SHADER,fsSrc));
+  gl.linkProgram(prog);gl.useProgram(prog);
+  const buf=gl.createBuffer();
+  gl.bindBuffer(gl.ARRAY_BUFFER,buf);
+  gl.bufferData(gl.ARRAY_BUFFER,new Float32Array([-1,-1,1,-1,-1,1,-1,1,1,-1,1,1]),gl.STATIC_DRAW);
+  const pos=gl.getAttribLocation(prog,'position');
+  gl.enableVertexAttribArray(pos);gl.vertexAttribPointer(pos,2,gl.FLOAT,false,0,0);
+  const lRes=gl.getUniformLocation(prog,'u_resolution');
+  const lT=gl.getUniformLocation(prog,'u_time');
+  const lM=gl.getUniformLocation(prog,'u_mouse');
+  const resize=()=>{
+    const d=Math.min(window.devicePixelRatio||1,2);
+    canvas.width=innerWidth*d;canvas.height=innerHeight*d;
+    gl.viewport(0,0,canvas.width,canvas.height);
+  };
+  addEventListener('resize',resize);resize();
+  return (tSec)=>{
+    gl.uniform2f(lRes,canvas.width,canvas.height);
+    gl.uniform1f(lT,tSec);
+    gl.uniform2f(lM,mouse.x,1-mouse.y);
+    gl.drawArrays(gl.TRIANGLES,0,6);
+    return true;
+  };
+}
+const drawDark=bootGL('bg-dark',FS_DARK);
+const drawLight=bootGL('bg-light',FS_LIGHT);
+const t0=Date.now();
+(function loop(){
+  const t=(Date.now()-t0)/1000;
+  drawDark(t);drawLight(t);
+  requestAnimationFrame(loop);
+})();
+
+// =============== 导航(翻页 / 圆点 / 键盘 / 滚轮 / 触屏) ===============
+const deck=document.getElementById('deck');
+const slides=deck.querySelectorAll('.slide');
+const nav=document.getElementById('nav');
+let idx=0,total=slides.length,lock=false;
+
+// 关键:矫正 deck 宽度为 total * 100vw,否则翻页会错位
+deck.style.width=(total*100)+'vw';
+
+slides.forEach((s,i)=>{
+  const b=document.createElement('button');
+  b.className='dot';b.dataset.i=i;b.setAttribute('aria-label','Page '+(i+1));
+  b.onclick=()=>go(i);
+  nav.appendChild(b);
+});
+
+function go(n){
+  if(lock)return;
+  idx=Math.max(0,Math.min(total-1,n));
+  deck.style.transform=`translateX(${-idx*100}vw)`;
+  nav.querySelectorAll('.dot').forEach((d,i)=>d.classList.toggle('active',i===idx));
+  /* 主题切换:优先读 data-theme,其次从 class(light/dark)推断 */
+  const el=slides[idx];
+  const th=el.dataset.theme || (el.classList.contains('light')?'light':(el.classList.contains('dark')?'dark':'dark'));
+  document.body.classList.toggle('light-bg',th==='light');
+  lock=true;setTimeout(()=>lock=false,700);
+}
+
+addEventListener('keydown',e=>{
+  if(e.key==='ArrowRight'||e.key==='PageDown'||e.key===' '||e.key==='ArrowDown')go(idx+1);
+  if(e.key==='ArrowLeft'||e.key==='PageUp'||e.key==='ArrowUp')go(idx-1);
+  if(e.key==='Home')go(0);
+  if(e.key==='End')go(total-1);
+});
+
+let wheelTO=null,wheelAcc=0;
+addEventListener('wheel',e=>{
+  wheelAcc+=e.deltaY+e.deltaX;
+  if(Math.abs(wheelAcc)>50){go(idx+(wheelAcc>0?1:-1));wheelAcc=0;}
+  clearTimeout(wheelTO);wheelTO=setTimeout(()=>wheelAcc=0,150);
+},{passive:true});
+
+let tx=0,ty=0;
+addEventListener('touchstart',e=>{tx=e.touches[0].clientX;ty=e.touches[0].clientY},{passive:true});
+addEventListener('touchend',e=>{
+  const dx=(e.changedTouches[0].clientX-tx);
+  const dy=(e.changedTouches[0].clientY-ty);
+  if(Math.abs(dx)>50&&Math.abs(dx)>Math.abs(dy))go(idx+(dx<0?1:-1));
+},{passive:true});
+
+go(0);
+</script>
+<script src="https://unpkg.com/lucide@latest/dist/umd/lucide.min.js"></script>
+<script>lucide.createIcons();</script>
+</body>
+</html>

+ 265 - 0
references/checklist.md

@@ -0,0 +1,265 @@
+# 质量检查清单(Checklist)
+
+这个清单来自"一人公司"分享 PPT 的真实迭代过程。每一条都是踩过坑之后总结的,按重要性排序。
+
+生成 PPT 前,先通读一遍;生成后,逐项自检。
+
+---
+
+## 🔴 P0 · 一定不能犯的错
+
+### 0. 生成前必须通过的类名校验(最重要)
+
+**现象**:直接把 layouts.md 的骨架粘到新 HTML,结果样式全部丢失——大标题变成非衬线、数据大字报字体小得像正文、pipeline 多页糊成一坨、图片堆到浏览器底部。
+
+**根因**:如果 `template.html` 的 `<style>` 里没有这些类的定义,浏览器就 fallback 到默认样式。
+
+**做法**:
+- **生成 PPT 前,必须先 `Read` `assets/template.html`**,确认 layouts.md 里用到的类都已定义
+- 最常见遗漏的类:`h-hero / h-xl / h-sub / h-md / lead / meta-row / stat-card / stat-label / stat-nb / stat-unit / stat-note / pipeline-section / pipeline-label / pipeline / step / step-nb / step-title / step-desc / grid-2-7-5 / grid-2-6-6 / grid-2-8-4 / grid-3-3 / frame / img-cap / callout-src`
+- 如果某个类确实缺了,**在 template.html 的 `<style>` 里补上**,不要在每页 inline 重写
+- 生成后打开浏览器,如果看到"大标题是非衬线"或"pipeline 步骤挤在一行",几乎 100% 是这个问题
+
+### 1. 不要用 emoji 作图标
+
+**现象**:在中式杂志风格里用 emoji(🎯 💡 ✅)会立刻破坏格调。
+
+**做法**:用 Lucide 图标库,CDN 方式引用:
+
+```html
+<script src="https://unpkg.com/lucide@latest/dist/umd/lucide.min.js"></script>
+...
+<i data-lucide="target" class="ico-md"></i>
+...
+<script>lucide.createIcons();</script>
+```
+
+常用图标名:`target / palette / search-check / compass / share-2 / crown / check-circle / x-circle / plus / arrow-right / grid-2x2 / network`
+
+### 2. 图片只允许裁底部,左右和顶部绝对不能切
+
+**现象**:用 `aspect-ratio` 撑图,网格会在父容器不足时堆叠或切掉图片关键信息(比如截图上部的标题栏)。
+
+**做法**:图片容器用**固定 height + overflow hidden**,图片走 `object-fit:cover + object-position:top`:
+
+```html
+<figure class="frame-img" style="height:26vh">
+  <img src="screenshot.png">
+</figure>
+```
+
+CSS 里 `.frame-img img` 已经预设 `object-position:top`,只裁底。
+
+**绝不用这种写法**(会在网格中撑破容器):
+
+```html
+<!-- 坏例 -->
+<figure class="frame-img" style="aspect-ratio: 16/9">...</figure>
+```
+
+**例外**:单张主视觉(非网格内)可以用 `aspect-ratio + max-height`,因为父容器会兜底。
+
+### 2b. 亮页面配暗 WebGL = 灰蒙蒙(主题切换没生效)
+
+**现象**:所有 light 页面背景都像蒙了一层灰,甚至 hero light 也灰。
+
+**根因**:JS 根据 slide 的主题切换两张 canvas 的 opacity。如果整个 deck 开场是 hero dark,而没有任何机制能把 bg 切到 light,body 永远不加 `light-bg` 类,`canvas#bg-dark` 一直在上面。
+
+**做法**:
+- 模板里 `go()` 函数已改为从 `classList` 推断主题(`light` / `dark`),所以 **slide 必须明确带 `light` 或 `dark` 类**。不要漏写,更不要用其他自定义主题名
+- hero 页用 `hero light` / `hero dark`,正文页用 `light` / `dark`。只写 `hero` 不带主题色是坏的
+- 一个 deck 里必须至少有一个 **非 hero 的 light 页**,确保 body 有机会加 `light-bg`
+
+### 2b-2. 整个 deck 全是 light,没有节奏
+
+**现象**:除封面 `hero dark` 外,其余所有页面默认写 `light`——视觉平淡,没有呼吸感,白花花一片。
+
+**根因**:layouts.md 的骨架默认全写 `light`,如果只是粘贴骨架不调整主题,就会全亮。
+
+**做法**:
+- **生成前画"主题节奏表"**:每一页写清 `hero dark` / `hero light` / `light` / `dark` 中的哪一个,对齐后再写代码
+- **硬规则**:连续 3 页以上同主题 = 不允许;8 页以上必须有 ≥1 `hero dark` + ≥1 `hero light`;不能全是 `light` 正文页——必须有 `dark` 正文页
+- **按布局选主题**(详见 layouts.md 开头"主题节奏规划"):
+  - 左文右图(Layout 4)、大引用(Layout 8)、图文混排(Layout 10)→ **`light` / `dark` 交替**
+  - 大字报、图片网格、Pipeline、对比页 → `light`(截图/数字/流程需要亮底)
+  - 封面、问题页 → `hero dark`
+  - 章节幕封 → `hero dark` 与 `hero light` 交替
+- **生成后自检**:`grep 'class="slide' index.html`,目视确认节奏有交错
+
+### 2c. chrome 和 kicker 不要写同一句话
+
+**现象**:左上角 `.chrome` 写"Design First · 设计先行",同一页里 `.kicker` 又写"Phase 01 · 设计阶段"——同义翻译,AI 味浓。
+
+**做法**:
+- **chrome = 杂志页眉 / 导航标签**:跨多页可相同(如 "Act II · Workflow"、"Data · Result"、"lukew.com · 2026.04")
+- **kicker = 本页独一份的引导句**:短、有钩子、是大标题的"小前缀"(如 "BUT"、"一个人,做了什么。"、"The Question")
+- 一个描述栏目,一个描述这一页——绝不互相翻译
+
+### 3. 大标题字号不能超过屏宽 / 单字数
+
+**现象**:中文大标题字号设太大(比如 13vw),结果每行只容 1 个字,强制换行非常难看。
+
+**做法**:
+- `h-hero`(最大):10vw,**且标题长度 ≤ 5 字**
+- `h-xl`(次大):6vw-7vw
+- 长标题用 `<br>` 手工断行,不要依赖自动换行
+- 必要时加 `white-space:nowrap`
+
+**示例**:`我不是程序员。`(6 字)用 `h-xl` 7.2vw + nowrap,一行排完。
+
+### 4. 字体分工:标题衬线、正文非衬线
+
+**做法**:
+- 大标题、重点 quote、数字大字 → **衬线字体**(Noto Serif SC + Playfair Display + Source Serif)
+- 正文、描述、pipeline 步骤名 → **非衬线字体**(Noto Sans SC + Inter)
+- 元数据、代码、标签 → **等宽字体**(IBM Plex Mono + JetBrains Mono)
+
+所有字体用 Google Fonts CDN 引入,模板里已预设。
+
+### 4b. 图片不要用 `align-self:end` 贴底
+
+**现象**:左文右图布局里,为了让右列图片和左列 callout 底部对齐,在 `<figure>` 上加 `align-self:end`。结果:
+- 如果父容器不是 grid(比如类名没定义),`align-self` 完全失效,图片掉到文档流最下面被浏览器底栏遮挡
+- 即使是 grid,图片会在 cell 里贴底,低分屏上仍然被 `.foot` 和 `#nav` 圆点遮挡
+
+**做法**:
+- 图文混排**必须用 `.frame.grid-2-7-5`**(或 `.grid-2-6-6`/`.grid-2-8-4`)
+- 右列 `<figure class="frame-img">` 用 **标准比例 16/10 或 4/3 + max-height:56vh**,自然贴顶即可
+- 要让左列 callout 看起来"贴底",给**左列**加 flex column + `justify-content:space-between`,不要动右列
+
+### 4c. 图片不要用原图奇葩比例
+
+**现象**:`aspect-ratio: 2592/1798` 这种从原图复制的比例,在不同屏幕下撑出奇怪的空白或溢出。
+
+**做法**:无论原图什么比例,占位器固定用标准比例 **16/10 / 4/3 / 3/2 / 1/1 / 16/9**。图片自动 `object-fit:cover + object-position:top`,顶部不裁,底部裁掉一点无伤大雅。
+
+### 5. 不要给图片加厚边框 / 阴影
+
+**现象**:为了"高级感"加了强阴影或黑框,瞬间变成商务 PPT。
+
+**做法**:最多 1-4px 的微圆角 + **极淡的底噪**(已在模板里)。不要加 `box-shadow`,不要加 `border`(除非 1px 极淡的灰)。
+
+---
+
+## 🟡 P1 · 排版节奏
+
+### 6. Hero 页和非 hero 页要交替
+
+**推荐节奏**(25-30 页):
+```
+Hero Cover → Act Divider (hero) → 3-4 pages non-hero → Act Divider (hero)
+→ 4-5 pages non-hero → Hero Question → ... → Hero Close
+```
+
+连续 2 页以上 hero 会让人疲劳,连续 4 页以上 non-hero 会让节奏死。
+
+### 7. 大字报页和密集页要交替
+
+大字报(big numbers / hero question)和密集页(pipeline / image grid)交替出现,听众眼睛才不累。
+
+### 8. 同一概念的英文/中文用法要统一
+
+**现象**:一会儿写 "Skills",一会儿写 "技能",一会儿写 "薄承载厚技能",全篇不一致。
+
+**做法**:
+- 术语优先用**英文单词**(Skills / Harness / Pipeline / Workflow),这些都是圈内熟悉词
+- **别硬翻译**,硬翻译反而生硬
+- 整个 deck 里同一个词 1 个写法
+
+### 9. 底部 chrome 的页码要一致
+
+用 `XX / 总页数` 的格式(比如 `05 / 27`)。**不要在右上角加动态页码**(会和 `.chrome` 重复)。
+
+---
+
+## 🟢 P2 · 视觉打磨
+
+### 10. WebGL 背景的遮罩透明度
+
+**dark hero**:遮罩 12-15%(WebGL 明显透出)
+**light hero**:遮罩 16-20%(WebGL 隐约可见,不抢字)
+**普通 light/dark 页**:遮罩 92-95%(几乎不透)
+
+如果页面文字非常少(hero question),遮罩可以再薄些;如果正文密集,必须加厚遮罩确保可读。
+
+### 11. Light hero 的 shader 不能有强中心点
+
+**现象**:Spiral Vortex、径向涟漪在 light 主题下太显眼,像 Windows 98 屏保。
+
+**做法**:light hero 用 FBM 域扭曲驱动的无中心流动,底色保持银/纸色(接近 #F0F0F0 / #FBF8F3),彩虹偏色 subtle(0.05 以下)。
+
+### 12. Dark hero 允许更多视觉冲击
+
+Dark hero 可以用 Holographic Dispersion(钛金色散)等带中心结构的 shader,因为黑底能容纳更多视觉信息。
+
+### 13. 左文右图的对齐
+
+- 左列的文字组 `justify-content:space-between`:标题贴顶,引用框贴底
+- 右列图片 `align-self:end`:和左列的底部元素对齐
+- 网格整体 `align-items:start`(不是 `center` / `end`)
+
+### 14. 图片的微弱圆角
+
+所有 `.frame-img` 和 `.frame-img img` 都加 `border-radius:4px`,视觉上"柔和"但不软。**不要超过 8px**,否则像消费 app UI。
+
+---
+
+## 🔵 P3 · 操作细节
+
+### 15. 图片路径用相对路径
+
+图片放在 `images/` 文件夹下,HTML 里用相对路径 `images/xxx.png`,不要用绝对路径。
+
+### 16. 页码在 `.chrome` 里写死
+
+JS 会动态算总页数并扩展底部翻页圆点,但 `.chrome` 里的 `XX / N` 是写死的。加页/删页时要手工改 N。
+
+### 17. 翻页导航要保留
+
+模板默认支持:← → / 滚轮 / 触屏滑动 / 底部圆点 / Home·End。不要删 JS 里的导航逻辑。
+
+### 18. 不要用 `height:100vh` 硬设,用 `min-height:80vh`
+
+`100vh` 会让内容刚好卡满屏幕,但浏览器工具栏、标签栏会吃掉一部分高度,导致内容溢出。用 `min-height:80vh + align-content:center` 更稳。
+
+---
+
+## 🧪 最终自检清单
+
+生成完 PPT 后,逐项对照这个清单(勾一下):
+
+```
+预检(生成前)
+  □ 已读过 template.html 的 <style>,确认所需类都存在
+  □ 已决定每页用哪个 Layout(1-10)
+  □ 已画出"主题节奏表":每页明确 hero dark / hero light / light / dark
+  □ 节奏表满足硬规则:无连续 3 页同主题 / 有 ≥1 hero dark + ≥1 hero light(8 页以上) / 至少有 1 个 dark 正文页
+  □ `<title>` 已改为实际 deck 标题(grep "[必填]" 应无结果)
+
+内容
+  □ 每一幕的页数比例合理(不会头重脚轻)
+  □ 没有使用 emoji 作图标
+  □ Skills / Harness 等术语用法统一
+  □ 每页的 kicker + 标题 + 正文 三级信息清晰
+
+排版
+  □ 所有大标题没有出现 1 字 1 行的换行
+  □ 图片网格用 height:Nvh 而非 aspect-ratio
+  □ 图片只裁底部,顶部和左右完整
+  □ 衬线/非衬线字体分工符合模板
+  □ Pipeline 多组之间有明显分隔
+
+视觉
+  □ hero 页和 non-hero 页交替
+  □ WebGL 背景在 hero 页可见
+  □ 图片有微弱圆角
+  □ 没有沉重的阴影和边框
+
+交互
+  □ ← → 翻页正常
+  □ 底部圆点数量与总页数匹配
+  □ chrome 里的页码和实际页号一致
+  □ ESC 键触发索引视图(如果保留)
+```
+
+全勾完,才是合格的 PPT。

+ 363 - 0
references/components.md

@@ -0,0 +1,363 @@
+# 组件参考 · Components
+
+这是 `magazine-web-ppt` skill 的组件手册。template.html 已经定义好了所有样式,这里只写"这个组件长什么样、怎么用"。
+
+## 目录
+
+- [基础 Slide 外壳](#基础-slide-外壳)
+- [字体 Typography](#字体-typography)
+- [Chrome & Foot](#chrome--foot)
+- [Callout 引用框](#callout-引用框)
+- [Stat 数字矩阵](#stat-数字矩阵)
+- [Platform 平台卡](#platform-平台卡)
+- [Rowline 表格行](#rowline-表格行)
+- [Pillar 支柱卡](#pillar-支柱卡)
+- [Tag & Kicker](#tag--kicker)
+- [Figure 图片框](#figure-图片框)
+- [Icons 图标](#icons-图标)
+- [Ghost 巨型背景字](#ghost-巨型背景字)
+- [Highlight 荧光标记](#highlight-荧光标记)
+
+---
+
+## 基础 Slide 外壳
+
+每一页都是一个 `<section class="slide ...">`。必须包含 `data-theme` 属性(`light` 或 `dark`),JS 翻页时会根据这个属性切换背景。
+
+```html
+<section class="slide light" data-theme="light">   <!-- 浅色页 -->
+<section class="slide dark" data-theme="dark">     <!-- 深色页 -->
+<section class="slide light hero" data-theme="light">  <!-- Hero 页:浅色 + 薄遮罩透出 WebGL -->
+<section class="slide dark hero" data-theme="dark">    <!-- Hero 页:深色 + 薄遮罩 -->
+```
+
+**light vs dark 的使用:交替使用**,每 2-3 页切换一次主题,避免连续超过 3 页同色。翻页时 WebGL 背景会自动在两个 shader 之间渐变过渡。
+
+**hero 类的使用**:只给视觉主导的页面加(封面、金句页、章节过渡、结尾)。加 `hero` 后遮罩降到 12-16%,WebGL 背景会大幅透出,所以不要在 hero 页上放太多文字。
+
+---
+
+## 字体 Typography
+
+字体分工是本模板最重要的规则,严禁混用。
+
+| Class | 用途 | 字体 |
+|---|---|---|
+| `.display` | 超大号英文(Hero 页) | Playfair Display 700, 11vw |
+| `.display-zh` | 超大号中文标题 | Noto Serif SC 700, 7.8vw |
+| `.h1-zh` | 页面主标题 | Noto Serif SC 700, 4.6vw |
+| `.h2-zh` | 副标题 | Noto Serif SC 600, 3.2vw |
+| `.h3-zh` | 流水线步骤标题 | Noto Serif SC 500, 1.9vw |
+| `.lead` | 引导段(比 body 大) | Noto Serif SC 400, 1.9vw |
+| `.body-zh` | **正文/描述(非衬线)** | Noto Sans SC 400, 1.22vw |
+| `.body-serif` | 正文(衬线) | Noto Serif SC 400, 1.3vw |
+| `.kicker` | 小节提示(标题上方) | IBM Plex Mono, 12px uppercase |
+| `.meta` | 元信息标签 | IBM Plex Mono, 0.88vw uppercase |
+| `.big-num` | 巨型数字 | Playfair Display 800, 10vw |
+| `.mid-num` | 中号数字 | Playfair Display 700, 5.5vw |
+
+**核心规则**:
+- **衬线**(`serif-zh` / `serif-en`):标题、重点金句、数字 —— 用于"视觉重音"
+- **非衬线**(`sans-zh`):正文描述、大段阅读内容 —— 用于"信息密度"
+- **等宽**(`mono`):kicker、meta、foot 的英文标签 —— 用于"装饰节奏"
+
+**强调技巧**:
+- `<em class="en">英文词</em>` —— 把英文词渲染成 Playfair Display 斜体(很好看)
+- `<em style="opacity:.65">短语</em>` —— 让标题后半段淡出,制造节奏
+
+---
+
+## Chrome & Foot
+
+每一页的顶部和底部的元信息条。几乎所有页都应该有。
+
+```html
+<div class="chrome">
+  <div class="left">
+    <span>第一幕 · 硬数据</span>
+    <span class="sep"></span>
+    <span>Act I</span>
+  </div>
+  <div class="right"><span>02 / 27</span></div>
+</div>
+
+<!-- ... 页面主体 ... -->
+
+<div class="foot">
+  <div class="title">项目名 · CodePilot | github.com/codepilot</div>
+  <div>Act I · Dev Numbers</div>
+</div>
+```
+
+**规则**:
+- `chrome.right` 总是放页码 `NN / TOTAL` (TOTAL 为总页数)
+- `foot.title` 是中文说明,`foot.right` 是英文 act 标记
+- chrome 和 foot 共同构成杂志感的"页眉页脚"
+
+---
+
+## Callout 引用框
+
+展示金句 / 关键观点 / 他人引言。
+
+```html
+<div class="callout" style="max-width:80vw">
+  <div class="q-big">"这东西在三年前,<br>需要一个十人团队做一年。"</div>
+  <span class="cite">— 一个观察者的判断</span>
+</div>
+```
+
+变体:
+- 不带 cite:去掉 `<span class="cite">` 即可
+- 带英文金句:`<em class="en">"Thin Harness, Fat Skills."</em>`
+- 在 hero 页使用:外层加 `style="position:relative;z-index:2"`(避免被背景遮罩盖住)
+
+---
+
+## Stat 数字矩阵
+
+展示数据指标,常与 `.grid-6` / `.grid-4` 配合。
+
+```html
+<div class="grid-6">
+  <div class="stat">
+    <span class="m">Duration</span>
+    <span class="n">64<em style="font-size:.4em;opacity:.5;font-style:normal"> 天</em></span>
+    <span class="l">从 0 到现在</span>
+  </div>
+  <!-- ... 更多 stat ... -->
+</div>
+```
+
+三段式结构:`.m` 等宽小标签 → `.n` 巨型数字 → `.l` 描述说明。数字后的单位用 `<em>` 缩小到 0.4em,opacity 0.5。
+
+**常用布局容器**:
+- `.grid-6` — 3×2 网格(最常用,6 个 stat)
+- `.grid-4` — 2×2 网格(4 个 stat)
+- `.grid-3` — 3 等分单行(3 个 stat / pillar)
+
+---
+
+## Platform 平台卡
+
+展示社交平台 / 渠道 + 粉丝数。
+
+```html
+<div class="plat">
+  <div class="sub">Weibo</div>
+  <div class="name">微博</div>
+  <div class="nb">289K</div>
+</div>
+```
+
+可选第四行(补充说明):
+```html
+<div class="body-zh" style="font-size:max(11px,.8vw);opacity:.5;margin-top:.6vh">
+  含小绿书同步
+</div>
+```
+
+**"Also On" 变体**(补充平台):
+```html
+<div class="plat" style="border-top-style:dashed;opacity:.72">
+  <div class="sub">Also On</div>
+  <div class="body-zh" style="font-weight:600;margin-top:.8vh">
+    B 站 · 知乎
+  </div>
+</div>
+```
+
+---
+
+## Rowline 表格行
+
+列表式内容,每行一个条目。
+
+```html
+<div class="rowline">
+  <div class="k">CLAUDE.md</div>
+  <div class="v">你该怎么做事 —— 行为规则 + 工作偏好 + 禁止事项</div>
+  <div class="m">EMPLOYEE · HANDBOOK</div>
+</div>
+```
+
+三列结构:`.k` 衬线关键词 · `.v` 正文描述 · `.m` 等宽标签(右对齐)。第一个和最后一个 rowline 自动加上下边框。
+
+**变体:2 列**:`style="grid-template-columns:1fr 3fr"` 去掉 `.m` 列。
+
+---
+
+## Pillar 支柱卡
+
+三支柱结构,常用于"概念并列"类型页面。
+
+```html
+<div class="grid-3">
+  <div class="pillar">
+    <div class="ic">01</div>
+    <div class="t">三层<br>文档体系</div>
+    <div class="d">CLAUDE.md<br>+ 项目知识库<br>+ 护栏文件</div>
+  </div>
+  <!-- ... 更多 pillar ... -->
+</div>
+```
+
+**带图标的 pillar(用于强调性页面)**:
+```html
+<div class="pillar" style="padding:4vh 2vw;border:1px solid currentColor;border-color:rgba(10,10,11,.2)">
+  <div class="ic"><i data-lucide="compass" class="ico-lg"></i></div>
+  <div class="t">判断力</div>
+  <div class="d">决策和方向的权威。<br>取舍、品味、方向感。</div>
+</div>
+```
+
+`.ic` 可以是序号(`01 / 02 / 03` 或 `A. / B. / C.`),也可以是 Lucide 图标。
+
+---
+
+## Tag & Kicker
+
+**Kicker** 是标题上方的小提示文字(等宽、全大写、小字号):
+```html
+<div class="kicker">过去 64 天 · 开发篇</div>
+<div class="h1-zh">一个人,做了什么。</div>
+```
+
+**Tag** 是独立的标签胶囊(带边框):
+```html
+<div style="display:flex;gap:1.6vw;flex-wrap:wrap">
+  <div class="tag">早上 10 点起床</div>
+  <div class="tag">周二 / 四下午健身</div>
+  <div class="tag">晚上照样看剧 · 玩游戏</div>
+</div>
+```
+
+---
+
+## Figure 图片框
+
+**这是本模板最容易踩坑的组件,务必遵守以下规则**。
+
+### 基础结构
+
+```html
+<figure class="tile">
+  <div class="frame-img" style="height:26vh">
+    <img src="图片素材/xxx.png" alt="说明">
+  </div>
+  <figcaption class="frame-cap">
+    <span class="pf">推特 · Twitter</span>
+    <span class="nb">137K</span>
+  </figcaption>
+</figure>
+```
+
+### 关键约束(血泪经验,不要违反)
+
+1. **必须用 `height:Nvh` 固定高度**,不要用 `aspect-ratio`。
+   - 原因:用 aspect-ratio 在网格里会撑破父容器,导致图片堆叠。
+   - 推荐尺寸:`height:18vh` (紧凑条形) / `22vh` (标准网格) / `26vh` (突出展示) / `28vh` (大图)。
+
+2. **`object-position:top center`(已在 CSS 里设好)**,只允许裁掉底部。
+   - 严禁裁剪左右和顶部 —— 这是图片的核心身份信息区。
+
+3. **网格里多张图时,用内联 grid 而不是 `grid-3`**:
+   ```html
+   <div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:1vh 1.2vw">
+     <figure class="tile">...</figure>
+     <figure class="tile">...</figure>
+     <figure class="tile">...</figure>
+   </div>
+   ```
+
+4. **图片与布局其他部分对齐**:figure 单独加 `align-self:end` 让图片贴底。
+
+### Frame Caption 变体
+
+```html
+<!-- 标准:左 figure 名,右数字 -->
+<figcaption class="frame-cap">
+  <span class="pf">推特 · Twitter</span>
+  <span class="nb">137K</span>
+</figcaption>
+
+<!-- 带编号 -->
+<figcaption class="frame-cap">
+  <span class="idx">01</span>
+  <span class="pf">AI 润色</span>
+  <span>Polish</span>
+</figcaption>
+```
+
+### 图片占位(设计阶段占位符)
+
+图片还没有就位时,用虚线框占位:
+```html
+<div class="img-slot r-4x3">  <!-- r-4x3 / r-16x9(default) / r-3x2 / r-1x1 -->
+  <span class="plus">+</span>
+  <span class="label">GitHub 截图位置</span>
+</div>
+```
+
+---
+
+## Icons 图标
+
+**严禁使用 emoji**。用 Lucide via CDN(template.html 已引入)。
+
+```html
+<i data-lucide="compass" class="ico-lg"></i>     <!-- 大图标(pillar 用) -->
+<i data-lucide="target" class="ico-md"></i>      <!-- 中图标(列表项用) -->
+<i data-lucide="check-circle" class="ico-sm"></i>  <!-- 小图标(inline 用) -->
+```
+
+**常用 Lucide 图标名**(按含义分组):
+
+- 判断类:`compass`, `target`, `crosshair`, `search-check`
+- 关系类:`share-2`, `users`, `network`, `link`, `handshake`
+- 品牌类:`crown`, `gem`, `award`, `star`, `badge-check`
+- 流程类:`workflow`, `route`, `arrow-right-left`, `repeat`
+- 数据类:`grid-2x2`, `bar-chart-3`, `trending-up`, `activity`
+- 审美类:`palette`, `brush`, `eye`, `sparkles`
+- 对错类:`check-circle`, `x-circle`, `check`, `x`
+- 方向类:`arrow-right`, `arrow-up-right`, `corner-down-right`
+
+**图标与文字 inline 组合**:
+```html
+<div class="h3-zh" style="display:flex;align-items:center;gap:.8em">
+  <i data-lucide="target" class="ico-md"></i>
+  判断 — 什么值得写
+</div>
+```
+
+---
+
+## Ghost 巨型背景字
+
+用作"装饰性背景字",极低透明度,营造杂志感。
+
+```html
+<div class="ghost" style="right:-6vw;top:-8vh">BUT</div>
+<div class="ghost" style="left:-8vw;bottom:-18vh;font-style:italic">Harness</div>
+```
+
+- 字号 34vw,opacity 0.06
+- 常用定位:`right:-6vw;top:-8vh`(右上超出)/ `left:-8vw;bottom:-18vh`(左下超出)
+- 内容:英文单词或数字(章节序号 01/02/03、关键词 BUT/NOW/HERE)
+
+**注意**:使用 ghost 的页面里,其他内容要加 `position:relative;z-index:2` 避免被压到下面。
+
+---
+
+## Highlight 荧光标记
+
+行内短语的"荧光笔"效果:
+
+```html
+<span class="hi">不是</span>
+<span class="hi">一次性爆发</span>
+```
+
+在文字底部生成一条半透明高亮条。深色主题用亮条,浅色主题用暗条(CSS 已处理)。
+
+**适合场景**:只对关键 1-3 个词使用,不要大面积用。

+ 630 - 0
references/layouts.md

@@ -0,0 +1,630 @@
+# 页面布局库(Layouts)
+
+本文档收录 10 种最常用的页面布局骨架。每种都是一个完整可粘贴的 `<section class="slide ...">...</section>` 代码块,直接替换文案/图片即可使用。
+
+---
+
+## ⚠️ 生成前必读(Pre-flight)
+
+### A. 类名必须来自 template.html
+
+layouts.md 使用的所有类(`h-hero` / `h-xl` / `h-sub` / `h-md` / `lead` / `meta-row` / `stat-card` / `stat-label` / `stat-nb` / `stat-unit` / `stat-note` / `pipeline-section` / `pipeline-label` / `pipeline` / `step` / `step-nb` / `step-title` / `step-desc` / `grid-2-7-5` / `grid-2-6-6` / `grid-2-8-4` / `grid-3-3` / `grid-6` / `grid-3` / `grid-4` / `frame` / `frame-img` / `img-cap` / `callout` / `callout-src` / `kicker`)都在 `assets/template.html` 的 `<style>` 块里预定义。
+
+**不要发明新类名**。如果必须自定义,用 `style="..."` inline 写。生成前若不确定某个类是否存在,grep template.html 确认。
+
+### B. 图片比例规范(非常重要)
+
+**永远用标准比例**,不要用原图 `aspect-ratio: 2592/1798` 这种奇葩比例:
+
+| 场景 | 推荐比例 | 写法 |
+|------|---------|------|
+| 左文右图 主图 | 16:10 或 4:3 | `aspect-ratio:16/10; max-height:54vh` |
+| 图片网格(多图对比) | 统一 | **固定 `height:26vh`,不用 aspect-ratio** |
+| 左小图 + 右文字 | 1:1 或 3:2 | `aspect-ratio:1/1; max-width:40vw` |
+| 全屏主视觉 | 16:9 | `aspect-ratio:16/9; max-height:64vh` |
+| 图文混排小插图 | 3:2 | `aspect-ratio:3/2; max-width:30vw` |
+
+图片必须包在 `<figure class="frame-img">` 里,里面的 `<img>` 会自动 `object-fit:cover + object-position:top center`,只裁底部,不裁顶/左/右。
+
+### C. 图片定位准则(避免图片堆到页面最底部、被浏览器工具栏遮挡)
+
+**错误做法**(已踩坑,不要再犯):
+- 在非 grid 容器里用 `align-self:end`:`align-self` 在 flex/grid 之外完全无效,图片会掉到文档流末尾堆底
+- 用 `position:absolute + bottom:0` 把图"固定"到底:会被底部 `.foot` 和 `#nav` 圆点遮挡
+- 单张图片只写 `height:N vh` 不限 `max-height`:在低分屏会撑出视口
+
+**正确做法**:
+- 图文混排**必须用 `.frame.grid-2-7-5`**(或 `.grid-2-6-6` / `.grid-2-8-4`)的 grid 结构
+- grid 容器默认 `align-items:start`(已在 template 中设置),图片自然贴到 cell 顶端
+- 如果需要"图片底对齐左列 callout":**左列用 flex column + `justify-content:space-between`**(让 callout 自己贴左列底),**右列 figure 直接保持 align-items:start 即可**,不要加 `align-self:end`
+- 所有 grid 父容器建议加 inline `style="padding-top:6vh"`,给标题区留呼吸空间
+
+### D. 主题色与主题节奏
+
+- 主题色从 `references/themes.md` 的 5 套预设里选一套,不允许自定义 hex 值
+- 主题节奏(每页用 light / dark / hero light / hero dark 哪一个)在下文"主题节奏规划"一节有硬规则,生成前必读
+- 两件事都要在挑布局之前决定,避免返工
+
+---
+
+## 0. 基础结构(所有 slide 都一样)
+
+```html
+<section class="slide [light|dark|hero light|hero dark]">
+  <div class="chrome">
+    <div>上下文标签 · 子标签</div>
+    <div>ACT · 页号 / 总页数</div>
+  </div>
+  <!-- 主内容 -->
+  <div class="foot">
+    <div>页码说明 · Page Description</div>
+    <div>— · —</div>
+  </div>
+</section>
+```
+
+- 非 hero 页建议加 `light` 或 `dark` 主题;hero 页加 `hero light` 或 `hero dark`(参与 WebGL 主题插值)
+- `chrome` 和 `foot` 是可选但推荐保留的上下左右四角元数据
+- **hero 页用于章节封面/开场/收束/转场**,非 hero 页用于正文
+
+### ⚠️ chrome 和 kicker 不要写同一句话
+
+这是最常见的内容重复问题。两者在语义上完全不同的维度:
+
+| 位置 | 角色 | 内容性质 | 例子 |
+|------|------|---------|------|
+| `.chrome` 左上 | **杂志页眉 / 导航元数据** | 稳定的"栏目名"或"章节分类",跨多页可以相同 | "Act II · Workflow" / "Data · Result" / "lukew.com · 2026.04" |
+| `.chrome` 右上 | **页号 + 幕号** | 固定格式 | "Act II · 15 / 25" |
+| `.kicker` | **这一页独一份的引导句** | 是大标题的"小前缀",像杂志大标题上方的一行话,每页都应不同 | "BUT" / "一个人,做了什么。" / "Phase 01 · 设计阶段" |
+
+**反例**(已踩坑):chrome 写"设计先行 · Design First",kicker 又写"Phase 01 · 设计阶段"——意思重复,读者一眼就觉得 AI 生成的。
+
+**正确做法**:chrome 是**栏目标签**(稳定、跨页可复用),kicker 是**本页钩子**(短句、有戏剧性),两者互为补充,不互相翻译。
+
+### ⚠️ 主题节奏规划(必读 · 生成前必做)
+
+**核心机制**:每页 `<section>` 必须带 `light` / `dark` / `hero light` / `hero dark` 之一。JS 根据 class 推断主题,决定 body 加不加 `light-bg`,从而切换暗/亮两张 WebGL canvas 哪张在前。不带主题或写自定义名 = fallback 出错。
+
+#### 按布局的主题默认值
+
+| Layout | 默认主题 | 原因 |
+|---|---|---|
+| 1. 开场封面 | `hero dark` | 开场仪式感,暗底强冲击 |
+| 2. 章节幕封 | `hero dark` 与 `hero light` **必须交替** | 呼吸节奏 |
+| 3. 大字报(数据) | `light` | 数字需纸白底;多幕连发时可偶插 `dark` |
+| 4. 左文右图 | **`light` / `dark` 交替** | 正文节奏主力 |
+| 5. 图片网格 | `light` | 截图需亮底 |
+| 6. Pipeline | `light` | 流程图需清晰 |
+| 7. 问题页 | `hero dark` | 强视觉冲击默认 |
+| 8. 大引用 | **`dark` 优先**,偶用 `light` | 金句仪式感靠暗底 |
+| 9. 对比页 | `light` | 双列需清晰 |
+| 10. 图文混排 | **`light` / `dark` 交替** | 节奏 |
+
+#### 节奏硬规则(生成后 grep 自检)
+
+- ❌ **禁止**连续 3 页以上相同主题(包括 light 堆叠和 dark 堆叠)
+- ❌ **禁止**8 页以上的 deck 没有至少 1 个 `hero dark` + 1 个 `hero light`
+- ❌ **禁止**整个 deck 只有 `light` 正文页没有任何 `dark` 正文页——会显得平淡、没呼吸
+- ✅ **推荐**每 3-4 页插入 1 个 hero(封面/幕封/问题/大引用)
+
+#### 8 页节奏模板(可直接套用)
+
+| 页 | 主题 | 布局 | 备注 |
+|---|---|---|---|
+| 1 | `hero dark` | 封面 | 开场 |
+| 2 | `light` | 大字报 | 数据抛出 |
+| 3 | `dark` | 左文右图 | 对比/故事 |
+| 4 | `light` | Pipeline | 流程 |
+| 5 | `hero light` | 章节幕封 | 呼吸 |
+| 6 | `dark` | 左文右图 or 大引用 | |
+| 7 | `hero dark` | 问题页 | 悬念收束 |
+| 8 | `light` | 大引用/结尾 | 收尾 |
+
+**先画这张表对齐,再动手写 slide**。跳过规划直接粘骨架 = 全是 light。
+
+---
+
+## Layout 1: 开场封面(Hero Cover)
+
+```html
+<section class="slide hero dark">
+  <div class="chrome">
+    <div>A Talk · 2026.04.22</div>
+    <div>Vol.01</div>
+  </div>
+  <div class="frame" style="display:grid; gap:4vh; align-content:center; min-height:80vh">
+    <div class="kicker">私享会 · 李继刚</div>
+    <h1 class="h-hero">一人公司</h1>
+    <h2 class="h-sub">被 AI 折叠的组织</h2>
+    <p class="lead" style="max-width:60vw">
+      一个 AI 创作者 —— 在 64 天里做了 11 万行代码、在 9 个平台上持续输出,生活节奏几乎没有被改变。
+    </p>
+    <div class="meta-row">
+      <span>歸藏 Guizang</span><span>·</span><span>独立创作者 / CodePilot 作者</span>
+    </div>
+  </div>
+  <div class="foot">
+    <div>一场关于 AI · 组织 · 个体的分享</div>
+    <div>— 2026 —</div>
+  </div>
+</section>
+```
+
+**要点**:
+- 用 `hero dark` 让 WebGL 背景在大部分区域透出
+- `h-hero` 是最大字号(10vw),这里作标题主视觉
+- 用 `min-height:80vh + align-content:center` 让内容整体垂直居中
+- 不需要 `.chrome` 里写页码,封面页自成一体
+
+---
+
+## Layout 2: 章节幕封(Act Divider)
+
+```html
+<section class="slide hero light">
+  <div class="chrome">
+    <div>第一幕 · 硬数据</div>
+    <div>Act I · 01 / 25</div>
+  </div>
+  <div class="frame" style="display:grid; gap:6vh; align-content:center; min-height:80vh">
+    <div class="kicker">Act I</div>
+    <h1 class="h-hero" style="font-size:8.5vw">硬数据</h1>
+    <p class="lead" style="max-width:55vw">
+      先看数字,再谈方法。
+    </p>
+  </div>
+  <div class="foot">
+    <div>第一幕引子</div>
+    <div>— · —</div>
+  </div>
+</section>
+```
+
+**要点**:
+- 极简,只需要 kicker + 大标题 + 一行引语
+- 两个幕的封面可以交替 `hero light` / `hero dark`,制造节奏
+- `h-hero` 字号可以从 10vw 调到 8.5vw 适配长短
+
+---
+
+## Layout 3: 数据大字报(Big Numbers Grid)
+
+```html
+<section class="slide light">
+  <div class="chrome">
+    <div>过去 64 天 · 开发篇</div>
+    <div>Act I / Dev · 02 / 25</div>
+  </div>
+  <div class="frame" style="padding-top:6vh">
+    <div class="kicker">一个人,做了什么。</div>
+    <h2 class="h-xl">过去 64 天</h2>
+    <p class="lead" style="margin-bottom:5vh">从 0 到开源 CodePilot。</p>
+
+    <div class="grid-6" style="margin-top:6vh">
+      <div class="stat-card">
+        <div class="stat-label">Duration</div>
+        <div class="stat-nb">64 <span class="stat-unit">天</span></div>
+        <div class="stat-note">从 0 到现在</div>
+      </div>
+      <div class="stat-card">
+        <div class="stat-label">Lines of Code</div>
+        <div class="stat-nb">110K+</div>
+        <div class="stat-note">一行行写到 11 万+</div>
+      </div>
+      <div class="stat-card">
+        <div class="stat-label">GitHub Stars</div>
+        <div class="stat-nb">5,166</div>
+        <div class="stat-note">一个开源仓库</div>
+      </div>
+      <div class="stat-card">
+        <div class="stat-label">Downloads</div>
+        <div class="stat-nb">41K+</div>
+        <div class="stat-note">装到了几万台电脑里</div>
+      </div>
+      <div class="stat-card">
+        <div class="stat-label">AI Providers</div>
+        <div class="stat-nb">19</div>
+        <div class="stat-note">跨平台接入</div>
+      </div>
+      <div class="stat-card">
+        <div class="stat-label">Commits</div>
+        <div class="stat-nb">608+</div>
+        <div class="stat-note">没有协作者</div>
+      </div>
+    </div>
+  </div>
+  <div class="foot">
+    <div>项目 · CodePilot | github.com/codepilot</div>
+    <div>Act I · Dev Numbers</div>
+  </div>
+</section>
+```
+
+**要点**:
+- 3×2 或 4×2 网格最稳(见 `.grid-6`)
+- 每个 `stat-card` 结构固定:label(英文小字)→ nb(大字数字)→ note(注释)
+- 数字建议 2-3 位字符(太长会溢出),用 K / M 简写
+- 留 5vh 以上的上方缓冲,让标题区先抢眼球
+
+---
+
+## Layout 4: 左文右图(Quote + Image)
+
+```html
+<section class="slide light">
+  <div class="chrome">
+    <div>身份反差 · The Twist</div>
+    <div>03 / 25</div>
+  </div>
+  <div class="frame grid-2-7-5" style="padding-top:6vh">
+    <!-- 左列:标题 + 正文 + callout,flex column 让 callout 贴列底 -->
+    <div style="display:flex; flex-direction:column; justify-content:space-between; gap:3vh">
+      <div>
+        <div class="kicker">BUT</div>
+        <h2 class="h-xl" style="white-space:nowrap; font-size:7.2vw">
+          我不是程序员。
+        </h2>
+        <p class="lead" style="margin-top:3vh">
+          大学毕业之后再也没写过一行代码。过去十年做的是 UI 设计和 AI 特效。
+        </p>
+      </div>
+      <div class="callout">
+        "这东西在三年前,<br>
+        需要一个十人团队做一年。"
+        <div class="callout-src">— 一个观察者的判断</div>
+      </div>
+    </div>
+    <!-- 右列:图片用标准 16/10 比例 + max-height,不要 align-self:end -->
+    <figure class="frame-img" style="aspect-ratio:16/10; max-height:56vh">
+      <img src="images/codepilot.png" alt="CodePilot 产品截图">
+      <figcaption class="img-cap">CodePilot · 产品截图</figcaption>
+    </figure>
+  </div>
+  <div class="foot">
+    <div>Page 03 · 我不是程序员</div>
+    <div>— · —</div>
+  </div>
+</section>
+```
+
+**要点**:
+- 用 `grid-2-7-5`(左 7 份、右 5 份),`align-items:start` 已在 template 预设
+- **左列**用 flex column + `justify-content:space-between`:标题贴顶,callout 自然贴底
+- **右列图片** **不要加 `align-self:end`**。会让图片滑到 cell 底部,低分屏下被浏览器工具栏遮挡
+- 图片必须用 **标准比例 16/10 或 4/3 + `max-height:56vh`**,不要用原图奇葩比例(`2592/1798` 这种)
+
+---
+
+## Layout 5: 图片网格(多图对比)
+
+```html
+<section class="slide light">
+  <div class="chrome">
+    <div>平台粉丝实证</div>
+    <div>Act I / Ops · 05 / 27</div>
+  </div>
+  <div class="frame" style="padding-top:5vh">
+    <div class="kicker">Proof · 粉丝实证</div>
+    <h2 class="h-xl">10 个平台 · 6 张截图</h2>
+
+    <div class="grid-3-3" style="margin-top:4vh">
+      <figure class="frame-img" style="height:26vh">
+        <img src="images/weibo.png" alt="微博 289K">
+        <figcaption class="img-cap">微博 · 289K</figcaption>
+      </figure>
+      <figure class="frame-img" style="height:26vh">
+        <img src="images/twitter.png" alt="推特 137K">
+        <figcaption class="img-cap">推特 · 137K</figcaption>
+      </figure>
+      <figure class="frame-img" style="height:26vh">
+        <img src="images/wechat.png" alt="公众号 96K">
+        <figcaption class="img-cap">公众号 · 96K</figcaption>
+      </figure>
+      <figure class="frame-img" style="height:26vh">
+        <img src="images/jike.png" alt="即刻 26K">
+        <figcaption class="img-cap">即刻 · 26K</figcaption>
+      </figure>
+      <figure class="frame-img" style="height:26vh">
+        <img src="images/xhs.png" alt="小红书 19K">
+        <figcaption class="img-cap">小红书 · 19K</figcaption>
+      </figure>
+      <figure class="frame-img" style="height:26vh">
+        <img src="images/douyin.png" alt="抖音 10K">
+        <figcaption class="img-cap">抖音 · 10K</figcaption>
+      </figure>
+    </div>
+  </div>
+  <div class="foot">
+    <div>截图时间 · 2026.04</div>
+    <div>Page 05 · 粉丝实证</div>
+  </div>
+</section>
+```
+
+**要点**:
+- 关键:每个 `frame-img` 必须写死 `height:NNvh`(不要用 `aspect-ratio`),否则网格会撑破
+- 图片会自动 `object-fit:cover + object-position:top`,只裁底部
+- 用 `.grid-3-3`(3×2)或 `.grid-3`(3×1)承载
+
+---
+
+## Layout 6: 两列流水线(Pipeline)
+
+```html
+<section class="slide light">
+  <div class="chrome">
+    <div>我的工作流 · Workflow</div>
+    <div>Act II · 15 / 27</div>
+  </div>
+  <div class="frame">
+    <div class="kicker">Pipeline · 流水线</div>
+    <h2 class="h-xl">两条流水线</h2>
+
+    <!-- 第一组:文本侧 -->
+    <div class="pipeline-section">
+      <div class="pipeline-label">文本侧 · Text Pipeline</div>
+      <div class="pipeline">
+        <div class="step">
+          <div class="step-nb">01</div>
+          <div class="step-title">Draft</div>
+          <div class="step-desc">AI 帮我起草初稿</div>
+        </div>
+        <div class="step">
+          <div class="step-nb">02</div>
+          <div class="step-title">Polish</div>
+          <div class="step-desc">AI 润色去 AI 味</div>
+        </div>
+        <div class="step">
+          <div class="step-nb">03</div>
+          <div class="step-title">Morph</div>
+          <div class="step-desc">AI 变形成推特 / 小红书</div>
+        </div>
+        <div class="step">
+          <div class="step-nb">04</div>
+          <div class="step-title">Illustrate</div>
+          <div class="step-desc">AI 生成信息图</div>
+        </div>
+        <div class="step">
+          <div class="step-nb">05</div>
+          <div class="step-title">Distribute</div>
+          <div class="step-desc">一键分发 9 平台</div>
+        </div>
+      </div>
+    </div>
+
+    <!-- 第二组:视频侧 -->
+    <div class="pipeline-section">
+      <div class="pipeline-label">视觉 · 视频侧 · Video Pipeline</div>
+      <div class="pipeline">
+        <div class="step">
+          <div class="step-nb">06</div>
+          <div class="step-title">Cut</div>
+          <div class="step-desc">AI 帮我剪辑</div>
+        </div>
+        <div class="step">
+          <div class="step-nb">07</div>
+          <div class="step-title">Wrap</div>
+          <div class="step-desc">AI 帮我包装</div>
+        </div>
+        <div class="step">
+          <div class="step-nb">08</div>
+          <div class="step-title">Cover</div>
+          <div class="step-desc">AI 生成封面</div>
+        </div>
+      </div>
+    </div>
+  </div>
+  <div class="foot">
+    <div>Page 15 · 我的内容工厂</div>
+    <div>Workflow</div>
+  </div>
+</section>
+```
+
+**要点**:
+- 用 `.pipeline-section` 分组 + `.pipeline-label` 作组标题
+- 两组之间用 3.6vh 的间距 + 顶部细分隔线(已在 CSS 中预设)
+- 每个 step 是固定的 nb → title → desc 结构
+- 步骤数不限但单行最好 ≤5 个,否则换到第二 pipeline
+
+---
+
+## Layout 7: 悬念收束 / 问题页(Hero Question)
+
+```html
+<section class="slide hero dark">
+  <div class="chrome">
+    <div>留给你的问题</div>
+    <div>24 / 27</div>
+  </div>
+  <div class="frame" style="display:grid; gap:8vh; align-content:center; min-height:80vh">
+    <div class="kicker">The Question</div>
+    <h1 class="h-hero" style="font-size:7vw; line-height:1.15">
+      你的公司里,<br>
+      哪些岗位本来就<br>
+      不该由人来做?
+    </h1>
+    <p class="lead" style="max-width:50vw">
+      这个问题,不是技术问题,是架构问题。
+    </p>
+  </div>
+  <div class="foot">
+    <div>Page 24 · The Question</div>
+    <div>— · —</div>
+  </div>
+</section>
+```
+
+**要点**:
+- Hero 页留白越多越好,只放一个问题
+- `h-hero` 字号视长度调整(7vw 适合 3 行,10vw 适合 1 行)
+- 用 `<br>` 手工断行,确保断点在语义处
+- 尾巴可以再给一行 `lead` 作为点破
+
+---
+
+## Layout 8: 大引用页(Big Quote · 衬线金句)
+
+```html
+<section class="slide light">
+  <div class="chrome">
+    <div>The Takeaway · 核心金句</div>
+    <div>18 / 25</div>
+  </div>
+  <div class="frame" style="display:grid; gap:5vh; align-content:center; min-height:80vh">
+    <div class="kicker">Quote · 金句</div>
+    <blockquote style="font-family:var(--serif-zh); font-weight:700; font-size:5.8vw; line-height:1.2; letter-spacing:-.01em; max-width:72vw">
+      "没有交接,<br>所有人都在构建。"
+    </blockquote>
+    <p class="lead" style="max-width:55vw; opacity:.65">
+      Without the handoff, everyone builds.<br>
+      And that makes all the difference.
+    </p>
+    <div class="meta-row">
+      <span>— Luke Wroblewski</span><span>·</span><span>2026.04.16</span>
+    </div>
+  </div>
+  <div class="foot">
+    <div>Page 18 · 金句</div>
+    <div>— · —</div>
+  </div>
+</section>
+```
+
+**要点**:
+- 整页留白,只放一个大引用 + 出处
+- `<blockquote>` 用 inline style 单独放大(5-6vw),不要用 `h-hero`(那是页面主标题的命名)
+- 下面跟随英文原文(lead · opacity:.65)制造层级
+- 配 `meta-row` 写出处 · 日期
+
+---
+
+## Layout 9: 并列对比(A vs B · 旧 vs 新)
+
+```html
+<section class="slide light">
+  <div class="chrome">
+    <div>旧 vs 新 · The Shift</div>
+    <div>12 / 25</div>
+  </div>
+  <div class="frame" style="padding-top:5vh">
+    <div class="kicker">Before / After · 范式转变</div>
+    <h2 class="h-xl" style="margin-bottom:4vh">从交接到共建</h2>
+
+    <div class="grid-2-6-6" style="gap:5vw 4vh">
+      <!-- 左列:旧 -->
+      <div style="padding:3vh 2vw; border-left:3px solid currentColor; opacity:.55">
+        <div class="kicker" style="opacity:.9">Before · 旧模式</div>
+        <h3 class="h-md" style="margin-top:2vh">设计 → 开发 → 交接</h3>
+        <ul style="margin-top:3vh; padding-left:1.2em; display:flex; flex-direction:column; gap:1.4vh; font-family:var(--sans-zh); font-size:max(14px,1.1vw); line-height:1.55">
+          <li>设计师在 Figma 做稿</li>
+          <li>开发者盯着文件翻译像素</li>
+          <li>反复 PR 沟通对齐</li>
+          <li>非技术人员无法触碰代码</li>
+        </ul>
+      </div>
+      <!-- 右列:新 -->
+      <div style="padding:3vh 2vw; border-left:3px solid currentColor">
+        <div class="kicker" style="opacity:.9">After · 新模式</div>
+        <h3 class="h-md" style="margin-top:2vh">同工具 · 并行 · 共建</h3>
+        <ul style="margin-top:3vh; padding-left:1.2em; display:flex; flex-direction:column; gap:1.4vh; font-family:var(--sans-zh); font-size:max(14px,1.1vw); line-height:1.55">
+          <li>三个角色同时在 Intent 工作</li>
+          <li>agents.md 作为共享上下文</li>
+          <li>代理处理对齐 / 冲突 / 动画</li>
+          <li>任何人都能安全贡献代码</li>
+        </ul>
+      </div>
+    </div>
+  </div>
+  <div class="foot">
+    <div>Page 12 · 范式转变</div>
+    <div>Before / After</div>
+  </div>
+</section>
+```
+
+**要点**:
+- 用 `.grid-2-6-6`(1:1)左右分半
+- 左列 `opacity:.55` 做"旧"的视觉弱化,右列满亮度做"新"的突出
+- 两列都用 `border-left:3px solid` + `padding-left` 做引用块感
+- 每列结构统一:`kicker` → `h-md` → `<ul>` 要点,节奏一致
+
+---
+
+## Layout 10: 图文混排(Lead Image + Side Text)
+
+```html
+<section class="slide light">
+  <div class="chrome">
+    <div>Design First · 设计先行</div>
+    <div>08 / 16</div>
+  </div>
+  <div class="frame grid-2-8-4" style="padding-top:6vh">
+    <!-- 左列:大段正文 + 引用 -->
+    <div>
+      <div class="kicker">Phase 01 · 设计阶段</div>
+      <h2 class="h-xl" style="margin-top:1vh; margin-bottom:3vh">设计先行 · 2 周</h2>
+
+      <p class="lead" style="margin-bottom:3vh">
+        在 Figma 中完成视觉探索与设计系统,网格 / 排版 / 颜色变量 / 可复用组件,桌面和移动端稿件几轮反馈迭代。
+      </p>
+
+      <p style="font-family:var(--sans-zh); font-size:max(14px,1.15vw); line-height:1.75; opacity:.78; margin-bottom:2.4vh">
+        两周之内,视觉风格、粗略结构、方向性内容全部稳定。这是扎实的传统设计流程——在这里还没什么新鲜事。
+      </p>
+
+      <div class="callout" style="margin-top:3vh">
+        "This phase was pretty standard.<br>Just a solid Web design process."
+        <div class="callout-src">— Luke Wroblewski</div>
+      </div>
+    </div>
+    <!-- 右列:辅助图 · 竖版或方形 -->
+    <figure class="frame-img" style="aspect-ratio:3/4; max-height:60vh">
+      <img src="images/figma.png" alt="Figma design system">
+      <figcaption class="img-cap">Figma · Design System</figcaption>
+    </figure>
+  </div>
+  <div class="foot">
+    <div>Page 08 · Design First</div>
+    <div>约 2 周</div>
+  </div>
+</section>
+```
+
+**要点**:
+- `.grid-2-8-4`(8:4) 让正文占主导,图片作辅助
+- 左列包含多种信息层级:kicker → 大标题 → lead → 正文段落 → callout(引用)
+- 右列图片用 **竖版 3:4** 或方形 1:1,避免和左列文本竞争注意力
+- 这种布局适合**页面信息量偏大**的场景(不像 Layout 4 只有一句金句)
+
+---
+
+## 附录:常用网格模板
+
+| 类名 | 配比 | 用途 |
+|---|---|---|
+| `.grid-2-6-6` | 6:6(1:1) | 对半分 |
+| `.grid-2-7-5` | 7:5 | 文字为主 + 辅助图 |
+| `.grid-2-8-4` | 8:4(2:1) | 大段文字 + 小图/数据 |
+| `.grid-3` | 1:1:1 | 3 项并列(案例/截图) |
+| `.grid-3-3` | 3×2 | 6 图矩阵 |
+| `.grid-6` | 3×2 | 6 个数据卡片 |
+
+所有网格都预留 `gap: 3vw 4vh`(水平 3vw、竖直 4vh),可以单独覆写。
+
+---
+
+## 页面节奏建议
+
+一场 25-30 页的分享,推荐以下节奏:
+
+1. **Hero Cover**(第 1 页)
+2. **Act Divider**(第一幕开场,hero light 或 hero dark)
+3. **Big Numbers**(抛硬数据制造冲击)
+4. **Quote + Image**(讲身份反差/挂钩)
+5. **Image Grid**(证据支撑)
+6. **Hero Question**(幕收束,留悬念)
+7. ... 第二幕、第三幕同样节奏 ...
+8. **Hero Close**(最后一页,问题或致谢)
+
+hero 页与 non-hero 页应该 **2-3 : 1 比例交错**,不要连续超过 3 页 non-hero,也不要连续超过 2 页 hero。

+ 122 - 0
references/themes.md

@@ -0,0 +1,122 @@
+# 主题色预设(Themes)
+
+5 套精心调配的主题色板,保证"电子杂志 × 电子墨水"的美学不垮。**不允许用户自定义颜色——色彩搭配错了画面瞬间变丑**,只从以下预设中挑选。
+
+---
+
+## 使用方法
+
+1. 问用户选哪套(或基于内容推荐一套)
+2. 打开 `assets/template.html` 的 `<style>` 块
+3. 找到开头的 `:root{` 块
+4. **整体替换**标有"主题色"注释的那几行 `--ink` / `--ink-rgb` / `--paper` / `--paper-rgb` / `--paper-tint` / `--ink-tint`
+5. 其他 CSS 都走 `var(--...)`,无需任何其他改动
+
+---
+
+## 🖋 墨水经典 (Monocle 默认)
+
+**适合**:通用分享、商业发布、科技产品、任何场景都安全的默认选择。
+**调性**:纯墨黑 + 暖米白,杂志感最强,Monocle / Apricot / A Book Apart 风。
+
+```css
+--ink:#0a0a0b;
+--ink-rgb:10,10,11;
+--paper:#f1efea;
+--paper-rgb:241,239,234;
+--paper-tint:#e8e5de;
+--ink-tint:#18181a;
+```
+
+---
+
+## 🌊 靛蓝瓷 (Indigo Porcelain)
+
+**适合**:科技/研究/数据分享、工程师文化、深度内容、技术发布会。
+**调性**:深靛蓝 + 瓷白,冷静、理性、有深度,像学术期刊或蓝印花瓷器。
+
+```css
+--ink:#0a1f3d;
+--ink-rgb:10,31,61;
+--paper:#f1f3f5;
+--paper-rgb:241,243,245;
+--paper-tint:#e4e8ec;
+--ink-tint:#152a4a;
+```
+
+---
+
+## 🌿 森林墨 (Forest Ink)
+
+**适合**:自然/可持续/文化/非虚构内容、户外品牌、环保主题。
+**调性**:深森林绿 + 象牙,沉稳、有呼吸感,像旧版《国家地理》。
+
+```css
+--ink:#1a2e1f;
+--ink-rgb:26,46,31;
+--paper:#f5f1e8;
+--paper-rgb:245,241,232;
+--paper-tint:#ece7da;
+--ink-tint:#253d2c;
+```
+
+---
+
+## 🍂 牛皮纸 (Kraft Paper)
+
+**适合**:怀旧/人文/阅读/历史/文学分享、独立杂志、手作品牌。
+**调性**:深棕 + 暖米,像牛皮信封或老笔记本,温暖、有年代感。
+
+```css
+--ink:#2a1e13;
+--ink-rgb:42,30,19;
+--paper:#eedfc7;
+--paper-rgb:238,223,199;
+--paper-tint:#e0d0b6;
+--ink-tint:#3a2a1d;
+```
+
+---
+
+## 🌙 沙丘 (Dune)
+
+**适合**:艺术/设计/创意/时尚分享、画廊手册、审美优先的私享会。
+**调性**:炭灰 + 沙色,克制、高级、中性,像沙漠黄昏或建筑设计图册。
+
+```css
+--ink:#1f1a14;
+--ink-rgb:31,26,20;
+--paper:#f0e6d2;
+--paper-rgb:240,230,210;
+--paper-tint:#e3d7bf;
+--ink-tint:#2d2620;
+```
+
+---
+
+## 推荐选择参考
+
+| 如果是... | 推荐主题 |
+|---|---|
+| 不知道选啥 / 第一次用 | 🖋 墨水经典 |
+| AI / 技术 / 产品发布 | 🌊 靛蓝瓷 |
+| 内容 / 行业观察 / 文化 | 🌿 森林墨 |
+| 书评 / 生活方式 / 人文 | 🍂 牛皮纸 |
+| 设计 / 艺术 / 品牌 | 🌙 沙丘 |
+
+---
+
+## 切换原则
+
+- **一份 deck 只用一套主题**,不要中途换色
+- WebGL shader 的默认主色(钛金色散 / 银色流动)适配所有 5 套(经测试可接受)
+- `currentColor` 驱动的 border / icon 会跟随 section 的 text color 自动适配,无需额外调整
+- 选定主题后,`<title>` 文字和 `chrome` 文案可以强化该主题的语义(例如牛皮纸配"Vol.03 · 秋"这种)
+
+## ❌ 不要做的事
+
+- ❌ **不允许混搭**(例如 ink 取墨水经典的,paper 取沙丘的)——会彻底违和
+- ❌ **不允许用户随便给一个 hex 值**——需委婉拒绝并展示 5 套预设让选
+- ❌ **不要直接修改 template.html 其他地方的颜色**——所有散落 rgba 都走 var,改 :root 一处即可
+
+选定主题后在 skill 对话中告诉用户:"用 🖋 墨水经典 / 🌊 靛蓝瓷 ..."并在 deck 项目记录里备注,方便后续迭代时保持一致。