/* ===== 自定义样式 ===== */

/* ===== 全局中文字体栈（2026-09-18：阿里巴巴普惠体优先） =====
   【本系统字体策略】
   1) **首选阿里巴巴普惠体**（Alibaba PuHuiTi）—— 这里把它的各种常见注册名都列上，
      因为不同来源安装后注册名不同（官方 3.0 是 "Alibaba PuHuiTi 3.0"，
      2.0 是 "Alibaba PuHuiTi 2.0"，部分打包版是 "AlibabaPuHuiTi"）。列全才能命中。
   2) **不把字体文件打进 /static** —— 收录在本文件顶部的既有决策仍然有效：
      普惠体是全字库 CJK 字体，单字重 woff2 约 5~8MB，全量自托管会让**内网首次加载多下 10~20MB**
      （手机端尤其明显），而收益只是字形观感。所以默认走"**优先本机已安装，否则回退系统字体**"。
      → 想真正看到普惠体：在各台电脑上装一次该字体即可（一次性、零带宽）。
      → 若日后要求所有设备一致，再按下面「自托管写法」打开即可，届时建议先用 pyftsubset
         按本系统实际用字做子集化（可压到几百 KB），而不是直接塞全字库。
   3) **回退链保持完整** —— 未装普惠体的机器继续用 苹方/微软雅黑/思源黑体，
      任意系统都不会回退到宋体等衬线字体；**视觉无回归**。

   —— 自托管写法（需要时把文件放到 /static/fonts/ 并取消注释）——
   @font-face {
       font-family: "Alibaba PuHuiTi 3.0";
       src: url("/static/fonts/AlibabaPuHuiTi-3-55-Regular.woff2") format("woff2");
       font-weight: 400; font-style: normal; font-display: swap;
   }
   （font-display: swap = 字体未就绪时先用回退字体渲染，避免文字长时间不可见） */
html, body {
    font-family: "Alibaba PuHuiTi 3.0", "Alibaba PuHuiTi 2.0", "Alibaba PuHuiTi",
        "AlibabaPuHuiTi", "Alibaba PuHuiTi 3",
        -apple-system, BlinkMacSystemFont, "Segoe UI",
        "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑",
        "Noto Sans CJK SC", "Source Han Sans SC", "WenQuanYi Micro Hei",
        "Helvetica Neue", Arial, sans-serif;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
    -webkit-text-size-adjust: 100%;
    -webkit-tap-highlight-color: rgba(0,0,0,0);
}

/* ===== 导航悬浮卡片：桥接触发器与卡片间的 8px 空隙（防 hover 闪断）+ 键盘 focus-within 打开 ===== */
#navbar-weather::after,
#navbar-user-area::after {
    content: "";
    position: absolute;
    top: 100%;
    right: 0;
    height: 10px;
    pointer-events: auto;
}
#navbar-weather::after { width: 20rem; }      /* 对齐天气卡片 w-80 */
#navbar-user-area::after { width: 9rem; }     /* 对齐用户下拉 w-36 */
#navbar-weather:focus-within #weather-popup,
#navbar-user-area:focus-within > div.absolute {
    opacity: 1 !important;
    visibility: visible !important;
}

/* KPI 卡片悬浮效果 */
.kpi-card {
    transition: transform 0.2s, box-shadow 0.2s;
}
.kpi-card:hover {
    transform: translateY(-2px);
    box-shadow: 0 8px 25px -5px rgba(0,0,0,0.1);
}

/* ============================================================================
   导航外壳：左侧导航栏 + 顶栏工具条（★ 2026-10-09 布局改版，用户拍板）
   ----------------------------------------------------------------------------
   结构来源：参考 MineAdmin 的**骨架**（左栏 + 独立顶栏），**不抄它的 Arco 蓝配色** ——
   本系统主色是琥珀、操作色是靛蓝，配色必须留在自己的设计系统里。

   【为什么这一整块全用自定义类、不写 Tailwind 工具类】
   Tailwind 是本地自托管 Play CDN（/static/tailwind.js），运行时把 <style> 注入 head，
   位置在 style.css **之后** → 同权重下 Tailwind 必胜。导航是「组件」不是一次性布局，
   所以按 DESIGN.md 第 4 节的约定走组件类（**替换**而非叠加）。

   【断点语义（相对旧版的关键变化）】
   旧：≥1024 顶部横排页签 / <1024 走 #mobile-menu（页签的第二份副本）
   新：≥1024 侧栏常驻（可收起为 64px 图标栏）/ <1024 侧栏变抽屉 + 顶栏出汉堡
   ⇒ 只有**一份**导航 DOM，靠 CSS 换形态；页签进了竖栏、横向再没有被挤压的可能，
     顺带解决 09-30 遗留待办「1024px 时 4 字页签折成两行」。 */

.app-shell {
    display: flex;
    min-height: 100vh;
    background: var(--c-surface-2);
}

/* ---------------- 侧栏 ---------------- */
.app-sidebar {
    flex: 0 0 200px;
    width: 200px;
    background: var(--c-surface);
    border-right: 1px solid var(--c-line);
    display: flex;
    flex-direction: column;
    position: sticky;
    top: 0;
    height: 100vh;
    z-index: 30;                              /* < 弹窗 z-50，> 内容 */
    transition: width var(--dur-med) var(--ease), flex-basis var(--dur-med) var(--ease);
}
.app-sidebar.is-rail {                        /* 收起态：只留图标 */
    flex-basis: 64px;
    width: 64px;
}

.nav-brand {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    height: 56px;                             /* 与顶栏同高 → 形成一条贯通的横线 */
    flex: 0 0 56px;
    padding: 0 var(--space-4);
    border-bottom: 1px solid var(--c-line);
    overflow: hidden;
    white-space: nowrap;
}
.nav-brand__logo {
    width: 26px; height: 26px; flex: 0 0 26px;
    border-radius: var(--radius-md);
    object-fit: contain;
    background: var(--c-surface-3);
    padding: 1px;
}
.nav-brand__txt { display: flex; flex-direction: column; line-height: 1.25; min-width: 0; }
.nav-brand__name { font-size: var(--fs-base); font-weight: 600; color: var(--c-ink); letter-spacing: .2px; }
.nav-brand__sub { font-size: var(--fs-xs); color: var(--c-ink-3); }
.app-sidebar.is-rail .nav-brand { justify-content: center; padding: 0; }
.app-sidebar.is-rail .nav-brand__txt { display: none; }

.nav-list {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: var(--space-3) var(--space-2);
    overflow-y: auto;
    overflow-x: hidden;
}

/* 导航项：默认无底，hover 淡灰，选中 = 琥珀浅底 + 深琥珀字
   （主色终于用在导航上 —— 旧版的靛蓝实底导航与本系统琥珀主色一直是打架的）
   ⚠️ 选中态**不改变 font-weight**：字重一变文字宽度就变，切换时会有抖动。
      选中只用「底色 + 字色 + 图标色」表达，零重排。 */
.ui-nav-item {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    width: 100%;
    padding: 9px var(--space-3);
    border: 0;
    border-radius: var(--radius-md);
    background: transparent;
    color: var(--c-ink-2);
    font-size: var(--fs-base);
    font-family: inherit;
    text-align: left;
    white-space: nowrap;
    overflow: hidden;
    cursor: pointer;
    transition: background-color var(--dur-fast) var(--ease),
                color var(--dur-fast) var(--ease),
                transform var(--dur-fast) var(--ease);
}
.ui-nav-item .nav-icon {
    flex: 0 0 18px;
    width: 18px; height: 18px;
    font-size: 18px; line-height: 1;
    color: var(--c-ink-3);
    transition: color var(--dur-fast) var(--ease);
}
.ui-nav-item:hover {
    background: var(--c-surface-2);
    color: var(--c-ink);
    transform: translateX(2px);                /* 只动 transform：不触发重排 */
}
.ui-nav-item:hover .nav-icon { color: var(--c-ink-2); }
.ui-nav-item:active { transform: translateX(2px) scale(.985); }
.ui-nav-item.is-active {
    background: var(--c-warn-soft);
    color: #92400e;                            /* amber-800：琥珀系里唯一在白底上 ≥7:1 的深字 */
}
.ui-nav-item.is-active .nav-icon { color: var(--c-primary-strong); }

