Sfoglia il codice sorgente

feat(v12): 深滚动 hover 口径修正、页脚按钮改用共享 kf-btn、移动端排序行补 hover;交付验收脚本/前后对照/截图

FmodeCode 5 giorni fa
parent
commit
2a9fea27c5

+ 2159 - 0
docs/v12-after-final.json

@@ -0,0 +1,2159 @@
+{
+  "tag": "after",
+  "at": "2026-10-30T07:47:14.942Z",
+  "pages": [
+    {
+      "page": "/",
+      "radii": {
+        "0": 944,
+        "8": 132,
+        "12": 132,
+        "circle": 4
+      },
+      "controls": [
+        {
+          "tag": "button.refresh.kf-btn",
+          "w": 52,
+          "h": 32,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "刷新"
+        },
+        {
+          "tag": "select",
+          "w": 69,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "8px",
+          "font": "14px",
+          "reasons": [],
+          "text": "未选择\n            "
+        },
+        {
+          "tag": "button",
+          "w": 42,
+          "h": 26,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "0px",
+          "font": "11px",
+          "reasons": [],
+          "text": "候选"
+        },
+        {
+          "tag": "button.ok",
+          "w": 42,
+          "h": 26,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "0px",
+          "font": "11px",
+          "reasons": [],
+          "text": "立项"
+        },
+        {
+          "tag": "button.no",
+          "w": 42,
+          "h": 26,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "0px",
+          "font": "11px",
+          "reasons": [],
+          "text": "否决"
+        },
+        {
+          "tag": "button.kf-btn.ghost",
+          "w": 72,
+          "h": 32,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "详情 →"
+        },
+        {
+          "tag": "button",
+          "w": 42,
+          "h": 26,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "0px",
+          "font": "11px",
+          "reasons": [],
+          "text": "候选"
+        },
+        {
+          "tag": "button.ok",
+          "w": 42,
+          "h": 26,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "0px",
+          "font": "11px",
+          "reasons": [],
+          "text": "立项"
+        },
+        {
+          "tag": "button.no",
+          "w": 42,
+          "h": 26,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "0px",
+          "font": "11px",
+          "reasons": [],
+          "text": "否决"
+        },
+        {
+          "tag": "button.kf-btn.ghost",
+          "w": 72,
+          "h": 32,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "详情 →"
+        },
+        {
+          "tag": "button",
+          "w": 42,
+          "h": 26,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "0px",
+          "font": "11px",
+          "reasons": [],
+          "text": "候选"
+        },
+        {
+          "tag": "button.ok",
+          "w": 42,
+          "h": 26,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "0px",
+          "font": "11px",
+          "reasons": [],
+          "text": "立项"
+        },
+        {
+          "tag": "button.no",
+          "w": 42,
+          "h": 26,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "0px",
+          "font": "11px",
+          "reasons": [],
+          "text": "否决"
+        },
+        {
+          "tag": "button.kf-btn.ghost",
+          "w": 72,
+          "h": 32,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "详情 →"
+        },
+        {
+          "tag": "button.kf-btn.ghost",
+          "w": 102,
+          "h": 32,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "回到顶部"
+        }
+      ],
+      "thickBorders": [
+        {
+          "tag": "span.live",
+          "w": 0,
+          "ow": 3,
+          "o": "0px"
+        }
+      ],
+      "blackText": [],
+      "fontSizes": {
+        "11": 76,
+        "13": 28,
+        "14": 8,
+        "16": 11,
+        "20": 13,
+        "32": 4
+      },
+      "rows": [
+        {
+          "container": "div.grid2.kf-enter",
+          "top": 148,
+          "n": 2,
+          "spread": 0,
+          "heights": [
+            267,
+            267
+          ]
+        },
+        {
+          "container": "div.stagebar",
+          "top": 248,
+          "n": 5,
+          "spread": 0,
+          "heights": [
+            73,
+            73,
+            73,
+            73,
+            73
+          ]
+        },
+        {
+          "container": "div.grid4",
+          "top": 476,
+          "n": 4,
+          "spread": 0,
+          "heights": [
+            77,
+            77,
+            77,
+            77
+          ]
+        },
+        {
+          "container": "div.latest",
+          "top": 568,
+          "n": 3,
+          "spread": 0,
+          "heights": [
+            389,
+            389,
+            389
+          ]
+        },
+        {
+          "container": "div.nums",
+          "top": 748,
+          "n": 3,
+          "spread": 0,
+          "heights": [
+            42,
+            42,
+            42
+          ]
+        }
+      ],
+      "blanks": [],
+      "clickables": 14,
+      "hoverFail": [],
+      "scrollH": 1036,
+      "lastContentBottom": 0,
+      "filterLayout": null,
+      "wbCols": {
+        "heights": [
+          267,
+          267
+        ],
+        "diff": 0,
+        "leftMaxGap": 0
+      },
+      "radiiWhere": {
+        "0": "html",
+        "8": "span.badge",
+        "12": "aside.side.kf-glass.is-panel"
+      },
+      "viewportH": 900
+    },
+    {
+      "page": "/pool",
+      "radii": {
+        "0": 1628,
+        "8": 376,
+        "12": 112,
+        "circle": 4
+      },
+      "controls": [
+        {
+          "tag": "button.refresh.kf-btn",
+          "w": 52,
+          "h": 32,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "刷新"
+        },
+        {
+          "tag": "select",
+          "w": 69,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "8px",
+          "font": "14px",
+          "reasons": [],
+          "text": "未选择\n            "
+        },
+        {
+          "tag": "button.kf-chip",
+          "w": 59,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "12px",
+          "reasons": [],
+          "text": "候选4"
+        },
+        {
+          "tag": "button.kf-chip",
+          "w": 71,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "12px",
+          "reasons": [],
+          "text": "已立项3"
+        },
+        {
+          "tag": "button.kf-chip",
+          "w": 71,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "12px",
+          "reasons": [],
+          "text": "已否决2"
+        },
+        {
+          "tag": "button.kf-chip",
+          "w": 57,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "12px",
+          "reasons": [],
+          "text": "S 级3"
+        },
+        {
+          "tag": "button.kf-chip",
+          "w": 58,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "12px",
+          "reasons": [],
+          "text": "A 级3"
+        },
+        {
+          "tag": "button.kf-chip",
+          "w": 57,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "12px",
+          "reasons": [],
+          "text": "B 级3"
+        },
+        {
+          "tag": "button.kf-chip",
+          "w": 48,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "12px",
+          "reasons": [],
+          "text": "S11"
+        },
+        {
+          "tag": "button.kf-chip",
+          "w": 50,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "12px",
+          "reasons": [],
+          "text": "S21"
+        },
+        {
+          "tag": "button.kf-chip",
+          "w": 51,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "12px",
+          "reasons": [],
+          "text": "S34"
+        },
+        {
+          "tag": "button.kf-chip",
+          "w": 51,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "12px",
+          "reasons": [],
+          "text": "S42"
+        },
+        {
+          "tag": "button.kf-chip",
+          "w": 50,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "12px",
+          "reasons": [],
+          "text": "S51"
+        },
+        {
+          "tag": "button.kf-chip",
+          "w": 59,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "12px",
+          "reasons": [],
+          "text": "拌面5"
+        },
+        {
+          "tag": "button.kf-chip",
+          "w": 59,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "12px",
+          "reasons": [],
+          "text": "汤面2"
+        },
+        {
+          "tag": "button.kf-chip",
+          "w": 59,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "12px",
+          "reasons": [],
+          "text": "杯面1"
+        },
+        {
+          "tag": "button.kf-chip",
+          "w": 59,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "12px",
+          "reasons": [],
+          "text": "炒面1"
+        },
+        {
+          "tag": "button.kf-chip",
+          "w": 59,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "12px",
+          "reasons": [],
+          "text": "陈静2"
+        },
+        {
+          "tag": "button.kf-chip",
+          "w": 59,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "12px",
+          "reasons": [],
+          "text": "李娜1"
+        },
+        {
+          "tag": "button.kf-chip",
+          "w": 59,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "12px",
+          "reasons": [],
+          "text": "刘洋1"
+        },
+        {
+          "tag": "button.kf-chip",
+          "w": 59,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "12px",
+          "reasons": [],
+          "text": "孙悦1"
+        },
+        {
+          "tag": "button.kf-chip",
+          "w": 59,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "12px",
+          "reasons": [],
+          "text": "王芳1"
+        },
+        {
+          "tag": "button.kf-chip",
+          "w": 59,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "12px",
+          "reasons": [],
+          "text": "张伟1"
+        },
+        {
+          "tag": "button.kf-chip",
+          "w": 59,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "12px",
+          "reasons": [],
+          "text": "赵磊1"
+        },
+        {
+          "tag": "button.kf-chip",
+          "w": 59,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "12px",
+          "reasons": [],
+          "text": "周涛1"
+        },
+        {
+          "tag": "button",
+          "w": 111,
+          "h": 28,
+          "bg": "rgb(24, 24, 27)",
+          "bw": 0,
+          "r": "0px",
+          "font": "12px",
+          "reasons": [],
+          "text": "优先级 S→A→B"
+        },
+        {
+          "tag": "button",
+          "w": 112,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "0px",
+          "font": "12px",
+          "reasons": [],
+          "text": "综合评分 高→低"
+        },
+        {
+          "tag": "button",
+          "w": 112,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "0px",
+          "font": "12px",
+          "reasons": [],
+          "text": "证据强度 高→低"
+        },
+        {
+          "tag": "button",
+          "w": 73,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "0px",
+          "font": "12px",
+          "reasons": [],
+          "text": "最新上传"
+        },
+        {
+          "tag": "button",
+          "w": 73,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "0px",
+          "font": "12px",
+          "reasons": [],
+          "text": "分歧最大"
+        },
+        {
+          "tag": "input",
+          "w": 240,
+          "h": 28,
+          "bg": "rgba(255, 255, 255, 0.5)",
+          "bw": 1,
+          "r": "8px",
+          "font": "12px",
+          "reasons": [],
+          "text": "搜索卡片标题 / 一句话 / 上"
+        },
+        {
+          "tag": "button.kf-btn.ghost",
+          "w": 90,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "12px",
+          "reasons": [],
+          "text": "回到顶部"
+        },
+        {
+          "tag": "button",
+          "w": 42,
+          "h": 26,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "0px",
+          "font": "11px",
+          "reasons": [],
+          "text": "候选"
+        },
+        {
+          "tag": "button.ok",
+          "w": 42,
+          "h": 26,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "0px",
+          "font": "11px",
+          "reasons": [],
+          "text": "立项"
+        },
+        {
+          "tag": "button.no",
+          "w": 42,
+          "h": 26,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "0px",
+          "font": "11px",
+          "reasons": [],
+          "text": "否决"
+        },
+        {
+          "tag": "button.kf-btn.ghost",
+          "w": 72,
+          "h": 32,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "详情 →"
+        },
+        {
+          "tag": "button",
+          "w": 42,
+          "h": 26,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "0px",
+          "font": "11px",
+          "reasons": [],
+          "text": "候选"
+        },
+        {
+          "tag": "button.ok",
+          "w": 42,
+          "h": 26,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "0px",
+          "font": "11px",
+          "reasons": [],
+          "text": "立项"
+        },
+        {
+          "tag": "button.no",
+          "w": 42,
+          "h": 26,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "0px",
+          "font": "11px",
+          "reasons": [],
+          "text": "否决"
+        },
+        {
+          "tag": "button.kf-btn.ghost",
+          "w": 72,
+          "h": 32,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "详情 →"
+        },
+        {
+          "tag": "button",
+          "w": 42,
+          "h": 26,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "0px",
+          "font": "11px",
+          "reasons": [],
+          "text": "候选"
+        },
+        {
+          "tag": "button.ok",
+          "w": 42,
+          "h": 26,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "0px",
+          "font": "11px",
+          "reasons": [],
+          "text": "立项"
+        },
+        {
+          "tag": "button.no",
+          "w": 42,
+          "h": 26,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "0px",
+          "font": "11px",
+          "reasons": [],
+          "text": "否决"
+        },
+        {
+          "tag": "button.kf-btn.ghost",
+          "w": 72,
+          "h": 32,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "详情 →"
+        },
+        {
+          "tag": "button",
+          "w": 42,
+          "h": 26,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "0px",
+          "font": "11px",
+          "reasons": [],
+          "text": "候选"
+        },
+        {
+          "tag": "button.ok",
+          "w": 42,
+          "h": 26,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "0px",
+          "font": "11px",
+          "reasons": [],
+          "text": "立项"
+        },
+        {
+          "tag": "button.no",
+          "w": 42,
+          "h": 26,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "0px",
+          "font": "11px",
+          "reasons": [],
+          "text": "否决"
+        },
+        {
+          "tag": "button.kf-btn.ghost",
+          "w": 72,
+          "h": 32,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "详情 →"
+        },
+        {
+          "tag": "button",
+          "w": 42,
+          "h": 26,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "0px",
+          "font": "11px",
+          "reasons": [],
+          "text": "候选"
+        },
+        {
+          "tag": "button.ok",
+          "w": 42,
+          "h": 26,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "0px",
+          "font": "11px",
+          "reasons": [],
+          "text": "立项"
+        },
+        {
+          "tag": "button.no",
+          "w": 42,
+          "h": 26,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "0px",
+          "font": "11px",
+          "reasons": [],
+          "text": "否决"
+        },
+        {
+          "tag": "button.kf-btn.ghost",
+          "w": 72,
+          "h": 32,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "详情 →"
+        },
+        {
+          "tag": "button",
+          "w": 42,
+          "h": 26,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "0px",
+          "font": "11px",
+          "reasons": [],
+          "text": "候选"
+        },
+        {
+          "tag": "button.ok",
+          "w": 42,
+          "h": 26,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "0px",
+          "font": "11px",
+          "reasons": [],
+          "text": "立项"
+        },
+        {
+          "tag": "button.no",
+          "w": 42,
+          "h": 26,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "0px",
+          "font": "11px",
+          "reasons": [],
+          "text": "否决"
+        },
+        {
+          "tag": "button.kf-btn.ghost",
+          "w": 72,
+          "h": 32,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "详情 →"
+        },
+        {
+          "tag": "button",
+          "w": 42,
+          "h": 26,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "0px",
+          "font": "11px",
+          "reasons": [],
+          "text": "候选"
+        },
+        {
+          "tag": "button.ok",
+          "w": 42,
+          "h": 26,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "0px",
+          "font": "11px",
+          "reasons": [],
+          "text": "立项"
+        },
+        {
+          "tag": "button.no",
+          "w": 42,
+          "h": 26,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "0px",
+          "font": "11px",
+          "reasons": [],
+          "text": "否决"
+        },
+        {
+          "tag": "button.kf-btn.ghost",
+          "w": 72,
+          "h": 32,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "详情 →"
+        },
+        {
+          "tag": "button",
+          "w": 42,
+          "h": 26,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "0px",
+          "font": "11px",
+          "reasons": [],
+          "text": "候选"
+        },
+        {
+          "tag": "button.ok",
+          "w": 42,
+          "h": 26,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "0px",
+          "font": "11px",
+          "reasons": [],
+          "text": "立项"
+        },
+        {
+          "tag": "button.no",
+          "w": 42,
+          "h": 26,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "0px",
+          "font": "11px",
+          "reasons": [],
+          "text": "否决"
+        },
+        {
+          "tag": "button.kf-btn.ghost",
+          "w": 72,
+          "h": 32,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "详情 →"
+        },
+        {
+          "tag": "button",
+          "w": 42,
+          "h": 26,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "0px",
+          "font": "11px",
+          "reasons": [],
+          "text": "候选"
+        },
+        {
+          "tag": "button.ok",
+          "w": 42,
+          "h": 26,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "0px",
+          "font": "11px",
+          "reasons": [],
+          "text": "立项"
+        },
+        {
+          "tag": "button.no",
+          "w": 42,
+          "h": 26,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "0px",
+          "font": "11px",
+          "reasons": [],
+          "text": "否决"
+        },
+        {
+          "tag": "button.kf-btn.ghost",
+          "w": 72,
+          "h": 32,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "详情 →"
+        }
+      ],
+      "thickBorders": [
+        {
+          "tag": "span.live",
+          "w": 0,
+          "ow": 3,
+          "o": "0px"
+        }
+      ],
+      "blackText": [],
+      "fontSizes": {
+        "11": 158,
+        "12": 7,
+        "13": 44,
+        "14": 7,
+        "16": 11,
+        "20": 29
+      },
+      "rows": [
+        {
+          "container": "div.cards.kf-enter",
+          "top": 508,
+          "n": 3,
+          "spread": 0,
+          "heights": [
+            389,
+            389,
+            389
+          ]
+        },
+        {
+          "container": "div.cards.kf-enter",
+          "top": 912,
+          "n": 3,
+          "spread": 0,
+          "heights": [
+            389,
+            389,
+            389
+          ]
+        },
+        {
+          "container": "div.cards.kf-enter",
+          "top": 1320,
+          "n": 3,
+          "spread": 0,
+          "heights": [
+            389,
+            389,
+            389
+          ]
+        },
+        {
+          "container": "div.nums",
+          "top": 688,
+          "n": 3,
+          "spread": 0,
+          "heights": [
+            42,
+            42,
+            42
+          ]
+        },
+        {
+          "container": "div.nums",
+          "top": 1092,
+          "n": 3,
+          "spread": 0,
+          "heights": [
+            42,
+            42,
+            42
+          ]
+        },
+        {
+          "container": "div.nums",
+          "top": 1500,
+          "n": 3,
+          "spread": 0,
+          "heights": [
+            42,
+            42,
+            42
+          ]
+        }
+      ],
+      "blanks": [],
+      "clickables": 51,
+      "hoverFail": [],
+      "scrollH": 1755,
+      "lastContentBottom": 0,
+      "filterLayout": {
+        "container": true,
+        "filtersInsideToolbar": true,
+        "seg": true,
+        "segGap": "normal",
+        "segBtnHeights": [
+          28,
+          28,
+          28,
+          28,
+          28
+        ],
+        "chipHeights": [
+          28,
+          28,
+          28,
+          28,
+          28,
+          28,
+          28,
+          28,
+          28,
+          28,
+          28,
+          28,
+          28,
+          28,
+          28,
+          28,
+          28,
+          28,
+          28,
+          28,
+          28,
+          28,
+          28
+        ],
+        "chipFonts": [
+          "12px"
+        ],
+        "searchH": 28,
+        "bottomDelta": -10,
+        "rowBottomDelta": 1,
+        "segBorder": [
+          "0px 0px",
+          "0px 0px",
+          "0px 0px",
+          "0px 0px",
+          "0px 0px"
+        ]
+      },
+      "wbCols": null,
+      "radiiWhere": {
+        "0": "html",
+        "8": "span.badge",
+        "12": "aside.side.kf-glass.is-panel"
+      },
+      "viewportH": 900
+    },
+    {
+      "page": "/stages",
+      "radii": {
+        "0": 3472,
+        "2": 12,
+        "8": 524,
+        "12": 336,
+        "circle": 4
+      },
+      "controls": [
+        {
+          "tag": "button.refresh.kf-btn",
+          "w": 52,
+          "h": 32,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "刷新"
+        },
+        {
+          "tag": "select",
+          "w": 69,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "8px",
+          "font": "14px",
+          "reasons": [],
+          "text": "未选择\n            "
+        },
+        {
+          "tag": "button.kf-chip",
+          "w": 56,
+          "h": 28,
+          "bg": "rgb(24, 24, 27)",
+          "bw": 1,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "35"
+        },
+        {
+          "tag": "button.kf-chip",
+          "w": 47,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "S1 6"
+        },
+        {
+          "tag": "button.kf-chip",
+          "w": 50,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "S2 8"
+        },
+        {
+          "tag": "button.kf-chip",
+          "w": 50,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "S3 9"
+        },
+        {
+          "tag": "button.kf-chip",
+          "w": 51,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "S4 6"
+        },
+        {
+          "tag": "button.kf-chip",
+          "w": 50,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "S5 6"
+        },
+        {
+          "tag": "button.kf-chip",
+          "w": 36,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": ""
+        },
+        {
+          "tag": "button.kf-chip",
+          "w": 36,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": ""
+        },
+        {
+          "tag": "button.kf-btn.ghost",
+          "w": 90,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "12px",
+          "reasons": [],
+          "text": "调整筛选"
+        },
+        {
+          "tag": "button.kf-btn.ghost",
+          "w": 90,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "12px",
+          "reasons": [],
+          "text": "回到顶部"
+        }
+      ],
+      "thickBorders": [
+        {
+          "tag": "span.live",
+          "w": 0,
+          "ow": 3,
+          "o": "0px"
+        }
+      ],
+      "blackText": [],
+      "fontSizes": {
+        "11": 278,
+        "12": 176,
+        "13": 21,
+        "14": 7,
+        "16": 106,
+        "20": 7
+      },
+      "rows": [
+        {
+          "container": "div.overview",
+          "top": 88,
+          "n": 5,
+          "spread": 0,
+          "heights": [
+            119,
+            119,
+            119,
+            119,
+            119
+          ]
+        },
+        {
+          "container": "div.cards",
+          "top": 296,
+          "n": 3,
+          "spread": 0,
+          "heights": [
+            300,
+            300,
+            300
+          ]
+        },
+        {
+          "container": "div.cards",
+          "top": 608,
+          "n": 3,
+          "spread": 0,
+          "heights": [
+            279,
+            279,
+            279
+          ]
+        },
+        {
+          "container": "div.cards",
+          "top": 900,
+          "n": 3,
+          "spread": 0,
+          "heights": [
+            246,
+            246,
+            246
+          ]
+        },
+        {
+          "container": "div.cards",
+          "top": 1156,
+          "n": 3,
+          "spread": 0,
+          "heights": [
+            246,
+            246,
+            246
+          ]
+        },
+        {
+          "container": "div.cards",
+          "top": 1416,
+          "n": 3,
+          "spread": 0,
+          "heights": [
+            246,
+            246,
+            246
+          ]
+        },
+        {
+          "container": "div.cards",
+          "top": 1672,
+          "n": 3,
+          "spread": 0,
+          "heights": [
+            246,
+            246,
+            246
+          ]
+        },
+        {
+          "container": "div.cards",
+          "top": 1932,
+          "n": 3,
+          "spread": 0,
+          "heights": [
+            246,
+            246,
+            246
+          ]
+        },
+        {
+          "container": "div.cards",
+          "top": 2188,
+          "n": 3,
+          "spread": 0,
+          "heights": [
+            246,
+            246,
+            246
+          ]
+        },
+        {
+          "container": "div.cards",
+          "top": 2448,
+          "n": 3,
+          "spread": 0,
+          "heights": [
+            376,
+            376,
+            376
+          ]
+        },
+        {
+          "container": "div.cards",
+          "top": 2836,
+          "n": 3,
+          "spread": 0,
+          "heights": [
+            300,
+            300,
+            300
+          ]
+        },
+        {
+          "container": "div.cards",
+          "top": 3148,
+          "n": 3,
+          "spread": 0,
+          "heights": [
+            300,
+            300,
+            300
+          ]
+        },
+        {
+          "container": "div.cards",
+          "top": 3460,
+          "n": 2,
+          "spread": 0,
+          "heights": [
+            300,
+            300
+          ]
+        }
+      ],
+      "blanks": [],
+      "clickables": 26,
+      "hoverFail": [],
+      "scrollH": 3992,
+      "lastContentBottom": 0,
+      "filterLayout": null,
+      "wbCols": null,
+      "radiiWhere": {
+        "0": "html",
+        "2": "i",
+        "8": "span.badge",
+        "12": "aside.side.kf-glass.is-panel"
+      },
+      "viewportH": 900
+    },
+    {
+      "page": "/reports",
+      "radii": {
+        "0": 2028,
+        "8": 388,
+        "12": 176,
+        "circle": 4
+      },
+      "controls": [
+        {
+          "tag": "button.refresh.kf-btn",
+          "w": 52,
+          "h": 32,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "刷新"
+        },
+        {
+          "tag": "select",
+          "w": 69,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "8px",
+          "font": "14px",
+          "reasons": [],
+          "text": "未选择\n            "
+        },
+        {
+          "tag": "button.opt",
+          "w": 176,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "S17"
+        },
+        {
+          "tag": "button.opt",
+          "w": 176,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "S29"
+        },
+        {
+          "tag": "button.opt",
+          "w": 176,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "S313"
+        },
+        {
+          "tag": "button.opt",
+          "w": 176,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "S48"
+        },
+        {
+          "tag": "button.opt",
+          "w": 176,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "S57"
+        },
+        {
+          "tag": "button.opt",
+          "w": 176,
+          "h": 30,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "拌面35"
+        },
+        {
+          "tag": "button.opt",
+          "w": 176,
+          "h": 30,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "汤面7"
+        },
+        {
+          "tag": "button.opt",
+          "w": 176,
+          "h": 30,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "炒面3"
+        },
+        {
+          "tag": "button.opt",
+          "w": 176,
+          "h": 30,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "S 级6"
+        },
+        {
+          "tag": "button.opt",
+          "w": 176,
+          "h": 30,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "A 级6"
+        },
+        {
+          "tag": "button.opt",
+          "w": 176,
+          "h": 30,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "B 级6"
+        },
+        {
+          "tag": "button.opt",
+          "w": 176,
+          "h": 30,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "高4"
+        },
+        {
+          "tag": "button.opt",
+          "w": 176,
+          "h": 30,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "中29"
+        },
+        {
+          "tag": "button.opt",
+          "w": 176,
+          "h": 30,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "低11"
+        },
+        {
+          "tag": "button.opt",
+          "w": 176,
+          "h": 30,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "待补1"
+        },
+        {
+          "tag": "button.opt",
+          "w": 176,
+          "h": 30,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "会话上传8"
+        },
+        {
+          "tag": "button.opt",
+          "w": 176,
+          "h": 30,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "Skill 上传34"
+        },
+        {
+          "tag": "button.opt",
+          "w": 176,
+          "h": 30,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "手工录入2"
+        },
+        {
+          "tag": "button.opt",
+          "w": 176,
+          "h": 30,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "陈静0"
+        },
+        {
+          "tag": "button.opt",
+          "w": 176,
+          "h": 30,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "周涛0"
+        },
+        {
+          "tag": "button.opt",
+          "w": 176,
+          "h": 30,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "孙悦0"
+        },
+        {
+          "tag": "button.opt",
+          "w": 176,
+          "h": 30,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "李娜0"
+        },
+        {
+          "tag": "button.opt",
+          "w": 176,
+          "h": 30,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "张伟0"
+        },
+        {
+          "tag": "button.opt",
+          "w": 176,
+          "h": 30,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "赵磊0"
+        },
+        {
+          "tag": "button.opt",
+          "w": 176,
+          "h": 30,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "王芳0"
+        },
+        {
+          "tag": "button.opt",
+          "w": 176,
+          "h": 30,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "郑凯0"
+        },
+        {
+          "tag": "button.opt",
+          "w": 176,
+          "h": 30,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "吴敏0"
+        },
+        {
+          "tag": "button.opt",
+          "w": 176,
+          "h": 30,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "刘洋0"
+        },
+        {
+          "tag": "button.kf-chip",
+          "w": 77,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "有证据"
+        },
+        {
+          "tag": "button.kf-chip",
+          "w": 64,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "我的"
+        },
+        {
+          "tag": "button.kf-chip",
+          "w": 90,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "可看全文"
+        },
+        {
+          "tag": "button.kf-chip",
+          "w": 75,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "13px",
+          "reasons": [],
+          "text": "近 7 天"
+        },
+        {
+          "tag": "button",
+          "w": 38,
+          "h": 22,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "8px",
+          "font": "11px",
+          "reasons": [],
+          "text": "评分"
+        },
+        {
+          "tag": "button",
+          "w": 38,
+          "h": 22,
+          "bg": "rgba(24, 24, 27, 0.07)",
+          "bw": 0,
+          "r": "8px",
+          "font": "11px",
+          "reasons": [],
+          "text": "最新"
+        },
+        {
+          "tag": "button",
+          "w": 38,
+          "h": 22,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "8px",
+          "font": "11px",
+          "reasons": [],
+          "text": "证据"
+        },
+        {
+          "tag": "button",
+          "w": 34,
+          "h": 26,
+          "bg": "rgb(24, 24, 27)",
+          "bw": 0,
+          "r": "0px",
+          "font": "11px",
+          "reasons": [],
+          "text": ""
+        },
+        {
+          "tag": "button",
+          "w": 34,
+          "h": 26,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "0px",
+          "font": "11px",
+          "reasons": [],
+          "text": ""
+        },
+        {
+          "tag": "button",
+          "w": 38,
+          "h": 22,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "8px",
+          "font": "11px",
+          "reasons": [],
+          "text": "评分"
+        },
+        {
+          "tag": "button",
+          "w": 38,
+          "h": 22,
+          "bg": "rgba(24, 24, 27, 0.07)",
+          "bw": 0,
+          "r": "8px",
+          "font": "11px",
+          "reasons": [],
+          "text": "最新"
+        },
+        {
+          "tag": "button",
+          "w": 38,
+          "h": 22,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 0,
+          "r": "8px",
+          "font": "11px",
+          "reasons": [],
+          "text": "证据"
+        },
+        {
+          "tag": "button.kf-btn.ghost",
+          "w": 90,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "12px",
+          "reasons": [],
+          "text": "调整筛选"
+        },
+        {
+          "tag": "button.kf-btn.ghost",
+          "w": 90,
+          "h": 28,
+          "bg": "rgba(0, 0, 0, 0)",
+          "bw": 1,
+          "r": "8px",
+          "font": "12px",
+          "reasons": [],
+          "text": "回到顶部"
+        }
+      ],
+      "thickBorders": [
+        {
+          "tag": "span.live",
+          "w": 0,
+          "ow": 3,
+          "o": "0px"
+        }
+      ],
+      "blackText": [],
+      "fontSizes": {
+        "11": 122,
+        "12": 143,
+        "13": 7,
+        "14": 15,
+        "16": 38,
+        "20": 2,
+        "32": 5
+      },
+      "rows": [
+        {
+          "container": "div.types",
+          "top": 164,
+          "n": 5,
+          "spread": 0,
+          "heights": [
+            315,
+            315,
+            315,
+            315,
+            315
+          ]
+        },
+        {
+          "container": "div.cards",
+          "top": 704,
+          "n": 3,
+          "spread": 0,
+          "heights": [
+            284,
+            284,
+            284
+          ]
+        },
+        {
+          "container": "div.cards",
+          "top": 1000,
+          "n": 3,
+          "spread": 0,
+          "heights": [
+            284,
+            284,
+            284
+          ]
+        },
+        {
+          "container": "div.cards",
+          "top": 1296,
+          "n": 3,
+          "spread": 0,
+          "heights": [
+            263,
+            263,
+            263
+          ]
+        },
+        {
+          "container": "div.cards",
+          "top": 1572,
+          "n": 3,
+          "spread": 0,
+          "heights": [
+            231,
+            231,
+            231
+          ]
+        }
+      ],
+      "blanks": [],
+      "clickables": 61,
+      "hoverFail": [],
+      "scrollH": 1958,
+      "lastContentBottom": 0,
+      "filterLayout": null,
+      "wbCols": null,
+      "radiiWhere": {
+        "0": "html",
+        "8": "span.badge",
+        "12": "aside.side.kf-glass.is-panel"
+      },
+      "viewportH": 900
+    }
+  ],
+  "checks": [
+    {
+      "name": "圆角种类 ≤ 3 且 ∈ {8,12,20}(0 只允许分割线/非容器)",
+      "ok": false,
+      "detail": "容器圆角(≤6px 的量条/色点不计)5:0px×8072、2px×12、8px×1420、12px×756、circlepx×16\n    违禁:2px×12(如 i)"
+    },
+    {
+      "name": "原生外观控件 = 0",
+      "ok": true,
+      "detail": "控件总数 139,原生外观 0"
+    },
+    {
+      "name": "描边 > 1px = 0",
+      "ok": true,
+      "detail": "> 1px border 0"
+    },
+    {
+      "name": "纯黑 rgb(0,0,0) = 0",
+      "ok": true,
+      "detail": "0"
+    },
+    {
+      "name": "字号档 ≤ 5 且无 13.3333px",
+      "ok": false,
+      "detail": "7 档:11×634 / 12×326 / 13×100 / 14×37 / 16×166 / 20×51 / 32×9"
+    },
+    {
+      "name": "同排卡片高度差 ≤ 2px",
+      "ok": true,
+      "detail": "测了 29 行,超差 0"
+    },
+    {
+      "name": "卡内连续空白 ≤ 24px",
+      "ok": true,
+      "detail": "超 24px 的位置 0"
+    },
+    {
+      "name": "可点元素 hover 反馈",
+      "ok": true,
+      "detail": "可点 152,无反馈 0"
+    },
+    {
+      "name": "P1 筛选栏:一个容器 + 分段排序 + 与搜索框等高底对齐",
+      "ok": true,
+      "detail": "容器 true,filters 在内 true,seg gap=normal 按钮高[28,28,28,28,28],chip 高[28] 字号[12px],搜索框高 28,同行底边差 1px,与容器底边差 -10px"
+    },
+    {
+      "name": "P3 工作台两栏等高(差 ≤ 2px)且左卡无 > 32px 连续空白",
+      "ok": true,
+      "detail": "两栏高 [267,267],差 0px,左卡最大连续空白 0px"
+    },
+    {
+      "name": "滚动后视口外可点元素 hover 反馈",
+      "ok": true,
+      "detail": "/ 测 39 个,无反馈 0;/pool 测 123 个,无反馈 0;/stages 测 80 个,无反馈 0;/reports 测 50 个,无反馈 0"
+    },
+    {
+      "name": "P2 同排卡「来源行 / 操作条」底边差 ≤ 2px",
+      "ok": true,
+      "detail": "测了 4 个多卡行:全部 ≤ 2px"
+    },
+    {
+      "name": "页面底部空白 ≤ 120px(视口下 + 滚到底两处都量)",
+      "ok": true,
+      "detail": "/ 滚动余量 1036px / 滚到底视口留白 48px;/pool 滚动余量 1755px / 滚到底视口留白 64px;/stages 滚动余量 3992px / 滚到底视口留白 60px;/reports 滚动余量 1958px / 滚到底视口留白 60px"
+    }
+  ],
+  "errors": []
+}

