/* ============================================================
   精修 · 按钮与控件体系（ds-refine-controls.css）
   ------------------------------------------------------------
   范围：只做按钮 / 图标按钮 / 输入控件 / toast / 弹窗底部操作区的
   视觉与手感。不改 data-action / data-form / name / required /
   disabled 契约，不改路由、接口、数据口径，不改任何 .js。

   引入位置（硬性）：必须在 tokens.css + styles.css + ds-controls.css
   「之后」。本文件用 `body` 前缀（+1 元素特异性）保证三件事：
     1) 稳定压过裸单类选择器（.btn / .close-btn / .field input），
        因此不受同批 ds-*.css 引入顺序微调影响；
     2) 主动让位给任何"页面域"上下文覆盖（.hero .btn、
        html.force-mobile .person-actions .btn 等，特异性更高）；
     3) 与 ds-controls.css 保持同一设计意图：只收口基元，不抢页面域。

   依据：
     · make-interfaces-feel-better → 同心圆角(1) / 阴影抬升、边框表结构(3) /
       可中断过渡(4) / 按压 scale(.96)(12) / tabular-nums(9) /
       禁用 transition:all(14) / 44px 触控区、热区不得重叠(16) /
       图标描边配字重(17) / 动效克制(19)
     · ui-ux-pro-max → 无障碍优先(1) / 触控与交互 44×44、间距≥8px、
       必须有加载反馈(2) / 表单可见标签与就近错误(8)
   变量真源：tokens.css。本文件只补 --rc-* 局部别名；
   --rc-* 里凡含字面色值的，均同时给出 :root（浅色）与
   html[data-theme="dark"]（深色）两套值。
   ============================================================ */

/* ---------------- 变量：几何（与主题无关） ---------------- */
:root {
  /* 三级按钮统一几何：主 / 次 / 危险 只差颜色，不差骨架 */
  --rc-btn-h: 44px;             /* 移动端触控下限（原则 16） */
  --rc-btn-h-dense: 40px;       /* 密集处下限，绝不低于此 */
  --rc-btn-h-sm: 32px;          /* 仅 .tiny：视觉 32，热区由伪元素补到 44 */
  --rc-btn-px: 16px;
  --rc-btn-px-sm: 12px;
  --rc-btn-gap: 8px;            /* 按钮组间距（ui-ux-pro-max：间距 ≥8px） */
  --rc-btn-radius: var(--ds-radius-button);

  /* 图标按钮：标准 40×40 / 图标 20px；密集 32×32 / 图标 18px */
  --rc-icon-btn: 40px;
  --rc-icon-btn-dense: 32px;
  --rc-icon-size: 20px;
  --rc-icon-size-dense: 18px;

  /* 输入控件与按钮同高，圆角走字段半径 */
  --rc-field-h: var(--rc-btn-h);
  --rc-field-h-dense: var(--rc-btn-h-dense);
  --rc-field-radius: var(--ds-radius-field);
  --rc-field-px: 14px;

  /* 手感：按压一律 scale(.96)，不得小于 .95（原则 12） */
  --rc-press: .96;
  --rc-dur-press: 120ms;
  --rc-dur-state: 160ms;
  --rc-ease: cubic-bezier(.2, 0, 0, 1);

  /* 焦点：2px 描边 + 2px 偏移，保证两种主题下都可见（无障碍优先） */
  --rc-focus-w: 2px;
  --rc-focus-offset: 2px;

  /* 底部安全区：mobile-nav 高度约 70px，浮动主行动必须让出这段 */
  --rc-safe-bottom: calc(72px + env(safe-area-inset-bottom, 0px));

  --rc-disabled-opacity: .75;   /* 与 --ds-ink 组合后正文对比度仍 >7:1 */
}

