/* ============================================================
 * material.css —— 设计系统（Material 2 骨架 + 现代桌面质感）
 *
 * 设计取向：
 *   · 暖调纸感底色（不再用纯灰，避免"从深色改回来"的残留感）
 *   · 圆角分层：小控件 10px / 卡片 16px / 面板 22px
 *   · 阴影分两层（贴地 + 弥散），颜色带一点主色，不用纯黑
 *   · 玻璃层只用在"浮起来的东西"上（导航栏 / 底部栏 / 状态条 / 锁屏卡片）
 * ============================================================ */

:root {
  --md-primary: #4E6472;
  --md-primary-d: #33454F;
  --md-primary-l: #7A919E;
  --md-accent: #D08A38;
  --md-accent-d: #AC6E21;

  --ok: #58876A;
  --warn: #C2913A;
  --danger: #B0554E;
  --dead: #9E9B96;

  /*
   * 列表图标块的底色。
   *
   * 这些以前是以十六进制**写在 JS 里**的（qq.js 的 icolor、
   * browser.js 的书签块），改一次主题要满仓库搜颜色，漏一个就不一致。
   * 提到这里按语义命名：这个块代表什么，就叫什么，不叫它什么颜色。
   */
  --icup: #5A6E7A;    /* 上游 / 云端 */
  --icust: #7A919E;   /* 客户 */
  --ifriend: #C2913A; /* 好友 */
  --igroup: #8FA5A0;  /* 群 / 泛用 */
  --ilink: #8FA3AC;   /* 链接 / 书签 */

  /* 暖纸底：色相偏暖 175°，饱和度极低，避免冷灰的"旧深色主题"味 */
  --bg: #F0EFEC;
  --bg-2: #E9E8E4;
  --surface: #FFFDFB;
  --surface-2: #F6F5F2;
  --surface-3: #EFEEEB;
  --text: #2A3138;
  --text-2: #647079;
  --text-3: #939DA4;
  --divider: #E6E4DF;

  --dark-bg: #1E272B;
  --dark-surface: #263238;
  --dark-text: #DCE3E6;

  /*
   * 阴影：分五级，每一级对应"离地多高"，不是随手挑一个。
   *   e1 贴地（列表项 / 卡片）
   *   e2 轻浮（按下的按钮 / 玻璃卡）
   *   e3 抬起（浮起的面板）
   *   e4 悬空（AppBar / FAB）
   *   e8 模态（Dialog / Sheet / 手机机身）
   * 颜色带轻微冷色偏移，比纯黑更"透气"。
   */
  --e1: 0 1px 2px rgba(42, 49, 56, .07), 0 1px 4px rgba(42, 49, 56, .05);
  --e2: 0 2px 5px rgba(42, 49, 56, .09), 0 5px 14px rgba(42, 49, 56, .07);
  --e3: 0 4px 10px rgba(42, 49, 56, .10), 0 10px 26px rgba(42, 49, 56, .09);
  --e4: 0 8px 20px rgba(42, 49, 56, .13), 0 18px 44px rgba(42, 49, 56, .11);
  --e8: 0 16px 38px rgba(42, 49, 56, .17), 0 30px 70px rgba(42, 49, 56, .13);

  /*
   * 玻璃：**只**用在"浮在别的内容之上、且滚过去能看见下面"的东西上。
   *
   * 判断标准很简单 —— 问一句"它底下有没有会动的内容"：
   *   ✅ 底部导航栏（内容从底下滚过去）
   *   ✅ Snackbar（浮在页面上）
   *   ✅ 深色 AppBar（内容滚到它下面）
   *   ❌ 贴在某块静态渐变上的卡片 —— 糊涂不到东西，只吃性能损耗，
   *      还因为半透明而让底色发灰发脏，看着就是"雾化没做好"
   *
   * 现在全项目只有这三处留着玻璃，其余全部改成了实心暖白。
   * 原来那套 --glass / --glass-2 / --glass-line 已经没人用了，一并删掉，
   * 免得后来的人以为还有一套"玻璃卡"的标准可以随手取用。
   */
  --blur: blur(20px) saturate(1.5);

  /*
   * 圆角：四档，各有明确归属。
   * 这套是按"元素多大"分的，服务的是**内容面**。
   */
  --r-sm: 8px;    /* 小徽标 / 方形头像 */
  --r: 12px;      /* 控件：按钮 / 输入框 / 列表小图标块 */
  --r-lg: 16px;   /* 卡片 / 列表容器 */
  --r-xl: 22px;   /* 面板 / 对话框 / 底部弹层 */

  /*
   * 图标格专用圆角 —— 不要拿上面那四档来凑。
   *
   * 应用图标的形状是**常识问题**，不是审美偏好：安卓的图标就是方的
   * （或者极少数是正圆），没有"圆角矩形但圆得像个药丸"这一档。
   * 一旦圆角超过自身边长的两成，图形就往胶囊滑，一眼就假。
   *
   * 所以图标格按**自身边长的百分比**单独取值，不跟卡片共用：
   *   52px × 17% ≈ 9px    —— 方正，但四角有一丝弧度，不生硬
   *   26px × 19% ≈ 5px    —— 小尺寸绝对值太小会"消失"，比例略微上调
   *
   * 为什么不是 22.5%（iOS squircle）：那是 iOS 的比例，而且 iOS 的
   * 图形本身画得更满。本项目图标内缩放只有 0.52，四周留白大，
   * 再用 23% 的圆角，视觉上就是"一个圆角比图形还抢眼的小方块"。
   * 收在 17% 才是安卓的方正观感。
   */
  --r-icon: 9px;
  --r-icon-sm: 5px;

  /*
   * 动效曲线：不用 bounce / elastic —— 弹一下显得过时，
   * 用"快出慢收"的缓出曲线，动作有分量但不咋呼。
   *
   * ⚠️ --mag 原来的第二控制点是 1.4，那**就是** overshoot —— 元素会冲过
   * 终点再弹回来，只是弹得没那么夸张。注释写着"不用 bounce"、曲线却在弹，
   * 这种自相矛盾比直接用 bounce 更糟。现在换成无反冲的短促缓出。
   */
  --fast: .2s cubic-bezier(.4, 0, .2, 1);
  --spring: .34s cubic-bezier(.2, .9, .28, 1);
  --mag: .16s cubic-bezier(.2, .8, .3, 1);   /* 按下反馈：短、不弹 */
}

