/* orbit 入口页版式 — 轨道星图（design「入口页机制」）
 *
 * 主体是贴纸。中间铺一层浅浅的手写标语当底子，贴纸浮在上面各自缓慢游走，
 * 头像缩到左下角当一枚落款——不居中，众星也就不用绕着谁转。
 *
 * 位置数学全在 CSS 里。构建期只往每颗星的 <li> 上写三个数：--ring / --angle / --size，
 * 本文件把它们换算成落点坐标。一行 JS 都没有——无 JS 也能看、能点、能 Tab。
 *
 * 落点是静态的（由登记表决定，因而确定、不重叠），动的只是每颗星在自己落点周围
 * 画的一个小圈。小圈的两个方向频率不同、相位各异，所以十颗星的轨迹各不相同，
 * 也都不画出来。频率一律取整数倍，路径才闭合——不闭合的话动画绕回起点时会跳一下。
 *
 * 漂移只有一个动画量：@property 注册的 --spin。浏览器不认 @property 时它停在 0deg，
 * 星图静止——与 reduced-motion 同一副样子，降级得干净。
 */

@property --spin {
  syntax: "<angle>";
  initial-value: 0deg;
  inherits: false;
}

/* 落点椭圆的扁率 = rx / ry。理想值就是"横向余量 ÷ 竖向余量"，两个方向同时顶满；
 * 以前按宽高比分档逼近，档位再细也总有比例落在档与档之间——而空着的那一边直接
 * 换算成更小的贴纸。calc 不许拿长度除长度，但 atan2() 吃两个长度、tan() 再把角度
 * 变回比值，绕过去了。
 * 注册成 <number> 是为了降级：浏览器不认 atan2 时值算不出来，会退回这里的
 * initial-value 而不是让整条 --ry-max 链失效（未注册的自定义属性会失效）。 */
@property --flat {
  syntax: "<number>";
  initial-value: 1.6;
  inherits: true;
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

html,
body {
  height: 100%;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-body);
  overflow: hidden;          /* 宽屏是一屏页面，不滚动；窄屏在下面改回来 */
  -webkit-font-smoothing: antialiased;
}

/* 纸纹与中心的一点亮：CSS 画的，不是资产（assets-brief 明确划了这条线）。 */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(115% 85% at 50% 46%, rgba(255, 255, 255, 0.72), rgba(255, 255, 255, 0) 62%),
    repeating-linear-gradient(114deg, rgba(20, 52, 42, 0.026) 0 1px, transparent 1px 7px);
}

/* ── 尺寸参数 ────────────────────────────────────────────────
 * 改这几行就能调整整张图的松紧——design 说"俏皮要靠可调的参数"。
 * --card-h 是卡片高度的预算值，只参与"四边留多少、星图才画得下"这道算术；
 * 卡片本身高度自适应，改卡片版式时要回来对一眼这个数——这个数偏大一点，
 * 直接就换算成更小的贴纸（M4 那轮把卡片压扁之后忘了回来改，白丢了 39px）。
 */
