# Stillbeat v3.3 视觉重置 · 交接文档

> 接手的 AI / 工程师:**先读这份,再读 `HANDOFF.md`(v3.2 全貌)与 `README.md`(用户视角)。**
> 本文只记录 **v3.3 视觉重置这一轮**做了什么、为什么、以及哪些规矩不能破。
> 所有改动**均已写入文件系统,但尚未 commit** —— 直接读工作区文件即可,`git diff` 可看全部改动。

---

## 0. 项目位置与状态

```
/Users/lvxuan/Desktop/SB 项目设计/
├── Stillbeat/          ← 主工程(所有改动都在这里)
├── Stillbeat.app/      ← 已打包的 Mac 应用(旧版,未含 v3.3)
└── Stillbeat-final.zip
```

```bash
cd "/Users/lvxuan/Desktop/SB 项目设计/Stillbeat"
git log --oneline -1        # 9088dcb  baseline: Stillbeat v3.2 视觉重置前基线
git status --short          # 7 个 modified + 6 个 untracked 目录(见下)
git diff                    # 全部改动明细
```

**基线 commit 只有一个**：`9088dcb` 是 v3.2 的快照。之后所有 v3.3 改动都在工作区，**未提交**。
任何文件可 `git checkout <file>` 单独回退；`git checkout .` 全量回退到 v3.2。

### 已改（modified，7 个文件，+306 −57）

| 文件 | 行数 | 改了什么 |
|---|---|---|
| `static/js/scenes.js` | +110 | `restrain/restrainAccent` 配色闸门、`photoScene`、`mockupScene`、`tintedLayer` |
| `static/js/montage.js` | +80 | `paletteFromLogo` 过闸、舞台光效绑定、`PHOTO_LIBRARY`(6)、`MOCKUP_LIBRARY`(12)、`make*Scene` |
| `static/js/brand.js` | +46 | 6 个原型各自克制色板、`defaultBrief` 改用原型色板 |
| `static/style.css` | +65 | 排印极端化、硬切、失败态语言、舞台光效、muted 加深、上传引导、示例卡 |
| `site/static/site.css` | +50 | muted 加深、`--orange-ink`、nav `:not(.btn)`、btn hover 去橙、世界硬切 CSS |
| `site/build.py` | +11 | hero 加世界图硬切层 + 错位圆环锚点 SVG |
| `static/index.html` | +1 | 上传卡加 `.flow` 流程条 |

### 新增（untracked，6 个目录）

| 目录 | 内容 |
|---|---|
| `brand/worlds/` | 18 张原始生成图（12 世界 + 6 微距），带底部水印，**源文件** |
| `brand/mockups/` | 12 张原始样机图，带水印，**源文件** |
| `site/static/img/worlds/` | 官网用：`world-*.jpg` ×12 + `macro-*.jpg` ×6（已裁水印） |
| `site/static/img/mockups/` | 官网用：12 品类 ×（`-base.jpg` 灰度 + `-mask.png` 遮罩）= 24 文件 |
| `static/img/` | 应用用：上两者的副本（应用静态根是 `static/`，官网是 `site/static/`） |
| `static/design-lab/` | 2 个设计原型：`tension.html`（品牌张力）、`mockups.html`（样机变色）|

---

## 1. 这一轮的核心命题

用户 feedback 原话：「**还是不够高级，没有品牌张力**」。

诊断结论：**v3.2 做的是"合规清理"（对比度/色板/违规色），不是"设计"**。修完 bug 之后界面依然寡淡，因为：

1. 界面**没有演示它自己的主张** —— Stillbeat 卖"硬切"，但界面自己全是 `.12s` 淡入
2. 没有尺度对比 —— 所有元素都是"中等"大小
3. 排印是 Archivo 默认参数（`wdth 100 / wght 400`）
4. 首屏是产品界面截图，不是一张有艺术指导的大片

**v3.3 的解法**：让产品自己演示自己的主张。

---

## 2. 改动明细（按主题）