.app-sidebar.is-rail .nav-list { padding-left: var(--space-1); padding-right: var(--space-1); }
.app-sidebar.is-rail .ui-nav-item { justify-content: center; padding-left: 0; padding-right: 0; }
.app-sidebar.is-rail .ui-nav-item:hover,
.app-sidebar.is-rail .ui-nav-item:active { transform: none; }
.app-sidebar.is-rail .nav-label { display: none; }

.nav-foot { padding: var(--space-2); border-top: 1px solid var(--c-line); }
.nav-collapse {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    width: 100%;
    padding: var(--space-2);
    border: 0;
    border-radius: var(--radius-md);
    background: transparent;
    color: var(--c-ink-3);
    font-size: var(--fs-xs);
    font-family: inherit;
    cursor: pointer;
    transition: background-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.nav-collapse:hover { background: var(--c-surface-2); color: var(--c-ink-2); }
.nav-collapse .nav-icon { font-size: 15px; line-height: 1; transition: transform var(--dur-med) var(--ease); }
.app-sidebar.is-rail .nav-collapse .nav-label { display: none; }
.app-sidebar.is-rail .nav-collapse .nav-icon { transform: rotate(180deg); }

/* ---------------- 主区 + 顶栏 ---------------- */
.app-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }

.app-topbar {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    height: 56px;
    flex: 0 0 56px;
    padding: 0 var(--space-4);
    background: var(--c-surface);
    border-bottom: 1px solid var(--c-line);
    /* ⚠️ 这里**不能**写 overflow:hidden —— 天气卡片与用户下拉要溢出顶栏显示 */
}
.app-topbar__spacer { flex: 1; }

/* 顶栏工具区：原来是靛蓝实底上的浅色字，白底必须换成墨色。
   ⚠️ 平移到白底后若不管，`text-indigo-100` 在白底上几乎不可见。
   这里用 id 选择器提特异性（(1,1,0) > Tailwind 的 (0,1,0)），HTML 上的类也已同步清掉。 */
.app-topbar #navbar-weather,
.app-topbar #navbar-user-area {
    color: var(--c-ink-3);
    border-radius: var(--radius-md);
    transition: background-color var(--dur-fast) var(--ease);
}
.app-topbar #navbar-weather:hover,
.app-topbar #navbar-user-area:hover { background: var(--c-surface-2); }
.app-topbar #weather-temp,
.app-topbar #navbar-username { color: var(--c-ink); }

/* 角色徽章（原 bg-indigo-500/50 + text-indigo-100，白底上不可读） */
.nav-role-badge {
    font-size: var(--fs-xs);                   /* 12px —— 项目的标签下限，不用原版的 10px */
    line-height: 1.4;
    padding: 1px 6px;
    border-radius: 999px;
    background: var(--c-indigo-soft);
    color: var(--c-indigo);
    font-weight: 500;
}

/* 顶栏分隔线（原为 bg-indigo-400，那是给深底用的高饱和线） */
.nav-sep {
    display: none;
    width: 1px;
    height: 24px;
    margin: 0 var(--space-1);
    background: var(--c-line);
}
@media (min-width: 640px) { .nav-sep { display: inline-block; } }

/* 汉堡：仅窄屏出现（与侧栏抽屉成对，改一个必须改另一个） */
.nav-toggle {
    display: none;
    align-items: center;
    justify-content: center;
    width: 34px; height: 34px;
    border: 0;
    border-radius: var(--radius-md);
    background: transparent;
    color: var(--c-ink-2);
    font-size: 19px;
    cursor: pointer;
    transition: background-color var(--dur-fast) var(--ease);
}
.nav-toggle:hover { background: var(--c-surface-2); }

/* 抽屉遮罩：用 visibility + opacity 而不是 display，
   否则 display:none → block 的切换不可过渡，遮罩会「啪」地出现而不是淡入。
   ⚠️ visibility 参与 transition 才能既淡入又可点（display 不行）。 */
.nav-scrim {
    position: fixed;
    inset: 0;
    background: rgba(15, 23, 42, .42);
    z-index: 35;                               /* 遮罩 < 抽屉(40) */
    opacity: 0;
    visibility: hidden;
    transition: opacity var(--dur-med) var(--ease), visibility var(--dur-med) var(--ease);
}

/* ---------------- 窄屏：侧栏变抽屉 ---------------- */
@media (max-width: 1023px) {
    .app-sidebar {
        position: fixed;
        top: 0; left: 0; bottom: 0;
        height: 100vh;
        transform: translateX(-100%);
        z-index: 40;
        transition: transform var(--dur-med) var(--ease), box-shadow var(--dur-med) var(--ease);
    }
    .app-sidebar.is-drawer-open {
        transform: translateX(0);
        box-shadow: var(--shadow-lift);
    }
    /* 窄屏下「收起成图标栏」没有意义（抽屉要么全开要么全关）→ 强制展开，并藏掉收起按钮 */
    .app-sidebar.is-rail { width: 200px; flex-basis: 200px; }
    .app-sidebar.is-rail .nav-brand { justify-content: flex-start; padding: 0 var(--space-4); }
    .app-sidebar.is-rail .nav-brand__txt { display: flex; }
    .app-sidebar.is-rail .nav-label { display: inline; }
    .app-sidebar.is-rail .nav-list { padding-left: var(--space-2); padding-right: var(--space-2); }
    .app-sidebar.is-rail .ui-nav-item {
        justify-content: flex-start;
        padding-left: var(--space-3);
        padding-right: var(--space-3);
    }
    .nav-foot { display: none; }
    .nav-scrim.is-on { opacity: 1; visibility: visible; }
    .nav-toggle { display: inline-flex; }
}

/* 表格行悬浮 */
tbody tr {
    transition: background-color 0.15s;
}
tbody tr:hover {
    background-color: var(--c-surface-2);
}

/* 拖拽区域激活态 */
.drop-zone-active {
    border-color: var(--c-indigo-line) !important;
    background-color: var(--c-indigo-soft) !important;
}

/* 自定义滚动条 */
::-webkit-scrollbar {
    width: 8px;
    height: 8px;
}
::-webkit-scrollbar-track {
    background: var(--c-line-soft);
}
::-webkit-scrollbar-thumb {
    background: var(--c-scroll);
    border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
    background: var(--c-scroll-hover);
}

/* 动画 */
@keyframes fadeIn {
    from { opacity: 0; transform: translateY(10px); }
    to { opacity: 1; transform: translateY(0); }
}
.fade-in {
    animation: fadeIn 0.2s ease;
}

/* Toast */
.toast-success {
    background-color: var(--c-ok);
    color: white;
}
.toast-error {
    background-color: var(--c-danger);
    color: white;
}
.toast-info {
    background-color: var(--c-info);
    color: white;
}

/* 修复 Tailwind hidden + grid 冲突：确保 hidden 始终优先 */
#expense-month-summary.hidden,
#month-summary-bar.hidden {
    display: none !important;
}

/* 数字字体（表格金额/订单数用，保证**列对齐**）
   ⚠️ 这处与全局栈**不能**简单等同处理：它的职责是"数字在列里对齐"，靠两个机制实现 ——
      ① `font-variant-numeric: tabular-nums`（要求字体本身带等宽数字字形）
      ② 兜底用等宽字体族
   所以改为"**普惠体优先 + 等宽兜底**"：
      · 本机装了普惠体 → 用普惠体 + tabular-nums（若该字体带等宽数字则对齐，已用浏览器实测）
      · 没装 → 回退到 SF Mono / Consolas 等宽族 → **对齐绝对不会坏**
   这样既满足"全站统一字体"，又不拿数据可读性去换观感。 */
