/* =============================================================================
   福宝数字员工 · 统一 UI 设计系统
   文件：public/assets/ui.css
   版本：v1.0（Phase 1 视觉方向）
   -----------------------------------------------------------------------------
   【定位】浅色、克制、面向零技术背景用户；统一左侧竖向主导航 + 统一组件。
   【依赖】零依赖：无 CDN、无外部字体、无图标库、无 JS 框架，file:// 离线可用。
   【作用域】.de-* 为全新命名空间；所有对旧类名的覆盖一律以 .de-main 为前缀，
             绝不在全局改写 .card/.btn/.row/...，未改造页面不会受影响。
   【引入顺序】<link rel="stylesheet" href="assets/ui.css"> 请放在页面内联 <style> 之后。
            基础 7 色写在 html{}（0,0,1）而非 :root{}（0,1,0），因此品牌登录页
            在页面里写 :root{--primary:<品牌色>} 时必定生效，与 <link>/<style> 先后无关。
   【DOM 约定】见文件末尾「附录 A」与 docs/UI视觉规范.html。
   【配色铁律】白字仅用于蓝底/橙底/红底徽章、主按钮、品牌标记等反白场景；
             头像一律浅色渐变 + 深色字（见第 13 节），不用白字；
             浅底一律配深字。禁止深色主题。
   ============================================================================= */

/* =============================================================================
   1. 设计令牌 Design Tokens
   ============================================================================= */
/* 用 html{}（类型选择器 0,0,1）声明全部令牌，而不是 :root{}（伪类 0,1,0）。
   原因：index.php / brand/index.php 会在页面内联 <style> 里写 :root{--primary:品牌色}，
   而 ui.css 由 deUiCss() 以 <link> 形式排在后面。若这里也用 :root，同特异性下
   「后来者胜」会把品牌主题色覆盖回默认蓝。改用低一档的 html{} 后，页面级 :root
   无论在前在后都能稳定覆盖，且未覆盖的页面仍拿到下面这套完整的浅色默认值。 */
html {
  /* ---- 1.1 用户硬性偏好（原样沿用，不得改深色） ----
     --bg      #f6f8fb  hsl(216, 38%, 97%)
     --card    #ffffff  hsl(  0,  0%,100%)
     --primary #2563eb  hsl(221, 83%, 53%)
     --accent  #d97706  hsl( 32, 95%, 44%)
     --text    #1e293b  hsl(217, 33%, 17%)
     --muted   #64748b  hsl(215, 16%, 47%)
     --border  #e2e8f0  hsl(214, 32%, 91%)                              */
  --bg: #f6f8fb;
  --card: #ffffff;
  --primary: #2563eb;
  --accent: #d97706;
  --text: #1e293b;
  --muted: #64748b;
  --border: #e2e8f0;

  /* ---- 1.2 主色 / 强调色派生 ---- */
  --primary-hover: #1d4ed8;      /* hsl(224,76%,48%) 白字对比 6.3:1 */
  --primary-active: #1e40af;     /* hsl(224,64%,40%) */
  --primary-50: #eff6ff;         /* 选中态浅蓝底 */
  --primary-100: #dbeafe;
  --primary-200: #bfdbfe;
  --primary-ink: #1e40af;        /* 浅蓝底上的深蓝字 */
  --accent-hover: #b45309;
  --accent-50: #fffbeb;
  --accent-ink: #92400e;

  /* ---- 1.3 中性表面 ---- */
  --surface: #ffffff;
  --surface-soft: #f8fafc;       /* 表头 / 代码块 / hover 行 */
  --surface-sunken: #f1f5f9;    /* 输入框禁用 / 图标底 / 进度槽 */
  --text-strong: #0f172a;
  --muted-strong: #475569;       /* 小字号正文请用它：在 --bg 上 6.9:1 */
  --placeholder: #94a3b8;
  --border-strong: #cbd5e1;

  /* ---- 1.4 语义色：浅底 + 深字 + 浅边框 + 实色 + 深实色 ----
     对比度（浅底/深字）：success 7.4:1、warn 6.8:1、danger 7.6:1、
                        info 8.6:1、neutral 6.9:1，全部高于 WCAG AA 4.5:1 */
  --success-bg: #ecfdf5;  --success-ink: #065f46;  --success-border: #a7f3d0;  --success: #059669;  --success-strong: #047857;
  --warn-bg: #fffbeb;     --warn-ink: #92400e;     --warn-border: #fde68a;     --warn: #d97706;     --warn-strong: #b45309;
  --danger-bg: #fef2f2;   --danger-ink: #991b1b;   --danger-border: #fecaca;   --danger: #dc2626;   --danger-strong: #b91c1c;
  --info-bg: #eff6ff;     --info-ink: #1e40af;     --info-border: #bfdbfe;     --info: #2563eb;     --info-strong: #1d4ed8;
  --neutral-bg: #f1f5f9;  --neutral-ink: #475569;  --neutral-border: #e2e8f0;  --neutral: #64748b;  --neutral-strong: #334155;

  /* ---- 1.5 间距阶梯（4 / 8 / 12 / 16 / 20 / 24 / 32，另附扩展档） ---- */
  --sp-1: 4px;
  --sp-2: 8px;
  --sp-3: 12px;
  --sp-4: 16px;
  --sp-5: 20px;
  --sp-6: 24px;
  --sp-7: 32px;
  --sp-8: 40px;   /* 扩展 */
  --sp-9: 48px;   /* 扩展 */

  /* ---- 1.6 圆角（禁止 >16px，避免「玩具感」） ---- */
  --r-xs: 4px;
  --r-sm: 6px;
  --r-md: 8px;
  --r-lg: 12px;
  --r-xl: 16px;
  --r-pill: 999px;

  /* ---- 1.7 阴影 / 焦点环 ---- */
  --sh-1: 0 1px 2px rgba(15, 23, 42, .04);
  --sh-2: 0 2px 8px rgba(15, 23, 42, .06);
  --sh-3: 0 8px 24px rgba(15, 23, 42, .08);
  --ring: 0 0 0 3px rgba(37, 99, 235, .20);
  --ring-danger: 0 0 0 3px rgba(220, 38, 38, .18);

  /* ---- 1.8 字号层级（12/13/14/16/18/22/28）+ 固定行高 ---- */
  --fs-12: 12px;  --lh-12: 18px;
  --fs-13: 13px;  --lh-13: 20px;
  --fs-14: 14px;  --lh-14: 22px;   /* 正文基准 */
  --fs-16: 16px;  --lh-16: 24px;
  --fs-18: 18px;  --lh-18: 26px;
  --fs-22: 22px;  --lh-22: 30px;
  --fs-28: 28px;  --lh-28: 36px;

  /* ---- 1.9 字重（只用 4 档，不堆砌） ---- */
  --fw-regular: 400;
  --fw-medium: 500;
  --fw-semibold: 600;
  --fw-bold: 700;

  /* ---- 1.10 字体 / 过渡 / 尺寸 ---- */
  --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
  --font-symbol: "Segoe UI Symbol", "Apple Symbols", "Noto Sans Symbols 2", var(--font-sans);
  --t-fast: 120ms ease;
  --t: 180ms ease;
  --sidebar-w: 232px;
  --content-max: 1180px;
}

/* =============================================================================
   2. 基座（仅作用于 .de-layout，不污染全局）
   ============================================================================= */
.de-layout,
.de-layout *,
.de-layout *::before,
.de-layout *::after { box-sizing: border-box; }

.de-layout {
  display: flex;
  align-items: stretch;
  min-height: 100vh;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-sans);
  font-size: var(--fs-14);
  line-height: var(--lh-14);
  -webkit-font-smoothing: antialiased;
}

.de-layout :focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
  border-radius: var(--r-xs);
}

.de-sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* 跳到主要内容：public/ui.php 的 deUiStart() 会输出
   <a class="de-skip de-sr-only" href="#de-content">，位于 .de-layout 之外。
   平时视觉隐藏，键盘聚焦时变为左上角可见的蓝底白字链接（反白场景允许）。 */
.de-skip:focus,
.de-skip:focus-visible {
  position: fixed;
  top: var(--sp-3); left: var(--sp-3);
  z-index: 100;
  width: auto; height: auto;
  margin: 0; padding: var(--sp-2) var(--sp-4);
  clip: auto; overflow: visible; white-space: nowrap;
  background: var(--primary);
  color: #fff;
  border-radius: var(--r-md);
  box-shadow: var(--sh-2);
  font-size: var(--fs-13);
  font-weight: var(--fw-semibold);
  line-height: var(--lh-13);
  text-decoration: none;
  outline: 2px solid var(--primary-active);
  outline-offset: 2px;
}

/* =============================================================================
   3. 左侧竖向主导航
   ============================================================================= */
.de-sidebar {
  flex: 0 0 var(--sidebar-w);
  width: var(--sidebar-w);
  position: sticky;
  top: 0;
  align-self: flex-start;
  height: 100vh;
  display: flex;
  flex-direction: column;
  background: var(--card);
  border-right: 1px solid var(--border);
  z-index: 20;
}

/* 3.1 品牌区 */
.de-brand {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 18px 18px 16px;
  border-bottom: 1px solid var(--border);
  color: var(--text);
  text-decoration: none;
}
.de-brand:hover { text-decoration: none; }
.de-brand__mark {
  flex: 0 0 34px;
  width: 34px; height: 34px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 10px;
  background: var(--primary);
  color: #fff;                       /* 反白场景：蓝底 */
  font-size: var(--fs-16);
  font-weight: var(--fw-bold);
  line-height: 1;
  box-shadow: 0 2px 6px rgba(37, 99, 235, .25);
}
.de-brand__name {
  display: flex; flex-direction: column; min-width: 0;
  font-size: 15px; font-weight: var(--fw-bold); line-height: 1.25;
  color: var(--text-strong);
}
.de-brand__name small {
  margin-top: 2px;
  font-size: 11px; font-weight: var(--fw-regular);
  color: var(--muted); letter-spacing: .02em;
}
/* 身份徽标样式见 4.3（.de-page-head__side），已在页头统一实现 */

/* 3.2 可滚动导航区 */
.de-sidebar__scroll {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  padding: var(--sp-2) 0 var(--sp-4);
  scrollbar-width: thin;
  scrollbar-color: var(--border-strong) transparent;
}
.de-sidebar__scroll::-webkit-scrollbar { width: 6px; }
.de-sidebar__scroll::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: var(--r-pill); }

.de-nav-group { padding: var(--sp-2) 0 var(--sp-1); }
.de-nav-group + .de-nav-group { border-top: 1px solid var(--border); margin-top: var(--sp-2); padding-top: var(--sp-3); }
.de-nav-group__title {
  padding: var(--sp-2) 20px 6px;
  font-size: var(--fs-12);
  line-height: var(--lh-12);
  font-weight: var(--fw-semibold);
  color: var(--placeholder);
  letter-spacing: .08em;
  white-space: nowrap;
}
.de-nav { display: flex; flex-direction: column; }

/* 3.3 导航项：默认 / hover / 选中 / 焦点 */
.de-nav__item {
  position: relative;
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 38px;
  margin: 2px 10px;
  padding: var(--sp-2) 10px var(--sp-2) var(--sp-3);
  border-radius: var(--r-md);
  color: var(--muted-strong);
  font-size: var(--fs-14);
  line-height: var(--lh-14);
  font-weight: var(--fw-medium);
  text-decoration: none;
  transition: background var(--t-fast), color var(--t-fast);
}
.de-nav__item::before {
  content: "";
  position: absolute;
  left: -10px; top: 6px; bottom: 6px;
  width: 3px;
  border-radius: 0 3px 3px 0;
  background: transparent;
  transition: background var(--t-fast);
}
.de-nav__item:hover {
  background: var(--surface-soft);
  color: var(--text-strong);
  text-decoration: none;
}
.de-nav__item:hover .de-nav__ico { background: var(--primary-50); color: var(--primary); }
.de-nav__item.is-active,
.de-nav__item[aria-current="page"] {
  background: var(--primary-50);       /* 浅蓝底 */
  color: var(--primary-ink);           /* 主色文字 */
  font-weight: var(--fw-semibold);
}
.de-nav__item.is-active::before,
.de-nav__item[aria-current="page"]::before { background: var(--primary); }   /* 左侧主色条 */
.de-nav__item.is-active .de-nav__ico,
.de-nav__item[aria-current="page"] .de-nav__ico { background: var(--primary-100); color: var(--primary-active); }
.de-nav__item:focus-visible { outline-offset: -2px; }

/* 3.3b 白标 / custom_css 回归防护 ------------------------------------------
   品牌主题色只覆盖 --primary / --accent（index.php 的 $themeCss、brand/index.php），
   不覆盖 --bg，因此 --muted-strong 在 --bg 上的 6.9:1 结论不变。
   当前项 4 种非颜色差异里，「浅蓝底 / 600 字重 / 图标块」用的是固定蓝色派生
   令牌（--primary-50 / --fw-semibold / --primary-100 + --primary-active），
   不随品牌色变化；只有 3px 指示条用 --primary。品牌色过浅时指示条可能贴不住
   白侧栏，故补 1px 固定深蓝发丝线保证形状可辨。整组声明提到 (0,4,0)，
   页面级 custom_css 即使覆盖 .de-nav__item 也不易抹掉可辨识性。
   状态徽章 / 提示条一律「文字标签 + 图标 + 底色」三重表意，颜色不是唯一信息源。 */
.de-layout .de-sidebar .de-nav__item.is-active,
.de-layout .de-sidebar .de-nav__item[aria-current="page"] {
  background: var(--primary-50);
  color: var(--primary-ink);
  font-weight: var(--fw-semibold);
}
.de-layout .de-sidebar .de-nav__item.is-active::before,
.de-layout .de-sidebar .de-nav__item[aria-current="page"]::before {
  background: var(--primary);
  box-shadow: 1px 0 0 var(--primary-ink);
}

.de-nav__ico {
  flex: 0 0 22px;
  width: 22px; height: 22px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--r-sm);
  background: var(--surface-sunken);
  color: var(--muted-strong);
  font-family: var(--font-symbol);
  font-size: 13px;
  line-height: 1;
  transition: background var(--t-fast), color var(--t-fast);
}
.de-nav__label {
  flex: 1 1 auto; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.de-nav__count {
  flex: 0 0 auto;
  min-width: 20px; height: 18px;
  padding: 0 5px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--r-pill);
  background: var(--surface-sunken);
  color: var(--muted-strong);
  font-size: 11px; font-weight: var(--fw-semibold);
}
.de-nav__item.is-active .de-nav__count,
.de-nav__item[aria-current="page"] .de-nav__count { background: var(--primary-100); color: var(--primary-active); }

/* 3.4 底部当前用户区 */
.de-sidebar__user {
  flex: 0 0 auto;
  display: flex; align-items: center; gap: 10px;
  padding: var(--sp-3) 14px;
  border-top: 1px solid var(--border);
  background: var(--surface-soft);
}
.de-sidebar__user .de-person { flex: 1 1 auto; min-width: 0; }
.de-sidebar__logout { margin: 0; flex: 0 0 auto; }
.de-sidebar__foot {
  padding: 0 14px var(--sp-3);
  background: var(--surface-soft);
  color: var(--placeholder);
  font-size: 11px;
  line-height: 16px;
}
.de-sidebar__foot a { font-size: var(--fs-12); line-height: var(--lh-12); font-weight: var(--fw-medium); }

/* 3.5 导航六类状态补充：折叠/展开、禁用只读、危险操作
   —— 折叠为纯 CSS，支持两种写法：
      A. <details class="de-nav-group" open><summary class="de-nav-group__title">…</summary><nav …>…</nav></details>
      B. <div class="de-nav-group is-collapsed"><div class="de-nav-group__title de-nav-group__title--toggle">…</div><nav …>…</nav></div>
   两种写法都不需要 JS；PHP 只需切换 open 属性 / is-collapsed 类。 */