.sky {
  --card-w: clamp(178px, 23vmin, 220px);
  --card-h: 186px;                               /* 量出来的最高一张 + 一点余量；改卡片版式要回来对这个数 */
  --card-dx: clamp(106px, 15.4vmin, 152px);      /* 卡片滑出的水平幅度 */
  --card-dy: clamp(30px, 4.5vmin, 46px);         /* 头像不在中间了，卡片往里探一点也没关系 */
  --card-tilt-mag: 5deg;
  --card-tilt: calc(-1 * var(--card-tilt-mag));

  /* 卡片是斜着的，四边要留的是它的外接框而不是卡片本身——一张 207×178 的卡歪 5°，
   * 外接框就成了 222×195。以前这两处直接拿 --card-w / --card-h 当留边，横向少留了
   * 15px、纵向少留了 17px，全靠 --slack 兜着。 */
  --card-box-w: calc(var(--card-w) * cos(var(--card-tilt-mag)) + var(--card-h) * sin(var(--card-tilt-mag)));
  --card-box-h: calc(var(--card-h) * cos(var(--card-tilt-mag)) + var(--card-w) * sin(var(--card-tilt-mag)));
  --wander: 0.17;                                /* 游走幅度，相对贴纸边长 */

  /* 落点场的中心：不在正中，往右上偏一点，把左下角整块让给标语与落款。
   * 下面两个 room 用的是"到最近那条边"的距离，必须和这里的偏移一致：
   * 54% → 46vw，47% → 47vh。改一个就要改另一个。
   * 往右偏是为了给左下角那块标语留出地方——最左边那颗星不能压到字上。 */
  --cx: 54%;
  --cy: 47%;

  /* 星图能铺多大：四边先留够卡片滑出的位置与游走的余量，剩下的全给落点椭圆。
   * --slack 写成固定值而不是从 --star-base 算，是为了避开循环引用：
   * 贴纸大小反过来要从 --ry-max 推。 */
  --slack: clamp(12px, 2.6vmin, 30px);
  --ry-room: calc(47vh - 8px - var(--slack) - var(--card-dy) - var(--card-box-h) / 2);
  --rx-room: calc(46vw - 8px - var(--slack) - var(--card-dx) - var(--card-box-w) / 2);
  --flat: clamp(1.1, tan(atan2(var(--rx-room), var(--ry-room))), 2.4);
  --ry-max: min(var(--ry-room), calc(var(--rx-room) / var(--flat)));

  /* 贴纸跟着"星图实际有多大"缩放，而不是自己量屏幕——否则矮窗口下星图被卡片挤小了、
   * 贴纸却还按屏幕算，十来颗立刻糊成一团。0.48 是量出来的上限：再大，最内圈那颗
   * 和第二圈斜上方那颗就会压到一起。 */
  --star-base: clamp(56px, calc(var(--ry-max) * 0.48), 224px);
  --avatar: clamp(56px, 8vmin, 92px);

  /* 背景星尘三档。上限 0.20 是算出来的：最小的那颗贴纸是 --star-base 的 0.82 倍，
   * 0.20 / 0.82 ≈ 24%，卡在"不超过最小贴纸四分之一"这条线以内。 */
  --mote-s: clamp(11px, calc(var(--star-base) * 0.13), 24px);
  --mote-m: clamp(14px, calc(var(--star-base) * 0.17), 30px);
  --mote-l: clamp(17px, calc(var(--star-base) * 0.20), 36px);

  position: relative;
  z-index: 1;
  height: 100%;
}

/* ── 背景星尘 ────────────────────────────────────────────────
 * 十来枚贴纸铺不满一整块大屏，空当得有东西接住，否则读起来是"没画完"而不是"天"。
 *
 * 这层是画出来的配件，不是 CSS 画的圆点（design「入口页机制」）：CSS 的圆是绝对的
 * 圆、没有笔触，凑近看是"网页上的点"，跟旁边手抖的贴纸不是同一只手。资产在
 * public/dust/，规格见 assets-brief「星尘配件规格」。
 *
 * 分寸靠数字卡死，不靠感觉：最大一档是 --star-base 的 0.20，而最小的那颗贴纸是
 * 0.82 倍——算下来 24%，不到四分之一。透明度两档 0.26 / 0.34，都在规范给的
 * 0.2–0.35 里。位置写死不用随机数：视觉快照要稳（design「构建管线」）。
 *
 * 分远近两层：远的小而淡、铺得密；近的大一点、深一点、稀。左下角整块留给标语，
 * 两层都不往那儿放。background-size 只写三个值——层数多于值时 CSS 会把列表循环用完，
 * 于是大小自动错落，不必给每一层各写一遍。
 * 六个母题里只用五个：dust-dot 缩到显示尺寸就是一坨实心墨块，在浅绿底上读成脏点，
 * 而且比五个画出来的母题都抢眼，按 assets-brief 的验收清单打回了。
 * 四十二处只下载五个文件。
 */
.sky::before,
.sky::after {
  content: "";
  position: fixed;
  inset: 0;
  /* .sky 自己就是个层叠上下文，所以这层只沉到星星底下，不会掉到纸纹下面去。 */
  z-index: -1;
  pointer-events: none;
  background-repeat: no-repeat;
}

