/* ══════════════════════════════════════════════════════════════════════════
   components.css — 组件层（方向 B · 现代工具风）
   ──────────────────────────────────────────────────────────────────────────
   职责：把 Bootstrap 5.3 的组件改成方向 B 的样子，并补上框架没有的组件
        （toast / dialog / 命令面板 / 骨架屏 / 空状态 / 工具条 / 批量条）。

   纪律：
     · 不碰 vendor/bootstrap.min.css，全部靠「覆盖变量 + 后置同特异性规则」。
     · Bootstrap 的 .bg-* / .py-* 等工具类带 !important，凡是与工具类冲突的
       地方一律用 !important 对等还击，否则样式会静默失效。
     · 只改观感，不改任何页面结构与类名 —— 页面改造留到逐页阶段。
   ══════════════════════════════════════════════════════════════════════════ */

/* ══════════════════ 按钮 ══════════════════ */
.btn {
  --bs-btn-padding-y: 8px;
  --bs-btn-padding-x: 14px;
  --bs-btn-font-size: var(--fz-fs-table);
  --bs-btn-font-weight: 600;
  --bs-btn-border-radius: 8px;
  --bs-btn-focus-box-shadow: 0 0 0 3px var(--fz-ring);
  --bs-btn-disabled-opacity: .45;
  display: inline-flex; align-items: center; gap: 7px;
  border-width: 1px;
  box-shadow: var(--fz-sh-1);
  transition: transform var(--fz-dur-1) var(--fz-ease),
              box-shadow var(--fz-dur-1) var(--fz-ease),
              background-color var(--fz-dur-1) var(--fz-ease),
              border-color var(--fz-dur-1) var(--fz-ease);
}
.btn:hover { box-shadow: var(--fz-sh-2); }
.btn:active { transform: translateY(1px); box-shadow: none; }
.btn:disabled, .btn.disabled { box-shadow: none; }
.btn-sm { --bs-btn-padding-y: 6px; --bs-btn-padding-x: 11px; --bs-btn-font-size: var(--fz-fs-sm); --bs-btn-border-radius: 7px; }
.btn-lg { --bs-btn-font-size: var(--fz-fs-base); --bs-btn-border-radius: var(--fz-r); }
.btn-group > .btn { box-shadow: var(--fz-sh-1); }
.btn-group > .btn:not(:first-child) { border-top-left-radius: 0; border-bottom-left-radius: 0; }
.btn-group > .btn:not(:last-child):not(.dropdown-toggle) { border-top-right-radius: 0; border-bottom-right-radius: 0; }

/* 实心主按钮 */
.btn-primary {
  --bs-btn-color: var(--fz-accent-fg);
  --bs-btn-bg: var(--fz-accent);
  --bs-btn-border-color: var(--fz-accent);
  --bs-btn-hover-color: var(--fz-accent-fg);
  --bs-btn-hover-bg: color-mix(in srgb, var(--fz-accent) 88%, var(--fz-fg));
  --bs-btn-hover-border-color: color-mix(in srgb, var(--fz-accent) 88%, var(--fz-fg));
  --bs-btn-active-color: var(--fz-accent-fg);
  --bs-btn-active-bg: color-mix(in srgb, var(--fz-accent) 78%, var(--fz-fg));
  --bs-btn-active-border-color: color-mix(in srgb, var(--fz-accent) 78%, var(--fz-fg));
  --bs-btn-disabled-bg: var(--fz-accent);
  --bs-btn-disabled-border-color: var(--fz-accent);
}
.btn-secondary, .btn-dark {
  --bs-btn-color: var(--fz-surface);
  --bs-btn-bg: var(--fz-fg);
  --bs-btn-border-color: var(--fz-fg);
  --bs-btn-hover-color: var(--fz-surface);
  --bs-btn-hover-bg: color-mix(in srgb, var(--fz-fg) 84%, var(--fz-bg));
  --bs-btn-hover-border-color: color-mix(in srgb, var(--fz-fg) 84%, var(--fz-bg));
}

/* 描边按钮 */
.btn-outline-primary   { --bs-btn-color: var(--fz-accent); --bs-btn-border-color: color-mix(in srgb, var(--fz-accent) 34%, transparent);
  --bs-btn-hover-bg: var(--fz-accent-soft); --bs-btn-hover-color: var(--fz-accent);
  --bs-btn-hover-border-color: var(--fz-accent); --bs-btn-active-bg: var(--fz-accent-soft); --bs-btn-active-color: var(--fz-accent); --bs-btn-active-border-color: var(--fz-accent); }
