:root {
  /* 中性色：低饱和度、微微偏绿的灰阶（取自原型），比旧版那套冷灰更柔和 */
  --fg: #17201c; --muted: #4b5651; --muted-2: #7b8681;
  --line: #d5dbd6; --line-soft: #e4e8e5; --line-strong: #b6bfb8;
  --bg: #fff; --bg-soft: #eef1ee; --bg-sunken: #e7ebe8;

  /* 三态状态色：饱和度压低，不再是糖果色；各自配一个浅底供告警条/徽标用 */
  --green: #2f7d55; --green-soft: #e0efe6;
  --amber: #a86a17; --amber-soft: #f6ecd9;
  --red: #b0413e; --red-soft: #f7e5e4;

  --accent: #0f6b5c;
  /* 强调色的浅底：选中态、高亮行这类「不是纯色块、只是用强调色打个底」的场合 */
  --accent-soft: #dfeeea;
  /* 需要用强调色打底、上面压白字的场合（页签选中态那类实心块）：深色主题下
     --accent 本身会调亮以便在暗底上更显眼，那个亮度配白字对比不够，这个
     token 专门留给「纯色块 + 白字」，深色主题覆盖块里会单独给它一个更深的值。 */
  --accent-strong: #0f6b5c;

  /* 涨跌色。A 股口径：红涨绿跌，且只给「变化」列用——语义是盈亏，不是好坏，
     所以不复用 --red/--green（那两个在别处表示告警和「已填」）。两个色的对比度
     都压在 4.8:1 以上：14px 的非粗体数字，再浅就读不动了。 */
  --up: #c23c37; --down: #1f7a4d;

  /* 资产大类配色 —— 全站统一，看板图例、盘点分组、汇总表共用同一套。沿用旧版
     的色相分工（现金蓝/金融靛/投资紫/房产橙/应收粉/负债红），只把饱和度往新
     配色的克制方向压了一档；负债直接复用 --red——两处本来就是同一个「警示」
     语义，没必要各自定一份数字，深色主题也就不用再单独覆盖它。 */
  --c-cash: #2f8fc4;
  --c-financial: #6a63c9;
  --c-investment: #9868c2;
  --c-property: #c9853a;
  --c-receivable: #c66594;
  --c-liability: var(--red);

  /* 恒定深色块：模块大标题横条、toast 这类「不管当前是明是暗、自己始终是一块
     深底白字」的元素专用（--solid-warn 是 toast 的告警变体）。特意不放进下面
     prefers-color-scheme 覆盖块——不重新定义就是让它们保持不变的手段，深色
     主题下这块横条不会跟着变浅、变得和白字贴不上对比度。 */
  --solid-bg: #17201c; --solid-ink: #f5f7f5; --solid-warn: #7a4a12;

  /* 数字一律用系统等宽字体：不加载 Google Fonts——用户在国内，
     fonts.googleapis.com 经常连不上或很慢，页面不能等一个字体 CDN。
     tabular-nums 配合等宽字形，金额上下对齐、闪动的只有数位本身。 */
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;

  /* 吸顶层叠：导航冻在最上，结果条紧贴其下。两个数都是量出来的（Chrome 实测
     导航桌面 51.4px、窄屏 48.4px），一律向上取整——宁可让结果条压住导航底边
     半个像素，也不能留下哪怕 0.4px 的缝：那条缝里会有内容滚过去。改导航的
     内边距或字号必须回来改这里。
     --bar-h 是 JS 跑起来之前的兜底值，不是常年生效的定数：grid.js 的
     observeBarHeight() 用 ResizeObserver 盯着 #sum-bar，结果条尺寸一变就把
     它的边框盒高度（offsetHeight）实时写回这个属性，页面跑起来之后这里写的
     数字就不再是唯一依据了。之所以还要让它大致准确，是因为分幕锚点的
     scroll-margin-top 在 ResizeObserver 第一次回调触发之前也得靠它——这里的
     104px 是桌面宽度下实测的兜底，窄屏另有一份（见下面 @media 块，约
     129px）：那个断点里结果条两格子行的小字（.sum-delta/.sum-sub）已经被
     隐藏，窄屏数字因此没有比桌面高很多，不是当初新增两格时以为的那样。 */
  --nav-h: 52px;
  --bar-h: 104px;
}

/* 深色主题：跟随系统 prefers-color-scheme，应用本身没有手动切换开关，所以
   只重定义 token 的值，不加 [data-theme] 这层手动覆盖。上面那一大批组件规则
   能从写死的十六进制颜色换成 var()，图的就是这里——颜色只在这一处分叉，
   选择器和数值结构一个字不用再动第二遍。 */
@media (prefers-color-scheme: dark) {
  :root {
    --fg: #e5ebe7; --muted: #a4b0aa; --muted-2: #7a8781;
    --line: #2d3833; --line-soft: #232b27; --line-strong: #43514b;
    --bg: #1a211e; --bg-soft: #101412; --bg-sunken: #232b27;

    --green: #55b184; --green-soft: #16301f;
    --amber: #cf9a45; --amber-soft: #322613;
    --red: #e07a74; --red-soft: #341b1a;

    --accent: #46b9a2;
    --accent-soft: #17322c;
    --accent-strong: #1c7566;

    --up: #d9726c; --down: #4cae7c;

    --c-cash: #56aee0;
    --c-financial: #8b84e0;
    --c-investment: #ab8fdb;
    --c-property: #d9a35c;
    --c-receivable: #d987b0;
    --c-liability: var(--red);
  }
}
* { box-sizing: border-box; }
body { margin: 0; font: 14px/1.6 -apple-system, "PingFang SC", "Microsoft YaHei", sans-serif;
       color: var(--fg); background: var(--bg-soft); }
main { max-width: 1100px; margin: 0 auto; padding: 24px 20px 64px; }
main.wide { max-width: 1600px; }
h1 { font-size: 22px; margin: 0; letter-spacing: -0.01em; }

/* ---------- 导航 ---------- */
/* 导航冻在顶上：盘点页要滚十几个账户，中途想去看板不该先滚回顶。层级高于
   结果条（z-index 20）与宽表表头（3），滚动时它们从导航底下穿过去。 */
.nav { display: flex; gap: 4px; align-items: center; padding: 0 20px;
       background: var(--bg); border-bottom: 1px solid var(--line);
       position: sticky; top: 0; z-index: 30; }
.nav a { color: var(--muted); text-decoration: none; padding: 14px 14px 12px;
         border-bottom: 2px solid transparent; font-weight: 500; }
.nav a:hover { color: var(--fg); }
.nav a.active { color: var(--accent); border-bottom-color: var(--accent); }
.nav .nav-right { margin-left: auto; }
.nav .nav-right button { color: var(--muted); border-color: transparent; background: transparent; }
.nav .nav-right button:hover { color: var(--fg); background: var(--bg-sunken); }

.login { max-width: 300px; margin: 15vh auto; display: flex; flex-direction: column; gap: 12px;
         background: var(--bg); padding: 28px; border-radius: 12px; border: 1px solid var(--line);
         box-shadow: 0 1px 3px rgba(0,0,0,.04); }
.error { color: var(--red); margin: 0; }

input, button, select { font: inherit; padding: 6px 10px; border: 1px solid var(--line);
                        border-radius: 6px; background: var(--bg); color: var(--fg); }
input:focus, select:focus { outline: 2px solid var(--accent); outline-offset: -1px; border-color: transparent; }
button { cursor: pointer; }
button:hover { background: var(--bg-sunken); }

/* ---------- 表格通用 ---------- */
table { border-collapse: separate; border-spacing: 0; width: 100%; background: var(--bg); }
/* 行节奏取自原型：行与行之间只用一道很淡的分隔线（--line-soft），表头那一道
   独立加粗（--line）收住整张表——旧版两者都是同一个 --line，读起来是一整片
   等重的网格线，现在只有表头这一道线是「结构」，其余都是「呼吸」。
   垂直内边距第一轮改版时抄了一遍原型的数字（7px），结果比改版前的 8px 还紧、
   没做到「更像账本」——原型是 15px/1.6 的正文字号撑出来的呼吸感，照搬像素
   数字在我们 14px 的正文下不成立。这里改大一号（表头 8px、数据行 10px），
   刻意留出「表头紧凑、数据行宽松」的落差：表头是一次性读的列名，数据行是
   反复扫视的数字，后者更值得留白（评审：原型对比意见 #8）。 */
th, td { padding: 10px 12px; border-bottom: 1px solid var(--line-soft); text-align: right; }
th { background: var(--bg-sunken); border-bottom: 1px solid var(--line);
     font-family: var(--mono); font-weight: 600; font-size: 11px; color: var(--muted-2);
     letter-spacing: .04em; white-space: nowrap; padding: 8px 12px; }
th:first-child, td:first-child { text-align: left; }
.num { font-family: var(--mono); font-variant-numeric: tabular-nums; white-space: nowrap; }
.neg { color: var(--red); }
.pos { color: var(--green); }
.readonly { color: var(--muted); background: var(--bg-soft); }

.status-green { color: var(--green); }
.status-amber { color: var(--amber); }
.status-red { color: var(--red); }
.status-unknown { color: var(--muted); }
/* 偏离警告色：与年度进度条配色一致 */
.status-ahead { color: var(--amber); }  /* 低支，棕色 */
.status-over { color: var(--red); }   /* 超支，红色 */

.warnings { margin: 16px 0; padding: 12px 16px; border-left: 3px solid var(--amber);
            background: var(--amber-soft); border-radius: 0 8px 8px 0; }
.warnings p { margin: 4px 0; }
/* 保存结果三态：成功不该长得像警告。默认（无 kind 类）保持琥珀色告警样式。 */
.warnings.status-ok    { border-left-color: var(--green); background: var(--green-soft); }
.warnings.status-warn  { border-left-color: var(--amber); background: var(--amber-soft); }
.warnings.status-error { border-left-color: var(--red);   background: var(--red-soft); }

/* ---------- 分类色带 ---------- */
.group-head th { text-align: left; background: var(--bg-sunken); font-weight: 700;
                 color: var(--fg); font-size: 13px; }
.group-total td { border-top: 1px solid var(--line); font-weight: 600; background: var(--bg-soft); }

.cat-cash td:first-child      { box-shadow: inset 3px 0 var(--c-cash); }
.cat-financial td:first-child { box-shadow: inset 3px 0 var(--c-financial); }
.cat-investment td:first-child{ box-shadow: inset 3px 0 var(--c-investment); }
.cat-property td:first-child  { box-shadow: inset 3px 0 var(--c-property); }
.cat-receivable td:first-child{ box-shadow: inset 3px 0 var(--c-receivable); }
.cat-long_term td:first-child, .cat-short_term td:first-child { box-shadow: inset 3px 0 var(--c-liability); }

