/* ============================================================
   咖莱云设计系统 · 组件库 components.css
   版本 v1.0 · 2026-09-11
   依赖 tokens.css
   ------------------------------------------------------------
   组件清单：
   1. 布局外壳 shell        6. 标签/芯片 chip
   2. 按钮 button           7. 表单 form
   3. 卡片 card             8. 表格 table
   4. KPI 指标卡            9. 反馈 feedback (alert/toast/empty/loading/skeleton)
   5. 徽标 badge           10. 金额与比例 Money / Percent
   ============================================================ */

/* ══════════════════════════════════════════════
   0. 作用域根
   ══════════════════════════════════════════════ */
.kx-scope {
  font-family: var(--ff-sans);
  font-size: var(--density-fs);
  line-height: var(--lh-base);
  color: var(--fg-primary);
  background: var(--bg-page);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* ══════════════════════════════════════════════
   1. 布局外壳
   ══════════════════════════════════════════════ */
.kx-app {
  display: grid;
  /* fallback 写死像素: 旧缓存 tokens.css 缺 --w-nav-* 时 var 链断裂会导致整条声明失效 -> 布局塌陷留白 */
  grid-template-columns: var(--nav-w, 240px) 1fr;
  grid-template-rows: var(--h-topbar, 52px) 1fr;
  grid-template-areas: "topbar topbar" "nav main";
  min-height: 100vh;
  transition: grid-template-columns var(--dur-base) var(--ease-out);
}
.kx-app[data-nav="compact"] { --nav-w: 72px; }
.kx-app[data-nav="collapsed"] { --nav-w: 64px; }

/* 品牌区 */
.kx-brand {
  flex: 0 0 auto;
  display: flex; align-items: center; gap: var(--sp-3);
  padding: 0 var(--sp-4) 0 var(--sp-2);
  background: transparent;
  border-right: 1px solid var(--bd-base);
  align-self: stretch;
  overflow: hidden; white-space: nowrap;
}
.kx-brand__logo {
  flex: 0 0 auto;
  width: 28px; height: 28px; border-radius: var(--rd-md);
  background: linear-gradient(135deg, var(--c-brand-500), var(--c-accent-500));
  display: grid; place-items: center;
  color: var(--c-const-white); font-weight: var(--fw-bold); font-size: var(--fs-14);
  letter-spacing: -0.02em;
}
.kx-brand__text { display: flex; flex-direction: column; line-height: 1.2; min-width: 0; }
.kx-brand__name {
  font-size: var(--fs-14); font-weight: var(--fw-semi);
  color: var(--fg-primary); letter-spacing: -0.01em;
}
.kx-brand__sub {
  font-size: var(--fs-11); color: var(--fg-tertiary);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.kx-app[data-nav="compact"] .kx-brand__text,
.kx-app[data-nav="collapsed"] .kx-brand__text { display: none; }

/* 顶栏 */
.kx-topbar {
  grid-area: topbar;
  display: flex; align-items: center; gap: var(--sp-4);
  padding: 0 var(--sp-5);
  background: var(--bg-surface);
  border-bottom: 1px solid var(--bd-base);
  position: sticky; top: 0; z-index: var(--z-sticky);
}
.kx-topbar__title {
  font-size: var(--fs-16); font-weight: var(--fw-semi);
  color: var(--fg-primary); letter-spacing: -0.01em;
}
.kx-topbar__crumb { font-size: var(--fs-13); color: var(--fg-second); }
.kx-topbar__spacer { flex: 1 1 auto; }
.kx-topbar__env {
  display: inline-flex; align-items: center; gap: var(--sp-1);
  font-size: var(--fs-11); font-weight: var(--fw-semi);
  padding: 2px var(--sp-2); border-radius: var(--rd-sm);
  background: var(--c-warn-bg); color: var(--c-warn-fg);
}

/* 侧栏 */
.kx-nav {
  grid-area: nav;
  background: var(--bg-surface);
  border-right: 1px solid var(--bd-base);
  padding: var(--sp-3) var(--sp-2);
  display: flex; flex-direction: column; gap: var(--sp-1);
  overflow-y: auto;
  position: sticky; top: var(--h-topbar);
  height: calc(100vh - var(--h-topbar));
}
.kx-nav__group {
  margin-top: var(--sp-3);
  font-size: var(--fs-11); font-weight: var(--fw-semi);
  color: var(--fg-tertiary); text-transform: uppercase;
  letter-spacing: .04em; padding: var(--sp-1) var(--sp-3);
}
.kx-app[data-nav="compact"] .kx-nav__group { display: none; }

.kx-nav__item {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--rd-md);
  color: var(--fg-second); font-size: var(--fs-13); font-weight: var(--fw-medium);
  text-decoration: none; cursor: pointer;
  border: 1px solid transparent;
  transition: background var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out);
  white-space: nowrap; overflow: hidden;
}
.kx-nav__item:hover { background: var(--bg-sunken); color: var(--fg-primary); }
.kx-nav__item[aria-current="page"] {
  background: var(--bg-brand-soft); color: var(--c-brand-600);
  font-weight: var(--fw-semi); border-color: var(--bd-brand-soft);
}
.kx-nav__ico {
  flex: 0 0 auto; width: 18px; height: 18px;
  display: grid; place-items: center;
  stroke: currentColor; stroke-width: 1.75; fill: none;
}
.kx-nav__text { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.kx-nav__count {
  flex: 0 0 auto; font-size: var(--fs-11); font-weight: var(--fw-semi);
  padding: 0 6px; border-radius: var(--rd-full);
  background: var(--c-bad-500); color: var(--c-const-white); line-height: 16px;
}
.kx-app[data-nav="compact"] .kx-nav__text,
.kx-app[data-nav="compact"] .kx-nav__count,
.kx-app[data-nav="collapsed"] .kx-nav__text,
.kx-app[data-nav="collapsed"] .kx-nav__count { display: none; }
.kx-app[data-nav="compact"] .kx-nav__item,
.kx-app[data-nav="collapsed"] .kx-nav__item { justify-content: center; padding: var(--sp-2); }
.kx-app[data-nav="collapsed"] .kx-nav__group { display: none; }

/* 侧栏折叠按钮(topbar 最左) */
.kx-topbar__navtoggle {
  flex: 0 0 auto;
  width: 32px; height: 32px;
  display: grid; place-items: center;
  background: none; border: 1px solid transparent; border-radius: var(--rd-md);
  color: var(--fg-second); font-size: 16px; cursor: pointer;
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.kx-topbar__navtoggle:hover { background: var(--bg-sunken); color: var(--fg-primary); }

/* 主区 */
.kx-main {
  grid-area: main;
  padding: var(--density-pad-y) var(--density-pad-x) var(--sp-12);
  overflow-x: hidden;
  min-width: 0;
}
.kx-page-head {
  display: flex; align-items: flex-start; gap: var(--sp-4);
  margin-bottom: var(--sp-5); flex-wrap: wrap;
}
.kx-page-head__main { flex: 1 1 320px; min-width: 0; }
.kx-page-head__title {
  font-size: var(--fs-24); font-weight: var(--fw-bold);
  color: var(--c-n-900); letter-spacing: -0.02em; margin: 0 0 var(--sp-1);
  line-height: var(--lh-tight);
}
.kx-page-head__desc {
  font-size: var(--fs-13); color: var(--fg-second); margin: 0;
  max-width: 68ch;
}
.kx-page-head__actions { display: flex; gap: var(--sp-2); flex-wrap: wrap; }

/* ══════════════════════════════════════════════
   2. 按钮
   ══════════════════════════════════════════════ */
.kx-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2);
  height: var(--h-control); padding: 0 var(--sp-4);
  font-family: inherit; font-size: var(--fs-13); font-weight: var(--fw-medium);
  line-height: 1; border-radius: var(--rd-md); cursor: pointer;
  border: 1px solid transparent; white-space: nowrap;
  transition: background var(--dur-fast), border-color var(--dur-fast),
              color var(--dur-fast), box-shadow var(--dur-fast);
  -webkit-user-select: none; user-select: none;
}
.kx-btn:focus-visible { outline: none; box-shadow: var(--sh-focus); }
.kx-btn:disabled, .kx-btn[aria-disabled="true"] {
  cursor: not-allowed; opacity: .5; pointer-events: none;
}
.kx-btn svg { width: 15px; height: 15px; stroke: currentColor; stroke-width: 1.9; fill: none; }

/* primary */
.kx-btn--primary {
  background: var(--c-brand-500); color: var(--c-const-white);
  border-color: var(--c-brand-500);
  box-shadow: var(--sh-xs);
}
.kx-btn--primary:hover { background: var(--c-brand-600); border-color: var(--c-brand-600); }
.kx-btn--primary:active { background: var(--c-brand-700); }

/* secondary（默认） */
.kx-btn--secondary {
  background: var(--bg-surface); color: var(--fg-primary);
  border-color: var(--bd-strong);
}
.kx-btn--secondary:hover { background: var(--bg-sunken); border-color: var(--c-n-400); }

/* ghost */
.kx-btn--ghost { background: transparent; color: var(--fg-second); }
.kx-btn--ghost:hover { background: var(--bg-sunken); color: var(--fg-primary); }

/* danger（破坏性操作，仅用于删除/回退/吊销） */
.kx-btn--danger {
  background: var(--c-bad-500); color: var(--c-const-white); border-color: var(--c-bad-500);
}
.kx-btn--danger:hover { background: var(--c-bad-600); border-color: var(--c-bad-600); }

/* 尺寸 */
.kx-btn--sm { height: var(--h-control-sm); padding: 0 var(--sp-3); font-size: var(--fs-12); }
.kx-btn--lg { height: var(--h-control-lg); padding: 0 var(--sp-5); font-size: var(--fs-14); }
.kx-btn--block { width: 100%; }

/* ══════════════════════════════════════════════
   3. 卡片
   ══════════════════════════════════════════════ */
.kx-card {
  background: var(--bg-surface);
  border: 1px solid var(--bd-base);
  border-radius: var(--rd-lg);
  box-shadow: var(--sh-xs);
  overflow: hidden;
}
.kx-card__head {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  border-bottom: 1px solid var(--bd-subtle);
}
.kx-card__title {
  font-size: var(--fs-14); font-weight: var(--fw-semi);
  color: var(--fg-primary); margin: 0;
}
.kx-card__sub { font-size: var(--fs-12); color: var(--fg-second); }
.kx-card__spacer { flex: 1 1 auto; }
.kx-card__body { padding: var(--sp-4); }
.kx-card__body--flush { padding: 0; }
.kx-card__foot {
  padding: var(--sp-3) var(--sp-4);
  border-top: 1px solid var(--bd-subtle);
  background: var(--bg-sunken);
  display: flex; align-items: center; gap: var(--sp-3);
}

/* 网格 */
.kx-grid { display: grid; gap: var(--density-gap); }
.kx-grid--2 { grid-template-columns: repeat(2, minmax(0,1fr)); }
.kx-grid--3 { grid-template-columns: repeat(3, minmax(0,1fr)); }
.kx-grid--4 { grid-template-columns: repeat(4, minmax(0,1fr)); }
.kx-grid--auto { grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); }
.kx-grid--sidebar { grid-template-columns: minmax(0,1fr) 320px; }
@media (max-width: 1100px) {
  .kx-grid--4 { grid-template-columns: repeat(2, minmax(0,1fr)); }
  .kx-grid--sidebar { grid-template-columns: minmax(0,1fr); }
}
@media (max-width: 720px) {
  .kx-grid--2, .kx-grid--3, .kx-grid--4 { grid-template-columns: minmax(0,1fr); }
}