.font-mono-num {
    font-variant-numeric: tabular-nums;
    font-family: "Alibaba PuHuiTi 3.0", "Alibaba PuHuiTi 2.0", "Alibaba PuHuiTi",
        "AlibabaPuHuiTi",
        'SF Mono', 'Consolas', 'Courier New', monospace;
}

/* ===== 移动端适配 ===== */
@media (max-width: 640px) {
    /* Toast 居中，避免被右侧遮挡 */
    #toast {
        left: 1rem !important;
        right: 1rem !important;
        bottom: 1rem !important;
        text-align: center;
    }
    /* 移动端表格文字更小，减少内边距 */
    table th, table td {
        padding-left: 0.5rem !important;
        padding-right: 0.5rem !important;
        font-size: 0.75rem;
    }
    /* 表单控件 16px：避免 iOS 聚焦输入框时强制放大页面 */
    input, select, textarea {
        font-size: 16px !important;
    }
    /* 任务卡片、表单等触摸区域不小于 44px */
    button, input, select, textarea {
        min-height: 2.5rem;
    }
    /* 表格内小按钮（编辑/删除等）扩大触摸目标 */
    table tbody button {
        min-height: 2.25rem;
        min-width: 2.25rem;
    }
    /* 天气弹出层在窄屏下宽度自适应 */
    #weather-popup {
        width: calc(100vw - 2rem) !important;
        max-width: 20rem;
    }
}

/* 隐藏移动菜单动画 */
#mobile-menu {
    transition: max-height 0.2s ease-out;
}

/* ===== 收料仓 KPI 卡（2026-09-23 视觉升级 · 作用于现有 DOM id，不改布局结构）=====
   5 格统一为「图标色块 + 标签 → 数值」一套骨架；色由**内联 CSS 变量**给
   （--kpi-tint 图标底 / --kpi-ink 图标与数字色）→ 不新增色类，也不会被 Tailwind 抢优先级。
   ⚠️ 数字一律 tabular-nums：多格并排时不换宽、不抖。
   ⚠️ 中性统计量**不套涨跌红绿** —— 这里的绿/橙是"已收 / 待收"的语义色，不是涨跌。 */
.recv-kpi {
    position: relative;
    overflow: hidden;                 /* 让左侧强调条被圆角裁齐 */
    display: flex;
    flex-direction: column;
    justify-content: center;          /* 内容垂直居中：与旁边的高卡（排行榜）等高时不留顶部堆叠 */
}
/* 左侧强调条：ink → tint 自上而下渐变，比 Tailwind `border-l-4` 的平涂更有层次 */
.recv-kpi::after {
    content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 4px;
    background: linear-gradient(180deg, var(--kpi-ink, var(--c-line)), var(--kpi-tint, var(--c-line-soft)));
}
/* 卡片顶部那根发丝线也跟着本卡主色走（否则 5 格都是同一根靛蓝线，与卡色打架）。
   ⚠️ 若 --kpi-ink 缺失 → 整条 background 作废 → 回落到 .ui-card::before 的原样式，安全。 */
#receiving-kpis > .ui-card::before {
    background: linear-gradient(90deg, var(--kpi-ink), transparent 70%);
    opacity: .38;
}
.recv-kpi__hd {
    display: flex; align-items: center; gap: 6px;
    font-size: var(--fs-xs); color: var(--c-ink-3);
}
.recv-kpi__hd i {
    display: inline-flex; align-items: center; justify-content: center;
    width: 22px; height: 22px; border-radius: 7px; flex: 0 0 auto;
    font-size: 14px; line-height: 1;
    background: var(--kpi-tint, var(--c-surface-3));
    color: var(--kpi-ink, var(--c-ink-2));
    transition: transform var(--dur-fast) var(--ease);
    transform-origin: center;
}
.recv-kpi__val {
    margin-top: 3px;
    font-size: var(--fs-2xl); font-weight: 700; line-height: 1.2;
    color: var(--kpi-ink, var(--c-ink));
    font-variant-numeric: tabular-nums;
}
.recv-kpi__sub { font-size: var(--fs-xs); color: var(--c-ink-3); }
.recv-kpi__note { margin-left: auto; color: var(--c-ink-3); font-size: var(--fs-xs); }

/* 排行榜卡：列表要撑满卡片（不是居中），所以单独放开对齐 */
.recv-kpi--rank { justify-content: flex-start; }

/* ---- 整行进场上浮 + 悬浮抬升（沿用原有动效语言，只是把时长/阴影改成带色相的） ---- */
#receiving-kpis > div {
    transition: transform var(--dur-med) var(--ease), box-shadow var(--dur-med) var(--ease);
    /* ⚠️ fill-mode 必须是 **backwards**，不能是 both ——
       both 会让动画结束后**永久**钉在最后一帧（transform: translateY(0)），
       而 CSS 动画的优先级高于普通声明 → 下面的 `:hover { transform: translateY(-3px) }` 被吃掉，
       表现为"悬停时只有阴影变了、卡片不抬"。换成 backwards：
       延迟期间仍显示 from 帧（错峰进场照旧），动画结束后**撒手**交还给普通样式。 */
    animation: fadeIn .26s ease backwards;
}
/* 逐格延迟：第 1 格 0s，之后每格 +40ms —— 漏了哪一格它就会"抢跑"到前面 */
#receiving-kpis > div:nth-child(2) { animation-delay: .04s; }
#receiving-kpis > div:nth-child(3) { animation-delay: .08s; }
#receiving-kpis > div:nth-child(4) { animation-delay: .12s; }
#receiving-kpis > div:nth-child(5) { animation-delay: .16s; }
/* 悬停：抬起 + **本卡主色**的柔和投影（比中性灰影更有质感）+ 图标轻抬 */
#receiving-kpis > div:hover {
    transform: translateY(-3px);
    box-shadow: 0 10px 22px -12px var(--kpi-ink, rgba(0,0,0,.35));
}
#receiving-kpis > div:hover .recv-kpi__hd i { transform: translateY(-1px) scale(1.08); }

/* ===== 收料仓「供料方数排行」卡（2026-09-23 新增，KPI **最后一格**）=====
   行数多时**卡片内滚动**（不撑高整行 KPI —— 否则 10 个供料人会把整行拉得很高）。
   每行两排：① 名次徽章 + 供料人 + 方量　② 车次 · 结算金额（缩进对齐名字）。
   ⚠️ 名次用「荣誉色」金/银/铜，**不用涨跌红绿** —— 这是排名，没有涨跌含义。 */
.recv-rank {
    /* ⚠️ 是 `0 1 auto` 不是 `1 1 auto`：不要 grow —— 否则只有一个供料人的月份，
       列表也会被撑到 max-height，卡片下方留一大片空白。
       自然高度 + 上限封顶，行数少就矮、行数多才滚动。 */
    flex: 0 1 auto;
    min-height: 0;
    /* 2026-09-23 实测（headless Chromium）：每行 **43.5px**（两排：名+方量 / 车次·金额），
       3 行 = 130.5px。原先给 118px → 第 3 行被裁掉 13px（截图里王国庆那行只看得到一半）。
       这里给 136px = 3 行 + 5px 余量；再多就滚动。改行高前先重测这个值。 */
    max-height: 136px;
    overflow-y: auto;
    margin-top: 6px;
    padding-right: 2px;
}
.recv-rank__row {
    position: relative;
    display: grid;
    grid-template-columns: 16px minmax(0, 1fr) auto;
    align-items: baseline;
    column-gap: 6px;
    padding: 3px 5px;
    border-radius: 6px;
    font-size: var(--fs-sm);
}
/* ★ 方量占比条（该行方量 ÷ 榜首方量）：垫在文字下、从左侧"长出来"。
   ⚠️ 用 transform: scaleX 而不是改 width —— 只走合成层，不触发布局，快了不止一点。
   ⚠️ --p 初值 0，渲染后**下一帧**才由 JS 赋真值 → 才看得到生长过程；
      万一 JS 没跑到，只是少了底色，**数字照常显示**（失败安全，不会把数据弄丢）。 */
