/* ============================================================
   Toy 版覆盖层
   ------------------------------------------------------------
   只管两件事：新首屏，和 Toy 环境（iframe、全屏、无外链）带来的差别。
   其余所有页面（身份、生涯、结算、图鉴）原样走 style.css。

   底色一个数都没动。2026-08-14 那次「撤线 + 提亮」被否掉的结论还算数 ——
   这套界面满屏是队徽、球衣、金徽章和队色卡片，底子一提亮那些东西就没处跳了。
   首页「干」的原因不是暗，是整屏没有一处密度、也没有主角：
   一块空球场 SVG 摆在左边，右边一摞表单控件。

   所以这儿加的全是**深度**，不是亮度：
     · 队徽拼贴当底噪（素材本来就躺在 assets/crests 里，141 个，一直没上过首页）
     · 中心一盏球场夜灯（径向渐变，压在球衣背后）
     · 颗粒（很淡的噪点，把大面积平色压住）
     · 球衣当主角，带着自己那省的主色打光
   ============================================================ */

/* ---------- 这一层新加的唯一一个色 ---------- */
/* --sup 是 B 站那个粉（#FB7299）。只给「关注 / 三连」那一块用，
   因为它说的是**站外**的动作 —— 混进墨绿那一套（能力、主按钮、选中项）
   会被当成游戏流程的一部分。底色和线一个数都没动，这只是多一个强调色。 */
:root { --sup: 340 95% 72%; }

/* ---------- Toy 环境 ---------- */
/* 跑在平台的 iframe 里，高度由外面给。100% 链一路传到 body，
   首屏那一屏才能真的占满一屏而不是被内容撑开。 */
html, body { height: 100%; }
body {
  /* 主站给底部 actionbar 留的位置。首页没有 actionbar，
     留着的话球衣整个偏上一截。身份流程那几步会把它加回来（.no-actionbar 反向）。 */
  padding-bottom: 0;
  overflow-x: hidden;
}

/* ---------- 开局遮罩 ---------- */
.boot {
  position: fixed; inset: 0; z-index: 90;
  display: flex; align-items: center; justify-content: center;
  background: hsl(var(--bg));
  transition: opacity .45s ease, visibility .45s;
}
.boot.gone { opacity: 0; visibility: hidden; }
.boot-inner { text-align: center; width: min(78vw, 300px); }
.boot-ball {
  font-size: 2.4rem; line-height: 1;
  animation: bootBounce 1.1s cubic-bezier(.5, .05, .5, .95) infinite;
}
@keyframes bootBounce {
  0%, 100% { transform: translateY(-14%) scale(1, .96); }
  50%      { transform: translateY(14%)  scale(.94, 1.05); }
}
.boot-bar {
  margin: 1.1rem 0 .7rem;
  height: 3px; border-radius: 3px;
  background: hsl(var(--border));
  overflow: hidden;
}
.boot-bar > i {
  display: block; height: 100%; width: 0%;
  border-radius: 3px;
  background: linear-gradient(90deg, hsl(var(--accent)), hsl(var(--gold1)));
  transition: width .35s ease;
}
.boot-txt {
  margin: 0; font-size: .82rem; color: hsl(var(--faint));
  letter-spacing: .04em;
}

/* ---------- 顶栏 ---------- */
.topbar { padding: .7rem 1rem; border-bottom: 0; }
/* 这两个元素是 game.js 直接按 id 抓的（brand-home 绑点击、thanks-row 填名单），
   删掉会抛 null，所以留在 DOM 里、在这儿收起来。
   ·brand：首屏正中已经写了「足一把」，左上角不再重复一遍
   ·thanks：那是主站的爱发电支持者名单，Toy 版整条不出现 */
.brand-off, .thanks-off { display: none !important; }
.brand { font-size: .95rem; }
.top-actions {
  margin-left: auto;
  display: flex; align-items: center; gap: .45rem;
}
/* style.css 那边 .corner-btn 是绝对定位钉在右上角的（那时候右上只有一个按钮）。
   这儿有两个，得让它们回到文档流里排队。 */
.topbar .corner-btn {
  position: static;
  top: auto; right: auto; transform: none;
}
/* 「玩法」那个按钮 game.js 还绑着（$("btn-help")），删元素会抛 null，
   所以留在 DOM 里收起来。设置面板里那一行点下去就是点它。 */
.corner-off { display: none !important; }

/* ---------- 设置 ----------
   2026-08-20：右上角原来是「玩法 + 铃铛」两个，现在合成这一个。
   2026-08-23：⚙ 换成「设置」两个字。齿轮要靠 emoji 字体画（还是那批
   COLRv1 里的字形），字体没到之前那儿是个空位或者豆腐块 —— 顶栏唯一的
   入口不该有这种不确定；而且一个符号得让人猜，两个字不用。

   必须带 .topbar 前缀 —— 上面那条 `.topbar .corner-btn { position: static }`
   特异性是 (0,2,0)，光写 `.corner-gear { position: relative }` (0,1,0) 压不住，
   压不住的后果不是「样式差一点」，是里头那个 absolute 的红点找不到定位祖先，
   直接飞到屏幕右上角去（上一版铃铛就是这么飞的）。 */
.topbar .corner-gear { position: relative; }
/* 红点只有一个来源：有没读过的更新公告。看过就没了 ——
   跟上一版铃铛不同（那个是「看过变灰但留着」），因为公告在设置里
   永远找得回来，不需要拿红点当书签。
   挂在按钮右上角**外沿**：齿轮那版红点是压在图标内的空白上，
   换成两个字之后里头没有空白了，压进去就落在「设」字上。 */
.gear-dot {
  position: absolute; top: -3px; right: -3px;
  width: 6px; height: 6px; border-radius: 50%;
  background: hsl(var(--failure));
  box-shadow: 0 0 0 2px hsl(var(--card));
  opacity: 0; transition: opacity .2s;
}
.corner-gear.has-dot .gear-dot { opacity: 1; animation: gearPulse 2.4s ease-in-out infinite; }
@keyframes gearPulse {
  0%, 100% { box-shadow: 0 0 0 2px hsl(var(--card)), 0 0 0 3px hsl(var(--failure) / .45); }
  50%      { box-shadow: 0 0 0 2px hsl(var(--card)), 0 0 0 7px hsl(var(--failure) / 0); }
}

/* ============================================================
   首屏
   ============================================================ */
/* style.css 给 #app 钉了 max-width:460（--maxw），首屏那一屏会缩成中间一条，
   两边全黑，队徽底噪也跟着只铺那一条。首页要的是整屏。

   用 #app.intro-wide 而不是自己加类：这个类是 game.js 的 show() 在切到
   view-intro 时自己挂上去的（game.js:3117），跟着它走就不会和它打架。
   其余页面（身份、生涯、结算）保持 style.css 原来的宽度，一个字没动。 */
#app.intro-wide { max-width: none; padding: 0; }

.view.intro {
  max-width: none;
  padding: 0;
}
.stage {
  /* ---------- 这一屏的尺度 ----------
     首屏上的每样东西（球衣、光晕、标题、家底一行、按钮、它们之间的间距）
     都是这个 --u 的倍数，没有一处自己写死的 px 或 vw。

     为什么：上一版每样东西各写各的 vw（球衣 54vw / 26vw、标题 9.2vw / 4.4vw），
     宽度一变全都跟着变大，可**屏幕高度没有跟着涨**。结果是
     1280×720 的笔记本上首屏内容 788px 高，比这一屏还高，而首页又是
     overflow:hidden 的 —— 「更多选项」整条被切掉；436×640 这种窄窗口里
     球衣一个人吃掉 42% 的屏高。宽屏和窄窗口是同一个毛病的两种长相。

     min(宽的一份, 矮的一份) 取小：屏幕一矮，整屏自动收一档。
     要整体再大一点或再小一点，只动下面这三个断点里的 --u，别去动倍数。 */
  --u: clamp(6px, min(2.05vw, 1.05vh), 11px);

  position: relative;
  min-height: calc(100vh - 3.4rem);
  min-height: calc(100dvh - 3.4rem);
  display: flex; flex-direction: column; align-items: center;
  justify-content: center;
  gap: 0;
  padding: calc(var(--u) * 2.4) calc(var(--u) * 2.2) calc(var(--u) * 4.8);
  text-align: center;
  overflow: hidden;
}

/* ---------- 底噪三层 ---------- */
/* 队徽拼贴。toy-ui.js 现填 <i> 进去，每个是一张队徽。
   透明度压到几乎看不见 —— 要的是「这屏后面有东西」，不是让人去认队徽。 */