/* ══════════════════════════════════════════════
   4. KPI 指标卡
   ══════════════════════════════════════════════ */
.kx-kpi {
  background: var(--bg-surface); border: 1px solid var(--bd-base);
  border-radius: var(--rd-lg); padding: var(--sp-4);
  display: flex; flex-direction: column; gap: var(--sp-2);
  position: relative; overflow: hidden;
}
.kx-kpi__label {
  font-size: var(--fs-12); color: var(--fg-second);
  font-weight: var(--fw-medium);
  display: flex; align-items: center; gap: var(--sp-1);
}
.kx-kpi__value {
  font-family: var(--ff-num);
  font-size: var(--fs-30); font-weight: var(--fw-bold);
  color: var(--c-n-900); letter-spacing: var(--ls-num);
  line-height: 1.1; font-variant-numeric: tabular-nums;
}
.kx-kpi__unit { font-size: var(--fs-14); font-weight: var(--fw-medium); color: var(--fg-second); margin-left: 2px; }
.kx-kpi__delta {
  font-size: var(--fs-12); font-weight: var(--fw-semi);
  display: inline-flex; align-items: center; gap: 3px;
}
/* 涨=红 跌=绿（中国股市惯例） */
.kx-kpi__delta--up   { color: var(--c-pos-600); }
.kx-kpi__delta--down { color: var(--c-neg-600); }
.kx-kpi__delta--flat { color: var(--fg-tertiary); }
.kx-kpi__foot { font-size: var(--fs-11); color: var(--fg-tertiary); }
.kx-kpi--accent { border-color: var(--bd-brand-soft); background: var(--bg-brand-soft); }
.kx-kpi--accent .kx-kpi__value { color: var(--c-brand-700); }
.kx-kpi--warn { border-color: var(--c-warn-500); background: var(--c-warn-bg); }

/* ══════════════════════════════════════════════
   5. 徽标 / 芯片
   ══════════════════════════════════════════════ */
.kx-badge {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: var(--fs-11); font-weight: var(--fw-semi);
  padding: 1px var(--sp-2); border-radius: var(--rd-sm);
  line-height: 18px; white-space: nowrap;
  background: var(--c-idle-bg); color: var(--c-idle-fg);
}
.kx-badge--ok   { background: var(--c-ok-bg);   color: var(--c-ok-fg); }
.kx-badge--warn { background: var(--c-warn-bg); color: var(--c-warn-fg); }
.kx-badge--bad  { background: var(--c-bad-bg);  color: var(--c-bad-fg); }
.kx-badge--info { background: var(--c-info-bg); color: var(--c-info-fg); }
.kx-badge--brand{ background: var(--bg-brand-soft-2); color: var(--fg-on-brand-soft); }
/* 中性态：已停用 / 已过期 / 草稿 —— 不是错误，不需要报警色 */
.kx-badge--idle { background: var(--c-idle-bg); color: var(--c-idle-fg); }
.kx-badge__dot {
  width: 6px; height: 6px; border-radius: 50%; background: currentColor;
  flex: 0 0 auto;
}
.kx-badge--pulse .kx-badge__dot { animation: kx-pulse 1.6s var(--ease-in-out) infinite; }
@keyframes kx-pulse { 0%,100%{opacity:1} 50%{opacity:.35} }

/* ══════════════════════════════════════════════
   6. 表单
   ══════════════════════════════════════════════ */
.kx-field { display: flex; flex-direction: column; gap: var(--sp-1); margin-bottom: var(--sp-4); }
.kx-field__label {
  font-size: var(--fs-12); font-weight: var(--fw-semi); color: var(--fg-primary);
  display: flex; align-items: center; gap: var(--sp-1);
}
.kx-field__label .kx-req { color: var(--c-bad-500); }
.kx-field__hint { font-size: var(--fs-11); color: var(--fg-tertiary); }
.kx-field__err {
  font-size: var(--fs-11); color: var(--c-bad-600);
  display: flex; align-items: center; gap: 4px; font-weight: var(--fw-medium);
}

.kx-input, .kx-select, .kx-textarea {
  width: 100%; height: var(--h-control);
  padding: 0 var(--sp-3);
  font-family: inherit; font-size: var(--fs-13);
  color: var(--fg-primary); background: var(--bg-surface);
  border: 1px solid var(--bd-strong); border-radius: var(--rd-md);
  transition: border-color var(--dur-fast), box-shadow var(--dur-fast);
}
.kx-textarea { height: auto; min-height: 76px; padding: var(--sp-2) var(--sp-3); line-height: var(--lh-base); resize: vertical; }
.kx-input::placeholder, .kx-textarea::placeholder { color: var(--fg-tertiary); }
.kx-input:hover, .kx-select:hover, .kx-textarea:hover { border-color: var(--c-n-400); }
.kx-input:focus, .kx-select:focus, .kx-textarea:focus {
  outline: none; border-color: var(--bd-focus); box-shadow: var(--sh-focus);
}
.kx-input[aria-invalid="true"], .kx-select[aria-invalid="true"] {
  border-color: var(--c-bad-500); box-shadow: var(--sh-focus-bad);
}
.kx-input:disabled, .kx-select:disabled {
  background: var(--bg-sunken); color: var(--fg-tertiary); cursor: not-allowed;
}
/* 金额/数量输入：等宽数字并右对齐 */
.kx-input--num { font-family: var(--ff-num); text-align: right; font-variant-numeric: tabular-nums; }
.kx-input--readonly { background: var(--bg-sunken); color: var(--fg-second); }

.kx-inputgroup { display: flex; align-items: stretch; }
.kx-inputgroup .kx-input { border-radius: var(--rd-md) 0 0 var(--rd-md); }
.kx-inputgroup__suffix {
  display: grid; place-items: center; padding: 0 var(--sp-3);
  font-size: var(--fs-12); color: var(--fg-second); font-weight: var(--fw-medium);
  background: var(--bg-sunken); border: 1px solid var(--bd-strong); border-left: none;
  border-radius: 0 var(--rd-md) var(--rd-md) 0; white-space: nowrap;
}

/* 复选 / 单选 / 开关 */
.kx-check { display: inline-flex; align-items: center; gap: var(--sp-2); font-size: var(--fs-13); cursor: pointer; }
.kx-check input { width: 15px; height: 15px; accent-color: var(--c-brand-500); cursor: pointer; }

.kx-switch {
  position: relative; width: 36px; height: 20px; flex: 0 0 auto;
  border-radius: var(--rd-full); background: var(--c-n-300);
  border: none; cursor: pointer; padding: 0;
  transition: background var(--dur-base) var(--ease-out);
}
.kx-switch::after {
  content: ""; position: absolute; top: 2px; left: 2px;
  width: 16px; height: 16px; border-radius: 50%; background: var(--c-const-white);
  box-shadow: var(--sh-xs);
  transition: transform var(--dur-base) var(--ease-out);
}
.kx-switch[aria-checked="true"] { background: var(--c-brand-500); }
.kx-switch[aria-checked="true"]::after { transform: translateX(16px); }

/* ══════════════════════════════════════════════
   7. 表格
   ══════════════════════════════════════════════ */
.kx-tablewrap {
  background: var(--bg-surface); border: 1px solid var(--bd-base);
  border-radius: var(--rd-lg); overflow: hidden;
}
.kx-table-scroll { overflow-x: auto; }
.kx-table { width: 100%; border-collapse: collapse; font-size: var(--density-fs); }
.kx-table thead th {
  text-align: left; font-size: var(--fs-12); font-weight: var(--fw-semi);
  color: var(--fg-second); background: var(--bg-sunken);
  padding: var(--sp-2) var(--sp-3); white-space: nowrap;
  border-bottom: 1px solid var(--bd-base);
  position: sticky; top: 0; z-index: 1;
}
.kx-table tbody td {
  padding: 0 var(--sp-3); height: var(--density-row-h);
  border-bottom: 1px solid var(--bd-subtle);
  color: var(--fg-primary); vertical-align: middle;
}
.kx-table tbody tr:last-child td { border-bottom: none; }
.kx-table tbody tr:hover td { background: var(--bg-row-hover); }
/* ⚠️ 选中行必须走 --bg-selected（深色主题下是半透明品牌色叠加）。
   原先写死 var(--c-brand-50) 在深色主题下是浅色 → 整行变亮、白字丢失对比度。 */
.kx-table tbody tr[aria-selected="true"] td { background: var(--bg-selected); }
/* 数字列右对齐 + 等宽 */
.kx-table .num {
  text-align: right; font-family: var(--ff-num);
  font-variant-numeric: tabular-nums; letter-spacing: var(--ls-num);
}
.kx-table .t-strong { font-weight: var(--fw-semi); color: var(--c-n-900); }
.kx-table td.actions { width: 1%; white-space: nowrap; text-align: right; }
.kx-table__caption {
  caption-side: bottom; padding: var(--sp-2) var(--sp-3);
  font-size: var(--fs-11); color: var(--fg-tertiary); text-align: left;
}

/* 分页 */
.kx-pager {
  display: flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-4);
  border-top: 1px solid var(--bd-subtle); background: var(--bg-surface);
  font-size: var(--fs-12); color: var(--fg-second);
}
.kx-pager__spacer { flex: 1 1 auto; }
.kx-pager__btn {
  min-width: 28px; height: 28px; padding: 0 var(--sp-2);
  border: 1px solid var(--bd-strong); background: var(--bg-surface);
  border-radius: var(--rd-sm); cursor: pointer; font-size: var(--fs-12);
  color: var(--fg-primary); font-family: inherit;
}
.kx-pager__btn:hover:not(:disabled) { background: var(--bg-sunken); }
.kx-pager__btn:disabled { opacity: .45; cursor: not-allowed; }
.kx-pager__btn[aria-current="true"] {
  background: var(--c-brand-500); color: var(--c-const-white); border-color: var(--c-brand-500);
}

/* ══════════════════════════════════════════════
   8. 反馈：Alert / Empty / Loading / Skeleton / Toast
   ══════════════════════════════════════════════ */
.kx-alert {
  display: flex; gap: var(--sp-3); align-items: flex-start;
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--rd-md); border: 1px solid transparent;
  font-size: var(--fs-13); line-height: var(--lh-snug);
}
.kx-alert__ico { flex: 0 0 auto; width: 17px; height: 17px; margin-top: 1px; }
.kx-alert__body { flex: 1 1 auto; min-width: 0; }
.kx-alert__title { font-weight: var(--fw-semi); margin: 0 0 2px; font-size: var(--fs-13); }
.kx-alert__text { margin: 0; color: inherit; opacity: .88; }
.kx-alert--info { background: var(--c-info-bg); color: var(--c-info-fg); border-color: var(--c-brand-200); }
.kx-alert--ok   { background: var(--c-ok-bg);   color: var(--c-ok-fg);   border-color: var(--c-ok-500); }
.kx-alert--warn { background: var(--c-warn-bg); color: var(--c-warn-fg); border-color: var(--c-warn-500); }
.kx-alert--bad  { background: var(--c-bad-bg);  color: var(--c-bad-fg);  border-color: var(--c-bad-500); }

/* 空态 */
.kx-empty {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--sp-3); padding: var(--sp-12) var(--sp-5); text-align: center;
}
.kx-empty__art {
  width: 56px; height: 56px; border-radius: var(--rd-xl);
  background: var(--bg-sunken); display: grid; place-items: center;
  color: var(--fg-tertiary);
}
.kx-empty__art svg { width: 26px; height: 26px; stroke: currentColor; stroke-width: 1.6; fill: none; }
/* 大插画变体：仪表盘卡内的空态需要更大视觉面积才不显得「塌」
   （小图标在大卡片里会被误读成页面渲染失败） */
