/* ==========================================================================
   pages.css — 页面级布局（只做「页面怎么排」，不做「组件长什么样」）
   --------------------------------------------------------------------------
   分工：
     · tokens.css      颜色 / 字体 / 间距 / 圆角 / 层级 / 动效的唯一真相
     · components.css  18 个组件的自身外观与 8 态（设计师交付，逐字节复制，不在此改）
     · pages.css       本文件：页面骨架、区块节奏、页面级容器

   铁律：
     1. 值一律 var(--...)，本文件不出现任何硬编码色值（#fff / #000 除外）
     2. 不写组件内部样式。要改组件请改 components.css 并同步设计侧，避免"两个地方都能改"
     3. 不用 :has() / CSS 嵌套 / container query / oklch() / color-mix()
        —— Chromium 100 基线，这些特性在旧内核里不报错、只丢样式
     4. 间距只取 4px 网格（4/8/12/16/20/24/32/48/64），不出现 5/7/13/22

   本文件的取值纪律（设计侧问过"是不是全部走 token"，答案在这里）：
     · **颜色 100% 走 var(--...)**，零字面色值（hex / rgb / hsl / 命名色都没有），
       由 fe_selfcheck 的色值扫描每次校验
     · **间距、字号、圆角、动效、层级** 全部走 --space-* / --text-* / --radius-* /
       --motion-* / --z-*，不写字面量
     · 只有 6 处结构性尺寸刻意不进 token，因为 tokens.css 里没有对应档位，
       硬塞会逼设计侧为一个页面级数值新增全局 token：
         1px   发丝线（与 components.css 的边框口径一致，全项目唯一写法）
         2px   项目符号的左偏移（不是间距，是图形定位）
         28px  行内图标按钮的点击目标（介于 24 与 32 之间，取 44px 会撑破 36px 行高）
         44px  输入框右侧为图标留出的内边距（28px 按钮 + 左右各 8px 间隙）
         208px 列映射下拉框最小宽度（要容得下 5 个汉字的表头）
         320px / 480px / 720px  历史文件名、结果表格、错误面板的最大宽/高
   ========================================================================== */

/* ==========================================================================
   1. 区块节奏 —— 结果页 L0→L3 的垂直间距只在这里定义一次
   --------------------------------------------------------------------------
   .band 是"层"的容器：相邻两层之间 24px。用 `+ *` 而不是给每层写 margin-bottom，
   是因为最后一层不该留尾巴（否则页面底部无端多 24px 空白）。
   ========================================================================== */

.band + .band { margin-top: var(--space-6); }
.band--tight + .band--tight { margin-top: var(--space-3); }

/* 通用纵向堆叠：把 gap 交回给容器，避免每个子元素各写一份 margin */
.stack {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}
.stack--tight { gap: var(--space-2); }
.stack--wide  { gap: var(--space-6); }

/* 横向一行：左侧主体 + 右侧操作 */
.row-between {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}
.row-between > .row-between__spacer { flex: 1; }

/* ==========================================================================
   2. 上传台（屏 2）—— 5fr / 7fr 非对称
   ========================================================================== */

.upload-page__body { margin-top: var(--space-2); }

/* 两栏投放区必须等高：左栏"单文件"、右栏"多文件"是业务差异，不是视觉层级差异。
   高度不一致会被读成"右边还没加载完"。清单只需一份，用文案说明，不用尺寸强调。 */

/* 发票多于 8 个时折叠提示（DESIGN §9.5：超过 8 个折叠「还有 N 个」） */
.file-list__more {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) 0 0;
  font-size: var(--text-sm);
  color: var(--color-muted);
}
.file-list__more-btn {
  background: none;
  border: 0;
  padding: 0;
  color: var(--color-accent);
  font-size: var(--text-sm);
  cursor: pointer;
  text-decoration: underline;
}

/* 空文件列表不占位：投放区本身已经说明"该放什么"，
   再补一句"还没有选择文件"只是把同一句话说两遍（标签右侧已有"未选择"）。 */