/* ---------------- 变量：颜色 ---------------- */
:root {
  /* 次行动（也是按钮的默认层级） */
  --rc-secondary-bg: var(--ds-surface);
  --rc-secondary-bg-hover: var(--ds-surface-soft);
  --rc-secondary-fg: var(--ds-ink);
  --rc-secondary-border: var(--ds-border);
  --rc-ghost-fg: var(--ds-text-secondary);

  /* 危险层级 */
  --rc-danger: var(--ds-error);
  --rc-danger-hover: #832424;
  --rc-danger-soft: rgba(154, 42, 42, .10);
  --rc-danger-border: rgba(154, 42, 42, .38);
  --rc-danger-soft-hover: rgba(154, 42, 42, .16);
  --rc-on-danger: #FFFFFF;      /* 朱砂底用白字，对比度 6.4:1 */

  /* 品牌浅底按钮（.soft）的 hover 态 */
  --rc-brand-soft-hover: rgba(154, 42, 42, .14);

  /* 错误态字段底色（与 ds-controls 的 --ds-ctl-error-soft 同口径） */
  --rc-error-soft: rgba(154, 42, 42, .07);

  /* 禁用态：靠色值下调而非整体压暗，保证文字仍可读 */
  --rc-disabled-bg: var(--ds-surface-soft);
  --rc-disabled-fg: var(--ds-ink);
  --rc-disabled-border: var(--ds-border);

  /* 抬升：主行动给一层极浅投影（原则 3：阴影表抬升，边框表结构） */
  --rc-elev-1: 0 1px 2px rgba(26, 26, 26, .10);
  --rc-elev-2: 0 4px 12px rgba(26, 26, 26, .14);
}

html[data-theme="dark"] {
  --rc-danger-hover: #F09A87;
  --rc-danger-soft: rgba(232, 131, 111, .14);
  --rc-danger-border: rgba(232, 131, 111, .42);
  --rc-danger-soft-hover: rgba(232, 131, 111, .22);
  --rc-on-danger: #15121A;      /* 琥珀底必须换墨色字，与 --on-brand 同口径 */
  --rc-brand-soft-hover: rgba(232, 163, 74, .22);
  --rc-error-soft: rgba(232, 131, 111, .14);
  --rc-elev-1: 0 1px 2px rgba(0, 0, 0, .45);
  --rc-elev-2: 0 6px 18px rgba(0, 0, 0, .50);
}

/* ============================================================
   1 · 按钮基座：三级体系共用同一副骨架
   层级只改颜色，不改几何 → 主/次/危险并排时高度、圆角、
   内边距、字号完全对齐，不会互相"打架"。
   ============================================================ */
body .btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--rc-btn-gap);
  min-height: var(--rc-btn-h);
  padding: 0 var(--rc-btn-px);
  border: 1px solid var(--rc-secondary-border);
  border-radius: var(--rc-btn-radius);
  background: var(--rc-secondary-bg);
  color: var(--rc-secondary-fg);
  font-family: var(--ds-font-ui);
  font-size: var(--ds-text-body);
  font-weight: 600;
  line-height: 1;
  letter-spacing: .01em;
  text-align: center;
  white-space: nowrap;
  cursor: pointer;
  user-select: none;
  /* 覆盖 styles.css 的 .btn:hover{transform:translateY(-1px)}：
     悬停位移会与按压缩放抢同一个 transform，只保留按压反馈 */
  transform: none;
  /* 原则 14：逐项声明，不用 transition:all（all 会把 layout 属性也卷进来） */
  transition:
    transform var(--rc-dur-press) var(--rc-ease),
    background-color var(--rc-dur-state) var(--rc-ease),
    border-color var(--rc-dur-state) var(--rc-ease),
    color var(--rc-dur-state) var(--rc-ease),
    box-shadow var(--rc-dur-state) var(--rc-ease),
    opacity var(--rc-dur-state) var(--rc-ease);
}

/* 悬停：只换背景（原则 3 的结构边框保留），不做位移 */
body .btn:not(:disabled):not([aria-disabled="true"]):hover {
  background: var(--rc-secondary-bg-hover);
}

/* 按压：统一 scale(.96)，可中断（原则 4 + 12） */
body .btn:not(:disabled):not([aria-disabled="true"]):active {
  transform: scale(var(--rc-press));
}

/* 焦点：2px 品牌描边 + 2px 偏移，浅色朱砂 / 深色琥珀均可见 */
body .btn:focus-visible {
  outline: var(--rc-focus-w) solid var(--ds-brand);
  outline-offset: var(--rc-focus-offset);
  box-shadow: var(--ds-focus-ring);
}

/* 禁用态：整体 .75 不透明度 + 中性底 + 主文字色，
   实测对比度 >7:1（若沿用 styles.css 的 .48 会掉到 3:1 以下） */
