/* 三角洲护航 H5 样式
   主色 / 渐变第二色不再写死在样式里：默认 #FF7043 / #E83A30 只是兜底值，
   运行时由 /api/site/config 的 theme.main_color / theme.button_color / theme.gradient 覆盖
   （后台：常规管理 → 系统配置 → 界面设置，ui_color_enable=1 时生效）
   tabbar 配色按原 uniapp pages.json：未选中 #8b8b8b / 选中 #000000 / 背景 #fff / 14px */

:root {
    /* 悬浮胶囊离屏幕下沿那一截，一把尺量到底：按他定的只留 3px，安全区不再额外撑。
       iPhone 上 env(safe-area-inset-bottom) 有 34px，上一版全额加是 44px、吃三成是 18px，
       他要的是"贴着底"，所以这里干脆不吃 env —— Home 条就落在胶囊下方那 3px 里。
       下面凡是给这颗胶囊让地方的（页面下边距、空态卡高度、两颗悬浮球）全认这一个变量，
       要再调高低只改这一处，别去一家一家数 */
    --tab-lift: 3px;
    --main-color: #FF7043;
    --button-color: #E83A30;
    /* 45° 双色斜向渐变，配色和参考站(Tuniao 的 orangered 色板)一致：#FF7043 → #E83A30
       后台把「启用渐变色」关掉时，app.js 会把它换成「主色 → 主色」，等价于纯色 */
    --main-gradient: linear-gradient(45deg, var(--main-color), var(--button-color));
    --main-a35: rgba(255, 112, 67, .35);
    --main-a0: rgba(255, 112, 67, 0);
    --main-a40: rgba(255, 112, 67, .4);
    /* 页面底色对齐旧站：旧站实测灰底是 #f8f7f8（tn-bg-gray--light），不是之前那个偏蓝的 #f5f6f8 */
    --page-bg: #f8f7f8;
    --text-main: #1a1a1a;
    /* 中间档：「我的」页宫格和订单格下面那行字用的灰，原来两处各写一遍 #555，
       现在收进变量。浅底三档：main(标题/正文) → mid(格子文字) → sub(次要) */
    --text-mid: #555;
    --text-sub: #8b8b8b;
    /* 打手主页那一屏的暗场三件套。留空语义在服务端：后台「打手主页底色」没填时 theme.css 不写这几行，
       用的就是下面这套内置暗色；填了则整组被覆盖，其中 --ds-ink 由后端按亮度翻成深色，
       --ds-ink-rgb 是它的分量值，下面所有 rgba() 都从这两个变量拼出来，所以整屏不会出现白字白底 */
    --ds-page: #0f0d16;
    --ds-ink: #ffffff;
    --ds-ink-rgb: 255, 255, 255;
}

* {
    box-sizing: border-box;
}

/* 图片加载失败后被换成本地占位图：让它完整显示，不要被 cover 裁掉 */
img.img-failed {
    object-fit: contain;
    background: #f2f3f5;
}

html, body {
    margin: 0;
    padding: 0;
    background: var(--page-bg);
    color: var(--text-main);
    font-size: 14px;
    font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Helvetica Neue", Arial, sans-serif;
    -webkit-font-smoothing: antialiased;
}

/* 「我的」页图标：参考站用的不是 at.alicdn 那套 dkicon，而是图鸟自己的 tuniaoFont
   （iconfont 项目 3063751，原站内嵌 base64）。字体已解出落地到本地，不走外链，
   两套字体字形集不同、码点不能混用，下面 \e7b5 这些码点对应的是 tuniaoFont。 */
@font-face {
    font-family: tuniaoFont;
    src: url("../fonts/tuniaoFont.woff2") format("woff2");
    font-weight: normal;
    font-style: normal;
    font-display: block;
}

.dk-icon {
    font-family: tuniaoFont;
    font-style: normal;
    font-weight: normal;
    line-height: 1;
    display: inline-block;
    -webkit-font-smoothing: antialiased;
    -webkit-text-stroke-width: .2px;
    text-stroke-width: .2px;
}

.dk-edit::before     { content: "\e7b5"; }
.dk-right::before    { content: "\e7de"; }
.dk-set::before      { content: "\e7d1"; }
.dk-service::before  { content: "\e821"; }
/* 「用户协议」和后台单页入口用的文档图标。
   原来用 E939（三条实心竖条），在一排线框图标里明显更重，换成线框版 E7B4。
   注意：后台「单页管理」的默认图标、Site 接口的兜底值都写的是 dk-book 这个类名，
   所以只改这里一处，三个地方自动跟着变。 */
.dk-book::before     { content: "\e7b4"; }
.dk-ticket::before   { content: "\e7f8"; }
.dk-receipt::before  { content: "\e7f6"; }
.dk-identity::before { content: "\e7cb"; }
.dk-safe::before     { content: "\e804"; }
.dk-close::before    { content: "\e762"; }
/* 收藏用空心心形（和上面那批一样都是线框风格，实心心会显得比别的行重）；
   足迹用时钟，表示「最近看过」 */
.dk-heart::before    { content: "\e781"; }
.dk-clock::before    { content: "\e7dc"; }
/* 优惠券用线框票券（e7c9）：同一批入口都是线框，实心的那个会显得重 */
.dk-coupon::before   { content: "\e7c9"; }
/* 下面两个是给后台「单页管理」挑图标用的（下拉里能选到）：
   剪贴板清单 = 流程/步骤，闭合挂锁 = 隐私。
   原本三条单页都用默认的 dk-book，图标一模一样分不出来。 */
.dk-list::before     { content: "\e7f1"; }
.dk-lock::before     { content: "\e7db"; }
/* 断网提示用的「wifi 打斜杠」。不用 Vant 那套 warning-o/info-o——它们自带一圈外框，
   搁在同样画了圆底板的错误卡上就是圈里套圈；这里是裸的一个字形，跟「我的」页那排同一种形态 */
.dk-net::before      { content: "\e81c"; }

/* 底部留白：tabbar / action-bar 都是 fixed 且自带 placeholder（会插一个等高的占位块），
   所以这里不能再按栏高去留，否则底部会空出 50+60 一大截。
   只留 12px 呼吸位，占位块负责把内容顶起来。 */
/* Vue 挂载前把整块内容藏住：底部那串 script 是阻塞式的，浏览器会先把 {{copyText}} 这类
   原文画一遍才轮到 Vue 接管，猛刷新时闪的就是它。force-show 是 index.html 里那个 4 秒兜底
   用的——JS 真没下来时宁可露出原文，也不要一直白屏 */
[v-cloak]:not(.force-show) {
    display: none !important;
}

.page {
    padding-bottom: 12px;
    min-height: 100vh;
}

/* 底栏不再占位（index.html 撤了 placeholder），这一段由页面自己让。
   分类页在下面 is-full 那条归零，它自己按 100dvh 算过 */
.page.has-tabbar {
    /* 62 → 86 → 76+尺子：胶囊本身 60 高，下面还要压一层 24px 的软影，留 16 余量。
       离屏幕下沿那一截全交给 --tab-lift，这里只算胶囊自己那 60 + 余量。
       别再往 76 里塞离底距离（上一版写成 84+尺子就是算了两次，桌面凭空多 6px） */
    padding-bottom: calc(76px + var(--tab-lift));
}

/* 「我的」页同时带着 is-white，那条是给登录屏写的、把下边距归零了，
   排在上面这条后面会把它盖掉，所以这里用多一个类名把它顶回来 */
.page.has-tabbar.is-white {
    padding-bottom: calc(76px + var(--tab-lift));
}

/* 分类页自己按 100dvh 减掉标题栏和 tabbar 高度算过高度了：
   底部留白要归零，min-height 也要取消（100vh 会把页面再撑高一屏，多出一段滚动） */
.page.is-full {
    padding-bottom: 0;
    min-height: auto;
    overflow: hidden;
}

/* 顶栏是 van-nav-bar fixed + placeholder，Vant 自己会插一个等高占位块，
   这里再加 padding-top 就等于顶出两份 46px（旧站内容紧贴标题栏，间距 0） */

/* Vant 给顶栏的层级只有 1，而「我的」页那张头像卡是 z-index 2、用负 margin 压在顶部背景上的，
   往上一滚头像就盖到标题栏脸上。把顶栏提到内容之上，卡片从它底下走过去 */
.van-nav-bar {
    z-index: 10;
    /* 白底整块从元素身上挪到下面那条 ::before。为什么要挪：带 backdrop-filter 的盒子
       会替它底下所有东西立一道 backdrop root，返回胶囊正坐在它里面 —— 顶栏自己糊，
       胶囊再怎么糊也只能糊到顶栏那层纱，糊不着底下滚过来的内容（1.0.88 那批就是这样，
       所以那颗当时只补边没挂 blur，白付 GPU 也没肉眼差别）。
       挪到 ::before 以后这一层是顶栏的负层（压在内容之上、文字之下），不是胶囊的祖先，
       胶囊那一层才真的吃到底下滚过来的东西 */
    --van-nav-bar-background: transparent;
    /* Vant 有一条 .van-nav-bar .van-icon{color:var(--van-nav-bar-icon-color)}，
       那个变量默认是 --van-primary-color（#1989fa 蓝，全站没覆盖过），优先级压过从 .nav-back
       继承下来的字色，所以返回箭头会单独蓝着。改这个变量而不是给图标补 color，
       深色页下面 body.nav-dark .van-nav-bar 那条已经把它翻成 --ds-ink，不用再动 */
    --van-nav-bar-icon-color: var(--main-color);
    /* 顶栏自带一条底部发丝线，透明时它会横切在顶部渐变上，跟着一起淡掉 */
    --van-border-color: rgba(235, 237, 240, var(--nav-alpha, 1));
}

/* 顶栏那层纱。浓度仍然跟着 app.js 写在 body 上的 --nav-alpha 走：
   普通页面恒为 1 就是原来的白底，「我的」/「资金明细」贴顶时为 0，顶栏透明露出下面那张顶部图。
   模糊深度也乘同一个系数，blur(0) 等于没有这层 —— 贴顶时那屏的卖点就是顶部那张图，
   糊了等于把重做过的东西抹平。一边渐显底色、一边渐显磨砂，两件事同一个进度，
   读起来才是"玻璃浮上来" */
.van-nav-bar::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    background: rgba(252, 253, 255, calc(.72 * var(--nav-alpha, 1)));
    -webkit-backdrop-filter: blur(calc(16px * var(--nav-alpha, 1))) saturate(150%) brightness(1.06);
    backdrop-filter: blur(calc(16px * var(--nav-alpha, 1))) saturate(150%) brightness(1.06);
}

/* 返回胶囊。底板选半透明白而不是主色的淡色块：白底顶栏上像个浮起的小按钮，
   资金明细那屏顶栏透明、背后是 --main-a35 淡紫氛围带，白底板照样立得住，
   深底那一屏由下面 body.nav-dark 整组翻掉。字色跟 --main-color，后台换配色它跟着换。
   ★ 这一颗现在才真的挂上 backdrop-filter：顶栏的纱挪到 ::before 之后它不再是 backdrop root，
     底下滚过来的商品图能一路走到胶囊底下被它糊到。纱跟着薄一档（.8→.74），
     不然糊是糊了、肉眼只看出一块更白的圆角。saturate 给 165% —— 这颗只有 28px 高，
     吃得下底栏嫌太浓的那一档：小面积才提得出通透，摊成整条横栏才会糊出脏痕 */
.nav-back {
    display: inline-flex;
    align-items: center;
    gap: 2px;
    height: 28px;
    padding: 0 11px 0 8px;
    border-radius: 999px;
    background: linear-gradient(180deg,
        rgba(255, 255, 255, .74) 0px,
        rgba(255, 255, 255, .58) 28px);
    -webkit-backdrop-filter: blur(12px) saturate(165%) brightness(1.05);
    backdrop-filter: blur(12px) saturate(165%) brightness(1.05);
    border: 1px solid rgba(255, 255, 255, .5);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .9),
                0 0 0 .5px rgba(31, 27, 46, .07),
                0 3px 9px rgba(31, 27, 46, .11);
    color: var(--main-color);
    font-size: 13.5px;
    font-weight: 500;
    line-height: 1;
    transition: transform .28s cubic-bezier(.34, 1.56, .64, 1), box-shadow .2s ease;
}

.nav-back .nb-ic {
    font-size: 15px;
}

/* 按下去把浮起的那层收掉：影子塌下去、亮边留一半，配过冲曲线才有"液体"感 */
.nav-back:active {
    transform: scale(.92);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .7),
                0 0 0 .5px rgba(31, 27, 46, .07),
                0 1px 3px rgba(31, 27, 46, .08);
}

/* 标题栏右上的二级管理入口（开票那两屏挂「开票资料」）。
   Vant 给 __right 预设的是它自己那个蓝（--van-primary-color），不压这一条后台换配色它不动 */
.nav-right {
    font-size: 13.5px;
    color: var(--main-color);
}

.nav-right:active {
    opacity: .55;
}

/* ---------------- 首屏全砸时的整页错误态 ----------------
   进这一屏一件数据都没落地（骨架屏还摆在屏幕上）才会换成这张卡，替的是 <component> 本身，
   不是往 .page 里多塞一个兄弟节点，所以分类/客服那两屏自算的高度不受影响 */
.perr {
    position: relative;
    z-index: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    box-sizing: border-box;
    /* 首页这类整页可滚的屏也要把这句提示摆在可视区中间。原来只给了 14px 上边距，
       一块白卡贴在标题栏底下、底下全是灰，读起来像模块没加载完而不是「连不上」。
       减掉的是顶栏 46 + 跑马灯 30 + 卡片自身上下留白 26（上边距 14 + 页面底部 12）。
       跑马灯那 30px 是按「有公告」算的：后台没配公告时底下少 30px 白，
       比多出一段能滚的空白要客气 */
    min-height: calc(100dvh - 46px - 30px - 26px);
    margin: 14px 12px 0;
    padding: 30px 18px 24px;
    background: #fff;
    border-radius: 14px;
    text-align: center;
}

/* 带底部导航的那几屏，中间还要再让出一条 tabbar，否则「重新加载」钮会被压在导航底下。
   胶囊占的那一段 = 60 高 + --tab-lift（离屏幕下沿那一截），
   这一条按同样的尺减，不能只减 60 —— 少了离底那一截，按钮正好压在胶囊上沿 */
.page.has-tabbar .perr {
    min-height: calc(100dvh - 46px - 30px - 60px - 26px - var(--tab-lift));
}

/* 卡片自己按可视区算过高度了，.page 那条 min-height:100vh 别再叠一遍，
   否则整页会比屏幕高出一截，中间摆好了却还能往下滚一段空白 */
.page.is-perr {
    min-height: auto;
}

.perr-ic {
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--main-color);
    font-size: 54px;
    line-height: 1;
}

.perr-t {
    margin-top: 14px;
    font-size: 15px;
    font-weight: 700;
    color: #1f1b2e;
}

.perr-d {
    margin-top: 6px;
    font-size: 12px;
    line-height: 1.8;
    color: #6b6785;
}

.perr-go {
    margin-top: 16px;
    padding: 9px 30px;
    border-radius: 999px;
    background: var(--main-gradient, var(--main-color));
    color: #fff;
    font-size: 13px;
    font-weight: 600;
    box-shadow: 0 6px 14px var(--main-a35, rgba(98, 54, 255, .25));
}

.perr-go:active {
    transform: scale(.96);
}

.perr-meta {
    margin-top: 9px;
    font-size: 10.5px;
    color: #a5a2b8;
}

/* 左右分栏那两屏没有整页滚动条，卡片要自己把高度撑满，否则缩成一小条 */
.page.is-full .perr {
    margin: 0;
    border-radius: 0;
    min-height: calc(100vh - 46px);
    min-height: calc(100dvh - 46px);
    justify-content: center;
}

.page.is-full.has-tabbar .perr {
    /* 116 → 106+尺子：46 顶栏 + 胶囊 60 高，离底那一截走 --tab-lift（现在就是 3px）。
       第一行是不认 var 的老内核兜底：按尺子当前值 3px 算死 = 109，宁少不多 */
    min-height: calc(100vh - 109px);
    min-height: calc(100dvh - 106px - var(--tab-lift));
}

/* blend 页整块内容往上顶了 46px 去铺顶部色带，卡片跟着下来一点，别压在透明顶栏脸上。
   上边距多了 46，居中的那条高度就要跟着少 46，否则卡片底部会多出一屏滚动 */
.page.is-blend .perr {
    margin-top: 60px;
    min-height: calc(100dvh - 60px - 30px - 26px);
}

/* 打手主页那片深底上，白卡会像一块亮斑，整组翻过来 */
body.nav-dark .perr {
    background: rgba(var(--ds-ink-rgb), .05);
    border: 1px solid rgba(var(--ds-ink-rgb), .1);
}

body.nav-dark .perr-ic {
    color: var(--ds-ink);
}

body.nav-dark .perr-t {
    color: var(--ds-ink);
}

body.nav-dark .perr-d,
body.nav-dark .perr-meta {
    color: rgba(var(--ds-ink-rgb), .55);
}

/* blend 页（现在只剩「资金明细」）：顶栏透明时得让顶部色块铺到屏幕最上边，
   不然透明顶栏后面露的是整页灰底，看着像缺了一条。Vant 那 46px 占位块先收回来 */
.page.is-blend {
    margin-top: -46px;
}

/* 打手主页是整屏深底：body 上的 nav-dark 由 app.js 按路由挂上，顶栏跟着换深色底白字。
   走 --ds-page/--ds-ink 而不是写死，后台把底色配成浅色时顶栏和字一起翻，不留一条白杠 */
body.nav-dark .van-nav-bar {
    --van-nav-bar-background: var(--ds-page);
    --van-border-color: rgba(var(--ds-ink-rgb), .1);
    --van-nav-bar-title-text-color: var(--ds-ink);
    --van-nav-bar-text-color: var(--ds-ink);
    --van-nav-bar-icon-color: var(--ds-ink);
}

/* 暗场那屏已经是深色实底渐变，再糊一层只会把已定稿的「暗场」搅浑。
   纱整层撤掉 —— 注意撤的是 ::before 那一层本身，光改 --van-nav-bar-background 没用，
   那块底色和模糊都长在 ::before 上 */
body.nav-dark .van-nav-bar::before {
    display: none;
}

/* 深底上白底板会变成一块突兀的亮斑，整组翻成半透明白壳 + 白字。
   backdrop-filter 也得撤：这一层是玻璃的"糊"，不跟着 background 一起走，
   暗场底下是整片深色，糊出来的还是一圈更亮的边（1.0.88 那批就漏过一次） */
body.nav-dark .nav-back {
    background: rgba(var(--ds-ink-rgb), .14);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    border-color: rgba(var(--ds-ink-rgb), .22);
    box-shadow: none;
    color: var(--ds-ink);
}

/* blend 页顶栏压在色块上时标题得是白的，滚成白底以后得翻回深色。
   同一个词铺两层、靠透明度交叉换，所以两层都绝对定位居中，标题不会跳一下高度 */
.nav-title {
    position: relative;
    width: 100%;
    height: 20px;
}

.nav-title span {
    position: absolute;
    top: 0;
    left: 50%;
    transform: translateX(-50%);
    white-space: nowrap;
    font-size: 16px;
    font-weight: 500;
}

.nav-title .nt-light {
    color: #fff;
}

.nav-title .nt-dark {
    color: var(--text-main);
}

/* ---------- 首页顶部（站点名交给标题栏了，这块只剩一个搜索框） ---------- */
.home-head {
    padding: 10px 12px;
}

/* 首页搜索框：做成可点击的假输入框，点了进搜索页 */
.home-search {
    display: flex;
    align-items: center;
    background: #fff;
    border-radius: 999px;
    padding: 8px 14px;
    color: #9a9a9a;
    font-size: 13px;
}

.home-search .van-icon {
    font-size: 16px;
    margin-right: 6px;
    color: var(--main-color);
}

/* 分类页顶部搜索条 */
.cate-search {
    display: flex;
    align-items: center;
    /* 左右 12px，和首页轮播 / 卡片 / 订单保持同一条基准线 */
    margin: 10px 12px 0;
    /* 页面底色已经是灰的了，搜索框再用纯白会和白卡片混成一样；
       用比底色更浅一点的白 + 细边，让它明确是个"输入框" */
    background: #fff;
    border: 1px solid #eceef0;
    border-radius: 999px;
    padding: 7px 12px;
    color: #9a9a9a;
    font-size: 13px;
}

.cate-search .van-icon {
    margin-right: 6px;
    color: var(--main-color);
}

/* ---------- 商品列表排序条 ---------- */
/* 一排纯文字，选中那格下面给一小条：不垫底板、不画圈，也不给整行铺白卡。
   这一排是贴着商品列表的筛选器，用色块会把注意力从商品卡上抢走 */