/* 提交中的进度区：与投放区同宽，避免布局跳动 */
.progress-region { margin-top: var(--space-4); }
.progress-region[hidden] { display: none; }

/* 底部 sticky 操作条：需要独占整页宽度感，故用负 margin 抵掉页面 gutter */
.sticky-actions--page {
  margin: 0 calc(-1 * var(--layout-gutter));
  padding-left: var(--layout-gutter);
  padding-right: var(--layout-gutter);
}
.sticky-actions__summary {
  font-size: var(--text-sm);
  color: var(--color-muted);
  font-variant-numeric: tabular-nums;
}

/* 最近核对摘要（上传页空态下方） */
.recent-block { margin-top: var(--space-12); }
.recent-block__title {
  margin-bottom: var(--space-2);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  color: var(--color-fg-2);
  letter-spacing: var(--tracking-caps);
}
.recent-list {
  display: flex;
  flex-direction: column;
  border-top: 1px solid var(--color-border-soft);
}
.recent-item {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  height: var(--table-row-height);
  padding: 0 var(--space-2);
  border-bottom: 1px solid var(--color-border-soft);
  font-size: var(--text-base);
  color: var(--color-fg);
  text-decoration: none;
}
.recent-item:hover { background: var(--color-surface-2); text-decoration: none; }
.recent-item__name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.recent-item__meta {
  flex: none;
  font-size: var(--text-sm);
  color: var(--color-muted);
  font-variant-numeric: tabular-nums;
}

/* ==========================================================================
   3. 结果页（屏 4）—— L0 / L1 / L2 / L2.5 / L2.6 / L3
   ========================================================================== */

/* 结论横幅右侧的主操作：垂直居中，不跟着多行文案往下跑 */
.banner__actions { align-self: center; }

/* ---- v3 结果页：三块结构的样式 -------------------------------------------
   这里只放"页面上新长出来的"几块：区块标题、判定逻辑入口、降级提示行、
   待确认行、导出动作条。被删掉的块（KPI / 咬合条 / 归因表）的样式不在此保留，
   需要时从 git 历史找 —— 留着会让人以为它们还能被打开。 */

/* 区块标题：图标 + 文案 + 计数，与段头同一视觉层级 */
.result-block__title {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0 0 var(--space-3);
  font-size: var(--text-lg);
  font-weight: var(--weight-semibold);
  color: var(--color-fg);
}
.result-block__title .i { color: var(--color-fg-2); }
.result-block__count {
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  color: var(--color-muted);
  font-variant-numeric: tabular-nums;
}

/* 结论横幅右侧的「查看判定逻辑」：口径条删掉后，它是唯一的解释入口 */
.banner__link { align-self: flex-start; white-space: nowrap; }

/* 副句：结论下方一行，比主句弱、比边界说明强。
   它是可核对的事实（"105 台车均有对应发票"），不是结论形容词 —— 后者是合规禁词。 */
.banner__sub {
  margin-top: var(--space-1);
  font-size: var(--text-md);
  color: var(--color-fg-2);
  font-variant-numeric: tabular-nums;
}

/* 降级提示：横幅内、结论下方的一行 12px 小字。
   刻意不用色块、不用描边、不做 pill —— 它只是"多知道一件事"，不是一个待办事项。
   用 warn 色文字而不是 muted：它比边界说明更需要被看见，但不该抢 pill 的位置（pill 已随 v3 删除）。 */
.banner__hint {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  margin-top: var(--space-2);
  font-size: var(--text-sm);
  line-height: var(--leading-body);
  color: var(--color-warn);
}
.banner__hint .i { margin-top: 2px; flex: none; }

/* 不可核原因（cannot_verify）：一行一条，各自带下一步动作 */
.banner__reasons {
  margin: var(--space-2) 0 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  font-size: var(--text-sm);
  line-height: var(--leading-body);
}

/* 行分类待定：一行放完 —— 标签 + 行号 + 提示，不展开成表 */
.pending-line {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  font-size: var(--text-sm);
}
.pending-line .result-block__title { margin: 0; font-size: var(--text-sm); font-weight: var(--weight-medium); }
.pending-line__rows { display: inline-flex; align-items: center; gap: var(--space-2); }
.pending-line__hint { color: var(--color-muted); }

