Files
cms-SiteForge/docs/superpowers/plans/2026-10-22-trade-pro-theme-port.md
T
浅念 e017d3a809 fix: home visual feedback round — soft bg for features, branded placeholder, plan 10j
- advantages section gains section--soft (white cards were invisible on white)
- .pro-art placeholder redesigned: brand gradient + initials + small kind
  label, ring ornament hidden (was heavy gray block)
- plan: new Task 10j (port cms header/footer chrome visuals) per user
  feedback that overall look should match cms
- verified live: mirror build 7f1e8c47f0c4 (374/384 real images pulled,
  203 pages, 0 errors)

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-23 11:00:13 +08:00

395 lines
23 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# trade-pro 第 5 主题移植实施计划
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 把 cms Astro 主题移植为 SiteForge V8_10 第 5 套内置主题 trade-pro(英文外贸 B2B),平台适配 + 骨架跑通 + 完整模板 + 验收。
**Architecture:** 三步走——先做平台适配(主题发现单一源、宏覆盖机制、主题资产优先、6 处前端入口),再以骨架主题包在真实 hesupipe 站点(`483295c29ea0`,193 条内容)跑通构建,最后按 cms 设计源(`D:\project\cms\runtimes\astro-site-runtime`)逐模板移植视觉。设计依据:[spec R3](../specs/2026-10-22-trade-pro-theme-design.md)。
**Tech Stack:** Python 3.12 / FastAPI / Jinja2(FileSystemLoader 多目录)/ pytest / 原生 JS。
**环境事实(执行者必读):**
- 工作目录:`C:\Users\30997\Downloads\SiteForge_Factory_V5_Web_Docker\SiteForge_Enterprise_V8_10`(下称 `$ROOT`,bash 里用正斜杠)
- Docker 实例已运行:`http://127.0.0.1:8787`(容器 siteforge-v8-10-siteforge-1/-worker-1),admin 密码在 `.env`
- 测试命令:`python -m pytest -q`(当前 210 项全绿,任何改动后必须保持);JS 检查 `node --check app/static/<file>.js`
- **宿主机快速循环**(V8_12 起):`PYTHONUTF8=1 .venv/Scripts/python -m pytest -q`,基线 **227 passed + 2 known-fail**(test_p0_backup.py::test_sqlite_backup_and_restore_with_media、test_p0_p4_completion_v89.py::test_restore_replaces_directory_atomically_and_preserves_previous——均为 Windows sqlite 文件锁环境问题,改动前即失败);任何新失败都是回归。权威全量验证用容器:`MSYS_NO_PATHCONV=1 docker run --rm -v "$(pwd -W):/project" -w /project python:3.12-slim sh -c "python -m pip install -q -r requirements-dev.lock && python -m pytest -q"`(容器基线 226 passed + 1 node 环境失败)
- cms 设计源:`D:/project/cms/runtimes/astro-site-runtime/src`(tokens.css 250 行、base.css 445 行、components/*.astro、pages/[locale]/*.astro);像素基准 `dist/hesupipe/en/`
- 改动 Python 后需 `docker compose up -d --build` 重建容器再验证 HTTP 层(构建约 1 分钟,有缓存)
- 契约清单已固化于 spec §2(F1-F13),本计划直接引用
---
## Phase 1+2:平台适配与骨架跑通(Task 1-8)
### Task 1: Git 基线 —— ✅ 已完成(2026-10-22,幂等跳过)
已执行并有扩展:仓库初始化、基线提交、远程 `http://219.157.255.213:25311/xinyouhe/cms-SiteForge.git` 配置并推送(main + feat/trade-pro-theme),远程原有 V5 基线历史经 `-s ours` 无损合并,`docs/COMPLETION_PLAN.md` 带入当前树;`.env`/密码确认未入库。随后追加 **V8_12 原地升级**(26 文件覆盖 + bug 修复重放 + compose 项目名钉回 `siteforge-v8-10` 保护数据卷)。执行者无需重跑本任务。
### Task 2: 主题发现单一源(F1)
**Files:**
- Modify: `app/core.py:12`
- Modify: `app/theme_pro.py:16-18`
- Modify: `app/theme_store.py:14`
- Test: `tests/test_theme_registry.py`(新建)
- [ ] **Step 1: 写失败测试**
```python
"""Theme discovery must be a single source of truth."""
import json
from pathlib import Path
from app import core, theme_pro, theme_store
ROOT = Path(core.__file__).resolve().parent.parent
def test_discover_returns_builtin_themes_with_manifests():
ids = core.discover_themes()
for tid in ('automotive', 'industrial', 'corporate', 'minimal'):
assert tid in ids
for tid in ids:
manifest = json.loads((ROOT / 'themes' / tid / 'manifest.json').read_text(encoding='utf-8'))
assert manifest.get('id') == tid
def test_three_whitelists_agree():
assert set(core.THEMES) == set(theme_pro.THEMES) == set(theme_store.BUILTIN)
assert isinstance(core.THEMES, tuple)
```
- [ ] **Step 2:** `python -m pytest tests/test_theme_registry.py -q` → 预期 FAIL(`discover_themes` 不存在)
- [ ] **Step 3: 实现**——`app/core.py:12` 替换为:
```python
ROOT_PATH = __import__('pathlib').Path(__file__).resolve().parent.parent
def discover_themes():
"""Single source of truth: scan themes/*/manifest.json (id must match folder)."""
import json
themes_dir = ROOT_PATH / 'themes'
found = []
for folder in sorted(themes_dir.iterdir()):
if not folder.is_dir() or folder.name.startswith('_'):
continue
manifest = folder / 'manifest.json'
if not manifest.is_file():
continue
try:
if json.loads(manifest.read_text(encoding='utf-8')).get('id') == folder.name:
found.append(folder.name)
except (ValueError, UnicodeError):
continue
return tuple(found)
THEMES = discover_themes()
```
(core.py 顶部已有 `import json`,可复用;实现时去掉重复 import,风格与文件一致。)
`app/theme_pro.py:16-18` 改为:
```python
from .core import sanitize, route_path, THEMES
```
(删除原第 18 行的元组定义。)
`app/theme_store.py:14` 改为:
```python
from .core import THEMES as BUILTIN
```
- [ ] **Step 4:** `python -m pytest tests/test_theme_registry.py -q` → PASS;`python -m pytest -q` 全绿
- [ ] **Step 5:** `git add -A && git commit -m "feat: single-source theme discovery replaces three hardcoded whitelists"`
### Task 3: theme_pro 每主题环境(宏覆盖机制,F4)
**Files:**
- Modify: `app/theme_pro.py:18-20`(LOADER 旁新增 env_for)
- Modify: `app/theme_pro.py:190`(render 用 env_for)
- Test: `tests/test_theme_registry.py`(追加)
- [ ] **Step 1: 追加失败测试**
```python
def test_env_for_theme_prefers_theme_dir_then_shared_root():
from app import theme_pro
env = theme_pro.env_for('industrial')
paths = [str(p) for p in env.loader.searchpaths]
assert paths[0].replace('\\', '/').endswith('themes/industrial')
assert paths[1].replace('\\', '/').endswith('themes')
# 共享宏库必须仍可解析(回落路径)
assert env.get_template('_professional_components.html') is not None
def test_env_for_is_cached_per_theme():
from app import theme_pro
assert theme_pro.env_for('industrial') is theme_pro.env_for('industrial')
```
- [ ] **Step 2:** 跑测试 → FAIL(env_for 不存在)
- [ ] **Step 3: 实现**——`app/theme_pro.py` 在 LOADER 定义后追加:
```python
_ENVS = {}
def env_for(theme):
"""Per-theme Jinja env: theme dir first, shared themes/ root as fallback.
Lets a theme override shared macros (_professional_components.html) while
inheriting everything else. Cached: one env per theme across 5000-page builds.
"""
env = _ENVS.get(theme)
if env is None:
theme_dir = ROOT / 'themes' / theme
search = [str(theme_dir), str(ROOT / 'themes')] if theme_dir.is_dir() else [str(ROOT / 'themes')]
env = Environment(loader=FileSystemLoader(search), autoescape=select_autoescape(['html', 'xml']))
_ENVS[theme] = env
return env
```
`render()` 末行(:190)改为:
```python
return env_for(theme).get_template('pro.html').render(**ctx)
```
- [ ] **Step 4:** `python -m pytest -q` 全绿(现有 test_theme_pro_v3.py 即回归)
- [ ] **Step 5:** commit `feat: per-theme Jinja env enables theme-level macro override`
### Task 4: builder 主题资产优先(F5)
**Files:**
- Modify: `app/builder.py:191`
- Test: `tests/test_theme_registry.py`(追加)
- [ ] **Step 1: 追加失败测试**
```python
def test_theme_blocks_css_overrides_platform(tmp_path):
from app import builder
from pathlib import Path
dist = tmp_path / 'assets'; dist.mkdir()
theme_dir = tmp_path / 'theme'; theme_dir.mkdir()
(theme_dir / 'blocks.css').write_text('/* theme version */', encoding='utf-8')
builder.copy_blocks_css(theme_dir, dist)
assert (dist / 'blocks.css').read_text(encoding='utf-8') == '/* theme version */'
# 无主题版时回落平台版
dist2 = tmp_path / 'assets2'; dist2.mkdir()
builder.copy_blocks_css(tmp_path / 'nonexistent', dist2)
assert (dist2 / 'blocks.css').is_file()
```
- [ ] **Step 2:** 跑 → FAIL
- [ ] **Step 3: 实现**——`app/builder.py:191` 的 `shutil.copy2(ROOT/'app'/'static'/'blocks.css',dist/'assets'/'blocks.css')` 替换为调用新函数,并在文件合适位置(output_path 附近)定义:
```python
def copy_blocks_css(theme_dir, dist_assets):
"""Theme-provided blocks.css wins; platform default is the fallback."""
src = theme_dir / 'blocks.css' if (theme_dir / 'blocks.css').is_file() else ROOT / 'app' / 'static' / 'blocks.css'
shutil.copy2(src, dist_assets / 'blocks.css')
```
- [ ] **Step 4:** `python -m pytest -q` 全绿;commit `feat: theme-level blocks.css takes priority over platform asset`
### Task 5: trade-pro 骨架主题包(Phase 2 核心)
**Files:**
- Create: `themes/trade-pro/manifest.json`、`site.html`、`pro.html`、`_professional_components.html`、`theme.css`、`blocks.css`、`pro.css`、`page-presets.json`、`pages/`(14 个 html)、`visual/.gitkeep`、`versions/.gitkeep`
- Test: `tests/test_theme_registry.py`(追加)
- [ ] **Step 1: 追加失败测试**
```python
def test_trade_pro_registered_and_compatible():
assert 'trade-pro' in core.THEMES
result = theme_store.compatibility('trade-pro')
assert result['valid'], result['errors']
assert not result['errors']
def test_trade_pro_builds_real_site(tmp_path):
# 复用 tests/test_theme_pro_v3.py 中"建站→切主题→构建→断言预览页"的既有写法
# (夹具工厂在 tests/test_factory_v2.py:15-30),theme 用 'trade-pro'。
# 产物读取走夹具的 HTTP 形式(与 v3 一致):
html = factory.get(f'/preview/{sid}/{bid}/').text
assert 'lang="en"' in html
assert '>首页<' not in html
```
(执行时先读 test_theme_pro_v3.py 的相邻测试,逐行复制其夹具调用方式,仅替换 theme 参数与断言;禁止保留 `...` 空壳。)
- [ ] **Step 2:** 跑 → FAIL
- [ ] **Step 3: 创建文件**
`manifest.json`:
```json
{
"id": "trade-pro",
"name": "Trade Pro — International B2B",
"description": "English-first international B2B theme ported from the hesupipe Astro design: SVG icon set, responsive trade catalogue, product specs and inquiry CTA.",
"version": "1.0.0",
"style": "international-b2b",
"entry": "pro.html",
"styles": ["theme.css", "blocks.css"],
"page_templates": ["home","about","contact","listing","category","product","article","brand","case","service","faq","standard","notfound"],
"blocks": ["hero","richtext","features","stats","collection","image_text","gallery","faq","cta","attributes","spacer","layout","carousel","video","tabs","pricing","map","form_link"],
"responsive": true,
"auto_binding": true,
"professional_layout": true,
"commercial_ui": true,
"accent": "#0b3d91"
}
```
`site.html`:以 `themes/industrial/site.html` 为底(它是 4.4KB 的完整独立模板,自带 header/footer 结构,**不引用共享宏库**),改 `lang="zh-CN"` → `lang="en"`。注意:pro 主题的正式渲染走 pro.html,site.html 主要服务非 pro 路径/兜底。
**已知新行为(评审发现,记录不阻断)**:Task 2 后 trade-pro 进入 BUILTIN,上传主题 ZIP 可声明 `layout_family:'trade-pro'`(theme_store.install 会拷贝 trade-pro 的 site/pro.html),而 ZIP 包渲染走共享宏回落——与依赖自有宏库的 trade-pro 存在潜在错配。本轮接受(上传包本就以声明式为主),后续如出问题在 install() 校验 layout_family 白名单。
`_professional_components.html`:先做**透传骨架**(从共享库复制 card/cover 等宏,Task 10 起逐个换成 cms 设计 + SVG 图标)。
`pro.html`:以 industrial 的 pro.html 为底最小化:`lang="en"`;页眉/页脚消费 site_design chrome 但按 spec 4.2.5 值比对输出英文兜底(中文默认值视为未配置);正文区渲染 page content 与 custom_blocks;按钮类名一律 `sf-tp-*`。
`theme.css`:先放 tokens 骨架(Task 9 全量移植):
```css
:root{--sf-tp-primary:#0b3d91;--sf-tp-accent:#f5a623;--sf-tp-bg:#ffffff;--sf-tp-text:#1a2332;--sf-tp-muted:#5a6a7e;--sf-tp-radius:8px;--sf-tp-font:'Inter',system-ui,-apple-system,sans-serif}
```
`blocks.css`:空文件 + 注释 `/* trade-pro component styles: filled in Phase 3 */`。
`pro.css`:空文件(兼容性硬性要求,spec F8)。
`page-presets.json`:**复制 industrial 的同名文件原样**(Phase 4 精修;13 种 key 齐全即可)。
`pages/`:`home.html` 写最小可用骨架(hero + showcase 列表 + cta 引用共享宏);其余 13 个(about/contact/listing/product/brand/article/case/service/faq/category/search/standard/notfound)先各写薄变体(复用 home 的结构、引用同样宏),文件必须存在(spec F7 硬性)。
- [ ] **Step 4:** `python -m pytest -q` 全绿(含新测试)
- [ ] **Step 5:** `docker compose up -d --build` 后:`curl -s http://127.0.0.1:8787/api/health` 版本不变、`curl -s "http://127.0.0.1:8787/api/factory/themes/trade-pro/compatibility"`(带登录 cookie)返回 valid=true
- [ ] **Step 6:** commit `feat: trade-pro skeleton theme package (Phase 2)`
### Task 6: theme_color 映射 + 6 处前端入口(F3/F11)
**Files:**
- Modify: `app/theme_pro.py`(theme_color 字典,make_context 内,约 :171)
- Modify: `app/templates/admin.html:13`(siteTheme select)
- Modify: `app/templates/workspace.html:41`(建站模态框 theme select)
- Modify: `app/templates/theme-gallery.html:4`(theme select)
- Modify: `app/static/workspace.js:5`(themeName)
- Modify: `app/static/admin.js:5`(cfg)
- Modify: `app/static/factory-v2.js:30`(schemes)
- Test: `tests/test_theme_registry.py`(追加)
- [ ] **Step 1: 追加失败测试**
```python
def test_frontend_entries_include_trade_pro():
import pathlib
base = pathlib.Path(__file__).resolve().parent.parent
for rel, needle in [
('app/templates/admin.html', 'value="trade-pro"'),
('app/templates/workspace.html', 'value="trade-pro"'),
('app/templates/theme-gallery.html', 'value="trade-pro"'),
('app/static/workspace.js', "'trade-pro'"),
('app/static/admin.js', "'trade-pro'"),
('app/static/factory-v2.js', "'trade-pro'"),
]:
assert needle in (base / rel).read_text(encoding='utf-8'), rel
def test_theme_color_map_covers_trade_pro():
# 真实断言:trade-pro 构建出的首页含 meta theme-color #0b3d91
# (复用 Task 5 的构建夹具与 /preview/ HTTP 读取写法)
html = factory.get(f'/preview/{sid}/{bid}/').text
assert '<meta name="theme-color" content="#0b3d91"' in html
```
- [ ] **Step 2:** 跑 → FAIL
- [ ] **Step 3: 实现**——逐处追加(保持原文风格):
- theme_color 字典追加 `'trade-pro':'#0b3d91'`
- 三个 HTML select 各追加:`<option value="trade-pro">Trade Pro · 外贸 B2B(英文)</option>`(theme-gallery 用 `Trade Pro · International B2B`)
- `workspace.js` themeName 追加 `'trade-pro':'Trade Pro'`
- `admin.js` cfg 追加 `trade-pro:{label:'Trade Pro',desc:'英文外贸 · B2B 目录与询盘',prev:'corp',title:'Go global.'}`
- `factory-v2.js` schemes 追加 `'trade-pro':['#0b3d91','#e8f1fb','GO GLOBAL']`
- [ ] **Step 4:** `python -m pytest -q` 全绿 + `for f in app/static/*.js; do node --check "$f"; done` 通过
- [ ] **Step 5:** commit `feat: register trade-pro in theme color map and all six frontend entry points`
### Task 7: E2E 验收——真实站点切主题构建(Phase 2 门槛)
**Files:** 无代码(验证 + 记录)
- [ ] **Step 1:** 登录拿 cookie/CSRF(参照已验证流程:POST /api/auth/login → GET /api/auth/me)
- [ ] **Step 2:** `PUT /api/factory/sites/483295c29ea0/theme` body `{"theme":"trade-pro"}`(确认返回成功)
- [ ] **Step 3:** `POST /api/sites/483295c29ea0/builds`(Idempotency-Key + CSRF)→ 轮询 job 至 done
- [ ] **Step 4:** 断言:pages ≥203、errors 空;预览首页 HTTP 200、含 `lang="en"`、不含"首页/联系我们"(平台中文默认,spec F9 验收);`/products/`、`/product/alloy-resin-system-window/` 200
- [ ] **Step 5:** 断言 6 入口:新工作台/`/classic` 建站表单、theme-gallery、factory-studio 主题卡里均出现 trade-pro(浏览器人工点验,截图存 `docs/superpowers/` 或记录于验收文件)
- [ ] **Step 6:** 把结果写入 `docs/superpowers/specs/2026-10-22-trade-pro-theme-design.md` 附录"Phase 2 验收记录";commit
---
## Phase 3:完整视觉移植(Task 8-12)
> 以下任务的代码无法在计划里全文预写(从 cms 源逐段移植),但每步给出**精确源文件、提取规则与验收断言**。共用纪律:每完成一个 Task 跑全量 pytest + commit。
### Task 8: 设计令牌与基础样式全量移植
- 源:`D:/project/cms/runtimes/astro-site-runtime/src/styles/tokens.css`(250 行)+ `base.css`(445 行)
- [ ] 变量重命名加 `--sf-tp-` 前缀(spec 4.2.6);按钮/交互元素全部 `sf-tp-` 类名(避开平台 `.btn/.primary/.request/.button/.nav-btn`)
- [ ] 验收断言:`themes/trade-pro/theme.css` 覆盖 tokens 全集(颜色/字体/圆角/间距);构建产物 `assets/theme.css` 含 `--sf-tp-` 变量;平台 custom.css 的 `.btn{...}` 与主题按钮类无交集(grep 断言)
- [ ] pytest(现有快照不受影响)+ commit
### Task 9: 图标宏库(新增设计——不是 cms 移植)
> 评审修正(2026-10-22):cms 源码与 dist 实测 **0 个 SVG**,其图标实为文本箭头 `→`(index 15 处、product 17 处)。用户需求"图标需要优化、现在很多都是箭头"意味着图标是**超出 cms 源的新增设计**,不与 dist 对照。
- [ ] `themes/trade-pro/_professional_components.html` 定义 `icon(name)` 宏:自设计内联 SVG 集合,线条风格与 `--sf-tp-*` 令牌一致,≥8 个:arrow-right / check / phone / mail / location / download / search / quote
- [ ] 尺寸/描边随 CSS 变量(`currentColor`、`1.5px` stroke),跟随深浅文字色
- [ ] 验收:home/product 渲染中**交互元素的图标均为 SVG**,不再以裸 `→` 作为唯一图标(对照对象是本主题自身,非 dist);`aria-hidden="true"` 保证可访问性
- [ ] commit
### Task 10: 九种独立布局逐个移植(每布局一个 commit)
顺序与源对照(cms `src/pages/[locale]/` ↔ dist `en/` ↔ trade-pro `pages/`):
1. `home`:index.astro ↔ dist 首页(Hero/FeatureGrid/CardGrid/CTA 结构)
2. `product`:products/[slug].astro ↔ dist/products/alloy-resin-system-window/(参数表 specs、多图 gallery、询盘 CTA)
3. `listing`:news/[...page].astro + products 列表 ↔ 按 kind 分支(Phase 0 已确认模板可拿到 page/kind 上下文;若不可分支由 category 承担产品列表,spec §7 兜底)
4. `category`:product-category ↔ 分类页
5. `article`:news/[slug] ↔ 文章详情
6. `about` / 7. `contact`(LeadForm 样式 + form_endpoint)/ 8. `case`(projects)/ 9. `standard`(applications、support)
每个布局的统一验收:构建对应真实页面 200;关键结构选择器与 dist 对照(header/hero/卡片/参数表/页脚);英文文案断言(无中文默认);手机宽度可读(人工抽查一次,全量视觉验收在 Task 12)。
### Task 11: 五个变体布局 + search
- brand/service/faq/notfound/search 以 standard/listing 变体补齐为"结构合理"版本(后续迭代精修,spec 4.3)
### Task 12: 预设、编辑器与全量验收(Phase 4)
- [ ] `page-presets.json` 13 种按 cms 首页/产品页实际区块结构重写(编辑器初始布局与真实设计一致)
- [ ] 编辑器验证:hero/cta/features/collection/attributes 在 trade-pro 下可编辑、预览、保存草稿、发布(spec §6)。**已知观察(Task 4 质量审查)**:main.py 编辑器侧读平台版 blocks.css,而构建产物可能用主题版——编辑器预览与最终站点可能样式不一致;本项验收时确认影响,必要时编辑器预览也走主题版
- [ ] 全站设计草稿验证:页眉/页脚/导航配置生效(显式值渲染、中文默认被值比对替换为英文兜底)
- [ ] 全量回归:`python -m pytest -q`(≥210+新增全绿)、`node --check` 全部 JS
- [ ] 实战验收:站点 483295c29ea0 最终构建 ≥203 页零错误;视觉对照两层标准(spec §6)逐页记录
- [ ] 更新 spec 附录验收记录;commit
## 与 spec 的已记录偏差(评审裁定可接受)
1. **发现函数放 core 而非 spec 4.2.1 写的 theme_store**:等价实现。core 已被全部引用方(builder/main/theme_pro)导入,放 core 消除反向耦合;theme_store.BUILTIN 经 import 别名保持一致。
2. **builder.ENV 保持单目录**(spec 4.2.4 提到两处构造点):已验证 site.html 是不引用共享宏的独立模板,且带主题前缀的路径加载在单目录下安全;仅 theme_pro.LOADER 改为 per-theme env_for。
3. **spec §6 "渲染快照"以 E2E 页面断言实现**(构建后逐页 200 + 结构选择器 + 文案断言),不另建 Jinja 快照测试文件。
4. **/api/themes(main.py:267)经 core.THEMES 动态化自动生效**,不设专项测试(catalog 由既有测试覆盖)。
5. **前端入口 option 文案**:三个 HTML 下拉用"Trade Pro · 外贸 B2B(英文)"中文风格与各文件现状一致(theme-gallery 用英文)。
6. **平台数据层中文残留(Task 5 质量审查发现)**:bootstrap 生成的 8 个标准页(/about/ /contact/ /products/ /news/ 等)的 `<title>`、`<h1>`、面包屑来自平台造的中文标题(关于我们/联系我们/产品中心…),主题层无法翻译用户数据。Phase 3 视觉验收时接受(真实站点内容为英文导入数据时自然英文),若 bootstrap 英文化成为需求属平台任务另行立项。
## 用户反馈修复队列(2026-10-23 首页预览反馈,10b 收口后立即执行)
0. **[新增 10j] cms 页眉/页脚移植**(用户反馈"整体不是 cms 效果"):在平台 site_design 生成的 DOM 结构上,用 .sf-theme-trade-pro 作用域 CSS 完整还原 cms Header/Footer 视觉(Header.astro/Footer.astro 源码样式:白底细分割线、导航字距/悬停态、蓝色 GET A QUOTE 按钮、cms 页脚深色带与栏目布局)。保持平台 chrome 数据流(导航/公告/CTA 配置仍生效)。排在 10c-10i 之后、Task 11 之前。
1. `.sf-tp-feature` 补 cms FeatureGrid.astro 的卡片皮肤:白底(--sf-tp-color-surface-default)+ hairline 边框(--sf-tp-color-border-subtle)+ 圆角 + 居中 + padding,900px 两列、620px 横排(源码 scoped CSS 全量对照)
2. `.sf-tp-card` 对照 cms dist 的 .card scoped CSS 补边框卡片皮肤(含 hover 抬升已有则保留)
3. `.sf-theme-trade-pro` 作用域内重设计 `.pro-art` 无图占位:brand 渐变底(surface-inverse → brand-primary 混合)+ 白色首字母居中 + 小写 kind 标签,替代共享宏灰块("10 PRODUCT"噪字)
4. Hero 双按钮改横排(flex gap,620px 换行)——核对 pro.css hero 区现状
5. 验收方式:浏览器截图前后对照(Playwright),产品卡/特性卡/占位块三处逐一确认
## 风险与回退
- 每个 Task 独立 commit,出问题 `git revert` 单点回退
- 平台适配层(Task 2-4)有 4 主题回归保护:任何现有测试变红立即停止
- trade-pro 目录独立,删目录 + revert 白名单改动即完全移除