### 2.1 配色系统（P0，最重要）

**问题**：`paletteFromLogo` 直接把 SVG 里最显眼的颜色当主色。火箭示例 logo 的荧光黄 `#fbbf24` 直接决定了整支片的调性（"麦辣鸡腿堡"），而选「极简克制」原型**完全不改变色板**（因为 `ARCHETYPES` 里根本没有 `palette` 字段）。

**解法**（三道闸门）：

1. `scenes.js` 新增 `restrain(hex, {maxSat, maxL, minL})` 与 `restrainAccent()`
   —— HSL 降饱和 + 明度钳制。任何色进色板前必须过闸。
   实测：`#fbbf24`（荧光黄）→ `#8d805e`（旧黄铜）；`#f97316`（亮橙）→ `#906547`（陶土棕）

2. `brand.js` 给 6 个原型各一套克制色板（饱和度 < 30%、主色中低明度）：
   ```
   minimal  #1F1F1D / luxury #17130F / tech #0F1418
   nature   #2B2721 / street #131313   / heritage #231D17
   ```

3. `montage.js` 的 `paletteFromLogo()` 改为：**先用原型色板打底**（调性由原型决定），**logo 只贡献色相**且必须过 `restrain`。

### 2.2 排印与硬切

- 标题 `wdth 108→92`、`wght 600→720`、`letter-spacing -.01em→-.028em`、`h1` 上限 `48px→76px`
- 标签 `.eyebrow` `11px→10px` + `letter-spacing .1em→.2em`；`.note` `12px→11px`
- **硬切原则**：颜色/背景/边框 → `transition: none`；位移/旋转 → 保留 `.15s`（物理反馈需要，不是装饰）
  - 已改：`button`、`#dropzone`、`#samples .sample`(只留 transform)、`.mt-row`、`.vcard`
  - 未改（有意保留）：`input/select`(聚焦)、`details .chev`(旋转)

### 2.3 失败态语言

所有失败/警告统一为 **中性胶囊 + 一枚 5px 红点 + 暗红字**：
```css
.chip.bad, #key-chip.bad, #license-chip.bad { background: var(--field); color: var(--err); border-color: transparent; }
.chip.bad::before { /* 5px 红点 */ }
```
之前 `#license-chip.bad` 用 `--orange` 做边框+文字，橙是"节拍/播放头"专用色，失败态用橙是越界。

### 2.4 舞台动态光效

`static/style.css` 的 `.stage` 从纯灰 `#45474F` 改为 **黑底 + 双层 radial-gradient**：
- `--glow-a` = 品牌主色（左上 22%/32%）
- `--glow-b` = 主色 + 180° 互补色（右下 78%/70%），由 `montage.js` 的 `applyBrief()` 用 `rgbToHsl`/`hslToHex` 算出
- 9s 呼吸动画，`prefers-reduced-motion` 自动降级

实测：极简克制 → 黄铜+蓝；街头潮流 `#A64128` → 砖红+蓝绿 `#368ba1`。**每个用户的舞台都是自己的品牌色。**

### 2.5 摄影素材（确定性、零积分）

这是 HANDOFF §8 第 2 项（"摄影级 mockup 库"）的落地。

**A. 超微距（`PHOTO_LIBRARY`，6 个）**
`scenes.js` 新增 `photoScene(url, name)` —— 与 `PROC` 同构，预加载 + cover 绘制。
素材：皮肤 / 皮革毛孔 / 织物纤维 / 金属拉丝 / 纸纤维 / 液体表面。
生成时已要求**中心留白**，logo 直接叠上去，**不需要过留白质检**。

**B. 高端品类样机（`MOCKUP_LIBRARY`，12 个，可变色）**
`scenes.js` 新增 `mockupScene(baseUrl, maskUrl, name)`：
```
灰度底图 → 'color' 模式叠加「品牌色 + mask」层
```
- 灰度底图保留**全部光影与材质纹理**
- mask 限定**只染产品不染背景**（从四角 floodfill 近白区域自动抠出）
- `globalCompositeOperation = 'color'` —— 取品牌色相/饱和，留底图明度
- 结果：**真实光影 + 品牌色的产品**，不是死板色块
- `tintedLayer()` 按 `base+mask+color` 缓存，避免每帧重复合成