body .btn:disabled,
body .btn[disabled],
body .btn[aria-disabled="true"] {
  opacity: var(--rc-disabled-opacity);
  background: var(--rc-disabled-bg);
  border-color: var(--rc-disabled-border);
  color: var(--rc-disabled-fg);
  box-shadow: none;
  cursor: not-allowed;
}

/* 加载态：app.js 已有 .is-busy 契约，这里补 aria-busy 与无图标按钮的自旋
   （ui-ux-pro-max 触控规则：任何异步动作必须有进行中反馈） */
body .btn.is-busy,
body .btn[aria-busy="true"] {
  opacity: .62;
  pointer-events: none;
  cursor: progress;
}

body .btn.is-busy .ico,
body .btn[aria-busy="true"] .ico {
  animation: rc-spin 900ms linear infinite;
}

body .btn.is-busy:not(:has(.ico))::after,
body .btn[aria-busy="true"]:not(:has(.ico))::after {
  content: "";
  width: 14px;
  height: 14px;
  flex: none;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  animation: rc-spin 800ms linear infinite;
}

/* ============================================================
   2 · 三级层级：主行动 / 次行动 / 危险
   ============================================================ */

/* 主行动：唯一强调色 + 白（深）字 + 一层极浅抬升 */
body .btn.primary {
  background: var(--ds-brand);
  border-color: var(--ds-brand);
  color: var(--on-brand);       /* 浅色白字 / 深色墨字，随主题切换 */
  box-shadow: var(--rc-elev-1);
}

body .btn.primary:not(:disabled):not([aria-disabled="true"]):hover {
  background: var(--ds-brand-hover);
  border-color: var(--ds-brand-hover);
  box-shadow: var(--rc-elev-2);
}

body .btn.primary:not(:disabled):not([aria-disabled="true"]):active {
  box-shadow: none;             /* 按下即"落回纸面" */
}

/* 次行动：显式别名，语义等价于裸 .btn，便于模板里写清楚层级 */
body .btn.secondary {
  background: var(--rc-secondary-bg);
  border-color: var(--rc-secondary-border);
  color: var(--rc-secondary-fg);
}

/* 危险：克制描边款为默认（不伪装成主按钮） */
body .btn.danger {
  background: var(--rc-danger-soft);
  border-color: var(--rc-danger-border);
  color: var(--rc-danger);
}

body .btn.danger:not(:disabled):not([aria-disabled="true"]):hover {
  background: var(--rc-danger-soft-hover);
}

/* 危险：实心款，仅用于"确认删除 / 解散 / 踢出"等终态确认按钮 */
body .btn.danger.solid,
body .btn.danger.fill {
  background: var(--rc-danger);
  border-color: var(--rc-danger);
  color: var(--rc-on-danger);
  box-shadow: var(--rc-elev-1);
}

body .btn.danger.solid:not(:disabled):not([aria-disabled="true"]):hover,
body .btn.danger.fill:not(:disabled):not([aria-disabled="true"]):hover {
  background: var(--rc-danger-hover);
  border-color: var(--rc-danger-hover);
}

/* 品牌浅底款（介于主与次之间，用于列表内的轻主行动） */
body .btn.soft {
  background: var(--ds-brand-soft);
  border-color: transparent;
  color: var(--ds-brand);
}

body .btn.soft:not(:disabled):not([aria-disabled="true"]):hover {
  background: var(--rc-brand-soft-hover);
}

/* 三级文字按钮：无底无框，靠 hover 背景而非仅变颜色 */
body .btn.ghost {
  background: transparent;
  border-color: transparent;
  color: var(--rc-ghost-fg);
}

body .btn.ghost:not(:disabled):not([aria-disabled="true"]):hover {
  background: var(--ds-surface-soft);
  color: var(--ds-ink);
}

/* ============================================================
   3 · 尺寸变体
   ============================================================ */

/* .tiny：视觉 32px；热区用伪元素纵向补到 44px。
   只纵向扩展，不横向 —— 避免 .post-actions（gap:4px）内相邻按钮热区重叠
   （原则 16：热区绝不允许相互重叠） */