.kx-empty__art--lg {
  width: 120px; height: 84px; border-radius: 0; background: transparent;
}
.kx-empty__art--lg svg { width: 120px; height: 84px; stroke: none; }
.kx-empty__title { font-size: var(--fs-14); font-weight: var(--fw-semi); color: var(--fg-primary); margin: 0; }
.kx-empty__desc { font-size: var(--fs-12); color: var(--fg-second); margin: 0; max-width: 46ch; }

/* 骨架屏 */
.kx-skel {
  background: linear-gradient(90deg, var(--c-n-100) 25%, var(--c-n-150) 37%, var(--c-n-100) 63%);
  background-size: 400% 100%;
  animation: kx-shimmer 1.4s ease-in-out infinite;
  border-radius: var(--rd-sm); height: 12px;
}
@keyframes kx-shimmer { 0%{background-position:100% 50%} 100%{background-position:0 50%} }
.kx-skel--title { height: 20px; width: 40%; }
.kx-skel--row { height: var(--density-row-h); border-radius: 0; }

/* 加载指示 */
.kx-spinner {
  width: 16px; height: 16px; border-radius: 50%;
  border: 2px solid var(--c-n-200); border-top-color: var(--c-brand-500);
  animation: kx-spin .7s linear infinite; flex: 0 0 auto;
}
@keyframes kx-spin { to { transform: rotate(360deg); } }
.kx-inline-load {
  display: flex; align-items: center; gap: var(--sp-2);
  font-size: var(--fs-12); color: var(--fg-second);
  padding: var(--sp-3) var(--sp-4);
}

/* Toast */
.kx-toast-layer {
  position: fixed; top: var(--sp-5); right: var(--sp-5);
  display: flex; flex-direction: column; gap: var(--sp-2);
  z-index: var(--z-toast); pointer-events: none;
}
.kx-toast {
  pointer-events: auto;
  display: flex; align-items: flex-start; gap: var(--sp-3);
  min-width: 260px; max-width: 380px;
  padding: var(--sp-3) var(--sp-4);
  background: var(--bg-surface); border: 1px solid var(--bd-base);
  border-left: 3px solid var(--c-brand-500);
  border-radius: var(--rd-md); box-shadow: var(--sh-lg);
  font-size: var(--fs-13); color: var(--fg-primary);
  animation: kx-toast-in var(--dur-base) var(--ease-out);
}
@keyframes kx-toast-in { from { opacity: 0; transform: translateX(12px); } }
.kx-toast--ok   { border-left-color: var(--c-ok-500); }
.kx-toast--warn { border-left-color: var(--c-warn-500); }
.kx-toast--bad  { border-left-color: var(--c-bad-500); }
.kx-toast__title { font-weight: var(--fw-semi); margin: 0 0 1px; }
.kx-toast__text { color: var(--fg-second); font-size: var(--fs-12); margin: 0; }

/* ══════════════════════════════════════════════
   9. 金额与比例（数值纪律的组件化落地）
   ══════════════════════════════════════════════ */
.kx-money {
  font-family: var(--ff-num); font-variant-numeric: tabular-nums;
  letter-spacing: var(--ls-num); white-space: nowrap;
}
.kx-money__sym { font-size: .85em; color: var(--fg-second); margin-right: 1px; font-weight: var(--fw-medium); }
.kx-money--lg { font-size: var(--fs-20); font-weight: var(--fw-bold); }
.kx-money--xl { font-size: var(--fs-24); font-weight: var(--fw-bold); }
.kx-money--strong { font-weight: var(--fw-semi); color: var(--c-n-900); }
/* 盈亏着色：涨=红 跌=绿 */
.kx-money--up   { color: var(--c-pos-600); }
.kx-money--down { color: var(--c-neg-600); }
.kx-money--muted { color: var(--fg-tertiary); }

.kx-pct { font-family: var(--ff-num); font-variant-numeric: tabular-nums; letter-spacing: var(--ls-num); }

/* ══════════════════════════════════════════════
   10. 杂项：工具条 / 分隔 / 状态点 / 进度 / 树
   ══════════════════════════════════════════════ */
.kx-toolbar {
  display: flex; align-items: center; gap: var(--sp-2);
  flex-wrap: wrap; margin-bottom: var(--sp-4);
}
.kx-toolbar__spacer { flex: 1 1 auto; }

.kx-divider { height: 1px; background: var(--bd-base); border: none; margin: var(--sp-4) 0; }
.kx-divider--v { width: 1px; height: 18px; background: var(--bd-base); margin: 0 var(--sp-1); }

.kx-dot { width: 7px; height: 7px; border-radius: 50%; display: inline-block; flex: 0 0 auto; }
.kx-dot--online  { background: var(--c-ok-500); box-shadow: 0 0 0 3px var(--c-ok-bg); }
.kx-dot--offline  { background: var(--c-n-400); }
.kx-dot--degraded { background: var(--c-warn-500); box-shadow: 0 0 0 3px var(--c-warn-bg); }

.kx-progress {
  height: 6px; background: var(--bg-sunken); border-radius: var(--rd-full);
  overflow: hidden; width: 100%;
}
.kx-progress__bar {
  height: 100%; background: var(--c-brand-500);
  border-radius: var(--rd-full);
  transition: width var(--dur-slow) var(--ease-out);
}
.kx-progress__bar--ok  { background: var(--c-ok-500); }
.kx-progress__bar--warn{ background: var(--c-warn-500); }

/* 步骤条（升级队列可视化用） */
.kx-steps { display: flex; align-items: center; gap: 0; flex-wrap: wrap; }
.kx-step {
  display: flex; align-items: center; gap: var(--sp-2);
  font-size: var(--fs-12); color: var(--fg-second); padding: var(--sp-1) 0;
}
.kx-step__dot {
  width: 18px; height: 18px; border-radius: 50%; flex: 0 0 auto;
  display: grid; place-items: center; font-size: 10px; font-weight: var(--fw-bold);
  background: var(--bg-sunken); color: var(--fg-tertiary);
  border: 1.5px solid var(--bd-strong);
}
.kx-step--done .kx-step__dot { background: var(--c-ok-500); border-color: var(--c-ok-500); color: var(--c-const-white); }
.kx-step--running .kx-step__dot { background: var(--c-brand-500); border-color: var(--c-brand-500); color: var(--c-const-white); }
.kx-step--failed .kx-step__dot { background: var(--c-bad-500); border-color: var(--c-bad-500); color: var(--c-const-white); }
.kx-step--running .kx-step__label { color: var(--c-brand-600); font-weight: var(--fw-semi); }
.kx-step__line { width: 22px; height: 1.5px; background: var(--bd-strong); margin: 0 var(--sp-1); flex: 0 0 auto; }

/* 定义列表（费用明细逐项罗列 — 用户偏好） */
.kx-dl { margin: 0; display: flex; flex-direction: column; }
.kx-dl__row {
  display: flex; align-items: baseline; gap: var(--sp-3);
  padding: var(--sp-2) 0; border-bottom: 1px dashed var(--bd-subtle);
}
.kx-dl__row:last-child { border-bottom: none; }
.kx-dl__k { flex: 1 1 auto; font-size: var(--fs-13); color: var(--fg-second); }
.kx-dl__v {
  flex: 0 0 auto; font-family: var(--ff-num); font-variant-numeric: tabular-nums;
  font-size: var(--fs-13); color: var(--fg-primary); font-weight: var(--fw-medium);
}
.kx-dl__row--total {
  border-top: 1px solid var(--bd-base); border-bottom: none;
  margin-top: var(--sp-1); padding-top: var(--sp-3);
}
.kx-dl__row--total .kx-dl__k { font-weight: var(--fw-semi); color: var(--fg-primary); }
.kx-dl__row--total .kx-dl__v { font-size: var(--fs-16); font-weight: var(--fw-bold); }

/* 树（区域收益 / 四级裂变） */
.kx-tree { list-style: none; margin: 0; padding: 0; }
.kx-tree .kx-tree { padding-left: var(--sp-5); border-left: 1px dashed var(--bd-strong); margin-left: var(--sp-2); }
.kx-tree__node {
  display: flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-2); border-radius: var(--rd-sm);
  font-size: var(--fs-13);
}
.kx-tree__node:hover { background: var(--bg-sunken); }
.kx-tree__label { flex: 1 1 auto; min-width: 0; }

/* 代码块 */
.kx-code {
  background: var(--bg-code); color: var(--fg-code);
  font-family: var(--ff-mono); font-size: var(--fs-12); line-height: 1.6;
  padding: var(--sp-3) var(--sp-4); border-radius: var(--rd-md);
  overflow-x: auto; white-space: pre; margin: 0;
}
.kx-code--inline {
  display: inline; padding: 1px 5px; border-radius: var(--rd-xs);
  font-family: var(--ff-mono); font-size: .92em;
  background: var(--bg-code); color: var(--fg-code);
}

/* 抽屉 */
.kx-drawer-bd {
  position: fixed; inset: 0; background: var(--bg-scrim);
  z-index: var(--z-drawer);
}
.kx-drawer {
  position: fixed; top: 0; right: 0; bottom: 0; width: min(520px, 92vw);
  background: var(--bg-surface); border-left: 1px solid var(--bd-base);
  box-shadow: var(--sh-lg); z-index: calc(var(--z-drawer) + 1);
  display: flex; flex-direction: column;
  animation: kx-drawer-in var(--dur-base) var(--ease-out);
}
@keyframes kx-drawer-in { from { transform: translateX(24px); opacity: .6; } }
.kx-drawer__head {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--bd-base);
}
.kx-drawer__body { flex: 1 1 auto; overflow-y: auto; padding: var(--sp-4); }
.kx-drawer__foot {
  padding: var(--sp-3) var(--sp-4); border-top: 1px solid var(--bd-base);
  display: flex; gap: var(--sp-2); justify-content: flex-end; background: var(--bg-sunken);
}

/* 仅供原型演示的注释条 */
.kx-demo-note {
  font-size: var(--fs-11); color: var(--fg-tertiary);
  border-left: 2px solid var(--bd-strong); padding-left: var(--sp-2);
  margin-top: var(--sp-2);
}


/* ══════════════════════════════════════════════════════════════
   双栏导航布局壳（Shell · 对齐 E店通后台布局）
   --------------------------------------------------------------
   结构：
     ┌──────────────────────────────────────────┐
     │ 顶栏（深色）  品牌 · 一级导航 · 用户     │ 48px
     ├──────────────────────────────────────────┤
     │ 公告条（可选，浅橙）                     │ 34px
     ├──────────┬───────────────────────────────┤
     │ 二级菜单 │  主区（卡片化）               │
     │ （浅色） │                               │
     │  156px   │                               │
     └──────────┴───────────────────────────────┘
   为什么是双层而不是单层左侧栏：
     一级 = 「域」（定价/利润/共享/渠道…），跨域跳转是低频动作，
            放顶栏横排可一次看全，且不占用纵向内容空间。
     二级 = 「页」，域内切换是高频动作，放左侧竖排符合 F 型扫视路径。
     单层左侧栏装 15+ 页会把「域」和「页」压成同一层级，用户需要
     逐条读文字才能定位；双层后「我再点一次就能到」的路径是可预测的。
   ══════════════════════════════════════════════════════════════ */