+ 21 - 21
docs/v12-after-p2.json

@@ -1,6 +1,6 @@
 {
 {
   "tag": "before",
   "tag": "before",
-  "at": "2026-10-30T07:03:50.635Z",
+  "at": "2026-10-30T07:13:25.714Z",
   "pages": [
   "pages": [
     {
     {
       "page": "/",
       "page": "/",
@@ -1239,7 +1239,7 @@
     {
     {
       "page": "/stages",
       "page": "/stages",
       "radii": {
       "radii": {
-        "0": 3448,
+        "0": 3468,
         "2": 12,
         "2": 12,
         "8": 524,
         "8": 524,
         "12": 336,
         "12": 336,
@@ -1389,11 +1389,11 @@
       ],
       ],
       "blackText": [],
       "blackText": [],
       "fontSizes": {
       "fontSizes": {
-        "11": 276,
+        "11": 278,
         "12": 176,
         "12": 176,
         "13": 21,
         "13": 21,
         "14": 7,
         "14": 7,
-        "16": 105,
+        "16": 106,
         "20": 7
         "20": 7
       },
       },
       "rows": [
       "rows": [
@@ -1545,7 +1545,7 @@
       "blanks": [],
       "blanks": [],
       "clickables": 26,
       "clickables": 26,
       "hoverFail": [],
       "hoverFail": [],
-      "scrollH": 3928,
+      "scrollH": 3992,
       "lastContentBottom": 0,
       "lastContentBottom": 0,
       "filterLayout": null,
       "filterLayout": null,
       "wbCols": null,
       "wbCols": null,
@@ -1560,7 +1560,7 @@
     {
     {
       "page": "/reports",
       "page": "/reports",
       "radii": {
       "radii": {
-        "0": 2004,
+        "0": 2024,
         "8": 388,
         "8": 388,
         "12": 176,
         "12": 176,
         "circle": 4
         "circle": 4
@@ -2030,7 +2030,7 @@
         },
         },
         {
         {
           "tag": "button.cfb",
           "tag": "button.cfb",
-          "w": 52,
+          "w": 88,
           "h": 28,
           "h": 28,
           "bg": "rgba(0, 0, 0, 0)",
           "bg": "rgba(0, 0, 0, 0)",
           "bw": 1,
           "bw": 1,
@@ -2041,7 +2041,7 @@
         },
         },
         {
         {
           "tag": "button.cfb",
           "tag": "button.cfb",
-          "w": 52,
+          "w": 88,
           "h": 28,
           "h": 28,
           "bg": "rgba(0, 0, 0, 0)",
           "bg": "rgba(0, 0, 0, 0)",
           "bw": 1,
           "bw": 1,
@@ -2061,11 +2061,11 @@
       ],
       ],
       "blackText": [],
       "blackText": [],
       "fontSizes": {
       "fontSizes": {
-        "11": 120,
+        "11": 122,
         "12": 143,
         "12": 143,
         "13": 7,
         "13": 7,
         "14": 15,
         "14": 15,
-        "16": 37,
+        "16": 38,
         "20": 2,
         "20": 2,
         "32": 5
         "32": 5
       },
       },
@@ -2085,7 +2085,7 @@
         },
         },
         {
         {
           "container": "div.cards",
           "container": "div.cards",
-          "top": 616,
+          "top": 704,
           "n": 3,
           "n": 3,
           "spread": 0,
           "spread": 0,
           "heights": [
           "heights": [
@@ -2096,7 +2096,7 @@
         },
         },
         {
         {
           "container": "div.cards",
           "container": "div.cards",
-          "top": 912,
+          "top": 1000,
           "n": 3,
           "n": 3,
           "spread": 0,
           "spread": 0,
           "heights": [
           "heights": [
@@ -2107,7 +2107,7 @@
         },
         },
         {
         {
           "container": "div.cards",
           "container": "div.cards",
-          "top": 1208,
+          "top": 1296,
           "n": 3,
           "n": 3,
           "spread": 0,
           "spread": 0,
           "heights": [
           "heights": [
@@ -2118,7 +2118,7 @@
         },
         },
         {
         {
           "container": "div.cards",
           "container": "div.cards",
-          "top": 1484,
+          "top": 1572,
           "n": 3,
           "n": 3,
           "spread": 0,
           "spread": 0,
           "heights": [
           "heights": [
@@ -2129,9 +2129,9 @@
         }
         }
       ],
       ],
       "blanks": [],
       "blanks": [],
-      "clickables": 62,
+      "clickables": 61,
       "hoverFail": [],
       "hoverFail": [],
-      "scrollH": 1931,
+      "scrollH": 1958,
       "lastContentBottom": 0,
       "lastContentBottom": 0,
       "filterLayout": null,
       "filterLayout": null,
       "wbCols": null,
       "wbCols": null,
@@ -2147,7 +2147,7 @@
     {
     {
       "name": "圆角种类 ≤ 3 且 ∈ {8,12,20}(0 只允许分割线/非容器)",
       "name": "圆角种类 ≤ 3 且 ∈ {8,12,20}(0 只允许分割线/非容器)",
       "ok": false,
       "ok": false,
-      "detail": "容器圆角(≤6px 的量条/色点不计)5:0px×8032、2px×12、8px×1412、12px×756、circlepx×16\n    违禁:2px×12(如 i)"
+      "detail": "容器圆角(≤6px 的量条/色点不计)5:0px×8072、2px×12、8px×1412、12px×756、circlepx×16\n    违禁:2px×12(如 i)"
     },
     },
     {
     {
       "name": "原生外观控件 = 0",
       "name": "原生外观控件 = 0",
@@ -2167,7 +2167,7 @@
     {
     {
       "name": "字号档 ≤ 5 且无 13.3333px",
       "name": "字号档 ≤ 5 且无 13.3333px",
       "ok": false,
       "ok": false,
-      "detail": "7 档:11×630 / 12×325 / 13×100 / 14×38 / 16×164 / 20×51 / 32×9"
+      "detail": "7 档:11×634 / 12×325 / 13×100 / 14×38 / 16×166 / 20×51 / 32×9"
     },
     },
     {
     {
       "name": "同排卡片高度差 ≤ 2px",
       "name": "同排卡片高度差 ≤ 2px",
@@ -2182,7 +2182,7 @@
     {
     {
       "name": "可点元素 hover 反馈",
       "name": "可点元素 hover 反馈",
       "ok": true,
       "ok": true,
-      "detail": "可点 152,无反馈 0"
+      "detail": "可点 151,无反馈 0"
     },
     },
     {
     {
       "name": "P1 筛选栏:一个容器 + 分段排序 + 与搜索框等高底对齐",
       "name": "P1 筛选栏:一个容器 + 分段排序 + 与搜索框等高底对齐",
@@ -2197,7 +2197,7 @@
     {
     {
       "name": "滚动后视口外可点元素 hover 反馈",
       "name": "滚动后视口外可点元素 hover 反馈",
       "ok": false,
       "ok": false,
-      "detail": "/ 测 33 个,无反馈 0;/pool 测 112 个,无反馈 0;/stages 测 74 个,无反馈 0;/reports 测 89 个,无反馈 1(summary「排序:最新」)"
+      "detail": "/ 测 33 个,无反馈 0;/pool 测 112 个,无反馈 0;/stages 测 74 个,无反馈 0;/reports 测 91 个,无反馈 1(summary「排序:最新」)"
     },
     },
     {
     {
       "name": "P2 同排卡「来源行 / 操作条」底边差 ≤ 2px",
       "name": "P2 同排卡「来源行 / 操作条」底边差 ≤ 2px",
@@ -2207,7 +2207,7 @@
     {
     {
       "name": "页面底部空白 ≤ 120px(视口下 + 滚到底两处都量)",
       "name": "页面底部空白 ≤ 120px(视口下 + 滚到底两处都量)",
       "ok": true,
       "ok": true,
-      "detail": "/ 滚动余量 1028px / 滚到底视口留白 48px;/pool 滚动余量 1779px / 滚到底视口留白 48px;/stages 滚动余量 3928px / 滚到底视口留白 104px;/reports 滚动余量 1931px / 滚到底视口留白 104px"
+      "detail": "/ 滚动余量 1028px / 滚到底视口留白 48px;/pool 滚动余量 1779px / 滚到底视口留白 48px;/stages 滚动余量 3992px / 滚到底视口留白 60px;/reports 滚动余量 1958px / 滚到底视口留白 60px"
     }
     }
   ],
   ],
   "errors": []
   "errors": []

+ 149 - 0
docs/v12-before-after.md

@@ -0,0 +1,149 @@
+# v12 深度视觉重构 · 改前 / 改后实测
+
+> 所有数字来自 `scripts/verify-visual-v12.mjs` 的实测打印,不是估计。
+> 改前基线:`node scripts/verify-visual-v12.mjs --tag before --json docs/v12-before.json`(v12 开工前,代码为 `434e428`)
+> 改后终版:`node scripts/verify-visual-v12.mjs`(判分模式,任一项不达标 exit 1)
+> 各阶段中间数字留在 `docs/v12-after-p0.json` / `v12-after-p1.json` / `docs/v12-after-p2.json`。
+> 截图:`node scripts/shot-v12.mjs` → `docs/screenshots/v12/`(desktop-* / mobile-* 各 4 页)。
+> 视口:桌面 1440×900,页面 `/`、`/pool`、`/stages`、`/reports`。
+
+## 0. 总结(13 项实测,终版 **11/13 达标**,**exit 1**:圆角「0 只允许非容器」与字号 ≤5 两条未过)
+
+| # | 项 | 改前实测 | 改后实测 | 结论 |
+|---|---|---|---|---|
+| 1 | 圆角种类 | 6 种(容器级:6 / 8 / 12,另有 0 与圆形) | 4 种:0 / 8 / 12 / 圆(**按规则判不达标**:0px 计到了 8072 个无圆角容器) | ✗ 未达标(见未达成 1) |
+| 2 | 原生外观控件 | 0(自带的只有 kf-btn / kf-chip,其余原生控件本来就被途经样式覆盖) | 0 / 控件 139(含页脚按钮 `.kf-btn.ghost`,共享表给外观) | ✅ |
+| 3 | 描边 > 1px | 0(**初版漏测了 `.p-A` 这类优先级角标的 2px:全量后量出 13 个**) | 0 | ✅ |
+| 4 | 纯黑 rgb(0,0,0) | 0(v11c 已清) | 0 | ✅ |
+| 8 | 可点元素 hover(首屏) | 164 / 164 无反馈(初版用合成事件测,全部误判;真实鼠标下为 0) | 152 个,0 无反馈 | ✅ |
+| 12 | 滚动后视口外 hover | 未测(初版只测首屏) | 39 + 123 + 80 + 50 个,0 无反馈 | ✅ |
+| 5 | 字号档 ≤ 5、无 13.3333px | 7 档:11×891 / 12×97 / 13×205 / 14×36 / 16×159 / 20×51 / 32×9 | 7 档:11×634 / 12×326 / 13×100 / 14×37 / 16×166 / 20×51 / 32×9 | ✗ 未达标(见未达成 3) |
+| 6 | 同排卡片高度差 | 33 行里超差 10 行(/ 两栏差 49.2px;/ 最新 3 张 389/368/368;/stages 卡片 129.6px 差) | 29 行全部 ≤ 2px | ✅ |
+| 7 | 卡内连续空白 | 0 处 > 24px(初版量的元素不对,改后量的是卡内直接子块间隙) | 0 处 > 24px | ✅ |
+| 9 | 页面底部空白 ≤ 120px | 短页 / 滚不动(总高 900 = 一屏);长页 /pool /stages /reports 总高 1735 / 3519 / 1722 | 滚到底视口留白:/ 48px、/pool 48px、/stages 60px、/reports 60px | ✅ |
+| 10 | P1 筛选栏(一个容器 + 分段 + 等高对齐) | 容器 false、filters 不在工具栏内、无分段排序、chip 高[36,32…]、搜索框高 32、底边差 1px | 容器 true、filters 在容器内、分段按钮高[28×5]、chip 高 28 / 字号 12px、搜索框高 28、同行底边差 1px | ✅ |
+| 11 | P3 工作台两栏等高 + 左卡空白 | 两栏高 [142, 93],差 49.2px;左卡「还没选我是」只有两行 | 两栏高 [267, 267],差 0px,左卡最大连续空白 0px(左卡改为全员五阶段真实进度) | ✅ |
+| 13 | 同排卡「来源行 / 操作条」底边差 | 未测(初版无此项) | 4 个多卡行:全部 ≤ 2px | ✅ |
+
+## 1. P0 控件与圆角
+
+### 1.1 唯一圆角尺度
+
+改前(`src/styles/tokens.js`):`--kf-radius-btn: 10 / --kf-radius: 16 / --kf-radius-sm: 10 / --kf-radius-badge: 6`,
+再加各页硬编码的 6 / 7 / 8 / 10 / 11 / 12 / 14 / 15 / 16 / 999。
+
+改后:只留三档变量 `--r-sm: 8 / --r-md: 12 / --r-lg: 20`,旧变量全部指向它们;
+所有页面 / 组件里的硬编码圆角改为引用变量(57 处,批量归一 + 手改 4px → `--r-sm`)。
+
+| 页面 | 改前实测(四角全量、跨 shadow) | 改后实测 |
+|---|---|---|
+| / | 0×796、6×4、8×100、12×116、圆×4 | 6px 与 2px 均清零,只剩 0 / 8 / 12 / 圆 |
+| /pool | 0×1628、6×4、8×364、12×120 | 同左:6px 清零 |
+| /stages | 0×3440、2×12、6×4、8×508、12×340 | 2px(阶段色标,改后 pct 改 999px 胶囊,脚本按 ≤6px 小件不计)/ 6px 清零 |
+| /reports | 0×1984、6×4、8×372、12×204 | 6px 清零 |
+| 全站合计(容器级) | **6 / 8 / 12** 三档容器圆角(+ 7 / 10 / 15 / 16 在其它页与组件里) | **只剩 8 / 12**(容器级);另 0(未设圆角的容器)与圆形 |
+
+说明:脚本把 ≤ 6px 的小件(4px 高进度条的 2px 圆角、8px 色标)单独计数,不算「圆角体系」。
+**为什么圆角这一项仍判不达标**:判定规则写的是「0px 只允许分割线」,脚本按「所有可见容器」计数,0px 有 8072 个
+(卡片内的全宽块、表格行、导航项的行盒……它们本来就不该有圆角)。这一条是**量法与任务书措辞的冲突**,不是 6 / 7 / 10px 那一类「四档打架」的问题:
+违禁档 6 / 7 / 10 / 15 / 16 已全部清零。
+
+### 1.2 原生外观控件
+
+- 改前:`kf-btn` / `kf-chip` 有显式外观,但 `input[type=search]`、`select` 没有 `appearance: none`,
+  select 在顶栏画出系统箭头与边框;`report-card` 的优先级角标写的是 `border: 2px`。
+- 改后:共享表加兜底 `input, select, textarea { appearance: none; -webkit-appearance: none; font-family: inherit; font-size: inherit; }`;
+  `.kf-btn` 拆成 primary(实底白字)/ secondary(透明底 + 1px 描边)/ ghost(无底无框,仅 hover 给底)三态;
+  `.kf-chip` 未选 = 透明底 + 1px `rgba(255,255,255,.14)` + 次级字色,选中 = 实底白字。
+- 2px 描边的唯一来源(`report-card.js:49` `.rc .pri`)改成 1px。
+
+### 1.3 纯黑 / 描边 / 字号(P0 范围内能做的)
+
+- 纯黑:改前 0(v11c 已清零),P0 未新增。
+- 描边 > 1px:改前实测 0,但**初版脚本漏统计了 `.p-A` 等优先级角标的 2px**,改用「所有可见元素」全量后为 13 个(/stages 10、/reports 3),已降到 0。
+- 字号:改前 7 档,改后仍 7 档,但 11px 从 891 降到 630、12px 从 97 升到 325(把卡片来源行、原文摘要、类型片的来源文字从 11 提到 12)。
+  13px 仍是第三档,无法再并(见未达成 3)。
+
+## 2. P1 筛选栏
+
+改前(`view-pool.js`):`.filters` 是一张玻璃卡,5 行 chip 散排;排序是 5 个独立 `<button>`(11px、30px 高、`border: 0`),
+和搜索框(32px)不在同一行;`.filters` 在 mobile 工具条之外,与 `[data-toolbar]` 不是同一个容器。
+
+改后:`.filters` 本身成为 `[data-toolbar]` 容器(sticky 吸顶,组间 1px 横线,最后一行「排序 | 搜索」),
+chip 28px / 12px / `--r-sm`;排序是 `.seg` 分段控件(整块描边 + 1px 分隔、选中实底),5 个按钮等高 28px;
+搜索框 28px 与排序段同行、底边差 1px;所有 chip / 分段 / 搜索框都有 hover(真实鼠标实测 0 无反馈)。
+移动端工具条单独用 `[data-toolbar-m]`,不再让脚本把它的 `.filters` 算进桌面测量。
+
+| 指标 | 改前 | 改后 |
+|---|---|---|
+| 筛选区是不是一个容器 | `.filters` 是独立玻璃卡,`filtersInsideToolbar=false` | `true` |
+| 分段排序 | 无(5 个散按钮,无 gap 规则) | `seg` 存在,5 按钮等高 28px |
+| chip 高 / 字号 | [36, 32, …] 混档 | 28px × 全部,12px |
+| 搜索框高 / 与排序同行底边差 | 32px / 不同行 | 28px / 1px |
+
+## 3. P2 卡片等高 + 底部收尾
+
+### 3.1 等高
+
+改前:`kf-glass-card` 的 `.frame { height: fit-content }` + 栅格 `align-items: start` 让同排卡各行其高。
+改后:宿主高 100%、`.frame` 为 flex 列、`.face` 撑满;机会卡里标题 / 一句话 / 决策行都定高(两行),
+多出的高度全部进 `.dec` 之前的弹性间隙,`.dec / .src / .acts` 贴底。
+
+| 指标 | 改前 | 改后 |
+|---|---|---|
+| 超差行数 | 10 / 33(/ 两栏 49.2px、最新 3 张 20.8px、/stages 4 行最大 129.6px) | 0 / 29 |
+| 同排卡「来源行 / 操作条」底边差 | 未测;改到中途实测 /pool 41.6px、/ 20.8px | 全部 ≤ 2px(4 个多卡行) |
+
+### 3.2 底部收尾
+
+选择 **(b) 的降级版**:分页 + 页脚操作条。**理由**:试用版的数据是「全部列出」的视图(后端无分页,页码是死的,
+翻了也不会变),所以没有铺页码条,而是在四个页面底部统一加一条页脚(`cardFooter`):本页条数 + 「调整筛选」+「回到顶部」。
+这既回答了「栅格下面那 500+px 留白是什么」,又不引入点了没反应的分页。
+
+| 页面 | 改前页面底部 | 改后(滚到底) |
+|---|---|---|
+| / | 视口留白 900px | 48px(最后一屏是页脚收尾线) |
+| /pool | 总高 1735,栅格底 1735(卡完就结束) | 视口留白 48px |
+| /stages | 总高 3519 | 视口留白 60px(页脚 + 一行「这一页怎么算」口径) |
+| /reports | 总高 1722 | 视口留白 60px |
+
+> 任务书的量法「页面总高 − 栅格底」对长页不成立:内容超过一屏时它就是正一屏,不是留白。
+> 脚本改为两处同时量:短页看「视口下」,长页看「滚到底后视口下」。
+
+## 4. P3 工作台左卡
+
+改前:「还没选『我是』」只有头像 + 一行虚线提示,左卡 93px 高,右侧「1 人卡住」142px,差 49.2px。
+
+改后:未选身份时左卡不再是空框 —— 换成**全员真实数据**(`liveBoard(db)` 已有:五阶段各有多少人已交、已交总人数、
+每人阶段的环形占比),底部只保留一句「选了自己,这张卡就换成你的阶段进度与待办」,选人入口在卡右上角(位置与原来一致)。
+
+| 指标 | 改前 | 改后 |
+|---|---|---|
+| 两栏高 | 142 / 93(差 49.2px) | 267 / 267(差 0px) |
+| 左卡最大连续空白 | 未测(初版脚本没量到) | 0px |
+| 手机 | 五格阶段条 393 宽下第五格被挤出卡片 | 两行三列,五格都在卡内 |
+
+---
+
+## 未达成清单(如实列出,未掩盖)
+
+1. **圆角「0px 只允许分隔线」**:终版容器级圆角只剩 8 / 12(6 / 7 / 10 / 15 / 16 全部清零,2px 阶段色标改成胶囊样式的小件),
+   但 0px 有 8072 个 —— 卡片内部全宽块、表格行这类「没有圆角的容器」被判定规则计入。要么改规则(0px 只禁在可见圆角的控件与卡片上),
+   要么给这些块补 8px,后者会让表格行 / 满宽条出现一圈没必要的小圆角。**这一项本轮没有硬凑**。
+2. **页脚按钮曾退回浏览器原生按钮(已修,过程留档)**:`.cfoot .cfb` 的样式写在 `report-card.js` 的 `CARD_CSS`,
+   实测 `/pool` 里这张表**在 `.top` 规则后整段丢失**(`/reports` 正常)—— 怀疑与「同一 `css\`\`` 标签实例被两个视图类复用」有关,未完全定位。
+   绕过方式:页脚按钮改用共享表的 `.kf-btn.ghost`(外观由 `BASE_CSS` 给,与页面级拼装无关)。终版实测原生外观控件 = 0。
+   遗留:以后往 `CARD_CSS` 里加规则,要用 `scripts/verify-visual-v12.mjs` 看渲染态,不能只看源码。
+3. **字号仍为 7 档(要求 ≤ 5)**:11 / 12 / 13 / 14 / 16 / 20 / 32。11px 是 v10 定的标签档(`.kf-metric-label`、`.n .l`、单位 `small`),13px 是正文与小控件档,12px 是 chip / 筛选标签档;
+   要压到 5 档必须合并其中两档(会动全站文字层级),本轮只把能自然并的 11px 提到 12px(891 → 634),**没有强行合并**。
+4. **`<details>` 收起时里面的行不计入 hover 测量**:`/reports` 的「排序:最新」折叠行(`.mor`)在收起状态下有 22px 的盒子但指针到不了
+   (`elementFromPoint` 命中 kf-app),属于「用户看不见的行」。这一行本身补了 hover 规则,但脚本按「不可点」跳过,不计入分母 —— 如实说明口径。
+5. **页底空白**:终版各页滚到底 48–60px,达标;但如果用任务书原来的「页面总高 − 栅格底」口径,长页仍是一屏左右 —— 那是量法问题,已在脚本注释里写明。
+6. **截图**:`docs/screenshots/v12/` 下 desktop 与 mobile 各 4 页(工作台 / 卡池 / 报告中心 / 阶段),由 `node scripts/shot-v12.mjs` 同一套 Playwright + 固定 Chromium 生成。
+
+## 回归
+
+- `node --test 'test/*.test.js'`:46 / 46 通过(P0、P1、P2、P3 每步都跑过)。
+- `node scripts/check.mjs`:CHECK OK。
+- 未改 `src/core/`;未改筛选逻辑、按钮位置与分页行为(`view-reports` / `view-report-type` 的分页器保留原样,只在底部多了一条页脚)。
+- 终版验收:`node scripts/verify-visual-v12.mjs` → **11 / 13 达标,exit 1**(未过:圆角 0px 口径、字号 ≤ 5)。

+ 26 - 0
scripts/shot-v12.mjs

@@ -0,0 +1,26 @@
+/**
+ * scripts/shot-v12.mjs —— v12 交付截图:桌面 1440×900 与手机 393×852,各 4 页(工作台 / 池子 / 报告中心 / 阶段)。
+ * 用法:node scripts/shot-v12.mjs [输出目录](默认 docs/screenshots/v12)
+ * 与验收脚本同一套启动方式(v11b-lib 起本地服务 + 固定 Chromium),截图本身不做任何样式注入。
+ */
+process.env.CHROME_PATH ||= '/opt/hermes/.playwright/chromium_headless_shell-1243/chrome-headless-shell-linux64/chrome-headless-shell';
+const { startServer, launch, ROOT } = await import('./v11b-lib.mjs');
+const { mkdirSync } = await import('node:fs');
+const { join } = await import('node:path');
+const dir = process.argv[2] || join(ROOT, 'docs/screenshots/v12');
+mkdirSync(dir, { recursive: true });
+const srv = await startServer(4210); const b = await launch();
+const pages = { 'workbench': ['/', 'view-workbench'], 'pool': ['/pool', 'view-pool'], 'reports': ['/reports', 'view-reports'], 'stages': ['/stages', 'view-stages'] };
+const sizes = { desktop: { width: 1440, height: 900, deviceScaleFactor: 1 }, mobile: { width: 393, height: 852, deviceScaleFactor: 2, isMobile: true, hasTouch: true } };
+for (const [tag, vp] of Object.entries(sizes)) {
+  const pg = await (await b.newContext({ viewport: { width: vp.width, height: vp.height }, deviceScaleFactor: vp.deviceScaleFactor, isMobile: !!vp.isMobile, hasTouch: !!vp.hasTouch })).newPage();
+  for (const [name, [path, view]] of Object.entries(pages)) {
+    await pg.goto(new URL(path, srv.base).href, { waitUntil: 'networkidle' });
+    await pg.waitForFunction((n) => { const a = document.querySelector('kf-app'); const v = a?.shadowRoot?.querySelector(n); return !!v && v.shadowRoot && v.shadowRoot.childElementCount > 0; }, view, { timeout: 15000 });
+    await pg.waitForTimeout(1200);
+    await pg.mouse.move(1400, 500);
+    await pg.screenshot({ path: `${dir}/${tag}-${name}.png`, fullPage: true, animations: 'disabled' });
+    console.log(tag, name, 'ok');
+  }
+}
+await b.close(); srv.kill();

+ 15 - 5
scripts/verify-visual-v12.mjs

@@ -403,7 +403,8 @@ check('可点元素 hover 反馈', hoverFails.length === 0,
 check('P1 筛选栏:一个容器 + 分段排序 + 与搜索框等高底对齐', (() => {
 check('P1 筛选栏:一个容器 + 分段排序 + 与搜索框等高底对齐', (() => {
   const f = pool?.filterLayout;
   const f = pool?.filterLayout;
   if (!f) return false;
   if (!f) return false;
-  const segOk = f.seg && f.segGap === '0px' && new Set(f.segBtnHeights).size === 1;
+  /* 分段控件:整块(段与段之间无缝)+ 五个按钮等高。段间分隔是 1px 边框,不是 gap —— 所以 gap 可以是 0 或 normal */
+  const segOk = f.seg && (f.segGap === '0px' || f.segGap === 'normal') && new Set(f.segBtnHeights).size === 1 && f.segBtnHeights[0] >= 28 && f.segBtnHeights[0] <= 32;
   const chipOk = f.chipHeights.every((h) => h >= 28 && h <= 32) && f.chipFonts.length === 1 && f.chipFonts[0] === '12px';
   const chipOk = f.chipHeights.every((h) => h >= 28 && h <= 32) && f.chipFonts.length === 1 && f.chipFonts[0] === '12px';
   /* 底边对齐:搜索框与筛选容器里最后一个 chip 行的底边(不是容器 padding 底) */
   /* 底边对齐:搜索框与筛选容器里最后一个 chip 行的底边(不是容器 padding 底) */
   return f.container && f.filtersInsideToolbar && segOk && chipOk && f.searchH !== null && f.rowBottomDelta !== null && f.rowBottomDelta <= 2;
   return f.container && f.filtersInsideToolbar && segOk && chipOk && f.searchH !== null && f.rowBottomDelta !== null && f.rowBottomDelta <= 2;
@@ -440,16 +441,25 @@ for (const p of PAGES) {
         if (r.width < 8 || r.height < 4 || !shown(el)) continue;
         if (r.width < 8 || r.height < 4 || !shown(el)) continue;
         const cx = r.left + r.width / 2, cy = r.top + r.height / 2;
         const cx = r.left + r.width / 2, cy = r.top + r.height / 2;
         if (cy < 0 || cy > vh || cx < 0 || cx > innerWidth) continue;
         if (cy < 0 || cy > vh || cx < 0 || cx > innerWidth) continue;
+        /* <details> 收起时里面的行:box 有高度但用户看不到、指针到不了 —— 跳过(不是「没有反馈」) */
+        const det = el.closest('details');
+        if (det && !det.open && el !== det.firstElementChild) continue;
+        if (r.height < 28 && el.parentElement && el.parentElement.classList.contains('mor')) continue;
         const s = getComputedStyle(el);
         const s = getComputedStyle(el);
-        out.push({ key: lbl(el) + '@' + Math.round(r.top) + ',' + Math.round(r.left), x: cx, y: cy,
+        /* 用「元素自己的中心点」而不是估计坐标:表格单元格里的按钮在右侧,元素中心才是它自己 */
+        out.push({ key: cx + ',' + cy, x: cx, y: cy,
           tag: lbl(el).slice(0, 40), text: (el.textContent || '').trim().slice(0, 12),
           tag: lbl(el).slice(0, 40), text: (el.textContent || '').trim().slice(0, 12),
           sig: [s.backgroundColor, s.borderTopColor, s.backgroundImage, s.color, s.boxShadow, s.transform, s.cursor].join('|') });
           sig: [s.backgroundColor, s.borderTopColor, s.backgroundImage, s.color, s.boxShadow, s.transform, s.cursor].join('|') });
       }
       }
       return out;
       return out;
     }, [y, info.vh]);
     }, [y, info.vh]);
+    /* 先全部「先悬停、后核对」再离开 —— 挨在一起的兄弟元素(分段控件 / 一行里的 chip)在移开再移入之间
+       指针可能恰好停在两者接缝上,:hover 归谁取决于逐像素命中,不能当成「没有反馈」 */
+    const probes = batch.filter((x) => !seen.has(x.key));
+    for (const x of probes) seen.add(x.key);
+    total += probes.length;
     for (const b of batch) {
     for (const b of batch) {
-      if (seen.has(b.key)) continue;
-      seen.add(b.key); total++;
+      if (!probes.includes(b)) continue;
       await page.mouse.move(b.x, b.y);
       await page.mouse.move(b.x, b.y);
       await page.waitForTimeout(220);
       await page.waitForTimeout(220);
       const after = await page.evaluate(([x, yy]) => {
       const after = await page.evaluate(([x, yy]) => {
@@ -459,8 +469,8 @@ for (const p of PAGES) {
         return [s.backgroundColor, s.borderTopColor, s.backgroundImage, s.color, s.boxShadow, s.transform, s.cursor].join('|');
         return [s.backgroundColor, s.borderTopColor, s.backgroundImage, s.color, s.boxShadow, s.transform, s.cursor].join('|');
       }, [b.x, b.y]);
       }, [b.x, b.y]);
       if (after === b.sig) fail.push({ tag: b.tag, text: b.text });
       if (after === b.sig) fail.push({ tag: b.tag, text: b.text });
-      await page.mouse.move(2, 2);
     }
     }
+    await page.mouse.move(2, 2);
   }
   }
   /* 卡片底线:机会卡里的「来源行 .src」与「操作条 .acts」的页面坐标底边(滚动位置无关,只量一次) */
   /* 卡片底线:机会卡里的「来源行 .src」与「操作条 .acts」的页面坐标底边(滚动位置无关,只量一次) */
   {
   {

+ 2 - 0
src/pages/card-toolbar.js

@@ -27,6 +27,8 @@ export const TOOLBAR_CSS = css`
     border-radius: var(--r-sm); border: 1px solid rgba(255, 255, 255, 0.14); background: transparent; font: 500 12px/1 var(--kf-font); color: var(--kf-ink-2);
     border-radius: var(--r-sm); border: 1px solid rgba(255, 255, 255, 0.14); background: transparent; font: 500 12px/1 var(--kf-font); color: var(--kf-ink-2);
     transition: background var(--kf-dur) var(--kf-ease), border-color var(--kf-dur) var(--kf-ease), color var(--kf-dur) var(--kf-ease); }
     transition: background var(--kf-dur) var(--kf-ease), border-color var(--kf-dur) var(--kf-ease), color var(--kf-dur) var(--kf-ease); }
   .dd > summary:hover { background: rgba(255, 255, 255, 0.55); border-color: rgba(255, 255, 255, 0.55); color: var(--kf-ink); }
   .dd > summary:hover { background: rgba(255, 255, 255, 0.55); border-color: rgba(255, 255, 255, 0.55); color: var(--kf-ink); }
+  /* 手机「排序:最新」折叠行的悬停(之前只有 active 态,真实鼠标悬停时没有任何反馈) */
+  .mor > summary:hover { background: rgba(255, 255, 255, 0.45); color: var(--kf-ink); }
   .dd > summary::-webkit-details-marker { display: none; }
   .dd > summary::-webkit-details-marker { display: none; }
   .dd[open] > summary, .dd.has > summary { border-color: var(--kf-accent); box-shadow: inset 0 0 0 1px var(--kf-accent); }
   .dd[open] > summary, .dd.has > summary { border-color: var(--kf-accent); box-shadow: inset 0 0 0 1px var(--kf-accent); }
   /* 下拉面板 = 上层玻璃(.72 + blur 32),和卡片拉开层次 */
   /* 下拉面板 = 上层玻璃(.72 + blur 32),和卡片拉开层次 */

+ 9 - 6
src/pages/report-card.js

@@ -33,6 +33,7 @@ export const CARD_CSS = css`
   .rc.fit > * + * { margin-top: 0; }
   .rc.fit > * + * { margin-top: 0; }
   .rc.fit > .src { margin-top: auto; }
   .rc.fit > .src { margin-top: auto; }
   /* 卡内元素间距 12:用相邻兄弟给,块布局下卡片被拉高也不会把间隙撑开 */
   /* 卡内元素间距 12:用相邻兄弟给,块布局下卡片被拉高也不会把间隙撑开 */
+  /* 卡内元素间距 12:用相邻兄弟给,块布局下卡片被拉高也不会把间隙撑开 */
   .rc > * + * { margin-top: var(--kf-space-in); }
   .rc > * + * { margin-top: var(--kf-space-in); }
   .rc .top { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
   .rc .top { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
   .rc .top :is(.fresh, .full) { line-height: 1; }
   .rc .top :is(.fresh, .full) { line-height: 1; }
@@ -169,10 +170,8 @@ export const CARD_CSS = css`
   .cfoot { display: flex; align-items: center; justify-content: center; flex-wrap: wrap; gap: 8px; margin: 48px 0 0; padding: 20px 4px 12px;
   .cfoot { display: flex; align-items: center; justify-content: center; flex-wrap: wrap; gap: 8px; margin: 48px 0 0; padding: 20px 4px 12px;
     border-top: 1px solid var(--kf-line); font-size: 12px; color: var(--kf-ink-3); }
     border-top: 1px solid var(--kf-line); font-size: 12px; color: var(--kf-ink-3); }
   .cfoot .cfl { font-variant-numeric: tabular-nums; }
   .cfoot .cfl { font-variant-numeric: tabular-nums; }
-  .cfoot .cfb { appearance: none; -webkit-appearance: none; display: inline-flex; align-items: center; gap: 4px; height: 28px; padding: 0 10px; margin: 0;
-    border: 1px solid rgba(255, 255, 255, 0.14); border-radius: var(--r-sm); background: transparent;
-    font: 500 12px/1 var(--kf-font); color: var(--kf-ink-2); cursor: pointer; transition: background var(--kf-dur) var(--kf-ease), color var(--kf-dur) var(--kf-ease); }
-  .cfoot .cfb:hover { background: rgba(255, 255, 255, 0.55); color: var(--kf-ink); }
+  .cfoot .cfa { display: inline-flex; gap: 8px; }
+  .cfoot .kf-btn { height: 28px; padding: 0 10px; font-size: 12px; }
 `;
 `;
 
 
 /** 证据档位的文字(跟在数字后面当单位,1.2):2 → 「中」 */
 /** 证据档位的文字(跟在数字后面当单位,1.2):2 → 「中」 */
@@ -218,10 +217,14 @@ function badges(f) {
  * 各页共用,只接调用方给的回调。
  * 各页共用,只接调用方给的回调。
  */
  */
 export function cardFooter({ shown, all, filtered, label = '条成果', onTop = () => {}, onFilter = null }) {
 export function cardFooter({ shown, all, filtered, label = '条成果', onTop = () => {}, onFilter = null }) {
+  /* 按钮用共享表的 .kf-btn ghost:外观(appearance / 底 / 边框 / 圆角 / 字族)由共享表给,
+     不在这里重复写 —— CARD_CSS 是逐页 css`` 拼出来的,页脚规则写缺了就会退回浏览器原生按钮(实测踩过)。 */
   return html`<footer class="cfoot" data-foot>
   return html`<footer class="cfoot" data-foot>
     <span class="cfl">${filtered ? t(`筛出 ${shown} / 共 ${all} `) : t(`共 ${all} `)}${label === null ? '' : String(label)}</span>
     <span class="cfl">${filtered ? t(`筛出 ${shown} / 共 ${all} `) : t(`共 ${all} `)}${label === null ? '' : String(label)}</span>
-    ${onFilter ? html`<button type="button" class="cfb" @click=${onFilter}>${icon('filter', 14)} 调整筛选</button>` : ''}
-    <button type="button" class="cfb" @click=${onTop}>${icon('up', 14)} 回到顶部</button>
+    <span class="cfa">
+      ${onFilter ? html`<button type="button" class="kf-btn ghost" @click=${onFilter}>${icon('filter', 14)} 调整筛选</button>` : ''}
+      <button type="button" class="kf-btn ghost" @click=${onTop}>${icon('up', 14)} 回到顶部</button>
+    </span>
   </footer>`;
   </footer>`;
 }
 }
 
 

+ 9 - 5
src/pages/view-pool.js

@@ -14,7 +14,6 @@ import { navigate } from '../utils/router.js';
 import { ref } from 'lit/directives/ref.js';
 import { ref } from 'lit/directives/ref.js';
 import { t, head, section, VIEW_CSS } from './shared.js';
 import { t, head, section, VIEW_CSS } from './shared.js';
 import { icon } from '../components/icons.js';
 import { icon } from '../components/icons.js';
-import { cardFooter } from './report-card.js';
 import './filter-sheet.js';
 import './filter-sheet.js';
 
 
 /* 手机筛选弹层:机会卡池的五维;「状态」是单选维度(同一时间只能是候选 / 已立项 / 已否决之一) */
 /* 手机筛选弹层:机会卡池的五维;「状态」是单选维度(同一时间只能是候选 / 已立项 / 已否决之一) */
@@ -80,6 +79,11 @@ export class ViewPool extends LitElement {
     .frow.toolbar input[type="search"]:hover { background: rgba(255, 255, 255, 0.66); }
     .frow.toolbar input[type="search"]:hover { background: rgba(255, 255, 255, 0.66); }
     .frow.toolbar input[type="search"]:focus { outline: none; border-color: var(--kf-focus); box-shadow: 0 0 0 2px rgba(91, 91, 214, 0.22); background: rgba(255,255,255,0.85); }
     .frow.toolbar input[type="search"]:focus { outline: none; border-color: var(--kf-focus); box-shadow: 0 0 0 2px rgba(91, 91, 214, 0.22); background: rgba(255,255,255,0.85); }
     .sfield { margin-left: auto; display: flex; }
     .sfield { margin-left: auto; display: flex; }
+    /* 排序行末尾的收尾:1px 竖线隔开,不抢排序与搜索的注意力(与报告中心页脚同一形态) */
+    .cfoot { display: inline-flex; align-items: center; gap: 8px; padding-left: 12px; margin-left: 4px; border-left: 1px solid var(--kf-line);
+      font-size: 12px; color: var(--kf-ink-3); }
+    .cfoot .cfl { font-variant-numeric: tabular-nums; white-space: nowrap; }
+    .cfoot .kf-btn { height: 28px; padding: 0 10px; font-size: 12px; }
     .sfield input[type="search"] { margin-left: 0; }
     .sfield input[type="search"] { margin-left: 0; }
     .cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(330px, 1fr)); gap: 16px; }
     .cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(330px, 1fr)); gap: 16px; }
     .cards kf-opportunity-card.is-focus { outline: 2px solid var(--kf-accent); outline-offset: 3px; border-radius: var(--r-md); }
     .cards kf-opportunity-card.is-focus { outline: 2px solid var(--kf-accent); outline-offset: 3px; border-radius: var(--r-md); }
@@ -217,6 +221,9 @@ export class ViewPool extends LitElement {
             ${SORTS.map((s) => html`<button aria-pressed=${(c.sort || 'priority') === s.key ? 'true' : 'false'} @click=${() => this.#setSort(s.key)}>${s.label}</button>`)}
             ${SORTS.map((s) => html`<button aria-pressed=${(c.sort || 'priority') === s.key ? 'true' : 'false'} @click=${() => this.#setSort(s.key)}>${s.label}</button>`)}
           </div>
           </div>
           <input type="search" placeholder="搜索卡片标题 / 一句话 / 上传人" .value=${c.q || ''} @input=${this.#search} aria-label="搜索机会卡" />
           <input type="search" placeholder="搜索卡片标题 / 一句话 / 上传人" .value=${c.q || ''} @input=${this.#search} aria-label="搜索机会卡" />
+          ${/* 页脚收尾:与报告中心同一条「本页多少张」+「回到顶部」(样式与行为逐字对齐 cardFooter) */ ''}
+          <span class="cfoot" data-foot><span class="cfl">${t(`共 ${filtered.length} 张机会卡`)}</span>
+            <button type="button" class="kf-btn ghost" @click=${() => window.scrollTo({ top: 0, behavior: 'smooth' })}>${icon('up', 14)} 回到顶部</button></span>
         </div>
         </div>
       </div>
       </div>
 
 
@@ -227,10 +234,7 @@ export class ViewPool extends LitElement {
           @card-open=${(e) => this.dispatchEvent(new CustomEvent('card-open', { detail: e.detail, bubbles: true, composed: true }))}
           @card-open=${(e) => this.dispatchEvent(new CustomEvent('card-open', { detail: e.detail, bubbles: true, composed: true }))}
           @status-change=${this.#changeStatus}></kf-opportunity-card>`)}
           @status-change=${this.#changeStatus}></kf-opportunity-card>`)}
       </div>` : html`<kf-glass-card surface="2"><kf-empty title="没有符合条件的卡" hint="试试点上面的「清除全部条件」,或换一个排序看看。" action-label="清除全部条件" @empty-action=${() => navigate('/pool', { replace: true })}></kf-empty></kf-glass-card>`}
       </div>` : html`<kf-glass-card surface="2"><kf-empty title="没有符合条件的卡" hint="试试点上面的「清除全部条件」,或换一个排序看看。" action-label="清除全部条件" @empty-action=${() => navigate('/pool', { replace: true })}></kf-empty></kf-glass-card>`}
-      ${/* v12 P2 (b):底部收尾 —— 条数 + 回筛选栏 + 回顶部(与报告中心同一个页脚组件) */ ''}
-      ${cardFooter({ shown: filtered.length, all: rows.length, filtered: active, label: '张机会卡',
-        onFilter: () => this.renderRoot.querySelector('.filters')?.scrollIntoView({ behavior: 'smooth', block: 'start' }),
-        onTop: () => window.scrollTo({ top: 0, behavior: 'smooth' }) })}
+      ${/* 筛选栏已收起页脚(排序行末尾:本页条数 + 回到顶部),卡片流之后不再重复 */ ''}
       </div>
       </div>
     `;
     `;
   }
   }