.btn-outline-secondary { --bs-btn-color: var(--fz-muted); --bs-btn-border-color: var(--fz-line-2);
  --bs-btn-hover-bg: var(--fz-sunken); --bs-btn-hover-color: var(--fz-fg); --bs-btn-hover-border-color: var(--fz-line-2);
  --bs-btn-active-bg: var(--fz-sunken); --bs-btn-active-color: var(--fz-fg); --bs-btn-active-border-color: var(--fz-dim); }
.btn-outline-success   { --bs-btn-color: var(--fz-ok); --bs-btn-border-color: color-mix(in srgb, var(--fz-ok) 34%, transparent);
  --bs-btn-hover-bg: var(--fz-ok-soft); --bs-btn-hover-color: var(--fz-ok); --bs-btn-hover-border-color: var(--fz-ok);
  --bs-btn-active-bg: var(--fz-ok-soft); --bs-btn-active-color: var(--fz-ok); --bs-btn-active-border-color: var(--fz-ok); }
.btn-outline-warning   { --bs-btn-color: var(--fz-warn); --bs-btn-border-color: color-mix(in srgb, var(--fz-warn) 34%, transparent);
  --bs-btn-hover-bg: var(--fz-warn-soft); --bs-btn-hover-color: var(--fz-warn); --bs-btn-hover-border-color: var(--fz-warn);
  --bs-btn-active-bg: var(--fz-warn-soft); --bs-btn-active-color: var(--fz-warn); --bs-btn-active-border-color: var(--fz-warn); }
.btn-outline-danger    { --bs-btn-color: var(--fz-danger); --bs-btn-border-color: color-mix(in srgb, var(--fz-danger) 34%, transparent);
  --bs-btn-hover-bg: var(--fz-danger-soft); --bs-btn-hover-color: var(--fz-danger); --bs-btn-hover-border-color: var(--fz-danger);
  --bs-btn-active-bg: var(--fz-danger-soft); --bs-btn-active-color: var(--fz-danger); --bs-btn-active-border-color: var(--fz-danger); }
.btn-outline-info      { --bs-btn-color: var(--fz-info); --bs-btn-border-color: color-mix(in srgb, var(--fz-info) 34%, transparent);
  --bs-btn-hover-bg: var(--fz-info-soft); --bs-btn-hover-color: var(--fz-info); --bs-btn-hover-border-color: var(--fz-info);
  --bs-btn-active-bg: var(--fz-info-soft); --bs-btn-active-color: var(--fz-info); --bs-btn-active-border-color: var(--fz-info); }
.btn-outline-light     { --bs-btn-color: var(--fz-muted); --bs-btn-border-color: var(--fz-line-2);
  --bs-btn-hover-bg: var(--fz-sunken); --bs-btn-hover-color: var(--fz-fg); --bs-btn-hover-border-color: var(--fz-line-2); }

/* 文字按钮：不需要阴影与描边 */
.btn-link, .btn-quiet, .btn-ghost {
  --bs-btn-color: var(--fz-muted);
  border-color: transparent !important; background: transparent !important; box-shadow: none !important;
  text-decoration: none;
}
.btn-link:hover, .btn-quiet:hover, .btn-ghost:hover { background: var(--fz-sunken) !important; color: var(--fz-fg) !important; }
.btn-link.text-danger, .btn-ghost.text-danger { color: var(--fz-danger) !important; }

/* 执行中（按钮内联 spinner 时保持尺寸不跳） */
.fz-loading { position: relative; pointer-events: none; opacity: .8; }
.fz-loading::after {
  content: ""; width: 13px; height: 13px; border-radius: 50%; flex: none;
  border: 2px solid currentColor; border-right-color: transparent; animation: fz-spin .7s linear infinite;
}
@keyframes fz-spin { to { transform: rotate(360deg); } }

/* ══════════════════ 卡片 ══════════════════ */
.card {
  background: var(--fz-surface);
  border: 1px solid var(--fz-line);
  border-radius: var(--fz-r);
  box-shadow: var(--fz-sh-1);
}
.card-header { background: transparent; border-bottom: 1px solid var(--fz-line); font-weight: 650; }
.card-header strong { font-weight: 650; font-size: var(--fz-fs-table); }
.card-footer { background: transparent; border-top: 1px solid var(--fz-line); }
.card-body > h5, .card-body > h6 { margin-bottom: var(--fz-s3); }
@media (hover: hover) and (pointer: fine) {
  .fz-hoverable:hover { transform: translateY(-1px); box-shadow: var(--fz-sh-2); }
}