.kx-shell {
  display: grid;
  grid-template-areas:
    "gnav   gnav"
    "notice notice"
    "subnav main";
  grid-template-columns: var(--w-subnav) minmax(0, 1fr);
  grid-template-rows: var(--h-global-nav) auto minmax(0, 1fr);
  height: 100vh;
  overflow: hidden;
  background: var(--bg-page);
}

/* 侧栏折叠（窄屏 / 用户偏好） */
.kx-shell[data-subnav="collapsed"] { grid-template-columns: var(--w-subnav-collapsed) minmax(0, 1fr); }
.kx-shell[data-subnav="collapsed"] .kx-subnav__text,
.kx-shell[data-subnav="collapsed"] .kx-subnav__head,
.kx-shell[data-subnav="collapsed"] .kx-subnav__count { display: none; }
.kx-shell[data-subnav="collapsed"] .kx-subnav__item { justify-content: center; padding: 0; }

/* ── 顶栏：一级导航（恒定深色）────────────────── */
.kx-gnav {
  grid-area: gnav;
  display: flex; align-items: stretch;
  background: var(--bg-dark);
  height: var(--h-global-nav);
  position: relative; z-index: var(--z-sticky);
  flex: 1 1 auto; min-width: 0;
}
.kx-gnav__brand {
  display: flex; align-items: center; gap: var(--sp-2);
  padding: 0 var(--sp-4) 0 var(--sp-4);
  flex: 0 0 auto; text-decoration: none;
}
.kx-gnav__logo {
  width: 26px; height: 26px; border-radius: var(--rd-sm);
  background: var(--c-brand-500); color: var(--fg-on-brand);
  display: grid; place-items: center;
  font-size: var(--fs-13); font-weight: var(--fw-bold);
}
.kx-gnav__name {
  font-size: var(--fs-16); font-weight: var(--fw-semi);
  color: var(--fg-on-dark); letter-spacing: var(--ls-tight); white-space: nowrap;
}
.kx-gnav__sub {
  font-size: var(--fs-11); color: var(--fg-on-dark-dim);
  padding-left: var(--sp-2); margin-left: var(--sp-1);
  border-left: 1px solid var(--bd-on-dark); white-space: nowrap;
}
.kx-gnav__menu { display: flex; align-items: stretch; gap: 2px; flex: 1 1 auto; min-width: 0; overflow-x: auto; scrollbar-width: none; }
.kx-gnav__menu::-webkit-scrollbar { display: none; }
.kx-gnav__item {
  position: relative;
  display: flex; align-items: center; gap: 6px;
  padding: 0 var(--sp-3);
  font-family: inherit; font-size: var(--fs-14);
  color: var(--fg-on-dark-dim); background: transparent; border: 0;
  text-decoration: none; white-space: nowrap; cursor: pointer;
  transition: color var(--dur-fast) var(--ease-in-out), background var(--dur-fast) var(--ease-in-out);
}
.kx-gnav__item:hover { color: var(--fg-on-dark); background: var(--bg-dark-2); }
.kx-gnav__item[aria-current="page"] { color: var(--c-brand-500); font-weight: var(--fw-medium); }
.kx-gnav__item[aria-current="page"]::after {
  content: ''; position: absolute; left: var(--sp-2); right: var(--sp-2); bottom: 0;
  height: 2px; border-radius: 2px 2px 0 0; background: var(--c-brand-500);
}
.kx-gnav__spacer { flex: 1 1 auto; }
.kx-gnav__right { display: flex; align-items: center; gap: var(--sp-1); padding: 0 var(--sp-4); flex: 0 0 auto; }
.kx-gnav__icon {
  position: relative;
  width: 30px; height: 30px; border-radius: var(--rd-sm);
  display: grid; place-items: center;
  background: transparent; border: 0; cursor: pointer;
  color: var(--fg-on-dark-dim); font-size: 15px;
  transition: color var(--dur-fast), background var(--dur-fast);
}
.kx-gnav__icon:hover { color: var(--fg-on-dark); background: var(--bg-dark-2); }
.kx-gnav__dot {
  position: absolute; top: 4px; right: 4px;
  width: 7px; height: 7px; border-radius: var(--rd-full);
  background: var(--c-bad-500); border: 1.5px solid var(--bg-dark);
}
.kx-gnav__user {
  display: flex; align-items: center; gap: var(--sp-2);
  padding-left: var(--sp-2); margin-left: var(--sp-1);
  border-left: 1px solid var(--bd-on-dark);
  color: var(--fg-on-dark); font-size: var(--fs-13); cursor: pointer;
}
.kx-gnav__avatar {
  width: var(--sz-avatar); height: var(--sz-avatar); border-radius: var(--rd-full);
  background: var(--c-brand-500); color: var(--fg-on-brand);
  display: grid; place-items: center;
  font-size: var(--fs-12); font-weight: var(--fw-semi);
}

/* ── 公告条 ──────────────────────────────────── */
.kx-notice {
  grid-area: notice;
  display: flex; align-items: center; gap: var(--sp-2);
  height: var(--h-notice-bar); padding: 0 var(--sp-4);
  /* ⚠️ 必须用 --c-warn-bg / --c-warn-fg 这对配对令牌，它们在深色主题下
     会反转为「半透明橙底 + 亮橙字」。直接用 --c-warn-50/600 会得到
     「近白底 + 暗橙字」，在深色页面上是一块刺眼的亮条。 */
  background: var(--c-warn-bg); color: var(--c-warn-fg);
  font-size: var(--fs-13);
}
/* 严重级：授权即将过期这类「不处理就停服」的提示 */
.kx-notice--danger { background: var(--c-bad-bg); color: var(--c-bad-fg); font-weight: var(--fw-medium); }
.kx-notice--info   { background: var(--c-info-bg); color: var(--c-info-fg); }
.kx-notice__link {
  flex: 0 0 auto; margin-left: var(--sp-1);
  color: inherit; font-weight: var(--fw-semi); text-decoration: underline;
  text-underline-offset: 2px; cursor: pointer;
}
.kx-notice__text { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.kx-notice__close {
  flex: 0 0 auto; background: transparent; border: 0; cursor: pointer;
  color: currentColor; font-size: var(--fs-14); line-height: 1; opacity: .75;
}
.kx-notice__close:hover { opacity: 1; }

/* ── 左侧二级菜单 ────────────────────────────── */
.kx-subnav {
  grid-area: subnav;
  background: var(--bg-surface);
  border-right: 1px solid var(--bd-base);
  overflow-y: auto; overflow-x: hidden;
  padding: var(--sp-2) 0 var(--sp-6);
}
.kx-subnav__head {
  display: flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-4) var(--sp-2);
  font-size: var(--fs-12); font-weight: var(--fw-medium);
  color: var(--fg-tertiary); letter-spacing: .02em;
}
.kx-subnav__group { padding-top: var(--sp-2); }
.kx-subnav__label {
  padding: var(--sp-2) var(--sp-4) 4px;
  font-size: var(--fs-11); color: var(--fg-tertiary);
}
.kx-subnav__item {
  display: flex; align-items: center; gap: var(--sp-2);
  height: var(--h-subnav-item); padding: 0 var(--sp-4);
  font-size: var(--fs-13); color: var(--fg-second);
  text-decoration: none; cursor: pointer;
  border-left: 3px solid transparent;
  transition: background var(--dur-fast) var(--ease-in-out), color var(--dur-fast) var(--ease-in-out);
}
.kx-subnav__item:hover { background: var(--bg-sunken); color: var(--fg-primary); }
.kx-subnav__item[aria-current="page"] {
  color: var(--c-brand-600); background: var(--bg-brand-soft);
  border-left-color: var(--c-brand-500); font-weight: var(--fw-medium);
}
.kx-subnav__ico { width: 16px; flex: 0 0 16px; display: grid; place-items: center; font-size: 15px; }
.kx-subnav__text { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.kx-subnav__count {
  flex: 0 0 auto; min-width: 17px; height: 17px; padding: 0 4px;
  border-radius: var(--rd-full);
  background: var(--c-brand-500); color: var(--fg-on-brand);
  font-size: var(--fs-11); line-height: 17px; text-align: center; font-weight: var(--fw-medium);
}
.kx-subnav__count--warn { background: var(--c-warn-500); }
.kx-subnav__foot {
  margin-top: var(--sp-4); padding: var(--sp-3) var(--sp-4) 0;
  border-top: 1px solid var(--bd-subtle);
  font-size: var(--fs-11); color: var(--fg-tertiary); line-height: var(--lh-snug);
}

/* ── 主区 ────────────────────────────────────── */
.kx-main2 {
  grid-area: main;
  overflow-y: auto; overflow-x: hidden;
  padding: var(--pad-page);
  background: var(--bg-page);
  scroll-behavior: smooth;
}
@media (prefers-reduced-motion: reduce) { .kx-main2 { scroll-behavior: auto; } }

/* ── 图标基类（SVG 线性图标，随字号缩放）─────── */
.kx-ico {
  display: inline-block; width: 1em; height: 1em;
  flex: 0 0 auto; vertical-align: -0.125em;
  fill: none; stroke: currentColor;
  stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round;
}
.kx-ico--solid { fill: currentColor; stroke: none; }


/* ══════════════════════════════════════════════════════════════
   仪表盘网格（12 列）
   ══════════════════════════════════════════════════════════════ */
.kx-dash { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); gap: var(--gap-card); }
.kx-dash > * { min-width: 0; }
.kx-span-1  { grid-column: span 1; }
.kx-span-2  { grid-column: span 2; }
.kx-span-3  { grid-column: span 3; }
.kx-span-4  { grid-column: span 4; }
.kx-span-5  { grid-column: span 5; }
.kx-span-6  { grid-column: span 6; }
.kx-span-7  { grid-column: span 7; }
.kx-span-8  { grid-column: span 8; }
.kx-span-9  { grid-column: span 9; }
.kx-span-10 { grid-column: span 10; }
.kx-span-12 { grid-column: span 12; }

@media (max-width: 1280px) {
  .kx-dash { grid-template-columns: repeat(6, minmax(0, 1fr)); }
  .kx-span-2, .kx-span-3 { grid-column: span 3; }
  .kx-span-4, .kx-span-5, .kx-span-6, .kx-span-7, .kx-span-8, .kx-span-9, .kx-span-10 { grid-column: span 6; }
}
@media (max-width: 860px) {
  .kx-dash { grid-template-columns: minmax(0, 1fr); }
  .kx-dash > * { grid-column: span 1 !important; }
  .kx-shell { grid-template-columns: var(--w-subnav-collapsed) minmax(0, 1fr); }
  .kx-shell .kx-subnav__text, .kx-shell .kx-subnav__head { display: none; }
  .kx-shell .kx-subnav__item { justify-content: center; padding: 0; }
}


/* ══════════════════════════════════════════════════════════════
   统计卡（Stat）— E店通式「实时概况」单卡
   --------------------------------------------------------------
   关键结构：标签 → 大数值 → 底行（昨日值 | 较昨日变化）
   底行两端对齐是刻意的：左侧是「参照系」，右侧是「变化量」，
   眼睛只需横向扫一条线即可完成一次对比。
   ══════════════════════════════════════════════════════════════ */