/* 远层：小、淡、密。 */
.sky::before {
  opacity: 0.26;
  background-image:
    url("/dust/dust-star.png"), url("/dust/dust-spark.png"), url("/dust/dust-cluster.png"),
    url("/dust/dust-star.png"), url("/dust/dust-wisp.png"), url("/dust/dust-spark.png"),
    url("/dust/dust-star.png"), url("/dust/dust-spark.png"), url("/dust/dust-cluster.png"),
    url("/dust/dust-star.png"), url("/dust/dust-wisp.png"), url("/dust/dust-spark.png"),
    url("/dust/dust-star.png"), url("/dust/dust-star.png"), url("/dust/dust-spark.png"),
    url("/dust/dust-cluster.png"), url("/dust/dust-star.png"), url("/dust/dust-wisp.png"),
    url("/dust/dust-spark.png"), url("/dust/dust-star.png"), url("/dust/dust-spark.png"),
    url("/dust/dust-cluster.png"), url("/dust/dust-star.png"), url("/dust/dust-wisp.png"),
    url("/dust/dust-spark.png"), url("/dust/dust-star.png");
  background-position:
     50.0%  50.0%,  25.5%   7.0%,  76.5%  21.0%,
     52.0%  77.9%,  27.4%  34.9%,  78.4%  48.9%,
     53.9%   5.9%,  29.4%  62.9%,   4.9%  19.8%,
     80.4%  76.8%,  55.9%  33.8%,  31.3%  90.8%,
      6.8%  47.8%,  82.3%   4.8%,  57.8%  61.7%,
     33.3%  18.7%,  84.3%  32.7%,  59.8%  89.7%,
     35.2%  46.7%,  10.7%   3.6%,  86.2%  60.6%,
     61.7%  17.6%,  37.2%  74.6%,  12.7%  31.6%,
     88.2%  88.6%,  63.7%  45.6%;
  background-size:
    var(--mote-s) var(--mote-s),
    var(--mote-s) var(--mote-s),
    var(--mote-m) var(--mote-m);
  animation: mote-far 19s ease-in-out infinite;
}

/* 近层：大一点、深一点、稀。彗星全页只此一枚。 */
.sky::after {
  opacity: 0.34;
  background-image:
    url("/dust/dust-star.png"), url("/dust/dust-spark.png"), url("/dust/dust-cluster.png"),
    url("/dust/dust-star.png"), url("/dust/dust-wisp.png"), url("/dust/dust-spark.png"),
    url("/dust/dust-star.png"), url("/dust/dust-comet.png"), url("/dust/dust-spark.png"),
    url("/dust/dust-star.png"), url("/dust/dust-cluster.png"), url("/dust/dust-wisp.png"),
    url("/dust/dust-spark.png"), url("/dust/dust-star.png"), url("/dust/dust-wisp.png"),
    url("/dust/dust-spark.png");
  background-position:
     45.1%  43.6%,  20.6%   0.6%,  96.1%  57.6%,
     71.6%  14.6%,  47.1%  71.5%,  22.5%  28.5%,
     98.0%  85.5%,  73.5%  42.5%,  49.0%  99.5%,
    100.0%  13.5%,  75.5%  70.4%,  51.0%  27.4%,
      1.9%  41.4%,  77.4%  98.4%,  42.1%   7.9%,
     93.0%  21.9%;
  background-size:
    var(--mote-m) var(--mote-m),
    var(--mote-l) var(--mote-l),
    var(--mote-m) var(--mote-m);
  animation: mote-near 26s ease-in-out infinite;
}

/* 一层几乎察觉不到的呼吸——幅度小到不构成"动"，只是让这层不像印上去的。
 * 两个周期互不整除，看起来才不同步。reduced-motion 下整个关掉。 */
@keyframes mote-far  { 0%, 100% { opacity: 0.26 } 50% { opacity: 0.31 } }
@keyframes mote-near { 0%, 100% { opacity: 0.34 } 50% { opacity: 0.29 } }