body .btn.tiny {
  position: relative;
  min-height: var(--rc-btn-h-sm);
  padding: 0 var(--rc-btn-px-sm);
  font-size: var(--ds-text-caption);
  border-radius: var(--rc-btn-radius);
}

body .btn.tiny::before {
  content: "";
  position: absolute;
  inset: -6px 0;
  border-radius: inherit;
}

/* 密集处（工具栏、筛选行）专用：40px，不再是 32px */
body .btn.dense {
  min-height: var(--rc-btn-h-dense);
  padding: 0 var(--rc-btn-px-sm);
  font-size: 13px;
}

/* 通栏按钮：移动端主行动的默认形态 */
body .btn.block {
  display: flex;
  width: 100%;
}

/* ============================================================
   4 · 图标按钮：统一 40×40（视觉 20px 图标），圆形
   ============================================================ */
body .icon-btn,
body .close-btn {
  position: relative;
  display: inline-grid;
  place-items: center;
  width: var(--rc-icon-btn);
  height: var(--rc-icon-btn);
  padding: 0;
  border: 1px solid var(--rc-secondary-border);
  border-radius: 50%;
  background: var(--rc-secondary-bg);
  color: var(--ds-text-secondary);
  cursor: pointer;
  transition:
    transform var(--rc-dur-press) var(--rc-ease),
    background-color var(--rc-dur-state) var(--rc-ease),
    border-color var(--rc-dur-state) var(--rc-ease),
    color var(--rc-dur-state) var(--rc-ease),
    box-shadow var(--rc-dur-state) var(--rc-ease);
}

/* 图标线与字重匹配：20px 图标配 1.65 描边（原则 17） */
body .icon-btn .ico,
body .close-btn .ico {
  width: var(--rc-icon-size);
  height: var(--rc-icon-size);
}

/* 热区补到 44×44：视觉仍是 40 */
body .icon-btn::before,
body .close-btn::before {
  content: "";
  position: absolute;
  inset: -2px;
  border-radius: 50%;
}

/* hover 靠背景变化，不只改颜色（原则 19：动效不是唯一反馈通道） */
body .icon-btn:not(:disabled):not([aria-disabled="true"]):hover,
body .close-btn:not(:disabled):not([aria-disabled="true"]):hover {
  background: var(--rc-secondary-bg-hover);
  color: var(--ds-ink);
}

body .icon-btn:not(:disabled):not([aria-disabled="true"]):active,
body .close-btn:not(:disabled):not([aria-disabled="true"]):active {
  transform: scale(var(--rc-press));
}

body .icon-btn:focus-visible,
body .close-btn:focus-visible {
  outline: var(--rc-focus-w) solid var(--ds-brand);
  outline-offset: var(--rc-focus-offset);
  box-shadow: var(--ds-focus-ring);
}

body .icon-btn:disabled,
body .icon-btn[aria-disabled="true"],
body .close-btn:disabled {
  opacity: var(--rc-disabled-opacity);
  color: var(--ds-text-secondary);
  box-shadow: none;
  cursor: not-allowed;
}

/* 小号图标按钮：32×32 / 18px 图标，用于卡片角标等密集位 */
body .icon-btn.sm {
  width: var(--rc-icon-btn-dense);
  height: var(--rc-icon-btn-dense);
}

body .icon-btn.sm .ico {
  width: var(--rc-icon-size-dense);
  height: var(--rc-icon-size-dense);
}

/* --- 既有 .icon-act（聚会卡右上角开始/置顶）收口 ---
   保持密集位 32×32 视觉 + 18px 图标，热区由伪元素补到 40×40；
   同时把 .float-actions 间距提到 8px，正好等于热区外扩量，
   两个按钮的热区恰好相切、不重叠。 */
body .float-actions {
  gap: var(--rc-btn-gap);
}

body .float-actions .icon-act {
  position: relative;
  width: var(--rc-icon-btn-dense);
  height: var(--rc-icon-btn-dense);
  padding: 0;
  display: inline-grid;
  place-items: center;
  border-radius: 50%;
  cursor: pointer;
  transition:
    transform var(--rc-dur-press) var(--rc-ease),
    background-color var(--rc-dur-state) var(--rc-ease),
    border-color var(--rc-dur-state) var(--rc-ease),
    color var(--rc-dur-state) var(--rc-ease);
}

