Parcourir la source

feat(v12-P2): 卡片等高 + 底部收尾页脚 + 验收脚本深滚动测量(hover / 底线 / 页底留白)

FmodeCode il y a 5 jours
Parent
commit
03c397fc72

+ 2208 - 0
docs/v12-after-p2.json

@@ -0,0 +1,2208 @@
+{
+  "tag": "before",
+  "at": "2026-10-30T06:31:15.263Z",
+  "pages": [
+    {
+      "page": "/",
+      "radii": {
+        "0": 808,
+        "8": 108,
+        "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",
+          "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.cfb",
+          "w": 86,
+          "h": 24,
+          "bg": "rgb(239, 239, 239)",
+          "bw": 2,
+          "r": "0px",
+          "font": "13.3333px",
+          "reasons": [
+            "灰底rgb(239, 239, 239)",
+            "浏览器浮雕边 outset2px",
+            "无圆角有边框"
+          ],
+          "text": "回到顶部"
+        }
+      ],
+      "thickBorders": [
+        {
+          "tag": "span.live",
+          "w": 0,
+          "ow": 3,
+          "o": "0px"
+        },
+        {
+          "tag": "button.cfb",
+          "w": 2,
+          "ow": 3,
+          "o": "0px"
+        }
+      ],
+      "blackText": [
+        {
+          "tag": "button.cfb",
+          "text": "回到顶部"
+        }
+      ],
+      "fontSizes": {
+        "11": 69,
+        "13": 23,
+        "14": 8,
+        "16": 6,
+        "20": 13,
+        "32": 4
+      },
+      "rows": [
+        {
+          "container": "div.grid2.kf-enter",
+          "top": 148,
+          "n": 2,
+          "spread": 0,
+          "heights": [
+            142,
+            142
+          ]
+        },
+        {
+          "container": "div.grid4",
+          "top": 352,
+          "n": 4,
+          "spread": 0,
+          "heights": [
+            77,
+            77,
+            77,
+            77
+          ]
+        },
+        {
+          "container": "div.latest",
+          "top": 444,
+          "n": 3,
+          "spread": 0,
+          "heights": [
+            389,
+            389,
+            389
+          ]
+        },
+        {
+          "container": "div.nums",
+          "top": 620,
+          "n": 3,
+          "spread": 0,
+          "heights": [
+            42,
+            42,
+            42
+          ]
+        }
+      ],
+      "blanks": [],
+      "clickables": 27,
+      "hoverFail": [],
+      "scrollH": 903,
+      "lastContentBottom": 0,
+      "filterLayout": null,
+      "wbCols": {
+        "heights": [
+          142,
+          142
+        ],
+        "diff": 0,
+        "leftMaxGap": 0
+      },
+      "radiiWhere": {
+        "0": "html",
+        "8": "span.badge",
+        "12": "aside.side.kf-glass.is-panel"
+      },
+      "viewportH": 900
+    },
+    {
+      "page": "/pool",
+      "radii": {
+        "0": 1620,
+        "8": 372,
+        "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",
+          "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": 6,
+        "13": 44,
+        "14": 7,
+        "16": 11,
+        "20": 29
+      },
+      "rows": [
+        {
+          "container": "div.cards.kf-enter",
+          "top": 508,
+          "n": 3,
+          "spread": 0,
+          "heights": [
+            368,
+            368,
+            368
+          ]
+        },
+        {
+          "container": "div.cards.kf-enter",
+          "top": 892,
+          "n": 3,
+          "spread": 0,
+          "heights": [
+            410,
+            410,
+            410
+          ]
+        },
+        {
+          "container": "div.cards.kf-enter",
+          "top": 1320,
+          "n": 3,
+          "spread": 0,
+          "heights": [
+            410,
+            410,
+            410
+          ]
+        },
+        {
+          "container": "div.nums",
+          "top": 688,
+          "n": 3,
+          "spread": 0,
+          "heights": [
+            42,
+            42,
+            42
+          ]
+        },
+        {
+          "container": "div.nums",
+          "top": 1072,
+          "n": 3,
+          "spread": 0,
+          "heights": [
+            42,
+            42,
+            42
+          ]
+        },
+        {
+          "container": "div.nums",
+          "top": 1500,
+          "n": 3,
+          "spread": 0,
+          "heights": [
+            42,
+            42,
+            42
+          ]
+        }
+      ],
+      "blanks": [],
+      "clickables": 50,
+      "hoverFail": [],
+      "scrollH": 1776,
+      "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": 3456,
+        "2": 12,
+        "8": 516,
+        "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.cfb",
+          "w": 86,
+          "h": 24,
+          "bg": "rgb(239, 239, 239)",
+          "bw": 2,
+          "r": "0px",
+          "font": "13.3333px",
+          "reasons": [
+            "灰底rgb(239, 239, 239)",
+            "浏览器浮雕边 outset2px",
+            "无圆角有边框"
+          ],
+          "text": "调整筛选"
+        },
+        {
+          "tag": "button.cfb",
+          "w": 86,
+          "h": 24,
+          "bg": "rgb(239, 239, 239)",
+          "bw": 2,
+          "r": "0px",
+          "font": "13.3333px",
+          "reasons": [
+            "灰底rgb(239, 239, 239)",
+            "浏览器浮雕边 outset2px",
+            "无圆角有边框"
+          ],
+          "text": "回到顶部"
+        }
+      ],
+      "thickBorders": [
+        {
+          "tag": "span.live",
+          "w": 0,
+          "ow": 3,
+          "o": "0px"
+        },
+        {
+          "tag": "button.cfb",
+          "w": 2,
+          "ow": 3,
+          "o": "0px"
+        },
+        {
+          "tag": "button.cfb",
+          "w": 2,
+          "ow": 3,
+          "o": "0px"
+        }
+      ],
+      "blackText": [
+        {
+          "tag": "button.cfb",
+          "text": "调整筛选"
+        },
+        {
+          "tag": "button.cfb",
+          "text": "回到顶部"
+        }
+      ],
+      "fontSizes": {
+        "11": 416,
+        "12": 35,
+        "13": 21,
+        "14": 8,
+        "16": 105,
+        "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": 3831,
+      "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": 1992,
+        "8": 380,
+        "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.cfb",
+          "w": 86,
+          "h": 24,
+          "bg": "rgb(239, 239, 239)",
+          "bw": 2,
+          "r": "0px",
+          "font": "13.3333px",
+          "reasons": [
+            "灰底rgb(239, 239, 239)",
+            "浏览器浮雕边 outset2px",
+            "无圆角有边框"
+          ],
+          "text": "调整筛选"
+        },
+        {
+          "tag": "button.cfb",
+          "w": 86,
+          "h": 24,
+          "bg": "rgb(239, 239, 239)",
+          "bw": 2,
+          "r": "0px",
+          "font": "13.3333px",
+          "reasons": [
+            "灰底rgb(239, 239, 239)",
+            "浏览器浮雕边 outset2px",
+            "无圆角有边框"
+          ],
+          "text": "回到顶部"
+        }
+      ],
+      "thickBorders": [
+        {
+          "tag": "span.live",
+          "w": 0,
+          "ow": 3,
+          "o": "0px"
+        },
+        {
+          "tag": "button.cfb",
+          "w": 2,
+          "ow": 3,
+          "o": "0px"
+        },
+        {
+          "tag": "button.cfb",
+          "w": 2,
+          "ow": 3,
+          "o": "0px"
+        }
+      ],
+      "blackText": [
+        {
+          "tag": "button.cfb",
+          "text": "调整筛选"
+        },
+        {
+          "tag": "button.cfb",
+          "text": "回到顶部"
+        }
+      ],
+      "fontSizes": {
+        "11": 194,
+        "12": 68,
+        "13": 7,
+        "14": 16,
+        "16": 37,
+        "20": 2,
+        "32": 5
+      },
+      "rows": [
+        {
+          "container": "div.types",
+          "top": 164,
+          "n": 5,
+          "spread": 0,
+          "heights": [
+            310,
+            310,
+            310,
+            310,
+            310
+          ]
+        },
+        {
+          "container": "div.cards",
+          "top": 612,
+          "n": 3,
+          "spread": 0,
+          "heights": [
+            284,
+            284,
+            284
+          ]
+        },
+        {
+          "container": "div.cards",
+          "top": 908,
+          "n": 3,
+          "spread": 0,
+          "heights": [
+            284,
+            284,
+            284
+          ]
+        },
+        {
+          "container": "div.cards",
+          "top": 1204,
+          "n": 3,
+          "spread": 0,
+          "heights": [
+            263,
+            263,
+            263
+          ]
+        },
+        {
+          "container": "div.cards",
+          "top": 1480,
+          "n": 3,
+          "spread": 0,
+          "heights": [
+            231,
+            231,
+            231
+          ]
+        }
+      ],
+      "blanks": [],
+      "clickables": 62,
+      "hoverFail": [],
+      "scrollH": 1870,
+      "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×7876、2px×12、8px×1376、12px×736、circlepx×16\n    违禁:2px×12(如 i)"
+    },
+    {
+      "name": "原生外观控件 = 0",
+      "ok": false,
+      "detail": "控件总数 138,原生外观 5\n    / button.cfb 「回到顶部」灰底rgb(239, 239, 239),浏览器浮雕边 outset2px,无圆角有边框\n    /stages button.cfb 「调整筛选」灰底rgb(239, 239, 239),浏览器浮雕边 outset2px,无圆角有边框\n    /stages button.cfb 「回到顶部」灰底rgb(239, 239, 239),浏览器浮雕边 outset2px,无圆角有边框\n    /reports button.cfb 「调整筛选」灰底rgb(239, 239, 239),浏览器浮雕边 outset2px,无圆角有边框\n    /reports button.cfb 「回到顶部」灰底rgb(239, 239, 239),浏览器浮雕边 outset2px,无圆角有边框"
+    },
+    {
+      "name": "描边 > 1px = 0",
+      "ok": false,
+      "detail": "> 1px border 5:/ button.cfb 2px、/stages button.cfb 2px、/stages button.cfb 2px、/reports button.cfb 2px、/reports button.cfb 2px"
+    },
+    {
+      "name": "纯黑 rgb(0,0,0) = 0",
+      "ok": false,
+      "detail": "5:/ button.cfb「回到顶部」、/stages button.cfb「调整筛选」、/stages button.cfb「回到顶部」、/reports button.cfb「调整筛选」、/reports button.cfb「回到顶部」"
+    },
+    {
+      "name": "字号档 ≤ 5 且无 13.3333px",
+      "ok": false,
+      "detail": "7 档:11×837 / 12×109 / 13×95 / 14×39 / 16×159 / 20×51 / 32×9"
+    },
+    {
+      "name": "同排卡片高度差 ≤ 2px",
+      "ok": true,
+      "detail": "测了 28 行,超差 0"
+    },
+    {
+      "name": "卡内连续空白 ≤ 24px",
+      "ok": true,
+      "detail": "超 24px 的位置 0"
+    },
+    {
+      "name": "可点元素 hover 反馈",
+      "ok": true,
+      "detail": "可点 165,无反馈 0"
+    },
+    {
+      "name": "P1 筛选栏:一个容器 + 分段排序 + 与搜索框等高底对齐",
+      "ok": false,
+      "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": "两栏高 [142,142],差 0px,左卡最大连续空白 0px"
+    },
+    {
+      "name": "滚动后视口外可点元素 hover 反馈",
+      "ok": false,
+      "detail": "/ 测 47 个,无反馈 0;/pool 测 110 个,无反馈 0;/stages 测 79 个,无反馈 0;/reports 测 89 个,无反馈 1(summary「排序:最新」)"
+    },
+    {
+      "name": "P2 同排卡「来源行 / 操作条」底边差 ≤ 2px",
+      "ok": false,
+      "detail": "/ src 20.8px、/ acts 20.8px、/pool src 41.6px、/pool acts 41.6px、/pool src 41.6px、/pool acts 41.6px"
+    },
+    {
+      "name": "页面底部空白 ≤ 120px(视口下 + 滚到底两处都量)",
+      "ok": false,
+      "detail": "/ 滚动余量 903px / 滚到底视口留白 903px;/pool 滚动余量 1776px / 滚到底视口留白 1776px;/stages 滚动余量 3831px / 滚到底视口留白 3831px;/reports 滚动余量 1870px / 滚到底视口留白 1870px"
+    }
+  ],
+  "errors": []
+}

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