.group-head.cat-cash th       { box-shadow: inset 3px 0 var(--c-cash); }
.group-head.cat-financial th  { box-shadow: inset 3px 0 var(--c-financial); }
.group-head.cat-investment th { box-shadow: inset 3px 0 var(--c-investment); }
.group-head.cat-property th   { box-shadow: inset 3px 0 var(--c-property); }
.group-head.cat-receivable th { box-shadow: inset 3px 0 var(--c-receivable); }
.group-head.cat-long_term th, .group-head.cat-short_term th { box-shadow: inset 3px 0 var(--c-liability); }

.hint { color: var(--muted); font-size: 12px; }
/* 标题和月份轴同一行。gap 给得比原来大：h1 和那块凹底之间要有明显的呼吸，
   否则读起来像标题的一部分。窄屏放不下时 wrap 让轨道掉到第二行。 */
.page-head { display: flex; gap: 20px; align-items: center; flex-wrap: wrap;
             margin-bottom: 20px; }
.panel { margin-top: 28px; background: var(--bg); border: 1px solid var(--line); border-radius: 12px;
         padding: 18px 20px; }
.panel h2 { font-size: 15px; margin: 0 0 12px; display: flex; gap: 12px; align-items: baseline; }
.panel h3 { font-size: 13px; color: var(--muted); margin: 16px 0 6px; font-weight: 600; }
/* 「支出」一幕下的两个小节标题：标题、说明、按钮排成一行，与 .panel h2 同款。
   第一个不要顶部外边距——它紧跟在 h2 下面。 */
.panel h3.sub-head { display: flex; gap: 12px; align-items: baseline; flex-wrap: wrap;
                     font-size: 14px; color: var(--fg); margin: 22px 0 8px; }
.panel h2 + h3.sub-head { margin-top: 0; }
.flow-table input, .flow-table select { width: 100%; }
.flow-add { margin-top: 8px; }

/* ---------- 盘点网格 ---------- */
/* overflow 不能 hidden：分类书签标签要跨出表格左框，hidden 会裁掉它。
   圆角改由 thead/tfoot 的角单元格自身承担。 */
#grid { border: 1px solid var(--line); border-radius: 12px; }
#grid thead th:first-child { border-top-left-radius: 12px; }
#grid thead th:last-child { border-top-right-radius: 12px; }
#grid tfoot tr:last-child td:first-child { border-bottom-left-radius: 12px; }
#grid tfoot tr:last-child td:last-child { border-bottom-right-radius: 12px; }
/* 所有输入右对齐（与期初/期末数字列一致）；边框走全局 input 样式，流转/备注
   不再是透明的——之前只有期末有可见边框，其余输入框悬停前不可见 */
#grid input { width: 100%; text-align: right; }
#grid input.closing { font-weight: 600; }
/* 备注：这一行唯一的自由文本，也是最少填的一格。给它一个窄的固定宽度、让单元格
   收缩到内容（width: 1% 在表格里就是「按内容收」），省下的横向空间还给左边那串
   金额列——它排在最末，占满剩余宽度的话整张表会被一个多半是空的文本框拖散。
   同时把对齐拨回左：上面那条右对齐是为金额列写的，文字右对齐读起来像倒着写。
   两条都只在宽屏成立，窄屏卡片布局里备注和别的格子一样是一整行（见下面
   @media 块里对 td[data-label] 的 width: auto）。 */
#grid td[data-label="备注"] { width: 1%; }
#grid input.note { width: 132px; text-align: left; }

/* 备注表头跟着它那一列的内容左对齐。上面 `th, td { text-align: right }` 是给
   金额列写的——表头右对齐正好压在数字末位上，一列数字扫下来是一条线。备注
   装的是自由文本、从左起排，表头还右对齐就会飘到列的另一头，标签和它命名的
   东西对不上。三张表（资产负债 / 固定支出 / 大额开销）的备注共用这一条。 */
th.c-note { text-align: left; }

/* 分类标签：曾经是绝对定位、挂在表格左框外的一枚窄标签——「非流动性债务」
   五个字塞进固定 6em 宽的盒子，justify 撑满导致字与字之间的间距被拉开、
   短一点的标签又会硬换行，整排标签在视觉上读成竖排色带，是这张表最吵的
   部分。改回文档流：不再需要 #grid 专属的覆盖规则，直接吃站内其它表格共用
   的 .group-head/.group-head.cat-* 规则（见上面「分类色带」一节）——背景色、
   左侧色条、加粗文字都已经在那儿定义好了，#grid 和汇总/规划页现在是同一
   套处理，不用再各写一份。 */

/* 模块大标题：资产 / 负债。深色横带把表格切成两个模块，区别于上面的分类标签。 */
#grid .module-head th { background: var(--solid-bg); color: var(--solid-ink); text-align: left;
                        font-size: 14px; font-weight: 700; letter-spacing: .35em;
                        padding: 6px 12px; border-bottom: none; box-shadow: none; }

/* 行状态点：空心=还没抄，绿=抄过了，琥珀=波动超 20% 却没填流转。
   最后这一态过去只在点了保存之后以一整框文字出现，而它在打完那一格的瞬间就
   已经知道了；标在行上还省掉「照着账户名回表里找那一行」这一步。
   小圆点放在分类色带（td:first-child 的 inset box-shadow）右边，两者不打架。 */
#grid tr[data-account-id] td:first-child { padding-left: 28px; position: relative; }
#grid tr[data-account-id] td:first-child::after {
  content: ""; position: absolute; left: 12px; top: 50%; transform: translateY(-50%);
  width: 7px; height: 7px; border-radius: 50%;
  border: 1.5px solid var(--line); background: transparent; }
#grid tr.is-filled td:first-child::after  { background: var(--green); border-color: var(--green); }
#grid tr.is-flagged td:first-child::after { background: var(--amber); border-color: var(--amber); }
/* 抄过的账户名退成次要色：还没抄的那几行才是眼睛要找的 */
#grid tr.is-filled td:first-child { color: var(--muted); }

/* ---------- 「变化」列：Δ 金额 + 环比，上下两行 ---------- */
/* 一个「+8.02%」回答不了「多了多少钱」，而盘点要对上的恰恰是后者——填了流转的
   行上 Δ 等于 0，才是「这笔变动已经被流转解释干净了」的确证。
   分工：Δ 给所有算得出的行，百分比仍旧只给金融资产（现金天然波动、房产两千五百万
   的 0.1% 没有意义）。

   两个数上下叠，都靠 td 自带的 text-align:right 贴右缘。原来是并排一行
   （flex + justify-content:flex-end），Δ 的右缘取决于它右边还有没有那枚百分比：
   有百分比的金融资产行被往左顶、藏了百分比的现金/房产行贴着右缘、小计行又是第三个
   位置——同一列三种右缘，和左边的「期初」列、表头的「变化」全对不上。叠成两行之后
   两个数各占一整行，右缘只能落在单元格内容区的右边界，也就是那条竖线上。 */
#grid td.mom, #grid td.subtotal-mom { line-height: 1.35; }
.delta-abs { display: block; font-variant-numeric: tabular-nums; white-space: nowrap; }
/* 第二行的百分比是从属信息，比金额小一号、行高压紧，两行合起来不比别的格子高 */
.mom-pct { display: block; font-size: 12px; line-height: 1.3;
           font-variant-numeric: tabular-nums; white-space: nowrap; }
/* display:block 不写这一条会压过 [hidden] 自带的 display:none —— 非金融资产的
   行会冒出一个空行。同一个坑 .draft-bar / .sum-detail 都踩过。 */
.mom-pct[hidden] { display: none; }

/* 方向箭头。放在数字左边而不是右边：数字的右缘是这一列的对齐基准，箭头挤到
   右边就会把它顶开，一列数字重新变得参差。
   Δ 为 0 不出箭头——它说的是「这一行已经对上了」，那是个结论，没有方向。 */
.delta-abs::before { font-size: 9px; margin-right: 5px; vertical-align: 1px; opacity: .75; }
.is-up .delta-abs::before { content: "\25B2"; }
.is-down .delta-abs::before { content: "\25BC"; }

/* 涨跌只改文字颜色和字重，没有底色——底色块会在一列数字里砸出几个大小不一的色斑，
   而这一列真正要读的是数字本身。
   红涨绿跌、加粗只给金融资产（连同它那行小计），和百分比是同一条老规矩：现金天然
   大幅波动、房产两千五百万的 0.1%、负债的本金增减都不是盈亏，给它们上色等于
   把记账差额说成赚了赔了。
   门槛写在 CSS 这一侧（靠 tr.cat-financial 筛），方向 class 由模板和 grid.js
   一视同仁地打在 td 上——两边都不必再写一遍「只有金融资产」这条规则，也就没有
   第三处能和它分叉。 */
#grid tr.cat-financial td.is-up .delta-abs,
#grid tr.cat-financial td.is-up .mom-pct { color: var(--up); }
#grid tr.cat-financial td.is-down .delta-abs,
#grid tr.cat-financial td.is-down .mom-pct { color: var(--down); }
/* 金融资产行的「变化」两个数加粗：涨跌幅是这一页要盯的数字。font-weight 打在
   span 上而不是 td 上：td.is-zero 给零值退成 400 的那条规则选择器短一截，这里
   类更多优先级更高，零值的金融行同样加粗，不用在意声明先后。 */
#grid tr.cat-financial td.mom .delta-abs,
#grid tr.cat-financial td.mom .mom-pct,
#grid tr.cat-financial td.subtotal-mom .delta-abs,
#grid tr.cat-financial td.subtotal-mom .mom-pct { font-weight: 600; }
/* Δ 为 0 退成次要色：它说的是「这一行已经对上了」，没什么可看的 */
#grid td.is-zero .delta-abs, #grid td.is-zero .mom-pct { color: var(--muted); font-weight: 400; }

/* 期末框里的幽灵值是上月余额（模板给的 placeholder）。要比真值淡得多，
   否则一眼看过去分不清哪些抄过、哪些只是回声。 */
#grid input.closing::placeholder { color: var(--muted-2); font-weight: 400; }

#grid .group-total td { font-weight: 700; }
/* 小计行末尾那格占位（对齐用，见模板注释）。窄屏下整行重排成卡片，一个既没有
   内容也没有列名的空格子会变成一条空白带——收掉。 */