/* ══════════════════ 表格 ══════════════════ */
.table {
  --bs-table-bg: transparent;
  margin-bottom: 0;
  font-size: var(--fz-fs-table);
  vertical-align: middle;
}
.table > thead > tr > th {
  position: sticky; top: 0; z-index: 2;
  background: var(--fz-sunken);
  color: var(--fz-muted);
  font-size: var(--fz-fs-sm);
  font-weight: 600;
  letter-spacing: .01em;
  white-space: nowrap;
  border-bottom: 1px solid var(--fz-line);
  padding: 10px 14px;
}
.table > tbody > tr > td { padding: 10px 14px; height: var(--fz-row-h); border-color: var(--fz-line); }
.table-sm > :not(caption) > * > * { padding: 10px 14px; }
.table > tbody > tr:last-child > td { border-bottom: 0; }
.table-hover > tbody > tr:hover > * { --bs-table-bg-state: var(--fz-sunken); }
.table-light { --bs-table-bg: var(--fz-sunken); --bs-table-color: var(--fz-muted); }

/* 表格容器：给卡片内的大表一个滚动上限，粘性表头才有落点 */
.card > .table-responsive { max-height: min(74vh, 720px); }
.table-responsive { -webkit-overflow-scrolling: touch; }
.table-responsive > .table > thead > tr > th { box-shadow: 0 1px 0 var(--fz-line); }

/* ══════════════════ 标签 / 角标（柔和填充，不描边）══════════════════ */
.badge {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 9px; border-radius: var(--fz-r-sm);
  font-size: var(--fz-fs-micro); font-weight: 600; line-height: 1.5;
  background: var(--fz-sunken); color: var(--fz-muted);
}
.badge.bg-light, .badge.bg-secondary { background: var(--fz-sunken) !important; color: var(--fz-muted) !important; }
.badge.bg-primary   { background: var(--fz-accent-soft) !important; color: var(--fz-accent) !important; }
.badge.bg-success   { background: var(--fz-ok-soft) !important;     color: var(--fz-ok) !important; }
.badge.bg-warning   { background: var(--fz-warn-soft) !important;   color: var(--fz-warn) !important; }
.badge.bg-danger    { background: var(--fz-danger-soft) !important; color: var(--fz-danger) !important; }
.badge.bg-info      { background: var(--fz-info-soft) !important;   color: var(--fz-info) !important; }
.badge.bg-dark      { background: color-mix(in srgb, var(--fz-fg) 12%, transparent) !important; color: var(--fz-fg) !important; }
/* 角标里的状态点（liveBadge 之类）：让点跟着文字色 */
.badge .fz-dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex: none; }

/* 状态点（LED）*/
.fz-led { width: 7px; height: 7px; border-radius: 50%; display: inline-block; flex: none; background: var(--fz-faint); }
.fz-led.ok { background: var(--fz-ok); }   .fz-led.warn { background: var(--fz-warn); }
.fz-led.bad { background: var(--fz-danger); } .fz-led.info { background: var(--fz-info); }
.fz-led.accent { background: var(--fz-accent); }
.fz-led.live { animation: fz-ring 2s var(--fz-ease) infinite; }
@keyframes fz-ring {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, currentColor 40%, transparent); }
  50% { box-shadow: 0 0 0 5px transparent; }
}

/* ══════════════════ 表单 ══════════════════ */
.form-label { font-size: var(--fz-fs-sm); font-weight: 600; color: var(--fz-muted); margin-bottom: 6px; }
.form-control, .form-select {
  font-size: var(--fz-fs-table);
  padding: 9px 12px;
  color: var(--fz-fg);
  background-color: var(--fz-surface);
  border: 1px solid var(--fz-line-2);
  border-radius: 8px;
  transition: border-color var(--fz-dur-1) var(--fz-ease), box-shadow var(--fz-dur-1) var(--fz-ease);
}
/* ★ 下拉箭头的预留空间：上面那句 padding 简写把 Bootstrap 留给箭头的
   padding-right（2.25rem）一起冲掉了，只余 10px —— 而箭头画在 right .5rem 处、
   宽 16px，于是正好压在文字上（select 越窄越明显）。
   这里把预留空间还回去，并换成与主题同色的箭头（深浅各一套）。 */
:root {
  --fz-select-arrow: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%236B6560' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");
}
html[data-bs-theme="dark"] {
  --fz-select-arrow: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23A8A29E' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");
}
.form-select {
  --bs-form-select-bg-img: var(--fz-select-arrow);
  padding-right: 2.1rem;
  background-position: right .7rem center;
  background-size: 14px 11px;
}
/* 深色那行必须带 html[...]，否则压不过 Bootstrap 的 [data-bs-theme=dark] .form-select */
html[data-bs-theme="dark"] .form-select { --bs-form-select-bg-img: var(--fz-select-arrow); }