12 个品类：高级腕表 / 高跟鞋 / 墨镜 / 戒指 / 烈酒瓶 / 香薰蜡烛 / 香氛瓶 / 手袋 / 易拉罐 / 包装盒 / 名片 / 帆布袋

**C. 素材注入**
`resetLibrary()` 末尾：
```js
for (const ph of PHOTO_LIBRARY) M.library.push(makePhotoScene(...));
for (const mk of MOCKUP_LIBRARY) M.library.push(makeMockupScene(...));
```
`kind: 'proc'` → **零积分、不走 AI 通道**。实测素材库共 52 个场景（6 微距 + 12 样机 + 原有）。

### 2.6 官网修复（`site/`）

| 问题 | 实测数据 | 修法 |
|---|---|---|
| muted 灰对比度不足 | `#7A7A78` 白底 **3.91:1** | → `#6B6B69`（4.85:1） |
| 国际橙做文字色 | `#FF5A1F` 白底 **2.98:1** | 新增 `--orange-ink:#C2410C`(4.71:1)，橙留给图形 |
| nav 按钮灰字 | 蓝底灰字 **1.64:1**（几乎看不见）| `nav.top .links a:not(.btn)` —— 原规则特异性 (0,2,2) 盖掉了 `.btn.primary` (0,2,0) 的白字 |
| `.btn.primary:hover` 蓝→橙跳变 | 互补色突跳，廉价感 | → `#0824A8`（同色系加深）|
| 首屏是产品界面截图 | — | 6 张世界图 `steps(1)` 硬切 + **错位圆环 demo logo 锁中心** |

**WCAG AA 对比度：首页 fails 8 → 0**（脚本修正后测量）。

⚠️ 测量踩坑记录：`getComputedStyle().backgroundColor` 在 `color(srgb r g b / a)` 格式下数值是 **0–1**，不是 0–255。不加判断会把对比度算错 5 倍，产生大量假阳性（我曾据此得出"188 个 fails"的错误结论）。

---

## 3. 硬规矩（**不可破坏**）

继承 `HANDOFF.md` §7，并新增 v3.3 三条：

1. **AI 密钥只存 `~/.stillbeat/config.json`** —— 不进浏览器代码、不进打包文件、不进日志、不进测试夹具
2. **logo 像素绝不发给任何 AI 模型** —— 只发文字提示词
3. 授权私钥 `SB_LICENSE_SK` 只在服务器 `.env`；`license_sk.hex`/`license_pk.hex` 不入库
4. 🚫 **克莱因蓝 `--klein` 只用于品牌点（dot）** —— 不得用于 CTA / hover / 徽章 / 角色色。
   验证：`grep -n "var(--klein)" static/style.css` 应只剩 line 15 的 `--dot`
5. 🚫 **橙 `--orange` 只用于节拍 / 播放头 / 进行中** —— 文字用 `--orange-ink`，失败态用 `--err`
6. 🚫 **任何进入色板的颜色必须过 `restrain()`** —— 否则一支荧光 logo 就能毁掉整支片
7. 🚫 **颜色/状态切换 `transition: none`（硬切）** —— 位移/旋转可保留短过渡
8. `SB_DEV=1` 只用于沙盒，生产必须为 0

---

## 4. 待办（按优先级）