#grid .group-total .cell-pad { padding: 0; }
#grid tfoot td { background: var(--bg-sunken); font-size: 15px; border-bottom: none; }
#grid tfoot tr:last-child td { background: var(--accent-soft); color: var(--accent); font-size: 17px; }

/* ---------- KPI ---------- */
.kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 14px; }
.kpi { padding: 16px 18px; background: var(--bg); border: 1px solid var(--line); border-radius: 12px;
       border-top: 3px solid var(--muted); }
.kpi-label { display: block; color: var(--muted); font-size: 12px; }
.kpi-value { display: block; font-size: 24px; margin-top: 4px; font-variant-numeric: tabular-nums;
             letter-spacing: -0.02em; }
.kpi:nth-child(2) { border-top-color: var(--c-financial); }
.kpi:nth-child(3) { border-top-color: var(--c-liability); }
.kpi:nth-child(4) { border-top-color: var(--c-property); }
.chart { height: 340px; }

/* ---------- 横向滚动表（年度汇总 / 规划） ---------- */
.scroll-x { overflow-x: auto; border: 1px solid var(--line); border-radius: 12px; background: var(--bg); }
.scroll-x table { width: max-content; min-width: 100%; }
.scroll-x th, .scroll-x td { border-right: 1px solid var(--line); }
.scroll-x th:last-child, .scroll-x td:last-child { border-right: none; }

/* 第一列固定，横向滚动时账户名始终可见 */
.scroll-x th:first-child, .scroll-x td:first-child {
  position: sticky; left: 0; z-index: 2; background: var(--bg);
  border-right: 1px solid var(--line); min-width: 150px; white-space: nowrap;
}
/* top 必须是 0，不能改成 var(--nav-h)「让开吸顶导航」：.scroll-x 因为
   overflow-x: auto 自己就是滚动容器，表头的 sticky 是相对这个容器定位的，不是
   相对视口——它本来就不会滑到导航底下。给非零值只会把表头往容器里推那么多像素，
   直接压住第一行数据（实测推了 52px）。 */
.scroll-x thead th { position: sticky; top: 0; z-index: 3; }
.scroll-x thead th:first-child { z-index: 4; }
.scroll-x .group-total td:first-child { background: var(--bg-soft); }
.scroll-x tfoot td:first-child { background: var(--bg-sunken); }

.acct { display: flex; align-items: baseline; gap: 8px; white-space: nowrap; }
.acct .hint { font-size: 11px; }

.summary tfoot td { background: var(--bg-sunken); font-weight: 600; border-bottom: none; }
.summary tfoot tr:last-child td { background: var(--accent-soft); color: var(--accent); font-size: 15px; }
.summary td.empty { color: var(--muted-2); }

/* ---------- 比率条形图 ---------- */
/* 四项体检从「四行各占一整行」改成两列栅格、每项一行——旧版名称/数值/健康线
   全部靠左堆成一坨，四条满宽的色带上下叠着，读起来像四条互不相干的进度条，
   不像「一张体检报告」。两列之后每项自己更窄，也更接近「一行说完一件事」。 */
.ratio-list { display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px 28px; }
.ratio-head { display: flex; gap: 8px; align-items: baseline; margin-bottom: 6px; }
/* 名称在左、健康线紧跟其后（次要信息，用 .hint 的淡色小字），数值用 order
   顶到最右边——DOM 顺序不用变（模板里名称/数值/健康线本来就是这个次序，
   value 挂 data-sum 之类的钩子都还在原地），只是视觉重排。 */
.ratio-head span:first-child { font-weight: 600; order: 1; }
.ratio-head .hint { order: 2; font-family: var(--mono); font-size: 11px; }
.ratio-head strong { order: 3; margin-left: auto;
                      font-family: var(--mono); font-size: 16px; font-variant-numeric: tabular-nums; }
/* 比率条改细、改哑：原型是「细条 + 淡色区间 + 一根清晰的刻度线」，旧版又粗又艳
   的三色条会把「达标区间」读成三段互不相干的色块，比率本身反而被埋没。区间色
   用 color-mix 把状态色按 42% 混进 --bg-sunken，两套主题下都跟着 token 走，
   不用再分别定义一份深色版本。 */
.ratio-bar { position: relative; display: flex; height: 8px; border-radius: 4px;
             overflow: hidden; background: var(--bg-sunken); }
.ratio-band { display: block; height: 100%; }
.band-green { background: color-mix(in srgb, var(--green) 42%, var(--bg-sunken)); }
.band-amber { background: color-mix(in srgb, var(--amber) 42%, var(--bg-sunken)); }
.band-red   { background: color-mix(in srgb, var(--red) 42%, var(--bg-sunken)); }
.ratio-marker { position: absolute; top: -3px; width: 3px; height: 14px;
                background: var(--fg); border-radius: 2px; transform: translateX(-50%);
                box-shadow: 0 0 0 2px var(--bg); }

/* 固定支出的年度进度条。参照线是「时间过了多少」，条长是「钱花了多少」，
   条超过线 = 花得比时间快。放在这里、640px 断点之前：这份文件的窄屏覆盖
   写在文件中部（见 #fixed-table 那几段），同优先级下后写的规则会整段压掉
   先写的——基础规则必须先出现，.summary-card 那段踩过同一个坑。 */
.pbar { display: flex; align-items: center; gap: 8px; }
.pbar .track { position: relative; flex: 1; min-width: 62px; height: 7px;
               border-radius: 4px; background: var(--bg-sunken); overflow: hidden; }
.pbar .fill { display: block; height: 100%; background: var(--green); }
.pbar .fill.status-ahead { background: var(--amber); }
.pbar .fill.status-over { background: var(--red); }
.pbar .fill.status-unplanned { background: var(--line-strong); }
/* 已知缺陷，暂不修：这条竖线跟被删掉的 .exec-mark 有同一个裁切问题。这条
   规则本身没写 left，位置由模板按行内联 style="left: {{ elapsed_pct }}%"
   给出，定的是竖线左边缘而不是居中——elapsed_pct 越接近 100，2px 宽的盒子
   就越往 .track 右边界外挤，等 elapsed_pct 到 100 时整个盒子已经完全挪到
   0–100% 的可见区间之外，被 .track 的 overflow:hidden 整根裁没，年末参照
   线直接消失，不是像素级的半裁。.exec-mark 当初把竖线位置换成居中
   （相当于 translateX(-50%)）之后用
   `left: clamp(0px, calc(var(--mark-pos) - 1px), calc(100% - 2px))`
   修过同一类问题（完整实现见 `git show 473f380:static/css/app.css`），但
   那套修法要求竖线位置走 CSS 变量（`--mark-pos`）而不是内联 `left`——而
   `.pbar` 同时被 templates/month.html 的月度进度条复用，要挪过去就得改
   month.html，直接撞上本计划「/month 页一个字不改」的硬约束，不划算，
   先记账。 */
.pbar .now { position: absolute; top: -2px; width: 2px; height: 11px; background: var(--muted); }
.pbar .pct { font-variant-numeric: tabular-nums; font-size: 11.5px; min-width: 34px;
             text-align: right; }

