/* ── 收尾修正层：放在 app.css 之后加载 ───────────────── */

/* 画室：不显示图片名字（灯箱里仍会显示） */
.gallery__cap b {
  display: none;
}
.gallery__cap {
  padding-top: 0.35rem;
}

/* 影像室：视频舞台居中 */
.cinema__stage {
  display: grid;
  place-items: center;
  margin-inline: auto;
}
.cinema__stage iframe,
.cinema__stage video {
  width: min(100%, 960px);
  aspect-ratio: 16 / 9;
  height: auto;
  margin-inline: auto;
  display: block;
  border: 0;
}
.cinema__placeholder {
  margin-inline: auto;
}

/* ── 许愿区的蛋糕 ─────────────────────────────────────── */
.cake {
  display: grid;
  place-items: center;
  margin: 0 auto 0.4rem;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  width: min(260px, 62vw);
  transition: transform 0.16s var(--ease);
}
.cake:hover {
  transform: translateY(-2px);
}
.cake:active {
  transform: scale(0.96);
}
.cake svg {
  width: 100%;
  height: auto;
  display: block;
  filter: drop-shadow(0 10px 26px rgba(120, 170, 255, 0.28));
}
.cake__flame {
  transform-origin: 50% 100%;
  animation: flameFlicker 1.5s ease-in-out infinite;
}
.cake__flame:nth-of-type(2n) {
  animation-delay: -0.4s;
}
.cake__flame:nth-of-type(3n) {
  animation-delay: -0.9s;
}
@keyframes flameFlicker {
  0%,
  100% {
    transform: scaleY(1) scaleX(1);
    opacity: 0.95;
  }
  45% {
    transform: scaleY(1.18) scaleX(0.92);
    opacity: 1;
  }
  70% {
    transform: scaleY(0.94) scaleX(1.05);
    opacity: 0.88;
  }
}
.cake__hint {
  color: var(--faint);
  font-size: 0.76rem;
  text-align: center;
  margin-top: 0.3rem;
}
@media (prefers-reduced-motion: reduce) {
  .cake__flame {
    animation: none;
  }
}
/* ── 全局小助手：非门厅页面里的官方 Q 版希儿（Spine）────────────
   只有中间那块「抓取热区」吃指针事件，其余 pointer-events:none，
   所以小人再大也不会挡住底下的按钮和输入框，但按住它就能拖走。
   ──────────────────────────────────────────────────────────── */
.seele-buddy {
  position: fixed;
  left: 0;
  top: 0;
  width: clamp(104px, 11vw, 132px);
  z-index: 30;
  pointer-events: none;
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
  -webkit-tap-highlight-color: transparent;
  will-change: transform;
}
.seele-buddy[hidden] {
  display: none !important;
}
.seele-buddy .puppet--spine {
  min-height: 0;
}
.seele-buddy .puppet__stage {
  width: 100%;
  aspect-ratio: 276 / 454;
  max-height: none;
}
.seele-buddy .puppet {
  transform-origin: 50% 92%;
  transition: transform 0.16s var(--ease), filter 0.2s var(--ease);
}
.seele-buddy.is-dragging .puppet {
  transform: scale(1.05);
}
.seele-buddy.is-dragging .puppet__stage {
  filter: drop-shadow(0 0 14px rgba(140, 196, 255, 0.55));
}
/* 抓取热区：按住这里拖动 */
.seele-buddy__grab {
  position: absolute;
  left: 50%;
  top: 24%;
  width: 64%;
  height: 52%;
  transform: translateX(-50%);
  border-radius: 46%;
  pointer-events: auto;
  touch-action: none;
  cursor: grab;
}
.seele-buddy.is-grabbing .seele-buddy__grab {
  cursor: grabbing;
}
/* 气泡挂头顶；贴顶边时改挂下面，靠右半边时改成右对齐，免得飘出屏幕 */
.seele-buddy .puppet__bubble {
  left: 0;
  right: auto;
  top: auto;
  bottom: 100%;
  transform: none;
  white-space: normal;
  width: max-content;
  max-width: min(14rem, 56vw);
  margin-bottom: 0.35rem;
  line-height: 1.55;
  text-align: left;
  pointer-events: none;
}
.seele-buddy.is-flip-x .puppet__bubble {
  left: auto;
  right: 0;
  text-align: right;
}
.seele-buddy.is-near-top .puppet__bubble {
  bottom: auto;
  top: 100%;
  margin: 0.35rem 0 0;
}
.seele-buddy__confetti {
  position: absolute;
  bottom: 45%;
  color: var(--crimson-soft);
  pointer-events: none;
  animation: buddyConfetti 1.4s ease-out forwards;
  animation-delay: var(--delay, 0s);
}
.seele-buddy__confetti:nth-child(3n) {
  color: var(--azure-soft);
}
@keyframes buddyConfetti {
  0% {
    transform: translate3d(0, 0, 0) rotate(0deg);
    opacity: 0;
  }
  12% {
    opacity: 1;
  }
  100% {
    transform: translate3d(var(--dx, 0), -96px, 0) rotate(var(--rot, 180deg));
    opacity: 0;
  }
}

/* 手机：缩小 + 半透明，别压住正文和按钮 */
@media (max-width: 720px) {
  .seele-buddy {
    width: 92px;
    opacity: 0.8;
  }
  .seele-buddy .puppet__bubble {
    max-width: min(10rem, 58vw);
    font-size: 0.78rem;
  }
}

/* ── 门厅祝福区那只分层立绘（chibi.png）────────────────────── */
.wish__puppet {
  display: grid;
  place-items: end center;
  width: min(128px, 34vw);
  margin: 0.9rem auto 0.2rem;
  cursor: pointer;
}
.wish__puppet .puppet {
  min-height: 0;
}
.wish__puppet .puppet__img {
  width: 100%;
}
.wish__puppet .puppet__bubble {
  left: 50%;
  top: auto;
  bottom: 100%;
  transform: translateX(-50%);
  white-space: normal;
  width: max-content;
  max-width: min(15rem, 62vw);
  margin-bottom: 0.3rem;
  line-height: 1.6;
  text-align: center;
}

/* ── 右上角的她（Spine）：超采样画布，动画特效不再被边缘切掉 ─────────────
   槽位（.puppet__stage）尺寸不变，canvas 以槽位为中心向外放大：
   多出来的空间正好装下「惊讶」这类动画的漫画特效外溢。
   放大多少由 spineSeele.js 按所有动画的最大包络算，这里只给兜底尺寸。 */
.hero__corner,
.hero__corner .puppet,
.puppet--spine,
.puppet--spine .puppet__stage {
  overflow: visible;
}
.puppet--spine .puppet__stage {
  position: relative;
}
.puppet--spine .puppet__stage > canvas {
  position: absolute;
  left: 50%;
  top: 50%;
  display: block;
  width: var(--seele-canvas-w, 172%);
  height: var(--seele-canvas-h, 140%);
  transform: translate(-50%, -50%);
  /* 画布不再挡点击：热区仍由 #corner-hero（原槽位大小）承担 */
  pointer-events: none;
}
/* 修正：Spine 运行时会把画布写成固定 px 尺寸（比槽位大，留给动作包络余量），
   所以槽位必须自己定高、画布脱离文档流并居中 —— 否则画布会把容器越撑越高
   （2026-10 那次 24252px 的鬼故事就是这么来的）。 */
.seele-buddy .puppet__stage {
  position: relative;
  width: 100%;
  height: 0;
  padding-bottom: 164.49%; /* 454 / 276 */
  max-height: none;
}
.seele-buddy .puppet__stage canvas {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 100%;
  height: 100%;
  transform: translate(-50%, -50%);
  display: block;
}

/* ── 唱片机（#/music）：只做说明页，播放器面板复用右上角那台 ──── */
.turntable__lead {
  color: var(--ash);
  font-size: 0.92rem;
}
.turntable__list {
  display: grid;
  gap: 0.45rem;
  margin: 0.9rem 0 1.2rem;
  color: var(--ash);
  font-size: 0.92rem;
}
.turntable__list li {
  position: relative;
  padding-left: 1.1rem;
}
.turntable__list li::before {
  content: '♪';
  position: absolute;
  left: 0;
  color: var(--azure-dim);
}
.turntable__list b {
  font-family: var(--f-display);
  font-size: 1.2rem;
  color: var(--azure-soft);
}

/* 手机：导航到 8 项了，挤一挤但必须还是一行、整页不溢出 */
@media (max-width: 720px) {
  .topbar__inner {
    gap: 0.6rem;
  }
  .nav {
    gap: 0.1rem;
  }
  .nav__link {
    padding: 0.4rem 0.5rem;
    font-size: 0.8rem;
    letter-spacing: 0.04em;
  }
}

/* 手机：抓取热区再收一点，别把页面上的按钮压住（小人本身还是可拖的） */
@media (max-width: 720px) {
  .seele-buddy__grab {
    top: 30%;
    width: 54%;
    height: 42%;
  }
}

/* ── 门厅右上角 Spine 画布：定高 + 居中 + 不许撑破页面 ────
   spineSeele.js 会把 canvas 写成内联像素尺寸（可能比容器大），
   如果不管，容器会被撑高/撑宽并造成整页横向溢出。 */
.hero__corner .puppet--spine .puppet__stage {
  position: relative;
  height: 0;
  padding-bottom: 164.49%;
  overflow: visible;
}
.hero__corner .puppet--spine canvas {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  max-width: none;
}
/* 兜底：任何情况下都不让画布把页面撑出横向滚动 */
#corner-hero {
  overflow: visible;
  max-width: 100%;
}
html,
body {
  overflow-x: hidden;
}

/* 飘字层本身不产生滚动（它已经在 fixed + overflow:hidden 里，这里再保一道） */
.drift {
  max-width: 100vw;
}

/* ── 跨页面小人在窄屏与游戏页让位（别压住游戏 HUD）────── */
@media (max-width: 720px) {
  .seele-buddy {
    opacity: 0.62;
    transform-origin: bottom left;
  }
  .seele-buddy:hover,
  .seele-buddy.is-dragging {
    opacity: 0.95;
  }
}
/* 游乐场：HUD 在左上，小人固定左下也容易挡，直接收起来（需要时仍可拖出来） */
body[data-room='games'] .seele-buddy {
  opacity: 0.28;
}
body[data-room='games'] .seele-buddy:hover,
body[data-room='games'] .seele-buddy.is-dragging {
  opacity: 1;
}

/* 祝福板块去掉蛋糕后收紧高度，只留希儿小人 */
.wish {
  padding-block: 1.1rem;
}
.wish__panel {
  display: grid;
  gap: 0.35rem;
}
.wish .cake-host,
.cake {
  display: none;
}
.wish__puppet {
  margin: 0 auto 0.2rem;
}
.wish__puppet .puppet {
  min-height: 0;
}
.wish__puppet .puppet__img {
  width: clamp(96px, 22vw, 150px);
}

/* ── 祝福板块：希儿放在文字正右边，并放大 ─────────────── */
.wish__panel {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  column-gap: clamp(0.8rem, 2.5vw, 2rem);
}
.wish__panel > .wish__puppet {
  grid-column: 2;
  grid-row: 1 / span 6;
  margin: 0;
  align-self: center;
}
.wish__panel > .wish__kicker,
.wish__panel > .wish__title,
.wish__panel > .wish__sub,
.wish__panel > .wish__row,
.wish__panel > .wish__fx {
  grid-column: 1;
}
.wish__puppet .puppet {
  min-height: 0;
}
.wish__puppet .puppet__img {
  width: clamp(132px, 30vw, 232px);
  filter: drop-shadow(0 10px 26px rgba(120, 170, 255, 0.3));
}
.wish__puppet .puppet__bubble {
  white-space: normal;
  max-width: 14rem;
  text-align: center;
  line-height: 1.6;
}
.wish__puppet {
  cursor: pointer;
}
@media (max-width: 720px) {
  .wish__panel {
    grid-template-columns: minmax(0, 1fr) auto;
    column-gap: 0.7rem;
  }
  .wish__puppet .puppet__img {
    width: clamp(96px, 26vw, 132px);
  }
}

/* 祝福区希儿：用 ID 提高优先级，确保比之前的规则更大 */
#wish-puppet .puppet__img {
  width: clamp(150px, 24vw, 250px);
  max-width: none;
}
@media (max-width: 720px) {
  #wish-puppet .puppet__img {
    width: clamp(104px, 28vw, 148px);
  }
}

/* ── 祝福板块的统计行：略微明显一点 ───────────────────── */
.wish__crowd {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  flex-wrap: wrap;
  margin-top: 0.55rem;
  padding: 0.32rem 0.7rem;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  background: linear-gradient(180deg, rgba(56, 78, 152, 0.5), rgba(20, 30, 66, 0.55));
  color: var(--ash);
  font-size: 0.82rem;
  letter-spacing: 0.02em;
}
.wish__crowd b {
  font-family: var(--f-display);
  font-size: 1.22rem;
  line-height: 1;
  color: var(--azure-soft);
  text-shadow: 0 0 12px rgba(140, 196, 255, 0.5);
  margin: 0 0.05rem;
}
.wish__stats .wish__count b {
  font-size: 2.5rem;
}
@media (max-width: 720px) {
  .wish__crowd {
    font-size: 0.76rem;
    padding: 0.28rem 0.55rem;
  }
  .wish__crowd b {
    font-size: 1.05rem;
  }
}

/* ── 角色故事（#/library）· 配图：改小、靠右、正文环绕 ────────────────
   规矩：图只放官方图，所以这里的排版让正文绕着图走 —— 图在源码里必须排在正文
   之前（story.js 把 figures() 提到了段落前面），float:right 才会被后面的文字环绕。
   窄屏不环绕：取消浮动、单列居中。 */
.story__figs {
  float: right;
  width: min(330px, 40%);
  margin: 0.4rem 0 1rem 1.6rem;
  /* 两张也竖着排，别再横着把正文挤成一条 */
  grid-template-columns: 1fr;
  gap: 0.9rem;
}
@media (min-width: 680px) {
  .story__figs--two {
    grid-template-columns: 1fr;
  }
}
.story__fig {
  max-width: 100%;
}
.story__fig figcaption {
  margin-top: 0.45rem;
  font-size: 0.72rem;
  line-height: 1.7;
}
.story__src {
  font-size: 0.62rem;
}

/* 浮动会脱离文档流：让故事页的容器自己包住它，图不会戳出面板外面 */
.story-page .panel,
.story-page .tl__body {
  display: flow-root;
}

/* 手机／窄屏：不环绕，单列居中 */
@media (max-width: 680px) {
  .story__figs,
  .tl__body .story__figs {
    float: none;
    width: min(100%, 420px);
    margin: 1.1rem auto 0.2rem;
  }
  .story__fig figcaption {
    font-size: 0.7rem;
  }
}

/* 祝福区希儿：锁在右侧栏内，不许再挤出面板 */
.wish__panel {
  grid-template-columns: minmax(0, 1fr) clamp(150px, 24vw, 280px);
  align-items: center;
}
#wish-puppet {
  min-width: 0;
  max-width: 100%;
  justify-self: end;
  align-self: center;
  display: grid;
  place-items: center;
  overflow: visible;
}
#wish-puppet .puppet {
  width: 100%;
  min-height: 0;
}
#wish-puppet .puppet__img {
  width: 100%;
  max-width: 280px;
  height: auto;
  margin-inline: auto;
}
@media (max-width: 720px) {
  .wish__panel {
    grid-template-columns: minmax(0, 1fr) clamp(104px, 28vw, 150px);
    column-gap: 0.6rem;
  }
  #wish-puppet .puppet__img {
    max-width: 150px;
  }
}

/* ── 影像室：把「开始放映」做得一眼就看见 ─────────────── */
.cinema__placeholder {
  position: relative;
  display: grid;
  place-items: center;
  gap: 0.7rem;
  width: 100%;
  min-height: clamp(190px, 32vh, 300px);
  padding: 1.4rem;
  border: 1px solid var(--line-strong);
  border-radius: var(--r);
  background:
    radial-gradient(65% 60% at 50% 38%, rgba(140, 196, 255, 0.16), transparent 70%),
    linear-gradient(180deg, rgba(40, 58, 118, 0.55), rgba(16, 24, 56, 0.65));
  color: var(--silk);
  cursor: pointer;
  overflow: hidden;
  transition: border-color 0.25s var(--ease), box-shadow 0.3s var(--ease), transform 0.15s var(--ease);
}
.cinema__placeholder:hover {
  border-color: var(--azure);
  box-shadow: 0 0 34px rgba(140, 196, 255, 0.35);
}
.cinema__placeholder:active {
  transform: scale(0.995);
}
/* 呼吸光晕，把视线拉过去 */
.cinema__placeholder::after {
  content: '';
  position: absolute;
  inset: -20%;
  background: radial-gradient(45% 45% at 50% 45%, rgba(140, 196, 255, 0.22), transparent 72%);
  animation: cinemaBreathe 3.4s ease-in-out infinite;
  pointer-events: none;
}
@keyframes cinemaBreathe {
  0%,
  100% {
    opacity: 0.55;
    transform: scale(0.96);
  }
  50% {
    opacity: 1;
    transform: scale(1.06);
  }
}
/* 大播放键 */
.cinema__play-glyph {
  position: relative;
  z-index: 1;
  width: clamp(64px, 9vw, 88px);
  height: clamp(64px, 9vw, 88px);
  display: grid;
  place-items: center;
  border-radius: 50%;
  font-size: clamp(24px, 3.4vw, 34px);
  line-height: 1;
  color: #fff;
  background: linear-gradient(160deg, rgba(140, 196, 255, 0.95), rgba(90, 130, 220, 0.9));
  box-shadow:
    0 0 0 6px rgba(140, 196, 255, 0.14),
    0 0 32px rgba(140, 196, 255, 0.55);
  animation: playNudge 2.6s ease-in-out infinite;
  padding-left: 4px; /* ▶ 视觉居中 */
}
@keyframes playNudge {
  0%,
  72%,
  100% {
    transform: scale(1);
  }
  80% {
    transform: scale(1.08);
  }
  88% {
    transform: scale(1);
  }
}
/* 提示文字：发光大字 */
#cinema-play-hint {
  position: relative;
  z-index: 1;
  display: inline-block;
  padding: 0.4rem 1.1rem;
  font-size: clamp(1rem, 2.4vw, 1.32rem);
  font-weight: 500;
  letter-spacing: 0.18em;
  color: var(--azure-soft);
  text-shadow: 0 0 16px rgba(140, 196, 255, 0.65), 0 0 34px rgba(140, 196, 255, 0.35);
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  background: rgba(20, 30, 66, 0.5);
}
#cinema-play-hint::before {
  content: '▶ ';
  color: var(--crimson-soft);
}
@media (prefers-reduced-motion: reduce) {
  .cinema__placeholder::after,
  .cinema__play-glyph {
    animation: none;
  }
}
@media (max-width: 720px) {
  .cinema__placeholder {
    min-height: 160px;
    gap: 0.5rem;
  }
  #cinema-play-hint {
    letter-spacing: 0.1em;
    padding: 0.32rem 0.8rem;
  }
}

/* ═══ 唱片机：常驻右上角（顶栏下方，不压导航和头像）══════════════
   收起时是一条窄的「正在播放」条；点开是完整歌单。
   顶栏里原来那个 ♪ 按钮不要了（面板自带开关），这里把它收掉，
   顺便盖掉 app.css 里那套会把它错当成浮层的 .music 规则。
   ────────────────────────────────────────────────────────── */
.topbar .music {
  display: none !important;
}

.tt {
  position: fixed;
  top: var(--tt-top, 4.5rem);
  right: max(0.7rem, env(safe-area-inset-right));
  z-index: 36;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.4rem;
  width: min(336px, calc(100vw - 1.4rem));
  color: var(--ash);
  pointer-events: none; /* 只有两条自己吃指针，其余地方不挡内容 */
}
.tt__bar,
.tt__panel {
  pointer-events: auto;
}

/* 收起状态：一条窄条（黑胶 + 曲名 + 细进度 + 展开） */
.tt__bar {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  width: min(214px, 100%);
  padding: 0.28rem 0.42rem 0.28rem 0.3rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: linear-gradient(180deg, rgba(40, 58, 116, 0.6), rgba(16, 24, 58, 0.68));
  backdrop-filter: blur(12px) saturate(130%);
  box-shadow: 0 8px 22px rgba(4, 8, 24, 0.38), inset 0 1px 0 rgba(200, 226, 255, 0.1);
  opacity: 0.84;
  transition: opacity 0.22s var(--ease), border-color 0.22s var(--ease);
}
.tt__bar:hover,
.tt.is-open .tt__bar {
  opacity: 1;
  border-color: var(--line-strong);
}

.tt__disc {
  flex: none;
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}
.tt__vinyl {
  position: relative;
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background:
    repeating-radial-gradient(circle at 50% 50%, rgba(255, 255, 255, 0.05) 0 1px, transparent 1px 3px),
    radial-gradient(circle at 50% 50%, #2b3568 0 42%, #14193a 43% 100%);
  box-shadow: inset 0 0 0 1px rgba(140, 196, 255, 0.24);
}
.tt__vinyl::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: linear-gradient(115deg, rgba(255, 255, 255, 0.18), transparent 46%);
}
.tt__vinyl i {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--crimson), var(--crimson-dim));
  box-shadow: 0 0 0 2px rgba(10, 14, 32, 0.85);
}
.tt.is-playing .tt__vinyl,
.tt.is-playing .tt__platter {
  animation: ttSpin 4.4s linear infinite;
}
@keyframes ttSpin {
  to {
    transform: rotate(360deg);
  }
}

.tt__rollbox {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
}
.tt__roll {
  display: inline-block;
  white-space: nowrap;
  font-size: 0.78rem;
  color: var(--azure-soft);
}
.tt__roll.is-rolling {
  animation: ttRoll 16s linear infinite;
}
@keyframes ttRoll {
  from {
    transform: translateX(0);
  }
  to {
    transform: translateX(-50%);
  }
}

.tt__mini {
  flex: none;
  width: 26px;
  height: 3px;
  border-radius: 2px;
  background: rgba(140, 196, 255, 0.2);
  overflow: hidden;
}
.tt__mini i {
  display: block;
  width: 100%;
  height: 100%;
  transform: scaleX(0);
  transform-origin: left center;
  background: linear-gradient(90deg, var(--azure), var(--crimson-soft));
}

