/* 刀版管理系统 —— 视觉语言与询价系统保持一致（同一套设计令牌与组件形态） */
:root {
  /* 底色与层次 */
  --bg: #f1f5fb;
  --surface: #ffffff;
  --surface-2: #f7f9fd;
  --line: #e8edf6;
  --line-strong: #dbe3f0;

  /* 文字 */
  --ink: #0f1a2e;
  --ink-2: #4a5670;
  --ink-3: #8b97b0;
  --ink-4: #aab4c8;

  /* 主题色 */
  --brand: #3b6cff;
  --brand-deep: #2550e0;
  --brand-soft: #eaf0ff;
  --teal: #12c9b6;
  --teal-soft: #e2faf7;
  --coral: #ff7a66;
  --coral-soft: #ffefe9;
  --amber: #ffb020;
  --amber-soft: #fff6e3;
  --violet: #8b6cf6;
  --violet-soft: #f1ecff;
  --danger: #ef3f3f;
  --danger-soft: #ffecec;
  --green: #16a34a;
  --green-soft: #e9f9ef;

  /* 刀版库位专用 */
  --slot-free: #2f6bff;
  --slot-free-2: #1f4fd8;
  --slot-used: #ef4444;
  --slot-used-2: #c62222;

  --radius: 16px;
  --radius-sm: 10px;
  --radius-xs: 8px;

  --shadow-1: 0 1px 2px rgba(16, 26, 46, .04), 0 6px 18px -8px rgba(16, 26, 46, .10);
  --shadow-2: 0 2px 6px rgba(16, 26, 46, .05), 0 18px 40px -18px rgba(16, 26, 46, .20);
  --shadow-pop: 0 24px 60px -20px rgba(16, 26, 46, .32);

  --font: "PingFang SC", "HarmonyOS Sans SC", "Microsoft YaHei UI", "Source Han Sans SC", "Noto Sans SC", system-ui, sans-serif;
  --font-num: "SF Mono", "JetBrains Mono", "Cascadia Mono", Consolas, var(--font);

  --nav-h: 66px;
}

* { box-sizing: border-box; }
[hidden] { display: none !important; }

html, body, #app { min-height: 100%; }

body {
  margin: 0;
  font-family: var(--font);
  font-size: 14px;
  line-height: 1.5;
  color: var(--ink);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
}

body::before {
  content: '';
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  background:
    radial-gradient(680px 420px at 8% -8%, rgba(59, 108, 255, .13), transparent 62%),
    radial-gradient(560px 380px at 96% 2%, rgba(18, 201, 182, .13), transparent 60%),
    radial-gradient(700px 460px at 62% 104%, rgba(255, 176, 32, .10), transparent 62%);
}

a { color: inherit; text-decoration: none; }
button, input, select, textarea { font: inherit; color: inherit; }

