/* =========================================================================
   app.css —— 潮玩陈列柜 × 手游质感
   -------------------------------------------------------------------------
   这一版相对上一版改了什么，以及为什么：

   上一版的问题：所有内容都是「白圆角卡 + 白圆角卡 + 白圆角卡」竖着堆，
   圆角一样、间距一样、没有形状也没有色块。结果就是很板正、没有设计感。

   这一版抓住四件事：
     1. 卡牌必须有框 —— 稀有度框的颜色本身就是信息，这是卡牌游戏的常识
     2. 按钮要有厚度 —— 顶高光 + 底部实心投影，按下时压缩，像真的被按下去
     3. 形状参与设计 —— 胶囊、圆、弧、悬浮球、贴纸角标，不是清一色矩形
     4. 弹窗走底部抽屉 —— 比居中弹框现代，也是主流 App / 手游的做法

   铁律没变：渐变只能出现在「材质光泽」和「UI 立体感」上，
   不做装饰性渐变背景。
   ========================================================================= */

/* ============================ 通用小件 ============================ */
.badge {
  position: absolute; top: -3px; right: -3px;
  min-width: 17px; height: 17px; padding: 0 4px;
  border-radius: var(--r-full);
  background: var(--brand); color: #fff;
  font-size: 10px; font-weight: 700; line-height: 17px; text-align: center;
  font-family: var(--font-num);
  box-shadow: 0 0 0 2px var(--paper);
}
/* ⚠️ 这里必须是 fill:none，跟 base.css 的描边式图标保持一致。
   曾经写成 fill: currentColor —— 它和 base.css 的 .ico 同特异性、
   又后加载，于是把 fill:none 盖掉了，全站图标静默变成实心剪影：
   信封的翻盖（一条开放路径）被填成看不见的细缝，只剩一个白方块；
   骰子的点、卡片和账单的内线、房子的门全部糊在底色里。
   精灵里那些显式的 fill="currentColor" stroke="none"（骰子点 / 列表点 /
   虚粒）只有在默认是 fill:none 时才需要写 —— 这是判断图标体系的最硬证据。 */
.ico { width: 20px; height: 20px; flex: none; fill: none; stroke: currentColor; }
.gem { display: inline-flex; align-items: center; gap: 3px; color: var(--brand); }
.gem .ico { width: 13px; height: 13px; }
.gem b { font-family: var(--font-num); font-weight: 700; }

.hint {
  display: flex; align-items: flex-start; gap: 8px;
  margin: 4px var(--pad) 0; padding: 13px 15px;
  border-radius: var(--r-md);
  background: var(--paper-2);
  font-size: 12px; line-height: 1.6; color: var(--ink-2);
}
.hint .ico { width: 15px; height: 15px; margin-top: 1px; color: var(--ink-3); }
.hint.top { margin-top: 10px; }
.center { text-align: center; }
.big-ok {
  width: 78px; height: 78px; margin: 4px auto 16px;
  border-radius: var(--r-full);
  background: radial-gradient(120% 120% at 50% 0%, #6FE3C0, #10B98D);
  color: #fff;
  display: grid; place-items: center;
  box-shadow: 0 10px 24px -8px rgba(16,185,141,.7);
}
.big-ok .ico { width: 38px; height: 38px; }

/* 装饰小星：潮玩品牌的页面都有这种散落的碎装饰，纯装饰不承载信息。
   ⚠️ `.doodle` / `.doodle .ico` 全项目零引用（没删之前是零），已删除 ——
   这套碎装饰最后没有实际用到任何页面上。
   要恢复的话去 `_backup/review-fix-before/app.css` 拿，一共 5 行。 */

/* ============================ 首页（宣传图 + 商品） ============================
   这一版首页**只有两块**：顶部宣传图轮播 + 商品网格。
   原来首页那 5 块的样式（顶栏 / 陈列柜 / 收益条 / 公告 / 礼包货架）已经删掉，
   原版在 `_backup/home-v1/app.css`。
   刻意留下、但首页不再用的：`.mini*`（快捷入口，别的页还在用）、
   `.gift*` / `.gift-art` / `.gift-detail`（礼包，等 `#/shop` 用）。          */

/* ---- 品牌条 ----
   原来这里是粉色大色块的头像条。改版后首页不讲「我是谁」——
   头像 / 等级 / ID 都归「我的」页。这里只留品牌字标 + 邮件入口。 */
.brandbar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; padding: calc(env(safe-area-inset-top) + 15px) var(--pad) 13px;
}
.bb-mark { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.bb-cn {
  font-size: 20px; font-weight: 900; color: var(--ink); line-height: 1;
  letter-spacing: .12em; text-indent: .12em;   /* 末字右侧会被 letter-spacing 撑开，补掉 */
}
.bb-en {
  font-size: 8.5px; font-weight: 700; color: var(--ink-3); line-height: 1;
  letter-spacing: .28em; text-indent: .28em;
}
.bb-mail {
  position: relative; flex: none; width: 38px; height: 38px;
  border-radius: var(--r-sm); display: grid; place-items: center;
  color: var(--ink-2); background: var(--card-2);
  box-shadow: inset 0 0 0 1px var(--line);
  transition: transform .14s ease;
}
.bb-mail:active { transform: scale(.94); }
.bb-mail .ico { width: 19px; height: 19px; }
/* 这里底色是页面纸色，不是白卡，所以徽标描边要用 --paper 而不是 --brand */
.bb-mail .badge { box-shadow: 0 0 0 2px var(--paper); }

/* ---- 宣传图轮播 ----
   用原生横滑 + scroll-snap，不写 JS 动画：手指滑动交给浏览器，
   比用 transform 自己算跟手，也不会跟页面的纵向滚动抢手势。
   指示点在 mount() 里跟着 scrollLeft 更新。

   **通栏出血**（不留内边距、不加圆角），跟下面商品带的 1:1 大图同一套语言。
   上一版是「圆角浮卡 + 卡外圆点」，放在大画册上面像两个不同 App 拼的。 */
.hero { position: relative; margin-top: 2px; }
.hero-track {
  display: flex; gap: 0;
  overflow-x: auto; overflow-y: hidden;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain;   /* 横滑到头别把整页拽走 */
  scrollbar-width: none;
}
.hero-track::-webkit-scrollbar { display: none; }

.hero-slide {
  position: relative; flex: 0 0 auto;
  width: 100%;                /* 通栏 = 430 */
  aspect-ratio: 2 / 1;        /* 430 x 215。promo 源图就是 2:1，零裁切 */
  overflow: hidden;
  scroll-snap-align: start;
  background: var(--sunk);
}
.hero-slide img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* 底部压一层深色。5 张图的背景从浅灰到红到青黄都有，
   没有这层遮罩白字读不出来。 */
.hero-slide::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 66%;
  background: linear-gradient(180deg,
    rgba(10,17,32,0) 0%, rgba(10,17,32,.26) 44%, rgba(10,17,32,.76) 100%);
  pointer-events: none;
}
/* 图上自带文案的（成品海报）压得轻得多。
   那层 .76 的遮罩是为了让左下角的白字读得出来，海报自己有字，
   再压这么重就是把人家设计好的画面糊掉 —— 尤其 promo-5 是浅底。 */
.hero-slide.no-cap::after {
  background: linear-gradient(180deg,
    rgba(10,17,32,0) 0%, rgba(10,17,32,.1) 55%, rgba(10,17,32,.34) 100%);
}
/* 右边留 92px 给叠在图上的指示点，标题不许压到它们 */
.hero-cap {
  position: absolute; left: 20px; right: 92px; bottom: 17px; z-index: 1;
  display: flex; flex-direction: column; gap: 6px;
}
.hero-cap b {
  font-size: 21px; font-weight: 900; color: #fff; line-height: 1.2;
  letter-spacing: .01em; text-shadow: 0 2px 16px rgba(0,0,0,.45);
}
.hero-cap span {
  font-size: 11px; font-weight: 600; color: rgba(255,255,255,.84);
  letter-spacing: .1em;
}

/* 指示点压在图右下角，不再占一行纸色高度 */
.hero-dots {
  position: absolute; right: 18px; bottom: 19px; z-index: 2;
  display: flex; align-items: center; gap: 6px;
  pointer-events: none;
}
.hero-dot {
  width: 6px; height: 6px; border-radius: var(--r-full);
  background: #fff; opacity: .38;
  transition: width .24s cubic-bezier(.22,1,.36,1), opacity .24s ease;
}
.hero-dot.on { width: 19px; opacity: 1; }

/* ---- 商品带（深色通栏画册）----
   上一版是 2 列小格子，海报里自带的「SPIKE GUARDIAN」那行字被缩到看不清，
   图里的排版语言全废了，整页就显廉价。现在反过来：每款一张通栏 1:1 大图，
   让海报自己的字体按原大小露出来。
   整带铺深色，是因为海报都是在深色影棚里拍的 —— 放纸色底上像三张贴纸，
   放深色里才是一本画册。

   ⚠️ margin-top 和上内边距都必须是 0 附近（原来 margin-top 是 28px）。
      宣传图是通栏大图，商品带的第一行是「商品」标题 —— 两者之间原本夹着
      28px 的 margin + 4px 的 padding，实测那 28px 露出来是纸色，一整条亮缝。
      用户看过第一版改法（把缝留在那儿、只是让底色变深）之后明确说了：
      **要的是把这段空间去掉，不是把它的颜色改掉。** 所以现在：
        · margin-top: 0     —— 商品带的深色直接从宣传图底边开始
        · padding-top: 4px  —— 只留 4px，不让「商品」标题贴死在图片上
      别再往这儿加"呼吸感"了，用户要的就是紧。 */