.form-control-sm { padding: 6px 10px; font-size: var(--fz-fs-sm); border-radius: 7px; }
.form-select-sm {
  padding: 6px 10px; padding-right: 1.9rem;
  font-size: var(--fz-fs-sm); border-radius: 7px;
  background-position: right .55rem center; background-size: 12px 10px;
}
.form-control::placeholder { color: var(--fz-dim); }
/* Bootstrap 的聚焦色是硬编码的 #86b7fe，必须显式覆盖 */
.form-control:focus, .form-select:focus {
  color: var(--fz-fg);
  background-color: var(--fz-surface);
  border-color: var(--fz-accent);
  box-shadow: 0 0 0 3px var(--fz-accent-soft);
  outline: 0;
}
.form-control:disabled, .form-select:disabled { background: var(--fz-sunken); color: var(--fz-faint); }
.form-control.is-invalid, .form-select.is-invalid { border-color: var(--fz-danger); }
.form-text { font-size: var(--fz-fs-sm); color: var(--fz-dim); }
.input-group-text { background: var(--fz-sunken); border-color: var(--fz-line-2); color: var(--fz-muted); font-size: var(--fz-fs-sm); }
.input-group > .form-control, .input-group > .form-select { border-radius: 8px; }

/* 勾选框 / 开关：Bootstrap 也是硬编码 #0d6efd */
.form-check-input { border-color: var(--fz-line-2); background-color: var(--fz-surface); }
.form-check-input:checked { background-color: var(--fz-accent); border-color: var(--fz-accent); }
.form-check-input:focus { border-color: var(--fz-accent); box-shadow: 0 0 0 3px var(--fz-accent-soft); }
.form-check-input:indeterminate { background-color: var(--fz-accent); border-color: var(--fz-accent); }
.form-check-label { font-size: var(--fz-fs-table); }
.form-switch .form-check-input { width: 2.4em; height: 1.3em; }
.form-switch .form-check-input:focus { background-image: var(--bs-form-switch-bg); }

/* 数字输入的微调箭头在暖色底上很脏 */
input[type="number"] { appearance: textfield; -moz-appearance: textfield; }
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button { appearance: none; margin: 0; }

/* ══════════════════ 提示条 / 告警 ══════════════════ */
.alert {
  --bs-alert-border-radius: var(--fz-r);
  --bs-alert-padding-y: 12px;
  --bs-alert-padding-x: 16px;
  --bs-alert-border-width: 1px;
  font-size: var(--fz-fs-table);
  border-width: 1px;
}
.alert-success { --bs-alert-color: var(--fz-ok);     --bs-alert-bg: var(--fz-ok-soft);
  --bs-alert-border-color: color-mix(in srgb, var(--fz-ok) 30%, transparent);
  --bs-alert-link-color: var(--fz-ok); }
.alert-danger  { --bs-alert-color: var(--fz-danger); --bs-alert-bg: var(--fz-danger-soft);
  --bs-alert-border-color: color-mix(in srgb, var(--fz-danger) 30%, transparent);
  --bs-alert-link-color: var(--fz-danger); }
.alert-warning { --bs-alert-color: var(--fz-warn);   --bs-alert-bg: var(--fz-warn-soft);
  --bs-alert-border-color: color-mix(in srgb, var(--fz-warn) 34%, transparent);
  --bs-alert-link-color: var(--fz-warn); }
.alert-info    { --bs-alert-color: var(--fz-info);   --bs-alert-bg: var(--fz-info-soft);
  --bs-alert-border-color: color-mix(in srgb, var(--fz-info) 30%, transparent);
  --bs-alert-link-color: var(--fz-info); }
.alert-light   { --bs-alert-color: var(--fz-muted);  --bs-alert-bg: var(--fz-sunken);
  --bs-alert-border-color: var(--fz-line); --bs-alert-link-color: var(--fz-accent); }
.alert-primary { --bs-alert-color: var(--fz-accent); --bs-alert-bg: var(--fz-accent-soft);
  --bs-alert-border-color: color-mix(in srgb, var(--fz-accent) 30%, transparent); }
.alert strong { font-weight: 650; }
.alert .btn-close { padding: 14px; }

.btn-close { --bs-btn-close-focus-shadow: 0 0 0 3px var(--fz-ring); }