.tt__fold {
  flex: none;
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  border: 0;
  border-radius: 50%;
  background: none;
  color: var(--faint);
  font-size: 0.8rem;
  line-height: 1;
  cursor: pointer;
  transition: transform 0.22s var(--ease), color 0.22s var(--ease);
}
.tt__fold:hover {
  color: var(--silk);
}
.tt.is-open .tt__fold {
  transform: rotate(180deg);
}

/* 展开状态：完整面板（歌单在里面滚，面板不超出屏幕） */
.tt__panel {
  display: flex;
  flex-direction: column;
  gap: 0.42rem;
  width: 100%;
  max-height: min(72vh, 34rem);
  overflow: hidden auto;
  padding: 0.7rem 0.8rem 0.75rem;
  border: 1px solid var(--line-strong);
  border-radius: 14px;
  background: linear-gradient(180deg, rgba(44, 62, 124, 0.9), rgba(14, 22, 54, 0.95));
  backdrop-filter: blur(14px) saturate(130%);
  box-shadow: 0 20px 48px rgba(4, 8, 24, 0.55), inset 0 1px 0 rgba(200, 226, 255, 0.12);
  animation: ttIn 0.22s var(--ease);
}
@keyframes ttIn {
  from {
    opacity: 0;
    transform: translateY(-6px);
  }
}

.tt__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem;
}
.tt__kicker {
  font-family: var(--f-mono);
  font-size: 0.62rem;
  letter-spacing: 0.24em;
  color: var(--azure-dim);
}
.tt__count {
  font-family: var(--f-mono);
  font-size: 0.62rem;
  color: var(--faint);
}

/* 唱片机本体：转盘 + 唱臂 */
.tt__stage {
  position: relative;
  height: 62px;
  margin: 0.1rem 0 0;
}
.tt__platter {
  position: absolute;
  left: 0;
  top: 3px;
  display: grid;
  place-items: center;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background:
    repeating-radial-gradient(circle at 50% 50%, rgba(255, 255, 255, 0.045) 0 1px, transparent 1px 3.5px),
    radial-gradient(circle at 50% 50%, #2f3a72 0 40%, #10142f 41% 100%);
  box-shadow: inset 0 0 0 1px rgba(140, 196, 255, 0.22), 0 6px 18px rgba(4, 8, 24, 0.4);
}
.tt__label {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 50%, var(--crimson-soft), var(--crimson-dim));
  box-shadow: 0 0 10px rgba(255, 92, 120, 0.35);
}
.tt__arm {
  position: absolute;
  left: 50px;
  top: 1px;
  width: 56px;
  height: 3px;
  border-radius: 2px;
  background: linear-gradient(90deg, rgba(200, 226, 255, 0.72), rgba(140, 196, 255, 0.22));
  transform-origin: 0 50%;
  transform: rotate(16deg);
  transition: transform 0.4s var(--ease);
}
.tt.is-playing .tt__arm {
  transform: rotate(30deg);
}
.tt__arm i {
  position: absolute;
  left: -4px;
  top: -3px;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--azure-dim);
  box-shadow: 0 0 0 2px rgba(10, 14, 32, 0.85);
}

.tt__now {
  font-family: var(--f-display);
  font-size: 1.06rem;
  line-height: 1.35;
  color: var(--silk);
}
.tt__credit {
  font-size: 0.66rem;
  line-height: 1.5;
  color: var(--faint);
  word-break: break-word;
}
.tt__note {
  font-size: 0.7rem;
  color: var(--azure-dim);
}

.tt__controls {
  display: flex;
  align-items: center;
  gap: 0.35rem;
}
.tt__btn {
  min-width: 30px;
  height: 28px;
  padding: 0 0.45rem;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: rgba(12, 18, 44, 0.5);
  color: var(--ash);
  font-size: 0.85rem;
  line-height: 1;
  cursor: pointer;
}
.tt__btn:hover {
  color: var(--silk);
  border-color: var(--line-strong);
}
.tt__btn--main {
  min-width: 44px;
  border-color: var(--line-strong);
  color: var(--azure-soft);
}
.tt__time {
  margin-left: auto;
  font-family: var(--f-mono);
  font-size: 0.64rem;
  color: var(--faint);
}

.tt__seek {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 3px;
  margin: 0.05rem 0 0;
  border-radius: 2px;
  background: linear-gradient(
    90deg,
    var(--azure) 0 var(--tt-fill, 0%),
    rgba(140, 196, 255, 0.2) var(--tt-fill, 0%) 100%
  );
  cursor: pointer;
}
.tt__seek::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 10px;
  height: 10px;
  border: 0;
  border-radius: 50%;
  background: var(--azure);
  box-shadow: 0 0 6px rgba(140, 196, 255, 0.6);
}
.tt__seek::-moz-range-thumb {
  width: 10px;
  height: 10px;
  border: 0;
  border-radius: 50%;
  background: var(--azure);
}

.tt__row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
.tt__vol {
  display: flex;
  flex: 1 1 auto;
  align-items: center;
  gap: 0.35rem;
  min-width: 0;
  font-size: 0.64rem;
  color: var(--faint);
}
.tt__vol input {
  flex: 1 1 auto;
  min-width: 0;
  height: 3px;
  -webkit-appearance: none;
  appearance: none;
  border-radius: 2px;
  background: rgba(140, 196, 255, 0.2);
  cursor: pointer;
}
.tt__vol input::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 9px;
  height: 9px;
  border: 0;
  border-radius: 50%;
  background: var(--crimson-soft);
}
.tt__vol input::-moz-range-thumb {
  width: 9px;
  height: 9px;
  border: 0;
  border-radius: 50%;
  background: var(--crimson-soft);
}

.tt__filter {
  width: 100%;
  padding: 0.32rem 0.5rem;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: rgba(8, 12, 32, 0.55);
  color: var(--silk);
  font-size: 0.74rem;
}
.tt__filter::placeholder {
  color: var(--faint);
}

/* 全部曲目：列表自己滚，序号 / 曲名 / 说明 / 版权一行不少 */
.tt__list {
  margin: 0;
  padding: 0;
  max-height: min(38vh, 16rem);
  overflow-y: auto;
  overscroll-behavior: contain;
  border-top: 1px solid var(--line);
  scrollbar-width: thin;
}
.tt__item {
  display: grid;
  grid-template-columns: 1.6rem 1fr auto;
  gap: 0.45rem;
  align-items: start;
  width: 100%;
  padding: 0.38rem 0.3rem;
  border: 0;
  border-bottom: 1px solid var(--line);
  background: none;
  color: var(--ash);
  text-align: left;
  cursor: pointer;
}
.tt__item:hover {
  background: rgba(140, 196, 255, 0.08);
  color: var(--silk);
}
.tt__item.is-active {
  color: var(--azure-soft);
  background: linear-gradient(90deg, rgba(255, 92, 120, 0.16), rgba(140, 196, 255, 0.12));
  box-shadow: inset 2px 0 0 var(--crimson);
}
.tt__item.is-bad .tt__title {
  text-decoration: line-through;
  text-decoration-color: var(--crimson);
}
.tt__no {
  padding-top: 0.12rem;
  font-family: var(--f-mono);
  font-size: 0.64rem;
  color: var(--faint);
}
.tt__meta {
  min-width: 0;
}
.tt__title {
  display: block;
  font-size: 0.82rem;
}
.tt__sub {
  display: block;
  font-size: 0.63rem;
  line-height: 1.45;
  color: var(--faint);
}
.tt__sub--credit {
  color: rgba(140, 196, 255, 0.55);
}
.tt__eq {
  display: none;
  align-items: flex-end;
  gap: 1.5px;
  height: 12px;
  padding-top: 0.2rem;
}
.tt__item.is-now .tt__eq {
  display: flex;
}
.tt__eq i {
  width: 2px;
  height: 4px;
  background: var(--crimson-soft);
  animation: ttEq 0.9s ease-in-out infinite;
}
.tt__eq i:nth-child(2) {
  animation-delay: 0.15s;
}
.tt__eq i:nth-child(3) {
  animation-delay: 0.3s;
}
@keyframes ttEq {
  0%,
  100% {
    height: 4px;
  }
  50% {
    height: 12px;
  }
}
.tt__hint {
  font-size: 0.63rem;
  color: var(--faint);
}

/* 手机：再小一点、贴边，不许撑出横向滚动 */
@media (max-width: 720px) {
  .tt {
    right: max(0.5rem, env(safe-area-inset-right));
    width: min(300px, calc(100vw - 1rem));
  }
  .tt__bar {
    width: min(188px, 100%);
  }
  .tt__panel {
    max-height: min(68vh, 30rem);
  }
  .tt__list {
    max-height: min(34vh, 13rem);
  }
  .tt__roll {
    font-size: 0.74rem;
  }
}
@media (max-width: 420px) {
  .tt {
    width: calc(100vw - 1rem);
  }
  .tt__bar {
    width: min(170px, 100%);
  }
  .tt__stage {
    height: 54px;
  }
}

/* ── 唱片机房间页（#/music）里的完整曲单 ─────────────────── */
.turntable__songs {
  display: grid;
  gap: 0.15rem;
  margin: 0.9rem 0 1.1rem;
  border-top: 1px solid var(--line);
}
.turntable__song {
  display: grid;
  grid-template-columns: 2.2rem 1fr;
  gap: 0.6rem;
  padding: 0.5rem 0.2rem;
  border-bottom: 1px solid var(--line);
}
.turntable__song-no {
  padding-top: 0.15rem;
  font-family: var(--f-mono);
  font-size: 0.7rem;
  color: var(--faint);
}
.turntable__song-title {
  color: var(--silk);
  font-size: 0.95rem;
}
.turntable__song-note {
  display: block;
  color: var(--azure-dim);
  font-size: 0.78rem;
}
.turntable__song-credit {
  display: block;
  color: var(--faint);
  font-size: 0.72rem;
  line-height: 1.5;
}

/* ── 唱片机修正：面板是纵向 flex，固定高度的部件被压扁会让转盘压住曲名 ── */
.tt__panel > * {
  flex: 0 0 auto;
}
.tt__stage {
  flex: 0 0 62px;
}
@media (max-width: 420px) {
  .tt__stage {
    flex: 0 0 54px;
  }
}
/* ── 唱片机修正二：让曲目列表吃掉面板的余量，避免面板和列表两层滚动条 ── */
.tt__list {
  flex: 1 1 auto;
  min-height: 7rem;
}
/* 收起条上的曲名区域也能点开面板（手机上 chevron 有点小） */
.tt__rollbox {
  cursor: pointer;
}

/* ══════════════════════════════════════════════════════════════════════
   《彼岸潮汐》游乐场小游戏：手机版收尾（追加段，勿改上文）
   app.css 的小屏规则把 .stage 压到 240px，比开始面板还矮，
   overflow:hidden 会把「开始」按钮切掉一截 —— 这里按内容把舞台放回去。
   ══════════════════════════════════════════════════════════════════════ */
@media (max-width: 720px) {
  .stage {
    min-height: 340px;
  }
  .stage__overlay {
    padding: 1.1rem 1rem;
    gap: 0.7rem;
  }
  .stage__note {
    max-width: 100%;
    font-size: 0.82rem;
    line-height: 1.7;
  }
  /* HUD 在窄屏靠整块换行排开，别让每块自己被压扁成两行 */
  .hud {
    flex-wrap: wrap;
    gap: 0.5rem 0.9rem;
    padding: 0.6rem 0.8rem;
  }
  .hud__item {
    font-size: 0.8rem;
    white-space: nowrap;
  }
  .hud__spacer {
    display: none;
  }
  .hud__score {
    padding: 0.25rem 0.7rem 0.3rem;
  }
  .hud__score b {
    min-width: 0;
    font-size: 1.4rem;
  }
}

/* 祝福区希儿：再放大一档 + 往左挪一点 */
.wish__panel {
  grid-template-columns: minmax(0, 1fr) clamp(190px, 30vw, 350px);
}
#wish-puppet {
  justify-self: center;
  padding-right: clamp(0.3rem, 2.2vw, 1.6rem);
}
#wish-puppet .puppet__img {
  max-width: 350px;
}
@media (max-width: 720px) {
  .wish__panel {
    grid-template-columns: minmax(0, 1fr) clamp(120px, 32vw, 170px);
  }
  #wish-puppet {
    padding-right: 0.2rem;
  }
  #wish-puppet .puppet__img {
    max-width: 170px;
  }
}

/* 修正：justify-self 必须是 stretch，靠右内边距实现「往左一点」，否则宽度会缩成自适应 */
#wish-puppet {
  justify-self: stretch;
  padding-right: clamp(0.4rem, 3vw, 2.2rem);
  padding-left: 0;
}
#wish-puppet .puppet {
  width: 100%;
}
#wish-puppet .puppet__img {
  width: 100%;
  max-width: 100%;
  height: auto;
}
.wish__panel {
  grid-template-columns: minmax(0, 1fr) clamp(200px, 30vw, 360px);
}
@media (max-width: 720px) {
  #wish-puppet {
    padding-right: 0.35rem;
  }
  .wish__panel {
    grid-template-columns: minmax(0, 1fr) clamp(130px, 34vw, 185px);
  }
}

/* 祝福区希儿：直接在容器上定宽（它当前在按钮那一行里，不归网格列管） */
#wish-puppet {
  flex: 0 0 auto;
  width: clamp(200px, 26vw, 340px);
  max-width: 100%;
  margin-left: 0;
  margin-right: auto;
  justify-self: auto;
  padding-right: 0;
}
#wish-puppet .puppet {
  width: 100%;
  min-height: 0;
}
#wish-puppet .puppet__img {
  width: 100%;
  max-width: 100%;
  height: auto;
  display: block;
}
/* 让它在行内靠左一点，别贴着面板右边缘 */
.wish__row {
  align-items: center;
  gap: clamp(0.6rem, 2vw, 1.4rem);
}
@media (max-width: 720px) {
  #wish-puppet {
    width: clamp(120px, 34vw, 168px);
  }
}

/* 祝福区希儿的话：挪到左侧偏上，别被面板/按钮遮住 */
#wish-puppet {
  position: relative;
}
#wish-puppet .puppet {
  position: relative;
}
#wish-puppet .puppet__bubble {
  position: absolute;
  left: auto;
  right: calc(100% - 12px);
  top: -0.2rem;
  transform: none;
  max-width: 16rem;
  white-space: normal;
  text-align: right;
  z-index: 5;
}
#wish-puppet .puppet__bubble.is-pop {
  animation: bubbleInLeft 0.3s var(--ease);
}
@keyframes bubbleInLeft {
  from {
    opacity: 0;
    transform: translateX(8px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}
@media (max-width: 720px) {
  #wish-puppet .puppet__bubble {
    right: auto;
    left: 0;
    top: -2.4rem;
    text-align: left;
    max-width: 12rem;
  }
}

/* ══ 祝福板块 · 说话气泡 v2 ══════════════════════════════════════
   1) 去蓝框：那圈淡蓝矩形就是 app.css 里 .puppet__bubble 的
      `border: 1px solid var(--line-strong)`（rgba(140,196,255,.42)）+ 直角。
      这里 border:0 + 大圆角 + 半透明底 + 柔和光晕 + 小尖尾巴。
   2) 可爱字体：只用系统字体栈（'Arial Rounded MT Bold' / 幼圆 / 苹方 …），
      圆角、加粗、稍大字号、柔和文字阴影，零外部依赖。
   3) 纵向：home.js 量出「标题中心 − 小人容器顶」→ --wb-cy，配合
      translateY(-50%)，所以气泡自己的高矮不影响对齐。
      横向：home.js 量出标题文字右缘，--wb-max 让开标题；窄屏放不下时
      加 .is-stacked，气泡挪进小人那一列、悬在她上方，纵向仍然对齐标题。
   4) 打字机 / 弹跳 / 光晕 / 淡出见 seelePuppet.js；prefers-reduced-motion
      下 tokens.css 已经一刀切掉动画，JS 那边也会直接整句显示。
   ═════════════════════════════════════════════════════════════ */
/* #wish-puppet 原来是 display:grid，而 .puppet 又是 place-items 的 grid 容器：
   绝对定位的气泡会被它们的对齐规则抢走 auto 的 bottom（盒子被上下拉死，
   高度只剩 padding，位置的 top 也会被忽略）。把这两层都降回普通块级，
   定位父级就只有一个普通 relative 块，几何完全可预测。 */
#wish-puppet {
  display: block;
}
#wish-puppet .puppet {
  display: block;
  position: static;
}
#wish-puppet .puppet__bubble {
  --wb-cy: 50%;
  --wb-max: 15.5rem;
  position: absolute;
  z-index: 6;
  left: auto;
  right: calc(100% + 10px);
  top: var(--wb-cy);
  /* 旧规则里有 bottom:100%（.wish__puppet .puppet__bubble）——上下一起来，
     盒子被拉死，高度只剩 padding、文字溢出。必须显式按掉。 */
  bottom: auto;
  transform: var(--wb-tr, translateY(-50%));
  transform-origin: 100% 50%;
  border: 0;
  border-radius: 18px 18px 6px 18px;
  padding: 0.5rem 0.95rem 0.55rem;
  max-width: var(--wb-max);
  white-space: normal;
  text-align: left;
  line-height: 1.5;
  font-family: 'Arial Rounded MT Bold', 'YuanTi SC', 'YouYuan', '幼圆', 'PingFang SC',
    'HarmonyOS Sans SC', 'Microsoft YaHei UI', 'Microsoft YaHei', 'Comic Sans MS', ui-rounded,
    system-ui, sans-serif;
  font-size: 1rem;
  font-weight: 700;
  font-synthesis-weight: auto;
  letter-spacing: 0.02em;
  color: #f5f8ff;
  text-shadow: 0 1px 0 rgba(24, 38, 84, 0.42), 0 0 6px rgba(170, 205, 255, 0.38);
  background: linear-gradient(180deg, rgba(74, 98, 180, 0.9), rgba(33, 47, 102, 0.93));
  box-shadow:
    0 8px 22px rgba(8, 14, 40, 0.5),
    0 0 20px rgba(150, 195, 255, 0.3),
    inset 0 1px 0 rgba(226, 238, 255, 0.3);
  backdrop-filter: blur(6px) saturate(1.15);
  -webkit-backdrop-filter: blur(6px) saturate(1.15);
  transition: opacity 0.34s ease, transform 0.34s ease;
}
/* 柔和光晕（气泡后面一层淡淡的蓝光，慢慢呼吸） */
#wish-puppet .puppet__bubble::before {
  content: '';
  position: absolute;
  inset: -13px;
  z-index: -1;
  border-radius: 30px;
  background: radial-gradient(62% 72% at 62% 44%, rgba(168, 208, 255, 0.36), transparent 72%);
  filter: blur(9px);
  pointer-events: none;
  animation: wbHalo 3.4s ease-in-out infinite;
}
/* 小尖尾巴：朝向右侧的小人 */
#wish-puppet .puppet__bubble::after {
  content: '';
  position: absolute;
  top: 50%;
  right: -5px;
  width: 12px;
  height: 12px;
  margin-top: -6px;
  border-radius: 3px;
  background: linear-gradient(135deg, rgba(74, 98, 180, 0.9), rgba(33, 47, 102, 0.93));
  box-shadow: 3px -3px 8px rgba(150, 195, 255, 0.2);
  transform: rotate(45deg);
  pointer-events: none;
}
#wish-puppet .puppet__bubble.is-pop {
  animation: wbPop 0.44s var(--ease);
}
#wish-puppet .puppet__bubble.is-out {
  opacity: 0;
  transform: var(--wb-tr, translateY(-50%)) scale(0.94);
}
@keyframes wbPop {
  0% {
    opacity: 0;
    transform: var(--wb-tr, translateY(-50%)) scale(0.82);
  }
  55% {
    opacity: 1;
    transform: var(--wb-tr, translateY(-50%)) scale(1.05);
  }
  78% {
    transform: var(--wb-tr, translateY(-50%)) scale(0.985);
  }
  100% {
    opacity: 1;
    transform: var(--wb-tr, translateY(-50%)) scale(1);
  }
}
@keyframes wbHalo {
  0%,
  100% {
    opacity: 0.55;
  }
  50% {
    opacity: 0.95;
  }
}

/* 窄屏：左列被标题文字占满，气泡搁在标题高度上必然压字。
   于是横着挪进「小人那一列」，纵向依旧对齐标题中心，
   悬在小人上方、尾巴朝下指着她。 */
#wish-puppet.is-stacked .puppet__bubble {
  right: auto;
  left: 50%;
  --wb-tr: translateX(-50%) translateY(-50%);
  transform-origin: 50% 100%;
  max-width: 100%;
  border-radius: 18px 18px 18px 6px;
  font-size: 0.92rem;
}
#wish-puppet.is-stacked .puppet__bubble::after {
  top: auto;
  bottom: -5px;
  right: auto;
  left: 50%;
  margin-top: 0;
  margin-left: -6px;
  box-shadow: -3px 3px 8px rgba(150, 195, 255, 0.2);
}

/* 其它小人（右上角立绘回退版）的气泡也一起淡出，别用硬切 */
.puppet__bubble {
  transition: opacity 0.34s ease;
}
.puppet__bubble.is-out {
  opacity: 0;
}

/* ── 《彼岸潮汐》：BOSS 横幅跟着本关从暗色盘里抽到的配色走 ─────
   JS 每次开 BOSS 时在 .fx-boss 上写入 --boss-rgb / --boss-edge / --boss-ink。
   没写的时候回落到原来的猩红，保证不影响别的玩法。 */
