/* ─────────────────────────────────────────────
   LoveAPI — 设计系统 v2
   Premium SaaS aesthetic · Emerald accent
   ───────────────────────────────────────────── */

@import url('https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,300..800;1,9..40,300..800&family=JetBrains+Mono:wght@400;500&display=swap');

*{margin:0;padding:0;box-sizing:border-box}

:root{--ease:cubic-bezier(.4,0,.2,1);--ease-out:cubic-bezier(0,0,.2,1);--ease-spring:cubic-bezier(.34,1.56,.64,1)}

[data-theme="light"]{
  --bg:#fff;--bg-alt:#fafafa;--surface:#f5f5f5;--surface-hover:#ebebeb;
  --border:#e5e5e5;--border-light:#f0f0f0;
  --text:#111;--text-s:#555;--text-m:#999;
  --brand:#111;--brand-text:#fff;
  --accent:#10a37f;--accent-text:#fff;--accent-soft:rgba(16,163,127,.08);
  --blue:#2563eb;--blue-soft:rgba(37,99,235,.08);
  --green:#10a37f;--danger:#ef4444;--warn:#ca8a04;
  --user-msg-bg:#f5f5f5;
  --sidebar-bg:#fafafa;--sidebar-hover:#efefef;
  --code-bg:#f6f6f6;--code-border:#e8e8e8;
  --dd-bg:#fff;--dd-shadow:rgba(0,0,0,.08);
  --ring:0 0 0 2px rgba(16,163,127,.3);
  --scrollbar:#ddd;--scrollbar-hover:#bbb;
  --badge-active-bg:rgba(16,163,127,.1);--badge-active-fg:#0d8a6a;
  --badge-warn-bg:rgba(202,138,4,.1);--badge-warn-fg:#a16207;
  --badge-danger-bg:rgba(239,68,68,.1);--badge-danger-fg:#dc2626;
  --badge-blue-bg:rgba(37,99,235,.08);--badge-blue-fg:#2563eb;
  --shadow-sm:0 1px 2px rgba(0,0,0,.04);
  --shadow:0 2px 8px rgba(0,0,0,.06);
  --shadow-lg:0 16px 48px rgba(0,0,0,.1);
  --glass:rgba(255,255,255,.7);
}
[data-theme="dark"]{
  --bg:#171717;--bg-alt:#111;--surface:#262626;--surface-hover:#333;
  --border:#333;--border-light:#2a2a2a;
  --text:#f5f5f5;--text-s:#a3a3a3;--text-m:#737373;
  --brand:#f5f5f5;--brand-text:#171717;
  --accent:#34d399;--accent-text:#111;--accent-soft:rgba(52,211,153,.1);
  --blue:#60a5fa;--blue-soft:rgba(96,165,250,.1);
  --green:#34d399;--danger:#f87171;--warn:#fbbf24;
  --user-msg-bg:#262626;
  --sidebar-bg:#111;--sidebar-hover:#222;
  --code-bg:#1a1a1a;--code-border:#333;
  --dd-bg:#222;--dd-shadow:rgba(0,0,0,.4);
  --ring:0 0 0 2px rgba(52,211,153,.3);
  --scrollbar:#444;--scrollbar-hover:#555;
  --badge-active-bg:rgba(52,211,153,.15);--badge-active-fg:#34d399;
  --badge-warn-bg:rgba(251,191,36,.15);--badge-warn-fg:#fbbf24;
  --badge-danger-bg:rgba(248,113,113,.15);--badge-danger-fg:#f87171;
  --badge-blue-bg:rgba(96,165,250,.15);--badge-blue-fg:#60a5fa;
  --shadow-sm:0 1px 2px rgba(0,0,0,.2);
  --shadow:0 2px 8px rgba(0,0,0,.3);
  --shadow-lg:0 16px 48px rgba(0,0,0,.5);
  --glass:rgba(23,23,23,.8);
}

body{
  font-family:'DM Sans',-apple-system,BlinkMacSystemFont,"Noto Sans SC",sans-serif;
  background:var(--bg);color:var(--text);-webkit-font-smoothing:antialiased;
  transition:background .3s var(--ease),color .2s;
}
button,input,textarea,select{font-family:inherit}
button{cursor:pointer}
a{color:inherit}

/* Focus */
:focus-visible{outline:none;box-shadow:var(--ring)}

/* Scrollbar */
::-webkit-scrollbar{width:5px;height:5px}
::-webkit-scrollbar-track{background:transparent}
::-webkit-scrollbar-thumb{background:var(--scrollbar);border-radius:10px}
::-webkit-scrollbar-thumb:hover{background:var(--scrollbar-hover)}

/* Reduce motion */
@media(prefers-reduced-motion:reduce){*,*::before,*::after{animation-duration:0.01ms!important;transition-duration:0.01ms!important}}

/* ── 图标按钮 ───────────────────────────────── */

.btn-icon{
  width:36px;height:36px;border-radius:10px;border:none;background:transparent;
  color:var(--text-m);display:flex;align-items:center;justify-content:center;
  transition:all .15s var(--ease);flex-shrink:0;
}
.btn-icon:hover{background:var(--surface-hover);color:var(--text)}
.btn-icon svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
.btn-icon.sm{width:30px;height:30px}
.btn-icon.sm svg{width:16px;height:16px}

/* ── 侧边栏 ─────────────────────────────────── */

.sidebar{
  display:flex;flex-direction:column;flex-shrink:0;background:var(--sidebar-bg);
  overflow:hidden;position:relative;z-index:10;
  border-right:1px solid var(--border-light);
  transition:width .25s var(--ease);
}
.sidebar[data-state="expanded"]{width:264px}
.sidebar[data-state="rail"]{width:56px}
.sidebar[data-state="hidden"]{width:0;border-right:none}

.sb-expanded{display:flex;flex-direction:column;height:100%;min-width:264px}
.sidebar[data-state="rail"] .sb-expanded,
.sidebar[data-state="hidden"] .sb-expanded{opacity:0;pointer-events:none;position:absolute}

.sb-rail{display:flex;flex-direction:column;align-items:center;height:100%;width:56px;padding:10px 0;gap:4px}
.sidebar[data-state="expanded"] .sb-rail,
.sidebar[data-state="hidden"] .sb-rail{display:none}
.sb-rail .rail-bottom{margin-top:auto;padding-bottom:6px}