::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-thumb { background: #d3dced; border-radius: 999px; border: 2px solid transparent; background-clip: content-box; }
::-webkit-scrollbar-thumb:hover { background: #bcc9e0; background-clip: content-box; }
::-webkit-scrollbar-track { background: transparent; }

/* ============================ 启动态 ============================ */
.boot { min-height: 100dvh; display: grid; place-content: center; justify-items: center; gap: 14px; position: relative; z-index: 1; }
.boot-mark { width: 54px; height: 54px; border-radius: 17px; display: grid; place-items: center; color: #fff; font-weight: 800; font-size: 22px; background: linear-gradient(135deg, #4a7bff, #2550e0 55%, #12c9b6); box-shadow: 0 10px 26px -10px rgba(37, 80, 224, .7); }
.boot-text { color: var(--ink-3); font-size: 13px; }

/* ============================ 登录页 ============================ */
.login-wrap { position: relative; z-index: 1; min-height: 100dvh; display: grid; place-items: center; padding: 28px 18px 40px; }
.login-card {
  width: min(940px, 100%);
  display: grid;
  grid-template-columns: 1.05fr .95fr;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 22px;
  box-shadow: var(--shadow-pop);
  overflow: hidden;
}
.login-side {
  position: relative;
  padding: 38px 34px;
  color: #fff;
  background: linear-gradient(150deg, #3b6cff, #2550e0 48%, #12c9b6 130%);
  display: flex; flex-direction: column; gap: 18px;
}
.login-side::after {
  content: ''; position: absolute; inset: 0;
  background:
    radial-gradient(320px 220px at 88% 8%, rgba(255, 255, 255, .26), transparent 66%),
    radial-gradient(260px 200px at 6% 96%, rgba(255, 255, 255, .18), transparent 66%);
  pointer-events: none;
}
.login-side > * { position: relative; z-index: 1; }
.login-badge { width: 46px; height: 46px; border-radius: 15px; display: grid; place-items: center; font-weight: 800; font-size: 19px; background: rgba(255, 255, 255, .18); border: 1px solid rgba(255, 255, 255, .34); }
.login-side h1 { margin: 0; font-size: 25px; letter-spacing: -.02em; font-weight: 800; }
.login-side .sub { margin: 0; font-size: 13px; color: rgba(255, 255, 255, .84); }
.login-points { margin: 6px 0 0; padding: 0; list-style: none; display: grid; gap: 11px; font-size: 13px; }
.login-points li { display: flex; gap: 9px; align-items: flex-start; color: rgba(255, 255, 255, .95); }
.login-points li::before { content: ''; flex: 0 0 auto; width: 7px; height: 7px; margin-top: 6px; border-radius: 50%; background: rgba(255, 255, 255, .78); }
.login-foot { margin-top: auto; font-size: 11.5px; color: rgba(255, 255, 255, .72); }
.login-form { padding: 38px 34px; display: flex; flex-direction: column; gap: 16px; justify-content: center; }
.login-form h2 { margin: 0 0 2px; font-size: 20px; letter-spacing: -.02em; }
.login-form .hint { margin: -8px 0 6px; font-size: 12.5px; color: var(--ink-3); }

/* ============================ 表单控件 ============================ */
.field { display: grid; gap: 6px; }
.field > label { font-size: 12.5px; font-weight: 650; color: var(--ink-2); }
.input, .select, .textarea {
  width: 100%; height: 40px; padding: 0 13px;
  border: 1px solid var(--line-strong); border-radius: var(--radius-sm);
  background: var(--surface); color: var(--ink); outline: none; transition: .15s;
}
.textarea { height: auto; min-height: 96px; padding: 10px 13px; resize: vertical; line-height: 1.6; }
.input:focus, .select:focus, .textarea:focus { border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-soft); }
.input::placeholder, .textarea::placeholder { color: var(--ink-4); }
.select { appearance: none; background-image: linear-gradient(45deg, transparent 50%, var(--ink-3) 50%), linear-gradient(135deg, var(--ink-3) 50%, transparent 50%); background-position: calc(100% - 17px) 17px, calc(100% - 12px) 17px; background-size: 5px 5px, 5px 5px; background-repeat: no-repeat; padding-right: 32px; }

/* ============================ 顶部导航 ============================ */
.topnav {
  position: sticky; top: 0; z-index: 60;
  height: var(--nav-h);
  display: flex; align-items: center; gap: 20px;
  padding: 0 24px;
  background: rgba(255, 255, 255, .84);
  backdrop-filter: saturate(180%) blur(14px);
  border-bottom: 1px solid var(--line);
}
.brand { display: flex; align-items: center; gap: 11px; flex-shrink: 0; }
.brand-mark { width: 34px; height: 34px; border-radius: 11px; display: grid; place-items: center; color: #fff; font-weight: 800; font-size: 15px; letter-spacing: -.03em; background: linear-gradient(135deg, #4a7bff, #2550e0 55%, #12c9b6); box-shadow: 0 6px 16px -6px rgba(37, 80, 224, .7); }
.brand-text { display: flex; flex-direction: column; line-height: 1.15; }
.brand-name { font-weight: 800; font-size: 15px; letter-spacing: -.02em; }
.brand-sub { font-size: 11px; color: var(--ink-3); letter-spacing: .04em; }

.menu { display: flex; align-items: center; gap: 4px; flex: 1; overflow-x: auto; scrollbar-width: none; }
.menu::-webkit-scrollbar { display: none; }
.menu button {
  border: 1px solid transparent; background: transparent; cursor: pointer;
  padding: 8px 15px; border-radius: 999px; font-size: 13.5px; font-weight: 650; color: var(--ink-2);
  white-space: nowrap; transition: .15s;
}
.menu button:hover { background: var(--surface-2); color: var(--ink); }
.menu button.on { color: var(--brand-deep); background: var(--brand-soft); border-color: rgba(59, 108, 255, .22); }
.menu .badge { display: inline-grid; place-items: center; min-width: 18px; height: 18px; padding: 0 5px; margin-left: 6px; border-radius: 999px; background: var(--danger); color: #fff; font-size: 11px; font-weight: 700; }

.nav-right { display: flex; align-items: center; gap: 10px; flex-shrink: 0; }
.user-chip { display: flex; align-items: center; gap: 9px; padding: 5px 12px 5px 6px; border: 1px solid var(--line); border-radius: 999px; background: var(--surface); }
.user-avatar { width: 26px; height: 26px; border-radius: 50%; display: grid; place-items: center; color: #fff; font-size: 12px; font-weight: 700; background: linear-gradient(135deg, #4a7bff, #2550e0); }
.user-name { font-size: 13px; font-weight: 650; }
.user-role { font-size: 11px; color: var(--ink-3); }

/* ============================ 页面容器 ============================ */
.page { position: relative; z-index: 1; max-width: 1520px; margin: 0 auto; padding: 22px 24px 72px; }
.page-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; flex-wrap: wrap; margin-bottom: 14px; }
.page-title { margin: 0; font-size: 19px; letter-spacing: -.02em; }
.page-desc { margin: 4px 0 0; font-size: 12.5px; color: var(--ink-3); }
.section { margin-top: 18px; }

/* ============================ 卡片 ============================ */
.card { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow-1); overflow: hidden; }
.card-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; padding: 15px 18px; border-bottom: 1px solid var(--line); background: linear-gradient(180deg, var(--surface), var(--surface-2)); }
.card-title { margin: 0; font-size: 14.5px; font-weight: 750; letter-spacing: -.01em; display: flex; align-items: center; gap: 8px; }
.card-sub { margin: 3px 0 0; font-size: 12px; color: var(--ink-3); }
.card-body { padding: 16px 18px; }
.card-body.tight { padding: 12px 14px; }

/* ============================ KPI ============================ */
.kpi-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(146px, 1fr)); gap: 12px; }
.kpi { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-sm); padding: 13px 15px; box-shadow: var(--shadow-1); }
.kpi .k-label { font-size: 12px; color: var(--ink-3); display: flex; align-items: center; gap: 6px; }
.kpi .k-val { font-family: var(--font-num); font-size: 25px; font-weight: 700; letter-spacing: -.02em; margin-top: 3px; }
.kpi .k-unit { font-size: 12px; font-weight: 500; color: var(--ink-3); margin-left: 4px; font-family: var(--font); }
.kpi.brand .k-val { color: var(--brand-deep); }
.kpi.red .k-val { color: var(--danger); }
.kpi.amber .k-val { color: #d98a00; }
.kpi.green .k-val { color: var(--green); }
.kpi.teal .k-val { color: #0d9b8c; }
.kpi-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--brand); }
.kpi-dot.red { background: var(--danger); }
.kpi-dot.amber { background: var(--amber); }
.kpi-dot.green { background: var(--green); }
.kpi-dot.teal { background: var(--teal); }