body .float-actions .icon-act .ico {
  width: var(--rc-icon-size-dense);
  height: var(--rc-icon-size-dense);
}

body .float-actions .icon-act::before {
  content: "";
  position: absolute;
  inset: -4px;
  border-radius: 50%;
}

body .float-actions .icon-act:not(:disabled):not(.is-busy):hover {
  background: var(--rc-secondary-bg-hover);
}

body .float-actions .icon-act:not(:disabled):not(.is-busy):active {
  transform: scale(var(--rc-press));
}

body .float-actions .icon-act:focus-visible {
  outline: var(--rc-focus-w) solid var(--ds-brand);
  outline-offset: var(--rc-focus-offset);
}

body .float-actions .icon-act:disabled,
body .float-actions .icon-act.is-busy {
  opacity: var(--rc-disabled-opacity);
  cursor: default;
}

/* 分段控件的按钮也统一下限高度 + 按压反馈（密集位 34px，热区补到 40） */
body .segmented button {
  position: relative;
  min-height: 34px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background-color var(--rc-dur-state) var(--rc-ease),
    color var(--rc-dur-state) var(--rc-ease),
    transform var(--rc-dur-press) var(--rc-ease);
}

body .segmented button::before {
  content: "";
  position: absolute;
  inset: -3px 0;
}

body .segmented button:active {
  transform: scale(var(--rc-press));
}

/* --- 轻量图文操作 .act（动态卡：赞 / 评论 / 查看聚会 / 编辑 / 管理） ---
   与 .icon-act 同一套手法：视觉尺寸与文案一律不动（不破坏社交域排版），
   只用伪元素把热区"纵向"补到 40px。
   · 为什么只纵向外扩：.act 都带文字，实测宽度 60~90px，横向已远超 40px；
     而 .post-actions 的列间距只有 4px，横向外扩必然与相邻按钮热区重叠
     （原则 16：热区绝不允许相互重叠）。
   · 为什么配套改 row-gap：.post-actions 是 flex-wrap 容器，5 个操作在
     390px 下必然换行；行间距 4px < 上下各 5px 的外扩量，换行时上下两行
     热区会重叠 6px。把 row-gap 提到 10px（= 外扩量）后恰好相切。
     单行时不换行 → row-gap 完全不参与布局，视觉零变化。 */
body .act {
  position: relative;
  cursor: pointer;
  transition: background-color var(--rc-dur-state) var(--rc-ease),
    color var(--rc-dur-state) var(--rc-ease),
    transform var(--rc-dur-press) var(--rc-ease),
    opacity var(--rc-dur-state) var(--rc-ease);
}

body .act::before {
  content: "";
  position: absolute;
  inset: -5px 0;
  border-radius: inherit;
}

/* hover / 按压都有非动效的静态反馈通道（原则 19） */
body .act:not(:disabled):hover {
  background: var(--ds-surface-soft);
}

/* 已选中（.act.on）的底色是 --wine-soft，hover 不能把它盖成中性色，
   否则"已赞"状态一悬停就丢色；改为同色系加深一档 */
body .act.on:not(:disabled):hover {
  background: var(--rc-brand-soft-hover);
}

body .act:not(:disabled):active {
  transform: scale(var(--rc-press));
}

body .act:focus-visible {
  outline: var(--rc-focus-w) solid var(--ds-brand);
  outline-offset: var(--rc-focus-offset);
}

/* 禁用态与全站一致：.55 会把 12px 文字压到 3:1 以下 */
body .act:disabled,
body .act[disabled] {
  opacity: var(--rc-disabled-opacity);
  cursor: not-allowed;
}

body .post-actions {
  row-gap: 10px;
}

@media (prefers-reduced-motion: reduce) {
  body .act {
    transition: none;
    animation: none;
  }

  body .act:not(:disabled):active {
    transform: none;
  }
}

/* ============================================================
   5 · 摆放规则
   · 主行动：通栏 / 固定在内容流底部（.action-bar），避开 mobile-nav 与 FAB
   · 次行动：随内容右对齐（.actions-inline）
   · 按钮组：间距 8px，组内等分（flex:1 1 0）
   · 表单底部：取消在左、确定在右
   ============================================================ */