| 优先级 | 项目 | 阻塞 | 备注 |
|---|---|---|---|
| 🔴 P0 | **跑 129 项回归测试** | 需先备份 `~/.stillbeat/config.json`（真实密钥）| 测试会把 config 指向 mock(`:9999` / `sk-relay-test-key`)，跑完必须恢复 |
| 🔴 P0 | **替换 9 处法务占位符** | 需注册个体户/公司 | `site/build.py` line 566 的 `FILL()` lambda，输出到 `/legal/*` 与 `/contact/`。**Paddle 审核前必填** |
| 🟠 P1 | **标志重做** | 需设计决策 | 当前错位圆环：`docs/标志撞车审查.md` 自己承认"加大错位后也只是离开了拥挤家族"，**不传达"标不动"的观点** |
| 🟠 P1 | 示例 logo 换成熟品牌风 | 需 image_gen | 现有火箭/纸飞机/花开是儿童插画风，与目标客户期望不符 |
| 🟡 P2 | 移动端真机测试 | 需真机 | CSS 有 `@media (max-width:1180px)`，未实测 |
| 🟡 P2 | 性能（图片体积/懒加载/字体子集）| 需 Lighthouse | `site/dist` 目前 9.4 MB |
| 🟢 P3 | 节拍卡点光脉冲 | — | 切点时舞台光晕脉冲一次，视觉化"踩在拍上" |
| 🟢 P3 | Editorial 排版加强官网 | — | 对标参考视频 #1 的杂志感 |

---

## 5. 怎么验证

```bash
cd "/Users/lvxuan/Desktop/SB 项目设计/Stillbeat"

# 起沙盒(官网 :8090 / 应用 :8765 / 在线版沙盒 :8800)
bash deploy/sandbox.sh
# 或双击 启动沙盒.command

# 改了 build.py 后必须重新生成官网
python3 site/build.py

# 停止(别用 pkill -f python,历史上误杀过别的进程)
bash deploy/sandbox.sh stop
```

**缓存坑**：官网 `:8090` 是 `python -m http.server`，有启发式缓存 → 改完必须 **Cmd+Shift+R 硬刷新**。
应用 `:8765` 发 `Cache-Control: no-store`，直接刷新即可。

**验证清单**：
- [ ] 应用 `:8765` 点示例 → 左栏切原型 → **舞台光效跟随变色**
- [ ] 右栏镜头序列含「微距 · ×6」与「样机 · ×12」
- [ ] 点样机镜头 → 产品呈现**品牌色**且光影纹理保留
- [ ] 官网 `:8090` 首屏 → 世界图硬切 + 中央圆环静止
- [ ] `grep -n "var(--klein)" static/style.css` 只剩 `--dot`

---

## 6. 给接手 AI 的三条建议

1. **先跑沙盒看一眼再动代码** —— 视觉工作的判断必须基于真实渲染，不能靠读代码想象。我曾两次凭截图误判（"三分种"错别字、材质 chip 是彩色标签），都是读图错误，靠 `getComputedStyle` / `grep` 才纠正。用数据验证，不用眼睛猜。

2. **改视觉前先确认"这是设计问题还是工程问题"** —— v3.3 的前半程在修工程问题（对比度、违规色），修完用户依然不满意；后半程才进入设计（尺度对比、硬切母题、光效）。如果用户说"还是不够高级"，通常意味着该做设计而不是继续修 bug。

3. **`git checkout <file>` 随时可退** —— 基线 `9088dcb` 是干净的 v3.2。任何一处改动都能单独回退，不用来回试错。

---

## 附：本轮生成的所有图片资产

| 类别 | 数量 | 位置 | 用途 |
|---|---|---|---|
| 世界图（商业品类）| 6 | `site/static/img/worlds/world-*.jpg` | 官网 hero 硬切背景 |
| 世界图（材质静物）| 6 | 同上 | 备用 |
| 超微距 | 6 | `macro-*.jpg` | 产品素材库（photoScene）|
| 高端样机 | 12 品类 ×2 | `site/static/img/mockups/` | 产品素材库（mockupScene，可变色）|
| 设计原型 | 2 | `static/design-lab/` | `tension.html` / `mockups.html`，可直接用 `:8765/design-lab/xxx.html` 预览 |

所有图片均经 PIL 处理：**裁掉底部 120px**（去生成水印）+ 统一尺寸 + JPEG q86 压缩。
样机额外生成 mask（floodfill 四角近白区域 → 反相）。