.fx-boss {
  background:
    linear-gradient(
      90deg,
      rgba(var(--boss-rgb, 255, 61, 95), 0) 0%,
      rgba(var(--boss-rgb, 255, 61, 95), 0.5) 42%,
      rgba(var(--boss-rgb, 255, 61, 95), 0.5) 58%,
      rgba(var(--boss-rgb, 255, 61, 95), 0) 100%
    ),
    linear-gradient(
      180deg,
      rgba(var(--boss-rgb, 255, 61, 95), 0) 0%,
      rgba(var(--boss-rgb, 255, 61, 95), 0.16) 50%,
      rgba(var(--boss-rgb, 255, 61, 95), 0) 100%
    );
  box-shadow:
    inset 0 1px 0 rgba(var(--boss-edge, 255, 160, 180), 0.62),
    inset 0 -1px 0 rgba(var(--boss-edge, 255, 160, 180), 0.62),
    0 0 42px rgba(var(--boss-rgb, 255, 61, 95), 0.4);
}
.fx-boss span {
  color: var(--boss-ink, #fff);
  text-shadow:
    0 0 10px rgba(var(--boss-rgb, 255, 61, 95), 1),
    0 0 32px rgba(var(--boss-rgb, 255, 61, 95), 0.8),
    0 0 60px rgba(var(--boss-rgb, 255, 61, 95), 0.45);
}

/* ── 免责声明（#/legal）与杂物间说明 ──────────────────────────
   长文页：行宽压在 38em 以内（约 40 个汉字 / 80 个半角字符），
   段间留白，关键承诺用 --crimson-soft 轻微强调。 */
.legal__head {
  margin-top: 0.4rem;
}
.legal__topline {
  font-family: var(--f-display);
  font-size: 1.22rem;
  line-height: 1.7;
  color: var(--silk);
}
.legal__meta {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: 0.55rem 1.4rem;
  margin: 1.1rem 0 0;
  padding-top: 1rem;
  border-top: 1px solid var(--line);
}
.legal__meta > div {
  display: flex;
  gap: 0.5rem;
  align-items: baseline;
  min-width: 0;
}
.legal__meta dt {
  color: var(--faint);
  font-size: 0.82rem;
  letter-spacing: 0.12em;
  white-space: nowrap;
}
.legal__meta dd {
  margin: 0;
  color: var(--ash);
  font-size: 0.9rem;
  overflow-wrap: anywhere;
}
.legal__keybox {
  margin-top: 1.2rem;
  padding: 0.9rem 1.1rem 1rem;
  border-left: 2px solid var(--crimson-soft);
  background: rgba(140, 196, 255, 0.06);
}
.legal__keybox-title {
  color: var(--crimson-soft);
  font-size: 0.8rem;
  letter-spacing: 0.2em;
}
.legal__keybox ul {
  margin: 0.5rem 0 0;
  padding-left: 1.1rem;
}
.legal__keybox li {
  margin-top: 0.28rem;
  color: var(--ash);
  font-size: 0.9rem;
  line-height: 1.85;
}
.legal__keybox b {
  color: var(--silk);
}

.legal__body {
  margin-top: 1.6rem;
}
.legal__sec {
  max-width: 38em;
}
.legal__sec + .legal__sec {
  margin-top: 2rem;
  padding-top: 1.7rem;
  border-top: 1px solid var(--line);
}
.legal__sec h3 {
  color: var(--silk);
  font-size: 1.06rem;
  letter-spacing: 0.06em;
}
.legal__sub {
  margin: 1.35rem 0 0;
  padding-left: 0.6rem;
  border-left: 2px solid var(--line-strong);
  color: var(--azure);
  font-size: 0.92rem;
  font-weight: 600;
  letter-spacing: 0.1em;
}
.legal__sec p,
.legal__sec li {
  color: var(--ash);
  font-size: 0.95rem;
  line-height: 1.95;
  overflow-wrap: anywhere;
}
.legal__sec p {
  margin-top: 0.85rem;
}
.legal__sec ul {
  margin-top: 0.7rem;
  padding-left: 1.15rem;
}
.legal__sec li {
  margin-top: 0.5rem;
}
.legal__sec b {
  color: var(--silk);
}
.legal__sec code {
  padding: 0.05rem 0.35rem;
  border: 1px solid var(--line);
  background: rgba(140, 196, 255, 0.08);
  color: var(--azure);
  font-size: 0.86em;
  overflow-wrap: anywhere;
}
.legal__sec a {
  color: var(--azure);
  border-bottom: 1px solid var(--line-strong);
}
.legal__sec a:hover {
  color: var(--silk);
  border-bottom-color: var(--crimson-soft);
}
.legal__mail {
  overflow-wrap: anywhere;
}

/* 曲目清单：一行一首，窄屏自动折行 */
.legal__tracks {
  margin-top: 1.3rem;
  padding: 0.9rem 1rem 1rem;
  border: 1px solid var(--line);
  background: rgba(12, 20, 48, 0.35);
}
.legal__tracks-head {
  color: var(--crimson-soft);
  font-size: 0.84rem;
  letter-spacing: 0.14em;
}
.legal__tracklist {
  margin: 0.8rem 0 0;
  padding: 0;
  list-style: none;
}
.legal__track {
  display: flex;
  gap: 0.7rem;
  padding: 0.55rem 0;
  border-top: 1px solid var(--line);
}
.legal__track:first-child {
  border-top: 0;
}
.legal__track-no {
  flex: 0 0 1.6rem;
  color: var(--faint);
  font-size: 0.78rem;
  line-height: 1.7;
  letter-spacing: 0.06em;
}
.legal__track-body {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.14rem;
}
.legal__track-title {
  color: var(--silk);
  font-size: 0.94rem;
  line-height: 1.6;
}
.legal__track-meta {
  color: var(--ash);
  font-size: 0.83rem;
  line-height: 1.7;
  overflow-wrap: anywhere;
}
.legal__track-meta a {
  color: var(--azure);
  border-bottom: 1px solid var(--line-strong);
}
.legal__track-note {
  color: var(--faint);
  font-size: 0.81rem;
  line-height: 1.7;
  overflow-wrap: anywhere;
}
.legal__note {
  margin-top: 0.7rem;
  color: var(--faint);
  font-size: 0.8rem;
  line-height: 1.75;
}

/* 杂物间：道谢页的清单（页面本身已不含任何收款元素） */
.tip__list {
  margin: 0.9rem 0 0;
  padding-left: 1.15rem;
}
.tip__list li {
  margin-top: 0.4rem;
  color: var(--ash);
  line-height: 1.95;
}
.tip__list b,
.tip__body b {
  color: var(--silk);
}

@media (max-width: 720px) {
  .legal__sec {
    max-width: none;
  }
  .legal__topline {
    font-size: 1.1rem;
  }
  .legal__meta {
    grid-template-columns: 1fr;
  }
  .legal__keybox {
    padding: 0.8rem 0.85rem 0.9rem;
  }
  .legal__sec p,
  .legal__sec li {
    font-size: 0.92rem;
    line-height: 1.9;
  }
  .legal__sec + .legal__sec {
    margin-top: 1.6rem;
    padding-top: 1.3rem;
  }
  .legal__tracks {
    padding: 0.8rem 0.7rem 0.9rem;
  }
  .legal__track {
    gap: 0.5rem;
  }
  .legal__track-no {
    flex-basis: 1.3rem;
  }
}

/* ── 任务卡：点一下去对应板块（映射见 js/taskroutes.js） ──── */
a.task {
  color: inherit; /* 别整成链接蓝 */
  text-decoration: none;
  cursor: pointer;
  transition: border-color 0.18s var(--ease), background-color 0.18s var(--ease),
    transform 0.18s var(--ease);
}
a.task:hover,
a.task:focus-visible {
  border-color: var(--line-strong);
  background: rgba(58, 80, 150, 0.5);
  transform: translateY(-2px);
}
a.task:focus-visible {
  outline: 2px solid var(--azure);
  outline-offset: 2px;
}
/* 右侧的「→」：只是提示，不动原来的名字/说明两行 */
a.task .task__go {
  margin-left: auto;
  align-self: center;
  padding-left: 0.2rem;
  color: var(--faint);
  font-size: 0.95rem;
  line-height: 1;
  transition: color 0.18s var(--ease), transform 0.18s var(--ease);
}
a.task:hover .task__go,
a.task:focus-visible .task__go {
  color: var(--azure-soft);
  transform: translateX(3px);
}
/* 已完成的那几张：还是能点，保留「压下去」的观感 */
a.task.is-done:hover,
a.task.is-done:focus-visible {
  opacity: 0.85;
}
@media (prefers-reduced-motion: reduce) {
  a.task:hover,
  a.task:focus-visible,
  a.task:hover .task__go,
  a.task:focus-visible .task__go {
    transform: none;
  }
}

/* ── 免责声明：导航到 9 项后手机上不再横向滑动 ────────────────
   允许换行，保证每个房间都点得到（375px 实测文档 scrollWidth == innerWidth）。 */
@media (max-width: 720px) {
  .nav {
    flex-wrap: wrap;
    overflow-x: visible;
    row-gap: 0.1rem;
  }
  .nav__link {
    flex: 1 1 auto;
    padding: 0.4rem 0.45rem;
    font-size: 0.8rem;
  }
  /* 左下角的希儿立绘会压住页脚第一个链接，给页脚这行让出位置 */
  .foot__tiny {
    padding-left: 5.5rem;
  }
}
/* ── 特殊荣誉头衔：彼岸执灯人（全站唯一）────────────── */
.chip--honor {
  color: #2a1c05 !important;
  background: linear-gradient(135deg, #ffe9a8 0%, #ffcf5c 42%, #ffb03a 70%, #ffe6a0 100%) !important;
  border: 1px solid rgba(255, 226, 150, 0.9) !important;
  font-weight: 700;
  letter-spacing: 0.02em;
  box-shadow:
    0 0 10px rgba(255, 196, 88, 0.55),
    0 0 22px rgba(255, 176, 58, 0.3);
  position: relative;
  overflow: hidden;
}
.chip--honor::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(115deg, transparent 30%, rgba(255, 255, 255, 0.75) 50%, transparent 70%);
  transform: translateX(-120%);
  animation: honorSheen 3.6s ease-in-out infinite;
}
@keyframes honorSheen {
  0%,
  60% {
    transform: translateX(-120%);
  }
  100% {
    transform: translateX(120%);
  }
}
@media (prefers-reduced-motion: reduce) {
  .chip--honor::after {
    animation: none;
  }
}

/* ── 审核台 · 账户面板（#/review 的「账户」标签）────────────────────
   IP、访问明细这些只给站长看的东西都排在这里。表格宽，所以整块交给
   内部滚动容器，页面本身永远不横向溢出。 */
.acct__notice {
  margin-top: 1.4rem;
  padding: 0.5rem 0.75rem;
  border: 1px solid rgba(255, 92, 120, 0.35);
  border-left: 2px solid var(--crimson);
  border-radius: var(--r);
  background: rgba(255, 92, 120, 0.07);
  color: var(--crimson-soft);
  font-size: 0.8rem;
  letter-spacing: 0.04em;
}
.acct__panel {
  margin-top: 0.9rem;
}
.acct__filters {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
}
.acct__field {
  flex: 1 1 9rem;
  min-width: 0;
}
.acct__days {
  flex: 0 0 auto;
}
.acct__reveal {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  white-space: nowrap;
  font-size: 0.82rem;
  color: var(--ash);
  cursor: pointer;
}
.acct__reveal input {
  accent-color: var(--azure);
  width: 1rem;
  height: 1rem;
  cursor: pointer;
}
.acct__meta {
  margin-top: 0.7rem;
  font-size: 0.78rem;
  color: var(--faint);
  word-break: break-word;
}
.acct__scroll {
  margin-top: 0.85rem;
  max-width: 100%;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  border: 1px solid var(--line);
  border-radius: var(--r);
}
.acct__table {
  width: 100%;
  min-width: 880px;
  border-collapse: collapse;
  font-size: 0.82rem;
}
.acct__table th {
  padding: 0.5rem 0.6rem;
  text-align: left;
  white-space: nowrap;
  font-weight: 600;
  font-size: 0.76rem;
  letter-spacing: 0.03em;
  color: var(--ash);
  border-bottom: 1px solid var(--line-strong);
}
.acct__table th.is-sortable {
  cursor: pointer;
  user-select: none;
  color: var(--azure-dim);
}
.acct__table th.is-sortable:hover,
.acct__table th.is-on {
  color: var(--azure);
}
.acct__table td {
  padding: 0.45rem 0.6rem;
  white-space: nowrap;
  border-bottom: 1px solid rgba(140, 196, 255, 0.1);
}
.acct__row {
  cursor: pointer;
}
.acct__row:hover,
.acct__row:focus-visible {
  background: rgba(140, 196, 255, 0.07);
}
.acct__row.is-open {
  background: rgba(140, 196, 255, 0.11);
}
.acct__ip {
  font-family: var(--f-mono);
  font-size: 0.76rem;
  letter-spacing: 0.02em;
}
.acct__drow > td {
  padding: 0;
  white-space: normal;
  background: rgba(9, 14, 34, 0.45);
}
.acct__detail {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 1.1rem;
  padding: 0.9rem 1rem 1.05rem;
}
.acct__detail-col {
  min-width: 0;
}
.acct__detail-title {
  margin-bottom: 0.4rem;
  font-size: 0.76rem;
  letter-spacing: 0.03em;
  color: var(--azure-soft);
}
.acct__iplist li,
.acct__pathlist li {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  padding: 0.2rem 0;
  font-size: 0.78rem;
  border-bottom: 1px dashed rgba(140, 196, 255, 0.12);
}
.acct__iplist b {
  font-family: var(--f-mono);
  font-weight: 600;
  color: var(--silk);
}
.acct__iplist span,
.acct__pathlist span {
  color: var(--faint);
  font-size: 0.74rem;
}
.acct__iplist i {
  margin-left: auto;
  font-style: normal;
  font-size: 0.7rem;
  color: var(--azure-dim);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 0 0.4rem;
}
.acct__pathlist {
  max-height: 11rem;
  overflow-y: auto;
}
.acct__pathlist code {
  font-family: var(--f-mono);
  font-size: 0.76rem;
  color: var(--azure-soft);
  overflow-wrap: anywhere;
}
.acct__pathlist span {
  margin-left: auto;
  white-space: nowrap;
}
.acct__bars {
  display: flex;
  align-items: flex-end;
  gap: 2px;
  height: 46px;
  max-width: 100%;
  overflow-x: auto;
}
.acct__day {
  flex: 1 0 3px;
  min-height: 2px;
  border-radius: 1px 1px 0 0;
  background: rgba(140, 196, 255, 0.16);
}
.acct__day.is-on {
  background: linear-gradient(180deg, var(--azure), var(--azure-dim));
}
.acct__bars-note {
  margin-top: 0.35rem;
  font-size: 0.73rem;
  color: var(--faint);
}
.acct__empty {
  padding: 0.6rem 0;
  color: var(--faint);
  font-size: 0.8rem;
  white-space: normal;
}
.acct__why {
  margin-top: 0.9rem;
  font-size: 0.78rem;
  line-height: 1.65;
  color: var(--faint);
  word-break: break-word;
}

@media (max-width: 720px) {
  .acct__filters {
    align-items: stretch;
  }
  .acct__field,
  .acct__reveal {
    flex: 1 1 100%;
  }
  .acct__detail {
    grid-template-columns: minmax(0, 1fr);
    gap: 0.9rem;
  }
  .acct__table {
    min-width: 760px;
    font-size: 0.78rem;
  }
  /* 展开的明细挂在跨列的 td 里，宽度会被表格撑到 760px 以上；
     窄屏上让它跟着滚动容器（而不是表格）的宽度走，再钉在左边，
     这样不用先把表格横向拖到底才看得见 IP 历史和每日访问。 */
  .acct__scroll {
    container-type: inline-size;
  }
  .acct__drow > td {
    position: sticky;
    left: 0;
  }
  .acct__detail {
    display: block;
    box-sizing: border-box;
    width: 100cqw;
  }
}


/* ══════════════════════════════════════════════════════════════════════
   《彼岸潮汐》手机版收尾（追加段，勿改上文）
   站主实测反馈两件事：
     · 字看不见 —— 舞台把 760×420 的固定世界硬缩进手机宽度（414 屏宽下画布只有
       372px，≈0.49 倍），门上的 ×1.5、BOSS 血量、人数胶囊全部只剩一半大小。
     · 界面被压缩太小 —— app.css 的小屏规则把 .stage 压到 240px，比开始面板还矮。
   JS 那边已经改成「世界宽度跟着舞台宽度走（≤760）+ 后备缓冲按 dpr 放大」，
   这里只补画布的盒子、开始/结算面板的可滚动，以及 HUD 在小屏上的排版。
   ══════════════════════════════════════════════════════════════════════ */

/* 画布自己撑高舞台：画布下面不留空白，也不让 min-height 反压 */
.stage--crowd {
  min-height: 0;
}
.stage--crowd canvas {
  display: block;
  width: 100%;
  height: auto;
  touch-action: none; /* 拖动换道时别把页面一起滚了（画布外照常滚） */
  user-select: none;
  -webkit-user-select: none;
  -webkit-tap-highlight-color: transparent;
}
/* 开始 / 结算面板：内容比画布高时可以在面板内部滚动，不许把「开始」按钮切掉 */
.stage--crowd .stage__overlay {
  overflow-y: auto;
  place-content: safe center;
  padding-block: 0.9rem;
  overscroll-behavior: contain;
}

/* HUD：手机上「看得清」+「一定不横向溢出」（长数字换行也留在盒子里） */
.hud--crowd {
  flex-wrap: wrap;
  row-gap: 0.45rem;
}
.hud--crowd .hud__item {
  min-width: 0;
  max-width: 100%;
  white-space: nowrap;
  font-size: 1rem;
  letter-spacing: 0.04em;
  font-variant-numeric: tabular-nums;
}
.hud--crowd .hud__item b {
  font-size: 1.35rem;
  font-variant-numeric: tabular-nums;
}
.hud--crowd .hud__score b {
  font-size: clamp(1.6rem, 5vmin, 2.3rem);
}
.hud--crowd .hud__spacer {
  margin-left: auto;
}

@media (max-width: 720px) {
  .stage--crowd .stage__overlay {
    padding: 1rem 0.85rem;
    gap: 0.6rem;
  }
  .stage--crowd .stage__title {
    font-size: 1.3rem;
    letter-spacing: 0.14em;
  }
  .stage--crowd .stage__note {
    font-size: 0.8rem;
    line-height: 1.62;
  }
  .hud--crowd {
    gap: 0.42rem 0.8rem;
    padding: 0.62rem 0.72rem;
  }
  .hud--crowd .hud__item {
    font-size: 1.08rem;
  }
  .hud--crowd .hud__item b {
    font-size: 1.42rem;
  }
  .hud--crowd .hud__score {
    padding: 0.24rem 0.7rem 0.3rem;
  }
  .hud--crowd .hud__score b {
    font-size: clamp(1.7rem, 8vmin, 2.1rem);
  }
}

/* 375 这种窄屏：一行放两个数字块，别把「关卡/BOSS」挤出去 */
@media (max-width: 400px) {
  .hud--crowd {
    gap: 0.36rem 0.66rem;
  }
  .hud--crowd .hud__item {
    font-size: 1rem;
  }
  .hud--crowd .hud__item b {
    font-size: 1.3rem;
  }
}

/* 矮宽屏（横屏手机 / 折叠屏展开后的矮视口）：把画布按视口高度收一下，
   免得画布比视口还高、一屏只剩跑道。宽高比跟着 JS 的世界（≤760×420）走。 */
@media (min-width: 600px) and (max-height: 640px) {
  .stage--crowd canvas {
    max-width: calc((100vh - 190px) * 1.9);
    margin-inline: auto;
  }
}

/* ══════════════════════════════════════════════════════════════════════
   整页滚动修复：折叠屏展开（视口变矮变宽）后页面滑不动
   ─────────────────────────────────────────────────────────────────────
   根因：app.css 把 `touch-action: none` 写在了「舞台」(.stage) 上，
   而 touch-action 是「自身 ∩ 所有祖先」求交 —— 于是舞台里的每一个
   像素（画布、开始/结算蒙层、舞台留白）都不能起手滑动。展开后舞台
   占视口 40%~70%，手指十有八九落在舞台里，整页看起来就是被锁死。
   改法：吞手势的范围缩到画布本身；舞台只保留 manipulation
   （可滚动、可捏合、去掉双击缩放延迟）。
   取舍：游戏跑起来后在画布内拖动不会滚动页面（不然画面会跟着页面
   滚走、触摸坐标全错位）；画布以外的任何地方照常滚整页。蒙层可见
   （没开跑 / 已结算）时，蒙层盖住画布，触碰落在蒙层上 → 整页照常滑。
   ══════════════════════════════════════════════════════════════════════ */
.stage {
  touch-action: manipulation;
}
.stage canvas {
  touch-action: none;
}

/* ── 追加二：电脑版（桌面）文字问题 ────────────────────────────────────
   站主实测「电脑版文字显示有问题」，实测下来的根因有两条：

   1) 画布里的字被 2 倍拉伸（发虚）：
      浏览器/显示器 devicePixelRatio = 2 时，700×420 的 CSS 画布占 1400×840 个
      物理像素，而后备缓冲只有 700×420 → 每个画出来的像素被放大 2 倍，
      门上的 ×1.5、BOSS 血量、人数胶囊这些 canvas 文字全是糊的。
      JS 侧已改成 backing = CSS 宽 × dpr（并在 dpr 变化时重建），这里不用管。

   2) 开始/结算面板的文字被裁：
      .stage__note 的 max-width 是 32ch，而 ch 是「0 的宽度」——中文一个字
      接近 2ch，于是每行只排下十几个汉字，250 字说明排成 17 行、高 421px，
      比画布（420px）还高，正好把「开始」按钮顶出舞台。改成按 em 限宽，
      桌面上一行能排 30 个汉字，面板就装得下了。
   ──────────────────────────────────────────────────────────────────── */
.stage--crowd .stage__note {
  max-width: min(100%, 30em);
}
/* HUD 数字变化（滚动 / 万·亿缩写切换）不许把旁边的元素挤走：
   给每个数字块留固定宽度 + 等宽数字 */
.hud--crowd .hud__item b {
  display: inline-block;
  min-width: 5ch;
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.hud--crowd .hud__item.hud__score b {
  min-width: 6ch;
  text-align: center;
}
@media (max-width: 720px) {
  /* 手机上「关卡 / BOSS」是两位数量级，别留太宽 */
  .hud--crowd .hud__item b {
    min-width: 4.4ch;
  }
}

/* ── 追加三：开始/结算面板在桌面上正好高 15px，把「开始」按钮挤出舞台 ──
   实测：30em 的说明列 + 1rem 间距 ×2 + 0.9rem 上下内边距 = 435px > 画布 420px，
   于是按钮被切在舞台外面（站主看到的「电脑版文字显示有问题」就有这一条）。
   把说明列放宽到 34em（中文一行 34 字，少排两行）、间距和内边距各收一点，
   桌面上一屏装得下；手机上仍按整宽排版。 */
.stage--crowd .stage__overlay {
  gap: 0.8rem;
  padding: 0.6rem 1rem;
}
.stage--crowd .stage__note {
  max-width: min(100%, 34em);
}
@media (max-width: 720px) {
  .stage--crowd .stage__overlay {
    gap: 0.55rem;
    padding: 0.7rem 0.85rem;
  }
  .stage--crowd .stage__note {
    max-width: 100%;
    font-size: 0.78rem;
    line-height: 1.58;
  }
}

/* ── 追加四：开始/结算面板做成「说明可滚动、按钮永远在」的竖排 ──────────
   手机上 372×340 的画布装不下「说明 + 开始按钮」（说明 336 字 ≈ 245px），
   差 19px 就把按钮顶出去。改成 flex 竖排：说明自己滚，按钮固定可见，
   以后无论文案多长都不会再出现「点了不存在的开始」。 */
.stage--crowd .stage__overlay {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.75rem;
  padding: 0.7rem 1rem;
  overflow: hidden;
}
.stage--crowd .stage__note {
  flex: 0 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  max-width: min(100%, 34em);
}
.stage--crowd .stage__overlay > div {
  flex: 0 0 auto;
}
@media (max-width: 720px) {
  .stage--crowd .stage__overlay {
    gap: 0.5rem;
    padding: 0.6rem 0.8rem;
  }
  .stage--crowd .stage__note {
    max-width: 100%;
    font-size: 0.75rem;
    line-height: 1.55;
  }
}

/* ── 追加（续）：蒙层别把整页滚动也一起吃掉 ──────────────────────────────
   上面 `.stage--crowd .stage__overlay { overscroll-behavior: contain }`
   是「蒙层自己滚到底了不要带着整页一起滚」。问题是蒙层/说明文字在
   `overflow:hidden` 或内容装得下时**根本滚不动**，`contain` 仍然会
   把这条手势吞掉 —— 实测 1000×600（折叠屏展开）下舞台 460~580 这一段
   起手滑动页面纹丝不动，正是站主说的「滑不动」。
   这里把链条还回去：自己滚不动时交还给整页，自己滚得动时先滚自己。
   取舍：说明文字滚到底之后页面会接着滚（浏览器默认行为），不会锁死。 */
.stage .stage__overlay,
.stage .stage__overlay .stage__note {
  overscroll-behavior-y: auto;
}

/* ── 顶部导航：一行横向滚动 + 细滑块，最后一项不再被挤掉 ── */
.topbar .nav,
#nav {
  display: flex;
  flex-wrap: nowrap;          /* 关键：不再换行，改成一排可滑 */
  align-items: center;
  gap: 0.15rem;
  flex: 1 1 auto;
  min-width: 0;               /* 关键：flex 子项必须能收缩，否则溢出被裁 */
  max-width: 100%;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-behavior: smooth;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain;
  scrollbar-width: thin;      /* Firefox */
  scrollbar-color: rgba(140, 196, 255, 0.55) rgba(255, 255, 255, 0.07);
  padding-bottom: 6px;        /* 给滑块留位置，别压住文字 */
}
#nav::-webkit-scrollbar {
  height: 5px;
}
#nav::-webkit-scrollbar-track {
  background: rgba(255, 255, 255, 0.07);
  border-radius: 999px;
}
#nav::-webkit-scrollbar-thumb {
  background: linear-gradient(90deg, rgba(140, 196, 255, 0.75), rgba(255, 92, 120, 0.6));
  border-radius: 999px;
}
#nav::-webkit-scrollbar-thumb:hover {
  background: linear-gradient(90deg, rgba(140, 196, 255, 1), rgba(255, 92, 120, 0.85));
}
/* 导航项不许被压扁 */
#nav .nav__link {
  flex: 0 0 auto;
  white-space: nowrap;
}
/* 两头留一点余量，滚到底时最后一项不会贴着音乐键 */
#nav::after {
  content: '';
  flex: 0 0 0.35rem;
}
/* 手机端：顶栏本来就矮，滑块更细一点 */
@media (max-width: 720px) {
  #nav {
    padding-bottom: 5px;
  }
  #nav::-webkit-scrollbar {
    height: 4px;
  }
}