/* ══════════════════ 导航栏 ══════════════════ */
.navbar {
  --bs-navbar-padding-y: 0;
  --bs-navbar-padding-x: var(--fz-s5);
  position: sticky; top: 0; z-index: var(--fz-z-nav);
  min-height: var(--fz-nav-h);
  background: color-mix(in srgb, var(--fz-bg) 88%, transparent);
  backdrop-filter: saturate(1.4) blur(10px);
  border-bottom: 1px solid var(--fz-line);
}
/* 导航内层容器跟着主内容同样的宽度上限，否则宽屏下导航与正文左边缘错位 */
.navbar > .container-fluid {
  max-width: var(--fz-w-page);
  margin: 0 auto;
  padding-left: var(--fz-s5);
  padding-right: var(--fz-s5);
}
@media (max-width: 767.98px) {
  .navbar > .container-fluid { padding-left: var(--fz-s3); padding-right: var(--fz-s3); }
}
.navbar-brand {
  display: inline-flex; align-items: center; gap: 9px;
  font-size: var(--fz-fs-md); font-weight: 650; color: var(--fz-fg) !important;
  margin-right: var(--fz-s4); padding: 0;
}
.navbar-brand .fz-brand-mark {
  width: 24px; height: 24px; border-radius: 8px; flex: none;
  background: var(--fz-accent); color: var(--fz-accent-fg);
  display: grid; place-items: center; font-size: 12.5px; font-weight: 700;
}
.navbar .navbar-nav { align-items: center; gap: 2px; }
.navbar .nav-link {
  color: var(--fz-muted);
  font-size: 14px;
  padding: 7px 12px;
  border-radius: 8px;
  transition: background-color var(--fz-dur-1) var(--fz-ease), color var(--fz-dur-1) var(--fz-ease);
}
.navbar .nav-link:hover, .navbar .nav-link:focus-visible { background: var(--fz-sunken); color: var(--fz-fg); }
.navbar .nav-link.active {
  background: var(--fz-surface); color: var(--fz-fg); font-weight: 600;
  box-shadow: var(--fz-sh-1);
}
.navbar-text { color: var(--fz-muted) !important; font-size: var(--fz-fs-table); }
.navbar-toggler { border-color: var(--fz-line-2); padding: 5px 8px; }
.navbar-toggler:focus { box-shadow: 0 0 0 3px var(--fz-ring); }
.navbar-toggler-icon {
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%2828,25,23,0.7%29' stroke-linecap='round' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");
}
html[data-bs-theme="dark"] .navbar-toggler-icon {
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28245,245,244,0.7%29' stroke-linecap='round' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");
}
@media (max-width: 991.98px) {
  .navbar .navbar-nav { align-items: stretch; }
  .navbar .nav-link { padding: 9px 12px; }
  .navbar .navbar-collapse { padding: var(--fz-s2) 0 var(--fz-s3); }
}

/* 导航里的图标按钮 */
.fz-nav-btn {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: var(--fz-fs-sm); font-weight: 600; color: var(--fz-muted);
  background: transparent; border: 1px solid transparent; border-radius: 8px;
  padding: 6px 10px; cursor: pointer;
  transition: background-color var(--fz-dur-1) var(--fz-ease), color var(--fz-dur-1) var(--fz-ease);
}
.fz-nav-btn:hover { background: var(--fz-sunken); color: var(--fz-fg); }
.fz-nav-btn .kbd {
  font-family: var(--fz-font-mono); font-size: 11px;
  border: 1px solid var(--fz-line); border-radius: 5px; padding: 0 5px; background: var(--fz-surface);
}

/* ══════════════════ 下拉菜单 ══════════════════ */
.dropdown-menu { padding: 6px; box-shadow: var(--fz-sh-pop); font-size: var(--fz-fs-table); }
.dropdown-item { border-radius: var(--fz-r-sm); padding: 8px 10px; }
.dropdown-item:active, .dropdown-item.active { background: var(--fz-accent-soft); color: var(--fz-accent); }
.dropdown-divider { border-color: var(--fz-line); margin: 5px 2px; }
.dropdown-toggle::after { margin-left: 5px; border-top-color: currentColor; }

/* ══════════════════ 列表组 / 面包屑 / 分页 / 标签页 ══════════════════ */
.list-group-item { background: transparent; border-color: var(--fz-line); font-size: var(--fz-fs-table); padding: 10px 18px; }
.breadcrumb { --bs-breadcrumb-item-padding-x: 8px; font-size: var(--fz-fs-table); margin-bottom: var(--fz-s4); }
.breadcrumb-item a { text-decoration: none; }
.breadcrumb-item.active { color: var(--fz-muted); }
.pagination { --bs-pagination-padding-y: 6px; --bs-pagination-padding-x: 11px; --bs-pagination-font-size: var(--fz-fs-sm); }
.page-link { border-radius: 7px !important; margin: 0 2px; border-color: var(--fz-line); }
.page-item.active .page-link { background: var(--fz-accent); border-color: var(--fz-accent); color: var(--fz-accent-fg); }
.nav-tabs { border-bottom-color: var(--fz-line); gap: 4px; }
.nav-tabs .nav-link {
  border: 0; border-bottom: 2px solid transparent; border-radius: 0;
  color: var(--fz-muted); font-size: var(--fz-fs-table); font-weight: 600; padding: 9px 14px;
  background: transparent;
}
.nav-tabs .nav-link:hover { border-bottom-color: var(--fz-line-2); color: var(--fz-fg); background: transparent; }
.nav-tabs .nav-link.active {
  color: var(--fz-accent); background: transparent; border-bottom-color: var(--fz-accent);
}
.tab-content > .tab-pane { padding-top: var(--fz-s4); }