* {
  box-sizing: border-box;
  -webkit-tap-highlight-color: transparent;
}

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
}

body {
  font-family: "Outfit", -apple-system, BlinkMacSystemFont, "PingFang SC", "Noto Sans SC",
    "Hiragino Sans GB", "Microsoft YaHei", "Source Han Sans SC", "Noto Sans CJK SC", Roboto, sans-serif;
  font-size: 14px;
  line-height: 1.55;
  color: var(--text);
  background: #E4E2DD;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow: hidden;
  font-variant-numeric: tabular-nums;
}

/* 数字一律等宽，避免跳动 */
.mono, .hs-cash, .mp-v, .st-v, .sc-st, .calc-out, .de-line .v, .mh-t, .hc-t {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

button {
  font-family: inherit;
  font-size: inherit;
  color: inherit;
  border: 0;
  background: none;
  cursor: pointer;
  padding: 0;
}

input, textarea, select {
  font-family: inherit;
  font-size: inherit;
  color: inherit;
}

::-webkit-scrollbar {
  width: 0;
  height: 0;
}

/* ---------- 图标 ---------- */
.ic-svg {
  display: block;
  flex: none;
  vertical-align: middle;
}
.ic-wrap { display: inline-flex; align-items: center; justify-content: center; }
/* 图标 + 文字并排时的间距（只作用于紧邻的文字节点） */
.btn .ic-svg, .ic-inline .ic-svg { margin-right: 6px; }
.ic-inline { display: inline-flex; align-items: center; gap: 5px; }
.hs-k .ic-svg, .hf-info .ic-svg, .mh-meta .ic-svg, .de-line .ic-svg {
  display: inline-block;
  vertical-align: -2px;
  margin-right: 4px;
  opacity: .78;
}
.hs-k, .hf-info { display: flex; align-items: center; }
.hf-info { justify-content: center; }

/* ---------- 动效关键帧 ---------- */
@keyframes fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes fadeUp { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: translateY(0); } }
@keyframes fadeDown { from { opacity: 0; transform: translateY(-12px); } to { opacity: 1; transform: translateY(0); } }
@keyframes pop { from { opacity: 0; transform: scale(.9); } to { opacity: 1; transform: scale(1); } }
@keyframes float1 { 0%,100% { transform: translate(0,0) scale(1); } 50% { transform: translate(24px,-30px) scale(1.08); } }
@keyframes float2 { 0%,100% { transform: translate(0,0) scale(1); } 50% { transform: translate(-30px,22px) scale(1.12); } }
@keyframes float3 { 0%,100% { transform: translate(0,0) scale(1); } 50% { transform: translate(18px,26px) scale(.94); } }