.kx-stat-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--gap-card); }
@media (max-width: 1100px) { .kx-stat-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

.kx-stat {
  display: flex; flex-direction: column; gap: 2px;
  padding: var(--sp-4);
  background: var(--bg-surface);
  border-radius: var(--rd-md);
  box-shadow: var(--sh-xs);
  min-height: 108px;
}
.kx-stat__label {
  display: flex; align-items: center; gap: 4px;
  font-size: var(--fs-13); color: var(--fg-second);
}
.kx-stat__value {
  font-family: var(--ff-num); font-variant-numeric: tabular-nums;
  /* 用类型阶里已有的 --fs-30，不要临时塞一个 --fs-32：
     未定义的令牌不会报错，只会静默回退成继承字号，肉眼很难发现。 */
  font-size: var(--fs-30); font-weight: var(--fw-bold);
  color: var(--fg-primary); line-height: 1.1; letter-spacing: var(--ls-num);
}
.kx-stat__unit { font-size: var(--fs-14); font-weight: var(--fw-regular); color: var(--fg-second); margin-left: 2px; }
.kx-stat__foot {
  margin-top: auto; padding-top: var(--sp-2);
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2);
  border-top: 1px dashed var(--bd-subtle);
  font-size: var(--fs-12); color: var(--fg-tertiary);
}
.kx-stat__prev b {
  font-family: var(--ff-num); font-variant-numeric: tabular-nums;
  font-weight: var(--fw-medium); color: var(--fg-second); margin-left: 3px;
}
.kx-stat__delta {
  font-family: var(--ff-num); font-variant-numeric: tabular-nums; font-weight: var(--fw-medium);
}
/* ⚠️ 涨红跌绿（中国股市惯例）。但「基数无意义」时必须走 flat：
   本期 0 / 上期 0 的百分比变化没有经营含义，染红绿会误导判断。 */
.kx-stat__delta--up   { color: var(--c-pos-500); }
.kx-stat__delta--down { color: var(--c-neg-500); }
.kx-stat__delta--flat { color: var(--fg-tertiary); font-weight: var(--fw-regular); }
.kx-stat--brand { background: var(--bg-brand-soft); box-shadow: none; border: 1px solid var(--bd-brand-soft); }

/* 警示统计卡：用于「有异常需要处理」的指标。
   注意用警示橙而非报错红 —— 这是需要人介入的业务状态，不是系统故障。 */
.kx-stat--warn { background: var(--c-warn-bg); box-shadow: none; border: 1px solid var(--c-warn-500); }
.kx-stat--warn .kx-stat__value { color: var(--c-warn-600); }
.kx-stat--warn .kx-stat__foot { border-top-color: var(--c-warn-500); }


/* ══════════════════════════════════════════════════════════════
   待办事项网格（Todo）
   ══════════════════════════════════════════════════════════════ */
.kx-todo { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); }
.kx-todo__item {
  display: flex; flex-direction: column; gap: 1px; justify-content: center;
  padding: var(--sp-3);
  border-right: 1px solid var(--bd-subtle);
  border-bottom: 1px solid var(--bd-subtle);
}
.kx-todo__item:nth-child(3n) { border-right: 0; }
.kx-todo__item:nth-last-child(-n+3) { border-bottom: 0; }
.kx-todo__num {
  font-family: var(--ff-num); font-variant-numeric: tabular-nums;
  font-size: var(--fs-24); font-weight: var(--fw-bold);
  color: var(--fg-primary); line-height: 1.15; letter-spacing: var(--ls-num);
}
/* 数值为 0 = 「无待处理」，压暗以让有值的项跳出来 */
.kx-todo__item.is-zero .kx-todo__num { color: var(--fg-tertiary); font-weight: var(--fw-medium); }
.kx-todo__label {
  display: flex; align-items: center; gap: 4px;
  font-size: var(--fs-12); color: var(--fg-second);
}
.kx-todo__label .kx-ico { color: var(--c-brand-500); font-size: 13px; }
.kx-todo__item.is-zero .kx-todo__label .kx-ico { color: var(--fg-tertiary); }


/* ══════════════════════════════════════════════════════════════
   快捷入口（常用功能）— 4 列实心图标方格
   ══════════════════════════════════════════════════════════════ */
.kx-quick { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 2px; }
.kx-quick__item {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  padding: var(--sp-3) var(--sp-1);
  border-radius: var(--rd-sm); text-decoration: none; cursor: pointer;
  transition: background var(--dur-fast) var(--ease-in-out);
}
.kx-quick__item:hover { background: var(--bg-sunken); }
.kx-quick__ico {
  width: 38px; height: 38px; border-radius: var(--rd-md);
  display: grid; place-items: center;
  background: var(--c-brand-500); color: var(--fg-on-brand);
  font-size: 18px;
}
.kx-quick__ico--soft { background: var(--bg-brand-soft); color: var(--c-brand-600); }
.kx-quick__txt { font-size: var(--fs-12); color: var(--fg-second); text-align: center; line-height: var(--lh-snug); }


/* ══════════════════════════════════════════════════════════════
   应用图标网格（应用中心）— 分组 + 自适应列 + 推荐角标
   ══════════════════════════════════════════════════════════════ */
.kx-appgrid { display: flex; flex-direction: column; gap: var(--sp-5); }
.kx-appgrid__group { min-width: 0; }
.kx-appgrid__gtitle {
  display: flex; align-items: center; gap: var(--sp-2);
  margin-bottom: var(--sp-3);
  font-size: var(--fs-14); font-weight: var(--fw-semi); color: var(--fg-primary);
}
.kx-appgrid__gtitle::before {
  content: ''; width: 3px; height: 13px; border-radius: 2px; background: var(--c-brand-500);
}
.kx-appgrid__count { font-size: var(--fs-12); font-weight: var(--fw-regular); color: var(--fg-tertiary); }
.kx-appgrid__list { display: grid; grid-template-columns: repeat(auto-fill, minmax(92px, 1fr)); gap: var(--sp-1); }
.kx-appgrid__item {
  position: relative;
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  padding: var(--sp-3) var(--sp-2);
  border-radius: var(--rd-sm); text-decoration: none; cursor: pointer;
  transition: background var(--dur-fast) var(--ease-in-out);
}
.kx-appgrid__item:hover { background: var(--bg-sunken); }
.kx-appgrid__ico {
  width: var(--sz-icon-tile); height: var(--sz-icon-tile);
  border-radius: var(--rd-lg);
  display: grid; place-items: center;
  background: var(--c-brand-500); color: var(--fg-on-brand);
  font-size: 20px;
}
.kx-appgrid__ico--soft { background: var(--bg-brand-soft); color: var(--c-brand-600); }
.kx-appgrid__ico--muted { background: var(--bg-sunken); color: var(--fg-tertiary); }
.kx-appgrid__txt { font-size: var(--fs-12); color: var(--fg-second); text-align: center; line-height: var(--lh-snug); }
.kx-appgrid__badge {
  position: absolute; top: 8px; left: calc(50% - 26px);
  padding: 0 4px; border-radius: var(--rd-xs);
  background: var(--c-bad-500); color: var(--fg-on-brand);
  font-size: var(--fs-11); line-height: 15px; white-space: nowrap;
}
.kx-appgrid__lock { position: absolute; top: 12px; right: calc(50% - 30px); color: var(--fg-tertiary); font-size: 12px; }


/* ══════════════════════════════════════════════════════════════
   排行榜（Rank）— 名次徽标 + 名称 + 双列数值
   ══════════════════════════════════════════════════════════════ */
.kx-rank { display: flex; flex-direction: column; }
.kx-rank__row {
  display: grid; grid-template-columns: 22px minmax(0, 1fr) auto auto;
  gap: var(--sp-2); align-items: center;
  height: 36px; font-size: var(--fs-13);
  border-bottom: 1px solid var(--bd-subtle);
}
.kx-rank__row:last-child { border-bottom: 0; }
.kx-rank__no {
  width: 20px; height: 20px; border-radius: var(--rd-full);
  display: grid; place-items: center;
  background: var(--bg-sunken); color: var(--fg-second);
  font-family: var(--ff-num); font-size: var(--fs-11); font-weight: var(--fw-semi);
}
.kx-rank__row:nth-child(1) .kx-rank__no { background: var(--viz-gold);   color: var(--fg-on-brand); }
.kx-rank__row:nth-child(2) .kx-rank__no { background: var(--viz-silver); color: var(--fg-on-brand); }
.kx-rank__row:nth-child(3) .kx-rank__no { background: var(--viz-bronze); color: var(--fg-on-brand); }
.kx-rank__name { color: var(--fg-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.kx-rank__v {
  font-family: var(--ff-num); font-variant-numeric: tabular-nums;
  font-size: var(--fs-12); color: var(--fg-second); text-align: right; min-width: 52px;
}
.kx-rank__v--strong { color: var(--fg-primary); font-weight: var(--fw-medium); }


/* ══════════════════════════════════════════════════════════════
   横向条形榜（HBars）— 分销商 Top10 这类「名称 + 条 + 值」
   ══════════════════════════════════════════════════════════════ */
.kx-hbars { display: flex; flex-direction: column; gap: 7px; }
.kx-hbar { display: grid; grid-template-columns: 104px minmax(0, 1fr) auto; gap: var(--sp-2); align-items: center; }
.kx-hbar__name { font-size: var(--fs-12); color: var(--fg-second); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.kx-hbar__track { height: 10px; border-radius: var(--rd-xs); background: var(--bg-sunken); overflow: hidden; }
.kx-hbar__fill { height: 100%; border-radius: var(--rd-xs); background: var(--viz, var(--viz-2)); }
.kx-hbar__v {
  font-family: var(--ff-num); font-variant-numeric: tabular-nums;
  font-size: var(--fs-12); color: var(--fg-primary); text-align: right; min-width: 62px;
}
@media (max-width: 1280px) { .kx-hbar { grid-template-columns: 80px minmax(0, 1fr) auto; } }


/* ══════════════════════════════════════════════════════════════
   环形图（Donut）— conic-gradient 实现，无图表库依赖
   ══════════════════════════════════════════════════════════════ */
.kx-donut {
  position: relative; flex: 0 0 auto;
  width: 104px; height: 104px; border-radius: var(--rd-full);
  background: conic-gradient(var(--c, var(--viz-1)) var(--p, 0%), var(--bg-sunken) 0);
  display: grid; place-items: center;
}
.kx-donut--sm { width: 76px; height: 76px; }
.kx-donut__hole {
  width: 70%; height: 70%; border-radius: var(--rd-full);
  background: var(--bg-surface);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1px;
}
.kx-donut__cap { font-size: var(--fs-11); color: var(--fg-tertiary); line-height: 1.2; text-align: center; }
.kx-donut__val {
  font-family: var(--ff-num); font-variant-numeric: tabular-nums;
  font-size: var(--fs-16); font-weight: var(--fw-bold); color: var(--fg-primary); line-height: 1.2;
}

/* 图例 */
.kx-legend { display: flex; flex-wrap: wrap; gap: 6px var(--sp-4); font-size: var(--fs-12); color: var(--fg-second); }
.kx-legend__i { display: inline-flex; align-items: center; gap: 5px; }
.kx-legend__dot { width: 8px; height: 8px; border-radius: var(--rd-full); background: var(--viz, var(--viz-1)); flex: 0 0 auto; }


/* ══════════════════════════════════════════════════════════════
   分段切换（Seg）— 「7天 / 15天 / 30天」
   ══════════════════════════════════════════════════════════════ */
.kx-seg {
  display: inline-flex; flex: 0 0 auto;
  border: 1px solid var(--bd-base); border-radius: var(--rd-sm); overflow: hidden;
}
.kx-seg__item {
  height: 24px; padding: 0 10px;
  font-family: inherit; font-size: var(--fs-12);
  color: var(--fg-second); background: var(--bg-surface);
  border: 0; border-left: 1px solid var(--bd-base); cursor: pointer;
  transition: background var(--dur-fast) var(--ease-in-out), color var(--dur-fast) var(--ease-in-out);
}
.kx-seg__item:first-child { border-left: 0; }
.kx-seg__item:hover { color: var(--c-brand-600); }
.kx-seg__item[aria-pressed="true"] { background: var(--c-brand-500); color: var(--fg-on-brand); font-weight: var(--fw-medium); }

/* 图表容器 */
.kx-chart { width: 100%; height: 176px; }
.kx-chart svg { width: 100%; height: 100%; display: block; overflow: visible; }
.kx-chart--short { height: 132px; }


/* ══════════════════════════════════════════════════════════════
   卡片式列表（Tiles）— 门店 / 模板 / 套餐卡
   ══════════════════════════════════════════════════════════════ */
.kx-tiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(238px, 1fr)); gap: var(--gap-card); }
.kx-tile {
  display: flex; flex-direction: column; gap: var(--sp-3);
  padding: var(--sp-3);
  background: var(--bg-surface); border-radius: var(--rd-md);
  box-shadow: var(--sh-xs);
  transition: box-shadow var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-out);
}
.kx-tile:hover { box-shadow: var(--sh-md); transform: translateY(-1px); }
.kx-tile__bar { display: flex; align-items: center; gap: var(--sp-2); }
.kx-tile__thumb {
  display: grid; place-items: center;
  aspect-ratio: 3 / 4; border-radius: var(--rd-sm);
  background: var(--bg-sunken); color: var(--fg-tertiary);
  overflow: hidden; font-size: 26px;
}
.kx-tile__foot { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--sp-2); }
.kx-tile__name { font-size: var(--fs-14); font-weight: var(--fw-semi); color: var(--fg-primary); margin-bottom: 2px; }
.kx-tile__meta { font-size: var(--fs-11); color: var(--fg-tertiary); line-height: var(--lh-snug); }
.kx-tile__qr {
  width: 48px; height: 48px; flex: 0 0 auto;
  display: grid; place-items: center;
  border: 1px solid var(--bd-base); border-radius: var(--rd-xs);
  color: var(--fg-tertiary); background: var(--bg-surface);
}