/* ══════════════════ 进度条 / 加载 ══════════════════ */
.progress { height: 8px; background: var(--fz-sunken); border-radius: var(--fz-r-pill); }
.progress-bar { font-size: 0; }
.spinner-border { border-width: 2px; }

/* ══════════════════ 统计卡（页面层 app.css 用的壳）══════════════════ */
.fz-stat {
  background: var(--fz-surface); border: 1px solid var(--fz-line); border-radius: var(--fz-r);
  box-shadow: var(--fz-sh-1); padding: 16px 17px;
  transition: transform var(--fz-dur-1) var(--fz-ease), box-shadow var(--fz-dur-1) var(--fz-ease);
}
.fz-stat .fz-stat-label { font-size: var(--fz-fs-sm); color: var(--fz-muted); display: flex; align-items: center; gap: 6px; }
.fz-stat .fz-stat-num { font-size: var(--fz-fs-stat); font-weight: 680; line-height: 1.15; margin-top: 8px; letter-spacing: -.02em; }
.fz-stat .fz-stat-foot { font-size: var(--fz-fs-micro); color: var(--fz-dim); margin-top: 8px; }
.fz-stat.is-ok .fz-stat-num { color: var(--fz-ok); }
.fz-stat.is-partial .fz-stat-num, .fz-stat.is-expire .fz-stat-num { color: var(--fz-warn); }
.fz-stat.is-pending .fz-stat-num { color: var(--fz-accent); }
.fz-stat.is-bad .fz-stat-num { color: var(--fz-danger); }

/* ══════════════════ 工具条 / 批量条 / 分区 ══════════════════ */
.fz-toolbar {
  display: flex; gap: 10px; align-items: flex-end; flex-wrap: wrap;
  background: var(--fz-surface); border: 1px solid var(--fz-line); border-radius: var(--fz-r);
  box-shadow: var(--fz-sh-1); padding: 14px 16px; margin-bottom: var(--fz-s4);
}
.fz-toolbar .fz-field { display: flex; flex-direction: column; min-width: 0; }
.fz-toolbar .fz-field.grow { flex: 1 1 320px; min-width: 220px; }
.fz-toolbar .fz-field > label { font-size: var(--fz-fs-sm); color: var(--fz-muted); margin-bottom: 5px; }
/* 几个筛选下拉宽窄不齐（宽度由最长的选项撑出来）—— 给它们一个统一的下限，
   一排框子就对齐了。目前只有账号页的工具条里有这种下拉。 */
.fz-toolbar .fz-field > .form-select { min-width: 8.5rem; }
.fz-bulkbar {
  display: flex; align-items: center; gap: 9px; flex-wrap: wrap;
  padding: 11px 16px; background: var(--fz-surface);
  border: 1px solid var(--fz-line); border-radius: var(--fz-r); box-shadow: var(--fz-sh-2);
  margin-bottom: var(--fz-s4);
  position: sticky; top: calc(var(--fz-nav-h) + 8px); z-index: var(--fz-z-sticky);
  transition: border-color var(--fz-dur-2) var(--fz-ease), box-shadow var(--fz-dur-2) var(--fz-ease);
}
/* 有未保存改动时整条变色：不用去读那行小字也能看出来 */
.fz-bulkbar.is-dirty {
  border-color: color-mix(in srgb, var(--fz-warn) 55%, transparent);
  box-shadow: var(--fz-sh-2), 0 0 0 3px var(--fz-warn-soft);
}
.fz-sec { display: flex; align-items: center; gap: 12px; margin: var(--fz-s5) 0 var(--fz-s3); }
.fz-sec > h2, .fz-sec > h3 { font-size: var(--fz-fs-table); font-weight: 650; color: var(--fz-muted); margin: 0; }
.fz-sec > .fz-rule { flex: 1; height: 1px; background: var(--fz-line); }
.fz-page-head { display: flex; align-items: flex-end; gap: var(--fz-s4); flex-wrap: wrap; margin-bottom: var(--fz-s5); }
.fz-page-head .fz-page-title { font-size: var(--fz-fs-h); font-weight: 680; margin: 0; }
.fz-page-head .fz-page-sub { font-size: var(--fz-fs-table); color: var(--fz-muted); margin-top: 4px; }
.fz-page-head .fz-page-acts { margin-left: auto; display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }

