/* ═══ 组件层 ═══
   规格的权威处是 design/委托/05-components.md，不是 .dc.html 板子。
   七种态：默认 / hover / focus / active / disabled / loading / error。
   三条通用禁令：
     · hover 不引起布局位移（不改尺寸、不加位移阴影、不改字重）——鼠标扫过时列表不能抖
     · **opacity 一律不用来弱化任何东西——这条绑机制，不绑状态名**
       （设计线 2026-09-06 `238bd21` 从 disabled 那一行扩上来的：绑状态名时管不住
       同一个机制在「冻结 / 暂停 / 只读 / 加载中」下的用法，而后果一模一样）。
       理由是**可算不可算**：opacity 把它盖住的**每一个**前景色同时拖向背景色，
       每一个的结果对比度都不一样也算不出来；要弱化就换色值，要盖一整块用定额遮罩。
       ⚠️ 它还有一个不在对比度上的后果：**半透明会把「用错了色」伪装成「色淡了一点」**
       （设计线实测：未读锚行一条严重度红 `#B0392C` + `opacity:.35` 看起来像中性色，
       八轮没人发现）。**所以排查顺序是先按机制搜 opacity，再看它盖住了什么。**
       两个合法用途（按「结果还算不算得出来」划，不按「装饰还是语义」划）：
       **@keyframes 动画**（配 prefers-reduced-motion）· **盖住的前景只有一种颜色时**
       （例：`.chip .x` 那枚 currentColor 单色 ✕——一种颜色压一种底，结果是一个可算的值）
     · loading 不用骨架屏——每分钟刷新的产品，闪一次就是每分钟闪一次；用上次的值置灰 */

/* ── 按钮：三档高度 × 三档强度，九种封顶 ── */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2);
  height: var(--control-h-md); padding: 0 var(--space-3); border-radius: var(--radius-md);
  font-size: var(--size-base); font-family: inherit; cursor: pointer;
  border: 1px solid transparent; white-space: nowrap;
  transition: background var(--motion-duration-user-initiated);
}
.btn.lg { height: var(--control-h-lg); padding: 0 var(--space-4); font-size: var(--size-md); }
.btn.sm { height: var(--control-h-sm); padding: 0 var(--space-2); font-size: var(--size-sm);
          border-radius: var(--radius-sm); }
.btn.pri { background: var(--c-neutral-900); color: var(--c-neutral-0); font-weight: 500; }
.btn.pri:hover  { background: var(--c-neutral-800); }
.btn.pri:active { background: var(--c-neutral-600); }
.btn.sec { background: var(--c-neutral-0); border-color: var(--border-control); color: var(--c-neutral-900); }
.btn.sec:hover  { background: var(--c-neutral-50); border-color: var(--border-strong); }
.btn.sec:active { background: var(--c-neutral-100); }
.btn.gho { background: none; color: var(--c-neutral-600); }
.btn.gho:hover  { background: var(--c-neutral-50); color: var(--c-neutral-900); }
.btn.gho:active { background: var(--c-neutral-100); }
.btn[disabled], .btn[aria-disabled="true"] {
  background: var(--c-neutral-50); color: var(--c-neutral-500);
  border-color: var(--c-neutral-100); cursor: default; pointer-events: none;
}
/* loading 写动词（「分享中」），不转圈：用户要知道在做什么，不是知道在忙。 */
.btn[data-loading] { color: var(--c-neutral-600); pointer-events: none; }
/* 纯图标按钮命中框 ≥ 32，即使视觉尺寸是 24。 */
.btn.icon { padding: 0; width: var(--control-h-md); min-width: var(--a11y-floor-target-used);
            min-height: var(--a11y-floor-target-used); }

/* ── 筛选 chip：可点，所以走 28px 控件几何，只借徽章的配色 ── */
.chip {
  display: inline-flex; align-items: center; gap: var(--space-1); height: 28px;   /* GAP chip-h-28 */
  padding: 0 10px; border-radius: var(--radius-md); font-size: var(--size-base);
  border: 1px solid var(--c-neutral-200); background: var(--c-neutral-0);
  color: var(--c-neutral-600); cursor: pointer; font-family: inherit; white-space: nowrap;
}
.chip:hover { background: var(--c-neutral-50); border-color: var(--border-control); }
.chip.on { background: var(--c-accent-wash); border-color: var(--c-accent-base);
           color: var(--c-accent-text); font-weight: 500; }