/* 导出动作条：左侧主按钮，右侧提示文字 */
.result-actions { display: flex; align-items: center; gap: var(--space-3); }

/* 口径声明条：正文可换行，链接不换行；额外声明显式成块、可在长文本后自然折行 */
.boundary-note__body { flex: 1; min-width: 0; }

/* 不可核原因列表：每条一句话自带下一步动作。
   不用普通圆点，用 1px 竖线做左侧引线 —— 这几条是"系统在解释自己为什么判不了"，
   形态上更像引文而不是并列项。 */
.boundary-note__reasons {
  margin: var(--space-2) 0 0;
  padding-left: var(--space-3);
  border-left: 1px solid var(--color-info-border);
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  font-size: var(--text-sm);
  color: var(--color-fg-2);
}
.boundary-note__text { display: block; }

/* KPI 四格：吨位差格的方向文字不参与 letter-spacing 压缩（数字要能逐位对齐） */
.kpi__cell--diff .kpi__value { letter-spacing: var(--tracking-normal); }

/* 相邻两行注释不要各带 4px 上边距，否则看起来像两组不相干的信息 */
.kpi__note + .kpi__note { margin-top: 0; }

/* 汇总行交叉校验不通过：说的是"表格本身有错"，不是"证据不完整"。
   前者让用户去改表，后者只会让他再传一次材料 —— 所以用 danger 而不是 warn。 */
.kpi__note--danger { color: var(--color-danger); }

/* 待确认行的三个子分组头（A 净重需确认 / B 净重偏低 / C 行分类待定）。
   子组头不是可折叠项：段头已经折过一次，再折一层会让"到底有几行待确认"看不出来。 */
.subgroup-head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: var(--space-4) 0 var(--space-2);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  color: var(--color-fg-2);
}
.subgroup-head:first-child { margin-top: 0; }
.subgroup-head__count {
  font-variant-numeric: tabular-nums;
  color: var(--color-fg);
}
.subgroup-head__note {
  font-weight: var(--weight-regular);
  color: var(--color-muted);
}

/* 吨位咬合条：数值用等宽 + 深色，与正文区分；整块可选中复制 */
.reconcile__body { flex: 1; min-width: 0; }
.reconcile__label { color: var(--color-muted); font-size: var(--text-sm); }

/* 疑似配对横条：两侧车牌等宽对齐，操作按钮不换行 */
.pairbar__sides { display: flex; align-items: center; gap: var(--space-3); flex: 1; min-width: 0; }
.pairbar__side { flex: 1; }

/* 明细区工具条：固定在表格上方，滚动时贴住视口顶（topbar 48 + 面包屑 40） */
.toolbar--sticky {
  position: sticky;
  top: calc(var(--topbar-height) + var(--breadcrumb-height));
  z-index: var(--z-sticky);
  background: var(--color-bg);
  border-bottom: 1px solid var(--color-border-soft);
}
.toolbar__hint { font-size: var(--text-sm); color: var(--color-muted); }

/* 分组体被 tab 隐藏时的硬保证（hidden 属性在 display:flex 容器里会失效） */
.result-group[hidden],
.result-group__body[hidden] { display: none !important; }

/* 结果表格：结果页表格不做 560px 内滚（分组内行数已经过业务收敛），
   但保留横向滚动兜底，防止 125% 缩放下宽表炸版 */
.result-group .t-table__wrap { max-height: 480px; }

/* 车牌 / 发票号列的等宽语义。
   这两列与行号列一样，是用户要逐字符人工比对的字段（0 与 O、1 与 I 的差别必须在
   视觉上留得住），所以由列本身承担等宽，而不是靠单元格里再包一个 span ——
   `getComputedStyle(td).fontFamily` 就能验，不依赖选择器猜结构。
   注：结果页当前不渲染发票号列（契约没有"已匹配逐行清单"，见 FRONTEND-NOTES §4-⑥），
   `.cell-invoice` 保留给后续加 `matched_rows[]` 时直接用。 */
