:root{
  /* ---- TDplus 主题 ---- */
  --bg:#0E1116; --surface:#161B22; --surface2:#1B2129; --surface3:#222A34; --surface4:#2A3340;
  --border:rgba(255,255,255,.08); --border-strong:rgba(255,255,255,.16);
  --text:#e6edf3; --text-dim:#9ba7b4; --text-mute:#6b7785;
  --accent:#4DA3FF;          /* TDplus 主色：蓝 */
  --accent2:#7C5CFF;         /* 副色：紫（渐变第二站） */
  --accent-soft:rgba(77,163,255,.14);
  --accent-line:rgba(77,163,255,.38);
  --grad:linear-gradient(135deg,#4DA3FF 0%,#7C5CFF 100%);
  --ok:#34d399; --warn:#fbbf24; --err:#fb7185; --info:#60a5fa; --violet:#a78bfa;
  --ok-soft:rgba(52,211,153,.13); --warn-soft:rgba(251,191,36,.13);
  --err-soft:rgba(251,113,133,.13); --info-soft:rgba(96,165,250,.13);
  --r-sm:8px; --r:12px; --r-lg:16px; --r-pill:999px;
  --shadow:0 16px 48px rgba(0,0,0,.55);
  --shadow-sm:0 4px 16px rgba(0,0,0,.35);
  --glow:0 0 0 1px var(--accent-line), 0 8px 30px rgba(77,163,255,.22);
  --sidebar:240px;

  /* ---- 结构色（浅色主题会整体覆盖这一组）---- */
  --hover:rgba(77,163,255,.05);           /* 表格行 hover */
  --sel:rgba(77,163,255,.10);             /* 表格行选中 */
  --sel-strong:rgba(77,163,255,.15);
  --track:rgba(255,255,255,.09);          /* 进度条底槽 */
  --line-dash:rgba(255,255,255,.10);      /* 树的虚线 */
  --hover-line:rgba(255,255,255,.22);     /* 按钮 hover 边框 */
  --chip-soft:rgba(255,255,255,.06);
  --glass-sidebar:rgba(14,17,22,.74);
  --glass-topbar:rgba(14,17,22,.86);
  --glass-bar:rgba(22,27,34,.94);
  --glass-toast:rgba(22,27,34,.96);
  --glass-tip:rgba(22,27,34,.96);
  --scrim:rgba(6,8,11,.68);
  --scrim-soft:rgba(6,8,11,.55);
  --scroll-thumb:rgba(255,255,255,.13);
  --skel-a:rgba(255,255,255,.045); --skel-b:rgba(77,163,255,.12);
  --brand-text-1:#fff; --brand-text-2:#bcd8ff;
  --fg-info:#9ec5ff; --fg-ok:#7ee2b8; --fg-warn:#fbc38b; --fg-err:#ffb0bb;
  --fg-accent:#d3e8ff; --fg-accent-strong:#e2f0ff; --fg-accent-soft:#c3dcff;
  --danger-fg:#ffc2cc;
  --spin-track:rgba(255,255,255,.28);
  --spot-1:rgba(77,163,255,.16); --spot-2:rgba(124,92,255,.10); --spot-3:rgba(77,163,255,.06);
  --chart-grid:rgba(255,255,255,.06);
  color-scheme:dark;

  /* ---- 设计 token：节奏 / 动效 / 层次（深浅色各一套）---- */
  --t-fast:.14s; --t:.2s; --t-slow:.34s;
  --ease:cubic-bezier(.2,1,.3,1);
  --card-inset:inset 0 1px 0 rgba(255,255,255,.045);
  --card-shadow:none;
  --btn-shadow:0 1px 2px rgba(0,0,0,.25);
  --btn-shadow-hover:0 3px 10px rgba(0,0,0,.3);
  --row-alt:#12161c; --row-hover:#131a22; --row-sel:#16202c; --row-sel-strong:#1a2735;
  --focus-ring:0 0 0 4px var(--accent-soft);
}
*{box-sizing:border-box}
html,body{height:100%}
html{-webkit-text-size-adjust:100%; text-size-adjust:100%}
body{
  margin:0; background:var(--bg); color:var(--text);
  touch-action:manipulation;   /* 去掉移动端 300ms 点击延迟 */
  font:14px/1.55 -apple-system,BlinkMacSystemFont,"Segoe UI","Microsoft YaHei",Roboto,Helvetica,Arial,sans-serif;
  -webkit-font-smoothing:antialiased; text-rendering:optimizeLegibility;
}
body::before{
  content:""; position:fixed; inset:0; pointer-events:none; z-index:0;
  background:
    radial-gradient(1000px 560px at 8% -12%, var(--spot-1), transparent 62%),
    radial-gradient(820px 460px at 96% 2%, var(--spot-2), transparent 62%),
    radial-gradient(700px 700px at 60% 120%, var(--spot-3), transparent 60%);
}
a{color:var(--accent2); text-decoration:none}
h1,h2,h3,h4{margin:0; font-weight:600; letter-spacing:-.015em}
::selection{background:rgba(77,163,255,.35)}
:focus-visible{outline:2px solid var(--accent); outline-offset:2px; border-radius:8px; box-shadow:var(--focus-ring)}
kbd{font:11px/1 ui-monospace,Consolas,monospace; background:var(--surface3); border:1px solid var(--border-strong);
  border-bottom-width:2px; border-radius:5px; padding:2px 5px; color:var(--text-dim)}
::-webkit-scrollbar{width:10px;height:10px}
@media (pointer:coarse){::-webkit-scrollbar{width:4px;height:4px}}
::-webkit-scrollbar-thumb{background:var(--scroll-thumb); border-radius:99px; border:3px solid transparent; background-clip:content-box}
::-webkit-scrollbar-thumb:hover{background:rgba(77,163,255,.5); background-clip:content-box}
::-webkit-scrollbar-track{background:transparent}

/* ---------- 基础组件 ---------- */
.card{background:var(--surface); border:1px solid var(--border); border-radius:var(--r-lg); padding:18px;
  box-shadow:var(--card-inset), var(--card-shadow)}
.card.tight{padding:14px}
.card.hoverable{transition:border-color .15s, transform .12s}
.card.hoverable:hover{border-color:var(--border-strong)}
.row{display:flex; align-items:center; gap:10px}
.row.wrap{flex-wrap:wrap}
.col{display:flex; flex-direction:column; gap:10px}
.spacer{flex:1}
.muted{color:var(--text-dim)}
.mute2{color:var(--text-mute)}
.mono{font-family:ui-monospace,SFMono-Regular,Consolas,"Cascadia Mono",monospace}
.hidden{display:none !important}
.right{margin-left:auto}
.nowrap{white-space:nowrap}
.trunc{overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
.sep{height:1px; background:var(--border); margin:4px 0}

.btn{
  appearance:none; border:1px solid var(--border-strong); background:var(--surface2); color:var(--text);
  padding:7px 13px; border-radius:var(--r-sm); cursor:pointer; font-size:13px; font-weight:500;
  transition:background var(--t-fast),border-color var(--t-fast),transform .08s var(--ease),box-shadow var(--t-fast);
  white-space:nowrap; display:inline-flex; align-items:center; gap:6px; justify-content:center;
  box-shadow:var(--btn-shadow);
}
@media (hover:hover){
  .btn:hover{background:var(--surface3); border-color:var(--hover-line); box-shadow:var(--btn-shadow-hover)}
  .btn:active{transform:translateY(1px); box-shadow:var(--btn-shadow)}
}
.btn:disabled{box-shadow:none}
.btn:disabled{opacity:.38; cursor:not-allowed; transform:none}
.btn.primary{background:var(--grad); border-color:transparent; color:#fff; box-shadow:0 6px 20px rgba(77,163,255,.28)}
@media (hover:hover){ .btn.primary:hover{filter:brightness(1.08); box-shadow:0 8px 26px rgba(77,163,255,.42)} }
.btn.ghost{background:transparent; border-color:transparent; color:var(--text-dim); padding:6px 9px}
.btn.ghost:hover{background:var(--surface2); color:var(--text)}
.btn.danger{color:var(--danger-fg); border-color:rgba(251,113,133,.32); background:rgba(251,113,133,.06)}
.btn.danger:hover{background:rgba(251,113,133,.14)}
.btn.sm{padding:4px 9px; font-size:12px; border-radius:7px}
.btn.block{width:100%}
.btn.busy{pointer-events:none; opacity:.75}
.btn.busy::before{content:""; width:12px; height:12px; border-radius:50%; border:2px solid var(--spin-track); border-top-color:#fff; display:inline-block; animation:spin .7s linear infinite}
@keyframes spin{to{transform:rotate(360deg)}}
.btn.icon{padding:7px; width:32px}
/* 触屏指针：所有可点元素至少 40px 高，避免"点不中" */
@media (pointer:coarse){
  .btn{min-height:40px}
  .btn.sm{min-height:34px}
  .btn.icon{width:40px}
  .chip{padding:9px 14px}
  .nav a{padding:11px}
  tbody td{padding:10px}
  input[type=checkbox]{width:19px; height:19px}
}

.input,.select,.textarea{
  width:100%; background:var(--surface2); border:1px solid var(--border-strong); color:var(--text);
  border-radius:var(--r-sm); padding:8px 11px; font-size:13px; font-family:inherit; outline:none;
  transition:border-color .15s,box-shadow .15s,background .15s;
}
.input:hover,.select:hover{background:var(--surface3)}
.input:focus,.select:focus,.textarea:focus{border-color:var(--accent-line); box-shadow:0 0 0 3px var(--accent-soft); background:var(--surface2)}
.input::placeholder{color:var(--text-mute)}
.select{appearance:none; cursor:pointer; padding-right:28px;
  background-image:linear-gradient(45deg,transparent 50%,var(--text-dim) 50%),linear-gradient(135deg,var(--text-dim) 50%,transparent 50%);
  background-position:calc(100% - 15px) 52%,calc(100% - 10px) 52%; background-size:5px 5px,5px 5px; background-repeat:no-repeat}
label.field{display:block}
label.field > span{display:block; font-size:12px; color:var(--text-dim); margin-bottom:5px}
input[type=checkbox]{width:15px;height:15px; accent-color:var(--accent); cursor:pointer; vertical-align:middle}
.search{position:relative}
.search .input{padding-left:32px}
.search::before{content:"⌕"; position:absolute; left:10px; top:50%; transform:translateY(-50%); color:var(--text-mute); font-size:15px; pointer-events:none}

.badge{display:inline-flex; align-items:center; gap:4px; padding:2px 8px; border-radius:var(--r-pill);
  font-size:11px; font-weight:600; border:1px solid transparent; white-space:nowrap}
.badge.inactive{background:var(--info-soft); color:var(--fg-info)}
.badge.active{background:var(--ok-soft); color:var(--fg-ok)}
.badge.expired{background:var(--warn-soft); color:var(--fg-warn)}
.badge.banned{background:var(--err-soft); color:var(--fg-err)}
.badge.off{background:var(--chip-soft); color:var(--text-dim)}
.badge.brand{background:var(--accent-soft); color:var(--fg-accent-soft); border-color:var(--accent-line)}
.dot-live{width:6px;height:6px;border-radius:50%;background:var(--ok); box-shadow:0 0 0 0 rgba(52,211,153,.6); animation:pulse 2s infinite}
@keyframes pulse{0%{box-shadow:0 0 0 0 rgba(52,211,153,.5)}70%{box-shadow:0 0 0 7px rgba(52,211,153,0)}100%{box-shadow:0 0 0 0 rgba(52,211,153,0)}}

/* chips */
.chips{display:flex; gap:6px; flex-wrap:wrap}
.chip{border:1px solid var(--border-strong); background:var(--surface2); color:var(--text-dim);
  padding:4px 11px; border-radius:var(--r-pill); font-size:12px; cursor:pointer; transition:all .15s; user-select:none}
.chip:hover{background:var(--surface3); color:var(--text)}
.chip.on{background:var(--accent-soft); border-color:var(--accent-line); color:var(--fg-accent)}
.chip .n{opacity:.65; margin-left:4px}

/* table */
table{width:100%; border-collapse:separate; border-spacing:0; font-size:13px}
thead th{
  position:sticky; top:0; z-index:2; background:var(--surface2); text-align:left;
  padding:9px 10px; font-size:11.5px; font-weight:600; color:var(--text-dim); white-space:nowrap;
  border-bottom:1px solid var(--border-strong); letter-spacing:.02em;
}
/* 数字列等宽对齐，扫一眼就能比大小 */
.table-wrap.responsive tbody td{font-variant-numeric:tabular-nums}
@media (min-width:901px){
  /* 行底色改不透明：左侧固定列横向滚动时不会透出下层内容 */
  #codesTable tbody td{background:var(--surface)}
  /* ★ 必须用**不透明**底色：固定列浮在滚动内容之上，半透明会透出下面的字 */
  #codesTable tbody tr:nth-child(even) td{background:var(--row-alt)}
  #codesTable tbody tr:hover td{background:var(--row-hover)}
  #codesTable tbody tr.sel td{background:var(--row-sel)}
  #codesTable tbody tr.sel:hover td{background:var(--row-sel-strong)}
  /* 横向滚动时前两列（勾选 34px + 卡密）固定，不丢上下文。
     只对 #codesTable 生效 —— 规则表/统计表的第一列不是 34px，套用会互相压住。 */
  #codesTable thead th:nth-child(1),
  #codesTable tbody td:nth-child(1){position:sticky; left:0; z-index:3}
  #codesTable thead th:nth-child(2),
  #codesTable tbody td:nth-child(2){position:sticky; left:34px; z-index:3; box-shadow:1px 0 0 var(--border)}
  #codesTable thead th:nth-child(1),
  #codesTable thead th:nth-child(2){z-index:5; background:var(--surface2)}
}
thead th.sortable{cursor:pointer; user-select:none}
thead th.sortable:hover{color:var(--text)}
thead th .arrow{opacity:.35; font-size:9px; margin-left:3px}
thead th.sorted .arrow{opacity:1; color:var(--accent2)}
tbody td{padding:8px 10px; border-bottom:1px solid var(--border); white-space:nowrap}
tbody tr{transition:background var(--t-fast)}
@media (max-width:900px){
  tbody tr:hover{background:var(--hover)}
  tbody tr.sel{background:var(--sel)}
}
.code-cell .act-copy, .code-cell .act-more{padding:2px 7px; font-size:12px; line-height:1.4}
@media (min-width:901px){ .code-cell .act-more{display:none} }
.code-copy{opacity:.28; transition:opacity .15s}
tbody tr:hover .code-copy{opacity:1}
tbody tr.sel .code-copy{opacity:.85}
@media (max-width:900px){ tbody tr.sel:hover{background:var(--sel-strong)} }
.table-wrap{overflow:auto; border:1px solid var(--border); border-radius:var(--r); background:var(--surface)}
.empty{padding:56px 16px; text-align:center; color:var(--text-mute); font-size:13px}
.empty .big{font-size:30px; opacity:.35; margin-bottom:8px}

/* ---------- 窄屏：表格转卡片（每格带 data-l 标签） ---------- */
@media (max-width:900px){
  .table-wrap.responsive{border:0; background:transparent}
  .table-wrap.responsive table,
  .table-wrap.responsive tbody,
  .table-wrap.responsive tr,
  .table-wrap.responsive td{display:block; width:100%}
  .table-wrap.responsive thead{display:none}
  .table-wrap.responsive tbody tr{
    border:1px solid var(--border); border-radius:var(--r); background:var(--surface);
    margin-bottom:10px; padding:6px 4px}
  .table-wrap.responsive tbody td{
    border:0; padding:5px 10px; white-space:normal; display:flex; gap:10px; align-items:baseline}
  .table-wrap.responsive tbody td::before{
    content:attr(data-l); flex:0 0 74px; color:var(--text-mute); font-size:11.5px; line-height:1.6}
  .table-wrap.responsive tbody td[data-l=""]::before{display:none}
  /* ★ 卡密那一格：33 位卡密是**一整串没有空格的长 token**，flex 里不会换行，
     会把后面的 ⧉ / ⋯ 按钮顶出屏幕（实测 50 行全部溢出到 414px > 375）。
     做法：卡密单独占一行并允许任意位置折行，按钮换到下一行。 */
  .table-wrap.responsive tbody td.code-cell{flex-wrap:wrap; row-gap:4px}
  .table-wrap.responsive tbody td.code-cell .code-txt{flex:1 1 100%; word-break:break-all; overflow-wrap:anywhere; line-height:1.5}
  /* 行内 ⧉ / ⋯：手机上加到 40x34（原来 24x34，点不准） */
  .table-wrap.responsive tbody td.code-cell .act-copy,
  .table-wrap.responsive tbody td.code-cell .act-more{padding:6px 14px; font-size:13px; min-width:40px; margin-left:6px}
  .code-cell .act-copy, .code-cell .act-more{margin-left:4px}
  /* 勾选框：手机上 13x15 太小，放大到 20（用宽度断点兜底 pointer:coarse 不生效的环境） */
  .table-wrap.responsive tbody td input.ck{width:20px; height:20px; min-width:20px}
  input[type=checkbox]{width:20px; height:20px}
  /* 空备注不占一行 */
  .table-wrap.responsive tbody td[data-l="备注"]:empty{display:none}
  /* 工具栏在窄屏压扁一点 */
  .toolbar .btn.sm{padding:5px 10px}
  .table-wrap.responsive tbody tr.sel{border-color:var(--accent-line); background:rgba(77,163,255,.10)}
  .table-wrap.responsive tbody tr:hover{background:var(--surface)}
  .code-copy{opacity:1}
}

/* 选中操作条 */
.selbar{position:sticky; bottom:16px; z-index:30; margin-top:12px;
  background:var(--glass-bar); backdrop-filter:blur(14px); border:1px solid var(--accent-line);
  border-radius:var(--r); padding:10px 14px; display:flex; align-items:center; gap:8px;
  box-shadow:var(--shadow); animation:rise .18s ease}
@keyframes rise{from{opacity:0; transform:translateY(10px)}to{opacity:1; transform:none}}

/* 侧栏遮罩（窄屏打开导航时） */
.scrim{position:fixed; inset:0; background:var(--scrim-soft); backdrop-filter:blur(2px); z-index:99; animation:fade .16s ease}
@media(min-width:901px){.scrim{display:none}}

/* ---------- 模态 / toast ---------- */
.overlay{position:fixed; inset:0; background:var(--scrim); backdrop-filter:blur(4px); z-index:80;
  display:flex; align-items:center; justify-content:center; padding:20px; animation:fade .14s ease}
.modal{background:var(--surface); border:1px solid var(--border-strong); border-radius:var(--r-lg);
  box-shadow:var(--shadow); width:min(560px,100%); max-height:88vh; display:flex; flex-direction:column; animation:pop .18s cubic-bezier(.2,1,.3,1)}
.modal header{padding:16px 18px; border-bottom:1px solid var(--border); display:flex; align-items:center; gap:10px}
.modal header h3{font-size:15px}
.modal .body{padding:18px; overflow:auto; display:flex; flex-direction:column; gap:14px}
.modal footer{padding:14px 18px; border-top:1px solid var(--border); display:flex; gap:8px; justify-content:flex-end}
@keyframes fade{from{opacity:0}to{opacity:1}}
@keyframes pop{from{opacity:0; transform:translateY(10px) scale(.98)}to{opacity:1; transform:none}}

#toasts{position:fixed; top:calc(16px + env(safe-area-inset-top)); right:16px; z-index:200;
  display:flex; flex-direction:column; gap:8px; pointer-events:none; max-width:calc(100vw - 32px)}
@media (max-width:520px){
  #toasts{left:12px; right:12px; top:calc(10px + env(safe-area-inset-top))}
  .toast{max-width:none; width:100%}
}
.toast{pointer-events:auto; display:flex; align-items:flex-start; gap:10px;
  background:var(--glass-toast); backdrop-filter:blur(14px); border:1px solid var(--border-strong);
  border-left:3px solid var(--info); border-radius:var(--r); padding:11px 14px;
  box-shadow:var(--shadow); min-width:210px; max-width:360px; font-size:13px; animation:slide .22s cubic-bezier(.2,1,.3,1)}
.toast .ic{font-size:14px; line-height:1.35}
.toast.ok{border-left-color:var(--ok)} .toast.ok .ic{color:var(--ok)}
.toast.err{border-left-color:var(--err)} .toast.err .ic{color:var(--err)}
.toast.warn{border-left-color:var(--warn)} .toast.warn .ic{color:var(--warn)}
.toast.info .ic{color:var(--info)}
@keyframes slide{from{opacity:0; transform:translateX(20px)}to{opacity:1; transform:none}}

/* 图表：窄屏下允许横向滚动查看密集数据点 */
@media (max-width:900px){
  .chart-host{overflow-x:auto; -webkit-overflow-scrolling:touch}
}

/* ---------- 骨架 ---------- */
.skel{background:linear-gradient(90deg,var(--skel-a),var(--skel-b),var(--skel-a));
  background-size:200% 100%; animation:shim 1.3s linear infinite; border-radius:6px; height:12px}
@keyframes shim{from{background-position:200% 0}to{background-position:-200% 0}}
.skel-row{display:flex; gap:10px; padding:9px 10px; border-bottom:1px solid var(--border)}
.skel-row > *{height:13px}

/* ---------- 布局 ---------- */
#app{display:flex; min-height:100vh; min-height:100dvh; position:relative; z-index:1}
.sidebar{width:var(--sidebar); flex:0 0 var(--sidebar); background:var(--glass-sidebar); backdrop-filter:blur(16px);
  border-right:1px solid var(--border); padding:16px 12px; display:flex; flex-direction:column; gap:4px;
  position:sticky; top:0; height:100vh}
.brand{display:flex; align-items:center; gap:10px; padding:6px 10px 16px}
.brand .dot{width:30px;height:30px;border-radius:10px;background:var(--grad); display:grid; place-items:center;
  font-size:13px; font-weight:700; color:#fff; letter-spacing:-.5px; box-shadow:0 6px 18px rgba(77,163,255,.35)}
.brand b{font-size:15px; letter-spacing:-.02em; background:linear-gradient(120deg,var(--brand-text-1),var(--brand-text-2)); -webkit-background-clip:text; background-clip:text; -webkit-text-fill-color:transparent}
.nav{display:flex; flex-direction:column; gap:2px}
.nav a{display:flex; align-items:center; gap:10px; padding:8px 11px; border-radius:9px; color:var(--text-dim);
  font-size:13.5px; cursor:pointer; transition:background .13s,color .13s; position:relative}
.nav a:hover{background:var(--surface2); color:var(--text)}
.nav a.on{background:var(--accent-soft); color:var(--fg-accent-strong); box-shadow:inset 0 0 0 1px var(--accent-line)}
.nav a.on::before{content:""; position:absolute; left:-12px; top:50%; transform:translateY(-50%);
  width:3px; height:18px; border-radius:0 3px 3px 0; background:var(--grad)}
.nav a .ic{width:17px; text-align:center; opacity:.9; font-size:14px}
/* ---------- 501 未启用：页签置灰（保留页签，只是不可点） ---------- */
.nav a.nav-disabled{pointer-events:none; opacity:.45; cursor:not-allowed; filter:grayscale(.35)}
.nav a.nav-disabled:hover{background:transparent; color:var(--text-dim)}
.nav a .nav-tag{margin-left:auto; font-style:normal; font-size:10px; line-height:1; letter-spacing:0;
  padding:2px 5px; border-radius:6px; border:1px solid var(--border-strong);
  background:var(--surface2); color:var(--text-mute); flex:0 0 auto}
.nav a.nav-disabled .ic{opacity:.55}
.nav .sec{font-size:10.5px; color:var(--text-mute); padding:14px 11px 5px; letter-spacing:.08em; text-transform:uppercase}
.side-foot{margin-top:auto; padding:10px 11px; border-top:1px solid var(--border); display:flex; align-items:center; gap:8px; font-size:12px; color:var(--text-dim)}
.main{flex:1; min-width:0; display:flex; flex-direction:column}
.topbar{position:sticky; top:0; z-index:20; display:flex; align-items:center; gap:12px; padding:13px 22px;
  background:var(--glass-topbar); backdrop-filter:blur(14px); border-bottom:1px solid var(--border)}
.topbar h1{font-size:16.5px}
.topbar .sub{font-size:11.5px; color:var(--text-mute); margin-top:1px}
.content{padding:20px 22px 40px; flex:1; min-width:0}
.page{display:flex; flex-direction:column; gap:16px; animation:fadeUp .2s cubic-bezier(.2,1,.3,1)}
@keyframes fadeUp{from{opacity:0; transform:translateY(6px)}to{opacity:1; transform:none}}
.grid{display:grid; gap:14px}
.g-4{grid-template-columns:repeat(4,minmax(0,1fr))}
.g-3{grid-template-columns:repeat(3,minmax(0,1fr))}
.g-2{grid-template-columns:repeat(2,minmax(0,1fr))}
.stat{padding:15px 16px; position:relative; overflow:hidden}
.stat::after{content:""; position:absolute; right:-30px; top:-30px; width:90px; height:90px; border-radius:50%;
  background:var(--grad); opacity:.07; filter:blur(14px)}
.stat .k{font-size:11.5px; color:var(--text-dim); display:flex; align-items:center; gap:6px}
.stat .v{font-size:27px; font-weight:650; letter-spacing:-.025em; margin-top:3px; font-variant-numeric:tabular-nums}
.stat .d{font-size:11.5px; margin-top:2px; display:flex; align-items:center; gap:4px}
.up{color:var(--ok)} .down{color:var(--err)}
.toolbar{display:flex; gap:8px; align-items:center; flex-wrap:wrap}
.split{display:flex; gap:14px; align-items:flex-start}
.pane-tree{flex:0 0 250px; position:sticky; top:76px; max-height:calc(100vh - 108px); overflow:auto}
.pane-list{flex:1; min-width:0; display:flex; flex-direction:column; gap:12px}
.pager{display:flex; align-items:center; gap:8px; padding:4px 2px; font-size:12.5px; color:var(--text-dim); flex-wrap:wrap}
.bar{height:6px; border-radius:99px; background:var(--track); overflow:hidden}
.bar > i{display:block; height:100%; background:var(--grad); transition:width .4s}
.tree{list-style:none; margin:0; padding:0; font-size:13px}
.tree ul{list-style:none; margin:0; padding-left:14px; border-left:1px dashed var(--line-dash)}
.tree li{margin:1px 0}
.tree .node{display:flex; align-items:center; gap:7px; padding:5px 8px; border-radius:8px; cursor:pointer; transition:background .12s}
.tree .node:hover{background:var(--surface2)}
.tree .node.on{background:var(--accent-soft); box-shadow:inset 0 0 0 1px var(--accent-line)}
.tree .tg{width:14px; text-align:center; color:var(--text-mute); font-size:10px; flex:0 0 14px}
.tree .nm{flex:1; overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
.tree .num{font-size:11px; color:var(--text-mute); font-variant-numeric:tabular-nums}
@media(max-width:1080px){
  .g-4{grid-template-columns:repeat(2,minmax(0,1fr))}
  /* ★ [修复] .split 是 align-items:flex-start 的横向 flex；改成纵向后交叉轴不再拉伸，
     子项会按 **max-content** 撑开 —— 卡密列表那张 nowrap 表格把 .pane-list 撑到 1089px，
     在 1024 宽的屏幕上整页横向溢出（浏览器实测 docScroll=1351 > 1024）。
     纵向排列时必须显式 align-items:stretch（或给子项 width:100%）。 */
  .split{flex-direction:column; align-items:stretch}
  .pane-list{width:100%}
  .pane-tree{flex:none; width:100%; position:static; max-height:none}
}
@media(max-width:900px){
  .sidebar{position:fixed; left:0; top:0; z-index:100; transform:translateX(-100%);
    transition:transform .22s cubic-bezier(.2,1,.3,1); width:min(82vw,300px); flex-basis:auto;
    padding-bottom:calc(16px + env(safe-area-inset-bottom))}
  .sidebar.open{transform:none; box-shadow:var(--shadow)}
  .g-4{grid-template-columns:repeat(2,minmax(0,1fr))}
  .g-3,.g-2{grid-template-columns:1fr}
  .stat{padding:12px 13px}
  .stat .v{font-size:23px}
  .content{padding:10px 10px calc(26px + env(safe-area-inset-bottom))}
  .topbar{padding:10px 12px}
  #burger{display:inline-flex !important}
  .hide-sm{display:none !important}
  /* 工具栏在窄屏一律换行，输入框占满整行 */
  .toolbar{gap:6px}
  .toolbar > .input, .toolbar > .select, .toolbar > .search, .toolbar > .chips{flex:1 1 100%; min-width:0}
  .toolbar .search{width:100% !important}
  .split{gap:10px}
  /* ★ 手机上"数据范围"树要占 200px 以上，把列表挤到 530px 以下才出现 ——
     窄屏直接收进筛选弹层里的下拉（#fAgentSel），页面不再放树 */
  .pane-tree{display:none !important}
  .page{gap:11px}
  .card.tight{padding:12px}
  .card{padding:15px}
  /* 底部操作条：吸底、占满宽度 */
  .selbar{bottom:calc(8px + env(safe-area-inset-bottom)); flex-wrap:wrap; padding:9px 10px}
  .selbar .btn{flex:1 1 auto}
  /* 弹窗变成底部抽屉，拇指可及 */
  .overlay{align-items:flex-end; padding:0}
  .modal{width:100%; max-width:none; max-height:92vh; border-radius:var(--r-lg) var(--r-lg) 0 0;
    animation:sheet .22s cubic-bezier(.2,1,.3,1)}
  .modal footer{padding-bottom:calc(14px + env(safe-area-inset-bottom))}
  .modal footer .btn{flex:1}
  .topbar h1{font-size:15px}
  .topbar .sub{font-size:11px}
  .modal .body .row{flex-wrap:wrap}
  .modal .body .row > *{flex:1 1 100% !important}
}
@keyframes sheet{from{opacity:0; transform:translateY(24px)}to{opacity:1; transform:none}}
@media(max-width:520px){ #tally .tally-states{display:none} }
@media(max-width:480px){
  .g-4{grid-template-columns:1fr}
}
@media(max-width:420px){
  .chips{gap:5px}
  .chip{font-size:11.5px; padding:5px 10px}
  .stat .v{font-size:23px}
}

/* iOS 聚焦时若字号 <16px 会自动放大页面：把输入框钉在 16px */
@media (max-width:900px){
  .input,.select,.textarea{font-size:16px}
}
@media (prefers-reduced-motion: reduce){
  *, *::before, *::after{ animation-duration:.01ms !important; animation-iteration-count:1 !important; transition-duration:.01ms !important }
}
#burger{display:none}

/* =============================================================================
   深浅色主题
   ★ 两份选择器必须保持**同一套变量**（同一段文本生成，改一处即两处）：
     ① [data-theme="light"] —— JS 按"用户选择 → 否则跟随系统"写在 <html> 上
     ② prefers-color-scheme —— 没跑 JS 时的兜底（媒体查询直接生效）
   ============================================================================= */
:root[data-theme="light"]{
  --bg:#f4f6fa; --surface:#ffffff; --surface2:#f3f6fb; --surface3:#e9eef6; --surface4:#dfe6f0;
  --border:rgba(17,24,38,.09); --border-strong:rgba(17,24,38,.17);
  --text:#141a26; --text-dim:#535d6e; --text-mute:#828b9a;
  --accent:#2C6FD1; --accent2:#7C5CFF;
  --accent-soft:rgba(77,163,255,.12); --accent-line:rgba(77,163,255,.34);
  --grad:linear-gradient(135deg,#4DA3FF 0%,#7C5CFF 100%);
  --ok:#047857; --warn:#b45309; --err:#dc2626; --info:#2563eb; --violet:#6D4AFF;
  --ok-soft:rgba(4,120,87,.12); --warn-soft:rgba(180,83,9,.12);
  --err-soft:rgba(220,38,38,.10); --info-soft:rgba(37,99,235,.10);
  --shadow:0 14px 40px rgba(14,24,45,.13); --shadow-sm:0 4px 14px rgba(14,24,45,.10);
  --glow:0 0 0 1px var(--accent-line), 0 8px 26px rgba(77,163,255,.16);
  --hover:rgba(77,163,255,.06); --sel:rgba(77,163,255,.11); --sel-strong:rgba(77,163,255,.16);
  --track:rgba(17,24,38,.08); --line-dash:rgba(17,24,38,.12); --hover-line:rgba(17,24,38,.22);
  --chip-soft:rgba(17,24,38,.06);
  --glass-sidebar:rgba(255,255,255,.80); --glass-topbar:rgba(250,252,255,.88);
  --glass-bar:rgba(255,255,255,.95); --glass-toast:rgba(255,255,255,.97); --glass-tip:rgba(255,255,255,.98);
  --scrim:rgba(14,20,32,.42); --scrim-soft:rgba(14,20,32,.30);
  --scroll-thumb:rgba(17,24,38,.18);
  --skel-a:rgba(17,24,38,.05); --skel-b:rgba(77,163,255,.14);
  --brand-text-1:#141a26; --brand-text-2:#2C6FD1;
  --fg-info:#1d4ed8; --fg-ok:#047857; --fg-warn:#b45309; --fg-err:#be123c;
  --fg-accent:#1F5FB8; --fg-accent-strong:#184C93; --fg-accent-soft:#2C6FD1;
  --danger-fg:#be123c;
  --spin-track:rgba(17,24,38,.25);
  --spot-1:rgba(77,163,255,.12); --spot-2:rgba(124,92,255,.08); --spot-3:rgba(77,163,255,.05);
  --chart-grid:rgba(17,24,38,.08);
  --card-inset:inset 0 1px 0 rgba(255,255,255,.9);
  --card-shadow:0 1px 2px rgba(14,24,45,.04);
  --btn-shadow:0 1px 2px rgba(14,24,45,.08);
  --btn-shadow-hover:0 3px 10px rgba(14,24,45,.12);
  --row-alt:#fafcff; --row-hover:#f4f8ff; --row-sel:#eaf2ff; --row-sel-strong:#e2edff;
  --focus-ring:0 0 0 4px var(--accent-soft);
  color-scheme:light;
}
@media (prefers-color-scheme: light){
  :root:not([data-theme]){
  /* ★ 这一段必须与上面的 :root[data-theme="light"] 保持完全一致（同一套 token），
     否则"跟随系统浅色"时会缺变量 → 底色/焦点环等声明失效。 */
  --bg:#f4f6fa; --surface:#ffffff; --surface2:#f3f6fb; --surface3:#e9eef6; --surface4:#dfe6f0;
  --border:rgba(17,24,38,.09); --border-strong:rgba(17,24,38,.17);
  --text:#141a26; --text-dim:#535d6e; --text-mute:#828b9a;
  --accent:#2C6FD1; --accent2:#7C5CFF;
  --accent-soft:rgba(77,163,255,.12); --accent-line:rgba(77,163,255,.34);
  --grad:linear-gradient(135deg,#4DA3FF 0%,#7C5CFF 100%);
  --ok:#047857; --warn:#b45309; --err:#dc2626; --info:#2563eb; --violet:#6D4AFF;
  --ok-soft:rgba(4,120,87,.12); --warn-soft:rgba(180,83,9,.12);
  --err-soft:rgba(220,38,38,.10); --info-soft:rgba(37,99,235,.10);
  --shadow:0 14px 40px rgba(14,24,45,.13); --shadow-sm:0 4px 14px rgba(14,24,45,.10);
  --glow:0 0 0 1px var(--accent-line), 0 8px 26px rgba(77,163,255,.16);
  --hover:rgba(77,163,255,.06); --sel:rgba(77,163,255,.11); --sel-strong:rgba(77,163,255,.16);
  --track:rgba(17,24,38,.08); --line-dash:rgba(17,24,38,.12); --hover-line:rgba(17,24,38,.22);
  --chip-soft:rgba(17,24,38,.06);
  --glass-sidebar:rgba(255,255,255,.80); --glass-topbar:rgba(250,252,255,.88);
  --glass-bar:rgba(255,255,255,.95); --glass-toast:rgba(255,255,255,.97); --glass-tip:rgba(255,255,255,.98);
  --scrim:rgba(14,20,32,.42); --scrim-soft:rgba(14,20,32,.30);
  --scroll-thumb:rgba(17,24,38,.18);
  --skel-a:rgba(17,24,38,.05); --skel-b:rgba(77,163,255,.14);
  --brand-text-1:#141a26; --brand-text-2:#2C6FD1;
  --fg-info:#1d4ed8; --fg-ok:#047857; --fg-warn:#b45309; --fg-err:#be123c;
  --fg-accent:#1F5FB8; --fg-accent-strong:#184C93; --fg-accent-soft:#2C6FD1;
  --danger-fg:#be123c;
  --spin-track:rgba(17,24,38,.25);
  --spot-1:rgba(77,163,255,.12); --spot-2:rgba(124,92,255,.08); --spot-3:rgba(77,163,255,.05);
  --chart-grid:rgba(17,24,38,.08);
  --card-inset:inset 0 1px 0 rgba(255,255,255,.9);
  --card-shadow:0 1px 2px rgba(14,24,45,.04);
  --btn-shadow:0 1px 2px rgba(14,24,45,.08);
  --btn-shadow-hover:0 3px 10px rgba(14,24,45,.12);
  --row-alt:#fafcff; --row-hover:#f4f8ff; --row-sel:#eaf2ff; --row-sel-strong:#e2edff;
  --focus-ring:0 0 0 4px var(--accent-soft);
  color-scheme:light;
  }
}

/* ---------- 图表悬停读数（原来写在 ui.js 的行内样式里，浅色下会看不清） ---------- */
.tip{position:absolute; pointer-events:none; background:var(--glass-tip); border:1px solid var(--border-strong);
  border-radius:10px; padding:8px 11px; font-size:12px; box-shadow:var(--shadow-sm); z-index:5; min-width:150px;
  color:var(--text)}

/* ---------- 触底加载 / 下拉刷新 ---------- */
.load-more{display:flex; align-items:center; justify-content:center; gap:8px; padding:14px 10px;
  color:var(--text-mute); font-size:12.5px; min-height:44px}
.load-more .spin{width:14px; height:14px; border-radius:50%; border:2px solid var(--spin-track);
  border-top-color:var(--accent); animation:spin .7s linear infinite}
.pull-hint{position:fixed; left:0; right:0; top:0; z-index:60; display:flex; align-items:center;
  justify-content:center; gap:6px; height:0; overflow:hidden; font-size:12.5px; color:var(--text-dim);
  background:var(--glass-topbar); backdrop-filter:blur(10px); transition:height .16s ease;
  padding-top:env(safe-area-inset-top)}
.pull-hint.on{height:44px}
.pull-hint .spin{width:13px; height:13px; border-radius:50%; border:2px solid var(--spin-track);
  border-top-color:var(--accent); animation:spin .7s linear infinite}

/* ---------- 卡片模式：左滑操作条 ---------- */
.swipe-bar{display:none}
@media (max-width:900px){
  .table-wrap.responsive tbody tr{position:relative}
  /* 卡片被左滑（或点了 ⋯）后展开操作条 */
  .table-wrap.responsive tbody tr.swiped{box-shadow:inset 0 0 0 1px var(--accent-line)}
  .table-wrap.responsive tbody tr.swiped .swipe-bar{
    display:flex; gap:6px; padding:8px 10px 2px; flex-wrap:wrap; border-top:1px dashed var(--line-dash); margin-top:6px}
  .table-wrap.responsive tbody tr .swipe-bar .btn{flex:1 1 auto}
  .act-more{display:inline-flex}
  .swipe-hint{font-size:11px; color:var(--text-mute); padding:0 10px 2px}
}
@media (min-width:901px){
  .act-more{display:none}
  .act-cell{display:none !important}   /* 左滑操作条那一格：桌面端不占列 */
}

/* 触摸设备上禁掉卡片的长按选中，避免滑动时选中文字 */
@media (pointer:coarse){ .table-wrap.responsive tbody tr{ -webkit-user-select:none; user-select:none } }

/* ---------- 响应式工具类 ---------- */
@media (min-width:901px){ .only-sm{display:none !important} }
.chips-row{display:flex; align-items:center; gap:8px; flex-wrap:wrap}
.chips-row .chips{flex:1 1 auto; min-width:0}
@media (max-width:900px){
  /* 状态标签 + 筛选按钮必须在**同一行**：flex-wrap 会让按钮掉到第二行，
     .chips-row 从 36px 变成 78px（实测），白白多占 42px */
  .chips-row{flex-wrap:nowrap}
  .chips-row #fSheet{flex:0 0 auto}
  /* 快捷标签一行横滑，省纵向空间 */
  .chips{flex-wrap:nowrap; overflow-x:auto; -webkit-overflow-scrolling:touch; scrollbar-width:none; padding-bottom:2px}
  .chips::-webkit-scrollbar{display:none}
  .chips .chip{flex:0 0 auto}
  .chip{min-height:34px; display:inline-flex; align-items:center}
  /* 代理树节点在窄屏换行，避免长代理名把按钮顶出去 */
  .tree .node{flex-wrap:wrap; row-gap:4px}
  .tree .node .num{flex:1 1 100%; padding-left:21px}
  /* 移动端筛选面板默认收进底部弹层 */
  #filterHost{display:none !important}
  /* 字段行更紧凑（原来 5px 上下内边距，一张卡 378px 高，一屏只放得下 1 张） */
  .table-wrap.responsive tbody td{padding:3px 10px; line-height:1.45}
  /* 卡密那一格不要"卡密"标签：33 位卡密本身一眼就是卡密，
     省下 74px 标签位后，卡密 + ⧉ + ⋯ 能排在同一行（少一整行 ≈34px） */
  .table-wrap.responsive tbody td.code-cell{padding-top:7px}
  .table-wrap.responsive tbody td.code-cell::before{display:none}
  .table-wrap.responsive tbody td.code-cell .code-txt{flex:1 1 auto; font-weight:600}
  /* 没左滑时那一格完全不占位（原来留一条 8px 的空行） */
  .table-wrap.responsive tbody tr:not(.swiped) .act-cell{display:none}
  /* 把 tr 变成可换行的 flex：让短字段两两并排（实测一张满字段卡 348px → 省两行 ≈50px） */
  .table-wrap.responsive tbody tr{display:flex; flex-wrap:wrap}
  .table-wrap.responsive tbody td{flex:1 1 100%; min-width:0}
  .table-wrap.responsive tbody td[data-l="规则"],
  .table-wrap.responsive tbody td[data-l="状态"],
  .table-wrap.responsive tbody td[data-l="激活时间"],
  .table-wrap.responsive tbody td[data-l="到期时间"]{flex:1 1 48%; width:auto; max-width:48%}
  .table-wrap.responsive tbody td[data-l="规则"]::before,
  .table-wrap.responsive tbody td[data-l="状态"]::before,
  .table-wrap.responsive tbody td[data-l="激活时间"]::before,
  .table-wrap.responsive tbody td[data-l="到期时间"]::before{flex:0 0 50px}
  .table-wrap.responsive tbody td[data-l="激活时间"],
  .table-wrap.responsive tbody td[data-l="到期时间"]{font-size:12.5px}
  /* 卡密格按钮再收一点，让"卡密 + ⧉ + ⋯"挤进一行 */
  .table-wrap.responsive tbody td.code-cell .act-copy,
  .table-wrap.responsive tbody td.code-cell .act-more{padding:6px 9px; min-width:34px; margin-left:5px}
  /* 空字段（"—" / 空）在窄屏不占行 */
  .table-wrap.responsive tbody td[data-empty]{display:none}
  /* 统计/导出行压扁 */
  .tally-card{padding:6px 10px}
  .tally-card .toolbar{gap:6px}
  /* 顶栏图标按钮与分页按钮在窄屏放大（pointer:coarse 不一定生效，用宽度兜底） */
  .topbar .btn.icon{width:40px; height:40px}
  .side-foot .btn{min-width:40px; min-height:36px}
  .pager .btn{min-width:40px; min-height:34px}
  .filter-card .search{margin-top:2px}
}

/* ---------- 空态图标 ---------- */
.empty .ico{display:block; width:46px; height:46px; margin:0 auto 10px; opacity:.45}
.empty .ico *{fill:none; stroke:var(--text-mute); stroke-width:1.5; stroke-linecap:round; stroke-linejoin:round}

/* ---------- 表格密度（只作用于桌面表格） ---------- */
@media (min-width:901px){
  #app[data-density="compact"] .table-wrap.responsive tbody td{padding:4px 9px; font-size:12.5px}
  #app[data-density="compact"] .table-wrap.responsive thead th{padding:6px 9px}
  #app[data-density="compact"] .code-cell .code-txt{font-size:12px}
}

/* ---------- 表单内联错误 ---------- */
.field-err{display:none; font-size:11.5px; color:var(--fg-err); margin-top:5px}
.field-err.on{display:block}
label.field.has-err .input, label.field.has-err .select{border-color:rgba(251,113,133,.55)}

/* ---------- 快捷键面板 ---------- */
.kbd-row{display:flex; align-items:center; gap:10px; padding:6px 0; border-bottom:1px dashed var(--line-dash)}
.kbd-row:last-child{border-bottom:0}
.kbd-row kbd{flex:0 0 auto; min-width:64px; text-align:center}