.chip .x { display: inline-flex; width: 12px; height: 12px; margin-left: var(--space-1);
           opacity: .8; }
.chip .x:hover { opacity: 1; }

/* ── 输入 · 高 32 ── */
.input, .select, .textarea {
  height: var(--control-h-md); padding: 0 var(--space-2); border-radius: var(--radius-md);
  border: 1px solid var(--border-control); background: var(--c-neutral-0);
  color: var(--c-neutral-900); font-family: inherit; font-size: var(--size-base); width: 100%;
}
.textarea { height: auto; padding: var(--space-2); line-height: var(--leading-snug); resize: vertical; }
.input:hover, .select:hover, .textarea:hover { border-color: var(--border-strong); }
.input:focus-visible, .select:focus-visible, .textarea:focus-visible {
  border-color: var(--a11y-floor-focus-color);
}
.input[disabled], .select[disabled], .textarea[disabled] {
  background: var(--c-neutral-50); color: var(--c-neutral-500); border-color: var(--c-neutral-100);
}
/* error 必须同时给边框色和图标，说明放框下——只靠红边框会漏掉色盲用户，
   也漏掉「到底哪里错了」。 */
.field.err .input, .field.err .select, .field.err .textarea { border-color: var(--c-severity-high); }
.field .msg { display: flex; align-items: center; gap: var(--space-1); margin-top: var(--space-1);
              font-size: var(--size-sm); color: var(--c-neutral-600); }
.field.err .msg { color: var(--c-severity-high); }
.field .lbl { display: block; font-size: var(--size-sm); color: var(--c-neutral-600);
              margin-bottom: var(--space-1); }

/* ── 复选框 16px：三态都是墨色，不吃语义色额度 ── */
.ck { display: inline-flex; align-items: center; justify-content: center; width: 16px; height: 16px;
      border: 1px solid var(--border-control); border-radius: var(--radius-sm);
      background: var(--c-neutral-0); cursor: pointer; flex-shrink: 0; padding: 0; }
.ck[aria-checked="true"], .ck[aria-checked="mixed"] {
  background: var(--c-neutral-900); border-color: var(--c-neutral-900); color: var(--c-neutral-0);
}
.ck svg { width: 12px; height: 12px; }
/* 命中框补到 32，视觉仍是 16。 */
.ck-hit { display: inline-flex; align-items: center; justify-content: center;
          min-width: var(--a11y-floor-target-used); min-height: var(--a11y-floor-target-used); }
/* 出现规则：行 hover 时淡入，占位一直预留，所以文字一个像素都不动。 */
.row .ck-hit { opacity: 0; transition: opacity var(--motion-duration-item-enter); }
.row:hover .ck-hit, .row:focus-within .ck-hit, .row.picked .ck-hit { opacity: 1; }

/* ── 卡片：一套骨架 + 三档严重度 ──
   档的区别是「有色 / 有形无色 / 什么都没有」，不是三种色相。
   卡片之间是 10px 真空隙不是分隔线——空隙让每张卡成为可以单独放弃的单元。 */
.card {
  border: 1px solid var(--c-neutral-100); border-radius: var(--radius-10);
  border-left: 3px solid var(--c-severity-low); padding: 11px 13px;      /* GAP bar-3 · card-pad */
  background: var(--c-neutral-0); position: relative; flex-shrink: 0; display: block;
  width: 100%; text-align: left; font-family: inherit; cursor: pointer;
}
.card:hover { background: var(--c-neutral-50); border-color: var(--c-neutral-200); }
.card.now  { border-left-color: var(--c-severity-high); }
.card.today{ border-left-color: var(--c-severity-mid); }
.card.archive { border-left-color: var(--c-severity-low); }
.card .row1 { display: flex; align-items: center; gap: var(--space-2); margin-bottom: 6px; }
.card .row1 > svg { width: 14px; height: 14px; flex-shrink: 0; }          /* GAP icon-14 */
.card .ev { font-size: var(--size-sm); font-weight: 500; color: var(--c-neutral-900); }
.card.now .ev { color: var(--c-severity-high); }
.card.archive .ev { color: var(--c-neutral-500); font-weight: 400; }
.card .t { margin-left: auto; font-family: var(--font-mono); font-size: var(--size-11);
           color: var(--c-neutral-500); }