/* ══════════════════════════════════════════════════════════════
   工具条（Toolbar2）— 列表页顶部「左主操作 + 右筛选」
   ══════════════════════════════════════════════════════════════ */
.kx-toolbar2 {
  display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap;
  padding: var(--sp-3) var(--sp-4); margin-bottom: var(--gap-card);
  background: var(--bg-surface); border-radius: var(--rd-md);
  box-shadow: var(--sh-xs);
}


/* ══════════════════════════════════════════════════════════════
   二级分页器（Pager2）— 「共 N 条 | ‹ 1 › | N 条/页 | 跳至 [] 页」
   ══════════════════════════════════════════════════════════════ */
.kx-pager2 {
  display: flex; align-items: center; justify-content: flex-end;
  gap: var(--sp-3); flex-wrap: wrap;
  padding: var(--sp-4) 0 var(--sp-1);
  font-size: var(--fs-13); color: var(--fg-second);
}
.kx-pager2__nav { display: inline-flex; align-items: center; gap: var(--sp-1); }
.kx-pager2__btn {
  min-width: 28px; height: 28px; padding: 0 6px;
  display: inline-grid; place-items: center;
  font-family: var(--ff-num); font-size: var(--fs-13);
  color: var(--fg-second); background: var(--bg-surface);
  border: 1px solid var(--bd-base); border-radius: var(--rd-sm); cursor: pointer;
  transition: color var(--dur-fast), border-color var(--dur-fast);
}
.kx-pager2__btn:hover:not(:disabled) { border-color: var(--c-brand-400); color: var(--c-brand-600); }
.kx-pager2__btn[aria-current="page"] {
  background: var(--c-brand-500); border-color: var(--c-brand-500);
  color: var(--fg-on-brand); font-weight: var(--fw-medium);
}
.kx-pager2__btn:disabled { color: var(--fg-tertiary); background: var(--bg-sunken); cursor: not-allowed; }
.kx-pager2__jump { display: inline-flex; align-items: center; gap: var(--sp-1); }
.kx-pager2__jump input {
  width: 46px; height: 28px; padding: 0 4px; text-align: center;
  font-family: var(--ff-num); font-size: var(--fs-13); color: var(--fg-primary);
  background: var(--bg-surface);
  border: 1px solid var(--bd-base); border-radius: var(--rd-sm);
}
.kx-pager2__jump input:focus { outline: none; border-color: var(--bd-focus); box-shadow: var(--sh-focus); }


/* ══════════════════════════════════════════════════════════════
   欢迎卡（Hello）— 工作台左上渐变卡
   ══════════════════════════════════════════════════════════════ */
.kx-hello {
  position: relative; overflow: hidden;
  display: flex; flex-direction: column; gap: 3px;
  padding: var(--sp-4);
  border-radius: var(--rd-md);
  background: linear-gradient(135deg, var(--c-brand-600) 0%, var(--c-brand-400) 100%);
  color: var(--fg-on-brand);
  min-height: 108px;
}
.kx-hello__deco {
  position: absolute; right: -30px; top: -30px;
  width: 104px; height: 104px; border-radius: var(--rd-full);
  background: var(--fg-on-brand); opacity: .10; pointer-events: none;
}
.kx-hello__title { font-size: var(--fs-16); font-weight: var(--fw-semi); }
.kx-hello__desc { font-size: var(--fs-11); line-height: var(--lh-snug); opacity: .85; }
.kx-hello__meta { font-size: var(--fs-11); opacity: .72; font-family: var(--ff-num); }
.kx-hello__store {
  display: flex; align-items: center; gap: var(--sp-2);
  margin-top: auto; padding-top: var(--sp-2);
  font-size: var(--fs-13); font-weight: var(--fw-medium);
}

/* 卡内「小标题 + 右上操作」通用条 */
.kx-cardhead {
  display: flex; align-items: center; gap: var(--sp-2);
  margin-bottom: var(--sp-3);
}
.kx-cardhead__t { font-size: var(--fs-14); font-weight: var(--fw-semi); color: var(--fg-primary); }
.kx-cardhead__sub { font-size: var(--fs-11); color: var(--fg-tertiary); }
.kx-cardhead__spacer { flex: 1 1 auto; }
.kx-cardhead__link { font-size: var(--fs-12); color: var(--fg-link); text-decoration: none; cursor: pointer; }
.kx-cardhead__link:hover { text-decoration: underline; }


/* ══════════════════════════════════════════════════════════════
   列表页三段式骨架（Listpage）— 对齐 E店通平台后台
   ------------------------------------------------------------
   参考后台（平台运营端）80% 的页面是同一个骨架：

     ① 统计卡组 ── 兼作快速筛选（首个卡=「全部」，默认选中）
     ② 筛选区   ── 3 列网格，label 在左、控件在右
     ③ 结果区   ── 数量/金额汇总行 + 表格 + 分页

   为什么把统计卡做成「筛选器」而不是纯展示：
   用户看到「已过期 13」的第一反应是「都是哪些？」。若卡不可点，
   他必须自己去下方筛选区拼条件。可点则一步直达，且顺带回答了
   「一共多少」——把两块信息压进一次点击，是这类后台最高频的动作。
   ══════════════════════════════════════════════════════════════ */

.kx-listpage { display: flex; flex-direction: column; gap: var(--gap-card); }

/* ── ① 统计卡组 ─────────────────────────────── */
.kx-fstat { display: grid; grid-template-columns: repeat(auto-fit, minmax(184px, 1fr)); gap: var(--gap-card); }
.kx-fstat__item {
  display: flex; flex-direction: column; gap: 2px;
  padding: var(--sp-4);
  text-align: left;
  background: var(--bg-surface);
  border: 1px solid var(--bd-base);
  border-radius: var(--rd-md);
  box-shadow: var(--sh-xs);
  cursor: pointer; min-width: 0;
  transition: background var(--dur-fast) var(--ease-in-out),
              border-color var(--dur-fast) var(--ease-in-out);
}
.kx-fstat__item:hover { border-color: var(--bd-selected); }
.kx-fstat__item:focus-visible { outline: none; box-shadow: var(--sh-focus); }
/* 选中态：品牌淡底 + 品牌边框双重编码。只靠淡底不够——在低质量屏幕上
   淡底可能被压成灰白，边框是保底的可辨识信号。 */
.kx-fstat__item[aria-pressed="true"] { background: var(--bg-selected); border-color: var(--bd-selected); }
.kx-fstat__item[aria-pressed="true"] .kx-fstat__label { color: var(--c-brand-600); font-weight: var(--fw-medium); }

.kx-fstat__label {
  display: flex; align-items: center; gap: 5px;
  font-size: var(--fs-13); color: var(--fg-second);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.kx-fstat__num {
  font-family: var(--ff-num); font-variant-numeric: tabular-nums;
  font-size: var(--fs-24); font-weight: var(--fw-bold);
  color: var(--fg-primary); line-height: 1.25;
}
.kx-fstat__unit { font-size: var(--fs-13); font-weight: var(--fw-regular); color: var(--fg-second); margin-left: 2px; }
.kx-fstat__sub { font-size: var(--fs-11); color: var(--fg-tertiary); }
/* 计数为 0 的次级卡压暗：四个卡里三个是 0 时，真值会被一片黑淹没 */
.kx-fstat__item.is-zero .kx-fstat__num { color: var(--fg-tertiary); }

/* 指标释义气泡。用原生 title 承载文案，不引 JS tooltip ——
   这类「到期不足15天」的解释是静态的，为它上浮层组件不划算。 */
.kx-tip {
  flex: 0 0 auto; display: inline-grid; place-items: center;
  width: 13px; height: 13px; border-radius: var(--rd-full);
  background: var(--bg-sunken); color: var(--fg-tertiary);
  font-size: 9px; font-weight: var(--fw-semi); cursor: help;
}

/* ── ② 筛选区 ───────────────────────────────── */
.kx-filters {
  padding: var(--sp-4);
  background: var(--bg-surface);
  border: 1px solid var(--bd-base);
  border-radius: var(--rd-md);
  box-shadow: var(--sh-xs);
}
.kx-filters__grid {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--sp-3) var(--sp-5);
}
.kx-filters__item {
  display: grid; grid-template-columns: 64px minmax(0, 1fr);
  align-items: center; gap: var(--sp-2); min-width: 0;
}
.kx-filters__label { font-size: var(--fs-13); color: var(--fg-second); text-align: right; white-space: nowrap; }
.kx-filters__ctl { display: flex; align-items: center; gap: var(--sp-1); min-width: 0; }
.kx-filters__ctl > .kx-input,
.kx-filters__ctl > .kx-select,
.kx-filters__ctl > .kx-inputgroup { width: 100%; min-width: 0; }
/* 日期区间：两个输入 + 中间连接符，两端等分不溢出 */
.kx-filters__range { display: flex; align-items: center; gap: 4px; min-width: 0; width: 100%; }
.kx-filters__range > .kx-input { flex: 1 1 0; min-width: 0; }
.kx-filters__sep { flex: 0 0 auto; color: var(--fg-tertiary); font-size: var(--fs-12); }
.kx-filters__foot {
  display: flex; align-items: center; gap: var(--sp-2);
  margin-top: var(--sp-4); padding-top: var(--sp-3);
  border-top: 1px dashed var(--bd-subtle);
}
.kx-filters__spacer { flex: 1 1 auto; }
.kx-filters__toggle {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: var(--fs-12); color: var(--fg-link);
  background: none; border: 0; padding: 0; cursor: pointer;
}
.kx-filters__toggle:hover { text-decoration: underline; }
@media (max-width: 1080px) { .kx-filters__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 720px) {
  .kx-filters__grid { grid-template-columns: 1fr; }
  .kx-filters__item { grid-template-columns: 1fr; }
  .kx-filters__label { text-align: left; }
}