/* ══════════════════════════════════════════════════════════════════
   私信（#/dm）与后台「私信」标签：微信式气泡
   只追加、不改上面任何一条规则；app.css 里没有任何 .dmx 选择器。
   两条硬约束写在这里：
   · 手机上「列表 ↔ 对话」两屏切换，靠 .dmx__app[data-view] 控制，不做横向位移；
   · 长文本永不出框：grid 子项 min-width:0 + 气泡 overflow-wrap:anywhere。
   ══════════════════════════════════════════════════════════════════ */
.dmx {
  min-width: 0;
}

.dmx__app {
  display: grid;
  grid-template-columns: minmax(220px, 300px) minmax(0, 1fr);
  height: clamp(420px, 68svh, 720px);
  min-width: 0;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: rgba(12, 20, 48, 0.55);
  box-shadow: 0 18px 60px rgba(4, 8, 24, 0.45);
}

/* ── 左：会话列表 ───────────────────────────────────── */
.dmx__list {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  border-right: 1px solid var(--line);
  background: rgba(10, 18, 44, 0.5);
}
.dmx__list-head {
  padding: 0.75rem 0.75rem 0.6rem;
  border-bottom: 1px solid var(--line);
}
.dmx__list-title {
  font-family: var(--f-display);
  font-size: 0.95rem;
  letter-spacing: 0.08em;
  color: var(--azure-soft);
  margin-bottom: 0.45rem;
}
.dmx__find {
  width: 100%;
  min-width: 0;
  font-size: 0.85rem;
}
.dmx__threads {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  overscroll-behavior: contain;
}
.dmx__thread {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  width: 100%;
  padding: 0.6rem 0.7rem;
  border: 0;
  border-bottom: 1px solid rgba(140, 196, 255, 0.1);
  background: transparent;
  text-align: left;
  cursor: pointer;
  color: inherit;
}
.dmx__thread:hover {
  background: rgba(140, 196, 255, 0.08);
}
.dmx__thread.is-active {
  background: linear-gradient(90deg, rgba(140, 196, 255, 0.2), rgba(140, 196, 255, 0.04));
  box-shadow: inset 3px 0 0 var(--azure);
}
.dmx__thread-body {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
}
.dmx__thread-top {
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
  min-width: 0;
}
.dmx__thread-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 0.9rem;
  color: var(--silk);
}
.dmx__thread-time {
  flex: none;
  font-size: 0.68rem;
  color: var(--faint);
}
.dmx__thread-last {
  font-size: 0.76rem;
  color: var(--faint);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.dmx__unread {
  flex: none;
  min-width: 19px;
  height: 19px;
  padding: 0 0.3rem;
  border-radius: 999px;
  background: var(--crimson);
  color: #1a0b12;
  font-size: 0.66rem;
  font-weight: 700;
  line-height: 19px;
  text-align: center;
}
.dmx__found {
  padding: 0.4rem 0.5rem 0.6rem;
  border-top: 1px solid var(--line);
}
.dmx__pick {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  width: 100%;
  min-width: 0;
  padding: 0.35rem 0.45rem;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--ash);
  font-size: 0.82rem;
  text-align: left;
  cursor: pointer;
}
.dmx__pick:hover {
  background: rgba(140, 196, 255, 0.12);
  color: var(--silk);
}

/* ── 右：对话 ───────────────────────────────────────── */
.dmx__chat {
  position: relative;
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
}
.dmx__chat-head {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.6rem 0.8rem;
  border-bottom: 1px solid var(--line);
  background: rgba(20, 32, 70, 0.5);
}
.dmx__back {
  display: none;
  flex: none;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: transparent;
  color: var(--azure-soft);
  font-size: 0.78rem;
  padding: 0.15rem 0.6rem;
  cursor: pointer;
}
.dmx__chat-peer {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 0.92rem;
  color: var(--silk);
}
.dmx__stream {
  flex: 1 1 auto;
  min-height: 0;
  min-width: 0;
  overflow-y: auto;
  overflow-x: hidden;
  overscroll-behavior: contain;
  padding: 0.8rem 0.9rem 0.4rem;
}
.dmx__empty {
  color: var(--faint);
  font-size: 0.85rem;
  text-align: center;
  padding: 1.6rem 0.8rem;
}

/* 日期分隔条 */
.dmx__day {
  display: flex;
  justify-content: center;
  margin: 0.7rem 0 0.5rem;
}
.dmx__day span {
  padding: 0.05rem 0.6rem;
  border-radius: 999px;
  background: rgba(140, 196, 255, 0.12);
  color: var(--faint);
  font-size: 0.7rem;
}