@media (max-width: 640px) {
  main { padding: 16px 12px 48px; }
  .nav { gap: 0; padding: 0 8px; }
  .nav a { padding: 12px 10px; }

  /* ---- 填报网格重排成「一个账户一张卡」（见评审 G3）----
     月度盘点是手机场景：挨个打开十几个 App 抄余额。6 列表格在 375px 上装不下，
     而横向滚动一张要逐格输入的表比装不下还糟。这里把表格整体改成块级流，
     每个账户行变成一张卡：账户名当标题，其余格子用 data-label 自带列名。
     只改 display 和定位，不动任何 class 和 DOM 结构——grid.js 靠
     tr[data-account-id]、.closing、.subtotal 这些选择器工作，重排对它透明。 */
  #grid, #grid tbody, #grid tfoot, #grid tr, #grid td, #grid th { display: block; }
  #grid thead { display: none; }                 /* 列名改由每格的 data-label 承担 */
  #grid { border: none; border-radius: 0; background: transparent; }

  #grid tr[data-account-id] {
    background: var(--bg); border: 1px solid var(--line); border-radius: 10px;
    padding: 10px 12px; margin-bottom: 8px;
  }
  /* 账户名：卡片标题。第一格没有 data-label，靠它和其余格子区分开 */
  #grid tr[data-account-id] td:first-child {
    font-weight: 600; font-size: 15px; border-bottom: 1px solid var(--line);
    padding: 0 0 8px; margin-bottom: 8px; box-shadow: none;
  }
  /* 其余格子：左列名、右数值的一行 */
  #grid td[data-label] {
    display: flex; align-items: center; justify-content: space-between; gap: 12px;
    padding: 3px 0; border-bottom: none; background: transparent;
    /* 撤掉宽屏给备注格的 width: 1%（那是让表格列按内容收）——这里每格都是卡片里
       的一整行，1% 会把它压成一条竖缝。 */
    width: auto;
  }
  #grid td[data-label]::before {
    content: attr(data-label); flex: none;
    color: var(--muted); font-size: 12px; font-weight: 500;
  }
  #grid td[data-label] input { width: 60%; max-width: 220px; }
  /* 期末是这一页真正要输入的字段，在卡片里给它更大的点击目标 */
  #grid tr[data-account-id] td[data-label="期末"] input {
    font-size: 16px;      /* < 16px 会让 iOS Safari 在聚焦时自动放大整页 */
    padding: 8px 10px;
  }
  /* 变化格在窄屏要同时容下列名和上下两行数字，td[data-label] 那条 flex 规则
     排不出来（三个子项会被摊成一行）。改成两列 grid：列名占左边第一行，Δ 和
     百分比都钉在第二列，于是自然落成上下两行，右缘照旧对齐。 */
  #grid td.mom, #grid td.subtotal-mom {
    display: grid; grid-template-columns: auto 1fr; column-gap: 12px; align-items: baseline;
  }
  #grid td.mom::before, #grid td.subtotal-mom::before { grid-column: 1; grid-row: 1; }
  #grid td.mom .delta-abs, #grid td.subtotal-mom .delta-abs,
  #grid td.mom .mom-pct, #grid td.subtotal-mom .mom-pct { grid-column: 2; }
  #grid td.mom { min-height: 28px; }

  /* 状态点：宽屏那份 padding-left 被上面的卡片标题规则整个覆盖掉了，在这里
     重新给。卡片标题是块级的，圆点改成挂在文字基线上而不是行高正中。 */
  #grid tr[data-account-id] td:first-child { padding-left: 18px; }
  #grid tr[data-account-id] td:first-child::after { left: 0; top: 11px; transform: none; }


  /* 分类标签行现在宽屏窄屏共用同一份（见上面「分类标签」注释），这里只给
     窄屏卡片化布局补一点组间距——th 在宽屏是表格单元格，margin 天然无效，
     这条规则只在下面 display:block 生效之后才起作用，两边不用分头写。 */
  #grid .group-head th { margin-top: 14px; }

  #grid .module-head th { border-radius: 8px; margin: 16px 0 4px; letter-spacing: .2em; }

  #grid .group-total .cell-pad { display: none; }

  /* 小计行与合计行：同样是「标签 + 数值」的一行，但用底色和边框和账户卡区分开 */
  #grid .group-total { background: var(--bg-soft); border: 1px solid var(--line);
                       border-radius: 10px; padding: 8px 12px; margin-bottom: 12px; }
  #grid .group-total td:first-child { font-weight: 700; padding: 0 0 4px; box-shadow: none; }
  #grid tfoot tr { display: flex; justify-content: space-between; align-items: baseline;
                   background: var(--bg-sunken); padding: 8px 12px; }
  #grid tfoot tr:first-child { border-radius: 10px 10px 0 0; }
  #grid tfoot tr:last-child { border-radius: 0 0 10px 10px; }
  #grid tfoot td { border-bottom: none; padding: 0; }

  /* 固定支出与大额开销两张表同样重排 */
  #fixed-table, #fixed-table tbody, #fixed-table tr, #fixed-table td, #fixed-table th,
  .flow-table, .flow-table tbody, .flow-table tr, .flow-table td, .flow-table th { display: block; }
  #fixed-table thead, .flow-table thead { display: none; }
  #fixed-table tr.fixed-row, .flow-table tr.flow-row {
    border: 1px solid var(--line); border-radius: 10px; padding: 10px 12px; margin-bottom: 8px;
  }
  #fixed-table tr.fixed-row td:first-child { font-weight: 600; border-bottom: none; padding: 0 0 6px; }
  #fixed-table td[data-label], .flow-table td[data-label] {
    display: flex; align-items: center; justify-content: space-between; gap: 12px;
    padding: 3px 0; border-bottom: none;
  }
  #fixed-table td[data-label]::before, .flow-table td[data-label]::before {
    content: attr(data-label); flex: none; color: var(--muted); font-size: 12px; font-weight: 500;
  }
  #fixed-table td[data-label] input, .flow-table td[data-label] input,
  .flow-table td[data-label] select { width: 60%; max-width: 220px; font-size: 16px; }
  #fixed-table .fixed-amount { width: 60%; }
  .flow-table tr.flow-row td:last-child { padding-top: 8px; text-align: right; }
  #fixed-table .group-head th { padding: 12px 0 6px; }

  /* 固定支出页主表（#fixed-main）同样重排——跟 #fixed-table 是同一种
     「类目一行一张卡」的表，只是长在另一个页面上，之前漏进了这份清单：每格
     的 data-label 都写了，但没人把 display 真的切成卡片，7 列在 375px 上
     整表溢出视口，年度进度那一列（这张表存在的意义）直接被推到视口外看
     不见。多年矩阵（.matrix-fold 里的 #planning，多年份、天生就宽）不在此列——
     那张表窄屏下继续走 .scroll-x 的横向滚动，跟历史页的账户×月矩阵是同一个
     处理方式，不该也切成卡片。
     .scroll-x-now：#fixed-main 外面包着 .scroll-x（宽屏横向滚动用），窄屏卡片
     化之后这层壳反而碍事——sticky 表头/首列在整表拆成竖排卡片后没有意义，
     min-width:100% 会把每张卡片撑到跟原表一样宽，外层白底圆角边框还会和
     卡片自己的边框叠成双层框。这里连壳带内容一起解除，模板里给这个 .scroll-x
     多加了一个类名，只作用于这一处，不影响多年矩阵那两个 .scroll-x。 */
  .scroll-x-now { overflow-x: visible; border: none; border-radius: 0; background: transparent; }
  #fixed-main, #fixed-main tbody, #fixed-main tr, #fixed-main td, #fixed-main th {
    display: block; width: auto; min-width: 0;
  }
  #fixed-main thead { display: none; }
  #fixed-main tr[data-category-id] {
    background: var(--bg); border: 1px solid var(--line); border-radius: 10px;
    padding: 10px 12px; margin-bottom: 8px;
  }
  #fixed-main tr[data-category-id] td:first-child {
    font-weight: 600; border-bottom: none; padding: 0 0 6px; position: static;
  }
  #fixed-main td[data-label] {
    display: flex; align-items: center; justify-content: space-between; gap: 12px;
    padding: 3px 0; border-bottom: none; background: transparent;
  }
  #fixed-main td[data-label]::before {
    content: attr(data-label); flex: none; color: var(--muted); font-size: 12px; font-weight: 500;
  }
  /* 左内边距要和卡片内容对齐：这条色带是通栏的，padding-left 为 0 时文字落在
     x=15（只让开 3px 的色条），而卡片自己有 12px 内边距、里面的标签从 x=25
     起排——差这 10px，分类名看上去就是贴着屏幕边的。 */
  #fixed-main .group-head th { padding: 12px 0 6px 12px; }

}

/* ---------- 规划页模块配色 ---------- */
.mod-family td:first-child { box-shadow: inset 3px 0 var(--c-property); }
.mod-child td:first-child  { box-shadow: inset 3px 0 var(--c-receivable); }
.group-head.mod-family th  { box-shadow: inset 3px 0 var(--c-property); }
.group-head.mod-child th   { box-shadow: inset 3px 0 var(--c-receivable); }
#planning input { width: 100%; min-width: 96px; border-color: transparent; background: transparent; }
#planning input:hover { border-color: var(--line); }
#planning input.cell { background: var(--bg); border-color: var(--line); }
#planning tfoot td { background: var(--accent-soft); color: var(--accent); font-weight: 700; border-bottom: none; }

/* ---------- 盘点页吸顶结果条 ---------- */
/* 头条只有两个数：结余和净资产。盘点的核心校验是拿前者解释后者的变化，而它们
   过去一个在页首、一个在资产表表尾，隔着九十行永不同屏。条子吸在视口顶上，
   十三个账户抄到哪一行都看得见，保存按钮也跟着走。
   sticky 的父级是 <main>（很高），不是任何一张卡片——放进卡片里，卡片一滚出
   视口就把条子一起带走了。 */
.sum-bar { position: sticky; top: var(--nav-h); z-index: 20; margin-top: 16px;
           display: flex; align-items: center; gap: 20px; flex-wrap: wrap;
           background: var(--bg); border: 1px solid var(--line); border-radius: 12px;
           padding: 12px 18px; box-shadow: 0 1px 3px rgba(15, 23, 42, 0.06); }
/* 四格头条数字改成 4 列栅格、自己撑满结果条里除进度条/保存按钮之外的
   全部宽度——原来只是普通 flex 项，各自按内容自身宽度排开，四格挤在结果条
   左侧约 40%，右边一大片空着，进度条和按钮反而跟着往左贴到数字后面，不像
   「常驻可见的头条 + 独立的操作区」，倒像是数字不小心散在一边。flex-basis
   给一个不算大的下限（320px）：宽度不够时也不至于把结果条硬撑爆，会正常
   wrap 到下一行（配合窄屏 2×2 的既有断点）。
   四列不是等分：结余/净资产是带千分位的完整元数字（这个家庭净资产是八位数，
   「25,159,226.00」比「投资收益率」那一栏的「+2.06%」长出一大截），等分
   4 列会把前两列挤得比内容还窄，数字会溢出格子、糊到下一列上——实测糊出
   过。1.6fr/1.8fr/1fr/1fr 把多出来的宽度让给前两列。 */
.sum-bar .sum-figures { flex: 1 1 360px; display: grid;
                         grid-template-columns: 1.5fr 1.7fr 1fr 1.2fr; gap: 6px 20px; }
.sum-bar .sum-fig strong { font-size: 20px; }
/* 进度：走条给出「还差多少」的形状，计数给出确切数字，按钮把光标送到下一个空框。
   走条刻意做得很细——它是余光扫的东西，不该和旁边两个头条数字抢注意力。 */
.sum-progress { margin: 0; color: var(--muted); font-size: 13px;
                display: flex; align-items: center; gap: 10px; }
.sum-progress b { color: var(--fg); font-weight: 600; }
.sum-progress-sep { margin: 0 2px; }
.prog-rail { width: 84px; height: 6px; border-radius: 3px; flex: none;
             background: var(--bg-sunken); overflow: hidden; }
.prog-fill { display: block; height: 100%; background: var(--green);
             transition: width .18s ease-out; }
.prog-text { white-space: nowrap; }
#jump-unfilled { font-size: 12px; padding: 4px 10px; white-space: nowrap; }
.sum-bar #save { margin-left: auto; }
/* 结余是唯一带方向的数：正数绿、负数红 */
.sum-bar .sum-net strong { color: var(--green); }
.sum-bar.is-negative .sum-net strong { color: var(--red); }

/* 告警/保存状态紧跟在条子下面，就在保存按钮的正下方 */
.sum-bar + .warnings { margin-top: 10px; }

/* 净资产环比：静态挂在吸顶条上，不参与 entry-summary.js 的实时重算
   （见 month.html 里那条注释——它比的是「和上月」，不随本月编辑变化）。 */
.sum-fig .sum-delta { display: block; font-size: 12px; color: var(--muted); margin-top: 2px; }

/* 投资收益率/资产重估两格下面那行小字（绝对值、或一句说明）：和 .sum-delta
   同一个视觉分量，独立成一个类是因为它不总是百分比——资产重估那格下面挂的是
   一句静态说明文字，不是数字，共用 .sum-delta 会让类名和内容脱节。 */
.sum-fig .sum-sub { display: block; font-size: 12px; color: var(--muted); margin-top: 2px; }

/* 看板并过来的总资产/总负债，紧跟在吸顶条下面的一小行卡片。复用 .kpis/.kpi
   的样式（原来是看板整页的四格），这里只有两格，加个修饰类收一收顶部间距。 */
.kpis-compact { margin-top: 16px; }

/* ---------- 财务比率体检（可折叠） ---------- */
/* 用 <details> 而不是 .panel + <h2>：这块是抄完数据之后的体检报告，允许收起来
   给输入区让地方，跳锚点强制常驻的那条规矩（见分幕锚点注释）不适用于它——
   它本来就不在分幕导航里。 */
.check { margin-top: 16px; background: var(--bg); border: 1px solid var(--line);
         border-radius: 12px; padding: 18px 20px; }