/* 这一层刻意是**静止**的，而且不带 filter。
   第一版给它挂了 grayscale + contrast 再加一段 90s 的漂移动画，
   结果整页卡到脚本都注入不进去：全屏的 filter 层每一帧都要重新合成，
   底下还叠着 grain 的 mix-blend-mode 和 halo 的 blur，三层昂贵合成一起动。
   底噪要的是密度，不是动效 —— 动的只留球衣和按钮那两处小面积的。 */
.stage-crests {
  position: absolute; inset: -4%;
  z-index: 0; pointer-events: none;
  display: grid;
  /* 格子大小跟着视口连续走。用断点跳变的话，平板那一档（600~760）
     会拿到给桌面定的 104px，一行只排得下五六个，大到能读出队名 ——
     底噪一旦读得出内容就不是底噪了，是背景图。 */
  grid-template-columns: repeat(auto-fill, minmax(clamp(56px, 7vw, 104px), 1fr));
  gap: clamp(14px, 3.5vw, 46px);
  /* 从顶铺到底，不居中：居中意味着格子不够时上下留白，正好露出底。 */
  align-content: start; justify-items: center;

  /* 去色是必须的：豪门徽章饱和度都很高（红蓝黄各一块），不去色会跟球衣抢眼睛。

     但**不能靠 brightness 提亮**。曼城、皇马这类内部大面积浅色的徽章，
     brightness(1.7) 会把内部整个推到白，只剩一个空心圆圈 —— 细节全丢，
     那两枚在 1707px 的屏上尤其明显。改成压对比度：暗部提上来、亮部压下去，
     整枚徽章落在中灰一档，线条和字都还在。不够亮就用 opacity 补，
     那个是整体调透明，不会削掉细节。

     filter 不带动画：静态的只在首次合成和 resize 时算一遍。 */
  opacity: .14;
  filter: grayscale(1) contrast(.45) brightness(1.25);

  /* 中间挖空。「要明显」和「别跟球衣抢」是一对矛盾：整层调淡则四周空，
     调浓则正中间那几枚压在球衣和标题背后，一屏吵成一片。
     用一张径向遮罩把中心那块渐隐掉 —— 边缘保持明显，
     球衣和字底下干干净净，两头都占。 */
  -webkit-mask-image: radial-gradient(ellipse 46% 42% at 50% 46%, transparent 25%, #000 78%);
  mask-image: radial-gradient(ellipse 46% 42% at 50% 46%, transparent 25%, #000 78%);
}
.stage-crests i {
  display: block; width: 100%; aspect-ratio: 1;
  background: center / contain no-repeat;
}

/* 球场夜灯。两盏：中心一盏冷绿的压在球衣背后，底下一盏暖金的托着按钮，
   外面再罩一圈暗角把注意力收到中间。 */
.stage-glow {
  position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background:
    radial-gradient(58% 44% at 50% 30%, hsl(var(--accent) / .17), transparent 68%),
    radial-gradient(48% 32% at 50% 88%, hsl(var(--gold1) / .10), transparent 72%),
    radial-gradient(125% 85% at 50% 0%,  hsl(var(--scrim) / .5), transparent 62%);
}

/* 颗粒。大面积平色是「干」的另一半原因，这层把它压住。
   SVG 噪声比位图省事，也不用多一个请求。 */
/* 同上：没有 mix-blend-mode。全屏混合模式会把它下面所有东西拖进
   每一帧的重新合成，代价和收益完全不成比例 —— 一层淡噪点而已。 */
.stage-grain {
  position: absolute; inset: 0; z-index: 0; pointer-events: none;
  opacity: .05;
  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='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='120' height='120' filter='url(%23n)' opacity='.5'/%3E%3C/svg%3E");
}

/* 首屏内容整体压在底噪上面 */
.stage > *:not(.stage-crests):not(.stage-glow):not(.stage-grain) {
  position: relative; z-index: 1;
}

/* 右列容器。窄屏下它就是一条普通的居中列，跟原来一样；
   宽屏下它是两列 grid 的右格（见文件末尾的 min-width:1024 那一段）。 */
.stage-side {
  display: flex; flex-direction: column; align-items: center;
  width: 100%;
}

/* ---------- 球衣（主角）---------- */
.jersey-stage {
  position: relative;
  margin-bottom: .5rem;
  animation: riseIn .7s cubic-bezier(.2, .7, .3, 1) both;
}
.jersey-halo {
  position: absolute; left: 50%; top: 46%;
  /* 尺寸是算出来的，所以居中靠 translate 而不是「负一半」的 margin ——
     后者得把整条 calc 再抄一遍。 */
  width: calc(var(--u) * 34); height: calc(var(--u) * 34);
  transform: translate(-50%, -50%);
  border-radius: 50%;
  /* --halo 由 toy-ui.js 跟着当前这件球衣的出身主色换。
     没有 filter: blur —— 径向渐变本身就是软边，再糊一道纯属白给，
     而一个 300px 的 blur 层配上 transition 每次换球衣都要重算一遍。 */
  background: radial-gradient(circle, var(--halo, hsl(var(--accent) / .3)) 0%, transparent 68%);
  opacity: .6;
  transition: background .8s ease;
  pointer-events: none;
}
.jersey-frame {
  position: relative;
  width: calc(var(--u) * 24);
  aspect-ratio: 1;
  margin: 0 auto;
  animation: jerseyFloat 6s ease-in-out infinite;
}
/* 地面投影。亮底上光靠 drop-shadow 勾不出「立着」的感觉 —— 球衣看着像
   贴在墙上。底下垫一小片椭圆暗影，它才落地。
   浮动动画在球衣上，这片影子不跟着动（真实世界里影子也不跟着飘）。 */
.jersey-stage::after {
  content: "";
  position: absolute; left: 50%; bottom: 2.1rem;
  width: 46%; height: 14px;
  transform: translateX(-50%);
  background: radial-gradient(ellipse at center, hsl(var(--scrim) / .55), transparent 70%);
  pointer-events: none;
}
/* 轮播是两张图叠着做 crossfade —— 单张换 src 会闪一下白 */
.jersey-frame img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: contain;
  opacity: 0;
  transition: opacity .6s ease, transform .6s ease;
  transform: scale(.97);
  filter: drop-shadow(0 16px 26px hsl(var(--scrim) / .65));
}
.jersey-frame img.on { opacity: 1; transform: scale(1); }
@keyframes jerseyFloat {
  0%, 100% { transform: translateY(-5px) rotate(-.6deg); }
  50%      { transform: translateY(5px)  rotate(.6deg); }
}
.jersey-cap {
  margin: calc(var(--u) * .9) 0 0;
  /* 字号跟着 --u 走，但两头收口：这是一行地名，屏幕再大也不该长成小标题 */
  font-size: clamp(11px, calc(var(--u) * 1.9), 18px);
  letter-spacing: .16em; text-indent: .16em;
}
#jersey-name {
  color: hsl(var(--muted)); font-weight: 600;
}

/* ---------- 标题 ---------- */
/* 站名两行，同一个字号同一个重量 —— 「足一把」和「生涯模拟器」是一个名字的
   两截，不是「标题 + 说明」。所以 .eyebrow 那套小字、大字距、全大写
   在这儿全部作废（它是给 style.css 的两栏首页设计的）。 */
.stage .hero-title,
.stage .eyebrow {
  margin: 0;
  font-size: calc(var(--u) * 4.07);
  line-height: 1.2;
  font-weight: 800;
  letter-spacing: .01em;
  text-transform: none;
  color: hsl(var(--text));
}
.stage .hero-title {
  margin-top: calc(var(--u) * 1.63);
  animation: riseIn .7s .06s cubic-bezier(.2, .7, .3, 1) both;
}
.stage .eyebrow {
  animation: riseIn .7s .12s cubic-bezier(.2, .7, .3, 1) both;
}
/* 第二行走金色。整句都上渐变会糊，压在一行上才读得出重音。 */
.stage .hero-title em {
  font-style: normal;
  background: linear-gradient(100deg, hsl(var(--gold1)), hsl(var(--gold2)) 55%, hsl(var(--gold1)));
  -webkit-background-clip: text; background-clip: text;
  color: transparent;
}

/* ---------- 家底一行 ---------- */
.stage .intro-facts {
  display: flex; flex-wrap: wrap;
  justify-content: center; align-items: baseline;
  gap: calc(var(--u) * .23) calc(var(--u) * 1.02);
  margin: calc(var(--u) * 1.54) 0 0; padding: 0;
  list-style: none;
  animation: riseIn .7s .18s cubic-bezier(.2, .7, .3, 1) both;
}
.stage .intro-facts li {
  /* style.css 那边这一排是四张卡片（grid + flex-direction:column + 描边）。
     首屏的重量要留给球衣和按钮，所以压成一行文字 —— 但 flex-direction
     必须显式写回 row，不然继承的还是 column，数字和名目会各占一行。 */
  display: flex; flex-direction: row; align-items: baseline; gap: .28em;
  padding: 0; border: 0; background: none;
  font-size: clamp(11px, calc(var(--u) * 1.56), 15px); color: hsl(var(--faint));
}
.stage .intro-facts li b {
  font-size: clamp(13px, calc(var(--u) * 1.9), 19px); font-weight: 700;
  color: hsl(var(--gold1));
  font-variant-numeric: tabular-nums;
}
.stage .intro-facts li span { color: hsl(var(--faint)); }