/* ── ③ 结果区汇总行 ─────────────────────────── */
.kx-resulthead {
  display: flex; align-items: baseline; gap: var(--sp-3); flex-wrap: wrap;
  padding-bottom: var(--sp-3);
  font-size: var(--fs-13); color: var(--fg-second);
}
.kx-resulthead__t { font-size: var(--fs-14); font-weight: var(--fw-semi); color: var(--fg-primary); }
.kx-resulthead__sum {
  font-family: var(--ff-num); font-variant-numeric: tabular-nums;
  font-size: var(--fs-14); font-weight: var(--fw-semi); color: var(--fg-primary);
}
/* 口径说明：金额类汇总必须写清算法，否则对账时无人能复现 */
.kx-resulthead__note { font-size: var(--fs-11); color: var(--fg-tertiary); }
.kx-resulthead__spacer { flex: 1 1 auto; }
.kx-resulthead__act { font-size: var(--fs-12); color: var(--fg-link); cursor: pointer; background: none; border: 0; padding: 0; }
.kx-resulthead__act:hover { text-decoration: underline; }


/* ══════════════════════════════════════════════════════════════
   表格增强（Table 2.0）
   ══════════════════════════════════════════════════════════════ */

/* 斑马纹：行数多且列多时，比 hover 更能帮眼睛横向追踪。
   ⚠️ 走中性阶（--c-n-150），它在深色主题下会被反转，安全。 */
.kx-table--zebra tbody tr:nth-child(even) td { background: var(--c-n-150); }
.kx-table--zebra tbody tr:hover td { background: var(--bg-row-hover); }

/* 异常行（幂等冲突、对账不符这类需要一眼定位的行）
   用「整行淡红底 + 左侧 3px 竖条」双重编码：只染底色的话，
   色觉障碍用户看到的就是一条普通行。竖条是第二通道。
   必须写在斑马纹规则之后，同特异性下靠源码顺序取胜。 */
.kx-table tbody tr.row-bad td { background: var(--c-bad-bg); }
.kx-table tbody tr.row-bad td:first-child { box-shadow: inset 3px 0 0 var(--c-bad-500); }

/* 复合单元格：缩略图/头像 + 主标题 + 副标题
   参考后台的「店铺信息」「用户信息」列都是这种双行结构，比把
   手机号另开一列更省宽度，也避免了两列的视觉关联被拉断。 */