/* 按钮组：8px 间距 + 等分（ui-ux-pro-max：触控元素间距 ≥8px） */
body .btn-group,
body [data-btn-group] {
  display: flex;
  align-items: center;
  gap: var(--rc-btn-gap);
}

body .btn-group > .btn,
body [data-btn-group] > .btn {
  flex: 1 1 0;
  min-width: 0;
}

/* 不等分变体：按内容宽度排布 */
body .btn-group.auto > .btn,
body [data-btn-group].auto > .btn {
  flex: 0 1 auto;
}

/* 次行动：随内容右对齐 */
body .actions-inline {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--rc-btn-gap);
  flex-wrap: wrap;
}

/* 底部主行动条：sticky 在内容流末尾，随页面滚动但不覆盖 mobile-nav
   （mobile-nav 是 workspace 的 flex 兄弟节点，不会被 sticky 遮住） */
body .action-bar {
  position: sticky;
  bottom: 0;
  z-index: 14;
  display: flex;
  align-items: center;
  gap: var(--rc-btn-gap);
  padding: 10px 0 calc(10px + env(safe-area-inset-bottom, 0px));
  background: var(--ds-bg);
}

/* 顶部渐隐遮罩，避免内容被硬切一刀 */
body .action-bar::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 100%;
  height: 16px;
  pointer-events: none;
  background: linear-gradient(transparent, var(--ds-bg));
}

body .action-bar > .btn {
  flex: 1 1 0;
  min-width: 0;
}

/* 浮动主行动：右下角，抬到 mobile-nav 之上（--rc-safe-bottom） */
body .action-bar.float {
  position: fixed;
  left: 12px;
  right: 12px;
  bottom: var(--rc-safe-bottom);
  padding: 10px 12px;
  border-radius: var(--rc-btn-radius);
  border: 1px solid var(--rc-secondary-border);
  background: var(--ds-surface);
  box-shadow: var(--ds-shadow-floating);
}

/* 手机外壳内 fixed 会溢出到框外，回落到 absolute（与 .toast 同口径） */
body .device-frame .action-bar.float {
  position: absolute;
}

/* 表单底部操作区：右对齐，DOM 顺序「取消 → 确定」即得到「取消在左、确定在右」 */
body .form-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--rc-btn-gap);
  flex-wrap: wrap;
}

/* 每个操作至少 96px 宽，避免"取消"这类两字按钮成为手指难点 */
body .form-actions > .btn {
  min-width: 96px;
}

/* 手机外壳内操作区等分，主行动居右的 DOM 顺序不变 */
html.force-mobile .form-actions > .btn,
html.force-mobile .modal-foot > .btn {
  flex: 1 1 0;
}

/* 弹窗底部操作区：统一叫 .modal-foot（建议新增）或与 .form-actions 同构 */
body .modal-foot {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--rc-btn-gap);
  margin-top: var(--ds-space-5);
  padding-top: var(--ds-space-4);
  border-top: 1px solid var(--ds-border);
}

body .modal-foot > .btn {
  min-width: 96px;
}

/* ============================================================
   6 · 输入控件：与按钮同高、同心圆角
   高度：输入框 / 下拉 = 44px；密集位（搜索框、筛选下拉）= 40px
   ============================================================ */
body .field input,
body .field select,
body .field textarea {
  min-height: var(--rc-field-h);
  padding: 12px var(--rc-field-px);
  border: 1px solid var(--ds-border);
  border-radius: var(--rc-field-radius);
  background: var(--ds-bg);
  color: var(--ds-ink);
  font-size: var(--ds-text-body);
  line-height: 1.4;
  outline: none;
  transition: border-color var(--rc-dur-state) var(--rc-ease),
    background-color var(--rc-dur-state) var(--rc-ease),
    box-shadow var(--rc-dur-state) var(--rc-ease);
}

/* 文本域不套 44px 硬高，保留多行语义 */
body .field textarea {
  min-height: 88px;
  resize: vertical;
}

body .field input:focus,
body .field select:focus,
body .field textarea:focus {
  background: var(--ds-surface);
  border-color: var(--ds-brand);
  box-shadow: var(--ds-focus-ring);
}