.recv-rank__row::before {
    content: ""; position: absolute; inset: 0; border-radius: 6px;
    background: var(--kpi-tint, var(--c-surface-2));
    opacity: .55;
    transform: scaleX(var(--p, 0));
    transform-origin: left center;
    transition: transform .5s cubic-bezier(.22,.8,.28,1) var(--d, 0s);
}
.recv-rank__row > * { position: relative; }   /* 伪元素先绘制，文字压在其上 */
.recv-rank__row:hover::before { opacity: .85; }
.recv-rank__no {
    font-size: var(--fs-xs);
    font-weight: 600;
    line-height: 16px;
    text-align: center;
    color: var(--c-ink-3);
    background: var(--c-surface-2);
    border-radius: 4px;
}
/* 前三名：浅底 + 深字（对比度足够），另加一圈同色描边把"荣誉"钉出来 */
.recv-rank__no.is-top1 { background: #fef3c7; color: #b45309; box-shadow: inset 0 0 0 1px #fcd34d; }
.recv-rank__no.is-top2 { background: #e5e7eb; color: #4b5563; box-shadow: inset 0 0 0 1px #d1d5db; }
.recv-rank__no.is-top3 { background: #ffedd5; color: #c2410c; box-shadow: inset 0 0 0 1px #fdba74; }
.recv-rank__name {
    min-width: 0;                 /* 必须：否则 ellipsis 在 grid 里不生效 */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--c-ink);
}
.recv-rank__vol {
    white-space: nowrap;
    font-weight: 600;
    color: var(--c-ink);
    font-feature-settings: 'tnum';
}
.recv-rank__vol small { font-weight: 400; color: var(--c-ink-3); margin-left: 1px; }
.recv-rank__meta {
    grid-column: 2 / -1;          /* 跨过名次列，与供料人名字左对齐 */
    font-size: var(--fs-xs);
    color: var(--c-ink-3);
    font-feature-settings: 'tnum';
}
.recv-rank__empty { font-size: var(--fs-sm); color: var(--c-ink-3); padding: 2px 0; }

/* 收料记录表格：行悬停轻微上浮与阴影，金额列强调 */
#receiving-table-body tr {
    transition: background-color .15s ease, transform .15s ease;
}
#receiving-table-body tr:hover {
    transform: translateY(-1px);
}
#receiving-table-body .font-mono-num {
    font-feature-settings: 'tnum';
}

/* 结清状态胶囊：柔和描边 + 呼吸光晕 */
#receiving-table-body span[class*="rounded-full"] {
    box-shadow: 0 1px 0 rgba(255,255,255,.6) inset;
}
/* 待结清轻微提醒感（琥珀色光晕），不过度刺眼 */
#receiving-table-body .bg-amber-100 {
    box-shadow: 0 0 0 1px rgba(245,158,11,.25), 0 2px 6px -2px rgba(245,158,11,.25);
}
#receiving-table-body .bg-green-100 {
    box-shadow: 0 0 0 1px rgba(16,185,129,.20);
}

/* 支付截图缩略图：hover 放大 + 描边强调 */
#receiving-table-body a img {
    transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease;
}
#receiving-table-body a:hover img {
    transform: scale(1.12);
    box-shadow: 0 4px 12px -3px rgba(0,0,0,.22);
    border-color: var(--c-primary);
}

/* 操作按钮：默认淡入、hover 强调（桌面用） */
#receiving-table-body tr .text-amber-600,
#receiving-table-body tr .text-red-400 {
    opacity: .72;
    transition: opacity .15s ease, background-color .15s ease;
}
@media (hover: hover) {
    #receiving-table-body tr:hover .text-amber-600,
    #receiving-table-body tr:hover .text-red-400 {
        opacity: 1;
    }
}

/* 下拉选择：供料人/收料人 聚焦描边统一 amber（与主色一致），替代散落不一致描边 */
#receiving-form select,
#receiving-form input[type="date"] {
    transition: border-color .15s ease, box-shadow .15s ease;
}
#receiving-form select:focus,
#receiving-form input[type="date"]:focus {
    border-color: var(--c-primary);
    box-shadow: 0 0 0 3px rgba(245,158,11,.15);
}

/* 支付截图上传区：placeholder 图标缓动，hover 更醒目 */
#recv-drop-zone:hover #recv-placeholder i {
    transform: translateY(-2px) scale(1.05);
    color: var(--c-primary);
}
#recv-drop-zone #recv-placeholder i {
    transition: transform .18s ease, color .18s ease;
}

/* 供料人选中档案信息条：点亮边框做反馈 */
#supplier-selected-info:not(.hidden) {
    border-left: 3px solid var(--c-primary);
    padding-left: .5rem;
    background: linear-gradient(to right, rgba(251,191,36,.08), transparent);
    border-radius: 4px;
}

/* 供料人档案表格：行分隔更清晰 */
#supplier-list-body tr {
    transition: background-color .15s ease;
}


/* ===== UI Designer 全局视觉系统（作用于所有频道，零 HTML 改动） ===== */

/* --- 设计令牌：把 Tailwind 散落 class 收敛到 CSS 变量，便于全局一致性 --- */
:root {
    /* 间距 4px 模块化 */
    --space-1: 0.25rem;
    --space-2: 0.5rem;
    --space-3: 0.75rem;
    --space-4: 1rem;
    --space-6: 1.5rem;
    --space-8: 2rem;
    /* 圆角 */
    --radius-md: 8px;
    --radius-lg: 12px;
    --radius-xl: 16px;
    /* 阴影两级 + focus ring */
    --shadow-card: 0 1px 2px rgba(15, 23, 42, 0.04), 0 1px 3px rgba(15, 23, 42, 0.06);
    --shadow-lift: 0 8px 24px -8px rgba(15, 23, 42, 0.12), 0 2px 6px rgba(15, 23, 42, 0.04);
    --ring-focus: 0 0 0 3px rgba(245, 158, 11, 0.18);
    --ring-focus-indigo: 0 0 0 3px rgba(79, 70, 229, 0.18);
    /* 动效 */
    --ease: cubic-bezier(0.22, 1, 0.36, 1); /* expo-out 风格 */
    --dur-fast: 120ms;
    --dur-med: 200ms;
    /* 加载转圈（spinner）每圈时长：1.4s = Material Design / Ant Design 的标准不定进度节奏。
       Tailwind 默认 animate-spin 是 1s，在本系统里观感偏急；统一用 1.4s。
       改这里即可全局调整所有加载动画速度。 */
    --dur-spin: 1.4s;

    /* ---- 颜色令牌（2026-09-18 补齐）----
       此前 :root 只有间距/圆角/阴影/动效，**颜色一个变量都没有** —— 全站硬编码着
       var(--c-primary) / var(--c-info) / var(--c-line) / var(--c-ink) / var(--c-surface-2) … 至少 16 处散落，
       改一次主色要全局搜替换，且极易漏改导致"同一语义两个色值"。
       命名约定：surface=面、ink=文字、line=描边、soft=浅底。 */
    --c-primary:        #f59e0b;   /* 系统主色（amber-500）：聚焦、强调、收料仓 */
    --c-primary-strong: #d97706;   /* amber-600：hover / 深一档文字 */
    --c-indigo:         #4f46e5;   /* indigo-600：新增类主操作 */
    --c-indigo-strong:  #4338ca;
    --c-ink:            #1e293b;   /* 正文主色（slate-800） */
    --c-ink-2:          #475569;   /* 次级文字（slate-600） */
    --c-ink-3:          #64748b;   /* 弱化文字（slate-500，仍 ≥4.5:1） */
    --c-line:           #e2e8f0;   /* 分隔线（slate-200） */
    --c-line-soft:      #f1f5f9;   /* 更浅的行分隔（slate-100） */
    --c-surface:        #ffffff;
    --c-surface-2:      #f8fafc;   /* 行 hover / 次级面 */
    --c-surface-3:      #fafbfc;   /* 表头渐变起点 */
    --c-surface-4:      #f5f7fa;   /* 表头渐变终点 */
    --c-indigo-line:    #6366f1;   /* indigo-500：拖拽激活描边 */
    --c-indigo-soft:    #eef2ff;   /* indigo-50：拖拽激活底色 */
    --c-scroll:         #cbd5e1;   /* 滚动条滑块 */
    --c-scroll-hover:   #94a3b8;
    /* 语义色：ok / warn / danger / info 各配一个浅底 */
    --c-ok:          #059669;  --c-ok-soft:     #ecfdf5;
    --c-warn:        #d97706;  --c-warn-soft:   #fffbeb;
    --c-danger:      #dc2626;  --c-danger-soft: #fef2f2;
    --c-info:        #4f46e5;  --c-info-soft:   #eef2ff;

    /* ---- 字号令牌（2026-09-18 补齐）----
       此前全站混用 text-[10px] / text-[11px] / text-xs / text-sm / text-base / text-lg，
       没有统一尺度；上一轮布局审计实测出「口径说明 11px」低于可读下限就是这个缺口的后果。
       **下限 12px**：12px 以下的正文字号属可读性反模式，不要再新增。 */
    --fs-xs:   12px;   /* 辅助说明 / 表头 / 徽章 */
    --fs-sm:   13px;   /* 表格正文 / 次级信息 */
    --fs-base: 14px;   /* 正文默认 */
    --fs-lg:   16px;   /* 卡片数值 / 小标题 */
    --fs-xl:   18px;   /* 概览卡大数值 */
    --fs-2xl:  22px;   /* KPI 数值（2026-09-20 补：原先漏定义，
                          导致 .kpi__v 的 font-size 解析失败、静默退回继承值 14px） */
}

