diff --git a/AGENTS.md b/AGENTS.md
index 2236bef..702fd88 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -142,4 +142,5 @@ samples/
- 2026-11-03:应用图标改为印泥铺满、不留白边,圆角按 Apple 连续曲率圆角由系统裁(B6);VI 手册 v1.1 第 22 页改为 iPhone 18 Pro 主屏幕模拟(`docs/design/vi/mockup/`,Apple 素材不进 git,机身需本人同意 Apple Design Resources 许可后放入)。
- 2026-11-03:分析参考项目标擎 BidEngine 的架构、提示词和模型路由,整理出可参考的做法和避坑清单(`docs/architecture/ref-bid-engine-analysis.md`),留给技术架构阶段用。
- 2026-11-03:技术架构设计 v0.2(`docs/architecture/architecture.md`)。T2–T5 已确认:Python 单语言、桌面端做客户端外壳、SaaS 先 Compose 后 K8s、云无关。之后确认了 AR1–AR3、AR5–AR9,前端改为 Vue 3;AR4 复议后改为引入 Valkey(已确认);AR10 定为不引入带样式的 UI 框架,用 Reka UI 打底。选型全部确认。编码前先做技术验证 S1–S4。
-- 下一步:用原型找种子用户走查(顺带问 G15 苏采云客户端的分册对应),再进入 UI 设计精修。
+- 2026-11-03:种子用户走查结束,没有问题。进入 UI 精修前先做 UI demo(`docs/design/ui-demo/`,6 个页面):控件、表单、卡片、列表,以及工作台、读标两个组合页;新增组件清单和待定问题 Q1–Q12 见其 index。
+- 下一步:UI 精修,先定 UI demo 里的 Q1–Q12。
diff --git a/docs/design/README.md b/docs/design/README.md
index 2323003..4a0ce5f 100644
--- a/docs/design/README.md
+++ b/docs/design/README.md
@@ -10,6 +10,7 @@
| [system/tokens.css](system/tokens.css) | 设计变量(原型和前端直接引用) | v0.1 |
| [system/components.css](system/components.css) | 组件样式参考实现(预览页和原型共用) | v0.1 |
| [system/components.html](system/components.html) | 组件预览页(用样本条款演示) | v0.1 |
+| [ui-demo/](ui-demo/index.html) | UI demo:控件总览、表单、卡片、列表与表格,以及工作台、读标两个组合页;附新增组件清单和 UI 精修待定问题 Q1–Q12 | 供 UI 精修评审 |
## 速查
diff --git a/docs/design/ui-demo/cards.html b/docs/design/ui-demo/cards.html
new file mode 100644
index 0000000..3ca86e6
--- /dev/null
+++ b/docs/design/ui-demo/cards.html
@@ -0,0 +1,240 @@
+
+
+
+
+
+
+
+
+ 01
项目卡
规范 §7.9 · 卡片视图(表格视图见 ④)
+
+
+ 某市政务服务中心智慧大厅建设项目
某市政务服务管理办公室 · 政府采购 · 公开招标
已检查
+
+
投标截止11月20日 09:30
剩 17 天
+
所投包采购包 1
+
就绪未就绪
+
+ 致命3重要3提示2第 2 次检查 · 11月2日
+
+
+
+ 某区智慧社区平台运维服务
某区民政局 · 政府采购 · 竞争性磋商
已检查
+
+
响应截止剩 2 天 5 小时
+
所投包全部
+
就绪就绪
+
+ 致命 —重要1第 3 次检查 · 11月3日
+
+
+
+ 某县数据中心机房改造项目
某县大数据管理局 · 招标投标
编制中
+
+
投标截止今天 17:00 截止
+
所投标段标段 1
+
清单草稿 v1
+
+ 跟进中编制中已检查已递交
+
+
+
+
+
+ 某区智慧校园一期
某区教育局 · 政府采购 · 公开招标
已递交
+
+
投标截止已截止
+
所投包全部
+
递交10月28日
+
+ ✓王敏10月28日 09:12标记已递交
+
+
+
+
+
+
+ ↑ 加载中(骨架屏)
+
+
+
+ - 致命项目卡左侧加 3px 致命红竖线(与问题行一致),方便在卡片墙里扫读;状态用中性描边标签,不用彩色。
- 术语跟着体系走:磋商项目写「响应截止」「响应文件」,招标投标体系写「标段」(§9.1)。
+
+
+
+
+ 02
统计格
规范 §4.2 Figure 30/36
+
+ - 统计数字一律墨色,即使是「致命问题 3」也不染红:红色留给标签,避免一屏红色太多。
+
+
+
+
+ 03
额度卡
规范 §7.10 · §8.3 · 红线 9
+
+
+
本期额度
专业版 · 至 2027-10-31
+
+
+
+
+
检查额度
已用 80%
+
检查剩余4/ 20 次
+
+
已用 80%。同一项目 24 小时内复查不扣额度。
+
+
+
+
检查额度
已用完
+
检查剩余0/ 20 次
+
+
本期检查额度已用完,续费或下期发放后可继续。
+
+
+
+
+
+
+
+
+ 04
任务进度卡
规范 §7.11
+
+
+
正在解析招标文件
已用时 1 分 40 秒
+
ZC-2026-0418 采购文件.pdf · 186 页
+
+ - ✓识别文件格式
+ - ✓提取文字186 页
+ - ✓识别分包2 个包
+ - 提取清单62%
+ - 核对出处
+
+
+
可以离开本页,完成后会发站内通知。
+
+
+
+
解析失败
11月2日 14:31
+
关于 ZC-2026-0418 的更正公告.pdf
+
+ - ✓识别文件格式
+ - !提取文字
+ - 识别分包
+ - 提取清单
+
+
!文件已加密,无法读取文字。已退回 1 次解析额度。
请上传未加密的版本,或在 PDF 阅读器里另存一份。
+
+
+
+
+
解析完成
用时 3 分 12 秒
+
+ - 提取要求
- 26 条 · 其中 ★ 4 条
+ - 待确认
- 2 条没有可靠出处
+ - 分包
- 2 个采购包
+
+
+
+
+
检查排队中
前面还有 2 个任务 · 约 4 分钟
+
+
+
+
+
+
+
+ 05
AI 摘要卡
规范 §7.16 · §8.1 · 红线 1、2
+
+
+
AI 摘要
项目要点
未确认 · 每一条都可以点出处核对
+
+
+ - 法律体系
- 政府采购 · 公开招标(按「投标无效」口径检查)
+ - 预算 / 限价
- 采购包 1:286.00 万元(最高限价同预算)第 2 页
+ - 评标方法
- 综合评分法:价格 10、技术 40、实施方案 20、售后 10、认证 6、业绩 8第 31 页
+ - 联合体
- 不接受第 2 页
+ - 保证金
- 不收取第 21 页
+ - 样品
- 未找到要求 · 已查找:第二章、第三章、第六章
+
+
+
+
+
+
项目要点
✓王敏11月2日 14:20确认无误
+
+
+ - 法律体系
- 政府采购 · 公开招标(按「投标无效」口径检查)
+ - 预算 / 限价
- 采购包 1:286.00 万元(最高限价同预算)第 2 页
+ - 评标方法
- 综合评分法:价格 10、技术 40、实施方案 20、售后 10、认证 6、业绩 8第 31 页
+ - 联合体
- 不接受 已修改 · 查看记录第 2 页
+ - 保证金
- 不收取第 21 页
+ - 样品
- 未找到要求 · 已查找:第二章、第三章、第六章
+
+
+
+
+
+ - 确认前:「AI 摘要」蓝铅笔标签 + 左侧虚线;确认后:标签换成绿色署名,虚线变墨色实线,正文本来就是墨色。
- 找不到的项写「未找到 · 已查找:…」,不猜测、不留空。胜率之类的数据不足时不展示(红线 5)。
+
+
+
+
+ 06
关键日期卡
规范 §7.9 倒计时 · §9.3
+
+
+
关键日期
以补遗 1 更正后的时间为准
+
+ - 10-28采购文件公告发布
+ - 11-09询问、质疑截止公告期满后 7 个工作日 · 剩 6 天第 6 页
+ - 11-20 09:30投标截止 / 开标剩 17 天 · 已被补遗 1 更新,原为
11-18 09:30补遗 1 · 第 1 页
+ - 2027-02-18投标有效期至截止日起 90 天第 21 页
+
+
MVP 只在站内显示倒计时,不发截止提醒。
+
+
+
+
+ - 今天 17:00某县数据中心机房改造项目剩 6 小时 · 编制中标段 1
+ - 11-05 14:30某区智慧社区平台运维服务剩 2 天 5 小时 · 就绪全部
+
+
- > 72 小时:墨色「11月20日 09:30 · 剩 17 天」;≤ 72 小时:赭色小字;≤ 24 小时:赭色加粗;已过:辅助灰「已截止」。
- 截止临近不用红色、不闪烁。
+
+
+
+
+
+
+
+
+
diff --git a/docs/design/ui-demo/controls.html b/docs/design/ui-demo/controls.html
new file mode 100644
index 0000000..065eaf9
--- /dev/null
+++ b/docs/design/ui-demo/controls.html
@@ -0,0 +1,492 @@
+
+
+
+
+
+
+
+
+ 01
按钮
规范 §7.1 · §8.3
+
+
类型 × 状态
+
+ | 默认 | 悬停 | 按下 | 键盘聚焦 | 禁用 | 加载中 |
+
+ | 主按钮 | | | | | | |
+ | 次按钮 | | | | | | |
+ | 文字按钮 | | | | | | (不用) |
+
+
+
+ - 一屏只有一个主按钮。没有红色的危险按钮:删除用次按钮 + 确认对话框。
+ - 加载中把文字换成「正在…」,两段文字叠在同一格里,按较宽的定宽,切换时按钮不跳动;不加转圈图标。
+ - 主按钮悬停变浅 8%(用 color-mix 计算,不新增颜色变量);按下回到墨色并加一道内阴影。
+
+
+
+
+
尺寸
+
+
- 28:表格行内 · 32:默认 · 40:登录页、空状态
+
+
+
带图标 · 只有图标
+
+
+
+
+
+
+
+
- 图标必须和文字一起出现;只有图标时要有 aria-label 和悬停提示。
+
+
+
会扣点的按钮 可以点
+
扣 1 次检查 · 剩 12 次
+
24 小时内复查,不扣额度
+
剩 0 次 · 按钮仍可点
+
+
+
+
+
+
+
+
+
+ 03
下拉选择
AR10 · Reka UI Select 打底
+
+
+
+
+
展开(浮层:4px 圆角 + 阴影)
+
+
+
错误
+
+
+
!请选择资质类型,查标时按类型核对
+
+
+
+
- 键盘:↑ ↓ 移动、Enter 选中、Esc 收起。选中项加粗并在右侧打 ✓(形状 + 字重,不只靠颜色)。
- 不可选的项用禁用灰,并在右侧写原因。
+
+
+
+
+
+ 04
日期选择
规范 §9.3 · AR10
+
+
+
收起 · 可以点
+
+
+
以补遗 1 的更正为准 · 剩 17 天
+
+
+
+
!发证日期晚于有效期,请核对证书
+
+
+
展开
+
+
- 一周从周一开始;数字用 Barlow 等宽。
- 今天:控件灰描边;选中:墨底白字;关键日期:下方小圆点。
+
+
+
有不可选的日期
+
+
- 不可选的日期加删除线(不只靠变灰)。
- 界面显示「11月20日 09:30」,输入框里用「2026-11-20」便于核对和粘贴。
+
+
+
+
+
+
+ 05
复选 · 单选 · 开关
规范 §8.2 批量勾选
+
+
+
+
单选
+
+
单选卡片(选项需要说明时)
+
+
+
+
+
+
+
开关 可以点
+
+
关
+
开
+
禁用 · 关
+
禁用 · 开
+
+
+
+
+
+
- 规范没有定义开关。这里沿用 2px 圆角做成方形滑块(不用胶囊形),旁边写「开 / 关」,不只靠颜色。
+
+
+
+
+
+
+ 06
级别标签 · 状态标签 · AI 标识 · 署名
规范 §3.3 · §7.2 · §7.5 · §7.16
+
+
+
问题级别(实心 / 描边 / 无框)
+
致命重要提示计数致命3重要3提示3
+
参数判断疑似不满足疑似不满足满足需人工判断未找到响应
+
项目状态 · 就绪 · 复查标记
+
跟进中编制中已检查已递交已归档
+
未就绪就绪新增沿用本次已解决
+
清单状态草稿 v3已确认 v2✓王敏11月2日 15:12确认
+
分类资格条件无效条款技术参数采购包 1通用
+
+
+
AI 产出 → 人工确认(颜色说明谁说的)
+
AI 摘要AI 初稿AI 提取人工添加补遗 1已修改 · 查看记录
+
+
AI 产出:蓝铅笔标签 + 左侧虚线
+
AI 摘要未确认预算 418.00 万元,分 2 个采购包;采购包 1 最高限价 286.00 万元。
招标文件 · 第 2 页
+
人工确认后:墨色正文 + 实线 + 绿色署名
+
预算 418.00 万元,分 2 个采购包;采购包 1 最高限价 286.00 万元。
招标文件 · 第 2 页
+
✓王敏11月2日 14:20确认无误
+
问题处理后的署名行
+
+
–王敏11月2日 14:26已忽略:招标文件允许撤销
+
+
+
+
+
+
+
+ 07
出处
规范 §7.3 · 最重要的组件
+
+
+
招标原文 → 投标原文
+
招标文件 · 第 41 页★ 2U机架式服务器,CPU:配置≥2颗海光架构32C 2.7GHz,内存: ≥512G DDR4,系统盘: 2块≥480G SSD……
+
+
- 页码用 Barlow;原句照录,不加空格、不改标点。
+
+
+
「未找到」也是出处
+
招标文件 · 第 44 页★ 支持硬件BFD,最小报文发送间隔≤50ms,提供相关证明材料(包含但不限于产品官网公开参数截图证明等)。
+
投标文件 · 未找到证明材料未找到 · 已查找:技术册(全部章节)、商务册(附件)
+
+
+
+
OCR · 截断 · 当前选中
+
+
招标文件 · 第 23 页2.财务状况报告,至少提供:(1)本项目开标时间前6个月内任一时间截点的资产负债表的扫描件1份;(2)本项目开标时间前6个月内任何1月(不含开标当月)利润表月报表的扫描件1份。3.本项目开标时间前6个月内任何1月的依法缴纳税收和社会保障资金的相关材料的扫描件。
+
+
- 选中的出处竖线加粗到 4px。超过 3 行截断,「展开原文」看全句。
+
+
+
+
+
+
+ 08
提醒(页内 Alert)
规范 §7.12 · §7.15
+
+
+
+
!本期检查额度已用 80%。剩 4 次,额度用完后续费或下期发放后可继续。
查看额度
+
!投标截止还剩 2 天 5 小时。还有 3 项致命问题待处理。
截止临近用赭色,不用红色;只提醒,不拦截。
+
+
+
!解析失败:文件已加密。已退回 1 次解析额度。请上传未加密的版本。
关于 ZC-2026-0418 的更正公告.pdf
+
i出了点问题,请稍后重试。本次操作未扣额度。
错误编号 E-20261103-7Q4K · 联系客服时请提供
+
✓报告已导出:智慧大厅建设项目-辅助检查报告-第 2 次.pdf
+
+
+ - 提醒放在相关内容上方,不自动消失。系统错误不用红色,用中性提醒 + 错误编号。
+
+
+
+
+ 09
轻提示(Toast)
规范 §7.12
+
+
+
样式
+
+
✓清单 v3 已确认
+
✓已标记「已修改」 · 复查时会再查
+
已复制错误编号
+
+
+
+
触发 可以点
+
+
- 只用于操作结果;4 秒后消失,鼠标悬停时暂停计时。
- 可撤销的操作在轻提示里给「撤销」;失败不用轻提示,用页内提醒说清原因。
- 多条从下往上堆叠,屏幕阅读器会播报。
+
+
+
+
+
+
+ 10
对话框
规范 §7.14 · 只用于不可撤销的操作
+
+
+
删除项目「某区智慧社区平台运维服务」?
+
+
项目下的招标文件、清单、检查结果和报告会一起删除。删除后 30 天内可以在回收站恢复,之后彻底删除。
+
+
+
+
+
+
打开真实对话框 可以点
+
+
+
+
+
+
- 宽 480(确认)/ 640(表单),圆角 4。
- 标题写要做的事,正文写后果;主操作在右,按钮写动作「删除项目」,不写「确定」。
- 删除没有红色:靠输入项目名确认。Esc 关闭,焦点回到触发按钮。
+
+
+
+
+
+
+ 11
标签页 · 分段控件 · 分页
规范 §5.3
+
+
+
标签页 可以点,← → 切换
+
+
+
+
+
+
+
+
+
+
+
+
+
+
分页 可以点
+
+
- 当前页用墨色描边 + 加粗(不填色,避免和主按钮抢)。每页条数固定 20 / 50,不做无限滚动,方便「第几页第几条」沟通。
+
+
+
+
+
+ 12
空状态 · 骨架屏 · 进度
规范 §7.10 · §7.11 · §7.15
+
+
+
空状态
+
还没有招标文件上传后,标博士会逐条提取要求,每条都附原文出处。
扣 1 次解析 · 剩 37 次
+
+
+
骨架屏(加载清单)
+
+
- 不用转圈占满屏。骨架做缓慢的明暗变化(1.6 秒),系统开了「减少动态效果」时静止。
+
+
+
分阶段进度
+
+ - ✓识别文件格式
+ - ✓提取文字186 页
+ - ✓识别分包2 个包
+ - 提取清单62%
+ - 核对出处
+
+
+
可以离开本页,完成后会发站内通知。
+
+
+
+
+
检查剩余 · 已用 80%4/ 20 次
已用 80%,剩 4 次
+
检查剩余 · 已用完0/ 20 次
本期检查额度已用完,续费或下期发放后可继续
+
+
+
+
+
+ 13
提示气泡 · 下拉菜单 · 快捷键
规范 §6 · §8.2
+
+
+
提示气泡
+
就绪
+
+
- 处理三按钮的悬停提示写清含义(§7.4)。墨底白字,4px 圆角。
+
+
+
+
快捷键
+
+
+ | J / K | 下一条 / 上一条 |
+ | O · Enter | 打开当前出处 |
+ | 1 2 3 | 确认无误 / 已修改 / 忽略 |
+ | U | 撤销当前条 |
+ | ? | 快捷键说明 |
+
+
+
在「④ 列表与表格」和「⑥ 读标」页可以实际用 J / K。
+
+
+
+
+
+
+
+
+
diff --git a/docs/design/ui-demo/demo.css b/docs/design/ui-demo/demo.css
new file mode 100644
index 0000000..00a78bd
--- /dev/null
+++ b/docs/design/ui-demo/demo.css
@@ -0,0 +1,524 @@
+/* 标博士 · UI demo 样式(2026-11-03)
+ * 依赖 ../system/tokens.css 和 ../system/components.css,只用 tokens.css 的变量,不写裸色值。
+ * 分两部分:
+ * A. 演示页排版:只属于 demo 页面(章节标题、状态陈列格),不进组件库;
+ * B. 新增组件:design-system.md 有描述、但 components.css 里还没有的组件。UI 精修定稿后再并入 components.css。
+ * 新增组件清单见 index.html。 */
+
+/* =====================================================================
+ * A. 演示页排版
+ * ===================================================================== */
+body.demo{min-width:1280px}
+.d-top{position:sticky;top:0;z-index:30;display:flex;align-items:center;gap:var(--sp-8);height:52px;padding:0 var(--sp-8);background:var(--bg-page);border-bottom:1px solid var(--border-subtle)}
+.d-top .brand img{height:26px;display:block}
+.d-top .tag{margin-left:calc(-1 * var(--sp-5))}
+.d-nav{display:flex;gap:var(--sp-1);margin-left:auto}
+.d-nav a{display:flex;align-items:center;height:52px;padding:0 var(--sp-3);color:var(--text-secondary);text-decoration:none;white-space:nowrap}
+.d-nav a:hover{color:var(--text-primary)}
+.d-nav a.on{color:var(--text-primary);font-weight:var(--fw-bold);box-shadow:inset 0 -2px 0 var(--c-ink)}
+.d-page{max-width:calc(var(--content-max) + 2 * var(--sp-8));margin:0 auto;padding:var(--sp-10) var(--sp-8) var(--sp-16)}
+.d-head h1{font:var(--fw-bold) var(--fs-h1)/var(--lh-h1) var(--font-serif);margin:0}
+.d-head p{color:var(--text-secondary);margin:var(--sp-2) 0 0;max-width:60em}
+.d-toc{display:flex;flex-wrap:wrap;gap:var(--sp-2) var(--sp-4);margin-top:var(--sp-4);font-size:var(--fs-caption)}
+.d-toc a{color:var(--text-secondary);text-underline-offset:3px}
+.d-toc a:hover{color:var(--text-primary)}
+.d-sec{margin-top:var(--sp-12);scroll-margin-top:72px}
+.d-sec-h{display:flex;align-items:baseline;gap:var(--sp-3);border-bottom:2px solid var(--border-strong);padding-bottom:var(--sp-2);margin-bottom:var(--sp-5)}
+.d-sec-h h2{font:var(--fw-bold) var(--fs-h2)/var(--lh-h2) var(--font-serif);margin:0}
+.d-sec-h .no{font:var(--fw-bold) var(--fs-title)/1 var(--font-figures);color:var(--text-secondary)}
+.d-sec-h .ref{margin-left:auto;font-size:var(--fs-caption);color:var(--text-secondary)}
+.d-grid{display:grid;gap:var(--sp-6);align-items:start}
+.d-grid.c2{grid-template-columns:repeat(2,minmax(0,1fr))}
+.d-grid.c3{grid-template-columns:repeat(3,minmax(0,1fr))}
+.d-grid.eq{align-items:stretch}
+.d-grid.c-side{grid-template-columns:minmax(0,1fr) 360px}
+.d-box{background:var(--bg-surface);border:1px solid var(--border-subtle);border-radius:var(--radius-sm);padding:var(--sp-5) var(--sp-6)}
+.d-box > h3{font:var(--fw-bold) var(--fs-title)/var(--lh-title) var(--font-sans);margin:0 0 var(--sp-4);display:flex;gap:var(--sp-2);align-items:baseline}
+.d-box > h3 small{font-weight:var(--fw-regular);font-size:var(--fs-caption);color:var(--text-secondary)}
+.d-box.flat{background:none;border-style:dashed;border-color:var(--border-control)}
+.d-states{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:var(--sp-5) var(--sp-5)}
+.d-states.wide{grid-template-columns:repeat(auto-fill,minmax(220px,1fr))}
+.d-state{display:flex;flex-direction:column;align-items:flex-start;gap:var(--sp-2);min-width:0}
+.d-state > small,.d-cap{font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--text-secondary)}
+.d-state .field{width:100%}
+.d-rules{margin:var(--sp-4) 0 0;padding:var(--sp-3) 0 0 1.2em;border-top:1px solid var(--border-subtle);font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--text-secondary)}
+.d-rules li + li{margin-top:2px}
+.d-label{display:block;font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--text-secondary);margin-bottom:var(--sp-2)}
+.d-stack > * + *{margin-top:var(--sp-4)}
+.d-row{display:flex;flex-wrap:wrap;gap:var(--sp-3);align-items:center}
+.d-row + .d-row{margin-top:var(--sp-3)}
+.d-matrix{border-collapse:collapse;width:100%}
+.d-matrix th{font-weight:var(--fw-regular);font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--text-secondary);text-align:left;padding:0 var(--sp-3) var(--sp-2) 0;white-space:nowrap}
+.d-matrix td{padding:var(--sp-2) var(--sp-3) var(--sp-2) 0;vertical-align:middle}
+.d-matrix td:first-child{font-size:var(--fs-caption);color:var(--text-secondary);white-space:nowrap;width:72px}
+.d-open{position:relative;padding-bottom:var(--sp-2)} /* 静态展开的浮层,占位用 */
+.d-open .menu,.d-open .cal{position:static;margin-top:var(--sp-1)}
+
+/* 目录页 */
+.d-cards{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--sp-4)}
+.d-link{display:block;background:var(--bg-surface);border:1px solid var(--border-subtle);border-radius:var(--radius-sm);padding:var(--sp-4) var(--sp-5);color:var(--text-primary);text-decoration:none}
+.d-link:hover{border-color:var(--border-control)}
+.d-link b{display:flex;gap:var(--sp-2);align-items:baseline;font-size:var(--fs-title);line-height:var(--lh-title)}
+.d-link b .fig{color:var(--text-secondary);font-size:var(--fs-title)}
+.d-link p{margin:var(--sp-2) 0 0;color:var(--text-secondary);font-size:var(--fs-caption);line-height:var(--lh-caption)}
+
+/* =====================================================================
+ * B. 新增组件
+ * ===================================================================== */
+
+/* ---- B1 图标(Lucide 线性,1.5px 描边,16 / 20px,§6) ---- */
+.ic{width:16px;height:16px;flex:none;fill:none;stroke:currentColor;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;vertical-align:-3px}
+.ic.lg{width:20px;height:20px}
+.ic.xl{width:32px;height:32px;stroke-width:1.25}
+
+/* ---- B2 页面框架:侧边栏 + 顶栏(§5.3) ---- */
+body.app{display:grid;grid-template-columns:var(--sidebar-w) minmax(0,1fr);min-height:100vh;min-width:1280px}
+.sidebar{position:sticky;top:0;height:100vh;display:flex;flex-direction:column;border-right:1px solid var(--border-subtle);background:var(--bg-page);padding:var(--sp-5) var(--sp-3)}
+.sidebar .brand{display:block;padding:0 var(--sp-2) var(--sp-6)}
+.sidebar .brand img{height:30px;display:block}
+.nav{display:flex;flex-direction:column;gap:2px}
+.nav a{display:flex;align-items:center;gap:var(--sp-3);height:36px;padding:0 var(--sp-3);border-radius:var(--radius-sm);color:var(--text-primary);text-decoration:none}
+.nav a:hover{background:var(--bg-hover)}
+.nav a.on{background:var(--bg-surface);box-shadow:inset 2px 0 0 var(--c-ink);font-weight:var(--fw-bold)}
+.nav a .fig{margin-left:auto;font-weight:var(--fw-medium);color:var(--text-secondary)}
+.nav .sub{margin:var(--sp-5) var(--sp-3) var(--sp-2);font-size:var(--fs-caption);color:var(--text-secondary)}
+.sidebar-foot{margin-top:auto;padding:var(--sp-3);border-top:1px solid var(--border-subtle);font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--text-secondary)}
+.sidebar-foot b{display:block;color:var(--text-primary);font-size:var(--fs-body)}
+.main{min-width:0;display:flex;flex-direction:column}
+.top{position:sticky;top:0;z-index:20;display:flex;align-items:center;gap:var(--sp-4);height:52px;padding:0 var(--sp-8);background:var(--bg-page);border-bottom:1px solid var(--border-subtle)}
+.crumbs{display:flex;gap:var(--sp-2);color:var(--text-secondary);min-width:0;white-space:nowrap;overflow:hidden}
+.crumbs a{color:var(--text-secondary);text-decoration:none}
+.crumbs a:hover{color:var(--text-primary);text-decoration:underline;text-underline-offset:3px}
+.crumbs b{color:var(--text-primary);font-weight:var(--fw-regular);overflow:hidden;text-overflow:ellipsis}
+.top-right{margin-left:auto;display:flex;align-items:center;gap:var(--sp-3)}
+.quota-chip{display:flex;gap:var(--sp-3);align-items:center;height:28px;padding:0 var(--sp-3);border:1px solid var(--border-subtle);border-radius:var(--radius-sm);color:var(--text-secondary);text-decoration:none;font-size:var(--fs-caption);background:var(--bg-surface)}
+.quota-chip.warn{border-color:var(--warn);color:var(--warn-text)}
+.quota-chip .fig{color:var(--text-primary);font-size:var(--fs-body);margin-left:3px}
+.content{padding:var(--sp-6) var(--sp-8) var(--sp-16);max-width:calc(var(--content-max) + 2 * var(--sp-8));width:100%}
+.page-title{display:flex;align-items:flex-end;gap:var(--sp-4);margin-bottom:var(--sp-5)}
+.page-title h1{font:var(--fw-bold) var(--fs-h1)/var(--lh-h1) var(--font-serif);margin:0}
+.page-title p{margin:var(--sp-1) 0 0;color:var(--text-secondary)}
+.page-title .actions{margin-left:auto;display:flex;gap:var(--sp-2);align-items:center}
+.section-h{display:flex;align-items:center;gap:var(--sp-3);margin:var(--sp-8) 0 var(--sp-3)}
+.section-h h2{font:var(--fw-bold) var(--fs-title)/var(--lh-title) var(--font-sans);margin:0}
+.section-h .more{margin-left:auto;display:flex;gap:var(--sp-2);align-items:center}
+
+/* 项目页头 */
+.proj-head{padding:var(--sp-5) var(--sp-8) 0;border-bottom:1px solid var(--border-subtle);background:var(--bg-page)}
+.proj-head .line1{display:flex;align-items:flex-start;gap:var(--sp-4)}
+.proj-head h1{font:var(--fw-bold) var(--fs-h1)/var(--lh-h1) var(--font-serif);margin:0}
+.proj-head .meta{margin-top:var(--sp-1);color:var(--text-secondary);display:flex;flex-wrap:wrap;gap:var(--sp-2) var(--sp-4)}
+.proj-head .facts{margin-left:auto;display:flex;gap:var(--sp-6);text-align:right;white-space:nowrap}
+.proj-head .facts small{display:block;font-size:var(--fs-caption);color:var(--text-secondary)}
+
+/* ---- B3 按钮补充状态(§7.1) ---- */
+.btn:active,.btn.is-active{background:var(--bg-sunken);border-color:var(--c-ink)}
+.btn.is-hover{background:var(--bg-hover)}
+.btn.primary.is-hover{background:color-mix(in srgb,var(--c-ink) 92%,var(--c-white))}
+.btn.primary:active,.btn.primary.is-active{background:var(--c-ink);box-shadow:inset 0 2px 0 color-mix(in srgb,var(--c-white) 18%,transparent)}
+.btn.text:active,.btn.text.is-active{background:var(--bg-sunken);border-color:transparent}
+.btn.is-focus{outline:2px solid var(--focus-ring);outline-offset:2px}
+.btn .ic{margin:0 -2px}
+/* 加载中:两个文字叠在同一格,按宽的那个定宽,切换时宽度不变 */
+.btn.swap{display:inline-grid;place-items:center}
+.btn.swap > span{grid-area:1/1;white-space:nowrap}
+.btn.swap > .busy,.btn.swap.is-loading > .idle{visibility:hidden}
+.btn.swap.is-loading > .busy{visibility:visible}
+.btn.is-loading{cursor:progress}
+.btn.primary.is-loading{background:color-mix(in srgb,var(--c-ink) 80%,var(--c-white));border-color:transparent}
+.btn.is-loading:not(.primary){color:var(--text-secondary)}
+.icon-btn{display:inline-grid;place-items:center;width:32px;height:32px;border-radius:var(--radius-sm);color:var(--text-primary);background:none;border:1px solid transparent;cursor:pointer;position:relative;flex:none}
+.icon-btn:hover{background:var(--bg-hover)}
+.icon-btn.bordered{border-color:var(--border-control);background:var(--bg-surface)}
+.icon-btn.sm{width:28px;height:28px}
+.icon-btn .dot{position:absolute;top:6px;right:6px;width:6px;height:6px;border-radius:50%;background:var(--c-ink)}
+.icon-btn[disabled]{color:var(--text-disabled);cursor:not-allowed;background:none}
+.btn-group{display:inline-flex}
+.btn-group .btn + .btn{margin-left:-1px}
+.btn-group .btn:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}
+.btn-group .btn:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}
+
+/* ---- B4 输入框、文本域、带单位、搜索(§7.13) ---- */
+.input{width:100%;transition:border-color var(--dur-fast) var(--ease)}
+.input:hover{border-color:var(--c-ink)}
+.input:focus,.input.is-focus{outline:2px solid var(--focus-ring);outline-offset:2px;border-color:var(--c-ink)}
+.input.is-hover{border-color:var(--c-ink)}
+.input[disabled],.input.is-disabled{background:var(--bg-sunken);border-color:var(--bg-sunken);color:var(--text-disabled);cursor:not-allowed}
+.input[disabled]::placeholder{color:var(--text-disabled)}
+.input[readonly]{background:var(--bg-sunken);border-color:var(--border-subtle)}
+.input[readonly]:focus{outline-color:var(--border-control)}
+textarea.input{height:auto;min-height:80px;padding:var(--sp-2) 10px;resize:vertical;line-height:var(--lh-body)}
+select.input{appearance:none;padding-right:28px;cursor:pointer}
+.sel-wrap{position:relative;display:block}
+.sel-wrap > .ic{position:absolute;right:8px;top:8px;pointer-events:none;color:var(--text-secondary)}
+.field label{display:flex;gap:var(--sp-1);align-items:baseline}
+.field label small{margin-left:2px}
+.field .msg-row{display:flex;gap:var(--sp-3);align-items:baseline}
+.field .msg-row .msg{flex:1}
+.field .count{font:var(--fw-medium) var(--fs-caption)/var(--lh-caption) var(--font-figures);font-variant-numeric:tabular-nums;color:var(--text-secondary);white-space:nowrap}
+.field.err .count{color:var(--warn-text)}
+.field.err textarea.input,.field.err .select{border-color:var(--warn);box-shadow:inset 0 0 0 1px var(--warn)}
+.field.dis label{color:var(--text-disabled)}
+.field.fill{max-width:none}
+.field + .field{margin-top:0}
+.input-unit{display:flex}
+.input-unit .input{border-top-right-radius:0;border-bottom-right-radius:0;font-family:var(--font-figures);font-variant-numeric:tabular-nums;font-size:15px}
+.input-unit .unit{display:flex;align-items:center;padding:0 10px;border:1px solid var(--border-control);border-left:0;border-radius:0 var(--radius-sm) var(--radius-sm) 0;background:var(--bg-sunken);color:var(--text-secondary);white-space:nowrap}
+.search{position:relative;display:block}
+.search > .ic{position:absolute;left:10px;top:8px;color:var(--text-secondary);pointer-events:none}
+.search .input{padding-left:32px}
+.search .kbd{position:absolute;right:8px;top:6px}
+
+/* 表单布局 */
+.form{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--sp-5) var(--sp-6)}
+.form .span2{grid-column:1 / -1}
+.form .field{max-width:none}
+.form-sec{margin:0;padding:0;border:0;min-width:0}
+.form-sec + .form-sec{margin-top:var(--sp-8);padding-top:var(--sp-6);border-top:1px solid var(--border-subtle)}
+.form-sec > legend{float:left;width:100%;padding:0;margin:0 0 var(--sp-4);font:var(--fw-bold) var(--fs-title)/var(--lh-title) var(--font-sans)}
+.form-sec > legend small{font-weight:var(--fw-regular);font-size:var(--fs-caption);color:var(--text-secondary);margin-left:var(--sp-2)}
+.form-sec > legend + *{clear:both}
+.form-foot{display:flex;gap:var(--sp-3);align-items:center;margin-top:var(--sp-8);padding-top:var(--sp-5);border-top:1px solid var(--border-subtle)}
+.form-foot .cost{margin-right:auto}
+.err-sum{margin-bottom:var(--sp-5)}
+.err-sum ul{margin:var(--sp-1) 0 0;padding-left:1.2em}
+.err-sum a{color:var(--warn-text);text-underline-offset:3px}
+
+/* ---- B5 下拉选择 / 下拉菜单(浮层,§5.1 只有浮层有阴影) ---- */
+.pop{position:relative;display:block}
+.pop.inline{display:inline-block}
+.select{display:flex;align-items:center;gap:var(--sp-2);width:100%;height:var(--control-h);padding:0 8px 0 10px;border:1px solid var(--border-control);border-radius:var(--radius-sm);background:var(--bg-surface);font:var(--fs-body) var(--font-sans);color:var(--text-primary);text-align:left;cursor:pointer}
+.select:hover{border-color:var(--c-ink)}
+.select[aria-expanded="true"],.select.is-focus{outline:2px solid var(--focus-ring);outline-offset:2px;border-color:var(--c-ink)}
+.select .val{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
+.select .ph{color:var(--text-secondary)}
+.select > .ic{color:var(--text-secondary);transition:transform var(--dur-fast) var(--ease)}
+.select[aria-expanded="true"] > .ic{transform:rotate(180deg)}
+.select[disabled]{background:var(--bg-sunken);border-color:var(--bg-sunken);color:var(--text-disabled);cursor:not-allowed}
+.select.sm{height:var(--control-h-sm)}
+.menu{position:absolute;z-index:45;top:calc(100% + 4px);left:0;min-width:100%;max-height:280px;overflow:auto;background:var(--bg-surface);border:1px solid var(--border-subtle);border-radius:var(--radius-md);box-shadow:var(--shadow-overlay);padding:var(--sp-1) 0;margin:0;list-style:none}
+.menu.right{left:auto;right:0}
+.menu [role=option],.menu [role=menuitem]{display:flex;align-items:center;gap:var(--sp-2);min-height:32px;padding:5px var(--sp-3);cursor:pointer;white-space:nowrap}
+.menu [role=option]:hover,.menu [role=menuitem]:hover,.menu .is-active{background:var(--bg-hover)}
+.menu [aria-selected="true"]{font-weight:var(--fw-bold)}
+.menu [aria-selected="true"]::after{content:"✓";margin-left:auto;padding-left:var(--sp-4)}
+.menu [aria-disabled="true"]{color:var(--text-disabled);cursor:not-allowed;background:none}
+.menu .grp{padding:var(--sp-2) var(--sp-3) 2px;font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--text-secondary)}
+.menu hr{border:0;border-top:1px solid var(--border-subtle);margin:var(--sp-1) 0}
+.menu small{color:var(--text-secondary);font-size:var(--fs-caption);margin-left:auto;padding-left:var(--sp-4)}
+.menu .ic{color:var(--text-secondary)}
+
+/* ---- B6 日期选择(§9.3 日期格式) ---- */
+.date-trigger{font-family:var(--font-figures);font-variant-numeric:tabular-nums;font-size:15px}
+.date-trigger .ph{font-family:var(--font-sans);font-size:var(--fs-body)}
+.cal{position:absolute;z-index:45;top:calc(100% + 4px);left:0;width:280px;background:var(--bg-surface);border:1px solid var(--border-subtle);border-radius:var(--radius-md);box-shadow:var(--shadow-overlay);padding:var(--sp-3)}
+.cal-h{display:flex;align-items:center;gap:var(--sp-1);margin-bottom:var(--sp-2)}
+.cal-h b{flex:1;text-align:center;font-weight:var(--fw-bold)}
+.cal-h b .fig{font-size:15px}
+.cal-grid{display:grid;grid-template-columns:repeat(7,1fr);gap:2px;text-align:center}
+.cal-grid .wd{font-size:var(--fs-caption);line-height:28px;color:var(--text-secondary)}
+.cal-grid button{position:relative;height:32px;border:1px solid transparent;border-radius:var(--radius-sm);background:none;font:var(--fw-medium) 15px/1 var(--font-figures);font-variant-numeric:tabular-nums;color:var(--text-primary);cursor:pointer}
+.cal-grid button:hover{background:var(--bg-hover)}
+.cal-grid button.out{color:var(--text-disabled)}
+.cal-grid button.today{border-color:var(--border-control)}
+.cal-grid button.mark::after{content:"";position:absolute;left:50%;bottom:3px;width:4px;height:4px;margin-left:-2px;border-radius:50%;background:var(--c-ink)}
+.cal-grid button[aria-selected="true"]{background:var(--c-ink);color:var(--text-on-dark);border-color:var(--c-ink)}
+.cal-grid button[aria-selected="true"].mark::after{background:var(--c-white)}
+.cal-grid button[disabled]{color:var(--text-disabled);cursor:not-allowed;background:none;text-decoration:line-through}
+.cal-f{display:flex;align-items:center;gap:var(--sp-2);margin-top:var(--sp-2);padding-top:var(--sp-2);border-top:1px solid var(--border-subtle);font-size:var(--fs-caption);color:var(--text-secondary)}
+.cal-f .btn{margin-left:auto}
+.cal-f .time{width:72px;height:var(--control-h-sm);font-family:var(--font-figures);font-size:15px;text-align:center}
+.dt-pair{display:grid;grid-template-columns:minmax(0,1fr) 96px;gap:var(--sp-2)}
+.field.err .dt-pair > .input:not([aria-invalid]){border-color:var(--border-control);box-shadow:none}
+
+/* ---- B7 复选框、单选、开关(§8.2 批量勾选) ---- */
+.ck{appearance:none;flex:none;display:inline-grid;place-content:center;width:16px;height:16px;margin:0;border:1px solid var(--border-control);border-radius:var(--radius-sm);background:var(--bg-surface);cursor:pointer;vertical-align:-3px}
+.ck:hover{border-color:var(--c-ink)}
+.ck::after{content:"";display:none}
+.ck:checked,.ck[type=checkbox]:indeterminate{background:var(--c-ink);border-color:var(--c-ink)}
+.ck:checked::after{display:block;width:8px;height:4px;border:solid var(--c-white);border-width:0 0 1.75px 1.75px;transform:translateY(-1px) rotate(-45deg)}
+.ck[type=checkbox]:indeterminate::after{display:block;width:8px;height:0;border-top:1.75px solid var(--c-white);transform:none}
+.ck[type=radio]{border-radius:50%}
+.ck[type=radio]:checked{background:var(--bg-surface);border-color:var(--c-ink)}
+.ck[type=radio]:checked::after{width:8px;height:8px;border:0;border-radius:50%;background:var(--c-ink);transform:none}
+.ck:disabled{background:var(--bg-sunken);border-color:var(--c-rule);cursor:not-allowed}
+.ck:disabled:checked{background:var(--c-muted);border-color:var(--c-muted)}
+.ck[type=radio]:disabled:checked{background:var(--bg-sunken)}
+.ck[type=radio]:disabled:checked::after{background:var(--c-muted)}
+.ck.is-focus{outline:2px solid var(--focus-ring);outline-offset:2px}
+.choice{display:inline-flex;gap:var(--sp-2);align-items:flex-start;cursor:pointer;line-height:var(--lh-body)}
+.choice .ck{margin-top:3px}
+.choice small{display:block;color:var(--text-secondary);font-size:var(--fs-caption);line-height:var(--lh-caption)}
+.choice.dis{color:var(--text-disabled);cursor:not-allowed}
+.choice.dis small{color:var(--text-disabled)}
+.choices{display:flex;flex-direction:column;gap:var(--sp-2)}
+.choices.h{flex-direction:row;flex-wrap:wrap;gap:var(--sp-2) var(--sp-6)}
+.choice-card{display:flex;gap:var(--sp-3);align-items:flex-start;padding:var(--sp-3) var(--sp-4);border:1px solid var(--border-control);border-radius:var(--radius-sm);background:var(--bg-surface);cursor:pointer}
+.choice-card:has(:checked){border-color:var(--c-ink);box-shadow:inset 0 0 0 1px var(--c-ink)}
+.choice-card .ck{margin-top:3px}
+.choice-card b{display:block}
+.choice-card small{display:block;color:var(--text-secondary);font-size:var(--fs-caption);line-height:var(--lh-caption)}
+/* 开关:沿用 2px 圆角,不做胶囊形(§5.1) */
+.switch{display:inline-flex;align-items:center;gap:var(--sp-2);padding:0;border:0;background:none;font:var(--fs-body) var(--font-sans);color:var(--text-primary);cursor:pointer}
+.switch .track{position:relative;width:32px;height:18px;border:1px solid var(--border-control);border-radius:var(--radius-sm);background:var(--bg-surface);transition:background var(--dur-fast) var(--ease)}
+.switch .track::after{content:"";position:absolute;top:2px;left:2px;width:12px;height:12px;border-radius:1px;background:var(--c-muted);transition:transform var(--dur-fast) var(--ease),background var(--dur-fast) var(--ease)}
+.switch[aria-checked="true"] .track{background:var(--c-ink);border-color:var(--c-ink)}
+.switch[aria-checked="true"] .track::after{transform:translateX(14px);background:var(--c-white)}
+.switch .st{font-size:var(--fs-caption);color:var(--text-secondary);min-width:2em}
+.switch[disabled]{color:var(--text-disabled);cursor:not-allowed}
+.switch[disabled] .track{background:var(--bg-sunken);border-color:var(--c-rule)}
+.switch[disabled] .track::after{background:var(--c-rule)}
+.switch[disabled][aria-checked="true"] .track{background:var(--c-muted);border-color:var(--c-muted)}
+
+/* ---- B8 分段控件、标签页(§5.3 页签、§7.5 分类) ---- */
+.seg{display:inline-flex;border:1px solid var(--border-control);border-radius:var(--radius-sm);overflow:hidden;background:var(--bg-surface)}
+.seg button{height:28px;padding:0 10px;border:0;background:none;font:var(--fs-body) var(--font-sans);color:var(--text-primary);cursor:pointer;display:flex;gap:6px;align-items:center;white-space:nowrap}
+.seg button:hover{background:var(--bg-hover)}
+.seg button + button{border-left:1px solid var(--border-subtle)}
+.seg button[aria-pressed="true"]{background:var(--c-ink);color:var(--text-on-dark)}
+.seg button[aria-pressed="true"] .fig{color:var(--text-on-dark)}
+.seg button[aria-pressed="true"] .sev.hint,.seg button[aria-pressed="true"] .sev.major{background:var(--c-white)}
+.seg .fig{font-weight:var(--fw-medium);color:var(--text-secondary)}
+.seg .sev .fig{color:inherit}
+.tabs{display:flex;gap:var(--sp-6);border-bottom:1px solid var(--border-subtle)}
+.tabs.flush{border-bottom:0}
+.tabs [role=tab]{display:flex;gap:6px;align-items:center;margin-bottom:-1px;padding:var(--sp-2) 0 10px;border:0;border-bottom:2px solid transparent;background:none;font:var(--fs-body) var(--font-sans);color:var(--text-secondary);cursor:pointer;text-decoration:none;white-space:nowrap}
+.tabs [role=tab]:hover{color:var(--text-primary)}
+.tabs [role=tab][aria-selected="true"]{color:var(--text-primary);font-weight:var(--fw-bold);border-bottom-color:var(--c-ink)}
+.tabs [role=tab][aria-disabled="true"]{color:var(--text-disabled);cursor:not-allowed}
+.tabs [role=tab] > .fig{font-weight:var(--fw-medium)}
+.tabpanel{padding-top:var(--sp-4)}
+
+/* ---- B9 分页 ---- */
+.pagination{display:flex;align-items:center;gap:var(--sp-1);margin-top:var(--sp-3);font-size:var(--fs-caption);color:var(--text-secondary)}
+.pagination .total{margin-right:auto}
+.pagination .fig{font-weight:var(--fw-medium)}
+.pagination button.pg{min-width:28px;height:28px;padding:0 6px;border:1px solid transparent;border-radius:var(--radius-sm);background:none;font:var(--fw-medium) 15px/1 var(--font-figures);font-variant-numeric:tabular-nums;color:var(--text-primary);cursor:pointer}
+.pagination button.pg:hover{background:var(--bg-hover)}
+.pagination button.pg[aria-current="page"]{border-color:var(--c-ink);font-weight:var(--fw-bold);background:var(--bg-surface)}
+.pagination button.pg[disabled]{color:var(--text-disabled);cursor:not-allowed;background:none}
+.pagination .gap{width:20px;text-align:center}
+.pagination .jump{display:flex;align-items:center;gap:6px;margin-left:var(--sp-4)}
+.pagination .jump .input{width:52px;height:28px;text-align:center;font-family:var(--font-figures);font-size:15px}
+
+/* ---- B10 轻提示 Toast(§7.12:只用于操作结果,4 秒消失,悬停暂停) ---- */
+.toasts{position:fixed;left:50%;bottom:var(--sp-8);transform:translateX(-50%);z-index:60;display:flex;flex-direction:column;gap:var(--sp-2);align-items:center;pointer-events:none}
+.toast{display:flex;gap:var(--sp-3);align-items:center;min-height:40px;padding:var(--sp-2) var(--sp-4);background:var(--c-ink);color:var(--text-on-dark);border-radius:var(--radius-md);box-shadow:var(--shadow-overlay);pointer-events:auto;white-space:nowrap}
+.toast .ok{display:inline-grid;place-items:center;width:16px;height:16px;border-radius:50%;background:var(--c-green);color:var(--c-white);font-size:11px;line-height:1}
+.toast a,.toast button.lnk{color:var(--text-on-dark);text-decoration:underline;text-underline-offset:3px;background:none;border:0;font:inherit;cursor:pointer;padding:0}
+.toast .sep{width:1px;align-self:stretch;margin:2px 0;background:color-mix(in srgb,var(--c-white) 24%,transparent)}
+.toast.static{position:static}
+
+/* ---- B11 对话框(§7.14) ---- */
+.scrim{position:fixed;inset:0;z-index:50;display:grid;place-items:center;background:color-mix(in srgb,var(--c-ink) 32%,transparent)}
+.dialog{position:relative;width:480px;max-width:calc(100vw - 32px);max-height:calc(100vh - 64px);overflow:auto;background:var(--bg-surface);border-radius:var(--radius-md);box-shadow:var(--shadow-overlay);padding:var(--sp-6)}
+.dialog.wide{width:640px}
+.dialog.static{position:static;max-width:none;box-shadow:var(--shadow-overlay)}
+.dialog h2{font:var(--fw-bold) var(--fs-title)/var(--lh-title) var(--font-sans);margin:0 var(--sp-8) var(--sp-3) 0}
+.dialog .x{position:absolute;top:var(--sp-4);right:var(--sp-4)}
+.dialog .body > p{margin:0 0 var(--sp-3)}
+.dialog .body > .field{margin-top:var(--sp-4)}
+.dialog .foot{display:flex;justify-content:flex-end;align-items:center;gap:var(--sp-2);margin-top:var(--sp-6)}
+.dialog .foot .left{margin-right:auto}
+
+/* ---- B12 提醒补充(§7.12、§7.15) ---- */
+.alert{align-items:flex-start}
+.alert > div{flex:1;min-width:0}
+.alert .act{margin-left:auto;flex:none;display:flex;gap:var(--sp-2);align-items:center}
+.alert .sub{display:block;margin-top:2px;font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--text-secondary)}
+.alert .fig{font-weight:var(--fw-medium)}
+.errno{font:var(--fw-medium) var(--fs-caption)/1 var(--font-figures);color:var(--text-secondary);letter-spacing:.02em}
+
+/* ---- B13 提示气泡(悬停 / 聚焦显示,§6 只有图标的按钮要有悬停提示) ---- */
+[data-tip]{position:relative}
+[data-tip]:hover::after,[data-tip]:focus-visible::after,[data-tip].tip-on::after{content:attr(data-tip);position:absolute;z-index:55;bottom:calc(100% + 6px);left:50%;transform:translateX(-50%);max-width:280px;width:max-content;padding:5px var(--sp-2);border-radius:var(--radius-md);background:var(--c-ink);color:var(--text-on-dark);font:var(--fw-regular) var(--fs-caption)/var(--lh-caption) var(--font-sans);white-space:normal;text-align:left;box-shadow:var(--shadow-overlay);pointer-events:none}
+[data-tip].tip-below:hover::after,[data-tip].tip-below.tip-on::after{bottom:auto;top:calc(100% + 6px)}
+[data-tip].tip-start:hover::after,[data-tip].tip-start.tip-on::after{left:0;transform:none}
+[data-tip].tip-left:hover::after,[data-tip].tip-left.tip-on::after{left:auto;right:0;transform:none}
+
+/* ---- B14 空状态、骨架屏、进度(§7.11、§7.15) ---- */
+.empty{display:flex;flex-direction:column;align-items:center;text-align:center;padding:var(--sp-12) var(--sp-6)}
+.empty > .ic{color:var(--text-secondary);margin-bottom:var(--sp-3)}
+.empty b{font-size:var(--fs-title);line-height:var(--lh-title)}
+.empty p{margin:var(--sp-1) 0 var(--sp-5);color:var(--text-secondary);max-width:30em}
+.empty.sm{padding:var(--sp-8) var(--sp-4)}
+.empty.sm p{margin-bottom:var(--sp-3)}
+.skel{display:block;height:12px;border-radius:var(--radius-sm);background:var(--bg-sunken);animation:skel 1.6s var(--ease) infinite alternate}
+.skel.t{height:16px;width:40%}
+.skel.w60{width:60%} .skel.w80{width:80%} .skel.w30{width:30%} .skel.w20{width:20%}
+.skel + .skel{margin-top:var(--sp-2)}
+.skel-row{display:grid;grid-template-columns:24px minmax(0,1fr) 120px;gap:var(--sp-3);padding:var(--sp-3) 0;border-top:1px solid var(--border-subtle)}
+.skel-row:first-child{border-top:0}
+@keyframes skel{from{opacity:1}to{opacity:.45}}
+@media (prefers-reduced-motion:reduce){.skel{animation:none}}
+.progress{display:flex;align-items:center;gap:var(--sp-3)}
+.progress .bar{flex:1;margin:0}
+.progress .fig{font-weight:var(--fw-medium);min-width:3em;text-align:right}
+.steps .fig{font-weight:var(--fw-medium);color:var(--text-secondary);margin-left:auto}
+.steps li > span:nth-child(2){flex:1}
+.steps .fail .dot{border-color:var(--warn);color:var(--warn-text);font-weight:var(--fw-bold)}
+.steps .fail{color:var(--warn-text)}
+
+/* ---- B15 杂项小件 ---- */
+.kbd{display:inline-block;min-width:20px;padding:0 5px;border:1px solid var(--border-control);border-bottom-width:2px;border-radius:3px;font:var(--fw-medium) var(--fs-caption)/18px var(--font-figures);text-align:center;background:var(--bg-surface);color:var(--text-primary)}
+.muted{color:var(--text-secondary)}
+.nw{white-space:nowrap}
+.cap{font-size:var(--fs-caption);line-height:var(--lh-caption)}
+.fig.m{font-weight:var(--fw-medium)}
+.kv{display:grid;grid-template-columns:96px minmax(0,1fr);gap:var(--sp-2) var(--sp-4);margin:0}
+.kv dt{color:var(--text-secondary)}
+.kv dd{margin:0}
+.chip{display:inline-flex;align-items:center;gap:var(--sp-1);height:24px;padding:0 4px 0 8px;border:1px solid var(--border-control);border-radius:var(--radius-sm);background:var(--bg-surface);font-size:var(--fs-caption);white-space:nowrap}
+.chip button{display:inline-grid;place-items:center;width:18px;height:18px;padding:0;border:0;border-radius:var(--radius-sm);background:none;color:var(--text-secondary);cursor:pointer}
+.chip button:hover{background:var(--bg-hover);color:var(--text-primary)}
+.drop{display:flex;flex-direction:column;align-items:center;gap:var(--sp-1);border:1.5px dashed var(--border-control);border-radius:var(--radius-sm);background:var(--bg-surface);padding:var(--sp-8) var(--sp-6);text-align:center;cursor:pointer}
+.drop:hover,.drop.is-over{border-color:var(--c-ink);background:var(--bg-hover)}
+.drop > .ic{color:var(--text-secondary)}
+.drop b{font-size:var(--fs-title);line-height:var(--lh-title)}
+.drop span{color:var(--text-secondary);font-size:var(--fs-caption);line-height:var(--lh-caption)}
+.file-row{display:grid;grid-template-columns:40px minmax(0,1fr) auto;gap:var(--sp-3);align-items:center;padding:var(--sp-3) var(--sp-4);background:var(--bg-surface);border:1px solid var(--border-subtle);border-radius:var(--radius-sm)}
+.file-row + .file-row{margin-top:var(--sp-2)}
+.file-row .ext{font:var(--fw-bold) 11px/1 var(--font-figures);border:1px solid var(--border-control);border-radius:var(--radius-sm);padding:4px 0;text-align:center}
+.file-row b{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
+.file-row .acts{display:flex;gap:var(--sp-1);align-items:center}
+.flowline{display:flex;align-items:center}
+.flowline .st{display:flex;align-items:center;gap:var(--sp-2);color:var(--text-secondary);white-space:nowrap}
+.flowline .st i{width:10px;height:10px;border-radius:50%;border:1.5px solid var(--border-control);flex:none}
+.flowline .st.done{color:var(--text-primary)}
+.flowline .st.done i{background:var(--c-ink);border-color:var(--c-ink)}
+.flowline .st.now{color:var(--text-primary);font-weight:var(--fw-bold)}
+.flowline .st.now i{border:3px solid var(--c-ink)}
+.flowline .ln{flex:1;min-width:16px;height:1px;background:var(--border-control);margin:0 var(--sp-2)}
+.flowline.sm{font-size:var(--fs-caption)}
+.flowline.sm .st i{width:8px;height:8px}
+.flowline.sm .st.now i{border-width:2.5px}
+
+/* ---- B16 AI 产出块(§3.3 左侧虚线竖线、§7.16) ---- */
+.ai-block{border-left:1.5px dashed var(--ai);padding:var(--sp-1) 0 var(--sp-1) var(--sp-3)}
+.ai-block.done{border-left:2px solid var(--c-ink)}
+.ai-label{display:flex;gap:var(--sp-2);align-items:center;margin-bottom:var(--sp-2);font-size:var(--fs-caption);color:var(--text-secondary)}
+.flag.box{border:1px solid var(--border-subtle);border-radius:var(--radius-sm);padding:0 5px;line-height:18px}
+.cite-src.inline{display:inline;margin-left:6px;white-space:nowrap}
+.sign.ign{color:var(--text-secondary)}
+.sign.sm{font-size:var(--fs-caption);margin-top:var(--sp-2)}
+.more-cites{display:inline-block;margin-top:var(--sp-2);font-size:var(--fs-caption);color:var(--ai);background:none;border:0;padding:0;cursor:pointer;font-family:var(--font-sans)}
+.more-cites:hover{text-decoration:underline;text-underline-offset:3px}
+.cite q.clamp{-webkit-line-clamp:3}
+.cite .expand{font-size:var(--fs-caption);color:var(--ai);background:none;border:0;padding:0;cursor:pointer;font-family:var(--font-sans)}
+
+/* ---- B17 卡片 ---- */
+.card-h{display:flex;align-items:center;gap:var(--sp-2);margin-bottom:var(--sp-3)}
+.card-h h3{margin:0;font:var(--fw-bold) var(--fs-title)/var(--lh-title) var(--font-sans)}
+.card-h .more{margin-left:auto;display:flex;gap:var(--sp-2);align-items:center}
+.card.tight{padding:var(--sp-4) var(--sp-5)}
+.card-foot{display:flex;gap:var(--sp-2);align-items:center;margin:var(--sp-4) calc(-1 * var(--sp-6)) calc(-1 * var(--sp-5));padding:var(--sp-3) var(--sp-6);border-top:1px solid var(--border-subtle)}
+.card.tight .card-foot{margin:var(--sp-4) calc(-1 * var(--sp-5)) calc(-1 * var(--sp-4));padding:var(--sp-3) var(--sp-5)}
+.card-foot .r{margin-left:auto;display:flex;gap:var(--sp-2);align-items:center}
+/* 项目卡 */
+.pcard{display:flex;flex-direction:column;gap:var(--sp-3);position:relative}
+.pcard.is-fatal::before{content:"";position:absolute;left:-1px;top:var(--sp-5);bottom:var(--sp-5);width:3px;background:var(--sev-fatal)}
+.pcard .pc-top{display:flex;gap:var(--sp-3);align-items:flex-start}
+.pcard .pc-top > div{flex:1;min-width:0}
+.pcard .card-foot{margin-top:auto}
+.pcard .facts{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--sp-3);padding:var(--sp-3) 0;border-top:1px solid var(--border-subtle);border-bottom:1px solid var(--border-subtle)}
+.pcard .facts small{display:block;font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--text-secondary)}
+.pcard .facts > div{min-width:0}
+/* 统计格 */
+.stats{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:var(--sp-4)}
+.stat{background:var(--bg-surface);border:1px solid var(--border-subtle);border-radius:var(--radius-sm);padding:var(--sp-4) var(--sp-5)}
+.stat small{display:block;color:var(--text-secondary);font-size:var(--fs-caption);line-height:var(--lh-caption)}
+.stat .big{font:var(--fw-bold) var(--fs-figure)/var(--lh-figure) var(--font-figures);font-variant-numeric:tabular-nums}
+.stat .unit{color:var(--text-secondary);margin-left:4px}
+.stat p{margin:var(--sp-1) 0 0;font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--text-secondary)}
+.stat p.due-soon,.stat p.due-today{color:var(--warn-text)}
+/* 额度卡 */
+.quota small{display:block;color:var(--text-secondary);font-size:var(--fs-caption);line-height:var(--lh-caption)}
+.quota .q2{display:grid;grid-template-columns:1fr 1fr;gap:var(--sp-5)}
+.quota p{margin:0;font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--text-secondary)}
+.quota p.warn{color:var(--warn-text)}
+/* 关键日期 */
+.dates{list-style:none;margin:0;padding:0}
+.dates li{display:grid;grid-template-columns:88px minmax(0,1fr) auto;gap:var(--sp-3);align-items:baseline;padding:var(--sp-2) 0;border-top:1px solid var(--border-subtle)}
+.dates li:first-child{border-top:0}
+.dates li > .fig{font-weight:var(--fw-medium);font-size:15px}
+.dates li.past{color:var(--text-secondary)}
+.dates li.key > span:nth-child(2) > b{font-weight:var(--fw-bold)}
+.dates .left{display:block;font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--text-secondary)}
+.dates .left.due-soon,.dates .left.due-today{color:var(--warn-text)}
+.dates .cite-src{white-space:nowrap}
+
+/* ---- B18 表格补充:排序、勾选、选中行、批量栏、空结果(§7.7、§8.2) ---- */
+.toolbar{display:flex;align-items:center;gap:var(--sp-3);margin-bottom:var(--sp-3);flex-wrap:wrap}
+.toolbar .right{margin-left:auto;display:flex;gap:var(--sp-2);align-items:center}
+.toolbar .search{width:260px}
+.toolbar .pop .select{width:auto;min-width:132px}
+.filters-on{display:flex;gap:var(--sp-2);align-items:center;flex-wrap:wrap;margin:calc(-1 * var(--sp-1)) 0 var(--sp-3);font-size:var(--fs-caption);color:var(--text-secondary)}
+table.data th.sortable{padding:0}
+.th-sort{display:flex;align-items:center;gap:4px;width:100%;padding:var(--sp-2) var(--sp-3);border:0;background:none;font:inherit;color:inherit;cursor:pointer;text-align:left;white-space:nowrap}
+.th-sort:hover{color:var(--text-primary)}
+.th-sort .ic{width:14px;height:14px;color:var(--text-disabled)}
+th[aria-sort] .th-sort{color:var(--text-primary)}
+th[aria-sort] .th-sort .ic{color:var(--text-primary)}
+table.data th.r .th-sort{justify-content:flex-end}
+table.data .ckc{width:40px;padding-right:0}
+table.data.hover tbody tr{cursor:pointer}
+table.data.hover tbody tr:hover td{background:var(--bg-hover)}
+table.data tbody tr.sel td{background:var(--bg-sunken)}
+table.data tbody tr.sel td:first-child{box-shadow:inset 2px 0 0 var(--c-ink)}
+table.data td.empty-cell{height:auto;padding:0}
+table.data td .cap{color:var(--text-secondary)}
+table.data .acts{white-space:nowrap;text-align:right}
+table.data td.mid{vertical-align:middle}
+table.data tr.is-fatal td:first-child{box-shadow:inset 3px 0 0 var(--sev-fatal)}
+.batchbar{position:sticky;top:calc(52px + var(--sp-2));z-index:10;display:flex;align-items:center;gap:var(--sp-3);height:44px;padding:0 var(--sp-4);margin-bottom:var(--sp-2);background:var(--bg-surface);border:1px solid var(--c-ink);border-radius:var(--radius-sm)}
+.batchbar b .fig{font-weight:var(--fw-bold)}
+.batchbar .r{margin-left:auto;display:flex;gap:var(--sp-2);align-items:center}
+.list-box{background:var(--bg-surface);border:1px solid var(--border-subtle);border-radius:var(--radius-sm)}
+.list-box > .lb-h{display:flex;align-items:center;gap:var(--sp-3);padding:var(--sp-1) var(--sp-4);border-bottom:1px solid var(--border-subtle);background:var(--bg-sunken);font-size:var(--fs-caption);color:var(--text-secondary)}
+.list-box > .lb-h .r{margin-left:auto;display:flex;gap:var(--sp-2);align-items:center}
+.list-box .item{padding:var(--sp-3) var(--sp-4);border-top:1px solid var(--border-subtle)}
+.list-box .item:first-of-type{border-top:0}
+.list-box .item.has-ck{grid-template-columns:20px 24px minmax(0,1fr) auto}
+.list-box .item .ck{margin-top:3px}
+.item.sel{background:var(--ai-soft);box-shadow:inset 3px 0 0 var(--ai)}
+.item.focus{box-shadow:inset 0 0 0 2px var(--c-ink)}
+.item[tabindex]{cursor:pointer;outline:none}
+.item[tabindex]:hover{background:var(--bg-hover)}
+.item.sel:hover{background:var(--ai-soft)}
+.item .upd{margin-top:6px;display:flex;gap:var(--sp-2);align-items:center;font-size:var(--fs-caption)}
+.item .upd .btn{height:24px;font-size:var(--fs-caption)}
+
+/* 问题行的紧凑版本(列表页) */
+.finding .ckw{position:absolute;left:var(--sp-2);top:calc(var(--sp-5) + 2px)}
+.finding.has-ck{padding-left:var(--sp-8)}
+.finding.focus{box-shadow:inset 0 0 0 2px var(--c-ink)}
+.u4{margin-top:var(--sp-3);padding:var(--sp-3) var(--sp-4);background:var(--bg-sunken);border-radius:var(--radius-sm)}
+.u4 .field{max-width:none}
+.u4 .row{margin-top:var(--sp-2)}
+.group-h{display:flex;align-items:center;gap:var(--sp-2);width:100%;padding:var(--sp-3) var(--sp-6);border:0;border-top:1px solid var(--border-subtle);background:var(--bg-sunken);color:var(--text-secondary);font:var(--fs-body) var(--font-sans);cursor:pointer;text-align:left}
+.group-h:hover{color:var(--text-primary)}
+
+.verdict.hint{color:var(--sev-hint)}
+
+/* ---- B19 对照布局:分类导航 + 列表 + 原文查看器(§5.3、§7.8) ---- */
+.split{display:grid;grid-template-columns:200px minmax(0,1fr) minmax(420px,45%);flex:1;min-height:0}
+.cats{border-right:1px solid var(--border-subtle);padding:var(--sp-4) var(--sp-3);overflow:auto}
+.cats button{display:flex;width:100%;align-items:center;justify-content:space-between;height:34px;padding:0 var(--sp-3);border:0;background:none;border-radius:var(--radius-sm);font:var(--fs-body) var(--font-sans);color:var(--text-primary);cursor:pointer;text-align:left}
+.cats button:hover{background:var(--bg-hover)}
+.cats button[aria-current="true"]{background:var(--bg-surface);box-shadow:inset 2px 0 0 var(--c-ink);font-weight:var(--fw-bold)}
+.cats button .fig{font-weight:var(--fw-medium);color:var(--text-secondary)}
+.cats button.ai,.cats button.ai .fig{color:var(--ai)}
+.cats hr{border:0;border-top:1px solid var(--border-subtle);margin:var(--sp-3) 0}
+.cats .hint{font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--text-secondary);padding:0 var(--sp-3);margin:0}
+.viewer{min-height:0}
+.viewer-bar{flex-wrap:wrap;min-height:44px}
+.viewer-bar .doc-name{display:flex;gap:var(--sp-2);align-items:baseline;min-width:0}
+.viewer-bar .doc-file{color:var(--text-secondary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:220px}
+.viewer-body{flex:1;overflow:auto;padding:var(--sp-4) 0}
+.viewer .sheet{min-height:560px;margin:0 auto;width:calc(100% - 48px);max-width:640px;position:relative;background:var(--c-white);color:var(--text-primary)}
+.sheet .pno{position:absolute;bottom:14px;left:0;right:0;text-align:center;font:var(--fs-caption) var(--font-figures);color:var(--text-secondary)}
+.sheet .h{font-weight:var(--fw-bold);margin:4px 0 10px}
+.sheet .faint{color:var(--text-secondary)}
+.sheet .stub{height:9px;background:var(--bg-sunken);margin:12px 0;border-radius:1px}
+.sheet .stub.s{width:60%}
+.viewer-note{font-size:var(--fs-caption);color:var(--text-secondary);padding:var(--sp-2) var(--sp-6) 0;margin:0;text-align:center}
diff --git a/docs/design/ui-demo/demo.js b/docs/design/ui-demo/demo.js
new file mode 100644
index 0000000..028aa55
--- /dev/null
+++ b/docs/design/ui-demo/demo.js
@@ -0,0 +1,275 @@
+/* 标博士 · UI demo 公共脚本:图标、页面框架、轻提示、对话框、下拉、日期、开关、标签页、排序。
+ * 只演示交互,不连后端。正式实现里这些交互件由 Reka UI 打底(AR10)。 */
+(function () {
+ const $ = (s, el = document) => el.querySelector(s);
+ const $$ = (s, el = document) => [...el.querySelectorAll(s)];
+ const esc = (s) => String(s).replace(/[&<>"]/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """ }[c]));
+
+ /* Lucide 图标(ISC 许可)路径 */
+ const ICONS = {
+ grid: '
+
+
+
+
+ 01
新建项目
规范 §7.13 · §8.3 · FR-M2-01
+
+
+
+
+
+
试一试
+
+ - 什么都不填,直接点「创建并开始解析」:顶部汇总 + 每个字段下方说明,点汇总里的链接跳到字段。
+ - 在项目名称里输入再清空,离开输入框时就会校验。
+ - 点上传区,会加入两个示意文件,其中一个扩展名和内容不符。
+ - 全部填好后提交:按钮变成「正在创建…」,宽度不变,表单整体禁用。
+
+
+
+
规则
+
+ - 必填写「(必填)」,不用红色星号;选填项多的表单反过来写「(选填)」。
+ - 错误说明写清楚怎么改,不写「格式错误」。
+ - 会扣点的提交按钮,左侧直接写扣多少、还剩多少。
+ - 文件扩展名和内容不符只提醒,不拦截(样本分析 07 的结论)。
+
+
+
+
+
+
+
+
+ 02
同一字段的状态对照
规范 §7.13
+
+
+
+
+
提交中
整表禁用,按钮换成「正在…」,宽度不变
+
+
+
+
+
+
+
+
+ 03
资质录入
FR-M6 最小资质库 · 红线 5 不编造
+
+
+
添加资质
+
+
查标时用来核对证书有没有过期、等级够不够。数据只来自你录入的内容,系统不从外部补数据。
+
+
+
+
+
+
+
打开真实对话框 可以点
+
+
空表单直接保存会看到错误状态;「附件」可以稍后补录,列表里显示「待补录」占位,不生成内容。
+
+
+
提醒和错误的区别
+
+ - 错误(赭色边框):必须改了才能保存,例如漏填证书编号。
+ - 提醒(只有赭色小字,边框不变):可以保存,但查标会提示,例如证书在投标截止日前到期。
+ - 到期判断以每个项目的投标截止日为准,不以今天为准。
+
+
+
+
+
+
+
+
+ 04
忽略并注明原因 · 致命问题的确认说明
规范 §7.4 · §7.13 · U4
+
+
+
+
重要开标一览表与分项价格表合计不一致
+
开标一览表 287.60 万元,分项价格表合计 286.80 万元,相差 0.80 万元。按招标文件规定,两者不一致时以开标一览表为准,不作无效处理;但会影响价格评审。
+
+
+
+
+
+
!至少写 5 个字,说明为什么不处理3 / 200
+
+
「已忽略」不等于确认无误,报告里单独列出
+
+
+
+
+
+
+
致命授权委托书的单位签章位置未发现盖章新增
+
商务册第 5 页《法定代表人授权委托书》的「投标人(电子签章)」位置是空的。本检查只看有无,不鉴定真伪。
+
+
+
+
+
对致命问题说「没问题」,需要写一句说明;说明会同时作为误报反馈提交。30 / 200
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/design/ui-demo/index.html b/docs/design/ui-demo/index.html
new file mode 100644
index 0000000..f42a4c1
--- /dev/null
+++ b/docs/design/ui-demo/index.html
@@ -0,0 +1,95 @@
+
+
+
+
+
+
标博士 · UI demo(2026-11-03)
+
用已确定的界面风格(方向 A「校样」、设计规范 v0.1)把常见控件和它们组合成的页面摆出来,用来决定 UI 精修的方向。颜色、字号、间距只用 system/tokens.css 的变量;已有组件直接用 system/components.css,规范里有描述但还没实现的组件放在本目录的 demo.css。不用任何 UI 框架和 CDN(AR10),图标是 Lucide 风格的内联 SVG(U3)。
+
cd docs && python3 -m http.server 8767
+# 打开 http://127.0.0.1:8767/design/ui-demo/ (设计基准 1440 宽,最小 1280)
+
+
+
+ 01
页面
示意数据:政府采购 · IT 集成,复用原型 prototype/assets/data.js
+
+ 资质库、额度页属于和工作台相同的「列表」布局,控件已在 ②③④ 覆盖,没有单独做组合页。
+
+
+
+ 02
新增组件清单
都在 demo.css 的 B 部分;精修定稿后并入 system/components.css
+
+ | 编号 | 组件 | 类名 | 依据 | 说明 |
+
+ | B1 | 图标 | .ic | §6 · U3 | Lucide 路径内联,1.5px 描边,16 / 20 / 32 |
+ | B2 | 页面框架 | .sidebar .top .crumbs .quota-chip .content .page-title .proj-head | §5.3 | 从原型 app.css 搬过来,去掉裸色值 |
+ | B3 | 按钮补充状态 | .btn.swap .is-loading .icon-btn .btn-group | §7.1 | 按下态、加载中定宽换字、只有图标的按钮、按钮组 |
+ | B4 | 输入框补充 | .input:focus/[disabled]/[readonly] .input-unit .search .form .form-sec .err-sum | §7.13 | 聚焦 / 禁用 / 只读、带单位、搜索、两列表单、错误汇总、字数 |
+ | B5 | 下拉选择 · 下拉菜单 | .select .menu .pop | AR10 | 选中项加粗 + ✓,不可选项写原因;正式实现用 Reka UI Select / DropdownMenu |
+ | B6 | 日期选择 | .cal .dt-pair .date-trigger | §9.3 · AR10 | 周一开始、Barlow 数字、今天描边、选中墨底、关键日期圆点、不可选删除线 |
+ | B7 | 复选 · 单选 · 单选卡片 · 开关 | .ck .choice .choice-card .switch | §8.2 | 墨色勾选、半选;开关是方形滑块 + 开 / 关文字 |
+ | B8 | 分段控件 · 标签页 | .seg .tabs [role=tab] | §5.3 · §7.5 | ← → 切换、不可用页签 |
+ | B9 | 分页 | .pagination | — | 规范未定义;当前页墨色描边 |
+ | B10 | 轻提示 | .toasts .toast | §7.12 | 4 秒消失、悬停暂停、可撤销、堆叠 |
+ | B11 | 对话框 | .scrim .dialog .wide | §7.14 | 480 / 640,焦点锁定,Esc 关闭,焦点回到触发处 |
+ | B12 | 提醒补充 | .alert .act .sub .errno | §7.12 · §7.15 | 右侧操作、副说明、错误编号 |
+ | B13 | 提示气泡 | [data-tip] | §6 | 墨底白字;正式实现用 Reka UI Tooltip |
+ | B14 | 空状态 · 骨架屏 · 进度 | .empty .skel .skel-row .progress .steps .fail | §7.11 · §7.15 | 骨架缓慢明暗变化,减少动态效果时静止 |
+ | B15 | 杂项 | .kbd .kv .chip .drop .file-row .flowline | §8.2 | 快捷键、键值表、可移除的筛选标签、上传区、文件行、状态流 |
+ | B16 | AI 产出块 | .ai-block .ai-label .sign.ign .more-cites | §3.3 · §7.16 | 左侧虚线 → 确认后墨色实线;「已忽略」灰色署名 |
+ | B17 | 卡片 | .card-h .card-foot .pcard .stats .stat .quota .dates | §7.9 · §7.10 | 项目卡、统计格、额度卡、关键日期 |
+ | B18 | 表格补充 | .toolbar .th-sort .batchbar .list-box .filters-on | §7.7 · §8.2 | 排序、勾选、选中行、批量栏、空结果行、问题行勾选 |
+ | B19 | 对照布局 | .split .cats .viewer-body | §5.3 · §7.8 | 分类导航 + 列表 + 原文查看器 |
+
+
+
+
+
+ 03
做的过程中发现的规范缺口 · 待拍板
UI 精修阶段逐条确认
+
+ | # | 问题 | demo 现在的做法 | 需要决定 |
+
+ | Q1 | 额度流水的「+」用了定稿绿 | 沿用 components.css 的 .flow td.plus(绿) | §3.4 说绿色只表示「有人确认过」。建议改成墨色「+」,靠符号区分 |
+ | Q2 | AI 产出的正文是什么颜色 | 正文始终墨色,只用「AI」标签 + 左侧虚线区分 | §7.16 写「确认后文字变为墨色」,暗示确认前不是墨色;大段蓝字阅读负担重,建议明确「正文始终墨色」 |
+ | Q3 | 「选中」用什么颜色 | 读标清单选中 = 蓝铅笔浅底(和原文高亮对应);表格勾选 = 凹陷灰 + 墨色竖线 | 蓝色只属于系统和出处(§3.4)。读标选中是否保留蓝色,需统一 |
+ | Q4 | 开关、分页、下拉、日期选择的视觉 | 开关做成方形滑块 + 开 / 关文字;分页当前页描边;下拉选中加粗 + ✓;不可选日期加删除线 | 规范第 7 节没有这几个组件,确认后补进规范 |
+ | Q5 | 参数对照表加「级别」列 | 多了一列级别标签,判断词颜色有形状兜底 | §7.7 的表没有这一列;只靠判断词颜色对色弱不够 |
+ | Q6 | 悬停、按下、遮罩缺颜色变量 | 用 color-mix() 从墨色算;components.css 的 #34404D、原文页的 #222 #888 是裸色值 | 建议 tokens.css 增加 --c-ink-hover、--scrim 等语义变量 |
+ | Q7 | 字阶外的字号 | Barlow 数字用了 13 / 15px(components.css 已有) | §4.2 字阶里没有,建议加 --fs-figure-sm 等变量 |
+ | Q8 | 骨架屏动不动 | 1.6 秒缓慢明暗变化;减少动态效果时静止 | 和「不闪烁」原则是否冲突 |
+ | Q9 | 顶栏额度 80% 后是否变色 | 顶栏额度描边变赭色 | §7.10 只写了额度条,没写顶栏 |
+ | Q10 | 日期的输入格式 | 界面显示「11月20日 09:30」,输入框用「2026-11-20」 | §9.3 只规定了显示格式 |
+ | Q11 | 项目卡片视图 | 做了项目卡,工作台仍用表格 | §7.9 只定义了项目行;要不要提供卡片视图 |
+ | Q12 | 卡片 / 页面里同时出现多个主按钮 | 组合页每屏一个主按钮;demo 陈列页为了对比会出现多个 | 表单页、对话框里的「保存」是否也算「一屏一个」 |
+
+
+
+
+
+
+
+
diff --git a/docs/design/ui-demo/lists.html b/docs/design/ui-demo/lists.html
new file mode 100644
index 0000000..6c33c55
--- /dev/null
+++ b/docs/design/ui-demo/lists.html
@@ -0,0 +1,295 @@
+
+
+
+
+
+
+
+
+ 01
项目列表
规范 §7.9 · 可以筛选、搜索、点表头排序
+
+
+ 默认按截止时间升序,截止临近的排在前面。「就绪」只在「已检查」显示,只表示致命问题都已处理,不代表投标文件没有问题。
+
+
+
+
+ 02
清单列表
规范 §7.5 · 点这块后按 J / K 移动、Enter 打开出处
+
+
+
+
+
+
+
+ 03
问题列表
规范 §7.4 · §8.2 · 只有「提示」级可以批量
+
+ 已选 0 项「提示」批量处理只开放给「提示」级
+
+
+
+
+
+ 04
额度流水
规范 §7.10 · 红线 9 额度即货币
+
+
+ | 时间 | 类型 | 额度 | 数量 | 余额 | 关联项目 / 任务 | 说明 |
+
+ | 11-02 14:31 | 退回 | 解析 | +1 | 37 | 智慧大厅建设项目 · 解析任务 PJ-0417 | 解析失败:文件已加密 |
+ | └11-02 14:28 | 预扣 | 解析 | −1 | 36 | 智慧大厅建设项目 · 解析任务 PJ-0417 | 和上一行的退回对应 |
+ | 11-02 14:20 | — | 检查 | 0 | 12 | 智慧大厅建设项目 · 第 2 次检查 | 24 小时内复查,不扣 |
+ | 11-02 10:05 | 结算 | 解析 | −1 | 37 | 智慧大厅建设项目 · 补遗 1 | |
+ | 11-01 16:00 | 结算 | 检查 | −1 | 12 | 智慧大厅建设项目 · 第 1 次检查 | |
+ | 11-01 10:40 | 结算 | 解析 | −1 | 38 | 智慧大厅建设项目 · 招标文件 | |
+ | 11-01 09:30 | 人工调整 | 检查 | +2 | 13 | — | 运营:系统异常补偿,工单 GD-1101 |
+ | 11-01 09:00 | 发放 | 解析 / 检查 | +50 / +20 | 50 / 20 | 专业版开通(对公转账) | 开通单 KT-1101 |
+
+
+
+ - 增加用定稿绿「+」(这里绿色表示「入账」,是否允许请在精修时拍板,见目录页的待定问题),扣减用墨色「−」;退回记录和对应的预扣记录相邻显示。
+
+
+
+
+ 05
参数对照表
规范 §7.7
+
+ - 判断只用四个词:满足 / 疑似不满足 / 未找到响应 / 需人工判断。「满足」用墨色普通字,不用绿色。
- 要求和响应列用 Barlow,数值左对齐。规范的表没有「级别」列,这里加了一列标签,让判断词的颜色有形状兜底(色弱)——是否保留请拍板。
+
+
+
+
+ 06
空结果
规范 §7.15
+
+
+
筛选后没有结果:写清楚条件,给「清除筛选」
+
| 项目 | 所投包 | 投标截止 | 状态 |
+ 没有符合条件的项目「已归档」里没有名称含「医保」的项目。 |
+
+
+
问题全部处理完:说清楚意味着什么,不庆祝
+
待处理的问题都已处理致命问题已全部处理,项目为「就绪」。这只表示每一项都有人处理过,不代表投标文件没有问题。
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/design/ui-demo/page-reading.html b/docs/design/ui-demo/page-reading.html
new file mode 100644
index 0000000..81465db
--- /dev/null
+++ b/docs/design/ui-demo/page-reading.html
@@ -0,0 +1,226 @@
+
+
+
+