/* ── 左下角那一小块：一堆浅浅的字，底下压一枚小头像 ───────────────
 * 标语不是标题也不是脚注，是角落里一叠随手记下的东西，要的是"什么都有"
 * 那个印象，不是让人逐行读完。整块缩在角落，中间让给贴纸——贴纸才是主体。
 * 每行的大小、深浅、倾斜都由 --i 算出来，因而确定、可快照；三个互质的模数
 * 让它不落进任何一种周期性，看起来才像随手写的而不是排版出来的。
 */
.corner {
  --gap: clamp(18px, 3vw, 44px);
  position: absolute;
  left: var(--gap);
  bottom: var(--gap);
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.7em;
  max-width: min(38vw, 26em);
  pointer-events: none;
}

.slogan {
  margin: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.1em;
  font-weight: 400;
  font-family: var(--font-hand);
  font-size: clamp(0.85rem, 1.9vmin, 1.4rem);
  color: var(--ink);
  user-select: none;
}

.slogan__line {
  --lean: calc(mod(var(--i), 5) - 2);            /* -2 … 2 */
  --swell: calc(mod(var(--i), 4));               /* 0 … 3 */

  font-size: calc(0.94em + var(--swell) * 0.1em);
  opacity: calc(0.26 + var(--swell) * 0.04);
  margin-left: calc(mod(var(--i), 3) * 0.55em);
  rotate: calc(var(--lean) * 0.6deg);
  white-space: nowrap;
}

/* 第一行是那句正主，给它一点分量，但也就一点。 */
.slogan__line:first-child {
  font-size: 1.34em;
  opacity: 0.5;
  margin-left: 0;
}

/* 落款：字堆底下压一枚小头像，像一枚闲章。 */
.mark {
  width: var(--avatar);
  height: var(--avatar);
  opacity: 0.92;
  rotate: -3deg;
}

/* 落款那一行：闲章 + 一个很小的信箱。信箱是整页唯一的联络方式，但它不该有存在感，
 * 所以只比背景深一点点，凑近了才看得清。 */
.sign {
  display: flex;
  align-items: center;
  gap: 0.7rem;
}

.sign__mail {
  /* .corner 整块不吃指针事件（免得一张透明的大网挡住底下的星），这里得单独要回来。 */
  pointer-events: auto;
  font-size: 0.78rem;
  letter-spacing: 0.03em;
  /* 低调是一回事，看不清是另一回事——这是整页唯一的联络方式。
   * --ink-faint 在这个字号下对比度只有 1.9，--ink-muted 也才 4.16，都不到 4.5。
   * 往墨色里兑三成，量出来 5.7。 */
  color: color-mix(in srgb, var(--ink) 30%, var(--ink-muted));
  text-decoration: none;
  transition: color 0.16s ease;
}

.sign__mail:hover,
.sign__mail:focus-visible {
  color: var(--ink);
  text-decoration: underline;
}

@media (prefers-reduced-motion: reduce) {
  .sign__mail {
    transition: none;
  }
}

/* ── 星 ──────────────────────────────────────────────────────
 * <nav> 与 <ul> 铺满整个星图但不吃指针事件，只有贴纸本身可点——
 * 否则一张透明的大网会挡住底下的标语，也会让 :has() 的悬停判定失真。
 */
.sky__stars {
  position: absolute;
  inset: 0;
  z-index: 4;
  pointer-events: none;
}