.check summary { font-size: 15px; font-weight: 600; cursor: pointer; }
.check[open] summary { margin-bottom: 12px; }

/* ---------- 历史页：账户 × 月矩阵（可折叠） ---------- */
/* 十三行十二列在 375px 上读不了，是合并到历史页之后手机端最大的一处拖累。
   默认展开/收起由 templates/history.html 底部的脚本按视口宽度设一次
   （<details open> 是属性不是样式，CSS 媒体查询驱动不了它），这里只管样式。
   summary 撑满整行且有明显的点击态，是手机上唯一需要点这块的地方。 */
.matrix { margin-top: 28px; background: var(--bg); border: 1px solid var(--line);
          border-radius: 12px; padding: 18px 20px; }
.matrix summary { font-size: 15px; font-weight: 600; cursor: pointer; }
.matrix[open] summary { margin-bottom: 12px; }
.matrix .scroll-x { margin: 0; }

/* ---------- 固定支出页：多年规划矩阵（可折叠） ---------- */
/* 矩阵交出了月基准编辑与增删归档（见 templates/fixed.html、static/js/fixed.js
   的入口唯一化），页面功能已经完整长在主表上——矩阵默认折起，只在要批量填
   格子、核对多年占比时才展开。 */
.matrix-fold { margin-top: 28px; padding: 0 18px; background: var(--bg);
               border: 1px solid var(--line); border-radius: 12px; }
.matrix-fold > summary { padding: 14px 0; font-size: 14px; font-weight: 600; cursor: pointer; }
.matrix-fold > summary .hint { margin-left: 8px; font-weight: 400; }
.matrix-fold .body { padding-bottom: 18px; }
.matrix-fold .scroll-x { margin: 0; }

/* ---------- 资产配置堆叠条 ---------- */
/* 颜色复用资产表左侧色条同一套分类色（--c-*），一眼看出「这条堆叠条里的紫色
   和上面表格里的『金融资产』书签是同一件事」。 */
.alloc-bar { display: flex; height: 12px; border-radius: 6px; overflow: hidden;
             background: var(--bg-sunken); }
.alloc-seg { display: block; height: 100%; }
.alloc-seg.cat-cash       { background: var(--c-cash); }
.alloc-seg.cat-financial  { background: var(--c-financial); }
.alloc-seg.cat-investment { background: var(--c-investment); }
.alloc-seg.cat-property   { background: var(--c-property); }
.alloc-seg.cat-receivable { background: var(--c-receivable); }
.alloc-legend { list-style: none; margin: 10px 0 0; padding: 0; display: flex;
                flex-wrap: wrap; gap: 6px 18px; font-size: 13px; color: var(--muted); }
.alloc-legend b { color: var(--fg); font-weight: 600; }
.alloc-legend .swatch.cat-cash       { background: var(--c-cash); }
.alloc-legend .swatch.cat-financial  { background: var(--c-financial); }
.alloc-legend .swatch.cat-investment { background: var(--c-investment); }
.alloc-legend .swatch.cat-property   { background: var(--c-property); }
.alloc-legend .swatch.cat-receivable { background: var(--c-receivable); }

/* ---------- 分幕锚点 ---------- */
/* 跳转而不是隐藏：资产变动与收支互为校验，拆 tab 藏掉一半等于砍掉盘点的意义。 */
/* ---------- 月份轴 ---------- */
/* 十二格一条横轨，任意近月一次点击直达。轨道本身可横滑（窄屏放不下十二格），
   grid.js 载入后把当前月滚到可视中央——否则手机上打开就是一条从 10 月开始、
   看不见「现在」的轨道。
   两端的箭头不是滑动按钮而是真跳转（各跳出窗口一格），于是十二格不是上限，
   一屏一屏能一直往回翻。 */
/* 一次只露 6 格：轨道宽度按格子数算死，多出来的月份横滚进来。这个 6 必须和
   routes/entry.py 的 RAIL_WINDOW 一致——箭头翻的是「露出来的那么多个月」，
   两处对不上就会翻半格。
   格子本身在这一轮改宽了（46px→68px，窄屏 42px→52px）：这条轨过去挤在标题
   右边一条窄槽里，格子小得只勉强够点，年份数字常年被 visibility:hidden 藏着
   看不出「够不够大」。现在轨道挪到标题下面独占一整行（见 month.html 的
   markup），有地方放大了——这是这页仅次于打字的第二常用操作，理应更好点、
   更好读。 */
.month-rail { --rail-window: 6; --chip-w: 68px; --rail-gap: 5px; --rail-pad: 4px;
              display: flex; align-items: center; gap: 8px;
              margin: 2px 0 20px; }

/* 分段控件：整条轨是一块凹下去的底，格子本身没有边框，只有当前月浮起来成一枚
   白药丸。之前是六个一模一样的描边方框加两个方框箭头——八个矩形等重排开，
   没有主次，眼睛落不到「现在在哪个月」上。

   position: relative 不是为了定位，是为了让轨道成为格子的 offsetParent。
   不写的话 chip.offsetLeft 是相对 body 量的，里面含着轨道自己在页面上的横向
   位置（实测 52px，还随视口宽度变），而 month-rail.js 拿它和 scrollLeft 比——
   于是「滚到当前月居中」和「最左边那格标年份」都整体偏五十几像素，年份会落在
   一个只露一半的格子上。

   宽度＝6 格 + 两侧内边距。写死而不是 flex: 1：抻满正文宽度的话一屏会露出
   二十几格，「一次 6 个月」就没了。flex 收缩仍然留着（0 1 auto + min-width: 0）：
   窄屏上挤不下时宁可少露一格，也不能把箭头顶出屏幕。 */
.rail-track { position: relative;
              display: flex; gap: var(--rail-gap); overflow-x: auto;
              flex: 0 1 auto; min-width: 0;
              width: calc(var(--rail-window) * var(--chip-w)
                          + (var(--rail-window) - 1) * var(--rail-gap)
                          + 2 * var(--rail-pad));
              background: var(--bg-sunken); border-radius: 11px;
              padding: var(--rail-pad);
              scrollbar-width: none;
              /* mandatory 而不是 proximity：proximity 只是「够近才吸」，翻屏和
                 拖动都可能停在半格上——一屏于是变成 5 个整格加两个半格，说好的
                 6 个月就没了。start 而不是 center：要的是最左边那格和轨道左缘
                 齐平，正好露出整整 6 格。 */
              scroll-behavior: smooth; scroll-snap-type: x mandatory;
              /* 拖轨道的时候别顺手把月份数字选中成一片蓝 */
              user-select: none; -webkit-user-select: none; }
.rail-track::-webkit-scrollbar { display: none; }
.rail-chip { scroll-snap-align: start; }

/* 拖动中：吸附和平滑都得让开。每划过一格就被吸回去一次的话手感是一顿一顿的，
   而 smooth 会让 scrollLeft 的赋值滞后于指针，拖起来像在拉皮筋。
   松手撤掉这个类，吸附随即恢复，浏览器自己滑到最近一格。 */
.rail-track.is-dragging { scroll-snap-type: none; scroll-behavior: auto;
                          cursor: grabbing; }

/* 还能往哪边滚，哪边的内壁压一道很淡的阴影。凹底本身已经把边界画清楚了，所以
   这里只要一点暗示，不能再用会把圆角一起吃掉的渐隐遮罩。判定见 paintEdges。 */
.rail-track.can-left { box-shadow: inset 9px 0 8px -9px rgba(15, 23, 42, .22); }
.rail-track.can-right { box-shadow: inset -9px 0 8px -9px rgba(15, 23, 42, .22); }
.rail-track.can-left.can-right { box-shadow: inset 9px 0 8px -9px rgba(15, 23, 42, .22),
                                             inset -9px 0 8px -9px rgba(15, 23, 42, .22); }

/* 箭头只留人字符号，不再是方框：它们是这条轨的配角，和月份格等重会让一行里
   出现八个同样大小的矩形。 */
.rail-step { flex: none; display: flex; align-items: center; padding: 0 5px;
             background: none; border: 0; cursor: pointer;
             color: var(--muted-2); font-size: 15px; line-height: 1; }
.rail-step:hover:enabled { color: var(--accent); }
.rail-step:disabled { color: var(--line-strong); cursor: default; }

/* 68px 宽、上下多留一点内边距：够得上舒适的触摸目标，也让年份和月份两行数字
   之间有呼吸感，不再挤成一坨。flex: none 是必须的——放进可滚动的 flex 轨道
   后，不写会被挤扁成一排细线。 */
.rail-chip { flex: none; width: var(--chip-w); padding: 7px 0 6px;
             text-align: center; text-decoration: none; color: var(--fg);
             font-family: var(--mono); border-radius: 8px; transition: background .12s; }
/* 悬浮态用当前主题的表面色而不是写死的白——深色主题下凹底是深色，糊一层
   写死的白色悬浮态会在暗底上炸出一块亮斑。 */
.rail-chip:hover { background: color-mix(in srgb, var(--bg) 65%, transparent); }
.rail-year { display: block; height: 13px; font-size: 11px; line-height: 13px;
             color: var(--muted); visibility: hidden; }
/* 露年份的只有每年的一月和当前最左边那一格。visibility 而不是 display：那 11px
   得一直占住，否则露出年份的格子会比邻居高一截，整条轨跟着抖。 */
.rail-chip.is-jan .rail-year,
.rail-chip.is-year-head .rail-year { visibility: visible; }
/* 月份数字自己分深浅：盘过的写实，没盘的淡下去。 */
.rail-month { display: block; font-size: 18px; font-weight: 600; line-height: 1.3;
              font-variant-numeric: tabular-nums; color: var(--muted); }
.rail-chip.is-filled .rail-month { color: var(--fg); }

/* 圆点答「哪几个月还没盘」。只给盘过的月份画点——给空月份也画一个灰点，等于
   用一个记号去说「这里什么都没有」，六个格子底下就多了六个要读的东西。 */
.rail-dot { display: block; width: 4px; height: 4px; margin: 2px auto 0;
            border-radius: 50%; background: transparent; }
.rail-chip.is-filled .rail-dot { background: var(--green); }

.rail-chip.is-active { background: var(--bg);
                       box-shadow: 0 1px 3px rgba(15, 23, 42, .10),
                                   0 0 0 1px color-mix(in srgb, var(--accent) 35%, transparent); }
.rail-chip.is-active .rail-month { color: var(--accent); }
.rail-chip.is-active.is-filled .rail-dot { background: var(--accent); }
/* 未来的月份不藏（下个月 1 号就要盘它），只是再淡一层 */
.rail-chip.is-future .rail-month { color: var(--muted-2); }

