/* =====================================================================
   球拍养护 · 四大满贯主题系统
   ---------------------------------------------------------------------
   结构层(本文件上半)不含任何具体颜色/质感,全部读 token。
   四套赛事主题(本文件下半)各自定义:表面、场地线、强调色、质感、
   字体、圆角、转场曲线、按钮形态。切换只换 .phone 上的一个类。

   共同骨架:内容不是"浮在背景上的卡片",而是"在球场表面上划出的分区"。
   四种场地的差别决定了分区长什么样 —— 土上是石灰线,草上是剪草边界,
   硬地上是漆线,夜场是灯光下的高反差漆线。

   老 webview 兼容:不用 color-mix()、不用 :has()、backdrop-filter 带前缀。
   ===================================================================== */

/* =====================================================================
   A. 结构层 —— 与主题无关
   ===================================================================== */

:root{
  --sans:"Noto Sans SC",-apple-system,system-ui,"PingFang SC",sans-serif;
  --serif:"Noto Serif SC",Georgia,"Songti SC","SimSun",serif;
  --mono:"Space Grotesk",-apple-system,"SF Pro Text",system-ui,"Helvetica Neue",sans-serif;

  /* 以下 token 由各赛事主题覆盖,这里只给安全兜底 */
  --surface:#2A2A2A; --surface-hi:#3A3A3A; --surface-lo:#1A1A1A; --surface-deep:#0E0E0E;
  --line:#FFFFFF; --line-worn:rgba(255,255,255,.5); --line-ghost:rgba(255,255,255,.2);
  --acc:#D9E84F; --acc-lo:#AFBE2E; --acc-ink:#1F2404; --acc-text:#D9E84F;
  --ink:#FFFFFF; --ink-dim:rgba(255,255,255,.7); --ink-faint:rgba(255,255,255,.5);
  --net:#241F1C;
  --ok:#8BC34A; --warn:#E8A33C; --hot:#E4692F; --dead:#C8402F;
  --rkstring:rgba(255,255,255,.42);

  --disp:var(--sans);          /* 标题字族(温网换衬线) */
  --disp-w:900;                /* 标题字重 */
  --disp-ls:-.035em;           /* 标题字距 */
  --led:var(--mono);

  --lw:2.5px;                  /* 场地线宽 */
  --r:0px;                     /* 圆角 */
  --r-sm:0px;
  --ease:cubic-bezier(.3,1.15,.4,1);
  --ease-out:cubic-bezier(.22,.95,.28,1);
  --dur:.42s;                  /* 转场时长 */
  --frame:#191410;             /* 设备外框内圈 */
  --gauge-h:10px; --gauge-r:0px;
  --cell-bg:rgba(0,0,0,.5); --cell-bd:rgba(255,255,255,.14); --r-cell:22px;
  --gauge-track:rgba(255,255,255,.14); --gauge-fill:#FFFFFF;
  --shadow-obj:rgba(0,0,0,.34);/* 实物投影色 */
}

/* 字重变量。中文那侧在 AT 段被压到 600 —— 苹方最高只有 Semibold,
   写 800 只会让 iOS 合成一个把笔画横向撑开的假粗体。 */
:root{--w7:700; --w8:800; --w9:900}
*{box-sizing:border-box;margin:0;padding:0;-webkit-tap-highlight-color:transparent}
html,body{height:100%}
body{background:#0B0B0B;color:var(--ink);font-family:var(--sans);
  -webkit-font-smoothing:antialiased;display:flex;align-items:center;justify-content:center;
  min-height:100dvh;padding:env(safe-area-inset-top) 0}

.phone{position:relative;width:100%;max-width:412px;height:min(890px,96dvh);
  overflow:hidden;border-radius:46px;background:var(--surface);
  box-shadow:0 40px 110px -30px rgba(0,0,0,.75),
             inset 0 0 0 8px var(--frame), inset 0 0 0 9px rgba(255,255,255,.06)}
html.native body{display:block;padding:0}
html.native .phone{width:100%;max-width:none;height:100dvh;border-radius:0;box-shadow:none;
  padding-top:env(safe-area-inset-top)}
html.native .island{display:none}
html.native .tabbar{padding-bottom:max(24px,env(safe-area-inset-bottom))}

.island{position:absolute;top:11px;left:50%;transform:translateX(-50%);
  width:104px;height:30px;background:var(--frame);border-radius:18px;z-index:50}

/* ---- 场地表面:四层,内容由主题定义 ---- */
.court{position:absolute;inset:0;z-index:0;overflow:hidden}
.court .base,.court .tex,.court .tex2,.court .vig{position:absolute;pointer-events:none}
.court .base{inset:0}
.court .tex,.court .tex2{inset:-20%}
.court .vig{inset:0}

.view{position:absolute;inset:0;z-index:2;display:flex;flex-direction:column}
.screen{flex:1;overflow-y:auto;overflow-x:hidden;padding:52px 18px 104px;
  scrollbar-width:none;background:transparent}
.screen::-webkit-scrollbar{display:none}
html.native .screen{padding-top:calc(52px + env(safe-area-inset-top))}

/* 转场:每套赛事有自己的 keyframes,这里只挂变量 */
.screen{animation:var(--enter) var(--dur) var(--ease-out)}

/* ---- 排版 ---- */
.eyebrow{font-family:var(--mono);font-size:10px;letter-spacing:0.117em;
  text-transform:uppercase;color:var(--ink-faint);font-weight:500}
.h1{font-family:var(--disp);font-weight:var(--disp-w);font-size:32px;line-height:1.02;
  letter-spacing:var(--disp-ls);color:var(--line)}
.h1 em{font-style:normal;color:var(--acc-text)}
.topbar{display:flex;align-items:center;justify-content:space-between;margin-bottom:18px;gap:10px}

/* ---- 分区 ---- */
.zone{position:relative;border:var(--lw) solid var(--line-worn);border-radius:var(--r);
  padding:16px;background:var(--fill-1)}
.zone.deep{background:var(--fill-2);border-color:var(--line-ghost)}
.zone::before,.zone::after{content:"";position:absolute;width:13px;height:13px;
  border:var(--lw) solid var(--line);pointer-events:none}
.zone::before{left:calc(var(--lw) * -1);top:calc(var(--lw) * -1);border-right:0;border-bottom:0}
.zone::after{right:calc(var(--lw) * -1);bottom:calc(var(--lw) * -1);border-left:0;border-top:0}

.sect{margin-top:26px}
.sect h3{font-family:var(--disp);font-size:12.5px;color:var(--line);font-weight:var(--w8);
  letter-spacing:.02em;display:flex;align-items:center;gap:9px;margin-bottom:11px;
  padding-bottom:7px;border-bottom:var(--lw) solid var(--line-ghost)}
.sect h3 .en{font-family:var(--mono);font-size:9px;color:var(--ink-faint);
  letter-spacing:0.09em;text-transform:uppercase;font-weight:500;margin-left:auto}

/* ---- 按钮 ---- */
.btn{border:0;border-radius:var(--r-sm);padding:15px 16px;font-family:var(--sans);
  font-weight:var(--w8);font-size:14px;cursor:pointer;position:relative;
  display:flex;align-items:center;justify-content:center;gap:8px;
  transition:transform .14s var(--ease),filter .18s,box-shadow .18s;letter-spacing:.01em}
.btn svg{width:16px;height:16px}
.btn.wide{width:100%;grid-column:1 / -1}
.btn.primary{background:var(--acc);color:var(--acc-ink);box-shadow:var(--btn-shadow)}
.btn.primary:active{transform:translateY(2px);box-shadow:var(--btn-shadow-a)}
.btn.ghost{background:transparent;border:var(--lw) solid var(--line-worn);color:var(--line)}
.btn.ghost:active{transform:scale(.975);background:var(--fill-hi);border-color:var(--line)}
.btn.dark{background:var(--net);color:var(--line);border:var(--lw) solid rgba(255,255,255,.12)}
.btn.dark:active{transform:scale(.975)}

.iconbtn{width:40px;height:40px;border-radius:50%;background:transparent;
  border:var(--lw) solid var(--line-worn);display:flex;align-items:center;justify-content:center;
  cursor:pointer;color:var(--line);flex:0 0 auto;position:relative;
  transition:transform .16s,background .18s,border-color .18s}
.iconbtn svg{width:17px;height:17px}
.iconbtn:active{transform:scale(.9);background:var(--fill-hi);border-color:var(--line)}

/* ---- 落在场地上的实物 ---- */
.obj{position:relative;display:flex;align-items:center;justify-content:center}
.obj::after{content:"";position:absolute;left:50%;bottom:2%;width:64%;height:9%;
  transform:translateX(-50%);background:radial-gradient(50% 50%,var(--shadow-obj),transparent 72%);
  filter:blur(3px);pointer-events:none}
.obj svg,.obj img{position:relative;z-index:1;filter:drop-shadow(4px 10px 10px var(--shadow-obj))}

/* ---- 首页 ---- */
.ctop{display:flex;justify-content:space-between;align-items:flex-start;gap:10px}
.ctop .ttl{font-family:var(--disp);font-weight:var(--disp-w);font-size:46px;line-height:.9;
  letter-spacing:var(--disp-ls);color:var(--line);margin:14px 0 0 -1px}
.ctop .ttl em{font-style:normal;color:var(--acc-text)}
.ctop .sub{font-family:var(--mono);font-size:10.5px;letter-spacing:0.081em;
  color:var(--ink-faint);text-transform:uppercase;margin-top:8px}

.hero{position:relative;margin-top:20px;padding:18px 16px 16px;border-radius:var(--r);
  border:var(--lw) solid var(--line);background:var(--fill-1);cursor:pointer;overflow:hidden;
  transition:transform .22s var(--ease)}
.hero:active{transform:scale(.987)}
.hero .dust{position:absolute;inset:0;pointer-events:none;opacity:0;
  background:var(--impact);transition:opacity .5s}
.hero:active .dust{opacity:1;transition:opacity .09s}
.hero .fig{position:relative;height:302px;margin:6px 0 4px;
  display:flex;align-items:center;justify-content:center}
.hero .fig svg,.hero .fig img{height:100%;width:auto;filter:drop-shadow(6px 16px 14px var(--shadow-obj))}
.hero .fig .real{position:absolute;inset:0;width:100%;height:100%;object-fit:contain}
.hero .fig::after{content:"";position:absolute;left:50%;bottom:-2px;width:52%;height:16px;
  transform:translateX(-50%);background:radial-gradient(50% 50%,var(--shadow-obj),transparent 72%);
  filter:blur(4px);
  animation:shadowSettle var(--land-dur,1.5s) var(--land-ease,cubic-bezier(.16,1,.3,1)) both}
/* 镜头还在拉远时不该有落地影 —— 球拍还没落地 */
@keyframes shadowSettle{0%,58%{opacity:0}100%{opacity:1}}
.hero .nm{display:flex;justify-content:space-between;align-items:flex-end;gap:10px}
.hero .nm b{font-family:var(--disp);font-size:24px;font-weight:var(--disp-w);color:var(--line);letter-spacing:-.02em}
.hero .nm .md{font-family:var(--mono);font-size:10px;letter-spacing:0.063em;
  color:var(--ink-faint);text-transform:uppercase;margin-top:3px}
.hero.show{cursor:default;padding:14px}
.hero.show canvas{position:relative;z-index:2;display:block;touch-action:none;margin:0 auto}

.specs{display:grid;grid-template-columns:1fr 1fr 1fr;margin-top:14px;border-radius:var(--r-sm);
  border:var(--lw) solid var(--line-worn);background:var(--fill-2);overflow:hidden}
.specs div{padding:11px 6px;text-align:center;border-right:var(--lw) solid var(--line-worn)}
.specs div:last-child{border-right:0}
.specs b{display:block;font-family:var(--led);font-weight:var(--w9);font-size:23px;line-height:1;
  color:var(--line);font-variant-numeric:tabular-nums}
.specs span{display:block;font-size:9.5px;color:var(--ink-faint);margin-top:5px;letter-spacing:0.027em}

.gauge{margin-top:16px}
.gauge .gh{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:9px}
.gauge .gh .t{font-size:11px;color:var(--ink-dim);letter-spacing:.04em}
.gauge .gh .v{font-family:var(--mono);font-weight:var(--w7);font-size:12.5px;letter-spacing:0.027em}
.gauge .track{position:relative;height:var(--lw);background:var(--line-ghost)}
.gauge .fill{position:absolute;left:0;top:0;bottom:0;background:var(--line);
  transition:width .7s cubic-bezier(.25,.9,.25,1)}
.gauge .mk{position:absolute;top:50%;width:var(--lw);height:11px;transform:translate(-50%,-50%);
  background:var(--line-ghost)}
.gauge .mk.done{background:var(--line)}
.gauge .mk.now{width:13px;height:13px;border-radius:50%;background:var(--acc);box-shadow:var(--mk-glow)}
.gauge .lbs{display:flex;justify-content:space-between;margin-top:11px}
.gauge .lbs span{font-size:9.5px;color:var(--ink-faint);font-family:var(--mono);
  flex:1;text-align:center;line-height:1.5;letter-spacing:.03em}
.gauge .lbs span:first-child{text-align:left}
.gauge .lbs span:last-child{text-align:right}

.row2{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-top:10px}
.mini{position:relative;border:var(--lw) solid var(--line-worn);border-radius:var(--r);padding:12px;
  min-height:112px;display:flex;flex-direction:column;cursor:pointer;overflow:hidden;
  background:var(--fill-1);transition:transform .2s var(--ease),border-color .2s}
.mini:active{transform:scale(.965);border-color:var(--line)}
.mini .mf{position:absolute;right:10px;top:11px;width:44px;height:60px;opacity:.26}
.mini .mf svg{width:100%;height:100%;transform:rotate(13deg)}
.mini .mn{font-weight:var(--w8);font-size:14px;color:var(--line);z-index:2;letter-spacing:-.01em}
.mini .md{font-family:var(--mono);font-size:9px;color:var(--ink-faint);z-index:2;margin-top:2px;letter-spacing:0.036em}
.mini .ms{margin-top:auto;display:flex;align-items:center;gap:6px;font-size:10px;
  color:var(--ink-dim);font-weight:var(--w7);z-index:2;font-family:var(--mono)}
.mini .ms i{width:6px;height:6px;border-radius:50%;flex:0 0 auto}
.add{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:9px;
  border:var(--lw) dashed var(--line-ghost);border-radius:var(--r);background:transparent;
  min-height:112px;color:var(--ink-faint);cursor:pointer;transition:transform .2s,border-color .2s}
.add:active{transform:scale(.965);border-color:var(--line-worn)}
.add .p{width:34px;height:34px;border-radius:50%;border:var(--lw) solid var(--line-ghost);
  display:flex;align-items:center;justify-content:center;font-size:19px;color:var(--line)}
.add span{font-size:11px;letter-spacing:.04em}

.say{margin-top:14px;padding-left:14px;border-left:var(--lw) solid var(--acc-text);
  font-size:13px;line-height:1.6;color:var(--ink);font-style:italic}
.say .who{display:block;font-family:var(--mono);font-size:9px;letter-spacing:0.072em;
  color:var(--acc-text);font-style:normal;margin-bottom:4px;text-transform:uppercase}

/* ---- 赛事切换器 ---- */
.slampick{display:flex;flex-direction:column;gap:6px;flex:0 0 auto}
.slamrow{display:flex;border:var(--lw) solid var(--line-ghost);border-radius:var(--r-sm);overflow:hidden}
.slamrow b{font-family:var(--mono);font-size:9px;font-weight:var(--w7);letter-spacing:0.045em;
  padding:7px 8px;color:var(--ink-faint);cursor:pointer;position:relative;
  border-right:var(--lw) solid var(--line-ghost);transition:background .2s,color .2s}
.slamrow b:last-child{border-right:0}
.slamrow b.on{background:var(--line);color:var(--surface-deep)}
.slamrow b::after{content:"";position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);
  width:max(100%,44px);height:44px}

/* 我的页:完整赛事选择 */
.slamgrid{display:grid;grid-template-columns:1fr 1fr;gap:9px}
.slamcard{position:relative;border:var(--lw) solid var(--line-ghost);border-radius:var(--r);
  padding:13px;cursor:pointer;overflow:hidden;min-height:82px;
  display:flex;flex-direction:column;justify-content:space-between;
  transition:transform .2s var(--ease),border-color .2s}
.slamcard:active{transform:scale(.965)}
.slamcard.on{border-color:var(--line)}
.slamcard .sw{position:absolute;right:12px;top:12px;width:38px;height:38px;border-radius:50%;opacity:.95}
.slamcard .sn{font-weight:var(--w8);font-size:14px;color:var(--line);z-index:2;position:relative}
.slamcard .se{font-family:var(--mono);font-size:8.5px;letter-spacing:0.063em;text-transform:uppercase;
  color:var(--ink-faint);margin-top:3px;z-index:2;position:relative}
.slamcard .ss{font-family:var(--mono);font-size:9.5px;color:var(--ink-dim);z-index:2;
  position:relative;margin-top:auto;letter-spacing:0.027em}