/* ============================ 按钮 ============================ */
.btn { display: inline-flex; align-items: center; justify-content: center; gap: 7px; height: 38px; padding: 0 16px; border-radius: 11px; border: 1px solid var(--line); background: var(--surface); color: var(--ink); font-weight: 650; font-size: 13.5px; cursor: pointer; white-space: nowrap; transition: .15s; }
.btn:hover:not(:disabled) { border-color: var(--line-strong); box-shadow: var(--shadow-1); transform: translateY(-1px); }
.btn:active:not(:disabled) { transform: translateY(0); }
.btn:disabled { opacity: .5; cursor: not-allowed; }
.btn-primary { border-color: transparent; color: #fff; background: linear-gradient(135deg, #4a7bff, #2550e0); box-shadow: 0 8px 20px -10px rgba(37, 80, 224, .85); }
.btn-primary:hover:not(:disabled) { box-shadow: 0 12px 26px -10px rgba(37, 80, 224, .9); }
.btn-teal { border-color: transparent; color: #fff; background: linear-gradient(135deg, #2ad6c2, #0d9b8c); box-shadow: 0 8px 20px -10px rgba(13, 155, 140, .8); }
.btn-danger { border-color: transparent; color: #fff; background: linear-gradient(135deg, #ff6a5a, #d92b2b); box-shadow: 0 8px 20px -10px rgba(217, 43, 43, .8); }
.btn-ghost { background: var(--surface-2); }
.btn-sm { height: 30px; padding: 0 11px; border-radius: 9px; font-size: 12.5px; font-weight: 600; }
.btn-xs { height: 26px; padding: 0 9px; border-radius: 8px; font-size: 12px; font-weight: 600; }
.btn-block { width: 100%; }
.btn-row { display: flex; gap: 9px; flex-wrap: wrap; align-items: center; }

/* ============================ 标签 ============================ */
.tag { display: inline-flex; align-items: center; gap: 5px; height: 23px; padding: 0 9px; border-radius: 999px; font-size: 11.5px; font-weight: 650; background: var(--surface-2); color: var(--ink-2); border: 1px solid var(--line); white-space: nowrap; }
.tag.brand { background: var(--brand-soft); color: var(--brand-deep); border-color: rgba(59, 108, 255, .2); }
.tag.green { background: var(--green-soft); color: #12703c; border-color: rgba(22, 163, 74, .2); }
.tag.amber { background: var(--amber-soft); color: #a86800; border-color: rgba(255, 176, 32, .32); }
.tag.red { background: var(--danger-soft); color: #b32222; border-color: rgba(239, 63, 63, .22); }
.tag.teal { background: var(--teal-soft); color: #0d7d72; border-color: rgba(18, 201, 182, .26); }
.tag.violet { background: var(--violet-soft); color: #6244d6; border-color: rgba(139, 108, 246, .24); }
.tag.grey { background: var(--surface-2); color: var(--ink-3); }
.tag.solid-red { background: linear-gradient(135deg, #ff6a5a, #d92b2b); color: #fff; border-color: transparent; }

/* ============================ 筛选条 ============================ */
.filters { display: flex; gap: 10px; flex-wrap: wrap; align-items: flex-end; }
.filters .field { min-width: 150px; }
.filters .field.grow { flex: 1 1 220px; }
.check { display: inline-flex; align-items: center; gap: 7px; height: 40px; font-size: 13px; color: var(--ink-2); cursor: pointer; user-select: none; }
.check input { width: 16px; height: 16px; accent-color: var(--brand); }

/* ============================ 表格 ============================ */
.table-wrap { overflow-x: auto; }
table.tbl { width: 100%; border-collapse: collapse; font-size: 13px; }
table.tbl th, table.tbl td { padding: 10px 12px; text-align: left; border-bottom: 1px solid var(--line); vertical-align: middle; white-space: nowrap; }
table.tbl thead th { background: var(--surface-2); color: var(--ink-2); font-weight: 700; font-size: 12px; position: sticky; top: 0; z-index: 2; }
table.tbl tbody tr:hover { background: #fbfdff; }
table.tbl td.num, table.tbl th.num { font-family: var(--font-num); }
table.tbl td.wrap { white-space: normal; max-width: 320px; }
table.tbl tr.pending-row { background: #fffaf0; }

/* 台账表格：列宽与行高**固定**，超长内容一律省略号（hover 有 title 看全文）。
   这样任何一格灌进长文本（例如误导入 xlsx 产生的乱码）都不会把表格挤变形。 */
@media (min-width: 761px) {
  table.tbl.ledger-tbl { table-layout: fixed; min-width: 1358px; }
  table.tbl.ledger-tbl th, table.tbl.ledger-tbl td { overflow: hidden; text-overflow: ellipsis; }
  /* 行高写 60px：这是"两行内容（类型 + 其他类型）"的高度，单行内容的行也会被撑到同一高度，
     于是整张表**每行等高**。写 54 的话两行内容的行会自己长到 60，行高就不齐了。 */
  table.tbl.ledger-tbl tbody tr { height: 60px; }
  /* 操作列例外：按钮必须一行排开，不能被压缩换行把行高顶起来 */
  table.tbl.ledger-tbl td.ops { overflow: visible; }
  table.tbl.ledger-tbl td.ops .btn-row { flex-wrap: nowrap; }
}
.empty { padding: 34px 16px; text-align: center; color: var(--ink-3); font-size: 13px; }
.mono { font-family: var(--font-num); font-weight: 650; }

/* ============================ 库位图 ============================ */
.legend { display: flex; gap: 14px; flex-wrap: wrap; align-items: center; font-size: 12.5px; color: var(--ink-2); }
.legend i { display: inline-block; width: 16px; height: 16px; border-radius: 5px; margin-right: 6px; vertical-align: -3px; }
.legend .lg-used { background: linear-gradient(160deg, #ff6a5a, #c62222); }
.legend .lg-free { background: linear-gradient(160deg, #4a7bff, #1f4fd8); }
.legend .lg-warn { background: linear-gradient(160deg, #ffc247, #f59e0b); }

.zone-tabs { display: flex; gap: 8px; flex-wrap: wrap; }
.zone-tab { border: 1px solid var(--line); background: var(--surface); border-radius: 999px; height: 32px; padding: 0 15px; font-size: 13px; font-weight: 650; color: var(--ink-2); cursor: pointer; transition: .15s; }
.zone-tab:hover { border-color: var(--line-strong); }
.zone-tab.on { background: linear-gradient(135deg, #4a7bff, #2550e0); color: #fff; border-color: transparent; box-shadow: 0 8px 18px -10px rgba(37, 80, 224, .9); }
.zone-add { border-style: dashed; border-color: rgba(59, 108, 255, .55); color: var(--brand-deep); background: var(--brand-soft); }
.zone-add:hover { background: #dfe8ff; }

.map-block { margin-top: 16px; }
.map-zone-title { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 13.5px; font-weight: 750; margin: 0 0 10px; }
.map-zone-name { white-space: nowrap; font-weight: 800; font-size: 14px; }
.map-hint { display: none; margin: 0 0 8px; font-size: 12px; color: var(--ink-3); }
.map-pad { height: 32px; }

/* 一层 = 一个 layer-block：左侧层名（A1/A2/A3），右侧一整排方块（A1 的 001–050、A2 的 051–100…）。
   列数由前端按该分区的每层位数写成内联 style，所以这里只在缺内联样式时兜底。
   层与层之间留大间距，避免连着看视觉疲劳。 */
.layer-list { display: grid; gap: 22px; }
.layer-block { display: grid; grid-template-columns: 44px minmax(0, 1fr); align-items: center; gap: 10px; }
.layer-name {
  font-family: var(--font-num); font-weight: 800; font-size: 14px; letter-spacing: -.02em;
  color: var(--brand-deep); text-align: center; padding: 6px 0; border-radius: var(--radius-xs);
  background: var(--brand-soft); border: 1px solid rgba(59, 108, 255, .2);
}
/* 列数 = 该分区的「每层位数」（前端写成 --cols），用 1fr 均分，一层正好铺满一行、不横滑。
   container-type 让方块能用 cqw 按「自己所在的这一排有多宽」定字号（见 .slot） */
.layer-grid { display: grid; grid-template-columns: repeat(var(--cols, 50), minmax(0, 1fr)); gap: 4px; container-type: inline-size; }
.map-rowlabel { font-size: 11.5px; color: var(--ink-3); display: flex; align-items: center; justify-content: flex-end; padding-right: 8px; white-space: nowrap; height: 34px; }
.map-collabel { font-size: 11px; color: var(--ink-4); display: grid; place-items: center; height: 18px; font-family: var(--font-num); }

/* 正方形库位：aspect-ratio 保证是正方形，列宽由 1fr 自适应 →
   50 个一排正好铺满、不换行也不横滑。字号用 vw 微调：大屏大一点、小屏自动缩，避免 3 位数溢出方块 */
.slot {
  aspect-ratio: 1 / 1; border-radius: 5px; border: 0; cursor: pointer;
  color: #fff; font-family: var(--font-num); font-weight: 700;
  /* 先给不支持容器查询的浏览器一个兜底，再用 cqw 按这一排的实际宽度定字号：
     格子占整排的 ~1/50（≈2cqw），三位数字约需 1.8 倍字号，所以 0.9cqw 能保证数字永远在方块内 */
  font-size: clamp(7.5px, 0.62vw, 10.5px);
  font-size: min(10.5px, 0.9cqw);
  letter-spacing: -.04em; font-variant-numeric: tabular-nums;
  display: grid; place-items: center; position: relative; padding: 0; overflow: hidden;
  transition: transform .12s, box-shadow .12s; white-space: nowrap;
}
.slot.free { background: linear-gradient(160deg, #4a7bff, #1f4fd8); box-shadow: inset 0 -2px 0 rgba(0, 0, 0, .12); }
.slot.used { background: linear-gradient(160deg, #ff6a5a, #c62222); box-shadow: inset 0 -2px 0 rgba(0, 0, 0, .16); }
.slot:hover { transform: translateY(-2px); box-shadow: var(--shadow-2); }
.slot .cnt { position: absolute; bottom: 1px; right: 2px; font-size: 8.5px; font-weight: 800; opacity: .92; line-height: 1; }
.slot .wn { position: absolute; bottom: 2px; left: 2px; width: 6px; height: 6px; border-radius: 50%; background: #ffd452; box-shadow: 0 0 0 1.5px rgba(0, 0, 0, .28); }
.slot .wn.red { background: #ffe9a8; }
.slot.sel { outline: 3px solid rgba(37, 80, 224, .45); outline-offset: 1px; }

/* ---------------- 库位图放大查看（选电影票式） ---------------- */
.map-area { position: relative; cursor: zoom-in; }
/* 「放大查看」按钮独立一行、靠右放在图区**下面**（不再绝对定位压在图上盖住库位） */
.map-actions { display: flex; justify-content: flex-end; margin-top: 12px; }
.map-zoombtn {
  border: 1px solid var(--line-2); background: var(--surface); color: var(--ink-2);
  border-radius: 999px; padding: 5px 13px; font-size: 12.5px; cursor: pointer;
  box-shadow: 0 1px 4px rgba(15, 30, 70, .08);
}
.map-zoombtn:hover { border-color: var(--brand); color: var(--brand-deep); }

/* 放大窗口 = 一张**贴住内容**的卡片（不是铺满全屏），
   所以不会出现大片空白；卡片外的页面背景做透明模糊（毛玻璃），看起来就是"聚焦到这张图上"。
   卡片高度由内容决定（JS 按画布实际高度设定 viewport 高度），宽度最多 1500px / 96vw。 */
.zoom-mask {
  z-index: 190; display: grid; place-items: center; padding: 16px; overflow: auto;
  background: rgba(12, 22, 44, .38);
  -webkit-backdrop-filter: blur(8px) saturate(1.1);
  backdrop-filter: blur(8px) saturate(1.1);
}
.zoom-panel {
  position: relative; display: flex; flex-direction: column; width: min(1500px, 96vw);
  max-height: 88dvh; overflow: hidden; background: var(--surface);
  border-radius: 14px; box-shadow: 0 24px 64px rgba(9, 18, 36, .34);
}
.zoom-head {
  display: flex; align-items: center; gap: 10px; padding: 10px 58px 10px 14px;
  border-bottom: 1px solid var(--line);
}
.zoom-title { font-size: 14px; font-weight: 700; color: var(--ink); }
.zoom-tip { margin-left: 8px; font-size: 12px; font-weight: 400; color: var(--ink-3); }
/* X 钉在**卡片**右上角：它不在被缩放/平移的那一层里（那层是 #zoom-canvas），
   所以放大、缩小、拖到哪儿，X 都停在卡片右上角随时能点 */
.zoom-close {
  position: absolute; top: 8px; right: 10px; z-index: 3;
  width: 40px; height: 40px; border: 0; border-radius: 10px; cursor: pointer;
  background: var(--surface-2); color: var(--ink-2); font-size: 17px; line-height: 1;
  touch-action: manipulation;
}
.zoom-close:hover { background: #e6eaf3; color: var(--ink); }
.zoom-viewport {
  position: relative; overflow: hidden; touch-action: none; cursor: grab;
  background: #fff; height: 200px; /* 打开时由 JS 按内容高度重设，避免大片留白 */
}
.zoom-viewport.grabbing { cursor: grabbing; }
.zoom-canvas { transform-origin: 0 0; will-change: transform; padding: 6px 10px; width: max-content; }
/* 放大窗口里的方块用固定尺寸（--cell 由 JS 按卡片宽度算好），层名列比页面里宽一点 */
.zoom-canvas .layer-block { grid-template-columns: 46px minmax(0, 1fr); gap: 8px; }
.zoom-canvas .layer-list { gap: 14px; }
.zoom-canvas .layer-grid { grid-template-columns: repeat(var(--cols, 50), var(--cell, 26px)); gap: 4px; }
/* 放大窗口里的字号按方块尺寸成比例（--cell × 0.48），这样缩放后数字与方块始终同比例：
   既不会"字比方块还大、挤到相邻方块上"，也能把可用宽度用足（三位数字约占 0.8 倍格宽）。 */
.zoom-canvas .slot { font-size: calc(var(--cell, 26px) * 0.48); border-radius: 5px; overflow: visible; }
.zoom-canvas .slot .cnt, .zoom-canvas .slot .wn { display: block; }
/* 放大窗口里的分区标题压紧：它属于内容，但若占掉几十像素，视觉上会像"上面的留白更大" */
.zoom-canvas .map-block { margin-top: 10px; }
.zoom-canvas .map-block:first-child { margin-top: 0; }
.zoom-canvas .map-zone-title { font-size: 12.5px; margin: 0 0 5px; }
.zoom-canvas .map-zone-title .tag { font-size: 11px; }
.zoom-bar {
  display: flex; align-items: center; gap: 8px; padding: 8px 12px;
  border-top: 1px solid var(--line); background: var(--surface-2); color: var(--ink-2); flex-wrap: wrap;
}
.zoom-pct { font-family: var(--font-num); font-size: 12.5px; min-width: 46px; text-align: center; }
.zoom-legend { margin-left: auto; font-size: 12px; color: var(--ink-3); display: flex; align-items: center; gap: 6px; }
.zoom-legend i { display: inline-block; width: 10px; height: 10px; border-radius: 3px; margin-left: 6px; }
.zoom-legend .lg-used { background: var(--danger, #e5484d); }
.zoom-legend .lg-free { background: #3b6cff; }
.zoom-legend .lg-warn { background: #f5a623; }

/* ---------------- 库位选择器（分区可折叠） ---------------- */
.picker { position: relative; }
.picker-trigger {
  width: 100%; display: flex; align-items: center; justify-content: space-between; gap: 8px;
  height: 38px; padding: 0 11px; border: 1px solid var(--line-2); border-radius: var(--radius-xs);
  background: var(--surface); color: var(--ink); font-size: 13.5px; cursor: pointer; text-align: left;
}
.picker-trigger:hover { border-color: var(--brand); }
.picker-trigger .chev { color: var(--ink-3); font-size: 11px; flex: none; }
.pk-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.picker-panel {
  position: absolute; z-index: 60; left: 0; right: 0; top: calc(100% + 4px);
  max-height: 300px; overflow-y: auto; padding: 6px;
  background: var(--surface); border: 1px solid var(--line-2); border-radius: var(--radius-sm);
  box-shadow: 0 12px 28px rgba(15, 30, 70, .16);
}
.picker-fold {
  width: 100%; display: flex; align-items: center; gap: 7px; padding: 7px 8px;
  border: 0; border-radius: var(--radius-xs); background: var(--brand-soft);
  color: var(--brand-deep); font-size: 12.5px; font-weight: 700; cursor: pointer;
}
.picker-fold:hover { background: #dfe8ff; }
.picker-fold .chev { width: 12px; font-size: 11px; }
.picker-fold .pk-count { margin-left: auto; font-weight: 500; color: var(--ink-3); font-family: var(--font-num); }
.picker-items { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 2px; padding: 3px 0 5px; }
.picker-item {
  display: flex; align-items: center; justify-content: space-between; gap: 6px;
  padding: 6px 8px; border: 0; border-radius: var(--radius-xs); background: transparent;
  color: var(--ink); font-size: 12.5px; font-family: var(--font-num); cursor: pointer; text-align: left;
}
.picker-item:hover { background: var(--surface-2); }
.picker-item.sel { background: var(--brand); color: #fff; }
.picker-item.sel .pi-state { color: rgba(255, 255, 255, .85); }
.picker-item.empty { font-family: inherit; font-weight: 600; margin-bottom: 4px; border: 1px dashed var(--line-2); width: 100%; }
.pi-state { font-size: 11px; color: var(--ink-3); flex: none; }
.pi-state.used { color: #c2410c; }

/* ---------------- 属性修改 ---------------- */
.opt-edit { display: flex; align-items: center; gap: 8px; }
.opt-edit .input { max-width: 260px; }
.opt-idx { color: var(--ink-3); font-family: var(--font-num); width: 44px; }
.opt-ops { display: flex; gap: 6px; flex-wrap: wrap; }
.opt-add { display: flex; align-items: center; gap: 8px; margin-top: 12px; }
.opt-add .input { max-width: 260px; }

/* ---------------- 公告栏 ---------------- */
.notice-form { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 10px; align-items: end; margin-bottom: 14px; }
.notice-form .textarea { min-height: 46px; resize: vertical; }
.notice-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }
.notice {
  border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--surface-2);
  padding: 10px 12px; border-left: 3px solid var(--brand);
}
.notice-body { font-size: 13.5px; line-height: 1.6; white-space: pre-wrap; word-break: break-word; color: var(--ink); }
.notice-foot { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-top: 6px; flex-wrap: wrap; }
.notice-meta { font-size: 12px; color: var(--ink-3); font-family: var(--font-num); }

.slot-detail { display: grid; gap: 8px; }
.sd-item { display: flex; align-items: center; gap: 10px; justify-content: space-between; padding: 10px 12px; border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--surface-2); flex-wrap: wrap; }
.sd-item .sd-main { display: grid; gap: 2px; min-width: 150px; }
.sd-item .sd-code { font-family: var(--font-num); font-weight: 700; font-size: 14px; }
.sd-item .sd-meta { font-size: 12px; color: var(--ink-3); display: flex; gap: 8px; flex-wrap: wrap; }

/* ============================ 记录时间线 ============================ */
.flow { display: grid; gap: 2px; }
.flow-item { display: grid; grid-template-columns: 132px 1fr; gap: 12px; padding: 10px 0; border-bottom: 1px dashed var(--line); }
.flow-time { font-family: var(--font-num); font-size: 12px; color: var(--ink-3); }
.flow-main { display: grid; gap: 3px; }
.flow-line { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 13px; }
.flow-note { font-size: 12px; color: var(--ink-3); }

/* ============================ 模态框 ============================ */
.modal-mask { position: fixed; inset: 0; z-index: 200; background: rgba(15, 26, 46, .42); backdrop-filter: blur(3px); display: grid; place-items: center; padding: 18px; overflow-y: auto; }
.modal { width: min(760px, 96vw); max-height: 92dvh; display: flex; flex-direction: column; background: var(--surface); border-radius: 20px; box-shadow: var(--shadow-pop); overflow: hidden; }
.modal-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; padding: 16px 18px; border-bottom: 1px solid var(--line); }
.modal-title { margin: 0; font-size: 16px; font-weight: 750; }
.modal-sub { margin: 3px 0 0; font-size: 12.5px; color: var(--ink-3); }
.modal-x { border: 0; background: var(--surface-2); width: 30px; height: 30px; border-radius: 9px; cursor: pointer; font-size: 16px; color: var(--ink-3); line-height: 1; }
.modal-x:hover { background: var(--line); color: var(--ink); }
.modal-body { padding: 16px 18px; overflow-y: auto; }
.modal-foot { display: flex; align-items: center; justify-content: flex-end; gap: 9px; padding: 13px 18px; border-top: 1px solid var(--line); background: var(--surface-2); flex-wrap: wrap; }
.form-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
.form-grid .full { grid-column: 1 / -1; }
/* 「下拉 + 手写」成对：左边选常用值，右边可以直接手写更具体的值（手写优先） */
.pair { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 8px; }
/* 字段下方的说明文字（例如"改代号会把库位号前缀换掉"），小一号、灰一点 */
.field .hint { margin-top: 5px; font-size: 11.5px; line-height: 1.5; color: var(--ink-3); }
/* 类型列的第二行（其他类型）：明显小于主值，避免两类属性被看成同一层 */
.cell-sub { margin-top: 2px; font-size: 11.5px; color: var(--ink-3); }
.notice { border: 1px solid var(--line); border-left: 3px solid var(--brand); border-radius: var(--radius-xs); background: var(--surface-2); padding: 10px 13px; font-size: 12.5px; color: var(--ink-2); line-height: 1.75; }
.notice.amber { border-left-color: var(--amber); background: var(--amber-soft); color: #8a5700; }
.notice.red { border-left-color: var(--danger); background: var(--danger-soft); color: #a52222; }
.notice.teal { border-left-color: var(--teal); background: var(--teal-soft); color: #0b6f66; }

/* ============================ Toast ============================ */
.toast-root { position: fixed; z-index: 400; left: 50%; transform: translateX(-50%); bottom: 26px; display: grid; gap: 8px; justify-items: center; pointer-events: none; }
.toast { padding: 10px 16px; border-radius: 999px; font-size: 13px; font-weight: 650; color: #fff; background: rgba(15, 26, 46, .92); box-shadow: var(--shadow-2); animation: tin .22s ease; max-width: 88vw; }
.toast.ok { background: linear-gradient(135deg, #1fbf6b, #12864a); }
.toast.err { background: linear-gradient(135deg, #ff6a5a, #c62222); }
@keyframes tin { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }

/* ============================ 规则说明 ============================ */
.rule-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 12px; }
.rule { border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--surface-2); padding: 13px 15px; }
.rule h4 { margin: 0 0 6px; font-size: 13px; font-weight: 750; display: flex; align-items: center; gap: 7px; }
.rule p { margin: 0; font-size: 12.5px; color: var(--ink-2); line-height: 1.8; }
.rule ul { margin: 4px 0 0; padding-left: 17px; font-size: 12.5px; color: var(--ink-2); line-height: 1.85; }

.pager { display: flex; align-items: center; gap: 10px; justify-content: flex-end; font-size: 12.5px; color: var(--ink-3); padding-top: 12px; }

/* 台账多选删除（按钮与「批量导入 / 盘点」同排，样式一致） */
th.pickcell, td.pickcell { width: 44px; padding-right: 0; }
input.pick, #pick-all { width: 17px; height: 17px; accent-color: var(--brand); cursor: pointer; vertical-align: middle; }

/* ============================ 响应式 ============================ */
@media (max-width: 1080px) {
  .login-card { grid-template-columns: 1fr; }
  .login-side { padding: 26px 22px; }
  .login-foot { display: none; }
  .login-form { padding: 26px 22px 30px; }
}
@media (max-width: 900px) {
  .page { padding: 16px 14px 80px; }
  .topnav { height: auto; min-height: var(--nav-h); padding: 10px 14px; flex-wrap: wrap; gap: 10px; }
  .brand-sub { display: none; }
  .menu { order: 3; flex-basis: 100%; }
  .nav-right { margin-left: auto; }
}
@media (max-width: 760px) {
  /* 移动端：KPI 与库位图紧凑化，优先把库位图放到首屏可见 */
  .kpi-grid { gap: 8px; grid-template-columns: repeat(auto-fit, minmax(126px, 1fr)); }
  .kpi { padding: 9px 10px; border-radius: var(--radius-xs); }
  .kpi .k-label { font-size: 11px; gap: 4px; }
  .kpi .k-val { font-size: 18px; margin-top: 1px; }
  .kpi .k-unit { font-size: 11px; }
  .map-hint { display: block; }
  .map-zone-title { gap: 6px; font-size: 13px; }
  .map-zone-name { font-size: 13.5px; }
  /* 移动端：与电脑端一致 —— 一层一排 50 个方块铺满，**一屏看完全部库位、不用滑动**。
     方块会缩到像素级（390px 宽时每格约 4px），此时编号必然放不下，所以字体归零：
     这一屏的作用就是"一眼看清红/蓝分布"，具体编号点方块弹窗（或点图区放大查看）看。 */
  .layer-block { grid-template-columns: 26px minmax(0, 1fr); align-items: center; gap: 5px; }
  .layer-name { justify-self: center; width: 24px; font-size: 9.5px; padding: 3px 0; }
  /* 间隙收到 1px：390px 宽下 50 个方块只有 ~300px 可用，2px 的缝会占掉 1/3 宽度、
     方块被挤到 4px 反而看不清；1px 缝能让方块拿到 ~5px */
  .layer-grid { grid-template-columns: repeat(var(--cols, 50), minmax(0, 1fr)); gap: 1px; }
  .slot { font-size: 0; border-radius: 1.5px; min-width: 0; }
  .slot .cnt, .slot .wn { display: none; }   /* 4px 的方块上画角标只会糊成一团 */
  .layer-list { gap: 14px; }
  .map-zone-title { gap: 6px; font-size: 12.5px; }
  .map-hint { font-size: 12px; }
  /* 数据表转卡片：窄屏可读性优先 */
  table.tbl.responsive thead { display: none; }
  table.tbl.responsive, table.tbl.responsive tbody, table.tbl.responsive tr, table.tbl.responsive td { display: block; width: 100%; }
  table.tbl.responsive tr { border: 1px solid var(--line); border-radius: var(--radius-sm); margin-bottom: 10px; padding: 6px 2px; background: var(--surface); box-shadow: var(--shadow-1); }
  table.tbl.responsive td { display: flex; gap: 10px; align-items: flex-start; border: 0; padding: 6px 12px; white-space: normal; }
  table.tbl.responsive td::before { content: attr(data-label); flex: 0 0 88px; color: var(--ink-3); font-size: 12px; font-weight: 600; }
  table.tbl.responsive td.wrap { max-width: none; }
  .form-grid { grid-template-columns: 1fr; }
  /* 窄屏下「下拉 + 手写」改为上下排列，否则两个控件各占一半宽度后都看不清内容 */
  .pair { grid-template-columns: 1fr; }
  .filters .field, .filters .field.grow { flex: 1 1 100%; min-width: 0; }
  .flow-item { grid-template-columns: 1fr; gap: 4px; }
  .sd-item { align-items: flex-start; }
  .modal { width: 100%; max-height: 94dvh; border-radius: 16px; }
  .card-head { padding: 13px 14px; }
  .card-body { padding: 13px 14px; }
  /* 窄屏：发布按钮单独一行，否则输入框会被压得只剩几个字宽 */
  .notice-form { grid-template-columns: 1fr; align-items: stretch; }
  .notice-form .btn { width: 100%; }
  /* 库位选择面板贴到容器两侧，避免在窄屏超出屏幕 */
  .picker-panel { left: 0; right: 0; max-height: 44dvh; }
  .picker-items { grid-template-columns: 1fr; }
  .opt-edit { flex-wrap: wrap; }
  .opt-edit .input, .opt-add .input { max-width: none; flex: 1 1 140px; }
}
@media (max-width: 480px) {
  .page-title { font-size: 17px; }
  .kpi .k-val { font-size: 22px; }
  /* 这里**不再**覆盖库位图：方块的列数与字号统一由 760px 那段决定
     （一层一排 50 个、1fr 均分、编号隐藏）。曾经在这里写死 22px + 8.5px 字号，
     结果把"一屏看全"又变回了横滑，是一次真回归。 */
  .map-actions { margin-top: 10px; }
  .map-zoombtn { padding: 6px 14px; font-size: 13px; }   /* 手机端按钮做大一点，好点 */
}

/* ============================ 批量导入结果 / 导入记录 ============================ *
 * 导入完成后**不关弹窗**，在弹窗里逐行交代：成功几条、报错几条、每条为什么。
 * 用色与下载的结果表（淡绿/淡红/深红）一致，让"屏幕上看"和"表里看"是同一套语言。 */
.im-rule { color: var(--danger); }
.im-panel { margin-top: 12px; display: grid; gap: 8px; }
.im-sum { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.im-list {
  display: grid; gap: 0; max-height: 46dvh; overflow: auto;
  border: 1px solid var(--line); border-radius: var(--radius-xs); background: var(--surface);
}
/* 每条一小行：行号 / 刀版号 / 动作 / 原因。整行左边缘一条色条，成功绿、失败红 */
.im-line {
  display: flex; gap: 10px; align-items: baseline; padding: 6px 10px;
  border-left: 3px solid var(--line-strong); border-bottom: 1px solid var(--line);
  font-size: 12.5px; line-height: 1.5;
}
.im-line:last-child { border-bottom: 0; }
.im-line.ok { border-left-color: var(--green); background: rgba(22, 163, 74, .05); }
.im-line.bad { border-left-color: var(--danger); background: rgba(239, 63, 63, .06); }
.im-no { flex: 0 0 58px; color: var(--ink-3); font-size: 12px; font-weight: 650; }
/* 刀版号固定不缩：否则旁边原因一长，它就被挤成 "DB-40…"（看着像数据出了问题） */
.im-code { flex: 0 0 auto; max-width: 150px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-family: var(--font-num); font-weight: 650; }
.im-tag { flex: 0 0 auto; font-size: 11.5px; font-weight: 700; }
.im-line.ok .im-tag { color: #12703c; }
.im-line.bad .im-tag { color: var(--danger-deep, #b32222); }
.im-why { flex: 1 1 auto; min-width: 0; word-break: break-word; }
.im-line.bad .im-why { color: var(--danger-deep, #b32222); font-weight: 600; }
.im-foot { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
/* 图例：与下载表里的三种颜色一一对应（淡绿 / 淡红 / 深红） */
.im-legend.ok { color: #12703c; }
.im-legend.bad { color: #b32222; }
.im-legend.deep { color: #fff; background: #c00000; padding: 1px 6px; border-radius: 4px; }
.ir-tbl td.num { white-space: nowrap; }
@media (max-width: 760px) {
  .im-no { flex: 0 0 50px; }
  .im-code { max-width: 130px; }
}

/* ============================ 网站备案信息（页脚 / 登录页底部） ============================ *
 * 法定展示要求：ICP 备案号（链接工信部）+ 公安联网备案号（连同图标）。
 * 值来自 config.json，留空则整块不渲染（filingLinksHtml 返回空串时不生成元素）。 */
.login-beian { margin-top: 6px; font-size: 11.5px; color: rgba(255, 255, 255, .82); display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.login-beian a { color: rgba(255, 255, 255, .95); text-decoration: none; border-bottom: 1px solid rgba(255, 255, 255, .38); }
.login-beian a:hover { border-bottom-color: rgba(255, 255, 255, .9); }
.site-beian {
  margin-top: 26px; padding-top: 14px; border-top: 1px solid var(--line);
  display: flex; flex-wrap: wrap; gap: 8px; align-items: center; justify-content: center;
  font-size: 12px; color: var(--ink-3);
}
.site-beian a { color: var(--ink-3); text-decoration: none; }
.site-beian a:hover { color: var(--brand, #3b6cff); text-decoration: underline; }
.site-beian img { vertical-align: -2px; margin-right: 4px; }
.bf-sep { color: var(--line-strong); margin: 0 2px; }
.login-side .bf-sep { color: rgba(255, 255, 255, .45); }
@media (max-width: 480px) {
  .site-beian { font-size: 11.5px; margin-top: 18px; }
}