.card .t.abs { background: var(--c-neutral-50); padding: 0 4px; border-radius: var(--radius-sm); }
.card .nm { font-size: var(--size-md); font-weight: 500; color: var(--c-neutral-900);
            overflow: hidden; text-overflow: ellipsis; white-space: nowrap; margin-bottom: 2px; }
.card .cn { font-size: var(--size-base); color: var(--c-neutral-500); overflow: hidden;
            text-overflow: ellipsis; white-space: nowrap; margin-bottom: var(--space-2); }
.card .meta { display: flex; align-items: center; gap: 10px; font-size: var(--size-sm);
              color: var(--c-neutral-600); flex-wrap: wrap; }
.card .meta b { font-family: var(--font-mono); font-weight: 600; color: var(--c-neutral-900);
                font-variant-numeric: tabular-nums; }
/* 存档备查：视觉降权压成单行灰色。降权不是删除——它仍在流里、可筛出、可翻查历史。 */
.card.archive { padding: 7px 13px; }
.card.archive .cn { display: none; }
.card.unread::after {
  content: ''; position: absolute; top: 13px; right: 11px; width: 6px; height: 6px;
  border-radius: 50%; background: var(--c-accent-base);
}
.feed { display: flex; flex-direction: column; gap: 10px; }               /* GAP feed-gap-10 */

/* ── 徽章：一套形状 + 几种质地。一律 nowrap（「未抓到」折成两行会读成两个词）── */
.bg {
  display: inline-flex; align-items: center; border-radius: var(--radius-sm);
  padding: 2px 7px; white-space: nowrap; font-size: var(--size-sm); line-height: 1.35;
}
/* 未抓到：斜纹 45°/3/8 + 等宽 600 + 字面写「未抓到」。三个记号缺一不可。 */
.bg.missing {
  background-image: repeating-linear-gradient(45deg,
    var(--c-provenance-missing-hatch-badge) 0 3px, transparent 3px 8px);
  color: var(--c-provenance-missing-text); font-family: var(--font-mono); font-weight: 600;
}
/* 推算：≈ 前缀 + 点线下划线，两个记号都要。 */
.bg.derived {
  border-bottom: 1px dotted var(--c-provenance-derived); color: var(--c-provenance-derived);
  border-radius: 0; padding: 1px 0; gap: 2px;
}
/* 计数 */
.bg.count { background: var(--c-neutral-50); border: 1px solid var(--c-neutral-100);
            color: var(--c-neutral-600); font-family: var(--font-mono); }
/* 已激活筛选 —— 全站唯一用强调色的徽章 */
.bg.filt { background: var(--c-accent-wash); color: var(--c-accent-text); font-weight: 500; }
/* 未标注：虚线框。不用斜纹——斜纹是「未抓到」专属；虚线框表示「这里以后会有东西」。 */
.bg.unlabeled { border: 1px dashed var(--border-control); color: var(--c-neutral-600);
                background: none; }
/* 状态未知：**实线**框。它不属于空值质地那一套（斜纹 / 虚线框，答「这是哪一种空」），
   属于异常那一套（答「屏幕上的数还能不能信」），术语文件把两套刻意分开。
   二档异常的规定表达是「就地插、**有形无色**」，所以这里只有形（实线框 + 图标），没有色。
   ⚠️ 绝不能用虚线：虚线锁给「时候未到」= 现在没有、**以后会有**；
   而状态未知是「**别信这个数**」——时间方向相反，同一种质地表达，
   用户就分不出「再等等」和「别信它」。 */
.bg.unknown { border: 1px solid var(--border-control); color: var(--c-neutral-900);
              background: none; gap: 4px; font-weight: 500; }
.unlabeled-wrap { display: inline-flex; align-items: center; gap: var(--space-2); }
/* 时候未到：虚线框 + 进度条。只有「还在积累」给进度条。 */
.pending { display: inline-flex; align-items: center; gap: var(--space-2);
           border: 1px dashed var(--border-control); border-radius: var(--radius-sm);
           padding: 2px 7px; font-size: var(--size-sm); color: var(--c-neutral-600); }