/* --- 统一加载转圈动画（替代 Tailwind animate-spin 的 1s 节奏） ---
   1.4s/圈、linear 匀速、无限循环：与 Material / Ant Design 的加载指示器一致，
   观感更自然流畅。所有 spinner 都挂 .spin-slow，速度由 --dur-spin 一个变量控制。
   注意：不再使用 Tailwind 的 animate-spin，避免两处 animation 声明打架。 */
@keyframes spinSmooth {
    to { transform: rotate(360deg); }
}
.spin-slow {
    display: inline-block;
    animation: spinSmooth var(--dur-spin, 1.4s) linear infinite;
}

/* --- 全局节奏：链接默认无下划线、过渡统一 --- */
a { transition: color var(--dur-fast) var(--ease); }
button { transition: background-color var(--dur-fast) var(--ease),
                   color var(--dur-fast) var(--ease),
                   transform var(--dur-fast) var(--ease),
                   box-shadow var(--dur-fast) var(--ease); }

/* --- 章节标题统一：所有频道的 h3 标题使用同一节奏 --- */

/* --- 频道章节底色：白底卡片视觉收口 --- */
#section-dashboard > *,
#section-expenses > *,
#section-data > *,
#section-commission > *,
#section-receiving > *,
#section-import > * {
    /* 子元素保持原样式，但保证顶层无突兀灰底混杂 */
}

/* --- 表格全局美化（除收料仓特殊样式外的统一） --- */
table thead th {
    background: linear-gradient(180deg, var(--c-surface-3) 0%, var(--c-surface-4) 100%);
    color: var(--c-ink-2);
    font-weight: 600;
    letter-spacing: 0.01em;
    text-transform: none;
    border-bottom: 1.5px solid var(--c-line);
}
table tbody td {
    border-bottom: 1px solid var(--c-line-soft);
    color: var(--c-ink);
    font-variant-numeric: tabular-nums;
}
table tbody tr:last-child td { border-bottom: none; }
table tbody tr {
    transition: background-color var(--dur-fast) var(--ease);
}
/* 除内网盘 explorer-card 外的默认行 hover（被 :hover 规则覆盖的保留） */
table thead + tbody tr:hover {
    background-color: var(--c-surface-2);
}

/* --- 按钮全局微交互（针对实际按钮元素，避免影响 cell-link） --- */
button[type="submit"],
button.bg-amber-600,
button.bg-indigo-600 {
    box-shadow: 0 1px 0 rgba(255,255,255,0.15) inset, 0 1px 2px rgba(15,23,42,0.06);
}
button[type="submit"]:hover,
button.bg-amber-600:hover,
button.bg-indigo-600:hover {
    transform: translateY(-1px);
    box-shadow: 0 4px 10px -2px rgba(15,23,42,0.18), 0 1px 0 rgba(255,255,255,0.15) inset;
    filter: brightness(1.03);
}
button[type="submit"]:active,
button.bg-amber-600:active,
button.bg-indigo-600:active {
    transform: translateY(0);
    box-shadow: 0 1px 2px rgba(15,23,42,0.06);
}

/* ★ 2026-09-21：新增收料时「收料人」锁定为当前登录用户（disabled）。
   ⚠️ disabled 控件**不派发任何 click 事件**（见下方"禁用态"一节的教训），
   所以"锁住"必须**看起来就是锁住的、且文字仍读得清** —— 默认的灰化会让人以为值丢了。
   用浅底 + 正常字色表达"只读"，而不是把对比度压到读不清。 */
#receiving-form-receiver:disabled {
    background-color: var(--c-surface-3);
    color: var(--c-ink);
    cursor: not-allowed;
    opacity: 1;                       /* 压掉浏览器对 disabled 的整体降透明 */
    -webkit-text-fill-color: var(--c-ink);   /* Safari/Chrome 会另用这个属性染灰 */
    /* 去掉原生下拉箭头：状态上"点不动"，视觉上也**不该再像个可选项** */
    -webkit-appearance: none;
    -moz-appearance: none;
    appearance: none;
    background-image: none;
    padding-right: var(--space-3);
}

/* --- 输入框聚焦统一琥珀描边（页面级别） --- */input[type="text"]:focus,
input[type="number"]:focus,
input[type="date"]:focus,
input[type="tel"]:focus,
input[type="password"]:focus,
select:focus,
textarea:focus {
    box-shadow: var(--ring-focus);
    border-color: var(--c-primary);
}
/* indigo 系输入框（登录页、任务等）用 indigo focus ring */

/* --- 标签页 / Tab 切换态：active tab 底部细线点缀 --- */

/* --- Toast 入场动画 --- */
#toast {
    animation: toastIn var(--dur-med) var(--ease);
}
@keyframes toastIn {
    from { transform: translateY(8px); opacity: 0; }
    to   { transform: translateY(0); opacity: 1; }
}

/* --- 弹窗入场动画：背景淡入 + 卡片轻微上浮 --- */
[id^="modal-"]:not(.hidden) > div:nth-child(2) {
    animation: modalIn var(--dur-med) var(--ease);
}
[id^="modal-"].hidden { display: none !important; }
@keyframes modalIn {
    from { transform: translateY(12px) scale(0.985); opacity: 0; }
    to   { transform: translateY(0) scale(1); opacity: 1; }
}
/* 背景遮罩淡入 */
[id^="modal-"]:not(.hidden) > div:first-child {
    animation: overlayIn var(--dur-med) var(--ease);
}
@keyframes overlayIn {
    from { opacity: 0; }
    to   { opacity: 1; }
}

/* --- 卡片统一阴影（不破坏原 border-l-* 强调条） --- */
.bg-white.rounded-xl.shadow-sm {
    box-shadow: var(--shadow-card);
}
.bg-white.rounded-xl.shadow-sm:hover {
    box-shadow: var(--shadow-lift);
}

/* --- 状态徽章统一微调：圆角统一 999，内边距统一 --- */
.rounded-full {
    letter-spacing: 0.01em;
}

/* --- 空状态文字 --- */