/* 气泡：自己靠右（青蓝系）、对方靠左（深色卡片 + 猩红点缀） */
.dmx__row {
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
  min-width: 0;
  margin: 0.5rem 0;
}
.dmx__row--mine {
  flex-direction: row-reverse;
}
.dmx__face {
  flex: none;
}
.dmx__avatar {
  width: 32px;
  height: 32px;
}
.dmx__col {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  min-width: 0;
  max-width: 70%;
}
.dmx__row--mine .dmx__col {
  align-items: flex-end;
}
.dmx__name {
  max-width: 100%;
  margin: 0 0.15rem 0.15rem;
  font-size: 0.7rem;
  color: var(--faint);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.dmx__bubble {
  max-width: 100%;
  padding: 0.5rem 0.75rem;
  border-radius: 14px;
  font-size: 0.94rem;
  line-height: 1.6;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  word-break: break-word;
}
.dmx__row--peer .dmx__bubble {
  background: linear-gradient(180deg, rgba(34, 48, 96, 0.95), rgba(24, 34, 72, 0.95));
  border: 1px solid var(--line);
  border-left: 2px solid var(--crimson-dim);
  border-top-left-radius: 4px;
  color: var(--silk);
}
.dmx__row--mine .dmx__bubble {
  background: linear-gradient(135deg, #2f4d92, #4a7fc4);
  border: 1px solid rgba(140, 196, 255, 0.5);
  border-top-right-radius: 4px;
  color: #f4f8ff;
}
.dmx__stamp {
  margin: 0.15rem 0.2rem 0;
  font-size: 0.66rem;
  color: var(--faint);
}
.dmx__row.is-pending .dmx__bubble {
  opacity: 0.62;
}
.dmx__row.is-failed .dmx__bubble {
  border-color: var(--crimson);
  opacity: 0.8;
}

/* 「有新消息 ↓」：只在用户往上翻了历史时才出现 */
.dmx__jump {
  position: absolute;
  right: 0.9rem;
  bottom: 6.4rem;
  z-index: 2;
  padding: 0.25rem 0.7rem;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  background: rgba(18, 28, 68, 0.95);
  color: var(--azure-soft);
  font-size: 0.76rem;
  cursor: pointer;
  box-shadow: 0 8px 24px rgba(4, 8, 24, 0.5);
}

/* 固定在底部的输入栏 */
.dmx__composer {
  display: flex;
  align-items: flex-end;
  gap: 0.5rem;
  padding: 0.55rem 0.7rem;
  border-top: 1px solid var(--line);
  background: rgba(16, 26, 60, 0.92);
}
.dmx__area {
  flex: 1 1 auto;
  min-width: 0;
  max-height: 140px;
  overflow-y: auto;
  resize: none;
  font-size: 0.9rem;
  line-height: 1.55;
}
.dmx__composer-side {
  flex: none;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.3rem;
}
.dmx__count {
  font-family: var(--f-mono);
  font-size: 0.68rem;
  color: var(--faint);
}
.dmx__count.is-max {
  color: var(--crimson-soft);
}
.dmx__send {
  white-space: nowrap;
}
.dmx__send[disabled] {
  opacity: 0.45;
  cursor: not-allowed;
}
.dmx__hint {
  padding: 0 0.8rem 0.6rem;
  font-size: 0.7rem;
  line-height: 1.5;
  color: var(--faint);
}

/* ── 后台「私信」标签（只读）───────────────────────── */
.dmx__pairs {
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.dmx__pair {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  width: 100%;
  min-width: 0;
  padding: 0.6rem 0.7rem;
  border: 1px solid transparent;
  border-bottom-color: rgba(140, 196, 255, 0.12);
  background: transparent;
  color: inherit;
  text-align: left;
  cursor: pointer;
}
.dmx__pair:hover {
  border-color: var(--line);
  background: rgba(140, 196, 255, 0.07);
}
.dmx__pair-who {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  flex-wrap: wrap;
  min-width: 0;
  font-size: 0.9rem;
  color: var(--silk);
}
.dmx__pair-x {
  color: var(--crimson-soft);
}
.dmx__pair-meta {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  min-width: 0;
  font-size: 0.74rem;
  color: var(--faint);
}
.dmx__pair-stat {
  font-family: var(--f-mono);
  font-size: 0.7rem;
}
.dmx__pair-last {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.dmx--readonly {
  min-width: 0;
  margin-top: 0.6rem;
}
.dmx__ro-banner {
  padding: 0.45rem 0.6rem;
  border: 1px solid rgba(255, 92, 120, 0.35);
  border-radius: 6px;
  background: rgba(255, 92, 120, 0.08);
  color: var(--crimson-soft);
  font-size: 0.76rem;
  line-height: 1.6;
}
.dmx__stream--flat {
  max-height: min(58svh, 560px);
  margin-top: 0.5rem;
  padding: 0.6rem;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: rgba(10, 18, 44, 0.5);
}
.acct__why--tight {
  margin: 0 0 0.6rem;
}

/* ── 手机：列表 ↔ 对话 两屏切换，不做横向滚动 ──────── */
@media (max-width: 720px) {
  .dmx__app {
    grid-template-columns: minmax(0, 1fr);
    height: min(72svh, 600px);
  }
  .dmx__list {
    border-right: 0;
  }
  .dmx__app[data-view='list'] .dmx__chat {
    display: none;
  }
  .dmx__app[data-view='chat'] .dmx__list {
    display: none;
  }
  .dmx__back {
    display: inline-block;
  }
  .dmx__col {
    max-width: 76%;
  }
  .dmx__jump {
    bottom: 7.6rem;
  }
  .dmx__pair-last {
    white-space: normal;
  }
}

/* ── 顶栏通知按钮：重画（原来吃的是浏览器默认按钮外观：浅灰底 + outset 方框 + 白图标看不见）── */
.topbar .alerts {
  position: relative;
  display: flex;
  align-items: center;
}
.topbar .alerts__btn {
  -webkit-appearance: none;
  appearance: none;
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  margin: 0;
  padding: 0;
  border: 1px solid var(--line-strong);
  border-radius: 50%;
  background: linear-gradient(180deg, rgba(56, 78, 152, 0.55), rgba(18, 28, 62, 0.62));
  color: var(--azure-soft);
  cursor: pointer;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.05);
  transition: color 0.2s var(--ease), border-color 0.2s var(--ease), box-shadow 0.25s var(--ease),
    transform 0.12s var(--ease);
}
.topbar .alerts__btn:hover {
  color: #fff;
  border-color: var(--azure);
  box-shadow: 0 0 18px rgba(140, 196, 255, 0.35), inset 0 0 0 1px rgba(255, 255, 255, 0.08);
}
.topbar .alerts__btn:active {
  transform: scale(0.94);
}
.topbar .alerts__btn[aria-expanded='true'] {
  color: var(--crimson-soft);
  border-color: var(--crimson);
  box-shadow: 0 0 18px rgba(255, 92, 120, 0.35);
}
.topbar .alerts__btn:focus-visible {
  outline: 2px solid var(--azure);
  outline-offset: 2px;
}
.alerts__icon {
  display: block;
  width: 19px;
  height: 19px;
}
/* 未读红点 */
.topbar .alerts__badge {
  position: absolute;
  top: -5px;
  right: -5px;
  min-width: 18px;
  height: 18px;
  padding: 0 4px;
  display: grid;
  place-items: center;
  border-radius: 999px;
  border: 1.5px solid rgba(11, 18, 38, 0.9);
  background: linear-gradient(180deg, #ff6b86, #b5304c);
  color: #fff;
  font-size: 11px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 0;
  box-shadow: 0 0 12px rgba(255, 92, 120, 0.55);
}
.topbar .alerts__badge[hidden] {
  display: none;
}
@media (max-width: 720px) {
  .topbar .alerts__btn {
    width: 32px;
    height: 32px;
  }
  .alerts__icon {
    width: 17px;
    height: 17px;
  }
}

/* ══════════════════════════════════════════════════════════
   通知面板（.alerts__*）——原来一行 CSS 都没有：
   面板是 position:static 被挤成 75px 宽（文字挤压），
   列表项是 <button> 吃浏览器默认外观（浅灰白底 + 立体框）。
   ══════════════════════════════════════════════════════════ */
.topbar {
  overflow: visible; /* 面板要能溢出顶栏显示，不能被裁 */
}
.topbar .alerts {
  position: relative;
}
.topbar .alerts__panel {
  position: absolute;
  top: calc(100% + 10px);
  right: 0;
  z-index: 60;
  width: min(340px, calc(100vw - 24px));
  max-height: min(58vh, 460px);
  padding: 0.5rem;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  border: 1px solid var(--line-strong);
  border-radius: 14px;
  background: linear-gradient(180deg, rgba(24, 34, 74, 0.97), rgba(12, 19, 44, 0.98));
  box-shadow: 0 18px 46px rgba(4, 8, 22, 0.6), 0 0 0 1px rgba(140, 196, 255, 0.06) inset;
  backdrop-filter: blur(10px);
  overflow-y: auto;
  overscroll-behavior: contain;
  text-align: left;
}
.topbar .alerts__panel[hidden] {
  display: none;
}
.topbar .alerts__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
  margin: 0 0 0.15rem;
  padding: 0.25rem 0.4rem 0.55rem;
  border-bottom: 1px solid rgba(140, 196, 255, 0.16);
  color: var(--azure-soft);
  font-size: 0.86rem;
  letter-spacing: 0.06em;
}
.topbar .alerts__list {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}
/* 列表项：清掉按钮默认外观 + 竖排「私信/@ 标签 + 文本 + 时间」 */
.topbar .alerts__item {
  -webkit-appearance: none;
  appearance: none;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-areas:
    'tag text'
    'at  at';
  align-items: start;
  gap: 0.25rem 0.5rem;
  width: 100%;
  margin: 0;
  padding: 0.5rem 0.55rem;
  border: 1px solid transparent;
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.03);
  color: var(--silk);
  font: inherit;
  font-size: 0.85rem;
  line-height: 1.5;
  text-align: left;
  cursor: pointer;
  transition: background 0.18s var(--ease), border-color 0.18s var(--ease), transform 0.12s var(--ease);
}
.topbar .alerts__item:hover {
  background: rgba(140, 196, 255, 0.1);
  border-color: rgba(140, 196, 255, 0.28);
}
.topbar .alerts__item:active {
  transform: scale(0.99);
}
.topbar .alerts__item:focus-visible {
  outline: 2px solid var(--azure);
  outline-offset: 1px;
}
.topbar .alerts__item.is-unread {
  background: rgba(255, 92, 120, 0.09);
  border-color: rgba(255, 92, 120, 0.28);
}
.topbar .alerts__tag {
  grid-area: tag;
  flex: 0 0 auto;
  padding: 0.06rem 0.4rem;
  border-radius: 999px;
  font-size: 0.72rem;
  line-height: 1.6;
  white-space: nowrap;
  color: #0d1530;
  background: var(--azure);
}
.topbar .alerts__tag--mention,
.topbar .alerts__tag--dm {
  background: var(--crimson-soft);
  color: #3a0b16;
}
.topbar .alerts__text {
  grid-area: text;
  min-width: 0;
  overflow-wrap: anywhere;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.topbar .alerts__at {
  grid-area: at;
  justify-self: end;
  color: var(--faint, rgba(238, 243, 255, 0.5));
  font-size: 0.74rem;
  letter-spacing: 0.02em;
}
/* 头像安全网：任何地方的头像都不许变形 */
.avatar {
  flex: 0 0 auto;
  aspect-ratio: 1 / 1;
  overflow: hidden;
}
.avatar > img,
.avatar > svg {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: inherit;
}
.topbar .alerts__panel img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
@media (max-width: 720px) {
  .topbar .alerts__panel {
    right: -4px;
    width: min(320px, calc(100vw - 16px));
  }
  .topbar .alerts__item {
    font-size: 0.83rem;
    padding: 0.46rem 0.5rem;
  }
}

/* ── 祝福榜：主页祝福区里送出祝福的人 + 每人收到的赞 ──
   只往 extra.css 末尾追加，不动 app.css。昵称列用 minmax(0,1fr) + 省略号，
   手机 375px 下整行不会把面板撑宽（外层 .wish 本身 overflow:hidden）。 */
.wishwall {
  margin-top: 0.9rem;
  padding-top: 0.7rem;
  border-top: 1px solid rgba(168, 179, 216, 0.16);
}
.wishwall[hidden] {
  display: none;
}
.wishwall__head {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  flex-wrap: wrap;
  margin-bottom: 0.45rem;
}
.wishwall__name {
  font-size: 0.9rem;
  letter-spacing: 0.06em;
  color: #eef3ff;
}
.wishwall__tip {
  font-size: 0.72rem;
  color: rgba(198, 211, 240, 0.6);
}
.wishwall__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.wishwall__row {
  display: grid;
  grid-template-columns: 1.5rem minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 0.5rem;
  min-width: 0;
  padding: 0.28rem 0.4rem;
  border-radius: 9px;
  font-size: 0.82rem;
  color: rgba(230, 238, 255, 0.9);
  background: rgba(140, 196, 255, 0.05);
}
.wishwall__row.is-me {
  background: rgba(255, 212, 121, 0.09);
  box-shadow: inset 0 0 0 1px rgba(255, 212, 121, 0.22);
}
.wishwall__rank {
  text-align: right;
  font-size: 0.72rem;
  color: rgba(198, 211, 240, 0.55);
  font-variant-numeric: tabular-nums;
}
.wishwall__list > li:first-child .wishwall__rank {
  color: #ffd479;
}
.wishwall__who {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  min-width: 0;
}
.wishwall__nick {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.wishwall__you {
  flex: 0 0 auto;
  font-style: normal;
  font-size: 0.62rem;
  line-height: 1;
  padding: 0.16rem 0.3rem;
  border-radius: 5px;
  background: rgba(255, 212, 121, 0.2);
  color: #ffd479;
}
.wishwall__count {
  white-space: nowrap;
  font-size: 0.72rem;
  color: rgba(198, 211, 240, 0.62);
  font-variant-numeric: tabular-nums;
}
.wishwall__like {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.22rem;
  min-width: 3.2rem;
  padding: 0.16rem 0.5rem;
  border: 1px solid rgba(255, 143, 166, 0.3);
  border-radius: 999px;
  background: rgba(255, 143, 166, 0.08);
  color: rgba(255, 196, 208, 0.92);
  font: inherit;
  font-size: 0.74rem;
  line-height: 1.4;
  cursor: pointer;
  transition: background 0.18s, color 0.18s, border-color 0.18s;
}
.wishwall__like:hover:not(:disabled) {
  background: rgba(255, 143, 166, 0.18);
}
.wishwall__like.is-on {
  background: rgba(255, 92, 120, 0.3);
  border-color: rgba(255, 92, 120, 0.62);
  color: #fff;
}
.wishwall__like:disabled {
  cursor: not-allowed;
  opacity: 0.42;
}
.wishwall__like b {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.wishwall__empty {
  margin: 0.2rem 0 0;
  font-size: 0.72rem;
  color: rgba(198, 211, 240, 0.5);
}
.wishwall__empty[hidden] {
  display: none;
}
@media (max-width: 720px) {
  .wishwall__row {
    grid-template-columns: 1.15rem minmax(0, 1fr) auto auto;
    gap: 0.35rem;
    padding: 0.26rem 0.3rem;
    font-size: 0.78rem;
  }
  .wishwall__tip {
    display: none;
  }
  .wishwall__count {
    font-size: 0.68rem;
  }
  .wishwall__like {
    min-width: 2.8rem;
    padding: 0.16rem 0.42rem;
  }
}

/* ══════════════════════════════════════════════════════════
   Tab 激活态（.channel）——原来激活/未激活都是深蓝，
   只差边框透明度与文字亮度，肉眼几乎看不出切换。
   现在改成强对比 + 过渡，切哪一块一目了然。
   ══════════════════════════════════════════════════════════ */
.channels {
  gap: 0.5rem;
}
.channel {
  position: relative;
  transition:
    background 0.28s var(--ease),
    color 0.28s var(--ease),
    border-color 0.28s var(--ease),
    box-shadow 0.32s var(--ease),
    transform 0.18s var(--ease);
}
/* 未激活：悬停有反馈 */
.channel:not(.is-active):not([aria-selected='true']):hover {
  background: rgba(56, 78, 152, 0.42);
  border-color: rgba(140, 196, 255, 0.5);
  color: var(--silk);
  transform: translateY(-1px);
}
/* 激活：亮底深字 + 青色描边 + 外发光 + 轻微上浮（和未激活形成强烈对比） */
.channel.is-active,
.channel[aria-selected='true'] {
  background: linear-gradient(135deg, #c2dcff 0%, #8cc4ff 45%, #5f95e6 100%) !important;
  color: #0b1226 !important;
  border-color: rgba(214, 234, 255, 0.95) !important;
  font-weight: 600;
  box-shadow:
    0 0 0 1px rgba(194, 220, 255, 0.35),
    0 6px 20px rgba(140, 196, 255, 0.35);
  transform: translateY(-1px);
}
/* 激活时左侧一道猩红竖条，和站点双色呼应 */
.channel.is-active::before,
.channel[aria-selected='true']::before {
  content: '';
  position: absolute;
  left: 6px;
  top: 50%;
  width: 3px;
  height: 55%;
  border-radius: 999px;
  transform: translateY(-50%);
  background: linear-gradient(180deg, var(--crimson), var(--crimson-dim));
}
/* 站长入口的标签更宽，内边距给足，别让竖条压到字 */
.channels--wide .channel.is-active::before,
.channels--wide .channel[aria-selected='true']::before {
  left: 7px;
}
.channels--wide .channel {
  padding-left: 1.15rem;
}
@media (prefers-reduced-motion: reduce) {
  .channel,
  .channel.is-active {
    transition: none;
    transform: none;
  }
}

/* .wish__panel 是「正文 1fr + 小人 auto」两列网格；正文那几块靠
   `.wish__panel > .wish__row { grid-column: 1 }` 之类的规则钉在左列。
   祝福榜是后加的那一块，不写这一条就会自动落到右列（手机上只有 clamp(130px…185px)，
   昵称会被挤成一个字）。让它横跨两列、排在小人下面，任何宽度都不挤压。 */
.wish__panel > .wishwall {
  grid-column: 1 / -1;
}

/* ── 头像裁剪器（public/js/cropper.js）────────────────────────────
   圆形取景框 = 头像最终露出来的部分（圆形由 .avatar 的 CSS 裁出来）。
   取景框里铺棋盘格：图片永远盖满它，真露出格子就说明有缝隙。
   ─────────────────────────────────────────────────────────────── */
.crop {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
  overflow-y: auto;
  background: rgba(6, 10, 26, 0.78);
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
}

.crop__panel {
  display: grid;
  gap: 0.85rem;
  width: min(420px, 100%);
  margin: auto;
  padding: 1.1rem 1.1rem 1.15rem;
  border: 1px solid var(--line-strong);
  background: rgba(24, 34, 74, 0.97);
}

.crop__title {
  font-family: var(--f-display);
  font-size: 1.25rem;
  letter-spacing: 0.04em;
  color: var(--silk);
}

.crop__hint {
  margin-top: 0.3rem;
  font-size: 0.79rem;
  line-height: 1.7;
  color: var(--faint);
}

.crop__stage {
  position: relative;
  width: min(320px, 100%, 58vh);
  aspect-ratio: 1 / 1;
  margin: 0 auto;
  overflow: hidden;
  border: 1px solid var(--line);
  background-color: #0d1533;
  background-image: repeating-conic-gradient(
    rgba(140, 196, 255, 0.1) 0% 25%,
    rgba(140, 196, 255, 0.02) 0% 50%
  );
  background-size: 20px 20px;
  cursor: grab;
  touch-action: none;
}

.crop__stage:active {
  cursor: grabbing;
}

.crop__stage canvas {
  display: block;
  width: 100%;
  height: 100%;
}

/* 圆圈以外压暗：一眼看出头像只取圆里那一块 */
.crop__ring {
  position: absolute;
  inset: 0;
  pointer-events: none;
  border-radius: 50%;
  box-shadow:
    0 0 0 9999px rgba(6, 10, 26, 0.66),
    inset 0 0 0 1px rgba(255, 255, 255, 0.34);
}

.crop__tools {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.55rem;
}

.crop__zoom {
  display: flex;
  flex: 1 1 140px;
  gap: 0.45rem;
  align-items: center;
  min-width: 0;
  font-size: 0.79rem;
  color: var(--ash);
}

.crop__zoom input[type='range'] {
  flex: 1 1 auto;
  min-width: 0;
  accent-color: var(--azure);
}

.crop__acts {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  justify-content: flex-end;
}

.crop__acts .btn {
  flex: 0 1 auto;
}

.crop__note {
  font-family: var(--f-mono);
  font-size: 0.74rem;
  text-align: right;
  color: var(--faint);
}

/* 「减弱动态」下裁剪界面不加任何过渡（与 tokens.css 的口径一致） */
.crop--static *,
.crop--static *::before,
.crop--static *::after {
  transition: none !important;
  animation: none !important;
}

/* ── 晒图举报弹窗（public/js/views/wall.js）──────────────────── */
.report {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
  overflow-y: auto;
  background: rgba(6, 10, 26, 0.74);
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
}

.report__panel {
  display: grid;
  gap: 0.7rem;
  width: min(420px, 100%);
  margin: auto;
  padding: 1.1rem;
  border: 1px solid var(--line-strong);
  background: rgba(24, 34, 74, 0.97);
}

.report__title {
  font-family: var(--f-display);
  font-size: 1.2rem;
  color: var(--silk);
}

.report__hint {
  font-size: 0.79rem;
  line-height: 1.7;
  color: var(--faint);
}

.report__reasons {
  display: grid;
  gap: 0.4rem;
}

.report__opt {
  display: flex;
  gap: 0.5rem;
  align-items: flex-start;
  padding: 0.5rem 0.6rem;
  border: 1px solid var(--line);
  font-size: 0.87rem;
  line-height: 1.6;
  color: var(--ash);
  cursor: pointer;
}

.report__opt:hover {
  border-color: var(--line-strong);
}

.report__opt:has(input:checked) {
  border-color: rgba(255, 92, 120, 0.5);
  color: var(--silk);
  background: rgba(255, 92, 120, 0.08);
}

.report__opt input {
  flex: none;
  margin-top: 0.3rem;
  accent-color: var(--crimson);
}

.report__acts {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  justify-content: flex-end;
}

/* 图上不压按钮：举报入口放在图下的次要位置，小字、克制 */
.wall__foot {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  align-items: center;
  min-height: 1.1rem;
  margin-top: 0.4rem;
}

.wall__report {
  padding: 0;
  border: 0;
  background: none;
  font-size: 0.74rem;
  color: var(--faint);
  text-decoration: underline;
  text-decoration-color: rgba(140, 196, 255, 0.32);
  text-underline-offset: 3px;
  cursor: pointer;
}

.wall__report:hover {
  color: var(--crimson-soft);
}

.wall__reported {
  font-size: 0.74rem;
  color: var(--faint);
  opacity: 0.85;
}

/* ── 审核台：晒图的事后管理（举报数 / 举报人 / 下架态）──────── */
.review__item .review__who {
  flex-wrap: wrap;
  gap: 0.45rem;
}

.review__item.is-off .review__thumb img {
  filter: grayscale(0.7) brightness(0.72);
}

.review__tag--bad {
  border-color: rgba(255, 92, 120, 0.5);
  color: var(--crimson-soft);
}

.review__tag--warn {
  border-color: rgba(255, 212, 121, 0.5);
  color: #ffd479;
}

.review__reports {
  margin-top: 0.5rem;
  font-size: 0.8rem;
  line-height: 1.8;
  color: var(--ash);
  overflow-wrap: anywhere;
}

.review__reports b {
  color: var(--crimson-soft);
}

.review__reporter {
  color: var(--faint);
}

.review__noreport {
  color: var(--faint);
}

@media (max-width: 420px) {
  .crop,
  .report {
    align-items: flex-start;
    padding: 0.75rem;
  }

  .crop__acts .btn,
  .report__acts .btn {
    flex: 1 1 auto;
  }
}

/* ── 无脚本回退（<noscript> 里那段 SEO 正文）──────────────────────
   只在浏览器关掉 JavaScript 时出现。开着 JS 时 main.js 的 mount()
   会把 #view 的子节点整个换掉，这一块根本不参与排版。 */
.page.seo {
  max-width: 62ch;
  margin: 3rem auto 4rem;
  color: var(--silk);
  line-height: 1.9;
}

.page.seo h1 {
  font-family: var(--f-display);
  font-size: clamp(1.35rem, 4.2vw, 2rem);
  line-height: 1.4;
  margin-bottom: 1rem;
}

.page.seo h2 {
  font-family: var(--f-display);
  font-size: 1.05rem;
  margin: 1.8rem 0 0.6rem;
}

.page.seo ul {
  padding-left: 1.2rem;
  margin: 0.6rem 0;
}

.page.seo li {
  margin: 0.35rem 0;
}

.page.seo p,
.page.seo li {
  overflow-wrap: anywhere;
}

/* ══════════════════════════════════════════════════════════════════════
   唱片机 v2（追加段，勿改上文）：收起态 = 一颗圆形黑胶，可以按住自由拖动
   —— 位置由 player.js 用 left/top 摆好（默认贴右上角 + 安全边距 + 顶栏下方），
      这里只管长相：唱片 / 进度环 / 播放呼吸光 / 右下角展开小箭头。
      收起态点唱片 = 播放暂停，点右下角小箭头 = 展开完整面板。
   ══════════════════════════════════════════════════════════════════════ */

/* 外壳：本身不占地方也不吃指针，位置由 JS 给 */
.tt {
  position: fixed;
  left: 0;
  top: 0;
  right: auto;
  bottom: auto;
  display: block;
  align-items: stretch;
  gap: 0;
  width: auto;
  max-width: none;
  z-index: 36;
  pointer-events: none;
  color: var(--ash);
}

/* ── 收起态：一颗唱片 + 右下角展开小箭头 ───────────────────── */
.tt__dock {
  position: relative;
  display: block;
  width: 56px;
  height: 56px;
  pointer-events: auto;
  touch-action: none; /* 拖唱片时别跟着滚页面 */
  user-select: none;
  -webkit-user-select: none;
  cursor: grab;
  transition: transform 0.16s var(--ease), filter 0.16s var(--ease);
}
.tt.is-dragging .tt__dock {
  cursor: grabbing;
  transform: scale(1.1);
  filter: drop-shadow(0 10px 18px rgba(4, 8, 24, 0.55));
}
.tt.is-open .tt__dock {
  display: none;
}

.tt__disc {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  width: 100%;
  height: 100%;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: none;
  cursor: inherit;
  -webkit-tap-highlight-color: transparent;
}
.tt__disc:focus-visible {
  outline: 2px solid var(--azure);
  outline-offset: 3px;
}

/* 黑胶本体：纹路 + 中心标签（转起来时标签跟着转） */
.tt__vinyl {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  background:
    repeating-radial-gradient(circle at 50% 50%, rgba(255, 255, 255, 0.05) 0 1px, transparent 1px 3px),
    radial-gradient(circle at 50% 50%, #2a3364 0 30%, #171d43 58%, #0a0e26 100%);
  box-shadow: inset 0 0 0 1px rgba(140, 196, 255, 0.3), 0 10px 26px rgba(4, 8, 24, 0.5);
}
.tt__vinyl i {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: radial-gradient(circle at 38% 32%, var(--crimson-soft), var(--crimson-dim));
  box-shadow: 0 0 0 2px rgba(10, 14, 32, 0.9), 0 0 12px rgba(255, 92, 120, 0.4);
  color: rgba(255, 255, 255, 0.88);
  font-size: 0.62rem;
  font-style: normal;
  line-height: 22px;
  text-align: center;
  transition: filter 0.22s var(--ease);
}
/* 暂停时中心标签暗一档：一眼看出「停了」 */
.tt:not(.is-playing) .tt__vinyl i {
  filter: saturate(0.62) brightness(0.86);
}

/* 进度环：conic-gradient 一圈，--tt-p（0~1）由 paintProgress 写 */
.tt__ring {
  position: absolute;
  inset: -3px;
  border-radius: 50%;
  pointer-events: none;
  background: conic-gradient(
    var(--azure) calc(var(--tt-p, 0) * 360deg),
    rgba(140, 196, 255, 0.16) 0
  );
  -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 3px), #000 calc(100% - 2px));
  mask: radial-gradient(farthest-side, transparent calc(100% - 3px), #000 calc(100% - 2px));
}

/* 播放中：唱片转起来 + 外面一圈呼吸光 */
.tt.is-playing .tt__vinyl {
  animation: ttSpin 4.4s linear infinite;
}
.tt.is-playing .tt__dock::after {
  content: '';
  position: absolute;
  inset: -6px;
  border-radius: 50%;
  pointer-events: none;
  box-shadow: 0 0 0 1px rgba(140, 196, 255, 0.55);
  animation: ttBreath 2.6s ease-in-out infinite;
}
@keyframes ttBreath {
  0%,
  100% {
    transform: scale(1);
    opacity: 0.55;
  }
  50% {
    transform: scale(1.09);
    opacity: 0.12;
  }
}

/* 右下角的小箭头：展开完整歌单（面板头里那个 × 负责收起） */
.tt__fold {
  position: absolute;
  right: 0;
  bottom: 0;
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  padding: 0;
  border: 1px solid var(--line-strong);
  border-radius: 50%;
  background: rgba(12, 18, 44, 0.94);
  color: var(--silk);
  font-size: 0.78rem;
  line-height: 1;
  cursor: pointer;
  transform: none;
  box-shadow: 0 4px 12px rgba(4, 8, 24, 0.55);
}
.tt__fold:hover {
  color: var(--silk);
  border-color: var(--azure);
}
.tt__fold span {
  display: block;
  transform: translateY(-1px);
}
/* 面板头右上角的收起按钮 */
.tt__close {
  margin-left: auto;
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: rgba(12, 18, 44, 0.5);
  color: var(--faint);
  font-size: 0.9rem;
  line-height: 1;
  cursor: pointer;
}
.tt__close:hover {
  color: var(--silk);
  border-color: var(--line-strong);
}
.tt__close span {
  display: block;
  transform: translateY(-1px);
}

/* ── 展开态：完整面板（位置由 JS 夹在视口里）────────────────── */
.tt__panel {
  width: min(336px, calc(100vw - 1.2rem));
  pointer-events: auto;
}
@media (max-width: 720px) {
  .tt__panel {
    width: min(308px, calc(100vw - 1rem));
  }
}
@media (max-width: 420px) {
  .tt__panel {
    width: calc(100vw - 1rem);
  }
  .tt__dock {
    width: 52px;
    height: 52px;
  }
}
/* 播放器的 <audio> 是挂在 DOM 里的（方便观察 / 排查），别让它占位 */
.tt__audio {
  display: none;
}

/* ══════════════════════════════════════════════════════════════════════
   开场幕布：液态玻璃（毛玻璃 + 流动高光 + 竖缝分块 + 涟漪）
   ──────────────────────────────────────────────────────────────────────
   毛玻璃的 backdrop-filter 只认「视口」这层背景，所以 .curtain 与 .curtain__glass
   上都不能挂 transform / opacity / filter —— 挂上就成 backdrop root，玻璃背后只剩
   幕布自己。位移、淡出全部交给里面的子层做。
   ══════════════════════════════════════════════════════════════════════ */
.curtain {
  background: transparent;
  transition: none;
  backdrop-filter: none;
  --glass-blur: 18px;
  --glass-sat: 1.7;
  --glass-fade: 0.62s;
}
.curtain::before {
  transition: opacity 0.45s var(--ease) 0.12s;
}
.curtain__inner {
  position: relative;
  z-index: 2;
  transition: opacity 0.26s var(--ease), transform 0.26s var(--ease);
}

.curtain__glass {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}
.curtain__tint {
  position: absolute;
  inset: 0;
  background: radial-gradient(120% 90% at 70% 0%, rgba(34, 49, 110, 0.7), rgba(13, 21, 51, 0.84));
  transition: opacity 0.5s var(--ease) 0.28s;
}
/* 真正的那层玻璃：模糊 + 提饱和，底下的门厅糊成一片色块 */
.curtain__frost {
  position: absolute;
  inset: 0;
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat));
  backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat));
  background:
    radial-gradient(64% 46% at 20% 6%, rgba(255, 255, 255, 0.13), transparent 72%),
    linear-gradient(
    112deg,
    rgba(238, 243, 255, 0.12) 0%,
    rgba(238, 243, 255, 0.02) 30%,
    rgba(140, 196, 255, 0.07) 60%,
    rgba(194, 220, 255, 0.11) 100%
  );
  box-shadow:
    inset 0 0 120px rgba(140, 196, 255, 0.12),
    inset 0 1px 0 rgba(238, 243, 255, 0.16);
  transition:
    opacity var(--glass-fade) var(--ease) 0.16s,
    backdrop-filter var(--glass-fade) var(--ease) 0.16s;
}
/* 竖缝：把整块玻璃读成一块块拼起来的，打开时各自错开淡出 */
.curtain__seam {
  position: absolute;
  top: -2%;
  bottom: -2%;
  left: calc((var(--i) + 1) * 100% / (var(--n) + 1));
  width: 1.5px;
  transform: translateX(-50%);
  background: linear-gradient(
    180deg,
    transparent,
    rgba(238, 243, 255, 0.4) 18%,
    rgba(194, 220, 255, 0.62) 50%,
    rgba(238, 243, 255, 0.34) 82%,
    transparent
  );
  box-shadow: 0 0 16px rgba(140, 196, 255, 0.45);
  opacity: 0.55;
  animation: seamWobble 11s ease-in-out infinite;
  animation-delay: calc(var(--i) * -1.7s);
  transition:
    opacity 0.46s var(--ease) calc(var(--i) * 0.05s + 0.05s),
    transform 0.46s var(--ease) calc(var(--i) * 0.05s + 0.05s);
}
@keyframes seamWobble {
  0%,
  100% {
    transform: translateX(-50%) scaleY(1);
  }
  50% {
    transform: translateX(calc(-50% + 3px)) scaleY(1.015);
  }
}
/* 玻璃外沿：一圈内高光，读作折射边 */
.curtain__edge {
  position: absolute;
  inset: 0;
  box-shadow:
    inset 0 0 0 1px rgba(238, 243, 255, 0.14),
    inset 0 0 60px rgba(140, 196, 255, 0.1);
  transition: opacity 0.5s var(--ease) 0.1s;
}
/* 缓慢横移的镜面反光 */
.curtain__sheen {
  position: absolute;
  top: -25%;
  left: -45%;
  width: 52%;
  height: 150%;
  transform: rotate(13deg);
  background: linear-gradient(
    90deg,
    transparent,
    rgba(238, 243, 255, 0.2) 40%,
    rgba(255, 255, 255, 0.42) 50%,
    rgba(194, 220, 255, 0.18) 60%,
    transparent
  );
  mix-blend-mode: screen;
  filter: blur(5px);
  opacity: 0.75;
  animation: glassSheen 9.5s ease-in-out infinite;
}
@keyframes glassSheen {
  0%,
  100% {
    transform: translate3d(0, 0, 0) rotate(13deg);
  }
  50% {
    transform: translate3d(150%, 0, 0) rotate(13deg);
  }
}
.curtain__ripple {
  position: absolute;
  inset: 0;
  margin: auto;
  width: 22vmin;
  height: 22vmin;
  border-radius: 50%;
  border: 1px solid rgba(194, 220, 255, 0.4);
  box-shadow: inset 0 0 40px rgba(140, 196, 255, 0.25);
  opacity: 0;
}

/* ── 打开：分块淡出 + 模糊收敛 + 高光扫过，全程约 0.85s ── */
.curtain.is-open {
  transform: none;
  opacity: 1;
  pointer-events: none;
  background: transparent;
}
.curtain.is-open::before {
  opacity: 0;
}
.curtain.is-open .curtain__inner {
  opacity: 0;
  transform: translateY(-10px);
}
.curtain.is-open .curtain__tint {
  opacity: 0;
}
.curtain.is-open .curtain__frost {
  opacity: 0;
  -webkit-backdrop-filter: blur(0px) saturate(1);
  backdrop-filter: blur(0px) saturate(1);
}
.curtain.is-open .curtain__edge {
  opacity: 0;
}
.curtain.is-open .curtain__seam {
  opacity: 0;
  animation: none;
  transform: translateX(calc(-50% + var(--dx, 0px))) scaleY(1.03);
}
.curtain.is-open .curtain__sheen {
  animation: glassSweep 0.85s var(--ease) forwards;
}
.curtain.is-open .curtain__ripple {
  animation: glassRipple 0.9s var(--ease) forwards;
}
@keyframes glassSweep {
  0% {
    transform: translate3d(-16%, 0, 0) rotate(13deg);
    opacity: 0.35;
  }
  52% {
    opacity: 0.85;
  }
  100% {
    transform: translate3d(240%, 0, 0) rotate(13deg);
    opacity: 0;
  }
}
@keyframes glassRipple {
  0% {
    opacity: 0.5;
    transform: scale(0.55);
  }
  100% {
    opacity: 0;
    transform: scale(7);
  }
}
/* 过渡跑完就整块收掉，别让 backdrop-filter 常驻占合成层 */
.curtain.is-gone {
  display: none;
}

/* ── 手机：玻璃薄一点（blur 半径减半 = 少一半采样），缝也更少 ── */
@media (max-width: 720px) {
  .curtain {
    --glass-blur: 9px;
    --glass-sat: 1.45;
  }
  .curtain__sheen {
    filter: blur(4px);
    opacity: 0.45;
  }
}

/* ── 低配设备（核数或内存 ≤ 4）：不用 backdrop-filter，只留半透明玻璃与高光 ── */
.curtain[data-glass-tier='lean'] .curtain__frost,
.curtain[data-glass-tier='lean'].is-open .curtain__frost {
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}
.curtain[data-glass-tier='lean'] .curtain__sheen {
  filter: none;
}

/* ── 减弱动态：不形变、不扫光，直接淡出（tokens.css 会把过渡压成 0.001ms，
       这里给一个看得见的短淡出）── */
@media (prefers-reduced-motion: reduce) {
  .curtain {
    transition: opacity 0.24s linear !important;
  }
  .curtain.is-open {
    opacity: 0;
    transform: none;
  }
  .curtain.is-open .curtain__sheen,
  .curtain.is-open .curtain__ripple {
    animation: none !important;
  }
  .curtain.is-open .curtain__seam {
    transform: translateX(-50%);
  }
  .curtain__frost,
  .curtain__tint,
  .curtain__edge,
  .curtain__seam,
  .curtain__inner,
  .curtain.is-open::before {
    transition: opacity 0.24s linear !important;
  }
}

/* ══════════════════════════════════════════════════════════════════════
   背景：左右两侧的量子之海
   ──────────────────────────────────────────────────────────────────────
   固定在视口两侧的竖条，里面三层 SVG 波浪缓慢错位横移 + 气泡上浮 + 细光斑。
   层级 -1：在 #vignette（z-index 0）与正文之下，暗角照样压得住；pointer-events: none。
   ══════════════════════════════════════════════════════════════════════ */
#waves {
  position: fixed;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
  --wave-w: min(9vw, 130px);
}
.waves__defs {
  position: absolute;
  width: 0;
  height: 0;
}
.waves__side {
  position: absolute;
  top: 0;
  bottom: 0;
  width: var(--wave-w);
  overflow: hidden;
  opacity: 0.88;
  -webkit-mask-image: linear-gradient(to right, #000 0 42%, transparent 100%);
  mask-image: linear-gradient(to right, #000 0 42%, transparent 100%);
}
.waves__side--l {
  left: 0;
}
.waves__side--r {
  right: 0;
  transform: scaleX(-1);
}
.waves__layer {
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 118%;
  will-change: transform;
}
.waves__layer--1 {
  opacity: 0.5;
  animation: waveDriftA 14s ease-in-out infinite;
}
.waves__layer--2 {
  opacity: 0.34;
  animation: waveDriftB 19s ease-in-out infinite;
}
.waves__layer--3 {
  opacity: 0.26;
  animation: waveDriftC 26s ease-in-out infinite;
}
/* 只用 transform：在合成层上跑，不碰布局也不重绘 */
@keyframes waveDriftA {
  0%,
  100% {
    transform: translate3d(-1.5%, 0.6%, 0);
  }
  50% {
    transform: translate3d(4.5%, -1.6%, 0);
  }
}
@keyframes waveDriftB {
  0%,
  100% {
    transform: translate3d(3%, -1%, 0);
  }
  50% {
    transform: translate3d(-4%, 1.4%, 0);
  }
}
@keyframes waveDriftC {
  0%,
  100% {
    transform: translate3d(0, 1.2%, 0);
  }
  50% {
    transform: translate3d(5.5%, -0.8%, 0);
  }
}
.waves__bubbles {
  position: absolute;
  inset: 0;
}
.waves__bubble {
  position: absolute;
  left: var(--x, 50%);
  bottom: -6%;
  width: var(--s, 4px);
  height: var(--s, 4px);
  border-radius: 50%;
  background: radial-gradient(
    circle at 34% 30%,
    rgba(238, 243, 255, 0.7),
    rgba(140, 196, 255, 0.22) 62%,
    transparent 74%
  );
  opacity: 0;
  animation: bubbleRise var(--dur, 18s) linear var(--delay, 0s) infinite;
}
@keyframes bubbleRise {
  0% {
    transform: translate3d(0, 0, 0) scale(0.7);
    opacity: 0;
  }
  14% {
    opacity: 0.5;
  }
  72% {
    opacity: 0.34;
  }
  100% {
    transform: translate3d(7px, -104vh, 0) scale(1.05);
    opacity: 0;
  }
}
.waves__glints {
  position: absolute;
  inset: 0;
}
.waves__glint {
  position: absolute;
  width: 130%;
  aspect-ratio: 1;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(194, 220, 255, 0.16), transparent 62%);
  animation: glintPulse 11s ease-in-out infinite;
}
.waves__glint--1 {
  top: 8%;
  left: -34%;
}
.waves__glint--2 {
  bottom: 6%;
  left: -12%;
  animation-duration: 15s;
  animation-delay: -4.5s;
}
@keyframes glintPulse {
  0%,
  100% {
    opacity: 0.34;
    transform: scale(0.9);
  }
  50% {
    opacity: 0.68;
    transform: scale(1.06);
  }
}

/* 手机：浪带收窄、透明度降一档（条带固定在视口上又有 overflow: hidden，撑不出横向滚动条） */
@media (max-width: 720px) {
  #waves {
    --wave-w: min(13vw, 58px);
  }
  .waves__side {
    opacity: 0.62;
  }
}
@media (max-width: 420px) {
  #waves {
    --wave-w: min(15vw, 52px);
  }
  .waves__side {
    opacity: 0.5;
  }
}
/* 宽屏才敢把浪带铺宽：≤1220 时 .wrap 两侧只剩 20px，浪带宽了会压到正文上 */
@media (min-width: 1220px) {
  #waves {
    --wave-w: min(9vw, 132px);
  }
}
/* 减弱动态：波浪停住，留静态的浪形与光斑 */
@media (prefers-reduced-motion: reduce) {
  .waves__layer,
  .waves__bubble,
  .waves__glint {
    animation: none;
  }
  .waves__bubble {
    opacity: 0.28;
  }
}