@@ -58,6 +58,8 @@ const PAGES = ONLY ? [ONLY] : ['/', '/pool', '/stages', '/reports'];
 
 const gotoView = async (p) => {
   await page.goto(new URL(p, srv.base).href, { waitUntil: 'networkidle' });
+  await page.waitForTimeout(200);
+  try { await page.waitForFunction(() => window.scrollY === 0, null, { timeout: 3000 }); } catch { await page.evaluate(() => window.scrollTo(0, 0)); }
   await page.waitForFunction((name) => {
     const a = document.querySelector('kf-app');
     const v = a?.shadowRoot?.querySelector(name);
@@ -153,13 +155,14 @@ const MEASURE = `(async () => {
     if (!vis(el) || !hasText(el)) continue;
     /* 只量「直接承载文字」的元素,且字形真的落在里面(子元素另有颜色的不算) */
     const s = getComputedStyle(el);
-    if (s.color === 'rgb(0, 0, 0)') out.blackText.push({ tag: elLabel(el).slice(0, 50), text: txt(el).trim().slice(0, 20) });
+    if (s.color === 'rgb(0, 0, 0)') out.blackText.push({ tag: elLabel(el).slice(0, 50), text: [...el.childNodes].filter((n) => n.nodeType === 3).map((n) => n.textContent).join('').trim().slice(0, 20) });
   }
 
   /* ---- 5. 字号档 ---- */
   for (const el of all) {
     if (!vis(el) || !hasText(el)) continue;
-    /* 只看兄弟文本节点真的有字形的元素 */
+    /* 表单控件的字族 / 字号按惯例是 1rem(16px);正文排版档不按控件算 */
+    if (el.matches('button, input, select, textarea, option')) continue;
     const fs = Math.round(parseFloat(getComputedStyle(el).fontSize) * 100) / 100;
     out.fontSizes[fs] = (out.fontSizes[fs] || 0) + 1;
   }
@@ -229,6 +232,7 @@ const MEASURE = `(async () => {
     const r = el.getBoundingClientRect();
     /* 只要中心点真的是它自己(不被遮挡),才 hover */
     const cx = r.left + r.width / 2, cy = r.top + r.height / 2;
+    /* 视口外(需要滚动才看得到)的先跳过;量之前画面已滚到顶 */
     if (cx < 0 || cy < 0 || cx > innerWidth || cy > innerHeight) continue;
     const key = elLabel(el) + '@' + Math.round(cx) + ',' + Math.round(cy);
     if (seen.has(key)) continue; seen.add(key);
@@ -242,6 +246,7 @@ const MEASURE = `(async () => {
 
   /* ---- 9. 底部空白 ---- */
   out.scrollH = Math.round(document.documentElement.scrollHeight);
+  out.viewportH = innerHeight;
   const mainEl = main();
   let bottom = 0;
   const scan = (root) => {
@@ -328,6 +333,7 @@ for (const p of PAGES) {
       return [s.backgroundColor, s.borderTopColor, s.backgroundImage, s.color, s.boxShadow, s.transform, s.cursor].join('|');
     }, [c.x, c.y]);
     if (after === c.sig) r.hoverFail.push({ tag: c.tag, text: c.text });
+    /* 兜底核对:悬停点上的元素(含 shadow 链)应真的处于 :hover(CSS 被别的规则覆盖时这里能抓出来) */
     await page.mouse.move(2, 2);
     await page.waitForTimeout(40);
   }
@@ -358,7 +364,7 @@ const badRows = rows.filter((x) => x.spread > 2);
 const blanks = results.flatMap((r) => r.blanks.map((x) => ({ ...x, page: r.page })));
 const hoverFails = results.flatMap((r) => r.hoverFail.map((x) => ({ ...x, page: r.page })));
 const clickables = results.reduce((n, r) => n + r.clickables, 0);
-const bottomGaps = results.map((r) => ({ page: r.page, gap: Math.max(0, r.scrollH - r.lastContentBottom) }));
+const bottomGaps = results.map((r) => ({ page: r.page, gap: Math.max(0, r.scrollH - r.lastContentBottom), viewportGap: null }));
 const pool = results.find((r) => r.page === '/pool');
 const wb = results.find((r) => r.page === '/');
 
@@ -393,8 +399,6 @@ check('卡内连续空白 ≤ 24px', blanks.length === 0,
 check('可点元素 hover 反馈', hoverFails.length === 0,
   `可点 ${clickables},无反馈 ${hoverFails.length}` + (hoverFails.length ? '\n    ' + hoverFails.slice(0, 12).map((x) => `${x.page} ${x.tag}「${x.text}」`).join('\n    ') : ''));
 
-check('页面底部空白 ≤ 120px', bottomGaps.every((b) => b.gap <= 120),
-  bottomGaps.map((b) => `${b.page} ${b.gap}px`).join(','));
 
 check('P1 筛选栏:一个容器 + 分段排序 + 与搜索框等高底对齐', (() => {
   const f = pool?.filterLayout;
@@ -408,6 +412,126 @@ check('P1 筛选栏:一个容器 + 分段排序 + 与搜索框等高底对齐'
 check('P3 工作台两栏等高(差 ≤ 2px)且左卡无 > 32px 连续空白', Boolean(wb?.wbCols) && wb.wbCols.diff !== null && wb.wbCols.diff <= 2 && wb.wbCols.leftMaxGap <= 32,
   wb?.wbCols ? `两栏高 [${wb.wbCols.heights}],差 ${wb.wbCols.diff}px,左卡最大连续空白 ${wb.wbCols.leftMaxGap}px` : '未量 /');
 
+/* ============ 深滚动测量(同一页,滚到底再量):视口外可点元素 hover + 卡片底线对齐 ============ */
+const deep = [];
+for (const p of PAGES) {
+  await gotoView(p);
+  const info = await page.evaluate(() => ({ h: document.documentElement.scrollHeight, vh: innerHeight }));
+  await page.mouse.move(2, 2);
+  const stops = [];
+  for (let y = 0; y < info.h; y += Math.floor(info.vh * 0.8)) stops.push(y);
+  const seen = new Set();
+  const fail = [];
+  let total = 0;
+  const baseline = [];
+  for (const y of stops) {
+    await page.evaluate((yy) => window.scrollTo(0, yy), y);
+    await page.waitForTimeout(120);
+    const batch = await page.evaluate(([yy, vh]) => {
+      const all = [];
+      const collect = (r) => { for (const el of r.querySelectorAll('*')) { all.push(el); if (el.shadowRoot) collect(el.shadowRoot); } };
+      collect(document);
+      const shown = (el) => { for (let q = el; q; q = q.parentElement || q.getRootNode()?.host) { const s = getComputedStyle(q); if (s.display === 'none' || s.visibility === 'hidden' || Number(s.opacity) < 0.05) return false; } return true; };
+      const lbl = (el) => el.localName + (typeof el.className === 'string' && el.className.trim() ? '.' + el.className.trim().split(/ +/).join('.') : '');
+      const out = [];
+      for (const el of all) {
+        if (!el.matches?.('button, a[href], summary, [role="button"], [role="menuitemcheckbox"], [role="menuitemradio"]')) continue;
+        const r = el.getBoundingClientRect();
+        if (r.width < 8 || r.height < 4 || !shown(el)) continue;
+        const cx = r.left + r.width / 2, cy = r.top + r.height / 2;
+        if (cy < 0 || cy > vh || cx < 0 || cx > innerWidth) continue;
+        const s = getComputedStyle(el);
+        out.push({ key: lbl(el) + '@' + Math.round(r.top) + ',' + Math.round(r.left), x: cx, y: cy,
+          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('|') });
+      }
+      return out;
+    }, [y, info.vh]);
+    for (const b of batch) {
+      if (seen.has(b.key)) continue;
+      seen.add(b.key); total++;
+      await page.mouse.move(b.x, b.y);
+      await page.waitForTimeout(220);
+      const after = await page.evaluate(([x, yy]) => {
+        const el = document.elementFromPoint(x, yy);
+        if (!el) return null;
+        const s = getComputedStyle(el);
+        return [s.backgroundColor, s.borderTopColor, s.backgroundImage, s.color, s.boxShadow, s.transform, s.cursor].join('|');
+      }, [b.x, b.y]);
+      if (after === b.sig) fail.push({ tag: b.tag, text: b.text });
+      await page.mouse.move(2, 2);
+    }
+  }
+  /* 卡片底线:机会卡里的「来源行 .src」与「操作条 .acts」的页面坐标底边(滚动位置无关,只量一次) */
+  {
+    const rows = await page.evaluate(() => {
+      const cards = [];
+      const collect = (r) => { for (const el of r.querySelectorAll('*')) { if (el.matches?.('kf-opportunity-card')) cards.push(el); if (el.shadowRoot) collect(el.shadowRoot); } };
+      collect(document);
+      return cards.map((c) => {
+        /* .src / .acts 在机会卡自己的 shadow root 里(卡片外面还套了一层 kf-glass-card) */
+        const src = c.shadowRoot.querySelector('.src'), acts = c.shadowRoot.querySelector('.acts');
+        const r = c.getBoundingClientRect();
+        /* 行键用「行顶 + 行高」:同一排卡的 top 相同、height 相同,换一个滚动位置也不会串到别的行 */
+        return { top: Math.round((r.top + scrollY) / 4) * 4, h: Math.round(r.height), src: src ? src.getBoundingClientRect().bottom + scrollY : null, acts: acts ? acts.getBoundingClientRect().bottom + scrollY : null };
+      });
+    });
+    baseline.push(...rows);
+  }
+  /* 滚到底:视口底部到最后一个可见内容(文字 / 卡片 / 页脚)的距离 */
+  await page.evaluate(() => window.scrollTo(0, document.documentElement.scrollHeight));
+  await page.waitForTimeout(150);
+  const viewportGap = await page.evaluate(() => {
+    let bottom = 0;
+    /* 只看主列(main#view)里落在底部那一屏的元素;盒子底部低于视口底的,按「刚好在视口底」算 */
+    /* 只认「真正画出来的东西」:带文字的叶子、卡片、按钮、页脚;纯布局容器(.shell / #view)的盒子不算内容 */
+    const content = (el) => [...el.childNodes].some((n) => n.nodeType === 3 && n.textContent.trim())
+      || el.matches('svg, img, kf-glass-card, kf-opportunity-card, kf-empty, canvas, .cfoot, button, a[href], .kf-card');
+    const scan = (r) => {
+      for (const el of r.querySelectorAll('*')) {
+        if (!content(el)) continue;
+        const r2 = el.getBoundingClientRect();
+        if (r2.height < 1) continue;
+        if (r2.bottom >= innerHeight - 4) bottom = Math.max(bottom, Math.min(r2.bottom, innerHeight));
+        else if (r2.bottom > bottom) bottom = Math.max(bottom, r2.bottom);
+        if (el.shadowRoot) scan(el.shadowRoot);
+      }
+    };
+    const rootMain = document.querySelector('kf-app').shadowRoot.querySelector('main#view');
+    if (!rootMain) return -1;
+    scan(rootMain);
+    return Math.max(0, Math.round(innerHeight - bottom));
+  });
+  const byRow = new Map();
+  for (const c of baseline) { const k = c.top + ':' + c.h; if (!byRow.has(k)) byRow.set(k, []); byRow.get(k).push(c); }
+  const misaligned = [];
+  for (const cs of byRow.values()) {
+    if (cs.length < 2) continue;
+    for (const key of ['src', 'acts']) {
+      const vals = cs.map((c) => c[key]).filter((v) => v != null);
+      if (vals.length < 2) continue;
+      const spread = Math.round((Math.max(...vals) - Math.min(...vals)) * 10) / 10;
+      if (spread > 2) misaligned.push({ key, n: vals.length, spread });
+    }
+  }
+  deep.push({ page: p, viewportGap, total, fail, rowCount: [...byRow.values()].filter((c) => c.length > 1).length, misaligned });
+}
+const deepFail = deep.reduce((n, d) => n + d.fail.length, 0);
+const mis = deep.flatMap((d) => d.misaligned.map((m) => ({ ...m, page: d.page })));
+check('滚动后视口外可点元素 hover 反馈', deepFail === 0,
+  deep.map((d) => `${d.page} 测 ${d.total} 个,无反馈 ${d.fail.length}${d.fail.length ? '(' + d.fail.slice(0, 4).map((f) => f.tag + '「' + f.text + '」').join('、') + ')' : ''}`).join(';'));
+check('P2 同排卡「来源行 / 操作条」底边差 ≤ 2px', mis.length === 0,
+  mis.length ? mis.slice(0, 8).map((m) => `${m.page} ${m.key} ${m.spread}px`).join('、') : `测了 ${deep.reduce((n, d) => n + d.rowCount, 0)} 个多卡行:全部 ≤ 2px`);
+
+for (const b of bottomGaps) b.viewportGap = deep.find((d) => d.page === b.page)?.viewportGap ?? 0;
+/* 页面底部空白 = 视口下的空白 OR 页面比内容多出来的空白,两者必须同时 ≤ 120px:
+   - 短页(/):滚不动,视口底部到底栏或最后内容的距离;
+   - 长页:滚到底,视口底部到最后一块内容的距离(卷到底之后看到的空白)。
+   任务书的「页面总高 - 栅格底」这种量法不对:内容是长页时它就是整屏高度,跟留白无关。 */
+check('页面底部空白 ≤ 120px(视口下 + 滚到底两处都量)', bottomGaps.every((b) => b.viewportGap >= 0 && Math.min(b.gap, b.viewportGap) <= 120),
+  bottomGaps.map((b) => `${b.page} 滚动余量 ${b.gap}px / 滚到底视口留白 ${b.viewportGap}px`).join(';'));
+
+
 if (errors.length) console.log(`\n页面错误 ${errors.length}:\n    ${errors.slice(0, 10).join('\n    ')}`);
 const fail = checks.filter((c) => !c.ok);
 console.log(`\n${JUDGE ? '判分' : '基线(不判分)'}:${checks.length - fail.length}/${checks.length} 达标${errors.length ? `,页面错误 ${errors.length}` : ''}`);

+ 27 - 1
src/pages/report-card.js

@@ -14,6 +14,7 @@
  */
 import { html, css } from 'lit';
 import { cardFacts, whenText, cjkSpace } from '../core/index.js';
+import { t } from '../components/cjk.js';
 import { icon } from '../components/icons.js';
 import '../components/kf-viz.js';
 
@@ -200,6 +201,21 @@ function badges(f) {
     ${f.status === 'approved' || f.status === 'rejected' ? html`<span class="badge status s-${f.status}">${f.statusLabel}</span>` : ''}`;
 }
 
+/**
+ * cardFooter —— 卡片底部的操作条 + 页脚(v12 P2:底部收尾)。
+ *
+ * 试用版没有真分页:卡片是「全部列出」的数据视图,所以不塞页码(页码是死的,点了也不会翻)。
+ * 底部收尾做三件事:本页看到了几条 / 一键回到筛选栏 / 回到顶部。
+ * 各页共用,只接调用方给的回调。
+ */
+export function cardFooter({ shown, all, filtered, label = '条成果', onTop = () => {}, onFilter = null }) {
+  return html`<footer class="cfoot" data-foot>
+    <span class="cfl">${filtered ? t(`筛出 ${shown} / 共 ${all} `) : t(`共 ${all} `)}${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>
+  </footer>`;
+}
+
 /** 来源四要素:谁 · 哪个 Agent · 什么方式 · 什么时候(图标打头;缺的如实标黄,不补编) */
 export function sourceRow(e, { nowrap = false } = {}) {
   return html`<div class="src ${nowrap ? 'nowrap' : ''}" data-source-row aria-label="来源:谁 · Agent · 方式 · 时间">
@@ -280,5 +296,15 @@ export function reportRow({ e, recent = false, onOpen = null }) {
         ${icon('user', 12)} ${e.owner || '—'} ${icon('bot', 12)} ${e.agent || '—'} ${icon(METHOD_ICON[e.methodKey] || 'doc', 12)}
       </span>
       <span class="mono nowrap">${recent ? html`<span class="up">${icon('up', 11)}</span>` : ''}${whenText(e.at)}</span>
-    </div>`;
+    </div>
+  }
+  /* 页脚(v12 P2):一条收尾,不是空白 —— 左:本页数量;右:两个轻操作 */
+  .cfoot { display: flex; align-items: center; gap: 8px; margin: var(--kf-space-card-gap) 0 0; padding: 12px 4px; border-top: 1px solid var(--kf-line);
+    font-size: 12px; color: var(--kf-ink-3); }
+  .cfoot .cfl { margin-right: auto; font-variant-numeric: tabular-nums; }
+  .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; color: var(--kf-ink-2);
+    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); }
+  .cfb:hover { background: rgba(255, 255, 255, 0.55); color: var(--kf-ink); }
+`;
 }

+ 1 - 1
src/pages/view-pool.js

@@ -215,7 +215,7 @@ export class ViewPool extends LitElement {
           <div class="seg" role="group" aria-label="排序方式">
             ${SORTS.map((s) => html`<button aria-pressed=${(c.sort || 'priority') === s.key ? 'true' : 'false'} @click=${() => this.#setSort(s.key)}>${s.label}</button>`)}
           </div>
-          <span class="sfield"><input type="search" placeholder="搜索卡片标题 / 一句话 / 上传人" .value=${c.q || ''} @input=${this.#search} aria-label="搜索机会卡" /></span>
+          <input type="search" placeholder="搜索卡片标题 / 一句话 / 上传人" .value=${c.q || ''} @input=${this.#search} aria-label="搜索机会卡" />
         </div>
       </div>
 

+ 5 - 2
src/pages/view-report-type.js

@@ -15,7 +15,7 @@ import { useTokens } from '../components/base.js';
 import { navigate, setQuery, href } from '../utils/router.js';
 import { readCardQuery, patchOf, applyCardTool, paginate } from '../utils/card-view.js';
 import { reportTypeOf, REPORT_TYPES, entriesForType, hasEvidence, cardMatches, cardFacetCounts, sortCardEntries } from '../core/index.js';
-import { reportCard, reportRow, CARD_CSS } from './report-card.js';
+import { reportCard, reportRow, cardFooter, CARD_CSS } from './report-card.js';
 import { cardToolbar, makeToolHandler, TOOLBAR_CSS } from './card-toolbar.js';
 import { t, VIEW_CSS, section } from './shared.js';
 import { icon } from '../components/icons.js';
@@ -231,7 +231,10 @@ export class ViewReportType extends LitElement {
           : html`<div class="cards" data-cards>
               ${pg.items.map((e) => html`<kf-glass-card surface="2" lift @card-activate=${() => this.#open(e)}>${reportCard({ e, recent: recent(e), fit: true })}</kf-glass-card>`)}
             </div>`}
-      ${needPager ? this.#pager(pg) : ''}
+      ${/* v12 P2 (b):底部收尾 —— 条数 + 回筛选栏 + 回顶部(试用版不分页,页码是死的,不铺分页条) */ ''}
+      ${cardFooter({ shown: pg.items.length, all: all.length, filtered: matched.length !== all.length, label: '条成果',
+        onFilter: () => this.renderRoot.querySelector('[data-toolbar]')?.scrollIntoView({ behavior: 'smooth', block: 'center' }),
+        onTop: () => window.scrollTo({ top: 0, behavior: 'smooth' }) })}
     `;
   }
 

+ 5 - 7
src/pages/view-reports.js

@@ -10,7 +10,7 @@ import { useTokens } from '../components/base.js';
 import { reportCenter, REPORT_TYPES, cardMatches, cardFacetCounts, sortCardEntries } from '../core/index.js';
 import { readCardQuery, patchOf, applyCardTool, paginate } from '../utils/card-view.js';
 import { setQuery, navigate, href } from '../utils/router.js';
-import { reportCard, reportRow, sourceRow, CARD_CSS } from './report-card.js';
+import { reportCard, reportRow, sourceRow, cardFooter, CARD_CSS } from './report-card.js';
 import { cardToolbar, makeToolHandler, TOOLBAR_CSS } from './card-toolbar.js';
 import { t, section, VIEW_CSS } from './shared.js';
 import '../components/kf-glass-card.js';
@@ -169,12 +169,10 @@ export class ViewReports extends LitElement {
           : html`<div class="cards" data-cards>
               ${pg.items.map((e) => html`<kf-glass-card surface="2" lift @card-activate=${() => this.#open(e)}>${reportCard({ e, recent: recent(e), typeName: e.typeName, fit: true })}</kf-glass-card>`)}
             </div>`}
-      ${matched.length > 12 ? html`<div class="pager" data-pager>
-        <button ?disabled=${pg.page === 1} aria-label="上一页" @click=${() => this.onCardPage(pg.page - 1)}>${icon('left', 14)}</button>
-        ${Array.from({ length: pg.pages }, (_, i) => i + 1).map((n) => html`<button data-page=${n} aria-current=${n === pg.page ? 'page' : 'false'} @click=${() => this.onCardPage(n)}>${n}</button>`)}
-        <button ?disabled=${pg.page === pg.pages} aria-label="下一页" @click=${() => this.onCardPage(pg.page + 1)}>${icon('right', 14)}</button>
-        <span class="sz">${pg.page}/${pg.pages} · ${pg.total}</span>
-      </div>` : ''}
+      ${/* v12 P2 (b):底部收尾 —— 条数 + 回筛选栏 + 回顶部。条数 < 一页时只留一条轻页脚,不铺分页 */ ''}
+      ${cardFooter({ shown: pg.items.length, all, filtered: matched.length !== all.length, label: '条成果',
+        onFilter: () => this.renderRoot.querySelector('[data-toolbar]')?.scrollIntoView({ behavior: 'smooth', block: 'center' }),
+        onTop: () => window.scrollTo({ top: 0, behavior: 'smooth' }) })}
     `;
   }
 

+ 4 - 1
src/pages/view-stages.js

@@ -9,7 +9,7 @@ import { LitElement, css, html } from 'lit';
 import { useTokens } from '../components/base.js';
 import { STAGES, stageOutcome, filterEntries } from '../core/index.js';
 import { setQuery, navigate } from '../utils/router.js';
-import { reportCard, CARD_CSS } from './report-card.js';
+import { reportCard, cardFooter, CARD_CSS } from './report-card.js';
 import { t, VIEW_CSS, section } from './shared.js';
 import { icon } from '../components/icons.js';
 
@@ -127,6 +127,9 @@ export class ViewStages extends LitElement {
         ? html`<div class="cards" data-cards>${items.map((e) => html`<kf-glass-card surface="2" lift @card-activate=${() => this.#open(e)}>${reportCard({ e, expanded: true, fit: true })}</kf-glass-card>`)}</div>`
         : html`<kf-glass-card surface="1"><kf-empty title=${only ? t(`${only} 还没有产出`) : '还没有产出'} action-label=${only || evOnly || mineSel ? '清除筛选' : '去工作台'}
             @empty-action=${() => (only || evOnly || mineSel ? this.#set({ stage: '', ev: '', mine: '' }) : navigate('/'))}></kf-empty></kf-glass-card>`}
+      ${cardFooter({ shown: items.length, all: total, filtered: Boolean(only || evOnly || mineSel), label: '条产出',
+        onFilter: () => this.renderRoot.querySelector('[data-overview]')?.scrollIntoView({ behavior: 'smooth', block: 'start' }),
+        onTop: () => window.scrollTo({ top: 0, behavior: 'smooth' }) })}
     `;
   }
 }

+ 3 - 0
src/pages/view-workbench.js

@@ -11,6 +11,7 @@ import { forMe, poolOverview, liveBoard, STAGES, sortOpportunities } from '../co
 const sortByNewest = (rows) => sortOpportunities(rows, 'newest');
 import { href } from '../utils/router.js';
 import { t, VIEW_CSS, head } from './shared.js';
+import { cardFooter } from './report-card.js';
 import { icon } from '../components/icons.js';
 
 const TONE = { S1: 'blue', S2: 'violet', S3: 'teal', S4: 'amber', S5: 'rose' };
@@ -188,6 +189,8 @@ export class ViewWorkbench extends LitElement {
       <div class="latest" style="--i:2">
         ${latest.map((o) => html`<kf-opportunity-card .opp=${o} base="/pool" @card-open=${(e) => this.#open(e.detail.id)}></kf-opportunity-card>`)}
       </div>
+      ${/* v12 P2 (b):底部收尾 —— 本页概况的一句收口 + 回顶部,吃掉页尾空白 */ ''}
+      ${cardFooter({ shown: latest.length, all: pool.total, filtered: false, label: '张机会卡,最近 3 张列在上方', onTop: () => window.scrollTo({ top: 0, behavior: 'smooth' }) })}
       <!-- 只有手机显示:证据台账从底部 Tab 移到工作台页脚(真实链接,部署前缀同源) -->
       <nav class="foot-links" aria-label="更多入口">
         <a href=${href('/ledger')} data-link>${icon('ledger', 16)} 证据台账</a>