/* 内联可编辑单元格（账号列表 / 账号详情）*/
.fz-editable { cursor: text; border-radius: var(--fz-r-sm); transition: background-color var(--fz-dur-1) var(--fz-ease); }
.fz-editable:hover { background: var(--fz-sunken); }
.fz-editable:focus-visible { outline: 2px solid var(--fz-accent); outline-offset: -2px; }
.fz-editable .fz-edit-hint { color: var(--fz-dim); font-size: var(--fz-fs-micro); opacity: 0; transition: opacity var(--fz-dur-1) var(--fz-ease); }
.fz-editable:hover .fz-edit-hint { opacity: 1; }
.fz-cell-flash { font-size: var(--fz-fs-micro); margin-left: 6px; }

/* ══════════════════ 吐司 ══════════════════ */
.fz-toasts {
  position: fixed; right: var(--fz-s5); bottom: var(--fz-s5); z-index: var(--fz-z-toast);
  display: flex; flex-direction: column; gap: 9px; pointer-events: none;
}
.fz-toast {
  display: flex; align-items: center; gap: 10px; min-width: 220px; max-width: min(420px, 88vw);
  background: var(--fz-surface); border: 1px solid var(--fz-line); border-radius: var(--fz-r);
  padding: 11px 15px; font-size: var(--fz-fs-table); box-shadow: var(--fz-sh-pop);
  pointer-events: auto; animation: fz-toast-in var(--fz-dur-3) var(--fz-ease) both;
}
.fz-toast.is-leaving { animation: fz-toast-out var(--fz-dur-2) var(--fz-ease) both; }
.fz-toast .fz-toast-body { flex: 1; }
.fz-toast .fz-toast-close { border: 0; background: transparent; color: var(--fz-dim); cursor: pointer; font-size: 16px; line-height: 1; padding: 2px 4px; }
.fz-toast .fz-toast-close:hover { color: var(--fz-fg); }
.fz-toast.ok   { border-left: 3px solid var(--fz-ok); }
.fz-toast.bad  { border-left: 3px solid var(--fz-danger); }
.fz-toast.warn { border-left: 3px solid var(--fz-warn); }
.fz-toast.info { border-left: 3px solid var(--fz-info); }
@keyframes fz-toast-in { from { opacity: 0; transform: translateY(8px) scale(.98); } to { opacity: 1; transform: none; } }
@keyframes fz-toast-out { to { opacity: 0; transform: translateY(6px) scale(.98); } }
@media (max-width: 575.98px) { .fz-toasts { left: var(--fz-s3); right: var(--fz-s3); bottom: var(--fz-s3); } }

/* ══════════════════ 对话框（原生 <dialog>，自带焦点陷阱与 Esc）══════════════════ */
dialog.fz-dialog {
  border: 0; padding: 0; background: transparent;
  max-width: min(460px, 92vw); width: 100%;
  margin: auto;
}
dialog.fz-dialog::backdrop { background: rgba(28,25,23,.44); backdrop-filter: blur(3px); }
html[data-bs-theme="dark"] dialog.fz-dialog::backdrop { background: rgba(0,0,0,.6); }
.fz-dialog-card {
  background: var(--fz-surface); border: 1px solid var(--fz-line); border-radius: var(--fz-r-lg);
  padding: 22px; box-shadow: var(--fz-sh-pop);
  animation: fz-dialog-in var(--fz-dur-3) var(--fz-ease) both;
}
@keyframes fz-dialog-in { from { opacity: 0; transform: scale(.96) translateY(6px); } to { opacity: 1; transform: none; } }
.fz-dialog-card h2, .fz-dialog-card h3 { font-size: var(--fz-fs-md); margin-bottom: 8px; }
.fz-dialog-card .fz-dialog-text { color: var(--fz-muted); font-size: var(--fz-fs-table); margin: 0 0 18px; white-space: pre-line; }
.fz-dialog-card .fz-dialog-acts { display: flex; gap: 9px; justify-content: flex-end; }