/* ---------- 主按钮 ---------- */
.hero-cta {
  width: min(100%, calc(var(--u) * 43), 620px);
  margin-top: calc(var(--u) * 2.72);
  animation: riseIn .7s .24s cubic-bezier(.2, .7, .3, 1) both;
}
.btn-hero {
  position: relative;
  width: 100%;
  /* 按钮是唯一要点的东西，两头都收口 —— 屏幕大不该跟着长成横幅，
     屏幕矮也不能缩到点不准（拇指目标 44px 起）。 */
  padding: clamp(14px, calc(var(--u) * 2.3), 24px) 1.2rem;
  font-size: clamp(16px, calc(var(--u) * 2.25), 22px); font-weight: 700;
  letter-spacing: .06em;
  border-radius: calc(var(--radius) - 2px);
  overflow: hidden;
  box-shadow:
    0 10px 26px hsl(var(--scrim) / .5),
    0 0 0 1px hsl(var(--accent) / .35),
    0 0 30px hsl(var(--accent) / .16);
}
/* 光泽扫过。慢、淡、间隔长 —— 它是「这个能点」的提示，不是特效。 */
.btn-hero::after {
  content: ""; position: absolute; top: 0; bottom: 0; left: -60%;
  width: 45%;
  background: linear-gradient(100deg, transparent, hsl(0 0% 100% / .22), transparent);
  transform: skewX(-18deg);
  animation: heroSheen 4.2s ease-in-out infinite;
}
@keyframes heroSheen {
  0%, 62% { left: -60%; }
  86%, 100% { left: 130%; }
}
.btn-hero:active { transform: translateY(1px); }

/* ---------- 更多选项 ---------- */
.more-toggle {
  display: inline-flex; align-items: center; gap: .3em;
  margin: calc(var(--u) * 1.36) auto 0;
  padding: .4rem .7rem;
  background: none; border: 0;
  color: hsl(var(--faint));
  font: inherit; font-size: clamp(12px, calc(var(--u) * 1.5), 16px);
  cursor: pointer;
  border-radius: var(--r-row);
}
.more-toggle:hover { color: hsl(var(--muted)); }
.more-caret { transition: transform .25s ease; font-style: normal; }
.more-toggle[aria-expanded="true"] .more-caret { transform: rotate(180deg); }

.more-panel {
  width: min(100%, calc(var(--u) * 43), 620px);
  margin-top: .5rem;
  display: flex; flex-direction: column; gap: .55rem;
  /* 自己带个底。展开之后它落在中心遮罩的下沿之外，背后的队徽是满亮度的，
     模式按钮和种子框直接压在上面会花得看不清。 */
  padding: .8rem;
  border-radius: var(--r-card);
  background: hsl(var(--card) / .82);
  border: 1px solid hsl(var(--border));
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}
.more-panel.hidden { display: none; }
.more-panel:not(.hidden) { animation: riseIn .35s cubic-bezier(.2, .7, .3, 1) both; }
.more-panel .seg-note {
  margin: 0; text-align: center;
  font-size: .74rem; color: hsl(var(--faint));
}

/* ---------- 回访才出现的两个 ---------- */
.btn-stack#return-stack {
  width: min(100%, 340px);
  margin-top: .9rem;
  display: flex; gap: .5rem;
}
#return-stack .btn { flex: 1; font-size: .84rem; padding: .62rem .5rem; }
#return-stack .btn.hidden { display: none; }
/* 两个都藏着的时候整行不占位 */
#return-stack:not(:has(.btn:not(.hidden))) { display: none; }

/* 主站那个第四按钮。设置里那行公告顶了它的位子，但 game.js 还要往里填字。 */
#home-news[aria-hidden="true"] { display: none !important; }
.hero-art-off { display: none !important; }

@keyframes riseIn {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}

/* ---------- 页脚 ---------- */
/* 首页那一屏底下不该再拖一段页脚 —— 主站是滚动页，Toy 是一屏。
   署名挪进「玩法」弹窗里那种做法试过更差（没人会去点玩法看署名），
   所以留在页面上，但只在首页之外的页面显示。 */
body:not(.on-intro) .footer { display: block; }
body.on-intro .footer { display: none; }
.footer { padding-bottom: 1.4rem; }
.footer-by { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: .5rem; }

/* ---------- 作者卡片 ----------
   第一版是署名后面挂一个绿色小胶囊，难看：它跟旁边那行灰字既不同色也不同形，
   像是从别的页面掉进来的。

   三个已上线的 Toy（万梗捏 / 灵墟 / 大狗）都不用孤零零一个关注按钮，
   而是把作者做成一块**有身份的卡片**（头像 + 名字 + 动作），
   关注只是卡片上的那个动作。这儿照这个来。

   注意它整块可能不出现：未登录（status 是 unauthorized）时不摆 ——
   给没登录的人看「未关注」，点了也只会跳到登录页。 */
.author-card {
  display: inline-flex; align-items: center; gap: .6rem;
  padding: .5rem .55rem .5rem .6rem;
  border-radius: 999px;
  background: hsl(var(--card));
  border: 1px solid hsl(var(--border));
  text-align: left;
}
.author-av {
  width: 1.9rem; height: 1.9rem; flex-shrink: 0;
  display: grid; place-items: center;
  border-radius: 50%;
  background: hsl(var(--row-hi));
  font-size: .95rem;
}
/* 真头像（getAuthorProfile 拿回来的）。overflow:hidden 让方图片被圆框裁圆，
   img 必须显式给宽高 —— 原来那一格的大小是 font-size 撑起来的（里面是个 ⚽），
   换成 <img> 之后没人管它，浏览器会按原图铺开，把整条页脚顶宽。 */
.author-av.has-img { background: hsl(var(--row-hi)); overflow: hidden; padding: 0; }
.author-av.has-img img {
  width: 100%; height: 100%; display: block;
  object-fit: cover; border-radius: 50%;
}
.author-meta { display: flex; flex-direction: column; line-height: 1.25; }
.author-name { font-size: .8rem; font-weight: 650; color: hsl(var(--text)); }
.author-sub { font-size: .68rem; color: hsl(var(--faint)); }
.author-btn {
  margin-left: .35rem;
  padding: .34rem .8rem;
  border-radius: 999px; border: 0;
  background: hsl(var(--accent)); color: hsl(var(--accent-on));
  font-family: inherit; font-size: .76rem; font-weight: 650;
  cursor: pointer; white-space: nowrap;
}
.author-btn:hover { filter: brightness(1.08); }
.author-btn.followed {
  background: hsl(var(--row-hi)); color: hsl(var(--faint));
  cursor: default;
}

/* ---------- 云存档状态 ---------- */
/* 一行小字，只在状态变化时冒一下头。存档是这类游戏最容易让人骂街的地方，
   「存哪儿了」得说清楚，但不值一个常驻角标。 */
.toy-toast {
  position: fixed; left: 50%; bottom: 1.5rem;
  transform: translate(-50%, 12px);
  z-index: 80;
  padding: .45rem .9rem;
  border-radius: 999px;
  background: hsl(var(--row-hi)); border: 1px solid hsl(var(--border-strong));
  color: hsl(var(--muted)); font-size: .78rem;
  opacity: 0; pointer-events: none;
  transition: opacity .3s ease, transform .3s ease;
}
.toy-toast.on { opacity: 1; transform: translate(-50%, 0); }

/* ---------- 窄屏 ---------- */
/* 窄屏：队徽格子跟着缩。104px 的格子在 430 宽的屏上一行只排得下三个，
   每个都大到能读出队名 —— 底噪读得出内容就不是底噪了，是背景图。 */
@media (max-width: 560px) {
  /* 小屏上略压一档：屏幕小，同样的透明度看着更满 */
  .stage-crests { opacity: .11; }
}

/* 原来这儿有一段「手机上放大一档」（球衣 54vw、标题 9.2vw）和一段
   「矮屏让位」（球衣 22vh）。两段都撤了 —— --u 的 min(2.05vw, 1.05vh)
   把它们要办的事一起办了：高瘦的手机吃 vw 那一份，窗口一矮自动切到 vh 那一份，
   中间不用再挑断点。留着的话反而会盖掉 --u，窄矮窗口又变回撑满整屏。 */

/* 平板一档：这个宽度上 vw 那一份会算得偏大（820 宽 → 16.8px），
   压到跟高度同一个量级，免得一台 820×1180 的 iPad 上球衣长到 400。 */
