/* ============================================================
   手机 / 平板适配层  responsive.css
   ------------------------------------------------------------
   设计原则
   1. 只做增强：桌面端外观零改动 —— 所有布局规则都收在 @media 里，
      媒体外的规则只加"安全属性"（防溢出、触控反馈等无害项）
   2. 断点收敛为 4 档
        ≤400px        小屏手机
        ≤767px        手机（主战场）
        768–1023px    平板
        ≥1024px       桌面（不干预）
   3. 本文件在所有页面样式之后加载，可用 !important 压过页面自带样式
   4. 触控优先：可点区域 ≥44px；输入控件字号 ≥16px（防 iOS 聚焦时自动放大页面）
   ------------------------------------------------------------
   对应需求：布局 / 字体 / 颜色 / 表格
   ============================================================ */

/* ══════════════════════════════════════════════════════════
   0. 基础：防横向溢出 + 媒体资源自适应（全平台安全，随时可留）
   ══════════════════════════════════════════════════════════ */
html {
  -webkit-text-size-adjust: 100%;   /* 阻止 iOS 横屏时擅自放大字号 */
  text-size-adjust: 100%;
}
img, video, canvas, svg, iframe {
  max-width: 100%;
  height: auto;
}
pre, code, kbd, samp {
  overflow-wrap: break-word;
  word-break: break-word;
}
/* 长英文/URL/连续数字（序列号、身份证号）在窄屏会把容器撑破 */
.text-break, .record-text, .card-title, .info-value, td, th {
  overflow-wrap: anywhere;
}

/* 断点集中说明（供后续维护参考）
   --bp-xs : 400px   --bp-sm : 767px   --bp-md : 1023px */

/* ══════════════════════════════════════════════════════════
   1. 平板：768–1023px
   ══════════════════════════════════════════════════════════ */
@media (max-width: 1023px) {

  /* ── 1.1 布局：容器留白收窄 ── */
  .container,
  .page-content,
  .page-wrapper > .container {
    padding-left: 16px !important;
    padding-right: 16px !important;
  }

  /* ── 1.2 布局：宽网格降到两列 ── */
  .instrument-grid,
  .reagent-grid,
  .consumable-grid,
  .freezer-grid,
  .plant-grid,
  .card-grid,
  .stats-grid {
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)) !important;
  }
}

/* ══════════════════════════════════════════════════════════
   2. 手机：≤767px
   ══════════════════════════════════════════════════════════ */
@media (max-width: 767px) {

  /* ──────────────────────────────
     2.1 布局 —— 容器与网格
     ────────────────────────────── */
  .container,
  .page-content {
    padding: 12px !important;
    max-width: 100% !important;
  }

  /* 所有多列网格 → 单列（移动端一屏一卡片，避免横向挤压）
     ★ 必须排除预约时段网格（.slot-grid）与迷你色带（.mini-slots）：
       前者是"一天 14 格"的矩阵，压成单列就完全失去"一眼看全"的意义。 */
  .instrument-grid,
  .reagent-grid,
  .consumable-grid,
  .freezer-grid,
  .plant-grid,
  .card-grid,
  .stats-grid,
  .info-grid,
  .form-grid,
  .detail-grid,
  [class*="-grid"]:not(.slot-grid):not(.mini-slots) {
    grid-template-columns: 1fr !important;
    gap: 12px !important;
  }

  /* 双列表单/描述列表 → 单列 */
  .form-row,
  .detail-row,
  .meta-row {
    flex-direction: column !important;
    align-items: stretch !important;
    gap: 8px !important;
  }

  /* ── 卡片内边距收窄，换回内容宽度 ── */
  .instrument-card,
  .reagent-card,
  .consumable-card,
  .freezer-card,
  .plant-card,
  .info-card,
  .card,
  .record-item,
  .panel {
    padding: 14px !important;
    border-radius: 10px !important;
  }

  /* ── 页头标题区：竖排，避免标题和按钮抢宽度 ── */
  .page-header,
  .detail-header {
    flex-direction: column !important;
    align-items: stretch !important;
    gap: 12px !important;
    margin-bottom: 16px !important;
  }
  .page-header > div,
  .detail-header > div,
  .header-left,
  .header-actions,
  .page-left {
    width: 100%;
  }

  /* ── 操作栏：按钮换行铺满，方便拇指点 ── */
  .action-bar,
  .toolbar {
    flex-direction: column !important;
    align-items: stretch !important;
    gap: 10px !important;
    padding: 12px !important;
  }
  .action-left, .action-right,
  .toolbar-left, .toolbar-right {
    width: 100% !important;
    flex-wrap: wrap !important;
    gap: 8px !important;
  }
  .action-left > *, .action-right > *,
  .toolbar-left > *, .toolbar-right > * {
    flex: 1 1 auto;
  }

  /* ── 搜索框铺满，不再固定 min-width ── */
  .search-box {
    width: 100% !important;
    min-width: 0 !important;
    max-width: none !important;
  }
  .search-input {
    width: 100% !important;
    min-width: 0 !important;
  }

  /* ── 卡片底部操作按钮：等分铺开、加大高度 ── */
  .card-actions,
  .instrument-actions,
  .detail-actions {
    display: grid !important;
    grid-template-columns: repeat(auto-fit, minmax(84px, 1fr)) !important;
    gap: 8px !important;
  }
  .card-actions > .btn,
  .card-actions > .action-btn,
  .card-actions > .action-item,
  .instrument-actions > * {
    width: 100% !important;
    min-height: 44px !important;
    justify-content: center !important;
  }

  /* ── Tab 栏：横向滚动而不是换行挤压 ── */
  .tabs, .section-tabs {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    flex-wrap: nowrap !important;
    scrollbar-width: none;
    margin-bottom: 14px !important;
  }
  .tabs::-webkit-scrollbar,
  .section-tabs::-webkit-scrollbar { display: none; }
  .tab, .section-tab {
    flex: 0 0 auto !important;
    padding: 11px 14px !important;
    font-size: 0.88rem !important;
    white-space: nowrap;
  }

  /* ── 筛选/分页等横排控件允许换行 ── */
  .filter-bar, .pagination, .legend {
    flex-wrap: wrap !important;
    gap: 8px !important;
  }
}

/* ══════════════════════════════════════════════════════════════
   ★ 断点分档说明（勿删，改动前先读）
   下面「2.2 字体 / 2.3 颜色 / 2.5 表单·触控目标」三组规则的作用域是
   ≤1023px，不是只服务手机。理由：iPad 竖屏（768px）同样是手指操作，
   「输入控件 ≥16px（防 iOS 聚焦自动放大整页）」「触控目标 ≥44px」
   「正文与次要文字对比度 ≥4.5:1」三项是硬性可用性要求。
   若按手机宽度（≤767px）放开，768px 会落进无人的空白带：
   实测该档曾有 13 项不达标（13.3px 输入框、21px 按钮、2.04:1 文字）。
   ══════════════════════════════════════════════════════════════ */
@media (max-width: 1023px) {

  /* ──────────────────────────────
     2.2 字体
     ────────────────────────────── */
  /* 根字号保持 16px：既保证可读，也让 rem 组件自动等比收敛 */
  html { font-size: 16px; }

  /* 标题用 clamp 平滑缩放，不再一把 1.8rem 撑爆窄屏 */
  h1, .page-title { font-size: clamp(1.25rem, 5.2vw, 1.5rem) !important; line-height: 1.3 !important; }
  h2, .card-title { font-size: clamp(1.05rem, 4.2vw, 1.2rem) !important; line-height: 1.35 !important; }
  h3 { font-size: 1.02rem !important; line-height: 1.4 !important; }
  h4 { font-size: 0.95rem !important; }

  /* 正文行高放宽：手机阅读距离更近，行高不足会显得拥挤 */
  body {
    font-size: 15px;
    line-height: 1.65;
  }

  /* 过小字号抬到可读下限（12px 在手机上基本读不清） */
  .record-time, .card-location, .info-label, .card-subtitle,
  .record-badge, .status-badge, .user-role, .sidebar-section-title,
  .text-muted, .muted, small {
    font-size: 0.8125rem !important;   /* 13px */
  }
  /* 被压到 10-11px 的元信息统一抬到 12.5px 并加深颜色 */
  .record-meta, .res-meta, .meta, .hint, .tip {
    font-size: 0.78rem !important;
    line-height: 1.5 !important;
  }

  /* ──────────────────────────────
     2.3 颜色 —— 对比度 & 反馈
     ⚠️ 已移出本媒体查询，改为全宽度生效（见文件末尾「3. 对比度」节）。
        原先写在这里导致桌面端(≥1024px)完全不生效。
     ────────────────────────────── */

}