/* 错误态下聚焦：保留错误底色与错误描边，只叠加焦点环。
   特异性比上面高一级（0,3,2 > 0,2,2），保证错误信号不被 focus 抢掉
   （ui-ux-pro-max 表单规则：错误信息必须就近且始终可见） */
body .field input:focus:user-invalid,
body .field input:focus[aria-invalid="true"],
body .field.is-invalid input:focus,
body .field select:focus:user-invalid,
body .field select:focus[aria-invalid="true"],
body .field.is-invalid select:focus,
body .field textarea:focus:user-invalid,
body .field textarea:focus[aria-invalid="true"],
body .field.is-invalid textarea:focus {
  background: var(--rc-error-soft);
  border-color: var(--ds-error);
  box-shadow: var(--ds-focus-ring);
}

/* placeholder 统一三级文字色；opacity:1 是必需的，
   否则 Chrome/Safari 默认的半透明会把对比度再砍一半 */
body .field input::placeholder,
body .field textarea::placeholder,
body .search input::placeholder,
body .composer textarea::placeholder,
body .moment-form input::placeholder {
  color: var(--ds-text-tertiary);
  opacity: 1;
}

/* 密集位：搜索框与筛选下拉 40px，圆角与字段一致 */
body .search {
  min-height: var(--rc-field-h-dense);
  gap: var(--rc-btn-gap);
  padding: 0 12px;
  border-radius: var(--rc-field-radius);
}

body .search:focus-within {
  border-color: var(--ds-brand);
  box-shadow: var(--ds-focus-ring);
}

body .filter {
  min-height: var(--rc-field-h-dense);
  padding: 0 12px;
  border-radius: var(--rc-field-radius);
}

body .composer textarea,
body .moment-form input,
body .transfer-row select,
body .mm-transfer select {
  min-height: var(--rc-field-h-dense);
  border-radius: var(--rc-field-radius);
}

body .composer textarea {
  min-height: 88px;
}

/* ============================================================
   7 · toast：底部操作区右对齐，主按钮在右
   ============================================================ */
body .toast {
  gap: 12px;
  align-items: center;
  padding: 10px 12px 10px 16px;
  border-radius: var(--ds-radius-button);
}

/* toast 里的操作按钮：视觉 32px，热区补满 toast 高度（40px） */
body .toast [data-toast-action] {
  position: relative;
  min-height: 32px;
  padding: 0 10px;
  border-radius: var(--ds-radius-button);
  font-size: var(--ds-text-body);
  font-weight: 650;
  white-space: nowrap;
}

body .toast [data-toast-action]::before {
  content: "";
  position: absolute;
  inset: -4px 0;
  border-radius: inherit;
}

body .toast [data-toast-action]:hover {
  background: rgba(255, 255, 255, .10);
}

body .toast [data-toast-action]:active {
  transform: scale(var(--rc-press));
}

body .toast [data-toast-action]:focus-visible {
  outline: var(--rc-focus-w) solid currentColor;
  outline-offset: var(--rc-focus-offset);
}

/* ============================================================
   8 · 数字：等宽数位，避免刷新时抖动（原则 9）
   ============================================================ */
body .stat-tile .n,
body .person-counter b,
body .mini-stat .num,
body .person-summary b,
body .leader-row .score,
body .rank-num,
body [data-num] {
  font-variant-numeric: tabular-nums;
}

/* ============================================================
   9 · 动效：明确的属性清单 + 减少动态偏好
   ============================================================ */
@keyframes rc-spin {
  to { transform: rotate(360deg); }
}

@media (prefers-reduced-motion: reduce) {
  body .btn,
  body .icon-btn,
  body .close-btn,
  body .float-actions .icon-act,
  body .segmented button,
  body .toast [data-toast-action],
  body .field input,
  body .field select,
  body .field textarea,
  body .search,
  body .filter {
    transition: none;
    animation: none;
  }

  body .btn:not(:disabled):not([aria-disabled="true"]):active,
  body .icon-btn:not(:disabled):active,
  body .close-btn:not(:disabled):active,
  body .float-actions .icon-act:not(:disabled):not(.is-busy):active,
  body .segmented button:active,
  body .toast [data-toast-action]:active {
    transform: none;
  }

  body .btn.is-busy:not(:has(.ico))::after,
  body .btn[aria-busy="true"]:not(:has(.ico))::after {
    animation: none;
  }
}