@media (min-width: 760px) {
  .stage { --u: clamp(8px, min(1.05vw, 1.05vh), 12px); }
}

/* ---------- 动效开关 ---------- */
@media (prefers-reduced-motion: reduce) {
  .jersey-frame, .stage-crests, .boot-ball { animation: none; }
  .btn-hero::after { display: none; }
  .jersey-stage, .stage .eyebrow, .stage .hero-title,
  .stage .intro-facts, .hero-cta { animation: none; }
}

/* ============================================================
   身份四步：铺开
   ------------------------------------------------------------
   主站是手机优先的一条窄栏（#app 460px），四步都在里面竖着滚。
   放到 PC 上就是「所有东西挤在正中间一条」，两边全空。
   ============================================================ */

/* 首页(.intro-wide)和生涯页(.wide)各有各的宽度，剩下的那几屏
   —— 身份四步、历史档案、结局图鉴 —— 都是卡片网格，760 才铺得开。
   不敢再宽：这几屏里还有整句的说明文字，一行超过四十来个字就难读了。 */
#app:not(.wide):not(.intro-wide) { max-width: 760px; }

/* ---------- 出身：横向网格 ---------- */
/* 原来是十一个出身竖着排，一屏看不完要滚。改成网格之后一眼全在这儿 ——
   「有多少种出身」本身就是这一步要传达的信息。 */
.pick-list {
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: .45rem;
}
/* 格子里改成竖排：球衣在上、名字在下。横排(flex row)在 160px 宽的格子里
   会把描述挤成两三行，高度参差不齐，网格就散了。 */
.pick {
  flex-direction: column; align-items: center; gap: .4rem;
  padding: .8rem .6rem .7rem;
  text-align: center;
  height: 100%;
}
.pick .j-mini, .pick svg { flex-shrink: 0; }
.pick-main { flex: none; width: 100%; }
.pick-name { font-size: .9rem; }
.pick-desc {
  font-size: .72rem; line-height: 1.45;
  /* 描述长短不一，截到两行保证每格一样高 */
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
/* 选中态原来是 inset 描边，在网格里不够明显（格子之间挨得近），加一档底色 */
.pick.selected {
  background: hsl(var(--row-hi));
  box-shadow: inset 0 0 0 1.5px hsl(var(--accent));
}

/* 窄屏两列。一列的话又变回要滚的长条了 */
@media (max-width: 520px) {
  .pick-list { grid-template-columns: repeat(2, 1fr); gap: .4rem; }
  .pick { padding: .7rem .45rem .6rem; }
  .pick-desc { font-size: .68rem; }
}

/* ============================================================
   宽屏：还是竖排，只是放大
   ------------------------------------------------------------
   试过左右两栏（球衣在左、标题和按钮在右），撤了 —— 一屏一个动作的东西
   分成两栏之后，眼睛要先横着找一遍才知道该点哪儿。竖排的好处是
   球衣→标题→按钮天然是一条从上往下的路。

   PC 上「空」的解法不是分栏，是把东西放大：球衣更大、标题更大。
   ============================================================ */
@media (min-width: 1024px) {
  /* PC 上 --u 换一套系数：宽屏的 vw 太富裕（1400 宽 → 28px），
     光按它算球衣能长到 670；高度那一份才是真正的约束。
     1024×768→9.2  1280×720→9.7  1366×768→10.4
     1400×900→12.2  1512×850→11.5  1920×1080→14.6 */
  .stage {
    --u: clamp(9px, min(.9vw, 1.35vh), 16px);
    /* 横向留白单独给，不走 --u：两侧那片队徽底噪是有意留的舞台，
       不该跟着屏幕高度一起缩。 */
    padding: calc(var(--u) * 2.4) clamp(2rem, 6vw, 5rem) calc(var(--u) * 3);
  }
}

/* 原来 1600 那一档（球衣 460、标题 5rem）撤了：
   --u 的 clamp 上限 16px 已经管住「再宽也不能无限放大」这件事，
   多一档只会跟它打架。 */

/* ---------- 身份四步：占满这一屏 ---------- */
/* 四步的内容都不长（一格网格、一块球场、两个输入框），在 PC 上摞在页面顶部，
   底下空掉大半屏。让它整体纵向居中，空白就匀到上下两边去了。
   底部留出 actionbar（返回/继续）的高度。 */
#view-identity {
  min-height: calc(100dvh - 3.4rem - 5.5rem);
  display: flex; flex-direction: column; justify-content: center;
}
/* 网格本身也放大一档：一行四个、格子更宽，球衣看得清 */
@media (min-width: 760px) {
  .pick-list { grid-template-columns: repeat(4, 1fr); gap: .55rem; }
  .pick { padding: 1rem .7rem .9rem; }
  .pick-name { font-size: .95rem; }
}

/* 身份流程里不显示页脚。
   那几步底下钉着 actionbar，页脚夹在内容和 actionbar 中间，
   既碍事又把「返回/继续」的位置说不清楚。署名在首屏和结算页各有一次，够了。 */
body.on-identity .footer { display: none; }

/* ---------- 位置 + 身份：并成一屏 ----------
   宽屏下 game.js（经 build.js 的补丁）把原来第 2 步和第 3 步渲染在一起：
   左边是名字/号码/惯用脚和那件会实时改字的球衣，右边是球场棋盘。

   这两步本来就该一起做 —— 球衣上印的号码，和在场上站哪儿，是同一张
   球员卡的正反面。分成两屏之后，改完名字还得再点一次「继续」才看得到位置。

   窄屏不合并（补丁里 identWide() 卡在 900px）：球场在手机上已经占满宽度。 */
.ident-split {
  display: grid;
  grid-template-columns: minmax(300px, 1fr) minmax(300px, 380px);
  gap: clamp(1.5rem, 4vw, 3rem);
  align-items: start;
}
.ident-side { min-width: 0; }
.ident-main { min-width: 0; }
/* 并屏之后球衣不用再撑那么大 —— 它旁边还有一整块球场要看 */
.ident-split .jersey-wrap { padding: .2rem 0 .6rem; }
.ident-split .jersey { width: 58%; max-width: 190px; }
.ident-split .pitch { max-width: none; }
.ident-split .pitch-hint { text-align: center; }

/* ---------- 心仪球队：高度定死 ----------
   联赛之间队数差得很远（英超 16 支，K联赛 2 支）。原来网格是按内容长的，
   点一下联赛标签整页就缩一下或者弹一下，底下的「继续」跟着跳 ——
   人正要去点的按钮自己挪了位置。

   所以给网格一个固定高度 + 内部滚动：队多的滚，队少的留白，
   外面这一屏的高度**永远不变**。留白比跳动好。 */
.dream-grid {
  height: 19.5rem;
  overflow-y: auto;
  align-content: start;
  /* 滚动条不占位，免得有滚动条和没滚动条时列宽又差一档 */
  scrollbar-width: thin;
}
.dream-grid::-webkit-scrollbar { width: 6px; }
.dream-grid::-webkit-scrollbar-thumb {
  background: hsl(var(--border-strong)); border-radius: 3px;
}

/* 「随缘」那块的说明文字长短不一（没选是一句，选了队要写队名 + 可能加一句
   出国费用的提醒），一样会把下面顶下去。固定两行。 */
.dream-none-d {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
  min-height: 2.2em;
}

/* 联赛标签横条：固定一行高，别因为换行变两行 */
.lg-tabs { flex-wrap: nowrap; }

@media (min-width: 1024px) {
  .dream-grid { height: 22rem; }
}

/* ============================================================
   首页：顶栏浮起来，一屏钉死
   ------------------------------------------------------------
   之前 .stage 的高度是 calc(100vh - 3.4rem)，顶栏在它上面单独占一条。
   于是队徽和光晕都从顶栏下面才开始 —— 最上面那 3.4rem 是一条什么都没有的
   纯底色，横在整屏之上，就是那条「黑条」。

   改法：让 stage 占满整个视口，顶栏脱离文档流浮在它上面、背景透明。
   ❤ 和 ⚙ 这两个按钮自己是有底色的胶囊，浮在队徽上照样看得清。
   ============================================================ */
body.on-intro .topbar {
  position: absolute; top: 0; left: 0; right: 0;
  z-index: 5;
  background: transparent;
  border-bottom: 0;
  padding: .8rem 1rem;
}
body.on-intro .stage {
  min-height: 100vh;
  min-height: 100dvh;
}

/* 首页不许滚。它是「一眼看完、一下点掉」的一屏，
   底下多出来半屏可拉的空白，会让人以为下面还有东西。 */
body.on-intro { overflow: hidden; }
body.on-intro #app { padding-bottom: 0; }