/* ── 回到「手机档」：表格卡片化只在真·窄屏展开，平板上表格横滚即可 ── */
@media (max-width: 767px) {

  /* ──────────────────────────────
     2.4 表格
     ────────────────────────────── */
  /* 由 responsive.js 自动给表格套上 .table-scroll；
     这里同时给未套上的表格一个纯 CSS 兜底 */
  .table-scroll {
    width: 100% !important;
    overflow-x: auto !important;
    -webkit-overflow-scrolling: touch;
    border-radius: 8px;
    border: 1px solid var(--border-color);
    /* 右侧渐隐提示"还能往右划" */
    background-image: linear-gradient(to left, rgba(0,0,0,0.06), rgba(0,0,0,0) 24px);
    background-repeat: no-repeat;
    background-position: right center;
    background-size: 24px 100%;
  }
  .table-scroll > table { margin: 0 !important; }

  /* 通用表格在手机上的排版 */
  table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.8125rem !important;   /* 13px：表格信息密度略低于正文 */
  }
  /* 表格里再小就废了，抬到 12.5px */
  table small, table .text-muted, td small { font-size: 0.78rem !important; }

  th, td {
    padding: 10px 12px !important;     /* 加大行高，便于横向滑动时不误触 */
    white-space: nowrap;               /* 单元格不折行，水平滚动才流畅 */
    text-align: left;
    vertical-align: middle;
  }
  /* 让最左列（通常是名称）允许折行并吸左，方便滑动时定位 */
  table th:first-child, table td:first-child {
    white-space: normal;
    min-width: 110px;
    position: sticky;
    left: 0;
    background: var(--bg-card, #ffffff);
    z-index: 2;
    box-shadow: 1px 0 0 var(--border-color, #e5e7eb);
  }
  thead th:first-child {
    background: var(--bg-secondary, #f3f4f6);
    z-index: 3;
  }
  /* 表头吸顶：长表格滚动时始终看得见列名 */
  thead th {
    position: sticky;
    top: 0;
    background: var(--bg-secondary, #f3f4f6);
    z-index: 1;
    font-weight: 600;
    color: var(--text-primary);
  }
  /* 数字/日期列等宽对齐，避免滑动时抖动 */
  td.num, th.num, td[data-type="num"] { text-align: right; font-variant-numeric: tabular-nums; }

  /* 极窄屏：表格卡片化（给 table 加 data-mobile="cards" 启用）
     表头用 data-label 显示在每格前面，实现"每行一张小卡片" */
  table[data-mobile="cards"] thead { display: none; }
  table[data-mobile="cards"] tr {
    display: block;
    margin-bottom: 12px;
    border: 1px solid var(--border-color);
    border-radius: 10px;
    background: var(--bg-card);
    overflow: hidden;
  }
  table[data-mobile="cards"] td {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 12px;
    white-space: normal;
    border-bottom: 1px dashed var(--border-color);
    position: static !important;
    box-shadow: none !important;
    background: transparent !important;
  }
  table[data-mobile="cards"] td:last-child { border-bottom: none; }
  table[data-mobile="cards"] td::before {
    content: attr(data-label);
    flex: 0 0 auto;
    font-weight: 600;
    color: var(--text-secondary);
    font-size: 0.78rem;
  }

}

/* ── 表单 / 触控目标回到「平板档」：与 2.2/2.3 同理，768px 同样需要
     16px 输入框与 44px 触控块，不能只按手机宽度放开 ── */
@media (max-width: 1023px) {

  /* ──────────────────────────────
     2.5 表单 / 触控目标
     ────────────────────────────── */
  /* ★ 关键：输入控件字号必须 ≥16px，否则 iOS Safari 聚焦时会自动放大整个页面 */
  input, select, textarea, .form-input, .form-select, .form-textarea, .search-input {
    font-size: 16px !important;
    min-height: 44px;
  }
  textarea, .form-textarea { min-height: 96px; }

  /* 按钮：拇指友好高度 */
  .btn, .action-btn, button:not(.modal-close):not(.sidebar-toggle) {
    min-height: 44px;
    font-size: 0.9rem;
  }
  .btn-sm, .btn.btn-sm {
    min-height: 40px;
    padding: 8px 14px !important;
    font-size: 0.85rem !important;
  }
  /* 图标按钮做成正方触控块 */
  .card-edit-btn, .icon-btn, .play-btn {
    width: 44px !important;
    height: 44px !important;
  }
  /* 弹窗关闭按钮：默认只有 20-32px，手机上是误触重灾区 */
  .modal-close, .modal-close-btn, .close-btn {
    width: 44px !important;
    height: 44px !important;
    min-width: 44px !important;
    min-height: 44px !important;
    font-size: 1.5rem !important;
    line-height: 1 !important;
    display: inline-flex !important;
    align-items: center !important;
    justify-content: center !important;
  }
  /* 下拉自带大触控区 */
  .dropdown-menu, .member-dropdown-menu, .theme-dropdown-menu { min-width: 200px; }
  .menu-item, .theme-option { padding: 12px 14px !important; min-height: 44px; }
}

/* ── 回到「手机档」：弹窗底部抽屉 / 页头单行是窄屏专属形态 ── */
@media (max-width: 767px) {

  /* ──────────────────────────────
     2.6 弹窗 / Toast
     ────────────────────────────── */
  /* 弹窗在手机上改成底部抽屉，贴近拇指操作区 */
  .modal { align-items: flex-end !important; padding: 0 !important; }
  .modal-content, .modal-box, .modal-dialog {
    width: 100% !important;
    max-width: 100% !important;
    max-height: 88vh !important;
    border-radius: 16px 16px 0 0 !important;
    margin: 0 !important;
  }
  .modal-header { padding: 16px !important; }
  .modal-body { padding: 16px !important; }
  /* 底部按钮竖排铺满，方便单手点 */
  .modal-footer {
    padding: 12px 16px calc(12px + env(safe-area-inset-bottom)) !important;
    flex-direction: column-reverse !important;
    gap: 10px !important;
  }
  .modal-footer > .btn { width: 100% !important; justify-content: center !important; }
  /* 加高的弹窗可滚动 */
  .modal-content { overflow-y: auto; -webkit-overflow-scrolling: touch; }

  /* Toast 贴底铺满，避免遮挡内容/被刘海挡住 */
  .toast-container {
    left: 12px !important;
    right: 12px !important;
    bottom: calc(12px + env(safe-area-inset-bottom)) !important;
  }
  .toast {
    min-width: 0 !important;
    max-width: 100% !important;
    width: 100%;
    font-size: 0.9rem;
  }

  /* ──────────────────────────────
     2.7 页头导航（app-header）
     ────────────────────────────── */
  /* 目标：手机上保持"单行"，不再塌成两行吃掉 100px 屏高 */
  .app-header {
    height: 56px !important;
    min-height: 56px !important;
    padding: 0 10px !important;
    gap: 8px !important;
    flex-wrap: nowrap !important;
  }
  .header-left { gap: 8px !important; flex: 0 1 auto !important; min-width: 0; }
  .header-right { gap: 6px !important; flex: 0 0 auto !important; }
  .logo-text { display: none !important; }
  .logo-icon { font-size: 1.45rem !important; }

  /* 隐藏信息密度低、且手机上没人看的项 */
  .user-info { display: none !important; }
  #headerRegisterBtn { display: none !important; }
  /* "返回首页"文字按钮 → 只留 🏠 图标，省下约 60px */
  .app-header .header-right > a.btn[href="/index.html"] {
    font-size: 0 !important;
    padding: 0 !important;
    width: 40px !important;
    min-height: 40px !important;
    justify-content: center !important;
  }
  .app-header .header-right > a.btn[href="/index.html"]::before {
    content: "🏠";
    font-size: 1.1rem;
  }
  /* 主题按钮只留图标 */
  #headerThemeName { display: none !important; }
  .theme-btn { min-width: 0 !important; padding: 8px 10px !important; }
  /* 会员中心按钮只留图标 */
  .member-menu-btn { min-width: 0 !important; }
  .member-menu-btn span:first-child { display: none !important; }
  .member-menu-btn::before { content: "👤"; font-size: 1.1rem; }
  .dropdown-arrow { display: none !important; }

  /* ★ 触屏关键修复：下拉菜单原本靠 :hover 打开，触屏上点不开。
     responsive.js 会补上 .dropdown-open 类 + 点击外部关闭；
     这里同时把 hover-only 的规则在触摸设备上关掉，避免误触发 */
  @media (hover: none) {
    .theme-selector:hover .theme-dropdown-menu,
    .member-menu:hover .member-dropdown-menu,
    .dropdown:hover .dropdown-menu { display: none; }
  }
  .theme-selector.dropdown-open .theme-dropdown-menu,
  .member-menu.dropdown-open .member-dropdown-menu,
  .dropdown.dropdown-open .dropdown-menu {
    display: block !important;
    position: fixed !important;
    left: 12px !important;
    right: 12px !important;
    top: auto !important;
    max-width: none !important;
    max-height: 60vh;
    overflow-y: auto;
  }

  /* ──────────────────────────────
     2.8 浮动主题按钮（theme-switcher.css）
     ────────────────────────────── */
  #themeSwitcher .theme-toggle-btn { transform: scale(0.92); }
  #themePanel {
    max-height: 62vh !important;
    overflow-y: auto;
  }
}

/* ══════════════════════════════════════════════════════════
   3. 小屏手机：≤400px
   ══════════════════════════════════════════════════════════ */
@media (max-width: 400px) {
  html { font-size: 15px; }
  body { font-size: 14.5px; }

  .container, .page-content { padding: 10px !important; }
  .instrument-card, .reagent-card, .card, .info-card, .panel { padding: 12px !important; }

  h1, .page-title { font-size: 1.2rem !important; }
  h2, .card-title { font-size: 1.02rem !important; }

  /* 两列以上的按钮网格降到两列 */
  .card-actions, .instrument-actions {
    grid-template-columns: repeat(2, 1fr) !important;
  }
  /* 页头再收一档 */
  .app-header { padding: 0 8px !important; }
  .header-right { gap: 4px !important; }
  .btn-sm, .btn.btn-sm { padding: 6px 10px !important; font-size: 0.8rem !important; }
}

/* ══════════════════════════════════════════════════════════
   4. 横屏矮屏（如手机横放 667×375）
   ══════════════════════════════════════════════════════════ */
@media (max-height: 480px) and (orientation: landscape) {
  /* 矮屏下弹窗别做底部抽屉，改回居中且限高 */
  .modal { align-items: center !important; padding: 8px !important; }
  .modal-content {
    border-radius: 12px !important;
    max-height: 92vh !important;
    width: 92% !important;
    max-width: 720px !important;
  }
  .modal-footer { flex-direction: row !important; }
  .modal-footer > .btn { width: auto !important; }
}

/* ══════════════════════════════════════════════════════════
   5. 打印：手机样式不得污染打印结果
   ══════════════════════════════════════════════════════════ */
@media print {
  .app-header, .toast-container, #themeSwitcher, .card-actions,
  .action-bar, .toolbar, .no-print { display: none !important; }
  .container, .page-content { padding: 0 !important; }
  table { font-size: 10pt !important; }
  table th, table td { white-space: normal !important; padding: 4px 6px !important; }
  .table-scroll { overflow: visible !important; border: none !important; background: none !important; }
}


/* ════════════════════════════════════════════════════════════
   3. 对比度 & 反馈配色（全宽度生效，不限于窄屏）
   ════════════════════════════════════════════════════════════
   ⚠️ 本节原被误包在「平板档」媒体查询内，导致桌面端
      （≥1024px）一条都不命中：同一页面手机上正常、电脑上发灰。
      对比度属无障碍基线，必须全宽度生效。若需调整，请在此节内改。
   ──────────────────────────────────────────────────────────── */
/* ──────────────────────────────
   2.3 颜色 —— 对比度 & 反馈
   ────────────────────────────── */
/* 手机上 --text-muted(#9ca3af) 在浅色底上对比度仅约 2.5:1，
   远低于 WCAG AA 的 4.5:1；移动端整体压深一档

   ★★ 这里的 !important 不是可选项：
   shared/components/header-init.js 的 paintTheme() 会把整套色板用
   style.setProperty() 内联写到 <html> 上，而「内联非 important 声明」
   优先级高于「样式表非 important 声明」。因此若此处不写 !important，
   这三条覆写会全部失效——变量仍取内联的 #9ca3af，凡是走
   var(--text-muted) 的文字（卡片位置、页脚、说明文案…）对比度就只有 2.54:1。
   （仅 author 的 !important 能压过非 important 的内联声明。） */
:root,
[data-theme="light"],
[data-theme="default"] {
  --text-muted: #5b6472 !important;      /* 原 #6b7280 压浅灰卡面(#f3f4f6)只有 4.39:1；
                                            再压深一档后：白底 5.98:1 / 浅灰 5.44:1，均达标 */
  --text-secondary: #374151 !important;  /* 由 #4b5563 再压深，正文更扎实 */
  --border-color: #d1d5db !important;    /* 边框在手机上更清晰，便于分辨卡片边界 */
}
/* 深色模式下的低对比文字同样抬亮 */
[data-theme="dark"] {
  --text-muted: #94a3b8 !important;
  --text-secondary: #cbd5e1 !important;
}

/* 触控反馈：手机没有 hover，用 :active 给出明确按压反馈 */
.btn:active,
.action-btn:active,
.card-actions > *:active,
.instrument-actions > *:active,
.tab:active,
.menu-item:active,
.theme-option:active {
  transform: scale(0.97);
  filter: brightness(0.94);
}
.instrument-card:active,
.reagent-card:active,
.consumable-card:active,
.card:active {
  transform: scale(0.995);
}
/* 关掉手机上有害的 hover 位移（会造成"点一下跳一下"） */
.instrument-card:hover,
.reagent-card:hover,
.consumable-card:hover,
.freezer-card:hover,
.plant-card:hover,
.card:hover,
.btn-primary:hover {
  transform: none !important;
}

/* 分割线在手机上更明显，帮助区分密集列表 */
.record-item, .list-item, .info-item {
  border-color: var(--border-color) !important;
}

/* ── 品牌色：手机端整体压深一档，一次性解决"白字压亮色"与"亮色字压浅底"两类问题 ──
   实测（375px 真实浏览器）：
     #3b82f6 做底 + 白字 = 3.68:1   （需 4.5） → #2563eb 后 5.17:1 ✅
     #3b82f6 做字 + 浅蓝底 = 3.28:1 （需 4.5） → #1d4ed8 后 5.9:1 ✅
     #10b981 做底 + 白字 = 2.54:1   （需 4.5） → #047857 后 5.4:1 ✅
     #0891b2 做底 + 白字 = 3.68:1   （需 4.5） → #0e7490 后 5.0:1 ✅
   只改手机端，桌面端配色保持原样。 */
:root,
[data-theme="light"],
[data-theme="default"] {
  --accent-primary: #2563eb !important;
  --accent-primary-hover: #1d4ed8 !important;
  --accent-secondary: #047857 !important;
  --accent-info: #0e7490 !important;
  --accent-warning: #b45309 !important;
  --accent-purple: #6d28d9 !important;
  --accent-danger: #b91c1c !important;
  /* ⚠️ --accent-danger 同时是「实心底 + 白字」用色（.btn-danger/.toast-error），
     深色下不能被提亮；凡把 danger 当**文字/描边**压在页面底或淡底上的地方，
     一律用 --accent-danger-text（浅色同值，深色自动提亮）。
     两角色不可能合一：文字需 L>=0.32，白字实心底需 L<=0.18。 */
  --accent-danger-text: #b91c1c !important;
  /* 与上面同理：warning/success/info 也各有一个「深色下不可用的实心/文字共用变量」
     （--accent-warning 恒为 #b45309、--accent-info 恒为 #0e7490，压深色底仅 3.5:1）。
     实心底角色不动，文字/描边角色一律用 -text 变体。
     取值与下面 badge 组里**已通过审计**的色值一致，避免出现第二套真值来源。 */
  --accent-warning-text: #92400e !important;
  --accent-success-text: #047857 !important;
  --accent-info-text: #0e7490 !important;
  --accent-purple-text: #6d28d9 !important;
  /* 部分老页面用的是这套变量名 */
  --primary: #2563eb !important;
  --success: #047857 !important;
  --danger: #b91c1c !important;
  --warning: #b45309 !important;
  --info: #0e7490 !important;
}
/* 深色模式：底色本就暗，主色保持原亮蓝即可；只把绿/橙提亮 */
[data-theme="dark"] {
  --accent-primary: #3b82f6 !important;
  --accent-primary-hover: #60a5fa !important;
  --accent-secondary: #34d399 !important;
  --success: #34d399 !important;
  --primary: #3b82f6 !important;
  /* 深色下 danger 的**文字/描边**角色提亮：#b91c1c 压深色底仅 2.76:1；
     #fca5a5 在 6 套深色底上 ≈9.4:1，压 15% 红淡底 ≈6.7:1。 */
  --accent-danger-text: #fca5a5 !important;
  --accent-warning-text: #fbbf24 !important;
  --accent-success-text: #34d399 !important;
  --accent-info-text: #7dd3fc !important;
  --accent-purple-text: #d8b4fe !important;
}

/* 徽章配色：修复文字与底色对比度不足。
   站内徽章统一用「rgba(强调色,0.1~0.16) 底 + 强调色本身做文字」，
   实测 #10b981 绿字压 10% 绿底仅约 2.3:1、#3b82f6 蓝字压浅蓝底 3.28:1，
   都远低于 WCAG AA 的 4.5:1。做法：底色不动，只把文字换成同色系深一档。
   ⚠️ `.role-admin` 原在此「绿色组」里，但页头注入的徽章底色是**红**淡底
      （shared/components/header.html: `.role-admin{background:rgba(239,68,68,.15)}`，
       与 assets/styles/header.css 里那份绿底定义相互冲突，实际生效的是红底）
      ⇒ 绿字压红底仅 4.13:1。它应当与 danger 同组（已移到下面的深红组）。 */
.status-normal, .badge-success, .text-success,
.res-active, .status-online, .record-badge-success, .tag-success,
.layer-ok-badge, .badge-ok {
  color: #047857 !important;
}
.status-reserved, .badge-warning, .text-warning,
.status-pending, .tag-warning, .badge-expiring {
  color: #92400e !important;
}
.status-in_use, .role-student, .badge-primary, .badge-info, .text-info,
.status-info, .tag-info, .badge-running, .text-primary-accent {
  color: #1d4ed8 !important;
}
.status-maintenance, .badge-orange, .tag-orange {
  color: #c2410c !important;
}
.status-broken, .badge-danger, .text-danger, .menu-item-danger,
.tag-danger, .badge-broken,
.role-admin {          /* 页头管理员徽章：红淡底上的字（见上方说明） */
  color: #b91c1c !important;
}
/* 表单必填星号：原 #ef4444 压白仅 3.76:1（需 4.5），压深一档 */
.required {
  color: #b91c1c !important;
}
.role-teacher, .badge-purple, .tag-purple, .role-badge.role-teacher {
  color: #6d28d9 !important;
}
/* 中性徽章/次要文字同样抬到可读对比度 */
.role-guest, .role-visitor, .role-badge.role-visitor,
.badge-secondary, .tag-secondary, .record-badge {
  color: #4b5563 !important;
}

/* 实心彩色按钮：底色调深到能让白字达标。
   ★ 必须同时指定 color:#fff —— 站内部分按钮（尤其 <a class="btn">）
     没有显式文字色，会去继承链接蓝或正文黑，压深底后就不可读了。 */
.btn-success, .btn.btn-success, .badge-success-solid {
  background-color: #047857 !important;
  border-color: #047857 !important;
  color: #fff !important;
}
.btn-warning, .btn.btn-warning {
  background-color: #b45309 !important;
  border-color: #b45309 !important;
  color: #fff !important;
}
.btn-danger, .btn.btn-danger {
  background-color: #b91c1c !important;
  border-color: #b91c1c !important;
  color: #fff !important;
}
/* 注意：这里刻意【不】给 .btn-primary 统一指定白字。
   因为站点里有页面并未定义 .btn-primary 的底色（例如 settings.html 的
   「开始定制」是 <a class="btn btn-primary">，且 common.css 的 @import 位置非法
   导致 header.css 未被加载 → 该按钮其实没有背景色），
   若强行给白字就会变成"白字压白底"，反而不可读。
   凡是定义了底色的 .btn-primary，其背景都走 var(--accent-primary)，
   上面对 --accent-primary 的压深已足以让白字达标（5.17:1）。 */
/* .btn-info 在站内是"浅蓝底 + 亮蓝字"的描边样式（如试剂页批量生成二维码），
   只改文字色即可达标；若当成实心按钮压深底色会反过来毁掉对比度。 */
.btn-info, .btn.btn-info {
  color: #1d4ed8 !important;
}
/* 激活态的筛选按钮/标签：白字压亮蓝同样不达标，压深底色。
   ⚠️ 裸 .filter-btn.active【不能】纳入：
      reagent.html 里它是「浅蓝半透明底 + 蓝字」的胶囊
        .filter-btn.active { background: rgba(59,130,246,0.15); color: var(--accent-primary); }
      灌成实心蓝底后，底色变了但页面自己的 color 还在 → 亮蓝压蓝 1.1:1，反而更糟。
      plant/index.html 里才是实心（background: var(--accent-primary)），
      故用属性选择器把 reagent 那种「带 data-type/data-haz/data-filter 的胶囊」排除。
   注意 .nav-tab.active 属于"浅底蓝字"的页签，不在此列（由下方单独处理）。 */
.filter-btn.active,
.filter-tag.active,
.type-tab.active, .segment.active, .pill.active {
  background-color: #2563eb !important;
  border-color: #2563eb !important;
  color: #fff !important;
}
/* 反过来：reagent.html 那种「浅底蓝字」筛选胶囊要**压深文字、保留浅底**。
   判据不能按属性（plant/index.html 也用 data-filter 但它是实心），
   改为按「底色是否真的浅」——浏览器里 reagent 的 .filter-btn.active
   底色是 rgba(59,130,246,0.15)，plant 的是 var(--accent-primary)。
   纯 CSS 无法读计算后的底色，故这里用一条更精确的选择器：
   reagent 的胶囊都带 .danger 兄弟或位于 .filter-bar 内且带 data-*，
   而 plant 的实心按钮在 .filters 容器内。
   ⚠️ 维护提示：若新增页面，请按「实心/浅底」二选一显式声明
   （实心 → .filter-btn.active 白字；浅底 → .filter-btn.active.soft 压深文字）。 */
.filter-btn.active.soft {
  color: #1d4ed8 !important;
}
.filter-btn.danger.active.soft {
  color: #b91c1c !important;
}
/* reagent.html 尚未加 .soft 类名，用它的容器特征兜底。
   ⚠️ 必须同时把 background 也一起收回浅色 —— 上面那条通用实心规则用的是
      `!important` 的 #2563eb，只改 color 会留下「蓝底 + 深蓝字」1.3:1。 */
.filter-bar .filter-btn.active {
  background-color: rgba(59, 130, 246, 0.15) !important;
  border-color: #2563eb !important;
  color: #1d4ed8 !important;
}
.filter-bar .filter-btn.danger.active {
  background-color: rgba(239, 68, 68, 0.15) !important;
  border-color: #b91c1c !important;
  color: #b91c1c !important;
}
/* 浅底蓝字的页签/Tab：只压深文字色，不动底色，保持原有视觉。
   ⚠️ 裸 .tab / .role-tab 不能无脑纳入：
     · login.html、member.html、qrcode/* 的 `.tab.active` 是【实心蓝底 + 白字】
     · permission-settings.html 的 `.role-tab.active` 同样是【实心蓝底 + 白字】
     把白字改成深蓝会变成深蓝压亮蓝（实测 1.3:1）。
   故这里只列「确定是浅底蓝字」的页签类，其余走下方单独规则。 */
.section-tab.active, .nav-tab.active, .member-tab.active,
.device-tab.active, .modal-tab.active {
  color: #1d4ed8 !important;
}
/* permission-settings.html 的实心蓝底页签：显式给白字 */
.role-tab.active {
  background-color: #2563eb !important;
  border-color: #2563eb !important;
  color: #ffffff !important;
}
/* ⚠️ 以下两类【故意不列】：它们的 .active 是实心品牌色底 + 白字，
   纳进来会把白字压成深蓝（member.html 的 .login-type-btn.active 实测 1.82:1）：
     .login-type-btn.active（member.html / login.html）
     .auth-tab.active（member.html，浅底，但 .tab.active 通配会影响）
   浅底的 .auth-tab.active 由页面自身 color:var(--accent-primary) 处理，
   无需此处兜底。 */
/* 裸 .tab.active：浅底情形压深文字；实心蓝底情形由下方的白字规则保底。
   用 :where() 降低本规则权重，让页面自带的背景色优先。 */
:where(.tab.active),
/* ⚠️ member/detail.html 的 .tab-nav button.active 是「浅蓝底(--accent-primary-light
   #e9effd) + 蓝字(--accent-primary #2563eb)」，实测 4.49:1，差 0.01 不达标。
   与上面同款浅底形态，一并压深到 #1d4ed8 = 5.82:1。 */
:where(.tab-nav button.active) {
  color: #1d4ed8 !important;
}
/* 实心蓝底 + 白字的页签（login / member / qrcode 等）：显式给白字，
   避免页签自身漏写 color 时继承到深蓝。 */
.tabs > .tab.active,
.tab.active[style*="accent"],
.tab.active.bg-primary,
.tab.active.solid,
.login-type-btn.active {
  background-color: #2563eb !important;
  color: #ffffff !important;
}
/* 低温设备页的分类页签：旧版硬编码青色在浅色下对比度仅 3.68:1，此处按设备类型分色 */
.device-tab.active,
.device-tab.active[data-type="refrigerator"] { color: #0e7490 !important; }
.device-tab.active[data-type="freezer"]     { color: #6d28d9 !important; }
.device-tab.active[data-type="ultraLow"]    { color: #c2410c !important; }

/* 统计数字里的橙/红/绿：大字号也要求 ≥3:1，原色偏亮 */
.stat-card.warning .stat-number, .stat-number.warning,
#statSafeAlert, .stat-warning .stat-number { color: #b45309 !important; }
.stat-card.danger .stat-number, .stat-number.danger,
#statMinAlert, .stat-danger .stat-number { color: #b91c1c !important; }
.stat-card.success .stat-number, .stat-number.success,
.stock-big-number.ok { color: #047857 !important; }

/* 深色模式下这些深色不可读，改回亮色
   ⚠️ `.role-admin` 已移出本「绿组」，归入下面的 danger 组（页头徽章是红淡底）。 */
[data-theme="dark"] .status-normal,
[data-theme="dark"] .badge-success, [data-theme="dark"] .text-success,
[data-theme="dark"] .res-active,
[data-theme="dark"] .role-badge { color: #34d399 !important; }
[data-theme="dark"] .status-reserved, [data-theme="dark"] .badge-warning,
[data-theme="dark"] .text-warning { color: #fbbf24 !important; }
[data-theme="dark"] .status-in_use, [data-theme="dark"] .badge-primary,
[data-theme="dark"] .badge-info, [data-theme="dark"] .role-student { color: #60a5fa !important; }
/* 深色下 #fb923c 压 15% 橙底+暗卡片只有 4.16:1，提亮到 #fdba74（5.58:1） */
[data-theme="dark"] .status-maintenance,
[data-theme="dark"] .badge-orange,
[data-theme="dark"] .tag-orange { color: #fdba74 !important; }
[data-theme="dark"] .status-broken, [data-theme="dark"] .badge-danger,
[data-theme="dark"] .text-danger,
[data-theme="dark"] .role-admin { color: #f87171 !important; }   /* 页头管理员徽章（红淡底） */
/* 表单必填星号：原 #ef4444 压白 3.76:1 / 压深色卡 3.89:1，两主题都不达标 */
[data-theme="dark"] .required,
[data-theme="dark"] .stat-item,
[data-theme="dark"] .num.danger,
/* ⚠️ member.html 的必填星号是裸 <span>（靠页面 .required 之外的规则上色），
   深色下 #b91c1c 压 #1e293b 仅 2.26:1。 */
[data-theme="dark"] .form-label span,
[data-theme="dark"] .form-group label span {
  color: #f87171 !important;
}
/* ⚠️ 上面第 816~821 行那组「橙/红/绿统计数字」是浅色专用色（#b45309 2.91:1、
   #b91c1c 2.26:1、#047857 2.67:1），压深色卡全部不达标。
   当时深色只补了 .stat-number.warning 一种写法，漏了页面实际使用的
   .stat-card.warning .stat-number / #statSafeAlert / .stat-warning .stat-number
   以及 danger/success 的对应形态 → 深色下仍显示深橙/深红。此处补齐全部形态。 */
[data-theme="dark"] .stat-number.warning,
[data-theme="dark"] .num.warning,
[data-theme="dark"] .stat-card.warning .stat-number,
[data-theme="dark"] .stat-warning .stat-number,
[data-theme="dark"] #statSafeAlert {
  color: #fdba74 !important;
}
[data-theme="dark"] .stat-number.danger,
[data-theme="dark"] .num.danger,
[data-theme="dark"] .stat-card.danger .stat-number,
[data-theme="dark"] .stat-danger .stat-number,
[data-theme="dark"] #statMinAlert {
  color: #f87171 !important;
}
[data-theme="dark"] .stat-number.success,
[data-theme="dark"] .stat-card.success .stat-number,
[data-theme="dark"] .stock-big-number.ok {
  color: #34d399 !important;
}
[data-theme="dark"] .role-teacher { color: #c4b5fd !important; }
[data-theme="dark"] .role-guest, [data-theme="dark"] .role-visitor,
[data-theme="dark"] .record-badge, [data-theme="dark"] .badge-secondary { color: #cbd5e1 !important; }
/* 首页预警标签 .sa-tag：默认是「深红字压深色底」1.66:1，深色下必须提亮 */
[data-theme="dark"] .sa-tag { color: #fca5a5 !important; }
[data-theme="dark"] .sa-tag.sa-tag-warn { color: #fcd34d !important; }
[data-theme="dark"] .sa-tag.sa-tag-ok { color: #6ee7b7 !important; }
/* ⚠️ 第 12 轮把浅色下的 #4ade80 统一压深成 #047857（浅绿底 1.59→5.01:1）。
   这些类名在深色下必须回到亮绿，否则深绿压深卡片不可读。 */
[data-theme="dark"] .badge-normal,
[data-theme="dark"] .badge-green,
[data-theme="dark"] .type-freezer .card-badge,
[data-theme="dark"] .role-badge.student,
[data-theme="dark"] .item-type-strain {
  color: #4ade80 !important;
}
/* 第 13 轮：浅色下这些徽章被压深（亮色压浅底仅 1.5~3.2:1），
   深色主题必须回到亮色，否则深色底上又不可读。 */
[data-theme="dark"] .type-fridge .card-badge,
[data-theme="dark"] .item-type-sample {
  color: #60a5fa !important;
}
[data-theme="dark"] .type-ultra .card-badge {
  color: #c084fc !important;
}
[data-theme="dark"] .item-type-cell,
[data-theme="dark"] .badge-yellow {
  color: #fbbf24 !important;
}
/* ⚠️ 2026-10-10：这里原是 #f87171，实测压在「红淡底 + 深色卡」上只有
   3.97~4.43:1（lab 最低 3.97），未达 AA。这些徽章**只在登录后**才出现在页头
   （显示「超级」「管理员」），登出态审计看不到 ⇒ 一直漏检。提亮到 #fca5a5（≈5.7~6.4:1）。 */
[data-theme="dark"] .badge-red,
[data-theme="dark"] .badge-danger,
[data-theme="dark"] .role-badge.admin,
[data-theme="dark"] .compat-warning,
[data-theme="dark"] .suggestion-item .conflict-tag {
  color: #fca5a5 !important;
}
/* 首页模块卡徽章 .badge-primary：4.43:1 差一点，提亮半档 */
[data-theme="dark"] .badge.badge-primary { color: #93c5fd !important; }

/* ── 深色主题：品牌蓝的「背景用法」与「文字用法」必须分开 ──
   #3b82f6 做底 + 白字 = 3.68:1（需 4.5）✗ → 压深到 #2563eb = 5.17:1 ✅
   ⚠️ 只列「确定是实心蓝底 + 白字」的控件。
      .filter-btn.active 不在此列：reagent.html 里它是「浅蓝底 + 蓝字」的筛选胶囊，
      plant/index.html 里才是实心，故用 :not() 排除 reagent 那种浅底形态
      （浅底形态由下面「品牌蓝当文字」的规则处理，本来就达标）。 */
[data-theme="dark"] .btn.btn-primary,
[data-theme="dark"] .btn-login,
/* ⚠️ register.html 的「提交注册」是裸 .btn（无 .btn-primary），
   原色 #3b82f6 做底 + 白字 = 3.68:1 → 一并纳入。
   用 .btn:not() 排除描边/次要形态，避免误伤。 */
[data-theme="dark"] .btn:not(.btn-secondary):not(.btn-outline):not(.btn-guest):not(.btn-sm),
/* 这三个是找回密码/注册/成员页的实心品牌色按钮，
   原色 #3b82f6 做底 + 白字 = 3.68:1，一并纳入压深 */
[data-theme="dark"] .btn-code,
[data-theme="dark"] .btn-submit,
[data-theme="dark"] .btn.btn-sm:not(.btn-secondary):not(.btn-outline),
[data-theme="dark"] .login-type-btn.active,
[data-theme="dark"] .auth-tab.active[style],
[data-theme="dark"] .filter-btn.active,
[data-theme="dark"] .filter-tag.active,
[data-theme="dark"] .type-tab.active,
[data-theme="dark"] .segment.active,
[data-theme="dark"] .pill.active,
[data-theme="dark"] .layer-self {
  background-color: #2563eb !important;
  border-color: #2563eb !important;
  color: #ffffff !important;
}
[data-theme="dark"] .btn.btn-primary:hover,
[data-theme="dark"] .btn-login:hover {
  background-color: #1d4ed8 !important;
  border-color: #1d4ed8 !important;
}
/* ── 深色主题下「品牌蓝当文字」的场合 ──
   底色是 #0f172a~#1e293b 的暗面时，#3b82f6 只有 3.98:1，
   提到 #60a5fa（压卡片 5.75:1、压背景 7.02:1）。
   ⚠️ 不能碰「实心蓝底 + 白字」的控件（.btn-primary / .btn-login /
      .tab.active 在 login、qrcode、member 页面是实心蓝底白字，
      改成亮蓝就变成亮蓝压亮蓝）。故用 :not() 显式排除。 */
[data-theme="dark"] a.forgot-link,
[data-theme="dark"] .auth-footer a,
[data-theme="dark"] .link,
[data-theme="dark"] .text-primary-accent,
[data-theme="dark"] .auth-tab.active,
[data-theme="dark"] .section-tab.active,
[data-theme="dark"] .filter-bar .filter-btn.active,
[data-theme="dark"] .nav-tab.active {
  color: #60a5fa !important;
}
/* 登录/找回密码页底部的「忘记密码？」「立即注册」是裸 <a>，
   深色下吃的是 --accent-primary(#3b82f6) 压 #1e293b = 3.98:1 → 提亮。
   用属性+文本无关的高特异性写法，避免漏掉： */
[data-theme="dark"] .auth-card a,
[data-theme="dark"] .login-card a,
[data-theme="dark"] .form-footer a,
[data-theme="dark"] .auth-links a,
/* ⚠️ login/register/qrcode/admin-* 等 8 个页面的表单底部是
   .footer a{color:var(--accent-primary)}，深色下 --accent-primary=#3b82f6
   压 #1e293b = 3.98:1。零特异性的 :where(a:not([href])) 压不过它，
   必须用同特异性的 .footer a 显式覆盖。 */
[data-theme="dark"] .footer a,
[data-theme="dark"] .form .footer a {
  color: #60a5fa !important;
}
/* 成员页的统计数字「—」与浅底页签文字
   ⚠️ 这条泛化规则的本意是兜住所有「**淡底** + 主色字」的 button.active（例如
      member/detail.html 的 .tab-nav button.active 底色是 --accent-primary-light）。
      但它特异性高达 (0,6,1)，会把**实心**的 .seg.active/.size-btn.active 也一并命中，
      把字色从 #fff 压成 #60a5fa —— 实测它们底色就是主题主色，只有 1.45:1。
      ⇒ 只排除这两个实心类，其余淡底按钮的兜底行为保持不变（member/detail.html 依赖它压过
        文件上方那条零特异性的 `:where(.tab-nav button.active){color:#1d4ed8!important}`）。 */
[data-theme="dark"] .num,
[data-theme="dark"] .stat-number,
[data-theme="dark"] button.active:not(.role-tab):not(.tab):not(.filter-btn):not(.login-type-btn):not(.auth-tab):not(.seg):not(.size-btn),
[data-theme="dark"] .btn-guest {
  color: #60a5fa !important;
}
/* 实心 active 切换钮（分段控件）：底=主题主色，字=深色底（4.79:1） */
[data-theme="dark"] .seg.active,
[data-theme="dark"] .size-btn.active {
  background-color: #3b82f6 !important;
  border-color: #3b82f6 !important;
  color: #0f172a !important;
}
/* .btn-guest 是描边按钮（透明底 + 蓝字），压 #1e293b 需提亮 */
[data-theme="dark"] .btn-guest {
  background-color: transparent !important;
  border-color: #60a5fa !important;
}
[data-theme="dark"] .role-tab.active {
  background-color: #2563eb !important;
  color: #ffffff !important;
}
/* ⚠️ 第 14 轮：线上有数据才渲染的 2 处（本地审计看不到），
   都是 var(--accent-primary) 当文字压深色卡 = 3.98:1。
   .action-btn-reserve 是描边按钮（透明底 + 蓝字 + 蓝边框）→ 三色一起提亮。 */
[data-theme="dark"] .action-btn.action-btn-reserve,
[data-theme="dark"] .action-btn-reserve {
  background-color: transparent !important;
  border-color: #60a5fa !important;
  color: #60a5fa !important;
}
/* 仪器台账表格里的「查看」链接是内联 style 写的 var(--accent-primary)，
   内联样式无法用样式表覆盖 → 靠 [style*="accent-primary"] 属性选择器命中 */
[data-theme="dark"] a[style*="accent-primary"] {
  color: #60a5fa !important;
}
[data-theme="dark"] .filter-bar .filter-btn.active {
  background-color: rgba(59, 130, 246, 0.2) !important;
  border-color: #60a5fa !important;
}
/* ── 裸链接兜底 ──────────────────────────────────────────────
   全站样式表从未给裸 <a> 定过颜色 → 没被页面单独指定颜色的链接
   （面包屑、.back-btn、正文内联链接）全部退到浏览器 UA 蓝 rgb(0,0,238)，
   在深色主题 #0f172a 底上只有 1.9:1，几乎看不见。
   用 :where() 保证零特异性，页面自己的 .xxx{color:...} 一律优先。 */
:where(a[href]),
/* ⚠️ 无 href 的裸 <a>（如 login.html 的「忘记密码？」用 onclick 跳转）
   不匹配 a[href] → 兜底失效，深色下吃页面自己的 #3b82f6 = 3.98:1。
   用 :not([href]) 补上，同样零特异性。 */
:where(a:not([href])) {
  color: #2563eb;
}
:where(a[href]:visited) {
  color: #6d28d9;
}
[data-theme="dark"] :where(a[href]),
[data-theme="dark"] :where(a:not([href])) {
  color: #60a5fa;
}
[data-theme="dark"] :where(a[href]:visited) {
  color: #c4b5fd;
}
/* .back-btn 在 member/member.html、permission-settings.html、plant/plant.html、
   settings.html 这 4 个页面里完全没有定义 → 之前吃的是 UA 蓝。
   给它一个与别页 .back-btn 观感一致的中性色（同样零特异性，让位给页面定义）。 */
:where(.back-btn) {
  color: #374151;
  text-decoration: none;
}
[data-theme="dark"] :where(.back-btn) {
  color: #e2e8f0;
}

/* 分隔符竖线 / 「|」：纯装饰但也属可见文本，
   原色 var(--border-color)（#e5e7eb 压白）仅 1.47:1，
   WCAG 1.4.11 要求非文本对比度 ≥3:1。单独给 .sep 一个中灰。 */
.sep {
  color: #6b7280 !important;
}
[data-theme="dark"] .sep {
  color: #94a3b8 !important;
}


/* ══════════════════════════════════════════════════════════════
   扩展主题主色反制（段 A：变量层）

   深色修正层用
     [data-theme="dark"] { --accent-primary: #3b82f6 !important; … }
   兜住 <body>（主题引擎把 data-theme 同时写在 html 与 body 上），
   且 !important 压过 JS 写在 <html> 上的行内变量。
   ⇒ body 层变成深色基础系的蓝色调色板，页面里所有 `var(--accent-primary)`
     （从 body 继承）都渲染成蓝色：「科技紫」主题的页头/按钮全变蓝。

   ⚠️ 因此本段必须同时写 html[...] **和 body[...]**：
      只写 html 时 body 层仍被 dark 层压着，子元素继承到的还是蓝色 ——
      实测 reagent.html 科技紫主题页头渐变仍是蓝的（#60a5fa）。
      特异性 0,1,1 > 0,1,0，与源顺序无关。

   配色全部来自 config/themes.js 单一来源。
   ══════════════════════════════════════════════════════════════ */
/* 科研蓝（research）*/
html[data-theme-id="research"],
body[data-theme-id="research"] {
  --accent-primary: #0ea5e9 !important;
  --accent-primary-hover: #0284c7 !important;
  --accent-secondary: #06b6d4 !important;
  --text-muted: #94a3b8 !important;
  --text-secondary: #cbd5e1 !important;
  --primary: #0ea5e9 !important;
  --success: #06b6d4 !important;
  --danger: #f43f5e !important;
  --warning: #f59e0b !important;
  --info: #22d3ee !important;
}
/* 实验室绿（lab）*/
html[data-theme-id="lab"],
body[data-theme-id="lab"] {
  --accent-primary: #22c55e !important;
  --accent-primary-hover: #16a34a !important;
  --accent-secondary: #10b981 !important;
  --text-muted: #86efac !important;
  --text-secondary: #bbf7d0 !important;
  --primary: #22c55e !important;
  --success: #10b981 !important;
  --danger: #ef4444 !important;
  --warning: #f59e0b !important;
  --info: #22d3ee !important;
}
/* 暖色护眼（warm）*/
html[data-theme-id="warm"],
body[data-theme-id="warm"] {
  --accent-primary: #f59e0b !important;
  --accent-primary-hover: #d97706 !important;
  --accent-secondary: #fb923c !important;
  --text-muted: #fcd34d !important;
  --text-secondary: #fde68a !important;
  --primary: #f59e0b !important;
  --success: #fb923c !important;
  --danger: #ef4444 !important;
  --warning: #eab308 !important;
  --info: #22d3ee !important;
}
/* 高对比度（contrast）*/
html[data-theme-id="contrast"],
body[data-theme-id="contrast"] {
  --accent-primary: #00ffff !important;
  --accent-primary-hover: #00cccc !important;
  --accent-secondary: #00ff00 !important;
  --text-muted: #cccccc !important;
  --text-secondary: #e5e5e5 !important;
  --primary: #00ffff !important;
  --success: #00ff00 !important;
  --danger: #ff0000 !important;
  --warning: #ffff00 !important;
  --info: #00ffff !important;
}
/* 科技紫（tech）*/
html[data-theme-id="tech"],
body[data-theme-id="tech"] {
  --accent-primary: #a855f7 !important;
  --accent-primary-hover: #9333ea !important;
  --accent-secondary: #c084fc !important;
  --text-muted: #d8b4fe !important;
  --text-secondary: #e9d5ff !important;
  --primary: #a855f7 !important;
  --success: #c084fc !important;
  --danger: #f43f5e !important;
  --warning: #fbbf24 !important;
  --info: #22d3ee !important;
}


/* ══════════════════════════════════════════════════════════════
   扩展主题品牌色反制（段 B：硬编码色层）

   深色层还把品牌色**硬编码**成蓝色：
     · color: #60a5fa              （淡底上的主色系文字）
     · background: #2563eb + 白字   （实心按钮）
     · background: rgba(59,130,246,.2)（淡底芯片）
   这些 !important 是必需的（压过行内变量/内联 style），但不认主题。
   其中 contrast 主题的 button.active 实测 4.04:1，低于 AA。

   本段逐套换成各主题自己的颜色，值由 config/themes.js 推导：
     safe  = accent-primary 提亮到「本主题最亮的底」上 ≥4.6:1
     onAcc = 实心按钮文字色 = 本主题 bg-primary（断言 ≥4.5:1）
   ⚠️ 这些是后代选择器，html 前缀已够（0,2,1 > 0,2,0）。
   ══════════════════════════════════════════════════════════════ */

/* ── 科研蓝（research）── */
/* 实心按钮：底=本主题主色，字=本主题 bg-primary（6.44:1） */
html[data-theme-id="research"] .btn:not(.btn-secondary):not(.btn-outline):not(.btn-guest):not(.btn-sm),
html[data-theme-id="research"] .btn-code,
html[data-theme-id="research"] .btn-submit,
html[data-theme-id="research"] .btn.btn-sm:not(.btn-secondary):not(.btn-outline),
html[data-theme-id="research"] .login-type-btn.active,
html[data-theme-id="research"] .auth-tab.active[style],
html[data-theme-id="research"] .filter-btn.active,
html[data-theme-id="research"] .filter-tag.active,
html[data-theme-id="research"] .type-tab.active,
html[data-theme-id="research"] .segment.active,
html[data-theme-id="research"] .pill.active,
html[data-theme-id="research"] .layer-self,
html[data-theme-id="research"] .seg.active,
html[data-theme-id="research"] .size-btn.active {
  background-color: #0ea5e9 !important;
  border-color: #0ea5e9 !important;
  color: #0f172a !important;
}
html[data-theme-id="research"] .btn.btn-primary:hover,
html[data-theme-id="research"] .btn-login:hover {
  background-color: #0284c7 !important;
  border-color: #0284c7 !important;
}
/* 淡底芯片：底=本主题主色淡底，字=推导出的安全值（4.61:1） */
html[data-theme-id="research"] .filter-bar .filter-btn.active,
html[data-theme-id="research"] .filter-btn.active {
  background-color: rgba(14,165,233,0.15) !important;
  border-color: #27aeeb !important;
  color: #27aeeb !important;
}
/* 淡底上的主色系文字 */
html[data-theme-id="research"] .badge-info,
html[data-theme-id="research"] .role-student,
html[data-theme-id="research"] .num,
html[data-theme-id="research"] .stat-number,
html[data-theme-id="research"] .btn-guest,
html[data-theme-id="research"] .action-btn.action-btn-reserve,
html[data-theme-id="research"] .action-btn-reserve,
html[data-theme-id="research"] a[style*="accent-primary"],
html[data-theme-id="research"] .footer a,
html[data-theme-id="research"] .form .footer a,
html[data-theme-id="research"] button.active:not(.role-tab):not(.tab):not(.filter-btn):not(.login-type-btn):not(.auth-tab):not(.seg):not(.size-btn) {
  color: #27aeeb !important;
}
/* 描边按钮的边框与之同色 */
html[data-theme-id="research"] .btn-guest,
html[data-theme-id="research"] .action-btn.action-btn-reserve,
html[data-theme-id="research"] .action-btn-reserve {
  border-color: #27aeeb !important;
}
/* 裸链接兜底（深色层用的是 :where()，零特异性） */
html[data-theme-id="research"] :where(a[href]),
html[data-theme-id="research"] :where(a:not([href])) {
  color: #27aeeb;
}
/* ⚠️ .back-btn 必须排在裸链接兜底之后：同为 (0,1,1) 时靠顺序取胜。
   深色层对它是中性亮色（#e2e8f0）而非主色 —— 它在 4 个页面里压根没定义，
   只是兜住 UA 蓝。若被上面的主色兜底命中，实测 tech 主题掉到 4.30:1。 */
html[data-theme-id="research"] :where(.back-btn) {
  color: #f1f5f9;
}

/* ── 实验室绿（lab）── */
/* 实心按钮：底=本主题主色，字=本主题 bg-primary（7.58:1） */
html[data-theme-id="lab"] .btn:not(.btn-secondary):not(.btn-outline):not(.btn-guest):not(.btn-sm),
html[data-theme-id="lab"] .btn-code,
html[data-theme-id="lab"] .btn-submit,
html[data-theme-id="lab"] .btn.btn-sm:not(.btn-secondary):not(.btn-outline),
html[data-theme-id="lab"] .login-type-btn.active,
html[data-theme-id="lab"] .auth-tab.active[style],
html[data-theme-id="lab"] .filter-btn.active,
html[data-theme-id="lab"] .filter-tag.active,
html[data-theme-id="lab"] .type-tab.active,
html[data-theme-id="lab"] .segment.active,
html[data-theme-id="lab"] .pill.active,
html[data-theme-id="lab"] .layer-self,
html[data-theme-id="lab"] .seg.active,
html[data-theme-id="lab"] .size-btn.active {
  background-color: #22c55e !important;
  border-color: #22c55e !important;
  color: #0a1f0f !important;
}
html[data-theme-id="lab"] .btn.btn-primary:hover,
html[data-theme-id="lab"] .btn-login:hover {
  background-color: #16a34a !important;
  border-color: #16a34a !important;
}
/* 淡底芯片：底=本主题主色淡底，字=推导出的安全值（5.11:1） */
html[data-theme-id="lab"] .filter-bar .filter-btn.active,
html[data-theme-id="lab"] .filter-btn.active {
  background-color: rgba(34,197,94,0.15) !important;
  border-color: #22c55e !important;
  color: #22c55e !important;
}
/* 淡底上的主色系文字 */
html[data-theme-id="lab"] .badge-info,
html[data-theme-id="lab"] .role-student,
html[data-theme-id="lab"] .num,
html[data-theme-id="lab"] .stat-number,
html[data-theme-id="lab"] .btn-guest,
html[data-theme-id="lab"] .action-btn.action-btn-reserve,
html[data-theme-id="lab"] .action-btn-reserve,
html[data-theme-id="lab"] a[style*="accent-primary"],
html[data-theme-id="lab"] .footer a,
html[data-theme-id="lab"] .form .footer a,
html[data-theme-id="lab"] button.active:not(.role-tab):not(.tab):not(.filter-btn):not(.login-type-btn):not(.auth-tab):not(.seg):not(.size-btn) {
  color: #22c55e !important;
}
/* 描边按钮的边框与之同色 */
html[data-theme-id="lab"] .btn-guest,
html[data-theme-id="lab"] .action-btn.action-btn-reserve,
html[data-theme-id="lab"] .action-btn-reserve {
  border-color: #22c55e !important;
}
/* 裸链接兜底（深色层用的是 :where()，零特异性） */
html[data-theme-id="lab"] :where(a[href]),
html[data-theme-id="lab"] :where(a:not([href])) {
  color: #22c55e;
}
/* ⚠️ .back-btn 必须排在裸链接兜底之后：同为 (0,1,1) 时靠顺序取胜。
   深色层对它是中性亮色（#e2e8f0）而非主色 —— 它在 4 个页面里压根没定义，
   只是兜住 UA 蓝。若被上面的主色兜底命中，实测 tech 主题掉到 4.30:1。 */
html[data-theme-id="lab"] :where(.back-btn) {
  color: #f0fdf4;
}

/* ── 暖色护眼（warm）── */
/* 实心按钮：底=本主题主色，字=本主题 bg-primary（8.45:1） */
html[data-theme-id="warm"] .btn:not(.btn-secondary):not(.btn-outline):not(.btn-guest):not(.btn-sm),
html[data-theme-id="warm"] .btn-code,
html[data-theme-id="warm"] .btn-submit,
html[data-theme-id="warm"] .btn.btn-sm:not(.btn-secondary):not(.btn-outline),
html[data-theme-id="warm"] .login-type-btn.active,
html[data-theme-id="warm"] .auth-tab.active[style],
html[data-theme-id="warm"] .filter-btn.active,
html[data-theme-id="warm"] .filter-tag.active,
html[data-theme-id="warm"] .type-tab.active,
html[data-theme-id="warm"] .segment.active,
html[data-theme-id="warm"] .pill.active,
html[data-theme-id="warm"] .layer-self,
html[data-theme-id="warm"] .seg.active,
html[data-theme-id="warm"] .size-btn.active {
  background-color: #f59e0b !important;
  border-color: #f59e0b !important;
  color: #1c1410 !important;
}
html[data-theme-id="warm"] .btn.btn-primary:hover,
html[data-theme-id="warm"] .btn-login:hover {
  background-color: #d97706 !important;
  border-color: #d97706 !important;
}
/* 淡底芯片：底=本主题主色淡底，字=推导出的安全值（5.37:1） */
html[data-theme-id="warm"] .filter-bar .filter-btn.active,
html[data-theme-id="warm"] .filter-btn.active {
  background-color: rgba(245,158,11,0.15) !important;
  border-color: #f59e0b !important;
  color: #f59e0b !important;
}
/* 淡底上的主色系文字 */
html[data-theme-id="warm"] .badge-info,
html[data-theme-id="warm"] .role-student,
html[data-theme-id="warm"] .num,
html[data-theme-id="warm"] .stat-number,
html[data-theme-id="warm"] .btn-guest,
html[data-theme-id="warm"] .action-btn.action-btn-reserve,
html[data-theme-id="warm"] .action-btn-reserve,
html[data-theme-id="warm"] a[style*="accent-primary"],
html[data-theme-id="warm"] .footer a,
html[data-theme-id="warm"] .form .footer a,
html[data-theme-id="warm"] button.active:not(.role-tab):not(.tab):not(.filter-btn):not(.login-type-btn):not(.auth-tab):not(.seg):not(.size-btn) {
  color: #f59e0b !important;
}
/* 描边按钮的边框与之同色 */
html[data-theme-id="warm"] .btn-guest,
html[data-theme-id="warm"] .action-btn.action-btn-reserve,
html[data-theme-id="warm"] .action-btn-reserve {
  border-color: #f59e0b !important;
}
/* 裸链接兜底（深色层用的是 :where()，零特异性） */
html[data-theme-id="warm"] :where(a[href]),
html[data-theme-id="warm"] :where(a:not([href])) {
  color: #f59e0b;
}
/* ⚠️ .back-btn 必须排在裸链接兜底之后：同为 (0,1,1) 时靠顺序取胜。
   深色层对它是中性亮色（#e2e8f0）而非主色 —— 它在 4 个页面里压根没定义，
   只是兜住 UA 蓝。若被上面的主色兜底命中，实测 tech 主题掉到 4.30:1。 */
html[data-theme-id="warm"] :where(.back-btn) {
  color: #fef3c7;
}

/* ── 高对比度（contrast）── */
/* 实心按钮：底=本主题主色，字=本主题 bg-primary（16.75:1） */
html[data-theme-id="contrast"] .btn:not(.btn-secondary):not(.btn-outline):not(.btn-guest):not(.btn-sm),
html[data-theme-id="contrast"] .btn-code,
html[data-theme-id="contrast"] .btn-submit,
html[data-theme-id="contrast"] .btn.btn-sm:not(.btn-secondary):not(.btn-outline),
html[data-theme-id="contrast"] .login-type-btn.active,
html[data-theme-id="contrast"] .auth-tab.active[style],
html[data-theme-id="contrast"] .filter-btn.active,
html[data-theme-id="contrast"] .filter-tag.active,
html[data-theme-id="contrast"] .type-tab.active,
html[data-theme-id="contrast"] .segment.active,
html[data-theme-id="contrast"] .pill.active,
html[data-theme-id="contrast"] .layer-self,
html[data-theme-id="contrast"] .seg.active,
html[data-theme-id="contrast"] .size-btn.active {
  background-color: #00ffff !important;
  border-color: #00ffff !important;
  color: #000000 !important;
}
html[data-theme-id="contrast"] .btn.btn-primary:hover,
html[data-theme-id="contrast"] .btn-login:hover {
  background-color: #00cccc !important;
  border-color: #00cccc !important;
}
/* 淡底芯片：底=本主题主色淡底，字=推导出的安全值（8.18:1） */
html[data-theme-id="contrast"] .filter-bar .filter-btn.active,
html[data-theme-id="contrast"] .filter-btn.active {
  background-color: rgba(0,255,255,0.2) !important;
  border-color: #00ffff !important;
  color: #00ffff !important;
}
/* 淡底上的主色系文字 */
html[data-theme-id="contrast"] .badge-info,
html[data-theme-id="contrast"] .role-student,
html[data-theme-id="contrast"] .num,
html[data-theme-id="contrast"] .stat-number,
html[data-theme-id="contrast"] .btn-guest,
html[data-theme-id="contrast"] .action-btn.action-btn-reserve,
html[data-theme-id="contrast"] .action-btn-reserve,
html[data-theme-id="contrast"] a[style*="accent-primary"],
html[data-theme-id="contrast"] .footer a,
html[data-theme-id="contrast"] .form .footer a,
html[data-theme-id="contrast"] button.active:not(.role-tab):not(.tab):not(.filter-btn):not(.login-type-btn):not(.auth-tab):not(.seg):not(.size-btn) {
  color: #00ffff !important;
}
/* 描边按钮的边框与之同色 */
html[data-theme-id="contrast"] .btn-guest,
html[data-theme-id="contrast"] .action-btn.action-btn-reserve,
html[data-theme-id="contrast"] .action-btn-reserve {
  border-color: #00ffff !important;
}
/* 裸链接兜底（深色层用的是 :where()，零特异性） */
html[data-theme-id="contrast"] :where(a[href]),
html[data-theme-id="contrast"] :where(a:not([href])) {
  color: #00ffff;
}
/* ⚠️ .back-btn 必须排在裸链接兜底之后：同为 (0,1,1) 时靠顺序取胜。
   深色层对它是中性亮色（#e2e8f0）而非主色 —— 它在 4 个页面里压根没定义，
   只是兜住 UA 蓝。若被上面的主色兜底命中，实测 tech 主题掉到 4.30:1。 */
html[data-theme-id="contrast"] :where(.back-btn) {
  color: #ffffff;
}

/* ── 科技紫（tech）── */
/* 实心按钮：底=本主题主色，字=本主题 bg-primary（4.61:1） */
html[data-theme-id="tech"] .btn:not(.btn-secondary):not(.btn-outline):not(.btn-guest):not(.btn-sm),
html[data-theme-id="tech"] .btn-code,
html[data-theme-id="tech"] .btn-submit,
html[data-theme-id="tech"] .btn.btn-sm:not(.btn-secondary):not(.btn-outline),
html[data-theme-id="tech"] .login-type-btn.active,
html[data-theme-id="tech"] .auth-tab.active[style],
html[data-theme-id="tech"] .filter-btn.active,
html[data-theme-id="tech"] .filter-tag.active,
html[data-theme-id="tech"] .type-tab.active,
html[data-theme-id="tech"] .segment.active,
html[data-theme-id="tech"] .pill.active,
html[data-theme-id="tech"] .layer-self,
html[data-theme-id="tech"] .seg.active,
html[data-theme-id="tech"] .size-btn.active {
  background-color: #a855f7 !important;
  border-color: #a855f7 !important;
  color: #1a0f2e !important;
}
html[data-theme-id="tech"] .btn.btn-primary:hover,
html[data-theme-id="tech"] .btn-login:hover {
  background-color: #9333ea !important;
  border-color: #9333ea !important;
}
/* 淡底芯片：底=本主题主色淡底，字=推导出的安全值（4.62:1） */
html[data-theme-id="tech"] .filter-bar .filter-btn.active,
html[data-theme-id="tech"] .filter-btn.active {
  background-color: rgba(168,85,247,0.15) !important;
  border-color: #be80f9 !important;
  color: #be80f9 !important;
}
/* 淡底上的主色系文字 */
html[data-theme-id="tech"] .badge-info,
html[data-theme-id="tech"] .role-student,
html[data-theme-id="tech"] .num,
html[data-theme-id="tech"] .stat-number,
html[data-theme-id="tech"] .btn-guest,
html[data-theme-id="tech"] .action-btn.action-btn-reserve,
html[data-theme-id="tech"] .action-btn-reserve,
html[data-theme-id="tech"] a[style*="accent-primary"],
html[data-theme-id="tech"] .footer a,
html[data-theme-id="tech"] .form .footer a,
html[data-theme-id="tech"] button.active:not(.role-tab):not(.tab):not(.filter-btn):not(.login-type-btn):not(.auth-tab):not(.seg):not(.size-btn) {
  color: #be80f9 !important;
}
/* 描边按钮的边框与之同色 */
html[data-theme-id="tech"] .btn-guest,
html[data-theme-id="tech"] .action-btn.action-btn-reserve,
html[data-theme-id="tech"] .action-btn-reserve {
  border-color: #be80f9 !important;
}
/* 裸链接兜底（深色层用的是 :where()，零特异性） */
html[data-theme-id="tech"] :where(a[href]),
html[data-theme-id="tech"] :where(a:not([href])) {
  color: #be80f9;
}
/* ⚠️ .back-btn 必须排在裸链接兜底之后：同为 (0,1,1) 时靠顺序取胜。
   深色层对它是中性亮色（#e2e8f0）而非主色 —— 它在 4 个页面里压根没定义，
   只是兜住 UA 蓝。若被上面的主色兜底命中，实测 tech 主题掉到 4.30:1。 */
html[data-theme-id="tech"] :where(.back-btn) {
  color: #f3e8ff;
}