/* 四个色卡不受当前主题影响,永远显示各自场地色 */
.sw-rg{background:radial-gradient(circle at 34% 30%,#C97246,#8E4224)}
.sw-ao{background:radial-gradient(circle at 34% 30%,#2E7CD6,#123E75)}
.sw-wb{background:radial-gradient(circle at 34% 30%,#2E6B3E,#14361F)}
.sw-uso{background:radial-gradient(circle at 34% 30%,#2F6FE0,#0C1E4A)}

/* ---- 球拍库 ---- */
.searchbar{display:flex;align-items:center;gap:10px;border-radius:var(--r-sm);
  border:var(--lw) solid var(--line-worn);padding:12px 13px;margin-top:14px;background:var(--fill-2)}
.searchbar svg{width:16px;height:16px;color:var(--ink-faint);flex:0 0 auto}
.searchbar input{flex:1;border:0;background:transparent;color:var(--ink);
  font-family:var(--sans);font-size:14px;outline:none;min-width:0}
.searchbar input::placeholder{color:var(--ink-faint)}
.searchbar .clr{width:22px;height:22px;border-radius:50%;background:var(--line-ghost);
  color:var(--line);display:none;align-items:center;justify-content:center;
  font-size:14px;line-height:1;cursor:pointer;flex:0 0 auto;position:relative}

.chips{display:flex;gap:8px;overflow-x:auto;scrollbar-width:none;margin:16px -18px 4px;padding:2px 18px 8px}
.chips::-webkit-scrollbar{display:none}
.chip{flex:0 0 auto;font-size:12px;font-weight:var(--w7);padding:9px 14px;position:relative;
  border-radius:var(--r-sm);border:var(--lw) solid var(--line-ghost);background:transparent;
  color:var(--ink-dim);cursor:pointer;letter-spacing:.02em;
  transition:transform .2s var(--ease),background .2s,color .2s,border-color .2s}
.chip.on{background:var(--line);color:var(--surface-deep);border-color:var(--line)}
.chip:active{transform:scale(.93)}

.grouphd{font-family:var(--mono);font-size:10px;letter-spacing:0.099em;text-transform:uppercase;
  color:var(--ink-faint);font-weight:var(--w7);margin:24px 0 8px;
  padding-bottom:7px;border-bottom:var(--lw) solid var(--line-ghost)}

.libcard{display:flex;gap:14px;align-items:center;padding:13px 14px;margin-top:10px;
  cursor:pointer;position:relative;overflow:hidden;border-radius:var(--r);
  border:var(--lw) solid var(--line-ghost);background:var(--fill-1);
  transition:transform .2s var(--ease),border-color .2s,background .2s}
.libcard:active{transform:scale(.982);border-color:var(--line-worn);background:var(--fill-2)}
.libcard .thumb{width:60px;height:86px;flex:0 0 auto}
.libcard .thumb svg,.libcard .thumb img{height:84px;width:auto}
.libcard .li{flex:1;min-width:0;z-index:2}
.libcard .lb{font-family:var(--mono);font-size:9px;letter-spacing:0.09em;text-transform:uppercase;color:var(--ink-faint)}
.libcard .lm{font-family:var(--disp);font-weight:var(--w8);font-size:16.5px;margin-top:2px;
  color:var(--line);letter-spacing:-.015em}
.libcard .lmeta{font-size:10.5px;color:var(--ink-dim);font-family:var(--mono);margin-top:4px;letter-spacing:.03em}
.libcard .chev{color:var(--ink-faint);z-index:2;flex:0 0 auto}

.tag{display:inline-block;margin-top:8px;margin-right:5px;font-family:var(--mono);font-size:9.5px;
  font-weight:var(--w7);letter-spacing:0.045em;padding:4px 8px;border-radius:var(--r-sm);
  border:var(--lw) solid var(--line-ghost);color:var(--ink-dim)}
.tag.ball{border-color:var(--acc-text);color:var(--acc-text)}
.tag.solid{background:var(--line);border-color:var(--line);color:var(--surface-deep)}

/* ---- 详情 ---- */
.pullhint{position:absolute;top:12px;left:0;right:0;text-align:center;z-index:3;
  font-family:var(--mono);font-size:10px;color:var(--ink-faint);letter-spacing:0.063em;transition:opacity .25s}
.dbadge{position:absolute;top:12px;right:12px;z-index:3;font-family:var(--mono);font-size:9.5px;
  letter-spacing:0.054em;color:var(--ink-dim);border:var(--lw) solid var(--line-ghost);
  padding:5px 9px;border-radius:var(--r-sm)}

.vital{display:flex;align-items:flex-end;justify-content:space-between;margin:20px 0 8px;gap:12px}
.vital .big{font-family:var(--led);font-weight:var(--w9);font-size:66px;line-height:.78;
  color:var(--line);letter-spacing:-.04em;font-variant-numeric:tabular-nums}
.vital .pct{font-family:var(--led);font-weight:var(--w7);font-size:22px;color:var(--ink-faint);margin-left:3px}
.vital .lab{text-align:right}
.vital .lab .st{font-weight:var(--w9);font-size:15px;letter-spacing:-.01em}
.vital .lab .sub{font-size:9.5px;color:var(--ink-faint);font-family:var(--mono);
  letter-spacing:0.063em;margin-top:4px;text-transform:uppercase}
.bar{height:9px;background:var(--fill-2);position:relative;
  border:var(--lw) solid var(--line-ghost);border-radius:var(--r-sm);overflow:hidden}
.bar > i{position:absolute;left:0;top:0;bottom:0;transition:width .7s cubic-bezier(.25,.9,.25,1)}

.statrow{display:grid;grid-auto-flow:column;grid-auto-columns:1fr;margin-top:16px;
  border:var(--lw) solid var(--line-worn);background:var(--fill-2);
  border-radius:var(--r-sm);overflow:hidden}
.statcard{padding:13px 11px;border-right:var(--lw) solid var(--line-worn);cursor:pointer;transition:background .2s}
.statcard:last-child{border-right:0}
.statcard:active{background:var(--fill-hi)}
.statcard .k{font-size:9.5px;color:var(--ink-faint);font-family:var(--mono);
  letter-spacing:0.072em;text-transform:uppercase}
.statcard .v{font-family:var(--led);font-weight:var(--w9);font-size:26px;margin-top:6px;
  color:var(--line);letter-spacing:-.02em;font-variant-numeric:tabular-nums}
.statcard .vsub{font-size:10px;color:var(--ink-dim);margin-top:3px}

.actions{display:grid;grid-template-columns:1fr 1fr;gap:9px;margin-top:16px}

.advice{background:var(--fill-2);border:var(--lw) solid var(--line-ghost);border-radius:var(--r);
  padding:13px 15px;font-size:12.5px;line-height:1.7;color:var(--ink-dim)}
.advice b{color:var(--line);font-weight:var(--w8)}

.trait{display:grid;grid-template-columns:44px 1fr auto;align-items:center;gap:12px;
  padding:11px 0;border-bottom:var(--lw) solid var(--line-ghost)}
.trait:last-child{border-bottom:0}
.trait .tn{font-size:12px;color:var(--ink-dim)}
.pips{display:grid;grid-template-columns:repeat(5,1fr);gap:4px}
.pip{height:7px;background:var(--line-ghost);border-radius:var(--r-sm)}
.pip.on{background:var(--line)}
.tw{font-size:12px;color:var(--line);font-weight:var(--w7);text-align:right;white-space:nowrap}

.log{display:flex;gap:13px;padding:12px 0;border-bottom:var(--lw) solid var(--line-ghost)}
.log:last-child{border-bottom:0}
.log .dot{width:9px;height:9px;margin-top:5px;flex:0 0 auto;border-radius:var(--r-sm)}
.log .lt{font-size:13px;font-weight:var(--w7);color:var(--line)}
.log .ld{font-size:10.5px;color:var(--ink-faint);font-family:var(--mono);margin-top:3px;letter-spacing:.03em}

/* ---- PK ---- */
.cmpslots{display:grid;grid-template-columns:1fr 1fr;margin-top:8px;
  border:var(--lw) solid var(--line-worn);border-radius:var(--r);overflow:hidden}
.cmpslot{padding:14px 10px;text-align:center;cursor:pointer;position:relative;
  background:var(--fill-1);transition:background .2s}
.cmpslot:first-child{border-right:var(--lw) solid var(--line-worn)}
.cmpslot:active{background:var(--fill-2)}
.cmpslot svg,.cmpslot img{height:86px;position:relative;z-index:2}
.cmpslot .cm{font-family:var(--disp);font-weight:var(--w8);font-size:14px;color:var(--line);margin-top:6px}
.cmpslot .swap{font-family:var(--mono);font-size:9px;color:var(--ink-faint);margin-top:7px;letter-spacing:0.054em}

.tug{padding:11px 0;border-bottom:var(--lw) solid var(--line-ghost)}
.tug:last-child{border-bottom:0}
.tugtop{display:flex;justify-content:center;font-size:11px;color:var(--ink-dim);
  font-weight:var(--w7);margin-bottom:7px;letter-spacing:.04em}
.tugbar{display:flex;align-items:center;height:9px}
.tugbar .half{flex:1;display:flex;height:9px}
.tugbar .half.l{justify-content:flex-end;border-right:var(--lw) solid var(--line)}
.tugbar .half .fill{height:9px;transition:width .6s cubic-bezier(.25,.9,.25,1)}

/* ---- 测验 ---- */
.qprog{display:flex;gap:5px;justify-content:center;margin:4px 0 26px}
.qprog i{width:26px;height:var(--lw);background:var(--line-ghost)}
.qprog i.on{background:var(--acc)}
.qq{font-family:var(--disp);font-weight:var(--disp-w);font-size:27px;color:var(--line);
  line-height:1.2;text-align:center;margin-bottom:24px;letter-spacing:var(--disp-ls)}
.qopts{display:flex;flex-direction:column;gap:9px}
.qopt{border:var(--lw) solid var(--line-ghost);border-radius:var(--r);background:var(--fill-1);
  padding:16px 18px;cursor:pointer;display:flex;justify-content:space-between;align-items:center;gap:10px;
  transition:transform .18s var(--ease),border-color .2s,background .2s}
.qopt:active{transform:scale(.975);border-color:var(--acc);background:var(--fill-acc)}
.qopt .ol{font-weight:var(--w8);font-size:16px;color:var(--line)}
.qopt .os{font-size:11px;color:var(--ink-faint);margin-top:3px}
.qopt .oa{color:var(--ink-faint);flex:0 0 auto}
.matchbadge{font-family:var(--mono);font-size:9.5px;letter-spacing:0.099em;color:var(--acc-text);
  text-transform:uppercase;margin-bottom:8px}

/* ---- 养护 ---- */
.remind{display:flex;align-items:center;gap:13px;padding:13px 14px;margin-top:10px;border-radius:var(--r);
  border:var(--lw) solid var(--line-ghost);background:var(--fill-1);transition:transform .2s,border-color .2s}
.remind:active{transform:scale(.982)}
.remind .rkmini{width:32px;height:46px;flex:0 0 auto;display:flex;align-items:center;justify-content:center}
.remind .rkmini svg,.remind .rkmini img{height:44px}
.remind .ri{flex:1;min-width:0}
.remind .rt{font-size:13px;font-weight:var(--w8);color:var(--line)}
.remind .rs{font-size:10.5px;color:var(--ink-dim);margin-top:3px;line-height:1.5}
.remind .rbtn{border:0;border-radius:var(--r-sm);background:var(--acc);color:var(--acc-ink);
  font-weight:var(--w8);font-size:11.5px;padding:9px 12px;cursor:pointer;flex:0 0 auto;position:relative;letter-spacing:.02em}
.remind.ok .rbtn{background:transparent;border:var(--lw) solid var(--line-ghost);color:var(--ink-faint)}

/* ---- 会员 ---- */
.mtable{border:var(--lw) solid var(--line-worn);background:var(--fill-2);margin-top:16px;
  border-radius:var(--r);overflow:hidden}
.mfeat{display:grid;grid-template-columns:1fr 60px 60px;align-items:center;padding:12px 15px;
  border-bottom:var(--lw) solid var(--line-ghost);font-size:12.5px}
.mfeat:last-child{border-bottom:0}
.mfeat.mhd{font-family:var(--mono);font-size:10px;color:var(--ink-faint);letter-spacing:0.063em}
.mfeat .mft{color:var(--ink)}
.mfeat .mfree{text-align:center;color:var(--ink-faint);font-size:12px}
.mfeat .mpro{text-align:center;color:var(--acc-text);font-weight:var(--w8);font-size:12px}
.mprice{text-align:center;margin:22px 0 16px}
.mprice .mp1{font-family:var(--led);font-weight:var(--w9);font-size:34px;color:var(--line);letter-spacing:-.02em}
.mprice .mp1 span{font-size:14px;color:var(--ink-faint);font-weight:500}
.mprice .mp2{font-size:11.5px;color:var(--ink-faint);margin-top:5px}

.upsell{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-top:14px;
  padding:14px 15px;cursor:pointer;border-radius:var(--r);
  border:var(--lw) solid var(--acc);background:var(--fill-acc);transition:transform .2s,background .2s}
.upsell:active{transform:scale(.985)}
.upsell .ut{font-weight:var(--w8);font-size:14px;color:var(--line)}
.upsell .us{font-size:11px;color:var(--ink-dim);margin-top:3px;line-height:1.45}
.upsell .ubtn{flex:0 0 auto;background:var(--acc);color:var(--acc-ink);font-weight:var(--w8);
  font-size:12px;padding:8px 13px;border-radius:var(--r-sm)}
.probadge{flex:0 0 auto;font-family:var(--mono);font-weight:var(--w8);font-size:11px;letter-spacing:0.063em;
  padding:6px 10px;border:var(--lw) solid var(--acc-text);color:var(--acc-text);border-radius:var(--r-sm)}

/* ---- 我的 ---- */
.petlist{display:flex;flex-direction:column;gap:10px}
.petcard{display:flex;gap:13px;align-items:center;padding:13px 14px;cursor:pointer;border-radius:var(--r);
  border:var(--lw) solid var(--line-ghost);background:var(--fill-1);
  transition:transform .2s var(--ease),border-color .2s}
.petcard:active{transform:scale(.982);border-color:var(--line-worn)}
.petcard .petrk{width:44px;height:58px;flex:0 0 auto;display:flex;align-items:center;justify-content:center}
.petcard .petrk svg{width:100%;height:100%}
.petc{min-width:0;flex:1}
.petname{font-weight:var(--w8);font-size:14px;color:var(--line)}
.petname .petmodel{font-weight:500;font-size:10.5px;color:var(--ink-faint);font-family:var(--mono);letter-spacing:0.027em}
.petsay{font-size:13px;color:var(--ink);font-style:italic;margin:4px 0 6px;line-height:1.5}
.petmood{font-size:10px;color:var(--ink-faint);font-family:var(--mono);display:flex;
  align-items:center;gap:6px;letter-spacing:0.027em}
.petmood i{width:6px;height:6px;flex:0 0 auto;border-radius:var(--r-sm)}

.avatar{width:56px;height:56px;border-radius:50%;overflow:hidden;flex:0 0 auto;position:relative;
  background:radial-gradient(circle at 32% 30%,var(--acc),var(--acc-lo));
  box-shadow:3px 6px 10px var(--shadow-obj)}
.avatar::before{content:"";position:absolute;inset:0;border-radius:50%;
  background:radial-gradient(circle at 50% 50%,transparent 62%,rgba(255,255,255,.28) 63%,transparent 66%)}
.avatar img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block}

.setrow{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:13px 0;
  border-bottom:var(--lw) solid var(--line-ghost)}
.setrow:last-child{border-bottom:0}
.setrow .sn{font-size:13px;color:var(--ink)}
.seg2{display:flex;border:var(--lw) solid var(--line-ghost);border-radius:var(--r-sm);overflow:hidden}
.segb{font-family:var(--mono);font-size:11px;font-weight:var(--w7);padding:7px 11px;color:var(--ink-faint);
  cursor:pointer;border:0;background:transparent;position:relative;
  border-right:var(--lw) solid var(--line-ghost)}
.segb:last-child{border-right:0}
.segb.on{background:var(--line);color:var(--surface-deep)}

.empty{text-align:center;color:var(--ink-faint);font-size:12.5px;padding:34px 0;line-height:1.7}
.fine{font-size:10.5px;color:var(--ink-faint);line-height:1.75;margin-top:16px;padding:0 2px}

.banner{display:flex;align-items:center;gap:12px;padding:14px 15px;margin-top:14px;cursor:pointer;
  border-radius:var(--r);border:var(--lw) solid var(--acc);background:var(--fill-acc);
  transition:transform .2s,background .2s}
.banner:active{transform:scale(.985)}
.banner .bi{width:38px;height:38px;border-radius:50%;border:var(--lw) solid var(--acc-text);
  display:flex;align-items:center;justify-content:center;color:var(--acc-text);flex:0 0 auto}
.banner .bi svg{width:18px;height:18px}
.banner .bt{flex:1}
.banner .bt b{font-size:14px;color:var(--line);font-weight:var(--w8)}
.banner .bt span{display:block;font-size:11px;color:var(--ink-dim);margin-top:2px}
.banner .chev{color:var(--acc-text);flex:0 0 auto}

/* ---- 底部导航 ---- */
.tabbar{position:absolute;left:0;right:0;bottom:0;z-index:30;height:86px;
  padding:12px 14px calc(24px + env(safe-area-inset-bottom));
  display:flex;justify-content:space-around;align-items:flex-start;
  background:var(--tabbg);border-top:var(--lw) solid var(--line-worn)}
.tab{display:flex;flex-direction:column;align-items:center;gap:4px;color:var(--ink-faint);
  cursor:pointer;font-size:9.5px;font-family:var(--mono);letter-spacing:0.045em;position:relative;
  transition:color .2s;text-transform:uppercase}
.tab .ti{padding:3px 10px;display:flex;transition:transform .2s}
.tab svg{width:22px;height:22px}
.tab:active .ti{transform:scale(.86)}
.tab.on{color:var(--line);font-weight:var(--w7)}
.tab .tdot{width:16px;height:var(--lw);background:transparent;margin-top:2px;transition:background .2s}
.tab.on .tdot{background:var(--acc)}

/* ---- toast / sheet ---- */
.toast{position:absolute;left:50%;bottom:108px;transform:translateX(-50%) translateY(18px);z-index:60;
  background:var(--net);color:var(--line);font-size:13px;font-weight:var(--w7);padding:12px 18px;
  border:var(--lw) solid var(--line-worn);border-radius:var(--r-sm);opacity:0;transition:.28s;
  pointer-events:none;display:flex;align-items:center;gap:9px;white-space:nowrap;
  box-shadow:0 14px 30px -12px rgba(0,0,0,.8)}
.toast.show{opacity:1;transform:translateX(-50%) translateY(0)}
.toast .tdot{width:8px;height:8px;border-radius:50%;background:var(--acc)}

.sheet-wrap{position:absolute;inset:0;z-index:70;display:none}
.sheet-wrap.open{display:block}
.sheet-bg{position:absolute;inset:0;background:rgba(0,0,0,.58);opacity:0;transition:.3s}
.sheet-wrap.open .sheet-bg{opacity:1}
.sheet{position:absolute;left:0;right:0;bottom:0;background:var(--sheetbg);
  border-top:var(--lw) solid var(--line);border-radius:var(--r) var(--r) 0 0;
  padding:18px 18px 30px;transform:translateY(100%);
  transition:transform .42s cubic-bezier(.2,.9,.24,1);max-height:88%;overflow-y:auto}
.sheet-wrap.open .sheet{transform:none}
.sheet h4{font-family:var(--disp);font-size:17px;font-weight:var(--disp-w);margin-bottom:5px;
  color:var(--line);letter-spacing:-.02em}
.sheet p{font-size:12px;color:var(--ink-dim);margin-bottom:16px;line-height:1.6}
.sheet .grab{width:40px;height:var(--lw);background:var(--line-worn);margin:0 auto 16px}
.opts{display:grid;grid-template-columns:1fr 1fr;gap:9px}
.opt{border:var(--lw) solid var(--line-ghost);border-radius:var(--r);background:var(--fill-2);
  padding:16px;text-align:center;cursor:pointer;font-weight:var(--w8);font-size:15px;color:var(--line);
  transition:transform .18s,border-color .2s}
.opt:active{transform:scale(.97);border-color:var(--acc)}
.opt small{display:block;font-size:10.5px;color:var(--ink-faint);font-weight:400;margin-top:4px}
.field{width:100%;background:var(--fill-2);border:var(--lw) solid var(--line-ghost);border-radius:var(--r-sm);
  padding:14px;color:var(--ink);font-family:var(--sans);font-size:15px;margin-bottom:14px;outline:none}
.field:focus{border-color:var(--acc)}
.picklist{display:flex;flex-direction:column;gap:8px}
.pickrow{display:flex;align-items:center;gap:12px;padding:10px;cursor:pointer;border-radius:var(--r);
  border:var(--lw) solid var(--line-ghost);background:var(--fill-1);transition:transform .18s,border-color .2s}
.pickrow:active{transform:scale(.985);border-color:var(--line-worn)}
.pickrow svg,.pickrow img{height:40px}
.pickrow .pm{font-weight:var(--w8);color:var(--line);font-size:14px}
.pickrow .pb{font-size:9.5px;color:var(--ink-faint);font-family:var(--mono);letter-spacing:0.063em}
#passcard{width:100%;display:block;border:var(--lw) solid var(--line-worn)}

/* ---- 触控热区 44pt ---- */
.iconbtn::after,.chip::after,.segb::after,.searchbar .clr::after,.remind .rbtn::after{
  content:"";position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);
  width:max(100%,44px);height:max(100%,44px)}
.tab::after{content:"";position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);
  width:max(100%,48px);height:58px}

/* ---- 列表进场 ---- */
@keyframes drawIn{from{opacity:0;transform:translateY(14px)}to{opacity:1;transform:none}}
.libcard,.remind,.qopt,.petcard,.grouphd,.row2>*,.slamcard{
  animation:drawIn .5s var(--ease-out) backwards}
.row2>*:nth-child(2){animation-delay:.06s}
.row2>*:nth-child(3){animation-delay:.12s}
.row2>*:nth-child(4){animation-delay:.18s}
.libcard:nth-child(odd){animation-delay:.05s}
.libcard:nth-child(even){animation-delay:.1s}
.qopt:nth-child(2){animation-delay:.06s}.qopt:nth-child(3){animation-delay:.12s}
.qopt:nth-child(4){animation-delay:.18s}.qopt:nth-child(5){animation-delay:.24s}

/* tapStat 回弹(actions.js 依赖) */
@keyframes cardpop{0%{transform:scale(1)}45%{transform:scale(.94)}100%{transform:scale(1)}}
.popped{animation:cardpop .3s var(--ease)}

@media (prefers-reduced-motion:reduce){
  .screen,.libcard,.remind,.qopt,.petcard,.grouphd,.row2>*,.slamcard,.hero .fig,.popped{animation:none}
  .court .tex2{opacity:.08}
}

/* =====================================================================
   B. 四套赛事主题
   ===================================================================== */

/* ---------------------------------------------------------------------
   1) 罗兰加洛斯 · 红土
   表面:干燥赭红土,有颗粒、土粒、拖网痕。
   线:石灰,被踩掉一半。
   气质:粗粝、慢、有重量。球落地会陷进去。
   --------------------------------------------------------------------- */
.slam-rg{
  --cell-bg:rgba(48,19,9,.62); --cell-bd:rgba(255,225,205,.13); --r-cell:22px;
  --surface:#9C4A2A; --surface-hi:#BA6540; --surface-lo:#7E3A1E; --surface-deep:#421C0F;
  --line:#F4EFE3; --line-worn:rgba(244,239,227,.52); --line-ghost:rgba(244,239,227,.22);
  --acc:#D9E84F; --acc-lo:#AFBE2E; --acc-ink:#1F2404;
  --ink:#FCF8F0; --ink-dim:rgba(252,248,240,.97); --ink-faint:rgba(252,248,240,.83);
  --acc-text:#D9E84F;
  --net:#241F1C; --frame:#191410;
  --ok:#B7CE43; --warn:#E8A33C; --hot:#E4692F; --dead:#C8402F;
  --rkstring:rgba(244,239,227,.42);
  --fill-1:rgba(50,20,10,.18); --fill-2:rgba(36,15,7,.40);
  --fill-hi:rgba(244,239,227,.11); --fill-acc:rgba(217,232,79,.09);
  --shadow-obj:rgba(38,13,5,.34);
  --tabbg:linear-gradient(180deg,transparent,rgba(46,18,9,.88) 46%,rgba(35,13,6,.97));
  --sheetbg:radial-gradient(120% 60% at 30% 0%,#BA6540,transparent 60%),#7E3A1E;
  --btn-shadow:0 3px 0 0 var(--acc-lo),0 10px 18px -10px rgba(0,0,0,.7);
  --btn-shadow-a:0 0 0 0 var(--acc-lo),0 4px 10px -8px rgba(0,0,0,.7);
  --mk-glow:0 0 0 3px rgba(174,87,52,.9),0 0 0 5.5px rgba(217,232,79,.34);
  --impact:radial-gradient(60% 40% at 50% 88%,rgba(226,178,140,.5),transparent 70%);
  --r:15px; --r-sm:10px; --lw:2.5px;
  --disp:var(--sans); --disp-w:900; --disp-ls:-.035em;
  --dur:.42s; --ease:cubic-bezier(.3,1.15,.4,1); --ease-out:cubic-bezier(.32,.9,.28,1);
  --enter:sweepIn; --land:rkLand;
}
/* 土的不均匀:受光、压实、场边 */
.slam-rg .base{background:
  radial-gradient(120% 70% at 22% 8%,  var(--surface-hi), transparent 58%),
  radial-gradient(110% 80% at 88% 42%, var(--surface-lo), transparent 62%),
  radial-gradient(150% 90% at 50% 108%,var(--surface-deep),transparent 55%),
  var(--surface)}
/* 细颗粒 */
.slam-rg .tex{opacity:.42;mix-blend-mode:overlay;background-size:180px 180px;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)'/%3E%3C/svg%3E")}
/* 土粒 + 拖网痕 */
.slam-rg .tex2{opacity:.26;background-image:
  radial-gradient(circle at 12% 22%, rgba(70,26,12,.9) .6px, transparent .8px),
  radial-gradient(circle at 63% 8%,  rgba(70,26,12,.7) .5px, transparent .8px),
  radial-gradient(circle at 34% 71%, rgba(212,150,110,.7) .6px, transparent .9px),
  radial-gradient(circle at 86% 55%, rgba(70,26,12,.8) .7px, transparent .9px),
  repeating-linear-gradient(74deg, rgba(255,225,200,.28) 0 1px, transparent 1px 11px);
  background-size:47px 53px,61px 43px,39px 67px,73px 59px,auto}
.slam-rg .vig{background:radial-gradient(125% 85% at 50% 42%,transparent 42%,rgba(40,15,7,.55) 100%)}
@keyframes sweepIn{from{opacity:0;clip-path:inset(0 100% 0 0)}to{opacity:1;clip-path:inset(0 0 0 0)}}
/* ---------------------------------------------------------------------
   2) 澳网 · 蓝色硬地
   表面:丙烯酸涂层,光滑、有一层薄反光。墨尔本正午,强光。
   线:白漆,锐利完整(硬地的线不会被踩掉)。
   气质:快、干净、锋利。球落地就弹走,不陷。
   --------------------------------------------------------------------- */
.slam-ao{
  --cell-bg:rgba(6,26,52,.66); --cell-bd:rgba(190,225,255,.16); --r-cell:28px;
  --surface:#1C5CA8; --surface-hi:#3B87D6; --surface-lo:#12447E; --surface-deep:#0A2749;
  --line:#FFFFFF; --line-worn:rgba(255,255,255,.66); --line-ghost:rgba(255,255,255,.26);
  --acc:#4FE3D0; --acc-lo:#2BB9A8; --acc-ink:#052E2A;
  --ink:#FFFFFF; --ink-dim:rgba(255,255,255,.88); --ink-faint:rgba(255,255,255,.75);
  --acc-text:#6BEDDC;
  --net:#0C2340; --frame:#0A1826;
  --ok:#4FE3D0; --warn:#FFC64D; --hot:#FF8A3D; --dead:#FF5A5A;
  --rkstring:rgba(255,255,255,.46);
  --fill-1:rgba(10,39,73,.30); --fill-2:rgba(6,28,54,.52);
  --fill-hi:rgba(255,255,255,.13); --fill-acc:rgba(79,227,208,.11);
  --shadow-obj:rgba(4,20,40,.36);
  --tabbg:linear-gradient(180deg,transparent,rgba(12,42,80,.90) 44%,rgba(8,32,62,.98));
  --sheetbg:linear-gradient(180deg,#2569B4,#12447E);
  --btn-shadow:0 8px 20px -10px rgba(79,227,208,.55);
  --btn-shadow-a:0 3px 10px -8px rgba(79,227,208,.5);
  --mk-glow:0 0 0 3px rgba(18,68,126,.95),0 0 0 5.5px rgba(79,227,208,.36);
  --impact:radial-gradient(60% 40% at 50% 88%,rgba(255,255,255,.26),transparent 70%);
  --r:19px; --r-sm:13px; --lw:2px;
  --disp:var(--sans); --disp-w:900; --disp-ls:-.02em;
  --dur:.28s; --ease:cubic-bezier(.2,1.3,.35,1); --ease-out:cubic-bezier(.16,1,.3,1);
  --enter:slideAO; --land:rkBounce;
}
/* 涂层:平滑渐变,中心受光 */
.slam-ao .base{background:
  radial-gradient(100% 66% at 50% 0%,   var(--surface-hi), transparent 62%),
  radial-gradient(120% 90% at 50% 110%, var(--surface-deep),transparent 58%),
  var(--surface)}
/* 极细丙烯酸颗粒:比土细得多 */
.slam-ao .tex{opacity:.20;mix-blend-mode:overlay;background-size:120px 120px;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='2.2' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='120' height='120' filter='url(%23n)'/%3E%3C/svg%3E")}
/* 硬地是哑光的。之前这里放了一道斜向白色高光带,那是室内地板的反光,
   不是球场 —— 真实丙烯酸涂层掺石英砂,近看是密集颗粒,不反光。
   换成第二层更粗的砂粒,和 .tex 的细粒叠出层次。 */
.slam-ao .tex2{opacity:.30;mix-blend-mode:soft-light;background-size:64px 64px;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='64' height='64'%3E%3Cfilter id='c'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.95' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='64' height='64' filter='url(%23c)'/%3E%3C/svg%3E")}
.slam-ao .vig{background:radial-gradient(130% 90% at 50% 38%,transparent 46%,rgba(4,20,40,.58) 100%)}
@keyframes slideAO{
  from{opacity:0;transform:translateX(26px)}
  to  {opacity:1;transform:none}}
/* ---------------------------------------------------------------------
   3) 温网 · 草地
   表面:剪过的草,有明暗相间的剪草条纹(温网最标志的视觉)。
   线:白色,画在草上。
   气质:传统、克制、安静。唯一用衬线体的一套。
   紫色是温网的另一半身份,用作强调而不是网球黄。
   --------------------------------------------------------------------- */
.slam-wb{
  --cell-bg:rgba(10,32,19,.66); --cell-bd:rgba(235,255,240,.15); --r-cell:14px;
  --surface:#2E6B3E; --surface-hi:#3C8450; --surface-lo:#215030; --surface-deep:#12301D;
  --line:#FFFFFF; --line-worn:rgba(255,255,255,.62); --line-ghost:rgba(255,255,255,.24);
  --acc:#7B5BA8; --acc-lo:#5F4488; --acc-ink:#FFFFFF; --acc-text:#E2D6F5;
  --ink:#FDFDFA; --ink-dim:rgba(253,253,250,.91); --ink-faint:rgba(253,253,250,.78);
  --net:#15281C; --frame:#0E1F14;
  --ok:#A8D46A; --warn:#E5C158; --hot:#DE8B4A; --dead:#CE5B52;
  --rkstring:rgba(255,255,255,.44);
  --fill-1:rgba(18,48,29,.30); --fill-2:rgba(12,34,20,.52);
  --fill-hi:rgba(255,255,255,.12); --fill-acc:rgba(156,123,200,.20);
  --shadow-obj:rgba(10,28,16,.34);
  --tabbg:linear-gradient(180deg,transparent,rgba(20,52,32,.90) 44%,rgba(14,38,23,.98));
  --sheetbg:linear-gradient(180deg,#3C8450,#215030);
  --btn-shadow:0 6px 16px -10px rgba(0,0,0,.7);
  --btn-shadow-a:0 2px 8px -7px rgba(0,0,0,.7);
  --mk-glow:0 0 0 3px rgba(33,80,48,.95),0 0 0 5px rgba(226,214,245,.55);
  --impact:radial-gradient(60% 40% at 50% 88%,rgba(255,255,255,.16),transparent 70%);
  --r:7px; --r-sm:5px; --lw:2px;
  --disp:var(--serif); --disp-w:700; --disp-ls:-.01em;
  --dur:.34s; --ease:cubic-bezier(.33,1,.4,1); --ease-out:cubic-bezier(.25,.9,.3,1);
  --enter:fadeWB; --land:rkSkid;
}
.slam-wb .base{background:
  radial-gradient(110% 70% at 50% 4%,  var(--surface-hi), transparent 60%),
  radial-gradient(130% 90% at 50% 108%,var(--surface-deep),transparent 56%),
  var(--surface)}
/* 草纹:极细的竖向纤维 */
.slam-wb .tex{opacity:.26;background:
  repeating-linear-gradient(91deg,
    rgba(255,255,255,.14) 0 1px,
    transparent 1px 3px,
    rgba(10,30,16,.16) 3px 4px,
    transparent 4px 6px)}
/* 剪草条纹:温网的签名。宽带交替明暗 */
.slam-wb .tex2{opacity:.42;background:
  repeating-linear-gradient(180deg,
    rgba(255,255,255,.075) 0 46px,
    rgba(8,26,14,.10)     46px 92px)}
.slam-wb .vig{background:radial-gradient(130% 92% at 50% 40%,transparent 48%,rgba(8,26,14,.56) 100%)}
/* 衬线体下标题需要更松的行高 */
.slam-wb .ctop .ttl{line-height:1.0;letter-spacing:-.005em;font-size:42px}
.slam-wb .qq{line-height:1.35}
@keyframes fadeWB{
  from{opacity:0;transform:translateY(9px)}
  to  {opacity:1;transform:none}}
/* ---------------------------------------------------------------------
   4) 美网 · 夜场
   表面:钴蓝硬地,四座灯塔打下来,场地有光斑与强反差。
   线:白漆,在灯下发亮。
   气质:纽约夜场。高对比、电子感、霓虹黄。
   这是四套里唯一允许发光的 —— 因为夜场本来就有灯。
   --------------------------------------------------------------------- */
.slam-uso{
  --cell-bg:rgba(3,10,32,.72); --cell-bd:rgba(200,220,255,.17); --r-cell:20px;
  --surface:#123A86; --surface-hi:#1E58BE; --surface-lo:#0B2557; --surface-deep:#050F2C;
  --line:#FFFFFF; --line-worn:rgba(255,255,255,.70); --line-ghost:rgba(255,255,255,.28);
  --acc:#E8FF3A; --acc-lo:#B8CC1C; --acc-ink:#161A00;
  --ink:#FFFFFF; --ink-dim:rgba(255,255,255,.80); --ink-faint:rgba(255,255,255,.62);
  --acc-text:#E8FF3A;
  --net:#04081C; --frame:#03081A;
  --ok:#8FE83A; --warn:#FFD024; --hot:#FF8A1E; --dead:#FF4747;
  --rkstring:rgba(255,255,255,.5);
  --fill-1:rgba(5,15,44,.42); --fill-2:rgba(3,10,32,.62);
  --fill-hi:rgba(255,255,255,.14); --fill-acc:rgba(232,255,58,.10);
  --shadow-obj:rgba(2,6,22,.40);
  --tabbg:linear-gradient(180deg,transparent,rgba(5,16,46,.92) 42%,rgba(3,10,32,.99));
  --sheetbg:linear-gradient(180deg,#164497,#0B2557);
  --btn-shadow:0 0 22px -4px rgba(232,255,58,.5),0 8px 18px -10px rgba(0,0,0,.8);
  --btn-shadow-a:0 0 10px -4px rgba(232,255,58,.4),0 3px 8px -7px rgba(0,0,0,.8);
  --mk-glow:0 0 0 3px rgba(11,37,87,.95),0 0 12px 2px rgba(232,255,58,.5);
  --impact:radial-gradient(60% 40% at 50% 88%,rgba(232,255,58,.22),transparent 70%);
  --r:13px; --r-sm:9px; --lw:2.5px;
  --disp:var(--sans); --disp-w:900; --disp-ls:-.045em;
  --dur:.30s; --ease:cubic-bezier(.24,1.25,.4,1); --ease-out:cubic-bezier(.18,.98,.3,1);
  --enter:pushUSO; --land:rkFlash;
}
.slam-uso .base{background:
  radial-gradient(90% 55% at 50% 22%, var(--surface-hi), transparent 60%),
  radial-gradient(140% 95% at 50% 112%,var(--surface-deep),transparent 54%),
  var(--surface)}
/* 四座灯塔的光斑 */
.slam-uso .tex{opacity:.55;background:
  radial-gradient(38% 26% at 14% 12%, rgba(210,230,255,.30), transparent 66%),
  radial-gradient(38% 26% at 86% 12%, rgba(210,230,255,.26), transparent 66%),
  radial-gradient(44% 30% at 16% 76%, rgba(190,215,255,.18), transparent 68%),
  radial-gradient(44% 30% at 84% 76%, rgba(190,215,255,.16), transparent 68%)}
/* 夜场涂层颗粒。原本这里还叠了一层 5px 网点想做"转播大屏"质感,
   但那个是 UI 覆盖层而不是球场表面,读起来像界面不像场地,已去掉。 */
.slam-uso .tex2{opacity:.26;background-size:140px 140px;background-image:
  url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.6' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E")}
.slam-uso .vig{background:radial-gradient(120% 82% at 50% 40%,transparent 34%,rgba(2,6,22,.72) 100%)}
/* 夜场标题:大写拉丁更锋利 */
.slam-uso .eyebrow{color:var(--acc-text);opacity:.9}
@keyframes pushUSO{
  from{opacity:0;transform:translateY(20px)}
  to  {opacity:1;transform:none}}
/* =====================================================================
   C. 四套字体系统
   ---------------------------------------------------------------------
   之前只有温网换了衬线,其余三套共用一套字,所以"看着像同一个 app 换了皮"。
   现在四套的拉丁字与数字字各不相同 —— 这是最容易被看见的差别,因为型号
   名(WILSON CLASH 100)和所有数字满屏都是。
   中文只有黑体/宋体两个选择,靠字重与字距再拉开一层。

     法网  Space Grotesk + 思源黑 900,字距收紧    —— 粗粝、方
     澳网  Outfit        + 思源黑 800,字距放开    —— 圆润、宽、现代
     温网  Playfair      + 思源宋 700             —— 高反差衬线、传统
     美网  Oswald        + 思源黑 700,字距最紧    —— 压缩大写、记分牌
   ===================================================================== */

.slam-rg{
  --sans:"Space Grotesk","Noto Sans SC",-apple-system,system-ui,"PingFang SC",sans-serif;
  --disp:"Space Grotesk","Noto Sans SC",-apple-system,system-ui,"PingFang SC",sans-serif;
  --mono:"Space Grotesk",-apple-system,"SF Pro Text",system-ui,"Helvetica Neue",sans-serif;
  --led:"Space Grotesk",-apple-system,"SF Pro Text",system-ui,"Helvetica Neue",sans-serif;
  --disp-w:900; --disp-ls:-.035em;
}
.slam-ao{
  --sans:"Outfit","Noto Sans SC",-apple-system,system-ui,"PingFang SC",sans-serif;
  --disp:"Outfit","Noto Sans SC",-apple-system,system-ui,"PingFang SC",sans-serif;
  --mono:"Outfit",-apple-system,"SF Pro Text",system-ui,"Helvetica Neue",sans-serif;
  --led:"Outfit",-apple-system,"SF Pro Text",system-ui,"Helvetica Neue",sans-serif;
  --disp-w:800; --disp-ls:.004em;
}
/* Outfit 字腔大,同号视觉更大,整体回收一点 */
.slam-ao .ctop .ttl{font-size:43px}
.slam-ao .eyebrow,.slam-ao .mini .md,.slam-ao .libcard .lb{letter-spacing:0.09em}

.slam-wb{
  --sans:"Noto Serif SC",Georgia,"Songti SC",serif;
  --disp:"Playfair Display","Noto Serif SC",Georgia,"Songti SC",serif;
  --mono:"Playfair Display",ui-serif,Georgia,serif;
  --led:"Playfair Display",ui-serif,Georgia,serif;
  --disp-w:700; --disp-ls:-.005em;
}
/* 衬线在小号和宽字距下会散,收字距、补字号、松行高 */
.slam-wb .ctop .ttl{font-size:41px;line-height:1.02}
.slam-wb .eyebrow{font-size:11px;letter-spacing:0.059em}
.slam-wb .mini .md,.slam-wb .libcard .lb,.slam-wb .petname .petmodel,
.slam-wb .grouphd,.slam-wb .sect h3 .en{letter-spacing:0.036em;font-size:10px}
.slam-wb .gauge .lbs span,.slam-wb .statcard .k,.slam-wb .log .ld{letter-spacing:.02em;font-size:10.5px}
.slam-wb .qq{line-height:1.4}
/* 思源宋在 12px 以下笔画会糊,正文整体垫高一档、行高放松 */
.slam-wb p,.slam-wb .advice,.slam-wb .petsay,.slam-wb .say,.slam-wb .setrow .sn,
.slam-wb .upsell .us,.slam-wb .mfeat .mft,.slam-wb .remind .rs,.slam-wb .empty{
  font-size:13.5px;line-height:1.85}
.slam-wb .fine{font-size:11.5px;line-height:1.9}
.slam-wb .mini .mn,.slam-wb .libcard .lm,.slam-wb .petname{letter-spacing:0}

.slam-uso{
  --sans:"Oswald","Noto Sans SC",-apple-system,system-ui,"PingFang SC",sans-serif;
  --disp:"Oswald","Noto Sans SC",-apple-system,system-ui,"PingFang SC",sans-serif;
  --mono:"Oswald",-apple-system,"SF Pro Text",system-ui,"Helvetica Neue",sans-serif;
  --led:"Oswald",-apple-system,"SF Pro Text",system-ui,"Helvetica Neue",sans-serif;
  --disp-w:700; --disp-ls:-.012em;
}
/* Oswald 是窄体,同号视觉更小,标题放大;正文回落到黑体保证长句可读 */
.slam-uso .ctop .ttl{font-size:52px;letter-spacing:-.02em}
.slam-uso .vital .big{font-size:74px;letter-spacing:-.01em}
.slam-uso p,.slam-uso .advice,.slam-uso .petsay,.slam-uso .say,.slam-uso .fine,
.slam-uso .setrow .sn,.slam-uso .upsell .us,.slam-uso .mfeat .mft{
  font-family:"Noto Sans SC",-apple-system,system-ui,sans-serif}


/* =====================================================================
   D. 进度条 —— 四种形态,不是四种颜色
   ---------------------------------------------------------------------
   高度、结构、边界处理全部不同:
     法网  12px 实心土带,拖网纹,直角          —— 一整片被翻过的土
     澳网   9px 分段涂层块,块间留缝,圆角      —— 一格格磨掉的涂层
     温网  17px 草皮,参差边界,秃斑            —— 底线被打秃的那块草
     美网   6px 细线 + 上方一排刻度点,发光     —— 转播大屏的读数条
   ===================================================================== */

.gauge .track{position:relative;height:var(--gauge-h);background:var(--gauge-track);
  border-radius:var(--gauge-r,0);overflow:visible}
.gauge .fill{position:absolute;left:0;top:0;bottom:0;border-radius:var(--gauge-r,0);
  background:var(--gauge-fill);overflow:hidden;
  transition:width .8s cubic-bezier(.22,.9,.24,1)}

/* 节点三态 */
.gauge .mk{position:absolute;top:50%;width:var(--lw);height:calc(var(--gauge-h) + 6px);
  transform:translate(-50%,-50%);background:var(--line-ghost);z-index:2}
.gauge .mk.done{background:var(--line)}
.gauge .mk.now{width:15px;height:15px;border-radius:50%;background:var(--st,var(--acc));
  border:2px solid var(--line);box-shadow:var(--mk-glow);z-index:3;
  animation:mkPulse 1.05s ease-out 3 both}
/* 进场脉冲三次后停:数值是静止的,无限脉冲不传递信息,只是噪音。 */
@keyframes mkPulse{
  0%  {transform:translate(-50%,-50%) scale(1)}
  38% {transform:translate(-50%,-50%) scale(1.22)}
  100%{transform:translate(-50%,-50%) scale(1)}}

.bar{height:var(--gauge-h);background:var(--gauge-track);border-radius:var(--gauge-r,0)}
.bar > i{background:var(--gauge-fill);border-radius:var(--gauge-r,0);overflow:hidden}

/* ---- 法网:一整片被拖网翻过的浮土 ---- */
.slam-rg{
  --gauge-h:12px; --gauge-r:0px;
  --gauge-track:repeating-linear-gradient(74deg,#4A1D0C 0 2px,#6E3018 2px 5px);
  --gauge-fill:
    linear-gradient(90deg,rgba(255,226,200,0) 84%,rgba(255,226,200,.62) 100%),
    linear-gradient(180deg,#D9966F,#C07049);
}
.slam-rg .gauge .fill::after,.slam-rg .bar > i::after{
  content:"";position:absolute;inset:0;
  background:repeating-linear-gradient(74deg,rgba(96,40,20,.34) 0 1px,transparent 1px 4px)}
/* 翻土的堆积边直接做进这块土自己的底色里(--gauge-fill 的第一层),
   不是竖条也不是阴影 —— 它就是这块土推到头时的受光面。 */

/* ---- 澳网:一格格磨掉的丙烯酸涂层 ---- */
.slam-ao{
  --gauge-h:9px; --gauge-r:5px;
  --gauge-track:linear-gradient(180deg,#0A2749,#061C36);
  --gauge-fill:linear-gradient(180deg,#8FD8FF,#3E9BD8);
}
/* 分段:每 7% 一格,格间留 2px 缝 */
.slam-ao .gauge .fill,.slam-ao .bar > i{
  -webkit-mask-image:repeating-linear-gradient(90deg,#000 0 4.4%,transparent 4.4% 5.6%);
  mask-image:repeating-linear-gradient(90deg,#000 0 4.4%,transparent 4.4% 5.6%)}
/* 涂层高光 */
.slam-ao .gauge .track::after,.slam-ao .bar::after{
  content:"";position:absolute;left:0;right:0;top:0;height:44%;border-radius:5px 5px 0 0;
  background:linear-gradient(180deg,rgba(255,255,255,.24),transparent);pointer-events:none;z-index:4}

/* ---- 温网:底线被打秃的那块草 ---- */
.slam-wb{
  --gauge-h:17px; --gauge-r:2px;
  --gauge-track:repeating-linear-gradient(91deg,#3F8A54 0 2px,#2E6B3E 2px 4px,#245636 4px 6px);
  --gauge-fill:linear-gradient(180deg,#E4D3A6,#C9B183);
}
/* 秃斑 */
.slam-wb .gauge .fill::after,.slam-wb .bar > i::after{
  content:"";position:absolute;inset:0;opacity:.65;
  background:
    radial-gradient(circle at 18% 62%, rgba(120,98,58,.8) 1.7px, transparent 2.2px),
    radial-gradient(circle at 47% 30%, rgba(120,98,58,.6) 1.3px, transparent 1.8px),
    radial-gradient(circle at 74% 70%, rgba(120,98,58,.7) 1.6px, transparent 2.1px),
    radial-gradient(circle at 91% 42%, rgba(120,98,58,.5) 1.2px, transparent 1.7px);
  background-size:23px 100%,17px 100%,29px 100%,19px 100%}
/* 草与秃地的交界是参差的,不是一刀切 */
.slam-wb .gauge .fill,.slam-wb .bar > i{
  -webkit-mask-image:linear-gradient(90deg,#000 calc(100% - 9px),transparent);
  mask-image:linear-gradient(90deg,#000 calc(100% - 9px),transparent)}
.slam-wb .gauge .mk.now{border-color:#FDFDFA}

/* ---- 美网:转播大屏的读数条 ---- */
.slam-uso{
  --gauge-h:6px; --gauge-r:0px;
  --gauge-track:#050F2C;
  --gauge-fill:linear-gradient(90deg,var(--st,#E8FF3A),#FFFFFF);
}
.slam-uso .gauge .fill,.slam-uso .bar > i{box-shadow:0 0 16px -1px var(--st,var(--acc))}
/* 条上方一排刻度点,像大屏读数 */
.slam-uso .gauge .track::before{
  content:"";position:absolute;left:0;right:0;top:-9px;height:4px;z-index:1;opacity:.62;
  background:repeating-linear-gradient(90deg,
    rgba(255,255,255,.75) 0 1.5px, transparent 1.5px 5%)}
/* 刻度尺浮在条上方 9px,要给标题行让出位置,否则会顶到"距离下次穿线" */
.slam-uso .gauge .gh{margin-bottom:19px}
.slam-uso .gauge .mk{height:16px}



/* 入场动画挂在 .real 上,不要在 .fig 上重复挂,否则两层变换叠加 */










/* =====================================================================
   F. 细节修正
   ===================================================================== */

/* 缩略卡的球拍原来 right:-18px,被卡片裁掉一截还压着文字。
   已在上面的组件定义里收进框内,这里只处理文字避让。 */
.mini{padding:13px 14px}
.mini .mn,.mini .md{padding-right:50px}
.mini .mn{line-height:1.25}

/* 主题色卡同样别探出边界 */
.slamcard{padding:14px}
.slamcard .sn,.slamcard .se{padding-right:46px}

/* 库卡片缩略图与左边界的呼吸 */
.libcard{padding:13px 15px 13px 13px}

/* 主拍区底部留白:进度条标签不要贴着分区线 */
.hero{padding-bottom:18px}
.gauge .lbs{margin-top:12px}

/* 详情页巨型数字与状态标签基线对齐更稳 */
.vital{align-items:flex-end}

@media (prefers-reduced-motion:reduce){
  .gauge .mk.now{animation:none}
  .hero .fig{animation:none}
  .slam-ao .court .tex{animation:none}
}

/* =====================================================================
   G. 签名细节 —— 只有这片场地才成立的东西
   ===================================================================== */

/* ---------- 法网:滑步痕 ----------
   红土是四大满贯里唯一能滑步的场地,这是它最强的身份特征。
   按住主拍区,土上留下一道斜滑痕,松手后慢慢被抹平。 */
.slam-rg .hero::after{
  content:"";position:absolute;left:13%;bottom:19%;width:46%;height:16px;
  transform:rotate(-9deg);transform-origin:left center;pointer-events:none;z-index:1;
  border-radius:50%;opacity:0;
  background:linear-gradient(90deg,transparent,rgba(255,228,203,.52) 24%,rgba(255,228,203,.3) 70%,transparent);
  filter:blur(1.6px);transition:opacity 1.2s ease-out}
.slam-rg .hero:active::after{opacity:1;transition:opacity .07s}
/* 球印:红土上争议球看球印。当前节点就是最后一个落点。 */
.slam-rg .gauge .mk.now::after{
  content:"";position:absolute;left:50%;top:50%;width:23px;height:9px;
  transform:translate(-50%,-50%) rotate(-12deg);border-radius:50%;
  border:1.5px solid rgba(255,235,215,.45);pointer-events:none}

/* ---------- 澳网:墨尔本的热 ----------
   一月的墨尔本能到 40°C,极端高温政策是澳网独有的规则。
   场地上方一层几乎看不见的热气,九秒一个周期缓慢上升。 */
.slam-ao .court .tex{animation:heatShimmer 9s ease-in-out infinite}
@keyframes heatShimmer{
  0%,100%{transform:translateY(0)    scaleY(1)}
  50%    {transform:translateY(-10px) scaleY(1.014)}}
/* 场内蓝 / 场外深蓝:AO 真实场地是双色的,底部导航压在场外区 */
.slam-ao .tabbar{border-top-color:rgba(255,255,255,.5)}
.slam-ao .tabbar::before{
  content:"";position:absolute;left:0;right:0;top:0;height:var(--lw);
  background:rgba(255,255,255,.26)}

/* ---------- 温网:记分牌 ----------
   深绿底 + 芥末黄数字,温网记分牌沿用几十年的配色。 */
.slam-wb .specs{background:#0E2A18;border-color:rgba(255,255,255,.5)}
.slam-wb .specs b{color:#E9D24A}
.slam-wb .statrow{background:#0E2A18}
.slam-wb .statcard .v{color:#E9D24A}
.slam-wb .vital .big{color:#E9D24A}
.slam-wb .mprice .mp1{color:#E9D24A}
/* 分区角标做成白色围栏柱,更短更方 */
.slam-wb .zone::before,.slam-wb .zone::after{width:10px;height:10px;border-width:2px}
/* 草地球低弹:按压反馈幅度全场最小 */
.slam-wb .btn:active,.slam-wb .libcard:active,.slam-wb .petcard:active,
.slam-wb .remind:active,.slam-wb .qopt:active{transform:scale(.99)}
.slam-wb .mini:active,.slam-wb .add:active,.slam-wb .slamcard:active{transform:scale(.985)}
.slam-wb .hero:active{transform:scale(.995)}

/* ---------- 美网:四座灯塔 ----------
   夜场四角打光,场上的球拍不是一个影子,是四个方向的淡影叠在一起。
   这是夜场比赛最直观的视觉特征,其余三套都是单向日光影。 */
.slam-uso .hero .fig svg,.slam-uso .hero .fig img{
  filter:
    drop-shadow(-13px 9px 9px rgba(2,6,22,.24))
    drop-shadow( 13px 9px 9px rgba(2,6,22,.24))
    drop-shadow(-7px -5px 6px rgba(2,6,22,.14))
    drop-shadow( 7px -5px 6px rgba(2,6,22,.14))}
.slam-uso .obj svg,.slam-uso .obj img{
  filter:drop-shadow(-6px 5px 5px rgba(2,6,22,.24)) drop-shadow(6px 5px 5px rgba(2,6,22,.24))}
/* 大屏读数:巨型数字下压一条扫描亮线 */
.slam-uso .vital{position:relative}
.slam-uso .vital::after{
  content:"";position:absolute;left:0;bottom:-4px;width:62%;height:1px;
  background:linear-gradient(90deg,var(--acc),transparent);opacity:.6}

@media (prefers-reduced-motion:reduce){
  .slam-ao .court .tex{animation:none}
  .slam-rg .hero::after{transition:none}
}

/* =====================================================================
   H. 材质层 —— 深色砖块 + 自发光主体 + 光晕溢出
   ---------------------------------------------------------------------
   参考里那颗「Recharge」胶囊的做法:
   砖是深的、哑的;主体自己发光;光溢到砖面上;顶部一条玻璃高光。
   四套赛事各自的 --acc / --st 提供发光色,材质规则共用。
   ===================================================================== */

/* 砖块:比场地表面更深一档,让内容浮起来 */
.cell{
  position:relative;border-radius:var(--r-cell,24px);padding:16px 17px;overflow:hidden;
  background:
    linear-gradient(180deg,rgba(255,255,255,.055),rgba(255,255,255,0) 42%),
    var(--cell-bg);
  border:1px solid var(--cell-bd);
  box-shadow:0 18px 34px -22px rgba(0,0,0,.85), inset 0 1px 0 rgba(255,255,255,.09);
  cursor:pointer;transition:transform .22s var(--ease)}
.cell:active{transform:scale(.982)}

.bento{display:grid;grid-template-columns:1fr 1fr;gap:11px;margin-top:11px}

/* 大数字砖 */
.cell.stat{display:flex;flex-direction:column;gap:2px;min-height:112px}
.cell.stat .k{font-family:var(--mono);font-size:10px;letter-spacing:0.072em;
  text-transform:uppercase;color:var(--ink-faint)}
.cell.stat .big{font-family:var(--led);font-weight:var(--w9);font-size:40px;line-height:1.02;
  color:var(--line);letter-spacing:-.03em;font-variant-numeric:tabular-nums;margin-top:5px}
.cell.stat em{font-style:normal;font-family:var(--mono);font-size:12px;color:var(--ink-dim);
  margin-left:-2px}
.cell.stat .sub{font-size:10.5px;color:var(--ink-faint);margin-top:auto;line-height:1.4}
.mini-bar{height:5px;border-radius:3px;background:rgba(255,255,255,.10);margin-top:auto;overflow:hidden}
.mini-bar i{display:block;height:100%;border-radius:3px;background:var(--acc);
  box-shadow:0 0 10px -1px var(--acc)}

/* ---- 线寿命:自发光胶囊 ---- */
.cell.recharge{margin-top:12px;padding:17px 18px 15px}
.recharge .rh{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:13px}
.recharge .rh span{font-family:var(--mono);font-size:10.5px;letter-spacing:0.072em;
  text-transform:uppercase;color:var(--ink-faint)}
.recharge .rh i{font-style:normal;font-weight:var(--w8);font-size:13px}
/* 管:内凹的深槽 */
.recharge .tube{position:relative;height:34px;border-radius:19px;
  background:linear-gradient(180deg,rgba(0,0,0,.42),rgba(0,0,0,.20));
  box-shadow:inset 0 2px 5px rgba(0,0,0,.6), inset 0 -1px 0 rgba(255,255,255,.07);
  overflow:hidden}
/* 液体:自发光,顶部一条高光,右端一个亮头 */
.recharge .juice{position:relative;height:100%;border-radius:19px;
  background:linear-gradient(180deg,
    rgba(255,255,255,.34) 0%, var(--st) 34%, var(--st) 68%, rgba(0,0,0,.26) 100%);
  box-shadow:0 0 26px -2px var(--st), 0 0 60px -6px var(--st),
             inset 0 2px 0 rgba(255,255,255,.55), inset 0 -3px 6px rgba(0,0,0,.28);
  transition:width .9s cubic-bezier(.2,.9,.24,1)}
.recharge .juice::after{content:"";position:absolute;right:3px;top:4px;bottom:4px;width:9px;
  border-radius:6px;background:rgba(255,255,255,.55);filter:blur(2px)}
.recharge .juice::before{content:"";position:absolute;left:10px;right:16px;top:4px;height:8px;
  border-radius:6px;background:linear-gradient(90deg,rgba(255,255,255,.5),rgba(255,255,255,.06));
  filter:blur(1px)}
.recharge .ticks{display:flex;justify-content:space-between;margin-top:8px;padding:0 3px}
.recharge .ticks span{font-family:var(--mono);font-size:9.5px;color:var(--ink-faint);letter-spacing:0.045em}
.recharge .rfoot{display:flex;align-items:baseline;gap:3px;margin-top:11px}
.recharge .rfoot b{font-family:var(--led);font-weight:var(--w9);font-size:30px;line-height:1;
  color:var(--line);letter-spacing:-.03em;font-variant-numeric:tabular-nums}
.recharge .rfoot span{font-family:var(--mono);font-size:12px;color:var(--ink-dim)}
.recharge .rfoot i{font-style:normal;font-size:10.5px;color:var(--ink-faint);margin-left:auto;
  letter-spacing:.02em}

/* ---- 主拍砖:两侧规格轨 ---- */
.hero{border-radius:var(--r-cell,24px);border:1px solid var(--cell-bd);
  background:linear-gradient(180deg,rgba(255,255,255,.05),rgba(255,255,255,0) 46%),var(--cell-bg);
  box-shadow:0 26px 48px -28px rgba(0,0,0,.9), inset 0 1px 0 rgba(255,255,255,.09);
  padding:16px 14px 18px}
/* 球拍背后的辉光,取这把拍的涂装色 */
.hero .halo{position:absolute;left:50%;top:44%;width:78%;height:58%;
  transform:translate(-50%,-50%);pointer-events:none;z-index:0;
  background:radial-gradient(closest-side,var(--rk),transparent 72%);
  opacity:.30;filter:blur(26px)}
.hero .herotop{position:relative;z-index:3;display:flex;justify-content:space-between;
  align-items:flex-start;gap:10px;margin-bottom:6px}
.hero .herotop b{font-family:var(--disp);font-size:22px;font-weight:var(--disp-w);
  color:var(--line);letter-spacing:-.02em}
.hero .herotop .md{font-family:var(--mono);font-size:9.5px;letter-spacing:0.059em;
  color:var(--ink-faint);text-transform:uppercase;margin-top:3px}

.hero .stage{position:relative;z-index:2;display:grid;
  grid-template-columns:64px 1fr 64px;align-items:center;gap:6px;min-height:300px}
.hero .rail{display:flex;flex-direction:column;gap:16px;
  transform:translateX(var(--px,0));transition:transform .12s linear}
.hero .rail.r{text-align:right}
.hero .rail .ri b{display:block;font-family:var(--led);font-weight:var(--w9);font-size:20px;
  line-height:1;color:var(--line);letter-spacing:-.02em;font-variant-numeric:tabular-nums}
.hero .rail .ri span{display:block;font-family:var(--mono);font-size:8.5px;letter-spacing:0.045em;
  color:var(--ink-faint);margin-top:4px;text-transform:uppercase}

.hero .fig{position:relative;height:300px;display:flex;align-items:center;justify-content:center}
.hero .fig .real{max-height:100%;max-width:100%;width:auto;height:100%;object-fit:contain;
  filter:drop-shadow(0 22px 26px rgba(0,0,0,.55))}
/* 网球:落在球拍旁边,轻微上下浮 */
.hero .fig .ball{position:absolute;right:-2px;bottom:8%;width:38px;height:38px;
  filter:drop-shadow(0 8px 10px rgba(0,0,0,.5));
  animation:ballFloat 3.6s ease-in-out infinite;z-index:3}
@keyframes ballFloat{
  0%,100%{transform:translateY(0) rotate(-6deg)}
  50%    {transform:translateY(-9px) rotate(6deg)}}

/* ---- 缩略卡也用真图 + 辉光 ---- */
.mini{border-radius:var(--r-cell,24px);border:1px solid var(--cell-bd);
  background:linear-gradient(180deg,rgba(255,255,255,.045),rgba(255,255,255,0) 50%),var(--cell-bg);
  box-shadow:0 14px 26px -20px rgba(0,0,0,.85), inset 0 1px 0 rgba(255,255,255,.07);
  min-height:124px}
.mini .mglow{position:absolute;right:-6px;top:2px;width:74px;height:74px;pointer-events:none;
  background:radial-gradient(closest-side,var(--rk),transparent 70%);opacity:.34;filter:blur(13px)}
.mini .mf{position:absolute;right:8px;top:9px;width:46px;height:70px;opacity:1;
  display:flex;align-items:center;justify-content:center}
.mini .mf img{max-width:100%;max-height:100%;object-fit:contain;
  filter:drop-shadow(0 6px 8px rgba(0,0,0,.5))}
.mini .mn,.mini .md{padding-right:54px}

/* 我的页宠物卡的球拍 */
.petcard .petrk img{max-width:100%;max-height:100%;object-fit:contain}
.libcard .thumb img,.remind .rkmini img,.cmpslot img,.pickrow img{object-fit:contain}

/* 测验结果页 hero 的真图 */
.hero .fig .real{position:relative;z-index:2}

/* =====================================================================
   I. 入场:镜头从拍面拉远,辉光与网球同步跟上
   ---------------------------------------------------------------------
   之前只有球拍在动,拉远之后画面两侧是空的。现在:
     球拍  从 3 倍拍面拉远到整拍(镜头后退)
     辉光  跟着从涨满收拢到常态
     规格轨 拉远收住之后才从两侧滑入 —— 先看清东西,再上信息
     网球  最后落进画面
   ===================================================================== */
.hero .fig .solid{transform-origin:50% 30%;
  animation:var(--land) var(--land-dur,1.5s) var(--land-ease,cubic-bezier(.16,1,.3,1)) both}
.hero .halo{animation:haloIn var(--land-dur,1.5s) var(--land-ease,cubic-bezier(.16,1,.3,1)) both}
@keyframes haloIn{
  0%  {opacity:0;transform:translate(-50%,-50%) scale(2.4)}
  40% {opacity:.5}
  100%{opacity:.30;transform:translate(-50%,-50%) scale(1)}}

.hero .rail .ri{animation:railIn .62s cubic-bezier(.2,.95,.26,1) both}
.hero .rail.l .ri{--dir:-22px}
.hero .rail.r .ri{--dir:22px}
.hero .rail .ri:nth-child(1){animation-delay:calc(var(--land-dur,1.5s) * .62)}
.hero .rail .ri:nth-child(2){animation-delay:calc(var(--land-dur,1.5s) * .70)}
.hero .rail .ri:nth-child(3){animation-delay:calc(var(--land-dur,1.5s) * .78)}
@keyframes railIn{
  from{opacity:0;transform:translateX(var(--dir,0)) }
  to  {opacity:1;transform:none}}

.hero .fig .ball{animation:
  ballDrop .78s cubic-bezier(.3,1.5,.45,1) calc(var(--land-dur,1.5s) * .66) both,
  ballFloat 3.6s ease-in-out calc(var(--land-dur,1.5s) * .66 + .78s) infinite}
@keyframes ballDrop{
  0%  {opacity:0;transform:translateY(-58px) scale(.7) rotate(-40deg)}
  62% {opacity:1;transform:translateY(0) scale(1) rotate(0)}
  78% {transform:translateY(-11px) scale(.98)}
  100%{opacity:1;transform:translateY(0) scale(1)}}

/* 线寿命胶囊:液体从 0 涨上来 */
.recharge .juice{animation:juiceFill 1.35s cubic-bezier(.22,.9,.24,1) .35s both}
@keyframes juiceFill{from{clip-path:inset(0 100% 0 0)}to{clip-path:inset(0 0 0 0)}}

/* 大数字砖:数字向上推入 */
.cell.stat .big{animation:numIn .7s cubic-bezier(.2,.95,.26,1) .5s both}
@keyframes numIn{from{opacity:0;transform:translateY(14px)}to{opacity:1;transform:none}}

@media (prefers-reduced-motion:reduce){
  .hero .fig .real,.hero .halo,.hero .rail .ri,.hero .fig .ball,
  .recharge .juice,.cell.stat .big{animation:none}
  .hero .fig .ball{opacity:1}
}

/* =====================================================================
   J. 主题卡:显示的是「场地」,不是一颗渐变球
   选中的那张用真网球做标记 —— 网球该出现在有意义的地方
   ===================================================================== */
.slamcard{overflow:hidden;min-height:96px;padding:13px 14px;
  background:linear-gradient(180deg,rgba(255,255,255,.05),rgba(255,255,255,0) 50%),var(--cell-bg);
  border:1px solid var(--cell-bd);border-radius:var(--r-cell,20px);
  box-shadow:0 14px 26px -20px rgba(0,0,0,.85), inset 0 1px 0 rgba(255,255,255,.07)}
.slamcard.on{border-color:var(--line);box-shadow:0 0 0 1px var(--line),0 16px 30px -20px rgba(0,0,0,.9)}
/* 右上角一小片真实场地。
   ---------------------------------------------------------------------
   之前那种斜向白色反光是廉价感的来源 —— 真实球场是哑光的,
   丙烯酸涂层里掺石英砂,近看是细密颗粒,根本不反光。反光那是室内地板。

   所以四套一律:哑光底色 + 明显的砂粒 + 场内外双色 + 一道白线。
   不加任何 sheen、gloss、highlight。 */
.slamcard .surf{position:absolute;right:0;top:0;width:104px;height:100%;pointer-events:none;
  -webkit-mask-image:linear-gradient(270deg,#000 46%,transparent);
  mask-image:linear-gradient(270deg,#000 46%,transparent);overflow:hidden}

/* 砂粒层:两种频率叠加,粗粒 + 细粒,这是硬地涂层的样子 */
.slamcard .surf::before{content:"";position:absolute;inset:-14%;
  opacity:.62;mix-blend-mode:soft-light;
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='56' height='56'%3E%3Cfilter id='a'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='2.6' numOctaves='1' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='56' height='56' filter='url(%23a)'/%3E%3C/svg%3E"),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='b'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.7' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23b)'/%3E%3C/svg%3E");
  background-size:56px 56px,140px 140px}
/* 左缘一道分界,和卡片本体断开 */
.slamcard .surf::after{content:"";position:absolute;left:0;top:0;bottom:0;width:1px;
  background:rgba(255,255,255,.16)}

/* 红土:干土色,场内外深浅不同,拖网斜纹 */
.sc-rg .surf{background:
  repeating-linear-gradient(74deg,rgba(255,232,208,.16) 0 1px,transparent 1px 8px),
  linear-gradient(90deg,#8C4021 0 30%,rgba(240,236,228,.78) 30% 31.2%,#B45F35 31.2%)}

/* 澳网:GreenSet 哑光蓝。场内亮、场外深,中间白线。不反光。 */
.sc-ao .surf{background:
  linear-gradient(90deg,#0F3462 0 30%,rgba(255,255,255,.82) 30% 31.2%,#2A6BA8 31.2%)}

/* 温网:草。剪草条纹 + 白线,草地本身也是哑的 */
.sc-wb .surf{background:
  repeating-linear-gradient(180deg,rgba(255,255,255,.09) 0 8px,rgba(6,24,12,.13) 8px 16px),
  linear-gradient(90deg,#143A23 0 30%,rgba(255,255,255,.85) 30% 31.2%,#347044 31.2%)}

/* 美网:Laykold 哑光钴蓝,夜场灯下整体偏暗 */
.sc-uso .surf{background:
  linear-gradient(90deg,#0A1A40 0 30%,rgba(255,255,255,.76) 30% 31.2%,#1B4FA8 31.2%)}
/* 夜场唯一的光是灯,不是场地自己反光 —— 只在场内落一小片,边缘很软 */
.sc-uso .surf{box-shadow:inset -26px 12px 34px -18px rgba(200,225,255,.34)}

.slamcard .sn,.slamcard .se,.slamcard .ss{position:relative;z-index:2;padding-right:0}
.slamcard .chk{position:absolute;right:11px;bottom:11px;width:22px;height:22px;z-index:3;
  filter:drop-shadow(0 3px 5px rgba(0,0,0,.5))}

/* 空位也放一颗球,别只有一个加号 */
.add{position:relative;overflow:hidden}
.add::after{content:"";position:absolute;right:-14px;bottom:-14px;width:56px;height:56px;
  border-radius:50%;background:radial-gradient(circle at 34% 32%,var(--acc),var(--acc-lo));
  opacity:.16;pointer-events:none}

/* =====================================================================
   K. 列表与卡片统一到砖块材质
   球拍库、养护、宠物卡都跟上,不然只有首页是新的、翻页就穿帮
   ===================================================================== */
.libcard,.remind,.petcard,.qopt{
  border-radius:var(--r-cell,20px);border:1px solid var(--cell-bd);
  background:linear-gradient(180deg,rgba(255,255,255,.04),rgba(255,255,255,0) 52%),var(--cell-bg);
  box-shadow:0 14px 26px -22px rgba(0,0,0,.8), inset 0 1px 0 rgba(255,255,255,.06)}
.libcard:active,.remind:active,.petcard:active,.qopt:active{border-color:var(--line-worn)}

/* 库卡片:球拍带自己的辉光,不再是平贴的缩略图 */
.libcard{padding:14px 16px 14px 12px;gap:12px}
.libcard .thumb{position:relative;width:66px;height:92px}
.libcard .thumb::before{content:"";position:absolute;inset:-8px;border-radius:50%;
  background:radial-gradient(closest-side,var(--rk),transparent 70%);opacity:.28;filter:blur(11px)}
.libcard .thumb img,.libcard .thumb svg{position:relative;height:90px;width:auto;
  filter:drop-shadow(0 8px 10px rgba(0,0,0,.5))}

.remind .rkmini{position:relative;width:38px;height:52px}
.remind .rkmini::before{content:"";position:absolute;inset:-6px;border-radius:50%;
  background:radial-gradient(closest-side,var(--acc),transparent 70%);opacity:.18;filter:blur(8px)}
.remind .rkmini img,.remind .rkmini svg{position:relative;height:50px;width:auto}

.petcard .petrk{position:relative;width:48px;height:62px}
.petcard .petrk::before{content:"";position:absolute;inset:-7px;border-radius:50%;
  background:radial-gradient(closest-side,var(--rk,var(--acc)),transparent 70%);opacity:.24;filter:blur(9px)}
.petcard .petrk img{position:relative;filter:drop-shadow(0 6px 8px rgba(0,0,0,.45))}

/* 统计条也用砖块 */
.statrow{border:1px solid var(--cell-bd);border-radius:var(--r-cell,20px);overflow:hidden;
  background:linear-gradient(180deg,rgba(255,255,255,.04),rgba(255,255,255,0) 52%),var(--cell-bg);
  box-shadow:0 14px 26px -22px rgba(0,0,0,.8), inset 0 1px 0 rgba(255,255,255,.06)}
.statcard{border-right:1px solid var(--cell-bd)}

/* PK 两格 / 会员表 / 分区 也跟上 */
.cmpslots,.mtable,.zone{border-radius:var(--r-cell,20px);border:1px solid var(--cell-bd);
  background:linear-gradient(180deg,rgba(255,255,255,.04),rgba(255,255,255,0) 52%),var(--cell-bg);
  box-shadow:0 14px 26px -22px rgba(0,0,0,.8), inset 0 1px 0 rgba(255,255,255,.06);overflow:hidden}
.zone::before,.zone::after{display:none}
.cmpslot:first-child{border-right:1px solid var(--cell-bd)}
.cmpslot{position:relative}
.cmpslot::before{content:"";position:absolute;left:50%;top:14%;width:88px;height:88px;
  transform:translateX(-50%);border-radius:50%;
  background:radial-gradient(closest-side,var(--acc),transparent 70%);opacity:.16;filter:blur(14px)}

/* 详情页 hero 也是砖 */
.hero.show{border-radius:var(--r-cell,24px);padding:12px}

/* 库卡片给 --rk:用不到时退回主题强调色 */
.libcard,.petcard{--rk:var(--acc)}

/* =====================================================================
   L. 四套入场:真的不一样
   ---------------------------------------------------------------------
   之前四条 keyframes 都是「缩放 + 淡入」,只是数值不同,看起来当然一样。
   现在按场地的物理特性各写各的:

     红土  砸下来陷进土里,拍身回正,一次深沉降           重、慢
     硬地  从右侧高速掠入,连续三次递减弹跳               快、脆
     草地  极稳的拉远,全程不转不弹,只有一次呼吸          静、正
     夜场  从远处推近,伴随曝光过亮收回,末尾横向定位      电子感

   球拍最终姿态是斜的(-13°),更占画面,也更像被人拿在手里。
   ===================================================================== */

/* 红土:落地陷土 */
.slam-rg{--land-dur:1.6s;--land-ease:cubic-bezier(.18,.92,.24,1);--tilt:0deg}
@keyframes rkLand{
  0%  {opacity:0;transform:rotate(16deg) scale(3.1) translateY(-4%);filter:blur(6px)}
  30% {opacity:1;filter:blur(0)}
  66% {transform:scale(1.03) translateY(0)}
  78% {transform:scale(.985) translateY(2.6%)}
  89% {transform:scale(1.005) translateY(-.6%)}
  100%{opacity:1;transform:scale(1) translateY(0);filter:blur(0)}}

/* 硬地:掠入 + 三次递减弹跳 */
.slam-ao{--land-dur:1.25s;--land-ease:cubic-bezier(.14,.98,.26,1);--tilt:0deg}
@keyframes rkBounce{
  0%  {opacity:0;transform:translateX(26%) rotate(-30deg) scale(2.6);filter:blur(5px)}
  26% {opacity:1;filter:blur(0)}
  52% {transform:translateX(0) translateY(0) scale(1)}
  64% {transform:translateY(-7.5%) scale(.99)}
  74% {transform:translateY(0) scale(1)}
  83% {transform:translateY(-3.4%) }
  91% {transform:translateY(0) }
  96% {transform:translateY(-1.2%) }
  100%{opacity:1;transform:translateY(0) scale(1)}}

/* 草地:极稳,只有一次呼吸,不转不弹 */
.slam-wb{--land-dur:1.9s;--land-ease:cubic-bezier(.2,1,.2,1);--tilt:0deg}
@keyframes rkSkid{
  0%  {opacity:0;transform:scale(2.5);filter:blur(7px)}
  42% {opacity:1;filter:blur(0)}
  82% {transform:scale(1.012)}
  100%{opacity:1;transform:scale(1);filter:blur(0)}}

/* 夜场:推近 + 过曝收回 + 末尾横向定位 */
.slam-uso{--land-dur:1.4s;--land-ease:cubic-bezier(.12,1,.24,1);--tilt:0deg}
@keyframes rkFlash{
  0%  {opacity:0;transform:translateX(-9%) scale(3.6);filter:blur(9px) brightness(3)}
  18% {opacity:1;filter:blur(4px) brightness(2.1)}
  46% {filter:blur(0) brightness(1.35)}
  74% {transform:translateX(2.4%) scale(1.015);filter:brightness(1.08)}
  100%{opacity:1;transform:translateX(0) scale(1);filter:brightness(1)}}

/* 落定之后接管:和网球一样的轻微悬浮 */
.hero .fig .solid{
  transform-origin:50% 34%;
  animation:var(--land) var(--land-dur,1.5s) var(--land-ease) both;
  will-change:transform,filter;touch-action:pan-y}
@keyframes rkFloat{
  0%,100%{transform:translateY(0)}
  50%    {transform:translateY(-10px)}}

/* 斜置之后要占更大画面 */
.hero .stage{grid-template-columns:60px 1fr 60px;min-height:322px}
.hero .fig{height:322px;overflow:visible}
.hero .fig .real{height:118%;max-height:none;width:auto;max-width:132%}
.hero .halo{width:88%;height:66%;opacity:.34;filter:blur(30px)}
.hero .fig .ball{right:-6px;bottom:4%;width:46px;height:46px}


/* =====================================================================
   N. 五维:带数值的能量条,四套形状不同
   ===================================================================== */
.trait{display:block;padding:12px 0;border-bottom:1px solid var(--cell-bd)}
.trait:last-child{border-bottom:0}
.trait .th{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:7px}
.trait .tn{font-size:12.5px;color:var(--ink-dim);letter-spacing:.02em}
.trait .tv{font-family:var(--led);font-weight:var(--w9);font-size:16px;color:var(--line);
  letter-spacing:-.02em;font-variant-numeric:tabular-nums}
.tmeter{position:relative;height:8px;border-radius:5px;overflow:hidden;
  background:linear-gradient(180deg,rgba(0,0,0,.34),rgba(0,0,0,.14));
  box-shadow:inset 0 1px 3px rgba(0,0,0,.5)}
.tmeter i{display:block;height:100%;border-radius:5px;
  background:linear-gradient(90deg,var(--acc-lo),var(--acc));
  box-shadow:0 0 12px -2px var(--acc), inset 0 1px 0 rgba(255,255,255,.45);
  animation:meterIn 1s cubic-bezier(.2,.9,.24,1) both}
@keyframes meterIn{from{transform:scaleX(0);transform-origin:left}}
.trait .tw{display:block;font-size:11px;color:var(--ink-faint);margin-top:6px;letter-spacing:.02em}
.trait:nth-child(2) .tmeter i{animation-delay:.06s}
.trait:nth-child(3) .tmeter i{animation-delay:.12s}
.trait:nth-child(4) .tmeter i{animation-delay:.18s}
.trait:nth-child(5) .tmeter i{animation-delay:.24s}

/* 红土:方角,颗粒感 */
.slam-rg .tmeter,.slam-rg .tmeter i{border-radius:1px}
.slam-rg .tmeter i{background:linear-gradient(90deg,#B4761F,#E8B15E);
  box-shadow:0 0 10px -3px #E8B15E, inset 0 1px 0 rgba(255,235,205,.5)}
/* 硬地:大圆角,涂层高光 */
.slam-ao .tmeter{height:9px;border-radius:6px}
.slam-ao .tmeter i{border-radius:6px}
/* 草地:细,克制,无光 */
.slam-wb .tmeter{height:5px;border-radius:2px;box-shadow:inset 0 1px 2px rgba(0,0,0,.4)}
.slam-wb .tmeter i{border-radius:2px;background:linear-gradient(90deg,#7B5BA8,#C3AEE4);box-shadow:none}
/* 夜场:分段点阵,像大屏 */
.slam-uso .tmeter{height:9px;border-radius:0}
.slam-uso .tmeter i{border-radius:0;
  -webkit-mask-image:repeating-linear-gradient(90deg,#000 0 3.2%,transparent 3.2% 4.6%);
  mask-image:repeating-linear-gradient(90deg,#000 0 3.2%,transparent 3.2% 4.6%);
  box-shadow:0 0 16px -1px var(--acc)}

@media (prefers-reduced-motion:reduce){
  .hero .fig .real,.lifeball img,.lifeball .ring .arc,.tmeter i{animation:none}
}


/* 详情页 / 看懂一把拍:拍头特写,画布给足高度 */
.hero.show{min-height:360px;padding:10px}
.hero.show canvas{width:100%;height:360px}
.hero.show .pullhint{top:14px}

/* =====================================================================
   O. 全站动效补齐 —— 参考里那种「东西是活的」的感觉
   ===================================================================== */

/* 1. 砖块按压:只做下沉,不加光。
   之前那圈从中心扩散的辉光是假的 —— 现实里按一个东西不会发光,
   会陷下去、影子变短。质感来自这个,不是来自加光。 */
.cell,.libcard,.remind,.petcard,.qopt,.mini,.slamcard{position:relative}

/* 2. 顶部标题:文字逐字上推 */
.ctop .ttl{animation:ttlIn .78s cubic-bezier(.2,.98,.24,1) both}
.ctop .eyebrow{animation:ttlIn .6s cubic-bezier(.2,.98,.24,1) both}
.ctop .slampick{animation:ttlIn .7s cubic-bezier(.2,.98,.24,1) .08s both}
@keyframes ttlIn{from{opacity:0;transform:translateY(16px)}to{opacity:1;transform:none}}

/* 3. bento 砖错峰浮起 */
.cell.life{animation:cellIn .72s cubic-bezier(.2,.96,.26,1) .18s both}
.bento .cell:nth-child(1){animation:cellIn .72s cubic-bezier(.2,.96,.26,1) .26s both}
.bento .cell:nth-child(2){animation:cellIn .72s cubic-bezier(.2,.96,.26,1) .33s both}
.say{animation:cellIn .7s cubic-bezier(.2,.96,.26,1) .40s both}
@keyframes cellIn{from{opacity:0;transform:translateY(22px) scale(.985)}to{opacity:1;transform:none}}

/* 4. 底部导航:切换时图标弹一下,指示条滑过去 */
.tab.on .ti{animation:tabPop .46s cubic-bezier(.24,1.5,.4,1)}
@keyframes tabPop{0%{transform:scale(1)}38%{transform:scale(1.22)}100%{transform:scale(1)}}
.tab.on .tdot{animation:dotGrow .4s cubic-bezier(.2,1.3,.4,1) both}
@keyframes dotGrow{from{transform:scaleX(0)}to{transform:scaleX(1)}}

/* 5. 区块标题:下划线从左画出来 */
.sect h3{position:relative;border-bottom-color:transparent}
.sect h3::after{content:"";position:absolute;left:0;right:0;bottom:-1px;height:1px;
  background:var(--cell-bd);transform:scaleX(0);transform-origin:left;
  animation:lineDraw .6s cubic-bezier(.2,.95,.26,1) .1s both}
@keyframes lineDraw{to{transform:scaleX(1)}}

/* 6. 详情页巨型数字:向上滚入 */
.vital .big,.statcard .v,.mprice .mp1{animation:numRoll .8s cubic-bezier(.2,.96,.24,1) both}
@keyframes numRoll{from{opacity:0;transform:translateY(20px)}to{opacity:1;transform:none}}
.statcard:nth-child(2) .v{animation-delay:.07s}
.statcard:nth-child(3) .v{animation-delay:.14s}

/* 7. 四套的转场节奏各自不同(时长已在主题里定,这里给列表错峰的步长) */
.slam-rg  .libcard{animation-duration:.56s}
.slam-ao  .libcard{animation-duration:.34s}
.slam-wb  .libcard{animation-duration:.62s}
.slam-uso .libcard{animation-duration:.4s}

@media (prefers-reduced-motion:reduce){
  .ctop .ttl,.ctop .eyebrow,.ctop .slampick,.cell.life,.bento .cell,.say,
  .tab.on .ti,.tab.on .tdot,.sect h3::after,.vital .big,.statcard .v,.mprice .mp1{animation:none}
  .sect h3{border-bottom-color:var(--cell-bd)}
  .sect h3::after{transform:scaleX(1)}
}

/* =====================================================================
   Q. 立体渐变卡片 —— 照参考图四的做法,配色用我们自己的
   ---------------------------------------------------------------------
   参考里那些卡不是「深色底 + 一点透明白」,是实打实的四层:

     1 底色渐变     从卡片左上到右下的彩色过渡,不是平的
     2 内发光       角落一团同色系的光,让卡面鼓起来
     3 顶边高光     1px 的亮线,模拟受光的上沿(这条最提质感)
     4 外投影       深而散的落影 + 一圈极淡的同色外辉

   四套赛事各自给一组色相,材质规则完全一样。
   ===================================================================== */

.cell,.libcard,.remind,.petcard,.qopt,.mini,.slamcard,
.statrow,.cmpslots,.mtable,.zone,.hero,.hero.show,.upsell,.banner{
  position:relative;
  background:
    /* 2 内发光:左上一团,右下一团更深 */
    radial-gradient(120% 90% at 12% 0%,  var(--g-hi), transparent 62%),
    radial-gradient(110% 90% at 92% 104%, var(--g-lo), transparent 66%),
    /* 1 底色渐变 */
    linear-gradient(152deg, var(--g-a), var(--g-b));
  border:1px solid var(--g-bd);
  box-shadow:
    0 1px 0 0 var(--g-edge) inset,           /* 3 顶边高光 */
    0 -14px 26px -20px rgba(0,0,0,.5) inset,
    0 20px 38px -24px rgba(0,0,0,.9),        /* 4 外投影 */
    0 4px 12px -8px rgba(0,0,0,.6),
    0 0 0 1px var(--g-ring);                 /*   同色外辉 */
}

/* 深一档的容器(表格、分区)用同一套但更沉 */
.zone,.mtable,.statrow{--g-a:var(--g-a2);--g-b:var(--g-b2)}

/* 主拍砖:最亮的一块,内发光更强 */
.hero{--g-hi:var(--g-hi2)}

/* 四套色相 —— 只换颜色,材质规则不动 */
.slam-rg{
  --g-a:#5B2A17; --g-b:#38180C; --g-a2:#4A2112; --g-b2:#2C1209;
  --g-hi:rgba(255,168,110,.16); --g-hi2:rgba(255,180,120,.22); --g-lo:rgba(120,44,16,.34);
  --g-bd:rgba(255,205,170,.14); --g-edge:rgba(255,220,190,.20); --g-ring:rgba(255,150,90,.05);
}
.slam-ao{
  --g-a:#12386E; --g-b:#08203F; --g-a2:#0E2C58; --g-b2:#061831;
  --g-hi:rgba(120,205,255,.17); --g-hi2:rgba(140,215,255,.24); --g-lo:rgba(10,70,140,.36);
  --g-bd:rgba(180,220,255,.16); --g-edge:rgba(200,232,255,.22); --g-ring:rgba(80,180,255,.06);
}
.slam-wb{
  --g-a:#1B4029; --g-b:#0C2416; --g-a2:#163422; --g-b2:#091B10;
  --g-hi:rgba(170,235,190,.15); --g-hi2:rgba(190,240,205,.21); --g-lo:rgba(20,70,40,.34);
  --g-bd:rgba(215,245,225,.15); --g-edge:rgba(230,250,235,.20); --g-ring:rgba(140,220,165,.05);
}
.slam-uso{
  --g-a:#122551; --g-b:#050D26; --g-a2:#0D1C40; --g-b2:#03081B;
  --g-hi:rgba(140,180,255,.18); --g-hi2:rgba(160,195,255,.26); --g-lo:rgba(12,30,90,.42);
  --g-bd:rgba(185,205,255,.17); --g-edge:rgba(205,220,255,.24); --g-ring:rgba(90,130,255,.07);
}

/* 按压:整块下沉,高光收窄 —— 实体感的来源 */
.cell:active,.libcard:active,.remind:active,.petcard:active,
.qopt:active,.mini:active,.slamcard:active,.hero:active{
  box-shadow:
    0 1px 0 0 rgba(255,255,255,.10) inset,
    0 -8px 16px -14px rgba(0,0,0,.5) inset,
    0 8px 18px -16px rgba(0,0,0,.9),
    0 2px 6px -5px rgba(0,0,0,.6),
    0 0 0 1px var(--g-ring)}

/* 圆角统一到参考那种大圆角,四套微调 */
.cell,.libcard,.remind,.petcard,.qopt,.mini,.slamcard,
.statrow,.cmpslots,.mtable,.zone,.hero,.upsell,.banner{border-radius:var(--r-cell)}
.slam-rg{--r-cell:20px}
.slam-ao{--r-cell:30px}
.slam-wb{--r-cell:12px}
.slam-uso{--r-cell:18px}

/* 内部分隔线跟着卡片色走,不再是通用白 */
.statcard{border-right:1px solid var(--g-bd)}
.cmpslot:first-child{border-right:1px solid var(--g-bd)}
.mfeat,.setrow,.trait,.log{border-bottom-color:var(--g-bd)}

/* 主拍砖的辉光跟卡片渐变叠在一起会糊,压一点 */
.hero .halo{opacity:.26;transition:transform .18s ease-out}

.hero .stage{touch-action:pan-y}

/* 「拖动可转」的提示去掉了 —— 现在只做视差,不宣称能旋转。
   界面不该承诺它做不到的事。 */

/* =====================================================================
   R. 线寿命 = 弦床
   ---------------------------------------------------------------------
   左边的弦还绷着:直、亮、粗、间距匀。
   右边的弦松了:下垂、发暗、变细、被打串了位置。
   分界那根标尺就是当前寿命,不需要再画条子。

   四套的差别在弦的材质:
     红土  哑光尼龙,暖色,最粗       硬地  聚酯冷光,细而利
     草地  天然肠线,米白,最细       夜场  灯下的荧光弦,自己亮
   ===================================================================== */
.cell.strings{margin-top:12px;padding:14px 18px 13px}
.strings .sh{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:7px}
.strings .sh .k{font-family:var(--mono);font-size:10.5px;letter-spacing:0.081em;
  text-transform:uppercase;color:var(--ink-faint)}
.strings .sh i{font-style:normal;font-weight:var(--w8);font-size:13.5px}

.bed{height:58px;margin:0 0 2px}
.bed svg{width:100%;height:100%;overflow:visible}
.bed path{fill:none;stroke-linecap:round;vector-effect:non-scaling-stroke}
.bed .taut {stroke:var(--str-taut)}
.bed .slack{stroke:var(--str-slack)}
.bed .cross{stroke:var(--str-cross);vector-effect:non-scaling-stroke}
.bed .mark{stroke:var(--line);stroke-width:1.5;vector-effect:non-scaling-stroke;opacity:.85}

/* 进场:绷紧的那段从左边一根根亮起来 */
.bed .bedg{animation:bedIn 1.15s cubic-bezier(.2,.92,.24,1) .25s both}
@keyframes bedIn{
  from{clip-path:inset(0 100% 0 0)}
  to  {clip-path:inset(0 0 0 0)}}
.bed .mark{animation:markIn .5s cubic-bezier(.2,1.2,.3,1) 1.25s both}
@keyframes markIn{from{opacity:0;transform:scaleY(.3)}to{opacity:.85;transform:scaleY(1)}}

.strings .sfoot{display:flex;align-items:baseline;gap:10px;margin-top:6px}
.strings .num{display:flex;align-items:baseline;gap:2px}
.strings .num b{font-family:var(--led);font-weight:var(--w9);font-size:30px;line-height:1.06;
  color:var(--line);letter-spacing:-.035em;font-variant-numeric:tabular-nums}
.strings .num em{font-style:normal;font-family:var(--mono);font-size:13px;color:var(--ink-dim)}
.strings .meta{font-size:10.5px;color:var(--ink-faint);margin-left:auto;letter-spacing:.02em}

/* 四套的弦材质 */
.slam-rg { --str-taut:#F0DCC4; --str-slack:#8C5B3C; --str-cross:rgba(240,220,196,.42) }
.slam-ao { --str-taut:#DFF4FF; --str-slack:#2E6796; --str-cross:rgba(223,244,255,.40) }
.slam-wb { --str-taut:#FBF7E8; --str-slack:#5C6B54; --str-cross:rgba(251,247,232,.38) }
.slam-uso{ --str-taut:#FFFFFF; --str-slack:#2C3E74; --str-cross:rgba(255,255,255,.36) }
/* 夜场的弦在灯下自己亮一点 */
.slam-uso .bed .taut{filter:drop-shadow(0 0 3px rgba(255,255,255,.5))}
/* 草地最细,红土最粗 */
.slam-wb .bed path{stroke-width:1.2}
.slam-rg .bed .taut{stroke-linecap:butt}

@media (prefers-reduced-motion:reduce){
  .bed .bedg,.bed .mark{animation:none}
}

/* =====================================================================
   S. 转场:一次运动,不是三段
   ---------------------------------------------------------------------
   之前是「首页球拍在 → 整页扫入(球拍没了)→ 克隆飞进来 → canvas 接管」,
   看着就是出现、消失、又出现。现在:

     飞行期间  整页扫入停掉,canvas 不画,屏幕上只有那一把拍在动
     快落位时  详情页其余内容才从下方浮上来
     最后 130ms canvas 淡入、克隆淡出,交接肉眼看不出来
   ===================================================================== */
html.flying .screen{animation:none !important}
html.flying .hero.show{background-image:none}

/* 其余内容等球拍快到位了再上来 */
html.landing .screen > *:not(.hero):not(.topbar){
  animation:landUp .46s cubic-bezier(.2,.96,.24,1) both}
html.landing .screen > .topbar{animation:landUp .34s cubic-bezier(.2,.96,.24,1) both}
@keyframes landUp{from{opacity:0;transform:translateY(18px)}to{opacity:1;transform:none}}
html.landing .screen > *:nth-child(3){animation-delay:.04s}
html.landing .screen > *:nth-child(4){animation-delay:.08s}
html.landing .screen > *:nth-child(5){animation-delay:.12s}
html.landing .screen > *:nth-child(6){animation-delay:.16s}

/* 飞行中的克隆。只动 transform,所以这里锁死尺寸相关属性,
   免得任何一条 CSS 意外触发布局。 */
.rk-fly{max-width:none;max-height:none;contain:layout style paint}

@media (prefers-reduced-motion:reduce){
  html.landing .screen > *{animation:none}
}

/* 主拍标记与「设为主拍」快捷 */
.mainbadge{display:inline-block;font-family:var(--mono);font-size:9px;font-weight:var(--w7);
  letter-spacing:0.063em;padding:2px 6px;border-radius:4px;vertical-align:middle;
  background:var(--acc);color:var(--acc-ink);margin-left:6px}
.mini .tomain{position:absolute;right:9px;bottom:9px;z-index:4;border:0;cursor:pointer;
  font-family:var(--mono);font-size:9px;font-weight:var(--w7);letter-spacing:0.045em;
  padding:5px 8px;border-radius:calc(var(--r-cell) / 2.6);
  background:rgba(255,255,255,.12);color:var(--ink-dim);
  transition:background .2s,color .2s}
.mini .tomain:active{background:var(--acc);color:var(--acc-ink)}
.mini .ms{padding-right:64px}

/* =====================================================================
   T. 新功能:相册 / 穿线曲线 / 亲密度 / 场地分布 / 头像
   ===================================================================== */

/* ---- 球拍相册 ---- */
.album{display:grid;grid-template-columns:repeat(3,1fr);gap:8px}
.album .ph{position:relative;aspect-ratio:3/4;border-radius:calc(var(--r-cell) * .6);
  overflow:hidden;background:var(--fill-2);border:1px solid var(--g-bd);cursor:pointer}
.album .ph img{width:100%;height:100%;object-fit:cover;display:block}
.album .ph .phx{position:absolute;right:4px;top:4px;width:20px;height:20px;border:0;
  border-radius:50%;background:rgba(0,0,0,.6);color:#fff;font-size:14px;line-height:1;
  cursor:pointer;display:flex;align-items:center;justify-content:center}
.album .ph.add{display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:4px;border:1px dashed var(--line-ghost);background:transparent;color:var(--ink-faint)}
.album .ph.add span{font-size:22px;font-weight:300;color:var(--line)}
.album .ph.add i{font-style:normal;font-size:10px;letter-spacing:0.027em}
.phhint{font-size:11.5px;color:var(--ink-faint);margin-top:9px;line-height:1.6}
.bigphoto{width:100%;border-radius:var(--r-cell);display:block}

/* ---- 穿线历史曲线 ---- */
.tenwrap{padding:2px 0}
.tenchart{width:100%;height:96px;overflow:visible}
.tenchart .area{fill:var(--acc);opacity:.13}
.tenchart .line{fill:none;stroke:var(--acc);stroke-width:2;vector-effect:non-scaling-stroke;
  stroke-linejoin:round;stroke-linecap:round;
  filter:drop-shadow(0 0 4px var(--acc))}
.tenchart .dots circle{fill:var(--line);stroke:var(--acc);stroke-width:1.6;
  vector-effect:non-scaling-stroke}
.tenfoot{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin-top:12px;
  padding-top:11px;border-top:1px solid var(--g-bd)}
.tenfoot b{display:block;font-family:var(--led);font-weight:var(--w9);font-size:21px;
  color:var(--line);letter-spacing:-.02em;font-variant-numeric:tabular-nums}
.tenfoot span{display:block;font-family:var(--mono);font-size:9px;letter-spacing:0.054em;
  text-transform:uppercase;color:var(--ink-faint);margin-top:4px}

/* ---- 亲密度 ---- */
.cell.bond .bhead{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:10px}
.cell.bond .bhead b{font-family:var(--disp);font-weight:var(--disp-w);font-size:19px;color:var(--line)}
.cell.bond .bhead span{font-family:var(--led);font-weight:var(--w9);font-size:20px;color:var(--line);
  font-variant-numeric:tabular-nums}
.cell.bond .bhead em{font-style:normal;font-size:12px;color:var(--ink-faint)}
.cell.bond .bbar{height:9px;border-radius:5px;overflow:hidden;
  background:linear-gradient(180deg,rgba(0,0,0,.34),rgba(0,0,0,.14));
  box-shadow:inset 0 1px 3px rgba(0,0,0,.5)}
.cell.bond .bbar i{display:block;height:100%;border-radius:5px;
  background:linear-gradient(90deg,var(--acc-lo),var(--acc));
  box-shadow:0 0 12px -2px var(--acc), inset 0 1px 0 rgba(255,255,255,.45);
  animation:meterIn 1s cubic-bezier(.2,.9,.24,1) .15s both}
.cell.bond .bfoot{display:flex;flex-direction:column;gap:4px;margin-top:11px}
.cell.bond .bfoot span{font-family:var(--mono);font-size:10.5px;letter-spacing:0.036em;color:var(--ink-dim)}
.cell.bond .bfoot i{font-style:normal;font-size:10.5px;color:var(--ink-faint);line-height:1.55}

/* ---- 场地分布 ---- */
.surfwrap{display:flex;flex-direction:column;gap:11px}
.srow{display:grid;grid-template-columns:44px 1fr 38px;align-items:center;gap:11px}
.srow .sname{font-size:12px;color:var(--ink-dim)}
.srow .spct{font-family:var(--led);font-weight:var(--w9);font-size:14px;color:var(--line);
  text-align:right;font-variant-numeric:tabular-nums}
.sbar{height:9px;border-radius:5px;overflow:hidden;
  background:linear-gradient(180deg,rgba(0,0,0,.3),rgba(0,0,0,.12));
  box-shadow:inset 0 1px 3px rgba(0,0,0,.45)}
.sbar i{display:block;height:100%;border-radius:5px;
  animation:meterIn .9s cubic-bezier(.2,.9,.24,1) both}
/* 三种场地用各自真实的颜色,不跟主题走 —— 这是「场地」不是「装饰」 */
.sb-clay  i{background:linear-gradient(90deg,#8E4224,#D9834F);box-shadow:0 0 10px -3px #D9834F}
.sb-hard  i{background:linear-gradient(90deg,#0E3E7A,#4FA3EE);box-shadow:0 0 10px -3px #4FA3EE}
.sb-grass i{background:linear-gradient(90deg,#17462A,#49A063);box-shadow:0 0 10px -3px #49A063}
.ssug{display:flex;align-items:center;gap:10px;margin-top:4px;padding-top:11px;
  border-top:1px solid var(--g-bd)}
.ssug span{font-size:11.5px;color:var(--ink-dim);line-height:1.5;flex:1}
.ssug button{flex:0 0 auto;border:0;cursor:pointer;font-weight:var(--w8);font-size:11.5px;
  padding:7px 12px;border-radius:calc(var(--r-cell) * .45);
  background:var(--acc);color:var(--acc-ink)}

/* ---- 可上传的头像 ---- */
.avatar.up{cursor:pointer;overflow:visible}
.avatar.up img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  border-radius:50%;display:block}
.avatar.up .avhint{position:absolute;left:50%;bottom:-17px;transform:translateX(-50%);
  font-family:var(--mono);font-size:8px;letter-spacing:0.045em;color:var(--ink-faint);
  white-space:nowrap;text-transform:uppercase;opacity:.7}
.avatar.up:active{transform:scale(.94)}

/* ---- 打球记录:场地选择 ---- */
.sesspick{display:flex;flex-direction:column;gap:16px}
.ssgrp .sslab{display:block;font-family:var(--mono);font-size:10px;letter-spacing:0.072em;
  text-transform:uppercase;color:var(--ink-faint);margin-bottom:9px}
.ssrow{display:grid;grid-template-columns:repeat(3,1fr);gap:8px}
.ssbtn{position:relative;border:1px solid var(--g-bd);cursor:pointer;
  border-radius:calc(var(--r-cell) * .55);padding:13px 8px;
  font-family:var(--sans);font-weight:var(--w8);font-size:13px;color:var(--ink-dim);
  background:var(--fill-2);overflow:hidden;transition:color .2s,border-color .2s,transform .16s}
/* 每个按钮左侧一条自己场地的真实颜色 */
.ssbtn::before{content:"";position:absolute;left:0;top:0;bottom:0;width:100%;opacity:.18;
  transition:opacity .22s}
.ssbtn.sb-clay::before {background:linear-gradient(120deg,#D9834F,#8E4224)}
.ssbtn.sb-hard::before {background:linear-gradient(120deg,#4FA3EE,#0E3E7A)}
.ssbtn.sb-grass::before{background:linear-gradient(120deg,#49A063,#17462A)}
.ssbtn.on{color:var(--line);border-color:var(--line)}
.ssbtn.on::before{opacity:.55}
.ssbtn:active{transform:scale(.96)}
.ssbtn > *{position:relative;z-index:2}

/* =====================================================================
   U. 球拍的体积 —— 撤掉了伪造厚度的做法
   ---------------------------------------------------------------------
   之前把同一张图叠六层往后推 Z 来伪造厚度。这对实心剪影有效,
   对球拍无效:弦床是透明的,后面五层压暗副本会从网眼里透出来,
   糊成一团,而且那层整块矩形的镜面高光看着就是一块玻璃板压在上面。

   现在只留一张图。体积感靠三样真实的东西:
     动态投影  影子随转角改变方向、长度和浓度
     动态明暗  迎光的一侧提亮,背光的一侧压暗(filter,不是叠图层)
     阻尼手感  转动有惯性、有回弹,松手会继续走一点再停

   真正的 360° 需要每把拍环拍序列帧,单张照片做不到,不假装能做到。
   ===================================================================== */
.hero .fig .solid{
  position:relative;width:100%;height:100%;
  display:flex;align-items:center;justify-content:center;
  will-change:transform,filter}
.hero .fig .solid .real{
  position:relative;height:118%;max-height:none;width:auto;max-width:132%;
  object-fit:contain}

/* 详情页的球拍是 canvas 画的,不走这套 */

/* =====================================================================
   V. 线寿命的四种表达
   ---------------------------------------------------------------------
   bed 弦床 / bounce 弹跳 / wave 张力波 / seg 分段格,用户自己选。
   四种都跟着赛事主题走 —— 不只是换颜色,形态参数也各自不同:
     红土 方角、粗、哑光      硬地 大圆角、细、冷光
     草地 最细、最克制、无光  夜场 窄、发光、高对比
   动态两种(弹跳/张力波)的物理参数在 gauge.js 里,跟着场地特性给。
   ===================================================================== */

/* 标题变成可点的切换按钮 */
.strings .sh .gswitch{display:inline-flex;align-items:center;gap:6px;border:0;cursor:pointer;
  background:transparent;padding:0;font-family:var(--mono);font-size:10.5px;letter-spacing:0.081em;
  text-transform:uppercase;color:var(--ink-faint);transition:color .2s}
.strings .sh .gswitch i{width:11px;height:11px;border-radius:50%;flex:0 0 auto;
  border:1px solid currentColor;position:relative;opacity:.7}
.strings .sh .gswitch i::after{content:"";position:absolute;left:50%;top:50%;width:3px;height:3px;
  border-radius:50%;background:currentColor;transform:translate(-50%,-50%)}
.strings .sh .gswitch:active{color:var(--line)}
.strings .sh .stt{font-style:normal;font-weight:var(--w8);font-size:13.5px}

/* ---- 动态两种的画布 ---- */
.gcv{position:relative;height:66px;margin:2px 0 4px}
.gcv canvas{display:block;width:100%;height:100%}
.g-bounce .gcv{height:76px}

/* ---- 分段格 ---- */
.segbar{display:flex;gap:var(--seg-gap,3px);height:var(--seg-h,26px);margin:6px 0 2px}
.segbar i{flex:1;border-radius:var(--seg-r,2px);background:rgba(255,255,255,.10)}
.segbar i.on{background:var(--st);box-shadow:0 0 10px -2px var(--st),inset 0 1px 0 rgba(255,255,255,.4)}
.segscale{display:flex;justify-content:space-between;margin-top:7px}
.segscale span{font-family:var(--mono);font-size:9.5px;color:var(--ink-faint);letter-spacing:0.045em}
.slam-rg  {--seg-gap:3px; --seg-r:1px;  --seg-h:26px}
.slam-ao  {--seg-gap:4px; --seg-r:7px;  --seg-h:24px}
.slam-wb  {--seg-gap:2px; --seg-r:1px;  --seg-h:14px}
.slam-uso {--seg-gap:2px; --seg-r:0px;  --seg-h:28px}
.slam-wb .segbar i.on{box-shadow:none}
.slam-uso .segbar i.on{box-shadow:0 0 16px -1px var(--st)}

/* ---- 我的页:四选一,带缩略示意 ---- */
.gpick{display:grid;grid-template-columns:1fr 1fr;gap:9px}
.gopt{position:relative;display:flex;flex-direction:column;gap:3px;cursor:pointer;text-align:left;
  border:1px solid var(--g-bd);border-radius:calc(var(--r-cell) * .58);padding:11px 12px 10px;
  background:var(--fill-2);transition:border-color .2s,transform .16s}
.gopt.on{border-color:var(--line)}
.gopt:active{transform:scale(.97)}
.gopt b{font-size:13.5px;font-weight:var(--w8);color:var(--line)}
.gopt i{font-style:normal;font-size:10.5px;color:var(--ink-faint);line-height:1.4}
.gp-ico{display:block;height:22px;margin-bottom:5px;opacity:.9}
/* 缩略示意:四种各画一个极简符号 */
.gp-bed{background:
  repeating-linear-gradient(90deg,var(--line) 0 1.5px,transparent 1.5px 6px) 0 0/50% 100% no-repeat,
  repeating-linear-gradient(90deg,var(--ink-faint) 0 1px,transparent 1px 7px) 50% 0/50% 100% no-repeat}
.gp-bounce{background:
  radial-gradient(circle 5px at 20% 78%,var(--acc) 98%,transparent),
  radial-gradient(circle 5px at 50% 26%,var(--acc) 98%,transparent),
  radial-gradient(circle 5px at 80% 62%,var(--acc) 98%,transparent),
  linear-gradient(var(--ink-faint),var(--ink-faint)) 0 96%/100% 1.4px no-repeat}
.gp-wave{background:
  radial-gradient(circle 2.6px at 2% 50%,var(--line) 98%,transparent),
  radial-gradient(circle 2.6px at 98% 50%,var(--line) 98%,transparent);
  position:relative}
.gp-wave::after{content:"";position:absolute;left:6%;right:6%;top:50%;height:16px;margin-top:-8px;
  border-top:1.8px solid var(--acc);border-radius:50%;
  clip-path:polygon(0 0,100% 0,100% 55%,0 55%);transform:scaleY(1.6)}
.gp-seg{background:repeating-linear-gradient(90deg,var(--acc) 0 8%,transparent 8% 12%) 0 0/58% 100% no-repeat,
  repeating-linear-gradient(90deg,var(--ink-faint) 0 8%,transparent 8% 12%) 58% 0/42% 100% no-repeat;
  opacity:.9}

/* =====================================================================
   W. 会员与锁
   ---------------------------------------------------------------------
   锁不是「把东西藏起来」。锁住的内容照常渲染,只是压暗 + 轻微模糊,
   上面盖一层说明。用户看到的是自己的那条曲线、自己的那些记录被压着,
   不是一张功能截图 —— 这是这套界面唯一想做对的事。

   四套赛事主题共用这一段:所有颜色走 --acc / --line / --ink 变量,
   换主题时锁的配色跟着场地走,不会出现一块违和的紫色会员皮肤。
   ===================================================================== */

/* ---- PRO 角标 ---- */
.prolock{
  position:absolute;right:9px;top:9px;z-index:6;
  font-family:var(--mono);font-size:8.5px;font-weight:var(--w7);letter-spacing:0.072em;
  padding:3px 6px;border-radius:3px;
  color:var(--acc-ink,#1a1a1a);background:var(--acc);
  box-shadow:0 2px 8px rgba(0,0,0,.3);
}
.prolock.big{position:static;font-size:10px;padding:4px 9px;letter-spacing:0.09em}

/* ---- 被锁住的卡片(主题、线寿命表达) ---- */
.slamcard.locked,
.gopt.locked{position:relative}
.slamcard.locked > *:not(.prolock),
.gopt.locked   > *:not(.prolock){opacity:.42}
.slamcard.locked::after,
.gopt.locked::after{
  content:"";position:absolute;inset:0;border-radius:inherit;
  background:rgba(0,0,0,.22);pointer-events:none;
}

/* ---- 锁住的整块内容(磅数曲线) ---- */
.lockwrap{position:relative;overflow:hidden}
.lockblur{filter:blur(4px) saturate(.5);opacity:.5;pointer-events:none;transform:scale(1.02)}
.lockover{
  position:absolute;inset:0;display:flex;flex-direction:column;
  align-items:center;justify-content:center;gap:7px;text-align:center;
  background:linear-gradient(180deg,rgba(0,0,0,.06),rgba(0,0,0,.34));
}
.lockover b{font-size:14px;font-weight:var(--w8);color:var(--line);letter-spacing:-.01em}
.lockover i{font-style:normal;font-size:11px;color:var(--ink-dim);
  font-family:var(--mono);letter-spacing:0.036em}

/* ---- 记录列表末尾那一行「更早的 N 条已锁」 ---- */
.log.lockrow{position:relative;cursor:pointer;opacity:.72}
.log.lockrow:active{opacity:1}
.log.lockrow .lt{color:var(--ink-dim)}
.log.lockrow .prolock{top:50%;transform:translateY(-50%)}

/* ---- 领养弹窗里的名额提示 ---- */
.slotnote{
  font-family:var(--mono);font-size:10.5px;letter-spacing:0.036em;
  color:var(--ink-faint);margin-top:9px;
}

/* =====================================================================
   会员页
   ===================================================================== */

/* ---- 你已经攒下的 ---- */
.invbox{
  margin-top:18px;padding:15px 16px 13px;
  border:var(--lw) solid var(--line-ghost);border-radius:var(--r);
  background:var(--fill-2);
}
.invhd{font-family:var(--mono);font-size:9.5px;letter-spacing:0.09em;
  text-transform:uppercase;color:var(--ink-faint)}
.invrow{display:grid;grid-template-columns:repeat(4,1fr);gap:8px;margin-top:12px}
.invcell{text-align:center}
.invcell b{display:block;font-family:var(--led);font-weight:var(--w9);font-size:24px;
  color:var(--line);letter-spacing:-.03em;font-variant-numeric:tabular-nums;line-height:1}
.invcell i{display:block;font-style:normal;font-size:9.5px;color:var(--ink-faint);
  margin-top:5px;line-height:1.3}
.invnote{font-size:11px;color:var(--ink-faint);line-height:1.65;margin-top:12px}

/* ---- 权益表:PRO 独有的几行加重,免费也有的不抢戏 ---- */
.mfeat.hl{background:rgba(255,255,255,.035)}
.mfeat.hl .mft{color:var(--line);font-weight:var(--w7)}
.mfeat .mft{font-size:12.5px;line-height:1.4;padding-right:8px}
.mfeat .mfree{font-size:11px}

/* ---- 价格:两张卡,年付默认选中 ---- */
.plans{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-top:20px}
.plan{
  position:relative;text-align:left;cursor:pointer;
  padding:15px 14px 13px;border-radius:var(--r);
  border:var(--lw) solid var(--line-ghost);background:var(--fill-2);
  color:inherit;font-family:inherit;
  transition:border-color .16s, transform .12s, background .16s;
}
.plan:active{transform:scale(.98)}
.plan.on{border-color:var(--acc);background:var(--fill-1)}
.plan b{display:block;font-size:12px;font-weight:var(--w8);color:var(--ink-dim);letter-spacing:.02em}
.plan.on b{color:var(--line)}
.plan .pp{font-family:var(--led);font-weight:var(--w9);font-size:25px;color:var(--line);
  letter-spacing:-.03em;margin-top:7px;line-height:1}
.plan .pp span{font-size:11px;color:var(--ink-faint);font-weight:500;letter-spacing:0}
.plan i{display:block;font-style:normal;font-size:10.5px;color:var(--ink-faint);margin-top:6px;
  font-family:var(--mono);letter-spacing:.04em}
.plan .ptag{
  position:absolute;right:10px;top:-9px;
  font-family:var(--mono);font-size:9px;font-weight:var(--w7);letter-spacing:0.045em;
  padding:3px 7px;border-radius:3px;
  background:var(--acc);color:var(--acc-ink,#1a1a1a);
}

/* ---- 主按钮 ---- */
.btn.big{margin-top:16px;padding:18px 16px;font-size:15.5px;flex-direction:column;gap:3px}
.btn.big .ctasub{font-family:var(--mono);font-size:9.5px;font-weight:500;
  letter-spacing:0.045em;opacity:.62}
.trialnote{text-align:center;font-size:11px;color:var(--ink-faint);
  margin-top:11px;line-height:1.6}

/* =====================================================================
   X. 空状态 · 表单 · 投稿 · 举报
   ===================================================================== */

/* ---- 大空状态:首页没有球拍、没有投稿时用 ---- */
.emptybig{
  display:flex;flex-direction:column;align-items:center;text-align:center;
  padding:52px 24px 40px;
}
.emptybig .ebico{
  width:56px;height:56px;opacity:.30;margin-bottom:18px;
}
.emptybig .ebico svg{width:100%;height:100%}
.emptybig .ebt{font-size:17px;font-weight:var(--w8);color:var(--line);letter-spacing:-.01em}
.emptybig .ebs{font-size:12.5px;color:var(--ink-faint);line-height:1.75;margin-top:9px;max-width:23em}

/* ---- 表单 ---- */
.fld{display:block;margin-bottom:13px}
.fld:last-child{margin-bottom:0}
.fld .fl{
  display:block;font-family:var(--mono);font-size:9.5px;letter-spacing:0.072em;
  text-transform:uppercase;color:var(--ink-faint);margin-bottom:6px;
}
.fld .fe{display:block;font-size:11px;color:var(--hot,#E4692F);margin-top:5px}
.fld.bad .field{border-color:var(--hot,#E4692F)}
.field.ta{resize:none;line-height:1.6;padding-top:11px;font-family:inherit}
.fh{font-size:11px;color:var(--ink-faint);line-height:1.65;margin-top:-4px;margin-bottom:13px}
.fgrid{display:grid;grid-template-columns:1fr 1fr;gap:0 12px}

/* ---- 「库里已经有了」提示 ---- */
.dupcard{
  display:flex;align-items:center;gap:10px;margin-top:16px;padding:13px 14px;
  border:var(--lw) solid var(--acc);border-radius:var(--r);
  background:var(--fill-1);cursor:pointer;font-size:12.5px;color:var(--line);
}
.dupcard svg{width:18px;height:18px;flex:none;color:var(--acc-text)}
.dupcard span{flex:1;line-height:1.5}

/* ---- 投稿照片 ---- */
.subphoto{
  display:flex;align-items:center;gap:11px;margin:4px 0 9px;padding:14px;
  border:var(--lw) dashed var(--line-ghost);border-radius:var(--r);
  cursor:pointer;color:var(--ink-dim);font-size:12.5px;
}
.subphoto svg{width:20px;height:20px;flex:none}
.subphoto.on{border-style:solid;border-color:var(--acc)}
.subphoto img{width:38px;height:50px;object-fit:contain;flex:none}
.subphoto .lnk{margin-left:auto}

/* ---- 投稿须知 ---- */
.rulesbox{
  margin-top:20px;padding:15px 16px;border-radius:var(--r);
  border:var(--lw) solid var(--line-ghost);background:var(--fill-2);
}
.rulesbox .rt{font-size:13px;font-weight:var(--w8);color:var(--line);margin-bottom:8px}
.rulesbox p{font-size:11.5px;color:var(--ink-faint);line-height:1.75}
.chk2{display:flex;align-items:flex-start;gap:9px;margin-top:13px;cursor:pointer}
.chk2 i{
  flex:none;width:17px;height:17px;margin-top:1px;border-radius:3px;
  border:2px solid var(--line-ghost);position:relative;
}
.chk2 i.on{border-color:var(--acc);background:var(--acc)}
.chk2 i.on::after{
  content:"";position:absolute;left:4px;top:1px;width:5px;height:9px;
  border:solid var(--acc-ink,#1a1a1a);border-width:0 2px 2px 0;transform:rotate(45deg);
}
.chk2 span{font-size:11.5px;color:var(--ink-dim);line-height:1.6}

/* ---- 我的投稿列表 ---- */
.sublist{margin-top:16px;border:var(--lw) solid var(--line-ghost);border-radius:var(--r);overflow:hidden}
.subrow{
  display:flex;align-items:center;gap:12px;padding:13px 14px;
  border-bottom:1px solid var(--line-ghost);background:var(--fill-2);
}
.subrow:last-child{border-bottom:0}
.subrow .sri{width:34px;height:44px;flex:none;display:flex;align-items:center;justify-content:center;opacity:.7}
.subrow .sri img{width:100%;height:100%;object-fit:contain}
.subrow .sri svg{width:20px;height:20px}
.subrow .src{flex:1;min-width:0}
.subrow .srt{font-size:13px;font-weight:var(--w7);color:var(--line);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.subrow .srs{font-size:11px;color:var(--ink-faint);margin-top:3px}
.subrow .srb{
  font-family:var(--mono);font-size:9px;letter-spacing:0.045em;padding:4px 7px;border-radius:3px;
  flex:none;background:rgba(255,255,255,.07);color:var(--ink-dim);
}
.subrow.st-approved .srb{background:var(--acc);color:var(--acc-ink,#1a1a1a);font-weight:var(--w7)}
.subrow.st-rejected .srb{background:rgba(228,105,47,.18);color:var(--hot,#E4692F)}

/* ---- 详情页的署名条 ---- */
.creditbar{
  display:flex;align-items:center;gap:10px;margin-top:12px;padding:9px 12px;
  border-left:var(--lw) solid var(--acc);background:var(--fill-2);
  font-size:11.5px;color:var(--ink-dim);
}
.creditbar span{flex:1}
.lnk{
  background:none;border:0;padding:0;cursor:pointer;font-family:var(--mono);
  font-size:10px;letter-spacing:0.045em;text-transform:uppercase;
  color:var(--ink-faint);text-decoration:underline;text-underline-offset:3px;
}

/* ---- 贡献者名单 ---- */
.credlist{margin-top:18px;border:var(--lw) solid var(--line-ghost);border-radius:var(--r);overflow:hidden}
.credrow{
  display:flex;align-items:center;gap:11px;padding:13px 14px;
  border-bottom:1px solid var(--line-ghost);background:var(--fill-2);
}
.credrow:last-child{border-bottom:0}
.credrow .crn{
  font-family:var(--led);font-weight:var(--w9);font-size:15px;color:var(--ink-faint);
  width:20px;text-align:center;flex:none;font-variant-numeric:tabular-nums;
}
.credrow .crc{flex:1;min-width:0}
.credrow .crt{font-size:13.5px;font-weight:var(--w8);color:var(--line)}
.credrow .crs{font-size:11px;color:var(--ink-faint);margin-top:3px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.credrow .crb{
  font-family:var(--mono);font-size:10px;color:var(--acc-text);flex:none;letter-spacing:0.027em;
}
.iconbtn.sm{width:28px;height:28px;flex:none;opacity:.5}
.iconbtn.sm svg{width:15px;height:15px}

/* ---- 举报弹窗 ---- */
.replist{display:flex;flex-direction:column;gap:8px;margin-top:14px}
.repopt{
  text-align:left;padding:14px 15px;border-radius:var(--r);cursor:pointer;
  border:var(--lw) solid var(--line-ghost);background:var(--fill-2);
  color:var(--ink);font-size:13.5px;font-family:inherit;
  transition:border-color .15s, transform .1s;
}
.repopt:active{transform:scale(.985);border-color:var(--acc)}

/* ---- 库页的投稿入口:和测验横幅区分开,用虚线边 ---- */
.banner.sub{border-style:dashed}

/* =====================================================================
   Y. 英文排版
   ---------------------------------------------------------------------
   英文之前难看,不是「翻译得不好」,是排版整个是按中文调的:

   1. 字族错了。四套主题的 --sans 里 CJK 字体排在拉丁字体后面当兜底,
      看着没问题 —— 但温网那套 --sans 直接是 "Noto Serif SC",
      美网那套更狠,正文明确指定 "Noto Sans SC"。
      结果英文正文是用中文字体里的拉丁字形渲染的:字腔宽、字重平、
      间距是为等宽方块字设计的。那套字形放中文里没问题,单独排英文就是廉价。

   2. 字号和行高是按中文调的。中文一个字一个方块、信息密度高,
      13px / 1.7 刚好;同样的参数排英文偏小偏挤。英文需要更大的 x-height
      余量和更松的行高才立得住。

   3. 字距。中文没有词间距,所以标题给了 -.035em 的负字距收紧;
      英文本来就有词间距,同样的负字距会让字母粘在一起。

   4. 大写。中文没有大小写,.toUpperCase() 在中文里是空操作;
      英文里「WILSON PRO STAFF SIX.ONE TOUR BLX 90」这么长一串全大写,
      又吵又难读。

   所以这一段不是「微调」,是给英文单独定一套排版参数。
   只在 html[lang="en"] 下生效,中文一个像素都不动。
   ===================================================================== */

html[lang="en"]{
  /* 拉丁正文栈:先用界面字体,CJK 只在真的出现汉字时兜底 */
  --sans-en: "Outfit", -apple-system, BlinkMacSystemFont, "SF Pro Text", system-ui, sans-serif;
  /* 正文衬线和标题衬线必须分开。Playfair 是展示体,笔画对比极大,
     14px 正文下细笔画会掉;iOS 的 New York(ui-serif)才是给正文用的。 */
  --serif-en: ui-serif, "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, serif;
  --disp-serif-en: "Playfair Display", ui-serif, Georgia, serif;
  --sans:var(--sans-en);
}

/* ---------------------------------------------------------------------
   1. 正文:更大、更松、断词更聪明
   --------------------------------------------------------------------- */
html[lang="en"] body{
  font-family:var(--sans-en);
  -webkit-font-smoothing:antialiased;
  -moz-osx-font-smoothing:grayscale;
}
html[lang="en"] p,
html[lang="en"] .advice,
html[lang="en"] .petsay,
html[lang="en"] .say,
html[lang="en"] .obsub,
html[lang="en"] .invnote,
html[lang="en"] .rulesbox p,
html[lang="en"] .empty,
html[lang="en"] .emptybig .ebs{
  font-size:13.5px;
  line-height:1.68;
  letter-spacing:0;
  /* 英文长句拆行容易留孤字,pretty 会把最后一行补齐 */
  text-wrap:pretty;
}
/* 一行别超过 ~62 个字符 —— 超过这个宽度眼睛回扫会找不到行 */
html[lang="en"] .screen > p,
html[lang="en"] .obsub,
html[lang="en"] .invnote{max-width:34em}

/* 细则、脚注:英文小字比中文更容易糊,补一档字号 */
html[lang="en"] .fine{font-size:11px;line-height:1.72;letter-spacing:.002em}

/* ---------------------------------------------------------------------
   2. 标题:负字距要减半
   中文标题 -.035em 是为了把方块字挤紧;英文本来就有词间距,
   同样的值会让字母互相粘。
   --------------------------------------------------------------------- */
html[lang="en"] .h1,
html[lang="en"] .ctop .ttl,
html[lang="en"] .ob .ttl{
  letter-spacing:-.018em;
  text-wrap:balance;          /* 两行标题不要头重脚轻 */
  line-height:1.04;
}
html[lang="en"] .h1{font-size:31px}

/* ---------------------------------------------------------------------
   3. eyebrow / 小标签:英文大写是对的,但要更宽的字距和更小的字号
   --------------------------------------------------------------------- */
html[lang="en"] .eyebrow,
html[lang="en"] .sect h3 .en,
html[lang="en"] .grouphd,
html[lang="en"] .invhd,
html[lang="en"] .obsect h3 .en{
  font-size:9.5px;
  letter-spacing:0.099em;
  font-weight:600;
}

/* ---------------------------------------------------------------------
   4. 全大写的品牌 + 型号:英文里太长了,改成只大写品牌
   --------------------------------------------------------------------- */
html[lang="en"] .mini .md,
html[lang="en"] .hero .md,
html[lang="en"] .petname .petmodel{
  text-transform:none;
  letter-spacing:.02em;
  font-size:10.5px;
}
html[lang="en"] .libcard .lb{letter-spacing:0.072em;font-size:9.5px}
html[lang="en"] .libcard .lm{
  font-size:14.5px;letter-spacing:-.008em;
  /* 型号名带括号和数字,别在括号中间断 */
  word-break:normal;overflow-wrap:anywhere;
}

/* ---------------------------------------------------------------------
   5. 数字:英文界面里统计数字用等宽数字,跳动时不抖
   --------------------------------------------------------------------- */
html[lang="en"] .score,
html[lang="en"] .vital .big,
html[lang="en"] .statcard .v,
html[lang="en"] .invcell b,
html[lang="en"] .plan .pp,
html[lang="en"] .trait .tv{
  font-variant-numeric:tabular-nums lining-nums;
  letter-spacing:-.02em;
}

/* ---------------------------------------------------------------------
   6. 四套主题各自的英文字族
   中文版每套主题都有一套 CJK 字体搭配;英文版要各自换成对应气质的拉丁字体,
   而不是继续挂着 CJK 兜底。
   --------------------------------------------------------------------- */

/* 红土:Space Grotesk 本来就是拉丁字体,只需要把 CJK 兜底挪后 */
html[lang="en"] .slam-rg{
  --sans:"Space Grotesk",-apple-system,system-ui,sans-serif;
  --disp:"Space Grotesk",-apple-system,system-ui,sans-serif;
  --disp-ls:-.022em;
}
html[lang="en"] .slam-rg .ctop .ttl{font-size:44px}

/* 澳网:Outfit,字腔大,标题回收一点 */
html[lang="en"] .slam-ao{
  --sans:"Outfit",-apple-system,system-ui,sans-serif;
  --disp:"Outfit",-apple-system,system-ui,sans-serif;
  --disp-ls:-.014em;
}
html[lang="en"] .slam-ao .ctop .ttl{font-size:42px}

/* 温网:标题 Playfair,正文换成真正的拉丁衬线(iOS 的 New York / Iowan),
   不再用思源宋的拉丁字形 —— 那是这一套之前最难看的地方 */
html[lang="en"] .slam-wb{
  --sans:var(--serif-en);
  --disp:var(--disp-serif-en);
  --disp-ls:-.006em;
}
html[lang="en"] .slam-wb .ctop .ttl{font-size:43px;line-height:1.02}
html[lang="en"] .slam-wb p,
html[lang="en"] .slam-wb .advice,
html[lang="en"] .slam-wb .say,
html[lang="en"] .slam-wb .petsay,
html[lang="en"] .slam-wb .obsub{
  font-size:14px;line-height:1.74;letter-spacing:.004em;
}
/* 衬线小字用大写会散,温网的 eyebrow 改小写 + 斜体,更像赛事节目单 */
html[lang="en"] .slam-wb .eyebrow,
html[lang="en"] .slam-wb .sect h3 .en{
  text-transform:none;font-style:italic;letter-spacing:0.027em;font-size:11px;font-weight:500;
}

/* 美网:Oswald 是压缩体,当标题和记分牌很对;正文绝不能用它,
   之前正文回落到 "Noto Sans SC",英文下就是用中文字体排英文 —— 换掉 */
html[lang="en"] .slam-uso{
  --sans:"Outfit",-apple-system,system-ui,sans-serif;
  --disp:"Oswald",-apple-system,system-ui,sans-serif;
  --disp-ls:-.006em;
}
html[lang="en"] .slam-uso .ctop .ttl{font-size:56px;letter-spacing:-.012em}
html[lang="en"] .slam-uso p,
html[lang="en"] .slam-uso .advice,
html[lang="en"] .slam-uso .petsay,
html[lang="en"] .slam-uso .say,
html[lang="en"] .slam-uso .fine,
html[lang="en"] .slam-uso .setrow .sn,
html[lang="en"] .slam-uso .mfeat .mft{
  font-family:"Outfit",-apple-system,system-ui,sans-serif;
}
/* Oswald 全大写 + 宽字距是它的正确用法,eyebrow 交给它 */
html[lang="en"] .slam-uso .eyebrow{font-family:"Oswald",system-ui,sans-serif;letter-spacing:0.117em}

/* ---------------------------------------------------------------------
   7. 英文里容易撑破的几个地方
   中文两个字的标签,英文常常是 "Beginner–Intermediate" 这种长串。
   --------------------------------------------------------------------- */
html[lang="en"] .tag{font-size:10px;letter-spacing:.01em;padding:4px 8px;white-space:nowrap}
html[lang="en"] .mfeat{grid-template-columns:1fr 68px 68px}
html[lang="en"] .mfeat .mft{font-size:12px;line-height:1.35}
html[lang="en"] .mfeat .mfree,
html[lang="en"] .mfeat .mpro{font-size:10.5px;line-height:1.3}
html[lang="en"] .setrow .sn{font-size:12.5px}
html[lang="en"] .gopt b{font-size:12px}
html[lang="en"] .gopt i{font-size:10px;line-height:1.35}
html[lang="en"] .slamcard .ss{font-size:9px;letter-spacing:0.027em}
html[lang="en"] .qopt .ol{font-size:14.5px;letter-spacing:-.005em}
html[lang="en"] .qopt .os{font-size:11.5px}
html[lang="en"] .qq{font-size:23px;letter-spacing:-.015em;text-wrap:balance}
html[lang="en"] .btn{letter-spacing:.005em}
html[lang="en"] .plan b{font-size:11.5px;letter-spacing:.04em;text-transform:uppercase}
html[lang="en"] .trialnote{font-size:11px;line-height:1.65;max-width:30em;margin-left:auto;margin-right:auto}

/* 表单标签在英文下更适合小写宽字距,而不是全大写 */
html[lang="en"] .fld .fl{font-size:9.5px;letter-spacing:0.081em}


/* =====================================================================
   Z. 首屏引导 —— 网球当角色
   ===================================================================== */
.ob{padding-top:6px}
.ob .obhd{margin-bottom:4px}
.ob .ttl{
  font-family:var(--disp);font-weight:var(--disp-w);letter-spacing:var(--disp-ls);
  font-size:38px;line-height:1.03;color:var(--line);margin-top:8px;
}
.ob .ttl em{font-style:normal;color:var(--acc-text)}
.ob .obsub{font-size:13px;color:var(--ink-dim);line-height:1.72;margin-top:12px}

/* ---- 球 + 气泡 ---- */
.obball{
  position:relative;display:flex;align-items:center;gap:14px;
  margin:26px 0 8px;cursor:pointer;-webkit-tap-highlight-color:transparent;
}
.obbubble{
  flex:1;position:relative;padding:15px 16px;border-radius:var(--r);
  border:var(--lw) solid var(--line-ghost);background:var(--fill-2);
  font-size:14px;line-height:1.55;color:var(--line);
}
/* 气泡尾巴指向球 */
.obbubble::after{
  content:"";position:absolute;right:-9px;top:50%;margin-top:-7px;
  width:0;height:0;border:7px solid transparent;border-left-color:var(--line-ghost);
}
.obbubble .obtap{
  display:block;font-style:normal;margin-top:7px;
  font-family:var(--mono);font-size:9px;letter-spacing:0.09em;
  text-transform:uppercase;color:var(--ink-faint);
}
.obb{
  flex:none;width:76px;height:76px;position:relative;
  animation:obBob 3.2s ease-in-out infinite;
}
.obb img{width:100%;height:100%;object-fit:contain;
  filter:drop-shadow(0 10px 16px rgba(0,0,0,.45))}
.obball:active .obb{animation:obSquash .34s var(--ease)}
@keyframes obBob{
  0%,100%{transform:translateY(0) rotate(-3deg)}
  50%    {transform:translateY(-7px) rotate(3deg)}
}
@keyframes obSquash{
  0%  {transform:scale(1,1)}
  35% {transform:scale(1.14,.86) translateY(4px)}
  70% {transform:scale(.94,1.08) translateY(-3px)}
  100%{transform:scale(1,1)}
}

/* ---- 热门六把 ---- */
.obsect{margin-top:26px}
.obsect h3{
  font-size:12px;color:var(--line);font-weight:var(--w8);letter-spacing:.01em;
  display:flex;align-items:center;gap:9px;margin-bottom:12px;
  padding-bottom:8px;border-bottom:var(--lw) solid var(--line-ghost);
}
.obsect h3 .en{
  font-family:var(--mono);font-size:9px;color:var(--ink-faint);
  letter-spacing:0.09em;text-transform:uppercase;font-weight:500;margin-left:auto;
}
.obgrid{display:grid;grid-template-columns:repeat(3,1fr);gap:9px}
.obcard{
  position:relative;overflow:hidden;cursor:pointer;
  display:flex;flex-direction:column;align-items:center;text-align:center;
  padding:12px 8px 11px;border-radius:var(--r);
  border:var(--lw) solid var(--line-ghost);background:var(--fill-2);
  color:inherit;font-family:inherit;
  transition:border-color .18s, transform .12s;
}
.obcard::before{
  content:"";position:absolute;left:50%;top:-30%;width:120%;height:80%;
  transform:translateX(-50%);pointer-events:none;
  background:radial-gradient(60% 60% at 50% 50%, var(--rk), transparent 70%);
  opacity:.16;
}
.obcard:active{transform:scale(.965);border-color:var(--rk)}
.obcard .obimg{height:64px;display:flex;align-items:center;justify-content:center;position:relative;z-index:1}
.obcard .obimg img{max-height:64px;max-width:100%;object-fit:contain}
.obcard b{
  font-size:11px;font-weight:var(--w7);color:var(--line);margin-top:8px;line-height:1.25;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;
}
.obcard i{font-style:normal;font-size:9px;color:var(--ink-faint);margin-top:4px;
  font-family:var(--mono);letter-spacing:0.045em;text-transform:uppercase}

/* ---- 两条出口 ---- */
.obrow{
  display:flex;align-items:center;gap:12px;margin-top:10px;padding:14px 15px;
  border-radius:var(--r);border:var(--lw) solid var(--line-ghost);
  background:var(--fill-2);cursor:pointer;
  transition:border-color .18s, transform .12s;
}
.obrow:active{transform:scale(.985);border-color:var(--acc)}
.obrow .obi{width:20px;height:20px;flex:none;color:var(--acc-text)}
.obrow .obi svg{width:100%;height:100%}
.obrow .obt{flex:1;min-width:0}
.obrow .obt b{display:block;font-size:13.5px;font-weight:var(--w7);color:var(--line)}
.obrow .obt span{display:block;font-size:11px;color:var(--ink-faint);margin-top:3px}
.obrow .chev{opacity:.4;flex:none}


/* =====================================================================
   AA. 美网主题卡看不见的问题
   ---------------------------------------------------------------------
   .sc-uso 的场地色是 #0A1A40,在美网这套本身就深蓝的背景上几乎糊成一块。
   再叠上锁定态的 opacity .42 + 一层 rgba(0,0,0,.22),整张卡就消失了。
   两件事都要改:场地本身提亮,锁定态别再压黑。
   ===================================================================== */
.sc-uso .surf{
  background:
    /* 夜场灯带:顶部一道冷白,这是美网夜场最认得出来的特征 */
    linear-gradient(180deg,rgba(190,225,255,.20) 0 6%,transparent 22%),
    linear-gradient(90deg,#15316B 0 30%,rgba(255,255,255,.92) 30% 31.2%,#2E6FD4 31.2%);
}
.sc-uso .surf{box-shadow:inset -22px 10px 30px -16px rgba(210,232,255,.42)}
/* 卡片自身给一道内描边,和背景断开 */
.slamcard.sc-uso{box-shadow:inset 0 0 0 1px rgba(150,190,255,.16)}

/* 锁定态:压暗改成降饱和 + 轻微压暗,不要再叠一层黑。
   目的是「看得见但明显不可用」,不是「看不见」。 */
.slamcard.locked > *:not(.prolock),
.gopt.locked   > *:not(.prolock){opacity:.62;filter:saturate(.55)}
.slamcard.locked::after,
.gopt.locked::after{background:rgba(0,0,0,.10)}
.slamcard.locked{border-style:dashed}


/* ---- 删除按钮:危险操作要看得出来 ---- */
.btn.ghost.danger{color:var(--hot,#E4692F);border-color:rgba(228,105,47,.36)}
.btn.ghost.danger:active{background:rgba(228,105,47,.10)}


/* =====================================================================
   AB. 版面修正 —— 按真机截图逐条改
   ===================================================================== */

/* ---------------------------------------------------------------------
   1. 标签字体不再是等宽
   --mono 这个名字起错了:它指的是「小号标签字体」,不是等宽字体。
   Space Grotesk / Outfit / Oswald 都是比例字体,兜底却写了 ui-monospace,
   web font 没加载时整屏变终端。上面已经全局换掉,这里再补两件事:
     · 需要数字对齐的地方单独用 --num,靠 font-variant-numeric 而不是等宽字体
     · 标签的字距按字号重新给,9px 配 .1em 在比例字体下太挤
   --------------------------------------------------------------------- */
:root{
  --num:-apple-system,"SF Pro Text",system-ui,sans-serif;
}
.score,.vital .big,.vital .pct,.statcard .v,.invcell b,.plan .pp,
.trait .tv,.tug .tv,.gauge .lbs span{
  font-variant-numeric:tabular-nums lining-nums;
}

/* ---------------------------------------------------------------------
   2. 首页赛事切换条:四等分,不再按内容撑开
   之前是 flex + 内容宽度,英文四个词加起来 35 个字符,412px 排不下,
   第四项直接被 overflow:hidden 吃掉。改成四等分网格,每格居中,
   文案也换成场地名(Clay/Hard/Grass/Night)—— 短、且这本来就是在选场地。
   --------------------------------------------------------------------- */
.slamrow{
  display:grid;grid-template-columns:repeat(4,1fr);
  border:var(--lw) solid var(--line-ghost);border-radius:var(--r-sm);
  overflow:hidden;width:100%;
}
.slamrow b{
  font-family:var(--sans);font-size:10.5px;font-weight:var(--w7);letter-spacing:0.027em;
  padding:8px 4px;text-align:center;white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis;
  color:var(--ink-dim);cursor:pointer;position:relative;
  border-right:var(--lw) solid var(--line-ghost);
  transition:background .2s,color .2s;
}
.slamrow b:last-child{border-right:0}
.slamrow b.on{background:var(--line);color:var(--surface-deep)}
html[lang="en"] .slamrow b{font-size:11px;letter-spacing:.02em;font-weight:600}

/* ---------------------------------------------------------------------
   3. 大标题被切
   line-height:.9 在 46~56px 下,上伸部一定超出行盒,父级一裁就没顶。
   --------------------------------------------------------------------- */
.ctop .ttl{line-height:1.0;padding-top:.06em;padding-bottom:.04em;margin-top:12px}
html[lang="en"] .ctop .ttl{line-height:1.02;padding-top:.08em}
.h1{line-height:1.06;padding-top:.04em}
.ob .ttl{line-height:1.06;padding-top:.05em}
/* 屏幕本身不要裁掉内容 */
.screen{overflow-x:clip}

/* ---------------------------------------------------------------------
   4. 头像下面那行提示压出卡片
   原来是 bottom:-17px 绝对定位,直接盖在卡片边缘上。
   改成把它放进卡片布局里 —— 头像和文字并排,提示跟在名字下面。
   --------------------------------------------------------------------- */
.avatar.up .avhint{display:none}
.profcard{
  margin-top:18px;padding:16px;border-radius:var(--r);
  border:var(--lw) solid var(--line-ghost);background:var(--fill-1);
  display:flex;gap:15px;align-items:center;
}
.profcard .pinfo{flex:1;min-width:0}
.profcard .pname{
  font-weight:var(--w8);font-size:18px;color:var(--line);letter-spacing:-.015em;line-height:1.2;
}
.profcard .pmeta{
  font-size:12px;color:var(--ink-dim);margin-top:5px;line-height:1.45;
  font-variant-numeric:tabular-nums;
}
.profcard .phint{
  font-size:10px;color:var(--ink-faint);margin-top:7px;letter-spacing:0.045em;
  text-transform:uppercase;
}
html[lang="en"] .profcard .pname{font-size:19px}
html[lang="en"] .profcard .pmeta{font-size:12.5px}

/* ---------------------------------------------------------------------
   5. PRO 角标:实心薄荷绿药丸太像贴纸
   改成描边 + 极淡底 + 宽字距的小型大写,像器材上的规格钢印,
   而不是电商促销标。
   --------------------------------------------------------------------- */
.prolock{
  position:absolute;right:9px;top:9px;z-index:6;
  font-family:var(--sans);font-size:8.5px;font-weight:var(--w7);letter-spacing:0.081em;
  padding:3.5px 7px 3px;border-radius:2px;
  color:var(--acc-text);
  background:rgba(0,0,0,.34);
  /* 不用 color-mix —— 老 webview 不支持,而且这里也不需要:
     描边用一个中性的半透明白,在四套主题下都成立。 */
  border:1px solid rgba(255,255,255,.26);
  box-shadow:none;
}
.prolock.big{position:static;font-size:9.5px;padding:5px 10px 4px;letter-spacing:0.099em}
.probadge{
  font-family:var(--sans);font-size:9px;font-weight:var(--w7);letter-spacing:0.081em;
  padding:4px 8px 3px;border-radius:2px;
  color:var(--acc-text);background:rgba(0,0,0,.3);
  border:1px solid rgba(255,255,255,.22);
}

/* ---------------------------------------------------------------------
   6. 「它们想对你说」那几张卡太挤
   四行信息(名字/型号/台词/状态)全挤在 58px 高的行里。
   英文字符串更长,挤得更明显。放开行距,把型号挪到第二行。
   --------------------------------------------------------------------- */
.petcard{gap:15px;padding:16px 15px;align-items:flex-start}
.petcard .petrk{width:46px;height:62px}
.petname{font-size:15px;line-height:1.25;letter-spacing:-.01em;
  display:flex;flex-direction:column;gap:3px}
.petname .petmodel{
  font-family:var(--sans);font-size:11px;letter-spacing:.02em;
  font-weight:500;color:var(--ink-faint);
}
.petsay{font-size:13.5px;margin:9px 0 9px;line-height:1.55}
.petmood{font-family:var(--sans);font-size:11px;letter-spacing:.01em;gap:7px}
.petlist{display:flex;flex-direction:column;gap:10px}
html[lang="en"] .petname{font-size:15.5px}
html[lang="en"] .petname .petmodel{font-size:11.5px}
html[lang="en"] .petsay{font-size:14px;line-height:1.55}

/* ---------------------------------------------------------------------
   7. 英文下所有小标签统一:比例字体 + 小型大写的排法,
      不再是「等宽 + 全大写」那套终端味
   --------------------------------------------------------------------- */
html[lang="en"] .eyebrow,
html[lang="en"] .sect h3 .en,
html[lang="en"] .obsect h3 .en,
html[lang="en"] .grouphd,
html[lang="en"] .invhd,
html[lang="en"] .statcard .k,
html[lang="en"] .statcard .vsub,
html[lang="en"] .log .ld,
html[lang="en"] .mini .md,
html[lang="en"] .libcard .lb,
html[lang="en"] .ctop .sub,
html[lang="en"] .profcard .phint{
  font-family:var(--sans);
  font-weight:600;
  letter-spacing:0.063em;
}
html[lang="en"] .log .ld,
html[lang="en"] .petmood,
html[lang="en"] .statcard .vsub{
  letter-spacing:.01em;font-weight:500;text-transform:none;
}

/* 统计卡在英文下三列会挤,给一点呼吸 */
html[lang="en"] .statcard{padding:14px 10px}
html[lang="en"] .statcard .k{font-size:9px;letter-spacing:0.072em}
html[lang="en"] .statcard .vsub{font-size:10px}

/* ---------------------------------------------------------------------
   AC. 没有 web font 时,四套主题也要还能分得出来
   ---------------------------------------------------------------------
   截图证明用户那边一个 web font 都没加载:标题落到了系统 sans,
   小标签落到了 SF Mono。也就是说 Space Grotesk / Outfit / Oswald /
   Playfair 这套四主题字体系统,用户从来没看见过。

   字体文件必须打进包里(见 上架清单)。但在那之前,兜底也不能是
   「四套长得一模一样」。用系统字体自己的可变轴做出区分:

     法网  系统 sans,字距收紧      —— 干脆、工业感
     澳网  系统 sans,字距放开      —— 开阔、现代
     温网  ui-serif(iOS 的 New York)—— 衬线,和其它三套天然不同
     美网  系统 sans + 压缩字宽     —— 记分牌那种窄高感

   font-stretch 在 iOS 16+ 的 SF 上是可变轴,真的会变窄;
   不支持的设备上这条被忽略,退回常规字宽,不会出错。
   --------------------------------------------------------------------- */
.slam-rg  .ctop .ttl, .slam-rg  .h1, .slam-rg  .ob .ttl{ letter-spacing:-.03em }
.slam-ao  .ctop .ttl, .slam-ao  .h1, .slam-ao  .ob .ttl{ letter-spacing:-.008em }
.slam-wb  .ctop .ttl, .slam-wb  .h1, .slam-wb  .ob .ttl{ font-family:var(--disp),ui-serif,Georgia,serif }
.slam-uso .ctop .ttl, .slam-uso .h1, .slam-uso .ob .ttl,
.slam-uso .score, .slam-uso .vital .big{
  /* font-stretch 只对带 wdth 可变轴的字体有效(iOS 16+ 的 SF 有),
     而美网这套的 display 字体是 Avenir Next Condensed —— 它本来就是窄的,
     再对它下宽度指令只会干扰字面匹配。两条互相打架的 font-stretch 一起去掉,
     窄高感交给字体家族本身。 */
  letter-spacing:-.015em;
}

/* 大号数字在任何字体下都要对齐 */
.score,.vital .big,.statcard .v,.invcell b,.plan .pp{
  font-variant-numeric:tabular-nums lining-nums;
  font-feature-settings:"tnum" 1,"lnum" 1;
}

/* =====================================================================
   AD. 字体:改用 iOS 自带字体,彻底不依赖网络
   ---------------------------------------------------------------------
   截图证明 Space Grotesk / Outfit / Oswald / Playfair 一个都没加载出来。
   之前的思路是「web font 优先,系统字体兜底」——
   但那意味着:离线首启没有设计、Google Fonts 被挡没有设计、
   加载慢的时候先闪一版再跳一版。对一个要上架的原生 app,这不成立。

   所以反过来:**系统字体优先,而且只用系统字体。**
   iPhone 上这四套是必然存在的,不用下载、不用打包、没有 FOUT、
   没有第三方请求(顺便省掉隐私问卷里那一条),而且它们本身就够好。

   四套的选择不是随手挑的,是按各自赛事的气质来的:

     法网  Futura              几何无衬线。欧陆、干脆、上世纪现代主义,
                               和红土那种硬朗的土场气质对得上。
     澳网  SF Pro(系统默认)    当代、中性、屏幕上最干净。澳网的品牌语言
                               本来就是最"现在"的那一个。
     温网  Baskerville         英国过渡期衬线。这是温网唯一说得通的选择 ——
                               草地、白衣、皇家俱乐部,只能是英式衬线。
     美网  Avenir Next Condensed  压缩体。夜场、记分牌、纽约。窄高的字形
                               和 Oswald 是同一路,而且它真的在每台 iPhone 上。

   中文那侧同理:苹方(PingFang SC)是系统默认,温网配宋体(Songti SC)。

   Google Fonts 的 <link> 已经从 index.html 拿掉了。
   ===================================================================== */

:root{
  --sans:-apple-system,BlinkMacSystemFont,"SF Pro Text","PingFang SC",system-ui,sans-serif;
  --serif:"Baskerville","Hoefler Text",ui-serif,"Songti SC",Georgia,serif;
  --mono:-apple-system,BlinkMacSystemFont,"SF Pro Text",system-ui,sans-serif;
  --led:-apple-system,BlinkMacSystemFont,"SF Pro Display",system-ui,sans-serif;
  --disp:var(--sans);
}

/* ---- 法网:Futura ---- */
.slam-rg{
  --sans:"Futura","Avenir Next",-apple-system,"PingFang SC",system-ui,sans-serif;
  --disp:"Futura","Avenir Next",-apple-system,"PingFang SC",system-ui,sans-serif;
  --mono:"Avenir Next",-apple-system,"PingFang SC",system-ui,sans-serif;
  --led:"Futura","Avenir Next",-apple-system,system-ui,sans-serif;
  --disp-w:600; --disp-ls:-.012em;
}
/* Futura 的大写 O/C 是正圆,字宽比 SF 大,标题回收一档 */
.slam-rg .ctop .ttl{font-size:42px}
.slam-rg .ob .ttl{font-size:36px}
/* Futura 没有很重的字重,靠字号和字距撑气势,不靠 900 */
.slam-rg .h1,.slam-rg .ctop .ttl,.slam-rg .ob .ttl{font-weight:600}
html[lang="en"] .slam-rg .eyebrow{letter-spacing:0.108em}

/* ---- 澳网:SF Pro ---- */
.slam-ao{
  --sans:-apple-system,BlinkMacSystemFont,"SF Pro Text","PingFang SC",system-ui,sans-serif;
  --disp:-apple-system,BlinkMacSystemFont,"SF Pro Display","PingFang SC",system-ui,sans-serif;
  --mono:-apple-system,BlinkMacSystemFont,"SF Pro Text","PingFang SC",system-ui,sans-serif;
  --led:-apple-system,BlinkMacSystemFont,"SF Pro Display",system-ui,sans-serif;
  --disp-w:800; --disp-ls:-.028em;
}
.slam-ao .ctop .ttl{font-size:46px}
.slam-ao .ob .ttl{font-size:38px}

/* ---- 温网:Baskerville ---- */
.slam-wb{
  --sans:"Baskerville","Hoefler Text",ui-serif,"Songti SC",Georgia,serif;
  --disp:"Baskerville","Hoefler Text",ui-serif,"Songti SC",Georgia,serif;
  --mono:-apple-system,"SF Pro Text","PingFang SC",system-ui,sans-serif;
  --led:"Baskerville",ui-serif,Georgia,serif;
  --disp-w:700; --disp-ls:-.004em;
}
/* Baskerville 的 x-height 偏小,正文和标题都要比无衬线大一档 */
.slam-wb .ctop .ttl{font-size:46px;line-height:1.04}
.slam-wb .ob .ttl{font-size:38px;line-height:1.08}
.slam-wb p,.slam-wb .advice,.slam-wb .say,.slam-wb .petsay,.slam-wb .obsub,
.slam-wb .empty,.slam-wb .setrow .sn,.slam-wb .mfeat .mft{
  font-size:14.5px;line-height:1.72;
}
/* 衬线的小号大写会散架,温网的 eyebrow 用小写斜体,像赛事节目单 */
.slam-wb .eyebrow,.slam-wb .sect h3 .en,.slam-wb .obsect h3 .en{
  font-family:"Baskerville",ui-serif,Georgia,serif;
  text-transform:none;font-style:italic;letter-spacing:.04em;
  font-size:12px;font-weight:400;
}

/* ---- 美网:Avenir Next Condensed ---- */
.slam-uso{
  --sans:-apple-system,"SF Pro Text","PingFang SC",system-ui,sans-serif;
  --disp:-apple-system,BlinkMacSystemFont,"SF Pro Display","PingFang SC",system-ui,sans-serif;
  --mono:-apple-system,"SF Pro Text","PingFang SC",system-ui,sans-serif;
  --led:-apple-system,BlinkMacSystemFont,"SF Pro Display",system-ui,sans-serif;
  --disp-w:700; --disp-ls:.004em;
}
/* 压缩体同字号视觉更小,标题放大;正文回到 SF 保证长句可读 */
.slam-uso .ctop .ttl{font-size:56px;letter-spacing:0}
.slam-uso .ob .ttl{font-size:46px;letter-spacing:0}
.slam-uso .vital .big{font-size:78px;letter-spacing:0}
.slam-uso p,.slam-uso .advice,.slam-uso .petsay,.slam-uso .say,.slam-uso .fine,
.slam-uso .obsub,.slam-uso .setrow .sn,.slam-uso .mfeat .mft{
  font-family:-apple-system,"SF Pro Text","PingFang SC",system-ui,sans-serif;
}
/* 压缩体天生适合宽字距的全大写小标签 */
.slam-uso .eyebrow{letter-spacing:0.135em;font-weight:600}

/* ---------------------------------------------------------------------
   之前那一堆 html[lang="en"] 的字族覆盖全部作废 ——
   现在中英共用同一套系统字体,拉丁字形本来就是这些字体自己的,
   不存在「用中文字体排英文」的问题了。
   英文只保留排版参数上的差别(字距、行高、行长)。
   --------------------------------------------------------------------- */
html[lang="en"] .slam-rg,
html[lang="en"] .slam-ao,
html[lang="en"] .slam-wb,
html[lang="en"] .slam-uso{
  --sans:inherit; --disp:inherit;
}
html[lang="en"] .slam-rg{
  --sans:"Futura","Avenir Next",-apple-system,system-ui,sans-serif;
  --disp:"Futura","Avenir Next",-apple-system,system-ui,sans-serif;
}
html[lang="en"] .slam-ao{
  --sans:-apple-system,BlinkMacSystemFont,"SF Pro Text",system-ui,sans-serif;
  --disp:-apple-system,BlinkMacSystemFont,"SF Pro Display",system-ui,sans-serif;
}
html[lang="en"] .slam-wb{
  --sans:"Baskerville","Hoefler Text",ui-serif,Georgia,serif;
  --disp:"Baskerville","Hoefler Text",ui-serif,Georgia,serif;
}
/* 美网这套原来用 Avenir Next Condensed 做标题。压缩体的问题是:
   它的字宽本来就不是常规值,一旦系统在字面匹配上退回到栈里的下一个
   ("Helvetica Neue"),字形宽度会突然变一档 —— 而这正是"字被拉伸"最像的样子,
   而且只在真机上出现(我这边的 Linux 根本没有这三个字体,永远看不到)。

   四套里只有这一套依赖非常规字宽。换成 SF Pro Display —— 它在每台 iPhone 上
   每个字重都是真字面,不存在退回。窄高的夜场感靠字距和字号给,不靠字宽。 */
html[lang="en"] .slam-uso{
  --sans:-apple-system,"SF Pro Text",system-ui,sans-serif;
  --disp:-apple-system,BlinkMacSystemFont,"SF Pro Display",system-ui,sans-serif;
}

/* =====================================================================
   AE. 首屏引导 —— 重做
   ---------------------------------------------------------------------
   上一版的问题不在概念,在呈现:
     · 球只有 76px,还被挤在一个灰框旁边,完全不是主角
     · 对话框是一个硬边灰矩形,没有任何角色感,像个报错提示
     · 六把拍是 3×2 的小方块,每张里的球拍只有 60px 高 —— 看不清是什么拍,
       而这一屏的唯一任务就是让人看清并选出一把
     · 整页从上到下全是等宽的方块,没有节奏

   这一版:
     · 球放大到 130px,落在场地上,有真的接触影(椭圆、贴地、边缘软)
     · 对话框做成从球嘴里出来的,尾巴指向球,底色透一点场地
     · 六把拍改成 2 列大卡,球拍高度翻倍到 128px,型号看得清
     · 卡片背景用该拍的涂装色做一层极淡的辉光,六张卡不再一模一样
     · 页面有节奏:大标题 → 球说话 → 选拍 → 两条出口
   ===================================================================== */

.ob{padding-top:2px}

/* ---- 标题区 ---- */
.ob .obhd{margin-bottom:2px}
.ob .ttl{
  font-family:var(--disp);font-weight:var(--disp-w);letter-spacing:var(--disp-ls);
  font-size:38px;line-height:1.06;color:var(--line);margin-top:10px;
  padding-top:.05em;text-wrap:balance;
}
.ob .ttl em{font-style:normal;color:var(--acc-text)}
.ob .obsub{
  font-size:13.5px;color:var(--ink-dim);line-height:1.62;margin-top:12px;max-width:31em;
}

/* ---- 球:这一屏的主角 ---- */
.obball{
  position:relative;display:flex;align-items:flex-end;gap:0;
  margin:22px 0 6px;min-height:150px;cursor:pointer;
  -webkit-tap-highlight-color:transparent;
}
/* 对话框:靠左,底色半透,能透出一点场地质感 */
.obbubble{
  flex:1;position:relative;margin-bottom:22px;margin-right:-14px;
  padding:16px 18px 14px;border-radius:var(--r);
  background:rgba(0,0,0,.30);
  border:1px solid rgba(255,255,255,.13);
  -webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);
  font-size:15px;line-height:1.5;color:var(--line);
  box-shadow:0 10px 26px -12px rgba(0,0,0,.5);
}
/* 尾巴:一个小三角,指向球 */
.obbubble::after{
  content:"";position:absolute;right:-7px;bottom:16px;
  width:14px;height:14px;transform:rotate(45deg);
  background:rgba(0,0,0,.30);
  border-right:1px solid rgba(255,255,255,.13);
  border-bottom:1px solid rgba(255,255,255,.13);
}
.obbubble .obtap{
  display:block;font-style:normal;margin-top:9px;
  font-family:var(--mono);font-size:9.5px;letter-spacing:0.09em;
  text-transform:uppercase;color:var(--ink-faint);
}

/* 球本体:130px,带接触影 */
.obb{
  flex:none;width:130px;height:130px;position:relative;z-index:2;
  animation:obBob 3.4s ease-in-out infinite;
}
.obb img{
  width:100%;height:100%;object-fit:contain;position:relative;z-index:2;
  filter:drop-shadow(0 6px 10px rgba(0,0,0,.34));
}
/* 接触影:贴在球正下方的椭圆,不是 drop-shadow ——
   drop-shadow 是跟着球一起动的,看起来像球浮在空中;
   这个影子单独放在地面上,球弹起来的时候它变淡变大,才有落地感。 */
.obb::after{
  content:"";position:absolute;left:50%;bottom:-6px;transform:translateX(-50%);
  width:88px;height:15px;border-radius:50%;
  background:radial-gradient(ellipse at 50% 50%,rgba(0,0,0,.42),transparent 70%);
  animation:obShadow 3.4s ease-in-out infinite;z-index:1;
}
@keyframes obBob{
  0%,100%{transform:translateY(0) rotate(-4deg)}
  50%    {transform:translateY(-13px) rotate(4deg)}
}
@keyframes obShadow{
  0%,100%{transform:translateX(-50%) scale(1);opacity:.9}
  50%    {transform:translateX(-50%) scale(.78);opacity:.5}
}
.obball:active .obb{animation:obSquash .4s var(--ease)}
@keyframes obSquash{
  0%  {transform:scale(1,1) translateY(0)}
  30% {transform:scale(1.18,.82) translateY(10px)}
  62% {transform:scale(.93,1.09) translateY(-6px)}
  100%{transform:scale(1,1) translateY(0)}
}
@media (prefers-reduced-motion:reduce){
  .obb,.obb::after{animation:none}
}

/* ---- 分区标题 ---- */
.obsect{margin-top:22px}
.obsect h3{
  font-size:12.5px;color:var(--line);font-weight:var(--w8);letter-spacing:.005em;
  display:flex;align-items:baseline;gap:9px;margin-bottom:13px;
  padding-bottom:9px;border-bottom:var(--lw) solid var(--line-ghost);
}
.obsect h3 .en{
  font-family:var(--mono);font-size:9.5px;color:var(--ink-faint);
  letter-spacing:0.09em;text-transform:uppercase;font-weight:600;margin-left:auto;
}

/* ---- 六把拍:两列大卡,球拍要看得清 ---- */
.obgrid{display:grid;grid-template-columns:1fr 1fr;gap:10px}
.obcard{
  position:relative;overflow:hidden;cursor:pointer;
  display:flex;flex-direction:column;align-items:center;text-align:center;
  padding:16px 10px 14px;border-radius:var(--r);
  border:var(--lw) solid var(--line-ghost);
  background:rgba(0,0,0,.20);
  color:inherit;font-family:inherit;
  transition:border-color .18s, transform .12s, background .18s;
}
/* 每张卡按该拍的涂装色透一层辉光,六张不再一模一样 */
.obcard::before{
  content:"";position:absolute;left:50%;top:-18%;width:150%;height:78%;
  transform:translateX(-50%);pointer-events:none;
  background:radial-gradient(50% 55% at 50% 50%, var(--rk), transparent 72%);
  opacity:.26;
}
.obcard:active{transform:scale(.972);border-color:var(--rk);background:rgba(0,0,0,.30)}
.obcard .obimg{
  height:128px;width:100%;display:flex;align-items:center;justify-content:center;
  position:relative;z-index:1;
}
.obcard .obimg img{
  max-height:128px;max-width:86%;object-fit:contain;
  filter:drop-shadow(0 8px 14px rgba(0,0,0,.45));
}
.obcard b{
  font-size:13px;font-weight:var(--w7);color:var(--line);margin-top:11px;line-height:1.3;
  letter-spacing:-.005em;position:relative;z-index:1;
}
.obcard i{
  font-style:normal;font-size:9.5px;color:var(--ink-faint);margin-top:5px;
  font-family:var(--mono);letter-spacing:0.072em;text-transform:uppercase;
  position:relative;z-index:1;
}

/* ---- 两条出口 ---- */
.obrow{
  display:flex;align-items:center;gap:13px;margin-top:10px;padding:15px 16px;
  border-radius:var(--r);border:var(--lw) solid var(--line-ghost);
  background:rgba(0,0,0,.18);cursor:pointer;
  transition:border-color .18s, transform .12s;
}
.obrow:active{transform:scale(.985);border-color:var(--acc)}
.obrow .obi{width:21px;height:21px;flex:none;color:var(--acc-text)}
.obrow .obi svg{width:100%;height:100%}
.obrow .obt{flex:1;min-width:0}
.obrow .obt b{display:block;font-size:14px;font-weight:var(--w7);color:var(--line);line-height:1.3}
.obrow .obt span{display:block;font-size:11.5px;color:var(--ink-faint);margin-top:4px;line-height:1.4}
.obrow .chev{opacity:.42;flex:none}

/* 「看全部」按钮和两条出口之间留一口气 */
.ob .btn.ghost.wide{margin-top:14px}

html[lang="en"] .ob .ttl{font-size:36px;line-height:1.08}
html[lang="en"] .ob .obsub{font-size:14px;line-height:1.6}
html[lang="en"] .obbubble{font-size:15.5px;line-height:1.48}
html[lang="en"] .obcard b{font-size:13.5px}


/* =====================================================================
   AF. PRO 角标不能压住图形
   ---------------------------------------------------------------------
   之前是 position:absolute; right:9px; top:9px —— 线寿命四张卡的示意图
   正好在顶部,弹跳那张的球就被角标盖住了。
   改成:角标进入卡片正常布局,和说明文字排在同一行的右端。
   它永远在文字旁边,不可能压到任何图形上。
   ===================================================================== */
.gopt{position:relative}
.gopt .prolock{
  position:static;margin-left:auto;align-self:center;
}
.gopt{display:flex;flex-direction:column;align-items:flex-start;gap:0}
.gopt .gp-ico{margin-bottom:10px}
.gopt b{display:block;width:100%}
.gopt .goptfoot{
  display:flex;align-items:center;gap:8px;width:100%;margin-top:4px;
}
.gopt .goptfoot i{flex:1;min-width:0}

/* 主题卡上的角标本来就不压图(图是整块背景),但也统一挪到右下角,
   免得和左上角的赛事名抢位置 */
.slamcard .prolock{top:auto;bottom:9px;right:9px}
.slamcard.on .prolock{bottom:auto;top:9px}

/* 引导页顶部:第一步标签 + 主题切换条并排 */
.ob .obtop{display:flex;align-items:center;gap:12px;margin-bottom:2px}
.ob .obtop .eyebrow{flex:none}
.ob .obtop .slamrow{flex:1;max-width:210px;margin-left:auto}
.ob .obtop .slamrow b{font-size:9.5px;padding:6px 3px}

/* =====================================================================
   AG. 质感修正
   ---------------------------------------------------------------------
   截图上「廉价」的具体来源,一条一条拆:

   1. 我上一轮把线寿命四张卡的图标弄没了。
      .gp-ico 是 display:block、只给了 height 没给 width,
      靠父级 align-items:stretch 撑满。我为了挪 PRO 角标加了
      align-items:flex-start,图标宽度塌成 0 —— 四个示意图全部消失。

   2. 字太细。深色底上白色 400 字重的中文,笔画一细就发虚。
      而且 10px 的说明文字 + .5 透明度的灰,在土色背景上几乎糊掉。

   3. 小字被拉开字距。letter-spacing:0.09em 对拉丁大写是对的,
      对中文是灾难 ——「点 我」两个字被硬生生拉开,像排版事故。

   4. 卡片底色是纯黑半透。压在红土上出来是脏褐色。
      深色卡片在暖色场地上应该压场地自己的深色,不是压黑。
   ===================================================================== */

/* ---- 1. 把图标救回来 ---- */
.gopt{align-items:stretch}
.gp-ico{display:block;width:100%;height:24px;margin-bottom:9px;opacity:.95}
.gopt .goptfoot{display:flex;align-items:center;gap:8px;width:100%;margin-top:5px}
.gopt .goptfoot i{flex:1;min-width:0}

/* ---- 2. 字重与对比:细字是廉价感的第一来源 ---- */
:root{
  /* 深色底上,.5 的灰在暖色场地上会糊掉。整体提一档。 */
  --ink-dim:rgba(255,255,255,.80);
  --ink-faint:rgba(255,255,255,.60);
}
body{font-weight:450}
p,.advice,.obsub,.petsay,.say,.empty,.emptybig .ebs,.invnote,.rulesbox p{
  font-weight:450;
}
/* 小字统一抬到 11.5px 以上 —— 10px 的说明文字在手机上就是看不清 */
.gopt i,.obrow .obt span,.subrow .srs,.credrow .crs,
.statcard .vsub,.log .ld,.petmood,.plan i,.fh{
  font-size:11.5px;font-weight:450;
}
.gopt b{font-size:14px;font-weight:var(--w7)}
.fine{font-size:11.5px;line-height:1.75;font-weight:450}
.sect h3,.obsect h3{font-size:13px;font-weight:var(--w8)}

/* 深色底上给关键文字一点点分离度,不是发光,是让笔画不糊在背景里 */
.ctop .ttl,.h1,.ob .ttl,.vital .big,.score{
  text-shadow:0 2px 10px rgba(0,0,0,.28);
}

/* ---- 3. 中文不要拉字距 ---- */
html[lang="zh-Hans"] .eyebrow,
html[lang="zh-Hans"] .obtap,
html[lang="zh-Hans"] .sect h3 .en,
html[lang="zh-Hans"] .obsect h3 .en,
html[lang="zh-Hans"] .grouphd,
html[lang="zh-Hans"] .statcard .k{
  letter-spacing:.05em;
}
/* 「点我」这种两个字的提示,拉开就成了排版事故 */
html[lang="zh-Hans"] .obtap{letter-spacing:.02em;font-size:11px;text-transform:none}

/* ---- 4. 卡片底色:压场地的深色,不压黑 ---- */
.obcard,.obrow,.obbubble{background:var(--fill-2)}
.obbubble{border-color:var(--line-ghost)}
.obbubble::after{background:var(--fill-2);border-color:var(--line-ghost)}

/* =====================================================================
   AH. 首屏热门卡:球拍要大,品牌名不许占一行
   ---------------------------------------------------------------------
   之前球拍 128px、下面型号一行、品牌又一行(还是宽字距大写)。
   结果是「一张卡里球拍只占一半,另一半全是字」。
   这一屏的任务是让人认出球拍并选一把,球拍就该是主体。
   ===================================================================== */
.obcard{padding:14px 10px 12px}
.obcard .obimg{height:158px}
.obcard .obimg img{max-height:158px;max-width:92%}
/* 型号和品牌合成一行:品牌小一号、淡一档,跟在型号后面 */
.obcard .obname{
  margin-top:10px;display:flex;flex-direction:column;gap:2px;
  position:relative;z-index:1;width:100%;
}
.obcard b{
  font-size:13.5px;font-weight:var(--w7);color:var(--line);line-height:1.25;
  letter-spacing:-.005em;margin-top:0;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.obcard i{
  font-style:normal;font-size:10.5px;color:var(--ink-faint);margin-top:0;
  font-family:var(--sans);letter-spacing:0.036em;text-transform:uppercase;font-weight:600;
}
html[lang="en"] .obcard b{font-size:14px}

/* 球拍在卡片里要有落影,不然像贴纸 */
.obcard .obimg img{filter:drop-shadow(0 10px 18px rgba(0,0,0,.5))}


/* =====================================================================
   AI. 主题切换条:保住大满贯的身份
   ---------------------------------------------------------------------
   上一轮为了塞得下,把标签换成了 Clay / Hard / Grass / Night ——
   宽度是解决了,但四大满贯的身份没了,变成了四种地面材质。
   改回赛事:用网球圈通行的缩写。RG / AO / WIM / USO 这四个代号,
   打球的人一眼就懂,而且比场地名更有身份感。
   下面那行小字标场地,两层信息都留住。
   ===================================================================== */
.slamrow{
  display:grid;grid-template-columns:repeat(4,1fr);
  border:var(--lw) solid var(--line-ghost);border-radius:var(--r-sm);
  overflow:hidden;width:100%;
}
.slamrow b{
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;
  font-family:var(--sans);font-size:12px;font-weight:var(--w8);letter-spacing:.04em;
  padding:7px 3px 6px;text-align:center;white-space:nowrap;
  color:var(--ink-dim);cursor:pointer;position:relative;
  border-right:var(--lw) solid var(--line-ghost);
  transition:background .2s,color .2s;
}
.slamrow b em{
  font-style:normal;font-size:8px;font-weight:600;letter-spacing:0.045em;
  text-transform:uppercase;opacity:.62;
}
.slamrow b:last-child{border-right:0}
.slamrow b.on{background:var(--line);color:var(--surface-deep)}
.slamrow b.on em{opacity:.7}
.ob .obtop .slamrow{max-width:236px}

/* =====================================================================
   AJ. 引导页对话框 —— 玻璃材质
   ---------------------------------------------------------------------
   之前是一个纯色方块 + 一个旋转 45° 的小方块当尾巴。
   小方块在球的左边露出来,看着像一块凭空浮着的深色积木,
   不像尾巴,更像渲染错误。

   改成玻璃:
     · 背景是低透明度的白 + 背后模糊,让场地纹理透上来
     · 顶边一道 1px 的高光,底边一道暗边 —— 这是玻璃厚度的来源,
       没有这两条就只是个半透明矩形
     · 外面一层柔和的落影,让它浮在场地之上
     · 不做三角尾巴。球和对话框重叠一小块,重叠本身就是连接关系,
       比一个几何小块干净得多
   ===================================================================== */
.obball{
  position:relative;display:flex;align-items:flex-end;
  margin:24px 0 8px;min-height:152px;cursor:pointer;
  -webkit-tap-highlight-color:transparent;
}

.obbubble{
  flex:1;position:relative;z-index:1;
  margin-bottom:20px;margin-right:-30px;padding:17px 40px 15px 19px;
  border-radius:18px;

  /* 玻璃本体 */
  background:linear-gradient(
    160deg,
    rgba(255,255,255,.16) 0%,
    rgba(255,255,255,.07) 42%,
    rgba(255,255,255,.04) 100%);
  -webkit-backdrop-filter:blur(14px) saturate(1.5);
          backdrop-filter:blur(14px) saturate(1.5);

  /* 厚度:顶边高光 + 底边暗边,内嵌两道 */
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.34),
    inset 0 -1px 0 rgba(0,0,0,.16),
    0 12px 30px -14px rgba(0,0,0,.6);
  border:1px solid rgba(255,255,255,.16);

  font-size:15.5px;line-height:1.5;font-weight:500;color:var(--line);
  text-shadow:0 1px 6px rgba(0,0,0,.32);
}
/* 三角尾巴不要了 —— 球压在对话框上,重叠就是连接 */
.obbubble::after{content:none}

/* 玻璃的边缘折射:右上角一道很淡的斜向亮带,只是暗示,不是装饰 */
.obbubble::before{
  content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none;
  background:linear-gradient(118deg,transparent 55%,rgba(255,255,255,.10) 72%,transparent 84%);
}

.obbubble .obtap{
  display:block;font-style:normal;margin-top:10px;
  font-family:var(--sans);font-size:11px;font-weight:600;letter-spacing:0.027em;
  color:var(--ink-faint);
}

/* 球压在对话框右端上面 */
.obb{
  flex:none;width:132px;height:132px;position:relative;z-index:3;
  animation:obBob 3.4s ease-in-out infinite;
}
.obb img{
  width:100%;height:100%;object-fit:contain;position:relative;z-index:2;
  filter:drop-shadow(0 8px 14px rgba(0,0,0,.42));
}
/* 落地影:椭圆,贴在球正下方。球弹起来时影子变小变淡。 */
.obb::after{
  content:"";position:absolute;left:50%;bottom:-4px;transform:translateX(-50%);
  width:86px;height:14px;border-radius:50%;
  background:radial-gradient(ellipse at 50% 50%,rgba(0,0,0,.40),transparent 72%);
  animation:obShadow 3.4s ease-in-out infinite;z-index:1;
}

/* =====================================================================
   AK. 热门卡:球拍是主体,字退到一行
   ---------------------------------------------------------------------
   按标注的比例重排。之前球拍 150px 而卡片里还留着两行字(型号一行、
   品牌又一行,还是宽字距大写),结果球拍只占卡片一半。

   这一屏的任务是让人认出球拍并选一把 —— 球拍就该占绝大部分面积。
   所以:球拍高度按卡片宽度算(不是写死 px),型号和品牌压成一行。
   ===================================================================== */
.obcard{padding:10px 8px 11px}
/* 球拍高度跟着卡宽走:卡宽约 175px 时给 208px 高,比例 1.19 */
.obcard .obimg{height:auto;aspect-ratio:.84;width:100%}
.obcard .obimg img{max-height:100%;max-width:100%;height:100%;object-fit:contain}
/* 型号 + 品牌一行:品牌小一号淡一档,跟在后面 */
.obcard .obname{
  margin-top:7px;display:flex;align-items:baseline;justify-content:center;
  gap:7px;flex-wrap:nowrap;width:100%;padding:0 2px;
}
.obcard b{
  font-size:14px;font-weight:600;color:var(--line);line-height:1.2;
  letter-spacing:-.008em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  flex:0 1 auto;min-width:0;
}
.obcard i{
  font-style:normal;font-size:9.5px;letter-spacing:0.045em;font-weight:600;
  color:var(--ink-faint);white-space:nowrap;flex:none;margin-top:0;
}
/* 型号太长时品牌先让位 */
.obcard i{max-width:46%;overflow:hidden;text-overflow:ellipsis}
html[lang="en"] .obcard b{font-size:14.5px}

/* =====================================================================
   AL. 发球打字 + 拖拽领养
   ===================================================================== */

/* 发球轨迹:虚线,落地淡掉 */
.obball .servetrail{
  position:absolute;inset:0;width:100%;height:100%;
  pointer-events:none;z-index:2;overflow:visible;
  transition:opacity .4s ease;
}
.obball .servetrail path{stroke:rgba(217,232,79,.42)}
/* 飞行中球不要再跟着呼吸,两个 transform 会打架 —— JS 里已经关了 animation,
   这里保证过渡不会插进来 */
.obball .obb{transition:none}

/* 拖拽中的卡片:自己浮起来,别被邻居盖住 */
.obcard.dragging{z-index:9;border-color:var(--rk)}
.obcard.dragging .obimg img{filter:drop-shadow(0 20px 30px rgba(0,0,0,.62))}
.obcard.dragover{border-color:var(--acc)}
.obcard.dragover::after{
  content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none;
  box-shadow:inset 0 0 0 2px var(--acc);
}
/* 球拍图要能被指针直接抓到,不要被父级吞掉手势 */
.obcard .obimg img{touch-action:none;-webkit-user-drag:none;user-select:none}


/* =====================================================================
   AM. 真机修正 —— 按 iPhone 上的截图逐条改
   ---------------------------------------------------------------------
   桌面预览里全对、装到手机上全错的几件事。共同点是:它们都只在
   iOS 的 standalone 模式下才成立,而我之前只在 jsdom 和桌面里看过。
   ===================================================================== */

/* ---------------------------------------------------------------------
   1. 标题被"拉伸"的真正原因:iOS 的文字自动放大
   ---------------------------------------------------------------------
   Safari 在 iPhone 上默认开着 text autosizing:它会挑一些块级容器
   把里面的字号往上顶,顶多少取决于容器宽度和它猜的"这段是正文吗"。
   结果就是同一套排版,标题被放大了、旁边的小字没有 —— 看上去就是
   比例失调、字被拉长。桌面 Safari 不做这件事,所以预览里永远正常。

   关掉它。一行,而且必须在最前面。
   --------------------------------------------------------------------- */
html{-webkit-text-size-adjust:100%;text-size-adjust:100%}

/* ---------------------------------------------------------------------
   2. 顶部空出一大块:安全区被算了两次
   ---------------------------------------------------------------------
   .phone 加了一次 padding-top:env(safe-area-inset-top),
   .screen 又加了一次 calc(52px + env(safe-area-inset-top))。
   灵动岛机型上 inset 是 59px,于是顶部一共空了 59+59+52 = 170px。

   安全区只该算一次,而且该算在 .screen 上 —— 让球场的质感一直铺到
   屏幕最上沿,内容再从状态栏下面开始。
   --------------------------------------------------------------------- */
/* 安全区走变量,不直接写 env():这样自动化测试能把它设成任意值来验版面。
   之前这两条只在真机上才有值,桌面预览永远是 0 —— 于是"顶部空 170px"
   这种错在我这边永远看不见。 */
:root{--sat:env(safe-area-inset-top,0px); --sab:env(safe-area-inset-bottom,0px)}
html.native .phone{padding-top:0}
html.native .screen{padding-top:calc(var(--sat) + 14px)}

/* ---------------------------------------------------------------------
   3. 滚动的内容直接撞上状态栏
   ---------------------------------------------------------------------
   状态栏是 black-translucent,内容会从它底下穿过去。截图里
   「落点精准,控制扎实」正好压在 15:55 上,两行字叠在一起。

   做一层渐进模糊的顶帘:越靠上越糊越暗,到状态栏底沿完全透明。
   不是一条实色横杠 —— 那会切断球场质感;是让内容"化"进去。
   --------------------------------------------------------------------- */
.view::before{
  content:"";position:absolute;left:0;right:0;top:0;z-index:25;pointer-events:none;
  height:calc(var(--sat) + 10px);
  background:linear-gradient(180deg,rgba(0,0,0,.42),rgba(0,0,0,.16) 62%,transparent);
  -webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);
  -webkit-mask-image:linear-gradient(180deg,#000 52%,rgba(0,0,0,.4) 80%,transparent);
          mask-image:linear-gradient(180deg,#000 52%,rgba(0,0,0,.4) 80%,transparent);
}
/* 浏览器预览里没有状态栏,这层就没必要 */
html:not(.native) .view::before{display:none}

/* ---------------------------------------------------------------------
   4. 底部标签栏:图标和文字掉到背板外面
   ---------------------------------------------------------------------
   height 写死 86px,padding-bottom 又是 24px + 安全区(34px)= 58px,
   留给图标和文字的只剩 16px。于是它们整个溢出到背板下面,
   压在球拍图上 —— 截图里那条白线以下全是穿帮。

   高度交给内容算,安全区加在 padding 上。背板同时补一层模糊,
   不然半透明底色在浅色球拍上等于没有。
   --------------------------------------------------------------------- */
.tabbar{
  height:auto;min-height:0;align-items:center;
  padding:11px 14px calc(11px + var(--sab));
  -webkit-backdrop-filter:blur(18px) saturate(1.3);backdrop-filter:blur(18px) saturate(1.3);
}
html.native .tabbar{padding-bottom:max(11px,var(--sab))}
.tab{gap:3px}
/* 滚动内容的下边距跟着标签栏的真实高度走 */
.screen{padding-bottom:calc(96px + var(--sab))}

/* =====================================================================
   AN. 热门卡重做 —— 夜场下不能是一块黑
   ---------------------------------------------------------------------
   美网主题下卡片底色是 rgba(3,10,32,.62),而 Speed MP 这类拍本身
   就是黑白涂装 —— 深色拍压在深色卡上,拍框直接消失。截图里那两张
   卡就是两块黑。这不是配色问题,是**照明**问题。

   实体店拍照怎么解决:球拍后面放一盏灯。所以这里也放一盏 ——
   卡片背后一团柔光,球拍逆着它,不管拍子本身是黑是白都能读出轮廓。

   卡片本身按玻璃做,和对话框同一套语言:
     · 顶边 1px 高光 + 底边 1px 暗边 = 玻璃的厚度
     · 斜向的渐变本体,上亮下暗,光从左上来
     · 拍柄底下一道接触影,球拍是"立"在卡里的,不是贴上去的
     · 涂装色只做很淡的一层,用来区分六张卡,不喧宾夺主
   ===================================================================== */
.obcard{
  -webkit-appearance:none;appearance:none;
  border-radius:var(--r);
  border:1px solid var(--card-bd,rgba(255,255,255,.14));
  background:
    linear-gradient(168deg,
      rgba(255,255,255,.13) 0%,
      rgba(255,255,255,.05) 38%,
      rgba(0,0,0,.10) 72%,
      rgba(0,0,0,.22) 100%),
    var(--fill-2);
  -webkit-backdrop-filter:blur(8px) saturate(1.25);backdrop-filter:blur(8px) saturate(1.25);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.26),
    inset 0 -1px 0 rgba(0,0,0,.22),
    0 14px 30px -18px rgba(0,0,0,.75);
  transition:transform .16s var(--ease), box-shadow .22s ease, border-color .18s ease;
}

/* 背光:球拍后面那盏灯。中性白光 + 一点点涂装色,
   白光负责让深色拍读得出来,涂装色负责让六张卡不一样。 */
.obcard::before{
  content:"";position:absolute;left:50%;top:6%;
  width:132%;height:62%;transform:translateX(-50%);
  pointer-events:none;z-index:0;
  background:
    radial-gradient(46% 50% at 50% 50%, rgba(255,255,255,.30), transparent 70%),
    radial-gradient(62% 64% at 50% 52%, var(--rk), transparent 72%);
  opacity:.52;
  transition:opacity .22s ease;
}
/* 夜场把灯开大一档 —— 那一套底色最深 */
.slam-uso .obcard::before{opacity:.66}
.slam-wb  .obcard::before{opacity:.40}   /* 草地本来就亮,再加会糊 */

/* 接触影:拍柄落在卡上的那一小片暗。没有这个,球拍就是贴纸。 */
.obcard .obimg::after{
  content:"";position:absolute;left:50%;bottom:2px;transform:translateX(-50%);
  width:52%;height:12px;border-radius:50%;pointer-events:none;
  background:radial-gradient(ellipse at 50% 50%,rgba(0,0,0,.42),transparent 72%);
}
.obcard .obimg{position:relative}
.obcard .obimg img{
  position:relative;z-index:1;
  filter:drop-shadow(0 12px 16px rgba(0,0,0,.42))
         drop-shadow(0 1px 0 rgba(255,255,255,.10));
}

/* 按下去:卡片被按进场地,同时背后那盏灯变亮 —— 光跟着手指走 */
.obcard:active{
  transform:scale(.968);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.18),
    inset 0 -1px 0 rgba(0,0,0,.28),
    0 4px 12px -10px rgba(0,0,0,.8);
  border-color:var(--rk);
}
.obcard:active::before{opacity:.85}

/* 两条出口和对话框跟卡片同一套材质,不然三种质感在一屏里打架 */
.obrow{
  border-radius:var(--r);
  border:1px solid rgba(255,255,255,.13);
  background:linear-gradient(168deg,rgba(255,255,255,.10),rgba(255,255,255,.03) 60%,rgba(0,0,0,.14)),
             var(--fill-2);
  -webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.20),0 10px 22px -16px rgba(0,0,0,.7);
}
.obrow:active{transform:scale(.985);border-color:var(--acc)}

/* 入场:四张卡依次落位。只在这一屏,而且很短 —— 引导页要有一点
   "东西刚摆好"的感觉,但不能让人等。 */
@keyframes obCardIn{from{opacity:0;transform:translateY(14px) scale(.97)}to{opacity:1;transform:none}}
.obgrid .obcard{animation:obCardIn .42s var(--ease-out) both}
.obgrid .obcard:nth-child(1){animation-delay:.04s}
.obgrid .obcard:nth-child(2){animation-delay:.10s}
.obgrid .obcard:nth-child(3){animation-delay:.16s}
.obgrid .obcard:nth-child(4){animation-delay:.22s}
@media (prefers-reduced-motion:reduce){
  .obgrid .obcard{animation:none}
}
/* 拖起来的时候把入场动画让开,不然 transform 会被覆盖 */
.obcard.dragging{animation:none!important}

/* =====================================================================
   AO. 球说的话要能兑现 · 碰一碰 · 天气 · 日程
   ===================================================================== */

/* ---- 气泡里的那颗按钮 ----
   球说"我帮你问几个问题",就得能真的问。这颗按钮跟着台词换,
   所以它必须一眼看出来是能点的 —— 用主色描边,不是又一行灰字。 */
.obact{
  display:inline-flex;align-items:center;gap:6px;margin-top:11px;
  font-size:12px;font-weight:var(--w7);letter-spacing:.01em;
  color:var(--acc-ink);background:var(--acc);
  padding:7px 13px;border-radius:999px;
  box-shadow:0 4px 14px -6px rgba(0,0,0,.6);
  transition:transform .14s var(--ease),filter .14s;
}
.obact::after{content:"→";font-weight:var(--w7);opacity:.75}
.obact:active{transform:scale(.94);filter:brightness(.94)}

/* 长按提示:一行,很淡。发现得了就发现,发现不了也不碍事。 */
.obtip{
  text-align:center;font-size:11px;color:var(--ink-faint);
  margin:-2px 0 14px;letter-spacing:.02em;
}

/* ---- 碰一碰 ---- */
/* 待机:球轻轻脉动,像在等 */
@keyframes bumpReady{
  0%,100%{transform:scale(1)}
  50%    {transform:scale(1.07)}
}
.obball.bumparmed .obb{animation:bumpReady 1.05s ease-in-out infinite}
.obball.bumparmed .obb::after{opacity:.5}
/* 待机时球外面一圈涟漪,告诉人"现在碰" */
.obball.bumparmed .obb::before{
  content:"";position:absolute;inset:-14px;border-radius:50%;
  border:2px solid var(--acc);opacity:0;
  animation:bumpRipple 1.05s ease-out infinite;pointer-events:none;
}
@keyframes bumpRipple{
  0%  {transform:scale(.82);opacity:.65}
  100%{transform:scale(1.28);opacity:0}
}
.bumpchip{
  position:absolute;left:0;bottom:-6px;z-index:5;
  -webkit-appearance:none;appearance:none;cursor:pointer;
  font-family:inherit;font-size:11px;font-weight:600;letter-spacing:.02em;
  color:var(--ink-dim);background:rgba(0,0,0,.34);
  border:1px solid var(--line-ghost);border-radius:999px;padding:6px 12px;
}
.bumpchip.manual{color:var(--acc-ink);background:var(--acc);border-color:transparent}
/* 对打中:球飞出屏幕要能被裁掉,不然会撑出横向滚动 */
.obball.bumping{overflow:visible}
.obball.bumping .obb{transition:opacity .08s linear}

/* ---- 天气 ---- */
.wxoff{
  display:flex;align-items:center;gap:13px;margin-bottom:16px;padding:15px 16px;
  border-radius:var(--r);border:var(--lw) solid var(--line-ghost);
  background:linear-gradient(168deg,rgba(255,255,255,.09),rgba(0,0,0,.12)),var(--fill-2);
  cursor:pointer;transition:transform .12s var(--ease),border-color .18s;
}
.wxoff:active{transform:scale(.985);border-color:var(--acc)}
.wxoff .wxi{width:21px;height:21px;flex:none;color:var(--acc-text)}
.wxoff .wxt{flex:1;min-width:0}
.wxoff .wxt b{display:block;font-size:14px;font-weight:var(--w7);color:var(--line)}
.wxoff .wxt span{display:block;font-size:11.5px;color:var(--ink-dim);margin-top:3px;line-height:1.5}
.wxoff .chev{color:var(--ink-faint);flex:none}

.wxcard{
  position:relative;overflow:hidden;margin-bottom:16px;padding:16px 16px 13px;
  border-radius:var(--r);border:var(--lw) solid var(--line-ghost);
  background:linear-gradient(168deg,rgba(255,255,255,.11),rgba(255,255,255,.03) 46%,rgba(0,0,0,.18)),var(--fill-2);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.22),0 12px 26px -18px rgba(0,0,0,.7);
  cursor:pointer;
}
/* 结论的颜色就是判断本身:绿=去打,黄=看情况,红=算了。
   不用天气图标 —— 图标说的是"天气",这里说的是"能不能打"。 */
.wxcard::before{
  content:"";position:absolute;left:0;top:0;bottom:0;width:3px;
  background:var(--wxc,var(--ok));
}
.wxcard.wx-go{--wxc:var(--ok)} .wxcard.wx-ok{--wxc:var(--warn)} .wxcard.wx-no{--wxc:var(--dead)}
.wxhd{display:flex;align-items:flex-start;gap:12px}
.wxv{font-family:var(--disp);font-weight:var(--disp-w);font-size:23px;line-height:1.15;
  letter-spacing:var(--disp-ls);color:var(--line)}
.wxwhy{font-size:12px;color:var(--ink-dim);line-height:1.55;margin-top:6px}
.wxscore{margin-left:auto;text-align:right;flex:none}
.wxscore b{display:block;font-family:var(--led);font-size:30px;font-weight:var(--w8);
  line-height:1;color:var(--wxc,var(--ok));font-variant-numeric:tabular-nums}
.wxscore i{display:block;font-style:normal;font-size:9px;letter-spacing:0.063em;
  text-transform:uppercase;color:var(--ink-faint);margin-top:4px}
.wxrow{display:flex;align-items:baseline;gap:10px;margin-top:12px;
  padding-top:10px;border-top:1px solid var(--line-ghost)}
.wxnow{font-size:11.5px;color:var(--ink-dim)}
.wxcity{margin-left:auto;font-size:10.5px;color:var(--ink-faint);letter-spacing:0.027em}
.wxdays{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin-top:11px}
.wxd{display:flex;align-items:baseline;gap:6px;padding:8px 9px;border-radius:var(--r-sm);
  background:rgba(0,0,0,.18);border-left:2px solid var(--wxc,var(--ok))}
.wxd.wx-go{--wxc:var(--ok)} .wxd.wx-ok{--wxc:var(--warn)} .wxd.wx-no{--wxc:var(--dead)}
.wxd i{font-style:normal;font-size:10.5px;color:var(--ink-faint)}
.wxd b{font-size:13px;font-weight:var(--w7);color:var(--line)}
.wxd span{margin-left:auto;font-size:10.5px;color:var(--wxc,var(--ok));font-weight:var(--w7);
  font-variant-numeric:tabular-nums}

.wxcur{font-size:12px;color:var(--ink-dim);margin:-4px 0 12px}
.wxsearch{margin:10px 0}
.wxres{max-height:180px;overflow-y:auto;margin-top:8px}
.wxopt{display:flex;align-items:baseline;gap:9px;padding:11px 12px;cursor:pointer;
  border-bottom:1px solid var(--line-ghost)}
.wxopt:active{background:rgba(255,255,255,.06)}
.wxopt b{font-size:14px;font-weight:600;color:var(--line)}
.wxopt span{font-size:11px;color:var(--ink-faint)}

/* ---- 日程 ---- */
.planrow{
  display:flex;align-items:center;gap:11px;padding:12px 13px;
  border-bottom:1px solid var(--line-ghost);
}
.planrow:last-of-type{border-bottom:0}
.planrow.ask{background:rgba(255,255,255,.05);border-radius:var(--r-sm);margin-bottom:6px}
.pdate{flex:none;width:60px;font-family:var(--led);font-size:12.5px;font-weight:var(--w7);
  color:var(--acc-text);line-height:1.3;font-variant-numeric:tabular-nums}
.planrow .pi{flex:1;min-width:0}
.planrow .pi b{display:block;font-size:13.5px;font-weight:600;color:var(--line);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.planrow .pi span{display:block;font-size:11.5px;color:var(--ink-faint);margin-top:3px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.pdel{
  flex:none;-webkit-appearance:none;appearance:none;cursor:pointer;
  width:26px;height:26px;border-radius:50%;border:1px solid var(--line-ghost);
  background:transparent;color:var(--ink-faint);font-size:15px;line-height:1;
  font-family:inherit;
}
.pdel:active{background:rgba(255,255,255,.08)}
.planact{display:flex;flex-direction:column;gap:8px;padding:12px 13px 4px}
.prow2{display:grid;grid-template-columns:1fr 1fr;gap:9px;margin-bottom:9px}
.prow2 .field{margin:0}
/* iOS 的原生日期/时间控件默认是浅色的,在深色卡上会变成一块白 */
input[type="date"].field,input[type="time"].field,select.field{
  -webkit-appearance:none;appearance:none;color-scheme:dark;
  font-family:inherit;font-size:14px;
}

/* =====================================================================
   AP. 看过真机截图之后的一轮修正
   ===================================================================== */

/* ---- 1. 底部标签栏是透的 ----
   截图里球拍的型号名直接从标签栏底下透出来,和图标叠在一起。
   --tabbg 那条渐变在顶端是完全透明的,而 backdrop-filter 只会模糊、
   不会遮挡 —— 模糊之后的球拍还是球拍。

   补一层实的底:上沿柔和淡出(不切断球场质感),往下迅速变实。
   顶边那条硬白线也换掉 —— 一条 1px 的实线横在屏幕上像裁切错误,
   改成两侧淡出的中间亮的分隔线。 */
.tabbar{border-top:0}
.tabbar::after{
  content:"";position:absolute;inset:0;z-index:-1;pointer-events:none;
  background:linear-gradient(180deg,
    rgba(0,0,0,0) 0%, rgba(0,0,0,.42) 26%, rgba(0,0,0,.72) 58%, rgba(0,0,0,.86) 100%);
}
.tabbar::before{
  content:"";position:absolute;left:0;right:0;top:0;height:1px;pointer-events:none;
  background:linear-gradient(90deg,transparent,var(--line-ghost) 22%,var(--line-ghost) 78%,transparent);
}

/* ---- 2. 中文大标题在句子中间断行 ----
   「先认识一下你的拍」被断成「先认识一 / 下你的拍」。
   中文没有词边界,浏览器就是逐字断的。模板本来就分成了两截
   (前半 + <em> 后半),把 <em> 变成不可拆的整块,断点自然落在两截之间。 */
.ob .ttl em,.ctop .ttl em,.h1 em{display:inline-block}
/* 美网压缩体在中文下并不压缩(中文走苹方),46px 八个字必然放不下 */
html[lang="zh-Hans"] .slam-uso .ob .ttl{font-size:38px}
html[lang="zh-Hans"] .slam-uso .ctop .ttl{font-size:44px}
html[lang="zh-Hans"] .slam-ao  .ob .ttl{font-size:34px}

/* ---- 3. 日程里的按钮没样式 ----
   .rbtn 的样式写在 .remind .rbtn 上,日程行里的同名按钮落不到,
   于是渲染成 iOS 的原生白方块。 */
.planrow .rbtn{
  flex:none;-webkit-appearance:none;appearance:none;cursor:pointer;font-family:inherit;
  border:0;border-radius:999px;background:var(--acc);color:var(--acc-ink);
  font-size:11.5px;font-weight:var(--w7);letter-spacing:.02em;padding:7px 13px;
}
/* 「加日历」是次要动作,不该和「打了」抢同一个高亮 */
.planrow:not(.ask) .rbtn{
  background:transparent;color:var(--ink-dim);
  border:1px solid var(--line-ghost);
}
.planrow .rbtn:active{transform:scale(.94)}

/* ---- 4. 「3d / 7d」这类分段控件被挤到溢出 ---- */
.seg2{flex:none}
.seg2 button{white-space:nowrap;padding-left:11px;padding-right:11px}
.setrow{gap:10px}
.setrow .sn{min-width:0}

/* 待机时把「点我」和长按提示让开 —— 那两句这会儿是过时信息,
   而且芯片会压在它们上面。 */
.obball.bumparmed .obtap{opacity:0}
.obball.bumparmed ~ .obtip{opacity:0}
.bumpchip{bottom:-10px;box-shadow:0 6px 16px -8px rgba(0,0,0,.7)}
.obtip{transition:opacity .2s}

/* =====================================================================
   AQ. 每张卡自己的配色 + 拍框高光 + 3D 倾斜
   ---------------------------------------------------------------------
   卡片底色不再是统一的半透明黑,而是**从这把拍自己的涂装色算出来的**。
   算法在 ui.js 的 tint():色相原样保留(那是身份),饱和度给下限、
   亮度压进可读区间 —— 因为库里 115 个 accent 有 25 个是灰的、11 个是黑的,
   原样铺开就是一堆黑卡,跟「每张卡有自己的颜色」正好相反。

   三层结构,顺序不能换:
     .obbg   配色渐变 + 顶部聚光 + 底部压暗      (裁在圆角里)
     .obimg  球拍 + 拍框高光                     (视差中层)
     .obname 型号                                (视差前层)
   ===================================================================== */

/* ---- 卡片本体 ----
   配色是 PRO 的选项,所以所有上色的规则都挂在 .tinted 下面。
   不选它的时候走上面 AN 段那套统一的深色玻璃卡,一行都不用改。 */
.obcard{overflow:hidden;will-change:transform}
.tinted .obcard{
  background:transparent;                 /* 底色交给 .obbg,这里必须让开 */
  border-color:rgba(255,255,255,.20);
}
.obbg{display:none}
.tinted .obbg{display:block}
.obbg{
  position:absolute;inset:0;z-index:0;pointer-events:none;border-radius:inherit;
  /* 为什么之前会看到一个圆:
     径向渐变的**圆心在卡片里面**,而且颜色和周围差得大,于是那圈渐变的
     收尾就成了一条看得见的边 —— 读起来是一个贴上去的光斑,不是照明。

     解法不是把它调淡(调淡只是变成一个淡的圆),是**把圆心挪到卡片外面**。
     圆心在上边界之外,卡片里只落得到这个圆的下半部分,没有任何一处是
     "收尾",看到的就只是一层从上往下的光。 */
  background:
    radial-gradient(150% 96% at var(--lx,50%) var(--ly,-16%), rgba(255,255,255,.13), rgba(255,255,255,0) 62%),
    radial-gradient(132% 82% at 50% -22%, var(--c-glow), rgba(0,0,0,0) 64%),
    linear-gradient(176deg, var(--c-hi) 0%, var(--c-lo) 54%, var(--c-deep) 100%);
}
/* 玻璃厚度:顶边高光、底边暗边。配色再重,这两条都要在,否则就是一块色板 */
.obcard{
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.30),
    inset 0 -1px 0 rgba(0,0,0,.30),
    0 16px 34px -20px rgba(0,0,0,.8);
}
/* 球拍背后的背光同样不能是一个圆 —— 图二里那个突兀的圆就是它。
   改成圆心在卡片上沿之外的椭圆:卡片里只有它的下半部分,没有收尾的边,
   但拍头那一带该有的亮度一点没少,深色拍照样读得出轮廓。 */
.obcard::before{
  left:50%;top:-34%;height:100%;width:152%;transform:translateX(-50%);
  background:radial-gradient(50% 50% at 50% 50%, rgba(255,255,255,.24), rgba(255,255,255,0) 70%);
  opacity:.92;z-index:1;
}
.slam-uso .obcard::before{opacity:1}
.slam-wb  .obcard::before{opacity:.72}

.obcard .obimg{z-index:2;will-change:transform}
.obcard .obname{z-index:3;will-change:transform}
/* 型号压在深色的底部渐变上,任何配色下都读得住 */
.obcard .obname{text-shadow:0 1px 8px rgba(0,0,0,.45)}

/* 按下去不再单独缩放 —— 缩放已经写进倾斜的那一串 transform 里了,
   两边都写会互相覆盖。这里只留光的变化。 */
.obcard:active{transform:none}
.obcard:active .obbg{filter:brightness(1.12)}

/* ---- 拍框高光划过 ----
   关键在 mask:用球拍图自己当遮罩,所以那道光只在拍框上走,
   不会溢到卡片上。不额外加任何资源 —— 遮罩就是已经加载好的那张图。 */
.glint{
  position:absolute;inset:0;pointer-events:none;z-index:4;overflow:hidden;
  /* mask-image 写在行内(见 ui.js 的 glintEl:自定义属性里的 url()
     会按样式表位置解析,404)。这里只管尺寸和位置。 */
  -webkit-mask-size:contain;mask-size:contain;
  -webkit-mask-position:center;mask-position:center;
  -webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;
}
.glint i{
  position:absolute;top:-32%;left:-62%;width:44%;height:168%;
  transform:rotate(16deg);filter:blur(2px);
  background:linear-gradient(90deg,transparent,rgba(255,255,255,.9),transparent);
}
/* 图片本身要能被遮罩层准确盖住:两边同一个盒子、同一种 contain */
.obcard .obimg{position:relative}

/* ---------------------------------------------------------------------
   「减弱动态效果」到底该关掉什么
   ---------------------------------------------------------------------
   之前我把所有动效一刀切了 —— 开了这个开关的人,发球打字没有、拍框高光
   没有、流光边框也不动。可这个系统开关针对的是**大幅位移和缩放**
   (前庭敏感的人会晕的那类),不是所有动画。

   苹果自己的做法就是分开的:大位移换成淡入淡出,细小的材质变化照常。

   所以现在:
     关掉  球飞过整屏(大位移)、卡片整块入场
     保留  拍框上一道高光、边框上一束慢慢转的光(局部、小、不位移)
   --------------------------------------------------------------------- */

/* ---- 库列表:同一套配色,横条不做倾斜 ----
   列表是要滚的,倾斜手势会和滚动打架。所以这里只留配色和高光。 */
.libcard{position:relative;overflow:hidden}
.tinted .libcard{
  background:
    radial-gradient(130% 160% at -8% 50%, var(--c-hi), rgba(0,0,0,0) 68%),
    linear-gradient(104deg, var(--c-lo) 0%, var(--c-deep) 84%);
  border-color:rgba(255,255,255,.16);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.20),0 10px 22px -16px rgba(0,0,0,.7);
}
.libcard .thumb{position:relative}
.libcard .lb,.libcard .lm{text-shadow:0 1px 6px rgba(0,0,0,.4)}

/* ---- 首页主拍:同一套配色 ---- */
.tinted .hero.tinted{
  background:
    radial-gradient(128% 84% at 50% -22%, var(--c-hi), rgba(0,0,0,0) 66%),
    linear-gradient(176deg, var(--c-lo) 0%, var(--c-deep) 100%);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.26),0 20px 44px -26px rgba(0,0,0,.8);
}

/* ---- 卡片和场地撞色时的分界 ----
   Yonex EZONE 那一系是蓝的,美网夜场也是蓝的 —— 卡片直接糊进背景。
   同理红土上的 Clash、草地上的 Blade。

   不能靠改色相解决:那就不是「这把拍自己的颜色」了。
   靠**边界**解决:一圈外描边 + 更深的落影。不管卡片什么颜色,
   它和场地之间永远有一条暗线和一层影,轮廓就永远在。 */
.obcard{
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.34),
    inset 0 -1px 0 rgba(0,0,0,.34),
    0 0 0 1.5px rgba(0,0,0,.45),
    0 20px 40px -22px rgba(0,0,0,.9);
  border-color:rgba(255,255,255,.22);
}
.obcard:active{
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.24),
    0 0 0 1px rgba(0,0,0,.34),
    0 6px 14px -10px rgba(0,0,0,.9);
}
.tinted .libcard{
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.22),
    0 0 0 1px rgba(0,0,0,.30),
    0 12px 24px -16px rgba(0,0,0,.8);
}
.tinted .hero.tinted{
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.28),
    0 0 0 1px rgba(0,0,0,.32),
    0 24px 48px -26px rgba(0,0,0,.85);
}

/* ---- 「素色 / 涂装色」的两张预览卡 ----
   和线寿命那四张一个模子,所以不用再教一次怎么用。
   预览里画的就是两张缩到 1/4 的卡片,选之前就看得见差别。 */
.tintprev{
  display:flex;gap:6px;height:40px;width:100%;align-items:stretch;
}
.tintprev i{
  flex:1;border-radius:4px;font-style:normal;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.22),0 0 0 1px rgba(0,0,0,.28);
}
.tp-plain i{background:linear-gradient(168deg,rgba(255,255,255,.13),rgba(0,0,0,.24))}
.tp-plain i+i{background:linear-gradient(168deg,rgba(255,255,255,.13),rgba(0,0,0,.24))}
/* 涂装色的预览。前两版都错在同一个地方:
     第一版 S39/L44 —— 中饱和 × 中亮度,浊色区,发脏
     第二版直接把饱和拉满 —— 变成两块糖果色,俗
   参考图(Mindful Palettes #245)的结构不是"饱和度高低"的问题,是**跨度**:
   #EFF0F0 → #EEDD55 → #E9AD17,一头几乎是白的,一头压到很深,
   艳的那一段只占中间一小截。看着高级是因为**大部分面积都不艳**。
   所以这两块也照这个来:很浅的头(78% 面积以上都是浅的)→ 一段中间色 → 深尾。 */
.tp-paint i:first-child{
  background:linear-gradient(178deg,
    #EFF0F0 0%, #F3EBB4 30%, #EEDD55 62%, #B98F12 100%)}
.tp-paint i+i{
  background:linear-gradient(178deg,
    #EDF2F8 0%, #9CC0E6 26%, #095BAF 60%, #062A78 100%)}

/* 涂装色关掉时,卡片走回统一深色 —— 这几条要能盖住 AQ 段 */
.obcard:not(.dragging){transition:transform .16s var(--ease), box-shadow .22s ease, border-color .18s ease}

/* =====================================================================
   AS. 按真机反馈重做:玻璃 + 流光边框 + 去掉 3D 倾斜
   ===================================================================== */

/* ---------------------------------------------------------------------
   1. 卡片改成磨砂玻璃,颜色只是一层色纱
   ---------------------------------------------------------------------
   上一版把涂装色做成了**不透明填充**,所以每张卡是一块色板,从场地上
   整块跳出来 —— 降饱和度只能让色板变淡,改不掉"这是一块贴上去的色板"
   这件事本身。

   这一版换了性质:卡片本体是磨砂玻璃(backdrop-filter 把球场糊在后面),
   涂装色只是很淡地染一下。球场的土纹、草纹、夜场颗粒都透得上来,
   卡片是"浮在场地上的一块玻璃",不是"盖在场地上的一块色纸"。

   这也是为什么 tint() 现在返回的是 hsla 而不是 hsl。
   --------------------------------------------------------------------- */
.tinted .obcard{
  background:rgba(255,255,255,.04);
  /* 关键是 saturate(.35):**先把背后的球场去色**,再上这把拍的颜色。
     不去色的话,红土那一片橙会直接透穿玻璃,四张卡在红土上全是暖褐色,
     分不出哪张是蓝拍哪张是黄拍 —— 玻璃有了,身份没了。
     苹果那套材质就是这么做的:模糊 + 降饱和 + 压亮度,得到一块中性的
     毛玻璃,颜色再单独染上去。这样底下的纹理还在(还是玻璃),
     但颜色是卡片自己的。 */
  -webkit-backdrop-filter:blur(22px) saturate(.55) brightness(1.02);
          backdrop-filter:blur(22px) saturate(.55) brightness(1.02);
  border-color:rgba(255,255,255,.20);
  /* 玻璃的三条边:顶边一道亮高光(厚度)、内圈一圈很淡的白(折射)、
     外圈一条暗线(和场地分开)。少了顶边那道,半透明的东西就只是"半透明",
     不会被读成玻璃。 */
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.55),
    inset 0 0 0 1px rgba(255,255,255,.10),
    inset 0 -1px 0 rgba(0,0,0,.28),
    0 0 0 1px rgba(0,0,0,.26),
    0 18px 36px -22px rgba(0,0,0,.72);
}
/* 参考图里那几套都是**竖直的三段渐变**:浅头 → 艳中 → 深尾。
   所以这里也是竖的,不再是斜的 —— 斜渐变会让三段的分界跟着卡片比例跑,
   宽一点窄一点观感就不一样。 */
.tinted .obbg{
  background:
    radial-gradient(150% 92% at 50% -26%, var(--c-glow), rgba(0,0,0,0) 60%),
    linear-gradient(180deg, var(--c-hi) 0%, var(--c-lo) 46%, var(--c-deep) 100%);
}
/* 三段渐变的浅头本身就在照明,再叠一层白只会把颜色洗成雾。
   涂装色模式下把那层白基本撤掉,只留一点点。 */
.tinted .obcard::before{opacity:.18}
.tinted.slam-uso .obcard::before{opacity:.24}
.tinted.slam-wb  .obcard::before{opacity:.12}

.tinted .libcard{
  /* 横条是横着走:浅头在左(缩略图那边),深尾在右(文字那边) */
  background:
    linear-gradient(96deg, var(--c-hi) 0%, var(--c-lo) 38%, var(--c-deep) 100%),
    rgba(255,255,255,.04);
  -webkit-backdrop-filter:blur(18px) saturate(.55) brightness(1.02);
          backdrop-filter:blur(18px) saturate(.55) brightness(1.02);
}
.tinted .hero.tinted{
  background:
    radial-gradient(140% 88% at 50% -24%, var(--c-glow), rgba(0,0,0,0) 62%),
    linear-gradient(180deg, var(--c-hi) 0%, var(--c-lo) 46%, var(--c-deep) 100%),
    rgba(255,255,255,.04);
  -webkit-backdrop-filter:blur(22px) saturate(.55) brightness(1.02);
          backdrop-filter:blur(22px) saturate(.55) brightness(1.02);
}

/* ---------------------------------------------------------------------
   2. 锥形流光边框 —— 四套主题四种光
   ---------------------------------------------------------------------
   边框不是"描边在变色",是**一束锥形的光在卡片背后转**,只从边框那一圈
   露出来(靠 mask 的 xor 挖掉中间)。

   转的是 transform,不是渐变本身 —— transform 走合成层,GPU 做,
   不触发重绘。要是去动 conic-gradient 的角度,那是每帧重绘,四张卡一起
   转会明显费电。

   四套的光色按赛事来,不是随便挑的:
     法网  陶土 → 奶油    土场在夕阳下就是这个渐变
     澳网  冰蓝 → 深蓝    蓝色硬地 + 墨尔本的强光
     温网  奶白 → 淡紫    白衣与温网紫,唯一不用高饱和的那套
     美网  霓虹黄 → 青    夜场灯光,四套里唯一允许发光的
   --------------------------------------------------------------------- */
/* 一套主题只给一个边框色 + 它自己的暗调,不再是两个颜色对撞 */
.slam-rg  { --ring1:#F0C49B; --ring-dim:rgba(240,196,155,.24) }
.slam-ao  { --ring1:#9BE6FF; --ring-dim:rgba(155,230,255,.22) }
.slam-wb  { --ring1:#FFF6DE; --ring-dim:rgba(255,246,222,.20) }
.slam-uso { --ring1:#E8FF3A; --ring-dim:rgba(232,255,58,.20) }

.obring{
  position:absolute;inset:0;border-radius:inherit;z-index:6;pointer-events:none;
  padding:1.8px;overflow:hidden;opacity:1;
  -webkit-mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite:xor;
          mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask-composite:exclude;
}
.tinted .obring{opacity:1}
/* 为什么不是纯锥形渐变:
   锥形渐变的颜色是按**角度**分布的,而卡片是竖长方形 —— 左右两条边只占
   很小的角度范围、上下两条边占很大的角度范围。结果就是颜色全挤在左右两侧,
   上下几乎是一个色。看上去不是"一圈光",是"两侧各挂了一道",
   转起来更像顺着边往下淌。

   拆成两层就干净了:
     i  连续的线性渐变,铺满整圈 —— 边框**永远是完整的一圈颜色**,不动,
        负责"大气"
     b  一束很淡的白光在转 —— 只负责"活着",不承担颜色 */
/* 有颜色的底边框整个去掉了。
   不管是双色还是单色渐变,只要它是"铺满一圈的静态色",在竖长卡片上就一定会
   出现明暗不均 —— 因为渐变是按直线走的,而边框是绕一圈的,两者对不上。
   与其继续调,不如不要:**边框只留一条几乎看不见的中性描边**,
   剩下的交给那束转的光。光走到哪儿哪儿亮,这才是"流光"该有的样子。 */
.obring i{
  position:absolute;inset:0;display:block;
  background:rgba(255,255,255,.10);
}
/* 上一版这束光是「白色 55% 透明」,而底下那圈本来就是亮色(美网是霓虹黄)——
   亮的叠在亮的上面,几乎看不出差别,所以你会觉得"根本没动"。
   要看得出移动,必须**两个方向都有对比**:一段比底色亮很多,
   对面一段比底色暗很多。这样光走到哪儿,哪儿的明暗都在变。 */
/* 转的这一层里原来有一段 55% 的纯黑 —— 它扫过卡片左边的时候就是一道黑影,
   换主题重渲染时动画从头开始,那道黑正好压在左侧,看着像"黑了一秒"。
   黑去掉。对比改由底层来给:底层中段本来就暗,白色高光扫过去自然有明暗差。 */
.obring b{
  position:absolute;inset:-62%;display:block;opacity:1;
  background:conic-gradient(from 0deg,
    rgba(255,255,255,0)    0deg,
    var(--ring1)          16deg,
    rgba(255,255,255,.98) 24deg,
    var(--ring1)          32deg,
    rgba(255,255,255,0)   62deg,
    rgba(255,255,255,0)  360deg);
  animation:obRingSpin 5.5s linear infinite;
}
@keyframes obRingSpin{to{transform:rotate(360deg)}}
/* 四张卡错开起转,不然像一排跑马灯 */
.obgrid .obcard:nth-child(2) .obring b{animation-delay:-1.9s}
.obgrid .obcard:nth-child(3) .obring b{animation-delay:-3.8s}
.obgrid .obcard:nth-child(4) .obring b{animation-delay:-5.6s}
/* 按下去光收紧一档,像被按亮 */
.obcard:active .obring{filter:brightness(1.5)}
/* 边框那束光留着 —— 它不位移,只是一圈局部的亮度变化。
   真要完全静止,只要把速度放到很慢就够温和了。 */
@media (prefers-reduced-motion:reduce){
  .obring b{animation-duration:22s}
}


/* =====================================================================
   AT. 中文不要合成粗体 —— 「字被拉伸」的真正原因
   ---------------------------------------------------------------------
   苹方(PingFang SC)只有到 Semibold(600)为止。CSS 里写 700/800/900,
   iOS 拿不到对应的字面,只能**合成**加粗 —— 而合成加粗的做法就是把笔画
   往外扩、字面往横里撑。所以中文标题看上去被拉宽了、糊了,英文却正常
   (SF Pro 有真的 800/900)。

   桌面预览里也看不出来:macOS 上苹方同样只到 600,但预览的字号小、
   我看的是英文版,一直没撞上。

   两道保险:
     1. font-synthesis 关掉粗细合成 —— 让系统退到最接近的真字面(Semibold)
     2. 中文界面下把所有 >=700 的地方显式压到 600
   第 2 条是给不支持 font-synthesis 的老 webview 兜底的。
   ===================================================================== */
html{font-synthesis:style;-webkit-font-synthesis:style}

/* 标题字重变量:中文一律 600,再高也只是合成出来的假粗 */
html[lang="zh-Hans"] .slam-rg,
html[lang="zh-Hans"] .slam-ao,
html[lang="zh-Hans"] .slam-wb,
html[lang="zh-Hans"] .slam-uso{--disp-w:var(--w7)}

/* 逐个列选择器永远会漏(第一版就漏了标签栏、头像行、提醒天数)。
   改成从声明侧解决:全站的 700/800/900 都写成变量,
   中文界面把这三个变量一起压到 600。一处定义,不可能漏。 */
html[lang="zh-Hans"]{--w7:600; --w8:600; --w9:600}

/* =====================================================================
   AU. 底部那条黑带
   ---------------------------------------------------------------------
   真机上四个标签下面黑掉一大块。两个原因叠在一起:
     · 背板那层渐变收到 rgba(0,0,0,.86) —— 几乎是纯黑
     · padding-bottom 直接给了整个 env(safe-area-inset-bottom)(34px)
   于是 home indicator 那一带成了一条实心黑带,占屏底将近十分之一。

   home indicator 本来就是系统画在上面的,底下不需要再垫一整块。
   收到安全区的一半多一点,够躲开就行;渐变也不收到黑,收到场地的深色。
   ===================================================================== */
.tabbar{padding-bottom:calc(7px + var(--sab) * 0.42)}
html.native .tabbar{padding-bottom:calc(7px + var(--sab) * 0.42)}
.tabbar::after{
  background:linear-gradient(180deg,
    rgba(0,0,0,0) 0%, rgba(0,0,0,.30) 30%, rgba(0,0,0,.52) 64%, rgba(0,0,0,.60) 100%);
}
.screen{padding-bottom:calc(84px + var(--sab) * 0.7)}

/* =====================================================================
   AV. 汉字不加字距
   ---------------------------------------------------------------------
   拉丁字母之间加 tracking 是排版手段;汉字之间加 tracking 是排版事故 ——
   汉字本来就是等宽方块,再撑开就是「一 个 一 个 蹦」,看着就是被拉伸。
   底部四个标签是 .1em,三个字散得最明显。

   .cjk 是渲染后按**内容**打上的(见 cardfx.js 的 markCJK):
   哪个元素的文字里有汉字,哪个元素收字距。
   「MOST ADOPTED」这类装饰性英文标签不含汉字,宽字距原样保留。
   ===================================================================== */
/* 这里用 !important 是有意的,不是偷懒:
   .cjk 是按内容判定的**修正**,它必须盖过所有按选择器写的字距 ——
   而那些选择器(.pcard .phint 之类)的权重天生比单个类高。
   而且没有任何一条规则「应该」赢过它:汉字就是不该有 tracking。 */
.cjk{letter-spacing:.008em !important}
/* 大标题本来就该收紧,汉字更是 */
.cjk.ttl,.ttl.cjk,.h1.cjk,.cjk.qq,.cjk.wxv{letter-spacing:-.02em !important}
/* 小号说明文字给一点点呼吸,但远小于拉丁的 tracking */
.cjk.obtap,.cjk.fine,.cjk.goptfoot{letter-spacing:.012em !important}

/* =====================================================================
   AW. 底部标签栏:从"贴在屏幕底部的一条实心板"改成一块浮着的玻璃
   ---------------------------------------------------------------------
   之前它是通栏的、有一条上边线、背后一层不太透的渐变 —— 那是 iOS 7-11
   的做法。现在的做法是:**一块浮在内容之上的圆角玻璃**,和屏幕边缘留空,
   背后的内容能看见、被模糊、被提亮,而不是被一块板挡住。

   几个关键点(缺一个就不像):
     · 和左右下都留空 —— 它是"浮着的",不是"贴着的"
     · 强模糊 + 提饱和 —— 玻璃会把背后的颜色带上来一点,这是它有厚度的证据
     · 顶边一道亮高光 + 一圈很淡的白边 —— 厚度和折射
     · 选中项是一颗填充的胶囊,不是一根下划线 —— 下划线是网页做法
   ===================================================================== */
.tabbar{
  left:14px; right:14px;
  bottom:max(10px, calc(var(--sab) * 0.52));
  height:auto;
  padding:9px 6px;
  border-radius:26px;
  border:0;
  background:rgba(255,255,255,.07);
  -webkit-backdrop-filter:blur(30px) saturate(1.9) brightness(1.06);
          backdrop-filter:blur(30px) saturate(1.9) brightness(1.06);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.42),
    inset 0 0 0 1px rgba(255,255,255,.13),
    0 12px 34px -12px rgba(0,0,0,.62);
  overflow:visible;
}
html.native .tabbar{padding:9px 6px}
/* 之前那两层(实色背板 + 顶边线)不要了 —— 玻璃自己就是背板 */
.tabbar::after{content:none}
.tabbar::before{content:none}

/* 每一格是一颗胶囊。没选中时透明,选中时填一层很淡的白 —— 
   像 iOS 现在的分段控件,而不是一根下划线。 */
.tab{
  flex:1;justify-content:center;gap:3px;
  padding:7px 4px 6px;border-radius:19px;
  transition:background .22s ease, color .22s ease;
}
.tab.on{
  background:rgba(255,255,255,.16);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.30);
}
.tab .ti{padding:0}
.tab svg{width:21px;height:21px}
/* 下划线那根点去掉,选中状态交给胶囊 */
.tab .tdot{display:none}

/* 浮起来之后内容的下边距要跟着改 */
.screen{padding-bottom:calc(96px + var(--sab) * 0.6)}

/* 场地本身要能从玻璃后面透上来,所以四套主题不要再给它上实色 */
.slam-ao .tabbar{border-top:0}
.slam-ao .tabbar::before{content:none}

/* 右滑返回时,内容层需要能被位移 */
#screen{will-change:auto}

/* 玻璃是透的,底下滚过什么都看得见 —— 给标签一点分离度,
   不然滚到某些内容上面文字会糊在背景里。不是加背景,是让笔画有边。 */
.tab span{text-shadow:0 1px 3px rgba(0,0,0,.45)}
.tab svg{filter:drop-shadow(0 1px 2px rgba(0,0,0,.4))}

/* =====================================================================
   AX. 玻璃底栏的最后一版
   ---------------------------------------------------------------------
   问题:玻璃是透的,底下滚过去的字**还认得出来**,一片乱。
   苹果的材质不是"透明"——是**把背后的东西糊成一片颜色**:
   大半径模糊 + 一层有分量的染色,结果是「你知道后面有东西、有明暗、有颜色,
   但读不出那是什么」。玻璃感来自明暗和颜色的起伏,不是来自"看得清"。

   所以三件事一起加:
     1. 模糊半径从 30px 提到 46px —— 字的笔画在这个半径下彻底散掉
     2. 加一层有分量的染色(不是 7% 的白,是一层实打实的中性暗色)
     3. 胶囊之外、屏幕最底下那一条也要糊 —— 否则字会从胶囊**旁边和下面**露出来
   ===================================================================== */

/* 1+2:胶囊本体的材质 */
/* 之前压了一层 42% 的中性暗色 —— 那一层把球场的颜色全吃掉了,
   所以不管在红土还是草地上,底栏都是一样的灰黑。
   真玻璃不是这样:它**吸背后的颜色**。
   把中性层降到 18%,饱和度从 1.6 提到 2.6(模糊会把颜色摊平,
   要靠 saturate 把它拉回来),再按主题给一层很淡的场地色。
   现在红土上它偏暖、草地上偏绿、夜场偏蓝 —— 是同一块玻璃,不是四块板。 */
/* saturate(2.6) 把吸上来的颜色搞得过艳,像一块彩色塑料。
   iOS 26 那种质感的配方其实是:**大模糊 + 克制的饱和 + 更高的透明度 +
   一道很亮的顶边**。颜色是"透上来的",不是"染上去的",所以饱和只要略高于
   背景就够(1.7),再高就变成材质自己有颜色了。 */
.tabbar{
  background:
    linear-gradient(180deg, rgba(255,255,255,.22), rgba(255,255,255,.06) 58%, rgba(255,255,255,.02)),
    var(--tabtint, rgba(20,20,26,.12));
  -webkit-backdrop-filter:blur(40px) saturate(1.7) brightness(1.05);
          backdrop-filter:blur(40px) saturate(1.7) brightness(1.05);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.46),
    inset 0 0 0 1px rgba(255,255,255,.12),
    inset 0 -12px 22px -14px rgba(0,0,0,.5),
    0 14px 36px -12px rgba(0,0,0,.68);
}
/* 胶囊往下坐,尽量贴底 —— 留的这点只是躲开 home indicator */
.tabbar{bottom:max(6px, calc(var(--sab) * 0.34))}

/* 3:屏幕最底下整条也要糊。
   这一层在胶囊底下(z-index 更低),宽度铺满,上沿用渐变遮罩化开,
   所以看不到边。它负责胶囊**旁边和下面**那些漏出来的字。 */
.view::after{
  content:"";position:absolute;left:0;right:0;bottom:0;z-index:24;pointer-events:none;
  height:calc(84px + var(--sab));
  -webkit-backdrop-filter:blur(20px) saturate(1.2);
          backdrop-filter:blur(20px) saturate(1.2);
  background:linear-gradient(180deg, rgba(0,0,0,0), rgba(0,0,0,.30) 52%, rgba(0,0,0,.46));
  -webkit-mask-image:linear-gradient(180deg, transparent 0%, #000 46%, #000 100%);
          mask-image:linear-gradient(180deg, transparent 0%, #000 46%, #000 100%);
}
/* 底下多糊了一条,内容的下边距跟着补 */
.screen{padding-bottom:calc(104px + var(--sab) * 0.6)}

/* =====================================================================
   AY. 主页顶部那颗球
   ---------------------------------------------------------------------
   引导页那颗球是这个 app 唯一的角色,选完拍之后它就没了 —— 可惜。
   现在主页顶部继续留着它:不说话、不挡路,点一下弹一下。
   位置正好卡在赛事切换条下面、主拍卡上面那段空白里。
   ===================================================================== */
.homeball{
  position:relative;align-self:flex-end;
  width:82px;height:82px;flex:none;
  margin:6px -2px -14px 0;
  cursor:pointer;-webkit-tap-highlight-color:transparent;
  animation:hbBob 3.8s ease-in-out infinite;
}
.homeball img{
  width:100%;height:100%;object-fit:contain;position:relative;z-index:2;
  filter:drop-shadow(0 7px 12px rgba(0,0,0,.42));
  pointer-events:none;
}
.homeball .hbsh{
  position:absolute;left:50%;bottom:-3px;transform:translateX(-50%);
  width:56px;height:10px;border-radius:50%;z-index:1;pointer-events:none;
  background:radial-gradient(ellipse at 50% 50%, rgba(0,0,0,.42), transparent 72%);
}
@keyframes hbBob{
  0%,100%{transform:translateY(0)}
  50%    {transform:translateY(-5px)}
}
/* 弹跳动画自己会接管 transform,呼吸这时候要让开 */
.homeball:active{animation:none}
@media (prefers-reduced-motion:reduce){
  .homeball{animation:none}
}
/* 赛事条和球现在是上下两个,把这一列理顺 */
.slampick{display:flex;flex-direction:column;align-items:flex-end;gap:0}

/* =====================================================================
   AZ. 主页顶部这一行的宽度必须算得住
   ---------------------------------------------------------------------
   量出来:英文 + 美网主题下,标题占 210px、赛事切换条要 164px,
   而可用宽度只有 357px —— 切换条被挤出屏幕 26px,球跟着一起出去。
   之前没暴露是因为右上角原来是个 40px 的小圆按钮,现在换成了 82px 的球。

   两头一起收:
     · 标题字号加上按屏宽的上限(vw),小屏自动缩,不再写死
     · 标题那一列允许收缩(min-width:0),切换条不参与收缩
   ===================================================================== */
.ctop{gap:12px}
.ctop > div:first-child{flex:1 1 auto;min-width:0}
.slampick{flex:0 0 auto;max-width:52%}
.slampick .slamrow{width:100%}

/* 标题字号:保留四套主题的大小差别,但都加一个按屏宽的上限 */
.slam-rg  .ctop .ttl{font-size:min(42px, 10.4vw)}
.slam-ao  .ctop .ttl{font-size:min(46px, 10.8vw)}
.slam-wb  .ctop .ttl{font-size:min(46px, 10.8vw)}
.slam-uso .ctop .ttl{font-size:min(56px, 12vw)}

/* 球贴着右边,但不许出界 */
.homeball{margin-right:0}

/* 屏幕最底那条糊得再狠一点 —— 20px 对大字号还不够,
   截图里「NEXT RESTRING」还认得出来。 */
.view::after{
  -webkit-backdrop-filter:blur(32px) saturate(1.15);
          backdrop-filter:blur(32px) saturate(1.15);
  background:linear-gradient(180deg, rgba(0,0,0,0), rgba(0,0,0,.34) 48%, rgba(0,0,0,.56));
}

/* 上面那四条被 AB 段的 html[lang="en"] 规则盖住了(带属性选择器,权重更高)——
   这类"按语言再分一次"的规则在这个文件里有一整段,只写 .slam-* 是压不过的。
   所以按同样的权重再写一遍,英文中文各一套。
   顺便把美网英文标题从 56px 收到 50px:旁边还站着一个赛事切换条,
   56px 在 393pt 宽的屏上必然打架。 */
html[lang="en"] .slam-rg  .ctop .ttl{font-size:min(44px, 9.6vw)}
html[lang="en"] .slam-ao  .ctop .ttl{font-size:min(42px, 9.3vw)}
html[lang="en"] .slam-wb  .ctop .ttl{font-size:min(43px, 9.9vw)}
html[lang="en"] .slam-uso .ctop .ttl{font-size:min(50px, 10.1vw)}
html[lang="zh-Hans"] .slam-rg  .ctop .ttl{font-size:min(42px, 10.4vw)}
html[lang="zh-Hans"] .slam-ao  .ctop .ttl{font-size:min(46px, 10.8vw)}
html[lang="zh-Hans"] .slam-wb  .ctop .ttl{font-size:min(46px, 10.8vw)}
html[lang="zh-Hans"] .slam-uso .ctop .ttl{font-size:min(44px, 10.4vw)}

/* 窄屏(SE / mini,375pt)上赛事切换条也要能收 —— 四格各自让出内边距,
   而不是整条挤出屏幕。文字本来就短,收内边距不影响可读。 */
.slampick{max-width:min(52%, 172px)}
.slampick .slamrow b{min-width:0;padding-left:3px;padding-right:3px}

/* 每套主题给底栏一层自己的场地色 —— 很淡,只负责"吸色",不负责遮挡。
   遮挡是 44px 模糊那一层的事。 */
.slam-rg  { --tabtint:rgba(96,38,20,.12) }
.slam-ao  { --tabtint:rgba(10,40,86,.12) }
.slam-wb  { --tabtint:rgba(12,46,26,.12) }
.slam-uso { --tabtint:rgba(8,26,68,.14) }
/* 底下那条全宽的模糊层同理:也别压成中性黑 */
.view::after{
  -webkit-backdrop-filter:blur(32px) saturate(1.9);
          backdrop-filter:blur(32px) saturate(1.9);
  background:linear-gradient(180deg, rgba(0,0,0,0), rgba(0,0,0,.26) 48%, rgba(0,0,0,.44));
}

/* ---- 底栏压矮 ----
   图标 21px + 文字 10px,上下却各留了 9px 内边距,加上每格自己的 7px,
   一共 32px 的空白托着 31px 的内容 —— 看着就是"空"。收一半。 */
.tabbar{padding:5px 6px}
html.native .tabbar{padding:5px 6px}
.tab{padding:5px 4px 4px;gap:2px;border-radius:16px}
.tab svg{width:20px;height:20px}

/* =====================================================================
   BA. 引导页顶部那一行不能这么空
   ---------------------------------------------------------------------
   「第一步」是 10px 的小字,右边站着一条 156px 的赛事切换条,中间空一大片 ——
   左边太轻,撑不住右边。把它做成真正的一行标签:字号提到 13px、加重、
   前面带一根短竖线当锚点。再把它和大标题之间的空隙收掉。
   ===================================================================== */
.ob .obtop{align-items:center;margin-bottom:0}
.ob .obtop .eyebrow{
  font-size:13px;font-weight:600;letter-spacing:.06em;
  color:var(--ink-dim);display:flex;align-items:center;gap:8px;
}
.ob .obtop .eyebrow::before{
  content:"";width:3px;height:15px;border-radius:2px;background:var(--acc);flex:none;
}
html[lang="zh-Hans"] .ob .obtop .eyebrow{letter-spacing:.02em;font-size:13.5px}
/* 标题往上收,别和上面那行拉出一条空带 */
.ob .obhd{margin-top:-2px}
.ob .ttl{margin-top:6px}

/* 夜场的窄高感改由字距给:全大写 + 负字距,不动字宽 */
.slam-uso .ctop .ttl,.slam-uso .h1,.slam-uso .ob .ttl{letter-spacing:-.032em}

/* 影子整体减轻 ——
   之前主色影是 55~72% 的黑,球拍和球后面糊着一大团,像贴在墙上而不是浮在场地上。
   实物的接触影是"近处深、马上散",不是一大片均匀的黑。所以透明度减半、
   模糊加大,近处那一点点深度靠 ::after 那条椭圆给。 */
.hero .fig svg,.hero .fig img{filter:drop-shadow(5px 14px 18px var(--shadow-obj))}
.obj svg,.obj img{filter:drop-shadow(3px 8px 12px var(--shadow-obj))}
.hero .fig::after{filter:blur(7px);opacity:.72}

/* =====================================================================
   BB. 穿线手感
   ---------------------------------------------------------------------
   一小块弦床 + 一颗会掉下来的球 + 一根磅数滑杆。
   弦床的疏密跟着磅数走(--tense),被砸中时会被压出一个坑(--dent)——
   两个都是 CSS 变量,由 feel.js 实时改,所以拖滑杆时它是连续变的。
   ===================================================================== */
.feelbox{--tense:.5;--dent:0px}
.feelbox .bed{
  position:relative;height:132px;border-radius:var(--r);overflow:hidden;
  background:linear-gradient(180deg, rgba(0,0,0,.24), rgba(0,0,0,.34));
  border:1px solid var(--line-ghost);
}
.feelbox .bed svg{
  position:absolute;inset:10px;width:calc(100% - 20px);height:calc(100% - 20px);
  transform:translateY(var(--dent));
  transition:transform .09s cubic-bezier(.3,.9,.3,1);
}
.feelbox .bed svg line{
  stroke:var(--line);
  /* 高磅:线细、亮、绷紧;低磅:粗、暗、松 */
  stroke-width:calc(0.5px + (1 - var(--tense)) * 0.6px);
  opacity:calc(.28 + var(--tense) * .42);
  vector-effect:non-scaling-stroke;
}
.feelbox .bed .fb{
  position:absolute;left:50%;bottom:34%;width:34px;height:34px;
  margin-left:-17px;object-fit:contain;pointer-events:none;
  filter:drop-shadow(0 5px 7px rgba(0,0,0,.42));
}
.tenrow{display:flex;align-items:center;gap:14px;margin-top:14px}
.tkg{flex:none;font-family:var(--led);color:var(--line);font-size:13px}
.tkg b{font-size:26px;font-weight:var(--w8);margin-right:2px;font-variant-numeric:tabular-nums}
.tenrange{
  -webkit-appearance:none;appearance:none;flex:1;height:4px;border-radius:3px;
  background:linear-gradient(90deg,var(--acc) 0%,var(--acc) calc(var(--p,50%)),var(--line-ghost) 0);
  background:var(--line-ghost);
}
.tenrange::-webkit-slider-thumb{
  -webkit-appearance:none;width:26px;height:26px;border-radius:50%;
  background:var(--acc);border:0;box-shadow:0 3px 10px -3px rgba(0,0,0,.7);
}
.feelsay{font-size:12.5px;color:var(--ink-dim);line-height:1.65;margin:12px 0 12px;min-height:2.6em}

/* =====================================================================
   BC. 两处黑影
   ---------------------------------------------------------------------
   1) 「换主题时球拍左侧的黑影」
      美网那套我当初按"夜场四座灯塔"做了**四个方向的投影**,其中两个是往
      左边打的(-13px 和 -7px)。另外三套都只有一个朝右下的日光影。
      所以一切到美网,球拍左边就凭空多出一道影 —— 你看到的就是它。

      灯塔那个想法在纯几何图形上成立,在**照片**上不成立:真实照片本身
      已经带着拍摄时的光,再从反方向补一道影,读起来不是"四面打光",
      是"糊了一块"。四套统一成同一个方向的单影,夜场的性格交给霓虹色和
      场地纹理去表达。

   2) 「球的影子存在一秒然后消失」
      影子是一个**独立的元素**(.hbsh / .obb::after),球是另一个元素。
      两者没有任何绑定关系,所以只要图片晚一帧出现、或者被重新解码,
      就会出现"影子在、球不在"。

      改法不是去补时序,是**让影子没法独立存在**:把它并进图片自己的
      drop-shadow 里。图片没画出来,它的影子在物理上就不可能存在。
      这类 bug 从此不会再有第二次。
   ===================================================================== */

/* 1. 四套统一成一个方向的影 */
.slam-uso .hero .fig svg,.slam-uso .hero .fig img{
  filter:drop-shadow(5px 14px 18px var(--shadow-obj));
}
.slam-uso .obj svg,.slam-uso .obj img{
  filter:drop-shadow(3px 8px 12px var(--shadow-obj));
}

/* 2. 影子并进图片本身 */
.homeball .hbsh{display:none}
.homeball img{
  filter:drop-shadow(0 8px 10px rgba(0,0,0,.30)) drop-shadow(0 2px 3px rgba(0,0,0,.22));
}
.obb::after{display:none}
.obb img{
  filter:drop-shadow(0 10px 13px rgba(0,0,0,.32)) drop-shadow(0 3px 4px rgba(0,0,0,.22));
}

/* 主页那颗球进入碰一碰待机时也要有涟漪 —— 之前只写了 .obball */
.homeball.bumparmed img{animation:bumpReady 1.05s ease-in-out infinite}
.homeball.bumparmed::before{
  content:"";position:absolute;inset:-12px;border-radius:50%;
  border:2px solid var(--acc);opacity:0;pointer-events:none;
  animation:bumpRipple 1.05s ease-out infinite;
}
.homeball .bumpchip{left:auto;right:0;bottom:-30px;white-space:nowrap}

/* =====================================================================
   BD. 卡片上那条笔直的深色矩形
   ---------------------------------------------------------------------
   截图放大之后看清了:它的右边界是**一条直线**。软阴影不会有直边,
   所以它不是阴影,是某个元素被画出来了。

   元凶是两个特性撞在一起:
     .obcard   有 backdrop-filter(磨砂玻璃)
     .obring   在它内部用 -webkit-mask-composite:xor 抠出边框那一圈

   Safari 里 backdrop-filter 会给元素单独开一个合成层,而同一棵子树里再用
   mask-composite,层的裁剪区算错就会漏出一块没被裁掉的底 —— 表现就是
   一条边缘笔直的深色矩形,而且只在某些重绘时机出现(所以换主题时最容易撞上,
   我在自己的浏览器里怎么点都复现不了)。

   这类"某个浏览器在某个组合下算错"的问题,靠调参数是修不好的。
   把组合拆掉:

     1. 卡片不再用 backdrop-filter。它现在的底色是完整的三段渐变,
        玻璃感来自顶边高光和内圈折射 —— 那两条留着就够,不差这层模糊。
        底栏和详情页展台继续用,因为它们内部没有 mask。

     2. 流光边框不再用 mask 抠。改成**把光环放在卡片背景的下面**:
        环比卡片大 1.8px、露出一圈,中间被不透明的 .obbg 盖住。
        同样的视觉,零 mask。
   ===================================================================== */
.tinted .obcard,
.tinted .libcard,
.tinted .hero.tinted{
  -webkit-backdrop-filter:none;backdrop-filter:none;
}
/* 没有模糊之后,底色要自己站得住 —— 在三段渐变**底下**垫一层暗色。
   注意必须写 background-color,不能写 background:
   background 是简写,会把 background-image 一并重置成 none ——
   也就是把上面那三段涂装色渐变整个抹掉。上一版就是这么把
   「按涂装色上色」这个功能悄悄弄没的:开关照常切换、类名照常加,
   但卡片背景变成一块纯深灰,看上去就是「点了没反应」。 */
.tinted .obcard{background-color:rgba(12,14,20,.30)}
.tinted .libcard{background-color:rgba(12,14,20,.26)}
.tinted .hero.tinted{background-color:rgba(12,14,20,.28)}

/* 流光环:不再用 mask,改成垫在背景底下露出一圈 */
.obcard{overflow:visible}
.obbg{overflow:hidden;border-radius:inherit;z-index:1}
.obring{
  inset:-1.8px;padding:0;overflow:hidden;
  border-radius:calc(var(--r) + 2px);
  z-index:0;
  -webkit-mask:none;mask:none;-webkit-mask-composite:add;mask-composite:add;
}
/* 卡片本身要恢复裁剪(球拍图不能出界),交给一个专门的裁剪层 */
.obcard::after{
  content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.30), inset 0 -1px 0 rgba(0,0,0,.28);
  z-index:5;
}
.obcard .obimg{overflow:hidden;border-radius:inherit}
.obcard .obname{position:relative;z-index:4}

/* =====================================================================
   BE. 球的投影落到了下面的卡片上
   ---------------------------------------------------------------------
   drop-shadow 是画在元素盒子**外面**的,而这颗球又故意压着主拍卡
   (margin-bottom:-14px)。于是它的影子直接投到「控制上旋」那颗标签上,
   看起来就是标签后面莫名有一团黑。

   两件事一起改:影子收紧到贴着球(8px→4px,单层不叠加),
   球也不再压住卡片。链式 drop-shadow 尤其要避免 —— 第二层投的是第一层的
   结果,阴影会滚雪球。
   ===================================================================== */
.homeball{margin-bottom:2px}
.homeball img{filter:drop-shadow(0 4px 5px rgba(0,0,0,.26))}
.obb img{filter:drop-shadow(0 5px 7px rgba(0,0,0,.28))}

/* =====================================================================
   BF. 详情页 · 玻璃展台
   ---------------------------------------------------------------------
   你选的那一版。三个数字和之前不一样的地方:
     球拍高度  从 66% 提到 **86%** —— 之前上下留白比球拍还多
     参数字号  从 19px 提到 **28px**,行距收紧,左下角是有分量的一块
     展台内缩  从 5% 收到 2.5%,玻璃几乎顶到屏幕两侧

   球拍现在是**整把**,和列表里那张图同一个构图 —— 所以转场只剩平移缩放。
   ===================================================================== */
.slab{
  position:relative;height:min(58vh,470px);margin:8px 0 12px;
  border-radius:var(--r-cell);
}
.slab .slabglass{
  position:absolute;inset:0;border-radius:inherit;pointer-events:none;
  background:linear-gradient(168deg,rgba(255,255,255,.15),rgba(255,255,255,.05) 46%,rgba(255,255,255,.02));
  -webkit-backdrop-filter:blur(20px) saturate(1.3);backdrop-filter:blur(20px) saturate(1.3);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.44),
    inset 0 0 0 1px rgba(255,255,255,.10),
    0 22px 46px -28px rgba(0,0,0,.55);
}
.slab .slabrk{
  position:absolute;left:50%;top:46%;height:86%;width:auto;
  transform:translate(-50%,-50%) rotate(-8deg);
  filter:drop-shadow(6px 16px 20px rgba(0,0,0,.30));
  transition:transform .42s var(--ease);
  cursor:pointer;z-index:2;
}
.slab .slabrk:active{transform:translate(-50%,-50%) rotate(-8deg) scale(1.03)}
.slab .slabshadow{
  position:absolute;left:50%;top:84%;width:44%;height:5%;transform:translateX(-50%);
  border-radius:50%;pointer-events:none;z-index:1;
  background:radial-gradient(ellipse at 50% 50%,rgba(0,0,0,.28),transparent 72%);
  filter:blur(5px);
}
/* 左下角参数:这一块要有分量,不能是三行小字 */
.slab .slabspec{
  position:absolute;left:16px;bottom:14px;z-index:3;
  display:flex;flex-direction:column;gap:2px;
}
.slab .slabspec div{display:flex;align-items:baseline;gap:5px}
.slab .slabspec b{
  font-family:var(--led);font-size:28px;font-weight:var(--w8);line-height:1.05;
  color:var(--line);letter-spacing:-.02em;font-variant-numeric:tabular-nums;
  text-shadow:0 2px 10px rgba(0,0,0,.35);
}
.slab .slabspec i{
  font-style:normal;font-size:9.5px;letter-spacing:.12em;text-transform:uppercase;
  color:var(--ink-faint);
}
.slab .slabball{
  position:absolute;right:16px;bottom:14px;width:58px;height:58px;z-index:3;
  object-fit:contain;cursor:pointer;
  filter:drop-shadow(0 5px 7px rgba(0,0,0,.28));
  animation:hbBob 3.6s ease-in-out infinite;
}
.slab .slabhint{
  position:absolute;left:0;right:0;top:10px;text-align:center;z-index:3;
  font-size:10.5px;letter-spacing:.04em;color:var(--ink-faint);pointer-events:none;
}
@media (prefers-reduced-motion:reduce){ .slab .slabball{animation:none} }

/* 原来那个 canvas 特写的样式不再用到,但 .hero.show 的入场动画还想要 */
.slab.show{animation:var(--land,rkFlash) .5s var(--ease-out) both}

/* 有球拍在飞的时候,展台自己不能再做入场动画 ——
   否则克隆飞向的是一个**正在移动的终点**,落位必然差一截(实测差 98px)。
   这条和 .screen 的入场动画是同一个道理,当初漏了展台。 */
html.flying .slab.show{animation:none}

/* =====================================================================
   BG. 比分
   ---------------------------------------------------------------------
   参考里那种是横着两行、加减号在两侧 —— 能用,但它长得像一个工具。
   这里换成两块大牌子:**点谁谁得分**。手指落在哪都不会点错,
   出汗湿手也按得动。这是记分器唯一真正的使用场景。
   ===================================================================== */
.scboard{display:grid;grid-template-columns:1fr 1fr;gap:9px;padding:2px}
.scside{
  -webkit-appearance:none;appearance:none;font-family:inherit;cursor:pointer;
  display:flex;flex-direction:column;align-items:center;gap:2px;
  padding:16px 10px 13px;border-radius:var(--r);
  border:1px solid var(--line-ghost);color:var(--line);
  background:linear-gradient(168deg,rgba(255,255,255,.10),rgba(0,0,0,.16));
  transition:transform .12s var(--ease), background .18s;
}
.scside:active{transform:scale(.97);background:linear-gradient(168deg,rgba(255,255,255,.18),rgba(0,0,0,.12))}
.scside.srv{border-color:var(--acc)}
.scname{display:flex;align-items:center;gap:6px;font-size:12.5px;color:var(--ink-dim);
  max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.srvdot{width:6px;height:6px;border-radius:50%;background:var(--acc);flex:none}
.scpt{font-family:var(--led);font-size:56px;font-weight:var(--w8);line-height:1.02;
  letter-spacing:-.03em;font-variant-numeric:tabular-nums}
.scgm{font-size:15px;font-weight:var(--w7);color:var(--ink-dim);display:flex;align-items:baseline;gap:4px}
.scgm em{font-style:normal;font-size:9px;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-faint)}
.scboard.tb .scpt{color:var(--acc-text)}

.scbar{display:flex;align-items:center;gap:8px;padding:12px 2px 2px}
.scsets{flex:1;display:flex;gap:7px;flex-wrap:wrap;font-family:var(--led);font-size:12.5px;color:var(--ink-dim)}
.scsets span{background:rgba(0,0,0,.24);padding:3px 8px;border-radius:5px;font-variant-numeric:tabular-nums}
.scsets .none{background:none;padding:0;color:var(--ink-faint)}
.scsets b{color:var(--acc-ink);background:var(--acc);padding:3px 8px;border-radius:5px;font-size:10px;letter-spacing:.08em}
.scundo{
  -webkit-appearance:none;appearance:none;font-family:inherit;cursor:pointer;flex:none;
  font-size:11.5px;font-weight:var(--w7);color:var(--ink-dim);
  background:transparent;border:1px solid var(--line-ghost);border-radius:999px;padding:6px 12px;
}
.scundo:active{background:rgba(255,255,255,.08)}
.scrow{display:flex;align-items:center;gap:11px;padding:12px 13px;border-bottom:1px solid var(--line-ghost)}
.scrow:last-of-type{border-bottom:0}
.scd{flex:none;width:52px;font-family:var(--led);font-size:12.5px;font-weight:var(--w7);color:var(--acc-text)}


/* =====================================================================
   BH. 那道「激光」—— 我改坏的
   ---------------------------------------------------------------------
   流光环去掉 mask 之后,是靠 .obbg 盖住中间、只露出边缘一圈。
   但 .obbg 只在「涂装色」模式下才 display:block —— 素色模式下它不存在,
   于是整个锥形白光毫无遮挡地铺在卡片上,就是你看到的那道斜着的强光。

   我改遮罩那次只验了涂装色模式。素色是默认模式,更该验。

   改法:.obbg 永远存在,只是两种模式下画不同的东西 ——
   素色画统一的深色玻璃,涂装色画三段渐变。环永远有东西盖着。
   ===================================================================== */
.obbg{display:block}
.obcard .obbg{
  background:linear-gradient(168deg,
    rgba(255,255,255,.13) 0%, rgba(255,255,255,.05) 38%,
    rgba(0,0,0,.10) 72%, rgba(0,0,0,.22) 100%),
    var(--fill-2);
}
.tinted .obcard .obbg{
  background:
    radial-gradient(150% 92% at 50% -26%, var(--c-glow), rgba(0,0,0,0) 60%),
    linear-gradient(180deg, var(--c-hi) 0%, var(--c-lo) 46%, var(--c-deep) 100%),
    rgba(12,14,20,.30);
}
/* 环再收一档:它是点缀,不是主角 */
.obring{inset:-1.4px;border-radius:calc(var(--r) + 1.6px);opacity:.9}
.obring b{opacity:.55}


/* 角度不要了 —— 正着放。斜着看更「设计」,但正着看更像**你手里那把拍**,
   而且转场两端都是同一个姿态,不需要在飞行里额外转一次。 */
.slab .slabrk{transform:translate(-50%,-50%)}
.slab .slabrk:active{transform:translate(-50%,-50%) scale(1.03)}
/* 库详情那块牌子的位置要让开右下角的球 */
/* 年份那一条:不要框。原来它同时写了 left 和 right,于是被撑成通栏 ——
   一个只有八个字的注脚顶着一整圈边框,谁看都别扭。 */
.slab .slabyear{position:absolute;left:16px;top:14px;z-index:3;
  font-family:var(--mono);font-size:9.5px;letter-spacing:.054em;color:var(--ink-faint)}

/* =====================================================================
   BI. 听声音测磅数 —— 独立一节,给足分量
   ===================================================================== */
.tuner{padding:2px}
/* 养护中心里要先选「测哪把拍」,所以是两个下拉并排 */
.tunepick{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:11px}
.tunepick .field{margin:0;min-width:0}
.tuner>.btn.wide{margin-bottom:12px}
.tuner .field[disabled]{opacity:.55}
.tunerow{display:flex;gap:10px;align-items:center;margin-bottom:12px}
.tunerow .field{flex:1;margin:0}
.tunerow .btn{flex:none;padding-left:16px;padding-right:16px}
/* 参照尺:这把拍在各个磅数下"应该"是多少赫兹 —— 让人看见这不是黑箱 */
/* 那把「频率参照尺」(485/536/583/626 Hz)撤了。
   它是给我调算法时看的:某个磅数下**应该**是多少赫兹。
   对用户毫无用处 —— 没人知道自己的拍现在多少赫兹,
   摆四个数字在那儿只会让人以为漏看了什么。
   量出来的结果本来就带 Hz,要对照那时候再看就够了。 */
.tuneverdict{
  font-size:12.5px;line-height:1.6;color:var(--ink);margin:2px 0 12px;
}
.tuneout:empty{display:none}
.tuneout{margin:4px 0 12px;text-align:center}
.tunekg{display:flex;align-items:baseline;justify-content:center;gap:8px}
.tunekg b{font-family:var(--led);font-size:56px;font-weight:var(--w8);color:var(--acc-text);
  line-height:1;letter-spacing:-.03em;font-variant-numeric:tabular-nums}
.tunekg i{font-style:normal;font-size:15px;color:var(--ink-dim)}
.tunehz{font-size:11.5px;color:var(--ink-faint);margin:4px 0 12px;letter-spacing:.06em}
p.tunehint{font-size:12px;color:var(--ink-dim);line-height:1.6;margin:2px 0 10px;min-height:2.4em}

/* =====================================================================
   BJ. 卡片材质统一到底栏那一套 + 比分自己的配色
   ---------------------------------------------------------------------
   你说得对,那些方块太透了。底栏那块玻璃的配方是:大模糊 + 一层有分量的
   染色 + 一道亮顶边。之前只有底栏用,别的地方各写各的。
   现在抽成一套变量,天气卡、日程行、比分牌全用同一个。
   ===================================================================== */
.wxoff,.wxcard,.scboard .scside,.scrow,.planrow.ask{
  background:
    linear-gradient(180deg, rgba(255,255,255,.16), rgba(255,255,255,.05) 60%, rgba(255,255,255,.02)),
    var(--tabtint, rgba(20,20,26,.14)) !important;
  -webkit-backdrop-filter:blur(26px) saturate(1.6);
          backdrop-filter:blur(26px) saturate(1.6);
  border:1px solid rgba(255,255,255,.13);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.34);
}

/* 比分:两侧各有自己的颜色。网球场就是这么分边的 —— 
   一侧用主色(你),一侧用对面场地的冷色(对手)。
   不是为了好看,是为了**扫一眼就知道哪边是自己**。 */
.scboard .scside:first-child{
  --scc:var(--acc);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.34), inset 0 -3px 0 var(--acc);
}
.scboard .scside:last-child{
  --scc:#4FC3F7;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.34), inset 0 -3px 0 #4FC3F7;
}
.scboard .scpt{color:var(--scc)}
.scboard .scside.srv{border-color:var(--scc)}
.scboard .srvdot{background:var(--scc)}
.scboard.tb .scpt{color:var(--scc)}


/* =====================================================================
   BK. 卡片上不再有任何会动的亮东西
   ---------------------------------------------------------------------
   .obring 和 .glint 都从模板里拿掉了。这里把样式也一起废掉,
   免得以后哪个地方还留着这两个类名又把它们唤醒。
   ===================================================================== */
.obring,.glint{display:none !important}
/* 卡片的"活"改由按压给:按下去背景提亮一点,松手回去。
   这是有因果的反馈,不是一直在自己动的装饰。 */
.obcard:active .obbg{filter:brightness(1.14)}
.obbg{transition:filter .16s var(--ease)}

/* =====================================================================
   BL. 开屏
   ---------------------------------------------------------------------
   五段动画,随机一段,不重复上一次;活动位优先。
   全部只动 transform / opacity,不碰布局 —— 再慢的机器也不会卡在这一屏。
   ===================================================================== */
.splash{
  position:fixed;inset:0;z-index:999;display:flex;flex-direction:column;
  align-items:center;justify-content:center;gap:22px;
  background:var(--surface-deep,#160A05);
  transition:opacity .3s ease, transform .3s ease;
}
.splash.out{opacity:0;transform:scale(1.04);pointer-events:none}
.spstage{position:relative;width:150px;height:150px}
.spball{position:absolute;inset:0;width:100%;height:100%;object-fit:contain;
  filter:drop-shadow(0 8px 14px rgba(0,0,0,.4))}
.spword{font-family:var(--disp);font-weight:var(--w8);font-size:20px;
  letter-spacing:.02em;color:var(--line);opacity:0;animation:spWord .5s .34s var(--ease-out) forwards}
@keyframes spWord{from{opacity:0;transform:translateY(8px)}to{opacity:.9;transform:none}}

/* 1 发球:抛起,定住 */
.sp-toss .spball{animation:spToss .9s cubic-bezier(.2,.9,.3,1) both}
@keyframes spToss{
  0%  {transform:translateY(120px) scale(.7);opacity:0}
  55% {transform:translateY(-16px) scale(1.04);opacity:1}
  100%{transform:none;opacity:1}}

/* 2 弹跳:落下,弹两下 */
.sp-bounce .spball{animation:spBounce 1s cubic-bezier(.3,.8,.4,1) both}
@keyframes spBounce{
  0%  {transform:translateY(-160px) scale(.9);opacity:0}
  30% {transform:translateY(0) scale(1.14,.86);opacity:1}
  48% {transform:translateY(-46px) scale(.96,1.05)}
  66% {transform:translateY(0) scale(1.08,.93)}
  82% {transform:translateY(-16px) scale(.99,1.02)}
  100%{transform:none}}

/* 3 穿线:弦从两侧扫出,织成一片,球落在中间 */
.spweave{position:absolute;inset:12%;display:block}
.spweave i{position:absolute;background:var(--line);opacity:.5;transform-origin:center}
.sp-weave .spweave i:nth-child(1){left:12%;top:0;bottom:0;width:1.5px;animation:spV .5s .02s both}
.sp-weave .spweave i:nth-child(2){left:37%;top:0;bottom:0;width:1.5px;animation:spV .5s .08s both}
.sp-weave .spweave i:nth-child(3){left:62%;top:0;bottom:0;width:1.5px;animation:spV .5s .14s both}
.sp-weave .spweave i:nth-child(4){top:20%;left:0;right:0;height:1.5px;animation:spH .5s .20s both}
.sp-weave .spweave i:nth-child(5){top:48%;left:0;right:0;height:1.5px;animation:spH .5s .26s both}
.sp-weave .spweave i:nth-child(6){top:76%;left:0;right:0;height:1.5px;animation:spH .5s .32s both}
@keyframes spV{from{transform:scaleY(0);opacity:0}to{transform:none;opacity:.5}}
@keyframes spH{from{transform:scaleX(0);opacity:0}to{transform:none;opacity:.5}}
.sp-weave .spball{width:52%;height:52%;left:24%;top:24%;animation:spDrop .5s .46s cubic-bezier(.2,1.3,.32,1) both}
@keyframes spDrop{from{transform:translateY(-70px) scale(.6);opacity:0}to{transform:none;opacity:1}}
/* 其它三段不显示弦 */
.splash:not(.sp-weave) .spweave{display:none}

/* 4 落位:球从旁边滚进来 */
.sp-land .spball{animation:spRoll .85s cubic-bezier(.22,.9,.26,1) both}
@keyframes spRoll{
  0%  {transform:translateX(-160px) rotate(-420deg);opacity:0}
  60% {opacity:1}
  100%{transform:none;opacity:1}}

/* 5 甜区:球定在中央,两圈余波扩散出去 —— 打到甜区那一下的感觉 */
.sp-sweet .spball{animation:spPop .5s cubic-bezier(.2,1.5,.32,1) both}
@keyframes spPop{from{transform:scale(.42);opacity:0}to{transform:none;opacity:1}}
.sp-sweet .spstage::before,.sp-sweet .spstage::after{
  content:"";position:absolute;left:50%;top:50%;width:150px;height:150px;margin:-75px 0 0 -75px;
  border-radius:50%;border:1.5px solid var(--acc);opacity:0}
.sp-sweet .spstage::before{animation:spWave .78s .30s var(--ease-out) both}
.sp-sweet .spstage::after {animation:spWave .78s .52s var(--ease-out) both}
@keyframes spWave{from{transform:scale(.5);opacity:.55}to{transform:scale(1.75);opacity:0}}

/* 活动位 */
.spcamp{display:flex;flex-direction:column;align-items:center;gap:12px;padding:0 32px;text-align:center;
  animation:spWord .5s .1s var(--ease-out) both}
.spcamp img{width:170px;height:auto;object-fit:contain}
.spcamp b{font-family:var(--disp);font-weight:var(--w8);font-size:23px;color:var(--line)}
.spcamp span{font-size:13px;color:var(--ink-dim);line-height:1.6}

/* 开了减弱动态效果:只淡入,不位移 */
.sp-plain .spball,.sp-plain .spweave i,.sp-plain .spword,.sp-plain .spcamp{animation:none !important;opacity:1}
@media (prefers-reduced-motion:reduce){
  .splash *{animation:none !important;opacity:1}
}


/* =====================================================================
   BM. 三处收尾
   ---------------------------------------------------------------------
   1. 库/养护/我的 右上角原来是个圆圈图标,**点了没反应** ——
      一个长得像按钮、又不是按钮的东西,比没有还糟。换成主页那颗球:
      平时慢慢浮,点一下弹。全 app 只有一种球、一种弹法。
   2. 点开球拍会看到两段动画:球拍飞过来落位(转场),紧接着展台自己
      又做一遍入场。飞进来的那把拍已经交代清楚"你从哪来、到哪去"了,
      展台再闪一下就是重复。留转场,撤展台的。
   ===================================================================== */
.topball{
  position:relative;width:46px;height:46px;flex:none;
  margin:2px -2px 0 0;cursor:pointer;-webkit-tap-highlight-color:transparent;
  animation:hbBob 4.4s ease-in-out infinite;
}
.topball img{
  width:100%;height:100%;object-fit:contain;position:relative;z-index:2;
  filter:drop-shadow(0 5px 9px rgba(0,0,0,.34));pointer-events:none;
}
.topball .hbsh{
  position:absolute;left:50%;bottom:-2px;transform:translateX(-50%);
  width:32px;height:7px;border-radius:50%;z-index:1;pointer-events:none;
  background:radial-gradient(ellipse at 50% 50%, rgba(0,0,0,.32), transparent 72%);
}
.topball:active{animation:none}
@media (prefers-reduced-motion:reduce){ .topball{animation:none} }

/* 从转场飞进来的展台,不再自己做一次入场 */
.slab.show.flew{animation:none !important}


/* =====================================================================
   BN. 展台也吃这把拍的涂装色 + 听声测磅那两个下拉
   ---------------------------------------------------------------------
   1. 卡片按涂装色上色,点进去却是一块中性玻璃 —— 中间那一下颜色是断的。
      展台现在也用同一套 --c-hi / --c-lo / --c-deep,只是更淡:
      展台上站着一整把球拍,颜色重了会跟球拍自己的漆面打架。
   2. 原生 <select> 展开的列表是**系统画的**,改不了样式 ——
      深色界面上突然弹出一块灰白方框,和这个 app 完全两回事。
      换成自己的横排选项。
   ===================================================================== */
.tinted .slab .slabglass{
  background:
    radial-gradient(130% 78% at 50% -18%, var(--c-glow), rgba(0,0,0,0) 62%),
    linear-gradient(176deg, var(--c-hi) 0%, var(--c-lo) 52%, var(--c-deep) 100%);
  /* 球拍是主角,底色只是把它托住 */
  opacity:.82;
}
/* 深色尾巴压在左下角那三个数字底下,白字才立得住 */
.tinted .slab .slabspec b,.tinted .slab .slabspec i{text-shadow:0 1px 6px rgba(0,0,0,.42)}

.tunelab{
  font-family:var(--mono);font-size:9.5px;letter-spacing:.06em;
  color:var(--ink-faint);margin:2px 0 6px;
}
.tunechips{
  display:flex;gap:7px;margin-bottom:12px;
  overflow-x:auto;-webkit-overflow-scrolling:touch;
  scrollbar-width:none;padding-bottom:2px;
}
.tunechips::-webkit-scrollbar{display:none}
.tunechips.off{opacity:.5;pointer-events:none}
.tchip{
  flex:none;padding:8px 13px;border-radius:999px;cursor:pointer;
  font-size:12.5px;font-weight:var(--w7);white-space:nowrap;
  color:var(--ink);background:var(--fill);
  border:var(--lw) solid var(--line-ghost);
  -webkit-tap-highlight-color:transparent;
  transition:transform .14s var(--ease), background .18s ease, color .18s ease, border-color .18s ease;
}
.tchip:active{transform:scale(.95)}
.tchip.on{
  background:var(--acc);color:var(--acc-ink);border-color:transparent;
  font-weight:var(--w8);
}
