/* ==========================================================================
   迷石 · 运营前台  ——  V3 沙盒视觉语言全量样式表（合并版 V3.1）
   ----------------------------------------------------------------------------
   设计源头  : C:\Projects\mishi_v3_sandbox\public\style.css
   规范依据  : 《前台页面细则.md》 §1 全局 UI 设计体系 / §18 实现细则
   关键支柱  : 深空底色 · 毛玻璃面板 · 霓虹辉光 · 动效呼吸
   废弃声明  : V2 黑蓝极客色板 + JetBrains Mono 字体 永久作废
   合并说明  : 本表 = 老 V3 沙盒精致皮肤  +  V3 审计新增功能（vault 角标 / 底数
              预览面板 / 骨架屏 / 三段优先级标签 / 闭店结余巨号）
   ========================================================================== */

@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&family=JetBrains+Mono:wght@400;500;700;800&display=swap');

/* ============= 0. 设计 Token (CSS Variables) ============= */
:root {
    /* —— 深空底色 / 面板 —— */
    --bg-main:        #050508;
    --bg-base:        #050508;                    /* legacy alias */
    --bg-panel:       rgba(15, 15, 20, 0.7);
    --bg-panel-deep:  rgba(10, 10, 15, 0.85);
    --bg-panel-modal: rgba(15, 15, 20, 0.95);
    --bg-hover:       rgba(40, 40, 60, 0.5);
    --bg-input:       rgba(0, 0, 0, 0.3);
    --bg-input-focus: rgba(0, 0, 0, 0.5);
    --bg-card-sub:    rgba(0, 0, 0, 0.2);
    --bg-elev1:       rgba(15, 15, 20, 0.65);
    --bg-elev2:       rgba(20, 20, 28, 0.65);
    --bg-elev3:       rgba(28, 28, 38, 0.65);

    /* —— 柔性边框 —— */
    --border-color:    rgba(255, 255, 255, 0.08);
    --border-soft:     rgba(255, 255, 255, 0.05);
    --border-strong:   rgba(255, 255, 255, 0.20);
    --border-line:     rgba(255, 255, 255, 0.10);
    --line:            rgba(255, 255, 255, 0.08); /* legacy alias */
    --line-soft:       rgba(255, 255, 255, 0.05); /* legacy alias */

    /* —— 强调色（V3 SSOT） —— */
    --accent-blue:    #3b82f6;
    --accent-purple:  #8b5cf6;
    --accent-green:   #10b981;
    --accent-orange:  #f59e0b;
    --accent-red:     #ef4444;
    --accent-cyan:    #06b6d4;
    --accent-yellow:  #fde68a;                    /* playing-state marquee 黄字 (.block-l2.marquee-scroll) — 收口原内联 fallback */
    --accent-blue-strong: #2563eb;                /* legacy alias */

    /* —— 文字 —— */
    --text-primary:   #f8fafc;
    --text-secondary: #94a3b8;
    --text-tertiary:  #7a8699;                    /* WCAG-AA: ≈5.1:1 deep bg / ≥4.4:1 panel (was #64748b ~3.4–4.0:1) */
    --text-disabled:  rgba(255, 255, 255, 0.25);
    --text-zero:      rgba(255, 255, 255, 0.45);  /* WCAG-AA: ≈4.4:1 — recon 零活动金额 (区别非零, 比 disabled 0.25 可读) */
    --text-main: var(--text-primary);             /* legacy alias */
    --text-mid:  var(--text-secondary);           /* legacy alias */
    --text-dim:  var(--text-tertiary);            /* legacy alias */

    /* —— 毛玻璃强度 —— */
    --glass-blur:        blur(16px);
    --glass-blur-light:  blur(8px);
    --glass-blur-faint:  blur(4px);
    --glass-blur-modal:  blur(10px);

    /* —— 背景辉光 —— */
    --glow-cyan:    rgba(6, 182, 212, 0.20);
    --glow-purple:  rgba(139, 92, 246, 0.20);

    /* —— 状态色辉光 —— */
    --glow-blue:    rgba(59, 130, 246, 0.30);
    --glow-orange:  rgba(245, 158, 11, 0.30);
    --glow-red:     rgba(239, 68, 68, 0.30);
    --glow-green:   rgba(16, 185, 129, 0.30);

    /* —— 布局尺寸 —— */
    --gantt-sidebar-w: 220px;
    --gantt-row-h:     72px;   /* 批V4 LAY-03: 90→72 回收死空间; .theme-item 与 .gantt-row 同读此 token(均 !important) 保 1:1 对齐 */
    --gantt-axis-min:  1200px; /* 批V5 LAY-02: 甘特轴最小宽 token 化 (.gantt-area/.time-axis/.gantt-row 三处同读); 窄屏断点下调换"整天可见" */
    --header-h:        80px;

    /* —— 字体 (优化轮 批V1: --font-sans=Inter 正文; --font-mono=JetBrains Mono 真等宽数字, 统一 Boss SPA style.css:37) —— */
    --font-sans: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Helvetica Neue', sans-serif;
    --font-mono: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, Consolas, monospace;

    /* —— 过渡曲线 —— */
    --easing: cubic-bezier(0.4, 0, 0.2, 1);
}

/* ============= 1. Reset & Base ============= */
* { box-sizing: border-box; }

html, body {
    margin: 0;
    padding: 0;
    height: 100vh;
    overflow: hidden;
    background-color: var(--bg-main);
    color: var(--text-primary);
    font-family: var(--font-sans);
    font-size: 14px;
    font-weight: 400;
    -webkit-font-smoothing: antialiased;
    position: relative;
}

body {
    display: flex;
    flex-direction: column;
}

/* 双焦点径向渐变背景 + 25s 缓慢平移呼吸 */
body::before {
    content: '';
    position: fixed;
    top: -50%; left: -50%;
    width: 200%; height: 200%;
    background:
        radial-gradient(ellipse at 20% 50%, var(--glow-cyan)   0%, transparent 50%),
        radial-gradient(ellipse at 80% 20%, var(--glow-purple) 0%, transparent 50%);
    z-index: -1;
    animation: bgShift 25s ease-in-out infinite alternate;
    pointer-events: none;
}

@keyframes bgShift {
    0%   { transform: translate(0, 0); }
    100% { transform: translate(-5%, 5%); }
}

[v-cloak] { display: none !important; }

button {
    font-family: inherit;
    cursor: pointer;
    user-select: none;
}

button:disabled { cursor: not-allowed; }

::selection {
    background: rgba(59, 130, 246, 0.4);
    color: #fff;
}

/* V3.1 §M10 TODO — 项目级建议加 stylelint custom rule 锁住:
   - declaration-property-value-disallowed-list: { "letter-spacing": ["/^0$/"] }
   - color-no-hex (or restrict to var(--*) only)
   防止未来回归到 V2 黑蓝色板 / `letter-spacing: 0` 等已废弃写法。 */

/* ============= 2. 滚动条铁律（V3.1 · 强制可见 10px） =============
   ❌ 之前 6px + 半透明白几乎不可见，用户视觉反馈"完全找不到滚动条"
   ✅ 强制 10px 宽 + 深灰 track + 半白 thumb，hover 加深至 0.6
   全部 !important 覆盖任何下游样式或浏览器默认            */
::-webkit-scrollbar {
    width: 10px !important;
    height: 10px !important;
    display: block !important;
}
::-webkit-scrollbar-track {
    background: rgba(0, 0, 0, 0.2) !important;
}
::-webkit-scrollbar-thumb {
    background: rgba(255, 255, 255, 0.3) !important;
    border-radius: 10px !important;
}
::-webkit-scrollbar-thumb:hover {
    background: rgba(255, 255, 255, 0.6) !important;
}

/* ============= 3. Number Input 去 spinner ============= */
input[type=number]::-webkit-inner-spin-button,
input[type=number]::-webkit-outer-spin-button {
    -webkit-appearance: none;
    margin: 0;
}
input[type=number] { -moz-appearance: textfield; }
.no-spin           { -moz-appearance: textfield !important; }
.no-spin::-webkit-inner-spin-button,
.no-spin::-webkit-outer-spin-button {
    -webkit-appearance: none !important;
    margin: 0 !important;
}

/* checkbox / radio 统一蓝色 */
input[type=checkbox],
input[type=radio] {
    accent-color: var(--accent-blue);
    cursor: pointer;
}

/* ============= 4. Toast 全局提示 ============= */
#toast {
    position: fixed;
    top: 20px;
    left: 50%;
    transform: translateX(-50%);
    padding: 12px 28px;
    border-radius: 12px;
    font-size: 14px;
    font-weight: 700;
    color: #fff;
    z-index: 9000;
    background: rgba(16, 185, 129, 0.9);
    backdrop-filter: var(--glass-blur);
    border: 1px solid rgba(16, 185, 129, 0.4);
    box-shadow: 0 10px 40px rgba(16, 185, 129, 0.4);
    transition: opacity 0.3s;
    animation: toast-in 0.25s ease-out;
}

@keyframes toast-in {
    from { opacity: 0; transform: translate(-50%, -10px); }
    to   { opacity: 1; transform: translate(-50%, 0); }
}

/* ============= 5. 网络锁屏 (红黑全屏阻断) ============= */
.net-lock-overlay {
    position: fixed;
    inset: 0;
    z-index: 9999;
    background:
        radial-gradient(circle at center, rgba(239, 68, 68, 0.25) 0%, #000 70%),
        repeating-linear-gradient(45deg,
            rgba(239, 68, 68, 0.04) 0,
            rgba(239, 68, 68, 0.04) 12px,
            transparent             12px,
            transparent             24px);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    color: var(--accent-red);
    font-family: var(--font-mono);
    user-select: none;
    backdrop-filter: blur(2px);
}

/* ============= 6. Header 顶栏 ============= */
.header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 4px 24px;
    /* W2-F1 (前线反馈#1 + Owner 0611 裁定): 换行权上收到整块级 —— 标题块/动作条/
       统计条 三块可整体上下排, 但每块内部恒横排(见 .header-center nowrap)。
       wrap 仅在溢出时生效, 三块同行放得下时与旧布局一致。 */
    flex-wrap: wrap;
    row-gap: 8px;
    min-height: var(--header-h);   /* 批V4 LAY-04: height→min-height+auto, header 拥挤时自适应增高不裁「新增场次」 */
    height: auto;
    background: rgba(10, 10, 15, 0.65);
    backdrop-filter: var(--glass-blur);
    border-bottom: 1px solid var(--border-color);
    z-index: 100;
    flex-shrink: 0;
}

.header-left {
    display: flex;
    align-items: center;
    gap: 14px;
    min-width: 220px;
    flex-wrap: wrap;
}

