/* ==========================================================================
   CardVault · 卡库资源管理平台 · 玻璃拟态企业管理后台
   设计：浅白淡蓝渐变通透背景 / 液态毛玻璃 / 大圆角 / 细微白描边
   ========================================================================== */

/* ---------- 设计令牌 ---------- */
:root {
  /* 品牌色 */
  --brand: #2563eb;
  --brand-2: #3b82f6;
  --brand-3: #60a5fa;
  --brand-soft: #dbeafe;
  --cyan: #06b6d4;
  --violet: #8b5cf6;
  --emerald: #10b981;
  --amber: #f59e0b;
  --rose: #f43f5e;

  /* 文本 */
  --text-1: #0f172a;
  --text-2: #334155;
  --text-3: #64748b;
  --text-4: #64748b;   /* 原 #94a3b8 过浅：占位符/副标题/坐标轴等全局偏淡，统一加深 */

  /* 玻璃卡片（第二级：比组件面更白，用来把卡片从组件里「托」起来） */
  --glass-bg: rgba(255, 255, 255, 0.72);
  --glass-bg-strong: rgba(255, 255, 255, 0.86);
  --glass-border: rgba(255, 255, 255, 0.85);
  --glass-blur: 22px;
  --glass-shadow: 0 8px 32px rgba(31, 38, 135, 0.06);
  --glass-shadow-hover: 0 16px 48px rgba(31, 38, 135, 0.12);

  /* 组件面（第三级）：输入框 / 下拉 / 分页按钮 / 结果行 / 标签块统一用这一档。
     原来是 rgba(255,255,255,.6~.75)，叠在玻璃卡片上等于白上白，边界全丢；
     改成带蓝调的半透明面 + 可见描边，三级层次才立得住。 */
  --surface: rgba(214, 231, 250, 0.62);
  --surface-strong: rgba(203, 224, 249, 0.82);
  --surface-flat: #eaf1fb;                 /* 需要不透明的地方（弹窗内） */
  --surface-line: rgba(37, 99, 235, 0.16);
  --surface-line-2: rgba(37, 99, 235, 0.32);

  /* 弹窗面：弹窗是「聚焦面」，背后压着一层深色遮罩，
     如果沿用 --glass-bg（.72）会被遮罩透上来变成灰面（实测 #dadbe1）。
     所以单独给一档近乎不透明、带冷调的浅色，既干净又仍能与内部组件拉开层次。 */
  --modal-bg: rgba(255, 255, 255, 0.94);

  /* 圆角 */
  --r-sm: 12px;
  --r-md: 16px;
  --r-lg: 22px;
  --r-xl: 28px;

  /* 布局 */
  --sidebar-w: 248px;
  --topbar-h: 72px;
}

/* ---------- 基础 ---------- */
* { margin: 0; padding: 0; box-sizing: border-box; }

html, body { height: 100%; }

body {
  font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI',
    'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif;
  color: var(--text-1);
  background: #eef4fb;
  font-size: 14px;
  line-height: 1.5;
  overflow: hidden;
  -webkit-font-smoothing: antialiased;
  letter-spacing: -0.01em;
}