.de-nav-group__title--toggle,
details.de-nav-group > summary.de-nav-group__title {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2);
  margin: 0 10px;
  padding: var(--sp-2) 10px 6px;
  border-radius: var(--r-sm);
  cursor: pointer;
  list-style: none;
  user-select: none;
  color: var(--placeholder);
  transition: background var(--t-fast), color var(--t-fast);
}
details.de-nav-group > summary.de-nav-group__title::-webkit-details-marker { display: none; }
.de-nav-group__title--toggle::after,
details.de-nav-group > summary.de-nav-group__title::after {
  content: "\25BE";                 /* ▾ 展开 */
  flex: 0 0 auto;
  font-family: var(--font-symbol);
  font-size: 11px;
  line-height: 1;
  color: var(--border-strong);
  transition: transform var(--t-fast), color var(--t-fast);
}
.de-nav-group.is-collapsed .de-nav-group__title--toggle::after,
details.de-nav-group:not([open]) > summary.de-nav-group__title::after {
  content: "\25B8";                 /* ▸ 折叠 */
  color: var(--placeholder);
}
.de-nav-group__title--toggle:hover,
details.de-nav-group > summary.de-nav-group__title:hover { background: var(--surface-soft); color: var(--primary); }
.de-nav-group__title--toggle:hover::after,
details.de-nav-group > summary.de-nav-group__title:hover::after { color: var(--primary); }
.de-nav-group__title--toggle:focus-visible,
details.de-nav-group > summary.de-nav-group__title:focus-visible {
  outline: 2px solid var(--primary); outline-offset: 1px; border-radius: var(--r-sm);
}
.de-nav-group.is-collapsed .de-nav { display: none; }
/* 折叠态在窄屏横向条中同样生效（分组标题隐藏时由 PHP 控制，不额外展示箭头） */

/* 禁用 / 只读导航项 */
.de-nav__item.is-disabled,
.de-nav__item[aria-disabled="true"] {
  opacity: .45;
  cursor: not-allowed;
  pointer-events: none;
  box-shadow: none;
}
.de-nav__item.is-disabled::before,
.de-nav__item[aria-disabled="true"]::before { background: transparent; }

/* 危险操作导航项（如删除 / 停用入口）：默认深红字，hover 浅红底，选中红条 */
.de-nav__item--danger { color: var(--danger-ink); }
.de-nav__item--danger .de-nav__ico { background: var(--danger-bg); color: var(--danger-ink); }
.de-nav__item--danger:hover { background: var(--danger-bg); color: var(--danger-ink); }
.de-nav__item--danger:hover .de-nav__ico { background: var(--danger-border); color: var(--danger-ink); }
.de-nav__item--danger.is-active,
.de-nav__item--danger[aria-current="page"] { background: var(--danger-bg); color: var(--danger-ink); }
.de-nav__item--danger.is-active::before,
.de-nav__item--danger[aria-current="page"]::before { background: var(--danger); }
.de-nav__item--danger.is-active .de-nav__ico,
.de-nav__item--danger[aria-current="page"] .de-nav__ico { background: var(--danger-border); color: var(--danger-ink); }

/* =============================================================================
   4. 右侧主内容区
   ============================================================================= */
.de-main {
  flex: 1 1 auto;
  min-width: 0;
  padding: var(--sp-6) var(--sp-7) var(--sp-9);
  /* 表单错误/焦点态的可继承开关，见 8.4 */
  --de-field-border: var(--border-strong);
  --de-field-focus: var(--primary);
  --de-field-ring: var(--ring);
}
.de-main__inner {
  width: 100%;
  max-width: var(--content-max);
  margin: 0 auto;
}

/* =============================================================================
   5. 排版 / 页头 / 通用文本
   ============================================================================= */
.de-h1, .de-h2, .de-h3, .de-h4 { margin: 0; color: var(--text-strong); font-weight: var(--fw-semibold); }
.de-h1 { font-size: var(--fs-28); line-height: var(--lh-28); letter-spacing: -.01em; }
.de-h2 { font-size: var(--fs-22); line-height: var(--lh-22); }
.de-h3 { font-size: var(--fs-18); line-height: var(--lh-18); }
.de-h4 { font-size: var(--fs-16); line-height: var(--lh-16); }
.de-p  { margin: 0 0 var(--sp-3); font-size: var(--fs-14); line-height: var(--lh-14); color: var(--text); }
.de-muted   { color: var(--muted-strong); font-size: var(--fs-13); line-height: var(--lh-13); }
.de-caption { color: var(--muted); font-size: var(--fs-12); line-height: var(--lh-12); }
.de-mono    { font-family: var(--font-mono); font-size: var(--fs-12); }
.de-strong  { font-weight: var(--fw-semibold); color: var(--text-strong); }
.de-link    { color: var(--primary); text-decoration: none; }
.de-link:hover { text-decoration: underline; }
.de-divider { height: 1px; border: 0; background: var(--border); margin: var(--sp-6) 0; }
.de-divider--tight { margin: var(--sp-4) 0; }

.de-crumb { margin: 0 0 6px; font-size: var(--fs-12); line-height: var(--lh-12); color: var(--muted); }
.de-crumb a { color: var(--muted-strong); text-decoration: none; }
.de-crumb a:hover { color: var(--primary); }
.de-crumb__sep { margin: 0 6px; color: var(--border-strong); }

.de-page-head {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: var(--sp-4); flex-wrap: wrap;
  padding-bottom: var(--sp-4);
  margin-bottom: var(--sp-6);
  border-bottom: 1px solid var(--border);
}
.de-page-head__main { min-width: 0; }
/* 身份徽标：平台端 / 品牌端。放在页头标题上方——每次翻页都能确认自己站在哪个区，
   比塞在 232px 的侧栏里更稳（侧栏窄，徽标会把品牌名挤到换行）。 */
.de-page-head__side {
  display: inline-flex; align-items: center; gap: 6px;
  margin: 0 0 9px;
  padding: 2px 10px 2px 8px;
  border-radius: 999px;
  font-size: 11px; font-weight: var(--fw-semibold); line-height: 16px;
  letter-spacing: .04em; white-space: nowrap;
}
.de-page-head__side::before {
  content: ''; flex: 0 0 auto;
  width: 5px; height: 5px; border-radius: 50%;
}
.de-page-head__side--admin {
  background: linear-gradient(135deg, rgba(37, 99, 235, .12), rgba(124, 58, 237, .10));
  border: 1px solid rgba(37, 99, 235, .20);
  color: var(--primary-ink);
}
.de-page-head__side--admin::before {
  background: linear-gradient(135deg, #2563eb, #7c3aed);
  box-shadow: 0 0 0 3px rgba(37, 99, 235, .12);
}
.de-page-head__side--brand {
  background: linear-gradient(135deg, rgba(13, 148, 136, .12), rgba(37, 99, 235, .08));
  border: 1px solid rgba(13, 148, 136, .20);
  color: #0f766e;
}
.de-page-head__side--brand::before {
  background: linear-gradient(135deg, #0d9488, #2563eb);
  box-shadow: 0 0 0 3px rgba(13, 148, 136, .12);
}
.de-page-head__title {
  margin: 0;
  font-size: var(--fs-28); line-height: var(--lh-28);
  font-weight: var(--fw-bold); color: var(--text-strong);
  letter-spacing: -.01em;
}
.de-page-head__sub {
  margin: 6px 0 0;
  font-size: var(--fs-14); line-height: var(--lh-14);
  color: var(--muted-strong);
}
.de-page-head__sub:empty { display: none; }   /* deUiStart($subtitle='') 时不留空行 */
.de-page-head__actions { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
.de-page-head__actions:empty { display: none; }

/* =============================================================================
   6. 布局组件：卡片 / 区块 / 栅格 / 数据小卡
   ============================================================================= */
.de-card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-1);
  margin: 0 0 var(--sp-5);
  overflow: hidden;
  transition: box-shadow var(--t), border-color var(--t);
}
.de-card:hover { border-color: var(--primary-100); box-shadow: var(--sh-2); }
.de-card__head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--sp-3); flex-wrap: wrap;
  padding: var(--sp-4) var(--sp-5);
  border-bottom: 1px solid var(--border);
  background: var(--card);
}
.de-card__head--tinted { background: var(--surface-soft); }
.de-card__title { margin: 0; font-size: var(--fs-16); line-height: var(--lh-16); font-weight: var(--fw-semibold); color: var(--text-strong); }
.de-card__desc  { margin: 2px 0 0; font-size: var(--fs-12); line-height: var(--lh-12); color: var(--muted-strong); }
.de-card__actions { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
.de-card__body { padding: var(--sp-5); }
.de-card__body--tight { padding: var(--sp-4); }
.de-card__foot {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--sp-3); flex-wrap: wrap;
  padding: var(--sp-3) var(--sp-5);
  border-top: 1px solid var(--border);
  background: var(--surface-soft);
  font-size: var(--fs-12); color: var(--muted-strong);
}
.de-card__body > :last-child { margin-bottom: 0; }

.de-section { margin: 0 0 var(--sp-7); }
.de-section__head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--sp-3); flex-wrap: wrap;
  margin-bottom: var(--sp-3);
}
.de-section__title {
  margin: 0;
  padding-left: 10px;
  border-left: 3px solid var(--primary);
  font-size: var(--fs-18); line-height: var(--lh-18);
  font-weight: var(--fw-semibold); color: var(--text-strong);
}
.de-section__desc { margin: 0; font-size: var(--fs-13); line-height: var(--lh-13); color: var(--muted-strong); }
.de-section__body { min-width: 0; }

.de-grid { display: grid; gap: var(--sp-4); grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); margin: 0 0 var(--sp-5); }
.de-grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.de-grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.de-grid--stats { grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }
.de-grid--tight { gap: var(--sp-3); }
.de-grid > * { min-width: 0; }

.de-stat {
  display: flex; align-items: flex-start; gap: var(--sp-3);
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-1);
  padding: var(--sp-4) var(--sp-5);
  min-width: 0;
}
.de-stat__ico {
  flex: 0 0 34px; width: 34px; height: 34px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--r-md);
  background: var(--primary-50); color: var(--primary);
  font-family: var(--font-symbol); font-size: 16px; line-height: 1;
}
.de-stat__body { min-width: 0; flex: 1 1 auto; }
.de-stat__label { margin: 0 0 2px; font-size: var(--fs-12); line-height: var(--lh-12); color: var(--muted-strong); font-weight: var(--fw-medium); }
.de-stat__value,
.de-stat__num {   /* __num 为 __value 的口语别名，语义完全相同 */
  display: flex; align-items: baseline; gap: 4px;
  margin: 0; font-size: var(--fs-28); line-height: var(--lh-28);
  font-weight: var(--fw-bold); color: var(--text-strong);
  font-variant-numeric: tabular-nums;
}
.de-stat__unit { font-size: var(--fs-13); font-weight: var(--fw-regular); color: var(--muted-strong); }
.de-stat__hint,
.de-stat__note { margin: 2px 0 0; font-size: var(--fs-12); line-height: var(--lh-12); color: var(--muted); }
.de-stat__delta { font-size: var(--fs-12); font-weight: var(--fw-semibold); }
.de-stat__delta--up { color: var(--success-strong); }
.de-stat__delta--down { color: var(--danger-strong); }
/* 需要关注的数字（如"待应用更新数>0"）：用琥珀色提示而非红色，避免误判为故障 */
.de-stat__num--warn { color: var(--accent); }

/* =============================================================================
   7. 按钮
   变体一律写成「.de-btn + 具体变体类」（0,2,0），确保能压过 .de-main 内的旧类覆盖。
   ============================================================================= */
.de-btn {
  display: inline-flex; align-items: center; justify-content: center;
  gap: 6px;
  min-height: 36px;
  padding: 0 var(--sp-4);
  border: 1px solid transparent;
  border-radius: var(--r-md);
  background: var(--surface-sunken);
  color: var(--text);
  font-family: inherit;
  font-size: var(--fs-14);
  font-weight: var(--fw-semibold);
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  vertical-align: middle;
  transition: background var(--t-fast), border-color var(--t-fast), color var(--t-fast), box-shadow var(--t-fast), transform var(--t-fast);
}
.de-btn:hover { text-decoration: none; }
.de-btn:active { transform: translateY(1px); }
.de-btn:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; box-shadow: var(--ring); }

/* 11.1 ghost 变体：无边框、无底色，只在 hover 时浮出浅底。
   用于「返回、取消、查看详情」这类次要动作与公开页脚返回入口，
   保证同屏只有一个视觉重量最高的主按钮。 */
.de-btn.de-btn--ghost {
  background: transparent; border-color: transparent; color: var(--muted-strong);
  padding-left: var(--sp-2); padding-right: var(--sp-2);
}
.de-btn.de-btn--ghost:hover { background: var(--surface-sunken); color: var(--text); }

/* 11.2 示例占位卡：页面尚未接入真实数据、只能展示结构示例时使用。
   必须与真实数据卡一眼可分（虚线边框 + 浅底 + 无阴影），并配合 HTML 里的
   <span class="de-badge de-badge--info">示例</span> 角标，避免用户误认。 */
.de-card.de-card--placeholder {
  background: var(--surface-soft);
  border-style: dashed;
  border-color: var(--border-strong);
  box-shadow: none;
}
.de-card.de-card--placeholder:hover { border-color: var(--primary-200); box-shadow: none; }
.de-card.de-card--placeholder .de-card__title { color: var(--muted-strong); }
.de-card.de-card--placeholder .de-card__desc { color: var(--muted); }

/* =============================================================================
   第 19 节：媒体卡（员工 / 岗位等"实体卡"专用）
   ---- 参考数字人平台岗位卡的结构：大图区 + 左上角小徽标 + 标题 + 一句话说明 +
        标签行 + 底部「参数｜动作」栏。本系统没有图片素材，也不引外链照片，
        媒体区用头像 t1~t6 同源的浅色渐变 + 大号几何字符承担视觉主角。
        六档渐变与头像一致，保证同一员工/岗位跨页面颜色稳定。 ------------- */

/* 通栏媒体卡：去掉默认内边距，让渐变媒体区顶到卡片边缘 */
.de-card.de-card--media { padding: 0; display: flex; flex-direction: column; }
.de-card.de-card--media .de-card__head {
  padding: var(--sp-4) var(--sp-5) 0;
  border-bottom: 0;              /* 卡头紧接媒体区，保留分隔线会切断视觉连续性 */
  background: transparent;
}
.de-card.de-card--media .de-card__body { padding: var(--sp-2) var(--sp-5) var(--sp-4); flex: 1 1 auto; }
/* 同一行里正文长短不一时，底部参数栏始终贴齐卡片底边 */
.de-card.de-card--media .de-card__meta { margin-top: auto; }