@media (max-width: 640px) {
  .month-rail { --chip-w: 52px; --rail-gap: 4px; margin: 2px 0 16px; }
}

.anchor-chips { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 14px; }
.anchor-chips a { font-size: 13px; color: var(--muted); text-decoration: none;
                  background: var(--bg); border: 1px solid var(--line); border-radius: 999px;
                  padding: 5px 12px; }
.anchor-chips a:hover { color: var(--accent); border-color: var(--accent); }
/* 滚到哪一幕，哪一枚亮着——三枚灰药丸本身答不上「我现在在哪」 */
.anchor-chips a.is-here { color: var(--accent); border-color: var(--accent);
                          background: var(--accent-soft); font-weight: 600; }
/* 完成勾：这一幕填过了。判定见 entry-assist.js 的 sectionState —— 收入和支出
   是「有一条就算」，只有资产负债要求十三个账户一个不落。 */
.anchor-chips a::after { content: "○"; margin-left: 6px; font-size: 11px; color: var(--muted-2); }
.anchor-chips a.is-done::after { content: "✓"; color: var(--green); }
/* 锚点落点要躲开整摞吸顶元素（导航 + 结果条），否则跳过去第一行正好被压住 */
.sec-head, [id^="sec-"] { scroll-margin-top: calc(var(--nav-h) + var(--bar-h) + 12px); }
.sec-head { font-size: 15px; margin: 28px 0 12px;
            display: flex; gap: 12px; align-items: baseline; flex-wrap: wrap; }
#carry-over { font-size: 12px; padding: 4px 10px; font-weight: 400; }

/* 一条大额开销都没有时整张表收起来，只留那个添加按钮 */
.flow-table.is-empty { display: none; }

/* 「按基准填充」灌进去的值：这一列的 placeholder 本来就是月基准，填完和手打的
   一模一样，一个月后回头分不清哪几笔真去对过账单。着色让它可辨，也让「撤销」
   有明确的作用范围——只清这一批，不碰手填的格子。手改过任何一格就摘掉标记
   （flows.js 的 input 监听）。 */
#fixed-table input.fixed-amount.is-auto {
  color: var(--accent); background: var(--accent-soft);
  border-color: color-mix(in srgb, var(--accent) 35%, transparent); }
#fixed-table input.fixed-amount::placeholder { color: var(--muted-2); }
.auto-filled { font-size: 12px; color: var(--accent); }
.auto-filled[hidden] { display: none; }      /* 见下面 .draft-bar 的同款说明 */
#undo-defaults { font-size: 12px; padding: 3px 8px; color: var(--accent);
                 border-color: color-mix(in srgb, var(--accent) 35%, transparent); margin-left: 4px; }

/* ---------- 保存反馈 toast ---------- */
/* 已保存 / 漏填流转两态浮在视口底部，不参与文档流。它们过去和报错共用页首那个
   .warnings 框，框一出现就把整张资产表往下顶一截，正在核对的那一行跳走——而
   这两条恰恰是最常出现的。报错仍留在框里（要照着改，不能自己消失）。 */
.toast { position: fixed; left: 50%; bottom: 26px; z-index: 60;
         transform: translate(-50%, 16px);
         max-width: min(560px, calc(100vw - 32px));
         background: var(--solid-bg); color: var(--solid-ink); padding: 11px 20px; border-radius: 10px;
         font-size: 13px; line-height: 1.6; box-shadow: 0 10px 30px rgba(15, 23, 42, .28);
         opacity: 0; pointer-events: none; transition: opacity .18s, transform .18s; }
.toast.is-on { opacity: 1; transform: translate(-50%, 0); }
.toast p { margin: 2px 0; }
.toast-warn { background: var(--solid-warn); }

/* ---------- 本地草稿恢复条 ---------- */
/* display:flex 的优先级高于 [hidden] 自带的 display:none —— 不写这一行，草稿条
   会在每次打开页面时都摊开，里面的时间还是空的。.sum-detail 踩过同一个坑。 */
.draft-bar[hidden] { display: none; }
.draft-bar { margin-top: 10px; padding: 9px 14px; border-radius: 8px;
             background: var(--accent-soft);
             border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
             color: var(--accent);
             font-size: 13px; display: flex; gap: 12px; align-items: center;
             flex-wrap: wrap; }
.draft-bar button { font-size: 12px; padding: 4px 10px; }
#draft-restore { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 35%, transparent);
                  font-weight: 600; }

/* ---------- 盘点页支出构成面板 ---------- */
/* 收入/支出两个数在这里，构成条回答「花在固定盘子还是临时开销」，明细饼图默认折叠。 */
.summary-card { margin-top: 16px; padding: 18px 20px 14px; }
.sum-figures { display: flex; gap: 32px; flex-wrap: wrap; }
.sum-fig { display: flex; flex-direction: column; }
.sum-label { color: var(--muted); font-size: 12px; }
.sum-fig strong { font-size: 24px; letter-spacing: -0.02em; margin-top: 2px; }

.sum-split { margin-top: 14px; }
.split-bar { display: flex; height: 8px; border-radius: 4px; overflow: hidden;
             background: var(--bg-sunken); }
.split-bar span { display: block; height: 100%; }
.split-fixed { background: var(--c-financial); }
.split-adhoc { background: var(--c-property); }
.split-legend { display: flex; gap: 18px; flex-wrap: wrap; align-items: baseline;
                margin: 8px 0 0; font-size: 13px; color: var(--muted); }
.split-legend b { color: var(--fg); font-weight: 600; }
.swatch { display: inline-block; width: 9px; height: 9px; border-radius: 2px;
          margin-right: 6px; vertical-align: baseline; }
.swatch-fixed { background: var(--c-financial); }
.swatch-adhoc { background: var(--c-property); }

.sum-actions { display: flex; gap: 10px; margin-top: 14px; }
.sum-toggle { font-size: 13px; color: var(--muted); }
.sum-toggle::after { content: " ▾"; }
.sum-toggle.is-open { color: var(--accent); border-color: var(--accent); }
.sum-toggle.is-open::after { content: " ▴"; }

/* 明细：左环形图、右图例列表。图例本身就是一张可读的表，图挂了也不影响用。 */
/* :not([hidden]) 不能省：display 的优先级高于 hidden 属性自带的 display:none，
   直接写 display:grid 会让两个明细区在首屏就摊开成两片空白（规划页的
   .add-form 踩过同一个坑）。 */
/* 图例不铺满整张卡：名称 flex:1 会把金额推到一千像素外，中间是一片死空白。
   给它一个上限，名称与金额始终待在同一次扫视里。 */
.sum-detail:not([hidden]) { display: grid; grid-template-columns: 200px minmax(0, 440px);
              gap: 24px; align-items: center; justify-content: start; }
.sum-detail { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--line); }
.sum-chart { height: 200px; }
.sum-legend { list-style: none; margin: 0; padding: 0; }
.sum-legend li { display: flex; align-items: baseline; gap: 10px; padding: 3px 0;
                 font-size: 13px; }
.sum-legend-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sum-legend b { font-weight: 600; }
.sum-legend-share { width: 52px; text-align: right; color: var(--muted); }
.sum-legend-empty { color: var(--muted); }

/* 概览卡的窄屏覆盖必须紧跟在上面的基础规则之后：站点那个 @media 断点写在文件
   中部，同优先级下会被这里的基础规则整个压掉（手机上明细区不堆叠、图例被挤成
   只剩数字的残片）。 */
@media (max-width: 640px) {
  /* 窄屏的导航矮 2px，结果条则因为四格数字压成 2×2 再加进度/保存一行而更高——
     两个 token 都要跟着变，否则条子会盖住导航，锚点也会跳错位置。
     这两个值只是 JS 跑起来之前（或者浏览器没有 ResizeObserver）那一瞬间的
     兜底近似——grid.js 的 observeBarHeight() 接管之后，--bar-h 会被换成当前
     这一帧真实量出来的高度，这里写死的数字不再是锚点跳转唯一的依据，只是不
     再需要「每次改结果条都手动重量一次」这件事本身消失了：JS 没跑起来的那一
     瞬间仍然需要一个不太离谱的初始值。 */
  /* 实测导航 48.4px；结果条隐藏了两格数字下面的小字（.sum-sub/.sum-delta）、
     收紧行高之后是 129.0px。一律向上取整——这两个值是锚点跳转的落点基准，
     短一像素，跳过去的分幕标题就被条子压住一像素。改结果条里的格数、控件、
     字号都必须回来重量。 */
  :root { --nav-h: 49px; --bar-h: 130px; }

  /* 吸顶条在 375px 上必须瘦：结余与净资产一排，进度与保存按钮一排。它和导航
     一起常驻屏幕（两者合计约 143px），多占的每一像素都是从输入区借的。 */
  .sum-bar { gap: 6px 16px; padding: 8px 12px; border-radius: 10px; }
  /* .sum-fig 不再写 flex:1——.sum-figures 下面这条把它的子元素从 flex 项改成了
     grid 项，flex 简写对 grid 项不生效，留着是死代码。min-width:0 还留着：
     grid 列不会自动收缩到内容宽度以下，去掉它长数字会把列撑宽顶破两列布局。 */
  .sum-bar .sum-fig { min-width: 0; }
  .sum-bar .sum-fig strong { font-size: 17px; }
  .sum-bar .sum-label { font-size: 11px; }
  .sum-bar .sum-progress { font-size: 12px; }
  .sum-bar #save { padding: 5px 12px; }

  .sum-figures { gap: 0 24px; }
  .sum-fig { flex: 1; min-width: 0; }
  /* 结果条从两格涨到四格（新加了投资收益率与资产重估），375px 上塞不下一排：
     四列每格只剩 80px，六位数金额（如资产重估的「+1,234,567.89」）会被截断。
     这条结果条是永久常驻的，占的每一像素都是从下面输入区借来的——改成
     display:grid 强制两列两行，比让 flex 硬挤成一排更省地方，也比缩成一列
     四行更省高度（那样会把结果条撑得更高，从输入区借走更多）。flex:1 1 100%
     留着没删：.sum-figures 自己仍是 .sum-bar（display:flex）的子元素，这条
     控制的是它在那层的占位，跟它自己是不是 grid 容器是两回事——曾经和下面的
     display/gap 分写在两条同选择器规则里，后写的覆盖了先写的 gap，读起来像是
     两条打架的规则，这里并成一条。 */
  .sum-bar .sum-figures { flex: 1 1 100%; display: grid; grid-template-columns: 1fr 1fr;
                           gap: 4px 20px; }
  .sum-fig .sum-sub { font-size: 11px; }
  /* 2×2 落地之后（每格 152.5px）还是超预算：4 个数字里有 3 个自带一行小字
     （净资产的 .sum-delta、投资收益率与资产重估各自的 .sum-sub），再加 body
     继承下来的 line-height:1.6，196.7px 几乎是两倍于两格时代的高度。这里在
     窄屏藏掉那三行小字、收紧数字本身的行高——不是砍功能，是砍打字时用不上
     的冗余：净资产的环比比的是「和上月」，本来就不随本月编辑变化；投资收益率
     的绝对值就是 financial 小计行「变化」列的同一个 Δ，那一格本身还在、还是
     活的；资产重估下面挂的是固定说明文字，不是数字。藏掉它们，打字时唯一会
     变的四个头条数字（结余、净资产、投资收益率、资产重估本身）一个没少。 */
  .sum-bar .sum-sub, .sum-bar .sum-delta { display: none; }
  .sum-bar .sum-fig strong { line-height: 1.15; margin-top: 0; }
  .sum-bar .sum-label { line-height: 1.25; }
  .sum-fig strong { font-size: 20px; }
  .sum-actions .sum-toggle { flex: 1; }
  /* 图在上、图例在下：并排两栏在 375px 上会把图例挤成一列省略号 */
  .sum-detail:not([hidden]) { grid-template-columns: 1fr; gap: 10px;
                              justify-content: stretch; }
  .sum-chart { height: 180px; }
}