/* ══════════════════ 命令面板 ══════════════════ */
.fz-palette-scrim {
  position: fixed; inset: 0; z-index: var(--fz-z-overlay);
  background: rgba(28,25,23,.42); backdrop-filter: blur(3px);
  display: flex; align-items: flex-start; justify-content: center; padding-top: 11vh;
  animation: fz-fade var(--fz-dur-2) var(--fz-ease) both;
}
html[data-bs-theme="dark"] .fz-palette-scrim { background: rgba(0,0,0,.58); }
@keyframes fz-fade { from { opacity: 0; } to { opacity: 1; } }
.fz-palette {
  width: min(560px, 92vw); background: var(--fz-surface);
  border: 1px solid var(--fz-line); border-radius: var(--fz-r-lg); box-shadow: var(--fz-sh-pop);
  overflow: hidden; animation: fz-dialog-in var(--fz-dur-3) var(--fz-ease) both;
}
.fz-palette-search { display: flex; align-items: center; gap: 11px; padding: 15px 18px; border-bottom: 1px solid var(--fz-line); color: var(--fz-muted); }
.fz-palette-search input { flex: 1; border: 0; background: none; color: var(--fz-fg); font: inherit; font-size: var(--fz-fs-base); outline: none; }
.fz-palette-list { list-style: none; margin: 0; padding: 8px; max-height: 46vh; overflow: auto; }
.fz-palette-list li {
  display: flex; align-items: center; gap: 11px; padding: 10px 12px;
  border-radius: var(--fz-r-sm); font-size: 14px; cursor: pointer;
}
.fz-palette-list li[aria-selected="true"] { background: var(--fz-accent-soft); color: var(--fz-accent); font-weight: 600; }
.fz-palette-list li .fz-hint { margin-left: auto; font-size: var(--fz-fs-sm); color: var(--fz-dim); }
.fz-palette-group { font-size: var(--fz-fs-micro); font-weight: 600; color: var(--fz-dim); padding: 10px 14px 4px; cursor: default; }
.fz-palette-group:hover { background: transparent !important; }
.fz-palette-empty { padding: 22px 18px; text-align: center; color: var(--fz-dim); font-size: var(--fz-fs-table); }

/* ══════════════════ 骨架屏 / 空状态 / 错误状态 ══════════════════ */
.fz-sk {
  background: linear-gradient(90deg, var(--fz-sunken) 0%, color-mix(in srgb, var(--fz-sunken) 55%, var(--fz-surface)) 50%, var(--fz-sunken) 100%);
  background-size: 200% 100%; animation: fz-sk 1.4s linear infinite;
  border-radius: var(--fz-r-sm); height: 12px;
}
@keyframes fz-sk { to { background-position: -200% 0; } }
.fz-empty { text-align: center; padding: 38px 20px; color: var(--fz-dim); }
.fz-empty .fz-empty-ico {
  width: 46px; height: 46px; margin: 0 auto 12px; border-radius: 14px;
  background: var(--fz-sunken); color: var(--fz-muted); display: grid; place-items: center;
}
.fz-empty b { display: block; color: var(--fz-fg); font-weight: 600; font-size: var(--fz-fs-table); margin-bottom: 5px; }
.fz-empty .fz-empty-act { margin-top: 14px; }

/* ══════════════════ 详情表（账号详情 / 结果卡）══════════════════ */
.fz-kv { width: 100%; font-size: var(--fz-fs-table); }
.fz-kv > tbody > tr > th {
  width: 34%; text-align: left; font-weight: 600; color: var(--fz-muted);
  padding: 9px 12px 9px 0; vertical-align: top; font-size: var(--fz-fs-sm);
}
.fz-kv > tbody > tr > td { padding: 9px 0; border-bottom: 1px solid var(--fz-line); }
.fz-kv > tbody > tr:last-child > td { border-bottom: 0; }

/* ══════════════════════════════════════════════════════════════════════════
   响应式收敛
   目标不是「手机上体验原生」，而是「窄屏不破版、每一项都还能点到」：
   宽表横向滚动而不是把列挤烂；页头操作区换行而不是挤出屏幕。
   ══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 991.98px) {
  .fz-page-head { align-items: flex-start; }
  .fz-page-head .fz-page-acts { margin-left: 0; width: 100%; }
  .fz-palette-scrim { padding-top: 6vh; }
  .fz-toolbar { padding: 12px 14px; gap: 9px; }
  .fz-toasts { right: var(--fz-s3); bottom: var(--fz-s3); left: var(--fz-s3); }
}
@media (max-width: 767.98px) {
  /* 宽表给一个下限宽度：宁可横向滚动，也不要把备注列压成一条线 */
  .card > .table-responsive > .table { min-width: 660px; }
  .table > thead > tr > th, .table > tbody > tr > td { padding: 10px 12px; }
}
@media (max-width: 575.98px) {
  .fz-page-head .fz-page-title { font-size: 20px; }
  /* 小屏上吸顶会挡内容，改成普通块 */
  .fz-bulkbar { position: static; }
  .fz-palette-scrim { padding: 0; align-items: stretch; }
  .fz-palette { width: 100%; border-radius: 0; }
  .fz-toast { min-width: 0; }
}