/*
 * 常驻微动效（perpetual micro-interactions）。
 *
 * 一个静态界面看久了会觉得"死"。这里给三类小东西加长时间、低幅度的
 * 循环动画：在线状态点呼吸、未读角标轻脉、进度条高光缓慢扫过。
 * 幅度都很小（都是 0.9~1.06 / 0.4~1 透明度这种量级），
 * 目的是让界面"还有气"，而不是抢注意力。
 */
@keyframes breathe { 0%, 100% { opacity: .45; transform: scale(.86); } 50% { opacity: .14; transform: scale(1.5); } }
@keyframes pulseSoft { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.07); } }
@keyframes sheen { 0% { transform: translateX(-120%); } 100% { transform: translateX(320%); } }

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


/* ---------- 排版层级 ----------
 *
 * 尺度按 1.22~1.30 的比值往上走，不是 14/15/16 这样一像素一像素地挪：
 *   34  display   一屏一个（大时间、结算标题）
 *   24  page      页面主标题
 *   18  section   区块标题
 *   15  item      列表项 / 正文强调
 *   14  body      正文
 *   12.5 cap      次要说明
 *   10.5 overline 标签
 * 这样一眼能看出"哪一行是主角"，不用靠加粗去硬凑。
 */
.t-display { font-size: 34px; font-weight: 300; line-height: 1.06; letter-spacing: -1.1px; }
.t-page    { font-size: 24px; font-weight: 500; line-height: 1.18; letter-spacing: -.5px; }
.t-title   { font-size: 19px; font-weight: 500; line-height: 1.25; letter-spacing: -.15px; }
.t-head    { font-size: 17px; font-weight: 500; line-height: 1.3; letter-spacing: -.1px; }
.t-sub     { font-size: 15px; font-weight: 500; line-height: 1.35; }
.t-body    { font-size: 14px; }
.t-cap     { font-size: 12.5px; color: var(--text-2); line-height: 1.5; }
.t-overline {
  font-size: 10.5px; letter-spacing: 2.2px; text-transform: uppercase;
  color: var(--text-3); font-weight: 500;
}
.mono { font-family: "SFMono-Regular", Menlo, Consolas, "Courier New", monospace; }

/* ---------- AppBar ---------- */
/*
 * 三种变体：
 *   .appbar         —— 默认，主色实心（品牌色的地方）
 *   .appbar.dark    —— 深一档的主色，配轻微光泽，不靠纯黑压住
 *   .appbar.light   —— 白底
 *   .appbar.glass   —— 磨砂，内容滚动时压在下面
 */