/* ---------- 录入页固定支出表 ---------- */

#fixed-table td.readonly { color: var(--muted); }
#fixed-table .fixed-amount { width: 8rem; }
#fixed-table .fixed-note { width: 100%; }

/* ---------- 看板本月收支面板 ---------- */
.cashflow-heads { display: flex; gap: 2rem; margin-bottom: .75rem; }
.cashflow-heads strong { font-size: 1.15rem; }
/* 对账等式是数字为主的一行，跟着 .num 同一套字体处理，数位对齐 */
.reconcile { color: var(--muted); font-family: var(--mono); font-size: .9rem; }
.reconcile-warn { color: var(--red); font-weight: 600; }

/* macOS 的悬浮滚动条平时完全不可见，用鼠标就没法横向滚这张宽表——强制常驻 */
.scroll-x::-webkit-scrollbar { -webkit-appearance: none; height: 10px; }
.scroll-x::-webkit-scrollbar-track { background: var(--bg-sunken); }
.scroll-x::-webkit-scrollbar-thumb {
  background: var(--line-strong); border-radius: 5px; border: 2px solid var(--bg-sunken);
}
.scroll-x::-webkit-scrollbar-thumb:hover { background: var(--muted-2); }
/* 这里不能再写 scrollbar-width：一旦设了这个标准属性，Chrome 就会忽略上面整套
   ::-webkit-scrollbar 规则，滚动条又变回 macOS 那个平时不可见的悬浮样式。 */

/* ---------- 规划页冻结区：类目 + 每月基准两列，横滚时不动 ---------- */
/* 冻结靠 class 标出而不是 nth-child：合计行的第一个格子是 colspan=2，
   按位置数会把年份列当成第二列钉住。 */
#planning .c-name  { position: sticky; left: 0;      z-index: 2; background: var(--bg);
                     width: 200px; min-width: 200px; }
#planning .c-monthly { position: sticky; left: 200px; z-index: 2; background: var(--bg);
                     width: 130px; min-width: 130px; }
#planning .c-span  { width: 330px; min-width: 330px; }
#planning .c-monthly, #planning .c-span { border-right: 2px solid var(--line); }
#planning thead .c-name, #planning thead .c-monthly { z-index: 4; }
#planning .group-total .c-name, #planning .group-total .c-monthly { background: var(--bg-soft); }
#planning tfoot .c-name, #planning tfoot .c-monthly { background: var(--bg-sunken); }

/* 模块名和「+ 新增类目」所在的格子横跨整行，钉住格子没意义（文字仍在最左边
   跟着滚走），只能让文字本身在滚动容器里 sticky。 */
#planning .pin-left { position: sticky; left: 12px; display: inline-block; }
/* 冻结列宽度写死，类目名再长也不能把「每月基准」挤歪——超出就省略号 */
#planning .cat-name { display: inline-block; max-width: 120px; overflow: hidden;
                      text-overflow: ellipsis; vertical-align: bottom; }

/* 模块分组行：整行上色 + 标题居中。用 #planning 提高优先级压过
   .scroll-x th:first-child 的白底（那条规则是给冻结列用的，这里整格横跨一行）。 */
#planning .group-head th { text-align: center; font-size: 15px; letter-spacing: .04em; }
#planning .group-head.mod-family th { background: var(--amber-soft); color: var(--amber); }
/* 「小孩」模块复用应收类目那支粉色——两处都是同一个色相家族，不必再定一份 */
#planning .group-head.mod-child th  { background: color-mix(in srgb, var(--c-receivable) 20%, var(--bg));
                                       color: var(--c-receivable); }
/* .pin-left 仍是 sticky：不滚动时按居中显示，横滚后自动贴到左边不消失 */

/* 年份列：表头字号放大居中，列本身收窄——6 个年份挤在一屏里才好比较 */
#planning .c-year { text-align: center; padding-left: 6px; padding-right: 6px; }
#planning thead .c-year { font-size: 15px; color: var(--fg); letter-spacing: .01em; }
#planning .c-year input { min-width: 0; text-align: center; }
#planning .c-year.num, #planning .c-year .num { width: 118px; }

/* 归档类目：默认藏起来，勾选「显示已归档」后以置灰只读行出现 */
#planning tr.archived { display: none; }
#planning.show-archived tr.archived { display: table-row; color: var(--muted-2); }
.tag-archived {
  font-size: 11px;
  color: var(--muted-2);
  border: 1px solid var(--line);
  border-radius: 3px;
  padding: 0 4px;
}
tr.fixed-row.archived { color: var(--muted-2); }

/* 类目列滚动时固定（见上面 .c-name 的 sticky）：这一列不能换行，否则冻结
   列的宽度会被撑开的类目名带乱。归档/恢复、新增类目的按钮曾经也长在这张表
   这一列里，入口唯一化（task 7）把它们连同 DOM 一起搬去了主表
   #fixed-main——那边的 .archive/.unarchive 靠通用的 .row-actions button
   规则上色（不是按类名单独定义），.add-toggle/.add-form 目前还没有专门的
   样式规则（沿用浏览器默认按钮/输入框外观）。这张表自己那几条
   #planning .archive/.unarchive/.add-toggle/.add-form/.add-cell 规则没有
   任何元素可匹配了，一并删掉，不留死规则。 */
#planning td:first-child { white-space: nowrap; }

/* 「未记录」是一个状态，不是一个数字——排版上就该和金额区分开：不用等宽数字，
   字号收一档，颜色退到 muted。避免读者把它扫成又一个 KPI 数值。 */
.cashflow-heads strong.unknown { font-size: 1rem; color: var(--muted); font-weight: 600; }
/* 对账不可用（无上月快照 / 本月未记支出）：陈述事实，不是告警，不能用红色 */
.reconcile-na { color: var(--muted); }
.reconcile-na a { color: var(--accent); }

/* 固定支出页主表的本年逐月迷你条。高度已经在 domain/fixed.py 按每行
   自己的 12 个月峰值算好（0–18px），模板只管拿数字画竖条，不做归一化。 */
.mini { display: inline-flex; align-items: flex-end; gap: 2px; height: 18px; }
.mini b { display: block; width: 4px; min-height: 1px; border-radius: 1px;
          background: var(--accent); opacity: .55; }

/* 迷你柱的两种退化态：虚线格 = 那个月没记过账，贴底细线 = 记了、就是 0。
   合成同一种画法，页面就再也分不出「没花」和「没记」——见 domain/fixed.py
   的 year_series。 */
.mini b.gap { height: 18px; width: 4px; background: none; border-radius: 0;
              border-left: 1px dotted var(--line-strong); }
.mini b.zero { height: 1px; background: var(--muted-2); }

/* 「月基准×12 ≠ 年度计划」的中性提示。刻意不用告警色：两套数并存是有意的
   设计，差异多半也是有意的，红色只会训练人忽略它。 */
.x12-diff { display: block; margin-top: 2px; font-size: 11px; color: var(--muted);
            font-family: var(--mono); white-space: nowrap; cursor: help; }

/* 和上面 .summary-card 那段同一个道理：这几条基础规则写在文件中部那个 @media
   断点之后，窄屏覆盖必须跟在它们后面自开一个断点，否则同优先级下被基础规则
   整个压掉。 */
@media (max-width: 640px) {
  /* 吸顶条在窄屏必须压成两行：两个头条数字一行，走条 + 计数 + 两颗按钮一行。
     任由它们各自换行会摞到 140px 高——一块 844px 的手机屏，这条子就吃掉六分之一，
     而它是常驻的。「已填」「个账户」两截词在这里收掉：走条已经把「完成了多少」
     说清楚了，数字留着回答「几个」。 */
  .sum-figures { width: 100%; gap: 24px; }
  .sum-progress { flex: 1; min-width: 0; gap: 8px; }
  .prog-rail { flex: 1 1 44px; width: auto; min-width: 36px; }
  .prog-word { display: none; }
  #jump-unfilled, .sum-bar #save { flex: none; margin-left: 0; padding: 5px 9px; }

  /* toast 贴着底边、两侧留边距，别压住最后一行输入 */
  .toast { left: 12px; right: 12px; bottom: 12px; max-width: none;
           transform: translateY(16px); }
  .toast.is-on { transform: translateY(0); }

  /* 「未填的全部沿用上月」在窄屏里换到标题下一行，别把「资产负债」挤成两行 */
  .sec-head { align-items: flex-start; }

  /* 「本月收支」三格「收入/支出/结余」原来是不换行的一整行——数字改成等宽字体后
     单字符更宽，「收入 xx,xxx.xx」×3 加两道 2rem 的间距在 375px 里已经摆不下，
     实测溢出到视口外。允许换行，386px 以下的机型也不会再横向滚动整个页面。 */
  .cashflow-heads { flex-wrap: wrap; gap: .5rem 1.5rem; }

  /* 财务比率体检收回单列：两列栅格在 375px 上每列不到 150px，「资产负债率」
     四个字加健康线加数值挤不下，会被迫把数值顶到第二行。 */
  .ratio-list { grid-template-columns: 1fr; gap: 16px; }
}