.pending-bar { display: block; width: 56px; height: 4px; border-radius: 2px;
               background: var(--c-neutral-100); overflow: hidden; }
.pending-bar i { display: block; height: 100%; background: var(--c-neutral-400); }
.pending-txt { font-family: var(--font-mono); }
.missing-t { font-family: var(--font-mono); color: var(--c-neutral-500); }
/* 「这家店的页面不提供该数据」在表格里的形态。刻意**不用斜纹、不用虚线**：
   斜纹＝这次没取到（下次可能有）· 虚线＝时候未到（以后会有），
   而这一类是**永远不会有**。所以它不进那两套质地，只是一句压低的实话。 */
.unavail { color: var(--c-neutral-500); font-size: var(--size-sm); white-space: nowrap; }
.pre-existing { color: var(--c-neutral-500); }

/* ── 表格：四条硬规则 ──
   ① 列宽写百分比并写进 <th>（只写 <colgroup> 会被渲染管线剥掉）
   ② 容器窄了横向滚动，绝不挤压列
   ③ table-layout: fixed
   ④ 徽章 nowrap */
.tablewrap { overflow-x: auto; border: 1px solid var(--c-neutral-100); border-radius: var(--radius-10); }
table.t { width: 100%; min-width: 720px; border-collapse: collapse; table-layout: fixed; }
table.t th, table.t td {
  padding: var(--space-2) var(--space-3); text-align: left; font-size: var(--size-base);
  border-bottom: 1px solid var(--c-neutral-100); vertical-align: middle;
}
table.t th { font-size: var(--size-sm); font-weight: 500; color: var(--c-neutral-500);
             background: var(--c-neutral-25); position: sticky; top: 0; z-index: 2; white-space: nowrap; }
table.t td.num, table.t th.num { text-align: right; font-family: var(--font-mono);
                                 font-variant-numeric: tabular-nums; }
table.t tbody tr:hover { background: var(--c-neutral-25); }
table.t tbody tr:last-child td { border-bottom: 0; }
table.t td .bg { max-width: 100%; }
table.t .cut { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ── 分组盒 ── */
.panel { border: 1px solid var(--c-neutral-100); border-radius: var(--radius-10);
         background: var(--c-neutral-0); }
.panel > header { padding: var(--space-3) var(--space-4); border-bottom: 1px solid var(--c-neutral-100);
                  display: flex; align-items: center; gap: var(--space-2); }
.panel > .body { padding: var(--space-4); }

/* ── KPI 四格 ── */
.kpi { display: flex; gap: 1px; background: var(--c-neutral-100);
       border: 1px solid var(--c-neutral-100); border-radius: var(--radius-10);
       overflow: hidden; flex-shrink: 0; }
.kpi > div { flex: 1; background: var(--c-neutral-0); padding: var(--space-3) var(--space-4); min-width: 0; }
.kpi .l { font-size: var(--size-11); color: var(--c-neutral-500); margin-bottom: 5px; }
.kpi .v { font-size: var(--size-2xl); font-weight: 600; letter-spacing: -.02em;
          line-height: var(--leading-tight); font-family: var(--font-mono);
          font-variant-numeric: tabular-nums; color: var(--c-neutral-900);
          display: flex; align-items: baseline; gap: 5px; }
.kpi .d { font-size: var(--size-sm); color: var(--c-neutral-600); font-weight: 500; }

/* ── 空状态 / 异常状态 ── */
.empty { display: flex; flex-direction: column; align-items: center; justify-content: center;
         gap: var(--space-3); padding: var(--space-8) var(--space-4); text-align: center;
         color: var(--c-neutral-600); }
.empty h3 { font-size: var(--size-lg); color: var(--c-neutral-900); }
.empty .hint { font-size: var(--size-base); max-width: 46ch; line-height: var(--leading-snug); }
.empty .acts { display: flex; gap: var(--space-2); flex-wrap: wrap; justify-content: center; }
.notice { display: flex; align-items: flex-start; gap: var(--space-2); padding: var(--space-3);
          border: 1px solid var(--c-neutral-100); border-radius: var(--radius-md);
          background: var(--c-neutral-25); font-size: var(--size-base); color: var(--c-neutral-600); }
.notice.err { border-color: var(--c-severity-high); }
.notice.err > svg { color: var(--c-severity-high); flex-shrink: 0; }

/* ── 浮层 · 动作条 · 胶囊 ── */
.pill {
  position: absolute; left: 50%; transform: translateX(-50%); z-index: 60; top: 96px;
  display: inline-flex; align-items: center; gap: var(--space-2); height: 32px;
  padding: 0 var(--space-4); border-radius: 20px; background: var(--c-neutral-900);
  color: var(--c-neutral-0); font-size: var(--size-base); font-weight: 500; border: 0;
  cursor: pointer; font-family: inherit; box-shadow: var(--shadow-popover);
}
.pill b { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.actionbar {
  position: absolute; left: 50%; bottom: 20px; transform: translateX(-50%); z-index: 60;
  display: flex; align-items: center; gap: var(--space-3); padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-lg); background: var(--c-neutral-900); color: var(--c-neutral-0);
  box-shadow: var(--shadow-actionbar); font-size: var(--size-base);
}
.actionbar .btn.sec { background: transparent; border-color: var(--c-neutral-600); color: var(--c-neutral-0); }
.actionbar .btn.gho { color: var(--c-neutral-200); }

dialog.modal { border: 1px solid var(--c-neutral-100); border-radius: var(--radius-lg);
               padding: 0; background: var(--c-neutral-0); color: var(--c-neutral-900);
               box-shadow: var(--shadow-popover); max-width: 560px; width: calc(100vw - 48px); }
dialog.modal::backdrop { background: var(--c-scrim); }
/* ⚠️ 深色套的层级分离**不能靠遮罩**（设计线 2026-09-05 算过：黑纱从 38% 加到 70%，
   弹层对遮罩的对比度只从 1.29 挪到 1.35 —— 页底本来就接近纯黑，再压没有余量）。
   靠边框：border.control 对遮罩 6.38，远超 3:1。这就是这套语言写死的
   「浅色靠阴影、深色靠明度」，也是 shadow-dark 三项全是 none 的原因。 */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) dialog.modal { border-color: var(--border-control); }
}
:root[data-theme="dark"] dialog.modal { border-color: var(--border-control); }
/* 不加 backdrop-filter。不是性能取舍，是不能做：
   批量动作确认弹层背后正是用户要核对的那批条目，同款确认弹层背后是他要比对的两张图。
   糊掉它，等于把他确认的依据拿走。 */