/* 品牌栏 */
.sb-brand{display:flex;align-items:center;gap:9px;padding:12px 12px 10px}
.brand-mark{
  width:28px;height:28px;border-radius:8px;
  background:linear-gradient(135deg,#10a37f,#0d8a6a);
  color:#fff;display:flex;align-items:center;justify-content:center;
  font-size:14px;font-weight:700;flex-shrink:0;letter-spacing:-.5px;
  box-shadow:0 2px 8px rgba(16,163,127,.3);
}
.brand-name{font-size:15px;font-weight:700;letter-spacing:-.3px;flex:1;white-space:nowrap}

/* 导航 */
.sb-nav{padding:2px 8px 8px;display:flex;flex-direction:column;gap:1px}
.nav-item{
  display:flex;align-items:center;gap:10px;padding:8px 10px;border-radius:10px;
  border:none;background:transparent;color:var(--text-s);
  font-size:13.5px;text-align:left;text-decoration:none;width:100%;
  transition:all .12s var(--ease);position:relative;
}
.nav-item:hover{background:var(--surface-hover);color:var(--text)}
.nav-item.active{background:var(--accent-soft);color:var(--accent);font-weight:600}
.nav-item svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round;flex-shrink:0}
.nav-item .nav-label{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

/* 通知圆点 */
.nav-item .dot{width:6px;height:6px;border-radius:50%;background:var(--danger);position:absolute;right:8px;top:8px;animation:dotPulse 2s ease infinite}
@keyframes dotPulse{0%,100%{opacity:1}50%{opacity:.4}}

/* 搜索 */
.sb-search{padding:0 12px 8px}
.sb-search input{
  width:100%;padding:8px 12px;background:var(--surface);
  border:1px solid transparent;border-radius:10px;
  color:var(--text);font-size:13px;outline:none;
  transition:all .2s var(--ease);
}
.sb-search input:focus{border-color:var(--accent);background:var(--bg);box-shadow:0 0 0 3px var(--accent-soft)}
.sb-search input::placeholder{color:var(--text-m)}

/* 分区标题 */
.section-head{
  display:flex;align-items:center;gap:6px;width:100%;
  padding:6px 10px;margin:2px 8px 2px;width:calc(100% - 16px);
  border:none;background:transparent;border-radius:6px;
  font-size:11px;font-weight:600;color:var(--text-m);letter-spacing:.5px;text-transform:uppercase;
  transition:color .1s;
}
.section-head:hover{color:var(--text-s)}
.section-head .chev{width:14px;height:14px;fill:none;stroke:currentColor;stroke-width:2.2;stroke-linecap:round;stroke-linejoin:round;transition:transform .18s;margin-left:auto}
.section-head.collapsed .chev{transform:rotate(-90deg)}

/* 用户卡 */
.sb-user{
  display:flex;align-items:center;gap:9px;margin:6px 8px 8px;padding:8px 10px;
  border-radius:12px;cursor:pointer;transition:background .15s var(--ease);
  border-top:1px solid transparent;
}
.sb-user:hover{background:var(--sidebar-hover)}
.u-avatar{
  width:30px;height:30px;border-radius:50%;
  background:linear-gradient(135deg,var(--green),#0d8a6a);
  display:flex;align-items:center;justify-content:center;
  font-size:12px;font-weight:700;color:#fff;flex-shrink:0;
}
.u-avatar.sm{width:28px;height:28px;font-size:11px}
.u-info{flex:1;min-width:0;line-height:1.3}
.u-name{font-size:13px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.u-sub{font-size:11px;color:var(--text-m);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:flex;gap:6px}
.u-sub .low{color:var(--danger);font-weight:600}
.sb-user .chev{width:15px;height:15px;fill:none;stroke:var(--text-m);stroke-width:2.2;stroke-linecap:round;stroke-linejoin:round;flex-shrink:0;transition:transform .2s var(--ease)}
.sb-user.open .chev{transform:rotate(180deg)}

/* 用户菜单 */
.user-menu{
  position:absolute;left:8px;right:8px;bottom:56px;z-index:60;
  background:var(--dd-bg);border:1px solid var(--border);border-radius:14px;
  padding:5px;box-shadow:var(--shadow-lg);display:none;
  backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);
}
.user-menu.open{display:block;animation:menuIn .15s var(--ease-out)}
@keyframes menuIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}
.um-row{
  display:flex;align-items:center;gap:10px;padding:8px 10px;border-radius:8px;
  border:none;background:transparent;color:var(--text-s);
  font-size:13.5px;width:100%;text-align:left;text-decoration:none;
  transition:all .1s var(--ease);
}
.um-row:hover{background:var(--surface-hover);color:var(--text)}
.um-row svg{width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;flex-shrink:0}
.um-sep{height:1px;background:var(--border-light);margin:4px 6px}
.um-row.danger:hover{color:var(--danger)}

/* ── 主区 / 顶栏 ────────────────────────────── */

.main{flex:1;display:flex;flex-direction:column;min-width:0;position:relative}

.topbar{
  height:54px;display:flex;align-items:center;gap:8px;padding:0 14px;flex-shrink:0;
  background:var(--bg);border-bottom:1px solid var(--border-light);
}
.tb-title{
  font-size:15px;font-weight:600;letter-spacing:-.2px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  min-width:0;flex:1;
}
.tb-actions{display:flex;align-items:center;gap:4px;flex-shrink:0}
.tb-toggle{display:none}

/* ── 按钮 ───────────────────────────────────── */

.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:6px;
  padding:8px 16px;border-radius:10px;border:1px solid transparent;
  font-size:13.5px;font-weight:600;
  transition:all .15s var(--ease);
}
.btn-primary{background:var(--accent);color:var(--accent-text)}
.btn-primary:hover{opacity:.9;transform:translateY(-1px);box-shadow:0 4px 12px rgba(16,163,127,.25)}
.btn-primary:active{transform:translateY(0)}
.btn-outline{background:transparent;border-color:var(--border);color:var(--text-s)}
.btn-outline:hover{background:var(--surface-hover);color:var(--text);border-color:var(--text-m)}
.btn-ghost{background:transparent;color:var(--text-s);border:1px solid var(--border)}
.btn-ghost:hover{background:var(--surface-hover);color:var(--text)}
.btn-danger{background:var(--danger);color:#fff}
.btn-danger:hover{opacity:.9;transform:translateY(-1px)}
.btn:disabled{opacity:.4;cursor:not-allowed;transform:none!important}
.btn-sm{padding:5px 12px;font-size:12px;border-radius:8px}
.btn svg{width:15px;height:15px;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}

/* Button loading state */
.btn.loading{pointer-events:none;opacity:.7}
.btn.loading::after{content:'';width:14px;height:14px;border:2px solid currentColor;border-top-color:transparent;border-radius:50%;animation:spin .5s linear infinite;margin-left:4px}

/* ── 卡片 / 徽章 ────────────────────────────── */

.card{
  background:var(--bg);border:1px solid var(--border);border-radius:14px;
  transition:box-shadow .2s var(--ease),transform .2s var(--ease);
}
.card:hover{box-shadow:var(--shadow)}

.badge{display:inline-flex;align-items:center;padding:3px 8px;border-radius:6px;font-size:11px;font-weight:600;letter-spacing:.2px}
.badge-active{background:var(--badge-active-bg);color:var(--badge-active-fg)}
.badge-rate_limited{background:var(--badge-warn-bg);color:var(--badge-warn-fg)}
.badge-expired,.badge-banned{background:var(--badge-danger-bg);color:var(--badge-danger-fg)}
.badge-pending{background:var(--badge-blue-bg);color:var(--badge-blue-fg)}
.badge-plan{background:var(--badge-blue-bg);color:var(--badge-blue-fg)}
.badge-warn{background:var(--badge-warn-bg);color:var(--badge-warn-fg)}

.mono{font-family:'JetBrains Mono','Cascadia Code',monospace;font-size:.92em}

/* ── Toast ─────────────────────────────────── */

.overlay{display:none;position:fixed;inset:0;background:rgba(0,0,0,.4);z-index:15;backdrop-filter:blur(2px)}
.overlay.open{display:block}

.toast{
  position:fixed;bottom:24px;right:24px;
  background:var(--surface);color:var(--text);
  border:1px solid var(--border);border-radius:12px;
  padding:12px 18px;font-size:13px;z-index:999;
  display:flex;align-items:center;gap:8px;
  transform:translateX(120%);opacity:0;
  transition:all .3s var(--ease-spring);
  box-shadow:var(--shadow-lg);max-width:360px;
}
.toast.show{transform:translateX(0);opacity:1}
.toast.err{border-color:var(--danger)}
.toast::before{content:'✓';font-size:15px;color:var(--green);flex-shrink:0}
.toast.err::before{content:'✗';color:var(--danger)}

/* ── 对话框 ─────────────────────────────────── */

dialog{
  background:var(--bg);color:var(--text);border:1px solid var(--border);
  border-radius:18px;padding:0;box-shadow:var(--shadow-lg);
  margin:auto;backdrop-filter:blur(20px);
}
dialog[open]{animation:dlgIn .2s var(--ease-out)}
dialog::backdrop{background:rgba(0,0,0,.5);backdrop-filter:blur(4px);animation:backdropIn .2s ease}
@keyframes dlgIn{from{opacity:0;transform:scale(.95) translateY(10px)}to{opacity:1;transform:scale(1) translateY(0)}}
@keyframes backdropIn{from{opacity:0}to{opacity:1}}

.dlg-head{display:flex;align-items:center;justify-content:space-between;padding:20px 24px 12px}
.dlg-head h2{font-size:17px;font-weight:700;letter-spacing:-.3px}
.dlg-head button{
  width:30px;height:30px;border-radius:8px;border:none;background:transparent;
  color:var(--text-m);display:flex;align-items:center;justify-content:center;
  transition:all .12s var(--ease);
}
.dlg-head button:hover{background:var(--surface-hover);color:var(--text)}
.dlg-head button svg{width:17px;height:17px;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round}
.dlg-body{padding:4px 24px 24px}
.dlg-label{font-size:12px;font-weight:600;color:var(--text-m);margin-bottom:8px;letter-spacing:.3px;text-transform:uppercase}

/* ── Spinner ────────────────────────────────── */

.spinner{
  width:18px;height:18px;border:2px solid var(--border);border-top-color:var(--accent);
  border-radius:50%;animation:spin .6s linear infinite;display:inline-block;vertical-align:middle;
}
.spinner.lg{width:28px;height:28px;border-width:2.5px}
@keyframes spin{to{transform:rotate(360deg)}}

.loading-center{display:flex;align-items:center;justify-content:center;gap:10px;padding:40px;color:var(--text-m);font-size:13px}

/* ── Typing indicator ──────────────────────── */

.typing-dots{display:inline-flex;gap:4px;align-items:center;padding:4px 0}
.typing-dots span{width:6px;height:6px;border-radius:50%;background:var(--text-m);animation:typingBounce .8s ease infinite}
.typing-dots span:nth-child(2){animation-delay:.15s}
.typing-dots span:nth-child(3){animation-delay:.3s}
@keyframes typingBounce{0%,80%,100%{transform:translateY(0)}40%{transform:translateY(-6px)}}

/* ── Skeleton ──────────────────────────────── */

.skeleton{border-radius:8px}
.skeleton-line{height:14px;margin-bottom:8px;border-radius:6px;background:var(--surface);animation:shimmer 1.5s ease infinite;background-size:200% 100%;background-image:linear-gradient(90deg,var(--surface) 25%,var(--surface-hover) 50%,var(--surface) 75%)}
.skeleton-line:last-child{width:60%}
@keyframes shimmer{0%{background-position:200% 0}100%{background-position:-200% 0}}

/* ── 确认对话框 ────────────────────────────── */

.confirm-dialog{width:420px;max-width:90vw}
.confirm-dialog .dlg-body{text-align:center}
.confirm-dialog .confirm-msg{font-size:14px;color:var(--text-s);margin-bottom:24px;line-height:1.6}
.confirm-dialog .confirm-actions{display:flex;gap:10px;justify-content:center}
.confirm-dialog .confirm-actions .btn{min-width:100px}

/* ── 表格排序 ──────────────────────────────── */

th.sortable{cursor:pointer;user-select:none;position:relative;padding-right:20px}
th.sortable::after{content:'↕';position:absolute;right:4px;top:50%;transform:translateY(-50%);font-size:10px;color:var(--text-m);opacity:.4}
th.sortable:hover::after{opacity:.8}
th.sortable.asc::after{content:'↑';opacity:1;color:var(--accent)}
th.sortable.desc::after{content:'↓';opacity:1;color:var(--accent)}

/* ── 筛选 pill ─────────────────────────────── */

.filter-bar{display:flex;gap:6px;margin-bottom:14px;flex-wrap:wrap}
.filter-pill{
  padding:5px 14px;border-radius:20px;font-size:12px;font-weight:600;
  border:1px solid var(--border);background:transparent;color:var(--text-m);
  cursor:pointer;transition:all .15s var(--ease);
}
.filter-pill:hover{border-color:var(--accent);color:var(--accent)}
.filter-pill.active{background:var(--accent);color:var(--accent-text);border-color:transparent;box-shadow:0 2px 8px rgba(16,163,127,.2)}

/* ── 进度条 ────────────────────────────────── */

.progress-bar{height:4px;background:var(--surface);border-radius:4px;overflow:hidden;margin:8px 0}
.progress-bar .fill{height:100%;background:linear-gradient(90deg,var(--accent),#34d399);border-radius:4px;transition:width .4s var(--ease)}

/* ── 进度环 ────────────────────────────────── */

.progress-ring{position:relative;display:inline-flex;align-items:center;justify-content:center}
.progress-ring svg{transform:rotate(-90deg)}
.progress-ring circle{fill:none;stroke-width:3;stroke-linecap:round}
.progress-ring .track{stroke:var(--surface)}
.progress-ring .fill{stroke:var(--accent);transition:stroke-dashoffset .6s var(--ease)}
.progress-ring .label{position:absolute;font-size:11px;font-weight:700;color:var(--text)}

/* ── 空状态 ────────────────────────────────── */

.empty-state{text-align:center;padding:48px 20px;color:var(--text-m)}
.empty-state .empty-icon{font-size:40px;margin-bottom:14px;opacity:.3}
.empty-state .empty-title{font-size:15px;font-weight:600;margin-bottom:6px;color:var(--text-s)}
.empty-state .empty-desc{font-size:13px;line-height:1.6;max-width:320px;margin:0 auto}

/* ── Tooltip ───────────────────────────────── */

[data-tip]{position:relative}
[data-tip]::after{
  content:attr(data-tip);position:absolute;bottom:calc(100% + 6px);left:50%;
  transform:translateX(-50%) translateY(4px);opacity:0;pointer-events:none;
  background:var(--text);color:var(--bg);padding:5px 10px;border-radius:7px;
  font-size:11px;font-weight:500;white-space:nowrap;z-index:100;
  transition:all .15s var(--ease);
}
[data-tip]:hover::after{opacity:1;transform:translateX(-50%) translateY(0)}

/* ── 分页 ──────────────────────────────────── */

.pagination{display:flex;align-items:center;justify-content:center;gap:4px;padding:14px 0;font-size:13px}
.pagination button{
  min-width:32px;height:32px;border:1px solid var(--border);border-radius:8px;
  background:transparent;color:var(--text-s);font-size:13px;cursor:pointer;
  transition:all .12s var(--ease);
}
.pagination button:hover{background:var(--surface-hover);color:var(--text)}
.pagination button.active{background:var(--accent);color:var(--accent-text);border-color:transparent}
.pagination button:disabled{opacity:.3;cursor:not-allowed}
.pagination .page-info{color:var(--text-m);font-size:12px;margin:0 8px}

/* ── Dropdown 菜单 ──────────────────────────── */

.dropdown{position:relative;display:inline-block}
.dropdown-menu{
  position:absolute;top:calc(100% + 4px);right:0;min-width:160px;z-index:50;
  background:var(--dd-bg);border:1px solid var(--border);border-radius:12px;
  padding:4px;box-shadow:var(--shadow-lg);display:none;
  backdrop-filter:blur(12px);
}
.dropdown.open .dropdown-menu{display:block;animation:menuIn .12s var(--ease-out)}
.dropdown-item{
  display:flex;align-items:center;gap:8px;padding:7px 12px;border-radius:8px;
  border:none;background:transparent;color:var(--text-s);font-size:13px;width:100%;
  text-align:left;cursor:pointer;transition:all .1s;
}
.dropdown-item:hover,.dropdown-item:focus{background:var(--surface-hover);color:var(--text);outline:none}
.dropdown-item.danger:hover{color:var(--danger)}
.dropdown-item[disabled]{opacity:.45;cursor:not-allowed}
.dropdown-item[disabled]:hover{background:transparent;color:var(--text-s)}
.dropdown-item svg{width:15px;height:15px;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;flex-shrink:0}

/* ── 表单校验 ─────────────────────────────── */

.invalid{border-color:var(--danger)!important}
.field-error{color:var(--danger);font-size:11px;margin-top:3px}

/* ── 健康指示灯 ────────────────────────────── */

.health-dot{width:8px;height:8px;border-radius:50%;display:inline-block;flex-shrink:0}
.health-dot.ok{background:#34d399;box-shadow:0 0 6px rgba(52,211,153,.5);animation:dotPulse 2s ease infinite}
.health-dot.warn{background:#fbbf24;box-shadow:0 0 6px rgba(251,191,36,.5)}
.health-dot.err{background:#f87171;box-shadow:0 0 6px rgba(248,113,113,.5)}

/* ── 统计数字动画 ──────────────────────────── */

.stat-num{font-variant-numeric:tabular-nums}

/* ── 响应式 ─────────────────────────────────── */

@media(max-width:820px){
  .sidebar{position:fixed;left:0;top:0;bottom:0;box-shadow:var(--shadow-lg)}
  .sidebar[data-state="expanded"]{width:280px}
  .sidebar[data-state="rail"]{width:0;border-right:none}
  .tb-toggle{display:flex}
}

/* ── 刷新中：旧资料留着、调暗、暂时不可点 ──────────
   自动刷新每 30 秒一次，整段清空重建会丢掉文字选取、
   关掉展开中的下拉、重置捲动。见 ui.js 的 beginRefresh()。 */
.is-refetching{opacity:.55;pointer-events:none;transition:opacity .15s var(--ease)}
@media(prefers-reduced-motion:reduce){.is-refetching{transition:none}}

/* ═════════════════════════════════════════════════════════════════
   皮肤二：iOS 玻璃拟态（GlassRelay 1:1 移植）
   -----------------------------------------------------------------
   与「浅色 / 深色」是两个独立的轴：
     <html data-theme="light|dark" data-skin="ios">

   本区块所有规则一律以 [data-skin="ios"] 起头 —— default 皮肤
   （不写 data-skin）连一条都吃不到，改这里不会动到原本的祖母绿扁平。

   数值来源：Next.js 专案 GlassRelay
     src/app/globals.css              → .glass / .glass-strong / .glass-rail
                                        / .glass-inset / drift-a,b,c / .sheen
     src/components/ios/primitives.tsx→ DynamicBackground / GlassCard
                                        / PageHeader / StatTile / Segmented
                                        / Pill / IosProgress / EmptyState / Spinner
     src/components/shell/app-shell.tsx → 外壳与浮动侧栏
   Tailwind v4（globals.css 里的 @theme inline 改写了 radius 阶）：
     rounded-2xl = 1rem = 16px          rounded-xl = calc(1rem + 4px) = 20px
     rounded-[26px] = 26px（GlassCard） rounded-[28px] = 28px（侧栏）
     blur-3xl = blur(64px)              blur-2xl = blur(40px)
     shadow-lg = 0 10px 15px -3px / 0 4px 6px -4px
     shadow-md = 0 4px 6px -1px  / 0 2px 4px -2px
     shadow-blue-500/N 把上面两段的颜色换成 rgba(59,130,246,N)
   ═════════════════════════════════════════════════════════════════ */

[data-skin="ios"]{
  /* GlassCard = rounded-[26px]；侧栏 = rounded-[28px] */
  --ios-r-card:26px;
  --ios-r-rail:28px;
  --ios-r-2xl:16px;   /* rounded-2xl：logo / 导航项 / 内嵌卡 */
  --ios-r-xl:20px;    /* rounded-xl：磁贴图示框 / 小按钮 */
  --ios-blue:#007aff;
  --ios-spring:cubic-bezier(.2,.85,.28,1.02);   /* ≈ spring(stiffness 260, damping 26) */

  /* ── .glass ──（globals.css 逐字移植） */
  --ios-glass-bg:linear-gradient(135deg,rgba(255,255,255,.72),rgba(255,255,255,.42));
  --ios-glass-blur:blur(28px) saturate(1.7);
  --ios-glass-border:rgba(255,255,255,.68);
  --ios-glass-shadow:0 8px 32px rgba(31,38,65,.09),inset 0 1px 0 rgba(255,255,255,.85);
  /* ── .glass-strong ── */
  --ios-strong-bg:linear-gradient(150deg,rgba(255,255,255,.9),rgba(255,255,255,.62));
  --ios-strong-blur:blur(36px) saturate(1.8);
  --ios-strong-border:rgba(255,255,255,.85);
  --ios-strong-shadow:0 14px 44px rgba(31,38,65,.13),inset 0 1px 0 rgba(255,255,255,.95);
  /* ── .glass-rail ── */
  --ios-rail-bg:linear-gradient(180deg,rgba(255,255,255,.66),rgba(255,255,255,.38));
  --ios-rail-blur:blur(32px) saturate(1.7);
  --ios-rail-border:rgba(255,255,255,.62);
  /* ── .glass-inset ── */
  --ios-inset-bg:rgba(120,120,128,.09);
  --ios-inset-border:rgba(255,255,255,.5);

  --ios-grad-primary:linear-gradient(135deg,#0a84ff,#007aff);
  --ios-grad-brand:linear-gradient(135deg,#0a84ff,#af52de,#ff2d55);
  --ios-shadow-blue-30:0 10px 15px -3px rgba(59,130,246,.3),0 4px 6px -4px rgba(59,130,246,.3);
  --ios-shadow-blue-25:0 10px 15px -3px rgba(59,130,246,.25),0 4px 6px -4px rgba(59,130,246,.25);
  --ios-shadow-blue-20:0 4px 6px -1px rgba(59,130,246,.2),0 2px 4px -2px rgba(59,130,246,.2);

  --ios-hairline:rgba(0,0,0,.06);   /* divide-black/[0.04] ~ /[0.06] */
  --ios-row-hover:rgba(255,255,255,.5);  /* hover:bg-white/50 */
  --ios-label:#6e6e73;
  --ios-label-dim:#aeaeb2;
  --ios-blob-opacity:1;
}

[data-skin="ios"][data-theme="light"]{
  --bg:#eef1f6;--bg-alt:#e7ebf2;   /* DynamicBackground 的 bg-[#eef1f6] */
  --surface:rgba(255,255,255,.62);--surface-hover:rgba(255,255,255,.82);
  --border:rgba(255,255,255,.65);--border-light:rgba(120,120,128,.14);
  --text:#1c1c1e;--text-s:#3a3a3c;--text-m:#6e6e73;
  --brand:#007aff;--brand-text:#fff;
  --accent:#007aff;--accent-text:#fff;--accent-soft:rgba(0,122,255,.1);
  --blue:#0a84ff;--blue-soft:rgba(10,132,255,.1);
  --green:#30d158;--danger:#ff3b30;--warn:#ff9f0a;
  --user-msg-bg:rgba(120,120,128,.1);
  --sidebar-bg:rgba(255,255,255,.45);--sidebar-hover:rgba(255,255,255,.5);
  --code-bg:rgba(120,120,128,.09);--code-border:rgba(255,255,255,.5);
  --dd-bg:rgba(255,255,255,.88);--dd-shadow:rgba(31,38,65,.13);
  --ring:0 0 0 3px rgba(0,122,255,.35);
  --scrollbar:rgba(60,60,67,.28);--scrollbar-hover:rgba(60,60,67,.45);
  --badge-active-bg:rgba(48,209,88,.15);--badge-active-fg:#248a3d;
  --badge-warn-bg:rgba(255,159,10,.15);--badge-warn-fg:#b25000;
  --badge-danger-bg:rgba(255,59,48,.13);--badge-danger-fg:#d70015;
  --badge-blue-bg:rgba(59,130,246,.14);--badge-blue-fg:#0060df;
  --shadow-sm:0 1px 2px rgba(31,38,65,.05);
  --shadow:0 8px 32px rgba(31,38,65,.09);
  --shadow-lg:0 14px 44px rgba(31,38,65,.13);
  --glass:rgba(255,255,255,.7);
}

/* ── 深色：来源专案只有浅色，这一份是我们自己延伸的 ──────────────
   GlassRelay 的 globals.css 没有 .dark 区块，DynamicBackground 也只
   有浅色一种。底下这套深色玻璃／深色光斑「不是从来源移植的」，是为
   了让 skin × theme 两个轴都能用而推导出来的：
     · 玻璃改成灰阶半透明（白色渐层在深色底上会变成一层雾）
     · 光斑降到 42% 并改 screen 混色 —— 深底上要「发光」而不是「盖住」
     · 噪点降到 3.5%，overlay 在深底上会明显放大颗粒
   ──────────────────────────────────────────────────────────── */
[data-skin="ios"][data-theme="dark"]{
  --bg:#0b0b0f;--bg-alt:#131318;
  --surface:rgba(60,60,67,.42);--surface-hover:rgba(72,72,80,.58);
  --border:rgba(255,255,255,.12);--border-light:rgba(255,255,255,.07);
  --text:#f5f5f7;--text-s:#c7c7cc;--text-m:#8e8e93;
  --brand:#0a84ff;--brand-text:#fff;
  --accent:#0a84ff;--accent-text:#fff;--accent-soft:rgba(10,132,255,.16);
  --blue:#0a84ff;--blue-soft:rgba(10,132,255,.16);
  --green:#30d158;--danger:#ff453a;--warn:#ff9f0a;
  --user-msg-bg:rgba(120,120,128,.24);
  --sidebar-bg:rgba(28,28,34,.55);--sidebar-hover:rgba(255,255,255,.08);
  --code-bg:rgba(120,120,128,.18);--code-border:rgba(255,255,255,.1);
  --dd-bg:rgba(38,38,44,.92);--dd-shadow:rgba(0,0,0,.5);
  --ring:0 0 0 3px rgba(10,132,255,.45);
  --scrollbar:rgba(255,255,255,.18);--scrollbar-hover:rgba(255,255,255,.3);
  --badge-active-bg:rgba(48,209,88,.18);--badge-active-fg:#30d158;
  --badge-warn-bg:rgba(255,159,10,.18);--badge-warn-fg:#ff9f0a;
  --badge-danger-bg:rgba(255,69,58,.18);--badge-danger-fg:#ff453a;
  --badge-blue-bg:rgba(10,132,255,.18);--badge-blue-fg:#0a84ff;
  --shadow-sm:0 1px 2px rgba(0,0,0,.3);
  --shadow:0 8px 32px rgba(0,0,0,.42);
  --shadow-lg:0 14px 44px rgba(0,0,0,.55);
  --glass:rgba(28,28,34,.8);

  --ios-glass-bg:linear-gradient(135deg,rgba(72,72,80,.5),rgba(48,48,54,.32));
  --ios-glass-border:rgba(255,255,255,.14);
  --ios-glass-shadow:0 8px 32px rgba(0,0,0,.45),inset 0 1px 0 rgba(255,255,255,.09);
  --ios-strong-bg:linear-gradient(150deg,rgba(58,58,66,.86),rgba(38,38,44,.7));
  --ios-strong-border:rgba(255,255,255,.18);
  --ios-strong-shadow:0 14px 44px rgba(0,0,0,.6),inset 0 1px 0 rgba(255,255,255,.12);
  --ios-rail-bg:linear-gradient(180deg,rgba(44,44,52,.66),rgba(28,28,34,.42));
  --ios-rail-border:rgba(255,255,255,.12);
  --ios-inset-bg:rgba(255,255,255,.06);
  --ios-inset-border:rgba(255,255,255,.1);

  --ios-hairline:rgba(255,255,255,.08);
  --ios-row-hover:rgba(255,255,255,.06);
  --ios-label:#8e8e93;
  --ios-label-dim:#6e6e73;
  --ios-blob-opacity:.55;
}

/* ── 字体：SF Pro 打头，中文回落到苹方 ────────────────────────── */
[data-skin="ios"] body{
  font-family:-apple-system,BlinkMacSystemFont,"SF Pro Text","SF Pro Display","PingFang SC","Hiragino Sans GB","Microsoft YaHei","Noto Sans SC",sans-serif;
  letter-spacing:-.01em;
}
[data-skin="ios"] .mono,[data-skin="ios"] code,[data-skin="ios"] pre,
[data-skin="ios"] .stat-num,[data-skin="ios"] .sc-value,[data-skin="ios"] .sc-val,
[data-skin="ios"] .stat-value{
  font-variant-numeric:tabular-nums;letter-spacing:-.02em;
}
[data-skin="ios"] .mono,[data-skin="ios"] code,[data-skin="ios"] pre{
  font-family:"SF Mono",ui-monospace,"JetBrains Mono","Fira Code",monospace;
}
[data-skin="ios"] ::selection{background:rgba(10,132,255,.25)}
[data-skin="ios"] ::-webkit-scrollbar{width:6px;height:6px}
[data-skin="ios"] ::-webkit-scrollbar-thumb{background:var(--scrollbar);border-radius:999px}
[data-skin="ios"] ::-webkit-scrollbar-track{background:transparent}

/* ═══ 1. DynamicBackground ════════════════════════════════════════
   来源是 6 个 div：5 颗光斑 + 1 层噪点，全包在
   `fixed inset-0 -z-10 overflow-hidden bg-[#eef1f6]` 里。

   我们只能用 CSS，所以拆成 4 个伪元素图层。分组方式跟来源的
   animation class 完全一致（blob-a 两颗、blob-b 两颗、blob-c 一颗），
   同一组本来就同动画同起点、位移完全同步，所以合并不会失真。
   底色 #eef1f6 走 body 的 --bg（html 不设背景 → body 背景传播到画布），
   因此四个图层都在它上面、又都在内容底下（z-index 负值）。

   每颗光斑的圆心 / 半径 / 颜色 / 不透明度都按来源逐一换算：
     · 元素 opacity 直接乘进渐层 alpha（blur 是线性运算，先乘后糊等价）
     · Tailwind 的 radial-gradient(circle,…) 预设 farthest-corner，
       方形盒子里 100% = 半边长 × √2，所以 68% 的停止点
       = 边长/2 × 1.41421 × .68 —— 底下的半径就是这样算出来的
   图层盒子刻意外扩 25vmax（inset:-25vmax）：来源的光斑是各自 blur
   之后才被父层 overflow-hidden 裁掉的，若图层只有 viewport 大小，
   blur(64px) 会在画面边缘糊出一圈假的渐隐。
   viewport 座标 (X,Y) → 盒内座标 calc(25vmax + X)。
   ════════════════════════════════════════════════════════════════ */

[data-skin="ios"]::before,
[data-skin="ios"]::after,
[data-skin="ios"] body::before{
  content:'';
  position:fixed;
  inset:-25vmax;
  pointer-events:none;
  filter:blur(64px);            /* blur-3xl */
  opacity:var(--ios-blob-opacity);
  will-change:transform;
}

/* blob-a 组：① 蓝 58vw @ -12%/-18%  ⑤ 绿 30vw @ right-24%/top-62% */
[data-skin="ios"]::before{
  z-index:-5;
  animation:iosDriftA 26s ease-in-out infinite;
  background-image:
    radial-gradient(circle 13.152vw at calc(25vmax + 61vw) calc(25vmax + 62vh + 15vw),
      rgba(48,209,88,.1925) 0%,rgba(48,209,88,0) 100%),
    radial-gradient(circle 27.888vw at calc(25vmax + 17vw) calc(25vmax + 29vw - 18vh),
      rgba(10,132,255,.385) 0%,rgba(10,132,255,0) 100%);
}
/* blob-b 组：② 紫 52vw @ right--16%/top-8%  ④ 橙 34vw @ 38%/36% */
[data-skin="ios"]::after{
  z-index:-4;
  animation:iosDriftB 32s ease-in-out infinite;
  background-image:
    radial-gradient(circle 15.387vw at calc(25vmax + 55vw) calc(25vmax + 36vh + 17vw),
      rgba(255,159,10,.2) 0%,rgba(255,159,10,0) 100%),
    radial-gradient(circle 25.003vw at calc(25vmax + 90vw) calc(25vmax + 8vh + 26vw),
      rgba(175,82,222,.3) 0%,rgba(175,82,222,0) 100%);
}
/* blob-c：③ 粉 55vw @ left-18%/bottom--22% */
[data-skin="ios"] body::before{
  z-index:-3;
  animation:iosDriftC 38s ease-in-out infinite;
  background-image:
    radial-gradient(circle 25.668vw at calc(25vmax + 45.5vw) calc(25vmax + 122vh - 27.5vw),
      rgba(255,45,85,.252) 0%,rgba(255,45,85,0) 100%);
}
/* fractal-noise 叠层：opacity-[0.05] mix-blend-overlay，尺寸同 viewport */
[data-skin="ios"] body::after{
  content:'';
  position:fixed;
  inset:0;
  z-index:-2;
  pointer-events:none;
  opacity:.05;
  mix-blend-mode:overlay;
  background-image:url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}
/* 深色是我们的延伸：光斑改 screen，让它在深底上是「发光」不是「蒙雾」 */
[data-skin="ios"][data-theme="dark"]::before,
[data-skin="ios"][data-theme="dark"]::after,
[data-skin="ios"][data-theme="dark"] body::before{mix-blend-mode:screen}
[data-skin="ios"][data-theme="dark"] body::after{opacity:.035}

/* globals.css 的 drift-a / drift-b / drift-c，逐字搬过来 */
@keyframes iosDriftA{
  0%,100%{transform:translate(0,0) scale(1)}
  33%{transform:translate(9vw,7vh) scale(1.16)}
  66%{transform:translate(-6vw,11vh) scale(.92)}
}
@keyframes iosDriftB{
  0%,100%{transform:translate(0,0) scale(1)}
  40%{transform:translate(-11vw,-6vh) scale(1.22)}
  75%{transform:translate(7vw,-9vh) scale(.94)}
}
@keyframes iosDriftC{
  0%,100%{transform:translate(0,0) scale(1)}
  50%{transform:translate(6vw,-12vh) scale(1.28)}
}

/* ═══ 2. 外壳：置中 1440 容器 + 浮动侧栏 ══════════════════════════
   来源：<div class="mx-auto flex w-full max-w-[1440px] gap-6 px-4 pt-5
          md:px-7 lg:h-screen lg:max-h-screen lg:overflow-hidden">
   我们的 body 本来就是 flex 容器（admin.html / portal.html 各自宣告），
   所以直接把它当成那个容器用，一行 HTML 都不必动。
   :has() 是为了只挑中这两页 —— chat.html 也有 .main，不能被波及。
   ════════════════════════════════════════════════════════════════ */
[data-skin="ios"] body:has(>nav.admin-nav),
[data-skin="ios"] body:has(>nav.nav){
  width:100%;
  max-width:1440px;
  margin-inline:auto;
  gap:24px;              /* gap-6 */
  padding:20px 16px;     /* pt-5 px-4 */
  align-items:stretch;
}
[data-skin="ios"] .admin-nav,
[data-skin="ios"] nav.nav{
  width:232px;
  flex:0 0 232px;
  align-self:flex-start;
  position:sticky;
  top:20px;                        /* sticky top-5 */
  height:calc(100vh - 40px);
  padding:16px;                    /* p-4 */
  border-radius:var(--ios-r-rail); /* rounded-[28px] */
  border:1px solid var(--ios-rail-border);
  background:var(--ios-rail-bg);
  backdrop-filter:var(--ios-rail-blur);
  -webkit-backdrop-filter:var(--ios-rail-blur);
  box-shadow:none;
  overflow:hidden auto;
  gap:4px;                         /* space-y-1 */
  scrollbar-width:none;
}
[data-skin="ios"] .admin-nav::-webkit-scrollbar,
[data-skin="ios"] nav.nav::-webkit-scrollbar{display:none}
[data-skin="ios"] .admin-nav>*,
[data-skin="ios"] nav.nav>*{flex-shrink:0}
[data-skin="ios"] .nav-spacer{flex:1 1 auto;min-height:8px}

/* 导航分组的小标（来源 app-shell.tsx L277：
   mb-1 px-4 text-[11px] font-bold uppercase tracking-wider text-[#aeaeb2]）。
   节点由 UI._iosNavGroups() 建 —— default 皮肤下根本不存在。 */
[data-skin="ios"] .ios-nav-group{
  font-size:11px;
  font-weight:700;
  letter-spacing:.06em;
  text-transform:uppercase;
  color:var(--ios-label-dim);
  padding-inline:16px;
  margin-block:16px 4px;
  -webkit-user-select:none;user-select:none;
  pointer-events:none;
}
[data-skin="ios"] .ios-nav-group[data-first]{margin-block-start:0}

/* 主栏：只有它捲动（来源 main.lg:h-full lg:overflow-y-auto lg:pr-1） */
[data-skin="ios"] .admin-main,
[data-skin="ios"] nav.nav~.main{
  flex:1 1 auto;
  min-width:0;
  max-width:none;
  padding:0;
  height:auto;
  overflow:visible;
}

@media(min-width:768px){
  [data-skin="ios"] body:has(>nav.admin-nav),
  [data-skin="ios"] body:has(>nav.nav){padding:20px 28px}   /* md:px-7 */
}
@media(min-width:1024px){
  [data-skin="ios"] body:has(>nav.admin-nav),
  [data-skin="ios"] body:has(>nav.nav){
    height:100vh;max-height:100vh;overflow:hidden;          /* lg:overflow-hidden */
  }
  [data-skin="ios"] .admin-main,
  [data-skin="ios"] nav.nav~.main{
    height:100%;overflow-y:auto;padding-right:4px;          /* lg:pr-1 */
  }
}

/* ═══ 3. 玻璃配方（.glass / .glass-strong / .glass-inset）═══════ */
[data-skin="ios"] .glass,
[data-skin="ios"] .card,
[data-skin="ios"] .stat-card,
[data-skin="ios"] .stat-card-rich,
[data-skin="ios"] .metric-card,
[data-skin="ios"] .plan-card,
[data-skin="ios"] .sub-card,
[data-skin="ios"] .sc,
[data-skin="ios"] .ann{
  background:var(--ios-glass-bg);
  backdrop-filter:var(--ios-glass-blur);
  -webkit-backdrop-filter:var(--ios-glass-blur);
  border:1px solid var(--ios-glass-border);
  box-shadow:var(--ios-glass-shadow);
  border-radius:var(--ios-r-card);          /* rounded-[26px] */
}
[data-skin="ios"] .card:hover,
[data-skin="ios"] .sc:hover{box-shadow:var(--ios-glass-shadow)}

/* .glass-strong —— 弹窗 / 对话框 / 浮动面板 */
[data-skin="ios"] .glass-strong,
[data-skin="ios"] .modal-box,
[data-skin="ios"] dialog,
[data-skin="ios"] .col-menu,
[data-skin="ios"] .dropdown-menu,
[data-skin="ios"] .user-menu,
[data-skin="ios"] .toast{
  background:var(--ios-strong-bg);
  backdrop-filter:var(--ios-strong-blur);
  -webkit-backdrop-filter:var(--ios-strong-blur);
  border:1px solid var(--ios-strong-border);
  box-shadow:var(--ios-strong-shadow);
  border-radius:var(--ios-r-card);
}
[data-skin="ios"] .col-menu,
[data-skin="ios"] .dropdown-menu,
[data-skin="ios"] .user-menu,
[data-skin="ios"] .toast{border-radius:var(--ios-r-xl)}
[data-skin="ios"] .modal-overlay,
[data-skin="ios"] .overlay,
[data-skin="ios"] .mobile-overlay{
  background:rgba(28,28,30,.28);
  backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);
}
[data-skin="ios"] dialog::backdrop{
  background:rgba(28,28,30,.28);backdrop-filter:blur(8px);
}

/* .glass-inset —— 卡中卡 / 代码块 / 端点框 */
[data-skin="ios"] .glass-inset,
[data-skin="ios"] .snip,
[data-skin="ios"] .endpoint-box,
[data-skin="ios"] .key-box,
[data-skin="ios"] .copybox,
[data-skin="ios"] .conn-test,
[data-skin="ios"] .diag-row{
  background:var(--ios-inset-bg);
  border:1px solid var(--ios-inset-border);
  border-radius:var(--ios-r-xl);            /* rounded-xl = 20px */
  box-shadow:none;
}
[data-skin="ios"] .diag-row{border-left-width:3px}
/* 选单项只换底色 —— 加 border 会让 hover 时整列位移 1px */
[data-skin="ios"] .col-menu-item,
[data-skin="ios"] .dropdown-item,
[data-skin="ios"] .um-row{border-radius:12px}
[data-skin="ios"] .col-menu-item:hover{background:var(--ios-inset-bg)}

/* .sheen —— 玻璃高光扫过（globals.css 的 .sheen::after，供卡片选用） */
@keyframes iosSheen{
  0%{transform:translateX(-130%) skewX(-18deg)}
  100%{transform:translateX(240%) skewX(-18deg)}
}
[data-skin="ios"] .sheen{position:relative;overflow:hidden}
[data-skin="ios"] .sheen::after{
  content:"";position:absolute;top:0;bottom:0;width:34%;
  background:linear-gradient(90deg,transparent,rgba(255,255,255,.55),transparent);
  animation:iosSheen 5.5s ease-in-out infinite;pointer-events:none;
}

/* ═══ 4. StatTile ════════════════════════════════════════════════
   来源：GlassCard(p-5) + 角落光晕 + label 13px/#6e6e73
        + value 26px/bold + h-8 w-8 rounded-xl 渐层图示框
   ════════════════════════════════════════════════════════════════ */
[data-skin="ios"] .stat-card,
[data-skin="ios"] .stat-card-rich,
[data-skin="ios"] .sc,
[data-skin="ios"] .metric-card{
  padding:20px;                      /* p-5 */
  position:relative;
  overflow:hidden;
  transition:transform .3s var(--ease),box-shadow .3s var(--ease),opacity .3s;
}
[data-skin="ios"] .stat-card:hover,
[data-skin="ios"] .stat-card-rich:hover,
[data-skin="ios"] .sc:hover,
[data-skin="ios"] .metric-card:hover{transform:translateY(-2px)}   /* hover:-translate-y-0.5 */

/* label：13px / 500 / #6e6e73，来源没有大写与字距 */
[data-skin="ios"] .stat-label,
[data-skin="ios"] .sc-title,
[data-skin="ios"] .sc-label,
[data-skin="ios"] .metric-title{
  font-size:13px;font-weight:500;color:var(--ios-label);
  text-transform:none;letter-spacing:-.01em;
}
/* value：26px / 700 / leading-tight */
[data-skin="ios"] .stat-value,
[data-skin="ios"] .sc-value,
[data-skin="ios"] .sc-val,
[data-skin="ios"] .metric-big{
  font-size:26px;font-weight:700;line-height:1.25;margin-top:8px;
}
[data-skin="ios"] .sc-detail,
[data-skin="ios"] .sc-sub,
[data-skin="ios"] .metric-sub{font-size:12px;color:var(--ios-label);margin-top:4px}

/* 图示框：h-8 w-8 rounded-xl + 渐层 */
[data-skin="ios"] .sc-icon{
  width:32px;height:32px;border-radius:var(--ios-r-xl);
  box-shadow:none;
}
[data-skin="ios"] .sc-icon-blue{background:linear-gradient(135deg,#0a84ff,#007aff)}
[data-skin="ios"] .sc-icon-purple{background:linear-gradient(135deg,#af52de,#bf5af2)}
[data-skin="ios"] .sc-icon-green{background:linear-gradient(135deg,#30d158,#00c7be)}
[data-skin="ios"] .sc-icon-yellow{background:linear-gradient(135deg,#ff9f0a,#ff2d55)}
[data-skin="ios"] .sc-icon-red{background:linear-gradient(135deg,#ff3b30,#ff2d55)}

/* 角落光晕：-right-8 -top-10 h-32 w-32 opacity-25 blur-2xl → hover .4 */
[data-skin="ios"] .stat-card-rich::after{
  content:'';position:absolute;right:-32px;top:-40px;
  width:128px;height:128px;border-radius:9999px;
  opacity:.25;filter:blur(40px);pointer-events:none;
  background:var(--ios-grad-primary);
  transition:opacity .3s var(--ease);
}
[data-skin="ios"] .stat-card-rich:hover::after{opacity:.4}
[data-skin="ios"] .stat-card-rich:has(.sc-icon-purple)::after{background:linear-gradient(135deg,#af52de,#bf5af2)}
[data-skin="ios"] .stat-card-rich:has(.sc-icon-green)::after{background:linear-gradient(135deg,#30d158,#00c7be)}
[data-skin="ios"] .stat-card-rich:has(.sc-icon-yellow)::after{background:linear-gradient(135deg,#ff9f0a,#ff2d55)}
[data-skin="ios"] .stat-card-rich:has(.sc-icon-red)::after{background:linear-gradient(135deg,#ff3b30,#ff2d55)}
[data-skin="ios"] .sc-header,[data-skin="ios"] .sc-value,
[data-skin="ios"] .sc-spark,[data-skin="ios"] .sc-detail{position:relative;z-index:1}
[data-skin="ios"] .sc-spark .bar{border-radius:3px 3px 0 0}

/* ═══ 5. 品牌标记（app-shell 的 40px logo）═══════════════════════ */
[data-skin="ios"] .brand-mark{
  width:40px;height:40px;
  border-radius:var(--ios-r-2xl);                 /* rounded-2xl */
  background:var(--ios-grad-brand);               /* #0a84ff → #af52de → #ff2d55 */
  color:#fff;font-size:17px;font-weight:700;letter-spacing:-.5px;
  box-shadow:var(--ios-shadow-blue-30);           /* shadow-lg shadow-blue-500/30 */
}
[data-skin="ios"] .sb-brand{
  gap:10px;                                       /* gap-2.5 */
  padding:4px 8px 0!important;                    /* px-2 pt-1；盖掉 HTML 上的行内 padding */
  margin-bottom:24px;                             /* mb-6 */
}
[data-skin="ios"] .brand-name{font-size:15px;font-weight:700;letter-spacing:-.2px;line-height:1.2}

/* ═══ 6. 导航 ════════════════════════════════════════════════════ */
[data-skin="ios"] .nav-item,
[data-skin="ios"] .ni{
  border-radius:var(--ios-r-2xl);   /* rounded-2xl = 16px */
  padding:10px 16px;                /* px-4 py-2.5 */
  gap:10px;                         /* gap-2.5 */
  font-size:13.5px;
  font-weight:500;
  color:#48484a;
  transition:background .18s var(--ease),color .18s var(--ease),box-shadow .18s var(--ease);
}
[data-theme="dark"][data-skin="ios"] .nav-item,
[data-theme="dark"][data-skin="ios"] .ni{color:var(--text-s)}
[data-skin="ios"] .nav-item:hover,
[data-skin="ios"] .ni:hover{background:var(--ios-row-hover);color:var(--text)}
[data-skin="ios"] .nav-item.active,
[data-skin="ios"] .ni.active{
  background:var(--ios-grad-primary);
  color:#fff;font-weight:500;
  box-shadow:var(--ios-shadow-blue-25);      /* shadow-lg shadow-blue-500/25 */
}
[data-skin="ios"] .nav-back,
[data-skin="ios"] .theme-toggle{
  border-radius:var(--ios-r-2xl);padding:10px 16px;font-size:13px;color:#48484a;
}
[data-theme="dark"][data-skin="ios"] .nav-back,
[data-theme="dark"][data-skin="ios"] .theme-toggle{color:var(--text-s)}
[data-skin="ios"] .nav-back:hover,
[data-skin="ios"] .theme-toggle:hover{background:var(--ios-row-hover);color:var(--text)}

/* 分区标签：11px / bold / uppercase / tracking-wider / #aeaeb2 */
[data-skin="ios"] .section-head,
[data-skin="ios"] .nav-section-label{
  font-size:11px;font-weight:700;text-transform:uppercase;
  letter-spacing:.05em;color:var(--ios-label-dim);
  padding:6px 16px;border-radius:10px;
}
[data-skin="ios"] .section-head:hover{color:var(--ios-label)}

/* 侧栏底部用户卡 = glass-inset rounded-2xl p-3 */
[data-skin="ios"] .sb-user{
  background:var(--ios-inset-bg);border:1px solid var(--ios-inset-border);
  border-radius:var(--ios-r-2xl);padding:12px;margin:6px 0 0;
}
[data-skin="ios"] .sb-user:hover{background:var(--ios-row-hover)}

/* chat.html 的整高侧栏：只上玻璃，不做浮动 rail（那页是全出血聊天界面） */
[data-skin="ios"] nav.sidebar{
  background:var(--ios-rail-bg);
  backdrop-filter:var(--ios-rail-blur);
  -webkit-backdrop-filter:var(--ios-rail-blur);
  border-right:1px solid var(--ios-rail-border);
}

/* ═══ 7. 标题 / 区块标题 ═════════════════════════════════════════
   来源 PageHeader：title 22px bold tracking-tight、mb-5 */
[data-skin="ios"] .page-title,
[data-skin="ios"] .title{
  font-size:22px;font-weight:700;letter-spacing:-.025em;margin-bottom:20px;
}
[data-skin="ios"] .section-title{font-size:15px;font-weight:700;letter-spacing:-.015em}

/* ═══ 8. 元件：按钮 / 徽章 / 分段 / 进度 / 表格 / 空状态 ═════════ */

/* 按钮：来源的 CTA 是 rounded-full px-5 py-2.5 + shadow-blue-500/30 */
[data-skin="ios"] .btn{
  border-radius:999px;font-weight:590;letter-spacing:-.01em;
  transition:transform .18s var(--ease),box-shadow .18s var(--ease),opacity .18s;
}
[data-skin="ios"] .btn-sm,[data-skin="ios"] .mini,
[data-skin="ios"] .key-toggle,[data-skin="ios"] .snip-copy{border-radius:999px}
[data-skin="ios"] .btn-primary{background:var(--ios-grad-primary);box-shadow:var(--ios-shadow-blue-20)}
[data-skin="ios"] .btn-primary:hover{box-shadow:var(--ios-shadow-blue-30);opacity:1}
[data-skin="ios"] .btn:active{transform:scale(.95)}
[data-skin="ios"] .btn-ghost,[data-skin="ios"] .btn-outline{
  background:var(--ios-inset-bg);border:1px solid var(--ios-inset-border);
}
[data-skin="ios"] .btn-icon{border-radius:999px}

/* Pill：rounded-full border-white/50 px-2 py-0.5 text-[11px] font-semibold */
[data-skin="ios"] .badge{
  border-radius:999px;padding:3px 9px;font-size:11px;font-weight:600;
  border:1px solid rgba(255,255,255,.5);letter-spacing:0;
}
[data-theme="dark"][data-skin="ios"] .badge{border-color:rgba(255,255,255,.12)}

/* Segmented：glass-inset 药丸容器 + 渐层滑块 */
[data-skin="ios"] .time-range{
  display:inline-flex;gap:0;padding:4px;border-radius:999px;
  background:var(--ios-inset-bg);border:1px solid var(--ios-inset-border);
}
[data-skin="ios"] .tr-pill,[data-skin="ios"] .filter-pill{
  border-radius:999px;border:1px solid transparent;background:transparent;
  padding:6px 16px;font-size:13px;font-weight:500;color:var(--ios-label);
  transition:color .2s var(--ease),background .2s var(--ease),box-shadow .2s var(--ease);
}
[data-skin="ios"] .tr-pill:hover,[data-skin="ios"] .filter-pill:hover{color:var(--text);border-color:transparent}
[data-skin="ios"] .tr-pill.active,[data-skin="ios"] .filter-pill.active{
  background:var(--ios-grad-primary);color:#fff;border-color:transparent;
  box-shadow:var(--ios-shadow-blue-20);
}
[data-skin="ios"] .filter-pill{border:1px solid var(--ios-inset-border);background:var(--ios-inset-bg)}
[data-skin="ios"] .filter-pill.active{border-color:transparent}

/* IosProgress：h-1.5 rounded-full bg-black/[0.07] */
[data-skin="ios"] .progress-bar{height:6px;border-radius:999px;background:rgba(0,0,0,.07)}
[data-theme="dark"][data-skin="ios"] .progress-bar{background:rgba(255,255,255,.1)}
[data-skin="ios"] .progress-fill,[data-skin="ios"] .progress-bar .fill{border-radius:999px}

/* Spinner：h-7 w-7 border-[3px] border-black/10 border-t-[#0a84ff] */
[data-skin="ios"] .spinner{
  width:28px;height:28px;border:3px solid rgba(0,0,0,.1);border-top-color:#0a84ff;
}
[data-theme="dark"][data-skin="ios"] .spinner{border-color:rgba(255,255,255,.12);border-top-color:#0a84ff}
[data-skin="ios"] .loading-center{padding:64px 0}

/* EmptyState */
[data-skin="ios"] .empty-state{padding:64px 20px;color:var(--ios-label)}
[data-skin="ios"] .empty-icon{
  width:56px;height:56px;margin:0 auto 8px;border-radius:var(--ios-r-2xl);
  background:rgba(0,0,0,.04);display:flex;align-items:center;justify-content:center;
  font-size:20px;opacity:1;
}
[data-theme="dark"][data-skin="ios"] .empty-icon{background:rgba(255,255,255,.06)}
[data-skin="ios"] .empty-title,[data-skin="ios"] .empty-text{font-size:14px;font-weight:600;color:var(--text-s)}
[data-skin="ios"] .empty-desc{font-size:12px;color:var(--ios-label)}

/* 输入 */
[data-skin="ios"] .field input,[data-skin="ios"] .field select,[data-skin="ios"] .field textarea,
[data-skin="ios"] .table-search,[data-skin="ios"] .mini-in,[data-skin="ios"] .cell-in,
[data-skin="ios"] .f input,[data-skin="ios"] .f select,[data-skin="ios"] .frow input,
[data-skin="ios"] .frow select,[data-skin="ios"] .admin-toolbar select,
[data-skin="ios"] .filter-bar select,[data-skin="ios"] .filter-bar input,
[data-skin="ios"] .sb-search input{
  border-radius:12px;background:var(--ios-inset-bg);
  border:1px solid var(--ios-inset-border);color:var(--text);
}
[data-skin="ios"] .cell-in{border-radius:9px;padding:4px 8px}

/* 下拉选单不要白框。
   --ios-inset-border 是白色 50%（玻璃的受光边缘）。卡片与文字输入框需要它，
   但选单本身已经有底色跟卡面区分，再描一圈白边就变成一个个白框。
   只去颜色、不去宽度 —— 拿掉 border-width 会让每个选单位移 2px。
   选择器逐条列出而不是写 [data-skin="ios"] select：上面那条含
   .filter-bar select（0,2,1），单靠元素选择器（0,1,1）压不过它。 */
[data-skin="ios"] .field select,[data-skin="ios"] .f select,
[data-skin="ios"] .frow select,[data-skin="ios"] .admin-toolbar select,
[data-skin="ios"] .filter-bar select,[data-skin="ios"] select{
  border-color:transparent;
}

/* 表格：hover:bg-white/50、divide-black/[0.04] */
[data-skin="ios"] thead th{
  font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;
  color:var(--ios-label-dim);border-bottom:1px solid var(--ios-hairline);
  background:transparent;backdrop-filter:none;-webkit-backdrop-filter:none;
}
[data-skin="ios"] tbody td{border-bottom:1px solid var(--ios-hairline)}
[data-skin="ios"] tbody tr:hover td{background:var(--ios-row-hover)}
[data-skin="ios"] tbody tr:last-child td{border-bottom:none}

/* 排名 / 迷你图 */
[data-skin="ios"] .rank-num{border-radius:9px}
[data-skin="ios"] .rank-bar{border-radius:999px;background:rgba(0,0,0,.07)}
[data-skin="ios"] .rank-bar-fill{border-radius:999px;background:linear-gradient(90deg,#0a84ffaa,#0a84ff)}

/* 方案卡 */
[data-skin="ios"] .plan-card{border:1px solid var(--ios-glass-border)}
[data-skin="ios"] .plan-card:hover{border-color:rgba(0,122,255,.45);box-shadow:var(--ios-strong-shadow)}
[data-skin="ios"] .plan-price{background:linear-gradient(100deg,#0a84ff 0%,#af52de 48%,#ff2d55 100%);
  -webkit-background-clip:text;background-clip:text;color:transparent}

/* 批次条 */
[data-skin="ios"] .bulk-bar{border-radius:var(--ios-r-xl);border-color:rgba(0,122,255,.35)}

/* ═══ 9. 进场动画（framer-motion stagger 0.06 + riseIn）══════════
   来源：variants stagger{staggerChildren:.06}、riseIn{y:18, scale:.985,
   spring stiffness 260 damping 26}。CSS 用 nth-child 排延迟复现。
   panel 是 display:none ⇄ block 切换，显示时动画会自动重播。
   ════════════════════════════════════════════════════════════════ */
@keyframes iosRise{
  from{opacity:0;transform:translateY(18px) scale(.985)}
  to{opacity:1;transform:none}
}
/* fill-mode 是 backwards 不是 both，而且这不是随手写的：
   both 会让 to{transform:none} 永远「填」在算出来的样式上，Chromium
   把「身上有还在填的 transform 动画」的元素当成 fixed 子孙的容纳块 ——
   连 identity matrix 都算。那会让详情抽屉（position:fixed）以这张卡为准
   置中，歪在内容区中央而不是视窗中央。to 的两个值（opacity:1 / transform:none）
   本来就等于基础样式，所以只填 backwards 画面一格都不差，
   动画跑完之后这个元素就干干净净了。 */
[data-skin="ios"] .panel.active>*{animation:iosRise .58s var(--ios-spring) backwards}
[data-skin="ios"] .panel.active>*:nth-child(1){animation-delay:0s}
[data-skin="ios"] .panel.active>*:nth-child(2){animation-delay:.06s}
[data-skin="ios"] .panel.active>*:nth-child(3){animation-delay:.12s}
[data-skin="ios"] .panel.active>*:nth-child(4){animation-delay:.18s}
[data-skin="ios"] .panel.active>*:nth-child(5){animation-delay:.24s}
[data-skin="ios"] .panel.active>*:nth-child(6){animation-delay:.3s}
[data-skin="ios"] .panel.active>*:nth-child(7){animation-delay:.36s}
[data-skin="ios"] .panel.active>*:nth-child(8){animation-delay:.42s}
[data-skin="ios"] .panel.active>*:nth-child(9){animation-delay:.48s}
[data-skin="ios"] .panel.active>*:nth-child(10){animation-delay:.54s}
[data-skin="ios"] .panel.active>*:nth-child(n+11){animation-delay:.6s}
/* 固定定位的东西不能被 transform 抓成容纳块，也不能被 fill-mode 强制显形 */
[data-skin="ios"] .panel.active>.modal-overlay,
[data-skin="ios"] .panel.active>dialog,
[data-skin="ios"] .panel.active>[hidden],
[data-skin="ios"] .panel.active>.toast{animation:none}

/* ═══ 10. 响应式：<1024 页面自己捲，<768 回到原本的抽屉 / 横向导航 ═ */
@media(max-width:1023px){
  [data-skin="ios"] .admin-nav,
  [data-skin="ios"] nav.nav{height:auto;max-height:calc(100vh - 40px)}
}
@media(max-width:768px){
  [data-skin="ios"] body:has(>nav.admin-nav),
  [data-skin="ios"] body:has(>nav.nav){
    flex-direction:column;max-width:none;gap:0;padding:0;
    height:auto;max-height:none;overflow:visible;
  }
  /* admin：抽屉式侧栏（原本的行为，只是换上玻璃与大圆角） */
  [data-skin="ios"] .admin-nav{
    position:fixed;left:0;top:0;bottom:0;z-index:30;
    width:272px;max-width:86vw;flex:0 0 auto;height:auto;max-height:none;
    align-self:auto;border-radius:0 var(--ios-r-rail) var(--ios-r-rail) 0;
    transform:translateX(-100%);transition:transform .22s var(--ease);
    box-shadow:var(--ios-strong-shadow);
  }
  [data-skin="ios"] .admin-nav.open{transform:translateX(0)}
  [data-skin="ios"] .mobile-header{
    background:var(--ios-rail-bg);
    backdrop-filter:var(--ios-rail-blur);-webkit-backdrop-filter:var(--ios-rail-blur);
    border-bottom:1px solid var(--ios-rail-border);
  }
  [data-skin="ios"] .mobile-header .brand-mark{width:32px;height:32px;border-radius:12px;font-size:15px}
  /* portal：横向捲动导航条 */
  [data-skin="ios"] nav.nav{
    position:sticky;top:0;z-index:20;
    width:auto;flex:0 0 auto;height:auto;max-height:none;align-self:auto;
    flex-direction:row;align-items:center;overflow:auto hidden;
    padding:8px 10px;border-radius:0;border:none;
    border-bottom:1px solid var(--ios-rail-border);
  }
  [data-skin="ios"] .ni{flex-shrink:0;padding:7px 14px}
  [data-skin="ios"] .admin-main,
  [data-skin="ios"] nav.nav~.main{padding:16px;height:auto;overflow:visible}
  [data-skin="ios"] .card,[data-skin="ios"] .stat-card,
  [data-skin="ios"] .stat-card-rich,[data-skin="ios"] .sc,
  [data-skin="ios"] .metric-card,[data-skin="ios"] .plan-card{border-radius:22px}
}

/* ═══ 11. 表格 → 卡片格线 ════════════════════════════════════════
   来源：shot-09-users.png / src/components/views/admin-users.tsx
   一列 = 一张玻璃卡；一格 = 一个「栏位名 + 值」。

   密度换可读性 —— 使用者明确接受了这个取舍。default 皮肤那边一格都没动，
   19 栏的表还是 19 栏的表，这里只是同一份 DOM 的另一种呈现。

   资料从哪来：ui.js 的 UI.labelCells()（admin/portal 的 afterTableRender
   与 setRows 都会呼叫）把 <thead> 的栏位名抄进每个 td 的 data-label，
   再照 <th data-card="…"> 宣告的角色替储存格打上 class：
     .ios-c-head   data-card="title"  → 卡片标题（左边挂头像）
     .ios-c-tag    data-card="badge"  → 贴在标题右边的徽章
     .ios-c-sub    data-card="sub"    → 标题底下那条 11px 灰副标，" · " 串成一行
     .ios-c-chip   data-card="chip"   → 副标与磁贴之间那一排小药丸（shot-13）
     .ios-c-metric data-card="metric" → 四格一排的数字磁贴（值在上、标签在下）
     .ios-c-detail data-card="detail" → 一整行「标签 → 值」
     .ios-c-off    没标到的栏           → 卡片里不出现（预设就是这个）
   另外几种是结构性的，不用宣告也永远认得：
     .ios-c-check 勾选框    → 压在头像上，滑过 / 已勾选才现身
     .ios-c-act   操作栏    → 卡片页脚（上缘一条分隔线）
     .ios-c-sw    操作栏里的「停用 / 启用」→ 右上角 iOS 开关（shot-13）
     .ios-c-caret 展开箭头  → 卡片右上角
     .ios-c-blank 空值      → 整格不显示
   表格本身要拿到 .ios-cards 才会变卡片（弹窗里的小表、图例表不会）；
   日志类的页面用 <table data-card-mode="off"> 留成真的表格（来源 shot-14）。
   ════════════════════════════════════════════════════════════════ */

/* 外层那张 .card 要让位：卡中卡等于两层毛玻璃，糊成一片 */
[data-skin="ios"] .card:has(table.ios-cards),
[data-skin="ios"] .section:has(table.ios-cards)>.card{
  background:none;backdrop-filter:none;-webkit-backdrop-filter:none;
  border:none;box-shadow:none;padding:0;
}
/* 卡片格线自己会换行，横向捲动条留着只会多一条空捲动区 */
[data-skin="ios"] .card:has(>table.ios-cards),
[data-skin="ios"] .card>div:has(>table.ios-cards){overflow:visible}

[data-skin="ios"] table.ios-cards{
  display:block;width:100%;border-collapse:separate;border-spacing:0;
}

/* ── 表头：整列藏起来，只留「全选」与排序钮 ────────────────────
   来源是 thead{display:none}，但我们的表有两个来源没有的东西：
   全选勾选框与可点排序的表头。整列藏掉等于把这两个功能删了，
   所以改成一条药丸控制列 —— 版面像来源的搜寻列，功能一个不少。 */
[data-skin="ios"] table.ios-cards thead{display:none}
[data-skin="ios"] table.ios-cards thead:has(th.col-check),
[data-skin="ios"] table.ios-cards thead:has(th.sortable){display:block;margin-bottom:14px}
[data-skin="ios"] table.ios-cards thead tr{
  display:flex;flex-wrap:wrap;align-items:center;gap:6px;
}
[data-skin="ios"] table.ios-cards thead th{
  display:none;border:none;background:none;padding:0;
}
[data-skin="ios"] table.ios-cards thead th.col-check{
  display:inline-flex;align-items:center;gap:7px;
  padding:5px 12px 5px 10px;border-radius:999px;
  background:var(--ios-inset-bg);border:1px solid var(--ios-inset-border);
  font-size:11px;font-weight:600;color:var(--ios-label);
  text-transform:none;letter-spacing:0;white-space:nowrap;
}
[data-skin="ios"] table.ios-cards thead th.col-check::after{content:'全选'}
[data-skin="ios"] table.ios-cards thead th.sortable{
  display:inline-flex;align-items:center;gap:3px;
  padding:5px 12px;border-radius:999px;cursor:pointer;
  background:var(--ios-inset-bg);border:1px solid var(--ios-inset-border);
  font-size:11px;font-weight:600;color:var(--ios-label);
  text-transform:none;letter-spacing:0;white-space:nowrap;
  transition:color .18s var(--ease),background .18s var(--ease);
}
[data-skin="ios"] table.ios-cards thead th.sortable:hover{color:var(--text)}
[data-skin="ios"] table.ios-cards thead th.sortable.asc,
[data-skin="ios"] table.ios-cards thead th.sortable.desc{
  background:var(--ios-grad-primary);color:#fff;border-color:transparent;
  box-shadow:var(--ios-shadow-blue-20);
}
[data-skin="ios"] table.ios-cards thead th.sortable::after{font-size:9px;opacity:.7}

/* ── 格线本体 ────────────────────────────────────────────────── */
[data-skin="ios"] table.ios-cards>tbody{
  display:grid;
  grid-template-columns:repeat(auto-fill,minmax(340px,1fr));
  gap:16px;
  align-items:start;
}
/* 挑过栏位的表：卡片要够宽，来源 shot-09 在 1100px 的内容宽度下是「一行两张」
   （xl:grid-cols-2）。min() 是给手机用的 —— 视窗比 430px 还窄时不该溢出。 */
[data-skin="ios"] table.ios-cards-curated>tbody{
  grid-template-columns:repeat(auto-fill,minmax(min(100%,430px),1fr));
}

/* ── 一列 = 一张 GlassCard（rounded-[26px] p-5 hover:-translate-y-0.5）──
   背景是三层，由上往下：
     ① 上缘那条 4px 色带 —— 来源 admin-channels.tsx:231
        <div className="h-1 w-full" style={{background: 启用中
          ? "linear-gradient(90deg,#30d158,#00c7be)" : "#c7c7cc"}} />
        量过来源那张 shot-13：色带就是 4 列像素（y 213–216），左端 rgb(45,208,93)
        右端 rgb(5,200,177)。这里不另外做元素 —— <tr> 的 ::before / ::after
        已经拿去当硬换行了，改用一层 no-repeat 的背景，圆角一样会把两端切掉。
     ② 一层很淡的色调 —— 来源那几张卡各自偏玫瑰 / 紫 / 薄荷，因为玻璃是半透明的、
        底下的彩色光斑透上来。我们的光斑照移了，但卡片上盖着几块灰色内嵌方块
        （数字磁贴）把色调压掉了；把磁贴的方块拿掉之后色调就看得见了，这一层
        只是再补一点点，让「每张卡一个色相」这件事不必靠光斑刚好飘到那里。
        色相 = 头像的色相（同一个字串永远同一个颜色），不额外编码任何资料。
     ③ 玻璃本体 —— .glass 逐字移植。
   background-origin 预设 padding-box：色带从 1px 的边框底下开始画，
   跟来源那个「卡片内、边框内」的 div 是同一个位置。 */
[data-skin="ios"] table.ios-cards>tbody>tr{
  /* stretch：同一行的栏位方块等高，看起来才是一张格线而不是高高低低的贴纸 */
  display:flex;flex-wrap:wrap;align-items:stretch;align-content:flex-start;
  gap:8px;
  padding:16px 18px;
  border-radius:var(--ios-r-card);
  background:
    var(--ios-strip,var(--ios-av,var(--ios-grad-primary))) 0 0/100% 4px no-repeat,
    var(--ios-tint,none) 0 0/100% 100% no-repeat,
    var(--ios-glass-bg);
  backdrop-filter:var(--ios-glass-blur);
  -webkit-backdrop-filter:var(--ios-glass-blur);
  border:1px solid var(--ios-glass-border);
  box-shadow:var(--ios-glass-shadow);
  position:relative;
  transition:transform .25s var(--ease),box-shadow .25s var(--ease),
             border-color .18s var(--ease),opacity .2s;
}
/* 有「停用 / 启用」开关的列：色带改成来源那两种状态色 —— 这时它真的在报
   一件事（这条线路是不是启用中），跟来源同一个语意。 */
[data-skin="ios"] table.ios-cards>tbody>tr.ios-row-sw:has(.ios-c-sw[data-on="1"]){
  --ios-strip:linear-gradient(90deg,#30d158,#00c7be);
}
[data-skin="ios"] table.ios-cards>tbody>tr.ios-row-sw:has(.ios-c-sw[data-on="0"]){
  --ios-strip:linear-gradient(90deg,#c7c7cc,#c7c7cc);
}
[data-skin="ios"] table.ios-cards>tbody>tr:hover{
  transform:translateY(-2px);box-shadow:var(--ios-strong-shadow);
}
/* 右上角要留位置给勾选框 / 展开箭头，否则标题会跑到它底下 */
[data-skin="ios"] table.ios-cards>tbody>tr:has(>td.ios-c-check),
[data-skin="ios"] table.ios-cards>tbody>tr:has(>td.ios-c-caret){padding-right:46px}
/* 挑过栏位的表把勾选框搬回文件流了 —— 右边不必再留白 */
[data-skin="ios"] table.ios-cards-curated>tbody>tr:has(>td.ios-c-check):not(:has(>td.ios-c-caret)){
  padding-right:16px;
}

/* 标题那一行要自己占满一整行（来源 shot-09 就是这样：头像 + 名称 + 徽章，
   底下才是数字方格）。flex 换行是照 flex-basis 算的，光靠 basis 调不出
   「刚好在这里断」—— 所以塞一个 100% 宽的伪元素当硬断行。
   ::before 在 flex 容器里本来就是一个 flex item，order 排在徽章之后、栏位之前。 */
[data-skin="ios"] table.ios-cards>tbody>tr::before{
  content:'';order:2;flex:0 0 100%;height:0;margin:0;padding:0;
}
[data-skin="ios"] table.ios-cards>tbody>tr.ios-row-wide::before,
[data-skin="ios"] table.ios-cards>tbody>tr:has(>td[colspan])::before{content:none}

/* 选中的卡片要一眼看得出来（批次操作是照勾选框走的） */
[data-skin="ios"] table.ios-cards>tbody>tr:has(.row-check:checked){
  border-color:rgba(0,122,255,.55);
  box-shadow:0 0 0 3px rgba(0,122,255,.16),var(--ios-glass-shadow);
}

/* ── 一般栏位：标签在上、值在下 ───────────────────────────────
   来源 shot-09 的数字磁贴：rounded-2xl bg-black/[0.04] px-3 py-2.5
   一行三个（auto-fill 的卡宽 340px 摆得下）。 */
[data-skin="ios"] table.ios-cards>tbody>tr>td{
  display:block;
  /* 不给 grow：最后一行没排满时该留白，不该把剩下那格拉成整行宽 */
  flex:0 1 calc(33.333% - 6px);
  min-width:0;
  order:5;
  margin:0;
  padding:7px 10px;
  border:none;
  border-radius:14px;
  background:rgba(0,0,0,.04);
  font-size:12.5px;line-height:1.4;
  white-space:normal;overflow-wrap:anywhere;
  vertical-align:baseline;
}
[data-theme="dark"][data-skin="ios"] table.ios-cards>tbody>tr>td{background:rgba(255,255,255,.05)}
[data-skin="ios"] table.ios-cards>tbody>tr>td[data-label]::before{
  content:attr(data-label);
  display:block;
  margin-bottom:2px;
  font-size:10px;font-weight:600;line-height:1.3;
  letter-spacing:.03em;
  color:var(--ios-label-dim);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
/* 表格里到处都是 .trunc（max-width:160px）—— 卡片里没有横向捲动，让它撑满 */
[data-skin="ios"] table.ios-cards>tbody>tr>td .trunc{max-width:100%!important}
[data-skin="ios"] table.ios-cards>tbody>tr>td>div[style]{max-width:100%!important}
/* 既有那条 tbody tr:hover td{background:白} 会把每个栏位刷白，挡掉 */
[data-skin="ios"] table.ios-cards>tbody>tr:hover>td{background:rgba(0,0,0,.04)}
[data-theme="dark"][data-skin="ios"] table.ios-cards>tbody>tr:hover>td{background:rgba(255,255,255,.05)}

/* ── 卡片标题：头像 + 名称（+ 名称后面本来就有的徽章）─────────
   来源 admin-users.tsx：h-11 w-11 rounded-2xl 渐层方块 + 15px bold。 */
/* flex:1 1 0 + max-width:max-content 是「徽章不换行」那个 bug 的解法。
   flexbox 是**先按假设尺寸分行、再压缩**：flex-basis:auto 的抬头格假设
   尺寸就是整串信箱的宽度，光靠 flex-shrink 救不了 —— 分行那一步已经把
   徽章推到第二行去了（塞占位 item 也没用，排不下时占位自己会跳行）。
   把 flex-basis 压成 0，分行时抬头格「不占宽度」，徽章一定留在第一行；
   接着 flex-grow 再把它长回来，而 max-width:max-content 让它长到内容宽度
   就停 —— 短抬头旁边的徽章仍然贴着走，长抬头则吃光剩下的空间后截断。
   （max-width 会一起夹住分行用的假设尺寸，所以不能写成 calc(100% - Npx)：
     那又变回一个大到会把徽章挤下去的假设尺寸。让位改用 margin，见下面
     .ios-c-tag-last / :not(:has(>td.ios-c-tag-last)) 那两条。） */
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-head{
  order:0;align-self:center;
  flex:1 1 0;max-width:max-content;min-width:0;
  display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  background:none!important;
  padding:0 0 2px;
  font-size:15px;font-weight:700;letter-spacing:-.015em;
  line-height:1.3;
}
/* 列渲染器给的行内 style（帐号池的抬头是 font-weight:550）会赢过上面那条 ——
   来源那张卡的抬头是 font-bold（700），量到的字重差看得出来。 */
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-head>*,
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-head>div>*:first-child{
  font-weight:700!important;
}
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-head::before{
  content:attr(data-avatar);
  flex:0 0 auto;
  width:44px;height:44px;
  display:flex;align-items:center;justify-content:center;
  margin:0;
  border-radius:var(--ios-r-2xl);
  background:var(--ios-av,var(--ios-grad-primary));
  color:#fff;font-size:17px;font-weight:700;letter-spacing:0;
  box-shadow:0 4px 6px -1px rgba(31,38,65,.18);
}
/* 头像底色由字串决定 —— 同一个人换页、换排序都是同一个颜色。
   --ios-tint 是同一个色相的极淡版本，给整张卡当底色用（见上面 <tr> 的
   background 第二层）。7% 的 alpha 在 62% 处收干净 —— 来源那几张卡的
   色调量到就是这个量级（卡面 (249,236,240) 对页底 (244,227,223)）。 */
[data-skin="ios"] .ios-av-0{--ios-av:linear-gradient(135deg,#0a84ff,#5e5ce6);
  --ios-tint:linear-gradient(150deg,rgba(10,132,255,.09),rgba(94,92,230,.03) 62%,rgba(255,255,255,0))}
[data-skin="ios"] .ios-av-1{--ios-av:linear-gradient(135deg,#30d158,#0a84ff);
  --ios-tint:linear-gradient(150deg,rgba(48,209,88,.10),rgba(10,132,255,.03) 62%,rgba(255,255,255,0))}
[data-skin="ios"] .ios-av-2{--ios-av:linear-gradient(135deg,#af52de,#bf5af2);
  --ios-tint:linear-gradient(150deg,rgba(175,82,222,.09),rgba(191,90,242,.03) 62%,rgba(255,255,255,0))}
[data-skin="ios"] .ios-av-3{--ios-av:linear-gradient(135deg,#ff9f0a,#ff2d55);
  --ios-tint:linear-gradient(150deg,rgba(255,159,10,.10),rgba(255,45,85,.03) 62%,rgba(255,255,255,0))}
[data-skin="ios"] .ios-av-4{--ios-av:linear-gradient(135deg,#ff2d55,#af52de);
  --ios-tint:linear-gradient(150deg,rgba(255,45,85,.08),rgba(175,82,222,.03) 62%,rgba(255,255,255,0))}
[data-skin="ios"] .ios-av-5{--ios-av:linear-gradient(135deg,#00c7be,#30d158);
  --ios-tint:linear-gradient(150deg,rgba(0,199,190,.10),rgba(48,209,88,.03) 62%,rgba(255,255,255,0))}
[data-skin="ios"] .ios-av-6{--ios-av:linear-gradient(135deg,#5e5ce6,#af52de);
  --ios-tint:linear-gradient(150deg,rgba(94,92,230,.09),rgba(175,82,222,.03) 62%,rgba(255,255,255,0))}
[data-skin="ios"] .ios-av-7{--ios-av:linear-gradient(135deg,#ff9f0a,#ffd60a);
  --ios-tint:linear-gradient(150deg,rgba(255,159,10,.10),rgba(255,214,10,.03) 62%,rgba(255,255,255,0))}
/* 深色主题不铺这层：深底上加一层半透明彩色不会变成「淡色调」，
   会变成一层蒙雾。深色那边的色相已经由上缘那条色带与头像给了。 */
[data-theme="dark"][data-skin="ios"] table.ios-cards>tbody>tr{--ios-tint:none}

/* ── 只有徽章的格：贴在标题旁边，不做成栏位 ───────────────── */
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-tag{
  order:1;flex:0 0 auto;
  align-self:center;
  background:none!important;padding:0;
}
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-tag::before{content:none}
/* data-card="badge" 指到的栏不一定真的包着 .badge（例如「供应商」只是文字）。
   没有徽章可用时自己长一颗药丸出来 —— 来源 Pill：
   rounded-full px-2 py-0.5 text-[11px] font-semibold border-white/50 */
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-tag:not(:has(.badge)):not(:has(.pill)):not(:has(.health-dot)){
  display:inline-flex;align-items:center;gap:4px;
  padding:2px 9px;border-radius:999px;
  background:var(--ios-inset-bg)!important;
  border:1px solid var(--ios-inset-border);
  font-size:11px;font-weight:600;line-height:1.5;
  color:var(--ios-label);
  white-space:nowrap;
}
/* 状态药丸前面那颗小圆点（来源 admin-channels.tsx:239 的 `● 运行中`）。
   认哪一颗是「状态」不靠猜：labelCells 把 <th> 的文字抄进了 data-label，
   所以这里比的就是那一栏的栏名本身。栏名对不上就不长点 —— 不会在
   「类型」「计划」那种徽章前面凭空多一个看起来像状态灯的东西。
   同一格里的第二颗徽章（帐号池的「已停用 / 自动停用」）不加，
   来源那颗点也只有一个。 */
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-tag[data-label="状态"]>.badge:first-child::before,
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-tag[data-label="状态"]>.pill:first-child::before{
  content:'\25CF';
  margin-right:3px;
  font-size:.72em;line-height:1;
}
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-tag[data-label="状态"]:not(:has(.badge)):not(:has(.pill)):not(:has(.health-dot))::before{
  content:'\25CF';
  font-size:.72em;line-height:1;
}

/* ── 副标行（data-card="sub"）────────────────────────────────
   来源 admin-users.tsx:143 —— 标题底下一条 11px 的灰线，
   `@username · email · 注册日期` 用 " · " 串成一行，truncate 收尾：
     <div class="mt-0.5 truncate text-[11px] text-[#8e8e93]">
   我们没有「一个 div 装三段字」的自由（一栏就是一个 td），所以让
   每个 sub 格自己排成 inline 的一段，中间那颗分隔点由 ::after 补 ——
   最后一格不补。「最后一格」不能用 :last-of-type 判：那数的是 <td> 兄弟，
   sub 不见得排在最后一栏（帐号池的 sub 是第 1/12/13 栏）。
   由 ui.js 的 labelCells 打 .ios-c-sub-last，跟 .ios-c-tag-last 同一套做法。 */
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-sub{
  order:2;flex:0 1 auto;min-width:0;max-width:100%;
  align-self:baseline;
  margin:0;padding:0;
  border:none;border-radius:0;
  background:none!important;
  font-size:11px!important;font-weight:400!important;line-height:1.45;
  letter-spacing:0;
  color:var(--ios-label)!important;
  text-align:left!important;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
/* 栏位名不印 —— 这一行读起来要像一句话，不是三个「标签：值」 */
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-sub::before{content:none}
/* 行内 style（font-size:12px / color:var(--text-m) / 直排的 div）会赢过上面那条 */
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-sub>*{
  display:inline!important;
  font-size:11px!important;font-weight:400!important;
  color:var(--ios-label)!important;
  max-width:100%!important;
}
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-sub:not(.ios-c-sub-last)::after{
  content:' · ';color:var(--ios-label-dim);
}
/* sub 那一行要自己占一行：上缘的断行由 tr::before（order:2，flex 里排在
   所有真实子元素之前）给，下缘再补一个 tr::after —— 否则第一颗数字磁贴
   会挤上来跟副标同行。只有真的有 sub 格的列才长这个伪元素，
   不然每张卡都会多吃一次 row-gap。 */
[data-skin="ios"] table.ios-cards>tbody>tr:has(>td.ios-c-sub)::after{
  content:'';order:2;flex:0 0 100%;height:0;margin:0;padding:0;
}

/* ── 药丸列（data-card="chip"）───────────────────────────────────
   来源 admin-channels.tsx:277-281 —— 副标底下、数字磁贴上面一排小药丸，
   渠道卡放的是这条线路支援的模型：
     <span class="num rounded-lg bg-black/[0.05] px-2 py-1
                  text-[10.5px] font-semibold text-[#48484a]">
   量 shot-13 那三颗：高 22px、圆角 10–11px（rounded-lg 在来源的
   @theme 里是 1rem，22px 高会被夹成 11px，等于一颗胶囊）、等宽字。

   这一排放什么是「有什么才放什么」：帐号池放的是供应商与凭证类型
   （accounts.provider / accounts.type，两个都是真的栏位，default 皮肤下
   本来就各自是一栏）。没有东西可放的表就一颗都不长 —— 宁可空着，
   也不生一排看起来像资料的装饰。 */
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-chip{
  order:3;flex:0 0 auto;align-self:center;
  margin:0;padding:2px 8px;
  border:none;border-radius:10px;
  background:rgba(0,0,0,.05);
  font-family:"SF Mono",ui-monospace,"JetBrains Mono","Fira Code",monospace;
  font-size:10.5px!important;font-weight:600!important;line-height:1.6;
  letter-spacing:0;
  color:#48484a!important;
  text-align:left!important;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%;
}
[data-theme="dark"][data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-chip{
  background:rgba(255,255,255,.08);color:var(--text-s)!important;
}
[data-skin="ios"] table.ios-cards>tbody>tr:hover>td.ios-c-chip{background:rgba(0,0,0,.05)}
[data-theme="dark"][data-skin="ios"] table.ios-cards>tbody>tr:hover>td.ios-c-chip{background:rgba(255,255,255,.08)}
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-chip::before{content:none}
/* 格子里本来就包着一颗徽章的（供应商 / 类型都是 .badge）—— 药丸里再套一颗
   药丸就是两层底色。把里面那颗摊平成纯文字，外面这一格才是药丸。 */
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-chip .badge,
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-chip .pill{
  background:none!important;color:inherit!important;
  padding:0!important;border:none!important;border-radius:0!important;
  font-size:inherit!important;font-weight:inherit!important;letter-spacing:0!important;
}

/* 药丸列与数字磁贴之间的硬换行。节点由 ui.js 的 UI._rowBreak 建
   （<tr> 的直接子元素、不是 <td>，所以 row.cells 数不到它）——
   理由与「为什么不能再用一个伪元素」写在那支函式的注解里。
   order:4 卡在药丸（3）与磁贴（5）中间，所以它挂在 <tr> 的哪个位置都无所谓。 */
[data-skin="ios"] table.ios-cards>tbody>tr>.ios-rowbreak{
  display:block;order:4;flex:0 0 100%;
  width:100%;height:0;min-height:0;
  margin:0;padding:0;border:none;
  visibility:hidden;                 /* 0 高度 + 不可见：不占空间也不吃 Tab */
}
/* 抽屉里同样需要这条换行：那边的磁贴是 2×2（flex-basis 50%），
   少了它第一颗磁贴会挤到药丸旁边，2×2 变成三行。 */

/* ── 大数字格（data-card="metric"）───────────────────────────
   来源 admin-users.tsx:148-159（列表卡，不是详情抽屉）：
     <div class="mt-4 grid grid-cols-4 gap-2">
       <div class="rounded-2xl bg-black/[0.04] px-3 py-2.5 text-center">
         <div class="truncate text-[13px] font-bold" style="color:…">值</div>
         <div class="mt-0.5 text-[10px] text-[#8e8e93]">标签</div>
   四个一排、置中、**值在上标签在下**、13px/700 的值配 10px 的灰标签。
   （抽屉里的 Stat 是另一套：grid-cols-2 的 2×2、标签在上 —— 两者刻意不同，
     列表卡要扫得快，抽屉要读得清。这里做的是列表卡。）
   值在上 ⇒ 标签只能走 ::after；上面那条泛用的 td[data-label]::before
   要一并关掉，否则标签会上下各印一次。
   列渲染器给的行内 style（text-align:right、color:var(--green)、font-size:12px）
   都要压掉，不然数字会靠右、还会被刷回 12px。 */
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-metric{
  order:5;
  /* basis 25% = 来源那个 grid-cols-4；min-width 只挡「窄到没法读」，
     不再撑成两栏 —— 只有两三个数字的表就让它们长成更宽的磁贴，
     跟来源在 stat 比较少时的行为一致。 */
  flex:1 1 calc(25% - 6px);
  min-width:64px;
  /* display:block（不是 flex）是有原因的：flex 容器里的匿名 flex item
     拿不到 text-overflow，长数字会变成硬切；block 排版下 Chrome 会把
     裸文字包进匿名区块并保留省略号。实测过两种都试。 */
  display:block;
  margin:0;padding:7px 10px 8px;
  border:none;border-radius:var(--ios-r-2xl);
  /* 卡面上的数字不装盒子：来源那四格是直接坐在卡面上的（shot-13 量到的
     底色变化只有 bg-black/[0.04]，4× 放大才勉强看得出边界）。我们原本用的
     --ios-inset-bg 是 rgba(120,120,128,.09)，四格排开等于把半张卡刷成灰色，
     连玻璃底下的色调都被压掉了。抽屉里的 2×2 仍然是内嵌盒 —— 那是弹层的
     版面，两者刻意不同（见 11b 段）。 */
  background:none;
  text-align:center!important;
  font-size:13px;font-weight:700;line-height:1.25;
  letter-spacing:-.01em;
  font-variant-numeric:tabular-nums;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  color:var(--ios-m-c,var(--text))!important;
}
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-metric::before{content:none}
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-metric::after{
  content:attr(data-label);
  display:block;margin-top:2px;
  font-size:10px;font-weight:400;line-height:1.3;letter-spacing:0;
  color:var(--ios-label);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
/* 里面若还包着 span / div，行内的 font-size 会赢过上面那条 —— 拉回来 */
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-metric>span,
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-metric>div{
  font-size:inherit!important;font-weight:inherit!important;
  color:inherit!important;display:inline;
}
/* 语意配色：钱=绿、笔数=蓝、次要=紫、告警=橘、错误=红（来源 admin-users.tsx 的
   #007aff / #af52de / #ff9f0a / #30b250 那组） */
[data-skin="ios"] td.ios-c-metric[data-tone="money"]{--ios-m-c:#30b250}
[data-skin="ios"] td.ios-c-metric[data-tone="count"]{--ios-m-c:#007aff}
[data-skin="ios"] td.ios-c-metric[data-tone="alt"]  {--ios-m-c:#af52de}
[data-skin="ios"] td.ios-c-metric[data-tone="warn"] {--ios-m-c:#ff9f0a}
[data-skin="ios"] td.ios-c-metric[data-tone="danger"]{--ios-m-c:#ff3b30}
[data-skin="ios"] td.ios-c-metric[data-tone="plain"]{--ios-m-c:var(--text)}
[data-theme="dark"][data-skin="ios"] td.ios-c-metric[data-tone="money"]{--ios-m-c:#30d158}
[data-theme="dark"][data-skin="ios"] td.ios-c-metric[data-tone="count"]{--ios-m-c:#409cff}
/* 0 不该跟真的有数字一样抢眼（来源的 0 也是淡的） */
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-metric[data-blank="1"]{
  --ios-m-c:var(--ios-label-dim);
}

/* 新面板的那几张柱状图：只有一天资料时 .bar 的 flex:1 会把单根柱子
   撑成一整块色板，看起来像画错了。夹一个上限，14 天时本来就比这窄。
   这四个 id 只存在于这一轮新加的面板里，既有图表一格不动。 */
#alTrend .bar, #maTrend .bar, #rpTrend .bar, #rpMyTrend .bar{max-width:56px}

/* ── 双色占比条（.fbar）──────────────────────────────────────
   与 UI.ratioBar 产出的 .ios-ratio 是两回事：那一套的样式全挂在
   [data-skin="ios"] 底下，放进 default 皮肤就是一排看不见的空 span。
   新面板的「健康 / 构成」那一格两种皮肤都要看得到，所以这条不分皮肤。
   分母为 0 时呼叫端根本不产生 .fbar（见 admin.html 的 faceBar）——
   这里不会出现一条空轨道假装「量到了、全是 0」。 */
.fbar{display:inline-flex;align-items:center;gap:7px;min-width:150px;max-width:100%}
.fbar>b{flex:0 0 auto;font-size:10.5px;font-weight:500;color:var(--text-m)}
.fbar-t{
  flex:1 1 auto;min-width:48px;height:6px;
  display:flex;overflow:hidden;
  border-radius:999px;background:rgba(0,0,0,.09);
}
[data-theme="dark"] .fbar-t{background:rgba(255,255,255,.12)}
.fbar-t>span{display:block;height:100%}
.fbar-t>span:first-child{border-radius:999px 0 0 999px}
.fbar-t>span:last-child{border-radius:0 999px 999px 0}
.fbar-t>span:only-child{border-radius:999px}
.fbar>i{
  flex:0 0 auto;font-style:normal;font-size:10.5px;
  font-variant-numeric:tabular-nums;color:var(--text-s);white-space:nowrap;
}

/* ── 没有上限的那一条（.fbar-none）───────────────────────────
   lib/account-limits.js 的 usageFor 在没设上限时回 pct = null，不是 0。
   所以这里**不画轨道**：一条 0% 的空轨道会被读成「用了 0%」，可是
   「没设上限」根本没有水位这回事。观测到的实际用量照样印（「这一分钟
   3 次」就算没有上限也是真的），只是它旁边不该有一条假的进度条。
   目前池子里绝大多数账号都是这个状态，所以这是常态样式、不是边缘情况。 */
.fbar-none{min-width:0}
.fbar-none>i{color:var(--text-m)}
/* 上限设成 0 = 明确停用（三态里的第二态）。那不是水位低，是闸门关死了 —— 用告警色。 */
.ph-cap-off{color:var(--danger)!important}

/* ── 封号风险的四条讯号（lib/ban-risk.js）─────────────────────
   三种状态要在一眼之内分得开，而且**「未观测」与「不适用」不能长得像
   「已观测而且干净」**：那正是这个功能要防的绿勾。
     .ph-sig-hit      这条讯号上有真的证据 —— 告警色
     .ph-sig-unknown  还没有任何一次可分类的观测 —— 降到次要色 + 虚线底
     .ph-sig-na       这种账号结构上就没有这条路（apikey 不刷新 token）—— 同上，
                      但语义不同，靠 title 讲清楚
   三个 class 只出现在 #phTable 的风险信号那一格，不会碰到别的面板。 */
.ph-sig-hit>i{color:var(--danger)!important;font-weight:600}
.ph-sig-unknown>b,.ph-sig-na>b{opacity:.55}
.ph-sig-unknown>i,.ph-sig-na>i{
  opacity:.7;border-bottom:1px dotted currentColor;
}

/* 一格里叠三条水位（额度 / RPM / TPM）。表格里直排，
   iOS 卡面上由 .ios-c-face>div 那条 !important 拉成横排并自动换行。 */
.ph-meters{display:flex;flex-direction:column;gap:4px;min-width:170px}
.ph-meters>.badge{align-self:flex-start}

/* 18 栏挤在一屏里，中文栏位会被压到一个字一行 —— 「从未探测过」变成竖排。
   外层本来就有 overflow-x:auto，所以不换行是安全的：宽度不够时走水平卷动，
   而不是把字排成一直行。最后错误那栏本来就靠 ellipsis 截断，nowrap 是它需要的。 */
#phTable th,
#phTable td{white-space:nowrap}
/* 卡面那格里叠着水位条，得留它自己换行 */
#phTable td .ph-meters,
#phTable td .ph-meters *{white-space:normal}

/* 额度 / 速率上限的编辑器：一格里两个紧凑控件。
   default 皮肤下是普通表格栏；iOS 皮肤下由 data-card="control" 收进详情抽屉，
   在那里 .ios-c-ctl 会把整块拉成整行宽。 */
.ph-lim{display:flex;align-items:center;gap:6px;flex-wrap:wrap}
/* 两个数字框长得一模一样，各自挂一个小标 —— 只靠 placeholder 的话，
   一旦填了值就分不出哪个是 RPM 哪个是 TPM。 */
.ph-lim-g{display:inline-flex;align-items:center;gap:4px}
.ph-lim-g>b{flex:0 0 auto;font-size:10.5px;font-weight:500;color:var(--text-m)}
.ph-lim .ph-lim-in{width:80px}
.ph-lim select.ph-lim-in{width:100px}
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-ctl>.ph-lim{
  flex:1 1 auto;justify-content:flex-end;margin-left:auto;
}
/* .ios-c-ctl 把所有数字框压成 64px；「不限」两个字加上微调箭头放不下，
   会被截成一个看不懂的符号。只放宽这三个框。 */
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-ctl .ph-lim .ph-lim-in{
  width:84px!important;flex:0 0 auto;margin:0!important;
}
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-ctl .ph-lim select.ph-lim-in{
  width:auto!important;flex:1 1 92px;min-width:92px;
}

/* ── 卡面横幅（data-card="face"）──────────────────────────────
   来源 round18-admin-pool-cards：卡面上除了一排数字磁贴，还有健康环、
   额度条与三条水位。那些东西是「一眼判断这一列健不健康」用的，
   收进抽屉等于每一列都得点开才知道 —— 所以独立一个角色：
   跟 detail 一样占满整行，但 **不进抽屉**（下面那条 .ios-row-drawer 的
   display:none 只列了 detail 与 ctl）。

   与 detail 的差别：detail 是「标签 → 值」一行文字，face 是一块图形。
   所以标签走 ::before 但独立成一行（小写、灰、11px），内容左对齐、
   可以换行 —— 环跟条排不下时往下掉，不要横向溢出把卡片撑宽。 */
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-face{
  order:6;flex:0 0 100%;
  /* 标签走同一行的左侧（跟 .ios-c-ctl 一样），不另起一行 ——
     卡面每多一行标题就多 16px，四张卡就是一屏。 */
  display:flex;align-items:center;flex-wrap:wrap;gap:8px;
  margin:0;padding:5px 11px 6px;
  border:none;border-radius:14px;
  /* 跟数字磁贴同一个理由：卡面上的内嵌灰块一多，玻璃底下的色调就整片被压掉。
     这一块留一点点底色（它是一群不同形状的东西 —— 环、条、折线 —— 需要
     一个框把它们收在一起），但从 rgba(120,120,128,.09) 降到 3.5% 的黑。
     抽屉里那一份仍然是完整的 glass-inset，见底下 .ios-card-open 那条。 */
  background:rgba(0,0,0,.035);
  font-size:12px;line-height:1.35;
  text-align:left!important;
  white-space:normal;
}
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-face::before{
  content:attr(data-label);
  flex:0 0 auto;
  font-size:11.5px;font-weight:500;letter-spacing:0;
  color:var(--ios-label);
  white-space:nowrap;
}
/* 里面那排东西：健康环 / 条 / sparkline 横排，排不下就换行。
   既有的列渲染器（账号池的「健康」、池健康度的「近期观测」）用的是
   行内 style 的直排 <div> —— 那是给表格看的，摊到卡面上要横过来，
   所以这里跟 .ios-c-detail>div 同一套写法用 !important 压掉行内值。 */
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-face>.ios-face-row,
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-face>div,
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-face>span{
  flex:1 1 auto;min-width:0;
  display:flex!important;flex-direction:row!important;flex-wrap:wrap;
  align-items:center!important;justify-content:flex-start;
  gap:4px 9px!important;text-align:left;
}
/* 环在卡面上不必跟表格里一样大 —— 缩一号，一张卡省 8px */
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-face .ios-ring{
  width:38px!important;height:38px!important;
}
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-face .ios-ring svg{width:38px;height:38px}
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-face .ios-ring .ios-ring-num{font-size:11px!important}
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-face .ios-face-bars{
  flex:1 1 150px;min-width:130px;
  display:flex;flex-direction:column;gap:5px;
}
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-face .ios-face-bar{
  display:flex;align-items:center;gap:7px;
}
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-face .ios-face-bar>b{
  flex:0 0 auto;min-width:30px;
  font-size:10px;font-weight:500;color:var(--ios-label);
}
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-face .ios-face-bar>.ios-ratio{
  flex:1 1 auto;min-width:0;margin:0;
}
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-face .ios-face-bar>i{
  flex:0 0 auto;font-style:normal;
  font-size:10px;font-variant-numeric:tabular-nums;color:var(--text-s);
}
/* 抽屉打开时这块跟其他内容一起摊平，不再是「一整行」而已 */
[data-skin="ios"] table.ios-cards>tbody>tr.ios-card-open>td.ios-c-face{
  background:var(--ios-inset-bg);padding:9px 14px 11px;
}

/* ── 明细行（data-card="detail"）─────────────────────────────
   来源 InfoRow：glass-inset flex justify-between rounded-2xl px-3.5 py-2.5 text-xs
   标签在左（灰）、值在右（粗）。整行占满卡片宽度。 */
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-detail{
  order:7;flex:0 0 100%;
  display:flex;align-items:center;justify-content:space-between;gap:12px;
  margin:0;padding:5px 12px;
  border:none;border-radius:12px;
  background:var(--ios-inset-bg);
  font-size:12.5px;line-height:1.4;
  text-align:right!important;
  min-height:32px;
}
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-detail::before{
  content:attr(data-label);
  flex:0 0 auto;margin:0;
  font-size:11.5px;font-weight:500;letter-spacing:0;
  color:var(--ios-label);
  white-space:nowrap;
}
/* 值那一侧：靠右、可截断，别把卡片撑开 */
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-detail>*{
  min-width:0;max-width:100%;
  margin-left:auto;
  overflow:hidden;text-overflow:ellipsis;
}
/* 列渲染器爱用直排的 <div>（健康栏 3 行、AT 到期 日期+徽章）。
   来源的 InfoRow 是一行 —— 摊成横排，卡片矮 20px，也比较好扫。 */
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-detail>div{
  display:flex!important;flex-direction:row!important;flex-wrap:wrap;
  align-items:center!important;justify-content:flex-end;
  gap:5px!important;text-align:right;
}
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-detail .hint-sm{font-size:11px}

/* ── 表单控制项（data-card="control"）─────────────────────────
   卡片正文里摆一个 <select> 看起来就是没做完（来源的卡片正文没有输入框，
   shot-13 只在右上角放一颗开关）。但归属 / 权重 / 日额度确实只有这里能改，
   所以做成半行宽的紧凑控制项：两个并排成一列，而不是各占一整行。 */
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-ctl{
  order:6;flex:1 1 calc(50% - 4px);min-width:150px;
  display:flex;align-items:center;gap:8px;
  margin:0;padding:4px 10px;
  border:none;border-radius:12px;
  background:var(--ios-inset-bg);
  font-size:12px;line-height:1.4;
  text-align:left!important;
  min-height:32px;
}
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-ctl::before{
  content:attr(data-label);
  flex:0 0 auto;
  font-size:11.5px;font-weight:500;letter-spacing:0;
  color:var(--ios-label);
  white-space:nowrap;
}
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-ctl>*{min-width:0}
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-ctl>div{
  display:flex!important;align-items:center;gap:4px;
  flex:1 1 auto;min-width:0;margin-left:auto;
}
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-ctl select,
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-ctl input:not([type="checkbox"]),
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-detail select,
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-detail input:not([type="checkbox"]){
  width:auto!important;max-width:100%!important;min-width:0;
  margin:0 0 0 auto;
  border-radius:9px!important;
  background:var(--surface)!important;
  border:1px solid var(--ios-inset-border)!important;
  padding:3px 7px!important;
  font-size:12px!important;
  text-align:left!important;
}
/* 抽屉里的选单同样不要白框。这两条的特异性（0,3,5）比下面那条通用的
   （0,2,5）高，所以必须在这里也写一次 —— 只加 !important 是压不过去的，
   两边都有 !important 时比的还是特异性。 */
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-ctl select,
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-detail select{
  background:var(--ios-inset-bg)!important;
  border-color:transparent!important;
}
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-ctl select{flex:1 1 auto}
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-ctl input[type="number"]{width:64px!important;flex:0 0 auto}
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-ctl .progress-bar,
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-detail .progress-bar{
  width:90px!important;min-width:90px;margin-top:0!important;
}

/* ── 没标到的栏：卡片里不出现 ────────────────────────────────
   资料还在 DOM 里 —— 栏位选单、排序、CSV 汇出、批次勾选全都还读得到，
   只是这一格不画。 */
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-off{display:none}

/* ── 勾选框：卡片右上角 ─────────────────────────────────────
   挑过栏位的表改摆在头像左边（见下）—— 右上角要让给开关。 */
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-check{
  position:absolute;top:14px;right:16px;z-index:2;
  flex:0 0 auto;order:-1;
  background:none!important;padding:0;border-radius:0;
}
/* 挑过栏位的表：勾选框压在头像上，平常收起来。
   来源那张卡根本没有勾选框（批次操作是我们独有的功能，而且是真的、
   有测试的），所以不删、只让它安静：滑过 / 键盘走到 / 已勾选 / 这张表
   已经进入「有东西被勾选」的状态时才现身，现身时头像淡出让位 ——
   跟 Finder、Gmail 同一套。
   位置刻意跟头像重叠（left/top = 卡片内距，40×40 = 头像大小）：
   放回文件流的话每次现身都会把标题往右推一次，读起来像画面在抖。 */
[data-skin="ios"] table.ios-cards-curated>tbody>tr>td.ios-c-check{
  position:absolute;left:16px;top:15px;z-index:3;
  order:-2;
  width:40px;height:40px;
  display:flex;align-items:center;justify-content:center;
  margin:0;padding:0;border-radius:13px;
  background:rgba(255,255,255,.62);
  backdrop-filter:blur(3px);-webkit-backdrop-filter:blur(3px);
  box-shadow:0 1px 3px rgba(31,38,65,.14);
  opacity:0;pointer-events:none;
  transition:opacity .16s var(--ease);
}
[data-theme="dark"][data-skin="ios"] table.ios-cards-curated>tbody>tr>td.ios-c-check{
  background:rgba(28,28,34,.72);
}
[data-skin="ios"] table.ios-cards-curated>tbody>tr:hover>td.ios-c-check,
[data-skin="ios"] table.ios-cards-curated>tbody>tr:focus-within>td.ios-c-check,
[data-skin="ios"] table.ios-cards-curated>tbody>tr:has(.row-check:checked)>td.ios-c-check,
[data-skin="ios"] table.ios-cards-curated:has(.row-check:checked)>tbody>tr>td.ios-c-check{
  opacity:1;pointer-events:auto;
}
/* 勾选框现身时头像退到背后 —— 两个 40×40 的方块叠在一起会看不懂 */
[data-skin="ios"] table.ios-cards-curated>tbody>tr:hover>td.ios-c-head::before,
[data-skin="ios"] table.ios-cards-curated>tbody>tr:focus-within>td.ios-c-head::before,
[data-skin="ios"] table.ios-cards-curated>tbody>tr:has(.row-check:checked)>td.ios-c-head::before,
[data-skin="ios"] table.ios-cards-curated:has(.row-check:checked)>tbody>tr>td.ios-c-head::before{
  opacity:.2;
}
[data-skin="ios"] table.ios-cards-curated>tbody>tr>td.ios-c-head::before{
  transition:opacity .16s var(--ease);
}
/* 没有 hover 的装置（手机 / 平板）：一律显示，不然勾不到 */
@media(hover:none){
  [data-skin="ios"] table.ios-cards-curated>tbody>tr>td.ios-c-check{opacity:1;pointer-events:auto}
  [data-skin="ios"] table.ios-cards-curated>tbody>tr>td.ios-c-head::before{opacity:.2}
}
@media(prefers-reduced-motion:reduce){
  [data-skin="ios"] table.ios-cards-curated>tbody>tr>td.ios-c-check,
  [data-skin="ios"] table.ios-cards-curated>tbody>tr>td.ios-c-head::before{transition:none}
}
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-check::before{content:none}
[data-skin="ios"] table.ios-cards .row-check,
[data-skin="ios"] table.ios-cards .check-all{
  width:18px;height:18px;border-radius:6px;accent-color:var(--ios-blue);
}

/* ── 展开箭头（使用记录 / 审计）：右上角 ─────────────────────── */
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-caret{
  position:absolute;top:12px;right:14px;z-index:2;
  flex:0 0 auto;order:-1;width:auto!important;
  background:none!important;padding:4px 6px;font-size:13px;
  color:var(--ios-label-dim);
}
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-caret::before{content:none}

/* ── 空值：整格不出现（来源的卡片不会印一排破折号）───────────── */
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-blank{display:none}

/* ── 操作栏：卡片页脚，上缘一条分隔线 ─────────────────────────
   这是「没有抽屉」那些卡（非 curated 的自动卡片）用的页脚：更小的钮、
   更紧的内距，读起来像卡片底下的一条附属条而不是第二张卡。
   挑过栏位的表已经跟来源一样了 —— 整排动作在详情抽屉里，
   卡面只留右上角那颗开关（见底下的 11b 段）。 */
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-act{
  order:8;flex:0 0 100%;
  display:flex;flex-wrap:wrap;align-items:center;gap:4px;
  margin-top:0;
  padding:6px 0 0;
  border-radius:0;
  border-top:1px solid var(--ios-hairline);
  background:none!important;
  text-align:left!important;
}
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-act::before{content:none}
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-act .btn{
  font-size:11px;font-weight:500;line-height:1.45;
  padding:2px 9px;border-radius:8px;
}

/* ── 停用 / 启用 → 卡片右上角的 iOS 开关 ─────────────────────
   来源 shot-13 / admin-channels.tsx：h-6 w-11 圆角胶囊 + h-5 w-5 白色圆钮。
   还是同一颗 <button>、同一个 click handler、打同一个端点 ——
   这里只是把它画成开关（文字用 font-size:0 收掉，aria-label 由 JS 补）。
   位置是相对 <tr> 的绝对定位（tr 本身 position:relative），
   所以它跳得出页脚、贴到卡片右上角。 */
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-act>.ios-c-sw{
  position:absolute;top:15px;right:16px;z-index:3;
  width:44px;height:26px;flex:0 0 auto;
  margin:0;padding:0!important;
  border:none!important;border-radius:999px!important;
  background:rgba(120,120,128,.28)!important;
  box-shadow:inset 0 0 0 1px rgba(0,0,0,.04)!important;
  font-size:0!important;line-height:0;color:transparent!important;
  cursor:pointer;overflow:hidden;
  transition:background .22s var(--ease);
}
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-act>.ios-c-sw[data-on="1"]{
  background:linear-gradient(135deg,#34c759,#30d158)!important;
}
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-act>.ios-c-sw::after{
  content:'';position:absolute;top:3px;left:3px;
  width:20px;height:20px;border-radius:50%;
  background:#fff;
  box-shadow:0 2px 4px rgba(0,0,0,.25),0 0 1px rgba(0,0,0,.12);
  transition:transform .22s var(--ios-spring);
}
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-act>.ios-c-sw[data-on="1"]::after{
  transform:translateX(18px);
}
[data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-act>.ios-c-sw:active{transform:scale(.9)}
/* 开关压在右上角（绝对定位，flex 排版看不见它），标题那一行得空出位置。
   给「标题列最后一个项目」一段 margin 就够了 —— margin 算进 flex 的外部
   尺寸，抬头的 flex-grow 长到那里就停，不会钻到开关底下。
   （ui.js 的 _tagLast 把 .ios-c-tag-last 标在最后一颗**还看得见**的徽章上；
    一颗都没有 / 都被栏位选单藏掉时，margin 改挂到抬头格自己身上。
    整张卡加 padding-right 会在右边留一条白沟，连数字磁贴都缩进来。） */
[data-skin="ios"] table.ios-cards>tbody>tr.ios-row-sw>td.ios-c-tag-last{margin-right:58px}
/* 一颗徽章都没有的列：抬头自己长到开关左边就停 */
[data-skin="ios"] table.ios-cards>tbody>tr.ios-row-sw:not(:has(>td.ios-c-tag-last))>td.ios-c-head{
  margin-right:62px;
}
[data-skin="ios"] table.ios-cards>tbody>tr.ios-row-sw>td.ios-c-head>div{min-width:0;overflow:hidden}
/* 没搬过勾选框的表（非 curated）勾选框还在右上角 —— 往左让给开关 */
[data-skin="ios"] table.ios-cards:not(.ios-cards-curated)>tbody>tr.ios-row-sw{padding-right:96px}
[data-skin="ios"] table.ios-cards:not(.ios-cards-curated)>tbody>tr.ios-row-sw>td.ios-c-check{right:62px}

/* ── 跨栏列（空状态 / 载入失败 / 展开的明细 / 无匹配结果）─────────
   这些不是资料列，摊平成整行、不套卡片外观。 */
[data-skin="ios"] table.ios-cards>tbody>tr.ios-row-wide,
[data-skin="ios"] table.ios-cards>tbody>tr:has(>td[colspan]){
  grid-column:1/-1;
  display:block;
  padding:0;
  background:none;backdrop-filter:none;-webkit-backdrop-filter:none;
  border:none;box-shadow:none;transform:none;
  border-radius:var(--ios-r-card);
  overflow:hidden;
}
[data-skin="ios"] table.ios-cards>tbody>tr.ios-row-wide:hover,
[data-skin="ios"] table.ios-cards>tbody>tr:has(>td[colspan]):hover{
  transform:none;box-shadow:none;
}
[data-skin="ios"] table.ios-cards>tbody>tr.ios-row-wide>td,
[data-skin="ios"] table.ios-cards>tbody>tr:has(>td[colspan])>td{
  display:block;flex:none;order:0;width:auto;
  background:none;border:none;border-radius:var(--ios-r-card);
}
[data-skin="ios"] table.ios-cards>tbody>tr.ios-row-wide>td::before,
[data-skin="ios"] table.ios-cards>tbody>tr:has(>td[colspan])>td::before{content:none}

/* 表格内的输入框在卡片里可以放宽（原本为了塞进窄栏被压到 56px） */
[data-skin="ios"] table.ios-cards>tbody>tr>td select,
[data-skin="ios"] table.ios-cards>tbody>tr>td input:not([type="checkbox"]){
  max-width:100%!important;width:100%!important;
  border-radius:10px!important;
  background:var(--surface)!important;
  border:1px solid var(--ios-inset-border)!important;
  padding:5px 8px!important;
}
[data-skin="ios"] table.ios-cards>tbody>tr>td .weight-input{text-align:left!important}

/* 卡片／抽屉里的选单同样不要白框。这里比上面更明显：--surface 在 iOS 皮肤下
   是 rgba(255,255,255,.62)，几乎是实心白，叠上白边就是玻璃卡上一个白盒子。
   改用 --ios-inset-bg（跟指标格、InfoRow 同一种凹陷底色），观感一致。
   上面那条带 !important，所以这里也得带。
   文字输入框刻意维持原样 —— 要打字的框子有实底比较好认。 */
[data-skin="ios"] table.ios-cards>tbody>tr>td select{
  background:var(--ios-inset-bg)!important;
  border-color:transparent!important;
}

/* ── 挑过栏位的卡片要更紧 ─────────────────────────────────────
   来源 shot-09 的用户卡在 1100px 的内容宽度下大约只有卡宽的 0.27 倍高。
   我们的卡多了页脚跟明细行，追不到 0.27，但版面的松紧要对得上：
   内距 p-5 → 14/16，格与格之间 8 → 7。 */
[data-skin="ios"] table.ios-cards-curated>tbody>tr{
  gap:7px;padding:15px 16px;
}
[data-skin="ios"] table.ios-cards-curated>tbody>tr>td.ios-c-head{
  /* 16 而不是来源的 15：来源那张卡宽 472px（量 shot-13 的色带两端 + 圆角
     反推），15px 占卡宽的 3.18%；我们的卡在 1440 视窗下是 542px，同样 15px
     只剩 2.77%，看起来就是小一号。16px = 2.95%，回到同一个视觉比例。 */
  padding:0;font-size:16px;
  /* 标题收成一行，跟在头像右边：
     · flex-wrap:nowrap —— 不然代理池那种一整条 URL 会整段掉到头像底下
       （头像自己就占满了第一行）
     · white-space:nowrap + overflow:hidden —— 一张卡的抬头长到三四行的话
       整个格线就参差不齐了。被切掉的部分看 title，那一格本来就带着完整字串。 */
  flex-wrap:nowrap;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
[data-skin="ios"] table.ios-cards-curated>tbody>tr>td.ios-c-head>*{min-width:0}
[data-skin="ios"] table.ios-cards-curated>tbody>tr>td.ios-c-head::before{
  width:40px;height:40px;font-size:16px;border-radius:13px;
}
/* 有真的副标行时，标题只留第一行。
   帐号池的标题格是列渲染器给的直排 <div>：第一行是信箱，第二行又是那串
   截断 ID —— 而 ID 已经是副标的第一段了。同一串字在同一张卡上印两次，
   看起来就像忘了删。第一行以外的都收掉（没有 label 的列第一行是「未命名」，
   照样留着）。 */
[data-skin="ios"] table.ios-cards-curated>tbody>tr:has(>td.ios-c-sub)>td.ios-c-head>div>*:not(:first-child){
  display:none;
}

/* ═══ 11b. 详情抽屉 ══════════════════════════════════════════════
   来源 admin-users.tsx:126（整张列表卡是一颗 <button onClick={setDetail}>）
   与 260-345（抽屉本体），画面见 .zscripts/shot-10-userdetail.png。

   卡面：头像 + 抬头 + 徽章 + 开关 + 副标 + 一排数字磁贴 + 右边一个箭头。
   次要栏位（detail）、下拉与输入框（control）、整排动作按钮（act）
   都不在卡面上 —— 点一下才在抽屉里出现。这是把卡片高度从 0.64 压到
   0.35（来源是 0.35）唯一剩下的那一段。

   实作上「不搬任何节点」：<tr> 还在 <tbody> 原位，只是多一个
   .ios-card-open，CSS 把它改画成固定定位、置中、可捲动的一张卡。
   搬家会同时弄坏 UI.syncRows 的节点身分、栏位选单的 cells[i] 行内样式、
   以及每颗按钮 onclick 依赖的「我在这一列」上下文 —— 换装一件都不碰。
   ════════════════════════════════════════════════════════════════ */

/* ── 卡面：detail / control 收进抽屉，主要动作留在页脚 ────────────
   这里改过一次：以前整排动作都收进抽屉，理由写的是「来源的列表卡整张是
   一颗按钮，卡面没有动作」—— 那句话对的是**用户卡**（shot-09 / shot-10 /
   admin-users.tsx）。渠道卡不是：admin-channels.tsx:335-345 的卡面底下就有
   一条分隔线 + 「🩺 连通测试 / ⚙️ 编辑配置 / 🗑 删除」，shot-13 量得到
   （分隔线在 y=395，蓝字在 y≈413-419，卡片下缘 y=450）。
   帐号池对应的是渠道卡，所以卡面留前 3 颗，其余（与全部栏位）仍在抽屉里。 */
[data-skin="ios"] table.ios-cards-curated>tbody>tr.ios-row-drawer:not(.ios-card-open)>td.ios-c-detail,
[data-skin="ios"] table.ios-cards-curated>tbody>tr.ios-row-drawer:not(.ios-card-open)>td.ios-c-ctl{
  display:none;
}
/* 页脚本体：上缘一条发丝线 + 一排平分宽度的连结（来源 flex + flex-1 + 置中）。
   开关仍然是这一格的子节点，但它是绝对定位的，不参与页脚的 flex 排版 ——
   位置照样算相对 <tr>（tr 是 position:relative），所以还是贴在右上角。 */
[data-skin="ios"] table.ios-cards-curated>tbody>tr.ios-row-drawer:not(.ios-card-open)>td.ios-c-act{
  order:8;flex:0 0 100%;
  position:static;pointer-events:auto;overflow:visible;
  display:flex;align-items:stretch;gap:0;
  margin:3px 0 0;padding:8px 0 0;
  border:none;border-top:1px solid var(--ios-hairline);border-radius:0;
  background:none!important;
  text-align:center!important;
}
[data-skin="ios"] table.ios-cards-curated>tbody>tr.ios-row-drawer:not(.ios-card-open)>td.ios-c-act::before{content:none}
[data-skin="ios"] table.ios-cards-curated>tbody>tr.ios-row-drawer:not(.ios-card-open)>td.ios-c-act>*{
  display:none;
}
[data-skin="ios"] table.ios-cards-curated>tbody>tr.ios-row-drawer:not(.ios-card-open)>td.ios-c-act>.ios-c-sw{
  display:block;pointer-events:auto;
}
/* 留在卡面上的那几颗（.ios-c-act-face 由 ui.js 的 _faceActions 标，
   取 DOM 顺序的前 3 颗、开关不算）。来源是 13px semibold 的纯文字连结，
   没有底色也没有边框 —— 页脚要读起来像卡片的一部分，不是第二排按钮。 */
[data-skin="ios"] table.ios-cards-curated>tbody>tr.ios-row-drawer:not(.ios-card-open)>td.ios-c-act>.ios-c-act-face{
  display:block;flex:1 1 0;min-width:0;
  margin:0;padding:4px 6px!important;
  border:none!important;border-radius:10px!important;
  background:none!important;box-shadow:none!important;
  font-family:inherit;
  font-size:13px!important;font-weight:600!important;line-height:1.5!important;
  letter-spacing:0;
  color:var(--text-s)!important;
  text-align:center!important;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  cursor:pointer;
  transition:background .16s var(--ease),color .16s var(--ease);
}
/* 来源那三颗是 蓝 / 灰 / 红：第一颗是主要动作，删除是破坏性动作 */
[data-skin="ios"] table.ios-cards-curated>tbody>tr.ios-row-drawer:not(.ios-card-open)>td.ios-c-act>.ios-c-act-face[data-face-i="0"]{
  color:var(--ios-blue)!important;
}
[data-skin="ios"] table.ios-cards-curated>tbody>tr.ios-row-drawer:not(.ios-card-open)>td.ios-c-act>.btn-danger.ios-c-act-face,
[data-skin="ios"] table.ios-cards-curated>tbody>tr.ios-row-drawer:not(.ios-card-open)>td.ios-c-act>.mini.danger.ios-c-act-face{
  color:#ff3b30!important;
}
[data-skin="ios"] table.ios-cards-curated>tbody>tr.ios-row-drawer:not(.ios-card-open)>td.ios-c-act>.ios-c-act-face:hover{
  background:rgba(0,0,0,.045)!important;
}
[data-theme="dark"][data-skin="ios"] table.ios-cards-curated>tbody>tr.ios-row-drawer:not(.ios-card-open)>td.ios-c-act>.ios-c-act-face:hover{
  background:rgba(255,255,255,.07)!important;
}
[data-skin="ios"] table.ios-cards-curated>tbody>tr.ios-row-drawer:not(.ios-card-open)>td.ios-c-act>.ios-c-act-face:active{
  transform:scale(.96);
}
[data-skin="ios"] table.ios-cards-curated>tbody>tr.ios-row-drawer:not(.ios-card-open)>td.ios-c-act>.ios-c-act-face[disabled]{
  opacity:.4;cursor:not-allowed;
}

/* ── 卡面右边那个箭头（来源 admin-users.tsx:145 的 m9 6 6 6-6 6）──
   挂在抬头格的 ::after 上：抬头格自己不是定位元素，所以这个绝对定位
   算的是 <tr>，等于「卡片的右缘」。有开关的列往左让到开关旁边 ——
   两个东西都在抬头那一行，跟来源的版面一致。
   不给整张卡 padding-right：那会把底下的数字磁贴一起缩进来，
   可是箭头只占抬头那一行（来源的磁贴是撑满卡宽的 grid-cols-4）。
   让位改用「最后一颗徽章的 margin」+「抬头的 max-width」，
   跟开关用的是同一套办法。 */
[data-skin="ios"] table.ios-cards-curated>tbody>tr.ios-row-drawer{cursor:pointer}
[data-skin="ios"] table.ios-cards-curated>tbody>tr.ios-row-drawer:focus-visible{
  outline:none;border-color:rgba(0,122,255,.6);
  box-shadow:0 0 0 3px rgba(0,122,255,.18),var(--ios-glass-shadow);
}
[data-skin="ios"] table.ios-cards-curated>tbody>tr.ios-row-drawer>td.ios-c-head::after{
  content:'';position:absolute;right:16px;top:35px;
  width:9px;height:9px;
  border-right:2px solid var(--ios-label-dim);
  border-top:2px solid var(--ios-label-dim);
  transform:translateY(-50%) rotate(45deg);
  pointer-events:none;
}
[data-skin="ios"] table.ios-cards-curated>tbody>tr.ios-row-drawer.ios-row-sw>td.ios-c-head::after{
  right:70px;
}
/* 抬头 / 徽章要让出的宽度：只有箭头 = 33px；箭头 + 开关 = 91px。
   让位一律走 margin，不走 max-width —— max-width 会一起夹住 flex 分行用的
   假设尺寸，那正是「徽章掉到第二行」那个 bug 的来源（见 .ios-c-head 的注解）。
   没有徽章（或徽章都被栏位选单藏掉）时改挂到抬头格上。 */
[data-skin="ios"] table.ios-cards-curated>tbody>tr.ios-row-drawer>td.ios-c-tag-last{
  margin-right:33px;
}
[data-skin="ios"] table.ios-cards-curated>tbody>tr.ios-row-drawer:not(:has(>td.ios-c-tag-last))>td.ios-c-head{
  margin-right:33px;
}
[data-skin="ios"] table.ios-cards-curated>tbody>tr.ios-row-drawer.ios-row-sw>td.ios-c-tag-last{
  margin-right:91px;
}
[data-skin="ios"] table.ios-cards-curated>tbody>tr.ios-row-drawer.ios-row-sw:not(:has(>td.ios-c-tag-last))>td.ios-c-head{
  margin-right:91px;
}
[data-skin="ios"] table.ios-cards-curated>tbody>tr.ios-card-open>td.ios-c-head::after{content:none}
/* 抽屉里开关跟着动作列走、箭头也不见了 —— 让位的 margin 要收回来 */
[data-skin="ios"] table.ios-cards>tbody>tr.ios-card-open>td.ios-c-tag-last{margin-right:0}
[data-skin="ios"] table.ios-cards>tbody>tr.ios-card-open>td.ios-c-head{
  margin-right:0;max-width:100%;flex:0 1 auto;
}

/* 抽屉是 position:fixed，可是祖先只要有 transform 就会变成它的容纳块 ——
   而 iosRise 是 fill-mode:both，动画跑完之后 to{transform:none} 还留在
   算出来的样式里，值是 matrix(1,0,0,1,0,0)：identity 也算 transform，
   一样会抓走容纳块。结果 inset:50% 是对着那张 .card 量的，抽屉会歪在
   内容区正中央而不是视窗正中央（第 1123 行那条注解讲的就是这个坑）。
   用 !important 压掉：CSS 层叠里 important author 高于动画宣告，
   而且不动 animation-name，关掉抽屉时整页不会重播一次进场动画。 */
[data-skin="ios"] .panel.active>*:has(tr.ios-card-open){
  transform:none!important;
}

/* ── 遮罩（来源 Modal：bg-black/35 backdrop-blur-[6px]）───────── */
.ios-drawer-backdrop{
  position:fixed;inset:0;z-index:1100;
  background:rgba(0,0,0,.35);
  backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);
  animation:iosDrawerFade .2s var(--ease,ease) both;
}
[data-theme="dark"] .ios-drawer-backdrop{background:rgba(0,0,0,.5)}
html.ios-drawer-lock{overflow:hidden}
@keyframes iosDrawerFade{from{opacity:0}to{opacity:1}}
/* 只管「进场」这一段位移，置中那一段交给 translate（见下） */
@keyframes iosDrawerIn{
  from{opacity:0;transform:translateY(34px) scale(.97)}
  to{opacity:1;transform:none}
}

/* ── 抽屉本体（来源 Modal：w-[min(520px,100vw-32px)] rounded-[28px] p-6）── */
[data-skin="ios"] table.ios-cards>tbody>tr.ios-card-open{
  position:fixed;z-index:1200;
  inset-block-start:50%;inset-inline-start:50%;
  /* 置中用独立的 translate 属性，不是 transform —— 卡片本来就带着
     transition:transform .25s（hover 的 -2px 上浮要用）。写进 transform
     的话，开 / 关的瞬间会从 none 补间到 translate(-50%,-50%)，
     看起来就是一张卡从右下角滑进画面正中央再滑回去。translate 不在
     那条 transition-property 里，所以是瞬间的；而 hover 的 translateY
     仍然叠在它后面（顺序是 translate → rotate → scale → transform）。 */
  translate:-50% -50%;
  inline-size:min(520px,calc(100vw - 32px));
  max-block-size:80vh;overflow:auto;overscroll-behavior:contain;
  gap:10px;
  padding:22px 22px 20px;
  border-radius:28px;
  background:var(--ios-strong-bg);
  backdrop-filter:var(--ios-strong-blur);
  -webkit-backdrop-filter:var(--ios-strong-blur);
  border:1px solid var(--ios-strong-border);
  box-shadow:var(--ios-strong-shadow);
  cursor:default;
  animation:iosDrawerIn .3s var(--ios-spring) backwards;
}
/* 抽屉是不动的：hover 的 -2px 上浮在这里没有意义（而且会跟进场动画打架） */
[data-skin="ios"] table.ios-cards>tbody>tr.ios-card-open:hover{
  transform:none;box-shadow:var(--ios-strong-shadow);
}
[data-skin="ios"] table.ios-cards>tbody>tr.ios-card-open:focus{outline:none}

/* 抬头：h-12 w-12 头像 + 18px 粗标题（来源 264-273） */
[data-skin="ios"] table.ios-cards>tbody>tr.ios-card-open>td.ios-c-head{
  font-size:18px;
  white-space:normal;overflow:visible;text-overflow:clip;max-width:100%;
}
[data-skin="ios"] table.ios-cards>tbody>tr.ios-card-open>td.ios-c-head::before{
  width:48px;height:48px;font-size:19px;border-radius:15px;
}
[data-skin="ios"] table.ios-cards>tbody>tr.ios-card-open>td.ios-c-sub{
  font-size:12px!important;white-space:normal;
}

/* ── 数字磁贴 → 抽屉的 Stat（来源 278-283 + 369-376）───────────
   grid-cols-2 gap-2.5、glass-inset rounded-2xl p-3.5，
   而且**标签在上、值在下** —— 跟卡面那个「值在上」刚好相反。
   两种形态共存，靠 .ios-card-open 分流：卡面要扫得快，抽屉要读得清。
   卡面的标签走 ::after（值在上），抽屉换回 ::before（标签在上）。 */
[data-skin="ios"] table.ios-cards>tbody>tr.ios-card-open>td.ios-c-metric{
  flex:0 1 calc(50% - 5px);
  padding:12px 14px;
  border-radius:var(--ios-r-2xl);
  /* 抽屉里的 Stat 是有内嵌盒的（来源 369-376 的 glass-inset rounded-2xl p-3.5）；
     卡面那四格没有 —— 两者刻意不同，靠 .ios-card-open 分流。 */
  background:var(--ios-inset-bg);
  text-align:left!important;
  font-size:18px;line-height:1.25;
}
[data-skin="ios"] table.ios-cards>tbody>tr.ios-card-open>td.ios-c-metric::after{content:none}
[data-skin="ios"] table.ios-cards>tbody>tr.ios-card-open>td.ios-c-metric::before{
  content:attr(data-label);
  display:block;margin:0 0 3px;
  font-size:12px;font-weight:400;line-height:1.3;letter-spacing:0;
  color:var(--ios-label);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}

/* ── 明细行 → InfoRow（来源 378-385）───────────────────────────
   glass-inset flex justify-between rounded-2xl px-3.5 py-2.5 text-xs */
[data-skin="ios"] table.ios-cards>tbody>tr.ios-card-open>td.ios-c-detail{
  padding:9px 14px;border-radius:var(--ios-r-2xl);
  font-size:12px;min-height:38px;
}
/* 控制项（分组 / 权重 / 归属）跟着 InfoRow 走，但整行占满、
   标签在左、控件在右 —— 来源那个「用户分组」区块就是这个骨架（289-302）。 */
[data-skin="ios"] table.ios-cards>tbody>tr.ios-card-open>td.ios-c-ctl{
  flex:0 0 100%;
  padding:8px 14px;border-radius:var(--ios-r-2xl);
  font-size:12px;min-height:38px;
}
[data-skin="ios"] table.ios-cards>tbody>tr.ios-card-open>td.ios-c-ctl::before,
[data-skin="ios"] table.ios-cards>tbody>tr.ios-card-open>td.ios-c-detail::before{
  font-size:12px;
}

/* ── 动作列（来源 328-341）─────────────────────────────────────
   grid-cols-3 gap-2.5，每颗 rounded-xl py-2.5 text-[13px] font-semibold。
   同一批 <button>、同一个 onclick、同一个端点 —— 只换外观与位置。 */
[data-skin="ios"] table.ios-cards>tbody>tr.ios-card-open>td.ios-c-act{
  order:8;flex:0 0 100%;
  display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px;
  margin-top:8px;padding:0;border-top:none;
  position:static;pointer-events:auto;
}
[data-skin="ios"] table.ios-cards>tbody>tr.ios-card-open>td.ios-c-act>*{display:block}
/* .mini 是 portal 那几张表用的钮，.btn 是 admin 的 —— 两套都要 */
[data-skin="ios"] table.ios-cards>tbody>tr.ios-card-open>td.ios-c-act .btn,
[data-skin="ios"] table.ios-cards>tbody>tr.ios-card-open>td.ios-c-act .mini,
[data-skin="ios"] table.ios-cards>tbody>tr.ios-card-open>td.ios-c-act>.ios-c-sw{
  width:100%;margin:0;
  padding:10px 8px!important;
  border:none!important;border-radius:12px!important;
  font-size:13px!important;font-weight:600!important;line-height:1.4!important;
  letter-spacing:0;
  background:rgba(0,0,0,.07)!important;
  color:var(--text)!important;
  box-shadow:none!important;
  transition:background .18s var(--ease),transform .12s var(--ease);
}
[data-theme="dark"][data-skin="ios"] table.ios-cards>tbody>tr.ios-card-open>td.ios-c-act .btn,
[data-theme="dark"][data-skin="ios"] table.ios-cards>tbody>tr.ios-card-open>td.ios-c-act .mini,
[data-theme="dark"][data-skin="ios"] table.ios-cards>tbody>tr.ios-card-open>td.ios-c-act>.ios-c-sw{
  background:rgba(255,255,255,.1)!important;
}
[data-skin="ios"] table.ios-cards>tbody>tr.ios-card-open>td.ios-c-act .btn:active,
[data-skin="ios"] table.ios-cards>tbody>tr.ios-card-open>td.ios-c-act .mini:active,
[data-skin="ios"] table.ios-cards>tbody>tr.ios-card-open>td.ios-c-act>.ios-c-sw:active{
  transform:scale(.95);
}
[data-skin="ios"] table.ios-cards>tbody>tr.ios-card-open>td.ios-c-act .btn[disabled],
[data-skin="ios"] table.ios-cards>tbody>tr.ios-card-open>td.ios-c-act .mini[disabled]{
  opacity:.4;cursor:not-allowed;
}
/* 删除 = 红（bg-red-500/12 text-red-500） */
[data-skin="ios"] table.ios-cards>tbody>tr.ios-card-open>td.ios-c-act .btn-danger,
[data-skin="ios"] table.ios-cards>tbody>tr.ios-card-open>td.ios-c-act .mini.danger{
  background:rgba(255,59,48,.12)!important;color:#ff3b30!important;
}
/* 「停用 / 启用」在抽屉里不是开关，是来源那颗橘 / 绿的动作钮：
   data-on=1（现在是启用中，按下去会停用）→ 橘；data-on=0 → 绿。 */
[data-skin="ios"] table.ios-cards>tbody>tr.ios-card-open>td.ios-c-act>.ios-c-sw{
  position:static;
  width:100%;height:auto;
  overflow:visible;
  font-size:13px!important;color:#c2410c!important;
  background:rgba(255,159,10,.16)!important;
}
[data-skin="ios"] table.ios-cards>tbody>tr.ios-card-open>td.ios-c-act>.ios-c-sw[data-on="0"]{
  color:#248a3d!important;background:rgba(48,209,88,.18)!important;
}
[data-theme="dark"][data-skin="ios"] table.ios-cards>tbody>tr.ios-card-open>td.ios-c-act>.ios-c-sw{
  color:#ff9f0a!important;
}
[data-theme="dark"][data-skin="ios"] table.ios-cards>tbody>tr.ios-card-open>td.ios-c-act>.ios-c-sw[data-on="0"]{
  color:#30d158!important;
}
[data-skin="ios"] table.ios-cards>tbody>tr.ios-card-open>td.ios-c-act>.ios-c-sw::after{content:none}

/* 勾选框在抽屉里回到抬头左边、而且一直看得见（批次选取在抽屉开着时也要能用，
   抽屉里没有「滑过卡片」这回事） */
[data-skin="ios"] table.ios-cards>tbody>tr.ios-card-open>td.ios-c-check{
  position:static;order:-2;align-self:center;
  width:auto;height:auto;border-radius:0;
  background:none;backdrop-filter:none;-webkit-backdrop-filter:none;
  box-shadow:none;
  opacity:1;pointer-events:auto;
}
[data-skin="ios"] table.ios-cards>tbody>tr.ios-card-open>td.ios-c-head::before{opacity:1}

/* ── 关闭钮（来源 342-345：w-full rounded-2xl py-3 15px 的蓝色渐层）──
   这是抽屉里唯一新增的节点，由 UI.openCardDrawer 挂在 <tr> 底下
   （不是 <td>，所以 row.cells 数不到它，栏位选单与 labelCells 都碰不到）。 */
.ios-drawer-close{display:none}
[data-skin="ios"] table.ios-cards>tbody>tr.ios-card-open>.ios-drawer-close{
  display:block;order:9;flex:0 0 100%;
  margin:4px 0 0;padding:12px;
  border:none;border-radius:var(--ios-r-2xl);cursor:pointer;
  font-family:inherit;font-size:15px;font-weight:600;color:#fff;
  background:var(--ios-grad-primary);
  box-shadow:var(--ios-shadow-blue-25);
  transition:transform .12s var(--ease);
}
[data-skin="ios"] table.ios-cards>tbody>tr.ios-card-open>.ios-drawer-close:active{transform:scale(.98)}

@media(max-width:640px){
  [data-skin="ios"] table.ios-cards>tbody>tr.ios-card-open{
    inline-size:calc(100vw - 20px);padding:18px 16px 16px;border-radius:24px;
  }
  [data-skin="ios"] table.ios-cards>tbody>tr.ios-card-open>td.ios-c-act{
    grid-template-columns:repeat(2,minmax(0,1fr));
  }
}

/* 会动的东西一律尊重系统的「减少动态效果」 */
@media(prefers-reduced-motion:reduce){
  [data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-act>.ios-c-sw,
  [data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-act>.ios-c-sw::after{
    transition:none;
  }
  .ios-drawer-backdrop,
  [data-skin="ios"] table.ios-cards>tbody>tr.ios-card-open{animation:none}
  [data-skin="ios"] table.ios-cards>tbody>tr.ios-card-open>td.ios-c-act .btn,
  [data-skin="ios"] table.ios-cards>tbody>tr.ios-card-open>td.ios-c-act>.ios-c-sw,
  [data-skin="ios"] table.ios-cards>tbody>tr.ios-card-open>.ios-drawer-close{transition:none}
}

/* 手机：一栏到底、栏位改两个一行 */
@media(max-width:768px){
  [data-skin="ios"] table.ios-cards>tbody,
  [data-skin="ios"] table.ios-cards-curated>tbody{grid-template-columns:1fr;gap:12px}
  [data-skin="ios"] table.ios-cards>tbody>tr{border-radius:22px;padding:14px 16px}
  [data-skin="ios"] table.ios-cards>tbody>tr>td{flex-basis:calc(50% - 4px)}
  /* 数字磁贴维持来源那个四格一排（来源的 grid-cols-4 没有断点变体），
     只把左右内距收紧，让 420px 的卡也放得下四个。字级不动 ——
     13px 已经是来源的大小，再降就不是同一张卡了。 */
  [data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-metric{
    flex-basis:calc(25% - 6px);padding:7px 5px 8px;
  }
  [data-skin="ios"] table.ios-cards>tbody>tr>td.ios-c-metric::after{font-size:9.5px}
}

/* ═══ 12. PageHeader ═════════════════════════════════════════════
   来源 primitives.tsx：mb-5 flex items-center gap-3
        + h-11 w-11 rounded-2xl 渐层图示（shadow-lg shadow-blue-500/25）
        + h1 22px bold tracking-tight + p mt-0.5 13px #6e6e73
   节点由 UI.iosPageHeader() 建出来 —— 只有 skin=ios 才会建。
   ════════════════════════════════════════════════════════════════ */
[data-skin="ios"] .ios-ph{
  display:flex;align-items:center;gap:12px;margin-bottom:20px;
}
[data-skin="ios"] .ios-ph-icon{
  flex:0 0 auto;width:44px;height:44px;
  display:flex;align-items:center;justify-content:center;
  border-radius:var(--ios-r-2xl);
  background:linear-gradient(135deg,#0a84ff,#5e5ce6);
  color:#fff;box-shadow:var(--ios-shadow-blue-25);
}
[data-skin="ios"] .ios-ph-icon svg{
  width:21px;height:21px;fill:none;stroke:currentColor;
  stroke-width:2;stroke-linecap:round;stroke-linejoin:round;
}
[data-skin="ios"] .ios-ph-text{min-width:0}
[data-skin="ios"] .ios-ph .page-title,
[data-skin="ios"] .ios-ph .title{margin:0;font-size:22px;font-weight:700;letter-spacing:-.025em}
[data-skin="ios"] .ios-ph-sub{
  margin:2px 0 0;font-size:13px;line-height:1.45;color:var(--ios-label);
}
/* 来源 app-shell.tsx L171：标题底下 11px 的「长日期 · 管理控制台」。 */
[data-skin="ios"] .ios-ph-meta{
  margin:3px 0 0;font-size:11px;line-height:1.45;color:var(--ios-label-dim);
}

/* ═══ 13. Hero 卡 ════════════════════════════════════════════════
   来源 shot-02-dashboard.png：一张横跨整栏的玻璃卡 ——
   左边「标签 + 实时徽章 / 大数字 / 一行注解」，右边一颗渐层行动钮。
   ════════════════════════════════════════════════════════════════ */
[data-skin="ios"] .ios-hero{
  display:flex;align-items:center;justify-content:space-between;
  gap:18px;flex-wrap:wrap;
  margin-bottom:22px;padding:20px 24px;
  border-radius:var(--ios-r-card);
  background:var(--ios-glass-bg);
  backdrop-filter:var(--ios-glass-blur);
  -webkit-backdrop-filter:var(--ios-glass-blur);
  border:1px solid var(--ios-glass-border);
  box-shadow:var(--ios-glass-shadow);
  position:relative;overflow:hidden;
}
/* 右上角光晕，跟 StatTile 同一个配方（-right-8 -top-10 blur-2xl opacity-25） */
[data-skin="ios"] .ios-hero::after{
  content:'';position:absolute;right:-40px;top:-56px;
  width:180px;height:180px;border-radius:9999px;
  opacity:.22;filter:blur(40px);pointer-events:none;
  background:var(--ios-grad-brand);
}
[data-skin="ios"] .ios-hero-body{position:relative;z-index:1;min-width:0}
[data-skin="ios"] .ios-hero-top{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
[data-skin="ios"] .ios-hero-label{font-size:13px;font-weight:500;color:var(--ios-label)}
[data-skin="ios"] .ios-hero-tag{
  display:inline-flex;align-items:center;
  padding:1px 8px;border-radius:999px;
  border:1px solid rgba(255,255,255,.5);
  background:rgba(48,209,88,.15);color:#248a3d;
  font-size:11px;font-weight:600;
}
[data-theme="dark"][data-skin="ios"] .ios-hero-tag{
  border-color:rgba(255,255,255,.12);background:rgba(48,209,88,.18);color:#30d158;
}
[data-skin="ios"] .ios-hero-num{
  margin-top:6px;
  font-size:34px;font-weight:700;line-height:1.15;letter-spacing:-.02em;
  font-variant-numeric:tabular-nums;
}
[data-skin="ios"] .ios-hero-note{
  margin-top:6px;font-size:12px;line-height:1.5;color:var(--ios-label-dim);
}
[data-skin="ios"] .ios-hero-cta{
  position:relative;z-index:1;flex:0 0 auto;
  border:none;cursor:pointer;font-family:inherit;
  padding:12px 24px;border-radius:999px;
  font-size:14px;font-weight:600;color:#fff;letter-spacing:-.01em;
  background:var(--ios-grad-primary);
  box-shadow:var(--ios-shadow-blue-30);
  transition:transform .18s var(--ease),box-shadow .18s var(--ease);
}
[data-skin="ios"] .ios-hero-cta:hover{box-shadow:var(--ios-shadow-blue-25)}
[data-skin="ios"] .ios-hero-cta:active{transform:scale(.95)}
[data-skin="ios"] .ios-hero[data-cta-tone="green"] .ios-hero-cta{
  background:linear-gradient(135deg,#30d158,#00c7be);
  box-shadow:0 10px 15px -3px rgba(48,209,88,.3),0 4px 6px -4px rgba(48,209,88,.3);
}
@media(max-width:640px){
  [data-skin="ios"] .ios-hero{padding:18px;gap:14px}
  [data-skin="ios"] .ios-hero-cta{width:100%}
  [data-skin="ios"] .ios-hero-num{font-size:30px}
}

/* ═══ 14. 手机：浮动玻璃标签列 + 「更多」面板 ═════════════════════
   来源 app-shell.tsx 的 <nav class="fixed inset-x-3 bottom-3 …">
   与 MobileMoreButton 的 grid-cols-4 弹出层（shot-15 / shot-16）。
   <lg 侧栏收起、标签列出场 —— 全部页面都还是走原本那颗导航项的 click()，
   所以路由与网址状态一行都没有第二套实作。
   ════════════════════════════════════════════════════════════════ */
[data-skin="ios"] .ios-tabbar,
[data-skin="ios"] .ios-more-sheet{display:none}

@media(max-width:1023px){
  /* 侧栏 / 抽屉 / 汉堡钮全部让位给标签列 */
  [data-skin="ios"] .admin-nav,
  [data-skin="ios"] nav.nav,
  [data-skin="ios"] .hamburger,
  [data-skin="ios"] .mobile-overlay{display:none!important}
  [data-skin="ios"] .admin-main,
  [data-skin="ios"] nav.nav~.main{padding-bottom:104px}

  [data-skin="ios"] .ios-tabbar{
    position:fixed;left:12px;right:12px;bottom:12px;z-index:40;
    display:flex;align-items:center;justify-content:space-around;
    padding:8px 4px;
    padding-bottom:max(8px,env(safe-area-inset-bottom));
    border-radius:var(--ios-r-card);
    background:rgba(255,255,255,.66);
    backdrop-filter:blur(32px) saturate(1.8);
    -webkit-backdrop-filter:blur(32px) saturate(1.8);
    border:1px solid rgba(255,255,255,.75);
    box-shadow:0 12px 40px rgba(31,38,65,.18);
  }
  [data-skin="ios"] .ios-more-sheet{
    position:fixed;left:12px;right:12px;bottom:96px;z-index:50;
    display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:8px;
    padding:12px;border-radius:var(--ios-r-card);
    background:rgba(255,255,255,.88);
    backdrop-filter:blur(36px) saturate(1.8);
    -webkit-backdrop-filter:blur(36px) saturate(1.8);
    border:1px solid rgba(255,255,255,.8);
    box-shadow:0 18px 60px rgba(31,38,65,.25);
    animation:iosSheetIn .28s var(--ios-spring) both;
  }
  [data-skin="ios"] .ios-more-sheet[hidden]{display:none}
}
[data-theme="dark"][data-skin="ios"] .ios-tabbar{
  background:rgba(34,34,40,.74);border-color:rgba(255,255,255,.14);
  box-shadow:0 12px 40px rgba(0,0,0,.5);
}
[data-theme="dark"][data-skin="ios"] .ios-more-sheet{
  background:rgba(34,34,40,.92);border-color:rgba(255,255,255,.16);
  box-shadow:0 18px 60px rgba(0,0,0,.6);
}
@keyframes iosSheetIn{
  from{opacity:0;transform:translateY(60px) scale(.94)}
  to{opacity:1;transform:none}
}
[data-skin="ios"] .ios-tab{
  flex:1 1 0;min-width:0;min-height:44px;
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;
  border:none;background:none;cursor:pointer;font-family:inherit;
  border-radius:var(--ios-r-2xl);color:#8e8e93;padding:0;
  transition:color .18s var(--ease);
}
[data-theme="dark"][data-skin="ios"] .ios-tab{color:var(--text-m)}
[data-skin="ios"] .ios-tab.active{color:var(--ios-blue)}
[data-skin="ios"] .ios-tab-ico{
  display:flex;align-items:center;justify-content:center;
  padding:4px 14px;border-radius:999px;
  transition:background .18s var(--ease);
}
[data-skin="ios"] .ios-tab.active .ios-tab-ico{background:rgba(10,132,255,.14)}
[data-skin="ios"] .ios-tab-ico svg{
  width:19px;height:19px;fill:none;stroke:currentColor;
  stroke-width:2;stroke-linecap:round;stroke-linejoin:round;
}
[data-skin="ios"] .ios-tab-label{
  font-size:10px;font-weight:500;line-height:1.2;
  max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
[data-skin="ios"] .ios-tab-grid{
  min-height:64px;gap:4px;padding:6px 2px;color:#48484a;
}
[data-theme="dark"][data-skin="ios"] .ios-tab-grid{color:var(--text-s)}
[data-skin="ios"] .ios-tab-grid.active{background:rgba(10,132,255,.12);color:var(--ios-blue)}
[data-skin="ios"] .ios-tab-grid .ios-tab-ico{padding:0;background:none!important}

/* ═══ 15. 登录页（index.html）════════════════════════════════════
   来源 shot-01-login.png / login-view.tsx：
     置中一栏 → 80px 渐层圆角方块（闪电）→ 渐层品牌字 → 副标
     → glass-strong 卡（标签在上的填色输入 + 整宽蓝钮）→ 灰色页脚
   左边那面 hero 收起来 —— 来源是纯置中版面，没有分栏。
   ════════════════════════════════════════════════════════════════ */
[data-skin="ios"] body:has(>main.auth){
  display:flex;align-items:center;justify-content:center;
  grid-template-columns:none;
}
[data-skin="ios"] body>aside.hero{display:none}
[data-skin="ios"] main.auth{
  flex:1;min-height:100vh;background:none;padding:40px 20px;
}
[data-skin="ios"] main.auth::before{display:none}   /* 原本的祖母绿光晕，让位给光斑背景 */
[data-skin="ios"] .auth .container{max-width:400px}

/* 80px rounded-[26px] 渐层方块 + 40px 闪电（shadow-2xl shadow-blue-500/40） */
[data-skin="ios"] .auth .logo{
  width:80px;height:80px;margin:0 auto 16px;
  border-radius:var(--ios-r-card);
  background:linear-gradient(135deg,#0a84ff 0%,#af52de 55%,#ff2d55 120%);
  box-shadow:0 25px 50px -12px rgba(59,130,246,.4);
  font-size:0;                                   /* 盖掉原本的 "L" 字 */
  animation:iosLogoIn .5s var(--ios-spring) both;
}
[data-skin="ios"] .auth .logo::after{
  content:'';display:block;width:40px;height:40px;background:#fff;
  -webkit-mask:var(--ios-bolt) center/contain no-repeat;
          mask:var(--ios-bolt) center/contain no-repeat;
}
[data-skin="ios"]{
  --ios-bolt:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M13 2L4.09 12.35a1 1 0 0 0 .77 1.65H11l-1 8 8.91-10.35a1 1 0 0 0-.77-1.65H13l1-8z'/%3E%3C/svg%3E");
}
@keyframes iosLogoIn{
  from{opacity:0;transform:rotate(-8deg) scale(.85)}
  to{opacity:1;transform:none}
}
[data-skin="ios"] .auth h1{
  font-size:28px;font-weight:700;letter-spacing:-.025em;margin-bottom:4px;
  background:linear-gradient(100deg,#0a84ff 0%,#af52de 48%,#ff2d55 100%);
  -webkit-background-clip:text;background-clip:text;color:transparent;
}
[data-skin="ios"] .auth .sub{
  font-size:14px;color:var(--ios-label);margin-bottom:28px;
}

/* 模式切换 = iOS 分段控制器 */
[data-skin="ios"] .auth .modes{
  gap:0;padding:4px;border-radius:999px;margin-bottom:18px;
  background:var(--ios-inset-bg);border:1px solid var(--ios-inset-border);
}
[data-skin="ios"] .auth .mode{
  border-radius:999px;font-size:13px;font-weight:500;padding:7px 6px;
  color:var(--ios-label);box-shadow:none;background:none;
}
[data-skin="ios"] .auth .mode:hover{color:var(--text)}
[data-skin="ios"] .auth .mode.active{
  background:var(--ios-grad-primary);color:#fff;
  box-shadow:var(--ios-shadow-blue-20);
}

/* 表单本体 = GlassCard strong + sheen（来源的 <GlassCard strong className="sheen">）*/
[data-skin="ios"] .auth .form{
  position:relative;overflow:hidden;
  padding:28px;border-radius:28px;
  background:var(--ios-strong-bg);
  backdrop-filter:var(--ios-strong-blur);
  -webkit-backdrop-filter:var(--ios-strong-blur);
  border:1px solid var(--ios-strong-border);
  box-shadow:var(--ios-strong-shadow);
}
[data-skin="ios"] .auth .form::before{
  content:"";position:absolute;top:0;bottom:0;left:0;width:34%;
  background:linear-gradient(90deg,transparent,rgba(255,255,255,.55),transparent);
  animation:iosSheen 5.5s ease-in-out infinite;pointer-events:none;z-index:0;
}
[data-skin="ios"] .auth .form>*{position:relative;z-index:1}
[data-skin="ios"] .auth .form label{
  font-size:13px;font-weight:600;color:var(--text-s);
  padding-left:4px;margin-bottom:6px;letter-spacing:0;
}
[data-skin="ios"] .auth .form input{
  padding:12px 42px 12px 16px;
  border-radius:var(--ios-r-2xl);
  font-size:15px;
  background:var(--ios-inset-bg);
  border:1px solid var(--ios-inset-border);
}
[data-skin="ios"] .auth .form input:focus{
  border-color:rgba(10,132,255,.55);
  background:rgba(255,255,255,.75);
  box-shadow:0 0 0 4px rgba(10,132,255,.14);
}
[data-theme="dark"][data-skin="ios"] .auth .form input:focus{background:rgba(255,255,255,.1)}
[data-skin="ios"] .auth .form .btn{
  width:100%;margin-top:20px;
  padding:14px;border-radius:var(--ios-r-2xl);
  font-size:16px;font-weight:600;letter-spacing:-.01em;
  background:var(--ios-grad-primary);color:#fff;
  box-shadow:var(--ios-shadow-blue-30);
}
[data-skin="ios"] .auth .form .btn:active{transform:scale(.97)}
[data-skin="ios"] .auth .inline .side-btn{
  border-radius:var(--ios-r-2xl);
  background:var(--ios-inset-bg);border:1px solid var(--ios-inset-border);
}
[data-skin="ios"] .auth .remember{font-size:13px;color:var(--ios-label)}
[data-skin="ios"] .auth .remember input{accent-color:var(--ios-blue);border-radius:5px}

/* 第三方登录 / 接入方式 / 页脚 */
[data-skin="ios"] .auth .oauth-btn{
  border-radius:var(--ios-r-2xl);
  background:var(--ios-glass-bg);
  backdrop-filter:var(--ios-glass-blur);
  -webkit-backdrop-filter:var(--ios-glass-blur);
  border:1px solid var(--ios-glass-border);
  box-shadow:var(--ios-glass-shadow);
  font-size:14px;font-weight:600;
  transition:transform .18s var(--ease),box-shadow .18s var(--ease);
}
[data-skin="ios"] .auth .oauth-btn:hover{transform:translateY(-2px);box-shadow:var(--ios-strong-shadow)}
[data-skin="ios"] .auth .int-title{
  font-size:11px;font-weight:500;letter-spacing:.15em;color:var(--ios-label-dim);
}
[data-skin="ios"] .auth .int-title::before,
[data-skin="ios"] .auth .int-title::after{background:var(--ios-inset-border)}
[data-skin="ios"] .auth .int-chip{
  border-radius:999px;font-size:11px;font-weight:600;
  background:var(--ios-inset-bg);border:1px solid var(--ios-inset-border);color:var(--ios-label);
}
[data-skin="ios"] .auth .footer,
[data-skin="ios"] .auth .terms{font-size:11.5px;line-height:1.7;color:var(--ios-label-dim)}
[data-skin="ios"] .auth .footer a,
[data-skin="ios"] .auth .link{color:var(--ios-blue)}
[data-skin="ios"] .theme-btn{
  width:40px;height:40px;border-radius:999px;
  background:var(--ios-glass-bg);
  backdrop-filter:var(--ios-glass-blur);
  -webkit-backdrop-filter:var(--ios-glass-blur);
  border:1px solid var(--ios-glass-border);
  box-shadow:var(--ios-glass-shadow);
}
@media(max-width:900px){
  [data-skin="ios"] main.auth{padding:32px 20px}
}

/* ═══ 16. prefers-reduced-motion ═════════════════════════════════
   全域那条 *{animation-duration:.01ms!important} 已经会把动画压掉，
   这里再明确关一次：无限循环的光斑动画就算 0.01ms 也还在跑。 */
@media(prefers-reduced-motion:reduce){
  [data-skin="ios"]::before,
  [data-skin="ios"]::after,
  [data-skin="ios"] body::before,
  [data-skin="ios"] .sheen::after{animation:none!important;transform:none!important}
  [data-skin="ios"] .panel.active>*{animation:none!important;opacity:1;transform:none}
  [data-skin="ios"] .auth .form::before,
  [data-skin="ios"] .auth .logo,
  [data-skin="ios"] .ios-more-sheet{animation:none!important;transform:none!important}
  [data-skin="ios"] .stat-card:hover,
  [data-skin="ios"] .stat-card-rich:hover,
  [data-skin="ios"] .sc:hover,
  [data-skin="ios"] .metric-card:hover,
  [data-skin="ios"] table.ios-cards>tbody>tr:hover,
  [data-skin="ios"] .ios-hero-cta:active,
  [data-skin="ios"] .auth .oauth-btn:hover,
  [data-skin="ios"] .btn:active{transform:none}
}

/* ═══ 17. 骨架屏（Sk / DashboardSkeleton / ListSkeleton / PageSkeleton）══
   来源 globals.css 的 sk-shimmer + feedback.tsx 的三种骨架。
   参考实作的 Sk 是「灰底 + 一道白光横着扫过去」：
     bg-black/[0.055] 的圆角块，里面一层 absolute inset-0 的渐层，
     动画只动 transform: translateX(-100%) → 100%。
   动 transform 而不动 background-position 是有理由的 —— 后者每帧都要
   重画渐层，一屏摆 12 块骨架时肉眼看得到掉帧。
   ════════════════════════════════════════════════════════════════ */
@keyframes iosSkShimmer{
  0%{transform:translateX(-100%)}
  100%{transform:translateX(100%)}
}
[data-skin="ios"] .ios-sk{
  position:relative;overflow:hidden;
  border-radius:var(--ios-r-2xl);
  background:rgba(0,0,0,.055);
}
[data-theme="dark"][data-skin="ios"] .ios-sk{background:rgba(255,255,255,.06)}
[data-skin="ios"] .ios-sk-shimmer{
  position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(90deg,transparent,rgba(255,255,255,.65),transparent);
  animation:iosSkShimmer 1.6s ease-in-out infinite;
}
[data-theme="dark"][data-skin="ios"] .ios-sk-shimmer{
  background:linear-gradient(90deg,transparent,rgba(255,255,255,.14),transparent);
}
/* 原本那份横线骨架在 ios 下也换成同一道白光，两种骨架不该长得不一样。
   background-image 要一起清掉 —— 只关 animation 的话，那层
   linear-gradient 会留在原地变成一条渐层横线。 */
[data-skin="ios"] .skeleton-line{
  position:relative;overflow:hidden;
  background:rgba(0,0,0,.055);background-image:none;border-radius:999px;
  animation:none;
}
[data-theme="dark"][data-skin="ios"] .skeleton-line{background:rgba(255,255,255,.06)}
[data-skin="ios"] .skeleton-line::after{
  content:'';position:absolute;inset:0;
  background:linear-gradient(90deg,transparent,rgba(255,255,255,.65),transparent);
  animation:iosSkShimmer 1.6s ease-in-out infinite;
}
[data-theme="dark"][data-skin="ios"] .skeleton-line::after{
  background:linear-gradient(90deg,transparent,rgba(255,255,255,.14),transparent);
}

[data-skin="ios"] .ios-sk-stack{display:flex;flex-direction:column;gap:12px}
[data-skin="ios"] .ios-sk-row{height:64px}
[data-skin="ios"] .ios-sk-head{display:flex;align-items:center;gap:12px;margin-bottom:20px}
[data-skin="ios"] .ios-sk-headtext{display:flex;flex-direction:column;gap:8px}
[data-skin="ios"] .ios-sk-avatar{width:44px;height:44px;flex:0 0 auto}
[data-skin="ios"] .ios-sk-title{width:160px;height:20px;border-radius:999px}
[data-skin="ios"] .ios-sk-sub{width:256px;height:12px;border-radius:999px}
[data-skin="ios"] .ios-sk-hero{height:148px;border-radius:var(--ios-r-card);margin-bottom:20px}
[data-skin="ios"] .ios-sk-tiles{
  display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:14px;margin-bottom:20px;
}
@media(min-width:1024px){
  [data-skin="ios"] .ios-sk-tiles{grid-template-columns:repeat(4,minmax(0,1fr))}
}
[data-skin="ios"] .ios-sk-tile{height:122px;border-radius:var(--ios-r-card)}
[data-skin="ios"] .ios-sk-big{height:300px;border-radius:var(--ios-r-card)}
[data-skin="ios"] .ios-sk-split{display:grid;gap:14px}
@media(min-width:1024px){
  [data-skin="ios"] .ios-sk-split{grid-template-columns:2fr 1fr}
}

/* ═══ 18. ⌘K 命令面板 ════════════════════════════════════════════
   来源 command-palette.tsx / round8-cmdk.png：
     遮罩 rgba(20,20,29,.32) + blur(6px)，面板置中偏上（pt-[12vh]）、
     max-w-560、rounded-[24px]、blur(40px) saturate(1.8)。
     游标那一列是实心蓝 #007aff + 白字。
   ════════════════════════════════════════════════════════════════ */
@keyframes iosCmdkIn{
  from{opacity:0;transform:translateY(-18px) scale(.97)}
  to{opacity:1;transform:none}
}
@keyframes iosFadeIn{from{opacity:0}to{opacity:1}}
[data-skin="ios"] .ios-cmdk{
  position:fixed;inset:0;z-index:90;
  display:flex;align-items:flex-start;justify-content:center;
  padding:12vh 16px 16px;
  background:rgba(20,20,29,.32);
  backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);
  animation:iosFadeIn .18s var(--ease) both;
}
[data-skin="ios"] .ios-cmdk[hidden]{display:none}
[data-skin="ios"] .ios-cmdk-panel{
  width:100%;max-width:560px;overflow:hidden;
  border-radius:24px;
  background:rgba(255,255,255,.88);
  backdrop-filter:blur(40px) saturate(1.8);
  -webkit-backdrop-filter:blur(40px) saturate(1.8);
  border:1px solid rgba(255,255,255,.85);
  box-shadow:0 24px 80px rgba(31,38,65,.32);
  animation:iosCmdkIn .28s var(--ios-spring) both;
}
[data-theme="dark"][data-skin="ios"] .ios-cmdk-panel{
  background:rgba(38,38,44,.9);border-color:rgba(255,255,255,.16);
  box-shadow:0 24px 80px rgba(0,0,0,.6);
}
[data-skin="ios"] .ios-cmdk-search{
  display:flex;align-items:center;gap:10px;
  padding:14px 20px;border-bottom:1px solid var(--ios-hairline);
}
[data-skin="ios"] .ios-cmdk-ico{display:flex;color:#8e8e93}
[data-skin="ios"] .ios-cmdk-input{
  flex:1;min-width:0;border:none;outline:none;background:transparent;
  font-family:inherit;font-size:15px;font-weight:500;color:var(--text);
  letter-spacing:-.01em;padding:0;
}
[data-skin="ios"] .ios-cmdk-input::placeholder{color:var(--ios-label-dim)}
/* 全域那条 :focus-visible{box-shadow:var(--ring)} 会在这颗无边框输入框外面
   画一圈蓝框，来源实作没有。这里关掉是安全的：面板一开就把焦点送进来，
   它是唯一的输入点，游标本身就是指示；Tab 出去落在结果列上时，
   那些 <button> 仍然吃得到全域的焦点环。 */
[data-skin="ios"] .ios-cmdk-input:focus,
[data-skin="ios"] .ios-cmdk-input:focus-visible{box-shadow:none;outline:none}
[data-skin="ios"] .ios-cmdk kbd{
  font-family:inherit;font-size:10px;font-weight:700;line-height:1.6;
  padding:1px 6px;border-radius:6px;
  background:rgba(0,0,0,.06);color:#8e8e93;
}
[data-theme="dark"][data-skin="ios"] .ios-cmdk kbd{background:rgba(255,255,255,.1);color:var(--text-m)}
[data-skin="ios"] .ios-cmdk-list{
  max-height:46vh;overflow-y:auto;overscroll-behavior:contain;padding:10px;
}
[data-skin="ios"] .ios-cmdk-none{padding:40px 16px;text-align:center;font-size:14px;color:#8e8e93}
[data-skin="ios"] .ios-cmdk-sec{
  padding:8px 12px 4px;
  font-size:10.5px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;
  color:var(--ios-label-dim);
}
[data-skin="ios"] .ios-cmdk-item{
  display:flex;align-items:center;gap:12px;width:100%;
  padding:10px 12px;border:none;border-radius:var(--ios-r-2xl);
  background:transparent;color:var(--text-s);
  font-family:inherit;text-align:left;cursor:pointer;
  transition:background .14s var(--ease),color .14s var(--ease);
}
[data-skin="ios"] .ios-cmdk-item-ico{
  flex:0 0 auto;display:flex;align-items:center;justify-content:center;
  width:20px;height:20px;color:#8e8e93;
}
[data-skin="ios"] .ios-cmdk-item-ico svg{
  width:19px;height:19px;fill:none;stroke:currentColor;
  stroke-width:2;stroke-linecap:round;stroke-linejoin:round;
}
[data-skin="ios"] .ios-cmdk-item-tx{flex:1;min-width:0}
[data-skin="ios"] .ios-cmdk-item-label{
  display:block;font-size:13.5px;font-weight:600;letter-spacing:-.01em;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
[data-skin="ios"] .ios-cmdk-item-hint{
  display:block;margin-top:1px;font-size:11px;color:var(--ios-label-dim);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
[data-skin="ios"] .ios-cmdk-cur{
  flex:0 0 auto;padding:1px 8px;border-radius:999px;
  background:rgba(0,122,255,.12);color:#007aff;
  font-size:10px;font-weight:700;
}
[data-skin="ios"] .ios-cmdk-go{display:none;flex:0 0 auto;font-size:11px;font-weight:700}
[data-skin="ios"] .ios-cmdk-item.is-cursor{
  background:#007aff;color:#fff;
  box-shadow:var(--ios-shadow-blue-20);
}
[data-skin="ios"] .ios-cmdk-item.is-cursor .ios-cmdk-item-ico{color:#fff}
[data-skin="ios"] .ios-cmdk-item.is-cursor .ios-cmdk-item-hint{color:rgba(255,255,255,.75)}
[data-skin="ios"] .ios-cmdk-item.is-cursor .ios-cmdk-cur{display:none}
[data-skin="ios"] .ios-cmdk-item.is-cursor .ios-cmdk-go{display:block;color:rgba(255,255,255,.85)}
[data-skin="ios"] .ios-cmdk-foot{
  display:flex;align-items:center;gap:16px;
  padding:10px 20px;border-top:1px solid var(--ios-hairline);
  font-size:10.5px;color:#8e8e93;
}
[data-skin="ios"] .ios-cmdk-foot>span{display:inline-flex;align-items:center;gap:4px}
[data-skin="ios"] .ios-cmdk-foot-end{margin-left:auto}
@media(max-width:640px){
  [data-skin="ios"] .ios-cmdk{padding-top:8vh}
  [data-skin="ios"] .ios-cmdk-foot{display:none}
}

/* ═══ 19. 图表原语：健康环 / 迷你折线 / 比例条 ════════════════════
   来源 charts.tsx 的 CacheGauge / Sparkline / RatioBar。
   ════════════════════════════════════════════════════════════════ */
[data-skin="ios"] .ios-ring{
  position:relative;display:inline-flex;align-items:center;justify-content:center;
}
[data-skin="ios"] .ios-ring svg{display:block;overflow:visible}
[data-skin="ios"] .ios-ring-track{stroke:rgba(60,60,67,.09)}
[data-theme="dark"][data-skin="ios"] .ios-ring-track{stroke:rgba(255,255,255,.09)}
/* 入场：弧线从 0 长到目标值。
   做法是 dasharray 固定成整圈周长、只动 dashoffset —— JS 先把 offset
   设成整圈（等于什么都没画），下一帧改成目标值，剩下的交给这条
   transition。参考实作用的是 opacity 淡入，改成描边生长之后
   读数与弧线才会一起动，而不是「整圈忽然浮出来」。 */
[data-skin="ios"] .ios-ring-arc{
  transition:stroke-dashoffset .9s var(--ios-spring);
}
[data-skin="ios"] .ios-ring[data-tone="good"] .ios-ring-arc{filter:drop-shadow(0 2px 5px rgba(48,209,88,.45))}
[data-skin="ios"] .ios-ring[data-tone="warn"] .ios-ring-arc{filter:drop-shadow(0 2px 5px rgba(255,159,10,.45))}
[data-skin="ios"] .ios-ring[data-tone="bad"] .ios-ring-arc{filter:drop-shadow(0 2px 5px rgba(255,59,48,.4))}
[data-skin="ios"] .ios-ring-center{
  position:absolute;inset:0;
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  pointer-events:none;
}
/* 字级由 JS 按环的直径算（size × .2）写在 style 上：同一支元件要能
   同时服务仪表板的 140px 大环与表格里的 40px 小环，写死一个 px 值
   两边一定有一边爆版，clamp() + cqw 又得先宣告 container-type。 */
[data-skin="ios"] .ios-ring-num{
  font-weight:700;line-height:1;
  letter-spacing:-.02em;font-variant-numeric:tabular-nums;color:var(--text);
}
[data-skin="ios"] .ios-ring-unit{font-size:.55em;font-weight:600}
[data-skin="ios"] .ios-ring-label{
  margin-top:6px;font-size:11px;font-weight:500;color:var(--ios-label);
}

[data-skin="ios"] .ios-spark{display:block;flex:0 0 auto;overflow:visible}

[data-skin="ios"] .ios-ratio{
  display:flex;width:100%;height:12px;overflow:hidden;
  border-radius:999px;background:rgba(0,0,0,.06);
}
[data-theme="dark"][data-skin="ios"] .ios-ratio{background:rgba(255,255,255,.08)}
[data-skin="ios"] .ios-ratio>span{
  display:block;height:100%;
  transition:width .7s var(--ios-spring);
}
[data-skin="ios"] .ios-ratio>span:first-child{border-radius:999px 0 0 999px}
[data-skin="ios"] .ios-ratio>span:last-child{border-radius:0 999px 999px 0}
[data-skin="ios"] .ios-ratio>span:only-child{border-radius:999px}

/* ═══ 20. LiveDot / TimeAgo / RefreshButton / 空状态动作钮 ════════ */
@keyframes iosPing{
  75%,100%{transform:scale(2);opacity:0}
}
[data-skin="ios"] .ios-live{display:inline-flex;align-items:center;gap:6px}
[data-skin="ios"] .ios-live-dot{
  position:relative;flex:0 0 auto;width:8px;height:8px;border-radius:999px;
  background:#30d158;
}
[data-skin="ios"] .ios-live-dot::after{
  content:'';position:absolute;inset:0;border-radius:999px;
  background:#30d158;opacity:.6;
  animation:iosPing 1.2s cubic-bezier(0,0,.2,1) infinite;
}
[data-skin="ios"] .ios-live-tx{font-size:11px;font-weight:600;letter-spacing:0}
/* Hero 徽章里的实时点已经在绿底上了，外圈再涂绿会糊成一团 */
[data-skin="ios"] .ios-hero-tag .ios-live-tx{color:inherit}

@keyframes iosSpin{to{transform:rotate(360deg)}}
[data-skin="ios"] .tb-refresh.is-spinning svg,
[data-skin="ios"] .ios-refresh.is-spinning svg{
  animation:iosSpin .9s linear infinite;transform-origin:50% 50%;
}
[data-skin="ios"] .ios-refresh{
  display:inline-flex;align-items:center;justify-content:center;
  width:36px;height:36px;flex:0 0 auto;
  border-radius:999px;border:1px solid var(--ios-glass-border);
  background:var(--ios-glass-bg);
  backdrop-filter:var(--ios-glass-blur);-webkit-backdrop-filter:var(--ios-glass-blur);
  box-shadow:var(--ios-glass-shadow);
  color:var(--text-s);cursor:pointer;
  transition:transform .18s var(--ease);
}
[data-skin="ios"] .ios-refresh:active{transform:scale(.9)}
[data-skin="ios"] .ios-refresh:disabled{cursor:default;opacity:.75}

[data-skin="ios"] .empty-act{
  margin-top:12px;background:var(--ios-grad-primary);color:#fff;
  box-shadow:var(--ios-shadow-blue-25);
}

/* 页头右侧的动作槽（铃铛 / 刷新钮）—— UI.iosHeaderSlot() 懒建 */
[data-skin="ios"] .ios-ph-act{
  margin-left:auto;display:flex;align-items:center;gap:8px;flex:0 0 auto;
}

/* ═══ 20b. 常驻「新增」表单 → 行动钮 + 置中玻璃弹层 ═══════════════
   来源 shot-13-channels.png / admin-channels.tsx L212 + L371：
   顶上「标题 + ＋ 接入新渠道」，表单收在 fixed inset-0 的玻璃弹层里，
   底下第一屏就直接是卡片格。

   这一段整段只在 ios 皮肤下成立；节点由 UI.iosFormPanels() 建 / 拆，
   default 皮肤下表单原样留在页面上，一个新节点都不会出现。
   ════════════════════════════════════════════════════════════════ */

/* 收起来的表单：留在原位，只是不画。
   不用 detach —— 背景刷新往它的 <select> 里塞选项、$('rtBulk') 之类的
   查询都还要命中同一个节点。 */
[data-skin="ios"] .ios-form-tucked{display:none!important}
/* 搬进弹层之后它是弹层的内容，不能再吃 .card / .section 的外框与间距 */
[data-skin="ios"] .ios-form-inmodal{
  margin:0!important;padding:0!important;
  background:none!important;border:none!important;box-shadow:none!important;
  backdrop-filter:none!important;-webkit-backdrop-filter:none!important;
  animation:none!important;opacity:1;transform:none;
}
[data-skin="ios"] .ios-form-inmodal>.section-title{display:none}
/* 表单里那颗主钮在弹层里变成底部整条的 CTA（来源 admin-channels.tsx L402
   与 shot-11-adjust.png：弹层最底下一条整宽的渐层钮）。
   刻意只挑「本来就排在最后」的那一颗：风控规则的送出钮后面还有欄位组与
   进阶 JSON，把它拉成整条会变成一条横在中间的分隔线，比留着原样更难读。
   那几份维持原本的行内钮 —— 版面照搬，不重新设计。 */
[data-skin="ios"] .ios-form-inmodal>.form-grid:last-child>.btn-primary,
[data-skin="ios"] .ios-form-inmodal>.btn-primary{
  width:100%;flex:1 0 100%;margin-top:6px;
  padding:12px 20px;font-size:15px;border-radius:var(--ios-r-2xl);
}

/* 区块标题那一行右侧的钮位 */
[data-skin="ios"] .ios-form-bar{margin-left:auto;display:inline-flex;gap:8px}
/* 页头还没建好时的后备钮位（default → ios 的执行期切换） */
[data-skin="ios"] .ios-form-bar-page{
  display:flex;justify-content:flex-end;gap:8px;margin:-8px 0 16px;
}
[data-skin="ios"] .ios-form-cta{
  padding:9px 18px;font-size:14px;white-space:nowrap;
}
[data-skin="ios"] .ios-form-hdr-btn{
  padding:9px 16px;font-size:13.5px;white-space:nowrap;
}

/* 弹层本体：来源那个 Modal 是 w-[min(520px,100vw-32px)] rounded-[28px] p-6，
   玻璃底 + 遮罩模糊都吃现成的 [data-skin="ios"] dialog 规则。 */
[data-skin="ios"] .ios-form-modal{
  border-radius:var(--ios-r-card);
  max-height:min(86vh,860px);display:flex;flex-direction:column;
}
[data-skin="ios"] .ios-form-modal>.dlg-body{overflow:auto;min-height:0}
/* 弹层里的输入框比页面上宽松一点 —— 参考实作的 .fi 是 rounded-14 padding-10/14 */
[data-skin="ios"] .ios-form-modal .field{min-width:0}
[data-skin="ios"] .ios-form-modal .form-grid{gap:12px}

/* 手机：参考实作在 sm 以下是从底部滑上来的 sheet（items-end + mb-4） */
@media(max-width:560px){
  [data-skin="ios"] .ios-form-modal{
    margin:auto auto 8px;width:calc(100vw - 16px)!important;max-width:none;
    max-height:88vh;
  }
  [data-skin="ios"] .ios-form-bar-page{margin-top:0}
}
@media(prefers-reduced-motion:reduce){
  [data-skin="ios"] .ios-form-modal{animation:none!important}
}

/* ═══ 21. SlideBanner / OfflineBanner / 公告横幅 ══════════════════
   来源 feedback.tsx 的 SlideBanner + OfflineBanner，
   以及 announcement-banner.tsx 的分级配色。
   ════════════════════════════════════════════════════════════════ */
@keyframes iosSlideIn{
  from{opacity:0;transform:translateY(-16px) scale(.98)}
  to{opacity:1;transform:none}
}
@keyframes iosSlideOut{
  from{opacity:1;transform:none}
  to{opacity:0;transform:translateY(-12px) scale(.98)}
}
[data-skin="ios"] .ios-slide[hidden]{display:none}
[data-skin="ios"] .ios-slide.is-in{animation:iosSlideIn .3s var(--ios-spring) both}
[data-skin="ios"] .ios-slide.is-out{animation:iosSlideOut .24s var(--ease) both}

[data-skin="ios"] .ios-offline{
  position:fixed;left:0;right:0;top:0;z-index:95;
  display:flex;justify-content:center;padding:12px 16px;
  pointer-events:none;
}
[data-skin="ios"] .ios-offline-pill{
  display:inline-flex;align-items:center;gap:8px;
  padding:8px 16px;border-radius:999px;
  background:linear-gradient(135deg,#ff9f0a,#ff2d55);
  color:#fff;font-size:12.5px;font-weight:600;letter-spacing:-.01em;
  box-shadow:0 12px 34px rgba(255,45,85,.35);
}

/* 公告：等级 → 边框 / 底色 / 图示（announcement-banner.tsx 的 LEVEL_STYLE）*/
[data-skin="ios"] .ios-ann{
  border-radius:20px;padding:16px;gap:12px;align-items:flex-start;
  animation:iosSlideIn .3s var(--ios-spring) both;
}
[data-skin="ios"] .ios-ann[data-level="info"]{border-color:rgba(10,132,255,.35);background:rgba(10,132,255,.08)}
[data-skin="ios"] .ios-ann[data-level="warn"]{border-color:rgba(255,159,10,.4);background:rgba(255,159,10,.09)}
[data-skin="ios"] .ios-ann[data-level="critical"]{border-color:rgba(255,59,48,.4);background:rgba(255,59,48,.09)}
[data-skin="ios"] .ios-ann-icon{flex:0 0 auto;margin-top:1px;font-size:18px;line-height:1.2}
[data-skin="ios"] .ios-ann-tag{
  padding:1px 7px;border-radius:999px;
  background:rgba(255,255,255,.6);color:#8e8e93;
  font-size:9.5px;font-weight:600;letter-spacing:0;
}
[data-theme="dark"][data-skin="ios"] .ios-ann-tag{background:rgba(255,255,255,.12);color:var(--text-m)}
[data-skin="ios"] .ios-ann-name{font-size:13.5px;font-weight:700;color:var(--text)}
/* 置顶（announcements.pinned）。一则被置顶的 info 会排在 critical 之上 ——
   没有这个记号的话，那个顺序只会看起来像排序坏了。 */
[data-skin="ios"] .ios-ann-pin{
  padding:1px 7px;border-radius:999px;
  background:linear-gradient(135deg,#0a84ff,#5e5ce6);color:#fff;
  font-size:9.5px;font-weight:700;letter-spacing:0;
}
[data-skin="ios"] .ios-ann.is-pinned{box-shadow:inset 3px 0 0 #0a84ff,var(--ios-glass-shadow)}
[data-skin="ios"] .ios-ann .ann-text{font-size:12px;line-height:1.6}
[data-skin="ios"] .ios-ann .ann-time{font-size:11px;color:var(--ios-label-dim)}
[data-skin="ios"] .ios-ann .ann-x{
  display:flex;align-items:center;justify-content:center;
  width:24px;height:24px;border-radius:999px;color:var(--ios-label-dim);
}
[data-skin="ios"] .ios-ann .ann-x:hover{background:rgba(0,0,0,.05);color:var(--text-s)}

/* ═══ 22. 铃铛 ═══════════════════════════════════════════════════
   来源 bell-button.tsx。红点只在真的数到未读数时才出现 ——
   数不到就没有红点，不画一个编出来的数字。
   ════════════════════════════════════════════════════════════════ */
[data-skin="ios"] .ios-bell{
  position:relative;flex:0 0 auto;
  display:inline-flex;align-items:center;justify-content:center;
  width:36px;height:36px;border-radius:999px;border:none;
  background:transparent;color:var(--text-s);cursor:pointer;
  transition:background .18s var(--ease),transform .18s var(--ease);
}
[data-skin="ios"] .ios-bell:hover{background:rgba(0,0,0,.05)}
[data-theme="dark"][data-skin="ios"] .ios-bell:hover{background:rgba(255,255,255,.08)}
[data-skin="ios"] .ios-bell:active{transform:scale(.9)}
[data-skin="ios"] .ios-bell-badge{
  position:absolute;top:1px;right:1px;
  min-width:17px;height:17px;padding:0 4px;border-radius:999px;
  display:flex;align-items:center;justify-content:center;
  background:linear-gradient(135deg,#ff2d55,#ff9f0a);
  color:#fff;font-size:10px;font-weight:700;line-height:1;
  font-variant-numeric:tabular-nums;
  box-shadow:0 4px 6px -1px rgba(255,45,85,.35);
}
[data-skin="ios"] .ios-bell-badge[hidden]{display:none}

/* ═══ 23. 返回顶部 ═══════════════════════════════════════════════
   来源 feedback.tsx 的 BackToTop：捲过 420px 才出现。
   位置避开手机版的浮动标签列（bottom-24 → lg:bottom-7）。
   ════════════════════════════════════════════════════════════════ */
@keyframes iosPop{
  from{opacity:0;transform:scale(.7) translateY(8px)}
  to{opacity:1;transform:none}
}
[data-skin="ios"] .ios-backtop{
  position:fixed;right:16px;bottom:96px;z-index:40;
  display:flex;align-items:center;justify-content:center;
  width:44px;height:44px;border-radius:999px;
  border:1px solid var(--ios-glass-border);
  background:var(--ios-glass-bg);
  backdrop-filter:var(--ios-glass-blur);-webkit-backdrop-filter:var(--ios-glass-blur);
  box-shadow:var(--ios-glass-shadow);
  color:var(--text-s);cursor:pointer;
  animation:iosPop .28s var(--ios-spring) both;
  transition:color .18s var(--ease),transform .18s var(--ease);
}
[data-skin="ios"] .ios-backtop[hidden]{display:none}
[data-skin="ios"] .ios-backtop:hover{color:var(--ios-blue)}
[data-skin="ios"] .ios-backtop:active{transform:scale(.88)}
@media(min-width:1024px){
  [data-skin="ios"] .ios-backtop{right:28px;bottom:28px}
}

/* ═══ 24. 接线处：这些元件真正落在页面上的那几个位置 ═════════════
   （类名来自 admin.html / portal.html，样式只在 ios 下生效）
   ════════════════════════════════════════════════════════════════ */

/* 实时监控的健康环：原本那颗手写 svg 与它下面的字幕让位给 CacheGauge，
   圆环自己就带「健康 / 一般 / 风险」的标签，两个一起出现是重复的。
   节点留在原地（只是不显示）—— 切回 default 时还要用它。 */
[data-skin="ios"] .health-ring-wrap>#healthRing,
[data-skin="ios"] .health-ring-wrap>.health-label{display:none}
[data-skin="ios"] .ios-ring-host{display:inline-flex}

/* 账号池：近 7 日成功率 = 小圆环 + 成功/失败比例条 + 最近延迟折线 */
[data-skin="ios"] .acct-health{display:flex;align-items:center;gap:10px}
[data-skin="ios"] .acct-health-ring{flex:0 0 auto}
[data-skin="ios"] .acct-health-bars{
  display:flex;flex-direction:column;gap:5px;min-width:96px;flex:1;
}
[data-skin="ios"] .acct-health-bars .ios-ratio{height:6px}

/* 管理台工具列的实时指示点 */
[data-skin="ios"] .tb-live{display:inline-flex;align-items:center;margin-right:2px}
[data-skin="ios"] .tb-live .ios-live-tx{color:#248a3d}
[data-theme="dark"][data-skin="ios"] .tb-live .ios-live-tx{color:#30d158}

/* 公告中心里已读的那几则：留着但收敛掉配色 */
[data-skin="ios"] .ann.ann-read{
  border-color:var(--ios-hairline);background:var(--ios-inset-bg);
  border-radius:20px;padding:14px 16px;
}
[data-skin="ios"] .ann.ann-read .ann-title{font-size:13px;font-weight:600;color:var(--text-s)}

/* ═══ 25. 上面这一整批的 reduced-motion ══════════════════════════
   跟第 16 段同一个理由：无限循环的动画就算被压成 0.01ms 也还在跑，
   得明确关掉。JS 那边（countUp / slideBanner / 平滑捲动）另外有
   UI._reduceMotion() 的短路，两边一起才算数。
   ════════════════════════════════════════════════════════════════ */
@media(prefers-reduced-motion:reduce){
  [data-skin="ios"] .ios-sk-shimmer,
  [data-skin="ios"] .skeleton-line::after,
  [data-skin="ios"] .ios-live-dot::after,
  [data-skin="ios"] .tb-refresh.is-spinning svg,
  [data-skin="ios"] .ios-refresh.is-spinning svg{animation:none!important}
  [data-skin="ios"] .ios-cmdk,
  [data-skin="ios"] .ios-cmdk-panel,
  [data-skin="ios"] .ios-slide.is-in,
  [data-skin="ios"] .ios-slide.is-out,
  [data-skin="ios"] .ios-ann,
  [data-skin="ios"] .ios-ring-arc,
  [data-skin="ios"] .ios-backtop{animation:none!important;transform:none!important;opacity:1}
  [data-skin="ios"] .ios-ring-arc,
  [data-skin="ios"] .ios-ratio>span{transition:none!important}
  [data-skin="ios"] .ios-bell:active,
  [data-skin="ios"] .ios-backtop:active,
  [data-skin="ios"] .ios-refresh:active{transform:none}
}

/* ═══ 26. 全域错误边界的卡片（UI.errorBoundary）════════════════════
   来源 src/app/error.tsx：橙红渐层盾牌图示 + 玻璃卡 + 重试 / 刷新。

   这一段**两种皮肤都要看得见**，所以基底不挂在 [data-skin="ios"] 底下 ——
   跟 .fbar 同一个判断：错误提示不是装饰品，default 皮肤下也得画得出来。
   之所以不影响 skin-regression：这些选择器只命中 UI._errbShow() 建出来的
   节点，而那个函式只有真的出错才会跑；没出错时页面上一个 .ui-errb 都没有。

   位置选右上角而不是右下：右下角已经住了 .toast（z-index 999）、
   .ios-backtop 与手机版的玻璃标签列。错误卡是「留在那里等你处理」的，
   跟会自己消失的 toast 挤同一格只会互相盖住。
   ════════════════════════════════════════════════════════════════ */
.ui-errb{
  position:fixed;top:16px;right:16px;z-index:1000;
  display:flex;flex-direction:column;gap:10px;
  width:min(400px,calc(100vw - 32px));
  pointer-events:none;                 /* 空的容器不吃点击，底下的页面照样能用 */
}
.ui-errb-card{
  pointer-events:auto;
  background:var(--bg);color:var(--text);
  border:1px solid var(--danger);border-radius:14px;
  box-shadow:var(--shadow-lg);
  padding:13px 14px 11px;
  animation:uiErrbIn .28s var(--ease-spring) both;
}
@keyframes uiErrbIn{
  from{opacity:0;transform:translateY(-10px) scale(.97)}
  to{opacity:1;transform:none}
}
.ui-errb-top{display:flex;align-items:flex-start;gap:10px}
.ui-errb-ico{
  flex:0 0 auto;width:30px;height:30px;border-radius:9px;
  display:flex;align-items:center;justify-content:center;
  background:var(--badge-danger-bg);color:var(--badge-danger-fg);
}
.ui-errb-tx{flex:1 1 auto;min-width:0}
.ui-errb-title{
  font-size:13.5px;font-weight:700;letter-spacing:-.2px;
  display:flex;align-items:center;gap:6px;
}
.ui-errb-n{
  font-size:10.5px;font-weight:700;padding:1px 6px;border-radius:999px;
  background:var(--badge-danger-bg);color:var(--badge-danger-fg);
  font-variant-numeric:tabular-nums;
}
.ui-errb-n[hidden]{display:none}
.ui-errb-sub{font-size:11.5px;color:var(--text-m);line-height:1.6;margin-top:2px}
.ui-errb-msg{
  font-size:12px;color:var(--danger);margin-top:6px;line-height:1.5;
  word-break:break-word;font-family:'JetBrains Mono','Cascadia Code',monospace;
}
.ui-errb-at{
  font-size:10.5px;color:var(--text-m);margin-top:3px;
  word-break:break-all;font-family:'JetBrains Mono','Cascadia Code',monospace;
}
.ui-errb-x{
  flex:0 0 auto;width:24px;height:24px;border-radius:7px;border:none;
  background:transparent;color:var(--text-m);
  display:flex;align-items:center;justify-content:center;
  transition:all .12s var(--ease);
}
.ui-errb-x:hover{background:var(--surface-hover);color:var(--text)}
.ui-errb-more{margin-top:8px}
.ui-errb-more>summary{
  font-size:11.5px;color:var(--text-m);cursor:pointer;user-select:none;
  list-style:none;padding:2px 0;
}
.ui-errb-more>summary::-webkit-details-marker{display:none}
.ui-errb-more>summary::before{content:'▸ ';font-size:10px}
.ui-errb-more[open]>summary::before{content:'▾ '}
.ui-errb-more>pre{
  margin-top:5px;max-height:150px;overflow:auto;
  background:var(--code-bg);border:1px solid var(--code-border);border-radius:8px;
  padding:8px 10px;font-size:10.5px;line-height:1.55;color:var(--text-s);
  font-family:'JetBrains Mono','Cascadia Code',monospace;white-space:pre-wrap;word-break:break-all;
}
.ui-errb-acts{display:flex;gap:7px;margin-top:10px;flex-wrap:wrap}

/* iOS 皮肤：玻璃卡 + 橙红渐层图示（error.tsx 的 rounded-[20px] 渐层方块） */
[data-skin="ios"] .ui-errb-card{
  border-radius:var(--ios-r-card);
  padding:16px 16px 14px;
  background:var(--ios-glass-bg);
  backdrop-filter:blur(36px) saturate(1.8);
  -webkit-backdrop-filter:blur(36px) saturate(1.8);
  border:1px solid var(--ios-glass-border);
  box-shadow:0 24px 70px rgba(31,38,65,.22);
}
[data-theme="dark"][data-skin="ios"] .ui-errb-card{box-shadow:0 24px 70px rgba(0,0,0,.55)}
[data-skin="ios"] .ui-errb-ico{
  width:34px;height:34px;border-radius:12px;color:#fff;
  background:linear-gradient(135deg,#ff9f0a,#ff2d55);
  box-shadow:0 8px 20px rgba(255,45,85,.3);
}
[data-skin="ios"] .ui-errb-n{background:rgba(255,59,48,.14);color:#ff3b30}
[data-skin="ios"] .ui-errb-msg{color:#ff3b30}

@media(max-width:560px){
  .ui-errb{left:12px;right:12px;top:12px;width:auto}
}
@media(prefers-reduced-motion:reduce){
  .ui-errb-card{animation:none!important}
}