.constellation {
  position: absolute;
  inset: 0;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* 落点：--ring 从 1 数到 --rings，两者都由构建期从登记表写进 style 属性。
 * +1.6 让最内圈离中心远一点：贴纸有一百多像素宽，内圈太靠里的话正上、正下那两颗
 * 会碰在一起。数字调大 = 各圈之间更均匀，调小 = 内圈更贴近中心。 */
.star {
  --t: calc((var(--ring) + 1.6) / (var(--rings) + 1.6));
  --rx: calc(var(--ry-max) * var(--flat) * var(--t));
  --ry: calc(var(--ry-max) * var(--t));
  --home: calc(var(--angle) * 1deg);

  --hit: calc(var(--star-base) * var(--size));
  --w: calc(var(--star-base) * var(--wander));
  --spin: 0deg;                                  /* @property 不可用时的兜底：静止 */
  --phase: calc(var(--home) + var(--spin));
  /* 内圈游得快、外圈游得慢，在 tokens 的 fast/slow 两端之间插值。
   * 除数用 --rings 而不是写死，登记表加到第六圈时不需要回来改这行。 */
  --period: calc(
    var(--drift-fast)
    + (var(--ring) - 1) * (var(--drift-slow) - var(--drift-fast)) / max(1, var(--rings) - 1)
  );

  position: absolute;
  left: var(--cx);
  top: var(--cy);
  width: var(--hit);
  height: var(--hit);
  margin: calc(var(--hit) / -2) 0 0 calc(var(--hit) / -2);
  z-index: calc(20 - var(--ring));
  transform: translate(
    calc(
      cos(var(--home)) * var(--rx)
      + cos(var(--phase)) * var(--w)
      + cos(calc(var(--phase) * 2)) * var(--w) * 0.35
    ),
    calc(
      sin(var(--home)) * var(--ry)
      + sin(calc(var(--phase) * 2 + var(--home))) * var(--w) * 0.8
      + sin(calc(var(--phase) * 3)) * var(--w) * 0.25
    )
  );
  will-change: transform;
  animation: orbit-drift var(--period) linear infinite;
}

@keyframes orbit-drift {
  to { --spin: 360deg; }
}

/* 悬停/聚焦时整片星空一起停住。只停被指的那一颗也能做，但同圈的星会被它落下，
 * 停久了各走各的相位；整片停住既省事，也更像"抬头看星星时天不动"。 */
.constellation:has(.star__hit:hover) .star,
.constellation:has(.star__hit:focus-visible) .star {
  animation-play-state: paused;
}

.star:hover,
.star:focus-within {
  z-index: 40;
}

.star__hit {
  position: absolute;
  inset: 0;
  display: block;
  color: inherit;
  text-decoration: none;
  pointer-events: auto;
  border-radius: 50%;
  outline-offset: 6px;
  -webkit-tap-highlight-color: transparent;
}

/* 键盘走到哪一颗要看得见。虚线描边和卡片圆角是同一套语言，不用系统默认的实线蓝框。 */
.star__hit:focus-visible {
  outline: 2px dashed var(--ink);
  outline-offset: 6px;
  border-radius: 50%;
}

.star__sticker {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  transform-origin: 50% 72%;                     /* 从"脚下"翘起，不是原地放大 */
  transition:
    scale 0.32s var(--ease-pop),
    rotate 0.32s var(--ease-pop),
    translate 0.32s var(--ease-pop);
}

/* 卡片抽出来的时候贴纸往反方向让开一步，否则它正好压在卡片的名字上。
 * 让开的距离按贴纸自身大小算，贴纸多大都刚好露出卡片的文字区。 */
.star__hit:hover .star__sticker,
.star__hit:focus-visible .star__sticker {
  scale: 1.12;
  rotate: -6deg;
  translate: calc(sign(calc(cos(var(--home)) + 0.001)) * var(--hit) * -0.3) -5%;
}

/* ── 悬停抽出的卡片 ─────────────────────────────────────────────
 * transform-origin 在星心上，卡片的位置全写在 transform 里（跟着 --spin 每帧重算，
 * 所以绝不能给它加 transition，否则会永远追不上自己）。
 * 抽出动作交给独立的 scale 属性：scale 是绕星心缩放的，0.5 时卡片正好缩回贴纸背后，
 * 1 时滑到位——"从贴纸背后斜着滑出"就是这么来的，不需要第二个位移量。
 */
.star__card {
  position: absolute;
  left: 50%;
  top: 50%;
  z-index: -1;                                   /* 压在贴纸底下，所以是"从背后"滑出 */
  display: block;                                /* 它是个 <span>，不写这句会被拆成行内碎片 */
  width: var(--card-w);
  padding: 9px 10px 11px;
  background: var(--surface);
  border: 2px solid var(--ink);
  border-radius: var(--r-card);
  box-shadow: var(--shadow-lift);
  transform-origin: 0 0;
  /* 横向用 sign() 而不是 cos()：卡片一律整个滑到左边或右边去。用 cos() 的话，
   * 落点接近正上/正下的那几颗（cos≈0）卡片会横跨在贴纸上，贴纸正好压住名字和说明。
   * 加 0.001 是为了让角度正好 90° / 270° 时 sign() 不返回 0。
   * 纵向仍按 sin() 连续取值——那只是让卡片跟着星的高低微调，不会造成横跨。 */
  transform:
    translate(
      calc(sign(calc(cos(var(--home)) + 0.001)) * var(--card-dx)),
      calc(sin(var(--home)) * var(--card-dy))
    )
    rotate(var(--card-tilt))
    translate(-50%, -50%);
  scale: 0.5;
  opacity: 0;
  pointer-events: none;
  transition:
    scale 0.34s var(--ease-pop),
    opacity 0.18s ease;
}

.star__hit:hover .star__card,
.star__hit:focus-visible .star__card {
  scale: 1;
  opacity: 1;
  pointer-events: auto;
}

/* 拍立得的取景窗，只在真有 poster 时才开。 */
.star__frame {
  display: block;
  aspect-ratio: 5 / 3;
  margin-bottom: 7px;
  overflow: hidden;
  border-radius: 6px 10px 7px 11px / 9px 6px 12px 7px;
  background: var(--surface-2);
}

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

/* 印章盖在卡片右上角，压着边框——像盖歪了一点的那种。 */
.star__stamp {
  position: absolute;
  right: -6px;
  top: -11px;
  font-family: var(--font-hand);
  font-size: 0.92rem;
  line-height: 1;
  letter-spacing: 0.06em;
  padding: 0.28em 0.46em 0.22em;
  background: var(--surface);
  border: 1.5px solid currentColor;
  border-radius: 5px 9px 6px 10px / 8px 5px 10px 6px;
  rotate: -8deg;
  opacity: 0.9;
  color: var(--ink-muted);
}

.star[data-state="soon"] .star__stamp  { color: var(--orange-dark); }
.star[data-state="gated"] .star__stamp { color: var(--silver-dark); }
.star[data-state="live"] .star__stamp  { color: var(--mint-dark); }

.star__name,
.star__blurb,
.star__myth,
.star__why {
  display: block;
}

/* 卡片上的字号次序是死规矩：名字最大，说明次之，星象副标题再次，点题最小。
 * 手写体在同样 px 下看着比正文小，所以副标题给到跟说明差不多的字号，但绝不能越过名字。 */
.star__name {
  font-weight: 700;
  font-size: 1rem;
  line-height: 1.35;
}

/* 英文名另起一行：跟在中文名后面的话，"Unsolicited Advice Lab" 这种长名字会把
 * 标题挤成三行，整张卡片跟着变高——而卡片高度是决定星图能画多大的那个数。 */
.star__name small {
  display: block;
  font-weight: 400;
  font-size: 0.72rem;
  line-height: 1.35;
  color: var(--ink-muted);
}

.star__blurb {
  margin-top: 2px;
  font-size: 0.8rem;
  line-height: 1.45;
  color: var(--ink-muted);
}

/* 星名 + 半句引文用手写体，点题那句用正文体压小——引文是气质，点题是解释，
 * 两者混成一坨的话谁都读不进去。 */
.star__myth {
  margin-top: 6px;
  padding-top: 5px;
  border-top: 1px dashed var(--border);
  font-family: var(--font-hand);
  font-size: 0.82rem;
  line-height: 1.3;
  color: var(--ink-muted);
}

.star__why {
  margin-top: 3px;
  font-size: 0.68rem;
  line-height: 1.4;
  color: var(--ink-faint);
}

/* ── 静止偏好 ───────────────────────────────────────────────
 * 全站不动，只留悬停/聚焦时卡片的抽出——那不是装饰，是读到内容的唯一途径。
 * tokens.css 已经把 --drift-* 归零，这里再显式断掉动画，不指望 0s 的边界行为。
 */
@media (prefers-reduced-motion: reduce) {
  .star {
    animation: none;
  }

  /* 星尘那层呼吸也停：幅度虽小，但它仍然是"自己在动"的东西。 */
  .sky::before,
  .sky::after {
    animation: none;
  }

  .star__sticker {
    transition: none;
  }

  .star__card {
    transition: opacity 0.15s ease;
  }
}

/* ── 窄屏：塌成网格 ─────────────────────────────────────────────
 * 同一份标记，只换布局：卡片不再是悬停才出现的东西，它直接就是网格里的那张卡，
 * 贴纸变成贴在卡片角上的一枚章。点一次直达，不做"先出卡再点进去"的两级点击。
 */
@media (max-width: 860px) {
  html,
  body {
    height: auto;
    overflow: visible;
  }

  .sky {
    --badge: clamp(72px, 22vw, 98px);            /* 贴纸当徽章贴在卡角上，三处都读它 */
    /* 窄屏没有落点场，--star-base 那套算术在这儿没意义，星尘改写死的尺寸。 */
    --mote-s: 12px;
    --mote-m: 17px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1.75rem;
    height: auto;
    padding: 2.5rem 1.1rem 3rem;
  }

  /* 只留远层。三十枚星尘摊在 375px 宽上就不是"天"了，是噪点；
   * 而且窄屏是要往下滚的，固定在视口上的近层会一直杵在同一个位置抢眼。 */
  .sky::after {
    display: none;
  }

  /* 窄屏里角落那块回到最上面：先看见"这儿都有什么"，再往下逐张翻。 */
  .corner {
    position: static;
    order: -1;
    align-items: center;
    max-width: none;
    pointer-events: auto;
  }

  .slogan {
    align-items: center;
    gap: 0.16em;
    font-size: clamp(0.82rem, 3.4vw, 1.1rem);
    text-align: center;
  }

  /* 手机上这堆字要是按宽屏的比例放，头一屏就全被它占满了，一颗贴纸都看不见。 */
  .slogan__line:first-child {
    font-size: 1.2em;
  }

  .slogan__line {
    margin-left: 0;
    white-space: normal;
  }

  .slogan__line:nth-child(n) {
    margin-left: 0;
  }

  .sky__stars {
    position: static;
    width: 100%;
    pointer-events: auto;
  }

  /* 行距要能塞下贴纸探出卡片上沿的那一截（0.58 × --badge），否则上一行卡片的
   * 下半截会压住下一行卡片的贴纸。列距不受影响，所以两个方向分开给。 */
  .constellation {
    position: static;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(148px, 1fr));
    gap: calc(var(--badge) * 0.72) 1.5rem;
  }

  .star {
    position: static;
    width: auto;
    height: auto;
    margin: 0;
    transform: none;
    animation: none;
    will-change: auto;
  }

  .star__hit {
    position: relative;
    inset: auto;
    border-radius: 0;
  }

  .star__sticker {
    position: absolute;
    inset: auto -5px auto auto;                  /* base 规则里是 inset:0，先撤掉再靠右上角 */
    top: calc(var(--badge) * -0.58);
    z-index: 2;
    width: var(--badge);
    height: var(--badge);
    rotate: 9deg;
  }

  .star__hit:hover .star__sticker,
  .star__hit:focus-visible .star__sticker {
    scale: 1;
    rotate: 9deg;
    translate: none;
  }

  /* 贴纸压进卡片顶上那一截（约 0.42 × --badge），卡片就把顶边让出这么多，
   * 名字从贴纸下沿开始排。用留白让位而不是给标题加右边距——加右边距的话
   * "不听劝实验室"这种名字会被挤成两行。改 --badge 不用回来改这行。 */
  .star__card {
    padding-top: calc(var(--badge) * 0.46);
  }

  /* 贴纸占了右上角，印章挪到左上角，否则两个叠在一起谁也看不清。 */
  .star__stamp {
    right: auto;
    left: -7px;
  }

  .star__card {
    position: static;
    z-index: auto;
    width: auto;
    transform: none;
    transform-origin: 50% 50%;
    scale: 1;
    opacity: 1;
    pointer-events: auto;
    transition: none;
    box-shadow: var(--shadow);
  }

  .star__frame {
    aspect-ratio: 5 / 4;
  }

  .mark {
    width: clamp(56px, 16vw, 76px);
    height: clamp(56px, 16vw, 76px);
  }
}