.de-media {
  position: relative;
  display: flex; align-items: center; justify-content: center;
  height: 132px;
  border-bottom: 1px solid var(--border);
  overflow: hidden;
}
.de-media__glyph {
  font-size: 52px; line-height: 1;
  opacity: .92;
  user-select: none;
}
.de-media__chip {
  position: absolute; top: 10px; left: 10px;
  display: inline-flex; align-items: center; gap: 4px;
  max-width: calc(100% - 20px);
  padding: 3px 10px; border-radius: 999px;
  background: rgba(255, 255, 255, .88);
  border: 1px solid var(--border);
  font-size: var(--fs-12); line-height: var(--lh-12);
  font-weight: var(--fw-medium); color: var(--muted-strong);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* 六档渐变：色值与第 13 节头像 t1~t6 完全同源 */
.de-media--t1 { background: linear-gradient(135deg, #dbeafe, #bfdbfe); }
.de-media--t1 .de-media__glyph { color: #1e40af; }
.de-media--t2 { background: linear-gradient(135deg, #cffafe, #a5f3fc); }
.de-media--t2 .de-media__glyph { color: #155e75; }
.de-media--t3 { background: linear-gradient(135deg, #ccfbf1, #99f6e4); }
.de-media--t3 .de-media__glyph { color: #115e59; }
.de-media--t4 { background: linear-gradient(135deg, #fef3c7, #fde68a); }
.de-media--t4 .de-media__glyph { color: #92400e; }
.de-media--t5 { background: linear-gradient(135deg, #ffe4e6, #fecdd3); }
.de-media--t5 .de-media__glyph { color: #9f1239; }
.de-media--t6 { background: linear-gradient(135deg, #ede9fe, #ddd6fe); }
.de-media--t6 .de-media__glyph { color: #5b21b6; }

/* 标签行：小药丸，一排不超过一行半，超出自动换行 */
.de-tags { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 var(--sp-2); }
.de-tag-pill {
  display: inline-flex; align-items: center;
  padding: 2px 10px; border-radius: 999px;
  background: var(--surface-sunken); color: var(--muted-strong);
  font-size: var(--fs-12); line-height: 18px; font-weight: var(--fw-medium);
}

/* 底部参数栏：左侧灰字参数（用量/模型/时长），右侧动作链接 */
.de-card__meta {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--sp-3); flex-wrap: wrap;
  padding: var(--sp-3) var(--sp-5);
  border-top: 1px solid var(--border);
  background: var(--surface-soft);
  font-size: var(--fs-12); line-height: var(--lh-12); color: var(--muted);
}
.de-link-action {
  display: inline-flex; align-items: center; gap: 2px;
  color: var(--primary); font-size: var(--fs-13);
  font-weight: var(--fw-semibold); text-decoration: none; white-space: nowrap;
}
.de-link-action:hover { color: var(--primary-hover); text-decoration: none; }
.de-link-action:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; border-radius: 4px; }
.de-link-action::after { content: "›"; font-size: 15px; line-height: 1; transform: translateY(-1px); }

@media (max-width: 640px) {
  .de-media { height: 108px; }
  .de-media__glyph { font-size: 40px; }
}

/* 紧凑档：一张页面里卡片数量多（10 张以上）时使用，媒体区降到 96px，
   既保留"图 + 文 + 底栏"的层次，又不至于把页面拉得过长。 */
.de-media--sm { height: 96px; }
.de-media--sm .de-media__glyph { font-size: 34px; }
@media (max-width: 640px) {
  .de-media--sm { height: 84px; }
  .de-media--sm .de-media__glyph { font-size: 28px; }
}

.de-btn.de-btn--primary { background: var(--primary); border-color: var(--primary); color: #fff; }
.de-btn.de-btn--primary:hover { background: var(--primary-hover); border-color: var(--primary-hover); color: #fff; }
.de-btn.de-btn--primary:active { background: var(--primary-active); border-color: var(--primary-active); }

.de-btn.de-btn--secondary { background: var(--card); border-color: var(--border-strong); color: var(--text); }
.de-btn.de-btn--secondary:hover { background: var(--primary-50); border-color: var(--primary-200); color: var(--primary-ink); }

.de-btn.de-btn--danger { background: var(--danger); border-color: var(--danger); color: #fff; }
.de-btn.de-btn--danger:hover { background: var(--danger-strong); border-color: var(--danger-strong); color: #fff; }

.de-btn.de-btn--light { background: var(--primary-50); border-color: transparent; color: var(--primary-ink); }
.de-btn.de-btn--light:hover { background: var(--primary-100); color: var(--primary-active); }

.de-btn.de-btn--accent { background: var(--accent); border-color: var(--accent); color: #fff; }
.de-btn.de-btn--accent:hover { background: var(--accent-hover); border-color: var(--accent-hover); color: #fff; }

.de-btn.de-btn--sm { min-height: 30px; padding: 0 var(--sp-3); font-size: var(--fs-13); }
.de-btn.de-btn--lg { min-height: 42px; padding: 0 var(--sp-5); font-size: var(--fs-16); }
.de-btn.de-btn--block { display: flex; width: 100%; }

.de-btn:disabled,
.de-btn[aria-disabled="true"],
.de-btn.is-disabled { opacity: .5; cursor: not-allowed; pointer-events: none; box-shadow: none; }

/* 纯 CSS 加载态 */
.de-btn.is-loading { position: relative; color: transparent !important; pointer-events: none; }
.de-btn.is-loading::after {
  content: "";
  position: absolute;
  width: 14px; height: 14px;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  color: #fff;
  animation: de-spin .7s linear infinite;
}
.de-btn.de-btn--secondary.is-loading::after,
.de-btn.de-btn--light.is-loading::after { color: var(--primary); }
@keyframes de-spin { to { transform: rotate(360deg); } }

.de-btn-group { display: inline-flex; gap: var(--sp-2); flex-wrap: wrap; align-items: center; }

/* =============================================================================
   8. 表单
   ============================================================================= */
.de-form { margin: 0; }
.de-field { margin: 0 0 var(--sp-4); }
.de-field:last-child { margin-bottom: 0; }
.de-label {
  display: block;
  margin: 0 0 6px;
  font-size: var(--fs-13); line-height: var(--lh-13);
  font-weight: var(--fw-medium); color: var(--muted-strong);
}
.de-required { margin-left: 3px; color: var(--danger); font-weight: var(--fw-bold); }
.de-help { margin: 6px 0 0; font-size: var(--fs-12); line-height: var(--lh-12); color: var(--muted); }
.de-error-text { margin: 6px 0 0; font-size: var(--fs-12); line-height: var(--lh-12); color: var(--danger-ink); font-weight: var(--fw-medium); }

.de-input,
.de-select,
.de-textarea,
.de-form input:not([type="radio"]):not([type="checkbox"]):not([type="hidden"]):not([type="submit"]):not([type="button"]):not([type="reset"]):not([type="file"]),
.de-form select,
.de-form textarea {
  display: block;
  width: 100%; max-width: 100%;
  min-height: 38px;
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-md);
  background: var(--card);
  color: var(--text);
  font-family: inherit;
  font-size: var(--fs-14);
  line-height: var(--lh-14);
  transition: border-color var(--t-fast), box-shadow var(--t-fast), background var(--t-fast);
}
.de-textarea, .de-form textarea { min-height: 88px; resize: vertical; }
.de-input::placeholder, .de-select::placeholder, .de-textarea::placeholder,
.de-form input::placeholder, .de-form textarea::placeholder { color: var(--placeholder); }

.de-input:focus, .de-select:focus, .de-textarea:focus,
.de-form input:not([type="radio"]):not([type="checkbox"]):not([type="hidden"]):not([type="submit"]):not([type="button"]):not([type="reset"]):not([type="file"]):focus,
.de-form select:focus, .de-form textarea:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: var(--ring);
}
.de-input:disabled, .de-select:disabled, .de-textarea:disabled,
.de-form input:disabled, .de-form select:disabled, .de-form textarea:disabled {
  background: var(--surface-sunken); color: var(--muted); cursor: not-allowed; box-shadow: none;
}
/* 错误态：红框 + 红色焦点环；错误文字单独用 .de-error-text / .de-main .err 承载。
   （背景不被强制染红，避免与高特异性输入基座规则打架，也避免整块表单泛红） */
.de-input.is-invalid, .de-select.is-invalid, .de-textarea.is-invalid,
.de-form .is-invalid, .de-form [aria-invalid="true"] {
  border-color: var(--danger) !important;
}
.de-input.is-invalid:focus, .de-select.is-invalid:focus, .de-textarea.is-invalid:focus,
.de-form .is-invalid:focus, .de-form [aria-invalid="true"]:focus { box-shadow: var(--ring-danger); }
.de-field.is-invalid .de-label { color: var(--danger-ink); }

.de-radio, .de-check {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  min-height: 30px; margin: 0 var(--sp-4) var(--sp-2) 0;
  font-size: var(--fs-14); color: var(--text); cursor: pointer;
}
.de-radio input, .de-check input { width: 16px; height: 16px; margin: 0; accent-color: var(--primary); cursor: pointer; }
.de-check-group, .de-radio-group { display: flex; flex-wrap: wrap; gap: 0; }
.de-form fieldset { margin: 0 0 var(--sp-4); padding: var(--sp-4); border: 1px solid var(--border); border-radius: var(--r-lg); }
.de-form legend { padding: 0 6px; font-size: var(--fs-13); font-weight: var(--fw-semibold); color: var(--muted-strong); }
.de-form-actions {
  display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap;
  margin-top: var(--sp-5); padding-top: var(--sp-4);
  border-top: 1px solid var(--border);
}

/* =============================================================================
   8.5 渐进增强表单控件（体验规范表 5.2 / 5.3 拆分后新增的 8 类控件）
   -----------------------------------------------------------------------------
   全部为纯 CSS / 原生表单元素，零图片、零图标库。需要 JS 的地方只有「模式 0
   增强成功后给共同祖先挂 .de-js-enhanced 标记类」，见 8.5.8b；其余交互
   （增删行由页面脚本负责 DOM，显隐/选中态由 [hidden] / :checked / <details>
   原生能力承担）都无需样式侧 JS。
   ============================================================================= */

/* --- 8.5.1 可增删重复行 .de-repeat ---------------------------------------- */
.de-repeat { margin: 0 0 var(--sp-4); }
.de-repeat__head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--sp-3); flex-wrap: wrap; margin-bottom: var(--sp-2);
}
.de-repeat__label { font-size: var(--fs-13); line-height: var(--lh-13); font-weight: var(--fw-medium); color: var(--muted-strong); }
.de-repeat__hint  { font-size: var(--fs-12); line-height: var(--lh-12); color: var(--muted-strong); }
.de-repeat__list  { display: flex; flex-direction: column; gap: var(--sp-2); margin: 0 0 var(--sp-3); padding: 0; list-style: none; }
.de-repeat__row {
  --de-cols: 3;
  display: grid;
  grid-template-columns: repeat(var(--de-cols), minmax(0, 1fr)) auto;
  gap: var(--sp-3);
  align-items: end;
  padding: var(--sp-3);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--surface-soft);
  transition: border-color var(--t-fast), box-shadow var(--t-fast);
}
.de-repeat__row--2 { --de-cols: 2; }
.de-repeat__row--3 { --de-cols: 3; }
.de-repeat__row--4 { --de-cols: 4; }
.de-repeat__row:hover { border-color: var(--primary-100); }
.de-repeat__row:focus-within { border-color: var(--primary); box-shadow: var(--ring); }
.de-repeat__row.is-disabled { opacity: .5; pointer-events: none; }
.de-repeat__cell { min-width: 0; }
.de-repeat__cell .de-label { margin-bottom: 4px; font-size: var(--fs-12); }
.de-repeat__actions { display: flex; align-items: center; gap: var(--sp-2); padding-bottom: 1px; }
.de-repeat__row--kv { grid-template-columns: minmax(0, 1fr) minmax(0, 2fr) auto; }  /* 成对键值行：项目 / 内容 / 操作 */

/* 行内小按钮：新增 / 删除（默认 / hover / 禁用 / 焦点四态齐全） */
.de-repeat__btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 4px;
  min-height: 30px; padding: 0 var(--sp-3);
  border: 1px solid var(--border-strong); border-radius: var(--r-md);
  background: var(--card); color: var(--muted-strong);
  font-family: inherit; font-size: var(--fs-12); font-weight: var(--fw-semibold);
  line-height: 1; white-space: nowrap; cursor: pointer;
  transition: background var(--t-fast), border-color var(--t-fast), color var(--t-fast), box-shadow var(--t-fast);
}
.de-repeat__btn:hover { background: var(--primary-50); border-color: var(--primary-200); color: var(--primary-ink); }
.de-repeat__btn:active { transform: translateY(1px); }
.de-repeat__btn:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; box-shadow: var(--ring); }
.de-repeat__btn:disabled,
.de-repeat__btn[aria-disabled="true"],
.de-repeat__btn.is-disabled { opacity: .45; cursor: not-allowed; pointer-events: none; box-shadow: none; }
.de-repeat__btn--add {
  width: 100%;
  border-style: dashed; border-color: var(--primary-200);
  background: var(--primary-50); color: var(--primary-ink);
}
.de-repeat__btn--add:hover { background: var(--primary-100); border-color: var(--primary); }
.de-repeat__btn--del:hover { background: var(--danger-bg); border-color: var(--danger-border); color: var(--danger-ink); }
/* 「只剩一行时删除按钮禁用」：PHP 输出 disabled 或 aria-disabled 即可，样式同上 */
.de-repeat__list > .de-repeat__row:only-child .de-repeat__btn--del { opacity: .45; cursor: not-allowed; pointer-events: none; }

/* --- 8.5.2 条件显隐字段组 .de-conditional ---------------------------------- */
.de-conditional { margin: 0 0 var(--sp-4); }
.de-conditional__switch { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-bottom: var(--sp-3); }
.de-conditional__group {
  padding: var(--sp-4);
  border: 1px solid var(--border);
  border-left: 3px solid var(--primary);
  border-radius: var(--r-md);
  background: var(--card);
}
.de-conditional__group + .de-conditional__group { margin-top: var(--sp-3); }
.de-conditional__title { margin: 0 0 var(--sp-3); font-size: var(--fs-14); font-weight: var(--fw-semibold); color: var(--text-strong); }
.de-conditional__group[hidden],
.de-conditional__group.is-hidden,
.de-conditional [hidden] { display: none !important; }   /* 静态显隐，无动画、无需 JS */
.de-conditional__group:focus-within { border-left-color: var(--primary-hover); }

/* --- 8.5.3 多选 / 标签式多选 .de-choice ------------------------------------- */
.de-choice-group { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin: 0 0 var(--sp-3); }
.de-choice {
  position: relative;
  display: inline-flex; align-items: center; gap: 6px;
  min-height: 32px; padding: 0 var(--sp-3);
  border: 1px solid var(--border-strong); border-radius: var(--r-pill);
  background: var(--card); color: var(--muted-strong);
  font-size: var(--fs-13); line-height: 1;
  font-weight: var(--fw-medium);
  cursor: pointer; user-select: none;
  transition: background var(--t-fast), border-color var(--t-fast), color var(--t-fast), box-shadow var(--t-fast);
}
.de-choice input[type="checkbox"], .de-choice input[type="radio"] {
  position: absolute; width: 1px; height: 1px; opacity: 0; margin: 0;
}
.de-choice__mark { width: 12px; height: 12px; border: 1px solid var(--border-strong); border-radius: 3px; background: var(--card); flex: 0 0 auto; }
.de-choice:hover { background: var(--primary-50); border-color: var(--primary-200); color: var(--primary-ink); }
/* :has() 让无 JS 时点击即可切换视觉；.is-checked / .is-disabled 为 PHP 服务端
   渲染的等价兜底。注意：含 :has() 的选择器必须单独成规则，不能与逗号列表里的
   普通选择器写在一起 —— 不支持 :has() 的浏览器会丢弃整条规则。 */
.de-choice:has(input:checked) {
  background: var(--primary-50); border-color: var(--primary);
  color: var(--primary-ink); font-weight: var(--fw-semibold);
}
.de-choice.is-checked {
  background: var(--primary-50); border-color: var(--primary);
  color: var(--primary-ink); font-weight: var(--fw-semibold);
}
.de-choice:has(input:checked) .de-choice__mark { background: var(--primary); border-color: var(--primary); box-shadow: inset 0 0 0 2px var(--card); }
.de-choice.is-checked .de-choice__mark { background: var(--primary); border-color: var(--primary); box-shadow: inset 0 0 0 2px var(--card); }
.de-choice:focus-within { outline: 2px solid var(--primary); outline-offset: 2px; box-shadow: var(--ring); }
.de-choice:has(input:disabled) { opacity: .45; cursor: not-allowed; pointer-events: none; box-shadow: none; }
.de-choice.is-disabled { opacity: .45; cursor: not-allowed; pointer-events: none; box-shadow: none; }
.de-choice__text { min-width: 0; }

/* --- 8.5.4 时间选择 + 星期多选组合 .de-schedule ----------------------------- */
.de-schedule {
  margin: 0 0 var(--sp-4); padding: var(--sp-4);
  border: 1px solid var(--border); border-radius: var(--r-lg); background: var(--card);
}
.de-schedule__row { display: flex; flex-wrap: wrap; gap: var(--sp-4); align-items: flex-start; }
.de-schedule__time { flex: 0 0 160px; min-width: 0; }
.de-schedule__days { flex: 1 1 260px; min-width: 0; }
.de-schedule__label { display: block; margin-bottom: 6px; font-size: var(--fs-13); font-weight: var(--fw-medium); color: var(--muted-strong); }
.de-schedule__time input[type="time"] { width: 100%; }
.de-schedule .de-choice-group { margin-bottom: 0; }

/* --- 8.5.5 取色控件 .de-color + 默认折叠高级分组 .de-advanced --------------- */
.de-color { display: inline-flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
/* 取色是原生 input[type=color]，需压过 .de-main 里高特异性的输入基座规则 */
.de-main .de-color input[type="color"],
.de-form .de-color input[type="color"] {
  display: inline-block;
  width: 48px !important; height: 38px !important; min-height: 0 !important;
  padding: 2px !important; margin: 0 !important;
  border: 1px solid var(--border-strong) !important; border-radius: var(--r-md) !important;
  background: var(--card) !important;
  cursor: pointer;
}
.de-main .de-color input[type="color"]:focus,
.de-form .de-color input[type="color"]:focus { border-color: var(--primary) !important; box-shadow: var(--ring) !important; }
.de-color__value {
  font-family: var(--font-mono); font-size: var(--fs-13);
  color: var(--muted-strong); min-width: 84px;
  font-variant-numeric: tabular-nums;
}

.de-advanced {
  margin: 0 0 var(--sp-4);
  border: 1px solid var(--border); border-radius: var(--r-lg);
  background: var(--card); overflow: hidden;
}
.de-advanced > summary {
  display: flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-4);
  font-size: var(--fs-13); font-weight: var(--fw-semibold);
  color: var(--muted-strong);
  cursor: pointer; list-style: none; user-select: none;
  transition: background var(--t-fast), color var(--t-fast);
}
.de-advanced > summary::-webkit-details-marker { display: none; }
.de-advanced > summary::before { content: "\25B8"; font-family: var(--font-symbol); font-size: 11px; color: var(--placeholder); }
.de-advanced[open] > summary::before { content: "\25BE"; }
.de-advanced > summary:hover { background: var(--surface-soft); color: var(--primary-ink); }
.de-advanced > summary:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }
.de-advanced__body { padding: var(--sp-4); border-top: 1px solid var(--border); }
.de-advanced__body > :last-child { margin-bottom: 0; }

/* --- 8.5.6 只读要点清单 .de-points（替代 <pre> 大段结构化输出） ------------- */
.de-points {
  margin: 0 0 var(--sp-4); padding: var(--sp-4) var(--sp-5);
  border: 1px solid var(--border); border-radius: var(--r-lg); background: var(--card);
}
.de-points__title { margin: 0 0 var(--sp-3); font-size: var(--fs-16); line-height: var(--lh-16); font-weight: var(--fw-semibold); color: var(--text-strong); }
.de-points__meta { margin: -6px 0 var(--sp-3); font-size: var(--fs-12); color: var(--muted-strong); }
.de-points__list { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: var(--sp-2); }
.de-points__item {
  position: relative; padding-left: var(--sp-4);
  font-size: var(--fs-13); line-height: var(--lh-13); color: var(--text);
}
.de-points__item::before {
  content: ""; position: absolute; left: 2px; top: 7px;
  width: 6px; height: 6px; border-radius: 50%; background: var(--primary);
}
.de-points__item--ok::before    { background: var(--success); }
.de-points__item--warn::before  { background: var(--warn); }
.de-points__item--error::before { background: var(--danger); }
.de-points__item--muted::before { background: var(--border-strong); }
.de-points__k { display: inline-block; min-width: 96px; color: var(--muted-strong); font-weight: var(--fw-medium); }

/* --- 8.5.7 「按原始方式查看 / 编辑」低调折叠入口 .de-raw -------------------- */
.de-raw { margin: var(--sp-4) 0 0; }
.de-raw > summary {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 4px 0;
  color: var(--muted-strong);
  font-size: var(--fs-12); line-height: var(--lh-12);
  cursor: pointer; list-style: none; user-select: none;
  text-decoration: underline dotted; text-underline-offset: 3px;
}
.de-raw > summary::-webkit-details-marker { display: none; }
.de-raw > summary::before { content: "\25B8"; font-family: var(--font-symbol); font-size: 10px; color: var(--placeholder); text-decoration: none; }
.de-raw[open] > summary::before { content: "\25BE"; }
.de-raw > summary:hover { color: var(--primary); }
.de-raw > summary:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; border-radius: var(--r-xs); }
.de-raw__body {
  margin-top: var(--sp-3); padding: var(--sp-3);
  border: 1px dashed var(--border-strong); border-radius: var(--r-md);
  background: var(--surface-soft);
}

/* --- 8.5.8 仅供读屏 / 跳转型元素（视觉隐藏，但本就应被读屏与键盘感知） -------
   【适用】跳到内容链接、仅供读屏的标签、aria-live 状态播报容器。
   【禁止用于】模式 0 的原始结构化输入控件 —— 见 8.5.8b。理由：那些控件带着
   JSON 占位 / 结构示例 / 技术字段，把它们留在 a11y 树与 Tab 顺序里，会让读屏
   用户听到一个普通用户看不到的重复编辑框，:focus 展开还会突然弹出原始文本，
   属于「普通用户看不到、读屏用户看得到」的信息不对等。读屏用户也是普通用户。
   1px + clip 保留在 a11y 树与焦点顺序；:focus 展开解决「Tab 到却看不见」。 */
.de-visually-hidden-field {
  position: absolute !important;
  width: 1px !important; height: 1px !important;
  padding: 0 !important; margin: -1px !important;
  overflow: hidden !important; clip: rect(0, 0, 0, 0) !important;
  white-space: nowrap !important; border: 0 !important;
  opacity: 0;
}
.de-visually-hidden-field:focus,
.de-visually-hidden-field:focus-within {
  position: static !important;
  display: block !important;
  width: 100% !important; height: auto !important; min-height: 88px !important;
  padding: var(--sp-2) var(--sp-3) !important; margin: 0 0 var(--sp-3) !important;
  overflow: visible !important; clip: auto !important;
  white-space: normal !important;
  border: 1px solid var(--primary) !important; border-radius: var(--r-md) !important;
  opacity: 1; box-shadow: var(--ring);
}

/* --- 8.5.8b 模式 0：原始结构化输入控件的增强态隐藏（唯一门控契约） -----------
   【裁决 · 体验设计师 5.7 / 表 5.4 / 表 5.5】
   增强态统一用 display:none（hidden 属性等价），把原控件从「视觉 / a11y 树 /
   焦点顺序」三处同时移除。name / value / type 原样保留、照常提交 ——
   display:none 与 hidden 属性都【不影响表单提交】，真正导致不提交的是 disabled，
   因此【disabled 绝对禁止】。也不单用 aria-hidden='true'（移出 a11y 树但不移出
   焦点顺序，造成「能 Tab 到却读不出」）。
   同一元素【不得】同时带 hidden 属性与 8.5.8 的 clip 隐藏类：UA 的
   [hidden]{display:none} 与作者样式表 display 声明优先级不同，结果会漂移，
   必须二选一。

   【门控 · 唯一兜底 · 单一契约】服务端输出不带 hidden 属性，只挂 data 钩子
   （HTML data 属性不进令牌体系，前端不新增任何类名、不另写隐藏 CSS）：
     <div class="de-enhance">                     ← 共同祖先（也可复用 .de-subcell / .de-field / form）
       <textarea data-de-raw name="...">历史值</textarea>
       <div class="de-friendly" data-de-friendly> ... 友好控件 ... </div>
     </div>
   脚本按「解析历史值 → 回填友好控件 → 绑定事件 → 全部成功后最后一步」给共同
   祖先加 .de-js-enhanced。CSS 通过 .de-js-enhanced [data-de-raw] 属性选择器
   只在该类存在时隐藏原控件、显示友好区。
     · 无 JS / JS 被 CSP 拦截 / 初始化抛异常 → 祖先无 .de-js-enhanced，
       原控件保持可见可编辑可提交、友好区保持隐藏，整块表单不会消失。
     · <noscript> 只能覆盖「JS 完全禁用」，覆盖不了后两种，故不依赖它做门控。
     · 【渐进增强铁律】默认态必须「原表单可见可用」，隐藏只能由成功执行的脚本
       触发；任何「服务端默认隐藏 + 某种机制恢复」都依赖自身可能失效的恢复机制。
   点「按原方式查看 / 编辑」时脚本给同一祖先加 .is-raw，原控件恢复可见可聚焦；
   友好区保留 .de-btn-group 操作条（[data-de-toggle] 在友好区内，整体隐藏会
   导致用户无法切回）。移除 .de-js-enhanced 只作失败 / 默认态，不用作切换。
   注：旧页面给 textarea 挂的 .de-raw 是折叠入口组件的类名，属命名撞车，
       请直接去掉该 class、只保留 data-de-raw 即可（.de-raw-field 为可选别名）。 */

/* 【已按体验规范 5.7 最终裁决移除】默认态隐藏一律由服务端输出的 hidden 属性裁决
   （UA 样式表 [hidden]{display:none} 天然生效，且不依赖任何可能失效的恢复机制），
   ui.css 不再输出无条件 .de-friendly{display:none}，否则会压死「hidden 切换」路径
   的页面（JS 移除 hidden 后仍被本条 display:none 挡住，导致原控件与友好区同时不可见）。
   增强态显示仍由下方 .de-js-enhanced 规则统一负责。 */

/* 增强成功：这条属性选择器就是体验规范 5.7 指定的唯一隐藏规则，由 ui.css 统一收进，
   前端不自行另写隐藏 CSS、不新造等价类名。[data-de-raw] 为规范钩子；
   .de-raw-field 只是 ui.css 内已定义的可选别名（不是前端自造类），两者等效。 */
.de-js-enhanced [data-de-raw] { display: none; }
.de-js-enhanced .de-raw-field { display: none; }
.de-js-enhanced [data-de-friendly],
.de-js-enhanced .de-friendly { display: block; }

/* 切回「按原方式查看 / 编辑」：原控件恢复可见可聚焦。
   【为什么保留友好区操作条】实测 9 个页面的 [data-de-toggle] 都位于
   [data-de-friendly] 内的 .de-btn-group 里；若把友好区整体隐藏，用户就点不到
   「回到分项填写」。故此处友好区仍占位，只隐藏其填写内容，保留 .de-btn-group
   与 [data-de-err]。移除祖先 .de-js-enhanced 只作为「失败 / 默认态」，不用作切换。 */
.de-js-enhanced.is-raw [data-de-raw],
.de-js-enhanced.is-raw .de-raw-field { display: block; }
.de-js-enhanced.is-raw [data-de-friendly],
.de-js-enhanced.is-raw .de-friendly { display: block; }
.de-js-enhanced.is-raw [data-de-friendly] > *:not(.de-btn-group):not([data-de-err]),
.de-js-enhanced.is-raw .de-friendly > *:not(.de-btn-group):not([data-de-err]) { display: none; }

/* 兼容写法：若前端仍保留 hidden 属性做单项切换，可用元素级 .is-visible /
   .is-hidden 显式表态（特异性 0,3,0，高于上面的门控 0,2,0）。二选一即可，
   不要与 8.5.8 的 clip 隐藏类叠在同一元素上。 */
.de-js-enhanced .de-raw-field.is-visible,
.de-js-enhanced [data-de-raw].is-visible { display: block; }
.de-js-enhanced .de-friendly.is-hidden,
.de-js-enhanced [data-de-friendly].is-hidden { display: none; }

/* --- 8.5.9 全局面包屑 / 页内上下文返回 -------------------------------------- */
.de-crumbbar {
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  padding: 0 0 var(--sp-3); margin: 0 0 var(--sp-5);
  border-bottom: 1px solid var(--border);
  font-size: var(--fs-12); line-height: var(--lh-12); color: var(--muted);
}
.de-crumbbar a { color: var(--muted-strong); text-decoration: none; }
.de-crumbbar a:hover { color: var(--primary); text-decoration: underline; }
.de-crumbbar__sep { color: var(--border-strong); }
.de-crumbbar__current { color: var(--text); font-weight: var(--fw-semibold); }
.de-backlink {
  display: inline-flex; align-items: center; gap: 4px;
  margin: 0 0 var(--sp-3);
  color: var(--muted-strong); font-size: var(--fs-13); text-decoration: none;
}
.de-backlink:hover { color: var(--primary); text-decoration: underline; }

/* --- 8.5.10 窄屏横向导航的可见滚动提示（默认隐藏，<900px 显示） ------------- */
.de-scroll-hint {
  display: none; align-items: center; gap: 4px;
  padding: 0 var(--sp-3);
  color: var(--muted); font-size: 11px; line-height: 1; white-space: nowrap;
}

/* --- 8.5.11 前端已在使用的结构钩子：零特异性兜底 -----------------------------
   背景：fe-forms / fe-shell 在页面内联 <style> 或 JS 生成的 HTML 里用到下面这批
   class。它们不是新组件，而是「结构钩子」。此处一律用 :where()（0,0,0）给一组
   安全默认值：
     · 页面自带同名规则（0,1,0 及以上）永远优先 —— 不会覆盖前端已调好的布局；
     · 页面没写规则时，也不会出现「行挤在一起 / 按钮换行 / 登录页不居中」。
   新增钩子时请继续沿用 :where()，不要改成高特异性选择器。 */
:where(.de-login) {
  width: 100%; max-width: 480px; margin: 48px auto; padding: 0 var(--sp-4);
}
:where(.de-login .de-card) { margin: 0; }
:where(.de-login .de-card__foot) { flex-wrap: wrap; gap: var(--sp-3); }
:where(.de-logo) { display: block; max-height: 48px; margin: 0 auto var(--sp-3); }
:where(.de-rows) { display: grid; gap: var(--sp-3); }
:where(.de-repeat-row) { align-items: end; margin-bottom: var(--sp-2); }
:where(.de-repeat-row__act) { grid-column: 1 / -1; display: flex; justify-content: flex-end; }
:where(.de-friendly) { margin: 0 0 var(--sp-4); }
:where(.de-pair-row), :where(.de-case-row) {
  display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; margin-bottom: var(--sp-2);
}
:where(.de-color-row) { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; }
:where(.de-color-pick) {
  width: 56px; height: 38px; padding: 2px;
  border: 1px solid var(--border); border-radius: var(--r-md);
  background: var(--card); cursor: pointer;
}
:where(.de-color-pick):focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
:where(.de-subcell) { vertical-align: top; }
:where(.de-obj-main) { min-height: 88px; }
/* 模式 0 原控件的默认形态：无 JS / 增强失败时可见可编辑（隐藏由 8.5.8b 门控负责）。
   原控件可能是 textarea（多数）也可能是 input（如 triggers.php 的 cron_expr），分开给。 */
:where(textarea.de-raw-field) { display: block; width: 100%; max-width: 100%; min-height: 88px; }
:where(input.de-raw-field) { display: block; width: 100%; max-width: 100%; }

/* [hidden] 保护：任何 .de-* 组件一旦设置了 display:flex/grid，就会盖掉浏览器
   默认的 [hidden]{display:none}，导致条件显隐失效。此规则在 .de-layout / .de-main
   内把 hidden 语义钉死，服务于「条件显隐字段组」与前端保留 hidden 属性切换的
   兼容路径。模式 0 的标准路径是 8.5.8b 的 .de-js-enhanced 门控（服务端不预置
   hidden），两条路径不要叠加在同一元素上。 */
.de-layout [hidden],
.de-main [hidden] { display: none !important; }

/* =============================================================================
   9. 提示条 / 警示条
   ============================================================================= */
.de-tip {
  display: flex; align-items: flex-start; gap: var(--sp-2);
  margin: 0 0 var(--sp-4);
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--info-border);
  border-left: 3px solid var(--info);
  border-radius: var(--r-md);
  background: var(--info-bg);
  color: var(--info-ink);
  font-size: var(--fs-13); line-height: var(--lh-13);
}
.de-tip__ico { flex: 0 0 auto; font-family: var(--font-symbol); font-size: 14px; line-height: 20px; }
.de-tip__body { min-width: 0; }
.de-tip__title { display: block; font-weight: var(--fw-semibold); margin-bottom: 2px; }
.de-tip--plain { border-left-width: 0; background: var(--surface-soft); border-color: var(--border); color: var(--muted-strong); }

.de-alert {
  display: flex; align-items: flex-start; gap: var(--sp-3);
  margin: 0 0 var(--sp-4);
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--neutral-border);
  border-left: 3px solid var(--neutral);
  border-radius: var(--r-md);
  background: var(--neutral-bg);
  color: var(--neutral-ink);
  font-size: var(--fs-13); line-height: var(--lh-13);
}
.de-alert__ico {
  flex: 0 0 20px; width: 20px; height: 20px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%;
  background: currentColor;
  color: var(--neutral-ink);
  font-family: var(--font-symbol);
  font-size: 12px; font-weight: var(--fw-bold); line-height: 1;
}
.de-alert__ico::before { content: "i"; color: var(--neutral-bg); }
.de-alert__body { flex: 1 1 auto; min-width: 0; }
.de-alert__title { display: block; margin-bottom: 2px; font-size: var(--fs-14); font-weight: var(--fw-semibold); }
.de-alert__close { flex: 0 0 auto; border: 0; background: transparent; color: inherit; cursor: pointer; font-size: 16px; line-height: 1; opacity: .7; }
.de-alert__close:hover { opacity: 1; }

.de-alert.de-alert--info    { background: var(--info-bg);    border-color: var(--info-border);    border-left-color: var(--info);    color: var(--info-ink); }
.de-alert.de-alert--success { background: var(--success-bg); border-color: var(--success-border); border-left-color: var(--success); color: var(--success-ink); }
.de-alert.de-alert--warn    { background: var(--warn-bg);    border-color: var(--warn-border);    border-left-color: var(--warn);    color: var(--warn-ink); }
.de-alert.de-alert--error   { background: var(--danger-bg);  border-color: var(--danger-border);  border-left-color: var(--danger);  color: var(--danger-ink); }
.de-alert.de-alert--info    .de-alert__ico { color: var(--info-ink); }
.de-alert.de-alert--success .de-alert__ico { color: var(--success-ink); }
.de-alert.de-alert--warn    .de-alert__ico { color: var(--warn-ink); }
.de-alert.de-alert--error   .de-alert__ico { color: var(--danger-ink); }
.de-alert.de-alert--info    .de-alert__ico::before { content: "i";  color: var(--info-bg); }
.de-alert.de-alert--success .de-alert__ico::before { content: "\2713"; color: var(--success-bg); }
.de-alert.de-alert--warn    .de-alert__ico::before { content: "!";  color: var(--warn-bg); }
.de-alert.de-alert--error   .de-alert__ico::before { content: "!";  color: var(--danger-bg); }

/* 体验规范 7 类状态说明条别名（与徽章语义一一对应）
   每条都含 __ico + __title + 正文，读屏与色盲用户均能理解，不依赖颜色单独表意。 */
.de-alert.de-alert--normal  { background: var(--success-bg); border-color: var(--success-border); border-left-color: var(--success); color: var(--success-ink); }
.de-alert.de-alert--confirm,
.de-alert.de-alert--remind  { background: var(--warn-bg);    border-color: var(--warn-border);    border-left-color: var(--warn);    color: var(--warn-ink); }
.de-alert.de-alert--failed  { background: var(--danger-bg);  border-color: var(--danger-border);  border-left-color: var(--danger);  color: var(--danger-ink); }
.de-alert.de-alert--normal  .de-alert__ico { color: var(--success-ink); }
.de-alert.de-alert--confirm .de-alert__ico,
.de-alert.de-alert--remind  .de-alert__ico { color: var(--warn-ink); }
.de-alert.de-alert--failed  .de-alert__ico { color: var(--danger-ink); }
.de-alert.de-alert--normal  .de-alert__ico::before { content: "\2713"; color: var(--success-bg); }
.de-alert.de-alert--confirm .de-alert__ico::before,
.de-alert.de-alert--remind  .de-alert__ico::before { content: "!"; color: var(--warn-bg); }
.de-alert.de-alert--failed  .de-alert__ico::before { content: "!"; color: var(--danger-bg); }

/* =============================================================================
   10. 状态徽章（浅底 + 深字；仅 .de-badge--primary/accent 允许反白）
   ============================================================================= */
.de-badge {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 2px 10px;
  border: 1px solid var(--neutral-border);
  border-radius: var(--r-pill);
  background: var(--neutral-bg);
  color: var(--neutral-ink);
  font-size: var(--fs-12); line-height: 18px;
  font-weight: var(--fw-semibold);
  white-space: nowrap; vertical-align: middle;
}
.de-badge::before {
  content: ""; width: 6px; height: 6px; border-radius: 50%;
  background: currentColor; opacity: .75;
}
.de-badge.de-badge--plain::before { display: none; }

.de-badge.de-badge--pending  { background: var(--warn-bg);    border-color: var(--warn-border);    color: var(--warn-ink); }   /* 待处理 */
.de-badge.de-badge--running  { background: var(--info-bg);    border-color: var(--info-border);    color: var(--info-ink); }   /* 进行中 */
.de-badge.de-badge--done     { background: var(--success-bg); border-color: var(--success-border); color: var(--success-ink); } /* 已完成 */
.de-badge.de-badge--off      { background: var(--neutral-bg); border-color: var(--neutral-border); color: var(--neutral-ink); } /* 已停用 */
.de-badge.de-badge--error    { background: var(--danger-bg);  border-color: var(--danger-border);  color: var(--danger-ink); } /* 异常 */
.de-badge.de-badge--info     { background: var(--info-bg);    border-color: var(--info-border);    color: var(--info-ink); }
.de-badge.de-badge--warn     { background: var(--warn-bg);    border-color: var(--warn-border);    color: var(--warn-ink); }
.de-badge.de-badge--success  { background: var(--success-bg); border-color: var(--success-border); color: var(--success-ink); }
.de-badge.de-badge--neutral  { background: var(--neutral-bg); border-color: var(--neutral-border); color: var(--neutral-ink); }
.de-badge.de-badge--primary  { background: var(--primary);    border-color: var(--primary);        color: #fff; }  /* 蓝底反白，允许 */

/* 10.1 语义别名：--ok / --danger / --muted 是前端改造中最常用的口语写法，
   与 --done / --error / --neutral 完全等价，颜色一律取自同一组状态令牌，
   不新增任何色值。用意是让「写对了意思」的类名一定生效，不必记别名表。 */
.de-badge.de-badge--ok     { background: var(--success-bg); border-color: var(--success-border); color: var(--success-ink); }
.de-badge.de-badge--danger { background: var(--danger-bg);  border-color: var(--danger-border);  color: var(--danger-ink); }
.de-badge.de-badge--muted  { background: var(--neutral-bg); border-color: var(--neutral-border); color: var(--neutral-ink); }
.de-badge.de-badge--accent   { background: var(--accent);     border-color: var(--accent);         color: #fff; }  /* 橙底反白，允许 */
.de-badge.de-badge--sm { padding: 1px 8px; font-size: 11px; line-height: 16px; }

/* 体验规范 7 类状态语义别名（与上面的业务状态共用同一组语义色，便于 PHP 直译文案）
   正常 --normal | 进行中 --running | 待确认 --confirm | 成功 --done/--success
   提醒 --remind | 失败 --failed/--error | 停用 --off
   所有徽章自带圆点 + 文字，不依赖颜色单独表意。 */
.de-badge.de-badge--normal { background: var(--success-bg); border-color: var(--success-border); color: var(--success-ink); }
.de-badge.de-badge--confirm,
.de-badge.de-badge--remind { background: var(--warn-bg); border-color: var(--warn-border); color: var(--warn-ink); }
.de-badge.de-badge--failed { background: var(--danger-bg); border-color: var(--danger-border); color: var(--danger-ink); }

/* =============================================================================
   11. 表格
   ============================================================================= */
.de-table-wrap { width: 100%; overflow-x: auto; margin: 0 0 var(--sp-4); border: 1px solid var(--border); border-radius: var(--r-lg); background: var(--card); }
.de-table { width: 100%; border-collapse: separate; border-spacing: 0; font-size: var(--fs-13); line-height: var(--lh-13); }
.de-table th, .de-table td { padding: 10px var(--sp-4); border-bottom: 1px solid var(--border); text-align: left; vertical-align: middle; }
.de-table th {
  background: var(--surface-soft);
  color: var(--muted-strong);
  font-size: var(--fs-12); font-weight: var(--fw-semibold);
  white-space: nowrap;
}
.de-table td { color: var(--text); }
.de-table tbody tr:hover { background: var(--surface-soft); }
.de-table tbody tr:last-child td { border-bottom: 0; }
.de-table.de-table--zebra tbody tr:nth-child(even) { background: var(--surface-soft); }
.de-table.de-table--compact th, .de-table.de-table--compact td { padding: 6px var(--sp-3); }
.de-table__num { text-align: right; font-variant-numeric: tabular-nums; }

/* =============================================================================
   12. 空态 / 键值说明 / 进度条
   ============================================================================= */
.de-empty {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--sp-2);
  padding: var(--sp-8) var(--sp-5);
  border: 1px dashed var(--border-strong);
  border-radius: var(--r-lg);
  background: var(--card);
  text-align: center;
  margin: 0 0 var(--sp-5);
}
.de-empty__ico {
  width: 48px; height: 48px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%;
  background: var(--surface-sunken);
  color: var(--muted);
  font-family: var(--font-symbol); font-size: 20px;
}
.de-empty__title { margin: 0; font-size: var(--fs-16); line-height: var(--lh-16); font-weight: var(--fw-semibold); color: var(--text-strong); }
.de-empty__desc { margin: 0; font-size: var(--fs-13); line-height: var(--lh-13); color: var(--muted-strong); max-width: 420px; }
.de-empty__actions { margin-top: var(--sp-2); display: flex; gap: var(--sp-2); flex-wrap: wrap; justify-content: center; }

.de-kv { margin: 0 0 var(--sp-4); }
.de-kv__row {
  display: grid; grid-template-columns: 132px minmax(0, 1fr);
  gap: var(--sp-4);
  padding: var(--sp-3) 0;
  border-bottom: 1px dashed var(--border);
  font-size: var(--fs-13); line-height: var(--lh-13);
}
.de-kv__row:last-child { border-bottom: 0; }
.de-kv__k { color: var(--muted-strong); font-weight: var(--fw-medium); }
.de-kv__v { color: var(--text); word-break: break-word; }

.de-progress { margin: 0 0 var(--sp-3); }
.de-progress__head { display: flex; justify-content: space-between; gap: var(--sp-3); margin-bottom: 6px; font-size: var(--fs-12); line-height: var(--lh-12); color: var(--muted-strong); }
.de-progress__val { font-weight: var(--fw-semibold); color: var(--text); font-variant-numeric: tabular-nums; }
.de-progress__track { height: 8px; border-radius: var(--r-pill); background: var(--surface-sunken); overflow: hidden; }
.de-progress__fill {
  height: 100%; width: 0;
  border-radius: var(--r-pill);
  background: var(--primary);
  transition: width var(--t);
}
.de-progress.de-progress--success .de-progress__fill { background: var(--success); }
.de-progress.de-progress--warn    .de-progress__fill { background: var(--warn); }
.de-progress.de-progress--error   .de-progress__fill { background: var(--danger); }
.de-progress.de-progress--neutral .de-progress__fill { background: var(--neutral); }

/* =============================================================================
   13. 拟人化组件（仅基于现有 name 字段：首字 + 由首字推导的渐变底色）
   -----------------------------------------------------------------------------
   渐变一律为「浅色系」，以 --primary 的蓝色系为主、其余 5 档为同明度邻近色，
   配以深色文字（--de-avatar-ink），符合「浅底配深字」铁律；
   24px 小尺寸下首字仍清晰。不使用白字、不使用图片、不引外部资源。
   ============================================================================= */
.de-avatar {
  --de-avatar-ink: var(--primary-ink);
  display: inline-flex; align-items: center; justify-content: center;
  flex: 0 0 auto;
  width: 32px; height: 32px;
  border-radius: 50%;
  background: linear-gradient(135deg, #dbeafe, #bfdbfe);
  color: var(--de-avatar-ink);
  font-size: var(--fs-13);
  font-weight: var(--fw-semibold);
  line-height: 1;
  letter-spacing: 0;
  user-select: none;
  box-shadow: inset 0 0 0 1px rgba(37, 99, 235, .12);
}
.de-avatar.de-avatar--s { width: 24px; height: 24px; font-size: 11px; font-weight: var(--fw-bold); }
.de-avatar.de-avatar--m { width: 32px; height: 32px; font-size: var(--fs-13); }
.de-avatar.de-avatar--l { width: 44px; height: 44px; font-size: var(--fs-18); }

/* 6 档浅色系渐变（均以蓝色系为基准明度，深色文字对比 ≥ 6:1） */
.de-avatar.de-avatar--t1 { --de-avatar-ink: #1e40af; background: linear-gradient(135deg, #dbeafe, #bfdbfe); }  /* 主色蓝 */
.de-avatar.de-avatar--t2 { --de-avatar-ink: #155e75; background: linear-gradient(135deg, #cffafe, #a5f3fc); }  /* 青 */
.de-avatar.de-avatar--t3 { --de-avatar-ink: #115e59; background: linear-gradient(135deg, #ccfbf1, #99f6e4); }  /* 青绿 */
.de-avatar.de-avatar--t4 { --de-avatar-ink: #92400e; background: linear-gradient(135deg, #fef3c7, #fde68a); }  /* 琥珀 */
.de-avatar.de-avatar--t5 { --de-avatar-ink: #9f1239; background: linear-gradient(135deg, #ffe4e6, #fecdd3); }  /* 玫红 */
.de-avatar.de-avatar--t6 { --de-avatar-ink: #5b21b6; background: linear-gradient(135deg, #ede9fe, #ddd6fe); }  /* 紫 */

/* 不给 tone 时按出现顺序自动轮换，PHP 只需输出首字即可 */
.de-avatar-row { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
.de-avatar-row > .de-avatar:nth-child(6n + 1) { --de-avatar-ink: #1e40af; background: linear-gradient(135deg, #dbeafe, #bfdbfe); }
.de-avatar-row > .de-avatar:nth-child(6n + 2) { --de-avatar-ink: #155e75; background: linear-gradient(135deg, #cffafe, #a5f3fc); }
.de-avatar-row > .de-avatar:nth-child(6n + 3) { --de-avatar-ink: #115e59; background: linear-gradient(135deg, #ccfbf1, #99f6e4); }
.de-avatar-row > .de-avatar:nth-child(6n + 4) { --de-avatar-ink: #92400e; background: linear-gradient(135deg, #fef3c7, #fde68a); }
.de-avatar-row > .de-avatar:nth-child(6n + 5) { --de-avatar-ink: #9f1239; background: linear-gradient(135deg, #ffe4e6, #fecdd3); }
.de-avatar-row > .de-avatar:nth-child(6n + 6) { --de-avatar-ink: #5b21b6; background: linear-gradient(135deg, #ede9fe, #ddd6fe); }
.de-avatar-row.de-avatar-row--stacked { gap: 0; }
.de-avatar-row.de-avatar-row--stacked > .de-avatar { margin-left: -8px; box-shadow: 0 0 0 2px var(--card); }
.de-avatar-row.de-avatar-row--stacked > .de-avatar:first-child { margin-left: 0; }

.de-person { display: inline-flex; align-items: center; gap: 10px; min-width: 0; }
.de-person__body { display: flex; flex-direction: column; min-width: 0; }
.de-person__name {
  font-size: var(--fs-14); line-height: 18px;
  font-weight: var(--fw-semibold); color: var(--text-strong);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.de-person__role {
  font-size: var(--fs-12); line-height: 16px; color: var(--muted-strong);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* =============================================================================
   14. 通用状态：焦点 / 禁用 / 加载（无障碍）
   ============================================================================= */
.de-layout [aria-disabled="true"],
.de-layout .is-disabled { opacity: .5; cursor: not-allowed; }
.de-layout a:focus-visible,
.de-layout button:focus-visible,
.de-layout input:focus-visible,
.de-layout select:focus-visible,
.de-layout textarea:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}
.de-loading {
  display: inline-block; width: 16px; height: 16px;
  border: 2px solid var(--primary-200);
  border-top-color: var(--primary);
  border-radius: 50%;
  animation: de-spin .7s linear infinite;
  vertical-align: -3px;
}
.de-skeleton {
  display: block; height: 14px; border-radius: var(--r-xs);
  background: linear-gradient(90deg, var(--surface-sunken) 25%, var(--surface-soft) 50%, var(--surface-sunken) 75%);
  background-size: 200% 100%;
  animation: de-shimmer 1.4s linear infinite;
}
@keyframes de-shimmer { to { background-position: -200% 0; } }

@media (prefers-reduced-motion: reduce) {
  .de-layout *, .de-layout *::before, .de-layout *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}

/* =============================================================================
   15. 旧类名作用域覆盖（极重要）
   -----------------------------------------------------------------------------
   规则：所有选择器以 .de-main 开头。未套新版布局（不在 .de-main 内）的页面
         完全不受影响；旧页面只需把原 .wrap 内 HTML 放进 .de-main 即自动统一。
   注意：元素上的行内 style="..." 优先级高于本文件任何选择器，需由 PHP 侧清理
         （见 docs/UI视觉规范.html「迁移约束」）。
   ============================================================================= */

/* 15.1 容器、面包屑、遗留页头/主菜单
   【重要 · 与体验规范 2.4 对齐】各页自写的 .topnav / .nav 返回条、header 内返回
   链接、<p class="sub"> 内嵌返回链接、features.php 的 4 条迷你菜单、footer 长串
   链接，必须由 PHP 从 DOM 中删除，不是靠 CSS 隐藏（隐藏后键盘/读屏仍会 Tab 到，
   且与左侧导航构成两套导航）。下面对 .topnav/.nav/.crumbs 的细线样式仅为
   「尚未迁移页面」的兜底降级，正式交付不应再出现这些节点。
   返回路径改由 fe-shell 共享渲染器在 .de-main__inner 顶部固定位置输出一条
   .de-crumbbar（见 8.5.9）；只放层级路径，不允许各页塞额外链接。
   页内上下文返回（「返回列表」「返回全部品牌排行」）保留但移到内容区，用 .de-backlink。 */
.de-main .wrap { max-width: var(--content-max); margin: 0 auto; padding: 0; }
.de-main > nav:not(.de-nav) { display: none; }              /* 旧横向主菜单（PHP 侧也应删除） */
.de-main .topnav,
.de-main .nav:not(.de-nav),
.de-main .crumbs {
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--border);
  border-radius: 0;
  padding: 0 0 var(--sp-3);
  margin: 0 0 var(--sp-5);
  font-size: var(--fs-13);
  line-height: var(--lh-13);
  color: var(--muted);
}
.de-main .topnav a,
.de-main .nav:not(.de-nav) a { color: var(--primary); text-decoration: none; margin-right: var(--sp-4); font-size: var(--fs-13); }
.de-main .topnav a:hover,
.de-main .nav:not(.de-nav) a:hover { text-decoration: underline; }
.de-main header:not(.de-page-head),
.de-main > header:not(.de-page-head) {
  display: flex; align-items: baseline; gap: var(--sp-3); flex-wrap: wrap;
  background: transparent; color: var(--text);
  padding: 0 0 var(--sp-3); margin: 0 0 var(--sp-4);
  border: 0; border-bottom: 1px solid var(--border);
}
.de-main header:not(.de-page-head) a { color: var(--primary); margin-left: 0; }

/* 15.2 标题与文本 */
.de-main h1 { margin: 0 0 var(--sp-1); font-size: var(--fs-28); line-height: var(--lh-28); font-weight: var(--fw-bold); color: var(--text-strong); }
.de-main h2 { margin: 0 0 var(--sp-3); font-size: var(--fs-18); line-height: var(--lh-18); font-weight: var(--fw-semibold); color: var(--text-strong); }
.de-main h3 { margin: 0 0 var(--sp-2); font-size: var(--fs-16); line-height: var(--lh-16); font-weight: var(--fw-semibold); color: var(--text-strong); }
.de-main .muted, .de-main .sub, .de-main .hint, .de-main .legend,
.de-main .desc, .de-main .note, .de-main .examples, .de-main .foot {
  color: var(--muted-strong);
  font-size: var(--fs-13);
  line-height: var(--lh-13);
}
.de-main .sub { margin: 0 0 var(--sp-5); }
.de-main .hint, .de-main .legend, .de-main .note, .de-main .examples { margin: var(--sp-2) 0 var(--sp-3); }
.de-main .foot { margin: var(--sp-7) 0 var(--sp-2); text-align: center; font-size: var(--fs-12); }
.de-main .field-title { margin: 0 0 var(--sp-2); font-size: var(--fs-14); line-height: var(--lh-14); font-weight: var(--fw-semibold); color: var(--text); }
/* 用 :where() 把链接本身降为 0 特异性，整体只有 .de-main 的 (0,1,0)。
   这样不会盖掉旧页面里 .card a / .btn / .topnav a 等已带背景色的链接
   （例如「进入模块 ›」是蓝底白字按钮，不能被改成蓝字）。 */
.de-main :where(a) { color: var(--primary); text-decoration: none; }
.de-main :where(a:hover) { text-decoration: underline; }
/* 新组件 a.de-btn 明确去下划线并保住反白字 */
.de-main a.de-btn, .de-main a.de-btn:hover { text-decoration: none; }
.de-main a.de-btn.de-btn--primary,
.de-main a.de-btn.de-btn--danger,
.de-main a.de-btn.de-btn--accent { color: #fff; }
.de-main a.de-btn.de-btn--secondary { color: var(--text); }
.de-main a.de-btn.de-btn--secondary:hover { color: var(--primary-ink); }
.de-main a.de-btn.de-btn--light { color: var(--primary-ink); }
.de-main a.de-btn.de-btn--light:hover { color: var(--primary-active); }

/* 15.3 卡片 / 面板（保留旧页面的 padding，避免内容贴边） */
.de-main .card,
.de-main .kpi-card,
.de-main .box,
.de-main .panel,
.de-main .preview,
.de-main .result {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-1);
  padding: var(--sp-5);
  margin: 0 0 var(--sp-5);
  min-width: 0;
  transition: box-shadow var(--t), border-color var(--t);
}
.de-main .card:hover, .de-main .kpi-card:hover { border-color: var(--primary-100); box-shadow: var(--sh-2); }
.de-main .card h1, .de-main .card h2, .de-main .card h3 { margin-top: 0; }
.de-main .card h2 { font-size: var(--fs-16); margin-bottom: var(--sp-3); }
.de-main .card h3 { font-size: var(--fs-16); margin-bottom: var(--sp-2); }
.de-main .card p { margin: 0 0 var(--sp-3); font-size: var(--fs-13); line-height: var(--lh-13); color: var(--muted-strong); }

/* 15.4 栅格 / 行 / 数据小卡 */
.de-main .row, .de-main .stats, .de-main .actions, .de-main .form-actions {
  display: flex; gap: var(--sp-4); flex-wrap: wrap; align-items: flex-start;
  margin: 0 0 var(--sp-4);
}
.de-main .row > div, .de-main .stats > * { flex: 1 1 220px; min-width: 0; }
.de-main .form-actions { margin-top: var(--sp-4); }
.de-main .grid, .de-main .grid2, .de-main .grid3,
.de-main .filters, .de-main .meta {
  display: grid; gap: var(--sp-4); margin: 0 0 var(--sp-4);
}
.de-main .grid { grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); }
.de-main .grid2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.de-main .grid3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.de-main .filters { grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); }
.de-main .meta { grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 0 var(--sp-5); }
.de-main .meta > div { border-bottom: 1px dashed var(--border); padding: 6px 0; font-size: var(--fs-13); }
.de-main .full { grid-column: 1 / -1; }
.de-main .stat {
  background: var(--card); border: 1px solid var(--border);
  border-radius: var(--r-lg); box-shadow: var(--sh-1);
  padding: var(--sp-4) var(--sp-5); min-width: 0;
}
.de-main .stat b, .de-main .stat .n, .de-main .score-big,
.de-main .num, .de-main .count {
  display: block; font-size: var(--fs-28); line-height: var(--lh-28);
  font-weight: var(--fw-bold); color: var(--primary);
  font-variant-numeric: tabular-nums;
}
.de-main .stat .l, .de-main .stat .k, .de-main .lbl, .de-main .km {
  font-size: var(--fs-12); line-height: var(--lh-12); color: var(--muted-strong);
}

/* 15.5 表格 */
.de-main table {
  width: 100%; border-collapse: separate; border-spacing: 0;
  border: 1px solid var(--border); border-radius: var(--r-lg);
  background: var(--card); overflow: hidden;
  font-size: var(--fs-13); line-height: var(--lh-13);
  margin: 0 0 var(--sp-4);
}
.de-main th, .de-main td {
  text-align: left; padding: 10px var(--sp-4);
  border-bottom: 1px solid var(--border); vertical-align: middle;
}
.de-main th { background: var(--surface-soft); color: var(--muted-strong); font-size: var(--fs-12); font-weight: var(--fw-semibold); white-space: nowrap; }
.de-main td { color: var(--text); }
.de-main tbody tr:hover { background: var(--surface-soft); }
.de-main tr:last-child td { border-bottom: 0; }

/* 15.6 表单元素（高特异性，压过各页内联 <style>）
   错误态通过继承的自定义属性生效，避免被本段高特异性基座选择器压掉。 */
.de-main input:not([type="radio"]):not([type="checkbox"]):not([type="hidden"]):not([type="submit"]):not([type="button"]):not([type="reset"]):not([type="file"]),
.de-main select,
.de-main textarea {
  display: block;
  width: 100%; max-width: 100%;
  min-height: 38px;
  margin: 0 0 var(--sp-3);
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--de-field-border);
  border-radius: var(--r-md);
  background: var(--card);
  color: var(--text);
  font-family: inherit;
  font-size: var(--fs-14);
  line-height: var(--lh-14);
  transition: border-color var(--t-fast), box-shadow var(--t-fast), background var(--t-fast);
}
.de-main textarea { min-height: 88px; height: auto; resize: vertical; }
.de-main input::placeholder, .de-main textarea::placeholder { color: var(--placeholder); }
.de-main input:not([type="radio"]):not([type="checkbox"]):not([type="hidden"]):not([type="submit"]):not([type="button"]):not([type="reset"]):not([type="file"]):focus,
.de-main select:focus,
.de-main textarea:focus {
  outline: none;
  border-color: var(--de-field-focus);
  box-shadow: var(--de-field-ring);
}
.de-main input:disabled, .de-main select:disabled, .de-main textarea:disabled {
  background: var(--surface-sunken); color: var(--muted); cursor: not-allowed; box-shadow: none;
}
/* 表单错误态：通过自定义属性继承，避免被上面的高特异性选择器压掉 */
.de-main .is-invalid,
.de-main [aria-invalid="true"],
.de-main .has-error {
  --de-field-border: var(--danger);
  --de-field-focus: var(--danger);
  --de-field-ring: var(--ring-danger);
}
.de-main .is-invalid,
.de-main [aria-invalid="true"] { border-color: var(--danger) !important; }
.de-main label {
  display: block; margin: 0 0 6px;
  font-size: var(--fs-13); line-height: var(--lh-13);
  font-weight: var(--fw-medium); color: var(--muted-strong);
}
.de-main input[type="radio"], .de-main input[type="checkbox"] {
  width: 16px; height: 16px; margin: 0 6px 0 0;
  accent-color: var(--primary); vertical-align: -3px; cursor: pointer;
}
.de-main input[type="file"] { width: 100%; font-size: var(--fs-13); color: var(--muted-strong); }
.de-main form.inline, .de-main .inline { display: inline; }
.de-main fieldset { margin: 0 0 var(--sp-4); padding: var(--sp-4); border: 1px solid var(--border); border-radius: var(--r-lg); }
.de-main legend { padding: 0 6px; font-size: var(--fs-13); font-weight: var(--fw-semibold); color: var(--muted-strong); }

/* 15.7 按钮（旧 .btn / 裸 <button> / input[type=submit]） */
.de-main button,
.de-main input[type="submit"],
.de-main input[type="button"] {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  min-height: 36px; padding: 0 var(--sp-4);
  border: 1px solid var(--primary); border-radius: var(--r-md);
  background: var(--primary); color: #fff;
  font-family: inherit; font-size: var(--fs-14); font-weight: var(--fw-semibold);
  line-height: 1; text-decoration: none; white-space: nowrap;
  cursor: pointer; vertical-align: middle;
  transition: background var(--t-fast), border-color var(--t-fast), color var(--t-fast), box-shadow var(--t-fast), transform var(--t-fast);
}
.de-main .btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  min-height: 36px; padding: 0 var(--sp-4);
  border: 1px solid var(--primary); border-radius: var(--r-md);
  background: var(--primary); color: #fff;
  font-family: inherit; font-size: var(--fs-14); font-weight: var(--fw-semibold);
  line-height: 1; text-decoration: none; white-space: nowrap;
  cursor: pointer; vertical-align: middle;
  transition: background var(--t-fast), border-color var(--t-fast), color var(--t-fast), box-shadow var(--t-fast), transform var(--t-fast);
}
.de-main .btn:hover, .de-main .btn:focus { color: #fff; text-decoration: none; background: var(--primary-hover); border-color: var(--primary-hover); }
.de-main .btn:active { transform: translateY(1px); background: var(--primary-active); }
.de-main button:hover, .de-main input[type="submit"]:hover, .de-main input[type="button"]:hover { background: var(--primary-hover); border-color: var(--primary-hover); }
.de-main .btn:disabled, .de-main .btn.disabled,
.de-main button:disabled, .de-main button.disabled { opacity: .5; cursor: not-allowed; pointer-events: none; }
.de-main .btn:focus-visible, .de-main button:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; box-shadow: var(--ring); }

/* 按钮尺寸与变体 */
.de-main .btn.sm, .de-main .btn.small { min-height: 30px; padding: 0 var(--sp-3); font-size: var(--fs-13); }
.de-main .btn.lg { min-height: 42px; padding: 0 var(--sp-5); font-size: var(--fs-16); }
.de-main .btn.ghost, .de-main .btn.muted, .de-main .btn.gray,
.de-main .btn.b-muted, .de-main .btn.b-gray {
  background: var(--card); border-color: var(--border-strong); color: var(--text);
}
.de-main .btn.ghost:hover, .de-main .btn.muted:hover, .de-main .btn.gray:hover,
.de-main .btn.b-muted:hover, .de-main .btn.b-gray:hover { background: var(--primary-50); border-color: var(--primary-200); color: var(--primary-ink); }
.de-main .btn.accent, .de-main .btn.s, .de-main .btn.b-accent, .de-main .btn.warn {
  background: var(--accent); border-color: var(--accent); color: #fff;
}
.de-main .btn.accent:hover, .de-main .btn.s:hover, .de-main .btn.b-accent:hover, .de-main .btn.warn:hover { background: var(--accent-hover); border-color: var(--accent-hover); color: #fff; }
.de-main .btn.danger, .de-main .btn.b-danger { background: var(--danger); border-color: var(--danger); color: #fff; }
.de-main .btn.danger:hover, .de-main .btn.b-danger:hover { background: var(--danger-strong); border-color: var(--danger-strong); color: #fff; }
.de-main .btn.light, .de-main .btn.b-soft, .de-main .btn.soft { background: var(--primary-50); border-color: transparent; color: var(--primary-ink); }
.de-main .btn.light:hover, .de-main .btn.b-soft:hover, .de-main .btn.soft:hover { background: var(--primary-100); color: var(--primary-active); }
/* 注意：.btn.s 在 index.php 中是「强调色」而非小号，本文件按强调色处理；
   小号统一用 .btn.sm / .de-btn--sm。 */
/* 组件内部的小按钮（关闭等）不能被裸 button 规则染成主按钮 */
.de-main .de-alert__close {
  min-height: 0; padding: 0; border: 0;
  background: transparent; color: inherit;
  font-size: var(--fs-16); line-height: 1; box-shadow: none; opacity: .7;
}
.de-main .de-alert__close:hover,
.de-main .de-alert__close:focus-visible {
  background: transparent; border: 0; color: inherit;
  box-shadow: none; opacity: 1; transform: none;
}

/* 15.8 提示 / 错误 / 成功反馈 */
.de-main .err, .de-main .error, .de-main .alert-error {
  background: var(--danger-bg);
  border: 1px solid var(--danger-border);
  border-left: 3px solid var(--danger);
  border-radius: var(--r-md);
  padding: var(--sp-3) var(--sp-4);
  margin: 0 0 var(--sp-4);
  color: var(--danger-ink);
  font-size: var(--fs-13); line-height: var(--lh-13);
}
.de-main .flash, .de-main .success, .de-main .alert-success {
  background: var(--success-bg);
  border: 1px solid var(--success-border);
  border-left: 3px solid var(--success);
  border-radius: var(--r-md);
  padding: var(--sp-3) var(--sp-4);
  margin: 0 0 var(--sp-4);
  color: var(--success-ink);
  font-size: var(--fs-13); line-height: var(--lh-13);
}
.de-main .tip, .de-main .notice, .de-main .alert-info {
  background: var(--info-bg);
  border: 1px solid var(--info-border);
  border-left: 3px solid var(--info);
  border-radius: var(--r-md);
  padding: var(--sp-3) var(--sp-4);
  margin: 0 0 var(--sp-4);
  color: var(--info-ink);
  font-size: var(--fs-13); line-height: var(--lh-13);
}
.de-main .warn, .de-main .alert-warn {
  background: var(--warn-bg);
  border: 1px solid var(--warn-border);
  border-left: 3px solid var(--warn);
  border-radius: var(--r-md);
  padding: var(--sp-3) var(--sp-4);
  margin: 0 0 var(--sp-4);
  color: var(--warn-ink);
  font-size: var(--fs-13); line-height: var(--lh-13);
}

/* 15.9 徽章 / 标签 / 状态（浅底深字；蓝/橙底允许白字） */
.de-main .badge, .de-main .tag, .de-main .chip, .de-main .pill,
.de-main .status, .de-main .scope-badge, .de-main .brand-tag,
.de-main .badge-soft, .de-main .badge-blue, .de-main .badge-gray,
.de-main .badge-orange, .de-main .bg-blue, .de-main .bg-orange, .de-main .bg-gray {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 2px 10px;
  border: 1px solid var(--neutral-border);
  border-radius: var(--r-pill);
  background: var(--neutral-bg);
  color: var(--neutral-ink);
  font-size: var(--fs-12); line-height: 18px;
  font-weight: var(--fw-semibold);
  white-space: nowrap; vertical-align: middle;
}
/* 成功 / 正常 / 启用 → 绿 */
.de-main .badge.ok, .de-main .badge.active, .de-main .badge.done, .de-main .badge.success,
.de-main .status.ok, .de-main .chip.ok, .de-main .tag.ok, .de-main .pill.ok {
  background: var(--success-bg); border-color: var(--success-border); color: var(--success-ink);
}
/* 蓝底反白徽章（允许白字） */
.de-main .bg-blue, .de-main .badge-blue, .de-main .badge.primary, .de-main .scope-badge {
  background: var(--primary); border-color: var(--primary); color: #fff;
}
/* 橙底反白徽章（允许白字） */
.de-main .bg-orange, .de-main .badge-orange, .de-main .badge.canary, .de-main .badge.tpl {
  background: var(--accent); border-color: var(--accent); color: #fff;
}
/* 警告 / 待处理 → 黄 */
.de-main .badge.warn, .de-main .badge.pending, .de-main .chip.warn,
.de-main .tag.m, .de-main .pill.warn, .de-main .badge.accent {
  background: var(--warn-bg); border-color: var(--warn-border); color: var(--warn-ink);
}
/* 危险 / 异常 / 停用异常态 → 红 */
.de-main .badge.no, .de-main .badge.danger, .de-main .badge.error,
.de-main .chip.danger, .de-main .tag.h, .de-main .status.bad, .de-main .pill.danger {
  background: var(--danger-bg); border-color: var(--danger-border); color: var(--danger-ink);
}
/* 中性 / 停用 / 草稿 */
.de-main .bg-gray, .de-main .badge-gray, .de-main .badge.rb, .de-main .badge.draft,
.de-main .badge.off, .de-main .badge.gray, .de-main .chip {
  background: var(--neutral-bg); border-color: var(--neutral-border); color: var(--neutral-ink);
}
/* 信息 / 蓝字浅蓝底 */
.de-main .badge.info, .de-main .badge.h, .de-main .tag.info, .de-main .pill.info {
  background: var(--info-bg); border-color: var(--info-border); color: var(--info-ink);
}
.de-main .status { gap: 8px; padding: var(--sp-2) var(--sp-4); border-radius: var(--r-md); font-weight: var(--fw-semibold); }

/* 15.10 代码 / 预格式 / 空态 / 键值 / 进度 / 图表 */
.de-main .code, .de-main .mono, .de-main code {
  font-family: var(--font-mono); font-size: var(--fs-12);
  background: var(--surface-sunken); color: var(--text);
  padding: 1px 6px; border-radius: var(--r-xs);
}
.de-main pre, .de-main .pre, .de-main .body-text, .de-main .sql-box {
  background: var(--surface-soft);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: var(--sp-3) var(--sp-4);
  margin: 0 0 var(--sp-4);
  font-family: var(--font-mono); font-size: var(--fs-12); line-height: 1.7;
  color: var(--text);
  white-space: pre-wrap; word-break: break-word;
  max-height: 360px; overflow: auto;
}
.de-main .empty, .de-main .empty-state {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--sp-2);
  padding: var(--sp-8) var(--sp-5);
  border: 1px dashed var(--border-strong);
  border-radius: var(--r-lg);
  background: var(--card);
  color: var(--muted-strong);
  font-size: var(--fs-14); text-align: center;
  margin: 0 0 var(--sp-5);
}
.de-main .kv, .de-main .detail-row {
  display: grid; grid-template-columns: 132px minmax(0, 1fr);
  gap: var(--sp-4); padding: var(--sp-3) 0;
  border-bottom: 1px dashed var(--border);
  font-size: var(--fs-13); line-height: var(--lh-13);
}
.de-main .k, .de-main .lbl, .de-main .km { color: var(--muted-strong); font-size: var(--fs-12); }
.de-main .v { color: var(--text); word-break: break-word; }
.de-main .bar-track { background: var(--surface-sunken); border-radius: var(--r-pill); height: 10px; overflow: hidden; }
.de-main .bar-fill { height: 100%; border-radius: var(--r-pill); background: var(--primary); }
.de-main .bar-label, .de-main .bar-val { font-size: var(--fs-12); color: var(--muted-strong); }
.de-main .right { margin-left: auto; }
.de-main .top { vertical-align: top; }
.de-main .gray, .de-main .b-muted { color: var(--muted-strong); }
.de-main .danger { color: var(--danger-ink); }
.de-main .link { color: var(--primary); }

/* -----------------------------------------------------------------------------
   15.11 补充旧类名覆盖（主理人实测清单：.login / .step / .ok / .n / footer /
        .tag.h / .tag.m / .msg / .dot / 以及栅格内卡片的间距协调）
   ----------------------------------------------------------------------------- */
.de-main .login { max-width: 520px; margin: 0 auto; }
.de-main footer {
  max-width: var(--content-max);
  margin: var(--sp-7) auto 0;
  padding: var(--sp-3) 0 0;
  border-top: 1px solid var(--border);
  color: var(--muted);
  font-size: var(--fs-12); line-height: var(--lh-12);
}
.de-main footer a { color: var(--muted-strong); font-weight: var(--fw-regular); }

/* 状态文字 / 消息块：成功绿、失败红，均带文字说明，不靠颜色单独表意 */
.de-main .ok { color: var(--success-ink); }
.de-main .n {
  font-size: var(--fs-22); line-height: var(--lh-22);
  font-weight: var(--fw-bold); color: var(--primary);
  font-variant-numeric: tabular-nums;
}
.de-main .msg {
  padding: var(--sp-3) var(--sp-4); margin: 0 0 var(--sp-4);
  border: 1px solid var(--neutral-border); border-left: 3px solid var(--neutral);
  border-radius: var(--r-md);
  background: var(--neutral-bg); color: var(--neutral-ink);
  font-size: var(--fs-13); line-height: var(--lh-13);
}
.de-main .msg.ok {
  background: var(--success-bg); border-color: var(--success-border);
  border-left-color: var(--success); color: var(--success-ink);
}
.de-main .msg.err {
  background: var(--danger-bg); border-color: var(--danger-border);
  border-left-color: var(--danger); color: var(--danger-ink);
}
.de-main .dot, .de-main .rec-dot {
  display: inline-block; width: 8px; height: 8px;
  border-radius: 50%; background: currentColor;
  margin-right: 6px; vertical-align: 1px;
}
/* 步骤条 / 步骤输出 */
.de-main .step {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  min-height: 32px; padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--border); border-radius: var(--r-md);
  background: var(--card); color: var(--muted-strong);
  font-size: var(--fs-13); line-height: var(--lh-13);
  margin: 0 var(--sp-2) var(--sp-3) 0;
}
.de-main .step.is-active, .de-main .step.active, .de-main .step.ok {
  background: var(--primary-50); border-color: var(--primary-200);
  color: var(--primary-ink); font-weight: var(--fw-semibold);
}
.de-main .step-out {
  margin: 0 0 var(--sp-3); padding: var(--sp-3) var(--sp-4);
  background: var(--surface-soft); border: 1px solid var(--border);
  border-radius: var(--r-md);
  font-family: var(--font-mono); font-size: var(--fs-12); line-height: 1.7;
  color: var(--text); white-space: pre-wrap; word-break: break-word;
}

/* 卡片放进栅格后去掉自带的下外边距，交给 grid gap（16px）统一控制，
   避免「多个 .card 纵向堆叠」改造成两列/三列时出现 36px 的不均间隙。
   .de-card 是 v1.0 组件，同样适用；此前只列了旧类名 .card，导致新组件进栅格后
   行距 32~36px、列距 16px 不一致，各页只好各写一行页内补丁。此处一次收口，
   页面不再需要各自兜底。 */
.de-main .de-grid > .de-card,
.de-main .de-grid > .card,
.de-main .grid > .card,
.de-main .grid2 > .card,
.de-main .grid3 > .card,
.de-main .filters > .card,
.de-main .meta > .card,
.de-main .row > .card,
.de-main .stats > .card,
.de-main .de-grid > .kpi-card,
.de-main .grid2 > .kpi-card,
.de-main .grid3 > .kpi-card { margin-bottom: 0; }
/* 栅格内的区块同样不应带下外边距，否则最后一个栅格行下方会多出一段空白 */
.de-main .de-grid > .de-section,
.de-main .de-grid > .de-stat { margin-bottom: 0; }
.de-main .de-grid > .stat,
.de-main .grid2 > .stat,
.de-main .grid3 > .stat,
.de-main .row > .stat { margin-bottom: 0; }
/* .row 里的 .stat 作为数据卡：统一最小宽度与栅格对齐 */
.de-main .row > .stat,
.de-main .stats > .stat { flex: 1 1 190px; }

/* -----------------------------------------------------------------------------
   15.12 保护 8.5 的新控件不被 .de-main 的旧类覆盖规则压掉
   （.de-main button 是 (0,1,1)、.de-main input[type=checkbox] 是 (0,2,1)，
    会盖过 8.5 里 (0,1,0)/(0,1,1) 的基础规则，故在此补更高特异性的作用域规则）
   ----------------------------------------------------------------------------- */
.de-main .de-repeat__btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 4px;
  min-height: 30px; padding: 0 var(--sp-3);
  border: 1px solid var(--border-strong); border-radius: var(--r-md);
  background: var(--card); color: var(--muted-strong);
  font-family: inherit; font-size: var(--fs-12); font-weight: var(--fw-semibold);
  line-height: 1; white-space: nowrap; cursor: pointer;
  box-shadow: none; transform: none;
}
.de-main .de-repeat__btn:hover { background: var(--primary-50); border-color: var(--primary-200); color: var(--primary-ink); transform: none; }
.de-main .de-repeat__btn:active { background: var(--primary-100); transform: translateY(1px); }
.de-main .de-repeat__btn:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; box-shadow: var(--ring); }
.de-main .de-repeat__btn:disabled,
.de-main .de-repeat__btn[aria-disabled="true"],
.de-main .de-repeat__btn.is-disabled { opacity: .45; cursor: not-allowed; pointer-events: none; box-shadow: none; }
.de-main .de-repeat__btn--add {
  width: 100%; border-style: dashed; border-color: var(--primary-200);
  background: var(--primary-50); color: var(--primary-ink);
}
.de-main .de-repeat__btn--add:hover { background: var(--primary-100); border-color: var(--primary); color: var(--primary-ink); }
.de-main .de-repeat__btn--del:hover { background: var(--danger-bg); border-color: var(--danger-border); color: var(--danger-ink); }
.de-main .de-choice input[type="checkbox"],
.de-main .de-choice input[type="radio"] {
  position: absolute; width: 1px; height: 1px;
  margin: 0; padding: 0; opacity: 0;
  vertical-align: baseline; accent-color: var(--primary);
}

/* =============================================================================
   16. 响应式
   -----------------------------------------------------------------------------
   ≥1200px：固定 232px 侧栏。
   900–1199px：窄栏 200px（分组可折叠），内容左右内边距收至 24px。
   <900px：侧栏由竖排变为「顶部横向可滚动条」，纯 CSS，无 JS（主理人硬性要求）。
   <640px：栅格单列、页头堆叠、内容区减小内边距。
   pointer:coarse：触控最小点击区域 44×44px。
   ============================================================================= */

/* 抽屉方案备用工具类：本项目默认不启用（<900px 走横向滚动条，零 JS）。
   若将来改用汉堡抽屉，遮罩 rgba(15,23,42,.45)、z-index 25，
   侧栏 .de-sidebar--drawer 用 z-index 26 + --sh-3；需配合 :target / checkbox 纯 CSS 触发。 */
.de-backdrop { position: fixed; inset: 0; background: rgba(15, 23, 42, .45); z-index: 25; }
.de-sidebar--drawer { position: fixed; z-index: 26; box-shadow: var(--sh-3); }

@media (min-width: 900px) and (max-width: 1199px) {
  :root { --sidebar-w: 200px; }
  .de-brand { padding: 14px; }
  .de-brand__name small { display: none; }
  .de-nav-group__title { padding-left: 14px; padding-right: 14px; }
  .de-nav__item { margin: 2px 8px; padding-left: 10px; gap: 8px; }
  .de-nav__item::before { left: -8px; }
  .de-main { padding-left: var(--sp-6); padding-right: var(--sp-6); }
}

@media (pointer: coarse) {
  .de-nav__item { min-height: 44px; }
  .de-btn, .de-main .btn, .de-main button { min-height: 44px; }
  .de-btn.de-btn--sm, .de-main .btn.sm, .de-main .btn.small { min-height: 36px; }
  .de-main input:not([type="radio"]):not([type="checkbox"]):not([type="hidden"]):not([type="submit"]):not([type="button"]):not([type="reset"]):not([type="file"]),
  .de-main select { min-height: 44px !important; }
  .de-main textarea { min-height: 88px !important; }
  .de-nav__item.is-active::before,
  .de-nav__item[aria-current="page"]::before { top: 8px; bottom: 8px; }
  /* 侧栏底部退出按钮也保证 44px 触控区（(0,3,0) 压过 <900px 里的压缩规则） */
  .de-layout .de-sidebar__logout .de-btn { min-height: 44px; padding: 0 var(--sp-3); }
  /* 8.5 新控件同样保证 44px 触控区 */
  .de-layout .de-repeat__btn { min-height: 44px; }
  .de-layout .de-choice { min-height: 44px; }
}

@media (max-width: 899px) {
  .de-layout { display: block; }
  .de-sidebar {
    position: sticky; top: 0; z-index: 30;
    width: 100%; height: auto; max-height: none;
    flex-direction: row; align-items: stretch;
    border-right: 0;
    border-bottom: 1px solid var(--border);
    box-shadow: var(--sh-1);
  }
  .de-brand {
    flex: 0 0 auto;
    padding: var(--sp-2) var(--sp-3);
    border-bottom: 0;
    border-right: 1px solid var(--border);
  }
  .de-brand__mark { width: 28px; height: 28px; flex-basis: 28px; border-radius: var(--r-md); font-size: var(--fs-13); }
  .de-brand__name { font-size: var(--fs-13); }
  .de-brand__name small { display: none; }

  .de-sidebar__scroll {
    flex: 1 1 auto;
    display: flex; flex-direction: row; align-items: center;
    overflow-x: auto; overflow-y: hidden;
    padding: 6px 0;
    scrollbar-width: thin;
  }
  .de-nav-group { display: flex; align-items: center; flex: 0 0 auto; padding: 0 var(--sp-1); }
  .de-nav-group + .de-nav-group { border-top: 0; border-left: 1px solid var(--border); margin: 0; padding: 0 var(--sp-1); }
  /* 分组标题只在视觉上隐藏，保留在 a11y 树中（读屏仍能听到分组语义）；
     同时 fe-shell 需给每个 .de-nav-group 输出 aria-label="分组名" 双保险。
     可交互的折叠标题（summary / --toggle）才真正 display:none，避免隐藏焦点项。 */
  .de-nav-group__title:not(summary):not(.de-nav-group__title--toggle) {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
  }
  .de-nav-group__title--toggle,
  details.de-nav-group > summary.de-nav-group__title { display: none; }
  /* 横向滚动容器由 PHP 输出 tabindex="0" + aria-label，键盘可聚焦并左右滚动 */
  .de-sidebar__scroll:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }
  .de-scroll-hint { display: inline-flex; flex: 0 0 auto; }
  .de-nav-group.is-collapsed .de-nav,
  details.de-nav-group:not([open]) > .de-nav {
    display: flex !important; content-visibility: visible !important;
  }  /* 横向条强制展开全部入口，避免折叠态在窄屏吞掉菜单 */
  .de-nav { flex-direction: row; align-items: center; }
  .de-nav__item { margin: 0 2px; padding: 6px 10px; min-height: 44px; white-space: nowrap; }  /* 触控 44px 最小点击区 */
  .de-nav__item::before { left: 8px; right: 8px; top: auto; bottom: 0; width: auto; height: 3px; border-radius: 3px 3px 0 0; }
  .de-nav__count { display: none; }

  .de-sidebar__user {
    flex: 0 0 auto;
    padding: 0 var(--sp-3);
    border-top: 0;
    border-left: 1px solid var(--border);
    background: var(--card);
  }
  .de-sidebar__user .de-person { display: none; }
  .de-sidebar__foot { display: none; }
  .de-sidebar__logout .de-btn { min-height: 36px; padding: 0 var(--sp-3); font-size: var(--fs-12); }

  .de-main { padding: var(--sp-4) var(--sp-4) var(--sp-8); }
  .de-grid--2, .de-grid--3 { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
  .de-main .grid2, .de-main .grid3 { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
}

@media (max-width: 639px) {
  .de-main { padding: var(--sp-4) var(--sp-3) var(--sp-8); }
  .de-main__inner { max-width: 100%; }
  .de-page-head { align-items: flex-start; flex-direction: column; gap: var(--sp-3); }
  .de-page-head__title { font-size: var(--fs-22); line-height: var(--lh-22); }
  .de-page-head__actions { width: 100%; }
  .de-grid, .de-grid--2, .de-grid--3,
  .de-main .grid, .de-main .grid2, .de-main .grid3,
  .de-main .filters, .de-main .meta { grid-template-columns: minmax(0, 1fr); }
  .de-main .row, .de-main .stats, .de-main .actions { flex-direction: column; }
  .de-main .row > div, .de-main .stats > * { flex: 1 1 auto; width: 100%; }
  .de-card__head, .de-card__body, .de-card__foot { padding-left: var(--sp-4); padding-right: var(--sp-4); }
  .de-main .card, .de-main .kpi-card, .de-main .box { padding: var(--sp-4); }
  .de-kv__row, .de-main .kv, .de-main .detail-row { grid-template-columns: 100px minmax(0, 1fr); gap: var(--sp-2); }
  .de-main table { display: block; overflow-x: auto; white-space: nowrap; }
  .de-brand__name { display: none; }
  /* 8.5 渐进增强控件窄屏堆叠：重复行 / 键值行 / 时间星期组合全部单列 */
  .de-repeat__row,
  .de-repeat__row--2,
  .de-repeat__row--3,
  .de-repeat__row--4,
  .de-repeat__row--kv { grid-template-columns: minmax(0, 1fr); align-items: stretch; }
  .de-repeat__actions { justify-content: flex-end; padding-top: var(--sp-1); }
  .de-schedule__row { flex-direction: column; gap: var(--sp-3); }
  .de-schedule__time, .de-schedule__days { flex: 1 1 auto; width: 100%; }
  .de-crumbbar { gap: 4px; }
}

/* =============================================================================
   第 16 节：提示框语义别名（与既有修饰符等价，供页面按语义直接使用）
   ---- 已有 --info / --success / --warn / --error，另有 --normal / --failed /
        --remind / --confirm 四个历史别名。前端改造中普遍使用 --ok / --danger
        两种更口语的写法，此处统一补齐，避免各页自行造色值。 ------------- */
.de-alert.de-alert--ok,
.de-alert.de-alert--danger { border-left-width: 4px; }
.de-alert.de-alert--ok {
  background: var(--success-bg); border-color: var(--success-border);
  border-left-color: var(--success); color: var(--success-ink);
}
.de-alert.de-alert--danger {
  background: var(--danger-bg); border-color: var(--danger-border);
  border-left-color: var(--danger); color: var(--danger-ink);
}
.de-alert.de-alert--ok     .de-alert__ico { color: var(--success-ink); }
.de-alert.de-alert--danger .de-alert__ico { color: var(--danger-ink); }
.de-alert.de-alert--ok     .de-alert__ico::before { content: "\2713"; color: var(--success-bg); }
.de-alert.de-alert--danger .de-alert__ico::before { content: "!";  color: var(--danger-bg); }

/* =============================================================================
   第 17 节：公开页容器（登录前的 marketplace / register 等页面）
   ---- 这些页面不输出 .de-layout / .de-sidebar / .de-crumbbar（体验规范 6.4），
        但要与内部页共用同一套令牌、字号、按钮与表单，不能各写一套。 -------- */
.de-public {
  min-height: 100vh;
  background: var(--bg);
  color: var(--text);
  padding: var(--sp-7) var(--sp-5) var(--sp-8);
  font-family: var(--font);
  font-size: 15px;
  line-height: 1.7;
  -webkit-font-smoothing: antialiased;
}
.de-public *,
.de-public *::before,
.de-public *::after { box-sizing: border-box; }

.de-public__wrap {
  width: 100%;
  max-width: 880px;
  margin: 0 auto;
}
.de-public--narrow .de-public__wrap { max-width: 520px; }

.de-public__brand {
  display: flex; align-items: center; gap: var(--sp-3);
  margin: 0 0 var(--sp-6);
}
.de-public__mark {
  width: 40px; height: 40px; flex: 0 0 40px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--r-md);
  background: var(--primary-50);
  color: var(--primary-ink);
  border: 1px solid var(--primary-100);
  font-weight: 700; font-size: 18px;
}
.de-public__name { font-size: 17px; font-weight: 700; color: var(--text-strong); line-height: 1.3; }
.de-public__tagline { font-size: 13px; color: var(--muted-strong); }

.de-public__head { margin: 0 0 var(--sp-5); }
.de-public__title {
  margin: 0 0 var(--sp-2);
  font-size: 26px; font-weight: 700; letter-spacing: -.01em;
  color: var(--text-strong); line-height: 1.35;
}
.de-public__desc { margin: 0; font-size: 14px; color: var(--muted-strong); }

.de-public__foot {
  margin: var(--sp-6) 0 0;
  padding-top: var(--sp-4);
  border-top: 1px solid var(--border);
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: var(--sp-3);
  font-size: 13px; color: var(--muted);
}

/* 公开页里的卡片与表单沿用内部页组件，只把外边距收紧一档 */
.de-public .de-card { margin-bottom: var(--sp-4); }
.de-public .de-section:last-child { margin-bottom: 0; }

@media (max-width: 640px) {
  .de-public { padding: var(--sp-5) var(--sp-4) var(--sp-7); }
  .de-public__title { font-size: 22px; }
}

/* =============================================================================
   第 20 节：视觉升级 v1.1 · 浅色科技感（AI 调性）
   -----------------------------------------------------------------------------
   【方向】用户要求"更具 AI、科技感，但不要用深色系"。做法不是加深色面板，
     而是三件事：① 低饱和蓝→靛紫渐变（只用于点缀：品牌标、主按钮、选中条、
     页头发丝线、卡片 hover 顶线）；② 极淡点阵 + 双光晕背景，让页面"有空间感"
     而不是一片死白；③ 微光与内描边，制造轻微的"界面发光"质感。
   【铁律】所有表面仍是 #ffffff / #f6f8fb，正文仍是深色；渐变只做小面积点缀，
     不铺满大块背景，保证文字对比度与打印可读性。本节的规则均写在文件末尾，
     同特异性下后写者生效，用来覆盖前面各节的对应值。
   ============================================================================= */
html {
  --ai-grad:      linear-gradient(135deg, #2563eb 0%, #4f46e5 55%, #7c3aed 100%);
  --ai-grad-weak: linear-gradient(135deg, rgba(37, 99, 235, .14), rgba(124, 58, 237, .08));
  --ai-line:      linear-gradient(90deg, #2563eb, #7c3aed);
  --ai-glow:      0 6px 18px -6px rgba(37, 99, 235, .55);
}

/* 20.1 页面底：双光晕 + 点阵。fixed 让滚动时背景稳定，不会出现"背景跟着卡片跑" */
.de-layout,
.de-public {
  background-color: var(--bg);
  background-image:
    radial-gradient(900px 420px at 6% -8%,  rgba(37, 99, 235, .09), transparent 62%),
    radial-gradient(820px 420px at 100% -2%, rgba(124, 58, 237, .07), transparent 58%),
    radial-gradient(rgba(37, 99, 235, .07) 1px, transparent 1px);
  background-size: auto, auto, 22px 22px;
  background-attachment: fixed, fixed, fixed;
}

/* 20.2 侧栏：半透明玻璃 + 右侧渐隐投影，和白底内容区形成层次但不割裂 */
.de-sidebar {
  background: linear-gradient(180deg, rgba(255, 255, 255, .94) 0%, rgba(249, 251, 255, .97) 58%, #ffffff 100%);
  border-right: 1px solid var(--border);
  box-shadow: 1px 0 0 rgba(37, 99, 235, .05), 16px 0 40px -32px rgba(15, 23, 42, .28);
}

/* 20.3 品牌标：渐变底 + 微光，是整页唯一允许"高饱和"的小色块 */
.de-brand__mark {
  background: var(--ai-grad);
  color: #fff;
  box-shadow: 0 6px 16px -6px rgba(79, 70, 229, .60), inset 0 1px 0 rgba(255, 255, 255, .38);
}

/* 20.4 分组标题：前置渐变细条，替代纯文字分组 */
.de-nav-group__title {
  display: flex; align-items: center; gap: 8px;
}
.de-nav-group__title::before {
  content: "";
  width: 3px; height: 11px; border-radius: 2px;
  background: var(--ai-line);
  opacity: .75;
}

/* 20.5 导航项：hover 轻微染色，选中项渐变底 + 渐变发光竖条 */
.de-nav__item:hover { background: rgba(37, 99, 235, .06); }
.de-layout .de-sidebar .de-nav__item.is-active,
.de-layout .de-sidebar .de-nav__item[aria-current="page"] {
  background: linear-gradient(90deg, rgba(37, 99, 235, .14), rgba(124, 58, 237, .07));
  box-shadow: inset 0 0 0 1px rgba(37, 99, 235, .14);
  color: var(--primary-ink);
}
.de-layout .de-sidebar .de-nav__item.is-active::before,
.de-layout .de-sidebar .de-nav__item[aria-current="page"]::before {
  background: var(--ai-line);
  box-shadow: 0 0 10px rgba(79, 70, 229, .55);
}

/* 20.6 主按钮：渐变 + 落影，hover 提亮（只改主按钮，次级按钮保持白底描边） */
.de-btn.de-btn--primary {
  background: var(--ai-grad);
  border-color: transparent;
  color: #fff;
  box-shadow: var(--ai-glow);
}
.de-btn.de-btn--primary:hover {
  background: var(--ai-grad);
  border-color: transparent;
  filter: brightness(1.07);
  box-shadow: 0 10px 24px -8px rgba(37, 99, 235, .62);
}
.de-btn.de-btn--primary:active { filter: brightness(.96); }

/* 20.7 页头：底部渐变发丝线，长度 132px，作为每页的"科技锚点" */
.de-page-head { position: relative; }
.de-page-head::after {
  content: "";
  position: absolute; left: 0; bottom: -1px;
  width: 132px; height: 2px; border-radius: 2px;
  background: var(--ai-line);
}

/* 20.8 卡片：hover 时顶部浮出渐变发丝线 + 柔和外发光（不改卡片底色） */
.de-card { position: relative; }
.de-card::before {
  content: "";
  position: absolute; inset: 0 0 auto 0; height: 2px;
  background: var(--ai-line);
  opacity: 0;
  transition: opacity .18s ease;
  pointer-events: none;
}
.de-card:hover::before { opacity: .9; }
.de-card:hover { box-shadow: 0 10px 28px -18px rgba(37, 99, 235, .45); }

/* 20.9 表头：浅渐变底，让数据表也有层次但不抢内容 */
.de-table th,
.de-main table thead th {
  background: linear-gradient(180deg, #f8fafc 0%, #f1f5f9 100%);
}

/* 20.10 面包屑：半透明白 + 模糊，滚动时与背景点阵有层次 */
.de-crumbbar {
  background: rgba(255, 255, 255, .72);
  backdrop-filter: blur(6px);
  padding-top: 8px;
}

/* 20.11 媒体卡：媒体区叠一层极淡点阵，强化"数字/虚拟"质感。
   放 ::after 并压到内容之下，字符与小徽标用 z-index 提到上层。 */
.de-media::after {
  content: "";
  position: absolute; inset: 0;
  background-image: radial-gradient(rgba(255, 255, 255, .55) 1px, transparent 1px);
  background-size: 14px 14px;
  opacity: .4;
  pointer-events: none;
  z-index: 0;
}
.de-media__glyph,
.de-media__chip { position: relative; z-index: 1; }

/* 20.12 统计卡：右上角一层极淡光晕 + 图标渐变底，让数据块有"仪表盘"质感。
   光晕是装饰，绝对定位在卡片右上、不被内容遮挡，也不影响点击。 */
.de-stat { position: relative; overflow: hidden; }
.de-stat::after {
  content: "";
  position: absolute; right: -28px; top: -28px;
  width: 96px; height: 96px; border-radius: 50%;
  background: radial-gradient(circle, rgba(37, 99, 235, .12), transparent 70%);
  pointer-events: none;
}
.de-stat__ico {
  background: var(--ai-grad-weak);
  color: var(--primary-ink);
  box-shadow: inset 0 0 0 1px rgba(37, 99, 235, .10);
}

/* 20.13 焦点态：蓝色描边 + 柔光，键盘用户更容易定位 */
.de-layout :focus-visible,
.de-public :focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
  box-shadow: 0 0 0 4px rgba(37, 99, 235, .14);
}

/* 20.14 尊重系统"减少动态效果"设置 */
@media (prefers-reduced-motion: reduce) {
  .de-card::before,
  .de-btn,
  .de-card { transition: none; }
  .de-card:hover { box-shadow: var(--sh-1); }
}

/* =============================================================================
   附录 A：左侧导航 DOM 结构约定（供 PHP 渲染函数对齐）
   -----------------------------------------------------------------------------
   <div class="de-layout">
     <aside class="de-sidebar" aria-label="主导航">
       <a class="de-brand" href="index.php?p=brand_dashboard">
         <span class="de-brand__mark" aria-hidden="true">福</span>
         <span class="de-brand__name">福宝数字员工<small>智能体工作台</small></span>
       </a>
       <div class="de-sidebar__scroll" tabindex="0" aria-label="主导航，可左右滚动">
         <span class="de-scroll-hint" aria-hidden="true">⇄ 可左右滚动</span>
         <div class="de-nav-group" aria-label="工作台">
           <div class="de-nav-group__title">工作台</div>
           <nav class="de-nav" aria-label="工作台">
             <a class="de-nav__item is-active" aria-current="page" href="...">
               <span class="de-nav__ico" aria-hidden="true">◫</span>
               <span class="de-nav__label">品牌总览</span>
               <span class="de-nav__count">3</span>
             </a>
           </nav>
         </div>
         <!-- 可重复多个 .de-nav-group -->
       </div>
       <div class="de-sidebar__user">
         <span class="de-avatar de-avatar--m de-avatar--t1">张</span>
         <span class="de-person">
           <span class="de-person__body">
             <b class="de-person__name">张三</b>
             <small class="de-person__role">品牌管理员</small>
           </span>
         </span>
         <form class="de-sidebar__logout" method="post"> ... </form>
       </div>
     </aside>
     <main class="de-main" id="de-content">
       <div class="de-main__inner">
         <!-- 全局统一面包屑：由共享渲染器固定输出，只放层级路径 -->
         <nav class="de-crumbbar" aria-label="当前位置">
           <a href="...">全部能力</a><span class="de-crumbbar__sep">/</span>
           <a href="...">知识与资料</a><span class="de-crumbbar__sep">/</span>
           <span class="de-crumbbar__current" aria-current="page">资料自动更新</span>
         </nav>
         <header class="de-page-head"> ... </header>
         <!-- 旧页面：把原 <div class="wrap"> 内的 HTML 原样粘贴到这里即可；
              各页自己的 .topnav / .nav 返回条必须已从 DOM 删除 -->
       </div>
     </main>
   </div>

   <900px 无障碍（由 fe-shell PHP 侧配合）：
     · 每个 .de-nav-group 都要带 aria-label="分组名"；分组标题视觉隐藏但不删，
       分组强制展开；可左右滚动提示 .de-scroll-hint 由 CSS 在 <900px 显示。
     · .de-sidebar__scroll 需 tabindex="0" + aria-label="主导航，可左右滚动"。
   6.4 的 5 个登录/公开页不输出 .de-layout，也不输出 .de-crumbbar。

   public/ui.php 的 deUiStart() 按此结构输出；它另在 .de-layout 之前输出
   <a class="de-skip de-sr-only" href="#de-content">跳到主要内容</a>，
   并在 <main class="de-main" id="de-content"> 上放锚点，CSS 见 2.1 的 .de-skip:focus 规则。
   选中态二选一：class="... is-active" 或 aria-current="page"（两者等效）。
   图标字符：请用「几何图形 / 杂项技术符号」区块的单色字符（◫ ◧ ▤ ▦ ◎ ▣ ◈ ◇ ⬡ ⊕ ⌂ ■ □ ▲ ◆ ●），
   禁止使用 emoji（👤 ✅ ⚠️ 🔔 等），避免多色、跨平台字形不一致。
   ============================================================================= */