.prodband {
  margin-top: 0;
  padding: 4px 0 34px;
  background: linear-gradient(176deg, #24365C 0%, var(--contra) 38%, #0B1220 100%);
}
.prodband .sec-title { color: #fff; }
.prodband .sec-sub   { color: rgba(255,255,255,.46); }

/* ---- 首页：把底部那段留白**去掉**（不是把它涂成深色）----
   首页最后一块是深色商品带，但 .view 还要为 tab 栏留底部留白，
   那段露出来是纸色：深色带 → 一条纸色 → tab 栏，中间一条亮边。

   ⚠️ 第一版改法是给 .view 铺一层渐变，把这段纸色的**颜色**换成深色。
      用户否了：**要的是把这段空间去掉，不是把颜色改掉。**
      所以现在改成直接压掉 .view 为「底部悬浮球」额外留的那 38px
      （--foot-extra 归零，见 base.css），商品带的底边就会正好落在 tab 栏顶边上。

   为什么首页敢压到 0：
     · --view-pad-b 里剩下的 66px 是 tab 栏本体的高度，压掉的是 extra 那部分；
     · 首页最后一样东西是「实物款式由运营与店长线下发放」那行说明，
       它在商品带自己的 34px 下内边距里面 —— 深色带贴到 tab 栏顶边之后，
       这行字离 tab 栏顶还有 34px，不会被悬浮球（高出 tab 栏 26px）压住。

   ⚠️ 只对首页开。别的页面最后一块直接贴着底边，压到 0 会被悬浮球压住内容 ——
      这个是实测过的，不是猜的（_bottomcheck.html 会量这条）。

   ⚠️ 覆盖点必须落在 **html 自己**身上，不能写 `html[data-page="home"] .view`。
      --view-pad-b 是在 base.css 的 :root 里算好的，其中的 var(--foot-extra)
      在 **html 这一层**就已经替换成具体数值，然后才作为「计算值」继承给 .view。
      所以改 .view 身上的 --foot-extra 完全没用 —— 第一版就是这么写的，
      实测 padding-bottom 还是 104px，白改一轮。 */
html[data-page="home"] { --foot-extra: 0px; }

/* ---- 商品切换条 ----
   同品类一次只看一款，切换靠这一排缩略图。
   上一版把 3 款各带大图 + 细节图全堆下来，整页 3300px —— 那是商品详情，不是首页。
   缩略图用各款自己的主图，让人先看到「切过去会得到什么」，比纯文字 tab 有用。 */
.prod-switch {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px;
  padding: 2px var(--pad) 0;
}
.ps { display: block; padding: 0; border: 0; background: none; }

.ps-art {
  display: block; position: relative; aspect-ratio: 1 / 1;
  border-radius: var(--r-md); overflow: hidden;
  background: #16213A;
  /* 用 outline 而不是 inset box-shadow：inset 阴影画在背景之上、内容之下，
     会被里头的 <img> 整个盖住，看不出来。outline 画在最后，压得住。
     也不要改成 border —— border 会改变盒子尺寸，三个缩略图就不齐了。 */
  outline: 2px solid transparent; outline-offset: -2px;
  /* 0.4 太暗了 —— 看着像「不可用」而不是「可以切过去」。
     0.58 还分得出主次，但两款都认得出来是什么。 */
  opacity: .58; filter: saturate(.5);
  transition: opacity .2s ease, filter .2s ease, outline-color .2s ease, transform .16s ease;
}
.ps-art img { width: 100%; height: 100%; object-fit: cover; display: block; }
.ps.on .ps-art { opacity: 1; filter: none; outline-color: var(--pc, #fff); }
.ps:active .ps-art { transform: scale(.96); }

.ps-name {
  display: block; margin-top: 9px; text-align: center;
  font-size: 11.5px; font-weight: 700; letter-spacing: .04em;
  color: rgba(255,255,255,.4);
  transition: color .2s ease;
}
.ps.on .ps-name { color: #fff; }

/* 切换时正文淡入。整块换 innerHTML 不会重播动画，
   所以 JS 那边靠「摘掉 .in → 强制重排 → 加回 .in」重新触发。 */
.prod-panel { margin-top: 22px; }
.prod-panel.in { animation: prod-in .3s cubic-bezier(.22,1,.36,1); }
@keyframes prod-in {
  from { opacity: 0; transform: translateY(9px); }
  to   { opacity: 1; transform: none; }
}

/* 通栏出血：不留左右内边距、不加圆角。这是整页最重的一击，
   圆角+白边会立刻把它降格成一张普通卡片。 */
.prod-hero-btn {
  display: block; width: 100%; padding: 0; border: 0; background: none;
  position: relative;
}
.prod-hero {
  display: block; width: 100%; aspect-ratio: 1 / 1;
  object-fit: cover; background: #16213A;
}
/* 「看细节」提示。
   ⚠️ 它**不在图上** —— 海报自带的「Blue Orange Series」是水平居中的，
   角标贴左下或右下都会撞上那行字（实测重叠约 16px）。
   放进信息行右侧则高度零增加，三款都不会撞。
   细节图本身不摆在首页上，一摆就又变成商品详情了。 */
.prod-meta {
  display: flex; align-items: center; gap: 12px;
  margin-top: 11px;
}
.prod-more {
  flex: none; display: inline-flex; align-items: center; gap: 4px;
  padding: 2px 0; border: 0; background: none;
  font-size: 11.5px; font-weight: 700; letter-spacing: .04em;
  color: var(--pc, #fff);            /* 用这款自己的强调色，跟系列名呼应 */
}
.prod-more .ico { width: 13px; height: 13px; }
.prod-more:active { opacity: .6; }

.prod-info { padding: 19px var(--pad) 0; }

.prod-kicker {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px; margin-bottom: 10px;
}
/* 系列名用这款自己的强调色（--pc 由页面从数据里挂上来），
   取自它海报里的主色 —— 三款各一个色，一眼能分开。 */
.prod-series {
  font-size: 9.5px; font-weight: 800; letter-spacing: .22em;
  text-transform: uppercase; color: var(--pc, #fff);
}
.prod-rar {
  flex: none; font-size: 9.5px; font-weight: 800; letter-spacing: .1em;
  line-height: 1; padding: 4px 10px; border-radius: var(--r-full); color: #fff;
}
/* 稀有度配色只有这一处真相来源：theme.css 里的 --rar-*-1/2/3。
   取 2→3（中阶到暗阶）是为了白字压得住 —— 1 阶太浅。 */
[data-rk="legend"] { background: linear-gradient(135deg, var(--rar-legend-2), var(--rar-legend-3)); }
[data-rk="epic"]   { background: linear-gradient(135deg, var(--rar-epic-2),   var(--rar-epic-3));   }
[data-rk="rare"]   { background: linear-gradient(135deg, var(--rar-rare-2),   var(--rar-rare-3));   }
[data-rk="normal"] { background: linear-gradient(135deg, var(--rar-normal-2), var(--rar-normal-3)); }

/* 中文名做主标、英文名做副标。海报里已经把英文排得很足了，
   这里换成中文才不会跟图里的字打架。 */
.prod-cn {
  font-size: 28px; font-weight: 900; color: #fff;
  line-height: 1.08; letter-spacing: .05em;
}
.prod-en {
  margin-top: 7px; font-size: 10px; font-weight: 700;
  letter-spacing: .2em; text-transform: uppercase; color: rgba(255,255,255,.42);
}
.prod-desc {
  flex: 1; min-width: 0; margin: 0;
  font-size: 12.5px; font-weight: 500;
  color: rgba(255,255,255,.6); letter-spacing: .02em;
}

/* 点大图后的细节抽屉。图片逐张竖排，不做左右翻页 —— 几张图不值得再写一套手势。 */
.gal { display: flex; flex-direction: column; gap: 14px; }
.gal figure { margin: 0; }
.gal img { width: 100%; display: block; border-radius: var(--r-md); background: var(--sunk); }
.gal figcaption {
  margin-top: 8px; font-size: 11.5px; font-weight: 700;
  letter-spacing: .08em; color: var(--ink-2);
}

/* ---- 详情抽屉顶部的副标语 / 短介绍 ----
   这两段原来挂在首页商品带上，用户看过之后否了：首页那块是**深色通栏画册**，
   一屏要并排三款，再塞两段文字就头重脚轻，把「图」这个主角压掉了。
   挪到详情页之后它们成了正文，所以颜色整套换掉 —— 这里是**浅色**抽屉，
   跟深色商品带上的 rgba(255,255,255,*) 完全不是一套。

   .gal-head 的下边距跟 .gal 的 gap 取同一个值（14px），
   这样「文案块 → 第一张图」的间距和「图 → 图」的间距一致，不会多出一道缝。 */
.gal-head { margin-bottom: 14px; }
.gal-tagline {
  font-size: 14px; font-weight: 800;
  letter-spacing: .14em; color: var(--ink);
}
.gal-intro {
  margin-top: 9px;
  font-size: 12.5px; font-weight: 500;
  line-height: 1.78; letter-spacing: .02em;
  color: var(--ink-2);
}

.prod-cta { padding: 18px var(--pad) 0; }
/* 深色带上的按钮：用玻璃质感而不是实心粉 —— 实心粉在这个带子里会抢戏，
   而且「购买」这个动作本身就应该克制（真正掏钱发生在线下）。 */
.btn-light {
  width: 100%; height: 51px;
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  border-radius: var(--r-md); color: #fff;
  font-size: 14.5px; font-weight: 800; letter-spacing: .06em;
  background: rgba(255,255,255,.08);
  box-shadow: inset 0 0 0 1.5px rgba(255,255,255,.3);
  transition: transform .14s ease, background .14s ease;
}
.btn-light:active { transform: scale(.975); background: rgba(255,255,255,.14); }
.btn-light .ico { width: 18px; height: 18px; }

.prod-note {
  margin: 30px var(--pad) 0; padding-top: 22px;
  border-top: 1px solid rgba(255,255,255,.1);
  font-size: 11.5px; line-height: 1.7; color: rgba(255,255,255,.38);
}

/* 细节图点开的大图 */
/* ============================ 卡牌（核心组件） ============================ */
/* 这是本次改动的重点。上一版是「一张白卡包住图 + 图下面再挂三行字」，
   又高又窄像条，而且文字和图是分离的两块。
   现在：真卡牌比例(5:7)，图铺满整张卡，所有信息压在卡面上，卡下什么都没有。 */
.card {
  position: relative; display: block;
  width: 100%; aspect-ratio: 5 / 7;   /* 真正的卡牌比例 2.5 : 3.5 */
  padding: 3px;                        /* 这 3px 就是卡框厚度 */
  border: 0; cursor: pointer;
  border-radius: var(--r-md);
  /* 卡身 = 稀有度金属渐变。不用 mask 描边，直接让卡自己当框，兼容性更稳 */
  background: linear-gradient(158deg,
    var(--rc1, #D5DAE4) 0%,
    var(--rc2, #9AA0AE) 40%,
    var(--rc3, #6F7686) 70%,
    var(--rc1, #D5DAE4) 100%);
  box-shadow: var(--sh-lift);
  transition: transform .16s cubic-bezier(.2,.9,.3,1);
  -webkit-tap-highlight-color: transparent;
  text-align: left;
}
.card:active { transform: scale(.955); }

/* 稀有度四档 —— 颜色本身就是信息，玩家扫一眼就知道价值 */
.card.r-normal { --rc1: var(--rar-normal-1); --rc2: var(--rar-normal-2); --rc3: var(--rar-normal-3); }
.card.r-rare   { --rc1: var(--rar-rare-1);   --rc2: var(--rar-rare-2);   --rc3: var(--rar-rare-3); }
.card.r-epic   { --rc1: var(--rar-epic-1);   --rc2: var(--rar-epic-2);   --rc3: var(--rar-epic-3); }
.card.r-legend {
  --rc1: var(--rar-legend-1); --rc2: var(--rar-legend-2); --rc3: var(--rar-legend-3);
  box-shadow: var(--sh-glow-legend), var(--sh-lift);   /* 传说卡带金辉 */
}

/* 卡内层：照片铺满。
   用绝对定位而不是 height:100% —— <button> 里的百分比高度在各家 WebView 上
   表现不一致，绝对定位是稳的。 */
.card-in {
  position: absolute; inset: 3px;
  display: block;
  border-radius: calc(var(--r-md) - 3px);
  overflow: hidden;
  background: var(--card-2);
  isolation: isolate;
}
.card-in .art-img {
  width: 100%; height: 100%; display: block;
  object-fit: cover; object-position: center 34%;
  transition: transform .35s cubic-bezier(.2,.9,.3,1);
}
.card:active .card-in .art-img { transform: scale(1.05); }
/* 没有真图时退回 SVG 公仔：淡色展示盒 */
.card-in .toy-back {
  position: absolute; inset: 0;
  background:
    radial-gradient(130% 76% at 50% 0%, rgba(255,255,255,.5), transparent 62%),
    var(--c, var(--paper-2));
  display: flex; align-items: flex-end; justify-content: center;
  padding: 8px 6px 0;
}
.card-in .toy-back .toy { width: 100%; height: auto; display: block; }

/* 材料卡：没有实物图，用符号居中压在淡色底上（开盒翻牌翻出来的就是它）
   选择器必须是 .card-in .toy-back.card-glyph-box 这个长度：
   上面 .card-in .toy-back 是两个类，只写一个类压不过它，
   符号会被 flex-end 推到右下角，正好压在卡名的位置上。 */
.card-in .toy-back.card-glyph-box { align-items: center; padding: 0; }
.card-in .card-glyph {
  font-size: 52px; line-height: 1; font-weight: 700;
  text-shadow: 0 4px 16px rgba(255,255,255,.9);
}

/* 卡面压字用的暗角：只在底部，不能糊住公仔 */
.card-scrim {
  position: absolute; left: 0; right: 0; bottom: 0; height: 58%;
  background: linear-gradient(180deg,
    rgba(12,16,28,0) 0%,
    rgba(12,16,28,.32) 44%,
    rgba(12,16,28,.82) 100%);
  pointer-events: none;
}
/* 顶部再来一点，保证稀有度芯片在浅色照片上也读得清 */
.card-top {
  position: absolute; left: 0; right: 0; top: 0; height: 30%;
  background: linear-gradient(180deg, rgba(12,16,28,.34), rgba(12,16,28,0));
  pointer-events: none;
}

.card-rar {
  position: absolute; top: 7px; left: 7px; z-index: 2;
  font-size: 9.5px; font-weight: 800; letter-spacing: .02em;
  padding: 3px 8px; border-radius: var(--r-full);
  color: #fff;
  background: linear-gradient(150deg, var(--rc1), var(--rc3));
  box-shadow: 0 2px 6px rgba(12,16,28,.32);
}
.card-days {
  position: absolute; top: 7px; right: 7px; z-index: 2;
  font-family: var(--font-num);
  font-size: 9.5px; font-weight: 700;
  padding: 3px 7px; border-radius: var(--r-full);
  background: rgba(12,16,28,.42); color: rgba(255,255,255,.95);
  -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px);
}
.card-foot {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 2;
  padding: 8px 9px 9px;
  display: block;
}
.card-name {
  display: block;
  font-size: 13px; font-weight: 800; color: #fff;
  letter-spacing: -.01em; line-height: 1.2;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  text-shadow: 0 1px 4px rgba(12,16,28,.5);
}
.card-yield {
  display: inline-flex; align-items: center; gap: 3px;
  margin-top: 4px;
  font-family: var(--font-num); font-size: 11px; font-weight: 800;
  color: #FFD86B;
  text-shadow: 0 1px 4px rgba(12,16,28,.55);
}
.card-yield .ico { width: 11px; height: 11px; }
.card-yield em { font-style: normal; font-weight: 600; opacity: .78; font-size: 10px; }

/* 空位：虚线卡框 + 居中引导。9 张上限的商业引导就藏在这里 */
.card.is-empty {
  /* 空卡位是**虚线空框**，不放卡背。
     我一度在这里铺了卡背（6 个空位 = 6 张背面朝上的牌），被否掉了：
     9 宫格里 6 张灰牌 + 3 张真卡，空位反而比真卡抢眼，
     而空位的职责是「提示你去激活」，不是当主角。
     **卡背只用在开盒翻牌**（.flip-cover），别再往这里加。 */
  background: none;
  border: 1.5px dashed #D3CBBC;
  padding: 0;
  box-shadow: none;
}
.card.is-empty .card-in {
  background: transparent;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 7px;
}
.card-lock { color: #C9C2B5; display: block; }
.card-lock .ico { width: 26px; height: 26px; display: block; }
.card-empty-t {
  display: block; text-align: center;
  font-size: 11.5px; font-weight: 700; color: var(--ink-3);
}
.card-empty-s {
  display: block; text-align: center;
  font-size: 9.5px; color: #BDB6A9; line-height: 1.35;
}
.card.is-empty:active { transform: scale(.97); }

/* 卡册网格 */
.cardgrid {
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: 11px; padding: 0 var(--pad);
}

/* ===================== 「我的空偶卡」未激活落地页 =====================
   一张卡都没有时整页换成这个（pages.js → cardLocked()）。
   原来的 3×3 虚线空册 + 一排 0 的收益条，对还没入门的玩家是
   一整屏「你什么都没有」；他要的是知道这东西是什么、怎么拿到第一张。 */

/* 宣传图通栏出血。box-hero 是 4:3、纯图无字，所以不加遮罩、不压标题 ——
   标题放在图**下面**（.lock-head）。压上去就把它变成一张「带字的图」了。

⚠️ 高度**故意不是原图的 4:3，而是 16:10**。
   4:3 在 430 宽的机器上算出 293px —— 吃掉首屏（#view 高约 588）的一半，
   把「用 CDK 激活」挤到了首屏之外（实测要滚 208px）。
   16:10 是 244px，省下 49px，375×667 这种小屏才放得下主按钮。
   裁切交给 CSS 的 object-fit，**不动原图文件**（原图是用户给的群像，
   从 4:3 硬裁成 16:9 会切掉头顶和鞋，16:10 只切掉脚下一线）。
   object-position 取 22% 而不是居中：这张图的头顶本来就贴着上边缘。 */
.lock-hero {
  margin: 0 var(--pad);
  aspect-ratio: 16 / 10;
  border-radius: var(--r-lg);
  overflow: hidden;
  background: var(--paper-2);
}
.lock-hero img {
  width: 100%; height: 100%; display: block;
  object-fit: cover; object-position: center 22%;
}

.lock-head { padding: 18px var(--pad) 0; }
.lock-title {
  font-size: 23px; font-weight: 800; color: var(--ink);
  letter-spacing: -.02em; line-height: 1.3;
}
.lock-sub {
  margin-top: 7px;
  font-size: 13px; color: var(--ink-2); line-height: 1.6;
}

/* 空偶卡「商品」：左边卡背、右边规格。
   ⚠️ 用**卡背**不是某张卡的正面 —— 正面会让人以为激活就送这一张。 */
.lock-prod {
  display: flex; align-items: center; gap: 16px;
  margin: 14px var(--pad) 0;
  padding: 16px;
  border-radius: var(--r-lg);
  background: var(--card);
  box-shadow: 0 2px 10px rgba(16,22,38,.06), 0 12px 30px rgba(16,22,38,.07);
}
.lock-card {
  flex: none; width: 92px;
  /* 卡背源图 600×852 = 0.704，跟卡册里的卡一致 */
  aspect-ratio: 704 / 1000;
  border-radius: var(--r-sm);
  overflow: hidden;
  box-shadow: 0 4px 14px rgba(16,22,38,.18);
}
.lock-card img { width: 100%; height: 100%; object-fit: cover; display: block; }

.lock-info { flex: 1; min-width: 0; }
.lock-name { font-size: 16px; font-weight: 800; color: var(--ink); }
.lock-desc {
  margin: 6px 0 10px;
  font-size: 12px; color: var(--ink-3); line-height: 1.55;
}
.lock-spec { border-top: 1px solid var(--line); padding-top: 8px; }
.lock-row {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px; padding: 4px 0;
  font-size: 12px;
}
.lock-row span { color: var(--ink-3); }
.lock-row b { color: var(--ink); font-weight: 700; font-variant-numeric: tabular-nums; }

.lock-cta {
  display: grid; gap: 10px;
  margin: 18px var(--pad) 0;
}

/* 大卡：卡详情弹窗用。
   比例跟着源图 0.62 走 —— 网格里 5:7 的卡是「陈列」，膝下裁掉一点没关系；
   详情里是「鉴赏」，必须全身都在。 */
.bigcard {
  width: 154px; margin: 0 auto 18px;
  padding: 4px;
  border-radius: var(--r-lg);
  aspect-ratio: 62 / 100;
}
.bigcard .card-in { inset: 4px; border-radius: calc(var(--r-lg) - 4px); }
.bigcard .card-lock .ico { width: 30px; height: 30px; }
.bigcard .card-name { font-size: 16px; }
.bigcard .card-yield { font-size: 13px; }
.bigcard .card-rar { font-size: 11px; padding: 4px 10px; top: 9px; left: 9px; }
.bigcard .card-days { font-size: 11px; top: 9px; right: 9px; }
.bigcard .card-foot { padding: 11px 12px 12px; }

/* ============================ 章节 ============================ */
.sec { margin-top: 26px; }
.sec-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 12px; padding: 0 var(--pad); margin-bottom: 13px;
}
.sec-title {
  font-size: 17.5px; font-weight: 800; color: var(--ink);
  letter-spacing: -.01em;
  display: inline-flex; align-items: center; gap: 8px;
}
.sec-title::before {
  content: ''; width: 4px; height: 16px; border-radius: var(--r-full);
  background: linear-gradient(180deg, var(--brand), var(--purple));
}
.sec-sub { font-size: 11.5px; color: var(--ink-3); font-weight: 500; }
.sec-more {
  font-size: 12.5px; font-weight: 700; color: var(--brand);
  white-space: nowrap; display: inline-flex; align-items: center; gap: 2px; flex: none;
}
.sec-more .ico { width: 13px; height: 13px; }

/* ============================ 按钮 ============================ */
/* 上一版是纯色胶囊 + 2px 位移。现在给「厚度」：
   顶部内高光（受光）+ 底部实心投影（体积）+ 按下时投影压缩（真实反馈）。 */
.btn {
  position: relative;
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  height: 50px; padding: 0 26px;
  border: 0; border-radius: var(--r-full);
  font-family: var(--font); font-size: 15px; font-weight: 800;
  letter-spacing: .02em; cursor: pointer; overflow: hidden;
  transition: transform .1s cubic-bezier(.2,.9,.3,1), box-shadow .1s ease;
  -webkit-tap-highlight-color: transparent;
}
/* 上半部一层白，做出圆柱受光；指针事件关掉，免得挡住点击 */
.btn::before {
  content: ''; position: absolute; left: 0; right: 0; top: 0; height: 52%;
  background: linear-gradient(180deg, rgba(255,255,255,.30), rgba(255,255,255,0));
  pointer-events: none;
}
.btn > * { position: relative; z-index: 1; }
.btn:active { transform: translateY(3px); }
.btn-block { width: 100%; }
.btn-sm { height: 40px; padding: 0 18px; font-size: 13.5px; border-radius: var(--r-full); }
.btn-xs { height: 32px; padding: 0 13px; font-size: 12px; border-radius: var(--r-full); }

.btn-primary {
  background: linear-gradient(180deg, #FF74A6 0%, var(--brand) 52%, var(--brand-d) 100%);
  color: #fff;
  box-shadow: 0 5px 0 var(--brand-d), var(--sh-glow-brand);
}
.btn-primary:active { box-shadow: 0 2px 0 var(--brand-d), 0 4px 12px -6px rgba(255,77,141,.55); }

.btn-gold {
  background: linear-gradient(180deg, #FFE086 0%, var(--yellow) 52%, #EAA312 100%);
  color: #563200;
  box-shadow: 0 5px 0 #C98A12, 0 10px 22px -8px rgba(255,201,60,.6);
}
.btn-gold:active { box-shadow: 0 2px 0 #C98A12, 0 4px 12px -6px rgba(255,201,60,.5); }

.btn-contra {
  background: linear-gradient(180deg, #33486F 0%, var(--contra) 54%, var(--contra-d) 100%);
  color: #fff;
  box-shadow: 0 5px 0 var(--contra-d), 0 10px 22px -10px rgba(27,42,74,.75);
}
.btn-contra:active { box-shadow: 0 2px 0 var(--contra-d), 0 4px 12px -8px rgba(27,42,74,.6); }

.btn-ghost {
  background: var(--card); color: var(--ink);
  box-shadow: inset 0 0 0 1.5px var(--line), 0 3px 0 rgba(27,34,51,.07);
}
.btn-ghost:active { box-shadow: inset 0 0 0 1.5px var(--line), 0 1px 0 rgba(27,34,51,.07); }

/* 危险动作（退出登录）：描边式而不是实心红。
   实心红太抢眼，退出登录不该比主操作更显眼。 */
.btn-danger {
  background: var(--card); color: #D2354F;
  box-shadow: inset 0 0 0 1.5px rgba(210,53,79,.28), 0 3px 0 rgba(210,53,79,.10);
}
.btn-danger:active { box-shadow: inset 0 0 0 1.5px rgba(210,53,79,.28), 0 1px 0 rgba(210,53,79,.10); }

.btn:disabled, .btn[disabled] {
  background: var(--paper-2); color: var(--ink-3);
  box-shadow: none; cursor: not-allowed; transform: none;
}
.btn:disabled::before, .btn[disabled]::before { display: none; }
/* 按钮里的虚粒图标必须跟按钮文字同色，否则粉色数字落在粉色底上看不见 */
.btn .gem, .btn .gem b { color: currentColor; }
.btn .gem .ico { width: 15px; height: 15px; }
.btn .ico { width: 17px; height: 17px; }

/* ============================ 快捷入口 ============================ */
.mini-row {
  display: grid; grid-template-columns: repeat(4, 1fr);
  gap: 10px; margin: 22px var(--pad) 0;
}
.mini {
  position: relative;
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  padding: 15px 4px 13px;
  border-radius: var(--r-lg);
  background: var(--card); box-shadow: var(--sh-tap);
  cursor: pointer; transition: transform .12s ease;
  overflow: hidden;
}
/* 每格顶部一条同色细线，把四个入口从「四张一样的小白卡」里拉出来 */
.mini::after {
  content: ''; position: absolute; left: 18%; right: 18%; top: 0; height: 3px;
  border-radius: 0 0 var(--r-full) var(--r-full);
  background: var(--mc, var(--brand));
}
.mini:active { transform: scale(.955); }
.mini-ico {
  width: 42px; height: 42px; border-radius: var(--r-sm);
  display: grid; place-items: center;
  color: #fff;
  background: linear-gradient(158deg,
    color-mix(in srgb, var(--mc, var(--brand)) 70%, #fff),
    var(--mc, var(--brand)));
  box-shadow: 0 6px 14px -6px var(--mc, var(--brand));
}
.mini-ico .ico { width: 21px; height: 21px; }
.mini-label { font-size: 11.5px; font-weight: 700; color: var(--ink-2); }

/* ============================ 礼包货架 ============================ */
.gift-row {
  display: flex; gap: 12px;
  padding: 0 var(--pad);
  overflow-x: auto; -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.gift-row::-webkit-scrollbar { display: none; }
.gift {
  position: relative;
  flex: 0 0 200px;
  background: var(--card); border-radius: var(--r-lg);
  box-shadow: var(--sh-tap); overflow: hidden;
  cursor: pointer;
  transition: transform .14s ease;
}
.gift:active { transform: scale(.97); }
.gift-art {
  position: relative; height: 132px;
  background: var(--card-2);
  display: grid; place-items: center; overflow: hidden;
}
.gift-tag {
  position: absolute; top: 9px; left: 9px; z-index: 2;
  font-size: 10px; font-weight: 800;
  padding: 4px 9px; border-radius: var(--r-full);
  background: linear-gradient(150deg, #33486F, var(--contra));
  color: #fff;
  box-shadow: 0 2px 6px rgba(12,16,28,.28);
}
.gift-body { padding: 12px 14px 14px; }
.gift-name {
  font-size: 13.5px; font-weight: 800; color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.gift-sub { font-size: 11.5px; color: var(--ink-3); margin-top: 5px; line-height: 1.45; }

/* ============================ 导航栏（二级页） ============================ */
.navbar {
  position: sticky; top: 0; z-index: 30;
  /* 右侧必须是 auto：放虚粒余额这种变长内容，固定宽度会被裁掉 */
  display: grid; grid-template-columns: 46px minmax(0, 1fr) auto;
  align-items: center;
  padding: calc(env(safe-area-inset-top) + 10px) 10px 10px;
  background: rgba(247,243,237,.86);
  -webkit-backdrop-filter: blur(18px); backdrop-filter: blur(18px);
  border-bottom: 1px solid var(--line);
}
.nav-back {
  width: 42px; height: 42px; border-radius: var(--r-full);
  background: var(--card); box-shadow: var(--sh-tap);
  display: grid; place-items: center; color: var(--ink);
  transition: transform .12s ease;
}
.nav-back:active { transform: scale(.92); }
.nav-back .ico { width: 20px; height: 20px; transform: rotate(180deg); }
.nav-title { text-align: center; font-size: 16px; font-weight: 800; letter-spacing: -.01em; }
.nav-right { text-align: right; padding-right: 12px; white-space: nowrap; }
.nav-note { font-size: 12px; color: var(--ink-3); font-weight: 600; }
.nav-note b { font-family: var(--font-num); color: var(--ink); }

/* ---- 一级页面（底部 tab 那几页）的顶栏 ----
   没有返回键，标题靠左。
   · 列数从 3 列减到 2 列 —— 那个 46px 的返回键列必须一起去掉，
     留着的话标题左边会空出一整块，比原来更怪。
   · 标题补 10px：.navbar 自己的左右内边距是 10px，而正文用的是 var(--pad)=20px。
     不补的话标题会比下面的内容往左凸 10px，一眼就不齐。
   · .nav-right 同理，从 12px 收到 10px，让右边的虚粒余额和标题一条线对齐。 */
.navbar.top { grid-template-columns: minmax(0, 1fr) auto; }
.navbar.top .nav-title { text-align: left; padding-left: 10px; }
.navbar.top .nav-right { padding-right: 10px; }

/* ============================ 底部 tab ============================ */
.tabbar {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 40;
  display: grid; grid-template-columns: repeat(5, 1fr);
  align-items: end;
  height: calc(var(--tabbar-h) + env(safe-area-inset-bottom));
  padding-bottom: env(safe-area-inset-bottom);
  background: rgba(255,255,255,.95);
  -webkit-backdrop-filter: blur(20px); backdrop-filter: blur(20px);
  border-top: 1px solid var(--line);
  border-radius: var(--r-xl) var(--r-xl) 0 0;
}
.tab {
  position: relative;
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  height: var(--tabbar-h);
  justify-content: center;
  color: var(--ink-3); cursor: pointer;
  font-family: var(--font);
}
.tab .ico { width: 22px; height: 22px; }
.tab-label { font-size: 10.5px; font-weight: 700; }
.tab.on { color: var(--brand); }
.tab.on .tab-label { font-weight: 800; }
/* 选中态：图标后面垫一个淡粉圆角块 */
.tab.on .tab-ico {
  background: var(--brand-l);
  border-radius: var(--r-sm);
  padding: 4px 12px;
}
.tab-ico {
  display: grid; place-items: center;
  padding: 4px 12px;
  transition: background .18s ease;
}

/* 中间的开盒做成悬浮球 —— 手游和潮玩 App 都这么干，
   一是把最重要的动作拎出来，二是打破「五个一样的 tab」这种板正 */
.tab-hero { overflow: visible; }
.tab-hero .tab-ico {
  width: 54px; height: 54px; padding: 0;
  margin-top: -26px;
  border-radius: var(--r-full);
  background: linear-gradient(165deg, #FF74A6 0%, var(--brand) 48%, #C9308C 100%);
  color: #fff;
  box-shadow: 0 8px 18px -6px rgba(255,77,141,.75), 0 0 0 4px #fff;
  animation: heroPulse 2.6s ease-in-out infinite;
}
.tab-hero .tab-ico .ico { width: 27px; height: 27px; }
.tab-hero .tab-label { margin-top: 5px; color: var(--brand); font-weight: 800; }
.tab-hero:active .tab-ico { transform: scale(.93); }
.tab-hero.on .tab-ico { background: linear-gradient(165deg, #FF74A6 0%, var(--brand) 48%, #C9308C 100%); }
@keyframes heroPulse {
  0%, 100% { box-shadow: 0 8px 18px -6px rgba(255,77,141,.75), 0 0 0 4px #fff; }
  50%      { box-shadow: 0 8px 24px -4px rgba(255,77,141,.95), 0 0 0 4px #fff; }
}

/* ---- 悬浮球里的角色行走动画（纯色剪影版）----
   资源是 8 帧 100ms 的动画 WebP（assets/tab-walk-white.webp，20KB），
   由 app.js 在**非 reduced-motion** 时才插入。

   为什么是全彩改剪影：
   46px 下全彩版的眼睛/手套/帽衫褶皱全糊在一起，远看只是一团灰白噪点，
   当图标用读不出形状。剪影只留外轮廓，小尺寸下反而最清楚 ——
   而且体积从 50KB 掉到 20KB。

   三条都是踩过才写下的：
   1. 必须 absolute 定位。.tab-ico 是 `display:grid; place-items:center`，
      直接塞两个子元素（svg + img）会变成**两行**，把圆球撑高、球心跑掉。
      绝对定位 + inset:0 + margin:auto 才能两者重叠居中。
   2. 尺寸 50px 是配 54px 球径量出来的：剪影是实心块，比线框「重」，
      可以比原来的 27px 图标大得多；再大头顶和脚就贴到球边了。
   3. 颜色是白色，和原来那个线框图标同色 —— 球是品牌粉渐变，
      白剪影的对比度最高。换成 tab-walk-navy / tab-walk-ink 只需改 app.js 里
      那个 src，CSS 不用动。 */
.tab-hero .tab-ico { position: relative; }
.tab-hero .tab-walk {
  position: absolute; inset: 0; margin: auto;
  width: 50px; height: 50px;
  pointer-events: none;                /* 别让图片吃掉点击，否则球按不动 */
}
/* 图**加载成功后**才藏掉原来的线框图标（app.js 加 .has-walk）。
   加载失败时这条不生效，球里仍然是原来那个盒子图标，不会变成空白。 */
.tab-ico.has-walk .ico { display: none; }

/* ============================ 数据条 ============================ */
.stat-strip {
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
  margin: 0 var(--pad); padding: 18px 0;
  background: var(--card); border-radius: var(--r-lg); box-shadow: var(--sh-tap);
}
.stat-strip > div { text-align: center; position: relative; }
.stat-strip > div + div::before {
  content: ''; position: absolute; left: 0; top: 12%; bottom: 12%;
  width: 1px; background: var(--line);
}
.stat-strip b {
  display: block; font-family: var(--font-num);
  font-size: 22px; font-weight: 800; color: var(--ink); letter-spacing: -.02em;
}
.stat-strip span { display: block; margin-top: 5px; font-size: 11.5px; color: var(--ink-3); font-weight: 500; }

/* ============================ 开盒 ============================ */
/* 腾讯的抽盒页布局：上=产品展示台，下=信息与操作 */
.box-hero {
  margin: 0 var(--pad);
  background: var(--card); border-radius: var(--r-xl);
  box-shadow: var(--sh-soft);
  padding: 20px 18px 20px;
  text-align: center;
  position: relative; overflow: hidden;
}
/* 展示台底下的彩色光晕：让盒子像被聚光灯照着 */
.box-hero::before {
  content: ''; position: absolute; left: 50%; top: 34%;
  width: 260px; height: 260px; margin-left: -130px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(139,92,246,.22), rgba(255,77,141,.10) 46%, transparent 70%);
  pointer-events: none;
}
/* ---- 开盒台：合照当台面，盒子从照片下沿「冒出来」 ---- */
.box-stage {
  position: relative;
  aspect-ratio: 4 / 3;
  padding: 0; margin-bottom: 44px;
  border-radius: var(--r-lg);
  overflow: visible;
}
.box-art {
  width: 100%; height: 100%; display: block;
  border-radius: var(--r-lg);
  object-fit: cover; object-position: center 46%;
  box-shadow: var(--sh-soft);
}
/* 盒子挂在照片右下角 —— 放正中间会挡住合照里的主角 */
.box-cube {
  position: absolute; right: 20px; bottom: -30px;
  width: 84px; height: 84px;
  animation: bob 3.4s ease-in-out infinite;
}
/* 三层错位 = 未拆的一摞盒子；最前面一层才是「正在开的这个」 */
.cube-side {
  position: absolute; inset: 0; border-radius: 24px;
  background: linear-gradient(150deg, var(--purple), var(--brand));
}
.cube-side:nth-child(1) { transform: translate(-14%, -14%); opacity: .18; }
.cube-side:nth-child(2) { transform: translate(-9.5%, -9.5%); opacity: .28; }
.cube-side:nth-child(3) { transform: translate(-4.5%, -4.5%); opacity: .40; }
.cube-front {
  position: absolute; inset: 0; border-radius: 24px;
  background:
    radial-gradient(120% 80% at 50% 6%, rgba(255,255,255,.55), transparent 62%),
    linear-gradient(150deg, var(--brand), var(--purple));
  display: grid; place-items: center;
  box-shadow: 0 16px 34px rgba(255,77,141,.35);
}
.cube-front .ico { width: 38px; height: 38px; color: #fff; }
@keyframes bob {
  0%, 100% { transform: translateY(0) rotate(-2deg); }
  50%      { transform: translateY(-9px) rotate(2deg); }
}
.box-price {
  font-size: 12.5px; color: var(--ink-2); font-weight: 500;
  margin-bottom: 18px;
}
.box-price .gem { vertical-align: -2px; }
.box-actions { display: flex; gap: 11px; position: relative; z-index: 1; }
.box-actions .btn { flex: 1; }
.box-note { font-size: 11.5px; color: var(--ink-3); margin-top: 15px; line-height: 1.6; }

/* ---- 翻牌开盒：整个流程的主角 ---- */
.flip-stage {
  position: fixed; inset: 0; z-index: 70;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 22px;
  padding: 30px var(--pad) calc(30px + env(safe-area-inset-bottom));
  background: radial-gradient(120% 90% at 50% 30%, #2B1B4D 0%, #120E22 62%, #0A0814 100%);
  opacity: 0; transition: opacity .26s ease;
}
.flip-stage.on { opacity: 1; }
/* 背景散射的星光，纯装饰，营造「开出来一个好东西」的仪式感 */
.flip-stage::before {
  content: ''; position: absolute; inset: 0;
  background:
    radial-gradient(2px 2px at 18% 22%, rgba(255,255,255,.7), transparent),
    radial-gradient(2px 2px at 78% 16%, rgba(255,216,107,.8), transparent),
    radial-gradient(1.6px 1.6px at 32% 74%, rgba(255,255,255,.5), transparent),
    radial-gradient(1.6px 1.6px at 86% 62%, rgba(255,155,192,.7), transparent),
    radial-gradient(1.4px 1.4px at 60% 36%, rgba(255,255,255,.45), transparent);
  animation: twinkle 3.2s ease-in-out infinite;
  pointer-events: none;
}
@keyframes twinkle { 0%,100% { opacity: .5; } 50% { opacity: 1; } }

.flip-title {
  position: relative; z-index: 1;
  font-size: 15px; font-weight: 800; color: rgba(255,255,255,.9);
  letter-spacing: .04em;
}
/* 3D 翻牌本体 */
.flip {
  position: relative; z-index: 1;
  width: 186px; aspect-ratio: 5 / 7;
  perspective: 1200px;
}
.flip-inner {
  position: relative; width: 100%; height: 100%;
  transform-style: preserve-3d;
  transition: transform .78s cubic-bezier(.34,1.16,.42,1);
}
.flip.on .flip-inner { transform: rotateY(180deg); }
.flip-face {
  position: absolute; inset: 0;
  -webkit-backface-visibility: hidden; backface-visibility: hidden;
  border-radius: var(--r-md);
  overflow: hidden;
}
.flip-back {
  transform: rotateY(180deg);
}
/* 牌背：斜纹 + 徽记 */
.flip-cover {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  /* 背面朝上的牌 = 卡背真图。以前这里画的是斜纹渐变 + 一个盒子图标，
     牌本身是空的；现在牌是真的，所以斜纹渐变撤掉，只留边框。 */
  background: linear-gradient(160deg, #6D4AFF 0%, #4A2BD6 52%, #2E1A8F 100%);
  border: 3px solid rgba(255,255,255,.24);
  border-radius: var(--r-md);
  overflow: hidden;
}
.flip-back-img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover; display: block;
}
.flip-cover .ico { width: 62px; height: 62px; color: rgba(255,255,255,.9); }
/* 牌背外圈的呼吸光，提示「可以点了」 */
.flip.pulse .flip-cover { animation: coverGlow 1.9s ease-in-out infinite; }
@keyframes coverGlow {
  0%, 100% { border-color: rgba(255,255,255,.24); }
  50%      { border-color: rgba(255,255,255,.6); }
}
.flip-hint {
  position: relative; z-index: 1;
  font-size: 12px; color: rgba(255,255,255,.6);
  animation: bob 2.2s ease-in-out infinite;
}
/* 翻牌里的卡牌：撑满整个牌面，所以直接复用 .card 的稀有度框和卡面排版 */
.flip-back .card {
  position: absolute; inset: 0;
  width: 100%; height: 100%; aspect-ratio: auto;
}
/* 翻开后露出的结果区 */
.flip-after {
  position: relative; z-index: 1;
  display: flex; flex-direction: column; align-items: center; gap: 13px;
  animation: pop .4s cubic-bezier(.2,1.4,.5,1) backwards;
}
.flip-got {
  display: flex; align-items: center; gap: 11px;
  padding: 11px 20px; border-radius: var(--r-full);
  background: rgba(255,255,255,.10);
  box-shadow: inset 0 0 0 1.5px rgba(255,255,255,.22);
  color: #fff;
}
.flip-got-ico {
  width: 30px; height: 30px; border-radius: var(--r-xs);
  display: grid; place-items: center; font-size: 15px; font-weight: 800;
}
.flip-got b { font-family: var(--font-num); font-size: 19px; font-weight: 800; }
.flip-got em { font-style: normal; font-size: 12.5px; color: rgba(255,255,255,.72); font-weight: 600; }
/* 开出来之后的光爆 */
.flip-burst {
  position: absolute; inset: -40%;
  border-radius: 50%;
  background: radial-gradient(circle, var(--rc1, #fff) 0%, transparent 58%);
  opacity: 0; pointer-events: none;
  animation: burst .72s ease-out forwards;
}
@keyframes burst {
  0%   { opacity: 0; transform: scale(.3); }
  40%  { opacity: .85; }
  100% { opacity: 0; transform: scale(1.5); }
}

/* 开盒结果：材料永远看得见，这是资产不是抽奖 */
/* ⚠️ 这里原来还有一组 `.reveal-row` / `.reveal` / `.reveal-ico` / `.reveal-name`
   / `.reveal-n` / `.reveal-tip` —— 是**上一版**的开盒结果区。现在的结果区是
   `.flip-after` / `.flip-got`（见上面「翻开后露出的结果区」那段），
   这 6 条规则全项目零引用，已删除（原版在 `_backup/review-fix-before/app.css`）。
   ⚠️ 下面这个 `@keyframes pop` **不是它们的**，`.flip-after` 还在用，别一起删。 */
@keyframes pop { from { opacity: 0; transform: scale(.7); } to { opacity: 1; transform: scale(1); } }

/* 材料栏：常驻可见，是资产不是隐藏背包 */
.mat-row {
  display: flex; gap: 10px; padding: 0 var(--pad) 4px;
  overflow-x: auto; -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.mat-row::-webkit-scrollbar { display: none; }
.mat {
  flex: 0 0 78px; text-align: center;
  background: var(--card); border-radius: var(--r-md);
  box-shadow: var(--sh-tap); padding: 13px 6px 12px;
  position: relative;
}
.mat-ico {
  width: 36px; height: 36px; margin: 0 auto 8px;
  border-radius: var(--r-sm); display: grid; place-items: center;
  font-size: 17px; font-weight: 700; line-height: 1;
}
.mat-num {
  font-family: var(--font-num); font-size: 15px; font-weight: 800; color: var(--ink);
}
.mat-name { font-size: 10px; color: var(--ink-3); margin-top: 2px; }
.mat-dot {
  position: absolute; top: 9px; right: 9px;
  width: 6px; height: 6px; border-radius: 50%; background: var(--rc, transparent);
}

/* 合成台 */
.recipe {
  margin: 0 var(--pad); padding: 17px 18px;
  background: var(--card); border-radius: var(--r-lg);
  box-shadow: var(--sh-tap);
}
.recipe + .recipe { margin-top: 11px; }
.recipe-list { padding-top: 2px; }
.recipe-head {
  display: flex; align-items: center; gap: 9px; margin-bottom: 13px;
}
.recipe-name { font-size: 15px; font-weight: 800; color: var(--ink); }
.tier {
  font-size: 10.5px; font-weight: 800;
  padding: 3px 9px; border-radius: var(--r-full);
  background: var(--paper-2); color: var(--ink-2);
}
.recipe-cost { display: flex; flex-wrap: wrap; gap: 7px; margin-bottom: 15px; }
.cost-chip {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 5px 11px; border-radius: var(--r-full);
  font-size: 11.5px; font-weight: 700;
  background: var(--paper-2); color: var(--ink-2);
  font-family: var(--font-num);
}
.cost-chip i { font-style: normal; }
.cost-chip.ok { background: #E4F7EF; color: #0A8F6E; }
.cost-chip.no { background: #FDECEF; color: #C7345A; }
.recipe-foot {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
}
.recipe-out { font-size: 12.5px; color: var(--ink-2); font-weight: 500; }
.recipe-out .gem { vertical-align: -2px; font-size: 14px; }
/* 未解锁的配方：整体压暗 + 虚线，一眼能跟可合成的区分开 */
.recipe.locked {
  background: var(--paper-2);
  box-shadow: none;
  border: 1.5px dashed #DCD5C8;
}
.recipe.locked .recipe-name,
.recipe.locked .tier,
.recipe.locked .recipe-out { opacity: .5; }
.recipe.locked .cost-chip { background: #E4DCCD; color: var(--ink-3); }

/* ============================ 娱乐 ============================ */
/* 版式参考用户给的 `娱乐2.jpg`：一条扁长卡，左边插图、右边色块里放游戏名和
   「进入」，左下角一个深色胶囊挂一句说明。
   ---------------------------------------------------------------------------
   比例是量出来的（参考图 1080 宽，卡 992×277 = 3.58:1），但**没有照抄**：
   按 3.58 算我们只有 109px 高，游戏名要压到 20px、「进入」压到 22px，
   中文在这个尺寸下发虚。118px 是把卡加高 8% 换来的可读性，
   整体观感还是同一种「扁长卡」。
   ---------------------------------------------------------------------------
   ⚠️ 插图是 position:absolute **铺满整张卡**的，右栏色块盖在它上面。
   这么写不是为了省事，是为了让右栏能切一条**斜边**（clip-path）——
   参考图里两块的分界是斜的，不是一条竖直线。
   斜边露出来的那一小块必须是插图，所以插图铺到 68%，
   比右栏 60% 的起点更靠右，两者重叠 8% 正好被斜角吃掉。
   ---------------------------------------------------------------------------
   ⚠️ button 里只能放 phrasing content，所以全是 <span>。
   <div> 会被解析器踢出 button，卡片当场散架。 */
.game-list { display: grid; gap: 14px; padding: 2px var(--pad) 0; }

/* ---- 娱乐页：「小游戏」这个标题上下的行距 ----
   这一页只有一级标题，导航栏 63px 高，标题本身才 25px —— 而它上面有 26px、
   下面有 13px 的空档，加起来标题像浮在一片空白中间，也就是用户说的「空旷」。
   这里只把标题上下的空档压下来，卡片和别的一律不动。

   ⚠️ 只对娱乐页生效。.sec 和 .sec-head 是全 App 共用的，
      换个页面改这套值会让所有列表页的标题一起移位。 */
html[data-page="play"] .sec { margin-top: 12px; }
html[data-page="play"] .sec-head { margin-bottom: 10px; }

/* ---- 娱乐页收尾：品牌口号 ----
   原来这块是个 .hint（灰底小字），写的是「本版只做入口与框架，游戏逻辑暂不实现」——
   那是给开发看的版本说明。现在只留 App 口号。

   为什么不用 .hint：.hint 是「提示块」的视觉语言（灰底、左边一个图标），
   拿来装品牌口号等于把口号降级成一条提示。口号要用「结束这一页」的姿态：
   居中、字距放开、不用底色。 */
.play-foot {
  margin: 18px var(--pad) 0;
  text-align: center;
}
.pf-slogan {
  font-size: 15px; font-weight: 700; color: var(--ink);
  letter-spacing: .18em;              /* 中文口号放开字距才像「题字」 */
  text-indent: .18em;                 /* 补掉最后一字右侧多出来的字距，视觉才居中 */
}

.game-card {
  position: relative;
  display: block;
  width: 100%; height: 118px;
  padding: 0; border: 0;
  border-radius: 14px;
  overflow: hidden;
  background: var(--ink);
  box-shadow: var(--sh-soft);
  cursor: pointer; font-family: var(--font);
  transition: transform .14s ease;
}
.game-card:active { transform: scale(.985); }

/* 插图铺 0..68% */
.game-art {
  position: absolute; top: 0; bottom: 0; left: 0; right: 32%;
  display: block;
}
.game-art img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover; display: block;
}

/* 左下角的说明胶囊。参考图里它就压在这个位置（距左 26/992、几乎贴底）。 */
.game-line {
  position: absolute; left: 10px; bottom: 9px;
  width: 57%; height: 24px;
  display: flex; align-items: center; gap: 6px;
  padding: 0 10px;
  border-radius: var(--r-full);
  background: rgba(12,16,26,.74);
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  color: #fff;
}
.game-line .ico { width: 12px; height: 12px; flex: none; color: #6EE7C8; }
.game-line em {
  font-style: normal; font-size: 11px; font-weight: 600;
  min-width: 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* 右栏色块。斜边由 clip-path 切出来，颜色是每个游戏自己的 --gc；
   上面那层白到黑的渐变是给所有品牌色统一压出白字对比度用的 ——
   四个游戏色里有两个（青、橙）配白字对比度不够，靠这层补上。 */
.game-side {
  position: absolute; top: 0; right: 0; bottom: 0;
  width: 40%;
  padding-left: 18px;                 /* 让内容躲开那条斜边 */
  display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 9px;
  clip-path: polygon(18px 0, 100% 0, 100% 100%, 0 100%);
  background:
    linear-gradient(165deg, rgba(255,255,255,.24), rgba(0,0,0,.46)),
    var(--gc, var(--brand));
}
.game-name {
  font-size: 21px; font-weight: 800; color: #fff;
  letter-spacing: -.01em;
  text-shadow: 0 2px 6px rgba(0,0,0,.35);
  white-space: nowrap;
}
.game-go {
  display: grid; place-items: center;
  width: 96px; height: 27px; border-radius: var(--r-full);
  background: #fff; color: var(--gc, var(--brand));
  font-size: 13px; font-weight: 800;
  box-shadow: 0 4px 10px -4px rgba(0,0,0,.5);
}
/* 娱乐页顶部原来有一块 hero（`.play-hero` / `.play-art` / `.play-t` / `.play-s`），
   已按用户要求移除 —— 下面 4 张游戏卡自带插图，hero 是第二张大图会抢第一眼，
   参考图 `娱乐2.jpg` 也是导航栏下面直接就是卡。
   抽水披露挪进了页面底部的 `.hint`，见 `pages.js`。 */

/* ============================ 交易行（含拍卖） ============================ */
/* 交易行和拍卖行原本是两个页面，现在合成一个：
   上面是玩家挂单直接成交，下面是竞价中的拍品。同一个「虚粒换东西」的动作，
   没必要拆两页。 */
.ex-card {
  margin: 0 var(--pad); padding: 20px;
  background: var(--card); border-radius: var(--r-xl);
  box-shadow: var(--sh-soft);
}
.ex-title { font-size: 17px; font-weight: 800; color: var(--ink); letter-spacing: -.01em; }
.ex-sub { font-size: 11.5px; color: var(--ink-3); margin: 6px 0 18px; line-height: 1.55; }

/* 分段切换：挂单 / 拍卖 */
.seg {
  display: flex; gap: 4px; margin: 0 var(--pad) 16px;
  padding: 4px; border-radius: var(--r-full);
  background: var(--paper-2);
}
.seg-item {
  flex: 1; height: 38px; border: 0; border-radius: var(--r-full);
  background: transparent; cursor: pointer;
  font-family: var(--font); font-size: 13px; font-weight: 700; color: var(--ink-3);
  transition: background .18s ease, color .18s ease, box-shadow .18s ease;
}
.seg-item.on {
  background: var(--card); color: var(--ink);
  box-shadow: var(--sh-tap);
}

/* 拍品卡：横向，图在左信息在右，和挂单列表区分开 */
.lot {
  display: flex; gap: 13px; align-items: stretch;
  width: calc(100% - var(--pad) * 2);
  margin: 0 var(--pad) 11px; padding: 12px;
  background: var(--card); border-radius: var(--r-lg);
  box-shadow: var(--sh-tap); cursor: pointer; text-align: left;
  font-family: var(--font);
  transition: transform .14s ease;
}
.lot:active { transform: scale(.98); }
.lot-art {
  width: 74px; flex: none; aspect-ratio: 5 / 7;
  border-radius: var(--r-sm); overflow: hidden;
  background: var(--card-2);
  display: grid; place-items: center;
}
.lot-art .art-img { width: 100%; height: 100%; object-fit: cover; object-position: center 34%; }
.lot-art .ico { width: 28px; height: 28px; color: var(--ink-3); }
.lot-body { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.lot-name {
  font-size: 14px; font-weight: 800; color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.lot-tag {
  display: inline-block; margin-top: 5px;
  font-size: 10px; font-weight: 800;
  padding: 2px 8px; border-radius: var(--r-full);
  background: var(--contra-l); color: var(--contra);
  align-self: flex-start;
}
.lot-tag.hot { background: #FFEDE0; color: #C25A13; }
.lot-foot {
  margin-top: auto; padding-top: 8px;
  display: flex; align-items: flex-end; justify-content: space-between; gap: 8px;
}
.lot-price { font-family: var(--font-num); font-size: 17px; font-weight: 800; color: var(--brand); }
.lot-price em { font-style: normal; font-size: 10px; color: var(--ink-3); font-weight: 600; margin-left: 3px; }
.lot-meta { font-size: 10.5px; color: var(--ink-3); text-align: right; line-height: 1.5; }
.lot-meta b { font-family: var(--font-num); color: var(--ink-2); }

/* 表单字段（交易行 / CDK 共用） */
.field { display: block; margin-bottom: 14px; }
.field > span {
  display: block; font-size: 11.5px; font-weight: 700;
  color: var(--ink-2); margin-bottom: 7px;
}
.field input {
  width: 100%; height: 52px; padding: 0 16px;
  border: 1.5px solid var(--line); border-radius: var(--r-md);
  background: var(--card-2); color: var(--ink);
  font-family: var(--font-num); font-size: 16px; font-weight: 700;
  outline: none; transition: border-color .15s, box-shadow .15s;
}
.field input:focus { border-color: var(--brand); background: #fff; box-shadow: 0 0 0 4px var(--brand-l); }
.field input::placeholder { color: #C4C8D2; font-family: var(--font); font-size: 13px; font-weight: 500; }
.ex-calc {
  display: flex; justify-content: space-between; gap: 12px;
  padding: 14px 16px; margin-bottom: 16px;
  border-radius: var(--r-md); background: var(--paper-2);
  font-size: 12px; color: var(--ink-2); font-weight: 500;
}
.ex-calc > div { display: flex; align-items: baseline; gap: 6px; }
.ex-calc b { color: var(--ink); font-family: var(--font-num); font-weight: 800; }

.trade-list { padding-top: 2px; }
.trade {
  display: flex; align-items: center; gap: 11px;
  padding: 13px 15px; margin: 0 var(--pad) 9px;
  background: var(--card); border-radius: var(--r-md);
  box-shadow: var(--sh-tap);
}
.trade-who {
  flex: 1; min-width: 0;
  display: flex; align-items: center; gap: 7px;
  font-size: 12.5px; font-weight: 700; color: var(--ink);
}
.trade-who .ico { width: 15px; height: 15px; color: var(--ink-3); flex: none; }
.trade-who span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.trade-amt { font-family: var(--font-num); font-size: 15px; font-weight: 800; color: var(--brand); flex: none; }
.trade-fee { font-size: 10.5px; color: var(--ink-3); flex: none; }
.trade-time { font-size: 10.5px; color: var(--ink-3); flex: none; font-family: var(--font-num); }

/* 交换确认 */
.confirm { border-radius: var(--r-md); overflow: hidden; }
.confirm .row {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 11px 0; border-bottom: 1px solid var(--line); font-size: 12.5px;
}
.confirm .row:last-child { border-bottom: 0; }
.confirm .row span { color: var(--ink-3); }
.confirm .row b { color: var(--ink); font-weight: 700; font-family: var(--font-num); }

/* ============================ 邮件 / 排行 / 账单 ============================ */
.mail {
  position: relative;
  display: flex; align-items: center; gap: 13px;
  margin: 0 var(--pad) 10px; padding: 15px 16px;
  background: var(--card); border-radius: var(--r-lg);
  box-shadow: var(--sh-tap); cursor: pointer;
}
.mail.unread { box-shadow: inset 3px 0 0 var(--brand), var(--sh-tap); }
.mail-ico {
  width: 44px; height: 44px; flex: none; border-radius: var(--r-md);
  background: var(--paper-2); color: var(--ink-2);
  display: grid; place-items: center;
}
.mail.unread .mail-ico { background: var(--brand-l); color: var(--brand); }
.mail-ico .ico { width: 21px; height: 21px; }
.mail-meta { flex: 1; min-width: 0; }
.mail-meta b {
  display: block; font-size: 13.5px; font-weight: 700; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.mail-meta em {
  display: block; font-style: normal;
  font-size: 11.5px; color: var(--ink-3); margin-top: 4px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.mail-time { font-size: 10.5px; color: var(--ink-3); flex: none; font-family: var(--font-num); }
.mail-dot {
  position: absolute; top: 13px; right: 13px;
  width: 7px; height: 7px; border-radius: 50%; background: var(--brand);
}
.mail-list { padding-top: 6px; }

.rank-list { padding-top: 6px; }

/* 「我的名次」条 —— 2026-09-20 新增。
   新版后端在榜单响应里给了 me:{rank,value,in_top}，所以进不了前 50
   也能看到自己（以前看不到，用户会以为数据丢了）。
   放在列表**上方**：它是"我在哪"，不是列表的一部分，混进名次里
   会让下面所有人的序号看起来错位。 */
.rank-me {
  display: flex; align-items: center; gap: 8px;
  margin: 2px var(--pad) 10px; padding: 10px 14px;
  background: var(--brand-l); border-radius: var(--r-md);
  box-shadow: inset 0 0 0 2px var(--brand);
  font-size: 12px; color: var(--ink-2);
}
.rank-me b {
  font-family: var(--font-num); font-size: 14px; font-weight: 800;
  color: var(--brand);
}
.rank-me em {
  margin-left: auto; font-style: normal;
  font-family: var(--font-num); font-weight: 700; color: var(--ink);
}
/* 未上榜：用低调的中性样式，别用品牌色 —— 那会让"没上榜"看起来像好消息 */
.rank-me.off {
  box-shadow: var(--sh-tap); color: var(--ink-3);
  justify-content: center;
}

.rank-row {
  display: flex; align-items: center; gap: 11px;
  padding: 11px 16px; margin: 0 var(--pad) 8px;
  background: var(--card); border-radius: var(--r-md);
  box-shadow: var(--sh-tap);
}
.rank-row.me { box-shadow: inset 0 0 0 2px var(--brand), var(--sh-tap); }
.rank-no {
  width: 27px; height: 27px; flex: none; border-radius: var(--r-full);
  display: grid; place-items: center;
  font-family: var(--font-num); font-size: 12px; font-weight: 800;
  background: var(--paper-2); color: var(--ink-3);
}
.rank-no.n1 {
  background: linear-gradient(160deg, #FFE086, #EAA312); color: #563200;
  box-shadow: 0 4px 10px -3px rgba(234,163,18,.8);
}
.rank-no.n2 {
  background: linear-gradient(160deg, #E8EDF5, #AEB9CC); color: #46536B;
  box-shadow: 0 4px 10px -3px rgba(174,185,204,.8);
}
.rank-no.n3 {
  background: linear-gradient(160deg, #F5C89A, #C97F3E); color: #fff;
  box-shadow: 0 4px 10px -3px rgba(201,127,62,.7);
}
.rank-av {
  width: 34px; height: 34px; flex: none; border-radius: var(--r-full);
  background: var(--paper-2); font-size: 17px; line-height: 1;
  display: grid; place-items: center;
  /* 里面有真图时必须裁成圆 + 必须 overflow:hidden。
     少了 overflow，圆角只裁背景不裁子元素。 */
  position: relative; overflow: hidden;
}
/* 榜单里现在既有 emoji 也有真图（店长榜用的是店长头像）。
   真图不能靠 width/height:100% —— 那是 grid 项，高度会被图片自身的
   宽高比接管（实测 34×54.8），结果头像溢出成一条竖的。
   用绝对定位写死四边。
   ⚠️ 原来还有 `object-position: center 8%; transform: scale(1.5)`，
   那是补偿「拿全身竖图当头像」的。现在头像是 246x246 方图，删掉了。 */
.rank-av .art-img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover; object-position: center;
}
.rank-name {
  flex: 1; min-width: 0; font-size: 13px; font-weight: 600; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.rank-name em {
  font-style: normal; font-size: 10px; font-weight: 800;
  color: var(--brand); background: var(--brand-l);
  padding: 2px 7px; border-radius: var(--r-full); margin-left: 6px;
}
.rank-gems { font-family: var(--font-num); font-size: 14px; font-weight: 800; color: var(--brand); flex: none; }

.bill-list { padding-top: 6px; }
.bill {
  display: flex; align-items: center; gap: 12px;
  padding: 14px 16px; margin: 0 var(--pad) 8px;
  background: var(--card); border-radius: var(--r-md);
  box-shadow: var(--sh-tap);
}
.bill-meta { flex: 1; min-width: 0; }
.bill-meta b { display: block; font-size: 13px; font-weight: 700; color: var(--ink); }
.bill-meta em { display: block; font-style: normal; font-size: 11px; color: var(--ink-3); margin-top: 4px; }
.bill-right { flex: none; text-align: right; }
.bill-delta { font-family: var(--font-num); font-size: 15px; font-weight: 800; }
.bill-delta.pos { color: #0A8F6E; }
.bill-delta.neg { color: var(--ink); }
.bill-time { font-size: 10.5px; color: var(--ink-3); margin-top: 4px; font-family: var(--font-num); }

/* ============================ 店长 ============================ */
/* 店长是线下成交的关键角色，所以这张卡要能直接抄联系方式，
   还要能点进去看他的二级店长 —— 一个卡片承载三层信息。 */
.mgr-hero {
  margin: 0 var(--pad) 4px; padding: 20px;
  border-radius: var(--r-xl);
  background: linear-gradient(158deg, #24365C 0%, var(--contra) 55%, #0D1526 100%);
  color: #fff;
  position: relative; overflow: hidden;
  box-shadow: 0 16px 34px -16px rgba(16,26,49,.8);
}
.mgr-hero::after {
  content: ''; position: absolute; top: -60px; right: -50px;
  width: 190px; height: 190px; border-radius: 50%;
  background: radial-gradient(circle, rgba(255,201,60,.42), transparent 68%);
  pointer-events: none;
}
.mgr-hero-top {
  display: flex; align-items: center; gap: 13px;
  position: relative; z-index: 1;
}
.mgr-hero-av {
  width: 58px; height: 58px; flex: none; border-radius: var(--r-full);
  overflow: hidden; display: grid; place-items: center;
  background: rgba(255,255,255,.16);
  box-shadow: inset 0 0 0 2.5px rgba(255,255,255,.55);
  font-size: 26px;
}
.mgr-hero-av .art-img { width: 100%; height: 100%; object-fit: cover; }
/* 头像是圆形容器，源图是全身竖图。取上部 + 放大，
   否则一整个小人缩在圆圈里会被大片摄影棚灰底吃掉，认不出是谁。 */
/* 店长 / 下级店长 / 榜单的头像。
   ⚠️ 这里原来有一句 `object-position: center 8%; transform: scale(1.5)` ——
   那是**拿 card-*.jpg 那种全身竖图当头像用时，放大 1.5 倍裁到脸**的临时办法。
   现在头像换成了专门的 246x246 方图（assets/avatar/），
   **那句必须删掉** —— 留着会把头像再放大 1.5 倍，脸直接出框。 */
.mgr-hero-av .art-img,
.mgr-av .art-img,
.sub-av .art-img { object-position: center; }
.mgr-hero-meta { flex: 1; min-width: 0; }
.mgr-hero-name {
  display: flex; align-items: center; gap: 6px;
  font-size: 17px; font-weight: 800; letter-spacing: -.01em;
}
.mgr-hero-name .vfy { width: 17px; height: 17px; color: var(--yellow); flex: none; }
.mgr-hero-sub { font-size: 11.5px; color: rgba(255,255,255,.62); margin-top: 5px; }
.mgr-hero-badge {
  position: relative; z-index: 1;
  display: inline-flex; align-items: center; gap: 5px;
  margin-top: 14px; padding: 5px 12px;
  border-radius: var(--r-full);
  background: rgba(255,201,60,.2); color: var(--yellow);
  font-size: 11px; font-weight: 800;
}

/* 联系方式行：既能看见，又能一点复制 */
.ct-row {
  display: flex; align-items: center; gap: 11px;
  width: 100%; margin-top: 10px; padding: 13px 14px;
  border: 0; border-radius: var(--r-md);
  background: var(--card); box-shadow: var(--sh-tap);
  cursor: pointer; text-align: left; font-family: var(--font);
  transition: transform .12s ease;
}
.ct-row:active { transform: scale(.98); }
.ct-badge {
  width: 38px; height: 38px; flex: none; border-radius: var(--r-sm);
  display: grid; place-items: center;
  color: #fff; font-size: 13px; font-weight: 800;
  letter-spacing: -.02em;
}
.ct-badge.wx { background: linear-gradient(158deg, #4ED06A, #1AAD3C); }
.ct-badge.qq { background: linear-gradient(158deg, #4FB8FF, #1287E8); }
.ct-meta { flex: 1; min-width: 0; }
.ct-label { display: block; font-size: 11px; color: var(--ink-3); font-weight: 600; }
/* ⚠️⚠️ 选择器必须是 `.ct-meta .ct-val`，不能只写 `.ct-val`。
   ---------------------------------------------------------------------------
   店长名录那组（`.ct-pill` / `.ct-tag`）里**也有一个 `.ct-val`** ——
   两条规则特异性相同，后写的（约 L2664 那条）会赢。
   结果：客服页的微信号 / QQ 号被压成 12.5px/700，
   比它上面 11px 的标签还不起眼，而设计要的是 15px/800。

   两处**都加父级作用域**，谁也别盖谁。
   加新组件时如果要复用 `.ct-val` 这个名字，先看这里。 */
.ct-meta .ct-val {
  display: block; margin-top: 3px;
  font-family: var(--font-num); font-size: 15px; font-weight: 800; color: var(--ink);
  letter-spacing: .01em;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* 这里原来是一个粉底「复制」小按钮。但整行本身已经是 data-copy 按钮，
   点哪儿都能复制 —— 再放一个按钮只是让用户先犹豫点哪个。
   现在只剩一个低对比的小图标，表示「这一行能点」。 */

/* 店长列表（可点进二级） */
/* 边距和间距必须在这里给。
   老的 .mgr 自带 margin，换成 .mgr2 之后忘了补 —— 结果卡片满宽（430）
   且卡与卡间距 0，几块白卡糊成一整块。放在容器上比放在卡片上稳：
   以后加卡片不用每张都记得写 margin。 */
.mgr-list {
  display: grid; gap: 11px;
  padding: 6px var(--pad) 0;
}
.mgr {
  display: flex; align-items: center; gap: 13px;
  width: calc(100% - var(--pad) * 2);
  margin: 0 var(--pad) 11px; padding: 15px 16px;
  background: var(--card); border-radius: var(--r-lg);
  box-shadow: var(--sh-tap); cursor: pointer; text-align: left;
  font-family: var(--font);
  transition: transform .14s ease;
}
.mgr:active { transform: scale(.98); }
.mgr-av {
  width: 50px; height: 50px; flex: none; border-radius: var(--r-full);
  overflow: hidden; display: grid; place-items: center;
  background: var(--paper-2); color: var(--ink-2);
  box-shadow: inset 0 0 0 2px var(--card), 0 0 0 2px var(--line);
}
.mgr-av .art-img { width: 100%; height: 100%; object-fit: cover; }
.mgr-av .ico { width: 22px; height: 22px; }
.mgr-meta { flex: 1; min-width: 0; }
.mgr-meta b {
  display: flex; align-items: center; gap: 5px;
  font-size: 14.5px; font-weight: 800; color: var(--ink);
}
.mgr-meta b .vfy { width: 15px; height: 15px; color: var(--blue); flex: none; }
.mgr-meta em { display: block; font-style: normal; font-size: 11.5px; color: var(--ink-3); margin-top: 4px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* 右侧「二级 N + 箭头」。
   二级人数原本拼在左边那行文字里，会把「团队 128 人」挤到第二行断成两截；
   挪到右侧当独立小胶囊就没这个问题了。 */
.mgr-go { flex: none; display: flex; align-items: center; gap: 7px; color: var(--ink-3); }
.mgr-go em {
  font-style: normal; font-family: var(--font-num);
  font-size: 11px; font-weight: 800; color: var(--ink-2);
  background: var(--paper-2); padding: 4px 9px; border-radius: var(--r-full);
  white-space: nowrap;
}
.mgr-go .ico { width: 16px; height: 16px; }

/* 二级店长 */
.sub {
  display: flex; align-items: center; gap: 11px;
  padding: 12px 14px; margin-bottom: 9px;
  background: var(--card-2); border-radius: var(--r-md);
  box-shadow: inset 0 0 0 1.5px var(--line);
}
.sub-av {
  width: 40px; height: 40px; flex: none; border-radius: var(--r-full);
  overflow: hidden; display: grid; place-items: center;
  background: var(--paper-2); color: var(--ink-3);
}
.sub-av .art-img { width: 100%; height: 100%; object-fit: cover; }
.sub-av .ico { width: 18px; height: 18px; }
.sub-meta { flex: 1; min-width: 0; }
.sub-meta b { display: block; font-size: 13.5px; font-weight: 800; color: var(--ink); }
.sub-meta em { display: block; font-style: normal; font-size: 11px; color: var(--ink-3); margin-top: 3px; }
.sub-tag {
  flex: none; font-size: 10px; font-weight: 800;
  padding: 3px 9px; border-radius: var(--r-full);
  background: var(--brand-l); color: var(--brand);
}

/* ============================ CDK ============================ */
.cdk-hero {
  margin: 0 var(--pad) 16px; padding: 24px 20px;
  background: var(--card); border-radius: var(--r-xl);
  box-shadow: var(--sh-soft); text-align: center;
}
.cdk-ico {
  width: 60px; height: 60px; margin: 0 auto 14px;
  border-radius: var(--r-md);
  background: linear-gradient(158deg, #FF74A6, var(--brand));
  color: #fff; display: grid; place-items: center;
  box-shadow: 0 10px 22px -8px rgba(255,77,141,.7);
}
.cdk-ico .ico { width: 28px; height: 28px; }
.cdk-t { font-size: 16px; font-weight: 800; color: var(--ink); letter-spacing: -.01em; }
.cdk-s { font-size: 11.5px; color: var(--ink-3); margin-top: 7px; }
/* CDK 兑换码输入框。
   ---------------------------------------------------------------------------
   ⚠️⚠️ 选择器必须是 `.field .cdk-input`（两个类），**不能只写 `.cdk-input`**。

   这个框在 HTML 里是 `<label class="field"><span>CDK 兑换码</span><input class="cdk-input">`，
   而 `.field input` 的特异性是 (0,1,1)，比 `.cdk-input` 的 (0,1,0) 高 ——
   只写一个类会被 `height:52px / 实线 / 左对齐 / 无字距` 那一套**静默盖掉**，
   而且不报错、不崩溃，只是「看起来是个普通输入框」。

   这件事真发生过：`pages.js` 那边的 input 曾经连 `cdk-input` 这个 class
   都没写（只写了 `data-cdk-input`），于是这一整块样式从头到尾没生效过。

   对一个 16 位兑换码来说，`letter-spacing` + 居中 + 等宽数字体
   不是装饰，是**可读性**。改这里之前先确认那三条都在。 */
.field .cdk-input {
  width: 100%; height: 58px; padding: 0 16px; margin-bottom: 14px;
  border: 1.5px dashed #D6CFC2; border-radius: var(--r-md);
  background: var(--card-2); color: var(--ink);
  font-family: var(--font-num); font-size: 18px; font-weight: 800;
  letter-spacing: .1em; text-align: center; outline: none;
  text-transform: uppercase;
}
.field .cdk-input:focus { border-color: var(--brand); border-style: solid; box-shadow: 0 0 0 4px var(--brand-l); }
.field .cdk-input::placeholder {
  font-family: var(--font-num); font-size: 16px; font-weight: 700;
  letter-spacing: .1em; color: #C4C8D2;
}
.field .cdk-input:disabled { opacity: .5; }

/* ============================ 弹窗 / 抽屉 / Toast ============================ */
/* ============================ 底部抽屉（唯一一种弹窗） ============================
   注意：ui.js 里建出来的遮罩节点 class 就叫 .modal，激活态加 .on，别改名。

   `.modal-box` / `.sheet` 原本是两套实现，量出来有 6 处没有理由的漂移
   （内边距 20 vs 18、阴影 .28 vs .24、抓手 14 vs 10、标题 18 vs 16px …），
   现在合成一套 `.drawer`。结构和取舍见 ui.js 顶部那段长注释。 */
.modal {
  position: fixed; inset: 0; z-index: 60;
  display: flex; align-items: flex-end; justify-content: center;
  background: rgba(16,22,38,.5);
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  opacity: 0; transition: opacity .2s ease;
  /* 下滑收起时面板会被移到屏幕外，别让它撑出一条滚动条 */
  overflow: hidden;
}
.modal.on { opacity: 1; }

.drawer {
  position: relative;
  width: 100%; max-width: var(--app-max);
  max-height: 88vh;
  /* flex column 是为了让**抓手条和按钮排固定在两端**，
     只有中间的 .drawer-scroll 滚 —— 见下面那条注释 */
  display: flex; flex-direction: column;
  background: var(--card);
  border-radius: var(--r-2xl) var(--r-2xl) 0 0;
  padding: 8px 20px calc(22px + env(safe-area-inset-bottom));
  box-shadow: 0 -14px 48px rgba(16,22,38,.28);
  transform: translateY(100%);
  transition: transform .3s cubic-bezier(.2,1,.3,1);
  /* 防御：固定高度的子项（抓手条 / 标题 / 按钮排）加起来超过 max-height 时，
     裁掉溢出的部分，别让它溢出圆角面板外面。
     ⚠️ 用 hidden 不是 auto —— scrollerAt() 只认 auto/scroll，
        写成 auto 会让「拖拽该不该让位给滚动」的判断多认一个容器。 */
  overflow: hidden;
}
.modal.on .drawer { transform: none; }

/* 抓手条。这是「可下拉关闭」的视觉约定，**也是下滑收起的热区**。
   ⚠️ 是真实元素，不是 ::before —— 拖拽要判断「手指是不是从这一带开始的」，
      ::before 命中不了（getBoundingClientRect 拿不到、elementFromPoint 也点不中）。
   高度 4 + 下边距 14 = 标题从面板顶 26px 处开始，跟拖拽的热区对得上。 */
.drawer-grab {
  flex: none;
  width: 40px; height: 4px; margin: 0 auto 14px;
  border-radius: var(--r-full); background: var(--line);
}

/* 右上角关闭。绝对定位，**不跟标题排成 flex 行** ——
   排成一行的话标题就不是相对整个抽屉居中了，会往左偏半个按钮的宽度。
   规矩只有一条：**有标题就补 X**（以前 .sheet 那条「有 footer 就不补」的
   例外取消了，同一个 App 不该有两种出口规矩）。 */
.drawer-x {
  position: absolute; top: 18px; right: 20px; z-index: 2;
  width: 34px; height: 34px; border-radius: var(--r-full);
  background: var(--paper-2); color: var(--ink-2);
  display: grid; place-items: center;
  transition: transform .12s ease;
}
.drawer-x:active { transform: scale(.9); }
.drawer-x .ico { width: 15px; height: 15px; }
.drawer-title {
  flex: none;
  font-size: 18px; font-weight: 800; color: var(--ink);
  letter-spacing: -.01em; line-height: 1.35;
  margin-bottom: 14px;
  /* 左对齐。给超长标题留出 X 的位置，**只加右边距** ——
     加 padding-left 只会把标题无端推离左边缘（踩过）。 */
  padding-right: 44px;
}

/* 唯一的滚动容器。
   ⚠️ 抓手条和按钮排都在它**外面**，所以内容再长它们也不会滚走。
      以前 .modal-box 自己是滚动容器，抓手条是它的 ::before、
      「确认」按钮是它的子元素 —— 内容一长，抓手条没了、主按钮也够不着。 */
.drawer-scroll {
  flex: 1 1 auto;
  min-height: 0;                  /* flex 子项默认 min-height:auto，不加这句滚不起来 */
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;   /* 滚到底不要带动外层 */
}
.drawer-scroll::-webkit-scrollbar { width: 0; }

.drawer-body { font-size: 13.5px; color: var(--ink-2); line-height: 1.6; }

/* 动作列表形态：没有按钮排，出口就是「最后一项 + 取消」 */
.drawer-list { padding: 0 2px; }
.drawer-item {
  display: flex; align-items: center; gap: 12px; width: 100%;
  padding: 15px 12px; border-radius: var(--r-md);
  font-size: 14.5px; font-weight: 700; color: var(--ink);
  text-align: left; font-family: var(--font);
}
.drawer-item .ico { width: 20px; height: 20px; color: var(--ink-2); }
.drawer-item em { margin-left: auto; font-style: normal; font-size: 12px; color: var(--ink-3); }
.drawer-item:active { background: var(--paper-2); }
.drawer-item.cancel {
  justify-content: center; margin-top: 8px; color: var(--ink-3); font-weight: 600;
}

/* 按钮排。也不在滚动容器里 —— 主按钮永远够得着。 */
.drawer-foot { flex: none; display: flex; gap: 10px; padding: 14px 2px 0; }
.drawer-foot .btn { flex: 1; }
.gift-detail p { font-size: 13px; color: var(--ink-2); line-height: 1.6; margin-bottom: 8px; }
.muted { color: var(--ink-3) !important; font-size: 12px !important; }
/* 等宽数字体。给「把用户输入的东西回显给他看」用（兑换码、ID、金额），
   等宽能让 0/O、1/l 分得开。⚠️ 只上字体，不覆盖字号和颜色 ——
   它是个纯工具类，要和 .muted / 各自的组件类叠加使用。 */
.mono { font-family: var(--font-num) !important; letter-spacing: .08em; }

/* ---------------------------------------------------------------------------
   页面左右留白 + 常用上边距
   ---------------------------------------------------------------------------
   整个 App 的「页面块级容器」都是 `padding: 0 var(--pad)`，
   以前这 10 处是写在 JS 里的 `style="padding:0 var(--pad)"` ——
   好处是当时省事，坏处是改一次页面左右边距要翻 10 个地方，
   而且 `--pad` 是主题令牌，本不该出现在 JS 字符串里。

   ⚠️ 只收「纯布局」的内联 style。**数据驱动的 CSS 变量不要往这儿搬** ——
      `style="--mc: <每件商品自己的强调色>"` 这种就该内联，那是数据不是样式。 */
.pad-x { padding-left: var(--pad); padding-right: var(--pad); }
.mt-6  { margin-top: 6px; }
.mt-14 { margin-top: var(--gap); }      /* --gap = 14px */
.mt-22 { margin-top: 22px; }

/* 卡详情：卡牌在上，参数行在下 */
.detail-meta .row {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; padding: 11px 0;
  border-bottom: 1px solid var(--line);
  font-size: 12.5px;
}
.detail-meta .row:last-child { border-bottom: 0; }
.detail-meta .row span { color: var(--ink-3); }
.detail-meta .row b { color: var(--ink); font-weight: 700; }

/* 抽屉的老实现（.sheet*）已经并进上面的 .drawer*，这里不再保留。 */

.toast {
  position: fixed; left: 50%; bottom: 16%; z-index: 90;
  transform: translate(-50%, 10px);
  max-width: 76vw; padding: 13px 22px;
  border-radius: var(--r-full);
  background: rgba(16,22,38,.94); color: #fff;
  font-size: 13px; font-weight: 700; text-align: center;
  box-shadow: 0 12px 30px -10px rgba(16,22,38,.6);
  opacity: 0; transition: opacity .2s ease, transform .2s ease;
  pointer-events: none;
}
.toast.on { opacity: 1; transform: translate(-50%, 0); }

/* ============================ 占位页 ============================ */
.stub { padding: 70px var(--pad) 0; text-align: center; }
.stub-ico {
  width: 82px; height: 82px; margin: 0 auto 20px;
  border-radius: var(--r-xl); background: var(--card);
  box-shadow: var(--sh-tap);
  display: grid; place-items: center; color: var(--ink-3);
}
.stub-ico .ico { width: 36px; height: 36px; }
.stub-t { font-size: 16px; font-weight: 800; color: var(--ink); }
.stub-s { font-size: 12.5px; color: var(--ink-3); margin-top: 9px; line-height: 1.6; }

/* ============================ 命中区兜底 ============================
   触屏上 44×44 是最低线（Apple HIG 44pt / Material 48dp）。
   下面这些元素在无头 Chrome 上实测只有 18-38px 高：

     .sec-more   40×18   「背包」「全部」这类次级链接
     .prod-more  79×21   首页「看细节 2 张」
     .mine-id   101×23   我的页「ID 88231145」（可点复制）
     .nav-btn    34×34   所有二级页的返回按钮
     .bb-mail    38×38   首页右上角邮件铃
     .mine-tool  38×38   我的页右上角工具钮
     .seg-item   高 38    所有分段控件

   **视觉尺寸一个都不改** —— 它们是次级链接或小圆钮，视觉上一放大就不对了。
   用 `::after` 铺一层透明的命中区，只影响可点范围。

   ⚠️ 为什么不用「padding + 负 margin」这种常见写法：
      这几个里有几个在 `align-items: baseline` 的 flex 行里（比如 .sec-head），
      负 margin 会改变基线对齐的参与方式，实测位置会漂几个像素。
      `::after` 是绝对定位的纯叠层，**绝对不会动布局**。

   ⚠️ 祖先有 `overflow: hidden` 时这一层会被裁掉。改这几个元素的父容器前先确认。
   ⚠️ 这一层会往外扩 13px 左右，可能盖到相邻元素头上。
      改完请跑 `_hitcheck.html` —— 它会用 elementFromPoint 实测
      「有没有哪个元素被这层挡掉了点击」。 */
.sec-more, .prod-more, .mine-id, .nav-btn, .bb-mail, .mine-tool, .seg-item {
  position: relative;
}
.sec-more::after, .prod-more::after, .mine-id::after, .nav-btn::after,
.bb-mail::after, .mine-tool::after, .seg-item::after {
  content: '';
  position: absolute; left: 0; right: 0;
  top: 50%; height: 44px;
  transform: translateY(-50%);
}

/* ============================ 排版兜底 ============================ */
/* flex 行里的文字容器必须能收缩，否则一个长句/nowrap 的文字
   就会把整行顶宽、把页面撑出屏幕。这一组别删。 */
.tb-meta, .cab-head, .earn-amount, .notice-text, .gift-body,
.mail-meta, .game-line, .mgr-meta, .sub-meta, .ct-meta, .lot-body,
.rank-name, .trade-who, .bill-meta, .drawer-title { min-width: 0; }

/* =====================================================================
   真图资源
   有 img 就走照片，没有就退回 SVG 公仔 —— 由 js/ui.js 的 PG.art() 决定。
   卡面源图统一是 496x800（比例 0.62）。
   注意：卡牌容器是 5:7 = 0.714，比源图宽，所以照片靠 object-fit:cover
   在底部裁掉一点（小腿以下）。这是刻意的 ——
   卡牌比例必须服从卡牌本身，不能为了不裁图把卡做成细长条。
   想看全身用卡详情里的大卡，那里仍然是完整的。
   ===================================================================== */
.art-img {
  width: 100%; height: 100%; display: block;
  object-fit: cover;
  object-position: center 34%;   /* 优先保住头 */
}
.gift-art.has-img { height: auto; aspect-ratio: 4 / 3; padding: 0; display: block; }
.gift-art .art-img { object-position: center 44%; }

/* 没图时的 SVG 公仔兜底。
   每个容器都必须给公仔一个明确尺寸 —— SVG 有 viewBox，不给尺寸就会
   按默认宽度撑开，把容器顶变形。所以这里逐个容器写死。 */
.gift-art .toy { width: 54%; height: auto; margin: auto; }
.mgr-av .toy,
.mgr-hero-av .toy,
.sub-av .toy { width: 100%; height: 100%; padding: 9%; }
.lot-art .toy { width: 100%; height: 100%; padding: 10%; }

/* =====================================================================
   「我的」页
   ---------------------------------------------------------------------
   这一页要同时装下 5 种不同密度的东西（头部 / 邀请卡片栏 / 4 宫格 /
   纵向列表 / 设置分组），靠「底色 + 圆角 + 层叠」分层，
   而不是给每一块都描一圈边 —— 卡片墙正是上一版「板正」的来源。
   ===================================================================== */

/* ---------- 头部：头像 / 昵称 / ID，右上角是邮件 + 设置 ---------- */
.mine-hero {
  position: relative;
  display: flex; align-items: center; gap: 14px;
  padding: 14px var(--pad) 30px;
  border-radius: 0 0 var(--r-xl) var(--r-xl);
  background: linear-gradient(168deg, #FF74A6 0%, var(--brand) 46%, #F03C7C 100%);
}
.mine-hero::after {          /* 底部压一层暗，把下面浮上来的卡片托住 */
  content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 46%;
  border-radius: 0 0 var(--r-xl) var(--r-xl);
  background: linear-gradient(180deg, rgba(0,0,0,0), rgba(0,0,0,.12));
  pointer-events: none;
}

.mine-av {
  flex: none; width: 62px; height: 62px;
  border: 0; border-radius: var(--r-full);
  background: rgba(255,255,255,.22);
  box-shadow: inset 0 0 0 3px rgba(255,255,255,.55), 0 8px 18px -8px rgba(0,0,0,.45);
  font-size: 30px; line-height: 1;
  display: grid; place-items: center;
  position: relative; z-index: 1; cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: transform .1s ease;
}
.mine-av:active { transform: scale(.94); }

.mine-meta { flex: 1; min-width: 0; position: relative; z-index: 1; }
.mine-name {
  display: flex; align-items: center; gap: 8px;
  font-size: 18px; font-weight: 800; color: #fff;
  text-shadow: 0 1px 3px rgba(0,0,0,.18);
}
.mine-lv {
  flex: none; padding: 2.5px 8px; border-radius: var(--r-full);
  background: rgba(255,255,255,.24);
  font-family: var(--font-num); font-size: 10.5px; font-weight: 800;
}
.mine-id {
  display: inline-flex; align-items: center; gap: 5px;
  margin-top: 8px; padding: 3px 9px 3px 10px;
  border: 0; border-radius: var(--r-full);
  background: rgba(255,255,255,.18);
  font-family: var(--font-num); font-size: 11.5px; font-weight: 600;
  color: rgba(255,255,255,.94); cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.mine-id .ico { width: 12px; height: 12px; opacity: .8; }
.mine-id:active { background: rgba(255,255,255,.32); }

.mine-tools { flex: none; display: flex; gap: 9px; position: relative; z-index: 1; }
.mine-tool {
  position: relative;                      /* .badge 是绝对定位，得要这个参照 */
  width: 38px; height: 38px;
  border: 0; border-radius: var(--r-full);
  background: rgba(255,255,255,.20); color: #fff;
  display: grid; place-items: center; cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: transform .1s ease, background .1s ease;
}
.mine-tool .ico { width: 19px; height: 19px; }
.mine-tool:active { background: rgba(255,255,255,.34); transform: scale(.94); }
.mine-tool .badge { box-shadow: 0 0 0 2px var(--brand); }

/* ---------- 邀请好友卡片栏（浮在头部下沿上） ---------- */
.invite {
  position: relative; z-index: 2;
  margin: -18px var(--pad) 0;
  padding: 15px 16px 14px;
  border-radius: var(--r-lg);
  background: linear-gradient(158deg, #23335A 0%, var(--contra) 58%, #16233D 100%);
  box-shadow: 0 14px 28px -14px rgba(27,42,74,.75);
}
.invite::after {             /* 右上角一圈光，做出「烫金卡」的质感 */
  content: ''; position: absolute; top: -30px; right: -24px;
  width: 112px; height: 112px; border-radius: var(--r-full);
  background: radial-gradient(circle, rgba(255,203,60,.30), rgba(255,203,60,0) 68%);
  pointer-events: none;
}
/* 头部整行是进邀请页的入口。做成 <button> 而不是 div+onclick：
   焦点、键盘、aria 都是白送的。 */
.invite-head {
  display: flex; align-items: center; gap: 11px;
  width: 100%; margin: -4px 0 0; padding: 4px 0;
  border: 0; background: none; text-align: left; font-family: inherit;
  cursor: pointer; position: relative; z-index: 1;
  -webkit-tap-highlight-color: transparent;
  transition: opacity .12s ease;
}
.invite-head:active { opacity: .62; }
.invite-go { flex: none; color: rgba(255,255,255,.42); display: grid; place-items: center; }
.invite-go .ico { width: 17px; height: 17px; }
.invite-ico {
  flex: none; width: 36px; height: 36px; border-radius: var(--r-full);
  background: linear-gradient(158deg, #FFE086, var(--yellow) 58%, #EAA312);
  color: #5A3600; display: grid; place-items: center;
  box-shadow: 0 6px 14px -6px rgba(255,201,60,.8);
}
.invite-ico .ico { width: 20px; height: 20px; }
.invite-meta { display: block; flex: 1; min-width: 0; }
.invite-t { display: block; font-size: 15px; font-weight: 800; color: #fff; }
.invite-s { display: block; font-size: 11.5px; line-height: 1.5; color: rgba(255,255,255,.60); margin-top: 4px; }

.invite-foot {
  display: flex; align-items: center; gap: 10px;
  margin-top: 13px; position: relative; z-index: 1;
}
/* 这一行有三个东西挤在一起（邀请码 / 已加入人数 / 生成海报），
   邀请码是 flex:1，会被另外两个压。上一版正好卡在 99/99 零余量，
   邀请码多一位就变成 PG8K3…。所以这里主动收紧，留出缓冲。 */
.invite-code {
  flex: 1; min-width: 0;
  display: flex; align-items: center; gap: 6px;
  height: 40px; padding: 0 10px;
  border: 0; border-radius: var(--r-md);
  background: rgba(255,255,255,.10);
  cursor: pointer; -webkit-tap-highlight-color: transparent;
  text-align: left;
}
.invite-code span { font-size: 10.5px; color: rgba(255,255,255,.50); flex: none; }
.invite-code b {
  font-family: var(--font-num); font-size: 14.5px; font-weight: 800;
  color: var(--yellow); letter-spacing: .04em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.invite-code .ico { width: 14px; height: 14px; color: rgba(255,255,255,.55); flex: none; margin-left: auto; }
.invite-code:active { background: rgba(255,255,255,.18); }

.invite-stat { flex: none; text-align: center; }
.invite-stat b {
  display: block; font-family: var(--font-num);
  font-size: 16px; font-weight: 800; color: #fff; line-height: 1.1;
}
.invite-stat em { display: block; font-style: normal; font-size: 10px; color: rgba(255,255,255,.48); margin-top: 2px; }

/* ⚠️ 必须是 inline-flex。原来没写 display，svg 和文字落进行内流，
   在按钮宽度不够时换行 —— 图标留在第一行、文字掉到第二行，
   而 height 写死 40px，文字就被挤到按钮外面去。
   （而且 flex: none 那个 .ico 也失效了，因为父级根本不是 flex。） */
.invite-cta {
  flex: none;
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  height: 40px; padding: 0 12px;
  border: 0; border-radius: var(--r-full);
  background: linear-gradient(180deg, #FFE086, var(--yellow) 52%, #EAA312);
  color: #5A3600; font-family: var(--font); font-size: 13.5px; font-weight: 800;
  white-space: nowrap;
  cursor: pointer; -webkit-tap-highlight-color: transparent;
  box-shadow: 0 4px 0 #C98A12;
  transition: transform .1s ease, box-shadow .1s ease;
}
.invite-cta .ico { width: 15px; height: 15px; }
.invite-cta:active { transform: translateY(2px); box-shadow: 0 2px 0 #C98A12; }

/* ---------- 常用功能 4 宫格 ---------- */
.mine-grid {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 9px;
  margin: 0 var(--pad);
}
.mgrid {
  display: flex; flex-direction: column; align-items: center; gap: 7px;
  padding: 15px 5px 13px;
  border: 0; border-radius: var(--r-md);
  background: var(--card); cursor: pointer;
  box-shadow: 0 3px 0 rgba(27,34,51,.05);
  -webkit-tap-highlight-color: transparent;
  transition: transform .1s ease;
}
.mgrid:active { transform: translateY(2px); box-shadow: none; }
.mgrid-ico {
  position: relative; overflow: hidden;
  width: 42px; height: 42px; border-radius: var(--r-full);
  display: grid; place-items: center;
  color: #fff; background: var(--mc);
  box-shadow: 0 7px 15px -7px var(--mc);
}
/* 同色加深用叠加而不是 color-mix() —— color-mix 在老安卓 WebView 上
   直接失效，整个图标底色会变透明。上半层白做受光，兼容性没有代价。 */
.mgrid-ico::before {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(255,255,255,.30), rgba(255,255,255,0));
  pointer-events: none;
}
.mgrid-ico .ico { position: relative; width: 21px; height: 21px; }
.mgrid-label { font-size: 11.5px; font-weight: 700; color: var(--ink); }
/* .mgrid-note 已废弃：常用功能宫格不再带说明文字。 */

/* 状态胶囊（实名认证的已认证 / 未认证） */
.pill {
  display: inline-block; padding: 1.5px 7px; border-radius: var(--r-full);
  font-size: 9.5px; font-weight: 800; line-height: 1.5;
}
.pill.ok   { background: rgba(16,185,141,.14); color: #0A8F6E; }
.pill.warn { background: rgba(255,138,61,.16); color: #C2600F; }

/* ---------- 纵向功能列表 ---------- */
.mine-list {
  margin: 14px var(--pad) 0;
  border-radius: var(--r-lg);
  background: var(--card);
  overflow: hidden;
  box-shadow: 0 3px 0 rgba(27,34,51,.05);
}
.mrow {
  display: flex; align-items: center; gap: 12px;
  width: 100%; padding: 14px 15px;
  border: 0; background: transparent; cursor: pointer;
  font-family: var(--font); text-align: left;
  -webkit-tap-highlight-color: transparent;
}
.mrow + .mrow { box-shadow: inset 0 1px 0 var(--line); }
.mrow:active { background: var(--paper-2); }
.mrow-ico {
  position: relative;
  flex: none; width: 32px; height: 32px; border-radius: 11px;
  display: grid; place-items: center;
  color: var(--mc);
}
.mrow-ico::before {
  content: ''; position: absolute; inset: 0; border-radius: inherit;
  background: var(--mc); opacity: .12;
}
.mrow-ico .ico { position: relative; width: 18px; height: 18px; }
.mrow-label { flex: 1; min-width: 0; font-size: 14.5px; font-weight: 700; color: var(--ink); }
.mrow-note {
  flex: none; font-size: 11.5px; color: var(--ink-3); font-weight: 600;
  max-width: 46%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.mrow-go { flex: none; color: var(--ink-3); display: grid; place-items: center; }
.mrow-go .ico { width: 15px; height: 15px; opacity: .5; }

/* ---------- 设置分组 ---------- */
.set-group { margin: 0 var(--pad) 14px; }
.set-group:first-of-type { margin-top: 12px; }
.set-cap {
  font-size: 11.5px; font-weight: 800; color: var(--ink-3);
  letter-spacing: .04em; padding: 0 4px 8px;
}
.set-group .mine-list { margin: 0; }

/* =====================================================================
   「我的」的二级页
   ===================================================================== */

/* ---------- 邀请好友 ---------- */
.invite-hero {
  margin: 6px var(--pad) 0; padding: 24px 20px 22px;
  border-radius: var(--r-xl); text-align: center;
  background: linear-gradient(160deg, #23335A 0%, var(--contra) 56%, #16233D 100%);
  position: relative; overflow: hidden;
}
.invite-hero::after {
  content: ''; position: absolute; top: -40px; left: 50%; transform: translateX(-50%);
  width: 190px; height: 190px; border-radius: var(--r-full);
  background: radial-gradient(circle, rgba(255,203,60,.26), rgba(255,203,60,0) 68%);
  pointer-events: none;
}
.invite-hero-t { position: relative; font-size: 17px; font-weight: 800; color: #fff; }
.invite-hero-s {
  position: relative; font-size: 12px; line-height: 1.6;
  color: rgba(255,255,255,.60); margin-top: 7px;
}
.invite-big {
  position: relative; display: block; width: 100%; margin-top: 18px;
  padding: 13px 0 11px;
  border: 0; border-radius: var(--r-md);
  background: rgba(255,255,255,.10);
  cursor: pointer; -webkit-tap-highlight-color: transparent;
}
.invite-big b {
  display: block; font-family: var(--font-num);
  font-size: 26px; font-weight: 800; letter-spacing: .12em; color: var(--yellow);
}
.invite-big span {
  display: flex; align-items: center; justify-content: center; gap: 4px;
  font-size: 11px; color: rgba(255,255,255,.55); margin-top: 6px;
}
.invite-big .ico { width: 12px; height: 12px; }
.invite-big:active { background: rgba(255,255,255,.18); }

.invite-stats {
  display: grid; grid-template-columns: repeat(3, 1fr);
  margin: 14px var(--pad) 0;
  border-radius: var(--r-lg); background: var(--card);
  box-shadow: 0 3px 0 rgba(27,34,51,.05);
  overflow: hidden;
}
.invite-stats > div { padding: 15px 4px; text-align: center; }
.invite-stats > div + div { box-shadow: inset 1px 0 0 var(--line); }
.invite-stats b {
  display: block; font-family: var(--font-num);
  font-size: 19px; font-weight: 800; color: var(--ink); line-height: 1.1;
}
.invite-stats span { display: block; font-size: 10.5px; color: var(--ink-3); margin-top: 5px; }

/* 四个指标排成 2×2。用 4 列会把「今日获得」这种四字标签挤到换行。
   （邀请页第一排是收益，好友页第一排是人数 —— 每页把最重要的放最上面。） */
.invite-stats.four { grid-template-columns: repeat(2, 1fr); }
.invite-stats.four > div + div { box-shadow: none; }          /* 先清掉 3 列那套分隔线 */
.invite-stats.four > div:nth-child(2) { box-shadow: inset 1px 0 0 var(--line); }
.invite-stats.four > div:nth-child(3) { box-shadow: inset 0 1px 0 var(--line); }
.invite-stats.four > div:nth-child(4) {
  box-shadow: inset 1px 0 0 var(--line), inset 0 1px 0 var(--line);
}
.invite-stats.four span { font-size: 11px; }
/* 「今日获得」是这一页唯一会动的数，给它一点颜色 */
.invite-stats .hot b { color: var(--brand-d); }

/* 海报按钮：压在深色邀请卡里，所以要描边不要实心 */
.invite-poster-btn { margin-top: 10px; }
.invite-poster-btn .ico { width: 17px; height: 17px; }

/* 好友入口预告条 */
.invite-friend-teaser {
  display: flex; align-items: center; gap: 13px; width: 100%;
  padding: 14px 15px; border: 0; text-align: left;
  border-radius: var(--r-lg); background: var(--card);
  box-shadow: 0 3px 0 rgba(27,34,51,.05);
  font-family: inherit;
}
.invite-friend-teaser:active { background: var(--paper-2); }
.ift-avs { display: flex; flex: none; }
.ift-av {
  width: 34px; height: 34px; border-radius: var(--r-full); overflow: hidden;
  background: var(--paper-2); box-shadow: 0 0 0 2px var(--card);
}
.ift-av + .ift-av { margin-left: -11px; }
.ift-av img, .ift-av svg { width: 100%; height: 100%; object-fit: cover; display: block; }
.ift-meta { flex: 1; min-width: 0; }
.ift-meta b { display: block; font-size: 14.5px; font-weight: 700; color: var(--ink); }
.ift-meta em {
  display: block; font-style: normal; font-size: 12px;
  color: var(--ink-3); margin-top: 3px;
}

/* ---------- 我的好友 ---------- */

/* 还没绑推荐人时占住那个位置的那张卡。
   ------------------------------------------------------------------
   为什么用**虚线框**而不是实心白卡：
   这个位置最终会长出一张深色的人物卡（.mgr-hero）。虚线是「还空着、
   可以填」的通用语言；做成实心卡的话，用户会以为这里已经有个人了。
   外边距 / 圆角刻意和 .mgr-hero 一致（var(--pad) / var(--r-xl)），
   绑上的一瞬间卡片原地替换，页面不会跳一下。

   ⚠️ 虚线颜色不要写死 rgba。--brand-l 是品牌色的极浅底，
   换皮时主色一变，这里跟着变。 */
.bind-card {
  margin: 0 var(--pad) 4px; padding: 17px 16px 15px;
  border-radius: var(--r-xl);
  background: var(--card);
  border: 2px dashed var(--brand-l);
  box-shadow: 0 3px 0 rgba(27,34,51,.04);
}
.bind-head { display: flex; align-items: center; gap: 12px; }
.bind-ico {
  flex: none; width: 48px; height: 48px; border-radius: var(--r-full);
  display: grid; place-items: center;
  color: var(--brand); background: var(--brand-l);
}
.bind-ico .ico { width: 24px; height: 24px; }
.bind-meta { flex: 1; min-width: 0; }
/* 这两个在 <span> 里，必须 display:block —— span 默认是行内的 */
.bind-t { display: block; font-size: 15px; font-weight: 800; color: var(--ink); }
.bind-s {
  display: block; margin-top: 4px; font-size: 11.5px;
  line-height: 1.45; color: var(--ink-3);
}

.bind-row { display: flex; align-items: stretch; gap: 8px; margin-top: 14px; }
.bind-inp {
  flex: 1; min-width: 0;
  display: flex; align-items: center; gap: 8px;
  height: 48px; padding: 0 14px;
  border-radius: var(--r-md);
  background: var(--paper-2);
  box-shadow: inset 0 0 0 1.5px var(--line);
  transition: box-shadow .15s ease;
}
.bind-inp:focus-within { box-shadow: inset 0 0 0 1.5px var(--brand), 0 0 0 4px var(--brand-l); }
.bind-inp .ico { width: 17px; height: 17px; color: var(--ink-3); flex: none; }
.bind-inp input {
  flex: 1; min-width: 0; border: 0; background: none; outline: none;
  font-family: var(--font-num); font-size: 15px; font-weight: 800;
  letter-spacing: .08em; text-transform: uppercase; color: var(--ink);
}
/* 占位文字不是码，不要跟着大写、不要用等宽数字体 */
.bind-inp input::placeholder {
  font-family: var(--font); font-size: 12.5px; font-weight: 500;
  letter-spacing: 0; text-transform: none; color: #C4C8D2;
}
/* .btn 默认是 r-full 的胶囊，这里和输入框统一成 r-md */
.bind-btn { flex: none; height: 48px; padding: 0 22px; font-size: 14.5px; border-radius: var(--r-md); }

.bind-note {
  display: flex; align-items: center; gap: 6px;
  margin-top: 11px; font-size: 11px; color: var(--ink-3);
}
.bind-note .ico { width: 14px; height: 14px; flex: none; }

.fr-list {
  margin: 0 var(--pad); border-radius: var(--r-lg); overflow: hidden;
  background: var(--card); box-shadow: 0 3px 0 rgba(27,34,51,.05);
}
.fr { display: flex; align-items: center; gap: 12px; padding: 13px 15px; }
.fr + .fr { box-shadow: inset 0 1px 0 var(--line); }
.fr-av {
  flex: none; width: 42px; height: 42px; border-radius: var(--r-full);
  overflow: hidden; background: var(--paper-2);
}
.fr-av img, .fr-av svg { width: 100%; height: 100%; object-fit: cover; display: block; }
.fr-meta { flex: 1; min-width: 0; }
.fr-meta b {
  display: flex; align-items: center; gap: 6px;
  font-size: 14.5px; font-weight: 700; color: var(--ink);
}
.fr-meta em {
  display: block; font-style: normal; font-size: 11.5px;
  color: var(--ink-3); margin-top: 4px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* ⚠️ `.fr-earn` / `.fr-earn b` / `.fr-earn em` 三条已删除 ——
   好友列表右侧那栏「赚了多少」的旧版式，全项目零引用。
   现在用的好友行结构见 `.fr-meta`（活着，别动）。
   原版在 `_backup/review-fix-before/app.css`。 */
.fr-empty { padding: 26px 15px; text-align: center; font-size: 13px; color: var(--ink-3); }

/* 列表到底提示。
   ---------------------------------------------------------------------------
   记录页三段加起来 40 多行，滚到底以前**什么都不显示** ——
   用户分不清「到底了」还是「还在加载」，会继续往下拽。
   实测 #/market/records 的 scrollTop 能到 750/750（确实有超出一屏的内容），
   而列表尾部连一个元素都没有，所以补这一条。

   ⚠️ 排行榜（#/rank）21 行但一屏放得下（scrollTop 0/0），**没加** ——
      加在那儿只会多一条永远滚不到的线。 */
.list-end {
  display: flex; align-items: center; gap: 10px;
  font-size: 11.5px; color: var(--ink-3); letter-spacing: .06em;
  padding: 20px var(--pad) 6px;
}
.list-end::before, .list-end::after {
  content: ''; flex: 1; height: 1px; background: var(--line);
}

/* ---------- 记录（已并入「交易与记录」的第三个分段） ---------- */
/* 外面已经有一层分段控件了，这里只用分组小标题，不再套第二层 */
.rec-cap {
  padding: 20px var(--pad) 9px;
  font-size: 12px; font-weight: 800; color: var(--ink-3);
  letter-spacing: .04em;
}
.rec-list { margin: 0 var(--pad); }
.rec-cap:first-child { padding-top: 10px; }

/* ---------- 上传头像 ---------- */
.avatar-up { margin: 12px auto 6px; }
.avatar-tip { font-size: 11.5px; color: var(--ink-3); margin-bottom: 16px; }
.avatar-cur img { width: 100%; height: 100%; object-fit: cover; border-radius: var(--r-full); display: block; }

/* 上传后的头像要盖掉那个 emoji 的字号和居中 */
.mine-av-img { width: 100%; height: 100%; object-fit: cover; border-radius: var(--r-full); display: block; }

/* ---------- 危险操作 ---------- */
.mrow.is-danger .mrow-label { color: #E0243F; }
.mrow.is-danger .mrow-go { opacity: .35; }
.warn-box {
  padding: 14px 15px; border-radius: var(--r-lg);
  background: #FFF1F3; box-shadow: inset 0 0 0 1px #FFD9DF;
}
.warn-t {
  display: flex; gap: 8px; align-items: flex-start;
  font-size: 13.5px; font-weight: 700; color: #C11D37; line-height: 1.5;
}
.warn-t .ico { flex: none; width: 16px; height: 16px; margin-top: 2px; }
.warn-list { margin: 10px 0 0; padding: 0 0 0 15px; }
.warn-list li {
  font-size: 12.5px; color: #8A3243; line-height: 1.75;
  list-style: disc;
}

/* ---------- 分享海报 ---------- */
.poster-wrap {
  display: flex; justify-content: center;
  padding: 2px 4px 16px;
}
.poster-canvas {
  /* 海报是**竖版 9:16**（750×1334），比旧版的 750×1200 更高。
     ⚠️ 只写 max-width 会在矮屏上把抽屉撑爆 —— 实测 375×667 的屏上，
     海报底部（邀请码、口号）和下面那排分享按钮**全部掉到屏幕外**，
     而分享按钮恰恰是这个抽屉唯一要做的事。
     所以再加一条**按高度算的上限**：抽屉自己的上限是 88vh，
     减掉抽屉里不随海报变化的那部分（抓手 + 标题 + 分享按钮排 + 上下内边距，
     约 190px，再加刘海屏安全区 34px），剩下的才是海报能用多少。
     宽高都给了上限时，浏览器会按图片自身比例取更严的那一个，不会拉变形。
       高屏（844）：上限 519 高 → 292 宽（max-width 300 基本不生效）
       矮屏（667）：上限 363 高 → 204 宽 */
  width: auto; height: auto;
  max-width: 300px;
  max-height: calc(88vh - 224px);
  display: block;
  border-radius: var(--r-lg);
  box-shadow: 0 8px 26px rgba(27,34,51,.22);
}
.share-row { display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px; }
.share-btn {
  display: flex; flex-direction: column; align-items: center; gap: 7px;
  padding: 10px 2px; border: 0; background: none;
  font-family: inherit; font-size: 11.5px; color: var(--ink-2);
  border-radius: var(--r-md);
}
.share-btn:active { background: var(--paper-2); }
.share-ico {
  display: flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; border-radius: var(--r-full);
  background: var(--paper-2); color: var(--contra);
}
.share-ico .ico { width: 21px; height: 21px; }
.share-ico.wx { background: #E7F7E9; color: #12B34A; }
.share-ico.mo { background: #FFF0E2; color: #FF8A3D; }
.share-ico.dl { background: var(--brand-l); color: var(--brand-d); }

/* 导航栏上的圆形图标按钮（好友页右上角） */
.nav-btn {
  display: flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; padding: 0; border: 0;
  border-radius: var(--r-full); background: var(--paper-2); color: var(--ink);
}
.nav-btn .ico { width: 18px; height: 18px; }
.nav-btn:active { background: var(--line); }


/* 步骤 */
.steps { margin: 0 var(--pad); }
.step { display: flex; gap: 12px; padding: 13px 15px; background: var(--card); }
.step:first-child { border-radius: var(--r-lg) var(--r-lg) 0 0; }
.step:last-child  { border-radius: 0 0 var(--r-lg) var(--r-lg); }
.step + .step { box-shadow: inset 0 1px 0 var(--line); }
.step-n {
  flex: none; width: 24px; height: 24px; border-radius: var(--r-full);
  background: var(--brand-l); color: var(--brand-d);
  font-family: var(--font-num); font-size: 12px; font-weight: 800;
  display: grid; place-items: center;
}
.step-body { flex: 1; min-width: 0; }
.step-body b { display: block; font-size: 13.5px; font-weight: 700; color: var(--ink); }
.step-body em {
  display: block; font-style: normal; font-size: 11.5px; line-height: 1.55;
  color: var(--ink-3); margin-top: 4px;
}

/* ---------- 我的虚粒 ---------- */
.wallet-hero {
  margin: 6px var(--pad) 0; padding: 22px 20px 18px;
  border-radius: var(--r-xl);
  background: linear-gradient(160deg, #23335A 0%, var(--contra) 56%, #16233D 100%);
  position: relative; overflow: hidden;
}
.wallet-hero::after {
  content: ''; position: absolute; top: -46px; right: -30px;
  width: 160px; height: 160px; border-radius: var(--r-full);
  background: radial-gradient(circle, rgba(107,227,255,.22), rgba(107,227,255,0) 68%);
  pointer-events: none;
}
.wallet-label { position: relative; font-size: 11.5px; color: rgba(255,255,255,.55); font-weight: 600; }
.wallet-amount {
  position: relative; display: flex; align-items: center; gap: 9px; margin-top: 9px;
}
.wallet-amount .ico { width: 27px; height: 27px; }
.wallet-amount b {
  font-family: var(--font-num); font-size: 34px; font-weight: 800;
  color: #fff; letter-spacing: -.01em; line-height: 1;
}
.wallet-row {
  position: relative; display: grid; grid-template-columns: repeat(3, 1fr);
  margin-top: 18px; padding-top: 15px;
  border-top: 1px solid rgba(255,255,255,.10);
}
.wallet-row > div { text-align: center; }
.wallet-row b {
  display: block; font-family: var(--font-num);
  font-size: 15px; font-weight: 800; color: var(--yellow); line-height: 1.1;
}
.wallet-row span { display: block; font-size: 10px; color: rgba(255,255,255,.50); margin-top: 4px; }

/* ---------- 实名认证 ---------- */
.verify-hero {
  margin: 6px var(--pad) 18px; padding: 26px 20px 22px;
  border-radius: var(--r-xl); text-align: center;
  background: linear-gradient(160deg, #FFF3E6, #FFE7D2);
}
.verify-hero.ok { background: linear-gradient(160deg, #E4F8F1, #D2F2E7); }
.verify-ico {
  width: 62px; height: 62px; margin: 0 auto 14px;
  border-radius: var(--r-full); display: grid; place-items: center;
  background: linear-gradient(158deg, #FFB572, #F58A2E);
  color: #fff; box-shadow: 0 12px 24px -10px rgba(245,138,46,.75);
}
.verify-hero.ok .verify-ico {
  background: linear-gradient(158deg, #6FE3C0, #10B98D);
  box-shadow: 0 12px 24px -10px rgba(16,185,141,.75);
}
.verify-ico .ico { width: 30px; height: 30px; }
.verify-t { font-size: 17px; font-weight: 800; color: var(--ink); }
.verify-s { font-size: 12px; line-height: 1.6; color: var(--ink-2); margin-top: 8px; }

/* ---------- 客服 ---------- */
.mgr-hero-av.svc {
  background: linear-gradient(158deg, #6FE3C0, #10B98D);
  color: #fff; display: grid; place-items: center;
}
.mgr-hero-av.svc .ico { width: 26px; height: 26px; }

/* ---------- 头像选择 ---------- */
.avatar-pick { padding: 8px var(--pad) 20px; text-align: center; }
.avatar-cur {
  position: relative;
  width: 82px; height: 82px; margin: 0 auto 10px;
  border: 0; padding: 0;
  border-radius: var(--r-full);
  background: linear-gradient(158deg, #FF74A6, var(--brand-d));
  box-shadow: 0 14px 26px -12px rgba(230,58,120,.8);
  font-size: 40px; line-height: 1;
  display: grid; place-items: center;
  cursor: pointer; overflow: visible;
  transition: transform .12s;
}
.avatar-cur:active { transform: scale(.95); }
/* 相机角标：不点开也知道这里能换头像 —— 光靠「点头像」这个惯例不够，
   很多用户不会去点一个看起来只是展示的元素。 */
.avatar-cam {
  position: absolute; right: -2px; bottom: -2px;
  width: 28px; height: 28px; border-radius: var(--r-full);
  background: var(--contra); color: #fff;
  display: grid; place-items: center;
  border: 2.5px solid var(--paper);
  pointer-events: none;
}
.avatar-cam .ico { width: 14px; height: 14px; }
.avatar-tap { font-size: 12.5px; color: var(--ink-2); font-weight: 600; margin-bottom: 8px; }
.avatar-or { font-size: 11.5px; color: var(--ink-3); margin-bottom: 12px; }
/* 4 列固定网格：flex 换行会按容器宽度凑出 7 + 1 这种断行，
   看起来像没对齐。4×2 在 320 到 430 之间都是同一个形状。 */
.avatar-grid {
  display: grid; grid-template-columns: repeat(4, 46px);
  justify-content: center; gap: 12px;
}
.avatar-opt {
  width: 46px; height: 46px;
  border: 0; border-radius: var(--r-full);
  background: var(--card); font-size: 23px; line-height: 1;
  display: grid; place-items: center; cursor: pointer;
  box-shadow: inset 0 0 0 1.5px var(--line);
  -webkit-tap-highlight-color: transparent;
  /* 候选头像是真图（246x246 PNG，四角透明、圆形贴边内切）。
     容器是圆 + overflow:hidden，所以透明角被裁掉，正好跟源图的圆重合。
     background 用 --paper 而不是 --card：源图的圆是浅灰底，
     垫白会看到一圈比图更亮的边。 */
  overflow: hidden;
  background: var(--paper-2);
}
.avatar-opt img {
  width: 100%; height: 100%;
  object-fit: cover; display: block;
}
.avatar-opt.on {
  box-shadow: inset 0 0 0 2.5px var(--brand), 0 0 0 3px var(--brand-l);
}
.avatar-opt:active { transform: scale(.92); }

/* ---------- 记录页 ---------- */
.rec {
  margin: 0 var(--pad) 10px; padding: 14px 15px;
  border-radius: var(--r-md); background: var(--card);
  box-shadow: 0 3px 0 rgba(27,34,51,.05);
}
.rec-top { display: flex; align-items: center; gap: 8px; }
.rec-kind {
  flex: none; padding: 2px 8px; border-radius: var(--r-full);
  background: var(--paper-2); color: var(--ink-2);
  font-size: 10.5px; font-weight: 800;
}
.rec-who {
  flex: 1; min-width: 0; font-size: 13px; font-weight: 700; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.rec-state { flex: none; font-size: 10.5px; color: var(--ink-3); font-weight: 600; }
.rec-time { flex: none; font-size: 10.5px; color: var(--ink-3); font-family: var(--font-num); margin-left: auto; }
.rec-got {
  font-size: 12px; line-height: 1.55; color: var(--ink-2);
  margin-top: 9px;
}
.rec-foot {
  display: flex; align-items: center; gap: 9px; margin-top: 9px;
  padding-top: 9px; box-shadow: inset 0 1px 0 var(--line);
}
.rec-amt {
  font-family: var(--font-num); font-size: 15px; font-weight: 800;
  color: #0A8F6E;
}
.rec-amt.neg { color: var(--ink); }
.rec-fee { font-size: 10.5px; color: var(--ink-3); }

/* ---------- 文本页 ---------- */
.doc { margin: 8px var(--pad) 0; }
.doc-updated {
  font-size: 11.5px; color: var(--ink-3); font-weight: 600;
  padding-bottom: 14px; margin-bottom: 16px;
  box-shadow: inset 0 -1px 0 var(--line);
}
.doc p {
  font-size: 13px; line-height: 1.85; color: var(--ink-2);
  margin-bottom: 13px;
}

/* ==========================================================
   虚粒柜（寄存生息）
   和钱包 hero 的区别：hero 是「我现在有多少」，柜子是「我存了多少」。
   所以柜子不自带大数字，用一行三格的紧凑读数，把视觉主位让给余额。
   ========================================================== */
.vault {
  margin: 16px var(--pad) 0;
  padding: 16px;
  border-radius: var(--r-xl);
  background: var(--card);
  box-shadow: var(--sh-soft);
}
.vault-top { display: flex; align-items: center; gap: 11px; }
.vault-ico {
  width: 38px; height: 38px; flex: none;
  border-radius: var(--r-md);
  background: linear-gradient(158deg, #8B7BFF, #5B45D6);
  color: #fff; display: grid; place-items: center;
  box-shadow: 0 8px 16px -8px rgba(91,69,214,.9);
}
.vault-ico .ico { width: 20px; height: 20px; }
.vault-meta { min-width: 0; flex: 1; }
.vault-t { font-size: 15px; font-weight: 800; letter-spacing: -.01em; }
.vault-s { font-size: 11.5px; color: var(--ink-3); margin-top: 3px; }
.vault-rate {
  flex: none; font-size: 10.5px; color: var(--ink-3); font-weight: 600;
  text-align: right; line-height: 1.4;
}
.vault-rate b { display: block; font-family: var(--font-num); font-size: 16px; color: #5B45D6; }

.vault-nums {
  display: grid; grid-template-columns: repeat(3, 1fr);
  margin-top: 14px; padding-top: 13px;
  box-shadow: inset 0 1px 0 var(--line);
}
.vault-nums > div { text-align: center; }
.vault-nums b {
  display: block; font-family: var(--font-num);
  font-size: 16px; font-weight: 800; letter-spacing: -.02em;
}
.vault-nums span { display: block; font-size: 10.5px; color: var(--ink-3); margin-top: 3px; }

.vault-foot {
  display: grid; grid-template-columns: 1fr 1fr; gap: 10px;
  margin-top: 14px;
}
.vault-foot .btn { justify-content: center; }

/* 存入 / 取出弹窗 */
.vault-form-top {
  display: flex; align-items: baseline; justify-content: space-between;
  font-size: 12px; color: var(--ink-3); margin-bottom: 8px;
}
.vault-form-top b { font-family: var(--font-num); font-size: 15px; color: var(--ink); }
.vault-inp {
  display: flex; align-items: center; gap: 10px;
  padding: 4px 16px; border-radius: var(--r-lg);
  background: var(--paper-2, rgba(0,0,0,.03));
  box-shadow: inset 0 0 0 1.5px var(--line);
}
.vault-inp .ico { width: 20px; height: 20px; color: var(--brand); flex: none; }
.vault-inp input {
  flex: 1; min-width: 0; border: 0; background: none; outline: none;
  font-family: var(--font-num); font-size: 28px; font-weight: 800;
  letter-spacing: -.02em; color: var(--ink); padding: 14px 0;
  -moz-appearance: textfield;
}
.vault-inp input::-webkit-outer-spin-button,
.vault-inp input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.vault-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
.vchip {
  border: 0; cursor: pointer;
  padding: 8px 14px; border-radius: var(--r-full);
  background: var(--card); color: var(--ink-2);
  font-family: var(--font-num); font-size: 13px; font-weight: 700;
  box-shadow: inset 0 0 0 1.5px var(--line);
}
.vchip:active { transform: scale(.96); background: rgba(255,77,141,.10); }
.vault-min { font-size: 11px; color: var(--ink-3); margin-top: 10px; }

/* ==========================================================
   店长名录卡片
   ----------------------------------------------------------
   一张卡 = 上下两段，中间一条分隔线：
     上段：头像 + 名字 + 资质      → 点它看这位的二级店长
     下段：微信 / QQ 两个胶囊      → 点哪个复制哪个
   两段必须**并列**，不能嵌套：按钮里不能再放按钮。
   （上一版把联系方式做成两行，每行右边还挂一个「复制」按钮，
     三个白盒子叠在一起，又花又撞。）
   ========================================================== */
.mgr2 {
  background: var(--card);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-soft);
  overflow: hidden;
}
.mgr2-top {
  width: 100%; border: 0; background: none; cursor: pointer;
  display: flex; align-items: center; gap: 12px;
  padding: 14px 14px 13px;
  text-align: left;
  font: inherit; color: inherit;
  -webkit-tap-highlight-color: transparent;
  transition: background .12s ease;
}
.mgr2-top:active { background: var(--paper-2); }

/* 下段：两个胶囊并排。上边框就是分隔线，不用另加 <hr>。 */
.mgr-ct {
  display: flex; gap: 8px;
  padding: 11px 12px 12px;
  border-top: 1px solid var(--line);
  background: linear-gradient(180deg, rgba(247,243,237,.55), rgba(247,243,237,0));
}
.ct-pill {
  flex: 1; min-width: 0;
  display: flex; align-items: center; gap: 7px;
  height: 38px; padding: 0 10px;
  border: 0; border-radius: var(--r-sm);
  background: var(--card);
  font: inherit; color: inherit; text-align: left;
  cursor: pointer; -webkit-tap-highlight-color: transparent;
  box-shadow: 0 0 0 1px var(--line);
  transition: transform .1s ease, box-shadow .1s ease;
}
.ct-pill:active {
  transform: scale(.97);
  box-shadow: 0 0 0 1px rgba(255,77,141,.4);
}
/* 微信绿 / QQ 蓝：这两个颜色本身就是信息，扫一眼就知道哪个是哪个，
   所以不必再靠文字去分辨。 */
.ct-tag {
  flex: none;
  padding: 2px 6px; border-radius: var(--r-xs);
  font-size: 10px; font-weight: 800; line-height: 1.5; color: #fff;
  letter-spacing: .02em;
}
.ct-pill.wx .ct-tag { background: #22A85A; }
.ct-pill.qq .ct-tag { background: #2C8FE0; }
/* ⚠️ 同名的另一条在客服页那一组（搜「`.ct-meta .ct-val`」）。
   两条都必须带父级作用域，只写 `.ct-val` 会互相覆盖。 */
.ct-pill .ct-val {
  flex: 1; min-width: 0;
  font-family: var(--font-num); font-size: 12.5px; font-weight: 700;
  color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* 这个图标是「能点」的提示，不是第二个按钮（它自己不可点、也没文案） */
.ct-ico { flex: none; width: 13px; height: 13px; color: var(--ink-3); }
.ct-pill:active .ct-ico { color: var(--brand); }

/* 榜单：推荐榜 / 店长榜的右侧数字下面是单位词，比虚粒多一行 */
.rank-gems em {
  display: block; font-style: normal;
  font-size: 10px; font-weight: 600; color: var(--ink-3); margin-top: 1px;
}
.rank-name i {
  display: block; font-style: normal;
  font-size: 10.5px; font-weight: 600; color: var(--ink-3); margin-top: 2px;
}

/* =========================================================================
   品牌亮相（启动页 + 关于我们页的品牌牌）
   -------------------------------------------------------------------------
   目标：把「App 叫什么 / 说什么」放到界面上。这两个地方是同一个视觉母题，
   共用一套排版，走的是 .mgr-hero 那块深色卡的渐变和光斑语言 ——
   品牌牌不能长得跟自己 App 的卡片不一样。
   ========================================================================= */

/* ---------- 启动页：冷启动一次，约 1 秒 ---------- */
.splash {
  position: fixed; inset: 0; z-index: 900;
  display: grid; place-items: center;
  background: linear-gradient(165deg, #24365C 0%, var(--contra) 52%, #0A1120 100%);
  opacity: 0; transition: opacity .38s ease;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.splash.in { opacity: 1; }
/* 光斑慢慢呼吸。启动页只有一秒，静止的光斑会显得是「卡住的画面」 */
.splash-glow {
  position: absolute; width: 340px; height: 340px; border-radius: 50%;
  background: radial-gradient(circle, rgba(255,77,141,.36), transparent 66%);
  animation: splashPulse 2.6s ease-in-out infinite;
  pointer-events: none;
}
@keyframes splashPulse {
  0%, 100% { transform: scale(.9);  opacity: .7; }
  50%      { transform: scale(1.07); opacity: 1; }
}
.splash-body { position: relative; text-align: center; color: #fff; padding: 0 24px; }
.splash-mark {
  font-size: 46px; font-weight: 900;
  letter-spacing: .2em; text-indent: .2em;   /* 字距会在右侧多留一格，补掉 */
  text-shadow: 0 6px 30px rgba(255,77,141,.55);
}
.splash-en {
  margin-top: 13px;
  font-family: var(--font-num); font-size: 10.5px; font-weight: 700;
  letter-spacing: .44em; text-indent: .44em;
  color: rgba(255,255,255,.48);
}
.splash-rule, .db-rule {
  width: 30px; height: 2px; border-radius: var(--r-full);
  margin: 19px auto 17px;
  background: var(--brand);
  box-shadow: var(--sh-glow-brand);
}
.splash-slogan {
  font-size: 14.5px; font-weight: 700;
  letter-spacing: .16em; text-indent: .16em;
  color: rgba(255,255,255,.88);
}

/* ---------- 关于我们页顶部的品牌牌 ---------- */
.doc-brand {
  margin: 4px var(--pad) 18px; padding: 30px 20px 26px;
  border-radius: var(--r-xl);
  background: linear-gradient(158deg, #24365C 0%, var(--contra) 55%, #0D1526 100%);
  color: #fff; text-align: center;
  position: relative; overflow: hidden;
  box-shadow: 0 16px 34px -16px rgba(16,26,49,.8);
}
/* 和 .mgr-hero::after 同一套光斑，保证两块深色卡是一家人 */
.doc-brand::after {
  content: ''; position: absolute; top: -70px; right: -50px;
  width: 200px; height: 200px; border-radius: 50%;
  background: radial-gradient(circle, rgba(255,201,60,.34), transparent 68%);
  pointer-events: none;
}
.db-mark {
  position: relative; z-index: 1;
  font-size: 32px; font-weight: 900;
  letter-spacing: .18em; text-indent: .18em;
}
.db-en {
  position: relative; z-index: 1;
  margin-top: 10px;
  font-family: var(--font-num); font-size: 10px; font-weight: 700;
  letter-spacing: .38em; text-indent: .38em;
  color: rgba(255,255,255,.5);
}
.db-rule { position: relative; z-index: 1; }
.db-slogan {
  position: relative; z-index: 1;
  font-size: 13.5px; font-weight: 700;
  letter-spacing: .14em; text-indent: .14em;
  color: rgba(255,255,255,.9);
}

/* ===========================================================================
   登录 / 注册（session.js）

   为什么单独开一段而不是复用启动页（.splash / .db-*）：
   启动页是**一次性**的、1 秒就过，样式可以随便上不透明度动画；
   登录页是**状态**，用户可能在上面停很久（输错密码、等限流冷却），
   所以它需要的是表单的清晰度，不是仪式感。两段只是共用了同一套品牌元素
   （字标 / 英文名 / 短横线 / slogan），尺寸和颜色都跟着变量走。

   表单本身复用 .field / .field input / .btn —— 不另起一套输入框样式，
   否则"登录页的输入框"和"App 里的输入框"会长得不一样。
   =========================================================================== */
.auth {
  position: relative;
  min-height: 100%;
  display: flex; flex-direction: column;
  padding: 30px var(--pad) calc(env(safe-area-inset-bottom) + 34px);
  overflow: hidden;
}
/* 顶部一团品牌色晕光。纯装饰，必须 pointer-events:none，
   不然它会盖住下面的表单把点击全吃掉。 */
.auth-glow {
  position: absolute; left: 50%; top: -170px;
  width: 430px; height: 430px; margin-left: -215px;
  border-radius: 50%; pointer-events: none;
  background: radial-gradient(circle, var(--brand-l) 0%, rgba(255,228,239,0) 68%);
}
.auth-head { position: relative; text-align: center; padding: 26px 0 24px; }
.auth-mark {
  font-size: 34px; font-weight: 900;
  letter-spacing: .16em; text-indent: .16em; color: var(--ink);
}
.auth-mark.sm { font-size: 26px; }
.auth-en {
  margin-top: 7px;
  font-family: var(--font-num); font-size: 10.5px; font-weight: 700;
  letter-spacing: .24em; text-indent: .24em; color: var(--ink-2);
}
.auth-rule {
  width: 38px; height: 2px; margin: 13px auto;
  border-radius: 2px; background: var(--brand);
}
.auth-slogan {
  font-size: 13px; font-weight: 700;
  letter-spacing: .12em; text-indent: .12em; color: var(--ink-2);
}

.auth-tabs {
  position: relative;
  display: grid; grid-template-columns: 1fr 1fr; gap: 5px;
  padding: 5px; margin-bottom: 22px;
  border-radius: var(--r-full); background: var(--sunk);
}
.auth-tab {
  height: 40px; border-radius: var(--r-full);
  font-size: 14px; font-weight: 800; color: var(--ink-2);
  transition: background .15s, color .15s;
}
.auth-tab.on { background: var(--card); color: var(--ink); box-shadow: var(--sh-tap); }

.auth-form { position: relative; }
/* [disabled] 一定要管：提交中按钮不变样的话，用户会以为没点上而连点，
   而登录限流是 5 次/分钟/IP —— 连点直接把自己锁在外面 1 分钟。 */
.btn[disabled] { opacity: .55; pointer-events: none; }

/* 提示区**恒定占高**。不留高度的话，出现错误信息时整个表单会往下跳一格，
   用户的手指正好在按钮上，第二次点击就点歪了。 */
.auth-hint {
  min-height: 36px; margin-top: 13px;
  font-size: 12px; line-height: 1.6; color: var(--ink-2);
}
.auth-hint.warn { color: #A9660A; }
.auth-hint.bad  { color: #D01C48; }
.auth-hint.ok   { color: #0A8F73; }

.auth-loading { margin: auto 0; text-align: center; }
.auth-loading-text {
  margin-top: 16px; font-size: 13px; color: var(--ink-2);
}

/* ===========================================================================
   店长页顶部的「渠道进度」条（pages.js 的 channelStrip）

   用深色底（--contra）而不是纸色卡片：它是这一页唯一一块**真数据**，
   而它下面是一长串同质的名片卡。同色会糊成一片，深色把它单拎出来。
   进度条的填充色走 brand → yellow 的渐变，和 App 里其它"进度/增长"的
   语言一致（虚粒柜、卡产出都用的这一套）。
   =========================================================================== */
.ch-strip {
  margin: 0 var(--pad) 16px;
  padding: 15px 16px;
  border-radius: var(--r-md);
  color: #fff;
  background: linear-gradient(180deg, #33486F 0%, var(--contra) 55%, var(--contra-d) 100%);
  box-shadow: 0 10px 22px -12px rgba(27,42,74,.8);
}
.ch-top { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.ch-name { font-size: 14px; font-weight: 800; letter-spacing: .02em; }
.ch-need {
  font-family: var(--font-num); font-size: 12.5px; font-weight: 800;
  color: var(--yellow);
}
.ch-bar {
  height: 7px; margin: 12px 0 9px;
  border-radius: var(--r-full); background: rgba(255,255,255,.16);
  overflow: hidden;
}
.ch-bar > i {
  display: block; height: 100%; width: 0;
  border-radius: var(--r-full);
  background: linear-gradient(90deg, var(--brand) 0%, var(--yellow) 100%);
  transition: width .35s cubic-bezier(.2,.9,.3,1);
}
.ch-sub { font-size: 11.5px; line-height: 1.5; color: rgba(255,255,255,.72); }