/* --- 响应式：移动端关键操作按钮加底栏样式（避免 hover-only 触发的隐藏按钮不可见） --- */
@media (max-width: 640px) {
    table thead th, table tbody td { white-space: nowrap; }
    /* 移动端表格行按钮始终可见（避免 hover 隐藏导致不可点击） */
    table tbody tr td:last-child button { opacity: 1 !important; }
    /* 表头字号略减 */
    table thead th { font-size: 11px; padding-top: 8px; padding-bottom: 8px; }
}

/* --- 减少动效偏好支持（无障碍） --- */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        transition-duration: 0.01ms !important;
    }
}

/* --- 滚动条增强（已存在，现统一宽度） --- */
::-webkit-scrollbar { width: 10px; height: 10px; }

/* ==================== UI 组件类（2026-09-18 第 1 步：把散落的 Tailwind 组合收敛成一套） ====================
   【核心约定：替换，而不是叠加】
   正确用法 = <div class="ui-card"> 取代 <div class="bg-white rounded-xl shadow-sm">；
   错误用法 = 在原有 Tailwind 组合上再追加 ui-card。
   原因：Tailwind 是**本地自托管 Play CDN**（index.html:10 引用 /static/tailwind.js），
   它运行时把 <style> 注入 head，位置在 style.css **之后** → 同权重下 Tailwind 必胜。
   叠加写法会逼出一堆 !important（本项目已吃过这个亏：.animate-spin 覆盖无效）。
   替换写法下两者不存在竞争，因此这里的组件类都**不写 !important**。

   ⚠️ 新增类名必须同步加入 check_before_deploy.py 的 CUSTOM_CLASSES，
      否则关卡不会替你校验"用了但没定义"这个最常见的静默失效。 */

/* --- 卡片 --- */
.ui-card {
    background: var(--c-surface);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-card);
    overflow: hidden;
    transition: box-shadow var(--dur-med) var(--ease);
}
.ui-card__hd {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-4);
    border-bottom: 1px solid var(--c-line);
}
.ui-card__hd h3,
.ui-card__hd h4 {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    margin: 0;
    font-size: var(--fs-base);   /* = 原 text-sm(14px)，等价替换 */
    /* ⚠️ 刻意**不设** color / font-weight：
       本选择器是 (0,1,1)（一个类 + 一个类型），会盖过 Tailwind 的单类规则 (0,1,0)。
       若在这里写 color，就会把原有的 text-gray-700 换掉，造成**无意的视觉回归**。
       「组件类只负责它该负责的维度」——布局与尺寸归组件类，配色留给语义工具类。 */
}

/* --- 按钮：一套尺寸 + 语义变体 --- */
.ui-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.375rem;
    padding: 0.5rem var(--space-4);
    border: 1px solid transparent;
    border-radius: var(--radius-md);
    font-size: var(--fs-base);
    font-weight: 500;
    line-height: 1.25;
    /* ★ 2026-09-24：`.ui-btn` 允许被用作 `<a>`（外链入口等）→ 少了这行链接会带默认下划线。
       对 `<button>` 本来就是无影响的（现有按钮全是 `<button>`，这行是给将来的 `<a>` 兜底）。 */
    text-decoration: none;
    cursor: pointer;
    transition: background-color var(--dur-fast) var(--ease),
                color var(--dur-fast) var(--ease),
                border-color var(--dur-fast) var(--ease),
                box-shadow var(--dur-fast) var(--ease),
                transform var(--dur-fast) var(--ease);
}
/* ★ 2026-09-21 口感反馈：按下时轻微收缩（"按得到"的手感）。只动 transform，GPU 友好 */
.ui-btn:active:not(:disabled) { transform: scale(.97); }
.ui-btn:disabled { opacity: .55; cursor: not-allowed; }
.ui-btn--primary { background: var(--c-indigo); color: #fff; }
.ui-btn--primary:hover:not(:disabled) { background: var(--c-indigo-strong); }
.ui-btn--danger  { background: var(--c-danger); color: #fff; }
.ui-btn--ghost   { background: transparent; color: var(--c-ink-2); border-color: var(--c-line); }
.ui-btn--ghost:hover:not(:disabled) { background: var(--c-surface-2); }
/* 尺寸变体 */
.ui-btn--sm { padding: 0.375rem 0.625rem; font-size: var(--fs-xs); }
.ui-btn--xs { padding: 0.375rem 0.625rem; font-size: var(--fs-xs); border-radius: 6px; }
/* 图标按钮（正方形，满足 WCAG 2.2 AA 的 24×24 CSS px 下限） */

/* --- 状态徽章（`.ui-badge` 一族）---
   ★ 2026-09-21：**已整体删除**。它最后只剩销售页组头那个「已录 n/4 平台 · 缺xxx」在用，
   而用户指出默认折叠后 19 个组头挂着一模一样的琥珀色胶囊、纯噪音 → 徽章取消，
   样式与 `CUSTOM_CLASSES` 登记一并清掉（不留死代码）。要再用就重新定义。 */

/* --- 表单控件（取代散落的 border-gray-300 rounded-lg px-3 py-2 text-sm） --- */
.ui-field {
    padding: 0.5rem var(--space-3);
    border: 1px solid var(--c-line);
    border-radius: var(--radius-md);
    font-size: var(--fs-base);
    color: var(--c-ink);
    background: var(--c-surface);
    transition: border-color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
}
.ui-field:focus {
    outline: none;
    border-color: var(--c-primary);
    box-shadow: var(--ring-focus);
}

/* --- 表格容器（统一横滚 + 圆角裁切） --- */
.ui-tablewrap { overflow-x: auto; }

/* ==================== 禁用态：让「锁住」一眼可见（2026-09-19 新增） ====================
   背景：费用表单的「凭证强制前置」会把 7 个字段 + 保存按钮设为 disabled，而
   **disabled 元素不派发任何 click 事件**（真机实测：连 document 捕获阶段都收不到）
   → 用户点它毫无反馈，会以为整个页面卡死（就是这次报的「点击无响应」）。
   光靠文字提示不够，控件本身必须"看起来就是不能点" —— 否则蓝色的保存按钮
   看着像可用的，实际点不动。
   选择器用 ID 提高特异性 (1,1,0)，压过 Tailwind（本地 Play CDN，运行时注入的
   <style> 在 style.css 之后，同权重它会赢）。禁用控件不受 WCAG 对比度约束。 */
#expense-form :disabled {
    background-color: #f3f4f6;
    color: #9ca3af;
    border-color: #e5e7eb;
    cursor: not-allowed;
}


/* ==========================================================================
   销售数据页 v2（2026-09-20）：页面骨架 / 分组表
   —— 全部走既有设计令牌，不新增硬编码颜色
   ★ 2026-09-21 质感层（用户：「能加图标的就加上，能做效果的做上，让界面显得有质感」）：
     原则取自设计库的 micro-interactions + 分层景深：
       · 图标 = RemixIcon（**已自托管，零新增依赖**），只在"能表达语义"的地方加
       · 动效 50~120ms、只动 transform / opacity / box-shadow（GPU 友好）
       · 全部硬编码色都取自既有令牌或同色系浅色，"质感"不靠加饱和度
       · ⚠️ 全局已有 `prefers-reduced-motion` 兜底（动效时长压到 0.01ms），无障碍不受影响
   ========================================================================== */
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }

/* --- ★ 质感：卡片顶部 1px 渐变发丝线（"高级感"最廉价的一招）+ 悬停抬升 --- */
.ui-card { position: relative; }
.ui-card::before {
    content: ""; position: absolute; inset: 0 0 auto 0; height: 2px; pointer-events: none;
    background: linear-gradient(90deg, rgba(99,102,241,.55), rgba(14,165,233,.35) 45%, rgba(99,102,241,0) 92%);
    opacity: .9;
}
.ui-card__hd h3 i { color: var(--c-indigo); font-size: 1.05em; }