/* ---------- 通透渐变背景 + 光斑 ---------- */
.bg-layer {
  position: fixed;
  inset: 0;
  z-index: 0;
  background:
    radial-gradient(1100px 700px at 8% 0%, #dbeafe 0%, transparent 55%),
    radial-gradient(900px 600px at 92% 8%, #e0f2fe 0%, transparent 50%),
    radial-gradient(800px 800px at 50% 100%, #f0f9ff 0%, transparent 60%),
    linear-gradient(160deg, #f7fbff 0%, #eaf2fb 45%, #e6eefb 100%);
}
.bg-layer::before,
.bg-layer::after {
  content: '';
  position: absolute;
  border-radius: 50%;
  filter: blur(90px);
  opacity: 0.55;
}
.bg-layer::before {
  width: 520px; height: 520px;
  background: radial-gradient(circle, rgba(96,165,250,0.45), transparent 70%);
  top: -140px; left: 12%;
  animation: float1 22s ease-in-out infinite;
}
.bg-layer::after {
  width: 460px; height: 460px;
  background: radial-gradient(circle, rgba(139,92,246,0.32), transparent 70%);
  bottom: -160px; right: 8%;
  animation: float2 26s ease-in-out infinite;
}
@keyframes float1 {
  0%, 100% { transform: translate(0, 0) scale(1); }
  50% { transform: translate(60px, 40px) scale(1.08); }
}
@keyframes float2 {
  0%, 100% { transform: translate(0, 0) scale(1); }
  50% { transform: translate(-50px, -30px) scale(1.12); }
}

/* 细网格纹理 */
.bg-grid {
  position: fixed;
  inset: 0;
  z-index: 0;
  background-image:
    linear-gradient(rgba(37,99,235,0.035) 1px, transparent 1px),
    linear-gradient(90deg, rgba(37,99,235,0.035) 1px, transparent 1px);
  background-size: 48px 48px;
  mask-image: radial-gradient(ellipse at 50% 40%, #000 30%, transparent 78%);
  -webkit-mask-image: radial-gradient(ellipse at 50% 40%, #000 30%, transparent 78%);
  pointer-events: none;
}

/* ---------- 布局骨架 ---------- */
.app { position: relative; z-index: 1; display: flex; height: 100vh; }

/* ---------- 侧边栏 ---------- */
.sidebar {
  width: var(--sidebar-w);
  flex-shrink: 0;
  height: 100%;
  padding: 20px 16px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  background: var(--glass-bg);
  backdrop-filter: blur(var(--glass-blur)) saturate(180%);
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(180%);
  border-right: 1px solid var(--glass-border);
  box-shadow: 4px 0 24px rgba(31, 38, 135, 0.05);
}

.logo {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 6px 10px 20px;
}
.logo-badge {
  width: 40px; height: 40px;
  border-radius: 13px;
  display: grid; place-items: center;
  background: linear-gradient(135deg, var(--brand-2), var(--brand));
  box-shadow: 0 6px 18px rgba(37, 99, 235, 0.32);
  flex-shrink: 0;
}
.logo-badge svg { width: 22px; height: 22px; color: #fff; }
.logo-text b { display: block; font-size: 15px; font-weight: 700; color: var(--text-1); letter-spacing: -0.02em; }
.logo-text span { font-size: 11px; color: var(--text-3); letter-spacing: 0.06em; }

.nav-group {
  font-size: 10.5px;
  font-weight: 600;
  color: var(--text-4);
  letter-spacing: 0.1em;
  padding: 14px 12px 6px;
  text-transform: uppercase;
}

.nav-item {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 10px 12px;
  border-radius: var(--r-sm);
  color: var(--text-2);
  font-size: 13.5px;
  font-weight: 500;
  cursor: pointer;
  border: 1px solid transparent;
  transition: all .22s cubic-bezier(.4,0,.2,1);
  position: relative;
  user-select: none;
}
.nav-item svg { width: 18px; height: 18px; flex-shrink: 0; opacity: .75; }
.nav-item:hover { background: rgba(255,255,255,.6); color: var(--text-1); }
.nav-item.active {
  background: linear-gradient(135deg, rgba(37,99,235,.95), rgba(59,130,246,.85));
  color: #fff;
  box-shadow: 0 6px 18px rgba(37, 99, 235, .28);
  border-color: rgba(255,255,255,.35);
}
.nav-item.active svg { opacity: 1; }
/* （原「导入中心未读徽标」样式已随功能移除） */

.sidebar-foot {
  margin-top: auto;
  padding: 12px;
  border-radius: var(--r-md);
  background: linear-gradient(135deg, rgba(219,234,254,.8), rgba(224,242,254,.6));
  border: 1px solid rgba(255,255,255,.8);
}
/* 3 行 + 无进度条。分页条底边要和侧边栏底部对齐（browser-test 有断言）：
   行数变多会把侧边栏撑溢出、对齐就没了 —— 加行前先看这条约束。 */
.sidebar-foot .foot-row { display: flex; justify-content: space-between; font-size: 12px; color: var(--text-2); padding: 3px 0; }
.sidebar-foot .foot-row b { color: var(--brand); font-weight: 700; }

/* ---------- 主区 ---------- */
.main { flex: 1; display: flex; flex-direction: column; min-width: 0; height: 100%; }

/* 顶栏
   注意：顶栏建立了独立层叠上下文（position + z-index），否则顶栏内的账户弹出层
   会被后面同样带 backdrop-filter 的 .glass 卡片盖住。
   层级：内容卡片(0) < 顶栏(50) < toast(200) < 自定义下拉(600) < 弹窗(9999)。
   背景：原来用半透明白（叠在页面底色上约 rgb(247,250,253)），跟内容区几乎分不出来；
   现在改成明显加深的淡蓝渐变 + 品牌色细分隔线，顶栏边界一眼可见。 */
.topbar {
  height: var(--topbar-h);
  flex-shrink: 0;
  position: relative;
  z-index: 50;
  padding: 0 28px;
  display: flex;
  align-items: center;
  gap: 16px;
  background: linear-gradient(180deg, #e9f2fe 0%, #d3e4f8 100%);
  border-bottom: 1px solid rgba(37, 99, 235, 0.18);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.9), 0 8px 24px rgba(31, 38, 135, 0.07);
}
.page-title h1 { font-size: 19px; font-weight: 700; letter-spacing: -0.02em; }
.page-title p { font-size: 12px; color: var(--text-3); margin-top: 1px; }

/* 顶部全局搜索框已移除 —— 母卡管理 / 活卡管理 各自有表内搜索，
   顶栏再放一个「搜索卡片」的框没有意义（它只搜卡库表）。
   右侧控件靠第一个图标按钮的 margin-left:auto 贴右。 */
.topbar > .icon-btn:first-of-type { margin-left: auto; }

.icon-btn {
  width: 40px; height: 40px;
  border-radius: 50%;
  display: grid; place-items: center;
  border: 1px solid var(--glass-border);
  background: rgba(255,255,255,.6);
  backdrop-filter: blur(8px);
  cursor: pointer;
  transition: all .2s;
  position: relative;
  flex-shrink: 0;
}
.icon-btn:hover { background: #fff; box-shadow: var(--glass-shadow); transform: translateY(-1px); }
.icon-btn svg { width: 18px; height: 18px; color: var(--text-2); }
.icon-btn .dot { position: absolute; top: 8px; right: 9px; width: 7px; height: 7px; border-radius: 50%; background: var(--rose); border: 1.5px solid #fff; }

.btn {
  height: 40px;
  padding: 0 18px;
  border-radius: 20px;
  border: none;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  transition: all .22s cubic-bezier(.4,0,.2,1);
  white-space: nowrap;
}
.btn svg { width: 16px; height: 16px; }
.btn-primary {
  background: linear-gradient(135deg, var(--brand-2), var(--brand));
  color: #fff;
  box-shadow: 0 6px 18px rgba(37,99,235,.3);
}
.btn-primary:hover { transform: translateY(-2px); box-shadow: 0 10px 26px rgba(37,99,235,.38); }
.btn-ghost {
  background: rgba(255,255,255,.6);
  backdrop-filter: blur(8px);
  color: var(--text-2);
  border: 1px solid var(--glass-border);
}
.btn-ghost:hover { background: #fff; color: var(--text-1); box-shadow: var(--glass-shadow); }
.btn-sm { height: 32px; padding: 0 13px; font-size: 12.5px; border-radius: 16px; }

/* ⚠️ 按钮禁用态：以前整个 .btn 系列**没有** :disabled 样式，
   「批量删除」没勾选时虽然 disabled 了，外观跟能点的按钮一模一样 ——
   用户看到的就是「没勾选为什么能按」（实际点了不会触发，但视觉上毫无提示）。
   禁用必须一眼看出来：变淡 + 禁用光标 + hover 不起反应。 */
.btn:disabled, .btn[disabled] {
  opacity: .42;
  cursor: not-allowed;
  box-shadow: none;
  filter: saturate(.5);
}
.btn:disabled:hover, .btn[disabled]:hover {
  background: rgba(255,255,255,.6);
  color: var(--text-2);
  box-shadow: none;
  transform: none;
}
.btn-primary:disabled, .btn-primary[disabled] { background: var(--text-3); }
.btn-primary:disabled:hover, .btn-primary[disabled]:hover {
  background: var(--text-3); transform: none; box-shadow: none;
}

.avatar {
  width: 38px; height: 38px;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--violet), var(--brand-2));
  display: grid; place-items: center;
  color: #fff; font-weight: 700; font-size: 13px;
  border: 2px solid rgba(255,255,255,.9);
  box-shadow: 0 4px 12px rgba(139,92,246,.3);
  flex-shrink: 0;
  cursor: pointer;
}

/* ---------- 内容滚动区 ---------- */
.content {
  flex: 1;
  overflow-y: auto;
  overflow-x: hidden;
  padding: 22px 28px 40px;
}
.content::-webkit-scrollbar { width: 9px; }
.content::-webkit-scrollbar-track { background: transparent; }
.content::-webkit-scrollbar-thumb { background: rgba(37,99,235,.18); border-radius: 9px; border: 2px solid transparent; background-clip: padding-box; }
.content::-webkit-scrollbar-thumb:hover { background: rgba(37,99,235,.32); background-clip: padding-box; }

/* ---------- 表格视图：表格内部滚动，分页条与侧边栏底部持平 ----------
   母卡管理 / 活卡管理 是「表格铺满」的视图。原来表格跟着 .content 一起滚，
   出来的是外部页面滚动条，分页条会被顶到视口外面。现在：
     · .content 自己不滚（overflow:hidden），变成纵向 flex 容器；
     · section 撑满剩余高度，卡片再撑满 section；
     · 卡片里的 .table-scroll 承担纵向滚动（表头 sticky 照常生效）；
     · 分页条 flex:none 钉在卡片底部。
   下内边距压到 20px，和侧边栏 .sidebar 的下内边距一致 ——
   分页条底边与左下角 .sidebar-foot 的底边落在同一条水平线上。 */
.content.table-mode {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  padding-bottom: 20px;
}
.content.table-mode > .view.active {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
}
.content.table-mode > .view.active > .table-card {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  margin-bottom: 0;
}
.content.table-mode > .view.active > .table-card > .table-scroll {
  flex: 1;
  min-height: 0;
  overflow: auto;
}
.content.table-mode > .view.active > .table-card > .pagination { flex: none; }
.content.table-mode .table-scroll::-webkit-scrollbar { width: 9px; height: 9px; }
.content.table-mode .table-scroll::-webkit-scrollbar-track { background: transparent; }
.content.table-mode .table-scroll::-webkit-scrollbar-thumb {
  background: rgba(37,99,235,.18); border-radius: 9px;
  border: 2px solid transparent; background-clip: padding-box;
}
.content.table-mode .table-scroll::-webkit-scrollbar-thumb:hover {
  background: rgba(37,99,235,.32); background-clip: padding-box;
}

/* ---------- 玻璃卡片基类 ---------- */
.glass {
  background: var(--glass-bg);
  backdrop-filter: blur(var(--glass-blur)) saturate(180%);
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(180%);
  border: 1px solid var(--glass-border);
  border-radius: var(--r-lg);
  box-shadow: var(--glass-shadow);
  position: relative;
  overflow: hidden;
}
/* 顶部高光描边 —— 液态玻璃感 */
.glass::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1px;
  background: linear-gradient(135deg, rgba(255,255,255,.95), rgba(255,255,255,.15) 40%, transparent 60%);
  -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  pointer-events: none;
}

.card-head {
  padding: 16px 20px 12px;
  display: flex;
  align-items: center;
  gap: 10px;
}
.card-head h3 { font-size: 14.5px; font-weight: 700; letter-spacing: -0.01em; }
.card-head .sub { font-size: 11.5px; color: var(--text-4); margin-left: auto; }

/* ---------- 指标卡片 ---------- */
.kpi-grid {
  display: grid;
  /* 5 张：卡库总量 / 信用卡 / 借记卡 / 预付卡 / 活卡总数（2026-10-12 补上预付卡）。
     ⚠️ 加卡片必须同步改这里的列数，否则第 5 张会掉到第二行变成孤零零一个。 */
  grid-template-columns: repeat(5, 1fr);
  gap: 16px;
  margin-bottom: 18px;
}
.kpi {
  padding: 18px 20px;
  transition: transform .28s cubic-bezier(.4,0,.2,1), box-shadow .28s;
  cursor: default;
}
.kpi:hover { transform: translateY(-4px); box-shadow: var(--glass-shadow-hover); }
.kpi-top { display: flex; align-items: flex-start; justify-content: space-between; }
.kpi-icon {
  width: 44px; height: 44px;
  border-radius: 14px;
  display: grid; place-items: center;
  flex-shrink: 0;
}
.kpi-icon svg { width: 22px; height: 22px; }
.kpi-trend {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  font-size: 11.5px;
  font-weight: 700;
  padding: 3px 8px;
  border-radius: 20px;
  background: rgba(16,185,129,.12);
  color: #059669;
}
.kpi-trend.down { background: rgba(244,63,94,.12); color: #e11d48; }
.kpi-trend svg { width: 12px; height: 12px; }
.kpi-label { font-size: 12px; color: var(--text-3); margin-top: 16px; font-weight: 500; }
.kpi-value {
  font-size: 27px;
  font-weight: 800;
  letter-spacing: -0.035em;
  margin-top: 3px;
  font-variant-numeric: tabular-nums;
  line-height: 1.15;
}
.kpi-value small { font-size: 14px; font-weight: 600; color: var(--text-3); margin-left: 2px; }
.kpi-bar { height: 4px; border-radius: 4px; background: rgba(15,23,42,.07); margin-top: 12px; overflow: hidden; }
.kpi-bar i { display: block; height: 100%; border-radius: 4px; }

/* ---------- 图表栅格 ---------- */
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-bottom: 16px; }
.grid-3 { display: grid; grid-template-columns: 1.35fr 1fr 1fr; gap: 16px; margin-bottom: 16px; }
.grid-8-4 { display: grid; grid-template-columns: 2fr 1fr; gap: 16px; margin-bottom: 16px; }

.chart-wrap { padding: 8px 12px 16px; }
.chart-box { width: 100%; position: relative; }
.chart-box.h260 { height: 260px; }
.chart-box.h300 { height: 300px; }
.chart-box.h220 { height: 220px; }
.chart-box.h200 { height: 200px; }

/* 图例 */
.legend { display: flex; gap: 14px; padding: 0 20px 6px; flex-wrap: wrap; }
.legend span { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--text-3); }
.legend i { width: 9px; height: 9px; border-radius: 3px; }

/* ---------- 3D 工厂模型 ---------- */
.factory-wrap { position: relative; height: 300px; overflow: hidden; }
.factory-wrap canvas { display: block; width: 100% !important; height: 100% !important; }
.factory-overlay {
  position: absolute;
  left: 20px; bottom: 16px;
  display: flex; gap: 8px; flex-wrap: wrap;
  pointer-events: none; z-index: 2;
}
.factory-chip {
  background: rgba(255,255,255,.75);
  backdrop-filter: blur(10px);
  border: 1px solid rgba(255,255,255,.9);
  border-radius: 20px;
  padding: 5px 11px;
  font-size: 11px;
  font-weight: 600;
  color: var(--text-2);
  display: flex; align-items: center; gap: 6px;
  box-shadow: 0 4px 12px rgba(31,38,135,.08);
}
.factory-chip .pulse {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--emerald);
  box-shadow: 0 0 0 0 rgba(16,185,129,.6);
  animation: pulse 2s infinite;
}
@keyframes pulse {
  0% { box-shadow: 0 0 0 0 rgba(16,185,129,.5); }
  70% { box-shadow: 0 0 0 8px rgba(16,185,129,0); }
  100% { box-shadow: 0 0 0 0 rgba(16,185,129,0); }
}

/* ---------- 表格区 ---------- */
.table-card { margin-bottom: 16px; }
.table-toolbar {
  padding: 14px 20px;
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  border-bottom: 1px solid rgba(255,255,255,.55);
}
.toolbar-search { position: relative; width: 260px; }
.toolbar-search svg { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); width: 15px; height: 15px; color: var(--text-4); }
.toolbar-search input {
  width: 100%; height: 36px;
  padding: 0 12px 0 34px;
  border-radius: 18px;
  border: 1px solid var(--glass-border);
  background: rgba(255,255,255,.65);
  font-size: 13px; outline: none; color: var(--text-1);
  transition: all .2s;
}
.toolbar-search input:focus { border-color: var(--brand-3); background: #fff; box-shadow: 0 0 0 3px rgba(37,99,235,.1); }

.filter-select {
  height: 36px;
  padding: 0 30px 0 13px;
  border-radius: 18px;
  border: 1px solid var(--glass-border);
  background: rgba(255,255,255,.65) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2364748b' stroke-width='3'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E") no-repeat right 12px center;
  font-size: 13px;
  color: var(--text-2);
  outline: none;
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
  transition: all .2s;
  font-family: inherit;
}
.filter-select:focus { border-color: var(--brand-3); background-color: #fff; }

.toolbar-right { margin-left: auto; display: flex; gap: 8px; align-items: center; }

.table-scroll { overflow-x: auto; }
.table-scroll::-webkit-scrollbar { height: 8px; }
.table-scroll::-webkit-scrollbar-thumb { background: rgba(37,99,235,.2); border-radius: 8px; }

table.data-table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  min-width: 1080px;
}
.data-table thead th {
  position: sticky;
  top: 0;
  /* 表头不用白色底：改成淡蓝灰，和表格内容区分开（原来是 rgba(255,255,255,.85)，
     在玻璃卡片上跟卡片、行底色几乎一样，看着像没画表头） */
  background: #e9f0fa;
  font-size: 11.5px;
  font-weight: 700;
  color: var(--text-3);
  text-align: center;                 /* 表头居中 */
  padding: 12px 16px;
  white-space: nowrap;
  letter-spacing: 0.02em;
  border-bottom: 1px solid rgba(37, 99, 235, 0.13);
  cursor: pointer;
  user-select: none;
  z-index: 2;
}
.data-table thead th:hover { color: var(--brand); }
/* 排序箭头**挂在文字旁边**（2026-10-13 用户要求：不要甩到单元格最右边一大截）。
   做法：th 保持 inline-flex + 居中（这是「表头跟内容居中对齐」的基础，动不得），
   箭头 span 设成 position:relative + width:0 —— 在文档流里不占宽度，
   所以**不参与居中计算**，表头里的文字该怎么居中还怎么居中；
   两个小箭头再用绝对定位挂到文字右侧。
   以前是 position:absolute; right:7px，贴在单元格右边缘，
   窄列（月/年/CVV）里离文字老远。 */
.data-table thead th .sort-ico {
  position: relative;
  width: 0; height: 0;
  margin-left: 6px;
  display: inline-block;
}
.data-table thead th .sort-ico svg {
  position: absolute;
  left: 0;
  display: block; width: 11px; height: 10px; stroke-width: 2.4;
  opacity: .32; color: var(--text-4);
  transition: opacity .18s, color .18s;
}
.data-table thead th .sort-ico .lucide-chevron-up   { top: 50%; margin-top: -10px; }
.data-table thead th .sort-ico .lucide-chevron-down { top: 50%; margin-top: 0; }
.data-table thead th:hover .sort-ico svg { opacity: .75; color: var(--text-2); }
.data-table thead th.sorted { color: var(--brand); }
.data-table thead th.sorted .sort-ico svg { color: var(--brand); opacity: .32; }
/* 方向高亮：按 lucide 名称类精准定位，避免位置选择器误判 */
.data-table thead th.sorted.asc  .sort-ico .lucide-chevron-up   { opacity: 1; }
.data-table thead th.sorted.desc .sort-ico .lucide-chevron-down { opacity: 1; }

.data-table tbody tr {
  transition: background .16s;
  border-bottom: 1px solid rgba(255,255,255,.45);
}
.data-table tbody tr:hover { background: rgba(219,234,254,.35); }
.data-table tbody tr.selected { background: rgba(37,99,235,.08); }
.data-table tbody td {
  padding: 11px 16px;
  font-size: 13px;
  color: var(--text-2);
  white-space: nowrap;
  /* ⚠️ 内容整体居中 —— 跟表头对齐（用户：「表格内容区和表头进行居中对齐了吗，表头！」）。
     单行单元格（卡号 / 状态 / 时间 …）直接居中即可。
     多行 / 组合单元格（头像+两行、中英两行）见下面「定宽块」规则。 */
  text-align: center;
  vertical-align: middle;
}
/* 全站表格排版：**表头居中 + 内容整块居中 + 块内左对齐**（2026-10-12 最终定案）。
   ⚠️ 2026-10-11 账户表曾经「表头和内容完全错位」—— 根因是 thead 少写了「状态」列
      （见 index.html），浏览器强行拉伸整张表，跟对齐方向无关，别再去改方向找原因。
   ⚠️ 账户表不单独覆盖：跟母卡管理 / 活卡 / 国家分表一个样式。

   ⚠️ 账户表额外：table-layout:fixed —— 让「账号/姓名」列固定 220px 不被拉宽，
      其余 6 列 width:auto 均分剩余空间。否则窗口拉宽时这列也变宽，头像和左边界
      距离越来越大，用户看着又像错位。固定列白名单（要小心）：不要把这个 fixed
      推广到其他表，否则发卡行 / 卡号 那些按内容宽的列会被裁切。

   🔑 「表格内容是一个整体」= 每个格子的内容当一个**定宽块**，整块居中，块内左对齐。
      ⚠️ 定宽是硬要求：块宽随内容变（inline-block / shrink-to-fit）会让
         A / O / V 头像在不同行落在不同的 x 上 —— 那就是新的错位。
         定宽 + 居中 ⇒ 每行左边界一致（头像对齐），整块又跟居中表头对上。 */

/* ⚠️ 账户列表「账号 / 姓名」列：头像 +（账号 / 姓名 两行）。
   定宽 168px + margin 居中 ⇒ 三行头像的 x 完全一致；块内两行共用左边界。 */
#accPanelList .data-table tbody td > .acc-user {
  display: flex; align-items: center; gap: 10px;
  /* ⚠️ max-content 只是「测量基准」：块宽 = 内容实际宽，先让内容贴紧不留空档
     （写死 168px 时块居中了、内容却只占左边一小截，实测可见中心偏左 35px）。
     真正的**统一宽度**由 js/app.js 的 alignAccUserBlocks() 在三行里取最大值后写回
     —— CSS 没法跨行取 max，写魔法数字又挡不住长用户名。 */
  width: max-content; margin: 0 auto; text-align: left; justify-content: flex-start;
}
/* 文字区不设 min-width：app.js 里 <div style="min-width:0"> 是内联，本来就压过 CSS，
   留在这里只会误导（曾经写 108px 以为生效，其实一直是 0）。 */
/* ⚠️ 账户表：列宽固定 + 剩余均分。表头在 index.html 已写好（账号 220px 固定、其余 auto）。 */
#accPanelList .data-table { table-layout: fixed; }
.data-table tbody tr:last-child td { border-bottom: none; }

/* 表头里的排序箭头：零宽内联块，行盒宽度不变，文字照旧居中 */
.data-table thead th .sort-ico { vertical-align: middle; }

.cell-id { font-variant-numeric: tabular-nums; color: var(--text-4); font-size: 12px; }
.cardnum-cell { font-family: 'SF Mono', 'JetBrains Mono', Menlo, monospace; font-size: 12.5px; letter-spacing: .01em; color: var(--text-1); font-weight: 500; }

.checkbox {
  width: 17px; height: 17px;
  border-radius: 5px;
  border: 1.8px solid var(--text-4);
  cursor: pointer;
  display: inline-grid;
  place-items: center;
  transition: all .16s;
  background: rgba(255,255,255,.6);
  flex-shrink: 0;
}
.checkbox:hover { border-color: var(--brand-3); }
.checkbox.on { background: var(--brand); border-color: var(--brand); }
.checkbox.on::after { content: ''; width: 9px; height: 5px; border-left: 2px solid #fff; border-bottom: 2px solid #fff; transform: rotate(-45deg) translate(1px,-2px); }
.checkbox.part { background: var(--brand); border-color: var(--brand); }
.checkbox.part::after { content: ''; width: 9px; height: 2px; background: #fff; border-radius: 2px; }

/* 标签 */
.tag {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 11px;
  font-weight: 700;
  padding: 3px 9px;
  border-radius: 20px;
  letter-spacing: .01em;
}
.tag-visa { background: rgba(37,99,235,.12); color: #1d4ed8; }
.tag-mc { background: rgba(249,115,22,.13); color: #ea580c; }
.tag-amex { background: rgba(6,182,212,.14); color: #0891b2; }
.tag-up { background: rgba(16,185,129,.13); color: #059669; }
.tag-jcb { background: rgba(139,92,246,.13); color: #7c3aed; }
.tag-dc { background: rgba(244,63,94,.12); color: #e11d48; }
.tag-other { background: rgba(100,116,139,.12); color: #475569; }
.tag-credit { background: rgba(139,92,246,.12); color: #7c3aed; }
.tag-debit { background: rgba(6,182,212,.12); color: #0891b2; }
/* 预付卡：琥珀色，和借记卡的青色明显区分（三类互斥，不能共用一色） */
.tag-prepaid { background: rgba(245,158,11,.14); color: #b45309; }

.status-dot { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; font-weight: 600; }
.status-dot i { width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0; }
.st-ok { color: #059669; } .st-ok i { background: var(--emerald); box-shadow: 0 0 6px rgba(16,185,129,.5); }
.st-wait { color: #d97706; } .st-wait i { background: var(--amber); }
.st-lock { color: #e11d48; } .st-lock i { background: var(--rose); }
.st-used { color: var(--text-4); } .st-used i { background: var(--text-4); }

.sub-text { font-size: 11px; color: var(--text-4); display: block; margin-top: 1px; }

/* 发卡行 / 等级列：中文在上、英文在下（与「国家」列同一套「上中下英」排版）。
   译名来自内置 BIN 库；查不到译名时只渲染主行（英文），不显示空的副行。
   ⚠️ 同头像问题：块宽 = 内容宽（max-content） + margin 居中 + 块内左对齐。
     之前写死 width:180px 时短名字只剩一截，块是居中、可见内容却偏左 —— 一样的坑。 */
/* ⚠️ 2026-10-12 改：块内也居中。
   之前是「块居中 + 块内左对齐」（账户表「头像+两行」用的同款策略，avatars 对齐要这条），
   但这里不一样 —— 卡库表的「发卡行 / 等级」两列里没有头像，块内多行不需要靠左对齐。
   改成纯「块居中 + 块内也居中」就跟居中表头完全对上了。 */
.i18n-cell {
  width: max-content; max-width: 100%; margin: 0 auto;
  min-width: 0; text-align: center;
}
.i18n-cell .zh {
  display: block; color: var(--text-1);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.i18n-cell .en {
  display: block; font-size: 11px; color: var(--text-4); margin-top: 1px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* 操作按钮：三处 row-actions 全在表格 td 里，跟着内容一起居中（整块当一个整体） */
.row-actions { display: flex; gap: 6px; justify-content: center; }
.act-btn {
  width: 30px; height: 30px;
  border-radius: 9px;
  display: grid; place-items: center;
  border: 1px solid rgba(255,255,255,.7);
  background: rgba(255,255,255,.55);
  cursor: pointer;
  transition: all .18s;
}
.act-btn:hover { background: var(--brand); border-color: var(--brand); }
.act-btn:hover svg { color: #fff; }
.act-btn svg { width: 15px; height: 15px; color: var(--text-3); transition: color .18s; }
.act-btn.danger:hover { background: var(--rose); border-color: var(--rose); }

/* 分页 */
.pagination {
  padding: 14px 20px;
  display: flex;
  align-items: center;
  gap: 10px;
  border-top: 1px solid rgba(255,255,255,.55);
  flex-wrap: wrap;
}
.page-info { font-size: 12.5px; color: var(--text-3); }
.page-info b { color: var(--text-1); font-weight: 700; }
.page-btns { margin-left: auto; display: flex; gap: 5px; align-items: center; }
.page-btn {
  min-width: 32px; height: 32px;
  padding: 0 8px;
  border-radius: 10px;
  border: 1px solid var(--glass-border);
  background: rgba(255,255,255,.6);
  font-size: 12.5px;
  font-weight: 600;
  color: var(--text-2);
  cursor: pointer;
  display: grid; place-items: center;
  transition: all .18s;
  font-variant-numeric: tabular-nums;
}
.page-btn:hover:not(:disabled) { background: #fff; color: var(--brand); box-shadow: var(--glass-shadow); }
.page-btn.active { background: var(--brand); color: #fff; border-color: var(--brand); }
.page-btn:disabled { opacity: .4; cursor: not-allowed; }
.page-size {
  height: 32px; padding: 0 26px 0 10px; border-radius: 10px;
  border: 1px solid var(--glass-border); background: rgba(255,255,255,.6) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='11' height='11' viewBox='0 0 24 24' fill='none' stroke='%2364748b' stroke-width='3'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E") no-repeat right 9px center;
  font-size: 12.5px; color: var(--text-2); outline: none; cursor: pointer; appearance: none; -webkit-appearance: none; font-family: inherit;
}

/* ---------- 空状态 ---------- */
.empty-state { padding: 60px 20px; text-align: center; }
/* 注意：这里必须用 `> svg`（直接子级）。空态里还有按钮，按钮里也有 lucide 图标；
   写成后代选择器 `.empty-state svg` 会把按钮图标一起改成 52px / 灰 / 半透明，
   「去导入」「生成测试数据」两个按钮的图标就是这样被撑坏、糊成一片的。 */
.empty-state > svg { width: 52px; height: 52px; color: var(--text-4); opacity: .5; margin-bottom: 12px; }
.empty-state p { font-size: 13.5px; color: var(--text-3); }
.empty-state .btn svg { width: 16px; height: 16px; opacity: 1; margin-bottom: 0; color: inherit; }

/* ---------- 模态 ---------- */
.modal-mask {
  position: fixed; inset: 0;
  background: rgba(15,23,42,.32);
  backdrop-filter: blur(6px);
  z-index: 100;
  display: none;
  align-items: center;
  justify-content: center;
  padding: 24px;
  animation: fadeIn .2s ease;
}
.modal-mask.show { display: flex; }
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
.modal {
  width: 100%;
  max-width: 560px;
  max-height: 84vh;
  overflow-y: auto;
  /* 弹窗壳用 --modal-bg（近乎不透明），
     内部组件用 --surface-flat / --surface 叠出层次，
     否则纯白壳 + 纯白组件 = 又糊成一片。 */
  background: var(--modal-bg);
  backdrop-filter: blur(22px) saturate(1.6);
  -webkit-backdrop-filter: blur(22px) saturate(1.6);
  border: 1px solid var(--glass-border);
  border-radius: var(--r-xl);
  box-shadow: 0 24px 70px rgba(15, 23, 42, 0.18), 0 2px 6px rgba(15, 23, 42, 0.04);
  animation: popIn .26s cubic-bezier(.34,1.4,.64,1);
}
@keyframes popIn { from { opacity: 0; transform: scale(.94) translateY(12px); } to { opacity: 1; transform: none; } }
.modal-head {
  padding: 14px 22px 10px;
  display: flex; align-items: center; gap: 12px;
  border-bottom: 1px solid var(--surface-line);
}
.modal-head h3 { font-size: 15.5px; font-weight: 700; color: var(--text-1); }
.modal-head .x {
  margin-left: auto; width: 30px; height: 30px; border-radius: 50%;
  display: grid; place-items: center; cursor: pointer;
  background: var(--surface); border: 1px solid var(--surface-line);
  transition: all .18s;
}
.modal-head .x:hover { background: var(--rose); border-color: var(--rose); }
.modal-head .x:hover svg { color: #fff; }
.modal-head .x svg { width: 15px; height: 15px; color: var(--text-3); }
.modal-body { padding: 14px 22px; }
/* 弹窗滚动条：细窄样式，避免出现系统默认粗滚动条 */
.modal::-webkit-scrollbar { width: 6px; }
.modal::-webkit-scrollbar-track { background: transparent; }
.modal::-webkit-scrollbar-thumb { background: rgba(15, 23, 42, .14); border-radius: 6px; }
.modal::-webkit-scrollbar-thumb:hover { background: rgba(15, 23, 42, .26); }
.modal-foot {
  padding: 12px 22px 14px;
  display: flex; gap: 10px; justify-content: flex-end;
  border-top: 1px solid #f1f5f9;
  background: #fafbfc;
  border-radius: 0 0 var(--r-xl) var(--r-xl);
}

/* 导入拖拽区 */
.dropzone {
  border: 2px dashed rgba(37,99,235,.32);
  border-radius: var(--r-lg);
  padding: 40px 20px;
  text-align: center;
  background: rgba(219,234,254,.22);
  cursor: pointer;
  transition: all .22s;
}
.dropzone:hover, .dropzone.drag { background: rgba(219,234,254,.45); border-color: var(--brand-3); }
.dropzone svg { width: 44px; height: 44px; color: var(--brand-3); margin-bottom: 12px; }
.dropzone h4 { font-size: 14.5px; font-weight: 700; margin-bottom: 5px; }
.dropzone p { font-size: 12.5px; color: var(--text-3); }
.dropzone .fmt { margin-top: 12px; display: flex; gap: 6px; justify-content: center; flex-wrap: wrap; }
.dropzone .fmt span { font-size: 11px; font-weight: 700; padding: 3px 9px; border-radius: 20px; background: rgba(255,255,255,.75); color: var(--text-3); border: 1px solid rgba(255,255,255,.9); }

/* 导入结果 */
.import-result { margin-top: 16px; }
.ir-row {
  display: flex; align-items: center; gap: 12px;
  padding: 11px 14px;
  border-radius: var(--r-sm);
  background: rgba(255,255,255,.6);
  border: 1px solid rgba(255,255,255,.8);
  margin-bottom: 8px;
  font-size: 13px;
}
.ir-row .ico { width: 30px; height: 30px; border-radius: 9px; display: grid; place-items: center; flex-shrink: 0; }
.ir-row .ico svg { width: 16px; height: 16px; }
.ir-row b { color: var(--text-1); font-weight: 700; }
.ir-row .val { margin-left: auto; font-weight: 700; font-variant-numeric: tabular-nums; }
.ir-ok .ico { background: rgba(16,185,129,.14); } .ir-ok .ico svg { color: #059669; } .ir-ok .val { color: #059669; }
.ir-dup .ico { background: rgba(245,158,11,.14); } .ir-dup .ico svg { color: #d97706; } .ir-dup .val { color: #d97706; }
.ir-err .ico { background: rgba(244,63,94,.13); } .ir-err .ico svg { color: #e11d48; } .ir-err .val { color: #e11d48; }

/* 表单 */
.field { margin-bottom: 15px; }
.field label { display: block; font-size: 12.5px; font-weight: 600; color: var(--text-2); margin-bottom: 7px; }
.field input, .field select, .field textarea {
  width: 100%;
  height: 40px;
  padding: 0 13px;
  border-radius: 12px;
  border: 1px solid rgba(255,255,255,.85);
  background: rgba(255,255,255,.7);
  font-size: 13px;
  color: var(--text-1);
  outline: none;
  transition: all .2s;
  font-family: inherit;
}
.field textarea { height: auto; padding: 11px 13px; resize: vertical; min-height: 100px; }
.field input:focus, .field select:focus, .field textarea:focus { border-color: var(--brand-3); background: #fff; box-shadow: 0 0 0 3px rgba(37,99,235,.1); }
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.field-hint { font-size: 11.5px; color: var(--text-4); margin-top: 5px; }
/* 「必填」星号：红色。
   ⚠️ 2026-10-12 调整：用户原话是「带*的文字颜色不要用黄色」（指 .req 标签本体
   的橘色底+橘色字），没说改星号；星号原本就是 var(--rose) 红色，刚才误改成
   黑色后用户明确「* 用红色」，这里恢复。 */
.req::after { content: ' *'; color: var(--rose); }

/* 明细列表 */
.detail-list { display: grid; gap: 0; }
.detail-item {
  display: flex;
  padding: 4px 0;
  border-bottom: 1px solid #f1f5f9;
  font-size: 13px;
  align-items: center;
}
.detail-item:last-child { border-bottom: none; }
.detail-item .k { width: 110px; color: var(--text-3); flex-shrink: 0; font-size: 12.5px; }
.detail-item .v { color: var(--text-1); font-weight: 500; word-break: break-all; flex: 1; }
.detail-item .v.mono { font-family: 'SF Mono', Menlo, monospace; font-size: 12.5px; }
/* 原始数据：**默认折起 2 行**，点一下展开看全文。
   含姓名/地址/电话/邮箱/IP/UA 的完整客户资料行能到 600+ 字符，
   不折会把详情弹窗撑得很长；但也绝不能像以前那样只拼三段假数据（2026-10-15 用户报的）。 */
.detail-item .all-raw {
  font-size: 11px; color: var(--text-3); line-height: 1.5;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; word-break: break-all;
}
/* 有真原文才可点开（.synth = 没原文时的三段兜底，点了也没意义） */
.detail-item .all-raw:not(.synth) { cursor: pointer; }
.detail-item .all-raw:not(.synth):hover { color: var(--text-2); }
.detail-item .all-raw.open {
  -webkit-line-clamp: unset;
  max-height: 240px; overflow-y: auto;
}
.detail-item .all-raw.synth { color: var(--text-4); }

/* 详情头部白卡 */
.detail-hero {
  display: flex; align-items: center; gap: 12px;
  padding: 11px 14px;
  border-radius: 14px;
  background: #ffffff;
  border: 1px solid #e2e8f0;
  margin-bottom: 12px;
  box-shadow: 0 1px 3px rgba(15, 23, 42, 0.04);
}
.detail-hero .hero-num { font-size: 16px; font-weight: 700; letter-spacing: .02em; }
.detail-hero .hero-bank { font-size: 12px; color: var(--text-3); margin-top: 1px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Toast —— 页面顶部居中，从上往下滑动弹出（2026-10-12 用户要求） */
.toast-wrap {
  position: fixed; top: 18px; left: 0; right: 0; z-index: 200;
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  pointer-events: none; /* 容器横跨整屏，不能挡住页面点击 */
}
.toast {
  display: flex; align-items: center; gap: 10px;
  padding: 12px 18px;
  border-radius: 14px;
  background: rgba(255,255,255,.85);
  backdrop-filter: blur(20px);
  border: 1px solid rgba(255,255,255,.9);
  box-shadow: 0 10px 30px rgba(31,38,135,.14);
  font-size: 13px;
  font-weight: 600;
  color: var(--text-1);
  animation: slideIn .42s cubic-bezier(.34,1.32,.5,1);
  min-width: 240px; max-width: min(92vw, 520px);
  pointer-events: auto;
}
.toast.hide { animation: slideOut .28s cubic-bezier(.4,0,1,1) forwards; }
/* 从屏幕上方 -56px 处落下来，带一点回弹（cubic-bezier 的 y>1 造成过冲） */
@keyframes slideIn { from { opacity: 0; transform: translateY(-56px) scale(.94); } to { opacity: 1; transform: none; } }
/* 退场往上收回，和进场方向相反 */
@keyframes slideOut { to { opacity: 0; transform: translateY(-32px) scale(.96); } }
.toast .ico { width: 26px; height: 26px; border-radius: 50%; display: grid; place-items: center; flex-shrink: 0; }
.toast .ico svg { width: 14px; height: 14px; }
.toast.success .ico { background: rgba(16,185,129,.15); } .toast.success .ico svg { color: #059669; }
.toast.error .ico { background: rgba(244,63,94,.14); } .toast.error .ico svg { color: #e11d48; }
.toast.info .ico { background: rgba(37,99,235,.14); } .toast.info .ico svg { color: var(--brand); }
/* 长报错（如 cookie 格式引导）要能换行读完整，不能被单行截断 */
.toast span { white-space: normal; line-height: 1.55; word-break: break-all; }

/* ---------- 旋转动画（检测中的行内按钮 / cookie 测试按钮共用） ---------- */
/* ⚠️ lucide 把 <i> 换成 <svg>，class 会带过去 —— 动画挂在 .spin 上，
   挂在 i 标签选择器上会在 createIcons 之后失效。 */
@keyframes spin360 { to { transform: rotate(360deg); } }
.spin { animation: spin360 .9s linear infinite; }
.spin svg { display: block; }

/* 行内「检测这一张」按钮的进行态：转圈 + 品牌色高亮，一眼看出这张卡正在测 */
.act-btn.check.checking { color: var(--brand); background: var(--brand-soft); pointer-events: none; }
.act-btn.check.checking svg { width: 15px; height: 15px; }

/* 检测闪电按钮（2026-10-13 用户要求「好看点的」）：
   实心闪电 —— stroke 用品牌色、fill 用品牌色淡底，比纯线框有分量；
   悬停微放大 + 底色。 */
.act-btn.check { color: var(--brand); }
.act-btn.check svg { fill: var(--brand-soft); transition: transform .16s; }
.act-btn.check:hover svg { transform: scale(1.15) rotate(-6deg); }
.act-btn.check:hover { background: var(--brand-soft); }

/* 进度条 */
.progress-line { height: 6px; border-radius: 6px; background: rgba(37,99,235,.12); overflow: hidden; margin-top: 10px; }
.progress-line i { display: block; height: 100%; border-radius: 6px; background: linear-gradient(90deg, var(--brand-3), var(--brand)); width: 0; transition: width .3s; }

/* ---------- 视图切换 ---------- */
.view { display: none; }
.view.active { display: block; animation: viewIn .3s cubic-bezier(.4,0,.2,1); }
@keyframes viewIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }

/* ==========================================================
   登录页
   ========================================================== */
.login-screen {
  position: fixed; inset: 0; z-index: 9999;
  display: flex; align-items: center; justify-content: center;
  padding: 24px;
  background:
    radial-gradient(900px 700px at 15% 0%, #dbeafe 0%, transparent 55%),
    radial-gradient(800px 600px at 95% 100%, #e0f2fe 0%, transparent 50%),
    linear-gradient(160deg, #f7fbff 0%, #eaf2fb 100%);
  overflow: hidden;
}
.login-card {
  position: relative; z-index: 2;
  width: 100%;
  max-width: 420px;
  padding: 30px 30px 24px;
  background: rgba(255, 255, 255, 0.78);
  backdrop-filter: blur(28px) saturate(180%);
  -webkit-backdrop-filter: blur(28px) saturate(180%);
  border: 1px solid rgba(255, 255, 255, 0.9);
  border-radius: 26px;
  box-shadow:
    0 24px 60px rgba(31, 38, 135, 0.18),
    0 2px 6px rgba(31, 38, 135, 0.04);
}
.login-logo {
  display: flex; align-items: center; gap: 14px;
  margin-bottom: 22px;
}
.login-logo h1 {
  font-size: 22px; font-weight: 800;
  letter-spacing: -0.025em; line-height: 1.1;
  background: linear-gradient(135deg, var(--brand), var(--violet));
  -webkit-background-clip: text; background-clip: text;
  color: transparent;
}
.login-logo p { font-size: 12px; color: var(--text-3); margin-top: 4px; letter-spacing: 0.03em; }
.login-form { display: flex; flex-direction: column; gap: 14px; }
/* 登录字段用独立类，不复用 .field —— 否则 .field input / .field input:focus
   （权重 0,2,1 > 0,1,1）会盖掉 .login-input-wrap 的透明背景，聚焦时冒出白色内框；
   且 .field 的 margin-bottom 会与 .login-form 的 gap 叠加成双倍间距。 */
.login-field label {
  display: block; font-size: 12.5px; font-weight: 600;
  color: var(--text-2); margin-bottom: 7px;
}
.login-input-wrap {
  position: relative;
  display: flex; align-items: center;
  height: 44px;
  border-radius: 14px;
  border: none;
  background: var(--surface);
  /* 组件面统一走 --surface（淡蓝），比毛玻璃卡底再深一档，
     既托出边界又不会像纯白那样和卡底糊在一起；
     另外保留一层极淡投影，比描边线更干净。 */
  box-shadow: 0 2px 8px rgba(15, 23, 42, 0.05);
  padding: 0 14px 0 42px;
  transition: all .2s;
}
.login-input-wrap:focus-within {
  background: rgba(255, 255, 255, 0.95);
  box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.12), 0 2px 8px rgba(15, 23, 42, 0.05);
}
/* 图标必须用绝对定位靠左贴边。
   注意：lucide 会把 <i data-lucide> 替换成 <svg class="lucide lucide-xxx">，
   只写 `> i` 会在替换后失配，图标退回普通 flex 项被左内边距推开 —— 故同时匹配 i 与 svg。 */
.login-input-wrap > i,
.login-input-wrap > svg {
  position: absolute; left: 14px; top: 50%; transform: translateY(-50%);
  width: 16px; height: 16px; color: var(--text-4);
  pointer-events: none;
  flex: none;
}
.login-input-wrap input {
  flex: 1; height: 100%;
  padding: 0;
  border: none; outline: none; background: transparent; box-shadow: none;
  font-size: 14px; color: var(--text-1);
  font-family: inherit;
}
.login-input-wrap input::placeholder { color: var(--text-4); }
.login-eye {
  border: none; background: transparent; cursor: pointer; padding: 0;
  width: 28px; height: 28px;
  display: grid; place-items: center;
  color: var(--text-4);
  border-radius: 8px;
  transition: background .15s;
}
.login-eye:hover { background: #f1f5f9; color: var(--text-2); }
.login-eye i { width: 16px; height: 16px; }
.login-row {
  display: flex; align-items: center; justify-content: space-between;
  margin-top: 2px;
  font-size: 12.5px;
}
.login-remember { display: inline-flex; align-items: center; gap: 7px; cursor: pointer; color: var(--text-2); user-select: none; }
.login-link { color: var(--brand); font-size: 12.5px; text-decoration: none; font-weight: 500; }
.login-link:hover { text-decoration: underline; }
.login-error {
  font-size: 12.5px;
  min-height: 18px;
  padding: 0 2px;
  color: var(--rose);
  display: flex; align-items: center; gap: 6px;
}
.login-error.show { font-weight: 600; }

/* 滑块验证码状态提示（2026-10-12 加）
   ⚠️ 三种状态都要让用户看得见：加载中 / 就绪 / 加载失败。
      失败时必须明确报错，不能让按钮变成「点了没反应」。 */
.login-captcha-tip {
  font-size: 12px; min-height: 16px; margin-top: 6px; text-align: center;
  color: var(--text-4); display: flex; align-items: center; justify-content: center; gap: 5px;
}
.login-captcha-tip.warn { color: var(--amber); }
.login-captcha-tip.bad  { color: var(--rose); font-weight: 600; }

/* 首次创建管理员时的说明条 */
.login-tip {
  display: flex; align-items: flex-start; gap: 8px;
  font-size: 12.5px; line-height: 1.6; color: var(--brand-2);
  background: rgba(37, 99, 235, .08);
  border: 1px solid rgba(37, 99, 235, .18);
  border-radius: 12px; padding: 10px 12px; margin-bottom: 4px;
}
.login-tip i { width: 16px; height: 16px; flex-shrink: 0; margin-top: 1px; }

.login-btn {
  height: 46px; border-radius: 14px; font-size: 14px;
  margin-top: 4px;
  justify-content: center;
}
.login-footer {
  margin-top: 22px;
  padding-top: 14px;
  border-top: 1px solid rgba(15, 23, 42, 0.06);
  display: flex; justify-content: space-between;
  font-size: 11px; color: var(--text-4);
}

/* 登录页装饰光斑 */
.login-decor {
  position: absolute; inset: 0; z-index: 1; pointer-events: none; overflow: hidden;
}
.login-decor-c1, .login-decor-c2, .login-decor-c3 {
  position: absolute; border-radius: 50%; filter: blur(80px); opacity: 0.55;
}
.login-decor-c1 { width: 420px; height: 420px; background: radial-gradient(circle, rgba(96,165,250,.55), transparent 70%); left: -120px; top: -100px; animation: float1 22s ease-in-out infinite; }
.login-decor-c2 { width: 360px; height: 360px; background: radial-gradient(circle, rgba(139,92,246,.4), transparent 70%); right: -100px; bottom: -120px; animation: float2 26s ease-in-out infinite; }
.login-decor-c3 { width: 240px; height: 240px; background: radial-gradient(circle, rgba(6,182,212,.35), transparent 70%); right: 25%; top: 60%; animation: float1 30s ease-in-out infinite reverse; }

/* ==========================================================
   用户菜单
   ========================================================== */
.user-chip {
  display: flex; align-items: center; gap: 9px;
  padding: 5px 10px 5px 5px;
  border-radius: 24px;
  border: 1px solid var(--glass-border);
  background: rgba(255,255,255,.6);
  backdrop-filter: blur(8px);
  cursor: pointer;
  transition: all .18s;
  position: relative;
  flex-shrink: 0;
}
.user-chip:hover { background: #fff; box-shadow: var(--glass-shadow); transform: translateY(-1px); }
.user-chip .avatar { width: 30px; height: 30px; font-size: 12px; box-shadow: none; }
.user-meta { display: flex; flex-direction: column; line-height: 1.1; }
.user-meta b { color: var(--text-1); font-size: 12.5px; font-weight: 600; }
.user-meta span { font-size: 10.5px; color: var(--text-3); margin-top: 1px; }
.user-caret { width: 13px; height: 13px; color: var(--text-4); transition: transform .18s; }
.user-chip.open .user-caret { transform: rotate(180deg); }

.user-menu {
  position: absolute; top: 62px; right: 22px;
  width: 240px;
  padding: 8px;
  background: rgba(255, 255, 255, 0.92);
  backdrop-filter: blur(24px) saturate(180%);
  border: 1px solid rgba(255, 255, 255, 0.9);
  border-radius: 16px;
  box-shadow: 0 16px 40px rgba(15, 23, 42, 0.14), 0 2px 6px rgba(15, 23, 42, 0.04);
  z-index: 90;
  display: none;
  animation: viewIn .2s cubic-bezier(.34,1.4,.64,1);
}
.user-menu.open { display: block; }
.user-menu-head {
  display: flex; align-items: center; gap: 11px;
  padding: 10px 10px 12px;
  border-bottom: 1px solid #f1f5f9;
  margin-bottom: 6px;
}
.user-menu-head b { display: block; font-size: 13px; font-weight: 600; color: var(--text-1); }
.user-menu-head span { font-size: 11.5px; color: var(--text-3); }
.user-menu-row {
  display: flex; align-items: center; gap: 9px;
  padding: 8px 10px;
  border-radius: 10px;
  font-size: 13px; color: var(--text-2); font-weight: 500;
  cursor: pointer;
  transition: background .15s, color .15s;
}
.user-menu-row:hover { background: #f1f5f9; color: var(--text-1); }
.user-menu-row.danger { color: var(--rose); }
.user-menu-row.danger:hover { background: rgba(244, 63, 94, 0.08); }
.user-menu-row i { width: 16px; height: 16px; flex-shrink: 0; }
.user-menu-divider {
  height: 1px; background: #f1f5f9; margin: 6px 0;
}

/* ==========================================================
   自定义下拉框（替换系统原生 select）
   ========================================================== */
.dd { position: relative; display: inline-block; vertical-align: middle; }
/* 原生 select 保留在 DOM 中存值，但完全不可见 */
.dd > select {
  position: absolute !important;
  width: 1px !important; height: 1px !important;
  opacity: 0 !important; pointer-events: none !important;
  margin: 0 !important; padding: 0 !important; border: 0 !important;
  appearance: none !important; -webkit-appearance: none !important;
}
.dd-trigger {
  display: inline-flex; align-items: center; gap: 8px;
  height: 36px; padding: 0 12px;
  border-radius: 18px;
  border: 1px solid var(--glass-border);
  background: rgba(255, 255, 255, 0.65);
  font-size: 13px; color: var(--text-2);
  cursor: pointer; font-family: inherit;
  transition: all .18s;
  white-space: nowrap;
  width: 100%;
}
.dd-trigger:hover { background: #fff; box-shadow: var(--glass-shadow); }
.dd-trigger.open { border-color: var(--brand-3); background: #fff; box-shadow: 0 0 0 3px rgba(37, 99, 235, .1); }
.dd-label { flex: 1; text-align: left; overflow: hidden; text-overflow: ellipsis; }
.dd-trigger.dd-placeholder .dd-label { color: var(--text-3); }
.dd-arrow { width: 14px; height: 14px; color: var(--text-4); transition: transform .2s; flex-shrink: 0; }
.dd-trigger.open .dd-arrow { transform: rotate(180deg); color: var(--brand); }
/* 禁用态：账户编辑里「角色」高于当前用户时会被锁住，必须看得出来 */
.dd-trigger.is-disabled { opacity: .45; cursor: not-allowed; }
.dd-trigger.is-disabled:hover { background: var(--surface, #fff); box-shadow: none; }

.dd-menu {
  position: fixed;
  z-index: 600;
  min-width: 150px; max-height: 300px;
  display: none; flex-direction: column;
  padding: 6px;
  background: rgba(255, 255, 255, 0.97);
  backdrop-filter: blur(20px) saturate(180%);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  border: 1px solid rgba(255, 255, 255, 0.9);
  border-radius: 14px;
  box-shadow: 0 12px 36px rgba(15, 23, 42, .16), 0 2px 6px rgba(15, 23, 42, .05);
  overflow: hidden;
}
.dd-menu.open { display: flex; animation: ddIn .16s cubic-bezier(.34, 1.3, .64, 1); }
@keyframes ddIn { from { opacity: 0; transform: translateY(-6px) scale(.98); } to { opacity: 1; transform: none; } }
.dd-search { position: relative; padding: 2px 2px 7px; border-bottom: 1px solid #f1f5f9; margin-bottom: 5px; flex-shrink: 0; }
.dd-search > i { position: absolute; left: 11px; top: 50%; transform: translateY(-62%); width: 13px; height: 13px; color: var(--text-4); }
.dd-search input {
  width: 100%; height: 30px; padding: 0 10px 0 29px;
  border-radius: 9px; border: 1px solid #e2e8f0;
  background: #f8fafc; font-size: 12.5px; outline: none; font-family: inherit;
  color: var(--text-1);
}
.dd-search input:focus { border-color: var(--brand-3); background: #fff; box-shadow: 0 0 0 3px rgba(37, 99, 235, .08); }
.dd-list { overflow-y: auto; max-height: 226px; flex: 1; }
.dd-list::-webkit-scrollbar { width: 7px; }
.dd-list::-webkit-scrollbar-thumb { background: rgba(37, 99, 235, .2); border-radius: 7px; }
.dd-item {
  display: flex; align-items: center; gap: 8px;
  padding: 7px 10px; border-radius: 9px;
  font-size: 13px; color: var(--text-2);
  cursor: pointer; transition: background .13s, color .13s;
  white-space: nowrap;
}
.dd-item:hover, .dd-item.hl { background: #eff6ff; color: var(--text-1); }
.dd-item.selected { color: var(--brand); font-weight: 600; background: rgba(37, 99, 235, .07); }
.dd-item > span { flex: 1; overflow: hidden; text-overflow: ellipsis; }
.dd-check { width: 14px; height: 14px; color: var(--brand); flex-shrink: 0; }
.dd-empty { padding: 16px 10px; text-align: center; font-size: 12.5px; color: var(--text-4); }

/* ---------- 响应式 ---------- */
@media (max-width: 1440px) {
  /* 5 张卡在 1440 以下挤 5 列太窄 —— 3 列排成 3+2，比 4+1 好看 */
  .kpi-grid { grid-template-columns: repeat(3, 1fr); }
  .grid-3 { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 1200px) {
  .kpi-grid { grid-template-columns: repeat(2, 1fr); }
  .grid-2, .grid-3, .grid-8-4 { grid-template-columns: 1fr; }
}
@media (max-width: 900px) {
  :root { --sidebar-w: 76px; }
  .logo-text, .nav-item span, .nav-group, .sidebar-foot { display: none; }
  .nav-item { justify-content: center; padding: 12px; }
  .user-meta { display: none; }
  .user-chip { padding: 3px; }
}

/* ==========================================================
   账户管理 / 权限 / 个人中心
   ========================================================== */
/* 顶栏账户菜单里的权限数提示 */
.user-menu-perm {
  margin: 2px 4px 8px;
  padding: 7px 10px;
  border-radius: 10px;
  background: rgba(37, 99, 235, 0.07);
  font-size: 11.5px;
  color: var(--brand);
  font-weight: 600;
  text-align: center;
}

/* 分段切换（账户列表 / 角色权限） */
.seg {
  display: inline-flex;
  gap: 3px;
  padding: 3px;
  border-radius: 13px;
  background: rgba(15, 23, 42, 0.05);
}
.seg-btn {
  display: inline-flex; align-items: center; gap: 6px;
  height: 32px; padding: 0 14px;
  border: none; border-radius: 10px;
  background: transparent;
  font-family: inherit; font-size: 12.5px; font-weight: 600;
  color: var(--text-3);
  cursor: pointer;
  transition: all .18s;
}
.seg-btn svg { width: 14px; height: 14px; }
.seg-btn:hover { color: var(--text-1); }
.seg-btn.active {
  background: #fff;
  color: var(--brand);
  box-shadow: 0 2px 8px rgba(15, 23, 42, 0.08);
}

/* 角色权限矩阵 */
.perm-table { min-width: 720px; }
.perm-table th { position: sticky; top: 0; background: #e9f0fa; }
.perm-table tbody tr:hover { background: rgba(219, 234, 254, 0.22); }

/* 个人资料头部 */
.profile-hero {
  display: flex; align-items: center; gap: 14px;
  padding: 16px;
  margin-bottom: 14px;
  border-radius: 16px;
  background: linear-gradient(135deg, rgba(37,99,235,.09), rgba(139,92,246,.07));
}

/* 初始化异常提示条（正常情况下不出现） */
#bootError {
  position: fixed; left: 50%; bottom: 22px; transform: translateX(-50%);
  z-index: 9999; max-width: 640px;
  display: flex; flex-direction: column; gap: 4px;
  padding: 14px 20px; border-radius: 14px;
  background: #fff1f2; border: 1px solid rgba(225, 29, 72, 0.35);
  box-shadow: 0 12px 32px rgba(225, 29, 72, 0.18);
  font-size: 12.5px; color: #9f1239;
}
#bootError b { font-size: 13.5px; }
#bootError em { font-style: normal; color: var(--text-3); }
#bootError kbd {
  font-family: inherit; font-size: 11.5px; padding: 1px 5px; border-radius: 5px;
  background: #fff; border: 1px solid rgba(225, 29, 72, 0.3);
}

/* ==========================================================
   弹窗内组件底色（全局修复）
   弹窗是纯白底（.modal background:#fff），但全局组件默认按「玻璃拟态」设计 ——
   半透明白底 + 白描边 rgba(255,255,255,...)。放在玻璃卡片上没问题，
   放进白色弹窗就变成白上白：下拉触发器、输入框、格式标签、结果行全都看不出边界。
   这里统一把 .modal 内的这些组件换成淡蓝灰填充 + 浅蓝描边。
   ========================================================== */
.modal .field input,
.modal .field select,
.modal .field textarea,
.modal .dd-trigger,
.modal .ir-row,
.modal .btn-ghost {
  background: #f2f6fc;
  border: 1px solid rgba(37, 99, 235, 0.14);
}
.modal .dd-trigger:hover,
.modal .btn-ghost:hover { background: #eaf1fd; border-color: rgba(37, 99, 235, 0.3); }
.modal .field input:focus,
.modal .field select:focus,
.modal .field textarea:focus {
  background: #fff;
  border-color: var(--brand-3);
  box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
}
/* 拖拽区与格式标签 */
.modal .dropzone { background: #f4f8fe; }
.modal .dropzone .fmt span {
  background: #fff;
  border: 1px solid rgba(37, 99, 235, 0.18);
  color: var(--text-2);
}
/* 详情弹窗：头部白卡改淡底、分隔线加深，避免与弹窗白底糊在一起 */
.modal .detail-hero { background: #f2f6fc; border-color: rgba(37, 99, 235, 0.14); }
.modal .detail-item { border-bottom-color: #e8eef7; }

/* ==========================================================
   母卡数据库 · 可见数据表选择器（系统设置）
   ========================================================== */
.tbl-picker {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(212px, 1fr));
  gap: 8px;
  max-height: 264px;
  overflow-y: auto;
  padding: 2px;
}
.tbl-chip {
  display: flex; align-items: center; gap: 9px;
  padding: 9px 12px;
  border-radius: 12px;
  border: 1px solid rgba(37, 99, 235, 0.12);
  background: rgba(255, 255, 255, 0.72);
  font-family: inherit; font-size: 12.5px; color: var(--text-3);
  cursor: pointer; text-align: left;
  transition: all .16s;
}
.tbl-chip:hover { border-color: rgba(37, 99, 235, 0.3); background: #fff; }
.tbl-chip.on {
  background: #eaf1fd; border-color: rgba(37, 99, 235, 0.32);
  color: var(--text-1); font-weight: 600;
}
.tbl-chip .tbl-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tbl-chip .tbl-count { font-size: 11.5px; color: var(--text-4); font-variant-numeric: tabular-nums; }
.tbl-chip.on .tbl-count { color: var(--brand); }

/* ==========================================================================
   数据清理面板（系统设置）+ 分页元信息 + 未读徽标
   ========================================================================== */
.purge-panel { display: flex; flex-direction: column; }
.purge-item {
  display: flex; align-items: flex-start; gap: 14px;
  padding: 11px 14px;
  border-radius: 12px;
  font-size: 13px;
  color: var(--text-2);
  background: rgba(255, 255, 255, 0.5);
  border: 1px solid rgba(255, 255, 255, 0.75);
}
.purge-item + .purge-item { margin-top: 8px; }
.purge-item .pi-k {
  flex: none; width: 96px;
  color: var(--text-4); font-size: 12px; font-weight: 600;
}
.purge-item .pi-v { flex: 1; min-width: 0; }
.purge-item .pi-v b { color: var(--brand); font-weight: 700; }
.purge-item .pi-num { font-variant-numeric: tabular-nums; font-size: 14px; }
.purge-item .pi-v em { font-style: normal; color: var(--text-4); }

.page-meta {
  font-size: 11.5px; color: var(--text-4);
  margin-left: 10px;
  font-variant-numeric: tabular-nums;
}

/* （.nav-item .badge 相关样式已随「未读徽标」功能一起移除） */

/* ==========================================================================
   全局组件底色统一（三级层次：页面底 → 玻璃卡片 → 组件面）
   --------------------------------------------------------------------------
   之前的问题是「白上白」：卡片是 rgba(255,255,255,.55)，卡片里的输入框、下拉框、
   分页按钮、结果行又各自是 rgba(255,255,255,.6~.85) / #fff，
   两层白叠在一起，组件边界完全看不出来。这里把**所有组件面**统一成
   --surface（带蓝调的半透明面）+ --surface-line（可见描边），
   卡片本身加白到 .72 —— 卡片和组件之间才有对比。
   放在文件末尾，同优先级下后者覆盖前者，不用去改上面几十处散落的规则。
   ========================================================================== */
.icon-btn,
.btn-ghost,
.user-chip,
.factory-chip,
.toolbar-search input,
.checkbox,
.act-btn,
.page-btn,
.dd-trigger,
.dd-search input,
.field input,
.field select,
.field textarea,
.ir-row,
.detail-hero,
.tbl-chip,
.purge-item,
.toast {
  background-color: var(--surface);
  border-color: var(--surface-line);
}
/* 带背景图（下拉箭头）的两个控件只能改 background-color，否则会把箭头冲掉 */
.filter-select,
.page-size {
  background-color: var(--surface);
  border-color: var(--surface-line);
}

/* hover / 选中态：同一色系加深一档，不要跳回纯白 */
.icon-btn:hover,
.btn-ghost:hover,
.user-chip:hover,
.page-btn:hover:not(:disabled),
.dd-trigger:hover,
.dd-trigger.open,
.tbl-chip:hover {
  background-color: var(--surface-strong);
  border-color: var(--surface-line-2);
}
.seg-btn.active { background: var(--surface-strong); border-color: var(--surface-line); }
.tbl-chip.on { background: var(--surface-strong); border-color: var(--surface-line-2); }
.dd-item:hover, .dd-item.hl { background: var(--surface); }
.dd-item.selected { background: var(--surface-strong); }
.toolbar-search input:focus,
.filter-select:focus,
.field input:focus,
.field select:focus,
.field textarea:focus,
.dd-search input:focus {
  background-color: rgba(255, 255, 255, 0.95);
  border-color: var(--brand-3);
}
.dropzone .fmt span { background: var(--surface-strong); border-color: var(--surface-line); }

/* 卡片内的分隔线：白线在白卡上看不见，统一换成淡蓝 */
.table-toolbar,
.pagination { border-color: var(--surface-line); }
.data-table tbody tr { border-bottom-color: rgba(37, 99, 235, 0.08); }
.modal-foot { border-top-color: var(--surface-line); background: var(--surface); }

/* 弹窗壳也是毛玻璃（--glass-bg），组件面用不透明的同一色档，避免半透明叠出灰斑 */
.modal .field input,
.modal .field select,
.modal .field textarea,
.modal .dd-trigger,
.modal .ir-row,
.modal .btn-ghost,
.modal .detail-hero,
.modal .dropzone .fmt span { background: var(--surface-flat); border-color: var(--surface-line); }
.modal .dd-trigger:hover,
.modal .btn-ghost:hover { background: var(--surface-strong); border-color: var(--surface-line-2); }
.modal .dropzone { background: var(--surface-flat); }
.modal .dd-search input { background: var(--surface-flat); border-color: var(--surface-line); }
/* 弹窗内的聚焦态：第 1268 行那条 `.modal .field input:focus { background:#fff }`
   权重是 0,2,1，比文件末尾的统一块高，必须在这里同权重覆盖掉 */
.modal .field input:focus,
.modal .field select:focus,
.modal .field textarea:focus,
.modal .dd-search input:focus {
  background: rgba(255, 255, 255, 0.95);
  border-color: var(--brand-3);
}

/* 空态按钮：主按钮保持品牌蓝（图标必须跟着变白） */
.empty-state .btn-ghost { background: var(--surface-strong); border: 1px solid var(--surface-line-2); box-shadow: none; }
.empty-state .btn-ghost:hover { background: var(--surface); }
.empty-state .btn-primary { color: #fff; }
.empty-state .btn-primary svg { color: #fff; }
.empty-state .btn-ghost svg { color: var(--brand); }
/* 空态里的操作按钮：原来靠内联 style 撑布局，现在统一成类 */
.empty-actions { display: flex; gap: 10px; justify-content: center; margin-top: 16px; flex-wrap: wrap; }

/* 解析模式说明（四要素全自动，不再需要手选分隔符） */
.parse-mode {
  display: flex; gap: 8px; align-items: flex-start;
  padding: 10px 12px; border-radius: 12px;
  background: var(--surface-flat);
  border: 1px solid var(--surface-line);
  font-size: 12px; line-height: 1.7; color: var(--text-2);
}
.parse-mode svg { width: 15px; height: 15px; color: var(--brand); flex-shrink: 0; margin-top: 2px; }

/* ==========================================================================
   本地 BIN 库查询面板
   ========================================================================== */
.bin-box {
  margin-top: 4px;
  border-radius: 14px;
  background: var(--surface-flat);
  border: 1px solid var(--surface-line);
  padding: 11px 13px;
}
.bin-head { display: flex; align-items: center; gap: 8px; font-size: 12.5px; color: var(--text-2); }
.bin-head svg { width: 15px; height: 15px; color: var(--brand); flex-shrink: 0; }
.bin-head b { font-weight: 700; color: var(--text-1); }
.bin-live {
  margin-left: auto; font-size: 11px; font-weight: 700;
  padding: 2px 9px; border-radius: 20px;
  background: rgba(100, 116, 139, .14); color: var(--text-3);
}
.bin-live.on { background: rgba(16, 185, 129, .14); color: #059669; }
.bin-live.off { background: rgba(244, 63, 94, .13); color: #e11d48; }
.bin-opts { display: flex; flex-wrap: wrap; gap: 10px 18px; margin-top: 9px; align-items: center; }
.bin-opt { display: inline-flex; align-items: center; gap: 7px; font-size: 12.5px; color: var(--text-2); cursor: pointer; }

.bin-status {  margin-top: 10px; padding: 9px 12px;
  border-radius: 12px;
  background: var(--surface-flat);
  border: 1px solid var(--surface-line);
  font-size: 12px; color: var(--text-2); line-height: 1.7;
  font-variant-numeric: tabular-nums;
}
.bin-status b { color: var(--brand); font-weight: 700; }
.bin-status em { font-style: normal; color: var(--text-4); }

/* 导入进度：解析 → BIN 检测 → 入库 三段式 */
.imp-prog { margin-top: 16px; }
.imp-prog .pp-bar {
  height: 8px; border-radius: 8px; overflow: hidden;
  background: var(--surface-strong);
}
.imp-prog .pp-bar i {
  display: block; height: 100%; width: 0;
  border-radius: 8px;
  background: linear-gradient(90deg, var(--brand-3), var(--brand));
  transition: width .18s ease;
}
.imp-prog .pp-msg { margin-top: 9px; font-size: 12.5px; color: var(--text-2); display: flex; gap: 10px; }
.imp-prog .pp-msg b { color: var(--text-1); }
.imp-prog .pp-num { margin-left: auto; color: var(--text-4); font-variant-numeric: tabular-nums; }

/* 解析预览表 */
.parse-prev { margin-top: 12px; }
.parse-prev .pv-title { font-size: 12px; color: var(--text-3); margin-bottom: 6px; }
.parse-prev table { width: 100%; border-collapse: collapse; font-size: 11.5px; }
.parse-prev th, .parse-prev td {
  padding: 5px 7px; text-align: center; border-bottom: 1px solid rgba(37, 99, 235, .08);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 130px;
}
.parse-prev th { background: #e9f0fa; color: var(--text-3); font-weight: 700; }
.parse-prev td.mono { font-family: 'SF Mono', Menlo, monospace; }
.parse-prev td.miss { color: #e11d48; font-weight: 700; }
.parse-prev td.src { color: var(--text-4); }

/* ==========================================================
   导入弹窗：紧凑排版
   ----------------------------------------------------------
   拖拽区 + 目标库 + BIN 面板 + 结果行堆起来很容易超过 84vh，
   一出现滚动条就把「开始导入」按钮顶出视野。
   用户明确要求：拖拽区小一点、不要滚动条（含导入成功后的结果展示）。
   所以这里只压 #modalImport 内部，不动其它弹窗。
   ========================================================== */
#modalImport .modal-body { padding: 12px 22px; }
#modalImport .dropzone { padding: 15px 16px 13px; }
#modalImport .dropzone svg { width: 25px; height: 25px; margin-bottom: 5px; }
#modalImport .dropzone h4 { font-size: 13px; margin-bottom: 2px; }
#modalImport .dropzone .fmt { margin-top: 7px; }
#modalImport .dropzone .fmt span { font-size: 10.5px; padding: 2px 8px; }
#modalImport .field { margin-bottom: 10px; }
#modalImport .bin-box { padding: 9px 12px; }
#modalImport .bin-opts { gap: 8px 16px; margin-top: 7px; }
#modalImport .import-result { margin-top: 12px; }
#modalImport .ir-row { padding: 7px 12px; margin-bottom: 6px; font-size: 12.5px; }
#modalImport .ir-row .ico { width: 25px; height: 25px; }
#modalImport .ir-row .ico svg { width: 14px; height: 14px; }
#modalImport .field-hint { line-height: 1.6; }
#modalImport .parse-prev th, #modalImport .parse-prev td { padding: 4px 6px; }

/* BIN「覆盖模式」的说明 —— 光看标题看不懂它是干嘛的 */
.bin-tip {
  margin-top: 8px; padding: 7px 10px; border-radius: 10px;
  font-size: 11.5px; line-height: 1.65; color: var(--text-3);
  background: var(--surface); border: 1px solid var(--surface-line);
}
.bin-tip b { color: var(--text-2); font-weight: 700; }

/* 选过文件之后：拖拽区收成一条细的「重新选择」条，省掉约 70px 高度 */
#modalImport .dropzone.compact {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  padding: 9px 12px; text-align: left;
}
#modalImport .dropzone.compact svg { width: 15px; height: 15px; margin: 0; }
#modalImport .dropzone.compact h4 { font-size: 12px; margin: 0; font-weight: 600; }
#modalImport .dropzone.compact .fmt { display: none; }
/* 结果行再压一点，保证「导入成功」的结果展示也不出现滚动条 */
#modalImport .ir-row { padding: 6px 11px; margin-bottom: 5px; }
#modalImport .field-hint { font-size: 11.5px; line-height: 1.55; }

/* 导入结果里的「噪声行」——不是错误，只是不含卡号的行，用中性灰蓝别用红 */
.ir-noise .ico { background: rgba(100, 116, 139, .14); }
.ir-noise .ico svg { color: #475569; }
.ir-noise .val { color: #475569; }
.ir-noise .sub-text { margin-top: 1px; }

/* 「跳过的行」原文示例：等宽小字，一眼能认出是哪几行 */
.ir-noise .sub-text code {
  display: inline-block; margin: 2px 0; padding: 1px 6px; border-radius: 6px;
  background: rgba(100, 116, 139, .13); color: var(--text-2);
  font-family: 'SF Mono', Menlo, monospace; font-size: 11px;
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* 导入弹窗：整体做成「头/脚固定 + 正文滚动」，
   这样无论内容多高，「开始导入 / 取消」永远在视野里（以前会被滚动条顶出去）。 */
#modalImport .modal { max-height: 92vh; display: flex; flex-direction: column; overflow: hidden; }
#modalImport .modal-head, #modalImport .modal-foot { flex-shrink: 0; }
#modalImport .modal-body { overflow-y: auto; }
#modalImport .modal-body::-webkit-scrollbar { width: 6px; }
#modalImport .modal-body::-webkit-scrollbar-track { background: transparent; }
#modalImport .modal-body::-webkit-scrollbar-thumb { background: rgba(15, 23, 42, .14); border-radius: 6px; }

/* 「跳过的行」原文：平铺 + 最多两行，不把结果区撑高 */
.ir-noise .sub-text { max-height: 36px; overflow: hidden; line-height: 1.7; }
.ir-noise .sub-text code {
  display: inline-block; margin: 1px 4px 1px 0; padding: 0 6px; border-radius: 6px;
  background: rgba(100, 116, 139, .13); color: var(--text-2);
  font-family: 'SF Mono', Menlo, monospace; font-size: 10.5px; line-height: 1.6;
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom;
}

/* ==========================================================================
   必选项高亮（导入目标库没选时）—— 脉冲描边 + 摇一摇
   ========================================================================== */
/* ⚠️ 2026-10-13 改：用户原话「必选用红色字体 不要搞那么一大块黄色背景
   而且还和下面的目标分表文字压在一起了」——
   大黄框整块去掉（就是那个 padding + 负 margin 把下面的「目标分表」顶住了），
   只把「必选」两个字标红。 */
.field.need-pick {
  border-radius: 0;
  padding: 0;
  margin: 0;
  border: none;
  background: none;
  animation: none;
}
.field.need-pick .pick-hint { display: flex; }
.field .pick-hint {
  display: none;
  align-items: center;
  gap: 5px;
  font-size: 11.5px;
  font-weight: 600;
  color: #b45309;
  margin-top: 6px;
}
.field .pick-hint svg { width: 13px; height: 13px; }
.field .req {
  /* ⚠️ 2026-10-13 改：用户原话「必选用红色字体」—— 无背景，就红字。 */
  display: inline-block;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--rose);
  background: none;
  border-radius: 0;
  padding: 0;
  margin-left: 2px;
  vertical-align: baseline;
}
/* 已选好时不再红 —— 提示使命完成 */
.field.picked .req { color: var(--text-4); }
/* 已经选好 → 高亮消失 */
.field.picked { border-color: transparent; background: none; animation: none; padding: 0; margin: 0; }
@keyframes pickPulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(245, 158, 11, .34); }
  50%      { box-shadow: 0 0 0 7px rgba(245, 158, 11, 0); }
}
/* 点「开始导入」却没选库 → 摇一摇，比只弹 toast 更容易被看见 */
.field.shake { animation: pickShake .5s cubic-bezier(.36,.07,.19,.97); }
@keyframes pickShake {
  10%, 90% { transform: translateX(-2px); }
  20%, 80% { transform: translateX(4px); }
  30%, 50%, 70% { transform: translateX(-7px); }
  40%, 60% { transform: translateX(7px); }
}

/* 危险按钮（删除 / 清空） */
.btn-danger {
  background: linear-gradient(135deg, #f43f5e, #e11d48);
  color: #fff;
  box-shadow: 0 8px 22px rgba(225, 29, 72, .3);
}
.btn-danger:hover { transform: translateY(-2px); box-shadow: 0 12px 28px rgba(225, 29, 72, .38); }

/* 确认弹窗正文 */
.cfm-msg { font-size: 13.5px; color: var(--text-2); line-height: 1.75; }
.cfm-msg b { color: var(--text-1); }
.cfm-msg .cfm-key {
  display: inline-block;
  font-weight: 700;
  color: #b45309;
  background: rgba(245, 158, 11, .16);
  border-radius: 8px;
  padding: 1px 8px;
}
.cfm-msg .cfm-list {
  margin: 10px 0 0;
  padding: 10px 12px;
  border-radius: 12px;
  background: var(--surface);
  border: 1px solid var(--surface-line);
  font-size: 12.5px;
}
.cfm-msg .cfm-list span.k { color: var(--text-4); margin-right: 6px; }

/* ==========================================================================
   测卡（大类 js/check/）· 界面样式
   --------------------------------------------------------------------------
   1) 活卡管理表格：勾选列 + 状态列（活卡/死卡）+ 备注列
   2) 检测进度面板
   3) 系统设置里的测卡配置区
   ⚠️ 表格排版口径（2026-10-12 定死）：表头居中 + 内容整块居中 + 单元格内多行彼此左对齐。
      这里的多行块一律用 inline-block/flex + text-align:left，别用 block（会撑满贴左）。
   ========================================================================== */

/* —— 勾选列 —— */
/* ⚠️ 用户 2026-10-13：「表格的复选框和左边留点间距」——
   原来 padding-left: 0 让勾选框紧贴表格左边缘，视觉上像被切掉一半。
   给左边留 12px（表头/表体同一条规则，保证列宽一致不错位）。 */
.data-table th.col-sel,
.data-table td.col-sel { width: 48px; text-align: center; padding-left: 12px; padding-right: 0; }
.data-table td.col-sel input[type=checkbox],
.data-table th.col-sel input[type=checkbox] {
  width: 15px; height: 15px; cursor: pointer; accent-color: var(--blue, #2563eb);
}

/* —— 状态列（活卡 / 死卡 / 未检测 / 通道异常） —— */
.chk-tag {
  display: inline-block;
  font-size: 12px; font-weight: 700;
  padding: 2px 9px; border-radius: 999px;
  line-height: 1.6; white-space: nowrap;
}
.chk-live  { color: #047857; background: rgba(16, 185, 129, .14); }
.chk-dead  { color: #b91c1c; background: rgba(244, 63, 94, .13); }
/* 未检测：灰，不给任何倾向性暗示 */
.chk-none  { color: var(--text-4); background: rgba(100, 116, 139, .1); }
/* RISK / ERR 是「通道当时没测成」，不是卡的结论 —— 用琥珀色跟死卡区分开 */
.chk-err   { color: #b45309; background: rgba(245, 158, 11, .16); cursor: help; }

/* —— 备注列 —— */
.data-table td.cell-beizhu {
  max-width: 150px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  color: var(--text-2); font-size: 12.5px;
}

/* —— 行内「检测」按钮 —— */
.act-btn.check { color: var(--blue, #2563eb); }
.act-btn.check:hover { background: rgba(37, 99, 235, .1); }

/* —— 检测进度面板（活卡管理表格上方） —— */
.check-panel {
  margin: 0 0 14px;
  padding: 12px 16px;
  border-radius: 14px;
  background: var(--surface);
  border: 1px solid var(--surface-line);
}
.check-panel .chk-head {
  display: flex; align-items: center; gap: 8px;
  font-size: 13px; color: var(--text-2);
}
.check-panel .chk-head > i { width: 15px; height: 15px; color: var(--blue, #2563eb); }
.check-panel .chk-head b { color: var(--text-1); font-size: 13.5px; }
.check-panel .chk-head .chk-cur { color: var(--text-4); font-size: 12px; }
.check-panel .chk-head .chk-right { margin-left: auto; font-variant-numeric: tabular-nums; }
.check-panel .chk-head .ok  { color: #047857; }
.check-panel .chk-head .bad { color: #b91c1c; }
.check-panel .chk-bar {
  margin-top: 9px; height: 6px; border-radius: 999px;
  background: rgba(100, 116, 139, .16); overflow: hidden;
}
.check-panel .chk-bar > i {
  display: block; height: 100%; border-radius: 999px;
  background: linear-gradient(90deg, #2563eb, #06b6d4);
  transition: width .35s ease;
}
.check-panel .chk-log {
  margin-top: 9px; max-height: 108px; overflow: auto;
  font-size: 12px; line-height: 1.75; color: var(--text-3);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}
.check-panel .chk-log-line { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* —— 系统设置 · 测卡配置区 —— */
.setting-sub {
  font-size: 12.5px; font-weight: 700; color: var(--text-3);
  margin: 18px 0 10px; letter-spacing: .02em;
}
.setting-sub:first-child { margin-top: 0; }

/* ⚠️ 必须限高 + 内部滚动（2026-10-15 用户：「cookie的保存不是内部滚动的形式
   只会无限拉高页面」）。以前这里只有 flex 列布局、没有 max-height，
   而每个 .ck-item 里还塞着一个 resize:vertical 的 textarea ——
   多几条 cookie 就能把整页顶得老长，得一直往下滚才能翻到别的设置项。 */
.ck-list {
  display: flex; flex-direction: column; gap: 10px;
  max-height: 430px;
  overflow-y: auto;
  overscroll-behavior: contain;   /* 滚到底别把整页一起带着滚 */
  padding-right: 8px;             /* 给滚动条留位置，别压住卡片右边缘 */
  scrollbar-width: thin;
}
.ck-list::-webkit-scrollbar { width: 8px; }
.ck-list::-webkit-scrollbar-track { background: transparent; }
.ck-list::-webkit-scrollbar-thumb {
  border-radius: 999px;
  background: rgba(37, 99, 235, .22);
}
.ck-list::-webkit-scrollbar-thumb:hover { background: rgba(37, 99, 235, .38); }
.ck-item {
  padding: 11px 13px;
  border-radius: 12px;
  background: var(--surface);
  border: 1px solid var(--surface-line);
}
.ck-item-head { display: flex; align-items: center; gap: 8px; }
.ck-item-head .ck-label {
  flex: 1; min-width: 0;
  height: 32px; padding: 0 11px;
  border-radius: 9px;
  border: 1px solid var(--surface-line);
  background: var(--surface-flat, #fff);
  color: var(--text-1); font-size: 13px;
}
.ck-item .ck-cookie {
  width: 100%; margin-top: 8px; padding: 8px 11px;
  border-radius: 9px; resize: vertical;
  /* ⚠️ 限高：resize:vertical 让用户能拖高，但不封顶的话一条 cookie 就能把
     .ck-list（430px 内部滚动区）撑爆，滚动条形同虚设 */
  min-height: 62px; max-height: 130px;
  border: 1px solid var(--surface-line);
  background: var(--surface-flat, #fff);
  color: var(--text-1);
  font-size: 12px; line-height: 1.6;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}
.ck-item .ck-mask {
  margin-top: 6px;
  font-size: 11.5px; color: var(--text-4);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  word-break: break-all;
}
.ck-row {
  display: flex; align-items: center; justify-content: space-between;
  padding: 11px 0;
  border-bottom: 1px solid var(--surface-line);
  font-size: 13.5px; cursor: pointer;
}
.ck-row input[type=number], .ck-row input[type=time] {
  height: 32px; padding: 0 9px;
  border-radius: 9px;
  border: 1px solid var(--surface-line);
  background: var(--surface-flat, #fff);
  color: var(--text-1); font-size: 13px;
}
.ck-row input[type=checkbox] { width: 15px; height: 15px; accent-color: var(--blue, #2563eb); }

/* 全测按钮运行态（进度画在按钮上，不再有大面板） */
.btn.checking { border-color: var(--brand, #2563eb); color: var(--brand, #2563eb); font-weight: 500; }

/* ==========================================================================
   测卡：标题栏进度动画块 + 「检测方式」三选一
   --------------------------------------------------------------------------
   2026-10-14 用户：「开始检测后在活卡管理标题栏中间做一个动画区块显示检测进度」
              「检测按钮点击弹出来 检测全部 / 断点续测 / 复检死卡」
   ========================================================================== */

/* —— 标题栏中间的进度块 —— */
/* ⚠️ 默认 display:none，靠 .on 打开。**不要改成用 [hidden] 属性**：
   下面 .check-progress 一旦带上 display:flex，就会盖掉 UA 的
   [hidden]{display:none}，「隐藏」当场失效。 */
.check-progress { display: none; }
.check-progress.on {
  display: flex; align-items: center; gap: 12px;
  flex: 1 1 auto; min-width: 0; justify-content: center; padding: 0 12px;
}
/* 呼吸的圆点：一眼知道「在跑」 */
.cp-dot {
  width: 9px; height: 9px; border-radius: 50%; flex-shrink: 0;
  background: var(--brand, #2563eb);
  animation: cpPulse 1.5s ease-out infinite;
}
@keyframes cpPulse {
  0%   { box-shadow: 0 0 0 0 rgba(37, 99, 235, .50); }
  70%  { box-shadow: 0 0 0 9px rgba(37, 99, 235, 0); }
  100% { box-shadow: 0 0 0 0 rgba(37, 99, 235, 0); }
}
.cp-mid { flex: 0 1 440px; min-width: 180px; }
.cp-line { display: flex; align-items: baseline; gap: 8px; font-size: 12px; color: var(--text-2); }
.cp-line b { font-weight: 700; color: var(--text-1); }
.cp-nums { font-variant-numeric: tabular-nums; color: var(--text-2); }
.cp-sub { font-size: 11px; color: var(--text-3); font-variant-numeric: tabular-nums; }
.cp-sub i { font-style: normal; font-weight: 600; }
.cp-sub .k-live { color: #047857; }
.cp-sub .k-dead { color: #b91c1c; }
.cp-sub .k-err  { color: #b45309; }

.cp-bar {
  margin-top: 4px; height: 5px; border-radius: 999px; overflow: hidden;
  background: rgba(37, 99, 235, .13);
}
.cp-bar > i {
  display: block; height: 100%; width: 0; border-radius: 999px;
  background: linear-gradient(90deg, #1d4ed8, #38bdf8, #1d4ed8);
  background-size: 200% 100%;
  transition: width .45s ease;
  animation: cpSheen 1.8s linear infinite;
}
@keyframes cpSheen { from { background-position: 0 0; } to { background-position: -200% 0; } }
/* 死卡复核轮：进度条转成琥珀色，跟初测区分开 */
.check-progress.round-retest .cp-bar > i { background: linear-gradient(90deg, #b45309, #fbbf24, #b45309); background-size: 200% 100%; }

.cp-cur {
  font-size: 12px; color: var(--text-3); font-variant-numeric: tabular-nums;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  max-width: 160px; flex-shrink: 0;
}

/* —— 「检测方式」三选一 —— */
.pick-list { display: flex; flex-direction: column; gap: 8px; }
.pick-item {
  display: flex; align-items: center; gap: 12px; width: 100%; text-align: left;
  padding: 12px 14px; border-radius: 12px; cursor: pointer; font: inherit;
  border: 1px solid var(--surface-line, rgba(37, 99, 235, .16));
  background: var(--surface-flat, #eaf1fb);
  transition: border-color .15s, box-shadow .15s, transform .08s;
}
.pick-item:hover { border-color: rgba(37, 99, 235, .5); box-shadow: 0 6px 18px rgba(31, 38, 135, .10); }
.pick-item:active { transform: scale(.995); }
.pick-item.off { opacity: .45; cursor: not-allowed; box-shadow: none; }
.pick-item.off:hover { border-color: var(--surface-line, rgba(37, 99, 235, .16)); }
.pick-item > svg { width: 18px; height: 18px; color: var(--brand, #2563eb); flex-shrink: 0; }
.pick-main { flex: 1; min-width: 0; }
.pick-main b { display: block; font-size: 13px; color: var(--text-1); }
.pick-main b em { font-style: normal; font-weight: 700; color: var(--brand, #2563eb); margin-left: 6px; }
.pick-main span { display: block; font-size: 11.5px; color: var(--text-3); margin-top: 3px; line-height: 1.5; }
.pick-go { width: 15px; height: 15px; color: var(--text-4); flex-shrink: 0; }
.pick-foot { margin-top: 10px; font-size: 11.5px; color: var(--text-3); line-height: 1.6; }

/* ==========================================================================
   启动遮罩（鉴权结论出来之前盖住整屏）
   --------------------------------------------------------------------------
   2026-10-15 用户：「每次刷新页面都会先进登录页 再进主页」——
   .login-screen 默认 display:flex，所以刷新时一定会先画登录页。
   这一层把它盖住，直到 showApp() / showLogin() / showBootError() 摘掉它。
   ⚠️ z-index 要高于 .login-screen 的 9999。
   ========================================================================== */
.boot-splash {
  position: fixed; inset: 0; z-index: 10050;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 16px;
  background:
    radial-gradient(900px 700px at 15% 0%, #dbeafe 0%, transparent 55%),
    radial-gradient(800px 600px at 95% 100%, #e0f2fe 0%, transparent 50%),
    linear-gradient(180deg, #eef5ff 0%, #e3edfb 100%);
  color: var(--text-3); font-size: 13px; letter-spacing: .01em;
}
.boot-splash.gone { display: none; }
.bs-ring {
  width: 34px; height: 34px; border-radius: 50%;
  border: 3px solid rgba(37, 99, 235, .18);
  border-top-color: var(--brand, #2563eb);
  animation: spin360 .8s linear infinite;   /* spin360 已在上文定义 */
}

/* ==========================================================================
   测卡配置区：提示文字与分隔线的间距 + cookie 测试结果动画
   ========================================================================== */

/* ⚠️ 2026-10-15 用户截图报：「横线下面的文字和横线没有间距 叠放在一起了」。
   根因：三个 .field-hint 都写死了内联 `style="margin:-4px 0 16px"` ——
   负的上边距把提示文字**提进了**上面那条分隔线（.ck-row 的 border-bottom）里，
   字号 12px/行高 1.5 时分半行距只有 3px，-4px 一拉就是负值：字和线叠在一起。
   这里统一给**正的**上边距，别再写负的。 */
.ck-hint { margin: 7px 0 18px; }

/* cookie 测试结果徽标：弹出来（用户 2026-10-15：「不管他是测试成功或者说无效都要有动画」） */
.ck-badge {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 3px 9px; border-radius: 999px;
  font-size: 11.5px; font-weight: 700; line-height: 1.7; white-space: nowrap;
  animation: ckBadgeIn .34s cubic-bezier(.34, 1.56, .64, 1);
}
.ck-badge svg { width: 13px; height: 13px; }
.ck-badge.ok  { color: #047857; background: rgba(16, 185, 129, .15); }
.ck-badge.bad { color: #b91c1c; background: rgba(244, 63, 94, .14); }
@keyframes ckBadgeIn {
  from { opacity: 0; transform: scale(.7) translateY(-4px); }
  to   { opacity: 1; transform: none; }
}
/* 测试中：按钮本身也给个呼吸感，别只是「灰掉」 */
.btn.ck-test.checking {
  border-color: var(--brand, #2563eb);
  color: var(--brand, #2563eb);
  font-weight: 500;
}
/* 无效原因：新粘贴（还没保存）的行也要能看到，不能只给已保存的行 */
.ck-mask.ck-err { color: #b91c1c; }

/* 余额不足：**仍算活卡**（用户定的口径），所以保持活卡的绿色系，
   不让人误判成死卡；但一眼要和普通「活卡」区分开 ——
   用户 2026-10-15 明确要求「余额不足状态就显示余额不足」。 */
.chk-tag.chk-funds {
  background: rgba(16, 185, 129, .10);
  box-shadow: inset 0 0 0 1px rgba(16, 185, 129, .5);
}

/* ==========================================================================
   无c生成（集成自 card-generator，2026-10-15）
   --------------------------------------------------------------------------
   两个编辑框：左边 BIN 输入、右边生成结果；下半部分是母卡库反查表。
   ⚠️ 用户明确要求「记得内部使用滚动条 不要无限增高页面」——
      所以生成结果 textarea **限高 + 自己滚**，反查表走 table-mode（表格内部滚 + 分页钉底）。
   ========================================================================== */
.gen-body { padding: 0 20px 20px; }
.gen-field-label {
  font-size: 12.5px; font-weight: 600; color: var(--text-3);
  margin: 0 0 6px;
}
.gen-input {
  width: 100%; padding: 10px 12px;
  border-radius: 10px; border: 1px solid var(--surface-line);
  background: var(--surface-flat); color: var(--text-1);
  font-size: 14px; letter-spacing: .4px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
.gen-input:focus { outline: none; border-color: var(--brand, #2563eb); }
.gen-opts {
  display: flex; flex-wrap: wrap; gap: 10px;
  margin: 12px 0 10px;
}
.gen-opts label {
  display: flex; align-items: center; gap: 6px;
  font-size: 12.5px; color: var(--text-3);
}
.gen-btns { display: flex; gap: 8px; }
/* ⚠️ 生成结果动辄几千行（上限 10000）：必须限高 + 内部滚动，
   否则一次 5000 行就把整页顶到几十屏高。 */
.gen-out {
  display: block; width: 100%;
  height: 236px; min-height: 140px; max-height: 236px;
  padding: 10px 12px; border-radius: 10px; resize: none;
  border: 1px solid var(--surface-line); background: var(--surface-flat);
  color: var(--text-1); font-size: 12.5px; line-height: 1.6;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  overflow: auto; white-space: pre;
}
.gen-out:focus { outline: none; border-color: var(--brand, #2563eb); }
.gen-out::-webkit-scrollbar { width: 9px; height: 9px; }
.gen-out::-webkit-scrollbar-track { background: transparent; }
.gen-out::-webkit-scrollbar-thumb {
  background: rgba(37, 99, 235, .18); border-radius: 9px;
  border: 2px solid transparent; background-clip: padding-box;
}
.gen-out::-webkit-scrollbar-thumb:hover { background: rgba(37, 99, 235, .32); background-clip: padding-box; }
/* 反查表列少，别跟着母卡表一起撑到 1080px 最小宽 */
.gen-table { min-width: 0; }