/* 展开「更多选项」之后内容会变高，而这一屏又不许滚 —— 所以展开时
   把球衣和标题收一档，把地方让给面板。收起来就还原。 */
/* 展开时也走 --u，只是倍数换一套小的 —— 展开要多塞一个分段器和一个输入框。 */
.stage.more-open .jersey-frame { width: calc(var(--u) * 17); }
.stage.more-open .jersey-halo {
  width: calc(var(--u) * 24); height: calc(var(--u) * 24);
}
.stage.more-open .hero-title,
.stage.more-open .eyebrow { font-size: calc(var(--u) * 3); }
.stage.more-open .jersey-cap { font-size: clamp(10px, calc(var(--u) * 1.5), 15px); }
.stage.more-open .intro-facts { margin-top: calc(var(--u) * .9); }
.stage.more-open .hero-cta { margin-top: calc(var(--u) * 1.6); }

/* ---------- 心仪球队：把地方用起来 ---------- */
/* 上一版给网格钉了 19.5rem 的高度，跳变是没了，但一格才那么点大，
   一屏里空着一大半。这一步是「找自己主队」，队徽该看得清。 */
@media (min-width: 760px) {
  /* 这一屏比别的几步更需要横向空间：一行多排两三个队，行数就少两行 */
  #app:not(.wide):not(.intro-wide) { max-width: 900px; }
  /* 出身那一格仍旧固定四列，不跟着变五列 —— 四列时每格的宽度正好放得下
     两行描述，五列就要挤成三行了 */
  .pick-list { grid-template-columns: repeat(4, 1fr); }
}
@media (min-width: 1024px) {
  .dream-grid {
    height: 30rem;
    grid-template-columns: repeat(auto-fill, minmax(8.4rem, 1fr));
    gap: .55rem;
  }
  .dream-team { padding: .8rem .4rem; font-size: .8rem; }
  .dream-team .crest, .dream-team .crest-pic, .dream-team svg {
    width: 2.6rem; height: 2.6rem;
  }
}

/* ---------- 别把固定高度压回去 ---------- */
/* #view-identity 是 flex column，#step-body 作为 flex item 默认 flex-shrink:1 ——
   内容比可用高度高的时候，浏览器会去压它，于是上面给 .dream-grid 钉的
   30rem 被压成了 16rem，固定高度形同虚设（跳变又回来了）。
   钉住不许压：真放不下就让它溢出，那是滚动条该管的事。 */
#view-identity > #step-body {
  flex-shrink: 0;
  /* 底部留出 actionbar（返回/继续）的高度 + 余量。
     身份页内容超高时页面会滚动，但 390×844 这类屏内容恰好一屏高、页面
     滚不动 —— 出身列表最后一项（四川·巴蜀）下半截正好落进固定底部的
     actionbar 里被盖住。5rem > actionbar 高(约 4.3rem)，滚到底时最后一项
     完整露在「返回/继续」上方。 */
  padding-bottom: calc(5rem + env(safe-area-inset-bottom, 0px));
}

/* ---------- 心仪球队：格子填满那块地 ---------- */
/* 网格高度是按「队最多的联赛」（英超 16 支）定死的。队少的联赛底下会空一截，
   那是不跳变必付的代价。但格子本身该长到把这块地填住 —— 
   英超三行正好铺满，队徽也顺带看得更清楚。 */
@media (min-width: 1024px) {
  .dream-grid { height: 27rem; gap: .6rem; }
  .dream-team {
    padding: 1.1rem .5rem;
    gap: .5rem;
    font-size: .84rem;
  }
  .dream-team .crest, .dream-team .crest-pic, .dream-team svg {
    width: 3.4rem; height: 3.4rem;
  }
}

/* ============================================================
   金球 / 世界杯 / 欧冠：得到的那一下
   ------------------------------------------------------------
   配的是 toy-cheer.js。整块全屏遮罩，2.8 秒自动退，点一下提前跳过。

   只用 transform / opacity 做动画 —— 这东西跑在手机的 iframe 沙箱里，
   动 width / top / filter 那几样每帧都要重排，掉帧就白做了。
   金屑是 12 个 <i>，不上 canvas：为这点效果拉一块画布不值。

   层级 120：上面还有 boot 遮罩 90、底部操作条 50，得盖住它们。
   ============================================================ */
.cheer {
  position: fixed; inset: 0; z-index: 120;
  display: flex; align-items: center; justify-content: center;
  /* 两层：底下一块几乎不透的暗底把整页压住，上面才是那点金色的光。
     写成单层渐变的话，为了让中心透出光，边缘也跟着透 —— 结果是屏幕
     四角还看得见底下的按钮和正文，一眼就脏。 */
  background:
    radial-gradient(115% 85% at 50% 44%,
      hsl(var(--gold2) / .20) 0%, hsl(var(--gold2) / .07) 38%, transparent 62%),
    hsl(var(--scrim) / .98);
  opacity: 0;
  transition: opacity .28s ease;
  cursor: pointer;
  overflow: hidden;
  -webkit-tap-highlight-color: transparent;
}
.cheer.on  { opacity: 1; }
/* 淡出这一下必须停掉命中测试，否则点击会穿到底下战报的「继续」上 */
.cheer.out { opacity: 0; pointer-events: none; }

/* ---------- 背后那束光 ---------- */
/* conic-gradient 画一圈射线再整体慢转。用 background 而不是几十个 div：
   一个合成层就够，转起来不碰布局。 */