.kx-cell2 { display: flex; align-items: center; gap: var(--sp-2); min-width: 0; }
.kx-cell2__media {
  flex: 0 0 auto; width: 28px; height: 28px; border-radius: var(--rd-sm);
  display: grid; place-items: center; overflow: hidden;
  background: var(--bg-sunken); color: var(--fg-tertiary); font-size: 13px;
}
.kx-cell2__body { display: block; min-width: 0; }
.kx-cell2__t {
  display: block;
  font-size: var(--fs-13); font-weight: var(--fw-medium); color: var(--fg-primary);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.kx-cell2__s {
  display: block;
  font-family: var(--ff-num); font-size: var(--fs-11); color: var(--fg-tertiary);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* 链接与操作列 */
.kx-link { color: var(--fg-link); text-decoration: none; cursor: pointer; background: none; border: 0; padding: 0; font-size: inherit; }
.kx-link:hover { text-decoration: underline; }
.kx-ops { display: inline-flex; align-items: center; gap: var(--sp-2); white-space: nowrap; }
.kx-ops__i {
  color: var(--fg-link); font-size: var(--fs-13);
  background: none; border: 0; padding: 0; cursor: pointer;
}
.kx-ops__i:hover { text-decoration: underline; }
.kx-ops__i--danger { color: var(--c-bad-500); }
.kx-ops__i--muted { color: var(--fg-tertiary); cursor: not-allowed; }
.kx-ops__sep { color: var(--bd-strong); user-select: none; }

/* 金额/数字列 ——
   ⚠️ 三条强制规则，缺一条就会出现参考后台那种「33827 / 8.71」
   上下折行的数字，两行数字在视觉上会被读成两个量。 */
.kx-table td.amount, .kx-table th.amount {
  text-align: right;
  white-space: nowrap;                     /* 1. 绝不折行 */
  font-family: var(--ff-num);
  font-variant-numeric: tabular-nums;      /* 2. 等宽数字，跨行小数点对齐 */
}
.kx-table td.amount--strong { color: var(--fg-primary); font-weight: var(--fw-semi); }

/* 可排序表头 */
.kx-th-sort { display: inline-flex; align-items: center; gap: 3px; cursor: pointer; user-select: none; }
.kx-th-sort:hover { color: var(--fg-primary); }
.kx-th-sort__ico { font-size: 9px; color: var(--fg-tertiary); line-height: 1; }
.kx-th-sort[aria-sort="ascending"] .kx-th-sort__ico,
.kx-th-sort[aria-sort="descending"] .kx-th-sort__ico { color: var(--c-brand-500); }


/* ══════════════════════════════════════════════════════════════
   页签（Tabs）— 对齐参考后台「应用管理 / 应用分组」
   ══════════════════════════════════════════════════════════════ */
.kx-tabs {
  display: flex; align-items: center; gap: var(--sp-5);
  border-bottom: 1px solid var(--bd-base);
  margin-bottom: var(--sp-4);
  overflow-x: auto;
}
.kx-tabs__item {
  position: relative; flex: 0 0 auto;
  padding: 0 0 var(--sp-3);
  font-size: var(--fs-14); color: var(--fg-second);
  background: none; border: 0; cursor: pointer; white-space: nowrap;
  transition: color var(--dur-fast) var(--ease-in-out);
}
.kx-tabs__item:hover { color: var(--fg-primary); }
.kx-tabs__item[aria-selected="true"] { color: var(--c-brand-600); font-weight: var(--fw-semi); }
/* 下划线指示条压在 1px 分隔线上（bottom:-1px），避免出现双线 */
.kx-tabs__item[aria-selected="true"]::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: -1px;
  height: 2px; border-radius: 1px; background: var(--c-brand-500);
}
.kx-tabs__count { font-family: var(--ff-num); font-size: var(--fs-11); color: var(--fg-tertiary); margin-left: 4px; }


/* ══════════════════════════════════════════════════════════════
   开关（Switch）— 应用上下架
   ------------------------------------------------------------
   用原生 checkbox + label 换皮，不写 JS 也能工作，
   且天然带着键盘可达性与读屏语义（role=switch 可由 aria 补充）。
   ══════════════════════════════════════════════════════════════ */
.kx-switch { position: relative; display: inline-flex; align-items: center; flex: 0 0 auto; cursor: pointer; }
.kx-switch input {
  position: absolute; inset: 0; width: 100%; height: 100%;
  margin: 0; opacity: 0; cursor: pointer;
}
.kx-switch__track {
  position: relative; display: block;
  width: 34px; height: 19px; border-radius: var(--rd-full);
  background: var(--bd-strong);
  transition: background var(--dur-base) var(--ease-in-out);
}
.kx-switch__track::after {
  content: ''; position: absolute; top: 2px; left: 2px;
  width: 15px; height: 15px; border-radius: var(--rd-full);
  /* 滑块恒为白：深色主题下换成深色滑块会与深色轨道糊在一起 */
  background: var(--c-const-white); box-shadow: var(--sh-xs);
  transition: transform var(--dur-base) var(--ease-out);
}
.kx-switch input:checked + .kx-switch__track { background: var(--c-brand-500); }
.kx-switch input:checked + .kx-switch__track::after { transform: translateX(15px); }
.kx-switch input:focus-visible + .kx-switch__track { box-shadow: var(--sh-focus); }
.kx-switch input:disabled + .kx-switch__track { opacity: .45; }
.kx-switch input:disabled { cursor: not-allowed; }
.kx-switch__txt { font-size: var(--fs-12); color: var(--fg-second); margin-left: var(--sp-2); }
.kx-switch input:checked ~ .kx-switch__txt { color: var(--c-brand-600); }


/* ══════════════════════════════════════════════════════════════
   应用大卡（Appcard）— 对齐参考后台「应用管理」
   ------------------------------------------------------------
   与 .kx-appgrid（小方格）的分工：
   · appgrid  = 纯入口导航，只需「图标 + 名称」
   · appcard  = 带商业属性的模块，需要展示 周期 / 价格 / 上下架
   ══════════════════════════════════════════════════════════════ */
.kx-appcards { display: grid; grid-template-columns: repeat(auto-fill, minmax(272px, 1fr)); gap: var(--gap-card); }
.kx-appcard {
  display: flex; flex-direction: column; gap: var(--sp-3);
  padding: var(--sp-4);
  background: var(--bg-surface);
  border: 1px solid var(--bd-base);
  border-radius: var(--rd-md);
  box-shadow: var(--sh-xs);
  transition: border-color var(--dur-fast) var(--ease-in-out),
              box-shadow var(--dur-fast) var(--ease-in-out);
}
.kx-appcard:hover { border-color: var(--bd-selected); box-shadow: var(--sh-sm); }
.kx-appcard__top { display: flex; align-items: flex-start; gap: var(--sp-3); }
.kx-appcard__ico {
  flex: 0 0 auto; width: 40px; height: 40px; border-radius: var(--rd-md);
  display: grid; place-items: center;
  background: var(--c-brand-500); color: var(--fg-on-brand); font-size: 19px;
}
.kx-appcard__ico--soft  { background: var(--bg-brand-soft); color: var(--c-brand-600); }
.kx-appcard__ico--muted { background: var(--bg-sunken); color: var(--fg-tertiary); }
.kx-appcard__meta { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.kx-appcard__t {
  display: flex; align-items: center; gap: 5px;
  font-size: var(--fs-14); font-weight: var(--fw-semi); color: var(--fg-primary);
}
.kx-appcard__d { font-size: var(--fs-12); color: var(--fg-second); line-height: var(--lh-snug); }
.kx-appcard__sw { flex: 0 0 auto; }

/* 周期条：用只读输入框的形态承载「永久 / 12个月」，
   视觉上比纯文本更像契约条款，也更难被误读成可编辑 */
.kx-appcard__term {
  display: flex; align-items: center; justify-content: center;
  height: var(--h-control-sm); padding: 0 var(--sp-3);
  background: var(--bg-surface);
  border: 1px solid var(--bd-base); border-radius: var(--rd-sm);
  font-size: var(--fs-12); color: var(--fg-second);
}
.kx-appcard__foot { display: flex; align-items: center; gap: var(--sp-2); margin-top: auto; }
.kx-appcard__price {
  font-family: var(--ff-num); font-variant-numeric: tabular-nums;
  font-size: var(--fs-13); font-weight: var(--fw-semi); color: var(--c-price);
}
.kx-appcard__price u { text-decoration: none; font-family: var(--ff-sans); font-size: var(--fs-11); font-weight: var(--fw-regular); color: var(--fg-tertiary); }
.kx-appcard__act {
  margin-left: auto; font-size: var(--fs-12); color: var(--fg-link);
  background: none; border: 0; padding: 0; cursor: pointer;
}
.kx-appcard__act:hover { text-decoration: underline; }
.kx-appcard__badge {
  flex: 0 0 auto; padding: 0 4px; border-radius: var(--rd-xs); line-height: 15px;
  font-size: var(--fs-11); background: var(--c-bad-bg); color: var(--c-bad-fg);
}
/* 已下架：降噪但不置灰到读不清 —— 用户仍需判断「它是什么、要不要开」 */
.kx-appcard.is-off { box-shadow: none; }
.kx-appcard.is-off .kx-appcard__ico,
.kx-appcard.is-off .kx-appcard__t { opacity: .62; }
.kx-appcard.is-off .kx-appcard__term { color: var(--fg-tertiary); }
.kx-appcard.is-off .kx-appcard__price { color: var(--fg-tertiary); }


/* ══════════════════════════════════════════════════════════════
   工具卡网格（Toolgrid）— 图标在左、标题+描述在右
   ══════════════════════════════════════════════════════════════ */
.kx-toolgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(224px, 1fr)); gap: var(--gap-card); }
.kx-toolcard {
  display: flex; align-items: flex-start; gap: var(--sp-3);
  padding: var(--sp-4);
  background: var(--bg-surface);
  border: 1px solid var(--bd-base);
  border-radius: var(--rd-md);
  box-shadow: var(--sh-xs);
  cursor: pointer; text-decoration: none; min-width: 0;
  transition: background var(--dur-fast) var(--ease-in-out),
              border-color var(--dur-fast) var(--ease-in-out);
}
.kx-toolcard:hover { border-color: var(--bd-selected); background: var(--bg-brand-soft); }
.kx-toolcard.is-active { border-color: var(--bd-selected); background: var(--bg-selected); }
.kx-toolcard__ico {
  flex: 0 0 auto; width: 32px; height: 32px; border-radius: var(--rd-sm);
  display: grid; place-items: center;
  background: var(--c-brand-500); color: var(--fg-on-brand); font-size: 16px;
}
.kx-toolcard__ico--soft { background: var(--bg-brand-soft); color: var(--c-brand-600); }
.kx-toolcard__body { min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.kx-toolcard__t { font-size: var(--fs-13); font-weight: var(--fw-medium); color: var(--fg-primary); }
.kx-toolcard__d { font-size: var(--fs-11); color: var(--fg-tertiary); line-height: var(--lh-snug); }


/* ══════════════════════════════════════════════════════════════
   详情网格（Deflist）— 授权详情这类「标签 : 值」只读信息
   ══════════════════════════════════════════════════════════════ */
.kx-deflist { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--sp-4) var(--sp-5); }
.kx-defitem { display: grid; grid-template-columns: 78px minmax(0, 1fr); gap: var(--sp-2); align-items: center; min-width: 0; }
.kx-defitem__k { display: flex; align-items: center; gap: 4px; font-size: var(--fs-13); color: var(--fg-tertiary); }
.kx-defitem__v {
  display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap;
  font-size: var(--fs-13); color: var(--fg-primary); min-width: 0;
}
/* 授权码/域名/IP 这类长串必须允许断行，否则会把整列撑破 */
.kx-defitem__v--break { word-break: break-all; }
.kx-defitem__v--num { font-family: var(--ff-num); font-variant-numeric: tabular-nums; }
.kx-defitem__sub { font-size: var(--fs-11); color: var(--fg-tertiary); }
@media (max-width: 1080px) { .kx-deflist { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 720px)  { .kx-deflist { grid-template-columns: 1fr; } }

/* 品牌色表头的卡片 —— 「授权详情」这类需要一眼定位的区块 */
.kx-card__head--brand { background: var(--c-brand-500); border-bottom-color: transparent; }
.kx-card__head--brand .kx-card__title { color: var(--fg-on-brand); }
.kx-card__head--brand .kx-card__sub { color: var(--fg-on-brand); opacity: .82; }


/* ══════════════════════════════════════════════════════════════
   时间轴 / 更新日志（Timeline）
   ══════════════════════════════════════════════════════════════ */
.kx-timeline { display: flex; flex-direction: column; }
.kx-timeline__item { position: relative; padding-left: 22px; padding-bottom: var(--sp-5); }
.kx-timeline__item:last-child { padding-bottom: 0; }
/* 竖线：从本项圆点下沿连到下一项圆点上沿；末项不画 */
.kx-timeline__item::before {
  content: ''; position: absolute; left: 4px; top: 14px; bottom: -5px;
  width: 1px; background: var(--bd-base);
}
.kx-timeline__item:last-child::before { display: none; }
.kx-timeline__item::after {
  content: ''; position: absolute; left: 0; top: 5px;
  width: 9px; height: 9px; border-radius: var(--rd-full);
  background: var(--bg-surface); border: 2px solid var(--bd-strong);
}
/* 最新一版用实心品牌色圆点，一眼看出「当前在哪」 */
.kx-timeline__item--latest::after { background: var(--c-brand-500); border-color: var(--c-brand-500); }

.kx-timeline__head { display: flex; align-items: baseline; gap: var(--sp-2); flex-wrap: wrap; }
.kx-timeline__ver {
  font-family: var(--ff-num); font-size: var(--fs-14);
  font-weight: var(--fw-semi); color: var(--c-brand-600);
}
.kx-timeline__date { font-family: var(--ff-num); font-size: var(--fs-12); color: var(--fg-tertiary); }
.kx-timeline__list { display: flex; flex-direction: column; gap: 5px; margin: var(--sp-2) 0 0; padding: 0; list-style: none; }
.kx-timeline__li {
  display: flex; align-items: flex-start; gap: var(--sp-2);
  font-size: var(--fs-12); color: var(--fg-second); line-height: var(--lh-snug);
}
.kx-timeline__tag {
  flex: 0 0 auto; padding: 0 4px; border-radius: var(--rd-xs);
  font-size: var(--fs-11); line-height: 16px;
  background: var(--c-ok-bg); color: var(--c-ok-fg);
}
.kx-timeline__tag--warn { background: var(--c-warn-bg); color: var(--c-warn-fg); }
.kx-timeline__tag--bad  { background: var(--c-bad-bg);  color: var(--c-bad-fg); }
.kx-timeline__tag--info { background: var(--c-info-bg); color: var(--c-info-fg); }


/* ══════════════════════════════════════════════════════════════
   权益展示通栏（Band）— 对齐参考后台「VIP · 权益列表」
   ------------------------------------------------------------
   这是全站唯一允许「不讲信息密度」的地方：它承担的是说服
   （这块能力值不值这个价），不是操作。因此用恒定深色 + 居中
   排版把节奏拉开，与日常页面的紧凑形成对比。
   ⚠️ 底色走 --bg-dark / --fg-on-dark 恒定深色令牌组，
   两主题下都是深底浅字，不随主题反转。
   ══════════════════════════════════════════════════════════════ */
.kx-band {
  position: relative; overflow: hidden;
  display: flex; flex-direction: column; align-items: center; gap: var(--sp-3);
  padding: var(--sp-8) var(--sp-6);
  border-radius: var(--rd-lg);
  background: linear-gradient(158deg, var(--bg-dark) 0%, var(--bg-dark-2) 100%);
  color: var(--fg-on-dark);
  text-align: center;
}
.kx-band__eyebrow { display: flex; align-items: center; gap: var(--sp-2); font-size: var(--fs-14); font-weight: var(--fw-semi); color: var(--fg-on-dark-dim); }
.kx-band__logo {
  width: 26px; height: 26px; border-radius: var(--rd-sm);
  display: grid; place-items: center;
  background: var(--c-brand-500); color: var(--fg-on-brand); font-size: 14px;
}
.kx-band__title { font-size: var(--fs-38); font-weight: var(--fw-bold); letter-spacing: .05em; line-height: 1.15; }
.kx-band__sub { font-size: var(--fs-13); color: var(--fg-on-dark-dim); }
/* 「—— 20 大专属权益 ——」：用伪元素画两侧横线，省一层 DOM */
.kx-band__rule {
  display: flex; align-items: center; gap: var(--sp-3);
  width: min(460px, 100%);
  font-size: var(--fs-14); color: var(--fg-on-dark-dim);
}
.kx-band__rule::before, .kx-band__rule::after {
  content: ''; flex: 1 1 auto; height: 1px; background: var(--bd-on-dark);
}
.kx-band__grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(148px, 1fr));
  gap: var(--sp-3); width: 100%; margin-top: var(--sp-2);
}
.kx-band__cell {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  padding: var(--sp-3) var(--sp-2);
  background: var(--bg-dark-2);
  border: 1px solid var(--bd-on-dark);
  border-radius: var(--rd-sm);
}
.kx-band__cell-ico {
  width: 38px; height: 38px; border-radius: var(--rd-full);
  display: grid; place-items: center;
  background: var(--c-brand-500); color: var(--fg-on-brand); font-size: 17px;
}
.kx-band__cell-t { font-size: var(--fs-13); font-weight: var(--fw-medium); color: var(--fg-on-dark); }
.kx-band__cell-d { font-size: var(--fs-11); color: var(--fg-on-dark-dim); line-height: var(--lh-snug); }


/* ══════════════════════════════════════════════════════════════
   统计卡补充：构成拆解副行
   ------------------------------------------------------------
   .kx-stat__foot 回答「和昨天比怎么样」（参照系）
   .kx-stat__sub  回答「这个数由什么构成」（拆解）
   两者语义不同，不能混用同一行。
   ══════════════════════════════════════════════════════════════ */
.kx-stat__sub {
  display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap;
  padding-top: var(--sp-1);
  font-size: var(--fs-11); color: var(--fg-tertiary);
}
.kx-stat__sub i { font-style: normal; font-family: var(--ff-num); color: var(--fg-second); }


/* ══════════════════════════════════════════════════════════════
   套餐 / 权益卡（Plan）
   ══════════════════════════════════════════════════════════════ */

/* 高亮推荐档：用 1px 品牌描边（box-shadow 描边不占布局宽度，
   避免与相邻卡的高度/宽度错位）*/
.kx-card--featured { border-color: transparent; box-shadow: 0 0 0 1px var(--c-brand-500), var(--sh-md); }

.kx-price {
  font-family: var(--ff-num); font-variant-numeric: tabular-nums;
  font-size: var(--fs-24); font-weight: var(--fw-bold);
  color: var(--c-price); line-height: 1.2;
}
.kx-price u { text-decoration: none; font-family: var(--ff-sans); font-size: var(--fs-13); font-weight: var(--fw-regular); color: var(--fg-tertiary); }

.kx-featlist { display: flex; flex-direction: column; gap: var(--sp-2); margin: 0; padding: 0; list-style: none; }
.kx-featlist li { display: flex; align-items: flex-start; gap: var(--sp-2); font-size: var(--fs-13); color: var(--fg-second); line-height: var(--lh-snug); }
.kx-featlist svg { flex: 0 0 auto; margin-top: 3px; color: var(--c-ok-500); }
/* 未包含项：划线 + 压暗，比只压暗多一层「明确没有」的语义 */
.kx-featlist li.is-off { color: var(--fg-tertiary); }
.kx-featlist li.is-off svg { color: var(--fg-tertiary); }

/* 页内警示条：复用公告条配色，但去掉外壳栅格的高度约束、补圆角 */
.kx-notice--inline { height: auto; min-height: var(--h-notice-bar); padding: var(--sp-2) var(--sp-3); border-radius: var(--rd-md); align-items: flex-start; }
.kx-notice--inline .kx-notice__text { white-space: normal; }