.cell-plate,
.cell-invoice {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  white-space: nowrap;
}

/* 表格里的行号集合：多个行号横向排，间距 8px，超出不换行 */
.rownums { display: inline-flex; align-items: center; gap: var(--space-2); font-variant-numeric: tabular-nums; }

/* 「+N」在 .evidence 之上只改配色与字体，不改变可点性 */
.evidence.evidence__more { font-family: var(--font-sans); text-decoration: none; color: var(--color-muted); }
.evidence.evidence__more:hover { color: var(--color-accent); text-decoration: underline; }

/* 空分组：该组计数为 0 时不渲染整组；但"本次未启用"这类必须显式说明的场合用这条 */
.inline-note {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface-2);
  font-size: var(--text-sm);
  line-height: var(--leading-body);
  color: var(--color-fg-2);
}
.inline-note .i { margin-top: 1px; color: var(--color-muted); }

/* 导出按钮的加载态：按「下载开始」而非「等待生成」，超时即视为 3003 */
.btn.is-loading { pointer-events: none; }

/* ==========================================================================
   4. 确认面板（屏 3 / needs_confirm）
   ========================================================================== */

.mapping-panel__body > .preview-note + .t-table__wrap { margin-top: 0; }

/* 预览表格：值一律逐字符原样，故等宽 + 不换行 + 可横向滚动 */
.preview-table .t-table { table-layout: auto; }
.preview-table .t-table thead th { white-space: nowrap; }
.preview-table .t-table tbody td {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  white-space: nowrap;
}

/* 列映射下拉：整列对齐，宽度随内容 */
.field-mapping__label { font-weight: var(--weight-medium); color: var(--color-fg); }
.field-mapping__select { min-width: 208px; }

/* components.css 里 .select 是 appearance:none（去掉系统箭头以便统一视觉），
   那就必须自己补一个 —— 否则下拉框看起来像一个普通输入框，用户不会想到能点 */
.field-mapping__selectwrap { position: relative; display: inline-flex; align-items: center; }
.field-mapping__caret {
  position: absolute;
  right: var(--space-3);
  color: var(--color-muted);
  pointer-events: none;
}

/* 必填未映射时的行内提示，跟在 select 后面 */
.field-mapping__miss { margin-left: var(--space-2); font-size: var(--text-sm); color: var(--color-danger); }

/* ==========================================================================
   5. 历史记录（屏 5）
   ========================================================================== */

/* 按所属期分组：组头行不参与斑马纹与 hover，读起来像小标题而非数据行 */
.t-table tbody tr.t-table__grouphead td {
  height: var(--space-8);
  background: var(--color-surface-2);
  color: var(--color-fg-2);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-caps);
  border-bottom: 1px solid var(--color-border);
}
.t-table tbody tr.t-table__grouphead:hover td { background: var(--color-surface-2); }