/* ══════════════════════════════════════════════════════════
   祝福区：助力徽章 + 送礼按钮 + 礼物特效（第 12 项）
   只碰祝福区自己的东西：徽章、礼物按钮、飞行特效。
   ══════════════════════════════════════════════════════════ */
.wish__sub--gift {
  margin-top: 0.3rem;
  font-size: 0.84rem;
  color: var(--faint);
}
.wish__sub--gift b {
  color: var(--azure-soft);
  font-weight: 600;
}

/* 统计那一行改成可换行的徽章组：窄屏不会横着挤出去 */
.wish__stats {
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 0.7rem 1.2rem;
}

/* 助力累计：徽章 + 发光数字 + 一道缓慢扫过的光 + 今日送礼进度线 */
.wish__badge {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 0.2rem 1.15rem;
  padding: 0.6rem 1.05rem 0.75rem;
  border: 1px solid var(--line-strong);
  border-radius: var(--r);
  background:
    radial-gradient(120% 150% at 6% 0%, rgba(140, 196, 255, 0.18), transparent 62%),
    radial-gradient(110% 140% at 98% 104%, rgba(255, 92, 120, 0.13), transparent 64%),
    linear-gradient(152deg, rgba(31, 45, 98, 0.92), rgba(15, 24, 58, 0.94));
  box-shadow:
    0 0 0 1px rgba(140, 196, 255, 0.05),
    0 12px 34px -20px rgba(0, 0, 0, 0.95);
  overflow: hidden;
}
.wish__badge-cell {
  display: inline-flex;
  align-items: baseline;
  gap: 0.32rem;
  font-size: 0.78rem;
  color: var(--faint);
}
.wish__badge-cell i {
  font-style: normal;
}
.wish__badge-cell b {
  font-family: var(--f-display);
  font-size: 1.7rem;
  line-height: 1;
  color: var(--azure-soft);
  text-shadow: 0 0 16px rgba(140, 196, 255, 0.55);
  font-variant-numeric: tabular-nums;
}
.wish__badge-cell--gift b {
  color: var(--crimson-soft);
  text-shadow: 0 0 18px rgba(255, 92, 120, 0.6);
}
.wish__badge-sheen {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(
    104deg,
    transparent 36%,
    rgba(206, 228, 255, 0.15) 50%,
    transparent 64%
  );
  animation: badgeSheen 8s var(--ease) infinite;
}
@keyframes badgeSheen {
  0%,
  58% {
    transform: translateX(-72%);
    opacity: 0;
  }
  68% {
    opacity: 1;
  }
  100% {
    transform: translateX(72%);
    opacity: 0;
  }
}
.wish__meter {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 2px;
  background: rgba(140, 196, 255, 0.14);
}
.wish__meter::after {
  content: '';
  display: block;
  height: 100%;
  width: calc(var(--p, 0) * 100%);
  background: linear-gradient(90deg, var(--azure-dim), var(--crimson-soft));
  transition: width 0.5s var(--ease);
}

/* 送礼按钮：和「送上祝福」并排，余额跟在后面 */
.wish__btn--gift {
  border-color: var(--azure-dim);
  background: linear-gradient(135deg, rgba(140, 196, 255, 0.22), rgba(255, 92, 120, 0.16));
}
.wish__btn--gift:hover {
  box-shadow: 0 0 28px rgba(140, 196, 255, 0.34);
}
.wish__btn--gift.is-empty {
  opacity: 0.7;
}
.wish__btn-glyph--gift {
  display: inline-flex;
  animation: none;
}
.wish__gift-left {
  padding-left: 0.6rem;
  border-left: 1px solid var(--line);
  font-size: 0.76rem;
  color: var(--ash);
}
.wish__gift-left b {
  font-family: var(--f-display);
  font-size: 1rem;
  color: var(--crimson-soft);
}
/* 窄屏时祝福区是两栏网格，立绘会压到按钮右半边：把礼物按钮抬到立绘之上 */
.wish__btn--gift {
  position: relative;
  z-index: 2;
}

/* 自己画的礼物图标：绀蓝盒身 + 猩红蝴蝶结 */
.gift-icon {
  width: 1.22em;
  height: 1.22em;
  display: block;
}
.gift-icon__box {
  fill: #3a56a8;
}
.gift-icon__lid {
  fill: #5d7cd2;
}
.gift-icon__ribbon {
  fill: var(--azure-soft);
}
.gift-icon__bow {
  fill: var(--crimson);
}

/* 送礼特效：飞过去 → 落地一圈光晕 → 「+1」飘起来 */
.gift__flyer {
  position: absolute;
  left: 0;
  top: 0;
  width: 30px;
  height: 30px;
  margin: -15px 0 0 -15px;
  pointer-events: none;
  filter: drop-shadow(0 0 12px rgba(160, 208, 255, 0.75));
  animation: giftFly 1.15s cubic-bezier(0.32, 0.86, 0.2, 1) forwards;
}
.gift__flyer .gift-icon {
  width: 100%;
  height: 100%;
}
@keyframes giftFly {
  0% {
    transform: translate(0, 0) scale(0.5) rotate(-16deg);
    opacity: 0;
  }
  18% {
    opacity: 1;
  }
  62% {
    transform: translate(calc(var(--gx) * 0.55), calc(var(--gy) * 0.72 - 26px)) scale(1.16)
      rotate(12deg);
  }
  100% {
    transform: translate(var(--gx), var(--gy)) scale(0.68) rotate(-6deg);
    opacity: 0;
  }
}
/* 光晕：亮一圈边 + 一点内发光。太淡的话在白希儿的立绘上等于没画 */
.gift__halo {
  position: absolute;
  width: 126px;
  height: 126px;
  margin: -63px 0 0 -63px;
  border-radius: 50%;
  border: 2px solid rgba(216, 236, 255, 0.9);
  background: radial-gradient(
    circle,
    rgba(255, 255, 255, 0.3),
    rgba(140, 196, 255, 0.24) 50%,
    transparent 72%
  );
  box-shadow:
    0 0 24px rgba(150, 205, 255, 0.5),
    inset 0 0 16px rgba(200, 228, 255, 0.38);
  pointer-events: none;
  animation: giftHalo 0.95s ease-out 0.42s both;
}
@keyframes giftHalo {
  0% {
    transform: scale(0.34);
    opacity: 0;
  }
  35% {
    opacity: 1;
  }
  100% {
    transform: scale(1.12);
    opacity: 0;
  }
}
/* 「+1」飘字：底下垫一枚小药丸，压在她头发上也看得清 */
.gift__plus {
  position: absolute;
  padding: 0.02rem 0.5rem;
  border: 1px solid rgba(255, 157, 174, 0.7);
  border-radius: 999px;
  background: rgba(11, 17, 40, 0.82);
  box-shadow: 0 0 16px rgba(255, 92, 120, 0.35);
  font-family: var(--f-display);
  font-size: 1.15rem;
  line-height: 1.7;
  color: var(--crimson-soft);
  text-shadow: 0 0 12px rgba(255, 92, 120, 0.8);
  pointer-events: none;
  animation: giftPlus 1.25s ease-out 0.42s both;
}
@keyframes giftPlus {
  0% {
    transform: translate(-50%, 0) scale(0.7);
    opacity: 0;
  }
  25% {
    opacity: 1;
  }
  100% {
    transform: translate(-50%, -64px) scale(1);
    opacity: 0;
  }
}

/* ══════════════════════════════════════════════════════════
   画室灯箱：主体是赞美语，来源缩成角落小字（第 10 项）
   :has 只在画室开灯箱时生效，别页面的图注不受影响
   ══════════════════════════════════════════════════════════ */
.lightbox:has(.lightbox__src) .lightbox__cap {
  max-width: min(680px, 88vw);
  margin-inline: auto;
  font-size: 1.02rem;
  line-height: 1.95;
  letter-spacing: 0.02em;
  color: var(--silk);
}
.lightbox__src {
  display: block;
  margin-top: 0.4rem;
  font-size: 0.74rem;
  color: var(--faint);
  letter-spacing: 0;
}

/* ══════════════════════════════════════════════════════════
   发言区富文本（第 13 项）：工具栏 + 渲染端字体字号
   ══════════════════════════════════════════════════════════ */
.rt-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem 0.5rem;
  margin: 0.25rem 0 0.6rem;
  padding: 0.4rem 0.5rem;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: rgba(16, 25, 60, 0.62);
}
.rt-bar__group {
  display: inline-flex;
  gap: 0.25rem;
}
.rt-bar__group + .rt-bar__group {
  padding-left: 0.5rem;
  border-left: 1px solid var(--line);
}
.rt-bar__btn {
  min-width: 2.1rem;
  min-height: 2.1rem;
  padding: 0 0.5rem;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: rgba(140, 196, 255, 0.06);
  color: var(--ash);
  font-size: 0.82rem;
  line-height: 1;
  cursor: pointer;
  white-space: nowrap;
}
.rt-bar__btn:hover {
  border-color: var(--line-strong);
  color: var(--silk);
}
.rt-bar__btn--italic {
  font-style: italic;
}
.rt-bar__btn--underline {
  text-decoration: underline;
}
.rt-bar__btn--strike {
  text-decoration: line-through;
}
.rt-bar__tip {
  margin-left: auto;
  font-size: 0.72rem;
  color: var(--faint);
  white-space: nowrap;
}

.rt-f-sans {
  font-family: var(--f-body);
}
.rt-f-serif {
  font-family: var(--f-display);
}
.rt-f-kai {
  font-family: 'Kaiti SC', 'KaiTi', 'STKaiti', 'Noto Serif SC', var(--f-display);
}
.rt-f-mono {
  font-family: var(--f-mono);
}
.rt-s-sm {
  font-size: 0.86em;
}
.rt-s-md {
  font-size: 1em;
}
.rt-s-lg {
  font-size: 1.34em;
  line-height: 1.45;
}

.field--daily {
  min-height: 88px;
}

/* 留言正文：再长的串也不许把卡片顶出屏幕 */
.msg__text {
  overflow-wrap: anywhere;
}

/* 频道页签：三个频道各占一格，手机上竖着排 */
.channels--board {
  grid-template-columns: repeat(auto-fit, minmax(165px, 1fr));
}

@media (max-width: 560px) {
  .channels--board {
    grid-template-columns: 1fr;
  }
  .rt-bar__tip {
    display: none;
  }
  /* 两栏网格下按钮只剩一百多像素：让「送礼 · 余 N」自己折一行，别把字挤成竖排 */
  .wish__btn--gift {
    flex-wrap: wrap;
    justify-content: center;
  }
  .wish__btn--gift .wish__btn-text {
    white-space: nowrap;
  }
}

@media (max-width: 420px) {
  .wish__badge {
    gap: 0.15rem 0.9rem;
    padding: 0.5rem 0.8rem 0.68rem;
  }
  .wish__badge-cell b {
    font-size: 1.42rem;
  }
  .wish__badge-cell {
    font-size: 0.72rem;
  }
  .rt-bar {
    gap: 0.3rem 0.4rem;
    padding: 0.35rem 0.4rem;
  }
  .rt-bar__btn {
    min-width: 1.95rem;
    min-height: 1.95rem;
    padding: 0 0.42rem;
  }
}


/* ══════════════════════════════════════════════════════════════════════════
   追加：手机适配总攻（游戏舞台铺满 · 全屏 / 横屏 · 触控目标 · 逐页挤压）
   ──────────────────────────────────────────────────────────────────────────
   这一整段只做「布局与可点性」，不动任何玩法判定、也不碰 app.css。
   ══════════════════════════════════════════════════════════════════════════ */

/* ── 1. 游戏舞台：画布铺满整个游戏区 ─────────────────────────────────────
   病根：app.css 给 .stage 写了 min-height（380px，小屏 240px），
   而《拾光》《希儿快跑》的画布是「宽 100% + 高 auto」——
   画布只占盒子上面一段，下面全是空盒子（站主说的「拾光只有上面部分」）。
   min-height 归零后，盒子高度完全由画布决定：canvas 与 .stage 同宽同高。 */
.stage {
  min-height: 0;
}
.stage canvas {
  display: block;
  width: 100%;
  height: auto;
  max-width: 100%;
}

/* 全屏（含 iOS Safari 的伪全屏固定层）：画布由 JS 按世界比例摆好并居中，
   四周留白就是舞台底色；画布盒永远等于画出来的那块像素，触摸坐标才不会错。 */
.stage:fullscreen,
.stage.is-fakefs {
  position: fixed;
  inset: 0;
  z-index: 9998;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100vw;
  height: 100vh;
  max-width: none;
  max-height: none;
  margin: 0;
  border: 0;
  background: #070c22;
}
body.has-fakefs {
  overflow: hidden;
}

