` 加起来等于外包层 | 分盘、Bhava、行运用组件库表格。本命表仍是原生 table |
+| 大运展开 | `` / `details:not([open])` | `data-slot="collapsible-content"`,收起用 `data-closed` | 大运改用 Collapsible。当前段默认展开,点按高度仍是 44px |
+| 零年大运 | 零年行没有 `` | 源码是 Sheet;格子静态渲染 23 项;选中后先关上再回调 | 测试宿主的浮层挂不进 body,不能假装点开过 |
+| D1 上升格 | 基线对照里是「/」按钮 | 可见「—」,说明句仍在 | 没有状态就写「—」。基线文件不改,只在断言里替换 |
+| D1 状态列 | 纯文本 | 同一列换成 Badge;落陷、敌是 destructive,其余是 secondary | 同上,fixture 文件不改 |
+| 分盘和月亮的上升格 | 可见「/」 | 可见「—」 | 没有状态就写「—」 |
+| 出生资料框 | 源码把点击交给 `chartBirthDialogShouldClose` | `Dialog` 的 `onOpenChange` 收到 false 才关;几何函数仍导出 | 改用对话框。点内容区内边距不关,和原来的结果相同 |
+| 白名单 | 含 5 个星盘页文件 | 这 5 个移出 | 它们已经改用组件库。名单只许变短 |
+
+移出的 5 个文件:`chart-birth-editor.tsx`、`chart-dasha-tab.tsx`、`chart-page-view.tsx`、`chart-type-picker.tsx`、`chart-vedic-tab.tsx`。
+
+## 自测
+
+| 检查 | 命令 | 结果 |
+| --- | --- | --- |
+| 类型 | 仓库根 `frontend\node_modules\.bin\tsc --noEmit -p frontend` | 退出码 0 |
+| 检查 | 在 `frontend` 下 `npm run lint` | 退出码 0。122 个警告,0 error |
+| 定向测试 | 在 `frontend` 下 `node node_modules/tsx/dist/cli.mjs --test` 下面列出的文件 | 188 条,通过 188,失败 0,取消 0 |
+| 构建 | 在 `frontend` 下 `npm run build` | 退出码 0。编译 10.3s,TypeScript 30.8s。`/` 仍是 ○ Static。5 条既有 skill-package-registry 警告,没有改 |
+| 首屏体积 | 首页 HTML 引用的 `/_next/static/**.js`,gzip-9 相加,不含 CSS | 基线 `d25dfdbe`(上一单记下的数):36 个文件,639127 字节。本单:36 个文件,缺失 0,640131 字节,多 1004(+0.16%)。±2% 上限 651910。`react-resizable-panels` 命中 0。`chart-page-vedic-split` 命中 0 |
+
+定向测试文件:`billing-panel.test.ts`、`chart-bhava.test.tsx`、`chart-birth-editor-and-report-table.test.tsx`、`chart-dasha-switch-layout.test.ts`、`chart-dasha-yogini.test.ts`、`chart-glyph-motion.test.ts`、`chart-moon-transit.test.tsx`、`chart-page-hook.test.ts`、`chart-page-mobile-width-20260929.test.ts`、`chart-page-view.test.tsx`、`chart-planet-glyphs.test.tsx`、`chart-symbol-help.test.tsx`、`chart-table-span-20261023.test.tsx`、`chart-type-picker.test.tsx`、`chart-varga-table.test.tsx`、`chart-view-zero-year.test.ts`、`secondary-page-entry.test.ts`、`surface-feedback-20260929.test.tsx`、`ui-kit-whitelist.test.ts`、`western-wheel-layout.test.ts`。
+
+没有跑前端全量 `npm test`,没有跑 Python 快速门,没有跑 `test:db`。`tests/test_repo_privacy_markers.py` 退出码 0。`page.tsx` 和 `scripts/jyotish_api_server.py` 没有改。
+
+## 偏离
+
+- 出生资料的输入框仍是档案页共用的那一份,没有拆成第二套 `Field` 和 `Input`。编辑框本身已是对话框。
+- 本命和月亮的行星表仍是原生 table。组件库表格用在分盘、Bhava、行运和西洋表。D1 基线文件没有改,状态列和上升格在断言里替换后再对照。
+- 「星盘类型」抽屉在这个测试宿主里挂不进 body。合同改为源码接线和 23 个格子的静态渲染。
+- 测试用的假文档补了 `head`、`querySelector` 和 `querySelectorAll`。组件库页签会把一段样式挂到 document 上,没有这些会在渲染时抛错。产品页面没有为测试改行为。
+- 可拖分栏的名字在首页脚本里仍出现 1 次,和上一单记下的次数一样。库文件名 `react-resizable-panels` 在首页脚本里是 0。星盘页的分栏是打开星盘后才载入。
+
+## 环境缺口
+
+- 没有登录态。没有在浏览器里打开过星盘页。1024、1280、390 三种宽度的截图没有做,不写成通过。可照做的步骤在 `docs/testing/ui-kit-migration-20261023.md` 的「星盘页」一节。
diff --git a/docs/testing/ui-kit-migration-20261023.md b/docs/testing/ui-kit-migration-20261023.md
index 90b13bca..8809bf61 100644
--- a/docs/testing/ui-kit-migration-20261023.md
+++ b/docs/testing/ui-kit-migration-20261023.md
@@ -29,3 +29,15 @@
3. 回答下面可以赞、踩、复制。点踩之后可以选原因,也可以什么都不选就提交。兑换窗的句子和以前一样。
4. 桌面上,点数右边有一个打开本命盘的按钮。打开后是当前人物的本命盘。拖右缘,宽度停在大约 280 到 560 像素。收起后再打开,宽度还在。刷新页面,开着和宽度都还在。
5. 把窗口改成大约 375 像素宽。顶栏没有打开本命盘的按钮。
+
+## 星盘页
+
+没有登录态,下面各项都还没在浏览器里看过。1024、1280 和 390 三种宽度的截图也没做。
+
+1. 打开星盘。四个页签是星盘、基础信息、大运、西洋盘。当前项有下划线。点基础信息、大运、西洋盘,内容跟着换。盘还没出来时,这四个不能点。
+2. 在星盘页里,本命、D9、月亮这些盘型是一排。选中的一格是亮的。盘型多时可以横着滑,页面本身不横滚。点右边「全部盘型」,从底部打开,能看到 23 种。点一种,抽屉关上,条上滚到那一格。
+3. 把窗口放到大约 1280 或 1024 宽。盘和旁边的参数中间有一条可以拖。拖宽之后行星表仍然横跨整行,没有缩回左半边。把窗口改成大约 390 宽,盘在上,参数在下,页面没有横向滚动条。
+4. 看本命行星表的状态列。有字的都是同一种小标记。落陷,以及写着「敌」的,颜色更重。没有状态的格子是「—」,上升那一格也是「—」。指上去能读到「上升点是命盘的起点,不是行星,不判断入旺或落陷」。
+5. 点顶栏的日期和地点。可以改出生资料。点框里面的空白不会把没保存的修改丢掉。按 Esc 或点外面会关上。
+6. 在本命和 D9 之间来回切。星体从原来的宫位移到新的宫位,停稳后和直接打开那张盘一样。换成另一个人时,格子重画,星体不飞。系统开了减少动态效果时,只淡入,没有位移。第一次打开星盘时的那一次画线,不会因为换盘再来一遍。
+7. 点开一段收着的大运,下面的小运展开。0 年的那一段不能展开。右栏先是 Chara。点 Yogini 才去取另一套。这一套失败时,再点一次 Yogini 会再要一次。
diff --git a/frontend/DESIGN.md b/frontend/DESIGN.md
index a20b2dd0..38e7c066 100644
--- a/frontend/DESIGN.md
+++ b/frontend/DESIGN.md
@@ -521,7 +521,7 @@ The birth-time rectification session is the consultation transcript plus a house
- Before the first message the list selects a person and updates that empty session's binding. Self is the visible default. After the first message the current person stays selected; another person offers only `新建会话并使用此人物` and does not rebind the old session. That action waits until the current request ends.
- Loading shows the small orbit (`OrbitWaiting`, 2026-09-30), with `人物列表还在读` for screen readers only. Failure reads `人物列表暂时读不出来` with `重试`. When the people API itself fails, the popover reads `暂时读不到其他人` and lists only the signed-in person; history stays on that person and the page does not drop into the recoverable error screen. An incomplete row reads `这份资料还不完整` and cannot be chosen. A deleted person reads `资料已删除 · {名字}`; the old session stays readable and sending is blocked. None of these states use a spinner.
- Rectification and daily horoscope do not get this control. Their quiet note, when present, remains the static `.chat-header-chart` span and still drops out below 768px. The person button stays on narrow screens and ellipsizes so the header remains one line.
-- The chart page birth line is a button (`.chart-birth-line`) after the person: `日期 时间 · 地点` and a pen. The name is not repeated. The whole row opens an editor on this page. Self can change every field. Someone else's already-saved birth time stays locked. The sentence `填报的出生时间保存后不能再改` appears once, beside that field, and is not repeated in the form description. Changing the date, time, or place shows `改了出生时间、日期或地点,之前生时校正采用的时间会作废,星盘改按你新填的时间排。` Saving uses the shared other-person write (self still goes through the account). A click on the dialog's padding, or releasing a text selection there, does not close the dialog or drop unsaved edits. Escape still closes it, and so does a click on the backdrop outside the dialog box. Below 768px that row wraps onto a second line and the chart header grows to fit it.
+- The chart page birth line is a button (`.chart-birth-line`) after the person: `日期 时间 · 地点` and a pen. The name is not repeated. The whole row opens an editor on this page. Self can change every field. Someone else's already-saved birth time stays locked. The sentence `填报的出生时间保存后不能再改` appears once, beside that field, and is not repeated in the form description. Changing the date, time, or place shows `改了出生时间、日期或地点,之前生时校正采用的时间会作废,星盘改按你新填的时间排。` Saving uses the shared other-person write (self still goes through the account). The editor is a component-library dialog (2026-10-25, K3). A click inside the form, including its padding, does not close it or drop unsaved edits. Escape and a click on the backdrop do close it. The fields stay the shared profile form used by the people page, so this page does not grow a second set of fields. Below 768px that row wraps onto a second line and the chart header grows to fit it.
- With the composer strip gone as well, the two pieces of permanent chrome went from 68 + 148 to 46 + 124 px.
- On viewports ≤767px the header row is 52px plus the safe-area inset. The credit control (`.credit-balance`) is a normal outline button, 44px, with a coins icon and the number; 点 stays in the accessible name. The rectification 「当前盘面」 chip stays 40px. The shared `::before` hit area stays inset -2px. The onboarding checklist trigger stays 32px.
- Window-level scroll is reset by `ViewportScrollLock` after the on-screen keyboard closes, so the header cannot be left above the screen.
@@ -1178,13 +1178,13 @@ Three product rules for assistant markdown in `.message-markdown` (BUG-962 / 963
页名「星盘」与出生资料一行在 `SecondaryHeader` 的 46px 顶栏里(§ Secondary page shell)。顶栏之下第一件东西是 Tab 行。不写眉标,不写「打开即有」、不写「主盘直接算 · 分盘按需 · 不消耗点数」,也不承诺打开速度。等待外壳和失败页只有原因/等待句。
-四个 Tab 按体系分:**星盘 / 基础信息 / 大运 / 西洋盘**,画成**一条下划线式 Tab 行**:无边框、无卡面,当前项 2px 下划线 + 主文字色,整行一条 1px 底线。行内 `flex-wrap: nowrap` 且自身横向滚动,所以 375px 下它仍是一行,不折行、不换成第二排——底线画在滚动容器自己的 border 上,不会跟着内容滚走。一排等重的药丸按钮会读成几个可按的东西,Tab 行读成「一个控件 + 一个当前位置」。
+四个 Tab 按体系分:**星盘 / 基础信息 / 大运 / 西洋盘**,画成**一条下划线式 Tab 行**:无边框、无卡面,当前项 2px 下划线 + 主文字色,整行一条 1px 底线。行内 `flex-wrap: nowrap` 且自身横向滚动,所以 375px 下它仍是一行,不折行、不换成第二排——底线画在滚动容器自己的 border 上,不会跟着内容滚走。一排等重的药丸按钮会读成几个可按的东西,Tab 行读成「一个控件 + 一个当前位置」。2026-10-25 起这四个页签是组件库 `Tabs`,`variant="line"`。盘还没到时四个都不可选。
### 盘型条与「星盘类型」面板(2026-09-28,TASK-chart-types D2)
-印度「星盘」Tab 内部是一条**盘型条**,共 23 种,顺序固定:本命、D9、月亮盘、Bhava、行运、D2–D8、D10、D11、D12、D16、D20、D24、D27、D30、D40、D45、D60。条上文字短:「本命 / D9 / 月亮 / Bhava / 行运 / D2 …」。条用既有 chip 的样子(比 Tab **低一级**:无描边、无卡面、caption 字号、三级文字色,当前项只有一块浅底),`flex-wrap: nowrap` 且自身横向滚动(`.chart-page-type-strip`),375px 下一行、页面不横滚。西洋盘不做成盘型。
+印度「星盘」Tab 内部是一条**盘型条**,共 23 种,顺序固定:本命、D9、月亮盘、Bhava、行运、D2–D8、D10、D11、D12、D16、D20、D24、D27、D30、D40、D45、D60。条上文字短:「本命 / D9 / 月亮 / Bhava / 行运 / D2 …」。2026-10-25 起这条是分段式 `Tabs`(灰底槽,选中的一格浮起亮底),放在横向 `ScrollArea` 里(`.chart-page-type-strip` 仍 `flex-wrap: nowrap`),375px 下一行、页面不横滚。不用描边的分段开关。西洋盘不做成盘型。
-条尾固定一个「≡」(`.chart-page-type-menu`,44×44,`aria-label="全部盘型"`),在滚动容器外面,不会被滚走。点它打开原生 `