.sort-bar {
    display: flex;
    align-items: center;
    gap: 20px;
    padding: 10px 12px 8px;
    font-size: 13px;
    color: var(--text-mid, #555);
}

.sort-bar .sb-i {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: 2px;
    line-height: 18px;
    padding-bottom: 2px;
}

.sort-bar .sb-i.on {
    color: var(--main-color, #6236ff);
    font-weight: 600;
}

.sort-bar .sb-i.on::after {
    content: '';
    position: absolute;
    left: 50%;
    bottom: -2px;
    transform: translateX(-50%);
    width: 16px;
    height: 2px;
    border-radius: 2px;
    background: var(--main-color, #6236ff);
}

.sort-bar .sb-arrow {
    font-size: 11px;
}

/* 分类页右栏是自己滚的容器，排序条钉在它顶上，否则想换个排法得先滚回顶部；
   搜索页滚的是整页，钉不住也不钉（那边列表不长） */
.cate-main .sort-bar {
    position: sticky;
    top: 0;
    z-index: 5;
    /* 钉住时底下滚过的卡片要压得住，底色照页面灰，不能留透明 */
    background: var(--page-bg, #f8f7f8);
}

/* 「全部」分类的图标占位 */
.cate-icon-all {
    width: 48px;
    height: 48px;
    border-radius: 50%;
    background: #fff2ec;
    color: var(--main-color);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 20px;
}

/* ---------- 轮播 ---------- */
/* 首页轮播：卡片式，左右留边距 + 圆角。
   van-swipe 的高度是内联 style 写死的，所以要 !important 覆盖成按比例自适应，
   否则不同屏宽下卡片被裁切的程度会差很多 */
.home .van-swipe {
    margin: 10px 12px 0;
    border-radius: 12px;
    overflow: hidden;
    height: auto !important;
    aspect-ratio: 2.2 / 1;
}

/* Vant 会用 JS 给 track / item 写内联 height（值来自 van-swipe 的 height 属性），
   内联优先级高于类选择器，这里必须 !important 才能让它们跟着容器的比例走 */
.home .van-swipe__track,
.home .van-swipe-item {
    height: 100% !important;
}

.swipe-img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

/* 首页横幅位（后台广告管理 type=banner，开关 ui_home_banner） */
.home-banner {
    margin: 10px 12px 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.home-banner img {
    width: 100%;
    height: auto;
    display: block;
    border-radius: 8px;
}

/* ---------- 首页案例横滑 ---------- */
/* 尺子照「优秀打手」那一排：左右 12px、卡间距 10px、白底 12 圆角，不另起一套 */
.case-scroll {
    display: flex;
    gap: 10px;
    overflow-x: auto;
    padding: 0 12px 12px;
    -webkit-overflow-scrolling: touch;
}

.case-scroll::-webkit-scrollbar {
    display: none;
}

.case-card {
    flex: none;
    width: 150px;
    border-radius: 12px;
    overflow: hidden;
    background: #fff;
}

.case-card img {
    width: 100%;
    height: 94px;
    display: block;
    object-fit: cover;
    /* 后台没传图时留一块中性底，别让卡片塌成一条缝 */
    background: #f2f4f6;
}

.case-name {
    padding: 8px 10px;
    font-size: 12px;
    color: #333;
    line-height: 1.4;
    /* 案例名容易写长，超一行就省略号收掉，不许把卡片撑得一排高低不齐 */
    display: -webkit-box;
    -webkit-line-clamp: 1;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* ---------- 首页平台介绍 ---------- */
/* 富文本本身的字号行高走 .detail-rich，这里只管卡片外壳和折叠 */
.home-intro {
    margin: 0 12px 12px;
    padding: 12px;
    border-radius: 12px;
    background: #fff;
}

.hi-title {
    font-size: 14px;
    font-weight: 600;
    color: #333;
    margin-bottom: 8px;
}

.hi-body.fold {
    /* 折叠态收成约五行：首页不该被一段自我介绍占掉三屏。
       底下始终挂着「展开全文」，被切掉的那半行也算在告知范围内 */
    max-height: 112px;
}

.hi-more {
    margin-top: 8px;
    text-align: center;
    font-size: 13px;
    color: var(--main-color, #6236ff);
}

/* ---------- 区块标题 ---------- */
.section {
    margin-top: 10px;
}

.section-title {
    font-size: 15px;
    font-weight: 600;
    padding: 12px 12px 8px;
}

/* ---------- 分类横滑 ---------- */
.cate-scroll {
    display: flex;
    overflow-x: auto;
    /* 上下留白要够：选中态的光晕是往外扩的（环 5px + 雾 4px），
       这个容器又是 overflow:auto，留窄了会被直接切掉。
       原来上边只有 4px，实测顶部被切掉 6px。左右 8px 维持原来的基准线。 */
    padding: 10px 8px 12px;
    -webkit-overflow-scrolling: touch;
}

.cate-scroll::-webkit-scrollbar {
    display: none;
}

.cate-item {
    flex: 0 0 auto;
    width: 68px;
    text-align: center;
    margin: 0 4px;
    /* 点下去的即时反馈（按压缩小），和选中态是两回事 */
    transition: transform .12s ease;
    /* 图标和文字分开做过渡，切换时不会一起跳 */
    -webkit-tap-highlight-color: transparent;
}

.cate-item:active {
    transform: scale(.94);
}

/* 图标外面套一层"光环"：
   选中时它不是贴着图案的一圈细描边，而是留出 3px 空隙的扩散光晕，
   看起来像图标被点亮了，而不是被框住了。
   这层平时是透明的，只占位——避免选中时尺寸跳变把文字挤动。 */
.cate-item img {
    width: 48px;
    height: 48px;
    border-radius: 50%;
    object-fit: cover;
    /* 用 box-shadow 而不是 border 做环：border 会占盒模型、把图案压小 */
    box-shadow: 0 0 0 0 var(--main-a0, rgba(255, 112, 67, 0));
    transition: box-shadow .25s cubic-bezier(.34, 1.4, .64, 1),
    transform .25s cubic-bezier(.34, 1.4, .64, 1),
    filter .25s ease;
}

.cate-item span {
    display: block;
    font-size: 12px;
    color: var(--text-sub);
    margin-top: 4px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    transition: color .25s ease, font-weight .25s ease;
}

/* ---------- 选中态 ---------- */
/* 1) 外圈光晕：3px 透明缝 + 2px 主色环，再往外一圈淡淡的同色雾，像发光 */
.cate-item.active img {
    box-shadow: 0 0 0 3px #fff,
    0 0 0 5px var(--main-color),
    0 0 10px 4px var(--main-a35, rgba(255, 112, 67, .35));
    /* 2) 轻微放大：有"被点到"的实感，幅度小才不廉价 */
    transform: scale(1.06);
    /* 3) 未选中的图标压一点亮度和饱和，让选中的那个更突出 */
    filter: none;
}

/* 未选中的稍微收一点，衬托选中项 —— 之前所有图标都是一个亮度，主次不分 */
.cate-item:not(.active) img {
    filter: saturate(.72) brightness(.94);
}

.cate-item.active span {
    color: var(--main-color);
    font-weight: 600;
}

/* 选中项文字下面一条小横线：像 tab 的下划线，给"当前在哪"一个明确的锚点 */
.cate-item span::after {
    content: "";
    display: block;
    width: 0;
    height: 2px;
    margin: 3px auto 0;
    border-radius: 1px;
    background: var(--main-color);
    transition: width .25s cubic-bezier(.34, 1.3, .64, 1);
}

.cate-item.active span::after {
    width: 16px;
}

/* 尊重系统的"减弱动态效果"设置 */
@media (prefers-reduced-motion: reduce) {
    .cate-item,
    .cate-item img,
    .cate-item span,
    .cate-item span::after {
        transition: none;
    }

    .cate-item.active img {
        transform: none;
    }
}

/* ---------- 分类「分离吸顶」条 ---------- */
/* 上面那排图标（48px 圆图 + 名字 + 留白，整块约 91px）滚走之后钉住的浓缩条：只留分类名。
   用 fixed 而不是 sticky：sticky 得先在文档流里占一个位，同一批分类名会在图标排下面重复一遍。
   层级 5 —— 压在商品卡上面，又低于 .van-nav-bar 的 10，不会盖住标题栏。
   没露脸时 opacity:0 + pointer-events:none，商品从它底下走过去照样点得着 */
.home-cate-bar {
    position: fixed;
    top: var(--van-nav-bar-height, 46px);
    left: 0;
    right: 0;
    z-index: 5;
    display: flex;
    align-items: center;
    overflow-x: auto;
    padding: 0 12px;
    background: #fff;
    border-bottom: 1px solid #f0f1f3;
    white-space: nowrap;
    opacity: 0;
    transform: translateY(-8px);
    pointer-events: none;
    transition: opacity .18s ease, transform .18s ease;
}

.home-cate-bar.on {
    opacity: 1;
    transform: none;
    pointer-events: auto;
}

.home-cate-bar::-webkit-scrollbar {
    display: none;
}

.hcb-i {
    flex: 0 0 auto;
    position: relative;
    padding: 12px 10px 11px;
    font-size: 13px;
    color: var(--text-sub);
    -webkit-tap-highlight-color: transparent;
}

.hcb-i + .hcb-i {
    margin-left: 8px;
}

.hcb-i.on {
    color: var(--main-color);
    font-weight: 600;
}

/* 选中跟图标排那一套同款式：只变主题色 + 底下一小条，底下不垫圆圈底板 */
.hcb-i::after {
    content: "";
    position: absolute;
    left: 50%;
    bottom: 5px;
    width: 0;
    height: 3px;
    transform: translateX(-50%);
    border-radius: 2px;
    background: var(--main-gradient, var(--main-color));
    transition: width .25s cubic-bezier(.34, 1.3, .64, 1);
}

.hcb-i.on::after {
    width: 16px;
}

/* ---------- 商品两列卡片 ---------- */
/* 网格内边距 8px + 卡片自身 margin 4px = 左右各 12px，
   正好和轮播 / 卡片 / 订单 / 详情页那几个区块的 12px 对齐 */
.goods-grid {
    display: flex;
    flex-wrap: wrap;
    padding: 0 8px;
}

/* 搜索结果页：商品区顶部要留出空隙，别贴着搜索框 */
.goods-grid.grid-top {
    padding-top: 12px;
}

.goods-card {
    width: calc(50% - 8px);
    margin: 4px;
    background: #fff;
    border-radius: 10px;
    overflow: hidden;
    /* 卡片改成纵向 flex：同一行里文字长短不同也能撑成等高 */
    display: flex;
    flex-direction: column;
}

.goods-img {
    width: 100%;
    /* 封面正方形（1:1）。
       注意：height 必须留 auto——同时写死 height 的话 aspect-ratio 会被忽略，
       浏览器会用那个固定高度，宽高就不等了。 */
    height: auto;
    aspect-ratio: 1 / 1;
    object-fit: cover;
    display: block;
    /* 图片没加载完时露出的底色。
       用和骨架屏同一套流动渐变，而不是一块死灰——外站图或大图慢的时候，
       这样看起来和骨架屏是连续的，不会先"死灰"再"跳"成图。 */
    background: linear-gradient(90deg, #f0f1f3 25%, #e6e7e9 37%, #f0f1f3 63%);
    background-size: 400% 100%;
    animation: sk-flash 1.4s ease infinite;
}

/* 封面外层：只为了给角标当定位参照 */
.goods-thumb {
    position: relative;
    /* 图是 block 且满宽，这层不需要额外尺寸，跟着图走 */
    line-height: 0;
}

/* 商品标签角标（后台「商品管理 → 标签」里填的），压在封面左上角。
   横排 + 自动换行：和淘宝/京东一样，标签占顶部一条，不沿着左边往下吃图。
   一行放不下才折到第二行；最多两个（接口层面已经截断）。 */
.goods-tags {
    position: absolute;
    top: 6px;
    left: 6px;
    /* 留出右边 6px，否则两个标签会顶到封面边缘 */
    right: 6px;
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: 4px;
    /* 不能挡点击：整张卡片是要跳详情页的 */
    pointer-events: none;
    z-index: 2;
}

.goods-tag {
    display: inline-block;
    /* 不设 max-width：横排时让它自然占宽，放不下由 flex-wrap 折行 */
    padding: 2px 7px;
    border-radius: 9px;
    font-size: 10px;
    line-height: 15px;
    color: #fff;
    /* 用主色，跟站内其他强调元素一致；渐变开着时在 app.js 里会换成渐变变量 */
    background: var(--main-gradient, var(--main-color));
    /* 图上什么底色都可能，加一点阴影保证白字看得清 */
    box-shadow: 0 1px 3px rgba(0, 0, 0, .28);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    /* 单个标签最宽不超过整行，超长自己省略 */
    max-width: 100%;
}

/* 第二个标签弱化一档，两个并排时不抢眼 */
.goods-tag + .goods-tag {
    opacity: .82;
}

/* 图片自己加载完了就不用再闪了 */
.goods-img.img-loaded {
    animation: none;
    background: #f2f3f5;
}

.goods-body {
    padding: 8px 10px 10px;
    flex: 1;
    display: flex;
    flex-direction: column;
}

.goods-name {
    font-size: 13px;
    line-height: 18px;
    /* 固定两行高度，标题一行两行都不影响卡片高度 */
    height: 36px;
    overflow: hidden;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
}

.goods-bottom {
    display: flex;
    /* 右边是按钮，用 center 对齐；baseline 会让价格和按钮错位 */
    align-items: center;
    justify-content: space-between;
    /* 价格行贴底，卡片再矮也不会往上跑 */
    margin-top: auto;
    padding-top: 6px;
}

.goods-price {
    color: var(--main-color);
    font-size: 16px;
    font-weight: 700;
}

.goods-sale {
    color: #b0b0b0;
    font-size: 11px;
}

/* 卡片右下角的「去下单」小按钮：主色渐变胶囊，不换行 */
.goods-buy {
    background: var(--main-gradient);
    color: #fff;
    font-size: 11px;
    line-height: 1;
    padding: 6px 11px;
    border-radius: 20px;
    white-space: nowrap;
}

.goods-buy:active {
    opacity: .85;
}

/* ---------- 分类页：左侧竖排分类 + 右侧商品，两侧独立滚动 ---------- */
.cate-page {
    display: flex;
    /* 高度不在这里算 —— 下面那条同名的 .cate-page（带 --tab-lift 那三行）才说了算。
       原来这里留了一份死数 116（= 46 顶栏 + 胶囊 70），胶囊离底那一截后来改成了会随安全区变的尺子，
       两份数就会打架；删掉这一份、只留下面那份带兜底行的，免得日后又各按各的 116 改 */
    /* 用全局灰底，不要写死白色：
       商品卡本身是白底，右侧再铺白底的话两者边界就没了，卡片看着"散"在页面上。
       首页是"白卡 + 灰底"所以卡片浮得出来，这里跟首页保持一致 */
    background: var(--page-bg);
}

/* 分类页整页高度 = 屏幕 - 46 顶栏 - 胶囊（60 高 + --tab-lift 那截离底距离）。
   这一页是 .page.is-full + overflow:hidden，滚不出来：少让一截，右侧商品列表最后一行
   就正好藏在胶囊底下永远点不到。写成三行是故意的：第一行不带 var，
   不认 env()/var 的老内核会把后面两条整条丢掉，还有这条兜着（按尺子 3px 算死 = 109） */
.cate-page {
    height: calc(100vh - 109px);
    height: calc(100vh - 106px - var(--tab-lift));
    height: calc(100dvh - 106px - var(--tab-lift));
}

.cate-side {
    /* 90px 时「充值卡」这种三字名已经贴边，四个字的分类会换行。
       留到 104px，右侧仍有 286px，卡片宽度不受影响（卡片是按百分比算的） */
    flex: 0 0 104px;
    overflow-y: auto;
    /* 比页面灰底再深一档，并加一道右边线：
       原来 #f7f8fa 和页面底几乎同色，左右两栏分不出边界 */
    background: #f0f1f3;
    border-right: 1px solid #e8eaed;
    -webkit-overflow-scrolling: touch;
}

/* 一级分组标题：本身没挂商品，只能看不能点，所以比二级矮一截、不给左边框。
   底色压深一档，跟下面能点的二级拉开，不然一行行同色看不出谁是谁的组 */
.cate-side-group {
    padding: 8px 8px 5px;
    font-size: 11px;
    font-weight: 600;
    color: #71767d;
    background: #e6e8ec;
    border-bottom: 1px solid #dcdfe4;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.cate-side-item {
    padding: 12px 8px;
    font-size: 13px;
    text-align: center;
    color: #4b4f55;
    border-left: 3px solid transparent;
    /* 长分类名别换行把行撑高（原来靠 vant sidebar 的样式兜着） */
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.cate-side-item.active {
    background: #fff;
    border-left-color: var(--main-color);
    color: var(--main-color);
    font-weight: 600;
}

.cate-main {
    flex: 1;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    padding-bottom: 20px;
}

/* 右侧窄，卡片宽度本来就小，正方形封面由 aspect-ratio 自动算高度，不再压固定值 */
.cate-main .goods-img {
    height: auto;
}

.cate-main .goods-name {
    font-size: 12px;
    height: 34px;
}

/* 分类页右侧只有半屏多一点，价格和按钮都要收一点，否则会挤到换行 */
.cate-main .goods-price {
    font-size: 14px;
}

.cate-main .goods-buy {
    font-size: 10px;
    padding: 5px 8px;
}

/* 320 这类小屏：分类页右侧只有 ~100px 宽，价格和按钮横排会挤出卡片，
   改成上下两行。每张卡片都一样，所以不影响等高 */
@media (max-width: 360px) {
    .cate-main .goods-bottom {
        flex-direction: column;
        align-items: stretch;
        gap: 6px;
    }

    .cate-main .goods-buy {
        text-align: center;
    }

    /* 主列表在小屏也收一点，给价格留余量 */
    .goods-buy {
        font-size: 10px;
        padding: 5px 9px;
    }
}

/* ---------- 订单 ---------- */
/* 那排状态分类器（全部/待分配/服务中…）原来跟着列表一起滚走，订单长一点想换个筛选就得先滚回顶。
   钉在标题栏下沿：顶栏是 fixed 的 46px，所以吸顶位置从 46px 起，别贴到 0 去跟标题栏叠一起 */
.order-tabs {
    position: sticky;
    top: var(--van-nav-bar-height, 46px);
    z-index: 3;
    background: #fff;
}

.order-item {
    background: #fff;
    margin: 10px 12px;
    padding: 12px;
    border-radius: 10px;
}

.order-top {
    display: flex;
    justify-content: space-between;
    font-size: 12px;
    color: var(--text-sub);
}

.order-state {
    color: var(--main-color);
    font-weight: 600;
}

.order-right {
    display: flex;
    align-items: center;
    gap: 6px;
}

/* 退款这一枚和订单状态并排：状态说的是活儿走到哪儿，这一枚说的是钱要不要退，
   两件事得同时在卡片上看见。配色跟着下面「退款进度」那几张卡走同一套 */
.order-refund {
    font-size: 11px;
    line-height: 16px;
    padding: 0 6px;
    border-radius: 3px;
    font-weight: 600;
}

/* 催过还没人接的单在列表上就标出来：他不点进详情也该看见「我催了，还没回音」 */
.order-hurry {
    font-size: 11px;
    line-height: 16px;
    padding: 0 6px;
    border-radius: 3px;
    font-weight: 600;
    color: #ee0a24;
    background: rgba(238, 10, 36, .08);
}

.order-mid {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    margin-top: 8px;
}

.order-price {
    font-size: 18px;
    font-weight: 700;
}

.order-time {
    font-size: 12px;
    color: var(--text-sub);
}

.order-remark {
    margin-top: 6px;
    font-size: 12px;
    color: var(--text-sub);
}

.order-goods {
    display: flex;
    align-items: center;
    margin-top: 10px;
}

.order-goods img {
    width: 40px;
    height: 40px;
    border-radius: 6px;
    object-fit: cover;
    margin-right: 8px;
}

.order-goods-name {
    font-size: 14px;
    color: var(--text-main);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.order-tip {
    margin-top: 8px;
    font-size: 11px;
    color: #c8c9cc;
    text-align: right;
}

/* 订单页空态：插画 + 文案 + 按钮整块在剩余空间里垂直居中。
   扣掉的是顶部标题栏 46 + 状态筛选栏 44 + 底部 tabbar。
   tabbar 改悬浮胶囊后占 70（60 + 10 底距），不再是 50，所以 140 → 160 */
.order-empty {
    justify-content: center;
    min-height: calc(100vh - 160px);
    min-height: calc(100dvh - 160px);
}

/* ---------- 订单详情 ---------- */
.od-state {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    margin-bottom: 10px;
}

.od-state-text {
    font-size: 17px;
    font-weight: 700;
    color: var(--main-color);
}

.od-no {
    font-size: 12px;
    color: var(--text-sub);
}

/* 催单那一行：红着说「还没人接」，转绿说「已经接住」。
   两态都上色——淡灰会被看成压根没催过 */
.od-hurry {
    display: flex;
    align-items: baseline;
    gap: 8px;
    margin-top: 10px;
    padding: 7px 10px;
    border-radius: 6px;
    font-size: 12px;
}

.od-hurry.wait {
    color: #ee0a24;
    background: rgba(238, 10, 36, .07);
}

.od-hurry.done {
    color: #07c160;
    background: rgba(7, 193, 96, .08);
}

.od-hurry-main {
    font-weight: 600;
}

.od-hurry-time {
    font-size: 11px;
    opacity: .75;
}

.od-goods {
    display: flex;
    align-items: center;
}

.od-goods img {
    width: 64px;
    height: 64px;
    border-radius: 8px;
    object-fit: cover;
    margin-right: 10px;
}

.od-goods-name {
    font-size: 15px;
    color: var(--text-main);
}

.od-price {
    margin-top: 6px;
    font-size: 17px;
    font-weight: 700;
}

.od-timeline {
    padding-left: 2px;
}

.od-step {
    position: relative;
    padding: 5px 0 5px 18px;
    font-size: 13px;
    color: var(--text-sub);
}

.od-dot {
    position: absolute;
    left: 0;
    top: 11px;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: #ebedf0;
}

.od-dot.on {
    background: var(--main-color);
}

/* ---------- 资金明细（按钱包 app 的路子排） ----------
   氛围带 → 压在带上的余额主卡 → 按月分组的流水（组头吸顶）→ 一行客服提示。
   带上的颜色只用后台配色的半透明版本（--main-a35 / --main-a0），不铺整块实色：
   贴顶时标题栏是透明的，实色块会把「资金明细」四个字压得看不清 */
.ml-bg {
    height: 150px;
    background: linear-gradient(180deg, var(--main-a35) 0%, var(--main-a0) 100%);
}

/* .page.is-blend 把顶栏那 46px 收回来了，带子跟着补高，卡片位置才和别的屏对齐 */
.page.is-blend .ml-bg {
    height: 196px;
}

.ml-hero {
    position: relative;
    z-index: 2;
    margin: -118px 12px 12px;
    padding: 16px 16px 0;
    background: #fff;
    border-radius: 14px;
    box-shadow: 0 10px 26px rgba(17, 24, 28, .07), 0 1px 3px rgba(17, 24, 28, .05);
    overflow: hidden;
    animation: ml-rise .5s cubic-bezier(.22, .61, .36, 1) both;
}

/* 卡面右上角压一枚很淡的主题色光斑：纯装饰，一张白卡压在灰底上不至于太平 */
.ml-hero::before {
    content: '';
    position: absolute;
    top: -52px;
    right: -40px;
    width: 156px;
    height: 156px;
    border-radius: 50%;
    background: radial-gradient(circle, var(--main-a35) 0%, var(--main-a0) 70%);
    pointer-events: none;
}

.ml-hero-top {
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.ml-label {
    font-size: 12px;
    color: var(--text-sub);
    letter-spacing: .4px;
}

/* 余额遮罩钮：点一下这一屏的钱全成点点点，在公共场合打开不至于把余额亮给别人看 */
.ml-eye {
    font-size: 17px;
    color: #a9b1bb;
    padding: 6px;
    margin: -6px;
}

.ml-num {
    display: flex;
    align-items: baseline;
    margin-top: 8px;
    color: var(--text-main);
    /* 数字用等宽字形，从 0 滚到真值的那 600 毫秒里个位不会左右跳 */
    font-variant-numeric: tabular-nums;
    font-feature-settings: 'tnum' 1;
}

.ml-yuan {
    font-size: 17px;
    font-weight: 600;
    margin-right: 3px;
}

.ml-int {
    font-size: 34px;
    font-weight: 700;
    line-height: 1.15;
    letter-spacing: -.5px;
}

.ml-dec {
    font-size: 20px;
    font-weight: 700;
}

.ml-blind {
    font-size: 26px;
    font-weight: 700;
    letter-spacing: 1px;
}

.ml-split {
    height: 1px;
    background: #f0f1f3;
    margin-top: 16px;
}

.ml-sum {
    display: flex;
    padding: 13px 0 15px;
}

.ml-cell {
    flex: 1;
    min-width: 0;
}

/* 第二栏左边一道发丝线，两栏才不像并排贴着的两段孤零零的字 */
.ml-cell + .ml-cell {
    border-left: 1px solid #f0f1f3;
    padding-left: 16px;
}

.ml-cell-k {
    font-size: 11px;
    color: var(--text-sub);
    letter-spacing: .4px;
}

.ml-cell-v {
    margin-top: 6px;
    font-size: 16px;
    font-weight: 500;
    color: var(--text-main);
    font-variant-numeric: tabular-nums;
}

.ml-group {
    animation: ml-rise .42s cubic-bezier(.22, .61, .36, 1) both;
}

/* 组头吸顶：滚过长流水时一直知道这一段是几月的。顶栏 fixed 高 46px，贴在它下沿 */
.ml-ghead {
    position: sticky;
    top: 46px;
    z-index: 3;
    padding: 10px 18px 6px;
    font-size: 12px;
    font-weight: 500;
    color: var(--text-sub);
    background: var(--page-bg);
    letter-spacing: .4px;
}

.ml-card {
    background: #fff;
    border-radius: 12px;
    margin: 0 12px 10px;
    padding: 2px 14px;
    box-shadow: 0 2px 12px rgba(17, 24, 28, .045);
}

.ml-item {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 12px 0;
    border-bottom: 1px solid #f5f6f7;
}

.ml-item:last-child {
    border-bottom: none;
}

.ml-memo {
    font-size: 14px;
    color: var(--text-main);
}

.ml-time {
    font-size: 11px;
    color: var(--text-sub);
    margin-top: 4px;
    font-variant-numeric: tabular-nums;
}

.ml-right {
    text-align: right;
    padding-left: 12px;
}

/* 变动额靠 +/- 号表方向，不加粗也不染色：钱的一行要看得安静 */
.ml-amt {
    font-size: 16px;
    color: var(--text-main);
    font-variant-numeric: tabular-nums;
    font-feature-settings: 'tnum' 1;
}

.ml-after {
    font-size: 11px;
    color: var(--text-sub);
    margin-top: 4px;
    font-variant-numeric: tabular-nums;
}

.ml-empty {
    padding: 34px 0 30px;
    text-align: center;
}

.ml-empty-ic {
    font-size: 40px;
    color: var(--main-a35);
}

.ml-empty-tx {
    margin-top: 12px;
    font-size: 13px;
    color: var(--text-sub);
}

.ml-more {
    font-size: 13px;
    color: var(--text-sub);
    border-color: #e6e8eb;
}

/* 客服提示收成一行脚注：原来那只有一个框、框里一句灰字，像是还没做完 */
.ml-foot {
    margin: 16px 20px 0;
    padding-top: 14px;
    position: relative;
    text-align: center;
    font-size: 12px;
    color: var(--text-sub);
}

.ml-foot::before {
    content: '';
    position: absolute;
    top: 0;
    left: 50%;
    width: 34px;
    height: 1px;
    margin-left: -17px;
    background: #e3e5e8;
}

@keyframes ml-rise {
    from {
        opacity: 0;
        transform: translateY(14px);
    }
    to {
        opacity: 1;
        transform: none;
    }
}

/* 系统里勾了「减弱动效」的，进场那一下不做位移，直接落到最终状态 */
@media (prefers-reduced-motion: reduce) {
    .ml-hero,
    .ml-group {
        animation: none;
    }
}

/* ---------- 设置 / 首页弹窗广告 ---------- */
.st-avatar {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    object-fit: cover;
}

/* 头像行：图和箭头一起贴右边界，整块都在上传热区内 */
.st-av {
    display: flex;
    align-items: center;
    justify-content: flex-end;
}

.st-av-arrow {
    margin-left: 6px;
    font-size: 12px;
    color: var(--text-sub);
}

/* 昵称/简介这两行：点开是弹窗，行上只放回显。有值走正文色，没值走淡灰提示；
   简介最长 50 个字，行上放不下就省略号，进弹窗看全 */
.st-cell .van-cell__value {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.st-val .van-cell__value {
    color: var(--text-main);
}

/* 改资料那个弹窗里的输入框：描边 + 光标用主色，不回填灰底 */
.st-pop {
    padding: 4px 16px 20px;
}

.st-pop .van-field {
    border: 1px solid #e6e8eb;
    border-radius: 8px;
    padding: 9px 12px;
}

.st-pop .van-cell::after {
    display: none;
}

.st-pop .van-field input,
.st-pop .van-field textarea {
    caret-color: var(--main-color);
}

/* 注销账号这一行收进「账号与安全」卡片里当一行，字色跟其它行一样（他判红色太重），
   说明写在值位。真要拦一手，靠的是提交页上那三道门槛，不是这一行的颜色 */
.st-cancel-sub {
    font-size: 12px;
    color: var(--text-sub);
}

/* ---------- 账号注销 ---------- */
/* 这一屏所有非 van-cell 的自定义行都补齐 16px 左右内边距：
   卡片本身没有横向内边距，不补的话说明文字会贴到卡片左边缘，和下面 cell 的 16px 差出一截 */
.ca-page {
    padding-bottom: 20px;
}

.ca-off {
    padding: 12px 16px;
    font-size: 14px;
    line-height: 1.7;
    color: var(--text-mid);
}

/* 状态行：短标签 + 时间，标签一律上色，不给灰 */
.ca-row {
    display: flex;
    align-items: center;
    padding: 0 16px;
    margin-bottom: 8px;
}

.ca-tag {
    padding: 2px 8px;
    border-radius: 4px;
    font-size: 12px;
    color: #fff;
    background: #ff9f1a;
}

.ca-tag.pending {
    background: #ff9f1a;
}

.ca-tag.passed {
    background: #ee0a24;
}

.ca-tag.rejected {
    background: #1989fa;
}

.ca-tag.revoked {
    background: #07c160;
}

.ca-when {
    margin-left: 8px;
    font-size: 12px;
    color: var(--text-sub);
}

.ca-line {
    padding: 0 16px;
    margin-top: 6px;
    font-size: 13px;
    line-height: 1.6;
    color: var(--text-main);
    word-break: break-all;
}

/* 卡片本身只有 4px 上下内边距，说明段又是这张卡的最后一块，
   不垫这 10px 的话灰字直接压在卡片下沿上 */
.ca-tip {
    padding: 0 16px 10px;
    margin-top: 8px;
    font-size: 12px;
    line-height: 1.7;
    color: var(--text-mid);
}

.ca-tip.ok {
    color: #07c160;
}

/* 「撤回注销申请」写成文字钮，不做大按钮：这一屏真正危险的动作只有提交那颗 */
.ca-undo {
    display: inline-block;
    margin-left: 6px;
    color: var(--main-color);
    font-weight: 600;
}

.ca-undo.off {
    opacity: .5;
}

/* 注销后会怎样：左标签右说明的一行行密集排，不堆色块 */
.ca-fact {
    display: flex;
    align-items: flex-start;
    padding: 9px 16px;
    border-bottom: 1px solid #f2f3f5;
    font-size: 13px;
    line-height: 1.6;
}

.ca-fact:last-child {
    border-bottom: none;
}

.ca-fact-k {
    flex: none;
    width: 76px;
    color: var(--text-main);
    font-weight: 600;
}

.ca-fact-v {
    flex: 1;
    color: var(--text-mid);
}

/* 两行的数字和标记各占一列：数字右对齐到同一条竖线，标记再往右各占同宽，
   不然 0.00 元 / 0 个 长短不一，看着就是没对齐。
   原来这里还有第三行「2 张」——券不再拦注销了，那一行挪到「注销之后」里当一句告知 */
.ca-num {
    display: inline-block;
    min-width: 78px;
    margin-right: 8px;
    font-size: 14px;
    color: var(--text-main);
    text-align: right;
    white-space: nowrap;
}

.ca-flag {
    display: inline-block;
    min-width: 46px;
    padding: 1px 6px;
    border-radius: 3px;
    font-size: 11px;
    text-align: center;
    color: #fff;
}

.ca-flag.bad {
    background: #ee0a24;
}

.ca-flag.good {
    background: #07c160;
}

.ca-foot {
    margin: 18px 12px 0;
}

.ca-way {
    margin-top: 10px;
    text-align: center;
    font-size: 12px;
    color: var(--text-sub);
}

/* 占位条要和它替的那几行走同一条竖线，不然首屏一闪、内容进来整排往右挪 */
.ca-sk {
    height: 16px;
    margin: 0 16px 12px;
}

.ca-sk-short {
    width: 60%;
}

.pop-ad {
    position: relative;
    /* 老浏览器兜底 */
    width: 82vw;
    max-width: 340px;
    /* 4:5 竖版：宽同时被屏宽和屏高卡住（图高 = 宽 × 1.25 ≤ 60vh），
       整块「图 + 关闭按钮」不会顶出屏幕 */
    width: min(82vw, 340px, 48vh);
}

/* 圆角和投影挂在这层，图本身不填圆角 */
.pop-ad-card {
    background: #fff;
    border-radius: 12px;
    overflow: hidden;
    box-shadow: 0 6px 20px rgba(0, 0, 0, .18);
}

/* 固定 4:5 的画面框：padding-top 撑比例（125% = 5/4），老 webview 也认；
   图铺满这个框，比例不符就从中间裁 */
.pop-ad-media {
    position: relative;
    width: 100%;
    height: 0;
    padding-top: 125%;
    background: #f2f3f5;
}

.pop-ad-media img {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

/* 广告下方居中的圆形关闭按钮 */
.pop-ad-close {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    margin: 16px auto 0;
    padding: 0;
    color: #fff;
    font-size: 15px;
    background: rgba(0, 0, 0, .35);
    border: 1px solid rgba(255, 255, 255, .55);
    border-radius: 50%;
}

/* ---------- 商品详情 ---------- */
.detail-img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

/* 详情页轮播容器高度由 van-swipe 的 height 属性给，item 撑满即可 */
.detail-page .van-swipe-item {
    height: 100%;
}

.detail-box {
    background: #fff;
    margin: 10px 12px;
    padding: 12px;
    border-radius: 10px;
}

.detail-box .card-title {
    padding: 0 0 8px;
    font-size: 14px;
}

/* ---------- 详情页锚点条 ---------- */
/* 轮播图下面五块内容一屏接一屏，想看评价只能手动往下扒，扒过了还得滚回来。
   钉在标题栏下沿：顶栏是 fixed 的 46px、层级 10，这条从 46px 起吸、层级给 3，
   压得住卡片又躲得开顶栏。两侧照旧留 12px，跟下面的卡片一样是浮在灰底上的一条白。 */
.detail-anchor {
    position: sticky;
    top: var(--van-nav-bar-height, 46px);
    z-index: 3;
    display: flex;
    margin: 0 12px 10px;
    background: #fff;
    border-radius: 10px;
}

.da-item {
    position: relative;
    flex: 1;
    text-align: center;
    font-size: 13px;
    line-height: 1;
    color: var(--text-sub);
    padding: 12px 0 14px;
    cursor: pointer;
}

.da-item.da-on {
    color: var(--main-color);
    font-weight: 600;
}

/* 选中就变主题色加一小条，底下不垫圆圈底板 */
.da-bar {
    position: absolute;
    left: 50%;
    bottom: 5px;
    width: 16px;
    height: 3px;
    margin-left: -8px;
    border-radius: 2px;
    background: var(--main-gradient);
    opacity: 0;
    transform: scaleX(.6);
    transition: opacity .18s, transform .18s;
}

.da-on .da-bar {
    opacity: 1;
    transform: scaleX(1);
}

.detail-price {
    color: var(--main-color);
    font-size: 24px;
    font-weight: 700;
}

.detail-old {
    color: #c0c0c0;
    font-size: 13px;
    font-weight: 400;
    text-decoration: line-through;
    margin-left: 6px;
}

.detail-name {
    font-size: 16px;
    font-weight: 600;
    margin-top: 8px;
    line-height: 1.4;
}

.detail-meta {
    display: flex;
    justify-content: space-between;
    color: var(--text-sub);
    font-size: 12px;
    margin-top: 10px;
}

.detail-text {
    font-size: 13px;
    color: #555;
    line-height: 1.7;
}

/* 购买须知：后台那个框是「每行一条」的写法，前端逐条列出来。
   序号走主题色实心圆，跟商品卡角标是同一套颜色，不加边框不画分割线 */
.notice-item {
    display: flex;
    align-items: flex-start;
    font-size: 13px;
    color: #555;
    line-height: 1.7;
    margin-top: 6px;
}

.notice-no {
    flex: 0 0 16px;
    height: 16px;
    /* 行高约 22px，圆点只有 16px，往下压 3px 才和首行文字视觉上齐平 */
    margin: 3px 8px 0 0;
    border-radius: 50%;
    background: var(--main-color);
    color: #fff;
    font-size: 11px;
    line-height: 16px;
    text-align: center;
}

.notice-t {
    flex: 1;
    word-break: break-word;
}

.detail-rich {
    font-size: 13px;
    color: #555;
    line-height: 1.7;
    overflow: hidden;
}

.detail-rich img {
    max-width: 100%;
    height: auto;
}

/* 富文本框里点出来的列表、标题、链接：全局 reset 把符号和内边距都清掉了，
   这里按编辑器里的样子还回来，不然后台加了项目符号前端完全看不出来 */
.detail-rich p {
    margin: 0 0 8px;
}

.detail-rich p:last-child {
    margin-bottom: 0;
}

.detail-rich ul,
.detail-rich ol {
    margin: 0 0 8px;
    padding-left: 20px;
}

.detail-rich ul {
    list-style: disc;
}

.detail-rich ol {
    list-style: decimal;
}

.detail-rich li {
    margin-top: 2px;
}

.detail-rich h1,
.detail-rich h2,
.detail-rich h3,
.detail-rich h4,
.detail-rich h5,
.detail-rich h6 {
    margin: 12px 0 6px;
    font-size: 15px;
    font-weight: 600;
    color: #333;
}

.detail-rich a {
    color: var(--main-color);
    word-break: break-all;
}

.detail-gap {
    height: 20px;
}

/* ---------- 搜索结果与热门词 ---------- */
.search-bar {
    background: #fff;
    padding: 2px 0;
}

.search-tip {
    padding: 4px 0 12px;
}

.tag-wrap {
    display: flex;
    flex-wrap: wrap;
    padding: 0 12px;
}

.tag-item {
    margin: 0 8px 10px 0;
}

/* ---------- 商品评价 ---------- */
.comment-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding-bottom: 10px;
    border-bottom: 1px solid #f2f2f2;
}

.comment-sum {
    display: flex;
    align-items: center;
    font-size: 12px;
    color: var(--text-sub);
}

.comment-sum em {
    font-style: normal;
    color: #ffb300;
    font-weight: 600;
    margin: 0 4px;
}

.comment-item {
    padding: 12px 0 10px;
    border-bottom: 1px solid #f7f7f7;
}

.comment-item:last-child {
    border-bottom: 0;
}

.comment-top {
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.comment-nick {
    font-size: 13px;
    color: #444;
}

.comment-text {
    font-size: 13px;
    color: #333;
    line-height: 1.6;
    margin-top: 6px;
    word-break: break-all;
}

.comment-imgs {
    display: flex;
    flex-wrap: wrap;
    margin-top: 8px;
}

.comment-imgs img {
    width: 72px;
    height: 72px;
    object-fit: cover;
    border-radius: 6px;
    margin: 0 6px 6px 0;
}

.comment-time {
    font-size: 11px;
    color: #b5b5b5;
    margin-top: 6px;
}

.comment-more {
    text-align: center;
    padding-top: 12px;
}

/* ---------- 我的 ----------
   整页白底：顶部不再铺那条主色渐变带，卡片底下也不压一层灰底。
   这页只有最上那张主题色卡有重量，其余全平。 */
.mine-page {
    padding-bottom: 20px;
    background: #fff;
}

/* 左右 16px 跟着全站那把尺走（.card-title 和 van-cell 都是 16），
   这一页原来是 14，和上下别的页差 2px，看着就是「这一列没对齐」。
   上下留 14：宫格/订单那两排自己有行高，不需要再垫。 */
.mine-card {
    background: #fff;
    border-radius: 8px;
    margin: 0 12px 10px;
    padding: 14px 16px;
    box-shadow: 0 0 18px rgba(0, 0, 0, .06);
}

/* ---------- 顶部那一张大卡 ----------
   照参考站那块卡搬的结构：头像+身份在上，一排数字在中，底下一条身份带。
   颜色不写死——还是拿 --main-gradient 打底，后台换配色这块跟着换。
   以前在上面压了一层深紫半透明（.5→.7），整页拍白以后这块就成了一块黑，太重；
   现在换成从顶部打一层白光，一路从 .18 提到 .30（他连着要了两次「再淡点」）。
   淡和白字是相互拉扯的：底越浅，白字对比越低，所以每提一档白纱，
   卡上的字和胶囊底色都得跟着补一档，不然小字先糊掉。
   卡上的白字只留三档——1（主角：昵称、余额、段位名）、.92（次：胶囊、编辑资料、次要数字、图标）、
   .82（辅：标签、说明文字、箭头）。原来 .8/.76/.74/.64 四档挤在一起，看着糊成一片等于没层级。 */
/* 左右内边距 16px 跟着全站那把尺走：头像/昵称这一列原来 14px，
   和下面三张白卡、和别的页的 van-cell 都对不上那 2px */
.mine-hero {
    position: relative;
    z-index: 2;
    margin: 12px 12px 10px;
    padding: 14px 16px;
    border-radius: 12px;
    background:
        linear-gradient(168deg, rgba(255, 255, 255, .3) 0%, rgba(255, 255, 255, .22) 55%, rgba(255, 255, 255, .18) 100%),
        var(--main-gradient);
    box-shadow: 0 6px 18px var(--main-a35);
    color: #fff;
}

.mh-top {
    display: flex;
    align-items: center;
}

.mh-av {
    width: 56px;
    height: 56px;
    border-radius: 50%;
    object-fit: cover;
    flex: none;
    background: rgba(255, 255, 255, .28);
    border: 1.5px solid rgba(255, 255, 255, .5);
    box-sizing: border-box;
}

/* 没头像（未登录、或登录了没上传）时不再借底部导航那张绿色人图标顶数——那是导航图标，
   绿色往主题色卡上一摆像贴错了东西。统一一块中性灰底 + 深灰人形，一眼就是「这儿还空着」。
   三处共用：「我的」页顶部卡、设置页头像行、打手工作台顶部 */
.mh-av.av-none,
.st-avatar.av-none,
.dw-av.av-none {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: #d5d9df;
    box-sizing: border-box;
}

/* Vant 的 .van-icon 自带 color，光给父级上色吃不进去，得点在图标自己身上 */
.av-none .van-icon {
    color: #8b919a;
}

.mh-av.av-none {
    font-size: 30px;
}

.st-avatar.av-none {
    font-size: 21px;
}

.dw-av.av-none {
    font-size: 28px;
}

.mh-who {
    flex: 1;
    min-width: 0;
    padding-left: 11px;
}

.mh-name {
    font-size: 17px;
    font-weight: 700;
    line-height: 1.25;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.mh-sub {
    font-size: 12px;
    color: rgba(255, 255, 255, .82);
    margin-top: 5px;
}

/* 身份那排小胶囊：方块圆角，不套圆圈 */
.mh-tags {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 5px;
    margin-top: 7px;
}

.mh-tag {
    font-size: 11px;
    font-weight: 500;
    line-height: 1;
    padding: 4px 7px;
    border-radius: 4px;
    color: rgba(255, 255, 255, .92);
    background: rgba(255, 255, 255, .22);
    border: 1px solid rgba(255, 255, 255, .32);
    white-space: nowrap;
}

.mh-edit {
    display: flex;
    align-items: center;
    flex: none;
    align-self: flex-start;
    margin-top: 2px;
    font-size: 12px;
    font-weight: 500;
    background: rgba(255, 255, 255, .22);
    border: 1px solid rgba(255, 255, 255, .34);
    border-radius: 14px;
    padding: 5px 10px;
}

.mh-edit .dk-icon {
    font-size: 12px;
    margin-left: 1px;
}

.mh-arrow {
    color: rgba(255, 255, 255, .82);
    font-size: 16px;
    flex: none;
}

/* 一排数字：格数跟着后台开关变，剩下的自己摊平，中间一条细分隔 */
.mh-stats {
    display: flex;
    margin-top: 15px;
}

.mh-stat {
    display: flex;
    flex-direction: column;
    align-items: center;
    flex: 1;
    min-width: 0;
    position: relative;
    text-align: center;
}

.mh-stat + .mh-stat:before {
    content: "";
    position: absolute;
    left: 0;
    top: 50%;
    transform: translateY(-50%);
    width: 1px;
    height: 22px;
    background: rgba(255, 255, 255, .26);
}

/* 三格数字原来同大小同字重，余额被另外两格平摊掉了。现在分两档：
   余额 22/700 当主角，优惠券和消息 17/600 降一档。两档坐在同一条底线上
   （固定 28px 高、贴底对齐），所以下面那排标签还是齐的，不会一高一低。
   tabular-nums：数字等宽，余额带小数时三位数字的宽度不会互相跳 */
.mh-num {
    display: flex;
    align-items: flex-end;
    justify-content: center;
    height: 28px;
    font-size: 22px;
    font-weight: 700;
    line-height: 1;
    letter-spacing: -.3px;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

.mh-num--sub {
    font-size: 17px;
    font-weight: 600;
    letter-spacing: 0;
    color: rgba(255, 255, 255, .92);
}

.mh-label {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 2px;
    font-size: 11px;
    color: rgba(255, 255, 255, .82);
    margin-top: 4px;
    white-space: nowrap;
}

.mh-more {
    display: inline-flex;
    align-items: center;
    color: rgba(255, 255, 255, .82);
}

.mh-more .dk-icon {
    font-size: 10px;
}

/* 底部那条身份带（参考站那条会员带的位置）。只有打手有内容 */
.mh-band {
    display: flex;
    align-items: center;
    margin-top: 14px;
    padding: 10px 12px;
    border-radius: 9px;
    background: rgba(255, 255, 255, .2);
    border: 1px solid rgba(255, 255, 255, .28);
}

.mb-main {
    flex: 1;
    min-width: 0;
}

.mb-title {
    display: flex;
    align-items: center;
    font-size: 14px;
    font-weight: 700;
}

.mb-title .dk-icon {
    font-size: 14px;
    margin-right: 5px;
    color: rgba(255, 255, 255, .92);
}

.mb-sub {
    font-size: 11px;
    color: rgba(255, 255, 255, .82);
    margin-top: 3px;
}

.mb-chip {
    display: inline-flex;
    align-items: center;
    flex: none;
    font-size: 12px;
    font-weight: 600;
    color: var(--main-color);
    background: #fff;
    border-radius: 14px;
    padding: 6px 11px;
}

.mb-chip .dk-icon {
    font-size: 12px;
}

.mo-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    font-size: 15px;
    font-weight: 600;
    color: var(--text-main);
}

.mo-all {
    display: flex;
    align-items: center;
    font-size: 12px;
    font-weight: 400;
    color: var(--text-sub);
}

.mo-grid {
    display: flex;
    margin-top: 14px;
}

.mo-item {
    flex: 1;
    text-align: center;
}

.mo-icon {
    width: 26px;
    height: 26px;
    line-height: 26px;
    margin: 0 auto 7px;
    color: var(--main-color);
    font-size: 25px;
}

.mo-text {
    font-size: 12px;
    color: var(--text-mid);
}

/* 功能列表：旧站 tn-list-cell 那种一条一条带分割线的样式。
   左右 16 跟别的卡一样；上面要留 14（和另两张卡的组标题同一个数），
   下面留 0——最后一行自己带 15px 下内边距，再垫就双重间距了 */
.mine-cells {
    padding: 14px 16px 0;
}

.mine-cell {
    display: flex;
    align-items: center;
    padding: 15px 0;
    font-size: 14px;
    color: var(--text-main);
    border-bottom: 1px solid #f2f3f5;
}

.mine-cell:last-child {
    border-bottom: 0;
}

.mc-icon {
    font-size: 18px;
    color: var(--main-color);
    margin-right: 10px;
}

.mc-text {
    flex: 1;
}

.mc-arrow {
    font-size: 14px;
    color: #c8c9cc;
}

/* 「我的」页那块图标宫格（常用服务）。
   图标裸着放，不铺圆底 —— 一排八个再各套一个圈，眼睛只看到圈看不到字。
   四列是写死的，条数由 quickCells 算：后台关掉一个开关就少一格，剩下的自己往上补，
   所以这里不能有 nth-child 之类的按位样式。 */
.mq-head {
    font-size: 15px;
    font-weight: 600;
    color: var(--text-main);
}

/* 这张卡上下内边距是 0（行自己带 15px），组标题得自己把下面那点留白补回来 */
.mine-cells .mq-head {
    margin-bottom: 6px;
}

/* 常用服务改成「两排一页、左右翻页」：每页定高两排，第二页哪怕只剩三格
   也照满两排站着，翻页时整张卡不会一高一低地抽 */
.mq-swipe .van-swipe__indicators {
    /* 圆点跟在两排下面自己占一行，不压在最后一排的图标上。
       Vant 原本靠 left:50% + translateX(-50%) 居中，改成 static 之后 left 失效、
       平移还留着，整排点被甩到左边屏幕外（实测左沿 -60），所以平移一起清掉，
       改由 flex 自己撑满一行居中 */
    position: static;
    transform: none;
    width: 100%;
    justify-content: center;
    margin-top: 4px;
}

/* 原来这里是 margin: 2px -5px 0 —— 用负边距把格子往外撑 5px 换格子间距，
   结果第一列的文字压在 147、比卡片内边距还往左 5px，跟全站 152 那条线对不上。
   负边距去掉，列间那点空隙由 .mq-item 自己左右各 5px 内边距顶着（间距 10px→5px） */
.mq-grid {
    display: flex;
    flex-wrap: wrap;
    margin-top: 2px;
    /* 一格 9+26+7+16+9≈67px，两排就是 134，四舍五入按 135 钉住 */
    min-height: 135px;
}

.mq-item {
    width: 25%;
    box-sizing: border-box;
    padding: 9px 5px;
    text-align: center;
}

.mq-icon {
    position: relative;
    width: 26px;
    height: 26px;
    line-height: 26px;
    margin: 0 auto 7px;
    color: var(--main-color);
}

.mq-icon i {
    font-size: 25px;
    line-height: 26px;
}

.mq-badge {
    position: absolute;
    top: -6px;
    right: -13px;
    min-width: 16px;
    height: 16px;
    line-height: 15px;
    padding: 0 4px;
    border-radius: 8px;
    background: #ee0a24;
    color: #fff;
    font-size: 10px;
    font-weight: 600;
    white-space: nowrap;
    box-sizing: border-box;
    /* 白描边：角标压在图标上，不描一圈会和线条糊在一起 */
    border: 1px solid #fff;
}

.mq-text {
    font-size: 12px;
    line-height: 1.35;
    color: var(--text-mid);
}

.mine-logout {
    margin: 18px 12px 0;
}

.mine-out-btn {
    height: 44px;
    line-height: 44px;
    border-radius: 22px;
    background: #fff;
    color: var(--text-main);
    font-size: 15px;
    text-align: center;
    box-shadow: 0 0 18px rgba(0, 0, 0, .06);
}

/* ---------- 详情页底部操作栏占位 ---------- */
/* ActionBar 自己有 placeholder，这里只留一点呼吸位；
   最后一块的 10px 下边距要清掉，否则内容会几乎贴着操作栏 */
.detail-page {
    padding-bottom: 16px;
}

.detail-page > *:last-child {
    margin-bottom: 0;
}

/* ---------- 悬浮客服按钮（玻璃 + 液态按压）---------- */
/* 玻璃成不成，全看它背后有没有东西能透出来：这颗压在首页滚动的商品卡上，
   所以 blur 之外还要 saturate 提一档，不然糊出来是灰蒙蒙一片 */
.fab-service {
    position: fixed;
    right: 14px;
    /* 这颗球钉在胶囊上肩：胶囊顶沿 = 60 高 + --tab-lift，再留 14px 缝 → 74 + 尺子。
       原来那个 84 + env() 是按"离底 10px、安全区全额"算的，胶囊离底改成走 --tab-lift
       之后这里必须跟着改，否则 iPhone 上胶囊降下去了、球还悬在老位置，中间空出一大截 */
    bottom: calc(74px + var(--tab-lift));
    z-index: 99;
    width: 46px;
    height: 46px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #fff;
    font-size: 22px;
    overflow: hidden;
    -webkit-backdrop-filter: blur(13px) saturate(175%);
    backdrop-filter: blur(13px) saturate(175%);
    background: rgba(255, 255, 255, .26);
    /* 玻璃的"读"其实靠三条：上缘一道亮边、下缘一道暗边、外圈一圈彩色投影。
       透明度给多大反而是次要的——背后的图糊成一片时，没有这三条就只是一颗扁圆 */
    box-shadow: inset 0 1.5px 0 rgba(255, 255, 255, .8),
                inset 0 -8px 14px rgba(0, 0, 0, .18),
                inset 0 0 0 1px rgba(255, 255, 255, .28),
                0 6px 16px var(--main-a40);
    /* 回弹用的曲线末尾 overshoot > 1：松手时略微过冲一下，才有「液体」而不是「塑料」 */
    transition: transform .34s cubic-bezier(.34, 1.56, .64, 1), box-shadow .22s ease;
}

/* 主色染色单独压一层：透明度只作用于这一层，底下那层模糊照常透出内容。
   .60 是两档背景量出来的——首页那种深图上再低也够玻璃味，但「我的」页底下是浅灰卡，
   染色一低整颗就洗成淡紫，白图标跟着糊 */
.fab-service::before {
    content: '';
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    border-radius: inherit;
    background: var(--main-gradient);
    opacity: .6;
}

/* 压在球面上半部的一道弧形高光，按下时往右下扫出去 */
.fab-service::after {
    content: '';
    position: absolute;
    left: -10%;
    top: -14%;
    width: 120%;
    height: 62%;
    border-radius: 50%;
    background: radial-gradient(ellipse at 30% 0%, rgba(255, 255, 255, .62), rgba(255, 255, 255, 0) 70%);
    pointer-events: none;
    transition: transform .34s ease, opacity .34s ease;
}

.fab-service > .van-icon {
    position: relative;
    z-index: 1;
    text-shadow: 0 1px 5px rgba(0, 0, 0, .34);
}

.fab-service:active {
    transform: scale(.86) translateY(2px);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .5),
                inset 0 -4px 9px rgba(0, 0, 0, .22),
                inset 0 0 0 1px rgba(255, 255, 255, .2),
                0 2px 8px var(--main-a40);
}

.fab-service:active::after {
    transform: translate(26%, 52%) scaleY(.7);
    opacity: .35;
}

/* 不支持 backdrop-filter 的内核（老版 X5 之类）会「透但不糊」，那比现在更脏：
   退回一颗接近实色的渐变钮，只保留按压缩放 */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    .fab-service {
        background: var(--main-gradient);
        box-shadow: 0 4px 12px var(--main-a40);
    }

    .fab-service::before,
    .fab-service::after {
        display: none;
    }

    /* 置顶那颗跟着一起退：不透明白球 + 原来那条软投影，玻璃的高光边全部撤掉 */
    .fab-top {
        background: #fff;
        color: #5f6368;
        box-shadow: 0 2px 10px rgba(0, 0, 0, .14);
    }

    .fab-top::after {
        display: none;
    }
}

/* 首页往下拉才露面的置顶：跟客服按钮同一条竖线、同一个大小，压在它上面。
   材质跟客服球一套（同一张尺子：纱 + 模糊 + 上缘亮边 + 下缘压深 + 外圈发丝），
   差别只在两处 —— 不给它垫主色渐变那一层（::before），影子也不走主色：
   主色那颗是「找客服」，这颗只是页面滚动工具，抢了注意力就分出主次了。
   字色从 #5f6368 提到 #464a52：球现在透了，深色一点才压得住背后滚过来的商品图 */
.fab-top {
    position: fixed;
    right: 14px;
    /* 跟客服球一起钉：那颗底在 74 + 尺子、高 46，顶在 120 + 尺子；
       这一颗留在它上面 12px 的缝，就是 132 + 尺子。两颗认同一把尺，才会一起降 */
    bottom: calc(132px + var(--tab-lift));
    z-index: 99;
    width: 46px;
    height: 46px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    color: #464a52;
    font-size: 20px;
    -webkit-backdrop-filter: blur(13px) saturate(165%);
    backdrop-filter: blur(13px) saturate(165%);
    background: rgba(255, 255, 255, .42);
    box-shadow: inset 0 1.5px 0 rgba(255, 255, 255, .85),
                inset 0 -8px 14px rgba(0, 0, 0, .12),
                inset 0 0 0 1px rgba(255, 255, 255, .3),
                0 6px 16px rgba(14, 16, 28, .18);
    transition: transform .34s cubic-bezier(.34, 1.56, .64, 1), box-shadow .22s ease;
}

/* 球面顶部那道弧形高光，按下时往右下扫出去 —— 跟客服球同一个动作 */
.fab-top::after {
    content: '';
    position: absolute;
    left: -10%;
    top: -14%;
    width: 120%;
    height: 62%;
    border-radius: 50%;
    background: radial-gradient(ellipse at 30% 0%, rgba(255, 255, 255, .6), rgba(255, 255, 255, 0) 70%);
    pointer-events: none;
    transition: transform .34s ease, opacity .34s ease;
}

.fab-top > .van-icon {
    position: relative;
    z-index: 1;
}

.fab-top:active {
    transform: scale(.86) translateY(2px);
    box-shadow: inset 0 1.5px 0 rgba(255, 255, 255, .6),
                inset 0 -5px 10px rgba(0, 0, 0, .14),
                inset 0 0 0 1px rgba(255, 255, 255, .2),
                0 2px 8px rgba(14, 16, 28, .16);
}

.fab-top:active::after {
    transform: translate(26%, 52%) scaleY(.7);
    opacity: .35;
}

/* ---------- 登录 ---------- */
/* 登录页整页走白底，不吃全局灰底 --page-bg：这一屏只有表单，灰底会把注意力分掉。
   顶栏 46px 占位要扣掉，否则底部多出 46px 灰边；padding-bottom 也归零，
   免得 .page 自己那 12px 留白露出灰底 */
.page.is-white {
    display: flex;
    flex-direction: column;
    background: #fff;
    min-height: calc(100vh - 46px);
    min-height: calc(100dvh - 46px);
    padding-bottom: 0;
}

/* 登录屏是 flex 列：表单吃掉剩下的高度，底部那条版权行自然贴到屏幕最下沿 */
.page.is-white .login-page {
    flex: 1 0 auto;
}

.login-page {
    padding-top: 30px;
}

/* 白底上原来靠灰底衬出来的输入卡片会糊成一片。这里不往回铺灰，改成：
   描边比页面只重一档 + 圆角跟下面那颗主按钮对齐取 12px + 一层极淡投影把卡片抬起来 */
.login-page .van-cell-group--inset {
    border: 1px solid #ececee;
    border-radius: 12px;
    box-shadow: 0 2px 14px rgba(0, 0, 0, .05);
    transition: border-color .2s, box-shadow .2s;
}

/* 点到卡片里（含「获取验证码」那一格）整块描边走主题色，外面套一圈淡光环。
   --main-color / --main-a35 都是 app.js 按后台「界面设置」重算过的，换配色它跟着换；
   :focus-within 挂在卡片自身，不用 :has，老安卓 webview 也认 */
.login-page .van-cell-group--inset:focus-within {
    border-color: var(--main-color);
    box-shadow: 0 0 0 3px var(--main-a35);
}

/* 光标那一竖线也染成主色，不再是系统默认蓝 */
.login-page .van-field__control {
    caret-color: var(--main-color);
}

/* 输入框加高：Vant 默认那档上下各 10px、算下来 44px 出头，手机上手指容易压到描边。
   只加内边距，字号、描边、12px 圆角都不动，卡片跟着长高一点 */
.login-page .van-cell {
    padding-top: 14px;
    padding-bottom: 14px;
}

/* ---------- 修改密码 / 忘记密码 ----------
   这两屏挂在 .login-page 上，为的是直接吃上面那套描边卡片 + 聚焦光环 + 加高输入框，
   不再另起一套口径。这里只把它们自己的节奏补回来。 */
.pwd-page {
    padding-top: 14px;
}

.pwd-tip {
    margin: 2px 16px 14px;
    font-size: 13px;
    line-height: 19px;
    color: var(--text-sub);
}

/* 「这个账号没绑手机号」那一行灰标签底下再补一句红，比只把按钮置灰说得更明白 */
.pwd-warn {
    color: var(--van-danger-color, #ee0a24);
}

/* 默认就停在旧密码那一种，顶上不再摆一排选法；要换别的走按钮上方右侧这个入口，
   点了从底部弹层挑（弹层里只列其余那几种，当前这种不重复出现） */
.pwd-more {
    /* 右边 12px 和下面那颗按钮取齐；往下压 8px，是为了抵掉按钮自带的 16px 上边距，
       让这一行和按钮之间留 8px，不像隔了一整段 */
    margin: 8px 12px -8px;
    text-align: right;
    font-size: 13px;
    line-height: 18px;
    color: var(--main-color);
    cursor: pointer;
}

/* 弹层每行下面那句小字（发到哪个手机号、什么情况下走这条） */
.van-action-sheet__subname {
    margin-top: 3px;
    font-size: 11.5px;
    line-height: 16px;
    color: var(--text-sub);
}

.pwd-submit {
    margin: 16px 12px 24px;
}

.pwd-submit .van-button + .van-button {
    margin-top: 10px;
}

/* 登录页「忘记密码？」：贴着卡片右下角，不另起一行大字抢主按钮的位置 */
.login-forget {
    margin: 6px 16px 0;
    text-align: right;
    font-size: 13px;
    line-height: 18px;
}

.login-forget span {
    color: var(--main-color);
    cursor: pointer;
}

.login-logo {
    text-align: center;
    font-size: 24px;
    font-weight: 700;
    color: var(--main-color);
}

/* 登录页协议勾选行 */
.agree-line {
    display: flex;
    align-items: flex-start;
    margin: 14px 12px 0;
    font-size: 12px;
    color: var(--text-sub);
    line-height: 18px;
}

.agree-box {
    width: 16px;
    height: 16px;
    min-width: 16px;
    margin-right: 6px;
    margin-top: 1px;
    border: 1px solid #c8ccd2;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #fff;
    font-size: 11px;
}

.agree-box.on {
    background: var(--main-color);
    border-color: var(--main-color);
}

.agree-text b {
    color: var(--main-color);
    font-weight: 400;
}

/* ---------- 协议页 / 单页文章 ---------- */
.agree-page {
    /* 左右 12px，和其它页面的区块基准线一致 */
    padding: 14px 14px 28px;
    background: #fff;
    min-height: 100vh;
}

.agree-meta {
    color: var(--text-sub);
    font-size: 12px;
    margin-bottom: 12px;
    padding-bottom: 10px;
    border-bottom: 1px solid #f2f3f5;
}

.agree-loading {
    padding: 30px 0;
    text-align: center;
}

/* 正文是后台富文本编辑器直接存下来的 HTML，标签只有 h1~h6 / p / ul / ol / li /
   strong / a / img / table / blockquote 这些，下面按这套逐个上样式 */
.agree-body {
    font-size: 15px;
    line-height: 1.9;
    color: #303237;
    letter-spacing: .2px;
    text-align: justify;
    /* 原来这条 break-all 会把英文单词和备案号从中间劈开，中文本来就不会溢出 */
    word-break: normal;
    overflow-wrap: break-word;
}

.agree-body > :first-child {
    margin-top: 0;
}

/* 小节标题：左边一小条主题色，字本身用近黑，整段读起来才有骨架 */
.agree-body h2,
.agree-body h3,
.agree-body h4 {
    position: relative;
    margin: 24px 0 10px;
    padding-left: 10px;
    font-weight: 600;
    color: #1f2126;
    line-height: 1.5;
    text-align: left;
}

.agree-body h2 {
    font-size: 17px;
}

.agree-body h3 {
    font-size: 16px;
}

.agree-body h4 {
    font-size: 15px;
}

.agree-body h2::before,
.agree-body h3::before,
.agree-body h4::before {
    content: "";
    position: absolute;
    left: 0;
    top: 5px;
    bottom: 5px;
    width: 3px;
    border-radius: 2px;
    background: var(--main-color);
}

.agree-body p {
    margin: 9px 0;
}

.agree-body ul,
.agree-body ol {
    margin: 9px 0;
    padding-left: 22px;
}

.agree-body li {
    margin: 5px 0;
    padding-left: 2px;
}

.agree-body ol > li::marker {
    color: var(--main-color);
    font-weight: 600;
}

.agree-body ul > li::marker {
    color: var(--main-color);
}

/* 编辑器里的「1. 2. 3.」多半是直接打在段落里的，段落之间就收紧一点，
   免得每行都还顶着 p 的 9px 上间距，看着比小标题还松 */
.agree-body p + p {
    margin-top: 6px;
}

.agree-body strong,
.agree-body b {
    font-weight: 600;
    color: #1f2126;
}

.agree-body em,
.agree-body i {
    font-style: normal;
    color: var(--main-color);
}

.agree-body a {
    color: var(--main-color);
    text-decoration: none;
    border-bottom: 1px solid rgba(0, 0, 0, .08);
    word-break: break-all;
}

.agree-body img {
    display: block;
    max-width: 100%;
    height: auto;
    margin: 12px auto;
    border-radius: 8px;
}

.agree-body blockquote {
    margin: 12px 0;
    padding: 10px 12px;
    background: #f8f8fb;
    border-left: 3px solid var(--main-color);
    border-radius: 0 6px 6px 0;
    color: #5a5d66;
    font-size: 14px;
    line-height: 1.8;
}

.agree-body blockquote p {
    margin: 0;
}

.agree-body hr {
    border: 0;
    border-top: 1px solid #f0f1f3;
    margin: 18px 0;
}

.agree-body code {
    padding: 1px 5px;
    background: #f4f5f7;
    border-radius: 3px;
    font-size: 13px;
}

/* 表格：整张表横向自己滚，不把整页撑出第二条滚动条 */
.agree-body table {
    width: 100%;
    margin: 12px 0;
    border-collapse: collapse;
    font-size: 13px;
    display: block;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

.agree-body th,
.agree-body td {
    border: 1px solid #ebedf0;
    padding: 8px 10px;
    text-align: left;
    white-space: nowrap;
}

.agree-body th {
    background: #f7f8fa;
    font-weight: 600;
    color: #1f2126;
}

.login-tip {
    text-align: center;
    color: var(--text-sub);
    font-size: 12px;
    margin: 8px 0 24px;
}

/* 登录方式页签（验证码登录 / 账号登录），指示条跟着主题色 */
.login-tabs {
    margin: 0 12px 12px;
    --van-tabs-bottom-bar-color: var(--main-color);
    --van-tab-active-text-color: var(--main-color);
}

/* Vant 的页签指示条只吃 background-color，渐变得用 background-image 叠上去，
   这样后台传不传 color 属性都不影响，渐变开关关掉时它就等价于纯色 */
.van-tabs__line {
    background-image: var(--main-gradient);
}

/* 详情页底部「立即下单」：Vant 的 danger 自带一套固定红渐变，不跟后台配色，
   这里覆盖成主题渐变；「咨询客服」保持 Vant 原样，两个按钮才有主次 */
.van-action-bar-button--danger {
    background: var(--main-gradient);
    border: none;
}

/* ---------- 通用 ---------- */
.card {
    background: #fff;
    border-radius: 10px;
    margin: 10px 12px;
    padding: 4px 0;
}

.card-title {
    font-size: 15px;
    font-weight: 600;
    padding: 12px 16px;
}

.empty {
    text-align: center;
    color: #999;
    font-size: 13px;
    padding: 40px 0;
}

/* 版权行：挂在首页 / 我的 / 登录这三屏底部（路由表里的 copy 开关）。
   字号压到最小一档、颜色用中性灰，不跟主按钮抢注意力 */
.app-copy {
    padding: 16px 12px 18px;
    text-align: center;
    font-size: 11px;
    line-height: 1.6;
    color: #b6b8bd;
    word-break: break-all;
}

.app-copy a {
    color: inherit;
    text-decoration: none;
}

.load-more {
    text-align: center;
    padding: 14px 0 24px;
}

/* 分类页到底换分类那行提示。
   商品少的时候这行是唯一的出口（滑不动就触发不了自动换类），
   所以做成明显能点的胶囊按钮，而不是一行灰字——原先的灰字看不出可以点。 */
.cate-tip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 13px;
    color: var(--main-color);
    line-height: 1.4;
    padding: 9px 16px;
    border: 1px solid var(--main-color);
    border-radius: 999px;
    background: #fff;
    cursor: pointer;
    text-align: left;
    transition: background .15s;
}

.cate-tip:active {
    background: rgba(255, 112, 67, .08);
}

/* 按钮末尾的小箭头，方向随文案（下一个分类 →） */
.cate-tip::after {
    content: "\203A";
    font-size: 16px;
    line-height: 1;
}

/* tabbar 自定义图标
   尺寸按旧站（uni-app pages.json）来的：栏高 50px、图标 24px、文字 14px。
   图标原本是 PNG，颜色烧死在图里（选中态是旧站那款绿 #00C59B），后台换主题色它不跟着变。
   这里改成 mask：图形还是原来那张图，颜色由 background 填，所以选中态直接吃 --main-gradient。
   两张图各自当遮罩用，所以「分类」选中是实心、未选中是描边这个原有区别还在。
   Vant 自带一条 .van-tabbar-item__icon img{height:20px}，会把 24px 宽的图标压成 24x20，
   看着就是扁的、变形的，所以这里用更长的选择器把高度顶回去。 */
.tab-icon {
    width: 24px;
    height: 24px;
    display: block;
    margin: 0 auto 2px;
    /* 未选中从 #8b8b8b 压到 #33333a。
       原来那个灰是"烧死"在图里的旧站配色，跟 inactive-color 也不联动；
       结果是图标比文字还淡，一颗描边图标压在浅色玻璃上几乎看不见轮廓。
       压深之后描边的形才立得住，和右侧那颗实心主题色的选中态也拉得开 */
    background-color: #33333a;
    -webkit-mask: var(--tab-img) no-repeat center / 100% 100%;
    mask: var(--tab-img) no-repeat center / 100% 100%;
    /* 每张图"画出来有多大"不一样，各给一个校准倍率，见下面那四行 */
    --tab-s: 1;
    transform: scale(var(--tab-s));
}

/* 图标的视觉大小校准（--tab-s）改成由 app.js 的 tabs 数据给、模板内联到 .tab-icon 上。
   原来这里是四条按格序的规则：
       .van-tabbar-item:nth-child(1) .tab-icon  →  --tab-s: 1.044
       ……
   踩过一次坑：为了做滑动水滴，在底栏里插了一个 .tab-fluid 当第一个子元素，
   四个 item 的序号整体后移一格，四条校准全部串位 —— 首页套上了分类的倍率、
   分类套上订单的、订单套上我的，四个图标又变得大小不一，
   而且不报错、只能靠肉眼看出来。改成数据驱动之后，底栏里再插几个节点都不受影响。
   四档数值的来历见 app.js 里 tabs 定义上那段注释。 */
.van-tabbar-item--active .tab-icon {
    background: var(--main-gradient);
}

/* 悬浮胶囊：脱开屏幕三边，做成一颗浮在内容上的玻璃药丸。
   原来是贴满整条宽度的通栏底栏 —— 那种形态下"玻璃"很难成立：
   一条横贯屏幕的白纱，视觉上就是一根白条，透过去的东西也只能是模糊的横纹。
   缩小成一颗胶囊之后，四周都能看见背后的内容，玻璃才读得出来。
   底下留一点而不是 0：它得"浮"着，贴死屏幕下沿就又变回通栏了。具体多少走 :root 的 --tab-lift
   （现在定 3px，不再吃 env 安全区 —— 他试过 8px 全额、吃三成，都要嫌高）
   也不能靠 Vant 那个 van-safe-area-bottom 的内边距 ——
   那个是往下撑高的，胶囊要的是整颗往上抬 */
.van-tabbar.van-tabbar--fixed {
    left: 12px;
    right: 12px;
    bottom: var(--tab-lift);
    /* Vant 给 .van-tabbar 写了 width:100%，会把上面那对 left/right 内缩顶掉 ——
       实测不加这句的话宽度还是整整一屏（390），右边跑到 -12，胶囊左右根本收不进来。
       置成 auto 之后宽度由 left/right 决定，才真的是"脱开两边" */
    width: auto;
    height: 60px;
    /* 左右各留 6px 内边距：四格均分时，首尾两格的选中椭圆会紧贴胶囊的圆角，
       看起来像被挤扁了，而中间两格四周都有留白 —— 同一颗椭圆在四个位置观感不一样。
       加这 6px 之后每格 88.5 宽，椭圆左右各有 ~10px 余量，四个位置就一致了 */
    padding-left: 6px;
    padding-right: 6px;
    /* 必须 border-box：Vant 没给底栏设这个，默认 content-box 会让上面那 12px
       内边距加在宽度外面 —— 宽屏那条按 480 收窄的规则因此量出来是 468 宽、
       左 222 右 210，整颗胶囊偏右 6px。设成 border-box 之后宽度就是净宽 */
    box-sizing: border-box;
    /* 抵消 van-safe-area-bottom 加的 padding-bottom，否则胶囊会被撑高、字偏上 */
    padding-bottom: 0;
    /* 全圆：60 高的一半。圆角够了才有"一颗"的感觉，21 那种只是圆角矩形 */
    border-radius: 30px;
    /* 不能裁：选中态那颗椭圆要"鼓"出胶囊的上下边沿，裁了就变成压在里面的一个椭圆，
       "液态"的感觉全没了。原来写 hidden 是为了裁上沿那道折射高光（它是一条横贯左右的亮带），
       现在改成给高光自己做左右淡出（见下面 .van-tabbar::before 的 mask），
       两端化开之后就不会从圆角外面飘出小尖角，也就不需要裁整条了 */
    overflow: visible;
}

.van-tabbar {
    /* 50 → 60：胶囊实际高 60（见上面 .van-tabbar--fixed 的 height）。
       这一版改悬浮胶囊时只改了外面那条 height，变量一直没跟上。
       当下不会立刻出错 —— 上面那条选择器权重更高，栏高仍按 60 走 ——
       但 Vant 内部按这个变量算占位块和格子高度，谁哪天把 --fixed 那条
       height 撤了，栏就会缩回 50，图标文字跟着挤成一团 */
    --van-tabbar-height: 60px;
    --van-tabbar-item-font-size: 14px;
    --van-tabbar-item-icon-size: 24px;
    --van-tabbar-item-icon-margin-bottom: 2px;
    /* ★ 这才是那块"白色长方形"的真身，不是白纱、也不是水滴。
       Vant 给选中的那一格自己刷了一层不透明白：.van-tabbar-item--active 的
       background-color 吃 --van-tabbar-item-active-background，默认值 #fff。
       实测该格 89×60、圆角 0、纯白 rgb(255,255,255)，直角矩形压在玻璃栏上，
       而且它 z-index:1 排在水滴（z-index:0）上面 —— 等于把水滴整个盖死，
       所以之前怎么调白纱、怎么调水滴都不见效（撤掉水滴画面上一点变化都没有，
       逐像素差最大只有 4/255，就是被这层白底挡住的）。
       撤成 transparent 之后玻璃才真的透，水滴也是第一次真的露出来 */
    --van-tabbar-item-active-background: transparent;
    /* 和顶栏同一个毛病：Vant 只给到底栏 1 层，而商品卡角标是 z-index 2，
       列表滚到底栏那一条时角标会浮到 tab 文字上面。把底栏提到内容之上（顶栏 10、客服悬浮球 99，都不冲突） */
    z-index: 10;
    /* 白纱：整层从 .86~.88 撤到 .34~.64。
       上一版虽然比 .94 那版透，但仍然厚到把胶囊填成一块白色长方形 ——
       「选中已经看得出是水滴了，底下这块白底还有必要吗」问的就是这一层。
       现在上半基本是清的（能直接看见背后的内容），只有图标和文字那一排
       （栏内 8~52px）才爬到 .64 当地。
       ★ 不能一路撤到全透明：未选中的图标是 #33333a 的深灰，首页底下那种深色
         banner 压在栏后面时，实测无底对比度只有 1.9:1（可读性下限 4.5:1），
         图标直接看不见。留 .64 这一档、再把下面 brightness 提到 1.35，
         深色底上量到 5.0:1，白块的感觉没了，字也还压得住。
       色标仍用绝对 px 不用百分比：Vant 给底栏挂了 van-safe-area-bottom，
       百分比会被那条安全区拉匀 */
    /* 白纱：栏这一层要"实"，选中态靠水滴自己带颜色去区分 —— 不要靠把栏做透。
       来回试过两档，结论是这一档：
         .86~.94  栏是实的，但水滴也是白的 → 白压白，椭圆看不见
         .34~.64  栏透了 → 背后的白色商品卡直接透成一个硬边白块，
                  那正是"选中底下有一块白底"的来源，而且水滴的淡紫也被冲掉
       ★ 关键认识：那颗椭圆要看得见，靠的不是"栏透"，是"水滴有颜色"。
         参考图里栏也是相当实的白，靠的是那颗带颜色的椭圆。
       所以栏压回 .78~.94，水滴给到 22% 主题色（见下面 .tab-fluid i::after）。
       最上面 8px 留 .78 当玻璃缘，能看出下面有东西，又不至于把内容整块放出来。
       色标仍用绝对 px 不用百分比：Vant 给底栏挂了 van-safe-area-bottom，
       百分比会被那条安全区拉匀 */
    --van-tabbar-background: linear-gradient(180deg,
        rgba(249, 250, 253, .78) 0px,
        rgba(249, 250, 253, .86) 8px,
        rgba(250, 251, 254, .90) 20px,
        rgba(250, 251, 254, .93) 34px,
        rgba(250, 251, 254, .94) 100%);
    /* brightness 1.35 → 1.12：1.35 是为了配 .64 那种薄纱才提上去的，
       纱回到 .94 之后它只会把白卡片顶到 255 封顶、加大左右色差。
       1.12 够把深色底抬到字压得住，又留出不封顶的余量 */
    -webkit-backdrop-filter: blur(26px) saturate(150%) brightness(1.12);
    backdrop-filter: blur(26px) saturate(150%) brightness(1.12);
    /* 边缘：胶囊四边都得有光，不然它跟底下的内容糊在一起、看不出是浮着的。
       一条外暗发丝线 + 紧贴它内侧的一条亮线，凑成一个"倒角"。
       单放白亮线在浅底上看不见（上一版就是这样，等于没写）；
       有暗线打底之后那条白线才有对比，玻璃的厚度才读得出来。
       投影改成四面扩散（0 8px 24px），不再只往上打：胶囊是浮着的，
       影子该落在它下面一圈，而不是像通栏那样只往上方压一条 */
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .85),
                inset 0 -1px 0 rgba(255, 255, 255, .5),
                0 0 0 .5px rgba(0, 0, 0, .08),
                0 2px 6px rgba(0, 0, 0, .07),
                0 10px 28px rgba(0, 0, 0, .18);
}

/* Vant 给底栏挂的是 van-hairline--top-bottom：::after 一条不透明白边压在上下两条沿上。
   换成玻璃以后这条实线正好挡在内容和玻璃的接缝处，等于把「东西从底下穿过去」那条线索掐了，
   所以把它的颜色撤了，分界交给上面那四条 box-shadow */
.van-tabbar.van-hairline--top-bottom::after {
    border-color: transparent;
}

/* 上沿那道折射高光单独拎出来掰。
   为什么不掰背后的内容：折射层压在白纱下面，白纱自己那层 blur(26) 会把掰过的内容再糊一遍，
   实测开与关逐行差值只有 1.5/255（峰值 9），看不见但每帧的开销照付，不划算。
   掰这道高光不受身后是什么影响，位移一挂上就是肉眼可见的一条水波边。
   z-index:-1 把它压在「栏自己的白底和 box-shadow 之上、四个图标和文字之下」，
   所以它不会被 tab 文字一起掰，也不会盖住图标。
   高光画在 y=4 而不是 y=0：±5px 的位移如果从最顶上开始，会有一半被推出栏外，
   看着像有根亮丝飘到商品图上。

   ★ 这一版在亮线下面垫了一条 3px 的暗肩（上一版只有亮线，位移开与关看不出来）：
     一条纯白细线压在已经发白的玻璃上，亮度差只有 20 级左右，掰弯了也分辨不出；
     配上"亮线 + 暗肩"这一对，位移一动就是明暗错位，边缘立刻读出是水波而不是直边。
     暗肩也就是厚玻璃边缘那种压深的感觉，本来就该有 */
.van-tabbar::before {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    height: 20px;
    z-index: -1;
    pointer-events: none;
    background: linear-gradient(180deg,
        rgba(255, 255, 255, 0) 0px,
        rgba(255, 255, 255, .95) 4px,
        rgba(255, 255, 255, .95) 6px,
        rgba(14, 16, 28, .055) 8px,
        rgba(14, 16, 28, .025) 11px,
        rgba(255, 255, 255, .22) 14px,
        rgba(255, 255, 255, 0) 20px);
    /* 左右两端淡出。胶囊改成 overflow:visible 之后不再裁这条亮带，
       而它是 left:0/right:0 拉满的，两端会从圆角外面探出两个小尖角。
       用 mask 把最后 26px 化掉，正好落在圆角那一段上，视觉上就收进去了。
       比给这条加 border-radius 稳：它只有 16px 高，而胶囊圆角是 30，
       CSS 会把放不下的圆角按比例压缩，压出来跟胶囊的曲线对不上，还是会露尖 */
    -webkit-mask-image: linear-gradient(90deg,
        transparent 0, #000 26px, #000 calc(100% - 26px), transparent 100%);
    mask-image: linear-gradient(90deg,
        transparent 0, #000 26px, #000 calc(100% - 26px), transparent 100%);
    -webkit-filter: url(#h5-glass-refract);
    filter: url(#h5-glass-refract);
}

/* 原来这里给图标打了一道 1px 白 drop-shadow、给文字打了白 text-shadow，
   那是未选中色还是 #8b8b8b、白纱只压到 .78 那一阵的补丁 —— 浅灰描边压在深色商品图上
   确实需要白边勾形。现在两处前提都没了：未选中已经压深到 #33333a，字那一排的纱压回 .88，
   底下是均匀的浅玻璃。白高光打在浅玻璃上等于没打，反而让描边图标边缘发虚、
   文字像带一圈脏边，所以撤掉 */

/* 点下去那一下：图标和文字一起往下沉并被压扁一点，松手用同一条过冲曲线弹回来。
   原来只有图标动，文字钉在原地，看着像图标从字上滑走了而不是整颗陷下去 */
.van-tabbar-item .tab-icon,
.van-tabbar-item .van-tabbar-item__text {
    transition: transform .34s cubic-bezier(.34, 1.56, .64, 1);
}

.van-tabbar-item:active .tab-icon {
    /* .86 得乘上每个图标自己的校准倍率，不能直接写 scale(.86)：
       那样一按下去，四个图标就都回到同一个大小，之前按绘制面积做的校准全丢了，
       松手再弹回来又会跳一下 */
    transform: translateY(2px) scale(calc(var(--tab-s, 1) * .86));
}

.van-tabbar-item:active .van-tabbar-item__text {
    transform: translateY(1px) scale(.94);
}

/* ---------- 选中态：一颗会在格与格之间流动的水滴 ----------
   上一版是每格各自挂一个 ::before/::after 圆斑、靠 opacity 淡入淡出 ——
   视觉上等于"选中态在原地长出来"，没有方向、没有过程，和水没关系。

   现在换成一颗真的会移动的实体，两层分开：
     · 外层 .tab-fluid 只负责位移，宽度正好一格，translateX 走格位
     · 内层 <i> 负责形变，换格时重建节点、重播一段 keyframes
   分开的好处是位移和形变各用各的时间函数，不用在一个 transform 里挤两条曲线。 */
.van-tabbar-item {
    position: relative;
    /* 抬一层，压住下面那颗水滴。水滴在 DOM 里排在 items 之前，
       但它有 position:absolute，不写 z-index 的话会盖在静态内容上面 */
    z-index: 1;
}

.tab-fluid {
    position: absolute;
    top: 0;
    /* left 要跟着胶囊的左右内边距走，宽度也得把它扣掉。
       绝对定位子元素的 100% 参照的是**内边距盒**（366），而四个 item 排在
       **内容盒**（354）里 —— 直接写 left:0 + width:100%/4 的话，
       水滴按 91.5 一格在走、图标按 88.5 一格在排，
       切到最后一格会错开 9px（实测切到「订单」停在 183 而不是 177） */
    left: 6px;
    height: 100%;
    width: calc((100% - 12px) / var(--tab-n, 4));
    pointer-events: none;
    z-index: 0;
    transform: translateX(calc(var(--tab-i, 0) * 100%));
    /* 尾端 1.06 的过冲：走到位之后轻微越过再弹回来，这一下就是"停稳"的手感。
       .4s 和你给的 400ms 对齐 */
    transition: transform .4s cubic-bezier(.32, .72, .24, 1.06);
}

.tab-fluid i {
    position: absolute;
    top: 50%;
    left: 50%;
    /* 84 × 56：胶囊本身 60 高，上下各留 2px，所以这颗水滴整个装在胶囊里面。
       之前是 68 高、上下各鼓出 4px，结果底沿下面挂出一小截白色，
       压在深色背景上像个白兜 —— 那截就是你说要去的"白色底"。
       液态感改由"移动时的拉长 + 变扁"来表达，不再靠静态溢出 */
    width: 84px;
    height: 56px;
    margin: -28px 0 0 -42px;
    border-radius: 50%;
    /* 缩放原点放在"后沿"那一侧：往右切时原点在左缘，放大只朝右长，
       于是前沿先探出去、后沿慢半拍跟上；往左切时 dir=-1，原点翻到右缘 */
    transform-origin: calc(50% - var(--tab-dir, 1) * 50%) 50%;
    /* 水滴本体不用白色。原来这层是白晕（中心 .82），撞上两件事：
       一是底栏压在白色商品卡上时，栏是白的、水滴也是白的，椭圆直接看不见；
       二是那团白把选中那一格整个刷白，看着就是"底下一个白底"。
       改成"极淡的主题色玻璃"：中心 20% 主题色 → 边缘化开。
       白底上是一颗看得出的淡紫椭圆，深底上也还在（因为它是叠加层，不会把底盖死）。 */
    background: radial-gradient(ellipse closest-side at 50% 46%,
        rgba(255, 255, 255, .34) 0%,
        rgba(255, 255, 255, .18) 46%,
        rgba(255, 255, 255, 0) 82%);
    box-shadow: 0 3px 12px rgba(0, 0, 0, .11),
                inset 0 1px 1px rgba(255, 255, 255, .9);
    /* :key 绑 tabIndex，换格就重建这个节点，重建就把这段 keyframes 重播一遍。
       不需要额外的 JS 状态，也不会和上一次的动画互相干扰 */
    animation: tab-fluid-flow .4s cubic-bezier(.32, .72, .24, 1.06) both;
}

@keyframes tab-fluid-flow {
    0% {
        transform: scaleX(1) scaleY(1);
        opacity: 1;
    }
    /* 起步那一下：被拉长、同时压扁。34% 处的 1.22 是"水流出去"最明显的瞬间 */
    34% {
        transform: scaleX(1.22) scaleY(.90);
        opacity: 1;
    }
    /* 到位：缩过头一点点，靠位移那条曲线尾端的过冲一起收回来 */
    72% {
        transform: scaleX(.95) scaleY(1.02);
        opacity: .55;
    }
    /* 落定就淡干净。这颗水滴本来一直在，只是先前被 Vant 那层不透明白底盖住看不见；
       白底一撤它就直接露成一颗停在图标底下的淡紫椭圆 —— 那是定过要避开的"底板"。
       所以液态只保留在切换这 .4s 里（形变+位移都还在），停稳后栏上只剩玻璃材质
       和主题色的图标文字，选中靠变色表达，不靠垫一块 */
    100% {
        transform: scaleX(1) scaleY(1);
        opacity: 0;
    }
}

/* 水滴的主体颜色在这层：一层主题色的实心填充，靠遮罩把边化开。
   为什么非它不可：下面那层白色晕圈只负责"玻璃的反光"，本身没有形状；
   真正让椭圆被看见的是这一层的色。原来只给 12%，压在白底栏上几乎看不出 ——
   提到 22% 之后白底上是一颗清楚的淡紫椭圆，深底上也不会脏。
   用 background-color + opacity 而不是 color-mix()：后者在 iOS 16.2 以下不认，
   一写整条 background 就废了 */
.tab-fluid i::after {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: inherit;
    background: var(--main-color);
    opacity: .32;
    /* 实心填充天生是硬边圆盘（主题色没法带 alpha 写进渐变），
       所以这一层必须自己把边化开，64%→97% 淡出去 */
    -webkit-mask-image: radial-gradient(ellipse closest-side at 50% 46%,
        #000 0%, #000 64%, rgba(0, 0, 0, 0) 97%);
    mask-image: radial-gradient(ellipse closest-side at 50% 46%,
        #000 0%, #000 64%, rgba(0, 0, 0, 0) 97%);
}

/* 新选中的那颗图标：先缩下去，再弹过头，最后落回。
   和水滴用同一条过冲曲线，让"水滴停稳"和"图标弹回"几乎同时收尾 */
.van-tabbar-item--active .tab-icon {
    animation: tab-icon-pop .4s cubic-bezier(.34, 1.56, .64, 1) both;
}

@keyframes tab-icon-pop {
    0% {
        transform: scale(calc(var(--tab-s, 1) * .68));
    }
    55% {
        transform: scale(calc(var(--tab-s, 1) * 1.12));
    }
    100% {
        transform: scale(var(--tab-s, 1));
    }
}

/* 圆斑要压在图标和文字底下，所以把它们抬一层。
   不能给圆斑写 z-index:-1 —— 那会跑到整条栏的背景后面去，等于没有 */
.van-tabbar-item__icon,
.van-tabbar-item__text {
    position: relative;
    z-index: 1;
}

/* 不支持 backdrop-filter 的内核（老版 X5 之类）会「透但不糊」，商品图直接从字底下穿过去，
   比原来那块纯白底难读得多：退回实色白底 */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    .van-tabbar {
        --van-tabbar-background: #fff;
        /* 原来是 `0 -1px 0` 一条通栏式的上沿分隔线 —— 那是栏还贴着屏幕三边时的写法。
           现在这颗是浮在内容上的胶囊，四周都不挨边，只往上压一条线的话
           左右下三边和背景糊成一片，胶囊的轮廓直接看不出来。
           换成和玻璃版同一套四面发丝线 + 往下散投影，只是没有模糊层 */
        box-shadow: 0 0 0 .5px rgba(0, 0, 0, .08),
                    0 2px 6px rgba(0, 0, 0, .07),
                    0 10px 28px rgba(0, 0, 0, .18);
    }

    /* 折射那道高光带也得跟着撤：它是掰给"玻璃边缘"用的，垫在一块实色白底上
       只剩一条发灰的横杠，看着像栏上蹭了一道脏 */
    .van-tabbar::before {
        display: none;
    }
}

.van-tabbar-item__icon .tab-icon {
    width: 24px;
    height: 24px;
}

/* ================= 全站统一玻璃材质 =================
   客服球和底栏那两批定的尺子，这一批铺到剩下所有"钉住层"。
   判据还是那一条：这一层背后有没有东西滚得过去。有 —— 才配糊；
   普通白卡片躺在页面灰底上、底下没东西滚，糊上去只是给自己蒙一层灰，所以一张不动。
   收进来的全是 fixed / sticky 的钉住层，加两颗悬浮球和返回胶囊。

   材质只分两种朝向。玻璃的"读"靠边上那道亮线再加外面一道暗发丝，
   而这两条要落在"内容钻进去的那一边"：钉在标题栏下面的条，内容从它下面走过去，接缝在下沿；
   钉在屏幕下沿的结算栏，内容从它上面走过去，接缝在上沿。
   朝反了就成了"内容压在玻璃上"，看着像栏自己脏了一圈 */
.home-cate-bar,
.cate-main .sort-bar,
.order-tabs,
.qi-tabs,
.qi-bar,
.ml-ghead,
.ds-filter {
    /* 纱比底栏薄一档：底栏 60px 高、里头只有图标和字，纱得压到 .94 才盖得住首页那块深色 banner；
       这些条只有 44px 上下，厚了就又变成一条白带子，玻璃味反而没了。
       色标一样用绝对 px：这些条里有几块带 env() 安全区，百分比会被拉匀 */
    background: linear-gradient(180deg,
        rgba(250, 251, 254, .72) 0px,
        rgba(250, 251, 254, .82) 12px);
    /* saturate 跟底栏同一个 150%：这条尺是量出来的 —— 180% 摊成一整条横栏，
       会把商品图的红和青放大成两道脏痕，只有小圆球上才吃得下 175% */
    -webkit-backdrop-filter: blur(18px) saturate(150%) brightness(1.08);
    backdrop-filter: blur(18px) saturate(150%) brightness(1.08);
    /* 上沿那道亮边是拿来跟顶栏分家的：首页那两条玻璃是叠着的（顶栏 + 分类吸顶条），
       没有各自一条亮边，滚起来看着像一块 */
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .8),
                inset 0 -1px 0 rgba(255, 255, 255, .5),
                0 0 0 .5px rgba(0, 0, 0, .05),
                0 8px 18px -10px rgba(0, 0, 0, .2);
}

/* 首页那条吸顶条原来自己有一条实底灰线，正好横在玻璃和内容之间，交给 box-shadow 打 */
.home-cate-bar {
    border-bottom: 0;
}

/* Vant 把 van-tabs 的那排标签底色挂在 --van-tabs-nav-background 上，默认吃 --van-background-2（#fff）。
   不撤成透明，糊的永远只是它外面那层，标签底下仍然是一块不透明白 ——
   跟底栏选中格那块白色长方形是同一个坑，只不过这次铺在整排上 */
.order-tabs,
.qi-tabs {
    --van-tabs-nav-background: transparent;
}

/* 屏幕下沿那两条：快捷开票的结算栏、详情页和订单页的底部操作条。
   纱和模糊挂在 ::before 上，不写在条自己身上——理由跟顶栏那条一模一样：
   带 backdrop-filter 的盒子会给它底下所有子孙立一道 backdrop root，
   而这两条里坐的正是「立即下单」「咨询客服」「申请退款」「去开票」那几颗主操作钮。
   纱留在条自己身上时，实测它们那 12px 的模糊只能糊到这条自己的白纱，等于没糊 */
.qi-foot,
.van-action-bar {
    /* 底色交给 Vant 自己的变量（操作条）和 ::before 那层纱，元素身上不留实底，
       否则那层不透明的白会盖在纱和内容之间 */
    background: none;
}

.qi-foot::before,
.van-action-bar::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    /* .62/.72：原来是 .78/.86，实测压在这条底下的那颗主钮开关自己那层模糊，
       整张图最大只差 2/255 —— 因为纱太浓，商品图的颜色根本上不来，钮糊什么都是白糊。
       退两档之后底下的颜色真的能透进这一条里，钮的半透明才有东西可透 */
    background: linear-gradient(180deg,
        rgba(250, 251, 254, .62) 0px,
        rgba(250, 251, 254, .72) 14px);
    -webkit-backdrop-filter: blur(20px) saturate(150%) brightness(1.1);
    backdrop-filter: blur(20px) saturate(150%) brightness(1.1);
    /* 三条边跟着纱一起挪到这一层： inset 亮线要压在纱上、外扩那道软影要打到条外面的页面上 */
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .85),
                inset 0 -1px 0 rgba(255, 255, 255, .4),
                0 0 0 .5px rgba(0, 0, 0, .06),
                0 -8px 18px -10px rgba(0, 0, 0, .2);
}

.qi-foot {
    border-top: 0;
}

.van-action-bar {
    --van-action-bar-background: transparent;
    /* Vant 还给操作条里每个图标格子挂了一块不透明白（同样是 --van-background-2）。
       不撤的话，详情页「首页」那一格会在玻璃上顶出一个直角白块，
       跟底栏那颗白色长方形一模一样 —— 已经踩过一次，这次提前拆掉。
       按下去的反馈色留着不动，它是压在透明格子上的一层浅黑，不碍事 */
    --van-action-bar-icon-background: transparent;
}

/* 详情页的锚点条是浮在灰底上的一条圆角白卡，四周都不挨边，
   所以亮边得绕一圈，不能只打下沿那条 */
.detail-anchor {
    background: linear-gradient(180deg,
        rgba(252, 253, 255, .66) 0px,
        rgba(252, 253, 255, .8) 22px);
    -webkit-backdrop-filter: blur(16px) saturate(150%) brightness(1.08);
    backdrop-filter: blur(16px) saturate(150%) brightness(1.08);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .85),
                inset 0 0 0 1px rgba(255, 255, 255, .35),
                0 0 0 .5px rgba(0, 0, 0, .05),
                0 6px 16px -8px rgba(0, 0, 0, .2);
}

/* ================= 按钮这一族 =================
   上面那批铺的是"钉住层"，这一批铺能按的东西。分三档，判据还是那一条 ——
   背后有没有东西滚得过去，有才配糊：
   ① 主操作那颗（提交 / 登录 / 立即下单 / 咨询客服 / 申请退款 / 去评价 / 接单 / 发送 / 获取验证码）。
      原来是一整块不透明的色，压在已经玻璃化的结算栏上像贴了张不干胶。
      这一族不管出自哪里 —— 行内 color 属性的实底、app.css 改过色的 Vant 原生蓝、
      还是操作条那两颗 Vant 语义橙/红 —— 底下都有东西滚得过去，所以每一颗都给 blur。
   ①b 卡片右下角那一族（去下单 / 领取）：同一份材质，唯一差别是不挂 blur，
      因为它们躺在白卡片里，背后只有卡片自己的白。
   ② 描边那颗（返回登录 / 问问客服 / 编辑）——Vant 给 plain 挂了 --van-button-plain-background，
      默认整块 #fff，跟底栏选中格、tabs 那排白底是同一个坑。
   ③ Vant 默认那颗（--van-button-default-background 同样吃 #fff）。
   ②③ 只拆白底、补薄纱和一圈发丝，不挂 blur：理由同 ①b。

   色片一律挂在 ::after 上，不能挂 ::before —— Vant 自己把 ::before 占了当按压遮罩
   （.van-button:active:before{opacity:.1}），抢过来点一下就变黑块。全站没用 hairline 按钮，
   ::after 才是空的。
   每档各自把"它本来该是什么色"写进 --gfill，负层只认这一个变量，
   于是一族的材质只有一份声明，换配色、加一档都不用再抄一遍色 */

/* ① 谁该是什么色。
   第一条认领的是 Vant 按 color 属性写进行内 style="" 的那一整族（实测：
   style="color: white; background: var(--main-gradient); border: 0px;"）。
   只认 background 这个关键字、不认颜色名：plain 那颗 Vant 行内只写 color 和 border-color、
   没有 background，天然被隔在外面；而后台以后换配色、加一颗别的颜色的钮，不用再回来补选择器。
   代价是色片拿不到行内那个值，只能按颜色再认领两条 —— 全站一共就这两种实底：
   主题实色（催单、保存图片）和那个注销红 */
.van-button[style*="background"],
.rc-empty .van-button,
.ms-d-btns .van-button--primary,
.kf-send {
    --gfill: var(--main-gradient);
}

.van-button[style*="main-color"] {
    --gfill: var(--main-color);
}

.van-button[style*="#ee0a24"] {
    --gfill: #ee0a24;
}

.van-action-bar-button--warning {
    --gfill: var(--van-action-bar-button-warning-color);
}

.van-action-bar-button--danger {
    --gfill: var(--van-action-bar-button-danger-color);
}

/* ① 材质本体。整组一个色值都不写死，后台换配色它跟着换。
   !important 是必须的：第一档那批的底色是行内样式，样式表里写多具体都压不动 */
.van-button[style*="background"],
.rc-empty .van-button,
.ms-d-btns .van-button--primary,
.kf-send,
.van-action-bar-button--warning,
.van-action-bar-button--danger {
    background: none !important;
    border-color: transparent !important;
    /* 色片在负一层，先让按钮自己成一个层叠上下文。不隔离的话它没有层叠上下文
       （Vant 只给了 position:relative，z-index 是 auto），负一层会掉到最近的祖先上下文里，
       被卡片自己的白底盖死 —— 底栏那次就是靠 z-index:-1 才没把图标文字一起折射 */
    isolation: isolate;
    -webkit-backdrop-filter: blur(12px) saturate(175%) brightness(1.05);
    backdrop-filter: blur(12px) saturate(175%) brightness(1.05);
    /* 亮边给两道：一道贴上沿、一道绕半圈。底色自己是渐变、没有边，
       少了这圈就还是一张贴纸，只是变淡了点 */
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .34),
                inset 0 0 0 .5px rgba(255, 255, 255, .22),
                0 6px 16px -8px rgba(31, 27, 46, .4);
    transition: transform .28s cubic-bezier(.34, 1.56, .64, 1), box-shadow .2s ease;
}

.van-button[style*="background"]::after,
.rc-empty .van-button::after,
.ms-d-btns .van-button--primary::after,
.kf-send::after,
.van-action-bar-button--warning::after,
.van-action-bar-button--danger::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    border-radius: inherit;
    background: var(--gfill);
    /* .84：再低主色就散了，主按钮得先认得出是主按钮；再高底下的东西透不上来，糊了等于没糊 */
    opacity: .84;
}

/* 按下去把浮起那层收掉，跟返回胶囊同一个过冲曲线。
   禁用那颗（获取验证码倒计时、发送中）不吃这一下 —— 它本来就不能按，缩一下像坏了 */
.van-button[style*="background"]:active:not(.van-button--disabled),
.rc-empty .van-button:active:not(.van-button--disabled),
.ms-d-btns .van-button--primary:active:not(.van-button--disabled),
.kf-send:active:not(.van-button--disabled),
.van-action-bar-button--warning:active:not(.van-button--disabled),
.van-action-bar-button--danger:active:not(.van-button--disabled) {
    transform: scale(.965);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .2),
                inset 0 0 0 .5px rgba(255, 255, 255, .14),
                0 2px 5px rgba(31, 27, 46, .2);
}

/* ①b 卡片右下角那一族：「去下单」「领取」「接单」「复制邀请码」。
    上一版整个漏掉，因为它们是手写的 <span>，一行 <van-button> 都不是，
    底色直接写在 app.css 自己身上 —— 一张卡片一颗，首页一屏就是十几颗。
   材质跟①同一份（拆实底、负一层色片、上缘亮边、按下缩一下），唯一差别是不挂 blur：
   实测过它们往上数到 html 一路没有第二个模糊层，可是它们躺在白卡片里，
   背后只有卡片自己的白，糊上去只会给自己蒙一层灰 */
.goods-buy,
.kf-head-buy,
.kf-goods-buy,
.cp-btn,
.dw-btn,
.iv-btn:not(.iv-plain) {
    --gfill: var(--main-gradient);
}

.kf-head-buy,
.kf-goods-buy {
    --gfill: var(--main-color);
}

.goods-buy,
.kf-head-buy,
.kf-goods-buy,
.cp-btn,
.dw-btn,
.iv-btn:not(.iv-plain) {
    position: relative;
    isolation: isolate;
    background: none !important;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .34),
                inset 0 0 0 .5px rgba(255, 255, 255, .2),
                0 4px 10px -6px rgba(31, 27, 46, .35);
    transition: transform .28s cubic-bezier(.34, 1.56, .64, 1), box-shadow .2s ease;
}

.goods-buy::after,
.kf-head-buy::after,
.kf-goods-buy::after,
.cp-btn::after,
.dw-btn::after,
.iv-btn:not(.iv-plain)::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    border-radius: inherit;
    background: var(--gfill);
    opacity: .84;
}

.goods-buy:active,
.kf-head-buy:active,
.kf-goods-buy:active,
.cp-btn:active,
.dw-btn:active,
.iv-btn:not(.iv-plain):active {
    transform: scale(.955);
}

/* ②③ 把 Vant 那两块不透明白底板拆掉。变量挂在 .van-button 上，
   ① 那一档自己写了 background:none !important，抢不到它 */
.van-button {
    --van-button-plain-background: rgba(255, 255, 255, .5);
    --van-button-default-background: rgba(255, 255, 255, .58);
}

/* 半透明白躺在白卡片上等于没底，所以补一圈发丝把它顶出来：
   描边那颗自己有 Vant 那圈主题色边框（行内 border-color），只需要内亮边 + 外发丝；
   默认那颗连边框都是浅灰，同样靠这圈阴影站住。
   :not([style*="background"]) 是把 ① 那批隔出去 —— 它们自己那圈亮边在后面，
   两档撞在一起的话谁在后面谁赢，读起来就是一堆说不清的边 */
.van-button--plain,
.van-button--default:not([style*="background"]) {
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .8),
                0 0 0 .5px rgba(31, 27, 46, .06),
                0 2px 6px -3px rgba(31, 27, 46, .16);
}

/* 不支持 backdrop-filter 的内核（老版 X5 之类）：这些条会"透但不糊"，
   商品图直接从字底下穿过去，比原来那块实底难读得多 —— 整套退回实色 */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    .home-cate-bar {
        background: #fff;
        border-bottom: 1px solid #f0f1f3;
        box-shadow: none;
    }

    .cate-main .sort-bar,
    .ml-ghead,
    .ds-filter {
        background: var(--page-bg, #f8f7f8);
        box-shadow: none;
    }

    .order-tabs,
    .qi-tabs,
    .qi-bar {
        background: #fff;
        box-shadow: none;
        --van-tabs-nav-background: #fff;
    }

    .detail-anchor,
    .qi-foot {
        background: #fff;
        box-shadow: none;
    }

    .qi-foot {
        border-top: 1px solid #f2f3f5;
    }

    .van-action-bar {
        background: #fff;
        box-shadow: none;
        --van-action-bar-background: #fff;
        --van-action-bar-icon-background: #fff;
    }

    /* 这两条的纱同样搬在 ::before 上，退化版一起撤掉，底色已经还回元素自己身上 */
    .qi-foot::before,
    .van-action-bar::before {
        display: none;
    }

    /* 顶栏的纱挪到 ::before 以后，退化版要撤的也是这一层：没有 blur 的 .72 白纱
       等于只透不糊，商品图直接从标题底下穿过去。底色还回元素自己身上，
       浓度照旧跟着 --nav-alpha 走（「我的」/「资金明细」贴顶那一屏要的是真透明，不是白） */
    .van-nav-bar {
        --van-nav-bar-background: rgba(252, 253, 255, calc(.94 * var(--nav-alpha, 1)));
    }

    .van-nav-bar::before {
        display: none;
    }

    .nav-back {
        background: #fbfbfd;
    }

    /* 主按钮那颗的色是画在 ::after 那层负色片上的，没有 blur 就只剩一块 .84 的淡渐变，
       比原来还虚 —— 撤掉色片，把底色还回元素自己。
       还色直接吃 --gfill：上面每档各自写好了自己该是什么色，这里不用再一家一家抄，
       操作条那两颗橙/红也不会被误还成主题紫。
       必须连这几条一起补 !important：它们身上的 background:none !important 是上面那档给的，
       而 .rc-empty / .ms-d-btns / .kf-send 自己的实底渐变写在 4700 多行、没加 !important，压不过去 */
    .van-button[style*="background"]::after,
    .rc-empty .van-button::after,
    .ms-d-btns .van-button--primary::after,
    .kf-send::after,
    .van-action-bar-button--warning::after,
    .van-action-bar-button--danger::after,
    .goods-buy::after,
    .kf-head-buy::after,
    .kf-goods-buy::after,
    .cp-btn::after,
    .dw-btn::after,
    .iv-btn:not(.iv-plain)::after {
        display: none;
    }

    .van-button[style*="background"],
    .rc-empty .van-button,
    .ms-d-btns .van-button--primary,
    .kf-send,
    .van-action-bar-button--warning,
    .van-action-bar-button--danger,
    .goods-buy,
    .kf-head-buy,
    .kf-goods-buy,
    .cp-btn,
    .dw-btn,
    .iv-btn:not(.iv-plain) {
        background: var(--gfill) !important;
        box-shadow: none;
    }

    .van-button {
        --van-button-plain-background: #fff;
        --van-button-default-background: #fff;
    }

    .van-button--plain,
    .van-button--default:not([style*="background"]) {
        box-shadow: none;
    }
}

/* ================= 大屏适配（平板 / 桌面） =================
   手机 H5 在宽屏下铺满会很难看（卡片被拉得很宽、一屏塞太多列），
   这里统一限制成 480px 居中，模拟手机视口，固定定位的元素也跟着居中 */
@media (min-width: 600px) {
    body {
        background: #e8eaed;
    }

    #app {
        max-width: 480px;
        margin: 0 auto;
        min-height: 100vh;
        background: var(--page-bg);
        box-shadow: 0 0 24px rgba(0, 0, 0, .08);
    }

    .van-tabbar,
    .van-nav-bar,
    .van-action-bar,
    /* 首页那条分类吸顶条也是 fixed + left/right 撑满的，不跟着收窄就会横穿整屏 */
    .home-cate-bar {
        width: 480px;
        max-width: 480px;
        left: 50%;
        right: auto;
        margin-left: -240px;
    }

    /* 胶囊底栏在宽屏要单独算：上面那条是按"通栏底栏收成 480"写的，
       给的是 width:480 + margin-left:-240。胶囊本身左右各要让 12px，
       所以宽度是 480-24=456，居中偏移也跟着变成 -(456/2)=-228。
       不写这条的后果实测过：上面那条的 margin-left:-240 会把整颗胶囊再往左推，
       900px 视口下量出来 left 是 -228，右边空出 648，整条歪在屏幕外 */
    .van-tabbar.van-tabbar--fixed {
        width: 456px;
        max-width: 456px;
        left: 50%;
        right: auto;
        margin-left: -228px;
    }

    .fab-service {
        right: calc(50% - 240px + 14px);
    }

    .fab-top {
        right: calc(50% - 240px + 14px);
    }

    /* 底部弹出的那一堆（下单填信息、选券、评价、筛选、海报、长按菜单）Vant 一律按视口铺满，
       宽屏下同样会横穿整屏，跟着收成 480 居中。
       这里必须按上面那组的「50% 减一半」来：Vant 已经把 left 和 right 都写成 0，
       绝对定位元素三个都不自动时左右 auto 外边距会被直接归零，压根不居中（实测 left 还是 0）。
       也不能用 transform 居中：Vant 的上滑动画就是 transform: translateY(100%)，会被挤掉。
       只挑 bottom，居中型那两个（首页广告、领券）本来就自己居中，动 left 会把它们弄歪 */
    .van-popup--bottom,
    .van-action-sheet {
        width: 480px;
        max-width: 480px;
        left: 50%;
        right: auto;
        margin-left: -240px;
    }

    /* 邀请海报那个自己把宽度压到 420，负边距得跟着减半，不然偏 30。
       写在同一条媒体查询里，但要用两个类压过它 4287 行那条单类的 max-width */
    .van-popup--bottom.iv-poster-pop {
        max-width: 420px;
        margin-left: -210px;
    }

    /* 开票那屏底下那条「合计 / 去开票」在自己的规则里写死了 left:0、right:0 撑满视口，
       而那条排在 5183 行、比这里晚，单类对单类后写的赢 —— 只跟着上面那一组排进去，
       负边距照样被 left:0 顶着，整条甩到屏幕外（实测 left=-240）。这里加一层 .qi-page 压过它 */
    .qi-page .qi-foot {
        width: 480px;
        max-width: 480px;
        left: 50%;
        right: auto;
        margin-left: -240px;
    }
}

/* ---------- 在线客服聊天 ---------- */
/* 顶栏 van-nav-bar 是 fixed + 46px 占位，这里按它扣高度，
   列表自己滚、输入栏钉底，整页不再出现第二条滚动条。
   最后一行的 --kf-vh 是给 iOS 键盘用的：iOS 弹键盘时不改 dvh（只改 visualViewport），
   光写 dvh 的话键盘一起来输入栏就被盖住。客服页的 bindKb 会把可视高度写进这个变量，
   键盘收着时它不存在，自动回落到 dvh。注意不能用 100vh 兜底——地址栏展开时它会偏大 */
.kf-chat {
    display: flex;
    flex-direction: column;
    height: calc(100vh - 46px);
    height: calc(100dvh - 46px);
    height: calc(var(--kf-vh, 100dvh) - 46px);
}

/* 键盘弹出时 iOS 会把可视视口整体往下推（visualViewport.offsetTop 变正），
   而元素是按布局视口定位的 —— 不补这个偏移，整块内容在屏幕上就显示得偏高，
   输入栏会飘到屏幕上半截去（实测偏移 336px 时，输入栏底边落在屏幕 172px 处，
   键盘顶却在 508px，差了整整一个键盘的高度）。

   补法是整块 #app 跟着挪下来。放在 #app 上而不是单挪聊天区：顶栏也得跟着走，
   不然返回键会跑到屏幕外面。这个 class 只在客服页、且偏移非零时才挂上 ——
   transform 会让 #app 变成 fixed 元素的包含块，长页面上底栏会跟着一起滚走 */
body.kf-shift #app {
    transform: translateY(var(--kf-vt, 0px));
}

/* 顶部商品卡：贴着标题栏铺满整条，不留缝也不做圆角，当成聊天页的头部；
   图在左，右边三行——红色价格→名称→一句灰字，最右一个去下单 */
.kf-head {
    flex: none;
    display: flex;
    align-items: center;
    padding: 10px 12px;
    background: #fff;
    border-bottom: 1px solid #f0f1f3;
}

.kf-head-img {
    flex: none;
    width: 64px;
    height: 64px;
    margin-right: 10px;
    border-radius: 6px;
    object-fit: cover;
    background: #f4f5f7;
}

.kf-head-noimg {
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 22px;
    color: #c8c9cc;
}

.kf-head-c {
    flex: 1 1 auto;
    min-width: 0;
}

.kf-head-p {
    font-size: 12px;
    color: var(--text-sub);
}

.kf-head-p em {
    font-style: normal;
    font-size: 18px;
    font-weight: 700;
    color: var(--main-color);
}

.kf-head-p del {
    margin-left: 5px;
    color: #bbb;
}

.kf-head-u {
    margin-left: 2px;
}

.kf-head-t {
    margin-top: 4px;
    font-size: 13px;
    line-height: 1.3;
    color: var(--text-main);
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}

.kf-head-m {
    margin-top: 4px;
    font-size: 11px;
    color: var(--text-sub);
}

/* 顶部卡片最右边的主按钮：看详情是次要动作，下单才是这屏要人点的那一下 */
.kf-head-buy {
    flex: none;
    margin-left: 8px;
    padding: 6px 13px;
    border-radius: 15px;
    font-size: 13px;
    color: #fff;
    background: var(--main-color);
}

/* 消息区外面包一层，「回到底部」的把手才有地方贴着：
   输入框会随打字长高，把手绝对定位在滚动区里才不会被顶走 */
.kf-scroll {
    flex: 1 1 auto;
    min-height: 0;
    position: relative;
    display: flex;
    flex-direction: column;
}

.kf-msgs {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    padding: 6px 12px 10px;
}

.kf-tail {
    position: absolute;
    left: 50%;
    transform: translateX(-50%);
    bottom: 14px;
    display: flex;
    align-items: center;
    padding: 6px 11px;
    border-radius: 15px;
    background: #fff;
    box-shadow: 0 2px 10px rgba(0, 0, 0, .14);
    font-size: 12px;
    color: var(--text-main);
}

.kf-tail .van-icon {
    margin-right: 4px;
    font-size: 14px;
    color: var(--main-color);
}

/* 我发的最后一条下面标客服看没看：未读用主题色提一句，已读就压成灰字 */
.kf-read {
    margin-top: 3px;
    font-size: 10px;
    line-height: 1.2;
}

.kf-read.read {
    color: #b6b9be;
}

.kf-read.unread {
    color: var(--main-color);
}

/* 引用块：压在正文下面一小条灰底，点它滚到被引的那一条。
   fit-content 让它只包到文字宽度，我这边再靠 margin-left:auto 贴右，跟气泡对齐 */
.kf-qt {
    width: fit-content;
    max-width: 100%;
    margin-top: 4px;
    padding: 4px 7px;
    border-radius: 5px;
    background: rgba(0, 0, 0, .05);
    color: #888;
    font-size: 11px;
    line-height: 1.4;
    text-align: left;
    word-break: break-all;
}

.kf-line.mine .kf-qt {
    margin-left: auto;
}

/* 撤回的那条收成一行居中灰字，和插时间那行同一副样子 */
.kf-recall {
    margin: 8px auto 0;
    text-align: center;
    font-size: 11px;
    color: var(--text-sub);
}

/* 待发引用条：和截图先进输入框同一套路，带叉叉能撤 */
.kf-quote {
    display: flex;
    align-items: center;
    margin-bottom: 6px;
    padding: 5px 8px;
    border-left: 3px solid var(--main-color);
    border-radius: 4px;
    background: #f4f5f7;
    font-size: 12px;
    color: #666;
}

.kf-quote-t {
    flex: 1 1 auto;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}

.kf-quote-x {
    flex: 0 0 auto;
    margin-left: 8px;
    padding: 2px;
    color: #b6b9be;
    font-size: 14px;
}

/* 点引用条跳到原文上闪一圈，让人看得出跳的是哪条 */
.kf-flash .kf-bubble,
.kf-flash .kf-img,
.kf-flash .kf-video,
.kf-flash .kf-goods {
    animation: kfFlash 1.2s ease;
}

@keyframes kfFlash {
    0%, 100% { box-shadow: 0 0 0 0 rgba(0, 0, 0, 0); }
    30% { box-shadow: 0 0 0 3px var(--main-color); }
    65% { box-shadow: 0 0 0 3px rgba(0, 0, 0, 0); }
}

.kf-time {
    text-align: center;
    font-size: 11px;
    color: var(--text-sub);
    padding: 10px 0 2px;
}

/* 客服正在敲字：长在下一条气泡该出现的位置，靠左和客服那侧对齐。
   这东西三秒就没用了，不做重样式，三个点错开轮动就够看出「在动」；
   用主题色而不是灰字，灰了看着像没接上 */
.kf-ty {
    display: flex;
    align-items: center;
    margin-top: 8px;
    font-size: 11px;
    color: var(--main-color);
}

.kf-ty i {
    display: inline-block;
    width: 4px;
    height: 4px;
    margin-left: 3px;
    border-radius: 50%;
    background: var(--main-color);
    animation: kfTy 1s infinite ease-in-out;
}

.kf-ty i:nth-of-type(2) {
    animation-delay: .18s;
}

.kf-ty i:nth-of-type(3) {
    animation-delay: .36s;
}

@keyframes kfTy {
    0%, 60%, 100% {
        transform: translateY(0);
        opacity: .35;
    }
    30% {
        transform: translateY(-3px);
        opacity: 1;
    }
}

.kf-line {
    display: flex;
    margin-top: 8px;
}

.kf-line.mine {
    flex-direction: row-reverse;
}

/* 方中带圆，和后台客服面板一个形状；色块靠 line-height 把图标居中 */
.kf-av {
    flex: none;
    width: 32px;
    height: 32px;
    border-radius: 6px;
    margin-right: 8px;
    overflow: hidden;
    object-fit: cover;
    text-align: center;
    line-height: 32px;
    font-size: 16px;
    color: #fff;
}

.kf-line.mine .kf-av {
    margin-right: 0;
    margin-left: 8px;
}

/* 气泡/图片/商品卡共用的这一栏，宽度挪到这里限，里面的组件就不用各自算百分比 */
.kf-body {
    max-width: 78%;
    min-width: 0;
}

.kf-line.mine .kf-body {
    text-align: right;
}

/* 谁说的这句：小灰字贴在气泡顶上，靠哪边跟着 .kf-body 的对齐走 */
.kf-who {
    margin: 0 2px 3px;
    font-size: 11px;
    line-height: 1.2;
    color: var(--text-sub);
}

/* 人工/自动：自动回复是系统代发的问候，不该让人以为是客服真人回的，所以单独标出来 */
.kf-tag {
    display: inline-block;
    margin-left: 4px;
    padding: 0 4px;
    border-radius: 2px;
    font-size: 10px;
    line-height: 15px;
    vertical-align: 1px;
}

.kf-tag.human {
    color: #2f7d5c;
    border: 1px solid #b9dcc9;
}

.kf-tag.auto {
    color: #9a6b00;
    border: 1px solid #f0dda8;
}

.kf-bubble {
    display: inline-block;
    max-width: 100%;
    text-align: left;
    padding: 8px 11px;
    border-radius: 10px;
    font-size: 14px;
    line-height: 1.5;
    word-break: break-word;
    white-space: pre-wrap;
    background: #fff;
    color: var(--text-main);
}

.kf-line.mine .kf-bubble {
    background: var(--main-gradient);
    color: #fff;
}

.kf-img {
    max-width: 180px;
    max-height: 180px;
    border-radius: 8px;
    display: block;
}

/* 视频气泡：宽度必须写死，不然 <video> 按原始分辨率铺，横屏那段能顶满一屏。
   黑底是缩放后留出来的边，#t=0.1 让浏览器拿第一帧当封面，不额外存图 */
.kf-video {
    width: 180px;
    max-height: 240px;
    border-radius: 8px;
    display: block;
    background: #000;
    object-fit: contain;
}

/* 客服发来的商品卡片：整张可点，点开就是这个商品的详情页 */
.kf-goods {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    max-width: 100%;
    padding: 10px;
    border-radius: 10px;
    background: #fff;
    box-shadow: 0 1px 3px rgba(0, 0, 0, .06);
}

.kf-goods-img {
    width: 54px;
    height: 54px;
    border-radius: 6px;
    object-fit: cover;
    margin-right: 9px;
    flex: none;
}

.kf-goods-info {
    flex: 1 1 auto;
    min-width: 0;
}

.kf-goods-t {
    font-size: 14px;
    color: var(--text-main);
    line-height: 1.35;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.kf-goods-p {
    margin-top: 4px;
    font-size: 12px;
    color: var(--text-sub);
}

.kf-goods-p em {
    font-style: normal;
    color: var(--main-color);
    font-size: 15px;
    font-weight: 700;
}

.kf-goods-p del {
    margin-left: 5px;
    color: #bbb;
}

/* 商品卡片底下这一行：左边「查看详情」是次要的看，右边「去下单」是要人点的 */
.kf-goods-foot {
    flex: 1 0 100%;
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-top: 8px;
    padding-top: 8px;
    border-top: 1px solid #f2f2f2;
}

.kf-goods-go {
    font-size: 12px;
    color: var(--text-sub);
}

.kf-goods-buy {
    padding: 4px 13px;
    border-radius: 14px;
    font-size: 12px;
    color: #fff;
    background: var(--main-color);
}

/* 客服发来的券卡片：和后台面板那张是同一个造型——左边色块写金额，右边名字加有效期，
   中间虚线当撕口，上下各咬一个半圆当票券的齿孔。卡片本身 overflow:hidden，
   所以伸出去的那半个圆会被裁成贴边的半圆，看着就像纸券上戳了个洞。
   底色块走 --main-gradient，和「我的优惠券」那页、下单选券那一张是同一套配色，
   后台换主题色时这三处一起换；描边用中性灰，免得主题色一换就对不上。
   券是直接进包的，卡片上不放领取按钮，只留一句「已放进你的券包」告诉他人已经拿到了 */
.kf-cp {
    position: relative;
    display: flex;
    align-items: stretch;
    overflow: hidden;
    text-align: left;
    border-radius: 8px;
    background: #fff;
    box-shadow: 0 1px 3px rgba(0, 0, 0, .06);
    line-height: 1.45;
}

.kf-cp .cp-l {
    flex: 0 0 74px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 10px 4px;
    background: var(--main-gradient);
    color: #fff;
}

.kf-cp .cp-money {
    font-size: 20px;
    font-weight: 700;
    white-space: nowrap;
}

.kf-cp .cp-money .u {
    font-size: 11px;
    font-weight: 400;
    margin-left: 1px;
}

.kf-cp .cp-min {
    font-size: 10px;
    margin-top: 2px;
    opacity: .92;
    white-space: nowrap;
}

.kf-cp .cp-r {
    flex: 1 1 auto;
    min-width: 0;
    padding: 9px 10px;
    border-left: 1px dashed #e6e4e6;
}

.kf-cp .cp-name {
    font-size: 13px;
    font-weight: 600;
    color: var(--text-main);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.kf-cp .cp-line {
    font-size: 11px;
    color: var(--text-sub);
    margin-top: 3px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.kf-cp .cp-got {
    font-size: 11px;
    color: var(--main-color);
    margin-top: 5px;
    white-space: nowrap;
}

.kf-cp:before,
.kf-cp:after {
    content: "";
    position: absolute;
    left: 67px;
    width: 14px;
    height: 14px;
    border-radius: 50%;
    background: var(--page-bg);
    z-index: 2;
}

.kf-cp:before {
    top: -7px;
}

.kf-cp:after {
    bottom: -7px;
}

.kf-bar {
    flex: none;
    background: #fff;
    border-top: 1px solid #ededed;
    padding: 6px 10px calc(6px + constant(safe-area-inset-bottom));
    padding: 6px 10px calc(6px + env(safe-area-inset-bottom));
}

.kf-bar-top {
    display: flex;
    justify-content: space-between;
    font-size: 11px;
    color: var(--text-sub);
    padding-bottom: 6px;
}

.kf-end {
    color: var(--main-color);
}

/* 常用语：横着滑的一排短句，只占一行高，不该把聊天区挤没。
   点一下是把话填进输入框，访客还能接着改，所以样式做轻一点，别像按钮那样抢发送的风头 */
.kf-quick {
    display: flex;
    flex-wrap: nowrap;
    overflow-x: auto;
    padding-bottom: 6px;
    margin-bottom: 2px;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
}

.kf-quick::-webkit-scrollbar {
    display: none;
}

.kf-quick-i {
    flex: none;
    max-width: 60%;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    padding: 5px 11px;
    margin-right: 6px;
    border-radius: 13px;
    background: #f4f5f7;
    font-size: 12px;
    color: #5f6368;
}

.kf-quick-i:active {
    background: #e8eaed;
    color: var(--main-color);
}

.kf-in {
    display: flex;
    /* 输入框会长高，图片和发送钉在底边才不会跟着飘 */
    align-items: flex-end;
}

.kf-photo {
    flex: none;
    font-size: 22px;
    color: var(--text-sub);
    width: 30px;
    /* 和发送键同高：整行落在同一条基线上，热区也一起顶到 44px。
       原来 32px 比输入框矮一截，手指点在图标上沿会落到输入框里去 */
    height: 44px;
    line-height: 44px;
    text-align: center;
    margin-left: 2px;
}

/* 表情按钮：展开的时候自己变主题色，一眼看得出现在开的是哪一面 */
.kf-emo-btn {
    flex: none;
    font-size: 22px;
    color: var(--text-sub);
    width: 30px;
    height: 44px;
    line-height: 44px;
    text-align: center;
    margin-left: 2px;
}

.kf-emo-btn.on {
    color: var(--main-color);
}

.kf-emo {
    border-top: 1px solid #f0f0f0;
    margin-top: 6px;
    padding-top: 6px;
}

.kf-emo-tabs {
    display: flex;
    padding: 0 2px 6px;
}

.kf-emo-tab {
    font-size: 12px;
    color: var(--text-sub);
    padding: 2px 10px;
    border-radius: 10px;
    margin-right: 6px;
}

.kf-emo-tab.on {
    color: var(--main-color);
    background: var(--page-bg);
    font-weight: 600;
}

.kf-emo-grid {
    display: grid;
    /* 8 列，一屏 5 行多一点，超出就在面板里滚，不把输入区顶出屏幕 */
    grid-template-columns: repeat(8, 1fr);
    max-height: 148px;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
}

.kf-emo-i {
    font-size: 22px;
    line-height: 1.7;
    text-align: center;
    border-radius: 6px;
    cursor: pointer;
}

.kf-emo-i:active {
    background: #eef0f3;
}

.kf-emo-foot {
    display: flex;
    justify-content: flex-end;
    padding: 4px 2px 2px;
}

.kf-emo-act {
    font-size: 12px;
    color: var(--text-sub);
    padding: 3px 10px;
    margin-left: 8px;
    background: var(--page-bg);
    border-radius: 11px;
}

/* 后台配的「图片表情」那一栏：格子换成大图，面板高一点才看得全 */
.kf-emo-grid.wide {
    grid-template-columns: repeat(4, 1fr);
    grid-gap: 8px;
    max-height: 190px;
    padding: 2px 0;
}

.kf-emo-big {
    width: 100%;
    height: 56px;
    object-fit: contain;
    border-radius: 6px;
    background: #fff;
    cursor: pointer;
}

.kf-emo-big:active {
    opacity: .6;
}

/* 聊天里的大图表情：不套气泡底色，就是一张贴纸 */
.kf-sticker {
    max-width: 132px;
    max-height: 132px;
    display: block;
}

.kf-input {
    flex: 1;
    min-width: 0;
    /* 一行 44px 起手，打字往上长，到 104px 封顶改框内滚动（跟 JS 里的上限同一个数） */
    min-height: 44px;
    max-height: 104px;
    border: 1px solid #ededed;
    border-radius: 10px;
    padding: 11px 12px;
    font-size: 14px;
    line-height: 1.5;
    background: var(--page-bg);
    outline: none;
    resize: none;
    overflow-y: auto;
    box-sizing: border-box;
    display: block;
    font-family: inherit;
}

/* 发送按钮：和输入框单行高度对齐。
   Vant 的 size="small" 只给 32px 高、12px 字，搁在 45px 的输入框旁边，
   看着像贴上去的一小块标签，手指也不容易点准。
   这里显式顶到 44px（iOS 建议的最小点按尺寸），字号和圆角都跟输入框取同一个值，
   一横排看过去才是一套东西，而不是"输入框 + 一个附属小件" */
.kf-send {
    flex: none;
    margin-left: 8px;
    /* Vant 默认 primary 是蓝色，跟整站主题对不上，换成后台可配的渐变 */
    background: var(--main-gradient);
    border: none;
    color: #fff;
    height: 44px;
    min-width: 62px;
    padding: 0 16px;
    font-size: 14px;
    font-weight: 500;
    border-radius: 10px;
}

.kf-file {
    display: none;
}

/* ---------------- 我的消息（我的 → 我的消息） ----------------
   两个 tab 共用一套卡片样式：客服那半边沿用下面的 .rc-*，系统通知这半边是 .ms-* */
.msg-page {
    padding-bottom: 20px;
}

/* Vant 的 tabs 内容区默认贴边，卡片要缩进来才和别的页面一个观感 */
.msg-page .van-tabs__content {
    padding: 10px 12px 0;
}

.rc-loading {
    display: flex;
    justify-content: center;
    padding: 40px 0;
}

/* 系统通知的一条：左边一个未读点，右边复用 .rc-main 那三行 */
.ms-item {
    display: flex;
    align-items: flex-start;
    padding: 12px 14px;
    border-bottom: 1px solid #f2f3f5;
}

.ms-item:last-child {
    border-bottom: 0;
}

.ms-dot {
    flex: none;
    width: 7px;
    height: 7px;
    margin: 7px 9px 0 0;
    border-radius: 50%;
    background: #ee0a24;
}

/* 已读的只留一个浅浅的坑位：不再抢眼，但位置还占着，几行的左边缘才对得齐 */
.ms-dot.is-read {
    background: #e5e6eb;
}

.ms-more {
    padding: 14px 0 4px;
    text-align: center;
    font-size: 12px;
    color: var(--text-sub);
}

/* 未读条数 + 全部已读：和下面的列表并成同一张白卡，所以圆角只给上面两个角 */
.ms-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 9px 14px;
    background: #fff;
    border-radius: 12px 12px 0 0;
    border-bottom: 1px solid #f2f3f5;
}

.ms-bar + .rc-list {
    border-radius: 0 0 12px 12px;
}

.ms-bar-num {
    font-size: 13px;
    color: #ee0a24;
}

/* 详情弹层：正文可能挺长，弹层自己滚，别顶到屏幕外 */
.ms-popup {
    max-height: 78vh;
    overflow-y: auto;
}

.ms-detail {
    padding: 20px 18px calc(16px + env(safe-area-inset-bottom, 0px));
}

.ms-d-title {
    font-size: 17px;
    font-weight: 600;
    line-height: 1.45;
    color: var(--text-main);
}

.ms-d-time {
    margin-top: 6px;
    font-size: 12px;
    color: var(--text-sub);
}

.ms-d-body {
    margin-top: 14px;
    font-size: 14px;
    line-height: 1.7;
    color: var(--text-main);
    /* 后台正文里敲的换行要看得见，不然一段话挤成一大坨 */
    white-space: pre-wrap;
    word-break: break-word;
}

.ms-d-btns {
    margin-top: 22px;
}

.ms-d-btns .van-button + .van-button {
    margin-top: 10px;
}

.ms-d-btns .van-button--primary {
    background: var(--main-gradient);
    border: none;
    color: #fff;
}

.rc-list {
    background: #fff;
    border-radius: 12px;
    overflow: hidden;
}

.rc-item {
    display: flex;
    align-items: center;
    padding: 12px 14px;
    border-bottom: 1px solid #f2f3f5;
}

.rc-item:last-child {
    border-bottom: 0;
}

/* 左头像、右商品封面，中间那列才是会随内容伸缩的；两头都 flex:none 才不会被挤扁 */
.rc-av {
    flex: none;
    width: 36px;
    height: 36px;
    margin-right: 10px;
    border-radius: 6px;
    overflow: hidden;
}

.rc-av img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.rc-av .van-icon {
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 6px;
    color: #fff;
    font-size: 18px;
}

.rc-main {
    flex: 1;
    min-width: 0;
}

.rc-cover {
    flex: none;
    width: 46px;
    height: 46px;
    margin-left: 9px;
    border-radius: 6px;
    object-fit: cover;
}

.rc-row1 {
    display: flex;
    align-items: center;
}

.rc-title {
    flex: 1;
    min-width: 0;
    font-size: 14px;
    color: var(--text-main);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.rc-time {
    flex: none;
    margin-left: 8px;
    font-size: 11px;
    color: var(--text-sub);
}

.rc-row2 {
    display: flex;
    align-items: center;
    margin-top: 6px;
}

.rc-brief {
    flex: 1;
    min-width: 0;
    font-size: 13px;
    color: var(--text-sub);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* 最后一条被撤了：摘要只留一个占位，灰到不抢眼，别和正常摘要一个轻重 */
.rc-brief.is-recalled {
    color: #c8c9cc;
}

.rc-unread {
    flex: none;
    min-width: 17px;
    height: 17px;
    line-height: 17px;
    padding: 0 5px;
    margin-left: 8px;
    border-radius: 9px;
    background: #ee0a24;
    color: #fff;
    font-size: 11px;
    text-align: center;
    box-sizing: border-box;
}

.rc-row3 {
    display: flex;
    align-items: center;
    margin-top: 8px;
}

.rc-tag {
    font-size: 11px;
    line-height: 1;
    padding: 3px 6px;
    border-radius: 3px;
}

.rc-tag.ing {
    color: #07c160;
    background: rgba(7, 193, 96, .1);
}

.rc-tag.end {
    color: #8b8b8b;
    background: var(--page-bg);
}

.rc-count {
    margin-left: 8px;
    font-size: 11px;
    color: var(--text-sub);
}

.rc-empty {
    padding: 40px 0;
    text-align: center;
}

.rc-empty .van-button {
    margin-top: 8px;
    background: var(--main-gradient);
    border: none;
    color: #fff;
}

/* 「我的」里沟通记录那行的未读数，和 tab 上那个角标同一本账 */
.mc-badge {
    flex: none;
    min-width: 17px;
    height: 17px;
    line-height: 17px;
    padding: 0 5px;
    margin-right: 6px;
    border-radius: 9px;
    background: #ee0a24;
    color: #fff;
    font-size: 11px;
    text-align: center;
    box-sizing: border-box;
}

/* ---------- 切换分类的过渡 ----------
   换分类时不再把列表清空（那会让整页高度从 1300px 塌到 0 再弹回来，就是那下"闪"），
   而是立刻铺上同尺寸的骨架卡，数据到了整批淡入。 */
.sk-swap {
    /* 骨架卡和真实卡同高，保证前后两次布局完全对得上，不产生位移 */
    pointer-events: none;
}

/* 新一批卡片整体淡入，而不是逐张闪出来。
   这条是给"整格被换掉"的场景用的（分类页/搜索页首屏）。
   首页商品格不走这里——它改成了逐张入场，见下面的 .card-anim */
.goods-grid {
    animation: grid-in .22s ease-out;
}

@keyframes grid-in {
    from {
        opacity: .35;
    }
    to {
        opacity: 1;
    }
}

/* ---------- 首页商品卡：逐张入场 ----------
   滑到底加载更多时，只有新到的那几张走这段动画。已经在列表里的卡片
   因为 v-for 的 :key="g.id" 没变，会被 Vue 原地复用，DOM 节点和 <img>
   一个都不重建，所以不会再有"整列表闪一下"。

   改之前是给容器绑 :key="enterTick"：每加载一页 key 就换一次，Vue 把整个
   格子当成全新元素销毁重建，连带已经渲染好的卡片一起重来——
   容器重播 grid-in 整体淡入 + 所有图片丢掉 .img-loaded 重放骨架流光，
   两下叠在一起就是滑到底时看到的那下闪（实测整列表从 35% 透明度淡回，217ms）。 */
.card-anim {
    /* 容器自己不再整体淡入：改由每张卡片各淡各的。
       两层透明叠在一起比单层更暗，首屏会显得发灰 */
    animation: none;
}

.card-in-enter-from {
    opacity: 0;
    transform: translateY(10px);
}

.card-in-enter-active {
    transition: opacity .26s ease-out, transform .26s ease-out;
}

/* 列表只增不减（换分类时整块换成骨架屏，这棵子树直接整个卸载），
   离开不做动画，免得残留的占位元素把布局多撑一会儿 */
.card-in-leave-active {
    display: none;
}

/* 尊重系统的"减弱动态效果"设置 */
@media (prefers-reduced-motion: reduce) {
    .goods-grid,
    .sk {
        animation: none;
    }

    /* 入场过渡也一并关掉，只留最终状态 */
    .card-in-enter-active {
        transition: none;
    }
}

/* 区块标题右跟着转的小圈，表示"正在换分类" */
.section-title .title-loading {
    display: inline-flex;
    vertical-align: middle;
    margin-left: 6px;
}

/* ---------- 骨架屏 ----------
   四个主页面 + 订单列表首屏数据没回来时，按各页真实布局铺占位块。
   灰块自己闪，没用 van-skeleton：它的行高是固定的，对不上卡片和封面的实际尺寸。 */
.sk {
    display: block;
    border-radius: 4px;
    background: linear-gradient(90deg, #f0f1f3 25%, #e6e7e9 37%, #f0f1f3 63%);
    background-size: 400% 100%;
    animation: sk-flash 1.4s ease infinite;
}

@keyframes sk-flash {
    0% {
        background-position: 100% 50%;
    }
    100% {
        background-position: 0 50%;
    }
}

/* 首页：轮播 → 五个分类圆 → 双列商品卡 */
.sk-swipe {
    margin: 10px 12px 0;
    border-radius: 12px;
    aspect-ratio: 2.2 / 1;
}

.sk-cate-dot {
    width: 48px;
    height: 48px;
    margin: 0 auto;
    border-radius: 50%;
}

.sk-cate-txt {
    width: 36px;
    height: 12px;
    margin: 8px auto 0;
}

.sk-name {
    width: 100%;
    height: 36px;
}

/* 骨架卡里的封面占位：要跟真实封面长得一模一样，
   否则骨架看起来是"白框里嵌了个灰块"，而不是"卡片顶部的图"。
   真实封面是 object-fit:cover 铺满卡片上部，上面两角跟着卡片圆角走
   （.goods-card 有 overflow:hidden，所以这里写成 10px 10px 0 0 能对上）。
   .sk 默认的 4px 四角圆角要在这里覆盖掉。 */
.goods-img.sk {
    border-radius: 10px 10px 0 0;
}

.sk-price {
    width: 45%;
    height: 18px;
    margin-top: 6px;
}

/* 分类页左侧那一列分类 */
.sk-side {
    padding: 12px 0;
}

.sk-side-item {
    height: 20px;
    margin: 0 12px 26px;
}

/* 订单页：三张卡，占位块贴着真实卡片那三行 */
.sk-o-long {
    width: 45%;
    height: 14px;
}

.sk-o-short {
    width: 64px;
    height: 14px;
}

.sk-o-img {
    width: 40px;
    height: 40px;
    border-radius: 6px;
    flex: none;
    margin-right: 8px;
}

.sk-o-price {
    width: 72px;
    height: 20px;
}

/* 「我的」页：头像 + 两行字 + 余额 */
/* 顶部主题色卡上的骨架：灰块在色底上得翻成半透明白，不然看着像两块脏斑 */
.sk.sk-dark {
    background: linear-gradient(90deg, rgba(255, 255, 255, .22) 25%, rgba(255, 255, 255, .38) 37%, rgba(255, 255, 255, .22) 63%);
    background-size: 400% 100%;
}

.sk.mh-av {
    border-color: transparent;
}

.mh-sk-name {
    width: 96px;
    height: 17px;
}

.mh-sk-tag {
    width: 126px;
    height: 11px;
    margin-top: 11px;
}

.mh-sk-num {
    flex: 1;
    height: 32px;
    margin: 0 6px;
}

/* ---------- 优惠券 ----------
   券卡统一是「左边色块写优惠，右边写名称和有效期」的票券样式：
   下单弹层里的选券和「我的优惠券」那一页共用这一套，免得两处各写一版对不上。 */
.cp-cell-on .van-cell__value {
    color: var(--button-color);
    font-weight: 600;
}

/* 选了券之后那行「原价 / 已减 / 实付」 */
.cp-sum {
    display: flex;
    align-items: center;
    margin: 12px 16px 0;
    font-size: 13px;
}

.cp-origin {
    color: var(--text-sub);
    text-decoration: line-through;
}

.cp-off {
    color: var(--button-color);
    margin-left: 8px;
}

.cp-pay {
    margin-left: auto;
    font-weight: 600;
}

.cp-page {
    padding-bottom: 12px;
}

.cp-card,
.cp-item {
    display: flex;
    background: #fff;
    border-radius: 10px;
    margin-bottom: 10px;
    overflow: hidden;
}

.cp-card {
    box-shadow: 0 1px 6px rgba(0, 0, 0, .05);
    /* 券列表页的卡片是顶满容器的（.cp-page 只给了下边距），左右得自己让开 12px，
       和全站其它卡片一个口径；上面再让 10px，免得和 tab 那根指示线贴上 */
    margin: 10px 12px;
}

.cp-item {
    border: 1px solid #ebedf0;
    align-items: center;
}

/* 选中的那张：描边跟着主色走，不靠回填底色（底色一填就和「可用/不可用」混在一起） */
.cp-item.on {
    border-color: var(--main-color);
    background: #fff8f6;
}

.cp-list {
    max-height: 52vh;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    padding: 4px 12px 0;
}

.cp-left {
    flex: none;
    width: 88px;
    align-self: stretch;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 10px 4px;
    background: var(--main-gradient);
    color: #fff;
}

.cp-money {
    font-size: 17px;
    font-weight: 600;
    line-height: 1.15;
    text-align: center;
}

.cp-min {
    font-size: 11px;
    opacity: .92;
    margin-top: 3px;
}

.cp-mid {
    flex: 1;
    min-width: 0;
    padding: 10px 12px;
}

.cp-name {
    font-size: 14px;
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.cp-line,
.cp-exp,
.cp-note {
    font-size: 12px;
    color: var(--text-sub);
    margin-top: 4px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.cp-got {
    font-size: 11px;
    color: #c0c2c6;
    margin-top: 5px;
}

.cp-right {
    flex: none;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 0 12px;
}

.cp-cut {
    color: var(--button-color);
    font-size: 15px;
    font-weight: 600;
}

/* 用不了的券：整张压暗，右边不写「不可用」而是写清为什么 */
.cp-item.no {
    opacity: .55;
}

.cp-item.no .cp-left {
    background: linear-gradient(135deg, #c8c9cc, #a8abb0);
}

.cp-why {
    font-size: 11px;
    color: var(--text-sub);
    max-width: 92px;
    line-height: 1.35;
    text-align: right;
}

.cp-on {
    color: var(--main-color);
    font-size: 18px;
    margin-top: 4px;
}

.cp-btn {
    font-size: 12px;
    color: #fff;
    background: var(--main-gradient);
    padding: 5px 11px;
    border-radius: 12px;
}

/* ---------- 首页领券弹窗 ---------- */
.cp-pop {
    position: relative;
    width: 82vw;
    max-width: 340px;
    /* 券配得多也不是没完地往下堆：卡片区自己滚，标题和下面那个关闭钮一直在 */
    max-height: 76vh;
}

.cp-pop-box {
    background: #fff;
    border-radius: 12px;
    overflow: hidden;
    box-shadow: 0 6px 20px rgba(0, 0, 0, .18);
    padding: 0 12px 12px;
}

/* 顶部氛围带：整个弹窗只有这一条是主题色，下面全是白底密排信息——
   一眼先看到「有券领」，再看具体哪几张。左右各负 12px 是为了盖掉 .cp-pop-box 的内边距，
   让带子铺到弹窗边上，圆角由外层那个 overflow:hidden 顺手裁掉 */
.cp-pop-top {
    margin: 0 -12px 2px;
    padding: 14px 12px 15px;
    background: var(--main-gradient);
    color: #fff;
    text-align: center;
}

.cp-pop-tag {
    display: inline-block;
    padding: 2px 9px;
    border: 1px solid rgba(255, 255, 255, .55);
    border-radius: 9px;
    font-size: 10px;
    letter-spacing: 2px;
}

.cp-pop-head {
    font-size: 17px;
    font-weight: 700;
    text-align: center;
    padding: 7px 0 0;
}

.cp-pop-list {
    max-height: 44vh;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
}

/* 外面这层白卡已经让开 12px，弹窗里的券卡不用再背券列表页那套左右边距和投影 */
.cp-pop .cp-card {
    position: relative;
    margin: 10px 0 0;
    border: 0;
    border-radius: 8px;
    box-shadow: 0 1px 4px rgba(0, 0, 0, .07);
}

/* 票券的齿孔：圆心正落在左边色块那条边上，卡片本身 overflow:hidden，
   伸出去的那半个圆被裁成贴边的半圆，看着就是纸券上戳了个洞。
   底色跟弹窗一样是白的，所以咬在色块上的那半边才看得出来 */
.cp-pop .cp-card:before,
.cp-pop .cp-card:after {
    content: "";
    position: absolute;
    left: 81px;
    width: 14px;
    height: 14px;
    border-radius: 50%;
    background: #fff;
    z-index: 2;
}

.cp-pop .cp-card:before {
    top: -7px;
}

.cp-pop .cp-card:after {
    bottom: -7px;
}

/* 撕口：色块和右边信息之间那道虚线，只画在弹窗这几张上，
   「我的优惠券」那一页的卡片没让动 */
.cp-pop .cp-mid {
    border-left: 1px dashed #e6e4e6;
}

/* 金额当主角：数字放到 25px，¥ / 折 这种符号退回 12px 当配角 */
.cp-pop .cp-money {
    font-size: 25px;
    font-weight: 700;
    line-height: 1.1;
}

.cp-pop .cp-money .u {
    font-size: 12px;
    font-weight: 400;
    margin: 0 1px;
}

.cp-pop .cp-cap {
    font-size: 10px;
    opacity: .9;
    margin-top: 2px;
}

.cp-pop-tip {
    font-size: 11px;
    color: var(--text-sub);
    text-align: center;
    margin-top: 12px;
}

/* 领到手的那张：不涂底色也不摆灰字，主题色的勾加一句「已领取」，
   和旁边那个还能点的按钮一眼分得开 */
.cp-done {
    display: flex;
    align-items: center;
    gap: 2px;
    font-size: 12px;
    color: var(--main-color);
}

.cp-st {
    font-size: 12px;
    color: var(--text-sub);
}

/* 已使用 / 已过期：色块退成灰，能用的那张永远是彩色，扫一眼就分得出来 */
.cp-card.used .cp-left,
.cp-card.expired .cp-left {
    background: linear-gradient(135deg, #c8c9cc, #a8abb0);
}

.cp-card.used .cp-name,
.cp-card.expired .cp-name {
    color: var(--text-sub);
}

/* 订单详情：用了券的要把这一单的钱逐条摆全——原价、哪张券减了多少、实付。
   减额那行不加粗不染色，只加重实付款，免得满屏都是重点。 */
.od-bill {
    margin-top: 10px;
    padding-top: 10px;
    border-top: 1px dashed #ebedf0;
    font-size: 13px;
    line-height: 24px;
}

.od-bill-row {
    display: flex;
    justify-content: space-between;
    gap: 12px;
    color: var(--text-sub);
}

.od-bill-pay {
    color: var(--text-main);
    font-weight: 700;
}

/* ---------- 订单详情：退款进度 ---------- */
.rf-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
}

.rf-tag {
    font-size: 12px;
    line-height: 20px;
    padding: 0 8px;
    border-radius: 4px;
    font-weight: 600;
}

/* 四种申请态各给一种颜色，撤回也是会员自己动过的一步，不能拿灰底糊过去 */
.rf-tag.rf-pending, .order-refund.rf-pending {
    color: #d46b08;
    background: #fff7e6;
}

.rf-tag.rf-approved, .order-refund.rf-approved {
    color: #0a8f4e;
    background: #eaf9f1;
}

.rf-tag.rf-rejected, .order-refund.rf-rejected {
    color: #d03050;
    background: #fdeff2;
}

.rf-tag.rf-revoked {
    color: #1e6fd0;
    background: #eaf2fd;
}

.rf-money {
    font-size: 20px;
    font-weight: 700;
}

.rf-desc {
    margin-top: 8px;
    font-size: 13px;
    line-height: 1.6;
    color: var(--text-sub);
}

/* 后台驳回时写的那一句，单独一块红底：这是这屏唯一要会员读进去的话 */
.rf-note {
    margin-top: 8px;
    padding: 8px 10px;
    border-radius: 6px;
    background: #fdeff2;
    color: #d03050;
    font-size: 13px;
    line-height: 1.6;
}

.rf-line {
    display: flex;
    justify-content: space-between;
    gap: 12px;
    margin-top: 6px;
    font-size: 12px;
    color: var(--text-sub);
}

.rf-line span:last-child {
    color: var(--text-main);
    text-align: right;
    overflow-wrap: anywhere;
}

.rf-acts {
    display: flex;
    /* 操作靠右：左边留给人看的是「这一张是什么」，右边才是「我要动手」 */
    justify-content: flex-end;
    gap: 10px;
    margin-top: 12px;
}

/* 申请弹层：可退金额当主角，说明句跟在输入框下面 */
.rf-ask {
    display: flex;
    align-items: baseline;
    justify-content: center;
    gap: 8px;
    padding: 4px 0 14px;
}

.rf-ask-label {
    font-size: 13px;
    color: var(--text-sub);
}

.rf-ask-money {
    font-size: 26px;
    font-weight: 700;
    color: var(--main-color);
}

.rf-ask-tip {
    margin: 12px 16px 0;
    font-size: 12px;
    line-height: 1.7;
    color: var(--text-sub);
}

/* ---------- 开票：抬头类型与票种改成顶部两排 tab ---------- */
/* 这两组原来藏在按钮上方右下角那两枚「换一种」入口里，点了才弹选单二选一。
   现在两档直接摊在表单最上面，点一下就切，切完下面该出的框跟着变 */
.iv-tab-group {
    margin: 12px 16px 0;
}

.iv-tab-label {
    display: block;
    font-size: 13px;
    color: var(--text-sub);
}

.iv-tab-row {
    display: flex;
    gap: 8px;
    margin-top: 7px;
}

.iv-tab-i {
    flex: 1;
    min-width: 0;
    padding: 8px 10px;
    border: 1px solid #ebedf0;
    border-radius: 8px;
    background: #fff;
    font-size: 14px;
    line-height: 20px;
    color: var(--text-main);
    text-align: center;
}

/* 选中只把描边和字换成主题色，不填底色：这两枚是选择器不是按钮，填了色块就看不出哪个能点 */
.iv-tab-i.on {
    border-color: var(--main-color);
    color: var(--main-color);
    font-weight: 600;
}

.iv-tab-sub {
    display: block;
    font-size: 11px;
    font-weight: 400;
    line-height: 15px;
    color: var(--text-sub);
}

.iv-tab-i.on .iv-tab-sub {
    color: var(--main-color);
    opacity: .72;
}

/* 右下角那颗「用存的抬头」：整排 tab 摊开之后，这一屏只剩这一个入口 */
.iv-swap {
    display: flex;
    justify-content: flex-end;
    align-items: center;
    gap: 18px;
    margin: 10px 16px 0;
    font-size: 13px;
    color: var(--main-color);
}

.iv-swap-i {
    display: inline-flex;
    align-items: center;
    gap: 2px;
    padding: 2px 0;
}

.iv-swap-i .dk-icon {
    font-size: 11px;
}

/* 选单里那一行行选项：一行一件事，右边要么是当前用的那个勾，要么是一句补充说明 */
.iv-sheet-head {
    padding: 15px 16px 12px;
    font-size: 16px;
    font-weight: 600;
    text-align: center;
    color: var(--text-main);
}

.iv-sheet-tip {
    margin: -4px 16px 8px;
    font-size: 12px;
    line-height: 1.7;
    color: var(--text-sub);
}

.iv-sheet-o {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    padding: 14px 16px;
    border-top: 1px solid #f5f6f8;
    font-size: 14px;
    color: var(--text-main);
}

.iv-sheet-name {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.iv-sheet-sub {
    display: block;
    margin-top: 3px;
    font-size: 11px;
    font-weight: 400;
    color: var(--text-sub);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.iv-sheet-tag {
    flex: none;
    font-size: 11px;
    color: var(--main-color);
}

.iv-sheet-check {
    flex: none;
    font-size: 16px;
    color: var(--main-color);
}

.iv-sheet-add {
    padding: 14px 16px;
    border-top: 1px solid #f5f6f8;
    text-align: center;
    font-size: 14px;
    color: var(--main-color);
}

/* 开专票才出现的那四栏，上面垫一句为什么突然多了四个框 */
.iv-need-note {
    margin: 12px 16px 0;
    font-size: 12px;
    line-height: 1.7;
    color: var(--text-sub);
}

/* ---------- 开票资料（抬头簿） ---------- */
.ivt-card {
    background: #fff;
    border-radius: 10px;
    margin: 0 12px 10px;
    padding: 14px;
    box-shadow: 0 1px 6px rgba(0, 0, 0, .05);
}

/* 默认那条在左上角挂一个主题色小条，不用整行刷底色 */
/* 默认这一条不靠左边垫一条色标认：那条色标要把整张卡的文字往右挤 8px，
   跟下面几张对不上尺。改成抬头后面挂一个实心「默认」标 */
.ivt-name {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 15px;
    font-weight: 600;
    color: var(--text-main);
    overflow-wrap: anywhere;
}

.ivt-chip {
    flex: none;
    padding: 0 5px;
    border: 1px solid var(--main-color);
    border-radius: 4px;
    font-size: 11px;
    font-weight: 400;
    line-height: 16px;
    color: var(--main-color);
    background: none;
}

/* 「默认」是这一条的身份，用实心挑出来；旁边企业/可开专票只是属性，描边就够 */
.ivt-chip-def {
    background: var(--main-color);
    color: #fff;
}

/* 这几张卡片里的「标签—数值」行和右下角的操作条，用的就是开票记录那两把尺
   （.rf-line / .rf-acts），同一站里同一种行不该有两种字号 */
.ivt-tip {
    margin: 0 12px 10px;
    font-size: 12px;
    line-height: 1.7;
    color: var(--text-sub);
}

.ivt-add {
    margin: 16px 12px 24px;
}

/* ---------- 看票：电子发票 PDF ---------- */
/* 底部弹层里塞一个 iframe，浏览器自己渲染。iPhone 上的 Safari 不渲染内嵌 PDF，
   点了就是下载，这条路留着「新窗口」那枚按钮兜底，不做额外适配 */
.iv-pdf {
    display: flex;
    flex-direction: column;
    height: 78vh;
}

.iv-pdf-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex: none;
    padding: 14px 16px;
    font-size: 15px;
    font-weight: 600;
    color: var(--text-main);
}

.iv-pdf-t {
    font-size: 15px;
    font-weight: 600;
    color: var(--text-main);
}

/* 两颗钮靠间距分开，中间不再画一道竖线 */
.iv-pdf-bar-i {
    display: flex;
    align-items: center;
    gap: 14px;
    font-size: 13px;
    font-weight: 400;
}

.iv-pdf-bar-i a {
    color: var(--main-color);
    text-decoration: none;
}

.iv-pdf-frame {
    flex: 1;
    width: 100%;
    border: 0;
    background: #f2f3f5;
}

/* iframe 在 iPhone 上只会是个空白框，这一句是给那条路的出口 */
.iv-pdf-tip {
    flex: none;
    padding: 10px 16px 14px;
    font-size: 12px;
    line-height: 1.6;
    text-align: center;
    color: var(--text-sub);
}

/* ---------- 快捷开票（一键开完） ----------
   单子一行一张，卡片尺子就用下面「邀请好友」那一节的 .iv-card（开票三屏共用同一把尺）。
   全选那条钉在标签下面那一排：单子多了想回头改勾选，不用先滚回顶。
   这一屏的尺子只有两条：块的外沿一律 12，白底块里的字一律 26（12 + 卡片/输入框自己那 14）。
   抬头簿那个弹窗整屏按 16 排，所以这些收到 12 的规则全部锁在 .qi-page 里，不外溢。 */

/* 顶部「待开票 / 开票记录」那排标签：钉在标题栏下沿，跟订单页那排状态分类器同一个位置。
   底下垫 10px 页面灰再轮到下面的内容——两块白底直接贴一起，谁也是一整片，看不出标签已经走完；
   这条灰跟着标签一起钉住，卡片从它底下滚过去也露不出来（底色照页面灰，跟首页那条分类吸顶条一个做法） */
.qi-tabs {
    position: sticky;
    top: var(--van-nav-bar-height, 46px);
    z-index: 4;
    border-bottom: 10px solid var(--page-bg, #f8f7f8);
}

.qi-bar {
    position: sticky;
    /* 压在标签那条灰的下沿：顶栏 46 + 那一排标签 44 + 灰带 10 */
    top: calc(var(--van-nav-bar-height, 46px) + var(--van-tabs-line-height, 44px) + 10px);
    z-index: 3;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    padding: 10px 12px;
    font-size: 13px;
}

.qi-bar-sum {
    color: var(--text-sub);
}

.qi-bar-sum b {
    font-size: 15px;
    color: var(--text-main);
}

.qi-row {
    display: flex;
    align-items: center;
    gap: 10px;
    /* 描边用内阴影占位，不真加 1px 边框：加了边框这张卡比旁边那几张往外鼓一圈，
       卡里的字也从 26 顶到 27，跟下面「开票记录」那些卡对不上 */
    box-shadow: 0 1px 6px rgba(0, 0, 0, .05), inset 0 0 0 1px transparent;
}

/* 勾上的那张只把描边换成主题色，整行底色不刷：一排六张全上色，看不出到底挑了哪几张 */
.qi-row.on {
    box-shadow: 0 1px 6px rgba(0, 0, 0, .05), inset 0 0 0 1px var(--main-color);
}

.qi-row-main {
    flex: 1;
    min-width: 0;
}

.qi-row-name {
    font-size: 14px;
    line-height: 20px;
    color: var(--text-main);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.qi-row-sub {
    margin-top: 3px;
    font-size: 12px;
    line-height: 16px;
    color: var(--text-sub);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* 表单上面那一行小标题：全站 .card-title 那把尺是「卡片里 16」，这一屏它不在卡片里，
   是贴在 12 那条线上的组标题，所以左右内边距收到 12；上面那点缝交给上排单子自己那 10px 下边距 */
.qi-page .qi-form-title {
    padding: 12px;
    margin-top: 0;
}

/* 表单里那几排选择器、那句说明、右下角「用存的抬头」：原来按弹窗那把 16 的尺排的，
   挪进这一屏就跟着块外沿一起收到 12 */
.qi-page .iv-tab-group {
    margin: 12px 12px 0;
}

.qi-page .iv-swap {
    margin: 10px 12px 0;
}

.qi-page .rf-ask-tip {
    margin: 12px 12px 0;
}

/* 那一组输入框：外框贴 12，框内的字再往里 14 落到 26，和卡片里的字一条线 */
.qi-page .van-cell-group--inset {
    margin: 0 12px;
    --van-cell-horizontal-padding: 14px;
}

/* 钉顶那条右边的「重新选单」：第二步给他留一个退回去改勾选的口，只变主色不垫底板 */
.qi-bar-edit {
    font-size: 13px;
    color: var(--main-color);
}

/* 底下一步栏：跟购物车那条结算栏一个位置，合计常在、右边那颗钮按步子换。
   浮着的，所以最后一张卡得给它让出自身高度，不然核单核到最底下那张被压住。
   左右内边距一行一行写：原来那句 padding 简写在第三段里带了 constant()，
   这个函数只有老 iOS 认，其它浏览器整条声明作废 —— 结果上下 10px、左右 0，
   「合计」贴着屏幕沿，跟上面那排单子的 12 那条线对不上 */
.qi-foot {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 4;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding-top: 10px;
    padding-bottom: calc(10px + env(safe-area-inset-bottom));
    padding-left: 12px;
    padding-right: 12px;
}

.iv-page.qi-page {
    /* 上面那排标签要贴住标题栏，容不得 --iv-page 默认那 10px 灰底缝 */
    padding: 0 0 84px;
}

.qi-foot-sum {
    display: flex;
    align-items: baseline;
    gap: 6px;
    min-width: 0;
}

.qi-foot-label,
.qi-foot-sub {
    font-size: 12px;
    color: var(--text-sub);
}

/* 「去开票」只有三个字，不兜一把宽度会缩成一枚圆蛋，跟右边那颗「一键开完这 N 张」对不齐 */
.qi-foot-btn {
    flex: none;
    min-width: 112px;
}

/* 券列表页首屏骨架：两张券卡 */
.sk-cp {
    box-shadow: none;
    border: 1px solid #f2f3f5;
}

.sk-cp-left {
    width: 88px;
    height: 64px;
    flex: none;
    border-radius: 0;
}

.sk-cp-long {
    width: 60%;
    height: 14px;
}

.sk-cp-short {
    width: 40%;
    height: 12px;
    margin-top: 6px;
}
/* 券列表页「可使用」栏顶部那句到期提醒：整条用淡底，不抢券卡的视觉 */
.cp-soon {
    margin: 10px 12px 0;
    padding: 8px 10px;
    border-radius: 8px;
    background: #fff4ee;
    color: var(--button-color);
    font-size: 12px;
    line-height: 18px;
}

/* ---------- 邀请好友 ----------
   一屏三块：邀请码卡、两个数、好友名单。卡片留白按全站口径 margin: 0 12px 10px */
.iv-page {
    padding: 10px 0 12px;
}

.iv-card {
    background: #fff;
    border-radius: 10px;
    margin: 0 12px 10px;
    padding: 14px;
    box-shadow: 0 1px 6px rgba(0, 0, 0, .05);
}

.iv-label {
    font-size: 12px;
    color: var(--text-sub);
}

.iv-code {
    font-size: 26px;
    font-weight: 700;
    letter-spacing: 3px;
    color: var(--main-color);
    margin-top: 4px;
}

.iv-reward {
    font-size: 13px;
    color: var(--text-main);
    margin-top: 8px;
    line-height: 19px;
}

/* 链接整条露出来：他要确认自己发出去的到底是哪个地址，藏起来反而不敢点 */
.iv-link {
    margin-top: 10px;
    padding: 8px 10px;
    background: var(--page-bg);
    border-radius: 8px;
    font-size: 12px;
    color: var(--text-sub);
    line-height: 18px;
    word-break: break-all;
}

.iv-btns {
    display: flex;
    gap: 10px;
    margin-top: 12px;
}

.iv-btn {
    flex: 1;
    text-align: center;
    font-size: 13px;
    color: #fff;
    background: var(--main-gradient);
    padding: 9px 0;
    border-radius: 18px;
}

.iv-plain {
    background: #fff;
    color: var(--main-color);
    border: 1px solid var(--main-color);
}

/* 海报还在画，这一颗先按下去看不出动，免得连点生成好几张 */
.iv-btn.is-busy {
    opacity: .55;
    pointer-events: none;
}

/* 二维码：白底描边的方卡，居中。图本身是 png 的 dataURL，微信里长按就能识别 */
.iv-qr {
    margin-top: 12px;
    text-align: center;
}

.iv-qr-img {
    width: 168px;
    height: 168px;
    padding: 8px;
    background: #fff;
    border: 1px solid #ebedf0;
    border-radius: 8px;
    display: block;
    margin: 0 auto;
}

.iv-qr-tip {
    margin-top: 6px;
    font-size: 12px;
    color: var(--text-sub);
    line-height: 18px;
}

.iv-stat {
    display: flex;
    padding: 12px 0;
}

.iv-stat-item {
    flex: 1;
    text-align: center;
}

.iv-num {
    font-size: 20px;
    font-weight: 700;
    color: var(--text-main);
    line-height: 26px;
}

.iv-row {
    display: flex;
    align-items: center;
    padding: 10px 0;
    border-bottom: 1px solid #f2f3f5;
}

.iv-row:last-child {
    border-bottom: none;
}

.iv-who {
    flex: 1;
    min-width: 0;
}

.iv-name {
    font-size: 14px;
    color: var(--text-main);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.iv-time {
    font-size: 12px;
    color: var(--text-sub);
    margin-top: 2px;
}

/* 三种状态都上色，不给灰色：灰色在这页读起来像"没数据" */
.iv-st {
    flex: none;
    margin-left: 10px;
    font-size: 11px;
    line-height: 18px;
    padding: 0 8px;
    border-radius: 9px;
}

.iv-st.ok {
    background: #e8f7f1;
    color: #07a56f;
}

.iv-st.wait {
    background: #fff4ee;
    color: var(--button-color);
}

.iv-st.miss {
    background: #fdeceb;
    color: #ee0a24;
}

.iv-foot {
    margin: 0 12px;
    font-size: 12px;
    color: var(--text-sub);
    line-height: 18px;
}

/* 海报弹层：只留画面和下面两个钮，不再套标题栏。
   图整宽显示按比例撑高，弹层最高 84vh，装不下就在这层滚，
   免得给图加 max-height 把 5:8 的海报压成两边留灰条 */
.iv-poster-pop {
    width: 100%;
    max-width: 420px;
    max-height: 84vh;
    margin: 0 auto;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
}

.iv-poster {
    padding: 14px 14px calc(14px + constant(safe-area-inset-bottom));
    padding-bottom: calc(14px + env(safe-area-inset-bottom));
}

.iv-poster-img {
    display: block;
    width: 100%;
    border-radius: 8px;
}

.iv-poster-btns {
    display: flex;
    gap: 10px;
    margin-top: 12px;
}

.iv-poster-tip {
    margin-top: 8px;
    text-align: center;
    font-size: 12px;
    color: var(--text-sub);
    line-height: 18px;
}

/* 邀请页首屏骨架：两张卡，各一条长一条短 */
.sk-iv {
    padding: 16px 14px;
}

.sk-iv-wide {
    width: 100%;
    height: 22px;
}

.sk-iv-narrow {
    width: 45%;
    height: 14px;
    margin-top: 10px;
}

/* ---------------- 断网提示细条 ----------------
   任意一次请求最终没通就挂在顶栏下面，不自动消失：点「重试」或后面任意一次问通了才收。
   和跑马灯抢的是同一条位置，出问题时公告让位（v-if 里已经写了 !netOn）。 */
.net-bar {
    display: flex;
    align-items: center;
    gap: 6px;
    height: 30px;
    padding: 0 12px;
    background: #fff4e8;
    border-bottom: 1px solid #ffe1c4;
    color: #b45a12;
    font-size: 11.5px;
    overflow: hidden;
}

.net-bar b {
    flex: 1;
    min-width: 0;
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.nbar-ic {
    flex: 0 0 auto;
    font-size: 14px;
}

.nbar-retry {
    flex: 0 0 auto;
    padding: 2px 10px;
    border: 1px solid #f3c99a;
    border-radius: 999px;
    background: #fff;
    color: #b45a12;
    font-size: 11px;
}

.nbar-retry:active {
    transform: scale(.94);
}

/* 分类/客服这类 full 页自己按 100dvh 减过高度，细条插在中间会把底部输入栏顶出屏幕，
   所以这种时候把它从文档流里摘出来浮在顶栏下面，宁可盖住一点内容也不弄坏那两屏的算式 */
body.page-full .net-bar {
    position: fixed;
    top: 46px;
    left: 0;
    right: 0;
    z-index: 9;
}

/* 打手主页那屏整片深底，暖橙条搁在这儿像块膏药，整组翻成深底上的暖色 */
body.nav-dark .net-bar {
    background: #2b2440;
    border-bottom-color: rgba(255, 217, 168, .16);
    color: #ffd9a8;
}

body.nav-dark .nbar-retry {
    background: rgba(255, 255, 255, .08);
    border-color: rgba(255, 217, 168, .3);
    color: #ffd9a8;
}

/* ---------------- 跑马灯公告 ----------------
   后台「站点设置→跑马灯」配的滚动公告，夹在顶栏和内容之间。
   多条拼成一行，靠 padding-left:100% + translateX(-100%) 做横向滚动。 */
.marquee {
    display: flex;
    align-items: center;
    gap: 6px;
    height: 34px;
    padding: 0 12px;
    background: #fff8e8;
    color: #b07a15;
    font-size: 12px;
    overflow: hidden;
}

.marquee-ic {
    flex: 0 0 auto;
    font-size: 14px;
    color: #e8a33d;
}

.marquee-view {
    flex: 1;
    min-width: 0;
    overflow: hidden;
}

.marquee-txt {
    display: inline-block;
    white-space: nowrap;
    padding-left: 100%;
    animation: marquee-scroll 18s linear infinite;
}

@keyframes marquee-scroll {
    0% {
        transform: translateX(0);
    }
    100% {
        transform: translateX(-100%);
    }
}

/* 系统开了「减少动态效果」时不滚，改成一行省略号，公告至少还看得见 */
@media (prefers-reduced-motion: reduce) {
    .marquee-txt {
        display: block;
        padding-left: 0;
        max-width: 100%;
        overflow: hidden;
        text-overflow: ellipsis;
        animation: none;
    }
}

/* ================= 打手（顾客端展示） =================
   名单是一排排「头像 + 信息」的横卡，不是商品那种两列网格：
   商品靠图挑，打手靠的是名字、星级和单量，图占大半屏反而没用的信息挤没了。 */

/* 筛选条：搜索 + 排序 + 类型。顶栏 fixed 高 46px，这一条吸在它下沿——
   名单翻长了想换个排序，不该先让人滚回顶部 */
/* 底色交给前面那条全站玻璃规则：这里再写一遍 var(--page-bg) 会把它整个盖成实色 */
.ds-filter {
    position: sticky;
    top: 46px;
    z-index: 3;
}

.ds-sort {
    display: flex;
    align-items: center;
    padding: 2px 12px 8px;
}

.ds-sort-item {
    margin-right: 8px;
    padding: 4px 14px;
    border-radius: 20px;
    background: #fff;
    border: 1px solid #fff;
    font-size: 13px;
    color: #646566;
}

/* 选中只用描边和字色，不填主色底：三个 chip 并排时填色的那个像被按住了，
   描边的反而是"这就是当前项" */
.ds-sort-item.active {
    color: var(--main-color);
    border-color: var(--main-color);
    font-weight: 600;
}

.ds-total {
    margin-left: auto;
    font-size: 12px;
    color: var(--text-sub);
}

/* 类型横滑（打手和商品共用后台那一套分类） */
.ds-cates {
    display: flex;
    overflow-x: auto;
    padding: 0 12px 10px;
    -webkit-overflow-scrolling: touch;
}

.ds-cates::-webkit-scrollbar {
    display: none;
}

.ds-cate {
    flex: none;
    margin-right: 8px;
    padding: 3px 10px;
    border-radius: 6px;
    background: #fff;
    font-size: 12px;
    color: #646566;
    white-space: nowrap;
}

/* 选中口径跟上面那排排序 chip 一致：主色描边 + 主色字。
   不填半透明主色底——后台配色是变量，写死的 rgba 一换色就串味。
   描边用 inset 阴影，加不加都不占位，切换时 chip 不会抖一下 */
.ds-cate.active {
    color: var(--main-color);
    box-shadow: inset 0 0 0 1px var(--main-color);
    font-weight: 600;
}

/* ---------- 名单卡片 ---------- */
.ds-rows {
    padding: 0 12px;
}

.ds-row {
    position: relative;
    display: flex;
    align-items: flex-start;
    padding: 14px 12px;
    margin-bottom: 10px;
    border-radius: 12px;
    background: #fff;
}

.ds-row:active {
    background: #fafafa;
}

/* 头像：没上传时由昵称首字顶上，两种情况占同一格、尺寸一致，列表不会错位 */
.ds-ava-wrap {
    position: relative;
    flex: none;
    margin-right: 12px;
}

.ds-ava {
    width: 56px;
    height: 56px;
    border-radius: 50%;
    object-fit: cover;
    display: block;
    background: #f2f3f5;
}

.ds-ava-txt {
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--main-gradient, var(--main-color));
    color: #fff;
    font-size: 24px;
    font-weight: 600;
}

/* 优秀打手：一枚压在头像右下角的小金牌，扫一眼就知道哪个是推荐的 */
.ds-crown {
    position: absolute;
    right: -2px;
    bottom: -2px;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background: #ffb300;
    border: 2px solid #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #fff;
    font-size: 11px;
}

.ds-info {
    flex: 1;
    min-width: 0;
}

.ds-name {
    display: flex;
    align-items: center;
    font-size: 16px;
    font-weight: 600;
    color: var(--text-main);
}

.ds-name b {
    max-width: 150px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ds-medal {
    flex: none;
    margin-left: 6px;
    padding: 1px 6px;
    border-radius: 4px;
    background: var(--main-gradient, var(--main-color));
    color: #fff;
    font-size: 10px;
    font-weight: 400;
}

/* 自报的在线/接单：绿点=能接，橙点=这一位暂停接单。
   两态都实打实上色，暂停接单这种事不该用灰色含糊过去 */
.ds-state {
    flex: none;
    display: inline-flex;
    align-items: center;
    gap: 4px;
    margin-left: 6px;
    padding: 0 6px;
    border-radius: 8px;
    font-size: 10px;
    font-weight: 400;
    line-height: 16px;
    white-space: nowrap;
}

.ds-state.on {
    color: #07c160;
    background: rgba(7, 193, 96, .1);
}

.ds-state.off {
    color: #f59f0b;
    background: rgba(245, 159, 11, .12);
}

.ds-dot {
    width: 5px;
    height: 5px;
    border-radius: 50%;
    flex: none;
}

.ds-state.on .ds-dot {
    background: #07c160;
}

.ds-state.off .ds-dot {
    background: #f59f0b;
}

/* 暗场头部那一枚：底换白纱、字给纯白，小点提亮一档才压得住暗底 */
.ds-who-name .ds-state {
    max-width: none;
}

.ds-state.hero {
    background: rgba(255, 255, 255, .18);
    color: #fff;
}

.ds-state.hero.on .ds-dot {
    background: #4ee5a4;
}

.ds-state.hero.off .ds-dot {
    background: #ffcf6b;
}

.ds-star {
    display: flex;
    align-items: center;
    margin-top: 6px;
}

.ds-star-txt {
    margin-left: 6px;
    font-size: 12px;
    color: #b76e00;
}

.ds-tags {
    display: flex;
    flex-wrap: wrap;
    margin-top: 8px;
}

/* 打手自己那串标签跟着后台配色走，不做灰底灰字：
   灰的在一排白卡里看着像没上色，和旁边那个实心的「优秀打手」也分不出主次 */
.ds-tag {
    margin: 0 6px 4px 0;
    padding: 2px 7px;
    border-radius: 4px;
    font-size: 11px;
    color: var(--main-color);
    box-shadow: inset 0 0 0 1px var(--main-a35, var(--main-color));
}

.ds-meta {
    margin-top: 6px;
    font-size: 12px;
    color: var(--text-sub);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* 三个数字压一条发丝线上面：这是挑人的硬指标，不该和"擅长什么"混成一句灰字 */
.ds-stats {
    display: flex;
    margin-top: 10px;
    padding-top: 8px;
    border-top: 1px solid #f2f3f5;
}

.ds-stats span {
    margin-right: 18px;
    font-size: 11px;
    color: var(--text-sub);
}

.ds-stats b {
    margin-right: 3px;
    font-size: 15px;
    font-weight: 600;
    color: var(--text-main);
}

.ds-arrow {
    align-self: center;
    color: #c8c9cc;
    font-size: 14px;
}

/* 首屏骨架：三行灰条，高度和真实的名字/星级/标签行对得上 */
.ds-row-sk {
    align-items: center;
}

.ds-row-sk .ds-info {
    display: flex;
    flex-direction: column;
}

.sk-ds-title {
    width: 40%;
    height: 18px;
}

.sk-ds-line {
    width: 100%;
    height: 12px;
    margin-top: 10px;
}

.sk-ds-line-short {
    width: 60%;
}

/* ---------- 打手主页（暗场「段位厅」） ---------- */
/* 这一屏整页深底，和全站浅底分开：底色后台「系统配置→界面设置→打手主页底色」单独配，
   留空就用下面这套内置暗色；填了色值时 theme.css 会覆盖 --ds-page / --ds-ink / --ds-ink-rgb，
   其中 --ds-ink 是后端按亮度算出来的字色（填浅色就翻深字），所以这里所有颜色都从变量拼，
   不写死 #fff——否则他填一个白色上去整屏白字白底就瞎了 */
.ds-home {
    min-height: calc(100vh - 46px);
    padding-bottom: 66px;
    background: radial-gradient(120% 55% at 84% 4%, var(--main-a35), transparent 62%),
        radial-gradient(80% 40% at 6% 0%, rgba(var(--ds-ink-rgb), .06), transparent 60%),
        var(--ds-page);
    color: var(--ds-ink);
    /* 空态那句灰字也跟着翻色 */
    --van-empty-description-color: rgba(var(--ds-ink-rgb), .5);
}

.ds-hero {
    padding: 18px 18px 0;
}

/* 段位名第一次当主角：后台「打手星级等级」那张表里的名字，字号最大、拉开字距 */
.ds-tier {
    font-size: 40px;
    font-weight: 800;
    letter-spacing: 2px;
    line-height: 1.1;
    text-shadow: 0 0 22px var(--main-a35);
}

/* 星星压在段位名下面：Vant 的空心星默认是浅灰，暗底上得换成压暗的白 */
.ds-tier-rate {
    display: flex;
    align-items: center;
    margin-top: 10px;
    --van-rate-icon-void-color: rgba(var(--ds-ink-rgb), .28);
}

.ds-who {
    display: flex;
    align-items: center;
    margin-top: 18px;
}

.ds-ava-lg {
    width: 60px;
    height: 60px;
    font-size: 25px;
    border: 2px solid rgba(var(--ds-ink-rgb), .3);
}

/* 头像没传图/传挂了时 .ds-ava 那个浅灰底会在暗场上亮成一块，压暗；
   只改 img，昵称首字那个渐变底（.ds-ava-txt 是个 span）不动 */
.ds-home img.ds-ava {
    background: rgba(var(--ds-ink-rgb), .08);
}

.ds-who-info {
    flex: 1;
    min-width: 0;
}

.ds-who-name {
    display: flex;
    align-items: center;
    font-size: 17px;
    font-weight: 700;
}

.ds-who-name span {
    max-width: 150px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ds-who-name em {
    flex: none;
    margin-left: 8px;
    padding: 2px 8px;
    border-radius: 20px;
    background: rgba(var(--ds-ink-rgb), .14);
    font-size: 11px;
    font-style: normal;
    font-weight: 400;
}

/* 擅长游戏/区服和入驻时间并成一行小字，压在昵称下面 */
.ds-who-meta {
    margin-top: 4px;
    font-size: 11px;
    color: rgba(var(--ds-ink-rgb), .55);
}

.ds-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 16px;
}

/* 标签描边借主题色的一道光，字本身用反白——主题色要是配得深（比如那个紫），
   深字压在深底上就看不见了，所以字不跟主题色走 */
.ds-chip {
    padding: 3px 10px;
    border-radius: 20px;
    font-size: 11px;
    color: rgba(var(--ds-ink-rgb), .85);
    background: rgba(var(--ds-ink-rgb), .04);
    box-shadow: inset 0 0 0 1px var(--main-a35, var(--main-color));
}

/* 三格数据拆成三张暗卡，中间不再用发丝竖线连（暗底上白线会打断成一条亮杠） */
.ds-nums {
    display: flex;
    gap: 8px;
    margin-top: 18px;
    padding: 0 18px;
}

.ds-num {
    flex: 1;
    min-width: 0;
    padding: 10px 8px 9px;
    border-radius: 12px;
    background: rgba(var(--ds-ink-rgb), .05);
    box-shadow: inset 0 0 0 1px rgba(var(--ds-ink-rgb), .09);
}

.ds-num b {
    display: block;
    font-size: 19px;
    font-weight: 800;
    color: var(--ds-ink);
    font-variant-numeric: tabular-nums;
}

.ds-num span {
    display: block;
    margin-top: 2px;
    font-size: 10px;
    letter-spacing: .5px;
    color: rgba(var(--ds-ink-rgb), .45);
}

/* 完成率光有数字看不出高矮，压一条进度条；条子走主题渐变 */
.ds-track {
    height: 3px;
    margin-top: 7px;
    border-radius: 2px;
    overflow: hidden;
    background: rgba(var(--ds-ink-rgb), .14);
}

.ds-track em {
    display: block;
    height: 100%;
    border-radius: 2px;
    background: var(--main-gradient, var(--main-color));
}

.ds-none {
    font-size: 13px;
    color: rgba(var(--ds-ink-rgb), .5);
}

/* 怎么指定这位打手：一句话说清备注和客服两条路，放在按钮上面而不是弹窗里，
   免得为一句说明再挡一层遮罩。暗底上它只是一句说明，不抢自我介绍那块 */
.ds-howto {
    margin: 16px 18px 0;
    padding: 10px 12px;
    border-radius: 10px;
    background: rgba(var(--ds-ink-rgb), .04);
    box-shadow: inset 0 0 0 1px rgba(var(--ds-ink-rgb), .07);
    font-size: 12px;
    line-height: 1.6;
    color: rgba(var(--ds-ink-rgb), .55);
}

/* 自我介绍那张卡：和商品详情共用 .detail-rich，那边是浅底黑字，压在暗底上等于隐形，
   所以正文/标题/链接/图片这几样在 .ds-home 作用域里重新给一遍色，商品详情不受影响 */
.ds-card {
    margin: 16px 18px 0;
    padding: 12px 13px;
    border-radius: 12px;
    background: rgba(var(--ds-ink-rgb), .045);
    box-shadow: inset 0 0 0 1px rgba(var(--ds-ink-rgb), .08);
}

.ds-card .card-title {
    padding: 0 0 6px;
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 1px;
    color: var(--ds-ink);
}

.ds-home .detail-rich {
    color: rgba(var(--ds-ink-rgb), .78);
}

.ds-home .detail-rich h1,
.ds-home .detail-rich h2,
.ds-home .detail-rich h3,
.ds-home .detail-rich h4,
.ds-home .detail-rich h5,
.ds-home .detail-rich h6 {
    color: var(--ds-ink);
}

/* 链接同理不跟主题色走，深浅底上都得看得见 */
.ds-home .detail-rich a {
    color: var(--ds-ink);
    text-decoration: underline;
}

/* 后台传上来的截图本身是白底，贴暗卡上会亮成一块，描边+圆角把它框住 */
.ds-home .detail-rich img {
    border-radius: 8px;
    box-shadow: 0 0 0 1px rgba(var(--ds-ink-rgb), .14);
}

/* 底部操作条：比页面再亮一档，和整屏拉开一层；「首页」那个图标跟着翻色。
   注意 --van-action-bar-icon-background：Vant 那个格子自带一块白底（var(--van-background-2)），
   只翻图标和文字色的话，暗底左边就会顶出一个白方块，必须一起压成透明 */
.ds-home .van-action-bar {
    background-color: var(--ds-page);
    background-image: linear-gradient(rgba(var(--ds-ink-rgb), .07), rgba(var(--ds-ink-rgb), .07));
    box-shadow: 0 -1px 0 rgba(var(--ds-ink-rgb), .08);
    --van-action-bar-icon-background: transparent;
    --van-action-bar-icon-text-color: rgba(var(--ds-ink-rgb), .6);
    --van-action-bar-icon-color: rgba(var(--ds-ink-rgb), .6);
    --van-action-bar-icon-active-color: rgba(var(--ds-ink-rgb), .1);
    /* 全站玻璃那条给 .van-action-bar 挂了模糊层，暗场这屏得撤掉：
       已经是深色实底，再糊一层只会把「暗场」这个已定稿的效果搅浑。
       纱现在住在这条的 ::before 上，所以撤的是 ::before，只写 background 撤不干净 */
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
}

.ds-home .van-action-bar::before {
    display: none;
}

/* 底部那个「找客服，指定TA接单」：默认跟全站主色渐变，后台单独配了打手按钮色才换成它。
   写在 .ds-home 作用域里，商品详情页那个「立即下单」不受影响。
   玻璃化之后实底被拆到 ::after 那层色片上了，所以这里要给的是 --gfill，
   原来那句 background 压不动上面的 background:none !important，配了打手按钮色也会糊成全局红 */
.ds-home .van-action-bar-button--danger {
    --gfill: var(--ds-btn, var(--main-gradient));
    color: var(--ds-btn-ink, #fff);
}

/* 拉取中的占位：灰条本来是浅底的 #f0f1f3，暗底上得换成压暗的白，不然像两块白斑 */
.ds-hero-sk {
    display: block;
    height: 190px;
    margin: 18px;
    border-radius: 12px;
}

/* 骨架那一屏借的是名单卡的排版，那套是白底：整屏压暗，不然暗场上摆一块白板 */
.ds-home .ds-rows {
    padding: 0;
    margin: 0 18px;
}

.ds-home .ds-row {
    background: rgba(var(--ds-ink-rgb), .04);
    box-shadow: inset 0 0 0 1px rgba(var(--ds-ink-rgb), .07);
}

.ds-home .sk {
    background-image: linear-gradient(90deg, rgba(var(--ds-ink-rgb), .06) 25%,
        rgba(var(--ds-ink-rgb), .12) 37%, rgba(var(--ds-ink-rgb), .06) 63%);
}

/* ---------- 首页「优秀打手」横滑位 ---------- */
.ds-sec-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 12px 12px 8px;
}

.ds-sec-head .ds-sec-title {
    font-size: 15px;
    font-weight: 600;
    color: var(--text-main);
}

.ds-sec-all {
    display: flex;
    align-items: center;
    font-size: 12px;
    color: var(--text-sub);
}

.ds-rank-scroll {
    display: flex;
    gap: 10px;
    overflow-x: auto;
    padding: 0 12px 12px;
    -webkit-overflow-scrolling: touch;
}

.ds-rank-scroll::-webkit-scrollbar {
    display: none;
}

.ds-rank-card {
    flex: none;
    width: 92px;
    padding: 12px 6px 10px;
    border-radius: 12px;
    background: #fff;
    text-align: center;
}

.ds-rank-card .ds-ava {
    width: 52px;
    height: 52px;
    margin: 0 auto 8px;
    font-size: 22px;
}

.ds-rank-name {
    font-size: 13px;
    font-weight: 600;
    color: var(--text-main);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ds-rank-star {
    margin-top: 4px;
    font-size: 11px;
    color: #b76e00;
}


/* ---------- 打手工作台 ----------
   顶部一块主色渐变头，下面是工单卡列表。
   色值走 CSS 变量，后台换主题色这里跟着变。 */
.dw-page {
    padding-bottom: 12px;
}

.dw-head {
    background: var(--main-gradient, var(--main-color));
    padding: 16px 14px 0;
    color: #fff;
}

.dw-user {
    display: flex;
    align-items: center;
    gap: 12px;
}

.dw-av {
    width: 52px;
    height: 52px;
    border-radius: 50%;
    object-fit: cover;
    border: 2px solid rgba(255, 255, 255, .6);
    background: rgba(255, 255, 255, .2);
    flex: none;
}

.dw-info {
    min-width: 0;
    flex: 1;
}

.dw-name {
    font-size: 17px;
    font-weight: 700;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.dw-meta {
    margin-top: 5px;
    font-size: 12px;
    opacity: .92;
    display: flex;
    align-items: center;
    gap: 6px;
}

/* 四个数字：点一下就切到对应列表，这里同时是「筛选器」 */
.dw-stats {
    display: flex;
    margin-top: 14px;
}

.dw-stat {
    flex: 1;
    text-align: center;
    padding: 8px 0 12px;
    border-radius: 8px 8px 0 0;
    position: relative;
    transition: background .2s;
}

.dw-stat b {
    display: block;
    font-size: 19px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

.dw-stat span {
    display: block;
    font-size: 11px;
    opacity: .85;
    margin-top: 2px;
}

.dw-stat.on {
    background: rgba(255, 255, 255, .18);
}

.dw-stat.on b {
    font-size: 21px;
}

/* 自报状态：一张白卡两行开关。左边说现在的状态，右边那枚就是他自己拨的 */
.dw-state-card {
    margin: 12px 12px 0;
    background: #fff;
    border-radius: 10px;
    padding: 2px 12px;
    box-shadow: 0 1px 4px rgba(0, 0, 0, .05);
}

.dw-sw-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 11px 0;
}

.dw-sw-row + .dw-sw-row {
    border-top: 1px solid #f2f4f6;
}

.dw-sw-txt {
    flex: 1;
    min-width: 0;
}

.dw-sw-txt b {
    display: block;
    font-size: 14px;
    color: var(--text-main);
}

.dw-sw-txt span {
    display: block;
    font-size: 11px;
    color: var(--text-sub);
    margin-top: 2px;
}

/* 有单被催了还没回应：整条铺红，进工作台第一眼就该看见它 */
.dw-hurry {
    display: flex;
    align-items: center;
    gap: 6px;
    margin: 10px 12px 0;
    padding: 10px 12px;
    border-radius: 8px;
    background: rgba(238, 10, 36, .09);
    color: #ee0a24;
    font-size: 13px;
    font-weight: 600;
}

.dw-hurry .van-icon {
    font-size: 16px;
}

.dw-hurry-go {
    margin-left: auto;
    font-size: 12px;
    font-weight: 400;
    text-decoration: underline;
}

.dw-list {
    padding: 12px 12px 0;
}

.dw-card {
    background: #fff;
    border-radius: 10px;
    padding: 12px;
    margin-bottom: 10px;
    box-shadow: 0 1px 4px rgba(0, 0, 0, .05);
}

.dw-card-top {
    display: flex;
    justify-content: space-between;
    align-items: center;
    font-size: 12px;
    color: #98a0a8;
}

.dw-no {
    font-variant-numeric: tabular-nums;
}

/* 卡片顶行中间那枚催单标记：红色实底，夹在单号和状态之间不会看漏 */
.dw-hurry-tag {
    font-size: 11px;
    line-height: 16px;
    padding: 0 6px;
    border-radius: 3px;
    font-weight: 600;
    color: #ee0a24;
    background: rgba(238, 10, 36, .08);
}

.dw-state {
    font-weight: 600;
}

/* 状态色：待接单要抢眼，打手中是进行态，待验收是等别人，已完成收灰 */
.dw-state.st2 { color: #f59f0b; }
.dw-state.st3 { color: var(--main-color); }
.dw-state.st4 { color: #10aeff; }
.dw-state.st5 { color: #98a0a8; }
.dw-state.st6 { color: #c3c9ce; }

.dw-goods {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-top: 10px;
}

.dw-goods img {
    width: 46px;
    height: 46px;
    border-radius: 8px;
    object-fit: cover;
    background: #f2f4f6;
    flex: none;
}

.dw-goods-body {
    flex: 1;
    min-width: 0;
}

.dw-goods-name {
    font-size: 14px;
    color: var(--text-main);
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    line-height: 19px;
}

.dw-goods-sub {
    font-size: 12px;
    color: var(--text-sub);
    margin-top: 4px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.dw-amount {
    font-size: 15px;
    font-weight: 700;
    color: var(--main-color);
    flex: none;
    font-variant-numeric: tabular-nums;
}

.dw-card-foot {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-top: 12px;
    padding-top: 10px;
    border-top: 1px solid #f4f6f8;
}

.dw-time {
    font-size: 12px;
    color: #b6bcc2;
}

.dw-btn {
    font-size: 13px;
    padding: 6px 16px;
    border-radius: 15px;
    background: var(--main-gradient, var(--main-color));
    color: #fff;
    font-weight: 600;
}

.dw-btn-main {
    background: var(--main-gradient, var(--main-color));
}

.dw-btn-none {
    font-size: 12px;
    color: #c3c9ce;
}

.dw-denied {
    margin: 60px 30px;
    text-align: center;
    color: var(--text-sub);
    font-size: 14px;
    line-height: 1.7;
}

.dw-denied .van-icon {
    font-size: 40px;
    color: #f59f0b;
    display: block;
    margin-bottom: 12px;
}

.dw-pop-title {
    text-align: center;
    font-size: 15px;
    font-weight: 600;
    color: var(--text-main);
    padding-bottom: 12px;
}

.dw-upload {
    padding: 12px 16px 0;
}

.dw-upload-tip {
    font-size: 12px;
    color: #a7aeb5;
    margin-top: 8px;
}

.dw-finish-time {
    font-size: 12px;
    color: #b6bcc2;
    margin-top: 8px;
}

/* 「已分配」这类状态字贴到了右边缘，给它留点内边距 */
.dw-card-top .dw-state {
    padding-right: 2px;
}

/* 卡片本身左右各 12px 由 .dw-list 的 padding 给，这里再把内容收一点，
   让「接单」按钮和状态字都不贴边 */
.dw-card {
    padding: 12px 14px;
}

/* ---------- 申请成为打手 ---------- */
.da-page {
    padding-bottom: 20px;
}

/* 三种结果态（已通过 / 审核中）共用一套居中版式 */
.da-state {
    margin: 50px 30px;
    text-align: center;
}

.da-state .van-icon {
    font-size: 56px;
    display: block;
    margin-bottom: 16px;
}

.da-state.da-ok .van-icon {
    color: #07c160;
}

.da-state.da-wait .van-icon {
    color: #f59f0b;
}

.da-state-t {
    font-size: 17px;
    font-weight: 700;
    color: var(--text-main);
}

.da-state-s {
    font-size: 13px;
    color: var(--text-sub);
    margin: 8px 0 20px;
    line-height: 1.6;
}

/* 顶部说明条 */
.da-tip {
    margin: 12px 12px 0;
    padding: 9px 12px;
    border-radius: 8px;
    font-size: 12.5px;
    line-height: 1.6;
    background: #fff7e6;
    color: #b76e00;
}

.da-tip-plain {
    background: #f2f7ff;
    color: #3d6fa8;
}

.da-upload {
    padding: 12px 16px 4px;
}

.da-upload-label {
    font-size: 13px;
    color: var(--text-sub);
    margin-bottom: 10px;
}

.da-submit {
    padding: 18px 16px 0;
}

.da-submit-tip {
    text-align: center;
    font-size: 12px;
    color: #b6bcc2;
    margin-top: 10px;
}


/* ==================================================================
   统一按压反馈
   ==================================================================
   全站把 -webkit-tap-highlight-color 关成了 transparent（见上面的 reset），
   好处是不会冒出安卓那块蓝方块 / 苹果那块灰方块，代价是：
   没自己写按压态的元素，手指按下去屏幕上什么都不会变。

   手机上这个很要命 —— 点完到接口回来这一两秒里，人分不清「点上了、在转」
   和「压根没点中」，于是连着点好几下，反而把重复提交的风险拉高。

   ★ 每条规则都写了两份选择器，值一模一样：
     · :active    —— 安卓和桌面的原生按下态，零延迟
     · .is-press  —— 给 iOS 兜底。那边 :active 压根不触发：
                     WebKit 老规矩是"元素没挂触摸监听就不应用 :active"(bug 138816)，
                     新一点的 iOS 更直接 —— 17.6.1 起连按钮上的 :active 也点不亮
                     (bug 281755)。所以由 app.js 的 PRESSABLE 那段自己挂类。
     两个都生效时不会叠成两倍深，因为声明完全相同。

   ⚠️ 下面这四组类名和 app.js 里 PRESSABLE 那份清单必须保持一致，
      改一边记得改另一边。

   刻意都不加 transition：按下要的是当场就变，加了过渡反而迟钝；
   松手瞬间弹回，手机上是正常手感。
   ------------------------------------------------------------------ */

/* 1. 有自己的底色、或者带图带渐变的块：整体压暗一档。
      用 filter 而不是 background，是因为这些元素里有白卡、有渐变按钮、
      有整张商品图，换成 background 只会改到图片背后看不见的那一层 */
:is(.goods-card, .cate-side-item, .cate-search, .home-search, .ds-rank-card,
    .order-item, .rc-item, .ms-item, .cp-item, .kf-head, .kf-goods, .kf-cp,
    .kf-head-buy, .kf-goods-buy, .kf-send, .mb-chip, .cp-btn, .iv-btn, .dw-btn):active,
:is(.goods-card, .cate-side-item, .cate-search, .home-search, .ds-rank-card,
    .order-item, .rc-item, .ms-item, .cp-item, .kf-head, .kf-goods, .kf-cp,
    .kf-head-buy, .kf-goods-buy, .kf-send, .mb-chip, .cp-btn, .iv-btn, .dw-btn).is-press {
    filter: brightness(.93);
}

/* 2. 透明底、横向铺开的行和格：垫一层浅灰当"按下"的面。
      这一类不做缩放 —— 整行的东西缩一下，左右会露出缝，看着像没对齐 */
:is(.mine-cell, .mq-item, .mo-item, .mh-top, .mh-stat, .dw-stat, .hcb-i,
    .ds-cate, .da-item, .agree-line, .kf-qt, .kf-emo-tab, .st-cell, .tag-item):active,
:is(.mine-cell, .mq-item, .mo-item, .mh-top, .mh-stat, .dw-stat, .hcb-i,
    .ds-cate, .da-item, .agree-line, .kf-qt, .kf-emo-tab, .st-cell, .tag-item).is-press {
    background: rgba(0, 0, 0, .055);
}

/* 3. 小文字链和小图标：淡一下就够了。
      给它们垫底色会凭空多出一块方块，比不给还难看 */
:is(.mo-all, .ds-sec-all, .kf-end, .kf-emo-act, .kf-quote-x, .kf-emo-btn,
    .kf-photo, .kf-goods-go, .iv-plain, .pop-ad-close):active,
:is(.mo-all, .ds-sec-all, .kf-end, .kf-emo-act, .kf-quote-x, .kf-emo-btn,
    .kf-photo, .kf-goods-go, .iv-plain, .pop-ad-close).is-press {
    opacity: .5;
}

/* 4. 聊天里的图和视频：点它是放大看，淡一档表示"收到了" */
:is(.kf-img, .kf-video, .kf-sticker):active,
:is(.kf-img, .kf-video, .kf-sticker).is-press {
    opacity: .82;
}

/* ---------- 切 tab 时内容轻推淡入 ----------
   和水滴同一条时间长度（.4s），所以"水滴滑到位"和"内容落定"是同时收尾的。
   位移只有 16px —— 再多就不像"轻推"，像整页翻过去了。
   方向来自 --tab-dir（1 往右、-1 往左），由 app.js 在切换时写到 #app 上：
   点右边那格，内容就从右边推进来，和手指的动作方向对得上。
   #app.tab-page-in 这个 class 是"这次切换的临时状态"，
   播完由 app.js 摘掉（460ms 后），所以不会干扰别的跳转。 */
@keyframes tab-page-push {
    from {
        opacity: 0;
        transform: translateX(calc(var(--tab-dir, 1) * 16px));
    }
    to {
        opacity: 1;
        transform: none;
    }
}

#app.tab-page-in .page {
    animation: tab-page-push .4s cubic-bezier(.32, .72, .24, 1) both;
}

/* 系统开了"减弱动态效果"就全部收掉：这几段动效都是纯装饰，
   不能因为用户开了这个开关还硬塞位移 */
@media (prefers-reduced-motion: reduce) {
    .tab-fluid {
        transition: none;
    }

    .tab-fluid i,
    .van-tabbar-item--active .tab-icon,
    #app.tab-page-in .page {
        animation: none;
    }
}