/* --- ① 页头 + KPI 汇总条 --- */
.page__hd { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; padding: var(--space-3) var(--space-4); }
.page__title { font-size: var(--fs-xl); font-weight: 600; }
.page__title small { font-size: var(--fs-xs); font-weight: 400; color: var(--c-ink-3); margin-left: 6px; }
.page__filters { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.page__filters .ui-field { width: auto; }
.page__actions { margin-left: auto; display: flex; gap: var(--space-2); flex-wrap: wrap; align-items: center; }
.kpi-strip { display: grid; grid-template-columns: repeat(auto-fit, minmax(128px, 1fr)); border-top: 1px solid var(--c-line); }
.kpi { padding: 10px var(--space-4); border-right: 1px solid var(--c-line); }
.kpi:last-child { border-right: 0; }
.kpi__k { font-size: var(--fs-xs); color: var(--c-ink-3); }
.kpi__v { font-size: var(--fs-2xl); font-weight: 700; line-height: 1.25; margin-top: 2px; }
/* ★ 2026-09-21 质感：标签前挂一枚**语义图标色块**。
   色由内联 CSS 变量给（--kpi-tint 底 / --kpi-ink 图标色）→ **不用新增类名**，
   也就不会和 Tailwind 抢优先级。数值用 tabular-nums 保证多格对齐不抖。 */
.kpi__k { display: flex; align-items: center; gap: 6px; }
.kpi__k i {
    display: inline-flex; align-items: center; justify-content: center;
    width: 20px; height: 20px; border-radius: 6px; flex: 0 0 auto;
    font-size: 13px; line-height: 1;
    background: var(--kpi-tint, var(--c-surface-3));
    color: var(--kpi-ink, var(--c-ink-2));
    transition: transform var(--dur-fast) var(--ease);
}
.kpi__v { font-variant-numeric: tabular-nums; }
/* 悬停：图标轻轻抬起 + 整格极淡底色。幅度刻意很小（50~120ms），只为"有反馈"，不抢注意力 */
.kpi { transition: background-color var(--dur-fast) var(--ease); }
.kpi:hover { background: var(--c-surface-2); }
.kpi:hover .kpi__k i { transform: translateY(-1px) scale(1.06); }

/* --- ② 工具条 --- */
.sales-toolbar { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap;
    padding: 10px var(--space-4); border-bottom: 1px solid var(--c-line); background: var(--c-surface-3); }
.sales-search { position: relative; flex: 1 1 200px; max-width: 300px; }
.sales-search input { padding-left: 32px; }
.sales-search svg { position: absolute; left: 10px; top: 50%; transform: translateY(-50%);
    width: 14px; height: 14px; color: var(--c-ink-3); pointer-events: none; }
.sales-toolbar__spacer { margin-left: auto; }
.sales-sortchip { display: inline-flex; align-items: center; gap: 5px; font-size: var(--fs-xs);
    color: var(--c-ink-2); background: var(--c-surface); border: 1px solid var(--c-line);
    border-radius: 999px; padding: 4px 10px; }
.sales-sortchip b { color: var(--c-ink); font-weight: 600; }
/* ★ 2026-09-21 工具条图标：`> i` 直接命中两个筛选图标（不新增类名）；
   它们只是**视觉前缀**，不承担语义（select 自己带 label，屏幕阅读器靠 sr-only label）。 */
.sales-toolbar > i {
    color: var(--c-ink-3); font-size: var(--fs-base); flex: 0 0 auto;
    margin-right: -4px;   /* 贴住右边的 select，形成"图标 + 控件"一组 */
}
.sales-sortchip i { color: var(--c-ink-3); font-size: var(--fs-sm); margin-right: 2px; }

/* --- ③ 唯一录入弹窗（2026-09-20）---
   ⚠️ 原先这里是「编辑态」的一整套（#edit-toggle / .iso-text / .iso-input / .only-read /
   .only-edit / .edit-banner），随「收敛为唯一录入入口」全部移除 —— 表格已改回**纯只读**，
   改数据一律走「录入当日数据」弹窗。别照着旧记忆再加回来。 */
.day-entry-wrap { overflow-x: auto; }
.day-entry { border: 1px solid var(--c-line); border-radius: var(--radius-md); overflow: hidden; }
.day-entry table { width: 100%; border-collapse: collapse; }
.day-entry thead th { background: var(--c-surface-3); color: var(--c-ink-3);
    font-size: var(--fs-xs); font-weight: 500; white-space: nowrap;
    padding: 6px 8px; text-align: right; border-bottom: 1px solid var(--c-line); }
.day-entry tbody th, .day-entry tbody td { padding: 5px 8px; font-size: var(--fs-sm);
    border-bottom: 1px solid var(--c-line-soft); }
.day-entry tbody th { font-weight: 500; color: var(--c-ink-2); text-align: left; white-space: nowrap; }
.day-entry tbody td { text-align: right; }
.day-entry thead th:first-child { text-align: left; }
.day-entry tbody tr:last-child th, .day-entry tbody tr:last-child td { border-bottom: 0; }
/* 单元格输入框：宽度自适应（3 个评价框挤在一格，所以下限取小值） */
.de-in { width: 100%; min-width: 58px; padding: 4px 6px; font-size: var(--fs-sm);
    font-family: inherit; color: var(--c-ink); background: #fff; border: 1px solid var(--c-line);
    border-radius: 6px; text-align: right; font-variant-numeric: tabular-nums; }
.de-in:focus { outline: none; border-color: var(--c-indigo); box-shadow: var(--ring-focus-indigo); }
.de-in::placeholder { color: var(--c-ink-3); }
/* 金额格（退款 / 结算金额）：右对齐不变，但给个浅色底把它与件数格区分开
   —— 8 列里有 6 列是件数、只有 2 列是钱，靠底色一眼能认出哪两格填钱 */
.de-in--amt { background: #f8fbff; }
.de-in--amt:focus { background: #fff; }

/* --- ④ 分组表（**13 列**，表头两行：列带 + 列名） --- */
.merged-daily .sales-th { padding: 3px 8px; font-size: var(--fs-xs); color: var(--c-ink-3);
    white-space: nowrap; border-bottom: 1px solid var(--c-line);
    background: linear-gradient(180deg, var(--c-surface-3), var(--c-surface-4)); text-align: right; }
.merged-daily thead tr.sales-band th { position: sticky; top: 0; z-index: 22; height: 20px; padding: 0 8px;
    font-size: 11px; letter-spacing: .1em; text-transform: uppercase; font-weight: 500; text-align: center; }
/* ★ 2026-09-21：表名行底边加深 1px —— 让 sticky 表头在滚动时"浮"在内容之上（分层景深），纯静态零 JS */
.merged-daily thead tr.sales-cols th { position: sticky; top: 20px; z-index: 21; height: 26px;
    box-shadow: inset 0 -1px 0 var(--c-line); }
.merged-daily thead tr.sales-band th i { margin-right: 4px; opacity: .8; }
.merged-daily thead tr.sales-cols th i { margin-right: 3px; opacity: .7; }
.merged-daily thead tr.sales-cols th { position: sticky; top: 20px; z-index: 21; height: 26px; }
/* 「日期 / 平台」两格跨两行（rowspan=2）—— 必须单独定样式：
   它们长在 .sales-band 行里，会继承列带那套「小字号 + letter-spacing + 大写」，
   导致左上角两格与右边分组标签串味。这里改回正常表头样式并垂直居中。 */
.merged-daily thead th.col-date, .merged-daily thead th.col-channel {
    text-align: left; vertical-align: middle; letter-spacing: 0; text-transform: none;
    font-size: var(--fs-xs); font-weight: 600; color: var(--c-ink-2);
}
.merged-daily th.band-start, .merged-daily td.band-start { border-left: 1px solid var(--c-line); }
.merged-daily .col-date { position: sticky; left: 0; z-index: 12; width: 92px; min-width: 92px;
    text-align: left; background: var(--c-surface); }
.merged-daily .col-channel { position: sticky; left: 92px; z-index: 11; width: 82px; min-width: 82px;
    text-align: left; background: var(--c-surface); }
.merged-daily thead .col-date, .merged-daily thead .col-channel { z-index: 30; }
.merged-daily .col-channel::after { content: ""; position: absolute; top: 0; right: 0; bottom: 0; width: 6px;
    transform: translateX(100%); pointer-events: none;
    background: linear-gradient(90deg, rgba(15,23,42,.06), transparent); }
.merged-daily tbody td { padding: 3px 8px; font-size: var(--fs-sm); text-align: right; white-space: nowrap;
    border-bottom: 1px solid var(--c-line-soft); background: var(--c-surface);
    transition: background-color var(--dur-fast) var(--ease); }
/* ⚠️ 2026-09-21：正文**保持 --fs-sm(13px)**，不要为了紧凑压到 12px ——
   上一轮压过一次，用户直接反馈"字体变小了，视觉上看起来好疲惫"。
   紧凑感靠**内边距与行高**拿（padding 8px10px → 3px8px、按钮不再顶高行），不靠缩字。
   12px 只用于表头标签与徽章这类短文本。 */
.merged-daily tbody td.col-date:empty { box-shadow: inset 3px 0 0 var(--c-line); }
.merged-daily tbody tr.group-head td { background: linear-gradient(180deg, var(--c-surface-2), var(--c-line-soft));
    border-top: 2px solid var(--c-line); border-bottom: 1px solid var(--c-line);
    padding: 4px 8px; font-size: var(--fs-sm); color: var(--c-ink-2);
    transition: background-color var(--dur-fast) var(--ease); }
/* ★ 2026-09-21：组头整行可点（用户：「不用费劲去点那个小箭头才能展开，单击某一排就自动展开」）
   —— 所以整行给 pointer 光标 + 悬停反馈，别让用户以为只有箭头那一小块能点。 */
.merged-daily tbody tr.group-head { cursor: pointer; }
.merged-daily tbody tr.group-head:hover td { background: linear-gradient(180deg, var(--c-line-soft), var(--c-surface-4)); }
/* 「今天」小胶囊：替代原来的纯文字 `· 今天`，一眼能扫到 */
.g-today {
    display: inline-flex; align-items: center; gap: 3px; margin-left: 4px;
    padding: 0 6px; border-radius: 999px; font-size: var(--fs-xs); line-height: 1.6;
    background: var(--c-indigo-soft); color: var(--c-indigo-strong); font-weight: 500;
}
/* 空态：图标 + 文案两行，比一行灰字有交代 */
.ui-empty { text-align: center; padding: 40px 0; color: var(--c-ink-3); font-size: var(--fs-sm); }
.ui-empty i { display: block; font-size: 34px; margin-bottom: 8px; opacity: .5; }
.merged-daily tbody tr.group-head td.col-date { background: var(--c-surface-4); font-size: var(--fs-base);
    font-weight: 600; color: var(--c-ink); }
.merged-daily tbody tr.group-head td.col-channel { background: #f4f7fa; }
.merged-daily tbody tr.group-head .g-left { text-align: left; }
.merged-daily tbody tr.group-head .g-sum { text-align: right; }
.merged-daily tbody tr.group-head .g-sum b { color: var(--c-ink); }
.merged-daily tbody tr.group-head .g-bar-cell { text-align: right; }
.merged-daily .orders-biz { font-weight: 600; }
.merged-daily .money { font-weight: 600; }
.merged-daily .is-null { color: var(--c-ink-3); }
.merged-daily .is-zero { color: var(--c-ink-2); }
.merged-daily .refund-pos { color: var(--c-warn); font-weight: 600; }
.merged-daily td.ops, .merged-daily th.ops { text-align: center; }
/* ★ 2026-09-21 紧凑化：操作列那两个图标按钮**曾把整行顶到 36px** ——
   `.ui-btn--xs` 默认上下各 6px 内边距 + 12px 字 → 27px 高的按钮，比同行的文字格高一大截。
   只在本表内收紧（**不动全局 .ui-btn--xs**：其它页面的按钮仍要够大好点，触控目标靠行高兜住）。 */
.merged-daily td.ops .ui-btn--xs { padding: 1px 5px; line-height: 1.2; }
.merged-daily td.ops .ui-btn--xs + .ui-btn--xs { margin-left: 2px; }
/* 组头的徽章同理：line-height 1.6 → 1.35，别让一枚徽章决定整行高度 */
.merged-daily .ui-badge { line-height: 1.35; padding: 1px 6px; }
.g-toggle { display: inline-flex; align-items: center; gap: 5px; cursor: pointer; border: 0;
    background: transparent; font: inherit; color: var(--c-ink-2); padding: 1px 4px; border-radius: 6px; }
.g-toggle:hover { background: #e8eef5; color: var(--c-ink); }
.g-caret { transition: transform var(--dur-fast) var(--ease); }
.merged-daily tr.group-head.is-closed .g-caret { transform: rotate(-90deg); }
.g-bar { display: inline-block; width: 40px; height: 5px; border-radius: 999px;
    background: #e2e8f0; overflow: hidden; vertical-align: middle; }
.g-bar > i { display: block; height: 100%; background: var(--c-primary); border-radius: 999px; }
/* 二级列（录入时间）固定宽度，避免列宽被最长内容拉拽抖动 */
.merged-daily .col-p2 { width: 84px; min-width: 84px; }
.merged-daily .cell-channel { font-weight: 500; }
/* 折叠：整组明细行一起隐藏 */
.merged-daily tbody tr.rows-of-day.is-hidden { display: none; }
.merged-daily tbody tr.rows-of-day:hover td { background: var(--c-surface-2); }
/* 底行去掉最后一条分隔线，与页脚衔接 */
.merged-daily tbody tr.rows-of-day:last-child td { border-bottom: 0; }

/* --- ⑤ 产品规格单量矩阵（**2026-09-21 已整体移除**）---
   原来这里是一族 .spx-* 规则（矩阵容器 / 卡片 / 数值 / 占比条 / 对账行）。
   该功能已删（当日录入的「按平台 × 规格」取代了它），规则一并清除。
   ⚠️ 只保留 --fs-2xl 令牌：.kpi__v 仍在用它，别一起删。 */

/* --- ⑥ 表格页脚 --- */
.sales-foot { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap;
    padding: 6px var(--space-4); border-top: 1px solid var(--c-line);
    background: var(--c-surface-3); font-size: var(--fs-xs); color: var(--c-ink-3); }

@media (max-width: 767px) {
    .kpi { padding: 10px var(--space-3); }
    .kpi__v { font-size: var(--fs-xl); }
    .page__actions { margin-left: 0; }
}

/* ==========================================================================
   ★ 2026-09-21 无障碍补齐（全局 focus 兜底）
   审计脚本量化出的缺陷：`button` 有 3 条规则、**带 :focus 的 0 条** ——
   键盘用户 Tab 到任何按钮上都没有可见焦点（WCAG 2.2 的 visible-focus 要求）。
   输入框此前靠 Tailwind 的 focus:ring 各自为政，按钮/链接则完全没有。
   这里统一兜底，**只用 :focus-visible**：鼠标点击不会冒出焦点框，只有键盘/读屏才显示。
   ⚠️ 用真实 `outline`（而非仅 box-shadow）—— 强制高对比模式下 outline 才会被保留。
   ========================================================================== */
:where(a, button, summary, [tabindex]):focus-visible {
    outline: 2px solid var(--c-primary);
    outline-offset: 2px;
    border-radius: var(--radius-md);
}
/* 深色底/主色按钮上换成白描边，避免"琥珀描边贴在琥珀按钮上看不见" */
:where(.bg-indigo-600, .bg-amber-600, .bg-green-600, .ui-btn--primary):focus-visible {
    outline-color: var(--c-surface);
    box-shadow: 0 0 0 2px var(--c-ink);
}