/* 舞台右上角：全屏 / 横屏 / 当前画质 */
.stage__tools {
  position: absolute;
  top: 0.4rem;
  right: 0.4rem;
  z-index: 4;
  display: flex;
  align-items: center;
  gap: 0.35rem;
  pointer-events: none; /* 只有按钮本身吃点击，别挡住画面拖动 */
}
.stage__tool {
  pointer-events: auto;
  display: inline-flex;
  align-items: center;
  gap: 0.28rem;
  min-height: 34px;
  padding: 0 0.62rem;
  border: 1px solid rgba(150, 195, 255, 0.42);
  border-radius: 999px;
  background: rgba(8, 14, 36, 0.74);
  color: #dbe9ff;
  font-size: 0.78rem;
  line-height: 1;
  letter-spacing: 0.04em;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.stage__tool span {
  font-size: 0.92rem;
}
.stage__tool:hover {
  border-color: rgba(190, 220, 255, 0.8);
  color: #fff;
}
.stage__tool--land {
  border-color: rgba(255, 215, 110, 0.5);
  color: #ffe9b0;
}
.stage__qlabel {
  pointer-events: none;
  border-color: transparent;
  background: rgba(8, 14, 36, 0.5);
  color: #9fc6f5;
  font-size: 0.68rem;
  letter-spacing: 0.02em;
}
.stage__tip {
  position: absolute;
  left: 0.5rem;
  right: 0.5rem;
  bottom: 0.5rem;
  z-index: 4;
  margin: 0;
  padding: 0.42rem 0.6rem;
  border: 1px solid rgba(255, 215, 110, 0.35);
  border-radius: 8px;
  background: rgba(8, 14, 36, 0.88);
  color: #ffd98a;
  font-size: 0.78rem;
  line-height: 1.5;
  text-align: center;
  pointer-events: none;
}

/* ── 2. 触控目标 ≥44px（只放大可点范围/小按钮，不动排版骨架）──────────── */
@media (pointer: coarse), (max-width: 720px) {
  /* 顶栏导航：一行文字 37px 高太难点，加到 44（顶栏本身 62px，塞得下） */
  .nav__link {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 44px;
  }
  .brand {
    align-items: center;
    min-height: 44px;
  }
  .music,
  .alerts__btn {
    min-width: 44px;
    min-height: 44px;
  }
  /* 通用按钮：小号按钮也不再是 33px 的细条 */
  .btn {
    min-height: 44px;
  }
  .btn--sm {
    min-height: 40px;
  }
  /* 舞台上的全屏/横屏按钮：手机上再大一点，拇指好按 */
  .stage__tool {
    min-height: 44px;
    padding: 0 0.8rem;
    font-size: 0.84rem;
  }
  /* 留言板配色小方块：22px → 30px，热区再撑到 44（与 7px 间距刚好相接） */
  .swatch {
    position: relative;
    width: 30px;
    height: 30px;
  }
  .swatch::after {
    content: '';
    position: absolute;
    inset: -7px;
  }
  /* 行内小链接（片源、外链）补热区：视觉不变，手指够得着 */
  .cinema__meta a,
  .story a,
  .legal a,
  .law a,
  .board__hint a {
    position: relative;
  }
  .cinema__meta a::after,
  .story a::after,
  .legal a::after,
  .law a::after,
  .board__hint a::after {
    content: '';
    position: absolute;
    inset: -12px -4px;
  }
  /* 唱片机：展开箭头 26px、上一首/下一首 30px 太小 */
  .tt__fold {
    width: 34px;
    height: 34px;
    right: -4px;
    bottom: -4px;
  }
  .tt__close {
    width: 34px;
    height: 34px;
  }
  .tt__btn {
    min-width: 44px;
    min-height: 44px;
  }
  .tt__seek,
  .tt__vol input {
    height: 28px;
  }
  .tt__label {
    font-size: 0.5rem;
    letter-spacing: 0;
  }
}

/* ── 3. 门厅影像室：片名被 flex 挤成省略号（「动画短片《希儿》」只剩 6 个字）──
   .cinema__now 是 flex:1 的窄格，标题 overflow:hidden + nowrap。
   窄屏把控制条改成两行：片名独占一行，上一部/下一部并排在下。 */
@media (max-width: 560px) {
  .cinema__controls {
    flex-wrap: wrap;
    row-gap: 0.6rem;
  }
  .cinema__now {
    order: -1;
    flex: 1 0 100%;
  }
  .cinema__now b {
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
    text-align: center;
    line-height: 1.45;
  }
}

/* ── 4. 个人中心 / 私信：窄屏上长昵称、长按钮别再互相挤 ────────────────── */
@media (max-width: 560px) {
  .profile__row,
  .me__row {
    flex-wrap: wrap;
    row-gap: 0.5rem;
  }
}

/* ── 5. 追加二：把上面那轮实测查出来的漏网小目标 / 挤压补齐 ─────────────── */
@media (pointer: coarse), (max-width: 720px) {
  /* 小号按钮（上一题 / 换一批 / 把面板再打开）也统一到 44 */
  .btn--sm {
    min-height: 44px;
  }
  /* 留言板富文本工具条：字体/字号/加粗那排 42×31 → 44×44（手机上最好点错的就是这排） */
  .rt-bar__btn {
    min-width: 2.75rem;
    min-height: 2.75rem;
  }
  /* 歌单面板右上角的收起按钮 */
  .tt__close {
    width: 40px;
    height: 40px;
  }
  /* 阅览室「出处」里的站外链接：行高放松一点，链接热区也补上 */
  .review__src {
    line-height: 2.6;
  }
  .review__src a {
    position: relative;
  }
  .review__src a::after {
    content: '';
    position: absolute;
    inset: -8px -6px;
  }
}

/* 门厅影像室：meta 行里的「在 bilibili 打开」被压掉 4px 文字 → 不许再压 */
.cinema__meta a {
  flex: 0 0 auto;
  white-space: nowrap;
}

/* 门厅飘字（不同语言的生日快乐）：它是 z-index:-1 的装饰层，
   窄屏上观感就是「压在台词上」，降到 30% 透明度并缩一号，退成背景纹理。 */
@media (max-width: 560px) {
  .drift {
    opacity: 0.3;
  }
  .drift__item {
    font-size: 0.78rem;
  }
}

/* ── 6. 追加三：游乐场页签别把四字游戏名拆成两行 ─────────────────────────
   375 上 .tabs 是 flex:1 均分，每个 83px，「彼岸潮汐 / 记忆翻牌」被折成
   「彼岸潮 / 汐」这种断词。改成不拆字 + 放不下就横向滑。 */
@media (max-width: 560px) {
  #game-tabs {
    flex-wrap: nowrap;
    overflow-x: auto;
    scrollbar-width: none;
  }
  #game-tabs::-webkit-scrollbar {
    display: none;
  }
  #game-tabs .tab {
    flex: 0 0 auto;
    white-space: nowrap;
    padding: 0.72rem 0.6rem;
    font-size: 0.84rem;
    letter-spacing: 0.06em;
  }
}

/* ── 追加：小人暂停态（游戏运行中整块隐藏，渲染循环同时停帧，见 companion.js / spineSeele.js）── */
.seele-buddy.is-suspended {
  display: none !important;
}

/* ═══════════════════════════════════════════════════════════════════
   追加段（本轮）：通知面板手机防超出 + 游戏开始面板按钮挪位
   只追加在文件末尾，不改动上方任何旧规则。
   ═══════════════════════════════════════════════════════════════════ */

/* ── 需求1 · 通知面板手机防超出 ─────────────────────────────────────────
   根因：面板是 position:absolute + right:0，锚在顶栏里那个信封按钮
   （.alerts，自身只有 32px 宽）上；手机上按钮右侧还排着 .who 用户块，
   340px 宽的面板一律往**左**伸出去，左缘直接冲出视口（375px 实测 −60px 左右）。
   顶栏原本 width:min(340px, calc(100vw-24px)) 治的是「比视口宽」，
   治不了「锚点本身离视口右边太远」。
   修法：≤720px 时改成 position:fixed，left/right 各 12px 直接对视口定宽。
   顶栏有 backdrop-filter，按规范会成为 fixed 后代的包含块——所以
   top:calc(100% + 10px) 仍以顶栏底边为基准，跟原来 absolute 时一致；
   就算个别浏览器不认这条包含块，fixed 退化为对视口定位，面板会落在
   屏幕底部而不是超出（依旧不横向溢出，可接受）。 */
@media (max-width: 720px) {
  .topbar .alerts__panel {
    position: fixed;
    top: calc(100% + 10px);
    left: 12px;
    right: 12px;
    width: auto;
    max-width: none;
  }
}

/* ── 需求2 · 开始面板：全屏/横屏排在主按钮两侧（DOM 由 games.js 挪动）────
   .stage__actions 是开始/结算面板底部那行按钮（games.js 加的 class）。
   面板出现时「⟳ 横屏」「⛶ 全屏」被挪到主按钮左右，这里只负责把行排整齐：
   窄屏上收小间距、按钮不出行；画质标签（.stage__qlabel）留在右上角
   HUD 角落——它是 8px 高的小字，面板内容居中，占不到它那块。 */
.stage__actions .stage__tool {
  flex: 0 0 auto;
}
@media (max-width: 720px) {
  .stage__actions {
    gap: 0.45rem;
    width: 100%;
  }
  .stage__actions .btn--fill {
    flex: 1 1 auto;
    min-width: 0;
  }
  .stage__actions .stage__tool {
    padding: 0 0.62rem;
    font-size: 0.78rem;
  }
}

/

/* ══════════════════════════════════════════════════════════════════════
   需求 · 《生还者》刷怪生存（public/js/games/survivor.js，顶视角 Brotato 手感）
   ──────────────────────────────────────────────────────────────────────
   1) 竞技场 = 画布盒子本身。JS 把后备缓冲按盒子尺寸 × dpr 放大，坐标系就是盒子的
      CSS 像素，所以这里只给舞台一个稳稳的高度、让画布铺满它 —— 横竖屏、全屏
      都不会留下「画布比盒子矮」的死空间。高度用 vh 控，画布永远不横向溢出。
   2) 虚拟摇杆做成 DOM 而不是画在 canvas 上：站主要求「摇杆可用」，DOM 元素
      看得见、摸得着、自动化也点得到；画布上那只浮动摇杆只是同一套输入的
      另一个入口（手指落在哪就从哪推）。
   3) 升级三选一面板 z-index 压在 .stage__tools（z-index:4）之下，所以面板开着
      的时候右上角「全屏 / 横屏」照样点得到。
   ══════════════════════════════════════════════════════════════════════ */