dialog.modal > header { padding: var(--space-4) var(--space-4) 0; }
dialog.modal > .body { padding: var(--space-3) var(--space-4); font-size: var(--size-base);
                       line-height: var(--leading-snug); color: var(--c-neutral-600); }
dialog.modal > footer { padding: var(--space-3) var(--space-4) var(--space-4);
                        display: flex; gap: var(--space-2); justify-content: flex-end; }

/* ── 分段控件 / tab ── */
.tabs { display: flex; gap: var(--space-1); border-bottom: 1px solid var(--c-neutral-100); }
.tab { height: 34px; padding: 0 var(--space-3); border: 0; background: none; cursor: pointer;
       font-family: inherit; font-size: var(--size-base); color: var(--c-neutral-600);
       border-bottom: 2px solid transparent; margin-bottom: -1px; }
.tab:hover { color: var(--c-neutral-900); }
.tab[aria-selected="true"] { color: var(--c-neutral-900); font-weight: 500;
                             border-bottom-color: var(--c-neutral-900); }

/* ── 开关（提醒规则的推送开关）── */
.sw { width: 34px; height: 20px; border-radius: 10px; border: 1px solid var(--border-control);
      background: var(--c-neutral-50); position: relative; cursor: pointer; padding: 0; flex-shrink: 0; }
.sw::after { content: ''; position: absolute; top: 2px; left: 2px; width: 14px; height: 14px;
             border-radius: 50%; background: var(--c-neutral-500);
             transition: transform var(--motion-duration-item-enter) var(--motion-easing-enter); }
.sw[aria-checked="true"] { background: var(--c-neutral-900); border-color: var(--c-neutral-900); }
.sw[aria-checked="true"]::after { background: var(--c-neutral-0); transform: translateX(14px); }
.sw[disabled] { cursor: default; background: var(--c-neutral-50); border-color: var(--c-neutral-100); }
.sw-hit { display: inline-flex; align-items: center; min-height: var(--a11y-floor-target-used); }