/* 历史表的时间列不用等宽（DATE 不是逐字符比对的证据），但要 tabular-nums */
.history-date { font-variant-numeric: tabular-nums; white-space: nowrap; color: var(--color-fg-2); }
.history-file { max-width: 320px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.history-actions__btn { text-decoration: none; }

/* 「查看」常驻，其余操作 hover 才出现。
   理由不是美观，是**唯一入口不能靠 hover**：静态列表里那一列全空时，
   鼠标不知道要悬停的人就找不到进入详情的路。
   （DESIGN 的 hover 出现是为密集表格的行内操作设计的，那里整行可点；这个列表不是那种场景，
   team-lead 2026-10-29 已裁定改为常驻。） */
.history-actions { display: flex; align-items: center; gap: var(--space-1); justify-content: flex-end; }
.history-actions__view { visibility: visible; }
.history-actions .btn { visibility: hidden; }
.history-actions .history-actions__view { visibility: visible; }
tr:hover .history-actions .btn,
tr:focus-within .history-actions .btn { visibility: visible; }

/* 删除前的二次确认：原生 confirm() 已足够（3~5 人内部工具），
   但按钮文案必须写清"删除后导出件也一并清理" */
.history-empty { margin-top: var(--space-6); }

/* ==========================================================================
   6. 登录页（屏 1）—— 补充 components.css 里 .auth* 的页面级细节
   ========================================================================== */

.auth__points li { position: relative; padding-left: var(--space-5); }

/* 实心小圆点，不是空心方框。
   第一版用了 1px 描边的 6px 方框，截图一看像三个"未勾选的复选框"—— 会诱导用户去点。
   4px 实心圆在任何缩放比下都读作项目符号，不会被误认成控件。 */
.auth__points li::before {
  content: "";
  position: absolute;
  left: 2px;
  top: 0.58em;
  width: var(--space-1);
  height: var(--space-1);
  border-radius: var(--radius-pill);
  background: currentColor;
  opacity: 0.45;
}

.auth__form-error { margin-bottom: var(--space-5); }
.auth__step-hint { margin-top: var(--space-3); font-size: var(--text-sm); color: var(--color-muted); }

/* 密码显隐按钮：贴在输入框右侧，不破坏 36px 行高 */
.field--with-action { position: relative; }
.field--with-action .input { padding-right: 44px; }
.field__action {
  position: absolute;
  right: var(--space-1);
  bottom: var(--space-1);
  width: 28px;
  height: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: 0;
  border-radius: var(--radius-md);
  color: var(--color-muted);
  cursor: pointer;
}
.field__action:hover { background: var(--color-surface-2); color: var(--color-fg); }

/* ==========================================================================
   7. 错误页 / 失败态
   ========================================================================== */

.error-page { padding-top: var(--space-12); }
.error-page__panel { max-width: 720px; }
.error-page__actions { margin-top: var(--space-5); display: flex; gap: var(--space-2); }
.error-page__code {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  color: var(--color-muted);
  font-variant-numeric: tabular-nums;
}

/* ==========================================================================
   8. 工具类（只加没有的；tokens.css 已提供 .u-num / .u-mono / .u-sr-only / .i）
   ========================================================================== */

/* 按钮与分段控件用 <a> 承载（筛选与跳转走链接，能被新标签页打开、能被收藏）时，
   去掉全局 a:hover 的下划线 —— 它们看起来是按钮，不该有正文链接的样式 */
a.btn,
a.tabs__item,
a.pagination__btn { text-decoration: none; }
a.btn:hover,
a.tabs__item:hover,
a.pagination__btn:hover { text-decoration: none; }
a.tabs__item { display: inline-flex; align-items: center; }
a.pagination__btn { display: inline-flex; align-items: center; }

/* hidden 属性必须压得住任何 display 声明。
   这是本项目最容易复发的一个坑：.file-list__more / .result-group__body / .progress
   都设了 display，一旦元素带 hidden 属性就会被"显示回来"，
   表现是"本不该出现的块出现在页面上"，而且不报错。 */
[hidden] { display: none !important; }

.u-hidden { display: none !important; }
.u-nowrap { white-space: nowrap; }
.u-muted  { color: var(--color-muted); }
.u-fg2    { color: var(--color-fg-2); }
.u-sm     { font-size: var(--text-sm); }
.u-xs     { font-size: var(--text-xs); }
.u-mt-2   { margin-top: var(--space-2); }
.u-mt-4   { margin-top: var(--space-4); }
.u-mt-6   { margin-top: var(--space-6); }
.u-w-full { width: 100%; }

/* 链接型按钮：与 <a> 同视觉，但走 <button>（做动作而非跳转，避免可点区域语义错） */
.linkish {
  background: none;
  border: 0;
  padding: 0;
  color: var(--color-accent);
  font-size: inherit;
  font-family: inherit;
  text-decoration: underline;
  cursor: pointer;
}

/* 无 JS 时的显式提示：这些页面依赖原生 ES modules 完成上传/裁决 */
.no-js-warning {
  margin: var(--space-4) 0;
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--color-warn-border);
  border-radius: var(--radius-md);
  background: var(--color-warn-bg);
  color: var(--color-warn);
  font-size: var(--text-sm);
}