.cheer-rays {
  position: absolute; left: 50%; top: 46%;
  width: 190vmax; height: 190vmax;
  transform: translate(-50%, -50%) rotate(0deg) scale(.2);
  background: repeating-conic-gradient(from 0deg,
      hsl(var(--gold1) / .22) 0deg 3.5deg,
      transparent 3.5deg 11deg);
  opacity: 0;
  -webkit-mask-image: radial-gradient(closest-side, #000 8%, transparent 58%);
          mask-image: radial-gradient(closest-side, #000 8%, transparent 58%);
}
.cheer.on .cheer-rays {
  animation: cheerRays 3s cubic-bezier(.16, .8, .3, 1) both;
}
@keyframes cheerRays {
  0%   { opacity: 0; transform: translate(-50%, -50%) rotate(0deg) scale(.2); }
  22%  { opacity: 1; }
  100% { opacity: .85; transform: translate(-50%, -50%) rotate(26deg) scale(1); }
}

/* ---------- 奖杯脚下的光晕 ---------- */
.cheer-glow {
  position: absolute; left: 50%; top: 46%;
  width: 30rem; height: 30rem; margin: -15rem 0 0 -15rem;
  border-radius: 50%;
  background: radial-gradient(circle,
      hsl(var(--gold1) / .42) 0%, hsl(var(--gold1) / .16) 38%, transparent 68%);
  opacity: 0;
  transform: scale(.35);
}
.cheer.on .cheer-glow {
  animation: cheerGlow 2.8s cubic-bezier(.16, .8, .3, 1) both;
}
@keyframes cheerGlow {
  0%   { opacity: 0;   transform: scale(.35); }
  18%  { opacity: 1;   transform: scale(1.06); }
  40%  { opacity: .78; transform: scale(1); }
  100% { opacity: .6;  transform: scale(1.04); }
}

/* ---------- 正文 ---------- */
.cheer-body {
  position: relative; z-index: 2;
  text-align: center;
  padding: 0 1.5rem;
  max-width: 30rem;
}

.cheer-cup {
  position: relative;
  display: inline-block;
  margin-bottom: 1.1rem;
}
.cheer-cup img {
  display: block;
  width: auto; height: 11.5rem;
  max-width: 62vw;
  /* 阴影只画一层往下的投影：暗底上让一样东西「站住」不能靠提亮度 */
  filter: drop-shadow(0 .9rem 1.6rem hsl(var(--scrim) / .75))
          drop-shadow(0 0 2.4rem hsl(var(--gold1) / .45));
  opacity: 0;
  transform: translateY(2.4rem) scale(.82);
}
.cheer.on .cheer-cup img {
  animation: cheerCup 1.05s cubic-bezier(.18, 1.15, .35, 1) both;
}
@keyframes cheerCup {
  0%   { opacity: 0; transform: translateY(2.4rem) scale(.82); }
  55%  { opacity: 1; transform: translateY(-.35rem) scale(1.04); }
  100% { opacity: 1; transform: translateY(0) scale(1); }
}

/* 一道光从杯身扫过。斜着的窄白带，靠 translateX 走完就完事 */
/* 关键的一行是 mask —— 拿奖杯图自己的 alpha 当遮罩。

   不加 mask 的话，这道光扫的是 <img> 的**边界框**：奖杯是抠出来的不规则
   剪影，边界框比杯身宽出一大截（大力神杯尤其明显，杯颈两侧全是透明），
   于是玩家看到的是一个长方形的亮块从屏幕上划过去，跟奖杯没关系。
   挂上 mask 之后，光只落在杯子实体上 —— 变成杯身的反光，而不是一块贴片。

   mask-image 的 url 由 JS 内联成 --cup 变量（每座杯子的图不一样）。
   mask-size: contain + center 跟 <img> 的 height/width:auto 摆法一致，
   两边的可见区域正好重合。 */
.cheer-sheen {
  position: absolute; inset: 0;
  overflow: hidden;
  pointer-events: none;
  -webkit-mask-image: var(--cup); mask-image: var(--cup);
  -webkit-mask-size: contain;     mask-size: contain;
  -webkit-mask-position: center;  mask-position: center;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
}
/* 这道光返工过一次，原因值得记下来。

   第一版：left:-60%、width:28%、translateX(0 → 420%)、opacity 在 15% 冲到 1
   然后一路衰减到 0。看上去没毛病，实际是一帧都看不见 ——
   **translateX 的百分比是相对元素自己的宽度算的**，不是容器。
   28% 宽的光带走 420%，只挪了 1.18 个容器宽；而它起步在容器左外侧，
   于是 opacity 最高的那一刻（15%）它还在 overflow:hidden 外面，
   等它挪到杯身上（约 68%）时 opacity 已经掉到 0.19 了。
   收窄光带的同时把行程也收短了，这一点当时没算。

   现在：行程按「起点在左外、终点在右外」倒推（-165% → 350%，
   配 46% 的宽度正好扫穿容器），opacity 改成梯形 —— 起来之后**保持**到
   75% 再落，覆盖住它经过杯身的整段。时长也从 1.1s 加到 1.4s。

   mix-blend-mode: screen 是配着 mask 用的：光落在金色杯身上要「提亮」，
   而不是盖一层白纱。 */
.cheer-sheen::before {
  content: "";
  position: absolute; top: -25%; left: 0;
  width: 46%; height: 150%;
  background: linear-gradient(100deg,
      transparent 0%, hsl(45 92% 94% / .85) 50%, transparent 100%);
  transform: skewX(-18deg) translateX(-165%);
  opacity: 0;
  mix-blend-mode: screen;
}
.cheer.on .cheer-sheen::before {
  animation: cheerSheen 1.4s .5s cubic-bezier(.35, 0, .5, 1) both;
}
@keyframes cheerSheen {
  0%   { opacity: 0; transform: skewX(-18deg) translateX(-165%); }
  16%  { opacity: 1; }
  75%  { opacity: 1; }
  100% { opacity: 0; transform: skewX(-18deg) translateX(350%); }
}

/* ---------- 三行字 ---------- */
.cheer-tag {
  font-size: .82rem; letter-spacing: .1em;
  color: hsl(var(--muted));
  margin-bottom: .35rem;
}
.cheer-title {
  font-size: 2.4rem; font-weight: 800;
  line-height: 1.15;
  /* 金色渐变字。background-clip 不生效的老 webview 会回退到 --gold1 的实色，
     所以 color 得先写上，不能只靠 transparent */
  color: hsl(var(--gold1));
  background: linear-gradient(180deg, hsl(45 85% 72%), hsl(var(--gold2)));
  -webkit-background-clip: text;
          background-clip: text;
  -webkit-text-fill-color: transparent;
  text-shadow: 0 .15rem 1.2rem hsl(var(--gold2) / .35);
}
.cheer-meta {
  margin-top: .45rem;
  font-size: .95rem;
  color: hsl(var(--muted));
}

.cheer.on .cheer-tag   { animation: cheerRise .6s .55s cubic-bezier(.2, .7, .3, 1) both; }
.cheer.on .cheer-title { animation: cheerRise .7s .62s cubic-bezier(.2, .7, .3, 1) both; }
.cheer.on .cheer-meta  { animation: cheerRise .6s .74s cubic-bezier(.2, .7, .3, 1) both; }
@keyframes cheerRise {
  from { opacity: 0; transform: translateY(.9rem); }
  to   { opacity: 1; transform: none; }
}

/* ---------- 金屑 ---------- */
/* left / 大小 / 快慢由 JS 内联（Math.random，绝不能借引擎的 rnd） */
.cheer-dust {
  position: absolute; inset: 0;
  pointer-events: none; overflow: hidden;
}
.cheer-dust i {
  position: absolute; bottom: 28%;
  border-radius: 50%;
  background: hsl(45 92% 78%);
  box-shadow: 0 0 .55rem hsl(var(--gold1) / .95);
  opacity: 0;
}
.cheer.on .cheer-dust i {
  animation-name: cheerDust;
  animation-timing-function: cubic-bezier(.3, .5, .5, 1);
  animation-fill-mode: both;
  /* 循环，不是喷一次。喷一次的话每粒只活 1.6~2.7 秒，而卡片要摆 2.8 秒 ——
     后半程屏幕上一粒都不剩，等于金屑只在前半段存在。
     循环 + 每粒各自的 delay/duration（JS 内联的随机值）叠出来的是
     一条持续往上走的流，任何一帧上都有七八粒在。 */
  animation-iteration-count: infinite;
}
@keyframes cheerDust {
  /* 亮度撑到七成路程再落。原来是 12% 见顶、一路线性淡到 0，
     结果任何一帧上都只看得见两三粒 —— 屏幕上像落了灰，不像在庆祝。 */
  0%   { opacity: 0;   transform: translateY(0) scale(.55); }
  10%  { opacity: 1; }
  70%  { opacity: .9;  }
  100% { opacity: 0;   transform: translateY(-18rem) scale(1); }
}

/* ---------- 跳过提示 ---------- */
.cheer-skip {
  position: absolute; left: 0; right: 0;
  bottom: calc(2.2rem + env(safe-area-inset-bottom, 0px));
  text-align: center;
  font-size: .8rem; letter-spacing: .08em;
  color: hsl(var(--faint));
}
.cheer.on .cheer-skip { animation: cheerRise .5s 1.35s ease both; }

/* ---------- 窄屏 ---------- */
@media (max-width: 480px) {
  .cheer-cup img   { height: 8.6rem; }
  .cheer-title     { font-size: 1.9rem; }
  .cheer-glow      { width: 22rem; height: 22rem; margin: -11rem 0 0 -11rem; }
}

/* ---------- 关了动效的那一档 ---------- */
/* 位移和旋转全撤掉，只留淡入 —— 该看见的（哪座奖杯、哪一年）一样不少。
   .calm 是 JS 按 prefers-reduced-motion 挂的，媒体查询这条是双保险：
   系统开关在动画播到一半时被改掉也吃得住。 */
.cheer.calm .cheer-rays,
.cheer.calm .cheer-dust,
.cheer.calm .cheer-sheen { display: none; }
.cheer.calm .cheer-cup img,
.cheer.calm .cheer-glow,
.cheer.calm .cheer-tag,
.cheer.calm .cheer-title,
.cheer.calm .cheer-meta,
.cheer.calm .cheer-skip {
  animation: none !important;
  opacity: 1; transform: none;
}
@media (prefers-reduced-motion: reduce) {
  .cheer-rays, .cheer-dust, .cheer-sheen { display: none; }
  .cheer *, .cheer::before, .cheer::after {
    animation: none !important;
    opacity: 1; transform: none;
  }
  .cheer-glow { opacity: .6; }
}

/* ============================================================
   关注 / 三连：入口、清单、结算页横幅
   ------------------------------------------------------------
   由 toy-support.js 全部动态插入，index.html 里没有对应的 DOM。

   配色上破了一处规矩、只破这一处：这一块用 B 站那个粉（--sup）当强调色，
   而不是全站的墨绿 --accent。理由是它说的不是游戏里的事 ——「关注」「三连」
   是站外的动作，跟绿色那一套（能力、主按钮、选中项）混在一起会被当成
   游戏流程的一部分点进去。底色和分割线一格没动，还是 --card / --border。

   顶栏那个心是**常驻**的：三个已上线的 Toy 都把作者入口摆在第一屏够得着的
   地方，而这个游戏的页脚在首页根本不显示。
   ============================================================ */

/* ---------- 顶栏入口 ---------- */
/* 带 .topbar 前缀的理由跟齿轮那条一样：压不住上面的 position:static，
   红点就飞到屏幕角上去了。 */
.topbar .corner-support {
  position: relative;
  padding-left: .55rem; padding-right: .55rem;
  border-color: hsl(var(--sup) / .45);
}
.corner-support:hover { border-color: hsl(var(--sup) / .85); }
.sup-heart {
  font-size: .95rem; line-height: 1;
  color: hsl(var(--sup));
  /* emoji 字体会把 ❤ 画成图形字，这儿要的是能跟着 color 走的那个字形 */
  font-family: "Inter", -apple-system, "Segoe UI", sans-serif;
}
.sup-red {
  position: absolute; top: .28rem; right: .3rem;
  width: 6px; height: 6px; border-radius: 50%;
  background: hsl(var(--sup));
  box-shadow: 0 0 0 2px hsl(var(--card));
  opacity: 0;                       /* 没事可做时不点人 */
  transition: opacity .2s;
}
.corner-support.has-dot .sup-red { opacity: 1; animation: supPulse 2.4s ease-in-out infinite; }
.corner-support.all-done { border-color: hsl(var(--sup) / .3); }
.corner-support.all-done .sup-heart { filter: none; opacity: .9; }
@keyframes supPulse {
  0%, 100% { box-shadow: 0 0 0 2px hsl(var(--card)), 0 0 0 3px hsl(var(--sup) / .45); }
  50%      { box-shadow: 0 0 0 2px hsl(var(--card)), 0 0 0 7px hsl(var(--sup) / 0); }
}

/* ---------- 面板 ----------
   结构照 .overlay/.modal 那一套（窄屏抽屉、宽屏居中卡片），但类名另起：
   game.js 给 .overlay 绑了 data-close 那套开关，混进去等于把它的状态机
   接管一半，两边都不好查。 */
.sup-overlay {
  position: fixed; inset: 0; z-index: 90;
  background: hsl(var(--scrim) / .68); backdrop-filter: blur(4px);
  display: flex; align-items: flex-end; justify-content: center;
}
.sup-overlay.hidden { display: none; }
.sup-panel {
  position: relative; width: 100%; max-width: var(--maxw);
  max-height: 86vh; overflow-y: auto;
  background: hsl(var(--card));
  border: 1px solid hsl(var(--border));
  border-top: 2px solid hsl(var(--sup) / .55);
  border-radius: var(--r-card) var(--r-card) 0 0;
  padding: 1.3rem 1.1rem calc(1.3rem + env(safe-area-inset-bottom, 0px));
  animation: supRise .26s ease both;
}
@keyframes supRise { from { transform: translateY(18px); opacity: 0; } to { transform: none; opacity: 1; } }
.sup-close {
  position: absolute; top: .9rem; right: .9rem;
  width: 1.75rem; height: 1.75rem; line-height: 1;
  border: 1px solid hsl(var(--border)); border-radius: var(--r-row);
  background: transparent; color: hsl(var(--faint)); cursor: pointer; font-size: .78rem;
}
.sup-title {
  font-size: 1.1rem; font-weight: 750; color: hsl(var(--text));
  margin: 0 0 .35rem;
}
.sup-title::before {
  content: "❤"; color: hsl(var(--sup)); margin-right: .4rem;
  font-family: "Inter", -apple-system, "Segoe UI", sans-serif;
}
.sup-lead { font-size: .82rem; line-height: 1.6; color: hsl(var(--muted)); margin: 0 0 .9rem; }

/* ---------- 进度 ---------- */
.sup-progress {
  height: 5px; border-radius: 999px;
  background: hsl(var(--row-hi));
  overflow: hidden;
}
.sup-progress > i {
  display: block; height: 100%; border-radius: 999px;
  background: linear-gradient(90deg, hsl(var(--sup)), hsl(var(--gold1)));
  transition: width .45s cubic-bezier(.3, 1.2, .5, 1);
}
.sup-count {
  margin: .35rem 0 .9rem;
  font-size: .72rem; letter-spacing: .04em; color: hsl(var(--faint));
  text-align: right;
}

/* ---------- 一项 ---------- */
.sup-task {
  display: flex; align-items: center; gap: .65rem;
  padding: .6rem .65rem;
  border: 1px solid hsl(var(--border));
  border-radius: var(--r-row);
  background: hsl(var(--row-hi) / .5);
  margin-bottom: .5rem;
}
.sup-task.ok { border-color: hsl(var(--sup) / .4); }
/* 左边那一栏是**一个固定宽度**：关注是圆的、视频是方的封面，但两个都占
   2.55rem，两行的标题才从同一条竖线起头。改宽度记得两处一起改。 */
.sup-ico {
  width: 2.55rem; height: 2.55rem; flex-shrink: 0;
  display: grid; place-items: center;
  border-radius: 50%;
  background: hsl(var(--sup) / .16); color: hsl(var(--sup));
  font-size: 1.05rem; font-weight: 700;
}
.sup-task.ok .sup-ico { background: hsl(var(--sup)); color: hsl(var(--card)); }

/* ---------- 图片型图标 ----------
   清单左边那一栏两项都是图：关注是**作者本人的头像**，视频是**这支视频的封面**。
   认出「是这个人」「是刷到过的那支」，比读懂按钮上那四个字快。
   两个都占 2.55rem（.sup-ico 定的），两行的标题才从同一条竖线起头。 */
.sup-ico.sup-pic {
  position: relative;
  overflow: hidden;
  background-color: hsl(var(--row-hi));
}
/* 图走 <img> 不走 background-image：data URI 里有分号，写进 style 属性
   要跟 CSS 的声明分隔符抢，img 的 src 属性没这个麻烦。 */
.sup-ico.sup-pic > img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* 头像那张图**本身就是圆的**（透明底、带一圈金环），所以这儿不描边、不垫底色，
   直接让它自己是什么样就什么样。 */
.sup-ico.sup-avatar { border-radius: 50%; background-color: transparent; }
.sup-ico.sup-cover  { border-radius: .45rem; border: 1px solid hsl(var(--border)); }

/* 上面 .sup-task.ok .sup-ico 那条会把 background 简写掉（连图一起），
   所以做完之后不换底色，只在图上盖一层带 ✓ 的膜。 */
.sup-task.ok .sup-ico.sup-pic { background-color: hsl(var(--row-hi)); color: hsl(var(--card)); }
.sup-ico.sup-pic.ok::after {
  content: "✓";
  position: absolute; inset: 0;
  display: grid; place-items: center;
  /* 膜别盖太死 —— 底下那张图还得看得见，这一格是「这项你做过了」，
     不是「这一格已作废」。 */
  background: hsl(var(--sup) / .62);
  color: hsl(var(--card));
  font-size: 1.05rem; font-weight: 700;
  text-shadow: 0 1px 2px hsl(var(--sup) / .8);
}

.sup-task-meta { display: flex; flex-direction: column; gap: .18rem; min-width: 0; flex: 1; }
.sup-task-meta > b {
  font-size: .82rem; font-weight: 650; color: hsl(var(--text));
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.sup-task-meta > span { font-size: .7rem; color: hsl(var(--faint)); }

/* 三连那三个点。灰着是「还没做」，亮起来是「做过了」；
   查不到状态（未登录 / 平台没给）时一律留灰，不猜。 */
.sup-dots { display: flex; gap: .4rem; flex-wrap: wrap; }
.sup-dot {
  display: inline-flex; align-items: center; gap: .18rem;
  padding: .12rem .38rem;
  border-radius: 999px;
  border: 1px solid hsl(var(--border));
  font-size: .64rem; font-style: normal; color: hsl(var(--faint));
  filter: grayscale(1); opacity: .75;
  transition: all .2s;
}
.sup-dot > em { font-style: normal; font-size: .72rem; line-height: 1; }
.sup-dot.on {
  filter: none; opacity: 1;
  color: hsl(var(--gold1));
  border-color: hsl(var(--gold1) / .5);
  background: hsl(var(--gold1) / .12);
}

.sup-go {
  flex-shrink: 0;
  padding: .34rem .78rem;
  border-radius: 999px; border: 0;
  background: hsl(var(--sup)); color: hsl(var(--card));
  font-family: inherit; font-size: .75rem; font-weight: 700;
  cursor: pointer; white-space: nowrap;
  box-shadow: 0 2px 8px hsl(var(--sup) / .28);
}
.sup-go:hover { filter: brightness(1.08); }
.sup-go.done {
  background: transparent; color: hsl(var(--faint));
  border: 1px solid hsl(var(--border)); box-shadow: none; cursor: default;
}
.sup-note { font-size: .7rem; color: hsl(var(--faint)); margin: .6rem 0 0; }
.sup-note.hidden { display: none; }

/* ---------- 结算页那一块 ----------
   只在这一屏出现。人刚看完自己这一辈子，是全场唯一一个愿意听你说话的时候。
   2026-08-23 从「一条横幅 + 看看按钮」换成清单本身，摆在种子那一行下面。 */
.sup-inline {
  margin: 1rem auto 0;
  max-width: var(--maxw);
  padding: .8rem .75rem .7rem;
  border: 1px solid hsl(var(--sup) / .38);
  border-radius: var(--r-card);
  background: linear-gradient(100deg, hsl(var(--sup) / .1), hsl(var(--gold1) / .06));
}
.sup-inline-lead {
  display: flex; align-items: center; gap: .4rem;
  margin: 0 0 .55rem;
  font-size: .84rem; font-weight: 700; color: hsl(var(--text));
}
.sup-inline-lead > span {
  color: hsl(var(--sup)); font-size: .95rem; line-height: 1;
  font-family: "Inter", -apple-system, "Segoe UI", sans-serif;
}
/* 卡片摞在这块半透明的底上，用不透明一点的行底把层次分开 */
.sup-inline .sup-task { background: hsl(var(--card) / .62); }
.sup-inline .sup-task:last-of-type { margin-bottom: 0; }
.sup-inline .sup-progress { margin-bottom: .1rem; }

/* 宽屏：跟别的弹窗一样，从底部抽屉改成居中卡片 */
@media (min-width: 820px) {
  .sup-overlay { align-items: center; padding: 1.5rem 1rem; }
  .sup-panel {
    max-height: min(86vh, 42rem);
    border-radius: var(--r-card);
    border-top-width: 2px;
    padding-bottom: 1.3rem;
  }
}

/* 窄屏：按钮和文字都别挤成一坨 */
@media (max-width: 400px) {
  .sup-task { gap: .5rem; padding: .55rem .5rem; }
  .sup-go { padding: .32rem .6rem; font-size: .72rem; }
  .sup-task-meta > b { font-size: .78rem; }
}

@media (prefers-reduced-motion: reduce) {
  .sup-panel { animation: none; }
  .corner-support.has-dot .sup-red { animation: none; }
  .sup-progress > i { transition: none; }
}

/* ============================================================
   设置面板
   ------------------------------------------------------------
   由 toy-settings.js 动态插入。外形跟「支持一下」那块是同一套
   （窄屏抽屉、宽屏居中卡片），但**不用粉色** —— 粉是留给站外动作的
   （关注、三连）。设置里全是站内的事，走全站原来那套墨绿和灰。

   里面的每一行都是「入口」，不是内容：玩法和公告的正文还在 game.js
   的两个弹窗里，这儿点一下把它们叫出来。
   ============================================================ */

.set-overlay {
  position: fixed; inset: 0; z-index: 88;
  background: hsl(var(--scrim) / .68); backdrop-filter: blur(4px);
  display: flex; align-items: flex-end; justify-content: center;
}
.set-overlay.hidden { display: none; }
.set-panel {
  position: relative; width: 100%; max-width: var(--maxw);
  max-height: 86vh; overflow-y: auto;
  background: hsl(var(--card));
  border: 1px solid hsl(var(--border));
  border-top: 2px solid hsl(var(--accent) / .5);
  border-radius: var(--r-card) var(--r-card) 0 0;
  padding: 1.3rem 1.1rem calc(1.3rem + env(safe-area-inset-bottom, 0px));
  animation: setRise .26s ease both;
}
@keyframes setRise { from { transform: translateY(18px); opacity: 0; } to { transform: none; opacity: 1; } }
.set-close {
  position: absolute; top: .9rem; right: .9rem;
  width: 1.75rem; height: 1.75rem; line-height: 1;
  border: 1px solid hsl(var(--border)); border-radius: var(--r-row);
  background: transparent; color: hsl(var(--faint)); cursor: pointer; font-size: .78rem;
}
.set-title {
  font-size: 1.1rem; font-weight: 750; color: hsl(var(--text));
  margin: 0 0 .9rem;
}
.set-title::before {
  content: "⚙"; color: hsl(var(--accent)); margin-right: .45rem; font-size: .95rem;
}

/* ---------- 存档那一条 ----------
   摆在最上面：进设置的人有一半是来找「我的进度在哪儿」的。
   连上是绿的、没连上是琥珀的 —— 没连上不是错误（没登录、外部浏览器打开
   都会这样），所以不用红。 */
.set-cloud {
  display: flex; align-items: center; gap: .6rem;
  padding: .65rem .7rem;
  border: 1px solid hsl(var(--warning) / .35);
  background: hsl(var(--warning) / .08);
  border-radius: var(--r-row);
  margin-bottom: .9rem;
}
.set-cloud.ok { border-color: hsl(var(--accent) / .35); background: hsl(var(--accent) / .08); }
.set-cloud-ico {
  width: 1.8rem; height: 1.8rem; flex-shrink: 0;
  display: grid; place-items: center; border-radius: 50%;
  background: hsl(var(--warning) / .16); color: hsl(var(--warning));
  font-size: .9rem;
}
.set-cloud.ok .set-cloud-ico { background: hsl(var(--accent) / .16); color: hsl(var(--accent)); }

/* ---------- 一组 ---------- */
.set-group {
  border: 1px solid hsl(var(--border));
  border-radius: var(--r-row);
  overflow: hidden;
  margin-bottom: .7rem;
}
.set-row {
  width: 100%;
  display: flex; align-items: center; gap: .65rem;
  padding: .7rem .7rem;
  background: hsl(var(--row-hi) / .45);
  border: 0; border-bottom: 1px solid hsl(var(--border));
  font-family: inherit; text-align: left; cursor: pointer;
  transition: background .15s;
}
.set-group .set-row:last-child { border-bottom: 0; }
.set-row:hover { background: hsl(var(--row-hi)); }
.set-row-ico {
  width: 1.8rem; height: 1.8rem; flex-shrink: 0;
  display: grid; place-items: center; border-radius: 50%;
  background: hsl(var(--row-hi)); font-size: .88rem;
}
.set-row-meta { display: flex; flex-direction: column; gap: .12rem; flex: 1; min-width: 0; }
.set-row-meta > b { font-size: .84rem; font-weight: 650; color: hsl(var(--text)); }
.set-row-meta > span {
  font-size: .7rem; color: hsl(var(--faint));
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.set-row-arrow { color: hsl(var(--faint)); font-size: 1.1rem; line-height: 1; flex-shrink: 0; }
/* 「支持一下」那行的心跟顶栏那个 ❤ 同色。不上色的话 emoji 字体会把它画成
   黑心，摆在深色圆底上几乎看不见 —— 这是整个面板里唯一一个非 emoji 的图标。 */
.set-row[data-do="support"] .set-row-ico {
  color: hsl(var(--sup));
  background: hsl(var(--sup) / .14);
  font-family: "Inter", -apple-system, "Segoe UI", sans-serif;
}
.set-tag {
  flex-shrink: 0;
  font-style: normal; font-size: .6rem; font-weight: 750; letter-spacing: .08em;
  padding: .1rem .34rem; border-radius: 4px;
  background: hsl(var(--failure)); color: hsl(var(--card));
}

/* ---------- 开关 ----------
   全站只有这一个真开关，所以不值得为它引一套 <input type=checkbox> 的样式，
   直接把整行做成 role="switch" 的按钮。 */
.set-switch { cursor: pointer; }
.set-knob {
  flex-shrink: 0;
  width: 2.1rem; height: 1.2rem; border-radius: 999px;
  background: hsl(var(--row-hi)); border: 1px solid hsl(var(--border-strong));
  position: relative; transition: background .18s, border-color .18s;
}
.set-knob::after {
  content: ""; position: absolute; top: 1px; left: 1px;
  width: 1rem; height: 1rem; border-radius: 50%;
  background: hsl(var(--faint));
  transition: transform .18s ease, background .18s;
}
.set-switch.on .set-knob { background: hsl(var(--accent) / .3); border-color: hsl(var(--accent)); }
.set-switch.on .set-knob::after { transform: translateX(.88rem); background: hsl(var(--accent)); }

.set-foot {
  margin: 1rem 0 0; text-align: center;
  font-size: .68rem; color: hsl(var(--faint));
}

/* 宽屏：抽屉改居中卡片，跟别的弹窗一致 */
@media (min-width: 820px) {
  .set-overlay { align-items: center; padding: 1.5rem 1rem; }
  .set-panel {
    max-height: min(86vh, 42rem);
    border-radius: var(--r-card);
    padding-bottom: 1.3rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  .set-panel { animation: none; }
  .corner-gear.has-dot .gear-dot { animation: none; }
  .set-knob, .set-knob::after { transition: none; }
}

/* ---------- 确认弹窗（#confirm-modal）----------
   window.confirm 在 B 站 iframe 里被宿主屏蔽（永远返回 false），
   生涯页「从头来过」、回首页、继续存档、清空档案那 4 处确认全换成它。
   game.js（build 补丁）的 confirmDialog() 读写这组元素。
   文字比 .modal p 的 --muted 亮一档：这是要人读的确认内容，不是陪衬。 */
.confirm-modal { text-align: center; }
.confirm-txt {
  margin: .2rem 0 1rem;
  font-size: .9rem; line-height: 1.65; color: hsl(var(--text));
  white-space: pre-line;
}
.confirm-actions { display: flex; gap: .6rem; }
.confirm-actions .btn { flex: 1; margin: 0; }