.stage--survivor {
  min-height: 0;
  height: clamp(300px, 58vh, 560px);
  background: radial-gradient(80% 60% at 50% 34%, #3a4c8f 0%, #1a2755 62%, #0c1230 100%);
  overflow: hidden;
}
.stage--survivor canvas {
  display: block;
  width: 100%;
  height: 100%;
  max-width: 100%;
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
  -webkit-tap-highlight-color: transparent;
}

/* ── 虚拟摇杆（左下角）───────────────────────────────────────────────── */
.ss-stick {
  position: absolute;
  left: 14px;
  bottom: 16px;
  z-index: 3;
  width: 118px;
  height: 118px;
  border-radius: 50%;
  border: 1px solid rgba(140, 196, 255, 0.34);
  background: radial-gradient(circle at 50% 50%, rgba(26, 42, 90, 0.6), rgba(8, 14, 34, 0.32) 72%);
  box-shadow:
    0 0 18px rgba(111, 240, 255, 0.16) inset,
    0 0 16px rgba(6, 10, 26, 0.5);
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
  -webkit-tap-highlight-color: transparent;
}
.ss-stick[hidden] {
  display: none;
}
.ss-stick__ring {
  position: absolute;
  inset: 22px;
  border-radius: 50%;
  border: 1px dashed rgba(140, 196, 255, 0.26);
  pointer-events: none;
}
.ss-stick__knob {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 52px;
  height: 52px;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  background: radial-gradient(circle at 38% 34%, rgba(206, 250, 255, 0.95), rgba(111, 240, 255, 0.5) 46%, rgba(34, 86, 176, 0.55) 100%);
  border: 1px solid rgba(190, 245, 255, 0.75);
  box-shadow: 0 0 18px rgba(111, 240, 255, 0.5);
  pointer-events: none;
}
.ss-stick.is-live {
  border-color: rgba(190, 245, 255, 0.7);
}
.ss-stick.is-live .ss-stick__knob {
  box-shadow: 0 0 26px rgba(111, 240, 255, 0.85);
}

/* ── 波次横幅 ───────────────────────────────────────────────────────── */
.ss-banner {
  position: absolute;
  left: 50%;
  top: 47px;
  z-index: 3;
  margin: 0;
  padding: 0.34rem 1.05rem 0.4rem;
  transform: translateX(-50%);
  border: 1px solid rgba(150, 195, 255, 0.42);
  border-radius: 999px;
  background: rgba(8, 14, 36, 0.78);
  color: #eef3ff;
  font-family: var(--f-display, inherit);
  font-size: 1rem;
  letter-spacing: 0.22em;
  white-space: nowrap;
  pointer-events: none;
}
.ss-banner[hidden] {
  display: none;
}
.ss-banner.is-boss {
  border-color: rgba(255, 92, 120, 0.78);
  color: #ffd8e0;
  box-shadow: 0 0 24px rgba(255, 92, 120, 0.45);
}

/* ── 升级三选一 ─────────────────────────────────────────────────────── */
.ss-up {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: grid;
  gap: 0.7rem;
  place-content: safe center;
  justify-items: center;
  padding: 3.1rem 0.9rem 1rem;
  overflow-y: auto;
  overscroll-behavior: contain;
  background: rgba(8, 14, 34, 0.86);
  backdrop-filter: blur(3px);
  text-align: center;
}
.ss-up[hidden] {
  display: none;
}
.ss-up__title {
  margin: 0;
  font-family: var(--f-display, inherit);
  font-size: 0.98rem;
  letter-spacing: 0.16em;
  color: #eef3ff;
}
.ss-up__title b {
  font-size: 1.34rem;
  color: #6ff0ff;
  text-shadow: 0 0 14px rgba(111, 240, 255, 0.7);
}
.ss-up__cards {
  display: grid;
  gap: 0.55rem;
  width: 100%;
  max-width: 660px;
}
.ss-up__card {
  position: relative;
  display: grid;
  gap: 0.22rem;
  padding: 0.68rem 1.9rem 0.72rem 0.9rem;
  border: 1px solid rgba(140, 196, 255, 0.4);
  background: linear-gradient(180deg, rgba(40, 62, 130, 0.72), rgba(14, 22, 52, 0.8));
  color: #dbe9ff;
  text-align: left;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.ss-up__card:hover,
.ss-up__card:focus-visible {
  border-color: rgba(190, 245, 255, 0.9);
  box-shadow: 0 0 22px rgba(111, 240, 255, 0.28);
  outline: none;
}
.ss-up__kind {
  font-family: var(--f-mono, monospace);
  font-size: 0.62rem;
  letter-spacing: 0.16em;
  color: #8cc4ff;
}
.ss-up__card[data-kind='skill'] .ss-up__kind {
  color: #ffd98a;
}
.ss-up__name {
  font-size: 1.02rem;
  letter-spacing: 0.06em;
  color: #ffffff;
}
.ss-up__desc {
  font-size: 0.78rem;
  line-height: 1.6;
  color: #9fb0d0;
}
.ss-up__key {
  position: absolute;
  right: 0.6rem;
  top: 50%;
  display: grid;
  place-items: center;
  width: 1.35rem;
  height: 1.35rem;
  transform: translateY(-50%);
  border: 1px solid rgba(140, 196, 255, 0.4);
  font-family: var(--f-mono, monospace);
  font-size: 0.68rem;
  color: #8cc4ff;
}
.ss-up__hint {
  margin: 0;
  font-size: 0.7rem;
  letter-spacing: 0.1em;
  color: rgba(194, 220, 255, 0.7);
}
@media (min-width: 760px) {
  .ss-up__cards {
    grid-template-columns: repeat(3, 1fr);
    max-width: 780px;
    gap: 0.6rem;
  }
  .ss-up__card {
    padding-right: 0.85rem;
  }
  .ss-up__key {
    top: 0.5rem;
    transform: none;
  }
}

/* ── HUD：手机上「看得清」+「长数字也换行留在盒子里」────────────────── */
.hud--survivor {
  flex-wrap: wrap;
  row-gap: 0.45rem;
}
.hud--survivor .hud__item {
  min-width: 0;
  max-width: 100%;
  white-space: nowrap;
  font-size: 0.95rem;
  letter-spacing: 0.04em;
}
.hud--survivor .hud__item b {
  font-size: 1.16rem;
  font-variant-numeric: tabular-nums;
}
.hud--survivor .hud__score b {
  font-size: clamp(1.5rem, 4.6vmin, 2.2rem);
}
.hud--survivor .hud__spacer {
  margin-left: auto;
}

@media (max-width: 720px) {
  .stage--survivor {
    height: min(62vh, 520px);
  }
  .ss-stick {
    left: 12px;
    bottom: 12px;
    width: 108px;
    height: 108px;
  }
  .ss-stick__knob {
    width: 46px;
    height: 46px;
  }
  .ss-stick__ring {
    inset: 20px;
  }
  .ss-up {
    padding: 2.9rem 0.62rem 0.8rem;
  }
  .ss-up__card {
    padding: 0.6rem 1.8rem 0.64rem 0.78rem;
  }
  .hud--survivor {
    gap: 0.4rem 0.7rem;
    padding: 0.62rem 0.72rem;
  }
  .hud--survivor .hud__item {
    font-size: 0.92rem;
  }
  .hud--survivor .hud__item b {
    font-size: 1.12rem;
  }
  .hud--survivor .hud__score b {
    font-size: clamp(1.5rem, 7vmin, 1.9rem);
  }
  /* 操作提示独占一行并允许折行：这一句最长，是横向溢出唯一的风险点 */
  .hud--survivor > .hud__item:last-child {
    flex-basis: 100%;
    white-space: normal;
    font-size: 0.78rem;
    color: rgba(194, 220, 255, 0.68);
  }
}

/* 矮宽屏（手机横屏 / 折叠屏展开）：别让画布比视口还高 */
@media (min-width: 600px) and (max-height: 640px) {
  .stage--survivor {
    height: min(74vh, 420px);
  }
}

/* ══════════════════════════════════════════════════════════════════════
   开场幕布 v2：厚幕布 · 多层视差划开（液态玻璃整套拆掉）
   ──────────────────────────────────────────────────────────────────────
   1) 幕布默认 display:none：只有 cover.js 判定「今天第 1 次访问」才写成
      .curtain[data-cover='on'] 把它点亮 ⇒ 当天第二次访问不会闪一下再消失。
   2) 幕布里一层毛玻璃都不建；这里再把 .curtain 子树里的 backdrop-filter 按死，
      上面那份液态玻璃的旧规则也翻不了身（旧文件不允许改，只能压住）。
   3) 3–4 块不透明深色厚板叠着，上滑时按 1 / .74 / .5 / .3 的跟手比例先后划出；
      每块板底边一条「截面亮线 + 渐变」，读作厚材质被切开——层与层之间因此有厚度。
   ══════════════════════════════════════════════════════════════════════ */
.curtain {
  display: none;
  overflow: hidden;
  background: transparent;
  transform: none;
  touch-action: none;
  -webkit-backdrop-filter: none !important;
  backdrop-filter: none !important;
  --cover-dur: 0.78s;
  /* ease-out-cubic：先快后慢 */
  --cover-ease: cubic-bezier(0.215, 0.61, 0.355, 1);
}
.curtain[data-cover='on'] {
  display: grid;
}
.curtain::before {
  display: none; /* 旧的门厅底图，跟着毛玻璃一起撤 */
}
.curtain * {
  -webkit-backdrop-filter: none !important;
  backdrop-filter: none !important;
}
.curtain__glass,
.curtain__tint,
.curtain__frost,
.curtain__seam,
.curtain__edge,
.curtain__sheen,
.curtain__ripple {
  display: none !important;
}
.curtain.is-open {
  transform: none;
  opacity: 1;
  pointer-events: none;
}
.curtain.is-gone {
  display: none;
}

/* ── 厚板本体：不透明深色 + 细微拉丝纹理 + 顶部一点环境光 ── */
.cover-layer {
  --face: linear-gradient(180deg, #0b1330 0%, #090f28 48%, #05081a 100%);
  position: absolute;
  inset: 0;
  z-index: 1;
  overflow: hidden;
  pointer-events: none;
  transform: translate3d(var(--x, 0px), var(--y, 0px), 0);
  transition: transform var(--cover-dur) var(--cover-ease);
  will-change: transform;
  background:
    radial-gradient(126% 62% at 50% -14%, rgba(96, 142, 232, 0.24), rgba(96, 142, 232, 0) 66%),
    repeating-linear-gradient(
      180deg,
      rgba(255, 255, 255, 0.024) 0 1px,
      rgba(255, 255, 255, 0) 1px 4px
    ),
    var(--face);
  box-shadow:
    0 -1px 0 rgba(228, 240, 255, 0.9),
    0 20px 44px rgba(4, 8, 20, 0.5);
}
/* 越靠后（越"深"）的板稍微亮一点、偏蓝一点，像被门厅的光扫到 */
.cover-layer[data-tone='1'] {
  --face: linear-gradient(180deg, #0f1a3d 0%, #0c1533 48%, #070c22 100%);
}
.cover-layer[data-tone='2'] {
  --face: linear-gradient(180deg, #152048 0%, #111c3e 48%, #0a1230 100%);
}
.cover-layer[data-tone='3'] {
  --face: linear-gradient(180deg, #1b2a5a 0%, #152048 50%, #0d1739 100%);
}
/* 跟手拖动的那一段不要缓动，否则手指和板子之间会"脱手" */
.curtain.is-dragging .cover-layer {
  transition: none;
}

/* ── 截面：板子底边那条亮线 + 往外洇的辉光，厚材质被切开的样子 ── */
.cover-layer__cut {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 22px;
  background: linear-gradient(
    180deg,
    rgba(6, 10, 24, 0) 0%,
    rgba(7, 12, 30, 0.55) 34%,
    rgba(132, 184, 255, 0.32) 62%,
    rgba(226, 240, 255, 0.96) 88%,
    rgba(150, 200, 255, 0.9) 96%,
    rgba(96, 150, 255, 0.45) 100%
  );
}
.cover-layer__cut::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1px;
  background: linear-gradient(
    90deg,
    rgba(226, 240, 255, 0.25),
    rgba(238, 246, 255, 0.95) 22%,
    rgba(226, 240, 255, 0.7) 50%,
    rgba(238, 246, 255, 0.95) 78%,
    rgba(226, 240, 255, 0.25)
  );
  box-shadow: 0 0 12px rgba(150, 200, 255, 0.75);
}

/* ── 最前面那块板上的缓慢镜面高光（没有 backdrop-filter，只是块渐变）── */
.cover-layer__gloss {
  position: absolute;
  top: -30%;
  left: -62%;
  width: 58%;
  height: 160%;
  transform: rotate(14deg);
  background: linear-gradient(
    90deg,
    rgba(198, 222, 255, 0) 0%,
    rgba(198, 222, 255, 0.1) 42%,
    rgba(238, 246, 255, 0.2) 52%,
    rgba(150, 200, 255, 0.08) 62%,
    rgba(198, 222, 255, 0) 100%
  );
  filter: blur(6px);
  opacity: 0.72;
  animation: coverGlossDrift 11s ease-in-out infinite;
}
@keyframes coverGlossDrift {
  0%,
  100% {
    transform: translate3d(0, 0, 0) rotate(14deg);
  }
  50% {
    transform: translate3d(96%, 0, 0) rotate(14deg);
  }
}
.curtain.is-open .cover-layer__gloss {
  animation: coverGlossSweep 0.78s var(--cover-ease) forwards;
}
@keyframes coverGlossSweep {
  0% {
    transform: translate3d(-10%, 0, 0) rotate(14deg);
    opacity: 0.3;
  }
  45% {
    opacity: 0.85;
  }
  100% {
    transform: translate3d(190%, 0, 0) rotate(14deg);
    opacity: 0;
  }
}

/* ── 幕布上的字：比最前面的板再快一点（1.14 倍），跟着一起掀上去 ── */
.curtain__inner {
  z-index: 3;
  transform: translate3d(0, var(--inner-y, 0px), 0);
  transition:
    opacity 0.3s var(--ease),
    transform var(--cover-dur) var(--cover-ease);
}
.curtain.is-dragging .curtain__inner {
  transition: none;
}
.curtain.is-open .curtain__inner {
  opacity: 0;
  transform: translate3d(0, -46px, 0);
}

/* ── 手机：板子薄一点、高光收一点 ── */
@media (max-width: 720px) {
  .cover-layer__cut {
    height: 17px;
  }
  .cover-layer__gloss {
    filter: blur(4px);
    opacity: 0.5;
  }
}

/* ── 低配设备（核数或内存 ≤4）：最前面那层高光直接不建，省一次合成 ── */
.curtain[data-cover-tier='lean'] .cover-layer__gloss {
  display: none;
}
.curtain[data-cover-tier='lean'] .cover-layer {
  box-shadow: 0 -1px 0 rgba(228, 240, 255, 0.75);
}

/* ── 减弱动态：不划、不形变，整块 0.26s 淡出（放最后，压住上面的 .is-open）── */
@media (prefers-reduced-motion: reduce) {
  .curtain {
    transition: opacity 0.26s linear !important;
  }
  .curtain.is-open {
    opacity: 0;
    transform: none;
  }
  .cover-layer,
  .cover-layer__gloss,
  .curtain__inner {
    transform: none !important;
    animation: none !important;
    transition: none !important;
  }
}

/* ── 需求1/3 · 表情选择器（私信 / 潮汐信笺 / 与希儿的日常共用同一个面板）────
   .emoji 是绝对定位的小面板，锚点交给宿主：
     · 私信：#dmx-form.dmx__composer（右对齐，往上开）
     · 发帖 / 日常：.composer__foot（左对齐，往上开，正好落在输入框下方）
     · 随意发言：.chat__row（往下开，免得盖住那一行输入框）
   宽度跟宿主走（min(320px, 100%)）再叠一层 ≤100vw-24px 的兜底：
   375px 窄屏上既不会横向溢出，也不会被裁掉。 */
.emoji {
  position: absolute;
  z-index: 60;
  width: min(320px, 100%);
  max-width: calc(100vw - 24px);
  max-height: min(19rem, 55vh);
  display: flex;
  flex-direction: column;
  padding: 0.45rem;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: rgba(14, 22, 52, 0.97);
  box-shadow: 0 18px 40px rgba(3, 6, 20, 0.6);
}
.emoji[hidden] {
  display: none;
}
.emoji__tabs {
  display: flex;
  gap: 0.25rem;
  padding: 0 0.1rem 0.4rem;
  overflow-x: auto;
  scrollbar-width: none;
}
.emoji__tabs::-webkit-scrollbar {
  display: none;
}
.emoji__tab {
  flex: none;
  padding: 0.16rem 0.55rem;
  border: 1px solid transparent;
  border-radius: 999px;
  background: transparent;
  color: var(--faint);
  font-size: 0.74rem;
  line-height: 1.4;
  cursor: pointer;
}
.emoji__tab:hover {
  color: var(--silk);
}
.emoji__tab.is-active {
  color: var(--silk);
  border-color: var(--line);
  background: rgba(140, 196, 255, 0.16);
}
.emoji__body {
  overflow-y: auto;
  overscroll-behavior: contain;
}
.emoji__pane {
  display: grid;
  grid-template-columns: repeat(8, minmax(0, 1fr));
  gap: 2px;
}
.emoji__pane[hidden] {
  display: none;
}
.emoji__cell {
  display: grid;
  place-items: center;
  width: 100%;
  min-width: 0;
  aspect-ratio: 1 / 1;
  padding: 0;
  border: 0;
  border-radius: 8px;
  background: transparent;
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
}
.emoji__cell:hover {
  background: rgba(140, 196, 255, 0.16);
}
.emoji__cell--seele {
  color: var(--azure-soft);
}
.emoji__cell--seele svg {
  width: 72%;
  height: 72%;
}
.emoji__open {
  flex: none;
  display: grid;
  place-items: center;
  width: 2.1rem;
  height: 2.1rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: transparent;
  color: var(--azure-soft);
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
}
.emoji__open:hover,
.emoji__open.is-on {
  color: var(--silk);
  background: rgba(140, 196, 255, 0.18);
}
/* 宿主必须是定位元素，面板才知道自己在哪儿 */
.dmx__composer {
  position: relative;
}
.composer__foot {
  position: relative;
}
.chat__row {
  position: relative;
}
.dmx__composer .emoji {
  right: 0.55rem;
  bottom: calc(100% + 6px);
}
.composer__foot .emoji {
  left: 0;
  bottom: calc(100% + 8px);
}
.chat__row .emoji {
  left: 0;
  top: calc(100% + 8px);
}

/* 气泡 / 留言正文里的希儿小表情（[[seele-hi]] → 内联 SVG）：跟着字号走 */
.emoji-sticker {
  display: inline-block;
  width: 1.35em;
  height: 1.35em;
  margin: 0 0.05em;
  vertical-align: -0.3em;
  color: var(--azure-soft);
}
.emoji-sticker svg {
  width: 100%;
  height: 100%;
}

/* 需求3：「与希儿的日常」能贴图，输入框给高一点，别让长文一进来就滚动 */
.field--daily {
  min-height: 150px;
}

@media (max-width: 420px) {
  .emoji {
    max-height: min(15rem, 50vh);
  }
  .emoji__cell {
    font-size: 1rem;
  }
  .emoji__open {
    width: 1.95rem;
    height: 1.95rem;
  }
}

/* ══════════════════════════════════════════════════════════════════════════
   追加（本轮）：网站公告（站主第 16 项）
   —— 门厅横幅 / #/announcements 时间线 / 审核台「公告」标签
   只追加在文件末尾，不改动上方任何旧规则。
   ══════════════════════════════════════════════════════════════════════════ */

/* ── 1. 门厅横幅 ────────────────────────────────────────────────────────
   挂点：main.js 的 boot() 里一行 mountAnnouncementBar()，节点插在 .topbar
   之后、<main> 之前（塞进 #view 会被 route() 的 replaceChildren 清掉）。
   没有公告时节点根本不建，所以这里不需要空态样式。
   刻意不写 position / z-index：.topbar 是 sticky 的定位元素，横幅留在普通流里，
   滚动时才会被顶栏压住，而不是反过来糊在顶栏上。 */
.hall-ann {
  border-top: 1px solid rgba(108, 224, 255, 0.26);
  border-bottom: 1px solid rgba(108, 224, 255, 0.42);
  background: linear-gradient(180deg, rgba(7, 14, 32, 0.95), rgba(10, 20, 44, 0.88));
  box-shadow: 0 8px 24px rgba(4, 10, 24, 0.42);
}
.hall-ann__inner {
  display: flex;
  align-items: center;
}
.hall-ann__link {
  display: flex;
  flex: 1 1 auto;
  align-items: center;
  gap: 0.5rem;
  min-width: 0;
  min-height: 40px;
  padding: 0.2rem 0;
  color: var(--azure-soft);
  text-decoration: none;
}
.hall-ann__icon {
  flex: 0 0 auto;
  color: #6ce0ff;
  filter: drop-shadow(0 0 6px rgba(108, 224, 255, 0.45));
}
.hall-ann__tag {
  flex: 0 0 auto;
  padding: 0 0.42rem;
  border: 1px solid rgba(108, 224, 255, 0.5);
  border-radius: 999px;
  font-family: var(--f-mono);
  font-size: 0.64rem;
  letter-spacing: 0.16em;
  line-height: 1.7;
  color: #6ce0ff;
}
/* 标题永远一行：flex 里 min-width:0 + nowrap + 省略号，再长也不换行、不撑破 */
.hall-ann__title {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  font-size: 0.92rem;
  letter-spacing: 0.02em;
  color: var(--silk);
}
.hall-ann__more {
  flex: 0 0 auto;
  font-size: 0.76rem;
  color: var(--azure-dim);
}
.hall-ann__link:hover .hall-ann__title {
  color: #fff;
}
.hall-ann__link:hover .hall-ann__more {
  color: var(--azure);
}
@media (max-width: 560px) {
  .hall-ann__icon {
    width: 15px;
    height: 15px;
  }
  .hall-ann__link {
    gap: 0.4rem;
  }
  .hall-ann__title {
    font-size: 0.85rem;
  }
  .hall-ann__more {
    display: none;
  }
}

/* ── 2. #/announcements 房间：时间线 ────────────────────────────────────
   卡片直接复用 .panel（和阅览室同一套面板语言），这里只补时间轴的竖线、
   节点圆点与置顶强调。左边 1.6rem 是轴的位置，正文宽度不受影响。 */
.ann {
  position: relative;
  display: grid;
  gap: 0.9rem;
  padding-left: 1.6rem;
}
.ann::before {
  content: '';
  position: absolute;
  top: 0.7rem;
  bottom: 0.7rem;
  left: 4px;
  width: 1px;
  background: linear-gradient(180deg, rgba(140, 196, 255, 0.45), rgba(140, 196, 255, 0.08));
}
.ann__item {
  position: relative;
}
.ann__dot {
  position: absolute;
  top: 1.15rem;
  left: -1.6rem;
  width: 9px;
  height: 9px;
  border: 1px solid var(--line-strong);
  border-radius: 50%;
  background: var(--abyss);
  box-shadow: 0 0 0 4px rgba(18, 28, 68, 0.92);
}
.ann__item.is-pinned .ann__dot {
  border-color: #6ce0ff;
  background: #6ce0ff;
  box-shadow:
    0 0 0 4px rgba(18, 28, 68, 0.92),
    0 0 12px rgba(108, 224, 255, 0.7);
}
.ann__card {
  padding: 0.95rem 1.1rem 1.05rem;
}
.ann__item.is-pinned .ann__card {
  border-color: rgba(108, 224, 255, 0.6);
}
.ann__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--f-mono);
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  color: var(--faint);
}
.ann__pin {
  padding: 0 0.42rem;
  border: 1px solid rgba(108, 224, 255, 0.5);
  border-radius: 999px;
  font-size: 0.66rem;
  letter-spacing: 0.16em;
  line-height: 1.7;
  color: #6ce0ff;
}
.ann__ago {
  color: var(--faint);
}
.ann__title {
  margin-top: 0.5rem;
  font-family: var(--f-display);
  font-size: 1.28rem;
  font-weight: 400;
  letter-spacing: 0.05em;
  line-height: 1.5;
  color: var(--silk);
  overflow-wrap: anywhere;
}
.ann__body {
  margin-top: 0.55rem;
  white-space: pre-wrap; /* 站长在后台敲的换行原样保留 */
  overflow-wrap: anywhere;
  font-size: 0.94rem;
  line-height: 1.95;
  color: var(--ash);
}
@media (max-width: 560px) {
  .ann {
    gap: 0.75rem;
    padding-left: 1.25rem;
  }
  .ann::before {
    left: 3px;
  }
  .ann__dot {
    left: -1.25rem;
  }
  .ann__card {
    padding: 0.8rem 0.9rem 0.9rem;
  }
  .ann__title {
    font-size: 1.14rem;
  }
}

/* ── 3. 审核台「公告」标签：新建表单 + 列表（含已下线）────────────────── */
.rv-ann__form {
  display: grid;
  gap: 0.6rem;
  padding: 0.9rem 1rem 1rem;
  border: 1px solid var(--line);
  background: rgba(16, 25, 60, 0.55);
}
.rv-ann__body {
  min-height: 96px;
  resize: vertical;
  line-height: 1.8;
}
.rv-ann__formrow {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem 0.9rem;
}
.rv-ann__pin {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font-size: 0.86rem;
  color: var(--ash);
  cursor: pointer;
}
.rv-ann__pin input {
  width: 17px;
  height: 17px;
  accent-color: #6ce0ff;
}
.rv-ann__list {
  display: grid;
  gap: 0.7rem;
  margin-top: 1.1rem;
}
.rv-ann__item {
  padding: 0.85rem 0.95rem 0.9rem;
  border: 1px solid var(--line);
  background: rgba(20, 30, 70, 0.5);
}
.rv-ann__item.is-off {
  opacity: 0.55;
}
.rv-ann__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
}
.rv-ann__head b {
  font-size: 1rem;
  font-weight: 600;
  color: var(--silk);
  overflow-wrap: anywhere;
}
.rv-ann__text {
  margin-top: 0.45rem;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  font-size: 0.88rem;
  line-height: 1.85;
  color: var(--ash);
}
@media (max-width: 560px) {
  .rv-ann__form {
    padding: 0.8rem 0.85rem 0.9rem;
  }
  .rv-ann__formrow .btn {
    flex: 1 1 auto;
  }
}

/* 需求1 修正：发言区（发帖 / 与希儿的日常）的表情面板改从按钮下面展开——
   原来往上开正好压住上面那个 textarea，用户点输入框会被面板吃掉。
   （私信那边的面板锚在整条 composer 之上，不挡输入框，保持不动。） */
.composer__foot .emoji {
  top: calc(100% + 8px);
  bottom: auto;
}

/* ── 小游戏暂停（追加，只加不改动上面任何一条）──────────────────────────
   需求：一局进行中，舞台角落给一个「❚❚」；点开是半透明遮罩 +「已暂停」+「继续」。
   放左上角：右上角是「全屏 / 横屏」工具区（开始面板出现时还会被 games.js 挪进按钮行），
   两个角各占一边不打架。暂停是「停帧」（游戏的 rAF 不再排下一帧），所以遮罩不用动画。 */
.stage__pause {
  position: absolute;
  top: 0.4rem;
  left: 0.4rem;
  z-index: 5;
  display: inline-flex;
  align-items: center;
  gap: 0.28rem;
  min-height: 34px;
  padding: 0 0.66rem;
  border: 1px solid rgba(150, 195, 255, 0.42);
  border-radius: 999px;
  background: rgba(8, 14, 36, 0.74);
  color: #dbe9ff;
  font-size: 0.78rem;
  line-height: 1;
  letter-spacing: 0.04em;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.stage__pause span {
  font-size: 0.86rem;
  letter-spacing: -0.08em;
}
.stage__pause:hover {
  border-color: rgba(190, 220, 255, 0.8);
  color: #fff;
}
.stage__paused {
  position: absolute;
  inset: 0;
  z-index: 6;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(4, 8, 22, 0.62);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
}
/* 遮罩里的卡片：窄屏（375px）也不许顶出画布 */
.stage__paused-box {
  box-sizing: border-box;
  display: grid;
  justify-items: center;
  gap: 0.7rem;
  max-width: calc(100% - 1.6rem);
  padding: 0.9rem 1.2rem;
  border: 1px solid rgba(150, 195, 255, 0.35);
  border-radius: 14px;
  background: rgba(8, 14, 36, 0.86);
}
.stage__paused-t {
  margin: 0;
  color: #dbe9ff;
  font-family: var(--f-display);
  font-size: 1.05rem;
  font-weight: 300;
  letter-spacing: 0.28em;
  text-indent: 0.28em; /* 字间距是加在字后面的，缩进一格才是真居中 */
}
.stage__paused-btn {
  min-width: 7.5rem;
}
/* 隐藏只靠 hidden 属性（这里 display 是 inline-flex / flex，必须显式压掉） */
.stage__pause[hidden],
.stage__paused[hidden] {
  display: none !important;
}
/* 手机上「❚❚」也要点得到：可点高度补到 44px（只放大按钮，不动排版骨架） */
@media (pointer: coarse), (max-width: 720px) {
  .stage__pause {
    min-height: 44px;
    padding: 0 0.8rem;
  }
}


/* ════════════════════════════════════════════════════════════════════
   门厅四问 · 追加（只加新规则 / 窄屏覆盖，上面任何一条老规则都没动）

   ① .hero__name          「希儿 · 10.18 生日应援」任何宽度都只占一行
   ② .hero__corner        ≤720px 下移 + 缩小 + 右移，让开标题与台词
   ③ .wish__panel         ≤720px 由「两列 grid」改成竖向流
   ④ .topbar .who__avatar  顶栏头像不再被 flex 压扁
   ════════════════════════════════════════════════════════════════════ */

/* ── ① 标题单行 ───────────────────────────────────────────────────────
   实测（本机 Chrome）：这 15 个字不换行时宽 = 字号 × 11.371，
   其中 letter-spacing: 0.1em 随字号等比，所以这个比值与字号无关。
   .wrap 已把 .hero 宽度定成 min(1180px, 100% - 2.5rem)，正是标题的可用宽度；
   字号取「可用宽度 / 12.4」——比 11.371 多留约 8% 余量，
   抵掉「100vw 含滚动条、100% 不含」的差额，任何视口都不会折行。
   上限 6.4rem 对应桌面 1180px 宽（1180 / 11.6 = 101.7px，仍有余量）。 */
.hero {
  --hero-title: clamp(0.95rem, calc((100vw - 2.5rem) / 12.4), 6.4rem);
}
.hero__name {
  white-space: nowrap;
  font-size: var(--hero-title);
}

/* ── ② 右上角的她：下移，别再压标题 ───────────────────────────────────
   老规则是 top: 4%（.hero 高度的百分比）——页面内容一变高她就往下飘。
   这里换成固定 px：桌面槽位最大 210px，超采样画布上下各外溢 0.224 × 槽位宽，
   .hero 自己的 padding-top 是 clamp(3rem, 9vw, 7rem)（桌面顶到 7rem = 112px），
   所以「让开标题」需要的 top = 7rem + 0.94 × 标题字号 + 0.224 × 210px + 8px
   ≈ 263px，取 17rem 对 721px 以上全都满足（最紧的 1440 也留 9px）。
   ≤720px 另给一档：槽位缩到 76px、右移，top 取 9.8rem
   （画布顶 ≈ 标题下沿 + 66px，且不压「拜托了，另一个我！」那行台词）。 */
.hero__corner {
  top: 17rem;
}
@media (max-width: 720px) {
  .hero__corner {
    width: 76px;
    right: -14px;
    top: 9.8rem;
  }
}

/* ── ③ 祝福区：手机上改竖向流 ─────────────────────────────────────────
   原来是「正文 1fr + 小人 auto」两列 grid：375px 下左列只剩 135px，
   两个按钮 + 统计被挤成 396px 高的一条长列（站主最不满的那条）。
   这里 ≤720px 换 flex 竖排，用 order 把顺序钉成
   标题 → 小人（居中）→ 按钮一排 → 统计行 → 祝福榜。
   .wish__panel 故意不加 position：.wish__fx 的定位父级是 .wish，
   动了礼物飞行轨迹就偏。桌面 (>720px) 一个字没改，仍是两列 grid。 */
@media (max-width: 720px) {
  .wish__panel {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    row-gap: 0.5rem;
  }
  .wish__panel > .wish__kicker {
    order: 1;
  }
  .wish__panel > .wish__title {
    order: 2;
  }
  .wish__panel > .wish__sub {
    order: 3;
  }
  .wish__panel > .wish__puppet {
    order: 4;
  }
  .wish__panel > .wish__row {
    order: 5;
  }
  .wish__panel > .wishwall {
    order: 6;
  }
  .wish__panel > .wish__fx {
    order: 7;
  }
  /* 小人：不再占右列，居中一行 */
  #wish-puppet {
    align-self: center;
    width: clamp(104px, 30vw, 150px);
    margin: 0.35rem auto 0.1rem;
    padding: 0;
  }
  /* 按钮一排并排 + 统计占满下一行 */
  .wish__panel > .wish__row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.55rem 0.6rem;
    align-items: stretch;
    margin-top: 0.4rem;
  }
  .wish__panel > .wish__row > .wish__btn {
    width: 100%;
    min-width: 0;
    justify-content: center;
    padding: 0.72rem 0.5rem;
    font-size: 0.9rem;
    gap: 0.4rem;
  }
  .wish__panel > .wish__row > .wish__stats {
    grid-column: 1 / -1;
  }
  /* 气泡：竖排后不再对齐「标题中心」，改成悬在小人正上方 */
  #wish-puppet .puppet__bubble,
  #wish-puppet.is-stacked .puppet__bubble {
    top: auto;
    bottom: calc(100% + 8px);
    right: auto;
    left: 50%;
    --wb-tr: translateX(-50%);
    max-width: min(15rem, 84vw);
    font-size: 0.92rem;
  }
  #wish-puppet .puppet__bubble::after,
  #wish-puppet.is-stacked .puppet__bubble::after {
    top: auto;
    bottom: -5px;
    right: auto;
    left: 50%;
    margin-top: 0;
    margin-left: -6px;
  }
}

/* ── ④ 顶栏头像不被压扁 ───────────────────────────────────────────────
   1280px 实测：.topbar__inner 是 flex 且内容超宽，.who__avatar 用默认的
   flex: 0 1 auto 被压到 4px 宽；里头的 <img class="avatar"> 又被全局
   max-width: 100% 跟着缩，于是 36×36 变成 2×36（ratio 0.056）。
   两头都钉死：按钮不许缩，头像 36×36 + aspect-ratio。
   多出来的 32px 由本来就溢出、本来就在省略的 .nav 承担
   （实测 .who__id / 昵称宽度不变，页面也没有新增横向溢出）。 */
.topbar .who__avatar {
  flex: 0 0 auto;
  min-width: 38px;
  min-height: 38px;
}
.topbar .who__avatar > .avatar {
  flex: 0 0 auto;
  width: 36px;
  height: 36px;
  min-width: 36px;
  max-width: 36px;
  aspect-ratio: 1 / 1;
}