.header-left h1 {
    margin: 0;
    font-size: 22px;
    font-weight: 900;
    background: linear-gradient(135deg, #fff 0%, #a5b4fc 100%);
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    letter-spacing: -0.5px;
    display: inline-block;
    vertical-align: middle;
}

.brand-logo {
    height: 28px;
    width: auto;
    margin-right: 4px;
    vertical-align: middle;
    filter: drop-shadow(0 0 8px rgba(139, 92, 246, 0.5));
}

.sys-time {
    font-size: 18px;
    color: var(--accent-cyan);
    font-family: var(--font-mono);
    font-weight: 700;
    text-shadow: 0 0 10px rgba(6, 182, 212, 0.4);
    letter-spacing: 1px;
}

.header-center {
    /* W2-F1: basis=auto(max-content)+shrink=0 → 放不下整条先掉行(上下排优先),
       绝不原地收缩; max-width 兜底 clamp 到行宽, 整条比视口还宽时才条内横滚。 */
    flex: 1 0 auto;
    max-width: 100%;
    display: flex;
    justify-content: center;
    justify-content: safe center; /* 溢出态退化为 start, 左缘可达(配 overflow-x) */
    gap: 20px;
    align-items: center;
    /* W2-F1 (Owner 0611 裁定, 取代批V4 LAY-04 的内部 wrap): 「新增场次/开店/资金
       核对」动作条本身一定要横向 —— 内部绝不换行; 空间不足时整条掉到标题块下一行
       (.header flex-wrap 承接), 仍不够则条内横滚, 永不竖叠。 */
    flex-wrap: nowrap;
    overflow-x: auto;
    scrollbar-width: thin;
    padding: 2px 0;      /* 吸收 vault-card hover translateY, 防 overflow 竖向滚动条抖动 */
}

.header-right {
    display: flex;
    align-items: center;
    gap: 16px;
}

/* ============= 7. 胶囊主操作按钮（新增场次 / 前台交接 大椭圆） ============= */
.btn-add-top,
.btn-finance {
    color: #fff;
    border: none;
    padding: 12px 36px;
    border-radius: 100px;
    font-size: 14px;
    font-weight: 700;
    white-space: nowrap;  /* W2-F1: 动作条 nowrap 后按钮会收缩, 标签恒单行不内折 */
    cursor: pointer;
    transition: all 0.3s var(--easing);
    font-family: inherit;
    position: relative;
    overflow: hidden;
    letter-spacing: 0.3px;
}

.btn-add-top {
    background: linear-gradient(135deg, var(--accent-blue) 0%, #1e3a8a 100%);
    box-shadow: 0 4px 15px var(--glow-blue);
}

.btn-finance {
    background: linear-gradient(135deg, var(--accent-purple) 0%, #4c1d95 100%);
    box-shadow: 0 4px 15px rgba(139, 92, 246, 0.3);
}

.btn-add-top:not(:disabled):hover,
.btn-finance:not(:disabled):hover {
    transform: translateY(-2px);
    filter: brightness(1.15);
}

.btn-add-top:not(:disabled):hover { box-shadow: 0 8px 25px rgba(59, 130, 246, 0.5); }
.btn-finance:not(:disabled):hover { box-shadow: 0 8px 25px rgba(139, 92, 246, 0.5); }

.btn-add-top:disabled,
.btn-finance:disabled {
    opacity: 0.4;
    cursor: not-allowed;
    transform: none;
    box-shadow: none;
    filter: none;
}

/* ============= 8. 多前台节点 (vault-card V3 audit) ============= */
.vault-card {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 6px;
    background: rgba(255, 255, 255, 0.025);
    border: 1px solid var(--border-color);
    backdrop-filter: var(--glass-blur-faint);
    padding: 8px 12px;
    border-radius: 12px;
    min-width: 140px;
    transition: all 0.25s var(--easing);
}

.vault-card:hover {
    background: rgba(255, 255, 255, 0.05);
    transform: translateY(-1px);
    border-color: rgba(6, 182, 212, 0.35);
    box-shadow: 0 0 22px rgba(6, 182, 212, 0.25);
}

.vault-card-label {
    font-size: 11px;
    color: var(--text-secondary);
    font-weight: 800;
    letter-spacing: 0.6px;
    text-transform: uppercase;
    text-align: center;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.vault-card .btn-base {
    width: 100%;
    padding: 6px 12px;
    border-radius: 6px;
    font-size: 12px;
    font-weight: 700;
    text-align: center;
}

/* V3.1 §D3 — "营业中"徽章按钮（替代行内 style，统一调皮肤） */
.vault-open-badge {
    background: rgba(16, 185, 129, 0.10) !important;
    color: var(--accent-green) !important;
    border: 1px solid var(--accent-green) !important;
    cursor: default !important;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    box-shadow: 0 0 12px rgba(16, 185, 129, 0.18);
}

/* "营业中 · X家门店" 绿色徽章 */
.vault-multi-badge {
    background: rgba(16, 185, 129, 0.18);
    color: var(--accent-green);
    border: 1px solid rgba(16, 185, 129, 0.5);
    border-radius: 100px;
    padding: 2px 8px;
    font-size: 10px;
    font-weight: 800;
    letter-spacing: 0.5px;
    margin-left: 6px;
    text-shadow: 0 0 6px var(--glow-green);
    vertical-align: middle;
    display: inline-block;
}

/* "⚠ Boss改底" 橙色角标 */
.vault-force-flag {
    position: absolute;
    top: -8px;
    right: -8px;
    background: var(--accent-orange);
    color: #1a1a1a;
    border: 1px solid rgba(255, 255, 255, 0.5);
    border-radius: 8px;
    padding: 2px 8px;
    font-size: 11px;
    font-weight: 900;
    letter-spacing: 0.5px;
    box-shadow: 0 0 14px var(--glow-orange);
    z-index: 5;
    cursor: help;
    animation: force-flag-blink 2.4s ease-in-out infinite;
    white-space: nowrap;
}

@keyframes force-flag-blink {
    0%, 100% { opacity: 1; transform: scale(1); }
    50%      { opacity: 0.65; transform: scale(1.06); }
}

/* 多门店 hover tooltip */
/* V17.2 — `.vault-tooltip` removed. The hover overlay on the 执行前台
   cards was getting clipped by the header's backdrop-filter stacking
   context and added no information beyond what's already on the card
   label / available via the 资金核对 modal. */

/* ============= 9. Stat 战报按钮 ============= */
.stat-btn {
    background: rgba(255, 255, 255, 0.02);
    border: 1px solid var(--border-color);
    color: var(--text-secondary);
    padding: 8px 16px;
    border-radius: 10px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    min-width: 80px;
    backdrop-filter: var(--glass-blur-faint);
    transition: all 0.25s ease;
    box-shadow: 0 0 8px rgba(6, 182, 212, 0.15);
}

.stat-btn:hover {
    background: rgba(6, 182, 212, 0.08);
    border-color: rgba(6, 182, 212, 0.4);
    box-shadow: 0 0 18px rgba(6, 182, 212, 0.35);
    transform: translateY(-1px);
}

.stat-title {
    font-size: 10px;
    color: var(--text-secondary);
    text-transform: uppercase;
    margin-bottom: 3px;
    font-weight: 600;
    letter-spacing: 0.5px;
}

.stat-val {
    font-size: 16px;
    font-weight: 800;
    color: #fff;
    font-family: var(--font-mono);
}

/* V17.2 — `.stats-popover` restored (was removed in V14.2 due to a
   z-index trap that's now solved structurally: the element is Teleported
   to <body> so it lives outside the header's backdrop-filter stacking
   context entirely).

   Anchored absolutely from inline `left` / `top` set by JS
   (showStatsPopover) — the helper reads the trigger button's
   getBoundingClientRect and clamps right-edge to viewport. z-index 5000
   sits above the Gantt (which tops out around 1000) and below the modal
   overlays (10000+). `pointer-events: auto` is essential because the
   user moves the cursor from button → popover; without it the popover's
   own @mouseenter wouldn't fire and the 200ms hide timer would dismiss
   the panel before the user could interact with it. */
.stats-popover {
    position: fixed;
    z-index: 5000;
    background: var(--bg-panel-modal);
    border: 1px solid var(--border-color);
    border-radius: 12px;
    padding: 12px 14px;
    min-width: 280px;
    max-width: 460px;
    color: var(--text-primary);
    box-shadow: 0 14px 50px rgba(0, 0, 0, 0.7);
    backdrop-filter: var(--glass-blur);
    pointer-events: auto;
    animation: fadeIn 0.18s var(--easing);
}

.stats-popover table {
    width: 100%;
}

@keyframes fadeIn {
    from { opacity: 0; transform: translateY(-5px); }
    to   { opacity: 1; transform: translateY(0); }
}

/* ============= 10. 主区 + 甘特图布局 (V3.1.2 冻结窗格 · 单一滚动容器) =============
   架构铁律（修复 2026-05-11 横向挤压 + 纵向错位）：
   ① .main-content 是唯一滚动容器：纵向 + 横向均由它接管
   ② .gantt-header-row 与 .gantt-body-row 内部不再独立滚动
   ③ .time-axis 与 .gantt-area 上加 min-width: 1200px 阻止挤压
   ④ .sidebar-header 与 .sidebar 用 position: sticky; left: 0 冻结左列
   ⑤ .theme-item 与 .gantt-row 用 !important 锁死 90px 高度 + flex-shrink: 0
      → 左右两列行高必然 1:1 对齐
*/
.main-content {
    display: flex;
    flex-direction: column;
    flex: 1;
    /* 显式高度兜底：即便 flex:1 失败也能有可滚动的容器 */
    height: calc(100vh - var(--header-h));
    max-height: calc(100vh - var(--header-h));
    overflow-y: auto;
    overflow-x: auto;
    position: relative;
}

.gantt-header-row {
    display: flex;
    position: sticky;
    top: 0;
    /* V14 — restored to the design-bible value (80). V13 had bumped this
       to 8000 to fix the store-dropdown overlap, but that hid the stat
       popovers (z:200) AND modal dropdowns (z:1100) behind the timeline.
       The store dropdown now uses Vue Teleport (front.js / index.html)
       to escape this stacking context, so the timeline can stay where it
       belongs in the z-index ladder. */
    z-index: 80;
    background: var(--bg-panel-deep);
    backdrop-filter: var(--glass-blur);
    border-bottom: 1px solid var(--border-line);
    height: 50px;
    flex-shrink: 0;
    /* 让 header row 跟随 body row 同宽，触发 main-content 横向滚动 */
    min-width: max-content;
    align-items: stretch;
    overflow: visible;
}

.sidebar-header {
    width: var(--gantt-sidebar-w);
    flex: 0 0 var(--gantt-sidebar-w);
    border-right: 1px solid var(--border-line);
    display: flex;
    align-items: center;
    padding: 0 16px;
    height: 50px;
    overflow: visible;
    /* 冻结左列：水平滚动时表头与侧栏顶部保持在视口左侧 */
    position: sticky;
    left: 0;
    /* V14 — restored to the bible value (50). The dropdown that lived
       here now teleports to <body>, so it no longer needs the parent to
       be raised above the timeline. */
    z-index: 50;
    background: var(--bg-panel-deep);
    backdrop-filter: var(--glass-blur);
}

.gantt-body-row {
    display: flex;
    flex-direction: row;
    flex: 1 1 auto;
    position: relative;
    align-items: flex-start;            /* 像素对齐铁律：杜绝条形码悬空 */
    min-width: max-content;
}

.sidebar {
    width: var(--gantt-sidebar-w);
    flex: 0 0 var(--gantt-sidebar-w);
    background: rgba(10, 10, 15, 0.85);
    border-right: 1px solid var(--border-soft);
    backdrop-filter: var(--glass-blur-light);
    /* V3.1.2: 取消独立 overflow-y → 由 .main-content 接管纵向滚动，
       与右侧 gantt-area 行高 1:1 同步滚动 */
    overflow: visible;
    /* 冻结左列：水平滚动时侧栏保持在视口左侧 */
    position: sticky;
    left: 0;
    z-index: 60;
}

.gantt-area {
    /* V3.1.2: 给时间轴定下最小宽度，窗口太窄时由外层 .main-content 提供横滚 */
    /* 批V5 LAY-02: 1200px → var(--gantt-axis-min), 窄屏断点下调以换整天可见 */
    flex: 1 1 auto;
    min-width: var(--gantt-axis-min);
    position: relative;
    display: flex;
    flex-direction: column;
    /* 不再独立 overflow → 滚动归 .main-content 一处统一管 */
    overflow: visible;
}

/* ============= 11. 主题侧栏卡片（高对比 · V3.1.2 行高死锁） ============= */
.theme-item {
    /* V3.1.2: 行高用 !important + flex-shrink:0 + box-sizing 严锁 90px。
       目标：与 .gantt-row 1:1 对齐，禁止文字换行/flex 压缩造成纵向错位 */
    height: var(--gantt-row-h) !important;
    min-height: var(--gantt-row-h) !important;
    max-height: var(--gantt-row-h) !important;
    box-sizing: border-box !important;
    flex-shrink: 0 !important;
    padding: 0 20px;
    border-bottom: 1px solid var(--border-soft);
    display: flex;
    flex-direction: column;
    justify-content: center;
    border-left: 4px solid transparent;
    transition: all 0.2s var(--easing);
    cursor: pointer;
    overflow: hidden;
}

.theme-item:hover {
    /* V3.1 §D8 — §1.5 hover 三选二: 颜色 + translateY + brightness */
    background: rgba(255, 255, 255, 0.04);
    border-left-color: var(--accent-blue);
    padding-left: 24px;            /* hover 时整体右移 4px，营造抽屉感 */
    transform: translateY(-1px);   /* §1.5 hover 律动 */
    filter: brightness(1.12);
    box-shadow: inset 4px 0 18px rgba(59, 130, 246, 0.15);
}

.theme-name {
    font-weight: 800;
    font-size: 15px;
    margin-bottom: 5px;
    color: var(--text-primary);
    letter-spacing: -0.2px;
    /* 批V4 LAY-06: 单行省略 → 2 行钳制, 长主题名可读(配合 :title 悬停看全名)。
       2 行 @15px×1.15 ≈ 34.5px, 含 margin-bottom 5px 远小于 72/64px 行高, 不溢出。 */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    line-height: 1.15;
}

.theme-store {
    font-size: 11px;
    color: var(--text-secondary);
    font-weight: 500;
}

.theme-meta {
    display: flex;
    gap: 10px;
    margin-top: 4px;
    font-size: 11px;
    font-family: var(--font-mono);
    color: var(--text-secondary);
    align-items: center;
}

.theme-price {
    color: var(--accent-green);
    font-weight: 700;
    text-shadow: 0 0 6px rgba(16, 185, 129, 0.3);
}

.theme-duration {
    color: var(--accent-cyan);
    font-weight: 600;
}

/* ============= 12. 时间轴 ============= */
.time-axis {
    /* V3.1.2: 横向不挤压 — 与 .gantt-area 同步 min-width，确保时刻刻度宽松可读 */
    /* 批V5 LAY-02: 1200px → var(--gantt-axis-min), 与 .gantt-area 同步降档 */
    flex: 1 1 auto;
    min-width: var(--gantt-axis-min);
    height: 50px;
    position: relative;
}

.time-tick {
    position: absolute;
    bottom: 0;
    height: 35%;
    border-left: 1px solid rgba(255, 255, 255, 0.15);
    padding-left: 8px;
    padding-bottom: 6px;
    font-size: 12px;
    color: var(--text-secondary);
    font-family: var(--font-mono);
    font-weight: 600;
    display: flex;
    align-items: flex-end;
    gap: 4px;
    white-space: nowrap;
}

.next-day-badge {
    display: inline-block;
    background: rgba(245, 158, 11, 0.15);
    color: var(--accent-orange);
    border: 1px solid rgba(245, 158, 11, 0.4);
    border-radius: 3px;
    padding: 1px 5px;
    font-size: 10px;
    font-weight: 800;
    letter-spacing: 0;
    line-height: 1.2;
    vertical-align: middle;
    text-shadow: 0 0 6px rgba(245, 158, 11, 0.4);
}

/* ============= 13. 当前时间红线（三角头） ============= */
.now-indicator {
    position: absolute;
    top: 0;
    bottom: 0;
    width: 2px;
    background: var(--accent-red);
    z-index: 45;
    pointer-events: none;
    box-shadow: 0 0 15px rgba(239, 68, 68, 0.8);
    transform: translateX(-50%);
    transition: left 1s linear;
}

.now-indicator-head {
    position: absolute;
    bottom: 0;
    width: 0;
    height: 0;
    border-left: 8px solid transparent;
    border-right: 8px solid transparent;
    border-bottom: 8px solid var(--accent-red);
    z-index: 85;
    transform: translateX(-50%);
    transition: left 1s linear;
    filter: drop-shadow(0 -2px 5px rgba(239, 68, 68, 0.8));
}

/* ============= 14. 甘特行 + 场次色块（V3 §1.10 状态色 SSOT） ============= */
.gantt-row {
    /* V3.1.2: 行高死锁 — 与 .theme-item 1:1 像素级对齐 */
    height: var(--gantt-row-h) !important;
    min-height: var(--gantt-row-h) !important;
    max-height: var(--gantt-row-h) !important;
    box-sizing: border-box !important;
    flex-shrink: 0 !important;
    border-bottom: 1px solid var(--border-soft);
    position: relative;
    background-image: linear-gradient(90deg, rgba(255, 255, 255, 0.02) 1px, transparent 1px);
    background-size: calc(100% / 15) 100%;
    transition: background 0.3s;
    /* min-width 让一行也撑住时间轴最小宽，配合外层 .main-content 横滚 */
    /* 批V5 LAY-02: 1200px → var(--gantt-axis-min), 三处轴宽统一受 token 控 */
    min-width: var(--gantt-axis-min);
}

.gantt-row:hover {
    background-color: rgba(255, 255, 255, 0.01);
}

.block {
    position: absolute;
    height: 56px;
    top: 8px;   /* 批V4 LAY-03: 行高 90→72 后重新居中 56px 块 (8+56+8=72) */
    /* 批V5 LAY-01: 极短场次(如 30 分钟 ≈ 3% 宽 ≈ 37px) 塌成不可读色条 →
       给绝对定位块一个像素下限, 让标题前 ~4 字幸存。inline `left:%` 锚点(index.html:295
       blockLeft 设)不受影响, 块仍锚定开场时刻; min-width 仅给 inline `width:%`(blockWidth)
       兜一个渲染下限。纯 CSS, 不碰 front.js 的 blockWidth 百分比逻辑。 */
    min-width: 64px;
    border-radius: 8px;
    /* V14 — tighter vertical padding so 3 lines (plus optional revenue
       4th line for ended) fit cleanly in the locked 56px height. Layout
       distributes lines top-to-bottom; the empty Line 2 stays a visual
       gap on waiting/ended blocks so Line 1 sits at the top and Line 3
       sits at the bottom. */
    padding: 5px 12px;
    font-size: 12px;
    cursor: pointer;
    color: #fff;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    transition: all 0.25s var(--easing);
    border: 1px solid rgba(255, 255, 255, 0.10);
    border-left: 4px solid rgba(255, 255, 255, 0.5);
    overflow: hidden;
    box-shadow: 0 4px 15px rgba(0, 0, 0, 0.4);
    backdrop-filter: var(--glass-blur-faint);
}

.block:hover {
    transform: translateY(-3px) scale(1.02);
    filter: brightness(1.2);
    z-index: 10;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.6);
}

.block-title {
    font-weight: 800;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    margin-bottom: 3px;
    font-size: 13px;
    letter-spacing: -0.2px;
    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
}

.block-info {
    font-size: 11px;
    opacity: 0.9;
    white-space: nowrap;
    overflow: hidden;
    position: relative;
    font-weight: 500;
}

/* V14 — per-line styles for the 3/4-line block content.
 *
 *   .block-l1 = session type + pax        (top, bold)
 *   .block-l2 = current_stage from STAGE_UPDATE   (middle, marquee when populated)
 *   .block-l3 = HH:mm - HH:mm time range   (bottom, mono)
 *   .block-l4 = ¥ revenue (ended only)     (very bottom, bold mono)
 *
 * Line 2 keeps its DOM slot even when empty so Line 1 sits at the top
 * and Line 3 sits at the bottom of the block — the empty middle is the
 * visual breathing room the spec calls for on waiting/ended blocks.
 */
.block-line {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    line-height: 1.1;
}
.block-l1 {
    font-weight: 800;
    font-size: 13px;   /* 批V4 TYPO-04: 12→13 场次标题层级 */
    letter-spacing: -0.2px;
    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
}
.block-l2 {
    font-size: 10px;
    opacity: 0.85;
    font-weight: 500;
    min-height: 11px;                /* preserves the gap when empty */
    position: relative;
}
.block-l3 {
    font-size: 10px;
    font-family: var(--font-mono, 'JetBrains Mono', 'SF Mono', monospace);
    opacity: 0.88;
    letter-spacing: 0.2px;
}
.block-l4 {
    font-size: 12px;   /* 批V4 TYPO-04: 11→12 ¥营收核心数字, 保持 mono+tabular */
    font-weight: 800;
    font-family: var(--font-mono, 'JetBrains Mono', 'SF Mono', monospace);
    color: var(--accent-green, #10b981);
    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
}
/* Marquee for the playing-state Line 2 — reuses the legacy @keyframes
   `marquee` defined later in this file (kept for back-compat with the
   .block-info.marquee-scroll selector that older templates still hit). */
.block-l2.marquee-scroll {
    overflow: hidden;
    color: var(--accent-yellow, #fde68a);
    font-weight: 700;
}
.block-l2.marquee-scroll span {
    display: inline-block;
    animation: marquee 10s linear infinite;
    padding-left: 100%;
}

/* 三态着色 (V3 SSOT §1.10) */
.block.waiting {
    background: linear-gradient(135deg, rgba(59, 130, 246, 0.8), rgba(37, 99, 235, 0.9));
    border-left-color: #bfdbfe;
}

.block.playing {
    background: linear-gradient(135deg, rgba(245, 158, 11, 0.8), rgba(217, 119, 6, 0.9));
    border-left-color: #fde68a;
}

/* WCAG-AA: playing 块 l1/l3 旧为 white@.88 压橙渐变仅 ~2.9–3.3:1(未达 AA）。
   改纯不透明深字 #1a1206 压琥珀带 → 实测 4.85(暗端)~5.66(亮端）过 AA。
   仅 playing 态；waiting(蓝/白) / ended(紫) / cancelled(灰) 各保自有色。
   l3 opacity .88→1 让深字不被稀释回橙（稀释态会跌回 ~4.0）。
   :not(.overlap-warn) 护栏：撞场红底（.block.overlap-warn 红渐变）下不转深字，
   保留 .block 基类白字（白压红 ~7.5:1），避免深字压红底跌到 ~2.5:1 的回归。
   注：l2 走马灯(current_stage) 是既有黄字高亮(#fde68a)，非本批 AA 目标、未纳入。 */
.block.playing:not(.overlap-warn) { color: #1a1206; }
.block.playing .block-l2,
.block.playing .block-l3 { opacity: 1; }

/* V14.4 — ended is unmistakably DARK PURPLE (was a near-black grey that
   operators kept reading as "cancelled"). Strong purple gradient with a
   bright purple border-left so it visually contrasts both the orange
   `playing` state above and the grey `cancelled` state below. */
.block.ended {
    background: linear-gradient(135deg, rgba(76, 29, 149, 0.85), rgba(46, 16, 101, 0.92));
    border-left-color: var(--accent-purple);
    color: #c4b5fd;
}

.block.ended .block-title { color: #ddd6fe; }

/* 批V4 LAY-05: ended 态营收行(l4 ¥)不被挤裁。
   blockLines(front.js) 对 ended 永不写 l2(恒为 ''), 且模板恒渲染 <span> 子节点,
   故 :empty 选 div 不命中 → 直接对 ended 的 l2 折叠(其 min-height:11px 占用的留白
   让给真 3 行 type/time/¥)。仅命中 .block.ended, 不影响 waiting/playing/cancelled。 */
.block.ended .block-l2 { display: none; }

/* V14.4 — cancelled was previously rendered without a dedicated class
   rule, falling back to the .block base style and looking identical to
   waiting (just lightly dimmed). Per V14.4 spec: cancelled = neutral grey
   with line-through, distinct from any active state. */
.block.cancelled {
    background: linear-gradient(135deg, rgba(71, 85, 105, 0.55), rgba(51, 65, 85, 0.65));
    border-left-color: #64748b;
    color: #94a3b8;
    text-decoration: line-through;
    text-decoration-color: rgba(148, 163, 184, 0.6);
    opacity: 0.7;
}

.block.cancelled .block-title { color: #cbd5e1; }

/* V14.4 — defensive: the overlap-warn class can ONLY decorate
   waiting/playing blocks per the V14.4 strict-color rule (overlapCheck
   in front.js already short-circuits for ended/cancelled). These
   `:not()` selectors are belt-and-suspenders: even if a future caller
   passes overlap-warn onto a frozen-status block, the red pulse won't
   override the proper status background. */
.block.overlap-warn:not(.ended):not(.cancelled) {
    background: linear-gradient(135deg, rgba(239, 68, 68, 0.9), rgba(185, 28, 28, 0.9)) !important;
    border: 2px solid #fca5a5 !important;
    animation: danger-pulse 1.5s infinite;
}

/* §18.2 强制写法：danger-pulse 红色脉冲 */
@keyframes danger-pulse {
    0%   { box-shadow: 0 0 0 0   rgba(239, 68, 68, 0.7); }
    70%  { box-shadow: 0 0 0 10px rgba(239, 68, 68, 0); }
    100% { box-shadow: 0 0 0 0   rgba(239, 68, 68, 0); }
}

/* playing 态 current_stage 跑马灯 */
.block-info.marquee-scroll {
    overflow: hidden;
}

.block-info.marquee-scroll span {
    display: inline-block;
    padding-left: 100%;
    animation: marquee 14s linear infinite;
}

@keyframes marquee {
    from { transform: translateX(0); }
    to   { transform: translateX(-100%); }
}

/* ============= 15. Hover Tooltip ============= */
.hover-tooltip {
    position: fixed;
    transform: translate(-50%, -100%);
    background: rgba(15, 15, 25, 0.92);
    backdrop-filter: blur(12px);
    border: 1px solid rgba(6, 182, 212, 0.30);
    border-radius: 8px;
    padding: 8px 14px;
    z-index: 1500;
    pointer-events: none;
    box-shadow: 0 4px 20px rgba(0, 0, 0, 0.6), 0 0 15px rgba(6, 182, 212, 0.10);
    max-width: 240px;
    white-space: nowrap;
}

/* ============= 16. Modal Overlay + Content ============= */
.modal-overlay {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.8);
    backdrop-filter: var(--glass-blur-modal);
    z-index: 1000;
    justify-content: center;
    align-items: center;
    animation: overlay-in 0.2s ease-out;
    /* V14.2 — Global small-screen scroll support.
       On a 1366×768 panel, modals whose panel height exceeds ~85vh used
       to clip the action buttons. Letting the overlay scroll lets the
       operator at least reach the bottom even when the inner panel's
       own overflow:auto fails (e.g. finance-modal-content's internal
       step containers don't always size correctly on first paint). */
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
}

@keyframes overlay-in {
    from { opacity: 0; }
    to   { opacity: 1; }
}

.modal-content {
    background: var(--bg-panel-modal);
    width: 95%;
    max-width: 900px;
    /* V14.2 — was 90vh; dropped to 85vh so the bottom action buttons
       have a visible breathing margin on 1366×768 panels. The internal
       scroll still kicks in when the form exceeds the panel. */
    max-height: 88vh;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    border-radius: 20px;
    border: 1px solid rgba(255, 255, 255, 0.10);
    padding: 20px 24px;
    box-shadow: 0 40px 120px rgba(0, 0, 0, 0.8);
    position: relative;
    animation: modal-in 0.22s var(--easing);
    /* V14.2 — flex margin so the panel doesn't kiss the viewport edge
       once the overlay itself scrolls. Without this, the very tall
       finance modal could push past the overlay's scrollable region
       on the bottom. */
    margin: 16px auto;
}

@keyframes modal-in {
    from { opacity: 0; transform: translateY(12px) scale(0.98); }
    to   { opacity: 1; transform: translateY(0)    scale(1);    }
}

/* ============= 17. Popover (场次操作) ============= */
.popover {
    position: absolute;
    background: var(--bg-panel-modal);
    border: 1px solid rgba(255, 255, 255, 0.10);
    border-radius: 16px;
    padding: 14px;
    box-shadow: 0 20px 60px rgba(0, 0, 0, 0.8);
    z-index: 200;
    width: 260px;
    backdrop-filter: var(--glass-blur);
}

/* ============= 18. 通用按钮 ============= */
.btn-base {
    padding: 10px 20px;
    border-radius: 10px;
    font-weight: 700;
    cursor: pointer;
    border: none;
    transition: all 0.2s var(--easing);
    font-size: 13px;
    font-family: inherit;
}

.btn-submit {
    background: linear-gradient(135deg, var(--accent-blue), #4f46e5);
    color: #fff;
    box-shadow: 0 4px 15px rgba(59, 130, 246, 0.3);
}

.btn-submit:hover {
    filter: brightness(1.15);
    transform: translateY(-2px);
    box-shadow: 0 8px 25px rgba(59, 130, 246, 0.4);
}

.btn-cancel {
    background: rgba(255, 255, 255, 0.05);
    color: var(--text-secondary);
    border: 1px solid rgba(255, 255, 255, 0.10);
}

.btn-cancel:hover {
    background: rgba(255, 255, 255, 0.10);
    color: #fff;
}

.btn-base:disabled {
    opacity: 0.4;
    cursor: not-allowed;
    transform: none !important;
    filter: none !important;
    box-shadow: none !important;
}

/* "开店" 节点专用 — 亮青绿渐变（老版"bright cyan"feel） */
.btn-open-vault {
    background: linear-gradient(135deg, #00b4db, #0083b0) !important;
    color: #fff !important;
    border: none !important;
    box-shadow: 0 4px 15px rgba(0, 180, 219, 0.35);
}

.btn-open-vault:not(:disabled):hover {
    transform: translateY(-1px);
    filter: brightness(1.15);
    box-shadow: 0 8px 25px rgba(0, 180, 219, 0.55);
}

/* ============= 19. 表单 section ============= */
.new-form-section {
    border-radius: 12px;
    border: 1px solid var(--border-soft);
    padding: 12px 14px;
    margin-bottom: 12px;
    background: var(--bg-card-sub);
}

.new-form-title {
    font-size: 12px;
    font-weight: 800;
    color: var(--text-primary);
    border-left: 4px solid var(--accent-cyan);
    padding-left: 10px;
    margin-bottom: 10px;
    letter-spacing: 0.5px;
    text-transform: uppercase;
}

.grid-2col { display: grid; grid-template-columns: 1fr 1fr;        gap: 14px; }
.grid-3col { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; }

/* ============= 20. Input + Select (黑玻璃) ============= */
.input-dark {
    width: 100%;
    background: var(--bg-input);
    border: 1px solid rgba(255, 255, 255, 0.10);
    color: #fff;
    padding: 8px 12px;
    border-radius: 8px;
    font-size: 16px;
    outline: none;
    transition: all 0.2s;
    font-family: inherit;
    font-variant-numeric: tabular-nums;
}

.input-dark::placeholder {
    color: var(--text-tertiary);
    font-style: italic;
}

.input-dark:focus {
    border-color: var(--accent-cyan);
    box-shadow: 0 0 0 3px rgba(6, 182, 212, 0.15);
    background: var(--bg-input-focus);
}

.input-dark:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}

select.input-dark {
    appearance: none;
    -webkit-appearance: none;
    background-image:
        linear-gradient(45deg,  transparent 50%, var(--accent-cyan) 50%),
        linear-gradient(135deg, var(--accent-cyan) 50%, transparent 50%);
    background-position: calc(100% - 14px) 50%, calc(100% - 9px) 50%;
    background-size: 5px 5px, 5px 5px;
    background-repeat: no-repeat;
    padding-right: 26px;
}

select.input-dark option {
    background: #0d0d11;
    color: #fff;
}

/*
 * V13 — multiline contact_phone textarea. Shared sessions can carry 4–8
 * players' phone numbers; the single-line input let long strings overflow
 * past the form column. word-break + pre-wrap keep every number visible
 * without clipping; resize:vertical lets the operator expand if needed.
 */
textarea.input-dark.contact-phone-textarea {
    word-break: break-all;
    white-space: pre-wrap;
    resize: vertical;
    min-height: 48px;
    line-height: 1.5;
    font-family: var(--font-mono, monospace);
}

/*
 * V13 — past-hour / past-minute hint on the time-selector dropdowns.
 * Options stay selectable (operators can backdate a session when needed)
 * but the option text dims to 40% so the operator can tell at a glance
 * which slots are in the past.
 */
.input-dark option.past-hour {
    opacity: 0.4;
    color: var(--text-tertiary, #777);
}

/* ============= 21. 自定义下拉 (custom-dropdown) ============= */
/*
 * V14 — the menu (.dropdown-content) is teleported to <body> in the Vue
 * template, so the BUTTON wrapper here doesn't need a high z-index any
 * more. The teleported menu uses fixed positioning + the bible's z:999.
 */
.custom-dropdown {
    position: relative;
    display: block;
    width: 100%;
}

.dropdown-btn {
    background: rgba(255, 255, 255, 0.03);
    color: #fff;
    padding: 10px 16px;
    border: 1px solid var(--border-color);
    border-radius: 8px;
    cursor: pointer;
    font-size: 14px;
    font-weight: 600;
    width: 100%;
    text-align: left;
    display: flex;
    justify-content: space-between;
    align-items: center;
    transition: all 0.2s;
    backdrop-filter: var(--glass-blur-faint);
}

.dropdown-btn:hover {
    background: rgba(255, 255, 255, 0.08);
    border-color: rgba(255, 255, 255, 0.20);
}

.dropdown-content {
    /* V14 — Teleported to <body>; we position via inline style (top/left)
       computed from the button's bounding rect. Fixed positioning + the
       bible's z:999 puts the menu above the timeline (z:80) and below
       modals (z:1000+). */
    display: none;
    position: fixed;
    background: rgba(20, 20, 25, 0.95);
    min-width: 220px;
    box-shadow: 0 15px 40px rgba(0, 0, 0, 0.8);
    border: 1px solid var(--border-color);
    border-radius: 10px;
    z-index: 999;
    overflow: hidden;
    backdrop-filter: var(--glass-blur);
}

.dropdown-content.show {
    display: block;
    animation: fadeIn 0.2s ease-out;
}

.dropdown-content label {
    display: block;
    padding: 10px 14px;
    cursor: pointer;
    font-size: 13px;
    color: #cbd5e1;
    border-bottom: 1px solid var(--border-soft);
    transition: background 0.2s;
}

.dropdown-content label:last-child { border-bottom: none; }

.dropdown-content label:hover {
    background: rgba(59, 130, 246, 0.20);
    color: #fff;
}

/* ============= 22. 异常退款 (refund) ============= */
.refund-box {
    background: rgba(239, 68, 68, 0.05);
    border: 1px solid rgba(239, 68, 68, 0.20);
    border-radius: 12px;
    padding: 10px 12px;
    margin-top: 10px;
}

.refund-title {
    color: var(--accent-red);
    font-size: 12px;
    font-weight: 800;
    margin-bottom: 8px;
    display: flex;
    align-items: center;
    gap: 8px;
    letter-spacing: 0.3px;
}

.refund-box.refund-active {
    border-color: rgba(239, 68, 68, 0.70) !important;
    box-shadow: 0 0 20px rgba(239, 68, 68, 0.15);
    animation: refundPulse 2s ease-in-out infinite;
}

@keyframes refundPulse {
    0%, 100% { border-color: rgba(239, 68, 68, 0.70); box-shadow: 0 0 20px rgba(239, 68, 68, 0.15); }
    50%      { border-color: rgba(239, 68, 68, 1);    box-shadow: 0 0 35px rgba(239, 68, 68, 0.30); }
}

/* V3.1 §M6 — 已删除孤立的 .refund-list / .refund-item / .refund-info 三组 CSS。
   异常退款明细在前台交接弹窗的"今日进账"区由 .finRefundDetails 内联渲染（index.html 第 740 行附近），
   不再需要独立的 list 类。如未来需要历史退款列表，重新引入即可。 */

/* 占位以保持后续 .refund-info 等可能引用稳定 — 实际无样式生效 */
.refund-list-placeholder {
    /* intentionally empty — see §M6 above */
}

/* ============= 23. 汇总预览 ============= */
.summary-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 0;
    border: 1px solid var(--border-line);
    border-radius: 12px;
    overflow: hidden;
    background: var(--bg-card-sub);
}

.sum-cell {
    padding: 10px 14px;
    border-right: 1px solid var(--border-soft);
    border-bottom: 1px solid var(--border-soft);
    display: flex;
    justify-content: space-between;
    align-items: center;
    transition: background 0.2s;
}

.sum-cell:hover { background: rgba(255, 255, 255, 0.02); }
.sum-cell:nth-child(3n) { border-right: none; }

.sum-label {
    font-size: 12px;
    color: var(--text-secondary);
    font-weight: 500;
}

.sum-value {
    font-family: var(--font-mono);
    font-size: 15px;
    font-weight: 800;
    color: #fff;
}

.sum-value.blue   { color: var(--accent-cyan);   text-shadow: 0 0 8px rgba(6, 182, 212, 0.30); }
.sum-value.orange { color: var(--accent-orange); text-shadow: 0 0 8px rgba(245, 158, 11, 0.30); }

.sum-final {
    grid-column: span 3;
    padding: 14px 20px;
    background: linear-gradient(90deg, rgba(16, 185, 129, 0.10), rgba(16, 185, 129, 0.05));
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.sum-final .label {
    font-size: 15px;
    font-weight: 800;
    color: var(--accent-green);
    letter-spacing: 0.5px;
}

.sum-final .value {
    font-family: var(--font-mono);
    font-size: 26px;
    font-weight: 900;
    color: var(--accent-green);
    text-shadow: 0 0 15px rgba(16, 185, 129, 0.40);
    letter-spacing: 1px;
}

/* ============= 24. 前台交接弹窗 V2 (Finance Modal) ============= */
.finance-modal-content {
    /* V16 layout — bumped from 1080px to 1280px to accommodate the new
       two-column always-visible region (left: 资金核对各渠道已入账;
       right: 今日快讯 + 今日交接记录). Below the 1024px breakpoint
       (.settlement-twocol-grid) the columns stack vertically and the
       overall modal width is bounded by the existing 95% / 16px margin
       rules, so narrow viewports remain readable. */
    max-width: 1280px;
    padding: 0;
    background: rgba(10, 10, 15, 0.95);
    /* V14.2 — was overflow:hidden, which clipped the multi-step wizard
       (mode select → fund check → reconcile) on 1366×768 panels: the
       bottom 执行闭店/交接 buttons fell off-screen. Switched to
       overflow-y:auto so the operator can scroll the panel itself
       (the overlay also scrolls now as a belt-and-braces fallback). */
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    border: 1px solid rgba(255, 255, 255, 0.10);
    width: 95%;
    max-height: 90vh;
    border-radius: 20px;
    margin: 16px auto;
}

/* 算式流 - 备用金底数死锁巨号 */
.math-base-val {
    font-family: var(--font-mono);
    font-size: 30px;
    font-weight: 900;
    letter-spacing: 1px;
    user-select: none;
    pointer-events: none;        /* 死锁不可点击 / 选中 */
    text-shadow: 0 0 14px currentColor;
    opacity: 0.96;
    line-height: 1.2;
    padding: 4px 0;
}

/* V3 audit · 闭店结余巨号（绿色霓虹） */
.math-final-val {
    font-family: var(--font-mono);
    font-size: 30px;
    font-weight: 900;
    color: var(--accent-green);
    text-shadow: 0 0 15px rgba(16, 185, 129, 0.4);
    letter-spacing: 1px;
    margin-top: 10px;
    line-height: 1.2;
}

/* 算式连接符 ➕ ➖ 🟰 */
.math-op-divider {
    text-align: center;
    font-size: 32px;
    font-weight: 900;
    margin: 6px 0;
    letter-spacing: 4px;
    text-shadow: 0 0 14px currentColor;
    user-select: none;
    animation: op-breathe 2.4s ease-in-out infinite;
}

@keyframes op-breathe {
    0%, 100% { opacity: 0.80; transform: scale(1);    }
    50%      { opacity: 1;    transform: scale(1.06); }
}

/* 钱弹窗内输入 focus 改为青色光圈（区别于场次编辑的蓝色） */
.finance-modal-content .input-dark:focus,
.modal-content .refund-box .input-dark:focus {
    border-color: var(--accent-cyan);
    box-shadow: 0 0 0 3px rgba(6, 182, 212, 0.25);
}

/* ============= 25. 警报弹窗动画 (异常终止 / 游戏结束 / 强力校准) ============= */
@keyframes danger-pulse-modal {
    0%, 100% { box-shadow: 0 0 30px rgba(239, 68, 68, 0.50), 0 0 0 1px rgba(239, 68, 68, 0.30); }
    50%      { box-shadow: 0 0 60px rgba(239, 68, 68, 0.85), 0 0 0 2px rgba(239, 68, 68, 0.65); }
}

@keyframes success-pulse {
    0%, 100% { box-shadow: 0 0 30px rgba(16, 185, 129, 0.35); }
    50%      { box-shadow: 0 0 60px rgba(16, 185, 129, 0.65); }
}

/* ============= 26. V3 audit · 备用金底数预览面板（开店弹窗 §7.4） ============= */
.base-preview-panel {
    background: rgba(20, 20, 25, 0.65);
    border: 1px solid var(--border-color);
    border-radius: 14px;
    padding: 12px 14px;
    margin: 10px 0;
    backdrop-filter: var(--glass-blur-light);
}

.base-preview-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 10px;
    font-size: 12px;
    font-weight: 800;
    color: var(--text-secondary);
    letter-spacing: 0.5px;
}

.base-preview-source {
    font-size: 10px;
    font-weight: 800;
    padding: 4px 12px;
    border-radius: 100px;
    letter-spacing: 0.5px;
    text-transform: uppercase;
    white-space: nowrap;
}

/* V12.2 §SSOT — source 标签三档 */
.base-preview-source.synced {
    background: rgba(59, 130, 246, 0.18);
    color: var(--accent-blue);
    border: 1px solid rgba(59, 130, 246, 0.4);
    text-shadow: 0 0 6px var(--glow-blue);
}

.base-preview-source.boss-set {
    background: rgba(245, 158, 11, 0.18);
    color: var(--accent-orange);
    border: 1px solid rgba(245, 158, 11, 0.5);
    box-shadow: 0 0 12px var(--glow-orange);
}

.base-preview-source.initial {
    background: rgba(148, 163, 184, 0.15);
    color: var(--text-secondary);
    border: 1px solid rgba(148, 163, 184, 0.4);
}

.base-preview-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 10px;
}

.base-preview-cell {
    background: rgba(0, 0, 0, 0.25);
    border: 1px solid var(--border-soft);
    border-radius: 10px;
    padding: 10px 12px;
}

.base-preview-cell-label {
    font-size: 11px; /* 批V6 TYPO-05: 10→11 px 弹窗内小字提可读 */
    color: var(--text-tertiary);
    font-weight: 700;
    margin-bottom: 4px;
    letter-spacing: 0.5px;
    text-transform: uppercase;
}

.base-preview-cell-value {
    font-family: var(--font-mono);
    font-size: 18px;
    font-weight: 900;
    color: var(--accent-green);
    text-shadow: 0 0 10px rgba(16, 185, 129, 0.35);
    letter-spacing: 0.5px;
}

.base-preview-help {
    font-size: 11px;
    color: var(--text-tertiary);
    margin-top: 8px;
    line-height: 1.5;
    border-top: 1px dashed var(--border-soft);
    padding-top: 8px;
}

/* ============= 27. V3 audit · 骨架屏 / 加载态 ============= */
.skeleton {
    background: linear-gradient(90deg,
        rgba(255, 255, 255, 0.04) 25%,
        rgba(255, 255, 255, 0.12) 50%,
        rgba(255, 255, 255, 0.04) 75%);
    background-size: 200% 100%;
    animation: skeleton-shimmer 1.4s ease-in-out infinite;
    border-radius: 6px;
    color: transparent !important;
    text-shadow: none !important;
    user-select: none;
}

@keyframes skeleton-shimmer {
    0%   { background-position: 100% 50%; }
    100% { background-position: -100% 50%; }
}

.skeleton-line {
    display: inline-block;
    height: 1em;
    min-width: 60px;
    border-radius: 4px;
    vertical-align: middle;
}

.skeleton-block {
    display: block;
    height: 32px;
    width: 100%;
    border-radius: 6px;
}

.loading-chip {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    color: var(--text-secondary);
    font-size: 12px;
    font-style: italic;
    font-weight: 600;
    letter-spacing: 0.5px;
}

.loading-chip::before {
    content: '';
    width: 10px;
    height: 10px;
    border: 2px solid var(--accent-cyan);
    border-top-color: transparent;
    border-radius: 50%;
    animation: spin 0.8s linear infinite;
}

@keyframes spin {
    to { transform: rotate(360deg); }
}

/* ============= 28. 杂项 / 工具类 ============= */
.ellipsis {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* 最末主题 / 最末甘特行底部对齐铁律 */
.theme-item:last-child,
.gantt-row:last-child {
    border-bottom: 1px solid var(--border-soft);
}

/* 普通按钮（btn-base 内联状态色按钮）通用 hover 反馈 */
.btn-base:not(:disabled):hover {
    filter: brightness(1.12);
    transform: translateY(-1px);
}

/* ============= 29. 响应式守门 ============= */
@media (max-width: 1280px) {
    .header           { padding: 0 16px; }
    .header-center    { gap: 12px; }
    .btn-add-top,
    .btn-finance      { padding: 10px 24px; font-size: 13px; }
    .stat-btn         { min-width: 64px; padding: 6px 12px; }
    .stat-val         { font-size: 14px; }
    /* V17.2 — narrow-viewport sizing for the stats popover. The JS clamp
       already prevents right-edge overflow; this just gives the panel a
       tighter min-width so a 1024px viewport doesn't stretch it across
       half the screen. */
    .stats-popover    { min-width: 220px; padding: 10px 12px; font-size: 12px; }
    .vault-card       { min-width: 120px; padding: 6px 10px; }
    :root {
        --gantt-sidebar-w: 200px;
        --gantt-row-h:     64px;   /* 批V4 LAY-03: 窄屏档 80→64, 同步回收 */
        --gantt-axis-min:  980px;  /* 批V5 LAY-02: 1280px 档轴宽 1200→980 (980/16hr≈61px/hr 仍可读), 1366 笔记本无需横滚看下午 */
        --header-h:        72px;
    }
    /* 批V4 收尾(窄屏 64px 档): 块居中(4+56+4=64) + 长主题名回退单行(配 :title 看全名)避免 2 行在 64px 行溢出 */
    .block      { top: 4px; }
    .theme-name { -webkit-line-clamp: 1; }
    /* 批V5 LAY-02 收尾: 窄屏轴降档 (≤1280→980px, ≤1024→760px) 后, 时间轴每小时像素收窄到
       61.25/47.5px。次日刻度的 "次日" 徽标 + HH:00 合计 footprint (~80px) 会撑爆刻度格 → 相邻标签碰撞。
       桌面 (>1280, 75px/hr) 不受影响, 保留徽标; ≤1280 窄屏隐藏徽标 —— 跨零点小时仍标号 00:00..06:00,
       在单调左→右时间轴上位置即"次日", 信息不丢, 仅去冗余撞车。纯 CSS, 可逆。 */
    .next-day-badge { display: none; }
}

/* W2-F1 (前线反馈#1): ≤1100 字号/留白下限档 — 动作条 nowrap 后再收一档,
   让横条在窄屏/高 DPI 下尽量整条放下, 放不下由 .header-center 横滚兜底(永不竖叠)。 */
@media (max-width: 1100px) {
    .header           { padding: 4px 12px; }
    .header-center    { gap: 8px; }
    .btn-add-top,
    .btn-finance      { padding: 8px 14px; font-size: 12px; white-space: nowrap; }
    .stat-btn         { min-width: 56px; padding: 5px 10px; }
    .stat-val         { font-size: 13px; }
    .vault-card       { min-width: 104px; padding: 5px 8px; }
    .header-left      { min-width: 0; }
    .header-left h1   { font-size: 18px; }
    .sys-time         { font-size: 14px; }
    .header-right     { gap: 10px; }
}

@media (max-width: 1024px) {
    .header-left h1 { font-size: 18px; }
    .sys-time       { font-size: 14px; }
    /* 批V5 LAY-02: 平板/小笔记本档 — 轴宽再降 + 侧栏收窄, 用每小时像素换整天可见。
       760/16hr≈47.5px/hr 仍 ≥45px 可读; sidebar 220→160 省 60px 给时间轴。
       (并入已存在的布局 1024 块, 不新建 media; 区别于 §31 line~1938 那个仅控
       .settlement-twocol-grid 的弹窗内 1024 块。) */
    :root {
        --gantt-axis-min:  760px;
        --gantt-sidebar-w: 160px;
    }
    /* 批V5 LAY-01 收尾: 平板档轴宽 760px (0.79px/min) 下, 全局 .block min-width:64px 会把正常
       60–80min 同主题背靠背场次 clamp 到 64px → 互相视觉重叠 (clamp 阈值在此档升到 ~81min)。
       此档降到 48px (≈ 60min 场次自然宽 47.5px; clamp 阈值降到 ~61min): 正常 ≥61min 背靠背场次不再
       重叠, 短场次仍得 48px 渲染下限 (优于此档 2% floor 的 15.2px), 标题前 ~1 字幸存。纯 CSS。 */
    .block { min-width: 48px; }
}

/* ============= 30. 打印态屏蔽（防止误触打印浏览器快捷键） ============= */
@media print {
    body::before,
    .stats-popover,
    .popover,
    .hover-tooltip,
    .modal-overlay { display: none !important; }
}

/* ============= 31. 资金核对弹窗 · Step 2 对账表 ============= */
/* V19 — TRANSPOSED grid. Channels run HORIZONTALLY (one column each);
 * timeline / attribute rows extend DOWNWARD. Replaces V12's recon-table
 * horizontal layout — the old version left a wide expanse of unused width
 * on the common-case 1-handover day. The single-column auto-fitting layout
 * gives a visually full table whether the operator has 3 channels or 7.
 *
 * Grid layout:
 *   col 1               = sticky label column (auto-width, content-fit)
 *   cols 2..n+1         = one per channel (1fr each, equal width)
 * Each "row" is N cells (1 label + N channels) using `display: contents`
 * on the wrapping template — we don't need an explicit row container because
 * the cells flow into the grid by source order.
 *
 * Inline custom property `--recon-channel-count` (set on .recon-grid via
 * :style) drives the template-columns count without JS measurement.
 */
.recon-grid {
    display: grid;
    grid-template-columns: minmax(96px, auto) repeat(var(--recon-channel-count, 3), minmax(120px, 1fr));
    gap: 1px;
    background: var(--border-color);
    border: 1px solid var(--border-color);
    border-radius: 10px;
    overflow: hidden;
    /* 毛玻璃 — sits on the modal's panel background through the 1px grid gaps */
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
}

.recon-grid__cell {
    padding: 10px 12px;
    background: var(--bg-panel);
    font-family: var(--font-mono);
    font-size: 14px;
    font-weight: 700;
    color: var(--text-primary);
    display: flex;
    align-items: center;
    min-height: 44px;
}

/* Empty top-left corner cell — keeps the header row's grid alignment clean */
.recon-grid__cell--corner {
    background: var(--bg-elev1);
}

/* Channel name header (微信 / 支付宝 / 现金 / 美团 / 抖音 / …) */
.recon-grid__cell--header {
    background: linear-gradient(180deg, var(--bg-elev2), var(--bg-elev1));
    color: var(--accent-cyan);
    font-family: 'Inter', system-ui, sans-serif;
    font-weight: 700;
    font-size: 12px;
    letter-spacing: 1.5px;
    text-transform: uppercase;
    justify-content: center;
    text-align: center;
    border-bottom: 1px solid var(--accent-blue);
}

/* Left-edge label cell (开店底数 / 今日进账 / …) */
.recon-grid__cell--label {
    background: var(--bg-elev1);
    font-family: 'Inter', system-ui, sans-serif;
    color: var(--text-secondary);
    font-size: 12px;
    font-weight: 600;
    letter-spacing: 1px;
    justify-content: flex-start;
}

/* Strong-emphasis label (当前应有) — pairs with the highlighted value row */
.recon-grid__cell--label-strong {
    color: var(--accent-cyan);
    font-weight: 800;
    font-size: 13px;
}

/* Generic value cell (numeric monospace, right-aligned for vertical scanning) */
.recon-grid__cell--value {
    justify-content: flex-end;
    color: var(--text-primary);
}

/* 当前应有 row — visually anchored as the "system says you should have this much" line */
.recon-grid__cell--expected {
    justify-content: flex-end;
    background: linear-gradient(180deg, rgba(56, 189, 248, 0.08), rgba(56, 189, 248, 0.04));
    color: var(--accent-cyan);
    font-weight: 900;
    font-size: 15px;
    border-top: 1px solid rgba(56, 189, 248, 0.25);
    border-bottom: 1px solid rgba(56, 189, 248, 0.25);
}

/* 实际金额 row — inline input bound to actualAmounts[ch.id] (same v-model as Step 1) */
.recon-grid__cell--input {
    padding: 6px 8px;
    background: var(--bg-elev1);
    justify-content: stretch;
}
.recon-grid__cell--input input.input-dark {
    width: 100%;
    padding: 6px 10px;
    font-family: var(--font-mono);
    font-size: 15px;
    font-weight: 700;
    text-align: right;
}

/* 资金差额 row — colour driven by inline :style binding (green=0, red=≠0) */
.recon-grid__cell--diff {
    justify-content: flex-end;
    font-weight: 800;
    font-size: 14px;
    background: rgba(15, 15, 20, 0.55);
    border-top: 1px solid var(--border-color);
}

/* ============= V14 资金核对 — 各渠道已入账 (recon-breakdown) =============
 *
 * Per-channel breakdown panel for shift handover. Each row is one channel
 * (Meituan reservation / Douyin group-buy / Alipay / WeChat / Cash / etc.)
 * with its actual received amount. No grand total — totals already live in
 * the top-bar stat buttons.
 */
.recon-breakdown {
    font-size: 13px;
}
.recon-section-title {
    font-size: 11px;
    color: var(--text-secondary);
    font-weight: 700;
    letter-spacing: 1px;
    text-transform: uppercase;
    margin: 6px 0 4px;
    padding-bottom: 4px;
    border-bottom: 1px dashed rgba(255, 255, 255, 0.06);
}
.recon-section-title:first-child {
    margin-top: 0;
}
/* V15.4 — compact 2-column reconciliation grid.
 *
 * Pre-V15.4 used `grid-template-columns: 1fr auto auto` which gave the
 * label column ALL the remaining horizontal space, pushing the amount
 * and meta far to the right of the panel. On a 480px-wide breakdown
 * card that's a 200px+ blank gap between "生日票" and "¥24.54", and
 * the operator's eye has to track across the void to pair them up.
 *
 * V15.4 collapses the layout into two columns: the LEFT column holds
 * the label with the metadata stacked beneath it in a smaller, dim
 * weight, and the RIGHT column holds the amount. `auto 1fr` on the
 * outer grid lets the label width shrink to its content so the amount
 * sits flush-right against it (with `gap: 12px`), eliminating the
 * gulf. Tabular numerals keep the amounts column-aligned across rows
 * even though there's no fixed min-width any more.
 */
.recon-row {
    display: grid;
    grid-template-columns: auto 1fr;
    align-items: center;
    gap: 2px 12px;
    padding: 4px 0;
    border-bottom: 1px solid rgba(255, 255, 255, 0.04);
}
.recon-row:last-child {
    border-bottom: none;
}
.recon-row .recon-label {
    color: var(--text-primary);
    font-weight: 600;
}
.recon-row .recon-amount {
    font-family: var(--font-mono, 'JetBrains Mono', 'SF Mono', monospace);
    font-variant-numeric: tabular-nums;
    font-weight: 800;
    font-size: 15px;
    color: var(--accent-green, #10b981);
    text-align: right;
    /* `justify-self: end` keeps the amount column-flush right while
     * still allowing it to sit immediately next to the label rather
     * than against the panel edge. */
    justify-self: end;
}
.recon-row .recon-amount.negative {
    color: var(--accent-red, #ef4444);
}
.recon-row .recon-meta {
    color: var(--text-secondary);
    font-size: 11px;
    /* The meta sits in the LEFT column directly under the label so the
     * "(3场已入场)" caption reads as a sub-line of "生日票" rather than
     * a floating right-side artifact. `grid-column: 1` pins it under
     * the label; the amount cell on the right spans both rows via
     * `grid-row: 1 / span 2`. */
    grid-column: 1;
    text-align: left;
    margin-left: 2px;
}
.recon-row .recon-amount {
    grid-row: 1 / span 2;
}
.recon-row.recon-deduction .recon-label {
    color: var(--accent-orange, #f59e0b);
    font-weight: 700;
}

/* ============= V16 资金核对 — 双栏布局 (settlement-twocol-grid) =============
 *
 * Left column  : 资金核对 — 各渠道已入账 (.recon-breakdown)
 * Right column : 今日快讯 (.recon-quickstats) + 渠道占比 (.recon-channel-bars)
 *                + 今日交接记录 (.handover-timeline)
 *
 * Pre-V16 every always-visible card stacked into a single vertical column on
 * the left, leaving the right half of the 1080-wide modal empty. V16 puts the
 * two long panels side-by-side and surfaces additional supplementary data on
 * the right that's already in the backend response but was never rendered.
 *
 * Breakpoint: at ≤ 1024px viewports (or older sub-modal containers narrower
 * than that) the grid collapses to one column so a phone / tablet operator
 * still sees the full content vertically.
 *
 * `min-width: 0` on the grid items is REQUIRED — without it the child grid
 * inside .recon-row (auto 1fr) would refuse to shrink and force horizontal
 * scrolling on the modal even when the column is narrower than the row's
 * intrinsic minimum.
 */
.settlement-twocol-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 14px;
    margin-bottom: 12px;
}
.settlement-twocol-grid > * { min-width: 0; }

@media (max-width: 1024px) {
    .settlement-twocol-grid {
        grid-template-columns: 1fr;
    }
}

/* — 今日快讯 (quick stats) — 4 mini tile grid */
.recon-quickstats {
    background: var(--bg-panel);
    border: 1px solid var(--border-color);
    border-radius: 12px;
    padding: 12px 16px;
    backdrop-filter: var(--glass-blur-light);
}
.recon-quickstats__title {
    font-size: 13px;
    font-weight: bold;
    margin-bottom: 8px;
    color: var(--text-primary);
    letter-spacing: 0.5px;
}
.recon-quickstats__grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 8px;
}
.recon-quickstats__tile {
    background: var(--bg-elev2);
    border: 1px solid var(--border-soft);
    border-radius: 8px;
    padding: 10px 12px;
    transition: border-color 0.2s var(--easing);
}
.recon-quickstats__tile:hover {
    border-color: rgba(59, 130, 246, 0.35);
}
.recon-quickstats__tile-label {
    font-size: 10px;
    color: var(--text-secondary);
    letter-spacing: 1px;
    text-transform: uppercase;
    margin-bottom: 4px;
}
.recon-quickstats__tile-value {
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
    font-size: 20px;
    font-weight: 800;
    color: var(--text-primary);
    line-height: 1.2;
}
.recon-quickstats__tile-meta {
    font-size: 10px;
    color: var(--text-tertiary);
    margin-top: 2px;
}
.recon-quickstats__tile.is-accent .recon-quickstats__tile-value {
    color: var(--accent-green);
    text-shadow: 0 0 12px rgba(16, 185, 129, 0.25);
}
.recon-quickstats__tile.is-info .recon-quickstats__tile-value {
    color: var(--accent-blue);
}

/* — 渠道占比 (channel revenue bars) — horizontal proportion view */
.recon-channel-bars {
    background: var(--bg-panel);
    border: 1px solid var(--border-color);
    border-radius: 12px;
    padding: 12px 16px;
    backdrop-filter: var(--glass-blur-light);
}
.recon-channel-bars__title {
    font-size: 13px;
    font-weight: bold;
    margin-bottom: 8px;
    color: var(--text-primary);
    letter-spacing: 0.5px;
    display: flex;
    align-items: baseline;
    justify-content: space-between;
}
.recon-channel-bars__total {
    font-size: 11px;
    color: var(--text-secondary);
    font-weight: 400;
    font-family: var(--font-mono);
}
.recon-channel-bars__row {
    display: grid;
    grid-template-columns: 90px 1fr 70px;
    align-items: center;
    gap: 8px;
    padding: 4px 0;
    font-size: 12px;
}
.recon-channel-bars__name {
    color: var(--text-primary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.recon-channel-bars__bar {
    position: relative;
    height: 8px;
    background: rgba(255, 255, 255, 0.04);
    border-radius: 4px;
    overflow: hidden;
}
.recon-channel-bars__bar-fill {
    position: absolute;
    inset: 0 auto 0 0;
    border-radius: 4px;
    transition: width 0.4s var(--easing);
}
.recon-channel-bars__bar-fill.is-online {
    background: linear-gradient(90deg, var(--accent-blue), var(--accent-cyan));
    box-shadow: 0 0 8px rgba(6, 182, 212, 0.35);
}
.recon-channel-bars__bar-fill.is-offline {
    background: linear-gradient(90deg, var(--accent-green), #34d399);
    box-shadow: 0 0 8px rgba(16, 185, 129, 0.35);
}
.recon-channel-bars__amount {
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
    color: var(--text-primary);
    font-weight: 700;
    text-align: right;
}
.recon-channel-bars__empty {
    text-align: center;
    padding: 14px 0;
    color: var(--text-secondary);
    font-size: 12px;
}

/* — Right column scroll containment so a very long handover timeline can
 *   scroll independently from the left financial summary on tall lists. */
.settlement-right-stack {
    display: flex;
    flex-direction: column;
    gap: 12px;
    min-width: 0;
}
.settlement-right-stack .handover-timeline {
    margin-bottom: 0 !important;  /* gap on the stack handles spacing */
}
.settlement-twocol-grid .recon-breakdown {
    margin-bottom: 0 !important;  /* grid gap replaces card margin */
}

/* ============= V17 资金核对 — 横向时间线矩阵 (recon-matrix) =============
 *
 * Paradigm: rows × columns matrix. Rows = channels (offline group + online
 * group); columns = chronological day events (opening → shift activity →
 * handover balance → next shift → … → current open shift).
 *
 * Layout strategy:
 *   • CSS Grid for the body so column widths can flex per kind.
 *   • The first column (channel name) uses `position: sticky; left: 0` so
 *     it stays anchored while the operator scrolls the wide matrix right.
 *   • `overflow-x: auto` on the outer .recon-matrix__scroll lets WebKit /
 *     Blink handle the native scrollbar; no JS scroll plumbing.
 *
 * Column widths (--col-* CSS variables let us tweak globally):
 *   • Channel name (sticky)     : 138px
 *   • Balance (opening/handover): 108px
 *   • Activity (shift)          : 224px  (4 × 52px stats + gap)
 *   • Live shift                : 224px, dashed accent border
 */
.recon-matrix {
    background: var(--bg-panel);
    border: 1px solid var(--border-color);
    border-radius: 12px;
    padding: 12px 16px;
    backdrop-filter: var(--glass-blur-light);
    margin-bottom: 12px;
}
.recon-matrix__header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 10px;
    flex-wrap: wrap;
    margin-bottom: 10px;
}
.recon-matrix__title {
    font-size: 13px;
    font-weight: bold;
    letter-spacing: 0.5px;
    color: var(--text-primary);
}
.recon-matrix__subtitle {
    font-size: 11px;
    color: var(--text-secondary);
    font-weight: 400;
}
.recon-matrix__legend {
    display: flex;
    gap: 12px;
    font-size: 10px;
    color: var(--text-tertiary);
    letter-spacing: 0.5px;
}
.recon-matrix__legend-chip {
    display: inline-flex;
    align-items: center;
    gap: 4px;
}
.recon-matrix__legend-dot {
    display: inline-block;
    width: 8px;
    height: 8px;
    border-radius: 2px;
}
.recon-matrix__legend-dot.is-base { background: rgba(148, 163, 184, 0.55); }
.recon-matrix__legend-dot.is-shift { background: rgba(59, 130, 246, 0.65); }
.recon-matrix__legend-dot.is-balance { background: rgba(16, 185, 129, 0.65); }
.recon-matrix__legend-dot.is-live {
    background: transparent;
    border: 1.5px dashed var(--accent-orange);
    width: 10px; height: 10px;
}

/* Horizontal scroll viewport. The sticky first column relies on this
 * container being the scrolling ancestor. */
.recon-matrix__scroll {
    overflow-x: auto;
    overflow-y: hidden;
    -webkit-overflow-scrolling: touch;
    border: 1px solid var(--border-soft);
    border-radius: 10px;
    background: rgba(0, 0, 0, 0.18);
}
.recon-matrix__scroll::-webkit-scrollbar {
    height: 10px;
}
.recon-matrix__scroll::-webkit-scrollbar-track {
    background: rgba(255, 255, 255, 0.03);
}
.recon-matrix__scroll::-webkit-scrollbar-thumb {
    background: rgba(59, 130, 246, 0.35);
    border-radius: 4px;
}
.recon-matrix__scroll::-webkit-scrollbar-thumb:hover {
    background: rgba(59, 130, 246, 0.55);
}

/* Grid backbone — auto column widths set inline by Vue (one per column). */
.recon-matrix__table {
    display: grid;
    /* grid-template-columns set inline via :style binding (varies with column count) */
    min-width: 100%;
}

/* Cells. The matrix is a single CSS grid, so every cell is a sibling div.
 * Row identity is encoded via .is-row-N class for striped hover (not used yet)
 * and via the inline grid-row binding. */
.recon-matrix__cell {
    padding: 8px 10px;
    border-right: 1px solid var(--border-soft);
    border-bottom: 1px solid var(--border-soft);
    font-size: 12px;
    color: var(--text-primary);
    background: transparent;
    min-width: 0;        /* let cell content shrink with column width */
    box-sizing: border-box;
}
.recon-matrix__cell:last-child {
    border-right: none;
}

/* Sticky channel-name column (first column). Z-index is 2 so it sits above
 * the data cells when scrolling; the header row uses z-index 3 to win over
 * the sticky body cells when they cross paths. */
.recon-matrix__cell.is-sticky {
    position: sticky;
    left: 0;
    z-index: 2;
    background: var(--bg-panel-deep);
    border-right: 1px solid var(--border-color);
    box-shadow: 4px 0 8px rgba(0, 0, 0, 0.35);
}

/* Header row variants (row 1 = column-kind label; row 2 = time + operator). */
.recon-matrix__cell.is-header {
    background: rgba(15, 15, 25, 0.85);
    z-index: 3;
    border-bottom: 1px solid var(--border-color);
}
.recon-matrix__cell.is-header.is-sticky {
    z-index: 4;
    background: rgba(10, 10, 18, 0.95);
}
.recon-matrix__col-label {
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 1px;
    color: var(--text-primary);
    text-transform: uppercase;
}
.recon-matrix__col-meta {
    font-size: 10px;
    color: var(--text-secondary);
    margin-top: 2px;
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Column-kind colour accents — left bar inside the header cell. */
.recon-matrix__cell.col-kind-opening { box-shadow: inset 3px 0 0 rgba(148, 163, 184, 0.55); }
.recon-matrix__cell.col-kind-shift { box-shadow: inset 3px 0 0 rgba(59, 130, 246, 0.65); }
.recon-matrix__cell.col-kind-handover_balance { box-shadow: inset 3px 0 0 rgba(16, 185, 129, 0.65); }
.recon-matrix__cell.col-kind-close_balance { box-shadow: inset 3px 0 0 rgba(239, 68, 68, 0.65); }
.recon-matrix__cell.col-kind-live_shift {
    background: linear-gradient(180deg, rgba(245, 158, 11, 0.06), transparent);
    box-shadow: inset 3px 0 0 var(--accent-orange);
    border-right: 1px dashed rgba(245, 158, 11, 0.4) !important;
}
.recon-matrix__cell.col-kind-live_shift.is-header {
    background: linear-gradient(180deg, rgba(245, 158, 11, 0.12), rgba(245, 158, 11, 0.03));
}
.recon-matrix__cell.is-sticky.col-kind-opening,
.recon-matrix__cell.is-sticky.col-kind-shift,
.recon-matrix__cell.is-sticky.col-kind-handover_balance,
.recon-matrix__cell.is-sticky.col-kind-close_balance,
.recon-matrix__cell.is-sticky.col-kind-live_shift {
    box-shadow: 4px 0 8px rgba(0, 0, 0, 0.35);   /* sticky shadow wins */
}

/* Group separator rows (线下收款 / 线上收款 sub-headers). */
.recon-matrix__cell.is-group-header {
    grid-column: 1 / -1;
    background: rgba(15, 15, 25, 0.5);
    border-bottom: 1px solid var(--border-color);
    border-right: none;
    padding: 6px 12px;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 2px;
    text-transform: uppercase;
    color: var(--text-secondary);
    position: sticky;
    left: 0;
    z-index: 2;
}
.recon-matrix__cell.is-group-header.group-online {
    color: var(--accent-cyan);
}
.recon-matrix__cell.is-group-header.group-offline {
    color: var(--accent-green);
}

/* Channel name (first-col body cell). */
.recon-matrix__channel {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}
.recon-matrix__channel-name {
    font-size: 13px;
    font-weight: 600;
    color: var(--text-primary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.recon-matrix__channel-rate {
    font-size: 10px;
    color: var(--text-tertiary);
    font-family: var(--font-mono);
}

/* Balance cell (opening / handover / close columns — single big number). */
.recon-matrix__balance {
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
    font-weight: 700;
    font-size: 14px;
    color: var(--text-primary);
    text-align: right;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 2px;
}
.recon-matrix__balance.is-placeholder {
    color: var(--text-tertiary);
    font-weight: 500;
}

/* V18 — Activity cell expanded from 4 to 5 stats: 应收 / 优惠 / 退款 / 扣点 / 实收.
 *
 * Layout: 田字格 (2-row × 2-col) for the four supplementary stats, with the
 * bottom-line 实收 spanning a full third row at the bottom — the eye should
 * land there last, and giving it a wider home prevents the truncation
 * ("¥1,97...") the user reported on long numbers in the V17 single-row
 * layout. Each cell has a 4-line vertical rhythm with the label + value
 * stacked.
 *
 *   ┌──────┬──────┐
 *   │ 应收 │ 优惠 │
 *   ├──────┼──────┤
 *   │ 退款 │ 扣点 │
 *   ├──────┴──────┤
 *   │     实收     │   ← .is-net-span (grid-column: 1 / -1)
 *   └─────────────┘
 *
 * Activity-column width is also bumped from 224px → 264px in the
 * matrix's grid-template-columns binding (in index.html) to give each
 * sub-tile enough room to hold a 5-digit yuan figure without ellipsing.
 */
.recon-matrix__stats {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-auto-rows: minmax(0, auto);
    gap: 4px;
    align-items: stretch;
}
.recon-matrix__stat {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
    padding: 5px 6px;
    border-radius: 5px;
    background: rgba(255, 255, 255, 0.02);
    text-align: center;
}
.recon-matrix__stat-label {
    font-size: 9px;
    letter-spacing: 0.5px;
    color: var(--text-tertiary);
    text-transform: uppercase;
}
.recon-matrix__stat-value {
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
    font-size: 11px;
    font-weight: 700;
    color: var(--text-primary);
    line-height: 1.15;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.recon-matrix__stat.is-gross    .recon-matrix__stat-value { color: var(--text-primary); }
.recon-matrix__stat.is-discount .recon-matrix__stat-value { color: var(--accent-purple); }
.recon-matrix__stat.is-refund   .recon-matrix__stat-value { color: var(--accent-red); }
.recon-matrix__stat.is-fee      .recon-matrix__stat-value { color: var(--accent-orange); }
.recon-matrix__stat.is-net      .recon-matrix__stat-value { color: var(--accent-green); font-weight: 800; }

/* V18 — 实收 takes the bottom row spanning both columns. Slightly larger
 * font + a subtle green tint background so it pops as the "this is what
 * the venue actually kept" bottom line. */
.recon-matrix__stat.is-net-span {
    grid-column: 1 / -1;
    background: rgba(16, 185, 129, 0.08);
    border: 1px solid rgba(16, 185, 129, 0.18);
    padding: 6px 8px;
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    text-align: left;
}
.recon-matrix__stat.is-net-span .recon-matrix__stat-label {
    font-size: 10px;
    color: var(--accent-green);
    opacity: 0.85;
}
.recon-matrix__stat.is-net-span .recon-matrix__stat-value {
    font-size: 13px;
    font-weight: 900;
}

/* Zero-activity cell — dim the values so the eye glides over to the
 * non-zero columns; keep all 5 labels for vertical alignment. */
.recon-matrix__stats.is-zero .recon-matrix__stat-value {
    color: var(--text-disabled);
    font-weight: 500;
}
.recon-matrix__stats.is-zero .recon-matrix__stat.is-net-span {
    background: rgba(255, 255, 255, 0.02);
    border-color: var(--border-soft);
}
.recon-matrix__stats.is-zero .recon-matrix__stat.is-net-span .recon-matrix__stat-value {
    color: var(--text-disabled);
}

/* Empty-matrix state (vault hasn't opened today). */
.recon-matrix__empty {
    text-align: center;
    padding: 28px 0;
    color: var(--text-secondary);
    font-size: 13px;
}
.recon-matrix__empty-hint {
    font-size: 11px;
    color: var(--text-tertiary);
    margin-top: 4px;
}

/* ════════════════════════════════════════════════════════════════════════
 * V20a — TRANSPOSED UNIFIED RECONCILIATION MATRIX (.recon-mtx-t)
 *
 * Layout pivot (vs the legacy V17 .recon-matrix above):
 *   • Rows = chronological events (Opening / Shift N / Handover N / Close /
 *            当前应有 / 实际金额 / 资金差额)
 *   • Columns = channels (offline payment methods then online billing items)
 *
 * Sticky axes:
 *   • is-corner    : top-left, sits above both sticky bars (z 4)
 *   • is-header    : sticky top, channel-name row              (z 3)
 *   • is-event-label : sticky left, event-name column          (z 2)
 *   • is-bottom-row : the folded Widget-B rows (当前应有 / 实际金额 / 资金差额)
 *                    — visually accented as the actionable footer
 *
 * Single CSS-grid backbone; grid-template-columns set inline so the
 * column count tracks (offline + online) channel count without a CSS recompile.
 * Each "row" is N+1 sibling cells (1 label + N channels) — no <tr> needed.
 * ════════════════════════════════════════════════════════════════════════ */
.recon-mtx-t {
    background: var(--bg-panel);
    border: 1px solid var(--border-color);
    border-radius: 12px;
    padding: 12px 16px;
    backdrop-filter: var(--glass-blur-light);
    margin-bottom: 12px;
}
.recon-mtx-t__header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 10px;
    flex-wrap: wrap;
    margin-bottom: 10px;
}
.recon-mtx-t__title {
    font-size: 13px;
    font-weight: bold;
    letter-spacing: 0.5px;
    color: var(--text-primary);
}
.recon-mtx-t__subtitle {
    font-size: 11px;
    color: var(--text-secondary);
    font-weight: 400;
}
.recon-mtx-t__legend {
    display: flex;
    gap: 12px;
    font-size: 10px;
    color: var(--text-tertiary);
    letter-spacing: 0.5px;
}
.recon-mtx-t__legend-chip {
    display: inline-flex;
    align-items: center;
    gap: 4px;
}
.recon-mtx-t__legend-dot {
    display: inline-block;
    width: 8px;
    height: 8px;
    border-radius: 2px;
}
.recon-mtx-t__legend-dot.is-base    { background: rgba(148, 163, 184, 0.55); }
.recon-mtx-t__legend-dot.is-shift   { background: rgba(59, 130, 246, 0.65); }
.recon-mtx-t__legend-dot.is-balance { background: rgba(16, 185, 129, 0.65); }
.recon-mtx-t__legend-dot.is-live {
    background: transparent;
    border: 1.5px dashed var(--accent-orange);
    width: 10px; height: 10px;
}

/* Scrollable viewport — sticky cells anchor to THIS container.
 * Both axes scroll: horizontal for many channels, vertical when the
 * stacked timeline + bottom-three rows exceed the modal height. */
.recon-mtx-t__scroll {
    overflow: auto;
    -webkit-overflow-scrolling: touch;
    border: 1px solid var(--border-soft);
    border-radius: 10px;
    background: rgba(0, 0, 0, 0.18);
    /* Cap height so very long timelines (3+ handovers) trigger vertical
     * scroll inside the modal instead of pushing the action buttons off-screen. */
    max-height: 60vh;
}
.recon-mtx-t__scroll::-webkit-scrollbar      { width: 10px; height: 10px; }
.recon-mtx-t__scroll::-webkit-scrollbar-track { background: rgba(255, 255, 255, 0.03); }
.recon-mtx-t__scroll::-webkit-scrollbar-thumb {
    background: rgba(59, 130, 246, 0.35);
    border-radius: 4px;
}
.recon-mtx-t__scroll::-webkit-scrollbar-thumb:hover {
    background: rgba(59, 130, 246, 0.55);
}

/* Grid backbone — column count set inline via Vue :style binding. */
.recon-mtx-t__table {
    display: grid;
    /* grid-template-columns set inline (160px event-label col + N channel cols) */
    min-width: 100%;
}

/* Generic cell. Every grid child uses this; modifier classes layer on top. */
.recon-mtx-t__cell {
    padding: 8px 10px;
    border-right: 1px solid var(--border-soft);
    border-bottom: 1px solid var(--border-soft);
    font-size: 12px;
    color: var(--text-primary);
    background: var(--bg-panel);
    min-width: 0;
    box-sizing: border-box;
}
.recon-mtx-t__cell:last-child {
    border-right: none;
}

/* ── Sticky axes ──
 * Sticky-left  : event-label column stays visible on horizontal scroll.
 * Sticky-top   : channel-name header row stays visible on vertical scroll.
 * Corner       : both — wins z-index over either sticky band.
 */
.recon-mtx-t__cell.is-event-label {
    position: sticky;
    left: 0;
    z-index: 2;
    background: var(--bg-panel-deep);
    border-right: 1px solid var(--border-color);
    box-shadow: 4px 0 8px rgba(0, 0, 0, 0.35);
}
.recon-mtx-t__cell.is-header {
    position: sticky;
    top: 0;
    z-index: 3;
    background: rgba(15, 15, 25, 0.92);
    border-bottom: 1px solid var(--border-color);
}
.recon-mtx-t__cell.is-corner {
    position: sticky;
    top: 0;
    left: 0;
    z-index: 4;
    background: rgba(10, 10, 18, 0.96);
    border-right: 1px solid var(--border-color);
    border-bottom: 1px solid var(--border-color);
}
.recon-mtx-t__col-label {
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 1px;
    color: var(--text-primary);
    text-transform: uppercase;
}
.recon-mtx-t__col-meta {
    font-size: 11px; /* 批V6 TYPO-05: 10→11 px 弹窗内小字提可读 */
    color: var(--text-secondary);
    margin-top: 2px;
    font-family: var(--font-mono);
}

/* Channel-name header cells (top row, non-corner). Offline = green accent,
 * online = cyan accent — same colour vocabulary the legacy .recon-matrix
 * used for the offline/online group headers. */
.recon-mtx-t__channel-name {
    font-size: 13px;
    font-weight: 700;
    color: var(--text-primary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.recon-mtx-t__channel-rate {
    font-size: 11px; /* 批V6 TYPO-05: 10→11 px 弹窗内小字提可读 */
    color: var(--text-tertiary);
    font-family: var(--font-mono);
    margin-top: 2px;
}
.recon-mtx-t__cell.is-channel-offline {
    box-shadow: inset 0 3px 0 var(--accent-green);
}
.recon-mtx-t__cell.is-channel-offline .recon-mtx-t__channel-rate {
    color: var(--accent-green);
    opacity: 0.7;
}
.recon-mtx-t__cell.is-channel-online {
    box-shadow: inset 0 3px 0 var(--accent-cyan);
}
.recon-mtx-t__cell.is-channel-online .recon-mtx-t__channel-rate {
    color: var(--accent-cyan);
    opacity: 0.7;
}

/* Event-label cell content (sticky-left first column). */
.recon-mtx-t__event-name {
    font-size: 12px;
    font-weight: 700;
    color: var(--text-primary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.recon-mtx-t__event-meta {
    font-size: 11px; /* 批V6 TYPO-05: 10→11 px 弹窗内小字提可读 */
    color: var(--text-secondary);
    margin-top: 2px;
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Event-kind colour accents on the sticky-left label cell.
 * The legacy matrix used inset box-shadow on the LEFT for the kind ribbon;
 * V20a uses the BOTTOM since each event is a row, not a column. */
.recon-mtx-t__cell.is-event-label.kind-opening          { box-shadow: 4px 0 8px rgba(0,0,0,0.35), inset 0 -2px 0 rgba(148, 163, 184, 0.55); }
.recon-mtx-t__cell.is-event-label.kind-shift            { box-shadow: 4px 0 8px rgba(0,0,0,0.35), inset 0 -2px 0 rgba(59, 130, 246, 0.65); }
.recon-mtx-t__cell.is-event-label.kind-handover_balance { box-shadow: 4px 0 8px rgba(0,0,0,0.35), inset 0 -2px 0 rgba(16, 185, 129, 0.65); }
.recon-mtx-t__cell.is-event-label.kind-close_balance    { box-shadow: 4px 0 8px rgba(0,0,0,0.35), inset 0 -2px 0 rgba(239, 68, 68, 0.65); }
.recon-mtx-t__cell.is-event-label.kind-live_shift       { box-shadow: 4px 0 8px rgba(0,0,0,0.35), inset 0 -2px 0 var(--accent-orange); }

/* Live-shift row gets a soft amber background across all its cells so the
 * "this is happening NOW" event is visually distinct. */
.recon-mtx-t__cell.kind-live_shift {
    background: linear-gradient(180deg, rgba(245, 158, 11, 0.06), transparent);
    border-bottom: 1px dashed rgba(245, 158, 11, 0.4);
}

/* Balance cell content (opening / handover / close kinds — single big yuan). */
.recon-mtx-t__balance {
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
    font-weight: 700;
    font-size: 14px;
    color: var(--text-primary);
    text-align: right;
}
.recon-mtx-t__balance.is-placeholder {
    color: var(--text-tertiary);
    font-weight: 500;
}

/* 5-stat block (shift / live_shift activity cells). Same 田字格 layout as
 * the legacy matrix — 应收 / 优惠 / 退款 / 扣点 on a 2×2 grid, with the
 * 实收 bottom-line spanning the full width as the focal "this is what the
 * venue kept" line. */
.recon-mtx-t__stats {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-auto-rows: minmax(0, auto);
    gap: 4px;
    align-items: stretch;
}
.recon-mtx-t__stat {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
    padding: 5px 6px;
    border-radius: 5px;
    background: rgba(255, 255, 255, 0.02);
    text-align: center;
}
.recon-mtx-t__stat-label {
    font-size: 10px; /* 批V6 TYPO-05: 9→10 px 弹窗内小字提可读 */
    letter-spacing: 0.5px;
    color: var(--text-tertiary);
    text-transform: uppercase;
}
.recon-mtx-t__stat-value {
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
    font-size: 11px;
    font-weight: 700;
    color: var(--text-primary);
    line-height: 1.15;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.recon-mtx-t__stat.is-gross    .recon-mtx-t__stat-value { color: var(--text-primary); }
.recon-mtx-t__stat.is-discount .recon-mtx-t__stat-value { color: var(--accent-purple); }
.recon-mtx-t__stat.is-refund   .recon-mtx-t__stat-value { color: var(--accent-red); }
.recon-mtx-t__stat.is-fee      .recon-mtx-t__stat-value { color: var(--accent-orange); }
.recon-mtx-t__stat.is-net      .recon-mtx-t__stat-value { color: var(--accent-green); font-weight: 800; }
.recon-mtx-t__stat.is-net-span {
    grid-column: 1 / -1;
    background: rgba(16, 185, 129, 0.08);
    border: 1px solid rgba(16, 185, 129, 0.18);
    padding: 6px 8px;
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    text-align: left;
}
.recon-mtx-t__stat.is-net-span .recon-mtx-t__stat-label {
    font-size: 11px; /* 批V6 TYPO-05: 10→11 px 常规 stat-label 9→10 后, 同步抬 实收 焦点行标签保 +1px 强调档(grid-column:1/-1 整行无溢出) */
    color: var(--accent-green);
    opacity: 0.85;
}
.recon-mtx-t__stat.is-net-span .recon-mtx-t__stat-value {
    font-size: 13px;
    font-weight: 900;
}
.recon-mtx-t__stats.is-zero .recon-mtx-t__stat-value {
    color: var(--text-zero);
    font-weight: 500;
}
.recon-mtx-t__stats.is-zero .recon-mtx-t__stat.is-net-span {
    background: rgba(255, 255, 255, 0.02);
    border-color: var(--border-soft);
}
.recon-mtx-t__stats.is-zero .recon-mtx-t__stat.is-net-span .recon-mtx-t__stat-value {
    color: var(--text-zero);
}

/* ── Bottom-row footer (folded-in Widget-B rows) ──
 * The three rows that the V19 .recon-grid used to host:
 *   当前应有  → is-expected   (cyan-tinted)
 *   实际金额  → is-actual-input (offline inputs; online '—')
 *   资金差额  → is-diff        (red/green driven by inline :style)
 *
 * is-bottom-row gives them visual separation from the timeline events
 * above (slightly heavier top border + accent background tier).
 */
.recon-mtx-t__cell.is-bottom-row {
    border-top: 1px solid var(--border-color);
    background: var(--bg-elev1);
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
    font-size: 13px;
    font-weight: 700;
    text-align: right;
    display: flex;
    align-items: center;
    justify-content: flex-end;
}
.recon-mtx-t__cell.is-bottom-row.is-event-label {
    justify-content: flex-start;
    text-align: left;
}
.recon-mtx-t__cell.is-expected {
    background: linear-gradient(180deg, rgba(56, 189, 248, 0.10), rgba(56, 189, 248, 0.04));
    color: var(--accent-cyan);
    font-weight: 900;
    font-size: 14px;
    border-top: 1px solid rgba(56, 189, 248, 0.30);
}
.recon-mtx-t__cell.is-expected-label .recon-mtx-t__event-name {
    color: var(--accent-cyan);
}

/* Input cell (实际金额 — editable). Visual mass to telegraph "this is
 * where you fill in" while still aligning with the grid. */
.recon-mtx-t__cell.is-actual-input {
    padding: 4px 6px;
    background: var(--bg-elev2);
    justify-content: stretch;
}
.recon-mtx-t__cell.is-actual-input input.input-dark {
    width: 100%;
    padding: 6px 10px;
    font-family: var(--font-mono);
    font-size: 16px;
    font-weight: 700;
    text-align: right;
    font-variant-numeric: tabular-nums;
}

/* Diff cell — value colour driven by inline :style; this just owns layout. */
.recon-mtx-t__cell.is-diff {
    background: rgba(15, 15, 20, 0.55);
    font-weight: 800;
    font-size: 14px;
    border-top: 1px solid var(--border-color);
}
.recon-mtx-t__cell.is-diff-label .recon-mtx-t__event-name {
    color: var(--accent-orange);
}

/* Placeholder '—' for cells that don't apply (online channels in
 * Widget-B rows since they have no physical cash box). */
.recon-mtx-t__cell.is-placeholder-cell {
    color: var(--text-tertiary);
    font-weight: 500;
    opacity: 0.55;
}

/* Empty states (no timeline events OR no channels configured). */
.recon-mtx-t__empty {
    text-align: center;
    padding: 28px 0;
    color: var(--text-secondary);
    font-size: 13px;
    background: rgba(0, 0, 0, 0.18);
    border: 1px solid var(--border-soft);
    border-radius: 10px;
}
.recon-mtx-t__empty-hint {
    font-size: 11px;
    color: var(--text-tertiary);
    margin-top: 4px;
}