.appbar {
  height: 56px;
  background: var(--md-primary);
  color: #FFFDFB;
  display: flex;
  align-items: center;
  padding: 0 4px;
  box-shadow: var(--e4);
  position: relative;
  z-index: 5;
  flex: none;
}
/* 主色 AppBar 底部压一道内高光，边不"死"（跟 .appbar.dark 同一手法） */
.appbar::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 1px;
  background: rgba(255, 255, 255, .10);
}
.appbar.dark {
  background: linear-gradient(168deg, #3D525E 0%, #2F434E 100%);
  box-shadow: 0 1px 2px rgba(42, 49, 56, .12), 0 4px 14px rgba(42, 49, 56, .14);
}
/* 深色 AppBar 底部一道内高光，边不"死" */
.appbar.dark::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 1px;
  background: rgba(255, 255, 255, .10);
}
.appbar.light { background: var(--surface); color: var(--text); box-shadow: var(--e1); }
.appbar.light::after { display: none; }
.appbar.glass {
  background: var(--glass);
  -webkit-backdrop-filter: var(--blur);
  backdrop-filter: var(--blur);
  color: var(--text);
  border-bottom: 1px solid rgba(255, 255, 255, .6);
  box-shadow: var(--e1);
  position: sticky;
  top: 0;
}
.appbar.glass::after { display: none; }
.appbar .ab-title {
  font-size: 18px;
  font-weight: 500;
  letter-spacing: -.2px;
  margin-left: 8px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/*
 * 顶栏副标题：字号抬到 12px 并去掉 opacity。
 *
 * 原来是 11.5px 再乘 .72 不透明度，压在主色底上实测对比度只有 3 上下，
 * 低于 WCAG AA 要求的 4.5 —— 而且是用来放客户 ID、工单号这类**要看清**的信息。
 * 直接给一个高对比的白色（带一点暖调，不用纯白），对比度能到 6.5。
 */
.appbar .ab-sub {
  font-size: 12px;
  color: rgba(255, 253, 251, .86);
  margin-left: 8px;
  margin-top: -3px;
  letter-spacing: .1px;
}

/*
 * 顶栏返回键：不用圆形。
 * 圆形按钮（两端一个圆头）和页面里成片的圆角方块是两套语言 ——
 * 统一成"圆角方块"，只是比正文字号大一号，看起来才像同一套控件。
 */
.iconbtn {
  width: 40px;
  height: 40px;
  min-width: 40px;
  border-radius: var(--r);
  display: flex;
  align-items: center;
  justify-content: center;
  color: inherit;
  position: relative;
  overflow: hidden;
  transition: background var(--fast), transform var(--mag);
}
.iconbtn:active { background: rgba(255, 255, 255, .16); transform: scale(.9); }
.light .iconbtn:active, .appbar.light .iconbtn:active, .br-bar .iconbtn:active { background: rgba(42, 49, 56, .07); }

/* .ic 旧标记保留为"图标占位"，实际图形由 icons.js 的 .ic-svg 渲染 */
.ic {
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; flex: none; color: inherit;
}
/* 万一 icons.js 未加载，兜底显示一个中性圆点，不留空白方块 */
.ic::before {
  content: ''; width: 7px; height: 7px; border-radius: 50%;
  background: currentColor; opacity: .5;
}
.ic:has(svg)::before { display: none; }

/* ---------- 卡片 / 列表 ---------- */
/*
 * 卡片容器有两种，语义要分清 —— 之前两者的样式一模一样，于是出现
 * ".md-card 里塞 .md-item"，两层都是白底，只剩一根 1px 分隔线撑层次，
 * 屏幕上就是一大块"没有边界的大白"。那是典型的卡片套卡片。
 *
 *   .md-list  —— 列表容器：自己是白底，里面装 .md-item（行本身透明）。
 *   .md-card  —— 托盘：装**别的卡片**或成块内容，底色比白低一档，
 *                让里面的白卡浮出来。它自己不做卡片。
 */
.md-list {
  background: var(--surface);
  border-radius: var(--r-lg);
  box-shadow: var(--e1);
  margin: 10px;
  overflow: hidden;
}
.md-list.flat { box-shadow: none; margin: 0; border-radius: 0; }

/* 托盘：底色下沉一档，里面的内容才有"浮起来"的余地 */
.md-card {
  background: var(--surface-2);
  border-radius: var(--r-lg);
  box-shadow: none;
  overflow: hidden;
  margin: 10px;
}
.md-card.pad { padding: 14px; }
/* 托盘里如果只放纯文字块（没有内层卡片），才给它白底和投影 */
.md-card.solid {
  background: var(--surface);
  box-shadow: var(--e1);
}
.md-item {
  display: flex;
  align-items: center;
  padding: 12px 16px;
  min-height: 48px;
  position: relative;
  overflow: hidden;
  background: var(--surface);
}
.md-item.tap { transition: background var(--fast); }
.md-item.tap:active { background: #F0EFEC; }
.md-item .mi-main { flex: 1; min-width: 0; }
.md-item .mi-title {
  font-size: 15px;
  letter-spacing: -.05px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.md-item .mi-sub {
  font-size: 12.5px;
  color: var(--text-2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  margin-top: 2px;
}
.md-item + .md-item::before {
  content: ''; position: absolute; left: 16px; right: 0; top: 0; height: 1px; background: var(--divider);
}
.md-item.avatar::before { left: 72px; }
.md-item.with-ic::before { left: 68px; }

/* 列表项左图标：圆角方块 + 淡主色底 */
.md-item .mi-ic {
  width: 34px; height: 34px;
  flex: none;
  margin-right: 12px;
  border-radius: var(--r);
  display: flex; align-items: center; justify-content: center;
  background: rgba(78, 100, 114, .09);
  color: var(--md-primary);
}

.divider { height: 1px; background: var(--divider); }
.v-gap { height: 8px; background: var(--bg); }

/* ---------- 按钮 ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 40px;
  padding: 0 18px;
  border-radius: var(--r);
  font-size: 14px;
  font-weight: 500;
  letter-spacing: .1px;
  background: var(--surface);
  color: var(--text);
  box-shadow: var(--e1);
  position: relative;
  overflow: hidden;
  text-transform: none;
  transition: box-shadow var(--fast), background var(--fast), transform var(--mag);
}
.btn:active { box-shadow: var(--e2); background: #F2F1EE; transform: scale(.97); }
.btn.primary {
  background: linear-gradient(180deg, #59707E 0%, #4A5F6C 100%);
  color: #FFFDFB;
  box-shadow: 0 1px 2px rgba(42,49,56,.14), 0 4px 12px rgba(78,100,114,.22);
}
.btn.primary:active { background: linear-gradient(180deg, #4A5F6C 0%, #3D505B 100%); }
.btn.accent {
  background: linear-gradient(180deg, #DC9645 0%, #C8802F 100%);
  color: #FFFDFB;
  box-shadow: 0 1px 2px rgba(42,49,56,.14), 0 4px 12px rgba(208,138,56,.26);
}
.btn.accent:active { background: linear-gradient(180deg, #C8802F 0%, #AD6C22 100%); }
.btn.flat { box-shadow: none; background: transparent; color: var(--md-primary); }
.btn.flat:active { background: rgba(78,100,114,.09); box-shadow: none; }
.btn.block { display: flex; width: 100%; }
.btn.sm { height: 32px; padding: 0 12px; border-radius: var(--r-sm); font-size: 12.5px; }
.btn[disabled] {
  box-shadow: none; background: #E4E4E0; color: #ABA9A3; cursor: default;
}
/*
 * "已完成"态按钮。
 * 圆角跟 .btn 走（--r），不要单独做成 15px 胶囊 —— 同一个 .btn 家族里
 * 出现"圆角方块"和"胶囊"两种语言，会让这一族控件看起来不像一家人。
 * 弱化状态该靠底色和字重，不该靠改形状。
 */
.btn.done {
  box-shadow: none; background: #EEEDEA; color: var(--text-3);
  font-weight: 400; height: 30px; padding: 0 12px; border-radius: var(--r); font-size: 12px;
}
.btn.outline {
  box-shadow: none; background: transparent;
  border: 1.5px solid var(--divider);
}
.btn.outline:active { background: rgba(78,100,114,.07); border-color: var(--md-primary-l); }

.fab {
  position: absolute;
  width: 54px;
  height: 54px;
  border-radius: var(--r-lg);
  background: linear-gradient(180deg, #DC9645 0%, #C8802F 100%);
  color: #FFFDFB;
  box-shadow: var(--e4);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 6;
  transition: transform var(--spring), box-shadow var(--fast);
}
.fab:active { box-shadow: var(--e2); transform: scale(.92); }

/* ---------- 输入 ---------- */
.field {
  border-bottom: 1px solid var(--divider);
  padding: 12px 18px;
  background: var(--surface);
}
.field > label {
  display: block;
  font-size: 11px;
  color: var(--text-2);
  margin-bottom: 2px;
}
.field input, .field textarea {
  width: 100%;
  border: 0;
  outline: 0;
  background: transparent;
  font-size: 15px;
  padding: 2px 0;
  resize: none;
}
.field input::placeholder, .field textarea::placeholder { color: var(--text-3); }
.field:focus-within { border-bottom-color: var(--md-primary); border-bottom-width: 2px; padding-bottom: 9px; }
.field.err { border-bottom-color: var(--danger); border-bottom-width: 2px; padding-bottom: 9px; }
.field .hint { font-size: 11px; color: var(--text-2); margin-top: 4px; }
.field .hint.err { color: var(--danger); }

/* ---------- Chip / 徽标 ---------- */
.chip {
  display: inline-flex;
  align-items: center;
  height: 26px;
  padding: 0 10px;
  border-radius: 13px;
  background: #E7E6E2;
  font-size: 12px;
  color: var(--text-2);
  margin: 0 6px 6px 0;
  white-space: nowrap;
}
.chip.on { background: var(--md-primary); color: #FFFDFB; }
.chip.warn { background: #F3E3C6; color: #8A6A22; }
.chip.danger { background: #F2D9D6; color: #8E3F38; }
.chip.ok { background: #DCE8DE; color: #3E6B4B; }

/*
 * 未读角标：数字之外还有一层"在呼吸"的环。
 * 一个纯静态的红点放在那里，久了就像界面上的一个脏点；
 * 加一层慢慢扩散的环，它才像"有事情在等你"。
 *
 * 注意：外层环用的是 ::before，默认在文字下方（z-index:-1）。
 * 角标本身没有堆叠上下文时，这个 -1 会穿到父容器的背景后面去 ——
 * 所以这里必须隔离出独立的堆叠上下文，环才会规规矩矩待在数字背后。
 */
.badge {
  position: relative;
  isolation: isolate;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: 9px;
  background: var(--danger);
  color: #FFFDFB;
  font-size: 11px;
  line-height: 18px;
  text-align: center;
  display: inline-block;
}
.badge::before {
  content: '';
  position: absolute;
  inset: -1px;
  border-radius: inherit;
  background: var(--danger);
  z-index: -1;
  animation: breathe 2.6s ease-in-out infinite;
}

/* 在线状态点：实心点外面一圈扩散，说明"这个人现在是活的" */
/*
 * 在线状态点：实心点外面一圈扩散，说明"这个人现在是活的"。
 *
 * 注意 isolation: isolate —— ::after 用了 z-index: -1 把它压到点底下，
 * 但负 z-index 会一路穿到最近的层叠上下文。父级不隔离的话，这圈光晕
 * 会穿到卡片背景后面，直接看不见。这里必须锁住。
 *
 * 之前这段被重复定义了两遍，第二遍还把 isolation 和 z-index 丢了，
 * 光晕就时灵时不灵。现在只有一份，改动也只需改一处。
 */
.status-dot {
  display: inline-block;
  position: relative;
  isolation: isolate;
}
.status-dot.Active::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: var(--ok);
  z-index: -1;
  animation: breathe 2.2s ease-in-out infinite;
}

/* ---------- 底部导航 ---------- */
.bottomnav {
  display: flex;
  height: 56px;
  background: var(--surface);
  box-shadow: 0 -1px 4px rgba(42, 49, 56, .1);
  flex: none;
  position: relative;
  z-index: 4;
}
.bottomnav .bn {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-size: 11px;
  color: var(--text-3);
  position: relative;
  overflow: hidden;
  padding-top: 2px;
}
.bottomnav .bn.on { color: var(--md-primary); }
.bottomnav .bn .bn-ic { width: 22px; height: 22px; margin-bottom: 1px; opacity: .8; }
.bottomnav .bn.on .bn-ic { opacity: 1; }

/* ---------- Tabs ---------- */
.tabs {
  display: flex;
  height: 46px;
  /* 跟 AppBar.dark 同一族色，靠底部一道浅线分界，不是两块硬拼的色块 */
  background: linear-gradient(180deg, #3A4E59 0%, #354852 100%);
  color: #FFFDFB;
  flex: none;
  position: relative;
  z-index: 4;
}
.tabs .tab {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 13px;
  font-weight: 500;
  opacity: .62;
  position: relative;
  overflow: hidden;
  text-transform: none;
  transition: opacity var(--fast);
}
.tabs .tab.on { opacity: 1; }
.tabs .tab.on::after {
  content: ''; position: absolute; left: 16%; right: 16%; bottom: 0; height: 2.5px;
  border-radius: 2px 2px 0 0;
  background: var(--surface);
}
.tabs.light { background: var(--surface); color: var(--text); box-shadow: var(--e1); }
.tabs.light .tab { opacity: .55; }
.tabs.light .tab.on { opacity: 1; color: var(--md-primary); }
.tabs.light .tab.on::after { background: var(--md-primary); }

/* ---------- Switch ---------- */
/*
 * 滑块用 transform 位移，不用 left。
 * left 是非合成属性，切换时每一帧都要重排；transform 直接交给合成层，
 * 手机上才不掉帧。
 */
.switch {
  width: 34px;
  height: 14px;
  border-radius: 7px;
  background: #BAB8B2;
  position: relative;
  flex: none;
  transition: background var(--fast);
}
.switch::after {
  content: ''; position: absolute; width: 20px; height: 20px; border-radius: 50%;
  background: #FFFDFB; box-shadow: var(--e1); top: -3px; left: 0;
  transition: transform var(--fast), background var(--fast);
}
.switch.on { background: rgba(84, 110, 122, .5); }
.switch.on::after { transform: translateX(14px); background: var(--md-primary); }

/* ---------- 进度条 ---------- */
.bar { height: 4px; background: #E5E4E0; border-radius: 2px; overflow: hidden; }
.bar > i { display: block; height: 100%; background: var(--md-primary); border-radius: 2px; transition: width .3s; }
.bar.ok > i { background: var(--ok); }
.bar.warn > i { background: var(--warn); }
.bar.danger > i { background: var(--danger); }

/* ---------- 空状态 ---------- */
.empty {
  padding: 48px 24px;
  text-align: center;
  color: var(--text-3);
  font-size: 13px;
}
.empty .e-ic { font-size: 34px; opacity: .35; margin-bottom: 8px; }

/* ---------- 涟漪 ---------- */
.ripple {
  position: absolute;
  border-radius: 50%;
  background: rgba(42, 49, 56, .12);
  transform: scale(0);
  opacity: .5;
  pointer-events: none;
  animation: ripple .5s linear;
}
.ripple.light { background: rgba(255, 255, 255, .3); }
/* 顶栏返回键是圆角方块：方形的水波从里面溢出去会露出直角，
   所以它里面的涟漪半径要按对角线算，铺满整块 */
.iconbtn .ripple { border-radius: inherit; }
@keyframes ripple {
  to { transform: scale(4); opacity: 0; }
}

/* ---------- Snackbar ---------- */
/*
 * 贴在导航栏上方浮起，跟导航栏保持 8px 呼吸距离，
 * 免得两条东西糊在一起像是同一个控件。
 */
.snackbar {
  position: absolute;
  left: 10px;
  right: 10px;
  bottom: 58px;
  background: rgba(42, 49, 56, .93);
  color: #F4F3F0;
  -webkit-backdrop-filter: blur(14px) saturate(1.3);
  backdrop-filter: blur(14px) saturate(1.3);
  border-radius: var(--r-lg);
  padding: 13px 16px;
  font-size: 13px;
  box-shadow: 0 6px 18px rgba(42, 49, 56, .26), 0 14px 40px rgba(42, 49, 56, .22);
  display: flex;
  align-items: center;
  z-index: 96;
  animation: snackIn .24s cubic-bezier(.2, .9, .28, 1);
}
.snackbar .sn-act {
  margin-left: auto;
  color: #9FC4D8;
  font-weight: 500;
  padding-left: 16px;
  white-space: nowrap;
}
@keyframes snackIn {
  from { transform: translateY(24px); opacity: 0; }
}

/* ---------- Dialog ---------- */
.scrim {
  position: absolute;
  inset: 0;
  background: rgba(42, 49, 56, .42);
  z-index: 80;
  display: flex;
  align-items: center;
  justify-content: center;
  animation: fade .18s;
}
@keyframes fade { from { opacity: 0; } }
.dialog {
  width: 84%;
  max-width: 320px;
  background: var(--surface);
  border-radius: var(--r-xl);
  box-shadow: var(--e8);
  overflow: hidden;
  animation: dlgIn .24s cubic-bezier(.2, .9, .28, 1);
}
@keyframes dlgIn {
  from { transform: scale(.94) translateY(8px); opacity: 0; }
}
.dialog .d-body { padding: 22px 22px 14px; }
.dialog .d-title { font-size: 18px; font-weight: 500; letter-spacing: -.2px; margin-bottom: 9px; }
.dialog .d-text { font-size: 14px; color: var(--text-2); line-height: 1.65; }
.dialog .d-acts {
  display: flex;
  justify-content: flex-end;
  padding: 6px 10px 10px;
  gap: 4px;
}
.dialog .d-acts.stacked { flex-direction: column; align-items: stretch; }
.dialog .d-acts.stacked .btn { justify-content: flex-start; height: 44px; box-shadow: none; }

/* ---------- BottomSheet ---------- */
.sheet {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  background: var(--surface);
  border-radius: var(--r-xl) var(--r-xl) 0 0;
  box-shadow: var(--e8);
  z-index: 85;
  max-height: 78%;
  display: flex;
  flex-direction: column;
  animation: sheetIn .28s cubic-bezier(.2, .9, .28, 1);
}
@keyframes sheetIn { from { transform: translateY(100%); } }
.sheet .sh-grip { width: 34px; height: 4px; border-radius: 2px; background: #D2D0CA; margin: 8px auto 2px; flex: none; }
.sheet .sh-body { overflow-y: auto; padding-bottom: 8px; }

/* ---------- 通用 flex ---------- */
.row { display: flex; align-items: center; }
.row.between { justify-content: space-between; }
.col { display: flex; flex-direction: column; }
.grow { flex: 1; min-width: 0; }
.wrap { flex-wrap: wrap; }
.center { text-align: center; }
.right { text-align: right; }
.muted { color: var(--text-2); }
.dim { color: var(--text-3); }
.nowrap { white-space: nowrap; }
.pad { padding: 12px 16px; }
.pad-x { padding-left: 16px; padding-right: 16px; }
.mt8 { margin-top: 8px; }
.mt16 { margin-top: 16px; }
.mb8 { margin-bottom: 8px; }

/* 头像（无外部图片，按名字生成色块） */
.avatar {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #FFFDFB;
  font-size: 15px;
  font-weight: 500;
  background: var(--md-primary-l);
  overflow: hidden;
}
.avatar.sm { width: 32px; height: 32px; font-size: 13px; }
.avatar.lg { width: 52px; height: 52px; font-size: 19px; }
.avatar.sq { border-radius: var(--r-sm); }