/* ---------- 固定支出页：年度汇总条 ---------- */
.sum-strip { display: flex; flex-wrap: wrap; background: var(--bg);
             border: 1px solid var(--line); border-radius: 12px;
             margin: 16px 0 12px; overflow: hidden; }
.sum-cell { flex: 1 1 160px; padding: 12px 16px; border-right: 1px solid var(--line-soft); }
.sum-cell:last-child { border-right: none; }
.sum-cell.is-total { background: var(--bg-soft); }
.sum-k { display: block; font-size: 12px; color: var(--muted); }
/* 大号数字不用 tabular-nums：等宽数位在 19px 上显得松垮，
   上下对齐只有表格里才需要 */
.sum-v { font-family: var(--mono); font-size: 19px; font-weight: 600;
         letter-spacing: -0.01em; }
.sum-sub { display: block; font-family: var(--mono); font-size: 11.5px; color: var(--muted-2); }
.toolbar { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; margin-bottom: 8px; }

/* ---------- 固定支出页：主表 ---------- */
#fixed-main td.editable { padding: 4px 8px; }
/* 平时无边框、hover 才浮出来：这张表静态时应该读起来像表，不像表单 */
#fixed-main input.cell-input { width: 100px; text-align: right;
  border-color: transparent; background: transparent; }
#fixed-main input.cell-input:hover { border-color: var(--line); }
#fixed-main input.cell-input:focus { border-color: transparent; background: var(--bg); }
#fixed-main tr.cat-row { cursor: pointer; }
#fixed-main tr.cat-row:hover > td,
#fixed-main tr.cat-row.is-open > td { background: var(--accent-soft); }
#fixed-main tr.exec-archived td { color: var(--muted); }
#fixed-main tr.archived-row { display: none; }
#fixed-main.show-archived tr.archived-row { display: table-row; }
.caret { display: inline-block; width: 12px; color: var(--muted-2); transition: transform .12s; }
tr.is-open .caret { transform: rotate(90deg); }
.row-actions { white-space: nowrap; }
.row-actions button { padding: 2px 8px; font-size: 12px; color: var(--muted); }
.row-actions button:hover { color: var(--fg); }

/* ---------- 固定支出页：展开抽屉与逐月柱状图 ---------- */
/* 必须排在 .show-archived 那条之后：归档行的抽屉同时命中两条规则，
   特异度相同，靠先后顺序让「收起」赢过「显示已归档」。 */
#fixed-main tr.drawer[hidden] { display: none; }
/* 后三条都在解同一处「误伤」：抽屉的 <td colspan> 是它那行唯一的格子，于是被
   .scroll-x td:first-child 命中了——那条是给多年矩阵写的「冻结首列」（账户名
   不换行、最小 150px、横滚时 sticky 不动），对一格横跨整张表的抽屉毫无意义。
   继承来的 nowrap 尤其要命：窄屏下 .scroll-x-now 把 overflow-x 交成了 visible，
   撑不开的标题就不是撑破抽屉、而是把整个页面撑宽（实测 390px 的视口被顶到
   726px，整页横向滚动）。不去动 .scroll-x 那条本身——多年矩阵和历史页的
   账户×月矩阵还靠它冻结首列。 */
#fixed-main tr.drawer > td { padding: 0; background: var(--bg-sunken);
                             white-space: normal; min-width: 0; position: static; }
.drawer-in { display: flex; gap: 24px; padding: 18px 16px; }
/* 不叫 .chart：那个类名已经被 history.html 的净资产/现金流图占了
   （height: 340px，ECharts 用它 init 时读到的容器高度定画布尺寸）。这里
   要是也叫 .chart，两条同特异度规则会按声明顺序叠加、互相漏属性，
   曾经真的把 /history 的两张图塌成 0 高——用专属类名从根上避免撞名。 */
.drawer-chart { flex: 1; min-width: 0; }
/* 峰值那个直接标注是贴着图顶往上画的，标题下面要留够它的高度 */
.drawer-title { margin: 0 0 28px; font-size: 13px; font-weight: 600; }
.drawer-title .hint { margin-left: 8px; font-weight: 400; }

.plot { position: relative; height: 128px; display: flex; align-items: flex-end;
        gap: 2px; border-bottom: 1px solid var(--line-strong); }
/* 整格都是命中区（128px 高），不要求指到柱子上——「记了就是 0」的柱子
   只有 3px 高，否则根本点不中 */
.plot .slot { flex: 1; min-width: 5px; height: 100%; display: flex;
              align-items: flex-end; font-style: normal; }
.plot .slot:hover { background: var(--accent-soft); }
/* 选择器必须带 .slot：.plot b 会把任何落在图里的 <b> 一起吃掉，
   月基准的说明标签就是这么变成一条横穿全图的色带的 */
.plot .slot b { width: 100%; background: var(--accent); border-radius: 3px 3px 0 0; }
.plot .slot b.zero { height: 3px; background: var(--accent); opacity: .45; }
.plot .slot b.gap { height: 100%; background: none; border-radius: 0;
                    border-left: 1px dotted var(--line-strong); }
/* 半高一条发丝线当唯一的栅格；实线，虚线会被读成「阈值」 */
.grid-half { position: absolute; left: 0; right: 0; bottom: 50%;
             border-top: 1px solid var(--line); pointer-events: none; }
.year-sep { position: absolute; top: 0; bottom: 0;
            border-left: 1px solid var(--line-soft); pointer-events: none; }
/* 月基准是真正的阈值，虚线在这里是对的。不挂标签：图上任何带底色的块都会
   挡住柱子，这条线是什么在标题里说一次就够了。 */
.baseline { position: absolute; left: 0; right: 0; z-index: 2;
            border-top: 1px dashed var(--amber); pointer-events: none; }
/* 直接标注只给两个点：最高月、最近一个有记录的月。每根柱子都标数是噪声 */
.pin { position: absolute; z-index: 2; transform: translate(-50%, -100%);
       font-family: var(--mono); font-size: 11px; color: var(--muted);
       background: var(--bg-sunken); padding: 0 3px;
       white-space: nowrap; pointer-events: none; }
.chart-tip { position: absolute; z-index: 5; transform: translate(-50%, 0);
             background: var(--solid-bg); color: var(--solid-ink);
             font-size: 11.5px; padding: 4px 8px; border-radius: 6px;
             white-space: nowrap; pointer-events: none; }
.chart-tip b { font-family: var(--mono); }
.chart-x { display: flex; margin-top: 6px; font-size: 11px; color: var(--muted-2); }
.chart-x span { text-align: center; }
.drawer-facts { flex: 0 0 210px; display: grid; grid-template-columns: 1fr 1fr;
                gap: 10px 14px; align-content: start; }
.drawer-facts .sum-v { font-size: 15px; }

/* 抽屉的窄屏覆盖必须自开一个断点：它的基础规则（.drawer-in / .drawer-facts）
   写在文件更靠后的位置，塞进上面任何一个 @media 块都会被后面的基础规则按
   声明顺序整个压掉——和 .summary-card、#fixed-main 那两段同一个道理。

   390px 实算：body 侧边距 16×2 → 358；窄屏卡片自身 padding 10px 12px 再吃
   24 → 334；.drawer-in 的 padding 18px 16px 吃 32 → 302；gap 24px → 278；
   .drawer-facts 的 flex: 0 0 210px 不收缩 → 图只剩约 68px，而 .plot 的 N 个
   slot（min-width 5px、间距 2px）最小需要 7N-2 px，N=32 时是 222px。

   溢出在这里格外要紧：#fixed-main 外面那层 .scroll-x-now 在窄屏显式写了
   overflow-x: visible（卡片化之后不该再有横向滚动），横向滚动被交了出去，
   撑破的就不是抽屉而是整个页面。 */
@media (max-width: 640px) {
  .drawer-in { flex-direction: column; gap: 12px; padding: 12px; }
  .drawer-facts { flex: none; }

  /* 下面两条跟抽屉是同一个病：#fixed-main 的这些基础规则写在窄屏块（约 515 行）
     之后，特异度又不低于那边，于是在窄屏按声明顺序把卡片化压掉了。补在这个位于
     文件末尾的断点里才压得回去——放进 515 那个块里没用，它在前面。

     可编辑格的左内边距必须和只读格一致：卡片化之后每格是
     「label ::before —— 值」的一行，td 的左内边距就是标签的起始位置。
     基础规则给 .editable 的 8px 会把「月基准」「年度计划」两行的标签整体右移
     8px，同一张卡片里的六个标签对不齐（是肉眼一看就出戏的那种错位）。 */
  #fixed-main td.editable { padding: 3px 0; }

  /* 勾「显示已归档类目」露出来的行必须还是卡片。基础规则那条为了盖住
     tr.archived-row{display:none} 用了 display:table-row，在窄屏就把这几行
     变回表格行、卡片布局对它们单独失效。 */
  #fixed-main.show-archived tr.archived-row { display: block; }

  /* 卡片里六行数值对齐到同一条右缘。两行是 <input>（月基准 / 年度计划），四行
     是纯文本——input 里的数字被自己的 padding-right 10px 加 1px 边框往里推了
     11px，纯文本却贴着格子右缘，六行数字于是排成两条参差的右缘（肉眼一眼就
     看得出）。让纯文本格让出同样的 11px，而不是把 input 的 padding 抹平：抹平
     之后 hover/focus 浮出来的那圈边框会紧贴数字，输入时更难看。
     11px 跟着全局 input 规则（padding: 6px 10px + 1px 边框）走，改那边记得回来
     改这里。年度进度条和本年逐月迷你条一并内收——它们和数字共用这条右缘。 */
  #fixed-main td[data-label]:not(.editable) { padding-right: 11px; }

  /* 月基准那句图例单独占一行：跟在标题同一行时，「日常生活费 · 逐月支出
     2024-01 — 2026-08」本身已经吃掉整个宽度了。 */
  .drawer-title .hint { display: block; margin-left: 0; margin-top: 2px; }

  /* 峰值的直接标注在窄屏撤掉：图宽只剩 341px、三十多根柱子挤在一起，那个标签
     必然压住柱子本身。信息没丢——右下小结里「最高月」那一格仍然给出数值和月份。
     宽屏不撤：那儿有地方，直接标注是有用的。 */
  .pin-hi { display: none; }

  /* 标题下面那 28px 是给峰值标注预留的高度，窄屏既然不画它就还给图。不收到底：
     最近一个月恰好是峰值时，剩下的那个标注会顶到图顶上方，还得留出它的高度。 */
  .drawer-title { margin-bottom: 16px; }
}
